summaryrefslogtreecommitdiff
path: root/sql
diff options
context:
space:
mode:
Diffstat (limited to 'sql')
-rwxr-xr-xsql/CMakeLists.txt141
-rw-r--r--sql/Makefile.am170
-rw-r--r--sql/authors.h152
-rw-r--r--sql/contributors.h39
-rw-r--r--sql/derror.cc88
-rw-r--r--sql/des_key_file.cc19
-rw-r--r--sql/discover.cc80
-rw-r--r--sql/event_data_objects.cc1583
-rw-r--r--sql/event_data_objects.h196
-rw-r--r--sql/event_db_repository.cc1168
-rw-r--r--sql/event_db_repository.h131
-rw-r--r--sql/event_parse_data.cc577
-rw-r--r--sql/event_parse_data.h124
-rw-r--r--sql/event_queue.cc794
-rw-r--r--sql/event_queue.h126
-rw-r--r--sql/event_scheduler.cc796
-rw-r--r--sql/event_scheduler.h155
-rw-r--r--sql/events.cc1217
-rw-r--r--sql/events.h164
-rw-r--r--sql/examples/ha_example.cc706
-rw-r--r--sql/examples/ha_example.h153
-rw-r--r--sql/examples/ha_tina.cc1003
-rw-r--r--sql/examples/ha_tina.h124
-rw-r--r--sql/field.cc2852
-rw-r--r--sql/field.h1209
-rw-r--r--sql/field_conv.cc161
-rw-r--r--sql/filesort.cc629
-rw-r--r--sql/gen_lex_hash.cc27
-rw-r--r--sql/gstream.h2
-rw-r--r--sql/ha_archive.cc1249
-rw-r--r--sql/ha_archive.h115
-rw-r--r--sql/ha_berkeley.cc2675
-rw-r--r--sql/ha_berkeley.h163
-rw-r--r--sql/ha_blackhole.cc339
-rw-r--r--sql/ha_blackhole.h99
-rw-r--r--sql/ha_federated.cc2961
-rw-r--r--sql/ha_federated.h324
-rw-r--r--sql/ha_heap.cc695
-rw-r--r--sql/ha_heap.h109
-rw-r--r--sql/ha_innodb.cc7425
-rw-r--r--sql/ha_innodb.h338
-rw-r--r--sql/ha_myisam.cc2013
-rw-r--r--sql/ha_myisam.h137
-rw-r--r--sql/ha_myisammrg.cc642
-rw-r--r--sql/ha_myisammrg.h85
-rw-r--r--sql/ha_ndbcluster.cc7487
-rw-r--r--sql/ha_ndbcluster.h376
-rw-r--r--sql/ha_ndbcluster_binlog.cc4425
-rw-r--r--sql/ha_ndbcluster_binlog.h227
-rw-r--r--sql/ha_ndbcluster_cond.cc14
-rw-r--r--sql/ha_ndbcluster_cond.h13
-rw-r--r--sql/ha_ndbcluster_tables.h23
-rw-r--r--sql/ha_partition.cc6468
-rw-r--r--sql/ha_partition.h1092
-rw-r--r--sql/handler.cc4142
-rw-r--r--sql/handler.h1673
-rw-r--r--sql/hash_filo.h8
-rw-r--r--sql/hostname.cc23
-rw-r--r--sql/init.cc8
-rw-r--r--sql/item.cc1594
-rw-r--r--sql/item.h465
-rw-r--r--sql/item_buff.cc19
-rw-r--r--sql/item_cmpfunc.cc1092
-rw-r--r--sql/item_cmpfunc.h247
-rw-r--r--sql/item_create.cc5097
-rw-r--r--sql/item_create.h297
-rw-r--r--sql/item_func.cc1323
-rw-r--r--sql/item_func.h201
-rw-r--r--sql/item_geofunc.cc20
-rw-r--r--sql/item_geofunc.h13
-rw-r--r--sql/item_row.cc19
-rw-r--r--sql/item_row.h6
-rw-r--r--sql/item_strfunc.cc323
-rw-r--r--sql/item_strfunc.h67
-rw-r--r--sql/item_subselect.cc288
-rw-r--r--sql/item_subselect.h23
-rw-r--r--sql/item_sum.cc315
-rw-r--r--sql/item_sum.h22
-rw-r--r--sql/item_timefunc.cc561
-rw-r--r--sql/item_timefunc.h124
-rw-r--r--sql/item_uniq.h62
-rw-r--r--sql/item_xmlfunc.cc2845
-rw-r--r--sql/item_xmlfunc.h63
-rw-r--r--sql/key.cc354
-rw-r--r--sql/lex.h306
-rw-r--r--sql/lex_symbol.h1
-rw-r--r--sql/lock.cc589
-rw-r--r--sql/log.cc4091
-rw-r--r--sql/log.h584
-rw-r--r--sql/log_event.cc5862
-rw-r--r--sql/log_event.h2797
-rw-r--r--sql/log_event_old.cc2907
-rw-r--r--sql/log_event_old.h571
-rw-r--r--sql/mf_iocache.cc27
-rw-r--r--sql/my_decimal.cc46
-rw-r--r--sql/my_decimal.h58
-rw-r--r--sql/my_lock.c2
-rw-r--r--sql/mysql_priv.h1315
-rw-r--r--sql/mysql_priv.h.pp10978
-rw-r--r--sql/mysqld.cc4200
-rw-r--r--sql/mysqld_suffix.h7
-rw-r--r--sql/net_serv.cc387
-rw-r--r--sql/nt_servc.cc75
-rw-r--r--sql/nt_servc.h16
-rw-r--r--sql/opt_range.cc2661
-rw-r--r--sql/opt_range.h114
-rw-r--r--sql/opt_sum.cc295
-rw-r--r--sql/parse_file.cc392
-rw-r--r--sql/parse_file.h49
-rw-r--r--sql/partition_element.h99
-rw-r--r--sql/partition_info.cc1327
-rw-r--r--sql/partition_info.h314
-rw-r--r--sql/password.c6
-rw-r--r--sql/procedure.cc11
-rw-r--r--sql/procedure.h14
-rw-r--r--sql/protocol.cc719
-rw-r--r--sql/protocol.h59
-rw-r--r--sql/records.cc243
-rw-r--r--sql/repl_failsafe.cc148
-rw-r--r--sql/repl_failsafe.h4
-rw-r--r--sql/rpl_constants.h18
-rw-r--r--sql/rpl_filter.cc547
-rw-r--r--sql/rpl_filter.h116
-rw-r--r--sql/rpl_injector.cc236
-rw-r--r--sql/rpl_injector.h337
-rw-r--r--sql/rpl_mi.cc414
-rw-r--r--sql/rpl_mi.h114
-rw-r--r--sql/rpl_record.cc348
-rw-r--r--sql/rpl_record.h (renamed from sql/matherr.c)48
-rw-r--r--sql/rpl_record_old.cc174
-rw-r--r--sql/rpl_record_old.h (renamed from sql/item_uniq.cc)28
-rw-r--r--sql/rpl_reporting.cc48
-rw-r--r--sql/rpl_reporting.h85
-rw-r--r--sql/rpl_rli.cc1200
-rw-r--r--sql/rpl_rli.h418
-rw-r--r--sql/rpl_tblmap.cc164
-rw-r--r--sql/rpl_tblmap.h111
-rw-r--r--sql/rpl_utility.cc226
-rw-r--r--sql/rpl_utility.h306
-rw-r--r--sql/scheduler.cc88
-rw-r--r--sql/scheduler.h60
-rw-r--r--sql/set_var.cc3079
-rw-r--r--sql/set_var.h850
-rw-r--r--sql/share/charsets/Index.xml5
-rw-r--r--sql/share/charsets/cp1250.xml21
-rw-r--r--sql/share/errmsg.txt10769
-rw-r--r--sql/slave.cc4084
-rw-r--r--sql/slave.h537
-rw-r--r--sql/sp.cc1585
-rw-r--r--sql/sp.h49
-rw-r--r--sql/sp_cache.cc34
-rw-r--r--sql/sp_cache.h1
-rw-r--r--sql/sp_head.cc1122
-rw-r--r--sql/sp_head.h352
-rw-r--r--sql/sp_pcontext.cc24
-rw-r--r--sql/sp_pcontext.h10
-rw-r--r--sql/sp_rcontext.cc24
-rw-r--r--sql/spatial.cc8
-rw-r--r--sql/spatial.h14
-rw-r--r--sql/sql_acl.cc1301
-rw-r--r--sql/sql_acl.h55
-rw-r--r--sql/sql_analyse.cc55
-rw-r--r--sql/sql_analyse.h50
-rw-r--r--sql/sql_array.h2
-rw-r--r--sql/sql_base.cc4554
-rw-r--r--sql/sql_binlog.cc244
-rw-r--r--sql/sql_bitmap.h10
-rw-r--r--sql/sql_builtin.cc.in (renamed from sql/sql_manager.h)19
-rw-r--r--sql/sql_cache.cc1245
-rw-r--r--sql/sql_cache.h108
-rw-r--r--sql/sql_class.cc1701
-rw-r--r--sql/sql_class.h1546
-rw-r--r--sql/sql_connect.cc1134
-rw-r--r--sql/sql_crypt.cc4
-rw-r--r--sql/sql_cursor.cc99
-rw-r--r--sql/sql_cursor.h11
-rw-r--r--sql/sql_db.cc801
-rw-r--r--sql/sql_delete.cc407
-rw-r--r--sql/sql_derived.cc89
-rw-r--r--sql/sql_do.cc16
-rw-r--r--sql/sql_error.cc40
-rw-r--r--sql/sql_error.h4
-rw-r--r--sql/sql_handler.cc331
-rw-r--r--sql/sql_help.cc69
-rw-r--r--sql/sql_insert.cc1357
-rw-r--r--sql/sql_lex.cc1102
-rw-r--r--sql/sql_lex.h784
-rw-r--r--sql/sql_list.cc34
-rw-r--r--sql/sql_list.h111
-rw-r--r--sql/sql_load.cc229
-rw-r--r--sql/sql_manager.cc101
-rw-r--r--sql/sql_map.cc16
-rw-r--r--sql/sql_map.h12
-rw-r--r--sql/sql_olap.cc12
-rw-r--r--sql/sql_parse.cc4806
-rw-r--r--sql/sql_partition.cc7126
-rw-r--r--sql/sql_partition.h211
-rw-r--r--sql/sql_plugin.cc3314
-rw-r--r--sql/sql_plugin.h140
-rw-r--r--sql/sql_prepare.cc1697
-rw-r--r--sql/sql_profile.cc672
-rw-r--r--sql/sql_profile.h297
-rw-r--r--sql/sql_rename.cc151
-rw-r--r--sql/sql_repl.cc373
-rw-r--r--sql/sql_repl.h14
-rw-r--r--sql/sql_select.cc4468
-rw-r--r--sql/sql_select.h248
-rw-r--r--sql/sql_servers.cc1305
-rw-r--r--sql/sql_servers.h43
-rw-r--r--sql/sql_show.cc5243
-rw-r--r--sql/sql_show.h41
-rw-r--r--sql/sql_string.cc108
-rw-r--r--sql/sql_string.h14
-rw-r--r--sql/sql_table.cc5432
-rw-r--r--sql/sql_tablespace.cc71
-rw-r--r--sql/sql_test.cc61
-rw-r--r--sql/sql_trigger.cc1452
-rw-r--r--sql/sql_trigger.h76
-rw-r--r--sql/sql_udf.cc162
-rw-r--r--sql/sql_union.cc47
-rw-r--r--sql/sql_update.cc729
-rw-r--r--sql/sql_view.cc364
-rw-r--r--sql/sql_view.h2
-rw-r--r--sql/sql_yacc.yy15993
-rw-r--r--sql/stacktrace.c589
-rw-r--r--sql/stacktrace.h62
-rw-r--r--sql/strfunc.cc111
-rw-r--r--sql/structs.h219
-rw-r--r--sql/table.cc2710
-rw-r--r--sql/table.h942
-rw-r--r--sql/thr_malloc.cc74
-rw-r--r--sql/time.cc272
-rw-r--r--sql/tzfile.h16
-rw-r--r--sql/tztime.cc333
-rw-r--r--sql/tztime.h49
-rw-r--r--sql/udf_example.c2
-rw-r--r--sql/uniques.cc19
-rw-r--r--sql/unireg.cc366
-rw-r--r--sql/unireg.h40
-rwxr-xr-xsql/watchdog_mysqld126
240 files changed, 152478 insertions, 70067 deletions
diff --git a/sql/CMakeLists.txt b/sql/CMakeLists.txt
index 860afb42a27..cfd049e1864 100755
--- a/sql/CMakeLists.txt
+++ b/sql/CMakeLists.txt
@@ -25,115 +25,138 @@ INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/include
${CMAKE_SOURCE_DIR}/sql
${CMAKE_SOURCE_DIR}/regex
${CMAKE_SOURCE_DIR}/zlib
- ${CMAKE_SOURCE_DIR}/bdb/build_win32
- ${CMAKE_SOURCE_DIR}/bdb/dbinc)
+)
SET_SOURCE_FILES_PROPERTIES(${CMAKE_SOURCE_DIR}/sql/sql_yacc.h
${CMAKE_SOURCE_DIR}/sql/sql_yacc.cc
${CMAKE_SOURCE_DIR}/include/mysql_version.h
+ ${CMAKE_SOURCE_DIR}/sql/sql_builtin.cc
${CMAKE_SOURCE_DIR}/sql/lex_hash.h
${PROJECT_SOURCE_DIR}/include/mysqld_error.h
${PROJECT_SOURCE_DIR}/include/mysqld_ername.h
${PROJECT_SOURCE_DIR}/include/sql_state.h
PROPERTIES GENERATED 1)
-ADD_DEFINITIONS(-DHAVE_INNOBASE -DMYSQL_SERVER
- -D_CONSOLE -DHAVE_DLOPEN)
-
-IF(DISABLE_GRANT_OPTIONS)
- ADD_DEFINITIONS(-DDISABLE_GRANT_OPTIONS)
-ENDIF(DISABLE_GRANT_OPTIONS)
-
-ADD_EXECUTABLE(mysqld${MYSQLD_EXE_SUFFIX}
- ../sql-common/client.c derror.cc des_key_file.cc
- discover.cc ../libmysql/errmsg.c field.cc stacktrace.c stacktrace.h field_conv.cc
- filesort.cc gstream.cc ha_blackhole.cc
- ha_archive.cc ha_heap.cc ha_myisam.cc ha_myisammrg.cc
- ha_innodb.cc ha_federated.cc ha_berkeley.cc
+ADD_DEFINITIONS(-DMYSQL_SERVER -D_CONSOLE -DHAVE_DLOPEN -DHAVE_EVENT_SCHEDULER)
+
+ADD_EXECUTABLE(mysqld
+ ../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
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
- item_uniq.cc key.cc log.cc lock.cc log_event.cc message.rc
+ key.cc log.cc lock.cc message.rc
+ 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
- ../myisammrg/myrg_rnext_same.c mysqld.cc net_serv.cc
+ mysqld.cc net_serv.cc
nt_servc.cc nt_servc.h 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 set_var.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_lex.cc
sql_list.cc sql_load.cc sql_manager.cc sql_map.cc sql_parse.cc
- sql_prepare.cc sql_rename.cc
+ sql_partition.cc sql_plugin.cc sql_prepare.cc sql_rename.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
- ../sql-common/my_user.c
- sql_locale.cc
+ 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
${PROJECT_SOURCE_DIR}/sql/sql_yacc.cc
- ${PROJECT_SOURCE_DIR}/sql/sql_yacc.h
- ${PROJECT_SOURCE_DIR}/include/mysqld_error.h
- ${PROJECT_SOURCE_DIR}/include/mysqld_ername.h
- ${PROJECT_SOURCE_DIR}/include/sql_state.h
- ${PROJECT_SOURCE_DIR}/include/mysql_version.h
- ${PROJECT_SOURCE_DIR}/sql/lex_hash.h)
-
-TARGET_LINK_LIBRARIES(mysqld${MYSQLD_EXE_SUFFIX}
- heap myisam myisammrg mysys yassl zlib debug dbug yassl
- taocrypt strings vio regex wsock32)
+ ${PROJECT_SOURCE_DIR}/sql/sql_yacc.h
+ ${PROJECT_SOURCE_DIR}/include/mysqld_error.h
+ ${PROJECT_SOURCE_DIR}/include/mysqld_ername.h
+ ${PROJECT_SOURCE_DIR}/include/sql_state.h
+ ${PROJECT_SOURCE_DIR}/include/mysql_version.h
+ ${PROJECT_SOURCE_DIR}/sql/sql_builtin.cc
+ ${PROJECT_SOURCE_DIR}/sql/lex_hash.h)
+
+TARGET_LINK_LIBRARIES(mysqld
+ heap myisam myisammrg mysys yassl zlib debug dbug yassl
+ taocrypt strings vio regex wsock32 ws2_32)
+
+SET_TARGET_PROPERTIES(mysqld PROPERTIES OUTPUT_NAME mysqld${MYSQLD_EXE_SUFFIX})
+
+IF(cmake_version EQUAL 20406)
+# Work around for 2.4.6 bug, OUTPUT_NAME will not set the right .PDB
+# file name. Note that COMPILE_FLAGS set some temporary pdb during build,
+# LINK_FLAGS sets the real one.
+SET_TARGET_PROPERTIES(mysqld PROPERTIES
+ COMPILE_FLAGS "/Fd${CMAKE_CFG_INTDIR}/mysqld${MYSQLD_EXE_SUFFIX}.pdb"
+ LINK_FLAGS "/PDB:${CMAKE_CFG_INTDIR}/mysqld${MYSQLD_EXE_SUFFIX}.pdb")
+ENDIF(cmake_version EQUAL 20406)
IF(EMBED_MANIFESTS)
MYSQL_EMBED_MANIFEST("mysqld" "asInvoker")
ENDIF(EMBED_MANIFESTS)
-
+IF(WITH_ARCHIVE_STORAGE_ENGINE)
+ TARGET_LINK_LIBRARIES(mysqld archive)
+ENDIF(WITH_ARCHIVE_STORAGE_ENGINE)
+IF(WITH_BLACKHOLE_STORAGE_ENGINE)
+ TARGET_LINK_LIBRARIES(mysqld blackhole)
+ENDIF(WITH_BLACKHOLE_STORAGE_ENGINE)
+IF(WITH_CSV_STORAGE_ENGINE)
+ TARGET_LINK_LIBRARIES(mysqld csv)
+ENDIF(WITH_CSV_STORAGE_ENGINE)
IF(WITH_EXAMPLE_STORAGE_ENGINE)
- TARGET_LINK_LIBRARIES(mysqld${MYSQLD_EXE_SUFFIX} example)
+ TARGET_LINK_LIBRARIES(mysqld example)
ENDIF(WITH_EXAMPLE_STORAGE_ENGINE)
-
+IF(WITH_FEDERATED_STORAGE_ENGINE)
+ TARGET_LINK_LIBRARIES(mysqld federated)
+ENDIF(WITH_FEDERATED_STORAGE_ENGINE)
IF(WITH_INNOBASE_STORAGE_ENGINE)
- TARGET_LINK_LIBRARIES(mysqld${MYSQLD_EXE_SUFFIX} innobase)
+ TARGET_LINK_LIBRARIES(mysqld innobase)
ENDIF(WITH_INNOBASE_STORAGE_ENGINE)
-IF(WITH_BERKELEY_STORAGE_ENGINE)
- TARGET_LINK_LIBRARIES(mysqld${MYSQLD_EXE_SUFFIX} bdb)
-ENDIF(WITH_BERKELEY_STORAGE_ENGINE)
-
+ADD_DEPENDENCIES(mysqld GenError)
-ADD_DEPENDENCIES(mysqld${MYSQLD_EXE_SUFFIX} GenError)
+# NOTE CMake 2.4.6 creates strange dependencies between files in OUTPUT,
+# so for now we only list one if more than one
# Sql Parser custom command
ADD_CUSTOM_COMMAND(
- SOURCE ${PROJECT_SOURCE_DIR}/sql/sql_yacc.yy
- OUTPUT ${PROJECT_SOURCE_DIR}/sql/sql_yacc.cc
- COMMAND bison.exe ARGS -y -p MYSQL --defines=sql_yacc.h
- --output=sql_yacc.cc sql_yacc.yy
- DEPENDS ${PROJECT_SOURCE_DIR}/sql/sql_yacc.yy)
+ OUTPUT ${PROJECT_SOURCE_DIR}/sql/sql_yacc.h
+# ${PROJECT_SOURCE_DIR}/sql/sql_yacc.cc
+ COMMAND bison.exe ARGS -y -p MYSQL --defines=sql_yacc.h
+ --output=sql_yacc.cc sql_yacc.yy
+ DEPENDS ${PROJECT_SOURCE_DIR}/sql/sql_yacc.yy)
-ADD_CUSTOM_COMMAND(
- OUTPUT ${PROJECT_SOURCE_DIR}/sql/sql_yacc.h
- COMMAND echo
- DEPENDS ${PROJECT_SOURCE_DIR}/sql/sql_yacc.cc
-)
# Gen_lex_hash
-# About "mysqlclient_notls", see note in "client/CMakeLists.txt"
ADD_EXECUTABLE(gen_lex_hash gen_lex_hash.cc)
-TARGET_LINK_LIBRARIES(gen_lex_hash mysqlclient_notls wsock32)
+TARGET_LINK_LIBRARIES(gen_lex_hash debug dbug mysqlclient wsock32)
GET_TARGET_PROPERTY(GEN_LEX_HASH_EXE gen_lex_hash LOCATION)
ADD_CUSTOM_COMMAND(
- OUTPUT ${PROJECT_SOURCE_DIR}/sql/lex_hash.h
- COMMAND ${GEN_LEX_HASH_EXE} ARGS > lex_hash.h
- DEPENDS ${GEN_LEX_HASH_EXE}
-)
-ADD_DEPENDENCIES(mysqld${MYSQLD_EXE_SUFFIX} gen_lex_hash)
+ OUTPUT ${PROJECT_SOURCE_DIR}/sql/lex_hash.h
+ COMMAND ${GEN_LEX_HASH_EXE} ARGS > lex_hash.h
+ DEPENDS ${GEN_LEX_HASH_EXE})
+
+ADD_CUSTOM_TARGET(
+ GenServerSource ALL
+ DEPENDS ${PROJECT_SOURCE_DIR}/sql/sql_yacc.h
+# ${PROJECT_SOURCE_DIR}/sql/sql_yacc.cc
+ ${PROJECT_SOURCE_DIR}/sql/message.h
+# ${PROJECT_SOURCE_DIR}/sql/message.rc
+ ${PROJECT_SOURCE_DIR}/sql/lex_hash.h)
+
+ADD_DEPENDENCIES(mysqld GenServerSource)
# 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")
ADD_LIBRARY(udf_example MODULE udf_example.c udf_example.def)
-ADD_DEPENDENCIES(udf_example strings)
+ADD_DEPENDENCIES(udf_example strings GenError)
TARGET_LINK_LIBRARIES(udf_example strings wsock32)
diff --git a/sql/Makefile.am b/sql/Makefile.am
index 96cdd04d5ba..e477a6123ec 100644
--- a/sql/Makefile.am
+++ b/sql/Makefile.am
@@ -18,118 +18,145 @@
MYSQLDATAdir = $(localstatedir)
MYSQLSHAREdir = $(pkgdatadir)
MYSQLBASEdir= $(prefix)
+MYSQLLIBdir= $(pkglibdir)
+pkgplugindir = $(pkglibdir)/plugin
INCLUDES = @ZLIB_INCLUDES@ \
- @bdb_includes@ @innodb_includes@ @ndbcluster_includes@ \
-I$(top_builddir)/include -I$(top_srcdir)/include \
- -I$(top_srcdir)/regex -I$(srcdir) \
- $(openssl_includes)
+ -I$(top_srcdir)/regex -I$(srcdir) $(openssl_includes)
WRAPLIBS= @WRAPLIBS@
SUBDIRS = share
libexec_PROGRAMS = mysqld
EXTRA_PROGRAMS = gen_lex_hash
bin_PROGRAMS = mysql_tzinfo_to_sql
-gen_lex_hash_LDFLAGS = @NOINST_LDFLAGS@
-LDADD = $(top_builddir)/myisam/libmyisam.a \
- $(top_builddir)/myisammrg/libmyisammrg.a \
- $(top_builddir)/heap/libheap.a \
- $(top_builddir)/vio/libvio.a \
+
+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.a \
- $(top_builddir)/strings/libmystrings.a @ZLIB_LIBS@ @NDB_SCI_LIBS@
-
-mysqld_LDADD = @MYSQLD_EXTRA_LDFLAGS@ \
- @bdb_libs@ @innodb_libs@ @pstack_libs@ \
- @innodb_system_libs@ \
- @ndbcluster_libs@ @ndbcluster_system_libs@ \
+ $(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@ \
+ @pstack_libs@ \
+ @mysql_plugin_libs@ \
$(LDADD) $(CXXLDFLAGS) $(WRAPLIBS) @LIBDL@ \
$(yassl_libs) $(openssl_libs) @MYSQLD_EXTRA_LIBS@
noinst_HEADERS = item.h item_func.h item_sum.h item_cmpfunc.h \
- item_strfunc.h item_timefunc.h item_uniq.h \
+ item_strfunc.h item_timefunc.h \
+ item_xmlfunc.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_manager.h sql_map.h sql_string.h unireg.h \
+ sql_map.h sql_string.h unireg.h \
sql_error.h field.h handler.h mysqld_suffix.h \
- ha_myisammrg.h\
- ha_heap.h ha_myisam.h ha_berkeley.h ha_innodb.h \
+ sql_profile.h \
ha_ndbcluster.h ha_ndbcluster_cond.h \
- opt_range.h protocol.h \
- sql_select.h structs.h table.h sql_udf.h hash_filo.h\
+ ha_ndbcluster_binlog.h ha_ndbcluster_tables.h \
+ ha_partition.h rpl_constants.h \
+ opt_range.h protocol.h rpl_tblmap.h rpl_utility.h \
+ rpl_reporting.h \
+ log.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 \
- log_event.h sql_repl.h slave.h \
- stacktrace.h sql_sort.h sql_cache.h set_var.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\
+ 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 \
- sql_array.h sql_cursor.h \
- examples/ha_example.h ha_archive.h \
- examples/ha_tina.h ha_blackhole.h \
- ha_federated.h
-mysqld_SOURCES = sql_lex.cc sql_handler.cc \
+ 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
+
+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_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 \
- set_var.cc sql_parse.cc sql_yacc.yy \
+ sql_connect.cc scheduler.cc sql_parse.cc \
+ set_var.cc sql_yacc.yy \
sql_base.cc table.cc sql_select.cc sql_insert.cc \
- sql_prepare.cc sql_error.cc sql_locale.cc \
+ sql_profile.cc \
+ sql_prepare.cc sql_error.cc sql_locale.cc \
sql_update.cc sql_delete.cc uniques.cc sql_do.cc \
- procedure.cc item_uniq.cc sql_test.cc \
- log.cc log_event.cc init.cc derror.cc sql_acl.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_sum.cc \
records.cc filesort.cc handler.cc \
- ha_heap.cc ha_myisam.cc ha_myisammrg.cc \
- ha_berkeley.cc ha_innodb.cc \
- ha_ndbcluster.cc ha_ndbcluster_cond.cc \
+ ha_partition.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 sql_union.cc sql_derived.cc \
- client.c sql_client.cc mini_client_errors.c pack.c\
- stacktrace.c repl_failsafe.h repl_failsafe.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_time.c my_user.c my_decimal.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 \
- examples/ha_example.cc ha_archive.cc \
- examples/ha_tina.cc ha_blackhole.cc \
- ha_federated.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
+
+nodist_mysqld_SOURCES = mini_client_errors.c pack.c client.c my_time.c my_user.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_LDADD = $(LDADD) $(CXXLDFLAGS)
-mysql_tzinfo_to_sql_SOURCES = mysql_tzinfo_to_sql.cc
-mysql_tzinfo_to_sql_LDADD = @MYSQLD_EXTRA_LDFLAGS@ $(LDADD) $(CXXLDFLAGS)
+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)\"" \
-DDATADIR="\"$(MYSQLDATAdir)\"" \
-DSHAREDIR="\"$(MYSQLSHAREdir)\"" \
+ -DPLUGINDIR="\"$(pkgplugindir)\"" \
+ -DHAVE_EVENT_SCHEDULER \
@DEFS@
-BUILT_SOURCES = sql_yacc.cc sql_yacc.h lex_hash.h
-EXTRA_DIST = $(BUILT_SOURCES) nt_servc.cc nt_servc.h \
- message.mc message.h message.rc MSG00001.bin \
- examples/CMakeLists.txt CMakeLists.txt \
- udf_example.c udf_example.def
-DISTCLEANFILES = lex_hash.h sql_yacc.output
+BUILT_MAINT_SRC = sql_yacc.cc sql_yacc.h
+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 \
+ message.mc message.h message.rc MSG00001.bin \
+ CMakeLists.txt
-AM_YFLAGS = -d --debug --verbose
+CLEANFILES = lex_hash.h sql_yacc.output link_sources
+DISTCLEANFILES = $(EXTRA_PROGRAMS)
+MAINTAINERCLEANFILES = $(BUILT_MAINT_SRC)
+AM_YFLAGS = -d --verbose
-mysql_tzinfo_to_sql.cc:
- rm -f mysql_tzinfo_to_sql.cc
- @LN_CP_F@ $(srcdir)/tztime.cc mysql_tzinfo_to_sql.cc
-
-link_sources: mysql_tzinfo_to_sql.cc
+# 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
@@ -140,25 +167,7 @@ link_sources: mysql_tzinfo_to_sql.cc
@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
-
-mysql_tzinfo_to_sql.o: $(mysql_tzinfo_to_sql_SOURCES)
- $(CXXCOMPILE) -c $(INCLUDES) -DTZINFO2SQL $<
-
-# Try to get better dependencies for the grammar. Othervise really bad
-# things like different grammars for different pars of MySQL can
-# happen if you are unlucky.
-sql_yacc.cc: sql_yacc.yy
-sql_yacc.h: sql_yacc.yy
-
-# Be careful here, note that we use VPATH and might or might not have
-# a pregenerated "sql_yacc.cc" in $(srcdir) or one we just built in
-# $(builddir). And it has to work if $(srcdir) == $(builddir).
-sql_yacc.o: sql_yacc.cc sql_yacc.h $(HEADERS)
- @SED@ -e 's/__attribute__ ((__unused__))//' $< > sql_yacc.cc-new
- @MV@ sql_yacc.cc-new sql_yacc.cc
- @echo "Note: The following compile may take a long time."
- @echo "If it fails, re-run configure with --with-low-memory"
- $(CXXCOMPILE) $(LM_CFLAGS) -c sql_yacc.cc
+ echo timestamp > link_sources
# This generates lex_hash.h
# NOTE Built sources should depend on their sources not the tool
@@ -169,10 +178,15 @@ lex_hash.h: gen_lex_hash.cc lex.h
$(MV) $@-t $@
# For testing of udf_example.so
-noinst_LTLIBRARIES= udf_example.la
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)
# Don't update the files from bitkeeper
%::SCCS/s.%
diff --git a/sql/authors.h b/sql/authors.h
new file mode 100644
index 00000000000..dfe3b143e2f
--- /dev/null
+++ b/sql/authors.h
@@ -0,0 +1,152 @@
+/* Copyright (C) 2005-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 St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+/* Structure of the name list */
+
+struct show_table_authors_st {
+ const char *name;
+ const char *location;
+ const char *comment;
+};
+
+/*
+ Output from "SHOW AUTHORS"
+
+ If you can update it, you get to be in it :)
+
+ Don't be offended if your name is not in here, just add it!
+
+ IMPORTANT: Names should be added in alphabetical order (by last name).
+
+ Names should be encoded using UTF-8.
+*/
+
+struct show_table_authors_st show_table_authors[]= {
+ { "Brian (Krow) Aker", "Seattle, WA, USA",
+ "Architecture, archive, federated, bunch of little stuff :)" },
+ { "Venu Anuganti", "", "Client/server protocol (4.1)" },
+ { "David Axmark", "Uppsala, Sweden",
+ "Small stuff long time ago, Monty ripped it out!" },
+ { "Alexander (Bar) Barkov", "Izhevsk, Russia",
+ "Unicode and character sets (4.1)" },
+ { "Omer BarNir", "Sunnyvale, CA, USA",
+ "Testing (sometimes) and general QA stuff" },
+ { "Guilhem Bichot", "Bordeaux, France", "Replication (since 4.0)" },
+ { "John Birrell", "", "Emulation of pthread_mutex() for OS/2" },
+ { "Andreas F. Bobak", "", "AGGREGATE extension to user-defined functions" },
+ { "Alexey Botchkov (Holyfoot)", "Izhevsk, Russia",
+ "GIS extensions (4.1), embedded server (4.1), precision math (5.0)"},
+ { "Reggie Burnett", "Nashville, TN, USA", "Windows development, Connectors" },
+ { "Oleksandr Byelkin", "Lugansk, Ukraine",
+ "Query Cache (4.0), Subqueries (4.1), Views (5.0)" },
+ { "Kent Boortz", "Orebro, Sweden", "Test platform, and general build stuff" },
+ { "Tim Bunce", "", "mysqlhotcopy" },
+ { "Yves Carlier", "", "mysqlaccess" },
+ { "Joshua Chamas", "Cupertino, CA, USA",
+ "Concurrent insert, extended date syntax" },
+ { "Petr Chardin", "Moscow, Russia",
+ "Instance Manager (5.0), Server log tables (5.1)" },
+ { "Wei-Jou Chen", "", "Chinese (Big5) character set" },
+ { "Albert Chin-A-Young", "",
+ "Tru64 port, large file support, better TCP wrappers support" },
+ { "Jorge del Conde", "Mexico City, Mexico", "Windows development" },
+ { "Antony T. Curtis", "Norwalk, CA, USA",
+ "Parser, port to OS/2, storage engines and some random stuff" },
+ { "Yuri Dario", "", "OS/2 port" },
+ { "Andrei Elkin", "Espoo, Finland", "Replication" },
+ { "Patrick Galbraith", "Sharon, NH", "Federated Engine, mysqlslap" },
+ { "Sergei Golubchik", "Kerpen, Germany",
+ "Full-text search, precision math" },
+ { "Lenz Grimmer", "Hamburg, Germany",
+ "Production (build and release) engineering" },
+ { "Nikolay Grishakin", "Austin, TX, USA", "Testing - Server" },
+ { "Wei He", "", "Chinese (GBK) character set" },
+ { "Eric Herman", "Amsterdam, Netherlands", "Bug fixing - federated" },
+ { "Andrey Hristov", "Walldorf, Germany", "Event scheduler (5.1)" },
+ { "Alexander (Alexi) Ivanov", "St. Petersburg, Russia", "Replication" },
+ { "Alexander (Salle) Keremidarski", "Sofia, Bulgaria",
+ "Bug fixing" },
+ { "Mats Kindahl", "Storvreta, Sweden", "Replication" },
+ { "Serge Kozlov", "Velikie Luki, Russia", "Testing - Cluster" },
+ { "Hakan Küçükyılmaz", "Walldorf, Germany", "Testing - Server" },
+ { "Greg (Groggy) Lehey", "Uchunga, SA, Australia", "Backup" },
+ { "Matthias Leich", "Berlin, Germany", "Testing - Server" },
+ { "Dmitri Lenev", "Moscow, Russia",
+ "Time zones support (4.1), Triggers (5.0)" },
+ { "Arjen Lentz", "Brisbane, Australia",
+ "Documentation (2001-2004), Dutch error messages, LOG2()" },
+ { "Marc Liyanage", "", "Created Mac OS X packages" },
+ { "Zarko Mocnik", "", "Sorting for Slovenian language" },
+ { "Per-Erik Martin", "Uppsala, Sweden", "Stored Procedures (5.0)" },
+ { "Alexis Mikhailov", "", "User-defined functions" },
+ { "Sinisa Milivojevic", "Larnaca, Cyprus",
+ "UNION (4.0), Subqueries in FROM clause (4.1), many other features" },
+ { "Jonathan (Jeb) Miller", "Kyle, TX, USA",
+ "Testing - Cluster, Replication" },
+ { "Elliot Murphy", "Cocoa, FL, USA", "Replication and backup" },
+ { "Kristian Nielsen", "Copenhagen, Denmark",
+ "General build stuff" },
+ { "Pekka Nouisiainen", "Stockholm, Sweden",
+ "NDB Cluster: BLOB support, character set support, ordered indexes" },
+ { "Alexander Nozdrin", "Moscow, Russia",
+ "Bug fixing (Stored Procedures, 5.0)" },
+ { "Per Eric Olsson", "", "Testing of dynamic record format" },
+ { "Jonas Oreland", "Stockholm, Sweden",
+ "NDB Cluster, Online Backup, lots of other things" },
+ { "Konstantin Osipov", "Moscow, Russia",
+ "Prepared statements (4.1), Cursors (5.0)" },
+ { "Alexander (Sasha) Pachev", "Provo, UT, USA",
+ "Statement-based replication, SHOW CREATE TABLE, mysql-bench" },
+ { "Irena Pancirov", "", "Port to Windows with Borland compiler" },
+ { "Jan Pazdziora", "", "Czech sorting order" },
+ { "Benjamin Pflugmann", "",
+ "Extended MERGE storage engine to handle INSERT" },
+ { "Igor Romanenko", "",
+ "mysqldump" },
+ { "Mikael Ronström", "Stockholm, Sweden",
+ "NDB Cluster, Partitioning (5.1), Optimizations" },
+ { "Tõnu Samuel", "",
+ "VIO interface, other miscellaneous features" },
+ { "Carsten Segieth (Pino)", "Fredersdorf, Germany", "Testing - Server"},
+ { "Martin Sköld", "Stockholm, Sweden",
+ "NDB Cluster: Unique indexes, integration into MySQL" },
+ { "Timothy Smith", "Auckland, New Zealand",
+ "Dynamic character sets, parts of the build system, libmysqld"},
+ { "Miguel Solorzano", "Florianopolis, Santa Catarina, Brazil",
+ "Windows development, Windows NT service"},
+ { "Punita Srivastava", "Austin, TX, USA", "Testing - Merlin"},
+ { "Alexey Stroganov (Ranger)", "Lugansk, Ukraine", "Testing - Benchmarks"},
+ { "Ingo Strüwing", "Berlin, Germany", "Bug fixing" },
+ { "Magnus Svensson", "Öregrund, Sweden",
+ "NDB Cluster: Integration into MySQL, test framework" },
+ { "Zeev Suraski", "", "FROM_UNIXTIME(), ENCRYPT()" },
+ { "TAMITO", "",
+ "The _MB character set macros and UJIS and SJIS character sets" },
+ { "Jani Tolonen", "Helsinki, Finland",
+ "mysqlimport, extensions to command-line clients, PROCEDURE ANALYSE()" },
+ { "Lars Thalmann", "Stockholm, Sweden",
+ "Replication and cluster development" },
+ { "Tomas Ulin", "Stockholm, Sweden",
+ "NDB Cluster: Configuration, installation" },
+ { "Gianmassimo Vigazzola", "", "Initial Windows port" },
+ { "Sergey Vojtovich", "Izhevsk, Russia", "Plugins infrastructure (5.1)" },
+ { "Matt Wagner", "Northfield, MN, USA", "Bug fixing" },
+ { "Jim Winstead Jr.", "Los Angeles, CA, USA", "Bug fixing" },
+ { "Michael (Monty) Widenius", "Tusby, Finland",
+ "Lead developer and main author" },
+ { "Peter Zaitsev", "Tacoma, WA, USA",
+ "SHA1(), AES_ENCRYPT(), AES_DECRYPT(), bug fixing" },
+ {NULL, NULL, NULL}
+};
diff --git a/sql/contributors.h b/sql/contributors.h
new file mode 100644
index 00000000000..87001e29d88
--- /dev/null
+++ b/sql/contributors.h
@@ -0,0 +1,39 @@
+/* Copyright (C) 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 St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+/* Structure of the name list */
+
+struct show_table_contributors_st {
+ const char *name;
+ const char *location;
+ const char *comment;
+};
+
+/*
+ Output from "SHOW CONTRIBUTORS"
+
+ Get permission before editing.
+
+ IMPORTANT: Names should be left in historical order.
+
+ Names should be encoded using UTF-8.
+*/
+
+struct show_table_contributors_st show_table_contributors[]= {
+ {"Ronald Bradford", "Brisbane, Australia", "EFF contribution for UC2006 Auction"},
+ {"Sheeri Kritzer", "Boston, Mass. USA", "EFF contribution for UC2006 Auction"},
+ {"Mark Shuttleworth", "London, UK.", "EFF contribution for UC2006 Auction"},
+ {NULL, NULL, NULL}
+};
diff --git a/sql/derror.cc b/sql/derror.cc
index 0e74d411b1f..a8cfa00ad1d 100644
--- a/sql/derror.cc
+++ b/sql/derror.cc
@@ -14,7 +14,12 @@
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
-/* Read language depeneded messagefile */
+/**
+ @file
+
+ @brief
+ Read language depeneded messagefile
+*/
#include "mysql_priv.h"
#include "mysys_err.h"
@@ -23,23 +28,23 @@ static bool read_texts(const char *file_name,const char ***point,
uint error_messages);
static void init_myfunc_errs(void);
-/*
+/**
Read messages from errorfile.
- SYNOPSIS
- init_errmessage()
-
- DESCRIPTION
- This function can be called multiple times to reload the messages.
+ This function can be called multiple times to reload the messages.
+ If it fails to load the messages, it will fail softly by initializing
+ the errmesg pointer to an array of empty strings or by keeping the
+ old array if it exists.
- RETURN
+ @retval
FALSE OK
+ @retval
TRUE Error
*/
bool init_errmessage(void)
{
- const char **errmsgs;
+ const char **errmsgs, **ptr;
DBUG_ENTER("init_errmessage");
/*
@@ -49,13 +54,20 @@ bool init_errmessage(void)
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))
- DBUG_RETURN(TRUE);
+ if (read_texts(ERRMSG_FILE, &errmsgs, ER_ERROR_LAST - ER_ERROR_FIRST + 1) &&
+ !errmsgs)
+ {
+ if (!(errmsgs= (const char**) my_malloc((ER_ERROR_LAST-ER_ERROR_FIRST+1)*
+ sizeof(char*), MYF(0))))
+ DBUG_RETURN(TRUE);
+ for (ptr= errmsgs; ptr < errmsgs + ER_ERROR_LAST - ER_ERROR_FIRST; ptr++)
+ *ptr= "";
+ }
/* Register messages for use with my_error(). */
if (my_error_register(errmsgs, ER_ERROR_FIRST, ER_ERROR_LAST))
{
- x_free((gptr) errmsgs);
+ x_free((uchar*) errmsgs);
DBUG_RETURN(TRUE);
}
@@ -65,21 +77,28 @@ bool init_errmessage(void)
}
- /* Read text from packed textfile in language-directory */
- /* If we can't read messagefile then it's panic- we can't continue */
+/**
+ 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)
{
register uint i;
- uint count,funktpos,length,textcount;
+ uint count,funktpos,textcount;
+ size_t length;
File file;
char name[FN_REFLEN];
- const char *buff;
+ uchar *buff;
uchar head[32],*pos;
+ const char *errmsg;
DBUG_ENTER("read_texts");
- *point=0; // If something goes wrong
LINT_INIT(buff);
funktpos=0;
if ((file=my_open(fn_format(name,file_name,language,"",4),
@@ -88,7 +107,7 @@ static bool read_texts(const char *file_name,const char ***point,
goto err; /* purecov: inspected */
funktpos=1;
- if (my_read(file,(byte*) head,32,MYF(MY_NABP))) goto err;
+ if (my_read(file,(uchar*) head,32,MYF(MY_NABP))) goto err;
if (head[0] != (uchar) 254 || head[1] != (uchar) 254 ||
head[2] != 2 || head[3] != 1)
goto err; /* purecov: inspected */
@@ -119,25 +138,27 @@ but it should contain at least %d error messages.\n\
Check that the above file is the right version for this program!",
name,count,error_messages);
VOID(my_close(file,MYF(MY_WME)));
- unireg_abort(1);
+ DBUG_RETURN(1);
}
- x_free((gptr) *point); /* Free old language */
+ x_free((uchar*) *point); /* Free old language */
if (!(*point= (const char**)
- my_malloc((uint) (length+count*sizeof(char*)),MYF(0))))
+ my_malloc((size_t) (length+count*sizeof(char*)),MYF(0))))
{
funktpos=2; /* purecov: inspected */
goto err; /* purecov: inspected */
}
- buff= (char*) (*point + count);
+ buff= (uchar*) (*point + count);
- if (my_read(file,(byte*) buff,(uint) count*2,MYF(MY_NABP))) goto err;
- for (i=0, pos= (uchar*) buff ; i< count ; i++)
+ if (my_read(file, buff, (size_t) count*2,MYF(MY_NABP)))
+ goto err;
+ for (i=0, pos= buff ; i< count ; i++)
{
- (*point)[i]=buff+uint2korr(pos);
+ (*point)[i]= (char*) buff+uint2korr(pos);
pos+=2;
}
- if (my_read(file,(byte*) buff,(uint) length,MYF(MY_NABP))) goto err;
+ if (my_read(file, buff, length, MYF(MY_NABP)))
+ goto err;
for (i=1 ; i < textcount ; i++)
{
@@ -149,25 +170,26 @@ Check that the above file is the right version for this program!",
err:
switch (funktpos) {
case 2:
- buff="Not enough memory for messagefile '%s'";
+ errmsg= "Not enough memory for messagefile '%s'";
break;
case 1:
- buff="Can't read from messagefile '%s'";
+ errmsg= "Can't read from messagefile '%s'";
break;
default:
- buff="Can't find messagefile '%s'";
+ errmsg= "Can't find messagefile '%s'";
break;
}
- sql_print_error(buff,name);
+ sql_print_error(errmsg, name);
err1:
if (file != FERR)
VOID(my_close(file,MYF(MY_WME)));
- unireg_abort(1);
- DBUG_RETURN(1); // keep compiler happy
+ DBUG_RETURN(1);
} /* read_texts */
- /* Initiates error-messages used by my_func-library */
+/**
+ Initiates error-messages used by my_func-library.
+*/
static void init_myfunc_errs()
{
diff --git a/sql/des_key_file.cc b/sql/des_key_file.cc
index d99d712b45a..317cb237360 100644
--- a/sql/des_key_file.cc
+++ b/sql/des_key_file.cc
@@ -21,17 +21,18 @@
struct st_des_keyschedule des_keyschedule[10];
uint des_default_key;
-/*
- Function which loads DES keys from plaintext file into memory on MySQL
- server startup and on command FLUSH DES_KEY_FILE.
- Blame tonu@spam.ee on bugs ;)
-
- RETURN
- 0 ok
- 1 Error
+#define des_cs &my_charset_latin1
+
+/**
+ Load DES keys from plaintext file into
+ memory on MySQL server startup and on command FLUSH DES_KEY_FILE.
+
+ @retval
+ 0 ok
+ @retval
+ 1 Error
*/
-#define des_cs &my_charset_latin1
bool
load_des_key_file(const char *file_name)
diff --git a/sql/discover.cc b/sql/discover.cc
index 5d24607cf6b..56dc00cc5c4 100644
--- a/sql/discover.cc
+++ b/sql/discover.cc
@@ -14,39 +14,42 @@
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
-/* Functions for discover of frm file from handler */
+/**
+ @file
+
+ @brief
+ Functions for discover of frm file from handler
+*/
#include "mysql_priv.h"
#include <my_dir.h>
-/*
- Read the contents of a .frm file
+/**
+ Read the contents of a .frm file.
- SYNOPSIS
- readfrm()
+ frmdata and len are set to 0 on error.
- name path to table-file "db/name"
- frmdata frm data
- len length of the read frmdata
+ @param name path to table-file "db/name"
+ @param frmdata frm data
+ @param len length of the read frmdata
- RETURN VALUES
- 0 ok
- 1 Could not open file
- 2 Could not stat file
- 3 Could not allocate data for read
- Could not read file
-
- frmdata and len are set to 0 on error
+ @retval
+ 0 ok
+ @retval
+ 1 Could not open file
+ @retval
+ 2 Could not stat file
+ @retval
+ 3 Could not allocate data for read. Could not read file
*/
-int readfrm(const char *name,
- const void **frmdata, uint *len)
+int readfrm(const char *name, uchar **frmdata, size_t *len)
{
int error;
char index_file[FN_REFLEN];
File file;
- ulong read_len;
- char *read_data;
+ size_t read_len;
+ uchar *read_data;
MY_STAT state;
DBUG_ENTER("readfrm");
DBUG_PRINT("enter",("name: '%s'",name));
@@ -54,7 +57,8 @@ int readfrm(const char *name,
*frmdata= NULL; // In case of errors
*len= 0;
error= 1;
- if ((file=my_open(fn_format(index_file,name,"",reg_ext,4),
+ if ((file=my_open(fn_format(index_file,name,"",reg_ext,
+ MY_UNPACK_FILENAME|MY_APPEND_EXT),
O_RDONLY | O_SHARE,
MYF(0))) < 0)
goto err_end;
@@ -67,12 +71,12 @@ int readfrm(const char *name,
// Read whole frm file
error= 3;
- read_data= 0;
+ read_data= 0; // Nothing to free
if (read_string(file, &read_data, read_len))
goto err;
// Setup return data
- *frmdata= (void*) read_data;
+ *frmdata= (uchar*) read_data;
*len= read_len;
error= 0;
@@ -87,37 +91,35 @@ int readfrm(const char *name,
/*
Write the content of a frm data pointer
- to a frm file
-
- SYNOPSIS
- writefrm()
+ to a frm file.
- name path to table-file "db/name"
- frmdata frm data
- len length of the frmdata
+ @param name path to table-file "db/name"
+ @param frmdata frm data
+ @param len length of the frmdata
- RETURN VALUES
- 0 ok
- 2 Could not write file
+ @retval
+ 0 ok
+ @retval
+ 2 Could not write file
*/
-int writefrm(const char *name, const void *frmdata, uint len)
+int writefrm(const char *name, const uchar *frmdata, size_t len)
{
File file;
char index_file[FN_REFLEN];
int error;
DBUG_ENTER("writefrm");
- DBUG_PRINT("enter",("name: '%s' len: %d ",name,len));
- //DBUG_DUMP("frmdata", (char*)frmdata, len);
+ DBUG_PRINT("enter",("name: '%s' len: %lu ",name, (ulong) len));
error= 0;
- if ((file=my_create(fn_format(index_file,name,"",reg_ext,4),
+ 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 (my_write(file,(byte*)frmdata,len,MYF(MY_WME | MY_NABP)))
+ if (my_write(file, frmdata, len,MYF(MY_WME | MY_NABP)))
error= 2;
+ VOID(my_close(file,MYF(0)));
}
- VOID(my_close(file,MYF(0)));
DBUG_RETURN(error);
} /* writefrm */
diff --git a/sql/event_data_objects.cc b/sql/event_data_objects.cc
new file mode 100644
index 00000000000..0eba357b632
--- /dev/null
+++ b/sql/event_data_objects.cc
@@ -0,0 +1,1583 @@
+/* Copyright (C) 2004-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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#define MYSQL_LEX 1
+#include "mysql_priv.h"
+#include "events.h"
+#include "event_data_objects.h"
+#include "event_db_repository.h"
+#include "sp_head.h"
+
+/**
+ @addtogroup Event_Scheduler
+ @{
+*/
+
+/*************************************************************************/
+
+/**
+ Event_creation_ctx -- creation context of events.
+*/
+
+class Event_creation_ctx :public Stored_program_creation_ctx,
+ public Sql_alloc
+{
+public:
+ static bool load_from_db(THD *thd,
+ MEM_ROOT *event_mem_root,
+ const char *db_name,
+ const char *event_name,
+ TABLE *event_tbl,
+ Stored_program_creation_ctx **ctx);
+
+public:
+ virtual Stored_program_creation_ctx *clone(MEM_ROOT *mem_root)
+ {
+ return new (mem_root)
+ Event_creation_ctx(m_client_cs, m_connection_cl, m_db_cl);
+ }
+
+protected:
+ virtual Object_creation_ctx *create_backup_ctx(THD *thd) const
+ {
+ /*
+ We can avoid usual backup/restore employed in stored programs since we
+ know that this is a top level statement and the worker thread is
+ allocated exclusively to execute this event.
+ */
+
+ return NULL;
+ }
+
+private:
+ Event_creation_ctx(CHARSET_INFO *client_cs,
+ CHARSET_INFO *connection_cl,
+ CHARSET_INFO *db_cl)
+ : Stored_program_creation_ctx(client_cs, connection_cl, db_cl)
+ { }
+};
+
+/**************************************************************************
+ Event_creation_ctx implementation.
+**************************************************************************/
+
+bool
+Event_creation_ctx::load_from_db(THD *thd,
+ MEM_ROOT *event_mem_root,
+ const char *db_name,
+ const char *event_name,
+ TABLE *event_tbl,
+ Stored_program_creation_ctx **ctx)
+{
+ /* Load character set/collation attributes. */
+
+ CHARSET_INFO *client_cs;
+ CHARSET_INFO *connection_cl;
+ CHARSET_INFO *db_cl;
+
+ bool invalid_creation_ctx= FALSE;
+
+ if (load_charset(event_mem_root,
+ event_tbl->field[ET_FIELD_CHARACTER_SET_CLIENT],
+ thd->variables.character_set_client,
+ &client_cs))
+ {
+ sql_print_warning("Event '%s'.'%s': invalid value "
+ "in column mysql.event.character_set_client.",
+ (const char *) db_name,
+ (const char *) event_name);
+
+ invalid_creation_ctx= TRUE;
+ }
+
+ if (load_collation(event_mem_root,
+ event_tbl->field[ET_FIELD_COLLATION_CONNECTION],
+ thd->variables.collation_connection,
+ &connection_cl))
+ {
+ sql_print_warning("Event '%s'.'%s': invalid value "
+ "in column mysql.event.collation_connection.",
+ (const char *) db_name,
+ (const char *) event_name);
+
+ invalid_creation_ctx= TRUE;
+ }
+
+ if (load_collation(event_mem_root,
+ event_tbl->field[ET_FIELD_DB_COLLATION],
+ NULL,
+ &db_cl))
+ {
+ sql_print_warning("Event '%s'.'%s': invalid value "
+ "in column mysql.event.db_collation.",
+ (const char *) db_name,
+ (const char *) event_name);
+
+ invalid_creation_ctx= TRUE;
+ }
+
+ /*
+ If we failed to resolve the database collation, load the default one
+ from the disk.
+ */
+
+ if (!db_cl)
+ db_cl= get_default_db_collation(thd, db_name);
+
+ /* Create the context. */
+
+ *ctx= new Event_creation_ctx(client_cs, connection_cl, db_cl);
+
+ return invalid_creation_ctx;
+}
+
+/*************************************************************************/
+
+/*
+ Initiliazes dbname and name of an Event_queue_element_for_exec
+ object
+
+ SYNOPSIS
+ Event_queue_element_for_exec::init()
+
+ RETURN VALUE
+ FALSE OK
+ TRUE Error (OOM)
+*/
+
+bool
+Event_queue_element_for_exec::init(LEX_STRING db, LEX_STRING n)
+{
+ if (!(dbname.str= my_strndup(db.str, dbname.length= db.length, MYF(MY_WME))))
+ return TRUE;
+ if (!(name.str= my_strndup(n.str, name.length= n.length, MYF(MY_WME))))
+ {
+ my_free((uchar*) dbname.str, MYF(0));
+ return TRUE;
+ }
+ return FALSE;
+}
+
+
+/*
+ Destructor
+
+ SYNOPSIS
+ Event_queue_element_for_exec::~Event_queue_element_for_exec()
+*/
+
+Event_queue_element_for_exec::~Event_queue_element_for_exec()
+{
+ my_free((uchar*) dbname.str, MYF(0));
+ my_free((uchar*) name.str, MYF(0));
+}
+
+
+/*
+ Constructor
+
+ SYNOPSIS
+ Event_basic::Event_basic()
+*/
+
+Event_basic::Event_basic()
+{
+ DBUG_ENTER("Event_basic::Event_basic");
+ /* init memory root */
+ init_alloc_root(&mem_root, 256, 512);
+ dbname.str= name.str= NULL;
+ dbname.length= name.length= 0;
+ time_zone= NULL;
+ DBUG_VOID_RETURN;
+}
+
+
+/*
+ Destructor
+
+ SYNOPSIS
+ Event_basic::Event_basic()
+*/
+
+Event_basic::~Event_basic()
+{
+ DBUG_ENTER("Event_basic::~Event_basic");
+ free_root(&mem_root, MYF(0));
+ DBUG_VOID_RETURN;
+}
+
+
+/*
+ Short function to load a char column into a LEX_STRING
+
+ SYNOPSIS
+ Event_basic::load_string_field()
+ field_name The field( enum_events_table_field is not actually used
+ because it's unknown in event_data_objects.h)
+ fields The Field array
+ field_value The value
+*/
+
+bool
+Event_basic::load_string_fields(Field **fields, ...)
+{
+ bool ret= FALSE;
+ va_list args;
+ enum enum_events_table_field field_name;
+ LEX_STRING *field_value;
+
+ DBUG_ENTER("Event_basic::load_string_fields");
+
+ va_start(args, fields);
+ field_name= (enum enum_events_table_field) va_arg(args, int);
+ while (field_name < ET_FIELD_COUNT)
+ {
+ field_value= va_arg(args, LEX_STRING *);
+ if ((field_value->str= get_field(&mem_root, fields[field_name])) == NullS)
+ {
+ ret= TRUE;
+ break;
+ }
+ field_value->length= strlen(field_value->str);
+
+ field_name= (enum enum_events_table_field) va_arg(args, int);
+ }
+ va_end(args);
+
+ DBUG_RETURN(ret);
+}
+
+
+bool
+Event_basic::load_time_zone(THD *thd, const LEX_STRING tz_name)
+{
+ String str(tz_name.str, &my_charset_latin1);
+ time_zone= my_tz_find(thd, &str);
+
+ return (time_zone == NULL);
+}
+
+
+/*
+ Constructor
+
+ SYNOPSIS
+ Event_queue_element::Event_queue_element()
+*/
+
+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)
+{
+ DBUG_ENTER("Event_queue_element::Event_queue_element");
+
+ starts= ends= execute_at= last_executed= 0;
+ starts_null= ends_null= execute_at_null= TRUE;
+
+ DBUG_VOID_RETURN;
+}
+
+
+/*
+ Destructor
+
+ SYNOPSIS
+ Event_queue_element::Event_queue_element()
+*/
+Event_queue_element::~Event_queue_element()
+{
+}
+
+
+/*
+ Constructor
+
+ SYNOPSIS
+ Event_timed::Event_timed()
+*/
+
+Event_timed::Event_timed():
+ created(0), modified(0), sql_mode(0)
+{
+ DBUG_ENTER("Event_timed::Event_timed");
+ init();
+ DBUG_VOID_RETURN;
+}
+
+
+/*
+ Destructor
+
+ SYNOPSIS
+ Event_timed::~Event_timed()
+*/
+
+Event_timed::~Event_timed()
+{
+}
+
+
+/*
+ Constructor
+
+ SYNOPSIS
+ Event_job_data::Event_job_data()
+*/
+
+Event_job_data::Event_job_data()
+ :sql_mode(0)
+{
+}
+
+/*
+ Init all member variables
+
+ SYNOPSIS
+ Event_timed::init()
+*/
+
+void
+Event_timed::init()
+{
+ DBUG_ENTER("Event_timed::init");
+
+ definer_user.str= definer_host.str= body.str= comment.str= NULL;
+ definer_user.length= definer_host.length= body.length= comment.length= 0;
+
+ sql_mode= 0;
+
+ DBUG_VOID_RETURN;
+}
+
+
+/**
+ Load an event's body from a row from mysql.event.
+
+ @details This method is silent on errors and should behave like that.
+ Callers should handle throwing of error messages. The reason is that the
+ class should not know about how to deal with communication.
+
+ @return Operation status
+ @retval FALSE OK
+ @retval TRUE Error
+*/
+
+bool
+Event_job_data::load_from_row(THD *thd, TABLE *table)
+{
+ char *ptr;
+ size_t len;
+ LEX_STRING tz_name;
+
+ DBUG_ENTER("Event_job_data::load_from_row");
+
+ if (!table)
+ DBUG_RETURN(TRUE);
+
+ if (table->s->fields < ET_FIELD_COUNT)
+ DBUG_RETURN(TRUE);
+
+ if (load_string_fields(table->field,
+ ET_FIELD_DB, &dbname,
+ ET_FIELD_NAME, &name,
+ ET_FIELD_BODY, &body,
+ ET_FIELD_DEFINER, &definer,
+ ET_FIELD_TIME_ZONE, &tz_name,
+ ET_FIELD_COUNT))
+ DBUG_RETURN(TRUE);
+
+ if (load_time_zone(thd, tz_name))
+ DBUG_RETURN(TRUE);
+
+ Event_creation_ctx::load_from_db(thd, &mem_root, dbname.str, name.str, table,
+ &creation_ctx);
+
+ ptr= strchr(definer.str, '@');
+
+ if (! ptr)
+ ptr= definer.str;
+
+ len= ptr - definer.str;
+ definer_user.str= strmake_root(&mem_root, definer.str, len);
+ definer_user.length= len;
+ len= definer.length - len - 1;
+ /* 1:because of @ */
+ definer_host.str= strmake_root(&mem_root, ptr + 1, len);
+ definer_host.length= len;
+
+ sql_mode= (ulong) table->field[ET_FIELD_SQL_MODE]->val_int();
+
+ DBUG_RETURN(FALSE);
+}
+
+
+/**
+ Load an event's body from a row from mysql.event.
+
+ @details This method is silent on errors and should behave like that.
+ Callers should handle throwing of error messages. The reason is that the
+ class should not know about how to deal with communication.
+
+ @return Operation status
+ @retval FALSE OK
+ @retval TRUE Error
+*/
+
+bool
+Event_queue_element::load_from_row(THD *thd, TABLE *table)
+{
+ char *ptr;
+ MYSQL_TIME time;
+ LEX_STRING tz_name;
+
+ DBUG_ENTER("Event_queue_element::load_from_row");
+
+ if (!table)
+ DBUG_RETURN(TRUE);
+
+ if (table->s->fields < ET_FIELD_COUNT)
+ DBUG_RETURN(TRUE);
+
+ if (load_string_fields(table->field,
+ ET_FIELD_DB, &dbname,
+ ET_FIELD_NAME, &name,
+ ET_FIELD_DEFINER, &definer,
+ ET_FIELD_TIME_ZONE, &tz_name,
+ ET_FIELD_COUNT))
+ DBUG_RETURN(TRUE);
+
+ if (load_time_zone(thd, tz_name))
+ DBUG_RETURN(TRUE);
+
+ starts_null= table->field[ET_FIELD_STARTS]->is_null();
+ my_bool not_used= FALSE;
+ if (!starts_null)
+ {
+ table->field[ET_FIELD_STARTS]->get_date(&time, TIME_NO_ZERO_DATE);
+ starts= my_tz_OFFSET0->TIME_to_gmt_sec(&time,&not_used);
+ }
+
+ ends_null= table->field[ET_FIELD_ENDS]->is_null();
+ if (!ends_null)
+ {
+ table->field[ET_FIELD_ENDS]->get_date(&time, TIME_NO_ZERO_DATE);
+ ends= my_tz_OFFSET0->TIME_to_gmt_sec(&time,&not_used);
+ }
+
+ if (!table->field[ET_FIELD_INTERVAL_EXPR]->is_null())
+ expression= table->field[ET_FIELD_INTERVAL_EXPR]->val_int();
+ else
+ expression= 0;
+ /*
+ If neigher STARTS and ENDS is set, then both fields are empty.
+ Hence, if ET_FIELD_EXECUTE_AT is empty there is an error.
+ */
+ execute_at_null= table->field[ET_FIELD_EXECUTE_AT]->is_null();
+ DBUG_ASSERT(!(starts_null && ends_null && !expression && execute_at_null));
+ if (!expression && !execute_at_null)
+ {
+ if (table->field[ET_FIELD_EXECUTE_AT]->get_date(&time,
+ TIME_NO_ZERO_DATE))
+ DBUG_RETURN(TRUE);
+ execute_at= my_tz_OFFSET0->TIME_to_gmt_sec(&time,&not_used);
+ }
+
+ /*
+ We load the interval type from disk as string and then map it to
+ an integer. This decouples the values of enum interval_type
+ and values actually stored on disk. Therefore the type can be
+ reordered without risking incompatibilities of data between versions.
+ */
+ if (!table->field[ET_FIELD_TRANSIENT_INTERVAL]->is_null())
+ {
+ int i;
+ char buff[MAX_FIELD_WIDTH];
+ String str(buff, sizeof(buff), &my_charset_bin);
+ LEX_STRING tmp;
+
+ table->field[ET_FIELD_TRANSIENT_INTERVAL]->val_str(&str);
+ if (!(tmp.length= str.length()))
+ DBUG_RETURN(TRUE);
+
+ tmp.str= str.c_ptr_safe();
+
+ i= find_string_in_array(interval_type_to_name, &tmp, system_charset_info);
+ if (i < 0)
+ DBUG_RETURN(TRUE);
+ interval= (interval_type) i;
+ }
+
+ if (!table->field[ET_FIELD_LAST_EXECUTED]->is_null())
+ {
+ table->field[ET_FIELD_LAST_EXECUTED]->get_date(&time,
+ 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);
+
+ DBUG_PRINT("load_from_row", ("Event [%s] is [%s]", name.str, ptr));
+
+ /* Set event status (ENABLED | SLAVESIDE_DISABLED | DISABLED) */
+ switch (ptr[0])
+ {
+ case 'E' :
+ status = Event_parse_data::ENABLED;
+ break;
+ case 'S' :
+ status = Event_parse_data::SLAVESIDE_DISABLED;
+ break;
+ case 'D' :
+ default:
+ status = Event_parse_data::DISABLED;
+ break;
+ }
+ if ((ptr= get_field(&mem_root, table->field[ET_FIELD_ORIGINATOR])) == NullS)
+ DBUG_RETURN(TRUE);
+ originator = table->field[ET_FIELD_ORIGINATOR]->val_int();
+
+ /* ToDo : Andrey . Find a way not to allocate ptr on event_mem_root */
+ if ((ptr= get_field(&mem_root,
+ table->field[ET_FIELD_ON_COMPLETION])) == NullS)
+ DBUG_RETURN(TRUE);
+
+ on_completion= (ptr[0]=='D'? Event_parse_data::ON_COMPLETION_DROP:
+ Event_parse_data::ON_COMPLETION_PRESERVE);
+
+ DBUG_RETURN(FALSE);
+}
+
+
+/**
+ Load an event's body from a row from mysql.event.
+
+ @details This method is silent on errors and should behave like that.
+ Callers should handle throwing of error messages. The reason is that the
+ class should not know about how to deal with communication.
+
+ @return Operation status
+ @retval FALSE OK
+ @retval TRUE Error
+*/
+
+bool
+Event_timed::load_from_row(THD *thd, TABLE *table)
+{
+ char *ptr;
+ size_t len;
+
+ DBUG_ENTER("Event_timed::load_from_row");
+
+ if (Event_queue_element::load_from_row(thd, table))
+ DBUG_RETURN(TRUE);
+
+ if (load_string_fields(table->field,
+ ET_FIELD_BODY, &body,
+ ET_FIELD_BODY_UTF8, &body_utf8,
+ ET_FIELD_COUNT))
+ DBUG_RETURN(TRUE);
+
+ if (Event_creation_ctx::load_from_db(thd, &mem_root, dbname.str, name.str,
+ table, &creation_ctx))
+ {
+ push_warning_printf(thd,
+ MYSQL_ERROR::WARN_LEVEL_WARN,
+ ER_EVENT_INVALID_CREATION_CTX,
+ ER(ER_EVENT_INVALID_CREATION_CTX),
+ (const char *) dbname.str,
+ (const char *) name.str);
+ }
+
+ ptr= strchr(definer.str, '@');
+
+ if (! ptr)
+ ptr= definer.str;
+
+ len= ptr - definer.str;
+ definer_user.str= strmake_root(&mem_root, definer.str, len);
+ definer_user.length= len;
+ len= definer.length - len - 1;
+ /* 1:because of @ */
+ definer_host.str= strmake_root(&mem_root, ptr + 1, len);
+ definer_host.length= len;
+
+ created= table->field[ET_FIELD_CREATED]->val_int();
+ modified= table->field[ET_FIELD_MODIFIED]->val_int();
+
+ comment.str= get_field(&mem_root, table->field[ET_FIELD_COMMENT]);
+ if (comment.str != NullS)
+ comment.length= strlen(comment.str);
+ else
+ comment.length= 0;
+
+ sql_mode= (ulong) table->field[ET_FIELD_SQL_MODE]->val_int();
+
+ DBUG_RETURN(FALSE);
+}
+
+
+/*
+ add_interval() adds a specified interval to time 'ltime' in time
+ zone 'time_zone', and returns the result converted to the number of
+ seconds since epoch (aka Unix time; in UTC time zone). Zero result
+ means an error.
+*/
+static
+my_time_t
+add_interval(MYSQL_TIME *ltime, const Time_zone *time_zone,
+ interval_type scale, INTERVAL interval)
+{
+ if (date_add_interval(ltime, scale, interval))
+ return 0;
+
+ my_bool not_used;
+ return time_zone->TIME_to_gmt_sec(ltime, &not_used);
+}
+
+
+/*
+ Computes the sum of a timestamp plus interval.
+
+ SYNOPSIS
+ get_next_time()
+ time_zone event time zone
+ next the sum
+ start add interval_value to this time
+ time_now current time
+ i_value quantity of time type interval to add
+ i_type type of interval to add (SECOND, MINUTE, HOUR, WEEK ...)
+
+ RETURN VALUE
+ 0 OK
+ 1 Error
+
+ NOTES
+ 1) If the interval is conversible to SECOND, like MINUTE, HOUR, DAY, WEEK.
+ Then we use TIMEDIFF()'s implementation as underlying and number of
+ seconds as resolution for computation.
+ 2) In all other cases - MONTH, QUARTER, YEAR we use MONTH as resolution
+ and PERIOD_DIFF()'s implementation
+*/
+
+static
+bool get_next_time(const Time_zone *time_zone, my_time_t *next,
+ my_time_t start, my_time_t time_now,
+ int i_value, interval_type i_type)
+{
+ DBUG_ENTER("get_next_time");
+ DBUG_PRINT("enter", ("start: %lu now: %lu", (long) start, (long) time_now));
+
+ DBUG_ASSERT(start <= time_now);
+
+ longlong months=0, seconds=0;
+
+ switch (i_type) {
+ case INTERVAL_YEAR:
+ months= i_value*12;
+ break;
+ case INTERVAL_QUARTER:
+ /* Has already been converted to months */
+ case INTERVAL_YEAR_MONTH:
+ case INTERVAL_MONTH:
+ months= i_value;
+ break;
+ case INTERVAL_WEEK:
+ /* WEEK has already been converted to days */
+ case INTERVAL_DAY:
+ seconds= i_value*24*3600;
+ break;
+ case INTERVAL_DAY_HOUR:
+ case INTERVAL_HOUR:
+ seconds= i_value*3600;
+ break;
+ case INTERVAL_DAY_MINUTE:
+ case INTERVAL_HOUR_MINUTE:
+ case INTERVAL_MINUTE:
+ seconds= i_value*60;
+ break;
+ case INTERVAL_DAY_SECOND:
+ case INTERVAL_HOUR_SECOND:
+ case INTERVAL_MINUTE_SECOND:
+ case INTERVAL_SECOND:
+ seconds= i_value;
+ break;
+ case INTERVAL_DAY_MICROSECOND:
+ case INTERVAL_HOUR_MICROSECOND:
+ case INTERVAL_MINUTE_MICROSECOND:
+ case INTERVAL_SECOND_MICROSECOND:
+ case INTERVAL_MICROSECOND:
+ /*
+ We should return an error here so SHOW EVENTS/ SELECT FROM I_S.EVENTS
+ would give an error then.
+ */
+ DBUG_RETURN(1);
+ break;
+ case INTERVAL_LAST:
+ DBUG_ASSERT(0);
+ }
+ DBUG_PRINT("info", ("seconds: %ld months: %ld", (long) seconds, (long) months));
+
+ MYSQL_TIME local_start;
+ MYSQL_TIME local_now;
+
+ /* Convert times from UTC to local. */
+ {
+ time_zone->gmt_sec_to_TIME(&local_start, start);
+ time_zone->gmt_sec_to_TIME(&local_now, time_now);
+ }
+
+ INTERVAL interval;
+ bzero(&interval, sizeof(interval));
+ my_time_t next_time= 0;
+
+ if (seconds)
+ {
+ longlong seconds_diff;
+ long microsec_diff;
+ bool negative= calc_time_diff(&local_now, &local_start, 1,
+ &seconds_diff, &microsec_diff);
+ if (!negative)
+ {
+ /*
+ The formula below returns the interval that, when added to
+ local_start, will always give the time in the future.
+ */
+ interval.second= seconds_diff - seconds_diff % seconds + seconds;
+ next_time= add_interval(&local_start, time_zone,
+ INTERVAL_SECOND, interval);
+ if (next_time == 0)
+ goto done;
+ }
+
+ if (next_time <= time_now)
+ {
+ /*
+ If 'negative' is true above, then 'next_time == 0', and
+ 'next_time <= time_now' is also true. If negative is false,
+ then next_time was set, but perhaps to the value that is less
+ then time_now. See below for elaboration.
+ */
+ DBUG_ASSERT(negative || next_time > 0);
+
+ /*
+ If local_now < local_start, i.e. STARTS time is in the future
+ according to the local time (it always in the past according
+ to UTC---this is a prerequisite of this function), then
+ STARTS is almost always in the past according to the local
+ time too. However, in the time zone that has backward
+ Daylight Saving Time shift, the following may happen: suppose
+ we have a backward DST shift at certain date after 2:59:59,
+ i.e. local time goes 1:59:59, 2:00:00, ... , 2:59:59, (shift
+ here) 2:00:00 (again), ... , 2:59:59 (again), 3:00:00, ... .
+ Now suppose the time has passed the first 2:59:59, has been
+ shifted backward, and now is (the second) 2:20:00. The user
+ does CREATE EVENT with STARTS 'current-date 2:40:00'. Local
+ time 2:40:00 from create statement is treated by time
+ functions as the first such time, so according to UTC it comes
+ before the second 2:20:00. But according to local time it is
+ obviously in the future, so we end up in this branch.
+
+ Since we are in the second pass through 2:00:00--2:59:59, and
+ any local time form this interval is treated by system
+ functions as the time from the first pass, we have to find the
+ time for the next execution that is past the DST-affected
+ interval (past the second 2:59:59 for our example,
+ i.e. starting from 3:00:00). We do this in the loop until the
+ local time is mapped onto future UTC time. 'start' time is in
+ the past, so we may use 'do { } while' here, and add the first
+ interval right away.
+
+ Alternatively, it could be that local_now >= local_start. Now
+ for the example above imagine we do CREATE EVENT with STARTS
+ 'current-date 2:10:00'. Local start 2:10 is in the past (now
+ is local 2:20), so we add an interval, and get next execution
+ time, say, 2:40. It is in the future according to local time,
+ but, again, since we are in the second pass through
+ 2:00:00--2:59:59, 2:40 will be converted into UTC time in the
+ past. So we will end up in this branch again, and may add
+ intervals in a 'do { } while' loop.
+
+ Note that for any given event we may end up here only if event
+ next execution time will map to the time interval that is
+ passed twice, and only if the server was started during the
+ second pass, or the event is being created during the second
+ pass. After that, we never will get here (unless we again
+ start the server during the second pass). In other words,
+ such a condition is extremely rare.
+ */
+ interval.second= seconds;
+ do
+ {
+ next_time= add_interval(&local_start, time_zone,
+ INTERVAL_SECOND, interval);
+ if (next_time == 0)
+ goto done;
+ }
+ while (next_time <= time_now);
+ }
+ }
+ else
+ {
+ long diff_months= (long) (local_now.year - local_start.year)*12 +
+ (local_now.month - local_start.month);
+ /*
+ Unlike for seconds above, the formula below returns the interval
+ that, when added to the local_start, will give the time in the
+ past, or somewhere in the current month. We are interested in
+ the latter case, to see if this time has already passed, or is
+ yet to come this month.
+
+ Note that the time is guaranteed to be in the past unless
+ (diff_months % months == 0), but no good optimization is
+ possible here, because (diff_months % months == 0) is what will
+ happen most of the time, as get_next_time() will be called right
+ after the execution of the event. We could pass last_executed
+ time to this function, and see if the execution has already
+ happened this month, but for that we will have to convert
+ last_executed from seconds since epoch to local broken-down
+ time, and this will greatly reduce the effect of the
+ optimization. So instead we keep the code simple and clean.
+ */
+ interval.month= (ulong) (diff_months - diff_months % months);
+ next_time= add_interval(&local_start, time_zone,
+ INTERVAL_MONTH, interval);
+ if (next_time == 0)
+ goto done;
+
+ if (next_time <= time_now)
+ {
+ interval.month= (ulong) months;
+ next_time= add_interval(&local_start, time_zone,
+ INTERVAL_MONTH, interval);
+ if (next_time == 0)
+ goto done;
+ }
+ }
+
+ DBUG_ASSERT(time_now < next_time);
+
+ *next= next_time;
+
+done:
+ DBUG_PRINT("info", ("next_time: %ld", (long) next_time));
+ DBUG_RETURN(next_time == 0);
+}
+
+
+/*
+ Computes next execution time.
+
+ SYNOPSIS
+ Event_queue_element::compute_next_execution_time()
+
+ RETURN VALUE
+ FALSE OK
+ TRUE Error
+
+ NOTES
+ The time is set in execute_at, if no more executions the latter is
+ set to 0.
+*/
+
+bool
+Event_queue_element::compute_next_execution_time()
+{
+ my_time_t time_now;
+ DBUG_ENTER("Event_queue_element::compute_next_execution_time");
+ DBUG_PRINT("enter", ("starts: %lu ends: %lu last_executed: %lu this: 0x%lx",
+ (long) starts, (long) ends, (long) last_executed,
+ (long) this));
+
+ if (status != Event_parse_data::ENABLED)
+ {
+ DBUG_PRINT("compute_next_execution_time",
+ ("Event %s is DISABLED", name.str));
+ goto ret;
+ }
+ /* If one-time, no need to do computation */
+ if (!expression)
+ {
+ /* Let's check whether it was executed */
+ if (last_executed)
+ {
+ DBUG_PRINT("info",("One-time event %s.%s of was already executed",
+ dbname.str, name.str));
+ dropped= (on_completion == Event_parse_data::ON_COMPLETION_DROP);
+ DBUG_PRINT("info",("One-time event will be dropped: %d.", dropped));
+
+ status= Event_parse_data::DISABLED;
+ status_changed= TRUE;
+ }
+ goto ret;
+ }
+
+ time_now= (my_time_t) current_thd->query_start();
+
+ DBUG_PRINT("info",("NOW: [%lu]", (ulong) time_now));
+
+ /* if time_now is after ends don't execute anymore */
+ if (!ends_null && ends < time_now)
+ {
+ DBUG_PRINT("info", ("NOW after ENDS, don't execute anymore"));
+ /* time_now is after ends. don't execute anymore */
+ execute_at= 0;
+ execute_at_null= TRUE;
+ if (on_completion == Event_parse_data::ON_COMPLETION_DROP)
+ dropped= TRUE;
+ DBUG_PRINT("info", ("Dropped: %d", dropped));
+ status= Event_parse_data::DISABLED;
+ status_changed= TRUE;
+
+ goto ret;
+ }
+
+ /*
+ Here time_now is before or equals ends if the latter is set.
+ Let's check whether time_now is before starts.
+ If so schedule for starts.
+ */
+ if (!starts_null && time_now <= starts)
+ {
+ if (time_now == starts && starts == last_executed)
+ {
+ /*
+ do nothing or we will schedule for second time execution at starts.
+ */
+ }
+ else
+ {
+ DBUG_PRINT("info", ("STARTS is future, NOW <= STARTS,sched for STARTS"));
+ /*
+ starts is in the future
+ time_now before starts. Scheduling for starts
+ */
+ execute_at= starts;
+ execute_at_null= FALSE;
+ goto ret;
+ }
+ }
+
+ if (!starts_null && !ends_null)
+ {
+ /*
+ Both starts and m_ends are set and time_now is between them (incl.)
+ If last_executed is set then increase with m_expression. The new MYSQL_TIME is
+ after m_ends set execute_at to 0. And check for on_completion
+ If not set then schedule for now.
+ */
+ DBUG_PRINT("info", ("Both STARTS & ENDS are set"));
+ if (!last_executed)
+ {
+ DBUG_PRINT("info", ("Not executed so far."));
+ }
+
+ {
+ my_time_t next_exec;
+
+ if (get_next_time(time_zone, &next_exec, starts, time_now,
+ (int) expression, interval))
+ goto err;
+
+ /* There was previous execution */
+ if (ends < next_exec)
+ {
+ DBUG_PRINT("info", ("Next execution of %s after ENDS. Stop executing.",
+ name.str));
+ /* Next execution after ends. No more executions */
+ execute_at= 0;
+ execute_at_null= TRUE;
+ if (on_completion == Event_parse_data::ON_COMPLETION_DROP)
+ dropped= TRUE;
+ status= Event_parse_data::DISABLED;
+ status_changed= TRUE;
+ }
+ else
+ {
+ DBUG_PRINT("info",("Next[%lu]", (ulong) next_exec));
+ execute_at= next_exec;
+ execute_at_null= FALSE;
+ }
+ }
+ goto ret;
+ }
+ else if (starts_null && ends_null)
+ {
+ /* starts is always set, so this is a dead branch !! */
+ DBUG_PRINT("info", ("Neither STARTS nor ENDS are set"));
+ /*
+ Both starts and m_ends are not set, so we schedule for the next
+ based on last_executed.
+ */
+ if (last_executed)
+ {
+ my_time_t next_exec;
+ if (get_next_time(time_zone, &next_exec, starts, time_now,
+ (int) expression, interval))
+ goto err;
+ execute_at= next_exec;
+ DBUG_PRINT("info",("Next[%lu]", (ulong) next_exec));
+ }
+ else
+ {
+ /* last_executed not set. Schedule the event for now */
+ DBUG_PRINT("info", ("Execute NOW"));
+ execute_at= time_now;
+ }
+ execute_at_null= FALSE;
+ }
+ else
+ {
+ /* either starts or m_ends is set */
+ if (!starts_null)
+ {
+ DBUG_PRINT("info", ("STARTS is set"));
+ /*
+ - starts is set.
+ - starts is not in the future according to check made before
+ Hence schedule for starts + m_expression in case last_executed
+ is not set, otherwise to last_executed + m_expression
+ */
+ if (!last_executed)
+ {
+ DBUG_PRINT("info", ("Not executed so far."));
+ }
+
+ {
+ my_time_t next_exec;
+ if (get_next_time(time_zone, &next_exec, starts, time_now,
+ (int) expression, interval))
+ goto err;
+ execute_at= next_exec;
+ DBUG_PRINT("info",("Next[%lu]", (ulong) next_exec));
+ }
+ execute_at_null= FALSE;
+ }
+ else
+ {
+ /* this is a dead branch, because starts is always set !!! */
+ DBUG_PRINT("info", ("STARTS is not set. ENDS is set"));
+ /*
+ - m_ends is set
+ - m_ends is after time_now or is equal
+ Hence check for m_last_execute and increment with m_expression.
+ If last_executed is not set then schedule for now
+ */
+
+ if (!last_executed)
+ execute_at= time_now;
+ else
+ {
+ my_time_t next_exec;
+
+ if (get_next_time(time_zone, &next_exec, starts, time_now,
+ (int) expression, interval))
+ goto err;
+
+ if (ends < next_exec)
+ {
+ DBUG_PRINT("info", ("Next execution after ENDS. Stop executing."));
+ 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;
+ }
+ else
+ {
+ DBUG_PRINT("info", ("Next[%lu]", (ulong) next_exec));
+ execute_at= next_exec;
+ execute_at_null= FALSE;
+ }
+ }
+ }
+ goto ret;
+ }
+ret:
+ DBUG_PRINT("info", ("ret: 0 execute_at: %lu", (long) execute_at));
+ DBUG_RETURN(FALSE);
+err:
+ DBUG_PRINT("info", ("ret=1"));
+ DBUG_RETURN(TRUE);
+}
+
+
+/*
+ Set the internal last_executed MYSQL_TIME struct to now. NOW is the
+ time according to thd->query_start(), so the THD's clock.
+
+ SYNOPSIS
+ Event_queue_element::mark_last_executed()
+ thd thread context
+*/
+
+void
+Event_queue_element::mark_last_executed(THD *thd)
+{
+ last_executed= (my_time_t) 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,
+ const char *name, uint len)
+{
+ char dtime_buff[20*2+32];/* +32 to make my_snprintf_{8bit|ucs2} happy */
+ buf->append(STRING_WITH_LEN(" "));
+ buf->append(name, len);
+ buf->append(STRING_WITH_LEN(" '"));
+ /*
+ Pass the buffer and the second param tells fills the buffer and
+ returns the number of chars to copy.
+ */
+ MYSQL_TIME time;
+ time_zone->gmt_sec_to_TIME(&time, secs);
+ buf->append(dtime_buff, my_datetime_to_str(&time, dtime_buff));
+ buf->append(STRING_WITH_LEN("'"));
+}
+
+
+/*
+ Get SHOW CREATE EVENT as string
+
+ SYNOPSIS
+ Event_timed::get_create_event(THD *thd, String *buf)
+ thd Thread
+ buf String*, should be already allocated. CREATE EVENT goes inside.
+
+ RETURN VALUE
+ 0 OK
+ EVEX_MICROSECOND_UNSUP Error (for now if mysql.event has been
+ tampered and MICROSECONDS interval or
+ derivative has been put there.
+*/
+
+int
+Event_timed::get_create_event(THD *thd, String *buf)
+{
+ char tmp_buf[2 * STRING_BUFFER_USUAL_SIZE];
+ String expr_buf(tmp_buf, sizeof(tmp_buf), system_charset_info);
+ expr_buf.length(0);
+
+ DBUG_ENTER("get_create_event");
+ DBUG_PRINT("ret_info",("body_len=[%d]body=[%s]",
+ (int) body.length, body.str));
+
+ if (expression && Events::reconstruct_interval_expression(&expr_buf, interval,
+ expression))
+ DBUG_RETURN(EVEX_MICROSECOND_UNSUP);
+
+ buf->append(STRING_WITH_LEN("CREATE EVENT "));
+ append_identifier(thd, buf, name.str, name.length);
+
+ if (expression)
+ {
+ buf->append(STRING_WITH_LEN(" ON SCHEDULE EVERY "));
+ buf->append(expr_buf);
+ buf->append(' ');
+ LEX_STRING *ival= &interval_type_to_name[interval];
+ buf->append(ival->str, ival->length);
+
+ if (!starts_null)
+ append_datetime(buf, time_zone, starts, STRING_WITH_LEN("STARTS"));
+
+ if (!ends_null)
+ append_datetime(buf, time_zone, ends, STRING_WITH_LEN("ENDS"));
+ }
+ else
+ {
+ append_datetime(buf, time_zone, execute_at,
+ STRING_WITH_LEN("ON SCHEDULE AT"));
+ }
+
+ if (on_completion == Event_parse_data::ON_COMPLETION_DROP)
+ buf->append(STRING_WITH_LEN(" ON COMPLETION NOT PRESERVE "));
+ else
+ buf->append(STRING_WITH_LEN(" ON COMPLETION PRESERVE "));
+
+ if (status == Event_parse_data::ENABLED)
+ buf->append(STRING_WITH_LEN("ENABLE"));
+ else if (status == Event_parse_data::SLAVESIDE_DISABLED)
+ buf->append(STRING_WITH_LEN("DISABLE ON SLAVE"));
+ else
+ buf->append(STRING_WITH_LEN("DISABLE"));
+
+ if (comment.length)
+ {
+ buf->append(STRING_WITH_LEN(" COMMENT "));
+ append_unescaped(buf, comment.str, comment.length);
+ }
+ buf->append(STRING_WITH_LEN(" DO "));
+ buf->append(body.str, body.length);
+
+ DBUG_RETURN(0);
+}
+
+
+/**
+ Get an artificial stored procedure to parse as an event definition.
+*/
+
+bool
+Event_job_data::construct_sp_sql(THD *thd, String *sp_sql)
+{
+ LEX_STRING buffer;
+ const uint STATIC_SQL_LENGTH= 44;
+
+ DBUG_ENTER("Event_job_data::construct_sp_sql");
+
+ /*
+ Allocate a large enough buffer on the thread execution memory
+ root to avoid multiple [re]allocations on system heap
+ */
+ buffer.length= STATIC_SQL_LENGTH + name.length + body.length;
+ if (! (buffer.str= (char*) thd->alloc(buffer.length)))
+ DBUG_RETURN(TRUE);
+
+ sp_sql->set(buffer.str, buffer.length, system_charset_info);
+ sp_sql->length(0);
+
+
+ sp_sql->append(C_STRING_WITH_LEN("CREATE "));
+ sp_sql->append(C_STRING_WITH_LEN("PROCEDURE "));
+ /*
+ Let's use the same name as the event name to perhaps produce a
+ better error message in case it is a part of some parse error.
+ We're using append_identifier here to successfully parse
+ events with reserved names.
+ */
+ append_identifier(thd, sp_sql, name.str, name.length);
+
+ /*
+ The default SQL security of a stored procedure is DEFINER. We
+ have already activated the security context of the event, so
+ let's execute the procedure with the invoker rights to save on
+ resets of security contexts.
+ */
+ sp_sql->append(C_STRING_WITH_LEN("() SQL SECURITY INVOKER "));
+
+ sp_sql->append(body.str, body.length);
+
+ DBUG_RETURN(thd->is_fatal_error);
+}
+
+
+/**
+ Get DROP EVENT statement to binlog the drop of ON COMPLETION NOT
+ PRESERVE event.
+*/
+
+bool
+Event_job_data::construct_drop_event_sql(THD *thd, String *sp_sql)
+{
+ LEX_STRING buffer;
+ const uint STATIC_SQL_LENGTH= 14;
+
+ DBUG_ENTER("Event_job_data::construct_drop_event_sql");
+
+ buffer.length= STATIC_SQL_LENGTH + name.length*2 + dbname.length*2;
+ if (! (buffer.str= (char*) thd->alloc(buffer.length)))
+ DBUG_RETURN(TRUE);
+
+ sp_sql->set(buffer.str, buffer.length, system_charset_info);
+ sp_sql->length(0);
+
+ sp_sql->append(C_STRING_WITH_LEN("DROP EVENT "));
+ append_identifier(thd, sp_sql, dbname.str, dbname.length);
+ sp_sql->append('.');
+ append_identifier(thd, sp_sql, name.str, name.length);
+
+ DBUG_RETURN(thd->is_fatal_error);
+}
+
+/**
+ Compiles and executes the event (the underlying sp_head object)
+
+ @retval TRUE error (reported to the error log)
+ @retval FALSE success
+*/
+
+bool
+Event_job_data::execute(THD *thd, bool drop)
+{
+ String sp_sql;
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+ Security_context event_sctx, *save_sctx= NULL;
+#endif
+ List<Item> empty_item_list;
+ bool ret= TRUE;
+
+ DBUG_ENTER("Event_job_data::execute");
+
+ mysql_reset_thd_for_next_command(thd);
+
+ /*
+ MySQL parser currently assumes that current database is either
+ present in THD or all names in all statements are fully specified.
+ And yet not fully specified names inside stored programs must be
+ be supported, even if the current database is not set:
+ CREATE PROCEDURE db1.p1() BEGIN CREATE TABLE t1; END//
+ -- in this example t1 should be always created in db1 and the statement
+ must parse even if there is no current database.
+
+ To support this feature and still address the parser limitation,
+ we need to set the current database here.
+ We don't have to call mysql_change_db, since the checks performed
+ in it are unnecessary for the purpose of parsing, and
+ mysql_change_db will be invoked anyway later, to activate the
+ procedure database before it's executed.
+ */
+ thd->set_db(dbname.str, dbname.length);
+
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+ if (event_sctx.change_security_context(thd,
+ &definer_user, &definer_host,
+ &dbname, &save_sctx))
+ {
+ sql_print_error("Event Scheduler: "
+ "[%s].[%s.%s] execution failed, "
+ "failed to authenticate the user.",
+ definer.str, dbname.str, name.str);
+ goto end_no_lex_start;
+ }
+#endif
+
+ if (check_access(thd, EVENT_ACL, dbname.str,
+ 0, 0, 0, is_schema_db(dbname.str)))
+ {
+ /*
+ This aspect of behavior is defined in the worklog,
+ and this is how triggers work too: if TRIGGER
+ privilege is revoked from trigger definer,
+ triggers are not executed.
+ */
+ sql_print_error("Event Scheduler: "
+ "[%s].[%s.%s] execution failed, "
+ "user no longer has EVENT privilege.",
+ definer.str, dbname.str, name.str);
+ goto end_no_lex_start;
+ }
+
+ if (construct_sp_sql(thd, &sp_sql))
+ goto end_no_lex_start;
+
+ /*
+ Set up global thread attributes to reflect the properties of
+ this Event. We can simply reset these instead of usual
+ backup/restore employed in stored programs since we know that
+ this is a top level statement and the worker thread is
+ allocated exclusively to execute this event.
+ */
+
+ thd->variables.sql_mode= sql_mode;
+ thd->variables.time_zone= time_zone;
+
+ /*
+ Peculiar initialization order is a crutch to avoid races in SHOW
+ PROCESSLIST which reads thd->{query/query_length} without a mutex.
+ */
+ thd->query_length= 0;
+ thd->query= sp_sql.c_ptr_safe();
+ thd->query_length= sp_sql.length();
+
+ {
+ Parser_state parser_state(thd, thd->query, thd->query_length);
+ lex_start(thd);
+
+ if (parse_sql(thd, & parser_state, creation_ctx))
+ {
+ sql_print_error("Event Scheduler: "
+ "%serror during compilation of %s.%s",
+ thd->is_fatal_error ? "fatal " : "",
+ (const char *) dbname.str, (const char *) name.str);
+ goto end;
+ }
+ }
+
+ {
+ sp_head *sphead= thd->lex->sphead;
+
+ DBUG_ASSERT(sphead);
+
+ if (thd->enable_slow_log)
+ sphead->m_flags|= sp_head::LOG_SLOW_STATEMENTS;
+ sphead->m_flags|= sp_head::LOG_GENERAL_LOG;
+
+ sphead->set_info(0, 0, &thd->lex->sp_chistics, sql_mode);
+ sphead->set_creation_ctx(creation_ctx);
+ sphead->optimize();
+
+ ret= sphead->execute_procedure(thd, &empty_item_list);
+ /*
+ There is no pre-locking and therefore there should be no
+ tables open and locked left after execute_procedure.
+ */
+ }
+
+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)
+ {
+ /*
+ We must do it here since here we're under the right authentication
+ ID of the event definer.
+ */
+ sql_print_information("Event Scheduler: Dropping %s.%s",
+ (const char *) dbname.str, (const char *) name.str);
+ /*
+ Construct a query for the binary log, to ensure the event is dropped
+ on the slave
+ */
+ if (construct_drop_event_sql(thd, &sp_sql))
+ ret= 1;
+ else
+ {
+ ulong saved_master_access;
+ /*
+ Peculiar initialization order is a crutch to avoid races in SHOW
+ PROCESSLIST which reads thd->{query/query_length} without a mutex.
+ */
+ thd->query_length= 0;
+ thd->query= sp_sql.c_ptr_safe();
+ thd->query_length= sp_sql.length();
+
+ /*
+ NOTE: even if we run in read-only mode, we should be able to lock
+ the mysql.event table for writing. In order to achieve this, we
+ should call mysql_lock_tables() under the super-user.
+ */
+
+ saved_master_access= thd->security_ctx->master_access;
+ thd->security_ctx->master_access |= SUPER_ACL;
+
+ ret= Events::drop_event(thd, dbname, name, FALSE);
+
+ thd->security_ctx->master_access= saved_master_access;
+ }
+ }
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+ 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->query_length= 0;
+ thd->query= NULL;
+
+ DBUG_PRINT("info", ("EXECUTED %s.%s ret: %d", dbname.str, name.str, ret));
+
+ DBUG_RETURN(ret);
+}
+
+
+/*
+ Checks whether two events are in the same schema
+
+ SYNOPSIS
+ event_basic_db_equal()
+ db Schema
+ et Compare et->dbname to `db`
+
+ RETURN VALUE
+ TRUE Equal
+ FALSE Not equal
+*/
+
+bool
+event_basic_db_equal(LEX_STRING db, Event_basic *et)
+{
+ return !sortcmp_lex_string(et->dbname, db, system_charset_info);
+}
+
+
+/*
+ Checks whether an event has equal `db` and `name`
+
+ SYNOPSIS
+ event_basic_identifier_equal()
+ db Schema
+ name Name
+ et The event object
+
+ RETURN VALUE
+ TRUE Equal
+ FALSE Not equal
+*/
+
+bool
+event_basic_identifier_equal(LEX_STRING db, LEX_STRING name, Event_basic *b)
+{
+ return !sortcmp_lex_string(name, b->name, system_charset_info) &&
+ !sortcmp_lex_string(db, b->dbname, system_charset_info);
+}
+
+/**
+ @} (End of group Event_Scheduler)
+*/
diff --git a/sql/event_data_objects.h b/sql/event_data_objects.h
new file mode 100644
index 00000000000..e32077b9c97
--- /dev/null
+++ b/sql/event_data_objects.h
@@ -0,0 +1,196 @@
+#ifndef _EVENT_DATA_OBJECTS_H_
+#define _EVENT_DATA_OBJECTS_H_
+/* Copyright (C) 2004-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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+/**
+ @addtogroup Event_Scheduler
+ @{
+
+ @file event_data_objects.h
+*/
+
+#include "event_parse_data.h"
+
+class Event_queue_element_for_exec
+{
+public:
+ Event_queue_element_for_exec(){};
+ ~Event_queue_element_for_exec();
+
+ bool
+ init(LEX_STRING dbname, LEX_STRING name);
+
+ LEX_STRING dbname;
+ LEX_STRING name;
+ bool dropped;
+ THD *thd;
+
+private:
+ /* Prevent use of these */
+ Event_queue_element_for_exec(const Event_queue_element_for_exec &);
+ void operator=(Event_queue_element_for_exec &);
+};
+
+
+class Event_basic
+{
+protected:
+ MEM_ROOT mem_root;
+
+public:
+
+ LEX_STRING dbname;
+ LEX_STRING name;
+ LEX_STRING definer;// combination of user and host
+
+ Time_zone *time_zone;
+
+ Event_basic();
+ virtual ~Event_basic();
+
+ virtual bool
+ load_from_row(THD *thd, TABLE *table) = 0;
+
+protected:
+ bool
+ load_string_fields(Field **fields, ...);
+
+ bool
+ load_time_zone(THD *thd, const LEX_STRING tz_name);
+};
+
+
+
+class Event_queue_element : public Event_basic
+{
+protected:
+ bool status_changed;
+ bool last_executed_changed;
+
+public:
+ int on_completion;
+ int status;
+ longlong originator;
+
+ my_time_t last_executed;
+ my_time_t execute_at;
+ my_time_t starts;
+ my_time_t ends;
+ my_bool starts_null;
+ my_bool ends_null;
+ my_bool execute_at_null;
+
+ longlong expression;
+ interval_type interval;
+
+ bool dropped;
+
+ uint execution_count;
+
+ Event_queue_element();
+ virtual ~Event_queue_element();
+
+ virtual bool
+ load_from_row(THD *thd, TABLE *table);
+
+ bool
+ compute_next_execution_time();
+
+ void
+ mark_last_executed(THD *thd);
+
+ bool
+ update_timing_fields(THD *thd);
+};
+
+
+class Event_timed : public Event_queue_element
+{
+ Event_timed(const Event_timed &); /* Prevent use of these */
+ void operator=(Event_timed &);
+
+public:
+ LEX_STRING body;
+
+ LEX_STRING definer_user;
+ LEX_STRING definer_host;
+
+ LEX_STRING comment;
+
+ ulonglong created;
+ ulonglong modified;
+
+ ulong sql_mode;
+
+ class Stored_program_creation_ctx *creation_ctx;
+ LEX_STRING body_utf8;
+
+ Event_timed();
+ virtual ~Event_timed();
+
+ void
+ init();
+
+ virtual bool
+ load_from_row(THD *thd, TABLE *table);
+
+ int
+ get_create_event(THD *thd, String *buf);
+};
+
+
+class Event_job_data : public Event_basic
+{
+public:
+ LEX_STRING body;
+ LEX_STRING definer_user;
+ LEX_STRING definer_host;
+
+ ulong sql_mode;
+
+ class Stored_program_creation_ctx *creation_ctx;
+
+ Event_job_data();
+
+ virtual bool
+ load_from_row(THD *thd, TABLE *table);
+
+ bool
+ execute(THD *thd, bool drop);
+private:
+ bool
+ construct_sp_sql(THD *thd, String *sp_sql);
+ bool
+ construct_drop_event_sql(THD *thd, String *sp_sql);
+
+ Event_job_data(const Event_job_data &); /* Prevent use of these */
+ void operator=(Event_job_data &);
+};
+
+
+/* Compares only the schema part of the identifier */
+bool
+event_basic_db_equal(LEX_STRING db, Event_basic *et);
+
+/* Compares the whole identifier*/
+bool
+event_basic_identifier_equal(LEX_STRING db, LEX_STRING name, Event_basic *b);
+
+/**
+ @} (End of group Event_Scheduler)
+*/
+
+#endif /* _EVENT_DATA_OBJECTS_H_ */
diff --git a/sql/event_db_repository.cc b/sql/event_db_repository.cc
new file mode 100644
index 00000000000..9a253d74546
--- /dev/null
+++ b/sql/event_db_repository.cc
@@ -0,0 +1,1168 @@
+/* Copyright 2004-2008 MySQL AB, 2008 Sun Microsystems, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#include "mysql_priv.h"
+#include "event_db_repository.h"
+#include "sp_head.h"
+#include "event_data_objects.h"
+#include "events.h"
+#include "sql_show.h"
+
+/**
+ @addtogroup Event_Scheduler
+ @{
+*/
+
+static
+const TABLE_FIELD_W_TYPE event_table_fields[ET_FIELD_COUNT] =
+{
+ {
+ { C_STRING_WITH_LEN("db") },
+ { C_STRING_WITH_LEN("char(64)") },
+ { C_STRING_WITH_LEN("utf8") }
+ },
+ {
+ { C_STRING_WITH_LEN("name") },
+ { C_STRING_WITH_LEN("char(64)") },
+ { C_STRING_WITH_LEN("utf8") }
+ },
+ {
+ { C_STRING_WITH_LEN("body") },
+ { C_STRING_WITH_LEN("longblob") },
+ {NULL, 0}
+ },
+ {
+ { C_STRING_WITH_LEN("definer") },
+ { C_STRING_WITH_LEN("char(77)") },
+ { C_STRING_WITH_LEN("utf8") }
+ },
+ {
+ { C_STRING_WITH_LEN("execute_at") },
+ { C_STRING_WITH_LEN("datetime") },
+ {NULL, 0}
+ },
+ {
+ { C_STRING_WITH_LEN("interval_value") },
+ { C_STRING_WITH_LEN("int(11)") },
+ {NULL, 0}
+ },
+ {
+ { C_STRING_WITH_LEN("interval_field") },
+ { C_STRING_WITH_LEN("enum('YEAR','QUARTER','MONTH','DAY',"
+ "'HOUR','MINUTE','WEEK','SECOND','MICROSECOND','YEAR_MONTH','DAY_HOUR',"
+ "'DAY_MINUTE','DAY_SECOND','HOUR_MINUTE','HOUR_SECOND','MINUTE_SECOND',"
+ "'DAY_MICROSECOND','HOUR_MICROSECOND','MINUTE_MICROSECOND',"
+ "'SECOND_MICROSECOND')") },
+ {NULL, 0}
+ },
+ {
+ { C_STRING_WITH_LEN("created") },
+ { C_STRING_WITH_LEN("timestamp") },
+ {NULL, 0}
+ },
+ {
+ { C_STRING_WITH_LEN("modified") },
+ { C_STRING_WITH_LEN("timestamp") },
+ {NULL, 0}
+ },
+ {
+ { C_STRING_WITH_LEN("last_executed") },
+ { C_STRING_WITH_LEN("datetime") },
+ {NULL, 0}
+ },
+ {
+ { C_STRING_WITH_LEN("starts") },
+ { C_STRING_WITH_LEN("datetime") },
+ {NULL, 0}
+ },
+ {
+ { C_STRING_WITH_LEN("ends") },
+ { C_STRING_WITH_LEN("datetime") },
+ {NULL, 0}
+ },
+ {
+ { C_STRING_WITH_LEN("status") },
+ { C_STRING_WITH_LEN("enum('ENABLED','DISABLED','SLAVESIDE_DISABLED')") },
+ {NULL, 0}
+ },
+ {
+ { C_STRING_WITH_LEN("on_completion") },
+ { C_STRING_WITH_LEN("enum('DROP','PRESERVE')") },
+ {NULL, 0}
+ },
+ {
+ { C_STRING_WITH_LEN("sql_mode") },
+ { C_STRING_WITH_LEN("set('REAL_AS_FLOAT','PIPES_AS_CONCAT','ANSI_QUOTES',"
+ "'IGNORE_SPACE','NOT_USED','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','INVALID_DATES',"
+ "'ERROR_FOR_DIVISION_BY_ZERO','TRADITIONAL','NO_AUTO_CREATE_USER',"
+ "'HIGH_NOT_PRECEDENCE','NO_ENGINE_SUBSTITUTION','PAD_CHAR_TO_FULL_LENGTH')") },
+ {NULL, 0}
+ },
+ {
+ { C_STRING_WITH_LEN("comment") },
+ { C_STRING_WITH_LEN("char(64)") },
+ { C_STRING_WITH_LEN("utf8") }
+ },
+ {
+ { C_STRING_WITH_LEN("originator") },
+ { C_STRING_WITH_LEN("int(10)") },
+ {NULL, 0}
+ },
+ {
+ { C_STRING_WITH_LEN("time_zone") },
+ { C_STRING_WITH_LEN("char(64)") },
+ { C_STRING_WITH_LEN("latin1") }
+ },
+ {
+ { C_STRING_WITH_LEN("character_set_client") },
+ { C_STRING_WITH_LEN("char(32)") },
+ { C_STRING_WITH_LEN("utf8") }
+ },
+ {
+ { C_STRING_WITH_LEN("collation_connection") },
+ { C_STRING_WITH_LEN("char(32)") },
+ { C_STRING_WITH_LEN("utf8") }
+ },
+ {
+ { C_STRING_WITH_LEN("db_collation") },
+ { C_STRING_WITH_LEN("char(32)") },
+ { C_STRING_WITH_LEN("utf8") }
+ },
+ {
+ { C_STRING_WITH_LEN("body_utf8") },
+ { C_STRING_WITH_LEN("longblob") },
+ { NULL, 0 }
+ }
+};
+
+
+/**
+ Puts some data common to CREATE and ALTER EVENT into a row.
+
+ Used both when an event is created and when it is altered.
+
+ @param thd THD
+ @param table The row to fill out
+ @param et Event's data
+ @param sp Event stored routine
+ @param is_update CREATE EVENT or ALTER EVENT
+
+ @retval FALSE success
+ @retval TRUE error
+*/
+
+static bool
+mysql_event_fill_row(THD *thd,
+ TABLE *table,
+ Event_parse_data *et,
+ sp_head *sp,
+ ulong sql_mode,
+ my_bool is_update)
+{
+ CHARSET_INFO *scs= system_charset_info;
+ enum enum_events_table_field f_num;
+ Field **fields= table->field;
+ int rs= FALSE;
+
+ DBUG_ENTER("mysql_event_fill_row");
+
+ DBUG_PRINT("info", ("dbname=[%s]", et->dbname.str));
+ DBUG_PRINT("info", ("name =[%s]", et->name.str));
+
+ DBUG_ASSERT(et->on_completion != Event_parse_data::ON_COMPLETION_DEFAULT);
+
+ if (table->s->fields < ET_FIELD_COUNT)
+ {
+ /*
+ Safety: this can only happen if someone started the server
+ and then altered mysql.event.
+ */
+ my_error(ER_COL_COUNT_DOESNT_MATCH_CORRUPTED, MYF(0), table->alias,
+ (int) ET_FIELD_COUNT, table->s->fields);
+ DBUG_RETURN(TRUE);
+ }
+
+ if (fields[f_num= ET_FIELD_DEFINER]->
+ store(et->definer.str, et->definer.length, scs))
+ goto err_truncate;
+
+ if (fields[f_num= ET_FIELD_DB]->store(et->dbname.str, et->dbname.length, scs))
+ goto err_truncate;
+
+ if (fields[f_num= ET_FIELD_NAME]->store(et->name.str, et->name.length, scs))
+ goto err_truncate;
+
+ /* both ON_COMPLETION and STATUS are NOT NULL thus not calling set_notnull()*/
+ rs|= fields[ET_FIELD_ON_COMPLETION]->store((longlong)et->on_completion, TRUE);
+ rs|= fields[ET_FIELD_STATUS]->store((longlong)et->status, TRUE);
+ rs|= fields[ET_FIELD_ORIGINATOR]->store((longlong)et->originator, TRUE);
+
+ /*
+ Change the SQL_MODE only if body was present in an ALTER EVENT and of course
+ always during CREATE EVENT.
+ */
+ if (et->body_changed)
+ {
+ DBUG_ASSERT(sp->m_body.str);
+
+ rs|= fields[ET_FIELD_SQL_MODE]->store((longlong)sql_mode, TRUE);
+
+ if (fields[f_num= ET_FIELD_BODY]->store(sp->m_body.str,
+ sp->m_body.length,
+ scs))
+ {
+ goto err_truncate;
+ }
+ }
+
+ if (et->expression)
+ {
+ const String *tz_name= thd->variables.time_zone->get_name();
+ if (!is_update || !et->starts_null)
+ {
+ fields[ET_FIELD_TIME_ZONE]->set_notnull();
+ rs|= fields[ET_FIELD_TIME_ZONE]->store(tz_name->ptr(), tz_name->length(),
+ tz_name->charset());
+ }
+
+ fields[ET_FIELD_INTERVAL_EXPR]->set_notnull();
+ rs|= fields[ET_FIELD_INTERVAL_EXPR]->store((longlong)et->expression, TRUE);
+
+ fields[ET_FIELD_TRANSIENT_INTERVAL]->set_notnull();
+
+ rs|= fields[ET_FIELD_TRANSIENT_INTERVAL]->
+ store(interval_type_to_name[et->interval].str,
+ interval_type_to_name[et->interval].length,
+ scs);
+
+ fields[ET_FIELD_EXECUTE_AT]->set_null();
+
+ if (!et->starts_null)
+ {
+ MYSQL_TIME time;
+ my_tz_OFFSET0->gmt_sec_to_TIME(&time, et->starts);
+
+ fields[ET_FIELD_STARTS]->set_notnull();
+ fields[ET_FIELD_STARTS]->store_time(&time, MYSQL_TIMESTAMP_DATETIME);
+ }
+
+ if (!et->ends_null)
+ {
+ MYSQL_TIME time;
+ my_tz_OFFSET0->gmt_sec_to_TIME(&time, et->ends);
+
+ fields[ET_FIELD_ENDS]->set_notnull();
+ fields[ET_FIELD_ENDS]->store_time(&time, MYSQL_TIMESTAMP_DATETIME);
+ }
+ }
+ else if (et->execute_at)
+ {
+ const String *tz_name= thd->variables.time_zone->get_name();
+ fields[ET_FIELD_TIME_ZONE]->set_notnull();
+ rs|= fields[ET_FIELD_TIME_ZONE]->store(tz_name->ptr(), tz_name->length(),
+ tz_name->charset());
+
+ fields[ET_FIELD_INTERVAL_EXPR]->set_null();
+ fields[ET_FIELD_TRANSIENT_INTERVAL]->set_null();
+ fields[ET_FIELD_STARTS]->set_null();
+ fields[ET_FIELD_ENDS]->set_null();
+
+ MYSQL_TIME time;
+ my_tz_OFFSET0->gmt_sec_to_TIME(&time, et->execute_at);
+
+ fields[ET_FIELD_EXECUTE_AT]->set_notnull();
+ fields[ET_FIELD_EXECUTE_AT]->
+ store_time(&time, MYSQL_TIMESTAMP_DATETIME);
+ }
+ else
+ {
+ DBUG_ASSERT(is_update);
+ /*
+ it is normal to be here when the action is update
+ this is an error if the action is create. something is borked
+ */
+ }
+
+ ((Field_timestamp *)fields[ET_FIELD_MODIFIED])->set_time();
+
+ if (et->comment.str)
+ {
+ if (fields[f_num= ET_FIELD_COMMENT]->
+ store(et->comment.str, et->comment.length, scs))
+ goto err_truncate;
+ }
+
+ fields[ET_FIELD_CHARACTER_SET_CLIENT]->set_notnull();
+ rs|= fields[ET_FIELD_CHARACTER_SET_CLIENT]->store(
+ thd->variables.character_set_client->csname,
+ strlen(thd->variables.character_set_client->csname),
+ system_charset_info);
+
+ fields[ET_FIELD_COLLATION_CONNECTION]->set_notnull();
+ rs|= fields[ET_FIELD_COLLATION_CONNECTION]->store(
+ thd->variables.collation_connection->name,
+ strlen(thd->variables.collation_connection->name),
+ system_charset_info);
+
+ {
+ CHARSET_INFO *db_cl= get_default_db_collation(thd, et->dbname.str);
+
+ fields[ET_FIELD_DB_COLLATION]->set_notnull();
+ rs|= fields[ET_FIELD_DB_COLLATION]->store(db_cl->name,
+ strlen(db_cl->name),
+ system_charset_info);
+ }
+
+ if (et->body_changed)
+ {
+ fields[ET_FIELD_BODY_UTF8]->set_notnull();
+ rs|= fields[ET_FIELD_BODY_UTF8]->store(sp->m_body_utf8.str,
+ sp->m_body_utf8.length,
+ system_charset_info);
+ }
+
+ if (rs)
+ {
+ my_error(ER_EVENT_STORE_FAILED, MYF(0), fields[f_num]->field_name, rs);
+ DBUG_RETURN(TRUE);
+ }
+
+ DBUG_RETURN(FALSE);
+
+err_truncate:
+ my_error(ER_EVENT_DATA_TOO_LONG, MYF(0), fields[f_num]->field_name);
+ DBUG_RETURN(TRUE);
+}
+
+
+/*
+ Performs an index scan of event_table (mysql.event) and fills schema_table.
+
+ SYNOPSIS
+ Event_db_repository::index_read_for_db_for_i_s()
+ thd Thread
+ schema_table The I_S.EVENTS table
+ event_table The event table to use for loading (mysql.event)
+ db For which schema to do an index scan.
+
+ RETURN VALUE
+ 0 OK
+ 1 Error
+*/
+
+bool
+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;
+ uchar *key_buf= NULL;
+ LINT_INIT(key_buf);
+
+ 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);
+ key_info= event_table->key_info;
+
+ if (key_info->key_parts == 0 ||
+ key_info->key_part[0].field != event_table->field[ET_FIELD_DB])
+ {
+ /* Corrupted table: no index or index on a wrong column */
+ my_error(ER_CANNOT_LOAD_FROM_TABLE, MYF(0), "event");
+ ret= 1;
+ goto end;
+ }
+
+ event_table->field[ET_FIELD_DB]->store(db, strlen(db), scs);
+ key_len= key_info->key_part[0].store_length;
+
+ if (!(key_buf= (uchar *)alloc_root(thd->mem_root, key_len)))
+ {
+ /* Don't send error, it would be done by sql_alloc_error_handler() */
+ ret= 1;
+ goto end;
+ }
+
+ key_copy(key_buf, event_table->record[0], key_info, key_len);
+ if (!(ret= event_table->file->index_read_map(event_table->record[0], key_buf,
+ (key_part_map)1,
+ HA_READ_PREFIX)))
+ {
+ DBUG_PRINT("info",("Found rows. Let's retrieve them. ret=%d", ret));
+ do
+ {
+ ret= copy_event_to_schema_table(thd, schema_table, event_table);
+ if (ret == 0)
+ ret= event_table->file->index_next_same(event_table->record[0],
+ key_buf, key_len);
+ } while (ret == 0);
+ }
+ DBUG_PRINT("info", ("Scan finished. ret=%d", ret));
+
+ /* ret is guaranteed to be != 0 */
+ if (ret == HA_ERR_END_OF_FILE || ret == HA_ERR_KEY_NOT_FOUND)
+ ret= 0;
+ else
+ event_table->file->print_error(ret, MYF(0));
+
+end:
+ event_table->file->ha_index_end();
+
+ DBUG_RETURN(test(ret));
+}
+
+
+/*
+ Performs a table scan of event_table (mysql.event) and fills schema_table.
+
+ SYNOPSIS
+ Events_db_repository::table_scan_all_for_i_s()
+ thd Thread
+ schema_table The I_S.EVENTS in memory table
+ event_table The event table to use for loading.
+
+ RETURN VALUE
+ FALSE OK
+ TRUE Error
+*/
+
+bool
+Event_db_repository::table_scan_all_for_i_s(THD *thd, TABLE *schema_table,
+ TABLE *event_table)
+{
+ int ret;
+ READ_RECORD read_record_info;
+ DBUG_ENTER("Event_db_repository::table_scan_all_for_i_s");
+
+ init_read_record(&read_record_info, thd, event_table, NULL, 1, 0, FALSE);
+
+ /*
+ rr_sequential, in read_record(), returns 137==HA_ERR_END_OF_FILE,
+ but rr_handle_error returns -1 for that reason. Thus, read_record()
+ returns -1 eventually.
+ */
+ do
+ {
+ ret= read_record_info.read_record(&read_record_info);
+ if (ret == 0)
+ ret= copy_event_to_schema_table(thd, schema_table, event_table);
+ } while (ret == 0);
+
+ DBUG_PRINT("info", ("Scan finished. ret=%d", ret));
+ end_read_record(&read_record_info);
+
+ /* ret is guaranteed to be != 0 */
+ DBUG_RETURN(ret == -1? FALSE:TRUE);
+}
+
+
+/**
+ Fills I_S.EVENTS with data loaded from mysql.event. Also used by
+ SHOW EVENTS
+
+ The reason we reset and backup open tables here is that this
+ function may be called from any query that accesses
+ INFORMATION_SCHEMA - including a query that is issued from
+ a pre-locked statement, one that already has open and locked
+ tables.
+
+ @retval FALSE success
+ @retval TRUE error
+*/
+
+bool
+Event_db_repository::fill_schema_events(THD *thd, TABLE_LIST *tables,
+ const char *db)
+{
+ TABLE *schema_table= tables->table;
+ TABLE *event_table= NULL;
+ 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))
+ DBUG_RETURN(TRUE);
+
+ /*
+ 1. SELECT I_S => use table scan. I_S.EVENTS does not guarantee order
+ thus we won't order it. OTOH, SHOW EVENTS will be
+ ordered.
+ 2. SHOW EVENTS => PRIMARY KEY with prefix scanning on (db)
+ Reasoning: Events are per schema, therefore a scan over an index
+ will save use from doing a table scan and comparing
+ 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);
+ else
+ ret= table_scan_all_for_i_s(thd, schema_table, event_table);
+
+ close_thread_tables(thd);
+
+ DBUG_PRINT("info", ("Return code=%d", ret));
+ DBUG_RETURN(ret);
+}
+
+
+/**
+ Open mysql.event table for read.
+
+ It's assumed that the caller knows what they are doing:
+ - whether it was necessary to reset-and-backup the open tables state
+ - whether the requested lock does not lead to a deadlock
+ - whether this open mode would work under LOCK TABLES, or inside a
+ stored function or trigger.
+
+ Note that if the table can't be locked successfully this operation will
+ close it. Therefore it provides guarantee that it either opens and locks
+ table or fails without leaving any tables open.
+
+ @param[in] thd Thread context
+ @param[in] lock_type How to lock the table
+ @param[out] table We will store the open table here
+
+ @retval TRUE open and lock failed - an error message is pushed into the
+ stack
+ @retval FALSE success
+*/
+
+bool
+Event_db_repository::open_event_table(THD *thd, enum thr_lock_type lock_type,
+ TABLE **table)
+{
+ TABLE_LIST tables;
+ DBUG_ENTER("Event_db_repository::open_event_table");
+
+ tables.init_one_table("mysql", "event", lock_type);
+
+ if (simple_open_n_lock_tables(thd, &tables))
+ {
+ close_thread_tables(thd);
+ DBUG_RETURN(TRUE);
+ }
+
+ *table= tables.table;
+ tables.table->use_all_columns();
+ DBUG_RETURN(FALSE);
+}
+
+
+/**
+ Creates an event record in mysql.event table.
+
+ Creates an event. Relies on mysql_event_fill_row which is shared with
+ ::update_event.
+
+ @pre All semantic checks must be performed outside. This function
+ only creates a record on disk.
+ @pre The thread handle has no open tables.
+
+ @param[in,out] thd THD
+ @param[in] parse_data Parsed event definition
+ @param[in] create_if_not TRUE if IF NOT EXISTS clause was provided
+ to CREATE EVENT statement
+
+ @retval FALSE success
+ @retval TRUE error
+*/
+
+bool
+Event_db_repository::create_event(THD *thd, Event_parse_data *parse_data,
+ my_bool create_if_not)
+{
+ int ret= 1;
+ TABLE *table= NULL;
+ sp_head *sp= thd->lex->sphead;
+ ulong saved_mode= thd->variables.sql_mode;
+
+ DBUG_ENTER("Event_db_repository::create_event");
+
+ DBUG_PRINT("info", ("open mysql.event for update"));
+ DBUG_ASSERT(sp);
+
+ /* Reset sql_mode during data dictionary operations. */
+ thd->variables.sql_mode= 0;
+
+ if (open_event_table(thd, TL_WRITE, &table))
+ goto end;
+
+ DBUG_PRINT("info", ("name: %.*s", (int) parse_data->name.length,
+ parse_data->name.str));
+
+ DBUG_PRINT("info", ("check existance of an event with the same name"));
+ if (!find_named_event(parse_data->dbname, parse_data->name, table))
+ {
+ if (create_if_not)
+ {
+ push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
+ ER_EVENT_ALREADY_EXISTS, ER(ER_EVENT_ALREADY_EXISTS),
+ parse_data->name.str);
+ ret= 0;
+ }
+ else
+ my_error(ER_EVENT_ALREADY_EXISTS, MYF(0), parse_data->name.str);
+ goto end;
+ }
+
+ DBUG_PRINT("info", ("non-existent, go forward"));
+
+ restore_record(table, s->default_values); // Get default values for fields
+
+ if (system_charset_info->cset->
+ numchars(system_charset_info, parse_data->dbname.str,
+ parse_data->dbname.str + parse_data->dbname.length) >
+ table->field[ET_FIELD_DB]->char_length())
+ {
+ my_error(ER_TOO_LONG_IDENT, MYF(0), parse_data->dbname.str);
+ goto end;
+ }
+
+ if (system_charset_info->cset->
+ numchars(system_charset_info, parse_data->name.str,
+ parse_data->name.str + parse_data->name.length) >
+ table->field[ET_FIELD_NAME]->char_length())
+ {
+ my_error(ER_TOO_LONG_IDENT, MYF(0), parse_data->name.str);
+ goto end;
+ }
+
+ if (sp->m_body.length > table->field[ET_FIELD_BODY]->field_length)
+ {
+ my_error(ER_TOO_LONG_BODY, MYF(0), parse_data->name.str);
+ goto end;
+ }
+
+ ((Field_timestamp *)table->field[ET_FIELD_CREATED])->set_time();
+
+ /*
+ mysql_event_fill_row() calls my_error() in case of error so no need to
+ handle it here
+ */
+ if (mysql_event_fill_row(thd, table, parse_data, sp, saved_mode, FALSE))
+ goto end;
+
+ table->field[ET_FIELD_STATUS]->store((longlong)parse_data->status, TRUE);
+
+ if ((ret= table->file->ha_write_row(table->record[0])))
+ {
+ table->file->print_error(ret, MYF(0));
+ goto end;
+ }
+ ret= 0;
+
+end:
+ if (table)
+ close_thread_tables(thd);
+ thd->variables.sql_mode= saved_mode;
+ DBUG_RETURN(test(ret));
+}
+
+
+/**
+ Used to execute ALTER EVENT. Pendant to Events::update_event().
+
+ @param[in,out] thd thread handle
+ @param[in] parse_data parsed event definition
+ @param[in] new_dbname not NULL if ALTER EVENT RENAME
+ points at a new database name
+ @param[in] new_name not NULL if ALTER EVENT RENAME
+ points at a new event name
+
+ @pre All semantic checks are performed outside this function,
+ it only updates the event definition on disk.
+ @pre We don't have any tables open in the given thread.
+
+ @retval FALSE success
+ @retval TRUE error (reported)
+*/
+
+bool
+Event_db_repository::update_event(THD *thd, Event_parse_data *parse_data,
+ LEX_STRING *new_dbname,
+ LEX_STRING *new_name)
+{
+ CHARSET_INFO *scs= system_charset_info;
+ TABLE *table= NULL;
+ sp_head *sp= thd->lex->sphead;
+ ulong saved_mode= thd->variables.sql_mode;
+ int ret= 1;
+
+ DBUG_ENTER("Event_db_repository::update_event");
+
+ /* None or both must be set */
+ DBUG_ASSERT(new_dbname && new_name || new_dbname == new_name);
+
+ /* Reset sql_mode during data dictionary operations. */
+ thd->variables.sql_mode= 0;
+
+ if (open_event_table(thd, TL_WRITE, &table))
+ goto end;
+
+ DBUG_PRINT("info", ("dbname: %s", parse_data->dbname.str));
+ DBUG_PRINT("info", ("name: %s", parse_data->name.str));
+ DBUG_PRINT("info", ("user: %s", parse_data->definer.str));
+
+ /* first look whether we overwrite */
+ if (new_name)
+ {
+ DBUG_PRINT("info", ("rename to: %s@%s", new_dbname->str, new_name->str));
+ if (!find_named_event(*new_dbname, *new_name, table))
+ {
+ my_error(ER_EVENT_ALREADY_EXISTS, MYF(0), new_name->str);
+ goto end;
+ }
+ }
+ /*
+ ...and then if there is such an event. Don't exchange the blocks
+ because you will get error 120 from table handler because new_name will
+ overwrite the key and SE will tell us that it cannot find the already found
+ row (copied into record[1] later
+ */
+ if (find_named_event(parse_data->dbname, parse_data->name, table))
+ {
+ my_error(ER_EVENT_DOES_NOT_EXIST, MYF(0), parse_data->name.str);
+ goto end;
+ }
+
+ store_record(table,record[1]);
+
+ /*
+ We check whether ALTER EVENT was given dates that are in the past.
+ However to know how to react, we need the ON COMPLETION type. The
+ check is deferred to this point because by now we have the previous
+ setting (from the event-table) to fall back on if nothing was specified
+ in the ALTER EVENT-statement.
+ */
+
+ if (parse_data->check_dates(thd,
+ (int) table->field[ET_FIELD_ON_COMPLETION]->val_int()))
+ goto end;
+
+ /* Don't update create on row update. */
+ table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET;
+
+ /*
+ mysql_event_fill_row() calls my_error() in case of error so no need to
+ handle it here
+ */
+ if (mysql_event_fill_row(thd, table, parse_data, sp, saved_mode, TRUE))
+ goto end;
+
+ if (new_dbname)
+ {
+ table->field[ET_FIELD_DB]->store(new_dbname->str, new_dbname->length, scs);
+ table->field[ET_FIELD_NAME]->store(new_name->str, new_name->length, scs);
+ }
+
+ if ((ret= table->file->ha_update_row(table->record[1], table->record[0])))
+ {
+ table->file->print_error(ret, MYF(0));
+ goto end;
+ }
+ ret= 0;
+
+end:
+ if (table)
+ close_thread_tables(thd);
+ thd->variables.sql_mode= saved_mode;
+ DBUG_RETURN(test(ret));
+}
+
+
+/**
+ Delete event record from mysql.event table.
+
+ @param[in,out] thd thread handle
+ @param[in] db Database name
+ @param[in] name Event name
+ @param[in] drop_if_exists DROP IF EXISTS clause was specified.
+ If set, and the event does not exist,
+ the error is downgraded to a warning.
+
+ @retval FALSE success
+ @retval TRUE error (reported)
+*/
+
+bool
+Event_db_repository::drop_event(THD *thd, LEX_STRING db, LEX_STRING name,
+ bool drop_if_exists)
+{
+ TABLE *table= NULL;
+ int ret= 1;
+
+ DBUG_ENTER("Event_db_repository::drop_event");
+ DBUG_PRINT("enter", ("%s@%s", db.str, name.str));
+
+ if (open_event_table(thd, TL_WRITE, &table))
+ goto end;
+
+ if (!find_named_event(db, name, table))
+ {
+ if ((ret= table->file->ha_delete_row(table->record[0])))
+ table->file->print_error(ret, MYF(0));
+ goto end;
+ }
+
+ /* Event not found */
+ if (!drop_if_exists)
+ {
+ my_error(ER_EVENT_DOES_NOT_EXIST, MYF(0), name.str);
+ goto end;
+ }
+
+ push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
+ ER_SP_DOES_NOT_EXIST, ER(ER_SP_DOES_NOT_EXIST),
+ "Event", name.str);
+ ret= 0;
+
+end:
+ if (table)
+ close_thread_tables(thd);
+
+ DBUG_RETURN(test(ret));
+}
+
+
+/**
+ Positions the internal pointer of `table` to the place where (db, name)
+ is stored.
+
+ In case search succeeded, the table cursor points at the found row.
+
+ @param[in] db database name
+ @param[in] name event name
+ @param[in,out] table mysql.event table
+
+
+ @retval FALSE an event with such db/name key exists
+ @retval TRUE no record found or an error occured.
+*/
+
+bool
+Event_db_repository::find_named_event(LEX_STRING db, LEX_STRING name,
+ TABLE *table)
+{
+ uchar key[MAX_KEY_LENGTH];
+ DBUG_ENTER("Event_db_repository::find_named_event");
+ DBUG_PRINT("enter", ("name: %.*s", (int) name.length, name.str));
+
+ /*
+ Create key to find row. We have to use field->store() to be able to
+ handle VARCHAR and CHAR fields.
+ Assumption here is that the two first fields in the table are
+ 'db' and 'name' and the first key is the primary key over the
+ same fields.
+ */
+ if (db.length > table->field[ET_FIELD_DB]->field_length ||
+ name.length > table->field[ET_FIELD_NAME]->field_length)
+ DBUG_RETURN(TRUE);
+
+ table->field[ET_FIELD_DB]->store(db.str, db.length, &my_charset_bin);
+ table->field[ET_FIELD_NAME]->store(name.str, name.length, &my_charset_bin);
+
+ key_copy(key, table->record[0], table->key_info, table->key_info->key_length);
+
+ if (table->file->index_read_idx_map(table->record[0], 0, key, HA_WHOLE_KEY,
+ HA_READ_KEY_EXACT))
+ {
+ DBUG_PRINT("info", ("Row not found"));
+ DBUG_RETURN(TRUE);
+ }
+
+ DBUG_PRINT("info", ("Row found!"));
+ DBUG_RETURN(FALSE);
+}
+
+
+/*
+ Drops all events in the selected database, from mysql.event.
+
+ SYNOPSIS
+ Event_db_repository::drop_schema_events()
+ thd Thread
+ schema The database to clean from events
+*/
+
+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));
+
+ if (open_event_table(thd, TL_WRITE, &table))
+ DBUG_VOID_RETURN;
+
+ /* only enabled events are in memory, so we go now and delete the rest */
+ init_read_record(&read_record_info, thd, table, NULL, 1, 0, FALSE);
+ while (!ret && !(read_record_info.read_record(&read_record_info)) )
+ {
+ char *et_field= get_field(thd->mem_root, table->field[field]);
+
+ /* et_field may be NULL if the table is corrupted or out of memory */
+ if (et_field)
+ {
+ LEX_STRING et_field_lex= { et_field, strlen(et_field) };
+ DBUG_PRINT("info", ("Current event %s name=%s", et_field,
+ get_field(thd->mem_root,
+ table->field[ET_FIELD_NAME])));
+
+ if (!sortcmp_lex_string(et_field_lex, field_value, system_charset_info))
+ {
+ DBUG_PRINT("info", ("Dropping"));
+ if ((ret= table->file->ha_delete_row(table->record[0])))
+ table->file->print_error(ret, MYF(0));
+ }
+ }
+ }
+ end_read_record(&read_record_info);
+ close_thread_tables(thd);
+
+ DBUG_VOID_RETURN;
+}
+
+
+/**
+ Looks for a named event in mysql.event and then loads it from
+ the table.
+
+ @pre The given thread does not have open tables.
+
+ @retval FALSE success
+ @retval TRUE error
+*/
+
+bool
+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;
+
+ DBUG_ENTER("Event_db_repository::load_named_event");
+ DBUG_PRINT("enter",("thd: 0x%lx name: %*s", (long) thd,
+ (int) name.length, name.str));
+
+ /* Reset sql_mode during data dictionary operations. */
+ thd->variables.sql_mode= 0;
+
+ if (!(ret= open_event_table(thd, TL_READ, &table)))
+ {
+ if ((ret= find_named_event(dbname, name, table)))
+ my_error(ER_EVENT_DOES_NOT_EXIST, MYF(0), name.str);
+ else if ((ret= etn->load_from_row(thd, table)))
+ my_error(ER_CANNOT_LOAD_FROM_TABLE, MYF(0), "event");
+
+ close_thread_tables(thd);
+ }
+
+ thd->variables.sql_mode= saved_mode;
+ DBUG_RETURN(ret);
+}
+
+
+/**
+ Update the event record in mysql.event table with a changed status
+ and/or last execution time.
+
+ @pre The thread handle does not have open tables.
+*/
+
+bool
+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;
+
+ DBUG_ENTER("Event_db_repository::update_timing_fields_for_event");
+
+ /*
+ Turn off row binlogging of event timing updates. These are not used
+ for RBR of events replicated to the slave.
+ */
+ if (thd->current_stmt_binlog_row_based)
+ thd->clear_current_stmt_binlog_row_based();
+
+ DBUG_ASSERT(thd->security_ctx->master_access & SUPER_ACL);
+
+ if (open_event_table(thd, TL_WRITE, &table))
+ goto end;
+
+ fields= table->field;
+
+ if (find_named_event(event_db_name, event_name, table))
+ goto end;
+
+ store_record(table, record[1]);
+ /* 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);
+
+ fields[ET_FIELD_LAST_EXECUTED]->set_notnull();
+ fields[ET_FIELD_LAST_EXECUTED]->store_time(&time,
+ MYSQL_TIMESTAMP_DATETIME);
+ }
+ if (update_status)
+ {
+ 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])))
+ {
+ table->file->print_error(ret, MYF(0));
+ goto end;
+ }
+
+ ret= 0;
+
+end:
+ if (table)
+ close_thread_tables(thd);
+
+ DBUG_RETURN(test(ret));
+}
+
+
+/**
+ Open mysql.db, mysql.user and mysql.event and check whether:
+ - mysql.db exists and is up to date (or from a newer version of MySQL),
+ - mysql.user has column Event_priv at an expected position,
+ - mysql.event exists and is up to date (or from a newer version of
+ MySQL)
+
+ This function is called only when the server is started.
+ @pre The passed in thread handle has no open tables.
+
+ @retval FALSE OK
+ @retval TRUE Error, an error message is output to the error log.
+*/
+
+bool
+Event_db_repository::check_system_tables(THD *thd)
+{
+ TABLE_LIST tables;
+ int ret= FALSE;
+ const unsigned int event_priv_column_position= 29;
+
+ DBUG_ENTER("Event_db_repository::check_system_tables");
+ DBUG_PRINT("enter", ("thd: 0x%lx", (long) thd));
+
+
+ /* Check mysql.db */
+ tables.init_one_table("mysql", "db", TL_READ);
+
+ if (simple_open_n_lock_tables(thd, &tables))
+ {
+ ret= 1;
+ sql_print_error("Cannot open mysql.db");
+ }
+ else
+ {
+ if (table_check_intact(tables.table, MYSQL_DB_FIELD_COUNT,
+ mysql_db_table_fields))
+ ret= 1;
+ /* in case of an error, the message is printed inside table_check_intact */
+
+ close_thread_tables(thd);
+ }
+ /* Check mysql.user */
+ tables.init_one_table("mysql", "user", TL_READ);
+
+ if (simple_open_n_lock_tables(thd, &tables))
+ {
+ ret= 1;
+ sql_print_error("Cannot open mysql.user");
+ }
+ else
+ {
+ if (tables.table->s->fields < event_priv_column_position ||
+ strncmp(tables.table->field[event_priv_column_position]->field_name,
+ STRING_WITH_LEN("Event_priv")))
+ {
+ sql_print_error("mysql.user has no `Event_priv` column at position %d",
+ event_priv_column_position);
+ ret= 1;
+ }
+ close_thread_tables(thd);
+ }
+ /* Check mysql.event */
+ tables.init_one_table("mysql", "event", TL_READ);
+
+ if (simple_open_n_lock_tables(thd, &tables))
+ {
+ ret= 1;
+ sql_print_error("Cannot open mysql.event");
+ }
+ else
+ {
+ if (table_check_intact(tables.table, ET_FIELD_COUNT, event_table_fields))
+ ret= 1;
+ /* in case of an error, the message is printed inside table_check_intact */
+ close_thread_tables(thd);
+ }
+
+ DBUG_RETURN(test(ret));
+}
+
+/**
+ @} (End of group Event_Scheduler)
+*/
diff --git a/sql/event_db_repository.h b/sql/event_db_repository.h
new file mode 100644
index 00000000000..ef778407d1e
--- /dev/null
+++ b/sql/event_db_repository.h
@@ -0,0 +1,131 @@
+#ifndef _EVENT_DB_REPOSITORY_H_
+#define _EVENT_DB_REPOSITORY_H_
+/* Copyright (C) 2004-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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+/**
+ @addtogroup Event_Scheduler
+ @{
+
+ @file event_db_repository.h
+
+ Data Dictionary related operations of Event Scheduler.
+
+ This is a private header file of Events module. Please do not include it
+ directly. All public declarations of Events module should be stored in
+ events.h and event_data_objects.h.
+*/
+
+enum enum_events_table_field
+{
+ ET_FIELD_DB = 0,
+ ET_FIELD_NAME,
+ ET_FIELD_BODY,
+ ET_FIELD_DEFINER,
+ ET_FIELD_EXECUTE_AT,
+ ET_FIELD_INTERVAL_EXPR,
+ ET_FIELD_TRANSIENT_INTERVAL,
+ ET_FIELD_CREATED,
+ ET_FIELD_MODIFIED,
+ ET_FIELD_LAST_EXECUTED,
+ ET_FIELD_STARTS,
+ ET_FIELD_ENDS,
+ ET_FIELD_STATUS,
+ ET_FIELD_ON_COMPLETION,
+ ET_FIELD_SQL_MODE,
+ ET_FIELD_COMMENT,
+ ET_FIELD_ORIGINATOR,
+ ET_FIELD_TIME_ZONE,
+ ET_FIELD_CHARACTER_SET_CLIENT,
+ ET_FIELD_COLLATION_CONNECTION,
+ ET_FIELD_DB_COLLATION,
+ ET_FIELD_BODY_UTF8,
+ ET_FIELD_COUNT /* a cool trick to count the number of fields :) */
+};
+
+
+int
+events_table_index_read_for_db(THD *thd, TABLE *schema_table,
+ TABLE *event_table);
+
+int
+events_table_scan_all(THD *thd, TABLE *schema_table, TABLE *event_table);
+
+
+class Event_basic;
+class Event_parse_data;
+
+class Event_db_repository
+{
+public:
+ Event_db_repository(){}
+
+ bool
+ create_event(THD *thd, Event_parse_data *parse_data, my_bool create_if_not);
+
+ bool
+ update_event(THD *thd, Event_parse_data *parse_data, LEX_STRING *new_dbname,
+ LEX_STRING *new_name);
+
+ bool
+ drop_event(THD *thd, LEX_STRING db, LEX_STRING name, bool drop_if_exists);
+
+ void
+ drop_schema_events(THD *thd, LEX_STRING schema);
+
+ bool
+ find_named_event(LEX_STRING db, LEX_STRING name, TABLE *table);
+
+ bool
+ load_named_event(THD *thd, LEX_STRING dbname, LEX_STRING name, Event_basic *et);
+
+ bool
+ open_event_table(THD *thd, enum thr_lock_type lock_type, TABLE **table);
+
+ bool
+ fill_schema_events(THD *thd, TABLE_LIST *tables, const char *db);
+
+ bool
+ 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);
+
+ bool
+ table_scan_all_for_i_s(THD *thd, TABLE *schema_table, TABLE *event_table);
+
+private:
+ /* Prevent use of these */
+ Event_db_repository(const Event_db_repository &);
+ void operator=(Event_db_repository &);
+};
+
+/**
+ @} (End of group Event_Scheduler)
+*/
+#endif /* _EVENT_DB_REPOSITORY_H_ */
diff --git a/sql/event_parse_data.cc b/sql/event_parse_data.cc
new file mode 100644
index 00000000000..86905b38627
--- /dev/null
+++ b/sql/event_parse_data.cc
@@ -0,0 +1,577 @@
+/* Copyright 2000-2008 MySQL AB, 2008 Sun Microsystems, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#include "mysql_priv.h"
+#include "sp_head.h"
+#include "event_parse_data.h"
+
+/*
+ Returns a new instance
+
+ SYNOPSIS
+ Event_parse_data::new_instance()
+
+ RETURN VALUE
+ Address or NULL in case of error
+
+ NOTE
+ Created on THD's mem_root
+*/
+
+Event_parse_data *
+Event_parse_data::new_instance(THD *thd)
+{
+ return new (thd->mem_root) Event_parse_data;
+}
+
+
+/*
+ Constructor
+
+ SYNOPSIS
+ Event_parse_data::Event_parse_data()
+*/
+
+Event_parse_data::Event_parse_data()
+ :on_completion(Event_parse_data::ON_COMPLETION_DEFAULT),
+ status(Event_parse_data::ENABLED),
+ do_not_create(FALSE),
+ body_changed(FALSE),
+ item_starts(NULL), item_ends(NULL), item_execute_at(NULL),
+ starts_null(TRUE), ends_null(TRUE), execute_at_null(TRUE),
+ item_expression(NULL), expression(0)
+{
+ DBUG_ENTER("Event_parse_data::Event_parse_data");
+
+ /* Actually in the parser STARTS is always set */
+ starts= ends= execute_at= 0;
+
+ comment.str= NULL;
+ comment.length= 0;
+
+ DBUG_VOID_RETURN;
+}
+
+
+/*
+ Set a name of the event
+
+ SYNOPSIS
+ Event_parse_data::init_name()
+ thd THD
+ spn the name extracted in the parser
+*/
+
+void
+Event_parse_data::init_name(THD *thd, sp_name *spn)
+{
+ DBUG_ENTER("Event_parse_data::init_name");
+
+ /* We have to copy strings to get them into the right memroot */
+ dbname.length= spn->m_db.length;
+ dbname.str= thd->strmake(spn->m_db.str, spn->m_db.length);
+ name.length= spn->m_name.length;
+ name.str= thd->strmake(spn->m_name.str, spn->m_name.length);
+
+ if (spn->m_qname.length == 0)
+ spn->init_qname(thd);
+
+ DBUG_VOID_RETURN;
+}
+
+
+/*
+ This function is called on CREATE EVENT or ALTER EVENT. When either
+ ENDS or AT is in the past, we are trying to create an event that
+ will never be executed. If it has ON COMPLETION NOT PRESERVE
+ (default), then it would normally be dropped already, so on CREATE
+ EVENT we give a warning, and do not create anyting. On ALTER EVENT
+ we give a error, and do not change the event.
+
+ If the event has ON COMPLETION PRESERVE, then we see if the event is
+ created or altered to the ENABLED (default) state. If so, then we
+ give a warning, and change the state to DISABLED.
+
+ Otherwise it is a valid event in ON COMPLETION PRESERVE DISABLE
+ state.
+*/
+
+void
+Event_parse_data::check_if_in_the_past(THD *thd, my_time_t ltime_utc)
+{
+ if (ltime_utc >= (my_time_t) thd->query_start())
+ return;
+
+ /*
+ We'll come back later when we have the real on_completion value
+ */
+ if (on_completion == Event_parse_data::ON_COMPLETION_DEFAULT)
+ return;
+
+ if (on_completion == Event_parse_data::ON_COMPLETION_DROP)
+ {
+ switch (thd->lex->sql_command) {
+ case SQLCOM_CREATE_EVENT:
+ push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
+ ER_EVENT_CANNOT_CREATE_IN_THE_PAST,
+ ER(ER_EVENT_CANNOT_CREATE_IN_THE_PAST));
+ break;
+ case SQLCOM_ALTER_EVENT:
+ my_error(ER_EVENT_CANNOT_ALTER_IN_THE_PAST, MYF(0));
+ break;
+ default:
+ DBUG_ASSERT(0);
+ }
+
+ do_not_create= TRUE;
+ }
+ else if (status == Event_parse_data::ENABLED)
+ {
+ status= Event_parse_data::DISABLED;
+ push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
+ ER_EVENT_EXEC_TIME_IN_THE_PAST,
+ ER(ER_EVENT_EXEC_TIME_IN_THE_PAST));
+ }
+}
+
+
+/*
+ Check time/dates in ALTER EVENT
+
+ We check whether ALTER EVENT was given dates that are in the past.
+ However to know how to react, we need the ON COMPLETION type. Hence,
+ the check is deferred until we have the previous ON COMPLETION type
+ from the event-db to fall back on if nothing was specified in the
+ ALTER EVENT-statement.
+
+ SYNOPSIS
+ Event_parse_data::check_dates()
+ thd Thread
+ on_completion ON COMPLETION value currently in event-db.
+ Will be overridden by value in ALTER EVENT if given.
+
+ RETURN VALUE
+ TRUE an error occurred, do not ALTER
+ FALSE OK
+*/
+
+bool
+Event_parse_data::check_dates(THD *thd, int previous_on_completion)
+{
+ if (on_completion == Event_parse_data::ON_COMPLETION_DEFAULT)
+ {
+ on_completion= previous_on_completion;
+ if (!ends_null)
+ check_if_in_the_past(thd, ends);
+ if (!execute_at_null)
+ check_if_in_the_past(thd, execute_at);
+ }
+ return do_not_create;
+}
+
+
+
+/*
+ Sets time for execution for one-time event.
+
+ SYNOPSIS
+ Event_parse_data::init_execute_at()
+ thd Thread
+
+ RETURN VALUE
+ 0 OK
+ ER_WRONG_VALUE Wrong value for execute at (reported)
+*/
+
+int
+Event_parse_data::init_execute_at(THD *thd)
+{
+ my_bool not_used;
+ MYSQL_TIME ltime;
+ my_time_t ltime_utc;
+
+ DBUG_ENTER("Event_parse_data::init_execute_at");
+
+ if (!item_execute_at)
+ DBUG_RETURN(0);
+
+ if (item_execute_at->fix_fields(thd, &item_execute_at))
+ goto wrong_value;
+
+ /* no starts and/or ends in case of execute_at */
+ DBUG_PRINT("info", ("starts_null && ends_null should be 1 is %d",
+ (starts_null && ends_null)));
+ DBUG_ASSERT(starts_null && ends_null);
+
+ if ((not_used= item_execute_at->get_date(&ltime, TIME_NO_ZERO_DATE)))
+ goto wrong_value;
+
+ ltime_utc= TIME_to_timestamp(thd,&ltime,&not_used);
+ if (!ltime_utc)
+ {
+ DBUG_PRINT("error", ("Execute AT after year 2037"));
+ goto wrong_value;
+ }
+
+ check_if_in_the_past(thd, ltime_utc);
+
+ execute_at_null= FALSE;
+ execute_at= ltime_utc;
+ DBUG_RETURN(0);
+
+wrong_value:
+ report_bad_value("AT", item_execute_at);
+ DBUG_RETURN(ER_WRONG_VALUE);
+}
+
+
+/*
+ Sets time for execution of multi-time event.s
+
+ SYNOPSIS
+ Event_parse_data::init_interval()
+ thd Thread
+
+ RETURN VALUE
+ 0 OK
+ EVEX_BAD_PARAMS Interval is not positive or MICROSECOND (reported)
+ ER_WRONG_VALUE Wrong value for interval (reported)
+*/
+
+int
+Event_parse_data::init_interval(THD *thd)
+{
+ String value;
+ INTERVAL interval_tmp;
+
+ DBUG_ENTER("Event_parse_data::init_interval");
+ if (!item_expression)
+ DBUG_RETURN(0);
+
+ switch (interval) {
+ case INTERVAL_MINUTE_MICROSECOND:
+ case INTERVAL_HOUR_MICROSECOND:
+ case INTERVAL_DAY_MICROSECOND:
+ case INTERVAL_SECOND_MICROSECOND:
+ case INTERVAL_MICROSECOND:
+ my_error(ER_NOT_SUPPORTED_YET, MYF(0), "MICROSECOND");
+ DBUG_RETURN(EVEX_BAD_PARAMS);
+ default:
+ break;
+ }
+
+ 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))
+ goto wrong_value;
+
+ expression= 0;
+
+ switch (interval) {
+ case INTERVAL_YEAR:
+ expression= interval_tmp.year;
+ break;
+ case INTERVAL_QUARTER:
+ case INTERVAL_MONTH:
+ expression= interval_tmp.month;
+ break;
+ case INTERVAL_WEEK:
+ case INTERVAL_DAY:
+ expression= interval_tmp.day;
+ break;
+ case INTERVAL_HOUR:
+ expression= interval_tmp.hour;
+ break;
+ case INTERVAL_MINUTE:
+ expression= interval_tmp.minute;
+ break;
+ case INTERVAL_SECOND:
+ expression= interval_tmp.second;
+ break;
+ case INTERVAL_YEAR_MONTH: // Allow YEAR-MONTH YYYYYMM
+ expression= interval_tmp.year* 12 + interval_tmp.month;
+ break;
+ case INTERVAL_DAY_HOUR:
+ expression= interval_tmp.day* 24 + interval_tmp.hour;
+ break;
+ case INTERVAL_DAY_MINUTE:
+ expression= (interval_tmp.day* 24 + interval_tmp.hour) * 60 +
+ interval_tmp.minute;
+ break;
+ case INTERVAL_HOUR_SECOND: /* day is anyway 0 */
+ case INTERVAL_DAY_SECOND:
+ /* DAY_SECOND having problems because of leap seconds? */
+ expression= ((interval_tmp.day* 24 + interval_tmp.hour) * 60 +
+ interval_tmp.minute)*60
+ + interval_tmp.second;
+ break;
+ case INTERVAL_HOUR_MINUTE:
+ expression= interval_tmp.hour * 60 + interval_tmp.minute;
+ break;
+ case INTERVAL_MINUTE_SECOND:
+ expression= interval_tmp.minute * 60 + interval_tmp.second;
+ break;
+ case INTERVAL_LAST:
+ DBUG_ASSERT(0);
+ default:
+ ;/* these are the microsec stuff */
+ }
+ if (interval_tmp.neg || expression == 0 ||
+ expression > EVEX_MAX_INTERVAL_VALUE)
+ {
+ my_error(ER_EVENT_INTERVAL_NOT_POSITIVE_OR_TOO_BIG, MYF(0));
+ DBUG_RETURN(EVEX_BAD_PARAMS);
+ }
+
+ DBUG_RETURN(0);
+
+wrong_value:
+ report_bad_value("INTERVAL", item_expression);
+ DBUG_RETURN(ER_WRONG_VALUE);
+}
+
+
+/*
+ Sets STARTS.
+
+ SYNOPSIS
+ Event_parse_data::init_starts()
+ expr how much?
+
+ NOTES
+ Note that activation time is not execution time.
+ EVERY 5 MINUTE STARTS "2004-12-12 10:00:00" means that
+ the event will be executed every 5 minutes but this will
+ start at the date shown above. Expressions are possible :
+ DATE_ADD(NOW(), INTERVAL 1 DAY) -- start tommorow at
+ same time.
+
+ RETURN VALUE
+ 0 OK
+ ER_WRONG_VALUE Starts before now
+*/
+
+int
+Event_parse_data::init_starts(THD *thd)
+{
+ my_bool not_used;
+ MYSQL_TIME ltime;
+ my_time_t ltime_utc;
+
+ DBUG_ENTER("Event_parse_data::init_starts");
+ if (!item_starts)
+ DBUG_RETURN(0);
+
+ if (item_starts->fix_fields(thd, &item_starts))
+ goto wrong_value;
+
+ if ((not_used= item_starts->get_date(&ltime, TIME_NO_ZERO_DATE)))
+ goto wrong_value;
+
+ ltime_utc= TIME_to_timestamp(thd, &ltime, &not_used);
+ if (!ltime_utc)
+ goto wrong_value;
+
+ DBUG_PRINT("info",("now: %ld starts: %ld",
+ (long) thd->query_start(), (long) ltime_utc));
+
+ starts_null= FALSE;
+ starts= ltime_utc;
+ DBUG_RETURN(0);
+
+wrong_value:
+ report_bad_value("STARTS", item_starts);
+ DBUG_RETURN(ER_WRONG_VALUE);
+}
+
+
+/*
+ Sets ENDS (deactivation time).
+
+ SYNOPSIS
+ Event_parse_data::init_ends()
+ thd THD
+
+ NOTES
+ Note that activation time is not execution time.
+ EVERY 5 MINUTE ENDS "2004-12-12 10:00:00" means that
+ the event will be executed every 5 minutes but this will
+ end at the date shown above. Expressions are possible :
+ DATE_ADD(NOW(), INTERVAL 1 DAY) -- end tommorow at
+ same time.
+
+ RETURN VALUE
+ 0 OK
+ EVEX_BAD_PARAMS Error (reported)
+*/
+
+int
+Event_parse_data::init_ends(THD *thd)
+{
+ my_bool not_used;
+ MYSQL_TIME ltime;
+ my_time_t ltime_utc;
+
+ DBUG_ENTER("Event_parse_data::init_ends");
+ if (!item_ends)
+ DBUG_RETURN(0);
+
+ if (item_ends->fix_fields(thd, &item_ends))
+ goto error_bad_params;
+
+ DBUG_PRINT("info", ("convert to TIME"));
+ if ((not_used= item_ends->get_date(&ltime, TIME_NO_ZERO_DATE)))
+ goto error_bad_params;
+
+ ltime_utc= TIME_to_timestamp(thd, &ltime, &not_used);
+ if (!ltime_utc)
+ goto error_bad_params;
+
+ /* Check whether ends is after starts */
+ DBUG_PRINT("info", ("ENDS after STARTS?"));
+ if (!starts_null && starts >= ltime_utc)
+ goto error_bad_params;
+
+ check_if_in_the_past(thd, ltime_utc);
+
+ ends_null= FALSE;
+ ends= ltime_utc;
+ DBUG_RETURN(0);
+
+error_bad_params:
+ my_error(ER_EVENT_ENDS_BEFORE_STARTS, MYF(0));
+ DBUG_RETURN(EVEX_BAD_PARAMS);
+}
+
+
+/*
+ Prints an error message about invalid value. Internally used
+ during input data verification
+
+ SYNOPSIS
+ Event_parse_data::report_bad_value()
+ item_name The name of the parameter
+ bad_item The parameter
+*/
+
+void
+Event_parse_data::report_bad_value(const char *item_name, Item *bad_item)
+{
+ char buff[120];
+ String str(buff,(uint32) sizeof(buff), system_charset_info);
+ String *str2= bad_item->fixed? bad_item->val_str(&str):NULL;
+ my_error(ER_WRONG_VALUE, MYF(0), item_name, str2? str2->c_ptr_safe():"NULL");
+}
+
+
+/*
+ Checks for validity the data gathered during the parsing phase.
+
+ SYNOPSIS
+ Event_parse_data::check_parse_data()
+ thd Thread
+
+ RETURN VALUE
+ FALSE OK
+ TRUE Error (reported)
+*/
+
+bool
+Event_parse_data::check_parse_data(THD *thd)
+{
+ bool ret;
+ DBUG_ENTER("Event_parse_data::check_parse_data");
+ DBUG_PRINT("info", ("execute_at: 0x%lx expr=0x%lx starts=0x%lx ends=0x%lx",
+ (long) item_execute_at, (long) item_expression,
+ (long) item_starts, (long) item_ends));
+
+ init_name(thd, identifier);
+
+ init_definer(thd);
+
+ ret= init_execute_at(thd) || init_interval(thd) || init_starts(thd) ||
+ init_ends(thd);
+ check_originator_id(thd);
+ DBUG_RETURN(ret);
+}
+
+
+/*
+ Inits definer (definer_user and definer_host) during parsing.
+
+ SYNOPSIS
+ Event_parse_data::init_definer()
+ thd Thread
+*/
+
+void
+Event_parse_data::init_definer(THD *thd)
+{
+ DBUG_ENTER("Event_parse_data::init_definer");
+
+ DBUG_ASSERT(thd->lex->definer);
+
+ const char *definer_user= thd->lex->definer->user.str;
+ const char *definer_host= thd->lex->definer->host.str;
+ size_t definer_user_len= thd->lex->definer->user.length;
+ size_t definer_host_len= thd->lex->definer->host.length;
+
+ DBUG_PRINT("info",("init definer_user thd->mem_root: 0x%lx "
+ "definer_user: 0x%lx", (long) thd->mem_root,
+ (long) definer_user));
+
+ /* + 1 for @ */
+ DBUG_PRINT("info",("init definer as whole"));
+ definer.length= definer_user_len + definer_host_len + 1;
+ definer.str= (char*) thd->alloc(definer.length + 1);
+
+ DBUG_PRINT("info",("copy the user"));
+ memcpy(definer.str, definer_user, definer_user_len);
+ definer.str[definer_user_len]= '@';
+
+ DBUG_PRINT("info",("copy the host"));
+ memcpy(definer.str + definer_user_len + 1, definer_host, definer_host_len);
+ definer.str[definer.length]= '\0';
+ DBUG_PRINT("info",("definer [%s] initted", definer.str));
+
+ DBUG_VOID_RETURN;
+}
+
+
+/**
+ Set the originator id of the event to the server_id if executing on
+ the master or set to the server_id of the master if executing on
+ the slave. If executing on slave, also set status to SLAVESIDE_DISABLED.
+
+ SYNOPSIS
+ Event_parse_data::check_originator_id()
+*/
+void Event_parse_data::check_originator_id(THD *thd)
+{
+ /* Disable replicated events on slave. */
+ if ((thd->system_thread == SYSTEM_THREAD_SLAVE_SQL) ||
+ (thd->system_thread == SYSTEM_THREAD_SLAVE_IO))
+ {
+ DBUG_PRINT("info", ("Invoked object status set to SLAVESIDE_DISABLED."));
+ if ((status == Event_parse_data::ENABLED) ||
+ (status == Event_parse_data::DISABLED))
+ status = Event_parse_data::SLAVESIDE_DISABLED;
+ originator = thd->server_id;
+ }
+ else
+ originator = server_id;
+}
diff --git a/sql/event_parse_data.h b/sql/event_parse_data.h
new file mode 100644
index 00000000000..8b42eb23937
--- /dev/null
+++ b/sql/event_parse_data.h
@@ -0,0 +1,124 @@
+/* Copyright 2000-2008 MySQL AB, 2008 Sun Microsystems, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#ifndef _EVENT_PARSE_DATA_H_
+#define _EVENT_PARSE_DATA_H_
+
+#define EVEX_GET_FIELD_FAILED -2
+#define EVEX_BAD_PARAMS -5
+#define EVEX_MICROSECOND_UNSUP -6
+#define EVEX_MAX_INTERVAL_VALUE 1000000000L
+
+class Event_parse_data : public Sql_alloc
+{
+public:
+ /*
+ ENABLED = feature can function normally (is turned on)
+ SLAVESIDE_DISABLED = feature is turned off on slave
+ DISABLED = feature is turned off
+ */
+ enum enum_status
+ {
+ ENABLED = 1,
+ DISABLED,
+ SLAVESIDE_DISABLED
+ };
+
+ enum enum_on_completion
+ {
+ /*
+ On CREATE EVENT, DROP is the DEFAULT as per the docs.
+ On ALTER EVENT, "no change" is the DEFAULT.
+ */
+ ON_COMPLETION_DEFAULT = 0,
+ ON_COMPLETION_DROP,
+ ON_COMPLETION_PRESERVE
+ };
+
+ int on_completion;
+ int status;
+ longlong originator;
+ /*
+ do_not_create will be set if STARTS time is in the past and
+ on_completion == ON_COMPLETION_DROP.
+ */
+ bool do_not_create;
+
+ bool body_changed;
+
+ LEX_STRING dbname;
+ LEX_STRING name;
+ LEX_STRING definer;// combination of user and host
+ LEX_STRING comment;
+
+ Item* item_starts;
+ Item* item_ends;
+ Item* item_execute_at;
+
+ my_time_t starts;
+ my_time_t ends;
+ my_time_t execute_at;
+ my_bool starts_null;
+ my_bool ends_null;
+ my_bool execute_at_null;
+
+ sp_name *identifier;
+ Item* item_expression;
+ longlong expression;
+ interval_type interval;
+
+ static Event_parse_data *
+ new_instance(THD *thd);
+
+ bool
+ check_parse_data(THD *thd);
+
+ bool
+ check_dates(THD *thd, int previous_on_completion);
+
+private:
+
+ void
+ init_definer(THD *thd);
+
+ void
+ init_name(THD *thd, sp_name *spn);
+
+ int
+ init_execute_at(THD *thd);
+
+ int
+ init_interval(THD *thd);
+
+ int
+ init_starts(THD *thd);
+
+ int
+ init_ends(THD *thd);
+
+ Event_parse_data();
+ ~Event_parse_data();
+
+ void
+ report_bad_value(const char *item_name, Item *bad_item);
+
+ void
+ check_if_in_the_past(THD *thd, my_time_t ltime_utc);
+
+ Event_parse_data(const Event_parse_data &); /* Prevent use of these */
+ void check_originator_id(THD *thd);
+ void operator=(Event_parse_data &);
+};
+#endif
diff --git a/sql/event_queue.cc b/sql/event_queue.cc
new file mode 100644
index 00000000000..04d4f858b43
--- /dev/null
+++ b/sql/event_queue.cc
@@ -0,0 +1,794 @@
+/* Copyright (C) 2004-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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#include "mysql_priv.h"
+#include "event_queue.h"
+#include "event_data_objects.h"
+
+/**
+ @addtogroup Event_Scheduler
+ @{
+*/
+
+#define EVENT_QUEUE_INITIAL_SIZE 30
+#define EVENT_QUEUE_EXTENT 30
+
+#ifdef __GNUC__
+#if __GNUC__ >= 2
+#define SCHED_FUNC __FUNCTION__
+#endif
+#else
+#define SCHED_FUNC "<unknown>"
+#endif
+
+#define LOCK_QUEUE_DATA() lock_data(SCHED_FUNC, __LINE__)
+#define UNLOCK_QUEUE_DATA() unlock_data(SCHED_FUNC, __LINE__)
+
+/*
+ Compares the execute_at members of two Event_queue_element instances.
+ Used as callback for the prioritized queue when shifting
+ elements inside.
+
+ SYNOPSIS
+ event_queue_element_data_compare_q()
+ vptr Not used (set it to NULL)
+ a First Event_queue_element object
+ b Second Event_queue_element object
+
+ RETURN VALUE
+ -1 a->execute_at < b->execute_at
+ 0 a->execute_at == b->execute_at
+ 1 a->execute_at > b->execute_at
+
+ NOTES
+ execute_at.second_part is not considered during comparison
+*/
+
+extern "C" int event_queue_element_compare_q(void *, uchar *, uchar *);
+
+int event_queue_element_compare_q(void *vptr, uchar* a, uchar *b)
+{
+ Event_queue_element *left = (Event_queue_element *)a;
+ Event_queue_element *right = (Event_queue_element *)b;
+ my_time_t lhs = left->execute_at;
+ my_time_t rhs = right->execute_at;
+
+ if (left->status == Event_parse_data::DISABLED)
+ return right->status != Event_parse_data::DISABLED;
+
+ if (right->status == Event_parse_data::DISABLED)
+ return 1;
+
+ return (lhs < rhs ? -1 : (lhs > rhs ? 1 : 0));
+}
+
+
+/*
+ Constructor of class Event_queue.
+
+ SYNOPSIS
+ Event_queue::Event_queue()
+*/
+
+Event_queue::Event_queue()
+ :next_activation_at(0),
+ mutex_last_locked_at_line(0),
+ mutex_last_unlocked_at_line(0),
+ mutex_last_attempted_lock_at_line(0),
+ mutex_last_locked_in_func("n/a"),
+ mutex_last_unlocked_in_func("n/a"),
+ mutex_last_attempted_lock_in_func("n/a"),
+ mutex_queue_data_locked(FALSE),
+ mutex_queue_data_attempting_lock(FALSE),
+ waiting_on_cond(FALSE)
+{
+ pthread_mutex_init(&LOCK_event_queue, MY_MUTEX_INIT_FAST);
+ pthread_cond_init(&COND_queue_state, NULL);
+}
+
+
+Event_queue::~Event_queue()
+{
+ deinit_queue();
+ pthread_mutex_destroy(&LOCK_event_queue);
+ pthread_cond_destroy(&COND_queue_state);
+}
+
+
+/*
+ This is a queue's constructor. Until this method is called, the
+ queue is unusable. We don't use a C++ constructor instead in
+ order to be able to check the return value. The queue is
+ initialized once at server startup. Initialization can fail in
+ case of a failure reading events from the database or out of
+ memory.
+
+ SYNOPSIS
+ Event_queue::init()
+
+ RETURN VALUE
+ FALSE OK
+ TRUE Error
+*/
+
+bool
+Event_queue::init_queue(THD *thd)
+{
+ DBUG_ENTER("Event_queue::init_queue");
+ DBUG_PRINT("enter", ("this: 0x%lx", (long) this));
+
+ LOCK_QUEUE_DATA();
+
+ if (init_queue_ex(&queue, EVENT_QUEUE_INITIAL_SIZE , 0 /*offset*/,
+ 0 /*max_on_top*/, event_queue_element_compare_q,
+ NULL, EVENT_QUEUE_EXTENT))
+ {
+ sql_print_error("Event Scheduler: Can't initialize the execution queue");
+ goto err;
+ }
+
+ UNLOCK_QUEUE_DATA();
+ DBUG_RETURN(FALSE);
+
+err:
+ UNLOCK_QUEUE_DATA();
+ DBUG_RETURN(TRUE);
+}
+
+
+/*
+ Deinits the queue. Remove all elements from it and destroys them
+ too.
+
+ SYNOPSIS
+ Event_queue::deinit_queue()
+*/
+
+void
+Event_queue::deinit_queue()
+{
+ DBUG_ENTER("Event_queue::deinit_queue");
+
+ LOCK_QUEUE_DATA();
+ empty_queue();
+ delete_queue(&queue);
+ UNLOCK_QUEUE_DATA();
+
+ DBUG_VOID_RETURN;
+}
+
+
+/**
+ Adds an event to the queue.
+
+ Compute the next execution time for an event, and if it is still
+ active, add it to the queue. Otherwise delete it.
+ The object is left intact in case of an error. Otherwise
+ the queue container assumes ownership of it.
+
+ @param[in] thd thread handle
+ @param[in] new_element a new element to add to the queue
+ @param[out] created set to TRUE if no error and the element is
+ added to the queue, FALSE otherwise
+
+ @retval TRUE an error occured. The value of created is undefined,
+ the element was not deleted.
+ @retval FALSE success
+*/
+
+bool
+Event_queue::create_event(THD *thd, Event_queue_element *new_element,
+ bool *created)
+{
+ DBUG_ENTER("Event_queue::create_event");
+ DBUG_PRINT("enter", ("thd: 0x%lx et=%s.%s", (long) thd,
+ new_element->dbname.str, new_element->name.str));
+
+ /* Will do nothing if the event is disabled */
+ new_element->compute_next_execution_time();
+ if (new_element->status != Event_parse_data::ENABLED)
+ {
+ delete new_element;
+ *created= FALSE;
+ DBUG_RETURN(FALSE);
+ }
+
+ DBUG_PRINT("info", ("new event in the queue: 0x%lx", (long) 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);
+ UNLOCK_QUEUE_DATA();
+
+ DBUG_RETURN(!*created);
+}
+
+
+/*
+ Updates an event from the scheduler queue
+
+ SYNOPSIS
+ Event_queue::update_event()
+ thd Thread
+ dbname Schema of the event
+ name Name of the event
+ new_schema New schema, in case of RENAME TO, otherwise NULL
+ new_name New name, in case of RENAME TO, otherwise NULL
+*/
+
+void
+Event_queue::update_event(THD *thd, LEX_STRING dbname, LEX_STRING name,
+ Event_queue_element *new_element)
+{
+ DBUG_ENTER("Event_queue::update_event");
+ DBUG_PRINT("enter", ("thd: 0x%lx et=[%s.%s]", (long) thd, dbname.str, name.str));
+
+ if ((new_element->status == Event_parse_data::DISABLED) ||
+ (new_element->status == Event_parse_data::SLAVESIDE_DISABLED))
+ {
+ DBUG_PRINT("info", ("The event is disabled."));
+ /*
+ Destroy the object but don't skip to end: because we may have to remove
+ object from the cache.
+ */
+ delete new_element;
+ new_element= NULL;
+ }
+ else
+ new_element->compute_next_execution_time();
+
+ LOCK_QUEUE_DATA();
+ find_n_remove_event(dbname, name);
+
+ /* If not disabled event */
+ if (new_element)
+ {
+ 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);
+ }
+
+ dbug_dump_queue(thd->query_start());
+ UNLOCK_QUEUE_DATA();
+
+ DBUG_VOID_RETURN;
+}
+
+
+/*
+ Drops an event from the queue
+
+ SYNOPSIS
+ Event_queue::drop_event()
+ thd Thread
+ dbname Schema of the event to drop
+ name Name of the event to drop
+*/
+
+void
+Event_queue::drop_event(THD *thd, LEX_STRING dbname, LEX_STRING name)
+{
+ DBUG_ENTER("Event_queue::drop_event");
+ DBUG_PRINT("enter", ("thd: 0x%lx db :%s name: %s", (long) thd,
+ dbname.str, name.str));
+
+ LOCK_QUEUE_DATA();
+ find_n_remove_event(dbname, name);
+ dbug_dump_queue(thd->query_start());
+ UNLOCK_QUEUE_DATA();
+
+ /*
+ We don't signal here because the scheduler will catch the change
+ next time it wakes up.
+ */
+
+ DBUG_VOID_RETURN;
+}
+
+
+/*
+ Drops all events from the in-memory queue and disk that match
+ certain pattern evaluated by a comparator function
+
+ SYNOPSIS
+ Event_queue::drop_matching_events()
+ thd THD
+ pattern A pattern string
+ comparator The function to use for comparing
+
+ RETURN VALUE
+ >=0 Number of dropped events
+
+ NOTE
+ Expected is the caller to acquire lock on LOCK_event_queue
+*/
+
+void
+Event_queue::drop_matching_events(THD *thd, LEX_STRING pattern,
+ bool (*comparator)(LEX_STRING, Event_basic *))
+{
+ uint i= 0;
+ DBUG_ENTER("Event_queue::drop_matching_events");
+ DBUG_PRINT("enter", ("pattern=%s", pattern.str));
+
+ while (i < queue.elements)
+ {
+ Event_queue_element *et= (Event_queue_element *) queue_element(&queue, i);
+ DBUG_PRINT("info", ("[%s.%s]?", et->dbname.str, et->name.str));
+ if (comparator(pattern, et))
+ {
+ /*
+ The queue is ordered. If we remove an element, then all elements
+ after it will shift one position to the left, if we imagine it as
+ an array from left to the right. In this case we should not
+ increment the counter and the (i < queue.elements) condition is ok.
+ */
+ queue_remove(&queue, i);
+ delete et;
+ }
+ else
+ i++;
+ }
+ /*
+ We don't call pthread_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
+ it will signal the scheduler
+ 2. The queue is not empty, but the next event after the previous top,
+ won't be executed any time sooner than the element we removed. Hence,
+ we may not notify the scheduler and it will realize the change when it
+ wakes up from timedwait.
+ */
+
+ DBUG_VOID_RETURN;
+}
+
+
+/*
+ Drops all events from the in-memory queue and disk that are from
+ certain schema.
+
+ SYNOPSIS
+ Event_queue::drop_schema_events()
+ thd HD
+ schema The schema name
+*/
+
+void
+Event_queue::drop_schema_events(THD *thd, LEX_STRING schema)
+{
+ DBUG_ENTER("Event_queue::drop_schema_events");
+ LOCK_QUEUE_DATA();
+ drop_matching_events(thd, schema, event_basic_db_equal);
+ UNLOCK_QUEUE_DATA();
+ DBUG_VOID_RETURN;
+}
+
+
+/*
+ Searches for an event in the queue
+
+ SYNOPSIS
+ Event_queue::find_n_remove_event()
+ db The schema of the event to find
+ name The event to find
+
+ NOTE
+ The caller should do the locking also the caller is responsible for
+ actual signalling in case an event is removed from the queue.
+*/
+
+void
+Event_queue::find_n_remove_event(LEX_STRING db, LEX_STRING name)
+{
+ uint i;
+ DBUG_ENTER("Event_queue::find_n_remove_event");
+
+ for (i= 0; i < queue.elements; ++i)
+ {
+ Event_queue_element *et= (Event_queue_element *) queue_element(&queue, i);
+ DBUG_PRINT("info", ("[%s.%s]==[%s.%s]?", db.str, name.str,
+ et->dbname.str, et->name.str));
+ if (event_basic_identifier_equal(db, name, et))
+ {
+ queue_remove(&queue, i);
+ delete et;
+ break;
+ }
+ }
+
+ DBUG_VOID_RETURN;
+}
+
+
+/*
+ Recalculates activation times in the queue. There is one reason for
+ that. Because the values (execute_at) by which the queue is ordered are
+ changed by calls to compute_next_execution_time() on a request from the
+ scheduler thread, if it is not running then the values won't be updated.
+ Once the scheduler is started again the values has to be recalculated
+ so they are right for the current time.
+
+ SYNOPSIS
+ Event_queue::recalculate_activation_times()
+ thd Thread
+*/
+
+void
+Event_queue::recalculate_activation_times(THD *thd)
+{
+ uint i;
+ DBUG_ENTER("Event_queue::recalculate_activation_times");
+
+ LOCK_QUEUE_DATA();
+ DBUG_PRINT("info", ("%u loaded events to be recalculated", queue.elements));
+ for (i= 0; i < queue.elements; 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);
+ /*
+ The disabled elements are moved to the end during the `fix`.
+ Start from the end and remove all of the elements which are
+ disabled. When we find the first non-disabled one we break, as we
+ have removed all. The queue has been ordered in a way the disabled
+ events are at the end.
+ */
+ for (i= queue.elements; i > 0; i--)
+ {
+ Event_queue_element *element = (Event_queue_element*)queue_element(&queue, i - 1);
+ if (element->status != Event_parse_data::DISABLED)
+ break;
+ /*
+ This won't cause queue re-order, because we remove
+ always the last element.
+ */
+ queue_remove(&queue, i - 1);
+ delete element;
+ }
+ UNLOCK_QUEUE_DATA();
+
+ /*
+ XXX: The events are dropped only from memory and not from disk
+ even if `drop_list[j]->dropped` is TRUE. There will be still on the
+ disk till next server restart.
+ Please add code here to do it.
+ */
+
+ DBUG_VOID_RETURN;
+}
+
+
+/*
+ Empties the queue and destroys the Event_queue_element objects in the
+ queue.
+
+ SYNOPSIS
+ Event_queue::empty_queue()
+
+ NOTE
+ Should be called with LOCK_event_queue locked
+*/
+
+void
+Event_queue::empty_queue()
+{
+ uint i;
+ DBUG_ENTER("Event_queue::empty_queue");
+ DBUG_PRINT("enter", ("Purging the queue. %u element(s)", queue.elements));
+ sql_print_information("Event Scheduler: Purging the queue. %u events",
+ queue.elements);
+ /* empty the queue */
+ for (i= 0; i < queue.elements; ++i)
+ {
+ Event_queue_element *et= (Event_queue_element *) queue_element(&queue, i);
+ delete et;
+ }
+ resize_queue(&queue, 0);
+ DBUG_VOID_RETURN;
+}
+
+
+/*
+ Dumps the queue to the trace log.
+
+ SYNOPSIS
+ Event_queue::dbug_dump_queue()
+ now Current timestamp
+*/
+
+void
+Event_queue::dbug_dump_queue(time_t now)
+{
+#ifndef DBUG_OFF
+ Event_queue_element *et;
+ uint i;
+ DBUG_ENTER("Event_queue::dbug_dump_queue");
+ DBUG_PRINT("info", ("Dumping queue . Elements=%u", queue.elements));
+ for (i = 0; i < queue.elements; i++)
+ {
+ et= ((Event_queue_element*)queue_element(&queue, i));
+ DBUG_PRINT("info", ("et: 0x%lx name: %s.%s", (long) et,
+ et->dbname.str, et->name.str));
+ DBUG_PRINT("info", ("exec_at: %lu starts: %lu ends: %lu execs_so_far: %u "
+ "expr: %ld et.exec_at: %ld now: %ld "
+ "(et.exec_at - now): %d if: %d",
+ (long) et->execute_at, (long) et->starts,
+ (long) et->ends, et->execution_count,
+ (long) et->expression, (long) et->execute_at,
+ (long) now, (int) (et->execute_at - now),
+ et->execute_at <= now));
+ }
+ DBUG_VOID_RETURN;
+#endif
+}
+
+static const char *queue_empty_msg= "Waiting on empty queue";
+static const char *queue_wait_msg= "Waiting for next activation";
+
+/*
+ Checks whether the top of the queue is elligible for execution and
+ returns an Event_job_data instance in case it should be executed.
+ `now` is compared against `execute_at` of the top element in the queue.
+
+ SYNOPSIS
+ Event_queue::get_top_for_execution_if_time()
+ thd [in] Thread
+ event_name [out] The object to execute
+
+ RETURN VALUE
+ FALSE No error. event_name != NULL
+ TRUE Serious error
+*/
+
+bool
+Event_queue::get_top_for_execution_if_time(THD *thd,
+ Event_queue_element_for_exec **event_name)
+{
+ bool ret= FALSE;
+ *event_name= NULL;
+ DBUG_ENTER("Event_queue::get_top_for_execution_if_time");
+
+ LOCK_QUEUE_DATA();
+ for (;;)
+ {
+ Event_queue_element *top= NULL;
+
+ /* Break loop if thd has been killed */
+ if (thd->killed)
+ {
+ DBUG_PRINT("info", ("thd->killed=%d", thd->killed));
+ goto end;
+ }
+
+ if (!queue.elements)
+ {
+ /* There are no events in the queue */
+ next_activation_at= 0;
+
+ /* Wait on condition until signaled. Release LOCK_queue while waiting. */
+ cond_wait(thd, NULL, queue_empty_msg, SCHED_FUNC, __LINE__);
+
+ continue;
+ }
+
+ top= ((Event_queue_element*) queue_element(&queue, 0));
+
+ thd->set_current_time(); /* Get current time */
+
+ next_activation_at= top->execute_at;
+ if (next_activation_at > thd->query_start())
+ {
+ /*
+ 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(top_time, next_activation_at - thd->query_start());
+ cond_wait(thd, &top_time, queue_wait_msg, SCHED_FUNC, __LINE__);
+
+ continue;
+ }
+
+ if (!(*event_name= new Event_queue_element_for_exec()) ||
+ (*event_name)->init(top->dbname, top->name))
+ {
+ ret= TRUE;
+ break;
+ }
+
+ DBUG_PRINT("info", ("Ready for execution"));
+ top->mark_last_executed(thd);
+ if (top->compute_next_execution_time())
+ top->status= Event_parse_data::DISABLED;
+ DBUG_PRINT("info", ("event %s status is %d", top->name.str, top->status));
+
+ top->execution_count++;
+ (*event_name)->dropped= top->dropped;
+
+ top->update_timing_fields(thd);
+ if (top->status == Event_parse_data::DISABLED)
+ {
+ DBUG_PRINT("info", ("removing from the queue"));
+ sql_print_information("Event Scheduler: Last execution of %s.%s. %s",
+ top->dbname.str, top->name.str,
+ top->dropped? "Dropping.":"");
+ delete top;
+ queue_remove(&queue, 0);
+ }
+ else
+ queue_replaced(&queue);
+
+ dbug_dump_queue(thd->query_start());
+ break;
+ }
+end:
+ UNLOCK_QUEUE_DATA();
+
+ DBUG_PRINT("info", ("returning %d et_new: 0x%lx ",
+ ret, (long) *event_name));
+
+ if (*event_name)
+ DBUG_PRINT("info", ("db: %s name: %s",
+ (*event_name)->dbname.str, (*event_name)->name.str));
+
+ DBUG_RETURN(ret);
+}
+
+
+/*
+ Auxiliary function for locking LOCK_event_queue. Used by the
+ LOCK_QUEUE_DATA macro
+
+ SYNOPSIS
+ Event_queue::lock_data()
+ func Which function is requesting mutex lock
+ line On which line mutex lock is requested
+*/
+
+void
+Event_queue::lock_data(const char *func, uint line)
+{
+ DBUG_ENTER("Event_queue::lock_data");
+ DBUG_PRINT("enter", ("func=%s line=%u", func, 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);
+ mutex_last_attempted_lock_in_func= "";
+ mutex_last_attempted_lock_at_line= 0;
+ mutex_queue_data_attempting_lock= FALSE;
+
+ mutex_last_locked_in_func= func;
+ mutex_last_locked_at_line= line;
+ mutex_queue_data_locked= TRUE;
+
+ DBUG_VOID_RETURN;
+}
+
+
+/*
+ Auxiliary function for unlocking LOCK_event_queue. Used by the
+ UNLOCK_QUEUE_DATA macro
+
+ SYNOPSIS
+ Event_queue::unlock_data()
+ func Which function is requesting mutex unlock
+ line On which line mutex unlock is requested
+*/
+
+void
+Event_queue::unlock_data(const char *func, uint line)
+{
+ DBUG_ENTER("Event_queue::unlock_data");
+ DBUG_PRINT("enter", ("func=%s line=%u", func, line));
+ mutex_last_unlocked_at_line= line;
+ mutex_queue_data_locked= FALSE;
+ mutex_last_unlocked_in_func= func;
+ pthread_mutex_unlock(&LOCK_event_queue);
+ DBUG_VOID_RETURN;
+}
+
+
+/*
+ Wrapper for pthread_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()
+ func Which function is requesting cond_wait
+ line On which line cond_wait is requested
+*/
+
+void
+Event_queue::cond_wait(THD *thd, struct timespec *abstime, const char* msg,
+ const char *func, uint line)
+{
+ DBUG_ENTER("Event_queue::cond_wait");
+ waiting_on_cond= TRUE;
+ mutex_last_unlocked_at_line= line;
+ mutex_queue_data_locked= FALSE;
+ mutex_last_unlocked_in_func= func;
+
+ thd->enter_cond(&COND_queue_state, &LOCK_event_queue, msg);
+
+ DBUG_PRINT("info", ("pthread_cond_%swait", abstime? "timed":""));
+ if (!abstime)
+ pthread_cond_wait(&COND_queue_state, &LOCK_event_queue);
+ else
+ pthread_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;
+ waiting_on_cond= FALSE;
+
+ /*
+ This will free the lock so we need to relock. Not the best thing to
+ do but we need to obey cond_wait()
+ */
+ thd->exit_cond("");
+ lock_data(func, line);
+
+ DBUG_VOID_RETURN;
+}
+
+
+/*
+ Dumps the internal status of the queue
+
+ SYNOPSIS
+ Event_queue::dump_internal_status()
+*/
+
+void
+Event_queue::dump_internal_status()
+{
+ DBUG_ENTER("Event_queue::dump_internal_status");
+
+ /* element count */
+ puts("");
+ puts("Event queue status:");
+ printf("Element count : %u\n", queue.elements);
+ printf("Data locked : %s\n", mutex_queue_data_locked? "YES":"NO");
+ printf("Attempting lock : %s\n", mutex_queue_data_attempting_lock? "YES":"NO");
+ printf("LLA : %s:%u\n", mutex_last_locked_in_func,
+ mutex_last_locked_at_line);
+ printf("LUA : %s:%u\n", mutex_last_unlocked_in_func,
+ mutex_last_unlocked_at_line);
+ if (mutex_last_attempted_lock_at_line)
+ printf("Last lock attempt at: %s:%u\n", mutex_last_attempted_lock_in_func,
+ mutex_last_attempted_lock_at_line);
+ printf("WOC : %s\n", waiting_on_cond? "YES":"NO");
+
+ MYSQL_TIME time;
+ my_tz_OFFSET0->gmt_sec_to_TIME(&time, next_activation_at);
+ if (time.year != 1970)
+ printf("Next activation : %04d-%02d-%02d %02d:%02d:%02d\n",
+ time.year, time.month, time.day, time.hour, time.minute, time.second);
+ else
+ printf("Next activation : never");
+
+ DBUG_VOID_RETURN;
+}
+
+/**
+ @} (End of group Event_Scheduler)
+*/
diff --git a/sql/event_queue.h b/sql/event_queue.h
new file mode 100644
index 00000000000..2870ecb4d0b
--- /dev/null
+++ b/sql/event_queue.h
@@ -0,0 +1,126 @@
+#ifndef _EVENT_QUEUE_H_
+#define _EVENT_QUEUE_H_
+/* Copyright (C) 2004-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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+/**
+
+ @addtogroup Event_Scheduler
+ @{
+
+ @file event_queue.h
+
+ Queue of events awaiting execution.
+*/
+
+class Event_basic;
+class Event_queue_element;
+class Event_queue_element_for_exec;
+
+class THD;
+
+/**
+ Queue of active events awaiting execution.
+*/
+
+class Event_queue
+{
+public:
+ Event_queue();
+ ~Event_queue();
+
+ bool
+ init_queue(THD *thd);
+
+ /* Methods for queue management follow */
+
+ bool
+ create_event(THD *thd, Event_queue_element *new_element,
+ bool *created);
+
+ void
+ update_event(THD *thd, LEX_STRING dbname, LEX_STRING name,
+ Event_queue_element *new_element);
+
+ void
+ drop_event(THD *thd, LEX_STRING dbname, LEX_STRING name);
+
+ void
+ drop_schema_events(THD *thd, LEX_STRING schema);
+
+ void
+ recalculate_activation_times(THD *thd);
+
+ bool
+ get_top_for_execution_if_time(THD *thd,
+ Event_queue_element_for_exec **event_name);
+
+
+ void
+ dump_internal_status();
+
+private:
+ void
+ empty_queue();
+
+ void
+ deinit_queue();
+ /* helper functions for working with mutexes & conditionals */
+ void
+ lock_data(const char *func, uint line);
+
+ void
+ unlock_data(const char *func, uint line);
+
+ void
+ cond_wait(THD *thd, struct timespec *abstime, const char* msg,
+ const char *func, uint line);
+
+ void
+ find_n_remove_event(LEX_STRING db, LEX_STRING name);
+
+
+ void
+ drop_matching_events(THD *thd, LEX_STRING pattern,
+ bool (*)(LEX_STRING, Event_basic *));
+
+
+ void
+ dbug_dump_queue(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;
+
+ /* The sorted queue with the Event_queue_element objects */
+ QUEUE queue;
+
+ my_time_t next_activation_at;
+
+ uint mutex_last_locked_at_line;
+ uint mutex_last_unlocked_at_line;
+ uint mutex_last_attempted_lock_at_line;
+ const char* mutex_last_locked_in_func;
+ const char* mutex_last_unlocked_in_func;
+ const char* mutex_last_attempted_lock_in_func;
+ bool mutex_queue_data_locked;
+ bool mutex_queue_data_attempting_lock;
+ bool waiting_on_cond;
+};
+/**
+ @} (End of group Event_Scheduler)
+*/
+
+#endif /* _EVENT_QUEUE_H_ */
diff --git a/sql/event_scheduler.cc b/sql/event_scheduler.cc
new file mode 100644
index 00000000000..d9d010783e8
--- /dev/null
+++ b/sql/event_scheduler.cc
@@ -0,0 +1,796 @@
+/* Copyright (C) 2004-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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#include "mysql_priv.h"
+#include "events.h"
+#include "event_data_objects.h"
+#include "event_scheduler.h"
+#include "event_queue.h"
+#include "event_db_repository.h"
+
+/**
+ @addtogroup Event_Scheduler
+ @{
+*/
+
+#ifdef __GNUC__
+#if __GNUC__ >= 2
+#define SCHED_FUNC __FUNCTION__
+#endif
+#else
+#define SCHED_FUNC "<unknown>"
+#endif
+
+#define LOCK_DATA() lock_data(SCHED_FUNC, __LINE__)
+#define UNLOCK_DATA() unlock_data(SCHED_FUNC, __LINE__)
+#define COND_STATE_WAIT(mythd, abstime, msg) \
+ cond_wait(mythd, abstime, msg, SCHED_FUNC, __LINE__)
+
+extern pthread_attr_t connection_attrib;
+
+
+Event_db_repository *Event_worker_thread::db_repository;
+
+
+static
+const LEX_STRING scheduler_states_names[] =
+{
+ { C_STRING_WITH_LEN("INITIALIZED") },
+ { C_STRING_WITH_LEN("RUNNING") },
+ { C_STRING_WITH_LEN("STOPPING") }
+};
+
+struct scheduler_param {
+ THD *thd;
+ Event_scheduler *scheduler;
+};
+
+
+/*
+ Prints the stack of infos, warnings, errors from thd to
+ the console so it can be fetched by the logs-into-tables and
+ checked later.
+
+ SYNOPSIS
+ evex_print_warnings
+ thd Thread used during the execution of the event
+ et The event itself
+*/
+
+void
+Event_worker_thread::print_warnings(THD *thd, Event_job_data *et)
+{
+ MYSQL_ERROR *err;
+ DBUG_ENTER("evex_print_warnings");
+ if (!thd->warn_list.elements)
+ DBUG_VOID_RETURN;
+
+ char msg_buf[10 * STRING_BUFFER_USUAL_SIZE];
+ char prefix_buf[5 * STRING_BUFFER_USUAL_SIZE];
+ String prefix(prefix_buf, sizeof(prefix_buf), system_charset_info);
+ prefix.length(0);
+ prefix.append("Event Scheduler: [");
+
+ prefix.append(et->definer.str, et->definer.length, system_charset_info);
+ prefix.append("][", 2);
+ prefix.append(et->dbname.str, et->dbname.length, system_charset_info);
+ prefix.append('.');
+ prefix.append(et->name.str, et->name.length, system_charset_info);
+ prefix.append("] ", 2);
+
+ List_iterator_fast<MYSQL_ERROR> it(thd->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());
+ }
+ DBUG_VOID_RETURN;
+}
+
+
+/*
+ Performs post initialization of structures in a new thread.
+
+ SYNOPSIS
+ post_init_event_thread()
+ thd Thread
+
+ NOTES
+ Before this is called, one should not do any DBUG_XXX() calls.
+
+*/
+
+bool
+post_init_event_thread(THD *thd)
+{
+ (void) init_new_connection_handler_thread();
+ if (init_thr_lock() || thd->store_globals())
+ {
+ thd->cleanup();
+ return TRUE;
+ }
+ lex_start(thd);
+
+ pthread_mutex_lock(&LOCK_thread_count);
+ threads.append(thd);
+ thread_count++;
+ thread_running++;
+ pthread_mutex_unlock(&LOCK_thread_count);
+
+ return FALSE;
+}
+
+
+/*
+ Cleans up the THD and the threaded environment of the thread.
+
+ SYNOPSIS
+ deinit_event_thread()
+ thd Thread
+*/
+
+void
+deinit_event_thread(THD *thd)
+{
+ thd->proc_info= "Clearing";
+ DBUG_ASSERT(thd->net.buff != 0);
+ net_end(&thd->net);
+ DBUG_PRINT("exit", ("Event thread finishing"));
+ pthread_mutex_lock(&LOCK_thread_count);
+ thread_count--;
+ thread_running--;
+ delete thd;
+ pthread_mutex_unlock(&LOCK_thread_count);
+}
+
+
+/*
+ Performs pre- pthread_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().
+
+ SYNOPSIS
+ pre_init_event_thread()
+ thd The THD of the thread. Has to be allocated by the caller.
+
+ NOTES
+ 1. The host of the thead is my_localhost
+ 2. thd->net is initted with NULL - no communication.
+*/
+
+void
+pre_init_event_thread(THD* thd)
+{
+ DBUG_ENTER("pre_init_event_thread");
+ thd->client_capabilities= 0;
+ thd->security_ctx->master_access= 0;
+ thd->security_ctx->db_access= 0;
+ thd->security_ctx->host_or_ip= (char*)my_localhost;
+ my_net_init(&thd->net, NULL);
+ 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->client_capabilities|= CLIENT_MULTI_RESULTS;
+ pthread_mutex_lock(&LOCK_thread_count);
+ thd->thread_id= thd->variables.pseudo_thread_id= thread_id++;
+ pthread_mutex_unlock(&LOCK_thread_count);
+
+ /*
+ Guarantees that we will see the thread in SHOW PROCESSLIST though its
+ vio is NULL.
+ */
+
+ thd->proc_info= "Initialized";
+ thd->version= refresh_version;
+ thd->set_time();
+
+ DBUG_VOID_RETURN;
+}
+
+
+/*
+ Function that executes the scheduler,
+
+ SYNOPSIS
+ event_scheduler_thread()
+ arg Pointer to `struct scheduler_param`
+
+ RETURN VALUE
+ 0 OK
+*/
+
+pthread_handler_t
+event_scheduler_thread(void *arg)
+{
+ /* needs to be first for thread_stack */
+ THD *thd= (THD *) ((struct scheduler_param *) arg)->thd;
+ Event_scheduler *scheduler= ((struct scheduler_param *) arg)->scheduler;
+ bool res;
+
+ thd->thread_stack= (char *)&thd; // remember where our stack is
+ res= post_init_event_thread(thd);
+
+ DBUG_ENTER("event_scheduler_thread");
+ my_free((char*)arg, MYF(0));
+ if (!res)
+ scheduler->run(thd);
+
+ my_thread_end();
+ DBUG_RETURN(0); // Against gcc warnings
+}
+
+
+/**
+ Function that executes an event in a child thread. Setups the
+ environment for the event execution and cleans after that.
+
+ SYNOPSIS
+ event_worker_thread()
+ arg The Event_job_data object to be processed
+
+ RETURN VALUE
+ 0 OK
+*/
+
+pthread_handler_t
+event_worker_thread(void *arg)
+{
+ THD *thd;
+ Event_queue_element_for_exec *event= (Event_queue_element_for_exec *)arg;
+
+ thd= event->thd;
+
+ Event_worker_thread worker_thread;
+ worker_thread.run(thd, event);
+
+ my_thread_end();
+ return 0; // Can't return anything here
+}
+
+
+/**
+ Function that executes an event in a child thread. Setups the
+ environment for the event execution and cleans after that.
+
+ SYNOPSIS
+ Event_worker_thread::run()
+ thd Thread context
+ event The Event_queue_element_for_exec object to be processed
+*/
+
+void
+Event_worker_thread::run(THD *thd, Event_queue_element_for_exec *event)
+{
+ /* needs to be first for thread_stack */
+ char my_stack;
+ Event_job_data job_data;
+ bool res;
+
+ thd->thread_stack= &my_stack; // remember where our stack is
+ res= post_init_event_thread(thd);
+
+ DBUG_ENTER("Event_worker_thread::run");
+ DBUG_PRINT("info", ("Time is %ld, THD: 0x%lx", (long) my_time(0), (long) thd));
+
+ if (res)
+ goto end;
+
+ if ((res= db_repository->load_named_event(thd, event->dbname, event->name,
+ &job_data)))
+ {
+ DBUG_PRINT("error", ("Got error from load_named_event"));
+ goto end;
+ }
+
+ thd->enable_slow_log= TRUE;
+
+ res= job_data.execute(thd, event->dropped);
+
+ print_warnings(thd, &job_data);
+
+ if (res)
+ sql_print_information("Event Scheduler: "
+ "[%s].[%s.%s] event execution failed.",
+ job_data.definer.str,
+ job_data.dbname.str, job_data.name.str);
+end:
+ DBUG_PRINT("info", ("Done with Event %s.%s", event->dbname.str,
+ event->name.str));
+
+ delete event;
+ deinit_event_thread(thd);
+
+ DBUG_VOID_RETURN;
+}
+
+
+Event_scheduler::Event_scheduler(Event_queue *queue_arg)
+ :state(INITIALIZED),
+ scheduler_thd(NULL),
+ queue(queue_arg),
+ mutex_last_locked_at_line(0),
+ mutex_last_unlocked_at_line(0),
+ mutex_last_locked_in_func("n/a"),
+ mutex_last_unlocked_in_func("n/a"),
+ mutex_scheduler_data_locked(FALSE),
+ waiting_on_cond(FALSE),
+ started_events(0)
+{
+ pthread_mutex_init(&LOCK_scheduler_state, MY_MUTEX_INIT_FAST);
+ pthread_cond_init(&COND_state, NULL);
+}
+
+
+Event_scheduler::~Event_scheduler()
+{
+ stop(); /* does nothing if not running */
+ pthread_mutex_destroy(&LOCK_scheduler_state);
+ pthread_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()
+
+ RETURN VALUE
+ FALSE OK
+ TRUE Error (not reported)
+*/
+
+bool
+Event_scheduler::start()
+{
+ THD *new_thd= NULL;
+ bool ret= FALSE;
+ pthread_t th;
+ struct scheduler_param *scheduler_param_value;
+ DBUG_ENTER("Event_scheduler::start");
+
+ LOCK_DATA();
+ DBUG_PRINT("info", ("state before action %s", scheduler_states_names[state].str));
+ if (state > INITIALIZED)
+ goto end;
+
+ if (!(new_thd= new THD))
+ {
+ sql_print_error("Event Scheduler: Cannot initialize the scheduler thread");
+ ret= TRUE;
+ goto end;
+ }
+ pre_init_event_thread(new_thd);
+ new_thd->system_thread= SYSTEM_THREAD_EVENT_SCHEDULER;
+ new_thd->command= COM_DAEMON;
+
+ /*
+ We should run the event scheduler thread under the super-user privileges.
+ In particular, this is needed to be able to lock the mysql.event table
+ for writing when the server is running in the read-only mode.
+ */
+ new_thd->security_ctx->master_access |= SUPER_ACL;
+
+ scheduler_param_value=
+ (struct scheduler_param *)my_malloc(sizeof(struct scheduler_param), MYF(0));
+ scheduler_param_value->thd= new_thd;
+ scheduler_param_value->scheduler= this;
+
+ scheduler_thd= new_thd;
+ 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))
+ {
+ DBUG_PRINT("error", ("cannot create a new thread"));
+ state= INITIALIZED;
+ scheduler_thd= NULL;
+ ret= TRUE;
+
+ 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_mutex_unlock(&LOCK_thread_count);
+ }
+end:
+ UNLOCK_DATA();
+
+ DBUG_RETURN(ret);
+}
+
+
+/*
+ The main loop of the scheduler.
+
+ SYNOPSIS
+ Event_scheduler::run()
+ thd Thread
+
+ RETURN VALUE
+ FALSE OK
+ TRUE Error (Serious error)
+*/
+
+bool
+Event_scheduler::run(THD *thd)
+{
+ int res= FALSE;
+ DBUG_ENTER("Event_scheduler::run");
+
+ sql_print_information("Event Scheduler: scheduler thread started with id %lu",
+ thd->thread_id);
+ /*
+ Recalculate the values in the queue because there could have been stops
+ in executions of the scheduler and some times could have passed by.
+ */
+ queue->recalculate_activation_times(thd);
+
+ while (is_running())
+ {
+ Event_queue_element_for_exec *event_name;
+
+ /* Gets a minimized version */
+ if (queue->get_top_for_execution_if_time(thd, &event_name))
+ {
+ sql_print_information("Event Scheduler: "
+ "Serious error during getting next "
+ "event to execute. Stopping");
+ break;
+ }
+
+ DBUG_PRINT("info", ("get_top_for_execution_if_time returned "
+ "event_name=0x%lx", (long) event_name));
+ if (event_name)
+ {
+ if ((res= execute_top(event_name)))
+ break;
+ }
+ else
+ {
+ DBUG_ASSERT(thd->killed);
+ DBUG_PRINT("info", ("job_data is NULL, the thread was killed"));
+ }
+ DBUG_PRINT("info", ("state=%s", scheduler_states_names[state].str));
+ }
+
+ LOCK_DATA();
+ deinit_event_thread(thd);
+ scheduler_thd= NULL;
+ state= INITIALIZED;
+ DBUG_PRINT("info", ("Signalling back to the stopper COND_state"));
+ pthread_cond_signal(&COND_state);
+ UNLOCK_DATA();
+
+ DBUG_RETURN(res);
+}
+
+
+/*
+ Creates a new THD instance and then forks a new thread, while passing
+ the THD pointer and job_data to it.
+
+ SYNOPSIS
+ Event_scheduler::execute_top()
+
+ RETURN VALUE
+ FALSE OK
+ TRUE Error (Serious error)
+*/
+
+bool
+Event_scheduler::execute_top(Event_queue_element_for_exec *event_name)
+{
+ THD *new_thd;
+ pthread_t th;
+ int res= 0;
+ DBUG_ENTER("Event_scheduler::execute_top");
+ if (!(new_thd= new THD()))
+ goto error;
+
+ pre_init_event_thread(new_thd);
+ new_thd->system_thread= SYSTEM_THREAD_EVENT_WORKER;
+ event_name->thd= new_thd;
+ DBUG_PRINT("info", ("Event %s@%s ready for start",
+ event_name->dbname.str, event_name->name.str));
+
+ /*
+ TODO: should use thread pool here, preferably with an upper limit
+ on number of threads: if too many events are scheduled for the
+ same time, starting all of them at once won't help them run truly
+ in parallel (because of the great amount of synchronization), so
+ we may as well execute them in sequence, keeping concurrency at a
+ reasonable level.
+ */
+ /* Major failure */
+ if ((res= pthread_create(&th, &connection_attrib, event_worker_thread,
+ event_name)))
+ goto error;
+
+ ++started_events;
+
+ DBUG_PRINT("info", ("Event is in THD: 0x%lx", (long) new_thd));
+ DBUG_RETURN(FALSE);
+
+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_mutex_unlock(&LOCK_thread_count);
+ }
+ delete event_name;
+ DBUG_RETURN(TRUE);
+}
+
+
+/*
+ Checks whether the state of the scheduler is RUNNING
+
+ SYNOPSIS
+ Event_scheduler::is_running()
+
+ RETURN VALUE
+ TRUE RUNNING
+ FALSE Not RUNNING
+*/
+
+bool
+Event_scheduler::is_running()
+{
+ LOCK_DATA();
+ bool ret= (state == RUNNING);
+ UNLOCK_DATA();
+ return ret;
+}
+
+
+/**
+ Stops the scheduler (again). Waits for acknowledgement from the
+ scheduler that it has stopped - synchronous stopping.
+
+ Already running events will not be stopped. If the user needs
+ them stopped manual intervention is needed.
+
+ SYNOPSIS
+ Event_scheduler::stop()
+
+ RETURN VALUE
+ FALSE OK
+ TRUE Error (not reported)
+*/
+
+bool
+Event_scheduler::stop()
+{
+ THD *thd= current_thd;
+ DBUG_ENTER("Event_scheduler::stop");
+ DBUG_PRINT("enter", ("thd: 0x%lx", (long) thd));
+
+ LOCK_DATA();
+ DBUG_PRINT("info", ("state before action %s", scheduler_states_names[state].str));
+ if (state != RUNNING)
+ goto end;
+
+ /* Guarantee we don't catch spurious signals */
+ do {
+ DBUG_PRINT("info", ("Waiting for COND_started_or_stopped from "
+ "the scheduler thread. Current value of state is %s . "
+ "workers count=%d", scheduler_states_names[state].str,
+ workers_count()));
+ /*
+ NOTE: We don't use kill_one_thread() because it can't kill COM_DEAMON
+ threads. In addition, kill_one_thread() requires THD but during shutdown
+ current_thd is NULL. Hence, if kill_one_thread should be used it has to
+ be modified to kill also daemons, by adding a flag, and also we have to
+ create artificial THD here. To save all this work, we just do what
+ kill_one_thread() does to kill a thread. See also sql_repl.cc for similar
+ usage.
+ */
+
+ state= STOPPING;
+ DBUG_PRINT("info", ("Scheduler thread has id %lu",
+ scheduler_thd->thread_id));
+ /* Lock from delete */
+ pthread_mutex_lock(&scheduler_thd->LOCK_delete);
+ /* 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(THD::KILL_CONNECTION);
+ pthread_mutex_unlock(&scheduler_thd->LOCK_delete);
+
+ /* thd could be 0x0, when shutting down */
+ sql_print_information("Event Scheduler: "
+ "Waiting for the scheduler thread to reply");
+ COND_STATE_WAIT(thd, NULL, "Waiting scheduler to stop");
+ } while (state == STOPPING);
+ DBUG_PRINT("info", ("Scheduler thread has cleaned up. Set state to INIT"));
+ sql_print_information("Event Scheduler: Stopped");
+end:
+ UNLOCK_DATA();
+ DBUG_RETURN(FALSE);
+}
+
+
+/*
+ Returns the number of living event worker threads.
+
+ SYNOPSIS
+ Event_scheduler::workers_count()
+*/
+
+uint
+Event_scheduler::workers_count()
+{
+ THD *tmp;
+ uint count= 0;
+
+ DBUG_ENTER("Event_scheduler::workers_count");
+ pthread_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);
+ DBUG_PRINT("exit", ("%d", count));
+ DBUG_RETURN(count);
+}
+
+
+/*
+ Auxiliary function for locking LOCK_scheduler_state. Used
+ by the LOCK_DATA macro.
+
+ SYNOPSIS
+ Event_scheduler::lock_data()
+ func Which function is requesting mutex lock
+ line On which line mutex lock is requested
+*/
+
+void
+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);
+ mutex_last_locked_in_func= func;
+ mutex_last_locked_at_line= line;
+ mutex_scheduler_data_locked= TRUE;
+ DBUG_VOID_RETURN;
+}
+
+
+/*
+ Auxiliary function for unlocking LOCK_scheduler_state. Used
+ by the UNLOCK_DATA macro.
+
+ SYNOPSIS
+ Event_scheduler::unlock_data()
+ func Which function is requesting mutex unlock
+ line On which line mutex unlock is requested
+*/
+
+void
+Event_scheduler::unlock_data(const char *func, uint line)
+{
+ DBUG_ENTER("Event_scheduler::unlock_data");
+ DBUG_PRINT("enter", ("func=%s line=%u", func, line));
+ mutex_last_unlocked_at_line= line;
+ mutex_scheduler_data_locked= FALSE;
+ mutex_last_unlocked_in_func= func;
+ pthread_mutex_unlock(&LOCK_scheduler_state);
+ DBUG_VOID_RETURN;
+}
+
+
+/*
+ Wrapper for pthread_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()
+ msg Message for thd->proc_info
+ func Which function is requesting cond_wait
+ line On which line cond_wait is requested
+*/
+
+void
+Event_scheduler::cond_wait(THD *thd, struct timespec *abstime, const char* msg,
+ const char *func, uint line)
+{
+ DBUG_ENTER("Event_scheduler::cond_wait");
+ waiting_on_cond= TRUE;
+ mutex_last_unlocked_at_line= line;
+ mutex_scheduler_data_locked= FALSE;
+ mutex_last_unlocked_in_func= func;
+ if (thd)
+ thd->enter_cond(&COND_state, &LOCK_scheduler_state, msg);
+
+ DBUG_PRINT("info", ("pthread_cond_%swait", abstime? "timed":""));
+ if (!abstime)
+ pthread_cond_wait(&COND_state, &LOCK_scheduler_state);
+ else
+ pthread_cond_timedwait(&COND_state, &LOCK_scheduler_state, abstime);
+ if (thd)
+ {
+ /*
+ This will free the lock so we need to relock. Not the best thing to
+ do but we need to obey cond_wait()
+ */
+ thd->exit_cond("");
+ LOCK_DATA();
+ }
+ mutex_last_locked_in_func= func;
+ mutex_last_locked_at_line= line;
+ mutex_scheduler_data_locked= TRUE;
+ waiting_on_cond= FALSE;
+ DBUG_VOID_RETURN;
+}
+
+
+/*
+ Dumps the internal status of the scheduler
+
+ SYNOPSIS
+ Event_scheduler::dump_internal_status()
+*/
+
+void
+Event_scheduler::dump_internal_status()
+{
+ DBUG_ENTER("Event_scheduler::dump_internal_status");
+
+ puts("");
+ puts("Event scheduler status:");
+ printf("State : %s\n", scheduler_states_names[state].str);
+ printf("Thread id : %lu\n", scheduler_thd? scheduler_thd->thread_id : 0);
+ printf("LLA : %s:%u\n", mutex_last_locked_in_func,
+ mutex_last_locked_at_line);
+ printf("LUA : %s:%u\n", mutex_last_unlocked_in_func,
+ mutex_last_unlocked_at_line);
+ printf("WOC : %s\n", waiting_on_cond? "YES":"NO");
+ printf("Workers : %u\n", workers_count());
+ printf("Executed : %lu\n", (ulong) started_events);
+ printf("Data locked: %s\n", mutex_scheduler_data_locked ? "YES":"NO");
+
+ DBUG_VOID_RETURN;
+}
+
+/**
+ @} (End of group Event_Scheduler)
+*/
diff --git a/sql/event_scheduler.h b/sql/event_scheduler.h
new file mode 100644
index 00000000000..0be93a65d33
--- /dev/null
+++ b/sql/event_scheduler.h
@@ -0,0 +1,155 @@
+#ifndef _EVENT_SCHEDULER_H_
+#define _EVENT_SCHEDULER_H_
+/* Copyright (C) 2004-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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+/**
+ @addtogroup Event_Scheduler
+ @{
+*/
+/**
+ @file
+
+ Declarations of the scheduler thread class
+ and related functionality.
+
+ This file is internal to Event_Scheduler module. Please do not
+ include it directly. All public declarations of Event_Scheduler
+ module are in events.h and event_data_objects.h.
+*/
+
+
+class Event_queue;
+class Event_job_data;
+class Event_db_repository;
+class Events;
+
+void
+pre_init_event_thread(THD* thd);
+
+bool
+post_init_event_thread(THD* thd);
+
+void
+deinit_event_thread(THD *thd);
+
+
+class Event_worker_thread
+{
+public:
+ static void
+ init(Event_db_repository *db_repository_arg)
+ {
+ db_repository= db_repository_arg;
+ }
+
+ void
+ run(THD *thd, Event_queue_element_for_exec *event);
+
+private:
+ void
+ print_warnings(THD *thd, Event_job_data *et);
+
+ static Event_db_repository *db_repository;
+};
+
+
+class Event_scheduler
+{
+public:
+ Event_scheduler(Event_queue *event_queue_arg);
+ ~Event_scheduler();
+
+
+ /* State changing methods follow */
+
+ bool
+ start();
+
+ bool
+ stop();
+
+ /*
+ Need to be public because has to be called from the function
+ passed to pthread_create.
+ */
+ bool
+ run(THD *thd);
+
+
+ /* Information retrieving methods follow */
+ bool
+ is_running();
+
+ void
+ dump_internal_status();
+
+private:
+ uint
+ workers_count();
+
+ /* helper functions */
+ bool
+ execute_top(Event_queue_element_for_exec *event_name);
+
+ /* helper functions for working with mutexes & conditionals */
+ void
+ lock_data(const char *func, uint line);
+
+ void
+ unlock_data(const char *func, uint line);
+
+ void
+ cond_wait(THD *thd, struct timespec *abstime, const char* msg,
+ const char *func, uint line);
+
+ pthread_mutex_t LOCK_scheduler_state;
+
+ enum enum_state
+ {
+ INITIALIZED = 0,
+ RUNNING,
+ STOPPING
+ };
+
+ /* This is the current status of the life-cycle of the scheduler. */
+ enum enum_state state;
+
+ THD *scheduler_thd;
+
+ pthread_cond_t COND_state;
+
+ Event_queue *queue;
+
+ uint mutex_last_locked_at_line;
+ uint mutex_last_unlocked_at_line;
+ const char* mutex_last_locked_in_func;
+ const char* mutex_last_unlocked_in_func;
+ bool mutex_scheduler_data_locked;
+ bool waiting_on_cond;
+
+ ulonglong started_events;
+
+private:
+ /* Prevent use of these */
+ Event_scheduler(const Event_scheduler &);
+ void operator=(Event_scheduler &);
+};
+
+/**
+ @} (End of group Event_Scheduler)
+*/
+
+#endif /* _EVENT_SCHEDULER_H_ */
diff --git a/sql/events.cc b/sql/events.cc
new file mode 100644
index 00000000000..4203ca06d7f
--- /dev/null
+++ b/sql/events.cc
@@ -0,0 +1,1217 @@
+/* Copyright (C) 2004-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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#include "mysql_priv.h"
+#include "events.h"
+#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
+
+/**
+ @addtogroup Event_Scheduler
+ @{
+*/
+
+/*
+ TODO list :
+ - CREATE EVENT should not go into binary log! Does it now? The SQL statements
+ issued by the EVENT are replicated.
+ I have an idea how to solve the problem at failover. So the status field
+ will be ENUM('DISABLED', 'ENABLED', 'SLAVESIDE_DISABLED').
+ In this case when CREATE EVENT is replicated it should go into the binary
+ as SLAVESIDE_DISABLED if it is ENABLED, when it's created as DISABLEd it
+ should be replicated as disabled. If an event is ALTERed as DISABLED the
+ query should go untouched into the binary log, when ALTERed as enable then
+ it should go as SLAVESIDE_DISABLED. This is regarding the SQL interface.
+ TT routines however modify mysql.event internally and this does not go the
+ log so in this case queries has to be injected into the log...somehow... or
+ maybe a solution is RBR for this case, because the event may go only from
+ ENABLED to DISABLED status change and this is safe for replicating. As well
+ an event may be deleted which is also safe for RBR.
+
+ - Add logging to file
+
+*/
+
+
+/*
+ If the user (un)intentionally removes an event directly from mysql.event
+ the following sequence has to be used to be able to remove the in-memory
+ counterpart.
+ 1. CREATE EVENT the_name ON SCHEDULE EVERY 1 SECOND DISABLE DO SELECT 1;
+ 2. DROP EVENT the_name
+
+ In other words, the first one will create a row in mysql.event . In the
+ second step because there will be a line, disk based drop will pass and
+ the scheduler will remove the memory counterpart. The reason is that
+ in-memory queue does not check whether the event we try to drop from memory
+ is disabled. Disabled events are not kept in-memory because they are not
+ 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;
+bool Events::check_system_tables_error= FALSE;
+
+
+/*
+ Compares 2 LEX strings regarding case.
+
+ SYNOPSIS
+ sortcmp_lex_string()
+ s First LEX_STRING
+ t Second LEX_STRING
+ cs Charset
+
+ RETURN VALUE
+ -1 s < t
+ 0 s == t
+ 1 s > t
+*/
+
+int sortcmp_lex_string(LEX_STRING s, LEX_STRING t, CHARSET_INFO *cs)
+{
+ return cs->coll->strnncollsp(cs, (uchar *) s.str,s.length,
+ (uchar *) t.str,t.length, 0);
+}
+
+
+/**
+ @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.
+*/
+
+bool Events::check_if_system_tables_error()
+{
+ DBUG_ENTER("Events::check_if_system_tables_error");
+
+ if (check_system_tables_error)
+ {
+ my_error(ER_EVENTS_DB_ERROR, MYF(0));
+ DBUG_RETURN(TRUE);
+ }
+
+ DBUG_RETURN(FALSE);
+}
+
+
+/**
+ Reconstructs interval expression from interval type and expression
+ value that is in form of a value of the smalles entity:
+ For
+ YEAR_MONTH - expression is in months
+ DAY_MINUTE - expression is in minutes
+
+ SYNOPSIS
+ Events::reconstruct_interval_expression()
+ buf Preallocated String buffer to add the value to
+ interval The interval type (for instance YEAR_MONTH)
+ expression The value in the lowest entity
+
+ RETURN VALUE
+ 0 OK
+ 1 Error
+*/
+
+int
+Events::reconstruct_interval_expression(String *buf, interval_type interval,
+ longlong expression)
+{
+ ulonglong expr= expression;
+ char tmp_buff[128], *end;
+ bool close_quote= TRUE;
+ int multipl= 0;
+ char separator=':';
+
+ switch (interval) {
+ case INTERVAL_YEAR_MONTH:
+ multipl= 12;
+ separator= '-';
+ goto common_1_lev_code;
+ case INTERVAL_DAY_HOUR:
+ multipl= 24;
+ separator= ' ';
+ goto common_1_lev_code;
+ case INTERVAL_HOUR_MINUTE:
+ case INTERVAL_MINUTE_SECOND:
+ multipl= 60;
+common_1_lev_code:
+ buf->append('\'');
+ end= longlong10_to_str(expression/multipl, tmp_buff, 10);
+ buf->append(tmp_buff, (uint) (end- tmp_buff));
+ expr= expr - (expr/multipl)*multipl;
+ break;
+ case INTERVAL_DAY_MINUTE:
+ {
+ ulonglong tmp_expr= expr;
+
+ tmp_expr/=(24*60);
+ buf->append('\'');
+ end= longlong10_to_str(tmp_expr, tmp_buff, 10);
+ buf->append(tmp_buff, (uint) (end- tmp_buff));// days
+ buf->append(' ');
+
+ tmp_expr= expr - tmp_expr*(24*60);//minutes left
+ end= longlong10_to_str(tmp_expr/60, tmp_buff, 10);
+ buf->append(tmp_buff, (uint) (end- tmp_buff));// hours
+
+ expr= tmp_expr - (tmp_expr/60)*60;
+ /* the code after the switch will finish */
+ }
+ break;
+ case INTERVAL_HOUR_SECOND:
+ {
+ ulonglong tmp_expr= expr;
+
+ buf->append('\'');
+ end= longlong10_to_str(tmp_expr/3600, tmp_buff, 10);
+ buf->append(tmp_buff, (uint) (end- tmp_buff));// hours
+ buf->append(':');
+
+ tmp_expr= tmp_expr - (tmp_expr/3600)*3600;
+ end= longlong10_to_str(tmp_expr/60, tmp_buff, 10);
+ buf->append(tmp_buff, (uint) (end- tmp_buff));// minutes
+
+ expr= tmp_expr - (tmp_expr/60)*60;
+ /* the code after the switch will finish */
+ }
+ break;
+ case INTERVAL_DAY_SECOND:
+ {
+ ulonglong tmp_expr= expr;
+
+ tmp_expr/=(24*3600);
+ buf->append('\'');
+ end= longlong10_to_str(tmp_expr, tmp_buff, 10);
+ buf->append(tmp_buff, (uint) (end- tmp_buff));// days
+ buf->append(' ');
+
+ tmp_expr= expr - tmp_expr*(24*3600);//seconds left
+ end= longlong10_to_str(tmp_expr/3600, tmp_buff, 10);
+ buf->append(tmp_buff, (uint) (end- tmp_buff));// hours
+ buf->append(':');
+
+ tmp_expr= tmp_expr - (tmp_expr/3600)*3600;
+ end= longlong10_to_str(tmp_expr/60, tmp_buff, 10);
+ buf->append(tmp_buff, (uint) (end- tmp_buff));// minutes
+
+ expr= tmp_expr - (tmp_expr/60)*60;
+ /* the code after the switch will finish */
+ }
+ break;
+ case INTERVAL_DAY_MICROSECOND:
+ case INTERVAL_HOUR_MICROSECOND:
+ case INTERVAL_MINUTE_MICROSECOND:
+ case INTERVAL_SECOND_MICROSECOND:
+ case INTERVAL_MICROSECOND:
+ my_error(ER_NOT_SUPPORTED_YET, MYF(0), "MICROSECOND");
+ return 1;
+ break;
+ case INTERVAL_QUARTER:
+ expr/= 3;
+ close_quote= FALSE;
+ break;
+ case INTERVAL_WEEK:
+ expr/= 7;
+ default:
+ close_quote= FALSE;
+ break;
+ }
+ if (close_quote)
+ buf->append(separator);
+ end= longlong10_to_str(expr, tmp_buff, 10);
+ buf->append(tmp_buff, (uint) (end- tmp_buff));
+ if (close_quote)
+ buf->append('\'');
+
+ return 0;
+}
+
+
+/**
+ Create a new event.
+
+ @param[in,out] thd THD
+ @param[in] parse_data Event's data from parsing stage
+ @param[in] if_not_exists Whether IF NOT EXISTS was
+ specified
+ In case there is an event with the same name (db) and
+ IF NOT EXISTS is specified, an warning is put into the stack.
+ @sa Events::drop_event for the notes about locking, pre-locking
+ and Events DDL.
+
+ @retval FALSE OK
+ @retval TRUE Error (reported)
+*/
+
+bool
+Events::create_event(THD *thd, Event_parse_data *parse_data,
+ bool if_not_exists)
+{
+ int ret;
+ 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);
+
+ /*
+ Perform semantic checks outside of Event_db_repository:
+ once CREATE EVENT is supported in prepared statements, the
+ checks will be moved to PREPARE phase.
+ */
+ if (parse_data->check_parse_data(thd))
+ DBUG_RETURN(TRUE);
+
+ /* 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)))
+ DBUG_RETURN(TRUE);
+
+ if (check_db_dir_existence(parse_data->dbname.str))
+ {
+ my_error(ER_BAD_DB_ERROR, MYF(0), parse_data->dbname.str);
+ DBUG_RETURN(TRUE);
+ }
+
+ if (parse_data->do_not_create)
+ DBUG_RETURN(FALSE);
+ /*
+ Turn off row binlogging of this statement and use statement-based
+ so that all supporting tables are updated for CREATE EVENT command.
+ */
+ if (thd->current_stmt_binlog_row_based)
+ thd->clear_current_stmt_binlog_row_based();
+
+ pthread_mutex_lock(&LOCK_event_metadata);
+
+ /* 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)))
+ {
+ Event_queue_element *new_element;
+
+ if (!(new_element= new Event_queue_element()))
+ ret= TRUE; // OOM
+ else if ((ret= db_repository->load_named_event(thd, parse_data->dbname,
+ parse_data->name,
+ new_element)))
+ {
+ db_repository->drop_event(thd, parse_data->dbname, parse_data->name,
+ TRUE);
+ delete new_element;
+ }
+ else
+ {
+ /* TODO: do not ignore the out parameter and a possible OOM error! */
+ bool created;
+ if (event_queue)
+ event_queue->create_event(thd, new_element, &created);
+ /* Binlog the create event. */
+ DBUG_ASSERT(thd->query && thd->query_length);
+ write_bin_log(thd, TRUE, thd->query, thd->query_length);
+ }
+ }
+ pthread_mutex_unlock(&LOCK_event_metadata);
+
+ DBUG_RETURN(ret);
+}
+
+
+/**
+ Alter an event.
+
+ @param[in,out] thd THD
+ @param[in] parse_data Event's data from parsing stage
+ @param[in] new_dbname A new schema name for the event. Set in the case of
+ ALTER EVENT RENAME, otherwise is NULL.
+ @param[in] new_name A new name for the event. Set in the case of
+ ALTER EVENT RENAME
+
+ Parameter 'et' contains data about dbname and event name.
+ Parameter 'new_name' is the new name of the event, if not null
+ this means that RENAME TO was specified in the query
+ @sa Events::drop_event for the locking notes.
+
+ @retval FALSE OK
+ @retval TRUE error (reported)
+*/
+
+bool
+Events::update_event(THD *thd, Event_parse_data *parse_data,
+ LEX_STRING *new_dbname, LEX_STRING *new_name)
+{
+ int ret;
+ Event_queue_element *new_element;
+
+ 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)))
+ DBUG_RETURN(TRUE);
+
+ if (new_dbname) /* It's a rename */
+ {
+ /* Check that the new and the old names differ. */
+ if ( !sortcmp_lex_string(parse_data->dbname, *new_dbname,
+ system_charset_info) &&
+ !sortcmp_lex_string(parse_data->name, *new_name,
+ system_charset_info))
+ {
+ my_error(ER_EVENT_SAME_NAME, MYF(0), parse_data->name.str);
+ DBUG_RETURN(TRUE);
+ }
+
+ /*
+ And the user has sufficient privileges to use the target database.
+ Do it before checking whether the database exists: we don't want
+ 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)))
+ DBUG_RETURN(TRUE);
+
+ /* Check that the target database exists */
+ if (check_db_dir_existence(new_dbname->str))
+ {
+ my_error(ER_BAD_DB_ERROR, MYF(0), new_dbname->str);
+ DBUG_RETURN(TRUE);
+ }
+ }
+
+ /*
+ Turn off row binlogging of this statement and use statement-based
+ so that all supporting tables are updated for UPDATE EVENT command.
+ */
+ if (thd->current_stmt_binlog_row_based)
+ thd->clear_current_stmt_binlog_row_based();
+
+ pthread_mutex_lock(&LOCK_event_metadata);
+
+ /* On error conditions my_error() is called so no need to handle here */
+ if (!(ret= db_repository->update_event(thd, parse_data,
+ new_dbname, new_name)))
+ {
+ LEX_STRING dbname= new_dbname ? *new_dbname : parse_data->dbname;
+ LEX_STRING name= new_name ? *new_name : parse_data->name;
+
+ if (!(new_element= new Event_queue_element()))
+ 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
+ {
+ /*
+ TODO: check if an update actually has inserted an entry
+ into the queue.
+ If not, and the element is ON COMPLETION NOT PRESERVE, delete
+ it right away.
+ */
+ if (event_queue)
+ event_queue->update_event(thd, parse_data->dbname, parse_data->name,
+ new_element);
+ /* Binlog the alter event. */
+ DBUG_ASSERT(thd->query && thd->query_length);
+ write_bin_log(thd, TRUE, thd->query, thd->query_length);
+ }
+ }
+ pthread_mutex_unlock(&LOCK_event_metadata);
+
+ DBUG_RETURN(ret);
+}
+
+
+/**
+ Drops an event
+
+ @param[in,out] thd THD
+ @param[in] dbname Event's schema
+ @param[in] name Event's name
+ @param[in] if_exists When this is set and the event does not exist
+ a warning is pushed into the warning stack.
+ Otherwise the operation produces an error.
+
+ @note Similarly to DROP PROCEDURE, we do not allow DROP EVENT
+ under LOCK TABLES mode, unless table mysql.event is locked. To
+ ensure that, we do not reset & backup the open tables state in
+ this function - if in LOCK TABLES or pre-locking mode, this will
+ lead to an error 'Table mysql.event is not locked with LOCK
+ TABLES' unless it _is_ locked. In pre-locked mode there is
+ another barrier - DROP EVENT commits the current transaction,
+ and COMMIT/ROLLBACK is not allowed in stored functions and
+ triggers.
+
+ @retval FALSE OK
+ @retval TRUE Error (reported)
+*/
+
+bool
+Events::drop_event(THD *thd, LEX_STRING dbname, LEX_STRING name, bool if_exists)
+{
+ int ret;
+ 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)))
+ 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.
+ */
+ if (thd->current_stmt_binlog_row_based)
+ thd->clear_current_stmt_binlog_row_based();
+
+ pthread_mutex_lock(&LOCK_event_metadata);
+ /* On error conditions my_error() is called so no need to handle here */
+ if (!(ret= db_repository->drop_event(thd, dbname, name, if_exists)))
+ {
+ if (event_queue)
+ event_queue->drop_event(thd, dbname, name);
+ /* Binlog the drop event. */
+ DBUG_ASSERT(thd->query && thd->query_length);
+ write_bin_log(thd, TRUE, thd->query, thd->query_length);
+ }
+ pthread_mutex_unlock(&LOCK_event_metadata);
+ DBUG_RETURN(ret);
+}
+
+
+/**
+ Drops all events from a schema
+
+ @note We allow to drop all events in a schema even if the
+ scheduler is disabled. This is to not produce any warnings
+ in case of DROP DATABASE and a disabled scheduler.
+
+ @param[in,out] thd Thread
+ @param[in] db ASCIIZ schema name
+*/
+
+void
+Events::drop_schema_events(THD *thd, char *db)
+{
+ LEX_STRING const db_lex= { db, strlen(db) };
+
+ DBUG_ENTER("Events::drop_schema_events");
+ DBUG_PRINT("enter", ("dropping events from %s", db));
+
+ /*
+ 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;
+}
+
+
+/**
+ A helper function to generate SHOW CREATE EVENT output from
+ a named event
+*/
+
+static bool
+send_show_create_event(THD *thd, Event_timed *et, Protocol *protocol)
+{
+ char show_str_buf[10 * STRING_BUFFER_USUAL_SIZE];
+ String show_str(show_str_buf, sizeof(show_str_buf), system_charset_info);
+ List<Item> field_list;
+ LEX_STRING sql_mode;
+ const String *tz_name;
+
+ DBUG_ENTER("send_show_create_event");
+
+ show_str.length(0);
+ if (et->get_create_event(thd, &show_str))
+ DBUG_RETURN(TRUE);
+
+ field_list.push_back(new Item_empty_string("Event", NAME_CHAR_LEN));
+
+ if (sys_var_thd_sql_mode::symbolic_mode_representation(thd, et->sql_mode,
+ &sql_mode))
+ DBUG_RETURN(TRUE);
+
+ field_list.push_back(new Item_empty_string("sql_mode", (uint) sql_mode.length));
+
+ tz_name= et->time_zone->get_name();
+
+ field_list.push_back(new Item_empty_string("time_zone",
+ tz_name->length()));
+
+ field_list.push_back(new Item_empty_string("Create Event",
+ show_str.length()));
+
+ field_list.push_back(
+ new Item_empty_string("character_set_client", MY_CS_NAME_SIZE));
+
+ field_list.push_back(
+ new Item_empty_string("collation_connection", MY_CS_NAME_SIZE));
+
+ field_list.push_back(
+ new Item_empty_string("Database Collation", MY_CS_NAME_SIZE));
+
+ if (protocol->send_fields(&field_list,
+ Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
+ DBUG_RETURN(TRUE);
+
+ protocol->prepare_for_resend();
+
+ protocol->store(et->name.str, et->name.length, system_charset_info);
+ protocol->store(sql_mode.str, sql_mode.length, system_charset_info);
+ protocol->store(tz_name->ptr(), tz_name->length(), system_charset_info);
+ protocol->store(show_str.c_ptr(), show_str.length(),
+ et->creation_ctx->get_client_cs());
+ protocol->store(et->creation_ctx->get_client_cs()->csname,
+ strlen(et->creation_ctx->get_client_cs()->csname),
+ system_charset_info);
+ protocol->store(et->creation_ctx->get_connection_cl()->name,
+ strlen(et->creation_ctx->get_connection_cl()->name),
+ system_charset_info);
+ protocol->store(et->creation_ctx->get_db_cl()->name,
+ strlen(et->creation_ctx->get_db_cl()->name),
+ system_charset_info);
+
+ if (protocol->write())
+ DBUG_RETURN(TRUE);
+
+ my_eof(thd);
+
+ DBUG_RETURN(FALSE);
+}
+
+
+/**
+ Implement SHOW CREATE EVENT statement
+
+ thd Thread context
+ spn The name of the event (db, name)
+
+ @retval FALSE OK
+ @retval TRUE error (reported)
+*/
+
+bool
+Events::show_create_event(THD *thd, LEX_STRING dbname, LEX_STRING name)
+{
+ Open_tables_state open_tables_backup;
+ Event_timed et;
+ bool ret;
+
+ DBUG_ENTER("Events::show_create_event");
+ DBUG_PRINT("enter", ("name: %s@%s", dbname.str, name.str));
+
+ 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)))
+ DBUG_RETURN(TRUE);
+
+ /*
+ We would like to allow SHOW CREATE EVENT under LOCK TABLES and
+ in pre-locked mode. mysql.event table is marked as a system table.
+ This flag reduces the set of its participation scenarios in LOCK TABLES
+ operation, and therefore an out-of-bound open of this table
+ for reading like the one below (sic, only for reading) is
+ more or less deadlock-free. For additional information about when a
+ 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);
+
+ DBUG_RETURN(ret);
+}
+
+
+/**
+ Check access rights and fill INFORMATION_SCHEMA.events table.
+
+ @param[in,out] thd Thread context
+ @param[in] tables The temporary table to fill.
+
+ In MySQL INFORMATION_SCHEMA tables are temporary tables that are
+ created and filled on demand. In this function, we fill
+ INFORMATION_SCHEMA.events. It is a callback for I_S module, invoked from
+ sql_show.cc
+
+ @return Has to be integer, as such is the requirement of the I_S API
+ @retval 0 success
+ @retval 1 an error, pushed into the error stack
+*/
+
+int
+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())
+ DBUG_RETURN(1);
+
+ /*
+ If it's SHOW EVENTS then thd->lex->select_lex.db is guaranteed not to
+ be NULL. Let's do an assert anyway.
+ */
+ 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))
+ 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);
+}
+
+
+/*
+ Inits the scheduler's structures.
+
+ SYNOPSIS
+ Events::init()
+
+ NOTES
+ This function is not synchronized.
+
+ RETURN VALUE
+ FALSE OK
+ TRUE Error in case the scheduler can't start
+*/
+
+bool
+Events::init(my_bool opt_noacl)
+{
+
+ THD *thd;
+ bool res= FALSE;
+
+ DBUG_ENTER("Events::init");
+
+ /* Disable the scheduler if running with --skip-grant-tables */
+ if (opt_noacl)
+ opt_event_scheduler= EVENTS_DISABLED;
+
+
+ /* We need a temporary THD during boot */
+ if (!(thd= new THD()))
+ {
+ res= TRUE;
+ goto end;
+ }
+ /*
+ The thread stack does not start from this function but we cannot
+ guess the real value. So better some value that doesn't assert than
+ no value.
+ */
+ thd->thread_stack= (char*) &thd;
+ thd->store_globals();
+ lex_start(thd);
+
+ /*
+ We will need Event_db_repository anyway, even if the scheduler is
+ disabled - to perform events DDL.
+ */
+ if (!(db_repository= new Event_db_repository))
+ {
+ res= TRUE; /* fatal error: request unireg_abort */
+ goto end;
+ }
+
+ /*
+ Since we allow event DDL even if the scheduler is disabled,
+ check the system tables, as we might need them.
+ */
+ if (Event_db_repository::check_system_tables(thd))
+ {
+ sql_print_error("Event Scheduler: An error occurred when initializing "
+ "system tables.%s",
+ opt_event_scheduler == EVENTS_DISABLED ?
+ "" : " Disabling the Event Scheduler.");
+
+ /* Disable the scheduler since the system tables are not up to date */
+ opt_event_scheduler= EVENTS_DISABLED;
+ check_system_tables_error= TRUE;
+ goto end;
+ }
+
+ /*
+ Was disabled explicitly from the command line, or because we're running
+ with --skip-grant-tables, or because we have no system tables.
+ */
+ if (opt_event_scheduler == Events::EVENTS_DISABLED)
+ goto end;
+
+
+ DBUG_ASSERT(opt_event_scheduler == Events::EVENTS_ON ||
+ opt_event_scheduler == Events::EVENTS_OFF);
+
+ if (!(event_queue= new Event_queue) ||
+ !(scheduler= new Event_scheduler(event_queue)))
+ {
+ res= TRUE; /* fatal error: request unireg_abort */
+ goto end;
+ }
+
+ if (event_queue->init_queue(thd) || load_events_from_db(thd) ||
+ opt_event_scheduler == EVENTS_ON && scheduler->start())
+ {
+ sql_print_error("Event Scheduler: Error while loading from disk.");
+ res= TRUE; /* fatal error: request unireg_abort */
+ goto end;
+ }
+ Event_worker_thread::init(db_repository);
+
+end:
+ if (res)
+ {
+ delete db_repository;
+ delete event_queue;
+ delete scheduler;
+ }
+ delete thd;
+ /* Remember that we don't have a THD */
+ my_pthread_setspecific_ptr(THR_THD, NULL);
+
+ DBUG_RETURN(res);
+}
+
+/*
+ Cleans up scheduler's resources. Called at server shutdown.
+
+ SYNOPSIS
+ Events::deinit()
+
+ NOTES
+ This function is not synchronized.
+*/
+
+void
+Events::deinit()
+{
+ DBUG_ENTER("Events::deinit");
+
+ if (opt_event_scheduler != EVENTS_DISABLED)
+ {
+ delete scheduler;
+ scheduler= NULL; /* safety */
+ delete event_queue;
+ event_queue= NULL; /* safety */
+ }
+
+ delete db_repository;
+ db_repository= NULL; /* safety */
+
+ DBUG_VOID_RETURN;
+}
+
+
+/**
+ Inits Events mutexes
+
+ SYNOPSIS
+ Events::init_mutexes()
+ thd Thread
+*/
+
+void
+Events::init_mutexes()
+{
+ pthread_mutex_init(&LOCK_event_metadata, MY_MUTEX_INIT_FAST);
+}
+
+
+/*
+ Destroys Events mutexes
+
+ SYNOPSIS
+ Events::destroy_mutexes()
+*/
+
+void
+Events::destroy_mutexes()
+{
+ pthread_mutex_destroy(&LOCK_event_metadata);
+}
+
+
+/*
+ Dumps the internal status of the scheduler and the memory cache
+ into a table with two columns - Name & Value. Different properties
+ which could be useful for debugging for instance deadlocks are
+ returned.
+
+ SYNOPSIS
+ Events::dump_internal_status()
+*/
+
+void
+Events::dump_internal_status()
+{
+ DBUG_ENTER("Events::dump_internal_status");
+ puts("\n\n\nEvents status:");
+ puts("LLA = Last Locked At LUA = Last Unlocked At");
+ puts("WOC = Waiting On Condition DL = Data Locked");
+
+ pthread_mutex_lock(&LOCK_event_metadata);
+ if (opt_event_scheduler == EVENTS_DISABLED)
+ puts("The Event Scheduler is disabled");
+ else
+ {
+ scheduler->dump_internal_status();
+ event_queue->dump_internal_status();
+ }
+
+ pthread_mutex_unlock(&LOCK_event_metadata);
+ 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 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));
+ goto end;
+ }
+
+ opt_event_scheduler= new_state;
+
+end:
+ pthread_mutex_unlock(&LOCK_event_metadata);
+ DBUG_RETURN(ret);
+}
+
+
+/**
+ Loads all ENABLED events from mysql.event into a prioritized
+ queue.
+
+ This function is called during the server start up. It reads
+ every event, computes the next execution time, and if the event
+ needs execution, adds it to a prioritized queue. Otherwise, if
+ ON COMPLETION DROP is specified, the event is automatically
+ removed from the table.
+
+ @param[in,out] thd Thread context. Used for memory allocation in some cases.
+
+ @retval FALSE success
+ @retval TRUE error, the load is aborted
+
+ @note Reports the error to the console
+*/
+
+bool
+Events::load_events_from_db(THD *thd)
+{
+ TABLE *table;
+ READ_RECORD read_record_info;
+ bool ret= TRUE;
+ uint count= 0;
+ ulong saved_master_access;
+
+ DBUG_ENTER("Events::load_events_from_db");
+ DBUG_PRINT("enter", ("thd: 0x%lx", (long) thd));
+
+ /*
+ NOTE: even if we run in read-only mode, we should be able to lock the
+ mysql.event table for writing. In order to achieve this, we should call
+ mysql_lock_tables() under the super user.
+ */
+
+ saved_master_access= thd->security_ctx->master_access;
+ thd->security_ctx->master_access |= SUPER_ACL;
+
+ ret= db_repository->open_event_table(thd, TL_WRITE, &table);
+
+ thd->security_ctx->master_access= saved_master_access;
+
+ if (ret)
+ {
+ sql_print_error("Event Scheduler: Failed to open table mysql.event");
+ DBUG_RETURN(TRUE);
+ }
+
+ init_read_record(&read_record_info, thd, table, NULL, 0, 1, FALSE);
+ while (!(read_record_info.read_record(&read_record_info)))
+ {
+ Event_queue_element *et;
+ bool created;
+ bool drop_on_completion;
+
+ if (!(et= new Event_queue_element))
+ goto end;
+
+ DBUG_PRINT("info", ("Loading event from row."));
+
+ if (et->load_from_row(thd, table))
+ {
+ sql_print_error("Event Scheduler: "
+ "Error while loading events from mysql.event. "
+ "The table probably contains bad data or is corrupted");
+ delete et;
+ goto end;
+ }
+ drop_on_completion= (et->on_completion ==
+ Event_parse_data::ON_COMPLETION_DROP);
+
+
+ if (event_queue->create_event(thd, et, &created))
+ {
+ /* Out of memory */
+ delete et;
+ goto end;
+ }
+ if (created)
+ count++;
+ else if (drop_on_completion)
+ {
+ /*
+ If not created, a stale event - drop if immediately if
+ ON COMPLETION NOT PRESERVE.
+ XXX: This won't be replicated, thus the drop won't appear in
+ in the slave. When the slave is restarted it will drop events.
+ However, as the slave will be "out of sync", it might happen that
+ an event created on the master, after master restart, won't be
+ replicated to the slave correctly, as the create will fail there.
+ */
+ int rc= table->file->ha_delete_row(table->record[0]);
+ if (rc)
+ {
+ table->file->print_error(rc, MYF(0));
+ goto end;
+ }
+ }
+ }
+ sql_print_information("Event Scheduler: Loaded %d event%s",
+ count, (count == 1) ? "" : "s");
+ ret= FALSE;
+
+end:
+ end_read_record(&read_record_info);
+
+ close_thread_tables(thd);
+
+ DBUG_RETURN(ret);
+}
+
+/**
+ @} (End of group Event_Scheduler)
+*/
diff --git a/sql/events.h b/sql/events.h
new file mode 100644
index 00000000000..2bc87517748
--- /dev/null
+++ b/sql/events.h
@@ -0,0 +1,164 @@
+#ifndef _EVENT_H_
+#define _EVENT_H_
+/* Copyright (C) 2004-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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+/**
+ @defgroup Event_Scheduler Event Scheduler
+ @ingroup Runtime_Environment
+ @{
+
+ @file events.h
+
+ A public interface of Events_Scheduler module.
+*/
+
+class Event_parse_data;
+class Event_db_repository;
+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
+};
+
+
+int
+sortcmp_lex_string(LEX_STRING s, LEX_STRING t, CHARSET_INFO *cs);
+
+/**
+ @brief A facade to the functionality of the Event Scheduler.
+
+ Every public operation against the scheduler has to be executed via the
+ interface provided by a static method of this class. No instance of this
+ class is ever created and it has no non-static data members.
+
+ The life cycle of the Events module is the following:
+
+ At server start up:
+ set_opt_event_scheduler() -> init_mutexes() -> init()
+ When the server is running:
+ create_event(), drop_event(), start_or_stop_event_scheduler(), etc
+ At shutdown:
+ deinit(), destroy_mutexes().
+
+ The peculiar initialization and shutdown cycle is an adaptation to the
+ outside server startup/shutdown framework and mimics the rest of MySQL
+ subsystems (ACL, time zone tables, etc).
+*/
+
+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();
+
+ /* A hack needed for Event_queue_element */
+ static Event_db_repository *
+ get_db_repository() { return db_repository; }
+
+ static bool
+ init(my_bool opt_noacl);
+
+ static void
+ deinit();
+
+ static void
+ init_mutexes();
+
+ static void
+ 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
+ update_event(THD *thd, Event_parse_data *parse_data,
+ LEX_STRING *new_dbname, LEX_STRING *new_name);
+
+ static bool
+ drop_event(THD *thd, LEX_STRING dbname, LEX_STRING name, bool if_exists);
+
+ static void
+ drop_schema_events(THD *thd, char *db);
+
+ static bool
+ show_create_event(THD *thd, LEX_STRING dbname, LEX_STRING name);
+
+ /* Needed for both SHOW CREATE EVENT and INFORMATION_SCHEMA */
+ static int
+ reconstruct_interval_expression(String *buf, interval_type interval,
+ longlong expression);
+
+ static int
+ fill_schema_events(THD *thd, TABLE_LIST *tables, COND * /* cond */);
+
+ static void
+ 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;
+
+private:
+ /* Prevent use of these */
+ Events(const Events &);
+ void operator=(Events &);
+};
+
+/**
+ @} (end of group Event Scheduler)
+*/
+
+#endif /* _EVENT_H_ */
diff --git a/sql/examples/ha_example.cc b/sql/examples/ha_example.cc
deleted file mode 100644
index d59ada3b445..00000000000
--- a/sql/examples/ha_example.cc
+++ /dev/null
@@ -1,706 +0,0 @@
-/* Copyright (C) 2003 MySQL AB
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; version 2 of the License.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
-
-/*
- ha_example is a stubbed storage engine. It does nothing at this point. It
- will let you create/open/delete tables but that is all. You can enable it
- in your buld by doing the following during your build process:
- ./configure --with-example-storage-engine
-
- Once this is done mysql will let you create tables with:
- CREATE TABLE A (...) ENGINE=EXAMPLE;
-
- The example is setup to use table locks. It implements an example "SHARE"
- that is inserted into a hash by table name. You can use this to store
- information of state that any example handler object will be able to see
- if it is using the same table.
-
- Please read the object definition in ha_example.h before reading the rest
- if this file.
-
- To get an idea of what occurs here is an example select that would do a
- scan of an entire table:
- ha_example::store_lock
- ha_example::external_lock
- ha_example::info
- ha_example::rnd_init
- ha_example::extra
- ENUM HA_EXTRA_CACHE Cash record in HA_rrnd()
- ha_example::rnd_next
- ha_example::rnd_next
- ha_example::rnd_next
- ha_example::rnd_next
- ha_example::rnd_next
- ha_example::rnd_next
- ha_example::rnd_next
- ha_example::rnd_next
- ha_example::rnd_next
- ha_example::extra
- ENUM HA_EXTRA_NO_CACHE End cacheing of records (def)
- ha_example::external_lock
- ha_example::extra
- ENUM HA_EXTRA_RESET Reset database to after open
-
- In the above example has 9 row called before rnd_next signalled that it was
- at the end of its data. In the above example the table was already opened
- (or you would have seen a call to ha_example::open(). Calls to
- ha_example::extra() are hints as to what will be occuring to the request.
-
- Happy coding!
- -Brian
-*/
-
-#ifdef USE_PRAGMA_IMPLEMENTATION
-#pragma implementation // gcc: Class implementation
-#endif
-
-#include "../mysql_priv.h"
-
-#ifdef HAVE_EXAMPLE_DB
-#include "ha_example.h"
-
-
-handlerton example_hton= {
- "EXAMPLE",
- SHOW_OPTION_YES,
- "Example storage engine",
- DB_TYPE_EXAMPLE_DB,
- NULL, /* We do need to write one! */
- 0, /* slot */
- 0, /* savepoint size. */
- NULL, /* close_connection */
- NULL, /* savepoint */
- NULL, /* rollback to savepoint */
- NULL, /* release savepoint */
- NULL, /* commit */
- NULL, /* rollback */
- NULL, /* prepare */
- NULL, /* recover */
- NULL, /* commit_by_xid */
- NULL, /* rollback_by_xid */
- NULL, /* create_cursor_read_view */
- NULL, /* set_cursor_read_view */
- NULL, /* close_cursor_read_view */
- HTON_CAN_RECREATE
-};
-
-/* Variables for example share methods */
-static HASH example_open_tables; // Hash used to track open tables
-pthread_mutex_t example_mutex; // This is the mutex we use to init the hash
-static int example_init= 0; // Variable for checking the init state of hash
-
-
-/*
- Function we use in the creation of our hash to get key.
-*/
-static byte* example_get_key(EXAMPLE_SHARE *share,uint *length,
- my_bool not_used __attribute__((unused)))
-{
- *length=share->table_name_length;
- return (byte*) share->table_name;
-}
-
-
-/*
- Example of simple lock controls. The "share" it creates is structure we will
- pass to each example handler. Do you have to have one of these? Well, you have
- pieces that are used for locking, and they are needed to function.
-*/
-static EXAMPLE_SHARE *get_share(const char *table_name, TABLE *table)
-{
- EXAMPLE_SHARE *share;
- uint length;
- char *tmp_name;
-
- /*
- 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 (!example_init)
- {
- /* Hijack a mutex for init'ing the storage engine */
- pthread_mutex_lock(&LOCK_mysql_create_db);
- if (!example_init)
- {
- example_init++;
- VOID(pthread_mutex_init(&example_mutex,MY_MUTEX_INIT_FAST));
- (void) hash_init(&example_open_tables,system_charset_info,32,0,0,
- (hash_get_key) example_get_key,0,0);
- }
- pthread_mutex_unlock(&LOCK_mysql_create_db);
- }
- pthread_mutex_lock(&example_mutex);
- length=(uint) strlen(table_name);
-
- if (!(share=(EXAMPLE_SHARE*) hash_search(&example_open_tables,
- (byte*) table_name,
- length)))
- {
- if (!(share=(EXAMPLE_SHARE *)
- my_multi_malloc(MYF(MY_WME | MY_ZEROFILL),
- &share, sizeof(*share),
- &tmp_name, length+1,
- NullS)))
- {
- pthread_mutex_unlock(&example_mutex);
- return NULL;
- }
-
- share->use_count=0;
- share->table_name_length=length;
- share->table_name=tmp_name;
- strmov(share->table_name,table_name);
- if (my_hash_insert(&example_open_tables, (byte*) share))
- goto error;
- thr_lock_init(&share->lock);
- pthread_mutex_init(&share->mutex,MY_MUTEX_INIT_FAST);
- }
- share->use_count++;
- pthread_mutex_unlock(&example_mutex);
-
- return share;
-
-error:
- pthread_mutex_destroy(&share->mutex);
- pthread_mutex_unlock(&example_mutex);
- my_free((gptr) share, MYF(0));
-
- return NULL;
-}
-
-
-/*
- 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.
-*/
-static int free_share(EXAMPLE_SHARE *share)
-{
- pthread_mutex_lock(&example_mutex);
- if (!--share->use_count)
- {
- hash_delete(&example_open_tables, (byte*) share);
- thr_lock_delete(&share->lock);
- pthread_mutex_destroy(&share->mutex);
- my_free((gptr) share, MYF(0));
- }
- pthread_mutex_unlock(&example_mutex);
-
- return 0;
-}
-
-
-ha_example::ha_example(TABLE *table_arg)
- :handler(&example_hton, table_arg)
-{}
-
-/*
- If frm_error() is called then we will use this to to find out what file extentions
- exist for the storage engine. This is also used by the default rename_table and
- delete_table method in handler.cc.
-
- For engines that have two file name extentions (separate meta/index file
- and data file), the order of elements is relevant. First element of engine
- file name extentions array should be meta/index file extention. Second
- element - data file extention. This order is assumed by
- prepare_for_repair() when REPAIR TABLE ... USE_FRM is issued.
-*/
-static const char *ha_example_exts[] = {
- NullS
-};
-
-const char **ha_example::bas_ext() const
-{
- return ha_example_exts;
-}
-
-
-/*
- Used for opening tables. The name will be the name of the file.
- A table is opened when it needs to be opened. For instance
- when a request comes in for a select on the table (tables are not
- open and closed for each request, they are cached).
-
- Called from handler.cc by handler::ha_open(). The server opens all tables by
- calling ha_open() which then calls the handler specific open().
-*/
-int ha_example::open(const char *name, int mode, uint test_if_locked)
-{
- DBUG_ENTER("ha_example::open");
-
- if (!(share = get_share(name, table)))
- DBUG_RETURN(1);
- thr_lock_data_init(&share->lock,&lock,NULL);
-
- DBUG_RETURN(0);
-}
-
-
-/*
- Closes a table. We call the free_share() function to free any resources
- that we have allocated in the "shared" structure.
-
- Called from sql_base.cc, sql_select.cc, and table.cc.
- In sql_select.cc it is only used to close up temporary tables or during
- the process where a temporary table is converted over to being a
- myisam table.
- For sql_base.cc look at close_data_tables().
-*/
-int ha_example::close(void)
-{
- DBUG_ENTER("ha_example::close");
- DBUG_RETURN(free_share(share));
-}
-
-
-/*
- write_row() inserts a row. No extra() hint is given currently if a bulk load
- is happeneding. buf() is a byte array of data. You can use the field
- information to extract the data from the native byte array type.
- Example of this would be:
- for (Field **field=table->field ; *field ; field++)
- {
- ...
- }
-
- See ha_tina.cc for an example of extracting all of the data as strings.
- ha_berekly.cc has an example of how to store it intact by "packing" it
- for ha_berkeley's own native storage type.
-
- See the note for update_row() on auto_increments and timestamps. This
- case also applied to write_row().
-
- Called from item_sum.cc, item_sum.cc, sql_acl.cc, sql_insert.cc,
- sql_insert.cc, sql_select.cc, sql_table.cc, sql_udf.cc, and sql_update.cc.
-*/
-int ha_example::write_row(byte * buf)
-{
- DBUG_ENTER("ha_example::write_row");
- DBUG_RETURN(HA_ERR_WRONG_COMMAND);
-}
-
-
-/*
- Yes, update_row() does what you expect, it updates a row. old_data will have
- the previous row record in it, while new_data will have the newest data in
- it.
- Keep in mind that the server can do updates based on ordering if an ORDER BY
- clause was used. Consecutive ordering is not guarenteed.
- Currently new_data will not have an updated auto_increament record, or
- and updated timestamp field. You can do these for example by doing these:
- if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_UPDATE)
- table->timestamp_field->set_time();
- if (table->next_number_field && record == table->record[0])
- update_auto_increment();
-
- Called from sql_select.cc, sql_acl.cc, sql_update.cc, and sql_insert.cc.
-*/
-int ha_example::update_row(const byte * old_data, byte * new_data)
-{
-
- DBUG_ENTER("ha_example::update_row");
- DBUG_RETURN(HA_ERR_WRONG_COMMAND);
-}
-
-
-/*
- This will delete a row. buf will contain a copy of the row to be deleted.
- The server will call this right after the current row has been called (from
- either a previous rnd_nexT() or index call).
- If you keep a pointer to the last row or can access a primary key it will
- make doing the deletion quite a bit easier.
- Keep in mind that the server does no guarentee consecutive deletions. ORDER BY
- clauses can be used.
-
- Called in sql_acl.cc and sql_udf.cc to manage internal table information.
- Called in sql_delete.cc, sql_insert.cc, and sql_select.cc. In sql_select it is
- used for removing duplicates while in insert it is used for REPLACE calls.
-*/
-int ha_example::delete_row(const byte * buf)
-{
- DBUG_ENTER("ha_example::delete_row");
- DBUG_RETURN(HA_ERR_WRONG_COMMAND);
-}
-
-
-/*
- Positions an index cursor to the index specified in the handle. Fetches the
- row if available. If the key value is null, begin at the first key of the
- index.
-*/
-int ha_example::index_read(byte * buf, const byte * key,
- uint key_len __attribute__((unused)),
- enum ha_rkey_function find_flag
- __attribute__((unused)))
-{
- DBUG_ENTER("ha_example::index_read");
- DBUG_RETURN(HA_ERR_WRONG_COMMAND);
-}
-
-
-/*
- Positions an index cursor to the index specified in key. Fetches the
- row if any. This is only used to read whole keys.
-*/
-int ha_example::index_read_idx(byte * buf, uint index, const byte * key,
- uint key_len __attribute__((unused)),
- enum ha_rkey_function find_flag
- __attribute__((unused)))
-{
- DBUG_ENTER("ha_example::index_read_idx");
- DBUG_RETURN(HA_ERR_WRONG_COMMAND);
-}
-
-
-/*
- Used to read forward through the index.
-*/
-int ha_example::index_next(byte * buf)
-{
- DBUG_ENTER("ha_example::index_next");
- DBUG_RETURN(HA_ERR_WRONG_COMMAND);
-}
-
-
-/*
- Used to read backwards through the index.
-*/
-int ha_example::index_prev(byte * buf)
-{
- DBUG_ENTER("ha_example::index_prev");
- DBUG_RETURN(HA_ERR_WRONG_COMMAND);
-}
-
-
-/*
- index_first() asks for the first key in the index.
-
- Called from opt_range.cc, opt_sum.cc, sql_handler.cc,
- and sql_select.cc.
-*/
-int ha_example::index_first(byte * buf)
-{
- DBUG_ENTER("ha_example::index_first");
- DBUG_RETURN(HA_ERR_WRONG_COMMAND);
-}
-
-
-/*
- index_last() asks for the last key in the index.
-
- Called from opt_range.cc, opt_sum.cc, sql_handler.cc,
- and sql_select.cc.
-*/
-int ha_example::index_last(byte * buf)
-{
- DBUG_ENTER("ha_example::index_last");
- DBUG_RETURN(HA_ERR_WRONG_COMMAND);
-}
-
-
-/*
- rnd_init() is called when the system wants the storage engine to do a table
- scan.
- See the example in the introduction at the top of this file to see when
- rnd_init() is called.
-
- Called from filesort.cc, records.cc, sql_handler.cc, sql_select.cc, sql_table.cc,
- and sql_update.cc.
-*/
-int ha_example::rnd_init(bool scan)
-{
- DBUG_ENTER("ha_example::rnd_init");
- DBUG_RETURN(HA_ERR_WRONG_COMMAND);
-}
-
-int ha_example::rnd_end()
-{
- DBUG_ENTER("ha_example::rnd_end");
- DBUG_RETURN(0);
-}
-
-/*
- This is called for each row of the table scan. When you run out of records
- you should return HA_ERR_END_OF_FILE. Fill buff up with the row information.
- The Field structure for the table is the key to getting data into buf
- in a manner that will allow the server to understand it.
-
- Called from filesort.cc, records.cc, sql_handler.cc, sql_select.cc, sql_table.cc,
- and sql_update.cc.
-*/
-int ha_example::rnd_next(byte *buf)
-{
- DBUG_ENTER("ha_example::rnd_next");
- DBUG_RETURN(HA_ERR_END_OF_FILE);
-}
-
-
-/*
- position() is called after each call to rnd_next() if the data needs
- to be ordered. You can do something like the following to store
- the position:
- my_store_ptr(ref, ref_length, current_position);
-
- The server uses ref to store data. ref_length in the above case is
- the size needed to store current_position. ref is just a byte array
- that the server will maintain. If you are using offsets to mark rows, then
- current_position should be the offset. If it is a primary key like in
- BDB, then it needs to be a primary key.
-
- Called from filesort.cc, sql_select.cc, sql_delete.cc and sql_update.cc.
-*/
-void ha_example::position(const byte *record)
-{
- DBUG_ENTER("ha_example::position");
- DBUG_VOID_RETURN;
-}
-
-
-/*
- This is like rnd_next, but you are given a position to use
- to determine the row. The position will be of the type that you stored in
- ref. You can use ha_get_ptr(pos,ref_length) to retrieve whatever key
- or position you saved when position() was called.
- Called from filesort.cc records.cc sql_insert.cc sql_select.cc sql_update.cc.
-*/
-int ha_example::rnd_pos(byte * buf, byte *pos)
-{
- DBUG_ENTER("ha_example::rnd_pos");
- DBUG_RETURN(HA_ERR_WRONG_COMMAND);
-}
-
-
-/*
- ::info() is used to return information to the optimizer.
- see my_base.h for the complete description
-
- Currently this table handler doesn't implement most of the fields
- really needed. SHOW also makes use of this data
- Another note, you will probably want to have the following in your
- code:
- if (records < 2)
- records = 2;
- The reason is that the server will optimize for cases of only a single
- record. If in a table scan you don't know the number of records
- it will probably be better to set records to two so you can return
- as many records as you need.
- Along with records a few more variables you may wish to set are:
- records
- deleted
- data_file_length
- index_file_length
- delete_length
- check_time
- Take a look at the public variables in handler.h for more information.
-
- Called in:
- filesort.cc
- ha_heap.cc
- item_sum.cc
- opt_sum.cc
- sql_delete.cc
- sql_delete.cc
- sql_derived.cc
- sql_select.cc
- sql_select.cc
- sql_select.cc
- sql_select.cc
- sql_select.cc
- sql_show.cc
- sql_show.cc
- sql_show.cc
- sql_show.cc
- sql_table.cc
- sql_union.cc
- sql_update.cc
-
-*/
-int ha_example::info(uint flag)
-{
- DBUG_ENTER("ha_example::info");
- DBUG_RETURN(0);
-}
-
-
-/*
- extra() is called whenever the server wishes to send a hint to
- the storage engine. The myisam engine implements the most hints.
- ha_innodb.cc has the most exhaustive list of these hints.
-*/
-int ha_example::extra(enum ha_extra_function operation)
-{
- DBUG_ENTER("ha_example::extra");
- DBUG_RETURN(0);
-}
-
-
-/*
- Deprecated and likely to be removed in the future. Storage engines normally
- just make a call like:
- ha_example::extra(HA_EXTRA_RESET);
- to handle it.
-*/
-int ha_example::reset(void)
-{
- DBUG_ENTER("ha_example::reset");
- DBUG_RETURN(0);
-}
-
-
-/*
- Used to delete all rows in a table. Both for cases of truncate and
- for cases where the optimizer realizes that all rows will be
- removed as a result of a SQL statement.
-
- Called from item_sum.cc by Item_func_group_concat::clear(),
- Item_sum_count_distinct::clear(), and Item_func_group_concat::clear().
- Called from sql_delete.cc by mysql_delete().
- Called from sql_select.cc by JOIN::reinit().
- Called from sql_union.cc by st_select_lex_unit::exec().
-*/
-int ha_example::delete_all_rows()
-{
- DBUG_ENTER("ha_example::delete_all_rows");
- DBUG_RETURN(HA_ERR_WRONG_COMMAND);
-}
-
-
-/*
- First you should go read the section "locking functions for mysql" in
- lock.cc to understand this.
- This create a lock on the table. If you are implementing a storage engine
- that can handle transacations look at ha_berkely.cc to see how you will
- want to goo about doing this. Otherwise you should consider calling flock()
- here.
-
- Called from lock.cc by lock_external() and unlock_external(). Also called
- from sql_table.cc by copy_data_between_tables().
-*/
-int ha_example::external_lock(THD *thd, int lock_type)
-{
- DBUG_ENTER("ha_example::external_lock");
- DBUG_RETURN(0);
-}
-
-
-/*
- The idea with handler::store_lock() is the following:
-
- The statement decided which locks we should need for the table
- for updates/deletes/inserts we get WRITE locks, for SELECT... we get
- read locks.
-
- Before adding the lock into the table lock handler (see thr_lock.c)
- mysqld calls store lock with the requested locks. Store lock can now
- modify a write lock to a read lock (or some other lock), ignore the
- lock (if we don't want to use MySQL table locks at all) or add locks
- for many tables (like we do when we are using a MERGE handler).
-
- Berkeley DB for example changes all WRITE locks to TL_WRITE_ALLOW_WRITE
- (which signals that we are doing WRITES, but we are still allowing other
- reader's and writer's.
-
- When releasing locks, store_lock() are also called. In this case one
- usually doesn't have to do anything.
-
- In some exceptional cases MySQL may send a request for a TL_IGNORE;
- This means that we are requesting the same lock as last time and this
- should also be ignored. (This may happen when someone does a flush
- table when we have opened a part of the tables, in which case mysqld
- closes and reopens the tables and tries to get the same locks at last
- time). In the future we will probably try to remove this.
-
- Called from lock.cc by get_lock_data().
-*/
-THR_LOCK_DATA **ha_example::store_lock(THD *thd,
- THR_LOCK_DATA **to,
- enum thr_lock_type lock_type)
-{
- if (lock_type != TL_IGNORE && lock.type == TL_UNLOCK)
- lock.type=lock_type;
- *to++= &lock;
- return to;
-}
-
-/*
- Used to delete a table. By the time delete_table() has been called all
- opened references to this table will have been closed (and your globally
- shared references released. The variable name will just be the name of
- the table. You will need to remove any files you have created at this point.
-
- If you do not implement this, the default delete_table() is called from
- handler.cc and it will delete all files with the file extentions returned
- by bas_ext().
-
- Called from handler.cc by delete_table and ha_create_table(). Only used
- during create if the table_flag HA_DROP_BEFORE_CREATE was specified for
- the storage engine.
-*/
-int ha_example::delete_table(const char *name)
-{
- DBUG_ENTER("ha_example::delete_table");
- /* This is not implemented but we want someone to be able that it works. */
- DBUG_RETURN(0);
-}
-
-/*
- Renames a table from one name to another from alter table call.
-
- If you do not implement this, the default rename_table() is called from
- handler.cc and it will delete all files with the file extentions returned
- by bas_ext().
-
- Called from sql_table.cc by mysql_rename_table().
-*/
-int ha_example::rename_table(const char * from, const char * to)
-{
- DBUG_ENTER("ha_example::rename_table ");
- DBUG_RETURN(HA_ERR_WRONG_COMMAND);
-}
-
-/*
- 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().
-*/
-ha_rows ha_example::records_in_range(uint inx, key_range *min_key,
- key_range *max_key)
-{
- DBUG_ENTER("ha_example::records_in_range");
- DBUG_RETURN(10); // low number to force index usage
-}
-
-
-/*
- create() is called to create a database. The variable name will have the name
- of the table. When create() is called you do not need to worry about opening
- the table. Also, the FRM file will have already been created so adjusting
- create_info will not do you any good. You can overwrite the frm file at this
- point if you wish to change the table definition, but there are no methods
- currently provided for doing that.
-
- Called from handle.cc by ha_create_table().
-*/
-int ha_example::create(const char *name, TABLE *table_arg,
- HA_CREATE_INFO *create_info)
-{
- DBUG_ENTER("ha_example::create");
- /* This is not implemented but we want someone to be able that it works. */
- DBUG_RETURN(0);
-}
-#endif /* HAVE_EXAMPLE_DB */
diff --git a/sql/examples/ha_example.h b/sql/examples/ha_example.h
deleted file mode 100644
index d6ec93cf97f..00000000000
--- a/sql/examples/ha_example.h
+++ /dev/null
@@ -1,153 +0,0 @@
-/* Copyright (C) 2003 MySQL AB
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; version 2 of the License.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
-
-/*
- Please read ha_exmple.cc before reading this file.
- Please keep in mind that the example storage engine implements all methods
- that are required to be implemented. handler.h has a full list of methods
- that you can implement.
-*/
-
-#ifdef USE_PRAGMA_INTERFACE
-#pragma interface /* gcc class implementation */
-#endif
-
-/*
- EXAMPLE_SHARE is a structure that will be shared amoung all open handlers
- The example implements the minimum of what you will probably need.
-*/
-typedef struct st_example_share {
- char *table_name;
- uint table_name_length,use_count;
- pthread_mutex_t mutex;
- THR_LOCK lock;
-} EXAMPLE_SHARE;
-
-/*
- Class definition for the storage engine
-*/
-class ha_example: public handler
-{
- THR_LOCK_DATA lock; /* MySQL lock */
- EXAMPLE_SHARE *share; /* Shared lock info */
-
-public:
- ha_example(TABLE *table_arg);
- ~ha_example()
- {
- }
- /* The name that will be used for display purposes */
- const char *table_type() const { return "EXAMPLE"; }
- /*
- The name of the index type that will be used for display
- don't implement this method unless you really have indexes
- */
- const char *index_type(uint inx) { return "HASH"; }
- const char **bas_ext() const;
- /*
- This is a list of flags that says what the storage engine
- implements. The current table flags are documented in
- handler.h
- */
- ulong table_flags() const
- {
- return 0;
- }
- /*
- This is a bitmap of flags that says how the storage engine
- implements indexes. The current index flags are documented in
- handler.h. If you do not implement indexes, just return zero
- here.
-
- part is the key part to check. First key part is 0
- If all_parts it's set, MySQL want to know the flags for the combined
- index up to and including 'part'.
- */
- ulong index_flags(uint inx, uint part, bool all_parts) const
- {
- return 0;
- }
- /*
- unireg.cc will call the following to make sure that the storage engine can
- handle the data it is about to send.
-
- Return *real* limits of your storage engine here. MySQL will do
- min(your_limits, MySQL_limits) automatically
-
- There is no need to implement ..._key_... methods if you don't suport
- indexes.
- */
- uint max_supported_record_length() const { return HA_MAX_REC_LENGTH; }
- uint max_supported_keys() const { return 0; }
- uint max_supported_key_parts() const { return 0; }
- uint max_supported_key_length() const { return 0; }
- /*
- Called in test_quick_select to determine if indexes should be used.
- */
- virtual double scan_time() { return (double) (records+deleted) / 20.0+10; }
- /*
- The next method will never be called if you do not implement indexes.
- */
- virtual double read_time(ha_rows rows) { return (double) rows / 20.0+1; }
-
- /*
- Everything below are methods that we implment in ha_example.cc.
-
- Most of these methods are not obligatory, skip them and
- MySQL will treat them as not implemented
- */
- int open(const char *name, int mode, uint test_if_locked); // required
- int close(void); // required
-
- int write_row(byte * buf);
- int update_row(const byte * old_data, byte * new_data);
- int delete_row(const byte * buf);
- int index_read(byte * buf, const byte * key,
- uint key_len, enum ha_rkey_function find_flag);
- int index_read_idx(byte * buf, uint idx, const byte * key,
- uint key_len, enum ha_rkey_function find_flag);
- int index_next(byte * buf);
- int index_prev(byte * buf);
- int index_first(byte * buf);
- int index_last(byte * buf);
- /*
- unlike index_init(), rnd_init() can be called two times
- without rnd_end() in between (it only makes sense if scan=1).
- then the second call should prepare for the new table scan
- (e.g if rnd_init allocates the cursor, second call should
- position it to the start of the table, no need to deallocate
- and allocate it again
- */
- int rnd_init(bool scan); //required
- int rnd_end();
- int rnd_next(byte *buf); //required
- int rnd_pos(byte * buf, byte *pos); //required
- void position(const byte *record); //required
- int info(uint); //required
-
- int extra(enum ha_extra_function operation);
- int reset(void);
- int external_lock(THD *thd, int lock_type); //required
- int delete_all_rows(void);
- ha_rows records_in_range(uint inx, key_range *min_key,
- key_range *max_key);
- int delete_table(const char *from);
- int rename_table(const char * from, const char * to);
- int create(const char *name, TABLE *form,
- HA_CREATE_INFO *create_info); //required
-
- THR_LOCK_DATA **store_lock(THD *thd, THR_LOCK_DATA **to,
- enum thr_lock_type lock_type); //required
-};
diff --git a/sql/examples/ha_tina.cc b/sql/examples/ha_tina.cc
deleted file mode 100644
index aaaa3b8ffb4..00000000000
--- a/sql/examples/ha_tina.cc
+++ /dev/null
@@ -1,1003 +0,0 @@
-/* Copyright (C) 2003 MySQL AB
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; version 2 of the License.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
-
-/*
- Make sure to look at ha_tina.h for more details.
-
- First off, this is a play thing for me, there are a number of things wrong with it:
- *) It was designed for csv and therefor its performance is highly questionable.
- *) Indexes have not been implemented. This is because the files can be traded in
- and out of the table directory without having to worry about rebuilding anything.
- *) NULLs and "" are treated equally (like a spreadsheet).
- *) There was in the beginning no point to anyone seeing this other then me, so there
- is a good chance that I haven't quite documented it well.
- *) Less design, more "make it work"
-
- Now there are a few cool things with it:
- *) Errors can result in corrupted data files.
- *) Data files can be read by spreadsheets directly.
-
-TODO:
- *) Move to a block system for larger files
- *) Error recovery, its all there, just need to finish it
- *) Document how the chains work.
-
- -Brian
-*/
-
-#ifdef USE_PRAGMA_IMPLEMENTATION
-#pragma implementation // gcc: Class implementation
-#endif
-
-#include "mysql_priv.h"
-
-#ifdef HAVE_CSV_DB
-
-#include "ha_tina.h"
-#include <sys/mman.h>
-
-/* Stuff for shares */
-pthread_mutex_t tina_mutex;
-static HASH tina_open_tables;
-static int tina_init= 0;
-
-handlerton tina_hton= {
- "CSV",
- SHOW_OPTION_YES,
- "CSV storage engine",
- DB_TYPE_CSV_DB,
- NULL, /* One needs to be written! */
- 0, /* slot */
- 0, /* savepoint size. */
- NULL, /* close_connection */
- NULL, /* savepoint */
- NULL, /* rollback to savepoint */
- NULL, /* release savepoint */
- NULL, /* commit */
- NULL, /* rollback */
- NULL, /* prepare */
- NULL, /* recover */
- NULL, /* commit_by_xid */
- NULL, /* rollback_by_xid */
- NULL, /* create_cursor_read_view */
- NULL, /* set_cursor_read_view */
- NULL, /* close_cursor_read_view */
- HTON_CAN_RECREATE
-};
-
-/*****************************************************************************
- ** TINA tables
- *****************************************************************************/
-
-/*
- Used for sorting chains with qsort().
-*/
-int sort_set (tina_set *a, tina_set *b)
-{
- /*
- We assume that intervals do not intersect. So, it is enought to compare
- any two points. Here we take start of intervals for comparison.
- */
- return ( a->begin > b->begin ? -1 : ( a->begin < b->begin ? 1 : 0 ) );
-}
-
-static byte* tina_get_key(TINA_SHARE *share,uint *length,
- my_bool not_used __attribute__((unused)))
-{
- *length=share->table_name_length;
- return (byte*) share->table_name;
-}
-
-
-int free_mmap(TINA_SHARE *share)
-{
- DBUG_ENTER("ha_tina::free_mmap");
- if (share->mapped_file)
- {
- /*
- Invalidate the mapped in pages. Some operating systems (eg OpenBSD)
- would reuse already cached pages even if the file has been altered
- using fd based I/O. This may be optimized by perhaps only invalidating
- the last page but optimization of deprecated code is not important.
- */
- msync(share->mapped_file, 0, MS_INVALIDATE);
- if (munmap(share->mapped_file, share->file_stat.st_size))
- DBUG_RETURN(1);
- }
- share->mapped_file= NULL;
- DBUG_RETURN(0);
-}
-
-/*
- Reloads the mmap file.
-*/
-int get_mmap(TINA_SHARE *share, int write)
-{
- DBUG_ENTER("ha_tina::get_mmap");
-
- if (free_mmap(share))
- DBUG_RETURN(1);
-
- if (my_fstat(share->data_file, &share->file_stat, MYF(MY_WME)) == -1)
- DBUG_RETURN(1);
-
- if (share->file_stat.st_size)
- {
- if (write)
- share->mapped_file= (byte *)mmap(NULL, share->file_stat.st_size,
- PROT_READ|PROT_WRITE, MAP_SHARED,
- share->data_file, 0);
- else
- share->mapped_file= (byte *)mmap(NULL, share->file_stat.st_size,
- PROT_READ, MAP_PRIVATE,
- share->data_file, 0);
- if ((share->mapped_file ==(caddr_t)-1))
- {
- /*
- Bad idea you think? See the problem is that nothing actually checks
- the return value of ::rnd_init(), so tossing an error is about
- it for us.
- Never going to happen right? :)
- */
- my_message(errno, "Woops, blew up opening a mapped file", 0);
- DBUG_ASSERT(0);
- DBUG_RETURN(1);
- }
- }
- else
- share->mapped_file= NULL;
-
- DBUG_RETURN(0);
-}
-
-/*
- Simple lock controls.
-*/
-static TINA_SHARE *get_share(const char *table_name, TABLE *table)
-{
- TINA_SHARE *share;
- char *tmp_name;
- uint length;
-
- if (!tina_init)
- {
- /* Hijack a mutex for init'ing the storage engine */
- pthread_mutex_lock(&LOCK_mysql_create_db);
- if (!tina_init)
- {
- tina_init++;
- VOID(pthread_mutex_init(&tina_mutex,MY_MUTEX_INIT_FAST));
- (void) hash_init(&tina_open_tables,system_charset_info,32,0,0,
- (hash_get_key) tina_get_key,0,0);
- }
- pthread_mutex_unlock(&LOCK_mysql_create_db);
- }
- pthread_mutex_lock(&tina_mutex);
- length=(uint) strlen(table_name);
- if (!(share=(TINA_SHARE*) hash_search(&tina_open_tables,
- (byte*) table_name,
- length)))
- {
- char data_file_name[FN_REFLEN];
- if (!my_multi_malloc(MYF(MY_WME | MY_ZEROFILL),
- &share, sizeof(*share),
- &tmp_name, length+1,
- NullS))
- {
- pthread_mutex_unlock(&tina_mutex);
- return NULL;
- }
-
- share->use_count=0;
- share->table_name_length=length;
- share->table_name=tmp_name;
- strmov(share->table_name,table_name);
- fn_format(data_file_name, table_name, "", ".CSV",
- MY_REPLACE_EXT | MY_UNPACK_FILENAME);
-
- if ((share->data_file= my_open(data_file_name, O_RDWR|O_APPEND,
- MYF(0))) == -1)
- goto error;
-
- if (my_hash_insert(&tina_open_tables, (byte*) share))
- goto error;
- thr_lock_init(&share->lock);
- pthread_mutex_init(&share->mutex,MY_MUTEX_INIT_FAST);
-
- /* We only use share->data_file for writing, so we scan to the end to append */
- if (my_seek(share->data_file, 0, SEEK_END, MYF(0)) == MY_FILEPOS_ERROR)
- goto error2;
-
- share->mapped_file= NULL; // We don't know the state since we just allocated it
- if (get_mmap(share, 0) > 0)
- goto error3;
- }
- share->use_count++;
- pthread_mutex_unlock(&tina_mutex);
-
- return share;
-
-error3:
- my_close(share->data_file,MYF(0));
-error2:
- thr_lock_delete(&share->lock);
- pthread_mutex_destroy(&share->mutex);
- hash_delete(&tina_open_tables, (byte*) share);
-error:
- pthread_mutex_unlock(&tina_mutex);
- my_free((gptr) share, MYF(0));
-
- return NULL;
-}
-
-
-/*
- Free lock controls.
-*/
-static int free_share(TINA_SHARE *share)
-{
- DBUG_ENTER("ha_tina::free_share");
- pthread_mutex_lock(&tina_mutex);
- int result_code= 0;
- if (!--share->use_count){
- /* Drop the mapped file */
- free_mmap(share);
- result_code= my_close(share->data_file,MYF(0));
- hash_delete(&tina_open_tables, (byte*) share);
- thr_lock_delete(&share->lock);
- pthread_mutex_destroy(&share->mutex);
- my_free((gptr) share, MYF(0));
- }
- pthread_mutex_unlock(&tina_mutex);
-
- DBUG_RETURN(result_code);
-}
-
-bool tina_end()
-{
- if (tina_init)
- {
- hash_free(&tina_open_tables);
- VOID(pthread_mutex_destroy(&tina_mutex));
- }
- tina_init= 0;
- return FALSE;
-}
-
-
-/*
- Finds the end of a line.
- Currently only supports files written on a UNIX OS.
-*/
-byte * find_eoln(byte *data, off_t begin, off_t end)
-{
- for (off_t x= begin; x < end; x++)
- if (data[x] == '\n')
- return data + x;
-
- return 0;
-}
-
-
-ha_tina::ha_tina(TABLE *table_arg)
- :handler(&tina_hton, table_arg),
- /*
- These definitions are found in hanler.h
- These are not probably completely right.
- */
- current_position(0), next_position(0), chain_alloced(0),
- chain_size(DEFAULT_CHAIN_LENGTH), records_is_known(0)
-{
- /* Set our original buffers from pre-allocated memory */
- buffer.set(byte_buffer, IO_SIZE, &my_charset_bin);
- chain= chain_buffer;
-}
-
-/*
- Encode a buffer into the quoted format.
-*/
-int ha_tina::encode_quote(byte *buf)
-{
- char attribute_buffer[1024];
- String attribute(attribute_buffer, sizeof(attribute_buffer), &my_charset_bin);
-
- buffer.length(0);
- for (Field **field=table->field ; *field ; field++)
- {
- const char *ptr;
- const char *end_ptr;
-
- (*field)->val_str(&attribute,&attribute);
- ptr= attribute.ptr();
- end_ptr= attribute.length() + ptr;
-
- buffer.append('"');
-
- while (ptr < end_ptr)
- {
- if (*ptr == '"')
- {
- buffer.append('\\');
- buffer.append('"');
- *ptr++;
- }
- else if (*ptr == '\r')
- {
- buffer.append('\\');
- buffer.append('r');
- *ptr++;
- }
- else if (*ptr == '\\')
- {
- buffer.append('\\');
- buffer.append('\\');
- *ptr++;
- }
- else if (*ptr == '\n')
- {
- buffer.append('\\');
- buffer.append('n');
- *ptr++;
- }
- else
- buffer.append(*ptr++);
- }
- buffer.append('"');
- buffer.append(',');
- }
- // Remove the comma, add a line feed
- buffer.length(buffer.length() - 1);
- buffer.append('\n');
- //buffer.replace(buffer.length(), 0, "\n", 1);
-
- return (buffer.length());
-}
-
-/*
- chain_append() adds delete positions to the chain that we use to keep track of space.
-*/
-int ha_tina::chain_append()
-{
- if ( chain_ptr != chain && (chain_ptr -1)->end == current_position)
- (chain_ptr -1)->end= next_position;
- else
- {
- /* We set up for the next position */
- if ((off_t)(chain_ptr - chain) == (chain_size -1))
- {
- off_t location= chain_ptr - chain;
- chain_size += DEFAULT_CHAIN_LENGTH;
- if (chain_alloced)
- {
- /* Must cast since my_malloc unlike malloc doesn't have a void ptr */
- if ((chain= (tina_set *)my_realloc((gptr)chain,chain_size,MYF(MY_WME))) == NULL)
- return -1;
- }
- else
- {
- tina_set *ptr= (tina_set *)my_malloc(chain_size * sizeof(tina_set),MYF(MY_WME));
- memcpy(ptr, chain, DEFAULT_CHAIN_LENGTH * sizeof(tina_set));
- chain= ptr;
- chain_alloced++;
- }
- chain_ptr= chain + location;
- }
- chain_ptr->begin= current_position;
- chain_ptr->end= next_position;
- chain_ptr++;
- }
-
- return 0;
-}
-
-
-/*
- Scans for a row.
-*/
-int ha_tina::find_current_row(byte *buf)
-{
- byte *mapped_ptr= (byte *)share->mapped_file + current_position;
- byte *end_ptr;
- DBUG_ENTER("ha_tina::find_current_row");
-
- /* EOF should be counted as new line */
- if ((end_ptr= find_eoln(share->mapped_file, current_position, share->file_stat.st_size)) == 0)
- DBUG_RETURN(HA_ERR_END_OF_FILE);
-
- /*
- Parse the line obtained using the following algorithm
-
- BEGIN
- 1) Store the EOL (end of line) for the current row
- 2) Until all the fields in the current query have not been
- filled
- 2.1) If the current character begins with a quote
- 2.1.1) Until EOL has not been reached
- a) If end of current field is reached, move
- to next field and jump to step 2.3
- b) If current character begins with \\ handle
- \\n, \\r, \\, \\"
- c) else append the current character into the buffer
- before checking that EOL has not been reached.
- 2.2) If the current character does not begin with a quote
- 2.2.1) Until EOL has not been reached
- a) If the end of field has been reached move to the
- next field and jump to step 2.3
- b) append the current character into the buffer
- 2.3) Store the current field value and jump to 2)
- TERMINATE
- */
-
- for (Field **field=table->field ; *field ; field++)
- {
- buffer.length(0);
- /* Handle the case where the first character begins with a quote */
- if (*mapped_ptr == '"')
- {
- /* Increment past the first quote */
- mapped_ptr++;
- /* Loop through the row to extract the values for the current field */
- for(; mapped_ptr != end_ptr; mapped_ptr++)
- {
- /* check for end of the current field */
- if (*mapped_ptr == '"' &&
- (mapped_ptr[1] == ',' || mapped_ptr == end_ptr -1 ))
- {
- /* Move past the , and the " */
- mapped_ptr += 2;
- break;
- }
- if (*mapped_ptr == '\\' && mapped_ptr != (end_ptr - 1))
- {
- mapped_ptr++;
- if (*mapped_ptr == 'r')
- buffer.append('\r');
- else if (*mapped_ptr == 'n' )
- buffer.append('\n');
- else if ((*mapped_ptr == '\\') || (*mapped_ptr == '"'))
- buffer.append(*mapped_ptr);
- else /* This could only happed with an externally created file */
- {
- buffer.append('\\');
- buffer.append(*mapped_ptr);
- }
- }
- else
- {
- /*
- If no last quote was found, but the end of row has been reached
- it implies that there has been error.
- */
- if (mapped_ptr == end_ptr -1)
- DBUG_RETURN(HA_ERR_END_OF_FILE);
- /* Store current character in the buffer for the field */
- buffer.append(*mapped_ptr);
- }
- }
- }
- else
- {
- /* Handle the case where the current row does not start with quotes */
-
- /* Loop through the row to extract the values for the current field */
- for (; mapped_ptr != end_ptr; mapped_ptr++)
- {
- /* check for end of current field */
- if (*mapped_ptr == ',')
- {
- /* Increment past the current comma */
- mapped_ptr++;
- break;
- }
- /* store the current character in the buffer for the field */
- buffer.append(*mapped_ptr);
- }
- }
- /* Store the field value from the buffer */
- (*field)->store(buffer.ptr(), buffer.length(), buffer.charset());
- }
- next_position= (end_ptr - share->mapped_file)+1;
- /* Maybe use \N for null? */
- memset(buf, 0, table->s->null_bytes); /* We do not implement nulls! */
-
- DBUG_RETURN(0);
-}
-
-/*
- If frm_error() is called in table.cc this is called to find out what file
- extensions exist for this handler.
-*/
-static const char *ha_tina_exts[] = {
- ".CSV",
- NullS
-};
-
-const char **ha_tina::bas_ext() const
-{
- return ha_tina_exts;
-}
-
-
-/*
- Open a database file. Keep in mind that tables are caches, so
- this will not be called for every request. Any sort of positions
- that need to be reset should be kept in the ::extra() call.
-*/
-int ha_tina::open(const char *name, int mode, uint test_if_locked)
-{
- DBUG_ENTER("ha_tina::open");
-
- if (!(share= get_share(name, table)))
- DBUG_RETURN(1);
- thr_lock_data_init(&share->lock,&lock,NULL);
- ref_length=sizeof(off_t);
-
- DBUG_RETURN(0);
-}
-
-
-/*
- Close a database file. We remove ourselves from the shared strucutre.
- If it is empty we destroy it and free the mapped file.
-*/
-int ha_tina::close(void)
-{
- DBUG_ENTER("ha_tina::close");
- DBUG_RETURN(free_share(share));
-}
-
-/*
- This is an INSERT. At the moment this handler just seeks to the end
- of the file and appends the data. In an error case it really should
- just truncate to the original position (this is not done yet).
-*/
-int ha_tina::write_row(byte * buf)
-{
- int size;
- DBUG_ENTER("ha_tina::write_row");
-
- statistic_increment(table->in_use->status_var.ha_write_count, &LOCK_status);
-
- if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_INSERT)
- table->timestamp_field->set_time();
-
- size= encode_quote(buf);
-
- /*
- we are going to alter the file so we must invalidate the in memory pages
- otherwise we risk a race between the in memory pages and the disk pages.
- */
- if (free_mmap(share))
- DBUG_RETURN(-1);
-
- if (my_write(share->data_file, buffer.ptr(), size, MYF(MY_WME | MY_NABP)))
- DBUG_RETURN(-1);
-
- /*
- Ok, this is means that we will be doing potentially bad things
- during a bulk insert on some OS'es. What we need is a cleanup
- call for ::write_row that would let us fix up everything after the bulk
- insert. The archive handler does this with an extra mutx call, which
- might be a solution for this.
- */
- if (get_mmap(share, 0) > 0)
- DBUG_RETURN(-1);
- records++;
- DBUG_RETURN(0);
-}
-
-
-/*
- This is called for an update.
- Make sure you put in code to increment the auto increment, also
- update any timestamp data. Currently auto increment is not being
- fixed since autoincrements have yet to be added to this table handler.
- This will be called in a table scan right before the previous ::rnd_next()
- call.
-*/
-int ha_tina::update_row(const byte * old_data, byte * new_data)
-{
- int size;
- DBUG_ENTER("ha_tina::update_row");
-
- statistic_increment(table->in_use->status_var.ha_read_rnd_next_count,
- &LOCK_status);
-
- if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_UPDATE)
- table->timestamp_field->set_time();
-
- size= encode_quote(new_data);
-
- if (chain_append())
- DBUG_RETURN(-1);
-
- /*
- we are going to alter the file so we must invalidate the in memory pages
- otherwise we risk a race between the in memory pages and the disk pages.
- */
- if (free_mmap(share))
- DBUG_RETURN(-1);
-
- if (my_write(share->data_file, buffer.ptr(), size, MYF(MY_WME | MY_NABP)))
- DBUG_RETURN(-1);
-
- /*
- Ok, this is means that we will be doing potentially bad things
- during a bulk update on some OS'es. Ideally, we should extend the length
- of the file, redo the mmap and then write all the updated rows. Upon
- finishing the bulk update, truncate the file length to the final length.
- Since this code is all being deprecated, not point now to optimize.
- */
- if (get_mmap(share, 0) > 0)
- DBUG_RETURN(-1);
-
- DBUG_RETURN(0);
-}
-
-
-/*
- Deletes a row. First the database will find the row, and then call this method.
- In the case of a table scan, the previous call to this will be the ::rnd_next()
- that found this row.
- The exception to this is an ORDER BY. This will cause the table handler to walk
- the table noting the positions of all rows that match a query. The table will
- then be deleted/positioned based on the ORDER (so RANDOM, DESC, ASC).
-*/
-int ha_tina::delete_row(const byte * buf)
-{
- DBUG_ENTER("ha_tina::delete_row");
- statistic_increment(table->in_use->status_var.ha_delete_count,&LOCK_status);
-
- if (chain_append())
- DBUG_RETURN(-1);
-
- --records;
-
- DBUG_RETURN(0);
-}
-
-/*
- Fill buf with value from key. Simply this is used for a single index read
- with a key.
-*/
-int ha_tina::index_read(byte * buf, const byte * key,
- uint key_len __attribute__((unused)),
- enum ha_rkey_function find_flag
- __attribute__((unused)))
-{
- DBUG_ENTER("ha_tina::index_read");
- DBUG_ASSERT(0);
- DBUG_RETURN(HA_ADMIN_NOT_IMPLEMENTED);
-}
-
-/*
- Fill buf with value from key. Simply this is used for a single index read
- with a key.
- Whatever the current key is we will use it. This is what will be in "index".
-*/
-int ha_tina::index_read_idx(byte * buf, uint index, const byte * key,
- uint key_len __attribute__((unused)),
- enum ha_rkey_function find_flag
- __attribute__((unused)))
-{
- DBUG_ENTER("ha_tina::index_read_idx");
- DBUG_ASSERT(0);
- DBUG_RETURN(HA_ADMIN_NOT_IMPLEMENTED);
-}
-
-
-/*
- Read the next position in the index.
-*/
-int ha_tina::index_next(byte * buf)
-{
- DBUG_ENTER("ha_tina::index_next");
- DBUG_ASSERT(0);
- DBUG_RETURN(HA_ADMIN_NOT_IMPLEMENTED);
-}
-
-/*
- Read the previous position in the index.
-*/
-int ha_tina::index_prev(byte * buf)
-{
- DBUG_ENTER("ha_tina::index_prev");
- DBUG_ASSERT(0);
- DBUG_RETURN(HA_ADMIN_NOT_IMPLEMENTED);
-}
-
-/*
- Read the first position in the index
-*/
-int ha_tina::index_first(byte * buf)
-{
- DBUG_ENTER("ha_tina::index_first");
- DBUG_ASSERT(0);
- DBUG_RETURN(HA_ADMIN_NOT_IMPLEMENTED);
-}
-
-/*
- Read the last position in the index
- With this we don't need to do a filesort() with index.
- We just read the last row and call previous.
-*/
-int ha_tina::index_last(byte * buf)
-{
- DBUG_ENTER("ha_tina::index_last");
- DBUG_ASSERT(0);
- DBUG_RETURN(HA_ADMIN_NOT_IMPLEMENTED);
-}
-
-/*
- All table scans call this first.
- The order of a table scan is:
-
- ha_tina::store_lock
- ha_tina::external_lock
- ha_tina::info
- ha_tina::rnd_init
- ha_tina::extra
- ENUM HA_EXTRA_CACHE Cash record in HA_rrnd()
- ha_tina::rnd_next
- ha_tina::rnd_next
- ha_tina::rnd_next
- ha_tina::rnd_next
- ha_tina::rnd_next
- ha_tina::rnd_next
- ha_tina::rnd_next
- ha_tina::rnd_next
- ha_tina::rnd_next
- ha_tina::extra
- ENUM HA_EXTRA_NO_CACHE End cacheing of records (def)
- ha_tina::external_lock
- ha_tina::extra
- ENUM HA_EXTRA_RESET Reset database to after open
-
- Each call to ::rnd_next() represents a row returned in the can. When no more
- rows can be returned, rnd_next() returns a value of HA_ERR_END_OF_FILE.
- The ::info() call is just for the optimizer.
-
-*/
-
-int ha_tina::rnd_init(bool scan)
-{
- DBUG_ENTER("ha_tina::rnd_init");
-
- current_position= next_position= 0;
- records= 0;
- records_is_known= 0;
- chain_ptr= chain;
-#ifdef HAVE_MADVISE
- if (scan)
- (void)madvise(share->mapped_file,share->file_stat.st_size,MADV_SEQUENTIAL);
-#endif
-
- DBUG_RETURN(0);
-}
-
-/*
- ::rnd_next() does all the heavy lifting for a table scan. You will need to populate *buf
- with the correct field data. You can walk the field to determine at what position you
- should store the data (take a look at how ::find_current_row() works). The structure
- is something like:
- 0Foo Dog Friend
- The first offset is for the first attribute. All space before that is reserved for null count.
- Basically this works as a mask for which rows are nulled (compared to just empty).
- This table handler doesn't do nulls and does not know the difference between NULL and "". This
- is ok since this table handler is for spreadsheets and they don't know about them either :)
-*/
-int ha_tina::rnd_next(byte *buf)
-{
- DBUG_ENTER("ha_tina::rnd_next");
-
- statistic_increment(table->in_use->status_var.ha_read_rnd_next_count,
- &LOCK_status);
-
- current_position= next_position;
- if (!share->mapped_file)
- DBUG_RETURN(HA_ERR_END_OF_FILE);
- if (HA_ERR_END_OF_FILE == find_current_row(buf) )
- DBUG_RETURN(HA_ERR_END_OF_FILE);
-
- records++;
- DBUG_RETURN(0);
-}
-
-/*
- In the case of an order by rows will need to be sorted.
- ::position() is called after each call to ::rnd_next(),
- the data it stores is to a byte array. You can store this
- data via my_store_ptr(). ref_length is a variable defined to the
- class that is the sizeof() of position being stored. In our case
- its just a position. Look at the bdb code if you want to see a case
- where something other then a number is stored.
-*/
-void ha_tina::position(const byte *record)
-{
- DBUG_ENTER("ha_tina::position");
- my_store_ptr(ref, ref_length, current_position);
- DBUG_VOID_RETURN;
-}
-
-
-/*
- Used to fetch a row from a posiion stored with ::position().
- my_get_ptr() retrieves the data for you.
-*/
-
-int ha_tina::rnd_pos(byte * buf, byte *pos)
-{
- DBUG_ENTER("ha_tina::rnd_pos");
- statistic_increment(table->in_use->status_var.ha_read_rnd_next_count,
- &LOCK_status);
- current_position= my_get_ptr(pos,ref_length);
- DBUG_RETURN(find_current_row(buf));
-}
-
-/*
- ::info() is used to return information to the optimizer.
- Currently this table handler doesn't implement most of the fields
- really needed. SHOW also makes use of this data
-*/
-int ha_tina::info(uint flag)
-{
- DBUG_ENTER("ha_tina::info");
- /* This is a lie, but you don't want the optimizer to see zero or 1 */
- if (!records_is_known && records < 2)
- records= 2;
- DBUG_RETURN(0);
-}
-
-/*
- Grab bag of flags that are sent to the able handler every so often.
- HA_EXTRA_RESET and HA_EXTRA_RESET_STATE are the most frequently called.
- You are not required to implement any of these.
-*/
-int ha_tina::extra(enum ha_extra_function operation)
-{
- DBUG_ENTER("ha_tina::extra");
- DBUG_RETURN(0);
-}
-
-/*
- This is no longer used.
-*/
-int ha_tina::reset(void)
-{
- DBUG_ENTER("ha_tina::reset");
- ha_tina::extra(HA_EXTRA_RESET);
- DBUG_RETURN(0);
-}
-
-
-/*
- Called after deletes, inserts, and updates. This is where we clean up all of
- the dead space we have collected while writing the file.
-*/
-int ha_tina::rnd_end()
-{
- DBUG_ENTER("ha_tina::rnd_end");
-
- records_is_known= 1;
-
- /* First position will be truncate position, second will be increment */
- if ((chain_ptr - chain) > 0)
- {
- tina_set *ptr;
- off_t length;
-
- /*
- Setting up writable map, this will contain all of the data after the
- get_mmap call that we have added to the file.
- */
- if (get_mmap(share, 1) > 0)
- DBUG_RETURN(-1);
- length= share->file_stat.st_size;
-
- /*
- The sort handles updates/deletes with random orders.
- It also sorts so that we move the final blocks to the
- beginning so that we move the smallest amount of data possible.
- */
- my_qsort(chain, (size_t)(chain_ptr - chain), sizeof(tina_set), (qsort_cmp)sort_set);
- for (ptr= chain; ptr < chain_ptr; ptr++)
- {
- memmove(share->mapped_file + ptr->begin, share->mapped_file + ptr->end,
- length - (size_t)ptr->end);
- length= length - (size_t)(ptr->end - ptr->begin);
- }
-
- /* Invalidate all cached mmap pages */
- if (free_mmap(share))
- DBUG_RETURN(-1);
-
- /* Truncate the file to the new size */
- if (my_chsize(share->data_file, length, 0, MYF(MY_WME)))
- DBUG_RETURN(-1);
-
- if (get_mmap(share, 0) > 0)
- DBUG_RETURN(-1);
- }
-
- DBUG_RETURN(0);
-}
-
-/*
- DELETE without WHERE calls it
-*/
-int ha_tina::delete_all_rows()
-{
- DBUG_ENTER("ha_tina::delete_all_rows");
-
- if (!records_is_known)
- DBUG_RETURN(my_errno=HA_ERR_WRONG_COMMAND);
-
- /* Invalidate all cached mmap pages */
- if (free_mmap(share))
- DBUG_RETURN(-1);
-
- int rc= my_chsize(share->data_file, 0, 0, MYF(MY_WME));
-
- if (get_mmap(share, 0) > 0)
- DBUG_RETURN(-1);
-
- records=0;
- DBUG_RETURN(rc);
-}
-
-/*
- Always called by the start of a transaction (or by "lock tables");
-*/
-int ha_tina::external_lock(THD *thd, int lock_type)
-{
- DBUG_ENTER("ha_tina::external_lock");
- DBUG_RETURN(0); // No external locking
-}
-
-/*
- Called by the database to lock the table. Keep in mind that this
- is an internal lock.
-*/
-THR_LOCK_DATA **ha_tina::store_lock(THD *thd,
- THR_LOCK_DATA **to,
- enum thr_lock_type lock_type)
-{
- if (lock_type != TL_IGNORE && lock.type == TL_UNLOCK)
- lock.type=lock_type;
- *to++= &lock;
- return to;
-}
-
-/*
- Create a table. You do not want to leave the table open after a call to
- this (the database will call ::open() if it needs to).
-*/
-
-int ha_tina::create(const char *name, TABLE *table_arg, HA_CREATE_INFO *create_info)
-{
- char name_buff[FN_REFLEN];
- File create_file;
- DBUG_ENTER("ha_tina::create");
-
- if ((create_file= my_create(fn_format(name_buff,name,"",".CSV",MY_REPLACE_EXT|MY_UNPACK_FILENAME),0,
- O_RDWR | O_TRUNC,MYF(MY_WME))) < 0)
- DBUG_RETURN(-1);
-
- my_close(create_file,MYF(0));
-
- DBUG_RETURN(0);
-}
-
-#endif /* enable CSV */
diff --git a/sql/examples/ha_tina.h b/sql/examples/ha_tina.h
deleted file mode 100644
index 98cba8bf4cd..00000000000
--- a/sql/examples/ha_tina.h
+++ /dev/null
@@ -1,124 +0,0 @@
-/* Copyright (C) 2003 MySQL AB
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; version 2 of the License.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
-
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <my_dir.h>
-
-#define DEFAULT_CHAIN_LENGTH 512
-
-typedef struct st_tina_share {
- char *table_name;
- byte *mapped_file; /* mapped region of file */
- uint table_name_length,use_count;
- MY_STAT file_stat; /* Stat information for the data file */
- File data_file; /* Current open data file */
- pthread_mutex_t mutex;
- THR_LOCK lock;
-} TINA_SHARE;
-
-typedef struct tina_set {
- off_t begin;
- off_t end;
-};
-
-class ha_tina: public handler
-{
- THR_LOCK_DATA lock; /* MySQL lock */
- TINA_SHARE *share; /* Shared lock info */
- off_t current_position; /* Current position in the file during a file scan */
- off_t next_position; /* Next position in the file scan */
- byte byte_buffer[IO_SIZE];
- String buffer;
- tina_set chain_buffer[DEFAULT_CHAIN_LENGTH];
- tina_set *chain;
- tina_set *chain_ptr;
- byte chain_alloced;
- uint32 chain_size;
- bool records_is_known;
-
-public:
- ha_tina(TABLE *table_arg);
- ~ha_tina()
- {
- if (chain_alloced)
- my_free((gptr)chain,0);
- }
- const char *table_type() const { return "CSV"; }
- const char *index_type(uint inx) { return "NONE"; }
- const char **bas_ext() const;
- ulong table_flags() const
- {
- return (HA_REC_NOT_IN_SEQ | HA_NOT_EXACT_COUNT |
- HA_NO_AUTO_INCREMENT );
- }
- ulong index_flags(uint idx, uint part, bool all_parts) const
- {
- /* We will never have indexes so this will never be called(AKA we return zero) */
- return 0;
- }
- uint max_record_length() const { return HA_MAX_REC_LENGTH; }
- uint max_keys() const { return 0; }
- uint max_key_parts() const { return 0; }
- uint max_key_length() const { return 0; }
- /*
- Called in test_quick_select to determine if indexes should be used.
- */
- virtual double scan_time() { return (double) (records+deleted) / 20.0+10; }
- /* The next method will never be called */
- virtual bool fast_key_read() { return 1;}
- /*
- TODO: return actual upper bound of number of records in the table.
- (e.g. save number of records seen on full table scan and/or use file size
- as upper bound)
- */
- ha_rows estimate_rows_upper_bound() { return HA_POS_ERROR; }
-
- int open(const char *name, int mode, uint test_if_locked);
- int close(void);
- int write_row(byte * buf);
- int update_row(const byte * old_data, byte * new_data);
- int delete_row(const byte * buf);
- int index_read(byte * buf, const byte * key,
- uint key_len, enum ha_rkey_function find_flag);
- int index_read_idx(byte * buf, uint idx, const byte * key,
- uint key_len, enum ha_rkey_function find_flag);
- int index_next(byte * buf);
- int index_prev(byte * buf);
- int index_first(byte * buf);
- int index_last(byte * buf);
- int rnd_init(bool scan=1);
- int rnd_next(byte *buf);
- int rnd_pos(byte * buf, byte *pos);
- int rnd_end();
- void position(const byte *record);
- int info(uint);
- int extra(enum ha_extra_function operation);
- int reset(void);
- int external_lock(THD *thd, int lock_type);
- int delete_all_rows(void);
- int create(const char *name, TABLE *form, HA_CREATE_INFO *create_info);
-
- THR_LOCK_DATA **store_lock(THD *thd, THR_LOCK_DATA **to,
- enum thr_lock_type lock_type);
-
- /* The following methods were added just for TINA */
- int encode_quote(byte *buf);
- int find_current_row(byte *buf);
- int chain_append();
-};
-
-bool tina_end();
-
diff --git a/sql/field.cc b/sql/field.cc
index f8ab4b852ec..973170223e4 100644
--- a/sql/field.cc
+++ b/sql/field.cc
@@ -1,4 +1,4 @@
-/* Copyright (C) 2000-2006 MySQL AB
+/* Copyright 2000-2008 MySQL AB, 2008 Sun Microsystems, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -14,9 +14,12 @@
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
-/*****************************************************************************
-** This file implements classes defined in field.h
-*****************************************************************************/
+/**
+ @file
+
+ @brief
+ This file implements classes defined in field.h
+*/
#ifdef USE_PRAGMA_IMPLEMENTATION
#pragma implementation // gcc: Class implementation
@@ -24,6 +27,8 @@
#include "mysql_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 <m_ctype.h>
#include <errno.h>
#ifdef HAVE_FCONVERT
@@ -38,8 +43,8 @@
*****************************************************************************/
#ifdef HAVE_EXPLICIT_TEMPLATE_INSTANTIATION
-template class List<create_field>;
-template class List_iterator<create_field>;
+template class List<Create_field>;
+template class List_iterator<Create_field>;
#endif
uchar Field_null::null[1]={1};
@@ -51,6 +56,9 @@ const char field_separator=',';
#define BLOB_PACK_LENGTH_TO_MAX_LENGH(arg) \
((ulong) ((LL(1) << min(arg, 4) * 8) - LL(1)))
+#define ASSERT_COLUMN_MARKED_FOR_READ DBUG_ASSERT(!table || (!table->read_set || bitmap_is_set(table->read_set, field_index)))
+#define ASSERT_COLUMN_MARKED_FOR_WRITE DBUG_ASSERT(!table || (!table->write_set || bitmap_is_set(table->write_set, field_index)))
+
/*
Rules for merging different types of fields in UNION
@@ -68,6 +76,7 @@ inline int field_type2index (enum_field_types field_type)
((int)FIELDTYPE_TEAR_FROM) + (field_type - FIELDTYPE_TEAR_TO) - 1);
}
+
static enum_field_types field_types_merge_rules [FIELDTYPE_NUM][FIELDTYPE_NUM]=
{
/* MYSQL_TYPE_DECIMAL -> */
@@ -909,14 +918,13 @@ static enum_field_types field_types_merge_rules [FIELDTYPE_NUM][FIELDTYPE_NUM]=
}
};
-/*
- Return type of which can carry value of both given types in UNION result
+/**
+ Return type of which can carry value of both given types in UNION result.
- SYNOPSIS
- Field::field_type_merge()
- a, b types for merging
+ @param a type for merging
+ @param b type for merging
- RETURN
+ @return
type of field
*/
@@ -988,14 +996,12 @@ test_if_important_data(CHARSET_INFO *cs, const char *str, const char *strend)
}
-/*
- Detect Item_result by given field type of UNION merge result
+/**
+ Detect Item_result by given field type of UNION merge result.
- SYNOPSIS
- Field::result_merge_type()
- field_type given field type
+ @param field_type given field type
- RETURN
+ @return
Item_result (type of internal MySQL expression result)
*/
@@ -1011,18 +1017,17 @@ Item_result Field::result_merge_type(enum_field_types field_type)
*****************************************************************************/
-/*
- Check whether a field type can be partially indexed by a key
+/**
+ Check whether a field type can be partially indexed by a key.
This is a static method, rather than a virtual function, because we need
to check the type of a non-Field in mysql_alter_table().
- SYNOPSIS
- type_can_have_key_part()
- type field type
+ @param type field type
- RETURN
+ @retval
TRUE Type can have a prefixed key
+ @retval
FALSE Type can not have a prefixed key
*/
@@ -1044,16 +1049,15 @@ bool Field::type_can_have_key_part(enum enum_field_types type)
}
-/*
- Numeric fields base class constructor
+/**
+ Numeric fields base class constructor.
*/
-Field_num::Field_num(char *ptr_arg,uint32 len_arg, uchar *null_ptr_arg,
+Field_num::Field_num(uchar *ptr_arg,uint32 len_arg, uchar *null_ptr_arg,
uchar null_bit_arg, utype unireg_check_arg,
const char *field_name_arg,
- struct st_table *table_arg,
uint8 dec_arg, bool zero_arg, bool unsigned_arg)
:Field(ptr_arg, len_arg, null_ptr_arg, null_bit_arg,
- unireg_check_arg, field_name_arg, table_arg),
+ unireg_check_arg, field_name_arg),
dec(dec_arg),zerofill(zero_arg),unsigned_flag(unsigned_arg)
{
if (zerofill)
@@ -1068,31 +1072,34 @@ void Field_num::prepend_zeros(String *value)
int diff;
if ((diff= (int) (field_length - value->length())) > 0)
{
- bmove_upp((char*) value->ptr()+field_length,value->ptr()+value->length(),
+ bmove_upp((uchar*) value->ptr()+field_length,
+ (uchar*) value->ptr()+value->length(),
value->length());
- bfill((char*) value->ptr(),diff,'0');
+ bfill((uchar*) value->ptr(),diff,'0');
value->length(field_length);
(void) value->c_ptr_quick(); // Avoid warnings in purify
}
}
-/*
+/**
Test if given number is a int.
- SYNOPSIS
- Field_num::check_int
- cs Character set
- str String to test
- end Pointer to char after last used digit
- length String length
- error Error returned by strntoull10rnd()
+ @todo
+ Make this multi-byte-character safe
- NOTE
+ @param str String to test
+ @param length Length of 'str'
+ @param int_end Pointer to char after last used digit
+ @param cs Character set
+
+ @note
This is called after one has called strntoull10rnd() function.
- RETURN
- 0 ok
+ @retval
+ 0 OK
+ @retval
1 error: empty string or wrong integer.
+ @retval
2 error: garbage at the end of string.
*/
@@ -1152,7 +1159,8 @@ bool Field_num::get_int(CHARSET_INFO *cs, const char *from, uint len,
char *end;
int error;
- *rnd= (longlong) cs->cset->strntoull10rnd(cs, from, len, unsigned_flag, &end,
+ *rnd= (longlong) cs->cset->strntoull10rnd(cs, from, len,
+ unsigned_flag, &end,
&error);
if (unsigned_flag)
{
@@ -1176,7 +1184,8 @@ bool Field_num::get_int(CHARSET_INFO *cs, const char *from, uint len,
goto out_of_range;
}
}
- if (table->in_use->count_cuted_fields && check_int(cs, from, len, end, error))
+ if (table->in_use->count_cuted_fields &&
+ check_int(cs, from, len, end, error))
return 1;
return 0;
@@ -1185,16 +1194,15 @@ out_of_range:
return 1;
}
-/*
+/**
Process decimal library return codes and issue warnings for overflow and
truncation.
- SYNOPSIS
- Field::warn_if_overflow()
- op_result decimal library return code (E_DEC_* see include/decimal.h)
+ @param op_result decimal library return code (E_DEC_* see include/decimal.h)
- RETURN
+ @retval
1 there was overflow
+ @retval
0 no error or some other errors except overflow
*/
@@ -1269,17 +1277,19 @@ static bool test_if_real(const char *str,int length, CHARSET_INFO *cs)
#endif
-/*
+/**
Interpret field value as an integer but return the result as a string.
- This is used for printing bit_fields as numbers while debugging
+ This is used for printing bit_fields as numbers while debugging.
*/
String *Field::val_int_as_str(String *val_buffer, my_bool unsigned_val)
{
+ ASSERT_COLUMN_MARKED_FOR_READ;
CHARSET_INFO *cs= &my_charset_bin;
uint length;
longlong value= val_int();
+
if (val_buffer->alloc(MY_INT64_NUM_DECIMAL_DIGITS))
return 0;
length= (uint) (*cs->cset->longlong10_to_str)(cs, (char*) val_buffer->ptr(),
@@ -1291,30 +1301,46 @@ String *Field::val_int_as_str(String *val_buffer, my_bool unsigned_val)
}
-/* This is used as a table name when the table structure is not set up */
-const char *unknown_table_name= 0;
-
-Field::Field(char *ptr_arg,uint32 length_arg,uchar *null_ptr_arg,
+/// This is used as a table name when the table structure is not set up
+Field::Field(uchar *ptr_arg,uint32 length_arg,uchar *null_ptr_arg,
uchar null_bit_arg,
- utype unireg_check_arg, const char *field_name_arg,
- struct st_table *table_arg)
+ utype unireg_check_arg, const char *field_name_arg)
:ptr(ptr_arg), null_ptr(null_ptr_arg),
- table(table_arg),orig_table(table_arg),
- table_name(table_arg ? &table_arg->alias : &unknown_table_name),
+ table(0), orig_table(0), table_name(0),
field_name(field_name_arg),
- query_id(0), key_start(0), part_of_key(0), part_of_sortkey(0),
- unireg_check(unireg_check_arg),
+ key_start(0), part_of_key(0), part_of_key_not_clustered(0),
+ part_of_sortkey(0), unireg_check(unireg_check_arg),
field_length(length_arg), null_bit(null_bit_arg),
is_created_from_null_item(FALSE)
{
flags=null_ptr ? 0: NOT_NULL_FLAG;
comment.str= (char*) "";
comment.length=0;
+ field_index= 0;
+}
+
+
+void Field::hash(ulong *nr, ulong *nr2)
+{
+ if (is_null())
+ {
+ *nr^= (*nr << 1) | 1;
+ }
+ else
+ {
+ uint len= pack_length();
+ CHARSET_INFO *cs= charset();
+ cs->coll->hash_sort(cs, ptr, len, nr, nr2);
+ }
}
-uint Field::offset()
+size_t
+Field::do_last_null_byte() const
{
- return (uint) (ptr - (char*) table->record[0]);
+ DBUG_ASSERT(null_ptr == NULL || null_ptr >= table->record[0]);
+ if (null_ptr)
+ return (size_t) (null_ptr - table->record[0]) + 1;
+ return LAST_NULL_BYTE_UNDEF;
}
@@ -1338,6 +1364,149 @@ 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).
+
+ @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::compatible_field_size(uint field_metadata,
+ const Relay_log_info *rli_arg __attribute__((unused)))
+{
+ uint const source_size= pack_length_from_metadata(field_metadata);
+ uint const destination_size= row_pack_length();
+ return (source_size <= destination_size);
+}
+
+
+int Field::store(const char *to, uint length, CHARSET_INFO *cs,
+ enum_check_fields check_level)
+{
+ int res;
+ enum_check_fields old_check_level= table->in_use->count_cuted_fields;
+ table->in_use->count_cuted_fields= check_level;
+ res= store(to, length, cs);
+ table->in_use->count_cuted_fields= old_check_level;
+ return res;
+}
+
+
+/**
+ Pack the field into a format suitable for storage and transfer.
+
+ To implement packing functionality, only the virtual function
+ should be overridden. The other functions are just convenience
+ functions and hence should not be overridden.
+
+ The value of <code>low_byte_first</code> is dependent on how the
+ packed data is going to be used: for local use, e.g., temporary
+ store on disk or in memory, use the native format since that is
+ faster. For data that is going to be transfered to other machines
+ (e.g., when writing data to the binary log), data should always be
+ stored in little-endian format.
+
+ @note The default method for packing fields just copy the raw bytes
+ of the record into the destination, but never more than
+ <code>max_length</code> characters.
+
+ @param to
+ Pointer to memory area where representation of field should be put.
+
+ @param from
+ Pointer to memory area where record representation of field is
+ stored.
+
+ @param max_length
+ Maximum length of the field, as given in the column definition. For
+ example, for <code>CHAR(1000)</code>, the <code>max_length</code>
+ is 1000. This information is sometimes needed to decide how to pack
+ the data.
+
+ @param low_byte_first
+ @c TRUE if integers should be stored little-endian, @c FALSE if
+ native format should be used. Note that for little-endian machines,
+ the value of this flag is a moot point since the native format is
+ little-endian.
+*/
+uchar *
+Field::pack(uchar *to, const uchar *from, uint max_length,
+ bool low_byte_first __attribute__((unused)))
+{
+ uint32 length= pack_length();
+ set_if_smaller(length, max_length);
+ memcpy(to, from, length);
+ return to+length;
+}
+
+/**
+ Unpack a field from row data.
+
+ This method is used to unpack a field from a master whose size of
+ the field is less than that of the slave.
+
+ The <code>param_data</code> parameter is a two-byte integer (stored
+ in the least significant 16 bits of the unsigned integer) usually
+ consisting of two parts: the real type in the most significant byte
+ and a original pack length in the least significant byte.
+
+ The exact layout of the <code>param_data</code> field is given by
+ the <code>Table_map_log_event::save_field_metadata()</code>.
+
+ This is the default method for unpacking a field. It just copies
+ the memory block in byte order (of original pack length bytes or
+ length of field, whichever is smaller).
+
+ @param to Destination of the data
+ @param from Source of the data
+ @param param_data Real type and original pack length of the field
+ data
+
+ @param low_byte_first
+ If this flag is @c true, all composite entities (e.g., lengths)
+ should be unpacked in little-endian format; otherwise, the entities
+ are unpacked in native order.
+
+ @return New pointer into memory based on from + length of the data
+*/
+const uchar *
+Field::unpack(uchar* to, const uchar *from, uint param_data,
+ bool low_byte_first __attribute__((unused)))
+{
+ uint length=pack_length();
+ int from_type= 0;
+ /*
+ If from length is > 255, it has encoded data in the upper bits. Need
+ to mask it out.
+ */
+ if (param_data > 255)
+ {
+ from_type= (param_data & 0xff00) >> 8U; // real_type.
+ param_data= param_data & 0x00ff; // length.
+ }
+
+ if ((param_data == 0) ||
+ (length == param_data) ||
+ (from_type != real_type()))
+ {
+ memcpy(to, from, length);
+ return from+length;
+ }
+
+ uint len= (param_data && (param_data < length)) ?
+ param_data : length;
+
+ memcpy(to, from, param_data > length ? length : len);
+ return from+len;
+}
+
+
my_decimal *Field::val_decimal(my_decimal *decimal)
{
/* This never have to be called */
@@ -1357,11 +1526,10 @@ void Field_num::add_zerofill_and_unsigned(String &res) const
void Field::make_field(Send_field *field)
{
- if (orig_table && orig_table->s->table_cache_key &&
- *(orig_table->s->table_cache_key))
+ if (orig_table && orig_table->s->db.str && *orig_table->s->db.str)
{
- field->org_table_name= orig_table->s->table_name;
- field->db_name= orig_table->s->table_cache_key;
+ field->db_name= orig_table->s->db.str;
+ field->org_table_name= orig_table->s->table_name.str;
}
else
field->org_table_name= field->db_name= "";
@@ -1384,17 +1552,15 @@ void Field::make_field(Send_field *field)
}
-/*
+/**
Conversion from decimal to longlong with checking overflow and
- setting correct value (min/max) in case of overflow
+ setting correct value (min/max) in case of overflow.
- SYNOPSIS
- Field::convert_decimal2longlong()
- val value which have to be converted
- unsigned_flag type of integer in which we convert val
- err variable to pass error code
+ @param val value which have to be converted
+ @param unsigned_flag type of integer in which we convert val
+ @param err variable to pass error code
- RETURN
+ @return
value converted from val
*/
longlong Field::convert_decimal2longlong(const my_decimal *val,
@@ -1428,47 +1594,46 @@ longlong Field::convert_decimal2longlong(const my_decimal *val,
}
-/*
+/**
Storing decimal in integer fields.
- SYNOPSIS
- Field_num::store_decimal()
- val value for storing
+ @param val value for storing
- NOTE
+ @note
This method is used by all integer fields, real/decimal redefine it
- RETURN
+ @retval
0 OK
- != 0 error
+ @retval
+ !=0 error
*/
int Field_num::store_decimal(const my_decimal *val)
{
+ ASSERT_COLUMN_MARKED_FOR_WRITE;
int err= 0;
longlong i= convert_decimal2longlong(val, unsigned_flag, &err);
return test(err | store(i, unsigned_flag));
}
-/*
- Return decimal value of integer field
+/**
+ Return decimal value of integer field.
- SYNOPSIS
- Field_num::val_decimal()
- decimal_value buffer for storing decimal value
+ @param decimal_value buffer for storing decimal value
- NOTE
- This method is used by all integer fields, real/decimal redefine it
+ @note
+ This method is used by all integer fields, real/decimal redefine it.
All longlong values fit in our decimal buffer which cal store 8*9=72
digits of integer number
- RETURN
+ @return
pointer to decimal buffer with value of field
*/
my_decimal* Field_num::val_decimal(my_decimal *decimal_value)
{
+ ASSERT_COLUMN_MARKED_FOR_READ;
DBUG_ASSERT(result_type() == INT_RESULT);
longlong nr= val_int();
int2my_decimal(E_DEC_FATAL_ERROR, nr, unsigned_flag, decimal_value);
@@ -1476,12 +1641,11 @@ my_decimal* Field_num::val_decimal(my_decimal *decimal_value)
}
-Field_str::Field_str(char *ptr_arg,uint32 len_arg, uchar *null_ptr_arg,
+Field_str::Field_str(uchar *ptr_arg,uint32 len_arg, uchar *null_ptr_arg,
uchar null_bit_arg, utype unireg_check_arg,
- const char *field_name_arg,
- struct st_table *table_arg, CHARSET_INFO *charset_arg)
+ const char *field_name_arg, CHARSET_INFO *charset_arg)
:Field(ptr_arg, len_arg, null_ptr_arg, null_bit_arg,
- unireg_check_arg, field_name_arg, table_arg)
+ unireg_check_arg, field_name_arg)
{
field_charset= charset_arg;
if (charset_arg->state & MY_CS_BINSORT)
@@ -1496,26 +1660,29 @@ void Field_num::make_field(Send_field *field)
field->decimals= dec;
}
-/*
- Decimal representation of Field_str
+/**
+ Decimal representation of Field_str.
- SYNOPSIS
- Field_str::store_decimal()
- d value for storing
+ @param d value for storing
- NOTE
- Field_str is the base class for fields like Field_enum, Field_date and some
- similar. Some dates use fraction and also string value should be
- converted to floating point value according our rules, so we use double
- to store value of decimal in string
+ @note
+ Field_str is the base class for fields like Field_enum,
+ Field_date and some similar. Some dates use fraction and also
+ string value should be converted to floating point value according
+ our rules, so we use double to store value of decimal in string.
- RETURN
+ @todo
+ use decimal2string?
+
+ @retval
0 OK
- != 0 error
+ @retval
+ !=0 error
*/
int Field_str::store_decimal(const my_decimal *d)
{
+ ASSERT_COLUMN_MARKED_FOR_WRITE;
double val;
/* TODO: use decimal2string? */
int err= warn_if_overflow(my_decimal2double(E_DEC_FATAL_ERROR &
@@ -1526,6 +1693,7 @@ int Field_str::store_decimal(const my_decimal *d)
my_decimal *Field_str::val_decimal(my_decimal *decimal_value)
{
+ ASSERT_COLUMN_MARKED_FOR_READ;
longlong nr= val_int();
int2my_decimal(E_DEC_FATAL_ERROR, nr, 0, decimal_value);
return decimal_value;
@@ -1582,15 +1750,16 @@ bool Field::get_time(MYSQL_TIME *ltime)
return 0;
}
-/*
- This is called when storing a date in a string
+/**
+ This is called when storing a date in a string.
- NOTES
- Needs to be changed if/when we want to support different time formats
+ @note
+ Needs to be changed if/when we want to support different time formats.
*/
int Field::store_time(MYSQL_TIME *ltime, timestamp_type type_arg)
{
+ ASSERT_COLUMN_MARKED_FOR_WRITE;
char buff[MAX_DATE_STRING_REP_LENGTH];
uint length= (uint) my_TIME_to_str(ltime, buff);
return store(buff, length, &my_charset_bin);
@@ -1616,7 +1785,7 @@ Field *Field::new_field(MEM_ROOT *root, struct st_table *new_table,
tmp->key_start.init(0);
tmp->part_of_key.init(0);
tmp->part_of_sortkey.init(0);
- tmp->unireg_check=Field::NONE;
+ tmp->unireg_check= Field::NONE;
tmp->flags&= (NOT_NULL_FLAG | BLOB_FLAG | UNSIGNED_FLAG |
ZEROFILL_FLAG | BINARY_FLAG | ENUM_FLAG | SET_FLAG);
tmp->reset_fields();
@@ -1625,7 +1794,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,
- char *new_ptr, uchar *new_null_ptr,
+ uchar *new_ptr, uchar *new_null_ptr,
uint new_null_bit)
{
Field *tmp;
@@ -1639,6 +1808,21 @@ 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 *tmp;
+ if ((tmp= (Field*) memdup_root(root,(char*) this,size_of())))
+ {
+ tmp->init(new_table);
+ tmp->move_field_offset((my_ptrdiff_t) (new_table->record[0] -
+ new_table->s->default_values));
+ }
+ return tmp;
+}
+
+
/****************************************************************************
Field_null, a field that always return NULL
****************************************************************************/
@@ -1664,7 +1848,7 @@ Field_decimal::reset(void)
void Field_decimal::overflow(bool negative)
{
uint len=field_length;
- char *to=ptr, filler= '9';
+ uchar *to=ptr, filler= '9';
set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
if (negative)
@@ -1700,35 +1884,37 @@ void Field_decimal::overflow(bool negative)
}
-int Field_decimal::store(const char *from, uint len, CHARSET_INFO *cs)
+int Field_decimal::store(const char *from_arg, uint len, CHARSET_INFO *cs)
{
+ ASSERT_COLUMN_MARKED_FOR_WRITE;
char buff[STRING_BUFFER_USUAL_SIZE];
String tmp(buff,sizeof(buff), &my_charset_bin);
+ const uchar *from= (uchar*) from_arg;
- /* Convert character set if the old one is multi byte */
+ /* Convert character set if the old one is multi uchar */
if (cs->mbmaxlen > 1)
{
uint dummy_errors;
- tmp.copy(from, len, cs, &my_charset_bin, &dummy_errors);
- from= tmp.ptr();
+ tmp.copy((char*) from, len, cs, &my_charset_bin, &dummy_errors);
+ from= (uchar*) tmp.ptr();
len= tmp.length();
}
- const char *end= from+len;
+ const uchar *end= from+len;
/* The pointer where the field value starts (i.e., "where to write") */
- char *to=ptr;
+ uchar *to= ptr;
uint tmp_dec, tmp_uint;
/*
The sign of the number : will be 0 (means positive but sign not
specified), '+' or '-'
*/
- char sign_char=0;
+ uchar sign_char=0;
/* The pointers where prezeros start and stop */
- const char *pre_zeros_from, *pre_zeros_end;
+ const uchar *pre_zeros_from, *pre_zeros_end;
/* The pointers where digits at the left of '.' start and stop */
- const char *int_digits_from, *int_digits_end;
+ const uchar *int_digits_from, *int_digits_end;
/* The pointers where digits at the right of '.' start and stop */
- const char *frac_digits_from, *frac_digits_end;
+ const uchar *frac_digits_from, *frac_digits_end;
/* The sign of the exponent : will be 0 (means no exponent), '+' or '-' */
char expo_sign_char=0;
uint exponent=0; // value of the exponent
@@ -1736,20 +1922,20 @@ int Field_decimal::store(const char *from, uint len, CHARSET_INFO *cs)
Pointers used when digits move from the left of the '.' to the
right of the '.' (explained below)
*/
- const char *int_digits_tail_from;
+ const uchar *int_digits_tail_from;
/* Number of 0 that need to be added at the left of the '.' (1E3: 3 zeros) */
uint int_digits_added_zeros;
/*
Pointer used when digits move from the right of the '.' to the left
of the '.'
*/
- const char *frac_digits_head_end;
+ const uchar *frac_digits_head_end;
/* Number of 0 that need to be added at the right of the '.' (for 1E-3) */
uint frac_digits_added_zeros;
- char *pos,*tmp_left_pos,*tmp_right_pos;
+ uchar *pos,*tmp_left_pos,*tmp_right_pos;
/* Pointers that are used as limits (begin and end of the field buffer) */
- char *left_wall,*right_wall;
- char tmp_char;
+ uchar *left_wall,*right_wall;
+ uchar tmp_char;
/*
To remember if table->in_use->cuted_fields has already been incremented,
to do that only once
@@ -2071,31 +2257,31 @@ int Field_decimal::store(const char *from, uint len, CHARSET_INFO *cs)
int Field_decimal::store(double nr)
{
+ ASSERT_COLUMN_MARKED_FOR_WRITE;
if (unsigned_flag && nr < 0)
{
overflow(1);
return 1;
}
-#ifdef HAVE_FINITE
- if (!finite(nr)) // Handle infinity as special case
+ if (!isfinite(nr)) // Handle infinity as special case
{
overflow(nr < 0.0);
return 1;
}
-#endif
- reg4 uint i,length;
- char fyllchar,*to;
+ reg4 uint i;
+ size_t length;
+ uchar fyllchar,*to;
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=(uint) strlen(buff);
+ length= strlen(buff);
#else
- length=(uint) my_sprintf(buff,(buff,"%.*f",dec,nr));
+ length= my_sprintf(buff,(buff,"%.*f",dec,nr));
#endif
if (length > field_length)
@@ -2116,9 +2302,11 @@ int Field_decimal::store(double nr)
int Field_decimal::store(longlong nr, bool unsigned_val)
{
+ ASSERT_COLUMN_MARKED_FOR_WRITE;
char buff[22];
uint length, int_part;
- char fyllchar, *to;
+ char fyllchar;
+ uchar *to;
if (nr < 0 && unsigned_flag && !unsigned_val)
{
@@ -2150,31 +2338,35 @@ int Field_decimal::store(longlong nr, bool unsigned_val)
double Field_decimal::val_real(void)
{
+ ASSERT_COLUMN_MARKED_FOR_READ;
int not_used;
char *end_not_used;
- return my_strntod(&my_charset_bin, ptr, field_length, &end_not_used,
+ return my_strntod(&my_charset_bin, (char*) ptr, field_length, &end_not_used,
&not_used);
}
longlong Field_decimal::val_int(void)
{
+ ASSERT_COLUMN_MARKED_FOR_READ;
int not_used;
if (unsigned_flag)
- return my_strntoull(&my_charset_bin, ptr, field_length, 10, NULL,
- &not_used);
- else
- return my_strntoll(&my_charset_bin, ptr, field_length, 10, NULL,
+ return my_strntoull(&my_charset_bin, (char*) ptr, field_length, 10, NULL,
&not_used);
+ return my_strntoll(&my_charset_bin, (char*) ptr, field_length, 10, NULL,
+ &not_used);
}
String *Field_decimal::val_str(String *val_buffer __attribute__((unused)),
String *val_ptr)
{
- char *str;
+ ASSERT_COLUMN_MARKED_FOR_READ;
+ uchar *str;
+ size_t tmp_length;
+
for (str=ptr ; *str == ' ' ; str++) ;
- uint tmp_length=(uint) (str-ptr);
val_ptr->set_charset(&my_charset_bin);
+ tmp_length= (size_t) (str-ptr);
if (field_length < tmp_length) // Error in data
val_ptr->length(0);
else
@@ -2182,14 +2374,14 @@ String *Field_decimal::val_str(String *val_buffer __attribute__((unused)),
return val_ptr;
}
-/*
-** Should be able to handle at least the following fixed decimal formats:
-** 5.00 , -1.0, 05, -05, +5 with optional pre/end space
+/**
+ Should be able to handle at least the following fixed decimal formats:
+ 5.00 , -1.0, 05, -05, +5 with optional pre/end space
*/
-int Field_decimal::cmp(const char *a_ptr,const char *b_ptr)
+int Field_decimal::cmp(const uchar *a_ptr,const uchar *b_ptr)
{
- const char *end;
+ const uchar *end;
int swap=0;
/* First remove prefixes '0', ' ', and '-' */
for (end=a_ptr+field_length;
@@ -2220,9 +2412,9 @@ int Field_decimal::cmp(const char *a_ptr,const char *b_ptr)
}
-void Field_decimal::sort_string(char *to,uint length)
+void Field_decimal::sort_string(uchar *to,uint length)
{
- char *str,*end;
+ uchar *str,*end;
for (str=ptr,end=ptr+length;
str != end &&
((my_isspace(&my_charset_bin,*str) || *str == '+' ||
@@ -2264,18 +2456,15 @@ void Field_decimal::sql_type(String &res) const
** Field_new_decimal
****************************************************************************/
-Field_new_decimal::Field_new_decimal(char *ptr_arg,
+Field_new_decimal::Field_new_decimal(uchar *ptr_arg,
uint32 len_arg, uchar *null_ptr_arg,
uchar null_bit_arg,
enum utype unireg_check_arg,
const char *field_name_arg,
- struct st_table *table_arg,
uint8 dec_arg,bool zero_arg,
bool unsigned_arg)
- :Field_num(ptr_arg, len_arg,
- null_ptr_arg, null_bit_arg,
- unireg_check_arg, field_name_arg, table_arg,
- dec_arg, zero_arg, unsigned_arg)
+ :Field_num(ptr_arg, len_arg, null_ptr_arg, null_bit_arg,
+ unireg_check_arg, field_name_arg, dec_arg, zero_arg, unsigned_arg)
{
precision= my_decimal_length_to_precision(len_arg, dec_arg, unsigned_arg);
set_if_smaller(precision, DECIMAL_MAX_PRECISION);
@@ -2288,14 +2477,11 @@ Field_new_decimal::Field_new_decimal(char *ptr_arg,
Field_new_decimal::Field_new_decimal(uint32 len_arg,
bool maybe_null_arg,
const char *name,
- struct st_table *t_arg,
uint8 dec_arg,
bool unsigned_arg)
- :Field_num((char*) 0, len_arg,
+ :Field_num((uchar*) 0, len_arg,
maybe_null_arg ? (uchar*) "": 0, 0,
- NONE, name, t_arg,
- dec_arg,
- 0, unsigned_arg)
+ NONE, name, dec_arg, 0, unsigned_arg)
{
precision= my_decimal_length_to_precision(len_arg, dec_arg, unsigned_arg);
set_if_smaller(precision, DECIMAL_MAX_PRECISION);
@@ -2312,13 +2498,11 @@ int Field_new_decimal::reset(void)
}
-/*
+/**
Generate max/min decimal value in case of overflow.
- SYNOPSIS
- Field_new_decimal::set_value_on_overflow();
- decimal_value buffer for value
- sign sign of value which caused overflow
+ @param decimal_value buffer for value
+ @param sign sign of value which caused overflow
*/
void Field_new_decimal::set_value_on_overflow(my_decimal *decimal_value,
@@ -2337,25 +2521,24 @@ void Field_new_decimal::set_value_on_overflow(my_decimal *decimal_value,
}
-/*
- Store decimal value in the binary buffer
+/**
+ Store decimal value in the binary buffer.
- SYNOPSIS
- store_value(const my_decimal *decimal_value)
- decimal_value my_decimal
+ Checks if decimal_value fits into field size.
+ If it does, stores the decimal in the buffer using binary format.
+ Otherwise sets maximal number that can be stored in the field.
- DESCRIPTION
- checks if decimal_value fits into field size.
- if it does, stores the decimal in the buffer using binary format.
- Otherwise sets maximal number that can be stored in the field.
+ @param decimal_value my_decimal
- RETURN
- 0 ok
- 1 error
+ @retval
+ 0 ok
+ @retval
+ 1 error
*/
bool Field_new_decimal::store_value(const my_decimal *decimal_value)
{
+ ASSERT_COLUMN_MARKED_FOR_WRITE;
int error= 0;
DBUG_ENTER("Field_new_decimal::store_value");
#ifndef DBUG_OFF
@@ -2376,7 +2559,7 @@ bool Field_new_decimal::store_value(const my_decimal *decimal_value)
#ifndef DBUG_OFF
{
char dbug_buff[DECIMAL_MAX_STR_LENGTH+1];
- DBUG_PRINT("info", ("saving with precision %d, scale: %d, value %s",
+ DBUG_PRINT("info", ("saving with precision %d scale: %d value %s",
(int)precision, (int)dec,
dbug_decimal_as_string(dbug_buff, decimal_value)));
}
@@ -2391,7 +2574,8 @@ bool Field_new_decimal::store_value(const my_decimal *decimal_value)
my_decimal2binary(E_DEC_FATAL_ERROR, &buff, ptr, precision, dec);
error= 1;
}
- DBUG_EXECUTE("info", print_decimal_buff(decimal_value, (byte *) ptr, bin_size););
+ DBUG_EXECUTE("info", print_decimal_buff(decimal_value, (uchar *) ptr,
+ bin_size););
DBUG_RETURN(error);
}
@@ -2399,13 +2583,15 @@ bool Field_new_decimal::store_value(const my_decimal *decimal_value)
int Field_new_decimal::store(const char *from, uint length,
CHARSET_INFO *charset_arg)
{
+ ASSERT_COLUMN_MARKED_FOR_WRITE;
int err;
my_decimal decimal_value;
DBUG_ENTER("Field_new_decimal::store(char*)");
if ((err= str2my_decimal(E_DEC_FATAL_ERROR &
- ~(E_DEC_OVERFLOW | E_DEC_BAD_NUM),
- from, length, charset_arg, &decimal_value)) &&
+ ~(E_DEC_OVERFLOW | E_DEC_BAD_NUM),
+ from, length, charset_arg,
+ &decimal_value)) &&
table->in_use->abort_on_warning)
{
/* Because "from" is not NUL-terminated and we use %s in the ER() */
@@ -2456,8 +2642,15 @@ int Field_new_decimal::store(const char *from, uint length,
}
+/**
+ @todo
+ Fix following when double2my_decimal when double2decimal
+ will return E_DEC_TRUNCATED always correctly
+*/
+
int Field_new_decimal::store(double nr)
{
+ ASSERT_COLUMN_MARKED_FOR_WRITE;
my_decimal decimal_value;
int err;
DBUG_ENTER("Field_new_decimal::store(double)");
@@ -2492,6 +2685,7 @@ int Field_new_decimal::store(double nr)
int Field_new_decimal::store(longlong nr, bool unsigned_val)
{
+ ASSERT_COLUMN_MARKED_FOR_WRITE;
my_decimal decimal_value;
int err;
@@ -2513,6 +2707,7 @@ int Field_new_decimal::store(longlong nr, bool unsigned_val)
int Field_new_decimal::store_decimal(const my_decimal *decimal_value)
{
+ ASSERT_COLUMN_MARKED_FOR_WRITE;
return store_value(decimal_value);
}
@@ -2526,6 +2721,7 @@ int Field_new_decimal::store_time(MYSQL_TIME *ltime, timestamp_type t_type)
double Field_new_decimal::val_real(void)
{
+ ASSERT_COLUMN_MARKED_FOR_READ;
double dbl;
my_decimal decimal_value;
my_decimal2double(E_DEC_FATAL_ERROR, val_decimal(&decimal_value), &dbl);
@@ -2535,6 +2731,7 @@ double Field_new_decimal::val_real(void)
longlong Field_new_decimal::val_int(void)
{
+ ASSERT_COLUMN_MARKED_FOR_READ;
longlong i;
my_decimal decimal_value;
my_decimal2int(E_DEC_FATAL_ERROR, val_decimal(&decimal_value),
@@ -2545,10 +2742,11 @@ longlong Field_new_decimal::val_int(void)
my_decimal* Field_new_decimal::val_decimal(my_decimal *decimal_value)
{
+ ASSERT_COLUMN_MARKED_FOR_READ;
DBUG_ENTER("Field_new_decimal::val_decimal");
binary2my_decimal(E_DEC_FATAL_ERROR, ptr, decimal_value,
precision, dec);
- DBUG_EXECUTE("info", print_decimal_buff(decimal_value, (byte *) ptr,
+ DBUG_EXECUTE("info", print_decimal_buff(decimal_value, (uchar *) ptr,
bin_size););
DBUG_RETURN(decimal_value);
}
@@ -2557,6 +2755,7 @@ my_decimal* Field_new_decimal::val_decimal(my_decimal *decimal_value)
String *Field_new_decimal::val_str(String *val_buffer,
String *val_ptr __attribute__((unused)))
{
+ ASSERT_COLUMN_MARKED_FOR_READ;
my_decimal decimal_value;
uint fixed_precision= zerofill ? precision : 0;
my_decimal2string(E_DEC_FATAL_ERROR, val_decimal(&decimal_value),
@@ -2565,13 +2764,13 @@ String *Field_new_decimal::val_str(String *val_buffer,
}
-int Field_new_decimal::cmp(const char *a,const char*b)
+int Field_new_decimal::cmp(const uchar *a,const uchar*b)
{
return memcmp(a, b, bin_size);
}
-void Field_new_decimal::sort_string(char *buff,
+void Field_new_decimal::sort_string(uchar *buff,
uint length __attribute__((unused)))
{
memcpy(buff, ptr, bin_size);
@@ -2587,12 +2786,149 @@ void Field_new_decimal::sql_type(String &str) const
}
+/**
+ Save the field metadata for new decimal fields.
+
+ Saves the precision in the first byte and decimals() in the second
+ byte of the field metadata array at index of *metadata_ptr and
+ *(metadata_ptr + 1).
+
+ @param metadata_ptr First byte of field metadata
+
+ @returns number of bytes written to metadata_ptr
+*/
+int Field_new_decimal::do_save_field_metadata(uchar *metadata_ptr)
+{
+ *metadata_ptr= precision;
+ *(metadata_ptr + 1)= decimals();
+ return 2;
+}
+
+
+/**
+ Returns the number of bytes field uses in row-based replication
+ row packed size.
+
+ This method is used in row-based replication to determine the number
+ of bytes that the field consumes in the row record format. This is
+ used to skip fields in the master that do not exist on the slave.
+
+ @param field_metadata Encoded size in field metadata
+
+ @returns The size of the field based on the field metadata.
+*/
+uint Field_new_decimal::pack_length_from_metadata(uint field_metadata)
+{
+ 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);
+ return (source_size);
+}
+
+
+/**
+ 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)))
+{
+ 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);
+}
+
+
+uint Field_new_decimal::is_equal(Create_field *new_field)
+{
+ return ((new_field->sql_type == real_type()) &&
+ ((new_field->flags & UNSIGNED_FLAG) ==
+ (uint) (flags & UNSIGNED_FLAG)) &&
+ ((new_field->flags & AUTO_INCREMENT_FLAG) ==
+ (uint) (flags & AUTO_INCREMENT_FLAG)) &&
+ (new_field->length == max_display_length()) &&
+ (new_field->decimals == dec));
+}
+
+
+/**
+ Unpack a decimal field from row data.
+
+ This method is used to unpack a decimal or numeric field from a master
+ whose size of the field is less than that of the slave.
+
+ @param to Destination of the data
+ @param from Source of the data
+ @param param_data Precision (upper) and decimal (lower) values
+
+ @return New pointer into memory based on from + length of the data
+*/
+const uchar *
+Field_new_decimal::unpack(uchar* to,
+ const uchar *from,
+ uint param_data,
+ bool low_byte_first)
+{
+ if (param_data == 0)
+ return Field::unpack(to, from, param_data, low_byte_first);
+
+ uint from_precision= (param_data & 0xff00) >> 8U;
+ uint from_decimal= param_data & 0x00ff;
+ uint length=pack_length();
+ uint from_pack_len= my_decimal_get_binary_size(from_precision, from_decimal);
+ uint len= (param_data && (from_pack_len < length)) ?
+ from_pack_len : length;
+ if ((from_pack_len && (from_pack_len < length)) ||
+ (from_precision < precision) ||
+ (from_decimal < decimals()))
+ {
+ /*
+ If the master's data is smaller than the slave, we need to convert
+ the binary to decimal then resize the decimal converting it back to
+ a decimal and write that to the raw data buffer.
+ */
+ decimal_digit_t dec_buf[DECIMAL_MAX_PRECISION];
+ decimal_t dec;
+ dec.len= from_precision;
+ dec.buf= dec_buf;
+ /*
+ Note: bin2decimal does not change the length of the field. So it is
+ just the first step the resizing operation. The second step does the
+ resizing using the precision and decimals from the slave.
+ */
+ bin2decimal((uchar *)from, &dec, from_precision, from_decimal);
+ decimal2bin(&dec, to, precision, decimals());
+ }
+ else
+ memcpy(to, from, len); // Sizes are the same, just copy the data.
+ return from+len;
+}
+
/****************************************************************************
** tiny int
****************************************************************************/
int Field_tiny::store(const char *from,uint len,CHARSET_INFO *cs)
{
+ ASSERT_COLUMN_MARKED_FOR_WRITE;
int error;
longlong rnd;
@@ -2604,6 +2940,7 @@ int Field_tiny::store(const char *from,uint len,CHARSET_INFO *cs)
int Field_tiny::store(double nr)
{
+ ASSERT_COLUMN_MARKED_FOR_WRITE;
int error= 0;
nr=rint(nr);
if (unsigned_flag)
@@ -2646,6 +2983,7 @@ int Field_tiny::store(double nr)
int Field_tiny::store(longlong nr, bool unsigned_val)
{
+ ASSERT_COLUMN_MARKED_FOR_WRITE;
int error= 0;
if (unsigned_flag)
@@ -2690,7 +3028,8 @@ int Field_tiny::store(longlong nr, bool unsigned_val)
double Field_tiny::val_real(void)
{
- int tmp= unsigned_flag ? (int) ((uchar*) ptr)[0] :
+ ASSERT_COLUMN_MARKED_FOR_READ;
+ int tmp= unsigned_flag ? (int) ptr[0] :
(int) ((signed char*) ptr)[0];
return (double) tmp;
}
@@ -2698,7 +3037,8 @@ double Field_tiny::val_real(void)
longlong Field_tiny::val_int(void)
{
- int tmp= unsigned_flag ? (int) ((uchar*) ptr)[0] :
+ ASSERT_COLUMN_MARKED_FOR_READ;
+ int tmp= unsigned_flag ? (int) ptr[0] :
(int) ((signed char*) ptr)[0];
return (longlong) tmp;
}
@@ -2707,6 +3047,7 @@ longlong Field_tiny::val_int(void)
String *Field_tiny::val_str(String *val_buffer,
String *val_ptr __attribute__((unused)))
{
+ ASSERT_COLUMN_MARKED_FOR_READ;
CHARSET_INFO *cs= &my_charset_bin;
uint length;
uint mlength=max(field_length+1,5*cs->mbmaxlen);
@@ -2715,7 +3056,7 @@ String *Field_tiny::val_str(String *val_buffer,
if (unsigned_flag)
length= (uint) cs->cset->long10_to_str(cs,to,mlength, 10,
- (long) *((uchar*) ptr));
+ (long) *ptr);
else
length= (uint) cs->cset->long10_to_str(cs,to,mlength,-10,
(long) *((signed char*) ptr));
@@ -2731,7 +3072,7 @@ bool Field_tiny::send_binary(Protocol *protocol)
return protocol->store_tiny((longlong) (int8) ptr[0]);
}
-int Field_tiny::cmp(const char *a_ptr, const char *b_ptr)
+int Field_tiny::cmp(const uchar *a_ptr, const uchar *b_ptr)
{
signed char a,b;
a=(signed char) a_ptr[0]; b= (signed char) b_ptr[0];
@@ -2740,12 +3081,12 @@ int Field_tiny::cmp(const char *a_ptr, const char *b_ptr)
return (a < b) ? -1 : (a > b) ? 1 : 0;
}
-void Field_tiny::sort_string(char *to,uint length __attribute__((unused)))
+void Field_tiny::sort_string(uchar *to,uint length __attribute__((unused)))
{
if (unsigned_flag)
*to= *ptr;
else
- to[0] = (char) ((uchar) ptr[0] ^ (uchar) 128); /* Revers signbit */
+ to[0] = (char) (ptr[0] ^ (uchar) 128); /* Revers signbit */
}
void Field_tiny::sql_type(String &res) const
@@ -2762,6 +3103,7 @@ void Field_tiny::sql_type(String &res) const
int Field_short::store(const char *from,uint len,CHARSET_INFO *cs)
{
+ ASSERT_COLUMN_MARKED_FOR_WRITE;
int store_tmp;
int error;
longlong rnd;
@@ -2782,6 +3124,7 @@ int Field_short::store(const char *from,uint len,CHARSET_INFO *cs)
int Field_short::store(double nr)
{
+ ASSERT_COLUMN_MARKED_FOR_WRITE;
int error= 0;
int16 res;
nr=rint(nr);
@@ -2833,6 +3176,7 @@ int Field_short::store(double nr)
int Field_short::store(longlong nr, bool unsigned_val)
{
+ ASSERT_COLUMN_MARKED_FOR_WRITE;
int error= 0;
int16 res;
@@ -2887,6 +3231,7 @@ int Field_short::store(longlong nr, bool unsigned_val)
double Field_short::val_real(void)
{
+ ASSERT_COLUMN_MARKED_FOR_READ;
short j;
#ifdef WORDS_BIGENDIAN
if (table->s->db_low_byte_first)
@@ -2899,6 +3244,7 @@ double Field_short::val_real(void)
longlong Field_short::val_int(void)
{
+ ASSERT_COLUMN_MARKED_FOR_READ;
short j;
#ifdef WORDS_BIGENDIAN
if (table->s->db_low_byte_first)
@@ -2913,6 +3259,7 @@ longlong Field_short::val_int(void)
String *Field_short::val_str(String *val_buffer,
String *val_ptr __attribute__((unused)))
{
+ ASSERT_COLUMN_MARKED_FOR_READ;
CHARSET_INFO *cs= &my_charset_bin;
uint length;
uint mlength=max(field_length+1,7*cs->mbmaxlen);
@@ -2944,7 +3291,7 @@ bool Field_short::send_binary(Protocol *protocol)
}
-int Field_short::cmp(const char *a_ptr, const char *b_ptr)
+int Field_short::cmp(const uchar *a_ptr, const uchar *b_ptr)
{
short a,b;
#ifdef WORDS_BIGENDIAN
@@ -2966,7 +3313,7 @@ int Field_short::cmp(const char *a_ptr, const char *b_ptr)
return (a < b) ? -1 : (a > b) ? 1 : 0;
}
-void Field_short::sort_string(char *to,uint length __attribute__((unused)))
+void Field_short::sort_string(uchar *to,uint length __attribute__((unused)))
{
#ifdef WORDS_BIGENDIAN
if (!table->s->db_low_byte_first)
@@ -3003,6 +3350,7 @@ void Field_short::sql_type(String &res) const
int Field_medium::store(const char *from,uint len,CHARSET_INFO *cs)
{
+ ASSERT_COLUMN_MARKED_FOR_WRITE;
int store_tmp;
int error;
longlong rnd;
@@ -3016,6 +3364,7 @@ int Field_medium::store(const char *from,uint len,CHARSET_INFO *cs)
int Field_medium::store(double nr)
{
+ ASSERT_COLUMN_MARKED_FOR_WRITE;
int error= 0;
nr=rint(nr);
if (unsigned_flag)
@@ -3061,6 +3410,7 @@ int Field_medium::store(double nr)
int Field_medium::store(longlong nr, bool unsigned_val)
{
+ ASSERT_COLUMN_MARKED_FOR_WRITE;
int error= 0;
if (unsigned_flag)
@@ -3109,6 +3459,7 @@ int Field_medium::store(longlong nr, bool unsigned_val)
double Field_medium::val_real(void)
{
+ ASSERT_COLUMN_MARKED_FOR_READ;
long j= unsigned_flag ? (long) uint3korr(ptr) : sint3korr(ptr);
return (double) j;
}
@@ -3116,6 +3467,7 @@ double Field_medium::val_real(void)
longlong Field_medium::val_int(void)
{
+ ASSERT_COLUMN_MARKED_FOR_READ;
long j= unsigned_flag ? (long) uint3korr(ptr) : sint3korr(ptr);
return (longlong) j;
}
@@ -3124,6 +3476,7 @@ longlong Field_medium::val_int(void)
String *Field_medium::val_str(String *val_buffer,
String *val_ptr __attribute__((unused)))
{
+ ASSERT_COLUMN_MARKED_FOR_READ;
CHARSET_INFO *cs= &my_charset_bin;
uint length;
uint mlength=max(field_length+1,10*cs->mbmaxlen);
@@ -3141,11 +3494,12 @@ String *Field_medium::val_str(String *val_buffer,
bool Field_medium::send_binary(Protocol *protocol)
{
+ ASSERT_COLUMN_MARKED_FOR_READ;
return protocol->store_long(Field_medium::val_int());
}
-int Field_medium::cmp(const char *a_ptr, const char *b_ptr)
+int Field_medium::cmp(const uchar *a_ptr, const uchar *b_ptr)
{
long a,b;
if (unsigned_flag)
@@ -3161,7 +3515,7 @@ int Field_medium::cmp(const char *a_ptr, const char *b_ptr)
return (a < b) ? -1 : (a > b) ? 1 : 0;
}
-void Field_medium::sort_string(char *to,uint length __attribute__((unused)))
+void Field_medium::sort_string(uchar *to,uint length __attribute__((unused)))
{
if (unsigned_flag)
to[0] = ptr[2];
@@ -3186,6 +3540,7 @@ void Field_medium::sql_type(String &res) const
int Field_long::store(const char *from,uint len,CHARSET_INFO *cs)
{
+ ASSERT_COLUMN_MARKED_FOR_WRITE;
long store_tmp;
int error;
longlong rnd;
@@ -3206,6 +3561,7 @@ int Field_long::store(const char *from,uint len,CHARSET_INFO *cs)
int Field_long::store(double nr)
{
+ ASSERT_COLUMN_MARKED_FOR_WRITE;
int error= 0;
int32 res;
nr=rint(nr);
@@ -3257,10 +3613,10 @@ int Field_long::store(double nr)
int Field_long::store(longlong nr, bool unsigned_val)
{
+ ASSERT_COLUMN_MARKED_FOR_WRITE;
int error= 0;
int32 res;
- DBUG_ASSERT(table->in_use == current_thd); // General safety
-
+
if (unsigned_flag)
{
if (nr < 0 && !unsigned_val)
@@ -3310,6 +3666,7 @@ int Field_long::store(longlong nr, bool unsigned_val)
double Field_long::val_real(void)
{
+ ASSERT_COLUMN_MARKED_FOR_READ;
int32 j;
#ifdef WORDS_BIGENDIAN
if (table->s->db_low_byte_first)
@@ -3322,6 +3679,7 @@ double Field_long::val_real(void)
longlong Field_long::val_int(void)
{
+ ASSERT_COLUMN_MARKED_FOR_READ;
int32 j;
/* See the comment in Field_long::store(long long) */
DBUG_ASSERT(table->in_use == current_thd);
@@ -3337,6 +3695,7 @@ longlong Field_long::val_int(void)
String *Field_long::val_str(String *val_buffer,
String *val_ptr __attribute__((unused)))
{
+ ASSERT_COLUMN_MARKED_FOR_READ;
CHARSET_INFO *cs= &my_charset_bin;
uint length;
uint mlength=max(field_length+1,12*cs->mbmaxlen);
@@ -3363,10 +3722,11 @@ String *Field_long::val_str(String *val_buffer,
bool Field_long::send_binary(Protocol *protocol)
{
+ ASSERT_COLUMN_MARKED_FOR_READ;
return protocol->store_long(Field_long::val_int());
}
-int Field_long::cmp(const char *a_ptr, const char *b_ptr)
+int Field_long::cmp(const uchar *a_ptr, const uchar *b_ptr)
{
int32 a,b;
#ifdef WORDS_BIGENDIAN
@@ -3386,7 +3746,7 @@ int Field_long::cmp(const char *a_ptr, const char *b_ptr)
return (a < b) ? -1 : (a > b) ? 1 : 0;
}
-void Field_long::sort_string(char *to,uint length __attribute__((unused)))
+void Field_long::sort_string(uchar *to,uint length __attribute__((unused)))
{
#ifdef WORDS_BIGENDIAN
if (!table->s->db_low_byte_first)
@@ -3427,7 +3787,8 @@ void Field_long::sql_type(String &res) const
int Field_longlong::store(const char *from,uint len,CHARSET_INFO *cs)
{
- int error;
+ ASSERT_COLUMN_MARKED_FOR_WRITE;
+ int error= 0;
char *end;
ulonglong tmp;
@@ -3456,6 +3817,7 @@ int Field_longlong::store(const char *from,uint len,CHARSET_INFO *cs)
int Field_longlong::store(double nr)
{
+ ASSERT_COLUMN_MARKED_FOR_WRITE;
int error= 0;
longlong res;
@@ -3507,6 +3869,7 @@ int Field_longlong::store(double nr)
int Field_longlong::store(longlong nr, bool unsigned_val)
{
+ ASSERT_COLUMN_MARKED_FOR_WRITE;
int error= 0;
if (nr < 0) // Only possible error
@@ -3537,6 +3900,7 @@ int Field_longlong::store(longlong nr, bool unsigned_val)
double Field_longlong::val_real(void)
{
+ ASSERT_COLUMN_MARKED_FOR_READ;
longlong j;
#ifdef WORDS_BIGENDIAN
if (table->s->db_low_byte_first)
@@ -3558,6 +3922,7 @@ double Field_longlong::val_real(void)
longlong Field_longlong::val_int(void)
{
+ ASSERT_COLUMN_MARKED_FOR_READ;
longlong j;
#ifdef WORDS_BIGENDIAN
if (table->s->db_low_byte_first)
@@ -3596,11 +3961,12 @@ String *Field_longlong::val_str(String *val_buffer,
bool Field_longlong::send_binary(Protocol *protocol)
{
+ ASSERT_COLUMN_MARKED_FOR_READ;
return protocol->store_longlong(Field_longlong::val_int(), unsigned_flag);
}
-int Field_longlong::cmp(const char *a_ptr, const char *b_ptr)
+int Field_longlong::cmp(const uchar *a_ptr, const uchar *b_ptr)
{
longlong a,b;
#ifdef WORDS_BIGENDIAN
@@ -3621,7 +3987,7 @@ int Field_longlong::cmp(const char *a_ptr, const char *b_ptr)
return (a < b) ? -1 : (a > b) ? 1 : 0;
}
-void Field_longlong::sort_string(char *to,uint length __attribute__((unused)))
+void Field_longlong::sort_string(uchar *to,uint length __attribute__((unused)))
{
#ifdef WORDS_BIGENDIAN
if (!table->s->db_low_byte_first)
@@ -3665,6 +4031,47 @@ void Field_longlong::sql_type(String &res) const
}
+/*
+ Floating-point numbers
+ */
+
+uchar *
+Field_real::pack(uchar *to, const uchar *from,
+ uint max_length, bool low_byte_first)
+{
+ DBUG_ENTER("Field_real::pack");
+ DBUG_ASSERT(max_length >= pack_length());
+#ifdef WORDS_BIGENDIAN
+ if (low_byte_first != table->s->db_low_byte_first)
+ {
+ const uchar *dptr= from + pack_length();
+ while (dptr-- > from)
+ *to++ = *dptr;
+ DBUG_RETURN(to);
+ }
+ else
+#endif
+ DBUG_RETURN(Field::pack(to, from, max_length, low_byte_first));
+}
+
+const uchar *
+Field_real::unpack(uchar *to, const uchar *from,
+ uint param_data, bool low_byte_first)
+{
+ DBUG_ENTER("Field_real::unpack");
+#ifdef WORDS_BIGENDIAN
+ if (low_byte_first != table->s->db_low_byte_first)
+ {
+ const uchar *dptr= from + pack_length();
+ while (dptr-- > from)
+ *to++ = *dptr;
+ DBUG_RETURN(from + pack_length());
+ }
+ else
+#endif
+ DBUG_RETURN(Field::unpack(to, from, param_data, low_byte_first));
+}
+
/****************************************************************************
single precision float
****************************************************************************/
@@ -3688,6 +4095,7 @@ int Field_float::store(const char *from,uint len,CHARSET_INFO *cs)
int Field_float::store(double nr)
{
+ ASSERT_COLUMN_MARKED_FOR_WRITE;
int error= truncate(&nr, FLT_MAX);
float j= (float)nr;
@@ -3698,19 +4106,21 @@ int Field_float::store(double nr)
}
else
#endif
- memcpy_fixed(ptr,(byte*) &j,sizeof(j));
+ memcpy_fixed(ptr,(uchar*) &j,sizeof(j));
return error;
}
int Field_float::store(longlong nr, bool unsigned_val)
{
- return store(unsigned_val ? ulonglong2double((ulonglong) nr) : (double) nr);
+ return Field_float::store(unsigned_val ? ulonglong2double((ulonglong) nr) :
+ (double) nr);
}
double Field_float::val_real(void)
{
+ ASSERT_COLUMN_MARKED_FOR_READ;
float j;
#ifdef WORDS_BIGENDIAN
if (table->s->db_low_byte_first)
@@ -3719,7 +4129,7 @@ double Field_float::val_real(void)
}
else
#endif
- memcpy_fixed((byte*) &j,ptr,sizeof(j));
+ memcpy_fixed((uchar*) &j,ptr,sizeof(j));
return ((double) j);
}
@@ -3733,7 +4143,7 @@ longlong Field_float::val_int(void)
}
else
#endif
- memcpy_fixed((byte*) &j,ptr,sizeof(j));
+ memcpy_fixed((uchar*) &j,ptr,sizeof(j));
return (longlong) rint(j);
}
@@ -3741,6 +4151,7 @@ longlong Field_float::val_int(void)
String *Field_float::val_str(String *val_buffer,
String *val_ptr __attribute__((unused)))
{
+ ASSERT_COLUMN_MARKED_FOR_READ;
float nr;
#ifdef WORDS_BIGENDIAN
if (table->s->db_low_byte_first)
@@ -3749,7 +4160,7 @@ String *Field_float::val_str(String *val_buffer,
}
else
#endif
- memcpy_fixed((byte*) &nr,ptr,sizeof(nr));
+ memcpy_fixed((uchar*) &nr,ptr,sizeof(nr));
uint to_length=max(field_length,70);
val_buffer->alloc(to_length);
@@ -3821,7 +4232,7 @@ String *Field_float::val_str(String *val_buffer,
}
-int Field_float::cmp(const char *a_ptr, const char *b_ptr)
+int Field_float::cmp(const uchar *a_ptr, const uchar *b_ptr)
{
float a,b;
#ifdef WORDS_BIGENDIAN
@@ -3841,7 +4252,7 @@ int Field_float::cmp(const char *a_ptr, const char *b_ptr)
#define FLT_EXP_DIG (sizeof(float)*8-FLT_MANT_DIG)
-void Field_float::sort_string(char *to,uint length __attribute__((unused)))
+void Field_float::sort_string(uchar *to,uint length __attribute__((unused)))
{
float nr;
#ifdef WORDS_BIGENDIAN
@@ -3853,7 +4264,7 @@ void Field_float::sort_string(char *to,uint length __attribute__((unused)))
#endif
memcpy_fixed(&nr,ptr,sizeof(float));
- uchar *tmp= (uchar*) to;
+ uchar *tmp= to;
if (nr == (float) 0.0)
{ /* Change to zero string */
tmp[0]=(uchar) 128;
@@ -3886,10 +4297,27 @@ void Field_float::sort_string(char *to,uint length __attribute__((unused)))
bool Field_float::send_binary(Protocol *protocol)
{
+ ASSERT_COLUMN_MARKED_FOR_READ;
return protocol->store((float) Field_float::val_real(), dec, (String*) 0);
}
+/**
+ Save the field metadata for float fields.
+
+ Saves the pack length in the first byte.
+
+ @param metadata_ptr First byte of field metadata
+
+ @returns number of bytes written to metadata_ptr
+*/
+int Field_float::do_save_field_metadata(uchar *metadata_ptr)
+{
+ *metadata_ptr= pack_length();
+ return 1;
+}
+
+
void Field_float::sql_type(String &res) const
{
if (dec == NOT_FIXED_DEC)
@@ -3929,6 +4357,7 @@ int Field_double::store(const char *from,uint len,CHARSET_INFO *cs)
int Field_double::store(double nr)
{
+ ASSERT_COLUMN_MARKED_FOR_WRITE;
int error= truncate(&nr, DBL_MAX);
#ifdef WORDS_BIGENDIAN
@@ -3945,7 +4374,8 @@ int Field_double::store(double nr)
int Field_double::store(longlong nr, bool unsigned_val)
{
- return store(unsigned_val ? ulonglong2double((ulonglong) nr) : (double) nr);
+ return Field_double::store(unsigned_val ? ulonglong2double((ulonglong) nr) :
+ (double) nr);
}
/*
@@ -4015,6 +4445,7 @@ int Field_real::store_decimal(const my_decimal *dm)
double Field_double::val_real(void)
{
+ ASSERT_COLUMN_MARKED_FOR_READ;
double j;
#ifdef WORDS_BIGENDIAN
if (table->s->db_low_byte_first)
@@ -4029,6 +4460,7 @@ double Field_double::val_real(void)
longlong Field_double::val_int(void)
{
+ ASSERT_COLUMN_MARKED_FOR_READ;
double j;
longlong res;
#ifdef WORDS_BIGENDIAN
@@ -4068,6 +4500,7 @@ warn:
my_decimal *Field_real::val_decimal(my_decimal *decimal_value)
{
+ ASSERT_COLUMN_MARKED_FOR_READ;
double2my_decimal(E_DEC_FATAL_ERROR, val_real(), decimal_value);
return decimal_value;
}
@@ -4076,6 +4509,7 @@ my_decimal *Field_real::val_decimal(my_decimal *decimal_value)
String *Field_double::val_str(String *val_buffer,
String *val_ptr __attribute__((unused)))
{
+ ASSERT_COLUMN_MARKED_FOR_READ;
double nr;
#ifdef WORDS_BIGENDIAN
if (table->s->db_low_byte_first)
@@ -4162,8 +4596,9 @@ bool Field_double::send_binary(Protocol *protocol)
}
-int Field_double::cmp(const char *a_ptr, const char *b_ptr)
+int Field_double::cmp(const uchar *a_ptr, const uchar *b_ptr)
{
+ ASSERT_COLUMN_MARKED_FOR_READ;
double a,b;
#ifdef WORDS_BIGENDIAN
if (table->s->db_low_byte_first)
@@ -4185,7 +4620,7 @@ int Field_double::cmp(const char *a_ptr, const char *b_ptr)
/* The following should work for IEEE */
-void Field_double::sort_string(char *to,uint length __attribute__((unused)))
+void Field_double::sort_string(uchar *to,uint length __attribute__((unused)))
{
double nr;
#ifdef WORDS_BIGENDIAN
@@ -4196,7 +4631,24 @@ void Field_double::sort_string(char *to,uint length __attribute__((unused)))
else
#endif
doubleget(nr,ptr);
- change_double_for_sort(nr, (byte*) to);
+ change_double_for_sort(nr, to);
+}
+
+
+/**
+ Save the field metadata for double fields.
+
+ Saves the pack length in the first byte of the field metadata array
+ at index of *metadata_ptr.
+
+ @param metadata_ptr First byte of field metadata
+
+ @returns number of bytes written to metadata_ptr
+*/
+int Field_double::do_save_field_metadata(uchar *metadata_ptr)
+{
+ *metadata_ptr= pack_length();
+ return 1;
}
@@ -4216,9 +4668,8 @@ void Field_double::sql_type(String &res) const
}
-/*
- TIMESTAMP type.
- Holds datetime values in range from 1970-01-01 00:00:01 UTC to
+/**
+ TIMESTAMP type holds datetime values in range from 1970-01-01 00:00:01 UTC to
2038-01-01 00:00:00 UTC stored as number of seconds since Unix
Epoch in UTC.
@@ -4251,7 +4702,7 @@ void Field_double::sql_type(String &res) const
NONE - field which is not auto-set on update with some other than NOW()
default value (TIMESTAMP DEFAULT 0).
- Note that TIMESTAMP_OLD_FIELD's are never created explicitly now, they are
+ Note that TIMESTAMP_OLD_FIELDs are never created explicitly now, they are
left only for preserving ability to read old tables. Such fields replaced
with their newer analogs in CREATE TABLE and in SHOW CREATE TABLE. This is
because we want to prefer NONE unireg_check before TIMESTAMP_OLD_FIELD for
@@ -4261,55 +4712,47 @@ void Field_double::sql_type(String &res) const
exception is different behavior of old/new timestamps during ALTER TABLE.
*/
-Field_timestamp::Field_timestamp(char *ptr_arg, uint32 len_arg,
+Field_timestamp::Field_timestamp(uchar *ptr_arg, uint32 len_arg,
uchar *null_ptr_arg, uchar null_bit_arg,
enum utype unireg_check_arg,
const char *field_name_arg,
- struct st_table *table_arg,
+ TABLE_SHARE *share,
CHARSET_INFO *cs)
:Field_str(ptr_arg, MAX_DATETIME_WIDTH, null_ptr_arg, null_bit_arg,
- unireg_check_arg, field_name_arg, table_arg, cs)
+ unireg_check_arg, field_name_arg, cs)
{
/* For 4.0 MYD and 4.0 InnoDB compatibility */
flags|= ZEROFILL_FLAG | UNSIGNED_FLAG;
- if (table && !table->timestamp_field &&
- unireg_check != NONE)
+ if (!share->timestamp_field && unireg_check != NONE)
{
/* This timestamp has auto-update */
- table->timestamp_field= this;
- flags|=TIMESTAMP_FLAG;
+ share->timestamp_field= this;
+ flags|= TIMESTAMP_FLAG;
+ if (unireg_check != TIMESTAMP_DN_FIELD)
+ flags|= ON_UPDATE_NOW_FLAG;
}
}
Field_timestamp::Field_timestamp(bool maybe_null_arg,
const char *field_name_arg,
- struct st_table *table_arg, CHARSET_INFO *cs)
- :Field_str((char*) 0, MAX_DATETIME_WIDTH,
+ CHARSET_INFO *cs)
+ :Field_str((uchar*) 0, MAX_DATETIME_WIDTH,
maybe_null_arg ? (uchar*) "": 0, 0,
- NONE, field_name_arg, table_arg, cs)
+ NONE, field_name_arg, cs)
{
/* For 4.0 MYD and 4.0 InnoDB compatibility */
flags|= ZEROFILL_FLAG | UNSIGNED_FLAG;
- if (table && !table->timestamp_field &&
- unireg_check != NONE)
- {
- /* This timestamp has auto-update */
- table->timestamp_field= this;
- flags|=TIMESTAMP_FLAG;
- }
+ if (unireg_check != TIMESTAMP_DN_FIELD)
+ flags|= ON_UPDATE_NOW_FLAG;
}
-/*
+/**
Get auto-set type for TIMESTAMP field.
- SYNOPSIS
- get_auto_set_type()
-
- DESCRIPTION
- Returns value indicating during which operations this TIMESTAMP field
- should be auto-set to current timestamp.
+ Returns value indicating during which operations this TIMESTAMP field
+ should be auto-set to current timestamp.
*/
timestamp_auto_set_type Field_timestamp::get_auto_set_type() const
{
@@ -4342,7 +4785,7 @@ timestamp_auto_set_type Field_timestamp::get_auto_set_type() const
int Field_timestamp::store(const char *from,uint len,CHARSET_INFO *cs)
{
-
+ ASSERT_COLUMN_MARKED_FOR_WRITE;
MYSQL_TIME l_time;
my_time_t tmp= 0;
int error;
@@ -4382,15 +4825,7 @@ int Field_timestamp::store(const char *from,uint len,CHARSET_INFO *cs)
error= 1;
}
}
-
-#ifdef WORDS_BIGENDIAN
- if (table && table->s->db_low_byte_first)
- {
- int4store(ptr,tmp);
- }
- else
-#endif
- longstore(ptr,tmp);
+ store_timestamp(tmp);
return error;
}
@@ -4413,6 +4848,7 @@ int Field_timestamp::store(double nr)
int Field_timestamp::store(longlong nr, bool unsigned_val)
{
+ ASSERT_COLUMN_MARKED_FOR_WRITE;
MYSQL_TIME l_time;
my_time_t timestamp= 0;
int error;
@@ -4449,26 +4885,19 @@ int Field_timestamp::store(longlong nr, bool unsigned_val)
WARN_DATA_TRUNCATED,
nr, MYSQL_TIMESTAMP_DATETIME, 1);
-#ifdef WORDS_BIGENDIAN
- if (table && table->s->db_low_byte_first)
- {
- int4store(ptr,timestamp);
- }
- else
-#endif
- longstore(ptr,(uint32) timestamp);
-
+ store_timestamp(timestamp);
return error;
}
-
double Field_timestamp::val_real(void)
{
+ ASSERT_COLUMN_MARKED_FOR_READ;
return (double) Field_timestamp::val_int();
}
longlong Field_timestamp::val_int(void)
{
+ ASSERT_COLUMN_MARKED_FOR_READ;
uint32 temp;
MYSQL_TIME time_tmp;
THD *thd= table ? table->in_use : current_thd;
@@ -4494,6 +4923,7 @@ longlong Field_timestamp::val_int(void)
String *Field_timestamp::val_str(String *val_buffer, String *val_ptr)
{
+ ASSERT_COLUMN_MARKED_FOR_READ;
uint32 temp, temp2;
MYSQL_TIME time_tmp;
THD *thd= table ? table->in_use : current_thd;
@@ -4602,7 +5032,7 @@ bool Field_timestamp::send_binary(Protocol *protocol)
}
-int Field_timestamp::cmp(const char *a_ptr, const char *b_ptr)
+int Field_timestamp::cmp(const uchar *a_ptr, const uchar *b_ptr)
{
int32 a,b;
#ifdef WORDS_BIGENDIAN
@@ -4621,7 +5051,7 @@ int Field_timestamp::cmp(const char *a_ptr, const char *b_ptr)
}
-void Field_timestamp::sort_string(char *to,uint length __attribute__((unused)))
+void Field_timestamp::sort_string(uchar *to,uint length __attribute__((unused)))
{
#ifdef WORDS_BIGENDIAN
if (!table || !table->s->db_low_byte_first)
@@ -4653,14 +5083,7 @@ void Field_timestamp::set_time()
THD *thd= table ? table->in_use : current_thd;
long tmp= (long) thd->query_start();
set_notnull();
-#ifdef WORDS_BIGENDIAN
- if (table && table->s->db_low_byte_first)
- {
- int4store(ptr,tmp);
- }
- else
-#endif
- longstore(ptr,tmp);
+ store_timestamp(tmp);
}
/****************************************************************************
@@ -4724,12 +5147,13 @@ int Field_time::store_time(MYSQL_TIME *ltime, timestamp_type time_type)
int Field_time::store(double nr)
{
+ ASSERT_COLUMN_MARKED_FOR_WRITE;
long tmp;
int error= 0;
if (nr > (double)TIME_MAX_VALUE)
{
tmp= TIME_MAX_VALUE;
- set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN,
+ set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN,
ER_WARN_DATA_OUT_OF_RANGE, nr, MYSQL_TIMESTAMP_TIME);
error= 1;
}
@@ -4761,6 +5185,7 @@ int Field_time::store(double nr)
int Field_time::store(longlong nr, bool unsigned_val)
{
+ ASSERT_COLUMN_MARKED_FOR_WRITE;
long tmp;
int error= 0;
if (nr < (longlong) -TIME_MAX_VALUE && !unsigned_val)
@@ -4798,17 +5223,20 @@ int Field_time::store(longlong nr, bool unsigned_val)
double Field_time::val_real(void)
{
+ ASSERT_COLUMN_MARKED_FOR_READ;
uint32 j= (uint32) uint3korr(ptr);
return (double) j;
}
longlong Field_time::val_int(void)
{
+ ASSERT_COLUMN_MARKED_FOR_READ;
return (longlong) sint3korr(ptr);
}
-/*
+/**
+ @note
This function is multi-byte safe as the result string is always of type
my_charset_bin
*/
@@ -4816,6 +5244,7 @@ longlong Field_time::val_int(void)
String *Field_time::val_str(String *val_buffer,
String *val_ptr __attribute__((unused)))
{
+ ASSERT_COLUMN_MARKED_FOR_READ;
MYSQL_TIME ltime;
val_buffer->alloc(MAX_DATE_STRING_REP_LENGTH);
long tmp=(long) sint3korr(ptr);
@@ -4834,7 +5263,8 @@ String *Field_time::val_str(String *val_buffer,
}
-/*
+/**
+ @note
Normally we would not consider 'time' as a valid date, but we allow
get_date() here to be able to do things like
DATE_FORMAT(time, "%l.%i %p")
@@ -4898,7 +5328,7 @@ bool Field_time::send_binary(Protocol *protocol)
}
-int Field_time::cmp(const char *a_ptr, const char *b_ptr)
+int Field_time::cmp(const uchar *a_ptr, const uchar *b_ptr)
{
int32 a,b;
a=(int32) sint3korr(a_ptr);
@@ -4906,7 +5336,7 @@ int Field_time::cmp(const char *a_ptr, const char *b_ptr)
return (a < b) ? -1 : (a > b) ? 1 : 0;
}
-void Field_time::sort_string(char *to,uint length __attribute__((unused)))
+void Field_time::sort_string(uchar *to,uint length __attribute__((unused)))
{
to[0] = (uchar) (ptr[2] ^ 128);
to[1] = ptr[1];
@@ -4926,6 +5356,7 @@ void Field_time::sql_type(String &res) const
int Field_year::store(const char *from, uint len,CHARSET_INFO *cs)
{
+ ASSERT_COLUMN_MARKED_FOR_WRITE;
char *end;
int error;
longlong nr= cs->cset->strntoull10rnd(cs, from, len, 0, &end, &error);
@@ -4955,7 +5386,7 @@ int Field_year::store(const char *from, uint len,CHARSET_INFO *cs)
else if (nr > 1900)
nr-= 1900;
}
- *ptr= (char) (unsigned char) nr;
+ *ptr= (char) (uchar) nr;
return error;
}
@@ -4973,6 +5404,7 @@ int Field_year::store(double nr)
int Field_year::store(longlong nr, bool unsigned_val)
{
+ ASSERT_COLUMN_MARKED_FOR_WRITE;
if (nr < 0 || nr >= 100 && nr <= 1900 || nr > 2155)
{
*ptr= 0;
@@ -4986,13 +5418,14 @@ int Field_year::store(longlong nr, bool unsigned_val)
else if (nr > 1900)
nr-= 1900;
}
- *ptr= (char) (unsigned char) nr;
+ *ptr= (char) (uchar) nr;
return 0;
}
bool Field_year::send_binary(Protocol *protocol)
{
+ ASSERT_COLUMN_MARKED_FOR_READ;
ulonglong tmp= Field_year::val_int();
return protocol->store_short(tmp);
}
@@ -5006,7 +5439,8 @@ double Field_year::val_real(void)
longlong Field_year::val_int(void)
{
- int tmp= (int) ((uchar*) ptr)[0];
+ ASSERT_COLUMN_MARKED_FOR_READ;
+ int tmp= (int) ptr[0];
if (field_length != 4)
tmp%=100; // Return last 2 char
else if (tmp)
@@ -5043,6 +5477,7 @@ void Field_year::sql_type(String &res) const
int Field_date::store(const char *from, uint len,CHARSET_INFO *cs)
{
+ ASSERT_COLUMN_MARKED_FOR_WRITE;
MYSQL_TIME l_time;
uint32 tmp;
int error;
@@ -5099,6 +5534,7 @@ int Field_date::store(double nr)
int Field_date::store(longlong nr, bool unsigned_val)
{
+ ASSERT_COLUMN_MARKED_FOR_WRITE;
MYSQL_TIME not_used;
int error;
longlong initial_nr= nr;
@@ -5150,6 +5586,7 @@ bool Field_date::send_binary(Protocol *protocol)
double Field_date::val_real(void)
{
+ ASSERT_COLUMN_MARKED_FOR_READ;
int32 j;
#ifdef WORDS_BIGENDIAN
if (table && table->s->db_low_byte_first)
@@ -5163,6 +5600,7 @@ double Field_date::val_real(void)
longlong Field_date::val_int(void)
{
+ ASSERT_COLUMN_MARKED_FOR_READ;
int32 j;
#ifdef WORDS_BIGENDIAN
if (table && table->s->db_low_byte_first)
@@ -5177,6 +5615,7 @@ longlong Field_date::val_int(void)
String *Field_date::val_str(String *val_buffer,
String *val_ptr __attribute__((unused)))
{
+ ASSERT_COLUMN_MARKED_FOR_READ;
MYSQL_TIME ltime;
val_buffer->alloc(field_length);
int32 tmp;
@@ -5202,7 +5641,7 @@ bool Field_date::get_time(MYSQL_TIME *ltime)
}
-int Field_date::cmp(const char *a_ptr, const char *b_ptr)
+int Field_date::cmp(const uchar *a_ptr, const uchar *b_ptr)
{
int32 a,b;
#ifdef WORDS_BIGENDIAN
@@ -5221,7 +5660,7 @@ int Field_date::cmp(const char *a_ptr, const char *b_ptr)
}
-void Field_date::sort_string(char *to,uint length __attribute__((unused)))
+void Field_date::sort_string(uchar *to,uint length __attribute__((unused)))
{
#ifdef WORDS_BIGENDIAN
if (!table || !table->s->db_low_byte_first)
@@ -5274,6 +5713,7 @@ void Field_date::sql_type(String &res) const
int Field_newdate::store(const char *from,uint len,CHARSET_INFO *cs)
{
+ ASSERT_COLUMN_MARKED_FOR_WRITE;
long tmp;
MYSQL_TIME l_time;
int error;
@@ -5323,6 +5763,7 @@ int Field_newdate::store(double nr)
int Field_newdate::store(longlong nr, bool unsigned_val)
{
+ ASSERT_COLUMN_MARKED_FOR_WRITE;
MYSQL_TIME l_time;
longlong tmp;
int error;
@@ -5356,8 +5797,9 @@ int Field_newdate::store(longlong nr, bool unsigned_val)
}
-int Field_newdate::store_time(MYSQL_TIME *ltime, timestamp_type time_type)
+int Field_newdate::store_time(MYSQL_TIME *ltime,timestamp_type time_type)
{
+ ASSERT_COLUMN_MARKED_FOR_WRITE;
long tmp;
int error= 0;
if (time_type == MYSQL_TIMESTAMP_DATE ||
@@ -5410,12 +5852,14 @@ bool Field_newdate::send_binary(Protocol *protocol)
double Field_newdate::val_real(void)
{
+ ASSERT_COLUMN_MARKED_FOR_READ;
return (double) Field_newdate::val_int();
}
longlong Field_newdate::val_int(void)
{
+ ASSERT_COLUMN_MARKED_FOR_READ;
ulong j= uint3korr(ptr);
j= (j % 32L)+(j / 32L % 16L)*100L + (j/(16L*32L))*10000L;
return (longlong) j;
@@ -5425,6 +5869,7 @@ longlong Field_newdate::val_int(void)
String *Field_newdate::val_str(String *val_buffer,
String *val_ptr __attribute__((unused)))
{
+ ASSERT_COLUMN_MARKED_FOR_READ;
val_buffer->alloc(field_length);
val_buffer->length(field_length);
uint32 tmp=(uint32) uint3korr(ptr);
@@ -5469,7 +5914,7 @@ bool Field_newdate::get_time(MYSQL_TIME *ltime)
}
-int Field_newdate::cmp(const char *a_ptr, const char *b_ptr)
+int Field_newdate::cmp(const uchar *a_ptr, const uchar *b_ptr)
{
uint32 a,b;
a=(uint32) uint3korr(a_ptr);
@@ -5478,7 +5923,7 @@ int Field_newdate::cmp(const char *a_ptr, const char *b_ptr)
}
-void Field_newdate::sort_string(char *to,uint length __attribute__((unused)))
+void Field_newdate::sort_string(uchar *to,uint length __attribute__((unused)))
{
to[0] = ptr[2];
to[1] = ptr[1];
@@ -5501,6 +5946,7 @@ void Field_newdate::sql_type(String &res) const
int Field_datetime::store(const char *from,uint len,CHARSET_INFO *cs)
{
+ ASSERT_COLUMN_MARKED_FOR_WRITE;
MYSQL_TIME time_tmp;
int error;
ulonglong tmp= 0;
@@ -5553,6 +5999,7 @@ int Field_datetime::store(double nr)
int Field_datetime::store(longlong nr, bool unsigned_val)
{
+ ASSERT_COLUMN_MARKED_FOR_WRITE;
MYSQL_TIME not_used;
int error;
longlong initial_nr= nr;
@@ -5590,10 +6037,11 @@ int Field_datetime::store(longlong nr, bool unsigned_val)
int Field_datetime::store_time(MYSQL_TIME *ltime,timestamp_type time_type)
{
+ ASSERT_COLUMN_MARKED_FOR_WRITE;
longlong tmp;
int error= 0;
/*
- We don't perform range checking here since values stored in MYSQL_TIME
+ We don't perform range checking here since values stored in TIME
structure always fit into DATETIME range.
*/
if (time_type == MYSQL_TIMESTAMP_DATE ||
@@ -5647,6 +6095,7 @@ double Field_datetime::val_real(void)
longlong Field_datetime::val_int(void)
{
+ ASSERT_COLUMN_MARKED_FOR_READ;
longlong j;
#ifdef WORDS_BIGENDIAN
if (table && table->s->db_low_byte_first)
@@ -5661,6 +6110,7 @@ longlong Field_datetime::val_int(void)
String *Field_datetime::val_str(String *val_buffer,
String *val_ptr __attribute__((unused)))
{
+ ASSERT_COLUMN_MARKED_FOR_READ;
val_buffer->alloc(field_length);
val_buffer->length(field_length);
ulonglong tmp;
@@ -5682,7 +6132,7 @@ String *Field_datetime::val_str(String *val_buffer,
part1=(long) (tmp/LL(1000000));
part2=(long) (tmp - (ulonglong) part1*LL(1000000));
- pos= (char*) val_buffer->ptr() + MAX_DATETIME_WIDTH;
+ pos=(char*) val_buffer->ptr() + MAX_DATETIME_WIDTH;
*pos--=0;
*pos--= (char) ('0'+(char) (part2%10)); part2/=10;
*pos--= (char) ('0'+(char) (part2%10)); part3= (int) (part2 / 10);
@@ -5730,7 +6180,7 @@ bool Field_datetime::get_time(MYSQL_TIME *ltime)
return Field_datetime::get_date(ltime,0);
}
-int Field_datetime::cmp(const char *a_ptr, const char *b_ptr)
+int Field_datetime::cmp(const uchar *a_ptr, const uchar *b_ptr)
{
longlong a,b;
#ifdef WORDS_BIGENDIAN
@@ -5749,7 +6199,7 @@ int Field_datetime::cmp(const char *a_ptr, const char *b_ptr)
((ulonglong) a > (ulonglong) b) ? 1 : 0;
}
-void Field_datetime::sort_string(char *to,uint length __attribute__((unused)))
+void Field_datetime::sort_string(uchar *to,uint length __attribute__((unused)))
{
#ifdef WORDS_BIGENDIAN
if (!table || !table->s->db_low_byte_first)
@@ -5798,6 +6248,7 @@ void Field_datetime::sql_type(String &res) const
well_formed_error_pos - where not well formed data was first met
cannot_convert_error_pos - where a not-convertable character was first met
end - end of the string
+ cs - character set of the string
NOTES
As of version 5.0 both cases return the same error:
@@ -5817,7 +6268,8 @@ static bool
check_string_copy_error(Field_str *field,
const char *well_formed_error_pos,
const char *cannot_convert_error_pos,
- const char *end)
+ const char *end,
+ CHARSET_INFO *cs)
{
const char *pos, *end_orig;
char tmp[64], *t;
@@ -5831,8 +6283,18 @@ check_string_copy_error(Field_str *field,
for (t= tmp; pos < end; pos++)
{
+ /*
+ If the source string is ASCII compatible (mbminlen==1)
+ and the source character is in ASCII printable range (0x20..0x7F),
+ then display the character as is.
+
+ Otherwise, if the source string is not ASCII compatible (e.g. UCS2),
+ or the source character is not in the printable range,
+ then print the character using HEX notation.
+ */
if (((unsigned char) *pos) >= 0x20 &&
- ((unsigned char) *pos) <= 0x7F)
+ ((unsigned char) *pos) <= 0x7F &&
+ cs->mbminlen == 1)
{
*t++= *pos;
}
@@ -5911,6 +6373,7 @@ Field_longstr::report_if_important_data(const char *ptr, const char *end,
int Field_string::store(const char *from,uint length,CHARSET_INFO *cs)
{
+ ASSERT_COLUMN_MARKED_FOR_WRITE;
uint copy_length;
const char *well_formed_error_pos;
const char *cannot_convert_error_pos;
@@ -5920,7 +6383,7 @@ int Field_string::store(const char *from,uint length,CHARSET_INFO *cs)
DBUG_ASSERT(table->in_use == current_thd);
copy_length= well_formed_copy_nchars(field_charset,
- ptr, field_length,
+ (char*) ptr, field_length,
cs, from, length,
field_length / field_charset->mbmaxlen,
&well_formed_error_pos,
@@ -5929,31 +6392,29 @@ int Field_string::store(const char *from,uint length,CHARSET_INFO *cs)
/* Append spaces if the string was shorter than the field. */
if (copy_length < field_length)
- field_charset->cset->fill(field_charset,ptr+copy_length,
+ field_charset->cset->fill(field_charset,(char*) ptr+copy_length,
field_length-copy_length,
field_charset->pad_char);
if (check_string_copy_error(this, well_formed_error_pos,
- cannot_convert_error_pos, from + length))
+ cannot_convert_error_pos, from + length, cs))
return 2;
return report_if_important_data(from_end_pos, from + length, FALSE);
}
-/*
+/**
Store double value in Field_string or Field_varstring.
- SYNOPSIS
- store(double nr)
- nr number
+ Pretty prints double number into field_length characters buffer.
- DESCRIPTION
- Pretty prints double number into field_length characters buffer.
+ @param nr number
*/
int Field_str::store(double nr)
{
+ ASSERT_COLUMN_MARKED_FOR_WRITE;
char buff[DOUBLE_TO_STRING_CONVERSION_BUFFER_SIZE];
uint length;
uint local_char_length= field_length / charset()->mbmaxlen;
@@ -6033,7 +6494,35 @@ int Field_str::store(double nr)
like inserting 500.0 in char(1)
*/
DBUG_ASSERT(local_char_length < 5 || length <= local_char_length+1);
- return store((const char *) buff, length, charset());
+ return store(buff, length, charset());
+}
+
+
+uint Field::is_equal(Create_field *new_field)
+{
+ return (new_field->sql_type == real_type());
+}
+
+
+/* If one of the fields is binary and the other one isn't return 1 else 0 */
+
+bool Field_str::compare_str_field_flags(Create_field *new_field, uint32 flag_arg)
+{
+ return (((new_field->flags & (BINCMP_FLAG | BINARY_FLAG)) &&
+ !(flag_arg & (BINCMP_FLAG | BINARY_FLAG))) ||
+ (!(new_field->flags & (BINCMP_FLAG | BINARY_FLAG)) &&
+ (flag_arg & (BINCMP_FLAG | BINARY_FLAG))));
+}
+
+
+uint Field_str::is_equal(Create_field *new_field)
+{
+ if (compare_str_field_flags(new_field, flags))
+ return 0;
+
+ return ((new_field->sql_type == real_type()) &&
+ new_field->charset == field_charset &&
+ new_field->length == max_display_length());
}
@@ -6056,22 +6545,29 @@ int Field_longstr::store_decimal(const my_decimal *d)
return store(str.ptr(), str.length(), str.charset());
}
+uint32 Field_longstr::max_data_length() const
+{
+ return field_length + (field_length > 255 ? 2 : 1);
+}
+
double Field_string::val_real(void)
{
+ ASSERT_COLUMN_MARKED_FOR_READ;
int error;
char *end;
CHARSET_INFO *cs= charset();
double result;
- result= my_strntod(cs,ptr,field_length,&end,&error);
+ result= my_strntod(cs,(char*) ptr,field_length,&end,&error);
if (!table->in_use->no_errors &&
- (error || (field_length != (uint32)(end - ptr) &&
- !check_if_only_end_space(cs, end, ptr + field_length))))
+ (error || (field_length != (uint32)(end - (char*) ptr) &&
+ !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(ptr, field_length, cs);
+ tmp.copy((char*) ptr, field_length, cs);
push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
ER_TRUNCATED_WRONG_VALUE,
ER(ER_TRUNCATED_WRONG_VALUE),
@@ -6083,19 +6579,21 @@ double Field_string::val_real(void)
longlong Field_string::val_int(void)
{
+ ASSERT_COLUMN_MARKED_FOR_READ;
int error;
char *end;
CHARSET_INFO *cs= charset();
longlong result;
- result= my_strntoll(cs,ptr,field_length,10,&end,&error);
+ result= my_strntoll(cs, (char*) ptr,field_length,10,&end,&error);
if (!table->in_use->no_errors &&
- (error || (field_length != (uint32)(end - ptr) &&
- !check_if_only_end_space(cs, end, ptr + field_length))))
+ (error || (field_length != (uint32)(end - (char*) ptr) &&
+ !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(ptr, field_length, cs);
+ tmp.copy((char*) ptr, field_length, cs);
push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
ER_TRUNCATED_WRONG_VALUE,
ER(ER_TRUNCATED_WRONG_VALUE),
@@ -6108,9 +6606,17 @@ longlong Field_string::val_int(void)
String *Field_string::val_str(String *val_buffer __attribute__((unused)),
String *val_ptr)
{
- uint length= field_charset->cset->lengthsp(field_charset, ptr, field_length);
+ ASSERT_COLUMN_MARKED_FOR_READ;
/* See the comment for Field_long::store(long long) */
DBUG_ASSERT(table->in_use == current_thd);
+ uint length;
+ if (table->in_use->variables.sql_mode &
+ MODE_PAD_CHAR_TO_FULL_LENGTH)
+ length= my_charpos(field_charset, ptr, ptr + field_length,
+ field_length / field_charset->mbmaxlen);
+ else
+ length= field_charset->cset->lengthsp(field_charset, (const char*) ptr,
+ field_length);
val_ptr->set((const char*) ptr, length, field_charset);
return val_ptr;
}
@@ -6118,14 +6624,15 @@ String *Field_string::val_str(String *val_buffer __attribute__((unused)),
my_decimal *Field_string::val_decimal(my_decimal *decimal_value)
{
- int err= str2my_decimal(E_DEC_FATAL_ERROR, ptr, field_length, charset(),
- decimal_value);
+ ASSERT_COLUMN_MARKED_FOR_READ;
+ int err= str2my_decimal(E_DEC_FATAL_ERROR, (char*) ptr, field_length,
+ charset(), decimal_value);
if (!table->in_use->no_errors && err)
{
char buf[DECIMAL_TO_STRING_CONVERSION_BUFFER_SIZE];
CHARSET_INFO *cs= charset();
String tmp(buf, sizeof(buf), cs);
- tmp.copy(ptr, field_length, cs);
+ tmp.copy((char*) ptr, field_length, cs);
push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
ER_TRUNCATED_WRONG_VALUE,
ER(ER_TRUNCATED_WRONG_VALUE),
@@ -6136,7 +6643,38 @@ my_decimal *Field_string::val_decimal(my_decimal *decimal_value)
}
-int Field_string::cmp(const char *a_ptr, const char *b_ptr)
+struct Check_field_param {
+ Field *field;
+};
+
+#ifdef HAVE_REPLICATION
+static bool
+check_field_for_37426(const void *param_arg)
+{
+ Check_field_param *param= (Check_field_param*) param_arg;
+ DBUG_ASSERT(param->field->real_type() == MYSQL_TYPE_STRING);
+ DBUG_PRINT("debug", ("Field %s - type: %d, size: %d",
+ param->field->field_name,
+ param->field->real_type(),
+ param->field->row_pack_length()));
+ return param->field->row_pack_length() > 255;
+}
+#endif
+
+int Field_string::compatible_field_size(uint field_metadata,
+ const Relay_log_info *rli_arg)
+{
+#ifdef HAVE_REPLICATION
+ const Check_field_param check_param = { this };
+ if (rpl_master_has_bug(rli_arg, 37426, TRUE,
+ check_field_for_37426, &check_param))
+ return FALSE; // Not compatible field sizes
+#endif
+ return Field::compatible_field_size(field_metadata, rli_arg);
+}
+
+
+int Field_string::cmp(const uchar *a_ptr, const uchar *b_ptr)
{
uint a_len, b_len;
@@ -6153,17 +6691,17 @@ int Field_string::cmp(const char *a_ptr, const char *b_ptr)
like in latin_de 'ae' and 0xe4
*/
return field_charset->coll->strnncollsp(field_charset,
- (const uchar*) a_ptr, a_len,
- (const uchar*) b_ptr, b_len,
+ a_ptr, a_len,
+ b_ptr, b_len,
0);
}
-void Field_string::sort_string(char *to,uint length)
+void Field_string::sort_string(uchar *to,uint length)
{
IF_DBUG(uint tmp=) my_strnxfrm(field_charset,
- (unsigned char *) to, length,
- (unsigned char *) ptr, field_length);
+ to, length,
+ ptr, field_length);
DBUG_ASSERT(tmp == length);
}
@@ -6188,7 +6726,9 @@ void Field_string::sql_type(String &res) const
}
-char *Field_string::pack(char *to, const char *from, uint max_length)
+uchar *Field_string::pack(uchar *to, const uchar *from,
+ uint max_length,
+ bool low_byte_first __attribute__((unused)))
{
uint length= min(field_length,max_length);
uint local_char_length= max_length/field_charset->mbmaxlen;
@@ -6196,32 +6736,120 @@ char *Field_string::pack(char *to, const char *from, uint max_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] == ' ')
+ while (length && from[length-1] == field_charset->pad_char)
length--;
- *to++= (char) (uchar) length;
+
+ // Length always stored little-endian
+ *to++= (uchar) length;
if (field_length > 255)
- *to++= (char) (uchar) (length >> 8);
+ *to++= (uchar) (length >> 8);
+
+ // Store the actual bytes of the string
memcpy(to, from, length);
return to+length;
}
-const char *Field_string::unpack(char *to, const char *from)
+/**
+ Unpack a string field from row data.
+
+ This method is used to unpack a string field from a master whose size
+ of the field is less than that of the slave. Note that there can be a
+ variety of field types represented with this class. Certain types like
+ ENUM or SET are processed differently. Hence, the upper byte of the
+ @c param_data argument contains the result of field->real_type() from
+ the master.
+
+ @note For information about how the length is packed, see @c
+ Field_string::do_save_field_metadata
+
+ @param to Destination of the data
+ @param from Source of the data
+ @param param_data Real type (upper) and length (lower) values
+
+ @return New pointer into memory based on from + length of the data
+*/
+const uchar *
+Field_string::unpack(uchar *to,
+ const uchar *from,
+ uint param_data,
+ bool low_byte_first __attribute__((unused)))
{
- uint length;
- if (field_length > 255)
+ uint from_length, length;
+
+ /*
+ Compute the declared length of the field on the master. This is
+ used to decide if one or two bytes should be read as length.
+ */
+ if (param_data)
+ from_length= (((param_data >> 4) & 0x300) ^ 0x300) + (param_data & 0x00ff);
+ else
+ from_length= field_length;
+
+ DBUG_PRINT("debug",
+ ("param_data: 0x%x, field_length: %u, from_length: %u",
+ param_data, field_length, from_length));
+ /*
+ Compute the actual length of the data by reading one or two bits
+ (depending on the declared field length on the master).
+ */
+ if (from_length > 255)
{
length= uint2korr(from);
from+= 2;
}
else
- length= (uint) (uchar) *from++;
- memcpy(to, from, (int) length);
- bfill(to+length, field_length - length, ' ');
+ length= (uint) *from++;
+
+ 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);
return from+length;
}
+/**
+ Save the field metadata for string fields.
+
+ Saves the real type in the first byte and the field length in the
+ second byte of the field metadata array at index of *metadata_ptr and
+ *(metadata_ptr + 1).
+
+ @note In order to be able to handle lengths exceeding 255 and be
+ backwards-compatible with pre-5.1.26 servers, an extra two bits of
+ the length has been added to the metadata in such a way that if
+ they are set, a new unrecognized type is generated. This will
+ cause pre-5.1-26 servers to stop due to a field type mismatch,
+ while new servers will be able to extract the extra bits. If the
+ length is <256, there will be no difference and both a new and an
+ old server will be able to handle it.
+
+ @note The extra two bits are added to bits 13 and 14 of the
+ parameter data (with 1 being the least siginficant bit and 16 the
+ most significant bit of the word) by xoring the extra length bits
+ with the real type. Since all allowable types have 0xF as most
+ significant bits of the metadata word, lengths <256 will not affect
+ the real type at all, while all other values will result in a
+ non-existant type in the range 17-244.
+
+ @see Field_string::unpack
+
+ @param metadata_ptr First byte of field metadata
+
+ @returns number of bytes written to metadata_ptr
+*/
+int Field_string::do_save_field_metadata(uchar *metadata_ptr)
+{
+ DBUG_ASSERT(field_length < 1024);
+ DBUG_ASSERT((real_type() & 0xF0) == 0xF0);
+ DBUG_PRINT("debug", ("field_length: %u, real_type: %u",
+ field_length, real_type()));
+ *metadata_ptr= (real_type() ^ ((field_length & 0x300) >> 4));
+ *(metadata_ptr + 1)= field_length & 0xFF;
+ return 2;
+}
+
+
/*
Compare two packed keys
@@ -6238,7 +6866,7 @@ const char *Field_string::unpack(char *to, const char *from)
> 0 a > b
*/
-int Field_string::pack_cmp(const char *a, const char *b, uint length,
+int Field_string::pack_cmp(const uchar *a, const uchar *b, uint length,
my_bool insert_or_update)
{
uint a_length, b_length;
@@ -6251,43 +6879,43 @@ int Field_string::pack_cmp(const char *a, const char *b, uint length,
}
else
{
- a_length= (uint) (uchar) *a++;
- b_length= (uint) (uchar) *b++;
+ a_length= (uint) *a++;
+ b_length= (uint) *b++;
}
return field_charset->coll->strnncollsp(field_charset,
- (const uchar*) a, a_length,
- (const uchar*) b, b_length,
+ a, a_length,
+ b, b_length,
insert_or_update);
}
-/*
- Compare a packed key against row
+/**
+ Compare a packed key against row.
- SYNOPSIS
- pack_cmp()
- key Original key
- length Key length. (May be less than field length)
- insert_or_update 1 if this is an insert or update
+ @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
+ @return
< 0 row < key
+ @return
0 row = key
+ @return
> 0 row > key
*/
-int Field_string::pack_cmp(const char *key, uint length,
+int Field_string::pack_cmp(const uchar *key, uint length,
my_bool insert_or_update)
{
uint row_length, local_key_length;
- char *end;
+ uchar *end;
if (length > 255)
{
local_key_length= uint2korr(key);
key+= 2;
}
else
- local_key_length= (uint) (uchar) *key++;
+ local_key_length= (uint) *key++;
/* Only use 'length' of key, not field_length */
end= ptr + length;
@@ -6296,17 +6924,17 @@ int Field_string::pack_cmp(const char *key, uint length,
row_length= (uint) (end - ptr);
return field_charset->coll->strnncollsp(field_charset,
- (const uchar*) ptr, row_length,
- (const uchar*) key, local_key_length,
+ ptr, row_length,
+ key, local_key_length,
insert_or_update);
}
-uint Field_string::packed_col_length(const char *data_ptr, uint length)
+uint Field_string::packed_col_length(const uchar *data_ptr, uint length)
{
if (length > 255)
return uint2korr(data_ptr)+2;
- return (uint) ((uchar) *data_ptr)+1;
+ return (uint) *data_ptr + 1;
}
@@ -6315,34 +6943,35 @@ uint Field_string::max_packed_col_length(uint max_length)
return (max_length > 255 ? 2 : 1)+max_length;
}
-uint Field_string::get_key_image(char *buff, uint length, imagetype type_arg)
+
+uint Field_string::get_key_image(uchar *buff, uint length, imagetype type_arg)
{
- uint bytes = my_charpos(field_charset, ptr, ptr + field_length,
+ uint bytes = my_charpos(field_charset, (char*) ptr,
+ (char*) ptr + field_length,
length / field_charset->mbmaxlen);
memcpy(buff, ptr, bytes);
if (bytes < length)
- field_charset->cset->fill(field_charset, buff + bytes, length - bytes,
- field_charset->pad_char);
+ field_charset->cset->fill(field_charset, (char*) buff + bytes,
+ length - bytes, field_charset->pad_char);
return bytes;
}
+
Field *Field_string::new_field(MEM_ROOT *root, struct st_table *new_table,
bool keep_type)
{
Field *field;
-
if (type() != MYSQL_TYPE_VAR_STRING || keep_type)
field= Field::new_field(root, new_table, keep_type);
- else
+ else if ((field= new Field_varstring(field_length, maybe_null(), field_name,
+ new_table->s, charset())))
{
-
/*
Old VARCHAR field which should be modified to a VARCHAR on copy
This is done to ensure that ALTER TABLE will convert old VARCHAR fields
to now VARCHAR fields.
*/
- field= new Field_varstring(field_length, maybe_null(),
- field_name, new_table, charset());
+ field->init(new_table);
/*
Normally orig_table is different from table only if field was created
via ::new_field. Here we alter the type of field, so ::new_field is
@@ -6354,6 +6983,7 @@ Field *Field_string::new_field(MEM_ROOT *root, struct st_table *new_table,
return field;
}
+
/****************************************************************************
VARCHAR type
Data in field->ptr is stored as:
@@ -6372,15 +7002,36 @@ Field *Field_string::new_field(MEM_ROOT *root, struct st_table *new_table,
const uint Field_varstring::MAX_SIZE= UINT_MAX16;
+/**
+ Save the field metadata for varstring fields.
+
+ Saves the field length in the first byte. Note: may consume
+ 2 bytes. Caller must ensure second byte is contiguous with
+ first byte (e.g. array index 0,1).
+
+ @param metadata_ptr First byte of field metadata
+
+ @returns number of bytes written to metadata_ptr
+*/
+int Field_varstring::do_save_field_metadata(uchar *metadata_ptr)
+{
+ char *ptr= (char *)metadata_ptr;
+ DBUG_ASSERT(field_length <= 65535);
+ int2store(ptr, field_length);
+ return 2;
+}
+
int Field_varstring::store(const char *from,uint length,CHARSET_INFO *cs)
{
+ ASSERT_COLUMN_MARKED_FOR_WRITE;
uint copy_length;
const char *well_formed_error_pos;
const char *cannot_convert_error_pos;
const char *from_end_pos;
copy_length= well_formed_copy_nchars(field_charset,
- ptr + length_bytes, field_length,
+ (char*) ptr + length_bytes,
+ field_length,
cs, from, length,
field_length / field_charset->mbmaxlen,
&well_formed_error_pos,
@@ -6393,7 +7044,7 @@ int Field_varstring::store(const char *from,uint length,CHARSET_INFO *cs)
int2store(ptr, copy_length);
if (check_string_copy_error(this, well_formed_error_pos,
- cannot_convert_error_pos, from + length))
+ cannot_convert_error_pos, from + length, cs))
return 2;
return report_if_important_data(from_end_pos, from + length, TRUE);
@@ -6416,27 +7067,30 @@ 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;
- uint length= length_bytes == 1 ? (uint) (uchar) *ptr : uint2korr(ptr);
- return my_strntod(field_charset, ptr+length_bytes, length, &end_not_used,
- &not_used);
+ uint length= length_bytes == 1 ? (uint) *ptr : uint2korr(ptr);
+ return my_strntod(field_charset, (char*) ptr+length_bytes, length,
+ &end_not_used, &not_used);
}
longlong Field_varstring::val_int(void)
{
+ ASSERT_COLUMN_MARKED_FOR_READ;
int not_used;
char *end_not_used;
- uint length= length_bytes == 1 ? (uint) (uchar) *ptr : uint2korr(ptr);
- return my_strntoll(field_charset, ptr+length_bytes, length, 10,
+ 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);
}
String *Field_varstring::val_str(String *val_buffer __attribute__((unused)),
String *val_ptr)
{
- uint length= length_bytes == 1 ? (uint) (uchar) *ptr : uint2korr(ptr);
+ ASSERT_COLUMN_MARKED_FOR_READ;
+ uint length= length_bytes == 1 ? (uint) *ptr : uint2korr(ptr);
val_ptr->set((const char*) ptr+length_bytes, length, field_charset);
return val_ptr;
}
@@ -6444,84 +7098,87 @@ String *Field_varstring::val_str(String *val_buffer __attribute__((unused)),
my_decimal *Field_varstring::val_decimal(my_decimal *decimal_value)
{
- uint length= length_bytes == 1 ? (uint) (uchar) *ptr : uint2korr(ptr);
- str2my_decimal(E_DEC_FATAL_ERROR, ptr+length_bytes, length, charset(),
- decimal_value);
+ ASSERT_COLUMN_MARKED_FOR_READ;
+ uint length= length_bytes == 1 ? (uint) *ptr : uint2korr(ptr);
+ str2my_decimal(E_DEC_FATAL_ERROR, (char*) ptr+length_bytes, length,
+ charset(), decimal_value);
return decimal_value;
}
-int Field_varstring::cmp(const char *a_ptr, const char *b_ptr)
+int Field_varstring::cmp_max(const uchar *a_ptr, const uchar *b_ptr,
+ uint max_len)
{
uint a_length, b_length;
int diff;
if (length_bytes == 1)
{
- a_length= (uint) (uchar) *a_ptr;
- b_length= (uint) (uchar) *b_ptr;
+ a_length= (uint) *a_ptr;
+ b_length= (uint) *b_ptr;
}
else
{
a_length= uint2korr(a_ptr);
b_length= uint2korr(b_ptr);
}
+ set_if_smaller(a_length, max_len);
+ set_if_smaller(b_length, max_len);
diff= field_charset->coll->strnncollsp(field_charset,
- (const uchar*) a_ptr+
+ a_ptr+
length_bytes,
a_length,
- (const uchar*) b_ptr+
+ b_ptr+
length_bytes,
b_length,0);
return diff;
}
-/*
- NOTE: varstring and blob keys are ALWAYS stored with a 2 byte length prefix
+/**
+ @note
+ varstring and blob keys are ALWAYS stored with a 2 byte length prefix
*/
-int Field_varstring::key_cmp(const byte *key_ptr, uint max_key_length)
+int Field_varstring::key_cmp(const uchar *key_ptr, uint max_key_length)
{
- uint length= length_bytes == 1 ? (uint) (uchar) *ptr : uint2korr(ptr);
+ uint length= length_bytes == 1 ? (uint) *ptr : uint2korr(ptr);
uint local_char_length= max_key_length / field_charset->mbmaxlen;
local_char_length= my_charpos(field_charset, ptr + length_bytes,
ptr + length_bytes + length, local_char_length);
set_if_smaller(length, local_char_length);
return field_charset->coll->strnncollsp(field_charset,
- (const uchar*) ptr + length_bytes,
+ ptr + length_bytes,
length,
- (const uchar*) key_ptr+
+ key_ptr+
HA_KEY_BLOB_LENGTH,
uint2korr(key_ptr), 0);
}
-/*
- Compare to key segments (always 2 byte length prefix)
+/**
+ Compare to key segments (always 2 byte length prefix).
- NOTE
+ @note
This is used only to compare key segments created for index_read().
(keys are created and compared in key.cc)
*/
-int Field_varstring::key_cmp(const byte *a,const byte *b)
+int Field_varstring::key_cmp(const uchar *a,const uchar *b)
{
return field_charset->coll->strnncollsp(field_charset,
- (const uchar*) a +
- HA_KEY_BLOB_LENGTH,
+ a + HA_KEY_BLOB_LENGTH,
uint2korr(a),
- (const uchar*) b +
- HA_KEY_BLOB_LENGTH,
+ b + HA_KEY_BLOB_LENGTH,
uint2korr(b),
0);
}
-void Field_varstring::sort_string(char *to,uint length)
+void Field_varstring::sort_string(uchar *to,uint length)
{
- uint tot_length= length_bytes == 1 ? (uint) (uchar) *ptr : uint2korr(ptr);
+ uint tot_length= length_bytes == 1 ? (uint) *ptr : uint2korr(ptr);
if (field_charset == &my_charset_bin)
{
@@ -6534,8 +7191,7 @@ void Field_varstring::sort_string(char *to,uint length)
}
tot_length= my_strnxfrm(field_charset,
- (uchar*) to, length,
- (uchar*) ptr + length_bytes,
+ to, length, ptr + length_bytes,
tot_length);
DBUG_ASSERT(tot_length == length);
}
@@ -6570,9 +7226,9 @@ void Field_varstring::sql_type(String &res) const
}
-uint32 Field_varstring::data_length(const char *from)
+uint32 Field_varstring::data_length()
{
- return length_bytes == 1 ? (uint) (uchar) *ptr : uint2korr(ptr);
+ return length_bytes == 1 ? (uint32) *ptr : uint2korr(ptr);
}
/*
@@ -6580,24 +7236,32 @@ uint32 Field_varstring::data_length(const char *from)
Here the number of length bytes are depending on the given max_length
*/
-char *Field_varstring::pack(char *to, const char *from, uint max_length)
+uchar *Field_varstring::pack(uchar *to, const uchar *from,
+ uint max_length,
+ bool low_byte_first __attribute__((unused)))
{
- uint length= length_bytes == 1 ? (uint) (uchar) *from : uint2korr(from);
+ uint length= length_bytes == 1 ? (uint) *from : uint2korr(from);
set_if_smaller(max_length, field_length);
if (length > max_length)
length=max_length;
- *to++= (char) (length & 255);
+
+ /* Length always stored little-endian */
+ *to++= length & 0xFF;
if (max_length > 255)
- *to++= (char) (length >> 8);
- if (length)
+ *to++= (length >> 8) & 0xFF;
+
+ /* Store bytes of string */
+ if (length > 0)
memcpy(to, from+length_bytes, length);
return to+length;
}
-char *Field_varstring::pack_key(char *to, const char *key, uint max_length)
+uchar *
+Field_varstring::pack_key(uchar *to, const uchar *key, uint max_length,
+ bool low_byte_first __attribute__((unused)))
{
- uint length= length_bytes == 1 ? (uint) (uchar) *key : uint2korr(key);
+ 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;
@@ -6616,31 +7280,29 @@ char *Field_varstring::pack_key(char *to, const char *key, uint max_length)
}
-/*
+/**
Unpack a key into a record buffer.
- SYNOPSIS
- unpack_key()
- to Pointer into the record buffer.
- key Pointer to the packed key.
- max_length Key length limit from key description.
+ 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'.
- DESCRIPTION
- 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
+ @return
Pointer to end of 'key' (To the next key part if multi-segment key)
*/
-const char *Field_varstring::unpack_key(char *to, const char *key,
- uint max_length)
+const uchar *
+Field_varstring::unpack_key(uchar *to, const uchar *key, uint max_length,
+ bool low_byte_first __attribute__((unused)))
{
/* get length of the blob key */
- uint32 length= *((uchar*) key++);
+ uint32 length= *key++;
if (max_length > 255)
- length+= (*((uchar*) key++)) << 8;
+ length+= (*key++) << 8;
/* put the length into the record buffer */
if (length_bytes == 1)
@@ -6651,21 +7313,20 @@ const char *Field_varstring::unpack_key(char *to, const char *key,
return key + length;
}
-/*
- Create a packed key that will be used for storage in the index tree
+/**
+ Create a packed key that will be used for storage in the index tree.
- SYNOPSIS
- pack_key_from_key_image()
- to Store packed key segment here
- from Key segment (as given to index_read())
- max_length Max length of key
+ @param to Store packed key segment here
+ @param from Key segment (as given to index_read())
+ @param max_length Max length of key
- RETURN
+ @return
end of key storage
*/
-char *Field_varstring::pack_key_from_key_image(char *to, const char *from,
- uint max_length)
+uchar *
+Field_varstring::pack_key_from_key_image(uchar *to, const uchar *from, uint max_length,
+ bool low_byte_first __attribute__((unused)))
{
/* Key length is always stored as 2 bytes */
uint length= uint2korr(from);
@@ -6680,16 +7341,37 @@ char *Field_varstring::pack_key_from_key_image(char *to, const char *from,
}
-/*
- unpack field packed with Field_varstring::pack()
-*/
+/**
+ Unpack a varstring field from row data.
+
+ This method is used to unpack a varstring field from a master
+ whose size of the field is less than that of the slave.
-const char *Field_varstring::unpack(char *to, const char *from)
+ @note
+ The string length is always packed little-endian.
+
+ @param to Destination of the data
+ @param from Source of the data
+ @param param_data Length bytes from the master's field data
+
+ @return New pointer into memory based on from + length of the data
+*/
+const uchar *
+Field_varstring::unpack(uchar *to, const uchar *from,
+ uint param_data,
+ bool low_byte_first __attribute__((unused)))
{
uint length;
- if (length_bytes == 1)
- length= (uint) (uchar) (*to= *from++);
- else
+ uint l_bytes= (param_data && (param_data < field_length)) ?
+ (param_data <= 255) ? 1 : 2 : length_bytes;
+ if (l_bytes == 1)
+ {
+ to[0]= *from++;
+ length= to[0];
+ if (length_bytes == 2)
+ to[1]= 0;
+ }
+ else /* l_bytes == 2 */
{
length= uint2korr(from);
to[0]= *from++;
@@ -6701,7 +7383,7 @@ const char *Field_varstring::unpack(char *to, const char *from)
}
-int Field_varstring::pack_cmp(const char *a, const char *b,
+int Field_varstring::pack_cmp(const uchar *a, const uchar *b,
uint key_length_arg,
my_bool insert_or_update)
{
@@ -6713,21 +7395,21 @@ int Field_varstring::pack_cmp(const char *a, const char *b,
}
else
{
- a_length= (uint) (uchar) *a++;
- b_length= (uint) (uchar) *b++;
+ a_length= (uint) *a++;
+ b_length= (uint) *b++;
}
return field_charset->coll->strnncollsp(field_charset,
- (const uchar*) a, a_length,
- (const uchar*) b, b_length,
+ a, a_length,
+ b, b_length,
insert_or_update);
}
-int Field_varstring::pack_cmp(const char *b, uint key_length_arg,
+int Field_varstring::pack_cmp(const uchar *b, uint key_length_arg,
my_bool insert_or_update)
{
- char *a= ptr+ length_bytes;
- uint a_length= length_bytes == 1 ? (uint) (uchar) *ptr : uint2korr(ptr);
+ 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 :
@@ -6738,7 +7420,7 @@ int Field_varstring::pack_cmp(const char *b, uint key_length_arg,
b_length=uint2korr(b); b+= HA_KEY_BLOB_LENGTH;
}
else
- b_length= (uint) (uchar) *b++;
+ b_length= (uint) *b++;
if (a_length > local_char_length)
{
@@ -6748,18 +7430,17 @@ int Field_varstring::pack_cmp(const char *b, uint key_length_arg,
}
return field_charset->coll->strnncollsp(field_charset,
- (const uchar*) a,
- a_length,
- (const uchar*) b, b_length,
+ a, a_length,
+ b, b_length,
insert_or_update);
}
-uint Field_varstring::packed_col_length(const char *data_ptr, uint length)
+uint Field_varstring::packed_col_length(const uchar *data_ptr, uint length)
{
if (length > 255)
return uint2korr(data_ptr)+2;
- return (uint) ((uchar) *data_ptr)+1;
+ return (uint) *data_ptr + 1;
}
@@ -6768,11 +7449,11 @@ uint Field_varstring::max_packed_col_length(uint max_length)
return (max_length > 255 ? 2 : 1)+max_length;
}
-uint Field_varstring::get_key_image(char *buff, uint length, imagetype type)
+uint Field_varstring::get_key_image(uchar *buff, uint length, imagetype type)
{
- uint f_length= length_bytes == 1 ? (uint) (uchar) *ptr : uint2korr(ptr);
+ uint f_length= length_bytes == 1 ? (uint) *ptr : uint2korr(ptr);
uint local_char_length= length / field_charset->mbmaxlen;
- char *pos= ptr+length_bytes;
+ uchar *pos= ptr+length_bytes;
local_char_length= my_charpos(field_charset, pos, pos + f_length,
local_char_length);
set_if_smaller(f_length, local_char_length);
@@ -6791,23 +7472,23 @@ uint Field_varstring::get_key_image(char *buff, uint length, imagetype type)
}
-void Field_varstring::set_key_image(char *buff,uint length)
+void Field_varstring::set_key_image(const uchar *buff,uint length)
{
length= uint2korr(buff); // Real length is here
- (void) Field_varstring::store(buff+HA_KEY_BLOB_LENGTH, length,
+ (void) Field_varstring::store((const char*) buff+HA_KEY_BLOB_LENGTH, length,
field_charset);
}
-int Field_varstring::cmp_binary(const char *a_ptr, const char *b_ptr,
+int Field_varstring::cmp_binary(const uchar *a_ptr, const uchar *b_ptr,
uint32 max_length)
{
uint32 a_length,b_length;
if (length_bytes == 1)
{
- a_length= (uint) (uchar) *a_ptr;
- b_length= (uint) (uchar) *b_ptr;
+ a_length= (uint) *a_ptr;
+ b_length= (uint) *b_ptr;
}
else
{
@@ -6835,7 +7516,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,
- char *new_ptr, uchar *new_null_ptr,
+ uchar *new_ptr, uchar *new_null_ptr,
uint new_null_bit)
{
Field_varstring *res;
@@ -6852,72 +7533,103 @@ Field *Field_varstring::new_key_field(MEM_ROOT *root,
}
+uint Field_varstring::is_equal(Create_field *new_field)
+{
+ if (new_field->sql_type == real_type() &&
+ new_field->charset == field_charset)
+ {
+ if (new_field->length == max_display_length())
+ return IS_EQUAL_YES;
+ if (new_field->length > max_display_length() &&
+ ((new_field->length <= 255 && max_display_length() <= 255) ||
+ (new_field->length > 255 && max_display_length() > 255)))
+ return IS_EQUAL_PACK_LENGTH; // VARCHAR, longer variable length
+ }
+ return IS_EQUAL_NO;
+}
+
+
+void Field_varstring::hash(ulong *nr, ulong *nr2)
+{
+ if (is_null())
+ {
+ *nr^= (*nr << 1) | 1;
+ }
+ else
+ {
+ uint len= length_bytes == 1 ? (uint) *ptr : uint2korr(ptr);
+ CHARSET_INFO *cs= charset();
+ cs->coll->hash_sort(cs, ptr + length_bytes, len, nr, nr2);
+ }
+}
+
+
/****************************************************************************
** blob type
** A blob is saved as a length and a pointer. The length is stored in the
** packlength slot and may be from 1-4.
****************************************************************************/
-Field_blob::Field_blob(char *ptr_arg, uchar *null_ptr_arg, uchar null_bit_arg,
+Field_blob::Field_blob(uchar *ptr_arg, uchar *null_ptr_arg, uchar null_bit_arg,
enum utype unireg_check_arg, const char *field_name_arg,
- struct st_table *table_arg,uint blob_pack_length,
+ TABLE_SHARE *share, uint blob_pack_length,
CHARSET_INFO *cs)
:Field_longstr(ptr_arg, BLOB_PACK_LENGTH_TO_MAX_LENGH(blob_pack_length),
- null_ptr_arg, null_bit_arg, unireg_check_arg, field_name_arg,
- table_arg, cs),
+ null_ptr_arg, null_bit_arg, unireg_check_arg, field_name_arg,
+ cs),
packlength(blob_pack_length)
{
flags|= BLOB_FLAG;
- if (table)
- {
- table->s->blob_fields++;
- /* TODO: why do not fill table->s->blob_field array here? */
- }
+ share->blob_fields++;
+ /* TODO: why do not fill table->s->blob_field array here? */
}
-void Field_blob::store_length(uint32 number)
+void Field_blob::store_length(uchar *i_ptr,
+ uint i_packlength,
+ uint32 i_number,
+ bool low_byte_first)
{
- switch (packlength) {
+ switch (i_packlength) {
case 1:
- ptr[0]= (uchar) number;
+ i_ptr[0]= (uchar) i_number;
break;
case 2:
#ifdef WORDS_BIGENDIAN
- if (table->s->db_low_byte_first)
+ if (low_byte_first)
{
- int2store(ptr,(unsigned short) number);
+ int2store(i_ptr,(unsigned short) i_number);
}
else
#endif
- shortstore(ptr,(unsigned short) number);
+ shortstore(i_ptr,(unsigned short) i_number);
break;
case 3:
- int3store(ptr,number);
+ int3store(i_ptr,i_number);
break;
case 4:
#ifdef WORDS_BIGENDIAN
- if (table->s->db_low_byte_first)
+ if (low_byte_first)
{
- int4store(ptr,number);
+ int4store(i_ptr,i_number);
}
else
#endif
- longstore(ptr,number);
+ longstore(i_ptr,i_number);
}
}
-uint32 Field_blob::get_length(const char *pos)
+uint32 Field_blob::get_length(const uchar *pos, uint packlength_arg, bool low_byte_first)
{
- switch (packlength) {
+ switch (packlength_arg) {
case 1:
- return (uint32) (uchar) pos[0];
+ return (uint32) pos[0];
case 2:
{
uint16 tmp;
#ifdef WORDS_BIGENDIAN
- if (table->s->db_low_byte_first)
+ if (low_byte_first)
tmp=sint2korr(pos);
else
#endif
@@ -6930,7 +7642,7 @@ uint32 Field_blob::get_length(const char *pos)
{
uint32 tmp;
#ifdef WORDS_BIGENDIAN
- if (table->s->db_low_byte_first)
+ if (low_byte_first)
tmp=uint4korr(pos);
else
#endif
@@ -6943,24 +7655,18 @@ uint32 Field_blob::get_length(const char *pos)
}
-/*
+/**
Put a blob length field into a record buffer.
- SYNOPSIS
- Field_blob::put_length()
- pos Pointer into the record buffer.
- length The length value to put.
+ 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'.
- DESCRIPTION
- 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'.
-
- RETURN
- nothing
+ @param pos Pointer into the record buffer.
+ @param length The length value to put.
*/
-void Field_blob::put_length(char *pos, uint32 length)
+void Field_blob::put_length(uchar *pos, uint32 length)
{
switch (packlength) {
case 1:
@@ -6981,6 +7687,7 @@ void Field_blob::put_length(char *pos, uint32 length)
int Field_blob::store(const char *from,uint length,CHARSET_INFO *cs)
{
+ ASSERT_COLUMN_MARKED_FOR_WRITE;
uint copy_length, new_length;
const char *well_formed_error_pos;
const char *cannot_convert_error_pos;
@@ -7022,6 +7729,17 @@ int Field_blob::store(const char *from,uint length,CHARSET_INFO *cs)
if (value.alloc(new_length))
goto oom_error;
+
+ if (f_is_hex_escape(flags))
+ {
+ copy_length= my_copy_with_hex_escaping(field_charset,
+ (char*) value.ptr(), new_length,
+ from, length);
+ Field_blob::store_length(copy_length);
+ tmp= value.ptr();
+ bmove(ptr + packlength, (uchar*) &tmp, sizeof(char*));
+ return 0;
+ }
/*
"length" is OK as "nchars" argument to well_formed_copy_nchars as this
is never used to limit the length of the data. The cut of long data
@@ -7037,10 +7755,10 @@ int Field_blob::store(const char *from,uint length,CHARSET_INFO *cs)
Field_blob::store_length(copy_length);
tmp= value.ptr();
- bmove(ptr+packlength,(char*) &tmp,sizeof(char*));
+ bmove(ptr+packlength,(uchar*) &tmp,sizeof(char*));
if (check_string_copy_error(this, well_formed_error_pos,
- cannot_convert_error_pos, from + length))
+ cannot_convert_error_pos, from + length, cs))
return 2;
return report_if_important_data(from_end_pos, from + length, TRUE);
@@ -7055,7 +7773,7 @@ oom_error:
int Field_blob::store(double nr)
{
CHARSET_INFO *cs=charset();
- value.set(nr, 2, cs);
+ value.set_real(nr, 2, cs);
return Field_blob::store(value.ptr(),(uint) value.length(), cs);
}
@@ -7063,16 +7781,14 @@ int Field_blob::store(double nr)
int Field_blob::store(longlong nr, bool unsigned_val)
{
CHARSET_INFO *cs=charset();
- if (unsigned_val)
- value.set((ulonglong) nr, cs);
- else
- value.set(nr, cs);
+ value.set_int(nr, unsigned_val, cs);
return Field_blob::store(value.ptr(), (uint) value.length(), cs);
}
double Field_blob::val_real(void)
{
+ ASSERT_COLUMN_MARKED_FOR_READ;
int not_used;
char *end_not_used, *blob;
uint32 length;
@@ -7089,6 +7805,7 @@ double Field_blob::val_real(void)
longlong Field_blob::val_int(void)
{
+ ASSERT_COLUMN_MARKED_FOR_READ;
int not_used;
char *blob;
memcpy_fixed(&blob,ptr+packlength,sizeof(char*));
@@ -7101,6 +7818,7 @@ longlong Field_blob::val_int(void)
String *Field_blob::val_str(String *val_buffer __attribute__((unused)),
String *val_ptr)
{
+ ASSERT_COLUMN_MARKED_FOR_READ;
char *blob;
memcpy_fixed(&blob,ptr+packlength,sizeof(char*));
if (!blob)
@@ -7113,37 +7831,47 @@ String *Field_blob::val_str(String *val_buffer __attribute__((unused)),
my_decimal *Field_blob::val_decimal(my_decimal *decimal_value)
{
+ ASSERT_COLUMN_MARKED_FOR_READ;
const char *blob;
- memcpy_fixed(&blob, ptr+packlength, sizeof(const char*));
+ size_t length;
+ memcpy_fixed(&blob, ptr+packlength, sizeof(const uchar*));
if (!blob)
+ {
blob= "";
- str2my_decimal(E_DEC_FATAL_ERROR, blob, get_length(ptr), charset(),
+ length= 0;
+ }
+ else
+ length= get_length(ptr);
+
+ str2my_decimal(E_DEC_FATAL_ERROR, blob, length, charset(),
decimal_value);
return decimal_value;
}
-int Field_blob::cmp(const char *a,uint32 a_length, const char *b,
+int Field_blob::cmp(const uchar *a,uint32 a_length, const uchar *b,
uint32 b_length)
{
return field_charset->coll->strnncollsp(field_charset,
- (const uchar*)a, a_length,
- (const uchar*)b, b_length,
+ a, a_length, b, b_length,
0);
}
-int Field_blob::cmp(const char *a_ptr, const char *b_ptr)
+int Field_blob::cmp_max(const uchar *a_ptr, const uchar *b_ptr,
+ uint max_length)
{
- char *blob1,*blob2;
+ uchar *blob1,*blob2;
memcpy_fixed(&blob1,a_ptr+packlength,sizeof(char*));
memcpy_fixed(&blob2,b_ptr+packlength,sizeof(char*));
- return Field_blob::cmp(blob1,get_length(a_ptr),
- blob2,get_length(b_ptr));
+ 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);
+ return Field_blob::cmp(blob1,a_len,blob2,b_len);
}
-int Field_blob::cmp_binary(const char *a_ptr, const char *b_ptr,
+int Field_blob::cmp_binary(const uchar *a_ptr, const uchar *b_ptr,
uint32 max_length)
{
char *a,*b;
@@ -7164,10 +7892,10 @@ int Field_blob::cmp_binary(const char *a_ptr, const char *b_ptr,
/* The following is used only when comparing a key */
-uint Field_blob::get_key_image(char *buff,uint length, imagetype type_arg)
+uint Field_blob::get_key_image(uchar *buff,uint length, imagetype type_arg)
{
uint32 blob_length= get_length(ptr);
- char *blob;
+ uchar *blob;
#ifdef HAVE_SPATIAL
if (type_arg == itMBR)
@@ -7184,7 +7912,7 @@ uint Field_blob::get_key_image(char *buff,uint length, imagetype type_arg)
return image_length;
}
get_ptr(&blob);
- gobj= Geometry::construct(&buffer, blob, blob_length);
+ gobj= Geometry::construct(&buffer, (char*) blob, blob_length);
if (!gobj || gobj->get_mbr(&mbr, &dummy))
bzero(buff, image_length);
else
@@ -7219,16 +7947,17 @@ uint Field_blob::get_key_image(char *buff,uint length, imagetype type_arg)
}
-void Field_blob::set_key_image(char *buff,uint length)
+void Field_blob::set_key_image(const uchar *buff,uint length)
{
length= uint2korr(buff);
- (void) Field_blob::store(buff+HA_KEY_BLOB_LENGTH, length, field_charset);
+ (void) Field_blob::store((const char*) buff+HA_KEY_BLOB_LENGTH, length,
+ field_charset);
}
-int Field_blob::key_cmp(const byte *key_ptr, uint max_key_length)
+int Field_blob::key_cmp(const uchar *key_ptr, uint max_key_length)
{
- char *blob1;
+ uchar *blob1;
uint blob_length=get_length(ptr);
memcpy_fixed(&blob1,ptr+packlength,sizeof(char*));
CHARSET_INFO *cs= charset();
@@ -7237,14 +7966,31 @@ int Field_blob::key_cmp(const byte *key_ptr, uint max_key_length)
local_char_length);
set_if_smaller(blob_length, local_char_length);
return Field_blob::cmp(blob1, blob_length,
- (char*) key_ptr+HA_KEY_BLOB_LENGTH,
+ key_ptr+HA_KEY_BLOB_LENGTH,
uint2korr(key_ptr));
}
-int Field_blob::key_cmp(const byte *a,const byte *b)
+int Field_blob::key_cmp(const uchar *a,const uchar *b)
{
- return Field_blob::cmp((char*) a+HA_KEY_BLOB_LENGTH, uint2korr(a),
- (char*) b+HA_KEY_BLOB_LENGTH, uint2korr(b));
+ return Field_blob::cmp(a+HA_KEY_BLOB_LENGTH, uint2korr(a),
+ b+HA_KEY_BLOB_LENGTH, uint2korr(b));
+}
+
+
+/**
+ Save the field metadata for blob fields.
+
+ Saves the pack length in the first byte of the field metadata array
+ at index of *metadata_ptr.
+
+ @param metadata_ptr First byte of field metadata
+
+ @returns number of bytes written to metadata_ptr
+*/
+int Field_blob::do_save_field_metadata(uchar *metadata_ptr)
+{
+ *metadata_ptr= pack_length_no_ptr();
+ return 1;
}
@@ -7255,9 +8001,9 @@ uint32 Field_blob::sort_length() const
}
-void Field_blob::sort_string(char *to,uint length)
+void Field_blob::sort_string(uchar *to,uint length)
{
- char *blob;
+ uchar *blob;
uint blob_length=get_length();
if (!blob_length)
@@ -7266,7 +8012,7 @@ void Field_blob::sort_string(char *to,uint length)
{
if (field_charset == &my_charset_bin)
{
- char *pos;
+ uchar *pos;
/*
Store length of blob last in blob to shorter blobs before longer blobs
@@ -7292,8 +8038,7 @@ void Field_blob::sort_string(char *to,uint length)
memcpy_fixed(&blob,ptr+packlength,sizeof(char*));
blob_length=my_strnxfrm(field_charset,
- (uchar*) to, length,
- (uchar*) blob, blob_length);
+ to, length, blob, blob_length);
DBUG_ASSERT(blob_length == length);
}
}
@@ -7318,46 +8063,80 @@ void Field_blob::sql_type(String &res) const
}
}
-
-char *Field_blob::pack(char *to, const char *from, uint max_length)
+uchar *Field_blob::pack(uchar *to, const uchar *from,
+ uint max_length, bool low_byte_first)
{
- char *save=ptr;
- ptr=(char*) from;
+ DBUG_ENTER("Field_blob::pack");
+ DBUG_PRINT("enter", ("to: 0x%lx; from: 0x%lx;"
+ " max_length: %u; low_byte_first: %d",
+ (ulong) to, (ulong) from,
+ max_length, low_byte_first));
+ DBUG_DUMP("record", from, table->s->reclength);
+ uchar *save= ptr;
+ ptr= (uchar*) from;
uint32 length=get_length(); // Length of from string
- if (length > max_length)
- {
- ptr=to;
- length=max_length;
- store_length(length); // Store max length
- ptr=(char*) from;
- }
- else
- memcpy(to,from,packlength); // Copy length
- if (length)
+
+ /*
+ Store max length, which will occupy packlength bytes. If the max
+ length given is smaller than the actual length of the blob, we
+ just store the initial bytes of the blob.
+ */
+ store_length(to, packlength, min(length, max_length), low_byte_first);
+
+ /*
+ Store the actual blob data, which will occupy 'length' bytes.
+ */
+ if (length > 0)
{
- get_ptr((char**) &from);
+ get_ptr((uchar**) &from);
memcpy(to+packlength, from,length);
}
ptr=save; // Restore org row pointer
- return to+packlength+length;
+ DBUG_DUMP("packed", to, packlength + length);
+ DBUG_RETURN(to+packlength+length);
}
-const char *Field_blob::unpack(char *to, const char *from)
-{
- memcpy(to,from,packlength);
- uint32 length=get_length(from);
- from+=packlength;
- if (length)
- memcpy_fixed(to+packlength, &from, sizeof(from));
- else
- bzero(to+packlength,sizeof(from));
- return from+length;
+/**
+ Unpack a blob field from row data.
+
+ This method is used to unpack a blob field from a master whose size of
+ the field is less than that of the slave. Note: This method is included
+ to satisfy inheritance rules, but is not needed for blob fields. It
+ simply is used as a pass-through to the original unpack() method for
+ blob fields.
+
+ @param to Destination of the data
+ @param from Source of the data
+ @param param_data @c TRUE if base types should be stored in little-
+ endian format, @c FALSE if native format should
+ be used.
+
+ @return New pointer into memory based on from + length of the data
+*/
+const uchar *Field_blob::unpack(uchar *to,
+ const uchar *from,
+ uint param_data,
+ bool low_byte_first)
+{
+ DBUG_ENTER("Field_blob::unpack");
+ DBUG_PRINT("enter", ("to: 0x%lx; from: 0x%lx;"
+ " param_data: %u; low_byte_first: %d",
+ (ulong) to, (ulong) from, param_data, low_byte_first));
+ uint const master_packlength=
+ param_data > 0 ? param_data & 0xFF : packlength;
+ uint32 const length= get_length(from, master_packlength, low_byte_first);
+ DBUG_DUMP("packed", from, length + master_packlength);
+ bitmap_set_bit(table->write_set, field_index);
+ store(reinterpret_cast<const char*>(from) + master_packlength,
+ length, field_charset);
+ DBUG_DUMP("record", to, table->s->reclength);
+ DBUG_RETURN(from + master_packlength + length);
}
/* Keys for blobs are like keys on varchars */
-int Field_blob::pack_cmp(const char *a, const char *b, uint key_length_arg,
+int Field_blob::pack_cmp(const uchar *a, const uchar *b, uint key_length_arg,
my_bool insert_or_update)
{
uint a_length, b_length;
@@ -7368,20 +8147,20 @@ int Field_blob::pack_cmp(const char *a, const char *b, uint key_length_arg,
}
else
{
- a_length= (uint) (uchar) *a++;
- b_length= (uint) (uchar) *b++;
+ a_length= (uint) *a++;
+ b_length= (uint) *b++;
}
return field_charset->coll->strnncollsp(field_charset,
- (const uchar*) a, a_length,
- (const uchar*) b, b_length,
+ a, a_length,
+ b, b_length,
insert_or_update);
}
-int Field_blob::pack_cmp(const char *b, uint key_length_arg,
+int Field_blob::pack_cmp(const uchar *b, uint key_length_arg,
my_bool insert_or_update)
{
- char *a;
+ uchar *a;
uint a_length, b_length;
memcpy_fixed(&a,ptr+packlength,sizeof(char*));
if (!a)
@@ -7393,24 +8172,26 @@ int Field_blob::pack_cmp(const char *b, uint key_length_arg,
b_length= uint2korr(b); b+=2;
}
else
- b_length= (uint) (uchar) *b++;
+ b_length= (uint) *b++;
return field_charset->coll->strnncollsp(field_charset,
- (const uchar*) a, a_length,
- (const uchar*) b, b_length,
+ a, a_length,
+ b, b_length,
insert_or_update);
}
-/* Create a packed key that will be used for storage from a MySQL row */
+/** Create a packed key that will be used for storage from a MySQL row. */
-char *Field_blob::pack_key(char *to, const char *from, uint max_length)
+uchar *
+Field_blob::pack_key(uchar *to, const uchar *from, uint max_length,
+ bool low_byte_first __attribute__((unused)))
{
- char *save=ptr;
- ptr=(char*) from;
- uint32 length=get_length(); // Length of from string
+ 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);
+ max_length/field_charset->mbmaxlen : max_length);
if (length)
- get_ptr((char**) &from);
+ get_ptr((uchar**) &from);
if (length > local_char_length)
local_char_length= my_charpos(field_charset, from, from+length,
local_char_length);
@@ -7424,35 +8205,35 @@ char *Field_blob::pack_key(char *to, const char *from, uint max_length)
}
-/*
+/**
Unpack a blob key into a record buffer.
- SYNOPSIS
- Field_blob::unpack_key()
- to Pointer into the record buffer.
- from Pointer to the packed key.
- max_length Key length limit from key description.
+ 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.
- DESCRIPTION
- 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.
- RETURN
+ @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.
*/
-const char *Field_blob::unpack_key(char *to, const char *from, uint max_length)
+const uchar *
+Field_blob::unpack_key(uchar *to, const uchar *from, uint max_length,
+ bool low_byte_first __attribute__((unused)))
{
/* get length of the blob key */
- uint32 length= *((uchar*) from++);
+ uint32 length= *from++;
if (max_length > 255)
- length+= (*((uchar*) from++)) << 8;
+ length+= *from++ << 8;
/* put the length into the record buffer */
put_length(to, length);
@@ -7468,10 +8249,11 @@ const char *Field_blob::unpack_key(char *to, const char *from, uint max_length)
}
-/* Create a packed key that will be used for storage from a MySQL key */
+/** Create a packed key that will be used for storage from a MySQL key. */
-char *Field_blob::pack_key_from_key_image(char *to, const char *from,
- uint max_length)
+uchar *
+Field_blob::pack_key_from_key_image(uchar *to, const uchar *from, uint max_length,
+ bool low_byte_first __attribute__((unused)))
{
uint length=uint2korr(from);
if (length > max_length)
@@ -7485,11 +8267,11 @@ char *Field_blob::pack_key_from_key_image(char *to, const char *from,
}
-uint Field_blob::packed_col_length(const char *data_ptr, uint length)
+uint Field_blob::packed_col_length(const uchar *data_ptr, uint length)
{
if (length > 255)
return uint2korr(data_ptr)+2;
- return (uint) ((uchar) *data_ptr)+1;
+ return (uint) *data_ptr + 1;
}
@@ -7499,6 +8281,18 @@ uint Field_blob::max_packed_col_length(uint max_length)
}
+uint Field_blob::is_equal(Create_field *new_field)
+{
+ if (compare_str_field_flags(new_field, flags))
+ return 0;
+
+ return ((new_field->sql_type == get_blob_type_from_length(max_data_length()))
+ && new_field->charset == field_charset &&
+ ((Field_blob *)new_field->field)->max_data_length() ==
+ max_data_length());
+}
+
+
#ifdef HAVE_SPATIAL
void Field_geom::sql_type(String &res) const
@@ -7647,13 +8441,15 @@ void Field_enum::store_type(ulonglong value)
}
-/*
-** Note. Storing a empty string in a enum field gives a warning
-** (if there isn't a empty value in the enum)
+/**
+ @note
+ Storing a empty string in a enum field gives a warning
+ (if there isn't a empty value in the enum)
*/
int Field_enum::store(const char *from,uint length,CHARSET_INFO *cs)
{
+ ASSERT_COLUMN_MARKED_FOR_WRITE;
int err= 0;
uint32 not_used;
char buff[STRING_BUFFER_USUAL_SIZE];
@@ -7702,6 +8498,7 @@ int Field_enum::store(double nr)
int Field_enum::store(longlong nr, bool unsigned_val)
{
+ ASSERT_COLUMN_MARKED_FOR_WRITE;
int error= 0;
if ((ulonglong) nr > typelib->count || nr == 0)
{
@@ -7725,49 +8522,69 @@ double Field_enum::val_real(void)
longlong Field_enum::val_int(void)
{
+ ASSERT_COLUMN_MARKED_FOR_READ;
switch (packlength) {
case 1:
- return (longlong) (uchar) ptr[0];
+ return (longlong) ptr[0];
case 2:
- {
- uint16 tmp;
+ {
+ uint16 tmp;
#ifdef WORDS_BIGENDIAN
- if (table->s->db_low_byte_first)
- tmp=sint2korr(ptr);
- else
+ if (table->s->db_low_byte_first)
+ tmp=sint2korr(ptr);
+ else
#endif
- shortget(tmp,ptr);
- return (longlong) tmp;
- }
+ shortget(tmp,ptr);
+ return (longlong) tmp;
+ }
case 3:
return (longlong) uint3korr(ptr);
case 4:
- {
- uint32 tmp;
+ {
+ uint32 tmp;
#ifdef WORDS_BIGENDIAN
- if (table->s->db_low_byte_first)
- tmp=uint4korr(ptr);
- else
+ if (table->s->db_low_byte_first)
+ tmp=uint4korr(ptr);
+ else
#endif
- longget(tmp,ptr);
- return (longlong) tmp;
- }
+ longget(tmp,ptr);
+ return (longlong) tmp;
+ }
case 8:
- {
- longlong tmp;
+ {
+ longlong tmp;
#ifdef WORDS_BIGENDIAN
- if (table->s->db_low_byte_first)
- tmp=sint8korr(ptr);
- else
+ if (table->s->db_low_byte_first)
+ tmp=sint8korr(ptr);
+ else
#endif
- longlongget(tmp,ptr);
- return tmp;
- }
+ longlongget(tmp,ptr);
+ return tmp;
+ }
}
return 0; // impossible
}
+/**
+ Save the field metadata for enum fields.
+
+ Saves the real type in the first byte and the pack length in the
+ second byte of the field metadata array at index of *metadata_ptr and
+ *(metadata_ptr + 1).
+
+ @param metadata_ptr First byte of field metadata
+
+ @returns number of bytes written to metadata_ptr
+*/
+int Field_enum::do_save_field_metadata(uchar *metadata_ptr)
+{
+ *metadata_ptr= real_type();
+ *(metadata_ptr + 1)= pack_length();
+ return 2;
+}
+
+
String *Field_enum::val_str(String *val_buffer __attribute__((unused)),
String *val_ptr)
{
@@ -7781,18 +8598,18 @@ String *Field_enum::val_str(String *val_buffer __attribute__((unused)),
return val_ptr;
}
-int Field_enum::cmp(const char *a_ptr, const char *b_ptr)
+int Field_enum::cmp(const uchar *a_ptr, const uchar *b_ptr)
{
- char *old=ptr;
- ptr=(char*) a_ptr;
+ uchar *old= ptr;
+ ptr= (uchar*) a_ptr;
ulonglong a=Field_enum::val_int();
- ptr=(char*) b_ptr;
+ ptr= (uchar*) b_ptr;
ulonglong b=Field_enum::val_int();
- ptr=old;
+ ptr= old;
return (a < b) ? -1 : (a > b) ? 1 : 0;
}
-void Field_enum::sort_string(char *to,uint length __attribute__((unused)))
+void Field_enum::sort_string(uchar *to,uint length __attribute__((unused)))
{
ulonglong value=Field_enum::val_int();
to+=packlength-1;
@@ -7850,6 +8667,7 @@ Field *Field_enum::new_field(MEM_ROOT *root, struct st_table *new_table,
int Field_set::store(const char *from,uint length,CHARSET_INFO *cs)
{
+ ASSERT_COLUMN_MARKED_FOR_WRITE;
bool got_warning= 0;
int err= 0;
char *not_used;
@@ -7889,6 +8707,7 @@ int Field_set::store(const char *from,uint length,CHARSET_INFO *cs)
int Field_set::store(longlong nr, bool unsigned_val)
{
+ ASSERT_COLUMN_MARKED_FOR_WRITE;
int error= 0;
ulonglong max_nr= set_bits(ulonglong, typelib->count);
if ((ulonglong) nr > max_nr)
@@ -7951,7 +8770,12 @@ void Field_set::sql_type(String &res) const
res.append(')');
}
-/* returns 1 if the fields are equally defined */
+/**
+ @retval
+ 1 if the fields are equally defined
+ @retval
+ 0 if the fields are unequally defined
+*/
bool Field::eq_def(Field *field)
{
@@ -7961,24 +8785,47 @@ bool Field::eq_def(Field *field)
return 1;
}
+
+/**
+ @return
+ returns 1 if the fields are equally defined
+*/
+
bool Field_enum::eq_def(Field *field)
{
if (!Field::eq_def(field))
return 0;
- TYPELIB *from_lib=((Field_enum*) field)->typelib;
+ return compare_enum_values(((Field_enum*) field)->typelib);
+}
- if (typelib->count < from_lib->count)
- return 0;
- for (uint i=0 ; i < from_lib->count ; i++)
+
+bool Field_enum::compare_enum_values(TYPELIB *values)
+{
+ if (typelib->count != values->count)
+ return FALSE;
+ for (uint i= 0; i < typelib->count; i++)
if (my_strnncoll(field_charset,
- (const uchar*)typelib->type_names[i],
- (uint) strlen(typelib->type_names[i]),
- (const uchar*)from_lib->type_names[i],
- (uint) strlen(from_lib->type_names[i])))
- return 0;
- return 1;
+ (const uchar*) typelib->type_names[i],
+ typelib->type_lengths[i],
+ (const uchar*) values->type_names[i],
+ values->type_lengths[i]))
+ return FALSE;
+ return TRUE;
+}
+
+
+uint Field_enum::is_equal(Create_field *new_field)
+{
+ if (!Field_str::is_equal(new_field))
+ return 0;
+ return compare_enum_values(new_field->interval);
}
+
+/**
+ @return
+ returns 1 if the fields are equally defined
+*/
bool Field_num::eq_def(Field *field)
{
if (!Field::eq_def(field))
@@ -7993,6 +8840,17 @@ bool Field_num::eq_def(Field *field)
}
+uint Field_num::is_equal(Create_field *new_field)
+{
+ return ((new_field->sql_type == real_type()) &&
+ ((new_field->flags & UNSIGNED_FLAG) == (uint) (flags &
+ UNSIGNED_FLAG)) &&
+ ((new_field->flags & AUTO_INCREMENT_FLAG) ==
+ (uint) (flags & AUTO_INCREMENT_FLAG)) &&
+ (new_field->length <= max_display_length()));
+}
+
+
/*
Bit field.
@@ -8022,12 +8880,11 @@ bool Field_num::eq_def(Field *field)
11 one byte for 'd'
*/
-Field_bit::Field_bit(char *ptr_arg, uint32 len_arg, uchar *null_ptr_arg,
+Field_bit::Field_bit(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg,
uchar null_bit_arg, uchar *bit_ptr_arg, uchar bit_ofs_arg,
- enum utype unireg_check_arg, const char *field_name_arg,
- struct st_table *table_arg)
+ enum utype unireg_check_arg, const char *field_name_arg)
: Field(ptr_arg, len_arg, null_ptr_arg, null_bit_arg,
- unireg_check_arg, field_name_arg, table_arg),
+ unireg_check_arg, field_name_arg),
bit_ptr(bit_ptr_arg), bit_ofs(bit_ofs_arg), bit_len(len_arg & 7),
bytes_in_rec(len_arg / 8)
{
@@ -8041,9 +8898,53 @@ Field_bit::Field_bit(char *ptr_arg, uint32 len_arg, uchar *null_ptr_arg,
}
+void Field_bit::hash(ulong *nr, ulong *nr2)
+{
+ if (is_null())
+ {
+ *nr^= (*nr << 1) | 1;
+ }
+ else
+ {
+ CHARSET_INFO *cs= &my_charset_bin;
+ longlong value= Field_bit::val_int();
+ uchar tmp[8];
+ mi_int8store(tmp,value);
+ cs->coll->hash_sort(cs, tmp, 8, nr, nr2);
+ }
+}
+
+
+size_t
+Field_bit::do_last_null_byte() const
+{
+ /*
+ Code elsewhere is assuming that bytes are 8 bits, so I'm using
+ that value instead of the correct one: CHAR_BIT.
+
+ REFACTOR SUGGESTION (Matz): Change to use the correct number of
+ bits. On systems with CHAR_BIT > 8 (not very common), the storage
+ will lose the extra bits.
+ */
+ DBUG_PRINT("test", ("bit_ofs: %d, bit_len: %d bit_ptr: 0x%lx",
+ bit_ofs, bit_len, (long) bit_ptr));
+ uchar *result;
+ if (bit_len == 0)
+ result= null_ptr;
+ else if (bit_ofs + bit_len > 8)
+ result= bit_ptr + 1;
+ else
+ result= bit_ptr;
+
+ if (result)
+ return (size_t) (result - table->record[0]) + 1;
+ return LAST_NULL_BYTE_UNDEF;
+}
+
+
Field *Field_bit::new_key_field(MEM_ROOT *root,
struct st_table *new_table,
- char *new_ptr, uchar *new_null_ptr,
+ uchar *new_ptr, uchar *new_null_ptr,
uint new_null_bit)
{
Field_bit *res;
@@ -8052,7 +8953,7 @@ Field *Field_bit::new_key_field(MEM_ROOT *root,
new_null_bit)))
{
/* Move bits normally stored in null_pointer to new_ptr */
- res->bit_ptr= (uchar*) new_ptr;
+ res->bit_ptr= new_ptr;
res->bit_ofs= 0;
if (bit_len)
res->ptr++; // Store rest of data here
@@ -8061,8 +8962,16 @@ Field *Field_bit::new_key_field(MEM_ROOT *root,
}
+uint Field_bit::is_equal(Create_field *new_field)
+{
+ return (new_field->sql_type == real_type() &&
+ new_field->length == max_display_length());
+}
+
+
int Field_bit::store(const char *from, uint length, CHARSET_INFO *cs)
{
+ ASSERT_COLUMN_MARKED_FOR_WRITE;
int delta;
for (; length && !*from; from++, length--); // skip left 0's
@@ -8109,7 +9018,7 @@ int Field_bit::store(const char *from, uint length, CHARSET_INFO *cs)
int Field_bit::store(double nr)
{
- return store((longlong) nr, FALSE);
+ return Field_bit::store((longlong) nr, FALSE);
}
@@ -8138,6 +9047,7 @@ double Field_bit::val_real(void)
longlong Field_bit::val_int(void)
{
+ ASSERT_COLUMN_MARKED_FOR_READ;
ulonglong bits= 0;
if (bit_len)
{
@@ -8147,7 +9057,7 @@ longlong Field_bit::val_int(void)
switch (bytes_in_rec) {
case 0: return bits;
- case 1: return bits | (ulonglong) (uchar) ptr[0];
+ case 1: return bits | (ulonglong) ptr[0];
case 2: return bits | mi_uint2korr(ptr);
case 3: return bits | mi_uint3korr(ptr);
case 4: return bits | mi_uint4korr(ptr);
@@ -8162,6 +9072,7 @@ longlong Field_bit::val_int(void)
String *Field_bit::val_str(String *val_buffer,
String *val_ptr __attribute__((unused)))
{
+ ASSERT_COLUMN_MARKED_FOR_READ;
char buff[sizeof(longlong)];
uint length= min(pack_length(), sizeof(longlong));
ulonglong bits= val_int();
@@ -8177,18 +9088,48 @@ String *Field_bit::val_str(String *val_buffer,
my_decimal *Field_bit::val_decimal(my_decimal *deciaml_value)
{
+ ASSERT_COLUMN_MARKED_FOR_READ;
int2my_decimal(E_DEC_FATAL_ERROR, val_int(), 1, deciaml_value);
return deciaml_value;
}
-int Field_bit::key_cmp(const byte *str, uint length)
+/*
+ Compare two bit fields using pointers within the record.
+ SYNOPSIS
+ cmp_max()
+ a Pointer to field->ptr in first record
+ b Pointer to field->ptr in second record
+ max_len Maximum length used in index
+ DESCRIPTION
+ This method is used from key_rec_cmp used by merge sorts used
+ by partitioned index read and later other similar places.
+ The a and b pointer must be pointers to the field in a record
+ (not the table->record[0] necessarily)
+*/
+int Field_bit::cmp_max(const uchar *a, const uchar *b, uint max_len)
+{
+ my_ptrdiff_t a_diff= a - ptr;
+ my_ptrdiff_t b_diff= b - ptr;
+ if (bit_len)
+ {
+ int flag;
+ uchar bits_a= get_rec_bits(bit_ptr+a_diff, bit_ofs, bit_len);
+ uchar bits_b= get_rec_bits(bit_ptr+b_diff, bit_ofs, bit_len);
+ if ((flag= (int) (bits_a - bits_b)))
+ return flag;
+ }
+ return memcmp(a, b, field_length);
+}
+
+
+int Field_bit::key_cmp(const uchar *str, uint length)
{
if (bit_len)
{
int flag;
uchar bits= get_rec_bits(bit_ptr, bit_ofs, bit_len);
- if ((flag= (int) (bits - *(uchar*) str)))
+ if ((flag= (int) (bits - *str)))
return flag;
str++;
length--;
@@ -8211,7 +9152,7 @@ int Field_bit::cmp_offset(uint row_offset)
}
-uint Field_bit::get_key_image(char *buff, uint length, imagetype type_arg)
+uint Field_bit::get_key_image(uchar *buff, uint length, imagetype type_arg)
{
if (bit_len)
{
@@ -8225,6 +9166,78 @@ uint Field_bit::get_key_image(char *buff, uint length, imagetype type_arg)
}
+/**
+ Save the field metadata for bit fields.
+
+ Saves the bit length in the first byte and bytes in record in the
+ second byte of the field metadata array at index of *metadata_ptr and
+ *(metadata_ptr + 1).
+
+ @param metadata_ptr First byte of field metadata
+
+ @returns number of bytes written to metadata_ptr
+*/
+int Field_bit::do_save_field_metadata(uchar *metadata_ptr)
+{
+ *metadata_ptr= bit_len;
+ *(metadata_ptr + 1)= bytes_in_rec;
+ return 2;
+}
+
+
+/**
+ Returns the number of bytes field uses in row-based replication
+ row packed size.
+
+ This method is used in row-based replication to determine the number
+ of bytes that the field consumes in the row record format. This is
+ used to skip fields in the master that do not exist on the slave.
+
+ @param field_metadata Encoded size in field metadata
+
+ @returns The size of the field based on the field metadata.
+*/
+uint Field_bit::pack_length_from_metadata(uint field_metadata)
+{
+ uint const from_len= (field_metadata >> 8U) & 0x00ff;
+ uint const from_bit_len= field_metadata & 0x00ff;
+ uint const source_size= from_len + ((from_bit_len > 0) ? 1 : 0);
+ return (source_size);
+}
+
+
+/**
+ 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)))
+{
+ int compatible= 0;
+ uint const source_size= pack_length_from_metadata(field_metadata);
+ uint const destination_size= row_pack_length();
+ uint const from_bit_len= field_metadata & 0x00ff;
+ uint const from_len= (field_metadata >> 8U) & 0x00ff;
+ if ((bit_len == 0) || (from_bit_len == 0))
+ compatible= (source_size <= destination_size);
+ else if (from_bit_len > bit_len)
+ compatible= (from_len < bytes_in_rec);
+ else
+ compatible= ((from_bit_len <= bit_len) && (from_len <= bytes_in_rec));
+ return (compatible);
+}
+
+
+
void Field_bit::sql_type(String &res) const
{
CHARSET_INFO *cs= res.charset();
@@ -8234,13 +9247,34 @@ void Field_bit::sql_type(String &res) const
}
-char *Field_bit::pack(char *to, const char *from, uint max_length)
+uchar *
+Field_bit::pack(uchar *to, const uchar *from, uint max_length,
+ bool low_byte_first __attribute__((unused)))
{
- DBUG_ASSERT(max_length);
+ DBUG_ASSERT(max_length > 0);
uint length;
- if (bit_len)
+ if (bit_len > 0)
{
- uchar bits= get_rec_bits(bit_ptr, bit_ofs, bit_len);
+ /*
+ We have the following:
+
+ ptr Points into a field in record R1
+ from Points to a field in a record R2
+ bit_ptr Points to the byte (in the null bytes) that holds the
+ odd bits of R1
+ from_bitp Points to the byte that holds the odd bits of R2
+
+ We have the following:
+
+ ptr - bit_ptr = from - from_bitp
+
+ We want to isolate 'from_bitp', so this gives:
+
+ ptr - bit_ptr - from = - from_bitp
+ - ptr + bit_ptr + from = from_bitp
+ bit_ptr + from - ptr = from_bitp
+ */
+ uchar bits= get_rec_bits(bit_ptr + (from - ptr), bit_ofs, bit_len);
*to++= bits;
}
length= min(bytes_in_rec, max_length - (bit_len > 0));
@@ -8249,29 +9283,95 @@ char *Field_bit::pack(char *to, const char *from, uint max_length)
}
-const char *Field_bit::unpack(char *to, const char *from)
+/**
+ Unpack a bit field from row data.
+
+ This method is used to unpack a bit field from a master whose size
+ of the field is less than that of the slave.
+
+ @param to Destination of the data
+ @param from Source of the data
+ @param param_data Bit length (upper) and length (lower) values
+
+ @return New pointer into memory based on from + length of the data
+*/
+const uchar *
+Field_bit::unpack(uchar *to, const uchar *from, uint param_data,
+ bool low_byte_first __attribute__((unused)))
{
- if (bit_len)
+ uint const from_len= (param_data >> 8U) & 0x00ff;
+ uint const from_bit_len= param_data & 0x00ff;
+ /*
+ 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.
+ */
+ if (param_data == 0 ||
+ (from_bit_len == bit_len) && (from_len == bytes_in_rec))
{
- set_rec_bits(*from, bit_ptr, bit_ofs, bit_len);
- from++;
+ if (bit_len > 0)
+ {
+ /*
+ set_rec_bits is a macro, don't put the post-increment in the
+ argument since that might cause strange side-effects.
+
+ For the choice of the second argument, see the explanation for
+ Field_bit::pack().
+ */
+ set_rec_bits(*from, bit_ptr + (to - ptr), bit_ofs, bit_len);
+ from++;
+ }
+ memcpy(to, from, bytes_in_rec);
+ return from + bytes_in_rec;
}
- memcpy(to, from, bytes_in_rec);
- return from + bytes_in_rec;
+
+ /*
+ We are converting a smaller bit field to a larger one here.
+ To do that, we first need to construct a raw value for the original
+ bit value stored in the from buffer. Then that needs to be converted
+ to the larger field then sent to store() for writing to the field.
+ Lastly the odd bits need to be masked out if the bytes_in_rec > 0.
+ Otherwise stray bits can cause spurious values.
+ */
+ uint new_len= (field_length + 7) / 8;
+ char *value= (char *)my_alloca(new_len);
+ bzero(value, new_len);
+ uint len= from_len + ((from_bit_len > 0) ? 1 : 0);
+ memcpy(value + (new_len - len), from, len);
+ /*
+ Mask out the unused bits in the partial byte.
+ TODO: Add code to the master to always mask these bits and remove
+ the following.
+ */
+ if ((from_bit_len > 0) && (from_len > 0))
+ value[new_len - len]= value[new_len - len] & ((1U << from_bit_len) - 1);
+ bitmap_set_bit(table->write_set,field_index);
+ store(value, new_len, system_charset_info);
+ my_afree(value);
+ return from + len;
}
+void Field_bit::set_default()
+{
+ if (bit_len > 0)
+ {
+ my_ptrdiff_t const offset= table->s->default_values - table->record[0];
+ uchar bits= get_rec_bits(bit_ptr + offset, bit_ofs, bit_len);
+ set_rec_bits(bits, bit_ptr, bit_ofs, bit_len);
+ }
+ Field::set_default();
+}
+
/*
Bit field support for non-MyISAM tables.
*/
-Field_bit_as_char::Field_bit_as_char(char *ptr_arg, uint32 len_arg,
+Field_bit_as_char::Field_bit_as_char(uchar *ptr_arg, uint32 len_arg,
uchar *null_ptr_arg, uchar null_bit_arg,
enum utype unireg_check_arg,
- const char *field_name_arg,
- struct st_table *table_arg)
- : Field_bit(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, 0,
- 0, unireg_check_arg, field_name_arg, table_arg)
+ const char *field_name_arg)
+ :Field_bit(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, 0, 0,
+ unireg_check_arg, field_name_arg)
{
flags|= UNSIGNED_FLAG;
bit_len= 0;
@@ -8281,6 +9381,7 @@ Field_bit_as_char::Field_bit_as_char(char *ptr_arg, uint32 len_arg,
int Field_bit_as_char::store(const char *from, uint length, CHARSET_INFO *cs)
{
+ ASSERT_COLUMN_MARKED_FOR_WRITE;
int delta;
uchar bits= (uchar) (field_length & 7);
@@ -8292,7 +9393,7 @@ int Field_bit_as_char::store(const char *from, uint length, CHARSET_INFO *cs)
{
memset(ptr, 0xff, bytes_in_rec);
if (bits)
- *ptr&= ((1 << bits) - 1); /* set first byte */
+ *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);
else
@@ -8315,20 +9416,14 @@ void Field_bit_as_char::sql_type(String &res) const
/*****************************************************************************
- Handling of field and create_field
+ Handling of field and Create_field
*****************************************************************************/
-/*
- Convert create_field::length from number of characters to number of bytes
-
- SYNOPSIS
- create_field::create_length_to_internal_length()
-
- DESCRIPTION
- Convert create_field::length from number of characters to number of bytes.
+/**
+ Convert create_field::length from number of characters to number of bytes.
*/
-void create_field::create_length_to_internal_length(void)
+void Create_field::create_length_to_internal_length(void)
{
switch (sql_type) {
case MYSQL_TYPE_TINY_BLOB:
@@ -8375,7 +9470,10 @@ void create_field::create_length_to_internal_length(void)
}
-void create_field::init_for_tmp_table(enum_field_types sql_type_arg,
+/**
+ Init for a tmp table field. To be extended if need be.
+*/
+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)
{
@@ -8393,30 +9491,30 @@ void create_field::init_for_tmp_table(enum_field_types sql_type_arg,
}
-/*
- Initialize field definition for create
+/**
+ Initialize field definition for create.
- SYNOPSIS
- thd Thread handle
- fld_name Field name
- fld_type Field type
- fld_length Field length
- fld_decimals Decimal (if any)
- fld_type_modifier Additional type information
- fld_default_value Field default value (if any)
- fld_on_update_value The value of ON UPDATE clause
- fld_comment Field comment
- fld_change Field change
- fld_interval_list Interval list (if any)
- fld_charset Field charset
- fld_geom_type Field geometry type (if any)
+ @param thd Thread handle
+ @param fld_name Field name
+ @param fld_type Field type
+ @param fld_length Field length
+ @param fld_decimals Decimal (if any)
+ @param fld_type_modifier Additional type information
+ @param fld_default_value Field default value (if any)
+ @param fld_on_update_value The value of ON UPDATE clause
+ @param fld_comment Field comment
+ @param fld_change Field change
+ @param fld_interval_list Interval list (if any)
+ @param fld_charset Field charset
+ @param fld_geom_type Field geometry type (if any)
- RETURN
+ @retval
FALSE on success
+ @retval
TRUE on error
*/
-bool create_field::init(THD *thd, char *fld_name, enum_field_types fld_type,
+bool Create_field::init(THD *thd, char *fld_name, enum_field_types fld_type,
char *fld_length, char *fld_decimals,
uint fld_type_modifier, Item *fld_default_value,
Item *fld_on_update_value, LEX_STRING *fld_comment,
@@ -8426,7 +9524,7 @@ bool create_field::init(THD *thd, char *fld_name, enum_field_types fld_type,
uint sign_len, allowed_type_modifier= 0;
ulong max_field_charlength= MAX_FIELD_CHARLENGTH;
- DBUG_ENTER("create_field::init()");
+ DBUG_ENTER("Create_field::init()");
field= 0;
field_name= fld_name;
@@ -8457,7 +9555,7 @@ bool create_field::init(THD *thd, char *fld_name, enum_field_types fld_type,
it is NOT NULL, not an AUTO_INCREMENT field and not a TIMESTAMP.
*/
if (!fld_default_value && !(fld_type_modifier & AUTO_INCREMENT_FLAG) &&
- (fld_type_modifier & NOT_NULL_FLAG) && fld_type != FIELD_TYPE_TIMESTAMP)
+ (fld_type_modifier & NOT_NULL_FLAG) && fld_type != MYSQL_TYPE_TIMESTAMP)
flags|= NO_DEFAULT_VALUE_FLAG;
if (fld_length != NULL)
@@ -8477,34 +9575,34 @@ bool create_field::init(THD *thd, char *fld_name, enum_field_types fld_type,
sign_len= fld_type_modifier & UNSIGNED_FLAG ? 0 : 1;
switch (fld_type) {
- case FIELD_TYPE_TINY:
+ case MYSQL_TYPE_TINY:
if (!fld_length)
length= MAX_TINYINT_WIDTH+sign_len;
allowed_type_modifier= AUTO_INCREMENT_FLAG;
break;
- case FIELD_TYPE_SHORT:
+ case MYSQL_TYPE_SHORT:
if (!fld_length)
length= MAX_SMALLINT_WIDTH+sign_len;
allowed_type_modifier= AUTO_INCREMENT_FLAG;
break;
- case FIELD_TYPE_INT24:
+ case MYSQL_TYPE_INT24:
if (!fld_length)
length= MAX_MEDIUMINT_WIDTH+sign_len;
allowed_type_modifier= AUTO_INCREMENT_FLAG;
break;
- case FIELD_TYPE_LONG:
+ case MYSQL_TYPE_LONG:
if (!fld_length)
length= MAX_INT_WIDTH+sign_len;
allowed_type_modifier= AUTO_INCREMENT_FLAG;
break;
- case FIELD_TYPE_LONGLONG:
+ case MYSQL_TYPE_LONGLONG:
if (!fld_length)
length= MAX_BIGINT_WIDTH;
allowed_type_modifier= AUTO_INCREMENT_FLAG;
break;
- case FIELD_TYPE_NULL:
+ case MYSQL_TYPE_NULL:
break;
- case FIELD_TYPE_NEWDECIMAL:
+ case MYSQL_TYPE_NEWDECIMAL:
my_decimal_trim(&length, &decimals);
if (length > DECIMAL_MAX_PRECISION)
{
@@ -8532,11 +9630,11 @@ bool create_field::init(THD *thd, char *fld_name, enum_field_types fld_type,
break;
case MYSQL_TYPE_STRING:
break;
- case FIELD_TYPE_BLOB:
- case FIELD_TYPE_TINY_BLOB:
- case FIELD_TYPE_LONG_BLOB:
- case FIELD_TYPE_MEDIUM_BLOB:
- case FIELD_TYPE_GEOMETRY:
+ case MYSQL_TYPE_BLOB:
+ case MYSQL_TYPE_TINY_BLOB:
+ case MYSQL_TYPE_LONG_BLOB:
+ case MYSQL_TYPE_MEDIUM_BLOB:
+ case MYSQL_TYPE_GEOMETRY:
if (fld_default_value)
{
/* Allow empty as default value. */
@@ -8568,12 +9666,12 @@ bool create_field::init(THD *thd, char *fld_name, enum_field_types fld_type,
}
flags|= BLOB_FLAG;
break;
- case FIELD_TYPE_YEAR:
+ case MYSQL_TYPE_YEAR:
if (!fld_length || length != 2)
length= 4; /* Default length */
flags|= ZEROFILL_FLAG | UNSIGNED_FLAG;
break;
- case FIELD_TYPE_FLOAT:
+ case MYSQL_TYPE_FLOAT:
/* change FLOAT(precision) to FLOAT or DOUBLE */
allowed_type_modifier= AUTO_INCREMENT_FLAG;
if (fld_length && !fld_decimals)
@@ -8586,7 +9684,7 @@ bool create_field::init(THD *thd, char *fld_name, enum_field_types fld_type,
}
else if (tmp_length > PRECISION_FOR_FLOAT)
{
- sql_type= FIELD_TYPE_DOUBLE;
+ sql_type= MYSQL_TYPE_DOUBLE;
length= DBL_DIG+7; /* -[digits].E+### */
}
else
@@ -8606,7 +9704,7 @@ bool create_field::init(THD *thd, char *fld_name, enum_field_types fld_type,
DBUG_RETURN(TRUE);
}
break;
- case FIELD_TYPE_DOUBLE:
+ case MYSQL_TYPE_DOUBLE:
allowed_type_modifier= AUTO_INCREMENT_FLAG;
if (!fld_length && !fld_decimals)
{
@@ -8620,7 +9718,7 @@ bool create_field::init(THD *thd, char *fld_name, enum_field_types fld_type,
DBUG_RETURN(TRUE);
}
break;
- case FIELD_TYPE_TIMESTAMP:
+ case MYSQL_TYPE_TIMESTAMP:
if (fld_length == NULL)
{
/* Compressed date YYYYMMDDHHMMSS */
@@ -8683,21 +9781,21 @@ bool create_field::init(THD *thd, char *fld_name, enum_field_types fld_type,
Field::NONE));
}
break;
- case FIELD_TYPE_DATE:
+ case MYSQL_TYPE_DATE:
/* Old date type. */
if (protocol_version != PROTOCOL_VERSION-1)
- sql_type= FIELD_TYPE_NEWDATE;
+ sql_type= MYSQL_TYPE_NEWDATE;
/* fall trough */
- case FIELD_TYPE_NEWDATE:
+ case MYSQL_TYPE_NEWDATE:
length= 10;
break;
- case FIELD_TYPE_TIME:
+ case MYSQL_TYPE_TIME:
length= 10;
break;
- case FIELD_TYPE_DATETIME:
+ case MYSQL_TYPE_DATETIME:
length= MAX_DATETIME_WIDTH;
break;
- case FIELD_TYPE_SET:
+ case MYSQL_TYPE_SET:
{
pack_length= get_set_pack_length(fld_interval_list->elements);
@@ -8713,7 +9811,7 @@ bool create_field::init(THD *thd, char *fld_name, enum_field_types fld_type,
length= 1;
break;
}
- case FIELD_TYPE_ENUM:
+ case MYSQL_TYPE_ENUM:
{
/* Should be safe. */
pack_length= get_enum_pack_length(fld_interval_list->elements);
@@ -8722,7 +9820,7 @@ bool create_field::init(THD *thd, char *fld_name, enum_field_types fld_type,
String *tmp;
while ((tmp= it++))
interval_list.push_back(tmp);
- length= 1; /* See comment for FIELD_TYPE_SET above. */
+ length= 1; /* See comment for MYSQL_TYPE_SET above. */
break;
}
case MYSQL_TYPE_VAR_STRING:
@@ -8741,19 +9839,19 @@ bool create_field::init(THD *thd, char *fld_name, enum_field_types fld_type,
pack_length= (length + 7) / 8;
break;
}
- case FIELD_TYPE_DECIMAL:
+ case MYSQL_TYPE_DECIMAL:
DBUG_ASSERT(0); /* Was obsolete */
}
/* Remember the value of length */
char_length= length;
if (!(flags & BLOB_FLAG) &&
- ((length > max_field_charlength && fld_type != FIELD_TYPE_SET &&
- fld_type != FIELD_TYPE_ENUM &&
+ ((length > max_field_charlength && fld_type != MYSQL_TYPE_SET &&
+ fld_type != MYSQL_TYPE_ENUM &&
(fld_type != MYSQL_TYPE_VARCHAR || fld_default_value)) ||
((length == 0) &&
fld_type != MYSQL_TYPE_STRING &&
- fld_type != MYSQL_TYPE_VARCHAR && fld_type != FIELD_TYPE_GEOMETRY)))
+ fld_type != MYSQL_TYPE_VARCHAR && fld_type != MYSQL_TYPE_GEOMETRY)))
{
my_error((fld_type == MYSQL_TYPE_VAR_STRING ||
fld_type == MYSQL_TYPE_VARCHAR ||
@@ -8778,13 +9876,13 @@ enum_field_types get_blob_type_from_length(ulong length)
{
enum_field_types type;
if (length < 256)
- type= FIELD_TYPE_TINY_BLOB;
+ type= MYSQL_TYPE_TINY_BLOB;
else if (length < 65536)
- type= FIELD_TYPE_BLOB;
+ type= MYSQL_TYPE_BLOB;
else if (length < 256L*256L*256L)
- type= FIELD_TYPE_MEDIUM_BLOB;
+ type= MYSQL_TYPE_MEDIUM_BLOB;
else
- type= FIELD_TYPE_LONG_BLOB;
+ type= MYSQL_TYPE_LONG_BLOB;
return type;
}
@@ -8798,32 +9896,32 @@ uint32 calc_pack_length(enum_field_types type,uint32 length)
switch (type) {
case MYSQL_TYPE_VAR_STRING:
case MYSQL_TYPE_STRING:
- case FIELD_TYPE_DECIMAL: return (length);
+ case MYSQL_TYPE_DECIMAL: return (length);
case MYSQL_TYPE_VARCHAR: return (length + (length < 256 ? 1: 2));
- case FIELD_TYPE_YEAR:
- case FIELD_TYPE_TINY : return 1;
- case FIELD_TYPE_SHORT : return 2;
- case FIELD_TYPE_INT24:
- case FIELD_TYPE_NEWDATE:
- case FIELD_TYPE_TIME: return 3;
- case FIELD_TYPE_TIMESTAMP:
- case FIELD_TYPE_DATE:
- case FIELD_TYPE_LONG : return 4;
- case FIELD_TYPE_FLOAT : return sizeof(float);
- case FIELD_TYPE_DOUBLE: return sizeof(double);
- case FIELD_TYPE_DATETIME:
- case FIELD_TYPE_LONGLONG: return 8; /* Don't crash if no longlong */
- case FIELD_TYPE_NULL : return 0;
- case FIELD_TYPE_TINY_BLOB: return 1+portable_sizeof_char_ptr;
- case FIELD_TYPE_BLOB: return 2+portable_sizeof_char_ptr;
- case FIELD_TYPE_MEDIUM_BLOB: return 3+portable_sizeof_char_ptr;
- case FIELD_TYPE_LONG_BLOB: return 4+portable_sizeof_char_ptr;
- case FIELD_TYPE_GEOMETRY: return 4+portable_sizeof_char_ptr;
- case FIELD_TYPE_SET:
- case FIELD_TYPE_ENUM:
- case FIELD_TYPE_NEWDECIMAL:
+ case MYSQL_TYPE_YEAR:
+ case MYSQL_TYPE_TINY : return 1;
+ case MYSQL_TYPE_SHORT : return 2;
+ case MYSQL_TYPE_INT24:
+ case MYSQL_TYPE_NEWDATE:
+ case MYSQL_TYPE_TIME: return 3;
+ case MYSQL_TYPE_TIMESTAMP:
+ case MYSQL_TYPE_DATE:
+ case MYSQL_TYPE_LONG : return 4;
+ case MYSQL_TYPE_FLOAT : return sizeof(float);
+ case MYSQL_TYPE_DOUBLE: return sizeof(double);
+ case MYSQL_TYPE_DATETIME:
+ case MYSQL_TYPE_LONGLONG: return 8; /* Don't crash if no longlong */
+ case MYSQL_TYPE_NULL : return 0;
+ case MYSQL_TYPE_TINY_BLOB: return 1+portable_sizeof_char_ptr;
+ case MYSQL_TYPE_BLOB: return 2+portable_sizeof_char_ptr;
+ case MYSQL_TYPE_MEDIUM_BLOB: return 3+portable_sizeof_char_ptr;
+ case MYSQL_TYPE_LONG_BLOB: return 4+portable_sizeof_char_ptr;
+ case MYSQL_TYPE_GEOMETRY: return 4+portable_sizeof_char_ptr;
+ case MYSQL_TYPE_SET:
+ case MYSQL_TYPE_ENUM:
+ case MYSQL_TYPE_NEWDECIMAL:
abort(); return 0; // This shouldn't happen
- case FIELD_TYPE_BIT: return length / 8;
+ case MYSQL_TYPE_BIT: return length / 8;
default:
return 0;
}
@@ -8833,17 +9931,17 @@ uint32 calc_pack_length(enum_field_types type,uint32 length)
uint pack_length_to_packflag(uint type)
{
switch (type) {
- case 1: return f_settype((uint) FIELD_TYPE_TINY);
- case 2: return f_settype((uint) FIELD_TYPE_SHORT);
- case 3: return f_settype((uint) FIELD_TYPE_INT24);
- case 4: return f_settype((uint) FIELD_TYPE_LONG);
- case 8: return f_settype((uint) FIELD_TYPE_LONGLONG);
+ case 1: return f_settype((uint) MYSQL_TYPE_TINY);
+ case 2: return f_settype((uint) MYSQL_TYPE_SHORT);
+ case 3: return f_settype((uint) MYSQL_TYPE_INT24);
+ case 4: return f_settype((uint) MYSQL_TYPE_LONG);
+ case 8: return f_settype((uint) MYSQL_TYPE_LONGLONG);
}
return 0; // This shouldn't happen
}
-Field *make_field(char *ptr, uint32 field_length,
+Field *make_field(TABLE_SHARE *share, uchar *ptr, uint32 field_length,
uchar *null_pos, uchar null_bit,
uint pack_flag,
enum_field_types field_type,
@@ -8851,14 +9949,13 @@ Field *make_field(char *ptr, uint32 field_length,
Field::geometry_type geom_type,
Field::utype unireg_check,
TYPELIB *interval,
- const char *field_name,
- struct st_table *table)
+ const char *field_name)
{
uchar *bit_ptr;
uchar bit_offset;
LINT_INIT(bit_ptr);
LINT_INIT(bit_offset);
- if (field_type == FIELD_TYPE_BIT && !f_bit_as_char(pack_flag))
+ if (field_type == MYSQL_TYPE_BIT && !f_bit_as_char(pack_flag))
{
bit_ptr= null_pos;
bit_offset= null_bit;
@@ -8879,15 +9976,14 @@ Field *make_field(char *ptr, uint32 field_length,
null_bit= ((uchar) 1) << null_bit;
}
- switch (field_type)
- {
- case FIELD_TYPE_DATE:
- case FIELD_TYPE_NEWDATE:
- case FIELD_TYPE_TIME:
- case FIELD_TYPE_DATETIME:
- case FIELD_TYPE_TIMESTAMP:
- field_charset= &my_charset_bin;
- default: break;
+ switch (field_type) {
+ case MYSQL_TYPE_DATE:
+ case MYSQL_TYPE_NEWDATE:
+ case MYSQL_TYPE_TIME:
+ case MYSQL_TYPE_DATETIME:
+ case MYSQL_TYPE_TIMESTAMP:
+ field_charset= &my_charset_bin;
+ default: break;
}
if (f_is_alpha(pack_flag))
@@ -8895,16 +9991,17 @@ Field *make_field(char *ptr, uint32 field_length,
if (!f_is_packed(pack_flag))
{
if (field_type == MYSQL_TYPE_STRING ||
- field_type == FIELD_TYPE_DECIMAL || // 3.23 or 4.0 string
+ field_type == MYSQL_TYPE_DECIMAL || // 3.23 or 4.0 string
field_type == MYSQL_TYPE_VAR_STRING)
return new Field_string(ptr,field_length,null_pos,null_bit,
- unireg_check, field_name, table,
+ unireg_check, field_name,
field_charset);
if (field_type == MYSQL_TYPE_VARCHAR)
return new Field_varstring(ptr,field_length,
HA_VARCHAR_PACKLENGTH(field_length),
null_pos,null_bit,
- unireg_check, field_name, table,
+ unireg_check, field_name,
+ share,
field_charset);
return 0; // Error
}
@@ -8916,103 +10013,105 @@ Field *make_field(char *ptr, uint32 field_length,
#ifdef HAVE_SPATIAL
if (f_is_geom(pack_flag))
return new Field_geom(ptr,null_pos,null_bit,
- unireg_check, field_name, table,
+ 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,
- unireg_check, field_name, table,
+ unireg_check, field_name, share,
pack_length, field_charset);
if (interval)
{
if (f_is_enum(pack_flag))
return new Field_enum(ptr,field_length,null_pos,null_bit,
- unireg_check, field_name, table,
+ unireg_check, field_name,
pack_length, interval, field_charset);
else
return new Field_set(ptr,field_length,null_pos,null_bit,
- unireg_check, field_name, table,
+ unireg_check, field_name,
pack_length, interval, field_charset);
}
}
switch (field_type) {
- case FIELD_TYPE_DECIMAL:
+ case MYSQL_TYPE_DECIMAL:
return new Field_decimal(ptr,field_length,null_pos,null_bit,
- unireg_check, field_name, table,
+ unireg_check, field_name,
f_decimals(pack_flag),
f_is_zerofill(pack_flag) != 0,
f_is_dec(pack_flag) == 0);
- case FIELD_TYPE_NEWDECIMAL:
+ case MYSQL_TYPE_NEWDECIMAL:
return new Field_new_decimal(ptr,field_length,null_pos,null_bit,
- unireg_check, field_name, table,
+ unireg_check, field_name,
f_decimals(pack_flag),
f_is_zerofill(pack_flag) != 0,
f_is_dec(pack_flag) == 0);
- case FIELD_TYPE_FLOAT:
+ case MYSQL_TYPE_FLOAT:
return new Field_float(ptr,field_length,null_pos,null_bit,
- unireg_check, field_name, table,
+ unireg_check, field_name,
f_decimals(pack_flag),
f_is_zerofill(pack_flag) != 0,
f_is_dec(pack_flag)== 0);
- case FIELD_TYPE_DOUBLE:
+ case MYSQL_TYPE_DOUBLE:
return new Field_double(ptr,field_length,null_pos,null_bit,
- unireg_check, field_name, table,
+ unireg_check, field_name,
f_decimals(pack_flag),
f_is_zerofill(pack_flag) != 0,
f_is_dec(pack_flag)== 0);
- case FIELD_TYPE_TINY:
+ case MYSQL_TYPE_TINY:
return new Field_tiny(ptr,field_length,null_pos,null_bit,
- unireg_check, field_name, table,
+ unireg_check, field_name,
f_is_zerofill(pack_flag) != 0,
f_is_dec(pack_flag) == 0);
- case FIELD_TYPE_SHORT:
+ case MYSQL_TYPE_SHORT:
return new Field_short(ptr,field_length,null_pos,null_bit,
- unireg_check, field_name, table,
+ unireg_check, field_name,
f_is_zerofill(pack_flag) != 0,
f_is_dec(pack_flag) == 0);
- case FIELD_TYPE_INT24:
+ case MYSQL_TYPE_INT24:
return new Field_medium(ptr,field_length,null_pos,null_bit,
- unireg_check, field_name, table,
+ unireg_check, field_name,
f_is_zerofill(pack_flag) != 0,
f_is_dec(pack_flag) == 0);
- case FIELD_TYPE_LONG:
+ case MYSQL_TYPE_LONG:
return new Field_long(ptr,field_length,null_pos,null_bit,
- unireg_check, field_name, table,
+ unireg_check, field_name,
f_is_zerofill(pack_flag) != 0,
f_is_dec(pack_flag) == 0);
- case FIELD_TYPE_LONGLONG:
+ case MYSQL_TYPE_LONGLONG:
return new Field_longlong(ptr,field_length,null_pos,null_bit,
- unireg_check, field_name, table,
+ unireg_check, field_name,
f_is_zerofill(pack_flag) != 0,
f_is_dec(pack_flag) == 0);
- case FIELD_TYPE_TIMESTAMP:
+ case MYSQL_TYPE_TIMESTAMP:
return new Field_timestamp(ptr,field_length, null_pos, null_bit,
- unireg_check, field_name, table,
+ unireg_check, field_name, share,
field_charset);
- case FIELD_TYPE_YEAR:
+ case MYSQL_TYPE_YEAR:
return new Field_year(ptr,field_length,null_pos,null_bit,
- unireg_check, field_name, table);
- case FIELD_TYPE_DATE:
+ unireg_check, field_name);
+ case MYSQL_TYPE_DATE:
return new Field_date(ptr,null_pos,null_bit,
- unireg_check, field_name, table, field_charset);
- case FIELD_TYPE_NEWDATE:
+ unireg_check, field_name, field_charset);
+ case MYSQL_TYPE_NEWDATE:
return new Field_newdate(ptr,null_pos,null_bit,
- unireg_check, field_name, table, field_charset);
- case FIELD_TYPE_TIME:
+ unireg_check, field_name, field_charset);
+ case MYSQL_TYPE_TIME:
return new Field_time(ptr,null_pos,null_bit,
- unireg_check, field_name, table, field_charset);
- case FIELD_TYPE_DATETIME:
+ unireg_check, field_name, field_charset);
+ case MYSQL_TYPE_DATETIME:
return new Field_datetime(ptr,null_pos,null_bit,
- unireg_check, field_name, table, field_charset);
- case FIELD_TYPE_NULL:
- return new Field_null(ptr,field_length,unireg_check,field_name,table, field_charset);
- case FIELD_TYPE_BIT:
+ unireg_check, field_name, field_charset);
+ case MYSQL_TYPE_NULL:
+ return new Field_null(ptr, field_length, unireg_check, field_name,
+ field_charset);
+ case MYSQL_TYPE_BIT:
return f_bit_as_char(pack_flag) ?
new Field_bit_as_char(ptr, field_length, null_pos, null_bit,
- unireg_check, field_name, table) :
+ unireg_check, field_name) :
new Field_bit(ptr, field_length, null_pos, null_bit, bit_ptr,
- bit_offset, unireg_check, field_name, table);
+ bit_offset, unireg_check, field_name);
+
default: // Impossible (Wrong version)
break;
}
@@ -9020,9 +10119,9 @@ Field *make_field(char *ptr, uint32 field_length,
}
-/* Create a field suitable for create of table */
+/** Create a field suitable for create of table. */
-create_field::create_field(Field *old_field,Field *orig_field)
+Create_field::Create_field(Field *old_field,Field *orig_field)
{
field= old_field;
field_name=change=old_field->field_name;
@@ -9042,12 +10141,12 @@ create_field::create_field(Field *old_field,Field *orig_field)
portable_sizeof_char_ptr);
switch (sql_type) {
- case FIELD_TYPE_BLOB:
+ case MYSQL_TYPE_BLOB:
switch (pack_length - portable_sizeof_char_ptr) {
- case 1: sql_type= FIELD_TYPE_TINY_BLOB; break;
- case 2: sql_type= FIELD_TYPE_BLOB; break;
- case 3: sql_type= FIELD_TYPE_MEDIUM_BLOB; break;
- default: sql_type= FIELD_TYPE_LONG_BLOB; break;
+ case 1: sql_type= MYSQL_TYPE_TINY_BLOB; break;
+ case 2: sql_type= MYSQL_TYPE_BLOB; break;
+ case 3: sql_type= MYSQL_TYPE_MEDIUM_BLOB; break;
+ default: sql_type= MYSQL_TYPE_LONG_BLOB; break;
}
length/= charset->mbmaxlen;
key_length/= charset->mbmaxlen;
@@ -9066,7 +10165,7 @@ create_field::create_field(Field *old_field,Field *orig_field)
length= (length+charset->mbmaxlen-1) / charset->mbmaxlen;
break;
#ifdef HAVE_SPATIAL
- case FIELD_TYPE_GEOMETRY:
+ case MYSQL_TYPE_GEOMETRY:
geom_type= ((Field_geom*)old_field)->geom_type;
break;
#endif
@@ -9083,36 +10182,35 @@ create_field::create_field(Field *old_field,Field *orig_field)
if (!(flags & (NO_DEFAULT_VALUE_FLAG | BLOB_FLAG)) &&
old_field->ptr && orig_field &&
- (sql_type != FIELD_TYPE_TIMESTAMP || /* set def only if */
+ (sql_type != MYSQL_TYPE_TIMESTAMP || /* set def only if */
old_field->table->timestamp_field != old_field || /* timestamp field */
unireg_check == Field::TIMESTAMP_UN_FIELD)) /* has default val */
{
+ char buff[MAX_FIELD_WIDTH];
+ String tmp(buff,sizeof(buff), charset);
my_ptrdiff_t diff;
/* Get the value from default_values */
diff= (my_ptrdiff_t) (orig_field->table->s->default_values-
orig_field->table->record[0]);
- orig_field->move_field(diff); // Points now at default_values
+ orig_field->move_field_offset(diff); // Points now at default_values
if (!orig_field->is_real_null())
{
- char buff[MAX_FIELD_WIDTH],*pos;
- String tmp(buff,sizeof(buff), charset), *res;
+ char buff[MAX_FIELD_WIDTH], *pos;
+ String tmp(buff, sizeof(buff), charset), *res;
res= orig_field->val_str(&tmp);
pos= (char*) sql_strmake(res->ptr(), res->length());
def= new Item_string(pos, res->length(), charset);
}
- orig_field->move_field(-diff); // Back to record[0]
+ orig_field->move_field_offset(-diff); // Back to record[0]
}
}
-/*
- maximum possible display length for blob
-
- SYNOPSIS
- Field_blob::max_display_length()
+/**
+ maximum possible display length for blob.
- RETURN
+ @return
length
*/
@@ -9139,24 +10237,23 @@ uint32 Field_blob::max_display_length()
Warning handling
*****************************************************************************/
-/*
- Produce warning or note about data saved into field
+/**
+ Produce warning or note about data saved into field.
- SYNOPSIS
- set_warning()
- level - level of message (Note/Warning/Error)
- code - error code of message to be produced
- cuted_increment - whenever we should increase cut fields count or not
+ @param level - level of message (Note/Warning/Error)
+ @param code - error code of message to be produced
+ @param cuted_increment - whenever we should increase cut fields count or not
- NOTE
+ @note
This function won't produce warning and increase cut fields counter
if count_cuted_fields == CHECK_FIELD_IGNORE for current thread.
if count_cuted_fields == CHECK_FIELD_IGNORE then we ignore notes.
This allows us to avoid notes in optimisation, like convert_constant_item().
- RETURN VALUE
+ @retval
1 if count_cuted_fields == CHECK_FIELD_IGNORE and error level is not NOTE
+ @retval
0 otherwise
*/
@@ -9180,21 +10277,19 @@ Field::set_warning(MYSQL_ERROR::enum_warning_level level, uint code,
}
-/*
- Produce warning or note about datetime string data saved into field
+/**
+ Produce warning or note about datetime string data saved into field.
- SYNOPSIS
- set_datime_warning()
- level - level of message (Note/Warning/Error)
- code - error code of message to be produced
- str - string value which we tried to save
- str_len - length of string which we tried to save
- ts_type - type of datetime value (datetime/date/time)
- cuted_increment - whenever we should increase cut fields count or not
-
- NOTE
- This function will always produce some warning but won't increase cut
- fields counter if count_cuted_fields == FIELD_CHECK_IGNORE for current
+ @param level level of message (Note/Warning/Error)
+ @param code error code of message to be produced
+ @param str string value which we tried to save
+ @param str_length length of string which we tried to save
+ @param ts_type type of datetime value (datetime/date/time)
+ @param cuted_increment whenever we should increase cut fields count or not
+
+ @note
+ This function will always produce some warning but won't increase cut
+ fields counter if count_cuted_fields ==FIELD_CHECK_IGNORE for current
thread.
*/
@@ -9212,20 +10307,18 @@ Field::set_datetime_warning(MYSQL_ERROR::enum_warning_level level, uint code,
}
-/*
- Produce warning or note about integer datetime value saved into field
+/**
+ Produce warning or note about integer datetime value saved into field.
- SYNOPSIS
- set_warning()
- level - level of message (Note/Warning/Error)
- code - error code of message to be produced
- nr - numeric value which we tried to save
- ts_type - type of datetime value (datetime/date/time)
- cuted_increment - whenever we should increase cut fields count or not
-
- NOTE
- This function will always produce some warning but won't increase cut
- fields counter if count_cuted_fields == FIELD_CHECK_IGNORE for current
+ @param level level of message (Note/Warning/Error)
+ @param code error code of message to be produced
+ @param nr numeric value which we tried to save
+ @param ts_type type of datetime value (datetime/date/time)
+ @param cuted_increment whenever we should increase cut fields count or not
+
+ @note
+ This function will always produce some warning but won't increase cut
+ fields counter if count_cuted_fields == FIELD_CHECK_IGNORE for current
thread.
*/
@@ -9246,19 +10339,17 @@ Field::set_datetime_warning(MYSQL_ERROR::enum_warning_level level, uint code,
}
-/*
- Produce warning or note about double datetime data saved into field
+/**
+ Produce warning or note about double datetime data saved into field.
- SYNOPSIS
- set_warning()
- level - level of message (Note/Warning/Error)
- code - error code of message to be produced
- nr - double value which we tried to save
- ts_type - type of datetime value (datetime/date/time)
-
- NOTE
- This function will always produce some warning but won't increase cut
- fields counter if count_cuted_fields == FIELD_CHECK_IGNORE for current
+ @param level level of message (Note/Warning/Error)
+ @param code error code of message to be produced
+ @param nr double value which we tried to save
+ @param ts_type type of datetime value (datetime/date/time)
+
+ @note
+ This function will always produce some warning but won't increase cut
+ fields counter if count_cuted_fields == FIELD_CHECK_IGNORE for current
thread.
*/
@@ -9277,4 +10368,3 @@ Field::set_datetime_warning(MYSQL_ERROR::enum_warning_level level, uint code,
field_name);
}
}
-
diff --git a/sql/field.h b/sql/field.h
index 2975719a591..5136f760fc1 100644
--- a/sql/field.h
+++ b/sql/field.h
@@ -1,4 +1,4 @@
-/* Copyright (C) 2000-2006 MySQL AB
+/* Copyright 2000-2008 MySQL AB, 2008 Sun Microsystems, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -13,7 +13,6 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
-
/*
Because of the function new_field() all field classes that have static
variables must declare the size_of() member function.
@@ -29,6 +28,9 @@ const uint32 max_field_size= (uint32) 4294967295U;
class Send_field;
class Protocol;
+class Create_field;
+class Relay_log_info;
+
struct st_cache_field;
int field_conv(Field *to,Field *from);
@@ -49,10 +51,10 @@ class Field
void operator=(Field &);
public:
static void *operator new(size_t size) throw ()
- { return (void*) sql_alloc((uint) size); }
+ { return sql_alloc(size); }
static void operator delete(void *ptr_arg, size_t size) { TRASH(ptr_arg, size); }
- char *ptr; // Position to field in record
+ uchar *ptr; // Position to field in record
uchar *null_ptr; // Byte where null_bit is
/*
Note that you can use table->in_use as replacement for current_thd member
@@ -62,9 +64,9 @@ public:
struct st_table *orig_table; // Pointer to original table
const char **table_name, *field_name;
LEX_STRING comment;
- query_id_t query_id; // For quick test of used fields
/* Field is part of the following keys */
- key_map key_start,part_of_key,part_of_sortkey;
+ key_map key_start, part_of_key, part_of_key_not_clustered;
+ key_map part_of_sortkey;
/*
We use three additional unireg types for TIMESTAMP to overcome limitation
of current binary format of .frm file. We'd like to be able to support
@@ -87,8 +89,8 @@ public:
utype unireg_check;
uint32 field_length; // Length of field
- uint field_index; // field number in fields array
- uint16 flags;
+ uint32 flags;
+ uint16 field_index; // field number in fields array
uchar null_bit; // Bit used to test null bit
/**
If true, this field was created in create_tmp_field_from_item from a NULL
@@ -101,16 +103,18 @@ public:
*/
bool is_created_from_null_item;
- Field(char *ptr_arg,uint32 length_arg,uchar *null_ptr_arg,uchar null_bit_arg,
- utype unireg_check_arg, const char *field_name_arg,
- struct st_table *table_arg);
+ Field(uchar *ptr_arg,uint32 length_arg,uchar *null_ptr_arg,
+ uchar null_bit_arg, utype unireg_check_arg,
+ const char *field_name_arg);
virtual ~Field() {}
/* Store functions returns 1 on overflow and -1 on fatal error */
- virtual int store(const char *to,uint length,CHARSET_INFO *cs)=0;
+ virtual int store(const char *to, uint length,CHARSET_INFO *cs)=0;
virtual int store(double nr)=0;
virtual int store(longlong nr, bool unsigned_val)=0;
virtual int store_decimal(const my_decimal *d)=0;
virtual int store_time(MYSQL_TIME *ltime, timestamp_type t_type);
+ int store(const char *to, uint length, CHARSET_INFO *cs,
+ enum_check_fields check_level);
virtual double val_real(void)=0;
virtual longlong val_int(void)=0;
virtual my_decimal *val_decimal(my_decimal *);
@@ -129,6 +133,11 @@ public:
*/
virtual String *val_str(String*,String *)=0;
String *val_int_as_str(String *val_buffer, my_bool unsigned_flag);
+ /*
+ str_needs_quotes() returns TRUE if the value returned by val_str() needs
+ to be quoted when used in constructing an SQL query.
+ */
+ virtual bool str_needs_quotes() { return FALSE; }
virtual Item_result result_type () const=0;
virtual Item_result cmp_type () const { return result_type(); }
virtual Item_result cast_to_int_type () const { return result_type(); }
@@ -155,12 +164,42 @@ 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 *);
+ 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.
+ */
+ virtual uint row_pack_length() { return 0; }
+ virtual int save_field_metadata(uchar *first_byte)
+ { return do_save_field_metadata(first_byte); }
/*
data_length() return the "real size" of the data in memory.
*/
- virtual uint32 data_length(const char *from) { return pack_length(); }
+ virtual uint32 data_length() { return pack_length(); }
virtual uint32 sort_length() const { return pack_length(); }
+
+ /**
+ Get the maximum size of the data in packed format.
+
+ @return Maximum data length of the field when packed using the
+ Field::pack() function.
+ */
+ virtual uint32 max_data_length() const {
+ return pack_length();
+ };
+
virtual int reset(void) { bzero(ptr,pack_length()); return 0; }
virtual void reset_fields() {}
virtual void set_default()
@@ -170,7 +209,7 @@ public:
memcpy(ptr, ptr + l_offset, pack_length());
if (null_ptr)
*null_ptr= ((*null_ptr & (uchar) ~null_bit) |
- null_ptr[l_offset] & null_bit);
+ (null_ptr[l_offset] & null_bit));
}
virtual bool binary() const { return 1; }
virtual bool zero_pack() const { return 1; }
@@ -178,18 +217,20 @@ public:
virtual uint32 key_length() const { return pack_length(); }
virtual enum_field_types type() const =0;
virtual enum_field_types real_type() const { return type(); }
- inline int cmp(const char *str) { return cmp(ptr,str); }
- virtual int cmp(const char *,const char *)=0;
- virtual int cmp_binary(const char *a,const char *b, uint32 max_length=~0L)
+ inline int cmp(const uchar *str) { return cmp(ptr,str); }
+ virtual int cmp_max(const uchar *a, const uchar *b, uint max_len)
+ { return cmp(a, b); }
+ virtual int cmp(const uchar *,const uchar *)=0;
+ virtual int cmp_binary(const uchar *a,const uchar *b, uint32 max_length=~0L)
{ return memcmp(a,b,pack_length()); }
virtual int cmp_offset(uint row_offset)
{ return cmp(ptr,ptr+row_offset); }
virtual int cmp_binary_offset(uint row_offset)
{ return cmp_binary(ptr, ptr+row_offset); };
- virtual int key_cmp(const byte *a,const byte *b)
- { return cmp((char*) a,(char*) b); }
- virtual int key_cmp(const byte *str, uint length)
- { return cmp(ptr,(char*) str); }
+ virtual int key_cmp(const uchar *a,const uchar *b)
+ { return cmp(a, b); }
+ virtual int key_cmp(const uchar *str, uint length)
+ { return cmp(ptr,str); }
virtual uint decimals() const { return 0; }
/*
Caller beware: sql_type can change str.Ptr, so check
@@ -198,25 +239,58 @@ public:
*/
virtual void sql_type(String &str) const =0;
virtual uint size_of() const =0; // For new field
- inline bool is_null(uint row_offset=0)
+ inline bool is_null(my_ptrdiff_t row_offset= 0)
{ return null_ptr ? (null_ptr[row_offset] & null_bit ? 1 : 0) : table->null_row; }
- inline bool is_real_null(uint row_offset=0)
+ inline bool is_real_null(my_ptrdiff_t row_offset= 0)
{ return null_ptr ? (null_ptr[row_offset] & null_bit ? 1 : 0) : 0; }
inline bool is_null_in_record(const uchar *record)
{
if (!null_ptr)
return 0;
- return test(record[(uint) (null_ptr - (uchar*) table->record[0])] &
+ return test(record[(uint) (null_ptr -table->record[0])] &
null_bit);
}
- inline void set_null(int row_offset=0)
+ inline bool is_null_in_record_with_offset(my_ptrdiff_t offset)
+ {
+ if (!null_ptr)
+ return 0;
+ return test(null_ptr[offset] & null_bit);
+ }
+ inline void set_null(my_ptrdiff_t row_offset= 0)
{ if (null_ptr) null_ptr[row_offset]|= null_bit; }
- inline void set_notnull(int row_offset=0)
+ inline void set_notnull(my_ptrdiff_t row_offset= 0)
{ if (null_ptr) null_ptr[row_offset]&= (uchar) ~null_bit; }
inline bool maybe_null(void) { return null_ptr != 0 || table->maybe_null; }
inline bool real_maybe_null(void) { return null_ptr != 0; }
+
+ enum {
+ LAST_NULL_BYTE_UNDEF= 0
+ };
+
+ /*
+ Find the position of the last null byte for the field.
+
+ SYNOPSIS
+ last_null_byte()
+
+ DESCRIPTION
+ Return a pointer to the last byte of the null bytes where the
+ field conceptually is placed.
+
+ RETURN VALUE
+ The position of the last null byte relative to the beginning of
+ the record. If the field does not use any bits of the null
+ bytes, the value 0 (LAST_NULL_BYTE_UNDEF) is returned.
+ */
+ size_t last_null_byte() const {
+ size_t bytes= do_last_null_byte();
+ DBUG_PRINT("debug", ("last_null_byte() ==> %ld", (long) bytes));
+ DBUG_ASSERT(bytes <= table->s->null_bytes);
+ return bytes;
+ }
+
virtual void make_field(Send_field *);
- virtual void sort_string(char *buff,uint length)=0;
+ virtual void sort_string(uchar *buff,uint length)=0;
virtual bool optimize_range(uint idx, uint part);
/*
This should be true for fields which, when compared with constant
@@ -230,22 +304,23 @@ public:
virtual Field *new_field(MEM_ROOT *root, struct st_table *new_table,
bool keep_type);
virtual Field *new_key_field(MEM_ROOT *root, struct st_table *new_table,
- char *new_ptr, uchar *new_null_ptr,
+ uchar *new_ptr, uchar *new_null_ptr,
uint new_null_bit);
- inline void move_field(char *ptr_arg,uchar *null_ptr_arg,uchar null_bit_arg)
+ Field *clone(MEM_ROOT *mem_root, struct st_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;
}
- inline void move_field(char *ptr_arg) { ptr=ptr_arg; }
- virtual inline void move_field(my_ptrdiff_t ptr_diff)
+ inline void move_field(uchar *ptr_arg) { ptr=ptr_arg; }
+ virtual void move_field_offset(my_ptrdiff_t ptr_diff)
{
- ptr=ADD_TO_PTR(ptr,ptr_diff,char*);
+ ptr=ADD_TO_PTR(ptr,ptr_diff, uchar*);
if (null_ptr)
null_ptr=ADD_TO_PTR(null_ptr,ptr_diff,uchar*);
}
- virtual void get_image(char *buff, uint length, CHARSET_INFO *cs)
+ virtual void get_image(uchar *buff, uint length, CHARSET_INFO *cs)
{ memcpy(buff,ptr,length); }
- virtual void set_image(char *buff,uint length, CHARSET_INFO *cs)
+ virtual void set_image(const uchar *buff,uint length, CHARSET_INFO *cs)
{ memcpy(ptr,buff,length); }
@@ -275,12 +350,12 @@ public:
Number of copied bytes (excluding padded zero bytes -- see above).
*/
- virtual uint get_key_image(char *buff, uint length, imagetype type)
+ virtual uint get_key_image(uchar *buff, uint length, imagetype type)
{
get_image(buff, length, &my_charset_bin);
return length;
}
- virtual void set_key_image(char *buff,uint length)
+ virtual void set_key_image(const uchar *buff,uint length)
{ set_image(buff,length, &my_charset_bin); }
inline longlong val_int_offset(uint row_offset)
{
@@ -289,53 +364,79 @@ public:
ptr-=row_offset;
return tmp;
}
-
- inline String *val_str(String *str, char *new_ptr)
+ inline longlong val_int(const uchar *new_ptr)
{
- char *old_ptr= ptr;
- ptr= new_ptr;
+ uchar *old_ptr= ptr;
+ longlong return_value;
+ ptr= (uchar*) new_ptr;
+ return_value= val_int();
+ ptr= old_ptr;
+ return return_value;
+ }
+ inline String *val_str(String *str, const uchar *new_ptr)
+ {
+ uchar *old_ptr= ptr;
+ ptr= (uchar*) new_ptr;
val_str(str);
ptr= old_ptr;
return str;
}
virtual bool send_binary(Protocol *protocol);
- virtual char *pack(char* to, const char *from, uint max_length=~(uint) 0)
+
+ virtual uchar *pack(uchar *to, const uchar *from,
+ uint max_length, bool low_byte_first);
+ /**
+ @overload Field::pack(uchar*, const uchar*, uint, bool)
+ */
+ uchar *pack(uchar *to, const uchar *from)
{
- uint32 length=pack_length();
- memcpy(to,from,length);
- return to+length;
+ DBUG_ENTER("Field::pack");
+ uchar *result= this->pack(to, from, UINT_MAX, table->s->db_low_byte_first);
+ DBUG_RETURN(result);
}
- virtual const char *unpack(char* to, const char *from)
+
+ virtual const uchar *unpack(uchar* to, const uchar *from,
+ uint param_data, bool low_byte_first);
+ /**
+ @overload Field::unpack(uchar*, const uchar*, uint, bool)
+ */
+ const uchar *unpack(uchar* to, const uchar *from)
{
- uint length=pack_length();
- memcpy(to,from,length);
- return from+length;
+ DBUG_ENTER("Field::unpack");
+ const uchar *result= unpack(to, from, 0U, table->s->db_low_byte_first);
+ DBUG_RETURN(result);
}
- virtual char *pack_key(char* to, const char *from, uint max_length)
+
+ virtual uchar *pack_key(uchar* to, const uchar *from,
+ uint max_length, bool low_byte_first)
{
- return pack(to,from,max_length);
+ return pack(to, from, max_length, low_byte_first);
}
- virtual char *pack_key_from_key_image(char* to, const char *from,
- uint max_length)
+ virtual uchar *pack_key_from_key_image(uchar* to, const uchar *from,
+ uint max_length, bool low_byte_first)
{
- return pack(to,from,max_length);
+ return pack(to, from, max_length, low_byte_first);
}
- virtual const char *unpack_key(char* to, const char *from, uint max_length)
+ virtual const uchar *unpack_key(uchar* to, const uchar *from,
+ uint max_length, bool low_byte_first)
{
- return unpack(to,from);
+ return unpack(to, from, max_length, low_byte_first);
}
- virtual uint packed_col_length(const char *to, uint length)
+ 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 char *a,const char *b, uint key_length_arg,
+ virtual int pack_cmp(const uchar *a,const uchar *b, uint key_length_arg,
my_bool insert_or_update)
{ return cmp(a,b); }
- virtual int pack_cmp(const char *b, uint key_length_arg,
+ virtual int pack_cmp(const uchar *b, uint key_length_arg,
my_bool insert_or_update)
{ return cmp(ptr,b); }
- uint offset(); // Should be inline ...
+ 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);
@@ -362,8 +463,16 @@ public:
return (op_result == E_DEC_OVERFLOW);
}
int warn_if_overflow(int op_result);
+ void init(TABLE *table_arg)
+ {
+ orig_table= table= table_arg;
+ table_name= &table_arg->alias;
+ }
+
/* maximum possible display length */
virtual uint32 max_display_length()= 0;
+
+ virtual uint is_equal(Create_field *new_field);
/* convert decimal to longlong with overflow check */
longlong convert_decimal2longlong(const my_decimal *val, bool unsigned_flag,
int *err);
@@ -372,14 +481,17 @@ public:
{
return field_length / charset()->mbmaxlen;
}
+
virtual geometry_type get_geometry_type()
{
/* shouldn't get here. */
DBUG_ASSERT(0);
return GEOM_GEOMETRY;
}
+ /* Hash value */
+ virtual void hash(ulong *nr, ulong *nr2);
friend bool reopen_table(THD *,struct st_table *,bool);
- friend int cre_myisam(my_string name, register TABLE *form, uint options,
+ friend int cre_myisam(char * name, register TABLE *form, uint options,
ulonglong auto_increment_value);
friend class Copy_field;
friend class Item_avg_field;
@@ -393,6 +505,104 @@ public:
friend class Item_sum_min;
friend class Item_sum_max;
friend class Item_func_group_concat;
+
+private:
+ /*
+ Primitive for implementing last_null_byte().
+
+ SYNOPSIS
+ do_last_null_byte()
+
+ DESCRIPTION
+ Primitive for the implementation of the last_null_byte()
+ function. This represents the inheritance interface and can be
+ overridden by subclasses.
+ */
+ virtual size_t do_last_null_byte() const;
+
+/**
+ Retrieve the field metadata for fields.
+
+ This default implementation returns 0 and saves 0 in the metadata_ptr
+ value.
+
+ @param metadata_ptr First byte of field metadata
+
+ @returns 0 no bytes written.
+*/
+ virtual int do_save_field_metadata(uchar *metadata_ptr)
+ { return 0; }
+
+protected:
+ /*
+ Helper function to pack()/unpack() int32 values
+ */
+ static void handle_int32(uchar *to, const uchar *from,
+ bool low_byte_first_from, bool low_byte_first_to)
+ {
+ int32 val;
+#ifdef WORDS_BIGENDIAN
+ if (low_byte_first_from)
+ val = sint4korr(from);
+ else
+#endif
+ longget(val, from);
+
+#ifdef WORDS_BIGENDIAN
+ if (low_byte_first_to)
+ int4store(to, val);
+ else
+#endif
+ longstore(to, val);
+ }
+
+ /*
+ Helper function to pack()/unpack() int64 values
+ */
+ static void handle_int64(uchar* to, const uchar *from,
+ bool low_byte_first_from, bool low_byte_first_to)
+ {
+ int64 val;
+#ifdef WORDS_BIGENDIAN
+ if (low_byte_first_from)
+ val = sint8korr(from);
+ else
+#endif
+ longlongget(val, from);
+
+#ifdef WORDS_BIGENDIAN
+ if (low_byte_first_to)
+ int8store(to, val);
+ else
+#endif
+ longlongstore(to, val);
+ }
+
+ uchar *pack_int32(uchar *to, const uchar *from, bool low_byte_first_to)
+ {
+ handle_int32(to, from, table->s->db_low_byte_first, low_byte_first_to);
+ return to + sizeof(int32);
+ }
+
+ const uchar *unpack_int32(uchar* to, const uchar *from,
+ bool low_byte_first_from)
+ {
+ handle_int32(to, from, low_byte_first_from, table->s->db_low_byte_first);
+ return from + sizeof(int32);
+ }
+
+ uchar *pack_int64(uchar* to, const uchar *from, bool low_byte_first_to)
+ {
+ handle_int64(to, from, table->s->db_low_byte_first, low_byte_first_to);
+ return to + sizeof(int64);
+ }
+
+ const uchar *unpack_int64(uchar* to, const uchar *from,
+ bool low_byte_first_from)
+ {
+ handle_int64(to, from, low_byte_first_from, table->s->db_low_byte_first);
+ return from + sizeof(int64);
+ }
};
@@ -400,21 +610,21 @@ class Field_num :public Field {
public:
const uint8 dec;
bool zerofill,unsigned_flag; // Purify cannot handle bit fields
- Field_num(char *ptr_arg,uint32 len_arg, uchar *null_ptr_arg,
+ Field_num(uchar *ptr_arg,uint32 len_arg, uchar *null_ptr_arg,
uchar null_bit_arg, utype unireg_check_arg,
const char *field_name_arg,
- struct st_table *table_arg,
uint8 dec_arg, bool zero_arg, bool unsigned_arg);
Item_result result_type () const { return REAL_RESULT; }
void prepend_zeros(String *value);
void add_zerofill_and_unsigned(String &res) const;
- friend class create_field;
+ friend class Create_field;
void make_field(Send_field *);
uint decimals() const { return (uint) dec; }
uint size_of() const { return sizeof(*this); }
bool eq_def(Field *field);
int store_decimal(const my_decimal *);
my_decimal *val_decimal(my_decimal *);
+ uint is_equal(Create_field *new_field);
int check_int(CHARSET_INFO *cs, const char *str, int length,
const char *int_end, int error);
bool get_int(CHARSET_INFO *cs, const char *from, uint len,
@@ -428,10 +638,9 @@ protected:
CHARSET_INFO *field_charset;
enum Derivation field_derivation;
public:
- Field_str(char *ptr_arg,uint32 len_arg, uchar *null_ptr_arg,
+ Field_str(uchar *ptr_arg,uint32 len_arg, uchar *null_ptr_arg,
uchar null_bit_arg, utype unireg_check_arg,
- const char *field_name_arg,
- struct st_table *table_arg, CHARSET_INFO *charset);
+ const char *field_name_arg, CHARSET_INFO *charset);
Item_result result_type () const { return STRING_RESULT; }
uint decimals() const { return NOT_FIXED_DEC; }
int store(double nr);
@@ -446,8 +655,11 @@ public:
{ field_derivation= derivation_arg; }
bool binary() const { return field_charset == &my_charset_bin; }
uint32 max_display_length() { return field_length; }
- friend class create_field;
+ friend class Create_field;
my_decimal *val_decimal(my_decimal *);
+ virtual bool str_needs_quotes() { return TRUE; }
+ bool compare_str_field_flags(Create_field *new_field, uint32 flags);
+ uint is_equal(Create_field *new_field);
};
@@ -459,15 +671,15 @@ protected:
int report_if_important_data(const char *ptr, const char *end,
bool count_spaces);
public:
- Field_longstr(char *ptr_arg, uint32 len_arg, uchar *null_ptr_arg,
+ Field_longstr(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg,
uchar null_bit_arg, utype unireg_check_arg,
- const char *field_name_arg,
- struct st_table *table_arg, CHARSET_INFO *charset_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, table_arg, charset_arg)
+ field_name_arg, charset_arg)
{}
int store_decimal(const my_decimal *d);
+ uint32 max_data_length() const;
};
/* base class for float and double and decimal (old one) */
@@ -475,37 +687,37 @@ class Field_real :public Field_num {
public:
my_bool not_fixed;
- Field_real(char *ptr_arg, uint32 len_arg, uchar *null_ptr_arg,
+ Field_real(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg,
uchar null_bit_arg, utype unireg_check_arg,
const char *field_name_arg,
- struct st_table *table_arg,
uint8 dec_arg, bool zero_arg, bool unsigned_arg)
:Field_num(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, unireg_check_arg,
- field_name_arg, table_arg, dec_arg, zero_arg, unsigned_arg),
+ field_name_arg, dec_arg, zero_arg, unsigned_arg),
not_fixed(dec_arg >= NOT_FIXED_DEC)
{}
-
-
int store_decimal(const my_decimal *);
my_decimal *val_decimal(my_decimal *);
int truncate(double *nr, double max_length);
uint32 max_display_length() { return field_length; }
uint size_of() const { return sizeof(*this); }
+ virtual const uchar *unpack(uchar* to, const uchar *from,
+ uint param_data, bool low_byte_first);
+ virtual uchar *pack(uchar* to, const uchar *from,
+ uint max_length, bool low_byte_first);
};
class Field_decimal :public Field_real {
public:
- Field_decimal(char *ptr_arg, uint32 len_arg, uchar *null_ptr_arg,
+ Field_decimal(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg,
uchar null_bit_arg,
enum utype unireg_check_arg, const char *field_name_arg,
- struct st_table *table_arg,
uint8 dec_arg,bool zero_arg,bool unsigned_arg)
:Field_real(ptr_arg, len_arg, null_ptr_arg, null_bit_arg,
- unireg_check_arg, field_name_arg, table_arg,
+ unireg_check_arg, field_name_arg,
dec_arg, zero_arg, unsigned_arg)
{}
- enum_field_types type() const { return FIELD_TYPE_DECIMAL;}
+ enum_field_types type() const { return MYSQL_TYPE_DECIMAL;}
enum ha_base_keytype key_type() const
{ return zerofill ? HA_KEYTYPE_BINARY : HA_KEYTYPE_NUM; }
int reset(void);
@@ -515,16 +727,28 @@ public:
double val_real(void);
longlong val_int(void);
String *val_str(String*,String *);
- int cmp(const char *,const char*);
- void sort_string(char *buff,uint length);
+ int cmp(const uchar *,const uchar *);
+ void sort_string(uchar *buff,uint length);
void overflow(bool negative);
bool zero_pack() const { return 0; }
void sql_type(String &str) const;
+ virtual const uchar *unpack(uchar* to, const uchar *from,
+ uint param_data, bool low_byte_first)
+ {
+ return Field::unpack(to, from, param_data, low_byte_first);
+ }
+ virtual uchar *pack(uchar* to, const uchar *from,
+ uint max_length, bool low_byte_first)
+ {
+ return Field::pack(to, from, max_length, low_byte_first);
+ }
};
/* New decimal/numeric field which use fixed point arithmetic */
class Field_new_decimal :public Field_num {
+private:
+ int do_save_field_metadata(uchar *first_byte);
public:
/* The maximum number of decimal digits can be stored */
uint precision;
@@ -535,16 +759,14 @@ public:
So for example we need to count length from precision handling
CREATE TABLE ( DECIMAL(x,y))
*/
- Field_new_decimal(char *ptr_arg, uint32 len_arg, uchar *null_ptr_arg,
+ Field_new_decimal(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg,
uchar null_bit_arg,
enum utype unireg_check_arg, const char *field_name_arg,
- struct st_table *table_arg,
uint8 dec_arg, bool zero_arg, bool unsigned_arg);
Field_new_decimal(uint32 len_arg, bool maybe_null_arg,
- const char *field_name_arg,
- struct st_table *table_arg, uint8 dec_arg,
+ const char *field_name_arg, uint8 dec_arg,
bool unsigned_arg);
- enum_field_types type() const { return FIELD_TYPE_NEWDECIMAL;}
+ enum_field_types type() const { return MYSQL_TYPE_NEWDECIMAL;}
enum ha_base_keytype key_type() const { return HA_KEYTYPE_BINARY; }
Item_result result_type () const { return DECIMAL_RESULT; }
int reset(void);
@@ -559,29 +781,35 @@ public:
longlong val_int(void);
my_decimal *val_decimal(my_decimal *);
String *val_str(String*, String *);
- int cmp(const char *, const char*);
- void sort_string(char *buff, uint length);
+ int cmp(const uchar *, const uchar *);
+ void sort_string(uchar *buff, uint length);
bool zero_pack() const { return 0; }
void sql_type(String &str) const;
uint32 max_display_length() { return field_length; }
uint size_of() const { return sizeof(*this); }
uint32 pack_length() const { return (uint32) bin_size; }
+ uint pack_length_from_metadata(uint field_metadata);
+ uint row_pack_length() { return pack_length(); }
+ int compatible_field_size(uint field_metadata,
+ const Relay_log_info *rli);
+ uint is_equal(Create_field *new_field);
+ virtual const uchar *unpack(uchar* to, const uchar *from,
+ uint param_data, bool low_byte_first);
};
class Field_tiny :public Field_num {
public:
- Field_tiny(char *ptr_arg, uint32 len_arg, uchar *null_ptr_arg,
+ Field_tiny(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg,
uchar null_bit_arg,
enum utype unireg_check_arg, const char *field_name_arg,
- struct st_table *table_arg,
bool zero_arg, bool unsigned_arg)
:Field_num(ptr_arg, len_arg, null_ptr_arg, null_bit_arg,
- unireg_check_arg, field_name_arg, table_arg,
+ unireg_check_arg, field_name_arg,
0, zero_arg,unsigned_arg)
{}
enum Item_result result_type () const { return INT_RESULT; }
- enum_field_types type() const { return FIELD_TYPE_TINY;}
+ enum_field_types type() const { return MYSQL_TYPE_TINY;}
enum ha_base_keytype key_type() const
{ return unsigned_flag ? HA_KEYTYPE_BINARY : HA_KEYTYPE_INT8; }
int store(const char *to,uint length,CHARSET_INFO *charset);
@@ -592,32 +820,45 @@ public:
longlong val_int(void);
String *val_str(String*,String *);
bool send_binary(Protocol *protocol);
- int cmp(const char *,const char*);
- void sort_string(char *buff,uint length);
+ int cmp(const uchar *,const uchar *);
+ void sort_string(uchar *buff,uint length);
uint32 pack_length() const { return 1; }
void sql_type(String &str) const;
uint32 max_display_length() { return 4; }
+
+ virtual uchar *pack(uchar* to, const uchar *from,
+ uint max_length, bool low_byte_first)
+ {
+ *to= *from;
+ return to + 1;
+ }
+
+ virtual const uchar *unpack(uchar* to, const uchar *from,
+ uint param_data, bool low_byte_first)
+ {
+ *to= *from;
+ return from + 1;
+ }
};
class Field_short :public Field_num {
public:
- Field_short(char *ptr_arg, uint32 len_arg, uchar *null_ptr_arg,
+ Field_short(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg,
uchar null_bit_arg,
enum utype unireg_check_arg, const char *field_name_arg,
- struct st_table *table_arg,
bool zero_arg, bool unsigned_arg)
:Field_num(ptr_arg, len_arg, null_ptr_arg, null_bit_arg,
- unireg_check_arg, field_name_arg, table_arg,
+ unireg_check_arg, field_name_arg,
0, zero_arg,unsigned_arg)
{}
Field_short(uint32 len_arg,bool maybe_null_arg, const char *field_name_arg,
- struct st_table *table_arg,bool unsigned_arg)
- :Field_num((char*) 0, len_arg, maybe_null_arg ? (uchar*) "": 0,0,
- NONE, field_name_arg, table_arg,0,0,unsigned_arg)
+ bool unsigned_arg)
+ :Field_num((uchar*) 0, len_arg, maybe_null_arg ? (uchar*) "": 0,0,
+ NONE, field_name_arg, 0, 0, unsigned_arg)
{}
enum Item_result result_type () const { return INT_RESULT; }
- enum_field_types type() const { return FIELD_TYPE_SHORT;}
+ enum_field_types type() const { return MYSQL_TYPE_SHORT;}
enum ha_base_keytype key_type() const
{ return unsigned_flag ? HA_KEYTYPE_USHORT_INT : HA_KEYTYPE_SHORT_INT;}
int store(const char *to,uint length,CHARSET_INFO *charset);
@@ -628,27 +869,65 @@ public:
longlong val_int(void);
String *val_str(String*,String *);
bool send_binary(Protocol *protocol);
- int cmp(const char *,const char*);
- void sort_string(char *buff,uint length);
+ int cmp(const uchar *,const uchar *);
+ void sort_string(uchar *buff,uint length);
uint32 pack_length() const { return 2; }
void sql_type(String &str) const;
uint32 max_display_length() { return 6; }
-};
+ virtual uchar *pack(uchar* to, const uchar *from,
+ uint max_length, bool low_byte_first)
+ {
+ int16 val;
+#ifdef WORDS_BIGENDIAN
+ if (table->s->db_low_byte_first)
+ val = sint2korr(from);
+ else
+#endif
+ shortget(val, from);
+
+#ifdef WORDS_BIGENDIAN
+ if (low_byte_first)
+ int2store(to, val);
+ else
+#endif
+ shortstore(to, val);
+ return to + sizeof(val);
+ }
+
+ virtual const uchar *unpack(uchar* to, const uchar *from,
+ uint param_data, bool low_byte_first)
+ {
+ int16 val;
+#ifdef WORDS_BIGENDIAN
+ if (low_byte_first)
+ val = sint2korr(from);
+ else
+#endif
+ shortget(val, from);
+
+#ifdef WORDS_BIGENDIAN
+ if (table->s->db_low_byte_first)
+ int2store(to, val);
+ else
+#endif
+ shortstore(to, val);
+ return from + sizeof(val);
+ }
+};
class Field_medium :public Field_num {
public:
- Field_medium(char *ptr_arg, uint32 len_arg, uchar *null_ptr_arg,
+ Field_medium(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg,
uchar null_bit_arg,
enum utype unireg_check_arg, const char *field_name_arg,
- struct st_table *table_arg,
bool zero_arg, bool unsigned_arg)
:Field_num(ptr_arg, len_arg, null_ptr_arg, null_bit_arg,
- unireg_check_arg, field_name_arg, table_arg,
+ unireg_check_arg, field_name_arg,
0, zero_arg,unsigned_arg)
{}
enum Item_result result_type () const { return INT_RESULT; }
- enum_field_types type() const { return FIELD_TYPE_INT24;}
+ enum_field_types type() const { return MYSQL_TYPE_INT24;}
enum ha_base_keytype key_type() const
{ return unsigned_flag ? HA_KEYTYPE_UINT24 : HA_KEYTYPE_INT24; }
int store(const char *to,uint length,CHARSET_INFO *charset);
@@ -659,32 +938,43 @@ public:
longlong val_int(void);
String *val_str(String*,String *);
bool send_binary(Protocol *protocol);
- int cmp(const char *,const char*);
- void sort_string(char *buff,uint length);
+ int cmp(const uchar *,const uchar *);
+ void sort_string(uchar *buff,uint length);
uint32 pack_length() const { return 3; }
void sql_type(String &str) const;
uint32 max_display_length() { return 8; }
+
+ virtual uchar *pack(uchar* to, const uchar *from,
+ uint max_length, bool low_byte_first)
+ {
+ return Field::pack(to, from, max_length, low_byte_first);
+ }
+
+ virtual const uchar *unpack(uchar* to, const uchar *from,
+ uint param_data, bool low_byte_first)
+ {
+ return Field::unpack(to, from, param_data, low_byte_first);
+ }
};
class Field_long :public Field_num {
public:
- Field_long(char *ptr_arg, uint32 len_arg, uchar *null_ptr_arg,
+ Field_long(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg,
uchar null_bit_arg,
enum utype unireg_check_arg, const char *field_name_arg,
- struct st_table *table_arg,
bool zero_arg, bool unsigned_arg)
:Field_num(ptr_arg, len_arg, null_ptr_arg, null_bit_arg,
- unireg_check_arg, field_name_arg, table_arg,
+ unireg_check_arg, field_name_arg,
0, zero_arg,unsigned_arg)
{}
Field_long(uint32 len_arg,bool maybe_null_arg, const char *field_name_arg,
- struct st_table *table_arg,bool unsigned_arg)
- :Field_num((char*) 0, len_arg, maybe_null_arg ? (uchar*) "": 0,0,
- NONE, field_name_arg, table_arg,0,0,unsigned_arg)
+ bool unsigned_arg)
+ :Field_num((uchar*) 0, len_arg, maybe_null_arg ? (uchar*) "": 0,0,
+ NONE, field_name_arg,0,0,unsigned_arg)
{}
enum Item_result result_type () const { return INT_RESULT; }
- enum_field_types type() const { return FIELD_TYPE_LONG;}
+ enum_field_types type() const { return MYSQL_TYPE_LONG;}
enum ha_base_keytype key_type() const
{ return unsigned_flag ? HA_KEYTYPE_ULONG_INT : HA_KEYTYPE_LONG_INT; }
int store(const char *to,uint length,CHARSET_INFO *charset);
@@ -695,34 +985,45 @@ public:
longlong val_int(void);
bool send_binary(Protocol *protocol);
String *val_str(String*,String *);
- int cmp(const char *,const char*);
- void sort_string(char *buff,uint length);
+ int cmp(const uchar *,const uchar *);
+ void sort_string(uchar *buff,uint length);
uint32 pack_length() const { return 4; }
void sql_type(String &str) const;
uint32 max_display_length() { return MY_INT32_NUM_DECIMAL_DIGITS; }
+ virtual uchar *pack(uchar* to, const uchar *from,
+ uint max_length __attribute__((unused)),
+ bool low_byte_first)
+ {
+ return pack_int32(to, from, low_byte_first);
+ }
+ virtual const uchar *unpack(uchar* to, const uchar *from,
+ uint param_data __attribute__((unused)),
+ bool low_byte_first)
+ {
+ return unpack_int32(to, from, low_byte_first);
+ }
};
#ifdef HAVE_LONG_LONG
class Field_longlong :public Field_num {
public:
- Field_longlong(char *ptr_arg, uint32 len_arg, uchar *null_ptr_arg,
+ Field_longlong(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg,
uchar null_bit_arg,
enum utype unireg_check_arg, const char *field_name_arg,
- struct st_table *table_arg,
bool zero_arg, bool unsigned_arg)
:Field_num(ptr_arg, len_arg, null_ptr_arg, null_bit_arg,
- unireg_check_arg, field_name_arg, table_arg,
+ unireg_check_arg, field_name_arg,
0, zero_arg,unsigned_arg)
{}
Field_longlong(uint32 len_arg,bool maybe_null_arg,
const char *field_name_arg,
- struct st_table *table_arg, bool unsigned_arg)
- :Field_num((char*) 0, len_arg, maybe_null_arg ? (uchar*) "": 0,0,
- NONE, field_name_arg, table_arg,0,0,unsigned_arg)
+ bool unsigned_arg)
+ :Field_num((uchar*) 0, len_arg, maybe_null_arg ? (uchar*) "": 0,0,
+ NONE, field_name_arg,0,0,unsigned_arg)
{}
enum Item_result result_type () const { return INT_RESULT; }
- enum_field_types type() const { return FIELD_TYPE_LONGLONG;}
+ enum_field_types type() const { return MYSQL_TYPE_LONGLONG;}
enum ha_base_keytype key_type() const
{ return unsigned_flag ? HA_KEYTYPE_ULONGLONG : HA_KEYTYPE_LONGLONG; }
int store(const char *to,uint length,CHARSET_INFO *charset);
@@ -737,33 +1038,44 @@ public:
longlong val_int(void);
String *val_str(String*,String *);
bool send_binary(Protocol *protocol);
- int cmp(const char *,const char*);
- void sort_string(char *buff,uint length);
+ int cmp(const uchar *,const uchar *);
+ void sort_string(uchar *buff,uint length);
uint32 pack_length() const { return 8; }
void sql_type(String &str) const;
bool can_be_compared_as_longlong() const { return TRUE; }
uint32 max_display_length() { return 20; }
+ virtual uchar *pack(uchar* to, const uchar *from,
+ uint max_length __attribute__((unused)),
+ bool low_byte_first)
+ {
+ return pack_int64(to, from, low_byte_first);
+ }
+ virtual const uchar *unpack(uchar* to, const uchar *from,
+ uint param_data __attribute__((unused)),
+ bool low_byte_first)
+ {
+ return unpack_int64(to, from, low_byte_first);
+ }
};
#endif
class Field_float :public Field_real {
public:
- Field_float(char *ptr_arg, uint32 len_arg, uchar *null_ptr_arg,
+ Field_float(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg,
uchar null_bit_arg,
enum utype unireg_check_arg, const char *field_name_arg,
- struct st_table *table_arg,
uint8 dec_arg,bool zero_arg,bool unsigned_arg)
:Field_real(ptr_arg, len_arg, null_ptr_arg, null_bit_arg,
- unireg_check_arg, field_name_arg, table_arg,
+ unireg_check_arg, field_name_arg,
dec_arg, zero_arg, unsigned_arg)
{}
Field_float(uint32 len_arg, bool maybe_null_arg, const char *field_name_arg,
- struct st_table *table_arg, uint8 dec_arg)
- :Field_real((char*) 0, len_arg, maybe_null_arg ? (uchar*) "": 0, (uint) 0,
- NONE, field_name_arg, table_arg, dec_arg, 0, 0)
+ uint8 dec_arg)
+ :Field_real((uchar*) 0, len_arg, maybe_null_arg ? (uchar*) "": 0, (uint) 0,
+ NONE, field_name_arg, dec_arg, 0, 0)
{}
- enum_field_types type() const { return FIELD_TYPE_FLOAT;}
+ enum_field_types type() const { return MYSQL_TYPE_FLOAT;}
enum ha_base_keytype key_type() const { return HA_KEYTYPE_FLOAT; }
int store(const char *to,uint length,CHARSET_INFO *charset);
int store(double nr);
@@ -773,35 +1085,37 @@ public:
longlong val_int(void);
String *val_str(String*,String *);
bool send_binary(Protocol *protocol);
- int cmp(const char *,const char*);
- void sort_string(char *buff,uint length);
+ int cmp(const uchar *,const uchar *);
+ void sort_string(uchar *buff,uint length);
uint32 pack_length() const { return sizeof(float); }
+ uint row_pack_length() { return pack_length(); }
void sql_type(String &str) const;
+private:
+ int do_save_field_metadata(uchar *first_byte);
};
class Field_double :public Field_real {
public:
- Field_double(char *ptr_arg, uint32 len_arg, uchar *null_ptr_arg,
+ Field_double(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg,
uchar null_bit_arg,
enum utype unireg_check_arg, const char *field_name_arg,
- struct st_table *table_arg,
uint8 dec_arg,bool zero_arg,bool unsigned_arg)
:Field_real(ptr_arg, len_arg, null_ptr_arg, null_bit_arg,
- unireg_check_arg, field_name_arg, table_arg,
+ unireg_check_arg, field_name_arg,
dec_arg, zero_arg, unsigned_arg)
{}
Field_double(uint32 len_arg, bool maybe_null_arg, const char *field_name_arg,
- struct st_table *table_arg, uint8 dec_arg)
- :Field_real((char*) 0, len_arg, maybe_null_arg ? (uchar*) "" : 0, (uint) 0,
- NONE, field_name_arg, table_arg, dec_arg, 0, 0)
+ uint8 dec_arg)
+ :Field_real((uchar*) 0, len_arg, maybe_null_arg ? (uchar*) "" : 0, (uint) 0,
+ NONE, field_name_arg, dec_arg, 0, 0)
{}
Field_double(uint32 len_arg, bool maybe_null_arg, const char *field_name_arg,
- struct st_table *table_arg, uint8 dec_arg, my_bool not_fixed_arg)
- :Field_real((char*) 0, len_arg, maybe_null_arg ? (uchar*) "" : 0, (uint) 0,
- NONE, field_name_arg, table_arg, dec_arg, 0, 0)
+ uint8 dec_arg, my_bool not_fixed_arg)
+ :Field_real((uchar*) 0, len_arg, maybe_null_arg ? (uchar*) "" : 0, (uint) 0,
+ NONE, field_name_arg, dec_arg, 0, 0)
{not_fixed= not_fixed_arg; }
- enum_field_types type() const { return FIELD_TYPE_DOUBLE;}
+ enum_field_types type() const { return MYSQL_TYPE_DOUBLE;}
enum ha_base_keytype key_type() const { return HA_KEYTYPE_DOUBLE; }
int store(const char *to,uint length,CHARSET_INFO *charset);
int store(double nr);
@@ -811,10 +1125,13 @@ public:
longlong val_int(void);
String *val_str(String*,String *);
bool send_binary(Protocol *protocol);
- int cmp(const char *,const char*);
- void sort_string(char *buff,uint length);
+ int cmp(const uchar *,const uchar *);
+ void sort_string(uchar *buff,uint length);
uint32 pack_length() const { return sizeof(double); }
+ uint row_pack_length() { return pack_length(); }
void sql_type(String &str) const;
+private:
+ int do_save_field_metadata(uchar *first_byte);
};
@@ -823,13 +1140,13 @@ public:
class Field_null :public Field_str {
static uchar null[1];
public:
- Field_null(char *ptr_arg, uint32 len_arg,
+ Field_null(uchar *ptr_arg, uint32 len_arg,
enum utype unireg_check_arg, const char *field_name_arg,
- struct st_table *table_arg, CHARSET_INFO *cs)
+ CHARSET_INFO *cs)
:Field_str(ptr_arg, len_arg, null, 1,
- unireg_check_arg, field_name_arg, table_arg, cs)
+ unireg_check_arg, field_name_arg, cs)
{}
- enum_field_types type() const { return FIELD_TYPE_NULL;}
+ enum_field_types type() const { return MYSQL_TYPE_NULL;}
int store(const char *to, uint length, CHARSET_INFO *cs)
{ null[0]=1; return 0; }
int store(double nr) { null[0]=1; return 0; }
@@ -841,8 +1158,8 @@ public:
my_decimal *val_decimal(my_decimal *) { return 0; }
String *val_str(String *value,String *value2)
{ value2->length(0); return value2;}
- int cmp(const char *a, const char *b) { return 0;}
- void sort_string(char *buff, uint length) {}
+ int cmp(const uchar *a, const uchar *b) { return 0;}
+ void sort_string(uchar *buff, uint length) {}
uint32 pack_length() const { return 0; }
void sql_type(String &str) const;
uint size_of() const { return sizeof(*this); }
@@ -852,14 +1169,13 @@ public:
class Field_timestamp :public Field_str {
public:
- Field_timestamp(char *ptr_arg, uint32 len_arg,
+ Field_timestamp(uchar *ptr_arg, uint32 len_arg,
uchar *null_ptr_arg, uchar null_bit_arg,
enum utype unireg_check_arg, const char *field_name_arg,
- struct st_table *table_arg,
- CHARSET_INFO *cs);
+ TABLE_SHARE *share, CHARSET_INFO *cs);
Field_timestamp(bool maybe_null_arg, const char *field_name_arg,
- struct st_table *table_arg, CHARSET_INFO *cs);
- enum_field_types type() const { return FIELD_TYPE_TIMESTAMP;}
+ CHARSET_INFO *cs);
+ enum_field_types type() const { return MYSQL_TYPE_TIMESTAMP;}
enum ha_base_keytype key_type() const { return HA_KEYTYPE_ULONG_INT; }
enum Item_result cmp_type () const { return INT_RESULT; }
int store(const char *to,uint length,CHARSET_INFO *charset);
@@ -870,8 +1186,8 @@ public:
longlong val_int(void);
String *val_str(String*,String *);
bool send_binary(Protocol *protocol);
- int cmp(const char *,const char*);
- void sort_string(char *buff,uint length);
+ int cmp(const uchar *,const uchar *);
+ void sort_string(uchar *buff,uint length);
uint32 pack_length() const { return 4; }
void sql_type(String &str) const;
bool can_be_compared_as_longlong() const { return TRUE; }
@@ -898,22 +1214,43 @@ public:
longget(tmp,ptr);
return tmp;
}
+ inline void store_timestamp(my_time_t timestamp)
+ {
+#ifdef WORDS_BIGENDIAN
+ if (table && table->s->db_low_byte_first)
+ {
+ int4store(ptr,timestamp);
+ }
+ else
+#endif
+ longstore(ptr,(uint32) timestamp);
+ }
bool get_date(MYSQL_TIME *ltime,uint fuzzydate);
bool get_time(MYSQL_TIME *ltime);
timestamp_auto_set_type get_auto_set_type() const;
+ uchar *pack(uchar *to, const uchar *from,
+ uint max_length __attribute__((unused)), bool low_byte_first)
+ {
+ return pack_int32(to, from, low_byte_first);
+ }
+ const uchar *unpack(uchar* to, const uchar *from,
+ uint param_data __attribute__((unused)),
+ bool low_byte_first)
+ {
+ return unpack_int32(to, from, low_byte_first);
+ }
};
class Field_year :public Field_tiny {
public:
- Field_year(char *ptr_arg, uint32 len_arg, uchar *null_ptr_arg,
+ Field_year(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg,
uchar null_bit_arg,
- enum utype unireg_check_arg, const char *field_name_arg,
- struct st_table *table_arg)
+ enum utype unireg_check_arg, const char *field_name_arg)
:Field_tiny(ptr_arg, len_arg, null_ptr_arg, null_bit_arg,
- unireg_check_arg, field_name_arg, table_arg, 1, 1)
+ unireg_check_arg, field_name_arg, 1, 1)
{}
- enum_field_types type() const { return FIELD_TYPE_YEAR;}
+ enum_field_types type() const { return MYSQL_TYPE_YEAR;}
int store(const char *to,uint length,CHARSET_INFO *charset);
int store(double nr);
int store(longlong nr, bool unsigned_val);
@@ -928,17 +1265,17 @@ public:
class Field_date :public Field_str {
public:
- Field_date(char *ptr_arg, uchar *null_ptr_arg, uchar null_bit_arg,
+ Field_date(uchar *ptr_arg, uchar *null_ptr_arg, uchar null_bit_arg,
enum utype unireg_check_arg, const char *field_name_arg,
- struct st_table *table_arg, CHARSET_INFO *cs)
+ CHARSET_INFO *cs)
:Field_str(ptr_arg, 10, null_ptr_arg, null_bit_arg,
- unireg_check_arg, field_name_arg, table_arg, cs)
+ unireg_check_arg, field_name_arg, cs)
{}
Field_date(bool maybe_null_arg, const char *field_name_arg,
- struct st_table *table_arg, CHARSET_INFO *cs)
- :Field_str((char*) 0,10, maybe_null_arg ? (uchar*) "": 0,0,
- NONE, field_name_arg, table_arg, cs) {}
- enum_field_types type() const { return FIELD_TYPE_DATE;}
+ CHARSET_INFO *cs)
+ :Field_str((uchar*) 0,10, maybe_null_arg ? (uchar*) "": 0,0,
+ NONE, 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; }
enum Item_result cmp_type () const { return INT_RESULT; }
int store(const char *to,uint length,CHARSET_INFO *charset);
@@ -950,28 +1287,40 @@ public:
String *val_str(String*,String *);
bool get_time(MYSQL_TIME *ltime);
bool send_binary(Protocol *protocol);
- int cmp(const char *,const char*);
- void sort_string(char *buff,uint length);
+ int cmp(const uchar *,const uchar *);
+ void sort_string(uchar *buff,uint length);
uint32 pack_length() const { return 4; }
void sql_type(String &str) const;
bool can_be_compared_as_longlong() const { return TRUE; }
bool zero_pack() const { return 1; }
+ uchar *pack(uchar* to, const uchar *from,
+ uint max_length __attribute__((unused)), bool low_byte_first)
+ {
+ return pack_int32(to, from, low_byte_first);
+ }
+ const uchar *unpack(uchar* to, const uchar *from,
+ uint param_data __attribute__((unused)),
+ bool low_byte_first)
+ {
+ return unpack_int32(to, from, low_byte_first);
+ }
};
+
class Field_newdate :public Field_str {
public:
- Field_newdate(char *ptr_arg, uchar *null_ptr_arg, uchar null_bit_arg,
+ Field_newdate(uchar *ptr_arg, uchar *null_ptr_arg, uchar null_bit_arg,
enum utype unireg_check_arg, const char *field_name_arg,
- struct st_table *table_arg, CHARSET_INFO *cs)
+ CHARSET_INFO *cs)
:Field_str(ptr_arg, 10, null_ptr_arg, null_bit_arg,
- unireg_check_arg, field_name_arg, table_arg, cs)
+ unireg_check_arg, field_name_arg, cs)
{}
Field_newdate(bool maybe_null_arg, const char *field_name_arg,
- struct st_table *table_arg, CHARSET_INFO *cs)
- :Field_str((char*) 0,10, maybe_null_arg ? (uchar*) "": 0,0,
- NONE, field_name_arg, table_arg, cs) {}
- enum_field_types type() const { return FIELD_TYPE_DATE;}
- enum_field_types real_type() const { return FIELD_TYPE_NEWDATE; }
+ CHARSET_INFO *cs)
+ :Field_str((uchar*) 0,10, maybe_null_arg ? (uchar*) "": 0,0,
+ NONE, field_name_arg, cs) {}
+ enum_field_types type() const { return MYSQL_TYPE_DATE;}
+ enum_field_types real_type() const { return MYSQL_TYPE_NEWDATE; }
enum ha_base_keytype key_type() const { return HA_KEYTYPE_UINT24; }
enum Item_result cmp_type () const { return INT_RESULT; }
int store(const char *to,uint length,CHARSET_INFO *charset);
@@ -983,8 +1332,8 @@ public:
longlong val_int(void);
String *val_str(String*,String *);
bool send_binary(Protocol *protocol);
- int cmp(const char *,const char*);
- void sort_string(char *buff,uint length);
+ int cmp(const uchar *,const uchar *);
+ void sort_string(uchar *buff,uint length);
uint32 pack_length() const { return 3; }
void sql_type(String &str) const;
bool can_be_compared_as_longlong() const { return TRUE; }
@@ -996,17 +1345,17 @@ public:
class Field_time :public Field_str {
public:
- Field_time(char *ptr_arg, uchar *null_ptr_arg, uchar null_bit_arg,
+ Field_time(uchar *ptr_arg, uchar *null_ptr_arg, uchar null_bit_arg,
enum utype unireg_check_arg, const char *field_name_arg,
- struct st_table *table_arg, CHARSET_INFO *cs)
+ CHARSET_INFO *cs)
:Field_str(ptr_arg, 8, null_ptr_arg, null_bit_arg,
- unireg_check_arg, field_name_arg, table_arg, cs)
+ unireg_check_arg, field_name_arg, cs)
{}
Field_time(bool maybe_null_arg, const char *field_name_arg,
- struct st_table *table_arg, CHARSET_INFO *cs)
- :Field_str((char*) 0,8, maybe_null_arg ? (uchar*) "": 0,0,
- NONE, field_name_arg, table_arg, cs) {}
- enum_field_types type() const { return FIELD_TYPE_TIME;}
+ CHARSET_INFO *cs)
+ :Field_str((uchar*) 0,8, maybe_null_arg ? (uchar*) "": 0,0,
+ NONE, field_name_arg, cs) {}
+ enum_field_types type() const { return MYSQL_TYPE_TIME;}
enum ha_base_keytype key_type() const { return HA_KEYTYPE_INT24; }
enum Item_result cmp_type () const { return INT_RESULT; }
int store_time(MYSQL_TIME *ltime, timestamp_type type);
@@ -1020,8 +1369,8 @@ public:
bool get_date(MYSQL_TIME *ltime, uint fuzzydate);
bool send_binary(Protocol *protocol);
bool get_time(MYSQL_TIME *ltime);
- int cmp(const char *,const char*);
- void sort_string(char *buff,uint length);
+ int cmp(const uchar *,const uchar *);
+ void sort_string(uchar *buff,uint length);
uint32 pack_length() const { return 3; }
void sql_type(String &str) const;
bool can_be_compared_as_longlong() const { return TRUE; }
@@ -1031,17 +1380,17 @@ public:
class Field_datetime :public Field_str {
public:
- Field_datetime(char *ptr_arg, uchar *null_ptr_arg, uchar null_bit_arg,
+ Field_datetime(uchar *ptr_arg, uchar *null_ptr_arg, uchar null_bit_arg,
enum utype unireg_check_arg, const char *field_name_arg,
- struct st_table *table_arg, CHARSET_INFO *cs)
+ CHARSET_INFO *cs)
:Field_str(ptr_arg, 19, null_ptr_arg, null_bit_arg,
- unireg_check_arg, field_name_arg, table_arg, cs)
+ unireg_check_arg, field_name_arg, cs)
{}
Field_datetime(bool maybe_null_arg, const char *field_name_arg,
- struct st_table *table_arg, CHARSET_INFO *cs)
- :Field_str((char*) 0,19, maybe_null_arg ? (uchar*) "": 0,0,
- NONE, field_name_arg, table_arg, cs) {}
- enum_field_types type() const { return FIELD_TYPE_DATETIME;}
+ CHARSET_INFO *cs)
+ :Field_str((uchar*) 0,19, maybe_null_arg ? (uchar*) "": 0,0,
+ NONE, field_name_arg, cs) {}
+ enum_field_types type() const { return MYSQL_TYPE_DATETIME;}
#ifdef HAVE_LONG_LONG
enum ha_base_keytype key_type() const { return HA_KEYTYPE_ULONGLONG; }
#endif
@@ -1060,31 +1409,42 @@ public:
longlong val_int(void);
String *val_str(String*,String *);
bool send_binary(Protocol *protocol);
- int cmp(const char *,const char*);
- void sort_string(char *buff,uint length);
+ int cmp(const uchar *,const uchar *);
+ void sort_string(uchar *buff,uint length);
uint32 pack_length() const { return 8; }
void sql_type(String &str) const;
bool can_be_compared_as_longlong() const { return TRUE; }
bool zero_pack() const { return 1; }
bool get_date(MYSQL_TIME *ltime,uint fuzzydate);
bool get_time(MYSQL_TIME *ltime);
+ uchar *pack(uchar* to, const uchar *from,
+ uint max_length __attribute__((unused)), bool low_byte_first)
+ {
+ return pack_int64(to, from, low_byte_first);
+ }
+ const uchar *unpack(uchar* to, const uchar *from,
+ uint param_data __attribute__((unused)),
+ bool low_byte_first)
+ {
+ return unpack_int64(to, from, low_byte_first);
+ }
};
class Field_string :public Field_longstr {
public:
bool can_alter_field_type;
- Field_string(char *ptr_arg, uint32 len_arg,uchar *null_ptr_arg,
+ Field_string(uchar *ptr_arg, uint32 len_arg,uchar *null_ptr_arg,
uchar null_bit_arg,
enum utype unireg_check_arg, const char *field_name_arg,
- struct st_table *table_arg, CHARSET_INFO *cs)
+ CHARSET_INFO *cs)
:Field_longstr(ptr_arg, len_arg, null_ptr_arg, null_bit_arg,
- unireg_check_arg, field_name_arg, table_arg, cs),
+ unireg_check_arg, field_name_arg, cs),
can_alter_field_type(1) {};
Field_string(uint32 len_arg,bool maybe_null_arg, const char *field_name_arg,
- struct st_table *table_arg, CHARSET_INFO *cs)
- :Field_longstr((char*) 0, len_arg, maybe_null_arg ? (uchar*) "": 0, 0,
- NONE, field_name_arg, table_arg, cs),
+ CHARSET_INFO *cs)
+ :Field_longstr((uchar*) 0, len_arg, maybe_null_arg ? (uchar*) "": 0, 0,
+ NONE, field_name_arg, cs),
can_alter_field_type(1) {};
enum_field_types type() const
@@ -1100,7 +1460,7 @@ public:
bool zero_pack() const { return 0; }
int reset(void)
{
- charset()->cset->fill(charset(),ptr,field_length,
+ charset()->cset->fill(charset(),(char*) ptr, field_length,
(has_charset() ? ' ' : 0));
return 0;
}
@@ -1111,22 +1471,36 @@ public:
longlong val_int(void);
String *val_str(String*,String *);
my_decimal *val_decimal(my_decimal *);
- int cmp(const char *,const char*);
- void sort_string(char *buff,uint length);
+ int cmp(const uchar *,const uchar *);
+ void sort_string(uchar *buff,uint length);
void sql_type(String &str) const;
- char *pack(char *to, const char *from, uint max_length=~(uint) 0);
- const char *unpack(char* to, const char *from);
- int pack_cmp(const char *a,const char *b,uint key_length,
+ virtual uchar *pack(uchar *to, const uchar *from,
+ uint max_length, bool low_byte_first);
+ virtual const uchar *unpack(uchar* to, const uchar *from,
+ uint param_data, bool low_byte_first);
+ uint pack_length_from_metadata(uint field_metadata)
+ {
+ DBUG_PRINT("debug", ("field_metadata: 0x%04x", field_metadata));
+ if (field_metadata == 0)
+ return row_pack_length();
+ return (((field_metadata >> 4) & 0x300) ^ 0x300) + (field_metadata & 0x00ff);
+ }
+ int compatible_field_size(uint field_metadata,
+ const Relay_log_info *rli);
+ uint row_pack_length() { return (field_length + 1); }
+ int pack_cmp(const uchar *a,const uchar *b,uint key_length,
my_bool insert_or_update);
- int pack_cmp(const char *b,uint key_length,my_bool insert_or_update);
- uint packed_col_length(const char *to, uint length);
+ int pack_cmp(const uchar *b,uint key_length,my_bool insert_or_update);
+ uint packed_col_length(const uchar *to, uint length);
uint max_packed_col_length(uint max_length);
uint size_of() const { return sizeof(*this); }
- enum_field_types real_type() const { return FIELD_TYPE_STRING; }
+ 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);
- virtual uint get_key_image(char *buff,uint length, imagetype type);
+ virtual uint get_key_image(uchar *buff,uint length, imagetype type);
+private:
+ int do_save_field_metadata(uchar *first_byte);
};
@@ -1139,32 +1513,30 @@ public:
static const uint MAX_SIZE;
/* Store number of bytes used to store length (1 or 2) */
uint32 length_bytes;
- Field_varstring(char *ptr_arg,
+ Field_varstring(uchar *ptr_arg,
uint32 len_arg, uint length_bytes_arg,
- uchar *null_ptr_arg,
- uchar null_bit_arg,
+ uchar *null_ptr_arg, uchar null_bit_arg,
enum utype unireg_check_arg, const char *field_name_arg,
- struct st_table *table_arg, CHARSET_INFO *cs)
+ TABLE_SHARE *share, CHARSET_INFO *cs)
:Field_longstr(ptr_arg, len_arg, null_ptr_arg, null_bit_arg,
- unireg_check_arg, field_name_arg, table_arg, cs),
+ unireg_check_arg, field_name_arg, cs),
length_bytes(length_bytes_arg)
{
- if (table)
- table->s->varchar_fields++;
+ share->varchar_fields++;
}
Field_varstring(uint32 len_arg,bool maybe_null_arg,
const char *field_name_arg,
- struct st_table *table_arg, CHARSET_INFO *cs)
- :Field_longstr((char*) 0,len_arg, maybe_null_arg ? (uchar*) "": 0, 0,
- NONE, field_name_arg, table_arg, cs),
+ TABLE_SHARE *share, CHARSET_INFO *cs)
+ :Field_longstr((uchar*) 0,len_arg, maybe_null_arg ? (uchar*) "": 0, 0,
+ NONE, field_name_arg, cs),
length_bytes(len_arg < 256 ? 1 :2)
{
- if (table)
- table->s->varchar_fields++;
+ share->varchar_fields++;
}
enum_field_types type() const { return MYSQL_TYPE_VARCHAR; }
enum ha_base_keytype key_type() const;
+ uint row_pack_length() { return field_length; }
bool zero_pack() const { return 0; }
int reset(void) { bzero(ptr,field_length+length_bytes); return 0; }
uint32 pack_length() const { return (uint32) field_length+length_bytes; }
@@ -1181,33 +1553,45 @@ public:
longlong val_int(void);
String *val_str(String*,String *);
my_decimal *val_decimal(my_decimal *);
- int cmp(const char *,const char*);
- void sort_string(char *buff,uint length);
- uint get_key_image(char *buff,uint length, imagetype type);
- void set_key_image(char *buff,uint length);
+ int cmp_max(const uchar *, const uchar *, uint max_length);
+ int cmp(const uchar *a,const uchar *b)
+ {
+ return cmp_max(a, b, ~0L);
+ }
+ void sort_string(uchar *buff,uint length);
+ uint get_key_image(uchar *buff,uint length, imagetype type);
+ void set_key_image(const uchar *buff,uint length);
void sql_type(String &str) const;
- char *pack(char *to, const char *from, uint max_length=~(uint) 0);
- char *pack_key(char *to, const char *from, uint max_length);
- char *pack_key_from_key_image(char* to, const char *from, uint max_length);
- const char *unpack(char* to, const char *from);
- const char *unpack_key(char* to, const char *from, uint max_length);
- int pack_cmp(const char *a, const char *b, uint key_length,
+ virtual uchar *pack(uchar *to, const uchar *from,
+ uint max_length, bool low_byte_first);
+ uchar *pack_key(uchar *to, const uchar *from, uint max_length, bool low_byte_first);
+ uchar *pack_key_from_key_image(uchar* to, const uchar *from,
+ uint max_length, bool low_byte_first);
+ virtual const uchar *unpack(uchar* to, const uchar *from,
+ uint param_data, bool low_byte_first);
+ const uchar *unpack_key(uchar* to, const uchar *from,
+ uint max_length, bool low_byte_first);
+ int pack_cmp(const uchar *a, const uchar *b, uint key_length,
my_bool insert_or_update);
- int pack_cmp(const char *b, uint key_length,my_bool insert_or_update);
- int cmp_binary(const char *a,const char *b, uint32 max_length=~0L);
- int key_cmp(const byte *,const byte*);
- int key_cmp(const byte *str, uint length);
- uint packed_col_length(const char *to, uint length);
+ int pack_cmp(const uchar *b, uint key_length,my_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);
+ uint packed_col_length(const uchar *to, uint length);
uint max_packed_col_length(uint max_length);
- uint32 data_length(const char *from);
+ uint32 data_length();
uint size_of() const { return sizeof(*this); }
enum_field_types real_type() const { return MYSQL_TYPE_VARCHAR; }
bool has_charset(void) const
{ return charset() == &my_charset_bin ? FALSE : TRUE; }
Field *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,
- char *new_ptr, uchar *new_null_ptr,
+ uchar *new_ptr, uchar *new_null_ptr,
uint new_null_bit);
+ uint is_equal(Create_field *new_field);
+ void hash(ulong *nr, ulong *nr2);
+private:
+ int do_save_field_metadata(uchar *first_byte);
};
@@ -1224,22 +1608,21 @@ protected:
String value;
public:
- Field_blob(char *ptr_arg, uchar *null_ptr_arg, uchar null_bit_arg,
+ Field_blob(uchar *ptr_arg, uchar *null_ptr_arg, uchar null_bit_arg,
enum utype unireg_check_arg, const char *field_name_arg,
- struct st_table *table_arg,uint blob_pack_length,
- CHARSET_INFO *cs);
+ TABLE_SHARE *share, uint blob_pack_length, CHARSET_INFO *cs);
Field_blob(uint32 len_arg,bool maybe_null_arg, const char *field_name_arg,
- struct st_table *table_arg, CHARSET_INFO *cs)
- :Field_longstr((char*) 0,len_arg, maybe_null_arg ? (uchar*) "": 0, 0,
- NONE, field_name_arg, table_arg, cs),
+ CHARSET_INFO *cs)
+ :Field_longstr((uchar*) 0, len_arg, maybe_null_arg ? (uchar*) "": 0, 0,
+ NONE, field_name_arg, cs),
packlength(4)
{
flags|= BLOB_FLAG;
}
Field_blob(uint32 len_arg,bool maybe_null_arg, const char *field_name_arg,
- struct st_table *table_arg, CHARSET_INFO *cs, bool set_packlength)
- :Field_longstr((char*) 0,len_arg, maybe_null_arg ? (uchar*) "": 0, 0,
- NONE, field_name_arg, table_arg, cs)
+ CHARSET_INFO *cs, bool set_packlength)
+ :Field_longstr((uchar*) 0,len_arg, maybe_null_arg ? (uchar*) "": 0, 0,
+ NONE, field_name_arg, cs)
{
flags|= BLOB_FLAG;
packlength= 4;
@@ -1251,7 +1634,10 @@ public:
l_char_length <= 16777215 ? 3 : 4;
}
}
- enum_field_types type() const { return FIELD_TYPE_BLOB;}
+ Field_blob(uint32 packlength_arg)
+ :Field_longstr((uchar*) 0, 0, (uchar*) "", 0, NONE, "temp", system_charset_info),
+ packlength(packlength_arg) {}
+ enum_field_types type() const { return MYSQL_TYPE_BLOB;}
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);
@@ -1261,72 +1647,133 @@ public:
longlong val_int(void);
String *val_str(String*,String *);
my_decimal *val_decimal(my_decimal *);
- int cmp(const char *,const char*);
- int cmp(const char *a, uint32 a_length, const char *b, uint32 b_length);
- int cmp_binary(const char *a,const char *b, uint32 max_length=~0L);
- int key_cmp(const byte *,const byte*);
- int key_cmp(const byte *str, uint length);
+ int cmp_max(const uchar *, const uchar *, uint max_length);
+ int cmp(const uchar *a,const uchar *b)
+ { return cmp_max(a, b, ~0L); }
+ int cmp(const uchar *a, uint32 a_length, const uchar *b, uint32 b_length);
+ int cmp_binary(const uchar *a,const uchar *b, uint32 max_length=~0L);
+ int key_cmp(const uchar *,const uchar*);
+ int key_cmp(const uchar *str, uint length);
uint32 key_length() const { return 0; }
- void sort_string(char *buff,uint length);
+ void sort_string(uchar *buff,uint length);
uint32 pack_length() const
{ return (uint32) (packlength+table->s->blob_ptr_size); }
+
+ /**
+ Return the packed length without the pointer size added.
+
+ This is used to determine the size of the actual data in the row
+ buffer.
+
+ @returns The length of the raw data itself without the pointer.
+ */
+ uint32 pack_length_no_ptr() const
+ { return (uint32) (packlength); }
+ uint row_pack_length() { return pack_length_no_ptr(); }
uint32 sort_length() const;
- inline uint32 max_data_length() const
+ virtual uint32 max_data_length() const
{
return (uint32) (((ulonglong) 1 << (packlength*8)) -1);
}
- int reset(void) { bzero(ptr, packlength+sizeof(char*)); return 0; }
- void reset_fields() { bzero((char*) &value,sizeof(value)); }
- void store_length(uint32 number);
- inline uint32 get_length(uint row_offset=0)
- { return get_length(ptr+row_offset); }
- uint32 get_length(const char *ptr);
- void put_length(char *pos, uint32 length);
- inline void get_ptr(char **str)
+ int reset(void) { bzero(ptr, packlength+sizeof(uchar*)); return 0; }
+ void reset_fields() { bzero((uchar*) &value,sizeof(value)); }
+ uint32 get_field_buffer_size(void) { return value.alloced_length(); }
+#ifndef WORDS_BIGENDIAN
+ static
+#endif
+ void store_length(uchar *i_ptr, uint i_packlength, uint32 i_number, bool low_byte_first);
+ void store_length(uchar *i_ptr, uint i_packlength, uint32 i_number)
+ {
+ store_length(i_ptr, i_packlength, i_number, table->s->db_low_byte_first);
+ }
+ inline void store_length(uint32 number)
+ {
+ 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, bool low_byte_first)
+ {return packlength + get_length(ptr_arg, packlength, low_byte_first);}
+
+ inline uint32 get_length(uint row_offset= 0)
+ { return get_length(ptr+row_offset, this->packlength, table->s->db_low_byte_first); }
+ uint32 get_length(const uchar *ptr, uint packlength, bool low_byte_first);
+ uint32 get_length(const uchar *ptr_arg)
+ { return get_length(ptr_arg, this->packlength, table->s->db_low_byte_first); }
+ void put_length(uchar *pos, uint32 length);
+ inline void get_ptr(uchar **str)
+ {
+ memcpy_fixed((uchar*) str,ptr+packlength,sizeof(uchar*));
+ }
+ inline void get_ptr(uchar **str, uint row_offset)
{
- memcpy_fixed(str,ptr+packlength,sizeof(char*));
+ memcpy_fixed((uchar*) str,ptr+packlength+row_offset,sizeof(char*));
}
- inline void set_ptr(char *length,char *data)
+ inline void set_ptr(uchar *length, uchar *data)
{
memcpy(ptr,length,packlength);
memcpy_fixed(ptr+packlength,&data,sizeof(char*));
}
- inline void set_ptr(uint32 length,char *data)
+ void set_ptr_offset(my_ptrdiff_t ptr_diff, uint32 length, uchar *data)
{
- store_length(length);
- memcpy_fixed(ptr+packlength,&data,sizeof(char*));
+ uchar *ptr_ofs= ADD_TO_PTR(ptr,ptr_diff,uchar*);
+ store_length(ptr_ofs, packlength, length);
+ memcpy_fixed(ptr_ofs+packlength,&data,sizeof(char*));
+ }
+ inline void set_ptr(uint32 length, uchar *data)
+ {
+ set_ptr_offset(0, length, data);
}
- uint get_key_image(char *buff,uint length, imagetype type);
- void set_key_image(char *buff,uint length);
+ uint get_key_image(uchar *buff,uint length, imagetype type);
+ void set_key_image(const uchar *buff,uint length);
void sql_type(String &str) const;
inline bool copy()
- { char *tmp;
+ {
+ uchar *tmp;
get_ptr(&tmp);
- if (value.copy(tmp,get_length(),charset()))
+ if (value.copy((char*) tmp, get_length(), charset()))
{
Field_blob::reset();
return 1;
}
- tmp=(char*) value.ptr(); memcpy_fixed(ptr+packlength,&tmp,sizeof(char*));
+ tmp=(uchar*) value.ptr();
+ memcpy_fixed(ptr+packlength,&tmp,sizeof(char*));
return 0;
}
- char *pack(char *to, const char *from, uint max_length= ~(uint) 0);
- char *pack_key(char *to, const char *from, uint max_length);
- char *pack_key_from_key_image(char* to, const char *from, uint max_length);
- const char *unpack(char *to, const char *from);
- const char *unpack_key(char* to, const char *from, uint max_length);
- int pack_cmp(const char *a, const char *b, uint key_length,
+ virtual uchar *pack(uchar *to, const uchar *from,
+ uint max_length, bool low_byte_first);
+ uchar *pack_key(uchar *to, const uchar *from,
+ uint max_length, bool low_byte_first);
+ uchar *pack_key_from_key_image(uchar* to, const uchar *from,
+ uint max_length, bool low_byte_first);
+ virtual const uchar *unpack(uchar *to, const uchar *from,
+ uint param_data, bool low_byte_first);
+ const uchar *unpack_key(uchar* to, const uchar *from,
+ uint max_length, bool low_byte_first);
+ int pack_cmp(const uchar *a, const uchar *b, uint key_length,
my_bool insert_or_update);
- int pack_cmp(const char *b, uint key_length,my_bool insert_or_update);
- uint packed_col_length(const char *col_ptr, uint length);
+ int pack_cmp(const uchar *b, uint key_length,my_bool insert_or_update);
+ uint packed_col_length(const uchar *col_ptr, uint length);
uint max_packed_col_length(uint max_length);
void free() { value.free(); }
- inline void clear_temporary() { bzero((char*) &value,sizeof(value)); }
+ inline void clear_temporary() { bzero((uchar*) &value,sizeof(value)); }
friend int field_conv(Field *to,Field *from);
uint size_of() const { return sizeof(*this); }
bool has_charset(void) const
{ return charset() == &my_charset_bin ? FALSE : TRUE; }
uint32 max_display_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); }
+private:
+ int do_save_field_metadata(uchar *first_byte);
};
@@ -1335,20 +1782,19 @@ class Field_geom :public Field_blob {
public:
enum geometry_type geom_type;
- Field_geom(char *ptr_arg, uchar *null_ptr_arg, uint null_bit_arg,
+ Field_geom(uchar *ptr_arg, uchar *null_ptr_arg, uint null_bit_arg,
enum utype unireg_check_arg, const char *field_name_arg,
- struct st_table *table_arg,uint blob_pack_length,
+ TABLE_SHARE *share, uint blob_pack_length,
enum geometry_type geom_type_arg)
:Field_blob(ptr_arg, null_ptr_arg, null_bit_arg, unireg_check_arg,
- field_name_arg, table_arg, blob_pack_length,&my_charset_bin)
+ field_name_arg, share, blob_pack_length, &my_charset_bin)
{ geom_type= geom_type_arg; }
Field_geom(uint32 len_arg,bool maybe_null_arg, const char *field_name_arg,
- struct st_table *table_arg, enum geometry_type geom_type_arg)
- :Field_blob(len_arg, maybe_null_arg, field_name_arg,
- table_arg, &my_charset_bin)
+ TABLE_SHARE *share, enum geometry_type geom_type_arg)
+ :Field_blob(len_arg, maybe_null_arg, field_name_arg, &my_charset_bin)
{ geom_type= geom_type_arg; }
enum ha_base_keytype key_type() const { return HA_KEYTYPE_VARBINARY2; }
- enum_field_types type() const { return FIELD_TYPE_GEOMETRY; }
+ enum_field_types type() const { return MYSQL_TYPE_GEOMETRY; }
void sql_type(String &str) const;
int store(const char *to, uint length, CHARSET_INFO *charset);
int store(double nr);
@@ -1366,84 +1812,105 @@ protected:
uint packlength;
public:
TYPELIB *typelib;
- Field_enum(char *ptr_arg, uint32 len_arg, uchar *null_ptr_arg,
- uchar null_bit_arg,
- enum utype unireg_check_arg, const char *field_name_arg,
- struct st_table *table_arg,uint packlength_arg,
- TYPELIB *typelib_arg,
- CHARSET_INFO *charset_arg)
+ Field_enum(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg,
+ uchar null_bit_arg,
+ enum utype unireg_check_arg, const char *field_name_arg,
+ uint packlength_arg,
+ TYPELIB *typelib_arg,
+ CHARSET_INFO *charset_arg)
:Field_str(ptr_arg, len_arg, null_ptr_arg, null_bit_arg,
- unireg_check_arg, field_name_arg, table_arg, charset_arg),
+ unireg_check_arg, field_name_arg, charset_arg),
packlength(packlength_arg),typelib(typelib_arg)
{
flags|=ENUM_FLAG;
}
Field *new_field(MEM_ROOT *root, struct st_table *new_table, bool keep_type);
- enum_field_types type() const { return FIELD_TYPE_STRING; }
+ enum_field_types type() const { return MYSQL_TYPE_STRING; }
enum Item_result cmp_type () const { return INT_RESULT; }
enum Item_result cast_to_int_type () const { return INT_RESULT; }
enum ha_base_keytype key_type() const;
int store(const char *to,uint length,CHARSET_INFO *charset);
int store(double nr);
int store(longlong nr, bool unsigned_val);
- int reset() { bzero(ptr,packlength); return 0; }
double val_real(void);
longlong val_int(void);
String *val_str(String*,String *);
- int cmp(const char *,const char*);
- void sort_string(char *buff,uint length);
+ int cmp(const uchar *,const uchar *);
+ void sort_string(uchar *buff,uint length);
uint32 pack_length() const { return (uint32) packlength; }
void store_type(ulonglong value);
void sql_type(String &str) const;
uint size_of() const { return sizeof(*this); }
- enum_field_types real_type() const { return FIELD_TYPE_ENUM; }
+ enum_field_types real_type() const { return MYSQL_TYPE_ENUM; }
+ uint pack_length_from_metadata(uint field_metadata)
+ { return (field_metadata & 0x00ff); }
+ uint row_pack_length() { return pack_length(); }
virtual bool zero_pack() const { return 0; }
bool optimize_range(uint idx, uint part) { return 0; }
bool eq_def(Field *field);
bool has_charset(void) const { return TRUE; }
/* enum and set are sorted as integers */
CHARSET_INFO *sort_charset(void) const { return &my_charset_bin; }
+private:
+ int do_save_field_metadata(uchar *first_byte);
+ bool compare_enum_values(TYPELIB *values);
+ uint is_equal(Create_field *new_field);
};
class Field_set :public Field_enum {
public:
- Field_set(char *ptr_arg, uint32 len_arg, uchar *null_ptr_arg,
+ Field_set(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg,
uchar null_bit_arg,
enum utype unireg_check_arg, const char *field_name_arg,
- struct st_table *table_arg,uint32 packlength_arg,
+ uint32 packlength_arg,
TYPELIB *typelib_arg, CHARSET_INFO *charset_arg)
:Field_enum(ptr_arg, len_arg, null_ptr_arg, null_bit_arg,
unireg_check_arg, field_name_arg,
- table_arg, packlength_arg,
- typelib_arg,charset_arg)
+ packlength_arg,
+ typelib_arg,charset_arg)
{
flags=(flags & ~ENUM_FLAG) | SET_FLAG;
}
int store(const char *to,uint length,CHARSET_INFO *charset);
int store(double nr) { return Field_set::store((longlong) nr, FALSE); }
int store(longlong nr, bool unsigned_val);
+
virtual bool zero_pack() const { return 1; }
String *val_str(String*,String *);
void sql_type(String &str) const;
- enum_field_types real_type() const { return FIELD_TYPE_SET; }
+ enum_field_types real_type() const { return MYSQL_TYPE_SET; }
bool has_charset(void) const { return TRUE; }
};
+/*
+ Note:
+ To use Field_bit::cmp_binary() you need to copy the bits stored in
+ the beginning of the record (the NULL bytes) to each memory you
+ want to compare (where the arguments point).
+
+ This is the reason:
+ - Field_bit::cmp_binary() is only implemented in the base class
+ (Field::cmp_binary()).
+ - Field::cmp_binary() currenly use pack_length() to calculate how
+ long the data is.
+ - pack_length() includes size of the bits stored in the NULL bytes
+ of the record.
+*/
class Field_bit :public Field {
public:
uchar *bit_ptr; // position in record where 'uneven' bits store
uchar bit_ofs; // offset to 'uneven' high bits
uint bit_len; // number of 'uneven' high bits
uint bytes_in_rec;
- Field_bit(char *ptr_arg, uint32 len_arg, uchar *null_ptr_arg,
+ Field_bit(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg,
uchar null_bit_arg, uchar *bit_ptr_arg, uchar bit_ofs_arg,
- enum utype unireg_check_arg, const char *field_name_arg,
- struct st_table *table_arg);
- enum_field_types type() const { return FIELD_TYPE_BIT; }
+ enum utype unireg_check_arg, const char *field_name_arg);
+ enum_field_types type() const { return MYSQL_TYPE_BIT; }
enum ha_base_keytype key_type() const { return HA_KEYTYPE_BIT; }
uint32 key_length() const { return (uint32) (field_length + 7) / 8; }
+ uint32 max_data_length() const { return (field_length + 7) / 8; }
uint32 max_display_length() { return field_length; }
uint size_of() const { return sizeof(*this); }
Item_result result_type () const { return INT_RESULT; }
@@ -1455,40 +1922,46 @@ public:
double val_real(void);
longlong val_int(void);
String *val_str(String*, String *);
+ virtual bool str_needs_quotes() { return TRUE; }
my_decimal *val_decimal(my_decimal *);
- int cmp(const char *a, const char *b)
+ int cmp(const uchar *a, const uchar *b)
{
DBUG_ASSERT(ptr == a);
- return Field_bit::key_cmp((const byte *) b, bytes_in_rec+test(bit_len));
+ return Field_bit::key_cmp(b, bytes_in_rec+test(bit_len));
}
- int key_cmp(const byte *a, const byte *b)
- { return cmp_binary((char *) a, (char *) b); }
- int key_cmp(const byte *str, uint length);
- int cmp_offset(uint row_offset);
int cmp_binary_offset(uint row_offset)
{ return cmp_offset(row_offset); }
- void get_image(char *buff, uint length, CHARSET_INFO *cs)
+ int cmp_max(const uchar *a, const uchar *b, uint max_length);
+ int key_cmp(const uchar *a, const uchar *b)
+ { return cmp_binary((uchar *) a, (uchar *) b); }
+ int key_cmp(const uchar *str, uint length);
+ int cmp_offset(uint row_offset);
+ void get_image(uchar *buff, uint length, CHARSET_INFO *cs)
{ get_key_image(buff, length, itRAW); }
- void set_image(char *buff,uint length, CHARSET_INFO *cs)
- { Field_bit::store(buff, length, cs); }
- uint get_key_image(char *buff, uint length, imagetype type);
- void set_key_image(char *buff, uint length)
- { Field_bit::store(buff, length, &my_charset_bin); }
- void sort_string(char *buff, uint length)
+ void set_image(const uchar *buff,uint length, CHARSET_INFO *cs)
+ { Field_bit::store((char *) buff, length, cs); }
+ uint get_key_image(uchar *buff, uint length, imagetype type);
+ void set_key_image(const uchar *buff, uint length)
+ { Field_bit::store((char*) buff, length, &my_charset_bin); }
+ void sort_string(uchar *buff, uint length)
{ get_key_image(buff, length, itRAW); }
uint32 pack_length() const { return (uint32) (field_length + 7) / 8; }
uint32 pack_length_in_rec() const { return bytes_in_rec; }
+ uint pack_length_from_metadata(uint field_metadata);
+ uint row_pack_length()
+ { return (bytes_in_rec + ((bit_len > 0) ? 1 : 0)); }
+ int compatible_field_size(uint field_metadata,
+ const Relay_log_info *rli);
void sql_type(String &str) const;
- char *pack(char *to, const char *from, uint max_length=~(uint) 0);
- const char *unpack(char* to, const char *from);
+ virtual uchar *pack(uchar *to, const uchar *from,
+ uint max_length, bool low_byte_first);
+ virtual const uchar *unpack(uchar *to, const uchar *from,
+ uint param_data, bool low_byte_first);
+ virtual void set_default();
+
Field *new_key_field(MEM_ROOT *root, struct st_table *new_table,
- char *new_ptr, uchar *new_null_ptr,
+ uchar *new_ptr, uchar *new_null_ptr,
uint new_null_bit);
- inline void move_field(my_ptrdiff_t ptr_diff)
- {
- Field::move_field(ptr_diff);
- bit_ptr= ADD_TO_PTR(bit_ptr, ptr_diff, uchar*);
- }
void set_bit_ptr(uchar *bit_ptr_arg, uchar bit_ofs_arg)
{
bit_ptr= bit_ptr_arg;
@@ -1500,15 +1973,32 @@ public:
bit_ptr == ((Field_bit *)field)->bit_ptr &&
bit_ofs == ((Field_bit *)field)->bit_ofs);
}
+ uint is_equal(Create_field *new_field);
+ void move_field_offset(my_ptrdiff_t ptr_diff)
+ {
+ Field::move_field_offset(ptr_diff);
+ bit_ptr= ADD_TO_PTR(bit_ptr, ptr_diff, uchar*);
+ }
+ void hash(ulong *nr, ulong *nr2);
+
+private:
+ virtual size_t do_last_null_byte() const;
+ int do_save_field_metadata(uchar *first_byte);
};
+/**
+ BIT field represented as chars for non-MyISAM tables.
+
+ @todo The inheritance relationship is backwards since Field_bit is
+ an extended version of Field_bit_as_char and not the other way
+ around. Hence, we should refactor it to fix the hierarchy order.
+ */
class Field_bit_as_char: public Field_bit {
public:
- Field_bit_as_char(char *ptr_arg, uint32 len_arg, uchar *null_ptr_arg,
+ Field_bit_as_char(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg,
uchar null_bit_arg,
- enum utype unireg_check_arg, const char *field_name_arg,
- struct st_table *table_arg);
+ enum utype unireg_check_arg, const char *field_name_arg);
enum ha_base_keytype key_type() const { return HA_KEYTYPE_BINARY; }
uint size_of() const { return sizeof(*this); }
int store(const char *to, uint length, CHARSET_INFO *charset);
@@ -1523,7 +2013,7 @@ public:
Create field class for CREATE TABLE
*/
-class create_field :public Sql_alloc
+class Create_field :public Sql_alloc
{
public:
const char *field_name;
@@ -1554,8 +2044,11 @@ public:
uint8 row,col,sc_length,interval_id; // For rea_create_table
uint offset,pack_flag;
- create_field() :after(0) {}
- create_field(Field *field, Field *orig_field);
+ Create_field() :after(0) {}
+ Create_field(Field *field, Field *orig_field);
+ /* Used to make a clone of this object for ALTER/CREATE TABLE */
+ Create_field *clone(MEM_ROOT *mem_root) const
+ { return new (mem_root) Create_field(*this); }
void create_length_to_internal_length(void);
/* Init for a tmp table field. To be extended if need be. */
@@ -1592,9 +2085,14 @@ class Send_field {
*/
class Copy_field :public Sql_alloc {
- void (*get_copy_func(Field *to,Field *from))(Copy_field *);
+ /**
+ Convenience definition of a copy function returned by
+ get_copy_func.
+ */
+ typedef void Copy_func(Copy_field*);
+ Copy_func *get_copy_func(Field *to, Field *from);
public:
- char *from_ptr,*to_ptr;
+ uchar *from_ptr,*to_ptr;
uchar *from_null_ptr,*to_null_ptr;
my_bool *null_row;
uint from_bit,to_bit;
@@ -1605,20 +2103,19 @@ public:
Copy_field() {}
~Copy_field() {}
void set(Field *to,Field *from,bool save); // Field to field
- void set(char *to,Field *from); // Field to string
+ void set(uchar *to,Field *from); // Field to string
void (*do_copy)(Copy_field *);
void (*do_copy2)(Copy_field *); // Used to handle null values
};
-Field *make_field(char *ptr, uint32 field_length,
+Field *make_field(TABLE_SHARE *share, uchar *ptr, uint32 field_length,
uchar *null_pos, uchar null_bit,
uint pack_flag, enum_field_types field_type,
CHARSET_INFO *cs,
Field::geometry_type geom_type,
Field::utype unireg_check,
- TYPELIB *interval, const char *field_name,
- struct st_table *table);
+ TYPELIB *interval, const char *field_name);
uint pack_length_to_packflag(uint type);
enum_field_types get_blob_type_from_length(ulong length);
uint32 calc_pack_length(enum_field_types type,uint32 length);
@@ -1647,6 +2144,7 @@ int set_field_to_null_with_conversions(Field *field, bool no_conversions);
#define FIELDFLAG_NO_DEFAULT 16384 /* sql */
#define FIELDFLAG_SUM ((uint) 32768)// predit: +#fieldflag
#define FIELDFLAG_MAYBE_NULL ((uint) 32768)// sql
+#define FIELDFLAG_HEX_ESCAPE ((uint) 0x10000)
#define FIELDFLAG_PACK_SHIFT 3
#define FIELDFLAG_DEC_SHIFT 8
#define FIELDFLAG_MAX_DEC 31
@@ -1672,3 +2170,4 @@ int set_field_to_null_with_conversions(Field *field, bool no_conversions);
#define f_maybe_null(x) (x & FIELDFLAG_MAYBE_NULL)
#define f_no_default(x) (x & FIELDFLAG_NO_DEFAULT)
#define f_bit_as_char(x) ((x) & FIELDFLAG_TREAT_BIT_AS_CHAR)
+#define f_is_hex_escape(x) ((x) & FIELDFLAG_HEX_ESCAPE)
diff --git a/sql/field_conv.cc b/sql/field_conv.cc
index 2705d4f617b..11d0bb9cc82 100644
--- a/sql/field_conv.cc
+++ b/sql/field_conv.cc
@@ -14,11 +14,15 @@
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
-/*
- Functions to copy data to or from fields
- This could be done with a single short function but opencoding this
- gives much more speed.
- */
+/**
+ @file
+
+ @brief
+ Functions to copy data to or from fields
+
+ This could be done with a single short function but opencoding this
+ gives much more speed.
+*/
#include "mysql_priv.h"
#include <m_ctype.h>
@@ -118,33 +122,32 @@ set_field_to_null(Field *field)
return 0;
}
field->reset();
- if (current_thd->count_cuted_fields == CHECK_FIELD_WARN)
+ if (field->table->in_use->count_cuted_fields == CHECK_FIELD_WARN)
{
field->set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED, 1);
return 0;
}
- if (!current_thd->no_errors)
+ if (!field->table->in_use->no_errors)
my_error(ER_BAD_NULL_ERROR, MYF(0), field->field_name);
return -1;
}
-/*
- Set field to NULL or TIMESTAMP or to next auto_increment number
-
- SYNOPSIS
- set_field_to_null_with_conversions()
- field Field to update
- no_conversion Set to 1 if we should return 1 if field can't
- take null values.
- If set to 0 we will do store the 'default value'
- if the field is a special field. If not we will
- give an error.
-
- RETURN VALUES
- 0 Field could take 0 or an automatic conversion was used
- -1 Field could not take NULL and no conversion was used.
- If no_conversion was not set, an error message is printed
+/**
+ Set field to NULL or TIMESTAMP or to next auto_increment number.
+
+ @param field Field to update
+ @param no_conversions Set to 1 if we should return 1 if field can't
+ take null values.
+ If set to 0 we will do store the 'default value'
+ if the field is a special field. If not we will
+ give an error.
+
+ @retval
+ 0 Field could take 0 or an automatic conversion was used
+ @retval
+ -1 Field could not take NULL and no conversion was used.
+ If no_conversion was not set, an error message is printed
*/
int
@@ -164,7 +167,7 @@ set_field_to_null_with_conversions(Field *field, bool no_conversions)
when set to NULL (TIMESTAMP fields which allow setting to NULL
are handled by first check).
*/
- if (field->type() == FIELD_TYPE_TIMESTAMP)
+ if (field->type() == MYSQL_TYPE_TIMESTAMP)
{
((Field_timestamp*) field)->set_time();
return 0; // Ok to set time to NULL
@@ -175,12 +178,12 @@ set_field_to_null_with_conversions(Field *field, bool no_conversions)
field->table->auto_increment_field_not_null= FALSE;
return 0; // field is set in fill_record()
}
- if (current_thd->count_cuted_fields == CHECK_FIELD_WARN)
+ if (field->table->in_use->count_cuted_fields == CHECK_FIELD_WARN)
{
field->set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_BAD_NULL_ERROR, 1);
return 0;
}
- if (!current_thd->no_errors)
+ if (!field->table->in_use->no_errors)
my_error(ER_BAD_NULL_ERROR, MYF(0), field->field_name);
return -1;
}
@@ -283,7 +286,7 @@ static void do_conv_blob(Copy_field *copy)
copy->tmp.charset());
}
-/* Save blob in copy->tmp for GROUP BY */
+/** Save blob in copy->tmp for GROUP BY. */
static void do_save_blob(Copy_field *copy)
{
@@ -352,9 +355,9 @@ static void do_field_decimal(Copy_field *copy)
}
-/*
+/**
string copy for single byte characters set when to string is shorter than
- from string
+ from string.
*/
static void do_cut_string(Copy_field *copy)
@@ -364,8 +367,8 @@ static void do_cut_string(Copy_field *copy)
/* Check if we loosed any important characters */
if (cs->cset->scan(cs,
- copy->from_ptr + copy->to_length,
- copy->from_ptr + copy->from_length,
+ (char*) copy->from_ptr + copy->to_length,
+ (char*) copy->from_ptr + copy->from_length,
MY_SEQ_SPACES) < copy->from_length - copy->to_length)
{
copy->to_field->set_warning(MYSQL_ERROR::WARN_LEVEL_WARN,
@@ -374,17 +377,19 @@ static void do_cut_string(Copy_field *copy)
}
-/*
+/**
string copy for multi byte characters set when to string is shorter than
- from string
+ from string.
*/
static void do_cut_string_complex(Copy_field *copy)
{ // Shorter string field
int well_formed_error;
CHARSET_INFO *cs= copy->from_field->charset();
- const char *from_end= copy->from_ptr + copy->from_length;
- uint copy_length= cs->cset->well_formed_len(cs, copy->from_ptr, from_end,
+ const uchar *from_end= copy->from_ptr + copy->from_length;
+ uint copy_length= cs->cset->well_formed_len(cs,
+ (char*) copy->from_ptr,
+ (char*) from_end,
copy->to_length / cs->mbmaxlen,
&well_formed_error);
if (copy->to_length < copy_length)
@@ -393,7 +398,8 @@ static void do_cut_string_complex(Copy_field *copy)
/* Check if we lost any important characters */
if (well_formed_error ||
- cs->cset->scan(cs, copy->from_ptr + copy_length, from_end,
+ cs->cset->scan(cs, (char*) copy->from_ptr + copy_length,
+ (char*) from_end,
MY_SEQ_SPACES) < (copy->from_length - copy_length))
{
copy->to_field->set_warning(MYSQL_ERROR::WARN_LEVEL_WARN,
@@ -401,7 +407,7 @@ static void do_cut_string_complex(Copy_field *copy)
}
if (copy_length < copy->to_length)
- cs->cset->fill(cs, copy->to_ptr + copy_length,
+ cs->cset->fill(cs, (char*) copy->to_ptr + copy_length,
copy->to_length - copy_length, ' ');
}
@@ -412,7 +418,7 @@ static void do_expand_binary(Copy_field *copy)
{
CHARSET_INFO *cs= copy->from_field->charset();
memcpy(copy->to_ptr,copy->from_ptr,copy->from_length);
- cs->cset->fill(cs, copy->to_ptr+copy->from_length,
+ cs->cset->fill(cs, (char*) copy->to_ptr+copy->from_length,
copy->to_length-copy->from_length, '\0');
}
@@ -422,7 +428,7 @@ static void do_expand_string(Copy_field *copy)
{
CHARSET_INFO *cs= copy->from_field->charset();
memcpy(copy->to_ptr,copy->from_ptr,copy->from_length);
- cs->cset->fill(cs, copy->to_ptr+copy->from_length,
+ cs->cset->fill(cs, (char*) copy->to_ptr+copy->from_length,
copy->to_length-copy->from_length, ' ');
}
@@ -433,7 +439,7 @@ static void do_varstring1(Copy_field *copy)
if (length > copy->to_length- 1)
{
length=copy->to_length - 1;
- if (current_thd->count_cuted_fields)
+ if (copy->from_field->table->in_use->count_cuted_fields)
copy->to_field->set_warning(MYSQL_ERROR::WARN_LEVEL_WARN,
WARN_DATA_TRUNCATED, 1);
}
@@ -447,9 +453,10 @@ static void do_varstring1_mb(Copy_field *copy)
int well_formed_error;
CHARSET_INFO *cs= copy->from_field->charset();
uint from_length= (uint) *(uchar*) copy->from_ptr;
- const char *from_ptr= copy->from_ptr + 1;
+ const uchar *from_ptr= copy->from_ptr + 1;
uint to_char_length= (copy->to_length - 1) / cs->mbmaxlen;
- uint length= cs->cset->well_formed_len(cs, from_ptr, from_ptr + from_length,
+ uint length= cs->cset->well_formed_len(cs, (char*) from_ptr,
+ (char*) from_ptr + from_length,
to_char_length, &well_formed_error);
if (length < from_length)
{
@@ -457,7 +464,7 @@ static void do_varstring1_mb(Copy_field *copy)
copy->to_field->set_warning(MYSQL_ERROR::WARN_LEVEL_WARN,
WARN_DATA_TRUNCATED, 1);
}
- *(uchar*) copy->to_ptr= (uchar) length;
+ *copy->to_ptr= (uchar) length;
memcpy(copy->to_ptr + 1, from_ptr, length);
}
@@ -468,7 +475,7 @@ static void do_varstring2(Copy_field *copy)
if (length > copy->to_length- HA_KEY_BLOB_LENGTH)
{
length=copy->to_length-HA_KEY_BLOB_LENGTH;
- if (current_thd->count_cuted_fields)
+ if (copy->from_field->table->in_use->count_cuted_fields)
copy->to_field->set_warning(MYSQL_ERROR::WARN_LEVEL_WARN,
WARN_DATA_TRUNCATED, 1);
}
@@ -484,8 +491,9 @@ static void do_varstring2_mb(Copy_field *copy)
CHARSET_INFO *cs= copy->from_field->charset();
uint char_length= (copy->to_length - HA_KEY_BLOB_LENGTH) / cs->mbmaxlen;
uint from_length= uint2korr(copy->from_ptr);
- const char *from_beg= copy->from_ptr + HA_KEY_BLOB_LENGTH;
- uint length= cs->cset->well_formed_len(cs, from_beg, from_beg + from_length,
+ const uchar *from_beg= copy->from_ptr + HA_KEY_BLOB_LENGTH;
+ uint length= cs->cset->well_formed_len(cs, (char*) from_beg,
+ (char*) from_beg + from_length,
char_length, &well_formed_error);
if (length < from_length)
{
@@ -502,7 +510,7 @@ static void do_varstring2_mb(Copy_field *copy)
** The different functions that fills in a Copy_field class
***************************************************************************/
-/*
+/**
copy of field to maybe null string.
If field is null then the all bytes are set to 0.
if field is not null then the first byte is set to 1 and the rest of the
@@ -510,7 +518,7 @@ static void do_varstring2_mb(Copy_field *copy)
The 'to' buffer should have a size of field->pack_length()+1
*/
-void Copy_field::set(char *to,Field *from)
+void Copy_field::set(uchar *to,Field *from)
{
from_ptr=from->ptr;
to_ptr=to;
@@ -555,7 +563,7 @@ void Copy_field::set(char *to,Field *from)
*/
void Copy_field::set(Field *to,Field *from,bool save)
{
- if (to->type() == FIELD_TYPE_NULL)
+ if (to->type() == MYSQL_TYPE_NULL)
{
to_null_ptr=0; // For easy debugging
to_ptr=0;
@@ -589,7 +597,7 @@ void Copy_field::set(Field *to,Field *from,bool save)
}
else
{
- if (to_field->type() == FIELD_TYPE_TIMESTAMP)
+ if (to_field->type() == MYSQL_TYPE_TIMESTAMP)
do_copy= do_copy_timestamp; // Automatic timestamp
else if (to_field == to_field->table->next_number_field)
do_copy= do_copy_next_number;
@@ -615,7 +623,8 @@ void Copy_field::set(Field *to,Field *from,bool save)
}
-void (*Copy_field::get_copy_func(Field *to,Field *from))(Copy_field*)
+Copy_field::Copy_func *
+Copy_field::get_copy_func(Field *to,Field *from)
{
bool compatible_db_low_byte_first= (to->table->s->db_low_byte_first ==
from->table->s->db_low_byte_first);
@@ -633,8 +642,8 @@ void (*Copy_field::get_copy_func(Field *to,Field *from))(Copy_field*)
}
else
{
- if (to->real_type() == FIELD_TYPE_BIT ||
- from->real_type() == FIELD_TYPE_BIT)
+ if (to->real_type() == MYSQL_TYPE_BIT ||
+ from->real_type() == MYSQL_TYPE_BIT)
return do_field_int;
if (to->result_type() == DECIMAL_RESULT)
return do_field_decimal;
@@ -658,17 +667,17 @@ void (*Copy_field::get_copy_func(Field *to,Field *from))(Copy_field*)
!compatible_db_low_byte_first ||
((to->table->in_use->variables.sql_mode &
(MODE_NO_ZERO_IN_DATE | MODE_NO_ZERO_DATE | MODE_INVALID_DATES)) &&
- to->type() == FIELD_TYPE_DATE ||
- to->type() == FIELD_TYPE_DATETIME))
+ to->type() == MYSQL_TYPE_DATE ||
+ to->type() == MYSQL_TYPE_DATETIME))
{
- if (from->real_type() == FIELD_TYPE_ENUM ||
- from->real_type() == FIELD_TYPE_SET)
+ if (from->real_type() == MYSQL_TYPE_ENUM ||
+ from->real_type() == MYSQL_TYPE_SET)
if (to->result_type() != STRING_RESULT)
return do_field_int; // Convert SET to number
return do_field_string;
}
- if (to->real_type() == FIELD_TYPE_ENUM ||
- to->real_type() == FIELD_TYPE_SET)
+ if (to->real_type() == MYSQL_TYPE_ENUM ||
+ to->real_type() == MYSQL_TYPE_SET)
{
if (!to->eq_def(from))
{
@@ -698,7 +707,7 @@ void (*Copy_field::get_copy_func(Field *to,Field *from))(Copy_field*)
do_cut_string : do_cut_string_complex);
else if (to_length > from_length)
{
- if ((to->flags & BINARY_FLAG) != 0)
+ if (to->charset() == &my_charset_bin)
return do_expand_binary;
else
return do_expand_string;
@@ -709,7 +718,7 @@ void (*Copy_field::get_copy_func(Field *to,Field *from))(Copy_field*)
to_length != from_length ||
!compatible_db_low_byte_first)
{
- if (to->real_type() == FIELD_TYPE_DECIMAL ||
+ if (to->real_type() == MYSQL_TYPE_DECIMAL ||
to->result_type() == STRING_RESULT)
return do_field_string;
if (to->result_type() == INT_RESULT)
@@ -720,7 +729,7 @@ void (*Copy_field::get_copy_func(Field *to,Field *from))(Copy_field*)
{
if (!to->eq_def(from) || !compatible_db_low_byte_first)
{
- if (to->real_type() == FIELD_TYPE_DECIMAL)
+ if (to->real_type() == MYSQL_TYPE_DECIMAL)
return do_field_string;
if (to->result_type() == INT_RESULT)
return do_field_int;
@@ -742,27 +751,27 @@ void (*Copy_field::get_copy_func(Field *to,Field *from))(Copy_field*)
}
-/* Simple quick field convert that is called on insert */
+/** Simple quick field convert that is called on insert. */
int field_conv(Field *to,Field *from)
{
if (to->real_type() == from->real_type() &&
- !(to->type() == FIELD_TYPE_BLOB && to->table->copy_blobs))
+ !(to->type() == MYSQL_TYPE_BLOB && to->table->copy_blobs))
{
if (to->pack_length() == from->pack_length() &&
!(to->flags & UNSIGNED_FLAG && !(from->flags & UNSIGNED_FLAG)) &&
- to->real_type() != FIELD_TYPE_ENUM &&
- to->real_type() != FIELD_TYPE_SET &&
- to->real_type() != FIELD_TYPE_BIT &&
- (to->real_type() != FIELD_TYPE_NEWDECIMAL ||
+ to->real_type() != MYSQL_TYPE_ENUM &&
+ to->real_type() != MYSQL_TYPE_SET &&
+ to->real_type() != MYSQL_TYPE_BIT &&
+ (to->real_type() != MYSQL_TYPE_NEWDECIMAL ||
(to->field_length == from->field_length &&
(((Field_num*)to)->dec == ((Field_num*)from)->dec))) &&
from->charset() == to->charset() &&
to->table->s->db_low_byte_first == from->table->s->db_low_byte_first &&
(!(to->table->in_use->variables.sql_mode &
(MODE_NO_ZERO_IN_DATE | MODE_NO_ZERO_DATE | MODE_INVALID_DATES)) ||
- to->type() != FIELD_TYPE_DATE &&
- to->type() != FIELD_TYPE_DATETIME) &&
+ to->type() != MYSQL_TYPE_DATE &&
+ to->type() != MYSQL_TYPE_DATETIME) &&
(from->real_type() != MYSQL_TYPE_VARCHAR ||
((Field_varstring*)from)->length_bytes ==
((Field_varstring*)to)->length_bytes))
@@ -775,7 +784,7 @@ int field_conv(Field *to,Field *from)
return 0;
}
}
- if (to->type() == FIELD_TYPE_BLOB)
+ if (to->type() == MYSQL_TYPE_BLOB)
{ // Be sure the value is stored
Field_blob *blob=(Field_blob*) to;
from->val_str(&blob->value);
@@ -790,8 +799,8 @@ int field_conv(Field *to,Field *from)
blob->value.copy();
return blob->store(blob->value.ptr(),blob->value.length(),from->charset());
}
- if (from->real_type() == FIELD_TYPE_ENUM &&
- to->real_type() == FIELD_TYPE_ENUM &&
+ if (from->real_type() == MYSQL_TYPE_ENUM &&
+ to->real_type() == MYSQL_TYPE_ENUM &&
from->val_int() == 0)
{
((Field_enum *)(to))->store_type(0);
@@ -799,9 +808,9 @@ int field_conv(Field *to,Field *from)
}
else if ((from->result_type() == STRING_RESULT &&
(to->result_type() == STRING_RESULT ||
- (from->real_type() != FIELD_TYPE_ENUM &&
- from->real_type() != FIELD_TYPE_SET))) ||
- to->type() == FIELD_TYPE_DECIMAL)
+ (from->real_type() != MYSQL_TYPE_ENUM &&
+ from->real_type() != MYSQL_TYPE_SET))) ||
+ to->type() == MYSQL_TYPE_DECIMAL)
{
char buff[MAX_FIELD_WIDTH];
String result(buff,sizeof(buff),from->charset());
diff --git a/sql/filesort.cc b/sql/filesort.cc
index f56e5b3a771..5d8b4e869c8 100644
--- a/sql/filesort.cc
+++ b/sql/filesort.cc
@@ -14,7 +14,12 @@
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
-/* Sorts a database */
+/**
+ @file
+
+ @brief
+ Sorts a database
+*/
#include "mysql_priv.h"
#ifdef HAVE_STDDEF_H
@@ -27,24 +32,24 @@
#define SKIP_DBUG_IN_FILESORT
#endif
- /* How to write record_ref. */
-
+/// How to write record_ref.
#define WRITE_REF(file,from) \
-if (my_b_write((file),(byte*) (from),param->ref_length)) \
+if (my_b_write((file),(uchar*) (from),param->ref_length)) \
DBUG_RETURN(1);
/* functions defined in this file */
static char **make_char_array(char **old_pos, register uint fields,
uint length, myf my_flag);
-static byte *read_buffpek_from_file(IO_CACHE *buffer_file, uint count,
- byte *buf);
+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, 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);
-static void make_sortkey(SORTPARAM *param,uchar *to, byte *ref_pos);
+static void make_sortkey(SORTPARAM *param,uchar *to, uchar *ref_pos);
+static void register_used_fields(SORTPARAM *param);
static int merge_index(SORTPARAM *param,uchar *sort_buffer,
BUFFPEK *buffpek,
uint maxbuffer,IO_CACHE *tempfile,
@@ -57,43 +62,46 @@ static uint sortlength(THD *thd, SORT_FIELD *sortorder, uint s_length,
static SORT_ADDON_FIELD *get_addon_fields(THD *thd, Field **ptabfield,
uint sortlength, uint *plength);
static void unpack_addon_fields(struct st_sort_addon_field *addon_field,
- byte *buff);
-
-/*
- Sort a table
-
- SYNOPSIS
- filesort()
- table Table to sort
- sortorder How to sort the table
- s_length Number of elements in sortorder
- select condition to apply to the rows
- special Not used.
- (This could be used to sort the rows pointed on by
- select->file)
- examined_rows Store number of examined rows here
-
- IMPLEMENTATION
- Creates a set of pointers that can be used to read the rows
- in sorted order. This should be done with the functions
- in records.cc
-
- REQUIREMENTS
- Before calling filesort, one must have done
- table->file->info(HA_STATUS_VARIABLE)
-
- RETURN
+ uchar *buff);
+/**
+ Sort a table.
+ Creates a set of pointers that can be used to read the rows
+ in sorted order. This should be done with the functions
+ in records.cc.
+
+ Before calling filesort, one must have done
+ table->file->info(HA_STATUS_VARIABLE)
+
+ The result set is stored in table->io_cache or
+ table->record_pointers.
+
+ @param thd Current thread
+ @param table Table to sort
+ @param sortorder How to sort the table
+ @param s_length Number of elements in sortorder
+ @param select condition to apply to the rows
+ @param max_rows Return only this many rows
+ @param sort_positions Set to 1 if we want to force sorting by position
+ (Needed by UPDATE/INSERT or ALTER TABLE)
+ @param examined_rows Store number of examined rows here
+
+ @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().
+
+ @retval
HA_POS_ERROR Error
- # Number of rows
-
+ @retval
+ \# Number of rows
+ @retval
examined_rows will be set to number of examined rows
-
- The result set is stored in table->io_cache or
- table->record_pointers
*/
ha_rows filesort(THD *thd, TABLE *table, SORT_FIELD *sortorder, uint s_length,
- SQL_SELECT *select, ha_rows max_rows, ha_rows *examined_rows)
+ SQL_SELECT *select, ha_rows max_rows,
+ bool sort_positions, ha_rows *examined_rows)
{
int error;
ulong memavl, min_sort_memory;
@@ -137,8 +145,8 @@ ha_rows filesort(THD *thd, TABLE *table, SORT_FIELD *sortorder, uint s_length,
param.ref_length= table->file->ref_length;
param.addon_field= 0;
param.addon_length= 0;
- if (!(table->file->table_flags() & HA_FAST_KEY_READ) &&
- !table->fulltext_searched)
+ if (!(table->file->ha_table_flags() & HA_FAST_KEY_READ) &&
+ !table->fulltext_searched && !sort_positions)
{
/*
Get the descriptors of all fields whose values are appended
@@ -156,7 +164,7 @@ ha_rows filesort(THD *thd, TABLE *table, SORT_FIELD *sortorder, uint s_length,
if (param.addon_field)
{
param.res_length= param.addon_length;
- if (!(table_sort.addon_buf= (byte *) my_malloc(param.addon_length,
+ if (!(table_sort.addon_buf= (uchar *) my_malloc(param.addon_length,
MYF(MY_WME))))
goto err;
}
@@ -174,17 +182,17 @@ ha_rows filesort(THD *thd, TABLE *table, SORT_FIELD *sortorder, uint s_length,
if (select && select->quick)
{
- statistic_increment(thd->status_var.filesort_range_count, &LOCK_status);
+ status_var_increment(thd->status_var.filesort_range_count);
}
else
{
- statistic_increment(thd->status_var.filesort_scan_count, &LOCK_status);
+ status_var_increment(thd->status_var.filesort_scan_count);
}
#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->records)+EXTRA_RECORDS;
+ table->file->stats.records)+EXTRA_RECORDS;
selected_records_file=0;
}
else
@@ -201,7 +209,7 @@ ha_rows filesort(THD *thd, TABLE *table, SORT_FIELD *sortorder, uint s_length,
}
if (multi_byte_charset &&
- !(param.tmp_buffer=my_malloc(param.sort_length,MYF(MY_WME))))
+ !(param.tmp_buffer= (char*) my_malloc(param.sort_length,MYF(MY_WME))))
goto err;
memavl= thd->variables.sortbuff_size;
@@ -251,7 +259,7 @@ ha_rows filesort(THD *thd, TABLE *table, SORT_FIELD *sortorder, uint s_length,
table_sort.buffpek= 0;
}
if (!(table_sort.buffpek=
- read_buffpek_from_file(&buffpek_pointers, maxbuffer,
+ (uchar *) read_buffpek_from_file(&buffpek_pointers, maxbuffer,
table_sort.buffpek)))
goto err;
buffpek= (BUFFPEK *) table_sort.buffpek;
@@ -291,9 +299,9 @@ ha_rows filesort(THD *thd, TABLE *table, SORT_FIELD *sortorder, uint s_length,
x_free(param.tmp_buffer);
if (!subselect || !subselect->is_uncacheable())
{
- x_free((gptr) sort_keys);
+ x_free((uchar*) sort_keys);
table_sort.sort_keys= 0;
- x_free((gptr) buffpek);
+ x_free((uchar*) buffpek);
table_sort.buffpek= 0;
table_sort.buffpek_len= 0;
}
@@ -331,19 +339,19 @@ void filesort_free_buffers(TABLE *table, bool full)
{
if (table->sort.record_pointers)
{
- my_free((gptr) table->sort.record_pointers,MYF(0));
+ my_free((uchar*) table->sort.record_pointers,MYF(0));
table->sort.record_pointers=0;
}
if (full)
{
if (table->sort.sort_keys )
{
- x_free((gptr) table->sort.sort_keys);
+ x_free((uchar*) table->sort.sort_keys);
table->sort.sort_keys= 0;
}
if (table->sort.buffpek)
{
- x_free((gptr) table->sort.buffpek);
+ x_free((uchar*) table->sort.buffpek);
table->sort.buffpek= 0;
table->sort.buffpek_len= 0;
}
@@ -357,7 +365,7 @@ void filesort_free_buffers(TABLE *table, bool full)
}
}
- /* Make a array of string pointers */
+/** Make a array of string pointers. */
static char **make_char_array(char **old_pos, register uint fields,
uint length, myf my_flag)
@@ -378,22 +386,22 @@ static char **make_char_array(char **old_pos, register uint fields,
} /* make_char_array */
-/* Read 'count' number of buffer pointers into memory */
+/** Read 'count' number of buffer pointers into memory. */
-static byte *read_buffpek_from_file(IO_CACHE *buffpek_pointers, uint count,
- byte *buf)
+static uchar *read_buffpek_from_file(IO_CACHE *buffpek_pointers, uint count,
+ uchar *buf)
{
ulong length= sizeof(BUFFPEK)*count;
- byte *tmp= buf;
+ uchar *tmp= buf;
DBUG_ENTER("read_buffpek_from_file");
if (count > UINT_MAX/sizeof(BUFFPEK))
return 0; /* sizeof(BUFFPEK)*count will overflow */
if (!tmp)
- tmp= (byte *)my_malloc(length, MYF(MY_WME));
+ tmp= (uchar *)my_malloc(length, MYF(MY_WME));
if (tmp)
{
if (reinit_io_cache(buffpek_pointers,READ_CACHE,0L,0,0) ||
- my_b_read(buffpek_pointers, tmp, length))
+ my_b_read(buffpek_pointers, (uchar*) tmp, length))
{
my_free((char*) tmp, MYF(0));
tmp=0;
@@ -453,38 +461,40 @@ static void dbug_print_record(TABLE *table, bool print_rowid)
}
#endif
-/*
+/**
Search after sort_keys and write them into tempfile.
- SYNOPSIS
- find_all_keys()
- param Sorting parameter
- select Use this to get source data
- sort_keys Array of pointers to sort key + addon buffers.
- buffpek_pointers File to write BUFFPEKs describing sorted segments
- in tempfile.
- tempfile File to write sorted sequences of sortkeys to.
- indexfile If !NULL, use it for source data (contains rowids)
-
- NOTE
+ All produced sequences are guaranteed to be non-empty.
+
+ @param param Sorting parameter
+ @param select Use this to get source data
+ @param sort_keys Array of pointers to sort key + addon buffers.
+ @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:
- while (get_next_sortkey())
- {
- if (no free space in sort_keys buffers)
- {
- sort sort_keys buffer;
- dump sorted sequence to 'tempfile';
- dump BUFFPEK describing sequence location into 'buffpek_pointers';
- }
- put sort key into 'sort_keys';
- }
- if (sort_keys has some elements && dumped at least once)
- sort-dump-dump as above;
- else
- don't sort, leave sort_keys array to be sorted by caller.
-
- All produced sequences are guaranteed to be non-empty.
- RETURN
+ @verbatim
+ while (get_next_sortkey())
+ {
+ if (no free space in sort_keys buffers)
+ {
+ sort sort_keys buffer;
+ dump sorted sequence to 'tempfile';
+ dump BUFFPEK describing sequence location into 'buffpek_pointers';
+ }
+ put sort key into 'sort_keys';
+ }
+ if (sort_keys has some elements && dumped at least once)
+ sort-dump-dump as above;
+ else
+ don't sort, leave sort_keys array to be sorted by caller.
+ @endverbatim
+
+ @retval
Number of records written on success.
+ @retval
HA_POS_ERROR on error.
*/
@@ -495,14 +505,17 @@ static ha_rows find_all_keys(SORTPARAM *param, SQL_SELECT *select,
{
int error,flag,quick_select;
uint idx,indexpos,ref_length;
- byte *ref_pos,*next_pos,ref_buff[MAX_REFLENGTH];
+ uchar *ref_pos,*next_pos,ref_buff[MAX_REFLENGTH];
my_off_t record;
TABLE *sort_form;
THD *thd= current_thd;
volatile THD::killed_state *killed= &thd->killed;
handler *file;
+ MY_BITMAP *save_read_set, *save_write_set;
DBUG_ENTER("find_all_keys");
- DBUG_PRINT("info",("using: %s",(select?select->quick?"ranges":"where":"every row")));
+ DBUG_PRINT("info",("using: %s",
+ (select ? select->quick ? "ranges" : "where":
+ "every row")));
idx=indexpos=0;
error=quick_select=0;
@@ -512,14 +525,14 @@ 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->table_flags() & HA_REC_NOT_IN_SEQ)
+ flag= ((!indexfile && file->ha_table_flags() & HA_REC_NOT_IN_SEQ)
|| quick_select);
if (indexfile || flag)
ref_pos= &file->ref[0];
next_pos=ref_pos;
if (! indexfile && ! quick_select)
{
- next_pos=(byte*) 0; /* Find records in sequence */
+ next_pos=(uchar*) 0; /* Find records in sequence */
file->ha_rnd_init(1);
file->extra_opt(HA_EXTRA_CACHE,
current_thd->variables.read_buff_size);
@@ -531,15 +544,25 @@ static ha_rows find_all_keys(SORTPARAM *param, SQL_SELECT *select,
DBUG_RETURN(HA_POS_ERROR);
}
+ /* Remember original bitmaps */
+ save_read_set= sort_form->read_set;
+ save_write_set= sort_form->write_set;
+ /* Set up temporary column read map for columns used by sort */
+ bitmap_clear_all(&sort_form->tmp_set);
+ /* 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 (select && select->cond)
+ select->cond->walk(&Item::register_field_in_read_map, 1,
+ (uchar*) sort_form);
+ sort_form->column_bitmaps_set(&sort_form->tmp_set, &sort_form->tmp_set);
+
for (;;)
{
if (quick_select)
{
if ((error= select->quick->get_next()))
- {
- error= HA_ERR_END_OF_FILE;
break;
- }
file->position(sort_form->record[0]);
DBUG_EXECUTE_IF("debug_filesort", dbug_print_record(sort_form, TRUE););
}
@@ -547,7 +570,7 @@ static ha_rows find_all_keys(SORTPARAM *param, SQL_SELECT *select,
{
if (indexfile)
{
- if (my_b_read(indexfile,(byte*) ref_pos,ref_length)) /* purecov: deadcode */
+ if (my_b_read(indexfile,(uchar*) ref_pos,ref_length)) /* purecov: deadcode */
{
error= my_errno ? my_errno : -1; /* Abort */
break;
@@ -595,7 +618,7 @@ static ha_rows find_all_keys(SORTPARAM *param, SQL_SELECT *select,
else
file->unlock_row();
/* It does not make sense to read more keys in case of a fatal error */
- if (thd->net.report_error)
+ if (thd->is_error())
break;
}
if (!quick_select)
@@ -605,9 +628,12 @@ static ha_rows find_all_keys(SORTPARAM *param, SQL_SELECT *select,
file->ha_rnd_end();
}
- if (thd->net.report_error)
+ if (thd->is_error())
DBUG_RETURN(HA_POS_ERROR);
+ /* Signal we should use orignal column read and write maps */
+ sort_form->column_bitmaps_set(save_read_set, save_write_set);
+
DBUG_PRINT("test",("error: %d indexpos: %d",error,indexpos));
if (error != HA_ERR_END_OF_FILE)
{
@@ -623,23 +649,25 @@ static ha_rows find_all_keys(SORTPARAM *param, SQL_SELECT *select,
} /* find_all_keys */
-/*
+/**
+ @details
Sort the buffer and write:
- 1) the sorted sequence to tempfile
- 2) a BUFFPEK describing the sorted sequence position to buffpek_pointers
- (was: Skriver en buffert med nycklar till filen)
- SYNOPSIS
- write_keys()
- param Sort parameters
- sort_keys Array of pointers to keys to sort
- count Number of elements in sort_keys array
- buffpek_pointers One 'BUFFPEK' struct will be written into this file.
- The BUFFPEK::{file_pos, count} will indicate where
- the sorted data was stored.
- tempfile The sorted sequence will be written into this file.
-
- RETURN
+ -# the sorted sequence to tempfile
+ -# a BUFFPEK describing the sorted sequence position to buffpek_pointers
+
+ (was: Skriver en buffert med nycklar till filen)
+
+ @param param Sort parameters
+ @param sort_keys Array of pointers to keys to sort
+ @param count Number of elements in sort_keys array
+ @param buffpek_pointers One 'BUFFPEK' struct will be written into this file.
+ The BUFFPEK::{file_pos, count} will indicate where
+ the sorted data was stored.
+ @param tempfile The sorted sequence will be written into this file.
+
+ @retval
0 OK
+ @retval
1 Error
*/
@@ -647,7 +675,7 @@ static int
write_keys(SORTPARAM *param, register uchar **sort_keys, uint count,
IO_CACHE *buffpek_pointers, IO_CACHE *tempfile)
{
- uint sort_length, rec_length;
+ size_t sort_length, rec_length;
uchar **end;
BUFFPEK buffpek;
DBUG_ENTER("write_keys");
@@ -657,7 +685,7 @@ write_keys(SORTPARAM *param, register uchar **sort_keys, uint count,
#ifdef MC68000
quicksort(sort_keys,count,sort_length);
#else
- my_string_ptr_sort((gptr) sort_keys, (uint) count, sort_length);
+ my_string_ptr_sort((uchar*) sort_keys, (uint) count, sort_length);
#endif
if (!my_b_inited(tempfile) &&
open_cached_file(tempfile, mysql_tmpdir, TEMP_PREFIX, DISK_BUFFER_SIZE,
@@ -671,9 +699,9 @@ write_keys(SORTPARAM *param, register uchar **sort_keys, uint count,
count=(uint) param->max_rows; /* purecov: inspected */
buffpek.count=(ha_rows) count;
for (end=sort_keys+count ; sort_keys != end ; sort_keys++)
- if (my_b_write(tempfile, (byte*) *sort_keys, (uint) rec_length))
+ if (my_b_write(tempfile, (uchar*) *sort_keys, (uint) rec_length))
goto err;
- if (my_b_write(buffpek_pointers, (byte*) &buffpek, sizeof(buffpek)))
+ if (my_b_write(buffpek_pointers, (uchar*) &buffpek, sizeof(buffpek)))
goto err;
DBUG_RETURN(0);
@@ -682,8 +710,8 @@ err:
} /* write_keys */
-/*
- Store length as suffix in high-byte-first order
+/**
+ Store length as suffix in high-byte-first order.
*/
static inline void store_length(uchar *to, uint length, uint pack_length)
@@ -705,10 +733,10 @@ static inline void store_length(uchar *to, uint length, uint pack_length)
}
- /* makes a sort-key from record */
+/** Make a sort-key from record. */
static void make_sortkey(register SORTPARAM *param,
- register uchar *to, byte *ref_pos)
+ register uchar *to, uchar *ref_pos)
{
reg3 Field *field;
reg1 SORT_FIELD *sort_field;
@@ -735,7 +763,7 @@ static void make_sortkey(register SORTPARAM *param,
else
*to++=1;
}
- field->sort_string((char*) to,sort_field->length);
+ field->sort_string(to, sort_field->length);
}
else
{ // Item
@@ -744,62 +772,70 @@ static void make_sortkey(register SORTPARAM *param,
switch (sort_field->result_type) {
case STRING_RESULT:
{
- 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);
- String *res= item->str_result(&tmp);
- if (!res)
- {
- if (maybe_null)
- bzero((char*) to-1,sort_field->length+1);
- else
- {
- DBUG_PRINT("warning",
- ("Got null on something that shouldn't be null"));
- bzero((char*) to,sort_field->length); // Avoid crash
- }
- 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; /* purecov: inspected */
- 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)
+ 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);
+ String *res= item->str_result(&tmp);
+ if (!res)
+ {
+ if (maybe_null)
+ bzero((char*) to-1,sort_field->length+1);
+ else
{
- char *from=(char*) res->ptr();
- uint tmp_length;
- if ((unsigned char *)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,
- (unsigned char *) from, length);
- DBUG_ASSERT(tmp_length == sort_field->length);
+ /* purecov: begin deadcode */
+ /*
+ This should only happen during extreme conditions if we run out
+ of memory or have an item marked not null when it can be null.
+ This code is here mainly to avoid a hard crash in this case.
+ */
+ DBUG_ASSERT(0);
+ DBUG_PRINT("warning",
+ ("Got null on something that shouldn't be null"));
+ bzero((char*) to,sort_field->length); // Avoid crash
+ /* purecov: end */
}
- else
+ 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;
+ if ((uchar*) from == to)
{
- my_strnxfrm(cs,(uchar*)to,length,(const uchar*)res->ptr(),length);
- cs->cset->fill(cs, (char *)to+length,diff,fill_char);
+ set_if_smaller(length,sort_field->length);
+ memcpy(param->tmp_buffer,from,length);
+ from=param->tmp_buffer;
}
- break;
+ tmp_length= my_strnxfrm(cs,to,sort_field->length,
+ (uchar*) from, length);
+ DBUG_ASSERT(tmp_length == sort_field->length);
+ }
+ else
+ {
+ my_strnxfrm(cs,(uchar*)to,length,(const uchar*)res->ptr(),length);
+ cs->cset->fill(cs, (char *)to+length,diff,fill_char);
+ }
+ break;
}
case INT_RESULT:
{
@@ -856,7 +892,7 @@ static void make_sortkey(register SORTPARAM *param,
}
*to++=1;
}
- my_decimal2binary(E_DEC_FATAL_ERROR, dec_val, (char*)to,
+ my_decimal2binary(E_DEC_FATAL_ERROR, dec_val, to,
item->max_length - (item->decimals ? 1:0),
item->decimals);
break;
@@ -874,7 +910,7 @@ static void make_sortkey(register SORTPARAM *param,
}
*to++=1;
}
- change_double_for_sort(value,(byte*) to);
+ change_double_for_sort(value,(uchar*) to);
break;
}
case ROW_RESULT:
@@ -924,13 +960,13 @@ static void make_sortkey(register SORTPARAM *param,
else
{
#ifdef HAVE_purify
- uchar *end= (uchar*) field->pack((char *) to, field->ptr);
+ uchar *end= field->pack(to, field->ptr);
uint length= (uint) ((to + addonf->length) - end);
DBUG_ASSERT((int) length >= 0);
if (length)
bzero(end, length);
#else
- (void) field->pack((char *) to, field->ptr);
+ (void) field->pack(to, field->ptr);
#endif
}
to+= addonf->length;
@@ -939,25 +975,68 @@ static void make_sortkey(register SORTPARAM *param,
else
{
/* Save filepos last */
- memcpy((byte*) to, ref_pos, (size_s) param->ref_length);
+ memcpy((uchar*) to, ref_pos, (size_t) param->ref_length);
}
return;
}
+
+/*
+ Register fields used by sorting in the sorted table's read set
+*/
+
+static void register_used_fields(SORTPARAM *param)
+{
+ reg1 SORT_FIELD *sort_field;
+ TABLE *table=param->sort_form;
+ MY_BITMAP *bitmap= table->read_set;
+
+ for (sort_field= param->local_sortorder ;
+ sort_field != param->end ;
+ sort_field++)
+ {
+ Field *field;
+ if ((field= sort_field->field))
+ {
+ if (field->table == table)
+ bitmap_set_bit(bitmap, field->field_index);
+ }
+ else
+ { // Item
+ sort_field->item->walk(&Item::register_field_in_read_map, 1,
+ (uchar *) table);
+ }
+ }
+
+ if (param->addon_field)
+ {
+ SORT_ADDON_FIELD *addonf= param->addon_field;
+ Field *field;
+ for ( ; (field= addonf->field) ; addonf++)
+ bitmap_set_bit(bitmap, field->field_index);
+ }
+ else
+ {
+ /* Save filepos last */
+ table->prepare_for_position();
+ }
+}
+
+
static bool save_index(SORTPARAM *param, uchar **sort_keys, uint count,
FILESORT_INFO *table_sort)
{
uint offset,res_length;
- byte *to;
+ uchar *to;
DBUG_ENTER("save_index");
- my_string_ptr_sort((gptr) sort_keys, (uint) count, param->sort_length);
+ my_string_ptr_sort((uchar*) sort_keys, (uint) count, param->sort_length);
res_length= param->res_length;
offset= param->rec_length-res_length;
if ((ha_rows) count > param->max_rows)
count=(uint) param->max_rows;
if (!(to= table_sort->record_pointers=
- (byte*) my_malloc(res_length*count, MYF(MY_WME))))
+ (uchar*) my_malloc(res_length*count, MYF(MY_WME))))
DBUG_RETURN(1); /* purecov: inspected */
for (uchar **end= sort_keys+count ; sort_keys != end ; sort_keys++)
{
@@ -968,7 +1047,7 @@ static bool save_index(SORTPARAM *param, uchar **sort_keys, uint count,
}
- /* Merge buffers to make < MERGEBUFF2 buffers */
+/** Merge buffers to make < MERGEBUFF2 buffers. */
int merge_many_buff(SORTPARAM *param, uchar *sort_buffer,
BUFFPEK *buffpek, uint *maxbuffer, IO_CACHE *t_file)
@@ -1021,8 +1100,12 @@ cleanup:
} /* merge_many_buff */
- /* Read data to buffer */
- /* This returns (uint) -1 if something goes wrong */
+/**
+ Read data to buffer.
+
+ @retval
+ (uint)-1 if something goes wrong
+*/
uint read_to_buffer(IO_CACHE *fromfile, BUFFPEK *buffpek,
uint rec_length)
@@ -1032,7 +1115,7 @@ 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,(byte*) buffpek->base,
+ if (my_pread(fromfile->file,(uchar*) buffpek->base,
(length= rec_length*count),buffpek->file_pos,MYF_RW))
return((uint) -1); /* purecov: inspected */
buffpek->key=buffpek->base;
@@ -1044,15 +1127,15 @@ uint read_to_buffer(IO_CACHE *fromfile, BUFFPEK *buffpek,
} /* read_to_buffer */
-/*
- Put all room used by freed buffer to use in adjacent buffer. Note, that
- we can't simply distribute memory evenly between all buffers, because
- new areas must not overlap with old ones.
- SYNOPSIS
- reuse_freed_buff()
- queue IN list of non-empty buffers, without freed buffer
- reuse IN empty buffer
- key_length IN key length
+/**
+ Put all room used by freed buffer to use in adjacent buffer.
+
+ Note, that we can't simply distribute memory evenly between all buffers,
+ because new areas must not overlap with old ones.
+
+ @param[in] queue list of non-empty buffers, without freed buffer
+ @param[in] reuse empty buffer
+ @param[in] key_length key length
*/
void reuse_freed_buff(QUEUE *queue, BUFFPEK *reuse, uint key_length)
@@ -1077,22 +1160,22 @@ void reuse_freed_buff(QUEUE *queue, BUFFPEK *reuse, uint key_length)
}
-/*
- Merge buffers to one buffer
- SYNOPSIS
- merge_buffers()
- param Sort parameter
- from_file File with source data (BUFFPEKs point to this file)
- to_file File to write the sorted result data.
- sort_buffer Buffer for data to store up to MERGEBUFF2 sort keys.
- lastbuff OUT Store here BUFFPEK describing data written to to_file
- Fb First element in source BUFFPEKs array
- Tb Last element in source BUFFPEKs array
- flag
-
- RETURN
- 0 - OK
- other - error
+/**
+ Merge buffers to one buffer.
+
+ @param param Sort parameter
+ @param from_file File with source data (BUFFPEKs point to this file)
+ @param to_file File to write the sorted result data.
+ @param sort_buffer Buffer for data to store up to MERGEBUFF2 sort keys.
+ @param lastbuff OUT Store here BUFFPEK describing data written to to_file
+ @param Fb First element in source BUFFPEKs array
+ @param Tb Last element in source BUFFPEKs array
+ @param flag
+
+ @retval
+ 0 OK
+ @retval
+ other error
*/
int merge_buffers(SORTPARAM *param, IO_CACHE *from_file,
@@ -1101,7 +1184,8 @@ int merge_buffers(SORTPARAM *param, IO_CACHE *from_file,
int flag)
{
int error;
- uint rec_length,sort_length,res_length,offset;
+ uint rec_length,res_length,offset;
+ size_t sort_length;
ulong maxcount;
ha_rows max_rows,org_max_rows;
my_off_t to_start_filepos;
@@ -1114,8 +1198,7 @@ int merge_buffers(SORTPARAM *param, IO_CACHE *from_file,
THD::killed_state not_killable;
DBUG_ENTER("merge_buffers");
- statistic_increment(current_thd->status_var.filesort_merge_passes,
- &LOCK_status);
+ status_var_increment(current_thd->status_var.filesort_merge_passes);
if (param->not_killable)
{
killed= &not_killable;
@@ -1157,7 +1240,7 @@ int merge_buffers(SORTPARAM *param, IO_CACHE *from_file,
if (error == -1)
goto err; /* purecov: inspected */
buffpek->max_keys= buffpek->mem_count; // If less data in buffers than expected
- queue_insert(&queue, (byte*) buffpek);
+ queue_insert(&queue, (uchar*) buffpek);
}
if (param->unique_buff)
@@ -1172,7 +1255,7 @@ int merge_buffers(SORTPARAM *param, IO_CACHE *from_file,
*/
buffpek= (BUFFPEK*) queue_top(&queue);
memcpy(param->unique_buff, buffpek->key, rec_length);
- if (my_b_write(to_file, (byte*) buffpek->key, rec_length))
+ if (my_b_write(to_file, (uchar*) buffpek->key, rec_length))
{
error=1; goto err; /* purecov: inspected */
}
@@ -1206,14 +1289,14 @@ int merge_buffers(SORTPARAM *param, IO_CACHE *from_file,
}
if (flag == 0)
{
- if (my_b_write(to_file,(byte*) buffpek->key, rec_length))
+ if (my_b_write(to_file,(uchar*) buffpek->key, rec_length))
{
error=1; goto err; /* purecov: inspected */
}
}
else
{
- if (my_b_write(to_file, (byte*) buffpek->key+offset, res_length))
+ if (my_b_write(to_file, (uchar*) buffpek->key+offset, res_length))
{
error=1; goto err; /* purecov: inspected */
}
@@ -1268,7 +1351,7 @@ int merge_buffers(SORTPARAM *param, IO_CACHE *from_file,
max_rows-= buffpek->mem_count;
if (flag == 0)
{
- if (my_b_write(to_file,(byte*) buffpek->key,
+ if (my_b_write(to_file,(uchar*) buffpek->key,
(rec_length*buffpek->mem_count)))
{
error= 1; goto err; /* purecov: inspected */
@@ -1282,7 +1365,7 @@ int merge_buffers(SORTPARAM *param, IO_CACHE *from_file,
strpos != end ;
strpos+= rec_length)
{
- if (my_b_write(to_file, (byte *) strpos, res_length))
+ if (my_b_write(to_file, (uchar *) strpos, res_length))
{
error=1; goto err;
}
@@ -1328,23 +1411,21 @@ static uint suffix_length(ulong string_length)
-/*
- Calculate length of sort key
-
- SYNOPSIS
- sortlength()
- thd Thread handler
- sortorder Order of items to sort
- uint s_length Number of items to sort
- multi_byte_charset (out)
- Set to 1 if we are using multi-byte charset
- (In which case we have to use strxnfrm())
-
- NOTES
- sortorder->length is updated for each sort item
+/**
+ Calculate length of sort key.
+
+ @param thd Thread handler
+ @param sortorder Order of items to sort
+ @param s_length Number of items to sort
+ @param[out] multi_byte_charset Set to 1 if we are using multi-byte charset
+ (In which case we have to use strxnfrm())
+
+ @note
+ sortorder->length is updated for each sort item.
+ @n
sortorder->need_strxnfrm is set 1 if we have to use strxnfrm
- RETURN
+ @return
Total length of sort buffer in bytes
*/
@@ -1431,33 +1512,31 @@ sortlength(THD *thd, SORT_FIELD *sortorder, uint s_length,
}
-/*
+/**
Get descriptors of fields appended to sorted fields and
- calculate its total length
-
- SYNOPSIS
- get_addon_fields()
- thd Current thread
- ptabfields Array of references to the table fields
- sortlength Total length of sorted fields
- plength out: Total length of appended fields
-
- DESCRIPTION
- The function first finds out what fields are used in the result set.
- Then it calculates the length of the buffer to store the values of
- these fields together with the value of sort values.
- If the calculated length is not greater than max_length_for_sort_data
- the function allocates memory for an array of descriptors containing
- layouts for the values of the non-sorted fields in the buffer and
- fills them.
-
- NOTES
+ calculate its total length.
+
+ The function first finds out what fields are used in the result set.
+ Then it calculates the length of the buffer to store the values of
+ these fields together with the value of sort values.
+ If the calculated length is not greater than max_length_for_sort_data
+ the function allocates memory for an array of descriptors containing
+ layouts for the values of the non-sorted fields in the buffer and
+ fills them.
+
+ @param thd Current thread
+ @param ptabfield Array of references to the table fields
+ @param sortlength Total length of sorted fields
+ @param[out] plength Total length of appended fields
+
+ @note
The null bits for the appended values are supposed to be put together
and stored the buffer just ahead of the value of the first field.
- RETURN
+ @return
Pointer to the layout descriptors for the appended fields, if any
- NULL - if we do not store field values with sort data.
+ @retval
+ NULL if we do not store field values with sort data.
*/
static SORT_ADDON_FIELD *
@@ -1469,7 +1548,8 @@ get_addon_fields(THD *thd, Field **ptabfield, uint sortlength, uint *plength)
uint length= 0;
uint fields= 0;
uint null_fields= 0;
- query_id_t query_id= thd->query_id;
+ MY_BITMAP *read_set= (*ptabfield)->table->read_set;
+
/*
If there is a reference to a field in the query add it
to the the set of appended fields.
@@ -1481,18 +1561,9 @@ get_addon_fields(THD *thd, Field **ptabfield, uint sortlength, uint *plength)
*/
*plength= 0;
- /*
- The following statement is added to avoid sorting in alter_table.
- The fact is the filter 'field->query_id != thd->query_id'
- doesn't work for alter table
- */
- if (thd->lex->sql_command != SQLCOM_SELECT &&
- thd->lex->sql_command != SQLCOM_INSERT_SELECT &&
- thd->lex->sql_command != SQLCOM_CREATE_TABLE)
- return 0;
for (pfield= ptabfield; (field= *pfield) ; pfield++)
{
- if (field->query_id != query_id)
+ if (!bitmap_is_set(read_set, field->field_index))
continue;
if (field->flags & BLOB_FLAG)
return 0;
@@ -1515,7 +1586,7 @@ get_addon_fields(THD *thd, Field **ptabfield, uint sortlength, uint *plength)
null_fields= 0;
for (pfield= ptabfield; (field= *pfield) ; pfield++)
{
- if (field->query_id != thd->query_id)
+ if (!bitmap_is_set(read_set, field->field_index))
continue;
addonf->field= field;
addonf->offset= length;
@@ -1541,25 +1612,23 @@ get_addon_fields(THD *thd, Field **ptabfield, uint sortlength, uint *plength)
}
-/*
- Copy (unpack) values appended to sorted fields from a buffer back to
+/**
+ Copy (unpack) values appended to sorted fields from a buffer back to
their regular positions specified by the Field::ptr pointers.
- SYNOPSIS
- unpack_addon_fields()
- addon_field Array of descriptors for appended fields
- buff Buffer which to unpack the value from
+ @param addon_field Array of descriptors for appended fields
+ @param buff Buffer which to unpack the value from
- NOTES
+ @note
The function is supposed to be used only as a callback function
when getting field values for the sorted result set.
- RETURN
+ @return
void.
*/
static void
-unpack_addon_fields(struct st_sort_addon_field *addon_field, byte *buff)
+unpack_addon_fields(struct st_sort_addon_field *addon_field, uchar *buff)
{
Field *field;
SORT_ADDON_FIELD *addonf= addon_field;
@@ -1572,7 +1641,7 @@ unpack_addon_fields(struct st_sort_addon_field *addon_field, byte *buff)
continue;
}
field->set_notnull();
- field->unpack(field->ptr, (char *) buff+addonf->offset);
+ field->unpack(field->ptr, buff + addonf->offset);
}
}
@@ -1583,7 +1652,7 @@ unpack_addon_fields(struct st_sort_addon_field *addon_field, byte *buff)
#define DBL_EXP_DIG (sizeof(double)*8-DBL_MANT_DIG)
-void change_double_for_sort(double nr,byte *to)
+void change_double_for_sort(double nr,uchar *to)
{
uchar *tmp=(uchar*) to;
if (nr == 0.0)
diff --git a/sql/gen_lex_hash.cc b/sql/gen_lex_hash.cc
index ef671ac7f9d..5a0904f87b9 100644
--- a/sql/gen_lex_hash.cc
+++ b/sql/gen_lex_hash.cc
@@ -13,8 +13,11 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
-/*
+/**
+ @file
+ @details
+@verbatim
The idea of presented algorithm see in
"The Art of Computer Programming" by Donald E. Knuth
Volume 3 "Sorting and searching"
@@ -63,12 +66,14 @@ for optimization, link is the 16-bit index in 'symbols' or 'sql_functions'
or search-array..
So, we can read full search-structure as 32-bit word
+@endverbatim
+
+@todo
+ use instead to_upper_lex, special array
+ (substitute chars) without skip codes..
+@todo
+ try use reverse order of comparing..
-TODO:
-1. use instead to_upper_lex, special array
- (substitute chars) without skip codes..
-2. try use reverse order of comparing..
-
*/
#define NO_YACC_SYMBOLS
@@ -90,8 +95,8 @@ struct my_option my_long_options[] =
{"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", (gptr*) &default_dbug_option,
- (gptr*) &default_dbug_option, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0},
+ {"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},
@@ -446,7 +451,7 @@ int main(int argc,char **argv)
printf("/*\n\n Do " "not " "edit " "this " "file " "directly!\n\n*/\n");
printf("\
-/* Copyright (C) 2001-2004 MySQL AB\n\
+/* Copyright 2001-2008 MySQL AB, 2008 Sun Microsystems, Inc.\n\
\n\
This program is free software; you can redistribute it and/or modify\n\
it under the terms of the GNU General Public License as published by\n\
@@ -481,8 +486,8 @@ int main(int argc,char **argv)
printf("\nstatic unsigned int symbols_max_len=%d;\n\n", max_len2);
printf("\
-static inline SYMBOL *get_hash_symbol(const char *s,\n\
- unsigned int len,bool function)\n\
+static SYMBOL *get_hash_symbol(const char *s,\n\
+ unsigned int len,bool function)\n\
{\n\
register uchar *hash_map;\n\
register const char *cur_str= s;\n\
diff --git a/sql/gstream.h b/sql/gstream.h
index 10274635413..1ef90ad5bf0 100644
--- a/sql/gstream.h
+++ b/sql/gstream.h
@@ -35,7 +35,7 @@ public:
{}
~Gis_read_stream()
{
- my_free(m_err_msg, MYF(MY_ALLOW_ZERO_PTR));
+ my_free((uchar*) m_err_msg, MYF(MY_ALLOW_ZERO_PTR));
}
enum enum_tok_types get_next_toc_type();
diff --git a/sql/ha_archive.cc b/sql/ha_archive.cc
deleted file mode 100644
index 04dbe678d4d..00000000000
--- a/sql/ha_archive.cc
+++ /dev/null
@@ -1,1249 +0,0 @@
-/* Copyright (C) 2003 MySQL AB
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; version 2 of the License.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
-
-#ifdef USE_PRAGMA_IMPLEMENTATION
-#pragma implementation // gcc: Class implementation
-#endif
-
-#include "mysql_priv.h"
-
-#if defined(HAVE_ARCHIVE_DB)
-#include "ha_archive.h"
-#include <my_dir.h>
-
-/*
- First, if you want to understand storage engines you should look at
- ha_example.cc and ha_example.h.
- This example was written as a test case for a customer who needed
- a storage engine without indexes that could compress data very well.
- So, welcome to a completely compressed storage engine. This storage
- engine only does inserts. No replace, deletes, or updates. All reads are
- complete table scans. Compression is done through gzip (bzip compresses
- better, but only marginally, if someone asks I could add support for
- it too, but beaware that it costs a lot more in CPU time then gzip).
-
- We keep a file pointer open for each instance of ha_archive for each read
- but for writes we keep one open file handle just for that. We flush it
- only if we have a read occur. gzip handles compressing lots of records
- at once much better then doing lots of little records between writes.
- It is possible to not lock on writes but this would then mean we couldn't
- handle bulk inserts as well (that is if someone was trying to read at
- the same time since we would want to flush).
-
- A "meta" file is kept alongside the data file. This file serves two purpose.
- The first purpose is to track the number of rows in the table. The second
- purpose is to determine if the table was closed properly or not. When the
- meta file is first opened it is marked as dirty. It is opened when the table
- itself is opened for writing. When the table is closed the new count for rows
- is written to the meta file and the file is marked as clean. If the meta file
- is opened and it is marked as dirty, it is assumed that a crash occured. At
- this point an error occurs and the user is told to rebuild the file.
- A rebuild scans the rows and rewrites the meta file. If corruption is found
- in the data file then the meta file is not repaired.
-
- At some point a recovery method for such a drastic case needs to be divised.
-
- Locks are row level, and you will get a consistant read.
-
- For performance as far as table scans go it is quite fast. I don't have
- good numbers but locally it has out performed both Innodb and MyISAM. For
- Innodb the question will be if the table can be fit into the buffer
- pool. For MyISAM its a question of how much the file system caches the
- MyISAM file. With enough free memory MyISAM is faster. Its only when the OS
- doesn't have enough memory to cache entire table that archive turns out
- to be any faster. For writes it is always a bit slower then MyISAM. It has no
- internal limits though for row length.
-
- Examples between MyISAM (packed) and Archive.
-
- Table with 76695844 identical rows:
- 29680807 a_archive.ARZ
- 920350317 a.MYD
-
-
- Table with 8991478 rows (all of Slashdot's comments):
- 1922964506 comment_archive.ARZ
- 2944970297 comment_text.MYD
-
-
- TODO:
- Add bzip optional support.
- Allow users to set compression level.
- Add truncate table command.
- Implement versioning, should be easy.
- Allow for errors, find a way to mark bad rows.
- Talk to the gzip guys, come up with a writable format so that updates are doable
- without switching to a block method.
- Add optional feature so that rows can be flushed at interval (which will cause less
- compression but may speed up ordered searches).
- Checkpoint the meta file to allow for faster rebuilds.
- Dirty open (right now the meta file is repaired if a crash occured).
- Option to allow for dirty reads, this would lower the sync calls, which would make
- inserts a lot faster, but would mean highly arbitrary reads.
-
- -Brian
-*/
-/*
- Notes on file formats.
- The Meta file is layed out as:
- check - Just an int of 254 to make sure that the the file we are opening was
- never corrupted.
- version - The current version of the file format.
- rows - This is an unsigned long long which is the number of rows in the data
- file.
- check point - Reserved for future use
- dirty - Status of the file, whether or not its values are the latest. This
- flag is what causes a repair to occur
-
- The data file:
- check - Just an int of 254 to make sure that the the file we are opening was
- never corrupted.
- version - The current version of the file format.
- data - The data is stored in a "row +blobs" format.
-*/
-
-/* If the archive storage engine has been inited */
-static bool archive_inited= FALSE;
-/* Variables for archive share methods */
-pthread_mutex_t archive_mutex;
-static HASH archive_open_tables;
-static z_off_t max_zfile_size;
-static int zoffset_size;
-
-/* The file extension */
-#define ARZ ".ARZ" // The data file
-#define ARN ".ARN" // Files used during an optimize call
-#define ARM ".ARM" // Meta file
-/*
- uchar + uchar + ulonglong + ulonglong + uchar
-*/
-#define META_BUFFER_SIZE 19 // Size of the data used in the meta file
-/*
- uchar + uchar
-*/
-#define DATA_BUFFER_SIZE 2 // Size of the data used in the data file
-#define ARCHIVE_CHECK_HEADER 254 // The number we use to determine corruption
-
-/*
- Number of rows that will force a bulk insert.
-*/
-#define ARCHIVE_MIN_ROWS_TO_USE_BULK_INSERT 2
-
-
-
-/* dummy handlerton - only to have something to return from archive_db_init */
-handlerton archive_hton = {
- "ARCHIVE",
- SHOW_OPTION_YES,
- "Archive storage engine",
- DB_TYPE_ARCHIVE_DB,
- archive_db_init,
- 0, /* slot */
- 0, /* savepoint size. */
- NULL, /* close_connection */
- NULL, /* savepoint */
- NULL, /* rollback to savepoint */
- NULL, /* releas savepoint */
- NULL, /* commit */
- NULL, /* rollback */
- NULL, /* prepare */
- NULL, /* recover */
- NULL, /* commit_by_xid */
- NULL, /* rollback_by_xid */
- NULL, /* create_cursor_read_view */
- NULL, /* set_cursor_read_view */
- NULL, /* close_cursor_read_view */
- HTON_NO_FLAGS
-};
-
-
-/*
- Used for hash table that tracks open tables.
-*/
-static byte* archive_get_key(ARCHIVE_SHARE *share,uint *length,
- my_bool not_used __attribute__((unused)))
-{
- *length=share->table_name_length;
- return (byte*) share->table_name;
-}
-
-
-/*
- Initialize the archive handler.
-
- SYNOPSIS
- archive_db_init()
- void
-
- RETURN
- FALSE OK
- TRUE Error
-*/
-
-bool archive_db_init()
-{
- DBUG_ENTER("archive_db_init");
- if (pthread_mutex_init(&archive_mutex, MY_MUTEX_INIT_FAST))
- goto error;
- if (hash_init(&archive_open_tables, system_charset_info, 32, 0, 0,
- (hash_get_key) archive_get_key, 0, 0))
- {
- VOID(pthread_mutex_destroy(&archive_mutex));
- }
- else
- {
- zoffset_size= 2 << ((zlibCompileFlags() >> 6) & 3);
- switch (zoffset_size) {
- case 2:
- max_zfile_size= INT_MAX16;
- break;
- case 8:
- max_zfile_size= (z_off_t) LONGLONG_MAX;
- break;
- case 4:
- default:
- max_zfile_size= INT_MAX32;
- }
- archive_inited= TRUE;
- DBUG_RETURN(FALSE);
- }
-error:
- have_archive_db= SHOW_OPTION_DISABLED; // If we couldn't use handler
- DBUG_RETURN(TRUE);
-}
-
-/*
- Release the archive handler.
-
- SYNOPSIS
- archive_db_end()
- void
-
- RETURN
- FALSE OK
-*/
-
-bool archive_db_end()
-{
- if (archive_inited)
- {
- hash_free(&archive_open_tables);
- VOID(pthread_mutex_destroy(&archive_mutex));
- }
- archive_inited= 0;
- return FALSE;
-}
-
-ha_archive::ha_archive(TABLE *table_arg)
- :handler(&archive_hton, table_arg), delayed_insert(0), bulk_insert(0)
-{
- /* Set our original buffer from pre-allocated memory */
- buffer.set((char *)byte_buffer, IO_SIZE, system_charset_info);
-
- /* The size of the offset value we will use for position() */
- ref_length = zoffset_size;
- DBUG_ASSERT(ref_length <= sizeof(z_off_t));
-}
-
-/*
- This method reads the header of a datafile and returns whether or not it was successful.
-*/
-int ha_archive::read_data_header(gzFile file_to_read)
-{
- uchar data_buffer[DATA_BUFFER_SIZE];
- DBUG_ENTER("ha_archive::read_data_header");
-
- if (gzrewind(file_to_read) == -1)
- DBUG_RETURN(HA_ERR_CRASHED_ON_USAGE);
-
- if (gzread(file_to_read, data_buffer, DATA_BUFFER_SIZE) != DATA_BUFFER_SIZE)
- DBUG_RETURN(errno ? errno : -1);
-
- DBUG_PRINT("ha_archive::read_data_header", ("Check %u", data_buffer[0]));
- DBUG_PRINT("ha_archive::read_data_header", ("Version %u", data_buffer[1]));
-
- if ((data_buffer[0] != (uchar)ARCHIVE_CHECK_HEADER) &&
- (data_buffer[1] != (uchar)ARCHIVE_VERSION))
- DBUG_RETURN(HA_ERR_CRASHED_ON_USAGE);
-
- DBUG_RETURN(0);
-}
-
-/*
- This method writes out the header of a datafile and returns whether or not it was successful.
-*/
-int ha_archive::write_data_header(gzFile file_to_write)
-{
- uchar data_buffer[DATA_BUFFER_SIZE];
- DBUG_ENTER("ha_archive::write_data_header");
-
- data_buffer[0]= (uchar)ARCHIVE_CHECK_HEADER;
- data_buffer[1]= (uchar)ARCHIVE_VERSION;
-
- if (gzwrite(file_to_write, &data_buffer, DATA_BUFFER_SIZE) !=
- DATA_BUFFER_SIZE)
- goto error;
- DBUG_PRINT("ha_archive::write_data_header", ("Check %u", (uint)data_buffer[0]));
- DBUG_PRINT("ha_archive::write_data_header", ("Version %u", (uint)data_buffer[1]));
-
- DBUG_RETURN(0);
-error:
- DBUG_RETURN(errno);
-}
-
-/*
- This method reads the header of a meta file and returns whether or not it was successful.
- *rows will contain the current number of rows in the data file upon success.
-*/
-int ha_archive::read_meta_file(File meta_file, ha_rows *rows)
-{
- uchar meta_buffer[META_BUFFER_SIZE];
- ulonglong check_point;
-
- DBUG_ENTER("ha_archive::read_meta_file");
-
- VOID(my_seek(meta_file, 0, MY_SEEK_SET, MYF(0)));
- if (my_read(meta_file, (byte*)meta_buffer, META_BUFFER_SIZE, 0) != META_BUFFER_SIZE)
- DBUG_RETURN(-1);
-
- /*
- Parse out the meta data, we ignore version at the moment
- */
- *rows= (ha_rows)uint8korr(meta_buffer + 2);
- check_point= uint8korr(meta_buffer + 10);
-
- DBUG_PRINT("ha_archive::read_meta_file", ("Check %d", (uint)meta_buffer[0]));
- DBUG_PRINT("ha_archive::read_meta_file", ("Version %d", (uint)meta_buffer[1]));
- DBUG_PRINT("ha_archive::read_meta_file", ("Rows %lu", (ulong) *rows));
- DBUG_PRINT("ha_archive::read_meta_file", ("Checkpoint %lu", (ulong) check_point));
- DBUG_PRINT("ha_archive::read_meta_file", ("Dirty %d", (int)meta_buffer[18]));
-
- if ((meta_buffer[0] != (uchar)ARCHIVE_CHECK_HEADER) ||
- ((bool)meta_buffer[18] == TRUE))
- DBUG_RETURN(HA_ERR_CRASHED_ON_USAGE);
-
- my_sync(meta_file, MYF(MY_WME));
-
- DBUG_RETURN(0);
-}
-
-/*
- This method writes out the header of a meta file and returns whether or not it was successful.
- By setting dirty you say whether or not the file represents the actual state of the data file.
- Upon ::open() we set to dirty, and upon ::close() we set to clean.
-*/
-int ha_archive::write_meta_file(File meta_file, ha_rows rows, bool dirty)
-{
- uchar meta_buffer[META_BUFFER_SIZE];
- ulonglong check_point= 0; //Reserved for the future
-
- DBUG_ENTER("ha_archive::write_meta_file");
-
- meta_buffer[0]= (uchar)ARCHIVE_CHECK_HEADER;
- meta_buffer[1]= (uchar)ARCHIVE_VERSION;
- int8store(meta_buffer + 2, (ulonglong)rows);
- int8store(meta_buffer + 10, check_point);
- *(meta_buffer + 18)= (uchar)dirty;
- DBUG_PRINT("ha_archive::write_meta_file", ("Check %d", (uint)ARCHIVE_CHECK_HEADER));
- DBUG_PRINT("ha_archive::write_meta_file", ("Version %d", (uint)ARCHIVE_VERSION));
- DBUG_PRINT("ha_archive::write_meta_file", ("Rows %lu", (ulong)rows));
- DBUG_PRINT("ha_archive::write_meta_file", ("Checkpoint %lu", (ulong) check_point));
- DBUG_PRINT("ha_archive::write_meta_file", ("Dirty %d", (uint)dirty));
-
- VOID(my_seek(meta_file, 0, MY_SEEK_SET, MYF(0)));
- if (my_write(meta_file, (byte *)meta_buffer, META_BUFFER_SIZE, 0) != META_BUFFER_SIZE)
- DBUG_RETURN(-1);
-
- my_sync(meta_file, MYF(MY_WME));
-
- DBUG_RETURN(0);
-}
-
-
-/*
- We create the shared memory space that we will use for the open table.
- No matter what we try to get or create a share. This is so that a repair
- table operation can occur.
-
- See ha_example.cc for a longer description.
-*/
-ARCHIVE_SHARE *ha_archive::get_share(const char *table_name, int *rc)
-{
- char meta_file_name[FN_REFLEN];
- uint length;
- char *tmp_name;
- DBUG_ENTER("ha_archive::get_share");
-
- pthread_mutex_lock(&archive_mutex);
- length=(uint) strlen(table_name);
-
- if (!(share=(ARCHIVE_SHARE*) hash_search(&archive_open_tables,
- (byte*) table_name,
- length)))
- {
- if (!my_multi_malloc(MYF(MY_WME | MY_ZEROFILL),
- &share, sizeof(*share),
- &tmp_name, length+1,
- NullS))
- {
- pthread_mutex_unlock(&archive_mutex);
- *rc= HA_ERR_OUT_OF_MEM;
- DBUG_RETURN(NULL);
- }
-
- share->use_count= 0;
- share->table_name_length= length;
- share->table_name= tmp_name;
- share->crashed= FALSE;
- share->archive_write_open= FALSE;
- fn_format(share->data_file_name,table_name,"",ARZ,
- MY_REPLACE_EXT|MY_UNPACK_FILENAME);
- fn_format(meta_file_name,table_name,"",ARM,
- MY_REPLACE_EXT|MY_UNPACK_FILENAME);
- strmov(share->table_name,table_name);
- /*
- We will use this lock for rows.
- */
- VOID(pthread_mutex_init(&share->mutex,MY_MUTEX_INIT_FAST));
- if ((share->meta_file= my_open(meta_file_name, O_RDWR, MYF(0))) == -1)
- share->crashed= TRUE;
-
- /*
- After we read, we set the file to dirty. When we close, we will do the
- opposite. If the meta file will not open we assume it is crashed and
- leave it up to the user to fix.
- */
- if (read_meta_file(share->meta_file, &share->rows_recorded))
- share->crashed= TRUE;
-
- VOID(my_hash_insert(&archive_open_tables, (byte*) share));
- thr_lock_init(&share->lock);
- }
- share->use_count++;
- DBUG_PRINT("info", ("archive table %.*s has %d open handles now",
- share->table_name_length, share->table_name,
- share->use_count));
- if (share->crashed)
- *rc= HA_ERR_CRASHED_ON_USAGE;
- pthread_mutex_unlock(&archive_mutex);
-
- DBUG_RETURN(share);
-}
-
-
-/*
- Free the share.
- See ha_example.cc for a description.
-*/
-int ha_archive::free_share()
-{
- int rc= 0;
- DBUG_ENTER("ha_archive::free_share");
- DBUG_PRINT("info", ("archive table %.*s has %d open handles on entrance",
- share->table_name_length, share->table_name,
- share->use_count));
-
- pthread_mutex_lock(&archive_mutex);
- if (!--share->use_count)
- {
- hash_delete(&archive_open_tables, (byte*) share);
- thr_lock_delete(&share->lock);
- VOID(pthread_mutex_destroy(&share->mutex));
- if (share->crashed)
- (void)write_meta_file(share->meta_file, share->rows_recorded, TRUE);
- else
- (void)write_meta_file(share->meta_file, share->rows_recorded, FALSE);
- if (share->archive_write_open)
- if (gzclose(share->archive_write) == Z_ERRNO)
- rc= 1;
- if (my_close(share->meta_file, MYF(0)))
- rc= 1;
- my_free((gptr) share, MYF(0));
- }
- pthread_mutex_unlock(&archive_mutex);
-
- DBUG_RETURN(rc);
-}
-
-int ha_archive::init_archive_writer()
-{
- DBUG_ENTER("ha_archive::init_archive_writer");
- (void)write_meta_file(share->meta_file, share->rows_recorded, TRUE);
-
- /*
- It is expensive to open and close the data files and since you can't have
- a gzip file that can be both read and written we keep a writer open
- that is shared amoung all open tables.
- */
- if ((share->archive_write= gzopen(share->data_file_name, "ab")) == NULL)
- {
- share->crashed= TRUE;
- DBUG_RETURN(1);
- }
- share->archive_write_open= TRUE;
- info(HA_STATUS_TIME);
- share->approx_file_size= (ulong) data_file_length;
- DBUG_RETURN(0);
-}
-
-
-/*
- We just implement one additional file extension.
-*/
-static const char *ha_archive_exts[] = {
- ARM,
- ARZ,
- NullS
-};
-
-const char **ha_archive::bas_ext() const
-{
- return ha_archive_exts;
-}
-
-
-/*
- When opening a file we:
- Create/get our shared structure.
- Init out lock.
- We open the file we will read from.
-*/
-int ha_archive::open(const char *name, int mode, uint open_options)
-{
- int rc= 0;
- DBUG_ENTER("ha_archive::open");
-
- DBUG_PRINT("info", ("archive table was opened for crash %s",
- (open_options & HA_OPEN_FOR_REPAIR) ? "yes" : "no"));
- share= get_share(name, &rc);
-
- if (rc == HA_ERR_CRASHED_ON_USAGE && !(open_options & HA_OPEN_FOR_REPAIR))
- {
- /* purecov: begin inspected */
- free_share();
- DBUG_RETURN(rc);
- /* purecov: end */
- }
- else if (rc == HA_ERR_OUT_OF_MEM)
- {
- DBUG_RETURN(rc);
- }
-
- thr_lock_data_init(&share->lock,&lock,NULL);
-
- if ((archive= gzopen(share->data_file_name, "rb")) == NULL)
- {
- if (errno == EROFS || errno == EACCES)
- DBUG_RETURN(my_errno= errno);
- DBUG_RETURN(HA_ERR_CRASHED_ON_USAGE);
- }
-
- DBUG_PRINT("info", ("archive table was crashed %s",
- rc == HA_ERR_CRASHED_ON_USAGE ? "yes" : "no"));
- if (rc == HA_ERR_CRASHED_ON_USAGE && open_options & HA_OPEN_FOR_REPAIR)
- {
- DBUG_RETURN(0);
- }
- else
- DBUG_RETURN(rc);
-}
-
-
-/*
- Closes the file.
-
- SYNOPSIS
- close();
-
- IMPLEMENTATION:
-
- We first close this storage engines file handle to the archive and
- then remove our reference count to the table (and possibly free it
- as well).
-
- RETURN
- 0 ok
- 1 Error
-*/
-
-int ha_archive::close(void)
-{
- int rc= 0;
- DBUG_ENTER("ha_archive::close");
-
- /* First close stream */
- if (gzclose(archive) == Z_ERRNO)
- rc= 1;
- /* then also close share */
- rc|= free_share();
-
- DBUG_RETURN(rc);
-}
-
-
-/*
- We create our data file here. The format is pretty simple.
- You can read about the format of the data file above.
- Unlike other storage engines we do not "pack" our data. Since we
- are about to do a general compression, packing would just be a waste of
- CPU time. If the table has blobs they are written after the row in the order
- of creation.
-*/
-
-int ha_archive::create(const char *name, TABLE *table_arg,
- HA_CREATE_INFO *create_info)
-{
- File create_file; // We use to create the datafile and the metafile
- char name_buff[FN_REFLEN];
- int error;
- DBUG_ENTER("ha_archive::create");
-
- if ((create_file= my_create(fn_format(name_buff,name,"",ARM,
- MY_REPLACE_EXT|MY_UNPACK_FILENAME),0,
- O_RDWR | O_TRUNC,MYF(MY_WME))) < 0)
- {
- error= my_errno;
- goto error;
- }
- write_meta_file(create_file, 0, FALSE);
- my_close(create_file,MYF(0));
-
- /*
- We reuse name_buff since it is available.
- */
- if ((create_file= my_create(fn_format(name_buff,name,"",ARZ,
- MY_REPLACE_EXT|MY_UNPACK_FILENAME),0,
- O_RDWR | O_TRUNC,MYF(MY_WME))) < 0)
- {
- error= my_errno;
- goto error;
- }
- if ((archive= gzdopen(dup(create_file), "wb")) == NULL)
- {
- error= errno;
- goto error2;
- }
- if (write_data_header(archive))
- {
- error= errno;
- goto error3;
- }
-
- if (gzclose(archive))
- {
- error= errno;
- goto error2;
- }
-
- my_close(create_file, MYF(0));
-
- DBUG_RETURN(0);
-
-error3:
- /* We already have an error, so ignore results of gzclose. */
- (void)gzclose(archive);
-error2:
- my_close(create_file, MYF(0));
- delete_table(name);
-error:
- /* Return error number, if we got one */
- DBUG_RETURN(error ? error : -1);
-}
-
-/*
- This is where the actual row is written out.
-*/
-int ha_archive::real_write_row(byte *buf, gzFile writer)
-{
- z_off_t written, total_row_length;
- uint *ptr, *end;
- DBUG_ENTER("ha_archive::real_write_row");
- total_row_length= table->s->reclength;
- for (ptr= table->s->blob_field, end= ptr + table->s->blob_fields;
- ptr != end; ptr++)
- total_row_length+= ((Field_blob*) table->field[*ptr])->get_length();
- if (share->approx_file_size > max_zfile_size - total_row_length)
- {
- gzflush(writer, Z_SYNC_FLUSH);
- info(HA_STATUS_TIME);
- share->approx_file_size= (ulong) data_file_length;
- if (share->approx_file_size > max_zfile_size - total_row_length)
- DBUG_RETURN(HA_ERR_RECORD_FILE_FULL);
- }
- share->approx_file_size+= total_row_length;
- written= gzwrite(writer, buf, table->s->reclength);
- DBUG_PRINT("ha_archive::real_write_row", ("Wrote %d bytes expected %lu", (int) written,
- table->s->reclength));
- if (!delayed_insert || !bulk_insert)
- share->dirty= TRUE;
-
- if (written != (z_off_t)table->s->reclength)
- DBUG_RETURN(errno ? errno : -1);
- /*
- We should probably mark the table as damagaged if the record is written
- but the blob fails.
- */
- for (ptr= table->s->blob_field, end= ptr + table->s->blob_fields ;
- ptr != end ;
- ptr++)
- {
- char *data_ptr;
- uint32 size= ((Field_blob*) table->field[*ptr])->get_length();
-
- if (size)
- {
- ((Field_blob*) table->field[*ptr])->get_ptr(&data_ptr);
- written= gzwrite(writer, data_ptr, (unsigned)size);
- if (written != (z_off_t)size)
- DBUG_RETURN(errno ? errno : -1);
- }
- }
- DBUG_RETURN(0);
-}
-
-
-/*
- Look at ha_archive::open() for an explanation of the row format.
- Here we just write out the row.
-
- Wondering about start_bulk_insert()? We don't implement it for
- archive since it optimizes for lots of writes. The only save
- for implementing start_bulk_insert() is that we could skip
- setting dirty to true each time.
-*/
-int ha_archive::write_row(byte *buf)
-{
- int rc;
- DBUG_ENTER("ha_archive::write_row");
-
- if (share->crashed)
- DBUG_RETURN(HA_ERR_CRASHED_ON_USAGE);
-
- statistic_increment(table->in_use->status_var.ha_write_count, &LOCK_status);
- if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_INSERT)
- table->timestamp_field->set_time();
- pthread_mutex_lock(&share->mutex);
- if (!share->archive_write_open)
- if (init_archive_writer())
- DBUG_RETURN(HA_ERR_CRASHED_ON_USAGE);
-
- /*
- Varchar structures are constant in size but are not cleaned up request
- to request. The following sets all unused space to null to improve
- compression.
- */
- for (Field **field=table->field ; *field ; field++)
- {
- DBUG_PRINT("archive",("Pack is %d\n", (*field)->pack_length()));
- DBUG_PRINT("archive",("MyPack is %d\n", (*field)->data_length((char*) buf + (*field)->offset())));
- if ((*field)->real_type() == MYSQL_TYPE_VARCHAR)
- {
-#ifndef DBUG_OFF
- uint actual_length= (*field)->data_length((char*) buf + (*field)->offset());
- uint offset= (*field)->offset() + actual_length +
- (actual_length > 255 ? 2 : 1);
- DBUG_PRINT("archive",("Offset is %d -> %d\n", actual_length, offset));
-#endif
- /*
- if ((*field)->pack_length() + (*field)->offset() != offset)
- bzero(buf + offset, (size_t)((*field)->pack_length() + (actual_length > 255 ? 2 : 1) - (*field)->data_length));
- */
- }
- }
-
- share->rows_recorded++;
- rc= real_write_row(buf, share->archive_write);
- pthread_mutex_unlock(&share->mutex);
-
- DBUG_RETURN(rc);
-}
-
-/*
- All calls that need to scan the table start with this method. If we are told
- that it is a table scan we rewind the file to the beginning, otherwise
- we assume the position will be set.
-*/
-
-int ha_archive::rnd_init(bool scan)
-{
- DBUG_ENTER("ha_archive::rnd_init");
-
- if (share->crashed)
- DBUG_RETURN(HA_ERR_CRASHED_ON_USAGE);
-
- /* We rewind the file so that we can read from the beginning if scan */
- if (scan)
- {
- scan_rows= share->rows_recorded;
- DBUG_PRINT("info", ("archive will retrieve %lu rows", (ulong) scan_rows));
- records= 0;
-
- /*
- If dirty, we lock, and then reset/flush the data.
- I found that just calling gzflush() doesn't always work.
- */
- if (share->dirty == TRUE)
- {
- pthread_mutex_lock(&share->mutex);
- if (share->dirty == TRUE)
- {
- DBUG_PRINT("info", ("archive flushing out rows for scan"));
- gzflush(share->archive_write, Z_SYNC_FLUSH);
- share->dirty= FALSE;
- }
- pthread_mutex_unlock(&share->mutex);
- }
-
- if (read_data_header(archive))
- DBUG_RETURN(HA_ERR_CRASHED_ON_USAGE);
- }
-
- DBUG_RETURN(0);
-}
-
-
-/*
- This is the method that is used to read a row. It assumes that the row is
- positioned where you want it.
-*/
-int ha_archive::get_row(gzFile file_to_read, byte *buf)
-{
- int read; // Bytes read, gzread() returns int
- uint *ptr, *end;
- char *last;
- size_t total_blob_length= 0;
- DBUG_ENTER("ha_archive::get_row");
-
- read= gzread(file_to_read, buf, table->s->reclength);
- DBUG_PRINT("ha_archive::get_row", ("Read %d bytes expected %lu", (int) read,
- table->s->reclength));
-
- if (read == Z_STREAM_ERROR)
- DBUG_RETURN(HA_ERR_CRASHED_ON_USAGE);
-
- /* If we read nothing we are at the end of the file */
- if (read == 0)
- DBUG_RETURN(HA_ERR_END_OF_FILE);
-
- /*
- If the record is the wrong size, the file is probably damaged, unless
- we are dealing with a delayed insert or a bulk insert.
- */
- if ((ulong) read != table->s->reclength)
- DBUG_RETURN(HA_ERR_END_OF_FILE);
-
- /* Calculate blob length, we use this for our buffer */
- for (ptr= table->s->blob_field, end=ptr + table->s->blob_fields ;
- ptr != end ;
- ptr++)
- total_blob_length += ((Field_blob*) table->field[*ptr])->get_length();
-
- /* Adjust our row buffer if we need be */
- buffer.alloc((uint) total_blob_length);
- last= (char *)buffer.ptr();
-
- /* Loop through our blobs and read them */
- for (ptr= table->s->blob_field, end=ptr + table->s->blob_fields ;
- ptr != end ;
- ptr++)
- {
- size_t size= ((Field_blob*) table->field[*ptr])->get_length();
- if (size)
- {
- read= gzread(file_to_read, last, (uint) size);
- if ((size_t) read != size)
- DBUG_RETURN(HA_ERR_END_OF_FILE);
- ((Field_blob*) table->field[*ptr])->set_ptr((uint) size, last);
- last += size;
- }
- }
- DBUG_RETURN(0);
-}
-
-
-/*
- Called during ORDER BY. Its position is either from being called sequentially
- or by having had ha_archive::rnd_pos() called before it is called.
-*/
-
-int ha_archive::rnd_next(byte *buf)
-{
- int rc;
- DBUG_ENTER("ha_archive::rnd_next");
-
- if (share->crashed)
- DBUG_RETURN(HA_ERR_CRASHED_ON_USAGE);
-
- if (!scan_rows)
- DBUG_RETURN(HA_ERR_END_OF_FILE);
- scan_rows--;
-
- statistic_increment(table->in_use->status_var.ha_read_rnd_next_count,
- &LOCK_status);
- current_position= gztell(archive);
- rc= get_row(archive, buf);
-
-
- if (rc != HA_ERR_END_OF_FILE)
- records++;
-
- DBUG_RETURN(rc);
-}
-
-
-/*
- Thanks to the table flag HA_REC_NOT_IN_SEQ this will be called after
- each call to ha_archive::rnd_next() if an ordering of the rows is
- needed.
-*/
-
-void ha_archive::position(const byte *record)
-{
- DBUG_ENTER("ha_archive::position");
- my_store_ptr(ref, ref_length, current_position);
- DBUG_VOID_RETURN;
-}
-
-
-/*
- This is called after a table scan for each row if the results of the
- scan need to be ordered. It will take *pos and use it to move the
- cursor in the file so that the next row that is called is the
- correctly ordered row.
-*/
-
-int ha_archive::rnd_pos(byte * buf, byte *pos)
-{
- DBUG_ENTER("ha_archive::rnd_pos");
- statistic_increment(table->in_use->status_var.ha_read_rnd_next_count,
- &LOCK_status);
- current_position= (z_off_t)my_get_ptr(pos, ref_length);
- (void)gzseek(archive, current_position, SEEK_SET);
-
- DBUG_RETURN(get_row(archive, buf));
-}
-
-/*
- This method repairs the meta file. It does this by walking the datafile and
- rewriting the meta file. Currently it does this by calling optimize with
- the extended flag.
-*/
-int ha_archive::repair(THD* thd, HA_CHECK_OPT* check_opt)
-{
- DBUG_ENTER("ha_archive::repair");
- check_opt->flags= T_EXTEND;
- int rc= optimize(thd, check_opt);
-
- if (rc)
- DBUG_RETURN(HA_ERR_CRASHED_ON_REPAIR);
-
- share->crashed= FALSE;
- DBUG_RETURN(0);
-}
-
-/*
- The table can become fragmented if data was inserted, read, and then
- inserted again. What we do is open up the file and recompress it completely.
-*/
-int ha_archive::optimize(THD* thd, HA_CHECK_OPT* check_opt)
-{
- DBUG_ENTER("ha_archive::optimize");
- int rc;
- gzFile writer;
- char writer_filename[FN_REFLEN];
-
- /* Open up the writer if we haven't yet */
- if (!share->archive_write_open)
- init_archive_writer();
-
- /* Flush any waiting data */
- gzflush(share->archive_write, Z_SYNC_FLUSH);
-
- /* Lets create a file to contain the new data */
- fn_format(writer_filename, share->table_name, "", ARN,
- MY_REPLACE_EXT|MY_UNPACK_FILENAME);
-
- if ((writer= gzopen(writer_filename, "wb")) == NULL)
- DBUG_RETURN(HA_ERR_CRASHED_ON_USAGE);
-
- /*
- An extended rebuild is a lot more effort. We open up each row and re-record it.
- Any dead rows are removed (aka rows that may have been partially recorded).
- */
-
- if (check_opt->flags == T_EXTEND)
- {
- byte *buf;
-
- /*
- First we create a buffer that we can use for reading rows, and can pass
- to get_row().
- */
- if (!(buf= (byte*) my_malloc(table->s->reclength, MYF(MY_WME))))
- {
- rc= HA_ERR_OUT_OF_MEM;
- goto error;
- }
-
- /*
- Now we will rewind the archive file so that we are positioned at the
- start of the file.
- */
- rc= read_data_header(archive);
-
- /*
- Assuming now error from rewinding the archive file, we now write out the
- new header for out data file.
- */
- if (!rc)
- rc= write_data_header(writer);
-
- /*
- On success of writing out the new header, we now fetch each row and
- insert it into the new archive file.
- */
- if (!rc)
- {
- share->rows_recorded= 0;
- while (!(rc= get_row(archive, buf)))
- {
- real_write_row(buf, writer);
- share->rows_recorded++;
- }
- }
- DBUG_PRINT("info", ("recovered %lu archive rows",
- (ulong) share->rows_recorded));
-
- my_free((char*)buf, MYF(0));
- if (rc && rc != HA_ERR_END_OF_FILE)
- goto error;
- }
- else
- {
- /*
- The quick method is to just read the data raw, and then compress it directly.
- */
- int read; // Bytes read, gzread() returns int
- char block[IO_SIZE];
- if (gzrewind(archive) == -1)
- {
- rc= HA_ERR_CRASHED_ON_USAGE;
- goto error;
- }
-
- while ((read= gzread(archive, block, IO_SIZE)))
- gzwrite(writer, block, read);
- }
-
- gzflush(writer, Z_SYNC_FLUSH);
- share->dirty= FALSE;
- gzclose(share->archive_write);
- share->archive_write= writer;
-
- my_rename(writer_filename,share->data_file_name,MYF(0));
-
- /*
- Now we need to reopen our read descriptor since it has changed.
- */
- gzclose(archive);
- if ((archive= gzopen(share->data_file_name, "rb")) == NULL)
- {
- rc= HA_ERR_CRASHED_ON_USAGE;
- goto error;
- }
-
-
- DBUG_RETURN(0);
-
-error:
- gzclose(writer);
-
- DBUG_RETURN(rc);
-}
-
-/*
- Below is an example of how to setup row level locking.
-*/
-THR_LOCK_DATA **ha_archive::store_lock(THD *thd,
- THR_LOCK_DATA **to,
- enum thr_lock_type lock_type)
-{
- if (lock_type == TL_WRITE_DELAYED)
- delayed_insert= TRUE;
- else
- delayed_insert= FALSE;
-
- if (lock_type != TL_IGNORE && lock.type == TL_UNLOCK)
- {
- /*
- Here is where we get into the guts of a row level lock.
- If TL_UNLOCK is set
- If we are not doing a LOCK TABLE or DISCARD/IMPORT
- TABLESPACE, then allow multiple writers
- */
-
- if ((lock_type >= TL_WRITE_CONCURRENT_INSERT &&
- lock_type <= TL_WRITE) && !thd->in_lock_tables
- && !thd->tablespace_op)
- lock_type = TL_WRITE_ALLOW_WRITE;
-
- /*
- In queries of type INSERT INTO t1 SELECT ... FROM t2 ...
- MySQL would use the lock TL_READ_NO_INSERT on t2, and that
- would conflict with TL_WRITE_ALLOW_WRITE, blocking all inserts
- to t2. Convert the lock to a normal read lock to allow
- concurrent inserts to t2.
- */
-
- if (lock_type == TL_READ_NO_INSERT && !thd->in_lock_tables)
- lock_type = TL_READ;
-
- lock.type=lock_type;
- }
-
- *to++= &lock;
-
- return to;
-}
-
-
-/*
- Hints for optimizer, see ha_tina for more information
-*/
-int ha_archive::info(uint flag)
-{
- DBUG_ENTER("ha_archive::info");
- /*
- This should be an accurate number now, though bulk and delayed inserts can
- cause the number to be inaccurate.
- */
- records= share->rows_recorded;
- deleted= 0;
- /* Costs quite a bit more to get all information */
- if (flag & HA_STATUS_TIME)
- {
- MY_STAT file_stat; // Stat information for the data file
-
- VOID(my_stat(share->data_file_name, &file_stat, MYF(MY_WME)));
-
- mean_rec_length= table->s->reclength + buffer.alloced_length();
- data_file_length= file_stat.st_size;
- create_time= file_stat.st_ctime;
- update_time= file_stat.st_mtime;
- max_data_file_length= share->rows_recorded * mean_rec_length;
- }
- delete_length= 0;
- index_file_length=0;
-
- DBUG_RETURN(0);
-}
-
-
-/*
- This method tells us that a bulk insert operation is about to occur. We set
- a flag which will keep write_row from saying that its data is dirty. This in
- turn will keep selects from causing a sync to occur.
- Basically, yet another optimizations to keep compression working well.
-*/
-void ha_archive::start_bulk_insert(ha_rows rows)
-{
- DBUG_ENTER("ha_archive::start_bulk_insert");
- if (!rows || rows >= ARCHIVE_MIN_ROWS_TO_USE_BULK_INSERT)
- bulk_insert= TRUE;
- DBUG_VOID_RETURN;
-}
-
-
-/*
- Other side of start_bulk_insert, is end_bulk_insert. Here we turn off the bulk insert
- flag, and set the share dirty so that the next select will call sync for us.
-*/
-int ha_archive::end_bulk_insert()
-{
- DBUG_ENTER("ha_archive::end_bulk_insert");
- bulk_insert= FALSE;
- share->dirty= TRUE;
- DBUG_RETURN(0);
-}
-
-/*
- We cancel a truncate command. The only way to delete an archive table is to drop it.
- This is done for security reasons. In a later version we will enable this by
- allowing the user to select a different row format.
-*/
-int ha_archive::delete_all_rows()
-{
- DBUG_ENTER("ha_archive::delete_all_rows");
- DBUG_RETURN(HA_ERR_WRONG_COMMAND);
-}
-
-/*
- We just return state if asked.
-*/
-bool ha_archive::is_crashed() const
-{
- DBUG_ENTER("ha_archive::is_crashed");
- DBUG_RETURN(share->crashed);
-}
-
-/*
- Simple scan of the tables to make sure everything is ok.
-*/
-
-int ha_archive::check(THD* thd, HA_CHECK_OPT* check_opt)
-{
- int rc= 0;
- const char *old_proc_info=thd->proc_info;
- ha_rows count= share->rows_recorded;
- DBUG_ENTER("ha_archive::check");
-
- thd->proc_info= "Checking table";
- /* Flush any waiting data */
- gzflush(share->archive_write, Z_SYNC_FLUSH);
-
- /*
- Now we will rewind the archive file so that we are positioned at the
- start of the file.
- */
- read_data_header(archive);
- while (!(rc= get_row(archive, table->record[0])))
- count--;
-
- thd->proc_info= old_proc_info;
-
- if ((rc && rc != HA_ERR_END_OF_FILE) || count)
- {
- share->crashed= FALSE;
- DBUG_RETURN(HA_ADMIN_CORRUPT);
- }
- else
- {
- DBUG_RETURN(HA_ADMIN_OK);
- }
-}
-
-/*
- Check and repair the table if needed.
-*/
-bool ha_archive::check_and_repair(THD *thd)
-{
- HA_CHECK_OPT check_opt;
- DBUG_ENTER("ha_archive::check_and_repair");
-
- check_opt.init();
-
- DBUG_RETURN(repair(thd, &check_opt));
-}
-#endif /* HAVE_ARCHIVE_DB */
diff --git a/sql/ha_archive.h b/sql/ha_archive.h
deleted file mode 100644
index 76765b98bc9..00000000000
--- a/sql/ha_archive.h
+++ /dev/null
@@ -1,115 +0,0 @@
-/* Copyright (C) 2003 MySQL AB
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; version 2 of the License.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
-
-#ifdef USE_PRAGMA_INTERFACE
-#pragma interface /* gcc class implementation */
-#endif
-
-#include <zlib.h>
-
-/*
- Please read ha_archive.cc first. If you are looking for more general
- answers on how storage engines work, look at ha_example.cc and
- ha_example.h.
-*/
-
-typedef struct st_archive_share {
- char *table_name;
- char data_file_name[FN_REFLEN];
- uint table_name_length,use_count;
- pthread_mutex_t mutex;
- THR_LOCK lock;
- File meta_file; /* Meta file we use */
- gzFile archive_write; /* Archive file we are working with */
- bool archive_write_open;
- bool dirty; /* Flag for if a flush should occur */
- bool crashed; /* Meta file is crashed */
- ha_rows rows_recorded; /* Number of rows in tables */
- z_off_t approx_file_size; /* Approximate archive data file size */
-} ARCHIVE_SHARE;
-
-/*
- Version for file format.
- 1 - Initial Version
-*/
-#define ARCHIVE_VERSION 1
-
-class ha_archive: public handler
-{
- THR_LOCK_DATA lock; /* MySQL lock */
- ARCHIVE_SHARE *share; /* Shared lock info */
- gzFile archive; /* Archive file we are working with */
- z_off_t current_position; /* The position of the row we just read */
- byte byte_buffer[IO_SIZE]; /* Initial buffer for our string */
- String buffer; /* Buffer used for blob storage */
- ha_rows scan_rows; /* Number of rows left in scan */
- bool delayed_insert; /* If the insert is delayed */
- bool bulk_insert; /* If we are performing a bulk insert */
-
-public:
- ha_archive(TABLE *table_arg);
- ~ha_archive()
- {
- }
- const char *table_type() const { return "ARCHIVE"; }
- const char *index_type(uint inx) { return "NONE"; }
- const char **bas_ext() const;
- ulong table_flags() const
- {
- return (HA_REC_NOT_IN_SEQ | HA_NOT_EXACT_COUNT | HA_NO_AUTO_INCREMENT |
- HA_FILE_BASED | HA_CAN_INSERT_DELAYED | HA_CAN_GEOMETRY);
- }
- ulong index_flags(uint idx, uint part, bool all_parts) const
- {
- return 0;
- }
- int open(const char *name, int mode, uint test_if_locked);
- int close(void);
- int write_row(byte * buf);
- int real_write_row(byte *buf, gzFile writer);
- int delete_all_rows();
- int rnd_init(bool scan=1);
- int rnd_next(byte *buf);
- int rnd_pos(byte * buf, byte *pos);
- int get_row(gzFile file_to_read, byte *buf);
- int read_meta_file(File meta_file, ha_rows *rows);
- int write_meta_file(File meta_file, ha_rows rows, bool dirty);
- ARCHIVE_SHARE *get_share(const char *table_name, int *rc);
- int free_share();
- int init_archive_writer();
- bool auto_repair() const { return 1; } // For the moment we just do this
- int read_data_header(gzFile file_to_read);
- int write_data_header(gzFile file_to_write);
- void position(const byte *record);
- int info(uint);
- int create(const char *name, TABLE *form, HA_CREATE_INFO *create_info);
- int optimize(THD* thd, HA_CHECK_OPT* check_opt);
- int repair(THD* thd, HA_CHECK_OPT* check_opt);
- void start_bulk_insert(ha_rows rows);
- int end_bulk_insert();
- enum row_type get_row_type() const
- {
- return ROW_TYPE_COMPRESSED;
- }
- THR_LOCK_DATA **store_lock(THD *thd, THR_LOCK_DATA **to,
- enum thr_lock_type lock_type);
- bool is_crashed() const;
- int check(THD* thd, HA_CHECK_OPT* check_opt);
- bool check_and_repair(THD *thd);
-};
-
-bool archive_db_init(void);
-bool archive_db_end(void);
-
diff --git a/sql/ha_berkeley.cc b/sql/ha_berkeley.cc
deleted file mode 100644
index 1de40db2724..00000000000
--- a/sql/ha_berkeley.cc
+++ /dev/null
@@ -1,2675 +0,0 @@
-/* Copyright (C) 2000-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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
-
-
-/*
- TODO:
- - Not compressed keys should use cmp_fix_length_key
- - Don't automaticly pack all string keys (To do this we need to modify
- CREATE TABLE so that one can use the pack_keys argument per key).
- - An argument to pack_key that we don't want compression.
- - DB_DBT_USERMEM should be used for fixed length tables
- We will need an updated Berkeley DB version for this.
- - Killing threads that has got a 'deadlock'
- - SHOW TABLE STATUS should give more information about the table.
- - Get a more accurate count of the number of rows (estimate_rows_upper_bound()).
- We could store the found number of rows when the table is scanned and
- then increment the counter for each attempted write.
- - We will need to extend the manager thread to makes checkpoints at
- given intervals.
- - When not using UPDATE IGNORE, don't make a sub transaction but abort
- the main transaction on errors.
- - Handling of drop table during autocommit=0 ?
- (Should we just give an error in this case if there is a pending
- transaction ?)
- - When using ALTER TABLE IGNORE, we should not start an transaction, but do
- everything wthout transactions.
- - When we do rollback, we need to subtract the number of changed rows
- from the updated tables.
-
- Testing of:
- - Mark tables that participate in a transaction so that they are not
- closed during the transaction. We need to test what happens if
- MySQL closes a table that is updated by a not commited transaction.
-*/
-
-
-#ifdef USE_PRAGMA_IMPLEMENTATION
-#pragma implementation // gcc: Class implementation
-#endif
-
-#include "mysql_priv.h"
-
-#ifdef HAVE_BERKELEY_DB
-#include <m_ctype.h>
-#include <myisampack.h>
-#include <hash.h>
-#include "ha_berkeley.h"
-#include "sql_manager.h"
-#include <stdarg.h>
-
-#define HA_BERKELEY_ROWS_IN_TABLE 10000 /* to get optimization right */
-#define HA_BERKELEY_RANGE_COUNT 100
-#define HA_BERKELEY_MAX_ROWS 10000000 /* Max rows in table */
-/* extra rows for estimate_rows_upper_bound() */
-#define HA_BERKELEY_EXTRA_ROWS 100
-
-/* Bits for share->status */
-#define STATUS_PRIMARY_KEY_INIT 1
-#define STATUS_ROW_COUNT_INIT 2
-#define STATUS_BDB_ANALYZE 4
-
-const char *ha_berkeley_ext=".db";
-bool berkeley_shared_data=0;
-u_int32_t berkeley_init_flags= DB_PRIVATE | DB_RECOVER, berkeley_env_flags=0,
- berkeley_lock_type=DB_LOCK_DEFAULT;
-ulong berkeley_cache_size, berkeley_log_buffer_size, berkeley_log_file_size=0;
-char *berkeley_home, *berkeley_tmpdir, *berkeley_logdir;
-long berkeley_lock_scan_time=0;
-ulong berkeley_trans_retry=1;
-ulong berkeley_max_lock;
-pthread_mutex_t bdb_mutex;
-
-static DB_ENV *db_env;
-static HASH bdb_open_tables;
-
-const char *berkeley_lock_names[] =
-{ "DEFAULT", "OLDEST","RANDOM","YOUNGEST",0 };
-u_int32_t berkeley_lock_types[]=
-{ DB_LOCK_DEFAULT, DB_LOCK_OLDEST, DB_LOCK_RANDOM };
-TYPELIB berkeley_lock_typelib= {array_elements(berkeley_lock_names)-1,"",
- berkeley_lock_names, NULL};
-
-static void berkeley_print_error(const char *db_errpfx, char *buffer);
-static byte* bdb_get_key(BDB_SHARE *share,uint *length,
- my_bool not_used __attribute__((unused)));
-static BDB_SHARE *get_share(const char *table_name, TABLE *table);
-static int free_share(BDB_SHARE *share, TABLE *table, uint hidden_primary_key,
- bool mutex_is_locked);
-static int write_status(DB *status_block, char *buff, uint length);
-static void update_status(BDB_SHARE *share, TABLE *table);
-static void berkeley_noticecall(DB_ENV *db_env, db_notices notice);
-
-static int berkeley_close_connection(THD *thd);
-static int berkeley_commit(THD *thd, bool all);
-static int berkeley_rollback(THD *thd, bool all);
-
-handlerton berkeley_hton = {
- "BerkeleyDB",
- SHOW_OPTION_YES,
- "Supports transactions and page-level locking",
- DB_TYPE_BERKELEY_DB,
- berkeley_init,
- 0, /* slot */
- 0, /* savepoint size */
- berkeley_close_connection,
- NULL, /* savepoint_set */
- NULL, /* savepoint_rollback */
- NULL, /* savepoint_release */
- berkeley_commit,
- berkeley_rollback,
- NULL, /* prepare */
- NULL, /* recover */
- NULL, /* commit_by_xid */
- NULL, /* rollback_by_xid */
- NULL, /* create_cursor_read_view */
- NULL, /* set_cursor_read_view */
- NULL, /* close_cursor_read_view */
- HTON_CLOSE_CURSORS_AT_COMMIT
-};
-
-typedef struct st_berkeley_trx_data {
- DB_TXN *all;
- DB_TXN *stmt;
- uint bdb_lock_count;
-} berkeley_trx_data;
-
-/* General functions */
-
-bool berkeley_init(void)
-{
- DBUG_ENTER("berkeley_init");
-
- if (have_berkeley_db != SHOW_OPTION_YES)
- goto error;
-
- if (!berkeley_tmpdir)
- berkeley_tmpdir=mysql_tmpdir;
- if (!berkeley_home)
- berkeley_home=mysql_real_data_home;
- DBUG_PRINT("bdb",("berkeley_home: %s",mysql_real_data_home));
-
- /*
- If we don't set set_lg_bsize() we will get into trouble when
- trying to use many open BDB tables.
- If log buffer is not set, assume that the we will need 512 byte per
- open table. This is a number that we have reached by testing.
- */
- if (!berkeley_log_buffer_size)
- {
- berkeley_log_buffer_size= max(table_cache_size*512,32*1024);
- }
- /*
- Berkeley DB require that
- berkeley_log_file_size >= berkeley_log_buffer_size*4
- */
- berkeley_log_file_size= berkeley_log_buffer_size*4;
- berkeley_log_file_size= MY_ALIGN(berkeley_log_file_size,1024*1024L);
- berkeley_log_file_size= max(berkeley_log_file_size, 10*1024*1024L);
-
- if (db_env_create(&db_env,0))
- goto error;
- db_env->set_errcall(db_env,berkeley_print_error);
- db_env->set_errpfx(db_env,"bdb");
- db_env->set_noticecall(db_env, berkeley_noticecall);
- db_env->set_tmp_dir(db_env, berkeley_tmpdir);
- db_env->set_data_dir(db_env, mysql_data_home);
- db_env->set_flags(db_env, berkeley_env_flags, 1);
- if (berkeley_logdir)
- db_env->set_lg_dir(db_env, berkeley_logdir); /* purecov: tested */
-
- if (opt_endinfo)
- db_env->set_verbose(db_env,
- DB_VERB_CHKPOINT | DB_VERB_DEADLOCK | DB_VERB_RECOVERY,
- 1);
-
- db_env->set_cachesize(db_env, 0, berkeley_cache_size, 0);
- db_env->set_lg_max(db_env, berkeley_log_file_size);
- db_env->set_lg_bsize(db_env, berkeley_log_buffer_size);
- db_env->set_lk_detect(db_env, berkeley_lock_type);
- if (berkeley_max_lock)
- db_env->set_lk_max(db_env, berkeley_max_lock);
-
- if (db_env->open(db_env,
- berkeley_home,
- berkeley_init_flags | DB_INIT_LOCK |
- DB_INIT_LOG | DB_INIT_MPOOL | DB_INIT_TXN |
- DB_CREATE | DB_THREAD, 0666))
- {
- db_env->close(db_env,0);
- db_env=0;
- goto error;
- }
-
- (void) hash_init(&bdb_open_tables,system_charset_info,32,0,0,
- (hash_get_key) bdb_get_key,0,0);
- pthread_mutex_init(&bdb_mutex,MY_MUTEX_INIT_FAST);
- DBUG_RETURN(FALSE);
-error:
- have_berkeley_db= SHOW_OPTION_DISABLED; // If we couldn't use handler
- DBUG_RETURN(TRUE);
-}
-
-
-bool berkeley_end(void)
-{
- int error;
- DBUG_ENTER("berkeley_end");
- if (!db_env)
- return 1; /* purecov: tested */
- berkeley_cleanup_log_files();
- error=db_env->close(db_env,0); // Error is logged
- db_env=0;
- hash_free(&bdb_open_tables);
- pthread_mutex_destroy(&bdb_mutex);
- DBUG_RETURN(error != 0);
-}
-
-static int berkeley_close_connection(THD *thd)
-{
- my_free((gptr)thd->ha_data[berkeley_hton.slot], MYF(0));
- return 0;
-}
-
-
-bool berkeley_flush_logs()
-{
- int error;
- bool result=0;
- DBUG_ENTER("berkeley_flush_logs");
- if ((error=db_env->log_flush(db_env,0)))
- {
- my_error(ER_ERROR_DURING_FLUSH_LOGS,MYF(0),error); /* purecov: inspected */
- result=1; /* purecov: inspected */
- }
- if ((error=db_env->txn_checkpoint(db_env,0,0,0)))
- {
- my_error(ER_ERROR_DURING_CHECKPOINT,MYF(0),error); /* purecov: inspected */
- result=1; /* purecov: inspected */
- }
- DBUG_RETURN(result);
-}
-
-static int berkeley_commit(THD *thd, bool all)
-{
- DBUG_ENTER("berkeley_commit");
- DBUG_PRINT("trans",("ending transaction %s", all ? "all" : "stmt"));
- berkeley_trx_data *trx=(berkeley_trx_data *)thd->ha_data[berkeley_hton.slot];
- DB_TXN **txn= all ? &trx->all : &trx->stmt;
- int error=txn_commit(*txn,0);
- *txn=0;
-#ifndef DBUG_OFF
- if (error)
- DBUG_PRINT("error",("error: %d",error));
-#endif
- DBUG_RETURN(error);
-}
-
-static int berkeley_rollback(THD *thd, bool all)
-{
- DBUG_ENTER("berkeley_rollback");
- DBUG_PRINT("trans",("aborting transaction %s", all ? "all" : "stmt"));
- berkeley_trx_data *trx=(berkeley_trx_data *)thd->ha_data[berkeley_hton.slot];
- DB_TXN **txn= all ? &trx->all : &trx->stmt;
- int error=txn_abort(*txn);
- *txn=0;
- DBUG_RETURN(error);
-}
-
-
-int berkeley_show_logs(Protocol *protocol)
-{
- char **all_logs, **free_logs, **a, **f;
- int error=1;
- MEM_ROOT **root_ptr= my_pthread_getspecific_ptr(MEM_ROOT**,THR_MALLOC);
- MEM_ROOT show_logs_root, *old_mem_root= *root_ptr;
- DBUG_ENTER("berkeley_show_logs");
-
- init_sql_alloc(&show_logs_root, BDB_LOG_ALLOC_BLOCK_SIZE,
- BDB_LOG_ALLOC_BLOCK_SIZE);
- *root_ptr= &show_logs_root;
-
- if ((error= db_env->log_archive(db_env, &all_logs,
- DB_ARCH_ABS | DB_ARCH_LOG)) ||
- (error= db_env->log_archive(db_env, &free_logs, DB_ARCH_ABS)))
- {
- DBUG_PRINT("error", ("log_archive failed (error %d)", error));
- db_env->err(db_env, error, "log_archive: DB_ARCH_ABS");
- if (error== DB_NOTFOUND)
- error=0; // No log files
- goto err;
- }
- /* Error is 0 here */
- if (all_logs)
- {
- for (a = all_logs, f = free_logs; *a; ++a)
- {
- protocol->prepare_for_resend();
- protocol->store(*a, system_charset_info);
- protocol->store(STRING_WITH_LEN("BDB"), system_charset_info);
- if (f && *f && strcmp(*a, *f) == 0)
- {
- f++;
- protocol->store(SHOW_LOG_STATUS_FREE, system_charset_info);
- }
- else
- protocol->store(SHOW_LOG_STATUS_INUSE, system_charset_info);
-
- if (protocol->write())
- {
- error=1;
- goto err;
- }
- }
- }
-err:
- free_root(&show_logs_root,MYF(0));
- *root_ptr= old_mem_root;
- DBUG_RETURN(error);
-}
-
-
-static void berkeley_print_error(const char *db_errpfx, char *buffer)
-{
- sql_print_error("%s: %s",db_errpfx,buffer); /* purecov: tested */
-}
-
-
-static void berkeley_noticecall(DB_ENV *db_env, db_notices notice)
-{
- switch (notice)
- {
- case DB_NOTICE_LOGFILE_CHANGED: /* purecov: tested */
- pthread_mutex_lock(&LOCK_manager);
- manager_status |= MANAGER_BERKELEY_LOG_CLEANUP;
- pthread_mutex_unlock(&LOCK_manager);
- pthread_cond_signal(&COND_manager);
- break;
- }
-}
-
-void berkeley_cleanup_log_files(void)
-{
- DBUG_ENTER("berkeley_cleanup_log_files");
- char **names;
- int error;
-
-// by HF. Sometimes it crashes. TODO - find out why
-#ifndef EMBEDDED_LIBRARY
- /* XXX: Probably this should be done somewhere else, and
- * should be tunable by the user. */
- if ((error = db_env->txn_checkpoint(db_env, 0, 0, 0)))
- my_error(ER_ERROR_DURING_CHECKPOINT, MYF(0), error); /* purecov: inspected */
-#endif
- if ((error = db_env->log_archive(db_env, &names, DB_ARCH_ABS)) != 0)
- {
- DBUG_PRINT("error", ("log_archive failed (error %d)", error)); /* purecov: inspected */
- db_env->err(db_env, error, "log_archive: DB_ARCH_ABS"); /* purecov: inspected */
- DBUG_VOID_RETURN; /* purecov: inspected */
- }
-
- if (names)
- { /* purecov: tested */
- char **np; /* purecov: tested */
- for (np = names; *np; ++np) /* purecov: tested */
- my_delete(*np, MYF(MY_WME)); /* purecov: tested */
-
- free(names); /* purecov: tested */
- }
-
- DBUG_VOID_RETURN;
-}
-
-
-/*****************************************************************************
-** Berkeley DB tables
-*****************************************************************************/
-
-ha_berkeley::ha_berkeley(TABLE *table_arg)
- :handler(&berkeley_hton, table_arg), alloc_ptr(0), rec_buff(0), file(0),
- int_table_flags(HA_REC_NOT_IN_SEQ | HA_FAST_KEY_READ |
- HA_NULL_IN_KEY | HA_CAN_INDEX_BLOBS | HA_NOT_EXACT_COUNT |
- HA_PRIMARY_KEY_IN_READ_INDEX | HA_FILE_BASED |
- HA_CAN_GEOMETRY |
- HA_AUTO_PART_KEY | HA_TABLE_SCAN_ON_INDEX),
- changed_rows(0), last_dup_key((uint) -1), version(0), using_ignore(0)
-{}
-
-
-static const char *ha_berkeley_exts[] = {
- ha_berkeley_ext,
- NullS
-};
-
-const char **ha_berkeley::bas_ext() const
-{
- return ha_berkeley_exts;
-}
-
-ulong ha_berkeley::index_flags(uint idx, uint part, bool all_parts) const
-{
- ulong flags= (HA_READ_NEXT | HA_READ_PREV | HA_READ_ORDER | HA_KEYREAD_ONLY
- | HA_READ_RANGE);
- for (uint i= all_parts ? 0 : part ; i <= part ; i++)
- {
- if (table->key_info[idx].key_part[i].field->type() == FIELD_TYPE_BLOB)
- {
- /* We can't use BLOBS to shortcut sorts */
- flags&= ~(HA_READ_ORDER | HA_KEYREAD_ONLY | HA_READ_RANGE);
- break;
- }
- switch (table->key_info[idx].key_part[i].field->key_type()) {
- case HA_KEYTYPE_TEXT:
- case HA_KEYTYPE_VARTEXT1:
- case HA_KEYTYPE_VARTEXT2:
- /*
- As BDB stores only one copy of equal strings, we can't use key read
- on these. Binary collations do support key read though.
- */
- if (!(table->key_info[idx].key_part[i].field->charset()->state
- & MY_CS_BINSORT))
- flags&= ~HA_KEYREAD_ONLY;
- break;
- default: // Keep compiler happy
- break;
- }
- }
- return flags;
-}
-
-
-void ha_berkeley::get_auto_primary_key(byte *to)
-{
- pthread_mutex_lock(&share->mutex);
- share->auto_ident++;
- int5store(to,share->auto_ident);
- pthread_mutex_unlock(&share->mutex);
-}
-
-
-static int
-berkeley_cmp_hidden_key(DB* file, const DBT *new_key, const DBT *saved_key)
-{
- ulonglong a=uint5korr((char*) new_key->data);
- ulonglong b=uint5korr((char*) saved_key->data);
- return a < b ? -1 : (a > b ? 1 : 0);
-}
-
-static int
-berkeley_cmp_packed_key(DB *file, const DBT *new_key, const DBT *saved_key)
-{
- KEY *key= (new_key->app_private ? (KEY*) new_key->app_private :
- (KEY*) (file->app_private));
- char *new_key_ptr= (char*) new_key->data;
- char *saved_key_ptr=(char*) saved_key->data;
- KEY_PART_INFO *key_part= key->key_part, *end=key_part+key->key_parts;
- uint key_length=new_key->size;
-
- DBUG_DUMP("key_in_index", (uchar *)saved_key_ptr, saved_key->size);
- for (; key_part != end && (int) key_length > 0; key_part++)
- {
- int cmp;
- uint length;
- if (key_part->null_bit)
- {
- if (*new_key_ptr != *saved_key_ptr++)
- return ((int) *new_key_ptr - (int) saved_key_ptr[-1]);
- key_length--;
- if (!*new_key_ptr++)
- continue;
- }
- if ((cmp= key_part->field->pack_cmp(new_key_ptr,saved_key_ptr,
- key_part->length,
- key->table->insert_or_update)))
- return cmp;
- length= key_part->field->packed_col_length(new_key_ptr,
- key_part->length);
- new_key_ptr+=length;
- key_length-=length;
- saved_key_ptr+=key_part->field->packed_col_length(saved_key_ptr,
- key_part->length);
- }
- return key->handler.bdb_return_if_eq;
-}
-
-
-/* The following is not yet used; Should be used for fixed length keys */
-
-#ifdef NOT_YET
-static int
-berkeley_cmp_fix_length_key(DB *file, const DBT *new_key, const DBT *saved_key)
-{
- KEY *key= (new_key->app_private ? (KEY*) new_key->app_private :
- (KEY*) (file->app_private));
- char *new_key_ptr= (char*) new_key->data;
- char *saved_key_ptr=(char*) saved_key->data;
- KEY_PART_INFO *key_part= key->key_part, *end=key_part+key->key_parts;
- uint key_length=new_key->size;
-
- for (; key_part != end && (int) key_length > 0 ; key_part++)
- {
- int cmp;
- if ((cmp=key_part->field->pack_cmp(new_key_ptr,saved_key_ptr,0,0)))
- return cmp;
- new_key_ptr+=key_part->length;
- key_length-= key_part->length;
- saved_key_ptr+=key_part->length;
- }
- return key->handler.bdb_return_if_eq;
-}
-#endif
-
-
-/* Compare key against row */
-
-static bool
-berkeley_key_cmp(TABLE *table, KEY *key_info, const char *key, uint key_length)
-{
- KEY_PART_INFO *key_part= key_info->key_part,
- *end=key_part+key_info->key_parts;
-
- for (; key_part != end && (int) key_length > 0; key_part++)
- {
- int cmp;
- uint length;
- if (key_part->null_bit)
- {
- key_length--;
- /*
- With the current usage, the following case will always be FALSE,
- because NULL keys are sorted before any other key
- */
- if (*key != (table->record[0][key_part->null_offset] &
- key_part->null_bit) ? 0 : 1)
- return 1;
- if (!*key++) // Null value
- continue;
- }
- /*
- Last argument has to be 0 as we are also using this to function to see
- if a key like 'a ' matched a row with 'a'
- */
- if ((cmp= key_part->field->pack_cmp(key, key_part->length, 0)))
- return cmp;
- length= key_part->field->packed_col_length(key,key_part->length);
- key+= length;
- key_length-= length;
- }
- return 0; // Identical keys
-}
-
-
-int ha_berkeley::open(const char *name, int mode, uint test_if_locked)
-{
- char name_buff[FN_REFLEN];
- uint open_mode=(mode == O_RDONLY ? DB_RDONLY : 0) | DB_THREAD;
- uint max_key_length;
- int error;
- TABLE_SHARE *table_share= table->s;
- DBUG_ENTER("ha_berkeley::open");
-
- /* Open primary key */
- hidden_primary_key=0;
- if ((primary_key= table_share->primary_key) >= MAX_KEY)
- { // No primary key
- primary_key= table_share->keys;
- key_used_on_scan=MAX_KEY;
- ref_length=hidden_primary_key=BDB_HIDDEN_PRIMARY_KEY_LENGTH;
- }
- else
- key_used_on_scan=primary_key;
-
- /* Need some extra memory in case of packed keys */
- max_key_length= table_share->max_key_length + MAX_REF_PARTS*3;
- if (!(alloc_ptr=
- my_multi_malloc(MYF(MY_WME),
- &key_buff, max_key_length,
- &key_buff2, max_key_length,
- &primary_key_buff,
- (hidden_primary_key ? 0 :
- table->key_info[table_share->primary_key].key_length),
- NullS)))
- DBUG_RETURN(1); /* purecov: inspected */
- if (!(rec_buff= (byte*) my_malloc((alloced_rec_buff_length=
- table_share->rec_buff_length),
- MYF(MY_WME))))
- {
- my_free(alloc_ptr,MYF(0)); /* purecov: inspected */
- DBUG_RETURN(1); /* purecov: inspected */
- }
-
- /* Init shared structure */
- if (!(share= get_share(name,table)))
- {
- my_free((char*) rec_buff,MYF(0)); /* purecov: inspected */
- my_free(alloc_ptr,MYF(0)); /* purecov: inspected */
- DBUG_RETURN(1); /* purecov: inspected */
- }
- thr_lock_data_init(&share->lock,&lock,(void*) 0);
- key_file = share->key_file;
- key_type = share->key_type;
- bzero((char*) &current_row,sizeof(current_row));
-
- /* Fill in shared structure, if needed */
- pthread_mutex_lock(&share->mutex);
- file= share->file;
- if (!share->use_count++)
- {
- if ((error=db_create(&file, db_env, 0)))
- {
- free_share(share,table, hidden_primary_key,1); /* purecov: inspected */
- my_free((char*) rec_buff,MYF(0)); /* purecov: inspected */
- my_free(alloc_ptr,MYF(0)); /* purecov: inspected */
- my_errno=error; /* purecov: inspected */
- DBUG_RETURN(1); /* purecov: inspected */
- }
- share->file= file;
-
- file->set_bt_compare(file,
- (hidden_primary_key ? berkeley_cmp_hidden_key :
- berkeley_cmp_packed_key));
- if (!hidden_primary_key)
- file->app_private= (void*) (table->key_info + table_share->primary_key);
- if ((error= txn_begin(db_env, 0, (DB_TXN**) &transaction, 0)) ||
- (error= (file->open(file, transaction,
- fn_format(name_buff, name, "", ha_berkeley_ext,
- 2 | 4),
- "main", DB_BTREE, open_mode, 0))) ||
- (error= transaction->commit(transaction, 0)))
- {
- free_share(share, table, hidden_primary_key,1); /* purecov: inspected */
- my_free((char*) rec_buff,MYF(0)); /* purecov: inspected */
- my_free(alloc_ptr,MYF(0)); /* purecov: inspected */
- my_errno=error; /* purecov: inspected */
- DBUG_RETURN(1); /* purecov: inspected */
- }
-
- /* Open other keys; These are part of the share structure */
- key_file[primary_key]=file;
- key_type[primary_key]=DB_NOOVERWRITE;
-
- DB **ptr=key_file;
- for (uint i=0, used_keys=0; i < table_share->keys ; i++, ptr++)
- {
- char part[7];
- if (i != primary_key)
- {
- if ((error=db_create(ptr, db_env, 0)))
- {
- close(); /* purecov: inspected */
- my_errno=error; /* purecov: inspected */
- DBUG_RETURN(1); /* purecov: inspected */
- }
- sprintf(part,"key%02d",++used_keys);
- key_type[i]=table->key_info[i].flags & HA_NOSAME ? DB_NOOVERWRITE : 0;
- (*ptr)->set_bt_compare(*ptr, berkeley_cmp_packed_key);
- (*ptr)->app_private= (void*) (table->key_info+i);
- if (!(table->key_info[i].flags & HA_NOSAME))
- {
- DBUG_PRINT("bdb",("Setting DB_DUP for key %u", i));
- (*ptr)->set_flags(*ptr, DB_DUP);
- }
- if ((error= txn_begin(db_env, 0, (DB_TXN**) &transaction, 0)) ||
- (error=((*ptr)->open(*ptr, transaction, name_buff, part, DB_BTREE,
- open_mode, 0))) ||
- (error= transaction->commit(transaction, 0)))
- {
- close(); /* purecov: inspected */
- my_errno=error; /* purecov: inspected */
- DBUG_RETURN(1); /* purecov: inspected */
- }
- }
- }
- /* Calculate pack_length of primary key */
- share->fixed_length_primary_key= 1;
- if (!hidden_primary_key)
- {
- ref_length=0;
- KEY_PART_INFO *key_part= table->key_info[primary_key].key_part;
- KEY_PART_INFO *end=key_part+table->key_info[primary_key].key_parts;
- for (; key_part != end ; key_part++)
- ref_length+= key_part->field->max_packed_col_length(key_part->length);
- share->fixed_length_primary_key=
- (ref_length == table->key_info[primary_key].key_length);
- share->status|= STATUS_PRIMARY_KEY_INIT;
- }
- share->ref_length= ref_length;
- }
- ref_length= share->ref_length; // If second open
- pthread_mutex_unlock(&share->mutex);
-
- transaction=0;
- cursor=0;
- key_read=0;
- block_size=8192; // Berkeley DB block size
- share->fixed_length_row= !(table_share->db_create_options &
- HA_OPTION_PACK_RECORD);
-
- get_status();
- info(HA_STATUS_NO_LOCK | HA_STATUS_VARIABLE | HA_STATUS_CONST);
- DBUG_RETURN(0);
-}
-
-
-int ha_berkeley::close(void)
-{
- DBUG_ENTER("ha_berkeley::close");
-
- my_free((char*) rec_buff,MYF(MY_ALLOW_ZERO_PTR));
- my_free(alloc_ptr,MYF(MY_ALLOW_ZERO_PTR));
- ha_berkeley::extra(HA_EXTRA_RESET); // current_row buffer
- DBUG_RETURN(free_share(share,table, hidden_primary_key,0));
-}
-
-
-/* Reallocate buffer if needed */
-
-bool ha_berkeley::fix_rec_buff_for_blob(ulong length)
-{
- if (! rec_buff || length > alloced_rec_buff_length)
- {
- byte *newptr;
- if (!(newptr=(byte*) my_realloc((gptr) rec_buff, length,
- MYF(MY_ALLOW_ZERO_PTR))))
- return 1; /* purecov: inspected */
- rec_buff=newptr;
- alloced_rec_buff_length=length;
- }
- return 0;
-}
-
-
-/* Calculate max length needed for row */
-
-ulong ha_berkeley::max_row_length(const byte *buf)
-{
- ulong length= table->s->reclength + table->s->fields*2;
- uint *ptr, *end;
- for (ptr= table->s->blob_field, end=ptr + table->s->blob_fields ;
- ptr != end ;
- ptr++)
- {
- Field_blob *blob= ((Field_blob*) table->field[*ptr]);
- length+= blob->get_length((char*) buf + blob->offset())+2;
- }
- return length;
-}
-
-
-/*
- Pack a row for storage. If the row is of fixed length, just store the
- row 'as is'.
- If not, we will generate a packed row suitable for storage.
- This will only fail if we don't have enough memory to pack the row, which;
- may only happen in rows with blobs, as the default row length is
- pre-allocated.
-*/
-
-int ha_berkeley::pack_row(DBT *row, const byte *record, bool new_row)
-{
- byte *ptr;
- bzero((char*) row,sizeof(*row));
- if (share->fixed_length_row)
- {
- row->data=(void*) record;
- row->size= table->s->reclength+hidden_primary_key;
- if (hidden_primary_key)
- {
- if (new_row)
- get_auto_primary_key(current_ident);
- memcpy_fixed((char*) record+table->s->reclength, (char*) current_ident,
- BDB_HIDDEN_PRIMARY_KEY_LENGTH);
- }
- return 0;
- }
- if (table->s->blob_fields)
- {
- if (fix_rec_buff_for_blob(max_row_length(record)))
- return HA_ERR_OUT_OF_MEM; /* purecov: inspected */
- }
-
- /* Copy null bits */
- memcpy(rec_buff, record, table->s->null_bytes);
- ptr= rec_buff + table->s->null_bytes;
-
- for (Field **field=table->field ; *field ; field++)
- ptr=(byte*) (*field)->pack((char*) ptr,
- (char*) record + (*field)->offset());
-
- if (hidden_primary_key)
- {
- if (new_row)
- get_auto_primary_key(current_ident);
- memcpy_fixed((char*) ptr, (char*) current_ident,
- BDB_HIDDEN_PRIMARY_KEY_LENGTH);
- ptr+=BDB_HIDDEN_PRIMARY_KEY_LENGTH;
- }
- row->data=rec_buff;
- row->size= (u_int32_t) (ptr - rec_buff);
- return 0;
-}
-
-
-void ha_berkeley::unpack_row(char *record, DBT *row)
-{
- if (share->fixed_length_row)
- memcpy(record,(char*) row->data,table->s->reclength+hidden_primary_key);
- else
- {
- /* Copy null bits */
- const char *ptr= (const char*) row->data;
- memcpy(record, ptr, table->s->null_bytes);
- ptr+= table->s->null_bytes;
- for (Field **field=table->field ; *field ; field++)
- ptr= (*field)->unpack(record + (*field)->offset(), ptr);
- }
-}
-
-
-/* Store the key and the primary key into the row */
-
-void ha_berkeley::unpack_key(char *record, DBT *key, uint index)
-{
- KEY *key_info= table->key_info+index;
- KEY_PART_INFO *key_part= key_info->key_part,
- *end= key_part+key_info->key_parts;
- char *pos= (char*) key->data;
-
- for (; key_part != end; key_part++)
- {
- if (key_part->null_bit)
- {
- if (!*pos++) // Null value
- {
- /*
- We don't need to reset the record data as we will not access it
- if the null data is set
- */
-
- record[key_part->null_offset]|=key_part->null_bit;
- continue;
- }
- record[key_part->null_offset]&= ~key_part->null_bit;
- }
- pos= (char*) key_part->field->unpack_key(record + key_part->field->offset(),
- pos, key_part->length);
- }
-}
-
-
-/*
- Create a packed key from a row. This key will be written as such
- to the index tree.
-
- This will never fail as the key buffer is pre-allocated.
-*/
-
-DBT *ha_berkeley::create_key(DBT *key, uint keynr, char *buff,
- const byte *record, int key_length)
-{
- bzero((char*) key,sizeof(*key));
- if (hidden_primary_key && keynr == primary_key)
- {
- /* We don't need to set app_private here */
- key->data=current_ident;
- key->size=BDB_HIDDEN_PRIMARY_KEY_LENGTH;
- return key;
- }
-
- KEY *key_info=table->key_info+keynr;
- KEY_PART_INFO *key_part=key_info->key_part;
- KEY_PART_INFO *end=key_part+key_info->key_parts;
- DBUG_ENTER("create_key");
-
- key->data=buff;
- key->app_private= key_info;
- for (; key_part != end && key_length > 0; key_part++)
- {
- if (key_part->null_bit)
- {
- /* Store 0 if the key part is a NULL part */
- if (record[key_part->null_offset] & key_part->null_bit)
- {
- *buff++ =0;
- key->flags|=DB_DBT_DUPOK;
- continue;
- }
- *buff++ = 1; // Store NOT NULL marker
- }
- buff=key_part->field->pack_key(buff,(char*) (record + key_part->offset),
- key_part->length);
- key_length-=key_part->length;
- }
- key->size= (u_int32_t) (buff - (char*) key->data);
- DBUG_DUMP("key",(uchar*) key->data, key->size);
- DBUG_RETURN(key);
-}
-
-
-/*
- Create a packed key from from a MySQL unpacked key (like the one that is
- sent from the index_read()
-
- This key is to be used to read a row
-*/
-
-DBT *ha_berkeley::pack_key(DBT *key, uint keynr, char *buff,
- const byte *key_ptr, uint key_length)
-{
- KEY *key_info=table->key_info+keynr;
- KEY_PART_INFO *key_part=key_info->key_part;
- KEY_PART_INFO *end=key_part+key_info->key_parts;
- DBUG_ENTER("bdb:pack_key");
-
- bzero((char*) key,sizeof(*key));
- key->data=buff;
- key->app_private= (void*) key_info;
-
- for (; key_part != end && (int) key_length > 0 ; key_part++)
- {
- uint offset=0;
- if (key_part->null_bit)
- {
- if (!(*buff++ = (*key_ptr == 0))) // Store 0 if NULL
- {
- key_length-= key_part->store_length;
- key_ptr+= key_part->store_length;
- key->flags|=DB_DBT_DUPOK;
- continue;
- }
- offset=1; // Data is at key_ptr+1
- }
- buff=key_part->field->pack_key_from_key_image(buff,(char*) key_ptr+offset,
- key_part->length);
- key_ptr+=key_part->store_length;
- key_length-=key_part->store_length;
- }
- key->size= (u_int32_t) (buff - (char*) key->data);
- DBUG_DUMP("key",(uchar*) key->data, key->size);
- DBUG_RETURN(key);
-}
-
-
-int ha_berkeley::write_row(byte * record)
-{
- DBT row,prim_key,key;
- int error;
- DBUG_ENTER("write_row");
-
- statistic_increment(table->in_use->status_var.ha_write_count, &LOCK_status);
- if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_INSERT)
- table->timestamp_field->set_time();
- if (table->next_number_field && record == table->record[0])
- {
- if ((error= update_auto_increment()))
- DBUG_RETURN(error);
- }
- if ((error=pack_row(&row, record,1)))
- DBUG_RETURN(error); /* purecov: inspected */
-
- table->insert_or_update= 1; // For handling of VARCHAR
- if (table->s->keys + test(hidden_primary_key) == 1)
- {
- error=file->put(file, transaction, create_key(&prim_key, primary_key,
- key_buff, record),
- &row, key_type[primary_key]);
- last_dup_key=primary_key;
- }
- else
- {
- DB_TXN *sub_trans = transaction;
- /* Don't use sub transactions in temporary tables */
- for (uint retry=0 ; retry < berkeley_trans_retry ; retry++)
- {
- key_map changed_keys(0);
- if (!(error=file->put(file, sub_trans, create_key(&prim_key, primary_key,
- key_buff, record),
- &row, key_type[primary_key])))
- {
- changed_keys.set_bit(primary_key);
- for (uint keynr=0 ; keynr < table->s->keys ; keynr++)
- {
- if (keynr == primary_key)
- continue;
- if ((error=key_file[keynr]->put(key_file[keynr], sub_trans,
- create_key(&key, keynr, key_buff2,
- record),
- &prim_key, key_type[keynr])))
- {
- last_dup_key=keynr;
- break;
- }
- changed_keys.set_bit(keynr);
- }
- }
- else
- last_dup_key=primary_key;
- if (error)
- {
- /* Remove inserted row */
- DBUG_PRINT("error",("Got error %d",error));
- if (using_ignore)
- {
- int new_error = 0;
- if (!changed_keys.is_clear_all())
- {
- new_error = 0;
- for (uint keynr=0;
- keynr < table->s->keys+test(hidden_primary_key);
- keynr++)
- {
- if (changed_keys.is_set(keynr))
- {
- if ((new_error = remove_key(sub_trans, keynr, record,
- &prim_key)))
- break; /* purecov: inspected */
- }
- }
- }
- if (new_error)
- {
- error=new_error; // This shouldn't happen /* purecov: inspected */
- break; /* purecov: inspected */
- }
- }
- }
- if (error != DB_LOCK_DEADLOCK)
- break;
- }
- }
- table->insert_or_update= 0;
- if (error == DB_KEYEXIST)
- error=HA_ERR_FOUND_DUPP_KEY;
- else if (!error)
- changed_rows++;
- DBUG_RETURN(error);
-}
-
-
-/* Compare if a key in a row has changed */
-
-int ha_berkeley::key_cmp(uint keynr, const byte * old_row,
- const byte * new_row)
-{
- KEY_PART_INFO *key_part=table->key_info[keynr].key_part;
- KEY_PART_INFO *end=key_part+table->key_info[keynr].key_parts;
-
- for (; key_part != end ; key_part++)
- {
- if (key_part->null_bit)
- {
- if ((old_row[key_part->null_offset] & key_part->null_bit) !=
- (new_row[key_part->null_offset] & key_part->null_bit))
- return 1;
- }
- if (key_part->key_part_flag & (HA_BLOB_PART | HA_VAR_LENGTH_PART))
- {
-
- if (key_part->field->cmp_binary((char*) (old_row + key_part->offset),
- (char*) (new_row + key_part->offset),
- (ulong) key_part->length))
- return 1;
- }
- else
- {
- if (memcmp(old_row+key_part->offset, new_row+key_part->offset,
- key_part->length))
- return 1;
- }
- }
- return 0;
-}
-
-
-/*
- Update a row from one value to another.
- Clobbers key_buff2
-*/
-
-int ha_berkeley::update_primary_key(DB_TXN *trans, bool primary_key_changed,
- const byte * old_row, DBT *old_key,
- const byte * new_row, DBT *new_key,
- bool local_using_ignore)
-{
- DBT row;
- int error;
- DBUG_ENTER("update_primary_key");
-
- if (primary_key_changed)
- {
- // Primary key changed or we are updating a key that can have duplicates.
- // Delete the old row and add a new one
- if (!(error=remove_key(trans, primary_key, old_row, old_key)))
- {
- if (!(error=pack_row(&row, new_row, 0)))
- {
- if ((error=file->put(file, trans, new_key, &row,
- key_type[primary_key])))
- {
- // Probably a duplicated key; restore old key and row if needed
- last_dup_key=primary_key;
- if (local_using_ignore)
- {
- int new_error;
- if ((new_error=pack_row(&row, old_row, 0)) ||
- (new_error=file->put(file, trans, old_key, &row,
- key_type[primary_key])))
- error=new_error; // fatal error /* purecov: inspected */
- }
- }
- }
- }
- }
- else
- {
- // Primary key didn't change; just update the row data
- if (!(error=pack_row(&row, new_row, 0)))
- error=file->put(file, trans, new_key, &row, 0);
- }
- DBUG_RETURN(error);
-}
-
-/*
- Restore changed keys, when a non-fatal error aborts the insert/update
- of one row.
- Clobbers keybuff2
-*/
-
-int ha_berkeley::restore_keys(DB_TXN *trans, key_map *changed_keys,
- uint primary_key,
- const byte *old_row, DBT *old_key,
- const byte *new_row, DBT *new_key)
-{
- int error;
- DBT tmp_key;
- uint keynr;
- DBUG_ENTER("restore_keys");
-
- /* Restore the old primary key, and the old row, but don't ignore
- duplicate key failure */
- if ((error=update_primary_key(trans, TRUE, new_row, new_key,
- old_row, old_key, FALSE)))
- goto err; /* purecov: inspected */
-
- /* Remove the new key, and put back the old key
- changed_keys is a map of all non-primary keys that need to be
- rolled back. The last key set in changed_keys is the one that
- triggered the duplicate key error (it wasn't inserted), so for
- that one just put back the old value. */
- if (!changed_keys->is_clear_all())
- {
- for (keynr=0 ; keynr < table->s->keys+test(hidden_primary_key) ; keynr++)
- {
- if (changed_keys->is_set(keynr))
- {
- if (changed_keys->is_prefix(1) &&
- (error = remove_key(trans, keynr, new_row, new_key)))
- break; /* purecov: inspected */
- if ((error = key_file[keynr]->put(key_file[keynr], trans,
- create_key(&tmp_key, keynr, key_buff2,
- old_row),
- old_key, key_type[keynr])))
- break; /* purecov: inspected */
- }
- }
- }
-
-err:
- DBUG_ASSERT(error != DB_KEYEXIST);
- DBUG_RETURN(error);
-}
-
-
-int ha_berkeley::update_row(const byte * old_row, byte * new_row)
-{
- DBT prim_key, key, old_prim_key;
- int error;
- DB_TXN *sub_trans;
- bool primary_key_changed;
- DBUG_ENTER("update_row");
- LINT_INIT(error);
-
- statistic_increment(table->in_use->status_var.ha_update_count,&LOCK_status);
- if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_UPDATE)
- table->timestamp_field->set_time();
-
- table->insert_or_update= 1; // For handling of VARCHAR
- if (hidden_primary_key)
- {
- primary_key_changed=0;
- bzero((char*) &prim_key,sizeof(prim_key));
- prim_key.data= (void*) current_ident;
- prim_key.size=BDB_HIDDEN_PRIMARY_KEY_LENGTH;
- old_prim_key=prim_key;
- }
- else
- {
- create_key(&prim_key, primary_key, key_buff, new_row);
-
- if ((primary_key_changed=key_cmp(primary_key, old_row, new_row)))
- create_key(&old_prim_key, primary_key, primary_key_buff, old_row);
- else
- old_prim_key=prim_key;
- }
-
- sub_trans = transaction;
- for (uint retry=0 ; retry < berkeley_trans_retry ; retry++)
- {
- key_map changed_keys(0);
- /* Start by updating the primary key */
- if (!(error=update_primary_key(sub_trans, primary_key_changed,
- old_row, &old_prim_key,
- new_row, &prim_key,
- using_ignore)))
- {
- // Update all other keys
- for (uint keynr=0 ; keynr < table->s->keys ; keynr++)
- {
- if (keynr == primary_key)
- continue;
- if (key_cmp(keynr, old_row, new_row) || primary_key_changed)
- {
- if ((error=remove_key(sub_trans, keynr, old_row, &old_prim_key)))
- {
- table->insert_or_update= 0;
- DBUG_RETURN(error); // Fatal error /* purecov: inspected */
- }
- changed_keys.set_bit(keynr);
- if ((error=key_file[keynr]->put(key_file[keynr], sub_trans,
- create_key(&key, keynr, key_buff2,
- new_row),
- &prim_key, key_type[keynr])))
- {
- last_dup_key=keynr;
- break;
- }
- }
- }
- }
- if (error)
- {
- /* Remove inserted row */
- DBUG_PRINT("error",("Got error %d",error));
- if (using_ignore)
- {
- int new_error = 0;
- if (!changed_keys.is_clear_all())
- new_error=restore_keys(transaction, &changed_keys, primary_key,
- old_row, &old_prim_key, new_row, &prim_key);
- if (new_error)
- {
- /* This shouldn't happen */
- error=new_error; /* purecov: inspected */
- break; /* purecov: inspected */
- }
- }
- }
- if (error != DB_LOCK_DEADLOCK)
- break;
- }
- table->insert_or_update= 0;
- if (error == DB_KEYEXIST)
- error=HA_ERR_FOUND_DUPP_KEY;
- DBUG_RETURN(error);
-}
-
-
-/*
- Delete one key
- This uses key_buff2, when keynr != primary key, so it's important that
- a function that calls this doesn't use this buffer for anything else.
-*/
-
-int ha_berkeley::remove_key(DB_TXN *trans, uint keynr, const byte *record,
- DBT *prim_key)
-{
- int error;
- DBT key;
- DBUG_ENTER("remove_key");
- DBUG_PRINT("enter",("index: %d",keynr));
-
- if (keynr == active_index && cursor)
- error=cursor->c_del(cursor,0);
- else if (keynr == primary_key ||
- ((table->key_info[keynr].flags & (HA_NOSAME | HA_NULL_PART_KEY)) ==
- HA_NOSAME))
- { // Unique key
- DBUG_ASSERT(keynr == primary_key || prim_key->data != key_buff2);
- error=key_file[keynr]->del(key_file[keynr], trans,
- keynr == primary_key ?
- prim_key :
- create_key(&key, keynr, key_buff2, record),
- 0);
- }
- else
- {
- /*
- To delete the not duplicated key, we need to open an cursor on the
- row to find the key to be delete and delete it.
- We will never come here with keynr = primary_key
- */
- DBUG_ASSERT(keynr != primary_key && prim_key->data != key_buff2);
- DBC *tmp_cursor;
- if (!(error=key_file[keynr]->cursor(key_file[keynr], trans,
- &tmp_cursor, 0)))
- {
- if (!(error=tmp_cursor->c_get(tmp_cursor,
- create_key(&key, keynr, key_buff2, record),
- prim_key, DB_GET_BOTH | DB_RMW)))
- { // This shouldn't happen
- error=tmp_cursor->c_del(tmp_cursor,0);
- }
- int result=tmp_cursor->c_close(tmp_cursor);
- if (!error)
- error=result;
- }
- }
- DBUG_RETURN(error);
-}
-
-
-/* Delete all keys for new_record */
-
-int ha_berkeley::remove_keys(DB_TXN *trans, const byte *record,
- DBT *new_record, DBT *prim_key, key_map *keys)
-{
- int result = 0;
- for (uint keynr=0;
- keynr < table->s->keys+test(hidden_primary_key);
- keynr++)
- {
- if (keys->is_set(keynr))
- {
- int new_error=remove_key(trans, keynr, record, prim_key);
- if (new_error)
- {
- result=new_error; // Return last error /* purecov: inspected */
- break; // Let rollback correct things /* purecov: inspected */
- }
- }
- }
- return result;
-}
-
-
-int ha_berkeley::delete_row(const byte * record)
-{
- int error;
- DBT row, prim_key;
- key_map keys= table->s->keys_in_use;
- DBUG_ENTER("delete_row");
- statistic_increment(table->in_use->status_var.ha_delete_count,&LOCK_status);
-
- if ((error=pack_row(&row, record, 0)))
- DBUG_RETURN((error)); /* purecov: inspected */
- create_key(&prim_key, primary_key, key_buff, record);
- if (hidden_primary_key)
- keys.set_bit(primary_key);
-
- /* Subtransactions may be used in order to retry the delete in
- case we get a DB_LOCK_DEADLOCK error. */
- DB_TXN *sub_trans = transaction;
- for (uint retry=0 ; retry < berkeley_trans_retry ; retry++)
- {
- error=remove_keys(sub_trans, record, &row, &prim_key, &keys);
- if (error)
- { /* purecov: inspected */
- DBUG_PRINT("error",("Got error %d",error));
- break; // No retry - return error
- }
- if (error != DB_LOCK_DEADLOCK)
- break;
- }
-#ifdef CANT_COUNT_DELETED_ROWS
- if (!error)
- changed_rows--;
-#endif
- DBUG_RETURN(error);
-}
-
-
-int ha_berkeley::index_init(uint keynr)
-{
- int error;
- DBUG_ENTER("ha_berkeley::index_init");
- DBUG_PRINT("enter",("table: '%s' key: %d", table->s->table_name, keynr));
-
- /*
- Under some very rare conditions (like full joins) we may already have
- an active cursor at this point
- */
- if (cursor)
- {
- DBUG_PRINT("note",("Closing active cursor"));
- cursor->c_close(cursor);
- }
- active_index=keynr;
- if ((error=key_file[keynr]->cursor(key_file[keynr], transaction, &cursor,
- table->reginfo.lock_type >
- TL_WRITE_ALLOW_READ ?
- 0 : 0)))
- cursor=0; // Safety /* purecov: inspected */
- bzero((char*) &last_key,sizeof(last_key));
- DBUG_RETURN(error);
-}
-
-int ha_berkeley::index_end()
-{
- int error=0;
- DBUG_ENTER("ha_berkely::index_end");
- if (cursor)
- {
- DBUG_PRINT("enter",("table: '%s'", table->s->table_name));
- error=cursor->c_close(cursor);
- cursor=0;
- }
- active_index=MAX_KEY;
- DBUG_RETURN(error);
-}
-
-
-/* What to do after we have read a row based on an index */
-
-int ha_berkeley::read_row(int error, char *buf, uint keynr, DBT *row,
- DBT *found_key, bool read_next)
-{
- DBUG_ENTER("ha_berkeley::read_row");
- if (error)
- {
- if (error == DB_NOTFOUND || error == DB_KEYEMPTY)
- error=read_next ? HA_ERR_END_OF_FILE : HA_ERR_KEY_NOT_FOUND;
- table->status=STATUS_NOT_FOUND;
- DBUG_RETURN(error);
- }
- if (hidden_primary_key)
- memcpy_fixed(current_ident,
- (char*) row->data+row->size-BDB_HIDDEN_PRIMARY_KEY_LENGTH,
- BDB_HIDDEN_PRIMARY_KEY_LENGTH);
- table->status=0;
- if (keynr != primary_key)
- {
- /* We only found the primary key. Now we have to use this to find
- the row data */
- if (key_read && found_key)
- {
- unpack_key(buf,found_key,keynr);
- if (!hidden_primary_key)
- unpack_key(buf,row,primary_key);
- DBUG_RETURN(0);
- }
- DBT key;
- bzero((char*) &key,sizeof(key));
- key.data=key_buff;
- key.size=row->size;
- key.app_private= (void*) (table->key_info+primary_key);
- memcpy(key_buff,row->data,row->size);
- /* Read the data into current_row */
- current_row.flags=DB_DBT_REALLOC;
- if ((error=file->get(file, transaction, &key, &current_row, 0)))
- {
- table->status=STATUS_NOT_FOUND; /* purecov: inspected */
- DBUG_RETURN(error == DB_NOTFOUND ? HA_ERR_CRASHED : error); /* purecov: inspected */
- }
- row= &current_row;
- }
- unpack_row(buf,row);
- DBUG_RETURN(0);
-}
-
-
-/* This is only used to read whole keys */
-
-int ha_berkeley::index_read_idx(byte * buf, uint keynr, const byte * key,
- uint key_len, enum ha_rkey_function find_flag)
-{
- table->in_use->status_var.ha_read_key_count++;
- DBUG_ENTER("index_read_idx");
- current_row.flags=DB_DBT_REALLOC;
- active_index=MAX_KEY;
- DBUG_RETURN(read_row(key_file[keynr]->get(key_file[keynr], transaction,
- pack_key(&last_key, keynr, key_buff, key,
- key_len),
- &current_row,0),
- (char*) buf, keynr, &current_row, &last_key, 0));
-}
-
-
-int ha_berkeley::index_read(byte * buf, const byte * key,
- uint key_len, enum ha_rkey_function find_flag)
-{
- DBT row;
- int error;
- KEY *key_info= &table->key_info[active_index];
- int do_prev= 0;
- DBUG_ENTER("ha_berkeley::index_read");
-
- table->in_use->status_var.ha_read_key_count++;
- bzero((char*) &row,sizeof(row));
- if (find_flag == HA_READ_BEFORE_KEY)
- {
- find_flag= HA_READ_KEY_OR_NEXT;
- do_prev= 1;
- }
- else if (find_flag == HA_READ_PREFIX_LAST_OR_PREV)
- {
- find_flag= HA_READ_AFTER_KEY;
- do_prev= 1;
- }
- if (key_len == key_info->key_length &&
- !(table->key_info[active_index].flags & HA_END_SPACE_KEY))
- {
- if (find_flag == HA_READ_AFTER_KEY)
- key_info->handler.bdb_return_if_eq= 1;
- error=read_row(cursor->c_get(cursor, pack_key(&last_key,
- active_index,
- key_buff,
- key, key_len),
- &row,
- (find_flag == HA_READ_KEY_EXACT ?
- DB_SET : DB_SET_RANGE)),
- (char*) buf, active_index, &row, (DBT*) 0, 0);
- key_info->handler.bdb_return_if_eq= 0;
- }
- else
- {
- /* read of partial key */
- pack_key(&last_key, active_index, key_buff, key, key_len);
- /* Store for compare */
- memcpy(key_buff2, key_buff, (key_len=last_key.size));
- /*
- If HA_READ_AFTER_KEY is set, return next key, else return first
- matching key.
- */
- key_info->handler.bdb_return_if_eq= (find_flag == HA_READ_AFTER_KEY ?
- 1 : -1);
- error=read_row(cursor->c_get(cursor, &last_key, &row, DB_SET_RANGE),
- (char*) buf, active_index, &row, (DBT*) 0, 0);
- key_info->handler.bdb_return_if_eq= 0;
- if (!error && find_flag == HA_READ_KEY_EXACT)
- {
- /* Ensure that we found a key that is equal to the current one */
- if (!error && berkeley_key_cmp(table, key_info, key_buff2, key_len))
- error=HA_ERR_KEY_NOT_FOUND;
- }
- }
- if (do_prev)
- {
- bzero((char*) &row, sizeof(row));
- error= read_row(cursor->c_get(cursor, &last_key, &row, DB_PREV),
- (char*) buf, active_index, &row, &last_key, 1);
- }
- DBUG_RETURN(error);
-}
-
-/*
- Read last key is solved by reading the next key and then reading
- the previous key
-*/
-
-int ha_berkeley::index_read_last(byte * buf, const byte * key, uint key_len)
-{
- DBT row;
- int error;
- KEY *key_info= &table->key_info[active_index];
- DBUG_ENTER("ha_berkeley::index_read");
-
- statistic_increment(table->in_use->status_var.ha_read_key_count,
- &LOCK_status);
- bzero((char*) &row,sizeof(row));
-
- /* read of partial key */
- pack_key(&last_key, active_index, key_buff, key, key_len);
- /* Store for compare */
- memcpy(key_buff2, key_buff, (key_len=last_key.size));
- key_info->handler.bdb_return_if_eq= 1;
- error=read_row(cursor->c_get(cursor, &last_key, &row, DB_SET_RANGE),
- (char*) buf, active_index, &row, (DBT*) 0, 0);
- key_info->handler.bdb_return_if_eq= 0;
- bzero((char*) &row,sizeof(row));
- if (read_row(cursor->c_get(cursor, &last_key, &row, DB_PREV),
- (char*) buf, active_index, &row, &last_key, 1) ||
- berkeley_key_cmp(table, key_info, key_buff2, key_len))
- error=HA_ERR_KEY_NOT_FOUND;
- DBUG_RETURN(error);
-}
-
-
-int ha_berkeley::index_next(byte * buf)
-{
- DBT row;
- DBUG_ENTER("index_next");
- statistic_increment(table->in_use->status_var.ha_read_next_count,
- &LOCK_status);
- bzero((char*) &row,sizeof(row));
- DBUG_RETURN(read_row(cursor->c_get(cursor, &last_key, &row, DB_NEXT),
- (char*) buf, active_index, &row, &last_key, 1));
-}
-
-int ha_berkeley::index_next_same(byte * buf, const byte *key, uint keylen)
-{
- DBT row;
- int error;
- DBUG_ENTER("index_next_same");
- statistic_increment(table->in_use->status_var.ha_read_next_count,
- &LOCK_status);
- bzero((char*) &row,sizeof(row));
- if (keylen == table->key_info[active_index].key_length &&
- !(table->key_info[active_index].flags & HA_END_SPACE_KEY))
- error=read_row(cursor->c_get(cursor, &last_key, &row, DB_NEXT_DUP),
- (char*) buf, active_index, &row, &last_key, 1);
- else
- {
- error=read_row(cursor->c_get(cursor, &last_key, &row, DB_NEXT),
- (char*) buf, active_index, &row, &last_key, 1);
- if (!error && ::key_cmp_if_same(table, key, active_index, keylen))
- error=HA_ERR_END_OF_FILE;
- }
- DBUG_RETURN(error);
-}
-
-
-int ha_berkeley::index_prev(byte * buf)
-{
- DBT row;
- DBUG_ENTER("index_prev");
- statistic_increment(table->in_use->status_var.ha_read_prev_count,
- &LOCK_status);
- bzero((char*) &row,sizeof(row));
- DBUG_RETURN(read_row(cursor->c_get(cursor, &last_key, &row, DB_PREV),
- (char*) buf, active_index, &row, &last_key, 1));
-}
-
-
-int ha_berkeley::index_first(byte * buf)
-{
- DBT row;
- DBUG_ENTER("index_first");
- statistic_increment(table->in_use->status_var.ha_read_first_count,
- &LOCK_status);
- bzero((char*) &row,sizeof(row));
- DBUG_RETURN(read_row(cursor->c_get(cursor, &last_key, &row, DB_FIRST),
- (char*) buf, active_index, &row, &last_key, 1));
-}
-
-int ha_berkeley::index_last(byte * buf)
-{
- DBT row;
- DBUG_ENTER("index_last");
- statistic_increment(table->in_use->status_var.ha_read_last_count,
- &LOCK_status);
- bzero((char*) &row,sizeof(row));
- DBUG_RETURN(read_row(cursor->c_get(cursor, &last_key, &row, DB_LAST),
- (char*) buf, active_index, &row, &last_key, 0));
-}
-
-int ha_berkeley::rnd_init(bool scan)
-{
- DBUG_ENTER("rnd_init");
- current_row.flags=DB_DBT_REALLOC;
- DBUG_RETURN(index_init(primary_key));
-}
-
-int ha_berkeley::rnd_end()
-{
- return index_end();
-}
-
-int ha_berkeley::rnd_next(byte *buf)
-{
- DBT row;
- DBUG_ENTER("rnd_next");
- statistic_increment(table->in_use->status_var.ha_read_rnd_next_count,
- &LOCK_status);
- bzero((char*) &row,sizeof(row));
- DBUG_RETURN(read_row(cursor->c_get(cursor, &last_key, &row, DB_NEXT),
- (char*) buf, primary_key, &row, &last_key, 1));
-}
-
-
-DBT *ha_berkeley::get_pos(DBT *to, byte *pos)
-{
- /* We don't need to set app_private here */
- bzero((char*) to,sizeof(*to));
-
- to->data=pos;
- if (share->fixed_length_primary_key)
- to->size=ref_length;
- else
- {
- KEY_PART_INFO *key_part=table->key_info[primary_key].key_part;
- KEY_PART_INFO *end=key_part+table->key_info[primary_key].key_parts;
-
- for (; key_part != end ; key_part++)
- pos+=key_part->field->packed_col_length((char*) pos,key_part->length);
- to->size= (uint) (pos- (byte*) to->data);
- }
- DBUG_DUMP("key", (uchar*) to->data, to->size);
- return to;
-}
-
-
-int ha_berkeley::rnd_pos(byte * buf, byte *pos)
-{
- DBT db_pos;
-
- DBUG_ENTER("ha_berkeley::rnd_pos");
- statistic_increment(table->in_use->status_var.ha_read_rnd_count,
- &LOCK_status);
- active_index= MAX_KEY;
- DBUG_RETURN(read_row(file->get(file, transaction,
- get_pos(&db_pos, pos),
- &current_row, 0),
- (char*) buf, primary_key, &current_row, (DBT*) 0, 0));
-}
-
-/*
- Set a reference to the current record in (ref,ref_length).
-
- SYNOPSIS
- ha_berkeley::position()
- record The current record buffer
-
- DESCRIPTION
- The BDB handler stores the primary key in (ref,ref_length).
- There is either an explicit primary key, or an implicit (hidden)
- primary key.
- During open(), 'ref_length' is calculated as the maximum primary
- key length. When an actual key is shorter than that, the rest of
- the buffer must be cleared out. The row cannot be identified, if
- garbage follows behind the end of the key. There is no length
- field for the current key, so that the whole ref_length is used
- for comparison.
-
- RETURN
- nothing
-*/
-
-void ha_berkeley::position(const byte *record)
-{
- DBT key;
- DBUG_ENTER("ha_berkeley::position");
- if (hidden_primary_key)
- {
- DBUG_ASSERT(ref_length == BDB_HIDDEN_PRIMARY_KEY_LENGTH);
- memcpy_fixed(ref, (char*) current_ident, BDB_HIDDEN_PRIMARY_KEY_LENGTH);
- }
- else
- {
- create_key(&key, primary_key, (char*) ref, record);
- if (key.size < ref_length)
- bzero(ref + key.size, ref_length - key.size);
- }
- DBUG_VOID_RETURN;
-}
-
-
-int ha_berkeley::info(uint flag)
-{
- DBUG_ENTER("ha_berkeley::info");
- if (flag & HA_STATUS_VARIABLE)
- {
- records = share->rows + changed_rows; // Just to get optimisations right
- deleted = 0;
- }
- if ((flag & HA_STATUS_CONST) || version != share->version)
- {
- version=share->version;
- for (uint i=0 ; i < table->s->keys ; i++)
- {
- table->key_info[i].rec_per_key[table->key_info[i].key_parts-1]=
- share->rec_per_key[i];
- }
- }
- /* Don't return key if we got an error for the internal primary key */
- if (flag & HA_STATUS_ERRKEY && last_dup_key < table->s->keys)
- errkey= last_dup_key;
- DBUG_RETURN(0);
-}
-
-
-int ha_berkeley::extra(enum ha_extra_function operation)
-{
- switch (operation) {
- case HA_EXTRA_RESET:
- case HA_EXTRA_RESET_STATE:
- key_read=0;
- using_ignore=0;
- if (current_row.flags & (DB_DBT_MALLOC | DB_DBT_REALLOC))
- {
- current_row.flags=0;
- if (current_row.data)
- {
- free(current_row.data);
- current_row.data=0;
- }
- }
- break;
- case HA_EXTRA_KEYREAD:
- key_read=1; // Query satisfied with key
- break;
- case HA_EXTRA_NO_KEYREAD:
- key_read=0;
- break;
- case HA_EXTRA_IGNORE_DUP_KEY:
- using_ignore=1;
- break;
- case HA_EXTRA_NO_IGNORE_DUP_KEY:
- using_ignore=0;
- break;
- default:
- break;
- }
- return 0;
-}
-
-
-int ha_berkeley::reset(void)
-{
- ha_berkeley::extra(HA_EXTRA_RESET);
- key_read=0; // Reset to state after open
- return 0;
-}
-
-
-/*
- As MySQL will execute an external lock for every new table it uses
- we can use this to start the transactions.
- If we are in auto_commit mode we just need to start a transaction
- for the statement to be able to rollback the statement.
- If not, we have to start a master transaction if there doesn't exist
- one from before.
-*/
-
-int ha_berkeley::external_lock(THD *thd, int lock_type)
-{
- int error=0;
- berkeley_trx_data *trx=(berkeley_trx_data *)thd->ha_data[berkeley_hton.slot];
- DBUG_ENTER("ha_berkeley::external_lock");
- if (!trx)
- {
- thd->ha_data[berkeley_hton.slot]= trx= (berkeley_trx_data *)
- my_malloc(sizeof(*trx), MYF(MY_ZEROFILL));
- if (!trx)
- DBUG_RETURN(1);
- }
- if (lock_type != F_UNLCK)
- {
- if (!trx->bdb_lock_count++)
- {
- DBUG_ASSERT(trx->stmt == 0);
- transaction=0; // Safety
- /* First table lock, start transaction */
- if ((thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN |
- OPTION_TABLE_LOCK)) && !trx->all)
- {
- /* We have to start a master transaction */
- DBUG_PRINT("trans",("starting transaction all: options: 0x%lx",
- (ulong) thd->options));
- if ((error=txn_begin(db_env, 0, &trx->all, 0)))
- {
- trx->bdb_lock_count--; // We didn't get the lock
- DBUG_RETURN(error);
- }
- trans_register_ha(thd, TRUE, &berkeley_hton);
- if (thd->in_lock_tables)
- DBUG_RETURN(0); // Don't create stmt trans
- }
- DBUG_PRINT("trans",("starting transaction stmt"));
- if ((error=txn_begin(db_env, trx->all, &trx->stmt, 0)))
- {
- /* We leave the possible master transaction open */
- trx->bdb_lock_count--; // We didn't get the lock
- DBUG_RETURN(error);
- }
- trans_register_ha(thd, FALSE, &berkeley_hton);
- }
- transaction= trx->stmt;
- }
- else
- {
- lock.type=TL_UNLOCK; // Unlocked
- thread_safe_add(share->rows, changed_rows, &share->mutex);
- changed_rows=0;
- if (!--trx->bdb_lock_count)
- {
- if (trx->stmt)
- {
- /*
- F_UNLCK is done without a transaction commit / rollback.
- This happens if the thread didn't update any rows
- We must in this case commit the work to keep the row locks
- */
- DBUG_PRINT("trans",("commiting non-updating transaction"));
- error= txn_commit(trx->stmt,0);
- trx->stmt= transaction= 0;
- }
- }
- }
- DBUG_RETURN(error);
-}
-
-
-/*
- When using LOCK TABLE's external_lock is only called when the actual
- TABLE LOCK is done.
- Under LOCK TABLES, each used tables will force a call to start_stmt.
-*/
-
-int ha_berkeley::start_stmt(THD *thd, thr_lock_type lock_type)
-{
- int error=0;
- DBUG_ENTER("ha_berkeley::start_stmt");
- berkeley_trx_data *trx=(berkeley_trx_data *)thd->ha_data[berkeley_hton.slot];
- DBUG_ASSERT(trx);
- /*
- note that trx->stmt may have been already initialized as start_stmt()
- is called for *each table* not for each storage engine,
- and there could be many bdb tables referenced in the query
- */
- if (!trx->stmt)
- {
- DBUG_PRINT("trans",("starting transaction stmt"));
- error=txn_begin(db_env, trx->all, &trx->stmt, 0);
- trans_register_ha(thd, FALSE, &berkeley_hton);
- }
- transaction= trx->stmt;
- DBUG_RETURN(error);
-}
-
-
-/*
- The idea with handler::store_lock() is the following:
-
- The statement decided which locks we should need for the table
- for updates/deletes/inserts we get WRITE locks, for SELECT... we get
- read locks.
-
- Before adding the lock into the table lock handler (see thr_lock.c)
- mysqld calls store lock with the requested locks. Store lock can now
- modify a write lock to a read lock (or some other lock), ignore the
- lock (if we don't want to use MySQL table locks at all) or add locks
- for many tables (like we do when we are using a MERGE handler).
-
- Berkeley DB changes all WRITE locks to TL_WRITE_ALLOW_WRITE (which
- signals that we are doing WRITES, but we are still allowing other
- reader's and writer's.
-
- When releasing locks, store_lock() are also called. In this case one
- usually doesn't have to do anything.
-
- In some exceptional cases MySQL may send a request for a TL_IGNORE;
- This means that we are requesting the same lock as last time and this
- should also be ignored. (This may happen when someone does a flush
- table when we have opened a part of the tables, in which case mysqld
- closes and reopens the tables and tries to get the same locks at last
- time). In the future we will probably try to remove this.
-*/
-
-
-THR_LOCK_DATA **ha_berkeley::store_lock(THD *thd, THR_LOCK_DATA **to,
- enum thr_lock_type lock_type)
-{
- if (lock_type != TL_IGNORE && lock.type == TL_UNLOCK)
- {
- /* If we are not doing a LOCK TABLE, then allow multiple writers */
- if ((lock_type >= TL_WRITE_CONCURRENT_INSERT &&
- lock_type <= TL_WRITE) &&
- !thd->in_lock_tables)
- lock_type = TL_WRITE_ALLOW_WRITE;
- lock.type= lock_type;
- }
- *to++= &lock;
- return to;
-}
-
-
-static int create_sub_table(const char *table_name, const char *sub_name,
- DBTYPE type, int flags)
-{
- int error;
- DB *file;
- DBUG_ENTER("create_sub_table");
- DBUG_PRINT("enter",("sub_name: %s flags: %d",sub_name, flags));
-
- if (!(error=db_create(&file, db_env, 0)))
- {
- file->set_flags(file, flags);
- error=(file->open(file, NULL, table_name, sub_name, type,
- DB_THREAD | DB_CREATE, my_umask));
- if (error)
- {
- DBUG_PRINT("error",("Got error: %d when opening table '%s'",error, /* purecov: inspected */
- table_name)); /* purecov: inspected */
- (void) file->remove(file,table_name,NULL,0); /* purecov: inspected */
- }
- else
- (void) file->close(file,0);
- }
- else
- {
- DBUG_PRINT("error",("Got error: %d when creting table",error)); /* purecov: inspected */
- }
- if (error)
- my_errno=error; /* purecov: inspected */
- DBUG_RETURN(error);
-}
-
-
-int ha_berkeley::create(const char *name, register TABLE *form,
- HA_CREATE_INFO *create_info)
-{
- char name_buff[FN_REFLEN];
- char part[7];
- uint index=1;
- int error;
- DBUG_ENTER("ha_berkeley::create");
-
- fn_format(name_buff,name,"", ha_berkeley_ext,2 | 4);
-
- /* Create the main table that will hold the real rows */
- if ((error= create_sub_table(name_buff,"main",DB_BTREE,0)))
- DBUG_RETURN(error); /* purecov: inspected */
-
- primary_key= table->s->primary_key;
- /* Create the keys */
- for (uint i=0; i < form->s->keys; i++)
- {
- if (i != primary_key)
- {
- sprintf(part,"key%02d",index++);
- if ((error= create_sub_table(name_buff, part, DB_BTREE,
- (table->key_info[i].flags & HA_NOSAME) ? 0 :
- DB_DUP)))
- DBUG_RETURN(error); /* purecov: inspected */
- }
- }
-
- /* Create the status block to save information from last status command */
- /* Is DB_BTREE the best option here ? (QUEUE can't be used in sub tables) */
-
- DB *status_block;
- if (!(error=(db_create(&status_block, db_env, 0))))
- {
- if (!(error=(status_block->open(status_block, NULL, name_buff,
- "status", DB_BTREE, DB_CREATE, 0))))
- {
- char rec_buff[4+MAX_KEY*4];
- uint length= 4+ table->s->keys*4;
- bzero(rec_buff, length);
- error= write_status(status_block, rec_buff, length);
- status_block->close(status_block,0);
- }
- }
- DBUG_RETURN(error);
-}
-
-
-
-int ha_berkeley::delete_table(const char *name)
-{
- int error;
- char name_buff[FN_REFLEN];
- DBUG_ENTER("delete_table");
- if ((error=db_create(&file, db_env, 0)))
- my_errno=error; /* purecov: inspected */
- else
- error=file->remove(file,fn_format(name_buff,name,"",ha_berkeley_ext,2 | 4),
- NULL,0);
- file=0; // Safety
- DBUG_RETURN(error);
-}
-
-
-int ha_berkeley::rename_table(const char * from, const char * to)
-{
- int error;
- char from_buff[FN_REFLEN];
- char to_buff[FN_REFLEN];
-
- if ((error= db_create(&file, db_env, 0)))
- my_errno= error;
- else
- {
- /* On should not do a file->close() after rename returns */
- error= file->rename(file,
- fn_format(from_buff, from, "", ha_berkeley_ext, 2 | 4),
- NULL, fn_format(to_buff, to, "", ha_berkeley_ext,
- 2 | 4), 0);
- }
- return error;
-}
-
-
-/*
- How many seeks it will take to read through the table
- This is to be comparable to the number returned by records_in_range so
- that we can decide if we should scan the table or use keys.
-*/
-
-double ha_berkeley::scan_time()
-{
- return rows2double(records/3);
-}
-
-ha_rows ha_berkeley::records_in_range(uint keynr, key_range *start_key,
- key_range *end_key)
-{
- DBT key;
- DB_KEY_RANGE start_range, end_range;
- DB *kfile=key_file[keynr];
- double start_pos,end_pos,rows;
- bool error;
- KEY *key_info= &table->key_info[keynr];
- DBUG_ENTER("ha_berkeley::records_in_range");
-
- /* Ensure we get maximum range, even for varchar keys with different space */
- key_info->handler.bdb_return_if_eq= -1;
- error= ((start_key && kfile->key_range(kfile,transaction,
- pack_key(&key, keynr, key_buff,
- start_key->key,
- start_key->length),
- &start_range,0)));
- if (error)
- {
- key_info->handler.bdb_return_if_eq= 0;
- // Better than returning an error
- DBUG_RETURN(HA_BERKELEY_RANGE_COUNT); /* purecov: inspected */
- }
- key_info->handler.bdb_return_if_eq= 1;
- error= (end_key && kfile->key_range(kfile,transaction,
- pack_key(&key, keynr, key_buff,
- end_key->key,
- end_key->length),
- &end_range,0));
- key_info->handler.bdb_return_if_eq= 0;
- if (error)
- {
- // Better than returning an error
- DBUG_RETURN(HA_BERKELEY_RANGE_COUNT); /* purecov: inspected */
- }
-
- if (!start_key)
- start_pos= 0.0;
- else if (start_key->flag == HA_READ_KEY_EXACT)
- start_pos=start_range.less;
- else
- start_pos=start_range.less+start_range.equal;
-
- if (!end_key)
- end_pos= 1.0;
- else if (end_key->flag == HA_READ_BEFORE_KEY)
- end_pos=end_range.less;
- else
- end_pos=end_range.less+end_range.equal;
- rows=(end_pos-start_pos)*records;
- DBUG_PRINT("exit",("rows: %g",rows));
- DBUG_RETURN((ha_rows)(rows <= 1.0 ? 1 : rows));
-}
-
-
-ulonglong ha_berkeley::get_auto_increment()
-{
- ulonglong nr=1; // Default if error or new key
- int error;
- (void) ha_berkeley::extra(HA_EXTRA_KEYREAD);
-
- /* Set 'active_index' */
- ha_berkeley::index_init(table->s->next_number_index);
-
- if (!table->s->next_number_key_offset)
- { // Autoincrement at key-start
- error=ha_berkeley::index_last(table->record[1]);
- }
- else
- {
- DBT row,old_key;
- bzero((char*) &row,sizeof(row));
- KEY *key_info= &table->key_info[active_index];
-
- /* Reading next available number for a sub key */
- ha_berkeley::create_key(&last_key, active_index,
- key_buff, table->record[0],
- table->s->next_number_key_offset);
- /* Store for compare */
- memcpy(old_key.data=key_buff2, key_buff, (old_key.size=last_key.size));
- old_key.app_private=(void*) key_info;
- error=1;
- {
- /* Modify the compare so that we will find the next key */
- key_info->handler.bdb_return_if_eq= 1;
- /* We lock the next key as the new key will probl. be on the same page */
- error=cursor->c_get(cursor, &last_key, &row, DB_SET_RANGE | DB_RMW);
- key_info->handler.bdb_return_if_eq= 0;
- if (!error || error == DB_NOTFOUND)
- {
- /*
- Now search go one step back and then we should have found the
- biggest key with the given prefix
- */
- error=1;
- if (!cursor->c_get(cursor, &last_key, &row, DB_PREV | DB_RMW) &&
- !berkeley_cmp_packed_key(key_file[active_index], &old_key,
- &last_key))
- {
- error=0; // Found value
- unpack_key((char*) table->record[1], &last_key, active_index);
- }
- }
- }
- }
- if (!error)
- nr= (ulonglong)
- table->next_number_field->val_int_offset(table->s->rec_buff_length)+1;
- ha_berkeley::index_end();
- (void) ha_berkeley::extra(HA_EXTRA_NO_KEYREAD);
- return nr;
-}
-
-void ha_berkeley::print_error(int error, myf errflag)
-{
- if (error == DB_LOCK_DEADLOCK)
- error=HA_ERR_LOCK_DEADLOCK;
- handler::print_error(error,errflag);
-}
-
-/****************************************************************************
- Analyzing, checking, and optimizing tables
-****************************************************************************/
-
-#ifdef NOT_YET
-static void print_msg(THD *thd, const char *table_name, const char *op_name,
- const char *msg_type, const char *fmt, ...)
-{
- Protocol *protocol= thd->protocol;
- char msgbuf[256];
- msgbuf[0] = 0;
- va_list args;
- va_start(args,fmt);
-
- my_vsnprintf(msgbuf, sizeof(msgbuf), fmt, args);
- msgbuf[sizeof(msgbuf) - 1] = 0; // healthy paranoia
- DBUG_PRINT(msg_type,("message: %s",msgbuf));
-
- protocol->set_nfields(4);
- protocol->prepare_for_resend();
- protocol->store(table_name);
- protocol->store(op_name);
- protocol->store(msg_type);
- protocol->store(msgbuf);
- if (protocol->write())
- thd->killed=THD::KILL_CONNECTION;
-}
-#endif
-
-int ha_berkeley::analyze(THD* thd, HA_CHECK_OPT* check_opt)
-{
- uint i;
- DB_BTREE_STAT *stat=0;
- DB_TXN_STAT *txn_stat_ptr= 0;
- berkeley_trx_data *trx=(berkeley_trx_data *)thd->ha_data[berkeley_hton.slot];
- DBUG_ASSERT(trx);
-
- /*
- Original bdb documentation says:
- "The DB->stat method cannot be transaction-protected.
- For this reason, it should be called in a thread of
- control that has no open cursors or active transactions."
- So, let's check if there are any changes have been done since
- the beginning of the transaction..
- */
-
- if (!db_env->txn_stat(db_env, &txn_stat_ptr, 0) &&
- txn_stat_ptr && txn_stat_ptr->st_nactive>=2)
- {
- DB_TXN_ACTIVE *atxn_stmt= 0, *atxn_all= 0;
-
- u_int32_t all_id= trx->all->id(trx->all);
- u_int32_t stmt_id= trx->stmt->id(trx->stmt);
-
- DB_TXN_ACTIVE *cur= txn_stat_ptr->st_txnarray;
- DB_TXN_ACTIVE *end= cur + txn_stat_ptr->st_nactive;
- for (; cur!=end && (!atxn_stmt || !atxn_all); cur++)
- {
- if (cur->txnid==all_id) atxn_all= cur;
- if (cur->txnid==stmt_id) atxn_stmt= cur;
- }
-
- if (atxn_stmt && atxn_all &&
- log_compare(&atxn_stmt->lsn,&atxn_all->lsn))
- {
- free(txn_stat_ptr);
- return HA_ADMIN_REJECT;
- }
- free(txn_stat_ptr);
- }
-
- for (i=0 ; i < table->s->keys ; i++)
- {
- if (stat)
- {
- free(stat);
- stat=0;
- }
- if ((key_file[i]->stat)(key_file[i], (void*) &stat, 0))
- goto err; /* purecov: inspected */
- share->rec_per_key[i]= (stat->bt_ndata /
- (stat->bt_nkeys ? stat->bt_nkeys : 1));
- }
- /* A hidden primary key is not in key_file[] */
- if (hidden_primary_key)
- {
- if (stat)
- {
- free(stat);
- stat=0;
- }
- if ((file->stat)(file, (void*) &stat, 0))
- goto err; /* purecov: inspected */
- }
- pthread_mutex_lock(&share->mutex);
- share->rows=stat->bt_ndata;
- share->status|=STATUS_BDB_ANALYZE; // Save status on close
- share->version++; // Update stat in table
- pthread_mutex_unlock(&share->mutex);
- update_status(share,table); // Write status to file
- if (stat)
- free(stat);
- return ((share->status & STATUS_BDB_ANALYZE) ? HA_ADMIN_FAILED :
- HA_ADMIN_OK);
-
-err:
- if (stat) /* purecov: inspected */
- free(stat); /* purecov: inspected */
- return HA_ADMIN_FAILED; /* purecov: inspected */
-}
-
-int ha_berkeley::optimize(THD* thd, HA_CHECK_OPT* check_opt)
-{
- return ha_berkeley::analyze(thd,check_opt);
-}
-
-
-int ha_berkeley::check(THD* thd, HA_CHECK_OPT* check_opt)
-{
- DBUG_ENTER("ha_berkeley::check");
-
- DBUG_RETURN(HA_ADMIN_NOT_IMPLEMENTED);
-
-#ifdef NOT_YET
- char name_buff[FN_REFLEN];
- int error;
- DB *tmp_file;
- /*
- To get this to work we need to ensure that no running transaction is
- using the table. We also need to create a new environment without
- locking for this.
- */
-
- /* We must open the file again to be able to check it! */
- if ((error=db_create(&tmp_file, db_env, 0)))
- {
- print_msg(thd, table->real_name, "check", "error",
- "Got error %d creating environment",error);
- DBUG_RETURN(HA_ADMIN_FAILED);
- }
-
- /* Compare the overall structure */
- tmp_file->set_bt_compare(tmp_file,
- (hidden_primary_key ? berkeley_cmp_hidden_key :
- berkeley_cmp_packed_key));
- tmp_file->app_private= (void*) (table->key_info+table->primary_key);
- fn_format(name_buff,share->table_name,"", ha_berkeley_ext, 2 | 4);
- if ((error=tmp_file->verify(tmp_file, name_buff, NullS, (FILE*) 0,
- hidden_primary_key ? 0 : DB_NOORDERCHK)))
- {
- print_msg(thd, table->real_name, "check", "error",
- "Got error %d checking file structure",error);
- tmp_file->close(tmp_file,0);
- DBUG_RETURN(HA_ADMIN_CORRUPT);
- }
-
- /* Check each index */
- tmp_file->set_bt_compare(tmp_file, berkeley_cmp_packed_key);
- for (uint index=0,i=0 ; i < table->keys ; i++)
- {
- char part[7];
- if (i == primary_key)
- strmov(part,"main");
- else
- sprintf(part,"key%02d",++index);
- tmp_file->app_private= (void*) (table->key_info+i);
- if ((error=tmp_file->verify(tmp_file, name_buff, part, (FILE*) 0,
- DB_ORDERCHKONLY)))
- {
- print_msg(thd, table->real_name, "check", "error",
- "Key %d was not in order (Error: %d)",
- index+ test(i >= primary_key),
- error);
- tmp_file->close(tmp_file,0);
- DBUG_RETURN(HA_ADMIN_CORRUPT);
- }
- }
- tmp_file->close(tmp_file,0);
- DBUG_RETURN(HA_ADMIN_OK);
-#endif
-}
-
-/****************************************************************************
- Handling the shared BDB_SHARE structure that is needed to provide table
- locking.
-****************************************************************************/
-
-static byte* bdb_get_key(BDB_SHARE *share,uint *length,
- my_bool not_used __attribute__((unused)))
-{
- *length=share->table_name_length;
- return (byte*) share->table_name;
-}
-
-static BDB_SHARE *get_share(const char *table_name, TABLE *table)
-{
- BDB_SHARE *share;
- pthread_mutex_lock(&bdb_mutex);
- uint length=(uint) strlen(table_name);
- if (!(share=(BDB_SHARE*) hash_search(&bdb_open_tables, (byte*) table_name,
- length)))
- {
- ulong *rec_per_key;
- char *tmp_name;
- DB **key_file;
- u_int32_t *key_type;
- uint keys= table->s->keys;
-
- if ((share=(BDB_SHARE *)
- my_multi_malloc(MYF(MY_WME | MY_ZEROFILL),
- &share, sizeof(*share),
- &rec_per_key, keys * sizeof(ha_rows),
- &tmp_name, length+1,
- &key_file, (keys+1) * sizeof(*key_file),
- &key_type, (keys+1) * sizeof(u_int32_t),
- NullS)))
- {
- share->rec_per_key = rec_per_key;
- share->table_name = tmp_name;
- share->table_name_length=length;
- strmov(share->table_name,table_name);
- share->key_file = key_file;
- share->key_type = key_type;
- if (my_hash_insert(&bdb_open_tables, (byte*) share))
- {
- pthread_mutex_unlock(&bdb_mutex); /* purecov: inspected */
- my_free((gptr) share,0); /* purecov: inspected */
- return 0; /* purecov: inspected */
- }
- thr_lock_init(&share->lock);
- pthread_mutex_init(&share->mutex,MY_MUTEX_INIT_FAST);
- }
- }
- pthread_mutex_unlock(&bdb_mutex);
- return share;
-}
-
-static int free_share(BDB_SHARE *share, TABLE *table, uint hidden_primary_key,
- bool mutex_is_locked)
-{
- int error, result = 0;
- uint keys= table->s->keys + test(hidden_primary_key);
- pthread_mutex_lock(&bdb_mutex);
- if (mutex_is_locked)
- pthread_mutex_unlock(&share->mutex); /* purecov: inspected */
- if (!--share->use_count)
- {
- DB **key_file = share->key_file;
- update_status(share,table);
- /* this does share->file->close() implicitly */
- for (uint i=0; i < keys; i++)
- {
- if (key_file[i] && (error=key_file[i]->close(key_file[i],0)))
- result=error; /* purecov: inspected */
- }
- if (share->status_block &&
- (error = share->status_block->close(share->status_block,0)))
- result = error; /* purecov: inspected */
- hash_delete(&bdb_open_tables, (byte*) share);
- thr_lock_delete(&share->lock);
- pthread_mutex_destroy(&share->mutex);
- my_free((gptr) share, MYF(0));
- }
- pthread_mutex_unlock(&bdb_mutex);
- return result;
-}
-
-/*
- Get status information that is stored in the 'status' sub database
- and the max used value for the hidden primary key.
-*/
-
-void ha_berkeley::get_status()
-{
- if (!test_all_bits(share->status,(STATUS_PRIMARY_KEY_INIT |
- STATUS_ROW_COUNT_INIT)))
- {
- pthread_mutex_lock(&share->mutex);
- if (!(share->status & STATUS_PRIMARY_KEY_INIT))
- {
- (void) extra(HA_EXTRA_KEYREAD);
- index_init(primary_key);
- if (!index_last(table->record[1]))
- share->auto_ident=uint5korr(current_ident);
- index_end();
- (void) extra(HA_EXTRA_NO_KEYREAD);
- }
- if (! share->status_block)
- {
- char name_buff[FN_REFLEN];
- uint open_mode= (((table->db_stat & HA_READ_ONLY) ? DB_RDONLY : 0)
- | DB_THREAD);
- fn_format(name_buff, share->table_name,"", ha_berkeley_ext, 2 | 4);
- if (!db_create(&share->status_block, db_env, 0))
- {
- if (share->status_block->open(share->status_block, NULL, name_buff,
- "status", DB_BTREE, open_mode, 0))
- {
- share->status_block->close(share->status_block, 0); /* purecov: inspected */
- share->status_block=0; /* purecov: inspected */
- }
- }
- }
- if (!(share->status & STATUS_ROW_COUNT_INIT) && share->status_block)
- {
- share->org_rows= share->rows=
- table->s->max_rows ? table->s->max_rows : HA_BERKELEY_MAX_ROWS;
- if (!share->status_block->cursor(share->status_block, 0, &cursor, 0))
- {
- DBT row;
- char rec_buff[64];
- bzero((char*) &row,sizeof(row));
- bzero((char*) &last_key,sizeof(last_key));
- row.data=rec_buff;
- row.ulen=sizeof(rec_buff);
- row.flags=DB_DBT_USERMEM;
- if (!cursor->c_get(cursor, &last_key, &row, DB_FIRST))
- {
- uint i;
- uchar *pos=(uchar*) row.data;
- share->org_rows=share->rows=uint4korr(pos); pos+=4;
- for (i=0 ; i < table->s->keys ; i++)
- {
- share->rec_per_key[i]=uint4korr(pos);
- pos+=4;
- }
- }
- cursor->c_close(cursor);
- }
- cursor=0; // Safety
- }
- share->status|= STATUS_PRIMARY_KEY_INIT | STATUS_ROW_COUNT_INIT;
- pthread_mutex_unlock(&share->mutex);
- }
-}
-
-
-static int write_status(DB *status_block, char *buff, uint length)
-{
- DBT row,key;
- int error;
- const char *key_buff="status";
-
- bzero((char*) &row,sizeof(row));
- bzero((char*) &key,sizeof(key));
- row.data=buff;
- key.data=(void*) key_buff;
- key.size=sizeof(key_buff);
- row.size=length;
- error=status_block->put(status_block, 0, &key, &row, 0);
- return error;
-}
-
-
-static void update_status(BDB_SHARE *share, TABLE *table)
-{
- DBUG_ENTER("update_status");
- if (share->rows != share->org_rows ||
- (share->status & STATUS_BDB_ANALYZE))
- {
- pthread_mutex_lock(&share->mutex);
- if (!share->status_block)
- {
- /*
- Create sub database 'status' if it doesn't exist from before
- (This '*should*' always exist for table created with MySQL)
- */
-
- char name_buff[FN_REFLEN]; /* purecov: inspected */
- if (db_create(&share->status_block, db_env, 0)) /* purecov: inspected */
- goto end; /* purecov: inspected */
- share->status_block->set_flags(share->status_block,0); /* purecov: inspected */
- if (share->status_block->open(share->status_block, NULL,
- fn_format(name_buff,share->table_name,"",
- ha_berkeley_ext,2 | 4),
- "status", DB_BTREE,
- DB_THREAD | DB_CREATE, my_umask)) /* purecov: inspected */
- goto end; /* purecov: inspected */
- }
- {
- char rec_buff[4+MAX_KEY*4], *pos=rec_buff;
- int4store(pos,share->rows); pos+=4;
- for (uint i=0 ; i < table->s->keys ; i++)
- {
- int4store(pos,share->rec_per_key[i]); pos+=4;
- }
- DBUG_PRINT("info",("updating status for %s",share->table_name));
- (void) write_status(share->status_block, rec_buff,
- (uint) (pos-rec_buff));
- share->status&= ~STATUS_BDB_ANALYZE;
- share->org_rows=share->rows;
- }
-end:
- pthread_mutex_unlock(&share->mutex);
- }
- DBUG_VOID_RETURN;
-}
-
-
-/*
- Return an estimated of the number of rows in the table.
- Used when sorting to allocate buffers and by the optimizer.
-*/
-
-ha_rows ha_berkeley::estimate_rows_upper_bound()
-{
- return share->rows + HA_BERKELEY_EXTRA_ROWS;
-}
-
-int ha_berkeley::cmp_ref(const byte *ref1, const byte *ref2)
-{
- if (hidden_primary_key)
- {
- ulonglong a=uint5korr((char*) ref1);
- ulonglong b=uint5korr((char*) ref2);
- return a < b ? -1 : (a > b ? 1 : 0);
- }
-
- int result;
- Field *field;
- KEY *key_info=table->key_info+table->s->primary_key;
- KEY_PART_INFO *key_part=key_info->key_part;
- KEY_PART_INFO *end=key_part+key_info->key_parts;
-
- for (; key_part != end; key_part++)
- {
- field= key_part->field;
- result= field->pack_cmp((const char*)ref1, (const char*)ref2,
- key_part->length, 0);
- if (result)
- return result;
- ref1+= field->packed_col_length((const char*)ref1, key_part->length);
- ref2+= field->packed_col_length((const char*)ref2, key_part->length);
- }
-
- return 0;
-}
-
-#endif /* HAVE_BERKELEY_DB */
diff --git a/sql/ha_berkeley.h b/sql/ha_berkeley.h
deleted file mode 100644
index 336c90f009a..00000000000
--- a/sql/ha_berkeley.h
+++ /dev/null
@@ -1,163 +0,0 @@
-/* Copyright (C) 2000-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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
-
-
-#ifdef USE_PRAGMA_INTERFACE
-#pragma interface /* gcc class implementation */
-#endif
-
-/* class for the the myisam handler */
-
-#include <db.h>
-
-#define BDB_HIDDEN_PRIMARY_KEY_LENGTH 5
-
-typedef struct st_berkeley_share {
- ulonglong auto_ident;
- ha_rows rows, org_rows;
- ulong *rec_per_key;
- THR_LOCK lock;
- pthread_mutex_t mutex;
- char *table_name;
- DB *status_block, *file, **key_file;
- u_int32_t *key_type;
- uint table_name_length,use_count;
- uint status,version;
- uint ref_length;
- bool fixed_length_primary_key, fixed_length_row;
-} BDB_SHARE;
-
-
-class ha_berkeley: public handler
-{
- THR_LOCK_DATA lock;
- DBT last_key,current_row;
- gptr alloc_ptr;
- byte *rec_buff;
- char *key_buff, *key_buff2, *primary_key_buff;
- DB *file, **key_file;
- DB_TXN *transaction;
- u_int32_t *key_type;
- DBC *cursor;
- BDB_SHARE *share;
- ulong int_table_flags;
- ulong alloced_rec_buff_length;
- ulong changed_rows;
- uint primary_key,last_dup_key, hidden_primary_key, version;
- bool key_read, using_ignore;
- bool fix_rec_buff_for_blob(ulong length);
- byte current_ident[BDB_HIDDEN_PRIMARY_KEY_LENGTH];
-
- ulong max_row_length(const byte *buf);
- int pack_row(DBT *row,const byte *record, bool new_row);
- void unpack_row(char *record, DBT *row);
- void unpack_key(char *record, DBT *key, uint index);
- DBT *create_key(DBT *key, uint keynr, char *buff, const byte *record,
- int key_length = MAX_KEY_LENGTH);
- DBT *pack_key(DBT *key, uint keynr, char *buff, const byte *key_ptr,
- uint key_length);
- int remove_key(DB_TXN *trans, uint keynr, const byte *record, DBT *prim_key);
- int remove_keys(DB_TXN *trans,const byte *record, DBT *new_record,
- DBT *prim_key, key_map *keys);
- int restore_keys(DB_TXN *trans, key_map *changed_keys, uint primary_key,
- const byte *old_row, DBT *old_key,
- const byte *new_row, DBT *new_key);
- int key_cmp(uint keynr, const byte * old_row, const byte * new_row);
- int update_primary_key(DB_TXN *trans, bool primary_key_changed,
- const byte * old_row, DBT *old_key,
- const byte * new_row, DBT *prim_key,
- bool local_using_ignore);
- int read_row(int error, char *buf, uint keynr, DBT *row, DBT *key, bool);
- DBT *get_pos(DBT *to, byte *pos);
-
- public:
- ha_berkeley(TABLE *table_arg);
- ~ha_berkeley() {}
- const char *table_type() const { return "BerkeleyDB"; }
- ulong index_flags(uint idx, uint part, bool all_parts) const;
- const char *index_type(uint key_number) { return "BTREE"; }
- const char **bas_ext() const;
- ulong table_flags(void) const { return int_table_flags; }
- uint max_supported_keys() const { return MAX_KEY-1; }
- uint extra_rec_buf_length() { return BDB_HIDDEN_PRIMARY_KEY_LENGTH; }
- ha_rows estimate_rows_upper_bound();
- uint max_supported_key_length() const { return UINT_MAX32; }
- uint max_supported_key_part_length() const { return UINT_MAX32; }
-
- const key_map *keys_to_use_for_scanning() { return &key_map_full; }
- bool has_transactions() { return 1;}
-
- int open(const char *name, int mode, uint test_if_locked);
- int close(void);
- double scan_time();
- int write_row(byte * buf);
- int update_row(const byte * old_data, byte * new_data);
- int delete_row(const byte * buf);
- int index_init(uint index);
- int index_end();
- int index_read(byte * buf, const byte * key,
- uint key_len, enum ha_rkey_function find_flag);
- int index_read_idx(byte * buf, uint index, const byte * key,
- uint key_len, enum ha_rkey_function find_flag);
- int index_read_last(byte * buf, const byte * key, uint key_len);
- int index_next(byte * buf);
- int index_next_same(byte * buf, const byte *key, uint keylen);
- int index_prev(byte * buf);
- int index_first(byte * buf);
- int index_last(byte * buf);
- int rnd_init(bool scan);
- int rnd_end();
- int rnd_next(byte *buf);
- int rnd_pos(byte * buf, byte *pos);
- void position(const byte *record);
- int info(uint);
- int extra(enum ha_extra_function operation);
- int reset(void);
- int external_lock(THD *thd, int lock_type);
- int start_stmt(THD *thd, thr_lock_type lock_type);
- void position(byte *record);
- int analyze(THD* thd,HA_CHECK_OPT* check_opt);
- int optimize(THD* thd, HA_CHECK_OPT* check_opt);
- int check(THD* thd, HA_CHECK_OPT* check_opt);
-
- ha_rows records_in_range(uint inx, key_range *min_key, key_range *max_key);
- int create(const char *name, register TABLE *form,
- HA_CREATE_INFO *create_info);
- int delete_table(const char *name);
- int rename_table(const char* from, const char* to);
- THR_LOCK_DATA **store_lock(THD *thd, THR_LOCK_DATA **to,
- enum thr_lock_type lock_type);
-
- void get_status();
- void get_auto_primary_key(byte *to);
- ulonglong get_auto_increment();
- void print_error(int error, myf errflag);
- uint8 table_cache_type() { return HA_CACHE_TBL_TRANSACT; }
- bool primary_key_is_clustered() { return true; }
- int cmp_ref(const byte *ref1, const byte *ref2);
-};
-
-extern bool berkeley_shared_data;
-extern u_int32_t berkeley_init_flags,berkeley_env_flags, berkeley_lock_type,
- berkeley_lock_types[];
-extern ulong berkeley_cache_size, berkeley_max_lock, berkeley_log_buffer_size;
-extern char *berkeley_home, *berkeley_tmpdir, *berkeley_logdir;
-extern long berkeley_lock_scan_time;
-extern TYPELIB berkeley_lock_typelib;
-
-bool berkeley_init(void);
-bool berkeley_end(void);
-bool berkeley_flush_logs(void);
-int berkeley_show_logs(Protocol *protocol);
diff --git a/sql/ha_blackhole.cc b/sql/ha_blackhole.cc
deleted file mode 100644
index 01ede3d3bd2..00000000000
--- a/sql/ha_blackhole.cc
+++ /dev/null
@@ -1,339 +0,0 @@
-/* Copyright (C) 2005 MySQL AB
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; version 2 of the License.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
-
-
-#ifdef USE_PRAGMA_IMPLEMENTATION
-#pragma implementation // gcc: Class implementation
-#endif
-
-#include "mysql_priv.h"
-#ifdef HAVE_BLACKHOLE_DB
-#include "ha_blackhole.h"
-
-/* Static declarations for shared structures */
-
-static pthread_mutex_t blackhole_mutex;
-static HASH blackhole_open_tables;
-static int blackhole_init= FALSE;
-
-static st_blackhole_share *get_share(const char *table_name);
-static void free_share(st_blackhole_share *share);
-
-/* Blackhole storage engine handlerton */
-
-handlerton blackhole_hton= {
- "BLACKHOLE",
- SHOW_OPTION_YES,
- "/dev/null storage engine (anything you write to it disappears)",
- DB_TYPE_BLACKHOLE_DB,
- blackhole_db_init,
- 0, /* slot */
- 0, /* savepoint size. */
- NULL, /* close_connection */
- NULL, /* savepoint */
- NULL, /* rollback to savepoint */
- NULL, /* release savepoint */
- NULL, /* commit */
- NULL, /* rollback */
- NULL, /* prepare */
- NULL, /* recover */
- NULL, /* commit_by_xid */
- NULL, /* rollback_by_xid */
- NULL, /* create_cursor_read_view */
- NULL, /* set_cursor_read_view */
- NULL, /* close_cursor_read_view */
- HTON_CAN_RECREATE
-};
-
-/*****************************************************************************
-** BLACKHOLE tables
-*****************************************************************************/
-
-ha_blackhole::ha_blackhole(TABLE *table_arg)
- :handler(&blackhole_hton, table_arg)
-{}
-
-
-static const char *ha_blackhole_exts[] = {
- NullS
-};
-
-const char **ha_blackhole::bas_ext() const
-{
- return ha_blackhole_exts;
-}
-
-int ha_blackhole::open(const char *name, int mode, uint test_if_locked)
-{
- DBUG_ENTER("ha_blackhole::open");
-
- if (!(share= get_share(name)))
- DBUG_RETURN(HA_ERR_OUT_OF_MEM);
-
- thr_lock_data_init(&share->lock, &lock, NULL);
- DBUG_RETURN(0);
-}
-
-int ha_blackhole::close(void)
-{
- DBUG_ENTER("ha_blackhole::close");
- free_share(share);
- DBUG_RETURN(0);
-}
-
-int ha_blackhole::create(const char *name, TABLE *table_arg,
- HA_CREATE_INFO *create_info)
-{
- DBUG_ENTER("ha_blackhole::create");
- DBUG_RETURN(0);
-}
-
-const char *ha_blackhole::index_type(uint key_number)
-{
- DBUG_ENTER("ha_blackhole::index_type");
- DBUG_RETURN((table->key_info[key_number].flags & HA_FULLTEXT) ?
- "FULLTEXT" :
- (table->key_info[key_number].flags & HA_SPATIAL) ?
- "SPATIAL" :
- (table->key_info[key_number].algorithm == HA_KEY_ALG_RTREE) ?
- "RTREE" :
- "BTREE");
-}
-
-int ha_blackhole::write_row(byte * buf)
-{
- DBUG_ENTER("ha_blackhole::write_row");
- DBUG_RETURN(table->next_number_field ? update_auto_increment() : 0);
-}
-
-int ha_blackhole::rnd_init(bool scan)
-{
- DBUG_ENTER("ha_blackhole::rnd_init");
- DBUG_RETURN(0);
-}
-
-
-int ha_blackhole::rnd_next(byte *buf)
-{
- DBUG_ENTER("ha_blackhole::rnd_next");
- DBUG_RETURN(HA_ERR_END_OF_FILE);
-}
-
-
-int ha_blackhole::rnd_pos(byte * buf, byte *pos)
-{
- DBUG_ENTER("ha_blackhole::rnd_pos");
- DBUG_ASSERT(0);
- DBUG_RETURN(0);
-}
-
-
-void ha_blackhole::position(const byte *record)
-{
- DBUG_ENTER("ha_blackhole::position");
- DBUG_ASSERT(0);
- DBUG_VOID_RETURN;
-}
-
-
-int ha_blackhole::info(uint flag)
-{
- DBUG_ENTER("ha_blackhole::info");
-
- records= 0;
- deleted= 0;
- errkey= 0;
- mean_rec_length= 0;
- data_file_length= 0;
- index_file_length= 0;
- max_data_file_length= 0;
- delete_length= 0;
- if (flag & HA_STATUS_AUTO)
- auto_increment_value= 1;
- DBUG_RETURN(0);
-}
-
-int ha_blackhole::external_lock(THD *thd, int lock_type)
-{
- DBUG_ENTER("ha_blackhole::external_lock");
- DBUG_RETURN(0);
-}
-
-
-THR_LOCK_DATA **ha_blackhole::store_lock(THD *thd,
- THR_LOCK_DATA **to,
- enum thr_lock_type lock_type)
-{
- DBUG_ENTER("ha_blackhole::store_lock");
- if (lock_type != TL_IGNORE && lock.type == TL_UNLOCK)
- {
- if ((lock_type >= TL_WRITE_CONCURRENT_INSERT &&
- lock_type <= TL_WRITE) && !thd->in_lock_tables)
- lock_type = TL_WRITE_ALLOW_WRITE;
-
- if (lock_type == TL_READ_NO_INSERT && !thd->in_lock_tables)
- lock_type = TL_READ;
-
- lock.type= lock_type;
- }
- *to++= &lock;
- DBUG_RETURN(to);
-}
-
-
-int ha_blackhole::index_read(byte * buf, const byte * key,
- uint key_len, enum ha_rkey_function find_flag)
-{
- DBUG_ENTER("ha_blackhole::index_read");
- DBUG_RETURN(HA_ERR_END_OF_FILE);
-}
-
-
-int ha_blackhole::index_read_idx(byte * buf, uint idx, const byte * key,
- uint key_len, enum ha_rkey_function find_flag)
-{
- DBUG_ENTER("ha_blackhole::index_read_idx");
- DBUG_RETURN(HA_ERR_END_OF_FILE);
-}
-
-
-int ha_blackhole::index_read_last(byte * buf, const byte * key, uint key_len)
-{
- DBUG_ENTER("ha_blackhole::index_read_last");
- DBUG_RETURN(HA_ERR_END_OF_FILE);
-}
-
-
-int ha_blackhole::index_next(byte * buf)
-{
- DBUG_ENTER("ha_blackhole::index_next");
- DBUG_RETURN(HA_ERR_END_OF_FILE);
-}
-
-
-int ha_blackhole::index_prev(byte * buf)
-{
- DBUG_ENTER("ha_blackhole::index_prev");
- DBUG_RETURN(HA_ERR_END_OF_FILE);
-}
-
-
-int ha_blackhole::index_first(byte * buf)
-{
- DBUG_ENTER("ha_blackhole::index_first");
- DBUG_RETURN(HA_ERR_END_OF_FILE);
-}
-
-
-int ha_blackhole::index_last(byte * buf)
-{
- DBUG_ENTER("ha_blackhole::index_last");
- DBUG_RETURN(HA_ERR_END_OF_FILE);
-}
-
-
-static st_blackhole_share *get_share(const char *table_name)
-{
- st_blackhole_share *share;
- uint length;
-
- length= (uint) strlen(table_name);
- pthread_mutex_lock(&blackhole_mutex);
-
- if (!(share= (st_blackhole_share*) hash_search(&blackhole_open_tables,
- (byte*) table_name, length)))
- {
- if (!(share= (st_blackhole_share*) my_malloc(sizeof(st_blackhole_share) +
- length,
- MYF(MY_WME | MY_ZEROFILL))))
- goto error;
-
- share->table_name_length= length;
- strmov(share->table_name, table_name);
-
- if (my_hash_insert(&blackhole_open_tables, (byte*) share))
- {
- my_free((gptr) share, MYF(0));
- share= NULL;
- goto error;
- }
-
- thr_lock_init(&share->lock);
- }
- share->use_count++;
-
-error:
- pthread_mutex_unlock(&blackhole_mutex);
- return share;
-}
-
-static void free_share(st_blackhole_share *share)
-{
- pthread_mutex_lock(&blackhole_mutex);
- if (!--share->use_count)
- hash_delete(&blackhole_open_tables, (byte*) share);
- pthread_mutex_unlock(&blackhole_mutex);
-}
-
-
-static byte* blackhole_get_key(st_blackhole_share *share, uint *length,
- my_bool not_used __attribute__((unused)))
-{
- *length= share->table_name_length;
- return (byte*) share->table_name;
-}
-
-
-static void blackhole_free_key(st_blackhole_share *share)
-{
- thr_lock_delete(&share->lock);
- my_free((gptr) share, MYF(0));
-}
-
-
-bool blackhole_db_init()
-{
- DBUG_ENTER("blackhole_db_init");
- if (pthread_mutex_init(&blackhole_mutex, MY_MUTEX_INIT_FAST))
- goto error;
- if (hash_init(&blackhole_open_tables, &my_charset_bin, 32, 0, 0,
- (hash_get_key) blackhole_get_key,
- (hash_free_key) blackhole_free_key, 0))
- {
- VOID(pthread_mutex_destroy(&blackhole_mutex));
- }
- else
- {
- blackhole_init= TRUE;
- DBUG_RETURN(FALSE);
- }
-error:
- have_blackhole_db= SHOW_OPTION_DISABLED; // If we couldn't use handler
- DBUG_RETURN(TRUE);
-}
-
-
-bool blackhole_db_end()
-{
- if (blackhole_init)
- {
- hash_free(&blackhole_open_tables);
- VOID(pthread_mutex_destroy(&blackhole_mutex));
- }
- blackhole_init= 0;
- return FALSE;
-}
-
-#endif /* HAVE_BLACKHOLE_DB */
diff --git a/sql/ha_blackhole.h b/sql/ha_blackhole.h
deleted file mode 100644
index 45ed0351457..00000000000
--- a/sql/ha_blackhole.h
+++ /dev/null
@@ -1,99 +0,0 @@
-/* Copyright (C) 2005 MySQL AB
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; version 2 of the License.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
-
-#ifdef USE_PRAGMA_INTERFACE
-#pragma interface /* gcc class implementation */
-#endif
-
-
-/*
- Shared structure for correct LOCK operation
-*/
-struct st_blackhole_share {
- THR_LOCK lock;
- uint use_count;
- uint table_name_length;
- char table_name[1];
-};
-
-
-/*
- Class definition for the blackhole storage engine
- "Dumbest named feature ever"
-*/
-class ha_blackhole: public handler
-{
- THR_LOCK_DATA lock; /* MySQL lock */
- st_blackhole_share *share;
-
-public:
- ha_blackhole(TABLE *table_arg);
- ~ha_blackhole()
- {
- }
- /* The name that will be used for display purposes */
- const char *table_type() const { return "BLACKHOLE"; }
- /*
- The name of the index type that will be used for display
- don't implement this method unless you really have indexes
- */
- const char *index_type(uint key_number);
- const char **bas_ext() const;
- ulong table_flags() const
- {
- return(HA_NULL_IN_KEY | HA_CAN_FULLTEXT | HA_CAN_SQL_HANDLER |
- HA_DUPP_POS | HA_CAN_INDEX_BLOBS | HA_AUTO_PART_KEY |
- HA_FILE_BASED | HA_CAN_GEOMETRY | HA_READ_RND_SAME);
- }
- ulong index_flags(uint inx, uint part, bool all_parts) const
- {
- return ((table->key_info[inx].algorithm == HA_KEY_ALG_FULLTEXT) ?
- 0 : HA_READ_NEXT | HA_READ_PREV | HA_READ_RANGE |
- HA_READ_ORDER | HA_KEYREAD_ONLY);
- }
- /* The following defines can be increased if necessary */
-#define BLACKHOLE_MAX_KEY 64 /* Max allowed keys */
-#define BLACKHOLE_MAX_KEY_SEG 16 /* Max segments for key */
-#define BLACKHOLE_MAX_KEY_LENGTH 1000
- uint max_supported_keys() const { return BLACKHOLE_MAX_KEY; }
- uint max_supported_key_length() const { return BLACKHOLE_MAX_KEY_LENGTH; }
- uint max_supported_key_part_length() const { return BLACKHOLE_MAX_KEY_LENGTH; }
- int open(const char *name, int mode, uint test_if_locked);
- int close(void);
- int write_row(byte * buf);
- int rnd_init(bool scan);
- int rnd_next(byte *buf);
- int rnd_pos(byte * buf, byte *pos);
- int index_read(byte * buf, const byte * key,
- uint key_len, enum ha_rkey_function find_flag);
- int index_read_idx(byte * buf, uint idx, const byte * key,
- uint key_len, enum ha_rkey_function find_flag);
- int index_read_last(byte * buf, const byte * key, uint key_len);
- int index_next(byte * buf);
- int index_prev(byte * buf);
- int index_first(byte * buf);
- int index_last(byte * buf);
- void position(const byte *record);
- int info(uint flag);
- int external_lock(THD *thd, int lock_type);
- int create(const char *name, TABLE *table_arg,
- HA_CREATE_INFO *create_info);
- THR_LOCK_DATA **store_lock(THD *thd,
- THR_LOCK_DATA **to,
- enum thr_lock_type lock_type);
-};
-
-bool blackhole_db_init(void);
-bool blackhole_db_end(void);
diff --git a/sql/ha_federated.cc b/sql/ha_federated.cc
deleted file mode 100644
index 6944dc5a030..00000000000
--- a/sql/ha_federated.cc
+++ /dev/null
@@ -1,2961 +0,0 @@
-/* Copyright (C) 2004 MySQL AB
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; version 2 of the License.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
-
-/*
-
- MySQL Federated Storage Engine
-
- ha_federated.cc - MySQL Federated Storage Engine
- Patrick Galbraith and Brian Aker, 2004
-
- This is a handler which uses a foreign database as the data file, as
- opposed to a handler like MyISAM, which uses .MYD files locally.
-
- How this handler works
- ----------------------------------
- Normal database files are local and as such: You create a table called
- 'users', a file such as 'users.MYD' is created. A handler reads, inserts,
- deletes, updates data in this file. The data is stored in particular format,
- so to read, that data has to be parsed into fields, to write, fields have to
- be stored in this format to write to this data file.
-
- With MySQL Federated storage engine, there will be no local files
- for each table's data (such as .MYD). A foreign database will store
- the data that would normally be in this file. This will necessitate
- the use of MySQL client API to read, delete, update, insert this
- data. The data will have to be retrieve via an SQL call "SELECT *
- FROM users". Then, to read this data, it will have to be retrieved
- via mysql_fetch_row one row at a time, then converted from the
- column in this select into the format that the handler expects.
-
- The create table will simply create the .frm file, and within the
- "CREATE TABLE" SQL, there SHALL be any of the following :
-
- comment=scheme://username:password@hostname:port/database/tablename
- comment=scheme://username@hostname/database/tablename
- comment=scheme://username:password@hostname/database/tablename
- comment=scheme://username:password@hostname/database/tablename
-
- An example would be:
-
- comment=mysql://username:password@hostname:port/database/tablename
-
- ***IMPORTANT***
-
- This is a first release, conceptual release
- Only 'mysql://' is supported at this release.
-
-
- This comment connection string is necessary for the handler to be
- able to connect to the foreign server.
-
-
- The basic flow is this:
-
- SQL calls issues locally ->
- mysql handler API (data in handler format) ->
- mysql client API (data converted to SQL calls) ->
- foreign database -> mysql client API ->
- convert result sets (if any) to handler format ->
- handler API -> results or rows affected to local
-
- What this handler does and doesn't support
- ------------------------------------------
- * Tables MUST be created on the foreign server prior to any action on those
- tables via the handler, first version. IMPORTANT: IF you MUST use the
- federated storage engine type on the REMOTE end, MAKE SURE [ :) ] That
- the table you connect to IS NOT a table pointing BACK to your ORIGNAL
- table! You know and have heard the screaching of audio feedback? You
- know putting two mirror in front of each other how the reflection
- continues for eternity? Well, need I say more?!
- * There will not be support for transactions.
- * There is no way for the handler to know if the foreign database or table
- has changed. The reason for this is that this database has to work like a
- data file that would never be written to by anything other than the
- database. The integrity of the data in the local table could be breached
- if there was any change to the foreign database.
- * Support for SELECT, INSERT, UPDATE , DELETE, indexes.
- * No ALTER TABLE, DROP TABLE or any other Data Definition Language calls.
- * Prepared statements will not be used in the first implementation, it
- remains to to be seen whether the limited subset of the client API for the
- server supports this.
- * This uses SELECT, INSERT, UPDATE, DELETE and not HANDLER for its
- implementation.
- * This will not work with the query cache.
-
- Method calls
-
- A two column table, with one record:
-
- (SELECT)
-
- "SELECT * FROM foo"
- ha_federated::info
- ha_federated::scan_time:
- ha_federated::rnd_init: share->select_query SELECT * FROM foo
- ha_federated::extra
-
- <for every row of data retrieved>
- ha_federated::rnd_next
- ha_federated::convert_row_to_internal_format
- ha_federated::rnd_next
- </for every row of data retrieved>
-
- ha_federated::rnd_end
- ha_federated::extra
- ha_federated::reset
-
- (INSERT)
-
- "INSERT INTO foo (id, ts) VALUES (2, now());"
-
- ha_federated::write_row
-
- ha_federated::reset
-
- (UPDATE)
-
- "UPDATE foo SET ts = now() WHERE id = 1;"
-
- ha_federated::index_init
- ha_federated::index_read
- ha_federated::index_read_idx
- ha_federated::rnd_next
- ha_federated::convert_row_to_internal_format
- ha_federated::update_row
-
- ha_federated::extra
- ha_federated::extra
- ha_federated::extra
- ha_federated::external_lock
- ha_federated::reset
-
-
- How do I use this handler?
- --------------------------
- First of all, you need to build this storage engine:
-
- ./configure --with-federated-storage-engine
- make
-
- Next, to use this handler, it's very simple. You must
- have two databases running, either both on the same host, or
- on different hosts.
-
- One the server that will be connecting to the foreign
- host (client), you create your table as such:
-
- CREATE TABLE test_table (
- id int(20) NOT NULL auto_increment,
- name varchar(32) NOT NULL default '',
- other int(20) NOT NULL default '0',
- PRIMARY KEY (id),
- KEY name (name),
- KEY other_key (other))
- ENGINE="FEDERATED"
- DEFAULT CHARSET=latin1
- COMMENT='root@127.0.0.1:9306/federated/test_federated';
-
- Notice the "COMMENT" and "ENGINE" field? This is where you
- respectively set the engine type, "FEDERATED" and foreign
- host information, this being the database your 'client' database
- will connect to and use as the "data file". Obviously, the foreign
- database is running on port 9306, so you want to start up your other
- database so that it is indeed on port 9306, and your federated
- database on a port other than that. In my setup, I use port 5554
- for federated, and port 5555 for the foreign database.
-
- Then, on the foreign database:
-
- CREATE TABLE test_table (
- id int(20) NOT NULL auto_increment,
- name varchar(32) NOT NULL default '',
- other int(20) NOT NULL default '0',
- PRIMARY KEY (id),
- KEY name (name),
- KEY other_key (other))
- ENGINE="<NAME>" <-- whatever you want, or not specify
- DEFAULT CHARSET=latin1 ;
-
- This table is exactly the same (and must be exactly the same),
- except that it is not using the federated handler and does
- not need the URL.
-
-
- How to see the handler in action
- --------------------------------
-
- When developing this handler, I compiled the federated database with
- debugging:
-
- ./configure --with-federated-storage-engine
- --prefix=/home/mysql/mysql-build/federated/ --with-debug
-
- Once compiled, I did a 'make install' (not for the purpose of installing
- the binary, but to install all the files the binary expects to see in the
- diretory I specified in the build with --prefix,
- "/home/mysql/mysql-build/federated".
-
- Then, I started the foreign server:
-
- /usr/local/mysql/bin/mysqld_safe
- --user=mysql --log=/tmp/mysqld.5555.log -P 5555
-
- Then, I went back to the directory containing the newly compiled mysqld,
- <builddir>/sql/, started up gdb:
-
- gdb ./mysqld
-
- Then, withn the (gdb) prompt:
- (gdb) run --gdb --port=5554 --socket=/tmp/mysqld.5554 --skip-innodb --debug
-
- Next, I open several windows for each:
-
- 1. Tail the debug trace: tail -f /tmp/mysqld.trace|grep ha_fed
- 2. Tail the SQL calls to the foreign database: tail -f /tmp/mysqld.5555.log
- 3. A window with a client open to the federated server on port 5554
- 4. A window with a client open to the federated server on port 5555
-
- I would create a table on the client to the foreign server on port
- 5555, and then to the federated server on port 5554. At this point,
- I would run whatever queries I wanted to on the federated server,
- just always remembering that whatever changes I wanted to make on
- the table, or if I created new tables, that I would have to do that
- on the foreign server.
-
- Another thing to look for is 'show variables' to show you that you have
- support for federated handler support:
-
- show variables like '%federat%'
-
- and:
-
- show storage engines;
-
- Both should display the federated storage handler.
-
-
- Testing
- -------
-
- There is a test for MySQL Federated Storage Handler in ./mysql-test/t,
- federatedd.test It starts both a slave and master database using
- the same setup that the replication tests use, with the exception that
- it turns off replication, and sets replication to ignore the test tables.
- After ensuring that you actually do have support for the federated storage
- handler, numerous queries/inserts/updates/deletes are run, many derived
- from the MyISAM tests, plus som other tests which were meant to reveal
- any issues that would be most likely to affect this handler. All tests
- should work! ;)
-
- To run these tests, go into ./mysql-test (based in the directory you
- built the server in)
-
- ./mysql-test-run federatedd
-
- To run the test, or if you want to run the test and have debug info:
-
- ./mysql-test-run --debug federated
-
- This will run the test in debug mode, and you can view the trace and
- log files in the ./mysql-test/var/log directory
-
- ls -l mysql-test/var/log/
- -rw-r--r-- 1 patg patg 17 4 Dec 12:27 current_test
- -rw-r--r-- 1 patg patg 692 4 Dec 12:52 manager.log
- -rw-rw---- 1 patg patg 21246 4 Dec 12:51 master-bin.000001
- -rw-rw---- 1 patg patg 68 4 Dec 12:28 master-bin.index
- -rw-r--r-- 1 patg patg 1620 4 Dec 12:51 master.err
- -rw-rw---- 1 patg patg 23179 4 Dec 12:51 master.log
- -rw-rw---- 1 patg patg 16696550 4 Dec 12:51 master.trace
- -rw-r--r-- 1 patg patg 0 4 Dec 12:28 mysqltest-time
- -rw-r--r-- 1 patg patg 2024051 4 Dec 12:51 mysqltest.trace
- -rw-rw---- 1 patg patg 94992 4 Dec 12:51 slave-bin.000001
- -rw-rw---- 1 patg patg 67 4 Dec 12:28 slave-bin.index
- -rw-rw---- 1 patg patg 249 4 Dec 12:52 slave-relay-bin.000003
- -rw-rw---- 1 patg patg 73 4 Dec 12:28 slave-relay-bin.index
- -rw-r--r-- 1 patg patg 1349 4 Dec 12:51 slave.err
- -rw-rw---- 1 patg patg 96206 4 Dec 12:52 slave.log
- -rw-rw---- 1 patg patg 15706355 4 Dec 12:51 slave.trace
- -rw-r--r-- 1 patg patg 0 4 Dec 12:51 warnings
-
- Of course, again, you can tail the trace log:
-
- tail -f mysql-test/var/log/master.trace |grep ha_fed
-
- As well as the slave query log:
-
- tail -f mysql-test/var/log/slave.log
-
- Files that comprise the test suit
- ---------------------------------
- mysql-test/t/federated.test
- mysql-test/r/federated.result
- mysql-test/r/have_federated_db.require
- mysql-test/include/have_federated_db.inc
-
-
- Other tidbits
- -------------
-
- These were the files that were modified or created for this
- Federated handler to work:
-
- ./configure.in
- ./sql/Makefile.am
- ./config/ac_macros/ha_federated.m4
- ./sql/handler.cc
- ./sql/mysqld.cc
- ./sql/set_var.cc
- ./sql/field.h
- ./sql/sql_string.h
- ./mysql-test/mysql-test-run(.sh)
- ./mysql-test/t/federated.test
- ./mysql-test/r/federated.result
- ./mysql-test/r/have_federated_db.require
- ./mysql-test/include/have_federated_db.inc
- ./sql/ha_federated.cc
- ./sql/ha_federated.h
-
-*/
-
-
-#include "mysql_priv.h"
-#ifdef USE_PRAGMA_IMPLEMENTATION
-#pragma implementation // gcc: Class implementation
-#endif
-
-#ifdef HAVE_FEDERATED_DB
-#include "ha_federated.h"
-
-#include "m_string.h"
-/* Variables for federated share methods */
-static HASH federated_open_tables; // Hash used to track open
- // tables
-pthread_mutex_t federated_mutex; // This is the mutex we use to
- // init the hash
-static int federated_init= FALSE; // Variable for checking the
- // init state of hash
-static char ident_quote_char= '`'; // Character for quoting
- // identifiers
-static char value_quote_char= '\''; // Character for quoting
- // literals
-static const int bulk_padding= 64; // bytes "overhead" in packet
-
-/* Federated storage engine handlerton */
-
-handlerton federated_hton= {
- "FEDERATED",
- SHOW_OPTION_YES,
- "Federated MySQL storage engine",
- DB_TYPE_FEDERATED_DB,
- federated_db_init,
- 0, /* slot */
- 0, /* savepoint size. */
- NULL, /* close_connection */
- NULL, /* savepoint */
- NULL, /* rollback to savepoint */
- NULL, /* release savepoint */
- NULL, /* commit */
- NULL, /* rollback */
- NULL, /* prepare */
- NULL, /* recover */
- NULL, /* commit_by_xid */
- NULL, /* rollback_by_xid */
- NULL, /* create_cursor_read_view */
- NULL, /* set_cursor_read_view */
- NULL, /* close_cursor_read_view */
- HTON_ALTER_NOT_SUPPORTED
-};
-
-
-/* Function we use in the creation of our hash to get key. */
-
-static byte *federated_get_key(FEDERATED_SHARE *share, uint *length,
- my_bool not_used __attribute__ ((unused)))
-{
- *length= share->connect_string_length;
- return (byte*) share->scheme;
-}
-
-/*
- Initialize the federated handler.
-
- SYNOPSIS
- federated_db_init()
- void
-
- RETURN
- FALSE OK
- TRUE Error
-*/
-
-bool federated_db_init()
-{
- DBUG_ENTER("federated_db_init");
- /* the federated engine can be disabled by a command line option */
- if (have_federated_db == SHOW_OPTION_DISABLED)
- DBUG_RETURN(TRUE);
- if (pthread_mutex_init(&federated_mutex, MY_MUTEX_INIT_FAST))
- goto error;
- if (hash_init(&federated_open_tables, &my_charset_bin, 32, 0, 0,
- (hash_get_key) federated_get_key, 0, 0))
- {
- VOID(pthread_mutex_destroy(&federated_mutex));
- }
- else
- {
- federated_init= TRUE;
- DBUG_RETURN(FALSE);
- }
-error:
- have_federated_db= SHOW_OPTION_DISABLED; // If we couldn't use handler
- DBUG_RETURN(TRUE);
-}
-
-
-/*
- Release the federated handler.
-
- SYNOPSIS
- federated_db_end()
- void
-
- RETURN
- FALSE OK
-*/
-
-bool federated_db_end()
-{
- if (federated_init)
- {
- hash_free(&federated_open_tables);
- VOID(pthread_mutex_destroy(&federated_mutex));
- }
- federated_init= 0;
- return FALSE;
-}
-
-
-/**
- @brief Append identifiers to the string.
-
- @param[in,out] string The target string.
- @param[in] name Identifier name
- @param[in] length Length of identifier name in bytes
- @param[in] quote_char Quote char to use for quoting identifier.
-
- @return Operation Status
- @retval FALSE OK
- @retval TRUE There was an error appending to the string.
-
- @note This function is based upon the append_identifier() function
- in sql_show.cc except that quoting always occurs.
-*/
-
-static bool append_ident(String *string, const char *name, uint length,
- const char quote_char)
-{
- bool result;
- uint clen;
- const char *name_end;
- DBUG_ENTER("append_ident");
-
- if (quote_char)
- {
- string->reserve(length * 2 + 2);
- if ((result= string->append(&quote_char, 1, system_charset_info)))
- goto err;
-
- for (name_end= name+length; name < name_end; name+= clen)
- {
- uchar c= *(uchar *) name;
- if (!(clen= my_mbcharlen(system_charset_info, c)))
- clen= 1;
- if (clen == 1 && c == (uchar) quote_char &&
- (result= string->append(&quote_char, 1, system_charset_info)))
- goto err;
- if ((result= string->append(name, clen, string->charset())))
- goto err;
- }
- result= string->append(&quote_char, 1, system_charset_info);
- }
- else
- result= string->append(name, length, system_charset_info);
-
-err:
- DBUG_RETURN(result);
-}
-
-
-static int parse_url_error(FEDERATED_SHARE *share, TABLE *table, int error_num)
-{
- char buf[FEDERATED_QUERY_BUFFER_SIZE];
- int buf_len;
- DBUG_ENTER("ha_federated parse_url_error");
-
- if (share->scheme)
- {
- DBUG_PRINT("info",
- ("error: parse_url. Returning error code %d freeing share->scheme 0x%lx",
- error_num, (long) share->scheme));
- my_free((gptr) share->scheme, MYF(0));
- share->scheme= 0;
- }
- buf_len= min(table->s->connect_string.length,
- FEDERATED_QUERY_BUFFER_SIZE-1);
- strmake(buf, table->s->connect_string.str, buf_len);
- my_error(error_num, MYF(0), buf);
- DBUG_RETURN(error_num);
-}
-
-/*
- Parse connection info from table->s->connect_string
-
- SYNOPSIS
- parse_url()
- share pointer to FEDERATED share
- table pointer to current TABLE class
- table_create_flag determines what error to throw
-
- DESCRIPTION
- populates the share with information about the connection
- to the foreign database that will serve as the data source.
- This string must be specified (currently) in the "comment" field,
- listed in the CREATE TABLE statement.
-
- This string MUST be in the format of any of these:
-
- scheme://username:password@hostname:port/database/table
- scheme://username@hostname/database/table
- scheme://username@hostname:port/database/table
- scheme://username:password@hostname/database/table
-
- An Example:
-
- mysql://joe:joespass@192.168.1.111:9308/federated/testtable
-
- ***IMPORTANT***
- Currently, only "mysql://" is supported.
-
- 'password' and 'port' are both optional.
-
- RETURN VALUE
- 0 success
- error_num particular error code
-
-*/
-
-static int parse_url(FEDERATED_SHARE *share, TABLE *table,
- uint table_create_flag)
-{
- uint error_num= (table_create_flag ?
- ER_FOREIGN_DATA_STRING_INVALID_CANT_CREATE :
- ER_FOREIGN_DATA_STRING_INVALID);
- DBUG_ENTER("ha_federated::parse_url");
-
- share->port= 0;
- share->socket= 0;
- DBUG_PRINT("info", ("Length %d \n", table->s->connect_string.length));
- DBUG_PRINT("info", ("String %.*s \n", table->s->connect_string.length,
- table->s->connect_string.str));
- share->scheme= my_strdup_with_length(table->s->connect_string.str,
- table->s->connect_string.length,
- MYF(0));
-
- share->connect_string_length= table->s->connect_string.length;
- DBUG_PRINT("info",("parse_url alloced share->scheme 0x%lx", (long) share->scheme));
-
- /*
- remove addition of null terminator and store length
- for each string in share
- */
- if (!(share->username= strstr(share->scheme, "://")))
- goto error;
- share->scheme[share->username - share->scheme]= '\0';
-
- if (strcmp(share->scheme, "mysql") != 0)
- goto error;
-
- share->username+= 3;
-
- if (!(share->hostname= strchr(share->username, '@')))
- goto error;
-
- share->username[share->hostname - share->username]= '\0';
- share->hostname++;
-
- if ((share->password= strchr(share->username, ':')))
- {
- share->username[share->password - share->username]= '\0';
- share->password++;
- share->username= share->username;
- /* make sure there isn't an extra / or @ */
- if ((strchr(share->password, '/') || strchr(share->hostname, '@')))
- goto error;
- /*
- Found that if the string is:
- user:@hostname:port/database/table
- Then password is a null string, so set to NULL
- */
- if ((share->password[0] == '\0'))
- share->password= NULL;
- }
- else
- share->username= share->username;
-
- /* make sure there isn't an extra / or @ */
- if ((strchr(share->username, '/')) || (strchr(share->hostname, '@')))
- goto error;
-
- if (!(share->database= strchr(share->hostname, '/')))
- goto error;
- share->hostname[share->database - share->hostname]= '\0';
- share->database++;
-
- if ((share->sport= strchr(share->hostname, ':')))
- {
- share->hostname[share->sport - share->hostname]= '\0';
- share->sport++;
- if (share->sport[0] == '\0')
- share->sport= NULL;
- else
- share->port= atoi(share->sport);
- }
-
- if (!(share->table_name= strchr(share->database, '/')))
- goto error;
- share->database[share->table_name - share->database]= '\0';
- share->table_name++;
-
- share->table_name_length= (uint) strlen(share->table_name);
-
- /* make sure there's not an extra / */
- if ((strchr(share->table_name, '/')))
- goto error;
-
- /*
- If hostname is omitted, we set it to NULL. According to
- mysql_real_connect() manual:
- The value of host may be either a hostname or an IP address.
- If host is NULL or the string "localhost", a connection to the
- local host is assumed.
- */
- if (share->hostname[0] == '\0')
- share->hostname= NULL;
-
- if (!share->port)
- {
- if (!share->hostname || strcmp(share->hostname, my_localhost) == 0)
- share->socket= (char*) MYSQL_UNIX_ADDR;
- else
- share->port= MYSQL_PORT;
- }
-
- DBUG_PRINT("info",
- ("scheme %s username %s password %s \
- hostname %s port %d database %s tablename %s\n",
- share->scheme, share->username, share->password,
- share->hostname, share->port, share->database,
- share->table_name));
-
- DBUG_RETURN(0);
-
-error:
- DBUG_RETURN(parse_url_error(share, table, error_num));
-}
-
-
-/*****************************************************************************
-** FEDERATED tables
-*****************************************************************************/
-
-ha_federated::ha_federated(TABLE *table_arg)
- :handler(&federated_hton, table_arg),
- mysql(0), stored_result(0)
-{
- bzero(&bulk_insert, sizeof(bulk_insert));
-}
-
-
-/*
- Convert MySQL result set row to handler internal format
-
- SYNOPSIS
- convert_row_to_internal_format()
- record Byte pointer to record
- row MySQL result set row from fetchrow()
- result Result set to use
-
- DESCRIPTION
- This method simply iterates through a row returned via fetchrow with
- values from a successful SELECT , and then stores each column's value
- in the field object via the field object pointer (pointing to the table's
- array of field object pointers). This is how the handler needs the data
- to be stored to then return results back to the user
-
- RETURN VALUE
- 0 After fields have had field values stored from record
- */
-
-uint ha_federated::convert_row_to_internal_format(byte *record,
- MYSQL_ROW row,
- MYSQL_RES *result)
-{
- ulong *lengths;
- Field **field;
- DBUG_ENTER("ha_federated::convert_row_to_internal_format");
-
- lengths= mysql_fetch_lengths(result);
-
- for (field= table->field; *field; field++)
- {
- /*
- index variable to move us through the row at the
- same iterative step as the field
- */
- size_t x= (field - table->field);
- my_ptrdiff_t old_ptr;
- old_ptr= (my_ptrdiff_t) (record - table->record[0]);
- (*field)->move_field(old_ptr);
- if (!row[x])
- {
- (*field)->set_null();
- (*field)->reset();
- }
- else
- {
- (*field)->set_notnull();
- (*field)->store(row[x], lengths[x], &my_charset_bin);
- }
- (*field)->move_field(-old_ptr);
- }
-
- DBUG_RETURN(0);
-}
-
-static bool emit_key_part_name(String *to, KEY_PART_INFO *part)
-{
- DBUG_ENTER("emit_key_part_name");
- if (append_ident(to, part->field->field_name,
- (uint) strlen(part->field->field_name), ident_quote_char))
- DBUG_RETURN(1); // Out of memory
- DBUG_RETURN(0);
-}
-
-static bool emit_key_part_element(String *to, KEY_PART_INFO *part,
- bool needs_quotes, bool is_like,
- const byte *ptr, uint len)
-{
- Field *field= part->field;
- DBUG_ENTER("emit_key_part_element");
-
- if (needs_quotes && to->append(FEDERATED_SQUOTE))
- DBUG_RETURN(1);
-
- if (part->type == HA_KEYTYPE_BIT)
- {
- char buff[STRING_BUFFER_USUAL_SIZE], *buf= buff;
-
- *buf++= '0';
- *buf++= 'x';
- buf= octet2hex(buf, (char*) ptr, len);
- if (to->append((char*) buff, (uint)(buf - buff)))
- DBUG_RETURN(1);
- }
- else if (part->key_part_flag & HA_BLOB_PART)
- {
- String blob;
- uint blob_length= uint2korr(ptr);
- blob.set_quick((char*) ptr+HA_KEY_BLOB_LENGTH,
- blob_length, &my_charset_bin);
- if (append_escaped(to, &blob))
- DBUG_RETURN(1);
- }
- else if (part->key_part_flag & HA_VAR_LENGTH_PART)
- {
- String varchar;
- uint var_length= uint2korr(ptr);
- varchar.set_quick((char*) ptr+HA_KEY_BLOB_LENGTH,
- var_length, &my_charset_bin);
- if (append_escaped(to, &varchar))
- DBUG_RETURN(1);
- }
- else
- {
- char strbuff[MAX_FIELD_WIDTH];
- String str(strbuff, sizeof(strbuff), part->field->charset()), *res;
-
- res= field->val_str(&str, (char *)ptr);
-
- if (field->result_type() == STRING_RESULT)
- {
- if (append_escaped(to, res))
- DBUG_RETURN(1);
- }
- else if (to->append(res->ptr(), res->length()))
- DBUG_RETURN(1);
- }
-
- if (is_like && to->append(FEDERATED_PERCENT))
- DBUG_RETURN(1);
-
- if (needs_quotes && to->append(FEDERATED_SQUOTE))
- DBUG_RETURN(1);
-
- DBUG_RETURN(0);
-}
-
-/*
- Create a WHERE clause based off of values in keys
- Note: This code was inspired by key_copy from key.cc
-
- SYNOPSIS
- create_where_from_key ()
- to String object to store WHERE clause
- key_info KEY struct pointer
- key byte pointer containing key
- key_length length of key
- range_type 0 - no range, 1 - min range, 2 - max range
- (see enum range_operation)
-
- DESCRIPTION
- Using iteration through all the keys via a KEY_PART_INFO pointer,
- This method 'extracts' the value of each key in the byte pointer
- *key, and for each key found, constructs an appropriate WHERE clause
-
- RETURN VALUE
- 0 After all keys have been accounted for to create the WHERE clause
- 1 No keys found
-
- Range flags Table per Timour:
-
- -----------------
- - start_key:
- * ">" -> HA_READ_AFTER_KEY
- * ">=" -> HA_READ_KEY_OR_NEXT
- * "=" -> HA_READ_KEY_EXACT
-
- - end_key:
- * "<" -> HA_READ_BEFORE_KEY
- * "<=" -> HA_READ_AFTER_KEY
-
- records_in_range:
- -----------------
- - start_key:
- * ">" -> HA_READ_AFTER_KEY
- * ">=" -> HA_READ_KEY_EXACT
- * "=" -> HA_READ_KEY_EXACT
-
- - end_key:
- * "<" -> HA_READ_BEFORE_KEY
- * "<=" -> HA_READ_AFTER_KEY
- * "=" -> HA_READ_AFTER_KEY
-
-0 HA_READ_KEY_EXACT, Find first record else error
-1 HA_READ_KEY_OR_NEXT, Record or next record
-2 HA_READ_KEY_OR_PREV, Record or previous
-3 HA_READ_AFTER_KEY, Find next rec. after key-record
-4 HA_READ_BEFORE_KEY, Find next rec. before key-record
-5 HA_READ_PREFIX, Key which as same prefix
-6 HA_READ_PREFIX_LAST, Last key with the same prefix
-7 HA_READ_PREFIX_LAST_OR_PREV, Last or prev key with the same prefix
-
-Flags that I've found:
-
-id, primary key, varchar
-
-id = 'ccccc'
-records_in_range: start_key 0 end_key 3
-read_range_first: start_key 0 end_key NULL
-
-id > 'ccccc'
-records_in_range: start_key 3 end_key NULL
-read_range_first: start_key 3 end_key NULL
-
-id < 'ccccc'
-records_in_range: start_key NULL end_key 4
-read_range_first: start_key NULL end_key 4
-
-id <= 'ccccc'
-records_in_range: start_key NULL end_key 3
-read_range_first: start_key NULL end_key 3
-
-id >= 'ccccc'
-records_in_range: start_key 0 end_key NULL
-read_range_first: start_key 1 end_key NULL
-
-id like 'cc%cc'
-records_in_range: start_key 0 end_key 3
-read_range_first: start_key 1 end_key 3
-
-id > 'aaaaa' and id < 'ccccc'
-records_in_range: start_key 3 end_key 4
-read_range_first: start_key 3 end_key 4
-
-id >= 'aaaaa' and id < 'ccccc';
-records_in_range: start_key 0 end_key 4
-read_range_first: start_key 1 end_key 4
-
-id >= 'aaaaa' and id <= 'ccccc';
-records_in_range: start_key 0 end_key 3
-read_range_first: start_key 1 end_key 3
-
-id > 'aaaaa' and id <= 'ccccc';
-records_in_range: start_key 3 end_key 3
-read_range_first: start_key 3 end_key 3
-
-numeric keys:
-
-id = 4
-index_read_idx: start_key 0 end_key NULL
-
-id > 4
-records_in_range: start_key 3 end_key NULL
-read_range_first: start_key 3 end_key NULL
-
-id >= 4
-records_in_range: start_key 0 end_key NULL
-read_range_first: start_key 1 end_key NULL
-
-id < 4
-records_in_range: start_key NULL end_key 4
-read_range_first: start_key NULL end_key 4
-
-id <= 4
-records_in_range: start_key NULL end_key 3
-read_range_first: start_key NULL end_key 3
-
-id like 4
-full table scan, select * from
-
-id > 2 and id < 8
-records_in_range: start_key 3 end_key 4
-read_range_first: start_key 3 end_key 4
-
-id >= 2 and id < 8
-records_in_range: start_key 0 end_key 4
-read_range_first: start_key 1 end_key 4
-
-id >= 2 and id <= 8
-records_in_range: start_key 0 end_key 3
-read_range_first: start_key 1 end_key 3
-
-id > 2 and id <= 8
-records_in_range: start_key 3 end_key 3
-read_range_first: start_key 3 end_key 3
-
-multi keys (id int, name varchar, other varchar)
-
-id = 1;
-records_in_range: start_key 0 end_key 3
-read_range_first: start_key 0 end_key NULL
-
-id > 4;
-id > 2 and name = '333'; remote: id > 2
-id > 2 and name > '333'; remote: id > 2
-id > 2 and name > '333' and other < 'ddd'; remote: id > 2 no results
-id > 2 and name >= '333' and other < 'ddd'; remote: id > 2 1 result
-id >= 4 and name = 'eric was here' and other > 'eeee';
-records_in_range: start_key 3 end_key NULL
-read_range_first: start_key 3 end_key NULL
-
-id >= 4;
-id >= 2 and name = '333' and other < 'ddd';
-remote: `id` >= 2 AND `name` >= '333';
-records_in_range: start_key 0 end_key NULL
-read_range_first: start_key 1 end_key NULL
-
-id < 4;
-id < 3 and name = '222' and other <= 'ccc'; remote: id < 3
-records_in_range: start_key NULL end_key 4
-read_range_first: start_key NULL end_key 4
-
-id <= 4;
-records_in_range: start_key NULL end_key 3
-read_range_first: start_key NULL end_key 3
-
-id like 4;
-full table scan
-
-id > 2 and id < 4;
-records_in_range: start_key 3 end_key 4
-read_range_first: start_key 3 end_key 4
-
-id >= 2 and id < 4;
-records_in_range: start_key 0 end_key 4
-read_range_first: start_key 1 end_key 4
-
-id >= 2 and id <= 4;
-records_in_range: start_key 0 end_key 3
-read_range_first: start_key 1 end_key 3
-
-id > 2 and id <= 4;
-id = 6 and name = 'eric was here' and other > 'eeee';
-remote: (`id` > 6 AND `name` > 'eric was here' AND `other` > 'eeee')
-AND (`id` <= 6) AND ( AND `name` <= 'eric was here')
-no results
-records_in_range: start_key 3 end_key 3
-read_range_first: start_key 3 end_key 3
-
-Summary:
-
-* If the start key flag is 0 the max key flag shouldn't even be set,
- and if it is, the query produced would be invalid.
-* Multipart keys, even if containing some or all numeric columns,
- are treated the same as non-numeric keys
-
- If the query is " = " (quotes or not):
- - records in range start key flag HA_READ_KEY_EXACT,
- end key flag HA_READ_AFTER_KEY (incorrect)
- - any other: start key flag HA_READ_KEY_OR_NEXT,
- end key flag HA_READ_AFTER_KEY (correct)
-
-* 'like' queries (of key)
- - Numeric, full table scan
- - Non-numeric
- records_in_range: start_key 0 end_key 3
- other : start_key 1 end_key 3
-
-* If the key flag is HA_READ_AFTER_KEY:
- if start_key, append >
- if end_key, append <=
-
-* If create_where_key was called by records_in_range:
-
- - if the key is numeric:
- start key flag is 0 when end key is NULL, end key flag is 3 or 4
- - if create_where_key was called by any other function:
- start key flag is 1 when end key is NULL, end key flag is 3 or 4
- - if the key is non-numeric, or multipart
- When the query is an exact match, the start key flag is 0,
- end key flag is 3 for what should be a no-range condition where
- you should have 0 and max key NULL, which it is if called by
- read_range_first
-
-Conclusion:
-
-1. Need logic to determin if a key is min or max when the flag is
-HA_READ_AFTER_KEY, and handle appending correct operator accordingly
-
-2. Need a boolean flag to pass to create_where_from_key, used in the
-switch statement. Add 1 to the flag if:
- - start key flag is HA_READ_KEY_EXACT and the end key is NULL
-
-*/
-
-bool ha_federated::create_where_from_key(String *to,
- KEY *key_info,
- const key_range *start_key,
- const key_range *end_key,
- bool from_records_in_range)
-{
- bool both_not_null=
- (start_key != NULL && end_key != NULL) ? TRUE : FALSE;
- const byte *ptr;
- uint remainder, length;
- char tmpbuff[FEDERATED_QUERY_BUFFER_SIZE];
- String tmp(tmpbuff, sizeof(tmpbuff), system_charset_info);
- const key_range *ranges[2]= { start_key, end_key };
- DBUG_ENTER("ha_federated::create_where_from_key");
-
- tmp.length(0);
- if (start_key == NULL && end_key == NULL)
- DBUG_RETURN(1);
-
- for (int i= 0; i <= 1; i++)
- {
- bool needs_quotes;
- KEY_PART_INFO *key_part;
- if (ranges[i] == NULL)
- continue;
-
- if (both_not_null)
- {
- if (i > 0)
- tmp.append(FEDERATED_CONJUNCTION);
- else
- tmp.append(FEDERATED_OPENPAREN);
- }
-
- for (key_part= key_info->key_part,
- remainder= key_info->key_parts,
- length= ranges[i]->length,
- ptr= ranges[i]->key; ;
- remainder--,
- key_part++)
- {
- Field *field= key_part->field;
- uint store_length= key_part->store_length;
- uint part_length= min(store_length, length);
- needs_quotes= 1;
- DBUG_DUMP("key, start of loop", (uchar *)ptr, length);
-
- if (key_part->null_bit)
- {
- if (*ptr++)
- {
- /*
- We got "IS [NOT] NULL" condition against nullable column. We
- distinguish between "IS NOT NULL" and "IS NULL" by flag. For
- "IS NULL", flag is set to HA_READ_KEY_EXACT.
- */
- if (emit_key_part_name(&tmp, key_part) ||
- tmp.append(ranges[i]->flag == HA_READ_KEY_EXACT ?
- FEDERATED_ISNULL : " IS NOT NULL "))
- DBUG_RETURN(1);
- /*
- We need to adjust pointer and length to be prepared for next
- key part. As well as check if this was last key part.
- */
- goto prepare_for_next_key_part;
- }
- }
-
- if (tmp.append(FEDERATED_OPENPAREN))
- DBUG_RETURN(1);
-
- switch(ranges[i]->flag) {
- case(HA_READ_KEY_EXACT):
- DBUG_PRINT("info", ("federated HA_READ_KEY_EXACT %d", i));
- if (store_length >= length ||
- !needs_quotes ||
- key_part->type == HA_KEYTYPE_BIT ||
- field->result_type() != STRING_RESULT)
- {
- if (emit_key_part_name(&tmp, key_part))
- DBUG_RETURN(1);
-
- if (from_records_in_range)
- {
- if (tmp.append(FEDERATED_GE))
- DBUG_RETURN(1);
- }
- else
- {
- if (tmp.append(FEDERATED_EQ))
- DBUG_RETURN(1);
- }
-
- if (emit_key_part_element(&tmp, key_part, needs_quotes, 0, ptr,
- part_length))
- DBUG_RETURN(1);
- }
- else
- /* LIKE */
- {
- if (emit_key_part_name(&tmp, key_part) ||
- tmp.append(FEDERATED_LIKE) ||
- emit_key_part_element(&tmp, key_part, needs_quotes, 1, ptr,
- part_length))
- DBUG_RETURN(1);
- }
- break;
- case(HA_READ_AFTER_KEY):
- DBUG_PRINT("info", ("federated HA_READ_AFTER_KEY %d", i));
- if (store_length >= length) /* end key */
- {
- if (emit_key_part_name(&tmp, key_part))
- DBUG_RETURN(1);
-
- if (i > 0) /* end key */
- {
- if (tmp.append(FEDERATED_LE))
- DBUG_RETURN(1);
- }
- else /* start key */
- {
- if (tmp.append(FEDERATED_GT))
- DBUG_RETURN(1);
- }
-
- if (emit_key_part_element(&tmp, key_part, needs_quotes, 0, ptr,
- part_length))
- {
- DBUG_RETURN(1);
- }
- break;
- }
- case(HA_READ_KEY_OR_NEXT):
- DBUG_PRINT("info", ("federated HA_READ_KEY_OR_NEXT %d", i));
- if (emit_key_part_name(&tmp, key_part) ||
- tmp.append(FEDERATED_GE) ||
- emit_key_part_element(&tmp, key_part, needs_quotes, 0, ptr,
- part_length))
- DBUG_RETURN(1);
- break;
- case(HA_READ_BEFORE_KEY):
- DBUG_PRINT("info", ("federated HA_READ_BEFORE_KEY %d", i));
- if (store_length >= length)
- {
- if (emit_key_part_name(&tmp, key_part) ||
- tmp.append(FEDERATED_LT) ||
- emit_key_part_element(&tmp, key_part, needs_quotes, 0, ptr,
- part_length))
- DBUG_RETURN(1);
- break;
- }
- case(HA_READ_KEY_OR_PREV):
- DBUG_PRINT("info", ("federated HA_READ_KEY_OR_PREV %d", i));
- if (emit_key_part_name(&tmp, key_part) ||
- tmp.append(FEDERATED_LE) ||
- emit_key_part_element(&tmp, key_part, needs_quotes, 0, ptr,
- part_length))
- DBUG_RETURN(1);
- break;
- default:
- DBUG_PRINT("info",("cannot handle flag %d", ranges[i]->flag));
- DBUG_RETURN(1);
- }
- if (tmp.append(FEDERATED_CLOSEPAREN))
- DBUG_RETURN(1);
-
-prepare_for_next_key_part:
- if (store_length >= length)
- break;
- DBUG_PRINT("info", ("remainder %d", remainder));
- DBUG_ASSERT(remainder > 1);
- length-= store_length;
- /*
- For nullable columns, null-byte is already skipped before, that is
- ptr was incremented by 1. Since store_length still counts null-byte,
- we need to subtract 1 from store_length.
- */
- ptr+= store_length - test(key_part->null_bit);
- if (tmp.append(FEDERATED_AND))
- DBUG_RETURN(1);
-
- DBUG_PRINT("info",
- ("create_where_from_key WHERE clause: %s",
- tmp.c_ptr_quick()));
- }
- }
- if (both_not_null)
- if (tmp.append(FEDERATED_CLOSEPAREN))
- DBUG_RETURN(1);
-
- if (to->append(FEDERATED_WHERE))
- DBUG_RETURN(1);
-
- if (to->append(tmp))
- DBUG_RETURN(1);
-
- DBUG_RETURN(0);
-}
-
-/*
- Example of simple lock controls. The "share" it creates is structure we will
- pass to each federated handler. Do you have to have one of these? Well, you
- have pieces that are used for locking, and they are needed to function.
-*/
-
-static FEDERATED_SHARE *get_share(const char *table_name, TABLE *table)
-{
- char *select_query;
- char query_buffer[FEDERATED_QUERY_BUFFER_SIZE];
- Field **field;
- String query(query_buffer, sizeof(query_buffer), &my_charset_bin);
- FEDERATED_SHARE *share= NULL, tmp_share;
- /*
- In order to use this string, we must first zero it's length,
- or it will contain garbage
- */
- query.length(0);
-
- pthread_mutex_lock(&federated_mutex);
-
- if (parse_url(&tmp_share, table, 0))
- goto error;
-
- /* TODO: change tmp_share.scheme to LEX_STRING object */
- if (!(share= (FEDERATED_SHARE *) hash_search(&federated_open_tables,
- (byte*) tmp_share.scheme,
- tmp_share.
- connect_string_length)))
- {
- query.set_charset(system_charset_info);
- query.append(FEDERATED_SELECT);
- for (field= table->field; *field; field++)
- {
- append_ident(&query, (*field)->field_name,
- (uint) strlen((*field)->field_name), ident_quote_char);
- query.append(FEDERATED_COMMA);
- }
- query.length(query.length() - (uint) strlen(FEDERATED_COMMA));
- query.append(FEDERATED_FROM);
-
- tmp_share.table_name_length= (uint) strlen(tmp_share.table_name);
- append_ident(&query, tmp_share.table_name,
- tmp_share.table_name_length, ident_quote_char);
-
- if (!(share= (FEDERATED_SHARE *)
- my_multi_malloc(MYF(MY_WME),
- &share, sizeof(*share),
- &select_query, query.length()+1,
- NullS)))
- goto error;
-
- memcpy(share, &tmp_share, sizeof(tmp_share));
- memcpy(select_query, query.ptr(), query.length()+1);
-
- share->select_query= select_query;
- share->use_count= 0;
- DBUG_PRINT("info",
- ("share->select_query %s", share->select_query));
-
- if (my_hash_insert(&federated_open_tables, (byte*) share))
- goto error;
- thr_lock_init(&share->lock);
- pthread_mutex_init(&share->mutex, MY_MUTEX_INIT_FAST);
- }
- else
- {
- /*
- Free tmp_share.scheme allocated in the parse_url()
- as we found share in the hash and tmp_share isn't needed anymore.
- */
- my_free((gptr) tmp_share.scheme, MYF(MY_ALLOW_ZERO_PTR));
- }
- share->use_count++;
- pthread_mutex_unlock(&federated_mutex);
-
- return share;
-
-error:
- pthread_mutex_unlock(&federated_mutex);
- my_free((gptr) tmp_share.scheme, MYF(MY_ALLOW_ZERO_PTR));
- my_free((gptr) share, MYF(MY_ALLOW_ZERO_PTR));
- return NULL;
-}
-
-
-/*
- 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.
-*/
-
-static int free_share(FEDERATED_SHARE *share)
-{
- DBUG_ENTER("free_share");
-
- pthread_mutex_lock(&federated_mutex);
- if (!--share->use_count)
- {
- hash_delete(&federated_open_tables, (byte*) share);
- my_free((gptr) share->scheme, MYF(MY_ALLOW_ZERO_PTR));
- thr_lock_delete(&share->lock);
- VOID(pthread_mutex_destroy(&share->mutex));
- my_free((gptr) share, MYF(0));
- }
- pthread_mutex_unlock(&federated_mutex);
-
- DBUG_RETURN(0);
-}
-
-
-ha_rows ha_federated::records_in_range(uint inx, key_range *start_key,
- key_range *end_key)
-{
- /*
-
- We really want indexes to be used as often as possible, therefore
- we just need to hard-code the return value to a very low number to
- force the issue
-
-*/
- DBUG_ENTER("ha_federated::records_in_range");
- DBUG_RETURN(FEDERATED_RECORDS_IN_RANGE);
-}
-/*
- If frm_error() is called then we will use this to to find out
- what file extentions exist for the storage engine. This is
- also used by the default rename_table and delete_table method
- in handler.cc.
-*/
-
-const char **ha_federated::bas_ext() const
-{
- static const char *ext[]=
- {
- NullS
- };
- return ext;
-}
-
-
-/*
- Used for opening tables. The name will be the name of the file.
- A table is opened when it needs to be opened. For instance
- when a request comes in for a select on the table (tables are not
- open and closed for each request, they are cached).
-
- Called from handler.cc by handler::ha_open(). The server opens
- all tables by calling ha_open() which then calls the handler
- specific open().
-*/
-
-int ha_federated::open(const char *name, int mode, uint test_if_locked)
-{
- DBUG_ENTER("ha_federated::open");
-
- if (!(share= get_share(name, table)))
- DBUG_RETURN(1);
- thr_lock_data_init(&share->lock, &lock, NULL);
-
- DBUG_ASSERT(mysql == NULL);
-
- ref_length= (table->s->primary_key != MAX_KEY ?
- table->key_info[table->s->primary_key].key_length :
- table->s->reclength);
- DBUG_PRINT("info", ("ref_length: %u", ref_length));
-
- reset();
-
- DBUG_RETURN(0);
-}
-
-
-/*
- Closes a table. We call the free_share() function to free any resources
- that we have allocated in the "shared" structure.
-
- Called from sql_base.cc, sql_select.cc, and table.cc.
- In sql_select.cc it is only used to close up temporary tables or during
- the process where a temporary table is converted over to being a
- myisam table.
- For sql_base.cc look at close_data_tables().
-*/
-
-int ha_federated::close(void)
-{
- int retval;
- DBUG_ENTER("ha_federated::close");
-
- /* free the result set */
- if (stored_result)
- {
- mysql_free_result(stored_result);
- stored_result= 0;
- }
- /* Disconnect from mysql */
- mysql_close(mysql);
- mysql= NULL;
- retval= free_share(share);
- DBUG_RETURN(retval);
-
-}
-
-/*
-
- Checks if a field in a record is SQL NULL.
-
- SYNOPSIS
- field_in_record_is_null()
- table TABLE pointer, MySQL table object
- field Field pointer, MySQL field object
- record char pointer, contains record
-
- DESCRIPTION
- This method uses the record format information in table to track
- the null bit in record.
-
- RETURN VALUE
- 1 if NULL
- 0 otherwise
-*/
-
-inline uint field_in_record_is_null(TABLE *table,
- Field *field,
- char *record)
-{
- int null_offset;
- DBUG_ENTER("ha_federated::field_in_record_is_null");
-
- if (!field->null_ptr)
- DBUG_RETURN(0);
-
- null_offset= (uint) ((char*)field->null_ptr - (char*)table->record[0]);
-
- if (record[null_offset] & field->null_bit)
- DBUG_RETURN(1);
-
- DBUG_RETURN(0);
-}
-
-
-/**
- @brief Construct the INSERT statement.
-
- @details This method will construct the INSERT statement and appends it to
- the supplied query string buffer.
-
- @return
- @retval FALSE No error
- @retval TRUE Failure
-*/
-
-bool ha_federated::append_stmt_insert(String *query)
-{
- char insert_buffer[FEDERATED_QUERY_BUFFER_SIZE];
- Field **field;
- uint tmp_length;
-
- /* The main insert query string */
- String insert_string(insert_buffer, sizeof(insert_buffer), &my_charset_bin);
- DBUG_ENTER("ha_federated::append_stmt_insert");
-
- insert_string.length(0);
-
- if (replace_duplicates)
- insert_string.append(STRING_WITH_LEN("REPLACE INTO "));
- else if (ignore_duplicates && !insert_dup_update)
- insert_string.append(STRING_WITH_LEN("INSERT IGNORE INTO "));
- else
- insert_string.append(STRING_WITH_LEN("INSERT INTO "));
- append_ident(&insert_string, share->table_name, share->table_name_length,
- ident_quote_char);
- insert_string.append(FEDERATED_OPENPAREN);
- tmp_length= insert_string.length() - (uint) strlen(FEDERATED_COMMA);
-
- /*
- loop through the field pointer array, add any fields to both the values
- list and the fields list that match the current query id
- */
- for (field= table->field; *field; field++)
- {
- /* append the field name */
- append_ident(&insert_string, (*field)->field_name,
- (uint) strlen((*field)->field_name), ident_quote_char);
-
- /* append commas between both fields and fieldnames */
- /*
- unfortunately, we can't use the logic
- if *(fields + 1) to make the following
- appends conditional because we may not append
- if the next field doesn't match the condition:
- (((*field)->query_id && (*field)->query_id == current_query_id)
- */
- insert_string.append(FEDERATED_COMMA);
- }
-
- /*
- remove trailing comma
- */
- insert_string.length(insert_string.length() - (uint) strlen(FEDERATED_COMMA));
-
- /*
- if there were no fields, we don't want to add a closing paren
- AND, we don't want to chop off the last char '('
- insert will be "INSERT INTO t1 VALUES ();"
- */
- if (insert_string.length() > tmp_length)
- {
- insert_string.append(FEDERATED_CLOSEPAREN);
- }
-
- insert_string.append(FEDERATED_VALUES);
-
- DBUG_RETURN(query->append(insert_string));
-}
-
-
-/*
- write_row() inserts a row. No extra() hint is given currently if a bulk load
- is happeneding. buf() is a byte array of data. You can use the field
- information to extract the data from the native byte array type.
- Example of this would be:
- for (Field **field=table->field ; *field ; field++)
- {
- ...
- }
-
- Called from item_sum.cc, item_sum.cc, sql_acl.cc, sql_insert.cc,
- sql_insert.cc, sql_select.cc, sql_table.cc, sql_udf.cc, and sql_update.cc.
-*/
-
-int ha_federated::write_row(byte *buf)
-{
- char values_buffer[FEDERATED_QUERY_BUFFER_SIZE];
- char insert_field_value_buffer[STRING_BUFFER_USUAL_SIZE];
- Field **field;
- uint tmp_length;
- int error= 0;
- bool use_bulk_insert;
- bool auto_increment_update_required= (table->next_number_field != NULL);
-
- /* The string containing the values to be added to the insert */
- String values_string(values_buffer, sizeof(values_buffer), &my_charset_bin);
- /* The actual value of the field, to be added to the values_string */
- String insert_field_value_string(insert_field_value_buffer,
- sizeof(insert_field_value_buffer),
- &my_charset_bin);
- values_string.length(0);
- insert_field_value_string.length(0);
- DBUG_ENTER("ha_federated::write_row");
-
- statistic_increment(table->in_use->status_var.ha_write_count, &LOCK_status);
- if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_INSERT)
- table->timestamp_field->set_time();
-
- /*
- start both our field and field values strings
- We must disable multi-row insert for "INSERT...ON DUPLICATE KEY UPDATE"
- Ignore duplicates is always true when insert_dup_update is true.
- When replace_duplicates == TRUE, we can safely enable multi-row insert.
- When performing multi-row insert, we only collect the columns values for
- the row. The start of the statement is only created when the first
- row is copied in to the bulk_insert string.
- */
- if (!(use_bulk_insert= bulk_insert.str &&
- (!insert_dup_update || replace_duplicates)))
- append_stmt_insert(&values_string);
-
- values_string.append(FEDERATED_OPENPAREN);
- tmp_length= values_string.length();
-
- /*
- loop through the field pointer array, add any fields to both the values
- list and the fields list that match the current query id
- */
- for (field= table->field; *field; field++)
- {
- if ((*field)->is_null())
- insert_field_value_string.append(FEDERATED_NULL);
- else
- {
- (*field)->val_str(&insert_field_value_string);
- values_string.append(value_quote_char);
- insert_field_value_string.print(&values_string);
- values_string.append(value_quote_char);
-
- insert_field_value_string.length(0);
- }
-
- /* append the value */
- values_string.append(insert_field_value_string);
- insert_field_value_string.length(0);
-
- /* append commas between both fields and fieldnames */
- /*
- unfortunately, we can't use the logic
- if *(fields + 1) to make the following
- appends conditional because we may not append
- if the next field doesn't match the condition:
- (((*field)->query_id && (*field)->query_id == current_query_id)
- */
- values_string.append(FEDERATED_COMMA);
- }
-
- /*
- if there were no fields, we don't want to add a closing paren
- AND, we don't want to chop off the last char '('
- insert will be "INSERT INTO t1 VALUES ();"
- */
- if (values_string.length() > tmp_length)
- {
- /* chops off leading commas */
- values_string.length(values_string.length() - (uint) strlen(FEDERATED_COMMA));
- }
- /* we always want to append this, even if there aren't any fields */
- values_string.append(FEDERATED_CLOSEPAREN);
-
- if (use_bulk_insert)
- {
- /*
- Send the current bulk insert out if appending the current row would
- cause the statement to overflow the packet size, otherwise set
- auto_increment_update_required to FALSE as no query was executed.
- */
- if (bulk_insert.length + values_string.length() + bulk_padding >
- mysql->net.max_packet_size && bulk_insert.length)
- {
- error= real_query(bulk_insert.str, bulk_insert.length);
- bulk_insert.length= 0;
- }
- else
- auto_increment_update_required= FALSE;
-
- if (bulk_insert.length == 0)
- {
- char insert_buffer[FEDERATED_QUERY_BUFFER_SIZE];
- String insert_string(insert_buffer, sizeof(insert_buffer),
- &my_charset_bin);
- insert_string.length(0);
- append_stmt_insert(&insert_string);
- dynstr_append_mem(&bulk_insert, insert_string.ptr(),
- insert_string.length());
- }
- else
- dynstr_append_mem(&bulk_insert, ",", 1);
-
- dynstr_append_mem(&bulk_insert, values_string.ptr(),
- values_string.length());
- }
- else
- {
- error= real_query(values_string.ptr(), values_string.length());
- }
-
- if (error)
- {
- DBUG_RETURN(stash_remote_error());
- }
- /*
- If the table we've just written a record to contains an auto_increment
- field, then store the last_insert_id() value from the foreign server
- */
- if (auto_increment_update_required)
- {
- update_auto_increment();
-
- /* mysql_insert() uses this for protocol return value */
- table->next_number_field->store(auto_increment_value, 1);
- }
-
- DBUG_RETURN(0);
-}
-
-
-/**
- @brief Prepares the storage engine for bulk inserts.
-
- @param[in] rows estimated number of rows in bulk insert
- or 0 if unknown.
-
- @details Initializes memory structures required for bulk insert.
-*/
-
-void ha_federated::start_bulk_insert(ha_rows rows)
-{
- uint page_size;
- DBUG_ENTER("ha_federated::start_bulk_insert");
-
- dynstr_free(&bulk_insert);
-
- /**
- We don't bother with bulk-insert semantics when the estimated rows == 1
- The rows value will be 0 if the server does not know how many rows
- would be inserted. This can occur when performing INSERT...SELECT
- */
-
- if (rows == 1)
- DBUG_VOID_RETURN;
-
- /*
- Make sure we have an open connection so that we know the
- maximum packet size.
- */
- if (!mysql && real_connect())
- DBUG_VOID_RETURN;
-
- page_size= (uint) my_getpagesize();
-
- if (init_dynamic_string(&bulk_insert, NULL, page_size, page_size))
- DBUG_VOID_RETURN;
-
- bulk_insert.length= 0;
- DBUG_VOID_RETURN;
-}
-
-
-/**
- @brief End bulk insert.
-
- @details This method will send any remaining rows to the remote server.
- Finally, it will deinitialize the bulk insert data structure.
-
- @return Operation status
- @retval 0 No error
- @retval != 0 Error occured at remote server. Also sets my_errno.
-*/
-
-int ha_federated::end_bulk_insert()
-{
- int error= 0;
- DBUG_ENTER("ha_federated::end_bulk_insert");
-
- if (bulk_insert.str && bulk_insert.length)
- {
- if (real_query(bulk_insert.str, bulk_insert.length))
- error= stash_remote_error();
- else
- if (table->next_number_field)
- update_auto_increment();
- }
-
- dynstr_free(&bulk_insert);
-
- DBUG_RETURN(my_errno= error);
-}
-
-
-/*
- ha_federated::update_auto_increment
-
- This method ensures that last_insert_id() works properly. What it simply does
- is calls last_insert_id() on the foreign database immediately after insert
- (if the table has an auto_increment field) and sets the insert id via
- thd->insert_id(ID) (as well as storing thd->prev_insert_id)
-*/
-void ha_federated::update_auto_increment(void)
-{
- THD *thd= current_thd;
- DBUG_ENTER("ha_federated::update_auto_increment");
-
- ha_federated::info(HA_STATUS_AUTO);
- thd->insert_id(auto_increment_value);
- DBUG_PRINT("info",("last_insert_id: %ld", (long) auto_increment_value));
-
- DBUG_VOID_RETURN;
-}
-
-int ha_federated::optimize(THD* thd, HA_CHECK_OPT* check_opt)
-{
- char query_buffer[STRING_BUFFER_USUAL_SIZE];
- String query(query_buffer, sizeof(query_buffer), &my_charset_bin);
- DBUG_ENTER("ha_federated::optimize");
-
- query.length(0);
-
- query.set_charset(system_charset_info);
- query.append(FEDERATED_OPTIMIZE);
- append_ident(&query, share->table_name, share->table_name_length,
- ident_quote_char);
-
- if (real_query(query.ptr(), query.length()))
- {
- DBUG_RETURN(stash_remote_error());
- }
-
- DBUG_RETURN(0);
-}
-
-
-int ha_federated::repair(THD* thd, HA_CHECK_OPT* check_opt)
-{
- char query_buffer[STRING_BUFFER_USUAL_SIZE];
- String query(query_buffer, sizeof(query_buffer), &my_charset_bin);
- DBUG_ENTER("ha_federated::repair");
-
- query.length(0);
-
- query.set_charset(system_charset_info);
- query.append(FEDERATED_REPAIR);
- append_ident(&query, share->table_name, share->table_name_length,
- ident_quote_char);
- if (check_opt->flags & T_QUICK)
- query.append(FEDERATED_QUICK);
- if (check_opt->flags & T_EXTEND)
- query.append(FEDERATED_EXTENDED);
- if (check_opt->sql_flags & TT_USEFRM)
- query.append(FEDERATED_USE_FRM);
-
- if (real_query(query.ptr(), query.length()))
- {
- DBUG_RETURN(stash_remote_error());
- }
-
- DBUG_RETURN(0);
-}
-
-
-/*
- Yes, update_row() does what you expect, it updates a row. old_data will have
- the previous row record in it, while new_data will have the newest data in
- it.
-
- Keep in mind that the server can do updates based on ordering if an ORDER BY
- clause was used. Consecutive ordering is not guarenteed.
- Currently new_data will not have an updated auto_increament record, or
- and updated timestamp field. You can do these for federated by doing these:
- if (table->timestamp_on_update_now)
- update_timestamp(new_row+table->timestamp_on_update_now-1);
- if (table->next_number_field && record == table->record[0])
- update_auto_increment();
-
- Called from sql_select.cc, sql_acl.cc, sql_update.cc, and sql_insert.cc.
-*/
-
-int ha_federated::update_row(const byte *old_data, byte *new_data)
-{
- /*
- This used to control how the query was built. If there was a
- primary key, the query would be built such that there was a where
- clause with only that column as the condition. This is flawed,
- because if we have a multi-part primary key, it would only use the
- first part! We don't need to do this anyway, because
- read_range_first will retrieve the correct record, which is what
- is used to build the WHERE clause. We can however use this to
- append a LIMIT to the end if there is NOT a primary key. Why do
- this? Because we only are updating one record, and LIMIT enforces
- this.
- */
- bool has_a_primary_key= (table->s->primary_key == 0 ? TRUE : FALSE);
- /*
- buffers for following strings
- */
- char field_value_buffer[STRING_BUFFER_USUAL_SIZE];
- char update_buffer[FEDERATED_QUERY_BUFFER_SIZE];
- char where_buffer[FEDERATED_QUERY_BUFFER_SIZE];
-
- /* Work area for field values */
- String field_value(field_value_buffer, sizeof(field_value_buffer),
- &my_charset_bin);
- /* stores the update query */
- String update_string(update_buffer,
- sizeof(update_buffer),
- &my_charset_bin);
- /* stores the WHERE clause */
- String where_string(where_buffer,
- sizeof(where_buffer),
- &my_charset_bin);
- DBUG_ENTER("ha_federated::update_row");
- /*
- set string lengths to 0 to avoid misc chars in string
- */
- field_value.length(0);
- update_string.length(0);
- where_string.length(0);
-
- if (ignore_duplicates)
- update_string.append(STRING_WITH_LEN("UPDATE IGNORE "));
- else
- update_string.append(STRING_WITH_LEN("UPDATE "));
- append_ident(&update_string, share->table_name,
- share->table_name_length, ident_quote_char);
- update_string.append(FEDERATED_SET);
-
-/*
- In this loop, we want to match column names to values being inserted
- (while building INSERT statement).
-
- Iterate through table->field (new data) and share->old_field (old_data)
- using the same index to create an SQL UPDATE statement. New data is
- used to create SET field=value and old data is used to create WHERE
- field=oldvalue
- */
-
- for (Field **field= table->field; *field; field++)
- {
- size_t field_name_length= strlen((*field)->field_name);
- append_ident(&where_string, (*field)->field_name, (uint) field_name_length,
- ident_quote_char);
- append_ident(&update_string, (*field)->field_name, (uint) field_name_length,
- ident_quote_char);
- update_string.append(FEDERATED_EQ);
-
- if ((*field)->is_null())
- update_string.append(FEDERATED_NULL);
- else
- {
- /* otherwise = */
- (*field)->val_str(&field_value);
- update_string.append(value_quote_char);
- field_value.print(&update_string);
- update_string.append(value_quote_char);
- field_value.length(0);
- }
-
- if (field_in_record_is_null(table, *field, (char*) old_data))
- where_string.append(FEDERATED_ISNULL);
- else
- {
- where_string.append(FEDERATED_EQ);
- (*field)->val_str(&field_value,
- (char*) (old_data + (*field)->offset()));
- where_string.append(value_quote_char);
- field_value.print(&where_string);
- where_string.append(value_quote_char);
- field_value.length(0);
- }
-
- /*
- Only append conjunctions if we have another field in which
- to iterate
- */
- if (*(field + 1))
- {
- update_string.append(FEDERATED_COMMA);
- where_string.append(FEDERATED_AND);
- }
- }
- update_string.append(FEDERATED_WHERE);
- update_string.append(where_string);
- /*
- If this table has not a primary key, then we could possibly
- update multiple rows. We want to make sure to only update one!
- */
- if (!has_a_primary_key)
- update_string.append(FEDERATED_LIMIT1);
-
- if (real_query(update_string.ptr(), update_string.length()))
- {
- DBUG_RETURN(stash_remote_error());
- }
- DBUG_RETURN(0);
-}
-
-/*
- This will delete a row. 'buf' will contain a copy of the row to be =deleted.
- The server will call this right after the current row has been called (from
- either a previous rnd_next() or index call).
- If you keep a pointer to the last row or can access a primary key it will
- make doing the deletion quite a bit easier.
- Keep in mind that the server does no guarentee consecutive deletions.
- ORDER BY clauses can be used.
-
- Called in sql_acl.cc and sql_udf.cc to manage internal table information.
- Called in sql_delete.cc, sql_insert.cc, and sql_select.cc. In sql_select
- it is used for removing duplicates while in insert it is used for REPLACE
- calls.
-*/
-
-int ha_federated::delete_row(const byte *buf)
-{
- char delete_buffer[FEDERATED_QUERY_BUFFER_SIZE];
- char data_buffer[FEDERATED_QUERY_BUFFER_SIZE];
-
- String delete_string(delete_buffer, sizeof(delete_buffer), &my_charset_bin);
- String data_string(data_buffer, sizeof(data_buffer), &my_charset_bin);
- DBUG_ENTER("ha_federated::delete_row");
-
- delete_string.length(0);
- delete_string.append(FEDERATED_DELETE);
- delete_string.append(FEDERATED_FROM);
- append_ident(&delete_string, share->table_name,
- share->table_name_length, ident_quote_char);
- delete_string.append(FEDERATED_WHERE);
-
- for (Field **field= table->field; *field; field++)
- {
- Field *cur_field= *field;
- data_string.length(0);
- append_ident(&delete_string, (*field)->field_name,
- (uint) strlen((*field)->field_name), ident_quote_char);
-
- if (cur_field->is_null())
- {
- delete_string.append(FEDERATED_ISNULL);
- }
- else
- {
- delete_string.append(FEDERATED_EQ);
- cur_field->val_str(&data_string);
- delete_string.append(value_quote_char);
- data_string.print(&delete_string);
- delete_string.append(value_quote_char);
- }
-
- delete_string.append(FEDERATED_AND);
- }
- delete_string.length(delete_string.length()-5); // Remove trailing AND
-
- delete_string.append(FEDERATED_LIMIT1);
- DBUG_PRINT("info",
- ("Delete sql: %s", delete_string.c_ptr_quick()));
- if (real_query(delete_string.ptr(), delete_string.length()))
- {
- DBUG_RETURN(stash_remote_error());
- }
- deleted+= (ha_rows) mysql->affected_rows;
- records-= (ha_rows) mysql->affected_rows;
- DBUG_PRINT("info",
- ("rows deleted %ld rows deleted for all time %ld",
- (long) mysql->affected_rows, (long) deleted));
-
- DBUG_RETURN(0);
-}
-
-
-/*
- Positions an index cursor to the index specified in the handle. Fetches the
- row if available. If the key value is null, begin at the first key of the
- index. This method, which is called in the case of an SQL statement having
- a WHERE clause on a non-primary key index, simply calls index_read_idx.
-*/
-
-int ha_federated::index_read(byte *buf, const byte *key,
- uint key_len, ha_rkey_function find_flag)
-{
- DBUG_ENTER("ha_federated::index_read");
-
- if (stored_result)
- mysql_free_result(stored_result);
- DBUG_RETURN(index_read_idx_with_result_set(buf, active_index, key,
- key_len, find_flag,
- &stored_result));
-}
-
-
-/*
- Positions an index cursor to the index specified in key. Fetches the
- row if any. This is only used to read whole keys.
-
- This method is called via index_read in the case of a WHERE clause using
- a primary key index OR is called DIRECTLY when the WHERE clause
- uses a PRIMARY KEY index.
-
- NOTES
- This uses an internal result set that is deleted before function
- returns. We need to be able to be calable from ha_rnd_pos()
-*/
-
-int ha_federated::index_read_idx(byte *buf, uint index, const byte *key,
- uint key_len, enum ha_rkey_function find_flag)
-{
- int retval;
- MYSQL_RES *mysql_result;
- DBUG_ENTER("ha_federated::index_read_idx");
-
- if ((retval= index_read_idx_with_result_set(buf, index, key,
- key_len, find_flag,
- &mysql_result)))
- DBUG_RETURN(retval);
- mysql_free_result(mysql_result);
- DBUG_RETURN(retval);
-}
-
-
-/*
- Create result set for rows matching query and return first row
-
- RESULT
- 0 ok In this case *result will contain the result set
- table->status == 0
- # error In this case *result will contain 0
- table->status == STATUS_NOT_FOUND
-*/
-
-int ha_federated::index_read_idx_with_result_set(byte *buf, uint index,
- const byte *key,
- uint key_len,
- ha_rkey_function find_flag,
- MYSQL_RES **result)
-{
- int retval;
- char error_buffer[FEDERATED_QUERY_BUFFER_SIZE];
- char index_value[STRING_BUFFER_USUAL_SIZE];
- char sql_query_buffer[FEDERATED_QUERY_BUFFER_SIZE];
- String index_string(index_value,
- sizeof(index_value),
- &my_charset_bin);
- String sql_query(sql_query_buffer,
- sizeof(sql_query_buffer),
- &my_charset_bin);
- key_range range;
- DBUG_ENTER("ha_federated::index_read_idx_with_result_set");
-
- *result= 0; // In case of errors
- index_string.length(0);
- sql_query.length(0);
- statistic_increment(table->in_use->status_var.ha_read_key_count,
- &LOCK_status);
-
- sql_query.append(share->select_query);
-
- range.key= key;
- range.length= key_len;
- range.flag= find_flag;
- create_where_from_key(&index_string,
- &table->key_info[index],
- &range,
- NULL, 0);
- sql_query.append(index_string);
-
- if (real_query(sql_query.ptr(), sql_query.length()))
- {
- my_sprintf(error_buffer, (error_buffer, "error: %d '%s'",
- mysql_errno(mysql), mysql_error(mysql)));
- retval= ER_QUERY_ON_FOREIGN_DATA_SOURCE;
- goto error;
- }
- if (!(*result= mysql_store_result(mysql)))
- {
- retval= HA_ERR_END_OF_FILE;
- goto error;
- }
- if (!(retval= read_next(buf, *result)))
- DBUG_RETURN(retval);
-
- mysql_free_result(*result);
- *result= 0;
- table->status= STATUS_NOT_FOUND;
- DBUG_RETURN(retval);
-
-error:
- table->status= STATUS_NOT_FOUND;
- my_error(retval, MYF(0), error_buffer);
- DBUG_RETURN(retval);
-}
-
-
-/*
- This method is used exlusevely by filesort() to check if we
- can create sorting buffers of necessary size.
- If the handler returns more records that it declares
- here server can just crash on filesort().
- We cannot guarantee that's not going to happen with
- the FEDERATED engine, as we have records==0 always if the
- client is a VIEW, and for the table the number of
- records can inpredictably change during execution.
- So we return maximum possible value here.
-*/
-
-ha_rows ha_federated::estimate_rows_upper_bound()
-{
- return HA_POS_ERROR;
-}
-
-
-/* Initialized at each key walk (called multiple times unlike rnd_init()) */
-
-int ha_federated::index_init(uint keynr)
-{
- DBUG_ENTER("ha_federated::index_init");
- DBUG_PRINT("info", ("table: '%s' key: %u", table->s->table_name, keynr));
- active_index= keynr;
- DBUG_RETURN(0);
-}
-
-
-/*
- Read first range
-*/
-
-int ha_federated::read_range_first(const key_range *start_key,
- const key_range *end_key,
- bool eq_range_arg, bool sorted)
-{
- char sql_query_buffer[FEDERATED_QUERY_BUFFER_SIZE];
- int retval;
- String sql_query(sql_query_buffer,
- sizeof(sql_query_buffer),
- &my_charset_bin);
- DBUG_ENTER("ha_federated::read_range_first");
-
- DBUG_ASSERT(!(start_key == NULL && end_key == NULL));
-
- sql_query.length(0);
- sql_query.append(share->select_query);
- create_where_from_key(&sql_query,
- &table->key_info[active_index],
- start_key, end_key, 0);
-
- if (stored_result)
- {
- mysql_free_result(stored_result);
- stored_result= 0;
- }
- if (real_query(sql_query.ptr(), sql_query.length()))
- {
- retval= ER_QUERY_ON_FOREIGN_DATA_SOURCE;
- goto error;
- }
- sql_query.length(0);
-
- if (!(stored_result= mysql_store_result(mysql)))
- {
- retval= HA_ERR_END_OF_FILE;
- goto error;
- }
-
- retval= read_next(table->record[0], stored_result);
- DBUG_RETURN(retval);
-
-error:
- table->status= STATUS_NOT_FOUND;
- DBUG_RETURN(retval);
-}
-
-
-int ha_federated::read_range_next()
-{
- int retval;
- DBUG_ENTER("ha_federated::read_range_next");
- retval= rnd_next(table->record[0]);
- DBUG_RETURN(retval);
-}
-
-
-/* Used to read forward through the index. */
-int ha_federated::index_next(byte *buf)
-{
- DBUG_ENTER("ha_federated::index_next");
- statistic_increment(table->in_use->status_var.ha_read_next_count,
- &LOCK_status);
- DBUG_RETURN(read_next(buf, stored_result));
-}
-
-
-/*
- rnd_init() is called when the system wants the storage engine to do a table
- scan.
-
- This is the method that gets data for the SELECT calls.
-
- See the federated in the introduction at the top of this file to see when
- rnd_init() is called.
-
- Called from filesort.cc, records.cc, sql_handler.cc, sql_select.cc,
- sql_table.cc, and sql_update.cc.
-*/
-
-int ha_federated::rnd_init(bool scan)
-{
- DBUG_ENTER("ha_federated::rnd_init");
- /*
- The use of the 'scan' flag is incredibly important for this handler
- to work properly, especially with updates containing WHERE clauses
- using indexed columns.
-
- When the initial query contains a WHERE clause of the query using an
- indexed column, it's index_read_idx that selects the exact record from
- the foreign database.
-
- When there is NO index in the query, either due to not having a WHERE
- clause, or the WHERE clause is using columns that are not indexed, a
- 'full table scan' done by rnd_init, which in this situation simply means
- a 'select * from ...' on the foreign table.
-
- In other words, this 'scan' flag gives us the means to ensure that if
- there is an index involved in the query, we want index_read_idx to
- retrieve the exact record (scan flag is 0), and do not want rnd_init
- to do a 'full table scan' and wipe out that result set.
-
- Prior to using this flag, the problem was most apparent with updates.
-
- An initial query like 'UPDATE tablename SET anything = whatever WHERE
- indexedcol = someval', index_read_idx would get called, using a query
- constructed with a WHERE clause built from the values of index ('indexcol'
- in this case, having a value of 'someval'). mysql_store_result would
- then get called (this would be the result set we want to use).
-
- After this rnd_init (from sql_update.cc) would be called, it would then
- unecessarily call "select * from table" on the foreign table, then call
- mysql_store_result, which would wipe out the correct previous result set
- from the previous call of index_read_idx's that had the result set
- containing the correct record, hence update the wrong row!
-
- */
-
- if (scan)
- {
- if (stored_result)
- {
- mysql_free_result(stored_result);
- stored_result= 0;
- }
-
- if (real_query(share->select_query, (uint) strlen(share->select_query)))
- goto error;
-
- stored_result= mysql_store_result(mysql);
- if (!stored_result)
- goto error;
- }
- DBUG_RETURN(0);
-
-error:
- DBUG_RETURN(stash_remote_error());
-}
-
-
-int ha_federated::rnd_end()
-{
- DBUG_ENTER("ha_federated::rnd_end");
- DBUG_RETURN(index_end());
-}
-
-
-int ha_federated::index_end(void)
-{
- DBUG_ENTER("ha_federated::index_end");
- if (stored_result)
- {
- mysql_free_result(stored_result);
- stored_result= 0;
- }
- active_index= MAX_KEY;
- DBUG_RETURN(0);
-}
-
-/*
- This is called for each row of the table scan. When you run out of records
- you should return HA_ERR_END_OF_FILE. Fill buff up with the row information.
- The Field structure for the table is the key to getting data into buf
- in a manner that will allow the server to understand it.
-
- Called from filesort.cc, records.cc, sql_handler.cc, sql_select.cc,
- sql_table.cc, and sql_update.cc.
-*/
-
-int ha_federated::rnd_next(byte *buf)
-{
- DBUG_ENTER("ha_federated::rnd_next");
-
- if (stored_result == 0)
- {
- /*
- Return value of rnd_init is not always checked (see records.cc),
- so we can get here _even_ if there is _no_ pre-fetched result-set!
- TODO: fix it. We can delete this in 5.1 when rnd_init() is checked.
- */
- DBUG_RETURN(1);
- }
- DBUG_RETURN(read_next(buf, stored_result));
-}
-
-
-/*
- ha_federated::read_next
-
- reads from a result set and converts to mysql internal
- format
-
- SYNOPSIS
- field_in_record_is_null()
- buf byte pointer to record
- result mysql result set
-
- DESCRIPTION
- This method is a wrapper method that reads one record from a result
- set and converts it to the internal table format
-
- RETURN VALUE
- 1 error
- 0 no error
-*/
-
-int ha_federated::read_next(byte *buf, MYSQL_RES *result)
-{
- int retval;
- MYSQL_ROW row;
- DBUG_ENTER("ha_federated::read_next");
-
- table->status= STATUS_NOT_FOUND; // For easier return
-
- /* Fetch a row, insert it back in a row format. */
- if (!(row= mysql_fetch_row(result)))
- DBUG_RETURN(HA_ERR_END_OF_FILE);
-
- if (!(retval= convert_row_to_internal_format(buf, row, result)))
- table->status= 0;
-
- DBUG_RETURN(retval);
-}
-
-
-/*
- store reference to current row so that we can later find it for
- a re-read, update or delete.
-
- In case of federated, a reference is either a primary key or
- the whole record.
-
- Called from filesort.cc, sql_select.cc, sql_delete.cc and sql_update.cc.
-*/
-
-void ha_federated::position(const byte *record)
-{
- DBUG_ENTER("ha_federated::position");
- if (table->s->primary_key != MAX_KEY)
- key_copy(ref, (byte *)record, table->key_info + table->s->primary_key,
- ref_length);
- else
- memcpy(ref, record, ref_length);
- DBUG_VOID_RETURN;
-}
-
-
-/*
- This is like rnd_next, but you are given a position to use to determine the
- row. The position will be of the type that you stored in ref.
-
- This method is required for an ORDER BY
-
- Called from filesort.cc records.cc sql_insert.cc sql_select.cc sql_update.cc.
-*/
-
-int ha_federated::rnd_pos(byte *buf, byte *pos)
-{
- int result;
- DBUG_ENTER("ha_federated::rnd_pos");
- statistic_increment(table->in_use->status_var.ha_read_rnd_count,
- &LOCK_status);
- if (table->s->primary_key != MAX_KEY)
- {
- /* We have a primary key, so use index_read_idx to find row */
- result= index_read_idx(buf, table->s->primary_key, pos,
- ref_length, HA_READ_KEY_EXACT);
- }
- else
- {
- /* otherwise, get the old record ref as obtained in ::position */
- memcpy(buf, pos, ref_length);
- result= 0;
- }
- table->status= result ? STATUS_NOT_FOUND : 0;
- DBUG_RETURN(result);
-}
-
-
-/*
- ::info() is used to return information to the optimizer.
- Currently this table handler doesn't implement most of the fields
- really needed. SHOW also makes use of this data
- Another note, you will probably want to have the following in your
- code:
- if (records < 2)
- records = 2;
- The reason is that the server will optimize for cases of only a single
- record. If in a table scan you don't know the number of records
- it will probably be better to set records to two so you can return
- as many records as you need.
- Along with records a few more variables you may wish to set are:
- records
- deleted
- data_file_length
- index_file_length
- delete_length
- check_time
- Take a look at the public variables in handler.h for more information.
-
- Called in:
- filesort.cc
- ha_heap.cc
- item_sum.cc
- opt_sum.cc
- sql_delete.cc
- sql_delete.cc
- sql_derived.cc
- sql_select.cc
- sql_select.cc
- sql_select.cc
- sql_select.cc
- sql_select.cc
- sql_show.cc
- sql_show.cc
- sql_show.cc
- sql_show.cc
- sql_table.cc
- sql_union.cc
- sql_update.cc
-
-*/
-
-int ha_federated::info(uint flag)
-{
- char error_buffer[FEDERATED_QUERY_BUFFER_SIZE];
- char status_buf[FEDERATED_QUERY_BUFFER_SIZE];
- int error;
- uint error_code;
- MYSQL_RES *result= 0;
- MYSQL_ROW row;
- String status_query_string(status_buf, sizeof(status_buf), &my_charset_bin);
- DBUG_ENTER("ha_federated::info");
-
- error_code= ER_QUERY_ON_FOREIGN_DATA_SOURCE;
- /* we want not to show table status if not needed to do so */
- if (flag & (HA_STATUS_VARIABLE | HA_STATUS_CONST))
- {
- status_query_string.length(0);
- status_query_string.append(FEDERATED_INFO);
- append_ident(&status_query_string, share->table_name,
- share->table_name_length, value_quote_char);
-
- if (real_query(status_query_string.ptr(), status_query_string.length()))
- goto error;
-
- status_query_string.length(0);
-
- result= mysql_store_result(mysql);
-
- /*
- We're going to use fields num. 4, 12 and 13 of the resultset,
- so make sure we have these fields.
- */
- if (!result || (mysql_num_fields(result) < 14))
- goto error;
-
- if (!mysql_num_rows(result))
- goto error;
-
- if (!(row= mysql_fetch_row(result)))
- goto error;
-
- if (flag & HA_STATUS_VARIABLE | HA_STATUS_CONST)
- {
- /*
- deleted is set in ha_federated::info
- */
- /*
- need to figure out what this means as far as federated is concerned,
- since we don't have a "file"
-
- data_file_length = ?
- index_file_length = ?
- delete_length = ?
- */
- if (row[4] != NULL)
- records= (ha_rows) my_strtoll10(row[4], (char**) 0, &error);
-
- mean_rec_length= table->s->reclength;
- data_file_length= records * mean_rec_length;
-
- if (row[12] != NULL)
- update_time= (time_t) my_strtoll10(row[12], (char**) 0, &error);
- if (row[13] != NULL)
- check_time= (time_t) my_strtoll10(row[13], (char**) 0, &error);
- }
-
- /*
- size of IO operations (This is based on a good guess, no high science
- involved)
- */
- block_size= 4096;
- }
-
- if (flag & HA_STATUS_AUTO)
- auto_increment_value= mysql->last_used_con->insert_id;
-
- mysql_free_result(result);
-
- DBUG_RETURN(0);
-
-error:
- mysql_free_result(result);
- if (mysql)
- {
- my_sprintf(error_buffer, (error_buffer, ": %d : %s",
- mysql_errno(mysql), mysql_error(mysql)));
- my_error(error_code, MYF(0), error_buffer);
- }
- else
- if (remote_error_number != -1 /* error already reported */)
- {
- error_code= remote_error_number;
- my_error(error_code, MYF(0), ER(error_code));
- }
- DBUG_RETURN(error_code);
-}
-
-
-/**
- @brief Handles extra signals from MySQL server
-
- @param[in] operation Hint for storage engine
-
- @return Operation Status
- @retval 0 OK
- */
-int ha_federated::extra(ha_extra_function operation)
-{
- DBUG_ENTER("ha_federated::extra");
- switch (operation) {
- case HA_EXTRA_IGNORE_DUP_KEY:
- ignore_duplicates= TRUE;
- break;
- case HA_EXTRA_NO_IGNORE_DUP_KEY:
- insert_dup_update= FALSE;
- ignore_duplicates= FALSE;
- break;
- case HA_EXTRA_WRITE_CAN_REPLACE:
- replace_duplicates= TRUE;
- break;
- case HA_EXTRA_WRITE_CANNOT_REPLACE:
- /*
- We use this flag to ensure that we do not create an "INSERT IGNORE"
- statement when inserting new rows into the remote table.
- */
- replace_duplicates= FALSE;
- break;
- case HA_EXTRA_INSERT_WITH_UPDATE:
- insert_dup_update= TRUE;
- break;
- case HA_EXTRA_RESET:
- insert_dup_update= FALSE;
- ignore_duplicates= FALSE;
- replace_duplicates= FALSE;
- break;
- default:
- /* do nothing */
- DBUG_PRINT("info",("unhandled operation: %d", (uint) operation));
- }
- DBUG_RETURN(0);
-}
-
-
-/*
- Used to delete all rows in a table. Both for cases of truncate and
- for cases where the optimizer realizes that all rows will be
- removed as a result of a SQL statement.
-
- Called from item_sum.cc by Item_func_group_concat::clear(),
- Item_sum_count_distinct::clear(), and Item_func_group_concat::clear().
- Called from sql_delete.cc by mysql_delete().
- Called from sql_select.cc by JOIN::reinit().
- Called from sql_union.cc by st_select_lex_unit::exec().
-*/
-
-int ha_federated::delete_all_rows()
-{
- char query_buffer[FEDERATED_QUERY_BUFFER_SIZE];
- String query(query_buffer, sizeof(query_buffer), &my_charset_bin);
- DBUG_ENTER("ha_federated::delete_all_rows");
-
- query.length(0);
-
- query.set_charset(system_charset_info);
- query.append(FEDERATED_TRUNCATE);
- append_ident(&query, share->table_name, share->table_name_length,
- ident_quote_char);
-
- /*
- TRUNCATE won't return anything in mysql_affected_rows
- */
- if (real_query(query.ptr(), query.length()))
- {
- DBUG_RETURN(stash_remote_error());
- }
- deleted+= records;
- records= 0;
- DBUG_RETURN(0);
-}
-
-
-/*
- The idea with handler::store_lock() is the following:
-
- The statement decided which locks we should need for the table
- for updates/deletes/inserts we get WRITE locks, for SELECT... we get
- read locks.
-
- Before adding the lock into the table lock handler (see thr_lock.c)
- mysqld calls store lock with the requested locks. Store lock can now
- modify a write lock to a read lock (or some other lock), ignore the
- lock (if we don't want to use MySQL table locks at all) or add locks
- for many tables (like we do when we are using a MERGE handler).
-
- Berkeley DB for federated changes all WRITE locks to TL_WRITE_ALLOW_WRITE
- (which signals that we are doing WRITES, but we are still allowing other
- reader's and writer's.
-
- When releasing locks, store_lock() are also called. In this case one
- usually doesn't have to do anything.
-
- In some exceptional cases MySQL may send a request for a TL_IGNORE;
- This means that we are requesting the same lock as last time and this
- should also be ignored. (This may happen when someone does a flush
- table when we have opened a part of the tables, in which case mysqld
- closes and reopens the tables and tries to get the same locks at last
- time). In the future we will probably try to remove this.
-
- Called from lock.cc by get_lock_data().
-*/
-
-THR_LOCK_DATA **ha_federated::store_lock(THD *thd,
- THR_LOCK_DATA **to,
- enum thr_lock_type lock_type)
-{
- DBUG_ENTER("ha_federated::store_lock");
- if (lock_type != TL_IGNORE && lock.type == TL_UNLOCK)
- {
- /*
- Here is where we get into the guts of a row level lock.
- If TL_UNLOCK is set
- If we are not doing a LOCK TABLE or DISCARD/IMPORT
- TABLESPACE, then allow multiple writers
- */
-
- if ((lock_type >= TL_WRITE_CONCURRENT_INSERT &&
- lock_type <= TL_WRITE) && !thd->in_lock_tables)
- lock_type= TL_WRITE_ALLOW_WRITE;
-
- /*
- In queries of type INSERT INTO t1 SELECT ... FROM t2 ...
- MySQL would use the lock TL_READ_NO_INSERT on t2, and that
- would conflict with TL_WRITE_ALLOW_WRITE, blocking all inserts
- to t2. Convert the lock to a normal read lock to allow
- concurrent inserts to t2.
- */
-
- if (lock_type == TL_READ_NO_INSERT && !thd->in_lock_tables)
- lock_type= TL_READ;
-
- lock.type= lock_type;
- }
-
- *to++= &lock;
-
- DBUG_RETURN(to);
-}
-
-/*
- create() does nothing, since we have no local setup of our own.
- FUTURE: We should potentially connect to the foreign database and
-*/
-
-int ha_federated::create(const char *name, TABLE *table_arg,
- HA_CREATE_INFO *create_info)
-{
- int retval;
- FEDERATED_SHARE tmp_share; // Only a temporary share, to test the url
- DBUG_ENTER("ha_federated::create");
-
- retval= parse_url(&tmp_share, table_arg, 1);
-
- my_free((gptr) tmp_share.scheme, MYF(MY_ALLOW_ZERO_PTR));
- DBUG_RETURN(retval);
-
-}
-
-
-int ha_federated::real_connect()
-{
- char buffer[FEDERATED_QUERY_BUFFER_SIZE];
- String sql_query(buffer, sizeof(buffer), &my_charset_bin);
- DBUG_ENTER("ha_federated::real_connect");
-
- /*
- Bug#25679
- Ensure that we do not hold the LOCK_open mutex while attempting
- to establish Federated connection to guard against a trivial
- Denial of Service scenerio.
- */
- safe_mutex_assert_not_owner(&LOCK_open);
-
- DBUG_ASSERT(mysql == NULL);
-
- if (!(mysql= mysql_init(NULL)))
- {
- remote_error_number= HA_ERR_OUT_OF_MEM;
- DBUG_RETURN(-1);
- }
-
- /*
- BUG# 17044 Federated Storage Engine is not UTF8 clean
- Add set names to whatever charset the table is at open
- of table
- */
- /* this sets the csname like 'set names utf8' */
- mysql_options(mysql,MYSQL_SET_CHARSET_NAME,
- this->table->s->table_charset->csname);
-
- sql_query.length(0);
-
- if (!mysql_real_connect(mysql,
- share->hostname,
- share->username,
- share->password,
- share->database,
- share->port,
- share->socket, 0))
- {
- stash_remote_error();
- mysql_close(mysql);
- mysql= NULL;
- my_error(ER_CONNECT_TO_FOREIGN_DATA_SOURCE, MYF(0), remote_error_buf);
- remote_error_number= -1;
- DBUG_RETURN(-1);
- }
-
- /*
- We have established a connection, lets try a simple dummy query just
- to check that the table and expected columns are present.
- */
- sql_query.append(share->select_query);
- sql_query.append(FEDERATED_WHERE);
- sql_query.append(FEDERATED_FALSE);
- if (mysql_real_query(mysql, sql_query.ptr(), sql_query.length()))
- {
- sql_query.length(0);
- sql_query.append("error: ");
- sql_query.qs_append(mysql_errno(mysql));
- sql_query.append(" '");
- sql_query.append(mysql_error(mysql));
- sql_query.append("'");
- mysql_close(mysql);
- mysql= NULL;
- my_error(ER_FOREIGN_DATA_SOURCE_DOESNT_EXIST, MYF(0), sql_query.ptr());
- remote_error_number= -1;
- DBUG_RETURN(-1);
- }
-
- /* Just throw away the result, no rows anyways but need to keep in sync */
- mysql_free_result(mysql_store_result(mysql));
-
- /*
- Since we do not support transactions at this version, we can let the client
- API silently reconnect. For future versions, we will need more logic to
- deal with transactions
- */
-
- mysql->reconnect= 1;
- DBUG_RETURN(0);
-}
-
-
-int ha_federated::real_query(const char *query, uint length)
-{
- int rc= 0;
- DBUG_ENTER("ha_federated::real_query");
-
- if (!mysql && (rc= real_connect()))
- goto end;
-
- if (!query || !length)
- goto end;
-
- rc= mysql_real_query(mysql, query, length);
-
-end:
- DBUG_RETURN(rc);
-}
-
-
-int ha_federated::stash_remote_error()
-{
- DBUG_ENTER("ha_federated::stash_remote_error()");
- if (!mysql)
- DBUG_RETURN(remote_error_number);
- remote_error_number= mysql_errno(mysql);
- strmake(remote_error_buf, mysql_error(mysql), sizeof(remote_error_buf)-1);
- if (remote_error_number == ER_DUP_ENTRY ||
- remote_error_number == ER_DUP_KEY)
- DBUG_RETURN(HA_ERR_FOUND_DUPP_KEY);
- DBUG_RETURN(HA_FEDERATED_ERROR_WITH_REMOTE_SYSTEM);
-}
-
-
-bool ha_federated::get_error_message(int error, String* buf)
-{
- DBUG_ENTER("ha_federated::get_error_message");
- DBUG_PRINT("enter", ("error: %d", error));
- if (error == HA_FEDERATED_ERROR_WITH_REMOTE_SYSTEM)
- {
- buf->append(STRING_WITH_LEN("Error on remote system: "));
- buf->qs_append(remote_error_number);
- buf->append(STRING_WITH_LEN(": "));
- buf->append(remote_error_buf);
-
- remote_error_number= 0;
- remote_error_buf[0]= '\0';
- }
- DBUG_PRINT("exit", ("message: %s", buf->ptr()));
- DBUG_RETURN(FALSE);
-}
-
-#endif /* HAVE_FEDERATED_DB */
diff --git a/sql/ha_federated.h b/sql/ha_federated.h
deleted file mode 100644
index 349c596ae5a..00000000000
--- a/sql/ha_federated.h
+++ /dev/null
@@ -1,324 +0,0 @@
-/* Copyright (C) 2003 MySQL AB
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; version 2 of the License.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
-
-/*
- Please read ha_exmple.cc before reading this file.
- Please keep in mind that the federated storage engine implements all methods
- that are required to be implemented. handler.h has a full list of methods
- that you can implement.
-*/
-
-#ifdef USE_PRAGMA_INTERFACE
-#pragma interface /* gcc class implementation */
-#endif
-
-#include <mysql.h>
-
-/*
- handler::print_error has a case statement for error numbers.
- This value is (10000) is far out of range and will envoke the
- default: case.
- (Current error range is 120-159 from include/my_base.h)
-*/
-#define HA_FEDERATED_ERROR_WITH_REMOTE_SYSTEM 10000
-
-#define FEDERATED_QUERY_BUFFER_SIZE STRING_BUFFER_USUAL_SIZE * 5
-#define FEDERATED_RECORDS_IN_RANGE 2
-
-#define FEDERATED_MAX_KEY_LENGTH 3500 // Same as innodb
-#define FEDERATED_INFO " SHOW TABLE STATUS LIKE "
-#define FEDERATED_INFO_LEN sizeof(FEDERATED_INFO)
-#define FEDERATED_SELECT "SELECT "
-#define FEDERATED_SELECT_LEN sizeof(FEDERATED_SELECT)
-#define FEDERATED_WHERE " WHERE "
-#define FEDERATED_WHERE_LEN sizeof(FEDERATED_WHERE)
-#define FEDERATED_FROM " FROM "
-#define FEDERATED_FROM_LEN sizeof(FEDERATED_FROM)
-#define FEDERATED_PERCENT "%"
-#define FEDERATED_PERCENT_LEN sizeof(FEDERATED_PERCENT)
-#define FEDERATED_IS " IS "
-#define FEDERATED_IS_LEN sizeof(FEDERATED_IS)
-#define FEDERATED_NULL " NULL "
-#define FEDERATED_NULL_LEN sizeof(FEDERATED_NULL)
-#define FEDERATED_ISNULL " IS NULL "
-#define FEDERATED_ISNULL_LEN sizeof(FEDERATED_ISNULL)
-#define FEDERATED_LIKE " LIKE "
-#define FEDERATED_LIKE_LEN sizeof(FEDERATED_LIKE)
-#define FEDERATED_TRUNCATE "TRUNCATE "
-#define FEDERATED_TRUNCATE_LEN sizeof(FEDERATED_TRUNCATE)
-#define FEDERATED_DELETE "DELETE "
-#define FEDERATED_DELETE_LEN sizeof(FEDERATED_DELETE)
-#define FEDERATED_INSERT "INSERT INTO "
-#define FEDERATED_INSERT_LEN sizeof(FEDERATED_INSERT)
-#define FEDERATED_OPTIMIZE "OPTIMIZE TABLE "
-#define FEDERATED_OPTIMIZE_LEN sizeof(FEDERATED_OPTIMIZE)
-#define FEDERATED_REPAIR "REPAIR TABLE "
-#define FEDERATED_REPAIR_LEN sizeof(FEDERATED_REPAIR)
-#define FEDERATED_QUICK " QUICK"
-#define FEDERATED_QUICK_LEN sizeof(FEDERATED_QUICK)
-#define FEDERATED_EXTENDED " EXTENDED"
-#define FEDERATED_EXTENDED_LEN sizeof(FEDERATED_EXTENDED)
-#define FEDERATED_USE_FRM " USE_FRM"
-#define FEDERATED_USE_FRM_LEN sizeof(FEDERATED_USE_FRM)
-#define FEDERATED_LIMIT1 " LIMIT 1"
-#define FEDERATED_LIMIT1_LEN sizeof(FEDERATED_LIMIT1)
-#define FEDERATED_VALUES "VALUES "
-#define FEDERATED_VALUES_LEN sizeof(FEDERATED_VALUES)
-#define FEDERATED_UPDATE "UPDATE "
-#define FEDERATED_UPDATE_LEN sizeof(FEDERATED_UPDATE)
-#define FEDERATED_SET " SET "
-#define FEDERATED_SET_LEN sizeof(FEDERATED_SET)
-#define FEDERATED_AND " AND "
-#define FEDERATED_AND_LEN sizeof(FEDERATED_AND)
-#define FEDERATED_CONJUNCTION ") AND ("
-#define FEDERATED_CONJUNCTION_LEN sizeof(FEDERATED_CONJUNCTION)
-#define FEDERATED_OR " OR "
-#define FEDERATED_OR_LEN sizeof(FEDERATED_OR)
-#define FEDERATED_NOT " NOT "
-#define FEDERATED_NOT_LEN sizeof(FEDERATED_NOT)
-#define FEDERATED_STAR "* "
-#define FEDERATED_STAR_LEN sizeof(FEDERATED_STAR)
-#define FEDERATED_SPACE " "
-#define FEDERATED_SPACE_LEN sizeof(FEDERATED_SPACE)
-#define FEDERATED_SQUOTE "'"
-#define FEDERATED_SQUOTE_LEN sizeof(FEDERATED_SQUOTE)
-#define FEDERATED_COMMA ", "
-#define FEDERATED_COMMA_LEN sizeof(FEDERATED_COMMA)
-#define FEDERATED_BTICK "`"
-#define FEDERATED_BTICK_LEN sizeof(FEDERATED_BTICK)
-#define FEDERATED_OPENPAREN " ("
-#define FEDERATED_OPENPAREN_LEN sizeof(FEDERATED_OPENPAREN)
-#define FEDERATED_CLOSEPAREN ") "
-#define FEDERATED_CLOSEPAREN_LEN sizeof(FEDERATED_CLOSEPAREN)
-#define FEDERATED_NE " != "
-#define FEDERATED_NE_LEN sizeof(FEDERATED_NE)
-#define FEDERATED_GT " > "
-#define FEDERATED_GT_LEN sizeof(FEDERATED_GT)
-#define FEDERATED_LT " < "
-#define FEDERATED_LT_LEN sizeof(FEDERATED_LT)
-#define FEDERATED_LE " <= "
-#define FEDERATED_LE_LEN sizeof(FEDERATED_LE)
-#define FEDERATED_GE " >= "
-#define FEDERATED_GE_LEN sizeof(FEDERATED_GE)
-#define FEDERATED_EQ " = "
-#define FEDERATED_EQ_LEN sizeof(FEDERATED_EQ)
-#define FEDERATED_FALSE " 1=0"
-#define FEDERATED_FALSE_LEN sizeof(FEDERATED_FALSE)
-
-/*
- FEDERATED_SHARE is a structure that will be shared amoung all open handlers
- The example implements the minimum of what you will probably need.
-*/
-typedef struct st_federated_share {
- /*
- the primary select query to be used in rnd_init
- */
- char *select_query;
- /*
- remote host info, parse_url supplies
- */
- char *scheme;
- char *connect_string;
- char *hostname;
- char *username;
- char *password;
- char *database;
- char *table_name;
- char *table;
- char *socket;
- char *sport;
- ushort port;
- uint table_name_length, connect_string_length, use_count;
- pthread_mutex_t mutex;
- THR_LOCK lock;
-} FEDERATED_SHARE;
-
-/*
- Class definition for the storage engine
-*/
-class ha_federated: public handler
-{
- THR_LOCK_DATA lock; /* MySQL lock */
- FEDERATED_SHARE *share; /* Shared lock info */
- MYSQL *mysql; /* MySQL connection */
- MYSQL_RES *stored_result;
- uint fetch_num; // stores the fetch num
- MYSQL_ROW_OFFSET current_position; // Current position used by ::position()
- int remote_error_number;
- char remote_error_buf[FEDERATED_QUERY_BUFFER_SIZE];
- bool ignore_duplicates, replace_duplicates;
- bool insert_dup_update;
- DYNAMIC_STRING bulk_insert;
-
-private:
- /*
- return 0 on success
- return errorcode otherwise
- */
- uint convert_row_to_internal_format(byte *buf, MYSQL_ROW row,
- MYSQL_RES *result);
- bool create_where_from_key(String *to, KEY *key_info,
- const key_range *start_key,
- const key_range *end_key,
- bool records_in_range);
- int stash_remote_error();
-
- bool append_stmt_insert(String *query);
-
- int read_next(byte *buf, MYSQL_RES *result);
- int index_read_idx_with_result_set(byte *buf, uint index,
- const byte *key,
- uint key_len,
- ha_rkey_function find_flag,
- MYSQL_RES **result);
- int real_query(const char *query, uint length);
- int real_connect();
-public:
- ha_federated(TABLE *table_arg);
- ~ha_federated()
- {
- }
- /* The name that will be used for display purposes */
- const char *table_type() const { return "FEDERATED"; }
- /*
- The name of the index type that will be used for display
- don't implement this method unless you really have indexes
- */
- // perhaps get index type
- const char *index_type(uint inx) { return "REMOTE"; }
- const char **bas_ext() const;
- /*
- This is a list of flags that says what the storage engine
- implements. The current table flags are documented in
- handler.h
- */
- ulong table_flags() const
- {
- /* fix server to be able to get remote server table flags */
- return (HA_NOT_EXACT_COUNT |
- HA_PRIMARY_KEY_IN_READ_INDEX | HA_FILE_BASED | HA_REC_NOT_IN_SEQ |
- HA_AUTO_PART_KEY | HA_CAN_INDEX_BLOBS| HA_NO_PREFIX_CHAR_KEYS |
- HA_NULL_IN_KEY
- );
- }
- /*
- This is a bitmap of flags that says how the storage engine
- implements indexes. The current index flags are documented in
- handler.h. If you do not implement indexes, just return zero
- here.
-
- part is the key part to check. First key part is 0
- If all_parts it's set, MySQL want to know the flags for the combined
- index up to and including 'part'.
- */
- /* fix server to be able to get remote server index flags */
- ulong index_flags(uint inx, uint part, bool all_parts) const
- {
- return (HA_READ_NEXT | HA_READ_RANGE | HA_READ_AFTER_KEY);
- }
- uint max_supported_record_length() const { return HA_MAX_REC_LENGTH; }
- uint max_supported_keys() const { return MAX_KEY; }
- uint max_supported_key_parts() const { return MAX_REF_PARTS; }
- uint max_supported_key_length() const { return FEDERATED_MAX_KEY_LENGTH; }
- uint max_supported_key_part_length() const { return FEDERATED_MAX_KEY_LENGTH; }
- /*
- Called in test_quick_select to determine if indexes should be used.
- Normally, we need to know number of blocks . For federated we need to
- know number of blocks on remote side, and number of packets and blocks
- on the network side (?)
- Talk to Kostja about this - how to get the
- number of rows * ...
- disk scan time on other side (block size, size of the row) + network time ...
- The reason for "records * 1000" is that such a large number forces
- this to use indexes "
- */
- double scan_time()
- {
- DBUG_PRINT("info", ("records %ld", (long) records));
- return (double)(records*1000);
- }
- /*
- The next method will never be called if you do not implement indexes.
- */
- double read_time(uint index, uint ranges, ha_rows rows)
- {
- /*
- Per Brian, this number is bugus, but this method must be implemented,
- and at a later date, he intends to document this issue for handler code
- */
- return (double) rows / 20.0+1;
- }
-
- const key_map *keys_to_use_for_scanning() { return &key_map_full; }
- /*
- Everything below are methods that we implment in ha_federated.cc.
-
- Most of these methods are not obligatory, skip them and
- MySQL will treat them as not implemented
- */
- int open(const char *name, int mode, uint test_if_locked); // required
- int close(void); // required
-
- void start_bulk_insert(ha_rows rows);
- int end_bulk_insert();
- int write_row(byte *buf);
- int update_row(const byte *old_data, byte *new_data);
- int delete_row(const byte *buf);
- int index_init(uint keynr);
- ha_rows estimate_rows_upper_bound();
- int index_read(byte *buf, const byte *key,
- uint key_len, enum ha_rkey_function find_flag);
- int index_read_idx(byte *buf, uint idx, const byte *key,
- uint key_len, enum ha_rkey_function find_flag);
- int index_next(byte *buf);
- int index_end();
- int read_range_first(const key_range *start_key,
- const key_range *end_key,
- bool eq_range, bool sorted);
- int read_range_next();
- /*
- unlike index_init(), rnd_init() can be called two times
- without rnd_end() in between (it only makes sense if scan=1).
- then the second call should prepare for the new table scan
- (e.g if rnd_init allocates the cursor, second call should
- position it to the start of the table, no need to deallocate
- and allocate it again
- */
- int rnd_init(bool scan); //required
- int rnd_end();
- int rnd_next(byte *buf); //required
- int rnd_pos(byte *buf, byte *pos); //required
- void position(const byte *record); //required
- int info(uint); //required
- int extra(ha_extra_function operation);
-
- void update_auto_increment(void);
- int repair(THD* thd, HA_CHECK_OPT* check_opt);
- int optimize(THD* thd, HA_CHECK_OPT* check_opt);
-
- int delete_all_rows(void);
- int create(const char *name, TABLE *form,
- HA_CREATE_INFO *create_info); //required
- ha_rows records_in_range(uint inx, key_range *start_key,
- key_range *end_key);
- uint8 table_cache_type() { return HA_CACHE_TBL_NOCACHE; }
-
- THR_LOCK_DATA **store_lock(THD *thd, THR_LOCK_DATA **to,
- enum thr_lock_type lock_type); //required
- bool get_error_message(int error, String *buf);
-};
-
-bool federated_db_init(void);
-bool federated_db_end(void);
diff --git a/sql/ha_heap.cc b/sql/ha_heap.cc
deleted file mode 100644
index 4b96e5b5744..00000000000
--- a/sql/ha_heap.cc
+++ /dev/null
@@ -1,695 +0,0 @@
-/* Copyright (C) 2000-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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
-
-
-#ifdef USE_PRAGMA_IMPLEMENTATION
-#pragma implementation // gcc: Class implementation
-#endif
-
-#include "mysql_priv.h"
-#include <myisampack.h>
-#include "ha_heap.h"
-
-handlerton heap_hton= {
- "MEMORY",
- SHOW_OPTION_YES,
- "Hash based, stored in memory, useful for temporary tables",
- DB_TYPE_HEAP,
- NULL,
- 0, /* slot */
- 0, /* savepoint size. */
- NULL, /* close_connection */
- NULL, /* savepoint */
- NULL, /* rollback to savepoint */
- NULL, /* release savepoint */
- NULL, /* commit */
- NULL, /* rollback */
- NULL, /* prepare */
- NULL, /* recover */
- NULL, /* commit_by_xid */
- NULL, /* rollback_by_xid */
- NULL, /* create_cursor_read_view */
- NULL, /* set_cursor_read_view */
- NULL, /* close_cursor_read_view */
- HTON_CAN_RECREATE
-};
-
-/*****************************************************************************
-** HEAP tables
-*****************************************************************************/
-
-ha_heap::ha_heap(TABLE *table_arg)
- :handler(&heap_hton, table_arg), file(0), records_changed(0),
- key_stat_version(0)
-{}
-
-
-static const char *ha_heap_exts[] = {
- NullS
-};
-
-const char **ha_heap::bas_ext() const
-{
- return ha_heap_exts;
-}
-
-/*
- Hash index statistics is updated (copied from HP_KEYDEF::hash_buckets to
- rec_per_key) after 1/HEAP_STATS_UPDATE_THRESHOLD fraction of table records
- have been inserted/updated/deleted. delete_all_rows() and table flush cause
- immediate update.
-
- NOTE
- hash index statistics must be updated when number of table records changes
- from 0 to non-zero value and vice versa. Otherwise records_in_range may
- erroneously return 0 and 'range' may miss records.
-*/
-#define HEAP_STATS_UPDATE_THRESHOLD 10
-
-int ha_heap::open(const char *name, int mode, uint test_if_locked)
-{
- if (!(file= heap_open(name, mode)) && my_errno == ENOENT)
- {
- HA_CREATE_INFO create_info;
- bzero(&create_info, sizeof(create_info));
- if (!create(name, table, &create_info))
- {
- file= heap_open(name, mode);
- implicit_emptied= 1;
- }
- }
- ref_length= sizeof(HEAP_PTR);
- if (file)
- {
- /* Initialize variables for the opened table */
- set_keys_for_scanning();
- /*
- We cannot run update_key_stats() here because we do not have a
- lock on the table. The 'records' count might just be changed
- temporarily at this moment and we might get wrong statistics (Bug
- #10178). Instead we request for update. This will be done in
- ha_heap::info(), which is always called before key statistics are
- used.
- */
- key_stat_version= file->s->key_stat_version-1;
- }
- return (file ? 0 : 1);
-}
-
-int ha_heap::close(void)
-{
- return heap_close(file);
-}
-
-
-/*
- Create a copy of this table
-
- DESCRIPTION
- Do same as default implementation but use file->s->name instead of
- table->s->path. This is needed by Windows where the clone() call sees
- '/'-delimited path in table->s->path, while ha_peap::open() was called
- with '\'-delimited path.
-*/
-
-handler *ha_heap::clone(MEM_ROOT *mem_root)
-{
- handler *new_handler= get_new_handler(table, mem_root, table->s->db_type);
- if (new_handler && !new_handler->ha_open(file->s->name, table->db_stat,
- HA_OPEN_IGNORE_IF_LOCKED))
- return new_handler;
- return NULL; /* purecov: inspected */
-}
-
-
-/*
- Compute which keys to use for scanning
-
- SYNOPSIS
- set_keys_for_scanning()
- no parameter
-
- DESCRIPTION
- Set the bitmap btree_keys, which is used when the upper layers ask
- which keys to use for scanning. For each btree index the
- corresponding bit is set.
-
- RETURN
- void
-*/
-
-void ha_heap::set_keys_for_scanning(void)
-{
- btree_keys.clear_all();
- for (uint i= 0 ; i < table->s->keys ; i++)
- {
- if (table->key_info[i].algorithm == HA_KEY_ALG_BTREE)
- btree_keys.set_bit(i);
- }
-}
-
-
-void ha_heap::update_key_stats()
-{
- for (uint i= 0; i < table->s->keys; i++)
- {
- KEY *key=table->key_info+i;
- if (!key->rec_per_key)
- continue;
- if (key->algorithm != HA_KEY_ALG_BTREE)
- {
- if (key->flags & HA_NOSAME)
- key->rec_per_key[key->key_parts-1]= 1;
- else
- {
- ha_rows hash_buckets= file->s->keydef[i].hash_buckets;
- uint no_records= hash_buckets ? (uint) (file->s->records/hash_buckets) : 2;
- if (no_records < 2)
- no_records= 2;
- key->rec_per_key[key->key_parts-1]= no_records;
- }
- }
- }
- records_changed= 0;
- /* At the end of update_key_stats() we can proudly claim they are OK. */
- key_stat_version= file->s->key_stat_version;
-}
-
-
-int ha_heap::write_row(byte * buf)
-{
- int res;
- statistic_increment(table->in_use->status_var.ha_write_count,&LOCK_status);
- if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_INSERT)
- table->timestamp_field->set_time();
- if (table->next_number_field && buf == table->record[0])
- {
- if ((res= update_auto_increment()))
- return res;
- }
- res= heap_write(file,buf);
- if (!res && (++records_changed*HEAP_STATS_UPDATE_THRESHOLD >
- file->s->records))
- {
- /*
- We can perform this safely since only one writer at the time is
- allowed on the table.
- */
- file->s->key_stat_version++;
- }
- return res;
-}
-
-int ha_heap::update_row(const byte * old_data, byte * new_data)
-{
- int res;
- statistic_increment(table->in_use->status_var.ha_update_count,&LOCK_status);
- if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_UPDATE)
- table->timestamp_field->set_time();
- res= heap_update(file,old_data,new_data);
- if (!res && ++records_changed*HEAP_STATS_UPDATE_THRESHOLD >
- file->s->records)
- {
- /*
- We can perform this safely since only one writer at the time is
- allowed on the table.
- */
- file->s->key_stat_version++;
- }
- return res;
-}
-
-int ha_heap::delete_row(const byte * buf)
-{
- int res;
- statistic_increment(table->in_use->status_var.ha_delete_count,&LOCK_status);
- res= heap_delete(file,buf);
- if (!res && table->s->tmp_table == NO_TMP_TABLE &&
- ++records_changed*HEAP_STATS_UPDATE_THRESHOLD > file->s->records)
- {
- /*
- We can perform this safely since only one writer at the time is
- allowed on the table.
- */
- file->s->key_stat_version++;
- }
- return res;
-}
-
-int ha_heap::index_read(byte * buf, const byte * key, uint key_len,
- enum ha_rkey_function find_flag)
-{
- DBUG_ASSERT(inited==INDEX);
- statistic_increment(table->in_use->status_var.ha_read_key_count,
- &LOCK_status);
- int error = heap_rkey(file,buf,active_index, key, key_len, find_flag);
- table->status = error ? STATUS_NOT_FOUND : 0;
- return error;
-}
-
-int ha_heap::index_read_last(byte *buf, const byte *key, uint key_len)
-{
- DBUG_ASSERT(inited==INDEX);
- statistic_increment(table->in_use->status_var.ha_read_key_count,
- &LOCK_status);
- int error= heap_rkey(file, buf, active_index, key, key_len,
- HA_READ_PREFIX_LAST);
- table->status= error ? STATUS_NOT_FOUND : 0;
- return error;
-}
-
-int ha_heap::index_read_idx(byte * buf, uint index, const byte * key,
- uint key_len, enum ha_rkey_function find_flag)
-{
- statistic_increment(table->in_use->status_var.ha_read_key_count,
- &LOCK_status);
- int error = heap_rkey(file, buf, index, key, key_len, find_flag);
- table->status = error ? STATUS_NOT_FOUND : 0;
- return error;
-}
-
-int ha_heap::index_next(byte * buf)
-{
- DBUG_ASSERT(inited==INDEX);
- statistic_increment(table->in_use->status_var.ha_read_next_count,
- &LOCK_status);
- int error=heap_rnext(file,buf);
- table->status=error ? STATUS_NOT_FOUND: 0;
- return error;
-}
-
-int ha_heap::index_prev(byte * buf)
-{
- DBUG_ASSERT(inited==INDEX);
- statistic_increment(table->in_use->status_var.ha_read_prev_count,
- &LOCK_status);
- int error=heap_rprev(file,buf);
- table->status=error ? STATUS_NOT_FOUND: 0;
- return error;
-}
-
-int ha_heap::index_first(byte * buf)
-{
- DBUG_ASSERT(inited==INDEX);
- statistic_increment(table->in_use->status_var.ha_read_first_count,
- &LOCK_status);
- int error=heap_rfirst(file, buf, active_index);
- table->status=error ? STATUS_NOT_FOUND: 0;
- return error;
-}
-
-int ha_heap::index_last(byte * buf)
-{
- DBUG_ASSERT(inited==INDEX);
- statistic_increment(table->in_use->status_var.ha_read_last_count,
- &LOCK_status);
- int error=heap_rlast(file, buf, active_index);
- table->status=error ? STATUS_NOT_FOUND: 0;
- return error;
-}
-
-int ha_heap::rnd_init(bool scan)
-{
- return scan ? heap_scan_init(file) : 0;
-}
-
-int ha_heap::rnd_next(byte *buf)
-{
- statistic_increment(table->in_use->status_var.ha_read_rnd_next_count,
- &LOCK_status);
- int error=heap_scan(file, buf);
- table->status=error ? STATUS_NOT_FOUND: 0;
- return error;
-}
-
-int ha_heap::rnd_pos(byte * buf, byte *pos)
-{
- int error;
- HEAP_PTR heap_position;
- statistic_increment(table->in_use->status_var.ha_read_rnd_count,
- &LOCK_status);
- memcpy_fixed((char*) &heap_position, pos, sizeof(HEAP_PTR));
- error=heap_rrnd(file, buf, heap_position);
- table->status=error ? STATUS_NOT_FOUND: 0;
- return error;
-}
-
-void ha_heap::position(const byte *record)
-{
- *(HEAP_PTR*) ref= heap_position(file); // Ref is aligned
-}
-
-int ha_heap::info(uint flag)
-{
- HEAPINFO hp_info;
- (void) heap_info(file,&hp_info,flag);
-
- records= hp_info.records;
- deleted= hp_info.deleted;
- errkey= hp_info.errkey;
- mean_rec_length= hp_info.reclength;
- data_file_length= hp_info.data_length;
- index_file_length= hp_info.index_length;
- max_data_file_length= hp_info.max_records* hp_info.reclength;
- delete_length= hp_info.deleted * hp_info.reclength;
- if (flag & HA_STATUS_AUTO)
- auto_increment_value= hp_info.auto_increment;
- /*
- If info() is called for the first time after open(), we will still
- have to update the key statistics. Hoping that a table lock is now
- in place.
- */
- if (key_stat_version != file->s->key_stat_version)
- update_key_stats();
- return 0;
-}
-
-int ha_heap::extra(enum ha_extra_function operation)
-{
- return heap_extra(file,operation);
-}
-
-int ha_heap::delete_all_rows()
-{
- heap_clear(file);
- if (table->s->tmp_table == NO_TMP_TABLE)
- {
- /*
- We can perform this safely since only one writer at the time is
- allowed on the table.
- */
- file->s->key_stat_version++;
- }
- return 0;
-}
-
-int ha_heap::external_lock(THD *thd, int lock_type)
-{
- return 0; // No external locking
-}
-
-
-/*
- Disable indexes.
-
- SYNOPSIS
- disable_indexes()
- mode mode of operation:
- HA_KEY_SWITCH_NONUNIQ disable all non-unique keys
- HA_KEY_SWITCH_ALL disable all keys
- HA_KEY_SWITCH_NONUNIQ_SAVE dis. non-uni. and make persistent
- HA_KEY_SWITCH_ALL_SAVE dis. all keys and make persistent
-
- DESCRIPTION
- Disable indexes and clear keys to use for scanning.
-
- IMPLEMENTATION
- HA_KEY_SWITCH_NONUNIQ is not implemented.
- HA_KEY_SWITCH_NONUNIQ_SAVE is not implemented with HEAP.
- HA_KEY_SWITCH_ALL_SAVE is not implemented with HEAP.
-
- RETURN
- 0 ok
- HA_ERR_WRONG_COMMAND mode not implemented.
-*/
-
-int ha_heap::disable_indexes(uint mode)
-{
- int error;
-
- if (mode == HA_KEY_SWITCH_ALL)
- {
- if (!(error= heap_disable_indexes(file)))
- set_keys_for_scanning();
- }
- else
- {
- /* mode not implemented */
- error= HA_ERR_WRONG_COMMAND;
- }
- return error;
-}
-
-
-/*
- Enable indexes.
-
- SYNOPSIS
- enable_indexes()
- mode mode of operation:
- HA_KEY_SWITCH_NONUNIQ enable all non-unique keys
- HA_KEY_SWITCH_ALL enable all keys
- HA_KEY_SWITCH_NONUNIQ_SAVE en. non-uni. and make persistent
- HA_KEY_SWITCH_ALL_SAVE en. all keys and make persistent
-
- DESCRIPTION
- Enable indexes and set keys to use for scanning.
- The indexes might have been disabled by disable_index() before.
- The function works only if both data and indexes are empty,
- since the heap storage engine cannot repair the indexes.
- To be sure, call handler::delete_all_rows() before.
-
- IMPLEMENTATION
- HA_KEY_SWITCH_NONUNIQ is not implemented.
- HA_KEY_SWITCH_NONUNIQ_SAVE is not implemented with HEAP.
- HA_KEY_SWITCH_ALL_SAVE is not implemented with HEAP.
-
- RETURN
- 0 ok
- HA_ERR_CRASHED data or index is non-empty. Delete all rows and retry.
- HA_ERR_WRONG_COMMAND mode not implemented.
-*/
-
-int ha_heap::enable_indexes(uint mode)
-{
- int error;
-
- if (mode == HA_KEY_SWITCH_ALL)
- {
- if (!(error= heap_enable_indexes(file)))
- set_keys_for_scanning();
- }
- else
- {
- /* mode not implemented */
- error= HA_ERR_WRONG_COMMAND;
- }
- return error;
-}
-
-
-/*
- Test if indexes are disabled.
-
- SYNOPSIS
- indexes_are_disabled()
- no parameters
-
- RETURN
- 0 indexes are not disabled
- 1 all indexes are disabled
- [2 non-unique indexes are disabled - NOT YET IMPLEMENTED]
-*/
-
-int ha_heap::indexes_are_disabled(void)
-{
- return heap_indexes_are_disabled(file);
-}
-
-THR_LOCK_DATA **ha_heap::store_lock(THD *thd,
- THR_LOCK_DATA **to,
- enum thr_lock_type lock_type)
-{
- if (lock_type != TL_IGNORE && file->lock.type == TL_UNLOCK)
- file->lock.type=lock_type;
- *to++= &file->lock;
- return to;
-}
-
-/*
- We have to ignore ENOENT entries as the HEAP table is created on open and
- not when doing a CREATE on the table.
-*/
-
-int ha_heap::delete_table(const char *name)
-{
- char buff[FN_REFLEN];
- int error= heap_delete_table(fn_format(buff,name,"","",
- MY_REPLACE_EXT|MY_UNPACK_FILENAME));
- return error == ENOENT ? 0 : error;
-}
-
-int ha_heap::rename_table(const char * from, const char * to)
-{
- return heap_rename(from,to);
-}
-
-
-ha_rows ha_heap::records_in_range(uint inx, key_range *min_key,
- key_range *max_key)
-{
- KEY *key=table->key_info+inx;
- if (key->algorithm == HA_KEY_ALG_BTREE)
- return hp_rb_records_in_range(file, inx, min_key, max_key);
-
- if (!min_key || !max_key ||
- min_key->length != max_key->length ||
- min_key->length != key->key_length ||
- min_key->flag != HA_READ_KEY_EXACT ||
- max_key->flag != HA_READ_AFTER_KEY)
- return HA_POS_ERROR; // Can only use exact keys
-
- if (records <= 1)
- return records;
-
- /* Assert that info() did run. We need current statistics here. */
- DBUG_ASSERT(key_stat_version == file->s->key_stat_version);
- return key->rec_per_key[key->key_parts-1];
-}
-
-
-int ha_heap::create(const char *name, TABLE *table_arg,
- HA_CREATE_INFO *create_info)
-{
- uint key, parts, mem_per_row= 0, keys= table_arg->s->keys;
- uint auto_key= 0, auto_key_type= 0;
- ha_rows max_rows;
- HP_KEYDEF *keydef;
- HA_KEYSEG *seg;
- char buff[FN_REFLEN];
- int error;
- TABLE_SHARE *share= table_arg->s;
- bool found_real_auto_increment= 0;
-
- for (key= parts= 0; key < keys; key++)
- parts+= table_arg->key_info[key].key_parts;
-
- if (!(keydef= (HP_KEYDEF*) my_malloc(keys * sizeof(HP_KEYDEF) +
- parts * sizeof(HA_KEYSEG),
- MYF(MY_WME))))
- return my_errno;
- seg= my_reinterpret_cast(HA_KEYSEG*) (keydef + keys);
- for (key= 0; key < keys; key++)
- {
- KEY *pos= table_arg->key_info+key;
- KEY_PART_INFO *key_part= pos->key_part;
- KEY_PART_INFO *key_part_end= key_part + pos->key_parts;
-
- keydef[key].keysegs= (uint) pos->key_parts;
- keydef[key].flag= (pos->flags & (HA_NOSAME | HA_NULL_ARE_EQUAL));
- keydef[key].seg= seg;
-
- switch (pos->algorithm) {
- case HA_KEY_ALG_UNDEF:
- case HA_KEY_ALG_HASH:
- keydef[key].algorithm= HA_KEY_ALG_HASH;
- mem_per_row+= sizeof(char*) * 2; // = sizeof(HASH_INFO)
- break;
- case HA_KEY_ALG_BTREE:
- keydef[key].algorithm= HA_KEY_ALG_BTREE;
- mem_per_row+=sizeof(TREE_ELEMENT)+pos->key_length+sizeof(char*);
- break;
- default:
- DBUG_ASSERT(0); // cannot happen
- }
-
- for (; key_part != key_part_end; key_part++, seg++)
- {
- Field *field= key_part->field;
-
- if (pos->algorithm == HA_KEY_ALG_BTREE)
- seg->type= field->key_type();
- else
- {
- if ((seg->type = field->key_type()) != (int) HA_KEYTYPE_TEXT &&
- seg->type != HA_KEYTYPE_VARTEXT1 &&
- seg->type != HA_KEYTYPE_VARTEXT2 &&
- seg->type != HA_KEYTYPE_VARBINARY1 &&
- seg->type != HA_KEYTYPE_VARBINARY2)
- seg->type= HA_KEYTYPE_BINARY;
- }
- seg->start= (uint) key_part->offset;
- seg->length= (uint) key_part->length;
- seg->flag= key_part->key_part_flag;
-
- if (field->flags & (ENUM_FLAG | SET_FLAG))
- seg->charset= &my_charset_bin;
- else
- seg->charset= field->charset();
- if (field->null_ptr)
- {
- seg->null_bit= field->null_bit;
- seg->null_pos= (uint) (field->null_ptr - (uchar*) table_arg->record[0]);
- }
- else
- {
- seg->null_bit= 0;
- seg->null_pos= 0;
- }
- if (field->flags & AUTO_INCREMENT_FLAG &&
- table_arg->found_next_number_field &&
- key == share->next_number_index)
- {
- /*
- Store key number and type for found auto_increment key
- We have to store type as seg->type can differ from it
- */
- auto_key= key+ 1;
- auto_key_type= field->key_type();
- }
- }
- }
- mem_per_row+= MY_ALIGN(share->reclength + 1, sizeof(char*));
- max_rows = (ha_rows) (table->in_use->variables.max_heap_table_size /
- (ulonglong) mem_per_row);
- if (table_arg->found_next_number_field)
- {
- keydef[share->next_number_index].flag|= HA_AUTO_KEY;
- found_real_auto_increment= share->next_number_key_offset == 0;
- }
- HP_CREATE_INFO hp_create_info;
- hp_create_info.auto_key= auto_key;
- hp_create_info.auto_key_type= auto_key_type;
- hp_create_info.auto_increment= (create_info->auto_increment_value ?
- create_info->auto_increment_value - 1 : 0);
- hp_create_info.max_table_size=current_thd->variables.max_heap_table_size;
- hp_create_info.with_auto_increment= found_real_auto_increment;
- max_rows = (ha_rows) (hp_create_info.max_table_size / mem_per_row);
- error= heap_create(fn_format(buff,name,"","",
- MY_REPLACE_EXT|MY_UNPACK_FILENAME),
- keys, keydef, share->reclength,
- (ulong) ((share->max_rows < max_rows &&
- share->max_rows) ?
- share->max_rows : max_rows),
- (ulong) share->min_rows, &hp_create_info);
- my_free((gptr) keydef, MYF(0));
- if (file)
- info(HA_STATUS_NO_LOCK | HA_STATUS_CONST | HA_STATUS_VARIABLE);
- return (error);
-}
-
-
-void ha_heap::update_create_info(HA_CREATE_INFO *create_info)
-{
- table->file->info(HA_STATUS_AUTO);
- if (!(create_info->used_fields & HA_CREATE_USED_AUTO))
- create_info->auto_increment_value= auto_increment_value;
-}
-
-ulonglong ha_heap::get_auto_increment()
-{
- ha_heap::info(HA_STATUS_AUTO);
- return auto_increment_value;
-}
diff --git a/sql/ha_heap.h b/sql/ha_heap.h
deleted file mode 100644
index 23583d0a6a7..00000000000
--- a/sql/ha_heap.h
+++ /dev/null
@@ -1,109 +0,0 @@
-/* Copyright (C) 2000-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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
-
-
-#ifdef USE_PRAGMA_INTERFACE
-#pragma interface /* gcc class implementation */
-#endif
-
-/* class for the the heap handler */
-
-#include <heap.h>
-
-class ha_heap: public handler
-{
- HP_INFO *file;
- key_map btree_keys;
- /* number of records changed since last statistics update */
- uint records_changed;
- uint key_stat_version;
-public:
- ha_heap(TABLE *table);
- ~ha_heap() {}
- handler *clone(MEM_ROOT *mem_root);
- const char *table_type() const
- {
- return (table->in_use->variables.sql_mode & MODE_MYSQL323) ?
- "HEAP" : "MEMORY";
- }
- const char *index_type(uint inx)
- {
- return ((table->key_info[inx].algorithm == HA_KEY_ALG_BTREE) ? "BTREE" :
- "HASH");
- }
- /* Rows also use a fixed-size format */
- enum row_type get_row_type() const { return ROW_TYPE_FIXED; }
- const char **bas_ext() const;
- ulong table_flags() const
- {
- return (HA_FAST_KEY_READ | HA_NO_BLOBS | HA_NULL_IN_KEY |
- HA_REC_NOT_IN_SEQ | HA_READ_RND_SAME |
- HA_CAN_INSERT_DELAYED);
- }
- ulong index_flags(uint inx, uint part, bool all_parts) const
- {
- return ((table->key_info[inx].algorithm == HA_KEY_ALG_BTREE) ?
- HA_READ_NEXT | HA_READ_PREV | HA_READ_ORDER | HA_READ_RANGE :
- HA_ONLY_WHOLE_INDEX | HA_KEY_SCAN_NOT_ROR);
- }
- const key_map *keys_to_use_for_scanning() { return &btree_keys; }
- uint max_supported_keys() const { return MAX_KEY; }
- uint max_supported_key_part_length() const { return MAX_KEY_LENGTH; }
- double scan_time() { return (double) (records+deleted) / 20.0+10; }
- double read_time(uint index, uint ranges, ha_rows rows)
- { return (double) rows / 20.0+1; }
-
- int open(const char *name, int mode, uint test_if_locked);
- int close(void);
- void set_keys_for_scanning(void);
- int write_row(byte * buf);
- int update_row(const byte * old_data, byte * new_data);
- int delete_row(const byte * buf);
- ulonglong get_auto_increment();
- int index_read(byte * buf, const byte * key,
- uint key_len, enum ha_rkey_function find_flag);
- int index_read_idx(byte * buf, uint idx, const byte * key,
- uint key_len, enum ha_rkey_function find_flag);
- int index_read_last(byte * buf, const byte * key, uint key_len);
- int index_next(byte * buf);
- int index_prev(byte * buf);
- int index_first(byte * buf);
- int index_last(byte * buf);
- int rnd_init(bool scan);
- int rnd_next(byte *buf);
- int rnd_pos(byte * buf, byte *pos);
- void position(const byte *record);
- int info(uint);
- int extra(enum ha_extra_function operation);
- int external_lock(THD *thd, int lock_type);
- int delete_all_rows(void);
- int disable_indexes(uint mode);
- int enable_indexes(uint mode);
- int indexes_are_disabled(void);
- ha_rows records_in_range(uint inx, key_range *min_key, key_range *max_key);
- int delete_table(const char *from);
- int rename_table(const char * from, const char * to);
- int create(const char *name, TABLE *form, HA_CREATE_INFO *create_info);
- void update_create_info(HA_CREATE_INFO *create_info);
-
- THR_LOCK_DATA **store_lock(THD *thd, THR_LOCK_DATA **to,
- enum thr_lock_type lock_type);
- int cmp_ref(const byte *ref1, const byte *ref2)
- {
- return memcmp(ref1, ref2, sizeof(HEAP_PTR));
- }
-private:
- void update_key_stats();
-};
diff --git a/sql/ha_innodb.cc b/sql/ha_innodb.cc
deleted file mode 100644
index 088ef55134f..00000000000
--- a/sql/ha_innodb.cc
+++ /dev/null
@@ -1,7425 +0,0 @@
-/* Copyright (C) 2000-2005 MySQL AB & Innobase Oy
-
- This 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 defines the InnoDB handler: the interface between MySQL and InnoDB
-NOTE: You can only use noninlined InnoDB functions in this file, because we
-have disabled the InnoDB inlining in this file. */
-
-/* TODO list for the InnoDB handler in 5.0:
- - Remove the flag trx->active_trans and look at the InnoDB
- trx struct state field
- - fix savepoint functions to use savepoint storage area
- - Find out what kind of problems the OS X case-insensitivity causes to
- table and database names; should we 'normalize' the names like we do
- in Windows?
-*/
-
-#ifdef USE_PRAGMA_IMPLEMENTATION
-#pragma implementation // gcc: Class implementation
-#endif
-
-#include "mysql_priv.h"
-#include "slave.h"
-
-#ifdef HAVE_INNOBASE_DB
-#include <m_ctype.h>
-#include <hash.h>
-#include <myisampack.h>
-#include <mysys_err.h>
-#include <my_sys.h>
-
-#define MAX_ULONG_BIT ((ulong) 1 << (sizeof(ulong)*8-1))
-
-#include "ha_innodb.h"
-
-pthread_mutex_t innobase_share_mutex, /* to protect innobase_open_files */
- prepare_commit_mutex; /* to force correct commit order in
- binlog */
-ulong commit_threads= 0;
-pthread_mutex_t commit_threads_m;
-pthread_cond_t commit_cond;
-pthread_mutex_t commit_cond_m;
-bool innodb_inited= 0;
-
-/*-----------------------------------------------------------------*/
-/* These variables are used to implement (semi-)synchronous MySQL binlog
-replication for InnoDB tables. */
-
-pthread_cond_t innobase_repl_cond; /* Posix cond variable;
- this variable is signaled
- when enough binlog has been
- sent to slave, so that a
- waiting trx can return the
- 'ok' message to the client
- for a commit */
-pthread_mutex_t innobase_repl_cond_mutex; /* Posix cond variable mutex
- that also protects the next
- innobase_repl_... variables */
-uint innobase_repl_state; /* 1 if synchronous replication
- is switched on and is working
- ok; else 0 */
-uint innobase_repl_file_name_inited = 0; /* This is set to 1 when
- innobase_repl_file_name
- contains meaningful data */
-char* innobase_repl_file_name; /* The binlog name up to which
- we have sent some binlog to
- the slave */
-my_off_t innobase_repl_pos; /* The position in that file
- up to which we have sent the
- binlog to the slave */
-uint innobase_repl_n_wait_threads = 0; /* This tells how many
- transactions currently are
- waiting for the binlog to be
- sent to the client */
-uint innobase_repl_wait_file_name_inited = 0; /* This is set to 1
- when we know the 'smallest'
- wait position */
-char* innobase_repl_wait_file_name; /* NULL, or the 'smallest'
- innobase_repl_file_name that
- a transaction is waiting for */
-my_off_t innobase_repl_wait_pos; /* The smallest position in
- that file that a trx is
- waiting for: the trx can
- proceed and send an 'ok' to
- the client when MySQL has sent
- the binlog up to this position
- to the slave */
-/*-----------------------------------------------------------------*/
-
-
-
-/* Store MySQL definition of 'byte': in Linux it is char while InnoDB
-uses unsigned char; the header univ.i which we include next defines
-'byte' as a macro which expands to 'unsigned char' */
-
-typedef byte mysql_byte;
-
-#define INSIDE_HA_INNOBASE_CC
-
-/* Include necessary InnoDB headers */
-extern "C" {
-#include "../innobase/include/univ.i"
-#include "../innobase/include/os0file.h"
-#include "../innobase/include/os0thread.h"
-#include "../innobase/include/srv0start.h"
-#include "../innobase/include/srv0srv.h"
-#include "../innobase/include/trx0roll.h"
-#include "../innobase/include/trx0trx.h"
-#include "../innobase/include/trx0sys.h"
-#include "../innobase/include/mtr0mtr.h"
-#include "../innobase/include/row0ins.h"
-#include "../innobase/include/row0mysql.h"
-#include "../innobase/include/row0sel.h"
-#include "../innobase/include/row0upd.h"
-#include "../innobase/include/log0log.h"
-#include "../innobase/include/lock0lock.h"
-#include "../innobase/include/dict0crea.h"
-#include "../innobase/include/btr0cur.h"
-#include "../innobase/include/btr0btr.h"
-#include "../innobase/include/fsp0fsp.h"
-#include "../innobase/include/sync0sync.h"
-#include "../innobase/include/fil0fil.h"
-#include "../innobase/include/trx0xa.h"
-}
-
-#define HA_INNOBASE_ROWS_IN_TABLE 10000 /* to get optimization right */
-#define HA_INNOBASE_RANGE_COUNT 100
-
-ulong innobase_large_page_size = 0;
-
-/* The default values for the following, type long or longlong, start-up
-parameters are declared in mysqld.cc: */
-
-long innobase_mirrored_log_groups, innobase_log_files_in_group,
- innobase_log_buffer_size, innobase_buffer_pool_awe_mem_mb,
- innobase_additional_mem_pool_size, innobase_file_io_threads,
- innobase_lock_wait_timeout, innobase_force_recovery,
- innobase_open_files;
-
-longlong innobase_buffer_pool_size, innobase_log_file_size;
-
-/* The default values for the following char* start-up parameters
-are determined in innobase_init below: */
-
-char* innobase_data_home_dir = NULL;
-char* innobase_data_file_path = NULL;
-char* innobase_log_group_home_dir = NULL;
-char* innobase_log_arch_dir = NULL;/* unused */
-/* The following has a misleading name: starting from 4.0.5, this also
-affects Windows: */
-char* innobase_unix_file_flush_method = NULL;
-
-/* Below we have boolean-valued start-up parameters, and their default
-values */
-
-ulong innobase_fast_shutdown = 1;
-my_bool innobase_log_archive = FALSE;/* unused */
-my_bool innobase_use_doublewrite = TRUE;
-my_bool innobase_use_checksums = TRUE;
-my_bool innobase_use_large_pages = FALSE;
-my_bool innobase_use_native_aio = FALSE;
-my_bool innobase_file_per_table = FALSE;
-my_bool innobase_locks_unsafe_for_binlog = FALSE;
-my_bool innobase_rollback_on_timeout = FALSE;
-my_bool innobase_create_status_file = FALSE;
-my_bool innobase_adaptive_hash_index = TRUE;
-
-static char *internal_innobase_data_file_path = NULL;
-
-/* The following counter is used to convey information to InnoDB
-about server activity: in selects it is not sensible to call
-srv_active_wake_master_thread after each fetch or search, we only do
-it every INNOBASE_WAKE_INTERVAL'th step. */
-
-#define INNOBASE_WAKE_INTERVAL 32
-ulong innobase_active_counter = 0;
-
-static HASH innobase_open_tables;
-
-#ifdef __NETWARE__ /* some special cleanup for NetWare */
-bool nw_panic = FALSE;
-#endif
-
-static mysql_byte* innobase_get_key(INNOBASE_SHARE *share,uint *length,
- my_bool not_used __attribute__((unused)));
-static INNOBASE_SHARE *get_share(const char *table_name);
-static void free_share(INNOBASE_SHARE *share);
-static int innobase_close_connection(THD* thd);
-static int innobase_commit(THD* thd, bool all);
-static int innobase_rollback(THD* thd, bool all);
-static int innobase_rollback_to_savepoint(THD* thd, void *savepoint);
-static int innobase_savepoint(THD* thd, void *savepoint);
-static int innobase_release_savepoint(THD* thd, void *savepoint);
-
-handlerton innobase_hton = {
- "InnoDB",
- SHOW_OPTION_YES,
- "Supports transactions, row-level locking, and foreign keys",
- DB_TYPE_INNODB,
- innobase_init,
- 0, /* slot */
- sizeof(trx_named_savept_t), /* savepoint size. TODO: use it */
- innobase_close_connection,
- innobase_savepoint,
- innobase_rollback_to_savepoint,
- innobase_release_savepoint,
- innobase_commit, /* commit */
- innobase_rollback, /* rollback */
- innobase_xa_prepare, /* prepare */
- innobase_xa_recover, /* recover */
- innobase_commit_by_xid, /* commit_by_xid */
- innobase_rollback_by_xid, /* rollback_by_xid */
- innobase_create_cursor_view,
- innobase_set_cursor_view,
- innobase_close_cursor_view,
- HTON_NO_FLAGS
-};
-
-/*********************************************************************
-Commits a transaction in an InnoDB database. */
-
-void
-innobase_commit_low(
-/*================*/
- trx_t* trx); /* in: transaction handle */
-
-struct show_var_st innodb_status_variables[]= {
- {"buffer_pool_pages_data",
- (char*) &export_vars.innodb_buffer_pool_pages_data, SHOW_LONG},
- {"buffer_pool_pages_dirty",
- (char*) &export_vars.innodb_buffer_pool_pages_dirty, SHOW_LONG},
- {"buffer_pool_pages_flushed",
- (char*) &export_vars.innodb_buffer_pool_pages_flushed, SHOW_LONG},
- {"buffer_pool_pages_free",
- (char*) &export_vars.innodb_buffer_pool_pages_free, SHOW_LONG},
-#ifdef UNIV_DEBUG
- {"buffer_pool_pages_latched",
- (char*) &export_vars.innodb_buffer_pool_pages_latched, SHOW_LONG},
-#endif /* UNIV_DEBUG */
- {"buffer_pool_pages_misc",
- (char*) &export_vars.innodb_buffer_pool_pages_misc, SHOW_LONG},
- {"buffer_pool_pages_total",
- (char*) &export_vars.innodb_buffer_pool_pages_total, SHOW_LONG},
- {"buffer_pool_read_ahead_rnd",
- (char*) &export_vars.innodb_buffer_pool_read_ahead_rnd, SHOW_LONG},
- {"buffer_pool_read_ahead_seq",
- (char*) &export_vars.innodb_buffer_pool_read_ahead_seq, SHOW_LONG},
- {"buffer_pool_read_requests",
- (char*) &export_vars.innodb_buffer_pool_read_requests, SHOW_LONG},
- {"buffer_pool_reads",
- (char*) &export_vars.innodb_buffer_pool_reads, SHOW_LONG},
- {"buffer_pool_wait_free",
- (char*) &export_vars.innodb_buffer_pool_wait_free, SHOW_LONG},
- {"buffer_pool_write_requests",
- (char*) &export_vars.innodb_buffer_pool_write_requests, SHOW_LONG},
- {"data_fsyncs",
- (char*) &export_vars.innodb_data_fsyncs, SHOW_LONG},
- {"data_pending_fsyncs",
- (char*) &export_vars.innodb_data_pending_fsyncs, SHOW_LONG},
- {"data_pending_reads",
- (char*) &export_vars.innodb_data_pending_reads, SHOW_LONG},
- {"data_pending_writes",
- (char*) &export_vars.innodb_data_pending_writes, SHOW_LONG},
- {"data_read",
- (char*) &export_vars.innodb_data_read, SHOW_LONG},
- {"data_reads",
- (char*) &export_vars.innodb_data_reads, SHOW_LONG},
- {"data_writes",
- (char*) &export_vars.innodb_data_writes, SHOW_LONG},
- {"data_written",
- (char*) &export_vars.innodb_data_written, SHOW_LONG},
- {"dblwr_pages_written",
- (char*) &export_vars.innodb_dblwr_pages_written, SHOW_LONG},
- {"dblwr_writes",
- (char*) &export_vars.innodb_dblwr_writes, SHOW_LONG},
- {"log_waits",
- (char*) &export_vars.innodb_log_waits, SHOW_LONG},
- {"log_write_requests",
- (char*) &export_vars.innodb_log_write_requests, SHOW_LONG},
- {"log_writes",
- (char*) &export_vars.innodb_log_writes, SHOW_LONG},
- {"os_log_fsyncs",
- (char*) &export_vars.innodb_os_log_fsyncs, SHOW_LONG},
- {"os_log_pending_fsyncs",
- (char*) &export_vars.innodb_os_log_pending_fsyncs, SHOW_LONG},
- {"os_log_pending_writes",
- (char*) &export_vars.innodb_os_log_pending_writes, SHOW_LONG},
- {"os_log_written",
- (char*) &export_vars.innodb_os_log_written, SHOW_LONG},
- {"page_size",
- (char*) &export_vars.innodb_page_size, SHOW_LONG},
- {"pages_created",
- (char*) &export_vars.innodb_pages_created, SHOW_LONG},
- {"pages_read",
- (char*) &export_vars.innodb_pages_read, SHOW_LONG},
- {"pages_written",
- (char*) &export_vars.innodb_pages_written, SHOW_LONG},
- {"row_lock_current_waits",
- (char*) &export_vars.innodb_row_lock_current_waits, SHOW_LONG},
- {"row_lock_time",
- (char*) &export_vars.innodb_row_lock_time, SHOW_LONGLONG},
- {"row_lock_time_avg",
- (char*) &export_vars.innodb_row_lock_time_avg, SHOW_LONG},
- {"row_lock_time_max",
- (char*) &export_vars.innodb_row_lock_time_max, SHOW_LONG},
- {"row_lock_waits",
- (char*) &export_vars.innodb_row_lock_waits, SHOW_LONG},
- {"rows_deleted",
- (char*) &export_vars.innodb_rows_deleted, SHOW_LONG},
- {"rows_inserted",
- (char*) &export_vars.innodb_rows_inserted, SHOW_LONG},
- {"rows_read",
- (char*) &export_vars.innodb_rows_read, SHOW_LONG},
- {"rows_updated",
- (char*) &export_vars.innodb_rows_updated, SHOW_LONG},
- {NullS, NullS, SHOW_LONG}};
-
-/* General functions */
-
-/**********************************************************************
-Save some CPU by testing the value of srv_thread_concurrency in inline
-functions. */
-inline
-void
-innodb_srv_conc_enter_innodb(
-/*=========================*/
- trx_t* trx) /* in: transaction handle */
-{
- if (UNIV_LIKELY(!srv_thread_concurrency)) {
-
- return;
- }
-
- srv_conc_enter_innodb(trx);
-}
-
-/**********************************************************************
-Save some CPU by testing the value of srv_thread_concurrency in inline
-functions. */
-inline
-void
-innodb_srv_conc_exit_innodb(
-/*========================*/
- trx_t* trx) /* in: transaction handle */
-{
- if (UNIV_LIKELY(!srv_thread_concurrency)) {
-
- return;
- }
-
- srv_conc_exit_innodb(trx);
-}
-
-/**********************************************************************
-Releases possible search latch and InnoDB thread FIFO ticket. These should
-be released at each SQL statement end, and also when mysqld passes the
-control to the client. It does no harm to release these also in the middle
-of an SQL statement. */
-inline
-void
-innobase_release_stat_resources(
-/*============================*/
- trx_t* trx) /* in: transaction object */
-{
- if (trx->has_search_latch) {
- trx_search_latch_release_if_reserved(trx);
- }
-
- if (trx->declared_to_be_inside_innodb) {
- /* Release our possible ticket in the FIFO */
-
- srv_conc_force_exit_innodb(trx);
- }
-}
-
-/************************************************************************
-Call this function when mysqld passes control to the client. That is to
-avoid deadlocks on the adaptive hash S-latch possibly held by thd. For more
-documentation, see handler.cc. */
-
-void
-innobase_release_temporary_latches(
-/*===============================*/
- THD *thd)
-{
- trx_t* trx;
-
- if (!innodb_inited) {
-
- return;
- }
-
- trx = (trx_t*) thd->ha_data[innobase_hton.slot];
-
- if (trx) {
- innobase_release_stat_resources(trx);
- }
-}
-
-/************************************************************************
-Increments innobase_active_counter and every INNOBASE_WAKE_INTERVALth
-time calls srv_active_wake_master_thread. This function should be used
-when a single database operation may introduce a small need for
-server utility activity, like checkpointing. */
-inline
-void
-innobase_active_small(void)
-/*=======================*/
-{
- innobase_active_counter++;
-
- if ((innobase_active_counter % INNOBASE_WAKE_INTERVAL) == 0) {
- srv_active_wake_master_thread();
- }
-}
-
-/************************************************************************
-Converts an InnoDB error code to a MySQL error code and also tells to MySQL
-about a possible transaction rollback inside InnoDB caused by a lock wait
-timeout or a deadlock. */
-static
-int
-convert_error_code_to_mysql(
-/*========================*/
- /* out: MySQL error code */
- int error, /* in: InnoDB error code */
- THD* thd) /* in: user thread handle or NULL */
-{
- if (error == DB_SUCCESS) {
-
- return(0);
-
- } else if (error == (int) DB_DUPLICATE_KEY) {
-
- return(HA_ERR_FOUND_DUPP_KEY);
-
- } else if (error == (int) DB_RECORD_NOT_FOUND) {
-
- return(HA_ERR_NO_ACTIVE_RECORD);
-
- } else if (error == (int) DB_ERROR) {
-
- return(-1); /* unspecified error */
-
- } else if (error == (int) DB_DEADLOCK) {
- /* Since we rolled back the whole transaction, we must
- tell it also to MySQL so that MySQL knows to empty the
- cached binlog for this transaction */
-
- mark_transaction_to_rollback(thd, TRUE);
-
- return(HA_ERR_LOCK_DEADLOCK);
-
- } else if (error == (int) DB_LOCK_WAIT_TIMEOUT) {
-
- /* Starting from 5.0.13, we let MySQL just roll back the
- latest SQL statement in a lock wait timeout. Previously, we
- rolled back the whole transaction. */
-
- mark_transaction_to_rollback(thd,
- (bool)row_rollback_on_timeout);
-
- return(HA_ERR_LOCK_WAIT_TIMEOUT);
-
- } else if (error == (int) DB_NO_REFERENCED_ROW) {
-
- return(HA_ERR_NO_REFERENCED_ROW);
-
- } else if (error == (int) DB_ROW_IS_REFERENCED) {
-
- return(HA_ERR_ROW_IS_REFERENCED);
-
- } else if (error == (int) DB_CANNOT_ADD_CONSTRAINT) {
-
- return(HA_ERR_CANNOT_ADD_FOREIGN);
-
- } else if (error == (int) DB_CANNOT_DROP_CONSTRAINT) {
-
- return(HA_ERR_ROW_IS_REFERENCED); /* TODO: This is a bit
- misleading, a new MySQL error
- code should be introduced */
- } else if (error == (int) DB_COL_APPEARS_TWICE_IN_INDEX) {
-
- return(HA_ERR_CRASHED);
-
- } else if (error == (int) DB_OUT_OF_FILE_SPACE) {
-
- return(HA_ERR_RECORD_FILE_FULL);
-
- } else if (error == (int) DB_TABLE_IS_BEING_USED) {
-
- return(HA_ERR_WRONG_COMMAND);
-
- } else if (error == (int) DB_TABLE_NOT_FOUND) {
-
- return(HA_ERR_NO_SUCH_TABLE);
-
- } else if (error == (int) DB_TOO_BIG_RECORD) {
-
- return(HA_ERR_TO_BIG_ROW);
-
- } else if (error == (int) DB_CORRUPTION) {
-
- return(HA_ERR_CRASHED);
- } else if (error == (int) DB_NO_SAVEPOINT) {
-
- return(HA_ERR_NO_SAVEPOINT);
- } else if (error == (int) DB_LOCK_TABLE_FULL) {
- /* Since we rolled back the whole transaction, we must
- tell it also to MySQL so that MySQL knows to empty the
- cached binlog for this transaction */
-
- mark_transaction_to_rollback(thd, TRUE);
-
- return(HA_ERR_LOCK_TABLE_FULL);
- } else if (error == DB_UNSUPPORTED) {
-
- return(HA_ERR_UNSUPPORTED);
- } else {
- return(-1); // Unknown error
- }
-}
-
-/*****************************************************************
-If you want to print a thd that is not associated with the current thread,
-you must call this function before reserving the InnoDB kernel_mutex, to
-protect MySQL from setting thd->query NULL. If you print a thd of the current
-thread, we know that MySQL cannot modify thd->query, and it is not necessary
-to call this. Call innobase_mysql_end_print_arbitrary_thd() after you release
-the kernel_mutex.
-NOTE that /mysql/innobase/lock/lock0lock.c must contain the prototype for this
-function! */
-extern "C"
-void
-innobase_mysql_prepare_print_arbitrary_thd(void)
-/*============================================*/
-{
- VOID(pthread_mutex_lock(&LOCK_thread_count));
-}
-
-/*****************************************************************
-Releases the mutex reserved by innobase_mysql_prepare_print_arbitrary_thd().
-NOTE that /mysql/innobase/lock/lock0lock.c must contain the prototype for this
-function! */
-extern "C"
-void
-innobase_mysql_end_print_arbitrary_thd(void)
-/*========================================*/
-{
- VOID(pthread_mutex_unlock(&LOCK_thread_count));
-}
-
-/*****************************************************************
-Prints info of a THD object (== user session thread) to the given file.
-NOTE that /mysql/innobase/trx/trx0trx.c must contain the prototype for
-this function! */
-extern "C"
-void
-innobase_mysql_print_thd(
-/*=====================*/
- FILE* f, /* in: output stream */
- void* input_thd, /* in: pointer to a MySQL THD object */
- uint max_query_len) /* in: max query length to print, or 0 to
- use the default max length */
-{
- const THD* thd;
- const Security_context *sctx;
- const char* s;
-
- thd = (const THD*) input_thd;
- /* We probably want to have original user as part of debug output. */
- sctx = &thd->main_security_ctx;
-
-
- fprintf(f, "MySQL thread id %lu, query id %lu",
- thd->thread_id, (ulong) thd->query_id);
- if (sctx->host) {
- putc(' ', f);
- fputs(sctx->host, f);
- }
-
- if (sctx->ip) {
- putc(' ', f);
- fputs(sctx->ip, f);
- }
-
- if (sctx->user) {
- putc(' ', f);
- fputs(sctx->user, f);
- }
-
- if ((s = thd->proc_info)) {
- putc(' ', f);
- fputs(s, f);
- }
-
- if ((s = thd->query)) {
- /* 3100 is chosen because currently 3000 is the maximum
- max_query_len we ever give this. */
- char buf[3100];
- uint len;
-
- /* If buf is too small, we dynamically allocate storage
- in this. */
- char* dyn_str = NULL;
-
- /* Points to buf or dyn_str. */
- char* str = buf;
-
- if (max_query_len == 0)
- {
- /* ADDITIONAL SAFETY: the default is to print at
- most 300 chars to reduce the probability of a
- seg fault if there is a race in
- thd->query_length in MySQL; after May 14, 2004
- probably no race any more, but better be
- safe */
- max_query_len = 300;
- }
-
- len = min(thd->query_length, max_query_len);
-
- if (len > (sizeof(buf) - 1))
- {
- dyn_str = my_malloc(len + 1, MYF(0));
- str = dyn_str;
- }
-
- /* Use strmake to reduce the timeframe for a race,
- compared to fwrite() */
- len = (uint) (strmake(str, s, len) - str);
- putc('\n', f);
- fwrite(str, 1, len, f);
-
- if (dyn_str)
- {
- my_free(dyn_str, MYF(0));
- }
- }
-
- putc('\n', f);
-}
-
-/**********************************************************************
-Get the variable length bounds of the given character set.
-
-NOTE that the exact prototype of this function has to be in
-/innobase/data/data0type.ic! */
-extern "C"
-void
-innobase_get_cset_width(
-/*====================*/
- ulint cset, /* in: MySQL charset-collation code */
- ulint* mbminlen, /* out: minimum length of a char (in bytes) */
- ulint* mbmaxlen) /* out: maximum length of a char (in bytes) */
-{
- CHARSET_INFO* cs;
- ut_ad(cset < 256);
- ut_ad(mbminlen);
- ut_ad(mbmaxlen);
-
- cs = all_charsets[cset];
- if (cs) {
- *mbminlen = cs->mbminlen;
- *mbmaxlen = cs->mbmaxlen;
- } else {
- ut_a(cset == 0);
- *mbminlen = *mbmaxlen = 0;
- }
-}
-
-/**********************************************************************
-Compares NUL-terminated UTF-8 strings case insensitively.
-
-NOTE that the exact prototype of this function has to be in
-/innobase/dict/dict0dict.c! */
-extern "C"
-int
-innobase_strcasecmp(
-/*================*/
- /* out: 0 if a=b, <0 if a<b, >1 if a>b */
- const char* a, /* in: first string to compare */
- const char* b) /* in: second string to compare */
-{
- return(my_strcasecmp(system_charset_info, a, b));
-}
-
-/**********************************************************************
-Makes all characters in a NUL-terminated UTF-8 string lower case.
-
-NOTE that the exact prototype of this function has to be in
-/innobase/dict/dict0dict.c! */
-extern "C"
-void
-innobase_casedn_str(
-/*================*/
- char* a) /* in/out: string to put in lower case */
-{
- my_casedn_str(system_charset_info, a);
-}
-
-/*************************************************************************
-Creates a temporary file. */
-extern "C"
-int
-innobase_mysql_tmpfile(void)
-/*========================*/
- /* out: temporary file descriptor, or < 0 on error */
-{
- char filename[FN_REFLEN];
- int fd2 = -1;
- File fd = create_temp_file(filename, mysql_tmpdir, "ib",
-#ifdef __WIN__
- O_BINARY | O_TRUNC | O_SEQUENTIAL |
- O_TEMPORARY | O_SHORT_LIVED |
-#endif /* __WIN__ */
- O_CREAT | O_EXCL | O_RDWR,
- MYF(MY_WME));
- if (fd >= 0) {
-#ifndef __WIN__
- /* On Windows, open files cannot be removed, but files can be
- created with the O_TEMPORARY flag to the same effect
- ("delete on close"). */
- unlink(filename);
-#endif /* !__WIN__ */
- /* Copy the file descriptor, so that the additional resources
- allocated by create_temp_file() can be freed by invoking
- my_close().
-
- Because the file descriptor returned by this function
- will be passed to fdopen(), it will be closed by invoking
- fclose(), which in turn will invoke close() instead of
- my_close(). */
- fd2 = dup(fd);
- if (fd2 < 0) {
- DBUG_PRINT("error",("Got error %d on dup",fd2));
- my_errno=errno;
- my_error(EE_OUT_OF_FILERESOURCES,
- MYF(ME_BELL+ME_WAITTANG),
- filename, my_errno);
- }
- my_close(fd, MYF(MY_WME));
- }
- return(fd2);
-}
-
-/*************************************************************************
-Gets the InnoDB transaction handle for a MySQL handler object, creates
-an InnoDB transaction struct if the corresponding MySQL thread struct still
-lacks one. */
-static
-trx_t*
-check_trx_exists(
-/*=============*/
- /* out: InnoDB transaction handle */
- THD* thd) /* in: user thread handle */
-{
- trx_t* trx;
-
- ut_ad(thd == current_thd);
-
- trx = (trx_t*) thd->ha_data[innobase_hton.slot];
-
- if (trx == NULL) {
- DBUG_ASSERT(thd != NULL);
- trx = trx_allocate_for_mysql();
-
- trx->mysql_thd = thd;
- trx->mysql_query_str = &(thd->query);
- trx->active_trans = 0;
-
- /* Update the info whether we should skip XA steps that eat
- CPU time */
- trx->support_xa = (ibool)(thd->variables.innodb_support_xa);
-
- thd->ha_data[innobase_hton.slot] = trx;
- } else {
- if (trx->magic_n != TRX_MAGIC_N) {
- mem_analyze_corruption((byte*)trx);
-
- ut_a(0);
- }
- }
-
- if (thd->options & OPTION_NO_FOREIGN_KEY_CHECKS) {
- trx->check_foreigns = FALSE;
- } else {
- trx->check_foreigns = TRUE;
- }
-
- if (thd->options & OPTION_RELAXED_UNIQUE_CHECKS) {
- trx->check_unique_secondary = FALSE;
- } else {
- trx->check_unique_secondary = TRUE;
- }
-
- return(trx);
-}
-
-
-/*************************************************************************
-Construct ha_innobase handler. */
-
-ha_innobase::ha_innobase(TABLE *table_arg)
- :handler(&innobase_hton, table_arg),
- int_table_flags(HA_REC_NOT_IN_SEQ |
- HA_NULL_IN_KEY |
- HA_CAN_INDEX_BLOBS |
- HA_CAN_SQL_HANDLER |
- HA_NOT_EXACT_COUNT |
- HA_PRIMARY_KEY_IN_READ_INDEX |
- HA_CAN_GEOMETRY |
- HA_TABLE_SCAN_ON_INDEX),
- start_of_scan(0),
- num_write_row(0)
-{}
-
-/*************************************************************************
-Updates the user_thd field in a handle and also allocates a new InnoDB
-transaction handle if needed, and updates the transaction fields in the
-prebuilt struct. */
-inline
-int
-ha_innobase::update_thd(
-/*====================*/
- /* out: 0 or error code */
- THD* thd) /* in: thd to use the handle */
-{
- row_prebuilt_t* prebuilt = (row_prebuilt_t*) innobase_prebuilt;
- trx_t* trx;
-
- trx = check_trx_exists(thd);
-
- if (prebuilt->trx != trx) {
-
- row_update_prebuilt_trx(prebuilt, trx);
- }
-
- user_thd = thd;
-
- return(0);
-}
-
-/*************************************************************************
-Registers that InnoDB takes part in an SQL statement, so that MySQL knows to
-roll back the statement if the statement results in an error. This MUST be
-called for every SQL statement that may be rolled back by MySQL. Calling this
-several times to register the same statement is allowed, too. */
-inline
-void
-innobase_register_stmt(
-/*===================*/
- THD* thd) /* in: MySQL thd (connection) object */
-{
- /* Register the statement */
- trans_register_ha(thd, FALSE, &innobase_hton);
-}
-
-/*************************************************************************
-Registers an InnoDB transaction in MySQL, so that the MySQL XA code knows
-to call the InnoDB prepare and commit, or rollback for the transaction. This
-MUST be called for every transaction for which the user may call commit or
-rollback. Calling this several times to register the same transaction is
-allowed, too.
-This function also registers the current SQL statement. */
-inline
-void
-innobase_register_trx_and_stmt(
-/*===========================*/
- THD* thd) /* in: MySQL thd (connection) object */
-{
- /* NOTE that actually innobase_register_stmt() registers also
- the transaction in the AUTOCOMMIT=1 mode. */
-
- innobase_register_stmt(thd);
-
- if (thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)) {
-
- /* No autocommit mode, register for a transaction */
- trans_register_ha(thd, TRUE, &innobase_hton);
- }
-}
-
-/* BACKGROUND INFO: HOW THE MYSQL QUERY CACHE WORKS WITH INNODB
- ------------------------------------------------------------
-
-1) The use of the query cache for TBL is disabled when there is an
-uncommitted change to TBL.
-
-2) When a change to TBL commits, InnoDB stores the current value of
-its global trx id counter, let us denote it by INV_TRX_ID, to the table object
-in the InnoDB data dictionary, and does only allow such transactions whose
-id <= INV_TRX_ID to use the query cache.
-
-3) When InnoDB does an INSERT/DELETE/UPDATE to a table TBL, or an implicit
-modification because an ON DELETE CASCADE, we invalidate the MySQL query cache
-of TBL immediately.
-
-How this is implemented inside InnoDB:
-
-1) Since every modification always sets an IX type table lock on the InnoDB
-table, it is easy to check if there can be uncommitted modifications for a
-table: just check if there are locks in the lock list of the table.
-
-2) When a transaction inside InnoDB commits, it reads the global trx id
-counter and stores the value INV_TRX_ID to the tables on which it had a lock.
-
-3) If there is an implicit table change from ON DELETE CASCADE or SET NULL,
-InnoDB calls an invalidate method for the MySQL query cache for that table.
-
-How this is implemented inside sql_cache.cc:
-
-1) The query cache for an InnoDB table TBL is invalidated immediately at an
-INSERT/UPDATE/DELETE, just like in the case of MyISAM. No need to delay
-invalidation to the transaction commit.
-
-2) To store or retrieve a value from the query cache of an InnoDB table TBL,
-any query must first ask InnoDB's permission. We must pass the thd as a
-parameter because InnoDB will look at the trx id, if any, associated with
-that thd.
-
-3) Use of the query cache for InnoDB tables is now allowed also when
-AUTOCOMMIT==0 or we are inside BEGIN ... COMMIT. Thus transactions no longer
-put restrictions on the use of the query cache.
-*/
-
-/**********************************************************************
-The MySQL query cache uses this to check from InnoDB if the query cache at
-the moment is allowed to operate on an InnoDB table. The SQL query must
-be a non-locking SELECT.
-
-The query cache is allowed to operate on certain query only if this function
-returns TRUE for all tables in the query.
-
-If thd is not in the autocommit state, this function also starts a new
-transaction for thd if there is no active trx yet, and assigns a consistent
-read view to it if there is no read view yet.
-
-Why a deadlock of threads is not possible: the query cache calls this function
-at the start of a SELECT processing. Then the calling thread cannot be
-holding any InnoDB semaphores. The calling thread is holding the
-query cache mutex, and this function will reserver the InnoDB kernel mutex.
-Thus, the 'rank' in sync0sync.h of the MySQL query cache mutex is above
-the InnoDB kernel mutex. */
-
-my_bool
-innobase_query_caching_of_table_permitted(
-/*======================================*/
- /* out: TRUE if permitted, FALSE if not;
- note that the value FALSE does not mean
- we should invalidate the query cache:
- invalidation is called explicitly */
- THD* thd, /* in: thd of the user who is trying to
- store a result to the query cache or
- retrieve it */
- char* full_name, /* in: concatenation of database name,
- the null character '\0', and the table
- name */
- uint full_name_len, /* in: length of the full name, i.e.
- len(dbname) + len(tablename) + 1 */
- ulonglong *unused) /* unused for this engine */
-{
- ibool is_autocommit;
- trx_t* trx;
- char norm_name[1000];
-
- ut_a(full_name_len < 999);
-
- if (thd->variables.tx_isolation == ISO_SERIALIZABLE) {
- /* In the SERIALIZABLE mode we add LOCK IN SHARE MODE to every
- plain SELECT if AUTOCOMMIT is not on. */
-
- return((my_bool)FALSE);
- }
-
- trx = check_trx_exists(thd);
- if (trx->has_search_latch) {
- ut_print_timestamp(stderr);
- sql_print_error("The calling thread is holding the adaptive "
- "search, latch though calling "
- "innobase_query_caching_of_table_permitted.");
-
- mutex_enter_noninline(&kernel_mutex);
- trx_print(stderr, trx, 1024);
- mutex_exit_noninline(&kernel_mutex);
- }
-
- innobase_release_stat_resources(trx);
-
- if (!(thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN))) {
-
- is_autocommit = TRUE;
- } else {
- is_autocommit = FALSE;
-
- }
-
- if (is_autocommit && trx->n_mysql_tables_in_use == 0) {
- /* We are going to retrieve the query result from the query
- cache. This cannot be a store operation to the query cache
- because then MySQL would have locks on tables already.
-
- TODO: if the user has used LOCK TABLES to lock the table,
- then we open a transaction in the call of row_.. below.
- That trx can stay open until UNLOCK TABLES. The same problem
- exists even if we do not use the query cache. MySQL should be
- modified so that it ALWAYS calls some cleanup function when
- the processing of a query ends!
-
- We can imagine we instantaneously serialize this consistent
- read trx to the current trx id counter. If trx2 would have
- changed the tables of a query result stored in the cache, and
- trx2 would have already committed, making the result obsolete,
- then trx2 would have already invalidated the cache. Thus we
- can trust the result in the cache is ok for this query. */
-
- return((my_bool)TRUE);
- }
-
- /* Normalize the table name to InnoDB format */
-
- memcpy(norm_name, full_name, full_name_len);
-
- norm_name[strlen(norm_name)] = '/'; /* InnoDB uses '/' as the
- separator between db and table */
- norm_name[full_name_len] = '\0';
-#ifdef __WIN__
- innobase_casedn_str(norm_name);
-#endif
- /* The call of row_search_.. will start a new transaction if it is
- not yet started */
-
- if (trx->active_trans == 0) {
-
- innobase_register_trx_and_stmt(thd);
- trx->active_trans = 1;
- }
-
- if (row_search_check_if_query_cache_permitted(trx, norm_name)) {
-
- /* printf("Query cache for %s permitted\n", norm_name); */
-
- return((my_bool)TRUE);
- }
-
- /* printf("Query cache for %s NOT permitted\n", norm_name); */
-
- return((my_bool)FALSE);
-}
-
-/*********************************************************************
-Invalidates the MySQL query cache for the table.
-NOTE that the exact prototype of this function has to be in
-/innobase/row/row0ins.c! */
-extern "C"
-void
-innobase_invalidate_query_cache(
-/*============================*/
- trx_t* trx, /* in: transaction which modifies the table */
- char* full_name, /* in: concatenation of database name, null
- char '\0', table name, null char'\0';
- NOTE that in Windows this is always
- in LOWER CASE! */
- ulint full_name_len) /* in: full name length where also the null
- chars count */
-{
- /* Note that the sync0sync.h rank of the query cache mutex is just
- above the InnoDB kernel mutex. The caller of this function must not
- have latches of a lower rank. */
-
- /* Argument TRUE below means we are using transactions */
-#ifdef HAVE_QUERY_CACHE
- query_cache.invalidate((THD*)(trx->mysql_thd),
- (const char*)full_name,
- (uint32)full_name_len,
- TRUE);
-#endif
-}
-
-/*********************************************************************
-Get the quote character to be used in SQL identifiers.
-This definition must match the one in innobase/ut/ut0ut.c! */
-extern "C"
-int
-mysql_get_identifier_quote_char(
-/*============================*/
- /* out: quote character to be
- used in SQL identifiers; EOF if none */
- trx_t* trx, /* in: transaction */
- const char* name, /* in: name to print */
- ulint namelen)/* in: length of name */
-{
- if (!trx || !trx->mysql_thd) {
- return(EOF);
- }
- return(get_quote_char_for_identifier((THD*) trx->mysql_thd,
- name, (int) namelen));
-}
-
-/**************************************************************************
-Determines if the currently running transaction has been interrupted. */
-extern "C"
-ibool
-trx_is_interrupted(
-/*===============*/
- /* out: TRUE if interrupted */
- trx_t* trx) /* in: transaction */
-{
- return(trx && trx->mysql_thd && ((THD*) trx->mysql_thd)->killed);
-}
-
-/**************************************************************************
-Obtain a pointer to the MySQL THD object, as in current_thd(). This
-definition must match the one in sql/ha_innodb.cc! */
-extern "C"
-void*
-innobase_current_thd(void)
-/*======================*/
- /* out: MySQL THD object */
-{
- return(current_thd);
-}
-
-/*********************************************************************
-Call this when you have opened a new table handle in HANDLER, before you
-call index_read_idx() etc. Actually, we can let the cursor stay open even
-over a transaction commit! Then you should call this before every operation,
-fetch next etc. This function inits the necessary things even after a
-transaction commit. */
-
-void
-ha_innobase::init_table_handle_for_HANDLER(void)
-/*============================================*/
-{
- row_prebuilt_t* prebuilt;
-
- /* If current thd does not yet have a trx struct, create one.
- If the current handle does not yet have a prebuilt struct, create
- one. Update the trx pointers in the prebuilt struct. Normally
- this operation is done in external_lock. */
-
- update_thd(current_thd);
-
- /* Initialize the prebuilt struct much like it would be inited in
- external_lock */
-
- prebuilt = (row_prebuilt_t*)innobase_prebuilt;
-
- innobase_release_stat_resources(prebuilt->trx);
-
- /* If the transaction is not started yet, start it */
-
- trx_start_if_not_started_noninline(prebuilt->trx);
-
- /* Assign a read view if the transaction does not have it yet */
-
- trx_assign_read_view(prebuilt->trx);
-
- /* Set the MySQL flag to mark that there is an active transaction */
-
- if (prebuilt->trx->active_trans == 0) {
-
- innobase_register_trx_and_stmt(current_thd);
-
- prebuilt->trx->active_trans = 1;
- }
-
- /* We did the necessary inits in this function, no need to repeat them
- in row_search_for_mysql */
-
- prebuilt->sql_stat_start = FALSE;
-
- /* We let HANDLER always to do the reads as consistent reads, even
- if the trx isolation level would have been specified as SERIALIZABLE */
-
- prebuilt->select_lock_type = LOCK_NONE;
- prebuilt->stored_select_lock_type = LOCK_NONE;
-
- /* Always fetch all columns in the index record */
-
- prebuilt->hint_need_to_fetch_extra_cols = ROW_RETRIEVE_ALL_COLS;
-
- /* We want always to fetch all columns in the whole row? Or do
- we???? */
-
- prebuilt->read_just_key = FALSE;
-
- prebuilt->used_in_HANDLER = TRUE;
-
- prebuilt->keep_other_fields_on_keyread = FALSE;
-}
-
-/*************************************************************************
-Opens an InnoDB database. */
-
-bool
-innobase_init(void)
-/*===============*/
- /* out: &innobase_hton, or NULL on error */
-{
- static char current_dir[3]; /* Set if using current lib */
- int err;
- bool ret;
- char *default_path;
-
- DBUG_ENTER("innobase_init");
-
- if (have_innodb != SHOW_OPTION_YES)
- goto error;
-
- ut_a(DATA_MYSQL_TRUE_VARCHAR == (ulint)MYSQL_TYPE_VARCHAR);
-
- /* Check that values don't overflow on 32-bit systems. */
- if (sizeof(ulint) == 4) {
- if (innobase_buffer_pool_size > UINT_MAX32) {
- sql_print_error(
- "innobase_buffer_pool_size can't be over 4GB"
- " on 32-bit systems");
-
- goto error;
- }
-
- if (innobase_log_file_size > UINT_MAX32) {
- sql_print_error(
- "innobase_log_file_size can't be over 4GB"
- " on 32-bit systems");
-
- goto error;
- }
- }
-
- os_innodb_umask = (ulint)my_umask;
-
- /* First calculate the default path for innodb_data_home_dir etc.,
- in case the user has not given any value.
-
- Note that when using the embedded server, the datadirectory is not
- necessarily the current directory of this program. */
-
- if (mysqld_embedded) {
- default_path = mysql_real_data_home;
- fil_path_to_mysql_datadir = mysql_real_data_home;
- } else {
- /* It's better to use current lib, to keep paths short */
- current_dir[0] = FN_CURLIB;
- current_dir[1] = FN_LIBCHAR;
- current_dir[2] = 0;
- default_path = current_dir;
- }
-
- ut_a(default_path);
-
- if (specialflag & SPECIAL_NO_PRIOR) {
- srv_set_thread_priorities = FALSE;
- } else {
- srv_set_thread_priorities = TRUE;
- srv_query_thread_priority = QUERY_PRIOR;
- }
-
- /* Set InnoDB initialization parameters according to the values
- read from MySQL .cnf file */
-
- /*--------------- Data files -------------------------*/
-
- /* The default dir for data files is the datadir of MySQL */
-
- srv_data_home = (innobase_data_home_dir ? innobase_data_home_dir :
- default_path);
-
- /* Set default InnoDB data file size to 10 MB and let it be
- auto-extending. Thus users can use InnoDB in >= 4.0 without having
- to specify any startup options. */
-
- if (!innobase_data_file_path) {
- innobase_data_file_path = (char*) "ibdata1:10M:autoextend";
- }
-
- /* Since InnoDB edits the argument in the next call, we make another
- copy of it: */
-
- internal_innobase_data_file_path = my_strdup(innobase_data_file_path,
- MYF(MY_FAE));
-
- ret = (bool) srv_parse_data_file_paths_and_sizes(
- internal_innobase_data_file_path,
- &srv_data_file_names,
- &srv_data_file_sizes,
- &srv_data_file_is_raw_partition,
- &srv_n_data_files,
- &srv_auto_extend_last_data_file,
- &srv_last_file_size_max);
- if (ret == FALSE) {
- sql_print_error(
- "InnoDB: syntax error in innodb_data_file_path");
- my_free(internal_innobase_data_file_path,
- MYF(MY_ALLOW_ZERO_PTR));
- goto error;
- }
-
- /* -------------- Log files ---------------------------*/
-
- /* The default dir for log files is the datadir of MySQL */
-
- if (!innobase_log_group_home_dir) {
- innobase_log_group_home_dir = default_path;
- }
-
-#ifdef UNIV_LOG_ARCHIVE
- /* Since innodb_log_arch_dir has no relevance under MySQL,
- starting from 4.0.6 we always set it the same as
- innodb_log_group_home_dir: */
-
- innobase_log_arch_dir = innobase_log_group_home_dir;
-
- srv_arch_dir = innobase_log_arch_dir;
-#endif /* UNIG_LOG_ARCHIVE */
-
- ret = (bool)
- srv_parse_log_group_home_dirs(innobase_log_group_home_dir,
- &srv_log_group_home_dirs);
-
- if (ret == FALSE || innobase_mirrored_log_groups != 1) {
- sql_print_error("syntax error in innodb_log_group_home_dir, or a "
- "wrong number of mirrored log groups");
-
- my_free(internal_innobase_data_file_path,
- MYF(MY_ALLOW_ZERO_PTR));
- goto error;
- }
-
- /* --------------------------------------------------*/
-
- srv_file_flush_method_str = innobase_unix_file_flush_method;
-
- srv_n_log_groups = (ulint) innobase_mirrored_log_groups;
- srv_n_log_files = (ulint) innobase_log_files_in_group;
- srv_log_file_size = (ulint) innobase_log_file_size;
-
-#ifdef UNIV_LOG_ARCHIVE
- srv_log_archive_on = (ulint) innobase_log_archive;
-#endif /* UNIV_LOG_ARCHIVE */
- srv_log_buffer_size = (ulint) innobase_log_buffer_size;
-
- /* We set srv_pool_size here in units of 1 kB. InnoDB internally
- changes the value so that it becomes the number of database pages. */
-
- if (innobase_buffer_pool_awe_mem_mb == 0) {
- /* Careful here: we first convert the signed long int to ulint
- and only after that divide */
-
- srv_pool_size = ((ulint) innobase_buffer_pool_size) / 1024;
- } else {
- srv_use_awe = TRUE;
- srv_pool_size = (ulint)
- (1024 * innobase_buffer_pool_awe_mem_mb);
- srv_awe_window_size = (ulint) innobase_buffer_pool_size;
-
- /* Note that what the user specified as
- innodb_buffer_pool_size is actually the AWE memory window
- size in this case, and the real buffer pool size is
- determined by .._awe_mem_mb. */
- }
-
- srv_mem_pool_size = (ulint) innobase_additional_mem_pool_size;
-
- srv_n_file_io_threads = (ulint) innobase_file_io_threads;
-
- srv_lock_wait_timeout = (ulint) innobase_lock_wait_timeout;
- srv_force_recovery = (ulint) innobase_force_recovery;
-
- srv_use_doublewrite_buf = (ibool) innobase_use_doublewrite;
- srv_use_checksums = (ibool) innobase_use_checksums;
-
- srv_use_adaptive_hash_indexes = (ibool) innobase_adaptive_hash_index;
-
- os_use_large_pages = (ibool) innobase_use_large_pages;
- os_large_page_size = (ulint) innobase_large_page_size;
-
- row_rollback_on_timeout = (ibool) innobase_rollback_on_timeout;
-
- srv_file_per_table = (ibool) innobase_file_per_table;
- srv_locks_unsafe_for_binlog = (ibool) innobase_locks_unsafe_for_binlog;
-
- srv_max_n_open_files = (ulint) innobase_open_files;
- srv_innodb_status = (ibool) innobase_create_status_file;
-
- srv_print_verbose_log = mysqld_embedded ? 0 : 1;
-
- /* Store the default charset-collation number of this MySQL
- installation */
-
- data_mysql_default_charset_coll = (ulint)default_charset_info->number;
-
- ut_a(DATA_MYSQL_LATIN1_SWEDISH_CHARSET_COLL ==
- my_charset_latin1.number);
- ut_a(DATA_MYSQL_BINARY_CHARSET_COLL == my_charset_bin.number);
-
- /* Store the latin1_swedish_ci character ordering table to InnoDB. For
- non-latin1_swedish_ci charsets we use the MySQL comparison functions,
- and consequently we do not need to know the ordering internally in
- InnoDB. */
-
- ut_a(0 == strcmp((char*)my_charset_latin1.name,
- (char*)"latin1_swedish_ci"));
- memcpy(srv_latin1_ordering, my_charset_latin1.sort_order, 256);
-
- /* Since we in this module access directly the fields of a trx
- struct, and due to different headers and flags it might happen that
- mutex_t has a different size in this module and in InnoDB
- modules, we check at run time that the size is the same in
- these compilation modules. */
-
- srv_sizeof_trx_t_in_ha_innodb_cc = sizeof(trx_t);
-
- err = innobase_start_or_create_for_mysql();
-
- if (err != DB_SUCCESS) {
- my_free(internal_innobase_data_file_path,
- MYF(MY_ALLOW_ZERO_PTR));
- goto error;
- }
-
- (void) hash_init(&innobase_open_tables,system_charset_info, 32, 0, 0,
- (hash_get_key) innobase_get_key, 0, 0);
- pthread_mutex_init(&innobase_share_mutex, MY_MUTEX_INIT_FAST);
- pthread_mutex_init(&prepare_commit_mutex, MY_MUTEX_INIT_FAST);
- pthread_mutex_init(&commit_threads_m, MY_MUTEX_INIT_FAST);
- pthread_mutex_init(&commit_cond_m, MY_MUTEX_INIT_FAST);
- pthread_cond_init(&commit_cond, NULL);
- innodb_inited= 1;
-
- /* If this is a replication slave and we needed to do a crash recovery,
- set the master binlog position to what InnoDB internally knew about
- how far we got transactions durable inside InnoDB. There is a
- problem here: if the user used also MyISAM tables, InnoDB might not
- know the right position for them.
-
- THIS DOES NOT WORK CURRENTLY because replication seems to initialize
- glob_mi also after innobase_init. */
-
-/* if (trx_sys_mysql_master_log_pos != -1) {
- ut_memcpy(glob_mi.log_file_name, trx_sys_mysql_master_log_name,
- 1 + ut_strlen(trx_sys_mysql_master_log_name));
- glob_mi.pos = trx_sys_mysql_master_log_pos;
- }
-*/
- DBUG_RETURN(FALSE);
-error:
- have_innodb= SHOW_OPTION_DISABLED; // If we couldn't use handler
- DBUG_RETURN(TRUE);
-}
-
-/***********************************************************************
-Closes an InnoDB database. */
-
-bool
-innobase_end(void)
-/*==============*/
- /* out: TRUE if error */
-{
- int err= 0;
-
- DBUG_ENTER("innobase_end");
-
-#ifdef __NETWARE__ /* some special cleanup for NetWare */
- if (nw_panic) {
- set_panic_flag_for_netware();
- }
-#endif
- if (innodb_inited) {
-
- srv_fast_shutdown = (ulint) innobase_fast_shutdown;
- innodb_inited = 0;
- if (innobase_shutdown_for_mysql() != DB_SUCCESS) {
- err = 1;
- }
- hash_free(&innobase_open_tables);
- my_free(internal_innobase_data_file_path,
- MYF(MY_ALLOW_ZERO_PTR));
- pthread_mutex_destroy(&innobase_share_mutex);
- pthread_mutex_destroy(&prepare_commit_mutex);
- pthread_mutex_destroy(&commit_threads_m);
- pthread_mutex_destroy(&commit_cond_m);
- pthread_cond_destroy(&commit_cond);
- }
-
- DBUG_RETURN(err);
-}
-
-/********************************************************************
-Flushes InnoDB logs to disk and makes a checkpoint. Really, a commit flushes
-the logs, and the name of this function should be innobase_checkpoint. */
-
-bool
-innobase_flush_logs(void)
-/*=====================*/
- /* out: TRUE if error */
-{
- bool result = 0;
-
- DBUG_ENTER("innobase_flush_logs");
-
- log_buffer_flush_to_disk();
-
- DBUG_RETURN(result);
-}
-
-/*********************************************************************
-Commits a transaction in an InnoDB database. */
-
-void
-innobase_commit_low(
-/*================*/
- trx_t* trx) /* in: transaction handle */
-{
- if (trx->conc_state == TRX_NOT_STARTED) {
-
- return;
- }
-
-#ifdef HAVE_REPLICATION
- THD *thd=current_thd;
-
- if (thd && thd->slave_thread) {
- /* Update the replication position info inside InnoDB */
-
- trx->mysql_master_log_file_name
- = active_mi->rli.group_master_log_name;
- trx->mysql_master_log_pos = ((ib_longlong)
- active_mi->rli.future_group_master_log_pos);
- }
-#endif /* HAVE_REPLICATION */
-
- trx_commit_for_mysql(trx);
-}
-
-/*********************************************************************
-Creates an InnoDB transaction struct for the thd if it does not yet have one.
-Starts a new InnoDB transaction if a transaction is not yet started. And
-assigns a new snapshot for a consistent read if the transaction does not yet
-have one. */
-
-int
-innobase_start_trx_and_assign_read_view(
-/*====================================*/
- /* out: 0 */
- THD* thd) /* in: MySQL thread handle of the user for whom
- the transaction should be committed */
-{
- trx_t* trx;
-
- DBUG_ENTER("innobase_start_trx_and_assign_read_view");
-
- /* Create a new trx struct for thd, if it does not yet have one */
-
- trx = check_trx_exists(thd);
-
- /* This is just to play safe: release a possible FIFO ticket and
- search latch. Since we will reserve the kernel mutex, we have to
- release the search system latch first to obey the latching order. */
-
- innobase_release_stat_resources(trx);
-
- /* If the transaction is not started yet, start it */
-
- trx_start_if_not_started_noninline(trx);
-
- /* Assign a read view if the transaction does not have it yet */
-
- trx_assign_read_view(trx);
-
- /* Set the MySQL flag to mark that there is an active transaction */
-
- if (trx->active_trans == 0) {
-
- innobase_register_trx_and_stmt(current_thd);
-
- trx->active_trans = 1;
- }
-
- DBUG_RETURN(0);
-}
-
-/*********************************************************************
-Commits a transaction in an InnoDB database or marks an SQL statement
-ended. */
-static
-int
-innobase_commit(
-/*============*/
- /* out: 0 */
- THD* thd, /* in: MySQL thread handle of the user for whom
- the transaction should be committed */
- bool all) /* in: TRUE - commit transaction
- FALSE - the current SQL statement ended */
-{
- trx_t* trx;
-
- DBUG_ENTER("innobase_commit");
- DBUG_PRINT("trans", ("ending transaction"));
-
- trx = check_trx_exists(thd);
-
- /* Update the info whether we should skip XA steps that eat CPU time */
- trx->support_xa = (ibool)(thd->variables.innodb_support_xa);
-
- /* Release a possible FIFO ticket and search latch. Since we will
- reserve the kernel mutex, we have to release the search system latch
- first to obey the latching order. */
-
- if (trx->has_search_latch) {
- trx_search_latch_release_if_reserved(trx);
- }
-
- /* The flag trx->active_trans is set to 1 in
-
- 1. ::external_lock(),
- 2. ::start_stmt(),
- 3. innobase_query_caching_of_table_permitted(),
- 4. innobase_savepoint(),
- 5. ::init_table_handle_for_HANDLER(),
- 6. innobase_start_trx_and_assign_read_view(),
- 7. ::transactional_table_lock()
-
- and it is only set to 0 in a commit or a rollback. If it is 0 we know
- there cannot be resources to be freed and we could return immediately.
- For the time being, we play safe and do the cleanup though there should
- be nothing to clean up. */
-
- if (trx->active_trans == 0
- && trx->conc_state != TRX_NOT_STARTED) {
-
- sql_print_error("trx->active_trans == 0, but trx->conc_state != "
- "TRX_NOT_STARTED");
- }
- if (all
- || (!(thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)))) {
-
- /* We were instructed to commit the whole transaction, or
- this is an SQL statement end and autocommit is on */
-
- /* We need current binlog position for ibbackup to work.
- Note, the position is current because of prepare_commit_mutex */
-retry:
- if (srv_commit_concurrency > 0)
- {
- pthread_mutex_lock(&commit_cond_m);
- commit_threads++;
- if (commit_threads > srv_commit_concurrency)
- {
- commit_threads--;
- pthread_cond_wait(&commit_cond, &commit_cond_m);
- pthread_mutex_unlock(&commit_cond_m);
- goto retry;
- }
- else
- pthread_mutex_unlock(&commit_cond_m);
- }
-
- trx->mysql_log_file_name = mysql_bin_log.get_log_fname();
- trx->mysql_log_offset =
- (ib_longlong)mysql_bin_log.get_log_file()->pos_in_file;
-
- innobase_commit_low(trx);
-
- if (srv_commit_concurrency > 0)
- {
- pthread_mutex_lock(&commit_cond_m);
- commit_threads--;
- pthread_cond_signal(&commit_cond);
- pthread_mutex_unlock(&commit_cond_m);
- }
-
- if (trx->active_trans == 2) {
-
- pthread_mutex_unlock(&prepare_commit_mutex);
- }
-
- trx->active_trans = 0;
-
- } else {
- /* We just mark the SQL statement ended and do not do a
- transaction commit */
-
- if (trx->auto_inc_lock) {
- /* If we had reserved the auto-inc lock for some
- table in this SQL statement we release it now */
-
- row_unlock_table_autoinc_for_mysql(trx);
- }
- /* Store the current undo_no of the transaction so that we
- know where to roll back if we have to roll back the next
- SQL statement */
-
- trx_mark_sql_stat_end(trx);
- }
-
- /* Tell the InnoDB server that there might be work for utility
- threads: */
- if (trx->declared_to_be_inside_innodb) {
- /* Release our possible ticket in the FIFO */
-
- srv_conc_force_exit_innodb(trx);
- }
- srv_active_wake_master_thread();
-
- DBUG_RETURN(0);
-}
-
-/* TODO: put the
-MySQL-4.1 functionality back to 5.0. This is needed to get InnoDB Hot Backup
-to work. */
-
-/*********************************************************************
-This is called when MySQL writes the binlog entry for the current
-transaction. Writes to the InnoDB tablespace info which tells where the
-MySQL binlog entry for the current transaction ended. Also commits the
-transaction inside InnoDB but does NOT flush InnoDB log files to disk.
-To flush you have to call innobase_commit_complete(). We have separated
-flushing to eliminate the bottleneck of LOCK_log in log.cc which disabled
-InnoDB's group commit capability. */
-
-int
-innobase_report_binlog_offset_and_commit(
-/*=====================================*/
- /* out: 0 */
- THD* thd, /* in: user thread */
- void* trx_handle, /* in: InnoDB trx handle */
- char* log_file_name, /* in: latest binlog file name */
- my_off_t end_offset) /* in: the offset in the binlog file
- up to which we wrote */
-{
- trx_t* trx;
-
- trx = (trx_t*)trx_handle;
-
- ut_a(trx != NULL);
-
- trx->mysql_log_file_name = log_file_name;
- trx->mysql_log_offset = (ib_longlong)end_offset;
-
- trx->flush_log_later = TRUE;
-
- innobase_commit(thd, TRUE);
-
- trx->flush_log_later = FALSE;
-
- return(0);
-}
-
-#if 0
-/***********************************************************************
-This function stores the binlog offset and flushes logs. */
-
-void
-innobase_store_binlog_offset_and_flush_log(
-/*=======================================*/
- char *binlog_name, /* in: binlog name */
- longlong offset) /* in: binlog offset */
-{
- mtr_t mtr;
-
- assert(binlog_name != NULL);
-
- /* Start a mini-transaction */
- mtr_start_noninline(&mtr);
-
- /* Update the latest MySQL binlog name and offset info
- in trx sys header */
-
- trx_sys_update_mysql_binlog_offset(
- binlog_name,
- offset,
- TRX_SYS_MYSQL_LOG_INFO, &mtr);
-
- /* Commits the mini-transaction */
- mtr_commit(&mtr);
-
- /* Synchronous flush of the log buffer to disk */
- log_buffer_flush_to_disk();
-}
-#endif
-
-/*********************************************************************
-This is called after MySQL has written the binlog entry for the current
-transaction. Flushes the InnoDB log files to disk if required. */
-
-int
-innobase_commit_complete(
-/*=====================*/
- /* out: 0 */
- THD* thd) /* in: user thread */
-{
- trx_t* trx;
-
- trx = (trx_t*) thd->ha_data[innobase_hton.slot];
-
- if (trx && trx->active_trans) {
-
- trx->active_trans = 0;
-
- if (UNIV_UNLIKELY(srv_flush_log_at_trx_commit == 0)) {
-
- return(0);
- }
-
- trx_commit_complete_for_mysql(trx);
- }
-
- return(0);
-}
-
-/*********************************************************************
-Rolls back a transaction or the latest SQL statement. */
-
-static int
-innobase_rollback(
-/*==============*/
- /* out: 0 or error number */
- THD* thd, /* in: handle to the MySQL thread of the user
- whose transaction should be rolled back */
- bool all) /* in: TRUE - commit transaction
- FALSE - the current SQL statement ended */
-{
- int error = 0;
- trx_t* trx;
-
- DBUG_ENTER("innobase_rollback");
- DBUG_PRINT("trans", ("aborting transaction"));
-
- trx = check_trx_exists(thd);
-
- /* Update the info whether we should skip XA steps that eat CPU time */
- trx->support_xa = (ibool)(thd->variables.innodb_support_xa);
-
- /* Release a possible FIFO ticket and search latch. Since we will
- reserve the kernel mutex, we have to release the search system latch
- first to obey the latching order. */
-
- innobase_release_stat_resources(trx);
-
- if (trx->auto_inc_lock) {
- /* If we had reserved the auto-inc lock for some table (if
- we come here to roll back the latest SQL statement) we
- release it now before a possibly lengthy rollback */
-
- row_unlock_table_autoinc_for_mysql(trx);
- }
-
- if (all
- || (!(thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)))) {
-
- error = trx_rollback_for_mysql(trx);
- trx->active_trans = 0;
- } else {
- error = trx_rollback_last_sql_stat_for_mysql(trx);
- }
-
- DBUG_RETURN(convert_error_code_to_mysql(error, NULL));
-}
-
-/*********************************************************************
-Rolls back a transaction */
-
-int
-innobase_rollback_trx(
-/*==================*/
- /* out: 0 or error number */
- trx_t* trx) /* in: transaction */
-{
- int error = 0;
-
- DBUG_ENTER("innobase_rollback_trx");
- DBUG_PRINT("trans", ("aborting transaction"));
-
- /* Release a possible FIFO ticket and search latch. Since we will
- reserve the kernel mutex, we have to release the search system latch
- first to obey the latching order. */
-
- innobase_release_stat_resources(trx);
-
- if (trx->auto_inc_lock) {
- /* If we had reserved the auto-inc lock for some table (if
- we come here to roll back the latest SQL statement) we
- release it now before a possibly lengthy rollback */
-
- row_unlock_table_autoinc_for_mysql(trx);
- }
-
- error = trx_rollback_for_mysql(trx);
-
- DBUG_RETURN(convert_error_code_to_mysql(error, NULL));
-}
-
-/*********************************************************************
-Rolls back a transaction to a savepoint. */
-
-static int
-innobase_rollback_to_savepoint(
-/*===========================*/
- /* out: 0 if success, HA_ERR_NO_SAVEPOINT if
- no savepoint with the given name */
- THD* thd, /* in: handle to the MySQL thread of the user
- whose transaction should be rolled back */
- void *savepoint) /* in: savepoint data */
-{
- ib_longlong mysql_binlog_cache_pos;
- int error = 0;
- trx_t* trx;
- char name[64];
-
- DBUG_ENTER("innobase_rollback_to_savepoint");
-
- trx = check_trx_exists(thd);
-
- /* Release a possible FIFO ticket and search latch. Since we will
- reserve the kernel mutex, we have to release the search system latch
- first to obey the latching order. */
-
- innobase_release_stat_resources(trx);
-
- /* TODO: use provided savepoint data area to store savepoint data */
-
- longlong2str((ulint)savepoint, name, 36);
-
- error = (int) trx_rollback_to_savepoint_for_mysql(trx, name,
- &mysql_binlog_cache_pos);
- DBUG_RETURN(convert_error_code_to_mysql(error, NULL));
-}
-
-/*********************************************************************
-Release transaction savepoint name. */
-static
-int
-innobase_release_savepoint(
-/*=======================*/
- /* out: 0 if success, HA_ERR_NO_SAVEPOINT if
- no savepoint with the given name */
- THD* thd, /* in: handle to the MySQL thread of the user
- whose transaction should be rolled back */
- void* savepoint) /* in: savepoint data */
-{
- int error = 0;
- trx_t* trx;
- char name[64];
-
- DBUG_ENTER("innobase_release_savepoint");
-
- trx = check_trx_exists(thd);
-
- /* TODO: use provided savepoint data area to store savepoint data */
-
- longlong2str((ulint)savepoint, name, 36);
-
- error = (int) trx_release_savepoint_for_mysql(trx, name);
-
- DBUG_RETURN(convert_error_code_to_mysql(error, NULL));
-}
-
-/*********************************************************************
-Sets a transaction savepoint. */
-static
-int
-innobase_savepoint(
-/*===============*/
- /* out: always 0, that is, always succeeds */
- THD* thd, /* in: handle to the MySQL thread */
- void* savepoint) /* in: savepoint data */
-{
- int error = 0;
- trx_t* trx;
-
- DBUG_ENTER("innobase_savepoint");
-
- /*
- In the autocommit mode there is no sense to set a savepoint
- (unless we are in sub-statement), so SQL layer ensures that
- this method is never called in such situation.
- */
- DBUG_ASSERT(thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN) ||
- thd->in_sub_stmt);
-
- trx = check_trx_exists(thd);
-
- /* Release a possible FIFO ticket and search latch. Since we will
- reserve the kernel mutex, we have to release the search system latch
- first to obey the latching order. */
-
- innobase_release_stat_resources(trx);
-
- /* cannot happen outside of transaction */
- DBUG_ASSERT(trx->active_trans);
-
- /* TODO: use provided savepoint data area to store savepoint data */
- char name[64];
- longlong2str((ulint)savepoint,name,36);
-
- error = (int) trx_savepoint_for_mysql(trx, name, (ib_longlong)0);
-
- DBUG_RETURN(convert_error_code_to_mysql(error, NULL));
-}
-
-/*********************************************************************
-Frees a possible InnoDB trx object associated with the current THD. */
-static
-int
-innobase_close_connection(
-/*======================*/
- /* out: 0 or error number */
- THD* thd) /* in: handle to the MySQL thread of the user
- whose resources should be free'd */
-{
- trx_t* trx;
-
- trx = (trx_t*)thd->ha_data[innobase_hton.slot];
-
- ut_a(trx);
-
- if (trx->active_trans == 0
- && trx->conc_state != TRX_NOT_STARTED) {
-
- sql_print_error("trx->active_trans == 0, but trx->conc_state != "
- "TRX_NOT_STARTED");
- }
-
-
- if (trx->conc_state != TRX_NOT_STARTED &&
- global_system_variables.log_warnings)
- sql_print_warning("MySQL is closing a connection that has an active "
- "InnoDB transaction. %lu row modifications will "
- "roll back.",
- (ulong)trx->undo_no.low);
-
- innobase_rollback_trx(trx);
-
- trx_free_for_mysql(trx);
-
- return(0);
-}
-
-
-/*****************************************************************************
-** InnoDB database tables
-*****************************************************************************/
-
-/********************************************************************
-Get the record format from the data dictionary. */
-enum row_type
-ha_innobase::get_row_type() const
-/*=============================*/
- /* out: ROW_TYPE_REDUNDANT or ROW_TYPE_COMPACT */
-{
- row_prebuilt_t* prebuilt = (row_prebuilt_t*) innobase_prebuilt;
-
- if (prebuilt && prebuilt->table) {
- if (prebuilt->table->comp) {
- return(ROW_TYPE_COMPACT);
- } else {
- return(ROW_TYPE_REDUNDANT);
- }
- }
- ut_ad(0);
- return(ROW_TYPE_NOT_USED);
-}
-
-/********************************************************************
-Gives the file extension of an InnoDB single-table tablespace. */
-static const char* ha_innobase_exts[] = {
- ".ibd",
- NullS
-};
-
-const char**
-ha_innobase::bas_ext() const
-/*========================*/
- /* out: file extension string */
-{
- return ha_innobase_exts;
-}
-
-
-/*********************************************************************
-Normalizes a table name string. A normalized name consists of the
-database name catenated to '/' and table name. An example:
-test/mytable. On Windows normalization puts both the database name and the
-table name always to lower case. */
-static
-void
-normalize_table_name(
-/*=================*/
- char* norm_name, /* out: normalized name as a
- null-terminated string */
- const char* name) /* in: table name string */
-{
- char* name_ptr;
- char* db_ptr;
- char* ptr;
-
- /* Scan name from the end */
-
- ptr = strend(name)-1;
-
- while (ptr >= name && *ptr != '\\' && *ptr != '/') {
- ptr--;
- }
-
- name_ptr = ptr + 1;
-
- DBUG_ASSERT(ptr > name);
-
- ptr--;
-
- while (ptr >= name && *ptr != '\\' && *ptr != '/') {
- ptr--;
- }
-
- db_ptr = ptr + 1;
-
- memcpy(norm_name, db_ptr, strlen(name) + 1 - (db_ptr - name));
-
- norm_name[name_ptr - db_ptr - 1] = '/';
-
-#ifdef __WIN__
- innobase_casedn_str(norm_name);
-#endif
-}
-
-/*********************************************************************
-Creates and opens a handle to a table which already exists in an InnoDB
-database. */
-
-int
-ha_innobase::open(
-/*==============*/
- /* out: 1 if error, 0 if success */
- const char* name, /* in: table name */
- int mode, /* in: not used */
- uint test_if_locked) /* in: not used */
-{
- dict_table_t* ib_table;
- char norm_name[1000];
- THD* thd;
-
- DBUG_ENTER("ha_innobase::open");
-
- UT_NOT_USED(mode);
- UT_NOT_USED(test_if_locked);
-
- thd = current_thd;
-
- /* Under some cases MySQL seems to call this function while
- holding btr_search_latch. This breaks the latching order as
- we acquire dict_sys->mutex below and leads to a deadlock. */
- if (thd != NULL) {
- innobase_release_temporary_latches(thd);
- }
-
- normalize_table_name(norm_name, name);
-
- user_thd = NULL;
-
- last_query_id = (ulong)-1;
-
- if (!(share=get_share(name))) {
-
- DBUG_RETURN(1);
- }
-
- /* Create buffers for packing the fields of a record. Why
- table->reclength did not work here? Obviously, because char
- fields when packed actually became 1 byte longer, when we also
- stored the string length as the first byte. */
-
- upd_and_key_val_buff_len =
- table->s->reclength + table->s->max_key_length
- + MAX_REF_PARTS * 3;
- if (!(mysql_byte*) my_multi_malloc(MYF(MY_WME),
- &upd_buff, upd_and_key_val_buff_len,
- &key_val_buff, upd_and_key_val_buff_len,
- NullS)) {
- free_share(share);
-
- DBUG_RETURN(1);
- }
-
- /* Get pointer to a table object in InnoDB dictionary cache */
-
- ib_table = dict_table_get_and_increment_handle_count(
- norm_name, NULL);
- if (NULL == ib_table) {
- ut_print_timestamp(stderr);
- sql_print_error("Cannot find table %s from the internal data "
- "dictionary\nof InnoDB though the .frm file "
- "for the table exists. Maybe you\nhave "
- "deleted and recreated InnoDB data files but "
- "have forgotten\nto delete the corresponding "
- ".frm files of InnoDB tables, or you\n"
- "have moved .frm files to another database?\n"
- "See http://dev.mysql.com/doc/refman/5.0/en/innodb-troubleshooting.html\n"
- "how you can resolve the problem.\n",
- norm_name);
- free_share(share);
- my_free((gptr) upd_buff, MYF(0));
- my_errno = ENOENT;
-
- DBUG_RETURN(HA_ERR_NO_SUCH_TABLE);
- }
-
- if (ib_table->ibd_file_missing && !thd->tablespace_op) {
- ut_print_timestamp(stderr);
- sql_print_error("MySQL is trying to open a table handle but "
- "the .ibd file for\ntable %s does not exist.\n"
- "Have you deleted the .ibd file from the "
- "database directory under\nthe MySQL datadir, "
- "or have you used DISCARD TABLESPACE?\n"
- "See http://dev.mysql.com/doc/refman/5.0/en/innodb-troubleshooting.html\n"
- "how you can resolve the problem.\n",
- norm_name);
- free_share(share);
- my_free((gptr) upd_buff, MYF(0));
- my_errno = ENOENT;
-
- dict_table_decrement_handle_count(ib_table);
- DBUG_RETURN(HA_ERR_NO_SUCH_TABLE);
- }
-
- innobase_prebuilt = row_create_prebuilt(ib_table);
-
- ((row_prebuilt_t*)innobase_prebuilt)->mysql_row_len =
- table->s->reclength;
-
- /* Looks like MySQL-3.23 sometimes has primary key number != 0 */
-
- primary_key = table->s->primary_key;
- key_used_on_scan = primary_key;
-
- /* Allocate a buffer for a 'row reference'. A row reference is
- a string of bytes of length ref_length which uniquely specifies
- a row in our table. Note that MySQL may also compare two row
- references for equality by doing a simple memcmp on the strings
- of length ref_length! */
-
- if (!row_table_got_default_clust_index(ib_table)) {
- if (primary_key >= MAX_KEY) {
- sql_print_error("Table %s has a primary key in InnoDB data "
- "dictionary, but not in MySQL!", name);
- }
-
- ((row_prebuilt_t*)innobase_prebuilt)
- ->clust_index_was_generated = FALSE;
- /* MySQL allocates the buffer for ref. key_info->key_length
- includes space for all key columns + one byte for each column
- that may be NULL. ref_length must be as exact as possible to
- save space, because all row reference buffers are allocated
- based on ref_length. */
-
- ref_length = table->key_info[primary_key].key_length;
- } else {
- if (primary_key != MAX_KEY) {
- sql_print_error("Table %s has no primary key in InnoDB data "
- "dictionary, but has one in MySQL! If you "
- "created the table with a MySQL version < "
- "3.23.54 and did not define a primary key, "
- "but defined a unique key with all non-NULL "
- "columns, then MySQL internally treats that "
- "key as the primary key. You can fix this "
- "error by dump + DROP + CREATE + reimport "
- "of the table.", name);
- }
-
- ((row_prebuilt_t*)innobase_prebuilt)
- ->clust_index_was_generated = TRUE;
-
- ref_length = DATA_ROW_ID_LEN;
-
- /* If we automatically created the clustered index, then
- MySQL does not know about it, and MySQL must NOT be aware
- of the index used on scan, to make it avoid checking if we
- update the column of the index. That is why we assert below
- that key_used_on_scan is the undefined value MAX_KEY.
- The column is the row id in the automatical generation case,
- and it will never be updated anyway. */
-
- if (key_used_on_scan != MAX_KEY) {
- sql_print_warning("Table %s key_used_on_scan is %lu even "
- "though there is no primary key inside "
- "InnoDB.", name, (ulong) key_used_on_scan);
- }
- }
-
- block_size = 16 * 1024; /* Index block size in InnoDB: used by MySQL
- in query optimization */
-
- /* Init table lock structure */
- thr_lock_data_init(&share->lock,&lock,(void*) 0);
-
- info(HA_STATUS_NO_LOCK | HA_STATUS_VARIABLE | HA_STATUS_CONST);
-
- DBUG_RETURN(0);
-}
-
-uint
-ha_innobase::max_supported_key_part_length() const
-{
- return(DICT_MAX_INDEX_COL_LEN - 1);
-}
-
-/**********************************************************************
-Closes a handle to an InnoDB table. */
-
-int
-ha_innobase::close(void)
-/*====================*/
- /* out: 0 */
-{
- THD* thd;
-
- DBUG_ENTER("ha_innobase::close");
-
- thd = current_thd; // avoid calling current_thd twice, it may be slow
- if (thd != NULL) {
- innobase_release_temporary_latches(thd);
- }
-
- row_prebuilt_free((row_prebuilt_t*) innobase_prebuilt);
-
- my_free((gptr) upd_buff, MYF(0));
- free_share(share);
-
- /* Tell InnoDB server that there might be work for
- utility threads: */
-
- srv_active_wake_master_thread();
-
- DBUG_RETURN(0);
-}
-
-/* The following accessor functions should really be inside MySQL code! */
-
-/******************************************************************
-Gets field offset for a field in a table. */
-inline
-uint
-get_field_offset(
-/*=============*/
- /* out: offset */
- TABLE* table, /* in: MySQL table object */
- Field* field) /* in: MySQL field object */
-{
- return((uint) (field->ptr - (char*) table->record[0]));
-}
-
-/******************************************************************
-Checks if a field in a record is SQL NULL. Uses the record format
-information in table to track the null bit in record. */
-inline
-uint
-field_in_record_is_null(
-/*====================*/
- /* out: 1 if NULL, 0 otherwise */
- TABLE* table, /* in: MySQL table object */
- Field* field, /* in: MySQL field object */
- char* record) /* in: a row in MySQL format */
-{
- int null_offset;
-
- if (!field->null_ptr) {
-
- return(0);
- }
-
- null_offset = (uint) ((char*) field->null_ptr
- - (char*) table->record[0]);
-
- if (record[null_offset] & field->null_bit) {
-
- return(1);
- }
-
- return(0);
-}
-
-/******************************************************************
-Sets a field in a record to SQL NULL. Uses the record format
-information in table to track the null bit in record. */
-inline
-void
-set_field_in_record_to_null(
-/*========================*/
- TABLE* table, /* in: MySQL table object */
- Field* field, /* in: MySQL field object */
- char* record) /* in: a row in MySQL format */
-{
- int null_offset;
-
- null_offset = (uint) ((char*) field->null_ptr
- - (char*) table->record[0]);
-
- record[null_offset] = record[null_offset] | field->null_bit;
-}
-
-extern "C" {
-/*****************************************************************
-InnoDB uses this function to compare two data fields for which the data type
-is such that we must use MySQL code to compare them. NOTE that the prototype
-of this function is in rem0cmp.c in InnoDB source code! If you change this
-function, remember to update the prototype there! */
-
-int
-innobase_mysql_cmp(
-/*===============*/
- /* out: 1, 0, -1, if a is greater,
- equal, less than b, respectively */
- int mysql_type, /* in: MySQL type */
- uint charset_number, /* in: number of the charset */
- unsigned char* a, /* in: data field */
- unsigned int a_length, /* in: data field length,
- not UNIV_SQL_NULL */
- unsigned char* b, /* in: data field */
- unsigned int b_length) /* in: data field length,
- not UNIV_SQL_NULL */
-{
- CHARSET_INFO* charset;
- enum_field_types mysql_tp;
- int ret;
-
- DBUG_ASSERT(a_length != UNIV_SQL_NULL);
- DBUG_ASSERT(b_length != UNIV_SQL_NULL);
-
- mysql_tp = (enum_field_types) mysql_type;
-
- switch (mysql_tp) {
-
- case MYSQL_TYPE_BIT:
- case MYSQL_TYPE_STRING:
- case MYSQL_TYPE_VAR_STRING:
- case FIELD_TYPE_TINY_BLOB:
- case FIELD_TYPE_MEDIUM_BLOB:
- case FIELD_TYPE_BLOB:
- case FIELD_TYPE_LONG_BLOB:
- case MYSQL_TYPE_VARCHAR:
- /* Use the charset number to pick the right charset struct for
- the comparison. Since the MySQL function get_charset may be
- slow before Bar removes the mutex operation there, we first
- look at 2 common charsets directly. */
-
- if (charset_number == default_charset_info->number) {
- charset = default_charset_info;
- } else if (charset_number == my_charset_latin1.number) {
- charset = &my_charset_latin1;
- } else {
- charset = get_charset(charset_number, MYF(MY_WME));
-
- if (charset == NULL) {
- sql_print_error("InnoDB needs charset %lu for doing "
- "a comparison, but MySQL cannot "
- "find that charset.",
- (ulong) charset_number);
- ut_a(0);
- }
- }
-
- /* Starting from 4.1.3, we use strnncollsp() in comparisons of
- non-latin1_swedish_ci strings. NOTE that the collation order
- changes then: 'b\0\0...' is ordered BEFORE 'b ...'. Users
- having indexes on such data need to rebuild their tables! */
-
- ret = charset->coll->strnncollsp(charset,
- a, a_length,
- b, b_length, 0);
- if (ret < 0) {
- return(-1);
- } else if (ret > 0) {
- return(1);
- } else {
- return(0);
- }
- default:
- assert(0);
- }
-
- return(0);
-}
-}
-
-/******************************************************************
-Converts a MySQL type to an InnoDB type. Note that this function returns
-the 'mtype' of InnoDB. InnoDB differentiates between MySQL's old <= 4.1
-VARCHAR and the new true VARCHAR in >= 5.0.3 by the 'prtype'. */
-inline
-ulint
-get_innobase_type_from_mysql_type(
-/*==============================*/
- /* out: DATA_BINARY, DATA_VARCHAR, ... */
- ulint* unsigned_flag, /* out: DATA_UNSIGNED if an 'unsigned type';
- at least ENUM and SET, and unsigned integer
- types are 'unsigned types' */
- Field* field) /* in: MySQL field */
-{
- /* The following asserts try to check that the MySQL type code fits in
- 8 bits: this is used in ibuf and also when DATA_NOT_NULL is ORed to
- the type */
-
- DBUG_ASSERT((ulint)FIELD_TYPE_STRING < 256);
- DBUG_ASSERT((ulint)FIELD_TYPE_VAR_STRING < 256);
- DBUG_ASSERT((ulint)FIELD_TYPE_DOUBLE < 256);
- DBUG_ASSERT((ulint)FIELD_TYPE_FLOAT < 256);
- DBUG_ASSERT((ulint)FIELD_TYPE_DECIMAL < 256);
-
- if (field->flags & UNSIGNED_FLAG) {
-
- *unsigned_flag = DATA_UNSIGNED;
- } else {
- *unsigned_flag = 0;
- }
-
- if (field->real_type() == FIELD_TYPE_ENUM
- || field->real_type() == FIELD_TYPE_SET) {
-
- /* MySQL has field->type() a string type for these, but the
- data is actually internally stored as an unsigned integer
- code! */
-
- *unsigned_flag = DATA_UNSIGNED; /* MySQL has its own unsigned
- flag set to zero, even though
- internally this is an unsigned
- integer type */
- return(DATA_INT);
- }
-
- switch (field->type()) {
- /* NOTE that we only allow string types in DATA_MYSQL
- and DATA_VARMYSQL */
- case MYSQL_TYPE_VAR_STRING: /* old <= 4.1 VARCHAR */
- case MYSQL_TYPE_VARCHAR: /* new >= 5.0.3 true VARCHAR */
- if (field->binary()) {
- return(DATA_BINARY);
- } else if (strcmp(
- field->charset()->name,
- "latin1_swedish_ci") == 0) {
- return(DATA_VARCHAR);
- } else {
- return(DATA_VARMYSQL);
- }
- case MYSQL_TYPE_BIT:
- case MYSQL_TYPE_STRING: if (field->binary()) {
-
- return(DATA_FIXBINARY);
- } else if (strcmp(
- field->charset()->name,
- "latin1_swedish_ci") == 0) {
- return(DATA_CHAR);
- } else {
- return(DATA_MYSQL);
- }
- case FIELD_TYPE_NEWDECIMAL:
- return(DATA_FIXBINARY);
- case FIELD_TYPE_LONG:
- case FIELD_TYPE_LONGLONG:
- case FIELD_TYPE_TINY:
- case FIELD_TYPE_SHORT:
- case FIELD_TYPE_INT24:
- case FIELD_TYPE_DATE:
- case FIELD_TYPE_DATETIME:
- case FIELD_TYPE_YEAR:
- case FIELD_TYPE_NEWDATE:
- case FIELD_TYPE_TIME:
- case FIELD_TYPE_TIMESTAMP:
- return(DATA_INT);
- case FIELD_TYPE_FLOAT:
- return(DATA_FLOAT);
- case FIELD_TYPE_DOUBLE:
- return(DATA_DOUBLE);
- case FIELD_TYPE_DECIMAL:
- return(DATA_DECIMAL);
- case FIELD_TYPE_GEOMETRY:
- case FIELD_TYPE_TINY_BLOB:
- case FIELD_TYPE_MEDIUM_BLOB:
- case FIELD_TYPE_BLOB:
- case FIELD_TYPE_LONG_BLOB:
- return(DATA_BLOB);
- default:
- assert(0);
- }
-
- return(0);
-}
-
-/***********************************************************************
-Writes an unsigned integer value < 64k to 2 bytes, in the little-endian
-storage format. */
-inline
-void
-innobase_write_to_2_little_endian(
-/*==============================*/
- byte* buf, /* in: where to store */
- ulint val) /* in: value to write, must be < 64k */
-{
- ut_a(val < 256 * 256);
-
- buf[0] = (byte)(val & 0xFF);
- buf[1] = (byte)(val / 256);
-}
-
-/***********************************************************************
-Reads an unsigned integer value < 64k from 2 bytes, in the little-endian
-storage format. */
-inline
-uint
-innobase_read_from_2_little_endian(
-/*===============================*/
- /* out: value */
- const mysql_byte* buf) /* in: from where to read */
-{
- return (uint) ((ulint)(buf[0]) + 256 * ((ulint)(buf[1])));
-}
-
-/***********************************************************************
-Stores a key value for a row to a buffer. */
-
-uint
-ha_innobase::store_key_val_for_row(
-/*===============================*/
- /* out: key value length as stored in buff */
- uint keynr, /* in: key number */
- char* buff, /* in/out: buffer for the key value (in MySQL
- format) */
- uint buff_len,/* in: buffer length */
- const mysql_byte* record)/* in: row in MySQL format */
-{
- KEY* key_info = table->key_info + keynr;
- KEY_PART_INFO* key_part = key_info->key_part;
- KEY_PART_INFO* end = key_part + key_info->key_parts;
- char* buff_start = buff;
- enum_field_types mysql_type;
- Field* field;
- ibool is_null;
-
- DBUG_ENTER("store_key_val_for_row");
-
- /* The format for storing a key field in MySQL is the following:
-
- 1. If the column can be NULL, then in the first byte we put 1 if the
- field value is NULL, 0 otherwise.
-
- 2. If the column is of a BLOB type (it must be a column prefix field
- in this case), then we put the length of the data in the field to the
- next 2 bytes, in the little-endian format. If the field is SQL NULL,
- then these 2 bytes are set to 0. Note that the length of data in the
- field is <= column prefix length.
-
- 3. In a column prefix field, prefix_len next bytes are reserved for
- data. In a normal field the max field length next bytes are reserved
- for data. For a VARCHAR(n) the max field length is n. If the stored
- value is the SQL NULL then these data bytes are set to 0.
-
- 4. We always use a 2 byte length for a true >= 5.0.3 VARCHAR. Note that
- in the MySQL row format, the length is stored in 1 or 2 bytes,
- depending on the maximum allowed length. But in the MySQL key value
- format, the length always takes 2 bytes.
-
- We have to zero-fill the buffer so that MySQL is able to use a
- simple memcmp to compare two key values to determine if they are
- equal. MySQL does this to compare contents of two 'ref' values. */
-
- bzero(buff, buff_len);
-
- for (; key_part != end; key_part++) {
- is_null = FALSE;
-
- if (key_part->null_bit) {
- if (record[key_part->null_offset]
- & key_part->null_bit) {
- *buff = 1;
- is_null = TRUE;
- } else {
- *buff = 0;
- }
- buff++;
- }
-
- field = key_part->field;
- mysql_type = field->type();
-
- if (mysql_type == MYSQL_TYPE_VARCHAR) {
- /* >= 5.0.3 true VARCHAR */
- ulint lenlen;
- ulint len;
- byte* data;
- ulint key_len;
- ulint true_len;
- CHARSET_INFO* cs;
- int error=0;
-
- key_len = key_part->length;
-
- if (is_null) {
- buff += key_len + 2;
-
- continue;
- }
- cs = field->charset();
-
- lenlen = (ulint)
- (((Field_varstring*)field)->length_bytes);
-
- data = row_mysql_read_true_varchar(&len,
- (byte*) (record
- + (ulint)get_field_offset(table, field)),
- lenlen);
-
- true_len = len;
-
- /* For multi byte character sets we need to calculate
- the true length of the key */
-
- if (len > 0 && cs->mbmaxlen > 1) {
- true_len = (ulint) cs->cset->well_formed_len(cs,
- (const char *) data,
- (const char *) data + len,
- (uint) (key_len /
- cs->mbmaxlen),
- &error);
- }
-
- /* In a column prefix index, we may need to truncate
- the stored value: */
-
- if (true_len > key_len) {
- true_len = key_len;
- }
-
- /* The length in a key value is always stored in 2
- bytes */
-
- row_mysql_store_true_var_len((byte*)buff, true_len, 2);
- buff += 2;
-
- memcpy(buff, data, true_len);
-
- /* Note that we always reserve the maximum possible
- length of the true VARCHAR in the key value, though
- only len first bytes after the 2 length bytes contain
- actual data. The rest of the space was reset to zero
- in the bzero() call above. */
-
- buff += key_len;
-
- } else if (mysql_type == FIELD_TYPE_TINY_BLOB
- || mysql_type == FIELD_TYPE_MEDIUM_BLOB
- || mysql_type == FIELD_TYPE_BLOB
- || mysql_type == FIELD_TYPE_LONG_BLOB) {
-
- CHARSET_INFO* cs;
- ulint key_len;
- ulint true_len;
- int error=0;
- ulint blob_len;
- byte* blob_data;
-
- ut_a(key_part->key_part_flag & HA_PART_KEY_SEG);
-
- key_len = key_part->length;
-
- if (is_null) {
- buff += key_len + 2;
-
- continue;
- }
-
- cs = field->charset();
-
- blob_data = row_mysql_read_blob_ref(&blob_len,
- (byte*) (record
- + (ulint)get_field_offset(table, field)),
- (ulint) field->pack_length());
-
- true_len = blob_len;
-
- ut_a(get_field_offset(table, field)
- == key_part->offset);
-
- /* For multi byte character sets we need to calculate
- the true length of the key */
-
- if (blob_len > 0 && cs->mbmaxlen > 1) {
- true_len = (ulint) cs->cset->well_formed_len(cs,
- (const char *) blob_data,
- (const char *) blob_data
- + blob_len,
- (uint) (key_len /
- cs->mbmaxlen),
- &error);
- }
-
- /* All indexes on BLOB and TEXT are column prefix
- indexes, and we may need to truncate the data to be
- stored in the key value: */
-
- if (true_len > key_len) {
- true_len = key_len;
- }
-
- /* MySQL reserves 2 bytes for the length and the
- storage of the number is little-endian */
-
- innobase_write_to_2_little_endian(
- (byte*)buff, true_len);
- buff += 2;
-
- memcpy(buff, blob_data, true_len);
-
- /* Note that we always reserve the maximum possible
- length of the BLOB prefix in the key value. */
-
- buff += key_len;
- } else {
- /* Here we handle all other data types except the
- true VARCHAR, BLOB and TEXT. Note that the column
- value we store may be also in a column prefix
- index. */
-
- CHARSET_INFO* cs;
- ulint true_len;
- ulint key_len;
- const mysql_byte* src_start;
- int error=0;
- enum_field_types real_type;
-
- key_len = key_part->length;
-
- if (is_null) {
- buff += key_len;
-
- continue;
- }
-
- src_start = record + key_part->offset;
- real_type = field->real_type();
- true_len = key_len;
-
- /* Character set for the field is defined only
- to fields whose type is string and real field
- type is not enum or set. For these fields check
- if character set is multi byte. */
-
- if (real_type != FIELD_TYPE_ENUM
- && real_type != FIELD_TYPE_SET
- && ( mysql_type == MYSQL_TYPE_VAR_STRING
- || mysql_type == MYSQL_TYPE_STRING)) {
-
- cs = field->charset();
-
- /* For multi byte character sets we need to
- calculate the true length of the key */
-
- if (key_len > 0 && cs->mbmaxlen > 1) {
-
- true_len = (ulint)
- cs->cset->well_formed_len(cs,
- (const char *)src_start,
- (const char *)src_start
- + key_len,
- (uint) (key_len /
- cs->mbmaxlen),
- &error);
- }
- }
-
- memcpy(buff, src_start, true_len);
- buff += true_len;
-
- /* Pad the unused space with spaces. Note that no
- padding is ever needed for UCS-2 because in MySQL,
- all UCS2 characters are 2 bytes, as MySQL does not
- support surrogate pairs, which are needed to represent
- characters in the range U+10000 to U+10FFFF. */
-
- if (true_len < key_len) {
- ulint pad_len = key_len - true_len;
- memset(buff, ' ', pad_len);
- buff += pad_len;
- }
- }
- }
-
- ut_a(buff <= buff_start + buff_len);
-
- DBUG_RETURN((uint)(buff - buff_start));
-}
-
-/******************************************************************
-Builds a 'template' to the prebuilt struct. The template is used in fast
-retrieval of just those column values MySQL needs in its processing. */
-static
-void
-build_template(
-/*===========*/
- row_prebuilt_t* prebuilt, /* in: prebuilt struct */
- THD* thd, /* in: current user thread, used
- only if templ_type is
- ROW_MYSQL_REC_FIELDS */
- TABLE* table, /* in: MySQL table */
- ulint templ_type) /* in: ROW_MYSQL_WHOLE_ROW or
- ROW_MYSQL_REC_FIELDS */
-{
- dict_index_t* index;
- dict_index_t* clust_index;
- mysql_row_templ_t* templ;
- Field* field;
- ulint n_fields;
- ulint n_requested_fields = 0;
- ibool fetch_all_in_key = FALSE;
- ibool fetch_primary_key_cols = FALSE;
- ulint i;
- /* byte offset of the end of last requested column */
- ulint mysql_prefix_len = 0;
-
- if (prebuilt->select_lock_type == LOCK_X) {
- /* We always retrieve the whole clustered index record if we
- use exclusive row level locks, for example, if the read is
- done in an UPDATE statement. */
-
- templ_type = ROW_MYSQL_WHOLE_ROW;
- }
-
- if (templ_type == ROW_MYSQL_REC_FIELDS) {
- if (prebuilt->hint_need_to_fetch_extra_cols
- == ROW_RETRIEVE_ALL_COLS) {
-
- /* We know we must at least fetch all columns in the key, or
- all columns in the table */
-
- if (prebuilt->read_just_key) {
- /* MySQL has instructed us that it is enough to
- fetch the columns in the key; looks like MySQL
- can set this flag also when there is only a
- prefix of the column in the key: in that case we
- retrieve the whole column from the clustered
- index */
-
- fetch_all_in_key = TRUE;
- } else {
- templ_type = ROW_MYSQL_WHOLE_ROW;
- }
- } else if (prebuilt->hint_need_to_fetch_extra_cols
- == ROW_RETRIEVE_PRIMARY_KEY) {
- /* We must at least fetch all primary key cols. Note that if
- the clustered index was internally generated by InnoDB on the
- row id (no primary key was defined), then
- row_search_for_mysql() will always retrieve the row id to a
- special buffer in the prebuilt struct. */
-
- fetch_primary_key_cols = TRUE;
- }
- }
-
- clust_index = dict_table_get_first_index_noninline(prebuilt->table);
-
- if (templ_type == ROW_MYSQL_REC_FIELDS) {
- index = prebuilt->index;
- } else {
- index = clust_index;
- }
-
- if (index == clust_index) {
- prebuilt->need_to_access_clustered = TRUE;
- } else {
- prebuilt->need_to_access_clustered = FALSE;
- /* Below we check column by column if we need to access
- the clustered index */
- }
-
- n_fields = (ulint)table->s->fields; /* number of columns */
-
- if (!prebuilt->mysql_template) {
- prebuilt->mysql_template = (mysql_row_templ_t*)
- mem_alloc_noninline(
- n_fields * sizeof(mysql_row_templ_t));
- }
-
- prebuilt->template_type = templ_type;
- prebuilt->null_bitmap_len = table->s->null_bytes;
-
- prebuilt->templ_contains_blob = FALSE;
-
- /* Note that in InnoDB, i is the column number. MySQL calls columns
- 'fields'. */
- for (i = 0; i < n_fields; i++) {
- templ = prebuilt->mysql_template + n_requested_fields;
- field = table->field[i];
-
- if (UNIV_LIKELY(templ_type == ROW_MYSQL_REC_FIELDS)) {
- /* Decide which columns we should fetch
- and which we can skip. */
- register const ibool index_contains_field =
- dict_index_contains_col_or_prefix(index, i);
-
- if (!index_contains_field && prebuilt->read_just_key) {
- /* If this is a 'key read', we do not need
- columns that are not in the key */
-
- goto skip_field;
- }
-
- if (index_contains_field && fetch_all_in_key) {
- /* This field is needed in the query */
-
- goto include_field;
- }
-
- if (thd->query_id == field->query_id) {
- /* This field is needed in the query */
-
- goto include_field;
- }
-
- if (fetch_primary_key_cols
- && dict_table_col_in_clustered_key(index->table,
- i)) {
- /* This field is needed in the query */
-
- goto include_field;
- }
-
- /* This field is not needed in the query, skip it */
-
- goto skip_field;
- }
-include_field:
- n_requested_fields++;
-
- templ->col_no = i;
-
- if (index == clust_index) {
- templ->rec_field_no = (index->table->cols + i)
- ->clust_pos;
- } else {
- templ->rec_field_no = dict_index_get_nth_col_pos(
- index, i);
- }
-
- if (templ->rec_field_no == ULINT_UNDEFINED) {
- prebuilt->need_to_access_clustered = TRUE;
- }
-
- if (field->null_ptr) {
- templ->mysql_null_byte_offset =
- (ulint) ((char*) field->null_ptr
- - (char*) table->record[0]);
-
- templ->mysql_null_bit_mask = (ulint) field->null_bit;
- } else {
- templ->mysql_null_bit_mask = 0;
- }
-
- templ->mysql_col_offset = (ulint)
- get_field_offset(table, field);
-
- templ->mysql_col_len = (ulint) field->pack_length();
- if (mysql_prefix_len < templ->mysql_col_offset
- + templ->mysql_col_len) {
- mysql_prefix_len = templ->mysql_col_offset
- + templ->mysql_col_len;
- }
- templ->type = index->table->cols[i].type.mtype;
- templ->mysql_type = (ulint)field->type();
-
- if (templ->mysql_type == DATA_MYSQL_TRUE_VARCHAR) {
- templ->mysql_length_bytes = (ulint)
- (((Field_varstring*)field)->length_bytes);
- }
-
- templ->charset = dtype_get_charset_coll_noninline(
- index->table->cols[i].type.prtype);
- templ->mbminlen = index->table->cols[i].type.mbminlen;
- templ->mbmaxlen = index->table->cols[i].type.mbmaxlen;
- templ->is_unsigned = index->table->cols[i].type.prtype
- & DATA_UNSIGNED;
- if (templ->type == DATA_BLOB) {
- prebuilt->templ_contains_blob = TRUE;
- }
-skip_field:
- ;
- }
-
- prebuilt->n_template = n_requested_fields;
- prebuilt->mysql_prefix_len = mysql_prefix_len;
-
- if (index != clust_index && prebuilt->need_to_access_clustered) {
- /* Change rec_field_no's to correspond to the clustered index
- record */
- for (i = 0; i < n_requested_fields; i++) {
- templ = prebuilt->mysql_template + i;
-
- templ->rec_field_no =
- (index->table->cols + templ->col_no)->clust_pos;
- }
- }
-}
-
-/************************************************************************
-Stores a row in an InnoDB database, to the table specified in this
-handle. */
-
-int
-ha_innobase::write_row(
-/*===================*/
- /* out: error code */
- mysql_byte* record) /* in: a row in MySQL format */
-{
- row_prebuilt_t* prebuilt = (row_prebuilt_t*)innobase_prebuilt;
- int error;
- longlong auto_inc;
- longlong dummy;
- ibool auto_inc_used= FALSE;
-
- DBUG_ENTER("ha_innobase::write_row");
-
- if (prebuilt->trx !=
- (trx_t*) current_thd->ha_data[innobase_hton.slot]) {
- sql_print_error("The transaction object for the table handle is at "
- "%p, but for the current thread it is at %p",
- prebuilt->trx,
- (trx_t*) current_thd->ha_data[innobase_hton.slot]);
-
- fputs("InnoDB: Dump of 200 bytes around prebuilt: ", stderr);
- ut_print_buf(stderr, ((const byte*)prebuilt) - 100, 200);
- fputs("\n"
- "InnoDB: Dump of 200 bytes around transaction.all: ",
- stderr);
- ut_print_buf(stderr,
- ((byte*)(&(current_thd->ha_data[innobase_hton.slot]))) - 100,
- 200);
- putc('\n', stderr);
- ut_error;
- }
-
- statistic_increment(current_thd->status_var.ha_write_count,
- &LOCK_status);
-
- if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_INSERT)
- table->timestamp_field->set_time();
-
- if ((user_thd->lex->sql_command == SQLCOM_ALTER_TABLE
- || user_thd->lex->sql_command == SQLCOM_OPTIMIZE
- || user_thd->lex->sql_command == SQLCOM_CREATE_INDEX
- || user_thd->lex->sql_command == SQLCOM_DROP_INDEX)
- && num_write_row >= 10000) {
- /* ALTER TABLE is COMMITted at every 10000 copied rows.
- The IX table lock for the original table has to be re-issued.
- As this method will be called on a temporary table where the
- contents of the original table is being copied to, it is
- a bit tricky to determine the source table. The cursor
- position in the source table need not be adjusted after the
- intermediate COMMIT, since writes by other transactions are
- being blocked by a MySQL table lock TL_WRITE_ALLOW_READ. */
-
- dict_table_t* src_table;
- ulint mode;
-
- num_write_row = 0;
-
- /* Commit the transaction. This will release the table
- locks, so they have to be acquired again. */
-
- /* Altering an InnoDB table */
- /* Get the source table. */
- src_table = lock_get_src_table(
- prebuilt->trx, prebuilt->table, &mode);
- if (!src_table) {
-no_commit:
- /* Unknown situation: do not commit */
- /*
- ut_print_timestamp(stderr);
- fprintf(stderr,
- " InnoDB error: ALTER TABLE is holding lock"
- " on %lu tables!\n",
- prebuilt->trx->mysql_n_tables_locked);
- */
- ;
- } else if (src_table == prebuilt->table) {
- /* Source table is not in InnoDB format:
- no need to re-acquire locks on it. */
-
- /* Altering to InnoDB format */
- innobase_commit(user_thd, 1);
- /* Note that this transaction is still active. */
- prebuilt->trx->active_trans = 1;
- /* We will need an IX lock on the destination table. */
- prebuilt->sql_stat_start = TRUE;
- } else {
- /* Ensure that there are no other table locks than
- LOCK_IX and LOCK_AUTO_INC on the destination table. */
-
- if (!lock_is_table_exclusive(prebuilt->table,
- prebuilt->trx)) {
- goto no_commit;
- }
-
- /* Commit the transaction. This will release the table
- locks, so they have to be acquired again. */
- innobase_commit(user_thd, 1);
- /* Note that this transaction is still active. */
- prebuilt->trx->active_trans = 1;
- /* Re-acquire the table lock on the source table. */
- row_lock_table_for_mysql(prebuilt, src_table, mode);
- /* We will need an IX lock on the destination table. */
- prebuilt->sql_stat_start = TRUE;
- }
- }
-
- num_write_row++;
-
- if (last_query_id != user_thd->query_id) {
- prebuilt->sql_stat_start = TRUE;
- last_query_id = user_thd->query_id;
-
- innobase_release_stat_resources(prebuilt->trx);
- }
-
- if (table->next_number_field && record == table->record[0]) {
- /* This is the case where the table has an
- auto-increment column */
-
- /* Initialize the auto-inc counter if it has not been
- initialized yet */
-
- if (0 == dict_table_autoinc_peek(prebuilt->table)) {
-
- /* This call initializes the counter */
- error = innobase_read_and_init_auto_inc(&dummy);
-
- if (error) {
- /* Deadlock or lock wait timeout */
-
- goto func_exit;
- }
-
- /* We have to set sql_stat_start to TRUE because
- the above call probably has called a select, and
- has reset that flag; row_insert_for_mysql has to
- know to set the IX intention lock on the table,
- something it only does at the start of each
- statement */
-
- prebuilt->sql_stat_start = TRUE;
- }
-
- /* We have to use the transactional lock mechanism on the
- auto-inc counter of the table to ensure that replication and
- roll-forward of the binlog exactly imitates also the given
- auto-inc values. The lock is released at each SQL statement's
- end. This lock also prevents a race where two threads would
- call ::get_auto_increment() simultaneously. */
-
- error = row_lock_table_autoinc_for_mysql(prebuilt);
-
- if (error != DB_SUCCESS) {
- /* Deadlock or lock wait timeout */
-
- error = convert_error_code_to_mysql(error, user_thd);
-
- goto func_exit;
- }
-
- /* We must use the handler code to update the auto-increment
- value to be sure that we increment it correctly. */
-
- if ((error= update_auto_increment()))
- goto func_exit;
- auto_inc_used = 1;
-
- }
-
- if (prebuilt->mysql_template == NULL
- || prebuilt->template_type != ROW_MYSQL_WHOLE_ROW) {
- /* Build the template used in converting quickly between
- the two database formats */
-
- build_template(prebuilt, NULL, table, ROW_MYSQL_WHOLE_ROW);
- }
-
- innodb_srv_conc_enter_innodb(prebuilt->trx);
-
- error = row_insert_for_mysql((byte*) record, prebuilt);
-
- if (error == DB_SUCCESS && auto_inc_used) {
-
- /* Fetch the value that was set in the autoincrement field */
-
- auto_inc = table->next_number_field->val_int();
-
- if (auto_inc != 0) {
- /* This call will update the counter according to the
- value that was inserted in the table */
-
- dict_table_autoinc_update(prebuilt->table, auto_inc);
- }
- }
-
- /* A REPLACE command and LOAD DATA INFILE REPLACE handle a duplicate
- key error themselves, and we must update the autoinc counter if we are
- performing those statements. */
-
- if (error == DB_DUPLICATE_KEY && auto_inc_used
- && (user_thd->lex->sql_command == SQLCOM_REPLACE
- || user_thd->lex->sql_command == SQLCOM_REPLACE_SELECT
- || (user_thd->lex->sql_command == SQLCOM_LOAD
- && user_thd->lex->duplicates == DUP_REPLACE))) {
-
- auto_inc = table->next_number_field->val_int();
-
- if (auto_inc != 0) {
- dict_table_autoinc_update(prebuilt->table, auto_inc);
- }
- }
-
- innodb_srv_conc_exit_innodb(prebuilt->trx);
-
- error = convert_error_code_to_mysql(error, user_thd);
-
- /* Tell InnoDB server that there might be work for
- utility threads: */
-func_exit:
- innobase_active_small();
-
- DBUG_RETURN(error);
-}
-
-/**************************************************************************
-Checks which fields have changed in a row and stores information
-of them to an update vector. */
-static
-int
-calc_row_difference(
-/*================*/
- /* out: error number or 0 */
- upd_t* uvect, /* in/out: update vector */
- mysql_byte* old_row, /* in: old row in MySQL format */
- mysql_byte* new_row, /* in: new row in MySQL format */
- struct st_table* table, /* in: table in MySQL data
- dictionary */
- mysql_byte* upd_buff, /* in: buffer to use */
- ulint buff_len, /* in: buffer length */
- row_prebuilt_t* prebuilt, /* in: InnoDB prebuilt struct */
- THD* thd) /* in: user thread */
-{
- mysql_byte* original_upd_buff = upd_buff;
- Field* field;
- enum_field_types field_mysql_type;
- uint n_fields;
- ulint o_len;
- ulint n_len;
- ulint col_pack_len;
- byte* new_mysql_row_col;
- byte* o_ptr;
- byte* n_ptr;
- byte* buf;
- upd_field_t* ufield;
- ulint col_type;
- ulint n_changed = 0;
- dfield_t dfield;
- uint i;
-
- n_fields = table->s->fields;
-
- /* We use upd_buff to convert changed fields */
- buf = (byte*) upd_buff;
-
- for (i = 0; i < n_fields; i++) {
- field = table->field[i];
-
- /* if (thd->query_id != field->query_id) { */
- /* TODO: check that these fields cannot have
- changed! */
-
- /* goto skip_field;
- }*/
-
- o_ptr = (byte*) old_row + get_field_offset(table, field);
- n_ptr = (byte*) new_row + get_field_offset(table, field);
-
- /* Use new_mysql_row_col and col_pack_len save the values */
-
- new_mysql_row_col = n_ptr;
- col_pack_len = field->pack_length();
-
- o_len = col_pack_len;
- n_len = col_pack_len;
-
- /* We use o_ptr and n_ptr to dig up the actual data for
- comparison. */
-
- field_mysql_type = field->type();
-
- col_type = prebuilt->table->cols[i].type.mtype;
-
- switch (col_type) {
-
- case DATA_BLOB:
- o_ptr = row_mysql_read_blob_ref(&o_len, o_ptr, o_len);
- n_ptr = row_mysql_read_blob_ref(&n_len, n_ptr, n_len);
-
- break;
-
- case DATA_VARCHAR:
- case DATA_BINARY:
- case DATA_VARMYSQL:
- if (field_mysql_type == MYSQL_TYPE_VARCHAR) {
- /* This is a >= 5.0.3 type true VARCHAR where
- the real payload data length is stored in
- 1 or 2 bytes */
-
- o_ptr = row_mysql_read_true_varchar(
- &o_len, o_ptr,
- (ulint)
- (((Field_varstring*)field)->length_bytes));
-
- n_ptr = row_mysql_read_true_varchar(
- &n_len, n_ptr,
- (ulint)
- (((Field_varstring*)field)->length_bytes));
- }
-
- break;
- default:
- ;
- }
-
- if (field->null_ptr) {
- if (field_in_record_is_null(table, field,
- (char*) old_row)) {
- o_len = UNIV_SQL_NULL;
- }
-
- if (field_in_record_is_null(table, field,
- (char*) new_row)) {
- n_len = UNIV_SQL_NULL;
- }
- }
-
- if (o_len != n_len || (o_len != UNIV_SQL_NULL &&
- 0 != memcmp(o_ptr, n_ptr, o_len))) {
- /* The field has changed */
-
- ufield = uvect->fields + n_changed;
-
- /* Let us use a dummy dfield to make the conversion
- from the MySQL column format to the InnoDB format */
-
- dfield.type = (prebuilt->table->cols + i)->type;
-
- if (n_len != UNIV_SQL_NULL) {
- buf = row_mysql_store_col_in_innobase_format(
- &dfield,
- (byte*)buf,
- TRUE,
- new_mysql_row_col,
- col_pack_len,
- prebuilt->table->comp);
- ufield->new_val.data = dfield.data;
- ufield->new_val.len = dfield.len;
- } else {
- ufield->new_val.data = NULL;
- ufield->new_val.len = UNIV_SQL_NULL;
- }
-
- ufield->exp = NULL;
- ufield->field_no = prebuilt->table->cols[i].clust_pos;
- n_changed++;
- }
- }
-
- uvect->n_fields = n_changed;
- uvect->info_bits = 0;
-
- ut_a(buf <= (byte*)original_upd_buff + buff_len);
-
- return(0);
-}
-
-/**************************************************************************
-Updates a row given as a parameter to a new value. Note that we are given
-whole rows, not just the fields which are updated: this incurs some
-overhead for CPU when we check which fields are actually updated.
-TODO: currently InnoDB does not prevent the 'Halloween problem':
-in a searched update a single row can get updated several times
-if its index columns are updated! */
-
-int
-ha_innobase::update_row(
-/*====================*/
- /* out: error number or 0 */
- const mysql_byte* old_row,/* in: old row in MySQL format */
- mysql_byte* new_row)/* in: new row in MySQL format */
-{
- row_prebuilt_t* prebuilt = (row_prebuilt_t*) innobase_prebuilt;
- upd_t* uvect;
- int error = 0;
-
- DBUG_ENTER("ha_innobase::update_row");
-
- ut_ad(prebuilt->trx ==
- (trx_t*) current_thd->ha_data[innobase_hton.slot]);
-
- if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_UPDATE)
- table->timestamp_field->set_time();
-
- if (last_query_id != user_thd->query_id) {
- prebuilt->sql_stat_start = TRUE;
- last_query_id = user_thd->query_id;
-
- innobase_release_stat_resources(prebuilt->trx);
- }
-
- if (prebuilt->upd_node) {
- uvect = prebuilt->upd_node->update;
- } else {
- uvect = row_get_prebuilt_update_vector(prebuilt);
- }
-
- /* Build an update vector from the modified fields in the rows
- (uses upd_buff of the handle) */
-
- calc_row_difference(uvect, (mysql_byte*) old_row, new_row, table,
- upd_buff, (ulint)upd_and_key_val_buff_len,
- prebuilt, user_thd);
-
- /* This is not a delete */
- prebuilt->upd_node->is_delete = FALSE;
-
- assert(prebuilt->template_type == ROW_MYSQL_WHOLE_ROW);
-
- innodb_srv_conc_enter_innodb(prebuilt->trx);
-
- error = row_update_for_mysql((byte*) old_row, prebuilt);
-
- /* We need to do some special AUTOINC handling for the following case:
-
- INSERT INTO t (c1,c2) VALUES(x,y) ON DUPLICATE KEY UPDATE ...
-
- We need to use the AUTOINC counter that was actually used by
- MySQL in the UPDATE statement, which can be different from the
- value used in the INSERT statement.*/
- if (error == DB_SUCCESS
- && table->next_number_field && new_row == table->record[0]
- && user_thd->lex->sql_command == SQLCOM_INSERT
- && user_thd->lex->duplicates == DUP_UPDATE) {
-
- longlong auto_inc;
-
- auto_inc = table->next_number_field->val_int();
-
- if (auto_inc != 0) {
- dict_table_autoinc_update(prebuilt->table, auto_inc);
- }
- }
-
- innodb_srv_conc_exit_innodb(prebuilt->trx);
-
- error = convert_error_code_to_mysql(error, user_thd);
-
- /* Tell InnoDB server that there might be work for
- utility threads: */
-
- innobase_active_small();
-
- DBUG_RETURN(error);
-}
-
-/**************************************************************************
-Deletes a row given as the parameter. */
-
-int
-ha_innobase::delete_row(
-/*====================*/
- /* out: error number or 0 */
- const mysql_byte* record) /* in: a row in MySQL format */
-{
- row_prebuilt_t* prebuilt = (row_prebuilt_t*) innobase_prebuilt;
- int error = 0;
-
- DBUG_ENTER("ha_innobase::delete_row");
-
- ut_ad(prebuilt->trx ==
- (trx_t*) current_thd->ha_data[innobase_hton.slot]);
-
- if (last_query_id != user_thd->query_id) {
- prebuilt->sql_stat_start = TRUE;
- last_query_id = user_thd->query_id;
-
- innobase_release_stat_resources(prebuilt->trx);
- }
-
- if (!prebuilt->upd_node) {
- row_get_prebuilt_update_vector(prebuilt);
- }
-
- /* This is a delete */
-
- prebuilt->upd_node->is_delete = TRUE;
-
- innodb_srv_conc_enter_innodb(prebuilt->trx);
-
- error = row_update_for_mysql((byte*) record, prebuilt);
-
- innodb_srv_conc_exit_innodb(prebuilt->trx);
-
- error = convert_error_code_to_mysql(error, user_thd);
-
- /* Tell the InnoDB server that there might be work for
- utility threads: */
-
- innobase_active_small();
-
- DBUG_RETURN(error);
-}
-
-/**************************************************************************
-Removes a new lock set on a row. This method does nothing unless the
-option innodb_locks_unsafe_for_binlog is set.*/
-
-void
-ha_innobase::unlock_row(void)
-/*=========================*/
-{
- row_prebuilt_t* prebuilt = (row_prebuilt_t*) innobase_prebuilt;
-
- DBUG_ENTER("ha_innobase::unlock_row");
-
- if (last_query_id != user_thd->query_id) {
- ut_print_timestamp(stderr);
- sql_print_error("last_query_id is %lu != user_thd_query_id is "
- "%lu", (ulong) last_query_id,
- (ulong) user_thd->query_id);
- mem_analyze_corruption((byte *) prebuilt->trx);
- ut_error;
- }
-
- /* Consistent read does not take any locks, thus there is
- nothing to unlock. */
-
- if (prebuilt->select_lock_type == LOCK_NONE) {
- DBUG_VOID_RETURN;
- }
-
- if (srv_locks_unsafe_for_binlog) {
- row_unlock_for_mysql(prebuilt, FALSE);
- }
-
- DBUG_VOID_RETURN;
-
-}
-
-/**********************************************************************
-Initializes a handle to use an index. */
-
-int
-ha_innobase::index_init(
-/*====================*/
- /* out: 0 or error number */
- uint keynr) /* in: key (index) number */
-{
- int error = 0;
- DBUG_ENTER("index_init");
-
- error = change_active_index(keynr);
-
- DBUG_RETURN(error);
-}
-
-/**********************************************************************
-Currently does nothing. */
-
-int
-ha_innobase::index_end(void)
-/*========================*/
-{
- int error = 0;
- DBUG_ENTER("index_end");
- active_index=MAX_KEY;
- DBUG_RETURN(error);
-}
-
-/*************************************************************************
-Converts a search mode flag understood by MySQL to a flag understood
-by InnoDB. */
-inline
-ulint
-convert_search_mode_to_innobase(
-/*============================*/
- enum ha_rkey_function find_flag)
-{
- switch (find_flag) {
- case HA_READ_KEY_EXACT: return(PAGE_CUR_GE);
- /* the above does not require the index to be UNIQUE */
- case HA_READ_KEY_OR_NEXT: return(PAGE_CUR_GE);
- case HA_READ_KEY_OR_PREV: return(PAGE_CUR_LE);
- case HA_READ_AFTER_KEY: return(PAGE_CUR_G);
- case HA_READ_BEFORE_KEY: return(PAGE_CUR_L);
- case HA_READ_PREFIX: return(PAGE_CUR_GE);
- case HA_READ_PREFIX_LAST: return(PAGE_CUR_LE);
- case HA_READ_PREFIX_LAST_OR_PREV:return(PAGE_CUR_LE);
- /* In MySQL-4.0 HA_READ_PREFIX and HA_READ_PREFIX_LAST always
- pass a complete-field prefix of a key value as the search
- tuple. I.e., it is not allowed that the last field would
- just contain n first bytes of the full field value.
- MySQL uses a 'padding' trick to convert LIKE 'abc%'
- type queries so that it can use as a search tuple
- a complete-field-prefix of a key value. Thus, the InnoDB
- search mode PAGE_CUR_LE_OR_EXTENDS is never used.
- TODO: when/if MySQL starts to use also partial-field
- prefixes, we have to deal with stripping of spaces
- and comparison of non-latin1 char type fields in
- innobase_mysql_cmp() to get PAGE_CUR_LE_OR_EXTENDS to
- work correctly. */
- case HA_READ_MBR_CONTAIN:
- case HA_READ_MBR_INTERSECT:
- case HA_READ_MBR_WITHIN:
- case HA_READ_MBR_DISJOINT:
- case HA_READ_MBR_EQUAL:
- my_error(ER_TABLE_CANT_HANDLE_SPKEYS, MYF(0));
- return(PAGE_CUR_UNSUPP);
- /* do not use "default:" in order to produce a gcc warning:
- enumeration value '...' not handled in switch
- (if -Wswitch or -Wall is used)
- */
- }
-
- my_error(ER_CHECK_NOT_IMPLEMENTED, MYF(0), "this functionality");
-
- return(PAGE_CUR_UNSUPP);
-}
-
-/*
- BACKGROUND INFO: HOW A SELECT SQL QUERY IS EXECUTED
- ---------------------------------------------------
-The following does not cover all the details, but explains how we determine
-the start of a new SQL statement, and what is associated with it.
-
-For each table in the database the MySQL interpreter may have several
-table handle instances in use, also in a single SQL query. For each table
-handle instance there is an InnoDB 'prebuilt' struct which contains most
-of the InnoDB data associated with this table handle instance.
-
- A) if the user has not explicitly set any MySQL table level locks:
-
- 1) MySQL calls ::external_lock to set an 'intention' table level lock on
-the table of the handle instance. There we set
-prebuilt->sql_stat_start = TRUE. The flag sql_stat_start should be set
-true if we are taking this table handle instance to use in a new SQL
-statement issued by the user. We also increment trx->n_mysql_tables_in_use.
-
- 2) If prebuilt->sql_stat_start == TRUE we 'pre-compile' the MySQL search
-instructions to prebuilt->template of the table handle instance in
-::index_read. The template is used to save CPU time in large joins.
-
- 3) In row_search_for_mysql, if prebuilt->sql_stat_start is true, we
-allocate a new consistent read view for the trx if it does not yet have one,
-or in the case of a locking read, set an InnoDB 'intention' table level
-lock on the table.
-
- 4) We do the SELECT. MySQL may repeatedly call ::index_read for the
-same table handle instance, if it is a join.
-
- 5) When the SELECT ends, MySQL removes its intention table level locks
-in ::external_lock. When trx->n_mysql_tables_in_use drops to zero,
- (a) we execute a COMMIT there if the autocommit is on,
- (b) we also release possible 'SQL statement level resources' InnoDB may
-have for this SQL statement. The MySQL interpreter does NOT execute
-autocommit for pure read transactions, though it should. That is why the
-table handler in that case has to execute the COMMIT in ::external_lock.
-
- B) If the user has explicitly set MySQL table level locks, then MySQL
-does NOT call ::external_lock at the start of the statement. To determine
-when we are at the start of a new SQL statement we at the start of
-::index_read also compare the query id to the latest query id where the
-table handle instance was used. If it has changed, we know we are at the
-start of a new SQL statement. Since the query id can theoretically
-overwrap, we use this test only as a secondary way of determining the
-start of a new SQL statement. */
-
-
-/**************************************************************************
-Positions an index cursor to the index specified in the handle. Fetches the
-row if any. */
-
-int
-ha_innobase::index_read(
-/*====================*/
- /* out: 0, HA_ERR_KEY_NOT_FOUND,
- or error number */
- mysql_byte* buf, /* in/out: buffer for the returned
- row */
- const mysql_byte* key_ptr,/* in: key value; if this is NULL
- we position the cursor at the
- start or end of index; this can
- also contain an InnoDB row id, in
- which case key_len is the InnoDB
- row id length; the key value can
- also be a prefix of a full key value,
- and the last column can be a prefix
- of a full column */
- uint key_len,/* in: key value length */
- enum ha_rkey_function find_flag)/* in: search flags from my_base.h */
-{
- row_prebuilt_t* prebuilt = (row_prebuilt_t*) innobase_prebuilt;
- ulint mode;
- dict_index_t* index;
- ulint match_mode = 0;
- int error;
- ulint ret;
-
- DBUG_ENTER("index_read");
-
- ut_ad(prebuilt->trx ==
- (trx_t*) current_thd->ha_data[innobase_hton.slot]);
-
- statistic_increment(current_thd->status_var.ha_read_key_count,
- &LOCK_status);
-
- if (last_query_id != user_thd->query_id) {
- prebuilt->sql_stat_start = TRUE;
- last_query_id = user_thd->query_id;
-
- innobase_release_stat_resources(prebuilt->trx);
- }
-
- index = prebuilt->index;
-
- /* Note that if the index for which the search template is built is not
- necessarily prebuilt->index, but can also be the clustered index */
-
- if (prebuilt->sql_stat_start) {
- build_template(prebuilt, user_thd, table,
- ROW_MYSQL_REC_FIELDS);
- }
-
- if (key_ptr) {
- /* Convert the search key value to InnoDB format into
- prebuilt->search_tuple */
-
- row_sel_convert_mysql_key_to_innobase(prebuilt->search_tuple,
- (byte*) key_val_buff,
- (ulint)upd_and_key_val_buff_len,
- index,
- (byte*) key_ptr,
- (ulint) key_len, prebuilt->trx);
- } else {
- /* We position the cursor to the last or the first entry
- in the index */
-
- dtuple_set_n_fields(prebuilt->search_tuple, 0);
- }
-
- mode = convert_search_mode_to_innobase(find_flag);
-
- match_mode = 0;
-
- if (find_flag == HA_READ_KEY_EXACT) {
- match_mode = ROW_SEL_EXACT;
-
- } else if (find_flag == HA_READ_PREFIX
- || find_flag == HA_READ_PREFIX_LAST) {
- match_mode = ROW_SEL_EXACT_PREFIX;
- }
-
- last_match_mode = (uint) match_mode;
-
- if (mode != PAGE_CUR_UNSUPP) {
-
- innodb_srv_conc_enter_innodb(prebuilt->trx);
-
- ret = row_search_for_mysql((byte*) buf, mode, prebuilt,
- match_mode, 0);
-
- innodb_srv_conc_exit_innodb(prebuilt->trx);
- } else {
-
- ret = DB_UNSUPPORTED;
- }
-
- if (ret == DB_SUCCESS) {
- error = 0;
- table->status = 0;
-
- } else if (ret == DB_RECORD_NOT_FOUND) {
- error = HA_ERR_KEY_NOT_FOUND;
- table->status = STATUS_NOT_FOUND;
-
- } else if (ret == DB_END_OF_INDEX) {
- error = HA_ERR_KEY_NOT_FOUND;
- table->status = STATUS_NOT_FOUND;
- } else {
- error = convert_error_code_to_mysql((int) ret, user_thd);
- table->status = STATUS_NOT_FOUND;
- }
-
- DBUG_RETURN(error);
-}
-
-/***********************************************************************
-The following functions works like index_read, but it find the last
-row with the current key value or prefix. */
-
-int
-ha_innobase::index_read_last(
-/*=========================*/
- /* out: 0, HA_ERR_KEY_NOT_FOUND, or an
- error code */
- mysql_byte* buf, /* out: fetched row */
- const mysql_byte* key_ptr, /* in: key value, or a prefix of a full
- key value */
- uint key_len) /* in: length of the key val or prefix
- in bytes */
-{
- return(index_read(buf, key_ptr, key_len, HA_READ_PREFIX_LAST));
-}
-
-/************************************************************************
-Changes the active index of a handle. */
-
-int
-ha_innobase::change_active_index(
-/*=============================*/
- /* out: 0 or error code */
- uint keynr) /* in: use this index; MAX_KEY means always clustered
- index, even if it was internally generated by
- InnoDB */
-{
- row_prebuilt_t* prebuilt = (row_prebuilt_t*) innobase_prebuilt;
- KEY* key=0;
- statistic_increment(current_thd->status_var.ha_read_key_count,
- &LOCK_status);
- DBUG_ENTER("change_active_index");
-
- ut_ad(user_thd == current_thd);
- ut_ad(prebuilt->trx ==
- (trx_t*) current_thd->ha_data[innobase_hton.slot]);
-
- active_index = keynr;
-
- if (keynr != MAX_KEY && table->s->keys > 0) {
- key = table->key_info + active_index;
-
- prebuilt->index = dict_table_get_index_noninline(
- prebuilt->table,
- key->name);
- } else {
- prebuilt->index = dict_table_get_first_index_noninline(
- prebuilt->table);
- }
-
- if (!prebuilt->index) {
- sql_print_error("Innodb could not find key n:o %u with name %s "
- "from dict cache for table %s",
- keynr, key ? key->name : "NULL",
- prebuilt->table->name);
- DBUG_RETURN(1);
- }
-
- assert(prebuilt->search_tuple != 0);
-
- dtuple_set_n_fields(prebuilt->search_tuple, prebuilt->index->n_fields);
-
- dict_index_copy_types(prebuilt->search_tuple, prebuilt->index,
- prebuilt->index->n_fields);
-
- /* MySQL changes the active index for a handle also during some
- queries, for example SELECT MAX(a), SUM(a) first retrieves the MAX()
- and then calculates the sum. Previously we played safe and used
- the flag ROW_MYSQL_WHOLE_ROW below, but that caused unnecessary
- copying. Starting from MySQL-4.1 we use a more efficient flag here. */
-
- build_template(prebuilt, user_thd, table, ROW_MYSQL_REC_FIELDS);
-
- DBUG_RETURN(0);
-}
-
-/**************************************************************************
-Positions an index cursor to the index specified in keynr. Fetches the
-row if any. */
-/* ??? This is only used to read whole keys ??? */
-
-int
-ha_innobase::index_read_idx(
-/*========================*/
- /* out: error number or 0 */
- mysql_byte* buf, /* in/out: buffer for the returned
- row */
- uint keynr, /* in: use this index */
- const mysql_byte* key, /* in: key value; if this is NULL
- we position the cursor at the
- start or end of index */
- uint key_len, /* in: key value length */
- enum ha_rkey_function find_flag)/* in: search flags from my_base.h */
-{
- if (change_active_index(keynr)) {
-
- return(1);
- }
-
- return(index_read(buf, key, key_len, find_flag));
-}
-
-/***************************************************************************
-Reads the next or previous row from a cursor, which must have previously been
-positioned using index_read. */
-
-int
-ha_innobase::general_fetch(
-/*=======================*/
- /* out: 0, HA_ERR_END_OF_FILE, or error
- number */
- mysql_byte* buf, /* in/out: buffer for next row in MySQL
- format */
- uint direction, /* in: ROW_SEL_NEXT or ROW_SEL_PREV */
- uint match_mode) /* in: 0, ROW_SEL_EXACT, or
- ROW_SEL_EXACT_PREFIX */
-{
- row_prebuilt_t* prebuilt = (row_prebuilt_t*) innobase_prebuilt;
- ulint ret;
- int error = 0;
-
- DBUG_ENTER("general_fetch");
-
- ut_ad(prebuilt->trx ==
- (trx_t*) current_thd->ha_data[innobase_hton.slot]);
-
- innodb_srv_conc_enter_innodb(prebuilt->trx);
-
- ret = row_search_for_mysql((byte*)buf, 0, prebuilt, match_mode,
- direction);
- innodb_srv_conc_exit_innodb(prebuilt->trx);
-
- if (ret == DB_SUCCESS) {
- error = 0;
- table->status = 0;
-
- } else if (ret == DB_RECORD_NOT_FOUND) {
- error = HA_ERR_END_OF_FILE;
- table->status = STATUS_NOT_FOUND;
-
- } else if (ret == DB_END_OF_INDEX) {
- error = HA_ERR_END_OF_FILE;
- table->status = STATUS_NOT_FOUND;
- } else {
- error = convert_error_code_to_mysql((int) ret, user_thd);
- table->status = STATUS_NOT_FOUND;
- }
-
- DBUG_RETURN(error);
-}
-
-/***************************************************************************
-Reads the next row from a cursor, which must have previously been
-positioned using index_read. */
-
-int
-ha_innobase::index_next(
-/*====================*/
- /* out: 0, HA_ERR_END_OF_FILE, or error
- number */
- mysql_byte* buf) /* in/out: buffer for next row in MySQL
- format */
-{
- statistic_increment(current_thd->status_var.ha_read_next_count,
- &LOCK_status);
-
- return(general_fetch(buf, ROW_SEL_NEXT, 0));
-}
-
-/***********************************************************************
-Reads the next row matching to the key value given as the parameter. */
-
-int
-ha_innobase::index_next_same(
-/*=========================*/
- /* out: 0, HA_ERR_END_OF_FILE, or error
- number */
- mysql_byte* buf, /* in/out: buffer for the row */
- const mysql_byte* key, /* in: key value */
- uint keylen) /* in: key value length */
-{
- statistic_increment(current_thd->status_var.ha_read_next_count,
- &LOCK_status);
-
- return(general_fetch(buf, ROW_SEL_NEXT, last_match_mode));
-}
-
-/***************************************************************************
-Reads the previous row from a cursor, which must have previously been
-positioned using index_read. */
-
-int
-ha_innobase::index_prev(
-/*====================*/
- /* out: 0, HA_ERR_END_OF_FILE, or error
- number */
- mysql_byte* buf) /* in/out: buffer for previous row in MySQL
- format */
-{
- statistic_increment(current_thd->status_var.ha_read_prev_count,
- &LOCK_status);
-
- return(general_fetch(buf, ROW_SEL_PREV, 0));
-}
-
-/************************************************************************
-Positions a cursor on the first record in an index and reads the
-corresponding row to buf. */
-
-int
-ha_innobase::index_first(
-/*=====================*/
- /* out: 0, HA_ERR_END_OF_FILE,
- or error code */
- mysql_byte* buf) /* in/out: buffer for the row */
-{
- int error;
-
- DBUG_ENTER("index_first");
- statistic_increment(current_thd->status_var.ha_read_first_count,
- &LOCK_status);
-
- error = index_read(buf, NULL, 0, HA_READ_AFTER_KEY);
-
- /* MySQL does not seem to allow this to return HA_ERR_KEY_NOT_FOUND */
-
- if (error == HA_ERR_KEY_NOT_FOUND) {
- error = HA_ERR_END_OF_FILE;
- }
-
- DBUG_RETURN(error);
-}
-
-/************************************************************************
-Positions a cursor on the last record in an index and reads the
-corresponding row to buf. */
-
-int
-ha_innobase::index_last(
-/*====================*/
- /* out: 0, HA_ERR_END_OF_FILE, or error code */
- mysql_byte* buf) /* in/out: buffer for the row */
-{
- int error;
-
- DBUG_ENTER("index_last");
- statistic_increment(current_thd->status_var.ha_read_last_count,
- &LOCK_status);
-
- error = index_read(buf, NULL, 0, HA_READ_BEFORE_KEY);
-
- /* MySQL does not seem to allow this to return HA_ERR_KEY_NOT_FOUND */
-
- if (error == HA_ERR_KEY_NOT_FOUND) {
- error = HA_ERR_END_OF_FILE;
- }
-
- DBUG_RETURN(error);
-}
-
-/********************************************************************
-Initialize a table scan. */
-
-int
-ha_innobase::rnd_init(
-/*==================*/
- /* out: 0 or error number */
- bool scan) /* in: ???????? */
-{
- int err;
-
- row_prebuilt_t* prebuilt = (row_prebuilt_t*) innobase_prebuilt;
-
- /* Store the active index value so that we can restore the original
- value after a scan */
-
- if (prebuilt->clust_index_was_generated) {
- err = change_active_index(MAX_KEY);
- } else {
- err = change_active_index(primary_key);
- }
-
- start_of_scan = 1;
-
- return(err);
-}
-
-/*********************************************************************
-Ends a table scan. */
-
-int
-ha_innobase::rnd_end(void)
-/*======================*/
- /* out: 0 or error number */
-{
- return(index_end());
-}
-
-/*********************************************************************
-Reads the next row in a table scan (also used to read the FIRST row
-in a table scan). */
-
-int
-ha_innobase::rnd_next(
-/*==================*/
- /* out: 0, HA_ERR_END_OF_FILE, or error number */
- mysql_byte* buf)/* in/out: returns the row in this buffer,
- in MySQL format */
-{
- int error;
-
- DBUG_ENTER("rnd_next");
- statistic_increment(current_thd->status_var.ha_read_rnd_next_count,
- &LOCK_status);
-
- if (start_of_scan) {
- error = index_first(buf);
- if (error == HA_ERR_KEY_NOT_FOUND) {
- error = HA_ERR_END_OF_FILE;
- }
- start_of_scan = 0;
- } else {
- error = general_fetch(buf, ROW_SEL_NEXT, 0);
- }
-
- DBUG_RETURN(error);
-}
-
-/**************************************************************************
-Fetches a row from the table based on a row reference. */
-
-int
-ha_innobase::rnd_pos(
-/*=================*/
- /* out: 0, HA_ERR_KEY_NOT_FOUND,
- or error code */
- mysql_byte* buf, /* in/out: buffer for the row */
- mysql_byte* pos) /* in: primary key value of the row in the
- MySQL format, or the row id if the clustered
- index was internally generated by InnoDB;
- the length of data in pos has to be
- ref_length */
-{
- row_prebuilt_t* prebuilt = (row_prebuilt_t*) innobase_prebuilt;
- int error;
- uint keynr = active_index;
- DBUG_ENTER("rnd_pos");
- DBUG_DUMP("key", (uchar*) pos, ref_length);
-
- statistic_increment(current_thd->status_var.ha_read_rnd_count,
- &LOCK_status);
-
- ut_ad(prebuilt->trx ==
- (trx_t*) current_thd->ha_data[innobase_hton.slot]);
-
- if (prebuilt->clust_index_was_generated) {
- /* No primary key was defined for the table and we
- generated the clustered index from the row id: the
- row reference is the row id, not any key value
- that MySQL knows of */
-
- error = change_active_index(MAX_KEY);
- } else {
- error = change_active_index(primary_key);
- }
-
- if (error) {
- DBUG_PRINT("error", ("Got error: %d", error));
- DBUG_RETURN(error);
- }
-
- /* Note that we assume the length of the row reference is fixed
- for the table, and it is == ref_length */
-
- error = index_read(buf, pos, ref_length, HA_READ_KEY_EXACT);
-
- if (error) {
- DBUG_PRINT("error", ("Got error: %d", error));
- }
-
- change_active_index(keynr);
-
- DBUG_RETURN(error);
-}
-
-/*************************************************************************
-Stores a reference to the current row to 'ref' field of the handle. Note
-that in the case where we have generated the clustered index for the
-table, the function parameter is illogical: we MUST ASSUME that 'record'
-is the current 'position' of the handle, because if row ref is actually
-the row id internally generated in InnoDB, then 'record' does not contain
-it. We just guess that the row id must be for the record where the handle
-was positioned the last time. */
-
-void
-ha_innobase::position(
-/*==================*/
- const mysql_byte* record) /* in: row in MySQL format */
-{
- row_prebuilt_t* prebuilt = (row_prebuilt_t*) innobase_prebuilt;
- uint len;
-
- ut_ad(prebuilt->trx ==
- (trx_t*) current_thd->ha_data[innobase_hton.slot]);
-
- if (prebuilt->clust_index_was_generated) {
- /* No primary key was defined for the table and we
- generated the clustered index from row id: the
- row reference will be the row id, not any key value
- that MySQL knows of */
-
- len = DATA_ROW_ID_LEN;
-
- memcpy(ref, prebuilt->row_id, len);
- } else {
- len = store_key_val_for_row(primary_key, (char*)ref,
- ref_length, record);
- }
-
- /* We assume that the 'ref' value len is always fixed for the same
- table. */
-
- if (len != ref_length) {
- sql_print_error("Stored ref len is %lu, but table ref len is %lu",
- (ulong) len, (ulong) ref_length);
- }
-}
-
-/*********************************************************************
-Creates a table definition to an InnoDB database. */
-static
-int
-create_table_def(
-/*=============*/
- trx_t* trx, /* in: InnoDB transaction handle */
- TABLE* form, /* in: information on table
- columns and indexes */
- const char* table_name, /* in: table name */
- const char* path_of_temp_table,/* in: if this is a table explicitly
- created by the user with the
- TEMPORARY keyword, then this
- parameter is the dir path where the
- table should be placed if we create
- an .ibd file for it (no .ibd extension
- in the path, though); otherwise this
- is NULL */
- ibool comp) /* in: TRUE=compact record format */
-{
- Field* field;
- dict_table_t* table;
- ulint n_cols;
- int error;
- ulint col_type;
- ulint col_len;
- ulint nulls_allowed;
- ulint unsigned_type;
- ulint binary_type;
- ulint long_true_varchar;
- ulint charset_no;
- ulint i;
-
- DBUG_ENTER("create_table_def");
- DBUG_PRINT("enter", ("table_name: %s", table_name));
-
- n_cols = form->s->fields;
-
- /* We pass 0 as the space id, and determine at a lower level the space
- id where to store the table */
-
- table = dict_mem_table_create(table_name, 0, n_cols, comp);
-
- if (path_of_temp_table) {
- table->dir_path_of_temp_table =
- mem_heap_strdup(table->heap, path_of_temp_table);
- }
-
- for (i = 0; i < n_cols; i++) {
- field = form->field[i];
-
- col_type = get_innobase_type_from_mysql_type(&unsigned_type,
- field);
- if (field->null_ptr) {
- nulls_allowed = 0;
- } else {
- nulls_allowed = DATA_NOT_NULL;
- }
-
- if (field->binary()) {
- binary_type = DATA_BINARY_TYPE;
- } else {
- binary_type = 0;
- }
-
- charset_no = 0;
-
- if (dtype_is_string_type(col_type)) {
-
- charset_no = (ulint)field->charset()->number;
-
- ut_a(charset_no < 256); /* in data0type.h we assume
- that the number fits in one
- byte */
- }
-
- ut_a(field->type() < 256); /* we assume in dtype_form_prtype()
- that this fits in one byte */
- col_len = field->pack_length();
-
- /* The MySQL pack length contains 1 or 2 bytes length field
- for a true VARCHAR. Let us subtract that, so that the InnoDB
- column length in the InnoDB data dictionary is the real
- maximum byte length of the actual data. */
-
- long_true_varchar = 0;
-
- if (field->type() == MYSQL_TYPE_VARCHAR) {
- col_len -= ((Field_varstring*)field)->length_bytes;
-
- if (((Field_varstring*)field)->length_bytes == 2) {
- long_true_varchar = DATA_LONG_TRUE_VARCHAR;
- }
- }
-
- dict_mem_table_add_col(table,
- (char*) field->field_name,
- col_type,
- dtype_form_prtype(
- (ulint)field->type()
- | nulls_allowed | unsigned_type
- | binary_type | long_true_varchar,
- charset_no),
- col_len,
- 0);
- }
-
- error = row_create_table_for_mysql(table, trx);
-
- error = convert_error_code_to_mysql(error, NULL);
-
- DBUG_RETURN(error);
-}
-
-/*********************************************************************
-Creates an index in an InnoDB database. */
-static
-int
-create_index(
-/*=========*/
- trx_t* trx, /* in: InnoDB transaction handle */
- TABLE* form, /* in: information on table
- columns and indexes */
- const char* table_name, /* in: table name */
- uint key_num) /* in: index number */
-{
- Field* field;
- dict_index_t* index;
- int error;
- ulint n_fields;
- KEY* key;
- KEY_PART_INFO* key_part;
- ulint ind_type;
- ulint col_type;
- ulint prefix_len;
- ulint is_unsigned;
- ulint i;
- ulint j;
- ulint* field_lengths;
-
- DBUG_ENTER("create_index");
-
- key = form->key_info + key_num;
-
- n_fields = key->key_parts;
-
- ind_type = 0;
-
- if (key_num == form->s->primary_key) {
- ind_type = ind_type | DICT_CLUSTERED;
- }
-
- if (key->flags & HA_NOSAME ) {
- ind_type = ind_type | DICT_UNIQUE;
- }
-
- /* We pass 0 as the space id, and determine at a lower level the space
- id where to store the table */
-
- index = dict_mem_index_create((char*) table_name, key->name, 0,
- ind_type, n_fields);
-
- field_lengths = (ulint*) my_malloc(sizeof(ulint) * n_fields,
- MYF(MY_FAE));
-
- for (i = 0; i < n_fields; i++) {
- key_part = key->key_part + i;
-
- /* (The flag HA_PART_KEY_SEG denotes in MySQL a column prefix
- field in an index: we only store a specified number of first
- bytes of the column to the index field.) The flag does not
- seem to be properly set by MySQL. Let us fall back on testing
- the length of the key part versus the column. */
-
- field = NULL;
- for (j = 0; j < form->s->fields; j++) {
-
- field = form->field[j];
-
- if (0 == innobase_strcasecmp(
- field->field_name,
- key_part->field->field_name)) {
- /* Found the corresponding column */
-
- break;
- }
- }
-
- ut_a(j < form->s->fields);
-
- col_type = get_innobase_type_from_mysql_type(
- &is_unsigned, key_part->field);
-
- if (DATA_BLOB == col_type
- || (key_part->length < field->pack_length()
- && field->type() != MYSQL_TYPE_VARCHAR)
- || (field->type() == MYSQL_TYPE_VARCHAR
- && key_part->length < field->pack_length()
- - ((Field_varstring*)field)->length_bytes)) {
-
- prefix_len = key_part->length;
-
- if (col_type == DATA_INT
- || col_type == DATA_FLOAT
- || col_type == DATA_DOUBLE
- || col_type == DATA_DECIMAL) {
- sql_print_error("MySQL is trying to create a column "
- "prefix index field, on an "
- "inappropriate data type. Table "
- "name %s, column name %s.",
- table_name,
- key_part->field->field_name);
-
- prefix_len = 0;
- }
- } else {
- prefix_len = 0;
- }
-
- field_lengths[i] = key_part->length;
-
- /* We assume all fields should be sorted in ascending
- order, hence the '0': */
-
- dict_mem_index_add_field(index,
- (char*) key_part->field->field_name,
- 0, prefix_len);
- }
-
- /* Even though we've defined max_supported_key_part_length, we
- still do our own checking using field_lengths to be absolutely
- sure we don't create too long indexes. */
- error = row_create_index_for_mysql(index, trx, field_lengths);
-
- error = convert_error_code_to_mysql(error, NULL);
-
- my_free((gptr) field_lengths, MYF(0));
-
- DBUG_RETURN(error);
-}
-
-/*********************************************************************
-Creates an index to an InnoDB table when the user has defined no
-primary index. */
-static
-int
-create_clustered_index_when_no_primary(
-/*===================================*/
- trx_t* trx, /* in: InnoDB transaction handle */
- const char* table_name) /* in: table name */
-{
- dict_index_t* index;
- int error;
-
- /* We pass 0 as the space id, and determine at a lower level the space
- id where to store the table */
-
- index = dict_mem_index_create((char*) table_name,
- (char*) "GEN_CLUST_INDEX",
- 0, DICT_CLUSTERED, 0);
- error = row_create_index_for_mysql(index, trx, NULL);
-
- error = convert_error_code_to_mysql(error, NULL);
-
- return(error);
-}
-
-/*********************************************************************
-Update create_info. Used in SHOW CREATE TABLE et al. */
-
-void
-ha_innobase::update_create_info(
-/*============================*/
- HA_CREATE_INFO* create_info) /* in/out: create info */
-{
- if (!(create_info->used_fields & HA_CREATE_USED_AUTO)) {
- ha_innobase::info(HA_STATUS_AUTO);
- create_info->auto_increment_value = auto_increment_value;
- }
-}
-
-/*********************************************************************
-Creates a new table to an InnoDB database. */
-
-int
-ha_innobase::create(
-/*================*/
- /* out: error number */
- const char* name, /* in: table name */
- TABLE* form, /* in: information on table
- columns and indexes */
- HA_CREATE_INFO* create_info) /* in: more information of the
- created table, contains also the
- create statement string */
-{
- int error;
- dict_table_t* innobase_table;
- trx_t* parent_trx;
- trx_t* trx;
- int primary_key_no;
- uint i;
- char name2[FN_REFLEN];
- char norm_name[FN_REFLEN];
- THD *thd= current_thd;
- ib_longlong auto_inc_value;
-
- DBUG_ENTER("ha_innobase::create");
-
- DBUG_ASSERT(thd != NULL);
-
- if (form->s->fields > 1000) {
- /* The limit probably should be REC_MAX_N_FIELDS - 3 = 1020,
- but we play safe here */
-
- DBUG_RETURN(HA_ERR_TO_BIG_ROW);
- }
-
- /* Get the transaction associated with the current thd, or create one
- if not yet created */
-
- parent_trx = check_trx_exists(current_thd);
-
- /* In case MySQL calls this in the middle of a SELECT query, release
- possible adaptive hash latch to avoid deadlocks of threads */
-
- trx_search_latch_release_if_reserved(parent_trx);
-
- trx = trx_allocate_for_mysql();
-
- trx->mysql_thd = thd;
- trx->mysql_query_str = &((*thd).query);
-
- if (thd->options & OPTION_NO_FOREIGN_KEY_CHECKS) {
- trx->check_foreigns = FALSE;
- }
-
- if (thd->options & OPTION_RELAXED_UNIQUE_CHECKS) {
- trx->check_unique_secondary = FALSE;
- }
-
- if (lower_case_table_names) {
- srv_lower_case_table_names = TRUE;
- } else {
- srv_lower_case_table_names = FALSE;
- }
-
- fn_format(name2, name, "", "", 2); // Remove the .frm extension
-
- normalize_table_name(norm_name, name2);
-
- /* Latch the InnoDB data dictionary exclusively so that no deadlocks
- or lock waits can happen in it during a table create operation.
- Drop table etc. do this latching in row0mysql.c. */
-
- row_mysql_lock_data_dictionary(trx);
-
- /* Create the table definition in InnoDB */
-
- error = create_table_def(trx, form, norm_name,
- create_info->options & HA_LEX_CREATE_TMP_TABLE ? name2 : NULL,
- form->s->row_type != ROW_TYPE_REDUNDANT);
-
- if (error) {
- goto cleanup;
- }
-
- /* Look for a primary key */
-
- primary_key_no= (table->s->primary_key != MAX_KEY ?
- (int) table->s->primary_key :
- -1);
-
- /* Our function row_get_mysql_key_number_for_index assumes
- the primary key is always number 0, if it exists */
-
- DBUG_ASSERT(primary_key_no == -1 || primary_key_no == 0);
-
- /* Create the keys */
-
- if (form->s->keys == 0 || primary_key_no == -1) {
- /* Create an index which is used as the clustered index;
- order the rows by their row id which is internally generated
- by InnoDB */
-
- error = create_clustered_index_when_no_primary(trx,
- norm_name);
- if (error) {
- goto cleanup;
- }
- }
-
- if (primary_key_no != -1) {
- /* In InnoDB the clustered index must always be created
- first */
- if ((error = create_index(trx, form, norm_name,
- (uint) primary_key_no))) {
- goto cleanup;
- }
- }
-
- for (i = 0; i < form->s->keys; i++) {
-
- if (i != (uint) primary_key_no) {
-
- if ((error = create_index(trx, form, norm_name, i))) {
- goto cleanup;
- }
- }
- }
-
- if (current_thd->query != NULL) {
- LEX_STRING q;
-
- if (thd->convert_string(&q, system_charset_info,
- current_thd->query,
- current_thd->query_length,
- current_thd->charset())) {
- error = HA_ERR_OUT_OF_MEM;
-
- goto cleanup;
- }
-
- error = row_table_add_foreign_constraints(trx,
- q.str, norm_name,
- create_info->options & HA_LEX_CREATE_TMP_TABLE);
-
- error = convert_error_code_to_mysql(error, NULL);
-
- if (error) {
- goto cleanup;
- }
- }
-
- innobase_commit_low(trx);
-
- row_mysql_unlock_data_dictionary(trx);
-
- /* Flush the log to reduce probability that the .frm files and
- the InnoDB data dictionary get out-of-sync if the user runs
- with innodb_flush_log_at_trx_commit = 0 */
-
- log_buffer_flush_to_disk();
-
- innobase_table = dict_table_get(norm_name, NULL);
-
- DBUG_ASSERT(innobase_table != 0);
-
- if ((create_info->used_fields & HA_CREATE_USED_AUTO) &&
- (create_info->auto_increment_value != 0)) {
-
- /* Query was ALTER TABLE...AUTO_INCREMENT = x; or
- CREATE TABLE ...AUTO_INCREMENT = x; Find out a table
- definition from the dictionary and get the current value
- of the auto increment field. Set a new value to the
- auto increment field if the value is greater than the
- maximum value in the column. */
-
- auto_inc_value = create_info->auto_increment_value;
- dict_table_autoinc_initialize(innobase_table, auto_inc_value);
- }
-
- /* Tell the InnoDB server that there might be work for
- utility threads: */
-
- srv_active_wake_master_thread();
-
- trx_free_for_mysql(trx);
-
- DBUG_RETURN(0);
-
-cleanup:
- innobase_commit_low(trx);
-
- row_mysql_unlock_data_dictionary(trx);
-
- trx_free_for_mysql(trx);
-
- DBUG_RETURN(error);
-}
-
-/*********************************************************************
-Discards or imports an InnoDB tablespace. */
-
-int
-ha_innobase::discard_or_import_tablespace(
-/*======================================*/
- /* out: 0 == success, -1 == error */
- my_bool discard) /* in: TRUE if discard, else import */
-{
- row_prebuilt_t* prebuilt = (row_prebuilt_t*) innobase_prebuilt;
- dict_table_t* dict_table;
- trx_t* trx;
- int err;
-
- DBUG_ENTER("ha_innobase::discard_or_import_tablespace");
-
- ut_a(prebuilt->trx && prebuilt->trx->magic_n == TRX_MAGIC_N);
- ut_a(prebuilt->trx ==
- (trx_t*) current_thd->ha_data[innobase_hton.slot]);
-
- dict_table = prebuilt->table;
- trx = prebuilt->trx;
-
- if (discard) {
- err = row_discard_tablespace_for_mysql(dict_table->name, trx);
- } else {
- err = row_import_tablespace_for_mysql(dict_table->name, trx);
- }
-
- err = convert_error_code_to_mysql(err, NULL);
-
- DBUG_RETURN(err);
-}
-
-/*********************************************************************
-Deletes all rows of an InnoDB table. */
-
-int
-ha_innobase::delete_all_rows(void)
-/*==============================*/
- /* out: error number */
-{
- row_prebuilt_t* prebuilt = (row_prebuilt_t*)innobase_prebuilt;
- int error;
- trx_t* trx;
- THD* thd = current_thd;
-
- DBUG_ENTER("ha_innobase::delete_all_rows");
-
- if (thd->lex->sql_command != SQLCOM_TRUNCATE) {
- fallback:
- /* We only handle TRUNCATE TABLE t as a special case.
- DELETE FROM t will have to use ha_innobase::delete_row(). */
- DBUG_RETURN(my_errno=HA_ERR_WRONG_COMMAND);
- }
-
- /* Get the transaction associated with the current thd, or create one
- if not yet created */
-
- trx = check_trx_exists(thd);
-
- /* Truncate the table in InnoDB */
-
- error = row_truncate_table_for_mysql(prebuilt->table, trx);
- if (error == DB_ERROR) {
- /* Cannot truncate; resort to ha_innobase::delete_row() */
- goto fallback;
- }
-
- error = convert_error_code_to_mysql(error, NULL);
-
- DBUG_RETURN(error);
-}
-
-/*********************************************************************
-Drops a table from an InnoDB database. Before calling this function,
-MySQL calls innobase_commit to commit the transaction of the current user.
-Then the current user cannot have locks set on the table. Drop table
-operation inside InnoDB will remove all locks any user has on the table
-inside InnoDB. */
-
-int
-ha_innobase::delete_table(
-/*======================*/
- /* out: error number */
- const char* name) /* in: table name */
-{
- ulint name_len;
- int error;
- trx_t* parent_trx;
- trx_t* trx;
- THD *thd= current_thd;
- char norm_name[1000];
-
- DBUG_ENTER("ha_innobase::delete_table");
-
- /* Get the transaction associated with the current thd, or create one
- if not yet created */
-
- parent_trx = check_trx_exists(current_thd);
-
- /* In case MySQL calls this in the middle of a SELECT query, release
- possible adaptive hash latch to avoid deadlocks of threads */
-
- trx_search_latch_release_if_reserved(parent_trx);
-
- if (lower_case_table_names) {
- srv_lower_case_table_names = TRUE;
- } else {
- srv_lower_case_table_names = FALSE;
- }
-
- trx = trx_allocate_for_mysql();
-
- trx->mysql_thd = current_thd;
- trx->mysql_query_str = &((*current_thd).query);
-
- if (thd->options & OPTION_NO_FOREIGN_KEY_CHECKS) {
- trx->check_foreigns = FALSE;
- }
-
- if (thd->options & OPTION_RELAXED_UNIQUE_CHECKS) {
- trx->check_unique_secondary = FALSE;
- }
-
- name_len = strlen(name);
-
- assert(name_len < 1000);
-
- /* Strangely, MySQL passes the table name without the '.frm'
- extension, in contrast to ::create */
-
- normalize_table_name(norm_name, name);
-
- /* Drop the table in InnoDB */
-
- error = row_drop_table_for_mysql(norm_name, trx,
- thd->lex->sql_command == SQLCOM_DROP_DB);
-
- /* Flush the log to reduce probability that the .frm files and
- the InnoDB data dictionary get out-of-sync if the user runs
- with innodb_flush_log_at_trx_commit = 0 */
-
- log_buffer_flush_to_disk();
-
- /* Tell the InnoDB server that there might be work for
- utility threads: */
-
- srv_active_wake_master_thread();
-
- innobase_commit_low(trx);
-
- trx_free_for_mysql(trx);
-
- error = convert_error_code_to_mysql(error, NULL);
-
- DBUG_RETURN(error);
-}
-
-/*********************************************************************
-Removes all tables in the named database inside InnoDB. */
-
-int
-innobase_drop_database(
-/*===================*/
- /* out: error number */
- char* path) /* in: database path; inside InnoDB the name
- of the last directory in the path is used as
- the database name: for example, in 'mysql/data/test'
- the database name is 'test' */
-{
- ulint len = 0;
- trx_t* parent_trx;
- trx_t* trx;
- char* ptr;
- int error;
- char* namebuf;
-
- /* Get the transaction associated with the current thd, or create one
- if not yet created */
-
- parent_trx = check_trx_exists(current_thd);
-
- /* In case MySQL calls this in the middle of a SELECT query, release
- possible adaptive hash latch to avoid deadlocks of threads */
-
- trx_search_latch_release_if_reserved(parent_trx);
-
- ptr = strend(path) - 2;
-
- while (ptr >= path && *ptr != '\\' && *ptr != '/') {
- ptr--;
- len++;
- }
-
- ptr++;
- namebuf = my_malloc((uint) len + 2, MYF(0));
-
- memcpy(namebuf, ptr, len);
- namebuf[len] = '/';
- namebuf[len + 1] = '\0';
-#ifdef __WIN__
- innobase_casedn_str(namebuf);
-#endif
- trx = trx_allocate_for_mysql();
- trx->mysql_thd = current_thd;
- trx->mysql_query_str = &((*current_thd).query);
-
- if (current_thd->options & OPTION_NO_FOREIGN_KEY_CHECKS) {
- trx->check_foreigns = FALSE;
- }
-
- error = row_drop_database_for_mysql(namebuf, trx);
- my_free(namebuf, MYF(0));
-
- /* Flush the log to reduce probability that the .frm files and
- the InnoDB data dictionary get out-of-sync if the user runs
- with innodb_flush_log_at_trx_commit = 0 */
-
- log_buffer_flush_to_disk();
-
- /* Tell the InnoDB server that there might be work for
- utility threads: */
-
- srv_active_wake_master_thread();
-
- innobase_commit_low(trx);
- trx_free_for_mysql(trx);
-
- error = convert_error_code_to_mysql(error, NULL);
-
- return(error);
-}
-
-/*************************************************************************
-Renames an InnoDB table. */
-
-int
-ha_innobase::rename_table(
-/*======================*/
- /* out: 0 or error code */
- const char* from, /* in: old name of the table */
- const char* to) /* in: new name of the table */
-{
- ulint name_len1;
- ulint name_len2;
- int error;
- trx_t* parent_trx;
- trx_t* trx;
- char norm_from[1000];
- char norm_to[1000];
-
- DBUG_ENTER("ha_innobase::rename_table");
-
- /* Get the transaction associated with the current thd, or create one
- if not yet created */
-
- parent_trx = check_trx_exists(current_thd);
-
- /* In case MySQL calls this in the middle of a SELECT query, release
- possible adaptive hash latch to avoid deadlocks of threads */
-
- trx_search_latch_release_if_reserved(parent_trx);
-
- if (lower_case_table_names) {
- srv_lower_case_table_names = TRUE;
- } else {
- srv_lower_case_table_names = FALSE;
- }
-
- trx = trx_allocate_for_mysql();
- trx->mysql_thd = current_thd;
- trx->mysql_query_str = &((*current_thd).query);
-
- if (current_thd->options & OPTION_NO_FOREIGN_KEY_CHECKS) {
- trx->check_foreigns = FALSE;
- }
-
- name_len1 = strlen(from);
- name_len2 = strlen(to);
-
- assert(name_len1 < 1000);
- assert(name_len2 < 1000);
-
- normalize_table_name(norm_from, from);
- normalize_table_name(norm_to, to);
-
- /* Rename the table in InnoDB */
-
- error = row_rename_table_for_mysql(norm_from, norm_to, trx);
-
- /* Flush the log to reduce probability that the .frm files and
- the InnoDB data dictionary get out-of-sync if the user runs
- with innodb_flush_log_at_trx_commit = 0 */
-
- log_buffer_flush_to_disk();
-
- /* Tell the InnoDB server that there might be work for
- utility threads: */
-
- srv_active_wake_master_thread();
-
- innobase_commit_low(trx);
- trx_free_for_mysql(trx);
-
- error = convert_error_code_to_mysql(error, NULL);
-
- DBUG_RETURN(error);
-}
-
-/*************************************************************************
-Estimates the number of index records in a range. */
-
-ha_rows
-ha_innobase::records_in_range(
-/*==========================*/
- /* out: estimated number of
- rows */
- uint keynr, /* in: index number */
- key_range *min_key, /* in: start key value of the
- range, may also be 0 */
- key_range *max_key) /* in: range end key val, may
- also be 0 */
-{
- row_prebuilt_t* prebuilt = (row_prebuilt_t*) innobase_prebuilt;
- KEY* key;
- dict_index_t* index;
- mysql_byte* key_val_buff2 = (mysql_byte*) my_malloc(
- table->s->reclength
- + table->s->max_key_length + 100,
- MYF(MY_FAE));
- ulint buff2_len = table->s->reclength
- + table->s->max_key_length + 100;
- dtuple_t* range_start;
- dtuple_t* range_end;
- ib_longlong n_rows;
- ulint mode1;
- ulint mode2;
- void* heap1;
- void* heap2;
-
- DBUG_ENTER("records_in_range");
-
- prebuilt->trx->op_info = (char*)"estimating records in index range";
-
- /* In case MySQL calls this in the middle of a SELECT query, release
- possible adaptive hash latch to avoid deadlocks of threads */
-
- trx_search_latch_release_if_reserved(prebuilt->trx);
-
- active_index = keynr;
-
- key = table->key_info + active_index;
-
- index = dict_table_get_index_noninline(prebuilt->table, key->name);
-
- range_start = dtuple_create_for_mysql(&heap1, key->key_parts);
- dict_index_copy_types(range_start, index, key->key_parts);
-
- range_end = dtuple_create_for_mysql(&heap2, key->key_parts);
- dict_index_copy_types(range_end, index, key->key_parts);
-
- row_sel_convert_mysql_key_to_innobase(
- range_start, (byte*) key_val_buff,
- (ulint)upd_and_key_val_buff_len,
- index,
- (byte*) (min_key ? min_key->key :
- (const mysql_byte*) 0),
- (ulint) (min_key ? min_key->length : 0),
- prebuilt->trx);
-
- row_sel_convert_mysql_key_to_innobase(
- range_end, (byte*) key_val_buff2,
- buff2_len, index,
- (byte*) (max_key ? max_key->key :
- (const mysql_byte*) 0),
- (ulint) (max_key ? max_key->length : 0),
- prebuilt->trx);
-
- mode1 = convert_search_mode_to_innobase(min_key ? min_key->flag :
- HA_READ_KEY_EXACT);
- mode2 = convert_search_mode_to_innobase(max_key ? max_key->flag :
- HA_READ_KEY_EXACT);
-
- if (mode1 != PAGE_CUR_UNSUPP && mode2 != PAGE_CUR_UNSUPP) {
-
- n_rows = btr_estimate_n_rows_in_range(index, range_start,
- mode1, range_end,
- mode2);
- } else {
-
- n_rows = 0;
- }
-
- dtuple_free_for_mysql(heap1);
- dtuple_free_for_mysql(heap2);
-
- my_free((gptr) key_val_buff2, MYF(0));
-
- prebuilt->trx->op_info = (char*)"";
-
- /* The MySQL optimizer seems to believe an estimate of 0 rows is
- always accurate and may return the result 'Empty set' based on that.
- The accuracy is not guaranteed, and even if it were, for a locking
- read we should anyway perform the search to set the next-key lock.
- Add 1 to the value to make sure MySQL does not make the assumption! */
-
- if (n_rows == 0) {
- n_rows = 1;
- }
-
- DBUG_RETURN((ha_rows) n_rows);
-}
-
-/*************************************************************************
-Gives an UPPER BOUND to the number of rows in a table. This is used in
-filesort.cc. */
-
-ha_rows
-ha_innobase::estimate_rows_upper_bound(void)
-/*======================================*/
- /* out: upper bound of rows */
-{
- row_prebuilt_t* prebuilt = (row_prebuilt_t*) innobase_prebuilt;
- dict_index_t* index;
- ulonglong estimate;
- ulonglong local_data_file_length;
-
- DBUG_ENTER("estimate_rows_upper_bound");
-
- /* We do not know if MySQL can call this function before calling
- external_lock(). To be safe, update the thd of the current table
- handle. */
-
- update_thd(current_thd);
-
- prebuilt->trx->op_info = (char*)
- "calculating upper bound for table rows";
-
- /* In case MySQL calls this in the middle of a SELECT query, release
- possible adaptive hash latch to avoid deadlocks of threads */
-
- trx_search_latch_release_if_reserved(prebuilt->trx);
-
- index = dict_table_get_first_index_noninline(prebuilt->table);
-
- local_data_file_length = ((ulonglong) index->stat_n_leaf_pages)
- * UNIV_PAGE_SIZE;
-
- /* Calculate a minimum length for a clustered index record and from
- that an upper bound for the number of rows. Since we only calculate
- new statistics in row0mysql.c when a table has grown by a threshold
- factor, we must add a safety factor 2 in front of the formula below. */
-
- estimate = 2 * local_data_file_length /
- dict_index_calc_min_rec_len(index);
-
- prebuilt->trx->op_info = (char*)"";
-
- DBUG_RETURN((ha_rows) estimate);
-}
-
-/*************************************************************************
-How many seeks it will take to read through the table. This is to be
-comparable to the number returned by records_in_range so that we can
-decide if we should scan the table or use keys. */
-
-double
-ha_innobase::scan_time()
-/*====================*/
- /* out: estimated time measured in disk seeks */
-{
- row_prebuilt_t* prebuilt = (row_prebuilt_t*) innobase_prebuilt;
-
- /* Since MySQL seems to favor table scans too much over index
- searches, we pretend that a sequential read takes the same time
- as a random disk read, that is, we do not divide the following
- by 10, which would be physically realistic. */
-
- return((double) (prebuilt->table->stat_clustered_index_size));
-}
-
-/**********************************************************************
-Calculate the time it takes to read a set of ranges through an index
-This enables us to optimise reads for clustered indexes. */
-
-double
-ha_innobase::read_time(
-/*===================*/
- /* out: estimated time measured in disk seeks */
- uint index, /* in: key number */
- uint ranges, /* in: how many ranges */
- ha_rows rows) /* in: estimated number of rows in the ranges */
-{
- ha_rows total_rows;
- double time_for_scan;
-
- if (index != table->s->primary_key) {
- /* Not clustered */
- return(handler::read_time(index, ranges, rows));
- }
-
- if (rows <= 2) {
-
- return((double) rows);
- }
-
- /* Assume that the read time is proportional to the scan time for all
- rows + at most one seek per range. */
-
- time_for_scan = scan_time();
-
- if ((total_rows = estimate_rows_upper_bound()) < rows) {
-
- return(time_for_scan);
- }
-
- return(ranges + (double) rows / (double) total_rows * time_for_scan);
-}
-
-/*************************************************************************
-Returns statistics information of the table to the MySQL interpreter,
-in various fields of the handle object. */
-
-int
-ha_innobase::info(
-/*==============*/
- uint flag) /* in: what information MySQL requests */
-{
- row_prebuilt_t* prebuilt = (row_prebuilt_t*) innobase_prebuilt;
- dict_table_t* ib_table;
- dict_index_t* index;
- ha_rows rec_per_key;
- ib_longlong n_rows;
- ulong j;
- ulong i;
- char path[FN_REFLEN];
- os_file_stat_t stat_info;
-
- DBUG_ENTER("info");
-
- /* If we are forcing recovery at a high level, we will suppress
- statistics calculation on tables, because that may crash the
- server if an index is badly corrupted. */
-
- if (srv_force_recovery >= SRV_FORCE_NO_IBUF_MERGE) {
-
- /* We return success (0) instead of HA_ERR_CRASHED,
- because we want MySQL to process this query and not
- stop, like it would do if it received the error code
- HA_ERR_CRASHED. */
-
- DBUG_RETURN(0);
- }
-
- /* We do not know if MySQL can call this function before calling
- external_lock(). To be safe, update the thd of the current table
- handle. */
-
- update_thd(current_thd);
-
- /* In case MySQL calls this in the middle of a SELECT query, release
- possible adaptive hash latch to avoid deadlocks of threads */
-
- prebuilt->trx->op_info = (char*)"returning various info to MySQL";
-
- trx_search_latch_release_if_reserved(prebuilt->trx);
-
- ib_table = prebuilt->table;
-
- if (flag & HA_STATUS_TIME) {
- /* In sql_show we call with this flag: update then statistics
- so that they are up-to-date */
-
- prebuilt->trx->op_info = (char*)"updating table statistics";
-
- dict_update_statistics(ib_table);
-
- prebuilt->trx->op_info = (char*)
- "returning various info to MySQL";
- my_snprintf(path, sizeof(path), "%s/%s%s",
- mysql_data_home, ib_table->name,
- reg_ext);
-
- unpack_filename(path,path);
-
- /* Note that we do not know the access time of the table,
- nor the CHECK TABLE time, nor the UPDATE or INSERT time. */
-
- if (os_file_get_status(path,&stat_info)) {
- create_time = stat_info.ctime;
- }
- }
-
- if (flag & HA_STATUS_VARIABLE) {
- n_rows = ib_table->stat_n_rows;
-
- /* Because we do not protect stat_n_rows by any mutex in a
- delete, it is theoretically possible that the value can be
- smaller than zero! TODO: fix this race.
-
- The MySQL optimizer seems to assume in a left join that n_rows
- is an accurate estimate if it is zero. Of course, it is not,
- since we do not have any locks on the rows yet at this phase.
- Since SHOW TABLE STATUS seems to call this function with the
- HA_STATUS_TIME flag set, while the left join optimizer does not
- set that flag, we add one to a zero value if the flag is not
- set. That way SHOW TABLE STATUS will show the best estimate,
- while the optimizer never sees the table empty. */
-
- if (n_rows < 0) {
- n_rows = 0;
- }
-
- if (n_rows == 0 && !(flag & HA_STATUS_TIME)) {
- n_rows++;
- }
-
- records = (ha_rows)n_rows;
- deleted = 0;
- data_file_length = ((ulonglong)
- ib_table->stat_clustered_index_size)
- * UNIV_PAGE_SIZE;
- index_file_length = ((ulonglong)
- ib_table->stat_sum_of_other_index_sizes)
- * UNIV_PAGE_SIZE;
- delete_length = 0;
- check_time = 0;
-
- if (records == 0) {
- mean_rec_length = 0;
- } else {
- mean_rec_length = (ulong) (data_file_length / records);
- }
- }
-
- if (flag & HA_STATUS_CONST) {
- index = dict_table_get_first_index_noninline(ib_table);
-
- if (prebuilt->clust_index_was_generated) {
- index = dict_table_get_next_index_noninline(index);
- }
-
- for (i = 0; i < table->s->keys; i++) {
- if (index == NULL) {
- ut_print_timestamp(stderr);
- sql_print_error("Table %s contains fewer "
- "indexes inside InnoDB than "
- "are defined in the MySQL "
- ".frm file. Have you mixed up "
- ".frm files from different "
- "installations? See "
-"http://dev.mysql.com/doc/refman/5.0/en/innodb-troubleshooting.html\n",
-
- ib_table->name);
- break;
- }
-
- for (j = 0; j < table->key_info[i].key_parts; j++) {
-
- if (j + 1 > index->n_uniq) {
- ut_print_timestamp(stderr);
- sql_print_error(
-"Index %s of %s has %lu columns unique inside InnoDB, but MySQL is asking "
-"statistics for %lu columns. Have you mixed up .frm files from different "
-"installations? "
-"See http://dev.mysql.com/doc/refman/5.0/en/innodb-troubleshooting.html\n",
- index->name,
- ib_table->name,
- (unsigned long)
- index->n_uniq, j + 1);
- break;
- }
-
- if (index->stat_n_diff_key_vals[j + 1] == 0) {
-
- rec_per_key = records;
- } else {
- rec_per_key = (ha_rows)(records /
- index->stat_n_diff_key_vals[j + 1]);
- }
-
- /* Since MySQL seems to favor table scans
- too much over index searches, we pretend
- index selectivity is 2 times better than
- our estimate: */
-
- rec_per_key = rec_per_key / 2;
-
- if (rec_per_key == 0) {
- rec_per_key = 1;
- }
-
- table->key_info[i].rec_per_key[j]=
- rec_per_key >= ~(ulong) 0 ? ~(ulong) 0 :
- (ulong) rec_per_key;
- }
-
- index = dict_table_get_next_index_noninline(index);
- }
- }
-
- if (flag & HA_STATUS_ERRKEY) {
- ut_a(prebuilt->trx && prebuilt->trx->magic_n == TRX_MAGIC_N);
-
- errkey = (unsigned int) row_get_mysql_key_number_for_index(
- (dict_index_t*)
- trx_get_error_info(prebuilt->trx));
- }
-
- if (flag & HA_STATUS_AUTO && table->found_next_number_field) {
- longlong auto_inc;
- int ret;
-
- /* The following function call can the first time fail in
- a lock wait timeout error because it reserves the auto-inc
- lock on the table. If it fails, then someone is already initing
- the auto-inc counter, and the second call is guaranteed to
- succeed. */
-
- ret = innobase_read_and_init_auto_inc(&auto_inc);
-
- if (ret != 0) {
- ret = innobase_read_and_init_auto_inc(&auto_inc);
-
- if (ret != 0) {
- ut_print_timestamp(stderr);
- sql_print_error("Cannot get table %s auto-inc"
- "counter value in ::info\n",
- ib_table->name);
- auto_inc = 0;
- }
- }
-
- auto_increment_value = auto_inc;
- }
-
- prebuilt->trx->op_info = (char*)"";
-
- DBUG_RETURN(0);
-}
-
-/**************************************************************************
-Updates index cardinalities of the table, based on 8 random dives into
-each index tree. This does NOT calculate exact statistics on the table. */
-
-int
-ha_innobase::analyze(
-/*=================*/
- /* out: returns always 0 (success) */
- THD* thd, /* in: connection thread handle */
- HA_CHECK_OPT* check_opt) /* in: currently ignored */
-{
- /* Simply call ::info() with all the flags */
- info(HA_STATUS_TIME | HA_STATUS_CONST | HA_STATUS_VARIABLE);
-
- return(0);
-}
-
-/**************************************************************************
-This is mapped to "ALTER TABLE tablename TYPE=InnoDB", which rebuilds
-the table in MySQL. */
-
-int
-ha_innobase::optimize(
-/*==================*/
- THD* thd, /* in: connection thread handle */
- HA_CHECK_OPT* check_opt) /* in: currently ignored */
-{
- return(HA_ADMIN_TRY_ALTER);
-}
-
-/***********************************************************************
-Tries to check that an InnoDB table is not corrupted. If corruption is
-noticed, prints to stderr information about it. In case of corruption
-may also assert a failure and crash the server. */
-
-int
-ha_innobase::check(
-/*===============*/
- /* out: HA_ADMIN_CORRUPT or
- HA_ADMIN_OK */
- THD* thd, /* in: user thread handle */
- HA_CHECK_OPT* check_opt) /* in: check options, currently
- ignored */
-{
- row_prebuilt_t* prebuilt = (row_prebuilt_t*) innobase_prebuilt;
- ulint ret;
-
- ut_a(prebuilt->trx && prebuilt->trx->magic_n == TRX_MAGIC_N);
- ut_a(prebuilt->trx ==
- (trx_t*) current_thd->ha_data[innobase_hton.slot]);
-
- if (prebuilt->mysql_template == NULL) {
- /* Build the template; we will use a dummy template
- in index scans done in checking */
-
- build_template(prebuilt, NULL, table, ROW_MYSQL_WHOLE_ROW);
- }
-
- ret = row_check_table_for_mysql(prebuilt);
-
- if (ret == DB_SUCCESS) {
- return(HA_ADMIN_OK);
- }
-
- return(HA_ADMIN_CORRUPT);
-}
-
-/*****************************************************************
-Adds information about free space in the InnoDB tablespace to a table comment
-which is printed out when a user calls SHOW TABLE STATUS. Adds also info on
-foreign keys. */
-
-char*
-ha_innobase::update_table_comment(
-/*==============================*/
- /* out: table comment + InnoDB free space +
- info on foreign keys */
- const char* comment)/* in: table comment defined by user */
-{
- uint length = (uint) strlen(comment);
- char* str;
- row_prebuilt_t* prebuilt = (row_prebuilt_t*)innobase_prebuilt;
- long flen;
-
- /* We do not know if MySQL can call this function before calling
- external_lock(). To be safe, update the thd of the current table
- handle. */
-
- if (length > 64000 - 3) {
- return((char*)comment); /* string too long */
- }
-
- update_thd(current_thd);
-
- prebuilt->trx->op_info = (char*)"returning table comment";
-
- /* In case MySQL calls this in the middle of a SELECT query, release
- possible adaptive hash latch to avoid deadlocks of threads */
-
- trx_search_latch_release_if_reserved(prebuilt->trx);
- str = NULL;
-
- /* output the data to a temporary file */
-
- mutex_enter_noninline(&srv_dict_tmpfile_mutex);
- rewind(srv_dict_tmpfile);
-
- fprintf(srv_dict_tmpfile, "InnoDB free: %llu kB",
- fsp_get_available_space_in_free_extents(
- prebuilt->table->space));
-
- dict_print_info_on_foreign_keys(FALSE, srv_dict_tmpfile,
- prebuilt->trx, prebuilt->table);
- flen = ftell(srv_dict_tmpfile);
- if (flen < 0) {
- flen = 0;
- } else if (length + flen + 3 > 64000) {
- flen = 64000 - 3 - length;
- }
-
- /* allocate buffer for the full string, and
- read the contents of the temporary file */
-
- str = my_malloc(length + flen + 3, MYF(0));
-
- if (str) {
- char* pos = str + length;
- if (length) {
- memcpy(str, comment, length);
- *pos++ = ';';
- *pos++ = ' ';
- }
- rewind(srv_dict_tmpfile);
- flen = (uint) fread(pos, 1, flen, srv_dict_tmpfile);
- pos[flen] = 0;
- }
-
- mutex_exit_noninline(&srv_dict_tmpfile_mutex);
-
- prebuilt->trx->op_info = (char*)"";
-
- return(str ? str : (char*) comment);
-}
-
-/***********************************************************************
-Gets the foreign key create info for a table stored in InnoDB. */
-
-char*
-ha_innobase::get_foreign_key_create_info(void)
-/*==========================================*/
- /* out, own: character string in the form which
- can be inserted to the CREATE TABLE statement,
- MUST be freed with ::free_foreign_key_create_info */
-{
- row_prebuilt_t* prebuilt = (row_prebuilt_t*)innobase_prebuilt;
- char* str = 0;
- long flen;
-
- ut_a(prebuilt != NULL);
-
- /* We do not know if MySQL can call this function before calling
- external_lock(). To be safe, update the thd of the current table
- handle. */
-
- update_thd(current_thd);
-
- prebuilt->trx->op_info = (char*)"getting info on foreign keys";
-
- /* In case MySQL calls this in the middle of a SELECT query,
- release possible adaptive hash latch to avoid
- deadlocks of threads */
-
- trx_search_latch_release_if_reserved(prebuilt->trx);
-
- mutex_enter_noninline(&srv_dict_tmpfile_mutex);
- rewind(srv_dict_tmpfile);
-
- /* output the data to a temporary file */
- dict_print_info_on_foreign_keys(TRUE, srv_dict_tmpfile,
- prebuilt->trx, prebuilt->table);
- prebuilt->trx->op_info = (char*)"";
-
- flen = ftell(srv_dict_tmpfile);
- if (flen < 0) {
- flen = 0;
- } else if (flen > 64000 - 1) {
- flen = 64000 - 1;
- }
-
- /* allocate buffer for the string, and
- read the contents of the temporary file */
-
- str = my_malloc(flen + 1, MYF(0));
-
- if (str) {
- rewind(srv_dict_tmpfile);
- flen = (uint) fread(str, 1, flen, srv_dict_tmpfile);
- str[flen] = 0;
- }
-
- mutex_exit_noninline(&srv_dict_tmpfile_mutex);
-
- return(str);
-}
-
-
-int
-ha_innobase::get_foreign_key_list(THD *thd, List<FOREIGN_KEY_INFO> *f_key_list)
-{
- dict_foreign_t* foreign;
-
- DBUG_ENTER("get_foreign_key_list");
- row_prebuilt_t* prebuilt = (row_prebuilt_t*)innobase_prebuilt;
- ut_a(prebuilt != NULL);
- update_thd(current_thd);
- prebuilt->trx->op_info = (char*)"getting list of foreign keys";
- trx_search_latch_release_if_reserved(prebuilt->trx);
- mutex_enter_noninline(&(dict_sys->mutex));
- foreign = UT_LIST_GET_FIRST(prebuilt->table->foreign_list);
-
- while (foreign != NULL)
- {
- uint i;
- FOREIGN_KEY_INFO f_key_info;
- LEX_STRING *name= 0;
- const char *tmp_buff;
-
- tmp_buff= foreign->id;
- i= 0;
- while (tmp_buff[i] != '/')
- i++;
- tmp_buff+= i + 1;
- f_key_info.forein_id= make_lex_string(thd, 0, tmp_buff,
- (uint) strlen(tmp_buff), 1);
- tmp_buff= foreign->referenced_table_name;
- i= 0;
- while (tmp_buff[i] != '/')
- i++;
- f_key_info.referenced_db= make_lex_string(thd, 0,
- tmp_buff, i, 1);
- tmp_buff+= i + 1;
- f_key_info.referenced_table= make_lex_string(thd, 0, tmp_buff,
- (uint) strlen(tmp_buff), 1);
-
- for (i= 0;;)
- {
- tmp_buff= foreign->foreign_col_names[i];
- name= make_lex_string(thd, name, tmp_buff, (uint) strlen(tmp_buff), 1);
- f_key_info.foreign_fields.push_back(name);
- tmp_buff= foreign->referenced_col_names[i];
- name= make_lex_string(thd, name, tmp_buff, (uint) strlen(tmp_buff), 1);
- f_key_info.referenced_fields.push_back(name);
- if (++i >= foreign->n_fields)
- break;
- }
-
- ulong length= 0;
- if (foreign->type == DICT_FOREIGN_ON_DELETE_CASCADE)
- {
- length=17;
- tmp_buff= "ON DELETE CASCADE";
- }
- else if (foreign->type == DICT_FOREIGN_ON_DELETE_SET_NULL)
- {
- length=18;
- tmp_buff= "ON DELETE SET NULL";
- }
- else if (foreign->type == DICT_FOREIGN_ON_DELETE_NO_ACTION)
- {
- length=19;
- tmp_buff= "ON DELETE NO ACTION";
- }
- else if (foreign->type == DICT_FOREIGN_ON_UPDATE_CASCADE)
- {
- length=17;
- tmp_buff= "ON UPDATE CASCADE";
- }
- else if (foreign->type == DICT_FOREIGN_ON_UPDATE_SET_NULL)
- {
- length=18;
- tmp_buff= "ON UPDATE SET NULL";
- }
- else if (foreign->type == DICT_FOREIGN_ON_UPDATE_NO_ACTION)
- {
- length=19;
- tmp_buff= "ON UPDATE NO ACTION";
- }
- f_key_info.constraint_method= make_lex_string(thd,
- f_key_info.constraint_method,
- tmp_buff, length, 1);
-
- FOREIGN_KEY_INFO *pf_key_info= ((FOREIGN_KEY_INFO *)
- thd->memdup((gptr) &f_key_info,
- sizeof(FOREIGN_KEY_INFO)));
- f_key_list->push_back(pf_key_info);
- foreign = UT_LIST_GET_NEXT(foreign_list, foreign);
- }
- mutex_exit_noninline(&(dict_sys->mutex));
- prebuilt->trx->op_info = (char*)"";
- DBUG_RETURN(0);
-}
-
-/*********************************************************************
-Checks if ALTER TABLE may change the storage engine of the table.
-Changing storage engines is not allowed for tables for which there
-are foreign key constraints (parent or child tables). */
-
-bool
-ha_innobase::can_switch_engines(void)
-/*=================================*/
-{
- row_prebuilt_t* prebuilt = (row_prebuilt_t*) innobase_prebuilt;
- bool can_switch;
-
- DBUG_ENTER("ha_innobase::can_switch_engines");
- prebuilt->trx->op_info =
- "determining if there are foreign key constraints";
- row_mysql_lock_data_dictionary(prebuilt->trx);
-
- can_switch = !UT_LIST_GET_FIRST(prebuilt->table->referenced_list)
- && !UT_LIST_GET_FIRST(prebuilt->table->foreign_list);
-
- row_mysql_unlock_data_dictionary(prebuilt->trx);
- prebuilt->trx->op_info = "";
-
- DBUG_RETURN(can_switch);
-}
-
-/***********************************************************************
-Checks if a table is referenced by a foreign key. The MySQL manual states that
-a REPLACE is either equivalent to an INSERT, or DELETE(s) + INSERT. Only a
-delete is then allowed internally to resolve a duplicate key conflict in
-REPLACE, not an update. */
-
-uint
-ha_innobase::referenced_by_foreign_key(void)
-/*========================================*/
- /* out: > 0 if referenced by a FOREIGN KEY */
-{
- row_prebuilt_t* prebuilt = (row_prebuilt_t*)innobase_prebuilt;
-
- if (dict_table_referenced_by_foreign_key(prebuilt->table)) {
-
- return(1);
- }
-
- return(0);
-}
-
-/***********************************************************************
-Frees the foreign key create info for a table stored in InnoDB, if it is
-non-NULL. */
-
-void
-ha_innobase::free_foreign_key_create_info(
-/*======================================*/
- char* str) /* in, own: create info string to free */
-{
- if (str) {
- my_free(str, MYF(0));
- }
-}
-
-/***********************************************************************
-Tells something additional to the handler about how to do things. */
-
-int
-ha_innobase::extra(
-/*===============*/
- /* out: 0 or error number */
- enum ha_extra_function operation)
- /* in: HA_EXTRA_RETRIEVE_ALL_COLS or some
- other flag */
-{
- row_prebuilt_t* prebuilt = (row_prebuilt_t*) innobase_prebuilt;
-
- /* Warning: since it is not sure that MySQL calls external_lock
- before calling this function, the trx field in prebuilt can be
- obsolete! */
-
- switch (operation) {
- case HA_EXTRA_FLUSH:
- if (prebuilt->blob_heap) {
- row_mysql_prebuilt_free_blob_heap(prebuilt);
- }
- break;
- case HA_EXTRA_RESET:
- if (prebuilt->blob_heap) {
- row_mysql_prebuilt_free_blob_heap(prebuilt);
- }
- prebuilt->keep_other_fields_on_keyread = 0;
- prebuilt->read_just_key = 0;
- break;
- case HA_EXTRA_RESET_STATE:
- prebuilt->keep_other_fields_on_keyread = 0;
- prebuilt->read_just_key = 0;
- break;
- case HA_EXTRA_NO_KEYREAD:
- prebuilt->read_just_key = 0;
- break;
- case HA_EXTRA_RETRIEVE_ALL_COLS:
- prebuilt->hint_need_to_fetch_extra_cols
- = ROW_RETRIEVE_ALL_COLS;
- break;
- case HA_EXTRA_RETRIEVE_PRIMARY_KEY:
- if (prebuilt->hint_need_to_fetch_extra_cols == 0) {
- prebuilt->hint_need_to_fetch_extra_cols
- = ROW_RETRIEVE_PRIMARY_KEY;
- }
- break;
- case HA_EXTRA_KEYREAD:
- prebuilt->read_just_key = 1;
- break;
- case HA_EXTRA_KEYREAD_PRESERVE_FIELDS:
- prebuilt->keep_other_fields_on_keyread = 1;
- break;
- default:/* Do nothing */
- ;
- }
-
- return(0);
-}
-
-/**********************************************************************
-MySQL calls this function at the start of each SQL statement inside LOCK
-TABLES. Inside LOCK TABLES the ::external_lock method does not work to
-mark SQL statement borders. Note also a special case: if a temporary table
-is created inside LOCK TABLES, MySQL has not called external_lock() at all
-on that table.
-MySQL-5.0 also calls this before each statement in an execution of a stored
-procedure. To make the execution more deterministic for binlogging, MySQL-5.0
-locks all tables involved in a stored procedure with full explicit table
-locks (thd->in_lock_tables is true in ::store_lock()) before executing the
-procedure. */
-
-int
-ha_innobase::start_stmt(
-/*====================*/
- /* out: 0 or error code */
- THD* thd, /* in: handle to the user thread */
- thr_lock_type lock_type)
-{
- row_prebuilt_t* prebuilt = (row_prebuilt_t*) innobase_prebuilt;
- trx_t* trx;
-
- update_thd(thd);
-
- trx = prebuilt->trx;
-
- /* Here we release the search latch and the InnoDB thread FIFO ticket
- if they were reserved. They should have been released already at the
- end of the previous statement, but because inside LOCK TABLES the
- lock count method does not work to mark the end of a SELECT statement,
- that may not be the case. We MUST release the search latch before an
- INSERT, for example. */
-
- innobase_release_stat_resources(trx);
-
- prebuilt->sql_stat_start = TRUE;
- prebuilt->hint_need_to_fetch_extra_cols = 0;
- prebuilt->read_just_key = 0;
- prebuilt->keep_other_fields_on_keyread = FALSE;
-
- if (!prebuilt->mysql_has_locked) {
- /* This handle is for a temporary table created inside
- this same LOCK TABLES; since MySQL does NOT call external_lock
- in this case, we must use x-row locks inside InnoDB to be
- prepared for an update of a row */
-
- prebuilt->select_lock_type = LOCK_X;
- } else {
- if (trx->isolation_level != TRX_ISO_SERIALIZABLE
- && thd->lex->sql_command == SQLCOM_SELECT
- && lock_type == TL_READ) {
-
- /* For other than temporary tables, we obtain
- no lock for consistent read (plain SELECT). */
-
- prebuilt->select_lock_type = LOCK_NONE;
- } else {
- /* Not a consistent read: restore the
- select_lock_type value. The value of
- stored_select_lock_type was decided in:
- 1) ::store_lock(),
- 2) ::external_lock(),
- 3) ::init_table_handle_for_HANDLER(), and
- 4) :.transactional_table_lock(). */
-
- prebuilt->select_lock_type =
- prebuilt->stored_select_lock_type;
- }
- }
-
- trx->detailed_error[0] = '\0';
-
- /* Set the MySQL flag to mark that there is an active transaction */
- if (trx->active_trans == 0) {
-
- innobase_register_trx_and_stmt(thd);
- trx->active_trans = 1;
- } else {
- innobase_register_stmt(thd);
- }
-
- return(0);
-}
-
-/**********************************************************************
-Maps a MySQL trx isolation level code to the InnoDB isolation level code */
-inline
-ulint
-innobase_map_isolation_level(
-/*=========================*/
- /* out: InnoDB isolation level */
- enum_tx_isolation iso) /* in: MySQL isolation level code */
-{
- switch(iso) {
- case ISO_REPEATABLE_READ: return(TRX_ISO_REPEATABLE_READ);
- case ISO_READ_COMMITTED: return(TRX_ISO_READ_COMMITTED);
- case ISO_SERIALIZABLE: return(TRX_ISO_SERIALIZABLE);
- case ISO_READ_UNCOMMITTED: return(TRX_ISO_READ_UNCOMMITTED);
- default: ut_a(0); return(0);
- }
-}
-
-/**********************************************************************
-As MySQL will execute an external lock for every new table it uses when it
-starts to process an SQL statement (an exception is when MySQL calls
-start_stmt for the handle) we can use this function to store the pointer to
-the THD in the handle. We will also use this function to communicate
-to InnoDB that a new SQL statement has started and that we must store a
-savepoint to our transaction handle, so that we are able to roll back
-the SQL statement in case of an error. */
-
-int
-ha_innobase::external_lock(
-/*=======================*/
- /* out: 0 */
- THD* thd, /* in: handle to the user thread */
- int lock_type) /* in: lock type */
-{
- row_prebuilt_t* prebuilt = (row_prebuilt_t*) innobase_prebuilt;
- trx_t* trx;
-
- DBUG_ENTER("ha_innobase::external_lock");
- DBUG_PRINT("enter",("lock_type: %d", lock_type));
-
- update_thd(thd);
-
- trx = prebuilt->trx;
-
- prebuilt->sql_stat_start = TRUE;
- prebuilt->hint_need_to_fetch_extra_cols = 0;
-
- prebuilt->read_just_key = 0;
- prebuilt->keep_other_fields_on_keyread = FALSE;
-
- if (lock_type == F_WRLCK) {
-
- /* If this is a SELECT, then it is in UPDATE TABLE ...
- or SELECT ... FOR UPDATE */
- prebuilt->select_lock_type = LOCK_X;
- prebuilt->stored_select_lock_type = LOCK_X;
- }
-
- if (lock_type != F_UNLCK) {
- /* MySQL is setting a new table lock */
-
- trx->detailed_error[0] = '\0';
-
- /* Set the MySQL flag to mark that there is an active
- transaction */
- if (trx->active_trans == 0) {
-
- innobase_register_trx_and_stmt(thd);
- trx->active_trans = 1;
- } else if (trx->n_mysql_tables_in_use == 0) {
- innobase_register_stmt(thd);
- }
-
- trx->n_mysql_tables_in_use++;
- prebuilt->mysql_has_locked = TRUE;
-
- if (trx->n_mysql_tables_in_use == 1) {
- trx->isolation_level = innobase_map_isolation_level(
- (enum_tx_isolation)
- thd->variables.tx_isolation);
-
- if (trx->isolation_level <= TRX_ISO_READ_COMMITTED
- && trx->global_read_view) {
-
- /* At low transaction isolation levels we let
- each consistent read set its own snapshot */
-
- read_view_close_for_mysql(trx);
- }
- }
-
- if (trx->isolation_level == TRX_ISO_SERIALIZABLE
- && prebuilt->select_lock_type == LOCK_NONE
- && (thd->options
- & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN))) {
-
- /* To get serializable execution, we let InnoDB
- conceptually add 'LOCK IN SHARE MODE' to all SELECTs
- which otherwise would have been consistent reads. An
- exception is consistent reads in the AUTOCOMMIT=1 mode:
- we know that they are read-only transactions, and they
- can be serialized also if performed as consistent
- reads. */
-
- prebuilt->select_lock_type = LOCK_S;
- prebuilt->stored_select_lock_type = LOCK_S;
- }
-
- /* Starting from 4.1.9, no InnoDB table lock is taken in LOCK
- TABLES if AUTOCOMMIT=1. It does not make much sense to acquire
- an InnoDB table lock if it is released immediately at the end
- of LOCK TABLES, and InnoDB's table locks in that case cause
- VERY easily deadlocks.
-
- We do not set InnoDB table locks if user has not explicitly
- requested a table lock. Note that thd->in_lock_tables
- can be TRUE on some cases e.g. at the start of a stored
- procedure call (SQLCOM_CALL). */
-
- if (prebuilt->select_lock_type != LOCK_NONE) {
-
- if (thd->in_lock_tables &&
- thd->lex->sql_command == SQLCOM_LOCK_TABLES &&
- thd->variables.innodb_table_locks &&
- (thd->options & OPTION_NOT_AUTOCOMMIT)) {
-
- ulint error;
- error = row_lock_table_for_mysql(prebuilt,
- NULL, 0);
-
- if (error != DB_SUCCESS) {
- error = convert_error_code_to_mysql(
- (int) error, user_thd);
- DBUG_RETURN((int) error);
- }
- }
-
- trx->mysql_n_tables_locked++;
- }
-
- DBUG_RETURN(0);
- }
-
- /* MySQL is releasing a table lock */
-
- trx->n_mysql_tables_in_use--;
- prebuilt->mysql_has_locked = FALSE;
-
- /* Release a possible FIFO ticket and search latch. Since we
- may reserve the kernel mutex, we have to release the search
- system latch first to obey the latching order. */
-
- innobase_release_stat_resources(trx);
-
- /* If the MySQL lock count drops to zero we know that the current SQL
- statement has ended */
-
- if (trx->n_mysql_tables_in_use == 0) {
-
- trx->mysql_n_tables_locked = 0;
- prebuilt->used_in_HANDLER = FALSE;
-
- if (!(thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN))) {
- if (trx->active_trans != 0) {
- innobase_commit(thd, TRUE);
- }
- } else {
- if (trx->isolation_level <= TRX_ISO_READ_COMMITTED
- && trx->global_read_view) {
-
- /* At low transaction isolation levels we let
- each consistent read set its own snapshot */
-
- read_view_close_for_mysql(trx);
- }
- }
- }
-
- DBUG_RETURN(0);
-}
-
-/**********************************************************************
-With this function MySQL request a transactional lock to a table when
-user issued query LOCK TABLES..WHERE ENGINE = InnoDB. */
-
-int
-ha_innobase::transactional_table_lock(
-/*==================================*/
- /* out: error code */
- THD* thd, /* in: handle to the user thread */
- int lock_type) /* in: lock type */
-{
- row_prebuilt_t* prebuilt = (row_prebuilt_t*) innobase_prebuilt;
- trx_t* trx;
-
- DBUG_ENTER("ha_innobase::transactional_table_lock");
- DBUG_PRINT("enter",("lock_type: %d", lock_type));
-
- /* We do not know if MySQL can call this function before calling
- external_lock(). To be safe, update the thd of the current table
- handle. */
-
- update_thd(thd);
-
- if (prebuilt->table->ibd_file_missing && !current_thd->tablespace_op) {
- ut_print_timestamp(stderr);
- fprintf(stderr, " InnoDB error:\n"
-"MySQL is trying to use a table handle but the .ibd file for\n"
-"table %s does not exist.\n"
-"Have you deleted the .ibd file from the database directory under\n"
-"the MySQL datadir?"
-"See http://dev.mysql.com/doc/refman/5.0/en/innodb-troubleshooting.html\n"
-"how you can resolve the problem.\n",
- prebuilt->table->name);
- DBUG_RETURN(HA_ERR_CRASHED);
- }
-
- trx = prebuilt->trx;
-
- prebuilt->sql_stat_start = TRUE;
- prebuilt->hint_need_to_fetch_extra_cols = 0;
-
- prebuilt->read_just_key = 0;
- prebuilt->keep_other_fields_on_keyread = FALSE;
-
- if (lock_type == F_WRLCK) {
- prebuilt->select_lock_type = LOCK_X;
- prebuilt->stored_select_lock_type = LOCK_X;
- } else if (lock_type == F_RDLCK) {
- prebuilt->select_lock_type = LOCK_S;
- prebuilt->stored_select_lock_type = LOCK_S;
- } else {
- ut_print_timestamp(stderr);
- fprintf(stderr, " InnoDB error:\n"
-"MySQL is trying to set transactional table lock with corrupted lock type\n"
-"to table %s, lock type %d does not exist.\n",
- prebuilt->table->name, lock_type);
- DBUG_RETURN(HA_ERR_CRASHED);
- }
-
- /* MySQL is setting a new transactional table lock */
-
- /* Set the MySQL flag to mark that there is an active transaction */
- if (trx->active_trans == 0) {
-
- innobase_register_trx_and_stmt(thd);
- trx->active_trans = 1;
- }
-
- if (thd->in_lock_tables && thd->variables.innodb_table_locks) {
- ulint error = DB_SUCCESS;
-
- error = row_lock_table_for_mysql(prebuilt, NULL, 0);
-
- if (error != DB_SUCCESS) {
- error = convert_error_code_to_mysql((int) error, user_thd);
- DBUG_RETURN((int) error);
- }
-
- if (thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)) {
-
- /* Store the current undo_no of the transaction
- so that we know where to roll back if we have
- to roll back the next SQL statement */
-
- trx_mark_sql_stat_end(trx);
- }
- }
-
- DBUG_RETURN(0);
-}
-
-/****************************************************************************
-Here we export InnoDB status variables to MySQL. */
-
-void
-innodb_export_status(void)
-/*======================*/
-{
- if (innodb_inited) {
- srv_export_innodb_status();
- }
-}
-
-/****************************************************************************
-Implements the SHOW INNODB STATUS command. Sends the output of the InnoDB
-Monitor to the client. */
-
-bool
-innodb_show_status(
-/*===============*/
- THD* thd) /* in: the MySQL query thread of the caller */
-{
- Protocol* protocol = thd->protocol;
- trx_t* trx;
- static const char truncated_msg[] = "... truncated...\n";
- const long MAX_STATUS_SIZE = 64000;
- ulint trx_list_start = ULINT_UNDEFINED;
- ulint trx_list_end = ULINT_UNDEFINED;
-
- DBUG_ENTER("innodb_show_status");
-
- if (have_innodb != SHOW_OPTION_YES) {
- my_message(ER_NOT_SUPPORTED_YET,
- "Cannot call SHOW INNODB STATUS because skip-innodb is defined",
- MYF(0));
- DBUG_RETURN(TRUE);
- }
-
- trx = check_trx_exists(thd);
-
- innobase_release_stat_resources(trx);
-
- /* We let the InnoDB Monitor to output at most MAX_STATUS_SIZE
- bytes of text. */
-
- long flen, usable_len;
- char* str;
-
- mutex_enter_noninline(&srv_monitor_file_mutex);
- rewind(srv_monitor_file);
- srv_printf_innodb_monitor(srv_monitor_file,
- &trx_list_start, &trx_list_end);
- flen = ftell(srv_monitor_file);
- os_file_set_eof(srv_monitor_file);
-
- if (flen < 0) {
- flen = 0;
- }
-
- if (flen > MAX_STATUS_SIZE) {
- usable_len = MAX_STATUS_SIZE;
- } else {
- usable_len = flen;
- }
-
- /* allocate buffer for the string, and
- read the contents of the temporary file */
-
- if (!(str = my_malloc(usable_len + 1, MYF(0))))
- {
- mutex_exit_noninline(&srv_monitor_file_mutex);
- DBUG_RETURN(TRUE);
- }
-
- rewind(srv_monitor_file);
- if (flen < MAX_STATUS_SIZE) {
- /* Display the entire output. */
- flen = (long) fread(str, 1, flen, srv_monitor_file);
- } else if (trx_list_end < (ulint) flen
- && trx_list_start < trx_list_end
- && trx_list_start + (flen - trx_list_end)
- < MAX_STATUS_SIZE - sizeof truncated_msg - 1) {
- /* Omit the beginning of the list of active transactions. */
- long len = (long) fread(str, 1, trx_list_start, srv_monitor_file);
- memcpy(str + len, truncated_msg, sizeof truncated_msg - 1);
- len += sizeof truncated_msg - 1;
- usable_len = (MAX_STATUS_SIZE - 1) - len;
- fseek(srv_monitor_file, flen - usable_len, SEEK_SET);
- len += (long) fread(str + len, 1, usable_len, srv_monitor_file);
- flen = len;
- } else {
- /* Omit the end of the output. */
- flen = (long) fread(str, 1, MAX_STATUS_SIZE - 1, srv_monitor_file);
- }
-
- mutex_exit_noninline(&srv_monitor_file_mutex);
-
- List<Item> field_list;
-
- field_list.push_back(new Item_empty_string("Status", flen));
-
- if (protocol->send_fields(&field_list, Protocol::SEND_NUM_ROWS |
- Protocol::SEND_EOF)) {
- my_free(str, MYF(0));
-
- DBUG_RETURN(TRUE);
- }
-
- protocol->prepare_for_resend();
- protocol->store(str, flen, system_charset_info);
- my_free(str, MYF(0));
-
- if (protocol->write()) {
-
- DBUG_RETURN(TRUE);
- }
- send_eof(thd);
-
- DBUG_RETURN(FALSE);
-}
-
-/****************************************************************************
-Implements the SHOW MUTEX STATUS command. . */
-
-bool
-innodb_mutex_show_status(
-/*===============*/
- THD* thd) /* in: the MySQL query thread of the caller */
-{
- Protocol *protocol= thd->protocol;
- List<Item> field_list;
- mutex_t* mutex;
-#ifdef UNIV_DEBUG
- ulint rw_lock_count= 0;
- ulint rw_lock_count_spin_loop= 0;
- ulint rw_lock_count_spin_rounds= 0;
- ulint rw_lock_count_os_wait= 0;
- ulint rw_lock_count_os_yield= 0;
- ulonglong rw_lock_wait_time= 0;
-#endif /* UNIV_DEBUG */
- DBUG_ENTER("innodb_mutex_show_status");
-
-#ifdef UNIV_DEBUG
- field_list.push_back(new Item_empty_string("Mutex", FN_REFLEN));
- field_list.push_back(new Item_empty_string("Module", FN_REFLEN));
- field_list.push_back(new Item_uint("Count", MY_INT64_NUM_DECIMAL_DIGITS));
- field_list.push_back(new Item_uint("Spin_waits", MY_INT64_NUM_DECIMAL_DIGITS));
- field_list.push_back(new Item_uint("Spin_rounds", MY_INT64_NUM_DECIMAL_DIGITS));
- field_list.push_back(new Item_uint("OS_waits", MY_INT64_NUM_DECIMAL_DIGITS));
- field_list.push_back(new Item_uint("OS_yields", MY_INT64_NUM_DECIMAL_DIGITS));
- field_list.push_back(new Item_uint("OS_waits_time", MY_INT64_NUM_DECIMAL_DIGITS));
-#else /* UNIV_DEBUG */
- field_list.push_back(new Item_empty_string("File", FN_REFLEN));
- field_list.push_back(new Item_uint("Line", MY_INT64_NUM_DECIMAL_DIGITS));
- field_list.push_back(new Item_uint("OS_waits", MY_INT64_NUM_DECIMAL_DIGITS));
-#endif /* UNIV_DEBUG */
-
- if (protocol->send_fields(&field_list,
- Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
- DBUG_RETURN(TRUE);
-
- mutex_enter_noninline(&mutex_list_mutex);
-
- mutex = UT_LIST_GET_FIRST(mutex_list);
-
- while ( mutex != NULL )
- {
-#ifdef UNIV_DEBUG
- if (mutex->mutex_type != 1)
- {
- if (mutex->count_using > 0)
- {
- protocol->prepare_for_resend();
- protocol->store(mutex->cmutex_name, system_charset_info);
- protocol->store(mutex->cfile_name, system_charset_info);
- protocol->store((ulonglong)mutex->count_using);
- protocol->store((ulonglong)mutex->count_spin_loop);
- protocol->store((ulonglong)mutex->count_spin_rounds);
- protocol->store((ulonglong)mutex->count_os_wait);
- protocol->store((ulonglong)mutex->count_os_yield);
- protocol->store((ulonglong)mutex->lspent_time/1000);
-
- if (protocol->write())
- {
- mutex_exit_noninline(&mutex_list_mutex);
- DBUG_RETURN(1);
- }
- }
- }
- else
- {
- rw_lock_count += mutex->count_using;
- rw_lock_count_spin_loop += mutex->count_spin_loop;
- rw_lock_count_spin_rounds += mutex->count_spin_rounds;
- rw_lock_count_os_wait += mutex->count_os_wait;
- rw_lock_count_os_yield += mutex->count_os_yield;
- rw_lock_wait_time += mutex->lspent_time;
- }
-#else /* UNIV_DEBUG */
- protocol->prepare_for_resend();
- protocol->store(mutex->cfile_name, system_charset_info);
- protocol->store((ulonglong)mutex->cline);
- protocol->store((ulonglong)mutex->count_os_wait);
-
- if (protocol->write())
- {
- mutex_exit_noninline(&mutex_list_mutex);
- DBUG_RETURN(1);
- }
-#endif /* UNIV_DEBUG */
-
- mutex = UT_LIST_GET_NEXT(list, mutex);
- }
-
- mutex_exit_noninline(&mutex_list_mutex);
-
-#ifdef UNIV_DEBUG
- protocol->prepare_for_resend();
- protocol->store("rw_lock_mutexes", system_charset_info);
- protocol->store("", system_charset_info);
- protocol->store((ulonglong)rw_lock_count);
- protocol->store((ulonglong)rw_lock_count_spin_loop);
- protocol->store((ulonglong)rw_lock_count_spin_rounds);
- protocol->store((ulonglong)rw_lock_count_os_wait);
- protocol->store((ulonglong)rw_lock_count_os_yield);
- protocol->store((ulonglong)rw_lock_wait_time/1000);
-
- if (protocol->write())
- {
- DBUG_RETURN(1);
- }
-#endif /* UNIV_DEBUG */
-
- send_eof(thd);
- DBUG_RETURN(FALSE);
-}
-
-/****************************************************************************
- Handling the shared INNOBASE_SHARE structure that is needed to provide table
- locking.
-****************************************************************************/
-
-static mysql_byte* innobase_get_key(INNOBASE_SHARE *share,uint *length,
- my_bool not_used __attribute__((unused)))
-{
- *length=share->table_name_length;
- return (mysql_byte*) share->table_name;
-}
-
-static INNOBASE_SHARE *get_share(const char *table_name)
-{
- INNOBASE_SHARE *share;
- pthread_mutex_lock(&innobase_share_mutex);
- uint length=(uint) strlen(table_name);
-
- if (!(share=(INNOBASE_SHARE*) hash_search(&innobase_open_tables,
- (mysql_byte*) table_name,
- length))) {
-
- share = (INNOBASE_SHARE *) my_malloc(sizeof(*share)+length+1,
- MYF(MY_FAE | MY_ZEROFILL));
-
- share->table_name_length=length;
- share->table_name=(char*) (share+1);
- strmov(share->table_name,table_name);
-
- if (my_hash_insert(&innobase_open_tables,
- (mysql_byte*) share)) {
- pthread_mutex_unlock(&innobase_share_mutex);
- my_free((gptr) share,0);
-
- return 0;
- }
-
- thr_lock_init(&share->lock);
- pthread_mutex_init(&share->mutex,MY_MUTEX_INIT_FAST);
- }
-
- share->use_count++;
- pthread_mutex_unlock(&innobase_share_mutex);
-
- return share;
-}
-
-static void free_share(INNOBASE_SHARE *share)
-{
- pthread_mutex_lock(&innobase_share_mutex);
- if (!--share->use_count)
- {
- hash_delete(&innobase_open_tables, (mysql_byte*) share);
- thr_lock_delete(&share->lock);
- pthread_mutex_destroy(&share->mutex);
- my_free((gptr) share, MYF(0));
- }
- pthread_mutex_unlock(&innobase_share_mutex);
-}
-
-/*********************************************************************
-Converts a MySQL table lock stored in the 'lock' field of the handle to
-a proper type before storing pointer to the lock into an array of pointers.
-MySQL also calls this if it wants to reset some table locks to a not-locked
-state during the processing of an SQL query. An example is that during a
-SELECT the read lock is released early on the 'const' tables where we only
-fetch one row. MySQL does not call this when it releases all locks at the
-end of an SQL statement. */
-
-THR_LOCK_DATA**
-ha_innobase::store_lock(
-/*====================*/
- /* out: pointer to the next
- element in the 'to' array */
- THD* thd, /* in: user thread handle */
- THR_LOCK_DATA** to, /* in: pointer to an array
- of pointers to lock structs;
- pointer to the 'lock' field
- of current handle is stored
- next to this array */
- enum thr_lock_type lock_type) /* in: lock type to store in
- 'lock'; this may also be
- TL_IGNORE */
-{
- row_prebuilt_t* prebuilt = (row_prebuilt_t*) innobase_prebuilt;
- trx_t* trx;
-
- /* Note that trx in this function is NOT necessarily prebuilt->trx
- because we call update_thd() later, in ::external_lock()! Failure to
- understand this caused a serious memory corruption bug in 5.1.11. */
-
- trx = check_trx_exists(thd);
-
- /* NOTE: MySQL can call this function with lock 'type' TL_IGNORE!
- Be careful to ignore TL_IGNORE if we are going to do something with
- only 'real' locks! */
-
- if ((lock_type == TL_READ && thd->in_lock_tables) ||
- (lock_type == TL_READ_HIGH_PRIORITY && thd->in_lock_tables) ||
- lock_type == TL_READ_WITH_SHARED_LOCKS ||
- lock_type == TL_READ_NO_INSERT ||
- (thd->lex->sql_command != SQLCOM_SELECT
- && lock_type != TL_IGNORE)) {
-
- /* The OR cases above are in this order:
- 1) MySQL is doing LOCK TABLES ... READ LOCAL, or we
- are processing a stored procedure or function, or
- 2) (we do not know when TL_READ_HIGH_PRIORITY is used), or
- 3) this is a SELECT ... IN SHARE MODE, or
- 4) we are doing a complex SQL statement like
- INSERT INTO ... SELECT ... and the logical logging (MySQL
- binlog) requires the use of a locking read, or
- MySQL is doing LOCK TABLES ... READ.
- 5) we let InnoDB do locking reads for all SQL statements that
- are not simple SELECTs; note that select_lock_type in this
- case may get strengthened in ::external_lock() to LOCK_X.
- Note that we MUST use a locking read in all data modifying
- SQL statements, because otherwise the execution would not be
- serializable, and also the results from the update could be
- unexpected if an obsolete consistent read view would be
- used. */
-
- if (srv_locks_unsafe_for_binlog &&
- trx->isolation_level != TRX_ISO_SERIALIZABLE &&
- (lock_type == TL_READ || lock_type == TL_READ_NO_INSERT) &&
- (thd->lex->sql_command == SQLCOM_INSERT_SELECT ||
- thd->lex->sql_command == SQLCOM_UPDATE ||
- thd->lex->sql_command == SQLCOM_CREATE_TABLE)) {
-
- /* In case we have innobase_locks_unsafe_for_binlog
- option set and isolation level of the transaction
- is not set to serializable and MySQL is doing
- INSERT INTO...SELECT or UPDATE ... = (SELECT ...) or
- CREATE ... SELECT... without FOR UPDATE or
- IN SHARE MODE in select, then we use consistent
- read for select. */
-
- prebuilt->select_lock_type = LOCK_NONE;
- prebuilt->stored_select_lock_type = LOCK_NONE;
- } else if (thd->lex->sql_command == SQLCOM_CHECKSUM) {
- /* Use consistent read for checksum table */
-
- prebuilt->select_lock_type = LOCK_NONE;
- prebuilt->stored_select_lock_type = LOCK_NONE;
- } else {
- prebuilt->select_lock_type = LOCK_S;
- prebuilt->stored_select_lock_type = LOCK_S;
- }
-
- } else if (lock_type != TL_IGNORE) {
-
- /* We set possible LOCK_X value in external_lock, not yet
- here even if this would be SELECT ... FOR UPDATE */
-
- prebuilt->select_lock_type = LOCK_NONE;
- prebuilt->stored_select_lock_type = LOCK_NONE;
- }
-
- if (lock_type != TL_IGNORE && lock.type == TL_UNLOCK) {
-
- /* Starting from 5.0.7, we weaken also the table locks
- set at the start of a MySQL stored procedure call, just like
- we weaken the locks set at the start of an SQL statement.
- MySQL does set thd->in_lock_tables TRUE there, but in reality
- we do not need table locks to make the execution of a
- single transaction stored procedure call deterministic
- (if it does not use a consistent read). */
-
- if (lock_type == TL_READ
- && thd->lex->sql_command == SQLCOM_LOCK_TABLES) {
- /* We come here if MySQL is processing LOCK TABLES
- ... READ LOCAL. MyISAM under that table lock type
- reads the table as it was at the time the lock was
- granted (new inserts are allowed, but not seen by the
- reader). To get a similar effect on an InnoDB table,
- we must use LOCK TABLES ... READ. We convert the lock
- type here, so that for InnoDB, READ LOCAL is
- equivalent to READ. This will change the InnoDB
- behavior in mysqldump, so that dumps of InnoDB tables
- are consistent with dumps of MyISAM tables. */
-
- lock_type = TL_READ_NO_INSERT;
- }
-
- /* If we are not doing a LOCK TABLE, DISCARD/IMPORT
- TABLESPACE or TRUNCATE TABLE then allow multiple
- writers. Note that ALTER TABLE uses a TL_WRITE_ALLOW_READ
- < TL_WRITE_CONCURRENT_INSERT.
-
- We especially allow multiple writers if MySQL is at the
- start of a stored procedure call (SQLCOM_CALL) or a
- stored function call (MySQL does have thd->in_lock_tables
- TRUE there). */
-
- if ((lock_type >= TL_WRITE_CONCURRENT_INSERT
- && lock_type <= TL_WRITE)
- && !(thd->in_lock_tables
- && thd->lex->sql_command == SQLCOM_LOCK_TABLES)
- && !thd->tablespace_op
- && thd->lex->sql_command != SQLCOM_TRUNCATE
- && thd->lex->sql_command != SQLCOM_OPTIMIZE
- && thd->lex->sql_command != SQLCOM_CREATE_TABLE) {
-
- lock_type = TL_WRITE_ALLOW_WRITE;
- }
-
- /* In queries of type INSERT INTO t1 SELECT ... FROM t2 ...
- MySQL would use the lock TL_READ_NO_INSERT on t2, and that
- would conflict with TL_WRITE_ALLOW_WRITE, blocking all inserts
- to t2. Convert the lock to a normal read lock to allow
- concurrent inserts to t2.
-
- We especially allow concurrent inserts if MySQL is at the
- start of a stored procedure call (SQLCOM_CALL)
- (MySQL does have thd->in_lock_tables TRUE there). */
-
- if (lock_type == TL_READ_NO_INSERT
- && thd->lex->sql_command != SQLCOM_LOCK_TABLES) {
-
- lock_type = TL_READ;
- }
-
- lock.type = lock_type;
- }
-
- *to++= &lock;
-
- return(to);
-}
-
-/***********************************************************************
-This function initializes the auto-inc counter if it has not been
-initialized yet. This function does not change the value of the auto-inc
-counter if it already has been initialized. In parameter ret returns
-the value of the auto-inc counter. */
-
-int
-ha_innobase::innobase_read_and_init_auto_inc(
-/*=========================================*/
- /* out: 0 or error code: deadlock or lock wait
- timeout */
- longlong* ret) /* out: auto-inc value */
-{
- row_prebuilt_t* prebuilt = (row_prebuilt_t*) innobase_prebuilt;
- longlong auto_inc;
- ulint old_select_lock_type;
- ibool trx_was_not_started = FALSE;
- int error;
-
- ut_a(prebuilt);
- ut_a(prebuilt->trx ==
- (trx_t*) current_thd->ha_data[innobase_hton.slot]);
- ut_a(prebuilt->table);
-
- if (prebuilt->trx->conc_state == TRX_NOT_STARTED) {
- trx_was_not_started = TRUE;
- }
-
- /* In case MySQL calls this in the middle of a SELECT query, release
- possible adaptive hash latch to avoid deadlocks of threads */
-
- trx_search_latch_release_if_reserved(prebuilt->trx);
-
- auto_inc = dict_table_autoinc_read(prebuilt->table);
-
- if (auto_inc != 0) {
- /* Already initialized */
- *ret = auto_inc;
-
- error = 0;
-
- goto func_exit_early;
- }
-
- error = row_lock_table_autoinc_for_mysql(prebuilt);
-
- if (error != DB_SUCCESS) {
- error = convert_error_code_to_mysql(error, user_thd);
-
- goto func_exit_early;
- }
-
- /* Check again if someone has initialized the counter meanwhile */
- auto_inc = dict_table_autoinc_read(prebuilt->table);
-
- if (auto_inc != 0) {
- *ret = auto_inc;
-
- error = 0;
-
- goto func_exit_early;
- }
-
- (void) extra(HA_EXTRA_KEYREAD);
- index_init(table->s->next_number_index);
-
- /* Starting from 5.0.9, we use a consistent read to read the auto-inc
- column maximum value. This eliminates the spurious deadlocks caused
- by the row X-lock that we previously used. Note the following flaw
- in our algorithm: if some other user meanwhile UPDATEs the auto-inc
- column, our consistent read will not return the largest value. We
- accept this flaw, since the deadlocks were a bigger trouble. */
-
- /* Fetch all the columns in the key */
-
- prebuilt->hint_need_to_fetch_extra_cols = ROW_RETRIEVE_ALL_COLS;
-
- old_select_lock_type = prebuilt->select_lock_type;
- prebuilt->select_lock_type = LOCK_NONE;
-
- /* Eliminate an InnoDB error print that happens when we try to SELECT
- from a table when no table has been locked in ::external_lock(). */
- prebuilt->trx->n_mysql_tables_in_use++;
-
- /* Since we will perform a MySQL SELECT query to determine the
- auto-inc value, set prebuilt->sql_stat_start = TRUE so that it
- is performed like any normal SELECT, regardless of the context
- we come here. */
- prebuilt->sql_stat_start = TRUE;
-
- error = index_last(table->record[1]);
-
- prebuilt->trx->n_mysql_tables_in_use--;
- prebuilt->select_lock_type = old_select_lock_type;
-
- if (error) {
- if (error == HA_ERR_END_OF_FILE) {
- /* The table was empty, initialize to 1 */
- auto_inc = 1;
-
- error = 0;
- } else {
- /* This should not happen in a consistent read */
- sql_print_error("Consistent read of auto-inc column "
- "returned %lu", (ulong) error);
- auto_inc = -1;
-
- goto func_exit;
- }
- } else {
- /* Initialize to max(col) + 1; we use
- 'found_next_number_field' below because MySQL in SHOW TABLE
- STATUS does not seem to set 'next_number_field'. The comment
- in table.h says that 'next_number_field' is set when it is
- 'active'. */
-
- auto_inc = (longlong) table->found_next_number_field->
- val_int_offset(table->s->rec_buff_length) + 1;
- }
-
- dict_table_autoinc_initialize(prebuilt->table, auto_inc);
-
-func_exit:
- (void) extra(HA_EXTRA_NO_KEYREAD);
-
- index_end();
-
- *ret = auto_inc;
-
-func_exit_early:
- /* Since MySQL does not seem to call autocommit after SHOW TABLE
- STATUS (even if we would register the trx here), we commit our
- transaction here if it was started here. This is to eliminate a
- dangling transaction. If the user had AUTOCOMMIT=0, then SHOW
- TABLE STATUS does leave a dangling transaction if the user does not
- himself call COMMIT. */
-
- if (trx_was_not_started) {
-
- innobase_commit_low(prebuilt->trx);
- }
-
- return(error);
-}
-
-/***********************************************************************
-This function initializes the auto-inc counter if it has not been
-initialized yet. This function does not change the value of the auto-inc
-counter if it already has been initialized. Returns the value of the
-auto-inc counter. */
-
-ulonglong
-ha_innobase::get_auto_increment()
-/*=============================*/
- /* out: auto-increment column value, -1 if error
- (deadlock or lock wait timeout) */
-{
- longlong nr;
- int error;
-
- error = innobase_read_and_init_auto_inc(&nr);
-
- if (error) {
- /* This should never happen in the current (5.0.6) code, since
- we call this function only after the counter has been
- initialized. */
-
- ut_print_timestamp(stderr);
- sql_print_error("Error %lu in ::get_auto_increment()",
- (ulong) error);
- return(~(ulonglong) 0);
- }
-
- return((ulonglong) nr);
-}
-
-/* See comment in handler.h */
-int
-ha_innobase::reset_auto_increment(ulonglong value)
-{
- DBUG_ENTER("ha_innobase::reset_auto_increment");
-
- row_prebuilt_t* prebuilt = (row_prebuilt_t*) innobase_prebuilt;
- int error;
-
- error = row_lock_table_autoinc_for_mysql(prebuilt);
-
- if (error != DB_SUCCESS) {
- error = convert_error_code_to_mysql(error, user_thd);
-
- DBUG_RETURN(error);
- }
-
- dict_table_autoinc_initialize(prebuilt->table, value);
-
- DBUG_RETURN(0);
-}
-
-/* See comment in handler.cc */
-bool
-ha_innobase::get_error_message(int error, String *buf)
-{
- trx_t* trx = check_trx_exists(current_thd);
-
- buf->copy(trx->detailed_error, (uint) strlen(trx->detailed_error),
- system_charset_info);
-
- return FALSE;
-}
-
-/***********************************************************************
-Compares two 'refs'. A 'ref' is the (internal) primary key value of the row.
-If there is no explicitly declared non-null unique key or a primary key, then
-InnoDB internally uses the row id as the primary key. */
-
-int
-ha_innobase::cmp_ref(
-/*=================*/
- /* out: < 0 if ref1 < ref2, 0 if equal, else
- > 0 */
- const mysql_byte* ref1, /* in: an (internal) primary key value in the
- MySQL key value format */
- const mysql_byte* ref2) /* in: an (internal) primary key value in the
- MySQL key value format */
-{
- row_prebuilt_t* prebuilt = (row_prebuilt_t*) innobase_prebuilt;
- enum_field_types mysql_type;
- Field* field;
- KEY_PART_INFO* key_part;
- KEY_PART_INFO* key_part_end;
- uint len1;
- uint len2;
- int result;
-
- if (prebuilt->clust_index_was_generated) {
- /* The 'ref' is an InnoDB row id */
-
- return(memcmp(ref1, ref2, DATA_ROW_ID_LEN));
- }
-
- /* Do a type-aware comparison of primary key fields. PK fields
- are always NOT NULL, so no checks for NULL are performed. */
-
- 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;
-
- for (; key_part != key_part_end; ++key_part) {
- field = key_part->field;
- mysql_type = field->type();
-
- if (mysql_type == FIELD_TYPE_TINY_BLOB
- || mysql_type == FIELD_TYPE_MEDIUM_BLOB
- || mysql_type == FIELD_TYPE_BLOB
- || mysql_type == FIELD_TYPE_LONG_BLOB) {
-
- /* In the MySQL key value format, a column prefix of
- a BLOB is preceded by a 2-byte length field */
-
- len1 = innobase_read_from_2_little_endian(ref1);
- len2 = innobase_read_from_2_little_endian(ref2);
-
- ref1 += 2;
- ref2 += 2;
- result = ((Field_blob*)field)->cmp(
- (const char*)ref1, len1,
- (const char*)ref2, len2);
- } else {
- result = field->key_cmp(ref1, ref2);
- }
-
- if (result) {
-
- return(result);
- }
-
- ref1 += key_part->store_length;
- ref2 += key_part->store_length;
- }
-
- return(0);
-}
-
-char*
-ha_innobase::get_mysql_bin_log_name()
-{
- return(trx_sys_mysql_bin_log_name);
-}
-
-ulonglong
-ha_innobase::get_mysql_bin_log_pos()
-{
- /* trx... is ib_longlong, which is a typedef for a 64-bit integer
- (__int64 or longlong) so it's ok to cast it to ulonglong. */
-
- return(trx_sys_mysql_bin_log_pos);
-}
-
-extern "C" {
-/**********************************************************************
-This function is used to find the storage length in bytes of the first n
-characters for prefix indexes using a multibyte character set. The function
-finds charset information and returns length of prefix_len characters in the
-index field in bytes.
-
-NOTE: the prototype of this function is copied to data0type.c! If you change
-this function, you MUST change also data0type.c! */
-
-ulint
-innobase_get_at_most_n_mbchars(
-/*===========================*/
- /* out: number of bytes occupied by the first
- n characters */
- ulint charset_id, /* in: character set id */
- ulint prefix_len, /* in: prefix length in bytes of the index
- (this has to be divided by mbmaxlen to get the
- number of CHARACTERS n in the prefix) */
- ulint data_len, /* in: length of the string in bytes */
- const char* str) /* in: character string */
-{
- ulint char_length; /* character length in bytes */
- ulint n_chars; /* number of characters in prefix */
- CHARSET_INFO* charset; /* charset used in the field */
-
- charset = get_charset((uint) charset_id, MYF(MY_WME));
-
- ut_ad(charset);
- ut_ad(charset->mbmaxlen);
-
- /* Calculate how many characters at most the prefix index contains */
-
- n_chars = prefix_len / charset->mbmaxlen;
-
- /* If the charset is multi-byte, then we must find the length of the
- first at most n chars in the string. If the string contains less
- characters than n, then we return the length to the end of the last
- character. */
-
- if (charset->mbmaxlen > 1) {
- /* my_charpos() returns the byte length of the first n_chars
- characters, or a value bigger than the length of str, if
- there were not enough full characters in str.
-
- Why does the code below work:
- Suppose that we are looking for n UTF-8 characters.
-
- 1) If the string is long enough, then the prefix contains at
- least n complete UTF-8 characters + maybe some extra
- characters + an incomplete UTF-8 character. No problem in
- this case. The function returns the pointer to the
- end of the nth character.
-
- 2) If the string is not long enough, then the string contains
- the complete value of a column, that is, only complete UTF-8
- characters, and we can store in the column prefix index the
- whole string. */
-
- char_length = my_charpos(charset, str,
- str + data_len, (int) n_chars);
- if (char_length > data_len) {
- char_length = data_len;
- }
- } else {
- if (data_len < prefix_len) {
- char_length = data_len;
- } else {
- char_length = prefix_len;
- }
- }
-
- return(char_length);
-}
-}
-
-extern "C" {
-/**********************************************************************
-This function returns true if
-
-1) SQL-query in the current thread
-is either REPLACE or LOAD DATA INFILE REPLACE.
-
-2) SQL-query in the current thread
-is INSERT ON DUPLICATE KEY UPDATE.
-
-NOTE that /mysql/innobase/row/row0ins.c must contain the
-prototype for this function ! */
-
-ibool
-innobase_query_is_update(void)
-/*==========================*/
-{
- THD* thd;
-
- thd = (THD *)innobase_current_thd();
-
- if (thd->lex->sql_command == SQLCOM_REPLACE ||
- thd->lex->sql_command == SQLCOM_REPLACE_SELECT ||
- (thd->lex->sql_command == SQLCOM_LOAD &&
- thd->lex->duplicates == DUP_REPLACE)) {
-
- return(1);
- }
-
- if (thd->lex->sql_command == SQLCOM_INSERT &&
- thd->lex->duplicates == DUP_UPDATE) {
-
- return(1);
- }
-
- return(0);
-}
-}
-
-/***********************************************************************
-This function is used to prepare X/Open XA distributed transaction */
-
-int
-innobase_xa_prepare(
-/*================*/
- /* out: 0 or error number */
- THD* thd, /* in: handle to the MySQL thread of the user
- whose XA transaction should be prepared */
- bool all) /* in: TRUE - commit transaction
- FALSE - the current SQL statement ended */
-{
- int error = 0;
- trx_t* trx = check_trx_exists(thd);
-
- if (thd->lex->sql_command != SQLCOM_XA_PREPARE) {
-
- /* For ibbackup to work the order of transactions in binlog
- and InnoDB must be the same. Consider the situation
-
- thread1> prepare; write to binlog; ...
- <context switch>
- thread2> prepare; write to binlog; commit
- thread1> ... commit
-
- To ensure this will not happen we're taking the mutex on
- prepare, and releasing it on commit.
-
- Note: only do it for normal commits, done via ha_commit_trans.
- If 2pc protocol is executed by external transaction
- coordinator, it will be just a regular MySQL client
- executing XA PREPARE and XA COMMIT commands.
- In this case we cannot know how many minutes or hours
- will be between XA PREPARE and XA COMMIT, and we don't want
- to block for undefined period of time.
- */
- pthread_mutex_lock(&prepare_commit_mutex);
- trx->active_trans = 2;
- }
-
- if (!thd->variables.innodb_support_xa) {
-
- return(0);
- }
-
- trx->xid=thd->transaction.xid_state.xid;
-
- /* Release a possible FIFO ticket and search latch. Since we will
- reserve the kernel mutex, we have to release the search system latch
- first to obey the latching order. */
-
- innobase_release_stat_resources(trx);
-
- if (trx->active_trans == 0 && trx->conc_state != TRX_NOT_STARTED) {
-
- sql_print_error("trx->active_trans == 0, but trx->conc_state != "
- "TRX_NOT_STARTED");
- }
-
- if (all
- || (!(thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)))) {
-
- /* We were instructed to prepare the whole transaction, or
- this is an SQL statement end and autocommit is on */
-
- ut_ad(trx->active_trans);
-
- error = (int) trx_prepare_for_mysql(trx);
- } else {
- /* We just mark the SQL statement ended and do not do a
- transaction prepare */
-
- if (trx->auto_inc_lock) {
- /* If we had reserved the auto-inc lock for some
- table in this SQL statement we release it now */
-
- row_unlock_table_autoinc_for_mysql(trx);
- }
- /* Store the current undo_no of the transaction so that we
- know where to roll back if we have to roll back the next
- SQL statement */
-
- trx_mark_sql_stat_end(trx);
- }
-
- /* Tell the InnoDB server that there might be work for utility
- threads: */
-
- srv_active_wake_master_thread();
-
- return error;
-}
-
-/***********************************************************************
-This function is used to recover X/Open XA distributed transactions */
-
-int
-innobase_xa_recover(
-/*================*/
- /* out: number of prepared transactions
- stored in xid_list */
- XID* xid_list, /* in/out: prepared transactions */
- uint len) /* in: number of slots in xid_list */
-{
- if (len == 0 || xid_list == NULL) {
-
- return(0);
- }
-
- return(trx_recover_for_mysql(xid_list, len));
-}
-
-/***********************************************************************
-This function is used to commit one X/Open XA distributed transaction
-which is in the prepared state */
-
-int
-innobase_commit_by_xid(
-/*===================*/
- /* out: 0 or error number */
- XID* xid) /* in: X/Open XA transaction identification */
-{
- trx_t* trx;
-
- trx = trx_get_trx_by_xid(xid);
-
- if (trx) {
- innobase_commit_low(trx);
-
- return(XA_OK);
- } else {
- return(XAER_NOTA);
- }
-}
-
-/***********************************************************************
-This function is used to rollback one X/Open XA distributed transaction
-which is in the prepared state */
-
-int
-innobase_rollback_by_xid(
-/*=====================*/
- /* out: 0 or error number */
- XID *xid) /* in: X/Open XA transaction identification */
-{
- trx_t* trx;
-
- trx = trx_get_trx_by_xid(xid);
-
- if (trx) {
- return(innobase_rollback_trx(trx));
- } else {
- return(XAER_NOTA);
- }
-}
-
-/***********************************************************************
-Create a consistent view for a cursor based on current transaction
-which is created if the corresponding MySQL thread still lacks one.
-This consistent view is then used inside of MySQL when accessing records
-using a cursor. */
-
-void*
-innobase_create_cursor_view(void)
-/*=============================*/
- /* out: Pointer to cursor view or NULL */
-{
- return(read_cursor_view_create_for_mysql(
- check_trx_exists(current_thd)));
-}
-
-/***********************************************************************
-Close the given consistent cursor view of a transaction and restore
-global read view to a transaction read view. Transaction is created if the
-corresponding MySQL thread still lacks one. */
-
-void
-innobase_close_cursor_view(
-/*=======================*/
- void* curview)/* in: Consistent read view to be closed */
-{
- read_cursor_view_close_for_mysql(check_trx_exists(current_thd),
- (cursor_view_t*) curview);
-}
-
-/***********************************************************************
-Set the given consistent cursor view to a transaction which is created
-if the corresponding MySQL thread still lacks one. If the given
-consistent cursor view is NULL global read view of a transaction is
-restored to a transaction read view. */
-
-void
-innobase_set_cursor_view(
-/*=====================*/
- void* curview)/* in: Consistent cursor view to be set */
-{
- read_cursor_set_for_mysql(check_trx_exists(current_thd),
- (cursor_view_t*) curview);
-}
-
-#endif /* HAVE_INNOBASE_DB */
diff --git a/sql/ha_innodb.h b/sql/ha_innodb.h
deleted file mode 100644
index 3db983901b3..00000000000
--- a/sql/ha_innodb.h
+++ /dev/null
@@ -1,338 +0,0 @@
-/* Copyright (C) 2000-2005 MySQL AB && Innobase Oy
-
- This 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 is based on ha_berkeley.h of MySQL distribution
-
- This file defines the Innodb handler: the interface between MySQL and
- Innodb
-*/
-
-#ifdef USE_PRAGMA_INTERFACE
-#pragma interface /* gcc class implementation */
-#endif
-
-typedef struct st_innobase_share {
- THR_LOCK lock;
- pthread_mutex_t mutex;
- char *table_name;
- uint table_name_length,use_count;
-} INNOBASE_SHARE;
-
-
-my_bool innobase_query_caching_of_table_permitted(THD* thd, char* full_name,
- uint full_name_len,
- ulonglong *unused);
-
-/* The class defining a handle to an Innodb table */
-class ha_innobase: public handler
-{
- void* innobase_prebuilt;/* (row_prebuilt_t*) prebuilt
- struct in InnoDB, used to save
- CPU time with prebuilt data
- structures*/
- THD* user_thd; /* the thread handle of the user
- currently using the handle; this is
- set in external_lock function */
- query_id_t last_query_id; /* the latest query id where the
- handle was used */
- THR_LOCK_DATA lock;
- INNOBASE_SHARE *share;
-
- byte* upd_buff; /* buffer used in updates */
- byte* key_val_buff; /* buffer used in converting
- search key values from MySQL format
- to Innodb format */
- ulong upd_and_key_val_buff_len;
- /* the length of each of the previous
- two buffers */
- ulong int_table_flags;
- uint primary_key;
- ulong start_of_scan; /* this is set to 1 when we are
- starting a table scan but have not
- yet fetched any row, else 0 */
- uint last_match_mode;/* match mode of the latest search:
- ROW_SEL_EXACT, ROW_SEL_EXACT_PREFIX,
- or undefined */
- uint num_write_row; /* number of write_row() calls */
-
- uint store_key_val_for_row(uint keynr, char* buff, uint buff_len,
- const byte* record);
- int update_thd(THD* thd);
- int change_active_index(uint keynr);
- int general_fetch(byte* buf, uint direction, uint match_mode);
- int innobase_read_and_init_auto_inc(longlong* ret);
-
- /* Init values for the class: */
- public:
- ha_innobase(TABLE *table_arg);
- ~ha_innobase() {}
- /*
- Get the row type from the storage engine. If this method returns
- ROW_TYPE_NOT_USED, the information in HA_CREATE_INFO should be used.
- */
- enum row_type get_row_type() const;
-
- const char* table_type() const { return("InnoDB");}
- const char *index_type(uint key_number) { return "BTREE"; }
- const char** bas_ext() const;
- ulong table_flags() const { return int_table_flags; }
- ulong index_flags(uint idx, uint part, bool all_parts) const
- {
- return (HA_READ_NEXT |
- HA_READ_PREV |
- HA_READ_ORDER |
- HA_READ_RANGE |
- HA_KEYREAD_ONLY);
- }
- uint max_supported_keys() const { return MAX_KEY; }
- /* An InnoDB page must store >= 2 keys;
- a secondary key record must also contain the
- primary key value:
- max key length is therefore set to slightly
- less than 1 / 4 of page size which is 16 kB;
- but currently MySQL does not work with keys
- whose size is > MAX_KEY_LENGTH */
- uint max_supported_key_length() const { return 3500; }
- uint max_supported_key_part_length() const;
- const key_map *keys_to_use_for_scanning() { return &key_map_full; }
- bool has_transactions() { return 1;}
-
- int open(const char *name, int mode, uint test_if_locked);
- int close(void);
- double scan_time();
- double read_time(uint index, uint ranges, ha_rows rows);
-
- int write_row(byte * buf);
- int update_row(const byte * old_data, byte * new_data);
- int delete_row(const byte * buf);
- void unlock_row();
-
- int index_init(uint index);
- int index_end();
- int index_read(byte * buf, const byte * key,
- uint key_len, enum ha_rkey_function find_flag);
- int index_read_idx(byte * buf, uint index, const byte * key,
- uint key_len, enum ha_rkey_function find_flag);
- int index_read_last(byte * buf, const byte * key, uint key_len);
- int index_next(byte * buf);
- int index_next_same(byte * buf, const byte *key, uint keylen);
- int index_prev(byte * buf);
- int index_first(byte * buf);
- int index_last(byte * buf);
-
- int rnd_init(bool scan);
- int rnd_end();
- int rnd_next(byte *buf);
- int rnd_pos(byte * buf, byte *pos);
-
- void position(const byte *record);
- int info(uint);
- int analyze(THD* thd,HA_CHECK_OPT* check_opt);
- int optimize(THD* thd,HA_CHECK_OPT* check_opt);
- int discard_or_import_tablespace(my_bool discard);
- int extra(enum ha_extra_function operation);
- int external_lock(THD *thd, int lock_type);
- int transactional_table_lock(THD *thd, int lock_type);
- int start_stmt(THD *thd, thr_lock_type lock_type);
-
- void position(byte *record);
- ha_rows records_in_range(uint inx, key_range *min_key, key_range
- *max_key);
- ha_rows estimate_rows_upper_bound();
-
- void update_create_info(HA_CREATE_INFO* create_info);
- int create(const char *name, register TABLE *form,
- HA_CREATE_INFO *create_info);
- int delete_all_rows();
- int delete_table(const char *name);
- int rename_table(const char* from, const char* to);
- int check(THD* thd, HA_CHECK_OPT* check_opt);
- char* update_table_comment(const char* comment);
- char* get_foreign_key_create_info();
- int get_foreign_key_list(THD *thd, List<FOREIGN_KEY_INFO> *f_key_list);
- bool can_switch_engines();
- uint referenced_by_foreign_key();
- void free_foreign_key_create_info(char* str);
- THR_LOCK_DATA **store_lock(THD *thd, THR_LOCK_DATA **to,
- enum thr_lock_type lock_type);
- void init_table_handle_for_HANDLER();
- ulonglong get_auto_increment();
- int reset_auto_increment(ulonglong value);
-
- virtual bool get_error_message(int error, String *buf);
-
- uint8 table_cache_type() { return HA_CACHE_TBL_ASKTRANSACT; }
- /*
- ask handler about permission to cache table during query registration
- */
- my_bool register_query_cache_table(THD *thd, char *table_key,
- uint key_length,
- qc_engine_callback *call_back,
- ulonglong *engine_data)
- {
- *call_back= innobase_query_caching_of_table_permitted;
- *engine_data= 0;
- return innobase_query_caching_of_table_permitted(thd, table_key,
- key_length,
- engine_data);
- }
- static char *get_mysql_bin_log_name();
- static ulonglong get_mysql_bin_log_pos();
- bool primary_key_is_clustered() { return true; }
- int cmp_ref(const byte *ref1, const byte *ref2);
-};
-
-extern struct show_var_st innodb_status_variables[];
-extern ulong innobase_fast_shutdown;
-extern ulong innobase_large_page_size;
-extern long innobase_mirrored_log_groups, innobase_log_files_in_group;
-extern longlong innobase_buffer_pool_size, innobase_log_file_size;
-extern long innobase_log_buffer_size;
-extern long innobase_additional_mem_pool_size;
-extern long innobase_buffer_pool_awe_mem_mb;
-extern long innobase_file_io_threads, innobase_lock_wait_timeout;
-extern long innobase_force_recovery;
-extern long innobase_open_files;
-extern char *innobase_data_home_dir, *innobase_data_file_path;
-extern char *innobase_log_group_home_dir, *innobase_log_arch_dir;
-extern char *innobase_unix_file_flush_method;
-/* The following variables have to be my_bool for SHOW VARIABLES to work */
-extern my_bool innobase_log_archive,
- innobase_use_doublewrite,
- innobase_use_checksums,
- innobase_use_large_pages,
- innobase_use_native_aio,
- innobase_file_per_table, innobase_locks_unsafe_for_binlog,
- innobase_rollback_on_timeout,
- innobase_create_status_file,
- innobase_adaptive_hash_index;
-extern my_bool innobase_very_fast_shutdown; /* set this to 1 just before
- calling innobase_end() if you want
- InnoDB to shut down without
- flushing the buffer pool: this
- is equivalent to a 'crash' */
-extern "C" {
-extern ulong srv_max_buf_pool_modified_pct;
-extern ulong srv_max_purge_lag;
-extern ulong srv_auto_extend_increment;
-extern ulong srv_n_spin_wait_rounds;
-extern ulong srv_n_free_tickets_to_enter;
-extern ulong srv_thread_sleep_delay;
-extern ulong srv_thread_concurrency;
-extern ulong srv_commit_concurrency;
-extern ulong srv_flush_log_at_trx_commit;
-}
-
-bool innobase_init(void);
-bool innobase_end(void);
-bool innobase_flush_logs(void);
-uint innobase_get_free_space(void);
-
-/*
- don't delete it - it may be re-enabled later
- as an optimization for the most common case InnoDB+binlog
-*/
-#if 0
-int innobase_report_binlog_offset_and_commit(
- THD* thd,
- void* trx_handle,
- char* log_file_name,
- my_off_t end_offset);
-int innobase_commit_complete(void* trx_handle);
-void innobase_store_binlog_offset_and_flush_log(char *binlog_name,longlong offset);
-#endif
-
-int innobase_drop_database(char *path);
-bool innodb_show_status(THD* thd);
-bool innodb_mutex_show_status(THD* thd);
-void innodb_export_status(void);
-
-void innobase_release_temporary_latches(THD *thd);
-
-void innobase_store_binlog_offset_and_flush_log(char *binlog_name,longlong offset);
-
-int innobase_start_trx_and_assign_read_view(THD* thd);
-
-/***********************************************************************
-This function is used to prepare X/Open XA distributed transaction */
-
-int innobase_xa_prepare(
-/*====================*/
- /* out: 0 or error number */
- THD* thd, /* in: handle to the MySQL thread of the user
- whose XA transaction should be prepared */
- bool all); /* in: TRUE - commit transaction
- FALSE - the current SQL statement ended */
-
-/***********************************************************************
-This function is used to recover X/Open XA distributed transactions */
-
-int innobase_xa_recover(
-/*====================*/
- /* out: number of prepared transactions
- stored in xid_list */
- XID* xid_list, /* in/out: prepared transactions */
- uint len); /* in: number of slots in xid_list */
-
-/***********************************************************************
-This function is used to commit one X/Open XA distributed transaction
-which is in the prepared state */
-
-int innobase_commit_by_xid(
-/*=======================*/
- /* out: 0 or error number */
- XID* xid); /* in : X/Open XA Transaction Identification */
-
-/***********************************************************************
-This function is used to rollback one X/Open XA distributed transaction
-which is in the prepared state */
-
-int innobase_rollback_by_xid(
- /* out: 0 or error number */
- XID *xid); /* in : X/Open XA Transaction Identification */
-
-
-/***********************************************************************
-Create a consistent view for a cursor based on current transaction
-which is created if the corresponding MySQL thread still lacks one.
-This consistent view is then used inside of MySQL when accessing records
-using a cursor. */
-
-void*
-innobase_create_cursor_view(void);
-/*=============================*/
- /* out: Pointer to cursor view or NULL */
-
-/***********************************************************************
-Close the given consistent cursor view of a transaction and restore
-global read view to a transaction read view. Transaction is created if the
-corresponding MySQL thread still lacks one. */
-
-void
-innobase_close_cursor_view(
-/*=======================*/
- void* curview); /* in: Consistent read view to be closed */
-
-/***********************************************************************
-Set the given consistent cursor view to a transaction which is created
-if the corresponding MySQL thread still lacks one. If the given
-consistent cursor view is NULL global read view of a transaction is
-restored to a transaction read view. */
-
-void
-innobase_set_cursor_view(
-/*=====================*/
- void* curview); /* in: Consistent read view to be set */
diff --git a/sql/ha_myisam.cc b/sql/ha_myisam.cc
deleted file mode 100644
index 8c8f027bb6b..00000000000
--- a/sql/ha_myisam.cc
+++ /dev/null
@@ -1,2013 +0,0 @@
-/* Copyright (C) 2000-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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
-
-
-#ifdef USE_PRAGMA_IMPLEMENTATION
-#pragma implementation // gcc: Class implementation
-#endif
-
-#include "mysql_priv.h"
-#include <m_ctype.h>
-#include <myisampack.h>
-#include "ha_myisam.h"
-#include <stdarg.h>
-#ifndef MASTER
-#include "../srclib/myisam/myisamdef.h"
-#else
-#include "../myisam/myisamdef.h"
-#include "../myisam/rt_index.h"
-#endif
-
-ulong myisam_recover_options= HA_RECOVER_NONE;
-
-/* bits in myisam_recover_options */
-const char *myisam_recover_names[] =
-{ "DEFAULT", "BACKUP", "FORCE", "QUICK", NullS};
-TYPELIB myisam_recover_typelib= {array_elements(myisam_recover_names)-1,"",
- myisam_recover_names, NULL};
-
-const char *myisam_stats_method_names[] = {"nulls_unequal", "nulls_equal",
- "nulls_ignored", NullS};
-TYPELIB myisam_stats_method_typelib= {
- array_elements(myisam_stats_method_names) - 1, "",
- myisam_stats_method_names, NULL};
-
-
-/*****************************************************************************
-** MyISAM tables
-*****************************************************************************/
-
-/* MyISAM handlerton */
-
-handlerton myisam_hton= {
- "MyISAM",
- SHOW_OPTION_YES,
- "Default engine as of MySQL 3.23 with great performance",
- DB_TYPE_MYISAM,
- NULL,
- 0, /* slot */
- 0, /* savepoint size. */
- NULL, /* close_connection */
- NULL, /* savepoint */
- NULL, /* rollback to savepoint */
- NULL, /* release savepoint */
- NULL, /* commit */
- NULL, /* rollback */
- NULL, /* prepare */
- NULL, /* recover */
- NULL, /* commit_by_xid */
- NULL, /* rollback_by_xid */
- NULL, /* create_cursor_read_view */
- NULL, /* set_cursor_read_view */
- NULL, /* close_cursor_read_view */
- /*
- MyISAM doesn't support transactions and doesn't have
- transaction-dependent context: cursors can survive a commit.
- */
- HTON_CAN_RECREATE
-};
-
-// collect errors printed by mi_check routines
-
-static void mi_check_print_msg(MI_CHECK *param, const char* msg_type,
- const char *fmt, va_list args)
-{
- THD* thd = (THD*)param->thd;
- Protocol *protocol= thd->protocol;
- uint length, msg_length;
- char msgbuf[MI_MAX_MSG_BUF];
- char name[NAME_LEN*2+2];
-
- msg_length= my_vsnprintf(msgbuf, sizeof(msgbuf), fmt, args);
- msgbuf[sizeof(msgbuf) - 1] = 0; // healthy paranoia
-
- DBUG_PRINT(msg_type,("message: %s",msgbuf));
-
- if (!thd->vio_ok())
- {
- sql_print_error(msgbuf);
- return;
- }
-
- if (param->testflag & (T_CREATE_MISSING_KEYS | T_SAFE_REPAIR |
- T_AUTO_REPAIR))
- {
- my_message(ER_NOT_KEYFILE,msgbuf,MYF(MY_WME));
- return;
- }
- length=(uint) (strxmov(name, param->db_name,".",param->table_name,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:
- mi_check_print_msg/push_warning/sql_alloc/my_pthread_getspecific_ptr.
-
- Also we likely need to lock mutex here (in both cases with protocol and
- push_warning).
- */
- protocol->prepare_for_resend();
- protocol->store(name, length, system_charset_info);
- protocol->store(param->op_name, system_charset_info);
- protocol->store(msg_type, system_charset_info);
- protocol->store(msgbuf, msg_length, system_charset_info);
- if (protocol->write())
- sql_print_error("Failed on my_net_write, writing to stderr instead: %s\n",
- msgbuf);
- return;
-}
-
-
-/*
- Convert TABLE object to MyISAM key and column definition
-
- SYNOPSIS
- table2myisam()
- table_arg in TABLE object.
- keydef_out out MyISAM key definition.
- recinfo_out out MyISAM column definition.
- records_out out Number of fields.
-
- DESCRIPTION
- This function will allocate and initialize MyISAM key and column
- definition for further use in mi_create or for a check for underlying
- table conformance in merge engine.
-
- RETURN VALUE
- 0 OK
- !0 error code
-*/
-
-int table2myisam(TABLE *table_arg, MI_KEYDEF **keydef_out,
- MI_COLUMNDEF **recinfo_out, uint *records_out)
-{
- uint i, j, recpos, minpos, fieldpos, temp_length, length;
- enum ha_base_keytype type= HA_KEYTYPE_BINARY;
- KEY *pos;
- MI_KEYDEF *keydef;
- MI_COLUMNDEF *recinfo, *recinfo_pos;
- HA_KEYSEG *keyseg;
- TABLE_SHARE *share= table_arg->s;
- uint options= share->db_options_in_use;
- DBUG_ENTER("table2myisam");
- if (!(my_multi_malloc(MYF(MY_WME),
- recinfo_out, (share->fields * 2 + 2) * sizeof(MI_COLUMNDEF),
- keydef_out, share->keys * sizeof(MI_KEYDEF),
- &keyseg,
- (share->key_parts + share->keys) * sizeof(HA_KEYSEG),
- NullS)))
- DBUG_RETURN(HA_ERR_OUT_OF_MEM); /* purecov: inspected */
- keydef= *keydef_out;
- recinfo= *recinfo_out;
- pos= table_arg->key_info;
- for (i= 0; i < share->keys; i++, pos++)
- {
- keydef[i].flag= ((uint16) pos->flags & (HA_NOSAME | HA_FULLTEXT | HA_SPATIAL));
- keydef[i].key_alg= pos->algorithm == HA_KEY_ALG_UNDEF ?
- (pos->flags & HA_SPATIAL ? HA_KEY_ALG_RTREE : HA_KEY_ALG_BTREE) :
- pos->algorithm;
- keydef[i].seg= keyseg;
- keydef[i].keysegs= pos->key_parts;
- for (j= 0; j < pos->key_parts; j++)
- {
- Field *field= pos->key_part[j].field;
- type= field->key_type();
- keydef[i].seg[j].flag= pos->key_part[j].key_part_flag;
-
- if (options & HA_OPTION_PACK_KEYS ||
- (pos->flags & (HA_PACK_KEY | HA_BINARY_PACK_KEY |
- HA_SPACE_PACK_USED)))
- {
- if (pos->key_part[j].length > 8 &&
- (type == HA_KEYTYPE_TEXT ||
- type == HA_KEYTYPE_NUM ||
- (type == HA_KEYTYPE_BINARY && !field->zero_pack())))
- {
- /* No blobs here */
- if (j == 0)
- keydef[i].flag|= HA_PACK_KEY;
- if (!(field->flags & ZEROFILL_FLAG) &&
- (field->type() == MYSQL_TYPE_STRING ||
- field->type() == MYSQL_TYPE_VAR_STRING ||
- ((int) (pos->key_part[j].length - field->decimals())) >= 4))
- keydef[i].seg[j].flag|= HA_SPACE_PACK;
- }
- else if (j == 0 && (!(pos->flags & HA_NOSAME) || pos->key_length > 16))
- keydef[i].flag|= HA_BINARY_PACK_KEY;
- }
- keydef[i].seg[j].type= (int) type;
- keydef[i].seg[j].start= pos->key_part[j].offset;
- keydef[i].seg[j].length= pos->key_part[j].length;
- keydef[i].seg[j].bit_start= keydef[i].seg[j].bit_end=
- keydef[i].seg[j].bit_length= 0;
- keydef[i].seg[j].bit_pos= 0;
- keydef[i].seg[j].language= field->charset()->number;
-
- if (field->null_ptr)
- {
- keydef[i].seg[j].null_bit= field->null_bit;
- keydef[i].seg[j].null_pos= (uint) (field->null_ptr-
- (uchar*) table_arg->record[0]);
- }
- else
- {
- keydef[i].seg[j].null_bit= 0;
- keydef[i].seg[j].null_pos= 0;
- }
- if (field->type() == FIELD_TYPE_BLOB ||
- field->type() == FIELD_TYPE_GEOMETRY)
- {
- keydef[i].seg[j].flag|= HA_BLOB_PART;
- /* save number of bytes used to pack length */
- keydef[i].seg[j].bit_start= (uint) (field->pack_length() -
- share->blob_ptr_size);
- }
- else if (field->type() == FIELD_TYPE_BIT)
- {
- keydef[i].seg[j].bit_length= ((Field_bit *) field)->bit_len;
- keydef[i].seg[j].bit_start= ((Field_bit *) field)->bit_ofs;
- keydef[i].seg[j].bit_pos= (uint) (((Field_bit *) field)->bit_ptr -
- (uchar*) table_arg->record[0]);
- }
- }
- keyseg+= pos->key_parts;
- }
- if (table_arg->found_next_number_field)
- keydef[share->next_number_index].flag|= HA_AUTO_KEY;
- recpos= 0;
- recinfo_pos= recinfo;
- while (recpos < (uint) share->reclength)
- {
- Field **field, *found= 0;
- minpos= share->reclength;
- length= 0;
-
- for (field= table_arg->field; *field; field++)
- {
- if ((fieldpos= (*field)->offset()) >= recpos &&
- fieldpos <= minpos)
- {
- /* skip null fields */
- if (!(temp_length= (*field)->pack_length_in_rec()))
- continue; /* Skip null-fields */
- if (! found || fieldpos < minpos ||
- (fieldpos == minpos && temp_length < length))
- {
- minpos= fieldpos;
- found= *field;
- length= temp_length;
- }
- }
- }
- DBUG_PRINT("loop", ("found: 0x%lx recpos: %d minpos: %d length: %d",
- (long) found, recpos, minpos, length));
- if (recpos != minpos)
- { // Reserved space (Null bits?)
- bzero((char*) recinfo_pos, sizeof(*recinfo_pos));
- recinfo_pos->type= (int) FIELD_NORMAL;
- recinfo_pos++->length= (uint16) (minpos - recpos);
- }
- if (!found)
- break;
-
- if (found->flags & BLOB_FLAG)
- recinfo_pos->type= (int) FIELD_BLOB;
- else if (found->type() == MYSQL_TYPE_VARCHAR)
- recinfo_pos->type= FIELD_VARCHAR;
- else if (!(options & HA_OPTION_PACK_RECORD))
- recinfo_pos->type= (int) FIELD_NORMAL;
- else if (found->zero_pack())
- recinfo_pos->type= (int) FIELD_SKIP_ZERO;
- else
- recinfo_pos->type= (int) ((length <= 3 ||
- (found->flags & ZEROFILL_FLAG)) ?
- FIELD_NORMAL :
- found->type() == MYSQL_TYPE_STRING ||
- found->type() == MYSQL_TYPE_VAR_STRING ?
- FIELD_SKIP_ENDSPACE :
- FIELD_SKIP_PRESPACE);
- if (found->null_ptr)
- {
- recinfo_pos->null_bit= found->null_bit;
- recinfo_pos->null_pos= (uint) (found->null_ptr -
- (uchar*) table_arg->record[0]);
- }
- else
- {
- recinfo_pos->null_bit= 0;
- recinfo_pos->null_pos= 0;
- }
- (recinfo_pos++)->length= (uint16) length;
- recpos= minpos + length;
- DBUG_PRINT("loop", ("length: %d type: %d",
- recinfo_pos[-1].length,recinfo_pos[-1].type));
- }
- *records_out= (uint) (recinfo_pos - recinfo);
- DBUG_RETURN(0);
-}
-
-
-/*
- Check for underlying table conformance
-
- SYNOPSIS
- check_definition()
- t1_keyinfo in First table key definition
- t1_recinfo in First table record definition
- t1_keys in Number of keys in first table
- t1_recs in Number of records in first table
- t2_keyinfo in Second table key definition
- t2_recinfo in Second table record definition
- t2_keys in Number of keys in second table
- t2_recs in Number of records in second table
- strict in Strict check switch
-
- DESCRIPTION
- This function compares two MyISAM definitions. By intention it was done
- to compare merge table definition against underlying table definition.
- It may also be used to compare dot-frm and MYI definitions of MyISAM
- table as well to compare different MyISAM table definitions.
-
- For merge table it is not required that number of keys in merge table
- must exactly match number of keys in underlying table. When calling this
- function for underlying table conformance check, 'strict' flag must be
- set to false, and converted merge definition must be passed as t1_*.
-
- Otherwise 'strict' flag must be set to 1 and it is not required to pass
- converted dot-frm definition as t1_*.
-
- RETURN VALUE
- 0 - Equal definitions.
- 1 - Different definitions.
-
- TODO
- - compare FULLTEXT keys;
- - compare SPATIAL keys;
- - compare FIELD_SKIP_ZERO which is converted to FIELD_NORMAL correctly
- (should be corretly detected in table2myisam).
-*/
-
-int check_definition(MI_KEYDEF *t1_keyinfo, MI_COLUMNDEF *t1_recinfo,
- uint t1_keys, uint t1_recs,
- MI_KEYDEF *t2_keyinfo, MI_COLUMNDEF *t2_recinfo,
- uint t2_keys, uint t2_recs, bool strict)
-{
- uint i, j;
- DBUG_ENTER("check_definition");
- if ((strict ? t1_keys != t2_keys : t1_keys > t2_keys))
- {
- DBUG_PRINT("error", ("Number of keys differs: t1_keys=%u, t2_keys=%u",
- t1_keys, t2_keys));
- DBUG_RETURN(1);
- }
- if (t1_recs != t2_recs)
- {
- DBUG_PRINT("error", ("Number of recs differs: t1_recs=%u, t2_recs=%u",
- t1_recs, t2_recs));
- DBUG_RETURN(1);
- }
- for (i= 0; i < t1_keys; i++)
- {
- HA_KEYSEG *t1_keysegs= t1_keyinfo[i].seg;
- HA_KEYSEG *t2_keysegs= t2_keyinfo[i].seg;
- if (t1_keyinfo[i].flag & HA_FULLTEXT && t2_keyinfo[i].flag & HA_FULLTEXT)
- continue;
- else if (t1_keyinfo[i].flag & HA_FULLTEXT ||
- t2_keyinfo[i].flag & HA_FULLTEXT)
- {
- DBUG_PRINT("error", ("Key %d has different definition", i));
- DBUG_PRINT("error", ("t1_fulltext= %d, t2_fulltext=%d",
- test(t1_keyinfo[i].flag & HA_FULLTEXT),
- test(t2_keyinfo[i].flag & HA_FULLTEXT)));
- DBUG_RETURN(1);
- }
- if (t1_keyinfo[i].flag & HA_SPATIAL && t2_keyinfo[i].flag & HA_SPATIAL)
- continue;
- else if (t1_keyinfo[i].flag & HA_SPATIAL ||
- t2_keyinfo[i].flag & HA_SPATIAL)
- {
- DBUG_PRINT("error", ("Key %d has different definition", i));
- DBUG_PRINT("error", ("t1_spatial= %d, t2_spatial=%d",
- test(t1_keyinfo[i].flag & HA_SPATIAL),
- test(t2_keyinfo[i].flag & HA_SPATIAL)));
- DBUG_RETURN(1);
- }
- if (t1_keyinfo[i].keysegs != t2_keyinfo[i].keysegs ||
- t1_keyinfo[i].key_alg != t2_keyinfo[i].key_alg)
- {
- DBUG_PRINT("error", ("Key %d has different definition", i));
- DBUG_PRINT("error", ("t1_keysegs=%d, t1_key_alg=%d",
- t1_keyinfo[i].keysegs, t1_keyinfo[i].key_alg));
- DBUG_PRINT("error", ("t2_keysegs=%d, t2_key_alg=%d",
- t2_keyinfo[i].keysegs, t2_keyinfo[i].key_alg));
- DBUG_RETURN(1);
- }
- for (j= t1_keyinfo[i].keysegs; j--;)
- {
- if (t1_keysegs[j].type != t2_keysegs[j].type ||
- t1_keysegs[j].language != t2_keysegs[j].language ||
- t1_keysegs[j].null_bit != t2_keysegs[j].null_bit ||
- t1_keysegs[j].length != t2_keysegs[j].length)
- {
- DBUG_PRINT("error", ("Key segment %d (key %d) has different "
- "definition", j, i));
- DBUG_PRINT("error", ("t1_type=%d, t1_language=%d, t1_null_bit=%d, "
- "t1_length=%d",
- t1_keysegs[j].type, t1_keysegs[j].language,
- t1_keysegs[j].null_bit, t1_keysegs[j].length));
- DBUG_PRINT("error", ("t2_type=%d, t2_language=%d, t2_null_bit=%d, "
- "t2_length=%d",
- t2_keysegs[j].type, t2_keysegs[j].language,
- t2_keysegs[j].null_bit, t2_keysegs[j].length));
-
- DBUG_RETURN(1);
- }
- }
- }
- for (i= 0; i < t1_recs; i++)
- {
- MI_COLUMNDEF *t1_rec= &t1_recinfo[i];
- MI_COLUMNDEF *t2_rec= &t2_recinfo[i];
- /*
- FIELD_SKIP_ZERO can be changed to FIELD_NORMAL in mi_create,
- see NOTE1 in mi_create.c
- */
- if ((t1_rec->type != t2_rec->type &&
- !(t1_rec->type == (int) FIELD_SKIP_ZERO &&
- t1_rec->length == 1 &&
- t2_rec->type == (int) FIELD_NORMAL)) ||
- t1_rec->length != t2_rec->length ||
- t1_rec->null_bit != t2_rec->null_bit)
- {
- DBUG_PRINT("error", ("Field %d has different definition", i));
- DBUG_PRINT("error", ("t1_type=%d, t1_length=%d, t1_null_bit=%d",
- t1_rec->type, t1_rec->length, t1_rec->null_bit));
- DBUG_PRINT("error", ("t2_type=%d, t2_length=%d, t2_null_bit=%d",
- t2_rec->type, t2_rec->length, t2_rec->null_bit));
- DBUG_RETURN(1);
- }
- }
- DBUG_RETURN(0);
-}
-
-
-extern "C" {
-
-volatile int *killed_ptr(MI_CHECK *param)
-{
- /* In theory Unsafe conversion, but should be ok for now */
- return (int*) &(((THD *)(param->thd))->killed);
-}
-
-void mi_check_print_error(MI_CHECK *param, const char *fmt,...)
-{
- param->error_printed|=1;
- param->out_flag|= O_DATA_LOST;
- va_list args;
- va_start(args, fmt);
- mi_check_print_msg(param, "error", fmt, args);
- va_end(args);
-}
-
-void mi_check_print_info(MI_CHECK *param, const char *fmt,...)
-{
- va_list args;
- va_start(args, fmt);
- mi_check_print_msg(param, "info", fmt, args);
- va_end(args);
-}
-
-void mi_check_print_warning(MI_CHECK *param, const char *fmt,...)
-{
- param->warning_printed=1;
- param->out_flag|= O_DATA_LOST;
- va_list args;
- va_start(args, fmt);
- mi_check_print_msg(param, "warning", fmt, args);
- va_end(args);
-}
-
-}
-
-
-ha_myisam::ha_myisam(TABLE *table_arg)
- :handler(&myisam_hton, table_arg), file(0),
- int_table_flags(HA_NULL_IN_KEY | HA_CAN_FULLTEXT | HA_CAN_SQL_HANDLER |
- HA_DUPP_POS | HA_CAN_INDEX_BLOBS | HA_AUTO_PART_KEY |
- HA_FILE_BASED | HA_CAN_GEOMETRY | HA_READ_RND_SAME |
- HA_CAN_INSERT_DELAYED | HA_CAN_BIT_FIELD | HA_CAN_RTREEKEYS),
- can_enable_indexes(1)
-{}
-
-handler *ha_myisam::clone(MEM_ROOT *mem_root)
-{
- ha_myisam *new_handler= static_cast <ha_myisam *>(handler::clone(mem_root));
- if (new_handler)
- new_handler->file->state= file->state;
- return new_handler;
-}
-
-
-static const char *ha_myisam_exts[] = {
- ".MYI",
- ".MYD",
- NullS
-};
-
-const char **ha_myisam::bas_ext() const
-{
- return ha_myisam_exts;
-}
-
-
-const char *ha_myisam::index_type(uint key_number)
-{
- return ((table->key_info[key_number].flags & HA_FULLTEXT) ?
- "FULLTEXT" :
- (table->key_info[key_number].flags & HA_SPATIAL) ?
- "SPATIAL" :
- (table->key_info[key_number].algorithm == HA_KEY_ALG_RTREE) ?
- "RTREE" :
- "BTREE");
-}
-
-#ifdef HAVE_REPLICATION
-int ha_myisam::net_read_dump(NET* net)
-{
- int data_fd = file->dfile;
- int error = 0;
-
- my_seek(data_fd, 0L, MY_SEEK_SET, MYF(MY_WME));
- for (;;)
- {
- ulong packet_len = my_net_read(net);
- if (!packet_len)
- break ; // end of file
- if (packet_len == packet_error)
- {
- sql_print_error("ha_myisam::net_read_dump - read error ");
- error= -1;
- goto err;
- }
- if (my_write(data_fd, (byte*)net->read_pos, (uint) packet_len,
- MYF(MY_WME|MY_FNABP)))
- {
- error = errno;
- goto err;
- }
- }
-err:
- return error;
-}
-
-
-int ha_myisam::dump(THD* thd, int fd)
-{
- MYISAM_SHARE* share = file->s;
- NET* net = &thd->net;
- uint blocksize = share->blocksize;
- my_off_t bytes_to_read = share->state.state.data_file_length;
- int data_fd = file->dfile;
- byte * buf = (byte*) my_malloc(blocksize, MYF(MY_WME));
- if (!buf)
- return ENOMEM;
-
- int error = 0;
- my_seek(data_fd, 0L, MY_SEEK_SET, MYF(MY_WME));
- for (; bytes_to_read > 0;)
- {
- uint bytes = my_read(data_fd, buf, blocksize, MYF(MY_WME));
- if (bytes == MY_FILE_ERROR)
- {
- error = errno;
- goto err;
- }
-
- if (fd >= 0)
- {
- if (my_write(fd, buf, bytes, MYF(MY_WME | MY_FNABP)))
- {
- error = errno ? errno : EPIPE;
- goto err;
- }
- }
- else
- {
- if (my_net_write(net, (char*) buf, bytes))
- {
- error = errno ? errno : EPIPE;
- goto err;
- }
- }
- bytes_to_read -= bytes;
- }
-
- if (fd < 0)
- {
- if (my_net_write(net, "", 0))
- error = errno ? errno : EPIPE;
- net_flush(net);
- }
-
-err:
- my_free((gptr) buf, MYF(0));
- return error;
-}
-#endif /* HAVE_REPLICATION */
-
- /* Name is here without an extension */
-
-int ha_myisam::open(const char *name, int mode, uint test_if_locked)
-{
- if (!(file=mi_open(name, mode, test_if_locked)))
- return (my_errno ? my_errno : -1);
-
- if (test_if_locked & (HA_OPEN_IGNORE_IF_LOCKED | HA_OPEN_TMP_TABLE))
- VOID(mi_extra(file, HA_EXTRA_NO_WAIT_LOCK, 0));
- info(HA_STATUS_NO_LOCK | HA_STATUS_VARIABLE | HA_STATUS_CONST);
- if (!(test_if_locked & HA_OPEN_WAIT_IF_LOCKED))
- VOID(mi_extra(file, HA_EXTRA_WAIT_LOCK, 0));
- if (!table->s->db_record_offset)
- int_table_flags|=HA_REC_NOT_IN_SEQ;
- if (file->s->options & (HA_OPTION_CHECKSUM | HA_OPTION_COMPRESS_RECORD))
- int_table_flags|=HA_HAS_CHECKSUM;
- return (0);
-}
-
-int ha_myisam::close(void)
-{
- MI_INFO *tmp=file;
- file=0;
- return mi_close(tmp);
-}
-
-int ha_myisam::write_row(byte * buf)
-{
- statistic_increment(table->in_use->status_var.ha_write_count,&LOCK_status);
-
- /* If we have a timestamp column, update it to the current time */
- if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_INSERT)
- table->timestamp_field->set_time();
-
- /*
- If we have an auto_increment column and we are writing a changed row
- or a new row, then update the auto_increment value in the record.
- */
- if (table->next_number_field && buf == table->record[0])
- {
- int error;
- if ((error= update_auto_increment()))
- return error;
- }
- return mi_write(file,buf);
-}
-
-int ha_myisam::check(THD* thd, HA_CHECK_OPT* check_opt)
-{
- if (!file) return HA_ADMIN_INTERNAL_ERROR;
- int error;
- MI_CHECK param;
- MYISAM_SHARE* share = file->s;
- const char *old_proc_info=thd->proc_info;
-
- thd->proc_info="Checking table";
- myisamchk_init(&param);
- param.thd = thd;
- param.op_name = "check";
- param.db_name= table->s->db;
- param.table_name= table->alias;
- param.testflag = check_opt->flags | T_CHECK | T_SILENT;
- param.stats_method= (enum_mi_stats_method)thd->variables.myisam_stats_method;
-
- if (!(table->db_stat & HA_READ_ONLY))
- param.testflag|= T_STATISTICS;
- param.using_global_keycache = 1;
-
- if (!mi_is_crashed(file) &&
- (((param.testflag & T_CHECK_ONLY_CHANGED) &&
- !(share->state.changed & (STATE_CHANGED | STATE_CRASHED |
- STATE_CRASHED_ON_REPAIR)) &&
- share->state.open_count == 0) ||
- ((param.testflag & T_FAST) && (share->state.open_count ==
- (uint) (share->global_changed ? 1 : 0)))))
- return HA_ADMIN_ALREADY_DONE;
-
- error = chk_status(&param, file); // Not fatal
- error = chk_size(&param, file);
- if (!error)
- error |= chk_del(&param, file, param.testflag);
- if (!error)
- error = chk_key(&param, file);
- if (!error)
- {
- if ((!(param.testflag & T_QUICK) &&
- ((share->options &
- (HA_OPTION_PACK_RECORD | HA_OPTION_COMPRESS_RECORD)) ||
- (param.testflag & (T_EXTEND | T_MEDIUM)))) ||
- mi_is_crashed(file))
- {
- uint old_testflag=param.testflag;
- param.testflag|=T_MEDIUM;
- if (!(error= init_io_cache(&param.read_cache, file->dfile,
- my_default_record_cache_size, READ_CACHE,
- share->pack.header_length, 1, MYF(MY_WME))))
- {
- error= chk_data_link(&param, file, param.testflag & T_EXTEND);
- end_io_cache(&(param.read_cache));
- }
- param.testflag= old_testflag;
- }
- }
- if (!error)
- {
- if ((share->state.changed & (STATE_CHANGED |
- STATE_CRASHED_ON_REPAIR |
- STATE_CRASHED | STATE_NOT_ANALYZED)) ||
- (param.testflag & T_STATISTICS) ||
- mi_is_crashed(file))
- {
- file->update|=HA_STATE_CHANGED | HA_STATE_ROW_CHANGED;
- pthread_mutex_lock(&share->intern_lock);
- share->state.changed&= ~(STATE_CHANGED | STATE_CRASHED |
- STATE_CRASHED_ON_REPAIR);
- if (!(table->db_stat & HA_READ_ONLY))
- error=update_state_info(&param,file,UPDATE_TIME | UPDATE_OPEN_COUNT |
- UPDATE_STAT);
- pthread_mutex_unlock(&share->intern_lock);
- info(HA_STATUS_NO_LOCK | HA_STATUS_TIME | HA_STATUS_VARIABLE |
- HA_STATUS_CONST);
- }
- }
- else if (!mi_is_crashed(file) && !thd->killed)
- {
- mi_mark_crashed(file);
- file->update |= HA_STATE_CHANGED | HA_STATE_ROW_CHANGED;
- }
-
- thd->proc_info=old_proc_info;
- return error ? HA_ADMIN_CORRUPT : HA_ADMIN_OK;
-}
-
-
-/*
- analyze the key distribution in the table
- As the table may be only locked for read, we have to take into account that
- two threads may do an analyze at the same time!
-*/
-
-int ha_myisam::analyze(THD *thd, HA_CHECK_OPT* check_opt)
-{
- int error=0;
- MI_CHECK param;
- MYISAM_SHARE* share = file->s;
-
- myisamchk_init(&param);
- param.thd = thd;
- param.op_name= "analyze";
- param.db_name= table->s->db;
- param.table_name= table->alias;
- param.testflag= (T_FAST | T_CHECK | T_SILENT | T_STATISTICS |
- T_DONT_CHECK_CHECKSUM);
- param.using_global_keycache = 1;
- param.stats_method= (enum_mi_stats_method)thd->variables.myisam_stats_method;
-
- if (!(share->state.changed & STATE_NOT_ANALYZED))
- return HA_ADMIN_ALREADY_DONE;
-
- error = chk_key(&param, file);
- if (!error)
- {
- pthread_mutex_lock(&share->intern_lock);
- error=update_state_info(&param,file,UPDATE_STAT);
- pthread_mutex_unlock(&share->intern_lock);
- }
- else if (!mi_is_crashed(file) && !thd->killed)
- mi_mark_crashed(file);
- return error ? HA_ADMIN_CORRUPT : HA_ADMIN_OK;
-}
-
-
-int ha_myisam::restore(THD* thd, HA_CHECK_OPT *check_opt)
-{
- HA_CHECK_OPT tmp_check_opt;
- char *backup_dir= thd->lex->backup_dir;
- char src_path[FN_REFLEN], dst_path[FN_REFLEN];
- const char *table_name= table->s->table_name;
- int error;
- const char* errmsg;
- DBUG_ENTER("restore");
-
- if (fn_format_relative_to_data_home(src_path, table_name, backup_dir,
- MI_NAME_DEXT))
- DBUG_RETURN(HA_ADMIN_INVALID);
-
- if (my_copy(src_path, fn_format(dst_path, table->s->path, "",
- MI_NAME_DEXT, 4), MYF(MY_WME)))
- {
- error= HA_ADMIN_FAILED;
- errmsg= "Failed in my_copy (Error %d)";
- goto err;
- }
-
- tmp_check_opt.init();
- tmp_check_opt.flags |= T_VERY_SILENT | T_CALC_CHECKSUM | T_QUICK;
- DBUG_RETURN(repair(thd, &tmp_check_opt));
-
- err:
- {
- MI_CHECK param;
- myisamchk_init(&param);
- param.thd= thd;
- param.op_name= "restore";
- param.db_name= table->s->db;
- param.table_name= table->s->table_name;
- param.testflag= 0;
- mi_check_print_error(&param, errmsg, my_errno);
- DBUG_RETURN(error);
- }
-}
-
-
-int ha_myisam::backup(THD* thd, HA_CHECK_OPT *check_opt)
-{
- char *backup_dir= thd->lex->backup_dir;
- char src_path[FN_REFLEN], dst_path[FN_REFLEN];
- const char *table_name= table->s->table_name;
- int error;
- const char *errmsg;
- DBUG_ENTER("ha_myisam::backup");
-
- if (fn_format_relative_to_data_home(dst_path, table_name, backup_dir,
- reg_ext))
- {
- errmsg= "Failed in fn_format() for .frm file (errno: %d)";
- error= HA_ADMIN_INVALID;
- goto err;
- }
-
- if (my_copy(fn_format(src_path, table->s->path, "", reg_ext,
- MY_UNPACK_FILENAME),
- dst_path,
- MYF(MY_WME | MY_HOLD_ORIGINAL_MODES | MY_DONT_OVERWRITE_FILE)))
- {
- error = HA_ADMIN_FAILED;
- errmsg = "Failed copying .frm file (errno: %d)";
- goto err;
- }
-
- /* Change extension */
- if (!fn_format(dst_path, dst_path, "", MI_NAME_DEXT,
- MY_REPLACE_EXT | MY_UNPACK_FILENAME | MY_SAFE_PATH))
- {
- errmsg = "Failed in fn_format() for .MYD file (errno: %d)";
- error = HA_ADMIN_INVALID;
- goto err;
- }
-
- if (my_copy(fn_format(src_path, table->s->path, "", MI_NAME_DEXT,
- MY_UNPACK_FILENAME),
- dst_path,
- MYF(MY_WME | MY_HOLD_ORIGINAL_MODES | MY_DONT_OVERWRITE_FILE)))
- {
- errmsg = "Failed copying .MYD file (errno: %d)";
- error= HA_ADMIN_FAILED;
- goto err;
- }
- DBUG_RETURN(HA_ADMIN_OK);
-
- err:
- {
- MI_CHECK param;
- myisamchk_init(&param);
- param.thd= thd;
- param.op_name= "backup";
- param.db_name= table->s->db;
- param.table_name= table->s->table_name;
- param.testflag = 0;
- mi_check_print_error(&param,errmsg, my_errno);
- DBUG_RETURN(error);
- }
-}
-
-
-int ha_myisam::repair(THD* thd, HA_CHECK_OPT *check_opt)
-{
- int error;
- MI_CHECK param;
- ha_rows start_records;
-
- if (!file) return HA_ADMIN_INTERNAL_ERROR;
-
- myisamchk_init(&param);
- param.thd = thd;
- param.op_name= "repair";
- param.testflag= ((check_opt->flags & ~(T_EXTEND)) |
- T_SILENT | T_FORCE_CREATE | T_CALC_CHECKSUM |
- (check_opt->flags & T_EXTEND ? T_REP : T_REP_BY_SORT));
- param.sort_buffer_length= check_opt->sort_buffer_size;
- start_records=file->state->records;
- while ((error=repair(thd,param,0)) && param.retry_repair)
- {
- param.retry_repair=0;
- if (test_all_bits(param.testflag,
- (uint) (T_RETRY_WITHOUT_QUICK | T_QUICK)))
- {
- param.testflag&= ~T_RETRY_WITHOUT_QUICK;
- sql_print_information("Retrying repair of: '%s' without quick",
- table->s->path);
- continue;
- }
- param.testflag&= ~T_QUICK;
- if ((param.testflag & T_REP_BY_SORT))
- {
- param.testflag= (param.testflag & ~T_REP_BY_SORT) | T_REP;
- sql_print_information("Retrying repair of: '%s' with keycache",
- table->s->path);
- continue;
- }
- break;
- }
- if (!error && start_records != file->state->records &&
- !(check_opt->flags & T_VERY_SILENT))
- {
- char llbuff[22],llbuff2[22];
- sql_print_information("Found %s of %s rows when repairing '%s'",
- llstr(file->state->records, llbuff),
- llstr(start_records, llbuff2),
- table->s->path);
- }
- return error;
-}
-
-int ha_myisam::optimize(THD* thd, HA_CHECK_OPT *check_opt)
-{
- int error;
- if (!file) return HA_ADMIN_INTERNAL_ERROR;
- MI_CHECK param;
-
- myisamchk_init(&param);
- param.thd = thd;
- param.op_name= "optimize";
- param.testflag= (check_opt->flags | T_SILENT | T_FORCE_CREATE |
- T_REP_BY_SORT | T_STATISTICS | T_SORT_INDEX);
- param.sort_buffer_length= check_opt->sort_buffer_size;
- if ((error= repair(thd,param,1)) && param.retry_repair)
- {
- sql_print_warning("Warning: Optimize table got errno %d on %s.%s, retrying",
- my_errno, param.db_name, param.table_name);
- param.testflag&= ~T_REP_BY_SORT;
- error= repair(thd,param,1);
- }
- return error;
-}
-
-
-int ha_myisam::repair(THD *thd, MI_CHECK &param, bool do_optimize)
-{
- int error=0;
- uint local_testflag=param.testflag;
- bool optimize_done= !do_optimize, statistics_done=0;
- const char *old_proc_info=thd->proc_info;
- char fixed_name[FN_REFLEN];
- MYISAM_SHARE* share = file->s;
- ha_rows rows= file->state->records;
- DBUG_ENTER("ha_myisam::repair");
-
- /*
- Normally this method is entered with a properly opened table. If the
- repair fails, it can be repeated with more elaborate options. Under
- special circumstances it can happen that a repair fails so that it
- closed the data file and cannot re-open it. In this case file->dfile
- is set to -1. We must not try another repair without an open data
- file. (Bug #25289)
- */
- if (file->dfile == -1)
- {
- sql_print_information("Retrying repair of: '%s' failed. "
- "Please try REPAIR EXTENDED or myisamchk",
- table->s->path);
- DBUG_RETURN(HA_ADMIN_FAILED);
- }
-
- param.db_name= table->s->db;
- param.table_name= table->alias;
- param.tmpfile_createflag = O_RDWR | O_TRUNC;
- param.using_global_keycache = 1;
- param.thd= thd;
- param.tmpdir= &mysql_tmpdir_list;
- param.out_flag= 0;
- strmov(fixed_name,file->filename);
-
- // Don't lock tables if we have used LOCK TABLE
- if (!thd->locked_tables &&
- mi_lock_database(file, table->s->tmp_table ? F_EXTRA_LCK : F_WRLCK))
- {
- mi_check_print_error(&param,ER(ER_CANT_LOCK),my_errno);
- DBUG_RETURN(HA_ADMIN_FAILED);
- }
-
- if (!do_optimize ||
- ((file->state->del || share->state.split != file->state->records) &&
- (!(param.testflag & T_QUICK) ||
- !(share->state.changed & STATE_NOT_OPTIMIZED_KEYS))))
- {
- ulonglong key_map= ((local_testflag & T_CREATE_MISSING_KEYS) ?
- mi_get_mask_all_keys_active(share->base.keys) :
- share->state.key_map);
- uint testflag=param.testflag;
- if (mi_test_if_sort_rep(file,file->state->records,key_map,0) &&
- (local_testflag & T_REP_BY_SORT))
- {
- local_testflag|= T_STATISTICS;
- param.testflag|= T_STATISTICS; // We get this for free
- statistics_done=1;
- if (thd->variables.myisam_repair_threads>1)
- {
- char buf[40];
- /* TODO: respect myisam_repair_threads variable */
- my_snprintf(buf, 40, "Repair with %d threads", my_count_bits(key_map));
- thd->proc_info=buf;
- error = mi_repair_parallel(&param, file, fixed_name,
- param.testflag & T_QUICK);
- thd->proc_info="Repair done"; // to reset proc_info, as
- // it was pointing to local buffer
- }
- else
- {
- thd->proc_info="Repair by sorting";
- error = mi_repair_by_sort(&param, file, fixed_name,
- param.testflag & T_QUICK);
- }
- }
- else
- {
- thd->proc_info="Repair with keycache";
- param.testflag &= ~T_REP_BY_SORT;
- error= mi_repair(&param, file, fixed_name,
- param.testflag & T_QUICK);
- }
- param.testflag=testflag;
- optimize_done=1;
- }
- if (!error)
- {
- if ((local_testflag & T_SORT_INDEX) &&
- (share->state.changed & STATE_NOT_SORTED_PAGES))
- {
- optimize_done=1;
- thd->proc_info="Sorting index";
- error=mi_sort_index(&param,file,fixed_name);
- }
- if (!statistics_done && (local_testflag & T_STATISTICS))
- {
- if (share->state.changed & STATE_NOT_ANALYZED)
- {
- optimize_done=1;
- thd->proc_info="Analyzing";
- error = chk_key(&param, file);
- }
- else
- local_testflag&= ~T_STATISTICS; // Don't update statistics
- }
- }
- thd->proc_info="Saving state";
- if (!error)
- {
- if ((share->state.changed & STATE_CHANGED) || mi_is_crashed(file))
- {
- share->state.changed&= ~(STATE_CHANGED | STATE_CRASHED |
- STATE_CRASHED_ON_REPAIR);
- file->update|=HA_STATE_CHANGED | HA_STATE_ROW_CHANGED;
- }
- /*
- the following 'if', thought conceptually wrong,
- is a useful optimization nevertheless.
- */
- if (file->state != &file->s->state.state)
- file->s->state.state = *file->state;
- if (file->s->base.auto_key)
- update_auto_increment_key(&param, file, 1);
- if (optimize_done)
- error = update_state_info(&param, file,
- UPDATE_TIME | UPDATE_OPEN_COUNT |
- (local_testflag &
- T_STATISTICS ? UPDATE_STAT : 0));
- info(HA_STATUS_NO_LOCK | HA_STATUS_TIME | HA_STATUS_VARIABLE |
- HA_STATUS_CONST);
- if (rows != file->state->records && ! (param.testflag & T_VERY_SILENT))
- {
- char llbuff[22],llbuff2[22];
- mi_check_print_warning(&param,"Number of rows changed from %s to %s",
- llstr(rows,llbuff),
- llstr(file->state->records,llbuff2));
- }
- }
- else
- {
- mi_mark_crashed_on_repair(file);
- file->update |= HA_STATE_CHANGED | HA_STATE_ROW_CHANGED;
- update_state_info(&param, file, 0);
- }
- thd->proc_info=old_proc_info;
- if (!thd->locked_tables)
- mi_lock_database(file,F_UNLCK);
- DBUG_RETURN(error ? HA_ADMIN_FAILED :
- !optimize_done ? HA_ADMIN_ALREADY_DONE : HA_ADMIN_OK);
-}
-
-
-/*
- Assign table indexes to a specific key cache.
-*/
-
-int ha_myisam::assign_to_keycache(THD* thd, HA_CHECK_OPT *check_opt)
-{
- KEY_CACHE *new_key_cache= check_opt->key_cache;
- const char *errmsg= 0;
- int error= HA_ADMIN_OK;
- ulonglong map= ~(ulonglong) 0;
- TABLE_LIST *table_list= table->pos_in_table_list;
- DBUG_ENTER("ha_myisam::assign_to_keycache");
-
- /* Check validity of the index references */
- if (table_list->use_index)
- {
- /* We only come here when the user did specify an index map */
- key_map kmap;
- if (get_key_map_from_key_list(&kmap, table, table_list->use_index))
- DBUG_RETURN(HA_ADMIN_FAILED);
- map= kmap.to_ulonglong();
- }
-
- if ((error= mi_assign_to_key_cache(file, map, new_key_cache)))
- {
- char buf[STRING_BUFFER_USUAL_SIZE];
- my_snprintf(buf, sizeof(buf),
- "Failed to flush to index file (errno: %d)", error);
- errmsg= buf;
- error= HA_ADMIN_CORRUPT;
- }
-
- if (error != HA_ADMIN_OK)
- {
- /* Send error to user */
- MI_CHECK param;
- myisamchk_init(&param);
- param.thd= thd;
- param.op_name= "assign_to_keycache";
- param.db_name= table->s->db;
- param.table_name= table->s->table_name;
- param.testflag= 0;
- mi_check_print_error(&param, errmsg);
- }
- DBUG_RETURN(error);
-}
-
-
-/*
- Preload pages of the index file for a table into the key cache.
-*/
-
-int ha_myisam::preload_keys(THD* thd, HA_CHECK_OPT *check_opt)
-{
- int error;
- const char *errmsg;
- ulonglong map= ~(ulonglong) 0;
- TABLE_LIST *table_list= table->pos_in_table_list;
- my_bool ignore_leaves= table_list->ignore_leaves;
-
- DBUG_ENTER("ha_myisam::preload_keys");
-
- /* Check validity of the index references */
- if (table_list->use_index)
- {
- key_map kmap;
- get_key_map_from_key_list(&kmap, table, table_list->use_index);
- if (kmap.is_set_all())
- DBUG_RETURN(HA_ADMIN_FAILED);
- if (!kmap.is_clear_all())
- map= kmap.to_ulonglong();
- }
-
- mi_extra(file, HA_EXTRA_PRELOAD_BUFFER_SIZE,
- (void *) &thd->variables.preload_buff_size);
-
- if ((error= mi_preload(file, map, ignore_leaves)))
- {
- switch (error) {
- case HA_ERR_NON_UNIQUE_BLOCK_SIZE:
- errmsg= "Indexes use different block sizes";
- break;
- case HA_ERR_OUT_OF_MEM:
- errmsg= "Failed to allocate buffer";
- break;
- default:
- char buf[ERRMSGSIZE+20];
- my_snprintf(buf, ERRMSGSIZE,
- "Failed to read from index file (errno: %d)", my_errno);
- errmsg= buf;
- }
- error= HA_ADMIN_FAILED;
- goto err;
- }
-
- DBUG_RETURN(HA_ADMIN_OK);
-
- err:
- {
- MI_CHECK param;
- myisamchk_init(&param);
- param.thd= thd;
- param.op_name= "preload_keys";
- param.db_name= table->s->db;
- param.table_name= table->s->table_name;
- param.testflag= 0;
- mi_check_print_error(&param, errmsg);
- DBUG_RETURN(error);
- }
-}
-
-
-/*
- Disable indexes, making it persistent if requested.
-
- SYNOPSIS
- disable_indexes()
- mode mode of operation:
- HA_KEY_SWITCH_NONUNIQ disable all non-unique keys
- HA_KEY_SWITCH_ALL disable all keys
- HA_KEY_SWITCH_NONUNIQ_SAVE dis. non-uni. and make persistent
- HA_KEY_SWITCH_ALL_SAVE dis. all keys and make persistent
-
- IMPLEMENTATION
- HA_KEY_SWITCH_NONUNIQ is not implemented.
- HA_KEY_SWITCH_ALL_SAVE is not implemented.
-
- RETURN
- 0 ok
- HA_ERR_WRONG_COMMAND mode not implemented.
-*/
-
-int ha_myisam::disable_indexes(uint mode)
-{
- int error;
-
- if (mode == HA_KEY_SWITCH_ALL)
- {
- /* call a storage engine function to switch the key map */
- error= mi_disable_indexes(file);
- }
- else if (mode == HA_KEY_SWITCH_NONUNIQ_SAVE)
- {
- mi_extra(file, HA_EXTRA_NO_KEYS, 0);
- info(HA_STATUS_CONST); // Read new key info
- error= 0;
- }
- else
- {
- /* mode not implemented */
- error= HA_ERR_WRONG_COMMAND;
- }
- return error;
-}
-
-
-/*
- Enable indexes, making it persistent if requested.
-
- SYNOPSIS
- enable_indexes()
- mode mode of operation:
- HA_KEY_SWITCH_NONUNIQ enable all non-unique keys
- HA_KEY_SWITCH_ALL enable all keys
- HA_KEY_SWITCH_NONUNIQ_SAVE en. non-uni. and make persistent
- HA_KEY_SWITCH_ALL_SAVE en. all keys and make persistent
-
- DESCRIPTION
- Enable indexes, which might have been disabled by disable_index() before.
- The modes without _SAVE work only if both data and indexes are empty,
- since the MyISAM repair would enable them persistently.
- To be sure in these cases, call handler::delete_all_rows() before.
-
- IMPLEMENTATION
- HA_KEY_SWITCH_NONUNIQ is not implemented.
- HA_KEY_SWITCH_ALL_SAVE is not implemented.
-
- RETURN
- 0 ok
- !=0 Error, among others:
- HA_ERR_CRASHED data or index is non-empty. Delete all rows and retry.
- HA_ERR_WRONG_COMMAND mode not implemented.
-*/
-
-int ha_myisam::enable_indexes(uint mode)
-{
- int error;
-
- if (mi_is_all_keys_active(file->s->state.key_map, file->s->base.keys))
- {
- /* All indexes are enabled already. */
- return 0;
- }
-
- if (mode == HA_KEY_SWITCH_ALL)
- {
- error= mi_enable_indexes(file);
- /*
- Do not try to repair on error,
- as this could make the enabled state persistent,
- but mode==HA_KEY_SWITCH_ALL forbids it.
- */
- }
- else if (mode == HA_KEY_SWITCH_NONUNIQ_SAVE)
- {
- THD *thd=current_thd;
- MI_CHECK param;
- const char *save_proc_info=thd->proc_info;
- thd->proc_info="Creating index";
- myisamchk_init(&param);
- param.op_name= "recreating_index";
- param.testflag= (T_SILENT | T_REP_BY_SORT | T_QUICK |
- T_CREATE_MISSING_KEYS);
- param.myf_rw&= ~MY_WAIT_IF_FULL;
- param.sort_buffer_length= thd->variables.myisam_sort_buff_size;
- param.stats_method= (enum_mi_stats_method)thd->variables.myisam_stats_method;
- param.tmpdir=&mysql_tmpdir_list;
- if ((error= (repair(thd,param,0) != HA_ADMIN_OK)) && param.retry_repair)
- {
- sql_print_warning("Warning: Enabling keys got errno %d on %s.%s, retrying",
- my_errno, param.db_name, param.table_name);
- /* Repairing by sort failed. Now try standard repair method. */
- param.testflag&= ~(T_REP_BY_SORT | T_QUICK);
- error= (repair(thd,param,0) != HA_ADMIN_OK);
- /*
- If the standard repair succeeded, clear all error messages which
- might have been set by the first repair. They can still be seen
- with SHOW WARNINGS then.
- */
- if (! error)
- thd->clear_error();
- }
- info(HA_STATUS_CONST);
- thd->proc_info=save_proc_info;
- }
- else
- {
- /* mode not implemented */
- error= HA_ERR_WRONG_COMMAND;
- }
- return error;
-}
-
-
-/*
- Test if indexes are disabled.
-
-
- SYNOPSIS
- indexes_are_disabled()
- no parameters
-
-
- RETURN
- 0 indexes are not disabled
- 1 all indexes are disabled
- [2 non-unique indexes are disabled - NOT YET IMPLEMENTED]
-*/
-
-int ha_myisam::indexes_are_disabled(void)
-{
-
- return mi_indexes_are_disabled(file);
-}
-
-
-/*
- prepare for a many-rows insert operation
- e.g. - disable indexes (if they can be recreated fast) or
- activate special bulk-insert optimizations
-
- SYNOPSIS
- start_bulk_insert(rows)
- rows Rows to be inserted
- 0 if we don't know
-
- NOTICE
- Do not forget to call end_bulk_insert() later!
-*/
-
-void ha_myisam::start_bulk_insert(ha_rows rows)
-{
- DBUG_ENTER("ha_myisam::start_bulk_insert");
- THD *thd= current_thd;
- ulong size= min(thd->variables.read_buff_size,
- (ulong) (table->s->avg_row_length*rows));
- DBUG_PRINT("info",("start_bulk_insert: rows %lu size %lu",
- (ulong) rows, size));
-
- /* don't enable row cache if too few rows */
- if (! rows || (rows > MI_MIN_ROWS_TO_USE_WRITE_CACHE))
- mi_extra(file, HA_EXTRA_WRITE_CACHE, (void*) &size);
-
- can_enable_indexes= mi_is_all_keys_active(file->s->state.key_map,
- file->s->base.keys);
-
- if (!(specialflag & SPECIAL_SAFE_MODE))
- {
- /*
- Only disable old index if the table was empty and we are inserting
- a lot of rows.
- We should not do this for only a few rows as this is slower and
- we don't want to update the key statistics based of only a few rows.
- */
- if (file->state->records == 0 && can_enable_indexes &&
- (!rows || rows >= MI_MIN_ROWS_TO_DISABLE_INDEXES))
- mi_disable_non_unique_index(file,rows);
- else
- if (!file->bulk_insert &&
- (!rows || rows >= MI_MIN_ROWS_TO_USE_BULK_INSERT))
- {
- mi_init_bulk_insert(file, thd->variables.bulk_insert_buff_size, rows);
- }
- }
- DBUG_VOID_RETURN;
-}
-
-/*
- end special bulk-insert optimizations,
- which have been activated by start_bulk_insert().
-
- SYNOPSIS
- end_bulk_insert()
- no arguments
-
- RETURN
- 0 OK
- != 0 Error
-*/
-
-int ha_myisam::end_bulk_insert()
-{
- mi_end_bulk_insert(file);
- int err=mi_extra(file, HA_EXTRA_NO_CACHE, 0);
- return err ? err : can_enable_indexes ?
- enable_indexes(HA_KEY_SWITCH_NONUNIQ_SAVE) : 0;
-}
-
-
-bool ha_myisam::check_and_repair(THD *thd)
-{
- int error=0;
- int marked_crashed;
- char *old_query;
- uint old_query_length;
- HA_CHECK_OPT check_opt;
- DBUG_ENTER("ha_myisam::check_and_repair");
-
- check_opt.init();
- check_opt.flags= T_MEDIUM | T_AUTO_REPAIR;
- // Don't use quick if deleted rows
- if (!file->state->del && (myisam_recover_options & HA_RECOVER_QUICK))
- check_opt.flags|=T_QUICK;
- sql_print_warning("Checking table: '%s'",table->s->path);
-
- old_query= thd->query;
- old_query_length= thd->query_length;
- pthread_mutex_lock(&LOCK_thread_count);
- thd->query= (char*) table->s->table_name;
- thd->query_length= (uint32) strlen(table->s->table_name);
- pthread_mutex_unlock(&LOCK_thread_count);
-
- if ((marked_crashed= mi_is_crashed(file)) || check(thd, &check_opt))
- {
- sql_print_warning("Recovering table: '%s'",table->s->path);
- check_opt.flags=
- ((myisam_recover_options & HA_RECOVER_BACKUP ? T_BACKUP_DATA : 0) |
- (marked_crashed ? 0 : T_QUICK) |
- (myisam_recover_options & HA_RECOVER_FORCE ? 0 : T_SAFE_REPAIR) |
- T_AUTO_REPAIR);
- if (repair(thd, &check_opt))
- error=1;
- }
- pthread_mutex_lock(&LOCK_thread_count);
- thd->query= old_query;
- thd->query_length= old_query_length;
- pthread_mutex_unlock(&LOCK_thread_count);
- DBUG_RETURN(error);
-}
-
-bool ha_myisam::is_crashed() const
-{
- return (file->s->state.changed & STATE_CRASHED ||
- (my_disable_locking && file->s->state.open_count));
-}
-
-int ha_myisam::update_row(const byte * old_data, byte * new_data)
-{
- statistic_increment(table->in_use->status_var.ha_update_count,&LOCK_status);
- if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_UPDATE)
- table->timestamp_field->set_time();
- return mi_update(file,old_data,new_data);
-}
-
-int ha_myisam::delete_row(const byte * buf)
-{
- statistic_increment(table->in_use->status_var.ha_delete_count,&LOCK_status);
- return mi_delete(file,buf);
-}
-
-int ha_myisam::index_read(byte * buf, const byte * key,
- uint key_len, enum ha_rkey_function find_flag)
-{
- DBUG_ASSERT(inited==INDEX);
- statistic_increment(table->in_use->status_var.ha_read_key_count,
- &LOCK_status);
- int error=mi_rkey(file,buf,active_index, key, key_len, find_flag);
- table->status=error ? STATUS_NOT_FOUND: 0;
- return error;
-}
-
-int ha_myisam::index_read_idx(byte * buf, uint index, const byte * key,
- uint key_len, enum ha_rkey_function find_flag)
-{
- statistic_increment(table->in_use->status_var.ha_read_key_count,
- &LOCK_status);
- int error=mi_rkey(file,buf,index, key, key_len, find_flag);
- table->status=error ? STATUS_NOT_FOUND: 0;
- return error;
-}
-
-int ha_myisam::index_read_last(byte * buf, const byte * key, uint key_len)
-{
- DBUG_ASSERT(inited==INDEX);
- statistic_increment(table->in_use->status_var.ha_read_key_count,
- &LOCK_status);
- int error=mi_rkey(file,buf,active_index, key, key_len, HA_READ_PREFIX_LAST);
- table->status=error ? STATUS_NOT_FOUND: 0;
- return error;
-}
-
-int ha_myisam::index_next(byte * buf)
-{
- DBUG_ASSERT(inited==INDEX);
- statistic_increment(table->in_use->status_var.ha_read_next_count,
- &LOCK_status);
- int error=mi_rnext(file,buf,active_index);
- table->status=error ? STATUS_NOT_FOUND: 0;
- return error;
-}
-
-int ha_myisam::index_prev(byte * buf)
-{
- DBUG_ASSERT(inited==INDEX);
- statistic_increment(table->in_use->status_var.ha_read_prev_count,
- &LOCK_status);
- int error=mi_rprev(file,buf, active_index);
- table->status=error ? STATUS_NOT_FOUND: 0;
- return error;
-}
-
-int ha_myisam::index_first(byte * buf)
-{
- DBUG_ASSERT(inited==INDEX);
- statistic_increment(table->in_use->status_var.ha_read_first_count,
- &LOCK_status);
- int error=mi_rfirst(file, buf, active_index);
- table->status=error ? STATUS_NOT_FOUND: 0;
- return error;
-}
-
-int ha_myisam::index_last(byte * buf)
-{
- DBUG_ASSERT(inited==INDEX);
- statistic_increment(table->in_use->status_var.ha_read_last_count,
- &LOCK_status);
- int error=mi_rlast(file, buf, active_index);
- table->status=error ? STATUS_NOT_FOUND: 0;
- return error;
-}
-
-int ha_myisam::index_next_same(byte * buf,
- const byte *key __attribute__((unused)),
- uint length __attribute__((unused)))
-{
- int error;
- DBUG_ASSERT(inited==INDEX);
- statistic_increment(table->in_use->status_var.ha_read_next_count,
- &LOCK_status);
- do
- {
- error= mi_rnext_same(file,buf);
- } while (error == HA_ERR_RECORD_DELETED);
- table->status=error ? STATUS_NOT_FOUND: 0;
- return error;
-}
-
-
-int ha_myisam::rnd_init(bool scan)
-{
- if (scan)
- return mi_scan_init(file);
- return mi_extra(file, HA_EXTRA_RESET, 0);
-}
-
-int ha_myisam::rnd_next(byte *buf)
-{
- statistic_increment(table->in_use->status_var.ha_read_rnd_next_count,
- &LOCK_status);
- int error=mi_scan(file, buf);
- table->status=error ? STATUS_NOT_FOUND: 0;
- return error;
-}
-
-int ha_myisam::restart_rnd_next(byte *buf, byte *pos)
-{
- return rnd_pos(buf,pos);
-}
-
-int ha_myisam::rnd_pos(byte * buf, byte *pos)
-{
- statistic_increment(table->in_use->status_var.ha_read_rnd_count,
- &LOCK_status);
- int error=mi_rrnd(file, buf, my_get_ptr(pos,ref_length));
- table->status=error ? STATUS_NOT_FOUND: 0;
- return error;
-}
-
-void ha_myisam::position(const byte* record)
-{
- my_off_t row_position= mi_position(file);
- my_store_ptr(ref, ref_length, row_position);
-}
-
-int ha_myisam::info(uint flag)
-{
- MI_ISAMINFO misam_info;
- char name_buff[FN_REFLEN];
-
- (void) mi_status(file,&misam_info,flag);
- if (flag & HA_STATUS_VARIABLE)
- {
- records= misam_info.records;
- deleted= misam_info.deleted;
- data_file_length= misam_info.data_file_length;
- index_file_length= misam_info.index_file_length;
- delete_length= misam_info.delete_length;
- check_time= misam_info.check_time;
- mean_rec_length= misam_info.mean_reclength;
- }
- if (flag & HA_STATUS_CONST)
- {
- TABLE_SHARE *share= table->s;
- max_data_file_length= misam_info.max_data_file_length;
- max_index_file_length= misam_info.max_index_file_length;
- create_time= misam_info.create_time;
- sortkey= misam_info.sortkey;
- ref_length= misam_info.reflength;
- share->db_options_in_use= misam_info.options;
- block_size= myisam_block_size;
- share->keys_in_use.set_prefix(share->keys);
- share->keys_in_use.intersect_extended(misam_info.key_map);
- share->keys_for_keyread.intersect(share->keys_in_use);
- share->db_record_offset= misam_info.record_offset;
- if (share->key_parts)
- memcpy((char*) table->key_info[0].rec_per_key,
- (char*) misam_info.rec_per_key,
- sizeof(table->key_info[0].rec_per_key)*share->key_parts);
- raid_type= misam_info.raid_type;
- raid_chunks= misam_info.raid_chunks;
- raid_chunksize= misam_info.raid_chunksize;
-
- /*
- Set data_file_name and index_file_name to point at the symlink value
- if table is symlinked (Ie; Real name is not same as generated name)
- */
- data_file_name=index_file_name=0;
- fn_format(name_buff, file->filename, "", MI_NAME_DEXT, 2);
- if (strcmp(name_buff, misam_info.data_file_name))
- data_file_name= misam_info.data_file_name;
- strmov(fn_ext(name_buff),MI_NAME_IEXT);
- if (strcmp(name_buff, misam_info.index_file_name))
- index_file_name= misam_info.index_file_name;
- }
- if (flag & HA_STATUS_ERRKEY)
- {
- errkey = misam_info.errkey;
- my_store_ptr(dupp_ref, ref_length, misam_info.dupp_key_pos);
- }
- if (flag & HA_STATUS_TIME)
- update_time = misam_info.update_time;
- if (flag & HA_STATUS_AUTO)
- auto_increment_value= misam_info.auto_increment;
-
- return 0;
-}
-
-
-int ha_myisam::extra(enum ha_extra_function operation)
-{
- if ((specialflag & SPECIAL_SAFE_MODE) && operation == HA_EXTRA_KEYREAD)
- return 0;
- return mi_extra(file, operation, 0);
-}
-
-
-/* To be used with WRITE_CACHE and EXTRA_CACHE */
-
-int ha_myisam::extra_opt(enum ha_extra_function operation, ulong cache_size)
-{
- if ((specialflag & SPECIAL_SAFE_MODE) && operation == HA_EXTRA_WRITE_CACHE)
- return 0;
- return mi_extra(file, operation, (void*) &cache_size);
-}
-
-int ha_myisam::delete_all_rows()
-{
- return mi_delete_all_rows(file);
-}
-
-int ha_myisam::delete_table(const char *name)
-{
- return mi_delete_table(name);
-}
-
-
-int ha_myisam::external_lock(THD *thd, int lock_type)
-{
- return mi_lock_database(file, !table->s->tmp_table ?
- lock_type : ((lock_type == F_UNLCK) ?
- F_UNLCK : F_EXTRA_LCK));
-}
-
-THR_LOCK_DATA **ha_myisam::store_lock(THD *thd,
- THR_LOCK_DATA **to,
- enum thr_lock_type lock_type)
-{
- if (lock_type != TL_IGNORE && file->lock.type == TL_UNLOCK)
- file->lock.type=lock_type;
- *to++= &file->lock;
- return to;
-}
-
-void ha_myisam::update_create_info(HA_CREATE_INFO *create_info)
-{
- ha_myisam::info(HA_STATUS_AUTO | HA_STATUS_CONST);
- if (!(create_info->used_fields & HA_CREATE_USED_AUTO))
- {
- create_info->auto_increment_value=auto_increment_value;
- }
- if (!(create_info->used_fields & HA_CREATE_USED_RAID))
- {
- create_info->raid_type= raid_type;
- create_info->raid_chunks= raid_chunks;
- create_info->raid_chunksize= raid_chunksize;
- }
- create_info->data_file_name=data_file_name;
- create_info->index_file_name=index_file_name;
-}
-
-
-int ha_myisam::create(const char *name, register TABLE *table_arg,
- HA_CREATE_INFO *ha_create_info)
-{
- int error;
- uint create_flags= 0, records;
- char buff[FN_REFLEN];
- MI_KEYDEF *keydef;
- MI_COLUMNDEF *recinfo;
- MI_CREATE_INFO create_info;
- TABLE_SHARE *share= table->s;
- uint options= share->db_options_in_use;
- DBUG_ENTER("ha_myisam::create");
- if ((error= table2myisam(table_arg, &keydef, &recinfo, &records)))
- DBUG_RETURN(error); /* purecov: inspected */
- bzero((char*) &create_info, sizeof(create_info));
- create_info.max_rows= share->max_rows;
- create_info.reloc_rows= share->min_rows;
- create_info.with_auto_increment= share->next_number_key_offset == 0;
- create_info.auto_increment= (ha_create_info->auto_increment_value ?
- ha_create_info->auto_increment_value -1 :
- (ulonglong) 0);
- create_info.data_file_length= ((ulonglong) share->max_rows *
- share->avg_row_length);
- create_info.raid_type= ha_create_info->raid_type;
- create_info.raid_chunks= (ha_create_info->raid_chunks ?
- ha_create_info->raid_chunks :
- RAID_DEFAULT_CHUNKS);
- create_info.raid_chunksize= (ha_create_info->raid_chunksize ?
- ha_create_info->raid_chunksize :
- RAID_DEFAULT_CHUNKSIZE);
- create_info.data_file_name= ha_create_info->data_file_name;
- create_info.index_file_name= ha_create_info->index_file_name;
-
- if (ha_create_info->options & HA_LEX_CREATE_TMP_TABLE)
- create_flags|= HA_CREATE_TMP_TABLE;
- if (ha_create_info->options & HA_CREATE_KEEP_FILES)
- create_flags|= HA_CREATE_KEEP_FILES;
- if (options & HA_OPTION_PACK_RECORD)
- create_flags|= HA_PACK_RECORD;
- if (options & HA_OPTION_CHECKSUM)
- create_flags|= HA_CREATE_CHECKSUM;
- if (options & HA_OPTION_DELAY_KEY_WRITE)
- create_flags|= HA_CREATE_DELAY_KEY_WRITE;
-
- /* TODO: Check that the following fn_format is really needed */
- error= mi_create(fn_format(buff, name, "", "",
- MY_UNPACK_FILENAME|MY_REPLACE_EXT),
- share->keys, keydef,
- records, recinfo,
- 0, (MI_UNIQUEDEF*) 0,
- &create_info, create_flags);
- my_free((gptr) recinfo, MYF(0));
- DBUG_RETURN(error);
-}
-
-
-int ha_myisam::rename_table(const char * from, const char * to)
-{
- return mi_rename(from,to);
-}
-
-
-ulonglong ha_myisam::get_auto_increment()
-{
- ulonglong nr;
- int error;
- byte key[MI_MAX_KEY_LENGTH];
-
- if (!table->s->next_number_key_offset)
- { // Autoincrement at key-start
- ha_myisam::info(HA_STATUS_AUTO);
- return auto_increment_value;
- }
-
- /* it's safe to call the following if bulk_insert isn't on */
- mi_flush_bulk_insert(file, table->s->next_number_index);
-
- (void) extra(HA_EXTRA_KEYREAD);
- key_copy(key, table->record[0],
- table->key_info + table->s->next_number_index,
- table->s->next_number_key_offset);
- error= mi_rkey(file,table->record[1],(int) table->s->next_number_index,
- key,table->s->next_number_key_offset,HA_READ_PREFIX_LAST);
- if (error)
- nr= 1;
- else
- {
- /* Get data from record[1] */
- nr= ((ulonglong) table->next_number_field->
- val_int_offset(table->s->rec_buff_length)+1);
- }
- extra(HA_EXTRA_NO_KEYREAD);
- return nr;
-}
-
-
-/*
- Find out how many rows there is in the given range
-
- SYNOPSIS
- records_in_range()
- inx Index to use
- min_key Start of range. Null pointer if from first key
- max_key End of range. Null pointer if to last key
-
- NOTES
- min_key.flag can have one of the following values:
- HA_READ_KEY_EXACT Include the key in the range
- HA_READ_AFTER_KEY Don't include key in range
-
- max_key.flag can have one of the following values:
- HA_READ_BEFORE_KEY Don't include key in range
- HA_READ_AFTER_KEY Include all 'end_key' values in the range
-
- RETURN
- HA_POS_ERROR Something is wrong with the index tree.
- 0 There is no matching keys in the given range
- number > 0 There is approximately 'number' matching rows in
- the range.
-*/
-
-ha_rows ha_myisam::records_in_range(uint inx, key_range *min_key,
- key_range *max_key)
-{
- return (ha_rows) mi_records_in_range(file, (int) inx, min_key, max_key);
-}
-
-
-int ha_myisam::ft_read(byte * buf)
-{
- int error;
-
- if (!ft_handler)
- return -1;
-
- thread_safe_increment(table->in_use->status_var.ha_read_next_count,
- &LOCK_status); // why ?
-
- error=ft_handler->please->read_next(ft_handler,(char*) buf);
-
- table->status=error ? STATUS_NOT_FOUND: 0;
- return error;
-}
-
-uint ha_myisam::checksum() const
-{
- return (uint)file->state->checksum;
-}
-
-#ifdef HAVE_QUERY_CACHE
-/**
- @brief Register a named table with a call back function to the query cache.
-
- @param thd The thread handle
- @param table_key A pointer to the table name in the table cache
- @param key_length The length of the table name
- @param[out] engine_callback The pointer to the storage engine call back
- function, currently 0
- @param[out] engine_data Engine data will be set to 0.
-
- @note Despite the name of this function, it is used to check each statement
- before it is cached and not to register a table or callback function.
-
- @see handler::register_query_cache_table
-
- @return The error code. The engine_data and engine_callback will be set to 0.
- @retval TRUE Success
- @retval FALSE An error occured
-*/
-
-my_bool ha_myisam::register_query_cache_table(THD *thd, char *table_name,
- uint table_name_len,
- qc_engine_callback
- *engine_callback,
- ulonglong *engine_data)
-{
- DBUG_ENTER("ha_myisam::register_query_cache_table");
- /*
- No call back function is needed to determine if a cached statement
- is valid or not.
- */
- *engine_callback= 0;
-
- /*
- No engine data is needed.
- */
- *engine_data= 0;
-
- if (file->s->concurrent_insert)
- {
- /*
- If a concurrent INSERT has happened just before the currently
- processed SELECT statement, the total size of the table is
- unknown.
-
- To determine if the table size is known, the current thread's snap
- shot of the table size with the actual table size are compared.
-
- If the table size is unknown the SELECT statement can't be cached.
-
- When concurrent inserts are disabled at table open, mi_open()
- does not assign a get_status() function. In this case the local
- ("current") status is never updated. We would wrongly think that
- we cannot cache the statement.
- */
- ulonglong actual_data_file_length;
- ulonglong current_data_file_length;
-
- /*
- POSIX visibility rules specify that "2. Whatever memory values a
- thread can see when it unlocks a mutex <...> can also be seen by any
- thread that later locks the same mutex". In this particular case,
- concurrent insert thread had modified the data_file_length in
- MYISAM_SHARE before it has unlocked (or even locked)
- structure_guard_mutex. So, here we're guaranteed to see at least that
- value after we've locked the same mutex. We can see a later value
- (modified by some other thread) though, but it's ok, as we only want
- to know if the variable was changed, the actual new value doesn't matter
- */
- actual_data_file_length= file->s->state.state.data_file_length;
- current_data_file_length= file->save_state.data_file_length;
-
- if (current_data_file_length != actual_data_file_length)
- {
- /* Don't cache current statement. */
- DBUG_RETURN(FALSE);
- }
- }
-
- /* It is ok to try to cache current statement. */
- DBUG_RETURN(TRUE);
-}
-#endif
diff --git a/sql/ha_myisam.h b/sql/ha_myisam.h
deleted file mode 100644
index 536ea211820..00000000000
--- a/sql/ha_myisam.h
+++ /dev/null
@@ -1,137 +0,0 @@
-/* Copyright (C) 2000-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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
-
-
-#ifdef USE_PRAGMA_INTERFACE
-#pragma interface /* gcc class implementation */
-#endif
-
-/* class for the the myisam handler */
-
-#include <myisam.h>
-#include <ft_global.h>
-
-#define HA_RECOVER_NONE 0 /* No automatic recover */
-#define HA_RECOVER_DEFAULT 1 /* Automatic recover active */
-#define HA_RECOVER_BACKUP 2 /* Make a backupfile on recover */
-#define HA_RECOVER_FORCE 4 /* Recover even if we loose rows */
-#define HA_RECOVER_QUICK 8 /* Don't check rows in data file */
-
-extern ulong myisam_sort_buffer_size;
-extern TYPELIB myisam_recover_typelib;
-extern ulong myisam_recover_options;
-
-class ha_myisam: public handler
-{
- MI_INFO *file;
- ulong int_table_flags;
- char *data_file_name, *index_file_name;
- bool can_enable_indexes;
- int repair(THD *thd, MI_CHECK &param, bool optimize);
-
- public:
- ha_myisam(TABLE *table_arg);
- ~ha_myisam() {}
- handler *clone(MEM_ROOT *mem_root);
- const char *table_type() const { return "MyISAM"; }
- const char *index_type(uint key_number);
- const char **bas_ext() const;
- ulong table_flags() const { return int_table_flags; }
- ulong index_flags(uint inx, uint part, bool all_parts) const
- {
- return ((table->key_info[inx].algorithm == HA_KEY_ALG_FULLTEXT) ?
- 0 : HA_READ_NEXT | HA_READ_PREV | HA_READ_RANGE |
- HA_READ_ORDER | HA_KEYREAD_ONLY);
- }
- uint max_supported_keys() const { return MI_MAX_KEY; }
- uint max_supported_key_length() const { return MI_MAX_KEY_LENGTH; }
- uint max_supported_key_part_length() const { return MI_MAX_KEY_LENGTH; }
- uint checksum() const;
-
- int open(const char *name, int mode, uint test_if_locked);
- int close(void);
- int write_row(byte * buf);
- int update_row(const byte * old_data, byte * new_data);
- int delete_row(const byte * buf);
- int index_read(byte * buf, const byte * key,
- uint key_len, enum ha_rkey_function find_flag);
- int index_read_idx(byte * buf, uint idx, const byte * key,
- uint key_len, enum ha_rkey_function find_flag);
- int index_read_last(byte * buf, const byte * key, uint key_len);
- int index_next(byte * buf);
- int index_prev(byte * buf);
- int index_first(byte * buf);
- int index_last(byte * buf);
- int index_next_same(byte *buf, const byte *key, uint keylen);
- int ft_init()
- {
- if (!ft_handler)
- return 1;
- ft_handler->please->reinit_search(ft_handler);
- return 0;
- }
- FT_INFO *ft_init_ext(uint flags, uint inx,String *key)
- {
- return ft_init_search(flags,file,inx,
- (byte *)key->ptr(), key->length(), key->charset(),
- table->record[0]);
- }
- int ft_read(byte *buf);
- int rnd_init(bool scan);
- int rnd_next(byte *buf);
- int rnd_pos(byte * buf, byte *pos);
- int restart_rnd_next(byte *buf, byte *pos);
- void position(const byte *record);
- int info(uint);
- int extra(enum ha_extra_function operation);
- int extra_opt(enum ha_extra_function operation, ulong cache_size);
- int external_lock(THD *thd, int lock_type);
- int delete_all_rows(void);
- int disable_indexes(uint mode);
- int enable_indexes(uint mode);
- int indexes_are_disabled(void);
- void start_bulk_insert(ha_rows rows);
- int end_bulk_insert();
- ha_rows records_in_range(uint inx, key_range *min_key, key_range *max_key);
- void update_create_info(HA_CREATE_INFO *create_info);
- int create(const char *name, TABLE *form, HA_CREATE_INFO *create_info);
- THR_LOCK_DATA **store_lock(THD *thd, THR_LOCK_DATA **to,
- enum thr_lock_type lock_type);
- ulonglong get_auto_increment();
- int rename_table(const char * from, const char * to);
- int delete_table(const char *name);
- int check(THD* thd, HA_CHECK_OPT* check_opt);
- int analyze(THD* thd,HA_CHECK_OPT* check_opt);
- int repair(THD* thd, HA_CHECK_OPT* check_opt);
- bool check_and_repair(THD *thd);
- bool is_crashed() const;
- bool auto_repair() const { return myisam_recover_options != 0; }
- int optimize(THD* thd, HA_CHECK_OPT* check_opt);
- int restore(THD* thd, HA_CHECK_OPT* check_opt);
- int backup(THD* thd, HA_CHECK_OPT* check_opt);
- int assign_to_keycache(THD* thd, HA_CHECK_OPT* check_opt);
- int preload_keys(THD* thd, HA_CHECK_OPT* check_opt);
-#ifdef HAVE_REPLICATION
- int dump(THD* thd, int fd);
- int net_read_dump(NET* net);
-#endif
-#ifdef HAVE_QUERY_CACHE
- my_bool register_query_cache_table(THD *thd, char *table_key,
- uint key_length,
- qc_engine_callback
- *engine_callback,
- ulonglong *engine_data);
-#endif
-};
diff --git a/sql/ha_myisammrg.cc b/sql/ha_myisammrg.cc
deleted file mode 100644
index f15a37efdc5..00000000000
--- a/sql/ha_myisammrg.cc
+++ /dev/null
@@ -1,642 +0,0 @@
-/* Copyright (C) 2000-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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
-
-
-#ifdef USE_PRAGMA_IMPLEMENTATION
-#pragma implementation // gcc: Class implementation
-#endif
-
-#include "mysql_priv.h"
-#include <m_ctype.h>
-#include "ha_myisammrg.h"
-#ifndef MASTER
-#include "../srclib/myisammrg/myrg_def.h"
-#else
-#include "../myisammrg/myrg_def.h"
-#endif
-
-/*****************************************************************************
-** MyISAM MERGE tables
-*****************************************************************************/
-
-/* MyISAM MERGE handlerton */
-
-handlerton myisammrg_hton= {
- "MRG_MYISAM",
- SHOW_OPTION_YES,
- "Collection of identical MyISAM tables",
- DB_TYPE_MRG_MYISAM,
- NULL,
- 0, /* slot */
- 0, /* savepoint size. */
- NULL, /* close_connection */
- NULL, /* savepoint */
- NULL, /* rollback to savepoint */
- NULL, /* release savepoint */
- NULL, /* commit */
- NULL, /* rollback */
- NULL, /* prepare */
- NULL, /* recover */
- NULL, /* commit_by_xid */
- NULL, /* rollback_by_xid */
- NULL, /* create_cursor_read_view */
- NULL, /* set_cursor_read_view */
- NULL, /* close_cursor_read_view */
- HTON_CAN_RECREATE
-};
-
-
-ha_myisammrg::ha_myisammrg(TABLE *table_arg)
- :handler(&myisammrg_hton, table_arg), file(0)
-{}
-
-static const char *ha_myisammrg_exts[] = {
- ".MRG",
- NullS
-};
-extern int table2myisam(TABLE *table_arg, MI_KEYDEF **keydef_out,
- MI_COLUMNDEF **recinfo_out, uint *records_out);
-extern int check_definition(MI_KEYDEF *t1_keyinfo, MI_COLUMNDEF *t1_recinfo,
- uint t1_keys, uint t1_recs,
- MI_KEYDEF *t2_keyinfo, MI_COLUMNDEF *t2_recinfo,
- uint t2_keys, uint t2_recs, bool strict);
-static void split_file_name(const char *file_name,
- LEX_STRING *db, LEX_STRING *name);
-
-
-extern "C" void myrg_print_wrong_table(const char *table_name)
-{
- LEX_STRING db, name;
- char buf[FN_REFLEN];
- split_file_name(table_name, &db, &name);
- memcpy(buf, db.str, db.length);
- buf[db.length]= '.';
- memcpy(buf + db.length + 1, name.str, name.length);
- buf[db.length + name.length + 1]= 0;
- push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_ERROR,
- ER_ADMIN_WRONG_MRG_TABLE, ER(ER_ADMIN_WRONG_MRG_TABLE),
- buf);
-}
-
-
-const char **ha_myisammrg::bas_ext() const
-{
- return ha_myisammrg_exts;
-}
-
-
-const char *ha_myisammrg::index_type(uint key_number)
-{
- return ((table->key_info[key_number].flags & HA_FULLTEXT) ?
- "FULLTEXT" :
- (table->key_info[key_number].flags & HA_SPATIAL) ?
- "SPATIAL" :
- (table->key_info[key_number].algorithm == HA_KEY_ALG_RTREE) ?
- "RTREE" :
- "BTREE");
-}
-
-
-int ha_myisammrg::open(const char *name, int mode, uint test_if_locked)
-{
- MI_KEYDEF *keyinfo;
- MI_COLUMNDEF *recinfo;
- MYRG_TABLE *u_table;
- uint recs;
- uint keys= table->s->keys;
- int error;
- char name_buff[FN_REFLEN];
-
- DBUG_PRINT("info", ("ha_myisammrg::open"));
- if (!(file=myrg_open(fn_format(name_buff,name,"","",2 | 4), mode,
- test_if_locked)))
- {
- DBUG_PRINT("info", ("ha_myisammrg::open exit %d", my_errno));
- return (my_errno ? my_errno : -1);
- }
- DBUG_PRINT("info", ("ha_myisammrg::open myrg_extrafunc..."));
- myrg_extrafunc(file, query_cache_invalidate_by_MyISAM_filename_ref);
- if (!(test_if_locked == HA_OPEN_WAIT_IF_LOCKED ||
- test_if_locked == HA_OPEN_ABORT_IF_LOCKED))
- myrg_extra(file,HA_EXTRA_NO_WAIT_LOCK,0);
- info(HA_STATUS_NO_LOCK | HA_STATUS_VARIABLE | HA_STATUS_CONST);
- if (!(test_if_locked & HA_OPEN_WAIT_IF_LOCKED))
- myrg_extra(file,HA_EXTRA_WAIT_LOCK,0);
-
- if (table->s->reclength != mean_rec_length && mean_rec_length)
- {
- DBUG_PRINT("error",("reclength: %lu mean_rec_length: %lu",
- table->s->reclength, mean_rec_length));
- if (test_if_locked & HA_OPEN_FOR_REPAIR)
- myrg_print_wrong_table(file->open_tables->table->filename);
- error= HA_ERR_WRONG_MRG_TABLE_DEF;
- goto err;
- }
- if ((error= table2myisam(table, &keyinfo, &recinfo, &recs)))
- {
- /* purecov: begin inspected */
- DBUG_PRINT("error", ("Failed to convert TABLE object to MyISAM "
- "key and column definition"));
- goto err;
- /* purecov: end */
- }
- for (u_table= file->open_tables; u_table < file->end_table; u_table++)
- {
- if (check_definition(keyinfo, recinfo, keys, recs,
- u_table->table->s->keyinfo, u_table->table->s->rec,
- u_table->table->s->base.keys,
- u_table->table->s->base.fields, false))
- {
- error= HA_ERR_WRONG_MRG_TABLE_DEF;
- if (test_if_locked & HA_OPEN_FOR_REPAIR)
- myrg_print_wrong_table(u_table->table->filename);
- else
- {
- my_free((gptr) recinfo, MYF(0));
- goto err;
- }
- }
- }
- my_free((gptr) recinfo, MYF(0));
- if (error == HA_ERR_WRONG_MRG_TABLE_DEF)
- goto err;
-#if !defined(BIG_TABLES) || SIZEOF_OFF_T == 4
- /* Merge table has more than 2G rows */
- if (table->s->crashed)
- {
- error= HA_ERR_WRONG_MRG_TABLE_DEF;
- goto err;
- }
-#endif
- return (0);
-err:
- myrg_close(file);
- file=0;
- return (my_errno= error);
-}
-
-int ha_myisammrg::close(void)
-{
- return myrg_close(file);
-}
-
-int ha_myisammrg::write_row(byte * buf)
-{
- statistic_increment(table->in_use->status_var.ha_write_count,&LOCK_status);
-
- if (file->merge_insert_method == MERGE_INSERT_DISABLED || !file->tables)
- return (HA_ERR_TABLE_READONLY);
-
- if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_INSERT)
- table->timestamp_field->set_time();
- if (table->next_number_field && buf == table->record[0])
- {
- int error;
- if ((error= update_auto_increment()))
- return error;
- }
- return myrg_write(file,buf);
-}
-
-int ha_myisammrg::update_row(const byte * old_data, byte * new_data)
-{
- statistic_increment(table->in_use->status_var.ha_update_count,&LOCK_status);
- if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_UPDATE)
- table->timestamp_field->set_time();
- return myrg_update(file,old_data,new_data);
-}
-
-int ha_myisammrg::delete_row(const byte * buf)
-{
- statistic_increment(table->in_use->status_var.ha_delete_count,&LOCK_status);
- return myrg_delete(file,buf);
-}
-
-int ha_myisammrg::index_read(byte * buf, const byte * key,
- uint key_len, enum ha_rkey_function find_flag)
-{
- statistic_increment(table->in_use->status_var.ha_read_key_count,
- &LOCK_status);
- int error=myrg_rkey(file,buf,active_index, key, key_len, find_flag);
- table->status=error ? STATUS_NOT_FOUND: 0;
- return error;
-}
-
-int ha_myisammrg::index_read_idx(byte * buf, uint index, const byte * key,
- uint key_len, enum ha_rkey_function find_flag)
-{
- statistic_increment(table->in_use->status_var.ha_read_key_count,
- &LOCK_status);
- int error=myrg_rkey(file,buf,index, key, key_len, find_flag);
- table->status=error ? STATUS_NOT_FOUND: 0;
- return error;
-}
-
-int ha_myisammrg::index_read_last(byte * buf, const byte * key, uint key_len)
-{
- statistic_increment(table->in_use->status_var.ha_read_key_count,
- &LOCK_status);
- int error=myrg_rkey(file,buf,active_index, key, key_len,
- HA_READ_PREFIX_LAST);
- table->status=error ? STATUS_NOT_FOUND: 0;
- return error;
-}
-
-int ha_myisammrg::index_next(byte * buf)
-{
- statistic_increment(table->in_use->status_var.ha_read_next_count,
- &LOCK_status);
- int error=myrg_rnext(file,buf,active_index);
- table->status=error ? STATUS_NOT_FOUND: 0;
- return error;
-}
-
-int ha_myisammrg::index_prev(byte * buf)
-{
- statistic_increment(table->in_use->status_var.ha_read_prev_count,
- &LOCK_status);
- int error=myrg_rprev(file,buf, active_index);
- table->status=error ? STATUS_NOT_FOUND: 0;
- return error;
-}
-
-int ha_myisammrg::index_first(byte * buf)
-{
- statistic_increment(table->in_use->status_var.ha_read_first_count,
- &LOCK_status);
- int error=myrg_rfirst(file, buf, active_index);
- table->status=error ? STATUS_NOT_FOUND: 0;
- return error;
-}
-
-int ha_myisammrg::index_last(byte * buf)
-{
- statistic_increment(table->in_use->status_var.ha_read_last_count,
- &LOCK_status);
- int error=myrg_rlast(file, buf, active_index);
- table->status=error ? STATUS_NOT_FOUND: 0;
- return error;
-}
-
-int ha_myisammrg::index_next_same(byte * buf,
- const byte *key __attribute__((unused)),
- uint length __attribute__((unused)))
-{
- int error;
- statistic_increment(table->in_use->status_var.ha_read_next_count,
- &LOCK_status);
- do
- {
- error= myrg_rnext_same(file,buf);
- } while (error == HA_ERR_RECORD_DELETED);
- table->status=error ? STATUS_NOT_FOUND: 0;
- return error;
-}
-
-int ha_myisammrg::rnd_init(bool scan)
-{
- return myrg_extra(file,HA_EXTRA_RESET,0);
-}
-
-int ha_myisammrg::rnd_next(byte *buf)
-{
- statistic_increment(table->in_use->status_var.ha_read_rnd_next_count,
- &LOCK_status);
- int error=myrg_rrnd(file, buf, HA_OFFSET_ERROR);
- table->status=error ? STATUS_NOT_FOUND: 0;
- return error;
-}
-
-int ha_myisammrg::rnd_pos(byte * buf, byte *pos)
-{
- statistic_increment(table->in_use->status_var.ha_read_rnd_count,
- &LOCK_status);
- int error=myrg_rrnd(file, buf, my_get_ptr(pos,ref_length));
- table->status=error ? STATUS_NOT_FOUND: 0;
- return error;
-}
-
-void ha_myisammrg::position(const byte *record)
-{
- ulonglong row_position= myrg_position(file);
- my_store_ptr(ref, ref_length, (my_off_t) row_position);
-}
-
-
-ha_rows ha_myisammrg::records_in_range(uint inx, key_range *min_key,
- key_range *max_key)
-{
- return (ha_rows) myrg_records_in_range(file, (int) inx, min_key, max_key);
-}
-
-
-int ha_myisammrg::info(uint flag)
-{
- MYMERGE_INFO mrg_info;
- (void) myrg_status(file,&mrg_info,flag);
- /*
- The following fails if one has not compiled MySQL with -DBIG_TABLES
- and one has more than 2^32 rows in the merge tables.
- */
- records = (ha_rows) mrg_info.records;
- deleted = (ha_rows) mrg_info.deleted;
-#if !defined(BIG_TABLES) || SIZEOF_OFF_T == 4
- if ((mrg_info.records >= (ulonglong) 1 << 32) ||
- (mrg_info.deleted >= (ulonglong) 1 << 32))
- table->s->crashed= 1;
-#endif
- data_file_length=mrg_info.data_file_length;
- errkey = mrg_info.errkey;
- table->s->keys_in_use.set_prefix(table->s->keys);
- table->s->db_options_in_use= mrg_info.options;
- table->s->is_view= 1;
- mean_rec_length= mrg_info.reclength;
-
- /*
- The handler::block_size is used all over the code in index scan cost
- calculations. It is used to get number of disk seeks required to
- retrieve a number of index tuples.
- If the merge table has N underlying tables, then (assuming underlying
- tables have equal size, the only "simple" approach we can use)
- retrieving X index records from a merge table will require N times more
- disk seeks compared to doing the same on a MyISAM table with equal
- number of records.
- In the edge case (file_tables > myisam_block_size) we'll get
- block_size==0, and index calculation code will act as if we need one
- disk seek to retrieve one index tuple.
-
- TODO: In 5.2 index scan cost calculation will be factored out into a
- virtual function in class handler and we'll be able to remove this hack.
- */
- block_size= 0;
- if (file->tables)
- block_size= myisam_block_size / file->tables;
-
- update_time=0;
-#if SIZEOF_OFF_T > 4
- ref_length=6; // Should be big enough
-#else
- ref_length=4; // Can't be > than my_off_t
-#endif
- if (flag & HA_STATUS_CONST)
- {
- if (table->s->key_parts && mrg_info.rec_per_key)
- {
-#ifdef HAVE_purify
- /*
- valgrind may be unhappy about it, because optimizer may access values
- between file->keys and table->key_parts, that will be uninitialized.
- It's safe though, because even if opimizer will decide to use a key
- with such a number, it'll be an error later anyway.
- */
- bzero((char*) table->key_info[0].rec_per_key,
- sizeof(table->key_info[0].rec_per_key) * table->s->key_parts);
-#endif
- memcpy((char*) table->key_info[0].rec_per_key,
- (char*) mrg_info.rec_per_key,
- sizeof(table->key_info[0].rec_per_key) *
- min(file->keys, table->s->key_parts));
- }
- }
- return 0;
-}
-
-
-int ha_myisammrg::extra(enum ha_extra_function operation)
-{
- /* As this is just a mapping, we don't have to force the underlying
- tables to be closed */
- if (operation == HA_EXTRA_FORCE_REOPEN ||
- operation == HA_EXTRA_PREPARE_FOR_DELETE)
- return 0;
- return myrg_extra(file,operation,0);
-}
-
-
-/* To be used with WRITE_CACHE, EXTRA_CACHE and BULK_INSERT_BEGIN */
-
-int ha_myisammrg::extra_opt(enum ha_extra_function operation, ulong cache_size)
-{
- if ((specialflag & SPECIAL_SAFE_MODE) && operation == HA_EXTRA_WRITE_CACHE)
- return 0;
- return myrg_extra(file, operation, (void*) &cache_size);
-}
-
-int ha_myisammrg::external_lock(THD *thd, int lock_type)
-{
- return myrg_lock_database(file,lock_type);
-}
-
-uint ha_myisammrg::lock_count(void) const
-{
- return file->tables;
-}
-
-
-THR_LOCK_DATA **ha_myisammrg::store_lock(THD *thd,
- THR_LOCK_DATA **to,
- enum thr_lock_type lock_type)
-{
- MYRG_TABLE *open_table;
-
- for (open_table=file->open_tables ;
- open_table != file->end_table ;
- open_table++)
- {
- *(to++)= &open_table->table->lock;
- if (lock_type != TL_IGNORE && open_table->table->lock.type == TL_UNLOCK)
- open_table->table->lock.type=lock_type;
- }
- return to;
-}
-
-
-/* Find out database name and table name from a filename */
-
-static void split_file_name(const char *file_name,
- LEX_STRING *db, LEX_STRING *name)
-{
- uint dir_length, prefix_length;
- char buff[FN_REFLEN];
-
- db->length= 0;
- strmake(buff, file_name, sizeof(buff)-1);
- dir_length= dirname_length(buff);
- if (dir_length > 1)
- {
- /* Get database */
- buff[dir_length-1]= 0; // Remove end '/'
- prefix_length= dirname_length(buff);
- db->str= (char*) file_name+ prefix_length;
- db->length= dir_length - prefix_length -1;
- }
- name->str= (char*) file_name+ dir_length;
- name->length= (uint) (fn_ext(name->str) - name->str);
-}
-
-
-void ha_myisammrg::update_create_info(HA_CREATE_INFO *create_info)
-{
- DBUG_ENTER("ha_myisammrg::update_create_info");
-
- if (!(create_info->used_fields & HA_CREATE_USED_UNION))
- {
- MYRG_TABLE *open_table;
- THD *thd=current_thd;
-
- create_info->merge_list.next= &create_info->merge_list.first;
- create_info->merge_list.elements=0;
-
- for (open_table=file->open_tables ;
- open_table != file->end_table ;
- open_table++)
- {
- TABLE_LIST *ptr;
- LEX_STRING db, name;
-
- if (!(ptr = (TABLE_LIST *) thd->calloc(sizeof(TABLE_LIST))))
- goto err;
- split_file_name(open_table->table->filename, &db, &name);
- if (!(ptr->table_name= thd->strmake(name.str, name.length)))
- goto err;
- if (db.length && !(ptr->db= thd->strmake(db.str, db.length)))
- goto err;
-
- create_info->merge_list.elements++;
- (*create_info->merge_list.next) = (byte*) ptr;
- create_info->merge_list.next= (byte**) &ptr->next_local;
- }
- *create_info->merge_list.next=0;
- }
- if (!(create_info->used_fields & HA_CREATE_USED_INSERT_METHOD))
- {
- create_info->merge_insert_method = file->merge_insert_method;
- }
- DBUG_VOID_RETURN;
-
-err:
- create_info->merge_list.elements=0;
- create_info->merge_list.first=0;
- DBUG_VOID_RETURN;
-}
-
-
-int ha_myisammrg::create(const char *name, register TABLE *form,
- HA_CREATE_INFO *create_info)
-{
- char buff[FN_REFLEN];
- const char **table_names, **pos;
- TABLE_LIST *tables= (TABLE_LIST*) create_info->merge_list.first;
- THD *thd= current_thd;
- uint dirlgt= dirname_length(name);
- DBUG_ENTER("ha_myisammrg::create");
-
- if (!(table_names= (const char**)
- thd->alloc((create_info->merge_list.elements+1) * sizeof(char*))))
- DBUG_RETURN(HA_ERR_OUT_OF_MEM);
- for (pos= table_names; tables; tables= tables->next_local)
- {
- const char *table_name;
- TABLE **tbl= 0;
- if (create_info->options & HA_LEX_CREATE_TMP_TABLE)
- tbl= find_temporary_table(thd, tables->db, tables->table_name);
- if (!tbl)
- {
- /*
- Construct the path to the MyISAM table. Try to meet two conditions:
- 1.) Allow to include MyISAM tables from different databases, and
- 2.) allow for moving DATADIR around in the file system.
- The first means that we need paths in the .MRG file. The second
- means that we should not have absolute paths in the .MRG file.
- The best, we can do, is to use 'mysql_data_home', which is '.'
- in mysqld and may be an absolute path in an embedded server.
- This means that it might not be possible to move the DATADIR of
- an embedded server without changing the paths in the .MRG file.
- */
- uint length= my_snprintf(buff, FN_REFLEN, "%s/%s/%s", mysql_data_home,
- tables->db, tables->table_name);
- /*
- If a MyISAM table is in the same directory as the MERGE table,
- we use the table name without a path. This means that the
- DATADIR can easily be moved even for an embedded server as long
- as the MyISAM tables are from the same database as the MERGE table.
- */
- if ((dirname_length(buff) == dirlgt) && ! memcmp(buff, name, dirlgt))
- table_name= tables->table_name;
- else
- if (! (table_name= thd->strmake(buff, length)))
- DBUG_RETURN(HA_ERR_OUT_OF_MEM);
- }
- else
- table_name= (*tbl)->s->path;
- *pos++= table_name;
- }
- *pos=0;
- DBUG_RETURN(myrg_create(fn_format(buff,name,"","",2+4+16),
- table_names,
- create_info->merge_insert_method,
- (my_bool) 0));
-}
-
-
-void ha_myisammrg::append_create_info(String *packet)
-{
- const char *current_db;
- uint db_length;
- THD *thd= current_thd;
-
- if (file->merge_insert_method != MERGE_INSERT_DISABLED)
- {
- packet->append(STRING_WITH_LEN(" INSERT_METHOD="));
- packet->append(get_type(&merge_insert_method,file->merge_insert_method-1));
- }
- /*
- There is no sence adding UNION clause in case there is no underlying
- tables specified.
- */
- if (file->open_tables == file->end_table)
- return;
- packet->append(STRING_WITH_LEN(" UNION=("));
- MYRG_TABLE *open_table,*first;
-
- current_db= table->s->db;
- db_length= (uint) strlen(current_db);
-
- for (first=open_table=file->open_tables ;
- open_table != file->end_table ;
- open_table++)
- {
- LEX_STRING db, name;
- split_file_name(open_table->table->filename, &db, &name);
- if (open_table != first)
- packet->append(',');
- /* Report database for mapped table if it isn't in current database */
- if (db.length &&
- (db_length != db.length ||
- strncmp(current_db, db.str, db.length)))
- {
- append_identifier(thd, packet, db.str, db.length);
- packet->append('.');
- }
- append_identifier(thd, packet, name.str, name.length);
- }
- packet->append(')');
-}
-
-
-int ha_myisammrg::check(THD* thd, HA_CHECK_OPT* check_opt)
-{
- return HA_ADMIN_OK;
-}
diff --git a/sql/ha_myisammrg.h b/sql/ha_myisammrg.h
deleted file mode 100644
index 2ba5b6b551e..00000000000
--- a/sql/ha_myisammrg.h
+++ /dev/null
@@ -1,85 +0,0 @@
-/* Copyright (C) 2000-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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
-
-
-#ifdef USE_PRAGMA_INTERFACE
-#pragma interface /* gcc class implementation */
-#endif
-
-/* class for the the myisam merge handler */
-
-#include <myisammrg.h>
-
-class ha_myisammrg: public handler
-{
- MYRG_INFO *file;
-
- public:
- ha_myisammrg(TABLE *table_arg);
- ~ha_myisammrg() {}
- const char *table_type() const { return "MRG_MyISAM"; }
- const char **bas_ext() const;
- const char *index_type(uint key_number);
- ulong table_flags() const
- {
- return (HA_REC_NOT_IN_SEQ | HA_AUTO_PART_KEY | HA_READ_RND_SAME |
- HA_NULL_IN_KEY | HA_CAN_INDEX_BLOBS | HA_FILE_BASED |
- HA_ANY_INDEX_MAY_BE_UNIQUE | HA_CAN_BIT_FIELD);
- }
- ulong index_flags(uint inx, uint part, bool all_parts) const
- {
- return ((table->key_info[inx].algorithm == HA_KEY_ALG_FULLTEXT) ?
- 0 : HA_READ_NEXT | HA_READ_PREV | HA_READ_RANGE |
- HA_READ_ORDER | HA_KEYREAD_ONLY);
- }
- uint max_supported_keys() const { return MI_MAX_KEY; }
- uint max_supported_key_length() const { return MI_MAX_KEY_LENGTH; }
- uint max_supported_key_part_length() const { return MI_MAX_KEY_LENGTH; }
- double scan_time()
- { return ulonglong2double(data_file_length) / IO_SIZE + file->tables; }
-
- int open(const char *name, int mode, uint test_if_locked);
- int close(void);
- int write_row(byte * buf);
- int update_row(const byte * old_data, byte * new_data);
- int delete_row(const byte * buf);
- int index_read(byte * buf, const byte * key,
- uint key_len, enum ha_rkey_function find_flag);
- int index_read_idx(byte * buf, uint idx, const byte * key,
- uint key_len, enum ha_rkey_function find_flag);
- int index_read_last(byte * buf, const byte * key, uint key_len);
- int index_next(byte * buf);
- int index_prev(byte * buf);
- int index_first(byte * buf);
- int index_last(byte * buf);
- int index_next_same(byte *buf, const byte *key, uint keylen);
- int rnd_init(bool scan);
- int rnd_next(byte *buf);
- int rnd_pos(byte * buf, byte *pos);
- void position(const byte *record);
- ha_rows records_in_range(uint inx, key_range *min_key, key_range *max_key);
- int info(uint);
- int extra(enum ha_extra_function operation);
- int extra_opt(enum ha_extra_function operation, ulong cache_size);
- int external_lock(THD *thd, int lock_type);
- uint lock_count(void) const;
- int create(const char *name, TABLE *form, HA_CREATE_INFO *create_info);
- THR_LOCK_DATA **store_lock(THD *thd, THR_LOCK_DATA **to,
- enum thr_lock_type lock_type);
- void update_create_info(HA_CREATE_INFO *create_info);
- void append_create_info(String *packet);
- MYRG_INFO *myrg_info() { return file; }
- int check(THD* thd, HA_CHECK_OPT* check_opt);
-};
diff --git a/sql/ha_ndbcluster.cc b/sql/ha_ndbcluster.cc
index 0a75b328ca0..3454f3558e8 100644
--- a/sql/ha_ndbcluster.cc
+++ b/sql/ha_ndbcluster.cc
@@ -14,9 +14,12 @@
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
-/*
- This file defines the NDB Cluster handler: the interface between MySQL and
- NDB Cluster
+/**
+ @file
+
+ @brief
+ This file defines the NDB Cluster handler: the interface between
+ MySQL and NDB Cluster
*/
#ifdef USE_PRAGMA_IMPLEMENTATION
@@ -24,58 +27,96 @@
#endif
#include "mysql_priv.h"
+#include "rpl_mi.h"
-#ifdef HAVE_NDBCLUSTER_DB
#include <my_dir.h>
+#ifdef WITH_NDBCLUSTER_STORAGE_ENGINE
#include "ha_ndbcluster.h"
#include <ndbapi/NdbApi.hpp>
#include "ha_ndbcluster_cond.h"
+#include <../util/Bitmask.hpp>
+#include <ndbapi/NdbIndexStat.hpp>
+
+#include "ha_ndbcluster_binlog.h"
+#include "ha_ndbcluster_tables.h"
+
+#include <mysql/plugin.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
// 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;
+
// Default value for parallelism
static const int parallelism= 0;
// Default value for max number of transactions
// createable against NDB from this handler
-static const int max_transactions= 2;
-
-static const char *ha_ndb_ext=".ndb";
-
-static int ndbcluster_close_connection(THD *thd);
-static int ndbcluster_commit(THD *thd, bool all);
-static int ndbcluster_rollback(THD *thd, bool all);
-
-handlerton ndbcluster_hton = {
- "ndbcluster",
- SHOW_OPTION_YES,
- "Clustered, fault-tolerant, memory-based tables",
- DB_TYPE_NDBCLUSTER,
- ndbcluster_init,
- 0, /* slot */
- 0, /* savepoint size */
- ndbcluster_close_connection,
- NULL, /* savepoint_set */
- NULL, /* savepoint_rollback */
- NULL, /* savepoint_release */
- ndbcluster_commit,
- ndbcluster_rollback,
- NULL, /* prepare */
- NULL, /* recover */
- NULL, /* commit_by_xid */
- NULL, /* rollback_by_xid */
- NULL, /* create_cursor_read_view */
- NULL, /* set_cursor_read_view */
- NULL, /* close_cursor_read_view */
- HTON_CAN_RECREATE
-};
+static const int max_transactions= 3; // should really be 2 but there is a transaction to much allocated when loch table is used
-#define NDB_AUTO_INCREMENT_RETRIES 10
+static uint ndbcluster_partition_flags();
+static uint ndbcluster_alter_table_flags(uint flags);
+static int ndbcluster_init(void *);
+static int ndbcluster_end(handlerton *hton, ha_panic_function flag);
+static bool ndbcluster_show_status(handlerton *hton, THD*,
+ stat_print_fn *,
+ enum ha_stat_type);
+static int ndbcluster_alter_tablespace(handlerton *hton,
+ THD* thd,
+ st_alter_tablespace *info);
+static int ndbcluster_fill_files_table(handlerton *hton,
+ THD *thd,
+ TABLE_LIST *tables,
+ COND *cond);
+
+handlerton *ndbcluster_hton;
-#define NDB_INVALID_SCHEMA_OBJECT 241
+static handler *ndbcluster_create_handler(handlerton *hton,
+ TABLE_SHARE *table,
+ MEM_ROOT *mem_root)
+{
+ return new (mem_root) ha_ndbcluster(hton, table);
+}
+
+static uint ndbcluster_partition_flags()
+{
+ return (HA_CAN_PARTITION | HA_CAN_UPDATE_PARTITION_KEY |
+ HA_CAN_PARTITION_UNIQUE | HA_USE_AUTO_PARTITION);
+}
+
+static uint ndbcluster_alter_table_flags(uint flags)
+{
+ if (flags & ALTER_DROP_PARTITION)
+ return 0;
+ else
+ return (HA_ONLINE_ADD_INDEX | HA_ONLINE_DROP_INDEX |
+ HA_ONLINE_ADD_UNIQUE_INDEX | HA_ONLINE_DROP_UNIQUE_INDEX |
+ HA_PARTITION_FUNCTION_SUPPORTED);
+
+}
+
+#define NDB_AUTO_INCREMENT_RETRIES 10
#define ERR_PRINT(err) \
DBUG_PRINT("error", ("%d message: %s", err.code, err.message))
@@ -83,54 +124,57 @@ handlerton ndbcluster_hton = {
#define ERR_RETURN(err) \
{ \
const NdbError& tmp= err; \
- ERR_PRINT(tmp); \
+ set_ndb_err(current_thd, tmp); \
DBUG_RETURN(ndb_to_mysql_error(&tmp)); \
}
-// Typedefs for long names
-typedef NdbDictionary::Column NDBCOL;
-typedef NdbDictionary::Table NDBTAB;
-typedef NdbDictionary::Index NDBINDEX;
-typedef NdbDictionary::Dictionary NDBDICT;
+#define ERR_BREAK(err, code) \
+{ \
+ const NdbError& tmp= err; \
+ set_ndb_err(current_thd, tmp); \
+ code= ndb_to_mysql_error(&tmp); \
+ break; \
+}
-bool ndbcluster_inited= FALSE;
+static int ndbcluster_inited= 0;
+int ndbcluster_terminating= 0;
static Ndb* g_ndb= NULL;
-static Ndb_cluster_connection* g_ndb_cluster_connection= NULL;
+Ndb_cluster_connection* g_ndb_cluster_connection= NULL;
+uchar g_node_id_map[max_ndb_nodes];
-// Handler synchronization
+/// Handler synchronization
pthread_mutex_t ndbcluster_mutex;
-// Table lock handling
-static HASH ndbcluster_open_tables;
+/// Table lock handling
+HASH ndbcluster_open_tables;
-static byte *ndbcluster_get_key(NDB_SHARE *share,uint *length,
+static uchar *ndbcluster_get_key(NDB_SHARE *share, size_t *length,
my_bool not_used __attribute__((unused)));
-static NDB_SHARE *get_share(const char *table_name);
-static void free_share(NDB_SHARE *share);
-
-static int packfrm(const void *data, uint len, const void **pack_data, uint *pack_len);
-static int unpackfrm(const void **data, uint *len,
- const void* pack_data);
-
-static int ndb_get_table_statistics(ha_ndbcluster*, bool, Ndb*, const char *,
+#ifdef HAVE_NDB_BINLOG
+static int rename_share(NDB_SHARE *share, const char *new_key);
+#endif
+static int ndb_get_table_statistics(ha_ndbcluster*, bool, Ndb*, const NDBTAB *,
struct Ndb_statistics *);
+
// Util thread variables
-static pthread_t ndb_util_thread;
+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;
pthread_handler_t ndb_util_thread_func(void *arg);
ulong ndb_cache_check_time;
-/*
+/**
Dummy buffer to read zero pack_length fields
- which are mapped to 1 char
+ which are mapped to 1 char.
*/
static uint32 dummy_buf;
-/*
- Stats that can be retrieved from ndb
+/**
+ Stats that can be retrieved from ndb.
*/
struct Ndb_statistics {
@@ -146,7 +190,9 @@ static long ndb_cluster_node_id= 0;
static const char * ndb_connected_host= 0;
static long ndb_connected_port= 0;
static long ndb_number_of_replicas= 0;
-static long ndb_number_of_data_nodes= 0;
+long ndb_number_of_data_nodes= 0;
+long ndb_number_of_ready_data_nodes= 0;
+long ndb_connect_count= 0;
static int update_status_variables(Ndb_cluster_connection *c)
{
@@ -154,11 +200,13 @@ static int update_status_variables(Ndb_cluster_connection *c)
ndb_connected_port= c->get_connected_port();
ndb_connected_host= c->get_connected_host();
ndb_number_of_replicas= 0;
- ndb_number_of_data_nodes= c->no_db_nodes();
+ ndb_number_of_ready_data_nodes= c->get_no_ready();
+ ndb_number_of_data_nodes= c->no_db_nodes();
+ ndb_connect_count= c->get_connect_count();
return 0;
}
-struct show_var_st ndb_status_variables[]= {
+SHOW_VAR ndb_status_variables[]= {
{"cluster_node_id", (char*) &ndb_cluster_node_id, SHOW_LONG},
{"config_from_host", (char*) &ndb_connected_host, SHOW_CHAR_PTR},
{"config_from_port", (char*) &ndb_connected_port, SHOW_LONG},
@@ -171,80 +219,79 @@ struct show_var_st ndb_status_variables[]= {
Error handling functions
*/
-struct err_code_mapping
-{
- int ndb_err;
- int my_err;
- int show_warning;
-};
+/* Note for merge: old mapping table, moved to storage/ndb/ndberror.c */
-static const err_code_mapping err_map[]=
+static int ndb_to_mysql_error(const NdbError *ndberr)
{
- { 626, HA_ERR_KEY_NOT_FOUND, 0 },
- { 630, HA_ERR_FOUND_DUPP_KEY, 1 },
- { 893, HA_ERR_FOUND_DUPP_KEY, 1 },
- { 721, HA_ERR_TABLE_EXIST, 1 },
- { 4244, HA_ERR_TABLE_EXIST, 1 },
-
- { 709, HA_ERR_NO_SUCH_TABLE, 0 },
-
- { 266, HA_ERR_LOCK_WAIT_TIMEOUT, 1 },
- { 274, HA_ERR_LOCK_WAIT_TIMEOUT, 1 },
- { 296, HA_ERR_LOCK_WAIT_TIMEOUT, 1 },
- { 297, HA_ERR_LOCK_WAIT_TIMEOUT, 1 },
- { 237, HA_ERR_LOCK_WAIT_TIMEOUT, 1 },
-
- { 623, HA_ERR_RECORD_FILE_FULL, 1 },
- { 624, HA_ERR_RECORD_FILE_FULL, 1 },
- { 625, HA_ERR_RECORD_FILE_FULL, 1 },
- { 826, HA_ERR_RECORD_FILE_FULL, 1 },
- { 827, HA_ERR_RECORD_FILE_FULL, 1 },
- { 832, HA_ERR_RECORD_FILE_FULL, 1 },
+ /* read the mysql mapped error code */
+ int error= ndberr->mysql_code;
- { 284, HA_ERR_TABLE_DEF_CHANGED, 0 },
-
- {4000, HA_ERR_OUT_OF_MEM, 1 },
- {4009, HA_ERR_NO_CONNECTION, 1 },
-
- { 0, 1, 0 },
-
- { -1, -1, 1 }
-};
+ switch (error)
+ {
+ /* errors for which we do not add warnings, just return mapped error code
+ */
+ case HA_ERR_NO_SUCH_TABLE:
+ case HA_ERR_KEY_NOT_FOUND:
+ return error;
+ /* Mapping missing, go with the ndb error code*/
+ case -1:
+ error= ndberr->code;
+ break;
+ /* Mapping exists, go with the mapped code */
+ default:
+ break;
+ }
-static int ndb_to_mysql_error(const NdbError *err)
-{
- uint i;
- for (i=0; err_map[i].ndb_err != err->code && err_map[i].my_err != -1; i++);
- if (err_map[i].show_warning)
- {
- // Push the NDB error message as warning
+ /*
+ Push the NDB error message as warning
+ - Used to be able to use SHOW WARNINGS toget more info on what the error is
+ - 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,
- ER_GET_ERRMSG, ER(ER_GET_ERRMSG),
- err->code, err->message, "NDB");
- }
- if (err_map[i].my_err == -1)
- return err->code;
- return err_map[i].my_err;
+ 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,
+ ER_GET_ERRMSG, ER(ER_GET_ERRMSG),
+ ndberr->code, ndberr->message, "NDB");
+ return error;
}
+int execute_no_commit_ignore_no_key(ha_ndbcluster *h, NdbTransaction *trans)
+{
+ if (trans->execute(NdbTransaction::NoCommit,
+ NdbOperation::AO_IgnoreError,
+ h->m_force_send) == -1)
+ return -1;
+
+ const NdbError &err= trans->getNdbError();
+ if (err.classification != NdbError::NoError &&
+ err.classification != NdbError::ConstraintViolation &&
+ err.classification != NdbError::NoDataFound)
+ return -1;
+ return 0;
+}
inline
int execute_no_commit(ha_ndbcluster *h, NdbTransaction *trans,
bool force_release)
{
h->release_completed_operations(trans, force_release);
- return trans->execute(NdbTransaction::NoCommit,
- NdbTransaction::AbortOnError,
- h->m_force_send);
+ return h->m_ignore_no_key ?
+ execute_no_commit_ignore_no_key(h,trans) :
+ trans->execute(NdbTransaction::NoCommit,
+ NdbOperation::AbortOnError,
+ h->m_force_send);
}
inline
int execute_commit(ha_ndbcluster *h, NdbTransaction *trans)
{
return trans->execute(NdbTransaction::Commit,
- NdbTransaction::AbortOnError,
+ NdbOperation::AbortOnError,
h->m_force_send);
}
@@ -252,7 +299,7 @@ inline
int execute_commit(THD *thd, NdbTransaction *trans)
{
return trans->execute(NdbTransaction::Commit,
- NdbTransaction::AbortOnError,
+ NdbOperation::AbortOnError,
thd->variables.ndb_force_send);
}
@@ -262,22 +309,38 @@ int execute_no_commit_ie(ha_ndbcluster *h, NdbTransaction *trans,
{
h->release_completed_operations(trans, force_release);
return trans->execute(NdbTransaction::NoCommit,
- NdbTransaction::AO_IgnoreError,
+ NdbOperation::AO_IgnoreError,
h->m_force_send);
}
/*
Place holder for ha_ndbcluster thread specific data
*/
+typedef struct st_thd_ndb_share {
+ const void *key;
+ struct Ndb_local_table_statistics stat;
+} THD_NDB_SHARE;
+static
+uchar *thd_ndb_share_get_key(THD_NDB_SHARE *thd_ndb_share, size_t *length,
+ my_bool not_used __attribute__((unused)))
+{
+ *length= sizeof(thd_ndb_share->key);
+ return (uchar*) &thd_ndb_share->key;
+}
+
Thd_ndb::Thd_ndb()
{
ndb= new Ndb(g_ndb_cluster_connection, "");
lock_count= 0;
+ start_stmt_count= 0;
count= 0;
- all= NULL;
- stmt= NULL;
- error= 0;
+ trans= NULL;
+ m_error= FALSE;
+ 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);
}
Thd_ndb::~Thd_ndb()
@@ -301,15 +364,17 @@ Thd_ndb::~Thd_ndb()
ndb= NULL;
}
changed_tables.empty();
+ hash_free(&open_tables);
}
-inline
-Thd_ndb *
-get_thd_ndb(THD *thd) { return (Thd_ndb *) thd->ha_data[ndbcluster_hton.slot]; }
-
-inline
void
-set_thd_ndb(THD *thd, Thd_ndb *thd_ndb) { thd->ha_data[ndbcluster_hton.slot]= thd_ndb; }
+Thd_ndb::init_open_tables()
+{
+ count= 0;
+ m_error= FALSE;
+ m_error_code= 0;
+ my_hash_reset(&open_tables);
+}
inline
Ndb *ha_ndbcluster::get_ndb()
@@ -321,22 +386,44 @@ Ndb *ha_ndbcluster::get_ndb()
* manage uncommitted insert/deletes during transactio to get records correct
*/
-struct Ndb_local_table_statistics {
- int no_uncommitted_rows_count;
- ulong last_count;
- ha_rows records;
-};
-
void ha_ndbcluster::set_rec_per_key()
{
DBUG_ENTER("ha_ndbcluster::get_status_const");
- for (uint i=0 ; i < table->s->keys ; i++)
+ for (uint i=0 ; i < table_share->keys ; i++)
{
table->key_info[i].rec_per_key[table->key_info[i].key_parts-1]= 1;
}
DBUG_VOID_RETURN;
}
+ha_rows ha_ndbcluster::records()
+{
+ ha_rows retval;
+ DBUG_ENTER("ha_ndbcluster::records");
+ struct Ndb_local_table_statistics *local_info= m_table_info;
+ DBUG_PRINT("info", ("id=%d, no_uncommitted_rows_count=%d",
+ ((const NDBTAB *)m_table)->getTableId(),
+ local_info->no_uncommitted_rows_count));
+
+ Ndb *ndb= get_ndb();
+ ndb->setDatabaseName(m_dbname);
+ struct Ndb_statistics stat;
+ if (ndb_get_table_statistics(this, TRUE, ndb, m_table, &stat) == 0)
+ {
+ retval= stat.row_count;
+ }
+ else
+ {
+ DBUG_RETURN(HA_POS_ERROR);
+ }
+
+ THD *thd= current_thd;
+ if (get_thd_ndb(thd)->m_error)
+ local_info->no_uncommitted_rows_count= 0;
+
+ DBUG_RETURN(retval + local_info->no_uncommitted_rows_count);
+}
+
int ha_ndbcluster::records_update()
{
if (m_ha_not_exact_count)
@@ -344,12 +431,10 @@ int ha_ndbcluster::records_update()
DBUG_ENTER("ha_ndbcluster::records_update");
int result= 0;
- struct Ndb_local_table_statistics *local_info=
- (struct Ndb_local_table_statistics *)m_table_info;
+ struct Ndb_local_table_statistics *local_info= m_table_info;
DBUG_PRINT("info", ("id=%d, no_uncommitted_rows_count=%d",
((const NDBTAB *)m_table)->getTableId(),
local_info->no_uncommitted_rows_count));
- // if (info->records == ~(ha_rows)0)
{
Ndb *ndb= get_ndb();
struct Ndb_statistics stat;
@@ -357,21 +442,21 @@ int ha_ndbcluster::records_update()
{
return my_errno= HA_ERR_OUT_OF_MEM;
}
- result= ndb_get_table_statistics(this, true, ndb, m_tabname, &stat);
+ result= ndb_get_table_statistics(this, TRUE, ndb, m_table, &stat);
if (result == 0)
{
- mean_rec_length= stat.row_size;
- data_file_length= stat.fragment_memory;
+ stats.mean_rec_length= stat.row_size;
+ stats.data_file_length= stat.fragment_memory;
local_info->records= stat.row_count;
}
}
{
THD *thd= current_thd;
- if (get_thd_ndb(thd)->error)
+ if (get_thd_ndb(thd)->m_error)
local_info->no_uncommitted_rows_count= 0;
}
- if(result==0)
- records= local_info->records+ local_info->no_uncommitted_rows_count;
+ if (result == 0)
+ stats.records= local_info->records+ local_info->no_uncommitted_rows_count;
DBUG_RETURN(result);
}
@@ -380,27 +465,8 @@ void ha_ndbcluster::no_uncommitted_rows_execute_failure()
if (m_ha_not_exact_count)
return;
DBUG_ENTER("ha_ndbcluster::no_uncommitted_rows_execute_failure");
- get_thd_ndb(current_thd)->error= 1;
- DBUG_VOID_RETURN;
-}
-
-void ha_ndbcluster::no_uncommitted_rows_init(THD *thd)
-{
- if (m_ha_not_exact_count)
- return;
- DBUG_ENTER("ha_ndbcluster::no_uncommitted_rows_init");
- struct Ndb_local_table_statistics *local_info=
- (struct Ndb_local_table_statistics *)m_table_info;
- Thd_ndb *thd_ndb= get_thd_ndb(thd);
- if (local_info->last_count != thd_ndb->count)
- {
- local_info->last_count= thd_ndb->count;
- local_info->no_uncommitted_rows_count= 0;
- local_info->records= ~(ha_rows)0;
- DBUG_PRINT("info", ("id=%d, no_uncommitted_rows_count=%d",
- ((const NDBTAB *)m_table)->getTableId(),
- local_info->no_uncommitted_rows_count));
- }
+ get_thd_ndb(current_thd)->m_error= TRUE;
+ get_thd_ndb(current_thd)->m_error_code= 0;
DBUG_VOID_RETURN;
}
@@ -409,8 +475,7 @@ void ha_ndbcluster::no_uncommitted_rows_update(int c)
if (m_ha_not_exact_count)
return;
DBUG_ENTER("ha_ndbcluster::no_uncommitted_rows_update");
- struct Ndb_local_table_statistics *local_info=
- (struct Ndb_local_table_statistics *)m_table_info;
+ struct Ndb_local_table_statistics *local_info= m_table_info;
local_info->no_uncommitted_rows_count+= c;
DBUG_PRINT("info", ("id=%d, no_uncommitted_rows_count=%d",
((const NDBTAB *)m_table)->getTableId(),
@@ -425,119 +490,66 @@ void ha_ndbcluster::no_uncommitted_rows_reset(THD *thd)
DBUG_ENTER("ha_ndbcluster::no_uncommitted_rows_reset");
Thd_ndb *thd_ndb= get_thd_ndb(thd);
thd_ndb->count++;
- thd_ndb->error= 0;
+ thd_ndb->m_error= FALSE;
DBUG_VOID_RETURN;
}
/*
- Take care of the error that occured in NDB
-
- RETURN
- 0 No error
- # The mapped error code
+ Sets the latest ndb error code on the thd_ndb object such that it
+ can be retrieved later to know which ndb error caused the handler
+ error.
*/
-
-void ha_ndbcluster::invalidate_dictionary_cache(bool global)
+static void set_ndb_err(THD *thd, const NdbError &err)
{
- Ndb * ndb= get_ndb();
- NDBDICT *dict= ndb->getDictionary();
- DBUG_ENTER("invalidate_dictionary_cache");
- DBUG_PRINT("info", ("invalidating %s", m_tabname));
+ DBUG_ENTER("set_ndb_err");
+ ERR_PRINT(err);
- if (global)
- {
- const NDBTAB *tab= dict->getTable(m_tabname);
- if (!tab)
- DBUG_VOID_RETURN;
- if (tab->getObjectStatus() == NdbDictionary::Object::Invalid)
- {
- // Global cache has already been invalidated
- dict->removeCachedTable(m_tabname);
- global= FALSE;
- }
- else
- dict->invalidateTable(m_tabname);
- }
- else
- dict->removeCachedTable(m_tabname);
- build_index_list(ndb, table, ILBP_OPEN);
- table->s->version=0L; /* Free when thread is ready */
- /* Invalidate indexes */
- for (uint i= 0; i < table->s->keys; i++)
+ Thd_ndb *thd_ndb= get_thd_ndb(thd);
+ if (thd_ndb == NULL)
+ DBUG_VOID_RETURN;
+#ifdef NOT_YET
+ /*
+ Check if error code is overwritten, in this case the original
+ failure cause will be lost. E.g. if 4350 error is given. So
+ push a warning so that it can be detected which is the root
+ error cause.
+ */
+ if (thd_ndb->m_query_id == thd->query_id &&
+ thd_ndb->m_error_code != 0 &&
+ thd_ndb->m_error_code != err.code)
{
- NDBINDEX *index = (NDBINDEX *) m_index[i].index;
- NDBINDEX *unique_index = (NDBINDEX *) m_index[i].unique_index;
- NDB_INDEX_TYPE idx_type= m_index[i].type;
-
- switch (idx_type) {
- case PRIMARY_KEY_ORDERED_INDEX:
- case ORDERED_INDEX:
- if (!index)
- break;
- if (global)
- dict->invalidateIndex(index->getName(), m_tabname);
- else
- dict->removeCachedIndex(index->getName(), m_tabname);
- break;
- case UNIQUE_ORDERED_INDEX:
- if (!index)
- break;
- if (global)
- dict->invalidateIndex(index->getName(), m_tabname);
- else
- dict->removeCachedIndex(index->getName(), m_tabname);
- case UNIQUE_INDEX:
- if (!unique_index)
- break;
- if (global)
- dict->invalidateIndex(unique_index->getName(), m_tabname);
- else
- dict->removeCachedIndex(unique_index->getName(), m_tabname);
- break;
- case PRIMARY_KEY_INDEX:
- case UNDEFINED_INDEX:
- break;
- }
+ char buf[FN_REFLEN];
+ ndb_error_string(thd_ndb->m_error_code, buf, sizeof(buf));
+ push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_ERROR,
+ ER_GET_ERRMSG, ER(ER_GET_ERRMSG),
+ thd_ndb->m_error_code, buf, "NDB");
}
+#endif
+ thd_ndb->m_query_id= thd->query_id;
+ thd_ndb->m_error_code= err.code;
DBUG_VOID_RETURN;
}
int ha_ndbcluster::ndb_err(NdbTransaction *trans)
{
+ THD *thd= current_thd;
int res;
NdbError err= trans->getNdbError();
DBUG_ENTER("ndb_err");
- ERR_PRINT(err);
+ set_ndb_err(thd, err);
+
switch (err.classification) {
case NdbError::SchemaError:
{
+ // TODO perhaps we need to do more here, invalidate also in the cache
+ m_table->setStatusInvalid();
/* Close other open handlers not used by any thread */
TABLE_LIST table_list;
bzero((char*) &table_list,sizeof(table_list));
table_list.db= m_dbname;
table_list.alias= table_list.table_name= m_tabname;
- close_cached_tables(current_thd, 0, &table_list);
-
- invalidate_dictionary_cache(TRUE);
-
- if (err.code==284)
- {
- /*
- Check if the table is _really_ gone or if the table has
- been alterend and thus changed table id
- */
- NDBDICT *dict= get_ndb()->getDictionary();
- DBUG_PRINT("info", ("Check if table %s is really gone", m_tabname));
- if (!(dict->getTable(m_tabname)))
- {
- err= dict->getNdbError();
- DBUG_PRINT("info", ("Table not found, error: %d", err.code));
- if (err.code != 709)
- DBUG_RETURN(1);
- }
- DBUG_PRINT("info", ("Table exists but must have changed"));
- }
+ close_cached_tables(thd, &table_list, FALSE, FALSE, FALSE);
break;
}
default:
@@ -559,8 +571,7 @@ int ha_ndbcluster::ndb_err(NdbTransaction *trans)
const NDBINDEX *unique_index=
(const NDBINDEX *) m_index[i].unique_index;
if (unique_index &&
- unique_index->getIndexTable() &&
- (char *) unique_index->getIndexTable()->getTableId() == error_data)
+ (char *) unique_index->getObjectId() == error_data)
{
dupkey= i;
break;
@@ -574,7 +585,7 @@ int ha_ndbcluster::ndb_err(NdbTransaction *trans)
violations here, so we need to return MAX_KEY for non-primary
to signal that key is unknown
*/
- m_dupkey= err.code == 630 ? table->s->primary_key : dupkey;
+ m_dupkey= err.code == 630 ? table_share->primary_key : dupkey;
}
else
{
@@ -586,10 +597,10 @@ int ha_ndbcluster::ndb_err(NdbTransaction *trans)
}
-/*
+/**
Override the default get_error_message in order to add the
- error message of NDB
- */
+ error message of NDB .
+*/
bool ha_ndbcluster::get_error_message(int error,
String *buf)
@@ -597,7 +608,7 @@ bool ha_ndbcluster::get_error_message(int error,
DBUG_ENTER("ha_ndbcluster::get_error_message");
DBUG_PRINT("enter", ("error: %d", error));
- Ndb *ndb= get_ndb();
+ Ndb *ndb= check_ndb_in_thd(current_thd);
if (!ndb)
DBUG_RETURN(FALSE);
@@ -610,7 +621,7 @@ bool ha_ndbcluster::get_error_message(int error,
#ifndef DBUG_OFF
-/*
+/**
Check if type is supported by NDB.
*/
@@ -652,32 +663,51 @@ static bool ndb_supported_type(enum_field_types type)
#endif /* !DBUG_OFF */
-/*
- Instruct NDB to set the value of the hidden primary key
+/**
+ Check if MySQL field type forces var part in ndb storage.
+*/
+static bool field_type_forces_var_part(enum_field_types type)
+{
+ switch (type) {
+ case MYSQL_TYPE_VAR_STRING:
+ case MYSQL_TYPE_VARCHAR:
+ return TRUE;
+ case MYSQL_TYPE_TINY_BLOB:
+ case MYSQL_TYPE_BLOB:
+ case MYSQL_TYPE_MEDIUM_BLOB:
+ case MYSQL_TYPE_LONG_BLOB:
+ case MYSQL_TYPE_GEOMETRY:
+ return FALSE;
+ default:
+ return FALSE;
+ }
+}
+
+/**
+ Instruct NDB to set the value of the hidden primary key.
*/
bool ha_ndbcluster::set_hidden_key(NdbOperation *ndb_op,
- uint fieldnr, const byte *field_ptr)
+ uint fieldnr, const uchar *field_ptr)
{
DBUG_ENTER("set_hidden_key");
- DBUG_RETURN(ndb_op->equal(fieldnr, (char*)field_ptr,
- NDB_HIDDEN_PRIMARY_KEY_LENGTH) != 0);
+ DBUG_RETURN(ndb_op->equal(fieldnr, (char*)field_ptr) != 0);
}
-/*
- Instruct NDB to set the value of one primary key attribute
+/**
+ Instruct NDB to set the value of one primary key attribute.
*/
int ha_ndbcluster::set_ndb_key(NdbOperation *ndb_op, Field *field,
- uint fieldnr, const byte *field_ptr)
+ uint fieldnr, const uchar *field_ptr)
{
uint32 pack_len= field->pack_length();
DBUG_ENTER("set_ndb_key");
DBUG_PRINT("enter", ("%d: %s, ndb_type: %u, len=%d",
fieldnr, field->field_name, field->type(),
pack_len));
- DBUG_DUMP("key", (uchar*)field_ptr, pack_len);
+ DBUG_DUMP("key", field_ptr, pack_len);
DBUG_ASSERT(ndb_supported_type(field->type()));
DBUG_ASSERT(! (field->flags & BLOB_FLAG));
@@ -686,20 +716,21 @@ int ha_ndbcluster::set_ndb_key(NdbOperation *ndb_op, Field *field,
}
-/*
- Instruct NDB to set the value of one attribute
+/**
+ Instruct NDB to set the value of one attribute.
*/
int ha_ndbcluster::set_ndb_value(NdbOperation *ndb_op, Field *field,
- uint fieldnr, bool *set_blob_value)
+ uint fieldnr, int row_offset,
+ bool *set_blob_value)
{
- const byte* field_ptr= field->ptr;
- uint32 pack_len= field->pack_length();
+ const uchar* field_ptr= field->ptr + row_offset;
+ uint32 pack_len= field->pack_length();
DBUG_ENTER("set_ndb_value");
- DBUG_PRINT("enter", ("%d: %s, type: %u, len=%d, is_null=%s",
+ DBUG_PRINT("enter", ("%d: %s type: %u len=%d is_null=%s",
fieldnr, field->field_name, field->type(),
- pack_len, field->is_null()?"Y":"N"));
- DBUG_DUMP("value", (uchar*) field_ptr, pack_len);
+ pack_len, field->is_null(row_offset) ? "Y" : "N"));
+ DBUG_DUMP("value", field_ptr, pack_len);
DBUG_ASSERT(ndb_supported_type(field->type()));
{
@@ -708,8 +739,8 @@ int ha_ndbcluster::set_ndb_value(NdbOperation *ndb_op, Field *field,
if (pack_len == 0)
{
pack_len= sizeof(empty_field);
- field_ptr= (byte *)&empty_field;
- if (field->is_null())
+ field_ptr= (uchar *)&empty_field;
+ if (field->is_null(row_offset))
empty_field= 0;
else
empty_field= 1;
@@ -718,13 +749,14 @@ int ha_ndbcluster::set_ndb_value(NdbOperation *ndb_op, Field *field,
{
if (field->type() != MYSQL_TYPE_BIT)
{
- if (field->is_null())
+ if (field->is_null(row_offset))
+ {
+ DBUG_PRINT("info", ("field is NULL"));
// Set value to NULL
- DBUG_RETURN((ndb_op->setValue(fieldnr,
- (char*)NULL, pack_len) != 0));
+ DBUG_RETURN((ndb_op->setValue(fieldnr, (char*)NULL) != 0));
+ }
// Common implementation for most field types
- DBUG_RETURN(ndb_op->setValue(fieldnr,
- (char*)field_ptr, pack_len) != 0);
+ DBUG_RETURN(ndb_op->setValue(fieldnr, (char*)field_ptr) != 0);
}
else // if (field->type() == MYSQL_TYPE_BIT)
{
@@ -733,9 +765,9 @@ int ha_ndbcluster::set_ndb_value(NdbOperation *ndb_op, Field *field,
// Round up bit field length to nearest word boundry
pack_len= ((pack_len + 3) >> 2) << 2;
DBUG_ASSERT(pack_len <= 8);
- if (field->is_null())
+ if (field->is_null(row_offset))
// Set value to NULL
- DBUG_RETURN((ndb_op->setValue(fieldnr, (char*)NULL, pack_len) != 0));
+ DBUG_RETURN((ndb_op->setValue(fieldnr, (char*)NULL) != 0));
DBUG_PRINT("info", ("bit field"));
DBUG_DUMP("value", (uchar*)&bits, pack_len);
#ifdef WORDS_BIGENDIAN
@@ -743,32 +775,32 @@ int ha_ndbcluster::set_ndb_value(NdbOperation *ndb_op, Field *field,
bits = ((bits >> 32) & 0x00000000FFFFFFFFLL)
| ((bits << 32) & 0xFFFFFFFF00000000LL);
#endif
- DBUG_RETURN(ndb_op->setValue(fieldnr, (char*)&bits, pack_len) != 0);
+ DBUG_RETURN(ndb_op->setValue(fieldnr, (char*)&bits) != 0);
}
}
// Blob type
NdbBlob *ndb_blob= ndb_op->getBlobHandle(fieldnr);
if (ndb_blob != NULL)
{
- if (field->is_null())
+ if (field->is_null(row_offset))
DBUG_RETURN(ndb_blob->setNull() != 0);
Field_blob *field_blob= (Field_blob*)field;
// Get length and pointer to data
uint32 blob_len= field_blob->get_length(field_ptr);
- char* blob_ptr= NULL;
+ uchar* blob_ptr= NULL;
field_blob->get_ptr(&blob_ptr);
// Looks like NULL ptr signals length 0 blob
if (blob_ptr == NULL) {
DBUG_ASSERT(blob_len == 0);
- blob_ptr= (char*)"";
+ blob_ptr= (uchar*)"";
}
- DBUG_PRINT("value", ("set blob ptr: %p len: %u",
- blob_ptr, blob_len));
- DBUG_DUMP("value", (uchar*)blob_ptr, min(blob_len, 26));
+ DBUG_PRINT("value", ("set blob ptr: 0x%lx len: %u",
+ (long) blob_ptr, blob_len));
+ DBUG_DUMP("value", blob_ptr, min(blob_len, 26));
if (set_blob_value)
*set_blob_value= TRUE;
@@ -780,7 +812,9 @@ int ha_ndbcluster::set_ndb_value(NdbOperation *ndb_op, Field *field,
}
-/*
+NdbBlob::ActiveHook g_get_ndb_blobs_value;
+
+/**
Callback to read all blob values.
- not done in unpack_record because unpack_record is valid
after execute(Commit) but reading blobs is not
@@ -788,11 +822,11 @@ int ha_ndbcluster::set_ndb_value(NdbOperation *ndb_op, Field *field,
somewhere before the data is available
- due to single buffer for all blobs, we let the last blob
process all blobs (last so that all are active)
- - null bit is still set in unpack_record
- - TODO allocate blob part aligned buffers
-*/
+ - null bit is still set in unpack_record.
-NdbBlob::ActiveHook g_get_ndb_blobs_value;
+ @todo
+ allocate blob part aligned buffers
+*/
int g_get_ndb_blobs_value(NdbBlob *ndb_blob, void *arg)
{
@@ -800,11 +834,20 @@ int g_get_ndb_blobs_value(NdbBlob *ndb_blob, void *arg)
if (ndb_blob->blobsNextBlob() != NULL)
DBUG_RETURN(0);
ha_ndbcluster *ha= (ha_ndbcluster *)arg;
- DBUG_RETURN(ha->get_ndb_blobs_value(ndb_blob, ha->m_blobs_offset));
+ int ret= get_ndb_blobs_value(ha->table, ha->m_value,
+ ha->m_blobs_buffer, ha->m_blobs_buffer_size,
+ ha->m_blobs_offset);
+ DBUG_RETURN(ret);
}
-int ha_ndbcluster::get_ndb_blobs_value(NdbBlob *last_ndb_blob,
- my_ptrdiff_t ptrdiff)
+/*
+ This routine is shared by injector. There is no common blobs buffer
+ so the buffer and length are passed by reference. Injector also
+ passes a record pointer diff.
+ */
+int get_ndb_blobs_value(TABLE* table, NdbValue* value_array,
+ uchar*& buffer, uint& buffer_size,
+ my_ptrdiff_t ptrdiff)
{
DBUG_ENTER("get_ndb_blobs_value");
@@ -816,62 +859,78 @@ int ha_ndbcluster::get_ndb_blobs_value(NdbBlob *last_ndb_blob,
for (uint i= 0; i < table->s->fields; i++)
{
Field *field= table->field[i];
- NdbValue value= m_value[i];
- if (value.ptr != NULL && (field->flags & BLOB_FLAG))
+ NdbValue value= value_array[i];
+ if (! (field->flags & BLOB_FLAG))
+ continue;
+ if (value.blob == NULL)
{
- Field_blob *field_blob= (Field_blob *)field;
- NdbBlob *ndb_blob= value.blob;
- Uint64 blob_len= 0;
- if (ndb_blob->getLength(blob_len) != 0)
- DBUG_RETURN(-1);
+ DBUG_PRINT("info",("[%u] skipped", i));
+ continue;
+ }
+ Field_blob *field_blob= (Field_blob *)field;
+ NdbBlob *ndb_blob= value.blob;
+ int isNull;
+ if (ndb_blob->getNull(isNull) != 0)
+ ERR_RETURN(ndb_blob->getNdbError());
+ if (isNull == 0) {
+ Uint64 len64= 0;
+ if (ndb_blob->getLength(len64) != 0)
+ ERR_RETURN(ndb_blob->getNdbError());
// Align to Uint64
- uint32 blob_size= blob_len;
- if (blob_size % 8 != 0)
- blob_size+= 8 - blob_size % 8;
+ uint32 size= len64;
+ if (size % 8 != 0)
+ size+= 8 - size % 8;
if (loop == 1)
{
- char *buf= m_blobs_buffer + offset;
+ uchar *buf= buffer + offset;
uint32 len= 0xffffffff; // Max uint32
- DBUG_PRINT("value", ("read blob ptr: 0x%lx len: %u",
- (long)buf, (uint)blob_len));
if (ndb_blob->readData(buf, len) != 0)
- DBUG_RETURN(-1);
- DBUG_ASSERT(len == blob_len);
+ ERR_RETURN(ndb_blob->getNdbError());
+ DBUG_PRINT("info", ("[%u] offset: %u buf: 0x%lx len=%u [ptrdiff=%d]",
+ i, offset, (long) buf, len, (int)ptrdiff));
+ DBUG_ASSERT(len == len64);
// Ugly hack assumes only ptr needs to be changed
- field_blob->ptr+= ptrdiff;
- field_blob->set_ptr(len, buf);
- field_blob->ptr-= ptrdiff;
+ field_blob->set_ptr_offset(ptrdiff, len, buf);
}
- offset+= blob_size;
+ offset+= size;
+ }
+ else if (loop == 1) // undefined or null
+ {
+ // have to set length even in this case
+ uchar *buf= buffer + offset; // or maybe NULL
+ uint32 len= 0;
+ field_blob->set_ptr_offset(ptrdiff, len, buf);
+ DBUG_PRINT("info", ("[%u] isNull=%d", i, isNull));
}
}
- if (loop == 0 && offset > m_blobs_buffer_size)
+ if (loop == 0 && offset > buffer_size)
{
- my_free(m_blobs_buffer, MYF(MY_ALLOW_ZERO_PTR));
- m_blobs_buffer_size= 0;
- DBUG_PRINT("value", ("allocate blobs buffer size %u", offset));
- m_blobs_buffer= my_malloc(offset, MYF(MY_WME));
- if (m_blobs_buffer == NULL)
+ my_free(buffer, MYF(MY_ALLOW_ZERO_PTR));
+ buffer_size= 0;
+ DBUG_PRINT("info", ("allocate blobs buffer size %u", offset));
+ buffer= (uchar*) my_malloc(offset, MYF(MY_WME));
+ if (buffer == NULL)
{
sql_print_error("ha_ndbcluster::get_ndb_blobs_value: "
"my_malloc(%u) failed", offset);
DBUG_RETURN(-1);
}
- m_blobs_buffer_size= offset;
+ buffer_size= offset;
}
}
DBUG_RETURN(0);
}
-/*
- Instruct NDB to fetch one field
- - data is read directly into buffer provided by field
- if field is NULL, data is read into memory provided by NDBAPI
+/**
+ Instruct NDB to fetch one field.
+
+ Data is read directly into buffer provided by field
+ if field is NULL, data is read into memory provided by NDBAPI.
*/
int ha_ndbcluster::get_ndb_value(NdbOperation *ndb_op, Field *field,
- uint fieldnr, byte* buf)
+ uint fieldnr, uchar* buf)
{
DBUG_ENTER("get_ndb_value");
DBUG_PRINT("enter", ("fieldnr: %d flags: %o", fieldnr,
@@ -886,13 +945,13 @@ int ha_ndbcluster::get_ndb_value(NdbOperation *ndb_op, Field *field,
{
if (field->type() != MYSQL_TYPE_BIT)
{
- byte *field_buf;
+ uchar *field_buf;
if (field->pack_length() != 0)
field_buf= buf + (field->ptr - table->record[0]);
else
- field_buf= (byte *)&dummy_buf;
+ field_buf= (uchar *)&dummy_buf;
m_value[fieldnr].rec= ndb_op->getValue(fieldnr,
- field_buf);
+ (char*) field_buf);
}
else // if (field->type() == MYSQL_TYPE_BIT)
{
@@ -907,7 +966,7 @@ int ha_ndbcluster::get_ndb_value(NdbOperation *ndb_op, Field *field,
if (ndb_blob != NULL)
{
// Set callback
- m_blobs_offset= buf - (byte*) table->record[0];
+ m_blobs_offset= buf - (uchar*) table->record[0];
void *arg= (void *)this;
DBUG_RETURN(ndb_blob->setActiveHook(g_get_ndb_blobs_value, arg) != 0);
}
@@ -915,120 +974,127 @@ int ha_ndbcluster::get_ndb_value(NdbOperation *ndb_op, Field *field,
}
// Used for hidden key only
- m_value[fieldnr].rec= ndb_op->getValue(fieldnr, m_ref);
+ m_value[fieldnr].rec= ndb_op->getValue(fieldnr, (char*) m_ref);
DBUG_RETURN(m_value[fieldnr].rec == NULL);
}
-
/*
+ Instruct NDB to fetch the partition id (fragment id)
+*/
+int ha_ndbcluster::get_ndb_partition_id(NdbOperation *ndb_op)
+{
+ DBUG_ENTER("get_ndb_partition_id");
+ DBUG_RETURN(ndb_op->getValue(NdbDictionary::Column::FRAGMENT,
+ (char *)&m_part_id) == NULL);
+}
+
+/**
Check if any set or get of blob value in current query.
*/
-bool ha_ndbcluster::uses_blob_value(bool all_fields)
+
+bool ha_ndbcluster::uses_blob_value()
{
- if (table->s->blob_fields == 0)
+ MY_BITMAP *bitmap;
+ uint *blob_index, *blob_index_end;
+ if (table_share->blob_fields == 0)
return FALSE;
- if (all_fields)
- return TRUE;
+
+ bitmap= m_write_op ? table->write_set : table->read_set;
+ blob_index= table_share->blob_field;
+ blob_index_end= blob_index + table_share->blob_fields;
+ do
{
- uint no_fields= table->s->fields;
- int i;
- THD *thd= current_thd;
- // They always put blobs at the end..
- for (i= no_fields - 1; i >= 0; i--)
- {
- Field *field= table->field[i];
- if (thd->query_id == field->query_id)
- {
- return TRUE;
- }
- }
- }
+ if (bitmap_is_set(bitmap, table->field[*blob_index]->field_index))
+ return TRUE;
+ } while (++blob_index != blob_index_end);
return FALSE;
}
-/*
- Get metadata for this table from NDB
+/**
+ Get metadata for this table from NDB.
+
+ Check that frm-file on disk is equal to frm-file
+ of table accessed in NDB.
- IMPLEMENTATION
- - check that frm-file on disk is equal to frm-file
- of table accessed in NDB
+ @retval
+ 0 ok
+ @retval
+ -2 Meta data has changed; Re-read data and try again
*/
+int cmp_frm(const NDBTAB *ndbtab, const void *pack_data,
+ uint pack_length)
+{
+ DBUG_ENTER("cmp_frm");
+ /*
+ Compare FrmData in NDB with frm file from disk.
+ */
+ if ((pack_length != ndbtab->getFrmLength()) ||
+ (memcmp(pack_data, ndbtab->getFrmData(), pack_length)))
+ DBUG_RETURN(1);
+ DBUG_RETURN(0);
+}
+
int ha_ndbcluster::get_metadata(const char *path)
{
Ndb *ndb= get_ndb();
NDBDICT *dict= ndb->getDictionary();
const NDBTAB *tab;
int error;
- bool invalidating_ndb_table= FALSE;
-
DBUG_ENTER("get_metadata");
DBUG_PRINT("enter", ("m_tabname: %s, path: %s", m_tabname, path));
- do {
- const void *data= NULL, *pack_data= NULL;
- uint length, pack_length;
+ DBUG_ASSERT(m_table == NULL);
+ DBUG_ASSERT(m_table_info == NULL);
- if (!(tab= dict->getTable(m_tabname)))
- ERR_RETURN(dict->getNdbError());
- // Check if thread has stale local cache
- if (tab->getObjectStatus() == NdbDictionary::Object::Invalid)
- {
- invalidate_dictionary_cache(FALSE);
- if (!(tab= dict->getTable(m_tabname)))
- ERR_RETURN(dict->getNdbError());
- DBUG_PRINT("info", ("Table schema version: %d", tab->getObjectVersion()));
- }
- /*
- Compare FrmData in NDB with frm file from disk.
- */
- error= 0;
- if (readfrm(path, &data, &length) ||
- packfrm(data, length, &pack_data, &pack_length))
- {
- my_free((char*)data, MYF(MY_ALLOW_ZERO_PTR));
- my_free((char*)pack_data, MYF(MY_ALLOW_ZERO_PTR));
- DBUG_RETURN(1);
- }
+ uchar *data= NULL, *pack_data= NULL;
+ size_t length, pack_length;
+
+ /*
+ Compare FrmData in NDB with frm file from disk.
+ */
+ error= 0;
+ 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));
+ DBUG_RETURN(1);
+ }
- if ((pack_length != tab->getFrmLength()) ||
- (memcmp(pack_data, tab->getFrmData(), pack_length)))
- {
- if (!invalidating_ndb_table)
- {
- DBUG_PRINT("info", ("Invalidating table"));
- invalidate_dictionary_cache(TRUE);
- invalidating_ndb_table= TRUE;
- }
- else
- {
- DBUG_PRINT("error",
- ("metadata, pack_length: %d getFrmLength: %d memcmp: %d",
- pack_length, tab->getFrmLength(),
- memcmp(pack_data, tab->getFrmData(), pack_length)));
- DBUG_DUMP("pack_data", (uchar*)pack_data, pack_length);
- DBUG_DUMP("frm", (uchar*)tab->getFrmData(), tab->getFrmLength());
- error= 3;
- invalidating_ndb_table= FALSE;
- }
- }
- else
- {
- invalidating_ndb_table= FALSE;
- }
- my_free((char*)data, MYF(0));
- my_free((char*)pack_data, MYF(0));
- } while (invalidating_ndb_table);
+ Ndb_table_guard ndbtab_g(dict, m_tabname);
+ if (!(tab= ndbtab_g.get_table()))
+ ERR_RETURN(dict->getNdbError());
+
+ if (get_ndb_share_state(m_share) != NSS_ALTERED
+ && cmp_frm(tab, pack_data, pack_length))
+ {
+ DBUG_PRINT("error",
+ ("metadata, pack_length: %lu getFrmLength: %d memcmp: %d",
+ (ulong) pack_length, tab->getFrmLength(),
+ memcmp(pack_data, tab->getFrmData(), pack_length)));
+ DBUG_DUMP("pack_data", (uchar*) pack_data, pack_length);
+ 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));
if (error)
- DBUG_RETURN(error);
-
- m_table_version= tab->getObjectVersion();
- m_table= (void *)tab;
- m_table_info= NULL; // Set in external lock
-
- DBUG_RETURN(build_index_list(ndb, table, ILBP_OPEN));
+ goto err;
+
+ DBUG_PRINT("info", ("fetched table %s", tab->getName()));
+ m_table= tab;
+ if ((error= open_indexes(ndb, table, FALSE)) == 0)
+ {
+ ndbtab_g.release();
+ DBUG_RETURN(0);
+ }
+err:
+ ndbtab_g.invalidate();
+ m_table= NULL;
+ DBUG_RETURN(error);
}
static int fix_unique_index_attr_order(NDB_INDEX_DATA &data,
@@ -1040,7 +1106,7 @@ static int fix_unique_index_attr_order(NDB_INDEX_DATA &data,
if (data.unique_index_attrid_map)
my_free((char*)data.unique_index_attrid_map, MYF(0));
- data.unique_index_attrid_map= (unsigned char*)my_malloc(sz,MYF(MY_WME));
+ data.unique_index_attrid_map= (uchar*)my_malloc(sz,MYF(MY_WME));
if (data.unique_index_attrid_map == 0)
{
sql_print_error("fix_unique_index_attr_order: my_malloc(%u) failure",
@@ -1071,132 +1137,312 @@ static int fix_unique_index_attr_order(NDB_INDEX_DATA &data,
DBUG_RETURN(0);
}
-
-
-int ha_ndbcluster::build_index_list(Ndb *ndb, TABLE *tab, enum ILBP phase)
+/*
+ Create all the indexes for a table.
+ If any index should fail to be created,
+ the error is returned immediately
+*/
+int ha_ndbcluster::create_indexes(Ndb *ndb, TABLE *tab)
{
uint i;
int error= 0;
const char *index_name;
- char unique_index_name[FN_LEN];
- bool null_in_unique_index= false;
- static const char* unique_suffix= "$unique";
KEY* key_info= tab->key_info;
const char **key_name= tab->s->keynames.type_names;
- NDBDICT *dict= ndb->getDictionary();
- DBUG_ENTER("ha_ndbcluster::build_index_list");
-
- m_has_unique_index= FALSE;
- // Save information about all known indexes
+ DBUG_ENTER("ha_ndbcluster::create_indexes");
+
for (i= 0; i < tab->s->keys; i++, key_info++, key_name++)
{
index_name= *key_name;
NDB_INDEX_TYPE idx_type= get_index_type_from_table(i);
- m_index[i].type= idx_type;
- if (idx_type == UNIQUE_ORDERED_INDEX || idx_type == UNIQUE_INDEX)
+ error= create_index(index_name, key_info, idx_type, i);
+ if (error)
{
- m_has_unique_index= TRUE;
- strxnmov(unique_index_name, FN_LEN, index_name, unique_suffix, NullS);
- DBUG_PRINT("info", ("Created unique index name \'%s\' for index %d",
- unique_index_name, i));
+ DBUG_PRINT("error", ("Failed to create index %u", i));
+ break;
}
- // Create secondary indexes if in create phase
- if (phase == ILBP_CREATE)
+ }
+
+ DBUG_RETURN(error);
+}
+
+static void ndb_init_index(NDB_INDEX_DATA &data)
+{
+ data.type= UNDEFINED_INDEX;
+ data.status= UNDEFINED;
+ data.unique_index= NULL;
+ data.index= NULL;
+ data.unique_index_attrid_map= NULL;
+ data.index_stat=NULL;
+ data.index_stat_cache_entries=0;
+ data.index_stat_update_freq=0;
+ data.index_stat_query_count=0;
+}
+
+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));
+ }
+ if (data.index_stat)
+ {
+ delete data.index_stat;
+ }
+ ndb_init_index(data);
+}
+
+/*
+ Associate a direct reference to an index handle
+ with an index (for faster access)
+ */
+int ha_ndbcluster::add_index_handle(THD *thd, NDBDICT *dict, KEY *key_info,
+ const char *index_name, uint index_no)
+{
+ int error= 0;
+ NDB_INDEX_TYPE idx_type= get_index_type_from_table(index_no);
+ m_index[index_no].type= idx_type;
+ DBUG_ENTER("ha_ndbcluster::add_index_handle");
+ DBUG_PRINT("enter", ("table %s", m_tabname));
+
+ if (idx_type != PRIMARY_KEY_INDEX && idx_type != UNIQUE_INDEX)
+ {
+ DBUG_PRINT("info", ("Get handle to index %s", index_name));
+ const NDBINDEX *index;
+ do
{
- DBUG_PRINT("info", ("Creating index %u: %s", i, index_name));
- switch (idx_type){
-
- case PRIMARY_KEY_INDEX:
- // Do nothing, already created
- break;
- case PRIMARY_KEY_ORDERED_INDEX:
- error= create_ordered_index(index_name, key_info);
- break;
- case UNIQUE_ORDERED_INDEX:
- if (!(error= create_ordered_index(index_name, key_info)))
- error= create_unique_index(unique_index_name, key_info);
- break;
- case UNIQUE_INDEX:
- if (check_index_fields_not_null(i))
- {
- push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
- ER_NULL_COLUMN_IN_INDEX,
- "Ndb does not support unique index on NULL valued attributes, index access with NULL value will become full table scan");
- null_in_unique_index= true;
- }
- error= create_unique_index(unique_index_name, key_info);
- break;
- case ORDERED_INDEX:
- if (key_info->algorithm == HA_KEY_ALG_HASH)
- {
- push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_ERROR,
- ER_UNSUPPORTED_EXTENSION,
- ER(ER_UNSUPPORTED_EXTENSION),
- "Ndb does not support non-unique "
- "hash based indexes");
- error= HA_ERR_UNSUPPORTED;
- break;
- }
- error= create_ordered_index(index_name, key_info);
- break;
- default:
- DBUG_ASSERT(FALSE);
+ index= dict->getIndexGlobal(index_name, *m_table);
+ if (!index)
+ ERR_RETURN(dict->getNdbError());
+ DBUG_PRINT("info", ("index: 0x%lx id: %d version: %d.%d status: %d",
+ (long) index,
+ index->getObjectId(),
+ index->getObjectVersion() & 0xFFFFFF,
+ index->getObjectVersion() >> 24,
+ index->getObjectStatus()));
+ DBUG_ASSERT(index->getObjectStatus() ==
+ NdbDictionary::Object::Retrieved);
+ break;
+ } while (1);
+ m_index[index_no].index= index;
+ // ordered index - add stats
+ NDB_INDEX_DATA& d=m_index[index_no];
+ delete d.index_stat;
+ d.index_stat=NULL;
+ if (thd->variables.ndb_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_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",
+ index->getName(),
+ d.index_stat_cache_entries,
+ d.index_stat_update_freq));
+ } else
+ {
+ DBUG_PRINT("info", ("index %s stat=off", index->getName()));
+ }
+ }
+ if (idx_type == UNIQUE_ORDERED_INDEX || idx_type == UNIQUE_INDEX)
+ {
+ char unique_index_name[FN_LEN];
+ static const char* unique_suffix= "$unique";
+ m_has_unique_index= TRUE;
+ strxnmov(unique_index_name, FN_LEN, index_name, unique_suffix, NullS);
+ DBUG_PRINT("info", ("Get handle to unique_index %s", unique_index_name));
+ const NDBINDEX *index;
+ do
+ {
+ index= dict->getIndexGlobal(unique_index_name, *m_table);
+ if (!index)
+ ERR_RETURN(dict->getNdbError());
+ DBUG_PRINT("info", ("index: 0x%lx id: %d version: %d.%d status: %d",
+ (long) index,
+ index->getObjectId(),
+ index->getObjectVersion() & 0xFFFFFF,
+ index->getObjectVersion() >> 24,
+ index->getObjectStatus()));
+ DBUG_ASSERT(index->getObjectStatus() ==
+ NdbDictionary::Object::Retrieved);
+ break;
+ } while (1);
+ m_index[index_no].unique_index= index;
+ error= fix_unique_index_attr_order(m_index[index_no], index, key_info);
+ }
+ if (!error)
+ m_index[index_no].status= ACTIVE;
+
+ DBUG_RETURN(error);
+}
+
+/*
+ Associate index handles for each index of a table
+*/
+int ha_ndbcluster::open_indexes(Ndb *ndb, TABLE *tab, bool ignore_error)
+{
+ uint i;
+ int error= 0;
+ THD *thd=current_thd;
+ NDBDICT *dict= ndb->getDictionary();
+ KEY* key_info= tab->key_info;
+ const char **key_name= tab->s->keynames.type_names;
+ DBUG_ENTER("ha_ndbcluster::open_indexes");
+ m_has_unique_index= FALSE;
+ for (i= 0; i < tab->s->keys; i++, key_info++, key_name++)
+ {
+ if ((error= add_index_handle(thd, dict, key_info, *key_name, i)))
+ if (ignore_error)
+ m_index[i].index= m_index[i].unique_index= NULL;
+ else
break;
+ m_index[i].null_in_unique_index= FALSE;
+ if (check_index_fields_not_null(key_info))
+ m_index[i].null_in_unique_index= TRUE;
+ }
+
+ if (error && !ignore_error)
+ {
+ while (i > 0)
+ {
+ i--;
+ if (m_index[i].index)
+ {
+ dict->removeIndexGlobal(*m_index[i].index, 1);
+ m_index[i].index= NULL;
}
- if (error)
+ if (m_index[i].unique_index)
{
- DBUG_PRINT("error", ("Failed to create index %u", i));
- drop_table();
- break;
+ dict->removeIndexGlobal(*m_index[i].unique_index, 1);
+ m_index[i].unique_index= NULL;
}
}
- // Add handles to index objects
- if (idx_type != PRIMARY_KEY_INDEX && idx_type != UNIQUE_INDEX)
- {
- DBUG_PRINT("info", ("Get handle to index %s", index_name));
- const NDBINDEX *index= dict->getIndex(index_name, m_tabname);
- if (!index)
- ERR_RETURN(dict->getNdbError());
- m_index[i].index= (void *) index;
+ }
+
+ DBUG_ASSERT(error == 0 || error == 4243);
+
+ DBUG_RETURN(error);
+}
+
+/*
+ Renumber indexes in index list by shifting out
+ indexes that are to be dropped
+ */
+void ha_ndbcluster::renumber_indexes(Ndb *ndb, TABLE *tab)
+{
+ uint i;
+ const char *index_name;
+ KEY* key_info= tab->key_info;
+ const char **key_name= tab->s->keynames.type_names;
+ DBUG_ENTER("ha_ndbcluster::renumber_indexes");
+
+ for (i= 0; i < tab->s->keys; i++, key_info++, key_name++)
+ {
+ index_name= *key_name;
+ NDB_INDEX_TYPE idx_type= get_index_type_from_table(i);
+ m_index[i].type= idx_type;
+ if (m_index[i].status == TO_BE_DROPPED)
+ {
+ DBUG_PRINT("info", ("Shifting index %s(%i) out of the list",
+ index_name, i));
+ NDB_INDEX_DATA tmp;
+ uint j= i + 1;
+ // Shift index out of list
+ while(j != MAX_KEY && m_index[j].status != UNDEFINED)
+ {
+ tmp= m_index[j - 1];
+ m_index[j - 1]= m_index[j];
+ m_index[j]= tmp;
+ j++;
+ }
}
- if (idx_type == UNIQUE_ORDERED_INDEX || idx_type == UNIQUE_INDEX)
+ }
+
+ DBUG_VOID_RETURN;
+}
+
+/*
+ Drop all indexes that are marked for deletion
+*/
+int ha_ndbcluster::drop_indexes(Ndb *ndb, TABLE *tab)
+{
+ uint i;
+ int error= 0;
+ const char *index_name;
+ KEY* key_info= tab->key_info;
+ NDBDICT *dict= ndb->getDictionary();
+ DBUG_ENTER("ha_ndbcluster::drop_indexes");
+
+ for (i= 0; i < tab->s->keys; i++, key_info++)
+ {
+ NDB_INDEX_TYPE idx_type= get_index_type_from_table(i);
+ m_index[i].type= idx_type;
+ if (m_index[i].status == TO_BE_DROPPED)
{
- DBUG_PRINT("info", ("Get handle to unique_index %s", unique_index_name));
- const NDBINDEX *index= dict->getIndex(unique_index_name, m_tabname);
- if (!index)
- ERR_RETURN(dict->getNdbError());
- m_index[i].unique_index= (void *) index;
- error= fix_unique_index_attr_order(m_index[i], index, key_info);
+ const NdbDictionary::Index *index= m_index[i].index;
+ const NdbDictionary::Index *unique_index= m_index[i].unique_index;
+
+ if (index)
+ {
+ index_name= index->getName();
+ DBUG_PRINT("info", ("Dropping index %u: %s", i, index_name));
+ // Drop ordered index from ndb
+ error= dict->dropIndexGlobal(*index);
+ if (!error)
+ {
+ dict->removeIndexGlobal(*index, 1);
+ m_index[i].index= NULL;
+ }
+ }
+ if (!error && unique_index)
+ {
+ index_name= unique_index->getName();
+ DBUG_PRINT("info", ("Dropping unique index %u: %s", i, index_name));
+ // Drop unique index from ndb
+ error= dict->dropIndexGlobal(*unique_index);
+ if (!error)
+ {
+ dict->removeIndexGlobal(*unique_index, 1);
+ m_index[i].unique_index= NULL;
+ }
+ }
+ if (error)
+ DBUG_RETURN(error);
+ ndb_clear_index(m_index[i]);
+ continue;
}
- if (idx_type == UNIQUE_INDEX &&
- phase != ILBP_CREATE &&
- check_index_fields_not_null(i))
- null_in_unique_index= true;
- m_index[i].null_in_unique_index= null_in_unique_index;
}
DBUG_RETURN(error);
}
-
-/*
+/**
Decode the type of an index from information
- provided in table object
+ provided in table object.
*/
NDB_INDEX_TYPE ha_ndbcluster::get_index_type_from_table(uint inx) const
{
- bool is_hash_index= (table->key_info[inx].algorithm == HA_KEY_ALG_HASH);
- if (inx == table->s->primary_key)
- return is_hash_index ? PRIMARY_KEY_INDEX : PRIMARY_KEY_ORDERED_INDEX;
+ return get_index_type_from_key(inx, table_share->key_info,
+ inx == table_share->primary_key);
+}
- return ((table->key_info[inx].flags & HA_NOSAME) ?
+NDB_INDEX_TYPE ha_ndbcluster::get_index_type_from_key(uint inx,
+ KEY *key_info,
+ bool primary) const
+{
+ bool is_hash_index= (key_info[inx].algorithm ==
+ HA_KEY_ALG_HASH);
+ if (primary)
+ return is_hash_index ? PRIMARY_KEY_INDEX : PRIMARY_KEY_ORDERED_INDEX;
+
+ return ((key_info[inx].flags & HA_NOSAME) ?
(is_hash_index ? UNIQUE_INDEX : UNIQUE_ORDERED_INDEX) :
ORDERED_INDEX);
}
-bool ha_ndbcluster::check_index_fields_not_null(uint inx)
+bool ha_ndbcluster::check_index_fields_not_null(KEY* key_info)
{
- KEY* key_info= table->key_info + inx;
KEY_PART_INFO* key_part= key_info->key_part;
KEY_PART_INFO* end= key_part+key_info->key_parts;
DBUG_ENTER("ha_ndbcluster::check_index_fields_not_null");
@@ -1205,56 +1451,63 @@ bool ha_ndbcluster::check_index_fields_not_null(uint inx)
{
Field* field= key_part->field;
if (field->maybe_null())
- DBUG_RETURN(true);
+ DBUG_RETURN(TRUE);
}
- DBUG_RETURN(false);
+ DBUG_RETURN(FALSE);
}
-void ha_ndbcluster::release_metadata()
+void ha_ndbcluster::release_metadata(THD *thd, Ndb *ndb)
{
uint i;
DBUG_ENTER("release_metadata");
DBUG_PRINT("enter", ("m_tabname: %s", m_tabname));
- m_table= NULL;
+ NDBDICT *dict= ndb->getDictionary();
+ int invalidate_indexes= 0;
+ if (thd && thd->lex && thd->lex->sql_command == SQLCOM_FLUSH)
+ {
+ invalidate_indexes = 1;
+ }
+ if (m_table != NULL)
+ {
+ if (m_table->getObjectStatus() == NdbDictionary::Object::Invalid)
+ invalidate_indexes= 1;
+ dict->removeTableGlobal(*m_table, invalidate_indexes);
+ }
+ // TODO investigate
+ DBUG_ASSERT(m_table_info == NULL);
m_table_info= NULL;
// Release index list
for (i= 0; i < MAX_KEY; i++)
{
- m_index[i].unique_index= NULL;
- m_index[i].index= NULL;
- if (m_index[i].unique_index_attrid_map)
+ if (m_index[i].unique_index)
+ {
+ DBUG_ASSERT(m_table != NULL);
+ dict->removeIndexGlobal(*m_index[i].unique_index, invalidate_indexes);
+ }
+ if (m_index[i].index)
{
- my_free((char *)m_index[i].unique_index_attrid_map, MYF(0));
- m_index[i].unique_index_attrid_map= NULL;
+ DBUG_ASSERT(m_table != NULL);
+ dict->removeIndexGlobal(*m_index[i].index, invalidate_indexes);
}
+ ndb_clear_index(m_index[i]);
}
+ m_table= NULL;
DBUG_VOID_RETURN;
}
int ha_ndbcluster::get_ndb_lock_type(enum thr_lock_type type)
{
- DBUG_ENTER("ha_ndbcluster::get_ndb_lock_type");
if (type >= TL_WRITE_ALLOW_WRITE)
- {
- DBUG_PRINT("info", ("Using exclusive lock"));
- DBUG_RETURN(NdbOperation::LM_Exclusive);
- }
- else if (type == TL_READ_WITH_SHARED_LOCKS ||
- uses_blob_value(m_retrieve_all_fields))
- {
- DBUG_PRINT("info", ("Using read lock"));
- DBUG_RETURN(NdbOperation::LM_Read);
- }
- else
- {
- DBUG_PRINT("info", ("Using committed read"));
- DBUG_RETURN(NdbOperation::LM_CommittedRead);
- }
+ return NdbOperation::LM_Exclusive;
+ if (type == TL_READ_WITH_SHARED_LOCKS ||
+ uses_blob_value())
+ return NdbOperation::LM_Read;
+ return NdbOperation::LM_CommittedRead;
}
static const ulong index_type_flags[]=
@@ -1308,10 +1561,10 @@ inline bool ha_ndbcluster::has_null_in_unique_index(uint idx_no) const
}
-/*
- Get the flags for an index
+/**
+ Get the flags for an index.
- RETURN
+ @return
flags depending on the type of the index.
*/
@@ -1319,13 +1572,13 @@ inline ulong ha_ndbcluster::index_flags(uint idx_no, uint part,
bool all_parts) const
{
DBUG_ENTER("ha_ndbcluster::index_flags");
- DBUG_PRINT("info", ("idx_no: %d", idx_no));
+ DBUG_PRINT("enter", ("idx_no: %u", idx_no));
DBUG_ASSERT(get_index_type_from_table(idx_no) < index_flags_size);
DBUG_RETURN(index_type_flags[get_index_type_from_table(idx_no)] |
HA_KEY_SCAN_NOT_ROR);
}
-static void shrink_varchar(Field* field, const byte* & ptr, char* buf)
+static void shrink_varchar(Field* field, const uchar* & ptr, uchar* buf)
{
if (field->type() == MYSQL_TYPE_VARCHAR && ptr != NULL) {
Field_varstring* f= (Field_varstring*)field;
@@ -1344,9 +1597,9 @@ static void shrink_varchar(Field* field, const byte* & ptr, char* buf)
}
}
-int ha_ndbcluster::set_primary_key(NdbOperation *op, const byte *key)
+int ha_ndbcluster::set_primary_key(NdbOperation *op, const uchar *key)
{
- KEY* key_info= table->key_info + table->s->primary_key;
+ KEY* key_info= table->key_info + table_share->primary_key;
KEY_PART_INFO* key_part= key_info->key_part;
KEY_PART_INFO* end= key_part+key_info->key_parts;
DBUG_ENTER("set_primary_key");
@@ -1354,8 +1607,8 @@ int ha_ndbcluster::set_primary_key(NdbOperation *op, const byte *key)
for (; key_part != end; key_part++)
{
Field* field= key_part->field;
- const byte* ptr= key;
- char buf[256];
+ const uchar* ptr= key;
+ uchar buf[256];
shrink_varchar(field, ptr, buf);
if (set_ndb_key(op, field,
key_part->fieldnr-1, ptr))
@@ -1366,9 +1619,9 @@ int ha_ndbcluster::set_primary_key(NdbOperation *op, const byte *key)
}
-int ha_ndbcluster::set_primary_key_from_record(NdbOperation *op, const byte *record)
+int ha_ndbcluster::set_primary_key_from_record(NdbOperation *op, const uchar *record)
{
- KEY* key_info= table->key_info + table->s->primary_key;
+ KEY* key_info= table->key_info + table_share->primary_key;
KEY_PART_INFO* key_part= key_info->key_part;
KEY_PART_INFO* end= key_part+key_info->key_parts;
DBUG_ENTER("set_primary_key_from_record");
@@ -1391,14 +1644,10 @@ bool ha_ndbcluster::check_index_fields_in_write_set(uint keyno)
uint i;
DBUG_ENTER("check_index_fields_in_write_set");
- if (m_retrieve_all_fields)
- {
- DBUG_RETURN(true);
- }
for (i= 0; key_part != end; key_part++, i++)
{
Field* field= key_part->field;
- if (field->query_id != current_thd->query_id)
+ if (!bitmap_is_set(table->write_set, field->field_index))
{
DBUG_RETURN(false);
}
@@ -1407,7 +1656,8 @@ bool ha_ndbcluster::check_index_fields_in_write_set(uint keyno)
DBUG_RETURN(true);
}
-int ha_ndbcluster::set_index_key_from_record(NdbOperation *op, const byte *record, uint keyno)
+int ha_ndbcluster::set_index_key_from_record(NdbOperation *op,
+ const uchar *record, uint keyno)
{
KEY* key_info= table->key_info + keyno;
KEY_PART_INFO* key_part= key_info->key_part;
@@ -1428,7 +1678,7 @@ int ha_ndbcluster::set_index_key_from_record(NdbOperation *op, const byte *recor
int
ha_ndbcluster::set_index_key(NdbOperation *op,
const KEY *key_info,
- const byte * key_ptr)
+ const uchar * key_ptr)
{
DBUG_ENTER("ha_ndbcluster::set_index_key");
uint i;
@@ -1438,8 +1688,8 @@ ha_ndbcluster::set_index_key(NdbOperation *op,
for (i= 0; key_part != end; key_part++, i++)
{
Field* field= key_part->field;
- const byte* ptr= key_part->null_bit ? key_ptr + 1 : key_ptr;
- char buf[256];
+ const uchar* ptr= key_part->null_bit ? key_ptr + 1 : key_ptr;
+ uchar buf[256];
shrink_varchar(field, ptr, buf);
if (set_ndb_key(op, field, m_index[active_index].unique_index_attrid_map[i], ptr))
ERR_RETURN(m_active_trans->getNdbError());
@@ -1449,35 +1699,32 @@ ha_ndbcluster::set_index_key(NdbOperation *op,
}
inline
-int ha_ndbcluster::define_read_attrs(byte* buf, NdbOperation* op)
+int ha_ndbcluster::define_read_attrs(uchar* buf, NdbOperation* op)
{
uint i;
- THD *thd= current_thd;
-
DBUG_ENTER("define_read_attrs");
// Define attributes to read
- for (i= 0; i < table->s->fields; i++)
+ for (i= 0; i < table_share->fields; i++)
{
Field *field= table->field[i];
- if ((thd->query_id == field->query_id) ||
- ((field->flags & PRI_KEY_FLAG)) ||
- m_retrieve_all_fields)
+ if (bitmap_is_set(table->read_set, i) ||
+ ((field->flags & PRI_KEY_FLAG)))
{
if (get_ndb_value(op, field, i, buf))
ERR_RETURN(op->getNdbError());
}
- else
+ else
{
m_value[i].ptr= NULL;
}
}
- if (table->s->primary_key == MAX_KEY)
+ if (table_share->primary_key == MAX_KEY)
{
DBUG_PRINT("info", ("Getting hidden key"));
// Scanning table with no primary key
- int hidden_no= table->s->fields;
+ int hidden_no= table_share->fields;
#ifndef DBUG_OFF
const NDBTAB *tab= (const NDBTAB *) m_table;
if (!tab->getColumn(hidden_no))
@@ -1489,20 +1736,23 @@ int ha_ndbcluster::define_read_attrs(byte* buf, NdbOperation* op)
DBUG_RETURN(0);
}
-/*
- Read one record from NDB using primary key
+
+/**
+ Read one record from NDB using primary key.
*/
-int ha_ndbcluster::pk_read(const byte *key, uint key_len, byte *buf)
+int ha_ndbcluster::pk_read(const uchar *key, uint key_len, uchar *buf,
+ uint32 part_id)
{
- uint no_fields= table->s->fields;
+ uint no_fields= table_share->fields;
NdbConnection *trans= m_active_trans;
NdbOperation *op;
int res;
DBUG_ENTER("pk_read");
DBUG_PRINT("enter", ("key_len: %u", key_len));
- DBUG_DUMP("key", (uchar*)key, key_len);
+ DBUG_DUMP("key", key, key_len);
+ m_write_op= FALSE;
NdbOperation::LockMode lm=
(NdbOperation::LockMode)get_ndb_lock_type(m_lock.type);
@@ -1510,11 +1760,11 @@ int ha_ndbcluster::pk_read(const byte *key, uint key_len, byte *buf)
op->readTuple(lm) != 0)
ERR_RETURN(trans->getNdbError());
- if (table->s->primary_key == MAX_KEY)
+ if (table_share->primary_key == MAX_KEY)
{
// This table has no primary key, use "hidden" primary key
DBUG_PRINT("info", ("Using hidden key"));
- DBUG_DUMP("key", (uchar*)key, 8);
+ DBUG_DUMP("key", key, 8);
if (set_hidden_key(op, no_fields, key))
ERR_RETURN(trans->getNdbError());
@@ -1530,8 +1780,20 @@ int ha_ndbcluster::pk_read(const byte *key, uint key_len, byte *buf)
if ((res= define_read_attrs(buf, op)))
DBUG_RETURN(res);
-
- if (execute_no_commit_ie(this,trans,false) != 0)
+
+ if (m_use_partition_function)
+ {
+ op->setPartitionId(part_id);
+ // If table has user defined partitioning
+ // and no indexes, we need to read the partition id
+ // to support ORDER BY queries
+ if (table_share->primary_key == MAX_KEY &&
+ get_ndb_partition_id(op))
+ ERR_RETURN(trans->getNdbError());
+ }
+
+ if ((res = execute_no_commit_ie(this,trans,FALSE)) != 0 ||
+ op->getNdbError().code)
{
table->status= STATUS_NOT_FOUND;
DBUG_RETURN(ndb_err(trans));
@@ -1543,42 +1805,60 @@ int ha_ndbcluster::pk_read(const byte *key, uint key_len, byte *buf)
DBUG_RETURN(0);
}
-/*
+/**
Read one complementing record from NDB using primary key from old_data
+ or hidden key.
*/
-int ha_ndbcluster::complemented_pk_read(const byte *old_data, byte *new_data)
+int ha_ndbcluster::complemented_read(const uchar *old_data, uchar *new_data,
+ uint32 old_part_id)
{
- uint no_fields= table->s->fields, i;
+ uint no_fields= table_share->fields, i;
NdbTransaction *trans= m_active_trans;
NdbOperation *op;
- THD *thd= current_thd;
- DBUG_ENTER("complemented_pk_read");
+ DBUG_ENTER("complemented_read");
+ m_write_op= FALSE;
- if (m_retrieve_all_fields)
+ if (bitmap_is_set_all(table->read_set))
+ {
// We have allready retrieved all fields, nothing to complement
DBUG_RETURN(0);
+ }
NdbOperation::LockMode lm=
(NdbOperation::LockMode)get_ndb_lock_type(m_lock.type);
if (!(op= trans->getNdbOperation((const NDBTAB *) m_table)) ||
op->readTuple(lm) != 0)
ERR_RETURN(trans->getNdbError());
- int res;
- if ((res= set_primary_key_from_record(op, old_data)))
- ERR_RETURN(trans->getNdbError());
+ if (table_share->primary_key != MAX_KEY)
+ {
+ if (set_primary_key_from_record(op, old_data))
+ ERR_RETURN(trans->getNdbError());
+ }
+ else
+ {
+ // This table has no primary key, use "hidden" primary key
+ if (set_hidden_key(op, table->s->fields, m_ref))
+ ERR_RETURN(op->getNdbError());
+ }
+
+ if (m_use_partition_function)
+ op->setPartitionId(old_part_id);
+
// Read all unreferenced non-key field(s)
for (i= 0; i < no_fields; i++)
{
Field *field= table->field[i];
if (!((field->flags & PRI_KEY_FLAG) ||
- (thd->query_id == field->query_id)))
+ bitmap_is_set(table->read_set, i)) &&
+ !bitmap_is_set(table->write_set, i))
{
if (get_ndb_value(op, field, i, new_data))
ERR_RETURN(trans->getNdbError());
}
}
- if (execute_no_commit(this,trans,false) != 0)
+
+ if (execute_no_commit(this,trans,FALSE) != 0)
{
table->status= STATUS_NOT_FOUND;
DBUG_RETURN(ndb_err(trans));
@@ -1588,14 +1868,14 @@ int ha_ndbcluster::complemented_pk_read(const byte *old_data, byte *new_data)
unpack_record(new_data);
table->status= 0;
- /**
+ /*
* restore m_value
*/
for (i= 0; i < no_fields; i++)
{
Field *field= table->field[i];
if (!((field->flags & PRI_KEY_FLAG) ||
- (thd->query_id == field->query_id)))
+ bitmap_is_set(table->read_set, i)))
{
m_value[i].ptr= NULL;
}
@@ -1604,12 +1884,12 @@ int ha_ndbcluster::complemented_pk_read(const byte *old_data, byte *new_data)
DBUG_RETURN(0);
}
-/*
- * Check that all operations between first and last all
- * have gotten the errcode
- * If checking for HA_ERR_KEY_NOT_FOUND then update m_dupkey
- * for all succeeding operations
- */
+/**
+ Check that all operations between first and last all
+ have gotten the errcode
+ If checking for HA_ERR_KEY_NOT_FOUND then update m_dupkey
+ for all succeeding operations
+*/
bool ha_ndbcluster::check_all_operations_for_error(NdbTransaction *trans,
const NdbOperation *first,
const NdbOperation *last,
@@ -1624,7 +1904,7 @@ bool ha_ndbcluster::check_all_operations_for_error(NdbTransaction *trans,
if (err.status != NdbError::Success)
{
if (ndb_to_mysql_error(&err) != (int) errcode)
- DBUG_RETURN(false);
+ DBUG_RETURN(FALSE);
if (op == last) break;
op= trans->getNextCompletedOperation(op);
}
@@ -1655,10 +1935,10 @@ bool ha_ndbcluster::check_all_operations_for_error(NdbTransaction *trans,
if (errcode == HA_ERR_KEY_NOT_FOUND)
m_dupkey= table->s->primary_key;
}
- DBUG_RETURN(false);
+ DBUG_RETURN(FALSE);
}
}
- DBUG_RETURN(true);
+ DBUG_RETURN(TRUE);
}
@@ -1667,7 +1947,7 @@ bool ha_ndbcluster::check_all_operations_for_error(NdbTransaction *trans,
*/
static
int
-check_null_in_record(const KEY* key_info, const byte *record)
+check_null_in_record(const KEY* key_info, const uchar *record)
{
KEY_PART_INFO *curr_part, *end_part;
curr_part= key_info->key_part;
@@ -1689,12 +1969,12 @@ check_null_in_record(const KEY* key_info, const byte *record)
*/
}
-/*
- * Peek to check if any rows already exist with conflicting
- * primary key or unique index values
+/**
+ Peek to check if any rows already exist with conflicting
+ primary key or unique index values
*/
-int ha_ndbcluster::peek_indexed_rows(const byte *record,
+int ha_ndbcluster::peek_indexed_rows(const uchar *record,
NDB_WRITE_OP write_op)
{
NdbTransaction *trans= m_active_trans;
@@ -1705,8 +1985,7 @@ int ha_ndbcluster::peek_indexed_rows(const byte *record,
DBUG_ENTER("peek_indexed_rows");
NdbOperation::LockMode lm=
- (NdbOperation::LockMode)get_ndb_lock_type(m_lock.type);
-
+ (NdbOperation::LockMode)get_ndb_lock_type(m_lock.type);
first= NULL;
if (write_op != NDB_UPDATE && table->s->primary_key != MAX_KEY)
{
@@ -1720,6 +1999,22 @@ int ha_ndbcluster::peek_indexed_rows(const byte *record,
first= op;
if ((res= set_primary_key_from_record(op, record)))
ERR_RETURN(trans->getNdbError());
+
+ if (m_use_partition_function)
+ {
+ uint32 part_id;
+ int error;
+ longlong func_value;
+ my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->read_set);
+ error= m_part_info->get_partition_id(m_part_info, &part_id, &func_value);
+ dbug_tmp_restore_column_map(table->read_set, old_map);
+ if (error)
+ {
+ m_part_info->err_value= func_value;
+ DBUG_RETURN(error);
+ }
+ op->setPartitionId(part_id);
+ }
}
/*
* Fetch any rows with colliding unique indexes
@@ -1747,13 +2042,11 @@ int ha_ndbcluster::peek_indexed_rows(const byte *record,
DBUG_PRINT("info", ("skipping check for key %u not in write_set", i));
continue;
}
-
NdbIndexOperation *iop;
- NDBINDEX *unique_index = (NDBINDEX *) m_index[i].unique_index;
+ const NDBINDEX *unique_index = m_index[i].unique_index;
key_part= key_info->key_part;
end= key_part + key_info->key_parts;
- if (!(iop= trans->getNdbIndexOperation(unique_index,
- (const NDBTAB *) m_table)) ||
+ if (!(iop= trans->getNdbIndexOperation(unique_index, m_table)) ||
iop->readTuple(lm) != 0)
ERR_RETURN(trans->getNdbError());
@@ -1765,7 +2058,7 @@ int ha_ndbcluster::peek_indexed_rows(const byte *record,
}
last= trans->getLastDefinedOperation();
if (first)
- res= execute_no_commit_ie(this,trans,false);
+ res= execute_no_commit_ie(this,trans,FALSE);
else
{
// Table has no keys
@@ -1785,25 +2078,25 @@ int ha_ndbcluster::peek_indexed_rows(const byte *record,
DBUG_RETURN(0);
}
-/*
- Read one record from NDB using unique secondary index
+
+/**
+ Read one record from NDB using unique secondary index.
*/
-int ha_ndbcluster::unique_index_read(const byte *key,
- uint key_len, byte *buf)
+int ha_ndbcluster::unique_index_read(const uchar *key,
+ uint key_len, uchar *buf)
{
int res;
NdbTransaction *trans= m_active_trans;
NdbIndexOperation *op;
DBUG_ENTER("ha_ndbcluster::unique_index_read");
DBUG_PRINT("enter", ("key_len: %u, index: %u", key_len, active_index));
- DBUG_DUMP("key", (uchar*)key, key_len);
+ DBUG_DUMP("key", key, key_len);
NdbOperation::LockMode lm=
(NdbOperation::LockMode)get_ndb_lock_type(m_lock.type);
- if (!(op= trans->getNdbIndexOperation((NDBINDEX *)
- m_index[active_index].unique_index,
- (const NDBTAB *) m_table)) ||
+ if (!(op= trans->getNdbIndexOperation(m_index[active_index].unique_index,
+ m_table)) ||
op->readTuple(lm) != 0)
ERR_RETURN(trans->getNdbError());
@@ -1814,7 +2107,8 @@ int ha_ndbcluster::unique_index_read(const byte *key,
if ((res= define_read_attrs(buf, op)))
DBUG_RETURN(res);
- if (execute_no_commit_ie(this,trans,false) != 0)
+ if (execute_no_commit_ie(this,trans,FALSE) != 0 ||
+ op->getNdbError().code)
{
int err= ndb_err(trans);
if(err==HA_ERR_KEY_NOT_FOUND)
@@ -1837,7 +2131,7 @@ inline int ha_ndbcluster::fetch_next(NdbScanOperation* cursor)
int local_check;
NdbTransaction *trans= m_active_trans;
- if (m_lock_tuple)
+ if (m_lock_tuple)
{
/*
Lock level m_lock.type either TL_WRITE_ALLOW_WRITE
@@ -1853,16 +2147,16 @@ inline int ha_ndbcluster::fetch_next(NdbScanOperation* cursor)
if (!(op= m_active_cursor->lockCurrentTuple()))
{
/* purecov: begin inspected */
- m_lock_tuple= false;
+ m_lock_tuple= FALSE;
ERR_RETURN(con_trans->getNdbError());
/* purecov: end */
}
m_ops_pending++;
}
- m_lock_tuple= false;
-
+ m_lock_tuple= FALSE;
+
bool contact_ndb= m_lock.type < TL_WRITE_ALLOW_WRITE &&
- m_lock.type != TL_READ_WITH_SHARED_LOCKS;
+ m_lock.type != TL_READ_WITH_SHARED_LOCKS;;
do {
DBUG_PRINT("info", ("Call nextResult, contact_ndb: %d", contact_ndb));
/*
@@ -1870,7 +2164,7 @@ inline int ha_ndbcluster::fetch_next(NdbScanOperation* cursor)
*/
if (m_ops_pending && m_blobs_pending)
{
- if (execute_no_commit(this,trans,false) != 0)
+ if (execute_no_commit(this,trans,FALSE) != 0)
DBUG_RETURN(ndb_err(trans));
m_ops_pending= 0;
m_blobs_pending= FALSE;
@@ -1902,7 +2196,7 @@ inline int ha_ndbcluster::fetch_next(NdbScanOperation* cursor)
{
if (m_transaction_on)
{
- if (execute_no_commit(this,trans,false) != 0)
+ if (execute_no_commit(this,trans,FALSE) != 0)
DBUG_RETURN(-1);
}
else
@@ -1928,18 +2222,17 @@ inline int ha_ndbcluster::fetch_next(NdbScanOperation* cursor)
DBUG_RETURN(1);
}
-/*
+/**
Get the next record of a started scan. Try to fetch
it locally from NdbApi cached records if possible,
otherwise ask NDB for more.
- NOTE
- If this is a update/delete make sure to not contact
- NDB before any pending ops have been sent to NDB.
-
+ @note
+ If this is a update/delete make sure to not contact
+ NDB before any pending ops have been sent to NDB.
*/
-inline int ha_ndbcluster::next_result(byte *buf)
+inline int ha_ndbcluster::next_result(uchar *buf)
{
int res;
DBUG_ENTER("next_result");
@@ -1969,15 +2262,17 @@ inline int ha_ndbcluster::next_result(byte *buf)
}
}
-/*
+/**
Set bounds for ordered index scan.
*/
int ha_ndbcluster::set_bounds(NdbIndexScanOperation *op,
+ uint inx,
+ bool rir,
const key_range *keys[2],
uint range_no)
{
- const KEY *const key_info= table->key_info + active_index;
+ const KEY *const key_info= table->key_info + inx;
const uint key_parts= key_info->key_parts;
uint key_tot_len[2];
uint tot_len;
@@ -2016,10 +2311,10 @@ int ha_ndbcluster::set_bounds(NdbIndexScanOperation *op,
struct part_st {
bool part_last;
const key_range *key;
- const byte *part_ptr;
+ const uchar *part_ptr;
bool part_null;
int bound_type;
- const char* bound_ptr;
+ const uchar* bound_ptr;
};
struct part_st part[2];
@@ -2042,7 +2337,10 @@ int ha_ndbcluster::set_bounds(NdbIndexScanOperation *op,
switch (p.key->flag)
{
case HA_READ_KEY_EXACT:
- p.bound_type= NdbIndexScanOperation::BoundEQ;
+ if (! rir)
+ p.bound_type= NdbIndexScanOperation::BoundEQ;
+ else // differs for records_in_range
+ p.bound_type= NdbIndexScanOperation::BoundLE;
break;
// ascending
case HA_READ_KEY_OR_NEXT:
@@ -2124,15 +2422,15 @@ int ha_ndbcluster::set_bounds(NdbIndexScanOperation *op,
// Set bound if not done with this key
if (p.key != NULL)
{
- DBUG_PRINT("info", ("key %d:%d offset=%d length=%d last=%d bound=%d",
+ DBUG_PRINT("info", ("key %d:%d offset: %d length: %d last: %d bound: %d",
j, i, tot_len, part_len, p.part_last, p.bound_type));
- DBUG_DUMP("info", (const uchar*)p.part_ptr, part_store_len);
+ DBUG_DUMP("info", p.part_ptr, part_store_len);
// Set bound if not cancelled via type -1
if (p.bound_type != -1)
{
- const char* ptr= p.bound_ptr;
- char buf[256];
+ const uchar* ptr= p.bound_ptr;
+ uchar buf[256];
shrink_varchar(field, ptr, buf);
if (op->setBound(i, p.bound_type, ptr))
ERR_RETURN(op->getNdbError());
@@ -2145,13 +2443,14 @@ int ha_ndbcluster::set_bounds(NdbIndexScanOperation *op,
DBUG_RETURN(op->end_of_bound(range_no));
}
-/*
- Start ordered index scan in NDB
+/**
+ Start ordered index scan in NDB.
*/
int ha_ndbcluster::ordered_index_scan(const key_range *start_key,
const key_range *end_key,
- bool sorted, bool descending, byte* buf)
+ bool sorted, bool descending,
+ uchar* buf, part_id_range *part_spec)
{
int res;
bool restart;
@@ -2162,6 +2461,7 @@ int ha_ndbcluster::ordered_index_scan(const key_range *start_key,
DBUG_PRINT("enter", ("index: %u, sorted: %d, descending: %d",
active_index, sorted, descending));
DBUG_PRINT("enter", ("Starting new ordered scan on %s", m_tabname));
+ m_write_op= FALSE;
// Check that sorted seems to be initialised
DBUG_ASSERT(sorted == 0 || sorted == 1);
@@ -2171,17 +2471,22 @@ int ha_ndbcluster::ordered_index_scan(const key_range *start_key,
restart= FALSE;
NdbOperation::LockMode lm=
(NdbOperation::LockMode)get_ndb_lock_type(m_lock.type);
- bool need_pk = (lm == NdbOperation::LM_Read);
- if (!(op= trans->getNdbIndexScanOperation((NDBINDEX *)
- m_index[active_index].index,
- (const NDBTAB *) m_table)) ||
- op->readTuples(lm, 0, parallelism, sorted, descending, false, need_pk))
+ bool need_pk = (lm == NdbOperation::LM_Read);
+ if (!(op= trans->getNdbIndexScanOperation(m_index[active_index].index,
+ m_table)) ||
+ op->readTuples(lm, 0, parallelism, sorted, descending, FALSE, need_pk))
ERR_RETURN(trans->getNdbError());
+ if (m_use_partition_function && part_spec != NULL &&
+ part_spec->start_part == part_spec->end_part)
+ op->setPartitionId(part_spec->start_part);
m_active_cursor= op;
} else {
restart= TRUE;
op= (NdbIndexScanOperation*)m_active_cursor;
+ if (m_use_partition_function && part_spec != NULL &&
+ part_spec->start_part == part_spec->end_part)
+ op->setPartitionId(part_spec->start_part);
DBUG_ASSERT(op->getSorted() == sorted);
DBUG_ASSERT(op->getLockMode() ==
(NdbOperation::LockMode)get_ndb_lock_type(m_lock.type));
@@ -2191,50 +2496,119 @@ int ha_ndbcluster::ordered_index_scan(const key_range *start_key,
{
const key_range *keys[2]= { start_key, end_key };
- res= set_bounds(op, keys);
+ res= set_bounds(op, active_index, FALSE, keys);
if (res)
DBUG_RETURN(res);
}
- if (!restart && m_cond && m_cond->generate_scan_filter(op))
- DBUG_RETURN(ndb_err(trans));
-
- if (!restart && (res= define_read_attrs(buf, op)))
+ if (!restart)
{
- DBUG_RETURN(res);
+ if (m_cond && m_cond->generate_scan_filter(op))
+ DBUG_RETURN(ndb_err(trans));
+
+ if ((res= define_read_attrs(buf, op)))
+ {
+ DBUG_RETURN(res);
+ }
+
+ // If table has user defined partitioning
+ // and no primary key, we need to read the partition id
+ // to support ORDER BY queries
+ if (m_use_partition_function &&
+ (table_share->primary_key == MAX_KEY) &&
+ (get_ndb_partition_id(op)))
+ ERR_RETURN(trans->getNdbError());
}
- if (execute_no_commit(this,trans,false) != 0)
+ if (execute_no_commit(this,trans,FALSE) != 0)
DBUG_RETURN(ndb_err(trans));
DBUG_RETURN(next_result(buf));
}
+static
+int
+guess_scan_flags(NdbOperation::LockMode lm,
+ const NDBTAB* tab, const MY_BITMAP* readset)
+{
+ int flags= 0;
+ flags|= (lm == NdbOperation::LM_Read) ? NdbScanOperation::SF_KeyInfo : 0;
+ if (tab->checkColumns(0, 0) & 2)
+ {
+ int ret = tab->checkColumns(readset->bitmap, no_bytes_in_map(readset));
+
+ if (ret & 2)
+ { // If disk columns...use disk scan
+ flags |= NdbScanOperation::SF_DiskScan;
+ }
+ else if ((ret & 4) == 0 && (lm == NdbOperation::LM_Exclusive))
+ {
+ // If no mem column is set and exclusive...guess disk scan
+ flags |= NdbScanOperation::SF_DiskScan;
+ }
+ }
+ return flags;
+}
+
+
/*
Unique index scan in NDB (full table scan with scan filter)
*/
int ha_ndbcluster::unique_index_scan(const KEY* key_info,
- const byte *key,
+ const uchar *key,
uint key_len,
- byte *buf)
+ uchar *buf)
{
int res;
NdbScanOperation *op;
NdbTransaction *trans= m_active_trans;
+ part_id_range part_spec;
DBUG_ENTER("unique_index_scan");
DBUG_PRINT("enter", ("Starting new scan on %s", m_tabname));
NdbOperation::LockMode lm=
(NdbOperation::LockMode)get_ndb_lock_type(m_lock.type);
- bool need_pk = (lm == NdbOperation::LM_Read);
+ int flags= guess_scan_flags(lm, m_table, table->read_set);
if (!(op=trans->getNdbScanOperation((const NDBTAB *) m_table)) ||
- op->readTuples(lm,
- (need_pk)?NdbScanOperation::SF_KeyInfo:0,
- parallelism))
+ op->readTuples(lm, flags, parallelism))
ERR_RETURN(trans->getNdbError());
m_active_cursor= op;
+
+ if (m_use_partition_function)
+ {
+ part_spec.start_part= 0;
+ part_spec.end_part= m_part_info->get_tot_partitions() - 1;
+ prune_partition_set(table, &part_spec);
+ DBUG_PRINT("info", ("part_spec.start_part = %u, part_spec.end_part = %u",
+ part_spec.start_part, part_spec.end_part));
+ /*
+ If partition pruning has found no partition in set
+ we can return HA_ERR_END_OF_FILE
+ If partition pruning has found exactly one partition in set
+ we can optimize scan to run towards that partition only.
+ */
+ if (part_spec.start_part > part_spec.end_part)
+ {
+ DBUG_RETURN(HA_ERR_END_OF_FILE);
+ }
+ else if (part_spec.start_part == part_spec.end_part)
+ {
+ /*
+ Only one partition is required to scan, if sorted is required we
+ don't need it any more since output from one ordered partitioned
+ index is always sorted.
+ */
+ m_active_cursor->setPartitionId(part_spec.start_part);
+ }
+ // If table has user defined partitioning
+ // and no primary key, we need to read the partition id
+ // to support ORDER BY queries
+ if ((table_share->primary_key == MAX_KEY) &&
+ (get_ndb_partition_id(op)))
+ ERR_RETURN(trans->getNdbError());
+ }
if (!m_cond)
m_cond= new ha_ndbcluster_cond;
if (!m_cond)
@@ -2247,40 +2621,75 @@ int ha_ndbcluster::unique_index_scan(const KEY* key_info,
if ((res= define_read_attrs(buf, op)))
DBUG_RETURN(res);
- if (execute_no_commit(this,trans,false) != 0)
+ if (execute_no_commit(this,trans,FALSE) != 0)
DBUG_RETURN(ndb_err(trans));
DBUG_PRINT("exit", ("Scan started successfully"));
DBUG_RETURN(next_result(buf));
}
-/*
- Start full table scan in NDB
- */
-int ha_ndbcluster::full_table_scan(byte *buf)
+/**
+ Start full table scan in NDB.
+*/
+int ha_ndbcluster::full_table_scan(uchar *buf)
{
int res;
NdbScanOperation *op;
NdbTransaction *trans= m_active_trans;
+ part_id_range part_spec;
DBUG_ENTER("full_table_scan");
DBUG_PRINT("enter", ("Starting new scan on %s", m_tabname));
+ m_write_op= FALSE;
NdbOperation::LockMode lm=
(NdbOperation::LockMode)get_ndb_lock_type(m_lock.type);
- bool need_pk = (lm == NdbOperation::LM_Read);
- if (!(op=trans->getNdbScanOperation((const NDBTAB *) m_table)) ||
- op->readTuples(lm,
- (need_pk)?NdbScanOperation::SF_KeyInfo:0,
- parallelism))
+ int flags= guess_scan_flags(lm, m_table, table->read_set);
+ if (!(op=trans->getNdbScanOperation(m_table)) ||
+ op->readTuples(lm, flags, parallelism))
ERR_RETURN(trans->getNdbError());
m_active_cursor= op;
+
+ if (m_use_partition_function)
+ {
+ part_spec.start_part= 0;
+ part_spec.end_part= m_part_info->get_tot_partitions() - 1;
+ prune_partition_set(table, &part_spec);
+ DBUG_PRINT("info", ("part_spec.start_part: %u part_spec.end_part: %u",
+ part_spec.start_part, part_spec.end_part));
+ /*
+ If partition pruning has found no partition in set
+ we can return HA_ERR_END_OF_FILE
+ If partition pruning has found exactly one partition in set
+ we can optimize scan to run towards that partition only.
+ */
+ if (part_spec.start_part > part_spec.end_part)
+ {
+ DBUG_RETURN(HA_ERR_END_OF_FILE);
+ }
+ else if (part_spec.start_part == part_spec.end_part)
+ {
+ /*
+ Only one partition is required to scan, if sorted is required we
+ don't need it any more since output from one ordered partitioned
+ index is always sorted.
+ */
+ m_active_cursor->setPartitionId(part_spec.start_part);
+ }
+ // If table has user defined partitioning
+ // and no primary key, we need to read the partition id
+ // to support ORDER BY queries
+ if ((table_share->primary_key == MAX_KEY) &&
+ (get_ndb_partition_id(op)))
+ ERR_RETURN(trans->getNdbError());
+ }
+
if (m_cond && m_cond->generate_scan_filter(op))
DBUG_RETURN(ndb_err(trans));
if ((res= define_read_attrs(buf, op)))
DBUG_RETURN(res);
- if (execute_no_commit(this,trans,false) != 0)
+ if (execute_no_commit(this,trans,FALSE) != 0)
DBUG_RETURN(ndb_err(trans));
DBUG_PRINT("exit", ("Scan started successfully"));
DBUG_RETURN(next_result(buf));
@@ -2289,26 +2698,33 @@ int ha_ndbcluster::full_table_scan(byte *buf)
int
ha_ndbcluster::set_auto_inc(Field *field)
{
- Ndb *ndb= get_ndb();
- Uint64 next_val= (Uint64) field->val_int() + 1;
DBUG_ENTER("ha_ndbcluster::set_auto_inc");
+ Ndb *ndb= get_ndb();
+ bool read_bit= bitmap_is_set(table->read_set, field->field_index);
+ bitmap_set_bit(table->read_set, field->field_index);
+ Uint64 next_val= (Uint64) field->val_int() + 1;
+ if (!read_bit)
+ bitmap_clear_bit(table->read_set, field->field_index);
#ifndef DBUG_OFF
char buff[22];
DBUG_PRINT("info",
("Trying to set next auto increment value to %s",
llstr(next_val, buff)));
#endif
- if (ndb->setAutoIncrementValue((const NDBTAB *) m_table, next_val, TRUE)
- == -1)
- ERR_RETURN(ndb->getNdbError());
+ if (ndb->checkUpdateAutoIncrementValue(m_share->tuple_id_range, next_val))
+ {
+ Ndb_tuple_id_range_guard g(m_share);
+ if (ndb->setAutoIncrementValue(m_table, g.range, next_val, TRUE)
+ == -1)
+ ERR_RETURN(ndb->getNdbError());
+ }
DBUG_RETURN(0);
}
-
-/*
- Insert one record into NDB
+/**
+ Insert one record into NDB.
*/
-int ha_ndbcluster::write_row(byte *record)
+int ha_ndbcluster::write_row(uchar *record)
{
bool has_auto_increment;
uint i;
@@ -2316,10 +2732,12 @@ int ha_ndbcluster::write_row(byte *record)
NdbOperation *op;
int res;
THD *thd= table->in_use;
- DBUG_ENTER("write_row");
+ longlong func_value= 0;
+ DBUG_ENTER("ha_ndbcluster::write_row");
+ m_write_op= TRUE;
has_auto_increment= (table->next_number_field && record == table->record[0]);
- if (table->s->primary_key != MAX_KEY)
+ if (table_share->primary_key != MAX_KEY)
{
/*
* Increase any auto_incremented primary key
@@ -2331,10 +2749,10 @@ int ha_ndbcluster::write_row(byte *record)
m_skip_auto_increment= FALSE;
if ((error= update_auto_increment()))
DBUG_RETURN(error);
- m_skip_auto_increment= !auto_increment_column_changed;
+ m_skip_auto_increment= (insert_id_for_cur_row == 0);
}
}
-
+
/*
* If IGNORE the ignore constraint violations on primary and unique keys
*/
@@ -2355,18 +2773,33 @@ int ha_ndbcluster::write_row(byte *record)
DBUG_RETURN(peek_res);
}
- statistic_increment(thd->status_var.ha_write_count, &LOCK_status);
+ ha_statistic_increment(&SSV::ha_write_count);
if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_INSERT)
table->timestamp_field->set_time();
- if (!(op= trans->getNdbOperation((const NDBTAB *) m_table)))
+ if (!(op= trans->getNdbOperation(m_table)))
ERR_RETURN(trans->getNdbError());
res= (m_use_write) ? op->writeTuple() :op->insertTuple();
if (res != 0)
ERR_RETURN(trans->getNdbError());
- if (table->s->primary_key == MAX_KEY)
+ if (m_use_partition_function)
+ {
+ uint32 part_id;
+ int error;
+ my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->read_set);
+ error= m_part_info->get_partition_id(m_part_info, &part_id, &func_value);
+ dbug_tmp_restore_column_map(table->read_set, old_map);
+ if (error)
+ {
+ m_part_info->err_value= func_value;
+ DBUG_RETURN(error);
+ }
+ op->setPartitionId(part_id);
+ }
+
+ if (table_share->primary_key == MAX_KEY)
{
// Table has hidden primary key
Ndb *ndb= get_ndb();
@@ -2375,41 +2808,76 @@ int ha_ndbcluster::write_row(byte *record)
int retry_sleep= 30; /* 30 milliseconds, transaction */
for (;;)
{
- if (ndb->getAutoIncrementValue((const NDBTAB *) m_table,
- auto_value, 1) == -1)
+ Ndb_tuple_id_range_guard g(m_share);
+ if (ndb->getAutoIncrementValue(m_table, g.range, auto_value, 1) == -1)
{
- if (--retries &&
- ndb->getNdbError().status == NdbError::TemporaryError)
- {
- my_sleep(retry_sleep);
- continue;
- }
- ERR_RETURN(ndb->getNdbError());
+ if (--retries &&
+ ndb->getNdbError().status == NdbError::TemporaryError)
+ {
+ my_sleep(retry_sleep);
+ continue;
+ }
+ ERR_RETURN(ndb->getNdbError());
}
break;
}
- if (set_hidden_key(op, table->s->fields, (const byte*)&auto_value))
+ if (set_hidden_key(op, table_share->fields, (const uchar*)&auto_value))
ERR_RETURN(op->getNdbError());
}
- else
+ else
{
- if ((res= set_primary_key_from_record(op, record)))
- return res;
+ int error;
+ if ((error= set_primary_key_from_record(op, record)))
+ DBUG_RETURN(error);
}
// Set non-key attribute(s)
bool set_blob_value= FALSE;
- for (i= 0; i < table->s->fields; i++)
+ my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->read_set);
+ for (i= 0; i < table_share->fields; i++)
{
Field *field= table->field[i];
if (!(field->flags & PRI_KEY_FLAG) &&
- set_ndb_value(op, field, i, &set_blob_value))
+ (bitmap_is_set(table->write_set, i) || !m_use_write) &&
+ set_ndb_value(op, field, i, record-table->record[0], &set_blob_value))
{
m_skip_auto_increment= TRUE;
+ dbug_tmp_restore_column_map(table->read_set, old_map);
ERR_RETURN(op->getNdbError());
}
}
+ dbug_tmp_restore_column_map(table->read_set, old_map);
+
+ if (m_use_partition_function)
+ {
+ /*
+ We need to set the value of the partition function value in
+ NDB since the NDB kernel doesn't have easy access to the function
+ to calculate the value.
+ */
+ if (func_value >= INT_MAX32)
+ func_value= INT_MAX32;
+ uint32 part_func_value= (uint32)func_value;
+ uint no_fields= table_share->fields;
+ if (table_share->primary_key == MAX_KEY)
+ no_fields++;
+ op->setValue(no_fields, part_func_value);
+ }
+ if (unlikely(m_slow_path))
+ {
+ /*
+ ignore TNTO_NO_LOGGING for slave thd. It is used to indicate
+ log-slave-updates option. This is instead handled in the
+ injector thread, by looking explicitly at the
+ opt_log_slave_updates flag.
+ */
+ Thd_ndb *thd_ndb= get_thd_ndb(thd);
+ if (thd->slave_thread)
+ op->setAnyValue(thd->server_id);
+ else if (thd_ndb->trans_options & TNTO_NO_LOGGING)
+ op->setAnyValue(NDB_ANYVALUE_FOR_NOLOGGING);
+ }
m_rows_changed++;
/*
@@ -2429,13 +2897,13 @@ int ha_ndbcluster::write_row(byte *record)
{
// Send rows to NDB
DBUG_PRINT("info", ("Sending inserts to NDB, "\
- "rows_inserted:%d, bulk_insert_rows: %d",
+ "rows_inserted: %d bulk_insert_rows: %d",
(int)m_rows_inserted, (int)m_bulk_insert_rows));
m_bulk_insert_not_flushed= FALSE;
if (m_transaction_on)
{
- if (execute_no_commit(this,trans,false) != 0)
+ if (execute_no_commit(this,trans,FALSE) != 0)
{
m_skip_auto_increment= TRUE;
no_uncommitted_rows_execute_failure();
@@ -2467,14 +2935,17 @@ int ha_ndbcluster::write_row(byte *record)
}
m_skip_auto_increment= TRUE;
+ DBUG_PRINT("exit",("ok"));
DBUG_RETURN(0);
}
-/* Compare if a key in a row has changed */
+/**
+ Compare if a key in a row has changed.
+*/
-int ha_ndbcluster::key_cmp(uint keynr, const byte * old_row,
- const byte * new_row)
+int ha_ndbcluster::key_cmp(uint keynr, const uchar * old_row,
+ const uchar * new_row)
{
KEY_PART_INFO *key_part=table->key_info[keynr].key_part;
KEY_PART_INFO *end=key_part+table->key_info[keynr].key_parts;
@@ -2490,8 +2961,8 @@ int ha_ndbcluster::key_cmp(uint keynr, const byte * old_row,
if (key_part->key_part_flag & (HA_BLOB_PART | HA_VAR_LENGTH_PART))
{
- if (key_part->field->cmp_binary((char*) (old_row + key_part->offset),
- (char*) (new_row + key_part->offset),
+ if (key_part->field->cmp_binary((old_row + key_part->offset),
+ (new_row + key_part->offset),
(ulong) key_part->length))
return 1;
}
@@ -2505,21 +2976,24 @@ int ha_ndbcluster::key_cmp(uint keynr, const byte * old_row,
return 0;
}
-/*
- Update one record in NDB using primary key
+/**
+ Update one record in NDB using primary key.
*/
-int ha_ndbcluster::update_row(const byte *old_data, byte *new_data)
+int ha_ndbcluster::update_row(const uchar *old_data, uchar *new_data)
{
- THD *thd= current_thd;
+ THD *thd= table->in_use;
NdbTransaction *trans= m_active_trans;
NdbScanOperation* cursor= m_active_cursor;
NdbOperation *op;
uint i;
- int auto_res;
- bool pk_update= (table->s->primary_key != MAX_KEY &&
- key_cmp(table->s->primary_key, old_data, new_data));
+ uint32 old_part_id= 0, new_part_id= 0;
+ int error;
+ longlong func_value;
+ bool pk_update= (table_share->primary_key != MAX_KEY &&
+ key_cmp(table_share->primary_key, old_data, new_data));
DBUG_ENTER("update_row");
+ m_write_op= TRUE;
/*
* If IGNORE the ignore constraint violations on primary and unique keys,
@@ -2539,25 +3013,37 @@ int ha_ndbcluster::update_row(const byte *old_data, byte *new_data)
DBUG_RETURN(peek_res);
}
- statistic_increment(thd->status_var.ha_update_count, &LOCK_status);
+ ha_statistic_increment(&SSV::ha_update_count);
if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_UPDATE)
{
table->timestamp_field->set_time();
- // Set query_id so that field is really updated
- table->timestamp_field->query_id= thd->query_id;
+ bitmap_set_bit(table->write_set, table->timestamp_field->field_index);
}
- /* Check for update of primary key for special handling */
- if (pk_update)
+ if (m_use_partition_function &&
+ (error= get_parts_for_update(old_data, new_data, table->record[0],
+ m_part_info, &old_part_id, &new_part_id,
+ &func_value)))
+ {
+ m_part_info->err_value= func_value;
+ DBUG_RETURN(error);
+ }
+
+ /*
+ * Check for update of primary key or partition change
+ * for special handling
+ */
+ if (pk_update || old_part_id != new_part_id)
{
int read_res, insert_res, delete_res, undo_res;
- DBUG_PRINT("info", ("primary key update, doing pk read+delete+insert"));
+ DBUG_PRINT("info", ("primary key update or partition change, "
+ "doing read+delete+insert"));
// Get all old fields, since we optimize away fields not in query
- read_res= complemented_pk_read(old_data, new_data);
+ read_res= complemented_read(old_data, new_data, old_part_id);
if (read_res)
{
- DBUG_PRINT("info", ("pk read failed"));
+ DBUG_PRINT("info", ("read failed"));
DBUG_RETURN(read_res);
}
// Delete old row
@@ -2577,10 +3063,11 @@ int ha_ndbcluster::update_row(const byte *old_data, byte *new_data)
then we need to update the auto_increment counter
*/
if (table->found_next_number_field &&
- table->found_next_number_field->query_id == thd->query_id &&
- (auto_res= set_auto_inc(table->found_next_number_field)))
+ bitmap_is_set(table->write_set,
+ table->found_next_number_field->field_index) &&
+ (error= set_auto_inc(table->found_next_number_field)))
{
- DBUG_RETURN(auto_res);
+ DBUG_RETURN(error);
}
insert_res= write_row(new_data);
m_primary_key_update= FALSE;
@@ -2591,7 +3078,7 @@ int ha_ndbcluster::update_row(const byte *old_data, byte *new_data)
{
// Undo delete_row(old_data)
m_primary_key_update= TRUE;
- undo_res= write_row((byte *)old_data);
+ undo_res= write_row((uchar *)old_data);
if (undo_res)
push_warning(current_thd,
MYSQL_ERROR::WARN_LEVEL_WARN,
@@ -2609,10 +3096,11 @@ int ha_ndbcluster::update_row(const byte *old_data, byte *new_data)
then we need to update the auto_increment counter
*/
if (table->found_next_number_field &&
- table->found_next_number_field->query_id == thd->query_id &&
- (auto_res= set_auto_inc(table->found_next_number_field)))
+ bitmap_is_set(table->write_set,
+ table->found_next_number_field->field_index) &&
+ (error= set_auto_inc(table->found_next_number_field)))
{
- DBUG_RETURN(auto_res);
+ DBUG_RETURN(error);
}
if (cursor)
{
@@ -2626,25 +3114,29 @@ int ha_ndbcluster::update_row(const byte *old_data, byte *new_data)
DBUG_PRINT("info", ("Calling updateTuple on cursor"));
if (!(op= cursor->updateCurrentTuple()))
ERR_RETURN(trans->getNdbError());
- m_lock_tuple= false;
+ m_lock_tuple= FALSE;
m_ops_pending++;
- if (uses_blob_value(FALSE))
+ if (uses_blob_value())
m_blobs_pending= TRUE;
+ if (m_use_partition_function)
+ cursor->setPartitionId(new_part_id);
}
else
{
- if (!(op= trans->getNdbOperation((const NDBTAB *) m_table)) ||
+ if (!(op= trans->getNdbOperation(m_table)) ||
op->updateTuple() != 0)
ERR_RETURN(trans->getNdbError());
- if (table->s->primary_key == MAX_KEY)
+ if (m_use_partition_function)
+ op->setPartitionId(new_part_id);
+ if (table_share->primary_key == MAX_KEY)
{
// This table has no primary key, use "hidden" primary key
DBUG_PRINT("info", ("Using hidden key"));
// Require that the PK for this record has previously been
// read into m_ref
- DBUG_DUMP("key", (uchar *)m_ref, NDB_HIDDEN_PRIMARY_KEY_LENGTH);
+ DBUG_DUMP("key", m_ref, NDB_HIDDEN_PRIMARY_KEY_LENGTH);
if (set_hidden_key(op, table->s->fields, m_ref))
ERR_RETURN(op->getNdbError());
@@ -2660,15 +3152,45 @@ int ha_ndbcluster::update_row(const byte *old_data, byte *new_data)
m_rows_changed++;
// Set non-key attribute(s)
- for (i= 0; i < table->s->fields; i++)
+ my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->read_set);
+ for (i= 0; i < table_share->fields; i++)
{
Field *field= table->field[i];
- if (((thd->query_id == field->query_id) || m_retrieve_all_fields) &&
+ if (bitmap_is_set(table->write_set, i) &&
(!(field->flags & PRI_KEY_FLAG)) &&
- set_ndb_value(op, field, i))
+ set_ndb_value(op, field, i, new_data - table->record[0]))
+ {
+ dbug_tmp_restore_column_map(table->read_set, old_map);
ERR_RETURN(op->getNdbError());
+ }
}
+ dbug_tmp_restore_column_map(table->read_set, old_map);
+ if (m_use_partition_function)
+ {
+ if (func_value >= INT_MAX32)
+ func_value= INT_MAX32;
+ uint32 part_func_value= (uint32)func_value;
+ uint no_fields= table_share->fields;
+ if (table_share->primary_key == MAX_KEY)
+ no_fields++;
+ op->setValue(no_fields, part_func_value);
+ }
+
+ if (unlikely(m_slow_path))
+ {
+ /*
+ ignore TNTO_NO_LOGGING for slave thd. It is used to indicate
+ log-slave-updates option. This is instead handled in the
+ injector thread, by looking explicitly at the
+ opt_log_slave_updates flag.
+ */
+ Thd_ndb *thd_ndb= get_thd_ndb(thd);
+ if (thd->slave_thread)
+ op->setAnyValue(thd->server_id);
+ else if (thd_ndb->trans_options & TNTO_NO_LOGGING)
+ op->setAnyValue(NDB_ANYVALUE_FOR_NOLOGGING);
+ }
/*
Execute update operation if we are not doing a scan for update
and there exist UPDATE AFTER triggers
@@ -2684,21 +3206,31 @@ int ha_ndbcluster::update_row(const byte *old_data, byte *new_data)
}
-/*
- Delete one record from NDB, using primary key
+/**
+ Delete one record from NDB, using primary key .
*/
-int ha_ndbcluster::delete_row(const byte *record)
+int ha_ndbcluster::delete_row(const uchar *record)
{
- THD *thd= current_thd;
+ THD *thd= table->in_use;
NdbTransaction *trans= m_active_trans;
NdbScanOperation* cursor= m_active_cursor;
NdbOperation *op;
+ uint32 part_id;
+ int error;
DBUG_ENTER("delete_row");
+ m_write_op= TRUE;
- statistic_increment(thd->status_var.ha_delete_count,&LOCK_status);
+ ha_statistic_increment(&SSV::ha_delete_count);
m_rows_changed++;
+ if (m_use_partition_function &&
+ (error= get_part_for_delete(record, table->record[0], m_part_info,
+ &part_id)))
+ {
+ DBUG_RETURN(error);
+ }
+
if (cursor)
{
/*
@@ -2711,11 +3243,30 @@ int ha_ndbcluster::delete_row(const byte *record)
DBUG_PRINT("info", ("Calling deleteTuple on cursor"));
if (cursor->deleteCurrentTuple() != 0)
ERR_RETURN(trans->getNdbError());
- m_lock_tuple= false;
+ m_lock_tuple= FALSE;
m_ops_pending++;
+ if (m_use_partition_function)
+ cursor->setPartitionId(part_id);
+
no_uncommitted_rows_update(-1);
+ if (unlikely(m_slow_path))
+ {
+ /*
+ ignore TNTO_NO_LOGGING for slave thd. It is used to indicate
+ log-slave-updates option. This is instead handled in the
+ injector thread, by looking explicitly at the
+ opt_log_slave_updates flag.
+ */
+ Thd_ndb *thd_ndb= get_thd_ndb(thd);
+ if (thd->slave_thread)
+ ((NdbOperation *)trans->getLastDefinedOperation())->
+ setAnyValue(thd->server_id);
+ else if (thd_ndb->trans_options & TNTO_NO_LOGGING)
+ ((NdbOperation *)trans->getLastDefinedOperation())->
+ setAnyValue(NDB_ANYVALUE_FOR_NOLOGGING);
+ }
if (!(m_primary_key_update || m_delete_cannot_batch))
// If deleting from cursor, NoCommit will be handled in next_result
DBUG_RETURN(0);
@@ -2723,13 +3274,16 @@ int ha_ndbcluster::delete_row(const byte *record)
else
{
- if (!(op=trans->getNdbOperation((const NDBTAB *) m_table)) ||
+ if (!(op=trans->getNdbOperation(m_table)) ||
op->deleteTuple() != 0)
ERR_RETURN(trans->getNdbError());
+ if (m_use_partition_function)
+ op->setPartitionId(part_id);
+
no_uncommitted_rows_update(-1);
- if (table->s->primary_key == MAX_KEY)
+ if (table_share->primary_key == MAX_KEY)
{
// This table has no primary key, use "hidden" primary key
DBUG_PRINT("info", ("Using hidden key"));
@@ -2739,65 +3293,107 @@ int ha_ndbcluster::delete_row(const byte *record)
}
else
{
- int res;
- if ((res= set_primary_key_from_record(op, record)))
- return res;
+ if ((error= set_primary_key_from_record(op, record)))
+ DBUG_RETURN(error);
+ }
+
+ if (unlikely(m_slow_path))
+ {
+ /*
+ ignore TNTO_NO_LOGGING for slave thd. It is used to indicate
+ log-slave-updates option. This is instead handled in the
+ injector thread, by looking explicitly at the
+ opt_log_slave_updates flag.
+ */
+ Thd_ndb *thd_ndb= get_thd_ndb(thd);
+ if (thd->slave_thread)
+ op->setAnyValue(thd->server_id);
+ else if (thd_ndb->trans_options & TNTO_NO_LOGGING)
+ op->setAnyValue(NDB_ANYVALUE_FOR_NOLOGGING);
}
}
// Execute delete operation
- if (execute_no_commit(this,trans,false) != 0) {
+ if (execute_no_commit(this,trans,FALSE) != 0) {
no_uncommitted_rows_execute_failure();
DBUG_RETURN(ndb_err(trans));
}
DBUG_RETURN(0);
}
-/*
- Unpack a record read from NDB
+/**
+ Unpack a record read from NDB.
- SYNOPSIS
- unpack_record()
- buf Buffer to store read row
+ @param buf Buffer to store read row
- NOTE
+ @note
The data for each row is read directly into the
destination buffer. This function is primarily
called in order to check if any fields should be
set to null.
*/
-void ha_ndbcluster::unpack_record(byte* buf)
+void ndb_unpack_record(TABLE *table, NdbValue *value,
+ MY_BITMAP *defined, uchar *buf)
{
- uint row_offset= (uint) (buf - table->record[0]);
- Field **field, **end;
- NdbValue *value= m_value;
- DBUG_ENTER("unpack_record");
+ Field **p_field= table->field, *field= *p_field;
+ my_ptrdiff_t row_offset= (my_ptrdiff_t) (buf - table->record[0]);
+ my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->write_set);
+ DBUG_ENTER("ndb_unpack_record");
- end= table->field + table->s->fields;
-
- // Set null flag(s)
- bzero(buf, table->s->null_bytes);
- for (field= table->field;
- field < end;
- field++, value++)
+ /*
+ Set the filler bits of the null byte, since they are
+ not touched in the code below.
+
+ The filler bits are the MSBs in the last null byte
+ */
+ if (table->s->null_bytes > 0)
+ buf[table->s->null_bytes - 1]|= 256U - (1U <<
+ table->s->last_null_bit_pos);
+ /*
+ Set null flag(s)
+ */
+ for ( ; field;
+ p_field++, value++, field= *p_field)
{
+ field->set_notnull(row_offset);
if ((*value).ptr)
{
- if (! ((*field)->flags & BLOB_FLAG))
+ if (!(field->flags & BLOB_FLAG))
{
- if ((*value).rec->isNULL())
- (*field)->set_null(row_offset);
- else if ((*field)->type() == MYSQL_TYPE_BIT)
+ int is_null= (*value).rec->isNULL();
+ if (is_null)
{
- uint pack_len= (*field)->pack_length();
- if (pack_len < 5)
+ if (is_null > 0)
+ {
+ DBUG_PRINT("info",("[%u] NULL",
+ (*value).rec->getColumn()->getColumnNo()));
+ field->set_null(row_offset);
+ }
+ else
+ {
+ DBUG_PRINT("info",("[%u] UNDEFINED",
+ (*value).rec->getColumn()->getColumnNo()));
+ bitmap_clear_bit(defined,
+ (*value).rec->getColumn()->getColumnNo());
+ }
+ }
+ else if (field->type() == MYSQL_TYPE_BIT)
+ {
+ Field_bit *field_bit= static_cast<Field_bit*>(field);
+
+ /*
+ Move internal field pointer to point to 'buf'. Calling
+ the correct member function directly since we know the
+ type of the object.
+ */
+ field_bit->Field_bit::move_field_offset(row_offset);
+ if (field->pack_length() < 5)
{
DBUG_PRINT("info", ("bit field H'%.8X",
(*value).rec->u_32_value()));
- ((Field_bit *) *field)->store((longlong)
- (*value).rec->u_32_value(),
- FALSE);
+ field_bit->Field_bit::store((longlong) (*value).rec->u_32_value(),
+ FALSE);
}
else
{
@@ -2807,57 +3403,96 @@ void ha_ndbcluster::unpack_record(byte* buf)
#ifdef WORDS_BIGENDIAN
/* lsw is stored first */
Uint32 *buf= (Uint32 *)(*value).rec->aRef();
- ((Field_bit *) *field)->store((((longlong)*buf)
- & 0x000000000FFFFFFFFLL)
- |
- ((((longlong)*(buf+1)) << 32)
- & 0xFFFFFFFF00000000LL),
- TRUE);
+ field_bit->Field_bit::store((((longlong)*buf)
+ & 0x000000000FFFFFFFFLL)
+ |
+ ((((longlong)*(buf+1)) << 32)
+ & 0xFFFFFFFF00000000LL),
+ TRUE);
#else
- ((Field_bit *) *field)->store((longlong)
- (*value).rec->u_64_value(), TRUE);
+ field_bit->Field_bit::store((longlong)
+ (*value).rec->u_64_value(), TRUE);
#endif
}
+ /*
+ Move back internal field pointer to point to original
+ value (usually record[0]).
+ */
+ field_bit->Field_bit::move_field_offset(-row_offset);
+ DBUG_PRINT("info",("[%u] SET",
+ (*value).rec->getColumn()->getColumnNo()));
+ DBUG_DUMP("info", field->ptr, field->pack_length());
+ }
+ else
+ {
+ DBUG_PRINT("info",("[%u] SET",
+ (*value).rec->getColumn()->getColumnNo()));
+ DBUG_DUMP("info", field->ptr, field->pack_length());
}
}
else
{
- NdbBlob* ndb_blob= (*value).blob;
- bool isNull= TRUE;
+ NdbBlob *ndb_blob= (*value).blob;
+ uint col_no = ndb_blob->getColumn()->getColumnNo();
+ int isNull;
+ ndb_blob->getDefined(isNull);
+ if (isNull == 1)
+ {
+ DBUG_PRINT("info",("[%u] NULL", col_no));
+ field->set_null(row_offset);
+ }
+ else if (isNull == -1)
+ {
+ DBUG_PRINT("info",("[%u] UNDEFINED", col_no));
+ bitmap_clear_bit(defined, col_no);
+ }
+ else
+ {
#ifndef DBUG_OFF
- int ret=
+ // pointer vas set in get_ndb_blobs_value
+ Field_blob *field_blob= (Field_blob*)field;
+ uchar *ptr;
+ field_blob->get_ptr(&ptr, row_offset);
+ uint32 len= field_blob->get_length(row_offset);
+ DBUG_PRINT("info",("[%u] SET ptr: 0x%lx len: %u",
+ col_no, (long) ptr, len));
#endif
- ndb_blob->getNull(isNull);
- DBUG_ASSERT(ret == 0);
- if (isNull)
- (*field)->set_null(row_offset);
+ }
}
}
}
-
+ dbug_tmp_restore_column_map(table->write_set, old_map);
+ DBUG_VOID_RETURN;
+}
+
+void ha_ndbcluster::unpack_record(uchar *buf)
+{
+ ndb_unpack_record(table, m_value, 0, buf);
#ifndef DBUG_OFF
// Read and print all values that was fetched
- if (table->s->primary_key == MAX_KEY)
+ if (table_share->primary_key == MAX_KEY)
{
// Table with hidden primary key
- int hidden_no= table->s->fields;
+ int hidden_no= table_share->fields;
+ const NDBTAB *tab= m_table;
char buff[22];
- const NDBTAB *tab= (const NDBTAB *) m_table;
const NDBCOL *hidden_col= tab->getColumn(hidden_no);
const NdbRecAttr* rec= m_value[hidden_no].rec;
DBUG_ASSERT(rec);
- DBUG_PRINT("hidden", ("%d: %s \"%s\"", hidden_no,
+ DBUG_PRINT("hidden", ("%d: %s \"%s\"", hidden_no,
hidden_col->getName(),
llstr(rec->u_64_value(), buff)));
}
- //print_results();
+ //DBUG_EXECUTE("value", print_results(););
#endif
- DBUG_VOID_RETURN;
}
-/*
- Utility function to print/dump the fetched field
- */
+/**
+ Utility function to print/dump the fetched field.
+
+ To avoid unnecessary work, wrap in DBUG_EXECUTE as in:
+ DBUG_EXECUTE("value", print_results(););
+*/
void ha_ndbcluster::print_results()
{
@@ -2868,7 +3503,7 @@ void ha_ndbcluster::print_results()
char buf_type[MAX_FIELD_WIDTH], buf_val[MAX_FIELD_WIDTH];
String type(buf_type, sizeof(buf_type), &my_charset_bin);
String val(buf_val, sizeof(buf_val), &my_charset_bin);
- for (uint f= 0; f < table->s->fields; f++)
+ for (uint f= 0; f < table_share->fields; f++)
{
/* Use DBUG_PRINT since DBUG_FILE cannot be filtered out */
char buf[2000];
@@ -2916,17 +3551,19 @@ print_value:
}
-int ha_ndbcluster::index_init(uint index)
+int ha_ndbcluster::index_init(uint index, bool sorted)
{
DBUG_ENTER("ha_ndbcluster::index_init");
- DBUG_PRINT("enter", ("index: %u", index));
- /*
+ DBUG_PRINT("enter", ("index: %u sorted: %d", index, sorted));
+ active_index= index;
+ m_sorted= sorted;
+ /*
Locks are are explicitly released in scan
unless m_lock.type == TL_READ_HIGH_PRIORITY
and no sub-sequent call to unlock_row()
- */
- m_lock_tuple= false;
- DBUG_RETURN(handler::index_init(index));
+ */
+ m_lock_tuple= FALSE;
+ DBUG_RETURN(0);
}
@@ -2937,17 +3574,16 @@ int ha_ndbcluster::index_end()
}
/**
- * Check if key contains nullable columns
- */
+ Check if key contains null.
+*/
static
int
-check_null_in_key(const KEY* key_info, const byte *key, uint key_len)
+check_null_in_key(const KEY* key_info, const uchar *key, uint key_len)
{
KEY_PART_INFO *curr_part, *end_part;
- const byte* end_ptr= key + key_len;
+ const uchar* end_ptr= key + key_len;
curr_part= key_info->key_part;
end_part= curr_part + key_info->key_parts;
-
for (; curr_part != end_part && key < end_ptr; curr_part++)
{
@@ -2959,59 +3595,20 @@ check_null_in_key(const KEY* key_info, const byte *key, uint key_len)
return 0;
}
-int ha_ndbcluster::index_read(byte *buf,
- const byte *key, uint key_len,
+int ha_ndbcluster::index_read(uchar *buf,
+ const uchar *key, uint key_len,
enum ha_rkey_function find_flag)
{
+ key_range start_key;
+ bool descending= FALSE;
DBUG_ENTER("ha_ndbcluster::index_read");
DBUG_PRINT("enter", ("active_index: %u, key_len: %u, find_flag: %d",
active_index, key_len, find_flag));
- int error;
- ndb_index_type type= get_index_type(active_index);
- const KEY* key_info= table->key_info+active_index;
- switch (type){
- case PRIMARY_KEY_ORDERED_INDEX:
- case PRIMARY_KEY_INDEX:
- if (find_flag == HA_READ_KEY_EXACT && key_info->key_length == key_len)
- {
- if (m_active_cursor && (error= close_scan()))
- DBUG_RETURN(error);
- DBUG_RETURN(pk_read(key, key_len, buf));
- }
- else if (type == PRIMARY_KEY_INDEX)
- {
- DBUG_RETURN(1);
- }
- break;
- case UNIQUE_ORDERED_INDEX:
- case UNIQUE_INDEX:
- if (find_flag == HA_READ_KEY_EXACT && key_info->key_length == key_len &&
- !check_null_in_key(key_info, key, key_len))
- {
- if (m_active_cursor && (error= close_scan()))
- DBUG_RETURN(error);
- DBUG_RETURN(unique_index_read(key, key_len, buf));
- }
- else if (type == UNIQUE_INDEX)
- {
- DBUG_RETURN(unique_index_scan(key_info, key, key_len, buf));
- }
- break;
- case ORDERED_INDEX:
- break;
- default:
- case UNDEFINED_INDEX:
- DBUG_ASSERT(FALSE);
- DBUG_RETURN(1);
- break;
- }
-
- key_range start_key;
start_key.key= key;
start_key.length= key_len;
start_key.flag= find_flag;
- bool descending= FALSE;
+ descending= FALSE;
switch (find_flag) {
case HA_READ_KEY_OR_PREV:
case HA_READ_BEFORE_KEY:
@@ -3022,101 +3619,113 @@ int ha_ndbcluster::index_read(byte *buf,
default:
break;
}
- error= ordered_index_scan(&start_key, 0, TRUE, descending, buf);
- DBUG_RETURN(error == HA_ERR_END_OF_FILE ? HA_ERR_KEY_NOT_FOUND : error);
+ DBUG_RETURN(read_range_first_to_buf(&start_key, 0, descending,
+ m_sorted, buf));
}
-int ha_ndbcluster::index_read_idx(byte *buf, uint index_no,
- const byte *key, uint key_len,
- enum ha_rkey_function find_flag)
-{
- statistic_increment(current_thd->status_var.ha_read_key_count, &LOCK_status);
- DBUG_ENTER("ha_ndbcluster::index_read_idx");
- DBUG_PRINT("enter", ("index_no: %u, key_len: %u", index_no, key_len));
- index_init(index_no);
- DBUG_RETURN(index_read(buf, key, key_len, find_flag));
-}
-
-
-int ha_ndbcluster::index_next(byte *buf)
+int ha_ndbcluster::index_next(uchar *buf)
{
DBUG_ENTER("ha_ndbcluster::index_next");
- statistic_increment(current_thd->status_var.ha_read_next_count,
- &LOCK_status);
+ ha_statistic_increment(&SSV::ha_read_next_count);
DBUG_RETURN(next_result(buf));
}
-int ha_ndbcluster::index_prev(byte *buf)
+int ha_ndbcluster::index_prev(uchar *buf)
{
DBUG_ENTER("ha_ndbcluster::index_prev");
- statistic_increment(current_thd->status_var.ha_read_prev_count,
- &LOCK_status);
+ ha_statistic_increment(&SSV::ha_read_prev_count);
DBUG_RETURN(next_result(buf));
}
-int ha_ndbcluster::index_first(byte *buf)
+int ha_ndbcluster::index_first(uchar *buf)
{
DBUG_ENTER("ha_ndbcluster::index_first");
- statistic_increment(current_thd->status_var.ha_read_first_count,
- &LOCK_status);
+ 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));
+ DBUG_RETURN(ordered_index_scan(0, 0, TRUE, FALSE, buf, NULL));
}
-int ha_ndbcluster::index_last(byte *buf)
+int ha_ndbcluster::index_last(uchar *buf)
{
DBUG_ENTER("ha_ndbcluster::index_last");
- statistic_increment(current_thd->status_var.ha_read_last_count,&LOCK_status);
- DBUG_RETURN(ordered_index_scan(0, 0, TRUE, TRUE, buf));
+ ha_statistic_increment(&SSV::ha_read_last_count);
+ DBUG_RETURN(ordered_index_scan(0, 0, TRUE, TRUE, buf, NULL));
}
-int ha_ndbcluster::index_read_last(byte * buf, const byte * key, uint key_len)
+int ha_ndbcluster::index_read_last(uchar * buf, const uchar * key, uint key_len)
{
DBUG_ENTER("ha_ndbcluster::index_read_last");
DBUG_RETURN(index_read(buf, key, key_len, HA_READ_PREFIX_LAST));
}
-inline
int ha_ndbcluster::read_range_first_to_buf(const key_range *start_key,
const key_range *end_key,
- bool eq_r, bool sorted,
- byte* buf)
+ bool desc, bool sorted,
+ uchar* buf)
{
- ndb_index_type type= get_index_type(active_index);
-KEY* key_info;
- int error= 1;
+ part_id_range part_spec;
+ ndb_index_type type= get_index_type(active_index);
+ const KEY* key_info= table->key_info+active_index;
+ int error;
DBUG_ENTER("ha_ndbcluster::read_range_first_to_buf");
- DBUG_PRINT("info", ("eq_r: %d, sorted: %d", eq_r, sorted));
+ DBUG_PRINT("info", ("desc: %d, sorted: %d", desc, sorted));
+
+ if (m_use_partition_function)
+ {
+ get_partition_set(table, buf, active_index, start_key, &part_spec);
+ DBUG_PRINT("info", ("part_spec.start_part: %u part_spec.end_part: %u",
+ part_spec.start_part, part_spec.end_part));
+ /*
+ If partition pruning has found no partition in set
+ we can return HA_ERR_END_OF_FILE
+ If partition pruning has found exactly one partition in set
+ we can optimize scan to run towards that partition only.
+ */
+ if (part_spec.start_part > part_spec.end_part)
+ {
+ DBUG_RETURN(HA_ERR_END_OF_FILE);
+ }
+ else if (part_spec.start_part == part_spec.end_part)
+ {
+ /*
+ Only one partition is required to scan, if sorted is required we
+ don't need it any more since output from one ordered partitioned
+ index is always sorted.
+ */
+ sorted= FALSE;
+ }
+ }
+ m_write_op= FALSE;
switch (type){
case PRIMARY_KEY_ORDERED_INDEX:
case PRIMARY_KEY_INDEX:
- key_info= table->key_info + active_index;
if (start_key &&
start_key->length == key_info->key_length &&
start_key->flag == HA_READ_KEY_EXACT)
{
if (m_active_cursor && (error= close_scan()))
DBUG_RETURN(error);
- error= pk_read(start_key->key, start_key->length, buf);
+ error= pk_read(start_key->key, start_key->length, buf,
+ part_spec.start_part);
DBUG_RETURN(error == HA_ERR_KEY_NOT_FOUND ? HA_ERR_END_OF_FILE : error);
}
break;
case UNIQUE_ORDERED_INDEX:
case UNIQUE_INDEX:
- key_info= table->key_info + active_index;
if (start_key && start_key->length == key_info->key_length &&
start_key->flag == HA_READ_KEY_EXACT &&
!check_null_in_key(key_info, start_key->key, start_key->length))
{
if (m_active_cursor && (error= close_scan()))
DBUG_RETURN(error);
+
error= unique_index_read(start_key->key, start_key->length, buf);
DBUG_RETURN(error == HA_ERR_KEY_NOT_FOUND ? HA_ERR_END_OF_FILE : error);
}
@@ -3129,25 +3738,19 @@ KEY* key_info;
default:
break;
}
-
// Start the ordered index scan and fetch the first row
- error= ordered_index_scan(start_key, end_key, sorted, FALSE, buf);
- DBUG_RETURN(error);
+ DBUG_RETURN(ordered_index_scan(start_key, end_key, sorted, desc, buf,
+ &part_spec));
}
-
int ha_ndbcluster::read_range_first(const key_range *start_key,
const key_range *end_key,
bool eq_r, bool sorted)
{
- byte* buf= table->record[0];
+ uchar* buf= table->record[0];
DBUG_ENTER("ha_ndbcluster::read_range_first");
-
- DBUG_RETURN(read_range_first_to_buf(start_key,
- end_key,
- eq_r,
- sorted,
- buf));
+ DBUG_RETURN(read_range_first_to_buf(start_key, end_key, FALSE,
+ sorted, buf));
}
int ha_ndbcluster::read_range_next()
@@ -3173,7 +3776,7 @@ int ha_ndbcluster::rnd_init(bool scan)
DBUG_RETURN(-1);
}
}
- index_init(table->s->primary_key);
+ index_init(table_share->primary_key, 0);
DBUG_RETURN(0);
}
@@ -3184,10 +3787,10 @@ int ha_ndbcluster::close_scan()
m_multi_cursor= 0;
if (!m_active_cursor && !m_multi_cursor)
- DBUG_RETURN(1);
+ DBUG_RETURN(0);
NdbScanOperation *cursor= m_active_cursor ? m_active_cursor : m_multi_cursor;
-
+
if (m_lock_tuple)
{
/*
@@ -3202,12 +3805,12 @@ int ha_ndbcluster::close_scan()
if (!(op= cursor->lockCurrentTuple()))
{
- m_lock_tuple= false;
+ m_lock_tuple= FALSE;
ERR_RETURN(trans->getNdbError());
}
m_ops_pending++;
}
- m_lock_tuple= false;
+ m_lock_tuple= FALSE;
if (m_ops_pending)
{
/*
@@ -3215,7 +3818,7 @@ int ha_ndbcluster::close_scan()
deleteing/updating transaction before closing the scan
*/
DBUG_PRINT("info", ("ops_pending: %ld", (long) m_ops_pending));
- if (execute_no_commit(this,trans,false) != 0) {
+ if (execute_no_commit(this,trans,FALSE) != 0) {
no_uncommitted_rows_execute_failure();
DBUG_RETURN(ndb_err(trans));
}
@@ -3234,11 +3837,10 @@ int ha_ndbcluster::rnd_end()
}
-int ha_ndbcluster::rnd_next(byte *buf)
+int ha_ndbcluster::rnd_next(uchar *buf)
{
DBUG_ENTER("rnd_next");
- statistic_increment(current_thd->status_var.ha_read_rnd_next_count,
- &LOCK_status);
+ ha_statistic_increment(&SSV::ha_read_rnd_next_count);
if (!m_active_cursor)
DBUG_RETURN(full_table_scan(buf));
@@ -3246,41 +3848,72 @@ int ha_ndbcluster::rnd_next(byte *buf)
}
-/*
+/**
An "interesting" record has been found and it's pk
- retrieved by calling position
- Now it's time to read the record from db once
- again
+ retrieved by calling position. Now it's time to read
+ the record from db once again.
*/
-int ha_ndbcluster::rnd_pos(byte *buf, byte *pos)
+int ha_ndbcluster::rnd_pos(uchar *buf, uchar *pos)
{
DBUG_ENTER("rnd_pos");
- statistic_increment(current_thd->status_var.ha_read_rnd_count,
- &LOCK_status);
+ 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"
- DBUG_RETURN(pk_read(pos, ref_length, buf));
+ {
+ part_id_range part_spec;
+ uint key_length= ref_length;
+ if (m_use_partition_function)
+ {
+ if (table_share->primary_key == MAX_KEY)
+ {
+ /*
+ The partition id has been fetched from ndb
+ and has been stored directly after the hidden key
+ */
+ DBUG_DUMP("key+part", pos, key_length);
+ key_length= ref_length - sizeof(m_part_id);
+ part_spec.start_part= part_spec.end_part= *(uint32 *)(pos + key_length);
+ }
+ else
+ {
+ key_range key_spec;
+ KEY *key_info= table->key_info + table_share->primary_key;
+ key_spec.key= pos;
+ key_spec.length= key_length;
+ key_spec.flag= HA_READ_KEY_EXACT;
+ get_full_part_id_from_key(table, buf, key_info,
+ &key_spec, &part_spec);
+ DBUG_ASSERT(part_spec.start_part == part_spec.end_part);
+ }
+ 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));
+ }
}
-/*
+/**
Store the primary key of this record in ref
variable, so that the row can be retrieved again later
- using "reference" in rnd_pos
+ using "reference" in rnd_pos.
*/
-void ha_ndbcluster::position(const byte *record)
+void ha_ndbcluster::position(const uchar *record)
{
KEY *key_info;
KEY_PART_INFO *key_part;
KEY_PART_INFO *end;
- byte *buff;
+ uchar *buff;
+ uint key_length;
+
DBUG_ENTER("position");
- if (table->s->primary_key != MAX_KEY)
+ if (table_share->primary_key != MAX_KEY)
{
- key_info= table->key_info + table->s->primary_key;
+ key_length= ref_length;
+ key_info= table->key_info + table_share->primary_key;
key_part= key_info->key_part;
end= key_part + key_info->key_parts;
buff= ref;
@@ -3298,7 +3931,7 @@ void ha_ndbcluster::position(const byte *record)
}
size_t len = key_part->length;
- const byte * ptr = record + key_part->offset;
+ const uchar * ptr = record + key_part->offset;
Field *field = key_part->field;
if (field->type() == MYSQL_TYPE_VARCHAR)
{
@@ -3328,18 +3961,30 @@ void ha_ndbcluster::position(const byte *record)
{
// No primary key, get hidden key
DBUG_PRINT("info", ("Getting hidden key"));
+ // If table has user defined partition save the partition id as well
+ if(m_use_partition_function)
+ {
+ DBUG_PRINT("info", ("Saving partition id %u", m_part_id));
+ key_length= ref_length - sizeof(m_part_id);
+ memcpy(ref+key_length, (void *)&m_part_id, sizeof(m_part_id));
+ }
+ else
+ key_length= ref_length;
#ifndef DBUG_OFF
int hidden_no= table->s->fields;
- const NDBTAB *tab= (const NDBTAB *) m_table;
+ const NDBTAB *tab= m_table;
const NDBCOL *hidden_col= tab->getColumn(hidden_no);
DBUG_ASSERT(hidden_col->getPrimaryKey() &&
hidden_col->getAutoIncrement() &&
- ref_length == NDB_HIDDEN_PRIMARY_KEY_LENGTH);
+ key_length == NDB_HIDDEN_PRIMARY_KEY_LENGTH);
#endif
- memcpy(ref, m_ref, ref_length);
+ memcpy(ref, m_ref, key_length);
}
-
- DBUG_DUMP("ref", (uchar*)ref, ref_length);
+#ifndef DBUG_OFF
+ if (table_share->primary_key == MAX_KEY && m_use_partition_function)
+ DBUG_DUMP("key+part", ref, key_length+sizeof(m_part_id));
+#endif
+ DBUG_DUMP("ref", ref, key_length);
DBUG_VOID_RETURN;
}
@@ -3362,7 +4007,7 @@ int ha_ndbcluster::info(uint flag)
if (m_table_info)
{
if (m_ha_not_exact_count)
- records= 100;
+ stats.records= 100;
else
result= records_update();
}
@@ -3371,23 +4016,24 @@ int ha_ndbcluster::info(uint flag)
if ((my_errno= check_ndb_connection()))
DBUG_RETURN(my_errno);
Ndb *ndb= get_ndb();
+ ndb->setDatabaseName(m_dbname);
struct Ndb_statistics stat;
if (ndb->setDatabaseName(m_dbname))
{
DBUG_RETURN(my_errno= HA_ERR_OUT_OF_MEM);
}
if (current_thd->variables.ndb_use_exact_count &&
- (result= ndb_get_table_statistics(this, true, ndb, m_tabname, &stat))
+ (result= ndb_get_table_statistics(this, TRUE, ndb, m_table, &stat))
== 0)
{
- mean_rec_length= stat.row_size;
- data_file_length= stat.fragment_memory;
- records= stat.row_count;
+ stats.mean_rec_length= stat.row_size;
+ stats.data_file_length= stat.fragment_memory;
+ stats.records= stat.row_count;
}
else
{
- mean_rec_length= 0;
- records= 100;
+ stats.mean_rec_length= 0;
+ stats.records= 100;
}
}
}
@@ -3409,18 +4055,19 @@ int ha_ndbcluster::info(uint flag)
if ((my_errno= check_ndb_connection()))
DBUG_RETURN(my_errno);
Ndb *ndb= get_ndb();
+ Ndb_tuple_id_range_guard g(m_share);
Uint64 auto_increment_value64;
- if (ndb->readAutoIncrementValue((const NDBTAB *) m_table,
+ if (ndb->readAutoIncrementValue(m_table, g.range,
auto_increment_value64) == -1)
{
const NdbError err= ndb->getNdbError();
sql_print_error("Error %lu in readAutoIncrementValue(): %s",
(ulong) err.code, err.message);
- auto_increment_value= ~(Uint64)0;
+ stats.auto_increment_value= ~(ulonglong)0;
}
else
- auto_increment_value= (ulonglong)auto_increment_value64;
+ stats.auto_increment_value= (ulonglong)auto_increment_value64;
}
}
@@ -3431,86 +4078,23 @@ int ha_ndbcluster::info(uint flag)
}
+void ha_ndbcluster::get_dynamic_partition_info(PARTITION_INFO *stat_info,
+ uint part_id)
+{
+ /*
+ This functions should be fixed. Suggested fix: to
+ implement ndb function which retrives the statistics
+ about ndb partitions.
+ */
+ bzero((char*) stat_info, sizeof(PARTITION_INFO));
+ return;
+}
+
+
int ha_ndbcluster::extra(enum ha_extra_function operation)
{
DBUG_ENTER("extra");
switch (operation) {
- case HA_EXTRA_NORMAL: /* Optimize for space (def) */
- DBUG_PRINT("info", ("HA_EXTRA_NORMAL"));
- break;
- case HA_EXTRA_QUICK: /* Optimize for speed */
- DBUG_PRINT("info", ("HA_EXTRA_QUICK"));
- break;
- case HA_EXTRA_RESET: /* Reset database to after open */
- DBUG_PRINT("info", ("HA_EXTRA_RESET"));
- reset();
- break;
- case HA_EXTRA_CACHE: /* Cash record in HA_rrnd() */
- DBUG_PRINT("info", ("HA_EXTRA_CACHE"));
- break;
- case HA_EXTRA_NO_CACHE: /* End cacheing of records (def) */
- DBUG_PRINT("info", ("HA_EXTRA_NO_CACHE"));
- break;
- case HA_EXTRA_NO_READCHECK: /* No readcheck on update */
- DBUG_PRINT("info", ("HA_EXTRA_NO_READCHECK"));
- break;
- case HA_EXTRA_READCHECK: /* Use readcheck (def) */
- DBUG_PRINT("info", ("HA_EXTRA_READCHECK"));
- break;
- case HA_EXTRA_KEYREAD: /* Read only key to database */
- DBUG_PRINT("info", ("HA_EXTRA_KEYREAD"));
- break;
- case HA_EXTRA_NO_KEYREAD: /* Normal read of records (def) */
- DBUG_PRINT("info", ("HA_EXTRA_NO_KEYREAD"));
- break;
- case HA_EXTRA_NO_USER_CHANGE: /* No user is allowed to write */
- DBUG_PRINT("info", ("HA_EXTRA_NO_USER_CHANGE"));
- break;
- case HA_EXTRA_KEY_CACHE:
- DBUG_PRINT("info", ("HA_EXTRA_KEY_CACHE"));
- break;
- case HA_EXTRA_NO_KEY_CACHE:
- DBUG_PRINT("info", ("HA_EXTRA_NO_KEY_CACHE"));
- break;
- case HA_EXTRA_WAIT_LOCK: /* Wait until file is avalably (def) */
- DBUG_PRINT("info", ("HA_EXTRA_WAIT_LOCK"));
- break;
- case HA_EXTRA_NO_WAIT_LOCK: /* If file is locked, return quickly */
- DBUG_PRINT("info", ("HA_EXTRA_NO_WAIT_LOCK"));
- break;
- case HA_EXTRA_WRITE_CACHE: /* Use write cache in ha_write() */
- DBUG_PRINT("info", ("HA_EXTRA_WRITE_CACHE"));
- break;
- case HA_EXTRA_FLUSH_CACHE: /* flush write_record_cache */
- DBUG_PRINT("info", ("HA_EXTRA_FLUSH_CACHE"));
- break;
- case HA_EXTRA_NO_KEYS: /* Remove all update of keys */
- DBUG_PRINT("info", ("HA_EXTRA_NO_KEYS"));
- break;
- case HA_EXTRA_KEYREAD_CHANGE_POS: /* Keyread, but change pos */
- DBUG_PRINT("info", ("HA_EXTRA_KEYREAD_CHANGE_POS")); /* xxxxchk -r must be used */
- break;
- case HA_EXTRA_REMEMBER_POS: /* Remember pos for next/prev */
- DBUG_PRINT("info", ("HA_EXTRA_REMEMBER_POS"));
- break;
- case HA_EXTRA_RESTORE_POS:
- DBUG_PRINT("info", ("HA_EXTRA_RESTORE_POS"));
- break;
- case HA_EXTRA_REINIT_CACHE: /* init cache from current record */
- DBUG_PRINT("info", ("HA_EXTRA_REINIT_CACHE"));
- break;
- case HA_EXTRA_FORCE_REOPEN: /* Datafile have changed on disk */
- DBUG_PRINT("info", ("HA_EXTRA_FORCE_REOPEN"));
- break;
- case HA_EXTRA_FLUSH: /* Flush tables to disk */
- DBUG_PRINT("info", ("HA_EXTRA_FLUSH"));
- break;
- case HA_EXTRA_NO_ROWS: /* Don't write rows */
- DBUG_PRINT("info", ("HA_EXTRA_NO_ROWS"));
- break;
- case HA_EXTRA_RESET_STATE: /* Reset positions */
- DBUG_PRINT("info", ("HA_EXTRA_RESET_STATE"));
- break;
case HA_EXTRA_IGNORE_DUP_KEY: /* Dup keys don't rollback everything*/
DBUG_PRINT("info", ("HA_EXTRA_IGNORE_DUP_KEY"));
DBUG_PRINT("info", ("Ignoring duplicate key"));
@@ -3520,36 +4104,20 @@ int ha_ndbcluster::extra(enum ha_extra_function operation)
DBUG_PRINT("info", ("HA_EXTRA_NO_IGNORE_DUP_KEY"));
m_ignore_dup_key= FALSE;
break;
- case HA_EXTRA_RETRIEVE_ALL_COLS: /* Retrieve all columns, not just those
- where field->query_id is the same as
- the current query id */
- DBUG_PRINT("info", ("HA_EXTRA_RETRIEVE_ALL_COLS"));
- m_retrieve_all_fields= TRUE;
+ case HA_EXTRA_IGNORE_NO_KEY:
+ DBUG_PRINT("info", ("HA_EXTRA_IGNORE_NO_KEY"));
+ DBUG_PRINT("info", ("Turning on AO_IgnoreError at Commit/NoCommit"));
+ m_ignore_no_key= TRUE;
break;
- case HA_EXTRA_PREPARE_FOR_DELETE:
- DBUG_PRINT("info", ("HA_EXTRA_PREPARE_FOR_DELETE"));
- break;
- case HA_EXTRA_PREPARE_FOR_UPDATE: /* Remove read cache if problems */
- DBUG_PRINT("info", ("HA_EXTRA_PREPARE_FOR_UPDATE"));
- break;
- case HA_EXTRA_PRELOAD_BUFFER_SIZE:
- DBUG_PRINT("info", ("HA_EXTRA_PRELOAD_BUFFER_SIZE"));
- break;
- case HA_EXTRA_RETRIEVE_PRIMARY_KEY:
- DBUG_PRINT("info", ("HA_EXTRA_RETRIEVE_PRIMARY_KEY"));
- m_retrieve_primary_key= TRUE;
- break;
- case HA_EXTRA_CHANGE_KEY_TO_UNIQUE:
- DBUG_PRINT("info", ("HA_EXTRA_CHANGE_KEY_TO_UNIQUE"));
- break;
- case HA_EXTRA_CHANGE_KEY_TO_DUP:
- DBUG_PRINT("info", ("HA_EXTRA_CHANGE_KEY_TO_DUP"));
- case HA_EXTRA_KEYREAD_PRESERVE_FIELDS:
- DBUG_PRINT("info", ("HA_EXTRA_KEYREAD_PRESERVE_FIELDS"));
+ case HA_EXTRA_NO_IGNORE_NO_KEY:
+ DBUG_PRINT("info", ("HA_EXTRA_NO_IGNORE_NO_KEY"));
+ DBUG_PRINT("info", ("Turning on AO_IgnoreError at Commit/NoCommit"));
+ m_ignore_no_key= FALSE;
break;
case HA_EXTRA_WRITE_CAN_REPLACE:
DBUG_PRINT("info", ("HA_EXTRA_WRITE_CAN_REPLACE"));
- if (!m_has_unique_index)
+ if (!m_has_unique_index ||
+ current_thd->slave_thread) /* always set if slave, quick fix for bug 27378 */
{
DBUG_PRINT("info", ("Turning ON use of write instead of insert"));
m_use_write= TRUE;
@@ -3584,31 +4152,38 @@ int ha_ndbcluster::reset()
m_cond->cond_clear();
}
+ /*
+ Regular partition pruning will set the bitmap appropriately.
+ Some queries like ALTER TABLE doesn't use partition pruning and
+ thus the 'used_partitions' bitmap needs to be initialized
+ */
+ if (m_part_info)
+ bitmap_set_all(&m_part_info->used_partitions);
+
/* reset flags set by extra calls */
- m_retrieve_all_fields= FALSE;
- m_retrieve_primary_key= FALSE;
m_ignore_dup_key= FALSE;
m_use_write= FALSE;
+ m_ignore_no_key= FALSE;
m_delete_cannot_batch= FALSE;
m_update_cannot_batch= FALSE;
+
DBUG_RETURN(0);
}
-/*
- Start of an insert, remember number of rows to be inserted, it will
- be used in write_row and get_autoincrement to send an optimal number
- of rows in each roundtrip to the server
+/**
+ Start of an insert, remember number of rows to be inserted, it will
+ be used in write_row and get_autoincrement to send an optimal number
+ of rows in each roundtrip to the server.
- SYNOPSIS
+ @param
rows number of rows to insert, 0 if unknown
-
*/
void ha_ndbcluster::start_bulk_insert(ha_rows rows)
{
int bytes, batch;
- const NDBTAB *tab= (const NDBTAB *) m_table;
+ const NDBTAB *tab= m_table;
DBUG_ENTER("start_bulk_insert");
DBUG_PRINT("enter", ("rows: %d", (int)rows));
@@ -3652,9 +4227,9 @@ void ha_ndbcluster::start_bulk_insert(ha_rows rows)
DBUG_VOID_RETURN;
}
-/*
- End of an insert
- */
+/**
+ End of an insert.
+*/
int ha_ndbcluster::end_bulk_insert()
{
int error= 0;
@@ -3666,12 +4241,12 @@ int ha_ndbcluster::end_bulk_insert()
NdbTransaction *trans= m_active_trans;
// Send rows to NDB
DBUG_PRINT("info", ("Sending inserts to NDB, "\
- "rows_inserted:%d, bulk_insert_rows: %d",
+ "rows_inserted: %d bulk_insert_rows: %d",
(int) m_rows_inserted, (int) m_bulk_insert_rows));
m_bulk_insert_not_flushed= FALSE;
if (m_transaction_on)
{
- if (execute_no_commit(this, trans,false) != 0)
+ if (execute_no_commit(this, trans,FALSE) != 0)
{
no_uncommitted_rows_execute_failure();
my_errno= error= ndb_err(trans);
@@ -3715,8 +4290,9 @@ const char** ha_ndbcluster::bas_ext() const
return ha_ndbcluster_exts;
}
-/*
- How many seeks it will take to read through the table
+/**
+ How many seeks it will take to read through the table.
+
This is to be comparable to the number returned by records_in_range so
that we can decide if we should scan the table or use keys.
*/
@@ -3724,7 +4300,7 @@ const char** ha_ndbcluster::bas_ext() const
double ha_ndbcluster::scan_time()
{
DBUG_ENTER("ha_ndbcluster::scan_time()");
- double res= rows2double(records*1000);
+ double res= rows2double(stats.records*1000);
DBUG_PRINT("exit", ("table: %s value: %f",
m_tabname, res));
DBUG_RETURN(res);
@@ -3804,12 +4380,202 @@ THR_LOCK_DATA **ha_ndbcluster::store_lock(THD *thd,
- refresh list of the indexes for the table if needed (if altered)
*/
+#ifdef HAVE_NDB_BINLOG
+extern Master_info *active_mi;
+static int ndbcluster_update_apply_status(THD *thd, int do_update)
+{
+ Thd_ndb *thd_ndb= get_thd_ndb(thd);
+ Ndb *ndb= thd_ndb->ndb;
+ NDBDICT *dict= ndb->getDictionary();
+ const NDBTAB *ndbtab;
+ NdbTransaction *trans= thd_ndb->trans;
+ ndb->setDatabaseName(NDB_REP_DB);
+ Ndb_table_guard ndbtab_g(dict, NDB_APPLY_TABLE);
+ if (!(ndbtab= ndbtab_g.get_table()))
+ {
+ return -1;
+ }
+ NdbOperation *op= 0;
+ int r= 0;
+ r|= (op= trans->getNdbOperation(ndbtab)) == 0;
+ DBUG_ASSERT(r == 0);
+ if (do_update)
+ r|= op->updateTuple();
+ else
+ r|= op->writeTuple();
+ DBUG_ASSERT(r == 0);
+ // server_id
+ r|= op->equal(0u, (Uint32)thd->server_id);
+ DBUG_ASSERT(r == 0);
+ if (!do_update)
+ {
+ // epoch
+ r|= op->setValue(1u, (Uint64)0);
+ DBUG_ASSERT(r == 0);
+ }
+ // log_name
+ char tmp_buf[FN_REFLEN];
+ ndb_pack_varchar(ndbtab->getColumn(2u), tmp_buf,
+ active_mi->rli.group_master_log_name,
+ strlen(active_mi->rli.group_master_log_name));
+ r|= op->setValue(2u, tmp_buf);
+ DBUG_ASSERT(r == 0);
+ // start_pos
+ r|= op->setValue(3u, (Uint64)active_mi->rli.group_master_log_pos);
+ DBUG_ASSERT(r == 0);
+ // end_pos
+ r|= op->setValue(4u, (Uint64)active_mi->rli.group_master_log_pos +
+ ((Uint64)active_mi->rli.future_event_relay_log_pos -
+ (Uint64)active_mi->rli.group_relay_log_pos));
+ DBUG_ASSERT(r == 0);
+ return 0;
+}
+#endif /* HAVE_NDB_BINLOG */
+
+void ha_ndbcluster::transaction_checks(THD *thd)
+{
+ if (thd->lex->sql_command == SQLCOM_LOAD)
+ {
+ m_transaction_on= FALSE;
+ /* Would be simpler if has_transactions() didn't always say "yes" */
+ thd->transaction.all.modified_non_trans_table=
+ thd->transaction.stmt.modified_non_trans_table= TRUE;
+ }
+ else if (!thd->transaction.on)
+ m_transaction_on= FALSE;
+ else
+ m_transaction_on= thd->variables.ndb_use_transactions;
+}
+
+int ha_ndbcluster::start_statement(THD *thd,
+ Thd_ndb *thd_ndb,
+ Ndb *ndb)
+{
+ DBUG_ENTER("ha_ndbcluster::start_statement");
+ PRINT_OPTION_FLAGS(thd);
+
+ trans_register_ha(thd, FALSE, ndbcluster_hton);
+ if (!thd_ndb->trans)
+ {
+ if (thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN))
+ trans_register_ha(thd, TRUE, ndbcluster_hton);
+ DBUG_PRINT("trans",("Starting transaction"));
+ thd_ndb->trans= ndb->startTransaction();
+ if (thd_ndb->trans == NULL)
+ ERR_RETURN(ndb->getNdbError());
+ thd_ndb->init_open_tables();
+ thd_ndb->query_state&= NDB_QUERY_NORMAL;
+ thd_ndb->trans_options= 0;
+ thd_ndb->m_slow_path= FALSE;
+ if (!(thd->options & OPTION_BIN_LOG) ||
+ thd->variables.binlog_format == BINLOG_FORMAT_STMT)
+ {
+ thd_ndb->trans_options|= TNTO_NO_LOGGING;
+ thd_ndb->m_slow_path= TRUE;
+ }
+ else if (thd->slave_thread)
+ thd_ndb->m_slow_path= TRUE;
+ }
+ /*
+ If this is the start of a LOCK TABLE, a table look
+ should be taken on the table in NDB
+
+ Check if it should be read or write lock
+ */
+ if (thd->options & (OPTION_TABLE_LOCK))
+ {
+ //lockThisTable();
+ DBUG_PRINT("info", ("Locking the table..." ));
+ }
+ DBUG_RETURN(0);
+}
+
+int ha_ndbcluster::init_handler_for_statement(THD *thd, Thd_ndb *thd_ndb)
+{
+ /*
+ This is the place to make sure this handler instance
+ has a started transaction.
+
+ The transaction is started by the first handler on which
+ MySQL Server calls external lock
+
+ Other handlers in the same stmt or transaction should use
+ the same NDB transaction. This is done by setting up the m_active_trans
+ pointer to point to the NDB transaction.
+ */
+
+ 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_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;
+ m_active_trans= thd_ndb->trans;
+ DBUG_ASSERT(m_active_trans);
+ // Start of transaction
+ m_rows_changed= 0;
+ m_ops_pending= 0;
+ m_slow_path= thd_ndb->m_slow_path;
+#ifdef HAVE_NDB_BINLOG
+ if (unlikely(m_slow_path))
+ {
+ if (m_share == ndb_apply_status_share && thd->slave_thread)
+ thd_ndb->trans_options|= TNTO_INJECTED_APPLY_STATUS;
+ }
+#endif
+
+ if (thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN))
+ {
+ const void *key= m_table;
+ HASH_SEARCH_STATE state;
+ THD_NDB_SHARE *thd_ndb_share=
+ (THD_NDB_SHARE*)hash_first(&thd_ndb->open_tables, (uchar *)&key, sizeof(key), &state);
+ while (thd_ndb_share && thd_ndb_share->key != key)
+ thd_ndb_share= (THD_NDB_SHARE*)hash_next(&thd_ndb->open_tables, (uchar *)&key, sizeof(key), &state);
+ if (thd_ndb_share == 0)
+ {
+ thd_ndb_share= (THD_NDB_SHARE *) alloc_root(&thd->transaction.mem_root,
+ sizeof(THD_NDB_SHARE));
+ if (!thd_ndb_share)
+ {
+ mem_alloc_error(sizeof(THD_NDB_SHARE));
+ DBUG_RETURN(1);
+ }
+ thd_ndb_share->key= key;
+ thd_ndb_share->stat.last_count= thd_ndb->count;
+ thd_ndb_share->stat.no_uncommitted_rows_count= 0;
+ thd_ndb_share->stat.records= ~(ha_rows)0;
+ my_hash_insert(&thd_ndb->open_tables, (uchar *)thd_ndb_share);
+ }
+ else if (thd_ndb_share->stat.last_count != thd_ndb->count)
+ {
+ thd_ndb_share->stat.last_count= thd_ndb->count;
+ thd_ndb_share->stat.no_uncommitted_rows_count= 0;
+ thd_ndb_share->stat.records= ~(ha_rows)0;
+ }
+ DBUG_PRINT("exit", ("thd_ndb_share: 0x%lx key: 0x%lx",
+ (long) thd_ndb_share, (long) key));
+ m_table_info= &thd_ndb_share->stat;
+ }
+ else
+ {
+ struct Ndb_local_table_statistics &stat= m_table_info_instance;
+ stat.last_count= thd_ndb->count;
+ stat.no_uncommitted_rows_count= 0;
+ stat.records= ~(ha_rows)0;
+ m_table_info= &stat;
+ }
+ DBUG_RETURN(0);
+}
+
int ha_ndbcluster::external_lock(THD *thd, int lock_type)
{
int error=0;
- NdbTransaction* trans= NULL;
-
DBUG_ENTER("external_lock");
+
/*
Check that this handler instance has a connection
set up to the Ndb object of thd
@@ -3820,150 +4586,23 @@ int ha_ndbcluster::external_lock(THD *thd, int lock_type)
Thd_ndb *thd_ndb= get_thd_ndb(thd);
Ndb *ndb= thd_ndb->ndb;
- DBUG_PRINT("enter", ("thd: 0x%lx thd_ndb: 0x%lx thd_ndb->lock_count: %d",
- (long) thd, (long) thd_ndb, thd_ndb->lock_count));
+ DBUG_PRINT("enter", ("this: 0x%lx thd: 0x%lx thd_ndb: %lx "
+ "thd_ndb->lock_count: %d",
+ (long) this, (long) thd, (long) thd_ndb,
+ thd_ndb->lock_count));
if (lock_type != F_UNLCK)
{
DBUG_PRINT("info", ("lock_type != F_UNLCK"));
- if (thd->lex->sql_command == SQLCOM_LOAD)
- {
- m_transaction_on= FALSE;
- /* Would be simpler if has_transactions() didn't always say "yes" */
- thd->transaction.all.modified_non_trans_table= thd->transaction.stmt.modified_non_trans_table= TRUE;
- }
- else if (!thd->transaction.on)
- m_transaction_on= FALSE;
- else
- m_transaction_on= thd->variables.ndb_use_transactions;
+ transaction_checks(thd);
if (!thd_ndb->lock_count++)
{
- PRINT_OPTION_FLAGS(thd);
- if (!(thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)))
- {
- // Autocommit transaction
- DBUG_ASSERT(!thd_ndb->stmt);
- DBUG_PRINT("trans",("Starting transaction stmt"));
-
- trans= ndb->startTransaction();
- if (trans == NULL)
- ERR_RETURN(ndb->getNdbError());
- no_uncommitted_rows_reset(thd);
- thd_ndb->stmt= trans;
- thd_ndb->query_state&= NDB_QUERY_NORMAL;
- trans_register_ha(thd, FALSE, &ndbcluster_hton);
- }
- else
- {
- if (!thd_ndb->all)
- {
- // Not autocommit transaction
- // A "master" transaction ha not been started yet
- DBUG_PRINT("trans",("starting transaction, all"));
-
- trans= ndb->startTransaction();
- if (trans == NULL)
- ERR_RETURN(ndb->getNdbError());
- no_uncommitted_rows_reset(thd);
- thd_ndb->all= trans;
- thd_ndb->query_state&= NDB_QUERY_NORMAL;
- trans_register_ha(thd, TRUE, &ndbcluster_hton);
-
- /*
- If this is the start of a LOCK TABLE, a table look
- should be taken on the table in NDB
-
- Check if it should be read or write lock
- */
- if (thd->options & (OPTION_TABLE_LOCK))
- {
- //lockThisTable();
- DBUG_PRINT("info", ("Locking the table..." ));
- }
-
- }
- }
+ if ((error= start_statement(thd, thd_ndb, ndb)))
+ goto error;
}
- /*
- This is the place to make sure this handler instance
- has a started transaction.
-
- The transaction is started by the first handler on which
- MySQL Server calls external lock
-
- Other handlers in the same stmt or transaction should use
- the same NDB transaction. This is done by setting up the m_active_trans
- pointer to point to the NDB transaction.
- */
-
- // 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_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;
- m_active_trans= thd_ndb->all ? thd_ndb->all : thd_ndb->stmt;
- DBUG_ASSERT(m_active_trans);
- // Start of transaction
- m_rows_changed= 0;
- m_retrieve_all_fields= FALSE;
- m_retrieve_primary_key= FALSE;
- m_ops_pending= 0;
- {
- NDBDICT *dict= ndb->getDictionary();
- const NDBTAB *tab;
- void *tab_info;
- if (!(tab= dict->getTable(m_tabname, &tab_info)))
- ERR_RETURN(dict->getNdbError());
- DBUG_PRINT("info", ("Table schema version: %d",
- tab->getObjectVersion()));
- // Check if thread has stale local cache
- // New transaction must not use old tables... (trans != 0)
- // Running might...
- if ((trans && tab->getObjectStatus() != NdbDictionary::Object::Retrieved)
- || tab->getObjectStatus() == NdbDictionary::Object::Invalid)
- {
- invalidate_dictionary_cache(FALSE);
- if (!(tab= dict->getTable(m_tabname, &tab_info)))
- ERR_RETURN(dict->getNdbError());
- DBUG_PRINT("info", ("Table schema version: %d",
- tab->getObjectVersion()));
- }
- if (m_table_version < tab->getObjectVersion())
- {
- /*
- The table has been altered, caller has to retry
- */
- NdbError err= ndb->getNdbError(NDB_INVALID_SCHEMA_OBJECT);
- DBUG_RETURN(ndb_to_mysql_error(&err));
- }
- if (m_table != (void *)tab)
- {
- m_table= (void *)tab;
- m_table_version = tab->getObjectVersion();
- if ((my_errno= build_index_list(ndb, table, ILBP_OPEN)))
- DBUG_RETURN(my_errno);
-
- const void *data= NULL, *pack_data= NULL;
- uint length, pack_length;
- if (readfrm(table->s->path, &data, &length) ||
- packfrm(data, length, &pack_data, &pack_length) ||
- pack_length != tab->getFrmLength() ||
- memcmp(pack_data, tab->getFrmData(), pack_length))
- {
- my_free((char*)data, MYF(MY_ALLOW_ZERO_PTR));
- my_free((char*)pack_data, MYF(MY_ALLOW_ZERO_PTR));
- NdbError err= ndb->getNdbError(NDB_INVALID_SCHEMA_OBJECT);
- DBUG_RETURN(ndb_to_mysql_error(&err));
- }
- my_free((char*)data, MYF(MY_ALLOW_ZERO_PTR));
- my_free((char*)pack_data, MYF(MY_ALLOW_ZERO_PTR));
- }
- m_table_info= tab_info;
- }
- no_uncommitted_rows_init(thd);
+ if ((error= init_handler_for_statement(thd, thd_ndb)))
+ goto error;
+ DBUG_RETURN(0);
}
else
{
@@ -3991,16 +4630,19 @@ int ha_ndbcluster::external_lock(THD *thd, int lock_type)
DBUG_PRINT("trans", ("Last external_lock"));
PRINT_OPTION_FLAGS(thd);
- if (thd_ndb->stmt)
+ if (!(thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)))
{
- /*
- Unlock is done without a transaction commit / rollback.
- This happens if the thread didn't update any rows
- We must in this case close the transaction to release resources
- */
- DBUG_PRINT("trans",("ending non-updating transaction"));
- ndb->closeTransaction(m_active_trans);
- thd_ndb->stmt= NULL;
+ if (thd_ndb->trans)
+ {
+ /*
+ Unlock is done without a transaction commit / rollback.
+ This happens if the thread didn't update any rows
+ We must in this case close the transaction to release resources
+ */
+ DBUG_PRINT("trans",("ending non-updating transaction"));
+ ndb->closeTransaction(thd_ndb->trans);
+ thd_ndb->trans= NULL;
+ }
}
}
m_table_info= NULL;
@@ -4029,11 +4671,14 @@ int ha_ndbcluster::external_lock(THD *thd, int lock_type)
if (m_ops_pending)
DBUG_PRINT("warning", ("ops_pending != 0L"));
m_ops_pending= 0;
+ DBUG_RETURN(0);
}
+error:
+ thd_ndb->lock_count--;
DBUG_RETURN(error);
}
-/*
+/**
Unlock the last row read in an open scan.
Rows are unlocked by default in ndb, but
for SELECT FOR UPDATE and SELECT LOCK WIT SHARE MODE
@@ -4045,79 +4690,93 @@ void ha_ndbcluster::unlock_row()
DBUG_ENTER("unlock_row");
DBUG_PRINT("info", ("Unlocking row"));
- m_lock_tuple= false;
+ m_lock_tuple= FALSE;
DBUG_VOID_RETURN;
}
-/*
+/**
Start a transaction for running a statement if one is not
already running in a transaction. This will be the case in
a BEGIN; COMMIT; block
When using LOCK TABLE's external_lock will start a transaction
- since ndb does not currently does not support table locking
+ since ndb does not currently does not support table locking.
*/
int ha_ndbcluster::start_stmt(THD *thd, thr_lock_type lock_type)
{
int error=0;
DBUG_ENTER("start_stmt");
- PRINT_OPTION_FLAGS(thd);
Thd_ndb *thd_ndb= get_thd_ndb(thd);
- NdbTransaction *trans= (thd_ndb->stmt)?thd_ndb->stmt:thd_ndb->all;
- if (!trans){
+ transaction_checks(thd);
+ if (!thd_ndb->start_stmt_count++)
+ {
Ndb *ndb= thd_ndb->ndb;
- DBUG_PRINT("trans",("Starting transaction stmt"));
- trans= ndb->startTransaction();
- if (trans == NULL)
- ERR_RETURN(ndb->getNdbError());
- no_uncommitted_rows_reset(thd);
- thd_ndb->stmt= trans;
- thd_ndb->query_state&= NDB_QUERY_NORMAL;
- trans_register_ha(thd, FALSE, &ndbcluster_hton);
+ if ((error= start_statement(thd, thd_ndb, ndb)))
+ goto error;
}
- m_active_trans= trans;
- // Start of statement
- m_retrieve_all_fields= FALSE;
- m_retrieve_primary_key= FALSE;
- m_ops_pending= 0;
-
+ if ((error= init_handler_for_statement(thd, thd_ndb)))
+ goto error;
+ DBUG_RETURN(0);
+error:
+ thd_ndb->start_stmt_count--;
DBUG_RETURN(error);
}
-/*
- Commit a transaction started in NDB
- */
+/**
+ Commit a transaction started in NDB.
+*/
-int ndbcluster_commit(THD *thd, bool all)
+static int ndbcluster_commit(handlerton *hton, THD *thd, bool all)
{
int res= 0;
Thd_ndb *thd_ndb= get_thd_ndb(thd);
Ndb *ndb= thd_ndb->ndb;
- NdbTransaction *trans= all ? thd_ndb->all : thd_ndb->stmt;
+ NdbTransaction *trans= thd_ndb->trans;
DBUG_ENTER("ndbcluster_commit");
- DBUG_PRINT("transaction",("%s",
- trans == thd_ndb->stmt ?
- "stmt" : "all"));
- DBUG_ASSERT(ndb && trans);
+ DBUG_ASSERT(ndb);
+ 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)))
+ {
+ /*
+ An odditity in the handler interface is that commit on handlerton
+ is called to indicate end of statement only in cases where
+ autocommit isn't used and the all flag isn't set.
+
+ We also leave quickly when a transaction haven't even been started,
+ in this case we are safe that no clean up is needed. In this case
+ the MySQL Server could handle the query without contacting the
+ NDB kernel.
+ */
+ DBUG_PRINT("info", ("Commit before start or end-of-statement only"));
+ DBUG_RETURN(0);
+ }
+
+#ifdef HAVE_NDB_BINLOG
+ if (unlikely(thd_ndb->m_slow_path))
+ {
+ if (thd->slave_thread)
+ ndbcluster_update_apply_status
+ (thd, thd_ndb->trans_options & TNTO_INJECTED_APPLY_STATUS);
+ }
+#endif /* HAVE_NDB_BINLOG */
if (execute_commit(thd,trans) != 0)
{
const NdbError err= trans->getNdbError();
const NdbOperation *error_op= trans->getNdbErrorOperation();
- ERR_PRINT(err);
+ set_ndb_err(thd, err);
res= ndb_to_mysql_error(&err);
if (res != -1)
ndbcluster_print_error(res, error_op);
}
ndb->closeTransaction(trans);
-
- if (all)
- thd_ndb->all= NULL;
- else
- thd_ndb->stmt= NULL;
+ thd_ndb->trans= NULL;
/* Clear commit_count for tables changed by transaction */
NDB_SHARE* share;
@@ -4137,38 +4796,39 @@ int ndbcluster_commit(THD *thd, bool all)
}
-/*
- Rollback a transaction started in NDB
- */
+/**
+ Rollback a transaction started in NDB.
+*/
-int ndbcluster_rollback(THD *thd, bool all)
+static int ndbcluster_rollback(handlerton *hton, THD *thd, bool all)
{
int res= 0;
Thd_ndb *thd_ndb= get_thd_ndb(thd);
Ndb *ndb= thd_ndb->ndb;
- NdbTransaction *trans= all ? thd_ndb->all : thd_ndb->stmt;
+ NdbTransaction *trans= thd_ndb->trans;
DBUG_ENTER("ndbcluster_rollback");
- DBUG_PRINT("transaction",("%s",
- trans == thd_ndb->stmt ?
- "stmt" : "all"));
- DBUG_ASSERT(ndb && trans);
+ DBUG_ASSERT(ndb);
+ thd_ndb->start_stmt_count= 0;
+ if (trans == NULL || (!all &&
+ thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)))
+ {
+ /* Ignore end-of-statement until real rollback or commit is called */
+ DBUG_PRINT("info", ("Rollback before start or end-of-statement only"));
+ DBUG_RETURN(0);
+ }
if (trans->execute(NdbTransaction::Rollback) != 0)
{
const NdbError err= trans->getNdbError();
const NdbOperation *error_op= trans->getNdbErrorOperation();
- ERR_PRINT(err);
+ set_ndb_err(thd, err);
res= ndb_to_mysql_error(&err);
if (res != -1)
ndbcluster_print_error(res, error_op);
}
ndb->closeTransaction(trans);
-
- if (all)
- thd_ndb->all= NULL;
- else
- thd_ndb->stmt= NULL;
+ thd_ndb->trans= NULL;
/* Clear list of tables changed by transaction */
thd_ndb->changed_tables.empty();
@@ -4177,14 +4837,17 @@ int ndbcluster_rollback(THD *thd, bool all)
}
-/*
+/**
Define NDB column based on Field.
- Returns 0 or mysql error code.
+
Not member of ha_ndbcluster because NDBCOL cannot be declared.
MySQL text types with character set "binary" are mapped to true
NDB binary types without a character set. This may change.
- */
+
+ @return
+ Returns 0 or mysql error code.
+*/
static int create_ndb_column(NDBCOL &col,
Field *field,
@@ -4471,107 +5134,96 @@ static int create_ndb_column(NDBCOL &col,
return 0;
}
-/*
+/**
Create a table in NDB Cluster
- */
-
-static void ndb_set_fragmentation(NDBTAB &tab, TABLE *form, uint pk_length)
-{
- ha_rows max_rows= form->s->max_rows;
- ha_rows min_rows= form->s->min_rows;
- if (max_rows < min_rows)
- max_rows= min_rows;
- if (max_rows == (ha_rows)0) /* default setting, don't set fragmentation */
- return;
- /**
- * get the number of fragments right
- */
- uint no_fragments;
- {
-#if MYSQL_VERSION_ID >= 50000
- uint acc_row_size= 25 + /*safety margin*/ 2;
-#else
- uint acc_row_size= pk_length*4;
- /* add acc overhead */
- if (pk_length <= 8) /* main page will set the limit */
- acc_row_size+= 25 + /*safety margin*/ 2;
- else /* overflow page will set the limit */
- acc_row_size+= 4 + /*safety margin*/ 4;
-#endif
- ulonglong acc_fragment_size= 512*1024*1024;
- /*
- * if not --with-big-tables then max_rows is ulong
- * the warning in this case is misleading though
- */
- ulonglong big_max_rows = (ulonglong)max_rows;
-#if MYSQL_VERSION_ID >= 50100
- no_fragments= (big_max_rows*acc_row_size)/acc_fragment_size+1;
-#else
- no_fragments= ((big_max_rows*acc_row_size)/acc_fragment_size+1
- +1/*correct rounding*/)/2;
-#endif
- }
- {
- uint no_nodes= g_ndb_cluster_connection->no_db_nodes();
- NDBTAB::FragmentType ftype;
- if (no_fragments > 2*no_nodes)
- {
- ftype= NDBTAB::FragAllLarge;
- if (no_fragments > 4*no_nodes)
- push_warning(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_UNKNOWN_ERROR,
- "Ndb might have problems storing the max amount of rows specified");
- }
- else if (no_fragments > no_nodes)
- ftype= NDBTAB::FragAllMedium;
- else
- ftype= NDBTAB::FragAllSmall;
- tab.setFragmentType(ftype);
- }
- tab.setMaxRows(max_rows);
- tab.setMinRows(min_rows);
-}
+*/
int ha_ndbcluster::create(const char *name,
TABLE *form,
HA_CREATE_INFO *create_info)
{
+ THD *thd= current_thd;
NDBTAB tab;
NDBCOL col;
- uint pack_length, length, i, pk_length= 0;
- const void *data= NULL, *pack_data= NULL;
- char name2[FN_HEADLEN];
+ size_t pack_length, length;
+ uint i, pk_length= 0;
+ uchar *data= NULL, *pack_data= NULL;
bool create_from_engine= (create_info->table_options & HA_OPTION_CREATE_FROM_ENGINE);
+ bool is_truncate= (thd->lex->sql_command == SQLCOM_TRUNCATE);
+ char tablespace[FN_LEN];
+ NdbDictionary::Table::SingleUserMode single_user_mode= NdbDictionary::Table::SingleUserModeLocked;
DBUG_ENTER("ha_ndbcluster::create");
DBUG_PRINT("enter", ("name: %s", name));
- fn_format(name2, name, "", "",2); // Remove the .frm extension
- set_dbname(name2);
- set_tabname(name2);
- if (current_thd->lex->sql_command == SQLCOM_TRUNCATE)
+ DBUG_ASSERT(*fn_rext((char*)name) == 0);
+ set_dbname(name);
+ set_tabname(name);
+
+ if ((my_errno= check_ndb_connection()))
+ DBUG_RETURN(my_errno);
+
+ Ndb *ndb= get_ndb();
+ NDBDICT *dict= ndb->getDictionary();
+
+ if (is_truncate)
{
+ {
+ Ndb_table_guard ndbtab_g(dict, m_tabname);
+ if (!(m_table= ndbtab_g.get_table()))
+ ERR_RETURN(dict->getNdbError());
+ if ((get_tablespace_name(thd, tablespace, FN_LEN)))
+ create_info->tablespace= tablespace;
+ m_table= NULL;
+ }
DBUG_PRINT("info", ("Dropping and re-creating table for TRUNCATE"));
if ((my_errno= delete_table(name)))
DBUG_RETURN(my_errno);
}
+ table= form;
if (create_from_engine)
{
/*
- Table alreay exists in NDB and frm file has been created by
+ Table already exists in NDB and frm file has been created by
caller.
Do Ndb specific stuff, such as create a .ndb file
*/
- my_errno= write_ndb_file();
+ if ((my_errno= write_ndb_file(name)))
+ DBUG_RETURN(my_errno);
+#ifdef HAVE_NDB_BINLOG
+ ndbcluster_create_binlog_setup(get_ndb(), name, strlen(name),
+ m_dbname, m_tabname, FALSE);
+#endif /* HAVE_NDB_BINLOG */
DBUG_RETURN(my_errno);
}
+#ifdef HAVE_NDB_BINLOG
+ /*
+ Don't allow table creation unless
+ schema distribution table is setup
+ ( unless it is a creation of the schema dist table itself )
+ */
+ if (!ndb_schema_share)
+ {
+ if (!(strcmp(m_dbname, NDB_REP_DB) == 0 &&
+ strcmp(m_tabname, NDB_SCHEMA_TABLE) == 0))
+ {
+ DBUG_PRINT("info", ("Schema distribution table not setup"));
+ DBUG_ASSERT(ndb_schema_share);
+ DBUG_RETURN(HA_ERR_NO_CONNECTION);
+ }
+ single_user_mode = NdbDictionary::Table::SingleUserModeReadWrite;
+ }
+#endif /* HAVE_NDB_BINLOG */
+
DBUG_PRINT("table", ("name: %s", m_tabname));
if (tab.setName(m_tabname))
{
DBUG_RETURN(my_errno= errno);
}
tab.setLogging(!(create_info->options & HA_LEX_CREATE_TMP_TABLE));
-
+ tab.setSingleUserMode(single_user_mode);
+
// Save frm data for this table
if (readfrm(name, &data, &length))
DBUG_RETURN(1);
@@ -4580,12 +5232,64 @@ int ha_ndbcluster::create(const char *name,
my_free((char*)data, MYF(0));
DBUG_RETURN(2);
}
-
- DBUG_PRINT("info", ("setFrm data: 0x%lx len: %d", (long) pack_data, pack_length));
+ 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));
+ /*
+ Check for disk options
+ */
+ if (create_info->storage_media == HA_SM_DISK)
+ {
+ if (create_info->tablespace)
+ tab.setTablespaceName(create_info->tablespace);
+ else
+ tab.setTablespaceName("DEFAULT-TS");
+ }
+ else if (create_info->tablespace)
+ {
+ if (create_info->storage_media == HA_SM_MEMORY)
+ {
+ push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_ERROR,
+ ER_ILLEGAL_HA_CREATE_OPTION,
+ ER(ER_ILLEGAL_HA_CREATE_OPTION),
+ ndbcluster_hton_name,
+ "TABLESPACE currently only supported for "
+ "STORAGE DISK");
+ DBUG_RETURN(HA_ERR_UNSUPPORTED);
+ }
+ tab.setTablespaceName(create_info->tablespace);
+ create_info->storage_media = HA_SM_DISK; //if use tablespace, that also means store on disk
+ }
+
+ /*
+ Handle table row type
+
+ Default is to let table rows have var part reference so that online
+ add column can be performed in the future. Explicitly setting row
+ type to fixed will omit var part reference, which will save data
+ memory in ndb, but at the cost of not being able to online add
+ column to this table
+ */
+ switch (create_info->row_type) {
+ case ROW_TYPE_FIXED:
+ tab.setForceVarPart(FALSE);
+ break;
+ case ROW_TYPE_DYNAMIC:
+ /* fall through, treat as default */
+ default:
+ /* fall through, treat as default */
+ case ROW_TYPE_DEFAULT:
+ tab.setForceVarPart(TRUE);
+ break;
+ }
+
+ /*
+ Setup columns
+ */
for (i= 0; i < form->s->fields; i++)
{
Field *field= form->field[i];
@@ -4594,6 +5298,33 @@ int ha_ndbcluster::create(const char *name,
field->pack_length()));
if ((my_errno= create_ndb_column(col, field, create_info)))
DBUG_RETURN(my_errno);
+
+ if (create_info->storage_media == HA_SM_DISK)
+ col.setStorageType(NdbDictionary::Column::StorageTypeDisk);
+ else
+ col.setStorageType(NdbDictionary::Column::StorageTypeMemory);
+
+ switch (create_info->row_type) {
+ case ROW_TYPE_FIXED:
+ if (field_type_forces_var_part(field->type()))
+ {
+ push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_ERROR,
+ ER_ILLEGAL_HA_CREATE_OPTION,
+ ER(ER_ILLEGAL_HA_CREATE_OPTION),
+ ndbcluster_hton_name,
+ "Row format FIXED incompatible with "
+ "variable sized attribute");
+ DBUG_RETURN(HA_ERR_UNSUPPORTED);
+ }
+ break;
+ case ROW_TYPE_DYNAMIC:
+ /*
+ Future: make columns dynamic in this case
+ */
+ break;
+ default:
+ break;
+ }
if (tab.addColumn(col))
{
DBUG_RETURN(my_errno= errno);
@@ -4601,7 +5332,17 @@ int ha_ndbcluster::create(const char *name,
if (col.getPrimaryKey())
pk_length += (field->pack_length() + 3) / 4;
}
-
+
+ KEY* key_info;
+ for (i= 0, key_info= form->key_info; i < form->s->keys; i++, key_info++)
+ {
+ KEY_PART_INFO *key_part= key_info->key_part;
+ KEY_PART_INFO *end= key_part + key_info->key_parts;
+ for (; key_part != end; key_part++)
+ tab.getColumn(key_part->fieldnr-1)->setStorageType(
+ NdbDictionary::Column::StorageTypeMemory);
+ }
+
// No primary key, create shadow key as 64 bit, auto increment
if (form->s->primary_key == MAX_KEY)
{
@@ -4621,7 +5362,7 @@ int ha_ndbcluster::create(const char *name,
}
pk_length += 2;
}
-
+
// Make sure that blob tables don't have to big part size
for (i= 0; i < form->s->fields; i++)
{
@@ -4655,39 +5396,282 @@ int ha_ndbcluster::create(const char *name,
}
}
- ndb_set_fragmentation(tab, form, pk_length);
-
- if ((my_errno= check_ndb_connection()))
+ // Check partition info
+ partition_info *part_info= form->part_info;
+ if ((my_errno= set_up_partition_info(part_info, form, (void*)&tab)))
+ {
DBUG_RETURN(my_errno);
-
+ }
+
// Create the table in NDB
- Ndb *ndb= get_ndb();
- NDBDICT *dict= ndb->getDictionary();
if (dict->createTable(tab) != 0)
{
const NdbError err= dict->getNdbError();
- ERR_PRINT(err);
+ set_ndb_err(thd, err);
+ my_errno= ndb_to_mysql_error(&err);
+ DBUG_RETURN(my_errno);
+ }
+
+ Ndb_table_guard ndbtab_g(dict, m_tabname);
+ // temporary set m_table during create
+ // reset at return
+ m_table= ndbtab_g.get_table();
+ // TODO check also that we have the same frm...
+ if (!m_table)
+ {
+ /* purecov: begin deadcode */
+ const NdbError err= dict->getNdbError();
+ set_ndb_err(thd, err);
my_errno= ndb_to_mysql_error(&err);
DBUG_RETURN(my_errno);
+ /* purecov: end */
}
+
DBUG_PRINT("info", ("Table %s/%s created successfully",
m_dbname, m_tabname));
// Create secondary indexes
- my_errno= build_index_list(ndb, form, ILBP_CREATE);
+ my_errno= create_indexes(ndb, form);
+
+ if (!my_errno)
+ my_errno= write_ndb_file(name);
+ else
+ {
+ /*
+ Failed to create an index,
+ drop the table (and all it's indexes)
+ */
+ while (dict->dropTableGlobal(*m_table))
+ {
+ switch (dict->getNdbError().status)
+ {
+ case NdbError::TemporaryError:
+ if (!thd->killed)
+ continue; // retry indefinitly
+ break;
+ default:
+ break;
+ }
+ break;
+ }
+ m_table = 0;
+ DBUG_RETURN(my_errno);
+ }
+#ifdef HAVE_NDB_BINLOG
if (!my_errno)
- my_errno= write_ndb_file();
+ {
+ NDB_SHARE *share= 0;
+ pthread_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)))
+ handle_trailing_share(share);
+ }
+ /*
+ get a new share
+ */
+
+ /* ndb_share reference create */
+ if (!(share= get_share(name, form, TRUE, TRUE)))
+ {
+ sql_print_error("NDB: allocating table share for %s failed", name);
+ /* my_errno is set */
+ }
+ else
+ {
+ DBUG_PRINT("NDB_SHARE", ("%s binlog create use_count: %u",
+ share->key, share->use_count));
+ }
+ pthread_mutex_unlock(&ndbcluster_mutex);
+
+ while (!IS_TMP_PREFIX(m_tabname))
+ {
+ String event_name(INJECTOR_EVENT_LEN);
+ ndb_rep_event_name(&event_name,m_dbname,m_tabname);
+ int do_event_op= ndb_binlog_running;
+
+ if (!ndb_schema_share &&
+ strcmp(share->db, NDB_REP_DB) == 0 &&
+ strcmp(share->table_name, NDB_SCHEMA_TABLE) == 0)
+ do_event_op= 1;
+ /*
+ Always create an event for the table, as other mysql servers
+ expect it to be there.
+ */
+ if (!ndbcluster_create_event(ndb, m_table, event_name.c_ptr(), share,
+ share && do_event_op ? 2 : 1/* push warning */))
+ {
+ if (ndb_extra_logging)
+ sql_print_information("NDB Binlog: CREATE TABLE Event: %s",
+ event_name.c_ptr());
+ if (share &&
+ ndbcluster_create_event_ops(share, m_table, event_name.c_ptr()))
+ {
+ sql_print_error("NDB Binlog: FAILED CREATE TABLE event operations."
+ " Event: %s", name);
+ /* a warning has been issued to the client */
+ }
+ }
+ /*
+ warning has been issued if ndbcluster_create_event failed
+ and (share && do_event_op)
+ */
+ if (share && !do_event_op)
+ share->flags|= NSF_NO_BINLOG;
+ ndbcluster_log_schema_op(thd, share,
+ thd->query, thd->query_length,
+ share->db, share->table_name,
+ m_table->getObjectId(),
+ m_table->getObjectVersion(),
+ (is_truncate) ?
+ SOT_TRUNCATE_TABLE : SOT_CREATE_TABLE,
+ 0, 0, 1);
+ break;
+ }
+ }
+#endif /* HAVE_NDB_BINLOG */
+
+ m_table= 0;
DBUG_RETURN(my_errno);
}
+int ha_ndbcluster::create_handler_files(const char *file,
+ const char *old_name,
+ int action_flag,
+ HA_CREATE_INFO *create_info)
+{
+ Ndb* ndb;
+ const NDBTAB *tab;
+ uchar *data= NULL, *pack_data= NULL;
+ size_t length, pack_length;
+ int error= 0;
+
+ DBUG_ENTER("create_handler_files");
+
+ if (action_flag != CHF_INDEX_FLAG)
+ {
+ DBUG_RETURN(FALSE);
+ }
+ DBUG_PRINT("enter", ("file: %s", file));
+ if (!(ndb= get_ndb()))
+ DBUG_RETURN(HA_ERR_NO_CONNECTION);
+
+ NDBDICT *dict= ndb->getDictionary();
+ if (!create_info->frm_only)
+ DBUG_RETURN(0); // Must be a create, ignore since frm is saved in create
+
+ // TODO handle this
+ DBUG_ASSERT(m_table != 0);
+
+ set_dbname(file);
+ set_tabname(file);
+ Ndb_table_guard ndbtab_g(dict, m_tabname);
+ DBUG_PRINT("info", ("m_dbname: %s, m_tabname: %s", m_dbname, m_tabname));
+ if (!(tab= ndbtab_g.get_table()))
+ DBUG_RETURN(0); // Unkown table, must be temporary table
+
+ DBUG_ASSERT(get_ndb_share_state(m_share) == NSS_ALTERED);
+ if (readfrm(file, &data, &length) ||
+ 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));
+ error= 1;
+ }
+ else
+ {
+ DBUG_PRINT("info", ("Table %s has changed, altering frm in ndb",
+ m_tabname));
+ NdbDictionary::Table new_tab= *tab;
+ new_tab.setFrm(pack_data, pack_length);
+ if (dict->alterTableGlobal(*tab, new_tab))
+ {
+ 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));
+ }
+
+ set_ndb_share_state(m_share, NSS_INITIAL);
+ /* ndb_share reference schema(?) free */
+ DBUG_PRINT("NDB_SHARE", ("%s binlog schema(?) free use_count: %u",
+ m_share->key, m_share->use_count));
+ free_share(&m_share); // Decrease ref_count
+
+ DBUG_RETURN(error);
+}
+
+int ha_ndbcluster::create_index(const char *name, KEY *key_info,
+ NDB_INDEX_TYPE idx_type, uint idx_no)
+{
+ int error= 0;
+ char unique_name[FN_LEN];
+ static const char* unique_suffix= "$unique";
+ DBUG_ENTER("ha_ndbcluster::create_ordered_index");
+ DBUG_PRINT("info", ("Creating index %u: %s", idx_no, name));
+
+ if (idx_type == UNIQUE_ORDERED_INDEX || idx_type == UNIQUE_INDEX)
+ {
+ strxnmov(unique_name, FN_LEN, name, unique_suffix, NullS);
+ DBUG_PRINT("info", ("Created unique index name \'%s\' for index %d",
+ unique_name, idx_no));
+ }
+
+ switch (idx_type){
+ case PRIMARY_KEY_INDEX:
+ // Do nothing, already created
+ break;
+ case PRIMARY_KEY_ORDERED_INDEX:
+ error= create_ordered_index(name, key_info);
+ break;
+ case UNIQUE_ORDERED_INDEX:
+ if (!(error= create_ordered_index(name, key_info)))
+ error= create_unique_index(unique_name, key_info);
+ break;
+ case UNIQUE_INDEX:
+ if (check_index_fields_not_null(key_info))
+ {
+ push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ ER_NULL_COLUMN_IN_INDEX,
+ "Ndb does not support unique index on NULL valued attributes, index access with NULL value will become full table scan");
+ }
+ error= create_unique_index(unique_name, key_info);
+ break;
+ case ORDERED_INDEX:
+ if (key_info->algorithm == HA_KEY_ALG_HASH)
+ {
+ push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_ERROR,
+ ER_ILLEGAL_HA_CREATE_OPTION,
+ ER(ER_ILLEGAL_HA_CREATE_OPTION),
+ ndbcluster_hton_name,
+ "Ndb does not support non-unique "
+ "hash based indexes");
+ error= HA_ERR_UNSUPPORTED;
+ break;
+ }
+ error= create_ordered_index(name, key_info);
+ break;
+ default:
+ DBUG_ASSERT(FALSE);
+ break;
+ }
+
+ DBUG_RETURN(error);
+}
int ha_ndbcluster::create_ordered_index(const char *name,
KEY *key_info)
{
DBUG_ENTER("ha_ndbcluster::create_ordered_index");
- DBUG_RETURN(create_index(name, key_info, FALSE));
+ DBUG_RETURN(create_ndb_index(name, key_info, FALSE));
}
int ha_ndbcluster::create_unique_index(const char *name,
@@ -4695,17 +5679,20 @@ int ha_ndbcluster::create_unique_index(const char *name,
{
DBUG_ENTER("ha_ndbcluster::create_unique_index");
- DBUG_RETURN(create_index(name, key_info, TRUE));
+ DBUG_RETURN(create_ndb_index(name, key_info, TRUE));
}
-/*
- Create an index in NDB Cluster
- */
+/**
+ Create an index in NDB Cluster.
+
+ @todo
+ Only temporary ordered indexes supported
+*/
-int ha_ndbcluster::create_index(const char *name,
- KEY *key_info,
- bool unique)
+int ha_ndbcluster::create_ndb_index(const char *name,
+ KEY *key_info,
+ bool unique)
{
Ndb *ndb= get_ndb();
NdbDictionary::Dictionary *dict= ndb->getDictionary();
@@ -4739,7 +5726,7 @@ int ha_ndbcluster::create_index(const char *name,
}
}
- if (dict->createIndex(ndb_index))
+ if (dict->createIndex(ndb_index, *m_table))
ERR_RETURN(dict->getNdbError());
// Success
@@ -4748,14 +5735,112 @@ int ha_ndbcluster::create_index(const char *name,
}
/*
- Rename a table in NDB Cluster
+ Prepare for an on-line alter table
+*/
+void ha_ndbcluster::prepare_for_alter()
+{
+ /* ndb_share reference schema */
+ ndbcluster_get_share(m_share); // Increase ref_count
+ DBUG_PRINT("NDB_SHARE", ("%s binlog schema use_count: %u",
+ m_share->key, m_share->use_count));
+ set_ndb_share_state(m_share, NSS_ALTERED);
+}
+
+/*
+ Add an index on-line to a table
+*/
+int ha_ndbcluster::add_index(TABLE *table_arg,
+ KEY *key_info, uint num_of_keys)
+{
+ int error= 0;
+ uint idx;
+ DBUG_ENTER("ha_ndbcluster::add_index");
+ DBUG_PRINT("enter", ("table %s", table_arg->s->table_name.str));
+ DBUG_ASSERT(m_share->state == NSS_ALTERED);
+
+ for (idx= 0; idx < num_of_keys; idx++)
+ {
+ KEY *key= key_info + idx;
+ KEY_PART_INFO *key_part= key->key_part;
+ KEY_PART_INFO *end= key_part + key->key_parts;
+ NDB_INDEX_TYPE idx_type= get_index_type_from_key(idx, key_info, false);
+ DBUG_PRINT("info", ("Adding index: '%s'", key_info[idx].name));
+ // Add fields to key_part struct
+ for (; key_part != end; key_part++)
+ key_part->field= table->field[key_part->fieldnr];
+ // Check index type
+ // Create index in ndb
+ if((error= create_index(key_info[idx].name, key, idx_type, idx)))
+ break;
+ }
+ if (error)
+ {
+ set_ndb_share_state(m_share, NSS_INITIAL);
+ /* ndb_share reference schema free */
+ DBUG_PRINT("NDB_SHARE", ("%s binlog schema free use_count: %u",
+ m_share->key, m_share->use_count));
+ free_share(&m_share); // Decrease ref_count
+ }
+ DBUG_RETURN(error);
+}
+
+/*
+ Mark one or several indexes for deletion. and
+ renumber the remaining indexes
+*/
+int ha_ndbcluster::prepare_drop_index(TABLE *table_arg,
+ uint *key_num, uint num_of_keys)
+{
+ DBUG_ENTER("ha_ndbcluster::prepare_drop_index");
+ DBUG_ASSERT(m_share->state == NSS_ALTERED);
+ // Mark indexes for deletion
+ uint idx;
+ for (idx= 0; idx < num_of_keys; idx++)
+ {
+ DBUG_PRINT("info", ("ha_ndbcluster::prepare_drop_index %u", *key_num));
+ m_index[*key_num++].status= TO_BE_DROPPED;
+ }
+ // Renumber indexes
+ THD *thd= current_thd;
+ Thd_ndb *thd_ndb= get_thd_ndb(thd);
+ Ndb *ndb= thd_ndb->ndb;
+ renumber_indexes(ndb, table_arg);
+ DBUG_RETURN(0);
+}
+
+/*
+ Really drop all indexes marked for deletion
+*/
+int ha_ndbcluster::final_drop_index(TABLE *table_arg)
+{
+ int error;
+ DBUG_ENTER("ha_ndbcluster::final_drop_index");
+ DBUG_PRINT("info", ("ha_ndbcluster::final_drop_index"));
+ // Really drop indexes
+ THD *thd= current_thd;
+ Thd_ndb *thd_ndb= get_thd_ndb(thd);
+ Ndb *ndb= thd_ndb->ndb;
+ if((error= drop_indexes(ndb, table_arg)))
+ {
+ m_share->state= NSS_INITIAL;
+ /* ndb_share reference schema free */
+ DBUG_PRINT("NDB_SHARE", ("%s binlog schema free use_count: %u",
+ m_share->key, m_share->use_count));
+ free_share(&m_share); // Decrease ref_count
+ }
+ DBUG_RETURN(error);
+}
+
+/**
+ Rename a table in NDB Cluster.
*/
int ha_ndbcluster::rename_table(const char *from, const char *to)
{
NDBDICT *dict;
- char new_tabname[FN_HEADLEN];
+ char old_dbname[FN_HEADLEN];
char new_dbname[FN_HEADLEN];
+ char new_tabname[FN_HEADLEN];
const NDBTAB *orig_tab;
int result;
bool recreate_indexes= FALSE;
@@ -4763,7 +5848,7 @@ int ha_ndbcluster::rename_table(const char *from, const char *to)
DBUG_ENTER("ha_ndbcluster::rename_table");
DBUG_PRINT("info", ("Renaming %s to %s", from, to));
- set_dbname(from);
+ set_dbname(from, old_dbname);
set_dbname(to, new_dbname);
set_tabname(from);
set_tabname(to, new_tabname);
@@ -4772,106 +5857,352 @@ int ha_ndbcluster::rename_table(const char *from, const char *to)
DBUG_RETURN(my_errno= HA_ERR_NO_CONNECTION);
Ndb *ndb= get_ndb();
+ ndb->setDatabaseName(old_dbname);
dict= ndb->getDictionary();
- if (!(orig_tab= dict->getTable(m_tabname)))
+ Ndb_table_guard ndbtab_g(dict, m_tabname);
+ if (!(orig_tab= ndbtab_g.get_table()))
ERR_RETURN(dict->getNdbError());
- // Check if thread has stale local cache
- if (orig_tab->getObjectStatus() == NdbDictionary::Object::Invalid)
+
+#ifdef HAVE_NDB_BINLOG
+ int ndb_table_id= orig_tab->getObjectId();
+ int ndb_table_version= orig_tab->getObjectVersion();
+
+ /* ndb_share reference temporary */
+ NDB_SHARE *share= get_share(from, 0, FALSE);
+ if (share)
{
- dict->removeCachedTable(m_tabname);
- if (!(orig_tab= dict->getTable(m_tabname)))
- ERR_RETURN(dict->getNdbError());
+ DBUG_PRINT("NDB_SHARE", ("%s temporary use_count: %u",
+ share->key, share->use_count));
+ IF_DBUG(int r=) rename_share(share, to);
+ DBUG_ASSERT(r == 0);
}
- if (my_strcasecmp(system_charset_info, new_dbname, m_dbname))
+#endif
+ if (my_strcasecmp(system_charset_info, new_dbname, old_dbname))
{
- dict->listIndexes(index_list, m_tabname);
+ dict->listIndexes(index_list, *orig_tab);
recreate_indexes= TRUE;
}
-
- m_table= (void *)orig_tab;
// Change current database to that of target table
set_dbname(to);
if (ndb->setDatabaseName(m_dbname))
{
ERR_RETURN(ndb->getNdbError());
}
- if (!(result= alter_table_name(new_tabname)))
+
+ NdbDictionary::Table new_tab= *orig_tab;
+ new_tab.setName(new_tabname);
+ if (dict->alterTableGlobal(*orig_tab, new_tab) != 0)
+ {
+ NdbError ndb_error= dict->getNdbError();
+#ifdef HAVE_NDB_BINLOG
+ if (share)
+ {
+ IF_DBUG(int ret=) rename_share(share, from);
+ DBUG_ASSERT(ret == 0);
+ /* ndb_share reference temporary free */
+ DBUG_PRINT("NDB_SHARE", ("%s temporary free use_count: %u",
+ share->key, share->use_count));
+ free_share(&share);
+ }
+#endif
+ ERR_RETURN(ndb_error);
+ }
+
+ // Rename .ndb file
+ if ((result= handler::rename_table(from, to)))
{
- // Rename .ndb file
- result= handler::rename_table(from, to);
+ // ToDo in 4.1 should rollback alter table...
+#ifdef HAVE_NDB_BINLOG
+ if (share)
+ {
+ /* ndb_share reference temporary free */
+ DBUG_PRINT("NDB_SHARE", ("%s temporary use_count: %u",
+ share->key, share->use_count));
+ free_share(&share);
+ }
+#endif
+ DBUG_RETURN(result);
+ }
+
+#ifdef HAVE_NDB_BINLOG
+ int is_old_table_tmpfile= 1;
+ if (share && share->op)
+ dict->forceGCPWait();
+
+ /* handle old table */
+ if (!IS_TMP_PREFIX(m_tabname))
+ {
+ is_old_table_tmpfile= 0;
+ String event_name(INJECTOR_EVENT_LEN);
+ ndb_rep_event_name(&event_name, from + sizeof(share_prefix) - 1, 0);
+ ndbcluster_handle_drop_table(ndb, event_name.c_ptr(), share,
+ "rename table");
+ }
+
+ if (!result && !IS_TMP_PREFIX(new_tabname))
+ {
+ /* always create an event for the table */
+ String event_name(INJECTOR_EVENT_LEN);
+ ndb_rep_event_name(&event_name, to + sizeof(share_prefix) - 1, 0);
+ Ndb_table_guard ndbtab_g2(dict, new_tabname);
+ const NDBTAB *ndbtab= ndbtab_g2.get_table();
+
+ if (!ndbcluster_create_event(ndb, ndbtab, event_name.c_ptr(), share,
+ share && ndb_binlog_running ? 2 : 1/* push warning */))
+ {
+ if (ndb_extra_logging)
+ sql_print_information("NDB Binlog: RENAME Event: %s",
+ event_name.c_ptr());
+ if (share &&
+ ndbcluster_create_event_ops(share, ndbtab, event_name.c_ptr()))
+ {
+ sql_print_error("NDB Binlog: FAILED create event operations "
+ "during RENAME. Event %s", event_name.c_ptr());
+ /* a warning has been issued to the client */
+ }
+ }
+ /*
+ warning has been issued if ndbcluster_create_event failed
+ and (share && ndb_binlog_running)
+ */
+ if (!is_old_table_tmpfile)
+ ndbcluster_log_schema_op(current_thd, share,
+ current_thd->query, current_thd->query_length,
+ old_dbname, m_tabname,
+ ndb_table_id, ndb_table_version,
+ SOT_RENAME_TABLE,
+ m_dbname, new_tabname, 1);
}
// If we are moving tables between databases, we need to recreate
// indexes
if (recreate_indexes)
{
- const NDBTAB *new_tab;
- set_tabname(to);
- if (!(new_tab= dict->getTable(m_tabname)))
- ERR_RETURN(dict->getNdbError());
-
- for (unsigned i = 0; i < index_list.count; i++) {
+ for (unsigned i = 0; i < index_list.count; i++)
+ {
NDBDICT::List::Element& index_el = index_list.elements[i];
- set_dbname(from);
- if (ndb->setDatabaseName(m_dbname))
- {
- ERR_RETURN(ndb->getNdbError());
- }
- const NDBINDEX * index= dict->getIndex(index_el.name, *new_tab);
- set_dbname(to);
- if (ndb->setDatabaseName(m_dbname))
- {
- ERR_RETURN(ndb->getNdbError());
- }
- DBUG_PRINT("info", ("Creating index %s/%s",
- m_dbname, index->getName()));
- dict->createIndex(*index);
- DBUG_PRINT("info", ("Dropping index %s/%s",
- m_dbname, index->getName()));
-
- set_dbname(from);
- if (ndb->setDatabaseName(m_dbname))
- {
- ERR_RETURN(ndb->getNdbError());
- }
- dict->dropIndex(*index);
+ // Recreate any indexes not stored in the system database
+ if (my_strcasecmp(system_charset_info,
+ index_el.database, NDB_SYSTEM_DATABASE))
+ {
+ set_dbname(from);
+ ndb->setDatabaseName(m_dbname);
+ const NDBINDEX * index= dict->getIndexGlobal(index_el.name, new_tab);
+ DBUG_PRINT("info", ("Creating index %s/%s",
+ index_el.database, index->getName()));
+ dict->createIndex(*index, new_tab);
+ DBUG_PRINT("info", ("Dropping index %s/%s",
+ index_el.database, index->getName()));
+ set_dbname(from);
+ ndb->setDatabaseName(m_dbname);
+ dict->dropIndexGlobal(*index);
+ }
}
}
+ if (share)
+ {
+ /* ndb_share reference temporary free */
+ DBUG_PRINT("NDB_SHARE", ("%s temporary free use_count: %u",
+ share->key, share->use_count));
+ free_share(&share);
+ }
+#endif
DBUG_RETURN(result);
}
-/*
- Rename a table in NDB Cluster using alter table
- */
+/**
+ Delete table from NDB Cluster.
+*/
+
+/* static version which does not need a handler */
-int ha_ndbcluster::alter_table_name(const char *to)
+int
+ha_ndbcluster::delete_table(ha_ndbcluster *h, Ndb *ndb,
+ const char *path,
+ const char *db,
+ const char *table_name)
{
- Ndb *ndb= get_ndb();
+ THD *thd= current_thd;
+ DBUG_ENTER("ha_ndbcluster::ndbcluster_delete_table");
NDBDICT *dict= ndb->getDictionary();
- const NDBTAB *orig_tab= (const NDBTAB *) m_table;
- DBUG_ENTER("alter_table_name_table");
+ int ndb_table_id= 0;
+ int ndb_table_version= 0;
+#ifdef HAVE_NDB_BINLOG
+ /*
+ Don't allow drop table unless
+ schema distribution table is setup
+ */
+ if (!ndb_schema_share)
+ {
+ DBUG_PRINT("info", ("Schema distribution table not setup"));
+ DBUG_ASSERT(ndb_schema_share);
+ DBUG_RETURN(HA_ERR_NO_CONNECTION);
+ }
+ /* ndb_share reference temporary */
+ NDB_SHARE *share= get_share(path, 0, FALSE);
+ if (share)
+ {
+ DBUG_PRINT("NDB_SHARE", ("%s temporary use_count: %u",
+ share->key, share->use_count));
+ }
+#endif
- NdbDictionary::Table new_tab= *orig_tab;
- if (new_tab.setName(to))
+ /* Drop the table from NDB */
+
+ int res= 0;
+ if (h && h->m_table)
{
- DBUG_RETURN(my_errno= errno);
+retry_temporary_error1:
+ if (dict->dropTableGlobal(*h->m_table) == 0)
+ {
+ ndb_table_id= h->m_table->getObjectId();
+ ndb_table_version= h->m_table->getObjectVersion();
+ DBUG_PRINT("info", ("success 1"));
+ }
+ else
+ {
+ switch (dict->getNdbError().status)
+ {
+ case NdbError::TemporaryError:
+ if (!thd->killed)
+ goto retry_temporary_error1; // retry indefinitly
+ break;
+ default:
+ break;
+ }
+ set_ndb_err(thd, dict->getNdbError());
+ res= ndb_to_mysql_error(&dict->getNdbError());
+ DBUG_PRINT("info", ("error(1) %u", res));
+ }
+ h->release_metadata(thd, ndb);
+ }
+ else
+ {
+ ndb->setDatabaseName(db);
+ while (1)
+ {
+ Ndb_table_guard ndbtab_g(dict, table_name);
+ if (ndbtab_g.get_table())
+ {
+ retry_temporary_error2:
+ if (dict->dropTableGlobal(*ndbtab_g.get_table()) == 0)
+ {
+ ndb_table_id= ndbtab_g.get_table()->getObjectId();
+ ndb_table_version= ndbtab_g.get_table()->getObjectVersion();
+ DBUG_PRINT("info", ("success 2"));
+ break;
+ }
+ else
+ {
+ switch (dict->getNdbError().status)
+ {
+ case NdbError::TemporaryError:
+ if (!thd->killed)
+ goto retry_temporary_error2; // retry indefinitly
+ break;
+ default:
+ if (dict->getNdbError().code == NDB_INVALID_SCHEMA_OBJECT)
+ {
+ ndbtab_g.invalidate();
+ continue;
+ }
+ break;
+ }
+ }
+ }
+ set_ndb_err(thd, dict->getNdbError());
+ res= ndb_to_mysql_error(&dict->getNdbError());
+ DBUG_PRINT("info", ("error(2) %u", res));
+ break;
+ }
}
- if (dict->alterTable(new_tab) != 0)
- ERR_RETURN(dict->getNdbError());
- m_table= NULL;
- m_table_info= NULL;
-
- DBUG_RETURN(0);
-}
+ if (res)
+ {
+#ifdef HAVE_NDB_BINLOG
+ /* the drop table failed for some reason, drop the share anyways */
+ if (share)
+ {
+ pthread_mutex_lock(&ndbcluster_mutex);
+ if (share->state != NSS_DROPPED)
+ {
+ /*
+ The share kept by the server has not been freed, free it
+ */
+ share->state= NSS_DROPPED;
+ /* ndb_share reference create free */
+ DBUG_PRINT("NDB_SHARE", ("%s create free use_count: %u",
+ share->key, share->use_count));
+ free_share(&share, TRUE);
+ }
+ /* ndb_share reference temporary free */
+ DBUG_PRINT("NDB_SHARE", ("%s temporary free use_count: %u",
+ share->key, share->use_count));
+ free_share(&share, TRUE);
+ pthread_mutex_unlock(&ndbcluster_mutex);
+ }
+#endif
+ DBUG_RETURN(res);
+ }
+#ifdef HAVE_NDB_BINLOG
+ /* stop the logging of the dropped table, and cleanup */
-/*
- Delete table from NDB Cluster
+ /*
+ drop table is successful even if table does not exist in ndb
+ and in case table was actually not dropped, there is no need
+ to force a gcp, and setting the event_name to null will indicate
+ that there is no event to be dropped
+ */
+ int table_dropped= dict->getNdbError().code != 709;
- */
+ if (!IS_TMP_PREFIX(table_name) && share &&
+ current_thd->lex->sql_command != SQLCOM_TRUNCATE)
+ {
+ ndbcluster_log_schema_op(thd, share,
+ thd->query, thd->query_length,
+ share->db, share->table_name,
+ ndb_table_id, ndb_table_version,
+ SOT_DROP_TABLE, 0, 0, 1);
+ }
+ else if (table_dropped && share && share->op) /* ndbcluster_log_schema_op
+ will do a force GCP */
+ dict->forceGCPWait();
+
+ if (!IS_TMP_PREFIX(table_name))
+ {
+ String event_name(INJECTOR_EVENT_LEN);
+ ndb_rep_event_name(&event_name, path + sizeof(share_prefix) - 1, 0);
+ ndbcluster_handle_drop_table(ndb,
+ table_dropped ? event_name.c_ptr() : 0,
+ share, "delete table");
+ }
+
+ if (share)
+ {
+ pthread_mutex_lock(&ndbcluster_mutex);
+ if (share->state != NSS_DROPPED)
+ {
+ /*
+ The share kept by the server has not been freed, free it
+ */
+ share->state= NSS_DROPPED;
+ /* ndb_share reference create free */
+ DBUG_PRINT("NDB_SHARE", ("%s create free use_count: %u",
+ share->key, share->use_count));
+ free_share(&share, TRUE);
+ }
+ /* ndb_share reference temporary free */
+ DBUG_PRINT("NDB_SHARE", ("%s temporary free use_count: %u",
+ share->key, share->use_count));
+ free_share(&share, TRUE);
+ pthread_mutex_unlock(&ndbcluster_mutex);
+ }
+#endif
+ DBUG_RETURN(0);
+}
int ha_ndbcluster::delete_table(const char *name)
{
@@ -4880,57 +6211,37 @@ int ha_ndbcluster::delete_table(const char *name)
set_dbname(name);
set_tabname(name);
+#ifdef HAVE_NDB_BINLOG
+ /*
+ Don't allow drop table unless
+ schema distribution table is setup
+ */
+ if (!ndb_schema_share)
+ {
+ DBUG_PRINT("info", ("Schema distribution table not setup"));
+ DBUG_ASSERT(ndb_schema_share);
+ DBUG_RETURN(HA_ERR_NO_CONNECTION);
+ }
+#endif
+
if (check_ndb_connection())
DBUG_RETURN(HA_ERR_NO_CONNECTION);
/* Call ancestor function to delete .ndb file */
handler::delete_table(name);
-
- /* Drop the table from NDB */
- DBUG_RETURN(drop_table());
-}
-
-
-/*
- Drop table in NDB Cluster
- */
-
-int ha_ndbcluster::drop_table()
-{
- THD *thd= current_thd;
- Ndb *ndb= get_ndb();
- NdbDictionary::Dictionary *dict= ndb->getDictionary();
- DBUG_ENTER("drop_table");
- DBUG_PRINT("enter", ("Deleting %s", m_tabname));
-
- release_metadata();
- while (dict->dropTable(m_tabname))
- {
- const NdbError err= dict->getNdbError();
- switch (err.status)
- {
- case NdbError::TemporaryError:
- if (!thd->killed)
- continue; // retry indefinitly
- break;
- default:
- break;
- }
- ERR_RETURN(dict->getNdbError());
- }
-
- DBUG_RETURN(0);
+ DBUG_RETURN(delete_table(this, get_ndb(),name, m_dbname, m_tabname));
}
-ulonglong ha_ndbcluster::get_auto_increment()
-{
+void ha_ndbcluster::get_auto_increment(ulonglong offset, ulonglong increment,
+ ulonglong nb_desired_values,
+ ulonglong *first_value,
+ ulonglong *nb_reserved_values)
+{
uint cache_size;
Uint64 auto_value;
THD *thd= current_thd;
- Uint64 step= thd->variables.auto_increment_increment;
- Uint64 start= thd->variables.auto_increment_offset;
DBUG_ENTER("get_auto_increment");
DBUG_PRINT("enter", ("m_tabname: %s", m_tabname));
Ndb *ndb= get_ndb();
@@ -4952,10 +6263,10 @@ ulonglong ha_ndbcluster::get_auto_increment()
int retry_sleep= 30; /* 30 milliseconds, transaction */
for (;;)
{
+ Ndb_tuple_id_range_guard g(m_share);
if (m_skip_auto_increment &&
- ndb->readAutoIncrementValue((const NDBTAB *) m_table, auto_value) ||
- ndb->getAutoIncrementValue((const NDBTAB *) m_table,
- auto_value, cache_size, step, start))
+ ndb->readAutoIncrementValue(m_table, g.range, auto_value) ||
+ ndb->getAutoIncrementValue(m_table, g.range, auto_value, cache_size, increment, offset))
{
if (--retries &&
ndb->getNdbError().status == NdbError::TemporaryError)
@@ -4966,41 +6277,58 @@ ulonglong ha_ndbcluster::get_auto_increment()
const NdbError err= ndb->getNdbError();
sql_print_error("Error %lu in ::get_auto_increment(): %s",
(ulong) err.code, err.message);
- DBUG_RETURN(~(ulonglong) 0);
+ *first_value= ~(ulonglong) 0;
+ DBUG_VOID_RETURN;
}
break;
}
- DBUG_RETURN((longlong)auto_value);
+ *first_value= (longlong)auto_value;
+ /* From the point of view of MySQL, NDB reserves one row at a time */
+ *nb_reserved_values= 1;
+ DBUG_VOID_RETURN;
}
-/*
- Constructor for the NDB Cluster table handler
- */
+/**
+ Constructor for the NDB Cluster table handler .
+*/
-ha_ndbcluster::ha_ndbcluster(TABLE *table_arg):
- handler(&ndbcluster_hton, table_arg),
+/*
+ Normal flags for binlogging is that ndb has HA_HAS_OWN_BINLOGGING
+ and preferes HA_BINLOG_ROW_CAPABLE
+ Other flags are set under certain circumstaces in table_flags()
+*/
+#define HA_NDBCLUSTER_TABLE_FLAGS \
+ HA_REC_NOT_IN_SEQ | \
+ HA_NULL_IN_KEY | \
+ HA_AUTO_PART_KEY | \
+ HA_NO_PREFIX_CHAR_KEYS | \
+ HA_NEED_READ_RANGE_BUFFER | \
+ HA_CAN_GEOMETRY | \
+ HA_CAN_BIT_FIELD | \
+ HA_PRIMARY_KEY_REQUIRED_FOR_POSITION | \
+ HA_PRIMARY_KEY_REQUIRED_FOR_DELETE | \
+ HA_PARTIAL_COLUMN_READ | \
+ HA_HAS_OWN_BINLOGGING | \
+ HA_BINLOG_ROW_CAPABLE | \
+ HA_HAS_RECORDS
+
+ha_ndbcluster::ha_ndbcluster(handlerton *hton, TABLE_SHARE *table_arg):
+ handler(hton, table_arg),
m_active_trans(NULL),
m_active_cursor(NULL),
m_table(NULL),
- m_table_version(-1),
m_table_info(NULL),
- m_table_flags(HA_REC_NOT_IN_SEQ |
- HA_NULL_IN_KEY |
- HA_AUTO_PART_KEY |
- HA_NO_PREFIX_CHAR_KEYS |
- HA_NEED_READ_RANGE_BUFFER |
- HA_CAN_GEOMETRY |
- HA_CAN_BIT_FIELD |
- HA_PARTIAL_COLUMN_READ |
- HA_EXTERNAL_AUTO_INCREMENT),
+ m_table_flags(HA_NDBCLUSTER_TABLE_FLAGS),
m_share(0),
+ m_part_info(NULL),
+ m_use_partition_function(FALSE),
+ m_sorted(FALSE),
m_use_write(FALSE),
m_ignore_dup_key(FALSE),
m_has_unique_index(FALSE),
m_primary_key_update(FALSE),
- m_retrieve_all_fields(FALSE),
- m_retrieve_primary_key(FALSE),
+ m_ignore_no_key(FALSE),
m_rows_to_insert((ha_rows) 1),
m_rows_inserted((ha_rows) 0),
m_bulk_insert_rows((ha_rows) 1024),
@@ -5029,32 +6357,44 @@ ha_ndbcluster::ha_ndbcluster(TABLE *table_arg):
m_tabname[0]= '\0';
m_dbname[0]= '\0';
- records= ~(ha_rows)0; // uninitialized
- block_size= 1024;
+ stats.records= ~(ha_rows)0; // uninitialized
+ stats.block_size= 1024;
for (i= 0; i < MAX_KEY; i++)
- {
- m_index[i].type= UNDEFINED_INDEX;
- m_index[i].unique_index= NULL;
- m_index[i].index= NULL;
- m_index[i].unique_index_attrid_map= NULL;
- }
+ ndb_init_index(m_index[i]);
DBUG_VOID_RETURN;
}
-/*
- Destructor for NDB Cluster table handler
- */
+int ha_ndbcluster::ha_initialise()
+{
+ DBUG_ENTER("ha_ndbcluster::ha_initialise");
+ if (check_ndb_in_thd(current_thd))
+ {
+ DBUG_RETURN(FALSE);
+ }
+ DBUG_RETURN(TRUE);
+}
+
+/**
+ Destructor for NDB Cluster table handler.
+*/
ha_ndbcluster::~ha_ndbcluster()
{
+ THD *thd= current_thd;
+ Ndb *ndb= thd ? check_ndb_in_thd(thd) : g_ndb;
DBUG_ENTER("~ha_ndbcluster");
if (m_share)
- free_share(m_share);
- release_metadata();
+ {
+ /* ndb_share reference handler free */
+ DBUG_PRINT("NDB_SHARE", ("%s handler free use_count: %u",
+ m_share->key, m_share->use_count));
+ free_share(&m_share);
+ }
+ release_metadata(thd, ndb);
my_free(m_blobs_buffer, MYF(MY_ALLOW_ZERO_PTR));
m_blobs_buffer= 0;
@@ -5079,41 +6419,64 @@ ha_ndbcluster::~ha_ndbcluster()
-/*
- Open a table for further use
+/**
+ Open a table for further use.
+
- fetch metadata for this table from NDB
- check that table exists
+
+ @retval
+ 0 ok
+ @retval
+ < 0 Table has changed
*/
int ha_ndbcluster::open(const char *name, int mode, uint test_if_locked)
{
int res;
KEY *key;
- DBUG_ENTER("open");
- DBUG_PRINT("enter", ("name: %s mode: %d test_if_locked: %d",
+ DBUG_ENTER("ha_ndbcluster::open");
+ DBUG_PRINT("enter", ("name: %s mode: %d test_if_locked: %d",
name, mode, test_if_locked));
- // Setup ref_length to make room for the whole
- // primary key to be written in the ref variable
+ /*
+ Setup ref_length to make room for the whole
+ primary key to be written in the ref variable
+ */
- if (table->s->primary_key != MAX_KEY)
+ if (table_share->primary_key != MAX_KEY)
{
- key= table->key_info+table->s->primary_key;
+ key= table->key_info+table_share->primary_key;
ref_length= key->key_length;
- DBUG_PRINT("info", (" ref_length: %d", ref_length));
}
+ else // (table_share->primary_key == MAX_KEY)
+ {
+ if (m_use_partition_function)
+ {
+ ref_length+= sizeof(m_part_id);
+ }
+ }
+
+ DBUG_PRINT("info", ("ref_length: %d", ref_length));
+
// Init table lock structure
- if (!(m_share=get_share(name)))
+ /* ndb_share reference handler */
+ if (!(m_share=get_share(name, table)))
DBUG_RETURN(1);
+ DBUG_PRINT("NDB_SHARE", ("%s handler use_count: %u",
+ m_share->key, m_share->use_count));
thr_lock_data_init(&m_share->lock,&m_lock,(void*) 0);
set_dbname(name);
set_tabname(name);
- if ((res= check_ndb_connection()) ||
+ if ((res= check_ndb_connection()) ||
(res= get_metadata(name)))
{
- free_share(m_share);
+ /* ndb_share reference handler free */
+ DBUG_PRINT("NDB_SHARE", ("%s handler free use_count: %u",
+ m_share->key, m_share->use_count));
+ free_share(&m_share);
m_share= 0;
DBUG_RETURN(res);
}
@@ -5122,41 +6485,84 @@ int ha_ndbcluster::open(const char *name, int mode, uint test_if_locked)
Ndb *ndb= get_ndb();
if (ndb->setDatabaseName(m_dbname))
{
+ set_ndb_err(current_thd, ndb->getNdbError());
res= ndb_to_mysql_error(&ndb->getNdbError());
break;
}
struct Ndb_statistics stat;
- res= ndb_get_table_statistics(NULL, false, ndb, m_tabname, &stat);
- records= stat.row_count;
+ res= ndb_get_table_statistics(NULL, FALSE, ndb, m_table, &stat);
+ stats.mean_rec_length= stat.row_size;
+ stats.data_file_length= stat.fragment_memory;
+ stats.records= stat.row_count;
if(!res)
res= info(HA_STATUS_CONST);
break;
}
if (res)
{
- free_share(m_share);
+ free_share(&m_share);
m_share= 0;
- release_metadata();
+ release_metadata(current_thd, get_ndb());
DBUG_RETURN(res);
}
+#ifdef HAVE_NDB_BINLOG
+ if (!ndb_binlog_tables_inited)
+ {
+ table->db_stat|= HA_READ_ONLY;
+ sql_print_information("table '%s' opened read only", name);
+ }
+#endif
DBUG_RETURN(0);
}
-
/*
- Close the table
- - release resources setup by open()
- */
+ Set partition info
+
+ SYNOPSIS
+ set_part_info()
+ part_info
+
+ RETURN VALUE
+ NONE
+
+ DESCRIPTION
+ Set up partition info when handler object created
+*/
+
+void ha_ndbcluster::set_part_info(partition_info *part_info)
+{
+ m_part_info= part_info;
+ if (!(m_part_info->part_type == HASH_PARTITION &&
+ m_part_info->list_of_part_fields &&
+ !m_part_info->is_sub_partitioned()))
+ m_use_partition_function= TRUE;
+}
+
+/**
+ Close the table; release resources setup by open().
+*/
int ha_ndbcluster::close(void)
{
- DBUG_ENTER("close");
- free_share(m_share); m_share= 0;
- release_metadata();
+ DBUG_ENTER("close");
+ THD *thd= table->in_use;
+ Ndb *ndb= thd ? check_ndb_in_thd(thd) : g_ndb;
+ /* ndb_share reference handler free */
+ DBUG_PRINT("NDB_SHARE", ("%s handler free use_count: %u",
+ m_share->key, m_share->use_count));
+ free_share(&m_share);
+ m_share= 0;
+ release_metadata(thd, ndb);
DBUG_RETURN(0);
}
+/**
+ @todo
+ - Alt.1 If init fails because to many allocated Ndb
+ wait on condition for a Ndb object to be released.
+ - Alt.2 Seize/release from pool, wait until next release
+*/
Thd_ndb* ha_ndbcluster::seize_thd_ndb()
{
Thd_ndb *thd_ndb;
@@ -5168,9 +6574,6 @@ Thd_ndb* ha_ndbcluster::seize_thd_ndb()
my_errno= HA_ERR_OUT_OF_MEM;
return NULL;
}
- thd_ndb->ndb->getDictionary()->set_local_table_data_size(
- sizeof(Ndb_local_table_statistics)
- );
if (thd_ndb->ndb->init(max_transactions) != 0)
{
ERR_PRINT(thd_ndb->ndb->getNdbError());
@@ -5195,7 +6598,7 @@ void ha_ndbcluster::release_thd_ndb(Thd_ndb* thd_ndb)
}
-/*
+/**
If this thread already has a Thd_ndb object allocated
in current THD, reuse it. Otherwise
seize a Thd_ndb object, assign it to current THD and use it.
@@ -5231,7 +6634,7 @@ int ha_ndbcluster::check_ndb_connection(THD* thd)
}
-int ndbcluster_close_connection(THD *thd)
+static int ndbcluster_close_connection(handlerton *hton, THD *thd)
{
Thd_ndb *thd_ndb= get_thd_ndb(thd);
DBUG_ENTER("ndbcluster_close_connection");
@@ -5244,17 +6647,21 @@ int ndbcluster_close_connection(THD *thd)
}
-/*
- Try to discover one table from NDB
- */
+/**
+ Try to discover one table from NDB.
+*/
-int ndbcluster_discover(THD* thd, const char *db, const char *name,
- const void** frmblob, uint* frmlen)
+int ndbcluster_discover(handlerton *hton, THD* thd, const char *db,
+ const char *name,
+ uchar **frmblob,
+ size_t *frmlen)
{
- uint len;
- const void* data;
- const NDBTAB* tab;
+ int error= 0;
+ NdbError ndb_error;
+ size_t len;
+ uchar* data= NULL;
Ndb* ndb;
+ char key[FN_REFLEN];
DBUG_ENTER("ndbcluster_discover");
DBUG_PRINT("enter", ("db: %s, name: %s", db, name));
@@ -5265,81 +6672,138 @@ int ndbcluster_discover(THD* thd, const char *db, const char *name,
ERR_RETURN(ndb->getNdbError());
}
NDBDICT* dict= ndb->getDictionary();
- dict->set_local_table_data_size(sizeof(Ndb_local_table_statistics));
- dict->invalidateTable(name);
- if (!(tab= dict->getTable(name)))
- {
- const NdbError err= dict->getNdbError();
- if (err.code == 709)
- DBUG_RETURN(-1);
- ERR_RETURN(err);
+ build_table_filename(key, sizeof(key), db, name, "", 0);
+ /* ndb_share reference temporary */
+ NDB_SHARE *share= get_share(key, 0, FALSE);
+ if (share)
+ {
+ DBUG_PRINT("NDB_SHARE", ("%s temporary use_count: %u",
+ share->key, share->use_count));
}
- DBUG_PRINT("info", ("Found table %s", tab->getName()));
-
- len= tab->getFrmLength();
- if (len == 0 || tab->getFrmData() == NULL)
+ if (share && get_ndb_share_state(share) == NSS_ALTERED)
{
- DBUG_PRINT("error", ("No frm data found."));
- DBUG_RETURN(1);
+ // Frm has been altered on disk, but not yet written to ndb
+ if (readfrm(key, &data, &len))
+ {
+ DBUG_PRINT("error", ("Could not read frm"));
+ error= 1;
+ goto err;
+ }
}
-
- if (unpackfrm(&data, &len, tab->getFrmData()))
+ else
{
- DBUG_PRINT("error", ("Could not unpack table"));
- DBUG_RETURN(1);
+ Ndb_table_guard ndbtab_g(dict, name);
+ const NDBTAB *tab= ndbtab_g.get_table();
+ if (!tab)
+ {
+ const NdbError err= dict->getNdbError();
+ if (err.code == 709 || err.code == 723)
+ {
+ error= -1;
+ DBUG_PRINT("info", ("ndb_error.code: %u", ndb_error.code));
+ }
+ else
+ {
+ error= -1;
+ ndb_error= err;
+ DBUG_PRINT("info", ("ndb_error.code: %u", ndb_error.code));
+ }
+ goto err;
+ }
+ DBUG_PRINT("info", ("Found table %s", tab->getName()));
+
+ len= tab->getFrmLength();
+ if (len == 0 || tab->getFrmData() == NULL)
+ {
+ DBUG_PRINT("error", ("No frm data found."));
+ error= 1;
+ goto err;
+ }
+
+ if (unpackfrm(&data, &len, (uchar*) tab->getFrmData()))
+ {
+ DBUG_PRINT("error", ("Could not unpack table"));
+ error= 1;
+ goto err;
+ }
}
*frmlen= len;
*frmblob= data;
+ if (share)
+ {
+ /* ndb_share reference temporary free */
+ DBUG_PRINT("NDB_SHARE", ("%s temporary free use_count: %u",
+ share->key, share->use_count));
+ free_share(&share);
+ }
+
DBUG_RETURN(0);
+err:
+ my_free((char*)data, MYF(MY_ALLOW_ZERO_PTR));
+ if (share)
+ {
+ /* ndb_share reference temporary free */
+ DBUG_PRINT("NDB_SHARE", ("%s temporary free use_count: %u",
+ share->key, share->use_count));
+ free_share(&share);
+ }
+ if (ndb_error.code)
+ {
+ ERR_RETURN(ndb_error);
+ }
+ DBUG_RETURN(error);
}
-/*
- Check if a table exists in NDB
-
- */
+/**
+ Check if a table exists in NDB.
+*/
-int ndbcluster_table_exists_in_engine(THD* thd, const char *db, const char *name)
+int ndbcluster_table_exists_in_engine(handlerton *hton, THD* thd,
+ const char *db,
+ const char *name)
{
- const NDBTAB* tab;
Ndb* ndb;
DBUG_ENTER("ndbcluster_table_exists_in_engine");
- DBUG_PRINT("enter", ("db: %s, name: %s", db, name));
+ DBUG_PRINT("enter", ("db: %s name: %s", db, name));
if (!(ndb= check_ndb_in_thd(thd)))
DBUG_RETURN(HA_ERR_NO_CONNECTION);
- if (ndb->setDatabaseName(db))
- {
- ERR_RETURN(ndb->getNdbError());
- }
NDBDICT* dict= ndb->getDictionary();
- dict->set_local_table_data_size(sizeof(Ndb_local_table_statistics));
- dict->invalidateTable(name);
- if (!(tab= dict->getTable(name)))
- {
+ NdbDictionary::Dictionary::List list;
+ if (dict->listObjects(list, NdbDictionary::Object::UserTable) != 0)
ERR_RETURN(dict->getNdbError());
+ for (uint i= 0 ; i < list.count ; i++)
+ {
+ NdbDictionary::Dictionary::List::Element& elmt= list.elements[i];
+ if (my_strcasecmp(system_charset_info, elmt.database, db))
+ continue;
+ if (my_strcasecmp(system_charset_info, elmt.name, name))
+ continue;
+ DBUG_PRINT("info", ("Found table"));
+ DBUG_RETURN(HA_ERR_TABLE_EXIST);
}
-
- DBUG_PRINT("info", ("Found table %s", tab->getName()));
- DBUG_RETURN(HA_ERR_TABLE_EXIST);
+ DBUG_RETURN(HA_ERR_NO_SUCH_TABLE);
}
-extern "C" byte* tables_get_key(const char *entry, uint *length,
+extern "C" uchar* tables_get_key(const char *entry, size_t *length,
my_bool not_used __attribute__((unused)))
{
*length= strlen(entry);
- return (byte*) entry;
+ return (uchar*) entry;
}
-/*
+/**
Drop a database in NDB Cluster
- */
-int ndbcluster_drop_database(const char *path)
+ @note
+ add a dummy void function, since stupid handlerton is returning void instead of int...
+*/
+int ndbcluster_drop_database_impl(const char *path)
{
DBUG_ENTER("ndbcluster_drop_database");
THD *thd= current_thd;
@@ -5354,25 +6818,28 @@ int ndbcluster_drop_database(const char *path)
DBUG_PRINT("enter", ("db: %s", dbname));
if (!(ndb= check_ndb_in_thd(thd)))
- DBUG_RETURN(HA_ERR_NO_CONNECTION);
+ DBUG_RETURN(-1);
// List tables in NDB
NDBDICT *dict= ndb->getDictionary();
if (dict->listObjects(list,
NdbDictionary::Object::UserTable) != 0)
- ERR_RETURN(dict->getNdbError());
+ DBUG_RETURN(-1);
for (i= 0 ; i < list.count ; i++)
{
- NdbDictionary::Dictionary::List::Element& t= list.elements[i];
- DBUG_PRINT("info", ("Found %s/%s in NDB", t.database, t.name));
+ NdbDictionary::Dictionary::List::Element& elmt= list.elements[i];
+ DBUG_PRINT("info", ("Found %s/%s in NDB", elmt.database, elmt.name));
// Add only tables that belongs to db
- if (my_strcasecmp(system_charset_info, t.database, dbname))
+ if (my_strcasecmp(system_charset_info, elmt.database, dbname))
continue;
- DBUG_PRINT("info", ("%s must be dropped", t.name));
- drop_list.push_back(thd->strdup(t.name));
+ DBUG_PRINT("info", ("%s must be dropped", elmt.name));
+ drop_list.push_back(thd->strdup(elmt.name));
}
// Drop any tables belonging to database
+ char full_path[FN_REFLEN];
+ char *tmp= full_path +
+ build_table_filename(full_path, sizeof(full_path), dbname, "", "", 0);
if (ndb->setDatabaseName(dbname))
{
ERR_RETURN(ndb->getNdbError());
@@ -5380,32 +6847,200 @@ int ndbcluster_drop_database(const char *path)
List_iterator_fast<char> it(drop_list);
while ((tabname=it++))
{
- while (dict->dropTable(tabname))
+ 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();
- switch (err.status)
+ if (err.code != 709 && err.code != 723)
{
- case NdbError::TemporaryError:
- if (!thd->killed)
- continue; // retry indefinitly
- break;
- default:
- break;
- }
- if (err.code != 709) // 709: No such table existed
- {
- ERR_PRINT(err);
+ set_ndb_err(thd, err);
ret= ndb_to_mysql_error(&err);
}
- break;
}
+ VOID(pthread_mutex_unlock(&LOCK_open));
}
DBUG_RETURN(ret);
}
+static void ndbcluster_drop_database(handlerton *hton, char *path)
+{
+ DBUG_ENTER("ndbcluster_drop_database");
+#ifdef HAVE_NDB_BINLOG
+ /*
+ Don't allow drop database unless
+ schema distribution table is setup
+ */
+ if (!ndb_schema_share)
+ {
+ DBUG_PRINT("info", ("Schema distribution table not setup"));
+ DBUG_ASSERT(ndb_schema_share);
+ DBUG_VOID_RETURN;
+ }
+#endif
+ ndbcluster_drop_database_impl(path);
+#ifdef HAVE_NDB_BINLOG
+ char db[FN_REFLEN];
+ THD *thd= current_thd;
+ 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);
+#endif
+ DBUG_VOID_RETURN;
+}
+
+int ndb_create_table_from_engine(THD *thd, const char *db,
+ const char *table_name)
+{
+ 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;
+}
+
+/*
+ find all tables in ndb and discover those needed
+*/
+int ndbcluster_find_all_files(THD *thd)
+{
+ Ndb* ndb;
+ char key[FN_REFLEN];
+ NDBDICT *dict;
+ int unhandled, retries= 5, skipped;
+ DBUG_ENTER("ndbcluster_find_all_files");
+
+ if (!(ndb= check_ndb_in_thd(thd)))
+ DBUG_RETURN(HA_ERR_NO_CONNECTION);
+
+ dict= ndb->getDictionary();
+
+ LINT_INIT(unhandled);
+ LINT_INIT(skipped);
+ do
+ {
+ NdbDictionary::Dictionary::List list;
+ if (dict->listObjects(list, NdbDictionary::Object::UserTable) != 0)
+ ERR_RETURN(dict->getNdbError());
+ unhandled= 0;
+ skipped= 0;
+ retries--;
+ for (uint i= 0 ; i < list.count ; i++)
+ {
+ NDBDICT::List::Element& elmt= list.elements[i];
+ if (IS_TMP_PREFIX(elmt.name) || IS_NDB_BLOB_PREFIX(elmt.name))
+ {
+ DBUG_PRINT("info", ("Skipping %s.%s in NDB", elmt.database, elmt.name));
+ continue;
+ }
+ DBUG_PRINT("info", ("Found %s.%s in NDB", elmt.database, elmt.name));
+ if (elmt.state != NDBOBJ::StateOnline &&
+ elmt.state != NDBOBJ::StateBackup &&
+ elmt.state != NDBOBJ::StateBuilding)
+ {
+ sql_print_information("NDB: skipping setup table %s.%s, in state %d",
+ elmt.database, elmt.name, elmt.state);
+ skipped++;
+ continue;
+ }
+
+ ndb->setDatabaseName(elmt.database);
+ Ndb_table_guard ndbtab_g(dict, elmt.name);
+ const NDBTAB *ndbtab= ndbtab_g.get_table();
+ if (!ndbtab)
+ {
+ if (retries == 0)
+ sql_print_error("NDB: failed to setup table %s.%s, error: %d, %s",
+ elmt.database, elmt.name,
+ dict->getNdbError().code,
+ dict->getNdbError().message);
+ unhandled++;
+ continue;
+ }
+
+ if (ndbtab->getFrmLength() == 0)
+ continue;
+
+ /* check if database exists */
+ char *end= key +
+ build_table_filename(key, sizeof(key), elmt.database, "", "", 0);
+ if (my_access(key, F_OK))
+ {
+ /* no such database defined, skip table */
+ continue;
+ }
+ /* finalize construction of path */
+ end+= tablename_to_filename(elmt.name, end,
+ sizeof(key)-(end-key));
+ uchar *data= 0, *pack_data= 0;
+ size_t length, pack_length;
+ int discover= 0;
+ if (readfrm(key, &data, &length) ||
+ packfrm(data, length, &pack_data, &pack_length))
+ {
+ discover= 1;
+ sql_print_information("NDB: missing frm for %s.%s, discovering...",
+ elmt.database, elmt.name);
+ }
+ else if (cmp_frm(ndbtab, pack_data, pack_length))
+ {
+ /* ndb_share reference temporary */
+ NDB_SHARE *share= get_share(key, 0, FALSE);
+ if (share)
+ {
+ DBUG_PRINT("NDB_SHARE", ("%s temporary use_count: %u",
+ share->key, share->use_count));
+ }
+ if (!share || get_ndb_share_state(share) != NSS_ALTERED)
+ {
+ discover= 1;
+ sql_print_information("NDB: mismatch in frm for %s.%s, discovering...",
+ elmt.database, elmt.name);
+ }
+ if (share)
+ {
+ /* ndb_share reference temporary free */
+ DBUG_PRINT("NDB_SHARE", ("%s temporary free use_count: %u",
+ share->key, share->use_count));
+ free_share(&share);
+ }
+ }
+ my_free((char*) data, MYF(MY_ALLOW_ZERO_PTR));
+ my_free((char*) pack_data, MYF(MY_ALLOW_ZERO_PTR));
+
+ pthread_mutex_lock(&LOCK_open);
+ if (discover)
+ {
+ /* ToDo 4.1 database needs to be created if missing */
+ if (ndb_create_table_from_engine(thd, elmt.database, elmt.name))
+ {
+ /* ToDo 4.1 handle error */
+ }
+ }
+#ifdef HAVE_NDB_BINLOG
+ else
+ {
+ /* set up replication for this table */
+ ndbcluster_create_binlog_setup(ndb, key, end-key,
+ elmt.database, elmt.name,
+ TRUE);
+ }
+#endif
+ pthread_mutex_unlock(&LOCK_open);
+ }
+ }
+ while (unhandled && retries);
-int ndbcluster_find_files(THD *thd,const char *db,const char *path,
- const char *wild, bool dir, List<char> *files)
+ DBUG_RETURN(-(skipped + unhandled));
+}
+
+int ndbcluster_find_files(handlerton *hton, THD *thd,
+ const char *db,
+ const char *path,
+ const char *wild, bool dir, List<LEX_STRING> *files)
{
DBUG_ENTER("ndbcluster_find_files");
DBUG_PRINT("enter", ("db: %s", db));
@@ -5414,7 +7049,7 @@ int ndbcluster_find_files(THD *thd,const char *db,const char *path,
Ndb* ndb;
char name[FN_REFLEN];
HASH ndb_tables, ok_tables;
- NdbDictionary::Dictionary::List list;
+ NDBDICT::List list;
if (!(ndb= check_ndb_in_thd(thd)))
DBUG_RETURN(HA_ERR_NO_CONNECTION);
@@ -5445,11 +7080,16 @@ int ndbcluster_find_files(THD *thd,const char *db,const char *path,
for (i= 0 ; i < list.count ; i++)
{
- NdbDictionary::Dictionary::List::Element& t= list.elements[i];
- DBUG_PRINT("info", ("Found %s/%s in NDB", t.database, t.name));
+ NDBDICT::List::Element& elmt= list.elements[i];
+ if (IS_TMP_PREFIX(elmt.name) || IS_NDB_BLOB_PREFIX(elmt.name))
+ {
+ DBUG_PRINT("info", ("Skipping %s.%s in NDB", elmt.database, elmt.name));
+ continue;
+ }
+ DBUG_PRINT("info", ("Found %s/%s in NDB", elmt.database, elmt.name));
// Add only tables that belongs to db
- if (my_strcasecmp(system_charset_info, t.database, db))
+ if (my_strcasecmp(system_charset_info, elmt.database, db))
continue;
// Apply wildcard to list of tables in NDB
@@ -5457,121 +7097,183 @@ int ndbcluster_find_files(THD *thd,const char *db,const char *path,
{
if (lower_case_table_names)
{
- if (wild_case_compare(files_charset_info, t.name, wild))
+ if (wild_case_compare(files_charset_info, elmt.name, wild))
continue;
}
- else if (wild_compare(t.name,wild,0))
+ else if (wild_compare(elmt.name,wild,0))
continue;
}
- DBUG_PRINT("info", ("Inserting %s into ndb_tables hash", t.name));
- my_hash_insert(&ndb_tables, (byte*)thd->strdup(t.name));
+ DBUG_PRINT("info", ("Inserting %s into ndb_tables hash", elmt.name));
+ my_hash_insert(&ndb_tables, (uchar*)thd->strdup(elmt.name));
}
- char *file_name;
- List_iterator<char> it(*files);
+ LEX_STRING *file_name;
+ List_iterator<LEX_STRING> it(*files);
List<char> delete_list;
+ char *file_name_str;
while ((file_name=it++))
{
- bool file_on_disk= false;
- DBUG_PRINT("info", ("%s", file_name));
- if (hash_search(&ndb_tables, file_name, strlen(file_name)))
+ bool file_on_disk= FALSE;
+ DBUG_PRINT("info", ("%s", file_name->str));
+ if (hash_search(&ndb_tables, (uchar*) file_name->str, file_name->length))
{
- DBUG_PRINT("info", ("%s existed in NDB _and_ on disk ", file_name));
- file_on_disk= true;
+ build_table_filename(name, sizeof(name), 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;
}
// Check for .ndb file with this name
- (void)strxnmov(name, FN_REFLEN,
- mysql_data_home,"/",db,"/",file_name,ha_ndb_ext,NullS);
+ build_table_filename(name, sizeof(name), db, file_name->str, ha_ndb_ext, 0);
DBUG_PRINT("info", ("Check access for %s", name));
- if (access(name, F_OK))
+ if (my_access(name, F_OK))
{
DBUG_PRINT("info", ("%s did not exist on disk", name));
// .ndb file did not exist on disk, another table type
if (file_on_disk)
{
- // Ignore this ndb table
- gptr record= hash_search(&ndb_tables, file_name, strlen(file_name));
+ // Ignore this ndb table
+ uchar *record= hash_search(&ndb_tables, (uchar*) file_name->str,
+ file_name->length);
DBUG_ASSERT(record);
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",
- db, file_name);
+ db, file_name->str);
}
continue;
}
if (file_on_disk)
{
// File existed in NDB and as frm file, put in ok_tables list
- my_hash_insert(&ok_tables, (byte*)file_name);
+ my_hash_insert(&ok_tables, (uchar*) file_name->str);
continue;
}
DBUG_PRINT("info", ("%s existed on disk", name));
// The .ndb file exists on disk, but it's not in list of tables in ndb
// Verify that handler agrees table is gone.
- if (ndbcluster_table_exists_in_engine(thd, db, file_name) == HA_ERR_NO_SUCH_TABLE)
+ if (ndbcluster_table_exists_in_engine(hton, thd, db, file_name->str) ==
+ HA_ERR_NO_SUCH_TABLE)
{
- DBUG_PRINT("info", ("NDB says %s does not exists", file_name));
+ DBUG_PRINT("info", ("NDB says %s does not exists", file_name->str));
it.remove();
// Put in list of tables to remove from disk
- delete_list.push_back(thd->strdup(file_name));
+ delete_list.push_back(thd->strdup(file_name->str));
}
}
+#ifdef HAVE_NDB_BINLOG
+ /* setup logging to binlog for all discovered tables */
+ {
+ char *end, *end1= name +
+ build_table_filename(name, sizeof(name), db, "", "", 0);
+ for (i= 0; i < ok_tables.records; i++)
+ {
+ file_name_str= (char*)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
+
// Check for new files to discover
DBUG_PRINT("info", ("Checking for new files to discover"));
List<char> create_list;
for (i= 0 ; i < ndb_tables.records ; i++)
{
- file_name= hash_element(&ndb_tables, i);
- if (!hash_search(&ok_tables, file_name, strlen(file_name)))
+ file_name_str= (char*) hash_element(&ndb_tables, i);
+ if (!hash_search(&ok_tables, (uchar*) file_name_str, strlen(file_name_str)))
{
- DBUG_PRINT("info", ("%s must be discovered", file_name));
- // File is in list of ndb tables and not in ok_tables
- // This table need to be created
- create_list.push_back(thd->strdup(file_name));
+ build_table_filename(name, sizeof(name), db, file_name_str, reg_ext, 0);
+ if (my_access(name, F_OK))
+ {
+ DBUG_PRINT("info", ("%s must be discovered", file_name_str));
+ // File is in list of ndb tables and not in ok_tables
+ // This table need to be created
+ create_list.push_back(thd->strdup(file_name_str));
+ }
}
}
- // Lock mutex before deleting and creating frm files
- pthread_mutex_lock(&LOCK_open);
-
if (!global_read_lock)
{
// Delete old files
List_iterator_fast<char> it3(delete_list);
- while ((file_name=it3++))
+ while ((file_name_str= it3++))
{
- DBUG_PRINT("info", ("Remove table %s/%s", db, file_name));
+ 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;
+ table_list.alias= table_list.table_name= (char*)file_name_str;
(void)mysql_rm_table_part2(thd, &table_list,
- /* if_exists */ FALSE,
- /* drop_temporary */ FALSE,
- /* drop_view */ FALSE,
- /* dont_log_query*/ TRUE);
+ FALSE, /* if_exists */
+ FALSE, /* drop_temporary */
+ FALSE, /* drop_view */
+ TRUE /* dont_log_query*/);
+
/* Clear error message that is returned when table is deleted */
thd->clear_error();
}
}
+ pthread_mutex_lock(&LOCK_open);
// Create new files
List_iterator_fast<char> it2(create_list);
- while ((file_name=it2++))
+ while ((file_name_str=it2++))
{
- DBUG_PRINT("info", ("Table %s need discovery", file_name));
- if (ha_create_table_from_engine(thd, db, file_name) == 0)
- files->push_back(thd->strdup(file_name));
+ DBUG_PRINT("info", ("Table %s need discovery", file_name_str));
+ if (ndb_create_table_from_engine(thd, db, file_name_str) == 0)
+ {
+ LEX_STRING *tmp_file_name= 0;
+ tmp_file_name= thd->make_lex_string(tmp_file_name, file_name_str,
+ strlen(file_name_str), TRUE);
+ files->push_back(tmp_file_name);
+ }
}
- pthread_mutex_unlock(&LOCK_open);
-
+ pthread_mutex_unlock(&LOCK_open);
+
hash_free(&ok_tables);
hash_free(&ndb_tables);
+
+ // Delete schema file from files
+ if (!strcmp(db, NDB_REP_DB))
+ {
+ uint count = 0;
+ while (count++ < files->elements)
+ {
+ file_name = (LEX_STRING *)files->pop();
+ if (!strcmp(file_name->str, NDB_SCHEMA_TABLE))
+ {
+ DBUG_PRINT("info", ("skip %s.%s table, it should be hidden to user",
+ NDB_REP_DB, NDB_SCHEMA_TABLE));
+ continue;
+ }
+ files->push_back(file_name);
+ }
+ }
} // extra bracket to avoid gcc 2.95.3 warning
DBUG_RETURN(0);
}
@@ -5585,17 +7287,73 @@ int ndbcluster_find_files(THD *thd,const char *db,const char *path,
/* Call back after cluster connect */
static int connect_callback()
{
+ pthread_mutex_lock(&LOCK_ndb_util_thread);
update_status_variables(g_ndb_cluster_connection);
+
+ uint node_id, i= 0;
+ Ndb_cluster_connection_node_iter node_iter;
+ memset((void *)g_node_id_map, 0xFFFF, sizeof(g_node_id_map));
+ 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);
return 0;
}
-bool ndbcluster_init()
+extern int ndb_dictionary_is_mysqld;
+extern pthread_mutex_t LOCK_plugin;
+
+static int ndbcluster_init(void *p)
{
int res;
DBUG_ENTER("ndbcluster_init");
- if (have_ndbcluster != SHOW_OPTION_YES)
- goto ndbcluster_init_error;
+ if (ndbcluster_inited)
+ DBUG_RETURN(FALSE);
+
+ /*
+ Below we create new THD's. They'll need LOCK_plugin, but it's taken now by
+ plugin initialization code. Release it to avoid deadlocks. It's safe, as
+ there're no threads that may concurrently access plugin control structures.
+ */
+ pthread_mutex_unlock(&LOCK_plugin);
+
+ 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);
+ ndb_util_thread_running= -1;
+ ndbcluster_terminating= 0;
+ ndb_dictionary_is_mysqld= 1;
+ ndbcluster_hton= (handlerton *)p;
+
+ {
+ handlerton *h= ndbcluster_hton;
+ h->state= SHOW_OPTION_YES;
+ h->db_type= DB_TYPE_NDBCLUSTER;
+ h->close_connection= ndbcluster_close_connection;
+ h->commit= ndbcluster_commit;
+ h->rollback= ndbcluster_rollback;
+ h->create= ndbcluster_create_handler; /* Create a new handler */
+ h->drop_database= ndbcluster_drop_database; /* Drop a database */
+ h->panic= ndbcluster_end; /* Panic call */
+ h->show_status= ndbcluster_show_status; /* Show status */
+ 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;
+#ifdef HAVE_NDB_BINLOG
+ ndbcluster_binlog_init_handlerton();
+#endif
+ h->flags= HTON_CAN_RECREATE | HTON_TEMPORARY_NOT_SUPPORTED;
+ h->discover= ndbcluster_discover;
+ h->find_files= ndbcluster_find_files;
+ h->table_exists_in_engine= ndbcluster_table_exists_in_engine;
+ }
+
+ // Initialize ndb interface
+ ndb_init_internal();
// Set connectstring if specified
if (opt_ndbcluster_connectstring != 0)
@@ -5623,7 +7381,6 @@ bool ndbcluster_init()
my_errno= HA_ERR_OUT_OF_MEM;
goto ndbcluster_init_error;
}
- g_ndb->getDictionary()->set_local_table_data_size(sizeof(Ndb_local_table_statistics));
if (g_ndb->init() != 0)
{
ERR_PRINT (g_ndb->getNdbError());
@@ -5665,10 +7422,11 @@ bool ndbcluster_init()
(void) hash_init(&ndbcluster_open_tables,system_charset_info,32,0,0,
(hash_get_key) ndbcluster_get_key,0,0);
- 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);
-
+#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
@@ -5680,9 +7438,29 @@ bool ndbcluster_init()
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);
goto ndbcluster_init_error;
}
+
+ /* Wait for the util thread to start */
+ pthread_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);
+ 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);
+ goto ndbcluster_init_error;
+ }
+
+ pthread_mutex_lock(&LOCK_plugin);
+
ndbcluster_inited= 1;
DBUG_RETURN(FALSE);
@@ -5693,29 +7471,48 @@ ndbcluster_init_error:
if (g_ndb_cluster_connection)
delete g_ndb_cluster_connection;
g_ndb_cluster_connection= NULL;
- have_ndbcluster= SHOW_OPTION_DISABLED; // If we couldn't use handler
- DBUG_RETURN(TRUE);
-}
+ ndbcluster_hton->state= SHOW_OPTION_DISABLED; // If we couldn't use handler
+ pthread_mutex_lock(&LOCK_plugin);
-/*
- End use of the NDB Cluster table handler
- - free all global variables allocated by
- ndbcluster_init()
-*/
+ DBUG_RETURN(TRUE);
+}
-bool ndbcluster_end()
+static int ndbcluster_end(handlerton *hton, ha_panic_function type)
{
DBUG_ENTER("ndbcluster_end");
if (!ndbcluster_inited)
DBUG_RETURN(0);
+ ndbcluster_inited= 0;
- // Kill ndb utility thread
- (void) pthread_mutex_lock(&LOCK_ndb_util_thread);
- DBUG_PRINT("exit",("killing ndb util thread: %lx", ndb_util_thread));
- (void) pthread_cond_signal(&COND_ndb_util_thread);
- (void) pthread_mutex_unlock(&LOCK_ndb_util_thread);
+ /* wait for util thread to finish */
+ sql_print_information("Stopping Cluster Utility thread");
+ pthread_mutex_lock(&LOCK_ndb_util_thread);
+ ndbcluster_terminating= 1;
+ pthread_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);
+
+
+#ifdef HAVE_NDB_BINLOG
+ {
+ pthread_mutex_lock(&ndbcluster_mutex);
+ while (ndbcluster_open_tables.records)
+ {
+ NDB_SHARE *share=
+ (NDB_SHARE*) 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);
+ }
+#endif
+ hash_free(&ndbcluster_open_tables);
if (g_ndb)
{
@@ -5738,40 +7535,58 @@ bool ndbcluster_end()
delete g_ndb_cluster_connection;
g_ndb_cluster_connection= NULL;
- hash_free(&ndbcluster_open_tables);
+ // 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);
- ndbcluster_inited= 0;
+ pthread_cond_destroy(&COND_ndb_util_ready);
DBUG_RETURN(0);
}
-/*
- Static error print function called from
- static handler method ndbcluster_commit
- and ndbcluster_rollback
+void ha_ndbcluster::print_error(int error, myf errflag)
+{
+ DBUG_ENTER("ha_ndbcluster::print_error");
+ DBUG_PRINT("enter", ("error: %d", error));
+
+ if (error == HA_ERR_NO_PARTITION_FOUND)
+ m_part_info->print_no_partition_found(table);
+ else
+ handler::print_error(error, errflag);
+ DBUG_VOID_RETURN;
+}
+
+
+/**
+ Static error print function called from static handler method
+ ndbcluster_commit and ndbcluster_rollback.
*/
void ndbcluster_print_error(int error, const NdbOperation *error_op)
{
DBUG_ENTER("ndbcluster_print_error");
- TABLE tab;
+ TABLE_SHARE share;
const char *tab_name= (error_op) ? error_op->getTableName() : "";
- tab.alias= (char *) tab_name;
- ha_ndbcluster error_handler(&tab);
- tab.file= &error_handler;
+ share.db.str= (char*) "";
+ share.db.length= 0;
+ share.table_name.str= (char *) tab_name;
+ share.table_name.length= strlen(tab_name);
+ ha_ndbcluster error_handler(ndbcluster_hton, &share);
error_handler.print_error(error, MYF(0));
DBUG_VOID_RETURN;
}
/**
- * Set a given location from full pathname to database name
- *
- */
+ Set a given location from full pathname to database name.
+*/
+
void ha_ndbcluster::set_dbname(const char *path_name, char *dbname)
{
- char *end, *ptr;
-
+ char *end, *ptr, *tmp_name;
+ char tmp_buff[FN_REFLEN];
+
+ tmp_name= tmp_buff;
/* Scan name from the end */
ptr= strend(path_name)-1;
while (ptr >= path_name && *ptr != '\\' && *ptr != '/') {
@@ -5783,23 +7598,24 @@ void ha_ndbcluster::set_dbname(const char *path_name, char *dbname)
ptr--;
}
uint name_len= end - ptr;
- memcpy(dbname, ptr + 1, name_len);
- dbname[name_len]= '\0';
+ memcpy(tmp_name, ptr + 1, name_len);
+ tmp_name[name_len]= '\0';
#ifdef __WIN__
/* Put to lower case */
- ptr= dbname;
+ ptr= tmp_name;
while (*ptr != '\0') {
*ptr= tolower(*ptr);
ptr++;
}
#endif
+ filename_to_tablename(tmp_name, dbname, FN_REFLEN);
}
-/*
- Set m_dbname from full pathname to table file
- */
+/**
+ Set m_dbname from full pathname to table file.
+*/
void ha_ndbcluster::set_dbname(const char *path_name)
{
@@ -5807,14 +7623,16 @@ void ha_ndbcluster::set_dbname(const char *path_name)
}
/**
- * Set a given location from full pathname to table file
- *
- */
+ Set a given location from full pathname to table file.
+*/
+
void
ha_ndbcluster::set_tabname(const char *path_name, char * tabname)
{
- char *end, *ptr;
-
+ char *end, *ptr, *tmp_name;
+ char tmp_buff[FN_REFLEN];
+
+ tmp_name= tmp_buff;
/* Scan name from the end */
end= strend(path_name)-1;
ptr= end;
@@ -5822,22 +7640,23 @@ ha_ndbcluster::set_tabname(const char *path_name, char * tabname)
ptr--;
}
uint name_len= end - ptr;
- memcpy(tabname, ptr + 1, end - ptr);
- tabname[name_len]= '\0';
+ memcpy(tmp_name, ptr + 1, end - ptr);
+ tmp_name[name_len]= '\0';
#ifdef __WIN__
/* Put to lower case */
- ptr= tabname;
+ ptr= tmp_name;
while (*ptr != '\0') {
*ptr= tolower(*ptr);
ptr++;
}
#endif
+ filename_to_tablename(tmp_name, tabname, FN_REFLEN);
}
-/*
- Set m_tabname from full pathname to table file
- */
+/**
+ Set m_tabname from full pathname to table file.
+*/
void ha_ndbcluster::set_tabname(const char *path_name)
{
@@ -5867,19 +7686,104 @@ ha_ndbcluster::records_in_range(uint inx, key_range *min_key,
(max_key && max_key->length == key_length)))
DBUG_RETURN(1);
+ if ((idx_type == PRIMARY_KEY_ORDERED_INDEX ||
+ idx_type == UNIQUE_ORDERED_INDEX ||
+ idx_type == ORDERED_INDEX) &&
+ m_index[inx].index_stat != NULL)
+ {
+ NDB_INDEX_DATA& d=m_index[inx];
+ const NDBINDEX* index= d.index;
+ Ndb* ndb=get_ndb();
+ NdbTransaction* trans=NULL;
+ NdbIndexScanOperation* op=NULL;
+ int res=0;
+ Uint64 rows;
+
+ do
+ {
+ // We must provide approx table rows
+ Uint64 table_rows=0;
+ Ndb_local_table_statistics *ndb_info= m_table_info;
+ if (ndb_info->records != ~(ha_rows)0 && ndb_info->records != 0)
+ {
+ table_rows = ndb_info->records;
+ DBUG_PRINT("info", ("use info->records: %lu", (ulong) table_rows));
+ }
+ else
+ {
+ Ndb_statistics stat;
+ if ((res=ndb_get_table_statistics(this, TRUE, ndb, m_table, &stat)))
+ break;
+ table_rows=stat.row_count;
+ DBUG_PRINT("info", ("use db row_count: %lu", (ulong) table_rows));
+ if (table_rows == 0) {
+ // Problem if autocommit=0
+#ifdef ndb_get_table_statistics_uses_active_trans
+ rows=0;
+ break;
+#endif
+ }
+ }
+
+ // Define scan op for the range
+ if ((trans=m_active_trans) == NULL ||
+ trans->commitStatus() != NdbTransaction::Started)
+ {
+ DBUG_PRINT("info", ("no active trans"));
+ if (! (trans=ndb->startTransaction()))
+ ERR_BREAK(ndb->getNdbError(), res);
+ }
+ if (! (op=trans->getNdbIndexScanOperation(index, (NDBTAB*)m_table)))
+ ERR_BREAK(trans->getNdbError(), res);
+ if ((op->readTuples(NdbOperation::LM_CommittedRead)) == -1)
+ ERR_BREAK(op->getNdbError(), res);
+ const key_range *keys[2]={ min_key, max_key };
+ if ((res=set_bounds(op, inx, TRUE, keys)) != 0)
+ break;
+
+ // Decide if db should be contacted
+ int flags=0;
+ if (d.index_stat_query_count < d.index_stat_cache_entries ||
+ (d.index_stat_update_freq != 0 &&
+ d.index_stat_query_count % d.index_stat_update_freq == 0))
+ {
+ DBUG_PRINT("info", ("force stat from db"));
+ flags|=NdbIndexStat::RR_UseDb;
+ }
+ if (d.index_stat->records_in_range(index, op, table_rows, &rows, flags) == -1)
+ ERR_BREAK(d.index_stat->getNdbError(), res);
+ d.index_stat_query_count++;
+ } while (0);
+
+ if (trans != m_active_trans && rows == 0)
+ rows = 1;
+ if (trans != m_active_trans && trans != NULL)
+ ndb->closeTransaction(trans);
+ if (res != 0)
+ DBUG_RETURN(HA_POS_ERROR);
+ DBUG_RETURN(rows);
+ }
+
DBUG_RETURN(10); /* Good guess when you don't know anything */
}
-ulong ha_ndbcluster::table_flags(void) const
+ulonglong ha_ndbcluster::table_flags(void) const
{
+ THD *thd= current_thd;
+ ulonglong f= m_table_flags;
if (m_ha_not_exact_count)
- return m_table_flags | HA_NOT_EXACT_COUNT;
- else
- return m_table_flags;
+ f= f & ~HA_STATS_RECORDS_IS_EXACT;
+ /*
+ To allow for logging of ndb tables during stmt based logging;
+ flag cabablity, but also turn off flag for OWN_BINLOGGING
+ */
+ if (thd->variables.binlog_format == BINLOG_FORMAT_STMT)
+ f= (f | HA_BINLOG_STMT_CAPABLE) & ~HA_HAS_OWN_BINLOGGING;
+ return f;
}
const char * ha_ndbcluster::table_type() const
{
- return("ndbcluster");
+ return("NDBCLUSTER");
}
uint ha_ndbcluster::max_supported_record_length() const
{
@@ -5909,10 +7813,6 @@ bool ha_ndbcluster::low_byte_first() const
return TRUE;
#endif
}
-bool ha_ndbcluster::has_transactions()
-{
- return TRUE;
-}
const char* ha_ndbcluster::index_type(uint key_number)
{
switch (get_index_type(key_number)) {
@@ -5937,23 +7837,25 @@ uint8 ha_ndbcluster::table_cache_type()
uint ndb_get_commitcount(THD *thd, char *dbname, char *tabname,
Uint64 *commit_count)
{
- DBUG_ENTER("ndb_get_commitcount");
-
char name[FN_REFLEN];
NDB_SHARE *share;
- (void)strxnmov(name, FN_REFLEN, "./",dbname,"/",tabname,NullS);
+ DBUG_ENTER("ndb_get_commitcount");
+
+ build_table_filename(name, sizeof(name), dbname, tabname, "", 0);
DBUG_PRINT("enter", ("name: %s", name));
pthread_mutex_lock(&ndbcluster_mutex);
if (!(share=(NDB_SHARE*) hash_search(&ndbcluster_open_tables,
- (byte*) name,
+ (uchar*) name,
strlen(name))))
{
pthread_mutex_unlock(&ndbcluster_mutex);
- DBUG_PRINT("info", ("Table %s not found in ndbcluster_open_tables",
- name));
+ DBUG_PRINT("info", ("Table %s not found in ndbcluster_open_tables", name));
DBUG_RETURN(1);
}
+ /* ndb_share reference temporary, free below */
share->use_count++;
+ DBUG_PRINT("NDB_SHARE", ("%s temporary use_count: %u",
+ share->key, share->use_count));
pthread_mutex_unlock(&ndbcluster_mutex);
pthread_mutex_lock(&share->mutex);
@@ -5968,7 +7870,10 @@ uint ndb_get_commitcount(THD *thd, char *dbname, char *tabname,
DBUG_PRINT("info", ("Getting commit_count: %s from share",
llstr(share->commit_count, buff)));
pthread_mutex_unlock(&share->mutex);
- free_share(share);
+ /* ndb_share reference temporary free */
+ DBUG_PRINT("NDB_SHARE", ("%s temporary free use_count: %u",
+ share->key, share->use_count));
+ free_share(&share);
DBUG_RETURN(0);
}
}
@@ -5984,10 +7889,17 @@ uint ndb_get_commitcount(THD *thd, char *dbname, char *tabname,
pthread_mutex_unlock(&share->mutex);
struct Ndb_statistics stat;
- if (ndb_get_table_statistics(NULL, false, ndb, tabname, &stat))
{
- free_share(share);
- DBUG_RETURN(1);
+ Ndb_table_guard ndbtab_g(ndb->getDictionary(), tabname);
+ if (ndbtab_g.get_table() == 0
+ || ndb_get_table_statistics(NULL, FALSE, ndb, ndbtab_g.get_table(), &stat))
+ {
+ /* ndb_share reference temporary free */
+ DBUG_PRINT("NDB_SHARE", ("%s temporary free use_count: %u",
+ share->key, share->use_count));
+ free_share(&share);
+ DBUG_RETURN(1);
+ }
}
pthread_mutex_lock(&share->mutex);
@@ -6007,36 +7919,38 @@ uint ndb_get_commitcount(THD *thd, char *dbname, char *tabname,
*commit_count= 0;
}
pthread_mutex_unlock(&share->mutex);
- free_share(share);
+ /* ndb_share reference temporary free */
+ DBUG_PRINT("NDB_SHARE", ("%s temporary free use_count: %u",
+ share->key, share->use_count));
+ free_share(&share);
DBUG_RETURN(0);
}
-/*
+/**
Check if a cached query can be used.
+
This is done by comparing the supplied engine_data to commit_count of
the table.
+
The commit_count is either retrieved from the share for the table, where
it has been cached by the util thread. If the util thread is not started,
NDB has to be contacetd to retrieve the commit_count, this will introduce
a small delay while waiting for NDB to answer.
- SYNOPSIS
- ndbcluster_cache_retrieval_allowed
- thd thread handle
- full_name concatenation of database name,
- the null character '\0', and the table
- name
- full_name_len length of the full name,
- i.e. len(dbname) + len(tablename) + 1
-
- engine_data parameter retrieved when query was first inserted into
- the cache. If the value of engine_data is changed,
- all queries for this table should be invalidated.
+ @param thd thread handle
+ @param full_name concatenation of database name,
+ the null character '\\0', and the table name
+ @param full_name_len length of the full name,
+ i.e. len(dbname) + len(tablename) + 1
+ @param engine_data parameter retrieved when query was first inserted into
+ the cache. If the value of engine_data is changed,
+ all queries for this table should be invalidated.
- RETURN VALUE
+ @retval
TRUE Yes, use the query from cache
+ @retval
FALSE No, don't use the cached query, and if engine_data
has changed, all queries for this table should be invalidated
@@ -6092,25 +8006,25 @@ ndbcluster_cache_retrieval_allowed(THD *thd,
/**
- Register a table for use in the query cache. Fetch the commit_count
- for the table and return it in engine_data, this will later be used
- to check if the table has changed, before the cached query is reused.
-
- SYNOPSIS
- ha_ndbcluster::can_query_cache_table
- thd thread handle
- full_name concatenation of database name,
- the null character '\0', and the table
- name
- full_name_len length of the full name,
- i.e. len(dbname) + len(tablename) + 1
- qc_engine_callback function to be called before using cache on this table
- engine_data out, commit_count for this table
-
- RETURN VALUE
+ Register a table for use in the query cache.
+
+ Fetch the commit_count for the table and return it in engine_data,
+ this will later be used to check if the table has changed, before
+ the cached query is reused.
+
+ @param thd thread handle
+ @param full_name concatenation of database name,
+ the null character '\\0', and the table name
+ @param full_name_len length of the full name,
+ i.e. len(dbname) + len(tablename) + 1
+ @param engine_callback function to be called before using cache on
+ this table
+ @param[out] engine_data commit_count for this table
+
+ @retval
TRUE Yes, it's ok to cahce this query
+ @retval
FALSE No, don't cach the query
-
*/
my_bool
@@ -6147,200 +8061,430 @@ ha_ndbcluster::register_query_cache_table(THD *thd,
}
-/*
+/**
Handling the shared NDB_SHARE structure that is needed to
provide table locking.
+
It's also used for sharing data with other NDB handlers
in the same MySQL Server. There is currently not much
data we want to or can share.
- */
+*/
-static byte* ndbcluster_get_key(NDB_SHARE *share,uint *length,
+static uchar *ndbcluster_get_key(NDB_SHARE *share, size_t *length,
my_bool not_used __attribute__((unused)))
{
- *length=share->table_name_length;
- return (byte*) share->table_name;
+ *length= share->key_length;
+ return (uchar*) share->key;
}
-static NDB_SHARE* get_share(const char *table_name)
+
+#ifndef DBUG_OFF
+
+static void print_share(const char* where, NDB_SHARE* share)
+{
+ fprintf(DBUG_FILE,
+ "%s %s.%s: use_count: %u, commit_count: %lu\n",
+ where, share->db, share->table_name, share->use_count,
+ (ulong) share->commit_count);
+ fprintf(DBUG_FILE,
+ " - key: %s, key_length: %d\n",
+ share->key, share->key_length);
+
+#ifdef HAVE_NDB_BINLOG
+ if (share->table)
+ fprintf(DBUG_FILE,
+ " - share->table: %p %s.%s\n",
+ share->table, share->table->s->db.str,
+ share->table->s->table_name.str);
+#endif
+}
+
+
+static void print_ndbcluster_open_tables()
{
- NDB_SHARE *share;
+ DBUG_LOCK_FILE;
+ 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));
+ fprintf(DBUG_FILE, "<ndbcluster_open_tables\n");
+ DBUG_UNLOCK_FILE;
+}
+
+#endif
+
+
+#define dbug_print_open_tables() \
+ DBUG_EXECUTE("info", \
+ print_ndbcluster_open_tables(););
+
+#define dbug_print_share(t, s) \
+ DBUG_LOCK_FILE; \
+ DBUG_EXECUTE("info", \
+ print_share((t), (s));); \
+ DBUG_UNLOCK_FILE;
+
+
+#ifdef HAVE_NDB_BINLOG
+/*
+ For some reason a share is still around, try to salvage the situation
+ by closing all cached tables. If the share still exists, there is an
+ error somewhere but only report this to the error log. Keep this
+ "trailing share" but rename it since there are still references to it
+ 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)
+*/
+int handle_trailing_share(NDB_SHARE *share)
+{
+ THD *thd= current_thd;
+ static ulong trailing_share_id= 0;
+ DBUG_ENTER("handle_trailing_share");
+
+ /* ndb_share reference temporary, free below */
+ ++share->use_count;
+ DBUG_PRINT("NDB_SHARE", ("%s temporary use_count: %u",
+ share->key, share->use_count));
+ pthread_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);
+
pthread_mutex_lock(&ndbcluster_mutex);
- uint length=(uint) strlen(table_name);
- if (!(share=(NDB_SHARE*) hash_search(&ndbcluster_open_tables,
- (byte*) table_name,
- length)))
+ /* 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 ((share=(NDB_SHARE *) my_malloc(sizeof(*share)+length+1,
- MYF(MY_WME | MY_ZEROFILL))))
- {
- share->table_name_length=length;
- share->table_name=(char*) (share+1);
- strmov(share->table_name,table_name);
- if (my_hash_insert(&ndbcluster_open_tables, (byte*) share))
- {
- pthread_mutex_unlock(&ndbcluster_mutex);
- my_free((gptr) share,0);
- return 0;
- }
- thr_lock_init(&share->lock);
- pthread_mutex_init(&share->mutex,MY_MUTEX_INIT_FAST);
- share->commit_count= 0;
- share->commit_count_lock= 0;
+ if (ndb_extra_logging)
+ sql_print_information("NDB_SHARE: trailing share "
+ "%s(connect_count: %u) "
+ "released by close_cached_tables at "
+ "connect_count: %u",
+ share->key,
+ share->connect_count,
+ g_ndb_cluster_connection->get_connect_count());
+ ndbcluster_real_free_share(&share);
+ DBUG_RETURN(0);
+ }
+
+ /*
+ share still exists, if share has not been dropped by server
+ release that share
+ */
+ if (share->state != NSS_DROPPED)
+ {
+ share->state= NSS_DROPPED;
+ /* ndb_share reference create free */
+ DBUG_PRINT("NDB_SHARE", ("%s create free use_count: %u",
+ share->key, share->use_count));
+ --share->use_count;
+
+ if (share->use_count == 0)
+ {
+ if (ndb_extra_logging)
+ sql_print_information("NDB_SHARE: trailing share "
+ "%s(connect_count: %u) "
+ "released after NSS_DROPPED check "
+ "at connect_count: %u",
+ share->key,
+ share->connect_count,
+ g_ndb_cluster_connection->get_connect_count());
+ ndbcluster_real_free_share(&share);
+ DBUG_RETURN(0);
}
- else
+ }
+
+ sql_print_warning("NDB_SHARE: %s already exists use_count=%d."
+ " Moving away for safety, but possible memleak.",
+ share->key, share->use_count);
+ dbug_print_open_tables();
+
+ /*
+ Ndb share has not been released as it should
+ */
+#ifdef NOT_YET
+ DBUG_ASSERT(FALSE);
+#endif
+
+ /*
+ This is probably an error. We can however save the situation
+ at the cost of a possible mem leak, by "renaming" the share
+ - First remove from hash
+ */
+ hash_delete(&ndbcluster_open_tables, (uchar*) share);
+
+ /*
+ now give it a new name, just a running number
+ if space is not enough allocate some more
+ */
+ {
+ const uint min_key_length= 10;
+ if (share->key_length < min_key_length)
{
- DBUG_PRINT("error", ("Failed to alloc share"));
- pthread_mutex_unlock(&ndbcluster_mutex);
- sql_print_error("get_share: my_malloc(%u) failed",
- (unsigned int)(sizeof(*share)+length+1));
- return 0;
+ share->key= (char*) alloc_root(&share->mem_root, min_key_length + 1);
+ share->key_length= min_key_length;
}
+ share->key_length=
+ my_snprintf(share->key, min_key_length + 1, "#leak%lu",
+ trailing_share_id++);
}
- share->use_count++;
+ /* Keep it for possible the future trailing free */
+ my_hash_insert(&ndbcluster_open_tables, (uchar*) share);
- DBUG_PRINT("share",
- ("table_name: %s length: %d use_count: %d commit_count: %lu",
- share->table_name, share->table_name_length, share->use_count,
- (ulong) share->commit_count));
- pthread_mutex_unlock(&ndbcluster_mutex);
- return share;
+ DBUG_RETURN(0);
}
-
-static void free_share(NDB_SHARE *share)
+/*
+ Rename share is used during rename table.
+*/
+static int rename_share(NDB_SHARE *share, const char *new_key)
{
+ NDB_SHARE *tmp;
pthread_mutex_lock(&ndbcluster_mutex);
- if (!--share->use_count)
+ 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)))
+ handle_trailing_share(tmp);
+
+ /* remove the share from hash */
+ hash_delete(&ndbcluster_open_tables, (uchar*) share);
+ dbug_print_open_tables();
+
+ /* save old stuff if insert should fail */
+ uint old_length= share->key_length;
+ char *old_key= share->key;
+
+ /*
+ now allocate and set the new key, db etc
+ enough space for key, db, and table_name
+ */
+ share->key= (char*) alloc_root(&share->mem_root, 2 * (new_length + 1));
+ strmov(share->key, new_key);
+ share->key_length= new_length;
+
+ if (my_hash_insert(&ndbcluster_open_tables, (uchar*) share))
{
- hash_delete(&ndbcluster_open_tables, (byte*) share);
- thr_lock_delete(&share->lock);
- pthread_mutex_destroy(&share->mutex);
- my_free((gptr) share, MYF(0));
+ // ToDo free the allocated stuff above?
+ DBUG_PRINT("error", ("rename_share: my_hash_insert %s failed",
+ share->key));
+ share->key= old_key;
+ share->key_length= old_length;
+ if (my_hash_insert(&ndbcluster_open_tables, (uchar*) share))
+ {
+ sql_print_error("rename_share: failed to recover %s", share->key);
+ DBUG_PRINT("error", ("rename_share: my_hash_insert %s failed",
+ share->key));
+ }
+ dbug_print_open_tables();
+ pthread_mutex_unlock(&ndbcluster_mutex);
+ return -1;
}
+ dbug_print_open_tables();
+
+ share->db= share->key + new_length + 1;
+ ha_ndbcluster::set_dbname(new_key, share->db);
+ share->table_name= share->db + strlen(share->db) + 1;
+ ha_ndbcluster::set_tabname(new_key, share->table_name);
+
+ dbug_print_share("rename_share:", share);
+ if (share->table)
+ {
+ if (share->op == 0)
+ {
+ share->table->s->db.str= share->db;
+ share->table->s->db.length= strlen(share->db);
+ share->table->s->table_name.str= share->table_name;
+ share->table->s->table_name.length= strlen(share->table_name);
+ }
+ }
+ /* else rename will be handled when the ALTER event comes */
+ share->old_names= old_key;
+ // ToDo free old_names after ALTER EVENT
+
pthread_mutex_unlock(&ndbcluster_mutex);
+ return 0;
}
+#endif
+
+/*
+ Increase refcount on existing share.
+ Always returns share and cannot fail.
+*/
+NDB_SHARE *ndbcluster_get_share(NDB_SHARE *share)
+{
+ pthread_mutex_lock(&ndbcluster_mutex);
+ share->use_count++;
+ dbug_print_open_tables();
+ dbug_print_share("ndbcluster_get_share:", share);
+ pthread_mutex_unlock(&ndbcluster_mutex);
+ return share;
+}
/*
- Internal representation of the frm blob
-
-*/
+ Get a share object for key
-struct frm_blob_struct
-{
- struct frm_blob_header
- {
- uint ver; // Version of header
- uint orglen; // Original length of compressed data
- uint complen; // Compressed length of data, 0=uncompressed
- } head;
- char data[1];
-};
+ Returns share for key, and increases the refcount on the share.
+ create_if_not_exists == TRUE:
+ creates share if it does not alreade exist
+ returns 0 only due to out of memory, and then sets my_error
+ create_if_not_exists == FALSE:
+ returns 0 if share does not exist
-static int packfrm(const void *data, uint len,
- const void **pack_data, uint *pack_len)
+ have_lock == TRUE, pthread_mutex_lock(&ndbcluster_mutex) already taken
+*/
+
+NDB_SHARE *ndbcluster_get_share(const char *key, TABLE *table,
+ bool create_if_not_exists,
+ bool have_lock)
{
- int error;
- ulong org_len, comp_len;
- uint blob_len;
- frm_blob_struct* blob;
- DBUG_ENTER("packfrm");
- DBUG_PRINT("enter", ("data: 0x%lx len: %d", (long) data, len));
-
- error= 1;
- org_len= len;
- if (my_compress((byte*)data, &org_len, &comp_len))
- {
- sql_print_error("packfrm: my_compress(org_len: %u)",
- (unsigned int)org_len);
- goto err;
- }
+ NDB_SHARE *share;
+ uint length= (uint) strlen(key);
+ DBUG_ENTER("ndbcluster_get_share");
+ DBUG_PRINT("enter", ("key: '%s'", key));
- DBUG_PRINT("info", ("org_len: %lu comp_len: %lu", org_len, comp_len));
- DBUG_DUMP("compressed", (uchar*)data, org_len);
-
- error= 2;
- blob_len= sizeof(frm_blob_struct::frm_blob_header)+org_len;
- if (!(blob= (frm_blob_struct*) my_malloc(blob_len,MYF(MY_WME))))
+ if (!have_lock)
+ pthread_mutex_lock(&ndbcluster_mutex);
+ if (!(share= (NDB_SHARE*) hash_search(&ndbcluster_open_tables,
+ (uchar*) key,
+ length)))
{
- sql_print_error("packfrm: my_malloc(%u)", blob_len);
- goto err;
+ if (!create_if_not_exists)
+ {
+ DBUG_PRINT("error", ("get_share: %s does not exist", key));
+ if (!have_lock)
+ pthread_mutex_unlock(&ndbcluster_mutex);
+ DBUG_RETURN(0);
+ }
+ if ((share= (NDB_SHARE*) my_malloc(sizeof(*share),
+ MYF(MY_WME | MY_ZEROFILL))))
+ {
+ MEM_ROOT **root_ptr=
+ my_pthread_getspecific_ptr(MEM_ROOT**, THR_MALLOC);
+ MEM_ROOT *old_root= *root_ptr;
+ init_sql_alloc(&share->mem_root, 1024, 0);
+ *root_ptr= &share->mem_root; // remember to reset before return
+ share->state= NSS_INITIAL;
+ /* enough space for key, db, and table_name */
+ share->key= (char*) alloc_root(*root_ptr, 2 * (length + 1));
+ share->key_length= length;
+ strmov(share->key, key);
+ if (my_hash_insert(&ndbcluster_open_tables, (uchar*) share))
+ {
+ free_root(&share->mem_root, MYF(0));
+ my_free((uchar*) share, 0);
+ *root_ptr= old_root;
+ if (!have_lock)
+ pthread_mutex_unlock(&ndbcluster_mutex);
+ DBUG_RETURN(0);
+ }
+ thr_lock_init(&share->lock);
+ pthread_mutex_init(&share->mutex, MY_MUTEX_INIT_FAST);
+ share->commit_count= 0;
+ share->commit_count_lock= 0;
+ share->db= share->key + length + 1;
+ ha_ndbcluster::set_dbname(key, share->db);
+ share->table_name= share->db + strlen(share->db) + 1;
+ ha_ndbcluster::set_tabname(key, share->table_name);
+#ifdef HAVE_NDB_BINLOG
+ if (ndbcluster_binlog_init_share(share, table))
+ {
+ DBUG_PRINT("error", ("get_share: %s could not init share", key));
+ ndbcluster_real_free_share(&share);
+ *root_ptr= old_root;
+ if (!have_lock)
+ pthread_mutex_unlock(&ndbcluster_mutex);
+ DBUG_RETURN(0);
+ }
+#endif
+ *root_ptr= old_root;
+ }
+ else
+ {
+ DBUG_PRINT("error", ("get_share: failed to alloc share"));
+ if (!have_lock)
+ pthread_mutex_unlock(&ndbcluster_mutex);
+ my_error(ER_OUTOFMEMORY, MYF(0), sizeof(*share));
+ DBUG_RETURN(0);
+ }
}
- // Store compressed blob in machine independent format
- int4store((char*)(&blob->head.ver), 1);
- int4store((char*)(&blob->head.orglen), comp_len);
- int4store((char*)(&blob->head.complen), org_len);
-
- // Copy frm data into blob, already in machine independent format
- memcpy(blob->data, data, org_len);
-
- *pack_data= blob;
- *pack_len= blob_len;
- error= 0;
-
- DBUG_PRINT("exit", ("pack_data: 0x%lx pack_len: %d", (long) *pack_data,
- *pack_len));
-err:
- DBUG_RETURN(error);
-
+ share->use_count++;
+
+ dbug_print_open_tables();
+ dbug_print_share("ndbcluster_get_share:", share);
+ if (!have_lock)
+ pthread_mutex_unlock(&ndbcluster_mutex);
+ DBUG_RETURN(share);
}
-static int unpackfrm(const void **unpack_data, uint *unpack_len,
- const void *pack_data)
+void ndbcluster_real_free_share(NDB_SHARE **share)
{
- const frm_blob_struct *blob= (frm_blob_struct*)pack_data;
- byte *data;
- ulong complen, orglen, ver;
- DBUG_ENTER("unpackfrm");
- DBUG_PRINT("enter", ("pack_data: 0x%lx", (long) pack_data));
+ DBUG_ENTER("ndbcluster_real_free_share");
+ dbug_print_share("ndbcluster_real_free_share:", *share);
- complen= uint4korr((char*)&blob->head.complen);
- orglen= uint4korr((char*)&blob->head.orglen);
- ver= uint4korr((char*)&blob->head.ver);
-
- DBUG_PRINT("blob",("ver: %lu complen: %lu orglen: %lu",
- ver,complen,orglen));
- DBUG_DUMP("blob->data", (uchar*) blob->data, complen);
-
- if (ver != 1)
- {
- sql_print_error("unpackfrm: ver != 1");
- DBUG_RETURN(1);
- }
- if (!(data= my_malloc(max(orglen, complen), MYF(MY_WME))))
- {
- sql_print_error("unpackfrm: my_malloc(%u)",
- (unsigned int)max(orglen, complen));
- DBUG_RETURN(HA_ERR_OUT_OF_MEM);
- }
- memcpy(data, blob->data, complen);
-
- if (my_uncompress(data, &complen, &orglen))
- {
- my_free((char*)data, MYF(0));
- sql_print_error("unpackfrm: my_uncompress(complen: %u, orglen: %u)",
- (unsigned int)complen, (unsigned int)orglen);
- DBUG_RETURN(3);
- }
+ hash_delete(&ndbcluster_open_tables, (uchar*) *share);
+ thr_lock_delete(&(*share)->lock);
+ pthread_mutex_destroy(&(*share)->mutex);
+
+#ifdef HAVE_NDB_BINLOG
+ if ((*share)->table)
+ {
+ // (*share)->table->mem_root is freed by closefrm
+ closefrm((*share)->table, 0);
+ // (*share)->table_share->mem_root is freed by free_table_share
+ free_table_share((*share)->table_share);
+#ifndef DBUG_OFF
+ bzero((uchar*)(*share)->table_share, sizeof(*(*share)->table_share));
+ bzero((uchar*)(*share)->table, sizeof(*(*share)->table));
+ (*share)->table_share= 0;
+ (*share)->table= 0;
+#endif
+ }
+#endif
+ free_root(&(*share)->mem_root, MYF(0));
+ my_free((uchar*) *share, MYF(0));
+ *share= 0;
- *unpack_data= data;
- *unpack_len= complen;
+ dbug_print_open_tables();
+ DBUG_VOID_RETURN;
+}
- DBUG_PRINT("exit", ("frmdata: 0x%lx len: %d", (long) *unpack_data,
- *unpack_len));
- DBUG_RETURN(0);
+void ndbcluster_free_share(NDB_SHARE **share, bool have_lock)
+{
+ if (!have_lock)
+ pthread_mutex_lock(&ndbcluster_mutex);
+ if ((*share)->util_lock == current_thd)
+ (*share)->util_lock= 0;
+ if (!--(*share)->use_count)
+ {
+ ndbcluster_real_free_share(share);
+ }
+ else
+ {
+ dbug_print_open_tables();
+ dbug_print_share("ndbcluster_free_share:", *share);
+ }
+ if (!have_lock)
+ pthread_mutex_unlock(&ndbcluster_mutex);
}
+
static
int
-ndb_get_table_statistics(ha_ndbcluster* file, bool report_error, Ndb* ndb,
- const char* table,
+ndb_get_table_statistics(ha_ndbcluster* file, bool report_error, Ndb* ndb, const NDBTAB *ndbtab,
struct Ndb_statistics * ndbstat)
{
NdbTransaction* pTrans;
@@ -6352,11 +8496,13 @@ ndb_get_table_statistics(ha_ndbcluster* file, bool report_error, Ndb* ndb,
char buff[22], buff2[22], buff3[22], buff4[22];
#endif
DBUG_ENTER("ndb_get_table_statistics");
- DBUG_PRINT("enter", ("table: %s", table));
+ DBUG_PRINT("enter", ("table: %s", ndbtab->getName()));
+
+ DBUG_ASSERT(ndbtab != 0);
do
{
- Uint64 rows, commits, mem;
+ Uint64 rows, commits, fixed_mem, var_mem;
Uint32 size;
Uint32 count= 0;
Uint64 sum_rows= 0;
@@ -6372,7 +8518,7 @@ ndb_get_table_statistics(ha_ndbcluster* file, bool report_error, Ndb* ndb,
goto retry;
}
- if ((pOp= pTrans->getNdbScanOperation(table)) == NULL)
+ if ((pOp= pTrans->getNdbScanOperation(ndbtab)) == NULL)
{
error= pTrans->getNdbError();
goto retry;
@@ -6393,10 +8539,13 @@ ndb_get_table_statistics(ha_ndbcluster* file, bool report_error, Ndb* ndb,
pOp->getValue(NdbDictionary::Column::ROW_COUNT, (char*)&rows);
pOp->getValue(NdbDictionary::Column::COMMIT_COUNT, (char*)&commits);
pOp->getValue(NdbDictionary::Column::ROW_SIZE, (char*)&size);
- pOp->getValue(NdbDictionary::Column::FRAGMENT_MEMORY, (char*)&mem);
+ pOp->getValue(NdbDictionary::Column::FRAGMENT_FIXED_MEMORY,
+ (char*)&fixed_mem);
+ pOp->getValue(NdbDictionary::Column::FRAGMENT_VARSIZED_MEMORY,
+ (char*)&var_mem);
if (pTrans->execute(NdbTransaction::NoCommit,
- NdbTransaction::AbortOnError,
+ NdbOperation::AbortOnError,
TRUE) == -1)
{
error= pTrans->getNdbError();
@@ -6409,7 +8558,7 @@ ndb_get_table_statistics(ha_ndbcluster* file, bool report_error, Ndb* ndb,
sum_commits+= commits;
if (sum_row_size < size)
sum_row_size= size;
- sum_mem+= mem;
+ sum_mem+= fixed_mem + var_mem;
count++;
}
@@ -6464,6 +8613,7 @@ retry:
my_sleep(retry_sleep);
continue;
}
+ set_ndb_err(current_thd, error);
break;
} while(1);
DBUG_PRINT("exit", ("failed, reterr: %u, NdbError %u(%s)", reterr,
@@ -6471,22 +8621,22 @@ retry:
DBUG_RETURN(reterr);
}
-/*
+/**
Create a .ndb file to serve as a placeholder indicating
- that the table with this name is a ndb table
+ that the table with this name is a ndb table.
*/
-int ha_ndbcluster::write_ndb_file()
+int ha_ndbcluster::write_ndb_file(const char *name)
{
File file;
bool error=1;
char path[FN_REFLEN];
DBUG_ENTER("write_ndb_file");
- DBUG_PRINT("enter", ("db: %s, name: %s", m_dbname, m_tabname));
+ DBUG_PRINT("enter", ("name: %s", name));
- (void)strxnmov(path, FN_REFLEN,
- mysql_data_home,"/",m_dbname,"/",m_tabname,ha_ndb_ext,NullS);
+ (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)
{
@@ -6530,19 +8680,19 @@ ha_ndbcluster::null_value_index_search(KEY_MULTI_RANGE *ranges,
KEY* key_info= table->key_info + active_index;
KEY_MULTI_RANGE *range= ranges;
ulong reclength= table->s->reclength;
- byte *curr= (byte*)buffer->buffer;
- byte *end_of_buffer= (byte*)buffer->buffer_end;
+ uchar *curr= (uchar*)buffer->buffer;
+ uchar *end_of_buffer= (uchar*)buffer->buffer_end;
for (; range<end_range && curr+reclength <= end_of_buffer;
range++)
{
- const byte *key= range->start_key.key;
+ const uchar *key= range->start_key.key;
uint key_len= range->start_key.length;
if (check_null_in_key(key_info, key, key_len))
- DBUG_RETURN(true);
+ DBUG_RETURN(TRUE);
curr += reclength;
}
- DBUG_RETURN(false);
+ DBUG_RETURN(FALSE);
}
int
@@ -6552,10 +8702,11 @@ ha_ndbcluster::read_multi_range_first(KEY_MULTI_RANGE **found_range_p,
bool sorted,
HANDLER_BUFFER *buffer)
{
+ m_write_op= FALSE;
int res;
KEY* key_info= table->key_info + active_index;
NDB_INDEX_TYPE cur_index_type= get_index_type(active_index);
- ulong reclength= table->s->reclength;
+ ulong reclength= table_share->reclength;
NdbOperation* op;
Thd_ndb *thd_ndb= get_thd_ndb(current_thd);
DBUG_ENTER("ha_ndbcluster::read_multi_range_first");
@@ -6563,8 +8714,8 @@ ha_ndbcluster::read_multi_range_first(KEY_MULTI_RANGE **found_range_p,
/**
* blobs and unique hash index with NULL can't be batched currently
*/
- if (uses_blob_value(m_retrieve_all_fields) ||
- (cur_index_type == UNIQUE_INDEX &&
+ if (uses_blob_value() ||
+ (cur_index_type == UNIQUE_INDEX &&
has_null_in_unique_index(active_index) &&
null_value_index_search(ranges, ranges+range_count, buffer))
|| m_delete_cannot_batch || m_update_cannot_batch)
@@ -6579,7 +8730,7 @@ ha_ndbcluster::read_multi_range_first(KEY_MULTI_RANGE **found_range_p,
thd_ndb->query_state|= NDB_QUERY_MULTI_READ_RANGE;
m_disable_multi_read= FALSE;
- /**
+ /*
* Copy arguments into member variables
*/
m_multi_ranges= ranges;
@@ -6588,7 +8739,7 @@ ha_ndbcluster::read_multi_range_first(KEY_MULTI_RANGE **found_range_p,
multi_range_sorted= sorted;
multi_range_buffer= buffer;
- /**
+ /*
* read multi range will read ranges as follows (if not ordered)
*
* input read order
@@ -6601,59 +8752,86 @@ ha_ndbcluster::read_multi_range_first(KEY_MULTI_RANGE **found_range_p,
* pk-op 6 pk-ok 6
*/
- /**
+ /*
* Variables for loop
*/
- byte *curr= (byte*)buffer->buffer;
- byte *end_of_buffer= (byte*)buffer->buffer_end;
+ uchar *curr= (uchar*)buffer->buffer;
+ uchar *end_of_buffer= (uchar*)buffer->buffer_end;
NdbOperation::LockMode lm=
(NdbOperation::LockMode)get_ndb_lock_type(m_lock.type);
bool need_pk = (lm == NdbOperation::LM_Read);
- const NDBTAB *tab= (const NDBTAB *) m_table;
- const NDBINDEX *unique_idx= (NDBINDEX *) m_index[active_index].unique_index;
- const NDBINDEX *idx= (NDBINDEX *) m_index[active_index].index;
+ const NDBTAB *tab= m_table;
+ const NDBINDEX *unique_idx= m_index[active_index].unique_index;
+ const NDBINDEX *idx= m_index[active_index].index;
const NdbOperation* lastOp= m_active_trans->getLastDefinedOperation();
NdbIndexScanOperation* scanOp= 0;
for (; multi_range_curr<multi_range_end && curr+reclength <= end_of_buffer;
multi_range_curr++)
{
+ part_id_range part_spec;
+ if (m_use_partition_function)
+ {
+ get_partition_set(table, curr, active_index,
+ &multi_range_curr->start_key,
+ &part_spec);
+ DBUG_PRINT("info", ("part_spec.start_part: %u part_spec.end_part: %u",
+ part_spec.start_part, part_spec.end_part));
+ /*
+ If partition pruning has found no partition in set
+ we can skip this scan
+ */
+ if (part_spec.start_part > part_spec.end_part)
+ {
+ /*
+ We can skip this partition since the key won't fit into any
+ partition
+ */
+ curr += reclength;
+ multi_range_curr->range_flag |= SKIP_RANGE;
+ continue;
+ }
+ }
switch (cur_index_type) {
case PRIMARY_KEY_ORDERED_INDEX:
if (!(multi_range_curr->start_key.length == key_info->key_length &&
- multi_range_curr->start_key.flag == HA_READ_KEY_EXACT))
- goto range;
- /* fall through */
+ multi_range_curr->start_key.flag == HA_READ_KEY_EXACT))
+ goto range;
+ // else fall through
case PRIMARY_KEY_INDEX:
+ {
multi_range_curr->range_flag |= UNIQUE_RANGE;
if ((op= m_active_trans->getNdbOperation(tab)) &&
!op->readTuple(lm) &&
!set_primary_key(op, multi_range_curr->start_key.key) &&
!define_read_attrs(curr, op) &&
- (op->setAbortOption(AO_IgnoreError), TRUE))
+ (!m_use_partition_function ||
+ (op->setPartitionId(part_spec.start_part), TRUE)))
curr += reclength;
else
ERR_RETURN(op ? op->getNdbError() : m_active_trans->getNdbError());
break;
+ }
+ break;
case UNIQUE_ORDERED_INDEX:
if (!(multi_range_curr->start_key.length == key_info->key_length &&
- multi_range_curr->start_key.flag == HA_READ_KEY_EXACT &&
- !check_null_in_key(key_info, multi_range_curr->start_key.key,
- multi_range_curr->start_key.length)))
- goto range;
- /* fall through */
+ multi_range_curr->start_key.flag == HA_READ_KEY_EXACT &&
+ !check_null_in_key(key_info, multi_range_curr->start_key.key,
+ multi_range_curr->start_key.length)))
+ goto range;
+ // else fall through
case UNIQUE_INDEX:
+ {
multi_range_curr->range_flag |= UNIQUE_RANGE;
if ((op= m_active_trans->getNdbIndexOperation(unique_idx, tab)) &&
- !op->readTuple(lm) &&
- !set_index_key(op, key_info, multi_range_curr->start_key.key) &&
- !define_read_attrs(curr, op) &&
- (op->setAbortOption(AO_IgnoreError), TRUE))
- curr += reclength;
+ !op->readTuple(lm) &&
+ !set_index_key(op, key_info, multi_range_curr->start_key.key) &&
+ !define_read_attrs(curr, op))
+ curr += reclength;
else
- ERR_RETURN(op ? op->getNdbError() : m_active_trans->getNdbError());
+ ERR_RETURN(op ? op->getNdbError() : m_active_trans->getNdbError());
break;
- case ORDERED_INDEX:
- {
+ }
+ case ORDERED_INDEX: {
range:
multi_range_curr->range_flag &= ~(uint)UNIQUE_RANGE;
if (scanOp == 0)
@@ -6687,7 +8865,8 @@ ha_ndbcluster::read_multi_range_first(KEY_MULTI_RANGE **found_range_p,
const key_range *keys[2]= { &multi_range_curr->start_key,
&multi_range_curr->end_key };
- if ((res= set_bounds(scanOp, keys, multi_range_curr-ranges)))
+ if ((res= set_bounds(scanOp, active_index, FALSE, keys,
+ multi_range_curr-ranges)))
DBUG_RETURN(res);
break;
}
@@ -6700,36 +8879,36 @@ ha_ndbcluster::read_multi_range_first(KEY_MULTI_RANGE **found_range_p,
if (multi_range_curr != multi_range_end)
{
- /**
+ /*
* Mark that we're using entire buffer (even if might not) as
* we haven't read all ranges for some reason
* This as we don't want mysqld to reuse the buffer when we read
* the remaining ranges
*/
- buffer->end_of_used_area= (byte*)buffer->buffer_end;
+ buffer->end_of_used_area= (uchar*)buffer->buffer_end;
}
else
{
buffer->end_of_used_area= curr;
}
- /**
+ /*
* Set first operation in multi range
*/
m_current_multi_operation=
lastOp ? lastOp->next() : m_active_trans->getFirstDefinedOperation();
- if (!(res= execute_no_commit_ie(this, m_active_trans, true)))
+ if (!(res= execute_no_commit_ie(this, m_active_trans,true)))
{
m_multi_range_defined= multi_range_curr;
multi_range_curr= ranges;
- m_multi_range_result_ptr= (byte*)buffer->buffer;
+ m_multi_range_result_ptr= (uchar*)buffer->buffer;
DBUG_RETURN(read_multi_range_next(found_range_p));
}
ERR_RETURN(m_active_trans->getNdbError());
}
#if 0
-#define DBUG_MULTI_RANGE(x) printf("read_multi_range_next: case %d\n", x);
+#define DBUG_MULTI_RANGE(x) DBUG_PRINT("info", ("read_multi_range_next: case %d\n", x));
#else
#define DBUG_MULTI_RANGE(x)
#endif
@@ -6740,19 +8919,26 @@ ha_ndbcluster::read_multi_range_next(KEY_MULTI_RANGE ** multi_range_found_p)
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));
}
int res;
int range_no;
- ulong reclength= table->s->reclength;
+ ulong reclength= table_share->reclength;
const NdbOperation* op= m_current_multi_operation;
for (;multi_range_curr < m_multi_range_defined; multi_range_curr++)
{
+ DBUG_MULTI_RANGE(12);
+ if (multi_range_curr->range_flag & SKIP_RANGE)
+ continue;
if (multi_range_curr->range_flag & UNIQUE_RANGE)
{
if (op->getNdbError().code == 0)
+ {
+ DBUG_MULTI_RANGE(13);
goto found_next;
+ }
op= m_active_trans->getNextCompletedOperation(op);
m_multi_range_result_ptr += reclength;
@@ -6769,6 +8955,7 @@ ha_ndbcluster::read_multi_range_next(KEY_MULTI_RANGE ** multi_range_found_p)
}
else
{
+ DBUG_MULTI_RANGE(14);
goto close_scan;
}
}
@@ -6800,18 +8987,19 @@ ha_ndbcluster::read_multi_range_next(KEY_MULTI_RANGE ** multi_range_found_p)
DBUG_MULTI_RANGE(6);
// First fetch from cursor
DBUG_ASSERT(range_no == -1);
- if ((res= m_multi_cursor->nextResult(true)))
+ if ((res= m_multi_cursor->nextResult(TRUE)))
{
+ DBUG_MULTI_RANGE(15);
goto close_scan;
}
multi_range_curr--; // Will be increased in for-loop
continue;
}
}
- else /** m_multi_cursor == 0 */
+ else /* m_multi_cursor == 0 */
{
DBUG_MULTI_RANGE(7);
- /**
+ /*
* Corresponds to range 5 in example in read_multi_range_first
*/
(void)1;
@@ -6829,18 +9017,20 @@ close_scan:
}
else
{
+ DBUG_MULTI_RANGE(9);
DBUG_RETURN(ndb_err(m_active_trans));
}
}
if (multi_range_curr == multi_range_end)
{
+ DBUG_MULTI_RANGE(16);
Thd_ndb *thd_ndb= get_thd_ndb(current_thd);
thd_ndb->query_state&= NDB_QUERY_NORMAL;
DBUG_RETURN(HA_ERR_END_OF_FILE);
}
- /**
+ /*
* Read remaining ranges
*/
DBUG_RETURN(read_multi_range_first(multi_range_found_p,
@@ -6850,7 +9040,7 @@ close_scan:
multi_range_buffer));
found:
- /**
+ /*
* Found a record belonging to a scan
*/
m_active_cursor= m_multi_cursor;
@@ -6862,7 +9052,7 @@ found:
DBUG_RETURN(0);
found_next:
- /**
+ /*
* Found a record belonging to a pk/index op,
* copy result and move to next to prepare for next call
*/
@@ -6886,7 +9076,7 @@ ha_ndbcluster::setup_recattr(const NdbRecAttr* curr)
Field **field, **end;
NdbValue *value= m_value;
- end= table->field + table->s->fields;
+ end= table->field + table_share->fields;
for (field= table->field; field < end; field++, value++)
{
@@ -6903,6 +9093,12 @@ ha_ndbcluster::setup_recattr(const NdbRecAttr* curr)
DBUG_RETURN(0);
}
+/**
+ @param[in] comment table comment defined by user
+
+ @return
+ table comment + additional
+*/
char*
ha_ndbcluster::update_table_comment(
/* out: table comment + additional */
@@ -6924,17 +9120,13 @@ ha_ndbcluster::update_table_comment(
{
return((char*)comment);
}
- NDBDICT* dict= ndb->getDictionary();
- const NDBTAB* tab;
- if (!(tab= dict->getTable(m_tabname)))
- {
- return((char*)comment);
- }
+ const NDBTAB* tab= m_table;
+ DBUG_ASSERT(tab != NULL);
char *str;
const char *fmt="%s%snumber_of_replicas: %d";
const unsigned fmt_len_plus_extra= length + strlen(fmt);
- if ((str= my_malloc(fmt_len_plus_extra, MYF(0))) == NULL)
+ if ((str= (char*) my_malloc(fmt_len_plus_extra, MYF(0))) == NULL)
{
sql_print_error("ha_ndbcluster::update_table_comment: "
"my_malloc(%u) failed", (unsigned int)fmt_len_plus_extra);
@@ -6948,16 +9140,22 @@ ha_ndbcluster::update_table_comment(
}
-// Utility thread main loop
+/**
+ Utility thread main loop.
+*/
pthread_handler_t ndb_util_thread_func(void *arg __attribute__((unused)))
{
THD *thd; /* needs to be first for thread_stack */
- Ndb* ndb;
struct timespec abstime;
+ Thd_ndb *thd_ndb;
+ uint share_list_size= 0;
+ NDB_SHARE **share_list= NULL;
my_thread_init();
DBUG_ENTER("ndb_util_thread");
DBUG_PRINT("enter", ("ndb_cache_check_time: %lu", ndb_cache_check_time));
+
+ pthread_mutex_lock(&LOCK_ndb_util_thread);
thd= new THD; /* note that contructor of THD uses DBUG_ */
if (thd == NULL)
@@ -6966,45 +9164,112 @@ pthread_handler_t ndb_util_thread_func(void *arg __attribute__((unused)))
DBUG_RETURN(NULL);
}
THD_CHECK_SENTRY(thd);
- ndb= new Ndb(g_ndb_cluster_connection, "");
- if (ndb == NULL)
- {
- thd->cleanup();
- delete thd;
- DBUG_RETURN(NULL);
- }
pthread_detach_this_thread();
ndb_util_thread= pthread_self();
thd->thread_stack= (char*)&thd; /* remember where our stack is */
- if (thd->store_globals() || (ndb->init() != 0))
+ 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;
+
+ CHARSET_INFO *charset_connection;
+ charset_connection= get_charset_by_csname("utf8",
+ MY_CS_PRIMARY, MYF(MY_WME));
+ thd->variables.character_set_client= charset_connection;
+ thd->variables.character_set_results= charset_connection;
+ thd->variables.collation_connection= charset_connection;
+ thd->update_charset();
+
+ /* Signal successful initialization */
+ ndb_util_thread_running= 1;
+ pthread_cond_signal(&COND_ndb_util_ready);
+ pthread_mutex_unlock(&LOCK_ndb_util_thread);
+
+ /*
+ wait for mysql server to start
+ */
+ pthread_mutex_lock(&LOCK_server_started);
+ while (!mysqld_server_started)
{
- thd->cleanup();
- delete thd;
- delete ndb;
- DBUG_RETURN(NULL);
+ set_timespec(abstime, 1);
+ pthread_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);
+ goto ndb_util_thread_end;
+ }
}
+ pthread_mutex_unlock(&LOCK_server_started);
- uint share_list_size= 0;
- NDB_SHARE **share_list= NULL;
- set_timespec(abstime, 0);
- for (;;)
+ /*
+ Wait for cluster to start
+ */
+ pthread_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);
+ if (ndbcluster_terminating)
+ goto ndb_util_thread_end;
+ }
+ pthread_mutex_unlock(&LOCK_ndb_util_thread);
- if (abort_loop)
- break; /* Shutting down server */
+ /* 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);
+ 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)
+ sql_print_information("NDB Binlog: Ndb tables initially read only.");
+ /* create tables needed by the replication */
+ ndbcluster_setup_binlog_table_shares(thd);
+#else
+ /*
+ Get all table definitions from the storage node
+ */
+ ndbcluster_find_all_files(thd);
+#endif
+
+ set_timespec(abstime, 0);
+ for (;;)
+ {
pthread_mutex_lock(&LOCK_ndb_util_thread);
- pthread_cond_timedwait(&COND_ndb_util_thread,
- &LOCK_ndb_util_thread,
- &abstime);
+ if (!ndbcluster_terminating)
+ pthread_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);
-
+#ifdef NDB_EXTRA_DEBUG_UTIL_THREAD
DBUG_PRINT("ndb_util_thread", ("Started, ndb_cache_check_time: %lu",
ndb_cache_check_time));
+#endif
- if (abort_loop)
- break; /* Shutting down server */
+#ifdef HAVE_NDB_BINLOG
+ /*
+ Check that the ndb_apply_status_share and ndb_schema_share
+ have been created.
+ If not try to create it
+ */
+ if (!ndb_binlog_tables_inited)
+ ndbcluster_setup_binlog_table_shares(thd);
+#endif
if (ndb_cache_check_time == 0)
{
@@ -7016,7 +9281,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);
- uint i, record_count= ndbcluster_open_tables.records;
+ uint i, open_count, record_count= ndbcluster_open_tables.records;
if (share_list_size < record_count)
{
NDB_SHARE ** new_share_list= new NDB_SHARE * [record_count];
@@ -7031,62 +9296,82 @@ pthread_handler_t ndb_util_thread_func(void *arg __attribute__((unused)))
share_list_size= record_count;
share_list= new_share_list;
}
- for (i= 0; i < record_count; i++)
+ for (i= 0, open_count= 0; i < record_count; i++)
{
share= (NDB_SHARE *)hash_element(&ndbcluster_open_tables, i);
+#ifdef HAVE_NDB_BINLOG
+ if ((share->use_count - (int) (share->op != 0) - (int) (share->op != 0))
+ <= 0)
+ continue; // injector thread is the only user, skip statistics
+ share->util_lock= current_thd; // Mark that util thread has lock
+#endif /* HAVE_NDB_BINLOG */
+ /* ndb_share reference temporary, free below */
share->use_count++; /* Make sure the table can't be closed */
+ DBUG_PRINT("NDB_SHARE", ("%s temporary use_count: %u",
+ share->key, share->use_count));
DBUG_PRINT("ndb_util_thread",
("Found open table[%d]: %s, use_count: %d",
i, share->table_name, share->use_count));
/* Store pointer to table */
- share_list[i]= share;
+ share_list[open_count++]= share;
}
pthread_mutex_unlock(&ndbcluster_mutex);
/* Iterate through the open files list */
- for (i= 0; i < record_count; i++)
+ for (i= 0; i < open_count; i++)
{
share= share_list[i];
- /* Split tab- and dbname */
- char buf[FN_REFLEN];
- char *tabname, *db;
- uint length= dirname_length(share->table_name);
- tabname= share->table_name+length;
- memcpy(buf, share->table_name, length-1);
- buf[length-1]= 0;
- db= buf+dirname_length(buf);
+#ifdef HAVE_NDB_BINLOG
+ if ((share->use_count - (int) (share->op != 0) - (int) (share->op != 0))
+ <= 1)
+ {
+ /*
+ Util thread and injector thread is the only user, skip statistics
+ */
+ /* ndb_share reference temporary free */
+ DBUG_PRINT("NDB_SHARE", ("%s temporary free use_count: %u",
+ share->key, share->use_count));
+ free_share(&share);
+ continue;
+ }
+#endif /* HAVE_NDB_BINLOG */
DBUG_PRINT("ndb_util_thread",
- ("Fetching commit count for: %s",
- share->table_name));
+ ("Fetching commit count for: %s", share->key));
- /* Contact NDB to get commit count for table */
struct Ndb_statistics stat;
uint lock;
pthread_mutex_lock(&share->mutex);
lock= share->commit_count_lock;
pthread_mutex_unlock(&share->mutex);
- if (ndb->setDatabaseName(db))
- {
- goto loop_next;
- }
- if (ndb_get_table_statistics(NULL, false, ndb, tabname, &stat) == 0)
{
+ /* Contact NDB to get commit count for table */
+ Ndb* ndb= thd_ndb->ndb;
+ if (ndb->setDatabaseName(share->db))
+ {
+ goto loop_next;
+ }
+ Ndb_table_guard ndbtab_g(ndb->getDictionary(), share->table_name);
+ if (ndbtab_g.get_table() &&
+ ndb_get_table_statistics(NULL, FALSE, ndb,
+ ndbtab_g.get_table(), &stat) == 0)
+ {
#ifndef DBUG_OFF
- char buff[22], buff2[22];
+ char buff[22], buff2[22];
#endif
- DBUG_PRINT("ndb_util_thread",
- ("Table: %s commit_count: %s rows: %s",
- share->table_name,
- llstr(stat.commit_count, buff),
- llstr(stat.row_count, buff2)));
- }
- else
- {
- DBUG_PRINT("ndb_util_thread",
- ("Error: Could not get commit count for table %s",
- share->table_name));
- stat.commit_count= 0;
+ DBUG_PRINT("info",
+ ("Table: %s commit_count: %s rows: %s",
+ share->key,
+ llstr(stat.commit_count, buff),
+ llstr(stat.row_count, buff2)));
+ }
+ else
+ {
+ DBUG_PRINT("ndb_util_thread",
+ ("Error: Could not get commit count for table %s",
+ share->key));
+ stat.commit_count= 0;
+ }
}
loop_next:
pthread_mutex_lock(&share->mutex);
@@ -7094,8 +9379,10 @@ pthread_handler_t ndb_util_thread_func(void *arg __attribute__((unused)))
share->commit_count= stat.commit_count;
pthread_mutex_unlock(&share->mutex);
- /* Decrease the use count and possibly free share */
- free_share(share);
+ /* ndb_share reference temporary free */
+ DBUG_PRINT("NDB_SHARE", ("%s temporary free use_count: %u",
+ share->key, share->use_count));
+ free_share(&share);
}
next:
/* Calculate new time to wake up */
@@ -7120,68 +9407,30 @@ next:
}
}
+ pthread_mutex_lock(&LOCK_ndb_util_thread);
+
+ndb_util_thread_end:
+ net_end(&thd->net);
+ndb_util_thread_fail:
if (share_list)
delete [] share_list;
thd->cleanup();
delete thd;
- delete ndb;
+
+ /* signal termination */
+ ndb_util_thread_running= 0;
+ pthread_cond_signal(&COND_ndb_util_ready);
+ pthread_mutex_unlock(&LOCK_ndb_util_thread);
DBUG_PRINT("exit", ("ndb_util_thread"));
my_thread_end();
pthread_exit(0);
DBUG_RETURN(NULL);
}
-int
-ndbcluster_show_status(THD* thd)
-{
- Protocol *protocol= thd->protocol;
- DBUG_ENTER("ndbcluster_show_status");
-
- if (have_ndbcluster != SHOW_OPTION_YES)
- {
- my_message(ER_NOT_SUPPORTED_YET,
- "Cannot call SHOW NDBCLUSTER STATUS because skip-ndbcluster is "
- "defined",
- MYF(0));
- DBUG_RETURN(TRUE);
- }
-
- List<Item> field_list;
- field_list.push_back(new Item_empty_string("free_list", 255));
- field_list.push_back(new Item_return_int("created", 10,MYSQL_TYPE_LONG));
- field_list.push_back(new Item_return_int("free", 10,MYSQL_TYPE_LONG));
- field_list.push_back(new Item_return_int("sizeof", 10,MYSQL_TYPE_LONG));
-
- if (protocol->send_fields(&field_list,
- Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
- DBUG_RETURN(TRUE);
-
- if (get_thd_ndb(thd) && get_thd_ndb(thd)->ndb)
- {
- Ndb* ndb= (get_thd_ndb(thd))->ndb;
- Ndb::Free_list_usage tmp;
- tmp.m_name= 0;
- while (ndb->get_free_list_usage(&tmp))
- {
- protocol->prepare_for_resend();
-
- protocol->store(tmp.m_name, &my_charset_bin);
- protocol->store((uint)tmp.m_created);
- protocol->store((uint)tmp.m_free);
- protocol->store((uint)tmp.m_sizeof);
- if (protocol->write())
- DBUG_RETURN(TRUE);
- }
- }
- send_eof(thd);
-
- DBUG_RETURN(FALSE);
-}
-
/*
Condition pushdown
*/
-/*
+/**
Push a condition to ndbcluster storage engine for evaluation
during table and index scans. The conditions will be stored on a stack
for possibly storing several conditions. The stack can be popped
@@ -7192,9 +9441,10 @@ ndbcluster_show_status(THD* thd)
expressions and function calls) and the following comparison operators:
=, !=, >, >=, <, <=, "is null", and "is not null".
- RETURN
+ @retval
NULL The condition was supported and will be evaluated for each
- row found during the scan
+ row found during the scan
+ @retval
cond The condition was not supported and all rows will be returned from
the scan for evaluation (and thus not saved on stack)
*/
@@ -7210,11 +9460,11 @@ ha_ndbcluster::cond_push(const COND *cond)
my_errno= HA_ERR_OUT_OF_MEM;
DBUG_RETURN(NULL);
}
- DBUG_EXECUTE("where",print_where((COND *)cond, m_tabname););
+ DBUG_EXECUTE("where",print_where((COND *)cond, m_tabname, QT_ORDINARY););
DBUG_RETURN(m_cond->cond_push(cond, table, (NDBTAB *)m_table));
}
-/*
+/**
Pop the top condition from the condition stack of the handler instance.
*/
void
@@ -7224,4 +9474,1083 @@ ha_ndbcluster::cond_pop()
m_cond->cond_pop();
}
-#endif /* HAVE_NDBCLUSTER_DB */
+
+/*
+ get table space info for SHOW CREATE TABLE
+*/
+char* ha_ndbcluster::get_tablespace_name(THD *thd, char* name, uint name_len)
+{
+ Ndb *ndb= check_ndb_in_thd(thd);
+ NDBDICT *ndbdict= ndb->getDictionary();
+ NdbError ndberr;
+ Uint32 id;
+ ndb->setDatabaseName(m_dbname);
+ const NDBTAB *ndbtab= m_table;
+ DBUG_ASSERT(ndbtab != NULL);
+ if (!ndbtab->getTablespace(&id))
+ {
+ return 0;
+ }
+ {
+ NdbDictionary::Tablespace ts= ndbdict->getTablespace(id);
+ ndberr= ndbdict->getNdbError();
+ if(ndberr.classification != NdbError::NoError)
+ goto err;
+ DBUG_PRINT("info", ("Found tablespace '%s'", ts.getName()));
+ if (name)
+ {
+ strxnmov(name, name_len, ts.getName(), NullS);
+ return name;
+ }
+ else
+ return (my_strdup(ts.getName(), MYF(0)));
+ }
+err:
+ if (ndberr.status == NdbError::TemporaryError)
+ push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_ERROR,
+ ER_GET_TEMPORARY_ERRMSG, ER(ER_GET_TEMPORARY_ERRMSG),
+ ndberr.code, ndberr.message, "NDB");
+ else
+ push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_ERROR,
+ ER_GET_ERRMSG, ER(ER_GET_ERRMSG),
+ ndberr.code, ndberr.message, "NDB");
+ return 0;
+}
+
+/*
+ Implements the SHOW NDB STATUS command.
+*/
+bool
+ndbcluster_show_status(handlerton *hton, THD* thd, stat_print_fn *stat_print,
+ enum ha_stat_type stat_type)
+{
+ char buf[IO_SIZE];
+ uint buflen;
+ DBUG_ENTER("ndbcluster_show_status");
+
+ if (stat_type != HA_ENGINE_STATUS)
+ {
+ DBUG_RETURN(FALSE);
+ }
+
+ update_status_variables(g_ndb_cluster_connection);
+ buflen=
+ my_snprintf(buf, sizeof(buf),
+ "cluster_node_id=%ld, "
+ "connected_host=%s, "
+ "connected_port=%ld, "
+ "number_of_data_nodes=%ld, "
+ "number_of_ready_data_nodes=%ld, "
+ "connect_count=%ld",
+ ndb_cluster_node_id,
+ ndb_connected_host,
+ ndb_connected_port,
+ ndb_number_of_data_nodes,
+ ndb_number_of_ready_data_nodes,
+ ndb_connect_count);
+ if (stat_print(thd, ndbcluster_hton_name, ndbcluster_hton_name_length,
+ STRING_WITH_LEN("connection"), buf, buflen))
+ DBUG_RETURN(TRUE);
+
+ if (get_thd_ndb(thd) && get_thd_ndb(thd)->ndb)
+ {
+ Ndb* ndb= (get_thd_ndb(thd))->ndb;
+ Ndb::Free_list_usage tmp;
+ tmp.m_name= 0;
+ while (ndb->get_free_list_usage(&tmp))
+ {
+ buflen=
+ my_snprintf(buf, sizeof(buf),
+ "created=%u, free=%u, sizeof=%u",
+ tmp.m_created, tmp.m_free, tmp.m_sizeof);
+ if (stat_print(thd, ndbcluster_hton_name, ndbcluster_hton_name_length,
+ tmp.m_name, strlen(tmp.m_name), buf, buflen))
+ DBUG_RETURN(TRUE);
+ }
+ }
+#ifdef HAVE_NDB_BINLOG
+ ndbcluster_show_status_binlog(thd, stat_print, stat_type);
+#endif
+
+ DBUG_RETURN(FALSE);
+}
+
+
+/*
+ Create a table in NDB Cluster
+ */
+static uint get_no_fragments(ulonglong max_rows)
+{
+#if MYSQL_VERSION_ID >= 50000
+ uint acc_row_size= 25 + /*safety margin*/ 2;
+#else
+ uint acc_row_size= pk_length*4;
+ /* add acc overhead */
+ if (pk_length <= 8) /* main page will set the limit */
+ acc_row_size+= 25 + /*safety margin*/ 2;
+ else /* overflow page will set the limit */
+ acc_row_size+= 4 + /*safety margin*/ 4;
+#endif
+ ulonglong acc_fragment_size= 512*1024*1024;
+#if MYSQL_VERSION_ID >= 50100
+ return (max_rows*acc_row_size)/acc_fragment_size+1;
+#else
+ return ((max_rows*acc_row_size)/acc_fragment_size+1
+ +1/*correct rounding*/)/2;
+#endif
+}
+
+
+/*
+ Routine to adjust default number of partitions to always be a multiple
+ of number of nodes and never more than 4 times the number of nodes.
+
+*/
+static bool adjusted_frag_count(uint no_fragments, uint no_nodes,
+ uint &reported_frags)
+{
+ uint i= 0;
+ reported_frags= no_nodes;
+ while (reported_frags < no_fragments && ++i < 4 &&
+ (reported_frags + no_nodes) < MAX_PARTITIONS)
+ reported_frags+= no_nodes;
+ return (reported_frags < no_fragments);
+}
+
+int ha_ndbcluster::get_default_no_partitions(HA_CREATE_INFO *create_info)
+{
+ ha_rows max_rows, min_rows;
+ if (create_info)
+ {
+ max_rows= create_info->max_rows;
+ min_rows= create_info->min_rows;
+ }
+ else
+ {
+ max_rows= table_share->max_rows;
+ min_rows= table_share->min_rows;
+ }
+ uint reported_frags;
+ uint no_fragments=
+ get_no_fragments(max_rows >= min_rows ? max_rows : min_rows);
+ uint no_nodes= g_ndb_cluster_connection->no_db_nodes();
+ if (adjusted_frag_count(no_fragments, no_nodes, reported_frags))
+ {
+ push_warning(current_thd,
+ MYSQL_ERROR::WARN_LEVEL_WARN, ER_UNKNOWN_ERROR,
+ "Ndb might have problems storing the max amount of rows specified");
+ }
+ return (int)reported_frags;
+}
+
+
+/*
+ Set-up auto-partitioning for NDB Cluster
+
+ SYNOPSIS
+ set_auto_partitions()
+ part_info Partition info struct to set-up
+
+ RETURN VALUE
+ NONE
+
+ DESCRIPTION
+ Set-up auto partitioning scheme for tables that didn't define any
+ partitioning. We'll use PARTITION BY KEY() in this case which
+ translates into partition by primary key if a primary key exists
+ and partition by hidden key otherwise.
+*/
+
+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)
+ {
+ case ND_KEYHASH:
+ part_info->linear_hash_ind= FALSE;
+ break;
+ case ND_LINHASH:
+ part_info->linear_hash_ind= TRUE;
+ break;
+ }
+ DBUG_VOID_RETURN;
+}
+
+
+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),
+ MYF(0));
+ uint i;
+ int error= 0;
+ bool unsigned_flag= part_info->part_expr->unsigned_flag;
+ DBUG_ENTER("set_range_data");
+
+ if (!range_data)
+ {
+ mem_alloc_error(part_info->no_parts*sizeof(int32));
+ DBUG_RETURN(1);
+ }
+ for (i= 0; i < part_info->no_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) ||
+ (range_val != LONGLONG_MAX))
+ {
+ my_error(ER_LIMITED_PART_RANGE, MYF(0), "NDB");
+ error= 1;
+ goto error;
+ }
+ range_val= INT_MAX32;
+ }
+ range_data[i]= (int32)range_val;
+ }
+ tab->setRangeListData(range_data, sizeof(int32)*part_info->no_parts);
+error:
+ my_free((char*)range_data, MYF(0));
+ 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
+ * sizeof(int32), MYF(0));
+ uint32 *part_id, i;
+ int error= 0;
+ bool unsigned_flag= part_info->part_expr->unsigned_flag;
+ DBUG_ENTER("set_list_data");
+
+ if (!list_data)
+ {
+ mem_alloc_error(part_info->no_list_values*2*sizeof(int32));
+ DBUG_RETURN(1);
+ }
+ for (i= 0; i < part_info->no_list_values; i++)
+ {
+ LIST_PART_ENTRY *list_entry= &part_info->list_array[i];
+ longlong list_val= list_entry->list_value;
+ if (unsigned_flag)
+ list_val-= 0x8000000000000000ULL;
+ if (list_val < INT_MIN32 || list_val > INT_MAX32)
+ {
+ my_error(ER_LIMITED_PART_RANGE, MYF(0), "NDB");
+ error= 1;
+ goto error;
+ }
+ list_data[2*i]= (int32)list_val;
+ 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);
+error:
+ my_free((char*)list_data, MYF(0));
+ DBUG_RETURN(error);
+}
+
+/*
+ User defined partitioning set-up. We need to check how many fragments the
+ user wants defined and which node groups to put those into. Later we also
+ want to attach those partitions to a tablespace.
+
+ All the functionality of the partition function, partition limits and so
+ forth are entirely handled by the MySQL Server. There is one exception to
+ this rule for PARTITION BY KEY where NDB handles the hash function and
+ this type can thus be handled transparently also by NDB API program.
+ For RANGE, HASH and LIST and subpartitioning the NDB API programs must
+ implement the function to map to a partition.
+*/
+
+uint ha_ndbcluster::set_up_partition_info(partition_info *part_info,
+ TABLE *table,
+ void *tab_par)
+{
+ uint16 frag_data[MAX_PARTITIONS];
+ char *ts_names[MAX_PARTITIONS];
+ ulong fd_index= 0, i, j;
+ NDBTAB *tab= (NDBTAB*)tab_par;
+ NDBTAB::FragmentType ftype= NDBTAB::UserDefined;
+ partition_element *part_elem;
+ bool first= TRUE;
+ uint tot_ts_name_len;
+ List_iterator<partition_element> part_it(part_info->partitions);
+ int error;
+ DBUG_ENTER("ha_ndbcluster::set_up_partition_info");
+
+ if (part_info->part_type == HASH_PARTITION &&
+ part_info->list_of_part_fields == TRUE)
+ {
+ Field **fields= part_info->part_field_array;
+
+ if (part_info->linear_hash_ind)
+ ftype= NDBTAB::DistrKeyLin;
+ else
+ ftype= NDBTAB::DistrKeyHash;
+
+ for (i= 0; i < part_info->part_field_list.elements; i++)
+ {
+ NDBCOL *col= tab->getColumn(fields[i]->field_index);
+ DBUG_PRINT("info",("setting dist key on %s", col->getName()));
+ col->setPartitionKey(TRUE);
+ }
+ }
+ else
+ {
+ if (!current_thd->variables.new_mode)
+ {
+ push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_ERROR,
+ ER_ILLEGAL_HA_CREATE_OPTION,
+ ER(ER_ILLEGAL_HA_CREATE_OPTION),
+ ndbcluster_hton_name,
+ "LIST, RANGE and HASH partition disabled by default,"
+ " use --new option to enable");
+ DBUG_RETURN(HA_ERR_UNSUPPORTED);
+ }
+ /*
+ Create a shadow field for those tables that have user defined
+ partitioning. This field stores the value of the partition
+ function such that NDB can handle reorganisations of the data
+ even when the MySQL Server isn't available to assist with
+ calculation of the partition function value.
+ */
+ NDBCOL col;
+ DBUG_PRINT("info", ("Generating partition func value field"));
+ col.setName("$PART_FUNC_VALUE");
+ col.setType(NdbDictionary::Column::Int);
+ col.setLength(1);
+ col.setNullable(FALSE);
+ col.setPrimaryKey(FALSE);
+ col.setAutoIncrement(FALSE);
+ tab->addColumn(col);
+ if (part_info->part_type == RANGE_PARTITION)
+ {
+ if ((error= set_range_data((void*)tab, part_info)))
+ {
+ DBUG_RETURN(error);
+ }
+ }
+ else if (part_info->part_type == LIST_PARTITION)
+ {
+ if ((error= set_list_data((void*)tab, part_info)))
+ {
+ DBUG_RETURN(error);
+ }
+ }
+ }
+ tab->setFragmentType(ftype);
+ i= 0;
+ tot_ts_name_len= 0;
+ do
+ {
+ uint ng;
+ part_elem= part_it++;
+ if (!part_info->is_sub_partitioned())
+ {
+ ng= part_elem->nodegroup_id;
+ if (first && ng == UNDEF_NODEGROUP)
+ ng= 0;
+ ts_names[fd_index]= part_elem->tablespace_name;
+ frag_data[fd_index++]= ng;
+ }
+ else
+ {
+ List_iterator<partition_element> sub_it(part_elem->subpartitions);
+ j= 0;
+ do
+ {
+ part_elem= sub_it++;
+ ng= part_elem->nodegroup_id;
+ if (first && ng == UNDEF_NODEGROUP)
+ ng= 0;
+ ts_names[fd_index]= part_elem->tablespace_name;
+ frag_data[fd_index++]= ng;
+ } while (++j < part_info->no_subparts);
+ }
+ first= FALSE;
+ } while (++i < part_info->no_parts);
+ tab->setDefaultNoPartitionsFlag(part_info->use_default_no_partitions);
+ tab->setLinearFlag(part_info->linear_hash_ind);
+ {
+ ha_rows max_rows= table_share->max_rows;
+ ha_rows min_rows= table_share->min_rows;
+ if (max_rows < min_rows)
+ max_rows= min_rows;
+ if (max_rows != (ha_rows)0) /* default setting, don't set fragmentation */
+ {
+ tab->setMaxRows(max_rows);
+ tab->setMinRows(min_rows);
+ }
+ }
+ tab->setTablespaceNames(ts_names, fd_index*sizeof(char*));
+ tab->setFragmentCount(fd_index);
+ tab->setFragmentData(&frag_data, fd_index*2);
+ DBUG_RETURN(0);
+}
+
+
+bool ha_ndbcluster::check_if_incompatible_data(HA_CREATE_INFO *create_info,
+ uint table_changes)
+{
+ DBUG_ENTER("ha_ndbcluster::check_if_incompatible_data");
+ uint i;
+ const NDBTAB *tab= (const NDBTAB *) m_table;
+
+ if (current_thd->variables.ndb_use_copying_alter_table)
+ {
+ DBUG_PRINT("info", ("On-line alter table disabled"));
+ DBUG_RETURN(COMPATIBLE_DATA_NO);
+ }
+
+ int pk= 0;
+ int ai= 0;
+
+ if (create_info->tablespace)
+ create_info->storage_media = HA_SM_DISK;
+ else
+ create_info->storage_media = HA_SM_MEMORY;
+
+ for (i= 0; i < table->s->fields; i++)
+ {
+ Field *field= table->field[i];
+ const NDBCOL *col= tab->getColumn(i);
+ if (col->getStorageType() == NDB_STORAGETYPE_MEMORY && create_info->storage_media != HA_SM_MEMORY ||
+ col->getStorageType() == NDB_STORAGETYPE_DISK && create_info->storage_media != HA_SM_DISK)
+ {
+ DBUG_PRINT("info", ("Column storage media is changed"));
+ DBUG_RETURN(COMPATIBLE_DATA_NO);
+ }
+
+ if (field->flags & FIELD_IS_RENAMED)
+ {
+ DBUG_PRINT("info", ("Field has been renamed, copy table"));
+ DBUG_RETURN(COMPATIBLE_DATA_NO);
+ }
+ if ((field->flags & FIELD_IN_ADD_INDEX) &&
+ col->getStorageType() == NdbDictionary::Column::StorageTypeDisk)
+ {
+ DBUG_PRINT("info", ("add/drop index not supported for disk stored column"));
+ DBUG_RETURN(COMPATIBLE_DATA_NO);
+ }
+
+ if (field->flags & PRI_KEY_FLAG)
+ pk=1;
+ if (field->flags & FIELD_IN_ADD_INDEX)
+ ai=1;
+ }
+
+ char tablespace_name[FN_LEN];
+ if (get_tablespace_name(current_thd, tablespace_name, FN_LEN))
+ {
+ if (create_info->tablespace)
+ {
+ if (strcmp(create_info->tablespace, tablespace_name))
+ {
+ DBUG_PRINT("info", ("storage media is changed, old tablespace=%s, new tablespace=%s",
+ tablespace_name, create_info->tablespace));
+ DBUG_RETURN(COMPATIBLE_DATA_NO);
+ }
+ }
+ else
+ {
+ DBUG_PRINT("info", ("storage media is changed, old is DISK and tablespace=%s, new is MEM",
+ tablespace_name));
+ DBUG_RETURN(COMPATIBLE_DATA_NO);
+ }
+ }
+ else
+ {
+ if (create_info->storage_media != HA_SM_MEMORY)
+ {
+ DBUG_PRINT("info", ("storage media is changed, old is MEM, new is DISK and tablespace=%s",
+ create_info->tablespace));
+ DBUG_RETURN(COMPATIBLE_DATA_NO);
+ }
+ }
+
+ if (table_changes != IS_EQUAL_YES)
+ DBUG_RETURN(COMPATIBLE_DATA_NO);
+
+ /* Check that auto_increment value was not changed */
+ if ((create_info->used_fields & HA_CREATE_USED_AUTO) &&
+ create_info->auto_increment_value != 0)
+ {
+ DBUG_PRINT("info", ("auto_increment value changed"));
+ DBUG_RETURN(COMPATIBLE_DATA_NO);
+ }
+
+ /* Check that row format didn't change */
+ if ((create_info->used_fields & HA_CREATE_USED_AUTO) &&
+ get_row_type() != create_info->row_type)
+ {
+ DBUG_PRINT("info", ("row format changed"));
+ DBUG_RETURN(COMPATIBLE_DATA_NO);
+ }
+
+ DBUG_PRINT("info", ("new table seems compatible"));
+ DBUG_RETURN(COMPATIBLE_DATA_YES);
+}
+
+bool set_up_tablespace(st_alter_tablespace *alter_info,
+ NdbDictionary::Tablespace *ndb_ts)
+{
+ ndb_ts->setName(alter_info->tablespace_name);
+ ndb_ts->setExtentSize(alter_info->extent_size);
+ ndb_ts->setDefaultLogfileGroup(alter_info->logfile_group_name);
+ return FALSE;
+}
+
+bool set_up_datafile(st_alter_tablespace *alter_info,
+ NdbDictionary::Datafile *ndb_df)
+{
+ if (alter_info->max_size > 0)
+ {
+ my_error(ER_TABLESPACE_AUTO_EXTEND_ERROR, MYF(0));
+ return TRUE;
+ }
+ ndb_df->setPath(alter_info->data_file_name);
+ ndb_df->setSize(alter_info->initial_size);
+ ndb_df->setTablespace(alter_info->tablespace_name);
+ return FALSE;
+}
+
+bool set_up_logfile_group(st_alter_tablespace *alter_info,
+ NdbDictionary::LogfileGroup *ndb_lg)
+{
+ ndb_lg->setName(alter_info->logfile_group_name);
+ ndb_lg->setUndoBufferSize(alter_info->undo_buffer_size);
+ return FALSE;
+}
+
+bool set_up_undofile(st_alter_tablespace *alter_info,
+ NdbDictionary::Undofile *ndb_uf)
+{
+ ndb_uf->setPath(alter_info->undo_file_name);
+ ndb_uf->setSize(alter_info->initial_size);
+ ndb_uf->setLogfileGroup(alter_info->logfile_group_name);
+ return FALSE;
+}
+
+int ndbcluster_alter_tablespace(handlerton *hton,
+ THD* thd, st_alter_tablespace *alter_info)
+{
+ int is_tablespace= 0;
+ NdbError err;
+ NDBDICT *dict;
+ int error;
+ const char *errmsg;
+ Ndb *ndb;
+ DBUG_ENTER("ha_ndbcluster::alter_tablespace");
+ LINT_INIT(errmsg);
+
+ ndb= check_ndb_in_thd(thd);
+ if (ndb == NULL)
+ {
+ DBUG_RETURN(HA_ERR_NO_CONNECTION);
+ }
+ dict= ndb->getDictionary();
+
+ switch (alter_info->ts_cmd_type){
+ case (CREATE_TABLESPACE):
+ {
+ error= ER_CREATE_FILEGROUP_FAILED;
+
+ NdbDictionary::Tablespace ndb_ts;
+ NdbDictionary::Datafile ndb_df;
+ NdbDictionary::ObjectId objid;
+ if (set_up_tablespace(alter_info, &ndb_ts))
+ {
+ DBUG_RETURN(1);
+ }
+ if (set_up_datafile(alter_info, &ndb_df))
+ {
+ DBUG_RETURN(1);
+ }
+ errmsg= "TABLESPACE";
+ if (dict->createTablespace(ndb_ts, &objid))
+ {
+ DBUG_PRINT("error", ("createTablespace returned %d", error));
+ goto ndberror;
+ }
+ DBUG_PRINT("alter_info", ("Successfully created Tablespace"));
+ errmsg= "DATAFILE";
+ if (dict->createDatafile(ndb_df))
+ {
+ err= dict->getNdbError();
+ NdbDictionary::Tablespace tmp= dict->getTablespace(ndb_ts.getName());
+ if (dict->getNdbError().code == 0 &&
+ tmp.getObjectId() == objid.getObjectId() &&
+ tmp.getObjectVersion() == objid.getObjectVersion())
+ {
+ dict->dropTablespace(tmp);
+ }
+
+ DBUG_PRINT("error", ("createDatafile returned %d", error));
+ goto ndberror2;
+ }
+ is_tablespace= 1;
+ break;
+ }
+ case (ALTER_TABLESPACE):
+ {
+ error= ER_ALTER_FILEGROUP_FAILED;
+ if (alter_info->ts_alter_tablespace_type == ALTER_TABLESPACE_ADD_FILE)
+ {
+ NdbDictionary::Datafile ndb_df;
+ if (set_up_datafile(alter_info, &ndb_df))
+ {
+ DBUG_RETURN(1);
+ }
+ errmsg= " CREATE DATAFILE";
+ if (dict->createDatafile(ndb_df))
+ {
+ goto ndberror;
+ }
+ }
+ else if(alter_info->ts_alter_tablespace_type == ALTER_TABLESPACE_DROP_FILE)
+ {
+ NdbDictionary::Tablespace ts= dict->getTablespace(alter_info->tablespace_name);
+ NdbDictionary::Datafile df= dict->getDatafile(0, alter_info->data_file_name);
+ NdbDictionary::ObjectId objid;
+ df.getTablespaceId(&objid);
+ if (ts.getObjectId() == objid.getObjectId() &&
+ strcmp(df.getPath(), alter_info->data_file_name) == 0)
+ {
+ errmsg= " DROP DATAFILE";
+ if (dict->dropDatafile(df))
+ {
+ goto ndberror;
+ }
+ }
+ else
+ {
+ DBUG_PRINT("error", ("No such datafile"));
+ my_error(ER_ALTER_FILEGROUP_FAILED, MYF(0), " NO SUCH FILE");
+ DBUG_RETURN(1);
+ }
+ }
+ else
+ {
+ DBUG_PRINT("error", ("Unsupported alter tablespace: %d",
+ alter_info->ts_alter_tablespace_type));
+ DBUG_RETURN(HA_ADMIN_NOT_IMPLEMENTED);
+ }
+ is_tablespace= 1;
+ break;
+ }
+ case (CREATE_LOGFILE_GROUP):
+ {
+ error= ER_CREATE_FILEGROUP_FAILED;
+ NdbDictionary::LogfileGroup ndb_lg;
+ NdbDictionary::Undofile ndb_uf;
+ NdbDictionary::ObjectId objid;
+ if (alter_info->undo_file_name == NULL)
+ {
+ /*
+ REDO files in LOGFILE GROUP not supported yet
+ */
+ DBUG_RETURN(HA_ADMIN_NOT_IMPLEMENTED);
+ }
+ if (set_up_logfile_group(alter_info, &ndb_lg))
+ {
+ DBUG_RETURN(1);
+ }
+ errmsg= "LOGFILE GROUP";
+ if (dict->createLogfileGroup(ndb_lg, &objid))
+ {
+ goto ndberror;
+ }
+ DBUG_PRINT("alter_info", ("Successfully created Logfile Group"));
+ if (set_up_undofile(alter_info, &ndb_uf))
+ {
+ DBUG_RETURN(1);
+ }
+ errmsg= "UNDOFILE";
+ if (dict->createUndofile(ndb_uf))
+ {
+ err= dict->getNdbError();
+ NdbDictionary::LogfileGroup tmp= dict->getLogfileGroup(ndb_lg.getName());
+ if (dict->getNdbError().code == 0 &&
+ tmp.getObjectId() == objid.getObjectId() &&
+ tmp.getObjectVersion() == objid.getObjectVersion())
+ {
+ dict->dropLogfileGroup(tmp);
+ }
+ goto ndberror2;
+ }
+ break;
+ }
+ case (ALTER_LOGFILE_GROUP):
+ {
+ error= ER_ALTER_FILEGROUP_FAILED;
+ if (alter_info->undo_file_name == NULL)
+ {
+ /*
+ REDO files in LOGFILE GROUP not supported yet
+ */
+ DBUG_RETURN(HA_ADMIN_NOT_IMPLEMENTED);
+ }
+ NdbDictionary::Undofile ndb_uf;
+ if (set_up_undofile(alter_info, &ndb_uf))
+ {
+ DBUG_RETURN(1);
+ }
+ errmsg= "CREATE UNDOFILE";
+ if (dict->createUndofile(ndb_uf))
+ {
+ goto ndberror;
+ }
+ break;
+ }
+ case (DROP_TABLESPACE):
+ {
+ error= ER_DROP_FILEGROUP_FAILED;
+ errmsg= "TABLESPACE";
+ if (dict->dropTablespace(dict->getTablespace(alter_info->tablespace_name)))
+ {
+ goto ndberror;
+ }
+ is_tablespace= 1;
+ break;
+ }
+ case (DROP_LOGFILE_GROUP):
+ {
+ error= ER_DROP_FILEGROUP_FAILED;
+ errmsg= "LOGFILE GROUP";
+ if (dict->dropLogfileGroup(dict->getLogfileGroup(alter_info->logfile_group_name)))
+ {
+ goto ndberror;
+ }
+ break;
+ }
+ case (CHANGE_FILE_TABLESPACE):
+ {
+ DBUG_RETURN(HA_ADMIN_NOT_IMPLEMENTED);
+ }
+ case (ALTER_ACCESS_MODE_TABLESPACE):
+ {
+ DBUG_RETURN(HA_ADMIN_NOT_IMPLEMENTED);
+ }
+ default:
+ {
+ DBUG_RETURN(HA_ADMIN_NOT_IMPLEMENTED);
+ }
+ }
+#ifdef HAVE_NDB_BINLOG
+ if (is_tablespace)
+ ndbcluster_log_schema_op(thd, 0,
+ thd->query, thd->query_length,
+ "", alter_info->tablespace_name,
+ 0, 0,
+ SOT_TABLESPACE, 0, 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);
+#endif
+ DBUG_RETURN(FALSE);
+
+ndberror:
+ err= dict->getNdbError();
+ndberror2:
+ set_ndb_err(thd, err);
+ ndb_to_mysql_error(&err);
+
+ my_error(error, MYF(0), errmsg);
+ DBUG_RETURN(1);
+}
+
+
+bool ha_ndbcluster::get_no_parts(const char *name, uint *no_parts)
+{
+ Ndb *ndb;
+ NDBDICT *dict;
+ int err;
+ DBUG_ENTER("ha_ndbcluster::get_no_parts");
+ LINT_INIT(err);
+
+ set_dbname(name);
+ set_tabname(name);
+ for (;;)
+ {
+ if (check_ndb_connection())
+ {
+ err= HA_ERR_NO_CONNECTION;
+ break;
+ }
+ ndb= get_ndb();
+ ndb->setDatabaseName(m_dbname);
+ 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();
+ DBUG_RETURN(FALSE);
+ }
+
+ print_error(err, MYF(0));
+ DBUG_RETURN(TRUE);
+}
+
+static int ndbcluster_fill_files_table(handlerton *hton,
+ THD *thd,
+ TABLE_LIST *tables,
+ COND *cond)
+{
+ TABLE* table= tables->table;
+ Ndb *ndb= check_ndb_in_thd(thd);
+ NdbDictionary::Dictionary* dict= ndb->getDictionary();
+ NdbDictionary::Dictionary::List dflist;
+ NdbError ndberr;
+ uint i;
+ DBUG_ENTER("ndbcluster_fill_files_table");
+
+ dict->listObjects(dflist, NdbDictionary::Object::Datafile);
+ ndberr= dict->getNdbError();
+ if (ndberr.classification != NdbError::NoError)
+ ERR_RETURN(ndberr);
+
+ for (i= 0; i < dflist.count; i++)
+ {
+ NdbDictionary::Dictionary::List::Element& elt = dflist.elements[i];
+ Ndb_cluster_connection_node_iter iter;
+ uint id;
+
+ g_ndb_cluster_connection->init_get_next_node(iter);
+
+ while ((id= g_ndb_cluster_connection->get_next_node(iter)))
+ {
+ init_fill_schema_files_row(table);
+ NdbDictionary::Datafile df= dict->getDatafile(id, elt.name);
+ ndberr= dict->getNdbError();
+ if(ndberr.classification != NdbError::NoError)
+ {
+ if (ndberr.classification == NdbError::SchemaError)
+ continue;
+
+ if (ndberr.classification == NdbError::UnknownResultError)
+ continue;
+
+ ERR_RETURN(ndberr);
+ }
+ NdbDictionary::Tablespace ts= dict->getTablespace(df.getTablespace());
+ ndberr= dict->getNdbError();
+ if (ndberr.classification != NdbError::NoError)
+ {
+ if (ndberr.classification == NdbError::SchemaError)
+ continue;
+ ERR_RETURN(ndberr);
+ }
+
+ table->field[IS_FILES_FILE_NAME]->set_notnull();
+ table->field[IS_FILES_FILE_NAME]->store(elt.name, strlen(elt.name),
+ system_charset_info);
+ table->field[IS_FILES_FILE_TYPE]->set_notnull();
+ table->field[IS_FILES_FILE_TYPE]->store("DATAFILE",8,
+ system_charset_info);
+ table->field[IS_FILES_TABLESPACE_NAME]->set_notnull();
+ table->field[IS_FILES_TABLESPACE_NAME]->store(df.getTablespace(),
+ strlen(df.getTablespace()),
+ system_charset_info);
+ table->field[IS_FILES_LOGFILE_GROUP_NAME]->set_notnull();
+ table->field[IS_FILES_LOGFILE_GROUP_NAME]->
+ store(ts.getDefaultLogfileGroup(),
+ strlen(ts.getDefaultLogfileGroup()),
+ system_charset_info);
+ table->field[IS_FILES_ENGINE]->set_notnull();
+ table->field[IS_FILES_ENGINE]->store(ndbcluster_hton_name,
+ ndbcluster_hton_name_length,
+ system_charset_info);
+
+ table->field[IS_FILES_FREE_EXTENTS]->set_notnull();
+ table->field[IS_FILES_FREE_EXTENTS]->store(df.getFree()
+ / ts.getExtentSize());
+ table->field[IS_FILES_TOTAL_EXTENTS]->set_notnull();
+ table->field[IS_FILES_TOTAL_EXTENTS]->store(df.getSize()
+ / ts.getExtentSize());
+ table->field[IS_FILES_EXTENT_SIZE]->set_notnull();
+ table->field[IS_FILES_EXTENT_SIZE]->store(ts.getExtentSize());
+ table->field[IS_FILES_INITIAL_SIZE]->set_notnull();
+ table->field[IS_FILES_INITIAL_SIZE]->store(df.getSize());
+ table->field[IS_FILES_MAXIMUM_SIZE]->set_notnull();
+ table->field[IS_FILES_MAXIMUM_SIZE]->store(df.getSize());
+ table->field[IS_FILES_VERSION]->set_notnull();
+ table->field[IS_FILES_VERSION]->store(df.getObjectVersion());
+
+ table->field[IS_FILES_ROW_FORMAT]->set_notnull();
+ table->field[IS_FILES_ROW_FORMAT]->store("FIXED", 5, system_charset_info);
+
+ char extra[30];
+ int len= my_snprintf(extra, sizeof(extra), "CLUSTER_NODE=%u", id);
+ table->field[IS_FILES_EXTRA]->set_notnull();
+ table->field[IS_FILES_EXTRA]->store(extra, len, system_charset_info);
+ schema_table_store_record(thd, table);
+ }
+ }
+
+ NdbDictionary::Dictionary::List uflist;
+ dict->listObjects(uflist, NdbDictionary::Object::Undofile);
+ ndberr= dict->getNdbError();
+ if (ndberr.classification != NdbError::NoError)
+ ERR_RETURN(ndberr);
+
+ for (i= 0; i < uflist.count; i++)
+ {
+ NdbDictionary::Dictionary::List::Element& elt= uflist.elements[i];
+ Ndb_cluster_connection_node_iter iter;
+ unsigned id;
+
+ g_ndb_cluster_connection->init_get_next_node(iter);
+
+ while ((id= g_ndb_cluster_connection->get_next_node(iter)))
+ {
+ NdbDictionary::Undofile uf= dict->getUndofile(id, elt.name);
+ ndberr= dict->getNdbError();
+ if (ndberr.classification != NdbError::NoError)
+ {
+ if (ndberr.classification == NdbError::SchemaError)
+ continue;
+ if (ndberr.classification == NdbError::UnknownResultError)
+ continue;
+ ERR_RETURN(ndberr);
+ }
+ NdbDictionary::LogfileGroup lfg=
+ dict->getLogfileGroup(uf.getLogfileGroup());
+ ndberr= dict->getNdbError();
+ if (ndberr.classification != NdbError::NoError)
+ {
+ if (ndberr.classification == NdbError::SchemaError)
+ continue;
+ ERR_RETURN(ndberr);
+ }
+
+ init_fill_schema_files_row(table);
+ table->field[IS_FILES_FILE_NAME]->set_notnull();
+ table->field[IS_FILES_FILE_NAME]->store(elt.name, strlen(elt.name),
+ system_charset_info);
+ table->field[IS_FILES_FILE_TYPE]->set_notnull();
+ table->field[IS_FILES_FILE_TYPE]->store("UNDO LOG", 8,
+ system_charset_info);
+ NdbDictionary::ObjectId objid;
+ uf.getLogfileGroupId(&objid);
+ table->field[IS_FILES_LOGFILE_GROUP_NAME]->set_notnull();
+ table->field[IS_FILES_LOGFILE_GROUP_NAME]->store(uf.getLogfileGroup(),
+ strlen(uf.getLogfileGroup()),
+ system_charset_info);
+ table->field[IS_FILES_LOGFILE_GROUP_NUMBER]->set_notnull();
+ table->field[IS_FILES_LOGFILE_GROUP_NUMBER]->store(objid.getObjectId());
+ table->field[IS_FILES_ENGINE]->set_notnull();
+ table->field[IS_FILES_ENGINE]->store(ndbcluster_hton_name,
+ ndbcluster_hton_name_length,
+ system_charset_info);
+
+ table->field[IS_FILES_TOTAL_EXTENTS]->set_notnull();
+ table->field[IS_FILES_TOTAL_EXTENTS]->store(uf.getSize()/4);
+ table->field[IS_FILES_EXTENT_SIZE]->set_notnull();
+ table->field[IS_FILES_EXTENT_SIZE]->store(4);
+
+ table->field[IS_FILES_INITIAL_SIZE]->set_notnull();
+ table->field[IS_FILES_INITIAL_SIZE]->store(uf.getSize());
+ table->field[IS_FILES_MAXIMUM_SIZE]->set_notnull();
+ table->field[IS_FILES_MAXIMUM_SIZE]->store(uf.getSize());
+
+ table->field[IS_FILES_VERSION]->set_notnull();
+ table->field[IS_FILES_VERSION]->store(uf.getObjectVersion());
+
+ char extra[100];
+ int len= my_snprintf(extra,sizeof(extra),"CLUSTER_NODE=%u;UNDO_BUFFER_SIZE=%lu",
+ id, (ulong) lfg.getUndoBufferSize());
+ table->field[IS_FILES_EXTRA]->set_notnull();
+ table->field[IS_FILES_EXTRA]->store(extra, len, system_charset_info);
+ schema_table_store_record(thd, table);
+ }
+ }
+
+ // now for LFGs
+ NdbDictionary::Dictionary::List lfglist;
+ dict->listObjects(lfglist, NdbDictionary::Object::LogfileGroup);
+ ndberr= dict->getNdbError();
+ if (ndberr.classification != NdbError::NoError)
+ ERR_RETURN(ndberr);
+
+ for (i= 0; i < lfglist.count; i++)
+ {
+ NdbDictionary::Dictionary::List::Element& elt= lfglist.elements[i];
+
+ NdbDictionary::LogfileGroup lfg= dict->getLogfileGroup(elt.name);
+ ndberr= dict->getNdbError();
+ if (ndberr.classification != NdbError::NoError)
+ {
+ if (ndberr.classification == NdbError::SchemaError)
+ continue;
+ ERR_RETURN(ndberr);
+ }
+
+ init_fill_schema_files_row(table);
+ table->field[IS_FILES_FILE_TYPE]->set_notnull();
+ table->field[IS_FILES_FILE_TYPE]->store("UNDO LOG", 8,
+ system_charset_info);
+
+ table->field[IS_FILES_LOGFILE_GROUP_NAME]->set_notnull();
+ table->field[IS_FILES_LOGFILE_GROUP_NAME]->store(elt.name,
+ strlen(elt.name),
+ system_charset_info);
+ table->field[IS_FILES_LOGFILE_GROUP_NUMBER]->set_notnull();
+ table->field[IS_FILES_LOGFILE_GROUP_NUMBER]->store(lfg.getObjectId());
+ table->field[IS_FILES_ENGINE]->set_notnull();
+ table->field[IS_FILES_ENGINE]->store(ndbcluster_hton_name,
+ ndbcluster_hton_name_length,
+ system_charset_info);
+
+ table->field[IS_FILES_FREE_EXTENTS]->set_notnull();
+ table->field[IS_FILES_FREE_EXTENTS]->store(lfg.getUndoFreeWords());
+ table->field[IS_FILES_EXTENT_SIZE]->set_notnull();
+ table->field[IS_FILES_EXTENT_SIZE]->store(4);
+
+ table->field[IS_FILES_VERSION]->set_notnull();
+ table->field[IS_FILES_VERSION]->store(lfg.getObjectVersion());
+
+ char extra[100];
+ int len= my_snprintf(extra,sizeof(extra),
+ "UNDO_BUFFER_SIZE=%lu",
+ (ulong) lfg.getUndoBufferSize());
+ table->field[IS_FILES_EXTRA]->set_notnull();
+ table->field[IS_FILES_EXTRA]->store(extra, len, system_charset_info);
+ schema_table_store_record(thd, table);
+ }
+ DBUG_RETURN(0);
+}
+
+SHOW_VAR ndb_status_variables_export[]= {
+ {"Ndb", (char*) &ndb_status_variables, SHOW_ARRAY},
+ {NullS, NullS, SHOW_LONG}
+};
+
+struct st_mysql_storage_engine ndbcluster_storage_engine=
+{ MYSQL_HANDLERTON_INTERFACE_VERSION };
+
+mysql_declare_plugin(ndbcluster)
+{
+ MYSQL_STORAGE_ENGINE_PLUGIN,
+ &ndbcluster_storage_engine,
+ ndbcluster_hton_name,
+ "MySQL AB",
+ "Clustered, fault-tolerant tables",
+ PLUGIN_LICENSE_GPL,
+ ndbcluster_init, /* Plugin Init */
+ NULL, /* Plugin Deinit */
+ 0x0100 /* 1.0 */,
+ ndb_status_variables_export,/* status variables */
+ NULL, /* system variables */
+ NULL /* config options */
+}
+mysql_declare_plugin_end;
+
+#endif
diff --git a/sql/ha_ndbcluster.h b/sql/ha_ndbcluster.h
index 0694de55c53..a17323d3fd6 100644
--- a/sql/ha_ndbcluster.h
+++ b/sql/ha_ndbcluster.h
@@ -24,25 +24,34 @@
#pragma interface /* gcc class implementation */
#endif
+/* Blob tables and events are internal to NDB and must never be accessed */
+#define IS_NDB_BLOB_PREFIX(A) is_prefix(A, "NDB$BLOB")
+
+#include <NdbApi.hpp>
#include <ndbapi_limits.h>
#define NDB_HIDDEN_PRIMARY_KEY_LENGTH 8
#define NDB_DEFAULT_AUTO_PREFETCH 32
-/* Forward declarations */
-class Ndb;
-class NdbOperation;
-class NdbTransaction;
-class NdbRecAttr;
+
+class Ndb; // Forward declaration
+class NdbOperation; // Forward declaration
+class NdbTransaction; // Forward declaration
+class NdbRecAttr; // Forward declaration
class NdbScanOperation;
class NdbIndexScanOperation;
class NdbBlob;
+class NdbIndexStat;
+class NdbEventOperation;
class ha_ndbcluster_cond;
// connectstring to cluster if given by mysqld
extern const char *ndbcluster_connectstring;
extern ulong ndb_cache_check_time;
-extern char opt_ndb_constrbuf[];
+#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,
@@ -53,12 +62,25 @@ typedef enum ndb_index_type {
ORDERED_INDEX = 5
} NDB_INDEX_TYPE;
+typedef enum ndb_index_status {
+ UNDEFINED = 0,
+ ACTIVE = 1,
+ TO_BE_DROPPED = 2
+} NDB_INDEX_STATUS;
+
typedef struct ndb_index_data {
NDB_INDEX_TYPE type;
- void *index;
- void *unique_index;
+ NDB_INDEX_STATUS status;
+ const NdbDictionary::Index *index;
+ const NdbDictionary::Index *unique_index;
unsigned char *unique_index_attrid_map;
bool null_in_unique_index;
+ // In this version stats are not shared between threads
+ NdbIndexStat* index_stat;
+ uint index_stat_cache_entries;
+ // Simple counter mechanism to decide when to connect to db
+ uint index_stat_update_freq;
+ uint index_stat_query_count;
} NDB_INDEX_DATA;
typedef enum ndb_write_op {
@@ -67,15 +89,86 @@ typedef enum ndb_write_op {
NDB_PK_UPDATE = 2
} NDB_WRITE_OP;
+typedef union { const NdbRecAttr *rec; NdbBlob *blob; void *ptr; } NdbValue;
+
+int get_ndb_blobs_value(TABLE* table, NdbValue* value_array,
+ uchar*& buffer, uint& buffer_size,
+ my_ptrdiff_t ptrdiff);
+
+typedef enum {
+ NSS_INITIAL= 0,
+ NSS_DROPPED,
+ NSS_ALTERED
+} NDB_SHARE_STATE;
+
typedef struct st_ndbcluster_share {
+ NDB_SHARE_STATE state;
+ MEM_ROOT mem_root;
THR_LOCK lock;
pthread_mutex_t mutex;
- char *table_name;
- uint table_name_length,use_count;
+ char *key;
+ uint key_length;
+ THD *util_lock;
+ uint use_count;
uint commit_count_lock;
ulonglong commit_count;
+ char *db;
+ char *table_name;
+ Ndb::TupleIdRange tuple_id_range;
+#ifdef HAVE_NDB_BINLOG
+ uint32 connect_count;
+ uint32 flags;
+ NdbEventOperation *op;
+ NdbEventOperation *op_old; // for rename table
+ char *old_names; // for rename table
+ TABLE_SHARE *table_share;
+ TABLE *table;
+ uchar *record[2]; // pointer to allocated records for receiving data
+ NdbValue *ndb_value[2];
+ MY_BITMAP *subscriber_bitmap;
+#endif
} NDB_SHARE;
+inline
+NDB_SHARE_STATE
+get_ndb_share_state(NDB_SHARE *share)
+{
+ NDB_SHARE_STATE state;
+ pthread_mutex_lock(&share->mutex);
+ state= share->state;
+ pthread_mutex_unlock(&share->mutex);
+ return state;
+}
+
+inline
+void
+set_ndb_share_state(NDB_SHARE *share, NDB_SHARE_STATE state)
+{
+ pthread_mutex_lock(&share->mutex);
+ share->state= state;
+ pthread_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);
+ }
+ ~Ndb_tuple_id_range_guard() {
+ pthread_mutex_unlock(&share->mutex);
+ }
+ NDB_SHARE* share;
+ Ndb::TupleIdRange& range;
+};
+
+#ifdef HAVE_NDB_BINLOG
+/* NDB_SHARE.flags */
+#define NSF_HIDDEN_PK 1 /* table has hidden primary key */
+#define NSF_BLOB_FLAG 2 /* table has blob attributes */
+#define NSF_NO_BINLOG 4 /* table should not be binlogged */
+#endif
+
typedef enum ndb_query_state_bits {
NDB_QUERY_NORMAL = 0,
NDB_QUERY_MULTI_READ_RANGE = 1
@@ -85,57 +178,83 @@ typedef enum ndb_query_state_bits {
Place holder for ha_ndbcluster thread specific data
*/
+enum THD_NDB_OPTIONS
+{
+ TNO_NO_LOG_SCHEMA_OP= 1 << 0
+};
+
+enum THD_NDB_TRANS_OPTIONS
+{
+ TNTO_INJECTED_APPLY_STATUS= 1 << 0
+ ,TNTO_NO_LOGGING= 1 << 1
+};
+
+struct Ndb_local_table_statistics {
+ int no_uncommitted_rows_count;
+ ulong last_count;
+ ha_rows records;
+};
+
class Thd_ndb
{
public:
Thd_ndb();
~Thd_ndb();
+
+ void init_open_tables();
+
Ndb *ndb;
ulong count;
uint lock_count;
- NdbTransaction *all;
- NdbTransaction *stmt;
- int error;
+ uint start_stmt_count;
+ NdbTransaction *trans;
+ bool m_error;
+ bool m_slow_path;
+ int m_error_code;
+ uint32 m_query_id; /* query id whn m_error_code was set */
+ uint32 options;
+ uint32 trans_options;
List<NDB_SHARE> changed_tables;
uint query_state;
+ HASH open_tables;
};
class ha_ndbcluster: public handler
{
public:
- ha_ndbcluster(TABLE *table);
+ ha_ndbcluster(handlerton *hton, TABLE_SHARE *table);
~ha_ndbcluster();
+ int ha_initialise();
int open(const char *name, int mode, uint test_if_locked);
int close(void);
- int write_row(byte *buf);
- int update_row(const byte *old_data, byte *new_data);
- int delete_row(const byte *buf);
- int index_init(uint index);
+ int write_row(uchar *buf);
+ int update_row(const uchar *old_data, uchar *new_data);
+ int delete_row(const uchar *buf);
+ int index_init(uint index, bool sorted);
int index_end();
- int index_read(byte *buf, const byte *key, uint key_len,
+ int index_read(uchar *buf, const uchar *key, uint key_len,
enum ha_rkey_function find_flag);
- int index_read_idx(byte *buf, uint index, const byte *key, uint key_len,
- enum ha_rkey_function find_flag);
- int index_next(byte *buf);
- int index_prev(byte *buf);
- int index_first(byte *buf);
- int index_last(byte *buf);
- int index_read_last(byte * buf, const byte * key, uint key_len);
+ int index_next(uchar *buf);
+ int index_prev(uchar *buf);
+ int index_first(uchar *buf);
+ int index_last(uchar *buf);
+ int index_read_last(uchar * buf, const uchar * key, uint key_len);
int rnd_init(bool scan);
int rnd_end();
- int rnd_next(byte *buf);
- int rnd_pos(byte *buf, byte *pos);
- void position(const byte *record);
+ int rnd_next(uchar *buf);
+ int rnd_pos(uchar *buf, uchar *pos);
+ void position(const uchar *record);
int read_range_first(const key_range *start_key,
const key_range *end_key,
bool eq_range, bool sorted);
int read_range_first_to_buf(const key_range *start_key,
const key_range *end_key,
bool eq_range, bool sorted,
- byte* buf);
+ uchar* buf);
int read_range_next();
+ int alter_tablespace(st_alter_tablespace *info);
/**
* Multi range stuff
@@ -147,17 +266,28 @@ class ha_ndbcluster: public handler
bool null_value_index_search(KEY_MULTI_RANGE *ranges,
KEY_MULTI_RANGE *end_range,
HANDLER_BUFFER *buffer);
+
bool get_error_message(int error, String *buf);
+ ha_rows records();
+ 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);
int extra(enum ha_extra_function operation);
int extra_opt(enum ha_extra_function operation, ulong cache_size);
int reset();
int external_lock(THD *thd, int lock_type);
void unlock_row();
int start_stmt(THD *thd, thr_lock_type lock_type);
+ void print_error(int error, myf errflag);
const char * table_type() const;
const char ** bas_ext() const;
- ulong table_flags(void) const;
+ ulonglong table_flags(void) const;
+ void prepare_for_alter();
+ int add_index(TABLE *table_arg, KEY *key_info, uint num_of_keys);
+ int prepare_drop_index(TABLE *table_arg, uint *key_num, uint num_of_keys);
+ int final_drop_index(TABLE *table_arg);
+ void set_part_info(partition_info *part_info);
ulong index_flags(uint idx, uint part, bool all_parts) const;
uint max_supported_record_length() const;
uint max_supported_keys() const;
@@ -168,12 +298,25 @@ class ha_ndbcluster: public handler
int rename_table(const char *from, const char *to);
int delete_table(const char *name);
int create(const char *name, TABLE *form, HA_CREATE_INFO *info);
+ int create_handler_files(const char *file, const char *old_name,
+ int action_flag, HA_CREATE_INFO *info);
+ int get_default_no_partitions(HA_CREATE_INFO *info);
+ bool get_no_parts(const char *name, uint *no_parts);
+ void set_auto_partitions(partition_info *part_info);
+ virtual bool is_fatal_error(int error, uint flags)
+ {
+ if (!handler::is_fatal_error(error, flags) ||
+ error == HA_ERR_NO_PARTITION_FOUND)
+ return FALSE;
+ return TRUE;
+ }
+
THR_LOCK_DATA **store_lock(THD *thd,
THR_LOCK_DATA **to,
enum thr_lock_type lock_type);
bool low_byte_first() const;
- bool has_transactions();
+
const char* index_type(uint key_number);
double scan_time();
@@ -208,7 +351,7 @@ static void set_tabname(const char *pathname, char *tabname);
AND ... AND pushed_condN)
or less restrictive condition, depending on handler's capabilities.
- handler->extra(HA_EXTRA_RESET) call empties the condition stack.
+ handler->reset() call empties the condition stack.
Calls to rnd_init/rnd_end, index_init/index_end etc do not affect the
condition stack.
The current implementation supports arbitrary AND/OR nested conditions
@@ -237,80 +380,110 @@ static void set_tabname(const char *pathname, char *tabname);
uint key_length,
qc_engine_callback *engine_callback,
ulonglong *engine_data);
+
+ bool check_if_incompatible_data(HA_CREATE_INFO *info,
+ uint table_changes);
+
private:
- int alter_table_name(const char *to);
- int drop_table();
- int create_index(const char *name, KEY *key_info, bool unique);
+ friend int ndbcluster_drop_database_impl(const char *path);
+ friend int ndb_handle_schema_change(THD *thd,
+ Ndb *ndb, NdbEventOperation *pOp,
+ NDB_SHARE *share);
+
+ static int delete_table(ha_ndbcluster *h, Ndb *ndb,
+ const char *path,
+ const char *db,
+ const char *table_name);
+ int create_ndb_index(const char *name, KEY *key_info, bool unique);
int create_ordered_index(const char *name, KEY *key_info);
int create_unique_index(const char *name, KEY *key_info);
- int initialize_autoincrement(const void *table);
- enum ILBP {ILBP_CREATE = 0, ILBP_OPEN = 1}; // Index List Build Phase
- int build_index_list(Ndb *ndb, TABLE *tab, enum ILBP phase);
+ int create_index(const char *name, KEY *key_info,
+ NDB_INDEX_TYPE idx_type, uint idx_no);
+// Index list management
+ int create_indexes(Ndb *ndb, TABLE *tab);
+ int open_indexes(Ndb *ndb, TABLE *tab, bool ignore_error);
+ void renumber_indexes(Ndb *ndb, TABLE *tab);
+ int drop_indexes(Ndb *ndb, TABLE *tab);
+ int add_index_handle(THD *thd, NdbDictionary::Dictionary *dict,
+ KEY *key_info, const char *index_name, uint index_no);
int get_metadata(const char* path);
- void release_metadata();
+ void release_metadata(THD *thd, Ndb *ndb);
NDB_INDEX_TYPE get_index_type(uint idx_no) const;
NDB_INDEX_TYPE get_index_type_from_table(uint index_no) const;
+ NDB_INDEX_TYPE get_index_type_from_key(uint index_no, KEY *key_info,
+ bool primary) const;
bool has_null_in_unique_index(uint idx_no) const;
- bool check_index_fields_not_null(uint index_no);
-
- int pk_read(const byte *key, uint key_len, byte *buf);
- int complemented_pk_read(const byte *old_data, byte *new_data);
- bool check_all_operations_for_error(NdbTransaction *trans,
- const NdbOperation *first,
- const NdbOperation *last,
- uint errcode);
- int peek_indexed_rows(const byte *record, NDB_WRITE_OP write_op);
- int unique_index_read(const byte *key, uint key_len,
- byte *buf);
+ bool check_index_fields_not_null(KEY *key_info);
+
+ uint set_up_partition_info(partition_info *part_info,
+ TABLE *table,
+ void *tab);
+ char* get_tablespace_name(THD *thd, char *name, uint name_len);
+ int set_range_data(void *tab, partition_info* part_info);
+ int set_list_data(void *tab, partition_info* part_info);
+ int complemented_read(const uchar *old_data, uchar *new_data,
+ uint32 old_part_id);
+ int pk_read(const uchar *key, uint key_len, uchar *buf, uint32 part_id);
int ordered_index_scan(const key_range *start_key,
const key_range *end_key,
- bool sorted, bool descending, byte* buf);
+ bool sorted, bool descending, uchar* buf,
+ part_id_range *part_spec);
+ int unique_index_read(const uchar *key, uint key_len,
+ uchar *buf);
int unique_index_scan(const KEY* key_info,
- const byte *key,
+ const uchar *key,
uint key_len,
- byte *buf);
+ uchar *buf);
+ int full_table_scan(uchar * buf);
- int full_table_scan(byte * buf);
+ bool check_all_operations_for_error(NdbTransaction *trans,
+ const NdbOperation *first,
+ const NdbOperation *last,
+ uint errcode);
+ int peek_indexed_rows(const uchar *record, NDB_WRITE_OP write_op);
int fetch_next(NdbScanOperation* op);
- int next_result(byte *buf);
int set_auto_inc(Field *field);
- int define_read_attrs(byte* buf, NdbOperation* op);
- int filtered_scan(const byte *key, uint key_len,
- byte *buf,
+ int next_result(uchar *buf);
+ int define_read_attrs(uchar* buf, NdbOperation* op);
+ int filtered_scan(const uchar *key, uint key_len,
+ uchar *buf,
enum ha_rkey_function find_flag);
int close_scan();
- void unpack_record(byte *buf);
+ void unpack_record(uchar *buf);
int get_ndb_lock_type(enum thr_lock_type type);
void set_dbname(const char *pathname);
void set_tabname(const char *pathname);
bool set_hidden_key(NdbOperation*,
- uint fieldnr, const byte* field_ptr);
+ uint fieldnr, const uchar* field_ptr);
int set_ndb_key(NdbOperation*, Field *field,
- uint fieldnr, const byte* field_ptr);
- int set_ndb_value(NdbOperation*, Field *field, uint fieldnr, bool *set_blob_value= 0);
- int get_ndb_value(NdbOperation*, Field *field, uint fieldnr, byte*);
+ uint fieldnr, const uchar* field_ptr);
+ int set_ndb_value(NdbOperation*, Field *field, uint fieldnr,
+ int row_offset= 0, bool *set_blob_value= 0);
+ int get_ndb_value(NdbOperation*, Field *field, uint fieldnr, uchar*);
+ int get_ndb_partition_id(NdbOperation *);
friend int g_get_ndb_blobs_value(NdbBlob *ndb_blob, void *arg);
- int get_ndb_blobs_value(NdbBlob *last_ndb_blob, my_ptrdiff_t ptrdiff);
- int set_primary_key(NdbOperation *op, const byte *key);
- int set_primary_key_from_record(NdbOperation *op, const byte *record);
+ int set_primary_key(NdbOperation *op, const uchar *key);
+ int set_primary_key_from_record(NdbOperation *op, const uchar *record);
bool check_index_fields_in_write_set(uint keyno);
- int set_index_key_from_record(NdbOperation *op, const byte *record,
+ int set_index_key_from_record(NdbOperation *op, const uchar *record,
uint keyno);
- int set_bounds(NdbIndexScanOperation*, const key_range *keys[2], uint= 0);
- int key_cmp(uint keynr, const byte * old_row, const byte * new_row);
- int set_index_key(NdbOperation *, const KEY *key_info, const byte *key_ptr);
+ int set_bounds(NdbIndexScanOperation*, uint inx, bool rir,
+ const key_range *keys[2], uint= 0);
+ int key_cmp(uint keynr, const uchar * old_row, const uchar * new_row);
+ int set_index_key(NdbOperation *, const KEY *key_info, const uchar *key_ptr);
void print_results();
- ulonglong get_auto_increment();
- void invalidate_dictionary_cache(bool global);
-
-bool uses_blob_value(bool all_fields);
+ virtual void get_auto_increment(ulonglong offset, ulonglong increment,
+ ulonglong nb_desired_values,
+ ulonglong *first_value,
+ ulonglong *nb_reserved_values);
+ bool uses_blob_value();
char *update_table_comment(const char * comment);
- int write_ndb_file();
+ int write_ndb_file(const char *name);
int check_ndb_connection(THD* thd= current_thd);
@@ -318,39 +491,48 @@ bool uses_blob_value(bool all_fields);
int records_update();
void no_uncommitted_rows_execute_failure();
void no_uncommitted_rows_update(int);
- void no_uncommitted_rows_init(THD *);
void no_uncommitted_rows_reset(THD *);
void release_completed_operations(NdbTransaction*, bool);
friend int execute_commit(ha_ndbcluster*, NdbTransaction*);
+ friend int execute_no_commit_ignore_no_key(ha_ndbcluster*, NdbTransaction*);
friend int execute_no_commit(ha_ndbcluster*, NdbTransaction*, bool);
friend int execute_no_commit_ie(ha_ndbcluster*, NdbTransaction*, bool);
+ void transaction_checks(THD *thd);
+ int start_statement(THD *thd, Thd_ndb *thd_ndb, Ndb* ndb);
+ int init_handler_for_statement(THD *thd, Thd_ndb *thd_ndb);
+
NdbTransaction *m_active_trans;
NdbScanOperation *m_active_cursor;
- void *m_table;
- int m_table_version;
- void *m_table_info;
+ const NdbDictionary::Table *m_table;
+ struct Ndb_local_table_statistics *m_table_info;
+ struct Ndb_local_table_statistics m_table_info_instance;
char m_dbname[FN_HEADLEN];
//char m_schemaname[FN_HEADLEN];
char m_tabname[FN_HEADLEN];
- ulong m_table_flags;
+ ulonglong m_table_flags;
THR_LOCK_DATA m_lock;
bool m_lock_tuple;
NDB_SHARE *m_share;
NDB_INDEX_DATA m_index[MAX_KEY];
// NdbRecAttr has no reference to blob
- typedef union { const NdbRecAttr *rec; NdbBlob *blob; void *ptr; } NdbValue;
NdbValue m_value[NDB_MAX_ATTRIBUTES_IN_TABLE];
- byte m_ref[NDB_HIDDEN_PRIMARY_KEY_LENGTH];
+ uchar m_ref[NDB_HIDDEN_PRIMARY_KEY_LENGTH];
+ partition_info *m_part_info;
+ uint32 m_part_id;
+ uchar *m_rec0;
+ Field **m_part_field_array;
+ bool m_use_partition_function;
+ bool m_sorted;
bool m_use_write;
bool m_ignore_dup_key;
bool m_has_unique_index;
bool m_primary_key_update;
- bool m_retrieve_all_fields;
- bool m_retrieve_primary_key;
- ha_rows m_rows_to_insert;
+ bool m_write_op;
+ bool m_ignore_no_key;
+ ha_rows m_rows_to_insert; // TODO: merge it with handler::estimation_rows_to_insert?
ha_rows m_rows_inserted;
ha_rows m_bulk_insert_rows;
ha_rows m_rows_changed;
@@ -360,9 +542,10 @@ bool uses_blob_value(bool all_fields);
ha_rows m_ops_pending;
bool m_skip_auto_increment;
bool m_blobs_pending;
+ bool m_slow_path;
my_ptrdiff_t m_blobs_offset;
// memory for blobs in one tuple
- char *m_blobs_buffer;
+ uchar *m_blobs_buffer;
uint32 m_blobs_buffer_size;
uint m_dupkey;
// set from thread variables at external lock
@@ -373,29 +556,28 @@ bool uses_blob_value(bool all_fields);
ha_ndbcluster_cond *m_cond;
bool m_disable_multi_read;
- byte *m_multi_range_result_ptr;
+ uchar *m_multi_range_result_ptr;
KEY_MULTI_RANGE *m_multi_ranges;
KEY_MULTI_RANGE *m_multi_range_defined;
const NdbOperation *m_current_multi_operation;
NdbIndexScanOperation *m_multi_cursor;
- byte *m_multi_range_cursor_result_ptr;
+ uchar *m_multi_range_cursor_result_ptr;
int setup_recattr(const NdbRecAttr*);
Ndb *get_ndb();
};
-extern struct show_var_st ndb_status_variables[];
-
-bool ndbcluster_init(void);
-bool ndbcluster_end(void);
+extern SHOW_VAR ndb_status_variables[];
int ndbcluster_discover(THD* thd, const char* dbname, const char* name,
const void** frmblob, uint* frmlen);
int ndbcluster_find_files(THD *thd,const char *db,const char *path,
- const char *wild, bool dir, List<char> *files);
+ const char *wild, bool dir, List<LEX_STRING> *files);
int ndbcluster_table_exists_in_engine(THD* thd,
const char *db, const char *name);
-int ndbcluster_drop_database(const char* path);
-
void ndbcluster_print_error(int error, const NdbOperation *error_op);
-int ndbcluster_show_status(THD*);
+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;
diff --git a/sql/ha_ndbcluster_binlog.cc b/sql/ha_ndbcluster_binlog.cc
new file mode 100644
index 00000000000..baf86d739eb
--- /dev/null
+++ b/sql/ha_ndbcluster_binlog.cc
@@ -0,0 +1,4425 @@
+/* Copyright (C) 2000-2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#include "mysql_priv.h"
+#include "sql_show.h"
+#ifdef WITH_NDBCLUSTER_STORAGE_ENGINE
+#include "ha_ndbcluster.h"
+
+#ifdef HAVE_NDB_BINLOG
+#include "rpl_injector.h"
+#include "rpl_filter.h"
+#include "slave.h"
+#include "ha_ndbcluster_binlog.h"
+#include "NdbDictionary.hpp"
+#include "ndb_cluster_connection.hpp"
+#include <util/NdbAutoPtr.hpp>
+
+#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
+
+/*
+ defines for cluster replication table names
+*/
+#include "ha_ndbcluster_tables.h"
+#define NDB_APPLY_TABLE_FILE "./" NDB_REP_DB "/" NDB_APPLY_TABLE
+#define NDB_SCHEMA_TABLE_FILE "./" NDB_REP_DB "/" NDB_SCHEMA_TABLE
+
+/*
+ Timeout for syncing schema events between
+ mysql servers, and between mysql server and the binlog
+*/
+const int opt_ndb_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;
+/*
+ Flag showing if the ndb binlog should be created, if so == TRUE
+ FALSE if not
+*/
+my_bool ndb_binlog_running= FALSE;
+my_bool ndb_binlog_tables_inited= FALSE;
+
+/*
+ Global reference to the ndb injector thread THD oject
+
+ Has one sole purpose, for setting the in_use table member variable
+ in get_share(...)
+*/
+THD *injector_thd= 0;
+
+/*
+ Global reference to ndb injector thd object.
+
+ Used mainly by the binlog index thread, but exposed to the client sql
+ thread for one reason; to setup the events operations for a table
+ to enable ndb injector thread receiving events.
+
+ Must therefore always be used with a surrounding
+ pthread_mutex_lock(&injector_mutex), when doing create/dropEventOperation
+*/
+static Ndb *injector_ndb= 0;
+static Ndb *schema_ndb= 0;
+
+static int ndbcluster_binlog_inited= 0;
+/*
+ Flag "ndbcluster_binlog_terminating" set when shutting down mysqld.
+ Server main loop should call handlerton function:
+
+ ndbcluster_hton->binlog_func ==
+ ndbcluster_binlog_func(...,BFN_BINLOG_END,...) ==
+ ndbcluster_binlog_end
+
+ at shutdown, which sets the flag. And then server needs to wait for it
+ to complete. Otherwise binlog will not be complete.
+
+ ndbcluster_hton->panic == ndbcluster_end() will not return until
+ ndb binlog is completed
+*/
+static int ndbcluster_binlog_terminating= 0;
+
+/*
+ Mutex and condition used for interacting between client sql thread
+ and injector thread
+*/
+pthread_t ndb_binlog_thread;
+pthread_mutex_t injector_mutex;
+pthread_cond_t injector_cond;
+
+/* NDB Injector thread (used for binlog creation) */
+static ulonglong ndb_latest_applied_binlog_epoch= 0;
+static ulonglong ndb_latest_handled_binlog_epoch= 0;
+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;
+
+extern my_bool opt_log_slave_updates;
+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;
+ char *key;
+ uint key_length;
+ uint use_count;
+ MY_BITMAP slock_bitmap;
+ uint32 slock[256/32]; // 256 bits for lock status of table
+} NDB_SCHEMA_OBJECT;
+static NDB_SCHEMA_OBJECT *ndb_get_schema_object(const char *key,
+ my_bool create_if_not_exists,
+ my_bool have_lock);
+static void ndb_free_schema_object(NDB_SCHEMA_OBJECT **ndb_schema_object,
+ bool have_lock);
+
+static Uint64 *p_latest_trans_gci= 0;
+
+/*
+ Global variables for holding the ndb_binlog_index table reference
+*/
+static TABLE *ndb_binlog_index= 0;
+static TABLE_LIST binlog_tables;
+
+/*
+ Helper functions
+*/
+
+#ifndef DBUG_OFF
+/* purecov: begin deadcode */
+static void print_records(TABLE *table, const uchar *record)
+{
+ for (uint j= 0; j < table->s->fields; j++)
+ {
+ char buf[40];
+ int pos= 0;
+ Field *field= table->field[j];
+ const uchar* field_ptr= field->ptr - table->record[0] + record;
+ int pack_len= field->pack_length();
+ int n= pack_len < 10 ? pack_len : 10;
+
+ for (int i= 0; i < n && pos < 20; i++)
+ {
+ pos+= sprintf(&buf[pos]," %x", (int) (uchar) field_ptr[i]);
+ }
+ buf[pos]= 0;
+ DBUG_PRINT("info",("[%u]field_ptr[0->%d]: %s", j, n, buf));
+ }
+}
+/* purecov: end */
+#else
+#define print_records(a,b)
+#endif
+
+
+#ifndef DBUG_OFF
+static void dbug_print_table(const char *info, TABLE *table)
+{
+ if (table == 0)
+ {
+ DBUG_PRINT("info",("%s: (null)", info));
+ return;
+ }
+ DBUG_PRINT("info",
+ ("%s: %s.%s s->fields: %d "
+ "reclength: %lu rec_buff_length: %u record[0]: 0x%lx "
+ "record[1]: 0x%lx",
+ info,
+ table->s->db.str,
+ table->s->table_name.str,
+ table->s->fields,
+ table->s->reclength,
+ table->s->rec_buff_length,
+ (long) table->record[0],
+ (long) table->record[1]));
+
+ for (unsigned int i= 0; i < table->s->fields; i++)
+ {
+ Field *f= table->field[i];
+ DBUG_PRINT("info",
+ ("[%d] \"%s\"(0x%lx:%s%s%s%s%s%s) type: %d pack_length: %d "
+ "ptr: 0x%lx[+%d] null_bit: %u null_ptr: 0x%lx[+%d]",
+ i,
+ f->field_name,
+ (long) f->flags,
+ (f->flags & PRI_KEY_FLAG) ? "pri" : "attr",
+ (f->flags & NOT_NULL_FLAG) ? "" : ",nullable",
+ (f->flags & UNSIGNED_FLAG) ? ",unsigned" : ",signed",
+ (f->flags & ZEROFILL_FLAG) ? ",zerofill" : "",
+ (f->flags & BLOB_FLAG) ? ",blob" : "",
+ (f->flags & BINARY_FLAG) ? ",binary" : "",
+ f->real_type(),
+ f->pack_length(),
+ (long) f->ptr, (int) (f->ptr - table->record[0]),
+ f->null_bit,
+ (long) f->null_ptr,
+ (int) ((uchar*) f->null_ptr - table->record[0])));
+ if (f->type() == MYSQL_TYPE_BIT)
+ {
+ Field_bit *g= (Field_bit*) f;
+ DBUG_PRINT("MYSQL_TYPE_BIT",("field_length: %d bit_ptr: 0x%lx[+%d] "
+ "bit_ofs: %d bit_len: %u",
+ g->field_length, (long) g->bit_ptr,
+ (int) ((uchar*) g->bit_ptr -
+ table->record[0]),
+ g->bit_ofs, g->bit_len));
+ }
+ }
+}
+#else
+#define dbug_print_table(a,b)
+#endif
+
+
+/*
+ Run a query through mysql_parse
+
+ Used to:
+ - purging the ndb_binlog_index
+ - creating the ndb_apply_status table
+*/
+static void run_query(THD *thd, char *buf, char *end,
+ const int *no_print_error, my_bool disable_binlog)
+{
+ ulong save_thd_query_length= thd->query_length;
+ char *save_thd_query= thd->query;
+ ulong save_thread_id= thd->variables.pseudo_thread_id;
+ 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));
+ NET save_thd_net= thd->net;
+ const char* found_semicolon= NULL;
+
+ bzero((char*) &thd->net, sizeof(NET));
+ thd->query_length= end - buf;
+ thd->query= buf;
+ thd->variables.pseudo_thread_id= thread_id;
+ thd->transaction.stmt.modified_non_trans_table= FALSE;
+ if (disable_binlog)
+ thd->options&= ~OPTION_BIN_LOG;
+
+ DBUG_PRINT("query", ("%s", thd->query));
+
+ DBUG_ASSERT(!thd->in_sub_stmt);
+ DBUG_ASSERT(!thd->prelocked_mode);
+
+ mysql_parse(thd, thd->query, thd->query_length, &found_semicolon);
+
+ if (no_print_error && thd->is_slave_error)
+ {
+ int i;
+ 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]))
+ 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_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
+ run_query() can be called from anywhere, including from within
+ a sub-statement.
+ This particular reset is a temporary hack to avoid an assert
+ for double assignment of the diagnostics area when run_query()
+ is called from ndbcluster_reset_logs(), which is called from
+ mysql_flush().
+ */
+ thd->main_da.reset_diagnostics_area();
+
+ thd->options= save_thd_options;
+ thd->query_length= save_thd_query_length;
+ thd->query= save_thd_query;
+ 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;
+
+ if (thd == injector_thd)
+ {
+ /*
+ running the query will close all tables, including the ndb_binlog_index
+ used in injector_thd
+ */
+ ndb_binlog_index= 0;
+ }
+}
+
+static void
+ndbcluster_binlog_close_table(THD *thd, NDB_SHARE *share)
+{
+ DBUG_ENTER("ndbcluster_binlog_close_table");
+ if (share->table_share)
+ {
+ closefrm(share->table, 1);
+ share->table_share= 0;
+ share->table= 0;
+ }
+ DBUG_ASSERT(share->table == 0);
+ DBUG_VOID_RETURN;
+}
+
+
+/*
+ Creates a TABLE object for the ndb cluster table
+
+ NOTES
+ This does not open the underlying table
+*/
+
+static int
+ndbcluster_binlog_open_table(THD *thd, NDB_SHARE *share,
+ TABLE_SHARE *table_share, TABLE *table,
+ int reopen)
+{
+ 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)))
+ {
+ DBUG_PRINT("error", ("open_table_def failed: %d my_errno: %d", error, my_errno));
+ free_table_share(table_share);
+ DBUG_RETURN(error);
+ }
+ if ((error= open_table_from_share(thd, table_share, "", 0 /* fon't allocate buffers */,
+ (uint) READ_ALL, 0, table, FALSE)))
+ {
+ DBUG_PRINT("error", ("open_table_from_share failed %d my_errno: %d", error, my_errno));
+ free_table_share(table_share);
+ DBUG_RETURN(error);
+ }
+ assign_new_table_id(table_share);
+
+ if (!reopen)
+ {
+ // allocate memory on ndb share so it can be reused after online alter table
+ (void)multi_alloc_root(&share->mem_root,
+ &(share->record[0]), table->s->rec_buff_length,
+ &(share->record[1]), table->s->rec_buff_length,
+ NULL);
+ }
+ {
+ my_ptrdiff_t row_offset= share->record[0] - table->record[0];
+ Field **p_field;
+ for (p_field= table->field; *p_field; p_field++)
+ (*p_field)->move_field_offset(row_offset);
+ table->record[0]= share->record[0];
+ table->record[1]= share->record[1];
+ }
+
+ table->in_use= injector_thd;
+
+ table->s->db.str= share->db;
+ table->s->db.length= strlen(share->db);
+ table->s->table_name.str= share->table_name;
+ table->s->table_name.length= strlen(share->table_name);
+
+ DBUG_ASSERT(share->table_share == 0);
+ share->table_share= table_share;
+ DBUG_ASSERT(share->table == 0);
+ share->table= table;
+ /* We can't use 'use_all_columns()' as the file object is not setup yet */
+ table->column_bitmaps_set_no_signal(&table->s->all_set, &table->s->all_set);
+#ifndef DBUG_OFF
+ dbug_print_table("table", table);
+#endif
+ DBUG_RETURN(0);
+}
+
+
+/*
+ Initialize the binlog part of the NDB_SHARE
+*/
+int ndbcluster_binlog_init_share(NDB_SHARE *share, TABLE *_table)
+{
+ THD *thd= current_thd;
+ MEM_ROOT *mem_root= &share->mem_root;
+ int do_event_op= ndb_binlog_running;
+ int error= 0;
+ DBUG_ENTER("ndbcluster_binlog_init_share");
+
+ share->connect_count= g_ndb_cluster_connection->get_connect_count();
+
+ share->op= 0;
+ share->table= 0;
+
+ if (!ndb_schema_share &&
+ strcmp(share->db, NDB_REP_DB) == 0 &&
+ strcmp(share->table_name, NDB_SCHEMA_TABLE) == 0)
+ do_event_op= 1;
+ else if (!ndb_apply_status_share &&
+ strcmp(share->db, NDB_REP_DB) == 0 &&
+ strcmp(share->table_name, NDB_APPLY_TABLE) == 0)
+ do_event_op= 1;
+
+ {
+ int i, no_nodes= g_ndb_cluster_connection->no_db_nodes();
+ share->subscriber_bitmap= (MY_BITMAP*)
+ alloc_root(mem_root, no_nodes * sizeof(MY_BITMAP));
+ for (i= 0; i < no_nodes; i++)
+ {
+ bitmap_init(&share->subscriber_bitmap[i],
+ (Uint32*)alloc_root(mem_root, max_ndb_nodes/8),
+ max_ndb_nodes, FALSE);
+ bitmap_clear_all(&share->subscriber_bitmap[i]);
+ }
+ }
+
+ if (!do_event_op)
+ {
+ if (_table)
+ {
+ if (_table->s->primary_key == MAX_KEY)
+ share->flags|= NSF_HIDDEN_PK;
+ if (_table->s->blob_fields != 0)
+ share->flags|= NSF_BLOB_FLAG;
+ }
+ else
+ {
+ share->flags|= NSF_NO_BINLOG;
+ }
+ DBUG_RETURN(error);
+ }
+ while (1)
+ {
+ int error;
+ TABLE_SHARE *table_share= (TABLE_SHARE *) alloc_root(mem_root, sizeof(*table_share));
+ TABLE *table= (TABLE*) alloc_root(mem_root, sizeof(*table));
+ if ((error= ndbcluster_binlog_open_table(thd, share, table_share, table, 0)))
+ break;
+ /*
+ ! do not touch the contents of the table
+ it may be in use by the injector thread
+ */
+ MEM_ROOT *mem_root= &share->mem_root;
+ share->ndb_value[0]= (NdbValue*)
+ alloc_root(mem_root, sizeof(NdbValue) *
+ (table->s->fields + 2 /*extra for hidden key and part key*/));
+ share->ndb_value[1]= (NdbValue*)
+ alloc_root(mem_root, sizeof(NdbValue) *
+ (table->s->fields + 2 /*extra for hidden key and part key*/));
+
+ if (table->s->primary_key == MAX_KEY)
+ share->flags|= NSF_HIDDEN_PK;
+ if (table->s->blob_fields != 0)
+ share->flags|= NSF_BLOB_FLAG;
+ break;
+ }
+ DBUG_RETURN(error);
+}
+
+/*****************************************************************
+ functions called from master sql client threads
+****************************************************************/
+
+/*
+ called in mysql_show_binlog_events and reset_logs to make sure we wait for
+ all events originating from this mysql server to arrive in the binlog
+
+ Wait for the last epoch in which the last transaction is a part of.
+
+ Wait a maximum of 30 seconds.
+*/
+static void ndbcluster_binlog_wait(THD *thd)
+{
+ if (ndb_binlog_running)
+ {
+ DBUG_ENTER("ndbcluster_binlog_wait");
+ const char *save_info= thd ? thd->proc_info : 0;
+ ulonglong wait_epoch= *p_latest_trans_gci;
+ int count= 30;
+ if (thd)
+ thd->proc_info= "Waiting for ndbcluster binlog update to "
+ "reach current position";
+ while (count && ndb_binlog_running &&
+ ndb_latest_handled_binlog_epoch < wait_epoch)
+ {
+ count--;
+ sleep(1);
+ }
+ if (thd)
+ thd->proc_info= save_info;
+ DBUG_VOID_RETURN;
+ }
+}
+
+/*
+ Called from MYSQL_BIN_LOG::reset_logs in log.cc when binlog is emptied
+*/
+static int ndbcluster_reset_logs(THD *thd)
+{
+ if (!ndb_binlog_running)
+ return 0;
+
+ DBUG_ENTER("ndbcluster_reset_logs");
+
+ /*
+ Wait for all events orifinating from this mysql server has
+ reached the binlog before continuing to reset
+ */
+ ndbcluster_binlog_wait(thd);
+
+ char buf[1024];
+ char *end= strmov(buf, "DELETE FROM " NDB_REP_DB "." NDB_REP_TABLE);
+
+ run_query(thd, buf, end, NULL, TRUE);
+
+ DBUG_RETURN(0);
+}
+
+/*
+ Called from MYSQL_BIN_LOG::purge_logs in log.cc when the binlog "file"
+ is removed
+*/
+
+static int
+ndbcluster_binlog_index_purge_file(THD *thd, const char *file)
+{
+ if (!ndb_binlog_running || thd->slave_thread)
+ return 0;
+
+ DBUG_ENTER("ndbcluster_binlog_index_purge_file");
+ DBUG_PRINT("enter", ("file: %s", file));
+
+ char buf[1024];
+ char *end= strmov(strmov(strmov(buf,
+ "DELETE FROM "
+ NDB_REP_DB "." NDB_REP_TABLE
+ " WHERE File='"), file), "'");
+
+ run_query(thd, buf, end, NULL, TRUE);
+
+ DBUG_RETURN(0);
+}
+
+static void
+ndbcluster_binlog_log_query(handlerton *hton, THD *thd, enum_binlog_command binlog_command,
+ const char *query, uint query_length,
+ const char *db, const char *table_name)
+{
+ DBUG_ENTER("ndbcluster_binlog_log_query");
+ DBUG_PRINT("enter", ("db: %s table_name: %s query: %s",
+ db, table_name, query));
+ enum SCHEMA_OP_TYPE type;
+ int log= 0;
+ switch (binlog_command)
+ {
+ case LOGCOM_CREATE_TABLE:
+ type= SOT_CREATE_TABLE;
+ DBUG_ASSERT(FALSE);
+ break;
+ case LOGCOM_ALTER_TABLE:
+ type= SOT_ALTER_TABLE;
+ log= 1;
+ break;
+ case LOGCOM_RENAME_TABLE:
+ type= SOT_RENAME_TABLE;
+ DBUG_ASSERT(FALSE);
+ break;
+ case LOGCOM_DROP_TABLE:
+ type= SOT_DROP_TABLE;
+ DBUG_ASSERT(FALSE);
+ break;
+ case LOGCOM_CREATE_DB:
+ type= SOT_CREATE_DB;
+ log= 1;
+ break;
+ case LOGCOM_ALTER_DB:
+ type= SOT_ALTER_DB;
+ log= 1;
+ break;
+ case LOGCOM_DROP_DB:
+ type= SOT_DROP_DB;
+ DBUG_ASSERT(FALSE);
+ break;
+ }
+ if (log)
+ {
+ ndbcluster_log_schema_op(thd, 0, query, query_length,
+ db, table_name, 0, 0, type,
+ 0, 0, 0);
+ }
+ DBUG_VOID_RETURN;
+}
+
+
+/*
+ End use of the NDB Cluster binlog
+ - wait for binlog thread to shutdown
+*/
+
+static int ndbcluster_binlog_end(THD *thd)
+{
+ DBUG_ENTER("ndbcluster_binlog_end");
+
+ if (!ndbcluster_binlog_inited)
+ DBUG_RETURN(0);
+ ndbcluster_binlog_inited= 0;
+
+#ifdef HAVE_NDB_BINLOG
+ if (ndb_util_thread_running > 0)
+ {
+ /*
+ Wait for util thread to die (as this uses the injector mutex)
+ There is a very small change that ndb_util_thread dies and the
+ following mutex is freed before it's accessed. This shouldn't
+ 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);
+ /* 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);
+ while (ndb_util_thread_running > 1)
+ pthread_cond_wait(&COND_ndb_util_ready, &LOCK_ndb_util_thread);
+ ndb_util_thread_running--;
+ pthread_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);
+ while (ndb_binlog_thread_running > 0)
+ pthread_cond_wait(&injector_cond, &injector_mutex);
+ pthread_mutex_unlock(&injector_mutex);
+
+ pthread_mutex_destroy(&injector_mutex);
+ pthread_cond_destroy(&injector_cond);
+ pthread_mutex_destroy(&ndb_schema_share_mutex);
+#endif
+
+ DBUG_RETURN(0);
+}
+
+/*****************************************************************
+ functions called from slave sql client threads
+****************************************************************/
+static void ndbcluster_reset_slave(THD *thd)
+{
+ if (!ndb_binlog_running)
+ return;
+
+ DBUG_ENTER("ndbcluster_reset_slave");
+ char buf[1024];
+ char *end= strmov(buf, "DELETE FROM " NDB_REP_DB "." NDB_APPLY_TABLE);
+ run_query(thd, buf, end, NULL, TRUE);
+ DBUG_VOID_RETURN;
+}
+
+/*
+ Initialize the binlog part of the ndb handlerton
+*/
+
+/**
+ Upon the sql command flush logs, we need to ensure that all outstanding
+ ndb data to be logged has made it to the binary log to get a deterministic
+ behavior on the rotation of the log.
+ */
+static bool ndbcluster_flush_logs(handlerton *hton)
+{
+ ndbcluster_binlog_wait(current_thd);
+ return FALSE;
+}
+
+static int ndbcluster_binlog_func(handlerton *hton, THD *thd,
+ enum_binlog_func fn,
+ void *arg)
+{
+ switch(fn)
+ {
+ case BFN_RESET_LOGS:
+ ndbcluster_reset_logs(thd);
+ break;
+ case BFN_RESET_SLAVE:
+ ndbcluster_reset_slave(thd);
+ break;
+ case BFN_BINLOG_WAIT:
+ ndbcluster_binlog_wait(thd);
+ break;
+ case BFN_BINLOG_END:
+ ndbcluster_binlog_end(thd);
+ break;
+ case BFN_BINLOG_PURGE_FILE:
+ ndbcluster_binlog_index_purge_file(thd, (const char *)arg);
+ break;
+ }
+ return 0;
+}
+
+void ndbcluster_binlog_init_handlerton()
+{
+ handlerton *h= ndbcluster_hton;
+ h->flush_logs= ndbcluster_flush_logs;
+ h->binlog_func= ndbcluster_binlog_func;
+ h->binlog_log_query= ndbcluster_binlog_log_query;
+}
+
+
+
+
+
+/*
+ check the availability af the ndb_apply_status share
+ - return share, but do not increase refcount
+ - return 0 if there is no share
+*/
+static NDB_SHARE *ndbcluster_check_ndb_apply_status_share()
+{
+ pthread_mutex_lock(&ndbcluster_mutex);
+
+ void *share= 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);
+ return (NDB_SHARE*) share;
+}
+
+/*
+ check the availability af the schema share
+ - return share, but do not increase refcount
+ - return 0 if there is no share
+*/
+static NDB_SHARE *ndbcluster_check_ndb_schema_share()
+{
+ pthread_mutex_lock(&ndbcluster_mutex);
+
+ void *share= 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);
+ return (NDB_SHARE*) share;
+}
+
+/*
+ Create the ndb_apply_status table
+*/
+static int ndbcluster_create_ndb_apply_status_table(THD *thd)
+{
+ DBUG_ENTER("ndbcluster_create_ndb_apply_status_table");
+
+ /*
+ Check if we already have the apply status table.
+ If so it should have been discovered at startup
+ and thus have a share
+ */
+
+ if (ndbcluster_check_ndb_apply_status_share())
+ DBUG_RETURN(0);
+
+ if (g_ndb_cluster_connection->get_no_ready() <= 0)
+ DBUG_RETURN(0);
+
+ char buf[1024], *end;
+
+ if (ndb_extra_logging)
+ sql_print_information("NDB: Creating " NDB_REP_DB "." NDB_APPLY_TABLE);
+
+ /*
+ Check if apply status table exists in MySQL "dictionary"
+ if so, remove it since there is none in Ndb
+ */
+ {
+ build_table_filename(buf, sizeof(buf),
+ NDB_REP_DB, NDB_APPLY_TABLE, reg_ext, 0);
+ my_delete(buf, MYF(0));
+ }
+
+ /*
+ Note, updating this table schema must be reflected in ndb_restore
+ */
+ end= strmov(buf, "CREATE TABLE IF NOT EXISTS "
+ NDB_REP_DB "." NDB_APPLY_TABLE
+ " ( server_id INT UNSIGNED NOT NULL,"
+ " epoch BIGINT UNSIGNED NOT NULL, "
+ " log_name VARCHAR(255) BINARY NOT NULL, "
+ " start_pos BIGINT UNSIGNED NOT NULL, "
+ " end_pos BIGINT UNSIGNED NOT NULL, "
+ " PRIMARY KEY USING HASH (server_id) ) ENGINE=NDB CHARACTER SET latin1");
+
+ const int no_print_error[6]= {ER_TABLE_EXISTS_ERROR,
+ 701,
+ 702,
+ 721, // Table already exist
+ 4009,
+ 0}; // do not print error 701 etc
+ run_query(thd, buf, end, no_print_error, TRUE);
+
+ DBUG_RETURN(0);
+}
+
+
+/*
+ Create the schema table
+*/
+static int ndbcluster_create_schema_table(THD *thd)
+{
+ DBUG_ENTER("ndbcluster_create_schema_table");
+
+ /*
+ Check if we already have the schema table.
+ If so it should have been discovered at startup
+ and thus have a share
+ */
+
+ if (ndbcluster_check_ndb_schema_share())
+ DBUG_RETURN(0);
+
+ if (g_ndb_cluster_connection->get_no_ready() <= 0)
+ DBUG_RETURN(0);
+
+ char buf[1024], *end;
+
+ if (ndb_extra_logging)
+ sql_print_information("NDB: Creating " NDB_REP_DB "." NDB_SCHEMA_TABLE);
+
+ /*
+ Check if schema table exists in MySQL "dictionary"
+ if so, remove it since there is none in Ndb
+ */
+ {
+ build_table_filename(buf, sizeof(buf),
+ NDB_REP_DB, NDB_SCHEMA_TABLE, reg_ext, 0);
+ my_delete(buf, MYF(0));
+ }
+
+ /*
+ Update the defines below to reflect the table schema
+ */
+ end= strmov(buf, "CREATE TABLE IF NOT EXISTS "
+ NDB_REP_DB "." NDB_SCHEMA_TABLE
+ " ( db VARBINARY(63) NOT NULL,"
+ " name VARBINARY(63) NOT NULL,"
+ " slock BINARY(32) NOT NULL,"
+ " query BLOB NOT NULL,"
+ " node_id INT UNSIGNED NOT NULL,"
+ " epoch BIGINT UNSIGNED NOT NULL,"
+ " id INT UNSIGNED NOT NULL,"
+ " version INT UNSIGNED NOT NULL,"
+ " type INT UNSIGNED NOT NULL,"
+ " PRIMARY KEY USING HASH (db,name) ) ENGINE=NDB CHARACTER SET latin1");
+
+ const int no_print_error[6]= {ER_TABLE_EXISTS_ERROR,
+ 701,
+ 702,
+ 721, // Table already exist
+ 4009,
+ 0}; // do not print error 701 etc
+ run_query(thd, buf, end, no_print_error, TRUE);
+
+ DBUG_RETURN(0);
+}
+
+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);
+ // always make sure we create the 'schema' first
+ if (!ndb_schema_share)
+ return 1;
+ }
+ }
+ 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);
+ if (!ndb_apply_status_share)
+ return 1;
+ }
+ }
+ if (!ndbcluster_find_all_files(thd))
+ {
+ pthread_mutex_lock(&LOCK_open);
+ ndb_binlog_tables_inited= TRUE;
+ if (ndb_extra_logging)
+ sql_print_information("NDB Binlog: ndb tables writable");
+ close_cached_tables(NULL, NULL, TRUE, FALSE, FALSE);
+ pthread_mutex_unlock(&LOCK_open);
+ /* Signal injector thread that all is setup */
+ pthread_cond_signal(&injector_cond);
+ }
+ return 0;
+}
+
+/*
+ Defines and struct for schema table.
+ Should reflect table definition above.
+*/
+#define SCHEMA_DB_I 0u
+#define SCHEMA_NAME_I 1u
+#define SCHEMA_SLOCK_I 2u
+#define SCHEMA_QUERY_I 3u
+#define SCHEMA_NODE_ID_I 4u
+#define SCHEMA_EPOCH_I 5u
+#define SCHEMA_ID_I 6u
+#define SCHEMA_VERSION_I 7u
+#define SCHEMA_TYPE_I 8u
+#define SCHEMA_SIZE 9u
+#define SCHEMA_SLOCK_SIZE 32u
+
+struct Cluster_schema
+{
+ uchar db_length;
+ char db[64];
+ uchar name_length;
+ char name[64];
+ uchar slock_length;
+ uint32 slock[SCHEMA_SLOCK_SIZE/4];
+ unsigned short query_length;
+ char *query;
+ Uint64 epoch;
+ uint32 node_id;
+ uint32 id;
+ uint32 version;
+ uint32 type;
+ uint32 any_value;
+};
+
+/*
+ Transfer schema table data into corresponding struct
+*/
+static void ndbcluster_get_schema(NDB_SHARE *share,
+ Cluster_schema *s)
+{
+ TABLE *table= share->table;
+ Field **field;
+ /* unpack blob values */
+ uchar* blobs_buffer= 0;
+ uint blobs_buffer_size= 0;
+ my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->read_set);
+ {
+ ptrdiff_t ptrdiff= 0;
+ int ret= get_ndb_blobs_value(table, share->ndb_value[0],
+ blobs_buffer, blobs_buffer_size,
+ ptrdiff);
+ if (ret != 0)
+ {
+ my_free(blobs_buffer, MYF(MY_ALLOW_ZERO_PTR));
+ DBUG_PRINT("info", ("blob read error"));
+ DBUG_ASSERT(FALSE);
+ }
+ }
+ /* db varchar 1 length uchar */
+ field= table->field;
+ s->db_length= *(uint8*)(*field)->ptr;
+ DBUG_ASSERT(s->db_length <= (*field)->field_length);
+ DBUG_ASSERT((*field)->field_length + 1 == sizeof(s->db));
+ memcpy(s->db, (*field)->ptr + 1, s->db_length);
+ s->db[s->db_length]= 0;
+ /* name varchar 1 length uchar */
+ field++;
+ s->name_length= *(uint8*)(*field)->ptr;
+ DBUG_ASSERT(s->name_length <= (*field)->field_length);
+ DBUG_ASSERT((*field)->field_length + 1 == sizeof(s->name));
+ memcpy(s->name, (*field)->ptr + 1, s->name_length);
+ s->name[s->name_length]= 0;
+ /* slock fixed length */
+ field++;
+ s->slock_length= (*field)->field_length;
+ DBUG_ASSERT((*field)->field_length == sizeof(s->slock));
+ memcpy(s->slock, (*field)->ptr, s->slock_length);
+ /* query blob */
+ field++;
+ {
+ Field_blob *field_blob= (Field_blob*)(*field);
+ uint blob_len= field_blob->get_length((*field)->ptr);
+ uchar *blob_ptr= 0;
+ field_blob->get_ptr(&blob_ptr);
+ DBUG_ASSERT(blob_len == 0 || blob_ptr != 0);
+ s->query_length= blob_len;
+ s->query= sql_strmake((char*) blob_ptr, blob_len);
+ }
+ /* node_id */
+ field++;
+ s->node_id= ((Field_long *)*field)->val_int();
+ /* epoch */
+ field++;
+ s->epoch= ((Field_long *)*field)->val_int();
+ /* id */
+ field++;
+ s->id= ((Field_long *)*field)->val_int();
+ /* version */
+ field++;
+ s->version= ((Field_long *)*field)->val_int();
+ /* type */
+ field++;
+ s->type= ((Field_long *)*field)->val_int();
+ /* free blobs buffer */
+ my_free(blobs_buffer, MYF(MY_ALLOW_ZERO_PTR));
+ dbug_tmp_restore_column_map(table->read_set, old_map);
+}
+
+/*
+ helper function to pack a ndb varchar
+*/
+char *ndb_pack_varchar(const NDBCOL *col, char *buf,
+ const char *str, int sz)
+{
+ switch (col->getArrayType())
+ {
+ case NDBCOL::ArrayTypeFixed:
+ memcpy(buf, str, sz);
+ break;
+ case NDBCOL::ArrayTypeShortVar:
+ *(uchar*)buf= (uchar)sz;
+ memcpy(buf + 1, str, sz);
+ break;
+ case NDBCOL::ArrayTypeMediumVar:
+ int2store(buf, sz);
+ memcpy(buf + 2, str, sz);
+ break;
+ }
+ return buf;
+}
+
+/*
+ acknowledge handling of schema operation
+*/
+static int
+ndbcluster_update_slock(THD *thd,
+ const char *db,
+ const char *table_name)
+{
+ DBUG_ENTER("ndbcluster_update_slock");
+ if (!ndb_schema_share)
+ {
+ DBUG_RETURN(0);
+ }
+
+ const NdbError *ndb_error= 0;
+ uint32 node_id= g_ndb_cluster_connection->node_id();
+ Ndb *ndb= check_ndb_in_thd(thd);
+ char save_db[FN_HEADLEN];
+ strcpy(save_db, ndb->getDatabaseName());
+
+ char tmp_buf[FN_REFLEN];
+ NDBDICT *dict= ndb->getDictionary();
+ ndb->setDatabaseName(NDB_REP_DB);
+ Ndb_table_guard ndbtab_g(dict, NDB_SCHEMA_TABLE);
+ const NDBTAB *ndbtab= ndbtab_g.get_table();
+ NdbTransaction *trans= 0;
+ int retries= 100;
+ int retry_sleep= 10; /* 10 milliseconds, transaction */
+ const NDBCOL *col[SCHEMA_SIZE];
+ unsigned sz[SCHEMA_SIZE];
+
+ MY_BITMAP slock;
+ uint32 bitbuf[SCHEMA_SLOCK_SIZE/4];
+ bitmap_init(&slock, bitbuf, sizeof(bitbuf)*8, false);
+
+ if (ndbtab == 0)
+ {
+ abort();
+ DBUG_RETURN(0);
+ }
+
+ {
+ uint i;
+ for (i= 0; i < SCHEMA_SIZE; i++)
+ {
+ col[i]= ndbtab->getColumn(i);
+ if (i != SCHEMA_QUERY_I)
+ {
+ sz[i]= col[i]->getLength();
+ DBUG_ASSERT(sz[i] <= sizeof(tmp_buf));
+ }
+ }
+ }
+
+ while (1)
+ {
+ if ((trans= ndb->startTransaction()) == 0)
+ goto err;
+ {
+ NdbOperation *op= 0;
+ int r= 0;
+
+ /* read the bitmap exlusive */
+ r|= (op= trans->getNdbOperation(ndbtab)) == 0;
+ DBUG_ASSERT(r == 0);
+ r|= op->readTupleExclusive();
+ DBUG_ASSERT(r == 0);
+
+ /* db */
+ ndb_pack_varchar(col[SCHEMA_DB_I], tmp_buf, db, strlen(db));
+ r|= op->equal(SCHEMA_DB_I, tmp_buf);
+ DBUG_ASSERT(r == 0);
+ /* name */
+ ndb_pack_varchar(col[SCHEMA_NAME_I], tmp_buf, table_name,
+ strlen(table_name));
+ r|= op->equal(SCHEMA_NAME_I, tmp_buf);
+ DBUG_ASSERT(r == 0);
+ /* slock */
+ r|= op->getValue(SCHEMA_SLOCK_I, (char*)slock.bitmap) == 0;
+ DBUG_ASSERT(r == 0);
+ }
+ if (trans->execute(NdbTransaction::NoCommit))
+ goto err;
+ bitmap_clear_bit(&slock, node_id);
+ {
+ NdbOperation *op= 0;
+ int r= 0;
+
+ /* now update the tuple */
+ r|= (op= trans->getNdbOperation(ndbtab)) == 0;
+ DBUG_ASSERT(r == 0);
+ r|= op->updateTuple();
+ DBUG_ASSERT(r == 0);
+
+ /* db */
+ ndb_pack_varchar(col[SCHEMA_DB_I], tmp_buf, db, strlen(db));
+ r|= op->equal(SCHEMA_DB_I, tmp_buf);
+ DBUG_ASSERT(r == 0);
+ /* name */
+ ndb_pack_varchar(col[SCHEMA_NAME_I], tmp_buf, table_name,
+ strlen(table_name));
+ r|= op->equal(SCHEMA_NAME_I, tmp_buf);
+ DBUG_ASSERT(r == 0);
+ /* slock */
+ r|= op->setValue(SCHEMA_SLOCK_I, (char*)slock.bitmap);
+ DBUG_ASSERT(r == 0);
+ /* node_id */
+ r|= op->setValue(SCHEMA_NODE_ID_I, node_id);
+ DBUG_ASSERT(r == 0);
+ /* type */
+ r|= op->setValue(SCHEMA_TYPE_I, (uint32)SOT_CLEAR_SLOCK);
+ DBUG_ASSERT(r == 0);
+ }
+ if (trans->execute(NdbTransaction::Commit) == 0)
+ {
+ dict->forceGCPWait();
+ DBUG_PRINT("info", ("node %d cleared lock on '%s.%s'",
+ node_id, db, table_name));
+ break;
+ }
+ err:
+ const NdbError *this_error= trans ?
+ &trans->getNdbError() : &ndb->getNdbError();
+ if (this_error->status == NdbError::TemporaryError)
+ {
+ if (retries--)
+ {
+ if (trans)
+ ndb->closeTransaction(trans);
+ my_sleep(retry_sleep);
+ continue; // retry
+ }
+ }
+ ndb_error= this_error;
+ break;
+ }
+
+ if (ndb_error)
+ push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_ERROR,
+ ER_GET_ERRMSG, ER(ER_GET_ERRMSG),
+ ndb_error->code,
+ ndb_error->message,
+ "Could not release lock on '%s.%s'",
+ db, table_name);
+ if (trans)
+ ndb->closeTransaction(trans);
+ ndb->setDatabaseName(save_db);
+ DBUG_RETURN(0);
+}
+
+/*
+ log query in schema table
+*/
+static void ndb_report_waiting(const char *key,
+ int the_time,
+ const char *op,
+ const char *obj)
+{
+ ulonglong ndb_latest_epoch= 0;
+ const char *proc_info= "<no info>";
+ pthread_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);
+ sql_print_information("NDB %s:"
+ " waiting max %u sec for %s %s."
+ " epochs: (%u,%u,%u)"
+ " injector proc_info: %s"
+ ,key, the_time, op, obj
+ ,(uint)ndb_latest_handled_binlog_epoch
+ ,(uint)ndb_latest_received_binlog_epoch
+ ,(uint)ndb_latest_epoch
+ ,proc_info
+ );
+}
+
+int ndbcluster_log_schema_op(THD *thd, NDB_SHARE *share,
+ const char *query, int query_length,
+ const char *db, const char *table_name,
+ 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)
+{
+ DBUG_ENTER("ndbcluster_log_schema_op");
+ Thd_ndb *thd_ndb= get_thd_ndb(thd);
+ if (!thd_ndb)
+ {
+ if (!(thd_ndb= ha_ndbcluster::seize_thd_ndb()))
+ {
+ sql_print_error("Could not allocate Thd_ndb object");
+ DBUG_RETURN(1);
+ }
+ set_thd_ndb(thd, thd_ndb);
+ }
+
+ DBUG_PRINT("enter",
+ ("query: %s db: %s table_name: %s thd_ndb->options: %d",
+ query, db, table_name, thd_ndb->options));
+ if (!ndb_schema_share || thd_ndb->options & TNO_NO_LOG_SCHEMA_OP)
+ {
+ DBUG_RETURN(0);
+ }
+
+ char tmp_buf2[FN_REFLEN];
+ const char *type_str;
+ switch (type)
+ {
+ case SOT_DROP_TABLE:
+ /* drop database command, do not log at drop table */
+ if (thd->lex->sql_command == SQLCOM_DROP_DB)
+ DBUG_RETURN(0);
+ /* redo the drop table query as is may contain several tables */
+ query= tmp_buf2;
+ query_length= (uint) (strxmov(tmp_buf2, "drop table `",
+ table_name, "`", NullS) - tmp_buf2);
+ type_str= "drop table";
+ break;
+ case SOT_RENAME_TABLE:
+ /* redo the rename table query as is may contain several tables */
+ query= tmp_buf2;
+ query_length= (uint) (strxmov(tmp_buf2, "rename table `",
+ db, ".", table_name, "` to `",
+ new_db, ".", new_table_name, "`", NullS) - tmp_buf2);
+ type_str= "rename table";
+ break;
+ case SOT_CREATE_TABLE:
+ type_str= "create table";
+ break;
+ case SOT_ALTER_TABLE:
+ type_str= "alter table";
+ break;
+ case SOT_DROP_DB:
+ type_str= "drop db";
+ break;
+ case SOT_CREATE_DB:
+ type_str= "create db";
+ break;
+ case SOT_ALTER_DB:
+ type_str= "alter db";
+ break;
+ case SOT_TABLESPACE:
+ type_str= "tablespace";
+ break;
+ case SOT_LOGFILE_GROUP:
+ type_str= "logfile group";
+ break;
+ case SOT_TRUNCATE_TABLE:
+ type_str= "truncate table";
+ break;
+ default:
+ abort(); /* should not happen, programming error */
+ }
+
+ NDB_SCHEMA_OBJECT *ndb_schema_object;
+ {
+ char key[FN_REFLEN];
+ build_table_filename(key, sizeof(key), db, table_name, "", 0);
+ ndb_schema_object= ndb_get_schema_object(key, TRUE, FALSE);
+ }
+
+ const NdbError *ndb_error= 0;
+ uint32 node_id= g_ndb_cluster_connection->node_id();
+ Uint64 epoch= 0;
+ MY_BITMAP schema_subscribers;
+ uint32 bitbuf[sizeof(ndb_schema_object->slock)/4];
+ char bitbuf_e[sizeof(bitbuf)];
+ bzero(bitbuf_e, sizeof(bitbuf_e));
+ {
+ int i, updated= 0;
+ int no_storage_nodes= g_ndb_cluster_connection->no_db_nodes();
+ bitmap_init(&schema_subscribers, bitbuf, sizeof(bitbuf)*8, FALSE);
+ bitmap_set_all(&schema_subscribers);
+
+ /* begin protect ndb_schema_share */
+ pthread_mutex_lock(&ndb_schema_share_mutex);
+ if (ndb_schema_share == 0)
+ {
+ pthread_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);
+ for (i= 0; i < no_storage_nodes; i++)
+ {
+ MY_BITMAP *table_subscribers= &ndb_schema_share->subscriber_bitmap[i];
+ if (!bitmap_is_clear_all(table_subscribers))
+ {
+ bitmap_intersect(&schema_subscribers,
+ table_subscribers);
+ updated= 1;
+ }
+ }
+ (void) pthread_mutex_unlock(&ndb_schema_share->mutex);
+ pthread_mutex_unlock(&ndb_schema_share_mutex);
+ /* end protect ndb_schema_share */
+
+ if (updated)
+ {
+ bitmap_clear_bit(&schema_subscribers, node_id);
+ /*
+ if setting own acknowledge bit it is important that
+ no other mysqld's are registred, as subsequent code
+ will cause the original event to be hidden (by blob
+ merge event code)
+ */
+ if (bitmap_is_clear_all(&schema_subscribers))
+ bitmap_set_bit(&schema_subscribers, node_id);
+ }
+ else
+ bitmap_clear_all(&schema_subscribers);
+
+ if (ndb_schema_object)
+ {
+ (void) pthread_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);
+ }
+
+ DBUG_DUMP("schema_subscribers", (uchar*)schema_subscribers.bitmap,
+ no_bytes_in_map(&schema_subscribers));
+ DBUG_PRINT("info", ("bitmap_is_clear_all(&schema_subscribers): %d",
+ bitmap_is_clear_all(&schema_subscribers)));
+ }
+
+ Ndb *ndb= thd_ndb->ndb;
+ char save_db[FN_REFLEN];
+ strcpy(save_db, ndb->getDatabaseName());
+
+ char tmp_buf[FN_REFLEN];
+ NDBDICT *dict= ndb->getDictionary();
+ ndb->setDatabaseName(NDB_REP_DB);
+ Ndb_table_guard ndbtab_g(dict, NDB_SCHEMA_TABLE);
+ const NDBTAB *ndbtab= ndbtab_g.get_table();
+ NdbTransaction *trans= 0;
+ int retries= 100;
+ int retry_sleep= 10; /* 10 milliseconds, transaction */
+ const NDBCOL *col[SCHEMA_SIZE];
+ unsigned sz[SCHEMA_SIZE];
+
+ if (ndbtab == 0)
+ {
+ if (strcmp(NDB_REP_DB, db) != 0 ||
+ strcmp(NDB_SCHEMA_TABLE, table_name))
+ {
+ ndb_error= &dict->getNdbError();
+ }
+ goto end;
+ }
+
+ {
+ uint i;
+ for (i= 0; i < SCHEMA_SIZE; i++)
+ {
+ col[i]= ndbtab->getColumn(i);
+ if (i != SCHEMA_QUERY_I)
+ {
+ sz[i]= col[i]->getLength();
+ DBUG_ASSERT(sz[i] <= sizeof(tmp_buf));
+ }
+ }
+ }
+
+ while (1)
+ {
+ const char *log_db= db;
+ const char *log_tab= table_name;
+ const char *log_subscribers= (char*)schema_subscribers.bitmap;
+ uint32 log_type= (uint32)type;
+ if ((trans= ndb->startTransaction()) == 0)
+ goto err;
+ while (1)
+ {
+ NdbOperation *op= 0;
+ int r= 0;
+ r|= (op= trans->getNdbOperation(ndbtab)) == 0;
+ DBUG_ASSERT(r == 0);
+ r|= op->writeTuple();
+ DBUG_ASSERT(r == 0);
+
+ /* db */
+ ndb_pack_varchar(col[SCHEMA_DB_I], tmp_buf, log_db, strlen(log_db));
+ r|= op->equal(SCHEMA_DB_I, tmp_buf);
+ DBUG_ASSERT(r == 0);
+ /* name */
+ ndb_pack_varchar(col[SCHEMA_NAME_I], tmp_buf, log_tab,
+ strlen(log_tab));
+ r|= op->equal(SCHEMA_NAME_I, tmp_buf);
+ DBUG_ASSERT(r == 0);
+ /* slock */
+ DBUG_ASSERT(sz[SCHEMA_SLOCK_I] == sizeof(bitbuf));
+ r|= op->setValue(SCHEMA_SLOCK_I, log_subscribers);
+ DBUG_ASSERT(r == 0);
+ /* query */
+ {
+ NdbBlob *ndb_blob= op->getBlobHandle(SCHEMA_QUERY_I);
+ DBUG_ASSERT(ndb_blob != 0);
+ uint blob_len= query_length;
+ const char* blob_ptr= query;
+ r|= ndb_blob->setValue(blob_ptr, blob_len);
+ DBUG_ASSERT(r == 0);
+ }
+ /* node_id */
+ r|= op->setValue(SCHEMA_NODE_ID_I, node_id);
+ DBUG_ASSERT(r == 0);
+ /* epoch */
+ r|= op->setValue(SCHEMA_EPOCH_I, epoch);
+ DBUG_ASSERT(r == 0);
+ /* id */
+ r|= op->setValue(SCHEMA_ID_I, ndb_table_id);
+ DBUG_ASSERT(r == 0);
+ /* version */
+ r|= op->setValue(SCHEMA_VERSION_I, ndb_table_version);
+ DBUG_ASSERT(r == 0);
+ /* type */
+ r|= op->setValue(SCHEMA_TYPE_I, log_type);
+ DBUG_ASSERT(r == 0);
+ /* any value */
+ if (!(thd->options & OPTION_BIN_LOG))
+ r|= op->setAnyValue(NDB_ANYVALUE_FOR_NOLOGGING);
+ else
+ r|= op->setAnyValue(thd->server_id);
+ DBUG_ASSERT(r == 0);
+ if (log_db != new_db && new_db && new_table_name)
+ {
+ log_db= new_db;
+ log_tab= new_table_name;
+ log_subscribers= bitbuf_e; // no ack expected on this
+ log_type= (uint32)SOT_RENAME_TABLE_NEW;
+ continue;
+ }
+ break;
+ }
+ if (trans->execute(NdbTransaction::Commit) == 0)
+ {
+ DBUG_PRINT("info", ("logged: %s", query));
+ break;
+ }
+err:
+ const NdbError *this_error= trans ?
+ &trans->getNdbError() : &ndb->getNdbError();
+ if (this_error->status == NdbError::TemporaryError)
+ {
+ if (retries--)
+ {
+ if (trans)
+ ndb->closeTransaction(trans);
+ my_sleep(retry_sleep);
+ continue; // retry
+ }
+ }
+ ndb_error= this_error;
+ break;
+ }
+end:
+ if (ndb_error)
+ push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_ERROR,
+ ER_GET_ERRMSG, ER(ER_GET_ERRMSG),
+ ndb_error->code,
+ ndb_error->message,
+ "Could not log query '%s' on other mysqld's");
+
+ if (trans)
+ ndb->closeTransaction(trans);
+ ndb->setDatabaseName(save_db);
+
+ /*
+ Wait for other mysqld's to acknowledge the table operation
+ */
+ if (ndb_error == 0 &&
+ !bitmap_is_clear_all(&schema_subscribers))
+ {
+ /*
+ if own nodeid is set we are a single mysqld registred
+ as an optimization we update the slock directly
+ */
+ if (bitmap_is_set(&schema_subscribers, node_id))
+ ndbcluster_update_slock(thd, db, table_name);
+ else
+ dict->forceGCPWait();
+
+ int max_timeout= opt_ndb_sync_timeout;
+ (void) pthread_mutex_lock(&ndb_schema_object->mutex);
+ if (have_lock_open)
+ {
+ safe_mutex_assert_owner(&LOCK_open);
+ (void) pthread_mutex_unlock(&LOCK_open);
+ }
+ 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);
+ if (thd->killed)
+ break;
+
+ /* begin protect ndb_schema_share */
+ pthread_mutex_lock(&ndb_schema_share_mutex);
+ if (ndb_schema_share == 0)
+ {
+ pthread_mutex_unlock(&ndb_schema_share_mutex);
+ break;
+ }
+ (void) pthread_mutex_lock(&ndb_schema_share->mutex);
+ for (i= 0; i < no_storage_nodes; i++)
+ {
+ /* remove any unsubscribed from schema_subscribers */
+ MY_BITMAP *tmp= &ndb_schema_share->subscriber_bitmap[i];
+ 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);
+ /* end protect ndb_schema_share */
+
+ /* remove any unsubscribed from ndb_schema_object->slock */
+ bitmap_intersect(&ndb_schema_object->slock_bitmap, &schema_subscribers);
+
+ DBUG_DUMP("ndb_schema_object->slock_bitmap.bitmap",
+ (uchar*)ndb_schema_object->slock_bitmap.bitmap,
+ no_bytes_in_map(&ndb_schema_object->slock_bitmap));
+
+ if (bitmap_is_clear_all(&ndb_schema_object->slock_bitmap))
+ break;
+
+ if (ret)
+ {
+ max_timeout--;
+ if (max_timeout == 0)
+ {
+ sql_print_error("NDB %s: distributing %s timed out. Ignoring...",
+ type_str, ndb_schema_object->key);
+ break;
+ }
+ if (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);
+ }
+
+ if (ndb_schema_object)
+ ndb_free_schema_object(&ndb_schema_object, FALSE);
+
+ DBUG_RETURN(0);
+}
+
+/*
+ Handle _non_ data events from the storage nodes
+*/
+int
+ndb_handle_schema_change(THD *thd, Ndb *ndb, NdbEventOperation *pOp,
+ NDB_SHARE *share)
+{
+ DBUG_ENTER("ndb_handle_schema_change");
+ TABLE* table= share->table;
+ TABLE_SHARE *table_share= share->table_share;
+ const char *dbname= table_share->db.str;
+ const char *tabname= table_share->table_name.str;
+ bool do_close_cached_tables= FALSE;
+ bool is_online_alter_table= FALSE;
+ bool is_rename_table= FALSE;
+ bool is_remote_change=
+ (uint) pOp->getReqNodeId() != g_ndb_cluster_connection->node_id();
+
+ if (pOp->getEventType() == NDBEVENT::TE_ALTER)
+ {
+ if (pOp->tableFrmChanged())
+ {
+ DBUG_PRINT("info", ("NDBEVENT::TE_ALTER: table frm changed"));
+ is_online_alter_table= TRUE;
+ }
+ else
+ {
+ DBUG_PRINT("info", ("NDBEVENT::TE_ALTER: name changed"));
+ DBUG_ASSERT(pOp->tableNameChanged());
+ is_rename_table= TRUE;
+ }
+ }
+
+ {
+ ndb->setDatabaseName(dbname);
+ Ndb_table_guard ndbtab_g(ndb->getDictionary(), tabname);
+ const NDBTAB *ev_tab= pOp->getTable();
+ const NDBTAB *cache_tab= ndbtab_g.get_table();
+ if (cache_tab &&
+ cache_tab->getObjectId() == ev_tab->getObjectId() &&
+ cache_tab->getObjectVersion() <= ev_tab->getObjectVersion())
+ ndbtab_g.invalidate();
+ }
+
+ /*
+ Refresh local frm file and dictionary cache if
+ remote on-line alter table
+ */
+ if (is_remote_change && is_online_alter_table)
+ {
+ const char *tabname= table_share->table_name.str;
+ char key[FN_REFLEN];
+ uchar *data= 0, *pack_data= 0;
+ size_t length, pack_length;
+ int error;
+ NDBDICT *dict= ndb->getDictionary();
+ const NDBTAB *altered_table= pOp->getTable();
+
+ DBUG_PRINT("info", ("Detected frm change of table %s.%s",
+ dbname, tabname));
+ build_table_filename(key, FN_LEN-1, dbname, tabname, NullS, 0);
+ /*
+ If the there is no local table shadowing the altered table and
+ it has an frm that is different than the one on disk then
+ overwrite it with the new table definition
+ */
+ if (!ndbcluster_check_if_local_table(dbname, tabname) &&
+ readfrm(key, &data, &length) == 0 &&
+ packfrm(data, length, &pack_data, &pack_length) == 0 &&
+ cmp_frm(altered_table, pack_data, pack_length))
+ {
+ 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));
+ data= NULL;
+ if ((error= unpackfrm(&data, &length,
+ (const uchar*) altered_table->getFrmData())) ||
+ (error= writefrm(key, data, length)))
+ {
+ sql_print_information("NDB: Failed write frm for %s.%s, error %d",
+ dbname, tabname, error);
+ }
+
+ // copy names as memory will be freed
+ NdbAutoPtr<char> a1((char *)(dbname= strdup(dbname)));
+ NdbAutoPtr<char> a2((char *)(tabname= strdup(tabname)));
+ ndbcluster_binlog_close_table(thd, share);
+
+ TABLE_LIST table_list;
+ 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);
+
+ if ((error= ndbcluster_binlog_open_table(thd, share,
+ table_share, table, 1)))
+ sql_print_information("NDB: Failed to re-open table %s.%s",
+ dbname, tabname);
+
+ table= share->table;
+ 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));
+ }
+
+ // 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);
+ DBUG_RETURN(0);
+ }
+
+ (void) pthread_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)
+ sql_print_information("NDB Binlog: rename table %s%s/%s -> %s.",
+ share_prefix, share->table->s->db.str,
+ share->table->s->table_name.str,
+ share->key);
+ {
+ ndb->setDatabaseName(share->table->s->db.str);
+ Ndb_table_guard ndbtab_g(ndb->getDictionary(),
+ share->table->s->table_name.str);
+ const NDBTAB *ev_tab= pOp->getTable();
+ const NDBTAB *cache_tab= ndbtab_g.get_table();
+ if (cache_tab &&
+ cache_tab->getObjectId() == ev_tab->getObjectId() &&
+ cache_tab->getObjectVersion() <= ev_tab->getObjectVersion())
+ ndbtab_g.invalidate();
+ }
+ /* do the rename of the table in the share */
+ share->table->s->db.str= share->db;
+ share->table->s->db.length= strlen(share->db);
+ share->table->s->table_name.str= share->table_name;
+ share->table->s->table_name.length= strlen(share->table_name);
+ }
+ DBUG_ASSERT(share->op == pOp || share->op_old == pOp);
+ if (share->op_old == pOp)
+ share->op_old= 0;
+ else
+ share->op= 0;
+ // 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);
+
+ pthread_mutex_lock(&ndbcluster_mutex);
+ /* ndb_share reference binlog free */
+ DBUG_PRINT("NDB_SHARE", ("%s binlog free use_count: %u",
+ share->key, share->use_count));
+ free_share(&share, TRUE);
+ if (is_remote_change && share && share->state != NSS_DROPPED)
+ {
+ DBUG_PRINT("info", ("remote change"));
+ share->state= NSS_DROPPED;
+ if (share->use_count != 1)
+ {
+ /* open handler holding reference */
+ /* wait with freeing create ndb_share to below */
+ do_close_cached_tables= TRUE;
+ }
+ else
+ {
+ /* ndb_share reference create free */
+ DBUG_PRINT("NDB_SHARE", ("%s create free use_count: %u",
+ share->key, share->use_count));
+ free_share(&share, TRUE);
+ share= 0;
+ }
+ }
+ else
+ share= 0;
+ pthread_mutex_unlock(&ndbcluster_mutex);
+
+ pOp->setCustomData(0);
+
+ pthread_mutex_lock(&injector_mutex);
+ ndb->dropEventOperation(pOp);
+ pOp= 0;
+ pthread_mutex_unlock(&injector_mutex);
+
+ if (do_close_cached_tables)
+ {
+ TABLE_LIST table_list;
+ 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);
+ /* ndb_share reference create free */
+ DBUG_PRINT("NDB_SHARE", ("%s create free use_count: %u",
+ share->key, share->use_count));
+ free_share(&share);
+ }
+ DBUG_RETURN(0);
+}
+
+static void ndb_binlog_query(THD *thd, Cluster_schema *schema)
+{
+ if (schema->any_value & NDB_ANYVALUE_RESERVED)
+ {
+ if (schema->any_value != NDB_ANYVALUE_FOR_NOLOGGING)
+ sql_print_warning("NDB: unknown value for binlog signalling 0x%X, "
+ "query not logged",
+ schema->any_value);
+ return;
+ }
+ uint32 thd_server_id_save= thd->server_id;
+ DBUG_ASSERT(sizeof(thd_server_id_save) == sizeof(thd->server_id));
+ char *thd_db_save= thd->db;
+ if (schema->any_value == 0)
+ thd->server_id= ::server_id;
+ else
+ thd->server_id= schema->any_value;
+ thd->db= schema->db;
+ thd->binlog_query(THD::STMT_QUERY_TYPE, schema->query,
+ schema->query_length, FALSE,
+ schema->name[0] == 0 || thd->db[0] == 0);
+ thd->server_id= thd_server_id_save;
+ thd->db= thd_db_save;
+}
+
+static int
+ndb_binlog_thread_handle_schema_event(THD *thd, Ndb *ndb,
+ NdbEventOperation *pOp,
+ List<Cluster_schema>
+ *post_epoch_log_list,
+ List<Cluster_schema>
+ *post_epoch_unlock_list,
+ MEM_ROOT *mem_root)
+{
+ DBUG_ENTER("ndb_binlog_thread_handle_schema_event");
+ NDB_SHARE *tmp_share= (NDB_SHARE *)pOp->getCustomData();
+ if (tmp_share && ndb_schema_share == tmp_share)
+ {
+ NDBEVENT::TableEvent ev_type= pOp->getEventType();
+ DBUG_PRINT("enter", ("%s.%s ev_type: %d",
+ tmp_share->db, tmp_share->table_name, ev_type));
+ if (ev_type == NDBEVENT::TE_UPDATE ||
+ ev_type == NDBEVENT::TE_INSERT)
+ {
+ Cluster_schema *schema= (Cluster_schema *)
+ sql_alloc(sizeof(Cluster_schema));
+ MY_BITMAP slock;
+ bitmap_init(&slock, schema->slock, 8*SCHEMA_SLOCK_SIZE, FALSE);
+ uint node_id= g_ndb_cluster_connection->node_id();
+ {
+ ndbcluster_get_schema(tmp_share, schema);
+ schema->any_value= pOp->getAnyValue();
+ }
+ enum SCHEMA_OP_TYPE schema_type= (enum SCHEMA_OP_TYPE)schema->type;
+ DBUG_PRINT("info",
+ ("%s.%s: log query_length: %d query: '%s' type: %d",
+ schema->db, schema->name,
+ schema->query_length, schema->query,
+ schema_type));
+ if (schema_type == SOT_CLEAR_SLOCK)
+ {
+ /*
+ handle slock after epoch is completed to ensure that
+ schema events get inserted in the binlog after any data
+ events
+ */
+ post_epoch_log_list->push_back(schema, mem_root);
+ DBUG_RETURN(0);
+ }
+ if (schema->node_id != node_id)
+ {
+ int log_query= 0, post_epoch_unlock= 0;
+ switch (schema_type)
+ {
+ case SOT_DROP_TABLE:
+ // fall through
+ case SOT_RENAME_TABLE:
+ // fall through
+ case SOT_RENAME_TABLE_NEW:
+ // fall through
+ case SOT_ALTER_TABLE:
+ post_epoch_log_list->push_back(schema, mem_root);
+ /* acknowledge this query _after_ epoch completion */
+ post_epoch_unlock= 1;
+ break;
+ case SOT_TRUNCATE_TABLE:
+ {
+ char key[FN_REFLEN];
+ build_table_filename(key, sizeof(key),
+ schema->db, schema->name, "", 0);
+ /* ndb_share reference temporary, free below */
+ NDB_SHARE *share= get_share(key, 0, FALSE, FALSE);
+ if (share)
+ {
+ DBUG_PRINT("NDB_SHARE", ("%s temporary use_count: %u",
+ share->key, share->use_count));
+ }
+ // invalidation already handled by binlog thread
+ if (!share || !share->op)
+ {
+ {
+ injector_ndb->setDatabaseName(schema->db);
+ Ndb_table_guard ndbtab_g(injector_ndb->getDictionary(),
+ schema->name);
+ ndbtab_g.invalidate();
+ }
+ TABLE_LIST table_list;
+ 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);
+ }
+ /* ndb_share reference temporary free */
+ if (share)
+ {
+ DBUG_PRINT("NDB_SHARE", ("%s temporary free use_count: %u",
+ share->key, share->use_count));
+ free_share(&share);
+ }
+ }
+ // 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'",
+ schema->db, schema->name));
+ sql_print_error("NDB Binlog: Skipping locally defined table '%s.%s' from "
+ "binlog schema event '%s' from node %d. ",
+ schema->db, schema->name, schema->query,
+ schema->node_id);
+ }
+ 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);
+ }
+ pthread_mutex_unlock(&LOCK_open);
+ log_query= 1;
+ break;
+ case SOT_DROP_DB:
+ /* Drop the database locally if it only contains ndb tables */
+ if (! ndbcluster_check_if_local_tables_in_db(thd, schema->db))
+ {
+ const int no_print_error[1]= {0};
+ run_query(thd, schema->query,
+ schema->query + schema->query_length,
+ no_print_error, /* print error */
+ TRUE); /* don't binlog the query */
+ /* binlog dropping database after any table operations */
+ post_epoch_log_list->push_back(schema, mem_root);
+ /* acknowledge this query _after_ epoch completion */
+ post_epoch_unlock= 1;
+ }
+ else
+ {
+ /* Database contained local tables, leave it */
+ sql_print_error("NDB Binlog: Skipping drop database '%s' since it contained local tables "
+ "binlog schema event '%s' from node %d. ",
+ schema->db, schema->query,
+ schema->node_id);
+ log_query= 1;
+ }
+ break;
+ case SOT_CREATE_DB:
+ /* fall through */
+ case SOT_ALTER_DB:
+ {
+ const int no_print_error[1]= {0};
+ run_query(thd, schema->query,
+ schema->query + schema->query_length,
+ no_print_error, /* print error */
+ TRUE); /* don't binlog the query */
+ log_query= 1;
+ break;
+ }
+ case SOT_TABLESPACE:
+ case SOT_LOGFILE_GROUP:
+ log_query= 1;
+ break;
+ case SOT_CLEAR_SLOCK:
+ abort();
+ }
+ if (log_query && ndb_binlog_running)
+ ndb_binlog_query(thd, schema);
+ /* signal that schema operation has been handled */
+ DBUG_DUMP("slock", (uchar*) schema->slock, schema->slock_length);
+ if (bitmap_is_set(&slock, node_id))
+ {
+ if (post_epoch_unlock)
+ post_epoch_unlock_list->push_back(schema, mem_root);
+ else
+ ndbcluster_update_slock(thd, schema->db, schema->name);
+ }
+ }
+ DBUG_RETURN(0);
+ }
+ /*
+ the normal case of UPDATE/INSERT has already been handled
+ */
+ switch (ev_type)
+ {
+ case NDBEVENT::TE_DELETE:
+ // skip
+ break;
+ case NDBEVENT::TE_CLUSTER_FAILURE:
+ if (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 &&
+ 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);
+ /* ndb_share reference binlog extra free */
+ DBUG_PRINT("NDB_SHARE", ("%s binlog extra free use_count: %u",
+ ndb_schema_share->key,
+ ndb_schema_share->use_count));
+ free_share(&ndb_schema_share);
+ ndb_schema_share= 0;
+ ndb_binlog_tables_inited= 0;
+ pthread_mutex_unlock(&ndb_schema_share_mutex);
+ /* end protect ndb_schema_share */
+
+ close_cached_tables(NULL, NULL, FALSE, FALSE, FALSE);
+ // fall through
+ case NDBEVENT::TE_ALTER:
+ ndb_handle_schema_change(thd, ndb, pOp, tmp_share);
+ break;
+ case NDBEVENT::TE_NODE_FAILURE:
+ {
+ uint8 node_id= g_node_id_map[pOp->getNdbdNodeId()];
+ DBUG_ASSERT(node_id != 0xFF);
+ (void) pthread_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)
+ {
+ sql_print_information("NDB Binlog: Node: %d, down,"
+ " Subscriber bitmask %x%x",
+ pOp->getNdbdNodeId(),
+ 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);
+ break;
+ }
+ case NDBEVENT::TE_SUBSCRIBE:
+ {
+ 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);
+ 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)
+ {
+ sql_print_information("NDB Binlog: Node: %d, subscribe from node %d,"
+ " Subscriber bitmask %x%x",
+ pOp->getNdbdNodeId(),
+ req_id,
+ 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);
+ break;
+ }
+ case NDBEVENT::TE_UNSUBSCRIBE:
+ {
+ 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);
+ 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)
+ {
+ sql_print_information("NDB Binlog: Node: %d, unsubscribe from node %d,"
+ " Subscriber bitmask %x%x",
+ pOp->getNdbdNodeId(),
+ req_id,
+ 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);
+ break;
+ }
+ default:
+ sql_print_error("NDB Binlog: unknown non data event %d for %s. "
+ "Ignoring...", (unsigned) ev_type, tmp_share->key);
+ }
+ }
+ DBUG_RETURN(0);
+}
+
+/*
+ process any operations that should be done after
+ the epoch is complete
+*/
+static void
+ndb_binlog_thread_handle_schema_event_post_epoch(THD *thd,
+ List<Cluster_schema>
+ *post_epoch_log_list,
+ List<Cluster_schema>
+ *post_epoch_unlock_list)
+{
+ if (post_epoch_log_list->elements == 0)
+ return;
+ DBUG_ENTER("ndb_binlog_thread_handle_schema_event_post_epoch");
+ Cluster_schema *schema;
+ while ((schema= post_epoch_log_list->pop()))
+ {
+ DBUG_PRINT("info",
+ ("%s.%s: log query_length: %d query: '%s' type: %d",
+ schema->db, schema->name,
+ schema->query_length, schema->query,
+ schema->type));
+ int log_query= 0;
+ {
+ enum SCHEMA_OP_TYPE schema_type= (enum SCHEMA_OP_TYPE)schema->type;
+ char key[FN_REFLEN];
+ build_table_filename(key, sizeof(key), schema->db, schema->name, "", 0);
+ if (schema_type == SOT_CLEAR_SLOCK)
+ {
+ pthread_mutex_lock(&ndbcluster_mutex);
+ NDB_SCHEMA_OBJECT *ndb_schema_object=
+ (NDB_SCHEMA_OBJECT*) hash_search(&ndb_schema_objects,
+ (uchar*) key, strlen(key));
+ if (ndb_schema_object)
+ {
+ pthread_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);
+ }
+ pthread_mutex_unlock(&ndbcluster_mutex);
+ continue;
+ }
+ /* ndb_share reference temporary, free below */
+ NDB_SHARE *share= get_share(key, 0, FALSE, FALSE);
+ if (share)
+ {
+ DBUG_PRINT("NDB_SHARE", ("%s temporary use_count: %u",
+ share->key, share->use_count));
+ }
+ switch (schema_type)
+ {
+ case SOT_DROP_DB:
+ log_query= 1;
+ break;
+ case SOT_DROP_TABLE:
+ log_query= 1;
+ // invalidation already handled by binlog thread
+ if (share && share->op)
+ {
+ break;
+ }
+ // fall through
+ case SOT_RENAME_TABLE:
+ // fall through
+ case SOT_ALTER_TABLE:
+ // invalidation already handled by binlog thread
+ if (!share || !share->op)
+ {
+ {
+ injector_ndb->setDatabaseName(schema->db);
+ Ndb_table_guard ndbtab_g(injector_ndb->getDictionary(),
+ schema->name);
+ ndbtab_g.invalidate();
+ }
+ TABLE_LIST table_list;
+ 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);
+ }
+ if (schema_type != SOT_ALTER_TABLE)
+ break;
+ // fall through
+ case SOT_RENAME_TABLE_NEW:
+ log_query= 1;
+ if (ndb_binlog_running && (!share || !share->op))
+ {
+ /*
+ we need to free any share here as command below
+ may need to call handle_trailing_share
+ */
+ if (share)
+ {
+ /* ndb_share reference temporary free */
+ DBUG_PRINT("NDB_SHARE", ("%s temporary free use_count: %u",
+ share->key, share->use_count));
+ 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'",
+ schema->db, schema->name));
+ sql_print_error("NDB Binlog: Skipping locally defined table '%s.%s' from "
+ "binlog schema event '%s' from node %d. ",
+ schema->db, schema->name, schema->query,
+ schema->node_id);
+ }
+ 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);
+ }
+ pthread_mutex_unlock(&LOCK_open);
+ }
+ break;
+ default:
+ DBUG_ASSERT(FALSE);
+ }
+ if (share)
+ {
+ /* ndb_share reference temporary free */
+ DBUG_PRINT("NDB_SHARE", ("%s temporary free use_count: %u",
+ share->key, share->use_count));
+ free_share(&share);
+ share= 0;
+ }
+ }
+ if (ndb_binlog_running && log_query)
+ ndb_binlog_query(thd, schema);
+ }
+ while ((schema= post_epoch_unlock_list->pop()))
+ {
+ ndbcluster_update_slock(thd, schema->db, schema->name);
+ }
+ DBUG_VOID_RETURN;
+}
+
+/*
+ Timer class for doing performance measurements
+*/
+
+/*********************************************************************
+ Internal helper functions for handeling of the cluster replication tables
+ - ndb_binlog_index
+ - ndb_apply_status
+*********************************************************************/
+
+/*
+ struct to hold the data to be inserted into the
+ ndb_binlog_index table
+*/
+struct ndb_binlog_index_row {
+ ulonglong gci;
+ const char *master_log_file;
+ ulonglong master_log_pos;
+ ulonglong n_inserts;
+ ulonglong n_updates;
+ ulonglong n_deletes;
+ ulonglong n_schemaops;
+};
+
+/*
+ Open the ndb_binlog_index table
+*/
+static int open_ndb_binlog_index(THD *thd, TABLE_LIST *tables,
+ TABLE **ndb_binlog_index)
+{
+ static char repdb[]= NDB_REP_DB;
+ static char reptable[]= NDB_REP_TABLE;
+ const char *save_proc_info= thd->proc_info;
+
+ bzero((char*) tables, sizeof(*tables));
+ tables->db= repdb;
+ tables->alias= tables->table_name= reptable;
+ tables->lock_type= 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 (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->proc_info= save_proc_info;
+ return -1;
+ }
+ *ndb_binlog_index= tables->table;
+ thd->proc_info= save_proc_info;
+ (*ndb_binlog_index)->use_all_columns();
+ return 0;
+}
+
+
+/*
+ Insert one row in the ndb_binlog_index
+*/
+
+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);
+
+ for ( ; ; ) /* loop for need_reopen */
+ {
+ 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;
+ }
+
+ /*
+ Intialize ndb_binlog_index->record[0]
+ */
+ empty_record(ndb_binlog_index);
+
+ ndb_binlog_index->field[0]->store(row.master_log_pos);
+ ndb_binlog_index->field[1]->store(row.master_log_file,
+ strlen(row.master_log_file),
+ &my_charset_bin);
+ ndb_binlog_index->field[2]->store(row.gci);
+ ndb_binlog_index->field[3]->store(row.n_inserts);
+ ndb_binlog_index->field[4]->store(row.n_updates);
+ ndb_binlog_index->field[5]->store(row.n_deletes);
+ ndb_binlog_index->field[6]->store(row.n_schemaops);
+
+ if ((error= ndb_binlog_index->file->ha_write_row(ndb_binlog_index->record[0])))
+ {
+ sql_print_error("NDB Binlog: Writing row to ndb_binlog_index: %d", error);
+ error= -1;
+ 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:
+ close_thread_tables(thd);
+ ndb_binlog_index= 0;
+ thd->options= saved_options;
+ return error;
+}
+
+/*********************************************************************
+ Functions for start, stop, wait for ndbcluster binlog thread
+*********************************************************************/
+
+enum Binlog_thread_state
+{
+ BCCC_running= 0,
+ BCCC_exit= 1,
+ BCCC_restart= 2
+};
+
+static enum Binlog_thread_state do_ndbcluster_binlog_close_connection= BCCC_restart;
+
+int ndbcluster_binlog_start()
+{
+ DBUG_ENTER("ndbcluster_binlog_start");
+
+ if (::server_id == 0)
+ {
+ sql_print_warning("NDB: server id set to zero will cause any other mysqld "
+ "with bin log to log with wrong server id");
+ }
+ else if (::server_id & 0x1 << 31)
+ {
+ sql_print_error("NDB: server id's with high bit set is reserved for internal "
+ "purposes");
+ 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);
+
+ /* Create injector thread */
+ if (pthread_create(&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);
+ DBUG_RETURN(-1);
+ }
+
+ ndbcluster_binlog_inited= 1;
+
+ /* Wait for the injector thread to start */
+ pthread_mutex_lock(&injector_mutex);
+ while (!ndb_binlog_thread_running)
+ pthread_cond_wait(&injector_cond, &injector_mutex);
+ pthread_mutex_unlock(&injector_mutex);
+
+ if (ndb_binlog_thread_running < 0)
+ DBUG_RETURN(-1);
+
+ DBUG_RETURN(0);
+}
+
+
+/**************************************************************
+ Internal helper functions for creating/dropping ndb events
+ used by the client sql threads
+**************************************************************/
+void
+ndb_rep_event_name(String *event_name,const char *db, const char *tbl)
+{
+ event_name->set_ascii("REPL$", 5);
+ event_name->append(db);
+ if (tbl)
+ {
+ event_name->append('/');
+ event_name->append(tbl);
+ }
+}
+
+bool
+ndbcluster_check_if_local_table(const char *dbname, const char *tabname)
+{
+ char key[FN_REFLEN];
+ char ndb_file[FN_REFLEN];
+
+ DBUG_ENTER("ndbcluster_check_if_local_table");
+ build_table_filename(key, FN_LEN-1, dbname, tabname, reg_ext, 0);
+ build_table_filename(ndb_file, FN_LEN-1, dbname, tabname, ha_ndb_ext, 0);
+ /* Check that any defined table is an ndb table */
+ DBUG_PRINT("info", ("Looking for file %s and %s", key, ndb_file));
+ if ((! my_access(key, F_OK)) && my_access(ndb_file, F_OK))
+ {
+ DBUG_PRINT("info", ("table file %s not on disk, local table", ndb_file));
+
+
+ DBUG_RETURN(true);
+ }
+
+ DBUG_RETURN(false);
+}
+
+bool
+ndbcluster_check_if_local_tables_in_db(THD *thd, const char *dbname)
+{
+ DBUG_ENTER("ndbcluster_check_if_local_tables_in_db");
+ DBUG_PRINT("info", ("Looking for files in directory %s", dbname));
+ LEX_STRING *tabname;
+ List<LEX_STRING> files;
+ char path[FN_REFLEN];
+
+ build_table_filename(path, sizeof(path), dbname, "", "", 0);
+ if (find_files(thd, &files, dbname, path, NullS, 0) != FIND_FILES_OK)
+ {
+ DBUG_PRINT("info", ("Failed to find files"));
+ DBUG_RETURN(true);
+ }
+ DBUG_PRINT("info",("found: %d files", files.elements));
+ while ((tabname= files.pop()))
+ {
+ DBUG_PRINT("info", ("Found table %s", tabname->str));
+ if (ndbcluster_check_if_local_table(dbname, tabname->str))
+ DBUG_RETURN(true);
+ }
+
+ DBUG_RETURN(false);
+}
+
+/*
+ Common function for setting up everything for logging a table at
+ create/discover.
+*/
+int ndbcluster_create_binlog_setup(Ndb *ndb, const char *key,
+ uint key_len,
+ const char *db,
+ const char *table_name,
+ my_bool share_may_exist)
+{
+ int do_event_op= ndb_binlog_running;
+ DBUG_ENTER("ndbcluster_create_binlog_setup");
+ DBUG_PRINT("enter",("key: %s key_len: %d %s.%s share_may_exist: %d",
+ key, key_len, db, table_name, share_may_exist));
+ DBUG_ASSERT(! IS_NDB_BLOB_PREFIX(table_name));
+ DBUG_ASSERT(strlen(key) == key_len);
+
+ pthread_mutex_lock(&ndbcluster_mutex);
+
+ /* Handle any trailing share */
+ NDB_SHARE *share= (NDB_SHARE*) hash_search(&ndbcluster_open_tables,
+ (uchar*) key, key_len);
+
+ if (share && share_may_exist)
+ {
+ if (share->flags & NSF_NO_BINLOG ||
+ share->op != 0 ||
+ share->op_old != 0)
+ {
+ pthread_mutex_unlock(&ndbcluster_mutex);
+ DBUG_RETURN(0); // replication already setup, or should not
+ }
+ }
+
+ if (share)
+ {
+ if (share->op || share->op_old)
+ {
+ my_errno= HA_ERR_TABLE_EXIST;
+ pthread_mutex_unlock(&ndbcluster_mutex);
+ DBUG_RETURN(1);
+ }
+ if (!share_may_exist || share->connect_count !=
+ g_ndb_cluster_connection->get_connect_count())
+ {
+ handle_trailing_share(share);
+ share= NULL;
+ }
+ }
+
+ /* Create share which is needed to hold replication information */
+ if (share)
+ {
+ /* ndb_share reference create */
+ ++share->use_count;
+ DBUG_PRINT("NDB_SHARE", ("%s create use_count: %u",
+ share->key, share->use_count));
+ }
+ /* ndb_share reference create */
+ else if (!(share= get_share(key, 0, TRUE, TRUE)))
+ {
+ sql_print_error("NDB Binlog: "
+ "allocating table share for %s failed", key);
+ }
+ else
+ {
+ DBUG_PRINT("NDB_SHARE", ("%s create use_count: %u",
+ share->key, share->use_count));
+ }
+
+ if (!ndb_schema_share &&
+ strcmp(share->db, NDB_REP_DB) == 0 &&
+ strcmp(share->table_name, NDB_SCHEMA_TABLE) == 0)
+ do_event_op= 1;
+ else if (!ndb_apply_status_share &&
+ strcmp(share->db, NDB_REP_DB) == 0 &&
+ strcmp(share->table_name, NDB_APPLY_TABLE) == 0)
+ do_event_op= 1;
+
+ if (!do_event_op)
+ {
+ share->flags|= NSF_NO_BINLOG;
+ pthread_mutex_unlock(&ndbcluster_mutex);
+ DBUG_RETURN(0);
+ }
+ pthread_mutex_unlock(&ndbcluster_mutex);
+
+ while (share && !IS_TMP_PREFIX(table_name))
+ {
+ /*
+ ToDo make sanity check of share so that the table is actually the same
+ I.e. we need to do open file from frm in this case
+ Currently awaiting this to be fixed in the 4.1 tree in the general
+ case
+ */
+
+ /* Create the event in NDB */
+ ndb->setDatabaseName(db);
+
+ NDBDICT *dict= ndb->getDictionary();
+ Ndb_table_guard ndbtab_g(dict, table_name);
+ const NDBTAB *ndbtab= ndbtab_g.get_table();
+ if (ndbtab == 0)
+ {
+ if (ndb_extra_logging)
+ sql_print_information("NDB Binlog: Failed to get table %s from ndb: "
+ "%s, %d", key, dict->getNdbError().message,
+ dict->getNdbError().code);
+ break; // error
+ }
+ String event_name(INJECTOR_EVENT_LEN);
+ ndb_rep_event_name(&event_name, db, table_name);
+ /*
+ event should have been created by someone else,
+ but let's make sure, and create if it doesn't exist
+ */
+ const NDBEVENT *ev= dict->getEvent(event_name.c_ptr());
+ if (!ev)
+ {
+ if (ndbcluster_create_event(ndb, ndbtab, event_name.c_ptr(), share))
+ {
+ sql_print_error("NDB Binlog: "
+ "FAILED CREATE (DISCOVER) TABLE Event: %s",
+ event_name.c_ptr());
+ break; // error
+ }
+ if (ndb_extra_logging)
+ sql_print_information("NDB Binlog: "
+ "CREATE (DISCOVER) TABLE Event: %s",
+ event_name.c_ptr());
+ }
+ else
+ {
+ delete ev;
+ if (ndb_extra_logging)
+ sql_print_information("NDB Binlog: DISCOVER TABLE Event: %s",
+ event_name.c_ptr());
+ }
+
+ /*
+ create the event operations for receiving logging events
+ */
+ if (ndbcluster_create_event_ops(share, ndbtab, event_name.c_ptr()))
+ {
+ sql_print_error("NDB Binlog:"
+ "FAILED CREATE (DISCOVER) EVENT OPERATIONS Event: %s",
+ event_name.c_ptr());
+ /* a warning has been issued to the client */
+ DBUG_RETURN(0);
+ }
+ DBUG_RETURN(0);
+ }
+ DBUG_RETURN(-1);
+}
+
+int
+ndbcluster_create_event(Ndb *ndb, const NDBTAB *ndbtab,
+ const char *event_name, NDB_SHARE *share,
+ int push_warning)
+{
+ THD *thd= current_thd;
+ DBUG_ENTER("ndbcluster_create_event");
+ DBUG_PRINT("info", ("table=%s version=%d event=%s share=%s",
+ ndbtab->getName(), ndbtab->getObjectVersion(),
+ event_name, share ? share->key : "(nil)"));
+ DBUG_ASSERT(! IS_NDB_BLOB_PREFIX(ndbtab->getName()));
+ if (!share)
+ {
+ DBUG_PRINT("info", ("share == NULL"));
+ DBUG_RETURN(0);
+ }
+ if (share->flags & NSF_NO_BINLOG)
+ {
+ DBUG_PRINT("info", ("share->flags & NSF_NO_BINLOG, flags: %x %d",
+ share->flags, share->flags & NSF_NO_BINLOG));
+ DBUG_RETURN(0);
+ }
+
+ NDBDICT *dict= ndb->getDictionary();
+ NDBEVENT my_event(event_name);
+ my_event.setTable(*ndbtab);
+ my_event.addTableEvent(NDBEVENT::TE_ALL);
+ if (share->flags & NSF_HIDDEN_PK)
+ {
+ if (share->flags & NSF_BLOB_FLAG)
+ {
+ sql_print_error("NDB Binlog: logging of table %s "
+ "with BLOB attribute and no PK is not supported",
+ share->key);
+ if (push_warning)
+ push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_ERROR,
+ ER_ILLEGAL_HA_CREATE_OPTION,
+ ER(ER_ILLEGAL_HA_CREATE_OPTION),
+ ndbcluster_hton_name,
+ "Binlog of table with BLOB attribute and no PK");
+
+ share->flags|= NSF_NO_BINLOG;
+ DBUG_RETURN(-1);
+ }
+ /* No primary key, subscribe for all attributes */
+ my_event.setReport(NDBEVENT::ER_ALL);
+ DBUG_PRINT("info", ("subscription all"));
+ }
+ else
+ {
+ if (ndb_schema_share || strcmp(share->db, NDB_REP_DB) ||
+ strcmp(share->table_name, NDB_SCHEMA_TABLE))
+ {
+ my_event.setReport(NDBEVENT::ER_UPDATED);
+ DBUG_PRINT("info", ("subscription only updated"));
+ }
+ else
+ {
+ my_event.setReport((NDBEVENT::EventReport)
+ (NDBEVENT::ER_ALL | NDBEVENT::ER_SUBSCRIBE));
+ DBUG_PRINT("info", ("subscription all and subscribe"));
+ }
+ }
+ if (share->flags & NSF_BLOB_FLAG)
+ my_event.mergeEvents(TRUE);
+
+ /* add all columns to the event */
+ int n_cols= ndbtab->getNoOfColumns();
+ for(int a= 0; a < n_cols; a++)
+ my_event.addEventColumn(a);
+
+ if (dict->createEvent(my_event)) // Add event to database
+ {
+ if (dict->getNdbError().classification != NdbError::SchemaObjectExists)
+ {
+ /*
+ failed, print a warning
+ */
+ if (push_warning > 1)
+ push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_ERROR,
+ ER_GET_ERRMSG, ER(ER_GET_ERRMSG),
+ dict->getNdbError().code,
+ dict->getNdbError().message, "NDB");
+ sql_print_error("NDB Binlog: Unable to create event in database. "
+ "Event: %s Error Code: %d Message: %s", event_name,
+ dict->getNdbError().code, dict->getNdbError().message);
+ DBUG_RETURN(-1);
+ }
+
+ /*
+ try retrieving the event, if table version/id matches, we will get
+ a valid event. Otherwise we have a trailing event from before
+ */
+ const NDBEVENT *ev;
+ if ((ev= dict->getEvent(event_name)))
+ {
+ delete ev;
+ DBUG_RETURN(0);
+ }
+
+ /*
+ trailing event from before; an error, but try to correct it
+ */
+ if (dict->getNdbError().code == NDB_INVALID_SCHEMA_OBJECT &&
+ dict->dropEvent(my_event.getName()))
+ {
+ if (push_warning > 1)
+ push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_ERROR,
+ ER_GET_ERRMSG, ER(ER_GET_ERRMSG),
+ dict->getNdbError().code,
+ dict->getNdbError().message, "NDB");
+ sql_print_error("NDB Binlog: Unable to create event in database. "
+ " Attempt to correct with drop failed. "
+ "Event: %s Error Code: %d Message: %s",
+ event_name,
+ dict->getNdbError().code,
+ dict->getNdbError().message);
+ DBUG_RETURN(-1);
+ }
+
+ /*
+ try to add the event again
+ */
+ if (dict->createEvent(my_event))
+ {
+ if (push_warning > 1)
+ push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_ERROR,
+ ER_GET_ERRMSG, ER(ER_GET_ERRMSG),
+ dict->getNdbError().code,
+ dict->getNdbError().message, "NDB");
+ sql_print_error("NDB Binlog: Unable to create event in database. "
+ " Attempt to correct with drop ok, but create failed. "
+ "Event: %s Error Code: %d Message: %s",
+ event_name,
+ dict->getNdbError().code,
+ dict->getNdbError().message);
+ DBUG_RETURN(-1);
+ }
+#ifdef NDB_BINLOG_EXTRA_WARNINGS
+ push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_ERROR,
+ ER_GET_ERRMSG, ER(ER_GET_ERRMSG),
+ 0, "NDB Binlog: Removed trailing event",
+ "NDB");
+#endif
+ }
+
+ DBUG_RETURN(0);
+}
+
+inline int is_ndb_compatible_type(Field *field)
+{
+ return
+ !(field->flags & BLOB_FLAG) &&
+ field->type() != MYSQL_TYPE_BIT &&
+ field->pack_length() != 0;
+}
+
+/*
+ - create eventOperations for receiving log events
+ - setup ndb recattrs for reception of log event data
+ - "start" the event operation
+
+ used at create/discover of tables
+*/
+int
+ndbcluster_create_event_ops(NDB_SHARE *share, const NDBTAB *ndbtab,
+ const char *event_name)
+{
+ THD *thd= current_thd;
+ /*
+ we are in either create table or rename table so table should be
+ locked, hence we can work with the share without locks
+ */
+
+ DBUG_ENTER("ndbcluster_create_event_ops");
+ DBUG_PRINT("enter", ("table: %s event: %s", ndbtab->getName(), event_name));
+ DBUG_ASSERT(! IS_NDB_BLOB_PREFIX(ndbtab->getName()));
+
+ DBUG_ASSERT(share != 0);
+
+ if (share->flags & NSF_NO_BINLOG)
+ {
+ DBUG_PRINT("info", ("share->flags & NSF_NO_BINLOG, flags: %x",
+ share->flags));
+ DBUG_RETURN(0);
+ }
+
+ int do_ndb_schema_share= 0, do_ndb_apply_status_share= 0;
+ if (!ndb_schema_share && strcmp(share->db, NDB_REP_DB) == 0 &&
+ strcmp(share->table_name, NDB_SCHEMA_TABLE) == 0)
+ do_ndb_schema_share= 1;
+ else if (!ndb_apply_status_share && strcmp(share->db, NDB_REP_DB) == 0 &&
+ strcmp(share->table_name, NDB_APPLY_TABLE) == 0)
+ do_ndb_apply_status_share= 1;
+ else if (!binlog_filter->db_ok(share->db) || !ndb_binlog_running)
+ {
+ share->flags|= NSF_NO_BINLOG;
+ DBUG_RETURN(0);
+ }
+
+ if (share->op)
+ {
+ assert(share->op->getCustomData() == (void *) share);
+
+ DBUG_ASSERT(share->use_count > 1);
+ sql_print_error("NDB Binlog: discover reusing old ev op");
+ /* ndb_share reference ToDo free */
+ DBUG_PRINT("NDB_SHARE", ("%s ToDo free use_count: %u",
+ share->key, share->use_count));
+ free_share(&share); // old event op already has reference
+ DBUG_RETURN(0);
+ }
+
+ TABLE *table= share->table;
+
+ int retries= 100;
+ /*
+ 100 milliseconds, temporary error on schema operation can
+ take some time to be resolved
+ */
+ int retry_sleep= 100;
+ while (1)
+ {
+ pthread_mutex_lock(&injector_mutex);
+ Ndb *ndb= injector_ndb;
+ if (do_ndb_schema_share)
+ ndb= schema_ndb;
+
+ if (ndb == 0)
+ {
+ pthread_mutex_unlock(&injector_mutex);
+ DBUG_RETURN(-1);
+ }
+
+ NdbEventOperation* op;
+ if (do_ndb_schema_share)
+ op= ndb->createEventOperation(event_name);
+ else
+ {
+ // set injector_ndb database/schema from table internal name
+ int ret= ndb->setDatabaseAndSchemaName(ndbtab);
+ assert(ret == 0);
+ op= ndb->createEventOperation(event_name);
+ // reset to catch errors
+ ndb->setDatabaseName("");
+ }
+ if (!op)
+ {
+ sql_print_error("NDB Binlog: Creating NdbEventOperation failed for"
+ " %s",event_name);
+ push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_ERROR,
+ ER_GET_ERRMSG, ER(ER_GET_ERRMSG),
+ ndb->getNdbError().code,
+ ndb->getNdbError().message,
+ "NDB");
+ pthread_mutex_unlock(&injector_mutex);
+ DBUG_RETURN(-1);
+ }
+
+ if (share->flags & NSF_BLOB_FLAG)
+ op->mergeEvents(TRUE); // currently not inherited from event
+
+ DBUG_PRINT("info", ("share->ndb_value[0]: 0x%lx share->ndb_value[1]: 0x%lx",
+ (long) share->ndb_value[0],
+ (long) share->ndb_value[1]));
+ int n_columns= ndbtab->getNoOfColumns();
+ int n_fields= table ? table->s->fields : 0; // XXX ???
+ for (int j= 0; j < n_columns; j++)
+ {
+ const char *col_name= ndbtab->getColumn(j)->getName();
+ NdbValue attr0, attr1;
+ if (j < n_fields)
+ {
+ Field *f= share->table->field[j];
+ if (is_ndb_compatible_type(f))
+ {
+ DBUG_PRINT("info", ("%s compatible", col_name));
+ attr0.rec= op->getValue(col_name, (char*) f->ptr);
+ attr1.rec= op->getPreValue(col_name,
+ (f->ptr - share->table->record[0]) +
+ (char*) share->table->record[1]);
+ }
+ else if (! (f->flags & BLOB_FLAG))
+ {
+ DBUG_PRINT("info", ("%s non compatible", col_name));
+ attr0.rec= op->getValue(col_name);
+ attr1.rec= op->getPreValue(col_name);
+ }
+ else
+ {
+ DBUG_PRINT("info", ("%s blob", col_name));
+ DBUG_ASSERT(share->flags & NSF_BLOB_FLAG);
+ attr0.blob= op->getBlobHandle(col_name);
+ attr1.blob= op->getPreBlobHandle(col_name);
+ if (attr0.blob == NULL || attr1.blob == NULL)
+ {
+ 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,
+ ER_GET_ERRMSG, ER(ER_GET_ERRMSG),
+ op->getNdbError().code,
+ op->getNdbError().message,
+ "NDB");
+ ndb->dropEventOperation(op);
+ pthread_mutex_unlock(&injector_mutex);
+ DBUG_RETURN(-1);
+ }
+ }
+ }
+ else
+ {
+ DBUG_PRINT("info", ("%s hidden key", col_name));
+ attr0.rec= op->getValue(col_name);
+ attr1.rec= op->getPreValue(col_name);
+ }
+ share->ndb_value[0][j].ptr= attr0.ptr;
+ share->ndb_value[1][j].ptr= attr1.ptr;
+ DBUG_PRINT("info", ("&share->ndb_value[0][%d]: 0x%lx "
+ "share->ndb_value[0][%d]: 0x%lx",
+ j, (long) &share->ndb_value[0][j],
+ j, (long) attr0.ptr));
+ DBUG_PRINT("info", ("&share->ndb_value[1][%d]: 0x%lx "
+ "share->ndb_value[1][%d]: 0x%lx",
+ j, (long) &share->ndb_value[0][j],
+ j, (long) attr1.ptr));
+ }
+ op->setCustomData((void *) share); // set before execute
+ share->op= op; // assign op in NDB_SHARE
+ if (op->execute())
+ {
+ share->op= NULL;
+ retries--;
+ if (op->getNdbError().status != NdbError::TemporaryError &&
+ op->getNdbError().code != 1407)
+ retries= 0;
+ if (retries == 0)
+ {
+ push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_ERROR,
+ ER_GET_ERRMSG, ER(ER_GET_ERRMSG),
+ op->getNdbError().code, op->getNdbError().message,
+ "NDB");
+ sql_print_error("NDB Binlog: ndbevent->execute failed for %s; %d %s",
+ event_name,
+ op->getNdbError().code, op->getNdbError().message);
+ }
+ ndb->dropEventOperation(op);
+ pthread_mutex_unlock(&injector_mutex);
+ if (retries)
+ {
+ my_sleep(retry_sleep);
+ continue;
+ }
+ DBUG_RETURN(-1);
+ }
+ pthread_mutex_unlock(&injector_mutex);
+ break;
+ }
+
+ /* ndb_share reference binlog */
+ get_share(share);
+ DBUG_PRINT("NDB_SHARE", ("%s binlog use_count: %u",
+ share->key, share->use_count));
+ if (do_ndb_apply_status_share)
+ {
+ /* ndb_share reference binlog extra */
+ 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);
+ }
+ else if (do_ndb_schema_share)
+ {
+ /* ndb_share reference binlog extra */
+ 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);
+ }
+
+ DBUG_PRINT("info",("%s share->op: 0x%lx share->use_count: %u",
+ share->key, (long) share->op, share->use_count));
+
+ if (ndb_extra_logging)
+ sql_print_information("NDB Binlog: logging %s", share->key);
+ DBUG_RETURN(0);
+}
+
+/*
+ when entering the calling thread should have a share lock id share != 0
+ then the injector thread will have one as well, i.e. share->use_count == 0
+ (unless it has already dropped... then share->op == 0)
+*/
+int
+ndbcluster_handle_drop_table(Ndb *ndb, const char *event_name,
+ NDB_SHARE *share, const char *type_str)
+{
+ DBUG_ENTER("ndbcluster_handle_drop_table");
+ THD *thd= current_thd;
+
+ NDBDICT *dict= ndb->getDictionary();
+ if (event_name && dict->dropEvent(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,
+ ER_GET_ERRMSG, ER(ER_GET_ERRMSG),
+ dict->getNdbError().code,
+ dict->getNdbError().message, "NDB");
+ /* error is not that the event did not exist */
+ sql_print_error("NDB Binlog: Unable to drop event in database. "
+ "Event: %s Error Code: %d Message: %s",
+ event_name,
+ dict->getNdbError().code,
+ dict->getNdbError().message);
+ /* ToDo; handle error? */
+ if (share && share->op &&
+ share->op->getState() == NdbEventOperation::EO_EXECUTING &&
+ dict->getNdbError().mysql_code != HA_ERR_NO_CONNECTION)
+ {
+ DBUG_ASSERT(FALSE);
+ DBUG_RETURN(-1);
+ }
+ }
+ }
+
+ if (share == 0 || share->op == 0)
+ {
+ DBUG_RETURN(0);
+ }
+
+/*
+ Syncronized drop between client thread and injector thread is
+ neccessary in order to maintain ordering in the binlog,
+ such that the drop occurs _after_ any inserts/updates/deletes.
+
+ The penalty for this is that the drop table becomes slow.
+
+ This wait is however not strictly neccessary to produce a binlog
+ that is usable. However the slave does not currently handle
+ these out of order, thus we are keeping the SYNC_DROP_ defined
+ for now.
+*/
+ const char *save_proc_info= thd->proc_info;
+#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;
+ while (share->op)
+ {
+ struct timespec abstime;
+ set_timespec(abstime, 1);
+ int ret= pthread_cond_timedwait(&injector_cond,
+ &share->mutex,
+ &abstime);
+ if (thd->killed ||
+ share->op == 0)
+ break;
+ if (ret)
+ {
+ max_timeout--;
+ if (max_timeout == 0)
+ {
+ sql_print_error("NDB %s: %s timed out. Ignoring...",
+ type_str, share->key);
+ break;
+ }
+ if (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);
+#else
+ (void) pthread_mutex_lock(&share->mutex);
+ share->op_old= share->op;
+ share->op= 0;
+ (void) pthread_mutex_unlock(&share->mutex);
+#endif
+ thd->proc_info= save_proc_info;
+
+ DBUG_RETURN(0);
+}
+
+
+/********************************************************************
+ Internal helper functions for differentd events from the stoarage nodes
+ used by the ndb injector thread
+********************************************************************/
+
+/*
+ Handle error states on events from the storage nodes
+*/
+static int ndb_binlog_thread_handle_error(Ndb *ndb, NdbEventOperation *pOp,
+ ndb_binlog_index_row &row)
+{
+ NDB_SHARE *share= (NDB_SHARE *)pOp->getCustomData();
+ DBUG_ENTER("ndb_binlog_thread_handle_error");
+
+ int overrun= pOp->isOverrun();
+ if (overrun)
+ {
+ /*
+ ToDo: this error should rather clear the ndb_binlog_index...
+ and continue
+ */
+ sql_print_error("NDB Binlog: Overrun in event buffer, "
+ "this means we have dropped events. Cannot "
+ "continue binlog for %s", share->key);
+ pOp->clearError();
+ DBUG_RETURN(-1);
+ }
+
+ if (!pOp->isConsistent())
+ {
+ /*
+ ToDo: this error should rather clear the ndb_binlog_index...
+ and continue
+ */
+ sql_print_error("NDB Binlog: Not Consistent. Cannot "
+ "continue binlog for %s. Error code: %d"
+ " Message: %s", share->key,
+ pOp->getNdbError().code,
+ pOp->getNdbError().message);
+ pOp->clearError();
+ DBUG_RETURN(-1);
+ }
+ sql_print_error("NDB Binlog: unhandled error %d for table %s",
+ pOp->hasError(), share->key);
+ pOp->clearError();
+ DBUG_RETURN(0);
+}
+
+static int
+ndb_binlog_thread_handle_non_data_event(THD *thd, Ndb *ndb,
+ NdbEventOperation *pOp,
+ ndb_binlog_index_row &row)
+{
+ NDB_SHARE *share= (NDB_SHARE *)pOp->getCustomData();
+ NDBEVENT::TableEvent type= pOp->getEventType();
+
+ switch (type)
+ {
+ case NDBEVENT::TE_CLUSTER_FAILURE:
+ if (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 &&
+ ndb_binlog_tables_inited && ndb_binlog_running)
+ sql_print_information("NDB Binlog: ndb tables initially "
+ "read only on reconnect.");
+ /* ndb_share reference binlog extra free */
+ DBUG_PRINT("NDB_SHARE", ("%s binlog extra free use_count: %u",
+ share->key, share->use_count));
+ free_share(&ndb_apply_status_share);
+ ndb_apply_status_share= 0;
+ ndb_binlog_tables_inited= 0;
+ }
+ DBUG_PRINT("error", ("CLUSTER FAILURE EVENT: "
+ "%s received share: 0x%lx op: 0x%lx share op: 0x%lx "
+ "op_old: 0x%lx",
+ share->key, (long) share, (long) pOp,
+ (long) share->op, (long) share->op_old));
+ break;
+ case NDBEVENT::TE_DROP:
+ if (ndb_apply_status_share == share)
+ {
+ if (ndb_extra_logging &&
+ ndb_binlog_tables_inited && ndb_binlog_running)
+ sql_print_information("NDB Binlog: ndb tables initially "
+ "read only on reconnect.");
+ /* ndb_share reference binlog extra free */
+ DBUG_PRINT("NDB_SHARE", ("%s binlog extra free use_count: %u",
+ share->key, share->use_count));
+ free_share(&ndb_apply_status_share);
+ ndb_apply_status_share= 0;
+ ndb_binlog_tables_inited= 0;
+ }
+ /* ToDo: remove printout */
+ if (ndb_extra_logging)
+ sql_print_information("NDB Binlog: drop table %s.", share->key);
+ // fall through
+ case NDBEVENT::TE_ALTER:
+ row.n_schemaops++;
+ DBUG_PRINT("info", ("TABLE %s EVENT: %s received share: 0x%lx op: 0x%lx "
+ "share op: 0x%lx op_old: 0x%lx",
+ type == NDBEVENT::TE_DROP ? "DROP" : "ALTER",
+ share->key, (long) share, (long) pOp,
+ (long) share->op, (long) share->op_old));
+ break;
+ case NDBEVENT::TE_NODE_FAILURE:
+ /* fall through */
+ case NDBEVENT::TE_SUBSCRIBE:
+ /* fall through */
+ case NDBEVENT::TE_UNSUBSCRIBE:
+ /* ignore */
+ return 0;
+ default:
+ sql_print_error("NDB Binlog: unknown non data event %d for %s. "
+ "Ignoring...", (unsigned) type, share->key);
+ return 0;
+ }
+
+ ndb_handle_schema_change(thd, ndb, pOp, share);
+ return 0;
+}
+
+/*
+ Handle data events from the storage nodes
+*/
+static int
+ndb_binlog_thread_handle_data_event(Ndb *ndb, NdbEventOperation *pOp,
+ ndb_binlog_index_row &row,
+ injector::transaction &trans)
+{
+ NDB_SHARE *share= (NDB_SHARE*) pOp->getCustomData();
+ if (share == ndb_apply_status_share)
+ return 0;
+
+ uint32 originating_server_id= pOp->getAnyValue();
+ if (originating_server_id == 0)
+ originating_server_id= ::server_id;
+ else if (originating_server_id & NDB_ANYVALUE_RESERVED)
+ {
+ if (originating_server_id != NDB_ANYVALUE_FOR_NOLOGGING)
+ sql_print_warning("NDB: unknown value for binlog signalling 0x%X, "
+ "event not logged",
+ originating_server_id);
+ return 0;
+ }
+ else if (!g_ndb_log_slave_updates)
+ {
+ /*
+ This event comes from a slave applier since it has an originating
+ server id set. Since option to log slave updates is not set, skip it.
+ */
+ return 0;
+ }
+
+ TABLE *table= share->table;
+ DBUG_ASSERT(trans.good());
+ DBUG_ASSERT(table != 0);
+
+ dbug_print_table("table", table);
+
+ TABLE_SHARE *table_s= table->s;
+ uint n_fields= table_s->fields;
+ MY_BITMAP b;
+ /* Potential buffer for the bitmap */
+ uint32 bitbuf[128 / (sizeof(uint32) * 8)];
+ bitmap_init(&b, n_fields <= sizeof(bitbuf) * 8 ? bitbuf : NULL,
+ n_fields, FALSE);
+ bitmap_set_all(&b);
+
+ /*
+ row data is already in table->record[0]
+ As we told the NdbEventOperation to do this
+ (saves moving data about many times)
+ */
+
+ /*
+ for now malloc/free blobs buffer each time
+ TODO if possible share single permanent buffer with handlers
+ */
+ uchar* blobs_buffer[2] = { 0, 0 };
+ uint blobs_buffer_size[2] = { 0, 0 };
+
+ switch(pOp->getEventType())
+ {
+ case NDBEVENT::TE_INSERT:
+ row.n_inserts++;
+ DBUG_PRINT("info", ("INSERT INTO %s.%s",
+ table_s->db.str, table_s->table_name.str));
+ {
+ if (share->flags & NSF_BLOB_FLAG)
+ {
+ my_ptrdiff_t ptrdiff= 0;
+ IF_DBUG(int ret =) 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,
+ injector::transaction::table(table,
+ TRUE),
+ &b, n_fields, table->record[0]);
+ DBUG_ASSERT(ret == 0);
+ }
+ break;
+ case NDBEVENT::TE_DELETE:
+ row.n_deletes++;
+ DBUG_PRINT("info",("DELETE FROM %s.%s",
+ table_s->db.str, table_s->table_name.str));
+ {
+ /*
+ table->record[0] contains only the primary key in this case
+ since we do not have an after image
+ */
+ int n;
+ if (table->s->primary_key != MAX_KEY)
+ n= 0; /*
+ use the primary key only as it save time and space and
+ it is the only thing needed to log the delete
+ */
+ else
+ n= 1; /*
+ we use the before values since we don't have a primary key
+ since the mysql server does not handle the hidden primary
+ key
+ */
+
+ 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],
+ blobs_buffer[n],
+ blobs_buffer_size[n],
+ ptrdiff);
+ DBUG_ASSERT(ret == 0);
+ }
+ 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,
+ injector::transaction::table(table,
+ TRUE),
+ &b, n_fields, table->record[n]);
+ DBUG_ASSERT(ret == 0);
+ }
+ break;
+ case NDBEVENT::TE_UPDATE:
+ row.n_updates++;
+ DBUG_PRINT("info", ("UPDATE %s.%s",
+ table_s->db.str, table_s->table_name.str));
+ {
+ if (share->flags & NSF_BLOB_FLAG)
+ {
+ my_ptrdiff_t ptrdiff= 0;
+ IF_DBUG(int ret =) 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]);
+ DBUG_EXECUTE("info", print_records(table, table->record[0]););
+ if (table->s->primary_key != MAX_KEY)
+ {
+ /*
+ since table has a primary key, we can do a write
+ using only after values
+ */
+ trans.write_row(originating_server_id,
+ injector::transaction::table(table, TRUE),
+ &b, n_fields, table->record[0]);// after values
+ }
+ else
+ {
+ /*
+ mysql server cannot handle the ndb hidden key and
+ therefore needs the before image as well
+ */
+ 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],
+ blobs_buffer[1],
+ blobs_buffer_size[1],
+ ptrdiff);
+ DBUG_ASSERT(ret == 0);
+ }
+ 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,
+ injector::transaction::table(table,
+ TRUE),
+ &b, n_fields,
+ table->record[1], // before values
+ table->record[0]);// after values
+ DBUG_ASSERT(ret == 0);
+ }
+ }
+ break;
+ default:
+ /* We should REALLY never get here. */
+ DBUG_PRINT("info", ("default - uh oh, a brain exploded."));
+ break;
+ }
+
+ 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));
+ }
+
+ return 0;
+}
+
+//#define RUN_NDB_BINLOG_TIMER
+#ifdef RUN_NDB_BINLOG_TIMER
+class Timer
+{
+public:
+ Timer() { start(); }
+ void start() { gettimeofday(&m_start, 0); }
+ void stop() { gettimeofday(&m_stop, 0); }
+ ulong elapsed_ms()
+ {
+ return (ulong)
+ (((longlong) m_stop.tv_sec - (longlong) m_start.tv_sec) * 1000 +
+ ((longlong) m_stop.tv_usec -
+ (longlong) m_start.tv_usec + 999) / 1000);
+ }
+private:
+ struct timeval m_start,m_stop;
+};
+#endif
+
+/****************************************************************
+ Injector thread main loop
+****************************************************************/
+
+static uchar *
+ndb_schema_objects_get_key(NDB_SCHEMA_OBJECT *schema_object,
+ size_t *length,
+ my_bool not_used __attribute__((unused)))
+{
+ *length= schema_object->key_length;
+ return (uchar*) schema_object->key;
+}
+
+static NDB_SCHEMA_OBJECT *ndb_get_schema_object(const char *key,
+ my_bool create_if_not_exists,
+ my_bool have_lock)
+{
+ NDB_SCHEMA_OBJECT *ndb_schema_object;
+ uint length= (uint) strlen(key);
+ DBUG_ENTER("ndb_get_schema_object");
+ DBUG_PRINT("enter", ("key: '%s'", key));
+
+ if (!have_lock)
+ pthread_mutex_lock(&ndbcluster_mutex);
+ while (!(ndb_schema_object=
+ (NDB_SCHEMA_OBJECT*) hash_search(&ndb_schema_objects,
+ (uchar*) key,
+ length)))
+ {
+ if (!create_if_not_exists)
+ {
+ DBUG_PRINT("info", ("does not exist"));
+ break;
+ }
+ if (!(ndb_schema_object=
+ (NDB_SCHEMA_OBJECT*) my_malloc(sizeof(*ndb_schema_object) + length + 1,
+ MYF(MY_WME | MY_ZEROFILL))))
+ {
+ DBUG_PRINT("info", ("malloc error"));
+ break;
+ }
+ ndb_schema_object->key= (char *)(ndb_schema_object+1);
+ memcpy(ndb_schema_object->key, key, length + 1);
+ ndb_schema_object->key_length= length;
+ if (my_hash_insert(&ndb_schema_objects, (uchar*) ndb_schema_object))
+ {
+ my_free((uchar*) ndb_schema_object, 0);
+ break;
+ }
+ pthread_mutex_init(&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);
+ break;
+ }
+ if (ndb_schema_object)
+ {
+ ndb_schema_object->use_count++;
+ DBUG_PRINT("info", ("use_count: %d", ndb_schema_object->use_count));
+ }
+ if (!have_lock)
+ pthread_mutex_unlock(&ndbcluster_mutex);
+ DBUG_RETURN(ndb_schema_object);
+}
+
+
+static void ndb_free_schema_object(NDB_SCHEMA_OBJECT **ndb_schema_object,
+ bool have_lock)
+{
+ DBUG_ENTER("ndb_free_schema_object");
+ DBUG_PRINT("enter", ("key: '%s'", (*ndb_schema_object)->key));
+ if (!have_lock)
+ pthread_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));
+ *ndb_schema_object= 0;
+ }
+ else
+ {
+ DBUG_PRINT("info", ("use_count: %d", (*ndb_schema_object)->use_count));
+ }
+ if (!have_lock)
+ pthread_mutex_unlock(&ndbcluster_mutex);
+ DBUG_VOID_RETURN;
+}
+
+
+pthread_handler_t ndb_binlog_thread_func(void *arg)
+{
+ THD *thd; /* needs to be first for thread_stack */
+ Ndb *i_ndb= 0;
+ Ndb *s_ndb= 0;
+ Thd_ndb *thd_ndb=0;
+ int ndb_update_ndb_binlog_index= 1;
+ injector *inj= injector::instance();
+ uint incident_id= 0;
+
+#ifdef RUN_NDB_BINLOG_TIMER
+ Timer main_timer;
+#endif
+
+ pthread_mutex_lock(&injector_mutex);
+ /*
+ Set up the Thread
+ */
+ my_thread_init();
+ DBUG_ENTER("ndb_binlog_thread");
+
+ thd= new THD; /* note that contructor of THD uses DBUG_ */
+ THD_CHECK_SENTRY(thd);
+
+ /* 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);
+ thd->thread_id= thread_id++;
+ pthread_mutex_unlock(&LOCK_thread_count);
+
+ thd->thread_stack= (char*) &thd; /* remember where our stack is */
+ if (thd->store_globals())
+ {
+ thd->cleanup();
+ delete thd;
+ ndb_binlog_thread_running= -1;
+ pthread_mutex_unlock(&injector_mutex);
+ pthread_cond_signal(&injector_cond);
+ my_thread_end();
+ pthread_exit(0);
+ DBUG_RETURN(NULL);
+ }
+ 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;
+
+ /*
+ Set up ndb binlog
+ */
+ sql_print_information("Starting MySQL Cluster Binlog Thread");
+
+ pthread_detach_this_thread();
+ thd->real_id= pthread_self();
+ pthread_mutex_lock(&LOCK_thread_count);
+ threads.append(thd);
+ pthread_mutex_unlock(&LOCK_thread_count);
+ thd->lex->start_transaction_opt= 0;
+
+ if (!(s_ndb= new Ndb(g_ndb_cluster_connection, "")) ||
+ s_ndb->init())
+ {
+ 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);
+ goto err;
+ }
+
+ // empty database
+ if (!(i_ndb= new Ndb(g_ndb_cluster_connection, "")) ||
+ i_ndb->init())
+ {
+ sql_print_error("NDB Binlog: Getting Ndb object failed");
+ ndb_binlog_thread_running= -1;
+ pthread_mutex_unlock(&injector_mutex);
+ pthread_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);
+
+ /*
+ 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);
+ */
+ injector_thd= thd;
+ injector_ndb= i_ndb;
+ p_latest_trans_gci=
+ injector_ndb->get_ndb_cluster_connection().get_latest_trans_gci();
+ schema_ndb= s_ndb;
+
+ if (opt_bin_log)
+ {
+ ndb_binlog_running= TRUE;
+ }
+
+ /* Thread start up completed */
+ ndb_binlog_thread_running= 1;
+ pthread_mutex_unlock(&injector_mutex);
+ pthread_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);
+ while (!mysqld_server_started)
+ {
+ struct timespec abstime;
+ set_timespec(abstime, 1);
+ pthread_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);
+ goto err;
+ }
+ }
+ pthread_mutex_unlock(&LOCK_server_started);
+restart:
+ /*
+ Main NDB Injector loop
+ */
+ while (ndb_binlog_running)
+ {
+ /*
+ check if it is the first log, if so we do not insert a GAP event
+ as there is really no log to have a GAP in
+ */
+ if (incident_id == 0)
+ {
+ LOG_INFO log_info;
+ mysql_bin_log.get_current_log(&log_info);
+ int len= strlen(log_info.log_file_name);
+ uint no= 0;
+ if ((sscanf(log_info.log_file_name + len - 6, "%u", &no) == 1) &&
+ no == 1)
+ {
+ /* this is the fist log, so skip GAP event */
+ break;
+ }
+ }
+
+ /*
+ Always insert a GAP event as we cannot know what has happened
+ in the cluster while not being connected.
+ */
+ LEX_STRING const msg[2]=
+ {
+ { C_STRING_WITH_LEN("mysqld startup") },
+ { C_STRING_WITH_LEN("cluster disconnect")}
+ };
+ IF_DBUG(int error=)
+ inj->record_incident(thd, INCIDENT_LOST_EVENTS, msg[incident_id]);
+ DBUG_ASSERT(!error);
+ break;
+ }
+ incident_id= 1;
+ {
+ thd->proc_info= "Waiting for ndbcluster to start";
+
+ pthread_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);
+ if (ndbcluster_binlog_terminating)
+ {
+ pthread_mutex_unlock(&injector_mutex);
+ goto err;
+ }
+ }
+ pthread_mutex_unlock(&injector_mutex);
+
+ if (thd_ndb == NULL)
+ {
+ DBUG_ASSERT(ndbcluster_hton->slot != ~(uint)0);
+ if (!(thd_ndb= ha_ndbcluster::seize_thd_ndb()))
+ {
+ sql_print_error("Could not allocate Thd_ndb object");
+ goto err;
+ }
+ set_thd_ndb(thd, thd_ndb);
+ thd_ndb->options|= TNO_NO_LOG_SCHEMA_OP;
+ thd->query_id= 0; // to keep valgrind quiet
+ }
+ }
+
+ {
+ // wait for the first event
+ thd->proc_info= "Waiting for first event from ndbcluster";
+ int schema_res, res;
+ Uint64 schema_gci;
+ do
+ {
+ DBUG_PRINT("info", ("Waiting for the first event"));
+
+ if (ndbcluster_binlog_terminating)
+ goto err;
+
+ schema_res= s_ndb->pollEvents(100, &schema_gci);
+ } while (schema_gci == 0 || ndb_latest_received_binlog_epoch == schema_gci);
+ if (ndb_binlog_running)
+ {
+ Uint64 gci= i_ndb->getLatestGCI();
+ while (gci < schema_gci || gci == ndb_latest_received_binlog_epoch)
+ {
+ if (ndbcluster_binlog_terminating)
+ goto err;
+ res= i_ndb->pollEvents(10, &gci);
+ }
+ if (gci > schema_gci)
+ {
+ schema_gci= gci;
+ }
+ }
+ // now check that we have epochs consistant with what we had before the restart
+ DBUG_PRINT("info", ("schema_res: %d schema_gci: %lu", schema_res,
+ (long) schema_gci));
+ {
+ i_ndb->flushIncompleteEvents(schema_gci);
+ s_ndb->flushIncompleteEvents(schema_gci);
+ if (schema_gci < ndb_latest_handled_binlog_epoch)
+ {
+ sql_print_error("NDB Binlog: cluster has been restarted --initial or with older filesystem. "
+ "ndb_latest_handled_binlog_epoch: %u, while current epoch: %u. "
+ "RESET MASTER should be issued. Resetting ndb_latest_handled_binlog_epoch.",
+ (unsigned) ndb_latest_handled_binlog_epoch, (unsigned) schema_gci);
+ *p_latest_trans_gci= 0;
+ ndb_latest_handled_binlog_epoch= 0;
+ ndb_latest_applied_binlog_epoch= 0;
+ ndb_latest_received_binlog_epoch= 0;
+ }
+ else if (ndb_latest_applied_binlog_epoch > 0)
+ {
+ sql_print_warning("NDB Binlog: cluster has reconnected. "
+ "Changes to the database that occured while "
+ "disconnected will not be in the binlog");
+ }
+ if (ndb_extra_logging)
+ {
+ sql_print_information("NDB Binlog: starting log at epoch %u",
+ (unsigned)schema_gci);
+ }
+ }
+ }
+ {
+ 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 ||
+ do_ndbcluster_binlog_close_connection) &&
+ ndb_latest_handled_binlog_epoch >= *p_latest_trans_gci) &&
+ do_ndbcluster_binlog_close_connection != BCCC_restart; )
+ {
+#ifndef DBUG_OFF
+ if (do_ndbcluster_binlog_close_connection)
+ {
+ DBUG_PRINT("info", ("do_ndbcluster_binlog_close_connection: %d, "
+ "ndb_latest_handled_binlog_epoch: %lu, "
+ "*p_latest_trans_gci: %lu",
+ do_ndbcluster_binlog_close_connection,
+ (ulong) ndb_latest_handled_binlog_epoch,
+ (ulong) *p_latest_trans_gci));
+ }
+#endif
+#ifdef RUN_NDB_BINLOG_TIMER
+ main_timer.stop();
+ sql_print_information("main_timer %ld ms", main_timer.elapsed_ms());
+ main_timer.start();
+#endif
+
+ /*
+ now we don't want any events before next gci is complete
+ */
+ thd->proc_info= "Waiting for event from ndbcluster";
+ thd->set_time();
+
+ /* wait for event or 1000 ms */
+ Uint64 gci= 0, schema_gci;
+ int res= 0, tot_poll_wait= 1000;
+ if (ndb_binlog_running)
+ {
+ res= i_ndb->pollEvents(tot_poll_wait, &gci);
+ tot_poll_wait= 0;
+ }
+ else
+ {
+ /*
+ Just consume any events, not used if no binlogging
+ e.g. node failure events
+ */
+ Uint64 tmp_gci;
+ if (i_ndb->pollEvents(0, &tmp_gci))
+ while (i_ndb->nextEvent())
+ ;
+ }
+ int schema_res= s_ndb->pollEvents(tot_poll_wait, &schema_gci);
+ ndb_latest_received_binlog_epoch= gci;
+
+ while (gci > schema_gci && schema_res >= 0)
+ {
+ static char buf[64];
+ thd->proc_info= "Waiting for schema epoch";
+ my_snprintf(buf, sizeof(buf), "%s %u(%u)", thd->proc_info, (unsigned) schema_gci, (unsigned) gci);
+ thd->proc_info= buf;
+ schema_res= s_ndb->pollEvents(10, &schema_gci);
+ }
+
+ if ((ndbcluster_binlog_terminating ||
+ do_ndbcluster_binlog_close_connection) &&
+ (ndb_latest_handled_binlog_epoch >= *p_latest_trans_gci ||
+ !ndb_binlog_running))
+ break; /* Shutting down server */
+
+ if (ndb_binlog_index && ndb_binlog_index->s->version < refresh_version)
+ {
+ if (ndb_binlog_index->s->version < refresh_version)
+ {
+ close_thread_tables(thd);
+ ndb_binlog_index= 0;
+ }
+ }
+
+ MEM_ROOT **root_ptr=
+ my_pthread_getspecific_ptr(MEM_ROOT**, THR_MALLOC);
+ MEM_ROOT *old_root= *root_ptr;
+ MEM_ROOT mem_root;
+ init_sql_alloc(&mem_root, 4096, 0);
+ List<Cluster_schema> post_epoch_log_list;
+ List<Cluster_schema> post_epoch_unlock_list;
+ *root_ptr= &mem_root;
+
+ if (unlikely(schema_res > 0))
+ {
+ thd->proc_info= "Processing events from schema table";
+ s_ndb->
+ setReportThreshEventGCISlip(ndb_report_thresh_binlog_epoch_slip);
+ s_ndb->
+ setReportThreshEventFreeMem(ndb_report_thresh_binlog_mem_usage);
+ NdbEventOperation *pOp= s_ndb->nextEvent();
+ while (pOp != NULL)
+ {
+ if (!pOp->hasError())
+ {
+ ndb_binlog_thread_handle_schema_event(thd, s_ndb, pOp,
+ &post_epoch_log_list,
+ &post_epoch_unlock_list,
+ &mem_root);
+ DBUG_PRINT("info", ("s_ndb first: %s", s_ndb->getEventOperation() ?
+ s_ndb->getEventOperation()->getEvent()->getTable()->getName() :
+ "<empty>"));
+ DBUG_PRINT("info", ("i_ndb first: %s", i_ndb->getEventOperation() ?
+ i_ndb->getEventOperation()->getEvent()->getTable()->getName() :
+ "<empty>"));
+ if (i_ndb->getEventOperation() == NULL &&
+ s_ndb->getEventOperation() == NULL &&
+ do_ndbcluster_binlog_close_connection == BCCC_running)
+ {
+ DBUG_PRINT("info", ("do_ndbcluster_binlog_close_connection= BCCC_restart"));
+ do_ndbcluster_binlog_close_connection= BCCC_restart;
+ if (ndb_latest_received_binlog_epoch < *p_latest_trans_gci && ndb_binlog_running)
+ {
+ sql_print_error("NDB Binlog: latest transaction in epoch %lu not in binlog "
+ "as latest received epoch is %lu",
+ (ulong) *p_latest_trans_gci,
+ (ulong) ndb_latest_received_binlog_epoch);
+ }
+ }
+ }
+ else
+ sql_print_error("NDB: error %lu (%s) on handling "
+ "binlog schema event",
+ (ulong) pOp->getNdbError().code,
+ pOp->getNdbError().message);
+ pOp= s_ndb->nextEvent();
+ }
+ }
+
+ if (res > 0)
+ {
+ DBUG_PRINT("info", ("pollEvents res: %d", res));
+ thd->proc_info= "Processing events";
+ NdbEventOperation *pOp= i_ndb->nextEvent();
+ ndb_binlog_index_row row;
+ while (pOp != NULL)
+ {
+#ifdef RUN_NDB_BINLOG_TIMER
+ Timer gci_timer, write_timer;
+ int event_count= 0;
+ gci_timer.start();
+#endif
+ gci= pOp->getGCI();
+ DBUG_PRINT("info", ("Handling gci: %d", (unsigned)gci));
+ // sometimes get TE_ALTER with invalid table
+ DBUG_ASSERT(pOp->getEventType() == NdbDictionary::Event::TE_ALTER ||
+ ! IS_NDB_BLOB_PREFIX(pOp->getEvent()->getTable()->getName()));
+ DBUG_ASSERT(gci <= ndb_latest_received_binlog_epoch);
+
+ /* 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);
+
+ bzero((char*) &row, sizeof(row));
+ thd->variables.character_set_client= &my_charset_latin1;
+ injector::transaction trans;
+ // pass table map before epoch
+ {
+ Uint32 iter= 0;
+ const NdbEventOperation *gci_op;
+ Uint32 event_types;
+ while ((gci_op= i_ndb->getGCIEventOperations(&iter, &event_types))
+ != NULL)
+ {
+ NDB_SHARE *share= (NDB_SHARE*)gci_op->getCustomData();
+ DBUG_PRINT("info", ("per gci_op: 0x%lx share: 0x%lx event_types: 0x%x",
+ (long) gci_op, (long) share, event_types));
+ // workaround for interface returning TE_STOP events
+ // which are normally filtered out below in the nextEvent loop
+ if ((event_types & ~NdbDictionary::Event::TE_STOP) == 0)
+ {
+ DBUG_PRINT("info", ("Skipped TE_STOP on table %s",
+ gci_op->getEvent()->getTable()->getName()));
+ continue;
+ }
+ // this should not happen
+ if (share == NULL || share->table == NULL)
+ {
+ DBUG_PRINT("info", ("no share or table %s!",
+ gci_op->getEvent()->getTable()->getName()));
+ continue;
+ }
+ if (share == ndb_apply_status_share)
+ {
+ // skip this table, it is handled specially
+ continue;
+ }
+ TABLE *table= share->table;
+#ifndef DBUG_OFF
+ const LEX_STRING &name= table->s->table_name;
+#endif
+ if ((event_types & (NdbDictionary::Event::TE_INSERT |
+ NdbDictionary::Event::TE_UPDATE |
+ NdbDictionary::Event::TE_DELETE)) == 0)
+ {
+ DBUG_PRINT("info", ("skipping non data event table: %.*s",
+ (int) name.length, name.str));
+ continue;
+ }
+ if (!trans.good())
+ {
+ DBUG_PRINT("info",
+ ("Found new data event, initializing transaction"));
+ inj->new_trans(thd, &trans);
+ }
+ 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);
+ DBUG_ASSERT(ret == 0);
+ }
+ }
+ if (trans.good())
+ {
+ if (ndb_apply_status_share)
+ {
+ TABLE *table= ndb_apply_status_share->table;
+
+#ifndef DBUG_OFF
+ const LEX_STRING& name= table->s->table_name;
+ DBUG_PRINT("info", ("use_table: %.*s",
+ (int) name.length, name.str));
+#endif
+ injector::transaction::table tbl(table, TRUE);
+ IF_DBUG(int ret=) trans.use_table(::server_id, tbl);
+ DBUG_ASSERT(ret == 0);
+
+ /*
+ Intialize table->record[0]
+ */
+ empty_record(table);
+
+ table->field[0]->store((longlong)::server_id);
+ table->field[1]->store((longlong)gci);
+ table->field[2]->store("", 0, &my_charset_bin);
+ table->field[3]->store((longlong)0);
+ table->field[4]->store((longlong)0);
+ trans.write_row(::server_id,
+ injector::transaction::table(table, TRUE),
+ &table->s->all_set, table->s->fields,
+ table->record[0]);
+ }
+ else
+ {
+ sql_print_error("NDB: Could not get apply status share");
+ }
+ }
+#ifdef RUN_NDB_BINLOG_TIMER
+ write_timer.start();
+#endif
+ do
+ {
+#ifdef RUN_NDB_BINLOG_TIMER
+ event_count++;
+#endif
+ if (pOp->hasError() &&
+ ndb_binlog_thread_handle_error(i_ndb, pOp, row) < 0)
+ goto err;
+
+#ifndef DBUG_OFF
+ {
+ NDB_SHARE *share= (NDB_SHARE*) pOp->getCustomData();
+ DBUG_PRINT("info",
+ ("EVENT TYPE: %d GCI: %ld last applied: %ld "
+ "share: 0x%lx (%s.%s)", pOp->getEventType(),
+ (long) gci,
+ (long) ndb_latest_applied_binlog_epoch,
+ (long) share,
+ share ? share->db : "'NULL'",
+ share ? share->table_name : "'NULL'"));
+ DBUG_ASSERT(share != 0);
+ }
+ // assert that there is consistancy between gci op list
+ // and event list
+ {
+ Uint32 iter= 0;
+ const NdbEventOperation *gci_op;
+ Uint32 event_types;
+ while ((gci_op= i_ndb->getGCIEventOperations(&iter, &event_types))
+ != NULL)
+ {
+ if (gci_op == pOp)
+ break;
+ }
+ DBUG_ASSERT(gci_op == pOp);
+ DBUG_ASSERT((event_types & pOp->getEventType()) != 0);
+ }
+#endif
+ if ((unsigned) pOp->getEventType() <
+ (unsigned) NDBEVENT::TE_FIRST_NON_DATA_EVENT)
+ ndb_binlog_thread_handle_data_event(i_ndb, pOp, row, trans);
+ else
+ {
+ // set injector_ndb database/schema from table internal name
+ IF_DBUG(int ret=)
+ i_ndb->setDatabaseAndSchemaName(pOp->getEvent()->getTable());
+ DBUG_ASSERT(ret == 0);
+ ndb_binlog_thread_handle_non_data_event(thd, i_ndb, pOp, row);
+ // reset to catch errors
+ i_ndb->setDatabaseName("");
+ DBUG_PRINT("info", ("s_ndb first: %s", s_ndb->getEventOperation() ?
+ s_ndb->getEventOperation()->getEvent()->getTable()->getName() :
+ "<empty>"));
+ DBUG_PRINT("info", ("i_ndb first: %s", i_ndb->getEventOperation() ?
+ i_ndb->getEventOperation()->getEvent()->getTable()->getName() :
+ "<empty>"));
+ if (i_ndb->getEventOperation() == NULL &&
+ s_ndb->getEventOperation() == NULL &&
+ do_ndbcluster_binlog_close_connection == BCCC_running)
+ {
+ DBUG_PRINT("info", ("do_ndbcluster_binlog_close_connection= BCCC_restart"));
+ do_ndbcluster_binlog_close_connection= BCCC_restart;
+ if (ndb_latest_received_binlog_epoch < *p_latest_trans_gci && ndb_binlog_running)
+ {
+ sql_print_error("NDB Binlog: latest transaction in epoch %lu not in binlog "
+ "as latest received epoch is %lu",
+ (ulong) *p_latest_trans_gci,
+ (ulong) ndb_latest_received_binlog_epoch);
+ }
+ }
+ }
+
+ pOp= i_ndb->nextEvent();
+ } while (pOp && pOp->getGCI() == gci);
+
+ /*
+ note! pOp is not referring to an event in the next epoch
+ or is == 0
+ */
+#ifdef RUN_NDB_BINLOG_TIMER
+ write_timer.stop();
+#endif
+
+ if (trans.good())
+ {
+ //DBUG_ASSERT(row.n_inserts || row.n_updates || row.n_deletes);
+ thd->proc_info= "Committing events to binlog";
+ injector::transaction::binlog_pos start= trans.start_pos();
+ if (int r= trans.commit())
+ {
+ sql_print_error("NDB Binlog: "
+ "Error during COMMIT of GCI. Error: %d",
+ r);
+ /* TODO: Further handling? */
+ }
+ row.gci= gci;
+ row.master_log_file= start.file_name();
+ row.master_log_pos= start.file_pos();
+
+ DBUG_PRINT("info", ("COMMIT gci: %lu", (ulong) gci));
+ if (ndb_update_ndb_binlog_index)
+ ndb_add_ndb_binlog_index(thd, &row);
+ ndb_latest_applied_binlog_epoch= gci;
+ }
+ ndb_latest_handled_binlog_epoch= gci;
+#ifdef RUN_NDB_BINLOG_TIMER
+ gci_timer.stop();
+ sql_print_information("gci %ld event_count %d write time "
+ "%ld(%d e/s), total time %ld(%d e/s)",
+ (ulong)gci, event_count,
+ write_timer.elapsed_ms(),
+ (1000*event_count) / write_timer.elapsed_ms(),
+ gci_timer.elapsed_ms(),
+ (1000*event_count) / gci_timer.elapsed_ms());
+#endif
+ }
+ }
+
+ ndb_binlog_thread_handle_schema_event_post_epoch(thd,
+ &post_epoch_log_list,
+ &post_epoch_unlock_list);
+ free_root(&mem_root, MYF(0));
+ *root_ptr= old_root;
+ ndb_latest_handled_binlog_epoch= ndb_latest_received_binlog_epoch;
+ }
+ if (do_ndbcluster_binlog_close_connection == BCCC_restart)
+ {
+ ndb_binlog_tables_inited= FALSE;
+ close_thread_tables(thd);
+ ndb_binlog_index= 0;
+ goto restart;
+ }
+err:
+ sql_print_information("Stopping Cluster Binlog");
+ DBUG_PRINT("info",("Shutting down cluster binlog thread"));
+ thd->proc_info= "Shutting down";
+ close_thread_tables(thd);
+ pthread_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);
+ thd->db= 0; // as not to try to free memory
+
+ if (ndb_apply_status_share)
+ {
+ /* ndb_share reference binlog extra free */
+ DBUG_PRINT("NDB_SHARE", ("%s binlog extra free use_count: %u",
+ ndb_apply_status_share->key,
+ ndb_apply_status_share->use_count));
+ free_share(&ndb_apply_status_share);
+ ndb_apply_status_share= 0;
+ }
+ if (ndb_schema_share)
+ {
+ /* begin protect ndb_schema_share */
+ pthread_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,
+ ndb_schema_share->use_count));
+ free_share(&ndb_schema_share);
+ ndb_schema_share= 0;
+ ndb_binlog_tables_inited= 0;
+ pthread_mutex_unlock(&ndb_schema_share_mutex);
+ /* end protect ndb_schema_share */
+ }
+
+ /* remove all event operations */
+ if (s_ndb)
+ {
+ NdbEventOperation *op;
+ DBUG_PRINT("info",("removing all event operations"));
+ while ((op= s_ndb->getEventOperation()))
+ {
+ DBUG_ASSERT(! IS_NDB_BLOB_PREFIX(op->getEvent()->getTable()->getName()));
+ DBUG_PRINT("info",("removing event operation on %s",
+ op->getEvent()->getName()));
+ NDB_SHARE *share= (NDB_SHARE*) op->getCustomData();
+ DBUG_ASSERT(share != 0);
+ DBUG_ASSERT(share->op == op ||
+ share->op_old == op);
+ share->op= share->op_old= 0;
+ /* ndb_share reference binlog free */
+ DBUG_PRINT("NDB_SHARE", ("%s binlog free use_count: %u",
+ share->key, share->use_count));
+ free_share(&share);
+ s_ndb->dropEventOperation(op);
+ }
+ delete s_ndb;
+ s_ndb= 0;
+ }
+ if (i_ndb)
+ {
+ NdbEventOperation *op;
+ DBUG_PRINT("info",("removing all event operations"));
+ while ((op= i_ndb->getEventOperation()))
+ {
+ DBUG_ASSERT(! IS_NDB_BLOB_PREFIX(op->getEvent()->getTable()->getName()));
+ DBUG_PRINT("info",("removing event operation on %s",
+ op->getEvent()->getName()));
+ NDB_SHARE *share= (NDB_SHARE*) op->getCustomData();
+ DBUG_ASSERT(share != 0);
+ DBUG_ASSERT(share->op == op ||
+ share->op_old == op);
+ share->op= share->op_old= 0;
+ /* ndb_share reference binlog free */
+ DBUG_PRINT("NDB_SHARE", ("%s binlog free use_count: %u",
+ share->key, share->use_count));
+ free_share(&share);
+ i_ndb->dropEventOperation(op);
+ }
+ delete i_ndb;
+ i_ndb= 0;
+ }
+
+ hash_free(&ndb_schema_objects);
+
+ net_end(&thd->net);
+ thd->cleanup();
+ delete thd;
+
+ ndb_binlog_thread_running= -1;
+ ndb_binlog_running= FALSE;
+ (void) pthread_cond_signal(&injector_cond);
+
+ DBUG_PRINT("exit", ("ndb_binlog_thread"));
+ my_thread_end();
+
+ pthread_exit(0);
+ DBUG_RETURN(NULL);
+}
+
+bool
+ndbcluster_show_status_binlog(THD* thd, stat_print_fn *stat_print,
+ enum ha_stat_type stat_type)
+{
+ char buf[IO_SIZE];
+ uint buflen;
+ ulonglong ndb_latest_epoch= 0;
+ DBUG_ENTER("ndbcluster_show_status_binlog");
+
+ pthread_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);
+
+ buflen=
+ snprintf(buf, sizeof(buf),
+ "latest_epoch=%s, "
+ "latest_trans_epoch=%s, "
+ "latest_received_binlog_epoch=%s, "
+ "latest_handled_binlog_epoch=%s, "
+ "latest_applied_binlog_epoch=%s",
+ llstr(ndb_latest_epoch, buff1),
+ llstr(*p_latest_trans_gci, buff2),
+ llstr(ndb_latest_received_binlog_epoch, buff3),
+ llstr(ndb_latest_handled_binlog_epoch, buff4),
+ llstr(ndb_latest_applied_binlog_epoch, buff5));
+ if (stat_print(thd, ndbcluster_hton_name, ndbcluster_hton_name_length,
+ "binlog", strlen("binlog"),
+ buf, buflen))
+ DBUG_RETURN(TRUE);
+ }
+ else
+ pthread_mutex_unlock(&injector_mutex);
+ DBUG_RETURN(FALSE);
+}
+
+#endif /* HAVE_NDB_BINLOG */
+#endif
diff --git a/sql/ha_ndbcluster_binlog.h b/sql/ha_ndbcluster_binlog.h
new file mode 100644
index 00000000000..1cad643e5ec
--- /dev/null
+++ b/sql/ha_ndbcluster_binlog.h
@@ -0,0 +1,227 @@
+/* Copyright (C) 2000-2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+// Typedefs for long names
+typedef NdbDictionary::Object NDBOBJ;
+typedef NdbDictionary::Column NDBCOL;
+typedef NdbDictionary::Table NDBTAB;
+typedef NdbDictionary::Index NDBINDEX;
+typedef NdbDictionary::Dictionary NDBDICT;
+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
+
+/* server id's with high bit set is reservered */
+#define NDB_ANYVALUE_FOR_NOLOGGING 0xFFFFFFFF
+#define NDB_ANYVALUE_RESERVED 0x80000000
+
+extern handlerton *ndbcluster_hton;
+
+/*
+ The numbers below must not change as they
+ are passed between mysql servers, and if changed
+ would break compatablility. Add new numbers to
+ the end.
+*/
+enum SCHEMA_OP_TYPE
+{
+ SOT_DROP_TABLE= 0,
+ SOT_CREATE_TABLE= 1,
+ SOT_RENAME_TABLE_NEW= 2,
+ SOT_ALTER_TABLE= 3,
+ SOT_DROP_DB= 4,
+ SOT_CREATE_DB= 5,
+ SOT_ALTER_DB= 6,
+ SOT_CLEAR_SLOCK= 7,
+ SOT_TABLESPACE= 8,
+ SOT_LOGFILE_GROUP= 9,
+ SOT_RENAME_TABLE= 10,
+ SOT_TRUNCATE_TABLE= 11
+};
+
+const uint max_ndb_nodes= 64; /* multiple of 32 */
+
+static const char *ha_ndb_ext=".ndb";
+static const char share_prefix[]= "./";
+
+class Ndb_table_guard
+{
+public:
+ Ndb_table_guard(NDBDICT *dict, const char *tabname)
+ : m_dict(dict)
+ {
+ DBUG_ENTER("Ndb_table_guard");
+ m_ndbtab= m_dict->getTableGlobal(tabname);
+ m_invalidate= 0;
+ DBUG_PRINT("info", ("m_ndbtab: %p", m_ndbtab));
+ DBUG_VOID_RETURN;
+ }
+ ~Ndb_table_guard()
+ {
+ DBUG_ENTER("~Ndb_table_guard");
+ if (m_ndbtab)
+ {
+ DBUG_PRINT("info", ("m_ndbtab: %p m_invalidate: %d",
+ m_ndbtab, m_invalidate));
+ m_dict->removeTableGlobal(*m_ndbtab, m_invalidate);
+ }
+ DBUG_VOID_RETURN;
+ }
+ const NDBTAB *get_table() { return m_ndbtab; }
+ void invalidate() { m_invalidate= 1; }
+ const NDBTAB *release()
+ {
+ DBUG_ENTER("Ndb_table_guard::release");
+ const NDBTAB *tmp= m_ndbtab;
+ DBUG_PRINT("info", ("m_ndbtab: %p", m_ndbtab));
+ m_ndbtab = 0;
+ DBUG_RETURN(tmp);
+ }
+private:
+ const NDBTAB *m_ndbtab;
+ NDBDICT *m_dict;
+ int m_invalidate;
+};
+
+#ifdef HAVE_NDB_BINLOG
+extern pthread_t ndb_binlog_thread;
+extern pthread_mutex_t injector_mutex;
+extern pthread_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 int ndbcluster_util_inited;
+extern pthread_mutex_t ndbcluster_mutex;
+extern HASH ndbcluster_open_tables;
+extern Ndb_cluster_connection* g_ndb_cluster_connection;
+extern long ndb_number_of_storage_nodes;
+
+/*
+ Initialize the binlog part of the ndb handlerton
+*/
+void ndbcluster_binlog_init_handlerton();
+/*
+ Initialize the binlog part of the NDB_SHARE
+*/
+int ndbcluster_binlog_init_share(NDB_SHARE *share, TABLE *table);
+
+bool ndbcluster_check_if_local_table(const char *dbname, const char *tabname);
+bool ndbcluster_check_if_local_tables_in_db(THD *thd, const char *dbname);
+
+int ndbcluster_create_binlog_setup(Ndb *ndb, const char *key,
+ uint key_len,
+ const char *db,
+ const char *table_name,
+ my_bool share_may_exist);
+int ndbcluster_create_event(Ndb *ndb, const NDBTAB *table,
+ const char *event_name, NDB_SHARE *share,
+ int push_warning= 0);
+int ndbcluster_create_event_ops(NDB_SHARE *share,
+ const NDBTAB *ndbtab,
+ const char *event_name);
+int ndbcluster_log_schema_op(THD *thd, NDB_SHARE *share,
+ const char *query, int query_length,
+ const char *db, const char *table_name,
+ 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);
+int ndbcluster_handle_drop_table(Ndb *ndb, const char *event_name,
+ NDB_SHARE *share,
+ const char *type_str);
+void ndb_rep_event_name(String *event_name,
+ const char *db, const char *tbl);
+int ndb_create_table_from_engine(THD *thd, const char *db,
+ const char *table_name);
+int ndbcluster_binlog_start();
+pthread_handler_t ndb_binlog_thread_func(void *arg);
+
+/*
+ table mysql.ndb_apply_status
+*/
+int ndbcluster_setup_binlog_table_shares(THD *thd);
+extern NDB_SHARE *ndb_apply_status_share;
+extern NDB_SHARE *ndb_schema_share;
+
+extern THD *injector_thd;
+extern my_bool ndb_binlog_running;
+extern my_bool ndb_binlog_tables_inited;
+
+bool
+ndbcluster_show_status_binlog(THD* thd, stat_print_fn *stat_print,
+ enum ha_stat_type stat_type);
+
+/*
+ prototypes for ndb handler utility function also needed by
+ the ndb binlog code
+*/
+int cmp_frm(const NDBTAB *ndbtab, const void *pack_data,
+ uint pack_length);
+int ndbcluster_find_all_files(THD *thd);
+#endif /* HAVE_NDB_BINLOG */
+
+void ndb_unpack_record(TABLE *table, NdbValue *value,
+ MY_BITMAP *defined, uchar *buf);
+char *ndb_pack_varchar(const NDBCOL *col, char *buf,
+ const char *str, int sz);
+
+NDB_SHARE *ndbcluster_get_share(const char *key,
+ TABLE *table,
+ bool create_if_not_exists,
+ bool have_lock);
+NDB_SHARE *ndbcluster_get_share(NDB_SHARE *share);
+void ndbcluster_free_share(NDB_SHARE **share, bool have_lock);
+void ndbcluster_real_free_share(NDB_SHARE **share);
+int handle_trailing_share(NDB_SHARE *share);
+inline NDB_SHARE *get_share(const char *key,
+ TABLE *table,
+ bool create_if_not_exists= TRUE,
+ bool have_lock= FALSE)
+{
+ return ndbcluster_get_share(key, table, create_if_not_exists, have_lock);
+}
+
+inline NDB_SHARE *get_share(NDB_SHARE *share)
+{
+ return ndbcluster_get_share(share);
+}
+
+inline void free_share(NDB_SHARE **share, bool have_lock= FALSE)
+{
+ ndbcluster_free_share(share, have_lock);
+}
+
+inline
+Thd_ndb *
+get_thd_ndb(THD *thd)
+{ return (Thd_ndb *) thd_get_ha_data(thd, ndbcluster_hton); }
+
+inline
+void
+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);
diff --git a/sql/ha_ndbcluster_cond.cc b/sql/ha_ndbcluster_cond.cc
index 37e710acff4..bb35211944b 100644
--- a/sql/ha_ndbcluster_cond.cc
+++ b/sql/ha_ndbcluster_cond.cc
@@ -24,7 +24,7 @@
#include "mysql_priv.h"
-#ifdef HAVE_NDBCLUSTER_DB
+#ifdef WITH_NDBCLUSTER_STORAGE_ENGINE
#include <ndbapi/NdbApi.hpp>
#include "ha_ndbcluster_cond.h"
@@ -360,8 +360,8 @@ void ndb_serialize_cond(const Item *item, void *arg)
else
{
DBUG_PRINT("info", ("Was not expecting field from table %s (%s)",
- context->table->s->table_name,
- field->table->s->table_name));
+ context->table->s->table_name.str,
+ field->table->s->table_name.str));
context->supported= FALSE;
}
break;
@@ -1402,9 +1402,9 @@ ha_ndbcluster_cond::generate_scan_filter_from_cond(NdbScanFilter& filter)
int ha_ndbcluster_cond::generate_scan_filter_from_key(NdbScanOperation *op,
const KEY* key_info,
- const byte *key,
+ const uchar *key,
uint key_len,
- byte *buf)
+ uchar *buf)
{
KEY_PART_INFO* key_part= key_info->key_part;
KEY_PART_INFO* end= key_part+key_info->key_parts;
@@ -1417,9 +1417,9 @@ int ha_ndbcluster_cond::generate_scan_filter_from_key(NdbScanOperation *op,
{
Field* field= key_part->field;
uint32 pack_len= field->pack_length();
- const byte* ptr= key;
+ const uchar* ptr= key;
DBUG_PRINT("info", ("Filtering value for %s", field->field_name));
- DBUG_DUMP("key", (uchar*)ptr, pack_len);
+ DBUG_DUMP("key", ptr, pack_len);
if (key_part->null_bit)
{
DBUG_PRINT("info", ("Generating ISNULL filter"));
diff --git a/sql/ha_ndbcluster_cond.h b/sql/ha_ndbcluster_cond.h
index 6504df8d9d4..4401a93c9e1 100644
--- a/sql/ha_ndbcluster_cond.h
+++ b/sql/ha_ndbcluster_cond.h
@@ -198,7 +198,7 @@ public:
return value.item->str_value.ptr();
break;
case(NDB_FIELD):
- return value.field_value->field->ptr;
+ return (char*) value.field_value->field->ptr;
default:
break;
}
@@ -212,7 +212,12 @@ public:
const Item *item= value.item;
if (item && field)
- ((Item *)item)->save_in_field(field, false);
+ {
+ my_bitmap_map *old_map=
+ dbug_tmp_use_all_columns(field->table, field->table->write_set);
+ ((Item *)item)->save_in_field(field, FALSE);
+ dbug_tmp_restore_column_map(field->table->write_set, old_map);
+ }
};
static NDB_FUNC_TYPE item_func_to_ndb_func(Item_func::Functype fun)
@@ -466,9 +471,9 @@ public:
int generate_scan_filter_from_cond(NdbScanFilter& filter);
int generate_scan_filter_from_key(NdbScanOperation* op,
const KEY* key_info,
- const byte *key,
+ const uchar *key,
uint key_len,
- byte *buf);
+ uchar *buf);
private:
bool serialize_cond(const COND *cond, Ndb_cond_stack *ndb_cond,
TABLE *table, const NdbDictionary::Table *ndb_table);
diff --git a/sql/ha_ndbcluster_tables.h b/sql/ha_ndbcluster_tables.h
new file mode 100644
index 00000000000..c6bc8f577f8
--- /dev/null
+++ b/sql/ha_ndbcluster_tables.h
@@ -0,0 +1,23 @@
+/* Copyright (C) 2000-2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#define NDB_REP_DB "mysql"
+#define OLD_NDB_REP_DB "cluster"
+#define NDB_REP_TABLE "ndb_binlog_index"
+#define NDB_APPLY_TABLE "ndb_apply_status"
+#define OLD_NDB_APPLY_TABLE "apply_status"
+#define NDB_SCHEMA_TABLE "ndb_schema"
+#define OLD_NDB_SCHEMA_TABLE "schema"
diff --git a/sql/ha_partition.cc b/sql/ha_partition.cc
new file mode 100644
index 00000000000..0e36a868b3e
--- /dev/null
+++ b/sql/ha_partition.cc
@@ -0,0 +1,6468 @@
+/* Copyright 2005-2008 MySQL AB, 2008 Sun Microsystems, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+/*
+ This handler was developed by Mikael Ronstrom for version 5.1 of MySQL.
+ It is an abstraction layer on top of other handlers such as MyISAM,
+ InnoDB, Federated, Berkeley DB and so forth. Partitioned tables can also
+ be handled by a storage engine. The current example of this is NDB
+ Cluster that has internally handled partitioning. This have benefits in
+ that many loops needed in the partition handler can be avoided.
+
+ Partitioning has an inherent feature which in some cases is positive and
+ in some cases is negative. It splits the data into chunks. This makes
+ the data more manageable, queries can easily be parallelised towards the
+ parts and indexes are split such that there are less levels in the
+ index trees. The inherent disadvantage is that to use a split index
+ one has to scan all index parts which is ok for large queries but for
+ small queries it can be a disadvantage.
+
+ Partitioning lays the foundation for more manageable databases that are
+ extremely large. It does also lay the foundation for more parallelism
+ in the execution of queries. This functionality will grow with later
+ versions of MySQL.
+
+ You can enable it in your buld by doing the following during your build
+ process:
+ ./configure --with-partition
+
+ The partition is setup to use table locks. It implements an partition "SHARE"
+ that is inserted into a hash by table name. You can use this to store
+ information of state that any partition handler object will be able to see
+ if it is using the same table.
+
+ Please read the object definition in ha_partition.h before reading the rest
+ if this file.
+*/
+
+#ifdef __GNUC__
+#pragma implementation // gcc: Class implementation
+#endif
+
+#include "mysql_priv.h"
+
+#ifdef WITH_PARTITION_STORAGE_ENGINE
+#include "ha_partition.h"
+
+#include <mysql/plugin.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
+
+/****************************************************************************
+ MODULE create/delete handler object
+****************************************************************************/
+
+static handler *partition_create_handler(handlerton *hton,
+ TABLE_SHARE *share,
+ MEM_ROOT *mem_root);
+static uint partition_flags();
+static uint alter_table_flags(uint flags);
+
+
+static int partition_initialize(void *p)
+{
+
+ handlerton *partition_hton;
+ partition_hton= (handlerton *)p;
+
+ partition_hton->state= SHOW_OPTION_YES;
+ partition_hton->db_type= DB_TYPE_PARTITION_DB;
+ partition_hton->create= partition_create_handler;
+ partition_hton->partition_flags= partition_flags;
+ partition_hton->alter_table_flags= alter_table_flags;
+ partition_hton->flags= HTON_NOT_USER_SELECTABLE | HTON_HIDDEN;
+
+ return 0;
+}
+
+/*
+ Create new partition handler
+
+ SYNOPSIS
+ partition_create_handler()
+ table Table object
+
+ RETURN VALUE
+ New partition object
+*/
+
+static handler *partition_create_handler(handlerton *hton,
+ TABLE_SHARE *share,
+ MEM_ROOT *mem_root)
+{
+ ha_partition *file= new (mem_root) ha_partition(hton, share);
+ if (file && file->initialize_partition(mem_root))
+ {
+ delete file;
+ file= 0;
+ }
+ return file;
+}
+
+/*
+ HA_CAN_PARTITION:
+ Used by storage engines that can handle partitioning without this
+ partition handler
+ (Partition, NDB)
+
+ HA_CAN_UPDATE_PARTITION_KEY:
+ Set if the handler can update fields that are part of the partition
+ function.
+
+ HA_CAN_PARTITION_UNIQUE:
+ Set if the handler can handle unique indexes where the fields of the
+ unique key are not part of the fields of the partition function. Thus
+ a unique key can be set on all fields.
+
+ HA_USE_AUTO_PARTITION
+ Set if the handler sets all tables to be partitioned by default.
+*/
+
+static uint partition_flags()
+{
+ return HA_CAN_PARTITION;
+}
+
+static uint alter_table_flags(uint flags __attribute__((unused)))
+{
+ return (HA_PARTITION_FUNCTION_SUPPORTED |
+ HA_FAST_CHANGE_PARTITION);
+}
+
+const uint ha_partition::NO_CURRENT_PART_ID= 0xFFFFFFFF;
+
+/*
+ Constructor method
+
+ SYNOPSIS
+ ha_partition()
+ table Table object
+
+ RETURN VALUE
+ NONE
+*/
+
+ha_partition::ha_partition(handlerton *hton, TABLE_SHARE *share)
+ :handler(hton, share), m_part_info(NULL), m_create_handler(FALSE),
+ m_is_sub_partitioned(0)
+{
+ DBUG_ENTER("ha_partition::ha_partition(table)");
+ init_handler_variables();
+ DBUG_VOID_RETURN;
+}
+
+
+/*
+ Constructor method
+
+ SYNOPSIS
+ ha_partition()
+ part_info Partition info
+
+ RETURN VALUE
+ NONE
+*/
+
+ha_partition::ha_partition(handlerton *hton, partition_info *part_info)
+ :handler(hton, NULL), m_part_info(part_info), m_create_handler(TRUE),
+ m_is_sub_partitioned(m_part_info->is_sub_partitioned())
+{
+ DBUG_ENTER("ha_partition::ha_partition(part_info)");
+ init_handler_variables();
+ DBUG_ASSERT(m_part_info);
+ DBUG_VOID_RETURN;
+}
+
+
+/*
+ Initialize handler object
+
+ SYNOPSIS
+ init_handler_variables()
+
+ RETURN VALUE
+ NONE
+*/
+
+void ha_partition::init_handler_variables()
+{
+ active_index= MAX_KEY;
+ m_mode= 0;
+ m_open_test_lock= 0;
+ m_file_buffer= NULL;
+ m_name_buffer_ptr= NULL;
+ m_engine_array= NULL;
+ m_file= NULL;
+ m_file_tot_parts= 0;
+ m_reorged_file= NULL;
+ m_new_file= NULL;
+ m_reorged_parts= 0;
+ m_added_file= NULL;
+ m_tot_parts= 0;
+ m_pkey_is_clustered= 0;
+ m_lock_type= F_UNLCK;
+ m_part_spec.start_part= NO_CURRENT_PART_ID;
+ m_scan_value= 2;
+ m_ref_length= 0;
+ m_part_spec.end_part= NO_CURRENT_PART_ID;
+ m_index_scan_type= partition_no_index_scan;
+ m_start_key.key= NULL;
+ m_start_key.length= 0;
+ m_myisam= FALSE;
+ m_innodb= FALSE;
+ m_extra_cache= FALSE;
+ m_extra_cache_size= 0;
+ m_handler_status= handler_not_initialized;
+ m_low_byte_first= 1;
+ m_part_field_array= NULL;
+ m_ordered_rec_buffer= NULL;
+ m_top_entry= NO_CURRENT_PART_ID;
+ m_rec_length= 0;
+ m_last_part= 0;
+ m_rec0= 0;
+ m_curr_key_info[0]= NULL;
+ m_curr_key_info[1]= NULL;
+ is_clone= FALSE,
+ auto_increment_lock= FALSE;
+ auto_increment_safe_stmt_log_lock= FALSE;
+ /*
+ this allows blackhole to work properly
+ */
+ m_no_locks= 0;
+
+#ifdef DONT_HAVE_TO_BE_INITALIZED
+ m_start_key.flag= 0;
+ m_ordered= TRUE;
+#endif
+}
+
+
+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();
+}
+
+
+/*
+ Destructor method
+
+ SYNOPSIS
+ ~ha_partition()
+
+ RETURN VALUE
+ NONE
+*/
+
+ha_partition::~ha_partition()
+{
+ DBUG_ENTER("ha_partition::~ha_partition()");
+ if (m_file != NULL)
+ {
+ uint i;
+ for (i= 0; i < m_tot_parts; i++)
+ delete m_file[i];
+ }
+ my_free((char*) m_ordered_rec_buffer, MYF(MY_ALLOW_ZERO_PTR));
+
+ clear_handler_file();
+ DBUG_VOID_RETURN;
+}
+
+
+/*
+ Initialize partition handler object
+
+ SYNOPSIS
+ initialize_partition()
+ mem_root Allocate memory through this
+
+ RETURN VALUE
+ 1 Error
+ 0 Success
+
+ DESCRIPTION
+
+ The partition handler is only a layer on top of other engines. Thus it
+ can't really perform anything without the underlying handlers. Thus we
+ add this method as part of the allocation of a handler object.
+
+ 1) Allocation of underlying handlers
+ If we have access to the partition info we will allocate one handler
+ instance for each partition.
+ 2) Allocation without partition info
+ The cases where we don't have access to this information is when called
+ in preparation for delete_table and rename_table and in that case we
+ only need to set HA_FILE_BASED. In that case we will use the .par file
+ that contains information about the partitions and their engines and
+ the names of each partition.
+ 3) Table flags initialisation
+ We need also to set table flags for the partition handler. This is not
+ static since it depends on what storage engines are used as underlying
+ handlers.
+ The table flags is set in this routine to simulate the behaviour of a
+ normal storage engine
+ The flag HA_FILE_BASED will be set independent of the underlying handlers
+ 4) Index flags initialisation
+ When knowledge exists on the indexes it is also possible to initialize the
+ index flags. Again the index flags must be initialized by using the under-
+ lying handlers since this is storage engine dependent.
+ 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
+ calculated here.
+
+*/
+
+bool ha_partition::initialize_partition(MEM_ROOT *mem_root)
+{
+ handler **file_array, *file;
+ ulonglong check_table_flags;
+ DBUG_ENTER("ha_partition::initialize_partition");
+
+ if (m_create_handler)
+ {
+ m_tot_parts= m_part_info->get_tot_partitions();
+ DBUG_ASSERT(m_tot_parts > 0);
+ if (new_handlers_from_part_info(mem_root))
+ DBUG_RETURN(1);
+ }
+ else if (!table_share || !table_share->normalized_path.str)
+ {
+ /*
+ Called with dummy table share (delete, rename and alter table).
+ Don't need to set-up anything.
+ */
+ DBUG_RETURN(0);
+ }
+ else if (get_from_handler_file(table_share->normalized_path.str, mem_root))
+ {
+ mem_alloc_error(2);
+ 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
+ has_transactions since they are called often in all kinds of places,
+ other parameters are calculated on demand.
+ Verify that all partitions have the same table_flags.
+ */
+ check_table_flags= m_file[0]->ha_table_flags();
+ m_low_byte_first= m_file[0]->low_byte_first();
+ m_pkey_is_clustered= TRUE;
+ file_array= m_file;
+ do
+ {
+ file= *file_array;
+ if (m_low_byte_first != file->low_byte_first())
+ {
+ // Cannot have handlers with different endian
+ my_error(ER_MIX_HANDLER_ERROR, MYF(0));
+ DBUG_RETURN(1);
+ }
+ if (!file->primary_key_is_clustered())
+ m_pkey_is_clustered= FALSE;
+ if (check_table_flags != file->ha_table_flags())
+ {
+ my_error(ER_MIX_HANDLER_ERROR, MYF(0));
+ DBUG_RETURN(1);
+ }
+ } while (*(++file_array));
+ m_handler_status= handler_initialized;
+ DBUG_RETURN(0);
+}
+
+/****************************************************************************
+ MODULE meta data changes
+****************************************************************************/
+/*
+ Delete a table
+
+ SYNOPSIS
+ delete_table()
+ name Full path of table name
+
+ RETURN VALUE
+ >0 Error
+ 0 Success
+
+ DESCRIPTION
+ Used to delete a table. By the time delete_table() has been called all
+ opened references to this table will have been closed (and your globally
+ shared references released. The variable name will just be the name of
+ the table. You will need to remove any files you have created at this
+ point.
+
+ If you do not implement this, the default delete_table() is called from
+ handler.cc and it will delete all files with the file extentions returned
+ by bas_ext().
+
+ Called from handler.cc by delete_table and ha_create_table(). Only used
+ during create if the table_flag HA_DROP_BEFORE_CREATE was specified for
+ the storage engine.
+*/
+
+int ha_partition::delete_table(const char *name)
+{
+ int error;
+ DBUG_ENTER("ha_partition::delete_table");
+
+ if ((error= del_ren_cre_table(name, NULL, NULL, NULL)))
+ DBUG_RETURN(error);
+ DBUG_RETURN(handler::delete_table(name));
+}
+
+
+/*
+ Rename a table
+
+ SYNOPSIS
+ rename_table()
+ from Full path of old table name
+ to Full path of new table name
+
+ RETURN VALUE
+ >0 Error
+ 0 Success
+
+ DESCRIPTION
+ Renames a table from one name to another from alter table call.
+
+ If you do not implement this, the default rename_table() is called from
+ handler.cc and it will rename all files with the file extentions returned
+ by bas_ext().
+
+ Called from sql_table.cc by mysql_rename_table().
+*/
+
+int ha_partition::rename_table(const char *from, const char *to)
+{
+ int error;
+ DBUG_ENTER("ha_partition::rename_table");
+
+ if ((error= del_ren_cre_table(from, to, NULL, NULL)))
+ DBUG_RETURN(error);
+ DBUG_RETURN(handler::rename_table(from, to));
+}
+
+
+/*
+ Create the handler file (.par-file)
+
+ SYNOPSIS
+ create_handler_files()
+ name Full path of table name
+ create_info Create info generated for CREATE TABLE
+
+ RETURN VALUE
+ >0 Error
+ 0 Success
+
+ DESCRIPTION
+ create_handler_files is called to create any handler specific files
+ before opening the file with openfrm to later call ::create on the
+ file object.
+ In the partition handler this is used to store the names of partitions
+ and types of engines in the partitions.
+*/
+
+int ha_partition::create_handler_files(const char *path,
+ const char *old_path,
+ int action_flag,
+ HA_CREATE_INFO *create_info)
+{
+ DBUG_ENTER("ha_partition::create_handler_files()");
+
+ /*
+ We need to update total number of parts since we might write the handler
+ file as part of a partition management command
+ */
+ if (action_flag == CHF_DELETE_FLAG ||
+ action_flag == CHF_RENAME_FLAG)
+ {
+ char name[FN_REFLEN];
+ char old_name[FN_REFLEN];
+
+ 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))) ||
+ (action_flag == CHF_RENAME_FLAG &&
+ my_rename(old_name, name, MYF(MY_WME))))
+ {
+ DBUG_RETURN(TRUE);
+ }
+ }
+ else if (action_flag == CHF_CREATE_FLAG)
+ {
+ if (create_handler_file(path))
+ {
+ my_error(ER_CANT_CREATE_HANDLER_FILE, MYF(0));
+ DBUG_RETURN(1);
+ }
+ }
+ DBUG_RETURN(0);
+}
+
+
+/*
+ Create a partitioned table
+
+ SYNOPSIS
+ create()
+ name Full path of table name
+ table_arg Table object
+ create_info Create info generated for CREATE TABLE
+
+ RETURN VALUE
+ >0 Error
+ 0 Success
+
+ DESCRIPTION
+ create() is called to create a table. The variable name will have the name
+ of the table. When create() is called you do not need to worry about
+ opening the table. Also, the FRM file will have already been created so
+ adjusting create_info will not do you any good. You can overwrite the frm
+ file at this point if you wish to change the table definition, but there
+ are no methods currently provided for doing that.
+
+ Called from handler.cc by ha_create_table().
+*/
+
+int ha_partition::create(const char *name, TABLE *table_arg,
+ HA_CREATE_INFO *create_info)
+{
+ char t_name[FN_REFLEN];
+ DBUG_ENTER("ha_partition::create");
+
+ strmov(t_name, name);
+ DBUG_ASSERT(*fn_rext((char*)name) == '\0');
+ if (del_ren_cre_table(t_name, NULL, table_arg, create_info))
+ {
+ handler::delete_table(t_name);
+ DBUG_RETURN(1);
+ }
+ DBUG_RETURN(0);
+}
+
+
+/*
+ Drop partitions as part of ALTER TABLE of partitions
+
+ SYNOPSIS
+ drop_partitions()
+ path Complete path of db and table name
+
+ RETURN VALUE
+ >0 Failure
+ 0 Success
+
+ DESCRIPTION
+ Use part_info object on handler object to deduce which partitions to
+ drop (each partition has a state attached to it)
+*/
+
+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 i= 0;
+ uint name_variant;
+ int ret_error;
+ int error= 0;
+ DBUG_ENTER("ha_partition::drop_partitions");
+
+ /*
+ Assert that it works without HA_FILE_BASED and lower_case_table_name = 2.
+ We use m_file[0] as long as all partitions have the same storage engine.
+ */
+ DBUG_ASSERT(!strcmp(path, get_canonical_filename(m_file[0], path,
+ part_name_buff)));
+ do
+ {
+ partition_element *part_elem= part_it++;
+ if (part_elem->part_state == PART_TO_BE_DROPPED)
+ {
+ handler *file;
+ /*
+ This part is to be dropped, meaning the part or all its subparts.
+ */
+ name_variant= NORMAL_PART_NAME;
+ if (m_is_sub_partitioned)
+ {
+ List_iterator<partition_element> sub_it(part_elem->subpartitions);
+ uint j= 0, part;
+ do
+ {
+ partition_element *sub_elem= sub_it++;
+ part= i * no_subparts + j;
+ create_subpartition_name(part_name_buff, path,
+ part_elem->partition_name,
+ sub_elem->partition_name, name_variant);
+ file= m_file[part];
+ DBUG_PRINT("info", ("Drop subpartition %s", part_name_buff));
+ if ((ret_error= file->ha_delete_table(part_name_buff)))
+ error= ret_error;
+ if (deactivate_ddl_log_entry(sub_elem->log_entry->entry_pos))
+ error= 1;
+ } while (++j < no_subparts);
+ }
+ else
+ {
+ create_partition_name(part_name_buff, path,
+ part_elem->partition_name, name_variant,
+ TRUE);
+ file= m_file[i];
+ DBUG_PRINT("info", ("Drop partition %s", part_name_buff));
+ if ((ret_error= file->ha_delete_table(part_name_buff)))
+ error= ret_error;
+ if (deactivate_ddl_log_entry(part_elem->log_entry->entry_pos))
+ error= 1;
+ }
+ if (part_elem->part_state == PART_IS_CHANGED)
+ part_elem->part_state= PART_NORMAL;
+ else
+ part_elem->part_state= PART_IS_DROPPED;
+ }
+ } while (++i < no_parts);
+ VOID(sync_ddl_log());
+ DBUG_RETURN(error);
+}
+
+
+/*
+ Rename partitions as part of ALTER TABLE of partitions
+
+ SYNOPSIS
+ rename_partitions()
+ path Complete path of db and table name
+
+ RETURN VALUE
+ TRUE Failure
+ FALSE Success
+
+ DESCRIPTION
+ When reorganising partitions, adding hash partitions and coalescing
+ partitions it can be necessary to rename partitions while holding
+ an exclusive lock on the table.
+ Which partitions to rename is given by state of partitions found by the
+ partition info struct referenced from the handler object
+*/
+
+int ha_partition::rename_partitions(const char *path)
+{
+ List_iterator<partition_element> part_it(m_part_info->partitions);
+ 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 part_count= 0;
+ uint no_subparts= m_part_info->no_subparts;
+ uint i= 0;
+ uint j= 0;
+ int error= 0;
+ int ret_error;
+ uint temp_partitions= m_part_info->temp_partitions.elements;
+ handler *file;
+ partition_element *part_elem, *sub_elem;
+ DBUG_ENTER("ha_partition::rename_partitions");
+
+ /*
+ Assert that it works without HA_FILE_BASED and lower_case_table_name = 2.
+ We use m_file[0] as long as all partitions have the same storage engine.
+ */
+ DBUG_ASSERT(!strcmp(path, get_canonical_filename(m_file[0], path,
+ norm_name_buff)));
+
+ if (temp_partitions)
+ {
+ /*
+ These are the reorganised partitions that have already been copied.
+ We delete the partitions and log the delete by inactivating the
+ delete log entry in the table log. We only need to synchronise
+ these writes before moving to the next loop since there is no
+ interaction among reorganised partitions, they cannot have the
+ same name.
+ */
+ do
+ {
+ part_elem= temp_it++;
+ if (m_is_sub_partitioned)
+ {
+ List_iterator<partition_element> sub_it(part_elem->subpartitions);
+ do
+ {
+ sub_elem= sub_it++;
+ file= m_reorged_file[part_count++];
+ create_subpartition_name(norm_name_buff, path,
+ part_elem->partition_name,
+ sub_elem->partition_name,
+ NORMAL_PART_NAME);
+ DBUG_PRINT("info", ("Delete subpartition %s", norm_name_buff));
+ if ((ret_error= file->ha_delete_table(norm_name_buff)))
+ error= ret_error;
+ else if (deactivate_ddl_log_entry(sub_elem->log_entry->entry_pos))
+ error= 1;
+ else
+ sub_elem->log_entry= NULL; /* Indicate success */
+ } while (++j < no_subparts);
+ }
+ else
+ {
+ file= m_reorged_file[part_count++];
+ create_partition_name(norm_name_buff, path,
+ part_elem->partition_name, NORMAL_PART_NAME,
+ TRUE);
+ DBUG_PRINT("info", ("Delete partition %s", norm_name_buff));
+ if ((ret_error= file->ha_delete_table(norm_name_buff)))
+ error= ret_error;
+ else if (deactivate_ddl_log_entry(part_elem->log_entry->entry_pos))
+ error= 1;
+ else
+ part_elem->log_entry= NULL; /* Indicate success */
+ }
+ } while (++i < temp_partitions);
+ VOID(sync_ddl_log());
+ }
+ i= 0;
+ do
+ {
+ /*
+ When state is PART_IS_CHANGED it means that we have created a new
+ TEMP partition that is to be renamed to normal partition name and
+ we are to delete the old partition with currently the normal name.
+
+ We perform this operation by
+ 1) Delete old partition with normal partition name
+ 2) Signal this in table log entry
+ 3) Synch table log to ensure we have consistency in crashes
+ 4) Rename temporary partition name to normal partition name
+ 5) Signal this to table log entry
+ It is not necessary to synch the last state since a new rename
+ should not corrupt things if there was no temporary partition.
+
+ The only other parts we need to cater for are new parts that
+ replace reorganised parts. The reorganised parts were deleted
+ by the code above that goes through the temp_partitions list.
+ Thus the synch above makes it safe to simply perform step 4 and 5
+ for those entries.
+ */
+ part_elem= part_it++;
+ if (part_elem->part_state == PART_IS_CHANGED ||
+ part_elem->part_state == PART_TO_BE_DROPPED ||
+ (part_elem->part_state == PART_IS_ADDED && temp_partitions))
+ {
+ if (m_is_sub_partitioned)
+ {
+ List_iterator<partition_element> sub_it(part_elem->subpartitions);
+ uint part;
+
+ j= 0;
+ do
+ {
+ sub_elem= sub_it++;
+ part= i * no_subparts + j;
+ create_subpartition_name(norm_name_buff, path,
+ part_elem->partition_name,
+ sub_elem->partition_name,
+ NORMAL_PART_NAME);
+ if (part_elem->part_state == PART_IS_CHANGED)
+ {
+ file= m_reorged_file[part_count++];
+ DBUG_PRINT("info", ("Delete subpartition %s", norm_name_buff));
+ if ((ret_error= file->ha_delete_table(norm_name_buff)))
+ error= ret_error;
+ else if (deactivate_ddl_log_entry(sub_elem->log_entry->entry_pos))
+ error= 1;
+ VOID(sync_ddl_log());
+ }
+ file= m_new_file[part];
+ create_subpartition_name(part_name_buff, path,
+ part_elem->partition_name,
+ sub_elem->partition_name,
+ TEMP_PART_NAME);
+ DBUG_PRINT("info", ("Rename subpartition from %s to %s",
+ part_name_buff, norm_name_buff));
+ if ((ret_error= file->ha_rename_table(part_name_buff,
+ norm_name_buff)))
+ error= ret_error;
+ else if (deactivate_ddl_log_entry(sub_elem->log_entry->entry_pos))
+ error= 1;
+ else
+ sub_elem->log_entry= NULL;
+ } while (++j < no_subparts);
+ }
+ else
+ {
+ create_partition_name(norm_name_buff, path,
+ part_elem->partition_name, NORMAL_PART_NAME,
+ TRUE);
+ if (part_elem->part_state == PART_IS_CHANGED)
+ {
+ file= m_reorged_file[part_count++];
+ DBUG_PRINT("info", ("Delete partition %s", norm_name_buff));
+ if ((ret_error= file->ha_delete_table(norm_name_buff)))
+ error= ret_error;
+ else if (deactivate_ddl_log_entry(part_elem->log_entry->entry_pos))
+ error= 1;
+ VOID(sync_ddl_log());
+ }
+ file= m_new_file[i];
+ create_partition_name(part_name_buff, path,
+ part_elem->partition_name, TEMP_PART_NAME,
+ TRUE);
+ DBUG_PRINT("info", ("Rename partition from %s to %s",
+ part_name_buff, norm_name_buff));
+ if ((ret_error= file->ha_rename_table(part_name_buff,
+ norm_name_buff)))
+ error= ret_error;
+ else if (deactivate_ddl_log_entry(part_elem->log_entry->entry_pos))
+ error= 1;
+ else
+ part_elem->log_entry= NULL;
+ }
+ }
+ } while (++i < no_parts);
+ VOID(sync_ddl_log());
+ DBUG_RETURN(error);
+}
+
+
+#define OPTIMIZE_PARTS 1
+#define ANALYZE_PARTS 2
+#define CHECK_PARTS 3
+#define REPAIR_PARTS 4
+
+static const char *opt_op_name[]= {NULL,
+ "optimize", "analyze", "check", "repair" };
+
+/*
+ Optimize table
+
+ SYNOPSIS
+ optimize()
+ thd Thread object
+ check_opt Check/analyze/repair/optimize options
+
+ RETURN VALUES
+ >0 Error
+ 0 Success
+*/
+
+int ha_partition::optimize(THD *thd, HA_CHECK_OPT *check_opt)
+{
+ DBUG_ENTER("ha_partition::optimize");
+
+ DBUG_RETURN(handle_opt_partitions(thd, check_opt, OPTIMIZE_PARTS));
+}
+
+
+/*
+ Analyze table
+
+ SYNOPSIS
+ analyze()
+ thd Thread object
+ check_opt Check/analyze/repair/optimize options
+
+ RETURN VALUES
+ >0 Error
+ 0 Success
+*/
+
+int ha_partition::analyze(THD *thd, HA_CHECK_OPT *check_opt)
+{
+ DBUG_ENTER("ha_partition::analyze");
+
+ DBUG_RETURN(handle_opt_partitions(thd, check_opt, ANALYZE_PARTS));
+}
+
+
+/*
+ Check table
+
+ SYNOPSIS
+ check()
+ thd Thread object
+ check_opt Check/analyze/repair/optimize options
+
+ RETURN VALUES
+ >0 Error
+ 0 Success
+*/
+
+int ha_partition::check(THD *thd, HA_CHECK_OPT *check_opt)
+{
+ DBUG_ENTER("ha_partition::check");
+
+ DBUG_RETURN(handle_opt_partitions(thd, check_opt, CHECK_PARTS));
+}
+
+
+/*
+ Repair table
+
+ SYNOPSIS
+ repair()
+ thd Thread object
+ check_opt Check/analyze/repair/optimize options
+
+ RETURN VALUES
+ >0 Error
+ 0 Success
+*/
+
+int ha_partition::repair(THD *thd, HA_CHECK_OPT *check_opt)
+{
+ DBUG_ENTER("ha_partition::repair");
+
+ DBUG_RETURN(handle_opt_partitions(thd, check_opt, REPAIR_PARTS));
+}
+
+
+/*
+ Handle optimize/analyze/check/repair of one partition
+
+ SYNOPSIS
+ handle_opt_part()
+ thd Thread object
+ check_opt Options
+ file Handler object of partition
+ flag Optimize/Analyze/Check/Repair flag
+
+ RETURN VALUE
+ >0 Failure
+ 0 Success
+*/
+
+static int handle_opt_part(THD *thd, HA_CHECK_OPT *check_opt,
+ handler *file, uint flag)
+{
+ int error;
+ DBUG_ENTER("handle_opt_part");
+ DBUG_PRINT("enter", ("flag = %u", flag));
+
+ if (flag == OPTIMIZE_PARTS)
+ error= file->ha_optimize(thd, 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);
+ else if (flag == REPAIR_PARTS)
+ error= file->ha_repair(thd, check_opt);
+ else
+ {
+ DBUG_ASSERT(FALSE);
+ error= 1;
+ }
+ if (error == HA_ADMIN_ALREADY_DONE)
+ error= 0;
+ DBUG_RETURN(error);
+}
+
+
+/*
+ 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,
+ const char* op_name, const char *fmt, ...)
+{
+ va_list args;
+ Protocol *protocol= thd->protocol;
+ uint length, msg_length;
+ char msgbuf[MI_MAX_MSG_BUF];
+ char name[NAME_LEN*2+2];
+
+ va_start(args, fmt);
+ msg_length= my_vsnprintf(msgbuf, sizeof(msgbuf), fmt, args);
+ va_end(args);
+ msgbuf[sizeof(msgbuf) - 1] = 0; // healthy paranoia
+
+
+ if (!thd->vio_ok())
+ {
+ sql_print_error(msgbuf);
+ return TRUE;
+ }
+
+ length=(uint) (strxmov(name, db_name, ".", table_name,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:
+ mi_check_print_msg/push_warning/sql_alloc/my_pthread_getspecific_ptr.
+
+ Also we likely need to lock mutex here (in both cases with protocol and
+ push_warning).
+ */
+ DBUG_PRINT("info",("print_admin_msg: %s, %s, %s, %s", name, op_name,
+ msg_type, msgbuf));
+ protocol->prepare_for_resend();
+ protocol->store(name, length, system_charset_info);
+ protocol->store(op_name, system_charset_info);
+ protocol->store(msg_type, system_charset_info);
+ protocol->store(msgbuf, msg_length, system_charset_info);
+ if (protocol->write())
+ {
+ sql_print_error("Failed on my_net_write, writing to stderr instead: %s\n",
+ msgbuf);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+
+/*
+ Handle optimize/analyze/check/repair of partitions
+
+ SYNOPSIS
+ handle_opt_partitions()
+ thd Thread object
+ check_opt Options
+ flag Optimize/Analyze/Check/Repair flag
+
+ RETURN VALUE
+ >0 Failure
+ 0 Success
+*/
+
+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 i= 0;
+ int error;
+ DBUG_ENTER("ha_partition::handle_opt_partitions");
+ DBUG_PRINT("enter", ("flag= %u", flag));
+
+ do
+ {
+ partition_element *part_elem= part_it++;
+ /*
+ when ALTER TABLE <CMD> PARTITION ...
+ it should only do named partitions, otherwise all partitions
+ */
+ if (!(thd->lex->alter_info.flags & ALTER_ADMIN_PARTITION) ||
+ part_elem->part_state == PART_CHANGED)
+ {
+ if (m_is_sub_partitioned)
+ {
+ List_iterator<partition_element> subpart_it(part_elem->subpartitions);
+ partition_element *sub_elem;
+ uint j= 0, part;
+ do
+ {
+ sub_elem= subpart_it++;
+ part= i * no_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)))
+ {
+ /* 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,
+ opt_op_name[flag],
+ "Subpartition %s returned error",
+ sub_elem->partition_name);
+ }
+ DBUG_RETURN(error);
+ }
+ } while (++j < no_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)))
+ {
+ /* 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,
+ opt_op_name[flag], "Partition %s returned error",
+ part_elem->partition_name);
+ }
+ DBUG_RETURN(error);
+ }
+ }
+ }
+ } while (++i < no_parts);
+ DBUG_RETURN(FALSE);
+}
+
+
+/**
+ @brief Check and repair the table if neccesary
+
+ @param thd Thread object
+
+ @retval TRUE Error/Not supported
+ @retval FALSE Success
+*/
+
+bool ha_partition::check_and_repair(THD *thd)
+{
+ handler **file= m_file;
+ DBUG_ENTER("ha_partition::check_and_repair");
+
+ do
+ {
+ if ((*file)->ha_check_and_repair(thd))
+ DBUG_RETURN(TRUE);
+ } while (*(++file));
+ DBUG_RETURN(FALSE);
+}
+
+
+/**
+ @breif Check if the table can be automatically repaired
+
+ @retval TRUE Can be auto repaired
+ @retval FALSE Cannot be auto repaired
+*/
+
+bool ha_partition::auto_repair() const
+{
+ DBUG_ENTER("ha_partition::auto_repair");
+
+ /*
+ As long as we only support one storage engine per table,
+ we can use the first partition for this function.
+ */
+ DBUG_RETURN(m_file[0]->auto_repair());
+}
+
+
+/**
+ @breif Check if the table is crashed
+
+ @retval TRUE Crashed
+ @retval FALSE Not crashed
+*/
+
+bool ha_partition::is_crashed() const
+{
+ handler **file= m_file;
+ DBUG_ENTER("ha_partition::is_crashed");
+
+ do
+ {
+ if ((*file)->is_crashed())
+ DBUG_RETURN(TRUE);
+ } while (*(++file));
+ DBUG_RETURN(FALSE);
+}
+
+
+/*
+ Prepare by creating a new partition
+
+ SYNOPSIS
+ prepare_new_partition()
+ table Table object
+ create_info Create info from CREATE TABLE
+ file Handler object of new partition
+ part_name partition name
+
+ RETURN VALUE
+ >0 Error
+ 0 Success
+*/
+
+int ha_partition::prepare_new_partition(TABLE *tbl,
+ HA_CREATE_INFO *create_info,
+ handler *file, const char *part_name,
+ partition_element *p_elem)
+{
+ int error;
+ bool create_flag= FALSE;
+ DBUG_ENTER("prepare_new_partition");
+
+ if ((error= set_up_table_before_create(tbl, part_name, create_info,
+ 0, p_elem)))
+ goto error;
+ if ((error= file->ha_create(part_name, tbl, create_info)))
+ goto error;
+ create_flag= TRUE;
+ if ((error= file->ha_open(tbl, part_name, m_mode, m_open_test_lock)))
+ goto error;
+ /*
+ 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)))
+ goto error;
+
+ DBUG_RETURN(0);
+error:
+ if (create_flag)
+ VOID(file->ha_delete_table(part_name));
+ DBUG_RETURN(error);
+}
+
+
+/*
+ Cleanup by removing all created partitions after error
+
+ SYNOPSIS
+ cleanup_new_partition()
+ part_count Number of partitions to remove
+
+ RETURN VALUE
+ NONE
+
+ DESCRIPTION
+ This function is called immediately after prepare_new_partition() in
+ case the latter fails.
+
+ In prepare_new_partition() last call that may return failure is
+ external_lock(). That means if prepare_new_partition() fails,
+ partition does not have external lock. Thus no need to call
+ external_lock(F_UNLCK) here.
+
+ TODO:
+ We must ensure that in the case that we get an error during the process
+ that we call external_lock with F_UNLCK, close the table and delete the
+ table in the case where we have been successful with prepare_handler.
+ We solve this by keeping an array of successful calls to prepare_handler
+ which can then be used to undo the call.
+*/
+
+void ha_partition::cleanup_new_partition(uint part_count)
+{
+ handler **save_m_file= m_file;
+ DBUG_ENTER("ha_partition::cleanup_new_partition");
+
+ if (m_added_file && m_added_file[0])
+ {
+ m_file= m_added_file;
+ m_added_file= NULL;
+
+ /* delete_table also needed, a bit more complex */
+ close();
+
+ m_added_file= m_file;
+ m_file= save_m_file;
+ }
+ DBUG_VOID_RETURN;
+}
+
+/*
+ Implement the partition changes defined by ALTER TABLE of partitions
+
+ SYNOPSIS
+ change_partitions()
+ create_info HA_CREATE_INFO object describing all
+ fields and indexes in table
+ path Complete path of db and table name
+ out: copied Output parameter where number of copied
+ records are added
+ out: deleted Output parameter where number of deleted
+ records are added
+ pack_frm_data Reference to packed frm file
+ pack_frm_len Length of packed frm file
+
+ RETURN VALUE
+ >0 Failure
+ 0 Success
+
+ DESCRIPTION
+ Add and copy if needed a number of partitions, during this operation
+ no other operation is ongoing in the server. This is used by
+ ADD PARTITION all types as well as by REORGANIZE PARTITION. For
+ one-phased implementations it is used also by DROP and COALESCE
+ PARTITIONs.
+ One-phased implementation needs the new frm file, other handlers will
+ get zero length and a NULL reference here.
+*/
+
+int ha_partition::change_partitions(HA_CREATE_INFO *create_info,
+ const char *path,
+ ulonglong * const copied,
+ ulonglong * const deleted,
+ const uchar *pack_frm_data
+ __attribute__((unused)),
+ size_t pack_frm_len
+ __attribute__((unused)))
+{
+ 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 i= 0;
+ uint no_remain_partitions, part_count, orig_count;
+ handler **new_file_array;
+ int error= 1;
+ bool first;
+ uint temp_partitions= m_part_info->temp_partitions.elements;
+ THD *thd= ha_thd();
+ DBUG_ENTER("ha_partition::change_partitions");
+
+ /*
+ Assert that it works without HA_FILE_BASED and lower_case_table_name = 2.
+ We use m_file[0] as long as all partitions have the same storage engine.
+ */
+ DBUG_ASSERT(!strcmp(path, get_canonical_filename(m_file[0], path,
+ part_name_buff)));
+ m_reorged_parts= 0;
+ if (!m_part_info->is_sub_partitioned())
+ no_subparts= 1;
+
+ /*
+ Step 1:
+ Calculate number of reorganised partitions and allocate space for
+ their handler references.
+ */
+ if (temp_partitions)
+ {
+ m_reorged_parts= temp_partitions * no_subparts;
+ }
+ else
+ {
+ do
+ {
+ partition_element *part_elem= part_it++;
+ if (part_elem->part_state == PART_CHANGED ||
+ part_elem->part_state == PART_REORGED_DROPPED)
+ {
+ m_reorged_parts+= no_subparts;
+ }
+ } while (++i < no_parts);
+ }
+ if (m_reorged_parts &&
+ !(m_reorged_file= (handler**)sql_calloc(sizeof(handler*)*
+ (m_reorged_parts + 1))))
+ {
+ mem_alloc_error(sizeof(handler*)*(m_reorged_parts+1));
+ DBUG_RETURN(ER_OUTOFMEMORY);
+ }
+
+ /*
+ Step 2:
+ Calculate number of partitions after change and allocate space for
+ their handler references.
+ */
+ no_remain_partitions= 0;
+ if (temp_partitions)
+ {
+ no_remain_partitions= no_parts * no_subparts;
+ }
+ else
+ {
+ part_it.rewind();
+ i= 0;
+ do
+ {
+ partition_element *part_elem= part_it++;
+ if (part_elem->part_state == PART_NORMAL ||
+ part_elem->part_state == PART_TO_BE_ADDED ||
+ part_elem->part_state == PART_CHANGED)
+ {
+ no_remain_partitions+= no_subparts;
+ }
+ } while (++i < no_parts);
+ }
+ if (!(new_file_array= (handler**)sql_calloc(sizeof(handler*)*
+ (2*(no_remain_partitions + 1)))))
+ {
+ mem_alloc_error(sizeof(handler*)*2*(no_remain_partitions+1));
+ DBUG_RETURN(ER_OUTOFMEMORY);
+ }
+ m_added_file= &new_file_array[no_remain_partitions + 1];
+
+ /*
+ Step 3:
+ Fill m_reorged_file with handler references and NULL at the end
+ */
+ if (m_reorged_parts)
+ {
+ i= 0;
+ part_count= 0;
+ first= TRUE;
+ part_it.rewind();
+ do
+ {
+ partition_element *part_elem= part_it++;
+ if (part_elem->part_state == PART_CHANGED ||
+ 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;
+ }
+ else if (first && temp_partitions &&
+ part_elem->part_state == PART_TO_BE_ADDED)
+ {
+ /*
+ When doing an ALTER TABLE REORGANIZE PARTITION a number of
+ partitions is to be reorganised into a set of new partitions.
+ The reorganised partitions are in this case in the temp_partitions
+ list. We copy all of them in one batch and thus we only do this
+ until we find the first partition with state PART_TO_BE_ADDED
+ since this is where the new partitions go in and where the old
+ 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],
+ sizeof(handler*)*m_reorged_parts);
+ }
+ } while (++i < no_parts);
+ }
+
+ /*
+ Step 4:
+ Fill new_array_file with handler references. Create the handlers if
+ needed.
+ */
+ i= 0;
+ part_count= 0;
+ orig_count= 0;
+ first= TRUE;
+ part_it.rewind();
+ do
+ {
+ partition_element *part_elem= part_it++;
+ if (part_elem->part_state == PART_NORMAL)
+ {
+ DBUG_ASSERT(orig_count + no_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;
+ }
+ else if (part_elem->part_state == PART_CHANGED ||
+ part_elem->part_state == PART_TO_BE_ADDED)
+ {
+ uint j= 0;
+ do
+ {
+ if (!(new_file_array[part_count++]=
+ get_new_handler(table->s,
+ thd->mem_root,
+ part_elem->engine_type)))
+ {
+ mem_alloc_error(sizeof(handler));
+ DBUG_RETURN(ER_OUTOFMEMORY);
+ }
+ } while (++j < no_subparts);
+ if (part_elem->part_state == PART_CHANGED)
+ orig_count+= no_subparts;
+ else if (temp_partitions && first)
+ {
+ orig_count+= (no_subparts * temp_partitions);
+ first= FALSE;
+ }
+ }
+ } while (++i < no_parts);
+ first= FALSE;
+ /*
+ Step 5:
+ Create the new partitions and also open, lock and call external_lock
+ on them to prepare them for copy phase and also for later close
+ calls
+ */
+ i= 0;
+ part_count= 0;
+ part_it.rewind();
+ do
+ {
+ partition_element *part_elem= part_it++;
+ if (part_elem->part_state == PART_TO_BE_ADDED ||
+ part_elem->part_state == PART_CHANGED)
+ {
+ /*
+ A new partition needs to be created PART_TO_BE_ADDED means an
+ entirely new partition and PART_CHANGED means a changed partition
+ that will still exist with either more or less data in it.
+ */
+ uint name_variant= NORMAL_PART_NAME;
+ if (part_elem->part_state == PART_CHANGED ||
+ (part_elem->part_state == PART_TO_BE_ADDED && temp_partitions))
+ name_variant= TEMP_PART_NAME;
+ if (m_part_info->is_sub_partitioned())
+ {
+ List_iterator<partition_element> sub_it(part_elem->subpartitions);
+ uint j= 0, part;
+ do
+ {
+ partition_element *sub_elem= sub_it++;
+ create_subpartition_name(part_name_buff, path,
+ part_elem->partition_name,
+ sub_elem->partition_name,
+ name_variant);
+ part= i * no_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)))
+ {
+ cleanup_new_partition(part_count);
+ DBUG_RETURN(error);
+ }
+ m_added_file[part_count++]= new_file_array[part];
+ } while (++j < no_subparts);
+ }
+ else
+ {
+ create_partition_name(part_name_buff, path,
+ part_elem->partition_name, name_variant,
+ TRUE);
+ DBUG_PRINT("info", ("Add partition %s", part_name_buff));
+ if ((error= prepare_new_partition(table, create_info,
+ new_file_array[i],
+ (const char *)part_name_buff,
+ part_elem)))
+ {
+ cleanup_new_partition(part_count);
+ DBUG_RETURN(error);
+ }
+ m_added_file[part_count++]= new_file_array[i];
+ }
+ }
+ } while (++i < no_parts);
+
+ /*
+ Step 6:
+ State update to prepare for next write of the frm file.
+ */
+ i= 0;
+ part_it.rewind();
+ do
+ {
+ partition_element *part_elem= part_it++;
+ if (part_elem->part_state == PART_TO_BE_ADDED)
+ part_elem->part_state= PART_IS_ADDED;
+ else if (part_elem->part_state == PART_CHANGED)
+ 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);
+ for (i= 0; i < temp_partitions; i++)
+ {
+ partition_element *part_elem= t_it++;
+ DBUG_ASSERT(part_elem->part_state == PART_TO_BE_REORGED);
+ part_elem->part_state= PART_TO_BE_DROPPED;
+ }
+ m_new_file= new_file_array;
+ DBUG_RETURN(copy_partitions(copied, deleted));
+}
+
+
+/*
+ Copy partitions as part of ALTER TABLE of partitions
+
+ SYNOPSIS
+ copy_partitions()
+ out:copied Number of records copied
+ out:deleted Number of records deleted
+
+ RETURN VALUE
+ >0 Error code
+ 0 Success
+
+ DESCRIPTION
+ change_partitions has done all the preparations, now it is time to
+ actually copy the data from the reorganised partitions to the new
+ partitions.
+*/
+
+int ha_partition::copy_partitions(ulonglong * const copied,
+ ulonglong * const deleted)
+{
+ uint reorg_part= 0;
+ int result= 0;
+ longlong func_value;
+ DBUG_ENTER("ha_partition::copy_partitions");
+
+ 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);
+ else
+ set_linear_hash_mask(m_part_info, m_part_info->no_subparts);
+ }
+
+ while (reorg_part < m_reorged_parts)
+ {
+ handler *file= m_reorged_file[reorg_part];
+ uint32 new_part;
+
+ late_extra_cache(reorg_part);
+ if ((result= file->ha_rnd_init(1)))
+ goto error;
+ while (TRUE)
+ {
+ if ((result= file->rnd_next(m_rec0)))
+ {
+ if (result == HA_ERR_RECORD_DELETED)
+ continue; //Probably MyISAM
+ if (result != HA_ERR_END_OF_FILE)
+ goto error;
+ /*
+ End-of-file reached, break out to continue with next partition or
+ end the copy process.
+ */
+ break;
+ }
+ /* Found record to insert into new handler */
+ if (m_part_info->get_partition_id(m_part_info, &new_part,
+ &func_value))
+ {
+ /*
+ This record is in the original table but will not be in the new
+ table since it doesn't fit into any partition any longer due to
+ changed partitioning ranges or list values.
+ */
+ (*deleted)++;
+ }
+ else
+ {
+ THD *thd= ha_thd();
+ /* Copy record to new handler */
+ (*copied)++;
+ tmp_disable_binlog(thd); /* Do not replicate the low-level changes. */
+ result= m_new_file[new_part]->ha_write_row(m_rec0);
+ reenable_binlog(thd);
+ if (result)
+ goto error;
+ }
+ }
+ late_extra_no_cache(reorg_part);
+ file->ha_rnd_end();
+ reorg_part++;
+ }
+ DBUG_RETURN(FALSE);
+error:
+ DBUG_RETURN(result);
+}
+
+
+/*
+ Update create info as part of ALTER TABLE
+
+ SYNOPSIS
+ update_create_info()
+ create_info Create info from ALTER TABLE
+
+ RETURN VALUE
+ NONE
+
+ DESCRIPTION
+ Method empty so far
+*/
+
+void ha_partition::update_create_info(HA_CREATE_INFO *create_info)
+{
+ /*
+ Fix for bug#38751, some engines needs info-calls in ALTER.
+ Archive need this since it flushes in ::info.
+ HA_STATUS_AUTO is optimized so it will not always be forwarded
+ to all partitions, but HA_STATUS_VARIABLE will.
+ */
+ info(HA_STATUS_VARIABLE);
+
+ info(HA_STATUS_AUTO);
+
+ if (!(create_info->used_fields & HA_CREATE_USED_AUTO))
+ create_info->auto_increment_value= stats.auto_increment_value;
+
+ create_info->data_file_name= create_info->index_file_name = NULL;
+ return;
+}
+
+
+void ha_partition::change_table_ptr(TABLE *table_arg, TABLE_SHARE *share)
+{
+ handler **file_array= m_file;
+ table= table_arg;
+ table_share= share;
+ do
+ {
+ (*file_array)->change_table_ptr(table_arg, share);
+ } while (*(++file_array));
+ if (m_added_file && m_added_file[0])
+ {
+ /* if in middle of a drop/rename etc */
+ file_array= m_added_file;
+ do
+ {
+ (*file_array)->change_table_ptr(table_arg, share);
+ } while (*(++file_array));
+ }
+}
+
+/*
+ Change comments specific to handler
+
+ SYNOPSIS
+ update_table_comment()
+ comment Original comment
+
+ RETURN VALUE
+ new comment
+
+ DESCRIPTION
+ No comment changes so far
+*/
+
+char *ha_partition::update_table_comment(const char *comment)
+{
+ return (char*) comment; /* Nothing to change */
+}
+
+
+
+/*
+ Handle delete, rename and create table
+
+ SYNOPSIS
+ del_ren_cre_table()
+ from Full path of old table
+ to Full path of new table
+ table_arg Table object
+ create_info Create info
+
+ RETURN VALUE
+ >0 Error
+ 0 Success
+
+ DESCRIPTION
+ Common routine to handle delete_table and rename_table.
+ The routine uses the partition handler file to get the
+ names of the partition instances. Both these routines
+ are called after creating the handler without table
+ object and thus the file is needed to discover the
+ names of the partitions and the underlying storage engines.
+*/
+
+uint ha_partition::del_ren_cre_table(const char *from,
+ const char *to,
+ TABLE *table_arg,
+ HA_CREATE_INFO *create_info)
+{
+ int save_error= 0;
+ int error;
+ char from_buff[FN_REFLEN], to_buff[FN_REFLEN], from_lc_buff[FN_REFLEN],
+ to_lc_buff[FN_REFLEN];
+ char *name_buffer_ptr;
+ const char *from_path;
+ const char *to_path= NULL;
+ uint i;
+ handler **file, **abort_file;
+ DBUG_ENTER("del_ren_cre_table()");
+
+ if (get_from_handler_file(from, ha_thd()->mem_root))
+ DBUG_RETURN(TRUE);
+ DBUG_ASSERT(m_file_buffer);
+ DBUG_PRINT("enter", ("from: (%s) to: (%s)", from, to));
+ name_buffer_ptr= m_name_buffer_ptr;
+ file= m_file;
+ /*
+ Since ha_partition has HA_FILE_BASED, it must alter underlying table names
+ if they do not have HA_FILE_BASED and lower_case_table_names == 2.
+ See Bug#37402, for Mac OS X.
+ The appended #P#<partname>[#SP#<subpartname>] will remain in current case.
+ Using the first partitions handler, since mixing handlers is not allowed.
+ */
+ from_path= get_canonical_filename(*file, from, from_lc_buff);
+ if (to != NULL)
+ to_path= get_canonical_filename(*file, to, to_lc_buff);
+ i= 0;
+ do
+ {
+ create_partition_name(from_buff, from_path, name_buffer_ptr,
+ NORMAL_PART_NAME, FALSE);
+
+ if (to != NULL)
+ { // Rename branch
+ create_partition_name(to_buff, to_path, name_buffer_ptr,
+ NORMAL_PART_NAME, FALSE);
+ error= (*file)->ha_rename_table(from_buff, to_buff);
+ }
+ else if (table_arg == NULL) // delete branch
+ error= (*file)->ha_delete_table(from_buff);
+ else
+ {
+ if ((error= set_up_table_before_create(table_arg, from_buff,
+ create_info, i, NULL)) ||
+ ((error= (*file)->ha_create(from_buff, table_arg, create_info))))
+ goto create_error;
+ }
+ name_buffer_ptr= strend(name_buffer_ptr) + 1;
+ if (error)
+ save_error= error;
+ i++;
+ } while (*(++file));
+ DBUG_RETURN(save_error);
+create_error:
+ name_buffer_ptr= m_name_buffer_ptr;
+ for (abort_file= file, file= m_file; file < abort_file; file++)
+ {
+ create_partition_name(from_buff, from_path, name_buffer_ptr, NORMAL_PART_NAME,
+ FALSE);
+ VOID((*file)->ha_delete_table((const char*) from_buff));
+ name_buffer_ptr= strend(name_buffer_ptr) + 1;
+ }
+ DBUG_RETURN(error);
+}
+
+/*
+ Find partition based on partition id
+
+ SYNOPSIS
+ find_partition_element()
+ part_id Partition id of partition looked for
+
+ RETURN VALUE
+ >0 Reference to partition_element
+ 0 Partition not found
+*/
+
+partition_element *ha_partition::find_partition_element(uint part_id)
+{
+ uint i;
+ uint curr_part_id= 0;
+ List_iterator_fast <partition_element> part_it(m_part_info->partitions);
+
+ for (i= 0; i < m_part_info->no_parts; i++)
+ {
+ partition_element *part_elem;
+ part_elem= part_it++;
+ if (m_is_sub_partitioned)
+ {
+ uint j;
+ List_iterator_fast <partition_element> sub_it(part_elem->subpartitions);
+ for (j= 0; j < m_part_info->no_subparts; j++)
+ {
+ part_elem= sub_it++;
+ if (part_id == curr_part_id++)
+ return part_elem;
+ }
+ }
+ else if (part_id == curr_part_id++)
+ return part_elem;
+ }
+ DBUG_ASSERT(0);
+ my_error(ER_OUT_OF_RESOURCES, MYF(0));
+ current_thd->fatal_error(); // Abort
+ return NULL;
+}
+
+
+/*
+ Set up table share object before calling create on underlying handler
+
+ SYNOPSIS
+ set_up_table_before_create()
+ table Table object
+ info Create info
+ part_id Partition id of partition to set-up
+
+ RETURN VALUE
+ TRUE Error
+ FALSE Success
+
+ DESCRIPTION
+ Set up
+ 1) Comment on partition
+ 2) MAX_ROWS, MIN_ROWS on partition
+ 3) Index file name on partition
+ 4) Data file name on partition
+*/
+
+int ha_partition::set_up_table_before_create(TABLE *tbl,
+ const char *partition_name_with_path,
+ HA_CREATE_INFO *info,
+ uint part_id,
+ partition_element *part_elem)
+{
+ int error= 0;
+ const char *partition_name;
+ THD *thd= ha_thd();
+ DBUG_ENTER("set_up_table_before_create");
+
+ if (!part_elem)
+ {
+ part_elem= find_partition_element(part_id);
+ if (!part_elem)
+ DBUG_RETURN(1); // Fatal error
+ }
+ tbl->s->max_rows= part_elem->part_max_rows;
+ tbl->s->min_rows= part_elem->part_min_rows;
+ partition_name= strrchr(partition_name_with_path, FN_LIBCHAR);
+ if ((part_elem->index_file_name &&
+ (error= append_file_to_dir(thd,
+ (const char**)&part_elem->index_file_name,
+ partition_name+1))) ||
+ (part_elem->data_file_name &&
+ (error= append_file_to_dir(thd,
+ (const char**)&part_elem->data_file_name,
+ partition_name+1))))
+ {
+ DBUG_RETURN(error);
+ }
+ info->index_file_name= part_elem->index_file_name;
+ info->data_file_name= part_elem->data_file_name;
+ DBUG_RETURN(0);
+}
+
+
+/*
+ Add two names together
+
+ SYNOPSIS
+ name_add()
+ out:dest Destination string
+ first_name First name
+ sec_name Second name
+
+ RETURN VALUE
+ >0 Error
+ 0 Success
+
+ DESCRIPTION
+ Routine used to add two names with '_' in between then. Service routine
+ to create_handler_file
+ Include the NULL in the count of characters since it is needed as separator
+ between the partition names.
+*/
+
+static uint name_add(char *dest, const char *first_name, const char *sec_name)
+{
+ return (uint) (strxmov(dest, first_name, "#SP#", sec_name, NullS) -dest) + 1;
+}
+
+
+/*
+ Create the special .par file
+
+ SYNOPSIS
+ create_handler_file()
+ name Full path of table name
+
+ RETURN VALUE
+ >0 Error code
+ 0 Success
+
+ DESCRIPTION
+ Method used to create handler file with names of partitions, their
+ engine types and the number of partitions.
+*/
+
+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_parts= 0;
+ uint tot_len_words, tot_len_byte, chksum, tot_name_words;
+ char *name_buffer_ptr;
+ uchar *file_buffer, *engine_array;
+ bool result= TRUE;
+ char file_name[FN_REFLEN];
+ char part_name[FN_REFLEN];
+ char subpart_name[FN_REFLEN];
+ File file;
+ 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));
+ tot_name_len= 0;
+ for (i= 0; i < no_parts; i++)
+ {
+ part_elem= part_it++;
+ if (part_elem->part_state != PART_NORMAL &&
+ part_elem->part_state != PART_TO_BE_ADDED &&
+ part_elem->part_state != PART_CHANGED)
+ continue;
+ tablename_to_filename(part_elem->partition_name, part_name,
+ FN_REFLEN);
+ part_name_len= strlen(part_name);
+ if (!m_is_sub_partitioned)
+ {
+ tot_name_len+= part_name_len + 1;
+ tot_parts++;
+ }
+ else
+ {
+ List_iterator_fast <partition_element> sub_it(part_elem->subpartitions);
+ for (j= 0; j < m_part_info->no_subparts; j++)
+ {
+ subpart_elem= sub_it++;
+ tablename_to_filename(subpart_elem->partition_name,
+ subpart_name,
+ FN_REFLEN);
+ subpart_name_len= strlen(subpart_name);
+ tot_name_len+= part_name_len + subpart_name_len + 5;
+ tot_parts++;
+ }
+ }
+ }
+ /*
+ File format:
+ Length in words 4 byte
+ Checksum 4 byte
+ Total number of partitions 4 byte
+ Array of engine types n * 4 bytes where
+ n = (m_tot_parts + 3)/4
+ Length of name part in bytes 4 bytes
+ Name part m * 4 bytes where
+ m = ((length_name_part + 3)/4)*4
+
+ All padding bytes are zeroed
+ */
+ tot_partition_words= (tot_parts + 3) / 4;
+ tot_name_words= (tot_name_len + 3) / 4;
+ tot_len_words= 4 + tot_partition_words + tot_name_words;
+ tot_len_byte= 4 * tot_len_words;
+ if (!(file_buffer= (uchar *) my_malloc(tot_len_byte, MYF(MY_ZEROFILL))))
+ DBUG_RETURN(TRUE);
+ engine_array= (file_buffer + 12);
+ name_buffer_ptr= (char*) (file_buffer + ((4 + tot_partition_words) * 4));
+ part_it.rewind();
+ for (i= 0; i < no_parts; i++)
+ {
+ part_elem= part_it++;
+ if (part_elem->part_state != PART_NORMAL &&
+ part_elem->part_state != PART_TO_BE_ADDED &&
+ part_elem->part_state != PART_CHANGED)
+ continue;
+ if (!m_is_sub_partitioned)
+ {
+ tablename_to_filename(part_elem->partition_name, part_name, FN_REFLEN);
+ name_buffer_ptr= strmov(name_buffer_ptr, part_name)+1;
+ *engine_array= (uchar) ha_legacy_type(part_elem->engine_type);
+ DBUG_PRINT("info", ("engine: %u", *engine_array));
+ engine_array++;
+ }
+ else
+ {
+ List_iterator_fast <partition_element> sub_it(part_elem->subpartitions);
+ for (j= 0; j < m_part_info->no_subparts; j++)
+ {
+ subpart_elem= sub_it++;
+ tablename_to_filename(part_elem->partition_name, part_name,
+ FN_REFLEN);
+ tablename_to_filename(subpart_elem->partition_name, subpart_name,
+ FN_REFLEN);
+ name_buffer_ptr+= name_add(name_buffer_ptr,
+ part_name,
+ subpart_name);
+ *engine_array= (uchar) ha_legacy_type(subpart_elem->engine_type);
+ DBUG_PRINT("info", ("engine: %u", *engine_array));
+ engine_array++;
+ }
+ }
+ }
+ chksum= 0;
+ int4store(file_buffer, tot_len_words);
+ int4store(file_buffer + 8, tot_parts);
+ int4store(file_buffer + 12 + (tot_partition_words * 4), tot_name_len);
+ for (i= 0; i < tot_len_words; i++)
+ chksum^= uint4korr(file_buffer + 4 * i);
+ int4store(file_buffer + 4, chksum);
+ /*
+ Remove .frm extension and replace with .par
+ Create and write and close file
+ 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)
+ {
+ result= my_write(file, (uchar *) file_buffer, tot_len_byte,
+ MYF(MY_WME | MY_NABP)) != 0;
+ VOID(my_close(file, MYF(0)));
+ }
+ else
+ result= TRUE;
+ my_free((char*) file_buffer, MYF(0));
+ DBUG_RETURN(result);
+}
+
+/*
+ Clear handler variables and free some memory
+
+ SYNOPSIS
+ clear_handler_file()
+
+ RETURN VALUE
+ NONE
+*/
+
+void ha_partition::clear_handler_file()
+{
+ if (m_engine_array)
+ plugin_unlock_list(NULL, m_engine_array, m_tot_parts);
+ my_free((char*) m_file_buffer, MYF(MY_ALLOW_ZERO_PTR));
+ my_free((char*) m_engine_array, MYF(MY_ALLOW_ZERO_PTR));
+ m_file_buffer= NULL;
+ m_engine_array= NULL;
+}
+
+/*
+ Create underlying handler objects
+
+ SYNOPSIS
+ create_handlers()
+ mem_root Allocate memory through this
+
+ RETURN VALUE
+ TRUE Error
+ FALSE Success
+*/
+
+bool ha_partition::create_handlers(MEM_ROOT *mem_root)
+{
+ uint i;
+ uint alloc_len= (m_tot_parts + 1) * sizeof(handler*);
+ handlerton *hton0;
+ DBUG_ENTER("create_handlers");
+
+ if (!(m_file= (handler **) alloc_root(mem_root, alloc_len)))
+ DBUG_RETURN(TRUE);
+ m_file_tot_parts= m_tot_parts;
+ bzero((char*) m_file, alloc_len);
+ for (i= 0; i < m_tot_parts; i++)
+ {
+ handlerton *hton= plugin_data(m_engine_array[i], handlerton*);
+ if (!(m_file[i]= get_new_handler(table_share, mem_root,
+ hton)))
+ DBUG_RETURN(TRUE);
+ DBUG_PRINT("info", ("engine_type: %u", hton->db_type));
+ }
+ /* For the moment we only support partition over the same table engine */
+ hton0= plugin_data(m_engine_array[0], handlerton*);
+ if (hton0 == myisam_hton)
+ {
+ DBUG_PRINT("info", ("MyISAM"));
+ m_myisam= TRUE;
+ }
+ /* INNODB may not be compiled in... */
+ else if (ha_legacy_type(hton0) == DB_TYPE_INNODB)
+ {
+ DBUG_PRINT("info", ("InnoDB"));
+ m_innodb= TRUE;
+ }
+ DBUG_RETURN(FALSE);
+}
+
+/*
+ Create underlying handler objects from partition info
+
+ SYNOPSIS
+ new_handlers_from_part_info()
+ mem_root Allocate memory through this
+
+ RETURN VALUE
+ TRUE Error
+ FALSE Success
+*/
+
+bool ha_partition::new_handlers_from_part_info(MEM_ROOT *mem_root)
+{
+ uint i, j, part_count;
+ partition_element *part_elem;
+ uint alloc_len= (m_tot_parts + 1) * sizeof(handler*);
+ List_iterator_fast <partition_element> part_it(m_part_info->partitions);
+ DBUG_ENTER("ha_partition::new_handlers_from_part_info");
+
+ if (!(m_file= (handler **) alloc_root(mem_root, alloc_len)))
+ {
+ mem_alloc_error(alloc_len);
+ goto error_end;
+ }
+ m_file_tot_parts= m_tot_parts;
+ bzero((char*) m_file, alloc_len);
+ DBUG_ASSERT(m_part_info->no_parts > 0);
+
+ i= 0;
+ part_count= 0;
+ /*
+ Don't know the size of the underlying storage engine, invent a number of
+ bytes allocated for error message if allocation fails
+ */
+ do
+ {
+ part_elem= part_it++;
+ if (m_is_sub_partitioned)
+ {
+ for (j= 0; j < m_part_info->no_subparts; j++)
+ {
+ if (!(m_file[part_count++]= get_new_handler(table_share, mem_root,
+ part_elem->engine_type)))
+ goto error;
+ DBUG_PRINT("info", ("engine_type: %u",
+ (uint) ha_legacy_type(part_elem->engine_type)));
+ }
+ }
+ else
+ {
+ if (!(m_file[part_count++]= get_new_handler(table_share, mem_root,
+ part_elem->engine_type)))
+ goto error;
+ DBUG_PRINT("info", ("engine_type: %u",
+ (uint) ha_legacy_type(part_elem->engine_type)));
+ }
+ } while (++i < m_part_info->no_parts);
+ if (part_elem->engine_type == myisam_hton)
+ {
+ DBUG_PRINT("info", ("MyISAM"));
+ m_myisam= TRUE;
+ }
+ DBUG_RETURN(FALSE);
+error:
+ mem_alloc_error(sizeof(handler));
+error_end:
+ DBUG_RETURN(TRUE);
+}
+
+
+/*
+ Get info about partition engines and their names from the .par file
+
+ SYNOPSIS
+ get_from_handler_file()
+ name Full path of table name
+ mem_root Allocate memory through this
+
+ RETURN VALUE
+ TRUE Error
+ FALSE Success
+
+ DESCRIPTION
+ Open handler file to get partition names, engine types and number of
+ partitions.
+*/
+
+bool ha_partition::get_from_handler_file(const char *name, MEM_ROOT *mem_root)
+{
+ char buff[FN_REFLEN], *address_tot_name_len;
+ File file;
+ char *file_buffer, *name_buffer_ptr;
+ handlerton **engine_array;
+ uint i, len_bytes, len_words, tot_partition_words, tot_name_words, chksum;
+ DBUG_ENTER("ha_partition::get_from_handler_file");
+ DBUG_PRINT("enter", ("table name: '%s'", name));
+
+ if (m_file_buffer)
+ 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(0))) < 0)
+ DBUG_RETURN(TRUE);
+ if (my_read(file, (uchar *) & buff[0], 8, MYF(MY_NABP)))
+ goto err1;
+ len_words= uint4korr(buff);
+ len_bytes= 4 * len_words;
+ if (!(file_buffer= (char*) my_malloc(len_bytes, MYF(0))))
+ goto err1;
+ VOID(my_seek(file, 0, MY_SEEK_SET, MYF(0)));
+ if (my_read(file, (uchar *) file_buffer, len_bytes, MYF(MY_NABP)))
+ goto err2;
+
+ chksum= 0;
+ for (i= 0; i < len_words; i++)
+ chksum ^= uint4korr((file_buffer) + 4 * i);
+ if (chksum)
+ goto err2;
+ m_tot_parts= uint4korr((file_buffer) + 8);
+ DBUG_PRINT("info", ("No of parts = %u", m_tot_parts));
+ tot_partition_words= (m_tot_parts + 3) / 4;
+ engine_array= (handlerton **) my_alloca(m_tot_parts * sizeof(handlerton*));
+ for (i= 0; i < m_tot_parts; i++)
+ engine_array[i]= ha_resolve_by_legacy_type(ha_thd(),
+ (enum legacy_db_type)
+ *(uchar *) ((file_buffer) + 12 + i));
+ address_tot_name_len= file_buffer + 12 + 4 * tot_partition_words;
+ tot_name_words= (uint4korr(address_tot_name_len) + 3) / 4;
+ if (len_words != (tot_partition_words + tot_name_words + 4))
+ goto err3;
+ name_buffer_ptr= file_buffer + 16 + 4 * tot_partition_words;
+ VOID(my_close(file, MYF(0)));
+ m_file_buffer= file_buffer; // Will be freed in clear_handler_file()
+ m_name_buffer_ptr= name_buffer_ptr;
+
+ if (!(m_engine_array= (plugin_ref*)
+ my_malloc(m_tot_parts * sizeof(plugin_ref), MYF(MY_WME))))
+ goto err3;
+
+ for (i= 0; i < m_tot_parts; i++)
+ m_engine_array[i]= ha_lock_engine(NULL, engine_array[i]);
+
+ my_afree((gptr) engine_array);
+
+ if (!m_file && create_handlers(mem_root))
+ {
+ clear_handler_file();
+ DBUG_RETURN(TRUE);
+ }
+ DBUG_RETURN(FALSE);
+
+err3:
+ my_afree((gptr) engine_array);
+err2:
+ my_free(file_buffer, MYF(0));
+err1:
+ VOID(my_close(file, MYF(0)));
+ DBUG_RETURN(TRUE);
+}
+
+
+/****************************************************************************
+ MODULE open/close object
+****************************************************************************/
+/*
+ Open handler object
+
+ SYNOPSIS
+ open()
+ name Full path of table name
+ mode Open mode flags
+ test_if_locked ?
+
+ RETURN VALUE
+ >0 Error
+ 0 Success
+
+ DESCRIPTION
+ Used for opening tables. The name will be the name of the file.
+ A table is opened when it needs to be opened. For instance
+ when a request comes in for a select on the table (tables are not
+ open and closed for each request, they are cached).
+
+ Called from handler.cc by handler::ha_open(). The server opens all tables
+ by calling ha_open() which then calls the handler specific open().
+*/
+
+int ha_partition::open(const char *name, int mode, uint test_if_locked)
+{
+ char *name_buffer_ptr= m_name_buffer_ptr;
+ int error;
+ uint alloc_len;
+ handler **file;
+ char name_buff[FN_REFLEN];
+ bool is_not_tmp_table= (table_share->tmp_table == NO_TMP_TABLE);
+ ulonglong check_table_flags= 0;
+ DBUG_ENTER("ha_partition::open");
+
+ DBUG_ASSERT(table->s == table_share);
+ ref_length= 0;
+ m_mode= mode;
+ m_open_test_lock= test_if_locked;
+ m_part_field_array= m_part_info->full_part_field_array;
+ if (get_from_handler_file(name, &table->mem_root))
+ DBUG_RETURN(1);
+ m_start_key.length= 0;
+ m_rec0= table->record[0];
+ m_rec_length= table_share->reclength;
+ alloc_len= m_tot_parts * (m_rec_length + PARTITION_BYTES_IN_POS);
+ alloc_len+= table_share->max_key_length;
+ if (!m_ordered_rec_buffer)
+ {
+ if (!(m_ordered_rec_buffer= (uchar*)my_malloc(alloc_len, MYF(MY_WME))))
+ {
+ DBUG_RETURN(1);
+ }
+ {
+ /*
+ We set-up one record per partition and each record has 2 bytes in
+ front where the partition id is written. This is used by ordered
+ index_read.
+ We also set-up a reference to the first record for temporary use in
+ setting up the scan.
+ */
+ char *ptr= (char*)m_ordered_rec_buffer;
+ uint i= 0;
+ do
+ {
+ int2store(ptr, i);
+ ptr+= m_rec_length + PARTITION_BYTES_IN_POS;
+ } while (++i < m_tot_parts);
+ m_start_key.key= (const uchar*)ptr;
+ }
+ }
+
+ /* Initialize the bitmap we use to determine what partitions are used */
+ if (!is_clone)
+ {
+ if (bitmap_init(&(m_part_info->used_partitions), NULL, m_tot_parts, TRUE))
+ DBUG_RETURN(1);
+ bitmap_set_all(&(m_part_info->used_partitions));
+ }
+
+ file= m_file;
+ do
+ {
+ create_partition_name(name_buff, name, name_buffer_ptr, NORMAL_PART_NAME,
+ FALSE);
+ if ((error= (*file)->ha_open(table, (const char*) name_buff, mode,
+ test_if_locked)))
+ goto err_handler;
+ m_no_locks+= (*file)->lock_count();
+ name_buffer_ptr+= strlen(name_buffer_ptr) + 1;
+ set_if_bigger(ref_length, ((*file)->ref_length));
+ /*
+ Verify that all partitions have the same set of table flags.
+ Mask all flags that partitioning enables/disables.
+ */
+ if (!check_table_flags)
+ {
+ check_table_flags= (((*file)->ha_table_flags() &
+ ~(PARTITION_DISABLED_TABLE_FLAGS)) |
+ (PARTITION_ENABLED_TABLE_FLAGS));
+ }
+ else if (check_table_flags != (((*file)->ha_table_flags() &
+ ~(PARTITION_DISABLED_TABLE_FLAGS)) |
+ (PARTITION_ENABLED_TABLE_FLAGS)))
+ {
+ error= HA_ERR_INITIALIZATION;
+ goto err_handler;
+ }
+ } while (*(++file));
+ key_used_on_scan= m_file[0]->key_used_on_scan;
+ implicit_emptied= m_file[0]->implicit_emptied;
+ /*
+ Add 2 bytes for partition id in position ref length.
+ ref_length=max_in_all_partitions(ref_length) + PARTITION_BYTES_IN_POS
+ */
+ ref_length+= PARTITION_BYTES_IN_POS;
+ m_ref_length= ref_length;
+ /*
+ Release buffer read from .par file. It will not be reused again after
+ being opened once.
+ */
+ clear_handler_file();
+ /*
+ Initialize priority queue, initialized to reading forward.
+ */
+ if ((error= init_queue(&m_queue, m_tot_parts, (uint) PARTITION_BYTES_IN_POS,
+ 0, key_rec_cmp, (void*)this)))
+ goto err_handler;
+
+ /*
+ Use table_share->ha_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)
+ {
+ HA_DATA_PARTITION *ha_data;
+ /* currently only needed for auto_increment */
+ table_share->ha_data= ha_data= (HA_DATA_PARTITION*)
+ alloc_root(&table_share->mem_root,
+ sizeof(HA_DATA_PARTITION));
+ if (!ha_data)
+ {
+ if (is_not_tmp_table)
+ pthread_mutex_unlock(&table_share->mutex);
+ goto err_handler;
+ }
+ DBUG_PRINT("info", ("table_share->ha_data 0x%p", ha_data));
+ bzero(ha_data, sizeof(HA_DATA_PARTITION));
+ }
+ if (is_not_tmp_table)
+ pthread_mutex_unlock(&table_share->mutex);
+ /*
+ Some handlers update statistics as part of the open call. This will in
+ some cases corrupt the statistics of the partition handler and thus
+ to ensure we have correct statistics we call info from open after
+ calling open on all individual handlers.
+ */
+ m_handler_status= handler_opened;
+ info(HA_STATUS_VARIABLE | HA_STATUS_CONST);
+ DBUG_RETURN(0);
+
+err_handler:
+ while (file-- != m_file)
+ (*file)->close();
+ if (!is_clone)
+ bitmap_free(&(m_part_info->used_partitions));
+
+ DBUG_RETURN(error);
+}
+
+handler *ha_partition::clone(MEM_ROOT *mem_root)
+{
+ handler *new_handler= get_new_handler(table->s, mem_root,
+ table->s->db_type());
+ ((ha_partition*)new_handler)->m_part_info= m_part_info;
+ ((ha_partition*)new_handler)->is_clone= TRUE;
+ if (new_handler && !new_handler->ha_open(table,
+ table->s->normalized_path.str,
+ table->db_stat,
+ HA_OPEN_IGNORE_IF_LOCKED))
+ return new_handler;
+ return NULL;
+}
+
+
+/*
+ Close handler object
+
+ SYNOPSIS
+ close()
+
+ RETURN VALUE
+ >0 Error code
+ 0 Success
+
+ DESCRIPTION
+ Called from sql_base.cc, sql_select.cc, and table.cc.
+ In sql_select.cc it is only used to close up temporary tables or during
+ the process where a temporary table is converted over to being a
+ myisam table.
+ For sql_base.cc look at close_data_tables().
+*/
+
+int ha_partition::close(void)
+{
+ bool first= TRUE;
+ handler **file;
+ DBUG_ENTER("ha_partition::close");
+
+ DBUG_ASSERT(table->s == table_share);
+ delete_queue(&m_queue);
+ if (!is_clone)
+ bitmap_free(&(m_part_info->used_partitions));
+ file= m_file;
+
+repeat:
+ do
+ {
+ (*file)->close();
+ } while (*(++file));
+
+ if (first && m_added_file && m_added_file[0])
+ {
+ file= m_added_file;
+ first= FALSE;
+ goto repeat;
+ }
+
+ m_handler_status= handler_closed;
+ DBUG_RETURN(0);
+}
+
+/****************************************************************************
+ MODULE start/end statement
+****************************************************************************/
+/*
+ A number of methods to define various constants for the handler. In
+ the case of the partition handler we need to use some max and min
+ of the underlying handlers in most cases.
+*/
+
+/*
+ Set external locks on table
+
+ SYNOPSIS
+ external_lock()
+ thd Thread object
+ lock_type Type of external lock
+
+ RETURN VALUE
+ >0 Error code
+ 0 Success
+
+ DESCRIPTION
+ First you should go read the section "locking functions for mysql" in
+ lock.cc to understand this.
+ This create a lock on the table. If you are implementing a storage engine
+ that can handle transactions look at ha_berkeley.cc to see how you will
+ want to go about doing this. Otherwise you should consider calling
+ flock() here.
+ Originally this method was used to set locks on file level to enable
+ several MySQL Servers to work on the same data. For transactional
+ engines it has been "abused" to also mean start and end of statements
+ to enable proper rollback of statements and transactions. When LOCK
+ TABLES has been issued the start_stmt method takes over the role of
+ indicating start of statement but in this case there is no end of
+ statement indicator(?).
+
+ Called from lock.cc by lock_external() and unlock_external(). Also called
+ from sql_table.cc by copy_data_between_tables().
+*/
+
+int ha_partition::external_lock(THD *thd, int lock_type)
+{
+ bool first= TRUE;
+ uint error;
+ handler **file;
+ DBUG_ENTER("ha_partition::external_lock");
+
+ DBUG_ASSERT(!auto_increment_lock && !auto_increment_safe_stmt_log_lock);
+ file= m_file;
+ m_lock_type= lock_type;
+
+repeat:
+ do
+ {
+ DBUG_PRINT("info", ("external_lock(thd, %d) iteration %d",
+ lock_type, (int) (file - m_file)));
+ if ((error= (*file)->ha_external_lock(thd, lock_type)))
+ {
+ if (F_UNLCK != lock_type)
+ goto err_handler;
+ }
+ } while (*(++file));
+
+ if (first && m_added_file && m_added_file[0])
+ {
+ DBUG_ASSERT(lock_type == F_UNLCK);
+ file= m_added_file;
+ first= FALSE;
+ goto repeat;
+ }
+ DBUG_RETURN(0);
+
+err_handler:
+ while (file-- != m_file)
+ {
+ (*file)->ha_external_lock(thd, F_UNLCK);
+ }
+ DBUG_RETURN(error);
+}
+
+
+/*
+ Get the lock(s) for the table and perform conversion of locks if needed
+
+ SYNOPSIS
+ store_lock()
+ thd Thread object
+ to Lock object array
+ lock_type Table lock type
+
+ RETURN VALUE
+ >0 Error code
+ 0 Success
+
+ DESCRIPTION
+ The idea with handler::store_lock() is the following:
+
+ The statement decided which locks we should need for the table
+ for updates/deletes/inserts we get WRITE locks, for SELECT... we get
+ read locks.
+
+ Before adding the lock into the table lock handler (see thr_lock.c)
+ mysqld calls store lock with the requested locks. Store lock can now
+ modify a write lock to a read lock (or some other lock), ignore the
+ lock (if we don't want to use MySQL table locks at all) or add locks
+ for many tables (like we do when we are using a MERGE handler).
+
+ Berkeley DB for partition changes all WRITE locks to TL_WRITE_ALLOW_WRITE
+ (which signals that we are doing WRITES, but we are still allowing other
+ reader's and writer's.
+
+ When releasing locks, store_lock() is also called. In this case one
+ usually doesn't have to do anything.
+
+ store_lock is called when holding a global mutex to ensure that only
+ one thread at a time changes the locking information of tables.
+
+ In some exceptional cases MySQL may send a request for a TL_IGNORE;
+ This means that we are requesting the same lock as last time and this
+ should also be ignored. (This may happen when someone does a flush
+ table when we have opened a part of the tables, in which case mysqld
+ closes and reopens the tables and tries to get the same locks as last
+ time). In the future we will probably try to remove this.
+
+ Called from lock.cc by get_lock_data().
+*/
+
+THR_LOCK_DATA **ha_partition::store_lock(THD *thd,
+ THR_LOCK_DATA **to,
+ enum thr_lock_type lock_type)
+{
+ handler **file;
+ DBUG_ENTER("ha_partition::store_lock");
+ file= m_file;
+ do
+ {
+ DBUG_PRINT("info", ("store lock %d iteration", (int) (file - m_file)));
+ to= (*file)->store_lock(thd, to, lock_type);
+ } while (*(++file));
+ DBUG_RETURN(to);
+}
+
+/*
+ Start a statement when table is locked
+
+ SYNOPSIS
+ start_stmt()
+ thd Thread object
+ lock_type Type of external lock
+
+ RETURN VALUE
+ >0 Error code
+ 0 Success
+
+ DESCRIPTION
+ This method is called instead of external lock when the table is locked
+ before the statement is executed.
+*/
+
+int ha_partition::start_stmt(THD *thd, thr_lock_type lock_type)
+{
+ int error= 0;
+ handler **file;
+ DBUG_ENTER("ha_partition::start_stmt");
+
+ file= m_file;
+ do
+ {
+ if ((error= (*file)->start_stmt(thd, lock_type)))
+ break;
+ } while (*(++file));
+ DBUG_RETURN(error);
+}
+
+
+/*
+ Get number of lock objects returned in store_lock
+
+ SYNOPSIS
+ lock_count()
+
+ RETURN VALUE
+ Number of locks returned in call to store_lock
+
+ DESCRIPTION
+ Returns the number of store locks needed in call to store lock.
+ We return number of partitions since we call store_lock on each
+ underlying handler. Assists the above functions in allocating
+ sufficient space for lock structures.
+*/
+
+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);
+}
+
+
+/*
+ Unlock last accessed row
+
+ SYNOPSIS
+ unlock_row()
+
+ RETURN VALUE
+ NONE
+
+ DESCRIPTION
+ Record currently processed was not in the result set of the statement
+ and is thus unlocked. Used for UPDATE and DELETE queries.
+*/
+
+void ha_partition::unlock_row()
+{
+ DBUG_ENTER("ha_partition::unlock_row");
+ m_file[m_last_part]->unlock_row();
+ DBUG_VOID_RETURN;
+}
+
+/**
+ Check if semi consistent read was used
+
+ SYNOPSIS
+ was_semi_consistent_read()
+
+ RETURN VALUE
+ TRUE Previous read was a semi consistent read
+ FALSE Previous read was not a semi consistent read
+
+ DESCRIPTION
+ See handler.h:
+ In an UPDATE or DELETE, if the row under the cursor was locked by another
+ transaction, and the engine used an optimistic read of the last
+ committed row value under the cursor, then the engine returns 1 from this
+ function. MySQL must NOT try to update this optimistic value. If the
+ optimistic value does not match the WHERE condition, MySQL can decide to
+ skip over this row. Currently only works for InnoDB. This can be used to
+ avoid unnecessary lock waits.
+
+ If this method returns nonzero, it will also signal the storage
+ engine that the next read will be a locking re-read of the row.
+*/
+bool ha_partition::was_semi_consistent_read()
+{
+ DBUG_ENTER("ha_partition::was_semi_consistent_read");
+ DBUG_ASSERT(m_last_part < m_tot_parts &&
+ bitmap_is_set(&(m_part_info->used_partitions), m_last_part));
+ DBUG_RETURN(m_file[m_last_part]->was_semi_consistent_read());
+}
+
+/**
+ Use semi consistent read if possible
+
+ SYNOPSIS
+ try_semi_consistent_read()
+ yes Turn on semi consistent read
+
+ RETURN VALUE
+ NONE
+
+ DESCRIPTION
+ See handler.h:
+ Tell the engine whether it should avoid unnecessary lock waits.
+ If yes, in an UPDATE or DELETE, if the row under the cursor was locked
+ by another transaction, the engine may try an optimistic read of
+ the last committed row value under the cursor.
+ Note: prune_partitions are already called before this call, so using
+ pruning is OK.
+*/
+void ha_partition::try_semi_consistent_read(bool yes)
+{
+ handler **file;
+ DBUG_ENTER("ha_partition::try_semi_consistent_read");
+
+ for (file= m_file; *file; file++)
+ {
+ if (bitmap_is_set(&(m_part_info->used_partitions), (file - m_file)))
+ (*file)->try_semi_consistent_read(yes);
+ }
+ DBUG_VOID_RETURN;
+}
+
+
+/****************************************************************************
+ MODULE change record
+****************************************************************************/
+
+/*
+ Insert a row to the table
+
+ SYNOPSIS
+ write_row()
+ buf The row in MySQL Row Format
+
+ RETURN VALUE
+ >0 Error code
+ 0 Success
+
+ DESCRIPTION
+ write_row() inserts a row. buf() is a byte array of data, normally
+ record[0].
+
+ You can use the field information to extract the data from the native byte
+ array type.
+
+ Example of this would be:
+ for (Field **field=table->field ; *field ; field++)
+ {
+ ...
+ }
+
+ See ha_tina.cc for a variant of extracting all of the data as strings.
+ ha_berkeley.cc has a variant of how to store it intact by "packing" it
+ for ha_berkeley's own native storage type.
+
+ Called from item_sum.cc, item_sum.cc, sql_acl.cc, sql_insert.cc,
+ sql_insert.cc, sql_select.cc, sql_table.cc, sql_udf.cc, and sql_update.cc.
+
+ ADDITIONAL INFO:
+
+ We have to set timestamp fields and auto_increment fields, because those
+ may be used in determining which partition the row should be written to.
+*/
+
+int ha_partition::write_row(uchar * buf)
+{
+ uint32 part_id;
+ int error;
+ 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 orig_timestamp_type= table->timestamp_field_type;
+#ifdef NOT_NEEDED
+ uchar *rec0= m_rec0;
+#endif
+ DBUG_ENTER("ha_partition::write_row");
+ DBUG_ASSERT(buf == m_rec0);
+
+ /* If we have a timestamp column, update it to the current time */
+ if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_INSERT)
+ table->timestamp_field->set_time();
+ table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET;
+
+ /*
+ If we have an auto_increment column and we are writing a changed row
+ or a new row, then update the auto_increment value in the record.
+ */
+ if (have_auto_increment)
+ {
+ if (!ha_data->auto_inc_initialized &&
+ !table->s->next_number_keypart)
+ {
+ /*
+ If auto_increment in table_share is not initialized, start by
+ initializing it.
+ */
+ info(HA_STATUS_AUTO);
+ }
+ error= update_auto_increment();
+
+ /*
+ If we have failed to set the auto-increment value for this row,
+ it is highly likely that we will not be able to insert it into
+ the correct partition. We must check and fail if neccessary.
+ */
+ if (error)
+ goto exit;
+ }
+
+ old_map= dbug_tmp_use_all_columns(table, table->read_set);
+#ifdef NOT_NEEDED
+ if (likely(buf == rec0))
+#endif
+ error= m_part_info->get_partition_id(m_part_info, &part_id,
+ &func_value);
+#ifdef NOT_NEEDED
+ else
+ {
+ set_field_ptr(m_part_field_array, buf, rec0);
+ error= m_part_info->get_partition_id(m_part_info, &part_id,
+ &func_value);
+ set_field_ptr(m_part_field_array, rec0, buf);
+ }
+#endif
+ dbug_tmp_restore_column_map(table->read_set, old_map);
+ if (unlikely(error))
+ {
+ m_part_info->err_value= func_value;
+ goto exit;
+ }
+ m_last_part= part_id;
+ DBUG_PRINT("info", ("Insert in partition %d", part_id));
+ tmp_disable_binlog(thd); /* Do not replicate the low-level changes. */
+ error= m_file[part_id]->ha_write_row(buf);
+ if (have_auto_increment && !table->s->next_number_keypart)
+ set_auto_increment_if_higher(table->next_number_field->val_int());
+ reenable_binlog(thd);
+exit:
+ table->timestamp_field_type= orig_timestamp_type;
+ DBUG_RETURN(error);
+}
+
+
+/*
+ Update an existing row
+
+ SYNOPSIS
+ update_row()
+ old_data Old record in MySQL Row Format
+ new_data New record in MySQL Row Format
+
+ RETURN VALUE
+ >0 Error code
+ 0 Success
+
+ DESCRIPTION
+ Yes, update_row() does what you expect, it updates a row. old_data will
+ have the previous row record in it, while new_data will have the newest
+ data in it.
+ Keep in mind that the server can do updates based on ordering if an
+ ORDER BY clause was used. Consecutive ordering is not guarenteed.
+
+ 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
+*/
+
+int ha_partition::update_row(const uchar *old_data, uchar *new_data)
+{
+ THD *thd= ha_thd();
+ uint32 new_part_id, old_part_id;
+ int error= 0;
+ longlong func_value;
+ timestamp_auto_set_type orig_timestamp_type= table->timestamp_field_type;
+ DBUG_ENTER("ha_partition::update_row");
+
+ /*
+ We need to set timestamp field once before we calculate
+ the partition. Then we disable timestamp calculations
+ inside m_file[*]->update_row() methods
+ */
+ if (orig_timestamp_type & TIMESTAMP_AUTO_SET_ON_UPDATE)
+ table->timestamp_field->set_time();
+ table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET;
+
+ if ((error= get_parts_for_update(old_data, new_data, table->record[0],
+ m_part_info, &old_part_id, &new_part_id,
+ &func_value)))
+ {
+ m_part_info->err_value= func_value;
+ goto exit;
+ }
+
+ m_last_part= new_part_id;
+ if (new_part_id == old_part_id)
+ {
+ DBUG_PRINT("info", ("Update in partition %d", new_part_id));
+ tmp_disable_binlog(thd); /* Do not replicate the low-level changes. */
+ error= m_file[new_part_id]->ha_update_row(old_data, new_data);
+ reenable_binlog(thd);
+ goto exit;
+ }
+ else
+ {
+ DBUG_PRINT("info", ("Update from partition %d to partition %d",
+ old_part_id, new_part_id));
+ tmp_disable_binlog(thd); /* Do not replicate the low-level changes. */
+ error= m_file[new_part_id]->ha_write_row(new_data);
+ reenable_binlog(thd);
+ if (error)
+ goto exit;
+
+ tmp_disable_binlog(thd); /* Do not replicate the low-level changes. */
+ error= m_file[old_part_id]->ha_delete_row(old_data);
+ reenable_binlog(thd);
+ if (error)
+ {
+#ifdef IN_THE_FUTURE
+ (void) m_file[new_part_id]->delete_last_inserted_row(new_data);
+#endif
+ goto exit;
+ }
+ }
+
+exit:
+ /*
+ if updating an auto_increment column, update
+ table_share->ha_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
+ table->found_next_number_field instead.
+ */
+ if (table->found_next_number_field && new_data == table->record[0] &&
+ !table->s->next_number_keypart)
+ {
+ HA_DATA_PARTITION *ha_data= (HA_DATA_PARTITION*) table_share->ha_data;
+ if (!ha_data->auto_inc_initialized)
+ info(HA_STATUS_AUTO);
+ set_auto_increment_if_higher(table->found_next_number_field->val_int());
+ }
+ table->timestamp_field_type= orig_timestamp_type;
+ DBUG_RETURN(error);
+}
+
+
+/*
+ Remove an existing row
+
+ SYNOPSIS
+ delete_row
+ buf Deleted row in MySQL Row Format
+
+ RETURN VALUE
+ >0 Error Code
+ 0 Success
+
+ DESCRIPTION
+ This will delete a row. buf will contain a copy of the row to be deleted.
+ The server will call this right after the current row has been read
+ (from either a previous rnd_xxx() or index_xxx() call).
+ If you keep a pointer to the last row or can access a primary key it will
+ make doing the deletion quite a bit easier.
+ Keep in mind that the server does no guarentee consecutive deletions.
+ ORDER BY clauses can be used.
+
+ Called in sql_acl.cc and sql_udf.cc to manage internal table information.
+ Called in sql_delete.cc, sql_insert.cc, and sql_select.cc. In sql_select
+ it is used for removing duplicates while in insert it is used for REPLACE
+ calls.
+
+ buf is either record[0] or record[1]
+*/
+
+int ha_partition::delete_row(const uchar *buf)
+{
+ uint32 part_id;
+ int error;
+ THD *thd= ha_thd();
+ DBUG_ENTER("ha_partition::delete_row");
+
+ if ((error= get_part_for_delete(buf, m_rec0, m_part_info, &part_id)))
+ {
+ DBUG_RETURN(error);
+ }
+ m_last_part= part_id;
+ tmp_disable_binlog(thd);
+ error= m_file[part_id]->ha_delete_row(buf);
+ reenable_binlog(thd);
+ DBUG_RETURN(error);
+}
+
+
+/*
+ Delete all rows in a table
+
+ SYNOPSIS
+ delete_all_rows()
+
+ RETURN VALUE
+ >0 Error Code
+ 0 Success
+
+ DESCRIPTION
+ Used to delete all rows in a table. Both for cases of truncate and
+ for cases where the optimizer realizes that all rows will be
+ removed as a result of a SQL statement.
+
+ Called from item_sum.cc by Item_func_group_concat::clear(),
+ Item_sum_count_distinct::clear(), and Item_func_group_concat::clear().
+ Called from sql_delete.cc by mysql_delete().
+ Called from sql_select.cc by JOIN::reinit().
+ Called from sql_union.cc by st_select_lex_unit::exec().
+*/
+
+int ha_partition::delete_all_rows()
+{
+ int error;
+ 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();
+ }
+ file= m_file;
+ do
+ {
+ if ((error= (*file)->ha_delete_all_rows()))
+ DBUG_RETURN(error);
+ } while (*(++file));
+ DBUG_RETURN(0);
+}
+
+
+/*
+ Start a large batch of insert rows
+
+ SYNOPSIS
+ start_bulk_insert()
+ rows Number of rows to insert
+
+ RETURN VALUE
+ NONE
+
+ DESCRIPTION
+ rows == 0 means we will probably insert many rows
+*/
+
+void ha_partition::start_bulk_insert(ha_rows rows)
+{
+ handler **file;
+ DBUG_ENTER("ha_partition::start_bulk_insert");
+
+ rows= rows ? rows/m_tot_parts + 1 : 0;
+ file= m_file;
+ do
+ {
+ (*file)->ha_start_bulk_insert(rows);
+ } while (*(++file));
+ DBUG_VOID_RETURN;
+}
+
+
+/*
+ Finish a large batch of insert rows
+
+ SYNOPSIS
+ end_bulk_insert()
+
+ RETURN VALUE
+ >0 Error code
+ 0 Success
+*/
+
+int ha_partition::end_bulk_insert()
+{
+ int error= 0;
+ handler **file;
+ DBUG_ENTER("ha_partition::end_bulk_insert");
+
+ file= m_file;
+ do
+ {
+ int tmp;
+ if ((tmp= (*file)->ha_end_bulk_insert()))
+ error= tmp;
+ } while (*(++file));
+ DBUG_RETURN(error);
+}
+
+
+/****************************************************************************
+ MODULE full table scan
+****************************************************************************/
+/*
+ Initialize engine for random reads
+
+ SYNOPSIS
+ ha_partition::rnd_init()
+ scan 0 Initialize for random reads through rnd_pos()
+ 1 Initialize for random scan through rnd_next()
+
+ RETURN VALUE
+ >0 Error code
+ 0 Success
+
+ DESCRIPTION
+ rnd_init() is called when the server wants the storage engine to do a
+ table scan or when the server wants to access data through rnd_pos.
+
+ When scan is used we will scan one handler partition at a time.
+ When preparing for rnd_pos we will init all handler partitions.
+ No extra cache handling is needed when scannning is not performed.
+
+ Before initialising we will call rnd_end to ensure that we clean up from
+ any previous incarnation of a table scan.
+ Called from filesort.cc, records.cc, sql_handler.cc, sql_select.cc,
+ sql_table.cc, and sql_update.cc.
+*/
+
+int ha_partition::rnd_init(bool scan)
+{
+ int error;
+ uint i= 0;
+ uint32 part_id;
+ DBUG_ENTER("ha_partition::rnd_init");
+
+ /*
+ For operations that may need to change data, we may need to extend
+ read_set.
+ */
+ if (m_lock_type == F_WRLCK)
+ {
+ /*
+ If write_set contains any of the fields used in partition and
+ subpartition expression, we need to set all bits in read_set because
+ the row may need to be inserted in a different [sub]partition. In
+ other words update_row() can be converted into write_row(), which
+ requires a complete record.
+ */
+ if (bitmap_is_overlapping(&m_part_info->full_part_field_set,
+ table->write_set))
+ bitmap_set_all(table->read_set);
+ else
+ {
+ /*
+ Some handlers only read fields as specified by the bitmap for the
+ read set. For partitioned handlers we always require that the
+ fields of the partition functions are read such that we can
+ calculate the partition id to place updated and deleted records.
+ */
+ bitmap_union(table->read_set, &m_part_info->full_part_field_set);
+ }
+ }
+
+ /* Now we see what the index of our first important partition is */
+ DBUG_PRINT("info", ("m_part_info->used_partitions: 0x%lx",
+ (long) m_part_info->used_partitions.bitmap));
+ part_id= bitmap_get_first_set(&(m_part_info->used_partitions));
+ DBUG_PRINT("info", ("m_part_spec.start_part %d", part_id));
+
+ if (MY_BIT_NONE == part_id)
+ {
+ error= 0;
+ goto err1;
+ }
+
+ /*
+ We have a partition and we are scanning with rnd_next
+ so we bump our cache
+ */
+ DBUG_PRINT("info", ("rnd_init on partition %d", part_id));
+ if (scan)
+ {
+ /*
+ rnd_end() is needed for partitioning to reset internal data if scan
+ is already in use
+ */
+ rnd_end();
+ late_extra_cache(part_id);
+ if ((error= m_file[part_id]->ha_rnd_init(scan)))
+ goto err;
+ }
+ else
+ {
+ for (i= part_id; i < m_tot_parts; i++)
+ {
+ if (bitmap_is_set(&(m_part_info->used_partitions), i))
+ {
+ if ((error= m_file[i]->ha_rnd_init(scan)))
+ goto err;
+ }
+ }
+ }
+ m_scan_value= scan;
+ m_part_spec.start_part= part_id;
+ m_part_spec.end_part= m_tot_parts - 1;
+ DBUG_PRINT("info", ("m_scan_value=%d", m_scan_value));
+ DBUG_RETURN(0);
+
+err:
+ while ((int)--i >= (int)part_id)
+ {
+ if (bitmap_is_set(&(m_part_info->used_partitions), i))
+ m_file[i]->ha_rnd_end();
+ }
+err1:
+ m_scan_value= 2;
+ m_part_spec.start_part= NO_CURRENT_PART_ID;
+ DBUG_RETURN(error);
+}
+
+
+/*
+ End of a table scan
+
+ SYNOPSIS
+ rnd_end()
+
+ RETURN VALUE
+ >0 Error code
+ 0 Success
+*/
+
+int ha_partition::rnd_end()
+{
+ handler **file;
+ DBUG_ENTER("ha_partition::rnd_end");
+ switch (m_scan_value) {
+ case 2: // Error
+ break;
+ case 1:
+ if (NO_CURRENT_PART_ID != m_part_spec.start_part) // Table scan
+ {
+ late_extra_no_cache(m_part_spec.start_part);
+ m_file[m_part_spec.start_part]->ha_rnd_end();
+ }
+ break;
+ case 0:
+ file= m_file;
+ do
+ {
+ if (bitmap_is_set(&(m_part_info->used_partitions), (file - m_file)))
+ (*file)->ha_rnd_end();
+ } while (*(++file));
+ break;
+ }
+ m_scan_value= 2;
+ m_part_spec.start_part= NO_CURRENT_PART_ID;
+ DBUG_RETURN(0);
+}
+
+/*
+ read next row during full table scan (scan in random row order)
+
+ SYNOPSIS
+ rnd_next()
+ buf buffer that should be filled with data
+
+ RETURN VALUE
+ >0 Error code
+ 0 Success
+
+ DESCRIPTION
+ This is called for each row of the table scan. When you run out of records
+ you should return HA_ERR_END_OF_FILE.
+ The Field structure for the table is the key to getting data into buf
+ in a manner that will allow the server to understand it.
+
+ Called from filesort.cc, records.cc, sql_handler.cc, sql_select.cc,
+ sql_table.cc, and sql_update.cc.
+*/
+
+int ha_partition::rnd_next(uchar *buf)
+{
+ handler *file;
+ int result= HA_ERR_END_OF_FILE;
+ uint part_id= m_part_spec.start_part;
+ DBUG_ENTER("ha_partition::rnd_next");
+
+ if (NO_CURRENT_PART_ID == part_id)
+ {
+ /*
+ The original set of partitions to scan was empty and thus we report
+ the result here.
+ */
+ goto end;
+ }
+
+ DBUG_ASSERT(m_scan_value == 1);
+ file= m_file[part_id];
+
+ while (TRUE)
+ {
+ result= file->rnd_next(buf);
+ if (!result)
+ {
+ m_last_part= part_id;
+ m_part_spec.start_part= part_id;
+ table->status= 0;
+ DBUG_RETURN(0);
+ }
+
+ /*
+ if we get here, then the current partition rnd_next returned failure
+ */
+ if (result == HA_ERR_RECORD_DELETED)
+ continue; // Probably MyISAM
+
+ if (result != HA_ERR_END_OF_FILE)
+ goto end_dont_reset_start_part; // Return error
+
+ /* End current partition */
+ late_extra_no_cache(part_id);
+ DBUG_PRINT("info", ("rnd_end on partition %d", part_id));
+ if ((result= file->ha_rnd_end()))
+ break;
+
+ /* Shift to next partition */
+ while (++part_id < m_tot_parts &&
+ !bitmap_is_set(&(m_part_info->used_partitions), part_id))
+ ;
+ if (part_id >= m_tot_parts)
+ {
+ result= HA_ERR_END_OF_FILE;
+ break;
+ }
+ m_last_part= part_id;
+ m_part_spec.start_part= part_id;
+ file= m_file[part_id];
+ DBUG_PRINT("info", ("rnd_init on partition %d", part_id));
+ if ((result= file->ha_rnd_init(1)))
+ break;
+ late_extra_cache(part_id);
+ }
+
+end:
+ m_part_spec.start_part= NO_CURRENT_PART_ID;
+end_dont_reset_start_part:
+ table->status= STATUS_NOT_FOUND;
+ DBUG_RETURN(result);
+}
+
+
+/*
+ Save position of current row
+
+ SYNOPSIS
+ position()
+ record Current record in MySQL Row Format
+
+ RETURN VALUE
+ NONE
+
+ DESCRIPTION
+ position() is called after each call to rnd_next() if the data needs
+ to be ordered. You can do something like the following to store
+ the position:
+ ha_store_ptr(ref, ref_length, current_position);
+
+ The server uses ref to store data. ref_length in the above case is
+ the size needed to store current_position. ref is just a byte array
+ that the server will maintain. If you are using offsets to mark rows, then
+ current_position should be the offset. If it is a primary key like in
+ BDB, then it needs to be a primary key.
+
+ Called from filesort.cc, sql_select.cc, sql_delete.cc and sql_update.cc.
+*/
+
+void ha_partition::position(const uchar *record)
+{
+ handler *file= m_file[m_last_part];
+ DBUG_ENTER("ha_partition::position");
+
+ file->position(record);
+ int2store(ref, m_last_part);
+ memcpy((ref + PARTITION_BYTES_IN_POS), file->ref,
+ (ref_length - PARTITION_BYTES_IN_POS));
+
+#ifdef SUPPORTING_PARTITION_OVER_DIFFERENT_ENGINES
+#ifdef HAVE_purify
+ bzero(ref + PARTITION_BYTES_IN_POS + ref_length,
+ max_ref_length-ref_length);
+#endif /* HAVE_purify */
+#endif
+ DBUG_VOID_RETURN;
+}
+
+
+void ha_partition::column_bitmaps_signal()
+{
+ handler::column_bitmaps_signal();
+ bitmap_union(table->read_set, &m_part_info->full_part_field_set);
+}
+
+
+/*
+ Read row using position
+
+ SYNOPSIS
+ rnd_pos()
+ out:buf Row read in MySQL Row Format
+ position Position of read row
+
+ RETURN VALUE
+ >0 Error code
+ 0 Success
+
+ DESCRIPTION
+ This is like rnd_next, but you are given a position to use
+ to determine the row. The position will be of the type that you stored in
+ ref. You can use ha_get_ptr(pos,ref_length) to retrieve whatever key
+ or position you saved when position() was called.
+ Called from filesort.cc records.cc sql_insert.cc sql_select.cc
+ sql_update.cc.
+*/
+
+int ha_partition::rnd_pos(uchar * buf, uchar *pos)
+{
+ uint part_id;
+ handler *file;
+ DBUG_ENTER("ha_partition::rnd_pos");
+
+ part_id= uint2korr((const uchar *) pos);
+ DBUG_ASSERT(part_id < m_tot_parts);
+ file= m_file[part_id];
+ m_last_part= part_id;
+ DBUG_RETURN(file->rnd_pos(buf, (pos + PARTITION_BYTES_IN_POS)));
+}
+
+
+/*
+ Read row using position using given record to find
+
+ SYNOPSIS
+ rnd_pos_by_record()
+ record Current record in MySQL Row Format
+
+ RETURN VALUE
+ >0 Error code
+ 0 Success
+
+ DESCRIPTION
+ this works as position()+rnd_pos() functions, but does some extra work,
+ calculating m_last_part - the partition to where the 'record'
+ should go.
+
+ called from replication (log_event.cc)
+*/
+
+int ha_partition::rnd_pos_by_record(uchar *record)
+{
+ DBUG_ENTER("ha_partition::rnd_pos_by_record");
+
+ if (unlikely(get_part_for_delete(record, m_rec0, m_part_info, &m_last_part)))
+ DBUG_RETURN(1);
+
+ DBUG_RETURN(handler::rnd_pos_by_record(record));
+}
+
+
+/****************************************************************************
+ MODULE index scan
+****************************************************************************/
+/*
+ Positions an index cursor to the index specified in the handle. Fetches the
+ row if available. If the key value is null, begin at the first key of the
+ index.
+
+ There are loads of optimisations possible here for the partition handler.
+ The same optimisations can also be checked for full table scan although
+ only through conditions and not from index ranges.
+ Phase one optimisations:
+ Check if the fields of the partition function are bound. If so only use
+ the single partition it becomes bound to.
+ Phase two optimisations:
+ If it can be deducted through range or list partitioning that only a
+ subset of the partitions are used, then only use those partitions.
+*/
+
+/*
+ Initialize handler before start of index scan
+
+ SYNOPSIS
+ index_init()
+ inx Index number
+ sorted Is rows to be returned in sorted order
+
+ RETURN VALUE
+ >0 Error code
+ 0 Success
+
+ DESCRIPTION
+ index_init is always called before starting index scans (except when
+ starting through index_read_idx and using read_range variants).
+*/
+
+int ha_partition::index_init(uint inx, bool sorted)
+{
+ int error= 0;
+ handler **file;
+ DBUG_ENTER("ha_partition::index_init");
+
+ DBUG_PRINT("info", ("inx %u sorted %u", inx, sorted));
+ active_index= inx;
+ m_part_spec.start_part= NO_CURRENT_PART_ID;
+ m_start_key.length= 0;
+ m_ordered= sorted;
+ m_curr_key_info[0]= table->key_info+inx;
+ if (m_pkey_is_clustered && table->s->primary_key != MAX_KEY)
+ {
+ /*
+ if PK is clustered, then the key cmp must use the pk to
+ differentiate between equal key in given index.
+ */
+ 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;
+ }
+ else
+ m_curr_key_info[1]= NULL;
+ /*
+ Some handlers only read fields as specified by the bitmap for the
+ read set. For partitioned handlers we always require that the
+ fields of the partition functions are read such that we can
+ calculate the partition id to place updated and deleted records.
+ But this is required for operations that may need to change data only.
+ */
+ if (m_lock_type == F_WRLCK)
+ bitmap_union(table->read_set, &m_part_info->full_part_field_set);
+ else if (sorted)
+ {
+ /*
+ An ordered scan is requested. We must make sure all fields of the
+ used index are in the read set, as partitioning requires them for
+ sorting (see ha_partition::handle_ordered_index_scan).
+
+ The SQL layer may request an ordered index scan without having index
+ fields in the read set when
+ - it needs to do an ordered scan over an index prefix.
+ - it evaluates ORDER BY with SELECT COUNT(*) FROM t1.
+
+ TODO: handle COUNT(*) queries via unordered scan.
+ */
+ uint i;
+ KEY **key_info= m_curr_key_info;
+ do
+ {
+ for (i= 0; i < (*key_info)->key_parts; i++)
+ bitmap_set_bit(table->read_set,
+ (*key_info)->key_part[i].field->field_index);
+ } while (*(++key_info));
+ }
+ 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)))
+ {
+ DBUG_ASSERT(0); // Should never happen
+ break;
+ }
+ } while (*(++file));
+ DBUG_RETURN(error);
+}
+
+
+/*
+ End of index scan
+
+ SYNOPSIS
+ index_end()
+
+ RETURN VALUE
+ >0 Error code
+ 0 Success
+
+ DESCRIPTION
+ index_end is called at the end of an index scan to clean up any
+ things needed to clean up.
+*/
+
+int ha_partition::index_end()
+{
+ int error= 0;
+ handler **file;
+ DBUG_ENTER("ha_partition::index_end");
+
+ active_index= MAX_KEY;
+ m_part_spec.start_part= NO_CURRENT_PART_ID;
+ file= m_file;
+ do
+ {
+ int tmp;
+ /* TODO RONM: Change to index_end() when code is stable */
+ if (bitmap_is_set(&(m_part_info->used_partitions), (file - m_file)))
+ if ((tmp= (*file)->ha_index_end()))
+ error= tmp;
+ } while (*(++file));
+ DBUG_RETURN(error);
+}
+
+
+/*
+ Read one record in an index scan and start an index scan
+
+ SYNOPSIS
+ index_read_map()
+ buf Read row in MySQL Row Format
+ key Key parts in consecutive order
+ keypart_map Which part of key is used
+ find_flag What type of key condition is used
+
+ RETURN VALUE
+ >0 Error code
+ 0 Success
+
+ DESCRIPTION
+ index_read_map starts a new index scan using a start key. The MySQL Server
+ will check the end key on its own. Thus to function properly the
+ partitioned handler need to ensure that it delivers records in the sort
+ order of the MySQL Server.
+ index_read_map can be restarted without calling index_end on the previous
+ index scan and without calling index_init. In this case the index_read_map
+ is on the same index as the previous index_scan. This is particularly
+ used in conjuntion with multi read ranges.
+*/
+
+int ha_partition::index_read_map(uchar *buf, const uchar *key,
+ key_part_map keypart_map,
+ enum ha_rkey_function find_flag)
+{
+ DBUG_ENTER("ha_partition::index_read_map");
+ end_range= 0;
+ m_index_scan_type= partition_index_read;
+ m_start_key.key= key;
+ m_start_key.keypart_map= keypart_map;
+ m_start_key.flag= find_flag;
+ DBUG_RETURN(common_index_read(buf, TRUE));
+}
+
+
+/*
+ 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)
+
+ DESCRIPTION
+ Start scanning the range (when invoked from read_range_first()) or doing
+ an index lookup (when invoked from index_read_XXX):
+ - If possible, perform partition selection
+ - Find the set of partitions we're going to use
+ - Depending on whether we need ordering:
+ NO: Get the first record from first used partition (see
+ 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;
+ 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 m_ordered_scan_ong %u have_start_key %u",
+ m_ordered, m_ordered_scan_ongoing, have_start_key));
+
+ 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_ASSERT(key_len);
+ }
+ if ((error= partition_scan_set_up(buf, have_start_key)))
+ {
+ DBUG_RETURN(error);
+ }
+
+ if (have_start_key &&
+ (m_start_key.flag == HA_READ_PREFIX_LAST ||
+ m_start_key.flag == HA_READ_PREFIX_LAST_OR_PREV ||
+ m_start_key.flag == HA_READ_BEFORE_KEY))
+ {
+ reverse_order= TRUE;
+ m_ordered_scan_ongoing= TRUE;
+ }
+ 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))
+ {
+ /*
+ 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 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
+ {
+ /*
+ In all other cases we will use the ordered index scan. This will use
+ the partition set created by the get_partition_set method.
+ */
+ error= handle_ordered_index_scan(buf, reverse_order);
+ }
+ DBUG_RETURN(error);
+}
+
+
+/*
+ Start an index scan from leftmost record and return first record
+
+ SYNOPSIS
+ index_first()
+ buf Read row in MySQL Row Format
+
+ RETURN VALUE
+ >0 Error code
+ 0 Success
+
+ DESCRIPTION
+ index_first() asks for the first key in the index.
+ This is similar to index_read except that there is no start key since
+ the scan starts from the leftmost entry and proceeds forward with
+ index_next.
+
+ Called from opt_range.cc, opt_sum.cc, sql_handler.cc,
+ and sql_select.cc.
+*/
+
+int ha_partition::index_first(uchar * buf)
+{
+ DBUG_ENTER("ha_partition::index_first");
+
+ end_range= 0;
+ m_index_scan_type= partition_index_first;
+ DBUG_RETURN(common_first_last(buf));
+}
+
+
+/*
+ Start an index scan from rightmost record and return first record
+
+ SYNOPSIS
+ index_last()
+ buf Read row in MySQL Row Format
+
+ RETURN VALUE
+ >0 Error code
+ 0 Success
+
+ DESCRIPTION
+ index_last() asks for the last key in the index.
+ This is similar to index_read except that there is no start key since
+ the scan starts from the rightmost entry and proceeds forward with
+ index_prev.
+
+ Called from opt_range.cc, opt_sum.cc, sql_handler.cc,
+ and sql_select.cc.
+*/
+
+int ha_partition::index_last(uchar * buf)
+{
+ DBUG_ENTER("ha_partition::index_last");
+
+ m_index_scan_type= partition_index_last;
+ DBUG_RETURN(common_first_last(buf));
+}
+
+/*
+ Common routine for index_first/index_last
+
+ SYNOPSIS
+ ha_partition::common_first_last()
+
+ see index_first for rest
+*/
+
+int ha_partition::common_first_last(uchar *buf)
+{
+ int error;
+
+ if ((error= partition_scan_set_up(buf, FALSE)))
+ return error;
+ if (!m_ordered_scan_ongoing &&
+ m_index_scan_type != partition_index_last)
+ return handle_unordered_scan_next_partition(buf);
+ return handle_ordered_index_scan(buf, FALSE);
+}
+
+
+/*
+ Read last using key
+
+ SYNOPSIS
+ index_read_last_map()
+ buf Read row in MySQL Row Format
+ key Key
+ keypart_map Which part of key is used
+
+ RETURN VALUE
+ >0 Error code
+ 0 Success
+
+ DESCRIPTION
+ This is used in join_read_last_key to optimise away an ORDER BY.
+ Can only be used on indexes supporting HA_READ_ORDER
+*/
+
+int ha_partition::index_read_last_map(uchar *buf, const uchar *key,
+ key_part_map keypart_map)
+{
+ DBUG_ENTER("ha_partition::index_read_last");
+
+ m_ordered= TRUE; // Safety measure
+ end_range= 0;
+ m_index_scan_type= partition_index_read_last;
+ m_start_key.key= key;
+ m_start_key.keypart_map= keypart_map;
+ m_start_key.flag= HA_READ_PREFIX_LAST;
+ DBUG_RETURN(common_index_read(buf, TRUE));
+}
+
+
+/*
+ Read next record in a forward index scan
+
+ SYNOPSIS
+ index_next()
+ buf Read row in MySQL Row Format
+
+ RETURN VALUE
+ >0 Error code
+ 0 Success
+
+ DESCRIPTION
+ Used to read forward through the index.
+*/
+
+int ha_partition::index_next(uchar * buf)
+{
+ DBUG_ENTER("ha_partition::index_next");
+
+ /*
+ 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()
+ */
+ DBUG_ASSERT(m_index_scan_type != partition_index_last);
+ if (!m_ordered_scan_ongoing)
+ {
+ DBUG_RETURN(handle_unordered_next(buf, FALSE));
+ }
+ DBUG_RETURN(handle_ordered_next(buf, FALSE));
+}
+
+
+/*
+ Read next record special
+
+ SYNOPSIS
+ index_next_same()
+ buf Read row in MySQL Row Format
+ key Key
+ keylen Length of key
+
+ RETURN VALUE
+ >0 Error code
+ 0 Success
+
+ DESCRIPTION
+ This routine is used to read the next but only if the key is the same
+ as supplied in the call.
+*/
+
+int ha_partition::index_next_same(uchar *buf, const uchar *key, uint keylen)
+{
+ DBUG_ENTER("ha_partition::index_next_same");
+
+ DBUG_ASSERT(keylen == m_start_key.length);
+ DBUG_ASSERT(m_index_scan_type != partition_index_last);
+ if (!m_ordered_scan_ongoing)
+ DBUG_RETURN(handle_unordered_next(buf, TRUE));
+ DBUG_RETURN(handle_ordered_next(buf, TRUE));
+}
+
+
+/*
+ Read next record when performing index scan backwards
+
+ SYNOPSIS
+ index_prev()
+ buf Read row in MySQL Row Format
+
+ RETURN VALUE
+ >0 Error code
+ 0 Success
+
+ DESCRIPTION
+ Used to read backwards through the index.
+*/
+
+int ha_partition::index_prev(uchar * buf)
+{
+ DBUG_ENTER("ha_partition::index_prev");
+
+ /* TODO: read comment in index_next */
+ DBUG_ASSERT(m_index_scan_type != partition_index_first);
+ DBUG_RETURN(handle_ordered_prev(buf));
+}
+
+
+/*
+ Start a read of one range with start and end key
+
+ SYNOPSIS
+ read_range_first()
+ start_key Specification of start key
+ end_key Specification of end key
+ eq_range_arg Is it equal range
+ sorted Should records be returned in sorted order
+
+ RETURN VALUE
+ >0 Error code
+ 0 Success
+
+ DESCRIPTION
+ We reimplement read_range_first since we don't want the compare_key
+ check at the end. This is already performed in the partition handler.
+ read_range_next is very much different due to that we need to scan
+ all underlying handlers.
+*/
+
+int ha_partition::read_range_first(const key_range *start_key,
+ const key_range *end_key,
+ bool eq_range_arg, bool sorted)
+{
+ int error;
+ DBUG_ENTER("ha_partition::read_range_first");
+
+ m_ordered= sorted;
+ eq_range= eq_range_arg;
+ end_range= 0;
+ if (end_key)
+ {
+ end_range= &save_end_range;
+ save_end_range= *end_key;
+ key_compare_result_on_equal=
+ ((end_key->flag == HA_READ_BEFORE_KEY) ? 1 :
+ (end_key->flag == HA_READ_AFTER_KEY) ? -1 : 0);
+ }
+
+ range_key_part= m_curr_key_info[0]->key_part;
+ if (start_key)
+ m_start_key= *start_key;
+ else
+ m_start_key.key= NULL;
+
+ m_index_scan_type= partition_read_range;
+ error= common_index_read(m_rec0, test(start_key));
+ DBUG_RETURN(error);
+}
+
+
+/*
+ Read next record in read of a range with start and end key
+
+ SYNOPSIS
+ read_range_next()
+
+ RETURN VALUE
+ >0 Error code
+ 0 Success
+*/
+
+int ha_partition::read_range_next()
+{
+ DBUG_ENTER("ha_partition::read_range_next");
+
+ if (m_ordered_scan_ongoing)
+ {
+ DBUG_RETURN(handle_ordered_next(table->record[0], eq_range));
+ }
+ DBUG_RETURN(handle_unordered_next(table->record[0], eq_range));
+}
+
+
+/*
+ Common routine to set up index scans
+
+ SYNOPSIS
+ ha_partition::partition_scan_set_up()
+ buf Buffer to later return record in (this function
+ needs it to calculcate partitioning function
+ values)
+
+ idx_read_flag TRUE <=> m_start_key has range start endpoint which
+ probably can be used to determine the set of partitions
+ to scan.
+ FALSE <=> there is no start endpoint.
+
+ DESCRIPTION
+ Find out which partitions we'll need to read when scanning the specified
+ range.
+
+ If we need to scan only one partition, set m_ordered_scan_ongoing=FALSE
+ as we will not need to do merge ordering.
+
+ RETURN VALUE
+ >0 Error code
+ 0 Success
+*/
+
+int ha_partition::partition_scan_set_up(uchar * buf, bool idx_read_flag)
+{
+ DBUG_ENTER("ha_partition::partition_scan_set_up");
+
+ if (idx_read_flag)
+ get_partition_set(table,buf,active_index,&m_start_key,&m_part_spec);
+ else
+ {
+ m_part_spec.start_part= 0;
+ m_part_spec.end_part= m_tot_parts - 1;
+ }
+ if (m_part_spec.start_part > m_part_spec.end_part)
+ {
+ /*
+ We discovered a partition set but the set was empty so we report
+ key not found.
+ */
+ DBUG_PRINT("info", ("scan with no partition to scan"));
+ DBUG_RETURN(HA_ERR_END_OF_FILE);
+ }
+ if (m_part_spec.start_part == m_part_spec.end_part)
+ {
+ /*
+ We discovered a single partition to scan, this never needs to be
+ performed using the ordered index scan.
+ */
+ DBUG_PRINT("info", ("index scan using the single partition %d",
+ m_part_spec.start_part));
+ m_ordered_scan_ongoing= FALSE;
+ }
+ else
+ {
+ /*
+ Set m_ordered_scan_ongoing according how the scan should be done
+ Only exact partitions are discovered atm by get_partition_set.
+ Verify this, also bitmap must have at least one bit set otherwise
+ the result from this table is the empty set.
+ */
+ uint start_part= bitmap_get_first_set(&(m_part_info->used_partitions));
+ if (start_part == MY_BIT_NONE)
+ {
+ DBUG_PRINT("info", ("scan with no partition to scan"));
+ DBUG_RETURN(HA_ERR_END_OF_FILE);
+ }
+ if (start_part > m_part_spec.start_part)
+ m_part_spec.start_part= start_part;
+ DBUG_ASSERT(m_part_spec.start_part < m_tot_parts);
+ m_ordered_scan_ongoing= m_ordered;
+ }
+ DBUG_ASSERT(m_part_spec.start_part < m_tot_parts &&
+ m_part_spec.end_part < m_tot_parts);
+ DBUG_RETURN(0);
+}
+
+
+/****************************************************************************
+ Unordered Index Scan Routines
+****************************************************************************/
+/*
+ Common routine to handle index_next with unordered results
+
+ SYNOPSIS
+ handle_unordered_next()
+ out:buf Read row in MySQL Row Format
+ next_same Called from index_next_same
+
+ RETURN VALUE
+ HA_ERR_END_OF_FILE End of scan
+ 0 Success
+ other Error code
+
+ DESCRIPTION
+ These routines are used to scan partitions without considering order.
+ This is performed in two situations.
+ 1) In read_multi_range this is the normal case
+ 2) When performing any type of index_read, index_first, index_last where
+ all fields in the partition function is bound. In this case the index
+ scan is performed on only one partition and thus it isn't necessary to
+ perform any sort.
+*/
+
+int ha_partition::handle_unordered_next(uchar *buf, bool is_next_same)
+{
+ handler *file= m_file[m_part_spec.start_part];
+ int error;
+ DBUG_ENTER("ha_partition::handle_unordered_next");
+
+ /*
+ We should consider if this should be split into three functions as
+ partition_read_range is_next_same are always local constants
+ */
+
+ if (m_index_scan_type == partition_read_range)
+ {
+ if (!(error= file->read_range_next()))
+ {
+ m_last_part= m_part_spec.start_part;
+ DBUG_RETURN(0);
+ }
+ }
+ else if (is_next_same)
+ {
+ if (!(error= file->index_next_same(buf, m_start_key.key,
+ m_start_key.length)))
+ {
+ m_last_part= m_part_spec.start_part;
+ DBUG_RETURN(0);
+ }
+ }
+ else
+ {
+ if (!(error= file->index_next(buf)))
+ {
+ m_last_part= m_part_spec.start_part;
+ DBUG_RETURN(0); // Row was in range
+ }
+ }
+
+ if (error == HA_ERR_END_OF_FILE)
+ {
+ m_part_spec.start_part++; // Start using next part
+ error= handle_unordered_scan_next_partition(buf);
+ }
+ DBUG_RETURN(error);
+}
+
+
+/*
+ Handle index_next when changing to new partition
+
+ SYNOPSIS
+ handle_unordered_scan_next_partition()
+ buf Read row in MySQL Row Format
+
+ RETURN VALUE
+ HA_ERR_END_OF_FILE End of scan
+ 0 Success
+ other Error code
+
+ DESCRIPTION
+ This routine is used to start the index scan on the next partition.
+ Both initial start and after completing scan on one partition.
+*/
+
+int ha_partition::handle_unordered_scan_next_partition(uchar * buf)
+{
+ uint i;
+ DBUG_ENTER("ha_partition::handle_unordered_scan_next_partition");
+
+ for (i= m_part_spec.start_part; i <= m_part_spec.end_part; i++)
+ {
+ int error;
+ handler *file;
+
+ if (!(bitmap_is_set(&(m_part_info->used_partitions), i)))
+ continue;
+ file= m_file[i];
+ m_part_spec.start_part= i;
+ switch (m_index_scan_type) {
+ case partition_read_range:
+ DBUG_PRINT("info", ("read_range_first on partition %d", i));
+ error= file->read_range_first(m_start_key.key? &m_start_key: NULL,
+ end_range, eq_range, FALSE);
+ break;
+ case partition_index_read:
+ DBUG_PRINT("info", ("index_read on partition %d", i));
+ error= file->index_read_map(buf, m_start_key.key,
+ m_start_key.keypart_map,
+ m_start_key.flag);
+ break;
+ case partition_index_first:
+ DBUG_PRINT("info", ("index_first on partition %d", i));
+ /* MyISAM engine can fail if we call index_first() when indexes disabled */
+ /* that happens if the table is empty. */
+ /* Here we use file->stats.records instead of file->records() because */
+ /* file->records() is supposed to return an EXACT count, and it can be */
+ /* possibly slow. We don't need an exact number, an approximate one- from*/
+ /* the last ::info() call - is sufficient. */
+ if (file->stats.records == 0)
+ {
+ error= HA_ERR_END_OF_FILE;
+ break;
+ }
+ error= file->index_first(buf);
+ break;
+ case partition_index_first_unordered:
+ /*
+ We perform a scan without sorting and this means that we
+ should not use the index_first since not all handlers
+ support it and it is also unnecessary to restrict sort
+ order.
+ */
+ DBUG_PRINT("info", ("read_range_first on partition %d", i));
+ table->record[0]= buf;
+ error= file->read_range_first(0, end_range, eq_range, 0);
+ table->record[0]= m_rec0;
+ break;
+ default:
+ DBUG_ASSERT(FALSE);
+ DBUG_RETURN(1);
+ }
+ if (!error)
+ {
+ m_last_part= i;
+ DBUG_RETURN(0);
+ }
+ 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));
+ }
+ m_part_spec.start_part= NO_CURRENT_PART_ID;
+ DBUG_RETURN(HA_ERR_END_OF_FILE);
+}
+
+
+/*
+ Common routine to start index scan with ordered results
+
+ SYNOPSIS
+ handle_ordered_index_scan()
+ out:buf Read row in MySQL Row Format
+
+ RETURN VALUE
+ HA_ERR_END_OF_FILE End of scan
+ 0 Success
+ other Error code
+
+ DESCRIPTION
+ 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
+ index_first and index_last.
+
+ We implement ordering by keeping one record plus a key buffer for each
+ partition. Every time a new entry is requested we will fetch a new
+ entry from the partition that is currently not filled with an entry.
+ Then the entry is put into its proper sort position.
+
+ Returning a record is done by getting the top record, copying the
+ record to the request buffer and setting the partition as empty on
+ entries.
+*/
+
+int ha_partition::handle_ordered_index_scan(uchar *buf, bool reverse_order)
+{
+ uint i;
+ uint j= 0;
+ bool found= FALSE;
+ DBUG_ENTER("ha_partition::handle_ordered_index_scan");
+
+ m_top_entry= NO_CURRENT_PART_ID;
+ queue_remove_all(&m_queue);
+
+ DBUG_PRINT("info", ("m_part_spec.start_part %d", m_part_spec.start_part));
+ for (i= m_part_spec.start_part; i <= m_part_spec.end_part; i++)
+ {
+ if (!(bitmap_is_set(&(m_part_info->used_partitions), i)))
+ continue;
+ uchar *rec_buf_ptr= rec_buf(i);
+ int error;
+ handler *file= m_file[i];
+
+ switch (m_index_scan_type) {
+ case partition_index_read:
+ error= file->index_read_map(rec_buf_ptr,
+ m_start_key.key,
+ m_start_key.keypart_map,
+ m_start_key.flag);
+ break;
+ case partition_index_first:
+ /* MyISAM engine can fail if we call index_first() when indexes disabled */
+ /* that happens if the table is empty. */
+ /* Here we use file->stats.records instead of file->records() because */
+ /* file->records() is supposed to return an EXACT count, and it can be */
+ /* possibly slow. We don't need an exact number, an approximate one- from*/
+ /* the last ::info() call - is sufficient. */
+ if (file->stats.records == 0)
+ {
+ error= HA_ERR_END_OF_FILE;
+ break;
+ }
+ error= file->index_first(rec_buf_ptr);
+ reverse_order= FALSE;
+ break;
+ case partition_index_last:
+ /* MyISAM engine can fail if we call index_last() when indexes disabled */
+ /* that happens if the table is empty. */
+ /* Here we use file->stats.records instead of file->records() because */
+ /* file->records() is supposed to return an EXACT count, and it can be */
+ /* possibly slow. We don't need an exact number, an approximate one- from*/
+ /* the last ::info() call - is sufficient. */
+ if (file->stats.records == 0)
+ {
+ error= HA_ERR_END_OF_FILE;
+ break;
+ }
+ error= file->index_last(rec_buf_ptr);
+ reverse_order= TRUE;
+ break;
+ case partition_index_read_last:
+ error= file->index_read_last_map(rec_buf_ptr,
+ m_start_key.key,
+ m_start_key.keypart_map);
+ reverse_order= TRUE;
+ break;
+ case partition_read_range:
+ {
+ /*
+ This can only read record to table->record[0], as it was set when
+ the table was being opened. We have to memcpy data ourselves.
+ */
+ error= file->read_range_first(m_start_key.key? &m_start_key: NULL,
+ end_range, eq_range, TRUE);
+ memcpy(rec_buf_ptr, table->record[0], m_rec_length);
+ reverse_order= FALSE;
+ break;
+ }
+ default:
+ DBUG_ASSERT(FALSE);
+ DBUG_RETURN(HA_ERR_END_OF_FILE);
+ }
+ if (!error)
+ {
+ found= TRUE;
+ /*
+ Initialize queue without order first, simply insert
+ */
+ queue_element(&m_queue, j++)= (uchar*)queue_buf(i);
+ }
+ else if (error != HA_ERR_KEY_NOT_FOUND && error != HA_ERR_END_OF_FILE)
+ {
+ DBUG_RETURN(error);
+ }
+ }
+ if (found)
+ {
+ /*
+ We found at least one partition with data, now sort all entries and
+ 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);
+ m_queue.elements= j;
+ queue_fix(&m_queue);
+ return_top_record(buf);
+ table->status= 0;
+ DBUG_PRINT("info", ("Record returned from partition %d", m_top_entry));
+ DBUG_RETURN(0);
+ }
+ DBUG_RETURN(HA_ERR_END_OF_FILE);
+}
+
+
+/*
+ Return the top record in sort order
+
+ SYNOPSIS
+ return_top_record()
+ out:buf Row returned in MySQL Row Format
+
+ RETURN VALUE
+ NONE
+*/
+
+void ha_partition::return_top_record(uchar *buf)
+{
+ uint part_id;
+ uchar *key_buffer= queue_top(&m_queue);
+ uchar *rec_buffer= key_buffer + PARTITION_BYTES_IN_POS;
+
+ part_id= uint2korr(key_buffer);
+ memcpy(buf, rec_buffer, m_rec_length);
+ m_last_part= part_id;
+ m_top_entry= part_id;
+}
+
+
+/*
+ Common routine to handle index_next with ordered results
+
+ SYNOPSIS
+ handle_ordered_next()
+ out:buf Read row in MySQL Row Format
+ next_same Called from index_next_same
+
+ RETURN VALUE
+ HA_ERR_END_OF_FILE End of scan
+ 0 Success
+ other Error code
+*/
+
+int ha_partition::handle_ordered_next(uchar *buf, bool is_next_same)
+{
+ int error;
+ uint part_id= m_top_entry;
+ handler *file= m_file[part_id];
+ DBUG_ENTER("ha_partition::handle_ordered_next");
+
+ if (m_index_scan_type == partition_read_range)
+ {
+ error= file->read_range_next();
+ memcpy(rec_buf(part_id), table->record[0], m_rec_length);
+ }
+ else if (!is_next_same)
+ error= file->index_next(rec_buf(part_id));
+ else
+ error= file->index_next_same(rec_buf(part_id), m_start_key.key,
+ m_start_key.length);
+ if (error)
+ {
+ if (error == HA_ERR_END_OF_FILE)
+ {
+ /* Return next buffered row */
+ queue_remove(&m_queue, (uint) 0);
+ if (m_queue.elements)
+ {
+ DBUG_PRINT("info", ("Record returned from partition %u (2)",
+ m_top_entry));
+ return_top_record(buf);
+ table->status= 0;
+ error= 0;
+ }
+ }
+ DBUG_RETURN(error);
+ }
+ queue_replaced(&m_queue);
+ return_top_record(buf);
+ DBUG_PRINT("info", ("Record returned from partition %u", m_top_entry));
+ DBUG_RETURN(0);
+}
+
+
+/*
+ Common routine to handle index_prev with ordered results
+
+ SYNOPSIS
+ handle_ordered_prev()
+ out:buf Read row in MySQL Row Format
+
+ RETURN VALUE
+ HA_ERR_END_OF_FILE End of scan
+ 0 Success
+ other Error code
+*/
+
+int ha_partition::handle_ordered_prev(uchar *buf)
+{
+ int error;
+ uint part_id= m_top_entry;
+ handler *file= m_file[part_id];
+ DBUG_ENTER("ha_partition::handle_ordered_prev");
+
+ if ((error= file->index_prev(rec_buf(part_id))))
+ {
+ if (error == HA_ERR_END_OF_FILE)
+ {
+ queue_remove(&m_queue, (uint) 0);
+ if (m_queue.elements)
+ {
+ return_top_record(buf);
+ DBUG_PRINT("info", ("Record returned from partition %d (2)",
+ m_top_entry));
+ error= 0;
+ table->status= 0;
+ }
+ }
+ DBUG_RETURN(error);
+ }
+ queue_replaced(&m_queue);
+ return_top_record(buf);
+ DBUG_PRINT("info", ("Record returned from partition %d", m_top_entry));
+ DBUG_RETURN(0);
+}
+
+
+/****************************************************************************
+ MODULE information calls
+****************************************************************************/
+
+/*
+ These are all first approximations of the extra, info, scan_time
+ and read_time calls
+*/
+
+/*
+ General method to gather info from handler
+
+ SYNOPSIS
+ info()
+ flag Specifies what info is requested
+
+ RETURN VALUE
+ NONE
+
+ DESCRIPTION
+ ::info() is used to return information to the optimizer.
+ Currently this table handler doesn't implement most of the fields
+ really needed. SHOW also makes use of this data
+ Another note, if your handler doesn't proved exact record count,
+ you will probably want to have the following in your code:
+ if (records < 2)
+ records = 2;
+ The reason is that the server will optimize for cases of only a single
+ record. If in a table scan you don't know the number of records
+ it will probably be better to set records to two so you can return
+ as many records as you need.
+
+ Along with records a few more variables you may wish to set are:
+ records
+ deleted
+ data_file_length
+ index_file_length
+ delete_length
+ check_time
+ Take a look at the public variables in handler.h for more information.
+
+ Called in:
+ filesort.cc
+ ha_heap.cc
+ item_sum.cc
+ opt_sum.cc
+ sql_delete.cc
+ sql_delete.cc
+ sql_derived.cc
+ sql_select.cc
+ sql_select.cc
+ sql_select.cc
+ sql_select.cc
+ sql_select.cc
+ sql_show.cc
+ sql_show.cc
+ sql_show.cc
+ sql_show.cc
+ sql_table.cc
+ sql_union.cc
+ sql_update.cc
+
+ Some flags that are not implemented
+ HA_STATUS_POS:
+ This parameter is never used from the MySQL Server. It is checked in a
+ place in MyISAM so could potentially be used by MyISAM specific
+ programs.
+ HA_STATUS_NO_LOCK:
+ This is declared and often used. It's only used by MyISAM.
+ It means that MySQL doesn't need the absolute latest statistics
+ information. This may save the handler from doing internal locks while
+ retrieving statistics data.
+*/
+
+int ha_partition::info(uint flag)
+{
+ 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)
+ {
+ lock_auto_increment();
+ stats.auto_increment_value= ha_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;
+ else
+ {
+ handler *file, **file_array;
+ ulonglong auto_increment_value= 0;
+ file_array= m_file;
+ DBUG_PRINT("info",
+ ("checking all partitions for auto_increment_value"));
+ do
+ {
+ file= *file_array;
+ file->info(HA_STATUS_AUTO);
+ set_if_bigger(auto_increment_value,
+ file->stats.auto_increment_value);
+ } while (*(++file_array));
+
+ DBUG_ASSERT(auto_increment_value);
+ 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;
+ DBUG_PRINT("info", ("initializing next_auto_inc_val to %lu",
+ (ulong) ha_data->next_auto_inc_val));
+ }
+ }
+ unlock_auto_increment();
+ }
+ }
+ if (flag & HA_STATUS_VARIABLE)
+ {
+ DBUG_PRINT("info", ("HA_STATUS_VARIABLE"));
+ /*
+ Calculates statistical variables
+ records: Estimate of number records in table
+ We report sum (always at least 2 if not empty)
+ deleted: Estimate of number holes in the table due to
+ deletes
+ We report sum
+ data_file_length: Length of data file, in principle bytes in table
+ We report sum
+ index_file_length: Length of index file, in principle bytes in
+ indexes in the table
+ We report sum
+ delete_length: Length of free space easily used by new records in table
+ We report sum
+ mean_record_length:Mean record length in the table
+ We calculate this
+ check_time: Time of last check (only applicable to MyISAM)
+ We report last time of all underlying handlers
+ */
+ handler *file, **file_array;
+ stats.records= 0;
+ stats.deleted= 0;
+ stats.data_file_length= 0;
+ stats.index_file_length= 0;
+ stats.check_time= 0;
+ stats.delete_length= 0;
+ file_array= m_file;
+ do
+ {
+ if (bitmap_is_set(&(m_part_info->used_partitions), (file_array - m_file)))
+ {
+ file= *file_array;
+ file->info(HA_STATUS_VARIABLE);
+ stats.records+= file->stats.records;
+ stats.deleted+= file->stats.deleted;
+ stats.data_file_length+= file->stats.data_file_length;
+ stats.index_file_length+= file->stats.index_file_length;
+ stats.delete_length+= file->stats.delete_length;
+ if (file->stats.check_time > stats.check_time)
+ stats.check_time= file->stats.check_time;
+ }
+ } while (*(++file_array));
+ if (stats.records && stats.records < 2 &&
+ !(m_file[0]->ha_table_flags() & HA_STATS_RECORDS_IS_EXACT))
+ stats.records= 2;
+ if (stats.records > 0)
+ stats.mean_rec_length= (ulong) (stats.data_file_length / stats.records);
+ else
+ stats.mean_rec_length= 0;
+ }
+ if (flag & HA_STATUS_CONST)
+ {
+ DBUG_PRINT("info", ("HA_STATUS_CONST"));
+ /*
+ Recalculate loads of constant variables. MyISAM also sets things
+ directly on the table share object.
+
+ Check whether this should be fixed since handlers should not
+ change things directly on the table object.
+
+ Monty comment: This should NOT be changed! It's the handlers
+ responsibility to correct table->s->keys_xxxx information if keys
+ have been disabled.
+
+ The most important parameters set here is records per key on
+ all indexes. block_size and primar key ref_length.
+
+ For each index there is an array of rec_per_key.
+ As an example if we have an index with three attributes a,b and c
+ we will have an array of 3 rec_per_key.
+ rec_per_key[0] is an estimate of number of records divided by
+ number of unique values of the field a.
+ rec_per_key[1] is an estimate of the number of records divided
+ by the number of unique combinations of the fields a and b.
+ rec_per_key[2] is an estimate of the number of records divided
+ by the number of unique combinations of the fields a,b and c.
+
+ Many handlers only set the value of rec_per_key when all fields
+ are bound (rec_per_key[2] in the example above).
+
+ If the handler doesn't support statistics, it should set all of the
+ above to 0.
+
+ We will allow the first handler to set the rec_per_key and use
+ this as an estimate on the total table.
+
+ max_data_file_length: Maximum data file length
+ We ignore it, is only used in
+ SHOW TABLE STATUS
+ max_index_file_length: Maximum index file length
+ We ignore it since it is never used
+ block_size: Block size used
+ We set it to the value of the first handler
+ ref_length: We set this to the value calculated
+ and stored in local object
+ create_time: Creation time of table
+ Set by first handler
+
+ So we calculate these constants by using the variables on the first
+ handler.
+ */
+ handler *file;
+
+ file= m_file[0];
+ file->info(HA_STATUS_CONST);
+ stats.create_time= file->stats.create_time;
+ ref_length= m_ref_length;
+ }
+ if (flag & HA_STATUS_ERRKEY)
+ {
+ handler *file= m_file[m_last_part];
+ DBUG_PRINT("info", ("info: HA_STATUS_ERRKEY"));
+ /*
+ This flag is used to get index number of the unique index that
+ reported duplicate key
+ We will report the errkey on the last handler used and ignore the rest
+ */
+ file->info(HA_STATUS_ERRKEY);
+ if (file->errkey != (uint) -1)
+ errkey= file->errkey;
+ }
+ if (flag & HA_STATUS_TIME)
+ {
+ handler *file, **file_array;
+ DBUG_PRINT("info", ("info: HA_STATUS_TIME"));
+ /*
+ This flag is used to set the latest update time of the table.
+ Used by SHOW commands
+ We will report the maximum of these times
+ */
+ stats.update_time= 0;
+ file_array= m_file;
+ do
+ {
+ file= *file_array;
+ file->info(HA_STATUS_TIME);
+ if (file->stats.update_time > stats.update_time)
+ stats.update_time= file->stats.update_time;
+ } while (*(++file_array));
+ }
+ DBUG_RETURN(0);
+}
+
+
+void ha_partition::get_dynamic_partition_info(PARTITION_INFO *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);
+
+ stat_info->records= file->stats.records;
+ stat_info->mean_rec_length= file->stats.mean_rec_length;
+ stat_info->data_file_length= file->stats.data_file_length;
+ stat_info->max_data_file_length= file->stats.max_data_file_length;
+ stat_info->index_file_length= file->stats.index_file_length;
+ stat_info->delete_length= file->stats.delete_length;
+ stat_info->create_time= file->stats.create_time;
+ stat_info->update_time= file->stats.update_time;
+ stat_info->check_time= file->stats.check_time;
+ stat_info->check_sum= 0;
+ if (file->ha_table_flags() & HA_HAS_CHECKSUM)
+ stat_info->check_sum= file->checksum();
+ return;
+}
+
+
+/*
+ General function to prepare handler for certain behavior
+
+ SYNOPSIS
+ extra()
+ operation Operation type for extra call
+
+ RETURN VALUE
+ >0 Error code
+ 0 Success
+
+ 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
+
+ The partition handler need to handle category 1), 2) and 3).
+
+ 1) Parameters used by most handlers
+ -----------------------------------
+ HA_EXTRA_RESET:
+ This option is used by most handlers and it resets the handler state
+ to the same state as after an open call. This includes releasing
+ any READ CACHE or WRITE CACHE or other internal buffer used.
+
+ It is called from the reset method in the handler interface. There are
+ three instances where this is called.
+ 1) After completing a INSERT ... SELECT ... query the handler for the
+ table inserted into is reset
+ 2) It is called from close_thread_table which in turn is called from
+ close_thread_tables except in the case where the tables are locked
+ in which case ha_commit_stmt is called instead.
+ It is only called from here if refresh_version hasn't changed and the
+ table is not an old table when calling close_thread_table.
+ close_thread_tables is called from many places as a general clean up
+ function after completing a query.
+ 3) It is called when deleting the QUICK_RANGE_SELECT object if the
+ QUICK_RANGE_SELECT object had its own handler object. It is called
+ immediatley before close of this local handler object.
+ HA_EXTRA_KEYREAD:
+ HA_EXTRA_NO_KEYREAD:
+ These parameters are used to provide an optimisation hint to the handler.
+ If HA_EXTRA_KEYREAD is set it is enough to read the index fields, for
+ many handlers this means that the index-only scans can be used and it
+ is not necessary to use the real records to satisfy this part of the
+ query. Index-only scans is a very important optimisation for disk-based
+ indexes. For main-memory indexes most indexes contain a reference to the
+ record and thus KEYREAD only says that it is enough to read key fields.
+ HA_EXTRA_NO_KEYREAD disables this for the handler, also HA_EXTRA_RESET
+ will disable this option.
+ The handler will set HA_KEYREAD_ONLY in its table flags to indicate this
+ feature is supported.
+ HA_EXTRA_FLUSH:
+ Indication to flush tables to disk, is supposed to be used to
+ ensure disk based tables are flushed at end of query execution.
+ Currently is never used.
+
+ 2) Parameters used by some non-MyISAM handlers
+ ----------------------------------------------
+ HA_EXTRA_KEYREAD_PRESERVE_FIELDS:
+ This is a strictly InnoDB feature that is more or less undocumented.
+ When it is activated InnoDB copies field by field from its fetch
+ cache instead of all fields in one memcpy. Have no idea what the
+ purpose of this is.
+ Cut from include/my_base.h:
+ When using HA_EXTRA_KEYREAD, overwrite only key member fields and keep
+ other fields intact. When this is off (by default) InnoDB will use memcpy
+ to overwrite entire row.
+ HA_EXTRA_IGNORE_DUP_KEY:
+ HA_EXTRA_NO_IGNORE_DUP_KEY:
+ Informs the handler to we will not stop the transaction if we get an
+ duplicate key errors during insert/upate.
+ Always called in pair, triggered by INSERT IGNORE and other similar
+ SQL constructs.
+ Not used by MyISAM.
+
+ 3) Parameters used only by MyISAM
+ ---------------------------------
+ HA_EXTRA_NORMAL:
+ Only used in MyISAM to reset quick mode, not implemented by any other
+ handler. Quick mode is also reset in MyISAM by HA_EXTRA_RESET.
+
+ It is called after completing a successful DELETE query if the QUICK
+ option is set.
+
+ HA_EXTRA_QUICK:
+ When the user does DELETE QUICK FROM table where-clause; this extra
+ option is called before the delete query is performed and
+ HA_EXTRA_NORMAL is called after the delete query is completed.
+ Temporary tables used internally in MySQL always set this option
+
+ The meaning of quick mode is that when deleting in a B-tree no merging
+ of leafs is performed. This is a common method and many large DBMS's
+ actually only support this quick mode since it is very difficult to
+ merge leaves in a tree used by many threads concurrently.
+
+ HA_EXTRA_CACHE:
+ This flag is usually set with extra_opt along with a cache size.
+ The size of this buffer is set by the user variable
+ record_buffer_size. The value of this cache size is the amount of
+ data read from disk in each fetch when performing a table scan.
+ This means that before scanning a table it is normal to call
+ extra with HA_EXTRA_CACHE and when the scan is completed to call
+ HA_EXTRA_NO_CACHE to release the cache memory.
+
+ Some special care is taken when using this extra parameter since there
+ could be a write ongoing on the table in the same statement. In this
+ one has to take special care since there might be a WRITE CACHE as
+ well. HA_EXTRA_CACHE specifies using a READ CACHE and using
+ READ CACHE and WRITE CACHE at the same time is not possible.
+
+ Only MyISAM currently use this option.
+
+ It is set when doing full table scans using rr_sequential and
+ reset when completing such a scan with end_read_record
+ (resetting means calling extra with HA_EXTRA_NO_CACHE).
+
+ It is set in filesort.cc for MyISAM internal tables and it is set in
+ a multi-update where HA_EXTRA_CACHE is called on a temporary result
+ table and after that ha_rnd_init(0) on table to be updated
+ and immediately after that HA_EXTRA_NO_CACHE on table to be updated.
+
+ Apart from that it is always used from init_read_record but not when
+ used from UPDATE statements. It is not used from DELETE statements
+ with ORDER BY and LIMIT but it is used in normal scan loop in DELETE
+ statements. The reason here is that DELETE's in MyISAM doesn't move
+ existings data rows.
+
+ It is also set in copy_data_between_tables when scanning the old table
+ to copy over to the new table.
+ And it is set in join_init_read_record where quick objects are used
+ to perform a scan on the table. In this case the full table scan can
+ even be performed multiple times as part of the nested loop join.
+
+ For purposes of the partition handler it is obviously necessary to have
+ special treatment of this extra call. If we would simply pass this
+ extra call down to each handler we would allocate
+ cache size * no of partitions amount of memory and this is not
+ necessary since we will only scan one partition at a time when doing
+ full table scans.
+
+ Thus we treat it by first checking whether we have MyISAM handlers in
+ the table, if not we simply ignore the call and if we have we will
+ record the call but will not call any underlying handler yet. Then
+ when performing the sequential scan we will check this recorded value
+ and call extra_opt whenever we start scanning a new partition.
+
+ monty: Neads to be fixed so that it's passed to all handlers when we
+ move to another partition during table scan.
+
+ HA_EXTRA_NO_CACHE:
+ When performing a UNION SELECT HA_EXTRA_NO_CACHE is called from the
+ flush method in the select_union class.
+ It is used to some extent when insert delayed inserts.
+ See HA_EXTRA_RESET_STATE for use in conjunction with delete_all_rows().
+
+ It should be ok to call HA_EXTRA_NO_CACHE on all underlying handlers
+ if they are MyISAM handlers. Other handlers we can ignore the call
+ for. If no cache is in use they will quickly return after finding
+ this out. And we also ensure that all caches are disabled and no one
+ is left by mistake.
+ In the future this call will probably be deleted an we will instead call
+ ::reset();
+
+ HA_EXTRA_WRITE_CACHE:
+ See above, called from various places. It is mostly used when we
+ do INSERT ... SELECT
+ No special handling to save cache space is developed currently.
+
+ HA_EXTRA_PREPARE_FOR_UPDATE:
+ This is called as part of a multi-table update. When the table to be
+ updated is also scanned then this informs MyISAM handler to drop any
+ caches if dynamic records are used (fixed size records do not care
+ about this call). We pass this along to all underlying MyISAM handlers
+ and ignore it for the rest.
+
+ HA_EXTRA_PREPARE_FOR_DROP:
+ Only used by MyISAM, called in preparation for a DROP TABLE.
+ It's used mostly by Windows that cannot handle dropping an open file.
+ On other platforms it has the same effect as HA_EXTRA_FORCE_REOPEN.
+
+ HA_EXTRA_PREPARE_FOR_RENAME:
+ Informs the handler we are about to attempt a rename of the table.
+
+ HA_EXTRA_READCHECK:
+ HA_EXTRA_NO_READCHECK:
+ Only one call to HA_EXTRA_NO_READCHECK from ha_open where it says that
+ this is not needed in SQL. The reason for this call is that MyISAM sets
+ the READ_CHECK_USED in the open call so the call is needed for MyISAM
+ to reset this feature.
+ The idea with this parameter was to inform of doing/not doing a read
+ check before applying an update. Since SQL always performs a read before
+ applying the update No Read Check is needed in MyISAM as well.
+
+ This is a cut from Docs/myisam.txt
+ Sometimes you might want to force an update without checking whether
+ another user has changed the record since you last read it. This is
+ somewhat dangerous, so it should ideally not be used. That can be
+ accomplished by wrapping the mi_update() call in two calls to mi_extra(),
+ using these functions:
+ HA_EXTRA_NO_READCHECK=5 No readcheck on update
+ HA_EXTRA_READCHECK=6 Use readcheck (def)
+
+ HA_EXTRA_FORCE_REOPEN:
+ Only used by MyISAM, called when altering table, closing tables to
+ enforce a reopen of the table files.
+
+ 4) Parameters only used by temporary tables for query processing
+ ----------------------------------------------------------------
+ HA_EXTRA_RESET_STATE:
+ Same as reset() except that buffers are not released. If there is
+ a READ CACHE it is reinit'ed. A cache is reinit'ed to restart reading
+ or to change type of cache between READ CACHE and WRITE CACHE.
+
+ This extra function is always called immediately before calling
+ delete_all_rows on the handler for temporary tables.
+ There are cases however when HA_EXTRA_RESET_STATE isn't called in
+ a similar case for a temporary table in sql_union.cc and in two other
+ cases HA_EXTRA_NO_CACHE is called before and HA_EXTRA_WRITE_CACHE
+ called afterwards.
+ The case with HA_EXTRA_NO_CACHE and HA_EXTRA_WRITE_CACHE means
+ disable caching, delete all rows and enable WRITE CACHE. This is
+ used for temporary tables containing distinct sums and a
+ functional group.
+
+ The only case that delete_all_rows is called on non-temporary tables
+ is in sql_delete.cc when DELETE FROM table; is called by a user.
+ In this case no special extra calls are performed before or after this
+ call.
+
+ The partition handler should not need to bother about this one. It
+ should never be called.
+
+ HA_EXTRA_NO_ROWS:
+ Don't insert rows indication to HEAP and MyISAM, only used by temporary
+ tables used in query processing.
+ Not handled by partition handler.
+
+ 5) Parameters only used by MyISAM internally
+ --------------------------------------------
+ HA_EXTRA_REINIT_CACHE:
+ This call reinitializes the READ CACHE described above if there is one
+ and otherwise the call is ignored.
+
+ We can thus safely call it on all underlying handlers if they are
+ MyISAM handlers. It is however never called so we don't handle it at all.
+ HA_EXTRA_FLUSH_CACHE:
+ Flush WRITE CACHE in MyISAM. It is only from one place in the code.
+ This is in sql_insert.cc where it is called if the table_flags doesn't
+ contain HA_DUPLICATE_POS. The only handler having the HA_DUPLICATE_POS
+ set is the MyISAM handler and so the only handler not receiving this
+ call is MyISAM.
+ Thus in effect this call is called but never used. Could be removed
+ from sql_insert.cc
+ HA_EXTRA_NO_USER_CHANGE:
+ Only used by MyISAM, never called.
+ Simulates lock_type as locked.
+ HA_EXTRA_WAIT_LOCK:
+ HA_EXTRA_WAIT_NOLOCK:
+ Only used by MyISAM, called from MyISAM handler but never from server
+ code on top of the handler.
+ Sets lock_wait on/off
+ HA_EXTRA_NO_KEYS:
+ Only used MyISAM, only used internally in MyISAM handler, never called
+ from server level.
+ HA_EXTRA_KEYREAD_CHANGE_POS:
+ HA_EXTRA_REMEMBER_POS:
+ HA_EXTRA_RESTORE_POS:
+ HA_EXTRA_PRELOAD_BUFFER_SIZE:
+ HA_EXTRA_CHANGE_KEY_TO_DUP:
+ HA_EXTRA_CHANGE_KEY_TO_UNIQUE:
+ Only used by MyISAM, never called.
+
+ 6) Parameters 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
+ ----------------------------------------------------------------
+ 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
+ ------------------------------
+ HA_EXTRA_DELETE_CANNOT_BATCH:
+ HA_EXTRA_UPDATE_CANNOT_BATCH:
+ Inform handler that delete_row()/update_row() cannot batch deletes/updates
+ and should perform them immediately. This may be needed when table has
+ AFTER DELETE/UPDATE triggers which access to subject table.
+ These flags are reset by the handler::extra(HA_EXTRA_RESET) call.
+*/
+
+int ha_partition::extra(enum ha_extra_function operation)
+{
+ DBUG_ENTER("ha_partition:extra");
+ DBUG_PRINT("info", ("operation: %d", (int) operation));
+
+ switch (operation) {
+ /* Category 1), used by most handlers */
+ case HA_EXTRA_KEYREAD:
+ case HA_EXTRA_NO_KEYREAD:
+ case HA_EXTRA_FLUSH:
+ DBUG_RETURN(loop_extra(operation));
+
+ /* Category 2), used by non-MyISAM handlers */
+ case HA_EXTRA_IGNORE_DUP_KEY:
+ case HA_EXTRA_NO_IGNORE_DUP_KEY:
+ case HA_EXTRA_KEYREAD_PRESERVE_FIELDS:
+ {
+ if (!m_myisam)
+ DBUG_RETURN(loop_extra(operation));
+ break;
+ }
+
+ /* Category 3), used by MyISAM handlers */
+ case HA_EXTRA_PREPARE_FOR_RENAME:
+ DBUG_RETURN(prepare_for_rename());
+ break;
+ case HA_EXTRA_NORMAL:
+ case HA_EXTRA_QUICK:
+ case HA_EXTRA_PREPARE_FOR_UPDATE:
+ case HA_EXTRA_FORCE_REOPEN:
+ case HA_EXTRA_PREPARE_FOR_DROP:
+ case HA_EXTRA_FLUSH_CACHE:
+ {
+ if (m_myisam)
+ DBUG_RETURN(loop_extra(operation));
+ break;
+ }
+ case HA_EXTRA_NO_READCHECK:
+ {
+ /*
+ This is only done as a part of ha_open, which is also used in
+ ha_partition::open, so no need to do anything.
+ */
+ break;
+ }
+ case HA_EXTRA_CACHE:
+ {
+ prepare_extra_cache(0);
+ break;
+ }
+ case HA_EXTRA_NO_CACHE:
+ case HA_EXTRA_WRITE_CACHE:
+ {
+ m_extra_cache= FALSE;
+ m_extra_cache_size= 0;
+ DBUG_RETURN(loop_extra(operation));
+ }
+ case HA_EXTRA_IGNORE_NO_KEY:
+ case HA_EXTRA_NO_IGNORE_NO_KEY:
+ {
+ /*
+ Ignore as these are specific to NDB for handling
+ idempotency
+ */
+ break;
+ }
+ case HA_EXTRA_WRITE_CAN_REPLACE:
+ case HA_EXTRA_WRITE_CANNOT_REPLACE:
+ {
+ /*
+ Informs handler that write_row() can replace rows which conflict
+ with row being inserted by PK/unique key without reporting error
+ to the SQL-layer.
+
+ This optimization is not safe for partitioned table in general case
+ since we may have to put new version of row into partition which is
+ different from partition in which old version resides (for example
+ when we partition by non-PK column or by some column which is not
+ part of unique key which were violated).
+ And since NDB which is the only engine at the moment that supports
+ this optimization handles partitioning on its own we simple disable
+ it here. (BTW for NDB this optimization is safe since it supports
+ only KEY partitioning and won't use this optimization for tables
+ which have additional unique constraints).
+ */
+ break;
+ }
+ /* Category 7), used by federated handlers */
+ case HA_EXTRA_INSERT_WITH_UPDATE:
+ DBUG_RETURN(loop_extra(operation));
+ /* Category 8) Parameters only used by NDB */
+ case HA_EXTRA_DELETE_CANNOT_BATCH:
+ case HA_EXTRA_UPDATE_CANNOT_BATCH:
+ {
+ /* Currently only NDB use the *_CANNOT_BATCH */
+ break;
+ }
+ default:
+ {
+ /* Temporary crash to discover what is wrong */
+ DBUG_ASSERT(0);
+ break;
+ }
+ }
+ DBUG_RETURN(0);
+}
+
+
+/*
+ Special extra call to reset extra parameters
+
+ SYNOPSIS
+ reset()
+
+ RETURN VALUE
+ >0 Error code
+ 0 Success
+
+ DESCRIPTION
+ Called at end of each statement to reste buffers
+*/
+
+int ha_partition::reset(void)
+{
+ int result= 0, tmp;
+ handler **file;
+ DBUG_ENTER("ha_partition::reset");
+ if (m_part_info)
+ bitmap_set_all(&m_part_info->used_partitions);
+ file= m_file;
+ do
+ {
+ if ((tmp= (*file)->ha_reset()))
+ result= tmp;
+ } while (*(++file));
+ DBUG_RETURN(result);
+}
+
+/*
+ Special extra method for HA_EXTRA_CACHE with cachesize as extra parameter
+
+ SYNOPSIS
+ extra_opt()
+ operation Must be HA_EXTRA_CACHE
+ cachesize Size of cache in full table scan
+
+ RETURN VALUE
+ >0 Error code
+ 0 Success
+*/
+
+int ha_partition::extra_opt(enum ha_extra_function operation, ulong cachesize)
+{
+ DBUG_ENTER("ha_partition::extra_opt()");
+
+ DBUG_ASSERT(HA_EXTRA_CACHE == operation);
+ prepare_extra_cache(cachesize);
+ DBUG_RETURN(0);
+}
+
+
+/*
+ Call extra on handler with HA_EXTRA_CACHE and cachesize
+
+ SYNOPSIS
+ prepare_extra_cache()
+ cachesize Size of cache for full table scan
+
+ RETURN VALUE
+ NONE
+*/
+
+void ha_partition::prepare_extra_cache(uint cachesize)
+{
+ DBUG_ENTER("ha_partition::prepare_extra_cache()");
+
+ m_extra_cache= TRUE;
+ m_extra_cache_size= cachesize;
+ if (m_part_spec.start_part != NO_CURRENT_PART_ID)
+ {
+ late_extra_cache(m_part_spec.start_part);
+ }
+ DBUG_VOID_RETURN;
+}
+
+
+/*
+ Prepares our new and reorged handlers for rename or delete
+
+ SYNOPSIS
+ prepare_for_delete()
+
+ RETURN VALUE
+ >0 Error code
+ 0 Success
+*/
+
+int ha_partition::prepare_for_rename()
+{
+ int result= 0, tmp;
+ handler **file;
+ DBUG_ENTER("ha_partition::prepare_for_rename()");
+
+ if (m_new_file != NULL)
+ {
+ for (file= m_new_file; *file; file++)
+ if ((tmp= (*file)->extra(HA_EXTRA_PREPARE_FOR_RENAME)))
+ result= tmp;
+ for (file= m_reorged_file; *file; file++)
+ if ((tmp= (*file)->extra(HA_EXTRA_PREPARE_FOR_RENAME)))
+ result= tmp;
+ DBUG_RETURN(result);
+ }
+
+ DBUG_RETURN(loop_extra(HA_EXTRA_PREPARE_FOR_RENAME));
+}
+
+/*
+ Call extra on all partitions
+
+ SYNOPSIS
+ loop_extra()
+ operation extra operation type
+
+ RETURN VALUE
+ >0 Error code
+ 0 Success
+*/
+
+int ha_partition::loop_extra(enum ha_extra_function operation)
+{
+ int result= 0, tmp;
+ handler **file;
+ DBUG_ENTER("ha_partition::loop_extra()");
+
+ /*
+ TODO, 5.2: this is where you could possibly add optimisations to add the bitmap
+ _if_ a SELECT.
+ */
+ for (file= m_file; *file; file++)
+ {
+ if ((tmp= (*file)->extra(operation)))
+ result= tmp;
+ }
+ DBUG_RETURN(result);
+}
+
+
+/*
+ Call extra(HA_EXTRA_CACHE) on next partition_id
+
+ SYNOPSIS
+ late_extra_cache()
+ partition_id Partition id to call extra on
+
+ RETURN VALUE
+ NONE
+*/
+
+void ha_partition::late_extra_cache(uint partition_id)
+{
+ handler *file;
+ DBUG_ENTER("ha_partition::late_extra_cache");
+
+ if (!m_extra_cache)
+ DBUG_VOID_RETURN;
+ file= m_file[partition_id];
+ if (m_extra_cache_size == 0)
+ VOID(file->extra(HA_EXTRA_CACHE));
+ else
+ VOID(file->extra_opt(HA_EXTRA_CACHE, m_extra_cache_size));
+ DBUG_VOID_RETURN;
+}
+
+
+/*
+ Call extra(HA_EXTRA_NO_CACHE) on next partition_id
+
+ SYNOPSIS
+ late_extra_no_cache()
+ partition_id Partition id to call extra on
+
+ RETURN VALUE
+ NONE
+*/
+
+void ha_partition::late_extra_no_cache(uint partition_id)
+{
+ handler *file;
+ DBUG_ENTER("ha_partition::late_extra_no_cache");
+
+ if (!m_extra_cache)
+ DBUG_VOID_RETURN;
+ file= m_file[partition_id];
+ VOID(file->extra(HA_EXTRA_NO_CACHE));
+ DBUG_VOID_RETURN;
+}
+
+
+/****************************************************************************
+ MODULE optimiser support
+****************************************************************************/
+
+/*
+ Get keys to use for scanning
+
+ SYNOPSIS
+ keys_to_use_for_scanning()
+
+ RETURN VALUE
+ key_map of keys usable for scanning
+*/
+
+const key_map *ha_partition::keys_to_use_for_scanning()
+{
+ DBUG_ENTER("ha_partition::keys_to_use_for_scanning");
+
+ DBUG_RETURN(m_file[0]->keys_to_use_for_scanning());
+}
+
+
+/*
+ Return time for a scan of the table
+
+ SYNOPSIS
+ scan_time()
+
+ RETURN VALUE
+ time for scan
+*/
+
+double ha_partition::scan_time()
+{
+ double scan_time= 0;
+ handler **file;
+ DBUG_ENTER("ha_partition::scan_time");
+
+ for (file= m_file; *file; file++)
+ if (bitmap_is_set(&(m_part_info->used_partitions), (file - m_file)))
+ scan_time+= (*file)->scan_time();
+ DBUG_RETURN(scan_time);
+}
+
+
+/*
+ Get time to read
+
+ SYNOPSIS
+ read_time()
+ index Index number used
+ ranges Number of ranges
+ rows Number of rows
+
+ RETURN VALUE
+ time for read
+
+ DESCRIPTION
+ This will be optimised later to include whether or not the index can
+ be used with partitioning. To achieve we need to add another parameter
+ that specifies how many of the index fields that are bound in the ranges.
+ Possibly added as a new call to handlers.
+*/
+
+double ha_partition::read_time(uint index, uint ranges, ha_rows rows)
+{
+ DBUG_ENTER("ha_partition::read_time");
+
+ DBUG_RETURN(m_file[0]->read_time(index, ranges, rows));
+}
+
+/*
+ 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)
+{
+ handler **file;
+ ha_rows in_range= 0;
+ DBUG_ENTER("ha_partition::records_in_range");
+
+ file= m_file;
+ do
+ {
+ if (bitmap_is_set(&(m_part_info->used_partitions), (file - m_file)))
+ {
+ ha_rows tmp_in_range= (*file)->records_in_range(inx, min_key, max_key);
+ if (tmp_in_range == HA_POS_ERROR)
+ DBUG_RETURN(tmp_in_range);
+ in_range+= tmp_in_range;
+ }
+ } while (*(++file));
+ DBUG_RETURN(in_range);
+}
+
+
+/*
+ Estimate upper bound of number of rows
+
+ SYNOPSIS
+ estimate_rows_upper_bound()
+
+ RETURN VALUE
+ Number of rows
+*/
+
+ha_rows ha_partition::estimate_rows_upper_bound()
+{
+ ha_rows rows, tot_rows= 0;
+ handler **file;
+ DBUG_ENTER("ha_partition::estimate_rows_upper_bound");
+
+ file= m_file;
+ 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);
+}
+
+
+/**
+ Number of rows in table. see handler.h
+
+ SYNOPSIS
+ records()
+
+ RETURN VALUE
+ Number of total rows in a partitioned table.
+*/
+
+ha_rows ha_partition::records()
+{
+ ha_rows rows, tot_rows= 0;
+ handler **file;
+ DBUG_ENTER("ha_partition::records");
+
+ file= m_file;
+ do
+ {
+ rows= (*file)->records();
+ if (rows == HA_POS_ERROR)
+ DBUG_RETURN(HA_POS_ERROR);
+ tot_rows+= rows;
+ } while (*(++file));
+ DBUG_RETURN(tot_rows);
+}
+
+
+/*
+ Is it ok to switch to a new engine for this table
+
+ SYNOPSIS
+ can_switch_engine()
+
+ RETURN VALUE
+ TRUE Ok
+ FALSE Not ok
+
+ DESCRIPTION
+ Used to ensure that tables with foreign key constraints are not moved
+ to engines without foreign key support.
+*/
+
+bool ha_partition::can_switch_engines()
+{
+ handler **file;
+ DBUG_ENTER("ha_partition::can_switch_engines");
+
+ file= m_file;
+ do
+ {
+ if (!(*file)->can_switch_engines())
+ DBUG_RETURN(FALSE);
+ } while (*(++file));
+ DBUG_RETURN(TRUE);
+}
+
+
+/*
+ Is table cache supported
+
+ SYNOPSIS
+ table_cache_type()
+
+*/
+
+uint8 ha_partition::table_cache_type()
+{
+ DBUG_ENTER("ha_partition::table_cache_type");
+
+ DBUG_RETURN(m_file[0]->table_cache_type());
+}
+
+
+/****************************************************************************
+ MODULE print messages
+****************************************************************************/
+
+const char *ha_partition::index_type(uint inx)
+{
+ DBUG_ENTER("ha_partition::index_type");
+
+ DBUG_RETURN(m_file[0]->index_type(inx));
+}
+
+
+enum row_type ha_partition::get_row_type() const
+{
+ handler **file;
+ enum row_type type= (*m_file)->get_row_type();
+
+ for (file= m_file, file++; *file; file++)
+ {
+ enum row_type part_type= (*file)->get_row_type();
+ if (part_type != type)
+ return ROW_TYPE_NOT_USED;
+ }
+
+ return type;
+}
+
+
+void ha_partition::print_error(int error, myf errflag)
+{
+ 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
+ m_file[m_last_part]->print_error(error, errflag);
+ DBUG_VOID_RETURN;
+}
+
+
+bool ha_partition::get_error_message(int error, String *buf)
+{
+ DBUG_ENTER("ha_partition::get_error_message");
+
+ /* Should probably look for my own errors first */
+ DBUG_RETURN(m_file[m_last_part]->get_error_message(error, buf));
+}
+
+
+/****************************************************************************
+ MODULE handler characteristics
+****************************************************************************/
+/**
+ alter_table_flags must be on handler/table level, not on hton level
+ due to the ha_partition hton does not know what the underlying hton is.
+*/
+uint ha_partition::alter_table_flags(uint flags)
+{
+ DBUG_ENTER("ha_partition::alter_table_flags");
+ DBUG_RETURN(ht->alter_table_flags(flags) |
+ m_file[0]->alter_table_flags(flags));
+}
+
+
+/**
+ check if copy of data is needed in alter table.
+*/
+bool ha_partition::check_if_incompatible_data(HA_CREATE_INFO *create_info,
+ uint table_changes)
+{
+ handler **file;
+ bool ret= COMPATIBLE_DATA_YES;
+
+ /*
+ The check for any partitioning related changes have already been done
+ in mysql_alter_table (by fix_partition_func), so it is only up to
+ the underlying handlers.
+ */
+ for (file= m_file; *file; file++)
+ if ((ret= (*file)->check_if_incompatible_data(create_info,
+ table_changes)) !=
+ COMPATIBLE_DATA_YES)
+ break;
+ return ret;
+}
+
+
+/**
+ Support of fast or online add/drop index
+*/
+int ha_partition::add_index(TABLE *table_arg, KEY *key_info, uint num_of_keys)
+{
+ handler **file;
+ int ret= 0;
+
+ /*
+ 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)))
+ break;
+ return ret;
+}
+
+
+int ha_partition::prepare_drop_index(TABLE *table_arg, uint *key_num,
+ uint num_of_keys)
+{
+ handler **file;
+ int ret= 0;
+
+ /*
+ DROP INDEX does not affect partitioning.
+ */
+ for (file= m_file; *file; file++)
+ if ((ret= (*file)->prepare_drop_index(table_arg, key_num, num_of_keys)))
+ break;
+ return ret;
+}
+
+
+int ha_partition::final_drop_index(TABLE *table_arg)
+{
+ handler **file;
+ int ret= HA_ERR_WRONG_COMMAND;
+
+ for (file= m_file; *file; file++)
+ if ((ret= (*file)->final_drop_index(table_arg)))
+ break;
+ return ret;
+}
+
+
+/*
+ If frm_error() is called then we will use this to to find out what file
+ extensions exist for the storage engine. This is also used by the default
+ rename_table and delete_table method in handler.cc.
+*/
+
+static const char *ha_partition_ext[]=
+{
+ ha_par_ext, NullS
+};
+
+const char **ha_partition::bas_ext() const
+{ return ha_partition_ext; }
+
+
+uint ha_partition::min_of_the_max_uint(
+ uint (handler::*operator_func)(void) const) const
+{
+ handler **file;
+ uint min_of_the_max= ((*m_file)->*operator_func)();
+
+ for (file= m_file+1; *file; file++)
+ {
+ uint tmp= ((*file)->*operator_func)();
+ set_if_smaller(min_of_the_max, tmp);
+ }
+ return min_of_the_max;
+}
+
+
+uint ha_partition::max_supported_key_parts() const
+{
+ return min_of_the_max_uint(&handler::max_supported_key_parts);
+}
+
+
+uint ha_partition::max_supported_key_length() const
+{
+ return min_of_the_max_uint(&handler::max_supported_key_length);
+}
+
+
+uint ha_partition::max_supported_key_part_length() const
+{
+ return min_of_the_max_uint(&handler::max_supported_key_part_length);
+}
+
+
+uint ha_partition::max_supported_record_length() const
+{
+ return min_of_the_max_uint(&handler::max_supported_record_length);
+}
+
+
+uint ha_partition::max_supported_keys() const
+{
+ return min_of_the_max_uint(&handler::max_supported_keys);
+}
+
+
+uint ha_partition::extra_rec_buf_length() const
+{
+ handler **file;
+ uint max= (*m_file)->extra_rec_buf_length();
+
+ for (file= m_file, file++; *file; file++)
+ if (max < (*file)->extra_rec_buf_length())
+ max= (*file)->extra_rec_buf_length();
+ return max;
+}
+
+
+uint ha_partition::min_record_length(uint options) const
+{
+ handler **file;
+ uint max= (*m_file)->min_record_length(options);
+
+ for (file= m_file, file++; *file; file++)
+ if (max < (*file)->min_record_length(options))
+ max= (*file)->min_record_length(options);
+ return max;
+}
+
+
+/****************************************************************************
+ MODULE compare records
+****************************************************************************/
+/*
+ Compare two positions
+
+ SYNOPSIS
+ cmp_ref()
+ ref1 First position
+ ref2 Second position
+
+ RETURN VALUE
+ <0 ref1 < ref2
+ 0 Equal
+ >0 ref1 > ref2
+
+ DESCRIPTION
+ 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.
+*/
+
+int ha_partition::cmp_ref(const uchar *ref1, const uchar *ref2)
+{
+ uint part_id;
+ my_ptrdiff_t diff1, diff2;
+ handler *file;
+ DBUG_ENTER("ha_partition::cmp_ref");
+
+ 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)));
+ }
+ diff1= ref2[1] - ref1[1];
+ diff2= ref2[0] - ref1[0];
+ if (diff1 > 0)
+ {
+ DBUG_RETURN(-1);
+ }
+ if (diff1 < 0)
+ {
+ DBUG_RETURN(+1);
+ }
+ if (diff2 > 0)
+ {
+ DBUG_RETURN(-1);
+ }
+ DBUG_RETURN(+1);
+}
+
+
+/****************************************************************************
+ MODULE auto increment
+****************************************************************************/
+
+
+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;
+ do
+ {
+ if ((res= (*file)->ha_reset_auto_increment(value)) != 0)
+ break;
+ } while (*(++file));
+ unlock_auto_increment();
+ DBUG_RETURN(res);
+}
+
+
+/**
+ 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
+ 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.
+*/
+
+void ha_partition::get_auto_increment(ulonglong offset, ulonglong increment,
+ ulonglong nb_desired_values,
+ ulonglong *first_value,
+ ulonglong *nb_reserved_values)
+{
+ DBUG_ENTER("ha_partition::get_auto_increment");
+ DBUG_PRINT("info", ("offset: %lu inc: %lu desired_values: %lu "
+ "first_value: %lu", (ulong) offset, (ulong) increment,
+ (ulong) nb_desired_values, (ulong) *first_value));
+ DBUG_ASSERT(increment && nb_desired_values);
+ *first_value= 0;
+ if (table->s->next_number_keypart)
+ {
+ /*
+ next_number_keypart is != 0 if the auto_increment column is a secondary
+ column in the index (it is allowed in MyISAM)
+ */
+ DBUG_PRINT("info", ("next_number_keypart != 0"));
+ ulonglong nb_reserved_values_part;
+ 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();
+ do
+ {
+ /* Only nb_desired_values = 1 makes sense */
+ (*file)->get_auto_increment(offset, increment, 1,
+ &first_value_part, &nb_reserved_values_part);
+ if (first_value_part == ~(ulonglong)(0)) // error in one partition
+ {
+ *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));
+ set_if_bigger(max_first_value, first_value_part);
+ } 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);
+ /*
+ Get a lock for handling the auto_increment in table_share->ha_data
+ for avoiding two concurrent statements getting the same number.
+ */
+
+ lock_auto_increment();
+
+ /*
+ In a multi-row insert statement like INSERT SELECT and LOAD DATA
+ where the number of candidate rows to insert is not known in advance
+ we must hold a lock/mutex for the whole statement if we have statement
+ based replication. Because the statement-based binary log contains
+ only the first generated value used by the statement, and slaves assumes
+ all other generated values used by this statement were consecutive to
+ this first one, we must exclusively lock the generator until the statement
+ is done.
+ */
+ 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))
+ {
+ 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;
+
+ unlock_auto_increment();
+ DBUG_PRINT("info", ("*first_value: %lu", (ulong) *first_value));
+ *nb_reserved_values= nb_desired_values;
+ }
+ DBUG_VOID_RETURN;
+}
+
+void ha_partition::release_auto_increment()
+{
+ DBUG_ENTER("ha_partition::release_auto_increment");
+
+ if (table->s->next_number_keypart)
+ {
+ for (uint i= 0; i < m_tot_parts; i++)
+ m_file[i]->ha_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;
+ if (next_insert_id < next_auto_inc_val &&
+ auto_inc_interval_for_cur_row.maximum() >= next_auto_inc_val)
+ ha_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));
+
+ /* Unlock the multi row statement lock taken in get_auto_increment */
+ if (auto_increment_safe_stmt_log_lock)
+ {
+ auto_increment_safe_stmt_log_lock= FALSE;
+ DBUG_PRINT("info", ("unlocking auto_increment_safe_stmt_log_lock"));
+ }
+
+ unlock_auto_increment();
+ }
+ DBUG_VOID_RETURN;
+}
+
+/****************************************************************************
+ MODULE initialize handler for HANDLER call
+****************************************************************************/
+
+void ha_partition::init_table_handle_for_HANDLER()
+{
+ return;
+}
+
+
+/****************************************************************************
+ MODULE enable/disable indexes
+****************************************************************************/
+
+/*
+ Disable indexes for a while
+ SYNOPSIS
+ disable_indexes()
+ mode Mode
+ RETURN VALUES
+ 0 Success
+ != 0 Error
+*/
+
+int ha_partition::disable_indexes(uint mode)
+{
+ handler **file;
+ int error= 0;
+
+ for (file= m_file; *file; file++)
+ {
+ if ((error= (*file)->ha_disable_indexes(mode)))
+ break;
+ }
+ return error;
+}
+
+
+/*
+ Enable indexes again
+ SYNOPSIS
+ enable_indexes()
+ mode Mode
+ RETURN VALUES
+ 0 Success
+ != 0 Error
+*/
+
+int ha_partition::enable_indexes(uint mode)
+{
+ handler **file;
+ int error= 0;
+
+ for (file= m_file; *file; file++)
+ {
+ if ((error= (*file)->ha_enable_indexes(mode)))
+ break;
+ }
+ return error;
+}
+
+
+/*
+ Check if indexes are disabled
+ SYNOPSIS
+ indexes_are_disabled()
+
+ RETURN VALUES
+ 0 Indexes are enabled
+ != 0 Indexes are disabled
+*/
+
+int ha_partition::indexes_are_disabled(void)
+{
+ handler **file;
+ int error= 0;
+
+ for (file= m_file; *file; file++)
+ {
+ if ((error= (*file)->indexes_are_disabled()))
+ break;
+ }
+ return error;
+}
+
+
+/****************************************************************************
+ 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;
+
+
+/*
+ Function we use in the creation of our hash to get key.
+*/
+
+static uchar *partition_get_key(PARTITION_SHARE *share, size_t *length,
+ my_bool not_used __attribute__ ((unused)))
+{
+ *length= share->table_name_length;
+ return (uchar *) share->table_name;
+}
+
+/*
+ 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.
+*/
+
+static PARTITION_SHARE *get_share(const char *table_name, TABLE *table)
+{
+ PARTITION_SHARE *share;
+ uint length;
+ char *tmp_name;
+
+ /*
+ 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)
+ {
+ /* 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);
+ }
+ pthread_mutex_lock(&partition_mutex);
+ length= (uint) strlen(table_name);
+
+ if (!(share= (PARTITION_SHARE *) hash_search(&partition_open_tables,
+ (uchar *) table_name, length)))
+ {
+ if (!(share= (PARTITION_SHARE *)
+ my_multi_malloc(MYF(MY_WME | MY_ZEROFILL),
+ &share, (uint) sizeof(*share),
+ &tmp_name, (uint) length + 1, NullS)))
+ {
+ pthread_mutex_unlock(&partition_mutex);
+ return NULL;
+ }
+
+ 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);
+
+ return share;
+
+error:
+ pthread_mutex_unlock(&partition_mutex);
+ my_free((uchar*) share, MYF(0));
+
+ return NULL;
+}
+
+
+/*
+ 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.
+*/
+
+static int free_share(PARTITION_SHARE *share)
+{
+ pthread_mutex_lock(&partition_mutex);
+ if (!--share->use_count)
+ {
+ hash_delete(&partition_open_tables, (uchar *) share);
+ thr_lock_delete(&share->lock);
+ pthread_mutex_destroy(&share->mutex);
+ my_free((uchar*) share, MYF(0));
+ }
+ pthread_mutex_unlock(&partition_mutex);
+
+ return 0;
+}
+#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;
+
+#endif
diff --git a/sql/ha_partition.h b/sql/ha_partition.h
new file mode 100644
index 00000000000..8a81a759e2a
--- /dev/null
+++ b/sql/ha_partition.h
@@ -0,0 +1,1092 @@
+/* Copyright 2005-2008 MySQL AB, 2008 Sun Microsystems, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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.
+*/
+
+#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
+{
+ ulonglong next_auto_inc_val; /**< first non reserved value */
+ bool auto_inc_initialized;
+} HA_DATA_PARTITION;
+
+#define PARTITION_BYTES_IN_POS 2
+#define PARTITION_ENABLED_TABLE_FLAGS (HA_FILE_BASED | HA_REC_NOT_IN_SEQ)
+#define PARTITION_DISABLED_TABLE_FLAGS (HA_CAN_GEOMETRY | \
+ HA_CAN_FULLTEXT | \
+ HA_DUPLICATE_POS | \
+ HA_CAN_SQL_HANDLER | \
+ HA_CAN_INSERT_DELAYED | \
+ HA_PRIMARY_KEY_REQUIRED_FOR_POSITION)
+class ha_partition :public handler
+{
+private:
+ enum partition_index_scan_type
+ {
+ partition_index_read= 0,
+ partition_index_first= 1,
+ partition_index_first_unordered= 2,
+ partition_index_last= 3,
+ partition_index_read_last= 4,
+ partition_read_range = 5,
+ partition_no_index_scan= 6
+ };
+ /* Data for the partition handler */
+ int m_mode; // Open mode
+ uint m_open_test_lock; // Open test_if_locked
+ char *m_file_buffer; // Buffer with names
+ char *m_name_buffer_ptr; // Pointer to first partition name
+ plugin_ref *m_engine_array; // Array of types of the handlers
+ handler **m_file; // Array of references to handler inst.
+ uint m_file_tot_parts; // Debug
+ handler **m_new_file; // Array of references to new handlers
+ handler **m_reorged_file; // Reorganised partitions
+ handler **m_added_file; // Added parts kept for errors
+ partition_info *m_part_info; // local reference to partition
+ Field **m_part_field_array; // Part field array locally to save acc
+ uchar *m_ordered_rec_buffer; // Row and key buffer for ord. idx scan
+ /*
+ Current index.
+ When used in key_rec_cmp: If clustered pk, index compare
+ must compare pk if given index is same for two rows.
+ So normally m_curr_key_info[0]= current index and m_curr_key[1]= NULL,
+ and if clustered pk, [0]= current index, [1]= pk, [2]= NULL
+ */
+ KEY *m_curr_key_info[3]; // Current index
+ uchar *m_rec0; // table->record[0]
+ QUEUE m_queue; // Prio queue used by sorted read
+ /*
+ Since the partition handler is a handler on top of other handlers, it
+ is necessary to keep information about what the underlying handler
+ characteristics is. It is not possible to keep any handler instances
+ for this since the MySQL Server sometimes allocating the handler object
+ without freeing them.
+ */
+ ulong m_low_byte_first;
+ enum enum_handler_status
+ {
+ handler_not_initialized= 0,
+ handler_initialized,
+ handler_opened,
+ handler_closed
+ };
+ enum_handler_status m_handler_status;
+
+ 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_last_part; // Last file that we update,write,read
+ int m_lock_type; // Remembers type of last
+ // external_lock
+ part_id_range m_part_spec; // Which parts to scan
+ uint m_scan_value; // Value passed in rnd_init
+ // call
+ uint m_ref_length; // Length of position in this
+ // handler object
+ key_range m_start_key; // index read key range
+ enum partition_index_scan_type m_index_scan_type;// What type of index
+ // scan
+ uint m_top_entry; // Which partition is to
+ // deliver next result
+ uint m_rec_length; // Local copy of record length
+
+ bool m_ordered; // Ordered/Unordered index scan
+ bool m_pkey_is_clustered; // Is primary key clustered
+ bool m_create_handler; // Handler used to create table
+ bool m_is_sub_partitioned; // Is subpartitioned
+ bool m_ordered_scan_ongoing;
+
+ /*
+ We keep track if all underlying handlers are MyISAM since MyISAM has a
+ great number of extra flags not needed by other handlers.
+ */
+ bool m_myisam; // Are all underlying handlers
+ // MyISAM
+ /*
+ We keep track of InnoDB handlers below since it requires proper setting
+ of query_id in fields at index_init and index_read calls.
+ */
+ bool m_innodb; // Are all underlying handlers
+ // InnoDB
+ /*
+ When calling extra(HA_EXTRA_CACHE) we do not pass this to the underlying
+ handlers immediately. Instead we cache it and call the underlying
+ immediately before starting the scan on the partition. This is to
+ prevent allocating a READ CACHE for each partition in parallel when
+ performing a full table scan on MyISAM partitioned table.
+ This state is cleared by extra(HA_EXTRA_NO_CACHE).
+ */
+ bool m_extra_cache;
+ uint m_extra_cache_size;
+
+ void init_handler_variables();
+ /*
+ Variables for lock structures.
+ */
+ THR_LOCK_DATA lock; /* MySQL lock */
+#ifdef NOT_USED
+ PARTITION_SHARE *share; /* Shared lock info */
+#endif
+
+ /*
+ TRUE <=> this object was created with ha_partition::clone and doesn't
+ "own" the m_part_info structure.
+ */
+ bool is_clone;
+ bool auto_increment_lock; /**< lock reading/updating auto_inc */
+ /**
+ Flag to keep the auto_increment lock through out the statement.
+ This to ensure it will work with statement based replication.
+ */
+ bool auto_increment_safe_stmt_log_lock;
+public:
+ handler *clone(MEM_ROOT *mem_root);
+ virtual void set_part_info(partition_info *part_info)
+ {
+ m_part_info= part_info;
+ m_is_sub_partitioned= part_info->is_sub_partitioned();
+ }
+ /*
+ -------------------------------------------------------------------------
+ MODULE create/delete handler object
+ -------------------------------------------------------------------------
+ Object create/delete methode. The normal called when a table object
+ exists. There is also a method to create the handler object with only
+ partition information. This is used from mysql_create_table when the
+ table is to be created and the engine type is deduced to be the
+ partition handler.
+ -------------------------------------------------------------------------
+ */
+ ha_partition(handlerton *hton, TABLE_SHARE * table);
+ ha_partition(handlerton *hton, partition_info * part_info);
+ ~ha_partition();
+ /*
+ A partition handler has no characteristics in itself. It only inherits
+ those from the underlying handlers. Here we set-up those constants to
+ enable later calls of the methods to retrieve constants from the under-
+ lying handlers. Returns false if not successful.
+ */
+ bool initialize_partition(MEM_ROOT *mem_root);
+
+ /*
+ -------------------------------------------------------------------------
+ MODULE meta data changes
+ -------------------------------------------------------------------------
+ Meta data routines to CREATE, DROP, RENAME table and often used at
+ ALTER TABLE (update_create_info used from ALTER TABLE and SHOW ..).
+
+ update_table_comment is used in SHOW TABLE commands to provide a
+ chance for the handler to add any interesting comments to the table
+ comments not provided by the users comment.
+
+ create_handler_files is called before opening a new handler object
+ with openfrm to call create. It is used to create any local handler
+ object needed in opening the object in openfrm
+ -------------------------------------------------------------------------
+ */
+ virtual int delete_table(const char *from);
+ virtual int rename_table(const char *from, const char *to);
+ virtual int create(const char *name, TABLE *form,
+ HA_CREATE_INFO *create_info);
+ virtual int create_handler_files(const char *name,
+ const char *old_name, int action_flag,
+ HA_CREATE_INFO *create_info);
+ virtual void update_create_info(HA_CREATE_INFO *create_info);
+ virtual char *update_table_comment(const char *comment);
+ virtual int change_partitions(HA_CREATE_INFO *create_info,
+ const char *path,
+ ulonglong * const copied,
+ ulonglong * const deleted,
+ const uchar *pack_frm_data,
+ 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)
+ {
+ DBUG_ENTER("ha_partition::get_no_parts");
+ *no_parts= m_tot_parts;
+ DBUG_RETURN(0);
+ }
+ virtual void change_table_ptr(TABLE *table_arg, TABLE_SHARE *share);
+ virtual bool check_if_incompatible_data(HA_CREATE_INFO *create_info,
+ uint table_changes);
+private:
+ int prepare_for_rename();
+ int copy_partitions(ulonglong * const copied, ulonglong * const deleted);
+ void cleanup_new_partition(uint part_count);
+ int prepare_new_partition(TABLE *table, HA_CREATE_INFO *create_info,
+ handler *file, const char *part_name,
+ partition_element *p_elem);
+ /*
+ delete_table, rename_table and create uses very similar logic which
+ is packed into this routine.
+ */
+ uint del_ren_cre_table(const char *from, const char *to,
+ TABLE *table_arg, HA_CREATE_INFO *create_info);
+ /*
+ One method to create the table_name.par file containing the names of the
+ underlying partitions, their engine and the number of partitions.
+ And one method to read it in.
+ */
+ bool create_handler_file(const char *name);
+ bool get_from_handler_file(const char *name, MEM_ROOT *mem_root);
+ bool new_handlers_from_part_info(MEM_ROOT *mem_root);
+ bool create_handlers(MEM_ROOT *mem_root);
+ void clear_handler_file();
+ int set_up_table_before_create(TABLE *table_arg,
+ const char *partition_name_with_path,
+ HA_CREATE_INFO *info,
+ uint part_id,
+ partition_element *p_elem);
+ partition_element *find_partition_element(uint part_id);
+
+public:
+
+ /*
+ -------------------------------------------------------------------------
+ MODULE open/close object
+ -------------------------------------------------------------------------
+ Open and close handler object to ensure all underlying files and
+ objects allocated and deallocated for query handling is handled
+ properly.
+ -------------------------------------------------------------------------
+
+ A handler object is opened as part of its initialisation and before
+ being used for normal queries (not before meta-data changes always.
+ If the object was opened it will also be closed before being deleted.
+ */
+ virtual int open(const char *name, int mode, uint test_if_locked);
+ virtual int close(void);
+
+ /*
+ -------------------------------------------------------------------------
+ MODULE start/end statement
+ -------------------------------------------------------------------------
+ This module contains methods that are used to understand start/end of
+ statements, transaction boundaries, and aid for proper concurrency
+ control.
+ The partition handler need not implement abort and commit since this
+ will be handled by any underlying handlers implementing transactions.
+ There is only one call to each handler type involved per transaction
+ and these go directly to the handlers supporting transactions
+ currently InnoDB, BDB and NDB).
+ -------------------------------------------------------------------------
+ */
+ virtual THR_LOCK_DATA **store_lock(THD * thd, THR_LOCK_DATA ** to,
+ enum thr_lock_type lock_type);
+ virtual int external_lock(THD * thd, int lock_type);
+ /*
+ When table is locked a statement is started by calling start_stmt
+ instead of external_lock
+ */
+ virtual int start_stmt(THD * thd, thr_lock_type lock_type);
+ /*
+ Lock count is number of locked underlying handlers (I assume)
+ */
+ virtual uint lock_count(void) const;
+ /*
+ Call to unlock rows not to be updated in transaction
+ */
+ virtual void unlock_row();
+ /*
+ Check if semi consistent read
+ */
+ virtual bool was_semi_consistent_read();
+ /*
+ Call to hint about semi consistent read
+ */
+ virtual void try_semi_consistent_read(bool);
+
+ /*
+ -------------------------------------------------------------------------
+ MODULE change record
+ -------------------------------------------------------------------------
+ This part of the handler interface is used to change the records
+ after INSERT, DELETE, UPDATE, REPLACE method calls but also other
+ special meta-data operations as ALTER TABLE, LOAD DATA, TRUNCATE.
+ -------------------------------------------------------------------------
+
+ These methods are used for insert (write_row), update (update_row)
+ and delete (delete_row). All methods to change data always work on
+ one row at a time. update_row and delete_row also contains the old
+ row.
+ delete_all_rows will delete all rows in the table in one call as a
+ special optimisation for DELETE from table;
+
+ Bulk inserts are supported if all underlying handlers support it.
+ start_bulk_insert and end_bulk_insert is called before and after a
+ number of calls to write_row.
+ Not yet though.
+ */
+ virtual int write_row(uchar * buf);
+ 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 void start_bulk_insert(ha_rows rows);
+ virtual int end_bulk_insert();
+
+ virtual bool is_fatal_error(int error, uint flags)
+ {
+ if (!handler::is_fatal_error(error, flags) ||
+ error == HA_ERR_NO_PARTITION_FOUND)
+ return FALSE;
+ return TRUE;
+ }
+ /*
+ -------------------------------------------------------------------------
+ MODULE full table scan
+ -------------------------------------------------------------------------
+ This module is used for the most basic access method for any table
+ handler. This is to fetch all data through a full table scan. No
+ indexes are needed to implement this part.
+ It contains one method to start the scan (rnd_init) that can also be
+ called multiple times (typical in a nested loop join). Then proceeding
+ to the next record (rnd_next) and closing the scan (rnd_end).
+ To remember a record for later access there is a method (position)
+ and there is a method used to retrieve the record based on the stored
+ position.
+ The position can be a file position, a primary key, a ROWID dependent
+ on the handler below.
+ -------------------------------------------------------------------------
+ */
+ /*
+ unlike index_init(), rnd_init() can be called two times
+ without rnd_end() in between (it only makes sense if scan=1).
+ then the second call should prepare for the new table scan
+ (e.g if rnd_init allocates the cursor, second call should
+ position it to the start of the table, no need to deallocate
+ and allocate it again
+ */
+ virtual int rnd_init(bool scan);
+ virtual int rnd_end();
+ virtual int rnd_next(uchar * buf);
+ virtual int rnd_pos(uchar * buf, uchar * pos);
+ virtual int rnd_pos_by_record(uchar *record);
+ virtual void position(const uchar * record);
+
+ /*
+ -------------------------------------------------------------------------
+ MODULE index scan
+ -------------------------------------------------------------------------
+ This part of the handler interface is used to perform access through
+ indexes. The interface is defined as a scan interface but the handler
+ can also use key lookup if the index is a unique index or a primary
+ key index.
+ Index scans are mostly useful for SELECT queries but are an important
+ part also of UPDATE, DELETE, REPLACE and CREATE TABLE table AS SELECT
+ and so forth.
+ Naturally an index is needed for an index scan and indexes can either
+ be ordered, hash based. Some ordered indexes can return data in order
+ but not necessarily all of them.
+ There are many flags that define the behavior of indexes in the
+ various handlers. These methods are found in the optimizer module.
+ -------------------------------------------------------------------------
+
+ index_read is called to start a scan of an index. The find_flag defines
+ the semantics of the scan. These flags are defined in
+ include/my_base.h
+ index_read_idx is the same but also initializes index before calling doing
+ the same thing as index_read. Thus it is similar to index_init followed
+ by index_read. This is also how we implement it.
+
+ index_read/index_read_idx does also return the first row. Thus for
+ key lookups, the index_read will be the only call to the handler in
+ the index scan.
+
+ index_init initializes an index before using it and index_end does
+ any end processing needed.
+ */
+ virtual int index_read_map(uchar * buf, const uchar * key,
+ key_part_map keypart_map,
+ enum ha_rkey_function find_flag);
+ virtual int index_init(uint idx, bool sorted);
+ virtual int index_end();
+
+ /*
+ These methods are used to jump to next or previous entry in the index
+ scan. There are also methods to jump to first and last entry.
+ */
+ virtual int index_next(uchar * buf);
+ virtual int index_prev(uchar * buf);
+ virtual int index_first(uchar * buf);
+ virtual int index_last(uchar * buf);
+ virtual int index_next_same(uchar * buf, const uchar * key, uint keylen);
+ virtual int index_read_last_map(uchar * buf, const uchar * key,
+ key_part_map keypart_map);
+
+ /*
+ read_first_row is virtual method but is only implemented by
+ handler.cc, no storage engine has implemented it so neither
+ will the partition handler.
+
+ virtual int read_first_row(uchar *buf, uint primary_key);
+ */
+
+ /*
+ We don't implement multi read range yet, will do later.
+ virtual int read_multi_range_first(KEY_MULTI_RANGE **found_range_p,
+ KEY_MULTI_RANGE *ranges, uint range_count,
+ bool sorted, HANDLER_BUFFER *buffer);
+ virtual int read_multi_range_next(KEY_MULTI_RANGE **found_range_p);
+ */
+
+
+ virtual int read_range_first(const key_range * start_key,
+ const key_range * end_key,
+ bool eq_range, bool sorted);
+ virtual int read_range_next();
+
+private:
+ int common_index_read(uchar * buf, bool have_start_key);
+ int common_first_last(uchar * buf);
+ int partition_scan_set_up(uchar * buf, bool idx_read_flag);
+ int handle_unordered_next(uchar * buf, bool next_same);
+ int handle_unordered_scan_next_partition(uchar * buf);
+ uchar *queue_buf(uint part_id)
+ {
+ return (m_ordered_rec_buffer +
+ (part_id * (m_rec_length + PARTITION_BYTES_IN_POS)));
+ }
+ uchar *rec_buf(uint part_id)
+ {
+ return (queue_buf(part_id) +
+ PARTITION_BYTES_IN_POS);
+ }
+ int handle_ordered_index_scan(uchar * buf, bool reverse_order);
+ int handle_ordered_next(uchar * buf, bool next_same);
+ int handle_ordered_prev(uchar * buf);
+ void return_top_record(uchar * buf);
+ void column_bitmaps_signal();
+public:
+ /*
+ -------------------------------------------------------------------------
+ MODULE information calls
+ -------------------------------------------------------------------------
+ This calls are used to inform the handler of specifics of the ongoing
+ scans and other actions. Most of these are used for optimisation
+ purposes.
+ -------------------------------------------------------------------------
+ */
+ virtual int info(uint);
+ void get_dynamic_partition_info(PARTITION_INFO *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);
+
+private:
+ static const uint NO_CURRENT_PART_ID;
+ int loop_extra(enum ha_extra_function operation);
+ void late_extra_cache(uint partition_id);
+ void late_extra_no_cache(uint partition_id);
+ void prepare_extra_cache(uint cachesize);
+public:
+
+ /*
+ -------------------------------------------------------------------------
+ MODULE optimiser support
+ -------------------------------------------------------------------------
+ -------------------------------------------------------------------------
+ */
+
+ /*
+ NOTE !!!!!!
+ -------------------------------------------------------------------------
+ -------------------------------------------------------------------------
+ One important part of the public handler interface that is not depicted in
+ the methods is the attribute records
+
+ which is defined in the base class. This is looked upon directly and is
+ set by calling info(HA_STATUS_INFO) ?
+ -------------------------------------------------------------------------
+ */
+
+ /*
+ keys_to_use_for_scanning can probably be implemented as the
+ intersection of all underlying handlers if mixed handlers are used.
+ This method is used to derive whether an index can be used for
+ index-only scanning when performing an ORDER BY query.
+ Only called from one place in sql_select.cc
+ */
+ virtual const key_map *keys_to_use_for_scanning();
+
+ /*
+ Called in test_quick_select to determine if indexes should be used.
+ */
+ virtual double scan_time();
+
+ /*
+ The next method will never be called if you do not implement indexes.
+ */
+ virtual double read_time(uint index, uint ranges, ha_rows rows);
+ /*
+ For the given range how many records are estimated to be in this range.
+ Used by optimiser to calculate cost of using a particular index.
+ */
+ virtual ha_rows records_in_range(uint inx, key_range * min_key,
+ key_range * max_key);
+
+ /*
+ Upper bound of number records returned in scan is sum of all
+ underlying handlers.
+ */
+ virtual ha_rows estimate_rows_upper_bound();
+
+ /*
+ table_cache_type is implemented by the underlying handler but all
+ underlying handlers must have the same implementation for it to work.
+ */
+ virtual uint8 table_cache_type();
+ virtual ha_rows records();
+
+ /*
+ -------------------------------------------------------------------------
+ MODULE print messages
+ -------------------------------------------------------------------------
+ This module contains various methods that returns text messages for
+ table types, index type and error messages.
+ -------------------------------------------------------------------------
+ */
+ /*
+ The name of the index type that will be used for display
+ Here we must ensure that all handlers use the same index type
+ for each index created.
+ */
+ virtual const char *index_type(uint inx);
+
+ /* The name of the table type that will be used for display purposes */
+ virtual const char *table_type() const;
+
+ /* The name of the row type used for the underlying tables. */
+ virtual enum row_type get_row_type() const;
+
+ /*
+ Handler specific error messages
+ */
+ virtual void print_error(int error, myf errflag);
+ virtual bool get_error_message(int error, String * buf);
+ /*
+ -------------------------------------------------------------------------
+ MODULE handler characteristics
+ -------------------------------------------------------------------------
+ This module contains a number of methods defining limitations and
+ characteristics of the handler. The partition handler will calculate
+ this characteristics based on underlying handler characteristics.
+ -------------------------------------------------------------------------
+
+ This is a list of flags that says what the storage engine
+ implements. The current table flags are documented in handler.h
+ The partition handler will support whatever the underlying handlers
+ support except when specifically mentioned below about exceptions
+ to this rule.
+ NOTE: This cannot be cached since it can depend on TRANSACTION ISOLATION
+ LEVEL which is dynamic, see bug#39084.
+
+ HA_READ_RND_SAME:
+ Not currently used. (Means that the handler supports the rnd_same() call)
+ (MyISAM, HEAP)
+
+ HA_TABLE_SCAN_ON_INDEX:
+ Used to avoid scanning full tables on an index. If this flag is set then
+ the handler always has a primary key (hidden if not defined) and this
+ index is used for scanning rather than a full table scan in all
+ situations.
+ (InnoDB, BDB, Federated)
+
+ HA_REC_NOT_IN_SEQ:
+ This flag is set for handlers that cannot guarantee that the rows are
+ returned accroding to incremental positions (0, 1, 2, 3...).
+ This also means that rnd_next() should return HA_ERR_RECORD_DELETED
+ if it finds a deleted row.
+ (MyISAM (not fixed length row), BDB, HEAP, NDB, InooDB)
+
+ HA_CAN_GEOMETRY:
+ Can the storage engine handle spatial data.
+ Used to check that no spatial attributes are declared unless
+ the storage engine is capable of handling it.
+ (MyISAM)
+
+ HA_FAST_KEY_READ:
+ Setting this flag indicates that the handler is equally fast in
+ finding a row by key as by position.
+ This flag is used in a very special situation in conjunction with
+ filesort's. For further explanation see intro to init_read_record.
+ (BDB, HEAP, InnoDB)
+
+ HA_NULL_IN_KEY:
+ Is NULL values allowed in indexes.
+ If this is not allowed then it is not possible to use an index on a
+ NULLable field.
+ (BDB, HEAP, MyISAM, NDB, InnoDB)
+
+ HA_DUPLICATE_POS:
+ Tells that we can the position for the conflicting duplicate key
+ record is stored in table->file->dupp_ref. (insert uses rnd_pos() on
+ this to find the duplicated row)
+ (MyISAM)
+
+ HA_CAN_INDEX_BLOBS:
+ Is the storage engine capable of defining an index of a prefix on
+ a BLOB attribute.
+ (BDB, Federated, MyISAM, InnoDB)
+
+ HA_AUTO_PART_KEY:
+ Auto increment fields can be part of a multi-part key. For second part
+ auto-increment keys, the auto_incrementing is done in handler.cc
+ (BDB, Federated, MyISAM, NDB)
+
+ HA_REQUIRE_PRIMARY_KEY:
+ Can't define a table without primary key (and cannot handle a table
+ with hidden primary key)
+ (No handler has this limitation currently)
+
+ HA_STATS_RECORDS_IS_EXACT:
+ Does the counter of records after the info call specify an exact
+ value or not. If it does this flag is set.
+ Only MyISAM and HEAP uses exact count.
+
+ HA_CAN_INSERT_DELAYED:
+ Can the storage engine support delayed inserts.
+ To start with the partition handler will not support delayed inserts.
+ Further investigation needed.
+ (HEAP, MyISAM)
+
+ HA_PRIMARY_KEY_IN_READ_INDEX:
+ This parameter is set when the handler will also return the primary key
+ when doing read-only-key on another index.
+
+ HA_NOT_DELETE_WITH_CACHE:
+ Seems to be an old MyISAM feature that is no longer used. No handler
+ has it defined but it is checked in init_read_record.
+ Further investigation needed.
+ (No handler defines it)
+
+ HA_NO_PREFIX_CHAR_KEYS:
+ Indexes on prefixes of character fields is not allowed.
+ (NDB)
+
+ HA_CAN_FULLTEXT:
+ Does the storage engine support fulltext indexes
+ The partition handler will start by not supporting fulltext indexes.
+ (MyISAM)
+
+ HA_CAN_SQL_HANDLER:
+ Can the HANDLER interface in the MySQL API be used towards this
+ storage engine.
+ (MyISAM, InnoDB)
+
+ HA_NO_AUTO_INCREMENT:
+ Set if the storage engine does not support auto increment fields.
+ (Currently not set by any handler)
+
+ HA_HAS_CHECKSUM:
+ Special MyISAM feature. Has special SQL support in CREATE TABLE.
+ No special handling needed by partition handler.
+ (MyISAM)
+
+ HA_FILE_BASED:
+ Should file names always be in lower case (used by engines
+ that map table names to file names.
+ Since partition handler has a local file this flag is set.
+ (BDB, Federated, MyISAM)
+
+ HA_CAN_BIT_FIELD:
+ Is the storage engine capable of handling bit fields?
+ (MyISAM, NDB)
+
+ HA_NEED_READ_RANGE_BUFFER:
+ Is Read Multi-Range supported => need multi read range buffer
+ This parameter specifies whether a buffer for read multi range
+ is needed by the handler. Whether the handler supports this
+ feature or not is dependent of whether the handler implements
+ read_multi_range* calls or not. The only handler currently
+ supporting this feature is NDB so the partition handler need
+ not handle this call. There are methods in handler.cc that will
+ transfer those calls into index_read and other calls in the
+ index scan module.
+ (NDB)
+
+ HA_PRIMARY_KEY_REQUIRED_FOR_POSITION:
+ Does the storage engine need a PK for position?
+ Used with hidden primary key in InnoDB.
+ Hidden primary keys cannot be supported by partitioning, since the
+ partitioning expressions columns must be a part of the primary key.
+ (InnoDB)
+
+ HA_FILE_BASED is always set for partition handler since we use a
+ special file for handling names of partitions, engine types.
+ HA_REC_NOT_IN_SEQ is always set for partition handler since we cannot
+ guarantee that the records will be returned in sequence.
+ HA_CAN_GEOMETRY, HA_CAN_FULLTEXT, HA_CAN_SQL_HANDLER, HA_DUPLICATE_POS,
+ HA_CAN_INSERT_DELAYED, HA_PRIMARY_KEY_REQUIRED_FOR_POSITION is disabled
+ until further investigated.
+ */
+ virtual Table_flags table_flags() const
+ {
+ DBUG_ENTER("ha_partition::table_flags");
+ if (m_handler_status < handler_initialized ||
+ m_handler_status >= handler_closed)
+ DBUG_RETURN(PARTITION_ENABLED_TABLE_FLAGS);
+ else
+ DBUG_RETURN((m_file[0]->ha_table_flags() &
+ ~(PARTITION_DISABLED_TABLE_FLAGS)) |
+ (PARTITION_ENABLED_TABLE_FLAGS));
+ }
+
+ /*
+ This is a bitmap of flags that says how the storage engine
+ implements indexes. The current index flags are documented in
+ handler.h. If you do not implement indexes, just return zero
+ here.
+
+ part is the key part to check. First key part is 0
+ If all_parts it's set, MySQL want to know the flags for the combined
+ index up to and including 'part'.
+
+ HA_READ_NEXT:
+ Does the index support read next, this is assumed in the server
+ code and never checked so all indexes must support this.
+ Note that the handler can be used even if it doesn't have any index.
+ (BDB, HEAP, MyISAM, Federated, NDB, InnoDB)
+
+ HA_READ_PREV:
+ Can the index be used to scan backwards.
+ (BDB, HEAP, MyISAM, NDB, InnoDB)
+
+ HA_READ_ORDER:
+ Can the index deliver its record in index order. Typically true for
+ all ordered indexes and not true for hash indexes.
+ In first step this is not true for partition handler until a merge
+ sort has been implemented in partition handler.
+ Used to set keymap part_of_sortkey
+ This keymap is only used to find indexes usable for resolving an ORDER BY
+ in the query. Thus in most cases index_read will work just fine without
+ order in result production. When this flag is set it is however safe to
+ order all output started by index_read since most engines do this. With
+ read_multi_range calls there is a specific flag setting order or not
+ order so in those cases ordering of index output can be avoided.
+ (BDB, InnoDB, HEAP, MyISAM, NDB)
+
+ HA_READ_RANGE:
+ Specify whether index can handle ranges, typically true for all
+ ordered indexes and not true for hash indexes.
+ Used by optimiser to check if ranges (as key >= 5) can be optimised
+ by index.
+ (BDB, InnoDB, NDB, MyISAM, HEAP)
+
+ HA_ONLY_WHOLE_INDEX:
+ Can't use part key searches. This is typically true for hash indexes
+ and typically not true for ordered indexes.
+ (Federated, NDB, HEAP)
+
+ HA_KEYREAD_ONLY:
+ Does the storage engine support index-only scans on this index.
+ Enables use of HA_EXTRA_KEYREAD and HA_EXTRA_NO_KEYREAD
+ Used to set key_map keys_for_keyread and to check in optimiser for
+ index-only scans. When doing a read under HA_EXTRA_KEYREAD the handler
+ only have to fill in the columns the key covers. If
+ HA_PRIMARY_KEY_IN_READ_INDEX is set then also the PRIMARY KEY columns
+ must be updated in the row.
+ (BDB, InnoDB, MyISAM)
+ */
+ virtual ulong index_flags(uint inx, uint part, bool all_parts) const
+ {
+ return m_file[0]->index_flags(inx, part, all_parts);
+ }
+
+ /**
+ wrapper function for handlerton alter_table_flags, since
+ the ha_partition_hton cannot know all its capabilities
+ */
+ virtual uint alter_table_flags(uint flags);
+ /*
+ extensions of table handler files
+ */
+ virtual const char **bas_ext() const;
+ /*
+ unireg.cc will call the following to make sure that the storage engine
+ can handle the data it is about to send.
+
+ The maximum supported values is the minimum of all handlers in the table
+ */
+ uint min_of_the_max_uint(uint (handler::*operator_func)(void) const) const;
+ virtual uint max_supported_record_length() const;
+ virtual uint max_supported_keys() const;
+ virtual uint max_supported_key_parts() const;
+ virtual uint max_supported_key_length() const;
+ virtual uint max_supported_key_part_length() const;
+
+ /*
+ All handlers in a partitioned table must have the same low_byte_first
+ */
+ virtual bool low_byte_first() const
+ { return m_low_byte_first; }
+
+ /*
+ The extra record buffer length is the maximum needed by all handlers.
+ The minimum record length is the maximum of all involved handlers.
+ */
+ virtual uint extra_rec_buf_length() const;
+ virtual uint min_record_length(uint options) const;
+
+ /*
+ Primary key is clustered can only be true if all underlying handlers have
+ this feature.
+ */
+ virtual bool primary_key_is_clustered()
+ { return m_pkey_is_clustered; }
+
+ /*
+ -------------------------------------------------------------------------
+ MODULE compare records
+ -------------------------------------------------------------------------
+ cmp_ref checks if two references are the same. For most handlers this is
+ a simple memcmp of the reference. However some handlers use primary key
+ as reference and this can be the same even if memcmp says they are
+ different. This is due to character sets and end spaces and so forth.
+ For the partition handler the reference is first two bytes providing the
+ partition identity of the referred record and then the reference of the
+ underlying handler.
+ Thus cmp_ref for the partition handler always returns FALSE for records
+ not in the same partition and uses cmp_ref on the underlying handler
+ to check whether the rest of the reference part is also the same.
+ -------------------------------------------------------------------------
+ */
+ virtual int cmp_ref(const uchar * ref1, const uchar * ref2);
+ /*
+ -------------------------------------------------------------------------
+ MODULE auto increment
+ -------------------------------------------------------------------------
+ This module is used to handle the support of auto increments.
+
+ This variable in the handler is used as part of the handler interface
+ It is maintained by the parent handler object and should not be
+ touched by child handler objects (see handler.cc for its use).
+
+ auto_increment_column_changed
+ -------------------------------------------------------------------------
+ */
+ virtual void get_auto_increment(ulonglong offset, ulonglong increment,
+ ulonglong nb_desired_values,
+ ulonglong *first_value,
+ ulonglong *nb_reserved_values);
+ virtual void release_auto_increment();
+private:
+ virtual int reset_auto_increment(ulonglong value);
+ virtual void lock_auto_increment()
+ {
+ /* lock already taken */
+ if (auto_increment_safe_stmt_log_lock)
+ return;
+ DBUG_ASSERT(table_share->ha_data && !auto_increment_lock);
+ if(table_share->tmp_table == NO_TMP_TABLE)
+ {
+ auto_increment_lock= TRUE;
+ pthread_mutex_lock(&table_share->mutex);
+ }
+ }
+ virtual void unlock_auto_increment()
+ {
+ DBUG_ASSERT(table_share->ha_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
+ ha_partition::release_auto_increment.
+ */
+ if(auto_increment_lock && !auto_increment_safe_stmt_log_lock)
+ {
+ pthread_mutex_unlock(&table_share->mutex);
+ auto_increment_lock= FALSE;
+ }
+ }
+ virtual void set_auto_increment_if_higher(const ulonglong nr)
+ {
+ HA_DATA_PARTITION *ha_data= (HA_DATA_PARTITION*) table_share->ha_data;
+ lock_auto_increment();
+ DBUG_ASSERT(ha_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;
+ unlock_auto_increment();
+ }
+
+public:
+
+ /*
+ -------------------------------------------------------------------------
+ MODULE initialize handler for HANDLER call
+ -------------------------------------------------------------------------
+ This method is a special InnoDB method called before a HANDLER query.
+ -------------------------------------------------------------------------
+ */
+ virtual void init_table_handle_for_HANDLER();
+
+ /*
+ The remainder of this file defines the handler methods not implemented
+ by the partition handler
+ */
+
+ /*
+ -------------------------------------------------------------------------
+ MODULE foreign key support
+ -------------------------------------------------------------------------
+ The following methods are used to implement foreign keys as supported by
+ InnoDB. Implement this ??
+ get_foreign_key_create_info is used by SHOW CREATE TABLE to get a textual
+ description of how the CREATE TABLE part to define FOREIGN KEY's is done.
+ free_foreign_key_create_info is used to free the memory area that provided
+ this description.
+ can_switch_engines checks if it is ok to switch to a new engine based on
+ the foreign key info in the table.
+ -------------------------------------------------------------------------
+
+ virtual char* get_foreign_key_create_info()
+ virtual void free_foreign_key_create_info(char* str)
+
+ virtual int get_foreign_key_list(THD *thd,
+ List<FOREIGN_KEY_INFO> *f_key_list)
+ virtual uint referenced_by_foreign_key()
+ */
+ virtual bool can_switch_engines();
+ /*
+ -------------------------------------------------------------------------
+ MODULE fulltext index
+ -------------------------------------------------------------------------
+ Fulltext stuff not yet.
+ -------------------------------------------------------------------------
+ virtual int ft_init() { return HA_ERR_WRONG_COMMAND; }
+ virtual FT_INFO *ft_init_ext(uint flags,uint inx,const uchar *key,
+ uint keylen)
+ { return NULL; }
+ virtual int ft_read(uchar *buf) { return HA_ERR_WRONG_COMMAND; }
+ */
+
+ /*
+ -------------------------------------------------------------------------
+ MODULE restart full table scan at position (MyISAM)
+ -------------------------------------------------------------------------
+ The following method is only used by MyISAM when used as
+ temporary tables in a join.
+ virtual int restart_rnd_next(uchar *buf, uchar *pos);
+ */
+
+ /*
+ -------------------------------------------------------------------------
+ MODULE on-line ALTER TABLE
+ -------------------------------------------------------------------------
+ These methods are in the handler interface. (used by innodb-plugin)
+ They are used for on-line/fast alter table add/drop index:
+ -------------------------------------------------------------------------
+ */
+ virtual int add_index(TABLE *table_arg, KEY *key_info, uint num_of_keys);
+ virtual int prepare_drop_index(TABLE *table_arg, uint *key_num,
+ uint num_of_keys);
+ virtual int final_drop_index(TABLE *table_arg);
+
+ /*
+ -------------------------------------------------------------------------
+ MODULE tablespace support
+ -------------------------------------------------------------------------
+ Admin of table spaces is not applicable to the partition handler (InnoDB)
+ This means that the following method is not implemented:
+ -------------------------------------------------------------------------
+ virtual int discard_or_import_tablespace(my_bool discard)
+ */
+
+ /*
+ -------------------------------------------------------------------------
+ MODULE admin MyISAM
+ -------------------------------------------------------------------------
+
+ -------------------------------------------------------------------------
+ OPTIMIZE TABLE, CHECK TABLE, ANALYZE TABLE and REPAIR TABLE are
+ mapped to a routine that handles looping over a given set of
+ partitions and those routines send a flag indicating to execute on
+ all partitions.
+ -------------------------------------------------------------------------
+ */
+ virtual int optimize(THD* thd, HA_CHECK_OPT *check_opt);
+ virtual int analyze(THD* thd, HA_CHECK_OPT *check_opt);
+ virtual int check(THD* thd, HA_CHECK_OPT *check_opt);
+ virtual int repair(THD* thd, HA_CHECK_OPT *check_opt);
+ virtual bool check_and_repair(THD *thd);
+ virtual bool auto_repair() const;
+ virtual bool is_crashed() const;
+
+ private:
+ int handle_opt_partitions(THD *thd, HA_CHECK_OPT *check_opt, uint flags);
+ public:
+ /*
+ -------------------------------------------------------------------------
+ Admin commands not supported currently (almost purely MyISAM routines)
+ This means that the following methods are not implemented:
+ -------------------------------------------------------------------------
+
+ virtual int backup(TD* thd, HA_CHECK_OPT *check_opt);
+ virtual int restore(THD* thd, HA_CHECK_OPT *check_opt);
+ virtual int 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;
+ */
+
+ /*
+ -------------------------------------------------------------------------
+ MODULE enable/disable indexes
+ -------------------------------------------------------------------------
+ Enable/Disable Indexes are only supported by HEAP and MyISAM.
+ -------------------------------------------------------------------------
+ */
+ virtual int disable_indexes(uint mode);
+ virtual int enable_indexes(uint mode);
+ virtual int indexes_are_disabled(void);
+
+ /*
+ -------------------------------------------------------------------------
+ MODULE append_create_info
+ -------------------------------------------------------------------------
+ append_create_info is only used by MyISAM MERGE tables and the partition
+ handler will not support this handler as underlying handler.
+ Implement this??
+ -------------------------------------------------------------------------
+ virtual void append_create_info(String *packet)
+ */
+};
diff --git a/sql/handler.cc b/sql/handler.cc
index d069d56caae..948cb08b13f 100644
--- a/sql/handler.cc
+++ b/sql/handler.cc
@@ -1,4 +1,4 @@
-/* Copyright (C) 2000-2006 MySQL AB
+/* Copyright 2000-2008 MySQL AB, 2008 Sun Microsystems, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -13,154 +13,56 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+/** @file handler.cc
-/* Handler-calling-functions */
+ @brief
+ Handler-calling-functions
+*/
#ifdef USE_PRAGMA_IMPLEMENTATION
#pragma implementation // gcc: Class implementation
#endif
#include "mysql_priv.h"
-#include "ha_heap.h"
-#include "ha_myisam.h"
-#include "ha_myisammrg.h"
-
-
-/*
- We have dummy hanldertons in case the handler has not been compiled
- in. This will be removed in 5.1.
-*/
-#ifdef HAVE_BERKELEY_DB
-#include "ha_berkeley.h"
-extern handlerton berkeley_hton;
-#else
-handlerton berkeley_hton = { "BerkeleyDB", SHOW_OPTION_NO,
- "Supports transactions and page-level locking", DB_TYPE_BERKELEY_DB, NULL,
- 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
- NULL, NULL, HTON_NO_FLAGS };
-#endif
-#ifdef HAVE_BLACKHOLE_DB
-#include "ha_blackhole.h"
-extern handlerton blackhole_hton;
-#else
-handlerton blackhole_hton = { "BLACKHOLE", SHOW_OPTION_NO,
- "/dev/null storage engine (anything you write to it disappears)",
- DB_TYPE_BLACKHOLE_DB, NULL, 0, 0, NULL, NULL,
- NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
- HTON_NO_FLAGS };
-#endif
-#ifdef HAVE_EXAMPLE_DB
-#include "examples/ha_example.h"
-extern handlerton example_hton;
-#else
-handlerton example_hton = { "EXAMPLE", SHOW_OPTION_NO,
- "Example storage engine",
- DB_TYPE_EXAMPLE_DB, NULL, 0, 0, NULL, NULL,
- NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
- HTON_NO_FLAGS };
-#endif
-#if defined(HAVE_ARCHIVE_DB)
-#include "ha_archive.h"
-extern handlerton archive_hton;
-#else
-handlerton archive_hton = { "ARCHIVE", SHOW_OPTION_NO,
- "Archive storage engine", DB_TYPE_ARCHIVE_DB, NULL, 0, 0, NULL, NULL,
- NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
- HTON_NO_FLAGS };
-#endif
-#ifdef HAVE_CSV_DB
-#include "examples/ha_tina.h"
-extern handlerton tina_hton;
-#else
-handlerton tina_hton = { "CSV", SHOW_OPTION_NO, "CSV storage engine",
- DB_TYPE_CSV_DB, NULL, 0, 0, NULL, NULL,
- NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
- HTON_NO_FLAGS };
-#endif
-#ifdef HAVE_INNOBASE_DB
-#include "ha_innodb.h"
-extern handlerton innobase_hton;
-#else
-handlerton innobase_hton = { "InnoDB", SHOW_OPTION_NO,
- "Supports transactions, row-level locking, and foreign keys",
- DB_TYPE_INNODB, NULL, 0, 0, NULL, NULL,
- NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
- HTON_NO_FLAGS };
-#endif
-#ifdef HAVE_NDBCLUSTER_DB
-#include "ha_ndbcluster.h"
-extern handlerton ndbcluster_hton;
-#else
-handlerton ndbcluster_hton = { "ndbcluster", SHOW_OPTION_NO,
- "Clustered, fault-tolerant, memory-based tables",
- DB_TYPE_NDBCLUSTER, NULL, 0, 0, NULL, NULL,
- NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
- HTON_NO_FLAGS };
-#endif
-#ifdef HAVE_FEDERATED_DB
-#include "ha_federated.h"
-extern handlerton federated_hton;
-#else
-handlerton federated_hton = { "FEDERATED", SHOW_OPTION_NO,
- "Federated MySQL storage engine", DB_TYPE_FEDERATED_DB, NULL, 0, 0, NULL,
- NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
- HTON_NO_FLAGS };
-#endif
+#include "rpl_filter.h"
#include <myisampack.h>
#include <errno.h>
-extern handlerton myisam_hton;
-extern handlerton myisammrg_hton;
-extern handlerton heap_hton;
-extern handlerton binlog_hton;
+#ifdef WITH_PARTITION_STORAGE_ENGINE
+#include "ha_partition.h"
+#endif
/*
- Obsolete
+ While we have legacy_db_type, we have this array to
+ check for dups and to find handlerton from legacy_db_type.
+ Remove when legacy_db_type is finally gone
*/
-handlerton isam_hton = { "ISAM", SHOW_OPTION_NO, "Obsolete storage engine",
- DB_TYPE_ISAM, NULL, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
- NULL, NULL, NULL, NULL, NULL, NULL, HTON_NO_FLAGS };
+st_plugin_int *hton2plugin[MAX_HA];
+
+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} };
/* number of entries in handlertons[] */
-ulong total_ha;
+ulong total_ha= 0;
/* number of storage engines (from handlertons[]) that support 2pc */
-ulong total_ha_2pc;
+ulong total_ha_2pc= 0;
/* size of savepoint storage area (see ha_init) */
-ulong savepoint_alloc_size;
+ulong savepoint_alloc_size= 0;
-/*
- This array is used for processing compiled in engines.
-*/
-handlerton *sys_table_types[]=
-{
- &myisam_hton,
- &heap_hton,
- &innobase_hton,
- &berkeley_hton,
- &blackhole_hton,
- &example_hton,
- &archive_hton,
- &tina_hton,
- &ndbcluster_hton,
- &federated_hton,
- &myisammrg_hton,
- &binlog_hton,
- &isam_hton,
- NULL
-};
-
-struct show_table_alias_st sys_table_aliases[]=
+static const LEX_STRING sys_table_aliases[]=
{
- {"INNOBASE", "InnoDB"},
- {"NDB", "NDBCLUSTER"},
- {"BDB", "BERKELEYDB"},
- {"HEAP", "MEMORY"},
- {"MERGE", "MRG_MYISAM"},
- {NullS, NullS}
+ { C_STRING_WITH_LEN("INNOBASE") }, { C_STRING_WITH_LEN("INNODB") },
+ { C_STRING_WITH_LEN("NDB") }, { C_STRING_WITH_LEN("NDBCLUSTER") },
+ { C_STRING_WITH_LEN("HEAP") }, { C_STRING_WITH_LEN("MEMORY") },
+ { C_STRING_WITH_LEN("MERGE") }, { C_STRING_WITH_LEN("MRG_MYISAM") },
+ {NullS, 0}
};
const char *ha_row_type[] = {
- "", "FIXED", "DYNAMIC", "COMPRESSED", "REDUNDANT", "COMPACT", "?","?","?"
+ "", "FIXED", "DYNAMIC", "COMPRESSED", "REDUNDANT", "COMPACT", "PAGE", "?","?","?"
};
const char *tx_isolation_names[] =
@@ -172,209 +74,222 @@ TYPELIB tx_isolation_typelib= {array_elements(tx_isolation_names)-1,"",
static TYPELIB known_extensions= {0,"known_exts", NULL, NULL};
uint known_extensions_id= 0;
-enum db_type ha_resolve_by_name(const char *name, uint namelen)
+
+
+static plugin_ref ha_default_plugin(THD *thd)
{
- THD *thd= current_thd;
- show_table_alias_st *table_alias;
- handlerton **types;
+ if (thd->variables.table_plugin)
+ return thd->variables.table_plugin;
+ return my_plugin_lock(thd, &global_system_variables.table_plugin);
+}
+
- if (thd && !my_strnncoll(&my_charset_latin1,
- (const uchar *)name, namelen,
- (const uchar *)"DEFAULT", 7))
- return (enum db_type) thd->variables.table_type;
+/** @brief
+ Return the default storage engine handlerton for thread
-retest:
- for (types= sys_table_types; *types; types++)
+ SYNOPSIS
+ ha_default_handlerton(thd)
+ thd current thread
+
+ RETURN
+ pointer to handlerton
+*/
+handlerton *ha_default_handlerton(THD *thd)
+{
+ plugin_ref plugin= ha_default_plugin(thd);
+ DBUG_ASSERT(plugin);
+ handlerton *hton= plugin_data(plugin, handlerton*);
+ DBUG_ASSERT(hton);
+ return hton;
+}
+
+
+/** @brief
+ Return the storage engine handlerton for the supplied name
+
+ SYNOPSIS
+ ha_resolve_by_name(thd, name)
+ thd current thread
+ name name of storage engine
+
+ RETURN
+ pointer to storage engine plugin handle
+*/
+plugin_ref ha_resolve_by_name(THD *thd, const LEX_STRING *name)
+{
+ const LEX_STRING *table_alias;
+ plugin_ref plugin;
+
+redo:
+ /* my_strnncoll is a macro and gcc doesn't do early expansion of macro */
+ if (thd && !my_charset_latin1.coll->strnncoll(&my_charset_latin1,
+ (const uchar *)name->str, name->length,
+ (const uchar *)STRING_WITH_LEN("DEFAULT"), 0))
+ return ha_default_plugin(thd);
+
+ if ((plugin= my_plugin_lock_by_name(thd, name, MYSQL_STORAGE_ENGINE_PLUGIN)))
{
- if (!my_strnncoll(&my_charset_latin1,
- (const uchar *)name, namelen,
- (const uchar *)(*types)->name,
- (uint) strlen((*types)->name)))
- return (enum db_type) (*types)->db_type;
+ handlerton *hton= plugin_data(plugin, handlerton *);
+ if (!(hton->flags & HTON_NOT_USER_SELECTABLE))
+ return plugin;
+
+ /*
+ unlocking plugin immediately after locking is relatively low cost.
+ */
+ plugin_unlock(thd, plugin);
}
/*
We check for the historical aliases.
*/
- for (table_alias= sys_table_aliases; table_alias->type; table_alias++)
+ for (table_alias= sys_table_aliases; table_alias->str; table_alias+= 2)
{
if (!my_strnncoll(&my_charset_latin1,
- (const uchar *)name, namelen,
- (const uchar *)table_alias->alias,
- (uint) strlen(table_alias->alias)))
+ (const uchar *)name->str, name->length,
+ (const uchar *)table_alias->str, table_alias->length))
{
- name= table_alias->type;
- namelen= (uint) strlen(name);
- goto retest;
+ name= table_alias + 1;
+ goto redo;
}
}
- return DB_TYPE_UNKNOWN;
+ return NULL;
}
-const char *ha_get_storage_engine(enum db_type db_type)
+plugin_ref ha_lock_engine(THD *thd, handlerton *hton)
{
- handlerton **types;
- for (types= sys_table_types; *types; types++)
+ if (hton)
{
- if (db_type == (*types)->db_type)
- return (*types)->name;
+ st_plugin_int **plugin= hton2plugin + hton->slot;
+
+#ifdef DBUG_OFF
+ return my_plugin_lock(thd, plugin);
+#else
+ return my_plugin_lock(thd, &plugin);
+#endif
}
- return "*NONE*";
+ return NULL;
}
-bool ha_check_storage_engine_flag(enum db_type db_type, uint32 flag)
+#ifdef NOT_USED
+static handler *create_default(TABLE_SHARE *table, MEM_ROOT *mem_root)
{
- handlerton **types;
- for (types= sys_table_types; *types; types++)
- {
- if (db_type == (*types)->db_type)
- return test((*types)->flags & flag);
- }
- return FALSE; // No matching engine
+ handlerton *hton= ha_default_handlerton(current_thd);
+ return (hton && hton->create) ? hton->create(hton, table, mem_root) : NULL;
}
+#endif
-my_bool ha_storage_engine_is_enabled(enum db_type database_type)
+handlerton *ha_resolve_by_legacy_type(THD *thd, enum legacy_db_type db_type)
{
- handlerton **types;
- for (types= sys_table_types; *types; types++)
- {
- if ((database_type == (*types)->db_type) &&
- ((*types)->state == SHOW_OPTION_YES))
- return TRUE;
+ plugin_ref plugin;
+ switch (db_type) {
+ case DB_TYPE_DEFAULT:
+ return ha_default_handlerton(thd);
+ default:
+ if (db_type > DB_TYPE_UNKNOWN && db_type < DB_TYPE_DEFAULT &&
+ (plugin= ha_lock_engine(thd, installed_htons[db_type])))
+ return plugin_data(plugin, handlerton*);
+ /* fall through */
+ case DB_TYPE_UNKNOWN:
+ return NULL;
}
- return FALSE;
}
-/* Use other database handler if databasehandler is not compiled in */
-
-enum db_type ha_checktype(THD *thd, enum db_type database_type,
+/**
+ Use other database handler if databasehandler is not compiled in.
+*/
+handlerton *ha_checktype(THD *thd, enum legacy_db_type database_type,
bool no_substitute, bool report_error)
{
- if (ha_storage_engine_is_enabled(database_type))
- return database_type;
+ handlerton *hton= ha_resolve_by_legacy_type(thd, database_type);
+ if (ha_storage_engine_is_enabled(hton))
+ return hton;
if (no_substitute)
{
if (report_error)
{
- const char *engine_name= ha_get_storage_engine(database_type);
+ const char *engine_name= ha_resolve_storage_engine_name(hton);
my_error(ER_FEATURE_DISABLED,MYF(0),engine_name,engine_name);
}
- return DB_TYPE_UNKNOWN;
+ return NULL;
}
switch (database_type) {
#ifndef NO_HASH
case DB_TYPE_HASH:
- return (database_type);
+ return ha_resolve_by_legacy_type(thd, DB_TYPE_HASH);
#endif
case DB_TYPE_MRG_ISAM:
- return (DB_TYPE_MRG_MYISAM);
+ return ha_resolve_by_legacy_type(thd, DB_TYPE_MRG_MYISAM);
default:
break;
}
- return ((enum db_type) thd->variables.table_type != DB_TYPE_UNKNOWN ?
- (enum db_type) thd->variables.table_type :
- ((enum db_type) global_system_variables.table_type !=
- DB_TYPE_UNKNOWN ?
- (enum db_type) global_system_variables.table_type : DB_TYPE_MYISAM)
- );
+ return ha_default_handlerton(thd);
} /* ha_checktype */
-handler *get_new_handler(TABLE *table, MEM_ROOT *alloc, enum db_type db_type)
+handler *get_new_handler(TABLE_SHARE *share, MEM_ROOT *alloc,
+ handlerton *db_type)
{
- switch (db_type) {
-#ifndef NO_HASH
- case DB_TYPE_HASH:
- return new (alloc) ha_hash(table);
-#endif
- case DB_TYPE_MRG_MYISAM:
- case DB_TYPE_MRG_ISAM:
- if (have_merge_db == SHOW_OPTION_YES)
- return new (alloc) ha_myisammrg(table);
- return NULL;
-#ifdef HAVE_BERKELEY_DB
- case DB_TYPE_BERKELEY_DB:
- if (have_berkeley_db == SHOW_OPTION_YES)
- return new (alloc) ha_berkeley(table);
- return NULL;
-#endif
-#ifdef HAVE_INNOBASE_DB
- case DB_TYPE_INNODB:
- if (have_innodb == SHOW_OPTION_YES)
- return new (alloc) ha_innobase(table);
- return NULL;
-#endif
-#ifdef HAVE_EXAMPLE_DB
- case DB_TYPE_EXAMPLE_DB:
- if (have_example_db == SHOW_OPTION_YES)
- return new (alloc) ha_example(table);
- return NULL;
-#endif
-#if defined(HAVE_ARCHIVE_DB)
- case DB_TYPE_ARCHIVE_DB:
- if (have_archive_db == SHOW_OPTION_YES)
- return new (alloc) ha_archive(table);
- return NULL;
-#endif
-#ifdef HAVE_BLACKHOLE_DB
- case DB_TYPE_BLACKHOLE_DB:
- if (have_blackhole_db == SHOW_OPTION_YES)
- return new (alloc) ha_blackhole(table);
- return NULL;
-#endif
-#ifdef HAVE_FEDERATED_DB
- case DB_TYPE_FEDERATED_DB:
- if (have_federated_db == SHOW_OPTION_YES)
- return new (alloc) ha_federated(table);
- return NULL;
-#endif
-#ifdef HAVE_CSV_DB
- case DB_TYPE_CSV_DB:
- if (have_csv_db == SHOW_OPTION_YES)
- return new (alloc) ha_tina(table);
- return NULL;
-#endif
-#ifdef HAVE_NDBCLUSTER_DB
- case DB_TYPE_NDBCLUSTER:
- if (have_ndbcluster == SHOW_OPTION_YES)
- return new (alloc) ha_ndbcluster(table);
- return NULL;
-#endif
- case DB_TYPE_HEAP:
- return new (alloc) ha_heap(table);
- default: // should never happen
+ handler *file;
+ DBUG_ENTER("get_new_handler");
+ DBUG_PRINT("enter", ("alloc: 0x%lx", (long) alloc));
+
+ if (db_type && db_type->state == SHOW_OPTION_YES && db_type->create)
{
- enum db_type def=(enum db_type) current_thd->variables.table_type;
- /* Try first with 'default table type' */
- if (db_type != def)
- return get_new_handler(table, alloc, def);
+ if ((file= db_type->create(db_type, share, alloc)))
+ file->init();
+ DBUG_RETURN(file);
}
- /* Fall back to MyISAM */
- case DB_TYPE_MYISAM:
- return new (alloc) ha_myisam(table);
+ /*
+ Try the default table type
+ Here the call to current_thd() is ok as we call this function a lot of
+ times but we enter this branch very seldom.
+ */
+ DBUG_RETURN(get_new_handler(share, alloc, ha_default_handlerton(current_thd)));
+}
+
+
+#ifdef WITH_PARTITION_STORAGE_ENGINE
+handler *get_ha_partition(partition_info *part_info)
+{
+ ha_partition *partition;
+ DBUG_ENTER("get_ha_partition");
+ if ((partition= new ha_partition(partition_hton, part_info)))
+ {
+ if (partition->initialize_partition(current_thd->mem_root))
+ {
+ delete partition;
+ partition= 0;
+ }
+ else
+ partition->init();
}
+ else
+ {
+ my_error(ER_OUTOFMEMORY, MYF(0), sizeof(ha_partition));
+ }
+ DBUG_RETURN(((handler*) partition));
}
+#endif
-/*
- Register handler error messages for use with my_error().
- SYNOPSIS
- ha_init_errors()
+/**
+ Register handler error messages for use with my_error().
- RETURN
+ @retval
0 OK
- != 0 Error
+ @retval
+ !=0 Error
*/
-static int ha_init_errors(void)
+int ha_init_errors(void)
{
#define SETMSG(nr, msg) errmsgs[(nr) - HA_ERR_FIRST]= (msg)
const char **errmsgs;
@@ -422,6 +337,7 @@ static int ha_init_errors(void)
SETMSG(HA_ERR_TABLE_EXIST, ER(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_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));
@@ -432,17 +348,14 @@ static int ha_init_errors(void)
}
-/*
+/**
Unregister handler error messages.
- SYNOPSIS
- ha_finish_errors()
-
- RETURN
+ @retval
0 OK
- != 0 Error
+ @retval
+ !=0 Error
*/
-
static int ha_finish_errors(void)
{
const char **errmsgs;
@@ -450,40 +363,181 @@ 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((gptr) errmsgs, MYF(0));
+ my_free((uchar*) errmsgs, MYF(0));
return 0;
}
-static inline void ha_was_inited_ok(handlerton **ht)
+int ha_finalize_handlerton(st_plugin_int *plugin)
{
- uint tmp= (*ht)->savepoint_offset;
- (*ht)->savepoint_offset= savepoint_alloc_size;
- savepoint_alloc_size+= tmp;
- (*ht)->slot= total_ha++;
- if ((*ht)->prepare)
- total_ha_2pc++;
-}
+ handlerton *hton= (handlerton *)plugin->data;
+ DBUG_ENTER("ha_finalize_handlerton");
-int ha_init()
-{
- int error= 0;
- handlerton **types;
- total_ha= savepoint_alloc_size= 0;
+ /* hton can be NULL here, if ha_initialize_handlerton() failed. */
+ if (!hton)
+ goto end;
- if (ha_init_errors())
- return 1;
+ switch (hton->state)
+ {
+ case SHOW_OPTION_NO:
+ case SHOW_OPTION_DISABLED:
+ break;
+ case SHOW_OPTION_YES:
+ if (installed_htons[hton->db_type] == hton)
+ installed_htons[hton->db_type]= NULL;
+ break;
+ };
+
+ if (hton->panic)
+ hton->panic(hton, HA_PANIC_CLOSE);
+
+ if (plugin->plugin->deinit)
+ {
+ /*
+ Today we have no defined/special behavior for uninstalling
+ engine plugins.
+ */
+ DBUG_PRINT("info", ("Deinitializing plugin: '%s'", plugin->name.str));
+ if (plugin->plugin->deinit(NULL))
+ {
+ DBUG_PRINT("warning", ("Plugin '%s' deinit function returned error.",
+ plugin->name.str));
+ }
+ }
/*
- We now initialize everything here.
+ In case a plugin is uninstalled and re-installed later, it should
+ reuse an array slot. Otherwise the number of uninstall/install
+ cycles would be limited.
*/
- for (types= sys_table_types; *types; types++)
+ hton2plugin[hton->slot]= NULL;
+
+ my_free((uchar*)hton, MYF(0));
+
+ end:
+ DBUG_RETURN(0);
+}
+
+
+int ha_initialize_handlerton(st_plugin_int *plugin)
+{
+ handlerton *hton;
+ DBUG_ENTER("ha_initialize_handlerton");
+ DBUG_PRINT("plugin", ("initialize plugin: '%s'", plugin->name.str));
+
+ hton= (handlerton *)my_malloc(sizeof(handlerton),
+ MYF(MY_WME | MY_ZEROFILL));
+ /* Historical Requirement */
+ plugin->data= hton; // shortcut for the future
+ if (plugin->plugin->init)
{
- if (!(*types)->init || !(*types)->init())
- ha_was_inited_ok(types);
- else
- (*types)->state= SHOW_OPTION_DISABLED;
+ if (plugin->plugin->init(hton))
+ {
+ sql_print_error("Plugin '%s' init function returned error.",
+ plugin->name.str);
+ goto err;
+ }
+ }
+
+ /*
+ the switch below and hton->state should be removed when
+ command-line options for plugins will be implemented
+ */
+ switch (hton->state) {
+ case SHOW_OPTION_NO:
+ break;
+ case SHOW_OPTION_YES:
+ {
+ uint tmp;
+ ulong fslot;
+ /* now check the db_type for conflict */
+ if (hton->db_type <= DB_TYPE_UNKNOWN ||
+ hton->db_type >= DB_TYPE_DEFAULT ||
+ installed_htons[hton->db_type])
+ {
+ int idx= (int) DB_TYPE_FIRST_DYNAMIC;
+
+ while (idx < (int) DB_TYPE_DEFAULT && installed_htons[idx])
+ idx++;
+
+ if (idx == (int) DB_TYPE_DEFAULT)
+ {
+ sql_print_warning("Too many storage engines!");
+ DBUG_RETURN(1);
+ }
+ if (hton->db_type != DB_TYPE_UNKNOWN)
+ sql_print_warning("Storage engine '%s' has conflicting typecode. "
+ "Assigning value %d.", plugin->plugin->name, idx);
+ hton->db_type= (enum legacy_db_type) idx;
+ }
+ 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
+ reuse an array slot. Otherwise the number of uninstall/install
+ cycles would be limited. So look for a free slot.
+ */
+ DBUG_PRINT("plugin", ("total_ha: %lu", total_ha));
+ for (fslot= 0; fslot < total_ha; fslot++)
+ {
+ if (!hton2plugin[fslot])
+ break;
+ }
+ if (fslot < total_ha)
+ hton->slot= fslot;
+ else
+ {
+ if (total_ha >= MAX_HA)
+ {
+ sql_print_error("Too many plugins loaded. Limit is %lu. "
+ "Failed on '%s'", (ulong) MAX_HA, plugin->name.str);
+ goto err;
+ }
+ hton->slot= total_ha++;
+ }
+
+ hton2plugin[hton->slot]=plugin;
+ if (hton->prepare)
+ total_ha_2pc++;
+ break;
+ }
+ /* fall through */
+ default:
+ hton->state= SHOW_OPTION_DISABLED;
+ break;
}
+
+ /*
+ This is entirely for legacy. We will create a new "disk based" hton and a
+ "memory" hton which will be configurable longterm. We should be able to
+ remove partition and myisammrg.
+ */
+ switch (hton->db_type) {
+ case DB_TYPE_HEAP:
+ heap_hton= hton;
+ break;
+ case DB_TYPE_MYISAM:
+ myisam_hton= hton;
+ break;
+ case DB_TYPE_PARTITION_DB:
+ partition_hton= hton;
+ break;
+ default:
+ break;
+ };
+
+ DBUG_RETURN(0);
+err:
+ DBUG_RETURN(1);
+}
+
+int ha_init()
+{
+ int error= 0;
+ DBUG_ENTER("ha_init");
DBUG_ASSERT(total_ha < MAX_HA);
/*
@@ -493,93 +547,368 @@ int ha_init()
*/
opt_using_transactions= total_ha>(ulong)opt_bin_log;
savepoint_alloc_size+= sizeof(SAVEPOINT);
- return error;
+ DBUG_RETURN(error);
}
- /* close, flush or restart databases */
- /* Ignore this for other databases than ours */
-
-int ha_panic(enum ha_panic_function flag)
+int ha_end()
{
- int error=0;
-#ifndef NO_HASH
- error|=h_panic(flag); /* fix hash */
-#endif
-#ifdef HAVE_ISAM
- error|=mrg_panic(flag);
- error|=nisam_panic(flag);
-#endif
- error|=heap_panic(flag);
- error|=mi_panic(flag);
- error|=myrg_panic(flag);
-#ifdef HAVE_BERKELEY_DB
- if (have_berkeley_db == SHOW_OPTION_YES)
- error|=berkeley_end();
-#endif
-#ifdef HAVE_BLACKHOLE_DB
- if (have_blackhole_db == SHOW_OPTION_YES)
- error|= blackhole_db_end();
-#endif
-#ifdef HAVE_INNOBASE_DB
- if (have_innodb == SHOW_OPTION_YES)
- error|=innobase_end();
-#endif
-#ifdef HAVE_NDBCLUSTER_DB
- if (have_ndbcluster == SHOW_OPTION_YES)
- error|=ndbcluster_end();
-#endif
-#ifdef HAVE_FEDERATED_DB
- if (have_federated_db == SHOW_OPTION_YES)
- error|= federated_db_end();
-#endif
-#if defined(HAVE_ARCHIVE_DB)
- if (have_archive_db == SHOW_OPTION_YES)
- error|= archive_db_end();
-#endif
-#ifdef HAVE_CSV_DB
- if (have_csv_db == SHOW_OPTION_YES)
- error|= tina_end();
-#endif
+ int error= 0;
+ DBUG_ENTER("ha_end");
+
+
+ /*
+ This should be eventualy based on the graceful shutdown flag.
+ So if flag is equal to HA_PANIC_CLOSE, the deallocate
+ the errors.
+ */
if (ha_finish_errors())
error= 1;
- return error;
-} /* ha_panic */
+
+ DBUG_RETURN(error);
+}
+
+static my_bool dropdb_handlerton(THD *unused1, plugin_ref plugin,
+ void *path)
+{
+ handlerton *hton= plugin_data(plugin, handlerton *);
+ if (hton->state == SHOW_OPTION_YES && hton->drop_database)
+ hton->drop_database(hton, (char *)path);
+ return FALSE;
+}
+
void ha_drop_database(char* path)
{
-#ifdef HAVE_INNOBASE_DB
- if (have_innodb == SHOW_OPTION_YES)
- innobase_drop_database(path);
-#endif
-#ifdef HAVE_NDBCLUSTER_DB
- if (have_ndbcluster == SHOW_OPTION_YES)
- ndbcluster_drop_database(path);
-#endif
+ plugin_foreach(NULL, dropdb_handlerton, MYSQL_STORAGE_ENGINE_PLUGIN, path);
}
-/* don't bother to rollback here, it's done already */
+
+static my_bool closecon_handlerton(THD *thd, plugin_ref plugin,
+ void *unused)
+{
+ handlerton *hton= plugin_data(plugin, handlerton *);
+ /*
+ there's no need to rollback here as all transactions must
+ be rolled back already
+ */
+ if (hton->state == SHOW_OPTION_YES && hton->close_connection &&
+ thd_get_ha_data(thd, hton))
+ hton->close_connection(hton, thd);
+ return FALSE;
+}
+
+
+/**
+ @note
+ don't bother to rollback here, it's done already
+*/
void ha_close_connection(THD* thd)
{
- handlerton **types;
- for (types= sys_table_types; *types; types++)
- if (thd->ha_data[(*types)->slot])
- (*types)->close_connection(thd);
+ plugin_foreach(thd, closecon_handlerton, MYSQL_STORAGE_ENGINE_PLUGIN, 0);
}
/* ========================================================================
======================= TRANSACTIONS ===================================*/
-/*
- Register a storage engine for a transaction
+/**
+ Transaction handling in the server
+ ==================================
+
+ In each client connection, MySQL maintains two transactional
+ states:
+ - a statement transaction,
+ - a standard, also called normal transaction.
+
+ Historical note
+ ---------------
+ "Statement transaction" is a non-standard term that comes
+ from the times when MySQL supported BerkeleyDB storage engine.
+
+ First of all, it should be said that in BerkeleyDB auto-commit
+ mode auto-commits operations that are atomic to the storage
+ engine itself, such as a write of a record, and are too
+ high-granular to be atomic from the application perspective
+ (MySQL). One SQL statement could involve many BerkeleyDB
+ auto-committed operations and thus BerkeleyDB auto-commit was of
+ little use to MySQL.
+
+ Secondly, instead of SQL standard savepoints, BerkeleyDB
+ provided the concept of "nested transactions". In a nutshell,
+ transactions could be arbitrarily nested, but when the parent
+ transaction was committed or aborted, all its child (nested)
+ transactions were handled committed or aborted as well.
+ Commit of a nested transaction, in turn, made its changes
+ visible, but not durable: it destroyed the nested transaction,
+ all its changes would become available to the parent and
+ currently active nested transactions of this parent.
+
+ So the mechanism of nested transactions was employed to
+ provide "all or nothing" guarantee of SQL statements
+ required by the standard.
+ A nested transaction would be created at start of each SQL
+ statement, and destroyed (committed or aborted) at statement
+ end. Such nested transaction was internally referred to as
+ a "statement transaction" and gave birth to the term.
+
+ <Historical note ends>
+
+ Since then a statement transaction is started for each statement
+ that accesses transactional tables or uses the binary log. If
+ the statement succeeds, the statement transaction is committed.
+ If the statement fails, the transaction is rolled back. Commits
+ of statement transactions are not durable -- each such
+ transaction is nested in the normal transaction, and if the
+ normal transaction is rolled back, the effects of all enclosed
+ statement transactions are undone as well. Technically,
+ a statement transaction can be viewed as a savepoint which is
+ maintained automatically in order to make effects of one
+ statement atomic.
+
+ The normal transaction is started by the user and is ended
+ usually upon a user request as well. The normal transaction
+ encloses transactions of all statements issued between
+ its beginning and its end.
+ In autocommit mode, the normal transaction is equivalent
+ to the statement transaction.
+
+ Since MySQL supports PSEA (pluggable storage engine
+ architecture), more than one transactional engine can be
+ active at a time. Hence transactions, from the server
+ point of view, are always distributed. In particular,
+ transactional state is maintained independently for each
+ engine. In order to commit a transaction the two phase
+ commit protocol is employed.
+
+ Not all statements are executed in context of a transaction.
+ Administrative and status information statements do not modify
+ engine data, and thus do not start a statement transaction and
+ also have no effect on the normal transaction. Examples of such
+ statements are SHOW STATUS and RESET SLAVE.
+
+ Similarly DDL statements are not transactional,
+ and therefore a transaction is [almost] never started for a DDL
+ statement. The difference between a DDL statement and a purely
+ administrative statement though is that a DDL statement always
+ commits the current transaction before proceeding, if there is
+ any.
+
+ At last, SQL statements that work with non-transactional
+ engines also have no effect on the transaction state of the
+ connection. Even though they are written to the binary log,
+ and the binary log is, overall, transactional, the writes
+ are done in "write-through" mode, directly to the binlog
+ file, followed with a OS cache sync, in other words,
+ bypassing the binlog undo log (translog).
+ They do not commit the current normal transaction.
+ A failure of a statement that uses non-transactional tables
+ would cause a rollback of the statement transaction, but
+ in case there no non-transactional tables are used,
+ no statement transaction is started.
+
+ Data layout
+ -----------
+
+ The server stores its transaction-related data in
+ thd->transaction. This structure has two members of type
+ THD_TRANS. These members correspond to the statement and
+ normal transactions respectively:
+
+ - thd->transaction.stmt contains a list of engines
+ that are participating in the given statement
+ - thd->transaction.all contains a list of engines that
+ have participated in any of the statement transactions started
+ within the context of the normal transaction.
+ Each element of the list contains a pointer to the storage
+ engine, engine-specific transactional data, and engine-specific
+ transaction flags.
+
+ In autocommit mode thd->transaction.all is empty.
+ Instead, data of thd->transaction.stmt is
+ used to commit/rollback the normal transaction.
+
+ The list of registered engines has a few important properties:
+ - no engine is registered in the list twice
+ - engines are present in the list a reverse temporal order --
+ new participants are always added to the beginning of the list.
+
+ Transaction life cycle
+ ----------------------
+
+ When a new connection is established, thd->transaction
+ members are initialized to an empty state.
+ If a statement uses any tables, all affected engines
+ are registered in the statement engine list. In
+ non-autocommit mode, the same engines are registered in
+ the normal transaction list.
+ At the end of the statement, the server issues a commit
+ or a roll back for all engines in the statement list.
+ At this point transaction flags of an engine, if any, are
+ propagated from the statement list to the list of the normal
+ transaction.
+ When commit/rollback is finished, the statement list is
+ cleared. It will be filled in again by the next statement,
+ and emptied again at the next statement's end.
+
+ The normal transaction is committed in a similar way
+ (by going over all engines in thd->transaction.all list)
+ but at different times:
+ - upon COMMIT SQL statement is issued by the user
+ - implicitly, by the server, at the beginning of a DDL statement
+ or SET AUTOCOMMIT={0|1} statement.
+
+ The normal transaction can be rolled back as well:
+ - if the user has requested so, by issuing ROLLBACK SQL
+ statement
+ - if one of the storage engines requested a rollback
+ by setting thd->transaction_rollback_request. This may
+ happen in case, e.g., when the transaction in the engine was
+ chosen a victim of the internal deadlock resolution algorithm
+ and rolled back internally. When such a situation happens, there
+ is little the server can do and the only option is to rollback
+ transactions in all other participating engines. In this case
+ the rollback is accompanied by an error sent to the user.
+
+ As follows from the use cases above, the normal transaction
+ is never committed when there is an outstanding statement
+ transaction. In most cases there is no conflict, since
+ commits of the normal transaction are issued by a stand-alone
+ administrative or DDL statement, thus no outstanding statement
+ transaction of the previous statement exists. Besides,
+ all statements that manipulate with the normal transaction
+ are prohibited in stored functions and triggers, therefore
+ no conflicting situation can occur in a sub-statement either.
+ The remaining rare cases when the server explicitly has
+ to commit the statement transaction prior to committing the normal
+ one cover error-handling scenarios (see for example
+ SQLCOM_LOCK_TABLES).
+
+ When committing a statement or a normal transaction, the server
+ either uses the two-phase commit protocol, or issues a commit
+ in each engine independently. The two-phase commit protocol
+ is used only if:
+ - all participating engines support two-phase commit (provide
+ handlerton::prepare PSEA API call) and
+ - transactions in at least two engines modify data (i.e. are
+ not read-only).
+
+ Note that the two phase commit is used for
+ statement transactions, even though they are not durable anyway.
+ This is done to ensure logical consistency of data in a multiple-
+ engine transaction.
+ For example, imagine that some day MySQL supports unique
+ constraint checks deferred till the end of statement. In such
+ case a commit in one of the engines may yield ER_DUP_KEY,
+ and MySQL should be able to gracefully abort statement
+ transactions of other participants.
+
+ After the normal transaction has been committed,
+ thd->transaction.all list is cleared.
+
+ When a connection is closed, the current normal transaction, if
+ any, is rolled back.
+
+ Roles and responsibilities
+ --------------------------
+
+ The server has no way to know that an engine participates in
+ the statement and a transaction has been started
+ in it unless the engine says so. Thus, in order to be
+ a part of a transaction, the engine must "register" itself.
+ This is done by invoking trans_register_ha() server call.
+ Normally the engine registers itself whenever handler::external_lock()
+ is called. trans_register_ha() can be invoked many times: if
+ an engine is already registered, the call does nothing.
+ In case autocommit is not set, the engine must register itself
+ twice -- both in the statement list and in the normal transaction
+ list.
+ In which list to register is a parameter of trans_register_ha().
+
+ Note, that although the registration interface in itself is
+ fairly clear, the current usage practice often leads to undesired
+ effects. E.g. since a call to trans_register_ha() in most engines
+ is embedded into implementation of handler::external_lock(), some
+ DDL statements start a transaction (at least from the server
+ point of view) even though they are not expected to. E.g.
+ CREATE TABLE does not start a transaction, since
+ handler::external_lock() is never called during CREATE TABLE. But
+ CREATE TABLE ... SELECT does, since handler::external_lock() is
+ called for the table that is being selected from. This has no
+ practical effects currently, but must be kept in mind
+ nevertheless.
+
+ Once an engine is registered, the server will do the rest
+ of the work.
+
+ During statement execution, whenever any of data-modifying
+ PSEA API methods is used, e.g. handler::write_row() or
+ handler::update_row(), the read-write flag is raised in the
+ statement transaction for the involved engine.
+ Currently All PSEA calls are "traced", and the data can not be
+ changed in a way other than issuing a PSEA call. Important:
+ unless this invariant is preserved the server will not know that
+ a transaction in a given engine is read-write and will not
+ involve the two-phase commit protocol!
+
+ At the end of a statement, server call
+ ha_autocommit_or_rollback() is invoked. This call in turn
+ invokes handlerton::prepare() for every involved engine.
+ Prepare is followed by a call to handlerton::commit_one_phase()
+ If a one-phase commit will suffice, handlerton::prepare() is not
+ invoked and the server only calls handlerton::commit_one_phase().
+ At statement commit, the statement-related read-write engine
+ flag is propagated to the corresponding flag in the normal
+ transaction. When the commit is complete, the list of registered
+ engines is cleared.
+
+ Rollback is handled in a similar fashion.
+
+ Additional notes on DDL and the normal transaction.
+ ---------------------------------------------------
+
+ DDLs and operations with non-transactional engines
+ do not "register" in thd->transaction lists, and thus do not
+ modify the transaction state. Besides, each DDL in
+ MySQL is prefixed with an implicit normal transaction commit
+ (a call to end_active_trans()), and thus leaves nothing
+ to modify.
+ However, as it has been pointed out with CREATE TABLE .. SELECT,
+ some DDL statements can start a *new* transaction.
+
+ Behaviour of the server in this case is currently badly
+ defined.
+ DDL statements use a form of "semantic" logging
+ to maintain atomicity: if CREATE TABLE .. SELECT failed,
+ the newly created table is deleted.
+ In addition, some DDL statements issue interim transaction
+ commits: e.g. ALTER TABLE issues a commit after data is copied
+ from the original table to the internal temporary table. Other
+ statements, e.g. CREATE TABLE ... SELECT do not always commit
+ after itself.
+ And finally there is a group of DDL statements such as
+ RENAME/DROP TABLE that doesn't start a new transaction
+ and doesn't commit.
+
+ This diversity makes it hard to say what will happen if
+ by chance a stored function is invoked during a DDL --
+ whether any modifications it makes will be committed or not
+ is not clear. Fortunately, SQL grammar of few DDLs allows
+ invocation of a stored function.
+
+ A consistent behaviour is perhaps to always commit the normal
+ transaction after all DDLs, just like the statement transaction
+ is always committed at the end of all statements.
+*/
- DESCRIPTION
- Every storage engine MUST call this function when it starts
- a transaction or a statement (that is it must be called both for the
- "beginning of transaction" and "beginning of statement").
- Only storage engines registered for the transaction/statement
- will know when to commit/rollback it.
+/**
+ Register a storage engine for a transaction.
- NOTE
+ Every storage engine MUST call this function when it starts
+ a transaction or a statement (that is it must be called both for the
+ "beginning of transaction" and "beginning of statement").
+ Only storage engines registered for the transaction/statement
+ will know when to commit/rollback it.
+
+ @note
trans_register_ha is idempotent - storage engine may register many
times per transaction.
@@ -587,7 +916,7 @@ void ha_close_connection(THD* thd)
void trans_register_ha(THD *thd, bool all, handlerton *ht_arg)
{
THD_TRANS *trans;
- handlerton **ht;
+ Ha_trx_info *ha_info;
DBUG_ENTER("trans_register_ha");
DBUG_PRINT("enter",("%s", all ? "all" : "stmt"));
@@ -599,39 +928,42 @@ void trans_register_ha(THD *thd, bool all, handlerton *ht_arg)
else
trans= &thd->transaction.stmt;
- for (ht=trans->ht; *ht; ht++)
- if (*ht == ht_arg)
- DBUG_VOID_RETURN; /* already registered, return */
+ ha_info= thd->ha_data[ht_arg->slot].ha_info + static_cast<unsigned>(all);
+
+ if (ha_info->is_started())
+ DBUG_VOID_RETURN; /* already registered, return */
+
+ ha_info->register_ha(trans, ht_arg);
- trans->ht[trans->nht++]=ht_arg;
- DBUG_ASSERT(*ht == ht_arg);
trans->no_2pc|=(ht_arg->prepare==0);
if (thd->transaction.xid_state.xid.is_null())
thd->transaction.xid_state.xid.set(thd->query_id);
DBUG_VOID_RETURN;
}
-/*
- RETURN
- 0 - ok
- 1 - error, transaction was rolled back
+/**
+ @retval
+ 0 ok
+ @retval
+ 1 error, transaction was rolled back
*/
int ha_prepare(THD *thd)
{
int error=0, all=1;
THD_TRANS *trans=all ? &thd->transaction.all : &thd->transaction.stmt;
- handlerton **ht=trans->ht;
+ Ha_trx_info *ha_info= trans->ha_list;
DBUG_ENTER("ha_prepare");
#ifdef USING_TRANSACTIONS
- if (trans->nht)
+ if (ha_info)
{
- for (; *ht; ht++)
+ for (; ha_info; ha_info= ha_info->next())
{
int err;
- statistic_increment(thd->status_var.ha_prepare_count,&LOCK_status);
- if ((*ht)->prepare)
+ handlerton *ht= ha_info->ht();
+ status_var_increment(thd->status_var.ha_prepare_count);
+ if (ht->prepare)
{
- if ((err= (*(*ht)->prepare)(thd, all)))
+ if ((err= ht->prepare(ht, thd, all)))
{
my_error(ER_ERROR_DURING_COMMIT, MYF(0), err);
ha_rollback_trans(thd, all);
@@ -642,7 +974,8 @@ int ha_prepare(THD *thd)
else
{
push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
- ER_ILLEGAL_HA, ER(ER_ILLEGAL_HA), (*ht)->name);
+ ER_ILLEGAL_HA, ER(ER_ILLEGAL_HA),
+ ha_resolve_storage_engine_name(ht));
}
}
}
@@ -650,21 +983,103 @@ int ha_prepare(THD *thd)
DBUG_RETURN(error);
}
-/*
- RETURN
- 0 - ok
- 1 - transaction was rolled back
- 2 - error during commit, data may be inconsistent
+/**
+ Check if we can skip the two-phase commit.
+
+ A helper function to evaluate if two-phase commit is mandatory.
+ As a side effect, propagates the read-only/read-write flags
+ of the statement transaction to its enclosing normal transaction.
+
+ If we have at least two engines with read-write changes we must
+ run a two-phase commit. Otherwise we can run several independent
+ commits as the only transactional engine has read-write changes
+ and others are read-only.
+
+ @retval 0 All engines are read-only.
+ @retval 1 We have the only engine with read-write changes.
+ @retval >1 More than one engine have read-write changes.
+ Note: return value might NOT be the exact number of
+ engines with read-write changes.
+*/
+
+static
+uint
+ha_check_and_coalesce_trx_read_only(THD *thd, Ha_trx_info *ha_list,
+ bool all)
+{
+ /* The number of storage engines that have actual changes. */
+ unsigned rw_ha_count= 0;
+ Ha_trx_info *ha_info;
+
+ for (ha_info= ha_list; ha_info; ha_info= ha_info->next())
+ {
+ if (ha_info->is_trx_read_write())
+ ++rw_ha_count;
+
+ if (! all)
+ {
+ Ha_trx_info *ha_info_all= &thd->ha_data[ha_info->ht()->slot].ha_info[1];
+ DBUG_ASSERT(ha_info != ha_info_all);
+ /*
+ Merge read-only/read-write information about statement
+ transaction to its enclosing normal transaction. Do this
+ only if in a real transaction -- that is, if we know
+ that ha_info_all is registered in thd->transaction.all.
+ Since otherwise we only clutter the normal transaction flags.
+ */
+ if (ha_info_all->is_started()) /* FALSE if autocommit. */
+ ha_info_all->coalesce_trx_with(ha_info);
+ }
+ else if (rw_ha_count > 1)
+ {
+ /*
+ It is a normal transaction, so we don't need to merge read/write
+ information up, and the need for two-phase commit has been
+ already established. Break the loop prematurely.
+ */
+ break;
+ }
+ }
+ return rw_ha_count;
+}
+
+
+/**
+ @retval
+ 0 ok
+ @retval
+ 1 transaction was rolled back
+ @retval
+ 2 error during commit, data may be inconsistent
+
+ @todo
+ Since we don't support nested statement transactions in 5.0,
+ we can't commit or rollback stmt transactions while we are inside
+ stored functions or triggers. So we simply do nothing now.
+ TODO: This should be fixed in later ( >= 5.1) releases.
*/
int ha_commit_trans(THD *thd, bool all)
{
int error= 0, cookie= 0;
+ /*
+ 'all' means that this is either an explicit commit issued by
+ user, or an implicit commit issued by a DDL.
+ */
THD_TRANS *trans= all ? &thd->transaction.all : &thd->transaction.stmt;
- bool is_real_trans= all || thd->transaction.all.nht == 0;
- handlerton **ht= trans->ht;
+ bool is_real_trans= all || thd->transaction.all.ha_list == 0;
+ Ha_trx_info *ha_info= trans->ha_list;
my_xid xid= thd->transaction.xid_state.xid.get_my_xid();
DBUG_ENTER("ha_commit_trans");
+ /*
+ We must not commit the normal transaction if a statement
+ transaction is pending. Otherwise statement transaction
+ flags will not get propagated to its normal transaction's
+ counterpart.
+ */
+ DBUG_ASSERT(thd->transaction.stmt.ha_list == NULL ||
+ trans == &thd->transaction.stmt);
+
if (thd->in_sub_stmt)
{
/*
@@ -686,30 +1101,62 @@ int ha_commit_trans(THD *thd, bool all)
DBUG_RETURN(2);
}
#ifdef USING_TRANSACTIONS
- if (trans->nht)
+ if (ha_info)
{
- if (is_real_trans && wait_if_global_read_lock(thd, 0, 0))
- {
- ha_rollback_trans(thd, all);
- DBUG_RETURN(1);
- }
+ uint rw_ha_count;
+ bool rw_trans;
+
DBUG_EXECUTE_IF("crash_commit_before", abort(););
/* Close all cursors that can not survive COMMIT */
if (is_real_trans) /* not a statement commit */
thd->stmt_map.close_transient_cursors();
- if (!trans->no_2pc && trans->nht > 1)
+ 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 */
+ rw_trans= is_real_trans && (rw_ha_count > 0);
+
+ if (rw_trans &&
+ wait_if_global_read_lock(thd, 0, 0))
+ {
+ ha_rollback_trans(thd, all);
+ DBUG_RETURN(1);
+ }
+
+ if (rw_trans &&
+ opt_readonly &&
+ !(thd->security_ctx->master_access & SUPER_ACL) &&
+ !thd->slave_thread)
+ {
+ my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--read-only");
+ ha_rollback_trans(thd, all);
+ error= 1;
+ goto end;
+ }
+
+ if (!trans->no_2pc && (rw_ha_count > 1))
{
- for (; *ht && !error; ht++)
+ for (; ha_info && !error; ha_info= ha_info->next())
{
int err;
- if ((err= (*(*ht)->prepare)(thd, all)))
+ handlerton *ht= ha_info->ht();
+ /*
+ Do not call two-phase commit if this particular
+ transaction is read-only. This allows for simpler
+ implementation in engines that are always read-only.
+ */
+ if (! ha_info->is_trx_read_write())
+ continue;
+ /*
+ Sic: we know that prepare() is not NULL since otherwise
+ trans->no_2pc would have been set.
+ */
+ if ((err= ht->prepare(ht, thd, all)))
{
my_error(ER_ERROR_DURING_COMMIT, MYF(0), err);
error= 1;
}
- statistic_increment(thd->status_var.ha_prepare_count,&LOCK_status);
+ status_var_increment(thd->status_var.ha_prepare_count);
}
DBUG_EXECUTE_IF("crash_commit_after_prepare", abort(););
if (error || (is_real_trans && xid &&
@@ -727,39 +1174,41 @@ int ha_commit_trans(THD *thd, bool all)
tc_log->unlog(cookie, xid);
DBUG_EXECUTE_IF("crash_commit_after", abort(););
end:
- if (is_real_trans)
+ if (rw_trans)
start_waiting_global_read_lock(thd);
}
#endif /* USING_TRANSACTIONS */
DBUG_RETURN(error);
}
-/*
- NOTE - this function does not care about global read lock.
- A caller should.
+/**
+ @note
+ This function does not care about global read lock. A caller should.
*/
int ha_commit_one_phase(THD *thd, bool all)
{
int error=0;
THD_TRANS *trans=all ? &thd->transaction.all : &thd->transaction.stmt;
- bool is_real_trans=all || thd->transaction.all.nht == 0;
- handlerton **ht=trans->ht;
+ bool is_real_trans=all || thd->transaction.all.ha_list == 0;
+ Ha_trx_info *ha_info= trans->ha_list, *ha_info_next;
DBUG_ENTER("ha_commit_one_phase");
#ifdef USING_TRANSACTIONS
- if (trans->nht)
+ if (ha_info)
{
- for (ht=trans->ht; *ht; ht++)
+ for (; ha_info; ha_info= ha_info_next)
{
int err;
- if ((err= (*(*ht)->commit)(thd, all)))
+ handlerton *ht= ha_info->ht();
+ if ((err= ht->commit(ht, thd, all)))
{
my_error(ER_ERROR_DURING_COMMIT, MYF(0), err);
error=1;
}
- statistic_increment(thd->status_var.ha_commit_count,&LOCK_status);
- *ht= 0;
+ status_var_increment(thd->status_var.ha_commit_count);
+ ha_info_next= ha_info->next();
+ ha_info->reset(); /* keep it conveniently zero-filled */
}
- trans->nht=0;
+ trans->ha_list= 0;
trans->no_2pc=0;
if (is_real_trans)
thd->transaction.xid_state.xid.null();
@@ -782,8 +1231,17 @@ int ha_rollback_trans(THD *thd, bool all)
{
int error=0;
THD_TRANS *trans=all ? &thd->transaction.all : &thd->transaction.stmt;
- bool is_real_trans=all || thd->transaction.all.nht == 0;
+ Ha_trx_info *ha_info= trans->ha_list, *ha_info_next;
+ bool is_real_trans=all || thd->transaction.all.ha_list == 0;
DBUG_ENTER("ha_rollback_trans");
+
+ /*
+ We must not rollback the normal transaction if a statement
+ transaction is pending.
+ */
+ DBUG_ASSERT(thd->transaction.stmt.ha_list == NULL ||
+ trans == &thd->transaction.stmt);
+
if (thd->in_sub_stmt)
{
/*
@@ -798,29 +1256,31 @@ int ha_rollback_trans(THD *thd, bool all)
DBUG_RETURN(1);
}
#ifdef USING_TRANSACTIONS
- if (trans->nht)
+ if (ha_info)
{
/* Close all cursors that can not survive ROLLBACK */
if (is_real_trans) /* not a statement commit */
thd->stmt_map.close_transient_cursors();
- for (handlerton **ht=trans->ht; *ht; ht++)
+ for (; ha_info; ha_info= ha_info_next)
{
int err;
- if ((err= (*(*ht)->rollback)(thd, all)))
+ handlerton *ht= ha_info->ht();
+ if ((err= ht->rollback(ht, thd, all)))
{ // cannot happen
my_error(ER_ERROR_DURING_ROLLBACK, MYF(0), err);
error=1;
}
- statistic_increment(thd->status_var.ha_rollback_count,&LOCK_status);
- *ht= 0;
+ status_var_increment(thd->status_var.ha_rollback_count);
+ ha_info_next= ha_info->next();
+ ha_info->reset(); /* keep it conveniently zero-filled */
}
- trans->nht=0;
+ trans->ha_list= 0;
trans->no_2pc=0;
if (is_real_trans)
{
if (thd->transaction_rollback_request)
- thd->transaction.xid_state.rm_error= thd->net.last_errno;
+ thd->transaction.xid_state.rm_error= thd->main_da.sql_errno();
else
thd->transaction.xid_state.xid.null();
}
@@ -844,37 +1304,41 @@ int ha_rollback_trans(THD *thd, bool all)
message in the error log, so we don't send it.
*/
if (is_real_trans && thd->transaction.all.modified_non_trans_table &&
- !thd->slave_thread)
+ !thd->slave_thread && thd->killed != THD::KILL_CONNECTION)
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 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.
+/**
+ 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.nht)
+ if (thd->transaction.stmt.ha_list)
{
if (!error)
{
- if (ha_commit_stmt(thd))
+ if (ha_commit_trans(thd, 0))
error=1;
}
- else if (thd->transaction_rollback_request && !thd->in_sub_stmt)
- (void) ha_rollback(thd);
- else
- (void) ha_rollback_stmt(thd);
+ else
+ {
+ (void) ha_rollback_trans(thd, 0);
+ if (thd->transaction_rollback_request && !thd->in_sub_stmt)
+ (void) ha_rollback(thd);
+ }
thd->variables.tx_isolation=thd->session_tx_isolation;
}
@@ -883,26 +1347,54 @@ int ha_autocommit_or_rollback(THD *thd, int error)
}
-int ha_commit_or_rollback_by_xid(XID *xid, bool commit)
+struct xahton_st {
+ XID *xid;
+ int result;
+};
+
+static my_bool xacommit_handlerton(THD *unused1, plugin_ref plugin,
+ void *arg)
{
- handlerton **types;
- int res= 1;
+ handlerton *hton= plugin_data(plugin, handlerton *);
+ if (hton->state == SHOW_OPTION_YES && hton->recover)
+ {
+ hton->commit_by_xid(hton, ((struct xahton_st *)arg)->xid);
+ ((struct xahton_st *)arg)->result= 0;
+ }
+ return FALSE;
+}
- for (types= sys_table_types; *types; types++)
+static my_bool xarollback_handlerton(THD *unused1, plugin_ref plugin,
+ void *arg)
+{
+ handlerton *hton= plugin_data(plugin, handlerton *);
+ if (hton->state == SHOW_OPTION_YES && hton->recover)
{
- if ((*types)->state == SHOW_OPTION_YES && (*types)->recover)
- {
- if ((*(commit ? (*types)->commit_by_xid :
- (*types)->rollback_by_xid))(xid))
- res= 0;
- }
+ hton->rollback_by_xid(hton, ((struct xahton_st *)arg)->xid);
+ ((struct xahton_st *)arg)->result= 0;
}
- return res;
+ return FALSE;
+}
+
+
+int ha_commit_or_rollback_by_xid(XID *xid, bool commit)
+{
+ struct xahton_st xaop;
+ xaop.xid= xid;
+ xaop.result= 1;
+
+ plugin_foreach(NULL, commit ? xacommit_handlerton : xarollback_handlerton,
+ MYSQL_STORAGE_ENGINE_PLUGIN, &xaop);
+
+ return xaop.result;
}
#ifndef DBUG_OFF
-/* this does not need to be multi-byte safe or anything */
+/**
+ @note
+ This does not need to be multi-byte safe or anything
+*/
static char* xid_to_str(char *buf, XID *xid)
{
int i;
@@ -954,118 +1446,138 @@ static char* xid_to_str(char *buf, XID *xid)
}
#endif
-/*
- recover() step of xa
-
- NOTE
- there are three modes of operation:
-
- - automatic recover after a crash
- in this case commit_list != 0, tc_heuristic_recover==0
- all xids from commit_list are committed, others are rolled back
-
- - manual (heuristic) recover
- in this case commit_list==0, tc_heuristic_recover != 0
- DBA has explicitly specified that all prepared transactions should
- be committed (or rolled back).
-
- - no recovery (MySQL did not detect a crash)
- in this case commit_list==0, tc_heuristic_recover == 0
- there should be no prepared transactions in this case.
+/**
+ recover() step of xa.
+
+ @note
+ there are three modes of operation:
+ - automatic recover after a crash
+ in this case commit_list != 0, tc_heuristic_recover==0
+ all xids from commit_list are committed, others are rolled back
+ - manual (heuristic) recover
+ in this case commit_list==0, tc_heuristic_recover != 0
+ DBA has explicitly specified that all prepared transactions should
+ be committed (or rolled back).
+ - no recovery (MySQL did not detect a crash)
+ in this case commit_list==0, tc_heuristic_recover == 0
+ there should be no prepared transactions in this case.
*/
-int ha_recover(HASH *commit_list)
+struct xarecover_st
{
- int len, got, found_foreign_xids=0, found_my_xids=0;
- handlerton **types;
- XID *list=0;
- bool dry_run=(commit_list==0 && tc_heuristic_recover==0);
- DBUG_ENTER("ha_recover");
-
- /* commit_list and tc_heuristic_recover cannot be set both */
- DBUG_ASSERT(commit_list==0 || tc_heuristic_recover==0);
- /* if either is set, total_ha_2pc must be set too */
- DBUG_ASSERT(dry_run || total_ha_2pc>(ulong)opt_bin_log);
-
- if (total_ha_2pc <= (ulong)opt_bin_log)
- DBUG_RETURN(0);
-
- if (commit_list)
- sql_print_information("Starting crash recovery...");
-
-#ifndef WILL_BE_DELETED_LATER
- /*
- for now, only InnoDB supports 2pc. It means we can always safely
- rollback all pending transactions, without risking inconsistent data
- */
- DBUG_ASSERT(total_ha_2pc == (ulong) opt_bin_log+1); // only InnoDB and binlog
- tc_heuristic_recover= TC_HEURISTIC_RECOVER_ROLLBACK; // forcing ROLLBACK
- dry_run=FALSE;
-#endif
+ int len, found_foreign_xids, found_my_xids;
+ XID *list;
+ HASH *commit_list;
+ bool dry_run;
+};
- for (len= MAX_XID_LIST_SIZE ; list==0 && len > MIN_XID_LIST_SIZE; len/=2)
- {
- list=(XID *)my_malloc(len*sizeof(XID), MYF(0));
- }
- if (!list)
- {
- sql_print_error(ER(ER_OUTOFMEMORY), len*sizeof(XID));
- DBUG_RETURN(1);
- }
+static my_bool xarecover_handlerton(THD *unused, plugin_ref plugin,
+ void *arg)
+{
+ handlerton *hton= plugin_data(plugin, handlerton *);
+ struct xarecover_st *info= (struct xarecover_st *) arg;
+ int got;
- for (types= sys_table_types; *types; types++)
+ if (hton->state == SHOW_OPTION_YES && hton->recover)
{
- if ((*types)->state != SHOW_OPTION_YES || !(*types)->recover)
- continue;
- while ((got=(*(*types)->recover)(list, len)) > 0 )
+ while ((got= hton->recover(hton, info->list, info->len)) > 0 )
{
sql_print_information("Found %d prepared transaction(s) in %s",
- got, (*types)->name);
+ got, ha_resolve_storage_engine_name(hton));
for (int i=0; i < got; i ++)
{
- my_xid x=list[i].get_my_xid();
+ my_xid x=info->list[i].get_my_xid();
if (!x) // not "mine" - that is generated by external TM
{
#ifndef DBUG_OFF
char buf[XIDDATASIZE*4+6]; // see xid_to_str
- sql_print_information("ignore xid %s", xid_to_str(buf, list+i));
+ sql_print_information("ignore xid %s", xid_to_str(buf, info->list+i));
#endif
- xid_cache_insert(list+i, XA_PREPARED);
- found_foreign_xids++;
+ xid_cache_insert(info->list+i, XA_PREPARED);
+ info->found_foreign_xids++;
continue;
}
- if (dry_run)
+ if (info->dry_run)
{
- found_my_xids++;
+ info->found_my_xids++;
continue;
}
// recovery mode
- if (commit_list ?
- hash_search(commit_list, (byte *)&x, sizeof(x)) != 0 :
+ if (info->commit_list ?
+ hash_search(info->commit_list, (uchar *)&x, sizeof(x)) != 0 :
tc_heuristic_recover == TC_HEURISTIC_RECOVER_COMMIT)
{
#ifndef DBUG_OFF
char buf[XIDDATASIZE*4+6]; // see xid_to_str
- sql_print_information("commit xid %s", xid_to_str(buf, list+i));
+ sql_print_information("commit xid %s", xid_to_str(buf, info->list+i));
#endif
- (*(*types)->commit_by_xid)(list+i);
+ hton->commit_by_xid(hton, info->list+i);
}
else
{
#ifndef DBUG_OFF
char buf[XIDDATASIZE*4+6]; // see xid_to_str
- sql_print_information("rollback xid %s", xid_to_str(buf, list+i));
+ sql_print_information("rollback xid %s",
+ xid_to_str(buf, info->list+i));
#endif
- (*(*types)->rollback_by_xid)(list+i);
+ hton->rollback_by_xid(hton, info->list+i);
}
}
- if (got < len)
+ if (got < info->len)
break;
}
}
- my_free((gptr)list, MYF(0));
- if (found_foreign_xids)
- sql_print_warning("Found %d prepared XA transactions", found_foreign_xids);
- if (dry_run && found_my_xids)
+ return FALSE;
+}
+
+int ha_recover(HASH *commit_list)
+{
+ struct xarecover_st info;
+ DBUG_ENTER("ha_recover");
+ info.found_foreign_xids= info.found_my_xids= 0;
+ info.commit_list= commit_list;
+ info.dry_run= (info.commit_list==0 && tc_heuristic_recover==0);
+ info.list= NULL;
+
+ /* commit_list and tc_heuristic_recover cannot be set both */
+ DBUG_ASSERT(info.commit_list==0 || tc_heuristic_recover==0);
+ /* if either is set, total_ha_2pc must be set too */
+ DBUG_ASSERT(info.dry_run || total_ha_2pc>(ulong)opt_bin_log);
+
+ if (total_ha_2pc <= (ulong)opt_bin_log)
+ DBUG_RETURN(0);
+
+ if (info.commit_list)
+ sql_print_information("Starting crash recovery...");
+
+#ifndef WILL_BE_DELETED_LATER
+ /*
+ for now, only InnoDB supports 2pc. It means we can always safely
+ rollback all pending transactions, without risking inconsistent data
+ */
+ DBUG_ASSERT(total_ha_2pc == (ulong) opt_bin_log+1); // only InnoDB and binlog
+ tc_heuristic_recover= TC_HEURISTIC_RECOVER_ROLLBACK; // forcing ROLLBACK
+ info.dry_run=FALSE;
+#endif
+
+ for (info.len= MAX_XID_LIST_SIZE ;
+ info.list==0 && info.len > MIN_XID_LIST_SIZE; info.len/=2)
+ {
+ info.list=(XID *)my_malloc(info.len*sizeof(XID), MYF(0));
+ }
+ if (!info.list)
+ {
+ sql_print_error(ER(ER_OUTOFMEMORY), info.len*sizeof(XID));
+ DBUG_RETURN(1);
+ }
+
+ plugin_foreach(NULL, xarecover_handlerton,
+ MYSQL_STORAGE_ENGINE_PLUGIN, &info);
+
+ my_free((uchar*)info.list, MYF(0));
+ if (info.found_foreign_xids)
+ sql_print_warning("Found %d prepared XA transactions",
+ info.found_foreign_xids);
+ if (info.dry_run && info.found_my_xids)
{
sql_print_error("Found %d prepared transactions! It means that mysqld was "
"not shut down properly last time and critical recovery "
@@ -1073,18 +1585,18 @@ int ha_recover(HASH *commit_list)
"after a crash. You have to start mysqld with "
"--tc-heuristic-recover switch to commit or rollback "
"pending transactions.",
- found_my_xids, opt_tc_log_file);
+ info.found_my_xids, opt_tc_log_file);
DBUG_RETURN(1);
}
- if (commit_list)
+ if (info.commit_list)
sql_print_information("Crash recovery finished.");
DBUG_RETURN(0);
}
-/*
- return the list of XID's to a client, the same way SHOW commands do
+/**
+ return the list of XID's to a client, the same way SHOW commands do.
- NOTE
+ @note
I didn't find in XA specs that an RM cannot return the same XID twice,
so mysql_xa_recover does not filter XID's to ensure uniqueness.
It can be easily fixed later, if necessary.
@@ -1126,11 +1638,12 @@ bool mysql_xa_recover(THD *thd)
}
pthread_mutex_unlock(&LOCK_xid_cache);
- send_eof(thd);
+ my_eof(thd);
DBUG_RETURN(0);
}
-/*
+/**
+ @details
This function should be called when MySQL sends rows of a SELECT result set
or the EOF mark to the client. It releases a possible adaptive hash index
S-latch held by thd in InnoDB and also releases a possible InnoDB query
@@ -1142,32 +1655,28 @@ bool mysql_xa_recover(THD *thd)
performs another SQL query. In MySQL-4.1 this is even more important because
there a connection can have several SELECT queries open at the same time.
- arguments:
- thd: the thread handle of the current connection
- return value: always 0
+ @param thd the thread handle of the current connection
+
+ @return
+ always 0
*/
int ha_release_temporary_latches(THD *thd)
{
-#ifdef HAVE_INNOBASE_DB
- if (opt_innodb)
- innobase_release_temporary_latches(thd);
-#endif
- return 0;
-}
-
-
-/*
- Export statistics for different engines. Currently we use it only for
- InnoDB.
-*/
+ Ha_trx_info *info;
-int ha_update_statistics()
-{
-#ifdef HAVE_INNOBASE_DB
- if (opt_innodb)
- innodb_export_status();
-#endif
+ /*
+ Note that below we assume that only transactional storage engines
+ may need release_temporary_latches(). If this will ever become false,
+ we could iterate on thd->open_tables instead (and remove duplicates
+ as if (!seen[hton->slot]) { seen[hton->slot]=1; ... }).
+ */
+ for (info= thd->transaction.stmt.ha_list; info; info= info->next())
+ {
+ handlerton *hton= info->ht();
+ if (hton && hton->release_temporary_latches)
+ hton->release_temporary_latches(hton, thd);
+ }
return 0;
}
@@ -1176,77 +1685,89 @@ int ha_rollback_to_savepoint(THD *thd, SAVEPOINT *sv)
int error=0;
THD_TRANS *trans= (thd->in_sub_stmt ? &thd->transaction.stmt :
&thd->transaction.all);
- handlerton **ht=trans->ht, **end_ht;
+ Ha_trx_info *ha_info, *ha_info_next;
+
DBUG_ENTER("ha_rollback_to_savepoint");
- trans->nht=sv->nht;
trans->no_2pc=0;
- end_ht=ht+sv->nht;
/*
rolling back to savepoint in all storage engines that were part of the
transaction when the savepoint was set
*/
- for (; ht < end_ht; ht++)
+ for (ha_info= sv->ha_list; ha_info; ha_info= ha_info->next())
{
int err;
- DBUG_ASSERT((*ht)->savepoint_set != 0);
- if ((err= (*(*ht)->savepoint_rollback)(thd, (byte *)(sv+1)+(*ht)->savepoint_offset)))
+ handlerton *ht= ha_info->ht();
+ DBUG_ASSERT(ht);
+ DBUG_ASSERT(ht->savepoint_set != 0);
+ if ((err= ht->savepoint_rollback(ht, thd,
+ (uchar *)(sv+1)+ht->savepoint_offset)))
{ // cannot happen
my_error(ER_ERROR_DURING_ROLLBACK, MYF(0), err);
error=1;
}
- statistic_increment(thd->status_var.ha_savepoint_rollback_count,&LOCK_status);
- trans->no_2pc|=(*ht)->prepare == 0;
+ status_var_increment(thd->status_var.ha_savepoint_rollback_count);
+ trans->no_2pc|= ht->prepare == 0;
}
/*
rolling back the transaction in all storage engines that were not part of
the transaction when the savepoint was set
*/
- for (; *ht ; ht++)
+ for (ha_info= trans->ha_list; ha_info != sv->ha_list;
+ ha_info= ha_info_next)
{
int err;
- if ((err= (*(*ht)->rollback)(thd, !thd->in_sub_stmt)))
+ handlerton *ht= ha_info->ht();
+ if ((err= ht->rollback(ht, thd, !thd->in_sub_stmt)))
{ // cannot happen
my_error(ER_ERROR_DURING_ROLLBACK, MYF(0), err);
error=1;
}
- statistic_increment(thd->status_var.ha_rollback_count,&LOCK_status);
- *ht=0; // keep it conveniently zero-filled
+ status_var_increment(thd->status_var.ha_rollback_count);
+ ha_info_next= ha_info->next();
+ ha_info->reset(); /* keep it conveniently zero-filled */
}
+ trans->ha_list= sv->ha_list;
DBUG_RETURN(error);
}
-/*
- note, that according to the sql standard (ISO/IEC 9075-2:2003)
+/**
+ @note
+ according to the sql standard (ISO/IEC 9075-2:2003)
section "4.33.4 SQL-statements and transaction states",
SAVEPOINT is *not* transaction-initiating SQL-statement
*/
-
int ha_savepoint(THD *thd, SAVEPOINT *sv)
{
int error=0;
THD_TRANS *trans= (thd->in_sub_stmt ? &thd->transaction.stmt :
&thd->transaction.all);
- handlerton **ht=trans->ht;
+ Ha_trx_info *ha_info= trans->ha_list;
DBUG_ENTER("ha_savepoint");
#ifdef USING_TRANSACTIONS
- for (; *ht; ht++)
+ for (; ha_info; ha_info= ha_info->next())
{
int err;
- if (! (*ht)->savepoint_set)
+ handlerton *ht= ha_info->ht();
+ DBUG_ASSERT(ht);
+ if (! ht->savepoint_set)
{
my_error(ER_CHECK_NOT_IMPLEMENTED, MYF(0), "SAVEPOINT");
error=1;
break;
}
- if ((err= (*(*ht)->savepoint_set)(thd, (byte *)(sv+1)+(*ht)->savepoint_offset)))
+ if ((err= ht->savepoint_set(ht, thd, (uchar *)(sv+1)+ht->savepoint_offset)))
{ // cannot happen
my_error(ER_GET_ERRNO, MYF(0), err);
error=1;
}
- statistic_increment(thd->status_var.ha_savepoint_count,&LOCK_status);
+ status_var_increment(thd->status_var.ha_savepoint_count);
}
- sv->nht=trans->nht;
+ /*
+ Remember the list of registered storage engines. All new
+ engines are prepended to the beginning of the list.
+ */
+ sv->ha_list= trans->ha_list;
#endif /* USING_TRANSACTIONS */
DBUG_RETURN(error);
}
@@ -1254,18 +1775,19 @@ int ha_savepoint(THD *thd, SAVEPOINT *sv)
int ha_release_savepoint(THD *thd, SAVEPOINT *sv)
{
int error=0;
- THD_TRANS *trans= (thd->in_sub_stmt ? &thd->transaction.stmt :
- &thd->transaction.all);
- handlerton **ht=trans->ht, **end_ht;
+ Ha_trx_info *ha_info= sv->ha_list;
DBUG_ENTER("ha_release_savepoint");
- end_ht=ht+sv->nht;
- for (; ht < end_ht; ht++)
+ for (; ha_info; ha_info= ha_info->next())
{
int err;
- if (!(*ht)->savepoint_release)
+ handlerton *ht= ha_info->ht();
+ /* Savepoint life time is enclosed into transaction life time. */
+ DBUG_ASSERT(ht);
+ if (!ht->savepoint_release)
continue;
- if ((err= (*(*ht)->savepoint_release)(thd, (byte *)(sv+1)+(*ht)->savepoint_offset)))
+ if ((err= ht->savepoint_release(ht, thd,
+ (uchar *)(sv+1) + ht->savepoint_offset)))
{ // cannot happen
my_error(ER_GET_ERRNO, MYF(0), err);
error=1;
@@ -1275,47 +1797,120 @@ int ha_release_savepoint(THD *thd, SAVEPOINT *sv)
}
+static my_bool snapshot_handlerton(THD *thd, plugin_ref plugin,
+ void *arg)
+{
+ handlerton *hton= plugin_data(plugin, handlerton *);
+ if (hton->state == SHOW_OPTION_YES &&
+ hton->start_consistent_snapshot)
+ {
+ hton->start_consistent_snapshot(hton, thd);
+ *((bool *)arg)= false;
+ }
+ return FALSE;
+}
+
int ha_start_consistent_snapshot(THD *thd)
{
-#ifdef HAVE_INNOBASE_DB
- if ((have_innodb == SHOW_OPTION_YES) &&
- !innobase_start_trx_and_assign_read_view(thd))
- return 0;
-#endif
+ bool warn= true;
+
+ plugin_foreach(thd, snapshot_handlerton, MYSQL_STORAGE_ENGINE_PLUGIN, &warn);
+
/*
Same idea as when one wants to CREATE TABLE in one engine which does not
exist:
*/
- push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_UNKNOWN_ERROR,
- "This MySQL server does not support any "
- "consistent-read capable storage engine");
+ if (warn)
+ push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_UNKNOWN_ERROR,
+ "This MySQL server does not support any "
+ "consistent-read capable storage engine");
return 0;
}
-bool ha_flush_logs()
+static my_bool flush_handlerton(THD *thd, plugin_ref plugin,
+ void *arg)
{
- bool result=0;
-#ifdef HAVE_BERKELEY_DB
- if ((have_berkeley_db == SHOW_OPTION_YES) &&
- berkeley_flush_logs())
- result=1;
-#endif
-#ifdef HAVE_INNOBASE_DB
- if ((have_innodb == SHOW_OPTION_YES) &&
- innobase_flush_logs())
- result=1;
-#endif
- return result;
+ handlerton *hton= plugin_data(plugin, handlerton *);
+ if (hton->state == SHOW_OPTION_YES && hton->flush_logs &&
+ hton->flush_logs(hton))
+ return TRUE;
+ return FALSE;
}
-/*
+
+bool ha_flush_logs(handlerton *db_type)
+{
+ if (db_type == NULL)
+ {
+ if (plugin_foreach(NULL, flush_handlerton,
+ MYSQL_STORAGE_ENGINE_PLUGIN, 0))
+ return TRUE;
+ }
+ else
+ {
+ if (db_type->state != SHOW_OPTION_YES ||
+ (db_type->flush_logs && db_type->flush_logs(db_type)))
+ return TRUE;
+ }
+ return FALSE;
+}
+
+const char *get_canonical_filename(handler *file, const char *path,
+ char *tmp_path)
+{
+ if (lower_case_table_names != 2 || (file->ha_table_flags() & HA_FILE_BASED))
+ return path;
+
+ /* Ensure that table handler get path in lower case */
+ if (tmp_path != path)
+ strmov(tmp_path, path);
+
+ /*
+ we only should turn into lowercase database/table part
+ so start the process after homedirectory
+ */
+ my_casedn_str(files_charset_info, tmp_path + mysql_data_home_len);
+ return tmp_path;
+}
+
+
+/**
+ An interceptor to hijack the text of the error message without
+ setting an error in the thread. We need the text to present it
+ in the form of a warning to the user.
+*/
+
+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);
+ 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)
+{
+ /* Grab the error message */
+ strmake(buff, message, sizeof(buff)-1);
+ return TRUE;
+}
+
+
+/** @brief
This should return ENOENT if the file doesn't exists.
The .frm file will be deleted only if we return 0 or ENOENT
*/
-
-int ha_delete_table(THD *thd, enum db_type table_type, const char *path,
- const char *alias, bool generate_warning)
+int ha_delete_table(THD *thd, handlerton *table_type, const char *path,
+ const char *db, const char *alias, bool generate_warning)
{
handler *file;
char tmp_path[FN_REFLEN];
@@ -1329,53 +1924,43 @@ int ha_delete_table(THD *thd, enum db_type table_type, const char *path,
dummy_table.s= &dummy_share;
/* DB_TYPE_UNKNOWN is used in ALTER TABLE when renaming only .frm files */
- if (table_type == DB_TYPE_UNKNOWN ||
- ! (file=get_new_handler(&dummy_table, thd->mem_root, table_type)))
+ if (table_type == NULL ||
+ ! (file=get_new_handler((TABLE_SHARE*)0, thd->mem_root, table_type)))
DBUG_RETURN(ENOENT);
- if (lower_case_table_names == 2 && !(file->table_flags() & HA_FILE_BASED))
- {
- /* Ensure that table handler get path in lower case */
- strmov(tmp_path, path);
- my_casedn_str(files_charset_info, tmp_path);
- path= tmp_path;
- }
- if ((error= file->delete_table(path)) && generate_warning)
+ path= get_canonical_filename(file, path, tmp_path);
+ if ((error= file->ha_delete_table(path)) && generate_warning)
{
/*
Because file->print_error() use my_error() to generate the error message
- we must store the error state in thd, reset it and restore it to
- be able to get hold of the error message.
- (We should in the future either rewrite handler::print_error() or make
- a nice method of this.
+ we use an internal error handler to intercept it and store the text
+ in a temporary buffer. Later the message will be presented to user
+ as a warning.
*/
- bool query_error= thd->query_error;
- sp_rcontext *spcont= thd->spcont;
- SELECT_LEX *current_select= thd->lex->current_select;
- char buff[sizeof(thd->net.last_error)];
- char new_error[sizeof(thd->net.last_error)];
- int last_errno= thd->net.last_errno;
-
- strmake(buff, thd->net.last_error, sizeof(buff)-1);
- thd->query_error= 0;
- thd->spcont= NULL;
- thd->lex->current_select= 0;
- thd->net.last_error[0]= 0;
+ Ha_delete_table_error_handler ha_delete_table_error_handler;
/* Fill up strucutures that print_error may need */
- dummy_table.s->path= path;
+ dummy_share.path.str= (char*) path;
+ dummy_share.path.length= strlen(path);
+ dummy_share.db.str= (char*) db;
+ dummy_share.db.length= strlen(db);
+ dummy_share.table_name.str= (char*) alias;
+ dummy_share.table_name.length= strlen(alias);
dummy_table.alias= alias;
+ file->change_table_ptr(&dummy_table, &dummy_share);
+
+ thd->push_internal_handler(&ha_delete_table_error_handler);
file->print_error(error, 0);
- strmake(new_error, thd->net.last_error, sizeof(buff)-1);
- /* restore thd */
- thd->query_error= query_error;
- thd->spcont= spcont;
- thd->lex->current_select= current_select;
- thd->net.last_errno= last_errno;
- strmake(thd->net.last_error, buff, sizeof(buff)-1);
- push_warning(thd, MYSQL_ERROR::WARN_LEVEL_ERROR, error, new_error);
+ thd->pop_internal_handler();
+
+ /*
+ XXX: should we convert *all* errors to warnings here?
+ What if the error is fatal?
+ */
+ push_warning(thd, MYSQL_ERROR::WARN_LEVEL_ERROR, error,
+ ha_delete_table_error_handler.buff);
}
delete file;
DBUG_RETURN(error);
@@ -1386,31 +1971,61 @@ int ha_delete_table(THD *thd, enum db_type table_type, const char *path,
****************************************************************************/
handler *handler::clone(MEM_ROOT *mem_root)
{
- handler *new_handler= get_new_handler(table, mem_root, table->s->db_type);
+ handler *new_handler= get_new_handler(table->s, mem_root, table->s->db_type());
/*
Allocate handler->ref here because otherwise ha_open will allocate it
on this->table->mem_root and we will not be able to reclaim that memory
when the clone handler object is destroyed.
*/
- if (!(new_handler->ref= (byte*) alloc_root(mem_root, ALIGN_SIZE(ref_length)*2)))
+ if (!(new_handler->ref= (uchar*) alloc_root(mem_root, ALIGN_SIZE(ref_length)*2)))
return NULL;
- if (new_handler && !new_handler->ha_open(table->s->path, table->db_stat,
+ if (new_handler && !new_handler->ha_open(table,
+ table->s->normalized_path.str,
+ table->db_stat,
HA_OPEN_IGNORE_IF_LOCKED))
return new_handler;
return NULL;
}
- /* Open database-handler. Try O_RDONLY if can't open as O_RDWR */
- /* Don't wait for locks if not HA_OPEN_WAIT_IF_LOCKED is set */
-int handler::ha_open(const char *name, int mode, int test_if_locked)
+void handler::ha_statistic_increment(ulong SSV::*offset) const
+{
+ status_var_increment(table->in_use->status_var.*offset);
+}
+
+void **handler::ha_data(THD *thd) const
+{
+ return thd_ha_data(thd, ht);
+}
+
+THD *handler::ha_thd(void) const
+{
+ DBUG_ASSERT(!table || !table->in_use || table->in_use == current_thd);
+ return (table && table->in_use) ? table->in_use : current_thd;
+}
+
+
+/** @brief
+ Open database-handler.
+
+ IMPLEMENTATION
+ Try O_RDONLY if cannot open as O_RDWR
+ Don't wait for locks if not HA_OPEN_WAIT_IF_LOCKED is set
+*/
+int handler::ha_open(TABLE *table_arg, const char *name, int mode,
+ int test_if_locked)
{
int error;
DBUG_ENTER("handler::ha_open");
- DBUG_PRINT("enter",("name: %s db_type: %d db_stat: %d mode: %d lock_test: %d",
- name, table->s->db_type, table->db_stat, mode,
- test_if_locked));
+ DBUG_PRINT("enter",
+ ("name: %s db_type: %d db_stat: %d mode: %d lock_test: %d",
+ name, ht->db_type, table_arg->db_stat, mode,
+ test_if_locked));
+
+ table= table_arg;
+ DBUG_ASSERT(table->s == table_share);
+ DBUG_ASSERT(alloc_root_inited(&table->mem_root));
if ((error=open(name,mode,test_if_locked)))
{
@@ -1423,7 +2038,7 @@ int handler::ha_open(const char *name, int mode, int test_if_locked)
}
if (error)
{
- my_errno=error; /* Safeguard */
+ my_errno= error; /* Safeguard */
DBUG_PRINT("error",("error: %d errno: %d",error,errno));
}
else
@@ -1432,39 +2047,40 @@ int handler::ha_open(const char *name, int mode, int test_if_locked)
table->db_stat|=HA_READ_ONLY;
(void) extra(HA_EXTRA_NO_READCHECK); // Not needed in SQL
- DBUG_ASSERT(alloc_root_inited(&table->mem_root));
/* ref is already allocated for us if we're called from handler::clone() */
- if (!ref && !(ref= (byte*) alloc_root(&table->mem_root,
+ if (!ref && !(ref= (uchar*) alloc_root(&table->mem_root,
ALIGN_SIZE(ref_length)*2)))
{
close();
error=HA_ERR_OUT_OF_MEM;
}
else
- dupp_ref=ref+ALIGN_SIZE(ref_length);
+ dup_ref=ref+ALIGN_SIZE(ref_length);
+ cached_table_flags= table_flags();
}
DBUG_RETURN(error);
}
-/*
- Read first row (only) from a table
- This is never called for InnoDB or BDB tables, as these table types
- has the HA_NOT_EXACT_COUNT set.
-*/
-int handler::read_first_row(byte * buf, uint primary_key)
+/**
+ Read first row (only) from a table.
+
+ This is never called for InnoDB tables, as these table types
+ has the HA_STATS_RECORDS_IS_EXACT set.
+*/
+int handler::read_first_row(uchar * buf, uint primary_key)
{
register int error;
DBUG_ENTER("handler::read_first_row");
- statistic_increment(current_thd->status_var.ha_read_first_count,&LOCK_status);
+ ha_statistic_increment(&SSV::ha_read_first_count);
/*
If there is very few deleted rows in the table, find the first row by
scanning the table.
TODO remove the test for HA_READ_ORDER
*/
- if (deleted < 10 || primary_key >= MAX_KEY ||
+ if (stats.deleted < 10 || primary_key >= MAX_KEY ||
!(index_flags(primary_key, 0, 0) & HA_READ_ORDER))
{
(void) ha_rnd_init(1);
@@ -1474,25 +2090,29 @@ int handler::read_first_row(byte * buf, uint primary_key)
else
{
/* Find the first row through the primary key */
- (void) ha_index_init(primary_key);
+ (void) ha_index_init(primary_key, 0);
error=index_first(buf);
(void) ha_index_end();
}
DBUG_RETURN(error);
}
-/*
- Generate the next auto-increment number based on increment and offset
+/**
+ Generate the next auto-increment number based on increment and offset.
+ computes the lowest number
+ - strictly greater than "nr"
+ - of the form: auto_increment_offset + N * auto_increment_increment
In most cases increment= offset= 1, in which case we get:
- 1,2,3,4,5,...
- If increment=10 and offset=5 and previous number is 1, we get:
- 1,5,15,25,35,...
+ @verbatim 1,2,3,4,5,... @endverbatim
+ If increment=10 and offset=5 and previous number is 1, we get:
+ @verbatim 1,5,15,25,35,... @endverbatim
*/
-
inline ulonglong
-next_insert_id(ulonglong nr,struct system_variables *variables)
+compute_next_insert_id(ulonglong nr,struct system_variables *variables)
{
+ if (variables->auto_increment_increment == 1)
+ return (nr+1); // optimization of the formula below
nr= (((nr+ variables->auto_increment_increment -
variables->auto_increment_offset)) /
(ulonglong) variables->auto_increment_increment);
@@ -1508,20 +2128,12 @@ void handler::adjust_next_insert_id_after_explicit_value(ulonglong nr)
explicitely-specified value larger than this, we need to increase
THD::next_insert_id to be greater than the explicit value.
*/
- THD *thd= table->in_use;
- if (thd->clear_next_insert_id && (nr >= thd->next_insert_id))
- {
- if (thd->variables.auto_increment_increment != 1)
- nr= next_insert_id(nr, &thd->variables);
- else
- nr++;
- thd->next_insert_id= nr;
- DBUG_PRINT("info",("next_insert_id: %lu", (ulong) nr));
- }
+ if ((next_insert_id > 0) && (nr >= next_insert_id))
+ set_next_insert_id(compute_next_insert_id(nr, &table->in_use->variables));
}
-/*
+/** @brief
Computes the largest number X:
- smaller than or equal to "nr"
- of the form: auto_increment_offset + N * auto_increment_increment
@@ -1536,7 +2148,6 @@ void handler::adjust_next_insert_id_after_explicit_value(ulonglong nr)
RETURN
The number X if it exists, "nr" otherwise.
*/
-
inline ulonglong
prev_insert_id(ulonglong nr, struct system_variables *variables)
{
@@ -1561,23 +2172,10 @@ prev_insert_id(ulonglong nr, struct system_variables *variables)
}
-/*
- Update the auto_increment field if necessary
-
- SYNOPSIS
- update_auto_increment()
-
- RETURN
- 0 ok
- HA_ERR_AUTOINC_READ_FAILED
- get_auto_increment() was called and returned ~(ulonglong) 0
- HA_ERR_AUTOINC_ERANGE
- storing value in field caused strict mode failure.
-
-
- IMPLEMENTATION
+/**
+ Update the auto_increment field if necessary.
- Updates columns with type NEXT_NUMBER if:
+ Updates columns with type NEXT_NUMBER if:
- If column value is set to NULL (in which case
auto_increment_field_not_null is 0)
@@ -1585,24 +2183,36 @@ prev_insert_id(ulonglong nr, struct system_variables *variables)
set. In the future we will only set NEXT_NUMBER fields if one sets them
to NULL (or they are not included in the insert list).
-
- There are two different cases when the above is true:
-
- - thd->next_insert_id == 0 (This is the normal case)
- In this case we set the set the column for the first row to the value
- next_insert_id(get_auto_increment(column))) which is normally
- max-used-column-value +1.
-
- We call get_auto_increment() only for the first row in a multi-row
- statement. For the following rows we generate new numbers based on the
- last used number.
-
- - thd->next_insert_id != 0. This happens when we have read a statement
- from the binary log or when one has used SET LAST_INSERT_ID=#.
-
- In this case we will set the column to the value of next_insert_id.
- The next row will be given the id
- next_insert_id(next_insert_id)
+ In those cases, we check if the currently reserved interval still has
+ values we have not used. If yes, we pick the smallest one and use it.
+ Otherwise:
+
+ - If a list of intervals has been provided to the statement via SET
+ INSERT_ID or via an Intvar_log_event (in a replication slave), we pick the
+ first unused interval from this list, consider it as reserved.
+
+ - Otherwise we set the column for the first row to the value
+ next_insert_id(get_auto_increment(column))) which is usually
+ max-used-column-value+1.
+ We call get_auto_increment() for the first row in a multi-row
+ statement. get_auto_increment() will tell us the interval of values it
+ reserved for us.
+
+ - In both cases, for the following rows we use those reserved values without
+ calling the handler again (we just progress in the interval, computing
+ each new value from the previous one). Until we have exhausted them, then
+ we either take the next provided interval or call get_auto_increment()
+ again to reserve a new interval.
+
+ - In both cases, the reserved intervals are remembered in
+ thd->auto_inc_intervals_in_cur_stmt_for_binlog if statement-based
+ binlogging; the last reserved interval is remembered in
+ auto_inc_interval_for_cur_row. The number of reserved intervals is
+ remembered in auto_inc_intervals_count. It differs from the number of
+ elements in thd->auto_inc_intervals_in_cur_stmt_for_binlog() because the
+ latter list is cumulative over all statements forming one binlog event
+ (when stored functions and triggers are used), and collapses two
+ contiguous intervals in one (see its append() method).
The idea is that generated auto_increment values are predictable and
independent of the column values in the table. This is needed to be
@@ -1613,133 +2223,272 @@ prev_insert_id(ulonglong nr, struct system_variables *variables)
inserts a column with a higher value than the last used one, we will
start counting from the inserted value.
- thd->next_insert_id is cleared after it's been used for a statement.
+ This function's "outputs" are: the table's auto_increment field is filled
+ with a value, thd->next_insert_id is filled with the value to use for the
+ next row, if a value was autogenerated for the current row it is stored in
+ thd->insert_id_for_cur_row, if get_auto_increment() was called
+ thd->auto_inc_interval_for_cur_row is modified, if that interval is not
+ present in thd->auto_inc_intervals_in_cur_stmt_for_binlog it is added to
+ this list.
+
+ @todo
+ Replace all references to "next number" or NEXT_NUMBER to
+ "auto_increment", everywhere (see below: there is
+ table->auto_increment_field_not_null, and there also exists
+ table->next_number_field, it's not consistent).
+
+ @retval
+ 0 ok
+ @retval
+ HA_ERR_AUTOINC_READ_FAILED get_auto_increment() was called and
+ returned ~(ulonglong) 0
+ @retval
+ HA_ERR_AUTOINC_ERANGE storing value in field caused strict mode
+ failure.
*/
+#define AUTO_INC_DEFAULT_NB_ROWS 1 // Some prefer 1024 here
+#define AUTO_INC_DEFAULT_NB_MAX_BITS 16
+#define AUTO_INC_DEFAULT_NB_MAX ((1 << AUTO_INC_DEFAULT_NB_MAX_BITS) - 1)
+
int handler::update_auto_increment()
{
- ulonglong nr;
+ ulonglong nr, nb_reserved_values;
+ bool append= FALSE;
THD *thd= table->in_use;
struct system_variables *variables= &thd->variables;
- bool external_auto_increment=
- table->file->table_flags() & HA_EXTERNAL_AUTO_INCREMENT;
DBUG_ENTER("handler::update_auto_increment");
/*
- We must save the previous value to be able to restore it if the
- row was not inserted
+ next_insert_id is a "cursor" into the reserved interval, it may go greater
+ than the interval, but not smaller.
*/
- thd->prev_insert_id= thd->next_insert_id;
+ DBUG_ASSERT(next_insert_id >= auto_inc_interval_for_cur_row.minimum());
if ((nr= table->next_number_field->val_int()) != 0 ||
table->auto_increment_field_not_null &&
thd->variables.sql_mode & MODE_NO_AUTO_VALUE_ON_ZERO)
{
- /* Mark that we didn't generate a new value **/
- auto_increment_column_changed=0;
+ /*
+ Update next_insert_id if we had already generated a value in this
+ statement (case of INSERT VALUES(null),(3763),(null):
+ the last NULL needs to insert 3764, not the value of the first NULL plus
+ 1).
+ */
adjust_next_insert_id_after_explicit_value(nr);
+ insert_id_for_cur_row= 0; // didn't generate anything
DBUG_RETURN(0);
}
- if (external_auto_increment || !(nr= thd->next_insert_id))
+
+ if ((nr= next_insert_id) >= auto_inc_interval_for_cur_row.maximum())
{
- if ((nr= get_auto_increment()) == ~(ulonglong) 0)
- DBUG_RETURN(HA_ERR_AUTOINC_READ_FAILED); // Mark failure
+ /* next_insert_id is beyond what is reserved, so we reserve more. */
+ const Discrete_interval *forced=
+ thd->auto_inc_intervals_forced.get_next();
+ if (forced != NULL)
+ {
+ nr= forced->minimum();
+ nb_reserved_values= forced->values();
+ }
+ else
+ {
+ /*
+ handler::estimation_rows_to_insert was set by
+ handler::ha_start_bulk_insert(); if 0 it means "unknown".
+ */
+ ulonglong nb_desired_values;
+ /*
+ If an estimation was given to the engine:
+ - use it.
+ - if we already reserved numbers, it means the estimation was
+ not accurate, then we'll reserve 2*AUTO_INC_DEFAULT_NB_ROWS the 2nd
+ time, twice that the 3rd time etc.
+ If no estimation was given, use those increasing defaults from the
+ start, starting from AUTO_INC_DEFAULT_NB_ROWS.
+ Don't go beyond a max to not reserve "way too much" (because
+ reservation means potentially losing unused values).
+ Note that in prelocked mode no estimation is given.
+ */
+ if ((auto_inc_intervals_count == 0) && (estimation_rows_to_insert > 0))
+ nb_desired_values= estimation_rows_to_insert;
+ else /* go with the increasing defaults */
+ {
+ /* avoid overflow in formula, with this if() */
+ if (auto_inc_intervals_count <= AUTO_INC_DEFAULT_NB_MAX_BITS)
+ {
+ nb_desired_values= AUTO_INC_DEFAULT_NB_ROWS *
+ (1 << auto_inc_intervals_count);
+ set_if_smaller(nb_desired_values, AUTO_INC_DEFAULT_NB_MAX);
+ }
+ 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,
+ &nb_reserved_values);
+ if (nr == ~(ulonglong) 0)
+ DBUG_RETURN(HA_ERR_AUTOINC_READ_FAILED); // Mark failure
- if (!external_auto_increment && variables->auto_increment_increment != 1)
- nr= next_insert_id(nr-1, variables);
- /*
- Update next row based on the found value. This way we don't have to
- call the handler for every generated auto-increment value on a
- multi-row statement
- */
- thd->next_insert_id= nr;
+ /*
+ That rounding below should not be needed when all engines actually
+ respect offset and increment in get_auto_increment(). But they don't
+ so we still do it. Wonder if for the not-first-in-index we should do
+ it. Hope that this rounding didn't push us out of the interval; even
+ if it did we cannot do anything about it (calling the engine again
+ will not help as we inserted no row).
+ */
+ nr= compute_next_insert_id(nr-1, variables);
+ }
+
+ if (table->s->next_number_keypart == 0)
+ {
+ /* We must defer the appending until "nr" has been possibly truncated */
+ append= TRUE;
+ }
+ else
+ {
+ /*
+ For such auto_increment there is no notion of interval, just a
+ singleton. The interval is not even stored in
+ thd->auto_inc_interval_for_cur_row, so we are sure to call the engine
+ for next row.
+ */
+ DBUG_PRINT("info",("auto_increment: special not-first-in-index"));
+ }
}
DBUG_PRINT("info",("auto_increment: %lu", (ulong) nr));
- /* Mark that we should clear next_insert_id before next stmt */
- thd->clear_next_insert_id= 1;
-
- if (likely(!table->next_number_field->store((longlong) nr, TRUE)))
- thd->insert_id((ulonglong) nr);
- else
- if (thd->killed != THD::KILL_BAD_DATA) /* did we fail strict mode? */
+ if (unlikely(table->next_number_field->store((longlong) nr, TRUE)))
{
/*
- overflow of the field; we'll use the max value, however we try to
- decrease it to honour auto_increment_* variables:
+ first test if the query was aborted due to strict mode constraints
+ */
+ if (thd->killed == THD::KILL_BAD_DATA)
+ DBUG_RETURN(HA_ERR_AUTOINC_ERANGE);
+
+ /*
+ 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).
*/
nr= prev_insert_id(table->next_number_field->val_int(), variables);
- thd->insert_id(nr);
if (unlikely(table->next_number_field->store((longlong) nr, TRUE)))
- thd->insert_id(nr= table->next_number_field->val_int());
+ nr= table->next_number_field->val_int();
+ }
+ if (append)
+ {
+ 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)
+ 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);
}
- else
- DBUG_RETURN(HA_ERR_AUTOINC_ERANGE);
/*
- We can't set next_insert_id if the auto-increment key is not the
- first key part, as there is no guarantee that the first parts will be in
- sequence
+ Record this autogenerated value. If the caller then
+ succeeds to insert this value, it will call
+ record_first_successful_insert_id_in_cur_stmt()
+ which will set first_successful_insert_id_in_cur_stmt if it's not
+ already set.
*/
- if (!table->s->next_number_key_offset)
- {
- /*
- Set next insert id to point to next auto-increment value to be able to
- handle multi-row statements
- This works even if auto_increment_increment > 1
- */
- thd->next_insert_id= next_insert_id(nr, variables);
- }
- else
- thd->next_insert_id= 0;
+ insert_id_for_cur_row= nr;
+ /*
+ Set next insert id to point to next auto-increment value to be able to
+ handle multi-row statements.
+ */
+ set_next_insert_id(compute_next_insert_id(nr, variables));
- /* Mark that we generated a new value */
- auto_increment_column_changed=1;
DBUG_RETURN(0);
}
-/*
- restore_auto_increment
- In case of error on write, we restore the last used next_insert_id value
- because the previous value was not used.
-*/
+/** @brief
+ MySQL signal that it changed the column bitmap
-void handler::restore_auto_increment()
+ USAGE
+ This is for handlers that needs to setup their own column bitmaps.
+ Normally the handler should set up their own column bitmaps in
+ index_init() or rnd_init() and in any column_bitmaps_signal() call after
+ this.
+
+ The handler is allowd to do changes to the bitmap after a index_init or
+ rnd_init() call is made as after this, MySQL will not use the bitmap
+ for any program logic checking.
+*/
+void handler::column_bitmaps_signal()
{
- THD *thd= table->in_use;
- if (thd->next_insert_id)
- {
- thd->next_insert_id= thd->prev_insert_id;
- if (thd->next_insert_id == 0)
- {
- /* we didn't generate a value, engine will be called again */
- thd->clear_next_insert_id= 0;
- }
- }
+ DBUG_ENTER("column_bitmaps_signal");
+ DBUG_PRINT("info", ("read_set: 0x%lx write_set: 0x%lx", (long) table->read_set,
+ (long) table->write_set));
+ DBUG_VOID_RETURN;
}
-ulonglong handler::get_auto_increment()
+/** @brief
+ Reserves an interval of auto_increment values from the handler.
+
+ SYNOPSIS
+ get_auto_increment()
+ offset
+ increment
+ nb_desired_values how many values we want
+ first_value (OUT) the first value reserved by the handler
+ nb_reserved_values (OUT) how many values the handler reserved
+
+ offset and increment means that we want values to be of the form
+ offset + N * increment, where N>=0 is integer.
+ If the function sets *first_value to ~(ulonglong)0 it means an error.
+ If the function sets *nb_reserved_values to ULONGLONG_MAX it means it has
+ reserved to "positive infinite".
+*/
+void handler::get_auto_increment(ulonglong offset, ulonglong increment,
+ ulonglong nb_desired_values,
+ ulonglong *first_value,
+ ulonglong *nb_reserved_values)
{
ulonglong nr;
int error;
(void) extra(HA_EXTRA_KEYREAD);
- index_init(table->s->next_number_index);
- if (!table->s->next_number_key_offset)
+ table->mark_columns_used_by_index_no_reset(table->s->next_number_index,
+ table->read_set);
+ column_bitmaps_signal();
+ index_init(table->s->next_number_index, 1);
+ if (table->s->next_number_keypart == 0)
{ // Autoincrement at key-start
error=index_last(table->record[1]);
+ /*
+ MySQL implicitely assumes such method does locking (as MySQL decides to
+ use nr+increment without checking again with the handler, in
+ handler::update_auto_increment()), so reserves to infinite.
+ */
+ *nb_reserved_values= ULONGLONG_MAX;
}
else
{
- byte key[MAX_KEY_LENGTH];
+ uchar key[MAX_KEY_LENGTH];
key_copy(key, table->record[0],
table->key_info + table->s->next_number_index,
table->s->next_number_key_offset);
- error= index_read(table->record[1], key, table->s->next_number_key_offset,
- HA_READ_PREFIX_LAST);
+ error= index_read_map(table->record[1], key,
+ make_prev_keypart_map(table->s->next_number_keypart),
+ HA_READ_PREFIX_LAST);
+ /*
+ MySQL needs to call us for next row: assume we are inserting ("a",null)
+ here, we return 3, and next this statement will want to insert
+ ("b",null): there is no reason why ("b",3+1) would be the good row to
+ insert: maybe it already exists, maybe 3+1 is too large...
+ */
+ *nb_reserved_values= 1;
}
if (error)
@@ -1749,20 +2498,65 @@ ulonglong handler::get_auto_increment()
val_int_offset(table->s->rec_buff_length)+1);
index_end();
(void) extra(HA_EXTRA_NO_KEYREAD);
- return nr;
+ *first_value= nr;
}
-/*
- Print error that we got from handler function
+void handler::ha_release_auto_increment()
+{
+ release_auto_increment();
+ insert_id_for_cur_row= 0;
+ auto_inc_interval_for_cur_row.replace(0, 0, 0);
+ auto_inc_intervals_count= 0;
+ if (next_insert_id > 0)
+ {
+ next_insert_id= 0;
+ /*
+ this statement used forced auto_increment values if there were some,
+ wipe them away for other statements.
+ */
+ table->in_use->auto_inc_intervals_forced.empty();
+ }
+}
- NOTE
- In case of delete table it's only safe to use the following parts of
- the 'table' structure:
- table->s->path
- table->alias
-*/
+void handler::print_keydup_error(uint key_nr, const char *msg)
+{
+ /* Write the duplicated key in the error message */
+ char key[MAX_KEY_LENGTH];
+ String str(key,sizeof(key),system_charset_info);
+
+ if (key_nr == MAX_KEY)
+ {
+ /* Key is unknown */
+ str.copy("", 0, system_charset_info);
+ my_printf_error(ER_DUP_ENTRY, msg, MYF(0), str.c_ptr(), "*UNKNOWN*");
+ }
+ else
+ {
+ /* Table is opened and defined at this point */
+ key_unpack(&str,table,(uint) key_nr);
+ uint max_length=MYSQL_ERRMSG_SIZE-(uint) strlen(msg);
+ if (str.length() >= max_length)
+ {
+ str.length(max_length-4);
+ str.append(STRING_WITH_LEN("..."));
+ }
+ my_printf_error(ER_DUP_ENTRY, msg,
+ MYF(0), str.c_ptr_safe(), table->key_info[key_nr].name);
+ }
+}
+
+
+/**
+ Print error that we got from handler function.
+
+ @note
+ In case of delete table it's only safe to use the following parts of
+ the 'table' structure:
+ - table->s->path
+ - table->alias
+*/
void handler::print_error(int error, myf errflag)
{
DBUG_ENTER("handler::print_error");
@@ -1792,30 +2586,35 @@ void handler::print_error(int error, myf errflag)
uint key_nr=get_dup_key(error);
if ((int) key_nr >= 0)
{
- /* Write the dupplicated key in the error message */
+ print_keydup_error(key_nr, ER(ER_DUP_ENTRY_WITH_KEY_NAME));
+ DBUG_VOID_RETURN;
+ }
+ textno=ER_DUP_KEY;
+ break;
+ }
+ case HA_ERR_FOREIGN_DUPLICATE_KEY:
+ {
+ uint key_nr= get_dup_key(error);
+ if ((int) key_nr >= 0)
+ {
+ uint max_length;
+ /* Write the key in the error message */
char key[MAX_KEY_LENGTH];
String str(key,sizeof(key),system_charset_info);
-
- if (key_nr == MAX_KEY)
- {
- /* Key is unknown */
- str.copy("", 0, system_charset_info);
- key_nr= (uint) -1;
- }
- else
+ /* Table is opened and defined at this point */
+ key_unpack(&str,table,(uint) key_nr);
+ max_length= (MYSQL_ERRMSG_SIZE-
+ (uint) strlen(ER(ER_FOREIGN_DUPLICATE_KEY)));
+ if (str.length() >= max_length)
{
- key_unpack(&str,table,(uint) key_nr);
- uint max_length=MYSQL_ERRMSG_SIZE-(uint) strlen(ER(ER_DUP_ENTRY));
- if (str.length() >= max_length)
- {
- str.length(max_length-4);
- str.append(STRING_WITH_LEN("..."));
- }
+ str.length(max_length-4);
+ str.append(STRING_WITH_LEN("..."));
}
- my_error(ER_DUP_ENTRY, MYF(0), str.c_ptr(), key_nr+1);
+ my_error(ER_FOREIGN_DUPLICATE_KEY, MYF(0), table_share->table_name.str,
+ str.c_ptr_safe(), key_nr+1);
DBUG_VOID_RETURN;
}
- textno=ER_DUP_KEY;
+ textno= ER_DUP_KEY;
break;
}
case HA_ERR_NULL_IN_SPATIAL:
@@ -1856,8 +2655,12 @@ void handler::print_error(int error, myf errflag)
break;
case HA_ERR_RECORD_FILE_FULL:
case HA_ERR_INDEX_FILE_FULL:
+ {
textno=ER_RECORD_FILE_FULL;
+ /* Write the error message to error log */
+ errflag|= ME_NOREFRESH;
break;
+ }
case HA_ERR_LOCK_WAIT_TIMEOUT:
textno=ER_LOCK_WAIT_TIMEOUT;
break;
@@ -1891,19 +2694,20 @@ void handler::print_error(int error, myf errflag)
textno=ER_TABLE_DEF_CHANGED;
break;
case HA_ERR_NO_SUCH_TABLE:
- {
- /*
- We have to use path to find database name instead of using
- table->table_cache_key because if the table didn't exist, then
- table_cache_key was not set up
- */
- char *db;
- char buff[FN_REFLEN];
- uint length= dirname_part(buff,table->s->path);
- buff[length-1]=0;
- db=buff+dirname_length(buff);
- my_error(ER_NO_SUCH_TABLE, MYF(0), db, table->alias);
+ my_error(ER_NO_SUCH_TABLE, MYF(0), table_share->db.str,
+ table_share->table_name.str);
+ DBUG_VOID_RETURN;
+ case HA_ERR_RBR_LOGGING_FAILED:
+ textno= ER_BINLOG_ROW_LOGGING_FAILED;
break;
+ case HA_ERR_DROP_INDEX_FK:
+ {
+ const char *ptr= "???";
+ uint key_nr= get_dup_key(error);
+ if ((int) key_nr >= 0)
+ ptr= table->key_info[key_nr].name;
+ my_error(ER_DROP_INDEX_FK, MYF(0), ptr);
+ DBUG_VOID_RETURN;
}
case HA_ERR_TABLE_NEEDS_UPGRADE:
textno=ER_TABLE_NEEDS_UPGRADE;
@@ -1937,21 +2741,20 @@ void handler::print_error(int error, myf errflag)
DBUG_VOID_RETURN;
}
}
- my_error(textno, errflag, table->alias, error);
+ my_error(textno, errflag, table_share->table_name.str, error);
DBUG_VOID_RETURN;
}
-/*
- Return an error message specific to this handler
-
- SYNOPSIS
- error error code previously returned by handler
- buf Pointer to String where to add error message
+/**
+ Return an error message specific to this handler.
- Returns true if this is a temporary error
- */
+ @param error error code previously returned by handler
+ @param buf pointer to String where to add error message
+ @return
+ Returns true if this is a temporary error
+*/
bool handler::get_error_message(int error, String* buf)
{
return FALSE;
@@ -1971,7 +2774,7 @@ int handler::check_collation_compatibility()
{
ulong mysql_version= table->s->mysql_version;
- if (mysql_version < 50048)
+ if (mysql_version < 50124)
{
KEY *key= table->key_info;
KEY *key_end= key + table->s->keys;
@@ -1985,15 +2788,18 @@ int handler::check_collation_compatibility()
continue;
Field *field= table->field[key_part->fieldnr - 1];
uint cs_number= field->charset()->number;
- if (mysql_version < 50048 &&
- (cs_number == 11 || /* ascii_general_ci - bug #29499, bug #27562 */
- cs_number == 41 || /* latin7_general_ci - bug #29461 */
- cs_number == 42 || /* latin7_general_cs - bug #29461 */
- cs_number == 20 || /* latin7_estonian_cs - bug #29461 */
- cs_number == 21 || /* latin2_hungarian_ci - bug #29461 */
- cs_number == 22 || /* koi8u_general_ci - bug #29461 */
- cs_number == 23 || /* cp1251_ukrainian_ci - bug #29461 */
- cs_number == 26)) /* cp1250_general_ci - bug #29461 */
+ if ((mysql_version < 50048 &&
+ (cs_number == 11 || /* ascii_general_ci - bug #29499, bug #27562 */
+ cs_number == 41 || /* latin7_general_ci - bug #29461 */
+ cs_number == 42 || /* latin7_general_cs - bug #29461 */
+ cs_number == 20 || /* latin7_estonian_cs - bug #29461 */
+ cs_number == 21 || /* latin2_hungarian_ci - bug #29461 */
+ cs_number == 22 || /* koi8u_general_ci - bug #29461 */
+ cs_number == 23 || /* cp1251_ukrainian_ci - bug #29461 */
+ cs_number == 26)) || /* cp1250_general_ci - bug #29461 */
+ (mysql_version < 50124 &&
+ (cs_number == 33 || /* utf8_general_ci - bug #27877 */
+ cs_number == 35))) /* ucs2_general_ci - bug #27877 */
return HA_ADMIN_NEEDS_UPGRADE;
}
}
@@ -2022,7 +2828,7 @@ int handler::ha_check_for_upgrade(HA_CHECK_OPT *check_opt)
if (!keypart->fieldnr)
continue;
Field *field= table->field[keypart->fieldnr-1];
- if (field->type() == FIELD_TYPE_BLOB)
+ if (field->type() == MYSQL_TYPE_BLOB)
{
if (check_opt->sql_flags & TT_FOR_UPGRADE)
check_opt->flags= T_MEDIUM;
@@ -2035,8 +2841,8 @@ int handler::ha_check_for_upgrade(HA_CHECK_OPT *check_opt)
return HA_ADMIN_NEEDS_ALTER;
if ((error= check_collation_compatibility()))
- return error;
-
+ return error;
+
return check_for_upgrade(check_opt);
}
@@ -2050,7 +2856,7 @@ int handler::check_old_types()
/* check for bad DECIMAL field */
for (field= table->field; (*field); field++)
{
- if ((*field)->type() == FIELD_TYPE_NEWDECIMAL)
+ if ((*field)->type() == MYSQL_TYPE_NEWDECIMAL)
{
return HA_ADMIN_NEEDS_ALTER;
}
@@ -2064,7 +2870,7 @@ int handler::check_old_types()
}
-static bool update_frm_version(TABLE *table, bool needs_lock)
+static bool update_frm_version(TABLE *table)
{
char path[FN_REFLEN];
File file;
@@ -2080,72 +2886,65 @@ static bool update_frm_version(TABLE *table, bool needs_lock)
if (table->s->mysql_version == MYSQL_VERSION_ID)
DBUG_RETURN(0);
- strxnmov(path, sizeof(path)-1, mysql_data_home, "/", table->s->db, "/",
- table->s->table_name, reg_ext, NullS);
- if (!unpack_filename(path, path))
- DBUG_RETURN(1);
-
- if (needs_lock)
- pthread_mutex_lock(&LOCK_open);
+ strxmov(path, table->s->normalized_path.str, reg_ext, NullS);
if ((file= my_open(path, O_RDWR|O_BINARY, MYF(MY_WME))) >= 0)
{
uchar version[4];
- char *key= table->s->table_cache_key;
- uint key_length= table->s->key_length;
+ 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,(byte*) version,4,51L,MYF_RW)))
+ if ((result= my_pwrite(file,(uchar*) version,4,51L,MYF_RW)))
goto err;
- for (entry=(TABLE*) hash_first(&open_cache,(byte*) key,key_length, &state);
+ for (entry=(TABLE*) hash_first(&open_cache,(uchar*) key,key_length, &state);
entry;
- entry= (TABLE*) hash_next(&open_cache,(byte*) key,key_length, &state))
+ entry= (TABLE*) hash_next(&open_cache,(uchar*) key,key_length, &state))
entry->s->mysql_version= MYSQL_VERSION_ID;
}
err:
if (file >= 0)
VOID(my_close(file,MYF(MY_WME)));
- if (needs_lock)
- pthread_mutex_unlock(&LOCK_open);
DBUG_RETURN(result);
}
-/* Return key if error because of duplicated keys */
-
+/**
+ @return
+ key if error because of duplicated keys
+*/
uint handler::get_dup_key(int error)
{
DBUG_ENTER("handler::get_dup_key");
table->file->errkey = (uint) -1;
- if (error == HA_ERR_FOUND_DUPP_KEY || error == HA_ERR_FOUND_DUPP_UNIQUE ||
- error == HA_ERR_NULL_IN_SPATIAL)
+ if (error == HA_ERR_FOUND_DUPP_KEY || error == HA_ERR_FOREIGN_DUPLICATE_KEY ||
+ error == HA_ERR_FOUND_DUPP_UNIQUE || error == HA_ERR_NULL_IN_SPATIAL ||
+ error == HA_ERR_DROP_INDEX_FK)
info(HA_STATUS_ERRKEY | HA_STATUS_NO_LOCK);
DBUG_RETURN(table->file->errkey);
}
-/*
- Delete all files with extension from bas_ext()
+/**
+ Delete all files with extension from bas_ext().
- SYNOPSIS
- delete_table()
- name Base name of table
+ @param name Base name of table
- NOTES
+ @note
We assume that the handler may return more extensions than
was actually used for the file.
- RETURN
+ @retval
0 If we successfully deleted at least one file from base_ext and
- didn't get any other errors than ENOENT
- # Error
+ didn't get any other errors than ENOENT
+ @retval
+ !0 Error
*/
-
int handler::delete_table(const char *name)
{
int error= 0;
@@ -2154,7 +2953,7 @@ int handler::delete_table(const char *name)
for (const char **ext=bas_ext(); *ext ; ext++)
{
- fn_format(buff, name, "", *ext, 2 | 4);
+ fn_format(buff, name, "", *ext, MY_UNPACK_FILENAME|MY_APPEND_EXT);
if (my_delete_with_symlink(buff, MYF(0)))
{
if ((error= my_errno) != ENOENT)
@@ -2184,23 +2983,28 @@ int handler::rename_table(const char * from, const char * to)
}
-/*
- Performs checks upon the table.
+void handler::drop_table(const char *name)
+{
+ close();
+ delete_table(name);
+}
- SYNOPSIS
- check()
- thd thread doing CHECK TABLE operation
- check_opt options from the parser
- NOTES
+/**
+ Performs checks upon the table.
- RETURN
- HA_ADMIN_OK Successful upgrade
- HA_ADMIN_NEEDS_UPGRADE Table has structures requiring upgrade
- HA_ADMIN_NEEDS_ALTER Table has structures requiring ALTER TABLE
- HA_ADMIN_NOT_IMPLEMENTED
-*/
+ @param thd thread doing CHECK TABLE operation
+ @param check_opt options from the parser
+ @retval
+ HA_ADMIN_OK Successful upgrade
+ @retval
+ HA_ADMIN_NEEDS_UPGRADE Table has structures requiring upgrade
+ @retval
+ HA_ADMIN_NEEDS_ALTER Table has structures requiring ALTER TABLE
+ @retval
+ HA_ADMIN_NOT_IMPLEMENTED
+*/
int handler::ha_check(THD *thd, HA_CHECK_OPT *check_opt)
{
int error;
@@ -2221,20 +3025,369 @@ int handler::ha_check(THD *thd, HA_CHECK_OPT *check_opt)
}
if ((error= check(thd, check_opt)))
return error;
- return update_frm_version(table, 0);
+ return update_frm_version(table);
}
+/**
+ A helper function to mark a transaction read-write,
+ if it is started.
+*/
+
+inline
+void
+handler::mark_trx_read_write()
+{
+ Ha_trx_info *ha_info= &ha_thd()->ha_data[ht->slot].ha_info[0];
+ /*
+ When a storage engine method is called, the transaction must
+ have been started, unless it's a DDL call, for which the
+ storage engine starts the transaction internally, and commits
+ it internally, without registering in the ha_list.
+ Unfortunately here we can't know know for sure if the engine
+ has registered the transaction or not, so we must check.
+ */
+ if (ha_info->is_started())
+ {
+ DBUG_ASSERT(has_transactions());
+ /*
+ table_share can be NULL in ha_delete_table(). See implementation
+ of standalone function ha_delete_table() in sql_base.cc.
+ */
+ if (table_share == NULL || table_share->tmp_table == NO_TMP_TABLE)
+ ha_info->set_trx_read_write();
+ }
+}
+
+
+/**
+ Repair table: public interface.
+
+ @sa handler::repair()
+*/
int handler::ha_repair(THD* thd, HA_CHECK_OPT* check_opt)
{
int result;
+
+ mark_trx_read_write();
+
if ((result= repair(thd, check_opt)))
return result;
- return update_frm_version(table, 0);
+ return update_frm_version(table);
}
-/*
+/**
+ Bulk update row: public interface.
+
+ @sa handler::bulk_update_row()
+*/
+
+int
+handler::ha_bulk_update_row(const uchar *old_data, uchar *new_data,
+ uint *dup_key_found)
+{
+ mark_trx_read_write();
+
+ return bulk_update_row(old_data, new_data, dup_key_found);
+}
+
+
+/**
+ Delete all rows: public interface.
+
+ @sa handler::delete_all_rows()
+*/
+
+int
+handler::ha_delete_all_rows()
+{
+ mark_trx_read_write();
+
+ return 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.
+
+ @sa handler::backup()
+*/
+
+int
+handler::ha_backup(THD* thd, HA_CHECK_OPT* check_opt)
+{
+ mark_trx_read_write();
+
+ return backup(thd, check_opt);
+}
+
+
+/**
+ Restore table: public interface.
+
+ @sa handler::restore()
+*/
+
+int
+handler::ha_restore(THD* thd, HA_CHECK_OPT* check_opt)
+{
+ mark_trx_read_write();
+
+ return restore(thd, check_opt);
+}
+
+
+/**
+ Optimize table: public interface.
+
+ @sa handler::optimize()
+*/
+
+int
+handler::ha_optimize(THD* thd, HA_CHECK_OPT* check_opt)
+{
+ mark_trx_read_write();
+
+ return optimize(thd, check_opt);
+}
+
+
+/**
+ Analyze table: public interface.
+
+ @sa handler::analyze()
+*/
+
+int
+handler::ha_analyze(THD* thd, HA_CHECK_OPT* check_opt)
+{
+ mark_trx_read_write();
+
+ return analyze(thd, check_opt);
+}
+
+
+/**
+ Check and repair table: public interface.
+
+ @sa handler::check_and_repair()
+*/
+
+bool
+handler::ha_check_and_repair(THD *thd)
+{
+ mark_trx_read_write();
+
+ return check_and_repair(thd);
+}
+
+
+/**
+ Disable indexes: public interface.
+
+ @sa handler::disable_indexes()
+*/
+
+int
+handler::ha_disable_indexes(uint mode)
+{
+ mark_trx_read_write();
+
+ return disable_indexes(mode);
+}
+
+
+/**
+ Enable indexes: public interface.
+
+ @sa handler::enable_indexes()
+*/
+
+int
+handler::ha_enable_indexes(uint mode)
+{
+ mark_trx_read_write();
+
+ return enable_indexes(mode);
+}
+
+
+/**
+ Discard or import tablespace: public interface.
+
+ @sa handler::discard_or_import_tablespace()
+*/
+
+int
+handler::ha_discard_or_import_tablespace(my_bool discard)
+{
+ mark_trx_read_write();
+
+ return discard_or_import_tablespace(discard);
+}
+
+
+/**
+ Prepare for alter: public interface.
+
+ Called to prepare an *online* ALTER.
+
+ @sa handler::prepare_for_alter()
+*/
+
+void
+handler::ha_prepare_for_alter()
+{
+ mark_trx_read_write();
+
+ prepare_for_alter();
+}
+
+
+/**
+ Rename table: public interface.
+
+ @sa handler::rename_table()
+*/
+
+int
+handler::ha_rename_table(const char *from, const char *to)
+{
+ mark_trx_read_write();
+
+ return rename_table(from, to);
+}
+
+
+/**
+ Delete table: public interface.
+
+ @sa handler::delete_table()
+*/
+
+int
+handler::ha_delete_table(const char *name)
+{
+ mark_trx_read_write();
+
+ return delete_table(name);
+}
+
+
+/**
+ Drop table in the engine: public interface.
+
+ @sa handler::drop_table()
+*/
+
+void
+handler::ha_drop_table(const char *name)
+{
+ mark_trx_read_write();
+
+ return drop_table(name);
+}
+
+
+/**
+ Create a table in the engine: public interface.
+
+ @sa handler::create()
+*/
+
+int
+handler::ha_create(const char *name, TABLE *form, HA_CREATE_INFO *info)
+{
+ mark_trx_read_write();
+
+ return create(name, form, info);
+}
+
+
+/**
+ Create handler files for CREATE TABLE: public interface.
+
+ @sa handler::create_handler_files()
+*/
+
+int
+handler::ha_create_handler_files(const char *name, const char *old_name,
+ int action_flag, HA_CREATE_INFO *info)
+{
+ mark_trx_read_write();
+
+ return create_handler_files(name, old_name, action_flag, info);
+}
+
+
+/**
+ Change partitions: public interface.
+
+ @sa handler::change_partitions()
+*/
+
+int
+handler::ha_change_partitions(HA_CREATE_INFO *create_info,
+ const char *path,
+ ulonglong * const copied,
+ ulonglong * const deleted,
+ const uchar *pack_frm_data,
+ size_t pack_frm_len)
+{
+ mark_trx_read_write();
+
+ return change_partitions(create_info, path, copied, deleted,
+ pack_frm_data, pack_frm_len);
+}
+
+
+/**
+ Drop partitions: public interface.
+
+ @sa handler::drop_partitions()
+*/
+
+int
+handler::ha_drop_partitions(const char *path)
+{
+ mark_trx_read_write();
+
+ return drop_partitions(path);
+}
+
+
+/**
+ Rename partitions: public interface.
+
+ @sa handler::rename_partitions()
+*/
+
+int
+handler::ha_rename_partitions(const char *path)
+{
+ mark_trx_read_write();
+
+ return rename_partitions(path);
+}
+
+
+/**
Tell the storage engine that it is allowed to "disable transaction" in the
handler. It is a hint that ACID is not required - it is used in NDB for
ALTER TABLE, for example, when data are copied to temporary table.
@@ -2242,14 +3395,13 @@ int handler::ha_repair(THD* thd, HA_CHECK_OPT* check_opt)
starts to commit every now and then automatically.
This hint can be safely ignored.
*/
-
int ha_enable_transaction(THD *thd, bool on)
{
int error=0;
-
DBUG_ENTER("ha_enable_transaction");
- thd->transaction.on= on;
- if (on)
+ DBUG_PRINT("enter", ("on: %d", (int) on));
+
+ if ((thd->transaction.on= on))
{
/*
Now all storage engines should have transaction handling enabled.
@@ -2257,24 +3409,86 @@ int ha_enable_transaction(THD *thd, bool on)
is an optimization hint that storage engine is free to ignore.
So, let's commit an open transaction (if any) now.
*/
- if (!(error= ha_commit_stmt(thd)))
+ if (!(error= ha_commit_trans(thd, 0)))
error= end_trans(thd, COMMIT);
}
DBUG_RETURN(error);
}
-int handler::index_next_same(byte *buf, const byte *key, uint keylen)
+int handler::index_next_same(uchar *buf, const uchar *key, uint keylen)
{
int error;
+ DBUG_ENTER("index_next_same");
if (!(error=index_next(buf)))
{
+ my_ptrdiff_t ptrdiff= buf - table->record[0];
+ uchar *save_record_0;
+ KEY *key_info;
+ KEY_PART_INFO *key_part;
+ KEY_PART_INFO *key_part_end;
+ LINT_INIT(save_record_0);
+ LINT_INIT(key_info);
+ LINT_INIT(key_part);
+ LINT_INIT(key_part_end);
+
+ /*
+ key_cmp_if_same() compares table->record[0] against 'key'.
+ In parts it uses table->record[0] directly, in parts it uses
+ field objects with their local pointers into table->record[0].
+ If 'buf' is distinct from table->record[0], we need to move
+ all record references. This is table->record[0] itself and
+ the field pointers of the fields used in this key.
+ */
+ if (ptrdiff)
+ {
+ save_record_0= table->record[0];
+ table->record[0]= buf;
+ key_info= table->key_info + active_index;
+ key_part= key_info->key_part;
+ key_part_end= key_part + key_info->key_parts;
+ for (; key_part < key_part_end; key_part++)
+ {
+ DBUG_ASSERT(key_part->field);
+ key_part->field->move_field_offset(ptrdiff);
+ }
+ }
+
if (key_cmp_if_same(table, key, active_index, keylen))
{
table->status=STATUS_NOT_FOUND;
error=HA_ERR_END_OF_FILE;
}
+
+ /* Move back if necessary. */
+ if (ptrdiff)
+ {
+ table->record[0]= save_record_0;
+ for (key_part= key_info->key_part; key_part < key_part_end; key_part++)
+ key_part->field->move_field_offset(-ptrdiff);
+ }
}
- return error;
+ DBUG_RETURN(error);
+}
+
+
+void handler::get_dynamic_partition_info(PARTITION_INFO *stat_info,
+ uint part_id)
+{
+ info(HA_STATUS_CONST | HA_STATUS_TIME | HA_STATUS_VARIABLE |
+ HA_STATUS_NO_LOCK);
+ stat_info->records= stats.records;
+ stat_info->mean_rec_length= stats.mean_rec_length;
+ stat_info->data_file_length= stats.data_file_length;
+ stat_info->max_data_file_length= stats.max_data_file_length;
+ stat_info->index_file_length= stats.index_file_length;
+ stat_info->delete_length= stats.delete_length;
+ stat_info->create_time= stats.create_time;
+ stat_info->update_time= stats.update_time;
+ stat_info->check_time= stats.check_time;
+ stat_info->check_sum= 0;
+ if (table_flags() & (ulong) HA_HAS_CHECKSUM)
+ stat_info->check_sum= checksum();
+ return;
}
@@ -2282,66 +3496,75 @@ int handler::index_next_same(byte *buf, const byte *key, uint keylen)
** Some general functions that isn't in the handler class
****************************************************************************/
-/*
- Initiates table-file and calls apropriate database-creator
- Returns 1 if something got wrong
-*/
+/**
+ Initiates table-file and calls appropriate database-creator.
-int ha_create_table(const char *name, HA_CREATE_INFO *create_info,
+ @retval
+ 0 ok
+ @retval
+ 1 error
+*/
+int ha_create_table(THD *thd, const char *path,
+ const char *db, const char *table_name,
+ HA_CREATE_INFO *create_info,
bool update_create_info)
{
- int error;
+ int error= 1;
TABLE table;
char name_buff[FN_REFLEN];
+ const char *name;
+ TABLE_SHARE share;
DBUG_ENTER("ha_create_table");
+
+ init_tmp_table_share(thd, &share, db, 0, table_name, path);
+ if (open_table_def(thd, &share, 0) ||
+ open_table_from_share(thd, &share, "", 0, (uint) READ_ALL, 0, &table,
+ TRUE))
+ goto err;
- if (openfrm(current_thd, name,"",0,(uint) READ_ALL, 0, &table))
- DBUG_RETURN(1);
if (update_create_info)
- {
update_create_info_from_table(create_info, &table);
- }
- if (lower_case_table_names == 2 &&
- !(table.file->table_flags() & HA_FILE_BASED))
- {
- /* Ensure that handler gets name in lower case */
- strmov(name_buff, name);
- my_casedn_str(files_charset_info, name_buff);
- name= name_buff;
- }
- error=table.file->create(name,&table,create_info);
- VOID(closefrm(&table));
+ name= get_canonical_filename(table.file, share.path.str, name_buff);
+
+ error= table.file->ha_create(name, &table, create_info);
+ VOID(closefrm(&table, 0));
if (error)
- my_error(ER_CANT_CREATE_TABLE, MYF(ME_BELL+ME_WAITTANG), name,error);
+ {
+ strxmov(name_buff, db, ".", table_name, NullS);
+ my_error(ER_CANT_CREATE_TABLE, MYF(ME_BELL+ME_WAITTANG), name_buff, error);
+ }
+err:
+ free_table_share(&share);
DBUG_RETURN(error != 0);
}
-/*
- Try to discover table from engine and
- if found, write the frm file to disk.
+/**
+ Try to discover table from engine.
- RETURN VALUES:
- -1 : Table did not exists
- 0 : Table created ok
- > 0 : Error, table existed but could not be created
+ @note
+ If found, write the frm file to disk.
+ @retval
+ -1 Table did not exists
+ @retval
+ 0 Table created ok
+ @retval
+ > 0 Error, table existed but could not be created
*/
-
-int ha_create_table_from_engine(THD* thd,
- const char *db,
- const char *name)
+int ha_create_table_from_engine(THD* thd, const char *db, const char *name)
{
int error;
- const void *frmblob;
- uint frmlen;
+ uchar *frmblob;
+ size_t frmlen;
char path[FN_REFLEN];
HA_CREATE_INFO create_info;
TABLE table;
+ TABLE_SHARE share;
DBUG_ENTER("ha_create_table_from_engine");
DBUG_PRINT("enter", ("name '%s'.'%s'", db, name));
- bzero((char*) &create_info,sizeof(create_info));
+ bzero((uchar*) &create_info,sizeof(create_info));
if ((error= ha_discover(thd, db, name, &frmblob, &frmlen)))
{
/* Table could not be discovered and thus not created */
@@ -2353,27 +3576,30 @@ int ha_create_table_from_engine(THD* thd,
frmblob and frmlen are set, write the frm to disk
*/
- (void)strxnmov(path,FN_REFLEN,mysql_data_home,"/",db,"/",name,NullS);
+ build_table_filename(path, FN_REFLEN-1, db, name, "", 0);
// Save the frm file
error= writefrm(path, frmblob, frmlen);
- my_free((char*) frmblob, MYF(0));
+ my_free(frmblob, MYF(0));
if (error)
DBUG_RETURN(2);
- if (openfrm(thd, path,"",0,(uint) READ_ALL, 0, &table))
+ init_tmp_table_share(thd, &share, db, 0, name, path);
+ if (open_table_def(thd, &share, 0))
+ {
DBUG_RETURN(3);
+ }
+ if (open_table_from_share(thd, &share, "" ,0, 0, 0, &table, FALSE))
+ {
+ free_table_share(&share);
+ DBUG_RETURN(3);
+ }
update_create_info_from_table(&create_info, &table);
create_info.table_options|= HA_OPTION_CREATE_FROM_ENGINE;
- if (lower_case_table_names == 2 &&
- !(table.file->table_flags() & HA_FILE_BASED))
- {
- /* Ensure that handler gets name in lower case */
- my_casedn_str(files_charset_info, path);
- }
- error=table.file->create(path,&table,&create_info);
- VOID(closefrm(&table));
+ get_canonical_filename(table.file, path, path);
+ error=table.file->ha_create(path, &table, &create_info);
+ VOID(closefrm(&table, 1));
DBUG_RETURN(error != 0);
}
@@ -2395,9 +3621,9 @@ void st_ha_check_opt::init()
call to ha_init_key_cache() (probably out of memory)
*****************************************************************************/
-/* Init a key cache if it has not been initied before */
-
-
+/**
+ Init a key cache if it has not been initied before.
+*/
int ha_init_key_cache(const char *name, KEY_CACHE *key_cache)
{
DBUG_ENTER("ha_init_key_cache");
@@ -2405,7 +3631,7 @@ int ha_init_key_cache(const char *name, KEY_CACHE *key_cache)
if (!key_cache->key_cache_inited)
{
pthread_mutex_lock(&LOCK_global_system_variables);
- ulong tmp_buff_size= (ulong) key_cache->param_buff_size;
+ 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;
@@ -2419,8 +3645,9 @@ int ha_init_key_cache(const char *name, KEY_CACHE *key_cache)
}
-/* Resize key cache */
-
+/**
+ Resize key cache.
+*/
int ha_resize_key_cache(KEY_CACHE *key_cache)
{
DBUG_ENTER("ha_resize_key_cache");
@@ -2428,7 +3655,7 @@ int ha_resize_key_cache(KEY_CACHE *key_cache)
if (key_cache->key_cache_inited)
{
pthread_mutex_lock(&LOCK_global_system_variables);
- long tmp_buff_size= (long) key_cache->param_buff_size;
+ 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;
@@ -2441,8 +3668,9 @@ int ha_resize_key_cache(KEY_CACHE *key_cache)
}
-/* Change parameters for key cache (like size) */
-
+/**
+ Change parameters for key cache (like size)
+*/
int ha_change_key_cache_param(KEY_CACHE *key_cache)
{
if (key_cache->key_cache_inited)
@@ -2456,16 +3684,18 @@ int ha_change_key_cache_param(KEY_CACHE *key_cache)
return 0;
}
-/* Free memory allocated by a 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 */
-
+/**
+ Move all tables from one key cache to another one.
+*/
int ha_change_key_cache(KEY_CACHE *old_key_cache,
KEY_CACHE *new_key_cache)
{
@@ -2474,103 +3704,311 @@ int ha_change_key_cache(KEY_CACHE *old_key_cache,
}
-/*
- Try to discover one table from handler(s)
+/**
+ Try to discover one table from handler(s).
- RETURN
- -1 : Table did not exists
- 0 : OK. In this case *frmblob and *frmlen are set
- >0 : error. frmblob and frmlen may not be set
+ @retval
+ -1 Table did not exists
+ @retval
+ 0 OK. In this case *frmblob and *frmlen are set
+ @retval
+ >0 error. frmblob and frmlen may not be set
*/
+struct st_discover_args
+{
+ const char *db;
+ const char *name;
+ uchar **frmblob;
+ size_t *frmlen;
+};
+
+static my_bool discover_handlerton(THD *thd, plugin_ref plugin,
+ void *arg)
+{
+ st_discover_args *vargs= (st_discover_args *)arg;
+ handlerton *hton= plugin_data(plugin, handlerton *);
+ if (hton->state == SHOW_OPTION_YES && hton->discover &&
+ (!(hton->discover(hton, thd, vargs->db, vargs->name,
+ vargs->frmblob,
+ vargs->frmlen))))
+ return TRUE;
+
+ return FALSE;
+}
int ha_discover(THD *thd, const char *db, const char *name,
- const void **frmblob, uint *frmlen)
+ uchar **frmblob, size_t *frmlen)
{
int error= -1; // Table does not exist in any handler
DBUG_ENTER("ha_discover");
DBUG_PRINT("enter", ("db: %s, name: %s", db, name));
+ st_discover_args args= {db, name, frmblob, frmlen};
+
if (is_prefix(name,tmp_file_prefix)) /* skip temporary tables */
DBUG_RETURN(error);
-#ifdef HAVE_NDBCLUSTER_DB
- if (have_ndbcluster == SHOW_OPTION_YES)
- error= ndbcluster_discover(thd, db, name, frmblob, frmlen);
-#endif
+
+ if (plugin_foreach(thd, discover_handlerton,
+ MYSQL_STORAGE_ENGINE_PLUGIN, &args))
+ error= 0;
+
if (!error)
- statistic_increment(thd->status_var.ha_discover_count,&LOCK_status);
+ status_var_increment(thd->status_var.ha_discover_count);
DBUG_RETURN(error);
}
-/*
+/**
Call this function in order to give the handler the possiblity
to ask engine if there are any new tables that should be written to disk
or any dropped tables that need to be removed from disk
*/
+struct st_find_files_args
+{
+ const char *db;
+ const char *path;
+ const char *wild;
+ bool dir;
+ List<LEX_STRING> *files;
+};
+
+static my_bool find_files_handlerton(THD *thd, plugin_ref plugin,
+ void *arg)
+{
+ st_find_files_args *vargs= (st_find_files_args *)arg;
+ handlerton *hton= plugin_data(plugin, handlerton *);
+
+
+ if (hton->state == SHOW_OPTION_YES && hton->find_files)
+ if (hton->find_files(hton, thd, vargs->db, vargs->path, vargs->wild,
+ vargs->dir, vargs->files))
+ return TRUE;
+
+ return FALSE;
+}
int
ha_find_files(THD *thd,const char *db,const char *path,
- const char *wild, bool dir, List<char> *files)
+ const char *wild, bool dir, List<LEX_STRING> *files)
{
int error= 0;
DBUG_ENTER("ha_find_files");
- DBUG_PRINT("enter", ("db: %s, path: %s, wild: %s, dir: %d",
- db, path, wild, dir));
-#ifdef HAVE_NDBCLUSTER_DB
- if (have_ndbcluster == SHOW_OPTION_YES)
- error= ndbcluster_find_files(thd, db, path, wild, dir, files);
-#endif
+ DBUG_PRINT("enter", ("db: '%s' path: '%s' wild: '%s' dir: %d",
+ db, path, wild ? wild : "NULL", dir));
+ st_find_files_args args= {db, path, wild, dir, files};
+
+ plugin_foreach(thd, find_files_handlerton,
+ MYSQL_STORAGE_ENGINE_PLUGIN, &args);
+ /* The return value is not currently used */
DBUG_RETURN(error);
}
-
-/*
- Ask handler if the table exists in engine
-
- RETURN
+/**
+ Ask handler if the table exists in engine.
+ @retval
HA_ERR_NO_SUCH_TABLE Table does not exist
+ @retval
HA_ERR_TABLE_EXIST Table exists
- # Error code
+ @retval
+ \# Error code
+*/
+struct st_table_exists_in_engine_args
+{
+ const char *db;
+ const char *name;
+ int err;
+};
+
+static my_bool table_exists_in_engine_handlerton(THD *thd, plugin_ref plugin,
+ void *arg)
+{
+ st_table_exists_in_engine_args *vargs= (st_table_exists_in_engine_args *)arg;
+ handlerton *hton= plugin_data(plugin, handlerton *);
+
+ int err= HA_ERR_NO_SUCH_TABLE;
+
+ if (hton->state == SHOW_OPTION_YES && hton->table_exists_in_engine)
+ err = hton->table_exists_in_engine(hton, thd, vargs->db, vargs->name);
+
+ vargs->err = err;
+ if (vargs->err == HA_ERR_TABLE_EXIST)
+ return TRUE;
+
+ return FALSE;
+}
- */
int ha_table_exists_in_engine(THD* thd, const char* db, const char* name)
{
- int error= HA_ERR_NO_SUCH_TABLE;
DBUG_ENTER("ha_table_exists_in_engine");
DBUG_PRINT("enter", ("db: %s, name: %s", db, name));
-#ifdef HAVE_NDBCLUSTER_DB
- if (have_ndbcluster == SHOW_OPTION_YES)
- error= ndbcluster_table_exists_in_engine(thd, db, name);
-#endif
- DBUG_PRINT("exit", ("error: %d", error));
- DBUG_RETURN(error);
+ st_table_exists_in_engine_args args= {db, name, HA_ERR_NO_SUCH_TABLE};
+ plugin_foreach(thd, table_exists_in_engine_handlerton,
+ MYSQL_STORAGE_ENGINE_PLUGIN, &args);
+ DBUG_PRINT("exit", ("error: %d", args.err));
+ DBUG_RETURN(args.err);
}
-
+#ifdef HAVE_NDB_BINLOG
/*
+ TODO: change this into a dynamic struct
+ List<handlerton> does not work as
+ 1. binlog_end is called when MEM_ROOT is gone
+ 2. cannot work with thd MEM_ROOT as memory should be freed
+*/
+#define MAX_HTON_LIST_ST 63
+struct hton_list_st
+{
+ handlerton *hton[MAX_HTON_LIST_ST];
+ uint sz;
+};
+
+struct binlog_func_st
+{
+ enum_binlog_func fn;
+ void *arg;
+};
+
+/** @brief
+ Listing handlertons first to avoid recursive calls and deadlock
+*/
+static my_bool binlog_func_list(THD *thd, plugin_ref plugin, void *arg)
+{
+ hton_list_st *hton_list= (hton_list_st *)arg;
+ handlerton *hton= plugin_data(plugin, handlerton *);
+ if (hton->state == SHOW_OPTION_YES && hton->binlog_func)
+ {
+ uint sz= hton_list->sz;
+ if (sz == MAX_HTON_LIST_ST-1)
+ {
+ /* list full */
+ return FALSE;
+ }
+ hton_list->hton[sz]= hton;
+ hton_list->sz= sz+1;
+ }
+ return FALSE;
+}
+
+static my_bool binlog_func_foreach(THD *thd, binlog_func_st *bfn)
+{
+ hton_list_st hton_list;
+ uint i, sz;
+
+ hton_list.sz= 0;
+ plugin_foreach(thd, binlog_func_list,
+ MYSQL_STORAGE_ENGINE_PLUGIN, &hton_list);
+
+ for (i= 0, sz= hton_list.sz; i < sz ; i++)
+ hton_list.hton[i]->binlog_func(hton_list.hton[i], thd, bfn->fn, bfn->arg);
+ return FALSE;
+}
+
+int ha_reset_logs(THD *thd)
+{
+ binlog_func_st bfn= {BFN_RESET_LOGS, 0};
+ binlog_func_foreach(thd, &bfn);
+ return 0;
+}
+
+void ha_reset_slave(THD* thd)
+{
+ binlog_func_st bfn= {BFN_RESET_SLAVE, 0};
+ binlog_func_foreach(thd, &bfn);
+}
+
+void ha_binlog_wait(THD* thd)
+{
+ binlog_func_st bfn= {BFN_BINLOG_WAIT, 0};
+ binlog_func_foreach(thd, &bfn);
+}
+
+int ha_binlog_end(THD* thd)
+{
+ binlog_func_st bfn= {BFN_BINLOG_END, 0};
+ binlog_func_foreach(thd, &bfn);
+ return 0;
+}
+
+int ha_binlog_index_purge_file(THD *thd, const char *file)
+{
+ binlog_func_st bfn= {BFN_BINLOG_PURGE_FILE, (void *)file};
+ binlog_func_foreach(thd, &bfn);
+ return 0;
+}
+
+struct binlog_log_query_st
+{
+ enum_binlog_command binlog_command;
+ const char *query;
+ uint query_length;
+ const char *db;
+ const char *table_name;
+};
+
+static my_bool binlog_log_query_handlerton2(THD *thd,
+ handlerton *hton,
+ void *args)
+{
+ struct binlog_log_query_st *b= (struct binlog_log_query_st*)args;
+ if (hton->state == SHOW_OPTION_YES && hton->binlog_log_query)
+ hton->binlog_log_query(hton, thd,
+ b->binlog_command,
+ b->query,
+ b->query_length,
+ b->db,
+ b->table_name);
+ return FALSE;
+}
+
+static my_bool binlog_log_query_handlerton(THD *thd,
+ plugin_ref plugin,
+ void *args)
+{
+ return binlog_log_query_handlerton2(thd, plugin_data(plugin, handlerton *), args);
+}
+
+void ha_binlog_log_query(THD *thd, handlerton *hton,
+ enum_binlog_command binlog_command,
+ const char *query, uint query_length,
+ const char *db, const char *table_name)
+{
+ struct binlog_log_query_st b;
+ b.binlog_command= binlog_command;
+ b.query= query;
+ b.query_length= query_length;
+ b.db= db;
+ b.table_name= table_name;
+ if (hton == 0)
+ plugin_foreach(thd, binlog_log_query_handlerton,
+ MYSQL_STORAGE_ENGINE_PLUGIN, &b);
+ else
+ binlog_log_query_handlerton2(thd, hton, &b);
+}
+#endif
+
+/**
Read the first row of a multi-range set.
- SYNOPSIS
- read_multi_range_first()
- found_range_p Returns a pointer to the element in 'ranges' that
- corresponds to the returned row.
- ranges An array of KEY_MULTI_RANGE range descriptions.
- range_count Number of ranges in 'ranges'.
- sorted If result should be sorted per key.
- buffer A HANDLER_BUFFER for internal handler usage.
-
- NOTES
- Record is read into table->record[0].
- *found_range_p returns a valid value only if read_multi_range_first()
+ @param found_range_p Returns a pointer to the element in 'ranges' that
+ corresponds to the returned row.
+ @param ranges An array of KEY_MULTI_RANGE range descriptions.
+ @param range_count Number of ranges in 'ranges'.
+ @param sorted If result should be sorted per key.
+ @param buffer A HANDLER_BUFFER for internal handler usage.
+
+ @note
+ - Record is read into table->record[0].
+ - *found_range_p returns a valid value only if read_multi_range_first()
returns 0.
- Sorting is done within each range. If you want an overall sort, enter
+ - Sorting is done within each range. If you want an overall sort, enter
'ranges' with sorted ranges.
- RETURN
+ @retval
0 OK, found a row
+ @retval
HA_ERR_END_OF_FILE No rows in range
- # Error code
+ @retval
+ \# Error code
*/
-
int handler::read_multi_range_first(KEY_MULTI_RANGE **found_range_p,
KEY_MULTI_RANGE *ranges, uint range_count,
bool sorted, HANDLER_BUFFER *buffer)
@@ -2580,13 +4018,16 @@ int handler::read_multi_range_first(KEY_MULTI_RANGE **found_range_p,
multi_range_sorted= sorted;
multi_range_buffer= buffer;
+ table->mark_columns_used_by_index_no_reset(active_index, table->read_set);
+ table->column_bitmaps_set(table->read_set, table->write_set);
+
for (multi_range_curr= ranges, multi_range_end= ranges + range_count;
multi_range_curr < multi_range_end;
multi_range_curr++)
{
- result= read_range_first(multi_range_curr->start_key.length ?
+ result= read_range_first(multi_range_curr->start_key.keypart_map ?
&multi_range_curr->start_key : 0,
- multi_range_curr->end_key.length ?
+ multi_range_curr->end_key.keypart_map ?
&multi_range_curr->end_key : 0,
test(multi_range_curr->range_flag & EQ_RANGE),
multi_range_sorted);
@@ -2600,25 +4041,24 @@ int handler::read_multi_range_first(KEY_MULTI_RANGE **found_range_p,
}
-/*
+/**
Read the next row of a multi-range set.
- SYNOPSIS
- read_multi_range_next()
- found_range_p Returns a pointer to the element in 'ranges' that
- corresponds to the returned row.
+ @param found_range_p Returns a pointer to the element in 'ranges' that
+ corresponds to the returned row.
- NOTES
- Record is read into table->record[0].
- *found_range_p returns a valid value only if read_multi_range_next()
+ @note
+ - Record is read into table->record[0].
+ - *found_range_p returns a valid value only if read_multi_range_next()
returns 0.
- RETURN
+ @retval
0 OK, found a row
+ @retval
HA_ERR_END_OF_FILE No (more) rows in range
- # Error code
+ @retval
+ \# Error code
*/
-
int handler::read_multi_range_next(KEY_MULTI_RANGE **found_range_p)
{
int result;
@@ -2640,6 +4080,8 @@ int handler::read_multi_range_next(KEY_MULTI_RANGE **found_range_p)
}
else
{
+ if (was_semi_consistent_read())
+ goto scan_it_again;
/*
We need to set this for the last range only, but checking this
condition is more expensive than just setting the result code.
@@ -2647,14 +4089,14 @@ int handler::read_multi_range_next(KEY_MULTI_RANGE **found_range_p)
result= HA_ERR_END_OF_FILE;
}
+ multi_range_curr++;
+scan_it_again:
/* Try the next range(s) until one matches a record. */
- for (multi_range_curr++;
- multi_range_curr < multi_range_end;
- multi_range_curr++)
+ for (; multi_range_curr < multi_range_end; multi_range_curr++)
{
- result= read_range_first(multi_range_curr->start_key.length ?
+ result= read_range_first(multi_range_curr->start_key.keypart_map ?
&multi_range_curr->start_key : 0,
- multi_range_curr->end_key.length ?
+ multi_range_curr->end_key.keypart_map ?
&multi_range_curr->end_key : 0,
test(multi_range_curr->range_flag & EQ_RANGE),
multi_range_sorted);
@@ -2671,27 +4113,25 @@ int handler::read_multi_range_next(KEY_MULTI_RANGE **found_range_p)
}
-/*
+/**
Read first row between two ranges.
- Store ranges for future calls to read_range_next
+ Store ranges for future calls to read_range_next.
- SYNOPSIS
- read_range_first()
- start_key Start key. Is 0 if no min range
- end_key End key. Is 0 if no max range
- eq_range_arg Set to 1 if start_key == end_key and the range endpoints
- will not change during query execution.
- sorted Set to 1 if result should be sorted per key
-
- NOTES
+ @param start_key Start key. Is 0 if no min range
+ @param end_key End key. Is 0 if no max range
+ @param eq_range_arg Set to 1 if start_key == end_key
+ @param sorted Set to 1 if result should be sorted per key
+
+ @note
Record is read into table->record[0]
- RETURN
+ @retval
0 Found row
+ @retval
HA_ERR_END_OF_FILE No rows in range
- # Error code
+ @retval
+ \# Error code
*/
-
int handler::read_range_first(const key_range *start_key,
const key_range *end_key,
bool eq_range_arg, bool sorted)
@@ -2713,12 +4153,12 @@ int handler::read_range_first(const key_range *start_key,
if (!start_key) // Read first record
result= index_first(table->record[0]);
else
- result= index_read(table->record[0],
- start_key->key,
- start_key->length,
- start_key->flag);
+ result= index_read_map(table->record[0],
+ start_key->key,
+ start_key->keypart_map,
+ start_key->flag);
if (result)
- DBUG_RETURN((result == HA_ERR_KEY_NOT_FOUND)
+ DBUG_RETURN((result == HA_ERR_KEY_NOT_FOUND)
? HA_ERR_END_OF_FILE
: result);
@@ -2726,21 +4166,19 @@ int handler::read_range_first(const key_range *start_key,
}
-/*
+/**
Read next row between two ranges.
- SYNOPSIS
- read_range_next()
-
- NOTES
+ @note
Record is read into table->record[0]
- RETURN
+ @retval
0 Found row
+ @retval
HA_ERR_END_OF_FILE No rows in range
- # Error code
+ @retval
+ \# Error code
*/
-
int handler::read_range_next()
{
int result;
@@ -2760,24 +4198,21 @@ int handler::read_range_next()
}
-/*
- Compare if found key (in row) is over max-value
+/**
+ Compare if found key (in row) is over max-value.
- SYNOPSIS
- compare_key
- range range to compare to row. May be 0 for no range
+ @param range range to compare to row. May be 0 for no range
- NOTES
- See key.cc::key_cmp() for details
+ @seealso
+ key.cc::key_cmp()
- RETURN
+ @return
The return value is SIGN(key_in_row - range_key):
- 0 Key is equal to range or 'range' == 0 (no range)
- -1 Key is less than range
- 1 Key is larger than range
+ - 0 : Key is equal to range or 'range' == 0 (no range)
+ - -1 : Key is less than range
+ - 1 : Key is larger than range
*/
-
int handler::compare_key(key_range *range)
{
int cmp;
@@ -2789,67 +4224,75 @@ int handler::compare_key(key_range *range)
return cmp;
}
-int handler::index_read_idx(byte * buf, uint index, const byte * key,
- uint key_len, enum ha_rkey_function find_flag)
+
+int handler::index_read_idx_map(uchar * buf, uint index, const uchar * key,
+ key_part_map keypart_map,
+ enum ha_rkey_function find_flag)
{
- int error= ha_index_init(index);
+ int error, error1;
+ error= index_init(index, 0);
if (!error)
- error= index_read(buf, key, key_len, find_flag);
- if (!error)
- error= ha_index_end();
- return error;
+ {
+ error= index_read_map(buf, key, keypart_map, find_flag);
+ error1= index_end();
+ }
+ return error ? error : error1;
}
-/*
+/**
Returns a list of all known extensions.
- SYNOPSIS
- ha_known_exts()
-
- NOTES
No mutexes, worst case race is a minor surplus memory allocation
We have to recreate the extension map if mysqld is restarted (for example
within libmysqld)
- RETURN VALUE
+ @retval
pointer pointer to TYPELIB structure
*/
+static my_bool exts_handlerton(THD *unused, plugin_ref plugin,
+ void *arg)
+{
+ List<char> *found_exts= (List<char> *) arg;
+ handlerton *hton= plugin_data(plugin, handlerton *);
+ handler *file;
+ if (hton->state == SHOW_OPTION_YES && hton->create &&
+ (file= hton->create(hton, (TABLE_SHARE*) 0, current_thd->mem_root)))
+ {
+ List_iterator_fast<char> it(*found_exts);
+ const char **ext, *old_ext;
+
+ for (ext= file->bas_ext(); *ext; ext++)
+ {
+ while ((old_ext= it++))
+ {
+ if (!strcmp(old_ext, *ext))
+ break;
+ }
+ if (!old_ext)
+ found_exts->push_back((char *) *ext);
+
+ it.rewind();
+ }
+ delete file;
+ }
+ return FALSE;
+}
TYPELIB *ha_known_exts(void)
{
- MEM_ROOT *mem_root= current_thd->mem_root;
if (!known_extensions.type_names || mysys_usage_id != known_extensions_id)
{
- handlerton **types;
List<char> found_exts;
- List_iterator_fast<char> it(found_exts);
const char **ext, *old_ext;
known_extensions_id= mysys_usage_id;
- found_exts.push_back((char*) triggers_file_ext);
- found_exts.push_back((char*) trigname_file_ext);
- for (types= sys_table_types; *types; types++)
- {
- if ((*types)->state == SHOW_OPTION_YES)
- {
- handler *file= get_new_handler(0, mem_root,
- (enum db_type) (*types)->db_type);
- for (ext= file->bas_ext(); *ext; ext++)
- {
- while ((old_ext= it++))
- {
- if (!strcmp(old_ext, *ext))
- break;
- }
- if (!old_ext)
- found_exts.push_back((char *) *ext);
-
- it.rewind();
- }
- delete file;
- }
- }
+ found_exts.push_back((char*) TRG_EXT);
+ found_exts.push_back((char*) TRN_EXT);
+
+ plugin_foreach(NULL, exts_handlerton,
+ MYSQL_STORAGE_ENGINE_PLUGIN, &found_exts);
+
ext= (const char **) my_once_alloc(sizeof(char *)*
(found_exts.elements+1),
MYF(MY_WME | MY_FAE));
@@ -2858,9 +4301,508 @@ TYPELIB *ha_known_exts(void)
known_extensions.count= found_exts.elements;
known_extensions.type_names= ext;
+ List_iterator_fast<char> it(found_exts);
while ((old_ext= it++))
*ext++= old_ext;
*ext= 0;
}
return &known_extensions;
}
+
+
+static bool stat_print(THD *thd, const char *type, uint type_len,
+ const char *file, uint file_len,
+ const char *status, uint status_len)
+{
+ Protocol *protocol= thd->protocol;
+ protocol->prepare_for_resend();
+ protocol->store(type, type_len, system_charset_info);
+ protocol->store(file, file_len, system_charset_info);
+ protocol->store(status, status_len, system_charset_info);
+ if (protocol->write())
+ return TRUE;
+ return FALSE;
+}
+
+
+static my_bool showstat_handlerton(THD *thd, plugin_ref plugin,
+ void *arg)
+{
+ enum ha_stat_type stat= *(enum ha_stat_type *) arg;
+ handlerton *hton= plugin_data(plugin, handlerton *);
+ if (hton->state == SHOW_OPTION_YES && hton->show_status &&
+ hton->show_status(hton, thd, stat_print, stat))
+ return TRUE;
+ return FALSE;
+}
+
+bool ha_show_status(THD *thd, handlerton *db_type, enum ha_stat_type stat)
+{
+ List<Item> field_list;
+ Protocol *protocol= thd->protocol;
+ bool result;
+
+ field_list.push_back(new Item_empty_string("Type",10));
+ 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,
+ Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
+ return TRUE;
+
+ if (db_type == NULL)
+ {
+ result= plugin_foreach(thd, showstat_handlerton,
+ MYSQL_STORAGE_ENGINE_PLUGIN, &stat);
+ }
+ else
+ {
+ if (db_type->state != SHOW_OPTION_YES)
+ {
+ const LEX_STRING *name=&hton2plugin[db_type->slot]->name;
+ result= stat_print(thd, name->str, name->length,
+ "", 0, "DISABLED", 8) ? 1 : 0;
+ }
+ else
+ result= db_type->show_status &&
+ db_type->show_status(db_type, thd, stat_print, stat) ? 1 : 0;
+ }
+
+ if (!result)
+ my_eof(thd);
+ return result;
+}
+
+/*
+ Function to check if the conditions for row-based binlogging is
+ correct for the table.
+
+ A row in the given table should be replicated if:
+ - Row-based replication is enabled in the current thread
+ - The binlog is enabled
+ - It is not a temporary table
+ - The binary log is open
+ - The database the table resides in shall be binlogged (binlog_*_db rules)
+ - table is not mysql.event
+*/
+
+static bool check_table_binlog_row_based(THD *thd, TABLE *table)
+{
+ if (table->s->cached_row_logging_check == -1)
+ {
+ int const check(table->s->tmp_table == NO_TMP_TABLE &&
+ binlog_filter->db_ok(table->s->db.str));
+ table->s->cached_row_logging_check= check;
+ }
+
+ DBUG_ASSERT(table->s->cached_row_logging_check == 0 ||
+ table->s->cached_row_logging_check == 1);
+
+ return (thd->current_stmt_binlog_row_based &&
+ table->s->cached_row_logging_check &&
+ (thd->options & OPTION_BIN_LOG) &&
+ mysql_bin_log.is_open());
+}
+
+
+/** @brief
+ Write table maps for all (manually or automatically) locked tables
+ to the binary log.
+
+ SYNOPSIS
+ write_locked_table_maps()
+ thd Pointer to THD structure
+
+ DESCRIPTION
+ This function will generate and write table maps for all tables
+ that are locked by the thread 'thd'. Either manually locked
+ (stored in THD::locked_tables) and automatically locked (stored
+ in THD::lock) are considered.
+
+ RETURN VALUE
+ 0 All OK
+ 1 Failed to write all table maps
+
+ 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 "
+ "thd->extra_lock: 0x%lx",
+ (long) thd, (long) thd->lock,
+ (long) thd->locked_tables, (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];
+ locks[0]= thd->extra_lock;
+ locks[1]= thd->lock;
+ locks[2]= thd->locked_tables;
+ for (uint i= 0 ; i < sizeof(locks)/sizeof(*locks) ; ++i )
+ {
+ MYSQL_LOCK const *const lock= locks[i];
+ if (lock == NULL)
+ continue;
+
+ TABLE **const end_ptr= lock->table + lock->table_count;
+ for (TABLE **table_ptr= lock->table ;
+ table_ptr != end_ptr ;
+ ++table_ptr)
+ {
+ TABLE *const table= *table_ptr;
+ DBUG_PRINT("info", ("Checking table %s", table->s->table_name.str));
+ if (table->current_lock == F_WRLCK &&
+ check_table_binlog_row_based(thd, table))
+ {
+ int const has_trans= table->file->has_transactions();
+ int const error= thd->binlog_write_table_map(table, has_trans);
+ /*
+ If an error occurs, it is the responsibility of the caller to
+ roll back the transaction.
+ */
+ if (unlikely(error))
+ DBUG_RETURN(1);
+ }
+ }
+ }
+ }
+ DBUG_RETURN(0);
+}
+
+
+typedef bool Log_func(THD*, TABLE*, bool, MY_BITMAP*,
+ uint, const uchar*, const uchar*);
+
+static int binlog_log_row(TABLE* table,
+ const uchar *before_record,
+ const uchar *after_record,
+ Log_func *log_func)
+{
+ if (table->no_replicate)
+ return 0;
+ bool error= 0;
+ THD *const thd= table->in_use;
+
+ if (check_table_binlog_row_based(thd, table))
+ {
+ MY_BITMAP cols;
+ /* Potential buffer on the stack for the bitmap */
+ uint32 bitbuf[BITMAP_STACKBUF_SIZE/sizeof(uint32)];
+ uint n_fields= table->s->fields;
+ my_bool use_bitbuf= n_fields <= sizeof(bitbuf)*8;
+
+ /*
+ If there are no table maps written to the binary log, this is
+ the first row handled in this statement. In that case, we need
+ to write table maps for all locked tables to the binary log.
+ */
+ if (likely(!(error= bitmap_init(&cols,
+ use_bitbuf ? bitbuf : NULL,
+ (n_fields + 7) & ~7UL,
+ FALSE))))
+ {
+ bitmap_set_all(&cols);
+ if (likely(!(error= write_locked_table_maps(thd))))
+ error= (*log_func)(thd, table, table->file->has_transactions(),
+ &cols, table->s->fields,
+ before_record, after_record);
+
+ if (!use_bitbuf)
+ bitmap_free(&cols);
+ }
+ }
+ return error ? HA_ERR_RBR_LOGGING_FAILED : 0;
+}
+
+int handler::ha_external_lock(THD *thd, int lock_type)
+{
+ DBUG_ENTER("handler::ha_external_lock");
+ /*
+ Whether this is lock or unlock, this should be true, and is to verify that
+ if get_auto_increment() was called (thus may have reserved intervals or
+ taken a table lock), ha_release_auto_increment() was too.
+ */
+ DBUG_ASSERT(next_insert_id == 0);
+
+ /*
+ 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();
+ DBUG_RETURN(error);
+}
+
+
+/** @brief
+ Check handler usage and reset state of file to after 'open'
+*/
+int handler::ha_reset()
+{
+ DBUG_ENTER("ha_reset");
+ /* Check that we have called all proper deallocation functions */
+ DBUG_ASSERT((uchar*) table->def_read_set.bitmap +
+ table->s->column_bitmap_size ==
+ (uchar*) table->def_write_set.bitmap);
+ DBUG_ASSERT(bitmap_is_set_all(&table->s->all_set));
+ DBUG_ASSERT(table->key_read == 0);
+ /* ensure that ha_index_end / ha_rnd_end has been called */
+ DBUG_ASSERT(inited == NONE);
+ /* Free cache used by filesort */
+ free_io_cache(table);
+ /* reset the bitmaps to point to defaults */
+ table->default_column_bitmaps();
+ DBUG_RETURN(reset());
+}
+
+
+int handler::ha_write_row(uchar *buf)
+{
+ int error;
+ Log_func *log_func= Write_rows_log_event::binlog_row_logging_function;
+ DBUG_ENTER("handler::ha_write_row");
+
+ mark_trx_read_write();
+
+ if (unlikely(error= write_row(buf)))
+ DBUG_RETURN(error);
+ if (unlikely(error= binlog_log_row(table, 0, buf, log_func)))
+ DBUG_RETURN(error); /* purecov: inspected */
+ DBUG_RETURN(0);
+}
+
+
+int handler::ha_update_row(const uchar *old_data, uchar *new_data)
+{
+ int error;
+ Log_func *log_func= Update_rows_log_event::binlog_row_logging_function;
+
+ /*
+ Some storage engines require that the new record is in record[0]
+ (and the old record is in record[1]).
+ */
+ DBUG_ASSERT(new_data == table->record[0]);
+
+ mark_trx_read_write();
+
+ if (unlikely(error= update_row(old_data, new_data)))
+ return error;
+ if (unlikely(error= binlog_log_row(table, old_data, new_data, log_func)))
+ return error;
+ return 0;
+}
+
+int handler::ha_delete_row(const uchar *buf)
+{
+ int error;
+ Log_func *log_func= Delete_rows_log_event::binlog_row_logging_function;
+
+ mark_trx_read_write();
+
+ if (unlikely(error= delete_row(buf)))
+ return error;
+ if (unlikely(error= binlog_log_row(table, buf, 0, log_func)))
+ return error;
+ return 0;
+}
+
+
+
+/** @brief
+ use_hidden_primary_key() is called in case of an update/delete when
+ (table_flags() and HA_PRIMARY_KEY_REQUIRED_FOR_DELETE) is defined
+ but we don't have a primary key
+*/
+void handler::use_hidden_primary_key()
+{
+ /* fallback to use all columns in the table to identify row */
+ table->use_all_columns();
+}
+
+
+/** @brief
+ Dummy function which accept information about log files which is not need
+ by handlers
+*/
+void signal_log_not_needed(struct handlerton, char *log_file)
+{
+ DBUG_ENTER("signal_log_not_needed");
+ DBUG_PRINT("enter", ("logfile '%s'", log_file));
+ DBUG_VOID_RETURN;
+}
+
+
+#ifdef TRANS_LOG_MGM_EXAMPLE_CODE
+/*
+ Example of transaction log management functions based on assumption that logs
+ placed into a directory
+*/
+#include <my_dir.h>
+#include <my_sys.h>
+int example_of_iterator_using_for_logs_cleanup(handlerton *hton)
+{
+ void *buffer;
+ int res= 1;
+ struct handler_iterator iterator;
+ struct handler_log_file_data data;
+
+ if (!hton->create_iterator)
+ return 1; /* iterator creator is not supported */
+
+ if ((*hton->create_iterator)(hton, HA_TRANSACTLOG_ITERATOR, &iterator) !=
+ HA_ITERATOR_OK)
+ {
+ /* error during creation of log iterator or iterator is not supported */
+ return 1;
+ }
+ while((*iterator.next)(&iterator, (void*)&data) == 0)
+ {
+ printf("%s\n", data.filename.str);
+ if (data.status == HA_LOG_STATUS_FREE &&
+ my_delete(data.filename.str, MYF(MY_WME)))
+ goto err;
+ }
+ res= 0;
+err:
+ (*iterator.destroy)(&iterator);
+ return res;
+}
+
+
+/*
+ Here we should get info from handler where it save logs but here is
+ just example, so we use constant.
+ IMHO FN_ROOTDIR ("/") is safe enough for example, because nobody has
+ rights on it except root and it consist of directories only at lest for
+ *nix (sorry, can't find windows-safe solution here, but it is only example).
+*/
+#define fl_dir FN_ROOTDIR
+
+
+/** @brief
+ Dummy function to return log status should be replaced by function which
+ really detect the log status and check that the file is a log of this
+ handler.
+*/
+enum log_status fl_get_log_status(char *log)
+{
+ MY_STAT stat_buff;
+ if (my_stat(log, &stat_buff, MYF(0)))
+ return HA_LOG_STATUS_INUSE;
+ return HA_LOG_STATUS_NOSUCHLOG;
+}
+
+
+struct fl_buff
+{
+ LEX_STRING *names;
+ enum log_status *statuses;
+ uint32 entries;
+ uint32 current;
+};
+
+
+int fl_log_iterator_next(struct handler_iterator *iterator,
+ void *iterator_object)
+{
+ struct fl_buff *buff= (struct fl_buff *)iterator->buffer;
+ struct handler_log_file_data *data=
+ (struct handler_log_file_data *) iterator_object;
+ if (buff->current >= buff->entries)
+ return 1;
+ data->filename= buff->names[buff->current];
+ data->status= buff->statuses[buff->current];
+ buff->current++;
+ return 0;
+}
+
+
+void fl_log_iterator_destroy(struct handler_iterator *iterator)
+{
+ my_free((uchar*)iterator->buffer, MYF(MY_ALLOW_ZERO_PTR));
+}
+
+
+/** @brief
+ returns buffer, to be assigned in handler_iterator struct
+*/
+enum handler_create_iterator_result
+fl_log_iterator_buffer_init(struct handler_iterator *iterator)
+{
+ MY_DIR *dirp;
+ struct fl_buff *buff;
+ char *name_ptr;
+ uchar *ptr;
+ FILEINFO *file;
+ uint32 i;
+
+ /* to be able to make my_free without crash in case of error */
+ iterator->buffer= 0;
+
+ if (!(dirp = my_dir(fl_dir, MYF(0))))
+ {
+ return HA_ITERATOR_ERROR;
+ }
+ if ((ptr= (uchar*)my_malloc(ALIGN_SIZE(sizeof(fl_buff)) +
+ ((ALIGN_SIZE(sizeof(LEX_STRING)) +
+ sizeof(enum log_status) +
+ + FN_REFLEN) *
+ (uint) dirp->number_off_files),
+ MYF(0))) == 0)
+ {
+ return HA_ITERATOR_ERROR;
+ }
+ buff= (struct fl_buff *)ptr;
+ buff->entries= buff->current= 0;
+ ptr= ptr + (ALIGN_SIZE(sizeof(fl_buff)));
+ buff->names= (LEX_STRING*) (ptr);
+ ptr= ptr + ((ALIGN_SIZE(sizeof(LEX_STRING)) *
+ (uint) dirp->number_off_files));
+ buff->statuses= (enum log_status *)(ptr);
+ name_ptr= (char *)(ptr + (sizeof(enum log_status) *
+ (uint) dirp->number_off_files));
+ for (i=0 ; i < (uint) dirp->number_off_files ; i++)
+ {
+ enum log_status st;
+ file= dirp->dir_entry + i;
+ if ((file->name[0] == '.' &&
+ ((file->name[1] == '.' && file->name[2] == '\0') ||
+ file->name[1] == '\0')))
+ continue;
+ if ((st= fl_get_log_status(file->name)) == HA_LOG_STATUS_NOSUCHLOG)
+ continue;
+ name_ptr= strxnmov(buff->names[buff->entries].str= name_ptr,
+ FN_REFLEN, fl_dir, file->name, NullS);
+ buff->names[buff->entries].length= (name_ptr -
+ buff->names[buff->entries].str) - 1;
+ buff->statuses[buff->entries]= st;
+ buff->entries++;
+ }
+
+ iterator->buffer= buff;
+ iterator->next= &fl_log_iterator_next;
+ iterator->destroy= &fl_log_iterator_destroy;
+ return HA_ITERATOR_OK;
+}
+
+
+/* An example of a iterator creator */
+enum handler_create_iterator_result
+fl_create_iterator(enum handler_iterator_type type,
+ struct handler_iterator *iterator)
+{
+ switch(type) {
+ case HA_TRANSACTLOG_ITERATOR:
+ return fl_log_iterator_buffer_init(iterator);
+ default:
+ return HA_ITERATOR_UNSUPPORTED;
+ }
+}
+#endif /*TRANS_LOG_MGM_EXAMPLE_CODE*/
diff --git a/sql/handler.h b/sql/handler.h
index 8706aae5fce..d43fc4725dd 100644
--- a/sql/handler.h
+++ b/sql/handler.h
@@ -1,4 +1,4 @@
-/* Copyright (C) 2000-2006 MySQL AB
+/* Copyright 2000-2008 MySQL AB, 2008 Sun Microsystems, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -20,6 +20,7 @@
#pragma interface /* gcc class implementation */
#endif
+#include <my_handler.h>
#include <ft_global.h>
#include <keycache.h>
@@ -27,10 +28,7 @@
#define NO_HASH /* Not yet implemented */
#endif
-#if defined(HAVE_BERKELEY_DB) || defined(HAVE_INNOBASE_DB) || \
- defined(HAVE_NDBCLUSTER_DB)
#define USING_TRANSACTIONS
-#endif
// the following is for checking tables
@@ -51,15 +49,18 @@
/* Bits in table_flags() to show what database can do */
-/*
- Can switch index during the scan with ::rnd_same() - not used yet.
- see mi_rsame/heap_rsame/myrg_rsame
-*/
-#define HA_READ_RND_SAME (1 << 0)
+#define HA_NO_TRANSACTIONS (1 << 0) /* Doesn't support transactions */
#define HA_PARTIAL_COLUMN_READ (1 << 1) /* read may not return all columns */
#define HA_TABLE_SCAN_ON_INDEX (1 << 2) /* No separate data/index file */
-#define HA_REC_NOT_IN_SEQ (1 << 3) /* ha_info don't return recnumber;
- It returns a position to ha_r_rnd */
+/*
+ The following should be set if the following is not true when scanning
+ a table with rnd_next()
+ - We will see all rows (including deleted ones)
+ - Row positions are 'table->s->db_record_offset' apart
+ If this flag is not set, filesort will do a postion() call for each matched
+ row to be able to find the row later.
+*/
+#define HA_REC_NOT_IN_SEQ (1 << 3)
#define HA_CAN_GEOMETRY (1 << 4)
/*
Reading keys in random order is as fast as reading keys in sort order
@@ -67,21 +68,41 @@
filesort to decide if we should sort key + data or key + pointer-to-row
*/
#define HA_FAST_KEY_READ (1 << 5)
+/*
+ Set the following flag if we on delete should force all key to be read
+ and on update read all keys that changes
+*/
+#define HA_REQUIRES_KEY_COLUMNS_FOR_DELETE (1 << 6)
#define HA_NULL_IN_KEY (1 << 7) /* One can have keys with NULL */
-#define HA_DUPP_POS (1 << 8) /* ha_position() gives dup row */
+#define HA_DUPLICATE_POS (1 << 8) /* ha_position() gives dup row */
#define HA_NO_BLOBS (1 << 9) /* Doesn't support blobs */
#define HA_CAN_INDEX_BLOBS (1 << 10)
#define HA_AUTO_PART_KEY (1 << 11) /* auto-increment in multi-part key */
#define HA_REQUIRE_PRIMARY_KEY (1 << 12) /* .. and can't create a hidden one */
-#define HA_NOT_EXACT_COUNT (1 << 13)
+#define HA_STATS_RECORDS_IS_EXACT (1 << 13) /* stats.records is exact */
/*
INSERT_DELAYED only works with handlers that uses MySQL internal table
level locks
*/
#define HA_CAN_INSERT_DELAYED (1 << 14)
+/*
+ If we get the primary key columns for free when we do an index read
+ It also implies that we have to retrive the primary key when using
+ position() and rnd_pos().
+*/
#define HA_PRIMARY_KEY_IN_READ_INDEX (1 << 15)
+/*
+ If HA_PRIMARY_KEY_REQUIRED_FOR_POSITION is set, it means that to position()
+ uses a primary key. Without primary key, we can't call position().
+*/
+#define HA_PRIMARY_KEY_REQUIRED_FOR_POSITION (1 << 16)
#define HA_CAN_RTREEKEYS (1 << 17)
#define HA_NOT_DELETE_WITH_CACHE (1 << 18)
+/*
+ The following is we need to a primary key to delete (and update) a row.
+ If there is no primary key, all columns needs to be read on update and delete
+*/
+#define HA_PRIMARY_KEY_REQUIRED_FOR_DELETE (1 << 19)
#define HA_NO_PREFIX_CHAR_KEYS (1 << 20)
#define HA_CAN_FULLTEXT (1 << 21)
#define HA_CAN_SQL_HANDLER (1 << 22)
@@ -93,8 +114,22 @@
#define HA_CAN_BIT_FIELD (1 << 28) /* supports bit fields */
#define HA_NEED_READ_RANGE_BUFFER (1 << 29) /* for read_multi_range */
#define HA_ANY_INDEX_MAY_BE_UNIQUE (1 << 30)
-/* The storage engine manages auto_increment itself */
-#define HA_EXTERNAL_AUTO_INCREMENT (1 << 31)
+#define HA_NO_COPY_ON_ALTER (LL(1) << 31)
+#define HA_HAS_RECORDS (LL(1) << 32) /* records() gives exact count*/
+/* Has it's own method of binlog logging */
+#define HA_HAS_OWN_BINLOGGING (LL(1) << 33)
+/*
+ Engine is capable of row-format and statement-format logging,
+ respectively
+*/
+#define HA_BINLOG_ROW_CAPABLE (LL(1) << 34)
+#define HA_BINLOG_STMT_CAPABLE (LL(1) << 35)
+
+/*
+ Set of all binlog flags. Currently only contain the capabilities
+ flags.
+ */
+#define HA_BINLOG_FLAGS (HA_BINLOG_ROW_CAPABLE | HA_BINLOG_STMT_CAPABLE)
/* bits in index_flags(index_number) for what you can do with index */
#define HA_READ_NEXT 1 /* TODO really use this flag */
@@ -105,11 +140,63 @@
#define HA_KEYREAD_ONLY 64 /* Support HA_EXTRA_KEYREAD */
/*
+ 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).
+*/
+#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*/
+/*
+ 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).
+*/
+#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*/
+/*
+ HA_PARTITION_FUNCTION_SUPPORTED indicates that the function is
+ supported at all.
+ HA_FAST_CHANGE_PARTITION means that optimised variants of the changes
+ exists but they are not necessarily done online.
+
+ HA_ONLINE_DOUBLE_WRITE means that the handler supports writing to both
+ the new partition and to the old partitions when updating through the
+ old partitioning schema while performing a change of the partitioning.
+ This means that we can support updating of the table while performing
+ the copy phase of the change. For no lock at all also a double write
+ from new to old must exist and this is not required when this flag is
+ set.
+ This is actually removed even before it was introduced the first time.
+ The new idea is that handlers will handle the lock level already in
+ store_lock for ALTER TABLE partitions.
+
+ HA_PARTITION_ONE_PHASE is a flag that can be set by handlers that take
+ care of changing the partitions online and in one phase. Thus all phases
+ needed to handle the change are implemented inside the storage engine.
+ The storage engine must also support auto-discovery since the frm file
+ is changed as part of the change and this change must be controlled by
+ the storage engine. A typical engine to support this is NDB (through
+ WL #2498).
+*/
+#define HA_PARTITION_FUNCTION_SUPPORTED (1L << 12)
+#define HA_FAST_CHANGE_PARTITION (1L << 13)
+#define HA_PARTITION_ONE_PHASE (1L << 14)
+
+/*
Index scan will not return records in rowid order. Not guaranteed to be
set for unordered (e.g. HASH) indexes.
*/
-#define HA_KEY_SCAN_NOT_ROR 128
-
+#define HA_KEY_SCAN_NOT_ROR 128
/* operations for disable/enable indexes */
#define HA_KEY_SWITCH_NONUNIQ 0
@@ -122,18 +209,9 @@
so: innodb + bdb + ndb + binlog + myisam + myisammrg + archive +
example + csv + heap + blackhole + federated + 0
(yes, the sum is deliberately inaccurate)
+ TODO remove the limit, use dynarrays
*/
-#define MAX_HA 14
-
-/*
- Bits in index_ddl_flags(KEY *wanted_index)
- for what ddl you can do with index
- If none is set, the wanted type of index is not supported
- by the handler at all. See WorkLog 1563.
-*/
-#define HA_DDL_SUPPORT 1 /* Supported by handler */
-#define HA_DDL_WITH_LOCK 2 /* Can create/drop with locked table */
-#define HA_DDL_ONLINE 4 /* Can create/drop without lock */
+#define MAX_HA 15
/*
Parameters for open() (in register form->filestat)
@@ -152,11 +230,6 @@
#define HA_BLOCK_LOCK 256 /* unlock when reading some records */
#define HA_OPEN_TEMPORARY 512
- /* Errors on write which is recoverable (Key exist) */
-#define HA_WRITE_SKIP 121 /* Duplicate key on write */
-#define HA_READ_CHECK 123 /* Update with is recoverable */
-#define HA_CANT_DO_THAT 131 /* Databasehandler can't do it */
-
/* Some key definitions */
#define HA_KEY_NULL_LENGTH 1
#define HA_KEY_BLOB_LENGTH 2
@@ -177,7 +250,12 @@
/* Options of START TRANSACTION statement (and later of SET TRANSACTION stmt) */
#define MYSQL_START_TRANS_OPT_WITH_CONS_SNAPSHOT 1
-enum db_type
+/* Flags for method is_fatal_error */
+#define HA_CHECK_DUP_KEY 1
+#define HA_CHECK_DUP_UNIQUE 2
+#define HA_CHECK_DUP (HA_CHECK_DUP_KEY + HA_CHECK_DUP_UNIQUE)
+
+enum legacy_db_type
{
DB_TYPE_UNKNOWN=0,DB_TYPE_DIAB_ISAM=1,
DB_TYPE_HASH,DB_TYPE_MISAM,DB_TYPE_PISAM,
@@ -188,18 +266,45 @@ enum db_type
DB_TYPE_EXAMPLE_DB, DB_TYPE_ARCHIVE_DB, DB_TYPE_CSV_DB,
DB_TYPE_FEDERATED_DB,
DB_TYPE_BLACKHOLE_DB,
- DB_TYPE_DEFAULT // Must be last
+ DB_TYPE_PARTITION_DB,
+ DB_TYPE_BINLOG,
+ DB_TYPE_SOLID,
+ DB_TYPE_PBXT,
+ DB_TYPE_TABLE_FUNCTION,
+ DB_TYPE_MEMCACHE,
+ DB_TYPE_FALCON,
+ DB_TYPE_MARIA,
+ DB_TYPE_FIRST_DYNAMIC=42,
+ DB_TYPE_DEFAULT=127 // Must be last
};
enum row_type { ROW_TYPE_NOT_USED=-1, ROW_TYPE_DEFAULT, ROW_TYPE_FIXED,
ROW_TYPE_DYNAMIC, ROW_TYPE_COMPRESSED,
- ROW_TYPE_REDUNDANT, ROW_TYPE_COMPACT };
+ ROW_TYPE_REDUNDANT, ROW_TYPE_COMPACT, ROW_TYPE_PAGE };
+
+enum enum_binlog_func {
+ BFN_RESET_LOGS= 1,
+ BFN_RESET_SLAVE= 2,
+ BFN_BINLOG_WAIT= 3,
+ BFN_BINLOG_END= 4,
+ BFN_BINLOG_PURGE_FILE= 5
+};
+
+enum enum_binlog_command {
+ LOGCOM_CREATE_TABLE,
+ LOGCOM_ALTER_TABLE,
+ LOGCOM_RENAME_TABLE,
+ LOGCOM_DROP_TABLE,
+ LOGCOM_CREATE_DB,
+ LOGCOM_ALTER_DB,
+ LOGCOM_DROP_DB
+};
/* struct to hold information about the table that should be created */
/* Bits in used_fields */
#define HA_CREATE_USED_AUTO (1L << 0)
-#define HA_CREATE_USED_RAID (1L << 1)
+#define HA_CREATE_USED_RAID (1L << 1) //RAID is no longer availble
#define HA_CREATE_USED_UNION (1L << 2)
#define HA_CREATE_USED_INSERT_METHOD (1L << 3)
#define HA_CREATE_USED_MIN_ROWS (1L << 4)
@@ -217,6 +322,9 @@ enum row_type { ROW_TYPE_NOT_USED=-1, ROW_TYPE_DEFAULT, ROW_TYPE_FIXED,
#define HA_CREATE_USED_COMMENT (1L << 16)
#define HA_CREATE_USED_PASSWORD (1L << 17)
#define HA_CREATE_USED_CONNECTION (1L << 18)
+#define HA_CREATE_USED_KEY_BLOCK_SIZE (1L << 19)
+#define HA_CREATE_USED_TRANSACTIONAL (1L << 20)
+#define HA_CREATE_USED_PAGE_CHECKSUM (1L << 21)
typedef ulonglong my_xid; // this line is the same as in log_event.h
#define MYSQL_XID_PREFIX "MySQLXid"
@@ -224,17 +332,28 @@ typedef ulonglong my_xid; // this line is the same as in log_event.h
#define MYSQL_XID_OFFSET (MYSQL_XID_PREFIX_LEN+sizeof(server_id))
#define MYSQL_XID_GTRID_LEN (MYSQL_XID_OFFSET+sizeof(my_xid))
-#define XIDDATASIZE 128
+#define XIDDATASIZE MYSQL_XIDDATASIZE
#define MAXGTRIDSIZE 64
#define MAXBQUALSIZE 64
+#define COMPATIBLE_DATA_YES 0
+#define COMPATIBLE_DATA_NO 1
+
+/**
+ struct xid_t is binary compatible with the XID structure as
+ in the X/Open CAE Specification, Distributed Transaction Processing:
+ The XA Specification, X/Open Company Ltd., 1991.
+ http://www.opengroup.org/bookstore/catalog/c193.htm
+
+ @see MYSQL_XID in mysql/plugin.h
+*/
struct xid_t {
long formatID;
long gtrid_length;
long bqual_length;
char data[XIDDATASIZE]; // not \0-terminated !
- xid_t() {} /* Remove gcc warning */
+ xid_t() {} /* Remove gcc warning */
bool eq(struct xid_t *xid)
{ return eq(xid->gtrid_length, xid->bqual_length, xid->data); }
bool eq(long g, long b, const char *d)
@@ -284,9 +403,9 @@ struct xid_t {
return sizeof(formatID)+sizeof(gtrid_length)+sizeof(bqual_length)+
gtrid_length+bqual_length;
}
- byte *key()
+ uchar *key()
{
- return (byte *)&gtrid_length;
+ return (uchar *)&gtrid_length;
}
uint key_length()
{
@@ -304,6 +423,162 @@ typedef struct xid_t XID;
#endif
/*
+ These structures are used to pass information from a set of SQL commands
+ on add/drop/change tablespace definitions to the proper hton.
+*/
+#define UNDEF_NODEGROUP 65535
+enum ts_command_type
+{
+ TS_CMD_NOT_DEFINED = -1,
+ CREATE_TABLESPACE = 0,
+ ALTER_TABLESPACE = 1,
+ CREATE_LOGFILE_GROUP = 2,
+ ALTER_LOGFILE_GROUP = 3,
+ DROP_TABLESPACE = 4,
+ DROP_LOGFILE_GROUP = 5,
+ CHANGE_FILE_TABLESPACE = 6,
+ ALTER_ACCESS_MODE_TABLESPACE = 7
+};
+
+enum ts_alter_tablespace_type
+{
+ TS_ALTER_TABLESPACE_TYPE_NOT_DEFINED = -1,
+ ALTER_TABLESPACE_ADD_FILE = 1,
+ ALTER_TABLESPACE_DROP_FILE = 2
+};
+
+enum tablespace_access_mode
+{
+ TS_NOT_DEFINED= -1,
+ TS_READ_ONLY = 0,
+ TS_READ_WRITE = 1,
+ TS_NOT_ACCESSIBLE = 2
+};
+
+struct handlerton;
+class st_alter_tablespace : public Sql_alloc
+{
+ public:
+ const char *tablespace_name;
+ const char *logfile_group_name;
+ enum ts_command_type ts_cmd_type;
+ enum ts_alter_tablespace_type ts_alter_tablespace_type;
+ const char *data_file_name;
+ const char *undo_file_name;
+ const char *redo_file_name;
+ ulonglong extent_size;
+ ulonglong undo_buffer_size;
+ ulonglong redo_buffer_size;
+ ulonglong initial_size;
+ ulonglong autoextend_size;
+ ulonglong max_size;
+ uint nodegroup_id;
+ handlerton *storage_engine;
+ bool wait_until_completed;
+ const char *ts_comment;
+ enum tablespace_access_mode ts_access_mode;
+ st_alter_tablespace()
+ {
+ tablespace_name= NULL;
+ logfile_group_name= "DEFAULT_LG"; //Default log file group
+ ts_cmd_type= TS_CMD_NOT_DEFINED;
+ data_file_name= NULL;
+ undo_file_name= NULL;
+ redo_file_name= NULL;
+ extent_size= 1024*1024; //Default 1 MByte
+ undo_buffer_size= 8*1024*1024; //Default 8 MByte
+ redo_buffer_size= 8*1024*1024; //Default 8 MByte
+ initial_size= 128*1024*1024; //Default 128 MByte
+ autoextend_size= 0; //No autoextension as default
+ max_size= 0; //Max size == initial size => no extension
+ storage_engine= NULL;
+ nodegroup_id= UNDEF_NODEGROUP;
+ wait_until_completed= TRUE;
+ ts_comment= NULL;
+ ts_access_mode= TS_NOT_DEFINED;
+ }
+};
+
+/* 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 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,
+ const char *file, uint file_len,
+ const char *status, uint status_len);
+enum ha_stat_type { HA_ENGINE_STATUS, HA_ENGINE_LOGS, HA_ENGINE_MUTEX };
+extern st_plugin_int *hton2plugin[MAX_HA];
+
+/* Transaction log maintains type definitions */
+enum log_status
+{
+ HA_LOG_STATUS_FREE= 0, /* log is free and can be deleted */
+ HA_LOG_STATUS_INUSE= 1, /* log can't be deleted because it is in use */
+ HA_LOG_STATUS_NOSUCHLOG= 2 /* no such log (can't be returned by
+ the log iterator status) */
+};
+/*
+ Function for signaling that the log file changed its state from
+ LOG_STATUS_INUSE to LOG_STATUS_FREE
+
+ Now it do nothing, will be implemented as part of new transaction
+ log management for engines.
+ TODO: implement the function.
+*/
+void signal_log_not_needed(struct handlerton, char *log_file);
+/*
+ Data of transaction log iterator.
+*/
+struct handler_log_file_data {
+ LEX_STRING filename;
+ enum log_status status;
+};
+
+
+enum handler_iterator_type
+{
+ /* request of transaction log iterator */
+ HA_TRANSACTLOG_ITERATOR= 1
+};
+enum handler_create_iterator_result
+{
+ HA_ITERATOR_OK, /* iterator created */
+ HA_ITERATOR_UNSUPPORTED, /* such type of iterator is not supported */
+ HA_ITERATOR_ERROR /* error during iterator creation */
+};
+
+/*
+ Iterator structure. Can be used by handler/handlerton for different purposes.
+
+ Iterator should be created in the way to point "before" the first object
+ it iterate, so next() call move it to the first object or return !=0 if
+ there is nothing to iterate through.
+*/
+struct handler_iterator {
+ /*
+ Moves iterator to next record and return 0 or return !=0
+ if there is no records.
+ iterator_object will be filled by this function if next() returns 0.
+ Content of the iterator_object depend on iterator type.
+ */
+ int (*next)(struct handler_iterator *, void *iterator_object);
+ /*
+ Free resources allocated by iterator, after this call iterator
+ is not usable.
+ */
+ void (*destroy)(struct handler_iterator *);
+ /*
+ Pointer to buffer for the iterator to use.
+ Should be allocated by function which created the iterator and
+ destroied by freed by above "destroy" call
+ */
+ void *buffer;
+};
+
+/*
handlerton is a singleton structure - one instance per storage engine -
to provide access to storage engine functionality that works on the
"global" level (unlike handler class that works on a per-table basis)
@@ -314,33 +589,18 @@ typedef struct xid_t XID;
savepoint_*, prepare, recover, and *_by_xid pointers can be 0.
*/
-typedef struct
+struct handlerton
{
/*
- storage engine name as it should be printed to a user
- */
- const char *name;
-
- /*
Historical marker for if the engine is available of not
*/
SHOW_COMP_OPTION state;
/*
- A comment used by SHOW to describe an engine.
- */
- const char *comment;
-
- /*
Historical number used for frm file to determine the correct storage engine.
This is going away and new engines will just use "name" for this.
*/
- enum db_type db_type;
- /*
- Method that initizlizes a storage engine
- */
- bool (*init)();
-
+ enum legacy_db_type db_type;
/*
each storage engine has it's own memory area (actually a pointer)
in the thd, for storing per-connection information.
@@ -369,18 +629,18 @@ typedef struct
this storage area - set it to something, so that MySQL would know
this storage engine was accessed in this connection
*/
- int (*close_connection)(THD *thd);
+ int (*close_connection)(handlerton *hton, THD *thd);
/*
sv points to an uninitialized storage area of requested size
(see savepoint_offset description)
*/
- int (*savepoint_set)(THD *thd, void *sv);
+ int (*savepoint_set)(handlerton *hton, THD *thd, void *sv);
/*
sv points to a storage area, that was earlier passed
to the savepoint_set call
*/
- int (*savepoint_rollback)(THD *thd, void *sv);
- int (*savepoint_release)(THD *thd, void *sv);
+ int (*savepoint_rollback)(handlerton *hton, THD *thd, void *sv);
+ int (*savepoint_release)(handlerton *hton, THD *thd, void *sv);
/*
'all' is true if it's a real commit, that makes persistent changes
'all' is false if it's not in fact a commit but an end of the
@@ -388,38 +648,90 @@ typedef struct
NOTE 'all' is also false in auto-commit mode where 'end of statement'
and 'real commit' mean the same event.
*/
- int (*commit)(THD *thd, bool all);
- int (*rollback)(THD *thd, bool all);
- int (*prepare)(THD *thd, bool all);
- int (*recover)(XID *xid_list, uint len);
- int (*commit_by_xid)(XID *xid);
- int (*rollback_by_xid)(XID *xid);
- void *(*create_cursor_read_view)();
- void (*set_cursor_read_view)(void *);
- void (*close_cursor_read_view)(void *);
+ int (*commit)(handlerton *hton, THD *thd, bool all);
+ int (*rollback)(handlerton *hton, THD *thd, bool all);
+ int (*prepare)(handlerton *hton, THD *thd, bool all);
+ int (*recover)(handlerton *hton, XID *xid_list, uint len);
+ int (*commit_by_xid)(handlerton *hton, XID *xid);
+ int (*rollback_by_xid)(handlerton *hton, XID *xid);
+ void *(*create_cursor_read_view)(handlerton *hton, THD *thd);
+ void (*set_cursor_read_view)(handlerton *hton, THD *thd, void *read_view);
+ void (*close_cursor_read_view)(handlerton *hton, THD *thd, void *read_view);
+ handler *(*create)(handlerton *hton, TABLE_SHARE *table, MEM_ROOT *mem_root);
+ void (*drop_database)(handlerton *hton, char* path);
+ int (*panic)(handlerton *hton, enum ha_panic_function flag);
+ int (*start_consistent_snapshot)(handlerton *hton, THD *thd);
+ bool (*flush_logs)(handlerton *hton);
+ bool (*show_status)(handlerton *hton, THD *thd, stat_print_fn *print, enum ha_stat_type stat);
+ uint (*partition_flags)();
+ uint (*alter_table_flags)(uint flags);
+ 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);
uint32 flags; /* global handler flags */
-} handlerton;
+ /*
+ Those handlerton functions below are properly initialized at handler
+ init.
+ */
+ int (*binlog_func)(handlerton *hton, THD *thd, enum_binlog_func fn, void *arg);
+ void (*binlog_log_query)(handlerton *hton, THD *thd,
+ enum_binlog_command binlog_command,
+ const char *query, uint query_length,
+ const char *db, const char *table_name);
+ int (*release_temporary_latches)(handlerton *hton, THD *thd);
-struct show_table_alias_st {
- const char *alias;
- const char *type;
+ /*
+ Get log status.
+ If log_status is null then the handler do not support transaction
+ log information (i.e. log iterator can't be created).
+ (see example of implementation in handler.cc, TRANS_LOG_MGM_EXAMPLE_CODE)
+
+ */
+ enum log_status (*get_log_status)(handlerton *hton, char *log);
+
+ /*
+ Iterators creator.
+ Presence of the pointer should be checked before using
+ */
+ enum handler_create_iterator_result
+ (*create_iterator)(handlerton *hton, enum handler_iterator_type type,
+ struct handler_iterator *fill_this_in);
+ int (*discover)(handlerton *hton, THD* thd, const char *db,
+ const char *name,
+ uchar **frmblob,
+ size_t *frmlen);
+ int (*find_files)(handlerton *hton, THD *thd,
+ const char *db,
+ const char *path,
+ const char *wild, bool dir, List<LEX_STRING> *files);
+ int (*table_exists_in_engine)(handlerton *hton, THD* thd, const char *db,
+ const char *name);
+ uint32 license; /* Flag for Engine License */
+ void *data; /* Location for engines to keep personal structures */
};
-/* Possible flags of a handlerton */
+
+/* Possible flags of a handlerton (there can be 32 of them) */
#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_HIDDEN (1 << 3) //Engine does not appear in lists
+#define HTON_FLUSH_AFTER_RENAME (1 << 4)
+#define HTON_NOT_USER_SELECTABLE (1 << 5)
+#define HTON_TEMPORARY_NOT_SUPPORTED (1 << 6) //Having temporary tables not supported
+#define HTON_SUPPORT_LOG_TABLES (1 << 7) //Engine supports log tables
+#define HTON_NO_PARTITION (1 << 8) //You can not partition these tables
+
+class Ha_trx_info;
-typedef struct st_thd_trans
+struct THD_TRANS
{
- /* number of entries in the ht[] */
- uint nht;
/* true is not all entries in the ht[] support 2pc */
bool no_2pc;
- /* storage engines that registered themselves for this transaction */
- handlerton *ht[MAX_HA];
+ /* storage engines that registered in this transaction */
+ Ha_trx_info *ha_list;
/*
The purpose of this flag is to keep track of non-transactional
tables that were modified in scope of:
@@ -449,48 +761,230 @@ typedef struct st_thd_trans
saved value.
*/
bool modified_non_trans_table;
-} THD_TRANS;
+
+ void reset() { no_2pc= FALSE; modified_non_trans_table= FALSE; }
+};
+
+
+/**
+ Either statement transaction or normal transaction - related
+ thread-specific storage engine data.
+
+ If a storage engine participates in a statement/transaction,
+ an instance of this class is present in
+ thd->transaction.{stmt|all}.ha_list. The addition to
+ {stmt|all}.ha_list is made by trans_register_ha().
+
+ When it's time to commit or rollback, each element of ha_list
+ is used to access storage engine's prepare()/commit()/rollback()
+ methods, and also to evaluate if a full two phase commit is
+ necessary.
+
+ @sa General description of transaction handling in handler.cc.
+*/
+
+class Ha_trx_info
+{
+public:
+ /** Register this storage engine in the given transaction context. */
+ void register_ha(THD_TRANS *trans, handlerton *ht_arg)
+ {
+ DBUG_ASSERT(m_flags == 0);
+ DBUG_ASSERT(m_ht == NULL);
+ DBUG_ASSERT(m_next == NULL);
+
+ m_ht= ht_arg;
+ m_flags= (int) TRX_READ_ONLY; /* Assume read-only at start. */
+
+ m_next= trans->ha_list;
+ trans->ha_list= this;
+ }
+
+ /** Clear, prepare for reuse. */
+ void reset()
+ {
+ m_next= NULL;
+ m_ht= NULL;
+ m_flags= 0;
+ }
+
+ Ha_trx_info() { reset(); }
+
+ void set_trx_read_write()
+ {
+ DBUG_ASSERT(is_started());
+ m_flags|= (int) TRX_READ_WRITE;
+ }
+ bool is_trx_read_write() const
+ {
+ DBUG_ASSERT(is_started());
+ return m_flags & (int) TRX_READ_WRITE;
+ }
+ bool is_started() const { return m_ht != NULL; }
+ /** Mark this transaction read-write if the argument is read-write. */
+ void coalesce_trx_with(const Ha_trx_info *stmt_trx)
+ {
+ /*
+ Must be called only after the transaction has been started.
+ Can be called many times, e.g. when we have many
+ read-write statements in a transaction.
+ */
+ DBUG_ASSERT(is_started());
+ if (stmt_trx->is_trx_read_write())
+ set_trx_read_write();
+ }
+ Ha_trx_info *next() const
+ {
+ DBUG_ASSERT(is_started());
+ return m_next;
+ }
+ handlerton *ht() const
+ {
+ DBUG_ASSERT(is_started());
+ return m_ht;
+ }
+private:
+ enum { TRX_READ_ONLY= 0, TRX_READ_WRITE= 1 };
+ /** Auxiliary, used for ha_list management */
+ Ha_trx_info *m_next;
+ /**
+ Although a given Ha_trx_info instance is currently always used
+ for the same storage engine, 'ht' is not-NULL only when the
+ corresponding storage is a part of a transaction.
+ */
+ handlerton *m_ht;
+ /**
+ Transaction flags related to this engine.
+ Not-null only if this instance is a part of transaction.
+ May assume a combination of enum values above.
+ */
+ uchar m_flags;
+};
+
enum enum_tx_isolation { ISO_READ_UNCOMMITTED, ISO_READ_COMMITTED,
ISO_REPEATABLE_READ, ISO_SERIALIZABLE};
+
+enum ndb_distribution { ND_KEYHASH= 0, ND_LINHASH= 1 };
+
+
+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 UNDEF_NODEGROUP 65535
+class Item;
+struct st_table_log_memory_entry;
+
+class partition_info;
+
+struct st_partition_iter;
+#define NOT_A_PARTITION_ID ((uint32)-1)
+
+enum ha_choice { HA_CHOICE_UNDEF, HA_CHOICE_NO, HA_CHOICE_YES };
+
typedef struct st_ha_create_information
{
CHARSET_INFO *table_charset, *default_table_charset;
LEX_STRING connect_string;
+ const char *password, *tablespace;
LEX_STRING comment;
- const char *password;
const char *data_file_name, *index_file_name;
const char *alias;
ulonglong max_rows,min_rows;
ulonglong auto_increment_value;
ulong table_options;
ulong avg_row_length;
- ulong raid_chunksize;
ulong used_fields;
+ ulong key_block_size;
SQL_LIST merge_list;
- enum db_type db_type;
+ handlerton *db_type;
enum row_type row_type;
uint null_bits; /* NULL bits at start of record */
uint options; /* OR of HA_CREATE_ options */
- uint raid_type,raid_chunks;
uint merge_insert_method;
uint extra_size; /* length of extra data segment */
+ /* 0 not used, 1 if not transactional, 2 if transactional */
+ 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 */
+ enum ha_choice page_checksum; /* If we have page_checksums */
} HA_CREATE_INFO;
-/* The handler for a table type. Will be included in the TABLE structure */
+typedef struct st_key_create_information
+{
+ enum ha_key_alg algorithm;
+ ulong block_size;
+ LEX_STRING parser_name;
+} KEY_CREATE_INFO;
-struct st_table;
-typedef struct st_table TABLE;
-struct st_foreign_key_info;
-typedef struct st_foreign_key_info FOREIGN_KEY_INFO;
+
+/*
+ Class for maintaining hooks used inside operations on tables such
+ as: create table functions, delete table functions, and alter table
+ functions.
+
+ Class is using the Template Method pattern to separate the public
+ usage interface from the private inheritance interface. This
+ imposes no overhead, since the public non-virtual function is small
+ enough to be inlined.
+
+ The hooks are usually used for functions that does several things,
+ e.g., create_table_from_items(), which both create a table and lock
+ it.
+ */
+class TABLEOP_HOOKS
+{
+public:
+ TABLEOP_HOOKS() {}
+ virtual ~TABLEOP_HOOKS() {}
+
+ inline void prelock(TABLE **tables, uint count)
+ {
+ do_prelock(tables, count);
+ }
+
+ inline int postlock(TABLE **tables, uint count)
+ {
+ return do_postlock(tables, count);
+ }
+private:
+ /* Function primitive that is called prior to locking tables */
+ virtual void do_prelock(TABLE **tables, uint count)
+ {
+ /* Default is to do nothing */
+ }
+
+ /**
+ Primitive called after tables are locked.
+
+ If an error is returned, the tables will be unlocked and error
+ handling start.
+
+ @return Error code or zero.
+ */
+ virtual int do_postlock(TABLE **tables, uint count)
+ {
+ return 0; /* Default is to do nothing */
+ }
+};
typedef struct st_savepoint SAVEPOINT;
extern ulong savepoint_alloc_size;
+extern KEY_CREATE_INFO default_key_create_info;
/* Forward declaration for condition pushdown to storage engine */
typedef class Item COND;
@@ -506,6 +1000,7 @@ typedef struct st_ha_check_opt
} HA_CHECK_OPT;
+
/*
This is a buffer area that the handler can use to store rows.
'end_of_used_area' should be kept updated after calls to
@@ -515,154 +1010,165 @@ typedef struct st_ha_check_opt
typedef struct st_handler_buffer
{
- const byte *buffer; /* Buffer one can start using */
- const byte *buffer_end; /* End of buffer */
- byte *end_of_used_area; /* End of area that was used by handler */
+ const uchar *buffer; /* Buffer one can start using */
+ const uchar *buffer_end; /* End of buffer */
+ uchar *end_of_used_area; /* End of area that was used by handler */
} HANDLER_BUFFER;
+typedef struct system_status_var SSV;
-class handler :public Sql_alloc
+class ha_statistics
{
- protected:
- struct st_table *table; /* The table definition */
-
- virtual int index_init(uint idx) { active_index=idx; return 0; }
- virtual int index_end() { active_index=MAX_KEY; return 0; }
- /*
- rnd_init() can be called two times without rnd_end() in between
- (it only makes sense if scan=1).
- then the second call should prepare for the new table scan (e.g
- if rnd_init allocates the cursor, second call should position it
- to the start of the table, no need to deallocate and allocate it again
- */
- virtual int rnd_init(bool scan) =0;
- virtual int rnd_end() { return 0; }
- /**
- Is not invoked for non-transactional temporary tables.
-
- Tells the storage engine that we intend to read or write data
- from the table. This call is prefixed with a call to handler::store_lock()
- and is invoked only for those handler instances that stored the lock.
-
- Calls to rnd_init/index_init are prefixed with this call. When table
- IO is complete, we call external_lock(F_UNLCK).
- A storage engine writer should expect that each call to
- ::external_lock(F_[RD|WR]LOCK is followed by a call to
- ::external_lock(F_UNLCK). If it is not, it is a bug in MySQL.
-
- The name and signature originate from the first implementation
- in MyISAM, which would call fcntl to set/clear an advisory
- lock on the data file in this method.
-
- @param lock_type F_RDLCK, F_WRLCK, F_UNLCK
-
- @return non-0 in case of failure, 0 in case of success.
- When lock_type is F_UNLCK, the return value is ignored.
- */
- virtual int external_lock(THD *thd, int lock_type) { return 0; }
-
public:
- const handlerton *ht; /* storage engine of this handler */
- byte *ref; /* Pointer to current row */
- byte *dupp_ref; /* Pointer to dupp row */
ulonglong data_file_length; /* Length off data file */
ulonglong max_data_file_length; /* Length off data file */
ulonglong index_file_length;
ulonglong max_index_file_length;
ulonglong delete_length; /* Free bytes */
ulonglong auto_increment_value;
- ha_rows records; /* Records in table */
+ /*
+ The number of records in the table.
+ 0 - means the table has exactly 0 rows
+ other - if (table_flags() & HA_STATS_RECORDS_IS_EXACT)
+ the value is the exact number of records in the table
+ else
+ it is an estimate
+ */
+ ha_rows records;
ha_rows deleted; /* Deleted records */
- ulong raid_chunksize;
ulong mean_rec_length; /* physical reclength */
time_t create_time; /* When table was created */
time_t check_time;
time_t update_time;
+ uint block_size; /* index block size */
+
+ ha_statistics():
+ data_file_length(0), max_data_file_length(0),
+ index_file_length(0), delete_length(0), auto_increment_value(0),
+ records(0), deleted(0), mean_rec_length(0), create_time(0),
+ check_time(0), update_time(0), block_size(0)
+ {}
+};
- /* The following are for read_multi_range */
+uint calculate_key_len(TABLE *, uint, const uchar *, key_part_map);
+/*
+ bitmap with first N+1 bits set
+ (keypart_map for a key prefix of [0..N] keyparts)
+*/
+#define make_keypart_map(N) (((key_part_map)2 << (N)) - 1)
+/*
+ bitmap with first N bits set
+ (keypart_map for a key prefix of [0..N-1] keyparts)
+*/
+#define make_prev_keypart_map(N) (((key_part_map)1 << (N)) - 1)
+
+/**
+ The handler class is the interface for dynamically loadable
+ storage engines. Do not add ifdefs and take care when adding or
+ changing virtual functions to avoid vtable confusion
+*/
+
+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_flags cached_table_flags; /* Set on init() and open() */
+
+ ha_rows estimation_rows_to_insert;
+public:
+ handlerton *ht; /* storage engine of this handler */
+ uchar *ref; /* Pointer to current row */
+ uchar *dup_ref; /* Pointer to duplicate row */
+
+ ha_statistics stats;
+
+ /** The following are for read_multi_range */
bool multi_range_sorted;
KEY_MULTI_RANGE *multi_range_curr;
KEY_MULTI_RANGE *multi_range_end;
HANDLER_BUFFER *multi_range_buffer;
- /* The following are for read_range() */
+ /** The following are for read_range() */
key_range save_end_range, *end_range;
KEY_PART_INFO *range_key_part;
int key_compare_result_on_equal;
bool eq_range;
uint errkey; /* Last dup key */
- uint sortkey, key_used_on_scan;
+ uint key_used_on_scan;
uint active_index;
- /* Length of ref (1-8 or the clustered key length) */
+ /** Length of ref (1-8 or the clustered key length) */
uint ref_length;
- uint block_size; /* index block size */
- uint raid_type,raid_chunks;
FT_INFO *ft_handler;
enum {NONE=0, INDEX, RND} inited;
bool locked;
- bool auto_increment_column_changed;
bool implicit_emptied; /* Can be !=0 only if HEAP */
const COND *pushed_cond;
-
- handler(const handlerton *ht_arg, TABLE *table_arg) :table(table_arg),
- ht(ht_arg),
- ref(0), data_file_length(0), max_data_file_length(0), index_file_length(0),
- delete_length(0), auto_increment_value(0),
- records(0), deleted(0), mean_rec_length(0),
- create_time(0), check_time(0), update_time(0),
- key_used_on_scan(MAX_KEY), active_index(MAX_KEY),
- ref_length(sizeof(my_off_t)), block_size(0),
- raid_type(0), ft_handler(0), inited(NONE),
+ /**
+ next_insert_id is the next value which should be inserted into the
+ auto_increment column: in a inserting-multi-row statement (like INSERT
+ SELECT), for the first row where the autoinc value is not specified by the
+ statement, get_auto_increment() called and asked to generate a value,
+ next_insert_id is set to the next value, then for all other rows
+ next_insert_id is used (and increased each time) without calling
+ get_auto_increment().
+ */
+ ulonglong next_insert_id;
+ /**
+ insert id for the current row (*autogenerated*; if not
+ autogenerated, it's 0).
+ At first successful insertion, this variable is stored into
+ THD::first_successful_insert_id_in_cur_stmt.
+ */
+ ulonglong insert_id_for_cur_row;
+ /**
+ Interval returned by get_auto_increment() and being consumed by the
+ inserter.
+ */
+ Discrete_interval auto_inc_interval_for_cur_row;
+ /**
+ Number of reserved auto-increment intervals. Serves as a heuristic
+ when we have no estimation of how many records the statement will insert:
+ the more intervals we have reserved, the bigger the next one. Reset in
+ handler::ha_release_auto_increment().
+ */
+ uint auto_inc_intervals_count;
+
+ handler(handlerton *ht_arg, TABLE_SHARE *share_arg)
+ :table_share(share_arg), table(0),
+ estimation_rows_to_insert(0), ht(ht_arg),
+ ref(0), key_used_on_scan(MAX_KEY), active_index(MAX_KEY),
+ ref_length(sizeof(my_off_t)),
+ ft_handler(0), inited(NONE),
locked(FALSE), implicit_emptied(0),
- pushed_cond(NULL)
+ pushed_cond(0), next_insert_id(0), insert_id_for_cur_row(0),
+ auto_inc_intervals_count(0)
{}
- virtual ~handler(void) { DBUG_ASSERT(locked == FALSE); /* TODO: DBUG_ASSERT(inited == NONE); */ }
+ virtual ~handler(void)
+ {
+ DBUG_ASSERT(locked == FALSE);
+ /* TODO: DBUG_ASSERT(inited == NONE); */
+ }
virtual handler *clone(MEM_ROOT *mem_root);
- int ha_open(const char *name, int mode, int test_if_locked);
- void adjust_next_insert_id_after_explicit_value(ulonglong nr);
- int update_auto_increment();
- virtual void print_error(int error, myf errflag);
- virtual bool get_error_message(int error, String *buf);
- uint get_dup_key(int error);
- void change_table_ptr(TABLE *table_arg) { table=table_arg; }
- virtual double scan_time()
- { return ulonglong2double(data_file_length) / IO_SIZE + 2; }
- virtual double read_time(uint index, uint ranges, ha_rows rows)
- { return rows2double(ranges+rows); }
- virtual const key_map *keys_to_use_for_scanning() { return &key_map_empty; }
- virtual bool has_transactions(){ return 0;}
- virtual uint extra_rec_buf_length() { return 0; }
-
- /*
- Return upper bound of current number of records in the table
- (max. of how many records one will retrieve when doing a full table scan)
- If upper bound is not known, HA_POS_ERROR should be returned as a max
- possible upper bound.
- */
- virtual ha_rows estimate_rows_upper_bound()
- { return records+EXTRA_RECORDS; }
-
- /*
- Get the row type from the storage engine. If this method returns
- ROW_TYPE_NOT_USED, the information in HA_CREATE_INFO should be used.
- */
- virtual enum row_type get_row_type() const { return ROW_TYPE_NOT_USED; }
-
- virtual const char *index_type(uint key_number) { DBUG_ASSERT(0); return "";}
-
- int ha_external_lock(THD *thd, int lock_type)
+ /** This is called after create to allow us to set up cached variables */
+ void init()
{
- DBUG_ENTER("ha_external_lock");
- locked= lock_type != F_UNLCK;
- DBUG_RETURN(external_lock(thd, lock_type));
+ cached_table_flags= table_flags();
}
- int ha_index_init(uint idx)
+ /* ha_ methods: pubilc wrappers for private virtual API */
+
+ int ha_open(TABLE *table, const char *name, int mode, int test_if_locked);
+ int ha_index_init(uint idx, bool sorted)
{
+ int result;
DBUG_ENTER("ha_index_init");
DBUG_ASSERT(inited==NONE);
- inited=INDEX;
- DBUG_RETURN(index_init(idx));
+ if (!(result= index_init(idx, sorted)))
+ inited=INDEX;
+ DBUG_RETURN(result);
}
int ha_index_end()
{
@@ -673,10 +1179,11 @@ public:
}
int ha_rnd_init(bool scan)
{
+ int result;
DBUG_ENTER("ha_rnd_init");
DBUG_ASSERT(inited==NONE || (inited==RND && scan));
- inited=RND;
- DBUG_RETURN(rnd_init(scan));
+ inited= (result= rnd_init(scan)) ? NONE: RND;
+ DBUG_RETURN(result);
}
int ha_rnd_end()
{
@@ -685,35 +1192,232 @@ public:
inited=NONE;
DBUG_RETURN(rnd_end());
}
+ int ha_reset();
/* this is necessary in many places, e.g. in HANDLER command */
int ha_index_or_rnd_end()
{
return inited == INDEX ? ha_index_end() : inited == RND ? ha_rnd_end() : 0;
}
+ /**
+ The cached_table_flags is set at ha_open and ha_external_lock
+ */
+ Table_flags ha_table_flags() const { return cached_table_flags; }
+ /**
+ These functions represent the public interface to *users* of the
+ handler class, hence they are *not* virtual. For the inheritance
+ interface, see the (private) functions write_row(), update_row(),
+ and delete_row() below.
+ */
+ int ha_external_lock(THD *thd, int lock_type);
+ int ha_write_row(uchar * buf);
+ int ha_update_row(const uchar * old_data, uchar * new_data);
+ int ha_delete_row(const uchar * buf);
+ void ha_release_auto_increment();
+
+ int check_collation_compatibility();
+ int ha_check_for_upgrade(HA_CHECK_OPT *check_opt);
+ /** to be actually called to get 'check()' functionality*/
+ int ha_check(THD *thd, HA_CHECK_OPT *check_opt);
+ int ha_repair(THD* thd, HA_CHECK_OPT* check_opt);
+ void ha_start_bulk_insert(ha_rows rows)
+ {
+ estimation_rows_to_insert= rows;
+ start_bulk_insert(rows);
+ }
+ int ha_end_bulk_insert()
+ {
+ estimation_rows_to_insert= 0;
+ return end_bulk_insert();
+ }
+ int ha_bulk_update_row(const uchar *old_data, uchar *new_data,
+ uint *dup_key_found);
+ int ha_delete_all_rows();
+ 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);
+ int ha_disable_indexes(uint mode);
+ int ha_enable_indexes(uint mode);
+ int ha_discard_or_import_tablespace(my_bool discard);
+ void ha_prepare_for_alter();
+ int ha_rename_table(const char *from, const char *to);
+ int ha_delete_table(const char *name);
+ void ha_drop_table(const char *name);
+
+ int ha_create(const char *name, TABLE *form, HA_CREATE_INFO *info);
+
+ int ha_create_handler_files(const char *name, const char *old_name,
+ int action_flag, HA_CREATE_INFO *info);
+
+ int ha_change_partitions(HA_CREATE_INFO *create_info,
+ const char *path,
+ ulonglong * const copied,
+ ulonglong * const deleted,
+ const uchar *pack_frm_data,
+ size_t pack_frm_len);
+ int ha_drop_partitions(const char *path);
+ int ha_rename_partitions(const char *path);
+
+ void adjust_next_insert_id_after_explicit_value(ulonglong nr);
+ int update_auto_increment();
+ void print_keydup_error(uint key_nr, const char *msg);
+ virtual void print_error(int error, myf errflag);
+ virtual bool get_error_message(int error, String *buf);
+ uint get_dup_key(int error);
+ virtual void change_table_ptr(TABLE *table_arg, TABLE_SHARE *share)
+ {
+ table= table_arg;
+ table_share= share;
+ }
+ virtual double scan_time()
+ { return ulonglong2double(stats.data_file_length) / IO_SIZE + 2; }
+ virtual double read_time(uint index, uint ranges, ha_rows rows)
+ { return rows2double(ranges+rows); }
+ virtual const key_map *keys_to_use_for_scanning() { return &key_map_empty; }
+ bool has_transactions()
+ { return (ha_table_flags() & HA_NO_TRANSACTIONS) == 0; }
+ virtual uint extra_rec_buf_length() const { return 0; }
+
+ /**
+ This method is used to analyse the error to see whether the error
+ is ignorable or not, certain handlers can have more error that are
+ ignorable than others. E.g. the partition handler can get inserts
+ into a range where there is no partition and this is an ignorable
+ error.
+ HA_ERR_FOUND_DUP_UNIQUE is a special case in MyISAM that means the
+ same thing as HA_ERR_FOUND_DUP_KEY but can in some cases lead to
+ a slightly different error message.
+ */
+ virtual bool is_fatal_error(int error, uint flags)
+ {
+ if (!error ||
+ ((flags & HA_CHECK_DUP_KEY) &&
+ (error == HA_ERR_FOUND_DUPP_KEY ||
+ error == HA_ERR_FOUND_DUPP_UNIQUE)))
+ return FALSE;
+ return TRUE;
+ }
+
+ /**
+ Number of rows in table. It will only be called if
+ (table_flags() & (HA_HAS_RECORDS | HA_STATS_RECORDS_IS_EXACT)) != 0
+ */
+ virtual ha_rows records() { return stats.records; }
+ /**
+ Return upper bound of current number of records in the table
+ (max. of how many records one will retrieve when doing a full table scan)
+ If upper bound is not known, HA_POS_ERROR should be returned as a max
+ possible upper bound.
+ */
+ virtual ha_rows estimate_rows_upper_bound()
+ { return stats.records+EXTRA_RECORDS; }
+
+ /**
+ Get the row type from the storage engine. If this method returns
+ ROW_TYPE_NOT_USED, the information in HA_CREATE_INFO should be used.
+ */
+ virtual enum row_type get_row_type() const { return ROW_TYPE_NOT_USED; }
+
+ virtual const char *index_type(uint key_number) { DBUG_ASSERT(0); return "";}
+
+
+ /**
+ Signal that the table->read_set and table->write_set table maps changed
+ The handler is allowed to set additional bits in the above map in this
+ call. Normally the handler should ignore all calls until we have done
+ a ha_rnd_init() or ha_index_init(), write_row(), update_row or delete_row()
+ as there may be several calls to this routine.
+ */
+ virtual void column_bitmaps_signal();
uint get_index(void) const { return active_index; }
- virtual int open(const char *name, int mode, uint test_if_locked)=0;
virtual int close(void)=0;
- virtual int write_row(byte * buf) { return HA_ERR_WRONG_COMMAND; }
- virtual int update_row(const byte * old_data, byte * new_data)
- { return HA_ERR_WRONG_COMMAND; }
- virtual int delete_row(const byte * buf)
- { return HA_ERR_WRONG_COMMAND; }
- virtual int index_read(byte * buf, const byte * key,
- uint key_len, enum ha_rkey_function find_flag)
- { return HA_ERR_WRONG_COMMAND; }
- virtual int index_read_idx(byte * buf, uint index, const byte * key,
- uint key_len, enum ha_rkey_function find_flag);
- virtual int index_next(byte * buf)
+
+ /**
+ @retval 0 Bulk update used by handler
+ @retval 1 Bulk update not used, normal operation used
+ */
+ virtual bool start_bulk_update() { return 1; }
+ /**
+ @retval 0 Bulk delete used by handler
+ @retval 1 Bulk delete not used, normal operation used
+ */
+ virtual bool start_bulk_delete() { return 1; }
+ /**
+ After this call all outstanding updates must be performed. The number
+ of duplicate key errors are reported in the duplicate key parameter.
+ It is allowed to continue to the batched update after this call, the
+ handler has to wait until end_bulk_update with changing state.
+
+ @param dup_key_found Number of duplicate keys found
+
+ @retval 0 Success
+ @retval >0 Error code
+ */
+ virtual int exec_bulk_update(uint *dup_key_found)
+ {
+ DBUG_ASSERT(FALSE);
+ return HA_ERR_WRONG_COMMAND;
+ }
+ /**
+ Perform any needed clean-up, no outstanding updates are there at the
+ moment.
+ */
+ virtual void end_bulk_update() { return; }
+ /**
+ Execute all outstanding deletes and close down the bulk delete.
+
+ @retval 0 Success
+ @retval >0 Error code
+ */
+ virtual int end_bulk_delete()
+ {
+ DBUG_ASSERT(FALSE);
+ return HA_ERR_WRONG_COMMAND;
+ }
+ /**
+ @brief
+ Positions an index cursor to the index specified in the handle. Fetches the
+ row if available. If the key value is null, begin at the first key of the
+ index.
+ */
+ virtual int index_read_map(uchar * buf, const uchar * key,
+ key_part_map keypart_map,
+ enum ha_rkey_function find_flag)
+ {
+ uint key_len= calculate_key_len(table, active_index, key, keypart_map);
+ return index_read(buf, key, key_len, find_flag);
+ }
+ /**
+ @brief
+ Positions an index cursor to the index specified in the handle. Fetches the
+ row if available. If the key value is null, begin at the first key of the
+ index.
+ */
+ virtual int index_read_idx_map(uchar * buf, uint index, const uchar * key,
+ key_part_map keypart_map,
+ enum ha_rkey_function find_flag);
+ virtual int index_next(uchar * buf)
{ return HA_ERR_WRONG_COMMAND; }
- virtual int index_prev(byte * buf)
+ virtual int index_prev(uchar * buf)
{ return HA_ERR_WRONG_COMMAND; }
- virtual int index_first(byte * buf)
+ virtual int index_first(uchar * buf)
{ return HA_ERR_WRONG_COMMAND; }
- virtual int index_last(byte * buf)
+ virtual int index_last(uchar * buf)
{ return HA_ERR_WRONG_COMMAND; }
- virtual int index_next_same(byte *buf, const byte *key, uint keylen);
- virtual int index_read_last(byte * buf, const byte * key, uint key_len)
- { return (my_errno=HA_ERR_WRONG_COMMAND); }
+ virtual int index_next_same(uchar *buf, const uchar *key, uint keylen);
+ /**
+ @brief
+ The following functions works like index_read, but it find the last
+ row with the current key value or prefix.
+ */
+ virtual int index_read_last_map(uchar * buf, const uchar * key,
+ key_part_map keypart_map)
+ {
+ uint key_len= calculate_key_len(table, active_index, key, keypart_map);
+ return index_read_last(buf, key, key_len);
+ }
virtual int read_multi_range_first(KEY_MULTI_RANGE **found_range_p,
KEY_MULTI_RANGE *ranges, uint range_count,
bool sorted, HANDLER_BUFFER *buffer);
@@ -727,122 +1431,129 @@ public:
void ft_end() { ft_handler=NULL; }
virtual FT_INFO *ft_init_ext(uint flags, uint inx,String *key)
{ return NULL; }
- virtual int ft_read(byte *buf) { return HA_ERR_WRONG_COMMAND; }
- virtual int rnd_next(byte *buf)=0;
- virtual int rnd_pos(byte * buf, byte *pos)=0;
- virtual int read_first_row(byte *buf, uint primary_key);
- /*
+ virtual int ft_read(uchar *buf) { return HA_ERR_WRONG_COMMAND; }
+ virtual int rnd_next(uchar *buf)=0;
+ virtual int rnd_pos(uchar * buf, uchar *pos)=0;
+ /**
+ One has to use this method when to find
+ random position by record as the plain
+ position() call doesn't work for some
+ handlers for random position.
+ */
+ virtual int rnd_pos_by_record(uchar *record)
+ {
+ position(record);
+ return rnd_pos(record, ref);
+ }
+ virtual int read_first_row(uchar *buf, uint primary_key);
+ /**
The following function is only needed for tables that may be temporary
- tables during joins
+ tables during joins.
*/
- virtual int restart_rnd_next(byte *buf, byte *pos)
+ virtual int restart_rnd_next(uchar *buf, uchar *pos)
{ return HA_ERR_WRONG_COMMAND; }
- virtual int rnd_same(byte *buf, uint inx)
+ virtual int rnd_same(uchar *buf, uint inx)
{ return HA_ERR_WRONG_COMMAND; }
- virtual ha_rows records_in_range(uint inx, key_range *min_key,
- key_range *max_key)
+ virtual ha_rows records_in_range(uint inx, key_range *min_key, key_range *max_key)
{ return (ha_rows) 10; }
- virtual void position(const byte *record)=0;
+ 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,
+ uint part_id);
virtual int extra(enum ha_extra_function operation)
{ return 0; }
virtual int extra_opt(enum ha_extra_function operation, ulong cache_size)
{ return extra(operation); }
- virtual int reset() { return extra(HA_EXTRA_RESET); }
- virtual void unlock_row() {}
- virtual int start_stmt(THD *thd, thr_lock_type lock_type) {return 0;}
- /*
- 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.
- */
- virtual int delete_all_rows()
- { return (my_errno=HA_ERR_WRONG_COMMAND); }
- virtual ulonglong get_auto_increment();
- virtual void restore_auto_increment();
- /*
- 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.
+ /**
+ In an UPDATE or DELETE, if the row under the cursor was locked by another
+ transaction, and the engine used an optimistic read of the last
+ committed row value under the cursor, then the engine returns 1 from this
+ function. MySQL must NOT try to update this optimistic value. If the
+ optimistic value does not match the WHERE condition, MySQL can decide to
+ skip over this row. Currently only works for InnoDB. This can be used to
+ avoid unnecessary lock waits.
+
+ If this method returns nonzero, it will also signal the storage
+ engine that the next read will be a locking re-read of the row.
*/
- virtual int reset_auto_increment(ulonglong value)
- { return HA_ERR_WRONG_COMMAND; }
+ virtual bool was_semi_consistent_read() { return 0; }
+ /**
+ Tell the engine whether it should avoid unnecessary lock waits.
+ If yes, in an UPDATE or DELETE, if the row under the cursor was locked
+ by another transaction, the engine may try an optimistic read of
+ the last committed row value under the cursor.
+ */
+ virtual void try_semi_consistent_read(bool) {}
+ virtual void unlock_row() {}
+ virtual int start_stmt(THD *thd, thr_lock_type lock_type) {return 0;}
+ virtual void get_auto_increment(ulonglong offset, ulonglong increment,
+ ulonglong nb_desired_values,
+ ulonglong *first_value,
+ ulonglong *nb_reserved_values);
+ void set_next_insert_id(ulonglong id)
+ {
+ DBUG_PRINT("info",("auto_increment: next value %lu", (ulong)id));
+ next_insert_id= id;
+ }
+ void restore_auto_increment(ulonglong prev_insert_id)
+ {
+ /*
+ Insertion of a row failed, re-use the lastly generated auto_increment
+ id, for the next row. This is achieved by resetting next_insert_id to
+ what it was before the failed insertion (that old value is provided by
+ the caller). If that value was 0, it was the first row of the INSERT;
+ then if insert_id_for_cur_row contains 0 it means no id was generated
+ for this first row, so no id was generated since the INSERT started, so
+ we should set next_insert_id to 0; if insert_id_for_cur_row is not 0, it
+ is the generated id of the first and failed row, so we use it.
+ */
+ next_insert_id= (prev_insert_id > 0) ? prev_insert_id :
+ insert_id_for_cur_row;
+ }
virtual void update_create_info(HA_CREATE_INFO *create_info) {}
-protected:
- /* to be implemented in handlers */
-
- /* admin commands - called from mysql_admin_table */
- virtual int check(THD* thd, HA_CHECK_OPT* check_opt)
- { return HA_ADMIN_NOT_IMPLEMENTED; }
-
- /*
- in these two methods check_opt can be modified
- to specify CHECK option to use to call check()
- upon the table
- */
- virtual int check_for_upgrade(HA_CHECK_OPT *check_opt)
- { return 0; }
-public:
- int check_collation_compatibility();
- int ha_check_for_upgrade(HA_CHECK_OPT *check_opt);
int check_old_types();
- /* to be actually called to get 'check()' functionality*/
- int ha_check(THD *thd, HA_CHECK_OPT *check_opt);
-
- virtual int backup(THD* thd, HA_CHECK_OPT* check_opt)
- { return HA_ADMIN_NOT_IMPLEMENTED; }
- /*
- restore assumes .frm file must exist, and that generate_table() has been
- called; It will just copy the data file and run repair.
- */
- virtual int restore(THD* thd, HA_CHECK_OPT* check_opt)
- { return HA_ADMIN_NOT_IMPLEMENTED; }
-protected:
- virtual int repair(THD* thd, HA_CHECK_OPT* check_opt)
- { return HA_ADMIN_NOT_IMPLEMENTED; }
-public:
- int ha_repair(THD* thd, HA_CHECK_OPT* check_opt);
- 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)
- { return HA_ADMIN_NOT_IMPLEMENTED; }
virtual int assign_to_keycache(THD* thd, HA_CHECK_OPT* check_opt)
{ return HA_ADMIN_NOT_IMPLEMENTED; }
virtual int preload_keys(THD* thd, HA_CHECK_OPT* check_opt)
{ return HA_ADMIN_NOT_IMPLEMENTED; }
/* end of the list of admin commands */
- virtual bool check_and_repair(THD *thd) { return TRUE; }
virtual int dump(THD* thd, int fd = -1) { return HA_ERR_WRONG_COMMAND; }
- virtual int disable_indexes(uint mode) { return HA_ERR_WRONG_COMMAND; }
- virtual int enable_indexes(uint mode) { return HA_ERR_WRONG_COMMAND; }
virtual int indexes_are_disabled(void) {return 0;}
- virtual void start_bulk_insert(ha_rows rows) {}
- virtual int end_bulk_insert() {return 0; }
- virtual int discard_or_import_tablespace(my_bool discard)
- {return HA_ERR_WRONG_COMMAND;}
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) {}
+ /**
+ If index == MAX_KEY then a check for table is made and if index <
+ MAX_KEY then a check is made if the table has foreign keys and if
+ a foreign key uses this index (and thus the index cannot be dropped).
+
+ @param index Index to check if foreign key uses it
+
+ @retval TRUE Foreign key defined on table or index
+ @retval FALSE No foreign key defined
+ */
+ virtual bool is_fk_defined_on_table_or_index(uint index)
+ { return FALSE; }
virtual char* get_foreign_key_create_info()
{ return(NULL);} /* gets foreign key create string from InnoDB */
- /* used in ALTER TABLE; 1 if changing storage engine is allowed */
+ virtual char* get_tablespace_name(THD *thd, char *name, uint name_len)
+ { return(NULL);} /* gets tablespace name from handler */
+ /** used in ALTER TABLE; 1 if changing storage engine is allowed */
virtual bool can_switch_engines() { return 1; }
- /* used in REPLACE; is > 0 if table is referred by a FOREIGN KEY */
+ /** 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)
{ return 0; }
virtual uint referenced_by_foreign_key() { return 0;}
virtual void init_table_handle_for_HANDLER()
{ return; } /* prepare InnoDB for HANDLER */
virtual void free_foreign_key_create_info(char* str) {}
- /* The following can be called without an open handler */
+ /** The following can be called without an open handler */
virtual const char *table_type() const =0;
- /*
+ /**
If frm_error() is called then we will use this to find out what file
extentions exist for the storage engine. This is also used by the default
rename_table and delete_table method in handler.cc.
@@ -854,13 +1565,25 @@ public:
prepare_for_repair() when REPAIR TABLE ... USE_FRM is issued.
*/
virtual const char **bas_ext() const =0;
- virtual ulong table_flags(void) const =0;
+
+ virtual int get_default_no_partitions(HA_CREATE_INFO *info) { return 1;}
+ virtual void set_auto_partitions(partition_info *part_info) { return; }
+ virtual bool get_no_parts(const char *name,
+ uint *no_parts)
+ {
+ *no_parts= 0;
+ return 0;
+ }
+ virtual void set_part_info(partition_info *part_info) {return;}
+
virtual ulong index_flags(uint idx, uint part, bool all_parts) const =0;
- virtual ulong index_ddl_flags(KEY *wanted_index) const
- { return (HA_DDL_SUPPORT); }
+
virtual int add_index(TABLE *table_arg, KEY *key_info, uint num_of_keys)
{ return (HA_ERR_WRONG_COMMAND); }
- virtual int drop_index(TABLE *table_arg, uint *key_num, uint num_of_keys)
+ virtual int prepare_drop_index(TABLE *table_arg, uint *key_num,
+ uint num_of_keys)
+ { return (HA_ERR_WRONG_COMMAND); }
+ virtual int final_drop_index(TABLE *table_arg)
{ return (HA_ERR_WRONG_COMMAND); }
uint max_record_length() const
@@ -886,25 +1609,35 @@ public:
virtual bool is_crashed() const { return 0; }
virtual bool auto_repair() const { return 0; }
- /*
- default rename_table() and delete_table() rename/delete files with a
- given name and extensions from bas_ext()
- */
- virtual int rename_table(const char *from, const char *to);
- virtual int delete_table(const char *name);
- virtual int create(const char *name, TABLE *form, HA_CREATE_INFO *info)=0;
+#define CHF_CREATE_FLAG 0
+#define CHF_DELETE_FLAG 1
+#define CHF_RENAME_FLAG 2
+#define CHF_INDEX_FLAG 3
- /* lock_count() can be more than one if the table is a MERGE */
+
+ /**
+ @note lock_count() can return > 1 if the table is MERGE or partitioned.
+ */
virtual uint lock_count(void) const { return 1; }
/**
Is not invoked for non-transactional temporary tables.
+
+ @note store_lock() can return more than one lock if the table is MERGE
+ or partitioned.
+
+ @note that one can NOT rely on table->in_use in store_lock(). It may
+ refer to a different thread if called from mysql_lock_abort_for_thread().
+
+ @note If the table is MERGE, store_lock() can return less locks
+ than lock_count() claimed. This can happen when the MERGE children
+ are not attached when this is called from another thread.
*/
virtual THR_LOCK_DATA **store_lock(THD *thd,
THR_LOCK_DATA **to,
enum thr_lock_type lock_type)=0;
- /* Type of table for caching query */
+ /** Type of table for caching query */
virtual uint8 table_cache_type() { return HA_CACHE_TBL_NONTRANSACT; }
@@ -950,13 +1683,12 @@ public:
/*
- RETURN
- true Primary key (if there is one) is clustered key covering all fields
- false otherwise
+ @retval TRUE Primary key (if there is one) is clustered
+ key covering all fields
+ @retval FALSE otherwise
*/
virtual bool primary_key_is_clustered() { return FALSE; }
-
- virtual int cmp_ref(const byte *ref1, const byte *ref2)
+ virtual int cmp_ref(const uchar *ref1, const uchar *ref2)
{
return memcmp(ref1, ref2, ref_length);
}
@@ -965,83 +1697,315 @@ public:
Condition pushdown to storage engines
*/
- /*
+ /**
Push condition down to the table handler.
- SYNOPSIS
- cond_push()
- cond Condition to be pushed. The condition tree must not be
- modified by the by the caller.
- RETURN
+
+ @param cond Condition to be pushed. The condition tree must not be
+ modified by the by the caller.
+
+ @return
The 'remainder' condition that caller must use to filter out records.
NULL means the handler will not return rows that do not match the
passed condition.
- NOTES
+
+ @note
The pushed conditions form a stack (from which one can remove the
last pushed condition using cond_pop).
- The table handler filters out rows using (pushed_cond1 AND pushed_cond2
+ The table handler filters out rows using (pushed_cond1 AND pushed_cond2
AND ... AND pushed_condN)
or less restrictive condition, depending on handler's capabilities.
- handler->extra(HA_EXTRA_RESET) call empties the condition stack.
+ handler->ha_reset() call empties the condition stack.
Calls to rnd_init/rnd_end, index_init/index_end etc do not affect the
condition stack.
- */
+ */
virtual const COND *cond_push(const COND *cond) { return cond; };
- /*
+ /**
Pop the top condition from the condition stack of the handler instance.
- SYNOPSIS
- cond_pop()
- Pops the top if condition stack, if stack is not empty
+
+ Pops the top if condition stack, if stack is not empty.
*/
virtual void cond_pop() { return; };
+ virtual bool check_if_incompatible_data(HA_CREATE_INFO *create_info,
+ uint table_changes)
+ { return COMPATIBLE_DATA_NO; }
+
+ /**
+ use_hidden_primary_key() is called in case of an update/delete when
+ (table_flags() and HA_PRIMARY_KEY_REQUIRED_FOR_DELETE) is defined
+ but we don't have a primary key
+ */
+ virtual void use_hidden_primary_key();
+ virtual uint alter_table_flags(uint flags)
+ {
+ if (ht->alter_table_flags)
+ return ht->alter_table_flags(flags);
+ return 0;
+ }
+
+protected:
+ /* Service methods for use by storage engines. */
+ void ha_statistic_increment(ulong SSV::*offset) const;
+ void **ha_data(THD *) const;
+ THD *ha_thd(void) const;
+
+ /**
+ Default rename_table() and delete_table() rename/delete files with a
+ given name and extensions from bas_ext().
+
+ These methods can be overridden, but their default implementation
+ provide useful functionality.
+ */
+ virtual int rename_table(const char *from, const char *to);
+ /**
+ Delete a table in the engine. Called for base as well as temporary
+ tables.
+ */
+ virtual int delete_table(const char *name);
+private:
+ /* Private helpers */
+ inline void mark_trx_read_write();
+private:
+ /*
+ Low-level primitives for storage engines. These should be
+ overridden by the storage engine class. To call these methods, use
+ the corresponding 'ha_*' method above.
+ */
+
+ virtual int open(const char *name, int mode, uint test_if_locked)=0;
+ virtual int index_init(uint idx, bool sorted) { active_index= idx; return 0; }
+ virtual int index_end() { active_index= MAX_KEY; return 0; }
+ /**
+ rnd_init() can be called two times without rnd_end() in between
+ (it only makes sense if scan=1).
+ then the second call should prepare for the new table scan (e.g
+ if rnd_init allocates the cursor, second call should position it
+ to the start of the table, no need to deallocate and allocate it again
+ */
+ virtual int rnd_init(bool scan)= 0;
+ virtual int rnd_end() { return 0; }
+ virtual int write_row(uchar *buf __attribute__((unused)))
+ {
+ return HA_ERR_WRONG_COMMAND;
+ }
+
+ virtual int update_row(const uchar *old_data __attribute__((unused)),
+ uchar *new_data __attribute__((unused)))
+ {
+ return HA_ERR_WRONG_COMMAND;
+ }
+
+ virtual int delete_row(const uchar *buf __attribute__((unused)))
+ {
+ return HA_ERR_WRONG_COMMAND;
+ }
+ /**
+ Reset state of file to after 'open'.
+ This function is called after every statement for all tables used
+ by that statement.
+ */
+ virtual int reset() { return 0; }
+ virtual Table_flags table_flags(void) const= 0;
+ /**
+ Is not invoked for non-transactional temporary tables.
+
+ Tells the storage engine that we intend to read or write data
+ from the table. This call is prefixed with a call to handler::store_lock()
+ and is invoked only for those handler instances that stored the lock.
+
+ Calls to rnd_init/index_init are prefixed with this call. When table
+ IO is complete, we call external_lock(F_UNLCK).
+ A storage engine writer should expect that each call to
+ ::external_lock(F_[RD|WR]LOCK is followed by a call to
+ ::external_lock(F_UNLCK). If it is not, it is a bug in MySQL.
+
+ The name and signature originate from the first implementation
+ in MyISAM, which would call fcntl to set/clear an advisory
+ lock on the data file in this method.
+
+ @param lock_type F_RDLCK, F_WRLCK, F_UNLCK
+
+ @return non-0 in case of failure, 0 in case of success.
+ When lock_type is F_UNLCK, the return value is ignored.
+ */
+ virtual int external_lock(THD *thd __attribute__((unused)),
+ int lock_type __attribute__((unused)))
+ {
+ return 0;
+ }
+ virtual void release_auto_increment() { return; };
+ /** admin commands - called from mysql_admin_table */
+ virtual int check_for_upgrade(HA_CHECK_OPT *check_opt)
+ { return 0; }
+ virtual int check(THD* thd, HA_CHECK_OPT* check_opt)
+ { return HA_ADMIN_NOT_IMPLEMENTED; }
+
+ /**
+ In this method check_opt can be modified
+ to specify CHECK option to use to call check()
+ upon the table.
+ */
+ virtual int repair(THD* thd, HA_CHECK_OPT* check_opt)
+ { 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,
+ enum ha_rkey_function find_flag)
+ { return HA_ERR_WRONG_COMMAND; }
+ virtual int index_read_last(uchar * buf, const uchar * key, uint key_len)
+ { return (my_errno= HA_ERR_WRONG_COMMAND); }
+ /**
+ This method is similar to update_row, however the handler doesn't need
+ to execute the updates at this point in time. The handler can be certain
+ that another call to bulk_update_row will occur OR a call to
+ exec_bulk_update before the set of updates in this query is concluded.
+
+ @param old_data Old record
+ @param new_data New record
+ @param dup_key_found Number of duplicate keys found
+
+ @retval 0 Bulk delete used by handler
+ @retval 1 Bulk delete not used, normal operation used
+ */
+ virtual int bulk_update_row(const uchar *old_data, uchar *new_data,
+ uint *dup_key_found)
+ {
+ DBUG_ASSERT(FALSE);
+ return HA_ERR_WRONG_COMMAND;
+ }
+ /**
+ 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.
+ */
+ 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.
+ */
+ 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; }
+ /**
+ Restore assumes .frm file must exist, and that generate_table() has been
+ called; It will just copy the data file and run repair.
+ */
+ virtual int restore(THD* thd, HA_CHECK_OPT* check_opt)
+ { return HA_ADMIN_NOT_IMPLEMENTED; }
+ 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)
+ { return HA_ADMIN_NOT_IMPLEMENTED; }
+ virtual bool check_and_repair(THD *thd) { return TRUE; }
+ virtual int disable_indexes(uint mode) { return HA_ERR_WRONG_COMMAND; }
+ virtual int enable_indexes(uint mode) { return HA_ERR_WRONG_COMMAND; }
+ virtual int discard_or_import_tablespace(my_bool discard)
+ { return (my_errno=HA_ERR_WRONG_COMMAND); }
+ virtual void prepare_for_alter() { return; }
+ virtual void drop_table(const char *name);
+ virtual int create(const char *name, TABLE *form, HA_CREATE_INFO *info)=0;
+
+ virtual int create_handler_files(const char *name, const char *old_name,
+ int action_flag, HA_CREATE_INFO *info)
+ { return FALSE; }
+
+ virtual int change_partitions(HA_CREATE_INFO *create_info,
+ const char *path,
+ ulonglong * const copied,
+ ulonglong * const deleted,
+ const uchar *pack_frm_data,
+ size_t pack_frm_len)
+ { return HA_ERR_WRONG_COMMAND; }
+ virtual int drop_partitions(const char *path)
+ { return HA_ERR_WRONG_COMMAND; }
+ virtual int rename_partitions(const char *path)
+ { return HA_ERR_WRONG_COMMAND; }
};
+
/* Some extern variables used with handlers */
-extern handlerton *sys_table_types[];
extern const char *ha_row_type[];
+extern const char *tx_isolation_names[];
+extern const char *binlog_format_names[];
extern TYPELIB tx_isolation_typelib;
extern TYPELIB myisam_stats_method_typelib;
extern ulong total_ha, total_ha_2pc;
- /* Wrapper functions */
-#define ha_commit_stmt(thd) (ha_commit_trans((thd), FALSE))
-#define ha_rollback_stmt(thd) (ha_rollback_trans((thd), FALSE))
+ /* Wrapper functions */
#define ha_commit(thd) (ha_commit_trans((thd), TRUE))
#define ha_rollback(thd) (ha_rollback_trans((thd), TRUE))
/* lookups */
-enum db_type ha_resolve_by_name(const char *name, uint namelen);
-const char *ha_get_storage_engine(enum db_type db_type);
-handler *get_new_handler(TABLE *table, MEM_ROOT *alloc, enum db_type db_type);
-enum db_type ha_checktype(THD *thd, enum db_type database_type,
+handlerton *ha_default_handlerton(THD *thd);
+plugin_ref ha_resolve_by_name(THD *thd, const LEX_STRING *name);
+plugin_ref ha_lock_engine(THD *thd, handlerton *hton);
+handlerton *ha_resolve_by_legacy_type(THD *thd, enum legacy_db_type db_type);
+handler *get_new_handler(TABLE_SHARE *share, MEM_ROOT *alloc,
+ handlerton *db_type);
+handlerton *ha_checktype(THD *thd, enum legacy_db_type database_type,
bool no_substitute, bool report_error);
-bool ha_check_storage_engine_flag(enum db_type db_type, uint32 flag);
+
+
+static inline enum legacy_db_type ha_legacy_type(const handlerton *db_type)
+{
+ return (db_type == NULL) ? DB_TYPE_UNKNOWN : db_type->db_type;
+}
+
+static inline const char *ha_resolve_storage_engine_name(const handlerton *db_type)
+{
+ return db_type == NULL ? "UNKNOWN" : hton2plugin[db_type->slot]->name.str;
+}
+
+static inline bool ha_check_storage_engine_flag(const handlerton *db_type, uint32 flag)
+{
+ return db_type == NULL ? FALSE : test(db_type->flags & flag);
+}
+
+static inline bool ha_storage_engine_is_enabled(const handlerton *db_type)
+{
+ return (db_type && db_type->create) ?
+ (db_type->state == SHOW_OPTION_YES) : FALSE;
+}
/* basic stuff */
+int ha_init_errors(void);
int ha_init(void);
+int ha_end(void);
+int ha_initialize_handlerton(st_plugin_int *plugin);
+int ha_finalize_handlerton(st_plugin_int *plugin);
+
TYPELIB *ha_known_exts(void);
int ha_panic(enum ha_panic_function flag);
-int ha_update_statistics();
void ha_close_connection(THD* thd);
-my_bool ha_storage_engine_is_enabled(enum db_type database_type);
-bool ha_flush_logs(void);
+bool ha_flush_logs(handlerton *db_type);
void ha_drop_database(char* path);
-int ha_create_table(const char *name, HA_CREATE_INFO *create_info,
+int ha_create_table(THD *thd, const char *path,
+ const char *db, const char *table_name,
+ HA_CREATE_INFO *create_info,
bool update_create_info);
-int ha_delete_table(THD *thd, enum db_type db_type, const char *path,
- const char *alias, bool generate_warning);
+int ha_delete_table(THD *thd, handlerton *db_type, const char *path,
+ const char *db, const char *alias, bool generate_warning);
+
+/* statistics and info */
+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);
int ha_discover(THD* thd, const char* dbname, const char* name,
- const void** frmblob, uint* frmlen);
+ uchar** frmblob, size_t* frmlen);
int ha_find_files(THD *thd,const char *db,const char *path,
- const char *wild, bool dir,List<char>* files);
+ const char *wild, bool dir, List<LEX_STRING>* files);
int ha_table_exists_in_engine(THD* thd, const char* db, const char* name);
/* key cache */
-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);
int ha_resize_key_cache(KEY_CACHE *key_cache);
int ha_change_key_cache_param(KEY_CACHE *key_cache);
int ha_change_key_cache(KEY_CACHE *old_key_cache, KEY_CACHE *new_key_cache);
@@ -1078,3 +2042,22 @@ void trans_register_ha(THD *thd, bool all, handlerton *ht);
*/
#define trans_need_2pc(thd, all) ((total_ha_2pc > 1) && \
!((all ? &thd->transaction.all : &thd->transaction.stmt)->no_2pc))
+
+#ifdef HAVE_NDB_BINLOG
+int ha_reset_logs(THD *thd);
+int ha_binlog_index_purge_file(THD *thd, const char *file);
+void ha_reset_slave(THD *thd);
+void ha_binlog_log_query(THD *thd, handlerton *db_type,
+ enum_binlog_command binlog_command,
+ const char *query, uint query_length,
+ const char *db, const char *table_name);
+void ha_binlog_wait(THD *thd);
+int ha_binlog_end(THD *thd);
+#else
+#define ha_reset_logs(a) do {} while (0)
+#define ha_binlog_index_purge_file(a,b) do {} while (0)
+#define ha_reset_slave(a) do {} while (0)
+#define ha_binlog_log_query(a,b,c,d,e,f,g) do {} while (0)
+#define ha_binlog_wait(a) do {} while (0)
+#define ha_binlog_end(a) do {} while (0)
+#endif
diff --git a/sql/hash_filo.h b/sql/hash_filo.h
index c25af67b572..ab13d338695 100644
--- a/sql/hash_filo.h
+++ b/sql/hash_filo.h
@@ -84,10 +84,10 @@ public:
first_link=last_link=0;
}
- hash_filo_element *search(gptr key,uint length)
+ hash_filo_element *search(uchar* key, size_t length)
{
hash_filo_element *entry=(hash_filo_element*)
- hash_search(&cache,(byte*) key,length);
+ hash_search(&cache,(uchar*) key,length);
if (entry)
{ // Found; link it first
if (entry != first_link)
@@ -113,9 +113,9 @@ public:
{
hash_filo_element *tmp=last_link;
last_link=last_link->prev_used;
- hash_delete(&cache,(byte*) tmp);
+ hash_delete(&cache,(uchar*) tmp);
}
- if (my_hash_insert(&cache,(byte*) entry))
+ if (my_hash_insert(&cache,(uchar*) entry))
{
if (free_element)
(*free_element)(entry); // This should never happen
diff --git a/sql/hostname.cc b/sql/hostname.cc
index 3b5f3adf88a..c8cf46383a9 100644
--- a/sql/hostname.cc
+++ b/sql/hostname.cc
@@ -14,9 +14,14 @@
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
-/*
- Get hostname for an IP. Hostnames are checked with reverse name lookup and
- checked that they doesn't resemble an ip.
+/**
+ @file
+
+ @brief
+ Get hostname for an IP.
+
+ Hostnames are checked with reverse name lookup and
+ checked that they doesn't resemble an ip.
*/
#include "mysql_priv.h"
@@ -25,7 +30,7 @@
#ifdef __cplusplus
extern "C" { // Because of SCO 3.2V4.2
#endif
-#if !defined( __WIN__) && !defined(OS2)
+#if !defined( __WIN__)
#ifdef HAVE_SYS_UN_H
#include <sys/un.h>
#endif
@@ -84,7 +89,7 @@ static void add_hostname(struct in_addr *in,const char *name)
{
VOID(pthread_mutex_lock(&hostname_cache->lock));
host_entry *entry;
- if (!(entry=(host_entry*) hostname_cache->search((gptr) &in->s_addr,0)))
+ if (!(entry=(host_entry*) hostname_cache->search((uchar*) &in->s_addr,0)))
{
uint length=name ? (uint) strlen(name) : 0;
@@ -115,7 +120,7 @@ void inc_host_errors(struct in_addr *in)
{
VOID(pthread_mutex_lock(&hostname_cache->lock));
host_entry *entry;
- if ((entry=(host_entry*) hostname_cache->search((gptr) &in->s_addr,0)))
+ if ((entry=(host_entry*) hostname_cache->search((uchar*) &in->s_addr,0)))
entry->errors++;
VOID(pthread_mutex_unlock(&hostname_cache->lock));
}
@@ -124,7 +129,7 @@ void reset_host_errors(struct in_addr *in)
{
VOID(pthread_mutex_lock(&hostname_cache->lock));
host_entry *entry;
- if ((entry=(host_entry*) hostname_cache->search((gptr) &in->s_addr,0)))
+ if ((entry=(host_entry*) hostname_cache->search((uchar*) &in->s_addr,0)))
entry->errors=0;
VOID(pthread_mutex_unlock(&hostname_cache->lock));
}
@@ -134,7 +139,7 @@ void reset_host_errors(struct in_addr *in)
#define INADDR_LOOPBACK 0x7f000001UL
#endif
-my_string ip_to_hostname(struct in_addr *in, uint *errors)
+char * ip_to_hostname(struct in_addr *in, uint *errors)
{
uint i;
host_entry *entry;
@@ -149,7 +154,7 @@ my_string ip_to_hostname(struct in_addr *in, uint *errors)
if (!(specialflag & SPECIAL_NO_HOST_CACHE))
{
VOID(pthread_mutex_lock(&hostname_cache->lock));
- if ((entry=(host_entry*) hostname_cache->search((gptr) &in->s_addr,0)))
+ if ((entry=(host_entry*) hostname_cache->search((uchar*) &in->s_addr,0)))
{
char *name;
if (!entry->hostname)
diff --git a/sql/init.cc b/sql/init.cc
index 2dd2031cdaa..afda36b6b9d 100644
--- a/sql/init.cc
+++ b/sql/init.cc
@@ -14,7 +14,12 @@
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
-/* Init and dummy functions for interface with unireg */
+/**
+ @file
+
+ @brief
+ Init and dummy functions for interface with unireg
+*/
#include "mysql_priv.h"
#include <m_ctype.h>
@@ -36,6 +41,7 @@ void unireg_init(ulong options)
#endif
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/item.cc b/sql/item.cc
index 4c2582e2c5c..8356dee0560 100644
--- a/sql/item.cc
+++ b/sql/item.cc
@@ -1,4 +1,4 @@
-/* Copyright (C) 2000-2006 MySQL AB
+/* Copyright 2000-2008 MySQL AB, 2008 Sun Microsystems, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -18,6 +18,7 @@
#pragma implementation // gcc: Class implementation
#endif
#include "mysql_priv.h"
+#include <mysql.h>
#include <m_ctype.h>
#include "my_dir.h"
#include "sp_rcontext.h"
@@ -56,7 +57,7 @@ Hybrid_type_traits::val_decimal(Hybrid_type *val, my_decimal *to) const
String *
Hybrid_type_traits::val_str(Hybrid_type *val, String *to, uint8 decimals) const
{
- to->set(val->real, decimals, &my_charset_bin);
+ to->set_real(val->real, decimals, &my_charset_bin);
return to;
}
@@ -95,6 +96,10 @@ void Hybrid_type_traits_decimal::add(Hybrid_type *val, Field *f) const
}
+/**
+ @todo
+ what is '4' for scale?
+*/
void Hybrid_type_traits_decimal::div(Hybrid_type *val, ulonglong u) const
{
int2my_decimal(E_DEC_FATAL_ERROR, u, TRUE, &val->dec_buf[2]);
@@ -156,16 +161,20 @@ Hybrid_type_traits_integer::fix_length_and_dec(Item *item, Item *arg) const
** Item functions
*****************************************************************************/
-/* Init all special items */
+/**
+ Init all special items.
+*/
void item_init(void)
{
item_user_lock_init();
+ uuid_short_init();
}
-/*
-TODO: make this functions class dependent
+/**
+ @todo
+ Make this functions class dependent
*/
bool Item::val_bool()
@@ -197,7 +206,7 @@ String *Item::val_string_from_real(String *str)
double nr= val_real();
if (null_value)
return 0; /* purecov: inspected */
- str->set(nr,decimals, &my_charset_bin);
+ str->set_real(nr,decimals, &my_charset_bin);
return str;
}
@@ -207,10 +216,7 @@ String *Item::val_string_from_int(String *str)
longlong nr= val_int();
if (null_value)
return 0;
- if (unsigned_flag)
- str->set((ulonglong) nr, &my_charset_bin);
- else
- str->set(nr, &my_charset_bin);
+ str->set_int(nr, unsigned_flag, &my_charset_bin);
return str;
}
@@ -398,10 +404,12 @@ Item::Item():
}
}
-/*
- Constructor used by Item_field, Item_*_ref & aggregate (sum) functions.
+/**
+ Constructor used by Item_field, Item_ref & aggregate (sum)
+ functions.
+
Used for duplicating lists in processing queries with temporary
- tables
+ tables.
*/
Item::Item(THD *thd, Item *item):
rsize(0),
@@ -438,9 +446,10 @@ uint Item::decimal_precision() const
}
-void Item::print_item_w_name(String *str)
+void Item::print_item_w_name(String *str, enum_query_type query_type)
{
- print(str);
+ print(str, query_type);
+
if (name)
{
THD *thd= current_thd;
@@ -461,15 +470,13 @@ void Item::cleanup()
}
-/*
- cleanup() item if it is 'fixed'
+/**
+ cleanup() item if it is 'fixed'.
- SYNOPSIS
- cleanup_processor()
- arg - a dummy parameter, is not used here
+ @param arg a dummy parameter, is not used here
*/
-bool Item::cleanup_processor(byte *arg)
+bool Item::cleanup_processor(uchar *arg)
{
if (fixed)
cleanup();
@@ -477,12 +484,10 @@ bool Item::cleanup_processor(byte *arg)
}
-/*
- rename item (used for views, cleanup() return original name)
+/**
+ rename item (used for views, cleanup() return original name).
- SYNOPSIS
- Item::rename()
- new_name new name of item;
+ @param new_name new name of item;
*/
void Item::rename(char *new_name)
@@ -497,42 +502,36 @@ void Item::rename(char *new_name)
}
-/*
+/**
Traverse item tree possibly transforming it (replacing items).
- SYNOPSIS
- Item::transform()
- transformer functor that performs transformation of a subtree
- arg opaque argument passed to the functor
-
- DESCRIPTION
- This function is designed to ease transformation of Item trees.
+ This function is designed to ease transformation of Item trees.
+ Re-execution note: every such transformation is registered for
+ rollback by THD::change_item_tree() and is rolled back at the end
+ of execution by THD::rollback_item_tree_changes().
- Re-execution note: every such transformation is registered for
- rollback by THD::change_item_tree() and is rolled back at the end
- of execution by THD::rollback_item_tree_changes().
+ Therefore:
+ - this function can not be used at prepared statement prepare
+ (in particular, in fix_fields!), as only permanent
+ transformation of Item trees are allowed at prepare.
+ - the transformer function shall allocate new Items in execution
+ memory root (thd->mem_root) and not anywhere else: allocated
+ items will be gone in the end of execution.
- Therefore:
+ If you don't need to transform an item tree, but only traverse
+ it, please use Item::walk() instead.
- - this function can not be used at prepared statement prepare
- (in particular, in fix_fields!), as only permanent
- transformation of Item trees are allowed at prepare.
- - the transformer function shall allocate new Items in execution
- memory root (thd->mem_root) and not anywhere else: allocated
- items will be gone in the end of execution.
+ @param transformer functor that performs transformation of a subtree
+ @param arg opaque argument passed to the functor
- If you don't need to transform an item tree, but only traverse
- it, please use Item::walk() instead.
-
-
- RETURN VALUE
+ @return
Returns pointer to the new subtree root. THD::change_item_tree()
should be called for it if transformation took place, i.e. if a
pointer to newly allocated item is returned.
*/
-Item* Item::transform(Item_transformer transformer, byte *arg)
+Item* Item::transform(Item_transformer transformer, uchar *arg)
{
DBUG_ASSERT(!current_thd->is_stmt_prepare());
@@ -554,7 +553,9 @@ Item_ident::Item_ident(Name_resolution_context *context_arg,
}
-/* Constructor used by Item_field & Item_*_ref (see Item comment) */
+/**
+ Constructor used by Item_field & Item_*_ref (see Item comment)
+*/
Item_ident::Item_ident(THD *thd, Item_ident *item)
:Item(thd, item),
@@ -590,7 +591,7 @@ void Item_ident::cleanup()
DBUG_VOID_RETURN;
}
-bool Item_ident::remove_dependence_processor(byte * arg)
+bool Item_ident::remove_dependence_processor(uchar * arg)
{
DBUG_ENTER("Item_ident::remove_dependence_processor");
if (depended_from == (st_select_lex *) arg)
@@ -599,29 +600,25 @@ bool Item_ident::remove_dependence_processor(byte * arg)
}
-/*
+/**
Store the pointer to this item field into a list if not already there.
- SYNOPSIS
- Item_field::collect_item_field_processor()
- arg pointer to a List<Item_field>
+ The method is used by Item::walk to collect all unique Item_field objects
+ from a tree of Items into a set of items represented as a list.
- DESCRIPTION
- The method is used by Item::walk to collect all unique Item_field objects
- from a tree of Items into a set of items represented as a list.
+ Item_cond::walk() and Item_func::walk() stop the evaluation of the
+ processor function for its arguments once the processor returns
+ true.Therefore in order to force this method being called for all item
+ arguments in a condition the method must return false.
- IMPLEMENTATION
- Item_cond::walk() and Item_func::walk() stop the evaluation of the
- processor function for its arguments once the processor returns
- true.Therefore in order to force this method being called for all item
- arguments in a condition the method must return false.
+ @param arg pointer to a List<Item_field>
- RETURN
+ @return
FALSE to force the evaluation of collect_item_field_processor
- for the subsequent items.
+ for the subsequent items.
*/
-bool Item_field::collect_item_field_processor(byte *arg)
+bool Item_field::collect_item_field_processor(uchar *arg)
{
DBUG_ENTER("Item_field::collect_item_field_processor");
DBUG_PRINT("info", ("%s", field->field_name ? field->field_name : "noname"));
@@ -638,25 +635,23 @@ bool Item_field::collect_item_field_processor(byte *arg)
}
-/*
+/**
Check if an Item_field references some field from a list of fields.
- SYNOPSIS
- Item_field::find_item_in_field_list_processor
- arg Field being compared, arg must be of type Field
+ Check whether the Item_field represented by 'this' references any
+ of the fields in the keyparts passed via 'arg'. Used with the
+ method Item::walk() to test whether any keypart in a sequence of
+ keyparts is referenced in an expression.
- DESCRIPTION
- Check whether the Item_field represented by 'this' references any
- of the fields in the keyparts passed via 'arg'. Used with the
- method Item::walk() to test whether any keypart in a sequence of
- keyparts is referenced in an expression.
+ @param arg Field being compared, arg must be of type Field
- RETURN
+ @retval
TRUE if 'this' references the field 'arg'
+ @retval
FALSE otherwise
*/
-bool Item_field::find_item_in_field_list_processor(byte *arg)
+bool Item_field::find_item_in_field_list_processor(uchar *arg)
{
KEY_PART_INFO *first_non_group_part= *((KEY_PART_INFO **) arg);
KEY_PART_INFO *last_part= *(((KEY_PART_INFO **) arg) + 1);
@@ -671,6 +666,23 @@ bool Item_field::find_item_in_field_list_processor(byte *arg)
}
+/*
+ Mark field in read_map
+
+ NOTES
+ This is used by filesort to register used fields in a a temporary
+ column read set or to register used fields in a view
+*/
+
+bool Item_field::register_field_in_read_map(uchar *arg)
+{
+ TABLE *table= (TABLE *) arg;
+ if (field->table == table || !table)
+ bitmap_set_bit(field->table->read_set, field->field_index);
+ return 0;
+}
+
+
bool Item::check_cols(uint c)
{
if (c != 1)
@@ -717,7 +729,7 @@ void Item::set_name(const char *str, uint length, CHARSET_INFO *cs)
}
if (!my_charset_same(cs, system_charset_info))
{
- uint32 res_length;
+ size_t res_length;
name= sql_strmake_with_convert(str, name_length= length, cs,
MAX_ALIAS_NAME, system_charset_info,
&res_length);
@@ -727,7 +739,8 @@ void Item::set_name(const char *str, uint length, CHARSET_INFO *cs)
}
-/*
+/**
+ @details
This function is called when:
- Comparing items in the WHERE clause (when doing where optimization)
- When trying to find an ORDER BY/GROUP BY item in the SELECT part
@@ -752,7 +765,8 @@ Item *Item::safe_charset_converter(CHARSET_INFO *tocs)
}
-/*
+/**
+ @details
Created mostly for mysql_prepare_table(). Important
when a string ENUM/SET column is described with a numeric default value:
@@ -797,6 +811,7 @@ Item *Item_string::safe_charset_converter(CHARSET_INFO *tocs)
{
Item_string *conv;
uint conv_errors;
+ char *ptr;
String tmp, cstr, *ostr= val_str(&tmp);
cstr.copy(ostr->ptr(), ostr->length(), ostr->charset(), tocs, &conv_errors);
if (conv_errors || !(conv= new Item_string(cstr.ptr(), cstr.length(),
@@ -811,7 +826,9 @@ Item *Item_string::safe_charset_converter(CHARSET_INFO *tocs)
*/
return NULL;
}
- conv->str_value.copy();
+ 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();
return conv;
@@ -876,10 +893,10 @@ bool Item_string::eq(const Item *item, bool binary_cmp) const
}
-/*
+/**
Get the value of the function as a MYSQL_TIME structure.
As a extra convenience the time structure is reset on error!
- */
+*/
bool Item::get_date(MYSQL_TIME *ltime,uint fuzzydate)
{
@@ -913,10 +930,11 @@ err:
return 1;
}
-/*
- Get time of first argument.
+/**
+ Get time of first argument.\
+
As a extra convenience the time structure is reset on error!
- */
+*/
bool Item::get_time(MYSQL_TIME *ltime)
{
@@ -937,16 +955,27 @@ CHARSET_INFO *Item::default_charset()
}
+/*
+ Save value in field, but don't give any warnings
+
+ NOTES
+ This is used to temporary store and retrieve a value in a column,
+ for example in opt_range to adjust the key value to fit the column.
+*/
+
int Item::save_in_field_no_warnings(Field *field, bool no_conversions)
{
int res;
- THD *thd= field->table->in_use;
+ TABLE *table= field->table;
+ 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;
thd->variables.sql_mode&= ~(MODE_NO_ZERO_IN_DATE | MODE_NO_ZERO_DATE);
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;
return res;
}
@@ -1104,7 +1133,7 @@ Item_splocal::this_item_addr(THD *thd, Item **)
}
-void Item_splocal::print(String *str)
+void Item_splocal::print(String *str, enum_query_type)
{
str->reserve(m_name.length+8);
str->append(m_name.str, m_name.length);
@@ -1123,8 +1152,8 @@ bool Item_splocal::set_value(THD *thd, sp_rcontext *ctx, Item **it)
Item_case_expr methods
*****************************************************************************/
-Item_case_expr::Item_case_expr(int case_expr_id)
- :Item_sp_variable((char *) STRING_WITH_LEN("case_expr")),
+Item_case_expr::Item_case_expr(uint case_expr_id)
+ :Item_sp_variable( C_STRING_WITH_LEN("case_expr")),
m_case_expr_id(case_expr_id)
{
}
@@ -1158,8 +1187,10 @@ Item_case_expr::this_item_addr(THD *thd, Item **)
}
-void Item_case_expr::print(String *str)
+void Item_case_expr::print(String *str, enum_query_type)
{
+ if (str->reserve(MAX_INT_WIDTH + sizeof("case_expr@")))
+ return; /* purecov: inspected */
VOID(str->append(STRING_WITH_LEN("case_expr@")));
str->qs_append(m_case_expr_id);
}
@@ -1291,12 +1322,12 @@ bool Item_name_const::fix_fields(THD *thd, Item **ref)
}
-void Item_name_const::print(String *str)
+void Item_name_const::print(String *str, enum_query_type query_type)
{
str->append(STRING_WITH_LEN("NAME_CONST("));
- name_item->print(str);
+ name_item->print(str, query_type);
str->append(',');
- value_item->print(str);
+ value_item->print(str, query_type);
str->append(')');
}
@@ -1313,37 +1344,36 @@ public:
const char *table_name_arg, const char *field_name_arg)
:Item_ref(context_arg, item, table_name_arg, field_name_arg) {}
- void print (String *str)
+ virtual inline void print (String *str, enum_query_type query_type)
{
if (ref)
- (*ref)->print(str);
+ (*ref)->print(str, query_type);
else
- Item_ident::print(str);
+ Item_ident::print(str, query_type);
}
+ virtual Ref_Type ref_type() { return AGGREGATE_REF; }
};
-/*
- Move SUM items out from item tree and replace with reference
+/**
+ Move SUM items out from item tree and replace with reference.
- SYNOPSIS
- split_sum_func2()
- thd Thread handler
- ref_pointer_array Pointer to array of reference fields
- fields All fields in select
- ref Pointer to item
- skip_registered <=> function be must skipped for registered SUM items
+ @param thd Thread handler
+ @param ref_pointer_array Pointer to array of reference fields
+ @param fields All fields in select
+ @param ref Pointer to item
+ @param skip_registered <=> function be must skipped for registered
+ SUM items
- NOTES
- This is from split_sum_func2() for items that should be split
+ @note
+ This is from split_sum_func2() for items that should be split
- All found SUM items are added FIRST in the fields list and
- we replace the item with a reference.
+ All found SUM items are added FIRST in the fields list and
+ we replace the item with a reference.
- thd->fatal_error() may be called if we are out of memory
+ thd->fatal_error() may be called if we are out of memory
*/
-
void Item::split_sum_func2(THD *thd, Item **ref_pointer_array,
List<Item> &fields, Item **ref,
bool skip_registered)
@@ -1410,41 +1440,42 @@ left_is_superset(DTCollation *left, DTCollation *right)
return FALSE;
}
-/*
- Aggregate two collations together taking
- into account their coercibility (aka derivation):
-
- 0 == DERIVATION_EXPLICIT - an explicitly written COLLATE clause
- 1 == DERIVATION_NONE - a mix of two different collations
- 2 == DERIVATION_IMPLICIT - a column
- 3 == DERIVATION_COERCIBLE - a string constant
-
- The most important rules are:
-
- 1. If collations are the same:
- chose this collation, and the strongest derivation.
-
- 2. If collations are different:
- - Character sets may differ, but only if conversion without
- data loss is possible. The caller provides flags whether
- character set conversion attempts should be done. If no
- flags are substituted, then the character sets must be the same.
- Currently processed flags are:
- MY_COLL_ALLOW_SUPERSET_CONV - allow conversion to a superset
- MY_COLL_ALLOW_COERCIBLE_CONV - allow conversion of a coercible value
- - two EXPLICIT collations produce an error, e.g. this is wrong:
- CONCAT(expr1 collate latin1_swedish_ci, expr2 collate latin1_german_ci)
- - the side with smaller derivation value wins,
- i.e. a column is stronger than a string constant,
- an explicit COLLATE clause is stronger than a column.
- - if derivations are the same, we have DERIVATION_NONE,
- we'll wait for an explicit COLLATE clause which possibly can
- come from another argument later: for example, this is valid,
- but we don't know yet when collecting the first two arguments:
- CONCAT(latin1_swedish_ci_column,
- latin1_german1_ci_column,
- expr COLLATE latin1_german2_ci)
+/**
+ Aggregate two collations together taking
+ into account their coercibility (aka derivation):.
+
+ 0 == DERIVATION_EXPLICIT - an explicitly written COLLATE clause @n
+ 1 == DERIVATION_NONE - a mix of two different collations @n
+ 2 == DERIVATION_IMPLICIT - a column @n
+ 3 == DERIVATION_COERCIBLE - a string constant.
+
+ The most important rules are:
+ -# If collations are the same:
+ chose this collation, and the strongest derivation.
+ -# If collations are different:
+ - Character sets may differ, but only if conversion without
+ data loss is possible. The caller provides flags whether
+ character set conversion attempts should be done. If no
+ flags are substituted, then the character sets must be the same.
+ Currently processed flags are:
+ MY_COLL_ALLOW_SUPERSET_CONV - allow conversion to a superset
+ MY_COLL_ALLOW_COERCIBLE_CONV - allow conversion of a coercible value
+ - two EXPLICIT collations produce an error, e.g. this is wrong:
+ CONCAT(expr1 collate latin1_swedish_ci, expr2 collate latin1_german_ci)
+ - the side with smaller derivation value wins,
+ i.e. a column is stronger than a string constant,
+ an explicit COLLATE clause is stronger than a column.
+ - if derivations are the same, we have DERIVATION_NONE,
+ we'll wait for an explicit COLLATE clause which possibly can
+ come from another argument later: for example, this is valid,
+ but we don't know yet when collecting the first two arguments:
+ @code
+ CONCAT(latin1_swedish_ci_column,
+ latin1_german1_ci_column,
+ expr COLLATE latin1_german2_ci)
+ @endcode
*/
+
bool DTCollation::aggregate(DTCollation &dt, uint flags)
{
if (!my_charset_same(collation, dt.collation))
@@ -1471,7 +1502,9 @@ bool DTCollation::aggregate(DTCollation &dt, uint flags)
set(dt);
}
else
- ; // Do nothing
+ {
+ // Do nothing
+ }
}
else if ((flags & MY_COLL_ALLOW_SUPERSET_CONV) &&
left_is_superset(this, &dt))
@@ -1608,8 +1641,9 @@ bool agg_item_collations_for_comparison(DTCollation &c, const char *fname,
}
-/*
+/**
Collect arguments' character sets together.
+
We allow to apply automatic character set conversion in some cases.
The conditions when conversion is possible are:
- arguments A and B have different charsets
@@ -1625,23 +1659,27 @@ bool agg_item_collations_for_comparison(DTCollation &c, const char *fname,
to the collation of A.
For functions with more than two arguments:
-
+ @code
collect(A,B,C) ::= collect(collect(A,B),C)
-
+ @endcode
Since this function calls THD::change_item_tree() on the passed Item **
pointers, it is necessary to pass the original Item **'s, not copies.
Otherwise their values will not be properly restored (see BUG#20769).
If the items are not consecutive (eg. args[2] and args[5]), use the
item_sep argument, ie.
-
+ @code
agg_item_charsets(coll, fname, &args[2], 2, flags, 3)
-
+ @endcode
*/
bool agg_item_charsets(DTCollation &coll, const char *fname,
Item **args, uint nargs, uint flags, int item_sep)
{
Item **arg, *safe_args[2];
+
+ LINT_INIT(safe_args[0]);
+ LINT_INIT(safe_args[1]);
+
if (agg_item_collations(coll, fname, args, nargs, flags, item_sep))
return TRUE;
@@ -1708,7 +1746,7 @@ bool agg_item_charsets(DTCollation &coll, const char *fname,
been created in prepare. In this case register the change for
rollback.
*/
- if (arena)
+ if (thd->is_stmt_prepare())
*arg= conv;
else
thd->change_item_tree(arg, conv);
@@ -1741,20 +1779,28 @@ void Item_ident_for_show::make_field(Send_field *tmp_field)
Item_field::Item_field(Field *f)
:Item_ident(0, NullS, *f->table_name, f->field_name),
- item_equal(0), no_const_subst(0),
+ item_equal(0), no_const_subst(0),
have_privileges(0), any_privileges(0)
{
set_field(f);
/*
- field_name and talbe_name should not point to garbage
+ field_name and table_name should not point to garbage
if this item is to be reused
*/
orig_table_name= orig_field_name= "";
}
+
+/**
+ Constructor used inside setup_wild().
+
+ Ensures that field, table, and database names will live as long as
+ Item_field (this is important in prepared statements).
+*/
+
Item_field::Item_field(THD *thd, Name_resolution_context *context_arg,
Field *f)
- :Item_ident(context_arg, f->table->s->db, *f->table_name, f->field_name),
+ :Item_ident(context_arg, f->table->s->db.str, *f->table_name, f->field_name),
item_equal(0), no_const_subst(0),
have_privileges(0), any_privileges(0)
{
@@ -1806,7 +1852,10 @@ Item_field::Item_field(Name_resolution_context *context_arg,
select->select_n_where_fields++;
}
-// Constructor need to process subselect with temporary tables (see Item)
+/**
+ Constructor need to process subselect with temporary tables (see Item)
+*/
+
Item_field::Item_field(THD *thd, Item_field *item)
:Item_ident(thd, item),
field(item->field),
@@ -1827,7 +1876,7 @@ void Item_field::set_field(Field *field_par)
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;
+ 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());
@@ -1837,7 +1886,7 @@ void Item_field::set_field(Field *field_par)
}
-/*
+/**
Reset this item to point to a field from the new temporary table.
This is used when we create a new temporary table for each execution
of prepared statement.
@@ -1875,7 +1924,7 @@ const char *Item_ident::full_name() const
return tmp;
}
-void Item_ident::print(String *str)
+void Item_ident::print(String *str, enum_query_type query_type)
{
THD *thd= current_thd;
char d_name_buff[MAX_ALIAS_NAME], t_name_buff[MAX_ALIAS_NAME];
@@ -2106,10 +2155,15 @@ Item *Item_field::get_tmp_table_item(THD *thd)
return new_item;
}
+longlong Item_field::val_int_endpoint(bool left_endp, bool *incl_endp)
+{
+ longlong res= val_int();
+ return null_value? LONGLONG_MIN : res;
+}
-/*
+/**
Create an item from a string we KNOW points to a valid longlong
- end \0 terminated number string.
+ end \\0 terminated number string.
This is always 'signed'. Unsigned values are created with Item_uint()
*/
@@ -2138,7 +2192,7 @@ String *Item_int::val_str(String *str)
return str;
}
-void Item_int::print(String *str)
+void Item_int::print(String *str, enum_query_type query_type)
{
// my_charset_bin is good enough for numbers
str_value.set(value, &my_charset_bin);
@@ -2169,7 +2223,7 @@ String *Item_uint::val_str(String *str)
}
-void Item_uint::print(String *str)
+void Item_uint::print(String *str, enum_query_type query_type)
{
// latin1 is good enough for numbers
str_value.set((ulonglong) value, default_charset());
@@ -2229,7 +2283,7 @@ Item_decimal::Item_decimal(my_decimal *value_par)
}
-Item_decimal::Item_decimal(const char *bin, int precision, int scale)
+Item_decimal::Item_decimal(const uchar *bin, int precision, int scale)
{
binary2my_decimal(E_DEC_FATAL_ERROR, bin,
&decimal_value, precision, scale);
@@ -2261,7 +2315,7 @@ String *Item_decimal::val_str(String *result)
return result;
}
-void Item_decimal::print(String *str)
+void Item_decimal::print(String *str, enum_query_type query_type)
{
my_decimal2string(E_DEC_FATAL_ERROR, &decimal_value, 0, 0, 0, &str_value);
str->append(str_value);
@@ -2300,7 +2354,7 @@ String *Item_float::val_str(String *str)
{
// following assert is redundant, because fixed=1 assigned in constructor
DBUG_ASSERT(fixed == 1);
- str->set(value,decimals,&my_charset_bin);
+ str->set_real(value,decimals,&my_charset_bin);
return str;
}
@@ -2314,27 +2368,52 @@ my_decimal *Item_float::val_decimal(my_decimal *decimal_value)
}
-void Item_string::print(String *str)
+void Item_string::print(String *str, enum_query_type query_type)
{
- str->append('_');
- str->append(collation.collation->csname);
+ if (query_type == QT_ORDINARY && is_cs_specified())
+ {
+ str->append('_');
+ str->append(collation.collation->csname);
+ }
+
str->append('\'');
- str_value.print(str);
+
+ if (query_type == QT_ORDINARY ||
+ my_charset_same(str_value.charset(), system_charset_info))
+ {
+ str_value.print(str);
+ }
+ else
+ {
+ THD *thd= current_thd;
+ LEX_STRING utf8_lex_str;
+
+ thd->convert_string(&utf8_lex_str,
+ system_charset_info,
+ str_value.c_ptr_safe(),
+ str_value.length(),
+ str_value.charset());
+
+ String utf8_str(utf8_lex_str.str,
+ utf8_lex_str.length,
+ system_charset_info);
+
+ utf8_str.print(str);
+ }
+
str->append('\'');
}
-double Item_string::val_real()
+double
+double_from_string_with_check (CHARSET_INFO *cs, const char *cptr, char *end)
{
- DBUG_ASSERT(fixed == 1);
int error;
- char *end, *org_end;
+ char *org_end;
double tmp;
- CHARSET_INFO *cs= str_value.charset();
- org_end= (char*) str_value.ptr() + str_value.length();
- tmp= my_strntod(cs, (char*) str_value.ptr(), str_value.length(), &end,
- &error);
+ org_end= end;
+ tmp= my_strntod(cs, (char*) cptr, end - cptr, &end, &error);
if (error || (end != org_end && !check_if_only_end_space(cs, end, org_end)))
{
/*
@@ -2344,22 +2423,28 @@ double Item_string::val_real()
push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
ER_TRUNCATED_WRONG_VALUE,
ER(ER_TRUNCATED_WRONG_VALUE), "DOUBLE",
- str_value.ptr());
+ cptr);
}
return tmp;
}
-longlong Item_string::val_int()
+double Item_string::val_real()
{
DBUG_ASSERT(fixed == 1);
+ return double_from_string_with_check (str_value.charset(), str_value.ptr(),
+ (char *) str_value.ptr() + str_value.length());
+}
+
+
+longlong
+longlong_from_string_with_check (CHARSET_INFO *cs, const char *cptr, char *end)
+{
int err;
longlong tmp;
- char *end= (char*) str_value.ptr()+ str_value.length();
char *org_end= end;
- CHARSET_INFO *cs= str_value.charset();
- tmp= (*(cs->cset->strtoll10))(cs, str_value.ptr(), &end, &err);
+ tmp= (*(cs->cset->strtoll10))(cs, cptr, &end, &err);
/*
TODO: Give error if we wanted a signed integer and we got an unsigned
one
@@ -2370,12 +2455,24 @@ longlong Item_string::val_int()
push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
ER_TRUNCATED_WRONG_VALUE,
ER(ER_TRUNCATED_WRONG_VALUE), "INTEGER",
- str_value.ptr());
+ cptr);
}
return tmp;
}
+/**
+ @todo
+ Give error if we wanted a signed integer and we got an unsigned one
+*/
+longlong Item_string::val_int()
+{
+ DBUG_ASSERT(fixed == 1);
+ return longlong_from_string_with_check(str_value.charset(), str_value.ptr(),
+ (char *) str_value.ptr()+ str_value.length());
+}
+
+
my_decimal *Item_string::val_decimal(my_decimal *decimal_value)
{
return val_decimal_from_string(decimal_value);
@@ -2423,9 +2520,9 @@ Item *Item_null::safe_charset_converter(CHARSET_INFO *tocs)
/*********************** Item_param related ******************************/
-/*
+/**
Default function of Item_param::set_param_func, so in case
- of malformed packet the server won't SIGSEGV
+ of malformed packet the server won't SIGSEGV.
*/
static void
@@ -2437,7 +2534,7 @@ default_set_param_func(Item_param *param,
}
-Item_param::Item_param(unsigned pos_in_query_arg) :
+Item_param::Item_param(uint pos_in_query_arg) :
state(NO_VALUE),
item_result_type(STRING_RESULT),
/* Don't pretend to be a literal unless value for this item is set. */
@@ -2499,16 +2596,14 @@ void Item_param::set_double(double d)
}
-/*
+/**
Set decimal parameter value from string.
- SYNOPSIS
- set_decimal()
- str - character string
- length - string length
+ @param str character string
+ @param length string length
- NOTE
- as we use character strings to send decimal values in
+ @note
+ As we use character strings to send decimal values in
binary protocol, we use str2my_decimal to convert it to
internal decimal value.
*/
@@ -2529,16 +2624,14 @@ void Item_param::set_decimal(const char *str, ulong length)
}
-/*
+/**
Set parameter value from MYSQL_TIME value.
- SYNOPSIS
- set_time()
- tm - datetime value to set (time_type is ignored)
- type - type of datetime value
- max_length_arg - max length of datetime value as string
+ @param tm datetime value to set (time_type is ignored)
+ @param type type of datetime value
+ @param max_length_arg max length of datetime value as string
- NOTE
+ @note
If we value to be stored is not normalized, zero value will be stored
instead and proper warning will be produced. This function relies on
the fact that even wrong value sent over binary protocol fits into
@@ -2554,7 +2647,7 @@ void Item_param::set_time(MYSQL_TIME *tm, timestamp_type time_type,
if (value.time.year > 9999 || value.time.month > 12 ||
value.time.day > 31 ||
- time_type != MYSQL_TIMESTAMP_TIME && value.time.hour > 23 ||
+ (time_type != MYSQL_TIMESTAMP_TIME && value.time.hour > 23) ||
value.time.minute > 59 || value.time.second > 59)
{
char buff[MAX_DATE_STRING_REP_LENGTH];
@@ -2614,16 +2707,15 @@ bool Item_param::set_longdata(const char *str, ulong length)
}
-/*
+/**
Set parameter value from user variable value.
- SYNOPSIS
- set_from_user_var
- thd Current thread
- entry User variable structure (NULL means use NULL value)
+ @param thd Current thread
+ @param entry User variable structure (NULL means use NULL value)
- RETURN
+ @retval
0 OK
+ @retval
1 Out of memory
*/
@@ -2656,7 +2748,8 @@ bool Item_param::set_from_user_var(THD *thd, const user_var_entry *entry)
CHARSET_INFO *tocs= thd->variables.collation_connection;
uint32 dummy_offset;
- value.cs_info.character_set_of_placeholder= fromcs;
+ value.cs_info.character_set_of_placeholder=
+ value.cs_info.character_set_client= fromcs;
/*
Setup source and destination character sets so that they
are different only if conversion is necessary: this will
@@ -2697,14 +2790,11 @@ bool Item_param::set_from_user_var(THD *thd, const user_var_entry *entry)
DBUG_RETURN(0);
}
-/*
- Resets parameter after execution.
-
- SYNOPSIS
- Item_param::reset()
-
- NOTES
- We clear null_value here instead of setting it in set_* methods,
+/**
+ Resets parameter after execution.
+
+ @note
+ We clear null_value here instead of setting it in set_* methods,
because we want more easily handle case for long data.
*/
@@ -2897,7 +2987,7 @@ String *Item_param::val_str(String* str)
case LONG_DATA_VALUE:
return &str_value_ptr;
case REAL_VALUE:
- str->set(value.real, NOT_FIXED_DEC, &my_charset_bin);
+ str->set_real(value.real, NOT_FIXED_DEC, &my_charset_bin);
return str;
case INT_VALUE:
str->set(value.integer, &my_charset_bin);
@@ -2923,24 +3013,25 @@ String *Item_param::val_str(String* str)
return str;
}
-/*
+/**
Return Param item values in string format, for generating the dynamic
- query used in update/binary logs
- TODO: change interface and implementation to fill log data in place
- and avoid one more memcpy/alloc between str and log string.
+ query used in update/binary logs.
+
+ @todo
+ - Change interface and implementation to fill log data in place
+ and avoid one more memcpy/alloc between str and log string.
+ - In case of error we need to notify replication
+ that binary log contains wrong statement
*/
const String *Item_param::query_val_str(String* str) const
{
switch (state) {
case INT_VALUE:
- if (unsigned_flag)
- str->set((ulonglong) value.integer, &my_charset_bin);
- else
- str->set(value.integer, &my_charset_bin);
+ str->set_int(value.integer, unsigned_flag, &my_charset_bin);
break;
case REAL_VALUE:
- str->set(value.real, NOT_FIXED_DEC, &my_charset_bin);
+ str->set_real(value.real, NOT_FIXED_DEC, &my_charset_bin);
break;
case DECIMAL_VALUE:
if (my_decimal2string(E_DEC_FATAL_ERROR, &decimal_value,
@@ -2983,7 +3074,7 @@ const String *Item_param::query_val_str(String* str) const
}
-/*
+/**
Convert string from client character set to the character set of
connection.
*/
@@ -3093,7 +3184,7 @@ Item_param::eq(const Item *arg, bool binary_cmp) const
/* End of Item_param related */
-void Item_param::print(String *str)
+void Item_param::print(String *str, enum_query_type query_type)
{
if (state == NO_VALUE)
{
@@ -3110,6 +3201,49 @@ void Item_param::print(String *str)
}
+/**
+ Preserve the original parameter types and values
+ when re-preparing a prepared statement.
+
+ @details Copy parameter type information and conversion
+ function pointers from a parameter of the old statement
+ to the corresponding parameter of the new one.
+
+ Move parameter values from the old parameters to the new
+ one. We simply "exchange" the values, which allows
+ to save on allocation and character set conversion in
+ case a parameter is a string or a blob/clob.
+
+ The old parameter gets the value of this one, which
+ ensures that all memory of this parameter is freed
+ correctly.
+
+ @param[in] src parameter item of the original
+ prepared statement
+*/
+
+void
+Item_param::set_param_type_and_swap_value(Item_param *src)
+{
+ unsigned_flag= src->unsigned_flag;
+ param_type= src->param_type;
+ set_param_func= src->set_param_func;
+ item_type= src->item_type;
+ item_result_type= src->item_result_type;
+
+ collation.set(src->collation);
+ maybe_null= src->maybe_null;
+ null_value= src->null_value;
+ max_length= src->max_length;
+ decimals= src->decimals;
+ state= src->state;
+ value= src->value;
+
+ decimal_value.swap(src->decimal_value);
+ str_value.swap(src->str_value);
+ str_value_ptr.swap(src->str_value_ptr);
+}
+
/****************************************************************************
Item_copy_string
****************************************************************************/
@@ -3207,17 +3341,16 @@ bool Item_ref_null_helper::get_date(MYSQL_TIME *ltime, uint fuzzydate)
}
-/*
- Mark item and SELECT_LEXs as dependent if item was resolved in outer SELECT
-
- SYNOPSIS
- mark_as_dependent()
- thd - thread handler
- last - select from which current item depend
- current - current select
- resolved_item - item which was resolved in outer SELECT(for warning)
- mark_item - item which should be marked (can be differ in case of
- substitution)
+/**
+ Mark item and SELECT_LEXs as dependent if item was resolved in
+ outer SELECT.
+
+ @param thd thread handler
+ @param last select from which current item depend
+ @param current current select
+ @param resolved_item item which was resolved in outer SELECT(for warning)
+ @param mark_item item which should be marked (can be differ in case of
+ substitution)
*/
static void mark_as_dependent(THD *thd, SELECT_LEX *last, SELECT_LEX *current,
@@ -3246,21 +3379,19 @@ static void mark_as_dependent(THD *thd, SELECT_LEX *last, SELECT_LEX *current,
}
-/*
- Mark range of selects and resolved identifier (field/reference) item as
- dependent
-
- SYNOPSIS
- mark_select_range_as_dependent()
- thd - thread handler
- last_select - select where resolved_item was resolved
- current_sel - current select (select where resolved_item was placed)
- found_field - field which was found during resolving
- found_item - Item which was found during resolving (if resolved
- identifier belongs to VIEW)
- resolved_item - Identifier which was resolved
-
- NOTE:
+/**
+ Mark range of selects and resolved identifier (field/reference)
+ item as dependent.
+
+ @param thd thread handler
+ @param last_select select where resolved_item was resolved
+ @param current_sel current select (select where resolved_item was placed)
+ @param found_field field which was found during resolving
+ @param found_item Item which was found during resolving (if resolved
+ identifier belongs to VIEW)
+ @param resolved_item Identifier which was resolved
+
+ @note
We have to mark all items between current_sel (including) and
last_select (excluding) as dependend (select before last_select should
be marked with actual table mask used by resolved item, all other with
@@ -3312,20 +3443,17 @@ void mark_select_range_as_dependent(THD *thd,
}
-/*
+/**
Search a GROUP BY clause for a field with a certain name.
- SYNOPSIS
- find_field_in_group_list()
- find_item the item being searched for
- group_list GROUP BY clause
+ Search the GROUP BY list for a column named as find_item. When searching
+ preference is given to columns that are qualified with the same table (and
+ database) name as the one being searched for.
- DESCRIPTION
- Search the GROUP BY list for a column named as find_item. When searching
- preference is given to columns that are qualified with the same table (and
- database) name as the one being searched for.
+ @param find_item the item being searched for
+ @param group_list GROUP BY clause
- RETURN
+ @return
- the found item on success
- NULL if find_item is not in group_list
*/
@@ -3421,43 +3549,40 @@ static Item** find_field_in_group_list(Item *find_item, ORDER *group_list)
}
-/*
+/**
Resolve a column reference in a sub-select.
- SYNOPSIS
- resolve_ref_in_select_and_group()
- thd current thread
- ref column reference being resolved
- select the select that ref is resolved against
-
- DESCRIPTION
- Resolve a column reference (usually inside a HAVING clause) against the
- SELECT and GROUP BY clauses of the query described by 'select'. The name
- resolution algorithm searches both the SELECT and GROUP BY clauses, and in
- case of a name conflict prefers GROUP BY column names over SELECT names. If
- both clauses contain different fields with the same names, a warning is
- issued that name of 'ref' is ambiguous. We extend ANSI SQL in that when no
- GROUP BY column is found, then a HAVING name is resolved as a possibly
- derived SELECT column. This extension is allowed only if the
- MODE_ONLY_FULL_GROUP_BY sql mode isn't enabled.
-
- NOTES
+ Resolve a column reference (usually inside a HAVING clause) against the
+ SELECT and GROUP BY clauses of the query described by 'select'. The name
+ resolution algorithm searches both the SELECT and GROUP BY clauses, and in
+ case of a name conflict prefers GROUP BY column names over SELECT names. If
+ both clauses contain different fields with the same names, a warning is
+ issued that name of 'ref' is ambiguous. We extend ANSI SQL in that when no
+ GROUP BY column is found, then a HAVING name is resolved as a possibly
+ derived SELECT column. This extension is allowed only if the
+ MODE_ONLY_FULL_GROUP_BY sql mode isn't enabled.
+
+ @param thd current thread
+ @param ref column reference being resolved
+ @param select the select that ref is resolved against
+
+ @note
The resolution procedure is:
- Search for a column or derived column named col_ref_i [in table T_j]
- in the SELECT clause of Q.
+ in the SELECT clause of Q.
- Search for a column named col_ref_i [in table T_j]
- in the GROUP BY clause of Q.
+ in the GROUP BY clause of Q.
- If found different columns with the same name in GROUP BY and SELECT
- - issue a warning and return the GROUP BY column,
- - otherwise
- - if the MODE_ONLY_FULL_GROUP_BY mode is enabled return error
- - else return the found SELECT column.
+ - issue a warning and return the GROUP BY column,
+ - otherwise
+ - if the MODE_ONLY_FULL_GROUP_BY mode is enabled return error
+ - else return the found SELECT column.
- RETURN
- NULL - there was an error, and the error was already reported
- not_found_item - the item was not resolved, no error was reported
- resolved item - if the item was resolved
+ @return
+ - NULL - there was an error, and the error was already reported
+ - not_found_item - the item was not resolved, no error was reported
+ - resolved item - if the item was resolved
*/
static Item**
@@ -3534,44 +3659,45 @@ resolve_ref_in_select_and_group(THD *thd, Item_ident *ref, SELECT_LEX *select)
}
-/*
+/**
Resolve the name of an outer select column reference.
- SYNOPSIS
- Item_field::fix_outer_field()
- thd [in] current thread
- from_field [in/out] found field reference or (Field*)not_found_field
- reference [in/out] view column if this item was resolved to a view column
+ The method resolves the column reference represented by 'this' as a column
+ present in outer selects that contain current select.
- DESCRIPTION
- The method resolves the column reference represented by 'this' as a column
- present in outer selects that contain current select.
-
- NOTES
- This is the inner loop of Item_field::fix_fields:
-
- for each outer query Q_k beginning from the inner-most one
- {
- search for a column or derived column named col_ref_i
- [in table T_j] in the FROM clause of Q_k;
+ In prepared statements, because of cache, find_field_in_tables()
+ can resolve fields even if they don't belong to current context.
+ In this case this method only finds appropriate context and marks
+ current select as dependent. The found reference of field should be
+ provided in 'from_field'.
- if such a column is not found
- Search for a column or derived column named col_ref_i
- [in table T_j] in the SELECT and GROUP clauses of Q_k.
- }
+ @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
- IMPLEMENTATION
- In prepared statements, because of cache, find_field_in_tables()
- can resolve fields even if they don't belong to current context.
- In this case this method only finds appropriate context and marks
- current select as dependent. The found reference of field should be
- provided in 'from_field'.
+ @note
+ This is the inner loop of Item_field::fix_fields:
+ @code
+ for each outer query Q_k beginning from the inner-most one
+ {
+ search for a column or derived column named col_ref_i
+ [in table T_j] in the FROM clause of Q_k;
- RETURN
- 1 - column succefully resolved and fix_fields() should continue.
- 0 - column fully fixed and fix_fields() should return FALSE
- -1 - error occured
+ if such a column is not found
+ Search for a column or derived column named col_ref_i
+ [in table T_j] in the SELECT and GROUP clauses of Q_k.
+ }
+ @endcode
+
+ @retval
+ 1 column succefully resolved and fix_fields() should continue.
+ @retval
+ 0 column fully fixed and fix_fields() should return FALSE
+ @retval
+ -1 error occured
*/
+
int
Item_field::fix_outer_field(THD *thd, Field **from_field, Item **reference)
{
@@ -3832,48 +3958,48 @@ Item_field::fix_outer_field(THD *thd, Field **from_field, Item **reference)
}
-/*
+/**
Resolve the name of a column reference.
- SYNOPSIS
- Item_field::fix_fields()
- thd [in] current thread
- reference [in/out] view column if this item was resolved to a view column
+ The method resolves the column reference represented by 'this' as a column
+ present in one of: FROM clause, SELECT clause, GROUP BY clause of a query
+ Q, or in outer queries that contain Q.
- DESCRIPTION
- The method resolves the column reference represented by 'this' as a column
- present in one of: FROM clause, SELECT clause, GROUP BY clause of a query
- Q, or in outer queries that contain Q.
+ The name resolution algorithm used is (where [T_j] is an optional table
+ name that qualifies the column name):
- NOTES
- The name resolution algorithm used is (where [T_j] is an optional table
- name that qualifies the column name):
+ @code
+ resolve_column_reference([T_j].col_ref_i)
+ {
+ search for a column or derived column named col_ref_i
+ [in table T_j] in the FROM clause of Q;
- resolve_column_reference([T_j].col_ref_i)
+ if such a column is NOT found AND // Lookup in outer queries.
+ there are outer queries
{
- search for a column or derived column named col_ref_i
- [in table T_j] in the FROM clause of Q;
-
- if such a column is NOT found AND // Lookup in outer queries.
- there are outer queries
+ for each outer query Q_k beginning from the inner-most one
{
- for each outer query Q_k beginning from the inner-most one
- {
- search for a column or derived column named col_ref_i
- [in table T_j] in the FROM clause of Q_k;
+ search for a column or derived column named col_ref_i
+ [in table T_j] in the FROM clause of Q_k;
- if such a column is not found
- Search for a column or derived column named col_ref_i
- [in table T_j] in the SELECT and GROUP clauses of Q_k.
- }
+ if such a column is not found
+ Search for a column or derived column named col_ref_i
+ [in table T_j] in the SELECT and GROUP clauses of Q_k.
}
}
+ }
+ @endcode
Notice that compared to Item_ref::fix_fields, here we first search the FROM
clause, and then we search the SELECT and GROUP BY clauses.
- RETURN
+ @param[in] thd current thread
+ @param[in,out] reference view column if this item was resolved to a
+ view column
+
+ @retval
TRUE if error
+ @retval
FALSE on success
*/
@@ -3894,7 +4020,9 @@ bool Item_field::fix_fields(THD *thd, Item **reference)
context->first_name_resolution_table,
context->last_name_resolution_table,
reference,
- IGNORE_EXCEPT_NON_UNIQUE,
+ thd->lex->use_only_table_context ?
+ REPORT_ALL_ERRORS :
+ IGNORE_EXCEPT_NON_UNIQUE,
!any_privileges,
TRUE)) ==
not_found_field)
@@ -3917,15 +4045,15 @@ bool Item_field::fix_fields(THD *thd, Item **reference)
if ((*res)->type() == Item::FIELD_ITEM)
{
/*
- It's an Item_field referencing another Item_field in the select
- list.
- use the field from the Item_field in the select list and leave
- the Item_field instance in place.
+ It's an Item_field referencing another Item_field in the select
+ list.
+ Use the field from the Item_field in the select list and leave
+ the Item_field instance in place.
*/
- Field *field= (*((Item_field**)res))->field;
+ Field *new_field= (*((Item_field**)res))->field;
- if (field == NULL)
+ if (new_field == NULL)
{
/* The column to which we link isn't valid. */
my_error(ER_BAD_FIELD_ERROR, MYF(0), (*res)->name,
@@ -3933,7 +4061,7 @@ bool Item_field::fix_fields(THD *thd, Item **reference)
return(1);
}
- set_field(field);
+ set_field(new_field);
return 0;
}
else
@@ -3966,8 +4094,8 @@ bool Item_field::fix_fields(THD *thd, Item **reference)
goto error;
if (!outer_fixed && cached_table && cached_table->select_lex &&
- context->select_lex &&
- cached_table->select_lex != context->select_lex)
+ context->select_lex &&
+ cached_table->select_lex != context->select_lex)
{
int ret;
if ((ret= fix_outer_field(thd, &from_field, reference)) < 0)
@@ -3981,7 +4109,7 @@ bool Item_field::fix_fields(THD *thd, Item **reference)
if it is not expression from merged VIEW we will set this field.
We can leave expression substituted from view for next PS/SP rexecution
- (i.e. do not register this substitution for reverting on cleupup()
+ (i.e. do not register this substitution for reverting on cleanup()
(register_item_tree_changing())), because this subtree will be
fix_field'ed during setup_tables()->setup_underlying() (i.e. before
all other expressions of query, and references on tables which do
@@ -3999,28 +4127,38 @@ bool Item_field::fix_fields(THD *thd, Item **reference)
set_if_bigger(thd->lex->in_sum_func->max_arg_level,
thd->lex->current_select->nest_level);
}
- else if (thd->set_query_id && field->query_id != thd->query_id)
- {
- /* We only come here in unions */
- TABLE *table=field->table;
- field->query_id=thd->query_id;
- table->used_fields++;
- table->used_keys.intersect(field->part_of_key);
- }
-#ifndef NO_EMBEDDED_ACCESS_CHECKS
- if (any_privileges)
+ else if (thd->mark_used_columns != MARK_COLUMNS_NONE)
{
- char *db, *tab;
- if (cached_table->view)
+ TABLE *table= field->table;
+ MY_BITMAP *current_bitmap, *other_bitmap;
+ if (thd->mark_used_columns == MARK_COLUMNS_READ)
{
- db= cached_table->view_db.str;
- tab= cached_table->view_name.str;
+ current_bitmap= table->read_set;
+ other_bitmap= table->write_set;
}
else
{
- db= cached_table->db;
- tab= cached_table->table_name;
+ current_bitmap= table->write_set;
+ other_bitmap= table->read_set;
}
+ if (!bitmap_fast_test_and_set(current_bitmap, field->field_index))
+ {
+ if (!bitmap_is_set(other_bitmap, field->field_index))
+ {
+ /* First usage of column */
+ table->used_fields++; // Used to optimize loops
+ /* purecov: begin inspected */
+ table->covering_keys.intersect(field->part_of_key);
+ /* purecov: end */
+ }
+ }
+ }
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+ if (any_privileges)
+ {
+ char *db, *tab;
+ db= cached_table->get_db_name();
+ tab= cached_table->get_table_name();
if (!(have_privileges= (get_column_grant(thd, &field->table->grant,
db, tab, field_name) &
VIEW_ANY_ACL)))
@@ -4089,26 +4227,24 @@ void Item_field::cleanup()
DBUG_VOID_RETURN;
}
-/*
- Find a field among specified multiple equalities
+/**
+ Find a field among specified multiple equalities.
- SYNOPSIS
- find_item_equal()
- cond_equal reference to list of multiple equalities where
- the field (this object) is to be looked for
-
- DESCRIPTION
- The function first searches the field among multiple equalities
- of the current level (in the cond_equal->current_level list).
- If it fails, it continues searching in upper levels accessed
- through a pointer cond_equal->upper_levels.
- The search terminates as soon as a multiple equality containing
- the field is found.
-
- RETURN VALUES
- First Item_equal containing the field, if success
- 0, otherwise
+ The function first searches the field among multiple equalities
+ of the current level (in the cond_equal->current_level list).
+ If it fails, it continues searching in upper levels accessed
+ through a pointer cond_equal->upper_levels.
+ The search terminates as soon as a multiple equality containing
+ the field is found.
+
+ @param cond_equal reference to list of multiple equalities where
+ the field (this object) is to be looked for
+
+ @return
+ - First Item_equal containing the field, if success
+ - 0, otherwise
*/
+
Item_equal *Item_field::find_item_equal(COND_EQUAL *cond_equal)
{
Item_equal *item= 0;
@@ -4130,36 +4266,37 @@ Item_equal *Item_field::find_item_equal(COND_EQUAL *cond_equal)
}
-/*
- Check whether a field can be substituted by an equal item
+/**
+ Check whether a field can be substituted by an equal item.
- SYNOPSIS
- equal_fields_propagator()
- arg - *arg != NULL <-> the field is in the context where
- substitution for an equal item is valid
-
- DESCRIPTION
- The function checks whether a substitution of the field
- occurrence for an equal item is valid.
+ The function checks whether a substitution of the field
+ occurrence for an equal item is valid.
- NOTES
+ @param arg *arg != NULL <-> the field is in the context where
+ substitution for an equal item is valid
+
+ @note
The following statement is not always true:
+ @n
x=y => F(x)=F(x/y).
+ @n
This means substitution of an item for an equal item not always
- yields an equavalent condition.
- Here's an example:
- 'a'='a '
- (LENGTH('a')=1) != (LENGTH('a ')=2)
+ yields an equavalent condition. Here's an example:
+ @code
+ 'a'='a '
+ (LENGTH('a')=1) != (LENGTH('a ')=2)
+ @endcode
Such a substitution is surely valid if either the substituted
field is not of a STRING type or if it is an argument of
- a comparison predicate.
+ a comparison predicate.
- RETURN
+ @retval
TRUE substitution is valid
+ @retval
FALSE otherwise
*/
-bool Item_field::subst_argument_checker(byte **arg)
+bool Item_field::subst_argument_checker(uchar **arg)
{
return (result_type() != STRING_RESULT) || (*arg);
}
@@ -4194,33 +4331,31 @@ 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)
-
- SYNOPSIS
- equal_fields_propagator()
- arg - reference to list of multiple equalities where
- the field (this object) is to be looked for
-
- DESCRIPTION
- 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.
- If the found multiple equality contains a constant, then the field
- reference is substituted for this constant, otherwise it sets a pointer
- to the multiple equality in the field item.
+ (if any).
- NOTES
+ 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.
+ If the found multiple equality contains a constant, then the field
+ reference is substituted for this constant, otherwise it sets a pointer
+ to the multiple equality in the field item.
+
+
+ @param arg reference to list of multiple equalities where
+ the field (this object) is to be looked for
+
+ @note
This function is supposed to be called as a callback parameter in calls
- of the compile method.
+ of the compile method.
- RETURN VALUES
- pointer to the replacing constant item, if the field item was substituted
- pointer to the field item, otherwise.
+ @return
+ - pointer to the replacing constant item, if the field item was substituted
+ - pointer to the field item, otherwise.
*/
-Item *Item_field::equal_fields_propagator(byte *arg)
+Item *Item_field::equal_fields_propagator(uchar *arg)
{
if (no_const_subst)
return this;
@@ -4258,12 +4393,13 @@ Item *Item_field::equal_fields_propagator(byte *arg)
}
-/*
- Mark the item to not be part of substitution if it's not a binary item
- See comments in Arg_comparator::set_compare_func() for details
+/**
+ Mark the item to not be part of substitution if it's not a binary item.
+
+ See comments in Arg_comparator::set_compare_func() for details.
*/
-bool Item_field::set_no_const_sub(byte *arg)
+bool Item_field::set_no_const_sub(uchar *arg)
{
if (field->charset() != &my_charset_bin)
no_const_subst=1;
@@ -4271,34 +4407,32 @@ bool Item_field::set_no_const_sub(byte *arg)
}
-/*
+/**
Replace an Item_field for an equal Item_field that evaluated earlier
- (if any)
-
- SYNOPSIS
- replace_equal_field_()
- arg - a dummy parameter, is not used here
-
- DESCRIPTION
- The function returns a pointer to an item that is taken from
- the very beginning of the item_equal list which the Item_field
- object refers to (belongs to) unless item_equal contains a constant
- item. In this case the function returns this constant item,
- (if the substitution does not require conversion).
- If the Item_field object does not refer any Item_equal object
- 'this' is returned
+ (if any).
- NOTES
+ The function returns a pointer to an item that is taken from
+ the very beginning of the item_equal list which the Item_field
+ object refers to (belongs to) unless item_equal contains a constant
+ item. In this case the function returns this constant item,
+ (if the substitution does not require conversion).
+ If the Item_field object does not refer any Item_equal object
+ 'this' is returned .
+
+ @param arg a dummy parameter, is not used here
+
+
+ @note
This function is supposed to be called as a callback parameter in calls
- of the thransformer method.
+ of the thransformer method.
- RETURN VALUES
- pointer to a replacement Item_field if there is a better equal item or
- a pointer to a constant equal item;
- this - otherwise.
+ @return
+ - pointer to a replacement Item_field if there is a better equal item or
+ a pointer to a constant equal item;
+ - this - otherwise.
*/
-Item *Item_field::replace_equal_field(byte *arg)
+Item *Item_field::replace_equal_field(uchar *arg)
{
if (item_equal)
{
@@ -4344,24 +4478,30 @@ void Item::make_field(Send_field *tmp_field)
}
-void Item_empty_string::make_field(Send_field *tmp_field)
+enum_field_types Item::string_field_type() const
{
- enum_field_types f_type= FIELD_TYPE_VAR_STRING;
+ enum_field_types f_type= MYSQL_TYPE_VAR_STRING;
if (max_length >= 16777216)
- f_type= FIELD_TYPE_LONG_BLOB;
+ f_type= MYSQL_TYPE_LONG_BLOB;
else if (max_length >= 65536)
- f_type= FIELD_TYPE_MEDIUM_BLOB;
- init_make_field(tmp_field, f_type);
+ f_type= MYSQL_TYPE_MEDIUM_BLOB;
+ return f_type;
+}
+
+
+void Item_empty_string::make_field(Send_field *tmp_field)
+{
+ init_make_field(tmp_field, string_field_type());
}
enum_field_types Item::field_type() const
{
switch (result_type()) {
- case STRING_RESULT: return MYSQL_TYPE_VARCHAR;
- case INT_RESULT: return FIELD_TYPE_LONGLONG;
- case DECIMAL_RESULT: return FIELD_TYPE_NEWDECIMAL;
- case REAL_RESULT: return FIELD_TYPE_DOUBLE;
+ case STRING_RESULT: return string_field_type();
+ case INT_RESULT: return MYSQL_TYPE_LONGLONG;
+ case DECIMAL_RESULT: return MYSQL_TYPE_NEWDECIMAL;
+ case REAL_RESULT: return MYSQL_TYPE_DOUBLE;
case ROW_RESULT:
default:
DBUG_ASSERT(0);
@@ -4469,108 +4609,133 @@ bool Item::eq_by_collation(Item *item, bool binary_cmp, CHARSET_INFO *cs)
}
-/*
- Create a field to hold a string value from an item
+/**
+ Create a field to hold a string value from an item.
- SYNOPSIS
- make_string_field()
- table Table for which the field is created
+ If max_length > CONVERT_IF_BIGGER_TO_BLOB create a blob @n
+ If max_length > 0 create a varchar @n
+ If max_length == 0 create a CHAR(0)
- IMPLEMENTATION
- If max_length > CONVERT_IF_BIGGER_TO_BLOB create a blob
- If max_length > 0 create a varchar
- If max_length == 0 create a CHAR(0)
+ @param table Table for which the field is created
*/
-
Field *Item::make_string_field(TABLE *table)
{
+ Field *field;
DBUG_ASSERT(collation.collation);
if (max_length/collation.collation->mbmaxlen > CONVERT_IF_BIGGER_TO_BLOB)
- return new Field_blob(max_length, maybe_null, name, table,
+ field= new Field_blob(max_length, maybe_null, name,
collation.collation);
/* Item_type_holder holds the exact type, do not change it */
- if (max_length > 0 &&
+ else if (max_length > 0 &&
(type() != Item::TYPE_HOLDER || field_type() != MYSQL_TYPE_STRING))
- return new Field_varstring(max_length, maybe_null, name, table,
+ field= new Field_varstring(max_length, maybe_null, name, table->s,
collation.collation);
- return new Field_string(max_length, maybe_null, name, table,
- collation.collation);
+ else
+ field= new Field_string(max_length, maybe_null, name,
+ collation.collation);
+ if (field)
+ field->init(table);
+ return field;
}
-/*
- Create a field based on field_type of argument
+/**
+ Create a field based on field_type of argument.
For now, this is only used to create a field for
- IFNULL(x,something)
+ IFNULL(x,something) and time functions
- RETURN
- 0 error
- # Created field
+ @retval
+ NULL error
+ @retval
+ \# Created field
*/
-Field *Item::tmp_table_field_from_field_type(TABLE *table)
+Field *Item::tmp_table_field_from_field_type(TABLE *table, bool fixed_length)
{
/*
The field functions defines a field to be not null if null_ptr is not 0
*/
uchar *null_ptr= maybe_null ? (uchar*) "" : 0;
+ Field *field;
switch (field_type()) {
case MYSQL_TYPE_DECIMAL:
case MYSQL_TYPE_NEWDECIMAL:
- return new Field_new_decimal((char*) 0, max_length, null_ptr, 0,
- Field::NONE, name, table, decimals, 0,
+ field= new Field_new_decimal((uchar*) 0, max_length, null_ptr, 0,
+ Field::NONE, name, decimals, 0,
unsigned_flag);
+ break;
case MYSQL_TYPE_TINY:
- return new Field_tiny((char*) 0, max_length, null_ptr, 0, Field::NONE,
- name, table, 0, unsigned_flag);
+ field= new Field_tiny((uchar*) 0, max_length, null_ptr, 0, Field::NONE,
+ name, 0, unsigned_flag);
+ break;
case MYSQL_TYPE_SHORT:
- return new Field_short((char*) 0, max_length, null_ptr, 0, Field::NONE,
- name, table, 0, unsigned_flag);
+ field= new Field_short((uchar*) 0, max_length, null_ptr, 0, Field::NONE,
+ name, 0, unsigned_flag);
+ break;
case MYSQL_TYPE_LONG:
- return new Field_long((char*) 0, max_length, null_ptr, 0, Field::NONE,
- name, table, 0, unsigned_flag);
+ field= new Field_long((uchar*) 0, max_length, null_ptr, 0, Field::NONE,
+ name, 0, unsigned_flag);
+ break;
#ifdef HAVE_LONG_LONG
case MYSQL_TYPE_LONGLONG:
- return new Field_longlong((char*) 0, max_length, null_ptr, 0, Field::NONE,
- name, table, 0, unsigned_flag);
+ field= new Field_longlong((uchar*) 0, max_length, null_ptr, 0, Field::NONE,
+ name, 0, unsigned_flag);
+ break;
#endif
case MYSQL_TYPE_FLOAT:
- return new Field_float((char*) 0, max_length, null_ptr, 0, Field::NONE,
- name, table, decimals, 0, unsigned_flag);
+ field= new Field_float((uchar*) 0, max_length, null_ptr, 0, Field::NONE,
+ name, decimals, 0, unsigned_flag);
+ break;
case MYSQL_TYPE_DOUBLE:
- return new Field_double((char*) 0, max_length, null_ptr, 0, Field::NONE,
- name, table, decimals, 0, unsigned_flag);
+ field= new Field_double((uchar*) 0, max_length, null_ptr, 0, Field::NONE,
+ name, decimals, 0, unsigned_flag);
+ break;
case MYSQL_TYPE_NULL:
- return new Field_null((char*) 0, max_length, Field::NONE,
- name, table, &my_charset_bin);
+ field= new Field_null((uchar*) 0, max_length, Field::NONE,
+ name, &my_charset_bin);
+ break;
case MYSQL_TYPE_INT24:
- return new Field_medium((char*) 0, max_length, null_ptr, 0, Field::NONE,
- name, table, 0, unsigned_flag);
+ field= new Field_medium((uchar*) 0, max_length, null_ptr, 0, Field::NONE,
+ name, 0, unsigned_flag);
+ break;
case MYSQL_TYPE_NEWDATE:
case MYSQL_TYPE_DATE:
- return new Field_newdate(maybe_null, name, table, &my_charset_bin);
+ field= new Field_newdate(maybe_null, name, &my_charset_bin);
+ break;
case MYSQL_TYPE_TIME:
- return new Field_time(maybe_null, name, table, &my_charset_bin);
+ field= new Field_time(maybe_null, name, &my_charset_bin);
+ break;
case MYSQL_TYPE_TIMESTAMP:
- return new Field_timestamp(maybe_null, name, table, &my_charset_bin);
+ field= new Field_timestamp(maybe_null, name, &my_charset_bin);
+ break;
case MYSQL_TYPE_DATETIME:
- return new Field_datetime(maybe_null, name, table, &my_charset_bin);
+ field= new Field_datetime(maybe_null, name, &my_charset_bin);
+ break;
case MYSQL_TYPE_YEAR:
- return new Field_year((char*) 0, max_length, null_ptr, 0, Field::NONE,
- name, table);
+ field= new Field_year((uchar*) 0, max_length, null_ptr, 0, Field::NONE,
+ name);
+ break;
case MYSQL_TYPE_BIT:
- return new Field_bit_as_char(NULL, max_length, null_ptr, 0,
- Field::NONE, name, table);
+ field= new Field_bit_as_char(NULL, max_length, null_ptr, 0,
+ Field::NONE, name);
+ break;
default:
/* This case should never be chosen */
DBUG_ASSERT(0);
/* If something goes awfully wrong, it's better to get a string than die */
+ case MYSQL_TYPE_STRING:
+ if (fixed_length && max_length < CONVERT_IF_BIGGER_TO_BLOB)
+ {
+ field= new Field_string(max_length, maybe_null, name,
+ collation.collation);
+ break;
+ }
+ /* Fall through to make_string_field() */
case MYSQL_TYPE_ENUM:
case MYSQL_TYPE_SET:
- case MYSQL_TYPE_STRING:
case MYSQL_TYPE_VAR_STRING:
case MYSQL_TYPE_VARCHAR:
return make_string_field(table);
@@ -4579,18 +4744,20 @@ Field *Item::tmp_table_field_from_field_type(TABLE *table)
case MYSQL_TYPE_LONG_BLOB:
case MYSQL_TYPE_BLOB:
if (this->type() == Item::TYPE_HOLDER)
- return new Field_blob(max_length, maybe_null, name, table,
- collation.collation, 1);
+ field= new Field_blob(max_length, maybe_null, name, collation.collation,
+ 1);
else
- return new Field_blob(max_length, maybe_null, name, table,
- collation.collation);
+ field= new Field_blob(max_length, maybe_null, name, collation.collation);
break; // Blob handled outside of case
#ifdef HAVE_SPATIAL
case MYSQL_TYPE_GEOMETRY:
- return new Field_geom(max_length, maybe_null,
- name, table, get_geometry_type());
+ field= new Field_geom(max_length, maybe_null,
+ name, table->s, get_geometry_type());
#endif /* HAVE_SPATIAL */
}
+ if (field)
+ field->init(table);
+ return field;
}
@@ -4608,8 +4775,8 @@ void Item_field::make_field(Send_field *tmp_field)
}
-/*
- Set a field:s value from a item
+/**
+ Set a field's value from a item.
*/
void Item_field::save_org_in_field(Field *to)
@@ -4645,21 +4812,19 @@ int Item_field::save_in_field(Field *to, bool no_conversions)
}
-/*
- Store null in field
+/**
+ Store null in field.
- SYNOPSIS
- save_in_field()
- field Field where we want to store NULL
+ This is used on INSERT.
+ Allow NULL to be inserted in timestamp and auto_increment values.
- DESCRIPTION
- This is used on INSERT.
- Allow NULL to be inserted in timestamp and auto_increment values
+ @param field Field where we want to store NULL
- RETURN VALUES
- 0 ok
- 1 Field doesn't support NULL values and can't handle 'field = NULL'
-*/
+ @retval
+ 0 ok
+ @retval
+ 1 Field doesn't support NULL values and can't handle 'field = NULL'
+*/
int Item_null::save_in_field(Field *field, bool no_conversions)
{
@@ -4667,17 +4832,16 @@ int Item_null::save_in_field(Field *field, bool no_conversions)
}
-/*
- Store null in field
+/**
+ Store null in field.
- SYNOPSIS
- save_safe_in_field()
- field Field where we want to store NULL
+ @param field Field where we want to store NULL
- RETURN VALUES
+ @retval
0 OK
+ @retval
1 Field doesn't support NULL values
-*/
+*/
int Item_null::save_safe_in_field(Field *field)
{
@@ -4695,8 +4859,8 @@ 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)
+ (result_type() == REAL_RESULT &&
+ field->result_type() == STRING_RESULT))
{
String *result;
CHARSET_INFO *cs= collation.collation;
@@ -4835,7 +4999,7 @@ static uint nr_of_decimals(const char *str, const char *end)
}
-/*
+/**
This function is only called during parsing. We will signal an error if
value is not a true double value (overflow)
*/
@@ -4872,7 +5036,7 @@ int Item_float::save_in_field(Field *field, bool no_conversions)
}
-void Item_float::print(String *str)
+void Item_float::print(String *str, enum_query_type query_type)
{
if (presentation)
{
@@ -4881,7 +5045,7 @@ void Item_float::print(String *str)
}
char buffer[20];
String num(buffer, sizeof(buffer), &my_charset_bin);
- num.set(value, decimals, &my_charset_bin);
+ num.set_real(value, decimals, &my_charset_bin);
str->append(num);
}
@@ -4993,7 +5157,7 @@ warn:
}
-void Item_hex_string::print(String *str)
+void Item_hex_string::print(String *str, enum_query_type query_type)
{
char *end= (char*) str_value.ptr() + str_value.length(),
*ptr= end - min(str_value.length(), sizeof(longlong));
@@ -5075,8 +5239,8 @@ Item_bin_string::Item_bin_string(const char *str, uint str_length)
}
-/*
- Pack data in buffer for sending
+/**
+ Pack data in buffer for sending.
*/
bool Item_null::send(Protocol *protocol, String *packet)
@@ -5084,8 +5248,8 @@ bool Item_null::send(Protocol *protocol, String *packet)
return protocol->store_null();
}
-/*
- This is only called from items that is not of type item_field
+/**
+ This is only called from items that is not of type item_field.
*/
bool Item::send(Protocol *protocol, String *buffer)
@@ -5239,7 +5403,7 @@ void Item_field::update_null_value()
this field otherwise
*/
-Item *Item_field::update_value_transformer(byte *select_arg)
+Item *Item_field::update_value_transformer(uchar *select_arg)
{
SELECT_LEX *select= (SELECT_LEX*)select_arg;
DBUG_ASSERT(fixed);
@@ -5262,7 +5426,7 @@ Item *Item_field::update_value_transformer(byte *select_arg)
}
-void Item_field::print(String *str)
+void Item_field::print(String *str, enum_query_type query_type)
{
if (field && field->table->const_table)
{
@@ -5274,7 +5438,7 @@ void Item_field::print(String *str)
str->append('\'');
return;
}
- Item_ident::print(str);
+ Item_ident::print(str, query_type);
}
@@ -5294,60 +5458,67 @@ Item_ref::Item_ref(Name_resolution_context *context_arg,
}
-/*
+/**
Resolve the name of a reference to a column reference.
- SYNOPSIS
- Item_ref::fix_fields()
- thd [in] current thread
- reference [in/out] view column if this item was resolved to a view column
+ The method resolves the column reference represented by 'this' as a column
+ present in one of: GROUP BY clause, SELECT clause, outer queries. It is
+ used typically for columns in the HAVING clause which are not under
+ aggregate functions.
- DESCRIPTION
- The method resolves the column reference represented by 'this' as a column
- present in one of: GROUP BY clause, SELECT clause, outer queries. It is
- used typically for columns in the HAVING clause which are not under
- aggregate functions.
+ POSTCONDITION @n
+ Item_ref::ref is 0 or points to a valid item.
- NOTES
+ @note
The name resolution algorithm used is (where [T_j] is an optional table
name that qualifies the column name):
- resolve_extended([T_j].col_ref_i)
- {
- Search for a column or derived column named col_ref_i [in table T_j]
- in the SELECT and GROUP clauses of Q.
-
- if such a column is NOT found AND // Lookup in outer queries.
- there are outer queries
+ @code
+ resolve_extended([T_j].col_ref_i)
{
- for each outer query Q_k beginning from the inner-most one
- {
- Search for a column or derived column named col_ref_i
- [in table T_j] in the SELECT and GROUP clauses of Q_k.
+ Search for a column or derived column named col_ref_i [in table T_j]
+ in the SELECT and GROUP clauses of Q.
- if such a column is not found AND
- - Q_k is not a group query AND
- - Q_k is not inside an aggregate function
- OR
- - Q_(k-1) is not in a HAVING or SELECT clause of Q_k
- {
- search for a column or derived column named col_ref_i
- [in table T_j] in the FROM clause of Q_k;
+ if such a column is NOT found AND // Lookup in outer queries.
+ there are outer queries
+ {
+ for each outer query Q_k beginning from the inner-most one
+ {
+ Search for a column or derived column named col_ref_i
+ [in table T_j] in the SELECT and GROUP clauses of Q_k.
+
+ if such a column is not found AND
+ - Q_k is not a group query AND
+ - Q_k is not inside an aggregate function
+ OR
+ - Q_(k-1) is not in a HAVING or SELECT clause of Q_k
+ {
+ search for a column or derived column named col_ref_i
+ [in table T_j] in the FROM clause of Q_k;
+ }
}
}
}
- }
-
+ @endcode
+ @n
This procedure treats GROUP BY and SELECT clauses as one namespace for
column references in HAVING. Notice that compared to
Item_field::fix_fields, here we first search the SELECT and GROUP BY
clauses, and then we search the FROM clause.
- POSTCONDITION
- Item_ref::ref is 0 or points to a valid item
+ @param[in] thd current thread
+ @param[in,out] reference view column if this item was resolved to a
+ view column
- RETURN
+ @todo
+ Here we could first find the field anyway, and then test this
+ condition, so that we can give a better error message -
+ ER_WRONG_FIELD_WITH_GROUP, instead of the less informative
+ ER_BAD_FIELD_ERROR which we produce now.
+
+ @retval
TRUE if error
+ @retval
FALSE on success
*/
@@ -5610,7 +5781,7 @@ void Item_ref::cleanup()
}
-void Item_ref::print(String *str)
+void Item_ref::print(String *str, enum_query_type query_type)
{
if (ref)
{
@@ -5621,10 +5792,10 @@ void Item_ref::print(String *str)
append_identifier(thd, str, name, (uint) strlen(name));
}
else
- (*ref)->print(str);
+ (*ref)->print(str, query_type);
}
else
- Item_ident::print(str);
+ Item_ident::print(str, query_type);
}
@@ -5829,11 +6000,11 @@ Item *Item_ref::get_tmp_table_item(THD *thd)
}
-void Item_ref_null_helper::print(String *str)
+void Item_ref_null_helper::print(String *str, enum_query_type query_type)
{
str->append(STRING_WITH_LEN("<ref_null_helper>("));
if (ref)
- (*ref)->print(str);
+ (*ref)->print(str, query_type);
else
str->append('?');
str->append(')');
@@ -5892,16 +6063,15 @@ bool Item_direct_ref::get_date(MYSQL_TIME *ltime,uint fuzzydate)
}
-/*
- Prepare referenced field then call usual Item_direct_ref::fix_fields
+/**
+ Prepare referenced field then call usual Item_direct_ref::fix_fields .
- SYNOPSIS
- Item_direct_view_ref::fix_fields()
- thd thread handler
- reference reference on reference where this item stored
+ @param thd thread handler
+ @param reference reference on reference where this item stored
- RETURN
+ @retval
FALSE OK
+ @retval
TRUE Error
*/
@@ -5944,25 +6114,23 @@ bool Item_outer_ref::fix_fields(THD *thd, Item **reference)
}
-/*
+/**
Compare two view column references for equality.
- SYNOPSIS
- Item_direct_view_ref::eq()
- item item to compare with
- binary_cmp make binary comparison
+ A view column reference is considered equal to another column
+ reference if the second one is a view column and if both column
+ references resolve to the same item. It is assumed that both
+ items are of the same type.
- DESCRIPTION
- A view column reference is considered equal to another column
- reference if the second one is a view column and if both column
- references resolve to the same item.
+ @param item item to compare with
+ @param binary_cmp make binary comparison
- RETURN
+ @retval
TRUE Referenced item is equal to given item
+ @retval
FALSE otherwise
*/
-
bool Item_direct_view_ref::eq(const Item *item, bool binary_cmp) const
{
if (item->type() == REF_ITEM)
@@ -6016,8 +6184,9 @@ 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());
- def_field->move_field(def_field->table->s->default_values -
- def_field->table->record[0]);
+ def_field->move_field_offset((my_ptrdiff_t)
+ (def_field->table->s->default_values -
+ def_field->table->record[0]));
set_field(def_field);
return FALSE;
@@ -6027,7 +6196,7 @@ error:
}
-void Item_default_value::print(String *str)
+void Item_default_value::print(String *str, enum_query_type query_type)
{
if (!arg)
{
@@ -6035,7 +6204,7 @@ void Item_default_value::print(String *str)
return;
}
str->append(STRING_WITH_LEN("default("));
- arg->print(str);
+ arg->print(str, query_type);
str->append(')');
}
@@ -6080,12 +6249,12 @@ int Item_default_value::save_in_field(Field *field_arg, bool no_conversions)
}
-/*
+/**
This method like the walk method traverses the item tree, but at the
- same time it can replace some nodes in the tree
+ same time it can replace some nodes in the tree.
*/
-Item *Item_default_value::transform(Item_transformer transformer, byte *args)
+Item *Item_default_value::transform(Item_transformer transformer, uchar *args)
{
DBUG_ASSERT(!current_thd->is_stmt_prepare());
@@ -6158,39 +6327,43 @@ bool Item_insert_value::fix_fields(THD *thd, Item **items)
if (!def_field)
return TRUE;
memcpy(def_field, field_arg->field, field_arg->field->size_of());
- def_field->move_field(def_field->table->insert_values -
- def_field->table->record[0]);
+ def_field->move_field_offset((my_ptrdiff_t)
+ (def_field->table->insert_values -
+ def_field->table->record[0]));
set_field(def_field);
}
else
{
Field *tmp_field= field_arg->field;
/* charset doesn't matter here, it's to avoid sigsegv only */
- set_field(new Field_null(0, 0, Field::NONE, tmp_field->field_name,
- tmp_field->table, &my_charset_bin));
+ tmp_field= new Field_null(0, 0, Field::NONE, field_arg->field->field_name,
+ &my_charset_bin);
+ if (tmp_field)
+ {
+ tmp_field->init(field_arg->field->table);
+ set_field(tmp_field);
+ }
}
return FALSE;
}
-void Item_insert_value::print(String *str)
+void Item_insert_value::print(String *str, enum_query_type query_type)
{
str->append(STRING_WITH_LEN("values("));
- arg->print(str);
+ arg->print(str, query_type);
str->append(')');
}
-/*
+/**
Find index of Field object which will be appropriate for item
representing field of row being changed in trigger.
- SYNOPSIS
- setup_field()
- thd - current thread context
- table - table of trigger (and where we looking for fields)
- table_grant_info - GRANT_INFO of the subject table
+ @param thd current thread context
+ @param table table of trigger (and where we looking for fields)
+ @param table_grant_info GRANT_INFO of the subject table
- NOTE
+ @note
This function does almost the same as fix_fields() for Item_field
but is invoked right after trigger definition parsing. Since at
this stage we can't say exactly what Field object (corresponding
@@ -6206,21 +6379,21 @@ void Item_trigger_field::setup_field(THD *thd, TABLE *table,
GRANT_INFO *table_grant_info)
{
/*
- There is no sense in marking fields used by trigger with current value
- of THD::query_id since it is completely unrelated to the THD::query_id
- value for statements which will invoke trigger. So instead we use
- Table_triggers_list::mark_fields_used() method which is called during
- execution of these statements.
+ It is too early to mark fields used here, because before execution
+ of statement that will invoke trigger other statements may use same
+ TABLE object, so all such mark-up will be wiped out.
+ So instead we do it in Table_triggers_list::mark_fields_used()
+ method which is called during execution of these statements.
*/
- bool save_set_query_id= thd->set_query_id;
- thd->set_query_id= 0;
+ enum_mark_columns save_mark_used_columns= thd->mark_used_columns;
+ thd->mark_used_columns= MARK_COLUMNS_NONE;
/*
Try to find field by its name and if it will be found
set field_idx properly.
*/
(void)find_field_in_table(thd, table, field_name, (uint) strlen(field_name),
0, &field_idx);
- thd->set_query_id= save_set_query_id;
+ thd->mark_used_columns= save_mark_used_columns;
triggers= table->triggers;
table_grants= table_grant_info;
}
@@ -6278,10 +6451,9 @@ bool Item_trigger_field::fix_fields(THD *thd, Item **items)
{
table_grants->want_privilege= want_privilege;
- if (check_grant_column(thd, table_grants, triggers->trigger_table->s->db,
- triggers->trigger_table->s->table_name,
- field_name,
- (uint) strlen(field_name), thd->security_ctx))
+ if (check_grant_column(thd, table_grants, triggers->trigger_table->s->db.str,
+ triggers->trigger_table->s->table_name.str, field_name,
+ strlen(field_name), thd->security_ctx))
return TRUE;
}
#endif // NO_EMBEDDED_ACCESS_CHECKS
@@ -6299,7 +6471,7 @@ bool Item_trigger_field::fix_fields(THD *thd, Item **items)
}
-void Item_trigger_field::print(String *str)
+void Item_trigger_field::print(String *str, enum_query_type query_type)
{
str->append((row_version == NEW_ROW) ? "NEW" : "OLD", 3);
str->append('.');
@@ -6318,11 +6490,6 @@ void Item_trigger_field::cleanup()
}
-/*
- If item is a const function, calculate it and return a const item
- The original item is freed if not returned
-*/
-
Item_result item_cmp_type(Item_result a,Item_result b)
{
if (a == STRING_RESULT && b == STRING_RESULT)
@@ -6430,8 +6597,10 @@ void resolve_const_item(THD *thd, Item **ref, Item *comp_item)
thd->change_item_tree(ref, new_item);
}
-/*
- Return true if the value stored in the field is equal to the const item
+/**
+ Return true if the value stored in the field is equal to the const
+ item.
+
We need to use this on the range optimizer because in some cases
we can't store the value in the field without some precision/character loss.
*/
@@ -6492,13 +6661,13 @@ Item_cache* Item_cache::get_cache(const Item *item)
}
-void Item_cache::print(String *str)
+void Item_cache::print(String *str, enum_query_type query_type)
{
str->append(STRING_WITH_LEN("<cache>("));
if (example)
- example->print(str);
+ example->print(str, query_type);
else
- Item::print(str);
+ Item::print(str, query_type);
str->append(')');
}
@@ -6552,7 +6721,7 @@ longlong Item_cache_real::val_int()
String* Item_cache_real::val_str(String *str)
{
DBUG_ASSERT(fixed == 1);
- str->set(value, decimals, default_charset());
+ str->set_real(value, decimals, default_charset());
return str;
}
@@ -6772,14 +6941,11 @@ Item_type_holder::Item_type_holder(THD *thd, Item *item)
}
-/*
- Return expression type of Item_type_holder
-
- SYNOPSIS
- Item_type_holder::result_type()
+/**
+ Return expression type of Item_type_holder.
- RETURN
- Item_result (type of internal MySQL expression result)
+ @return
+ Item_result (type of internal MySQL expression result)
*/
Item_result Item_type_holder::result_type() const
@@ -6788,13 +6954,10 @@ Item_result Item_type_holder::result_type() const
}
-/*
- Find real field type of item
-
- SYNOPSIS
- Item_type_holder::get_real_type()
+/**
+ Find real field type of item.
- RETURN
+ @return
type of field which should be created to store item value
*/
@@ -6859,17 +7022,16 @@ enum_field_types Item_type_holder::get_real_type(Item *item)
return item->field_type();
}
-/*
+/**
Find field type which can carry current Item_type_holder type and
type of given Item.
- SYNOPSIS
- Item_type_holder::join_types()
- thd thread handler
- item given item to join its parameters with this item ones
+ @param thd thread handler
+ @param item given item to join its parameters with this item ones
- RETURN
+ @retval
TRUE error - types are incompatible
+ @retval
FALSE OK
*/
@@ -6971,14 +7133,12 @@ bool Item_type_holder::join_types(THD *thd, Item *item)
DBUG_RETURN(FALSE);
}
-/*
- Calculate lenth for merging result for given Item type
+/**
+ Calculate lenth for merging result for given Item type.
- SYNOPSIS
- Item_type_holder::real_length()
- item Item for lrngth detection
+ @param item Item for length detection
- RETURN
+ @return
length
*/
@@ -7032,15 +7192,13 @@ uint32 Item_type_holder::display_length(Item *item)
}
-/*
+/**
Make temporary table field according collected information about type
- of UNION result
+ of UNION result.
- SYNOPSIS
- Item_type_holder::make_field_by_type()
- table temporary table for which we create fields
+ @param table temporary table for which we create fields
- RETURN
+ @return
created field
*/
@@ -7050,36 +7208,41 @@ Field *Item_type_holder::make_field_by_type(TABLE *table)
The field functions defines a field to be not null if null_ptr is not 0
*/
uchar *null_ptr= maybe_null ? (uchar*) "" : 0;
- switch (fld_type)
- {
+ Field *field;
+
+ switch (fld_type) {
case MYSQL_TYPE_ENUM:
DBUG_ASSERT(enum_set_typelib);
- return new Field_enum((char *) 0, max_length, null_ptr, 0,
+ field= new Field_enum((uchar *) 0, max_length, null_ptr, 0,
Field::NONE, name,
- table, get_enum_pack_length(enum_set_typelib->count),
+ get_enum_pack_length(enum_set_typelib->count),
enum_set_typelib, collation.collation);
+ if (field)
+ field->init(table);
+ return field;
case MYSQL_TYPE_SET:
DBUG_ASSERT(enum_set_typelib);
- return new Field_set((char *) 0, max_length, null_ptr, 0,
+ field= new Field_set((uchar *) 0, max_length, null_ptr, 0,
Field::NONE, name,
- table, get_set_pack_length(enum_set_typelib->count),
+ get_set_pack_length(enum_set_typelib->count),
enum_set_typelib, collation.collation);
+ if (field)
+ field->init(table);
+ return field;
case MYSQL_TYPE_NULL:
return make_string_field(table);
default:
break;
}
- return tmp_table_field_from_field_type(table);
+ return tmp_table_field_from_field_type(table, 0);
}
-/*
+/**
Get full information from Item about enum/set fields to be able to create
- them later
+ them later.
- SYNOPSIS
- Item_type_holder::get_full_info
- item Item for information collection
+ @param item Item for information collection
*/
void Item_type_holder::get_full_info(Item *item)
{
@@ -7142,26 +7305,21 @@ void Item_result_field::cleanup()
DBUG_VOID_RETURN;
}
-/*
- Dummy error processor used by default by Name_resolution_context
-
- SYNOPSIS
- dummy_error_processor()
+/**
+ Dummy error processor used by default by Name_resolution_context.
- NOTE
+ @note
do nothing
*/
void dummy_error_processor(THD *thd, void *data)
{}
-/*
- Wrapper of hide_view_error call for Name_resolution_context error processor
-
- SYNOPSIS
- view_error_processor()
+/**
+ Wrapper of hide_view_error call for Name_resolution_context error
+ processor.
- NOTE
+ @note
hide view underlying tables details in error messages
*/
diff --git a/sql/item.h b/sql/item.h
index 1058cc5dbb8..d28ee1a29fb 100644
--- a/sql/item.h
+++ b/sql/item.h
@@ -1,4 +1,4 @@
-/* Copyright (C) 2000-2006 MySQL AB
+/* Copyright 2000-2008 MySQL AB, 2008 Sun Microsystems, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -113,7 +113,6 @@ public:
}
};
-
/*************************************************************************/
/*
A framework to easily handle different return types for hybrid items
@@ -378,6 +377,35 @@ public:
}
};
+
+/*
+ This enum is used to report information about monotonicity of function
+ represented by Item* tree.
+ Monotonicity is defined only for Item* trees that represent table
+ partitioning expressions (i.e. have no subselects/user vars/PS parameters
+ etc etc). An Item* tree is assumed to have the same monotonicity properties
+ as its correspoinding function F:
+
+ [signed] longlong F(field1, field2, ...) {
+ put values of field_i into table record buffer;
+ return item->val_int();
+ }
+
+ NOTE
+ At the moment function monotonicity is not well defined (and so may be
+ incorrect) for Item trees with parameters/return types that are different
+ from INT_RESULT, may be NULL, or are unsigned.
+ It will be possible to address this issue once the related partitioning bugs
+ (BUG#16002, BUG#15447, BUG#13436) are fixed.
+*/
+
+typedef enum monotonicity_info
+{
+ NON_MONOTONIC, /* none of the below holds */
+ MONOTONIC_INCREASING, /* F() is unary and (x < y) => (F(x) <= F(y)) */
+ MONOTONIC_STRICT_INCREASING /* F() is unary and (x < y) => (F(x) < F(y)) */
+} enum_monotonicity_info;
+
/*************************************************************************/
class sp_rcontext;
@@ -418,7 +446,7 @@ public:
};
-typedef bool (Item::*Item_processor) (byte *arg);
+typedef bool (Item::*Item_processor) (uchar *arg);
/*
Analyzer function
SYNOPSIS
@@ -430,8 +458,8 @@ typedef bool (Item::*Item_processor) (byte *arg);
FALSE Don't do it
*/
-typedef bool (Item::*Item_analyzer) (byte **argp);
-typedef Item* (Item::*Item_transformer) (byte *arg);
+typedef bool (Item::*Item_analyzer) (uchar **argp);
+typedef Item* (Item::*Item_transformer) (uchar *arg);
typedef void (*Cond_traverser) (const Item *item, void *arg);
@@ -440,9 +468,9 @@ class Item {
void operator=(Item &);
public:
static void *operator new(size_t size) throw ()
- { return (void*) sql_alloc((uint) size); }
+ { return sql_alloc(size); }
static void *operator new(size_t size, MEM_ROOT *mem_root) throw ()
- { return (void*) alloc_root(mem_root, (uint) size); }
+ { return alloc_root(mem_root, size); }
static void operator delete(void *ptr,size_t size) { TRASH(ptr, size); }
static void operator delete(void *ptr, MEM_ROOT *mem_root) {}
@@ -453,6 +481,7 @@ public:
FIELD_VARIANCE_ITEM, INSERT_VALUE_ITEM,
SUBSELECT_ITEM, ROW_ITEM, CACHE_ITEM, TYPE_HOLDER,
PARAM_ITEM, TRIGGER_FIELD_ITEM, DECIMAL_ITEM,
+ XPATH_NODESET, XPATH_NODESET_CMP,
VIEW_FIXER_ITEM};
enum cond_result { COND_UNDEF,COND_OK,COND_TRUE,COND_FALSE };
@@ -467,9 +496,9 @@ public:
save_in_field
*/
String str_value;
- my_string name; /* Name from select */
+ char * name; /* Name from select */
/* Original item name (if it was renamed)*/
- my_string orig_name;
+ char * orig_name;
Item *next;
uint32 max_length;
uint name_length; /* Length of name */
@@ -527,8 +556,55 @@ public:
virtual bool eq(const Item *, bool binary_cmp) const;
virtual Item_result result_type() const { return REAL_RESULT; }
virtual Item_result cast_to_int_type() const { return result_type(); }
+ virtual enum_field_types string_field_type() const;
virtual enum_field_types field_type() const;
virtual enum Type type() const =0;
+
+ /*
+ Return information about function monotonicity. See comment for
+ enum_monotonicity_info for details. This function can only be called
+ after fix_fields() call.
+ */
+ virtual enum_monotonicity_info get_monotonicity_info() const
+ { return NON_MONOTONIC; }
+
+ /*
+ Convert "func_arg $CMP$ const" half-interval into "FUNC(func_arg) $CMP2$ const2"
+
+ SYNOPSIS
+ val_int_endpoint()
+ left_endp FALSE <=> The interval is "x < const" or "x <= const"
+ TRUE <=> The interval is "x > const" or "x >= const"
+
+ incl_endp IN TRUE <=> the comparison is '<' or '>'
+ FALSE <=> the comparison is '<=' or '>='
+ OUT The same but for the "F(x) $CMP$ F(const)" comparison
+
+ DESCRIPTION
+ This function is defined only for unary monotonic functions. The caller
+ supplies the source half-interval
+
+ x $CMP$ const
+
+ The value of const is supplied implicitly as the value this item's
+ argument, the form of $CMP$ comparison is specified through the
+ function's arguments. The calle returns the result interval
+
+ F(x) $CMP2$ F(const)
+
+ passing back F(const) as the return value, and the form of $CMP2$
+ through the out parameter. NULL values are assumed to be comparable and
+ be less than any non-NULL values.
+
+ RETURN
+ The output range bound, which equal to the value of val_int()
+ - If the value of the function is NULL then the bound is the
+ smallest possible value of LONGLONG_MIN
+ */
+ virtual longlong val_int_endpoint(bool left_endp, bool *incl_endp)
+ { DBUG_ASSERT(0); return 0; }
+
+
/* valXXX methods must return NULL or 0 or 0.0 if null_value is set. */
/*
Return double precision floating point representation of item.
@@ -619,6 +695,7 @@ public:
TRUE value is true (not equal to 0)
*/
virtual bool val_bool();
+ virtual String *val_nodeset(String*) { return 0; }
/* Helper functions, see item_sum.cc */
String *val_string_from_real(String *str);
String *val_string_from_int(String *str);
@@ -692,20 +769,24 @@ public:
*/
virtual bool const_during_execution() const
{ return (used_tables() & ~PARAM_TABLE_BIT) == 0; }
- /*
- This is an essential method for correct functioning of VIEWS.
- To save a view in an .frm file we need its unequivocal
- definition in SQL that takes into account sql_mode and
- environmental settings. Currently such definition is restored
- by traversing through the parsed tree of a view and
- print()'ing SQL syntax of every node to a String buffer. This
- method is used to print the SQL definition of an item. The
- second use of this method is for EXPLAIN EXTENDED, to print
- the SQL of a query after all optimizations of the parsed tree
- have been done.
- */
- virtual void print(String *str_arg) { str_arg->append(full_name()); }
- void print_item_w_name(String *);
+
+ /**
+ This method is used for to:
+ - to generate a view definition query (SELECT-statement);
+ - to generate a SQL-query for EXPLAIN EXTENDED;
+ - to generate a SQL-query to be shown in INFORMATION_SCHEMA;
+ - debug.
+
+ For more information about view definition query, INFORMATION_SCHEMA
+ query and why they should be generated from the Item-tree, @see
+ mysql_register_view().
+ */
+ virtual inline void print(String *str, enum_query_type query_type)
+ {
+ str->append(full_name());
+ }
+
+ void print_item_w_name(String *, enum_query_type query_type);
virtual void update_used_tables() {}
virtual void split_sum_func(THD *thd, Item **ref_pointer_array,
List<Item> &fields) {}
@@ -761,12 +842,12 @@ public:
static CHARSET_INFO *default_charset();
virtual CHARSET_INFO *compare_collation() { return NULL; }
- virtual bool walk(Item_processor processor, byte *arg)
+ virtual bool walk(Item_processor processor, bool walk_subquery, uchar *arg)
{
return (this->*processor)(arg);
}
- virtual Item* transform(Item_transformer transformer, byte *arg);
+ virtual Item* transform(Item_transformer transformer, uchar *arg);
/*
This function performs a generic "compilation" of the Item tree.
@@ -784,8 +865,8 @@ public:
i.e. analysis is performed top-down while transformation is done
bottom-up.
*/
- virtual Item* compile(Item_analyzer analyzer, byte **arg_p,
- Item_transformer transformer, byte *arg_t)
+ virtual Item* compile(Item_analyzer analyzer, uchar **arg_p,
+ Item_transformer transformer, uchar *arg_t)
{
if ((this->*analyzer) (arg_p))
return ((this->*transformer) (arg_t));
@@ -798,24 +879,77 @@ public:
(*traverser)(this, arg);
}
- virtual bool remove_dependence_processor(byte * arg) { return 0; }
- virtual bool remove_fixed(byte * arg) { fixed= 0; return 0; }
- virtual bool cleanup_processor(byte *arg);
- virtual bool collect_item_field_processor(byte * arg) { return 0; }
- virtual bool find_item_in_field_list_processor(byte *arg) { return 0; }
- virtual bool change_context_processor(byte *context) { return 0; }
- virtual bool reset_query_id_processor(byte *query_id_arg) { return 0; }
- virtual bool is_expensive_processor(byte *arg) { return 0; }
- virtual bool subst_argument_checker(byte **arg)
+ virtual bool remove_dependence_processor(uchar * arg) { return 0; }
+ virtual bool remove_fixed(uchar * arg) { fixed= 0; return 0; }
+ virtual bool cleanup_processor(uchar *arg);
+ virtual bool collect_item_field_processor(uchar * arg) { return 0; }
+ virtual bool find_item_in_field_list_processor(uchar *arg) { return 0; }
+ virtual bool change_context_processor(uchar *context) { return 0; }
+ virtual bool reset_query_id_processor(uchar *query_id_arg) { return 0; }
+ virtual bool is_expensive_processor(uchar *arg) { return 0; }
+ virtual bool register_field_in_read_map(uchar *arg) { return 0; }
+ /*
+ Check if a partition function is allowed
+ SYNOPSIS
+ check_partition_func_processor()
+ int_arg Ignored
+ RETURN VALUE
+ TRUE Partition function not accepted
+ FALSE Partition function accepted
+
+ DESCRIPTION
+ check_partition_func_processor is used to check if a partition function
+ uses an allowed function. An allowed function will always ensure that
+ X=Y guarantees that also part_function(X)=part_function(Y) where X is
+ a set of partition fields and so is Y. The problems comes mainly from
+ character sets where two equal strings can be quite unequal. E.g. the
+ german character for double s is equal to 2 s.
+
+ The default is that an item is not allowed
+ in a partition function. Allowed functions
+ can never depend on server version, they cannot depend on anything
+ related to the environment. They can also only depend on a set of
+ fields in the table itself. They cannot depend on other tables and
+ cannot contain any queries and cannot contain udf's or similar.
+ If a new Item class is defined and it inherits from a class that is
+ allowed in a partition function then it is very important to consider
+ whether this should be inherited to the new class. If not the function
+ below should be defined in the new Item class.
+
+ The general behaviour is that most integer functions are allowed.
+ If the partition function contains any multi-byte collations then
+ the function check_part_func_fields will report an error on the
+ partition function independent of what functions are used. So the
+ only character sets allowed are single character collation and
+ even for those only a limited set of functions are allowed. The
+ problem with multi-byte collations is that almost every string
+ function has the ability to change things such that two strings
+ that are equal will not be equal after manipulated by a string
+ function. E.g. two strings one contains a double s, there is a
+ special german character that is equal to two s. Now assume a
+ string function removes one character at this place, then in
+ one the double s will be removed and in the other there will
+ still be one s remaining and the strings are no longer equal
+ and thus the partition function will not sort equal strings into
+ the same partitions.
+
+ So the check if a partition function is valid is two steps. First
+ check that the field types are valid, next check that the partition
+ function is valid. The current set of partition functions valid
+ assumes that there are no multi-byte collations amongst the partition
+ fields.
+ */
+ virtual bool check_partition_func_processor(uchar *bool_arg) { return TRUE;}
+ virtual bool subst_argument_checker(uchar **arg)
{
if (*arg)
*arg= NULL;
return TRUE;
}
- virtual Item *equal_fields_propagator(byte * arg) { return this; }
- virtual bool set_no_const_sub(byte *arg) { return FALSE; }
- virtual Item *replace_equal_field(byte * arg) { return this; }
+ virtual Item *equal_fields_propagator(uchar * arg) { return this; }
+ virtual bool set_no_const_sub(uchar *arg) { return FALSE; }
+ virtual Item *replace_equal_field(uchar * arg) { return this; }
/*
For SP local variable returns pointer to Item representing its
@@ -840,11 +974,11 @@ public:
// used in row subselects to get value of elements
virtual void bring_value() {}
- Field *tmp_table_field_from_field_type(TABLE *table);
+ Field *tmp_table_field_from_field_type(TABLE *table, bool fixed_length);
virtual Item_field *filed_for_view_update() { return 0; }
virtual Item *neg_transformer(THD *thd) { return NULL; }
- virtual Item *update_value_transformer(byte *select_arg) { return this; }
+ virtual Item *update_value_transformer(uchar *select_arg) { return this; }
virtual Item *safe_charset_converter(CHARSET_INFO *tocs);
void delete_self()
{
@@ -958,7 +1092,7 @@ inline void Item_sp_variable::make_field(Send_field *field)
if (name)
it->set_name(name, (uint) strlen(name), system_charset_info);
else
- it->set_name(m_name.str, m_name.length, system_charset_info);
+ it->set_name(m_name.str, (uint) m_name.length, system_charset_info);
it->make_field(field);
}
@@ -1022,7 +1156,7 @@ public:
const Item *this_item() const;
Item **this_item_addr(THD *thd, Item **);
- void print(String *str);
+ virtual void print(String *str, enum_query_type query_type);
public:
inline const LEX_STRING *my_name() const;
@@ -1075,7 +1209,7 @@ inline Item_result Item_splocal::result_type() const
class Item_case_expr :public Item_sp_variable
{
public:
- Item_case_expr(int case_expr_id);
+ Item_case_expr(uint case_expr_id);
public:
Item *this_item();
@@ -1091,10 +1225,10 @@ public:
Item_case_expr can not occur in views, so here it is only for debug
purposes.
*/
- void print(String *str);
+ virtual void print(String *str, enum_query_type query_type);
private:
- int m_case_expr_id;
+ uint m_case_expr_id;
};
/*****************************************************************************
@@ -1142,7 +1276,7 @@ public:
String *val_str(String *sp);
my_decimal *val_decimal(my_decimal *);
bool is_null();
- void print(String *str);
+ virtual void print(String *str, enum_query_type query_type);
Item_result result_type() const
{
@@ -1179,6 +1313,7 @@ public:
Item_num() {} /* 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;}
};
#define NO_CACHED_FIELD_INDEX ((uint)(-1))
@@ -1222,9 +1357,9 @@ public:
Item_ident(THD *thd, Item_ident *item);
const char *full_name() const;
void cleanup();
- bool remove_dependence_processor(byte * arg);
- void print(String *str);
- virtual bool change_context_processor(byte *cntx)
+ bool remove_dependence_processor(uchar * arg);
+ virtual void print(String *str, enum_query_type query_type);
+ virtual bool change_context_processor(uchar *cntx)
{ context= (Name_resolution_context *)cntx; return FALSE; }
friend bool insert_fields(THD *thd, Name_resolution_context *context,
const char *db_name,
@@ -1322,6 +1457,11 @@ public:
{
return field->type();
}
+ enum_monotonicity_info get_monotonicity_info() const
+ {
+ return MONOTONIC_STRICT_INCREASING;
+ }
+ 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);
@@ -1330,31 +1470,26 @@ public:
bool is_null() { return field->is_null(); }
void update_null_value();
Item *get_tmp_table_item(THD *thd);
- bool collect_item_field_processor(byte * arg);
- bool find_item_in_field_list_processor(byte *arg);
- bool reset_query_id_processor(byte *arg)
- {
- field->query_id= *((query_id_t *) arg);
- if (result_field)
- result_field->query_id= field->query_id;
- return 0;
- }
+ bool collect_item_field_processor(uchar * arg);
+ bool find_item_in_field_list_processor(uchar *arg);
+ bool register_field_in_read_map(uchar *arg);
+ bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
void cleanup();
bool result_as_longlong()
{
return field->can_be_compared_as_longlong();
}
Item_equal *find_item_equal(COND_EQUAL *cond_equal);
- bool subst_argument_checker(byte **arg);
- Item *equal_fields_propagator(byte *arg);
- bool set_no_const_sub(byte *arg);
- Item *replace_equal_field(byte *arg);
+ bool subst_argument_checker(uchar **arg);
+ Item *equal_fields_propagator(uchar *arg);
+ bool set_no_const_sub(uchar *arg);
+ Item *replace_equal_field(uchar *arg);
inline uint32 max_disp_length() { return field->max_display_length(); }
Item_field *filed_for_view_update() { return this; }
Item *safe_charset_converter(CHARSET_INFO *tocs);
int fix_outer_field(THD *thd, Field **field, Item **reference);
- virtual Item *update_value_transformer(byte *select_arg);
- void print(String *str);
+ virtual Item *update_value_transformer(uchar *select_arg);
+ virtual void print(String *str, enum_query_type query_type);
Field::geometry_type get_geometry_type() const
{
DBUG_ASSERT(field_type() == MYSQL_TYPE_GEOMETRY);
@@ -1390,8 +1525,14 @@ public:
bool basic_const_item() const { return 1; }
Item *clone_item() { return new Item_null(name); }
bool is_null() { return 1; }
- void print(String *str) { str->append(STRING_WITH_LEN("NULL")); }
+
+ virtual inline void print(String *str, enum_query_type query_type)
+ {
+ str->append(STRING_WITH_LEN("NULL"));
+ }
+
Item *safe_charset_converter(CHARSET_INFO *tocs);
+ bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
};
class Item_null_result :public Item_null
@@ -1404,6 +1545,7 @@ public:
{
save_in_field(result_field, no_conversions);
}
+ bool check_partition_func_processor(uchar *int_arg) {return TRUE;}
};
/* Item represents one placeholder ('?') of prepared statement */
@@ -1520,7 +1662,7 @@ public:
*/
virtual table_map used_tables() const
{ return state != NO_VALUE ? (table_map)0 : PARAM_TABLE_BIT; }
- void print(String *str);
+ virtual void print(String *str, enum_query_type query_type);
bool is_null()
{ DBUG_ASSERT(state != NO_VALUE); return state == NULL_VALUE; }
bool basic_const_item() const;
@@ -1544,6 +1686,7 @@ public:
bool eq(const Item *item, bool binary_cmp) const;
/** Item is a argument to a limit clause. */
bool limit_clause_param;
+ void set_param_type_and_swap_value(Item_param *from);
};
@@ -1573,11 +1716,12 @@ public:
int save_in_field(Field *field, bool no_conversions);
bool basic_const_item() const { return 1; }
Item *clone_item() { return new Item_int(name,value,max_length); }
- void print(String *str);
+ virtual void print(String *str, enum_query_type query_type);
Item_num *neg() { value= -value; return this; }
uint decimal_precision() const
{ return (uint)(max_length - test(value < 0)); }
bool eq(const Item *, bool binary_cmp) const;
+ bool check_partition_func_processor(uchar *bool_arg) { return FALSE;}
};
@@ -1592,9 +1736,10 @@ public:
String *val_str(String*);
Item *clone_item() { return new Item_uint(name, value, max_length); }
int save_in_field(Field *field, bool no_conversions);
- void print(String *str);
+ virtual void print(String *str, enum_query_type query_type);
Item_num *neg ();
uint decimal_precision() const { return max_length; }
+ bool check_partition_func_processor(uchar *bool_arg) { return FALSE;}
};
@@ -1610,7 +1755,7 @@ public:
Item_decimal(my_decimal *value_par);
Item_decimal(longlong val, bool unsig);
Item_decimal(double val, int precision, int scale);
- Item_decimal(const char *bin, int precision, int scale);
+ Item_decimal(const uchar *bin, int precision, int scale);
enum Type type() const { return DECIMAL_ITEM; }
enum Item_result result_type () const { return DECIMAL_RESULT; }
@@ -1625,7 +1770,7 @@ public:
{
return new Item_decimal(name, &decimal_value, decimals, max_length);
}
- void print(String *str);
+ virtual void print(String *str, enum_query_type query_type);
Item_num *neg()
{
my_decimal_neg(&decimal_value);
@@ -1635,6 +1780,7 @@ public:
uint decimal_precision() const { return decimal_value.precision(); }
bool eq(const Item *, bool binary_cmp) const;
void set_decimal_value(my_decimal *value_par);
+ bool check_partition_func_processor(uchar *bool_arg) { return FALSE;}
};
@@ -1653,8 +1799,11 @@ public:
max_length=length;
fixed= 1;
}
- Item_float(double value_par) :presentation(0), value(value_par) { fixed= 1; }
-
+ Item_float(double value_par, uint decimal_par) :presentation(0), value(value_par)
+ {
+ decimals= (uint8) decimal_par;
+ fixed= 1;
+ }
int save_in_field(Field *field, bool no_conversions);
enum Type type() const { return REAL_ITEM; }
enum_field_types field_type() const { return MYSQL_TYPE_DOUBLE; }
@@ -1678,7 +1827,7 @@ public:
Item *clone_item()
{ return new Item_float(name, value, decimals, max_length); }
Item_num *neg() { value= -value; return this; }
- void print(String *str);
+ virtual void print(String *str, enum_query_type query_type);
bool eq(const Item *, bool binary_cmp) const;
};
@@ -1691,7 +1840,12 @@ public:
uint length)
:Item_float(NullS, val_arg, decimal_par, length), func_name(str)
{}
- void print(String *str) { str->append(func_name); }
+
+ virtual inline void print(String *str, enum_query_type query_type)
+ {
+ str->append(func_name);
+ }
+
Item *safe_charset_converter(CHARSET_INFO *tocs);
};
@@ -1702,6 +1856,7 @@ public:
Item_string(const char *str,uint length,
CHARSET_INFO *cs, Derivation dv= DERIVATION_COERCIBLE,
uint repertoire= MY_REPERTOIRE_UNICODE30)
+ : m_cs_specified(FALSE)
{
str_value.set_or_copy_aligned(str, length, cs);
collation.set(cs, dv, repertoire);
@@ -1709,7 +1864,7 @@ public:
We have to have a different max_length than 'length' here to
ensure that we get the right length if we do use the item
to create a new table. In this case max_length must be the maximum
- number of chars for a string of this type because we in create_field::
+ number of chars for a string of this type because we in Create_field::
divide the max_length with mbmaxlen).
*/
max_length= str_value.numchars()*cs->mbmaxlen;
@@ -1720,6 +1875,7 @@ public:
}
/* Just create an item and do not fill string representation */
Item_string(CHARSET_INFO *cs, Derivation dv= DERIVATION_COERCIBLE)
+ : m_cs_specified(FALSE)
{
collation.set(cs, dv);
max_length= 0;
@@ -1730,6 +1886,7 @@ public:
Item_string(const char *name_par, const char *str, uint length,
CHARSET_INFO *cs, Derivation dv= DERIVATION_COERCIBLE,
uint repertoire= MY_REPERTOIRE_UNICODE30)
+ : m_cs_specified(FALSE)
{
str_value.set_or_copy_aligned(str, length, cs);
collation.set(cs, dv, repertoire);
@@ -1779,10 +1936,58 @@ public:
str_value.append(str, length);
max_length= str_value.numchars() * collation.collation->mbmaxlen;
}
- void print(String *str);
+ virtual void print(String *str, enum_query_type query_type);
+ bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
+
+ /**
+ Return TRUE if character-set-introducer was explicitly specified in the
+ original query for this item (text literal).
+
+ This operation is to be called from Item_string::print(). The idea is
+ that when a query is generated (re-constructed) from the Item-tree,
+ character-set-introducers should appear only for those literals, where
+ they were explicitly specified by the user. Otherwise, that may lead to
+ loss collation information (character set introducers implies default
+ collation for the literal).
+
+ Basically, that makes sense only for views and hopefully will be gone
+ one day when we start using original query as a view definition.
+
+ @return This operation returns the value of m_cs_specified attribute.
+ @retval TRUE if character set introducer was explicitly specified in
+ the original query.
+ @retval FALSE otherwise.
+ */
+ inline bool is_cs_specified() const
+ {
+ return m_cs_specified;
+ }
+
+ /**
+ Set the value of m_cs_specified attribute.
+
+ m_cs_specified attribute shows whether character-set-introducer was
+ explicitly specified in the original query for this text literal or
+ not. The attribute makes sense (is used) only for views.
+
+ This operation is to be called from the parser during parsing an input
+ query.
+ */
+ inline void set_cs_specified(bool cs_specified)
+ {
+ m_cs_specified= cs_specified;
+ }
+
+private:
+ bool m_cs_specified;
};
+longlong
+longlong_from_string_with_check (CHARSET_INFO *cs, const char *cptr, char *end);
+double
+double_from_string_with_check (CHARSET_INFO *cs, const char *cptr, char *end);
+
class Item_static_string_func :public Item_string
{
const char *func_name;
@@ -1793,43 +1998,73 @@ public:
:Item_string(NullS, str, length, cs, dv), func_name(name_par)
{}
Item *safe_charset_converter(CHARSET_INFO *tocs);
- void print(String *str) { str->append(func_name); }
+
+ virtual inline void print(String *str, enum_query_type query_type)
+ {
+ str->append(func_name);
+ }
+
+ bool check_partition_func_processor(uchar *int_arg) {return TRUE;}
};
/* for show tables */
+class Item_partition_func_safe_string: public Item_string
+{
+public:
+ Item_partition_func_safe_string(const char *name, uint length,
+ CHARSET_INFO *cs= NULL):
+ Item_string(name, length, cs)
+ {}
+};
+
+
+class Item_return_date_time :public Item_partition_func_safe_string
+{
+ enum_field_types date_time_field_type;
+public:
+ Item_return_date_time(const char *name_arg, enum_field_types field_type_arg)
+ :Item_partition_func_safe_string(name_arg, 0, &my_charset_bin),
+ date_time_field_type(field_type_arg)
+ { }
+ enum_field_types field_type() const { return date_time_field_type; }
+};
-class Item_datetime :public Item_string
+
+class Item_blob :public Item_partition_func_safe_string
{
public:
- Item_datetime(const char *item_name): Item_string(item_name,"",0,
- &my_charset_bin)
- { max_length=19;}
- enum_field_types field_type() const { return MYSQL_TYPE_DATETIME; }
+ Item_blob(const char *name, uint length) :
+ Item_partition_func_safe_string(name, length, &my_charset_bin)
+ { max_length= length; }
+ enum Type type() const { return TYPE_HOLDER; }
+ enum_field_types field_type() const { return MYSQL_TYPE_BLOB; }
};
+
/**
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
the client.
*/
-class Item_empty_string :public Item_string
+class Item_empty_string :public Item_partition_func_safe_string
{
public:
Item_empty_string(const char *header,uint length, CHARSET_INFO *cs= NULL) :
- Item_string("",0, cs ? cs : &my_charset_utf8_general_ci)
+ Item_partition_func_safe_string("",0, cs ? cs : &my_charset_utf8_general_ci)
{ name=(char*) header; max_length= length * collation.collation->mbmaxlen; }
void make_field(Send_field *field);
};
+
class Item_return_int :public Item_int
{
enum_field_types int_field_type;
public:
Item_return_int(const char *name_arg, uint length,
- enum_field_types field_type_arg)
- :Item_int(name_arg, 0, length), int_field_type(field_type_arg)
+ enum_field_types field_type_arg, longlong value= 0)
+ :Item_int(name_arg, value, length), int_field_type(field_type_arg)
{
unsigned_flag=1;
}
@@ -1856,9 +2091,10 @@ public:
enum Item_result result_type () const { return STRING_RESULT; }
enum Item_result cast_to_int_type() const { return INT_RESULT; }
enum_field_types field_type() const { return MYSQL_TYPE_VARCHAR; }
- void print(String *str);
+ virtual void print(String *str, enum_query_type query_type);
bool eq(const Item *item, bool binary_cmp) const;
virtual Item *safe_charset_converter(CHARSET_INFO *tocs);
+ bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
};
@@ -1897,7 +2133,7 @@ class Item_ref :public Item_ident
protected:
void set_properties();
public:
- enum Ref_Type { REF, DIRECT_REF, VIEW_REF, OUTER_REF };
+ enum Ref_Type { REF, DIRECT_REF, VIEW_REF, OUTER_REF, AGGREGATE_REF };
Field *result_field; /* Save result here */
Item **ref;
Item_ref(Name_resolution_context *context_arg,
@@ -1975,9 +2211,9 @@ public:
{
return ref ? (*ref)->real_item() : this;
}
- bool walk(Item_processor processor, byte *arg)
- { return (*ref)->walk(processor, arg); }
- void print(String *str);
+ bool walk(Item_processor processor, bool walk_subquery, uchar *arg)
+ { return (*ref)->walk(processor, walk_subquery, arg); }
+ virtual void print(String *str, enum_query_type query_type);
bool result_as_longlong()
{
return (*ref)->result_as_longlong();
@@ -2153,7 +2389,7 @@ public:
my_decimal *val_decimal(my_decimal *);
bool val_bool();
bool get_date(MYSQL_TIME *ltime, uint fuzzydate);
- void print(String *str);
+ virtual void print(String *str, enum_query_type query_type);
/*
we add RAND_TABLE_BIT to prevent moving this item from HAVING to WHERE
*/
@@ -2191,7 +2427,7 @@ public:
virtual Item *real_item() { return ref; }
};
-
+#ifdef MYSQL_SERVER
#include "gstream.h"
#include "spatial.h"
#include "item_sum.h"
@@ -2201,8 +2437,9 @@ public:
#include "item_strfunc.h"
#include "item_geofunc.h"
#include "item_timefunc.h"
-#include "item_uniq.h"
#include "item_subselect.h"
+#include "item_xmlfunc.h"
+#endif
class Item_copy_string :public Item
{
@@ -2299,7 +2536,7 @@ public:
class Cached_item_field :public Cached_item
{
- char *buff;
+ uchar *buff;
Field *field;
uint length;
@@ -2307,7 +2544,7 @@ public:
Cached_item_field(Item_field *item)
{
field= item->field;
- buff= (char*) sql_calloc(length=field->pack_length());
+ buff= (uchar*) sql_calloc(length=field->pack_length());
}
bool cmp(void);
};
@@ -2327,17 +2564,17 @@ public:
enum Type type() const { return DEFAULT_VALUE_ITEM; }
bool eq(const Item *item, bool binary_cmp) const;
bool fix_fields(THD *, Item **);
- void print(String *str);
+ virtual void print(String *str, enum_query_type query_type);
int save_in_field(Field *field_arg, bool no_conversions);
table_map used_tables() const { return (table_map)0L; }
- bool walk(Item_processor processor, byte *args)
+ bool walk(Item_processor processor, bool walk_subquery, uchar *args)
{
- return arg->walk(processor, args) ||
+ return arg->walk(processor, walk_subquery, args) ||
(this->*processor)(args);
}
- Item *transform(Item_transformer transformer, byte *args);
+ Item *transform(Item_transformer transformer, uchar *args);
};
/*
@@ -2360,7 +2597,7 @@ public:
arg(a) {}
bool eq(const Item *item, bool binary_cmp) const;
bool fix_fields(THD *, Item **);
- void print(String *str);
+ virtual void print(String *str, enum_query_type query_type);
int save_in_field(Field *field_arg, bool no_conversions)
{
return Item_field::save_in_field(field_arg, no_conversions);
@@ -2371,9 +2608,9 @@ public:
*/
table_map used_tables() const { return RAND_TABLE_BIT; }
- bool walk(Item_processor processor, byte *args)
+ bool walk(Item_processor processor, bool walk_subquery, uchar *args)
{
- return arg->walk(processor, args) ||
+ return arg->walk(processor, walk_subquery, args) ||
(this->*processor)(args);
}
};
@@ -2431,7 +2668,7 @@ public:
enum Type type() const { return TRIGGER_FIELD_ITEM; }
bool eq(const Item *item, bool binary_cmp) const;
bool fix_fields(THD *, Item **);
- void print(String *str);
+ virtual void print(String *str, enum_query_type query_type);
table_map used_tables() const { return (table_map)0L; }
Field *get_tmp_table_field() { return 0; }
Item *copy_or_same(THD *thd) { return this; }
@@ -2487,10 +2724,19 @@ protected:
For all other uses of Item_cache, cached_field doesn't matter.
*/
Field *cached_field;
+ enum enum_field_types cached_field_type;
public:
- Item_cache(): example(0), used_table_map(0), cached_field(0)
+ Item_cache():
+ example(0), used_table_map(0), cached_field(0), cached_field_type(MYSQL_TYPE_STRING)
{
- fixed= 1; null_value= 1;
+ fixed= 1;
+ 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)
+ {
+ fixed= 1;
+ null_value= 1;
}
void set_used_tables(table_map map) { used_table_map= map; }
@@ -2509,10 +2755,11 @@ public:
};
virtual void store(Item *)= 0;
enum Type type() const { return CACHE_ITEM; }
+ enum_field_types field_type() const { return cached_field_type; }
static Item_cache* get_cache(const Item *item);
table_map used_tables() const { return used_table_map; }
virtual void keep_array() {}
- void print(String *str);
+ virtual void print(String *str, enum_query_type query_type);
bool eq_def(Field *field)
{
return cached_field ? cached_field->eq_def (field) : FALSE;
@@ -2530,6 +2777,8 @@ protected:
longlong value;
public:
Item_cache_int(): Item_cache(), value(0) {}
+ Item_cache_int(enum_field_types field_type_arg):
+ Item_cache(field_type_arg), value(0) {}
void store(Item *item);
void store(Item *item, longlong val_arg);
diff --git a/sql/item_buff.cc b/sql/item_buff.cc
index c162b84f457..2f45d0a17c2 100644
--- a/sql/item_buff.cc
+++ b/sql/item_buff.cc
@@ -14,12 +14,17 @@
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
-/* Buffers to save and compare item values */
+/**
+ @file
+
+ @brief
+ Buffers to save and compare item values
+*/
#include "mysql_priv.h"
-/*
-** Create right type of Cached_item for an item
+/**
+ Create right type of Cached_item for an item.
*/
Cached_item *new_Cached_item(THD *thd, Item *item)
@@ -45,9 +50,11 @@ Cached_item *new_Cached_item(THD *thd, Item *item)
Cached_item::~Cached_item() {}
-/*
-** Compare with old value and replace value with new value
-** Return true if values have changed
+/**
+ Compare with old value and replace value with new value.
+
+ @return
+ Return true if values have changed
*/
Cached_item_str::Cached_item_str(THD *thd, Item *arg)
diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc
index 3b1d18b4252..bd90dd81365 100644
--- a/sql/item_cmpfunc.cc
+++ b/sql/item_cmpfunc.cc
@@ -14,7 +14,12 @@
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
-/* This file defines all compare functions */
+/**
+ @file
+
+ @brief
+ This file defines all compare functions
+*/
#ifdef USE_PRAGMA_IMPLEMENTATION
#pragma implementation // gcc: Class implementation
@@ -24,8 +29,7 @@
#include <m_ctype.h>
#include "sql_select.h"
-static bool convert_constant_item(THD *thd, Item_field *field_item,
- Item **item);
+static bool convert_constant_item(THD *, Item_field *, Item **);
static Item_result item_store_type(Item_result a, Item *item,
my_bool unsigned_flag)
@@ -105,12 +109,11 @@ static int cmp_row_type(Item* item1, Item* item2)
}
-/*
+/**
Aggregates result types from the array of items.
SYNOPSIS:
agg_cmp_type()
- thd thread handle
type [out] the aggregated type
items array of items to aggregate the type from
nitems number of items in the array
@@ -119,15 +122,17 @@ static int cmp_row_type(Item* item1, Item* item2)
This function aggregates result types from the array of items. Found type
supposed to be used later for comparison of values of these items.
Aggregation itself is performed by the item_cmp_type() function.
- The function also checks compatibility of row signatures for the
- submitted items (see the spec for the cmp_row_type function).
+ @param[out] type the aggregated type
+ @param items array of items to aggregate the type from
+ @param nitems number of items in the array
- RETURN VALUES
+ @retval
1 type incompatibility has been detected
+ @retval
0 otherwise
*/
-static int agg_cmp_type(THD *thd, Item_result *type, Item **items, uint nitems)
+static int agg_cmp_type(Item_result *type, Item **items, uint nitems)
{
uint i;
type[0]= items[0]->result_type();
@@ -177,6 +182,41 @@ enum_field_types agg_field_type(Item **items, uint nitems)
return res;
}
+/*
+ Collects different types for comparison of first item with each other items
+
+ SYNOPSIS
+ collect_cmp_types()
+ items Array of items to collect types from
+ nitems Number of items in the array
+
+ DESCRIPTION
+ This function collects different result types for comparison of the first
+ item in the list with each of the remaining items in the 'items' array.
+
+ RETURN
+ 0 - if row type incompatibility has been detected (see cmp_row_type)
+ Bitmap of collected types - otherwise
+*/
+
+static uint collect_cmp_types(Item **items, uint nitems)
+{
+ uint i;
+ uint found_types;
+ Item_result left_result= items[0]->result_type();
+ DBUG_ASSERT(nitems > 1);
+ found_types= 0;
+ for (i= 1; i < nitems ; i++)
+ {
+ if ((left_result == ROW_RESULT ||
+ items[i]->result_type() == ROW_RESULT) &&
+ cmp_row_type(items[0], items[i]))
+ return 0;
+ found_types|= 1<< (uint)item_cmp_type(left_result,
+ items[i]->result_type());
+ }
+ return found_types;
+}
static void my_coll_agg_error(DTCollation &c1, DTCollation &c2,
const char *fname)
@@ -246,17 +286,18 @@ longlong Item_func_not::val_int()
higher than the precedence of NOT.
*/
-void Item_func_not::print(String *str)
+void Item_func_not::print(String *str, enum_query_type query_type)
{
str->append('(');
- Item_func::print(str);
+ Item_func::print(str, query_type);
str->append(')');
}
-/*
- special NOT for ALL subquery
+/**
+ special NOT for ALL subquery.
*/
+
longlong Item_func_not_all::val_int()
{
DBUG_ASSERT(fixed == 1);
@@ -280,19 +321,22 @@ bool Item_func_not_all::empty_underlying_subquery()
(test_sub_item && !test_sub_item->any_value()));
}
-void Item_func_not_all::print(String *str)
+void Item_func_not_all::print(String *str, enum_query_type query_type)
{
if (show)
- Item_func::print(str);
+ Item_func::print(str, query_type);
else
- args[0]->print(str);
+ args[0]->print(str, query_type);
}
-/*
- Special NOP (No OPeration) for ALL subquery it is like Item_func_not_all
- (return TRUE if underlying subquery do not return rows) but if subquery
- returns some rows it return same value as argument (TRUE/FALSE).
+/**
+ Special NOP (No OPeration) for ALL subquery. It is like
+ Item_func_not_all.
+
+ @return
+ (return TRUE if underlying subquery do not return rows) but if subquery
+ returns some rows it return same value as argument (TRUE/FALSE).
*/
longlong Item_func_nop_all::val_int()
@@ -312,16 +356,9 @@ longlong Item_func_nop_all::val_int()
}
-/*
- Convert a constant item to an int and replace the original item
-
- SYNOPSIS
- convert_constant_item()
- thd thread handle
- field_item item will be converted using the type of this field
- item [in/out] reference to the item to convert
+/**
+ Convert a constant item to an int and replace the original item.
- DESCRIPTION
The function converts a constant expression or string to an integer.
On successful conversion the original item is substituted for the
result of the item evaluation.
@@ -329,16 +366,21 @@ longlong Item_func_nop_all::val_int()
also when comparing bigint to strings (in which case strings
are converted to bigints).
- NOTES
+ @param thd thread handle
+ @param field item will be converted using the type of this field
+ @param[in,out] item reference to the item to convert
+
+ @note
This function is called only at prepare stage.
As all derived tables are filled only after all derived tables
are prepared we do not evaluate items with subselects here because
they can contain derived tables and thus we may attempt to use a
table that has not been populated yet.
- RESULT VALUES
- 0 Can't convert item
- 1 Item was replaced with an integer version of the item
+ @retval
+ 0 Can't convert item
+ @retval
+ 1 Item was replaced with an integer version of the item
*/
static bool convert_constant_item(THD *thd, Item_field *field_item,
@@ -349,30 +391,45 @@ static bool convert_constant_item(THD *thd, Item_field *field_item,
if (!(*item)->with_subselect && (*item)->const_item())
{
- /* For comparison purposes allow invalid dates like 2000-01-32 */
+ TABLE *table= field->table;
ulong 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 orig_field_val; /* original field value if valid */
+
+ LINT_INIT(old_maps[0]);
+ LINT_INIT(old_maps[1]);
LINT_INIT(orig_field_val);
+
+ if (table)
+ dbug_tmp_use_all_columns(table, old_maps,
+ table->read_set, table->write_set);
+ /* For comparison purposes allow invalid dates like 2000-01-32 */
thd->variables.sql_mode= (orig_sql_mode & ~MODE_NO_ZERO_DATE) |
MODE_INVALID_DATES;
thd->count_cuted_fields= CHECK_FIELD_IGNORE;
+
/*
- Store the value of the field if it references an outer field because
- the call to save_in_field below overrides that value.
+ Store the value of the field/constant if it references an outer field
+ because the call to save_in_field below overrides that value.
+ Don't save field value if no data has been read yet.
+ Outer constant values are always saved.
*/
- if (field_item->depended_from)
+ bool save_field_value= (field_item->depended_from &&
+ (field_item->const_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))
{
- Item *tmp=new Item_int_with_ref(field->val_int(), *item,
- test(field->flags & UNSIGNED_FLAG));
+ 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 (field_item->depended_from)
+ if (save_field_value)
{
result= field->store(orig_field_val, TRUE);
/* orig_field_val must be a valid value that can be restored back. */
@@ -380,6 +437,8 @@ static bool convert_constant_item(THD *thd, Item_field *field_item,
}
thd->variables.sql_mode= orig_sql_mode;
thd->count_cuted_fields= orig_count_cuted_fields;
+ if (table)
+ dbug_tmp_restore_column_maps(table->read_set, table->write_set, old_maps);
}
return result;
}
@@ -525,8 +584,8 @@ int Arg_comparator::set_compare_func(Item_bool_func2 *item, Item_result type)
which would be transformed to:
WHERE col= 'j'
*/
- (*a)->walk(&Item::set_no_const_sub, (byte*) 0);
- (*b)->walk(&Item::set_no_const_sub, (byte*) 0);
+ (*a)->walk(&Item::set_no_const_sub, FALSE, (uchar*) 0);
+ (*b)->walk(&Item::set_no_const_sub, FALSE, (uchar*) 0);
}
break;
}
@@ -898,12 +957,15 @@ get_datetime_value(THD *thd, Item ***item_arg, Item **cache_arg,
{
value= item->val_int();
*is_null= item->null_value;
+ enum_field_types f_type= item->field_type();
/*
Item_date_add_interval may return MYSQL_TYPE_STRING as the result
field type. To detect that the DATE value has been returned we
- compare it with 1000000L - any DATE value should be less than it.
+ compare it with 100000000L - any DATE value should be less than it.
+ Don't shift cached DATETIME values up for the second time.
*/
- if (item->field_type() == MYSQL_TYPE_DATE || value < 100000000L)
+ if (f_type == MYSQL_TYPE_DATE ||
+ (f_type != MYSQL_TYPE_DATETIME && value < 100000000L))
value*= 1000000L;
}
else
@@ -912,7 +974,7 @@ get_datetime_value(THD *thd, Item ***item_arg, Item **cache_arg,
*is_null= item->null_value;
}
if (*is_null)
- return -1;
+ return ~(ulonglong) 0;
/*
Convert strings to the integer DATE/DATETIME representation.
Even if both dates provided in strings we can't compare them directly as
@@ -940,7 +1002,7 @@ get_datetime_value(THD *thd, Item ***item_arg, Item **cache_arg,
if (item->const_item() && cache_arg && (item->type() != Item::FUNC_ITEM ||
((Item_func*)item)->functype() != Item_func::GUSERVAR_FUNC))
{
- Item_cache_int *cache= new Item_cache_int();
+ Item_cache_int *cache= new Item_cache_int(MYSQL_TYPE_DATETIME);
/* Mark the cache as non-const to prevent re-caching. */
cache->set_used_tables(1);
cache->store(item, value);
@@ -1026,13 +1088,15 @@ int Arg_comparator::compare_string()
}
-/*
+/**
Compare strings byte by byte. End spaces are also compared.
- RETURN
- < 0 *a < *b
- 0 *b == *b
- > 0 *a > *b
+ @retval
+ <0 *a < *b
+ @retval
+ 0 *b == *b
+ @retval
+ >0 *a > *b
*/
int Arg_comparator::compare_binary_string()
@@ -1054,10 +1118,11 @@ int Arg_comparator::compare_binary_string()
}
-/*
- Compare strings, but take into account that NULL == NULL
+/**
+ Compare strings, but take into account that NULL == NULL.
*/
+
int Arg_comparator::compare_e_string()
{
String *res1,*res2;
@@ -1198,7 +1263,7 @@ int Arg_comparator::compare_int_signed()
}
-/*
+/**
Compare values as BIGINT UNSIGNED.
*/
@@ -1221,7 +1286,7 @@ int Arg_comparator::compare_int_unsigned()
}
-/*
+/**
Compare signed (*a) with unsigned (*B)
*/
@@ -1246,7 +1311,7 @@ int Arg_comparator::compare_int_signed_unsigned()
}
-/*
+/**
Compare unsigned (*a) with signed (*B)
*/
@@ -1282,7 +1347,7 @@ int Arg_comparator::compare_e_int()
return test(val1 == val2);
}
-/*
+/**
Compare unsigned *a with signed *b or signed *a with unsigned *b.
*/
int Arg_comparator::compare_e_int_diff_signedness()
@@ -1362,10 +1427,10 @@ void Item_func_truth::fix_length_and_dec()
}
-void Item_func_truth::print(String *str)
+void Item_func_truth::print(String *str, enum_query_type query_type)
{
str->append('(');
- args[0]->print(str);
+ args[0]->print(str, query_type);
str->append(STRING_WITH_LEN(" is "));
if (! affirmative)
str->append(STRING_WITH_LEN("not "));
@@ -1558,8 +1623,8 @@ void Item_in_optimizer::cleanup()
bool Item_in_optimizer::is_null()
{
- cache->store(args[0]);
- return (null_value= (cache->null_value || args[1]->is_null()));
+ val_int();
+ return null_value;
}
@@ -1571,7 +1636,7 @@ longlong Item_func_eq::val_int()
}
-/* Same as Item_func_eq, but NULL = NULL */
+/** Same as Item_func_eq, but NULL = NULL. */
void Item_func_equal::fix_length_and_dec()
{
@@ -1664,8 +1729,10 @@ void Item_func_interval::fix_length_and_dec()
{
uint rows= row->cols();
- use_decimal_comparison= (row->element_index(0)->result_type() == DECIMAL_RESULT) ||
- (row->element_index(0)->result_type() == INT_RESULT);
+ use_decimal_comparison= ((row->element_index(0)->result_type() ==
+ DECIMAL_RESULT) ||
+ (row->element_index(0)->result_type() ==
+ INT_RESULT));
if (rows > 8)
{
bool not_null_consts= TRUE;
@@ -1723,21 +1790,18 @@ void Item_func_interval::fix_length_and_dec()
}
-/*
- Execute Item_func_interval()
-
- SYNOPSIS
- Item_func_interval::val_int()
+/**
+ Execute Item_func_interval().
- NOTES
- If we are doing a decimal comparison, we are
- evaluating the first item twice.
+ @note
+ If we are doing a decimal comparison, we are evaluating the first
+ item twice.
- RETURN
- -1 if null value,
- 0 if lower than lowest
- 1 - arg_count-1 if between args[n] and args[n+1]
- arg_count if higher than biggest argument
+ @return
+ - -1 if null value,
+ - 0 if lower than lowest
+ - 1 - arg_count-1 if between args[n] and args[n+1]
+ - arg_count if higher than biggest argument
*/
longlong Item_func_interval::val_int()
@@ -1819,32 +1883,31 @@ longlong Item_func_interval::val_int()
}
-/*
- Perform context analysis of a BETWEEN item tree
-
- SYNOPSIS:
- fix_fields()
- thd reference to the global context of the query thread
- tables list of all open tables involved in the query
- ref pointer to Item* variable where pointer to resulting "fixed"
- item is to be assigned
+/**
+ Perform context analysis of a BETWEEN item tree.
- DESCRIPTION
This function performs context analysis (name resolution) and calculates
various attributes of the item tree with Item_func_between as its root.
The function saves in ref the pointer to the item or to a newly created
item that is considered as a replacement for the original one.
- NOTES
+ @param thd reference to the global context of the query thread
+ @param ref pointer to Item* variable where pointer to resulting "fixed"
+ item is to be assigned
+
+ @note
Let T0(e)/T1(e) be the value of not_null_tables(e) when e is used on
a predicate/function level. Then it's easy to show that:
+ @verbatim
T0(e BETWEEN e1 AND e2) = union(T1(e),T1(e1),T1(e2))
T1(e BETWEEN e1 AND e2) = union(T1(e),intersection(T1(e1),T1(e2)))
T0(e NOT BETWEEN e1 AND e2) = union(T1(e),intersection(T1(e1),T1(e2)))
T1(e NOT BETWEEN e1 AND e2) = union(T1(e),intersection(T1(e1),T1(e2)))
+ @endverbatim
- RETURN
+ @retval
0 ok
+ @retval
1 got error
*/
@@ -1871,11 +1934,11 @@ bool Item_func_between::fix_fields(THD *thd, Item **ref)
void Item_func_between::fix_length_and_dec()
{
max_length= 1;
- THD *thd= current_thd;
int i;
bool datetime_found= FALSE;
int time_items_found= 0;
compare_as_dates= TRUE;
+ THD *thd= current_thd;
/*
As some compare functions are generated after sql_yacc,
@@ -1883,7 +1946,7 @@ void Item_func_between::fix_length_and_dec()
*/
if (!args[0] || !args[1] || !args[2])
return;
- if ( agg_cmp_type(thd, &cmp_type, args, 3))
+ 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))
@@ -2051,16 +2114,16 @@ longlong Item_func_between::val_int()
}
-void Item_func_between::print(String *str)
+void Item_func_between::print(String *str, enum_query_type query_type)
{
str->append('(');
- args[0]->print(str);
+ args[0]->print(str, query_type);
if (negated)
str->append(STRING_WITH_LEN(" not"));
str->append(STRING_WITH_LEN(" between "));
- args[1]->print(str);
+ args[1]->print(str, query_type);
str->append(STRING_WITH_LEN(" and "));
- args[2]->print(str);
+ args[2]->print(str, query_type);
str->append(')');
}
@@ -2120,7 +2183,7 @@ enum_field_types Item_func_ifnull::field_type() const
Field *Item_func_ifnull::tmp_table_field(TABLE *table)
{
- return tmp_table_field_from_field_type(table);
+ return tmp_table_field_from_field_type(table, 0);
}
double
@@ -2191,30 +2254,29 @@ Item_func_ifnull::str_op(String *str)
}
-/*
- Perform context analysis of an IF item tree
-
- SYNOPSIS:
- fix_fields()
- thd reference to the global context of the query thread
- tables list of all open tables involved in the query
- ref pointer to Item* variable where pointer to resulting "fixed"
- item is to be assigned
+/**
+ Perform context analysis of an IF item tree.
- DESCRIPTION
This function performs context analysis (name resolution) and calculates
various attributes of the item tree with Item_func_if as its root.
The function saves in ref the pointer to the item or to a newly created
item that is considered as a replacement for the original one.
- NOTES
+ @param thd reference to the global context of the query thread
+ @param ref pointer to Item* variable where pointer to resulting "fixed"
+ item is to be assigned
+
+ @note
Let T0(e)/T1(e) be the value of not_null_tables(e) when e is used on
a predicate/function level. Then it's easy to show that:
+ @verbatim
T0(IF(e,e1,e2) = T1(IF(e,e1,e2))
T1(IF(e,e1,e2)) = intersection(T1(e1),T1(e2))
+ @endverbatim
- RETURN
+ @retval
0 ok
+ @retval
1 got error
*/
@@ -2360,11 +2422,14 @@ Item_func_nullif::fix_length_and_dec()
}
-/*
- nullif () returns NULL if arguments are equal, else it returns the
- first argument.
+/**
+ @note
Note that we have to evaluate the first argument twice as the compare
may have been done with a different type than return value
+ @return
+ NULL if arguments are equal
+ @return
+ the first argument if not equal
*/
double
@@ -2435,94 +2500,59 @@ Item_func_nullif::is_null()
return (null_value= (!cmp.compare() ? 1 : args[0]->null_value));
}
-/*
- CASE expression
- Return the matching ITEM or NULL if all compares (including else) failed
+
+/**
+ Find and return matching items for CASE or ELSE item if all compares
+ are failed or NULL if ELSE item isn't defined.
+
+ IMPLEMENTATION
+ In order to do correct comparisons of the CASE expression (the expression
+ between CASE and the first WHEN) with each WHEN expression several
+ comparators are used. One for each result type. CASE expression can be
+ evaluated up to # of different result types are used. To check whether
+ the CASE expression already was evaluated for a particular result type
+ a bit mapped variable value_added_map is used. Result types are mapped
+ to it according to their int values i.e. STRING_RESULT is mapped to bit
+ 0, REAL_RESULT to bit 1, so on.
+
+ @retval
+ NULL Nothing found and there is no ELSE expression defined
+ @retval
+ item Found item or ELSE item if defined and all comparisons are
+ failed
*/
Item *Item_func_case::find_item(String *str)
{
- String *first_expr_str, *tmp;
- my_decimal *first_expr_dec, first_expr_dec_val;
- longlong first_expr_int;
- double first_expr_real;
- char buff[MAX_FIELD_WIDTH];
- String buff_str(buff,sizeof(buff),default_charset());
-
- /* These will be initialized later */
- LINT_INIT(first_expr_str);
- LINT_INIT(first_expr_int);
- LINT_INIT(first_expr_real);
- LINT_INIT(first_expr_dec);
+ uint value_added_map= 0;
- if (first_expr_num != -1)
+ if (first_expr_num == -1)
{
- switch (cmp_type)
- {
- case STRING_RESULT:
- // We can't use 'str' here as this may be overwritten
- if (!(first_expr_str= args[first_expr_num]->val_str(&buff_str)))
- return else_expr_num != -1 ? args[else_expr_num] : 0; // Impossible
- break;
- case INT_RESULT:
- first_expr_int= args[first_expr_num]->val_int();
- if (args[first_expr_num]->null_value)
- return else_expr_num != -1 ? args[else_expr_num] : 0;
- break;
- case REAL_RESULT:
- first_expr_real= args[first_expr_num]->val_real();
- if (args[first_expr_num]->null_value)
- return else_expr_num != -1 ? args[else_expr_num] : 0;
- break;
- case DECIMAL_RESULT:
- first_expr_dec= args[first_expr_num]->val_decimal(&first_expr_dec_val);
- if (args[first_expr_num]->null_value)
- return else_expr_num != -1 ? args[else_expr_num] : 0;
- break;
- case ROW_RESULT:
- default:
- // This case should never be chosen
- DBUG_ASSERT(0);
- break;
- }
- }
-
- // Compare every WHEN argument with it and return the first match
- for (uint i=0 ; i < ncases ; i+=2)
- {
- if (first_expr_num == -1)
+ for (uint i=0 ; i < ncases ; i+=2)
{
// No expression between CASE and the first WHEN
if (args[i]->val_bool())
return args[i+1];
continue;
}
- switch (cmp_type) {
- case STRING_RESULT:
- if ((tmp=args[i]->val_str(str))) // If not null
- if (sortcmp(tmp,first_expr_str,cmp_collation.collation)==0)
- return args[i+1];
- break;
- case INT_RESULT:
- if (args[i]->val_int()==first_expr_int && !args[i]->null_value)
- return args[i+1];
- break;
- case REAL_RESULT:
- if (args[i]->val_real() == first_expr_real && !args[i]->null_value)
- return args[i+1];
- break;
- case DECIMAL_RESULT:
+ }
+ else
+ {
+ /* Compare every WHEN argument with it and return the first match */
+ for (uint i=0 ; i < ncases ; i+=2)
{
- my_decimal value;
- if (my_decimal_cmp(args[i]->val_decimal(&value), first_expr_dec) == 0)
- return args[i+1];
- break;
- }
- case ROW_RESULT:
- default:
- // This case should never be chosen
- DBUG_ASSERT(0);
- break;
+ cmp_type= item_cmp_type(left_result_type, args[i]->result_type());
+ DBUG_ASSERT(cmp_type != ROW_RESULT);
+ DBUG_ASSERT(cmp_items[(uint)cmp_type]);
+ if (!(value_added_map & (1<<(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;
+ }
+ if (!cmp_items[(uint)cmp_type]->cmp(args[i]) && !args[i]->null_value)
+ return args[i + 1];
}
}
// No, WHEN clauses all missed, return ELSE expression
@@ -2612,7 +2642,7 @@ bool Item_func_case::fix_fields(THD *thd, Item **ref)
Item_func_case::val_int() -> Item_func_case::find_item()
*/
#ifndef EMBEDDED_LIBRARY
- char buff[MAX_FIELD_WIDTH*2+sizeof(String)*2+sizeof(String*)*2+sizeof(double)*2+sizeof(longlong)*2];
+ 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);
/*
@@ -2647,8 +2677,7 @@ void Item_func_case::fix_length_and_dec()
{
Item **agg;
uint nagg;
- THD *thd= current_thd;
-
+ uint found_types= 0;
if (!(agg= (Item**) sql_alloc(sizeof(Item*)*(ncases+1))))
return;
@@ -2675,17 +2704,42 @@ void Item_func_case::fix_length_and_dec()
*/
if (first_expr_num != -1)
{
+ uint i;
agg[0]= args[first_expr_num];
+ left_result_type= agg[0]->result_type();
+
for (nagg= 0; nagg < ncases/2 ; nagg++)
agg[nagg+1]= args[nagg*2];
nagg++;
- if (agg_cmp_type(thd, &cmp_type, agg, nagg))
- return;
- if ((cmp_type == STRING_RESULT) &&
- agg_arg_charsets(cmp_collation, agg, nagg, MY_COLL_CMP_CONV, 1))
+ if (!(found_types= collect_cmp_types(agg, nagg)))
return;
+ if (with_sum_func || current_thd->lex->current_select->group_list.elements)
+ {
+ /*
+ 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.
+ */
+ found_types |= (1 << item_cmp_type(left_result_type, STRING_RESULT));
+ }
+
+ for (i= 0; i <= (uint)DECIMAL_RESULT; i++)
+ {
+ if (found_types & (1 << 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 (!(cmp_items[i]=
+ cmp_item::get_comparator((Item_result)i,
+ cmp_collation.collation)))
+ return;
+ }
+ }
}
-
+
if (else_expr_num == -1 || args[else_expr_num]->maybe_null)
maybe_null=1;
@@ -2723,34 +2777,52 @@ uint Item_func_case::decimal_precision() const
}
-/* TODO: Fix this so that it prints the whole CASE expression */
+/**
+ @todo
+ Fix this so that it prints the whole CASE expression
+*/
-void Item_func_case::print(String *str)
+void Item_func_case::print(String *str, enum_query_type query_type)
{
str->append(STRING_WITH_LEN("(case "));
if (first_expr_num != -1)
{
- args[first_expr_num]->print(str);
+ args[first_expr_num]->print(str, query_type);
str->append(' ');
}
for (uint i=0 ; i < ncases ; i+=2)
{
str->append(STRING_WITH_LEN("when "));
- args[i]->print(str);
+ args[i]->print(str, query_type);
str->append(STRING_WITH_LEN(" then "));
- args[i+1]->print(str);
+ args[i+1]->print(str, query_type);
str->append(' ');
}
if (else_expr_num != -1)
{
str->append(STRING_WITH_LEN("else "));
- args[else_expr_num]->print(str);
+ args[else_expr_num]->print(str, query_type);
str->append(' ');
}
str->append(STRING_WITH_LEN("end)"));
}
-/*
+
+void Item_func_case::cleanup()
+{
+ uint i;
+ DBUG_ENTER("Item_func_case::cleanup");
+ Item_func::cleanup();
+ for (i= 0; i <= (uint)DECIMAL_RESULT; i++)
+ {
+ delete cmp_items[i];
+ cmp_items[i]= 0;
+ }
+ DBUG_VOID_RETURN;
+}
+
+
+/**
Coalesce - return first not NULL argument.
*/
@@ -2963,7 +3035,7 @@ static int cmp_decimal(void *cmp_arg, my_decimal *a, my_decimal *b)
int in_vector::find(Item *item)
{
- byte *result=get_value(item);
+ uchar *result=get_value(item);
if (!result || !used_count)
return 0; // Null value
@@ -3021,9 +3093,9 @@ void in_string::set(uint pos,Item *item)
}
-byte *in_string::get_value(Item *item)
+uchar *in_string::get_value(Item *item)
{
- return (byte*) item->val_str(&tmp);
+ return (uchar*) item->val_str(&tmp);
}
in_row::in_row(uint elements, Item * item)
@@ -3045,12 +3117,12 @@ in_row::~in_row()
delete [] (cmp_item_row*) base;
}
-byte *in_row::get_value(Item *item)
+uchar *in_row::get_value(Item *item)
{
tmp.store_value(item);
if (item->is_null())
return 0;
- return (byte *)&tmp;
+ return (uchar *)&tmp;
}
void in_row::set(uint pos, Item *item)
@@ -3073,26 +3145,26 @@ void in_longlong::set(uint pos,Item *item)
buff->unsigned_flag= item->unsigned_flag;
}
-byte *in_longlong::get_value(Item *item)
+uchar *in_longlong::get_value(Item *item)
{
tmp.val= item->val_int();
if (item->null_value)
return 0;
tmp.unsigned_flag= item->unsigned_flag;
- return (byte*) &tmp;
+ return (uchar*) &tmp;
}
void in_datetime::set(uint pos,Item *item)
{
- Item **tmp= &item;
+ Item **tmp_item= &item;
bool is_null;
struct packed_longlong *buff= &((packed_longlong*) base)[pos];
- buff->val= get_datetime_value(thd, &tmp, 0, warn_item, &is_null);
+ buff->val= get_datetime_value(thd, &tmp_item, 0, warn_item, &is_null);
buff->unsigned_flag= 1L;
}
-byte *in_datetime::get_value(Item *item)
+uchar *in_datetime::get_value(Item *item)
{
bool is_null;
Item **tmp_item= lval_cache ? &lval_cache : &item;
@@ -3100,7 +3172,7 @@ byte *in_datetime::get_value(Item *item)
if (item->null_value)
return 0;
tmp.unsigned_flag= 1L;
- return (byte*) &tmp;
+ return (uchar*) &tmp;
}
in_double::in_double(uint elements)
@@ -3112,12 +3184,12 @@ void in_double::set(uint pos,Item *item)
((double*) base)[pos]= item->val_real();
}
-byte *in_double::get_value(Item *item)
+uchar *in_double::get_value(Item *item)
{
tmp= item->val_real();
if (item->null_value)
return 0; /* purecov: inspected */
- return (byte*) &tmp;
+ return (uchar*) &tmp;
}
@@ -3139,12 +3211,12 @@ void in_decimal::set(uint pos, Item *item)
}
-byte *in_decimal::get_value(Item *item)
+uchar *in_decimal::get_value(Item *item)
{
my_decimal *result= item->val_decimal(&val);
if (item->null_value)
return 0;
- return (byte *)result;
+ return (uchar *)result;
}
@@ -3372,32 +3444,31 @@ bool Item_func_in::nulls_in_row()
}
-/*
- Perform context analysis of an IN item tree
-
- SYNOPSIS:
- fix_fields()
- thd reference to the global context of the query thread
- tables list of all open tables involved in the query
- ref pointer to Item* variable where pointer to resulting "fixed"
- item is to be assigned
+/**
+ Perform context analysis of an IN item tree.
- DESCRIPTION
This function performs context analysis (name resolution) and calculates
various attributes of the item tree with Item_func_in as its root.
The function saves in ref the pointer to the item or to a newly created
item that is considered as a replacement for the original one.
- NOTES
+ @param thd reference to the global context of the query thread
+ @param ref pointer to Item* variable where pointer to resulting "fixed"
+ item is to be assigned
+
+ @note
Let T0(e)/T1(e) be the value of not_null_tables(e) when e is used on
a predicate/function level. Then it's easy to show that:
+ @verbatim
T0(e IN(e1,...,en)) = union(T1(e),intersection(T1(ei)))
T1(e IN(e1,...,en)) = union(T1(e),intersection(T1(ei)))
T0(e NOT IN(e1,...,en)) = union(T1(e),union(T1(ei)))
T1(e NOT IN(e1,...,en)) = union(T1(e),intersection(T1(ei)))
+ @endverbatim
- RETURN
+ @retval
0 ok
+ @retval
1 got error
*/
@@ -3433,21 +3504,20 @@ static int srtcmp_in(CHARSET_INFO *cs, const String *x,const String *y)
void Item_func_in::fix_length_and_dec()
{
Item **arg, **arg_end;
- uint const_itm= 1;
+ bool const_itm= 1;
THD *thd= current_thd;
bool datetime_found= FALSE;
/* TRUE <=> arguments values will be compared as DATETIMEs. */
bool compare_as_datetime= FALSE;
Item *date_arg= 0;
-
- if (agg_cmp_type(thd, &cmp_type, args, arg_count))
+ uint found_types= 0;
+ uint type_cnt= 0, i;
+ Item_result cmp_type= STRING_RESULT;
+ left_result_type= args[0]->result_type();
+ if (!(found_types= collect_cmp_types(args, arg_count)))
return;
-
- if (cmp_type == STRING_RESULT &&
- agg_arg_charsets(cmp_collation, args, arg_count, MY_COLL_CMP_CONV, 1))
- return;
-
- for (arg=args+1, arg_end=args+arg_count; arg != arg_end ; arg++)
+
+ for (arg= args + 1, arg_end= args + arg_count; arg != arg_end ; arg++)
{
if (!arg[0]->const_item())
{
@@ -3455,92 +3525,111 @@ void Item_func_in::fix_length_and_dec()
break;
}
}
- /*
- When comparing rows create the row comparator object beforehand to ease
- the DATETIME comparison detection procedure.
- */
- if (cmp_type == ROW_RESULT)
+ for (i= 0; i <= (uint)DECIMAL_RESULT; i++)
{
- cmp_item_row *cmp= 0;
- if (const_itm && !nulls_in_row())
- {
- array= new in_row(arg_count-1, 0);
- cmp= &((in_row*)array)->tmp;
- }
- else
+ if (found_types & 1 << i)
{
- if (!(cmp= new cmp_item_row))
- return;
- in_item= cmp;
+ (type_cnt)++;
+ cmp_type= (Item_result) i;
}
- cmp->n= args[0]->cols();
- cmp->alloc_comparators();
}
- /* All DATE/DATETIME fields/functions has the STRING result type. */
- if (cmp_type == STRING_RESULT || cmp_type == ROW_RESULT)
- {
- uint col, cols= args[0]->cols();
- for (col= 0; col < cols; col++)
+ if (type_cnt == 1)
+ {
+ if (cmp_type == STRING_RESULT &&
+ agg_arg_charsets(cmp_collation, args, arg_count, MY_COLL_CMP_CONV, 1))
+ return;
+ arg_types_compatible= TRUE;
+ }
+ if (type_cnt == 1)
+ {
+ /*
+ When comparing rows create the row comparator object beforehand to ease
+ the DATETIME comparison detection procedure.
+ */
+ if (cmp_type == ROW_RESULT)
{
- bool skip_column= FALSE;
- /*
- Check that all items to be compared has the STRING result type and at
- least one of them is a DATE/DATETIME item.
- */
- for (arg= args, arg_end= args + arg_count; arg != arg_end ; arg++)
+ cmp_item_row *cmp= 0;
+ if (const_itm && !nulls_in_row())
{
- Item *itm= ((cmp_type == STRING_RESULT) ? arg[0] :
- arg[0]->element_index(col));
- if (itm->result_type() != STRING_RESULT)
- {
- skip_column= TRUE;
- break;
- }
- else if (itm->is_datetime())
+ array= new in_row(arg_count-1, 0);
+ cmp= &((in_row*)array)->tmp;
+ }
+ else
+ {
+ if (!(cmp= new cmp_item_row))
+ return;
+ cmp_items[ROW_RESULT]= cmp;
+ }
+ cmp->n= args[0]->cols();
+ cmp->alloc_comparators();
+ }
+ /* All DATE/DATETIME fields/functions has the STRING result type. */
+ if (cmp_type == STRING_RESULT || cmp_type == ROW_RESULT)
+ {
+ uint col, cols= args[0]->cols();
+
+ for (col= 0; col < cols; col++)
+ {
+ bool skip_column= FALSE;
+ /*
+ Check that all items to be compared has the STRING result type and at
+ least one of them is a DATE/DATETIME item.
+ */
+ for (arg= args, arg_end= args + arg_count; arg != arg_end ; arg++)
{
- datetime_found= TRUE;
- /*
- Internally all DATE/DATETIME values are converted to the DATETIME
- type. So try to find a DATETIME item to issue correct warnings.
- */
- if (!date_arg)
- date_arg= itm;
- else if (itm->field_type() == MYSQL_TYPE_DATETIME)
+ Item *itm= ((cmp_type == STRING_RESULT) ? arg[0] :
+ arg[0]->element_index(col));
+ if (itm->result_type() != STRING_RESULT)
{
- date_arg= itm;
- /* All arguments are already checked to have the STRING result. */
- if (cmp_type == STRING_RESULT)
- break;
+ skip_column= TRUE;
+ break;
+ }
+ else if (itm->is_datetime())
+ {
+ datetime_found= TRUE;
+ /*
+ Internally all DATE/DATETIME values are converted to the DATETIME
+ type. So try to find a DATETIME item to issue correct warnings.
+ */
+ if (!date_arg)
+ date_arg= itm;
+ else if (itm->field_type() == MYSQL_TYPE_DATETIME)
+ {
+ date_arg= itm;
+ /* All arguments are already checked to have the STRING result. */
+ if (cmp_type == STRING_RESULT)
+ break;
+ }
}
}
- }
- if (skip_column)
- continue;
- if (datetime_found)
- {
- if (cmp_type == ROW_RESULT)
+ if (skip_column)
+ continue;
+ if (datetime_found)
{
- cmp_item **cmp= 0;
- if (array)
- cmp= ((in_row*)array)->tmp.comparators + col;
+ if (cmp_type == ROW_RESULT)
+ {
+ cmp_item **cmp= 0;
+ if (array)
+ cmp= ((in_row*)array)->tmp.comparators + col;
+ else
+ cmp= ((cmp_item_row*)cmp_items[ROW_RESULT])->comparators + col;
+ *cmp= new cmp_item_datetime(date_arg);
+ /* Reset variables for the next column. */
+ date_arg= 0;
+ datetime_found= FALSE;
+ }
else
- cmp= ((cmp_item_row*)in_item)->comparators + col;
- *cmp= new cmp_item_datetime(date_arg);
- /* Reset variables for the next column. */
- date_arg= 0;
- datetime_found= FALSE;
+ compare_as_datetime= TRUE;
}
- else
- compare_as_datetime= TRUE;
}
}
}
/*
- Row item with NULLs inside can return NULL or FALSE =>
+ Row item with NULLs inside can return NULL or FALSE =>
they can't be processed as static
*/
- if (const_itm && !nulls_in_row())
+ if (type_cnt == 1 && const_itm && !nulls_in_row())
{
if (compare_as_datetime)
array= new in_datetime(date_arg, arg_count - 1);
@@ -3609,63 +3698,103 @@ void Item_func_in::fix_length_and_dec()
else
have_null= 1;
}
- if ((array->used_count=j))
+ if ((array->used_count= j))
array->sort();
}
}
else
{
- if (in_item)
+ if (compare_as_datetime)
+ cmp_items[STRING_RESULT]= new cmp_item_datetime(date_arg);
+ else
{
- /*
- The row comparator was created at the beginning but only DATETIME
- items comparators were initialized. Call store_value() to setup
- others.
- */
- in_item->store_value(args[0]);
+ for (i= 0; i <= (uint) DECIMAL_RESULT; i++)
+ {
+ if (found_types & (1 << 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 (!cmp_items[i] && !(cmp_items[i]=
+ cmp_item::get_comparator((Item_result)i,
+ cmp_collation.collation)))
+ return;
+ }
+ }
}
- else if (compare_as_datetime)
- in_item= new cmp_item_datetime(date_arg);
- else
- in_item= cmp_item::get_comparator(cmp_type, cmp_collation.collation);
- if (cmp_type == STRING_RESULT)
- in_item->cmp_charset= cmp_collation.collation;
}
max_length= 1;
}
-void Item_func_in::print(String *str)
+void Item_func_in::print(String *str, enum_query_type query_type)
{
str->append('(');
- args[0]->print(str);
+ args[0]->print(str, query_type);
if (negated)
str->append(STRING_WITH_LEN(" not"));
str->append(STRING_WITH_LEN(" in ("));
- print_args(str, 1);
+ print_args(str, 1, query_type);
str->append(STRING_WITH_LEN("))"));
}
+/*
+ Evaluate the function and return its value.
+
+ SYNOPSIS
+ val_int()
+
+ DESCRIPTION
+ Evaluate the function and return its value.
+
+ IMPLEMENTATION
+ If the array object is defined then the value of the function is
+ calculated by means of this array.
+ Otherwise several cmp_item objects are used in order to do correct
+ comparison of left expression and an expression from the values list.
+ One cmp_item object correspond to one used comparison type. Left
+ expression can be evaluated up to number of different used comparison
+ types. A bit mapped variable value_added_map is used to check whether
+ the left expression already was evaluated for a particular result type.
+ Result types are mapped to it according to their integer values i.e.
+ STRING_RESULT is mapped to bit 0, REAL_RESULT to bit 1, so on.
+
+ RETURN
+ Value of the function
+*/
+
longlong Item_func_in::val_int()
{
+ cmp_item *in_item;
DBUG_ASSERT(fixed == 1);
+ uint value_added_map= 0;
if (array)
{
int tmp=array->find(args[0]);
null_value=args[0]->null_value || (!tmp && have_null);
return (longlong) (!null_value && tmp != negated);
}
- in_item->store_value(args[0]);
- if ((null_value=args[0]->null_value))
- return 0;
+
have_null= 0;
- for (uint i=1 ; i < arg_count ; i++)
+ for (uint i= 1 ; i < arg_count ; i++)
{
+ Item_result cmp_type= item_cmp_type(left_result_type, args[i]->result_type());
+ in_item= cmp_items[(uint)cmp_type];
+ DBUG_ASSERT(in_item);
+ if (!(value_added_map & (1 << (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;
+ }
if (!in_item->cmp(args[i]) && !args[i]->null_value)
return (longlong) (!negated);
have_null|= args[i]->null_value;
}
+
null_value= have_null;
return (longlong) (!null_value && negated);
}
@@ -3736,7 +3865,7 @@ Item_cond::fix_fields(THD *thd, Item **ref)
List_iterator<Item> li(list);
Item *item;
#ifndef EMBEDDED_LIBRARY
- char buff[sizeof(char*)]; // Max local vars in function
+ uchar buff[sizeof(char*)]; // Max local vars in function
#endif
not_null_tables_cache= used_tables_cache= 0;
const_item_cache= 1;
@@ -3803,27 +3932,20 @@ Item_cond::fix_fields(THD *thd, Item **ref)
return FALSE;
}
-bool Item_cond::walk(Item_processor processor, byte *arg)
+bool Item_cond::walk(Item_processor processor, bool walk_subquery, uchar *arg)
{
List_iterator_fast<Item> li(list);
Item *item;
while ((item= li++))
- if (item->walk(processor, arg))
+ if (item->walk(processor, walk_subquery, arg))
return 1;
- return Item_func::walk(processor, arg);
+ return Item_func::walk(processor, walk_subquery, arg);
}
-/*
- Transform an Item_cond object with a transformer callback function
-
- SYNOPSIS
- transform()
- transformer the transformer callback function to be applied to the nodes
- of the tree of the object
- arg parameter to be passed to the transformer
+/**
+ Transform an Item_cond object with a transformer callback function.
- DESCRIPTION
The function recursively applies the transform method to each
member item of the condition list.
If the call of the method for a member item returns a new item
@@ -3831,11 +3953,15 @@ bool Item_cond::walk(Item_processor processor, byte *arg)
After this the transformer is applied to the root node
of the Item_cond object.
- RETURN VALUES
+ @param transformer the transformer callback function to be applied to
+ the nodes of the tree of the object
+ @param arg parameter to be passed to the transformer
+
+ @return
Item returned as the result of transformation of the root node
*/
-Item *Item_cond::transform(Item_transformer transformer, byte *arg)
+Item *Item_cond::transform(Item_transformer transformer, uchar *arg)
{
DBUG_ASSERT(!current_thd->is_stmt_prepare());
@@ -3860,19 +3986,10 @@ Item *Item_cond::transform(Item_transformer transformer, byte *arg)
}
-/*
- Compile Item_cond object with a processor and a transformer callback functions
-
- SYNOPSIS
- compile()
- analyzer the analyzer callback function to be applied to the nodes
- of the tree of the object
- arg_p in/out parameter to be passed to the analyzer
- transformer the transformer callback function to be applied to the nodes
- of the tree of the object
- arg_t parameter to be passed to the transformer
+/**
+ Compile Item_cond object with a processor and a transformer
+ callback functions.
- DESCRIPTION
First the function applies the analyzer to the root node of
the Item_func object. Then if the analyzer succeeeds (returns TRUE)
the function recursively applies the compile method to member
@@ -3882,12 +3999,19 @@ Item *Item_cond::transform(Item_transformer transformer, byte *arg)
After this the transformer is applied to the root node
of the Item_cond object.
- RETURN VALUES
+ @param analyzer the analyzer callback function to be applied to the
+ nodes of the tree of the object
+ @param[in,out] arg_p parameter to be passed to the analyzer
+ @param transformer the transformer callback function to be applied to the
+ nodes of the tree of the object
+ @param arg_t parameter to be passed to the transformer
+
+ @return
Item returned as the result of transformation of the root node
*/
-Item *Item_cond::compile(Item_analyzer analyzer, byte **arg_p,
- Item_transformer transformer, byte *arg_t)
+Item *Item_cond::compile(Item_analyzer analyzer, uchar **arg_p,
+ Item_transformer transformer, uchar *arg_t)
{
if (!(this->*analyzer)(arg_p))
return 0;
@@ -3900,7 +4024,7 @@ Item *Item_cond::compile(Item_analyzer analyzer, byte **arg_p,
The same parameter value of arg_p must be passed
to analyze any argument of the condition formula.
*/
- byte *arg_v= *arg_p;
+ uchar *arg_v= *arg_p;
Item *new_item= item->compile(analyzer, &arg_v, transformer, arg_t);
if (new_item && new_item != item)
li.replace(new_item);
@@ -3932,23 +4056,21 @@ void Item_cond::traverse_cond(Cond_traverser traverser,
}
}
-/*
- Move SUM items out from item tree and replace with reference
+/**
+ Move SUM items out from item tree and replace with reference.
- SYNOPSIS
- split_sum_func()
- thd Thread handler
- ref_pointer_array Pointer to array of reference fields
- fields All fields in select
-
- NOTES
- This function is run on all expression (SELECT list, WHERE, HAVING etc)
- that have or refer (HAVING) to a SUM expression.
-
- The split is done to get an unique item for each SUM function
- so that we can easily find and calculate them.
- (Calculation done by update_sum_func() and copy_sum_funcs() in
- sql_select.cc)
+ The split is done to get an unique item for each SUM function
+ so that we can easily find and calculate them.
+ (Calculation done by update_sum_func() and copy_sum_funcs() in
+ sql_select.cc)
+
+ @param thd Thread handler
+ @param ref_pointer_array Pointer to array of reference fields
+ @param fields All fields in select
+
+ @note
+ This function is run on all expression (SELECT list, WHERE, HAVING etc)
+ that have or refer (HAVING) to a SUM expression.
*/
void Item_cond::split_sum_func(THD *thd, Item **ref_pointer_array,
@@ -3984,19 +4106,19 @@ void Item_cond::update_used_tables()
}
-void Item_cond::print(String *str)
+void Item_cond::print(String *str, enum_query_type query_type)
{
str->append('(');
List_iterator_fast<Item> li(list);
Item *item;
if ((item=li++))
- item->print(str);
+ item->print(str, query_type);
while ((item=li++))
{
str->append(' ');
str->append(func_name());
str->append(' ');
- item->print(str);
+ item->print(str, query_type);
}
str->append(')');
}
@@ -4019,20 +4141,22 @@ void Item_cond::neg_arguments(THD *thd)
}
-/*
- Evaluation of AND(expr, expr, expr ...)
+/**
+ Evaluation of AND(expr, expr, expr ...).
- NOTES:
+ @note
abort_if_null is set for AND expressions for which we don't care if the
result is NULL or 0. This is set for:
- WHERE clause
- HAVING clause
- IF(expression)
- RETURN VALUES
+ @retval
1 If all expressions are true
+ @retval
0 If all expressions are false or if we find a NULL expression and
'abort_on_null' is set.
+ @retval
NULL if all expression are either 1 or NULL
*/
@@ -4074,24 +4198,23 @@ longlong Item_cond_or::val_int()
return 0;
}
-/*
- Create an AND expression from two expressions
+/**
+ Create an AND expression from two expressions.
- SYNOPSIS
- and_expressions()
- a expression or NULL
- b expression.
- org_item Don't modify a if a == *org_item
- If a == NULL, org_item is set to point at b,
- to ensure that future calls will not modify b.
-
- NOTES
+ @param a expression or NULL
+ @param b expression.
+ @param org_item Don't modify a if a == *org_item.
+ If a == NULL, org_item is set to point at b,
+ to ensure that future calls will not modify b.
+
+ @note
This will not modify item pointed to by org_item or b
The idea is that one can call this in a loop and create and
'and' over all items without modifying any of the original items.
- RETURN
+ @retval
NULL Error
+ @retval
Item
*/
@@ -4136,7 +4259,7 @@ longlong Item_is_not_null_test::val_int()
if (!used_tables_cache && !with_subselect)
{
owner->was_null|= (!cached_value);
- DBUG_PRINT("info", ("cached :%ld", (long) cached_value));
+ DBUG_PRINT("info", ("cached: %ld", (long) cached_value));
DBUG_RETURN(cached_value);
}
if (args[0]->is_null())
@@ -4149,7 +4272,9 @@ longlong Item_is_not_null_test::val_int()
DBUG_RETURN(1);
}
-/* Optimize case of not_null_column IS NULL */
+/**
+ Optimize case of not_null_column IS NULL.
+*/
void Item_is_not_null_test::update_used_tables()
{
if (!args[0]->maybe_null)
@@ -4176,10 +4301,10 @@ longlong Item_func_isnotnull::val_int()
}
-void Item_func_isnotnull::print(String *str)
+void Item_func_isnotnull::print(String *str, enum_query_type query_type)
{
str->append('(');
- args[0]->print(str);
+ args[0]->print(str, query_type);
str->append(STRING_WITH_LEN(" is not null)"));
}
@@ -4209,7 +4334,9 @@ longlong Item_func_like::val_int()
}
-/* We can optimize a where if first character isn't a wildcard */
+/**
+ We can optimize a where if first character isn't a wildcard
+*/
Item_func::optimize_type Item_func_like::select_optimize() const
{
@@ -4493,6 +4620,7 @@ void Item_func_regex::cleanup()
{
my_regfree(&preg);
regex_compiled=0;
+ prev_regexp.length(0);
}
DBUG_VOID_RETURN;
}
@@ -4508,10 +4636,9 @@ void Item_func_regex::cleanup()
#endif
-/**********************************************************************
- turboBM_compute_suffixes()
+/**
Precomputation dependent only on pattern_len.
-**********************************************************************/
+*/
void Item_func_like::turboBM_compute_suffixes(int *suff)
{
@@ -4565,10 +4692,9 @@ void Item_func_like::turboBM_compute_suffixes(int *suff)
}
-/**********************************************************************
- turboBM_compute_good_suffix_shifts()
- Precomputation dependent only on pattern_len.
-**********************************************************************/
+/**
+ Precomputation dependent only on pattern_len.
+*/
void Item_func_like::turboBM_compute_good_suffix_shifts(int *suff)
{
@@ -4610,10 +4736,9 @@ void Item_func_like::turboBM_compute_good_suffix_shifts(int *suff)
}
-/**********************************************************************
- turboBM_compute_bad_character_shifts()
+/**
Precomputation dependent on pattern_len.
-**********************************************************************/
+*/
void Item_func_like::turboBM_compute_bad_character_shifts()
{
@@ -4639,10 +4764,12 @@ void Item_func_like::turboBM_compute_bad_character_shifts()
}
-/**********************************************************************
- turboBM_matches()
- Search for pattern in text, returns true/false for match/no match
-**********************************************************************/
+/**
+ Search for pattern in text.
+
+ @return
+ returns true/false for match/no match
+*/
bool Item_func_like::turboBM_matches(const char* text, int text_len) const
{
@@ -4722,24 +4849,20 @@ bool Item_func_like::turboBM_matches(const char* text, int text_len) const
}
-/*
+/**
Make a logical XOR of the arguments.
- SYNOPSIS
- val_int()
-
- DESCRIPTION
If either operator is NULL, return NULL.
- NOTE
- As we don't do any index optimization on XOR this is not going to be
- very fast to use.
-
- TODO (low priority)
- Change this to be optimized as:
- A XOR B -> (A) == 1 AND (B) <> 1) OR (A <> 1 AND (B) == 1)
+ @todo
+ (low priority) Change this to be optimized as: @n
+ A XOR B -> (A) == 1 AND (B) <> 1) OR (A <> 1 AND (B) == 1) @n
To be able to do this, we would however first have to extend the MySQL
range optimizer to handle OR better.
+
+ @note
+ As we don't do any index optimization on XOR this is not going to be
+ very fast to use.
*/
longlong Item_cond_xor::val_int()
@@ -4761,15 +4884,12 @@ longlong Item_cond_xor::val_int()
return (longlong) result;
}
-/*
+/**
Apply NOT transformation to the item and return a new one.
- SYNOPSIS
- neg_transformer()
- thd thread handler
- DESCRIPTION
Transform the item using next rules:
+ @verbatim
a AND b AND ... -> NOT(a) OR NOT(b) OR ...
a OR b OR ... -> NOT(a) AND NOT(b) AND ...
NOT(a) -> a
@@ -4781,8 +4901,11 @@ longlong Item_cond_xor::val_int()
a <= b -> a > b
IS NULL(a) -> IS NOT NULL(a)
IS NOT NULL(a) -> IS NULL(a)
+ @endverbatim
- RETURN
+ @param thd thread handler
+
+ @return
New item or
NULL if we cannot apply NOT transformation (see Item::neg_transformer()).
*/
@@ -4800,7 +4923,9 @@ Item *Item_bool_rowready_func2::neg_transformer(THD *thd)
}
-/* a IS NULL -> a IS NOT NULL */
+/**
+ a IS NULL -> a IS NOT NULL.
+*/
Item *Item_func_isnull::neg_transformer(THD *thd)
{
Item *item= new Item_func_isnotnull(args[0]);
@@ -4808,7 +4933,9 @@ Item *Item_func_isnull::neg_transformer(THD *thd)
}
-/* a IS NOT NULL -> a IS NULL */
+/**
+ a IS NOT NULL -> a IS NULL.
+*/
Item *Item_func_isnotnull::neg_transformer(THD *thd)
{
Item *item= new Item_func_isnull(args[0]);
@@ -4891,7 +5018,9 @@ Item *Item_func_le::negated_item() /* a <= b -> a > b */
return new Item_func_gt(args[0], args[1]);
}
-// just fake method, should never be called
+/**
+ just fake method, should never be called.
+*/
Item *Item_bool_rowready_func2::negated_item()
{
DBUG_ASSERT(0);
@@ -4956,18 +5085,16 @@ uint Item_equal::members()
}
-/*
- Check whether a field is referred in the multiple equality
+/**
+ Check whether a field is referred in the multiple equality.
- SYNOPSIS
- contains()
- field field whose occurrence is to be checked
-
- DESCRIPTION
- The function checks whether field is occurred in the Item_equal object
-
- RETURN VALUES
+ The function checks whether field is occurred in the Item_equal object .
+
+ @param field field whose occurrence is to be checked
+
+ @retval
1 if nultiple equality contains a reference to field
+ @retval
0 otherwise
*/
@@ -4984,22 +5111,15 @@ bool Item_equal::contains(Field *field)
}
-/*
- Join members of another Item_equal object
-
- SYNOPSIS
- merge()
- item multiple equality whose members are to be joined
+/**
+ Join members of another Item_equal object.
- DESCRIPTION
The function actually merges two multiple equalities.
After this operation the Item_equal object additionally contains
the field items of another item of the type Item_equal.
If the optional constant items are not equal the cond_false flag is
set to 1.
-
- RETURN VALUES
- none
+ @param item multiple equality whose members are to be joined
*/
void Item_equal::merge(Item_equal *item)
@@ -5019,28 +5139,21 @@ void Item_equal::merge(Item_equal *item)
}
-/*
- Order field items in multiple equality according to a sorting criteria
+/**
+ Order field items in multiple equality according to a sorting criteria.
- SYNOPSIS
- sort()
- cmp function to compare field item
- arg context extra parameter for the cmp function
-
- DESCRIPTION
- The function perform ordering of the field items in the Item_equal
- object according to the criteria determined by the cmp callback parameter.
- If cmp(item_field1,item_field2,arg)<0 than item_field1 must be
- placed after item_fiel2.
+ The function perform ordering of the field items in the Item_equal
+ object according to the criteria determined by the cmp callback parameter.
+ If cmp(item_field1,item_field2,arg)<0 than item_field1 must be
+ placed after item_fiel2.
- IMPLEMENTATION
- The function sorts field items by the exchange sort algorithm.
- The list of field items is looked through and whenever two neighboring
- members follow in a wrong order they are swapped. This is performed
- again and again until we get all members in a right order.
-
- RETURN VALUES
- None
+ The function sorts field items by the exchange sort algorithm.
+ The list of field items is looked through and whenever two neighboring
+ members follow in a wrong order they are swapped. This is performed
+ again and again until we get all members in a right order.
+
+ @param cmp function to compare field item
+ @param arg context extra parameter for the cmp function
*/
void Item_equal::sort(Item_field_cmpfunc cmp, void *arg)
@@ -5075,21 +5188,14 @@ void Item_equal::sort(Item_field_cmpfunc cmp, void *arg)
}
-/*
- Check appearance of new constant items in the multiple equality object
+/**
+ Check appearance of new constant items in the multiple equality object.
- SYNOPSIS
- update_const()
-
- DESCRIPTION
- The function checks appearance of new constant items among
- the members of multiple equalities. Each new constant item is
- compared with the designated constant item if there is any in the
- multiple equality. If there is none the first new constant item
- becomes designated.
-
- RETURN VALUES
- none
+ The function checks appearance of new constant items among
+ the members of multiple equalities. Each new constant item is
+ compared with the designated constant item if there is any in the
+ multiple equality. If there is none the first new constant item
+ becomes designated.
*/
void Item_equal::update_const()
@@ -5170,17 +5276,19 @@ void Item_equal::fix_length_and_dec()
item->collation.collation);
}
-bool Item_equal::walk(Item_processor processor, byte *arg)
+bool Item_equal::walk(Item_processor processor, bool walk_subquery, uchar *arg)
{
List_iterator_fast<Item_field> it(fields);
Item *item;
while ((item= it++))
- if (item->walk(processor, arg))
+ {
+ if (item->walk(processor, walk_subquery, arg))
return 1;
- return Item_func::walk(processor, arg);
+ }
+ return Item_func::walk(processor, walk_subquery, arg);
}
-Item *Item_equal::transform(Item_transformer transformer, byte *arg)
+Item *Item_equal::transform(Item_transformer transformer, uchar *arg)
{
DBUG_ASSERT(!current_thd->is_stmt_prepare());
@@ -5204,24 +5312,24 @@ Item *Item_equal::transform(Item_transformer transformer, byte *arg)
return Item_func::transform(transformer, arg);
}
-void Item_equal::print(String *str)
+void Item_equal::print(String *str, enum_query_type query_type)
{
str->append(func_name());
str->append('(');
List_iterator_fast<Item_field> it(fields);
Item *item;
if (const_item)
- const_item->print(str);
+ const_item->print(str, query_type);
else
{
item= it++;
- item->print(str);
+ item->print(str, query_type);
}
while ((item= it++))
{
str->append(',');
str->append(' ');
- item->print(str);
+ item->print(str, query_type);
}
str->append(')');
}
diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h
index db831c7030c..c2227fa04e0 100644
--- a/sql/item_cmpfunc.h
+++ b/sql/item_cmpfunc.h
@@ -123,7 +123,7 @@ public:
virtual bool val_bool();
virtual longlong val_int();
virtual void fix_length_and_dec();
- virtual void print(String *str);
+ virtual void print(String *str, enum_query_type query_type);
protected:
Item_func_truth(Item *a, bool a_value, bool a_affirmative)
@@ -338,7 +338,12 @@ public:
optimize_type select_optimize() const { return OPTIMIZE_OP; }
virtual enum Functype rev_functype() const { return UNKNOWN_FUNC; }
bool have_rev_func() const { return rev_functype() != UNKNOWN_FUNC; }
- void print(String *str) { Item_func::print_op(str); }
+
+ virtual inline void print(String *str, enum_query_type query_type)
+ {
+ Item_func::print_op(str, query_type);
+ }
+
bool is_null() { return test(args[0]->is_null() || args[1]->is_null()); }
bool is_bool_func() { return 1; }
CHARSET_INFO *compare_collation() { return cmp.cmp_collation.collation; }
@@ -357,7 +362,7 @@ public:
}
Item *neg_transformer(THD *thd);
virtual Item *negated_item();
- bool subst_argument_checker(byte **arg) { return TRUE; }
+ bool subst_argument_checker(uchar **arg) { return TRUE; }
};
class Item_func_not :public Item_bool_func
@@ -368,7 +373,7 @@ public:
enum Functype functype() const { return NOT_FUNC; }
const char *func_name() const { return "not"; }
Item *neg_transformer(THD *thd);
- void print(String *str);
+ virtual void print(String *str, enum_query_type query_type);
};
class Item_maxmin_subselect;
@@ -433,7 +438,7 @@ public:
longlong val_int();
enum Functype functype() const { return NOT_ALL_FUNC; }
const char *func_name() const { return "<not>"; }
- void print(String *str);
+ virtual void print(String *str, enum_query_type query_type);
void set_sum_test(Item_sum_hybrid *item) { test_sum_item= item; };
void set_sub_test(Item_maxmin_subselect *item) { test_sub_item= item; };
bool empty_underlying_subquery();
@@ -572,7 +577,7 @@ public:
return this;
}
bool eq(const Item *item, bool binary_cmp) const;
- bool subst_argument_checker(byte **arg) { return TRUE; }
+ bool subst_argument_checker(uchar **arg) { return TRUE; }
};
@@ -594,7 +599,7 @@ public:
const char *func_name() const { return "between"; }
bool fix_fields(THD *, Item **);
void fix_length_and_dec();
- void print(String *str);
+ virtual void print(String *str, enum_query_type query_type);
bool is_bool_func() { return 1; }
CHARSET_INFO *compare_collation() { return cmp_collation.collation; }
uint decimal_precision() const { return 1; }
@@ -608,7 +613,11 @@ public:
longlong val_int();
optimize_type select_optimize() const { return OPTIMIZE_NONE; }
const char *func_name() const { return "strcmp"; }
- void print(String *str) { Item_func::print(str); }
+
+ virtual inline void print(String *str, enum_query_type query_type)
+ {
+ Item_func::print(str, query_type);
+ }
};
@@ -711,55 +720,14 @@ public:
void fix_length_and_dec();
uint decimal_precision() const { return args[0]->decimal_precision(); }
const char *func_name() const { return "nullif"; }
- void print(String *str) { Item_func::print(str); }
- table_map not_null_tables() const { return 0; }
- bool is_null();
-};
-
-class Item_func_case :public Item_func
-{
- int first_expr_num, else_expr_num;
- enum Item_result cached_result_type;
- String tmp_value;
- uint ncases;
- Item_result cmp_type;
- DTCollation cmp_collation;
- enum_field_types cached_field_type;
-public:
- Item_func_case(List<Item> &list, Item *first_expr_arg, Item *else_expr_arg)
- :Item_func(), first_expr_num(-1), else_expr_num(-1),
- cached_result_type(INT_RESULT)
+ virtual inline void print(String *str, enum_query_type query_type)
{
- ncases= list.elements;
- if (first_expr_arg)
- {
- first_expr_num= list.elements;
- list.push_back(first_expr_arg);
- }
- if (else_expr_arg)
- {
- else_expr_num= list.elements;
- list.push_back(else_expr_arg);
- }
- set_arguments(list);
+ Item_func::print(str, query_type);
}
- double val_real();
- longlong val_int();
- String *val_str(String *);
- my_decimal *val_decimal(my_decimal *);
- bool fix_fields(THD *thd, Item **ref);
- void fix_length_and_dec();
- uint decimal_precision() const;
+
table_map not_null_tables() const { return 0; }
- enum Item_result result_type () const { return cached_result_type; }
- enum_field_types field_type() const { return cached_field_type; }
- const char *func_name() const { return "case"; }
- void print(String *str);
- Item *find_item(String *str);
- CHARSET_INFO *compare_collation() { return cmp_collation.collation; }
- void agg_str_lengths(Item *arg);
- void agg_num_lengths(Item *arg);
+ bool is_null();
};
@@ -785,7 +753,7 @@ public:
count(elements), used_count(elements) {}
virtual ~in_vector() {}
virtual void set(uint pos,Item *item)=0;
- virtual byte *get_value(Item *item)=0;
+ virtual uchar *get_value(Item *item)=0;
void sort()
{
my_qsort2(base,used_count,size,compare,collation);
@@ -817,6 +785,7 @@ public:
{
return test(compare(collation, base + pos1*size, base + pos2*size));
}
+ virtual Item_result result_type()= 0;
};
class in_string :public in_vector
@@ -827,7 +796,7 @@ public:
in_string(uint elements,qsort2_cmp cmp_func, CHARSET_INFO *cs);
~in_string();
void set(uint pos,Item *item);
- byte *get_value(Item *item);
+ uchar *get_value(Item *item);
Item* create_item()
{
return new Item_string(collation);
@@ -838,6 +807,7 @@ public:
Item_string *to= (Item_string*)item;
to->str_value= *str;
}
+ Item_result result_type() { return STRING_RESULT; }
};
class in_longlong :public in_vector
@@ -856,7 +826,7 @@ protected:
public:
in_longlong(uint elements);
void set(uint pos,Item *item);
- byte *get_value(Item *item);
+ uchar *get_value(Item *item);
Item* create_item()
{
@@ -872,6 +842,7 @@ public:
((Item_int*) item)->unsigned_flag= (my_bool)
((packed_longlong*) base)[pos].unsigned_flag;
}
+ Item_result result_type() { return INT_RESULT; }
friend int cmp_longlong(void *cmp_arg, packed_longlong *a,packed_longlong *b);
};
@@ -896,35 +867,37 @@ public:
:in_longlong(elements), thd(current_thd), warn_item(warn_item_arg),
lval_cache(0) {};
void set(uint pos,Item *item);
- byte *get_value(Item *item);
+ uchar *get_value(Item *item);
friend int cmp_longlong(void *cmp_arg, packed_longlong *a,packed_longlong *b);
};
+
class in_double :public in_vector
{
double tmp;
public:
in_double(uint elements);
void set(uint pos,Item *item);
- byte *get_value(Item *item);
+ uchar *get_value(Item *item);
Item *create_item()
{
- return new Item_float(0.0);
+ return new Item_float(0.0, 0);
}
void value_to_item(uint pos, Item *item)
{
((Item_float*)item)->value= ((double*) base)[pos];
}
-
+ Item_result result_type() { return REAL_RESULT; }
};
+
class in_decimal :public in_vector
{
my_decimal val;
public:
in_decimal(uint elements);
void set(uint pos, Item *item);
- byte *get_value(Item *item);
+ uchar *get_value(Item *item);
Item *create_item()
{
return new Item_decimal(0, FALSE);
@@ -935,6 +908,8 @@ public:
Item_decimal *item_dec= (Item_decimal*)item;
item_dec->set_decimal_value(dec);
}
+ Item_result result_type() { return DECIMAL_RESULT; }
+
};
@@ -965,7 +940,9 @@ class cmp_item_string :public cmp_item
protected:
String *value_res;
public:
+ cmp_item_string () {}
cmp_item_string (CHARSET_INFO *cs) { cmp_charset= cs; }
+ void set_charset(CHARSET_INFO *cs) { cmp_charset= cs; }
friend class cmp_item_sort_string;
friend class cmp_item_sort_string_in_static;
};
@@ -976,6 +953,8 @@ protected:
char value_buff[STRING_BUFFER_USUAL_SIZE];
String value;
public:
+ cmp_item_sort_string():
+ cmp_item_string() {}
cmp_item_sort_string(CHARSET_INFO *cs):
cmp_item_string(cs),
value(value_buff, sizeof(value_buff), cs) {}
@@ -997,6 +976,11 @@ public:
return sortcmp(value_res, l_cmp->value_res, cmp_charset);
}
cmp_item *make_same();
+ void set_charset(CHARSET_INFO *cs)
+ {
+ cmp_charset= cs;
+ value.set_quick(value_buff, sizeof(value_buff), cs);
+ }
};
class cmp_item_int :public cmp_item
@@ -1111,22 +1095,111 @@ public:
}
};
+
+/*
+ The class Item_func_case is the CASE ... WHEN ... THEN ... END function
+ implementation.
+
+ When there is no expression between CASE and the first WHEN
+ (the CASE expression) then this function simple checks all WHEN expressions
+ one after another. When some WHEN expression evaluated to TRUE then the
+ value of the corresponding THEN expression is returned.
+
+ When the CASE expression is specified then it is compared to each WHEN
+ expression individually. When an equal WHEN expression is found
+ corresponding THEN expression is returned.
+ In order to do correct comparisons several comparators are used. One for
+ each result type. Different result types that are used in particular
+ CASE ... END expression are collected in the fix_length_and_dec() member
+ function and only comparators for there result types are used.
+*/
+
+class Item_func_case :public Item_func
+{
+ int first_expr_num, else_expr_num;
+ enum Item_result cached_result_type, left_result_type;
+ String tmp_value;
+ uint ncases;
+ Item_result cmp_type;
+ DTCollation cmp_collation;
+ enum_field_types cached_field_type;
+ cmp_item *cmp_items[5]; /* For all result types */
+ cmp_item *case_item;
+public:
+ Item_func_case(List<Item> &list, Item *first_expr_arg, Item *else_expr_arg)
+ :Item_func(), first_expr_num(-1), else_expr_num(-1),
+ cached_result_type(INT_RESULT), left_result_type(INT_RESULT), case_item(0)
+ {
+ ncases= list.elements;
+ if (first_expr_arg)
+ {
+ first_expr_num= list.elements;
+ list.push_back(first_expr_arg);
+ }
+ if (else_expr_arg)
+ {
+ else_expr_num= list.elements;
+ list.push_back(else_expr_arg);
+ }
+ set_arguments(list);
+ bzero(&cmp_items, sizeof(cmp_items));
+ }
+ double val_real();
+ longlong val_int();
+ String *val_str(String *);
+ my_decimal *val_decimal(my_decimal *);
+ bool fix_fields(THD *thd, Item **ref);
+ void fix_length_and_dec();
+ uint decimal_precision() const;
+ table_map not_null_tables() const { return 0; }
+ enum Item_result result_type () const { return cached_result_type; }
+ enum_field_types field_type() const { return cached_field_type; }
+ const char *func_name() const { return "case"; }
+ virtual void print(String *str, enum_query_type query_type);
+ Item *find_item(String *str);
+ CHARSET_INFO *compare_collation() { return cmp_collation.collation; }
+ void cleanup();
+ void agg_str_lengths(Item *arg);
+ void agg_num_lengths(Item *arg);
+};
+
+/*
+ The Item_func_in class implements the in_expr IN(values_list) function.
+
+ The current implementation distinguishes 2 cases:
+ 1) all items in the value_list are constants and have the same
+ result type. This case is handled by in_vector class.
+ 2) items in the value_list have different result types or there is some
+ non-constant items.
+ In this case Item_func_in employs several cmp_item objects to performs
+ comparisons of in_expr and an item from the values_list. One cmp_item
+ object for each result type. Different result types are collected in the
+ fix_length_and_dec() member function by means of collect_cmp_types()
+ function.
+*/
class Item_func_in :public Item_func_opt_neg
{
public:
- Item_result cmp_type;
/*
an array of values when the right hand arguments of IN
are all SQL constant and there are no nulls
*/
in_vector *array;
- cmp_item *in_item;
bool have_null;
+ /*
+ true when all arguments of the IN clause are of compatible types
+ and can be used safely as comparisons for key conditions
+ */
+ bool arg_types_compatible;
+ Item_result left_result_type;
+ cmp_item *cmp_items[6]; /* One cmp_item for each result type */
DTCollation cmp_collation;
Item_func_in(List<Item> &list)
- :Item_func_opt_neg(list), array(0), in_item(0), have_null(0)
+ :Item_func_opt_neg(list), array(0), have_null(0),
+ arg_types_compatible(FALSE)
{
+ bzero(&cmp_items, sizeof(cmp_items));
allowed_arg_cols= 0; // Fetch this value from first argument
}
longlong val_int();
@@ -1135,17 +1208,21 @@ public:
uint decimal_precision() const { return 1; }
void cleanup()
{
+ uint i;
DBUG_ENTER("Item_func_in::cleanup");
Item_int_func::cleanup();
delete array;
- delete in_item;
array= 0;
- in_item= 0;
+ for (i= 0; i <= (uint)DECIMAL_RESULT + 1; i++)
+ {
+ delete cmp_items[i];
+ cmp_items[i]= 0;
+ }
DBUG_VOID_RETURN;
}
optimize_type select_optimize() const
{ return OPTIMIZE_KEY; }
- void print(String *str);
+ virtual void print(String *str, enum_query_type query_type);
enum Functype functype() const { return IN_FUNC; }
const char *func_name() const { return " IN "; }
bool nulls_in_row();
@@ -1177,8 +1254,9 @@ public:
in_row(uint elements, Item *);
~in_row();
void set(uint pos,Item *item);
- byte *get_value(Item *item);
+ uchar *get_value(Item *item);
friend void Item_func_in::fix_length_and_dec();
+ Item_result result_type() { return ROW_RESULT; }
};
/* Functions used by where clause */
@@ -1266,7 +1344,7 @@ public:
table_map not_null_tables() const
{ return abort_on_null ? not_null_tables_cache : 0; }
Item *neg_transformer(THD *thd);
- void print(String *str);
+ virtual void print(String *str, enum_query_type query_type);
CHARSET_INFO *compare_collation() { return args[0]->collation.collation; }
void top_level_item() { abort_on_null=1; }
};
@@ -1331,7 +1409,12 @@ public:
longlong val_int();
bool fix_fields(THD *thd, Item **ref);
const char *func_name() const { return "regexp"; }
- void print(String *str) { print_op(str); }
+
+ virtual inline void print(String *str, enum_query_type query_type)
+ {
+ print_op(str, query_type);
+ }
+
CHARSET_INFO *compare_collation() { return cmp_collation.collation; }
};
@@ -1343,7 +1426,11 @@ public:
Item_func_regex(Item *a,Item *b) :Item_bool_func(a,b) {}
longlong val_int() { return 0;}
const char *func_name() const { return "regex"; }
- void print(String *str) { print_op(str); }
+
+ virtual inline void print(String *str, enum_query_type query_type)
+ {
+ print_op(str, query_type);
+ }
};
#endif /* USE_REGEX */
@@ -1380,20 +1467,20 @@ public:
List<Item>* argument_list() { return &list; }
table_map used_tables() const;
void update_used_tables();
- void print(String *str);
+ virtual void print(String *str, enum_query_type query_type);
void split_sum_func(THD *thd, Item **ref_pointer_array, List<Item> &fields);
friend int setup_conds(THD *thd, TABLE_LIST *tables, TABLE_LIST *leaves,
COND **conds);
void top_level_item() { abort_on_null=1; }
void copy_andor_arguments(THD *thd, Item_cond *item);
- bool walk(Item_processor processor, byte *arg);
- Item *transform(Item_transformer transformer, byte *arg);
+ bool walk(Item_processor processor, bool walk_subquery, uchar *arg);
+ Item *transform(Item_transformer transformer, uchar *arg);
void traverse_cond(Cond_traverser, void *arg, traverse_order order);
void neg_arguments(THD *thd);
- bool subst_argument_checker(byte **arg) { return TRUE; }
- Item *compile(Item_analyzer analyzer, byte **arg_p,
- Item_transformer transformer, byte *arg_t);
enum_field_types field_type() const { return MYSQL_TYPE_LONGLONG; }
+ bool subst_argument_checker(uchar **arg) { return TRUE; }
+ Item *compile(Item_analyzer analyzer, uchar **arg_p,
+ Item_transformer transformer, uchar *arg_t);
};
@@ -1502,9 +1589,9 @@ public:
void fix_length_and_dec();
bool fix_fields(THD *thd, Item **ref);
void update_used_tables();
- bool walk(Item_processor processor, byte *arg);
- Item *transform(Item_transformer transformer, byte *arg);
- void print(String *str);
+ bool walk(Item_processor processor, bool walk_subquery, uchar *arg);
+ Item *transform(Item_transformer transformer, uchar *arg);
+ virtual void print(String *str, enum_query_type query_type);
CHARSET_INFO *compare_collation()
{ return fields.head()->collation.collation; }
};
diff --git a/sql/item_create.cc b/sql/item_create.cc
index c897b7aef79..349c47816ad 100644
--- a/sql/item_create.cc
+++ b/sql/item_create.cc
@@ -13,781 +13,5134 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
-/* Functions to create an item. Used by lex.h */
+/**
+ @file
+
+ @brief
+ Functions to create an item. Used by sql_yac.yy
+*/
#include "mysql_priv.h"
+#include "item_create.h"
+#include "sp_head.h"
+#include "sp.h"
+
+/*
+=============================================================================
+ LOCAL DECLARATIONS
+=============================================================================
+*/
+
+/**
+ Adapter for native functions with a variable number of arguments.
+ The main use of this class is to discard the following calls:
+ <code>foo(expr1 AS name1, expr2 AS name2, ...)</code>
+ which are syntactically correct (the syntax can refer to a UDF),
+ but semantically invalid for native functions.
+*/
+
+class Create_native_func : public Create_func
+{
+public:
+ virtual Item *create(THD *thd, LEX_STRING name, List<Item> *item_list);
+
+ /**
+ Builder method, with no arguments.
+ @param thd The current thread
+ @param name The native function name
+ @param item_list The function parameters, none of which are named
+ @return An item representing the function call
+ */
+ virtual Item *create_native(THD *thd, LEX_STRING name,
+ List<Item> *item_list) = 0;
+
+protected:
+ /** Constructor. */
+ Create_native_func() {}
+ /** Destructor. */
+ virtual ~Create_native_func() {}
+};
+
+
+/**
+ Adapter for functions that takes exactly zero arguments.
+*/
+
+class Create_func_arg0 : public Create_func
+{
+public:
+ virtual Item *create(THD *thd, LEX_STRING name, List<Item> *item_list);
+
+ /**
+ Builder method, with no arguments.
+ @param thd The current thread
+ @return An item representing the function call
+ */
+ virtual Item *create(THD *thd) = 0;
+
+protected:
+ /** Constructor. */
+ Create_func_arg0() {}
+ /** Destructor. */
+ virtual ~Create_func_arg0() {}
+};
+
+
+/**
+ Adapter for functions that takes exactly one argument.
+*/
+
+class Create_func_arg1 : public Create_func
+{
+public:
+ virtual Item *create(THD *thd, LEX_STRING name, List<Item> *item_list);
+
+ /**
+ Builder method, with one argument.
+ @param thd The current thread
+ @param arg1 The first argument of the function
+ @return An item representing the function call
+ */
+ virtual Item *create(THD *thd, Item *arg1) = 0;
+
+protected:
+ /** Constructor. */
+ Create_func_arg1() {}
+ /** Destructor. */
+ virtual ~Create_func_arg1() {}
+};
+
+
+/**
+ Adapter for functions that takes exactly two arguments.
+*/
+
+class Create_func_arg2 : public Create_func
+{
+public:
+ virtual Item *create(THD *thd, LEX_STRING name, List<Item> *item_list);
+
+ /**
+ Builder method, with two arguments.
+ @param thd The current thread
+ @param arg1 The first argument of the function
+ @param arg2 The second argument of the function
+ @return An item representing the function call
+ */
+ virtual Item *create(THD *thd, Item *arg1, Item *arg2) = 0;
+
+protected:
+ /** Constructor. */
+ Create_func_arg2() {}
+ /** Destructor. */
+ virtual ~Create_func_arg2() {}
+};
+
+
+/**
+ Adapter for functions that takes exactly three arguments.
+*/
+
+class Create_func_arg3 : public Create_func
+{
+public:
+ virtual Item *create(THD *thd, LEX_STRING name, List<Item> *item_list);
+
+ /**
+ Builder method, with three arguments.
+ @param thd The current thread
+ @param arg1 The first argument of the function
+ @param arg2 The second argument of the function
+ @param arg3 The third argument of the function
+ @return An item representing the function call
+ */
+ virtual Item *create(THD *thd, Item *arg1, Item *arg2, Item *arg3) = 0;
+
+protected:
+ /** Constructor. */
+ Create_func_arg3() {}
+ /** Destructor. */
+ virtual ~Create_func_arg3() {}
+};
+
+
+/**
+ Function builder for Stored Functions.
+*/
+
+class Create_sp_func : public Create_qfunc
+{
+public:
+ virtual Item *create(THD *thd, LEX_STRING db, LEX_STRING name,
+ bool use_explicit_name, List<Item> *item_list);
+
+ static Create_sp_func s_singleton;
+
+protected:
+ /** Constructor. */
+ Create_sp_func() {}
+ /** Destructor. */
+ virtual ~Create_sp_func() {}
+};
+
+
+#ifndef HAVE_SPATIAL
+/**
+ Common (non) builder for geometry functions.
+ This builder is used in <code>--without-geometry</code> builds only,
+ to report an error.
+*/
+
+class Create_func_no_geom : public Create_func
+{
+public:
+ virtual Item *create(THD *thd, LEX_STRING name, List<Item> *item_list);
+
+ /** Singleton. */
+ static Create_func_no_geom s_singleton;
+
+protected:
+ /** Constructor. */
+ Create_func_no_geom() {}
+ /** Destructor. */
+ virtual ~Create_func_no_geom() {}
+};
+#endif
+
+
+/*
+ Concrete functions builders (native functions).
+ Please keep this list sorted in alphabetical order,
+ it helps to compare code between versions, and helps with merges conflicts.
+*/
+
+class Create_func_abs : public Create_func_arg1
+{
+public:
+ virtual Item *create(THD *thd, Item *arg1);
+
+ static Create_func_abs s_singleton;
+
+protected:
+ Create_func_abs() {}
+ virtual ~Create_func_abs() {}
+};
+
+
+class Create_func_acos : public Create_func_arg1
+{
+public:
+ virtual Item *create(THD *thd, Item *arg1);
+
+ static Create_func_acos s_singleton;
+
+protected:
+ Create_func_acos() {}
+ virtual ~Create_func_acos() {}
+};
+
+
+class Create_func_addtime : public Create_func_arg2
+{
+public:
+ virtual Item *create(THD *thd, Item *arg1, Item *arg2);
+
+ static Create_func_addtime s_singleton;
+
+protected:
+ Create_func_addtime() {}
+ virtual ~Create_func_addtime() {}
+};
+
+
+class Create_func_aes_encrypt : public Create_func_arg2
+{
+public:
+ virtual Item *create(THD *thd, Item *arg1, Item *arg2);
+
+ static Create_func_aes_encrypt s_singleton;
+
+protected:
+ Create_func_aes_encrypt() {}
+ virtual ~Create_func_aes_encrypt() {}
+};
+
+
+class Create_func_aes_decrypt : public Create_func_arg2
+{
+public:
+ virtual Item *create(THD *thd, Item *arg1, Item *arg2);
+
+ static Create_func_aes_decrypt s_singleton;
+
+protected:
+ Create_func_aes_decrypt() {}
+ virtual ~Create_func_aes_decrypt() {}
+};
+
+
+#ifdef HAVE_SPATIAL
+class Create_func_area : public Create_func_arg1
+{
+public:
+ virtual Item *create(THD *thd, Item *arg1);
+
+ static Create_func_area s_singleton;
+
+protected:
+ Create_func_area() {}
+ virtual ~Create_func_area() {}
+};
+#endif
+
+
+#ifdef HAVE_SPATIAL
+class Create_func_as_wkb : public Create_func_arg1
+{
+public:
+ virtual Item *create(THD *thd, Item *arg1);
+
+ static Create_func_as_wkb s_singleton;
+
+protected:
+ Create_func_as_wkb() {}
+ virtual ~Create_func_as_wkb() {}
+};
+#endif
+
+
+#ifdef HAVE_SPATIAL
+class Create_func_as_wkt : public Create_func_arg1
+{
+public:
+ virtual Item *create(THD *thd, Item *arg1);
+
+ static Create_func_as_wkt s_singleton;
+
+protected:
+ Create_func_as_wkt() {}
+ virtual ~Create_func_as_wkt() {}
+};
+#endif
+
+
+class Create_func_asin : public Create_func_arg1
+{
+public:
+ virtual Item *create(THD *thd, Item *arg1);
+
+ static Create_func_asin s_singleton;
+
+protected:
+ Create_func_asin() {}
+ virtual ~Create_func_asin() {}
+};
+
+
+class Create_func_atan : public Create_native_func
+{
+public:
+ virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list);
+
+ static Create_func_atan s_singleton;
+
+protected:
+ Create_func_atan() {}
+ virtual ~Create_func_atan() {}
+};
+
+
+class Create_func_benchmark : public Create_func_arg2
+{
+public:
+ virtual Item *create(THD *thd, Item *arg1, Item *arg2);
+
+ static Create_func_benchmark s_singleton;
+
+protected:
+ Create_func_benchmark() {}
+ virtual ~Create_func_benchmark() {}
+};
+
+
+class Create_func_bin : public Create_func_arg1
+{
+public:
+ virtual Item *create(THD *thd, Item *arg1);
+
+ static Create_func_bin s_singleton;
+
+protected:
+ Create_func_bin() {}
+ virtual ~Create_func_bin() {}
+};
+
+
+class Create_func_bit_count : public Create_func_arg1
+{
+public:
+ virtual Item *create(THD *thd, Item *arg1);
+
+ static Create_func_bit_count s_singleton;
+
+protected:
+ Create_func_bit_count() {}
+ virtual ~Create_func_bit_count() {}
+};
+
+
+class Create_func_bit_length : public Create_func_arg1
+{
+public:
+ virtual Item *create(THD *thd, Item *arg1);
+
+ static Create_func_bit_length s_singleton;
+
+protected:
+ Create_func_bit_length() {}
+ virtual ~Create_func_bit_length() {}
+};
+
+
+class Create_func_ceiling : public Create_func_arg1
+{
+public:
+ virtual Item *create(THD *thd, Item *arg1);
+
+ static Create_func_ceiling s_singleton;
+
+protected:
+ Create_func_ceiling() {}
+ virtual ~Create_func_ceiling() {}
+};
+
+
+#ifdef HAVE_SPATIAL
+class Create_func_centroid : public Create_func_arg1
+{
+public:
+ virtual Item *create(THD *thd, Item *arg1);
+
+ static Create_func_centroid s_singleton;
+
+protected:
+ Create_func_centroid() {}
+ virtual ~Create_func_centroid() {}
+};
+#endif
+
+
+class Create_func_char_length : public Create_func_arg1
+{
+public:
+ virtual Item *create(THD *thd, Item *arg1);
+
+ static Create_func_char_length s_singleton;
+
+protected:
+ Create_func_char_length() {}
+ virtual ~Create_func_char_length() {}
+};
+
+
+class Create_func_coercibility : public Create_func_arg1
+{
+public:
+ virtual Item *create(THD *thd, Item *arg1);
+
+ static Create_func_coercibility s_singleton;
+
+protected:
+ Create_func_coercibility() {}
+ virtual ~Create_func_coercibility() {}
+};
+
+
+class Create_func_compress : public Create_func_arg1
+{
+public:
+ virtual Item *create(THD *thd, Item *arg1);
+
+ static Create_func_compress s_singleton;
+
+protected:
+ Create_func_compress() {}
+ virtual ~Create_func_compress() {}
+};
+
+
+class Create_func_concat : public Create_native_func
+{
+public:
+ virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list);
+
+ static Create_func_concat s_singleton;
+
+protected:
+ Create_func_concat() {}
+ virtual ~Create_func_concat() {}
+};
+
+
+class Create_func_concat_ws : public Create_native_func
+{
+public:
+ virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list);
+
+ static Create_func_concat_ws s_singleton;
+
+protected:
+ Create_func_concat_ws() {}
+ virtual ~Create_func_concat_ws() {}
+};
+
+
+class Create_func_connection_id : public Create_func_arg0
+{
+public:
+ virtual Item *create(THD *thd);
+
+ static Create_func_connection_id s_singleton;
+
+protected:
+ Create_func_connection_id() {}
+ virtual ~Create_func_connection_id() {}
+};
+
+
+#ifdef HAVE_SPATIAL
+class Create_func_contains : public Create_func_arg2
+{
+public:
+ virtual Item *create(THD *thd, Item *arg1, Item *arg2);
+
+ static Create_func_contains s_singleton;
+
+protected:
+ Create_func_contains() {}
+ virtual ~Create_func_contains() {}
+};
+#endif
+
+
+class Create_func_conv : public Create_func_arg3
+{
+public:
+ virtual Item *create(THD *thd, Item *arg1, Item *arg2, Item *arg3);
+
+ static Create_func_conv s_singleton;
+
+protected:
+ Create_func_conv() {}
+ virtual ~Create_func_conv() {}
+};
+
+
+class Create_func_convert_tz : public Create_func_arg3
+{
+public:
+ virtual Item *create(THD *thd, Item *arg1, Item *arg2, Item *arg3);
+
+ static Create_func_convert_tz s_singleton;
+
+protected:
+ Create_func_convert_tz() {}
+ virtual ~Create_func_convert_tz() {}
+};
+
+
+class Create_func_cos : public Create_func_arg1
+{
+public:
+ virtual Item *create(THD *thd, Item *arg1);
+
+ static Create_func_cos s_singleton;
+
+protected:
+ Create_func_cos() {}
+ virtual ~Create_func_cos() {}
+};
+
+
+class Create_func_cot : public Create_func_arg1
+{
+public:
+ virtual Item *create(THD *thd, Item *arg1);
+
+ static Create_func_cot s_singleton;
+
+protected:
+ Create_func_cot() {}
+ virtual ~Create_func_cot() {}
+};
+
+
+class Create_func_crc32 : public Create_func_arg1
+{
+public:
+ virtual Item *create(THD *thd, Item *arg1);
+
+ static Create_func_crc32 s_singleton;
+
+protected:
+ Create_func_crc32() {}
+ virtual ~Create_func_crc32() {}
+};
+
+
+#ifdef HAVE_SPATIAL
+class Create_func_crosses : public Create_func_arg2
+{
+public:
+ virtual Item *create(THD *thd, Item *arg1, Item *arg2);
+
+ static Create_func_crosses s_singleton;
+
+protected:
+ Create_func_crosses() {}
+ virtual ~Create_func_crosses() {}
+};
+#endif
+
+
+class Create_func_date_format : public Create_func_arg2
+{
+public:
+ virtual Item *create(THD *thd, Item *arg1, Item *arg2);
+
+ static Create_func_date_format s_singleton;
+
+protected:
+ Create_func_date_format() {}
+ virtual ~Create_func_date_format() {}
+};
+
+
+class Create_func_datediff : public Create_func_arg2
+{
+public:
+ virtual Item *create(THD *thd, Item *arg1, Item *arg2);
+
+ static Create_func_datediff s_singleton;
+
+protected:
+ Create_func_datediff() {}
+ virtual ~Create_func_datediff() {}
+};
+
+
+class Create_func_dayname : public Create_func_arg1
+{
+public:
+ virtual Item *create(THD *thd, Item *arg1);
+
+ static Create_func_dayname s_singleton;
+
+protected:
+ Create_func_dayname() {}
+ virtual ~Create_func_dayname() {}
+};
+
+
+class Create_func_dayofmonth : public Create_func_arg1
+{
+public:
+ virtual Item *create(THD *thd, Item *arg1);
+
+ static Create_func_dayofmonth s_singleton;
+
+protected:
+ Create_func_dayofmonth() {}
+ virtual ~Create_func_dayofmonth() {}
+};
+
+
+class Create_func_dayofweek : public Create_func_arg1
+{
+public:
+ virtual Item *create(THD *thd, Item *arg1);
+
+ static Create_func_dayofweek s_singleton;
+
+protected:
+ Create_func_dayofweek() {}
+ virtual ~Create_func_dayofweek() {}
+};
+
+
+class Create_func_dayofyear : public Create_func_arg1
+{
+public:
+ virtual Item *create(THD *thd, Item *arg1);
+
+ static Create_func_dayofyear s_singleton;
+
+protected:
+ Create_func_dayofyear() {}
+ virtual ~Create_func_dayofyear() {}
+};
+
+
+class Create_func_decode : public Create_func_arg2
+{
+public:
+ virtual Item *create(THD *thd, Item *arg1, Item *arg2);
+
+ static Create_func_decode s_singleton;
+
+protected:
+ Create_func_decode() {}
+ virtual ~Create_func_decode() {}
+};
+
+
+class Create_func_degrees : public Create_func_arg1
+{
+public:
+ virtual Item *create(THD *thd, Item *arg1);
+
+ static Create_func_degrees s_singleton;
+
+protected:
+ Create_func_degrees() {}
+ virtual ~Create_func_degrees() {}
+};
+
+
+class Create_func_des_decrypt : public Create_native_func
+{
+public:
+ virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list);
+
+ static Create_func_des_decrypt s_singleton;
+
+protected:
+ Create_func_des_decrypt() {}
+ virtual ~Create_func_des_decrypt() {}
+};
+
+
+class Create_func_des_encrypt : public Create_native_func
+{
+public:
+ virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list);
+
+ static Create_func_des_encrypt s_singleton;
+
+protected:
+ Create_func_des_encrypt() {}
+ virtual ~Create_func_des_encrypt() {}
+};
+
+
+#ifdef HAVE_SPATIAL
+class Create_func_dimension : public Create_func_arg1
+{
+public:
+ virtual Item *create(THD *thd, Item *arg1);
+
+ static Create_func_dimension s_singleton;
+
+protected:
+ Create_func_dimension() {}
+ virtual ~Create_func_dimension() {}
+};
+#endif
+
+
+#ifdef HAVE_SPATIAL
+class Create_func_disjoint : public Create_func_arg2
+{
+public:
+ virtual Item *create(THD *thd, Item *arg1, Item *arg2);
+
+ static Create_func_disjoint s_singleton;
+
+protected:
+ Create_func_disjoint() {}
+ virtual ~Create_func_disjoint() {}
+};
+#endif
+
+
+class Create_func_elt : public Create_native_func
+{
+public:
+ virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list);
+
+ static Create_func_elt s_singleton;
+
+protected:
+ Create_func_elt() {}
+ virtual ~Create_func_elt() {}
+};
+
+
+class Create_func_encode : public Create_func_arg2
+{
+public:
+ virtual Item *create(THD *thd, Item *arg1, Item *arg2);
+
+ static Create_func_encode s_singleton;
+
+protected:
+ Create_func_encode() {}
+ virtual ~Create_func_encode() {}
+};
+
+
+class Create_func_encrypt : public Create_native_func
+{
+public:
+ virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list);
+
+ static Create_func_encrypt s_singleton;
+
+protected:
+ Create_func_encrypt() {}
+ virtual ~Create_func_encrypt() {}
+};
+
+
+#ifdef HAVE_SPATIAL
+class Create_func_endpoint : public Create_func_arg1
+{
+public:
+ virtual Item *create(THD *thd, Item *arg1);
+
+ static Create_func_endpoint s_singleton;
+
+protected:
+ Create_func_endpoint() {}
+ virtual ~Create_func_endpoint() {}
+};
+#endif
+
+
+#ifdef HAVE_SPATIAL
+class Create_func_envelope : public Create_func_arg1
+{
+public:
+ virtual Item *create(THD *thd, Item *arg1);
+
+ static Create_func_envelope s_singleton;
+
+protected:
+ Create_func_envelope() {}
+ virtual ~Create_func_envelope() {}
+};
+#endif
+
+
+#ifdef HAVE_SPATIAL
+class Create_func_equals : public Create_func_arg2
+{
+public:
+ virtual Item *create(THD *thd, Item *arg1, Item *arg2);
+
+ static Create_func_equals s_singleton;
+
+protected:
+ Create_func_equals() {}
+ virtual ~Create_func_equals() {}
+};
+#endif
+
+
+class Create_func_exp : public Create_func_arg1
+{
+public:
+ virtual Item *create(THD *thd, Item *arg1);
+
+ static Create_func_exp s_singleton;
+
+protected:
+ Create_func_exp() {}
+ virtual ~Create_func_exp() {}
+};
+
+
+class Create_func_export_set : public Create_native_func
+{
+public:
+ virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list);
+
+ static Create_func_export_set s_singleton;
+
+protected:
+ Create_func_export_set() {}
+ virtual ~Create_func_export_set() {}
+};
+
+
+#ifdef HAVE_SPATIAL
+class Create_func_exteriorring : public Create_func_arg1
+{
+public:
+ virtual Item *create(THD *thd, Item *arg1);
+
+ static Create_func_exteriorring s_singleton;
+
+protected:
+ Create_func_exteriorring() {}
+ virtual ~Create_func_exteriorring() {}
+};
+#endif
+
+
+class Create_func_field : public Create_native_func
+{
+public:
+ virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list);
+
+ static Create_func_field s_singleton;
+
+protected:
+ Create_func_field() {}
+ virtual ~Create_func_field() {}
+};
+
+
+class Create_func_find_in_set : public Create_func_arg2
+{
+public:
+ virtual Item *create(THD *thd, Item *arg1, Item *arg2);
+
+ static Create_func_find_in_set s_singleton;
+
+protected:
+ Create_func_find_in_set() {}
+ virtual ~Create_func_find_in_set() {}
+};
+
+
+class Create_func_floor : public Create_func_arg1
+{
+public:
+ virtual Item *create(THD *thd, Item *arg1);
+
+ static Create_func_floor s_singleton;
+
+protected:
+ Create_func_floor() {}
+ virtual ~Create_func_floor() {}
+};
+
+
+class Create_func_format : public Create_func_arg2
+{
+public:
+ virtual Item *create(THD *thd, Item *arg1, Item *arg2);
+
+ static Create_func_format s_singleton;
+
+protected:
+ Create_func_format() {}
+ virtual ~Create_func_format() {}
+};
+
+
+class Create_func_found_rows : public Create_func_arg0
+{
+public:
+ virtual Item *create(THD *thd);
+
+ static Create_func_found_rows s_singleton;
+
+protected:
+ Create_func_found_rows() {}
+ virtual ~Create_func_found_rows() {}
+};
+
+
+class Create_func_from_days : public Create_func_arg1
+{
+public:
+ virtual Item *create(THD *thd, Item *arg1);
+
+ static Create_func_from_days s_singleton;
+
+protected:
+ Create_func_from_days() {}
+ virtual ~Create_func_from_days() {}
+};
+
+
+class Create_func_from_unixtime : public Create_native_func
+{
+public:
+ virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list);
+
+ static Create_func_from_unixtime s_singleton;
+
+protected:
+ Create_func_from_unixtime() {}
+ virtual ~Create_func_from_unixtime() {}
+};
+
+
+#ifdef HAVE_SPATIAL
+class Create_func_geometry_from_text : public Create_native_func
+{
+public:
+ virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list);
+
+ static Create_func_geometry_from_text s_singleton;
+
+protected:
+ Create_func_geometry_from_text() {}
+ virtual ~Create_func_geometry_from_text() {}
+};
+#endif
+
+
+#ifdef HAVE_SPATIAL
+class Create_func_geometry_from_wkb : public Create_native_func
+{
+public:
+ virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list);
+
+ static Create_func_geometry_from_wkb s_singleton;
+
+protected:
+ Create_func_geometry_from_wkb() {}
+ virtual ~Create_func_geometry_from_wkb() {}
+};
+#endif
+
+
+#ifdef HAVE_SPATIAL
+class Create_func_geometry_type : public Create_func_arg1
+{
+public:
+ virtual Item *create(THD *thd, Item *arg1);
+
+ static Create_func_geometry_type s_singleton;
+
+protected:
+ Create_func_geometry_type() {}
+ virtual ~Create_func_geometry_type() {}
+};
+#endif
+
+
+#ifdef HAVE_SPATIAL
+class Create_func_geometryn : public Create_func_arg2
+{
+public:
+ virtual Item *create(THD *thd, Item *arg1, Item *arg2);
+
+ static Create_func_geometryn s_singleton;
+
+protected:
+ Create_func_geometryn() {}
+ virtual ~Create_func_geometryn() {}
+};
+#endif
+
+
+class Create_func_get_lock : public Create_func_arg2
+{
+public:
+ virtual Item *create(THD *thd, Item *arg1, Item *arg2);
+
+ static Create_func_get_lock s_singleton;
+
+protected:
+ Create_func_get_lock() {}
+ virtual ~Create_func_get_lock() {}
+};
+
+
+#ifdef HAVE_SPATIAL
+class Create_func_glength : public Create_func_arg1
+{
+public:
+ virtual Item *create(THD *thd, Item *arg1);
+
+ static Create_func_glength s_singleton;
+
+protected:
+ Create_func_glength() {}
+ virtual ~Create_func_glength() {}
+};
+#endif
+
+
+class Create_func_greatest : public Create_native_func
+{
+public:
+ virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list);
+
+ static Create_func_greatest s_singleton;
+
+protected:
+ Create_func_greatest() {}
+ virtual ~Create_func_greatest() {}
+};
+
+
+class Create_func_hex : public Create_func_arg1
+{
+public:
+ virtual Item *create(THD *thd, Item *arg1);
+
+ static Create_func_hex s_singleton;
+
+protected:
+ Create_func_hex() {}
+ virtual ~Create_func_hex() {}
+};
+
+
+class Create_func_ifnull : public Create_func_arg2
+{
+public:
+ virtual Item *create(THD *thd, Item *arg1, Item *arg2);
+
+ static Create_func_ifnull s_singleton;
+
+protected:
+ Create_func_ifnull() {}
+ virtual ~Create_func_ifnull() {}
+};
+
+
+class Create_func_inet_ntoa : public Create_func_arg1
+{
+public:
+ virtual Item *create(THD *thd, Item *arg1);
+
+ static Create_func_inet_ntoa s_singleton;
+
+protected:
+ Create_func_inet_ntoa() {}
+ virtual ~Create_func_inet_ntoa() {}
+};
+
+
+class Create_func_inet_aton : public Create_func_arg1
+{
+public:
+ virtual Item *create(THD *thd, Item *arg1);
+
+ static Create_func_inet_aton s_singleton;
+
+protected:
+ Create_func_inet_aton() {}
+ virtual ~Create_func_inet_aton() {}
+};
+
+
+class Create_func_instr : public Create_func_arg2
+{
+public:
+ virtual Item *create(THD *thd, Item *arg1, Item *arg2);
+
+ static Create_func_instr s_singleton;
+
+protected:
+ Create_func_instr() {}
+ virtual ~Create_func_instr() {}
+};
+
+
+#ifdef HAVE_SPATIAL
+class Create_func_interiorringn : public Create_func_arg2
+{
+public:
+ virtual Item *create(THD *thd, Item *arg1, Item *arg2);
+
+ static Create_func_interiorringn s_singleton;
+
+protected:
+ Create_func_interiorringn() {}
+ virtual ~Create_func_interiorringn() {}
+};
+#endif
+
+
+#ifdef HAVE_SPATIAL
+class Create_func_intersects : public Create_func_arg2
+{
+public:
+ virtual Item *create(THD *thd, Item *arg1, Item *arg2);
+
+ static Create_func_intersects s_singleton;
+
+protected:
+ Create_func_intersects() {}
+ virtual ~Create_func_intersects() {}
+};
+#endif
+
+
+class Create_func_is_free_lock : public Create_func_arg1
+{
+public:
+ virtual Item *create(THD *thd, Item *arg1);
+
+ static Create_func_is_free_lock s_singleton;
+
+protected:
+ Create_func_is_free_lock() {}
+ virtual ~Create_func_is_free_lock() {}
+};
+
+
+class Create_func_is_used_lock : public Create_func_arg1
+{
+public:
+ virtual Item *create(THD *thd, Item *arg1);
+
+ static Create_func_is_used_lock s_singleton;
+
+protected:
+ Create_func_is_used_lock() {}
+ virtual ~Create_func_is_used_lock() {}
+};
+
+
+#ifdef HAVE_SPATIAL
+class Create_func_isclosed : public Create_func_arg1
+{
+public:
+ virtual Item *create(THD *thd, Item *arg1);
+
+ static Create_func_isclosed s_singleton;
+
+protected:
+ Create_func_isclosed() {}
+ virtual ~Create_func_isclosed() {}
+};
+#endif
+
+
+#ifdef HAVE_SPATIAL
+class Create_func_isempty : public Create_func_arg1
+{
+public:
+ virtual Item *create(THD *thd, Item *arg1);
+
+ static Create_func_isempty s_singleton;
+
+protected:
+ Create_func_isempty() {}
+ virtual ~Create_func_isempty() {}
+};
+#endif
+
+
+class Create_func_isnull : public Create_func_arg1
+{
+public:
+ virtual Item *create(THD *thd, Item *arg1);
+
+ static Create_func_isnull s_singleton;
+
+protected:
+ Create_func_isnull() {}
+ virtual ~Create_func_isnull() {}
+};
+
+
+#ifdef HAVE_SPATIAL
+class Create_func_issimple : public Create_func_arg1
+{
+public:
+ virtual Item *create(THD *thd, Item *arg1);
+
+ static Create_func_issimple s_singleton;
+
+protected:
+ Create_func_issimple() {}
+ virtual ~Create_func_issimple() {}
+};
+#endif
+
+
+class Create_func_last_day : public Create_func_arg1
+{
+public:
+ virtual Item *create(THD *thd, Item *arg1);
+
+ static Create_func_last_day s_singleton;
+
+protected:
+ Create_func_last_day() {}
+ virtual ~Create_func_last_day() {}
+};
+
+
+class Create_func_last_insert_id : public Create_native_func
+{
+public:
+ virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list);
+
+ static Create_func_last_insert_id s_singleton;
+
+protected:
+ Create_func_last_insert_id() {}
+ virtual ~Create_func_last_insert_id() {}
+};
+
+
+class Create_func_lcase : public Create_func_arg1
+{
+public:
+ virtual Item *create(THD *thd, Item *arg1);
+
+ static Create_func_lcase s_singleton;
+
+protected:
+ Create_func_lcase() {}
+ virtual ~Create_func_lcase() {}
+};
+
+
+class Create_func_least : public Create_native_func
+{
+public:
+ virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list);
+
+ static Create_func_least s_singleton;
+
+protected:
+ Create_func_least() {}
+ virtual ~Create_func_least() {}
+};
+
+
+class Create_func_length : public Create_func_arg1
+{
+public:
+ virtual Item *create(THD *thd, Item *arg1);
+
+ static Create_func_length s_singleton;
+
+protected:
+ Create_func_length() {}
+ virtual ~Create_func_length() {}
+};
+
+
+class Create_func_ln : public Create_func_arg1
+{
+public:
+ virtual Item *create(THD *thd, Item *arg1);
+
+ static Create_func_ln s_singleton;
+
+protected:
+ Create_func_ln() {}
+ virtual ~Create_func_ln() {}
+};
+
-Item *create_func_abs(Item* a)
+class Create_func_load_file : public Create_func_arg1
{
- return new Item_func_abs(a);
+public:
+ virtual Item *create(THD *thd, Item *arg1);
+
+ static Create_func_load_file s_singleton;
+
+protected:
+ Create_func_load_file() {}
+ virtual ~Create_func_load_file() {}
+};
+
+
+class Create_func_locate : public Create_native_func
+{
+public:
+ virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list);
+
+ static Create_func_locate s_singleton;
+
+protected:
+ Create_func_locate() {}
+ virtual ~Create_func_locate() {}
+};
+
+
+class Create_func_log : public Create_native_func
+{
+public:
+ virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list);
+
+ static Create_func_log s_singleton;
+
+protected:
+ Create_func_log() {}
+ virtual ~Create_func_log() {}
+};
+
+
+class Create_func_log10 : public Create_func_arg1
+{
+public:
+ virtual Item *create(THD *thd, Item *arg1);
+
+ static Create_func_log10 s_singleton;
+
+protected:
+ Create_func_log10() {}
+ virtual ~Create_func_log10() {}
+};
+
+
+class Create_func_log2 : public Create_func_arg1
+{
+public:
+ virtual Item *create(THD *thd, Item *arg1);
+
+ static Create_func_log2 s_singleton;
+
+protected:
+ Create_func_log2() {}
+ virtual ~Create_func_log2() {}
+};
+
+
+class Create_func_lpad : public Create_func_arg3
+{
+public:
+ virtual Item *create(THD *thd, Item *arg1, Item *arg2, Item *arg3);
+
+ static Create_func_lpad s_singleton;
+
+protected:
+ Create_func_lpad() {}
+ virtual ~Create_func_lpad() {}
+};
+
+
+class Create_func_ltrim : public Create_func_arg1
+{
+public:
+ virtual Item *create(THD *thd, Item *arg1);
+
+ static Create_func_ltrim s_singleton;
+
+protected:
+ Create_func_ltrim() {}
+ virtual ~Create_func_ltrim() {}
+};
+
+
+class Create_func_makedate : public Create_func_arg2
+{
+public:
+ virtual Item *create(THD *thd, Item *arg1, Item *arg2);
+
+ static Create_func_makedate s_singleton;
+
+protected:
+ Create_func_makedate() {}
+ virtual ~Create_func_makedate() {}
+};
+
+
+class Create_func_maketime : public Create_func_arg3
+{
+public:
+ virtual Item *create(THD *thd, Item *arg1, Item *arg2, Item *arg3);
+
+ static Create_func_maketime s_singleton;
+
+protected:
+ Create_func_maketime() {}
+ virtual ~Create_func_maketime() {}
+};
+
+
+class Create_func_make_set : public Create_native_func
+{
+public:
+ virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list);
+
+ static Create_func_make_set s_singleton;
+
+protected:
+ Create_func_make_set() {}
+ virtual ~Create_func_make_set() {}
+};
+
+
+class Create_func_master_pos_wait : public Create_native_func
+{
+public:
+ virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list);
+
+ static Create_func_master_pos_wait s_singleton;
+
+protected:
+ Create_func_master_pos_wait() {}
+ virtual ~Create_func_master_pos_wait() {}
+};
+
+
+class Create_func_md5 : public Create_func_arg1
+{
+public:
+ virtual Item *create(THD *thd, Item *arg1);
+
+ static Create_func_md5 s_singleton;
+
+protected:
+ Create_func_md5() {}
+ virtual ~Create_func_md5() {}
+};
+
+
+class Create_func_monthname : public Create_func_arg1
+{
+public:
+ virtual Item *create(THD *thd, Item *arg1);
+
+ static Create_func_monthname s_singleton;
+
+protected:
+ Create_func_monthname() {}
+ virtual ~Create_func_monthname() {}
+};
+
+
+class Create_func_name_const : public Create_func_arg2
+{
+public:
+ virtual Item *create(THD *thd, Item *arg1, Item *arg2);
+
+ static Create_func_name_const s_singleton;
+
+protected:
+ Create_func_name_const() {}
+ virtual ~Create_func_name_const() {}
+};
+
+
+class Create_func_nullif : public Create_func_arg2
+{
+public:
+ virtual Item *create(THD *thd, Item *arg1, Item *arg2);
+
+ static Create_func_nullif s_singleton;
+
+protected:
+ Create_func_nullif() {}
+ virtual ~Create_func_nullif() {}
+};
+
+
+#ifdef HAVE_SPATIAL
+class Create_func_numgeometries : public Create_func_arg1
+{
+public:
+ virtual Item *create(THD *thd, Item *arg1);
+
+ static Create_func_numgeometries s_singleton;
+
+protected:
+ Create_func_numgeometries() {}
+ virtual ~Create_func_numgeometries() {}
+};
+#endif
+
+
+#ifdef HAVE_SPATIAL
+class Create_func_numinteriorring : public Create_func_arg1
+{
+public:
+ virtual Item *create(THD *thd, Item *arg1);
+
+ static Create_func_numinteriorring s_singleton;
+
+protected:
+ Create_func_numinteriorring() {}
+ virtual ~Create_func_numinteriorring() {}
+};
+#endif
+
+
+#ifdef HAVE_SPATIAL
+class Create_func_numpoints : public Create_func_arg1
+{
+public:
+ virtual Item *create(THD *thd, Item *arg1);
+
+ static Create_func_numpoints s_singleton;
+
+protected:
+ Create_func_numpoints() {}
+ virtual ~Create_func_numpoints() {}
+};
+#endif
+
+
+class Create_func_oct : public Create_func_arg1
+{
+public:
+ virtual Item *create(THD *thd, Item *arg1);
+
+ static Create_func_oct s_singleton;
+
+protected:
+ Create_func_oct() {}
+ virtual ~Create_func_oct() {}
+};
+
+
+class Create_func_ord : public Create_func_arg1
+{
+public:
+ virtual Item *create(THD *thd, Item *arg1);
+
+ static Create_func_ord s_singleton;
+
+protected:
+ Create_func_ord() {}
+ virtual ~Create_func_ord() {}
+};
+
+
+#ifdef HAVE_SPATIAL
+class Create_func_overlaps : public Create_func_arg2
+{
+public:
+ virtual Item *create(THD *thd, Item *arg1, Item *arg2);
+
+ static Create_func_overlaps s_singleton;
+
+protected:
+ Create_func_overlaps() {}
+ virtual ~Create_func_overlaps() {}
+};
+#endif
+
+
+class Create_func_period_add : public Create_func_arg2
+{
+public:
+ virtual Item *create(THD *thd, Item *arg1, Item *arg2);
+
+ static Create_func_period_add s_singleton;
+
+protected:
+ Create_func_period_add() {}
+ virtual ~Create_func_period_add() {}
+};
+
+
+class Create_func_period_diff : public Create_func_arg2
+{
+public:
+ virtual Item *create(THD *thd, Item *arg1, Item *arg2);
+
+ static Create_func_period_diff s_singleton;
+
+protected:
+ Create_func_period_diff() {}
+ virtual ~Create_func_period_diff() {}
+};
+
+
+class Create_func_pi : public Create_func_arg0
+{
+public:
+ virtual Item *create(THD *thd);
+
+ static Create_func_pi s_singleton;
+
+protected:
+ Create_func_pi() {}
+ virtual ~Create_func_pi() {}
+};
+
+
+#ifdef HAVE_SPATIAL
+class Create_func_pointn : public Create_func_arg2
+{
+public:
+ virtual Item *create(THD *thd, Item *arg1, Item *arg2);
+
+ static Create_func_pointn s_singleton;
+
+protected:
+ Create_func_pointn() {}
+ virtual ~Create_func_pointn() {}
+};
+#endif
+
+
+class Create_func_pow : public Create_func_arg2
+{
+public:
+ virtual Item *create(THD *thd, Item *arg1, Item *arg2);
+
+ static Create_func_pow s_singleton;
+
+protected:
+ Create_func_pow() {}
+ virtual ~Create_func_pow() {}
+};
+
+
+class Create_func_quote : public Create_func_arg1
+{
+public:
+ virtual Item *create(THD *thd, Item *arg1);
+
+ static Create_func_quote s_singleton;
+
+protected:
+ Create_func_quote() {}
+ virtual ~Create_func_quote() {}
+};
+
+
+class Create_func_radians : public Create_func_arg1
+{
+public:
+ virtual Item *create(THD *thd, Item *arg1);
+
+ static Create_func_radians s_singleton;
+
+protected:
+ Create_func_radians() {}
+ virtual ~Create_func_radians() {}
+};
+
+
+class Create_func_rand : public Create_native_func
+{
+public:
+ virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list);
+
+ static Create_func_rand s_singleton;
+
+protected:
+ Create_func_rand() {}
+ virtual ~Create_func_rand() {}
+};
+
+
+class Create_func_release_lock : public Create_func_arg1
+{
+public:
+ virtual Item *create(THD *thd, Item *arg1);
+
+ static Create_func_release_lock s_singleton;
+
+protected:
+ Create_func_release_lock() {}
+ virtual ~Create_func_release_lock() {}
+};
+
+
+class Create_func_reverse : public Create_func_arg1
+{
+public:
+ virtual Item *create(THD *thd, Item *arg1);
+
+ static Create_func_reverse s_singleton;
+
+protected:
+ Create_func_reverse() {}
+ virtual ~Create_func_reverse() {}
+};
+
+
+class Create_func_round : public Create_native_func
+{
+public:
+ virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list);
+
+ static Create_func_round s_singleton;
+
+protected:
+ Create_func_round() {}
+ virtual ~Create_func_round() {}
+};
+
+
+class Create_func_row_count : public Create_func_arg0
+{
+public:
+ virtual Item *create(THD *thd);
+
+ static Create_func_row_count s_singleton;
+
+protected:
+ Create_func_row_count() {}
+ virtual ~Create_func_row_count() {}
+};
+
+
+class Create_func_rpad : public Create_func_arg3
+{
+public:
+ virtual Item *create(THD *thd, Item *arg1, Item *arg2, Item *arg3);
+
+ static Create_func_rpad s_singleton;
+
+protected:
+ Create_func_rpad() {}
+ virtual ~Create_func_rpad() {}
+};
+
+
+class Create_func_rtrim : public Create_func_arg1
+{
+public:
+ virtual Item *create(THD *thd, Item *arg1);
+
+ static Create_func_rtrim s_singleton;
+
+protected:
+ Create_func_rtrim() {}
+ virtual ~Create_func_rtrim() {}
+};
+
+
+class Create_func_sec_to_time : public Create_func_arg1
+{
+public:
+ virtual Item *create(THD *thd, Item *arg1);
+
+ static Create_func_sec_to_time s_singleton;
+
+protected:
+ Create_func_sec_to_time() {}
+ virtual ~Create_func_sec_to_time() {}
+};
+
+
+class Create_func_sha : public Create_func_arg1
+{
+public:
+ virtual Item *create(THD *thd, Item *arg1);
+
+ static Create_func_sha s_singleton;
+
+protected:
+ Create_func_sha() {}
+ virtual ~Create_func_sha() {}
+};
+
+
+class Create_func_sign : public Create_func_arg1
+{
+public:
+ virtual Item *create(THD *thd, Item *arg1);
+
+ static Create_func_sign s_singleton;
+
+protected:
+ Create_func_sign() {}
+ virtual ~Create_func_sign() {}
+};
+
+
+class Create_func_sin : public Create_func_arg1
+{
+public:
+ virtual Item *create(THD *thd, Item *arg1);
+
+ static Create_func_sin s_singleton;
+
+protected:
+ Create_func_sin() {}
+ virtual ~Create_func_sin() {}
+};
+
+
+class Create_func_sleep : public Create_func_arg1
+{
+public:
+ virtual Item *create(THD *thd, Item *arg1);
+
+ static Create_func_sleep s_singleton;
+
+protected:
+ Create_func_sleep() {}
+ virtual ~Create_func_sleep() {}
+};
+
+
+class Create_func_soundex : public Create_func_arg1
+{
+public:
+ virtual Item *create(THD *thd, Item *arg1);
+
+ static Create_func_soundex s_singleton;
+
+protected:
+ Create_func_soundex() {}
+ virtual ~Create_func_soundex() {}
+};
+
+
+class Create_func_space : public Create_func_arg1
+{
+public:
+ virtual Item *create(THD *thd, Item *arg1);
+
+ static Create_func_space s_singleton;
+
+protected:
+ Create_func_space() {}
+ virtual ~Create_func_space() {}
+};
+
+
+class Create_func_sqrt : public Create_func_arg1
+{
+public:
+ virtual Item *create(THD *thd, Item *arg1);
+
+ static Create_func_sqrt s_singleton;
+
+protected:
+ Create_func_sqrt() {}
+ virtual ~Create_func_sqrt() {}
+};
+
+
+#ifdef HAVE_SPATIAL
+class Create_func_srid : public Create_func_arg1
+{
+public:
+ virtual Item *create(THD *thd, Item *arg1);
+
+ static Create_func_srid s_singleton;
+
+protected:
+ Create_func_srid() {}
+ virtual ~Create_func_srid() {}
+};
+#endif
+
+
+#ifdef HAVE_SPATIAL
+class Create_func_startpoint : public Create_func_arg1
+{
+public:
+ virtual Item *create(THD *thd, Item *arg1);
+
+ static Create_func_startpoint s_singleton;
+
+protected:
+ Create_func_startpoint() {}
+ virtual ~Create_func_startpoint() {}
+};
+#endif
+
+
+class Create_func_str_to_date : public Create_func_arg2
+{
+public:
+ virtual Item *create(THD *thd, Item *arg1, Item *arg2);
+
+ static Create_func_str_to_date s_singleton;
+
+protected:
+ Create_func_str_to_date() {}
+ virtual ~Create_func_str_to_date() {}
+};
+
+
+class Create_func_strcmp : public Create_func_arg2
+{
+public:
+ virtual Item *create(THD *thd, Item *arg1, Item *arg2);
+
+ static Create_func_strcmp s_singleton;
+
+protected:
+ Create_func_strcmp() {}
+ virtual ~Create_func_strcmp() {}
+};
+
+
+class Create_func_substr_index : public Create_func_arg3
+{
+public:
+ virtual Item *create(THD *thd, Item *arg1, Item *arg2, Item *arg3);
+
+ static Create_func_substr_index s_singleton;
+
+protected:
+ Create_func_substr_index() {}
+ virtual ~Create_func_substr_index() {}
+};
+
+
+class Create_func_subtime : public Create_func_arg2
+{
+public:
+ virtual Item *create(THD *thd, Item *arg1, Item *arg2);
+
+ static Create_func_subtime s_singleton;
+
+protected:
+ Create_func_subtime() {}
+ virtual ~Create_func_subtime() {}
+};
+
+
+class Create_func_tan : public Create_func_arg1
+{
+public:
+ virtual Item *create(THD *thd, Item *arg1);
+
+ static Create_func_tan s_singleton;
+
+protected:
+ Create_func_tan() {}
+ virtual ~Create_func_tan() {}
+};
+
+
+class Create_func_time_format : public Create_func_arg2
+{
+public:
+ virtual Item *create(THD *thd, Item *arg1, Item *arg2);
+
+ static Create_func_time_format s_singleton;
+
+protected:
+ Create_func_time_format() {}
+ virtual ~Create_func_time_format() {}
+};
+
+
+class Create_func_time_to_sec : public Create_func_arg1
+{
+public:
+ virtual Item *create(THD *thd, Item *arg1);
+
+ static Create_func_time_to_sec s_singleton;
+
+protected:
+ Create_func_time_to_sec() {}
+ virtual ~Create_func_time_to_sec() {}
+};
+
+
+class Create_func_timediff : public Create_func_arg2
+{
+public:
+ virtual Item *create(THD *thd, Item *arg1, Item *arg2);
+
+ static Create_func_timediff s_singleton;
+
+protected:
+ Create_func_timediff() {}
+ virtual ~Create_func_timediff() {}
+};
+
+
+class Create_func_to_days : public Create_func_arg1
+{
+public:
+ virtual Item *create(THD *thd, Item *arg1);
+
+ static Create_func_to_days s_singleton;
+
+protected:
+ Create_func_to_days() {}
+ virtual ~Create_func_to_days() {}
+};
+
+
+#ifdef HAVE_SPATIAL
+class Create_func_touches : public Create_func_arg2
+{
+public:
+ virtual Item *create(THD *thd, Item *arg1, Item *arg2);
+
+ static Create_func_touches s_singleton;
+
+protected:
+ Create_func_touches() {}
+ virtual ~Create_func_touches() {}
+};
+#endif
+
+
+class Create_func_ucase : public Create_func_arg1
+{
+public:
+ virtual Item *create(THD *thd, Item *arg1);
+
+ static Create_func_ucase s_singleton;
+
+protected:
+ Create_func_ucase() {}
+ virtual ~Create_func_ucase() {}
+};
+
+
+class Create_func_uncompress : public Create_func_arg1
+{
+public:
+ virtual Item *create(THD *thd, Item *arg1);
+
+ static Create_func_uncompress s_singleton;
+
+protected:
+ Create_func_uncompress() {}
+ virtual ~Create_func_uncompress() {}
+};
+
+
+class Create_func_uncompressed_length : public Create_func_arg1
+{
+public:
+ virtual Item *create(THD *thd, Item *arg1);
+
+ static Create_func_uncompressed_length s_singleton;
+
+protected:
+ Create_func_uncompressed_length() {}
+ virtual ~Create_func_uncompressed_length() {}
+};
+
+
+class Create_func_unhex : public Create_func_arg1
+{
+public:
+ virtual Item *create(THD *thd, Item *arg1);
+
+ static Create_func_unhex s_singleton;
+
+protected:
+ Create_func_unhex() {}
+ virtual ~Create_func_unhex() {}
+};
+
+
+class Create_func_unix_timestamp : public Create_native_func
+{
+public:
+ virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list);
+
+ static Create_func_unix_timestamp s_singleton;
+
+protected:
+ Create_func_unix_timestamp() {}
+ virtual ~Create_func_unix_timestamp() {}
+};
+
+
+class Create_func_uuid : public Create_func_arg0
+{
+public:
+ virtual Item *create(THD *thd);
+
+ static Create_func_uuid s_singleton;
+
+protected:
+ Create_func_uuid() {}
+ virtual ~Create_func_uuid() {}
+};
+
+
+class Create_func_uuid_short : public Create_func_arg0
+{
+public:
+ virtual Item *create(THD *thd);
+
+ static Create_func_uuid_short s_singleton;
+
+protected:
+ Create_func_uuid_short() {}
+ virtual ~Create_func_uuid_short() {}
+};
+
+
+class Create_func_version : public Create_func_arg0
+{
+public:
+ virtual Item *create(THD *thd);
+
+ static Create_func_version s_singleton;
+
+protected:
+ Create_func_version() {}
+ virtual ~Create_func_version() {}
+};
+
+
+class Create_func_weekday : public Create_func_arg1
+{
+public:
+ virtual Item *create(THD *thd, Item *arg1);
+
+ static Create_func_weekday s_singleton;
+
+protected:
+ Create_func_weekday() {}
+ virtual ~Create_func_weekday() {}
+};
+
+
+class Create_func_weekofyear : public Create_func_arg1
+{
+public:
+ virtual Item *create(THD *thd, Item *arg1);
+
+ static Create_func_weekofyear s_singleton;
+
+protected:
+ Create_func_weekofyear() {}
+ virtual ~Create_func_weekofyear() {}
+};
+
+
+#ifdef HAVE_SPATIAL
+class Create_func_within : public Create_func_arg2
+{
+public:
+ virtual Item *create(THD *thd, Item *arg1, Item *arg2);
+
+ static Create_func_within s_singleton;
+
+protected:
+ Create_func_within() {}
+ virtual ~Create_func_within() {}
+};
+#endif
+
+
+#ifdef HAVE_SPATIAL
+class Create_func_x : public Create_func_arg1
+{
+public:
+ virtual Item *create(THD *thd, Item *arg1);
+
+ static Create_func_x s_singleton;
+
+protected:
+ Create_func_x() {}
+ virtual ~Create_func_x() {}
+};
+#endif
+
+
+class Create_func_xml_extractvalue : public Create_func_arg2
+{
+public:
+ virtual Item *create(THD *thd, Item *arg1, Item *arg2);
+
+ static Create_func_xml_extractvalue s_singleton;
+
+protected:
+ Create_func_xml_extractvalue() {}
+ virtual ~Create_func_xml_extractvalue() {}
+};
+
+
+class Create_func_xml_update : public Create_func_arg3
+{
+public:
+ virtual Item *create(THD *thd, Item *arg1, Item *arg2, Item *arg3);
+
+ static Create_func_xml_update s_singleton;
+
+protected:
+ Create_func_xml_update() {}
+ virtual ~Create_func_xml_update() {}
+};
+
+
+#ifdef HAVE_SPATIAL
+class Create_func_y : public Create_func_arg1
+{
+public:
+ virtual Item *create(THD *thd, Item *arg1);
+
+ static Create_func_y s_singleton;
+
+protected:
+ Create_func_y() {}
+ virtual ~Create_func_y() {}
+};
+#endif
+
+
+class Create_func_year_week : public Create_native_func
+{
+public:
+ virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list);
+
+ static Create_func_year_week s_singleton;
+
+protected:
+ Create_func_year_week() {}
+ virtual ~Create_func_year_week() {}
+};
+
+
+/*
+=============================================================================
+ IMPLEMENTATION
+=============================================================================
+*/
+
+/**
+ Checks if there are named parameters in a parameter list.
+ The syntax to name parameters in a function call is as follow:
+ <code>foo(expr AS named, expr named, expr AS "named", expr "named")</code>
+ @param params The parameter list, can be null
+ @return true if one or more parameter is named
+*/
+static bool has_named_parameters(List<Item> *params)
+{
+ if (params)
+ {
+ Item *param;
+ List_iterator<Item> it(*params);
+ while ((param= it++))
+ {
+ if (! param->is_autogenerated_name)
+ return true;
+ }
+ }
+
+ return false;
+}
+
+#ifndef HAVE_SPATIAL
+Create_func_no_geom Create_func_no_geom::s_singleton;
+
+Item*
+Create_func_no_geom::create(THD * /* unused */,
+ LEX_STRING /* unused */,
+ List<Item> * /* unused */)
+{
+ /* FIXME: error message can't be translated. */
+ my_error(ER_FEATURE_DISABLED, MYF(0),
+ sym_group_geom.name, sym_group_geom.needed_define);
+ return NULL;
+}
+#endif
+
+
+Item*
+Create_qfunc::create(THD *thd, LEX_STRING name, List<Item> *item_list)
+{
+ LEX_STRING db;
+
+ if (! thd->db && ! thd->lex->sphead)
+ {
+ /*
+ The proper error message should be in the lines of:
+ Can't resolve <name>() to a function call,
+ because this function:
+ - is not a native function,
+ - is not a user defined function,
+ - can not match a qualified (read: stored) function
+ since no database is selected.
+ Reusing ER_SP_DOES_NOT_EXIST have a message consistent with
+ the case when a default database exist, see Create_sp_func::create().
+ */
+ my_error(ER_SP_DOES_NOT_EXIST, MYF(0),
+ "FUNCTION", name.str);
+ return NULL;
+ }
+
+ if (thd->lex->copy_db_to(&db.str, &db.length))
+ return NULL;
+
+ return create(thd, db, name, false, item_list);
+}
+
+
+#ifdef HAVE_DLOPEN
+Create_udf_func Create_udf_func::s_singleton;
+
+Item*
+Create_udf_func::create(THD *thd, LEX_STRING name, List<Item> *item_list)
+{
+ udf_func *udf= find_udf(name.str, name.length);
+ DBUG_ASSERT(udf);
+ return create(thd, udf, item_list);
+}
+
+
+Item*
+Create_udf_func::create(THD *thd, udf_func *udf, List<Item> *item_list)
+{
+ Item *func= NULL;
+ int arg_count= 0;
+
+ if (item_list != NULL)
+ arg_count= item_list->elements;
+
+ thd->lex->set_stmt_unsafe();
+
+ DBUG_ASSERT( (udf->type == UDFTYPE_FUNCTION)
+ || (udf->type == UDFTYPE_AGGREGATE));
+
+ switch(udf->returns) {
+ case STRING_RESULT:
+ {
+ if (udf->type == UDFTYPE_FUNCTION)
+ {
+ if (arg_count)
+ func= new (thd->mem_root) Item_func_udf_str(udf, *item_list);
+ else
+ func= new (thd->mem_root) Item_func_udf_str(udf);
+ }
+ else
+ {
+ if (arg_count)
+ func= new (thd->mem_root) Item_sum_udf_str(udf, *item_list);
+ else
+ func= new (thd->mem_root) Item_sum_udf_str(udf);
+ }
+ break;
+ }
+ case REAL_RESULT:
+ {
+ if (udf->type == UDFTYPE_FUNCTION)
+ {
+ if (arg_count)
+ func= new (thd->mem_root) Item_func_udf_float(udf, *item_list);
+ else
+ func= new (thd->mem_root) Item_func_udf_float(udf);
+ }
+ else
+ {
+ if (arg_count)
+ func= new (thd->mem_root) Item_sum_udf_float(udf, *item_list);
+ else
+ func= new (thd->mem_root) Item_sum_udf_float(udf);
+ }
+ break;
+ }
+ case INT_RESULT:
+ {
+ if (udf->type == UDFTYPE_FUNCTION)
+ {
+ if (arg_count)
+ func= new (thd->mem_root) Item_func_udf_int(udf, *item_list);
+ else
+ func= new (thd->mem_root) Item_func_udf_int(udf);
+ }
+ else
+ {
+ if (arg_count)
+ func= new (thd->mem_root) Item_sum_udf_int(udf, *item_list);
+ else
+ func= new (thd->mem_root) Item_sum_udf_int(udf);
+ }
+ break;
+ }
+ case DECIMAL_RESULT:
+ {
+ if (udf->type == UDFTYPE_FUNCTION)
+ {
+ if (arg_count)
+ func= new (thd->mem_root) Item_func_udf_decimal(udf, *item_list);
+ else
+ func= new (thd->mem_root) Item_func_udf_decimal(udf);
+ }
+ else
+ {
+ if (arg_count)
+ func= new (thd->mem_root) Item_sum_udf_decimal(udf, *item_list);
+ else
+ func= new (thd->mem_root) Item_sum_udf_decimal(udf);
+ }
+ break;
+ }
+ default:
+ {
+ my_error(ER_NOT_SUPPORTED_YET, MYF(0), "UDF return type");
+ }
+ }
+ thd->lex->safe_to_cache_query= 0;
+ return func;
}
+#endif
-Item *create_func_acos(Item* a)
+
+Create_sp_func Create_sp_func::s_singleton;
+
+Item*
+Create_sp_func::create(THD *thd, LEX_STRING db, LEX_STRING name,
+ bool use_explicit_name, List<Item> *item_list)
{
- return new Item_func_acos(a);
+ int arg_count= 0;
+ Item *func= NULL;
+ LEX *lex= thd->lex;
+ sp_name *qname;
+
+ if (has_named_parameters(item_list))
+ {
+ /*
+ The syntax "db.foo(expr AS p1, expr AS p2, ...) is invalid,
+ and has been rejected during syntactic parsing already,
+ because a stored function call may not have named parameters.
+
+ The syntax "foo(expr AS p1, expr AS p2, ...)" is correct,
+ because it can refer to a User Defined Function call.
+ For a Stored Function however, this has no semantic.
+ */
+ my_error(ER_WRONG_PARAMETERS_TO_STORED_FCT, MYF(0), name.str);
+ return NULL;
+ }
+
+ if (item_list != NULL)
+ arg_count= item_list->elements;
+
+ qname= new (thd->mem_root) sp_name(db, name, use_explicit_name);
+ qname->init_qname(thd);
+ sp_add_used_routine(lex, thd, qname, TYPE_ENUM_FUNCTION);
+
+ if (arg_count > 0)
+ func= new (thd->mem_root) Item_func_sp(lex->current_context(), qname,
+ *item_list);
+ else
+ func= new (thd->mem_root) Item_func_sp(lex->current_context(), qname);
+
+ lex->safe_to_cache_query= 0;
+ return func;
}
-Item *create_func_aes_encrypt(Item* a, Item* b)
+
+Item*
+Create_native_func::create(THD *thd, LEX_STRING name, List<Item> *item_list)
{
- return new Item_func_aes_encrypt(a, b);
+ if (has_named_parameters(item_list))
+ {
+ my_error(ER_WRONG_PARAMETERS_TO_NATIVE_FCT, MYF(0), name.str);
+ return NULL;
+ }
+
+ return create_native(thd, name, item_list);
}
-Item *create_func_aes_decrypt(Item* a, Item* b)
+
+Item*
+Create_func_arg0::create(THD *thd, LEX_STRING name, List<Item> *item_list)
{
- return new Item_func_aes_decrypt(a, b);
+ int arg_count= 0;
+
+ if (item_list != NULL)
+ arg_count= item_list->elements;
+
+ if (arg_count != 0)
+ {
+ my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str);
+ return NULL;
+ }
+
+ return create(thd);
}
-Item *create_func_ascii(Item* a)
+
+Item*
+Create_func_arg1::create(THD *thd, LEX_STRING name, List<Item> *item_list)
{
- return new Item_func_ascii(a);
+ int arg_count= 0;
+
+ if (item_list)
+ arg_count= item_list->elements;
+
+ if (arg_count != 1)
+ {
+ my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str);
+ return NULL;
+ }
+
+ Item *param_1= item_list->pop();
+
+ if (! param_1->is_autogenerated_name)
+ {
+ my_error(ER_WRONG_PARAMETERS_TO_NATIVE_FCT, MYF(0), name.str);
+ return NULL;
+ }
+
+ return create(thd, param_1);
}
-Item *create_func_ord(Item* a)
+
+Item*
+Create_func_arg2::create(THD *thd, LEX_STRING name, List<Item> *item_list)
{
- return new Item_func_ord(a);
+ int arg_count= 0;
+
+ if (item_list)
+ arg_count= item_list->elements;
+
+ if (arg_count != 2)
+ {
+ my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str);
+ return NULL;
+ }
+
+ Item *param_1= item_list->pop();
+ Item *param_2= item_list->pop();
+
+ if ( (! param_1->is_autogenerated_name)
+ || (! param_2->is_autogenerated_name))
+ {
+ my_error(ER_WRONG_PARAMETERS_TO_NATIVE_FCT, MYF(0), name.str);
+ return NULL;
+ }
+
+ return create(thd, param_1, param_2);
}
-Item *create_func_asin(Item* a)
+
+Item*
+Create_func_arg3::create(THD *thd, LEX_STRING name, List<Item> *item_list)
{
- return new Item_func_asin(a);
+ int arg_count= 0;
+
+ if (item_list)
+ arg_count= item_list->elements;
+
+ if (arg_count != 3)
+ {
+ my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str);
+ return NULL;
+ }
+
+ Item *param_1= item_list->pop();
+ Item *param_2= item_list->pop();
+ Item *param_3= item_list->pop();
+
+ if ( (! param_1->is_autogenerated_name)
+ || (! param_2->is_autogenerated_name)
+ || (! param_3->is_autogenerated_name))
+ {
+ my_error(ER_WRONG_PARAMETERS_TO_NATIVE_FCT, MYF(0), name.str);
+ return NULL;
+ }
+
+ return create(thd, param_1, param_2, param_3);
}
-Item *create_func_bin(Item* a)
+
+Create_func_abs Create_func_abs::s_singleton;
+
+Item*
+Create_func_abs::create(THD *thd, Item *arg1)
{
- return new Item_func_conv(a,new Item_int((int32) 10,2),
- new Item_int((int32) 2,1));
+ return new (thd->mem_root) Item_func_abs(arg1);
}
-Item *create_func_bit_count(Item* a)
+
+Create_func_acos Create_func_acos::s_singleton;
+
+Item*
+Create_func_acos::create(THD *thd, Item *arg1)
{
- return new Item_func_bit_count(a);
+ return new (thd->mem_root) Item_func_acos(arg1);
}
-Item *create_func_ceiling(Item* a)
+
+Create_func_addtime Create_func_addtime::s_singleton;
+
+Item*
+Create_func_addtime::create(THD *thd, Item *arg1, Item *arg2)
{
- return new Item_func_ceiling(a);
+ return new (thd->mem_root) Item_func_add_time(arg1, arg2, 0, 0);
}
-Item *create_func_connection_id(void)
+
+Create_func_aes_encrypt Create_func_aes_encrypt::s_singleton;
+
+Item*
+Create_func_aes_encrypt::create(THD *thd, Item *arg1, Item *arg2)
{
- THD *thd= current_thd;
- thd->lex->safe_to_cache_query= 0;
- return new Item_func_connection_id();
+ return new (thd->mem_root) Item_func_aes_encrypt(arg1, arg2);
}
-Item *create_func_conv(Item* a, Item *b, Item *c)
+
+Create_func_aes_decrypt Create_func_aes_decrypt::s_singleton;
+
+Item*
+Create_func_aes_decrypt::create(THD *thd, Item *arg1, Item *arg2)
{
- return new Item_func_conv(a,b,c);
+ return new (thd->mem_root) Item_func_aes_decrypt(arg1, arg2);
}
-Item *create_func_cos(Item* a)
+
+#ifdef HAVE_SPATIAL
+Create_func_area Create_func_area::s_singleton;
+
+Item*
+Create_func_area::create(THD *thd, Item *arg1)
{
- return new Item_func_cos(a);
+ return new (thd->mem_root) Item_func_area(arg1);
}
+#endif
+
-Item *create_func_cot(Item* a)
+#ifdef HAVE_SPATIAL
+Create_func_as_wkb Create_func_as_wkb::s_singleton;
+
+Item*
+Create_func_as_wkb::create(THD *thd, Item *arg1)
{
- return new Item_func_div(new Item_int((char*) "1",1,1),
- new Item_func_tan(a));
+ return new (thd->mem_root) Item_func_as_wkb(arg1);
}
+#endif
+
-Item *create_func_date_format(Item* a,Item *b)
+#ifdef HAVE_SPATIAL
+Create_func_as_wkt Create_func_as_wkt::s_singleton;
+
+Item*
+Create_func_as_wkt::create(THD *thd, Item *arg1)
{
- return new Item_func_date_format(a,b,0);
+ return new (thd->mem_root) Item_func_as_wkt(arg1);
}
+#endif
+
+
+Create_func_asin Create_func_asin::s_singleton;
-Item *create_func_dayofmonth(Item* a)
+Item*
+Create_func_asin::create(THD *thd, Item *arg1)
{
- return new Item_func_dayofmonth(a);
+ return new (thd->mem_root) Item_func_asin(arg1);
}
-Item *create_func_dayofweek(Item* a)
+
+Create_func_atan Create_func_atan::s_singleton;
+
+Item*
+Create_func_atan::create_native(THD *thd, LEX_STRING name,
+ List<Item> *item_list)
{
- return new Item_func_weekday(a, 1);
+ Item* func= NULL;
+ int arg_count= 0;
+
+ if (item_list != NULL)
+ arg_count= item_list->elements;
+
+ switch (arg_count) {
+ case 1:
+ {
+ Item *param_1= item_list->pop();
+ func= new (thd->mem_root) Item_func_atan(param_1);
+ break;
+ }
+ case 2:
+ {
+ Item *param_1= item_list->pop();
+ Item *param_2= item_list->pop();
+ func= new (thd->mem_root) Item_func_atan(param_1, param_2);
+ break;
+ }
+ default:
+ {
+ my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str);
+ break;
+ }
+ }
+
+ return func;
}
-Item *create_func_dayofyear(Item* a)
+
+Create_func_benchmark Create_func_benchmark::s_singleton;
+
+Item*
+Create_func_benchmark::create(THD *thd, Item *arg1, Item *arg2)
{
- return new Item_func_dayofyear(a);
+ thd->lex->uncacheable(UNCACHEABLE_SIDEEFFECT);
+ return new (thd->mem_root) Item_func_benchmark(arg1, arg2);
}
-Item *create_func_dayname(Item* a)
+
+Create_func_bin Create_func_bin::s_singleton;
+
+Item*
+Create_func_bin::create(THD *thd, Item *arg1)
{
- return new Item_func_dayname(a);
+ Item *i10= new (thd->mem_root) Item_int((int32) 10,2);
+ Item *i2= new (thd->mem_root) Item_int((int32) 2,1);
+ return new (thd->mem_root) Item_func_conv(arg1, i10, i2);
}
-Item *create_func_degrees(Item *a)
+
+Create_func_bit_count Create_func_bit_count::s_singleton;
+
+Item*
+Create_func_bit_count::create(THD *thd, Item *arg1)
{
- return new Item_func_units((char*) "degrees",a,180/M_PI,0.0);
+ return new (thd->mem_root) Item_func_bit_count(arg1);
}
-Item *create_func_exp(Item* a)
+
+Create_func_bit_length Create_func_bit_length::s_singleton;
+
+Item*
+Create_func_bit_length::create(THD *thd, Item *arg1)
{
- return new Item_func_exp(a);
+ return new (thd->mem_root) Item_func_bit_length(arg1);
}
-Item *create_func_find_in_set(Item* a, Item *b)
+
+Create_func_ceiling Create_func_ceiling::s_singleton;
+
+Item*
+Create_func_ceiling::create(THD *thd, Item *arg1)
{
- return new Item_func_find_in_set(a, b);
+ return new (thd->mem_root) Item_func_ceiling(arg1);
}
-Item *create_func_floor(Item* a)
+
+#ifdef HAVE_SPATIAL
+Create_func_centroid Create_func_centroid::s_singleton;
+
+Item*
+Create_func_centroid::create(THD *thd, Item *arg1)
{
- return new Item_func_floor(a);
+ return new (thd->mem_root) Item_func_centroid(arg1);
}
+#endif
+
-Item *create_func_found_rows(void)
+Create_func_char_length Create_func_char_length::s_singleton;
+
+Item*
+Create_func_char_length::create(THD *thd, Item *arg1)
{
- THD *thd=current_thd;
- thd->lex->safe_to_cache_query= 0;
- return new Item_func_found_rows();
+ return new (thd->mem_root) Item_func_char_length(arg1);
}
-Item *create_func_from_days(Item* a)
+
+Create_func_coercibility Create_func_coercibility::s_singleton;
+
+Item*
+Create_func_coercibility::create(THD *thd, Item *arg1)
{
- return new Item_func_from_days(a);
+ return new (thd->mem_root) Item_func_coercibility(arg1);
}
-Item *create_func_get_lock(Item* a, Item *b)
+
+Create_func_concat Create_func_concat::s_singleton;
+
+Item*
+Create_func_concat::create_native(THD *thd, LEX_STRING name,
+ List<Item> *item_list)
{
- current_thd->lex->uncacheable(UNCACHEABLE_SIDEEFFECT);
- return new Item_func_get_lock(a, b);
+ int arg_count= 0;
+
+ if (item_list != NULL)
+ arg_count= item_list->elements;
+
+ if (arg_count < 1)
+ {
+ my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str);
+ return NULL;
+ }
+
+ return new (thd->mem_root) Item_func_concat(*item_list);
}
-Item *create_func_hex(Item *a)
+
+Create_func_concat_ws Create_func_concat_ws::s_singleton;
+
+Item*
+Create_func_concat_ws::create_native(THD *thd, LEX_STRING name,
+ List<Item> *item_list)
{
- return new Item_func_hex(a);
+ int arg_count= 0;
+
+ if (item_list != NULL)
+ arg_count= item_list->elements;
+
+ /* "WS" stands for "With Separator": this function takes 2+ arguments */
+ if (arg_count < 2)
+ {
+ my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str);
+ return NULL;
+ }
+
+ return new (thd->mem_root) Item_func_concat_ws(*item_list);
}
-Item *create_func_inet_ntoa(Item* a)
+
+Create_func_compress Create_func_compress::s_singleton;
+
+Item*
+Create_func_compress::create(THD *thd, Item *arg1)
{
- return new Item_func_inet_ntoa(a);
+ return new (thd->mem_root) Item_func_compress(arg1);
}
-Item *create_func_inet_aton(Item* a)
+
+Create_func_connection_id Create_func_connection_id::s_singleton;
+
+Item*
+Create_func_connection_id::create(THD *thd)
{
- return new Item_func_inet_aton(a);
+ thd->lex->safe_to_cache_query= 0;
+ return new (thd->mem_root) Item_func_connection_id();
}
-Item *create_func_ifnull(Item* a, Item *b)
+#ifdef HAVE_SPATIAL
+Create_func_contains Create_func_contains::s_singleton;
+
+Item*
+Create_func_contains::create(THD *thd, Item *arg1, Item *arg2)
{
- return new Item_func_ifnull(a,b);
+ return new (thd->mem_root) Item_func_spatial_rel(arg1, arg2,
+ Item_func::SP_CONTAINS_FUNC);
}
+#endif
+
-Item *create_func_nullif(Item* a, Item *b)
+Create_func_conv Create_func_conv::s_singleton;
+
+Item*
+Create_func_conv::create(THD *thd, Item *arg1, Item *arg2, Item *arg3)
{
- return new Item_func_nullif(a,b);
+ return new (thd->mem_root) Item_func_conv(arg1, arg2, arg3);
}
-Item *create_func_locate(Item* a, Item *b)
+
+Create_func_convert_tz Create_func_convert_tz::s_singleton;
+
+Item*
+Create_func_convert_tz::create(THD *thd, Item *arg1, Item *arg2, Item *arg3)
{
- return new Item_func_locate(b,a);
+ return new (thd->mem_root) Item_func_convert_tz(arg1, arg2, arg3);
}
-Item *create_func_instr(Item* a, Item *b)
+
+Create_func_cos Create_func_cos::s_singleton;
+
+Item*
+Create_func_cos::create(THD *thd, Item *arg1)
{
- return new Item_func_locate(a,b);
+ return new (thd->mem_root) Item_func_cos(arg1);
}
-Item *create_func_isnull(Item* a)
+
+Create_func_cot Create_func_cot::s_singleton;
+
+Item*
+Create_func_cot::create(THD *thd, Item *arg1)
{
- return new Item_func_isnull(a);
+ 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);
}
-Item *create_func_lcase(Item* a)
+
+Create_func_crc32 Create_func_crc32::s_singleton;
+
+Item*
+Create_func_crc32::create(THD *thd, Item *arg1)
{
- return new Item_func_lcase(a);
+ return new (thd->mem_root) Item_func_crc32(arg1);
}
-Item *create_func_length(Item* a)
+
+#ifdef HAVE_SPATIAL
+Create_func_crosses Create_func_crosses::s_singleton;
+
+Item*
+Create_func_crosses::create(THD *thd, Item *arg1, Item *arg2)
{
- return new Item_func_length(a);
+ return new (thd->mem_root) Item_func_spatial_rel(arg1, arg2,
+ Item_func::SP_CROSSES_FUNC);
}
+#endif
-Item *create_func_bit_length(Item* a)
+
+Create_func_date_format Create_func_date_format::s_singleton;
+
+Item*
+Create_func_date_format::create(THD *thd, Item *arg1, Item *arg2)
{
- return new Item_func_bit_length(a);
+ return new (thd->mem_root) Item_func_date_format(arg1, arg2, 0);
}
-Item *create_func_coercibility(Item* a)
+
+Create_func_datediff Create_func_datediff::s_singleton;
+
+Item*
+Create_func_datediff::create(THD *thd, Item *arg1, Item *arg2)
{
- return new Item_func_coercibility(a);
+ Item *i1= new (thd->mem_root) Item_func_to_days(arg1);
+ Item *i2= new (thd->mem_root) Item_func_to_days(arg2);
+
+ return new (thd->mem_root) Item_func_minus(i1, i2);
}
-Item *create_func_char_length(Item* a)
+
+Create_func_dayname Create_func_dayname::s_singleton;
+
+Item*
+Create_func_dayname::create(THD *thd, Item *arg1)
{
- return new Item_func_char_length(a);
+ return new (thd->mem_root) Item_func_dayname(arg1);
}
-Item *create_func_ln(Item* a)
+
+Create_func_dayofmonth Create_func_dayofmonth::s_singleton;
+
+Item*
+Create_func_dayofmonth::create(THD *thd, Item *arg1)
{
- return new Item_func_ln(a);
+ return new (thd->mem_root) Item_func_dayofmonth(arg1);
}
-Item *create_func_log2(Item* a)
+
+Create_func_dayofweek Create_func_dayofweek::s_singleton;
+
+Item*
+Create_func_dayofweek::create(THD *thd, Item *arg1)
{
- return new Item_func_log2(a);
+ return new (thd->mem_root) Item_func_weekday(arg1, 1);
}
-Item *create_func_log10(Item* a)
+
+Create_func_dayofyear Create_func_dayofyear::s_singleton;
+
+Item*
+Create_func_dayofyear::create(THD *thd, Item *arg1)
{
- return new Item_func_log10(a);
+ return new (thd->mem_root) Item_func_dayofyear(arg1);
}
-Item *create_func_lpad(Item* a, Item *b, Item *c)
+
+Create_func_decode Create_func_decode::s_singleton;
+
+Item*
+Create_func_decode::create(THD *thd, Item *arg1, Item *arg2)
{
- return new Item_func_lpad(a,b,c);
+ return new (thd->mem_root) Item_func_decode(arg1, arg2);
}
-Item *create_func_ltrim(Item* a)
+
+Create_func_degrees Create_func_degrees::s_singleton;
+
+Item*
+Create_func_degrees::create(THD *thd, Item *arg1)
{
- return new Item_func_ltrim(a);
+ return new (thd->mem_root) Item_func_units((char*) "degrees", arg1,
+ 180/M_PI, 0.0);
}
-Item *create_func_md5(Item* a)
+
+Create_func_des_decrypt Create_func_des_decrypt::s_singleton;
+
+Item*
+Create_func_des_decrypt::create_native(THD *thd, LEX_STRING name,
+ List<Item> *item_list)
{
- return new Item_func_md5(a);
+ Item *func= NULL;
+ int arg_count= 0;
+
+ if (item_list != NULL)
+ arg_count= item_list->elements;
+
+ switch (arg_count) {
+ case 1:
+ {
+ Item *param_1= item_list->pop();
+ func= new (thd->mem_root) Item_func_des_decrypt(param_1);
+ break;
+ }
+ case 2:
+ {
+ Item *param_1= item_list->pop();
+ Item *param_2= item_list->pop();
+ func= new (thd->mem_root) Item_func_des_decrypt(param_1, param_2);
+ break;
+ }
+ default:
+ {
+ my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str);
+ break;
+ }
+ }
+
+ return func;
}
-Item *create_func_mod(Item* a, Item *b)
+
+Create_func_des_encrypt Create_func_des_encrypt::s_singleton;
+
+Item*
+Create_func_des_encrypt::create_native(THD *thd, LEX_STRING name,
+ List<Item> *item_list)
{
- return new Item_func_mod(a,b);
+ Item *func= NULL;
+ int arg_count= 0;
+
+ if (item_list != NULL)
+ arg_count= item_list->elements;
+
+ switch (arg_count) {
+ case 1:
+ {
+ Item *param_1= item_list->pop();
+ func= new (thd->mem_root) Item_func_des_encrypt(param_1);
+ break;
+ }
+ case 2:
+ {
+ Item *param_1= item_list->pop();
+ Item *param_2= item_list->pop();
+ func= new (thd->mem_root) Item_func_des_encrypt(param_1, param_2);
+ break;
+ }
+ default:
+ {
+ my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str);
+ break;
+ }
+ }
+
+ return func;
}
-Item *create_func_name_const(Item *a, Item *b)
+
+#ifdef HAVE_SPATIAL
+Create_func_dimension Create_func_dimension::s_singleton;
+
+Item*
+Create_func_dimension::create(THD *thd, Item *arg1)
{
- return new Item_name_const(a,b);
+ return new (thd->mem_root) Item_func_dimension(arg1);
}
+#endif
+
-Item *create_func_monthname(Item* a)
+#ifdef HAVE_SPATIAL
+Create_func_disjoint Create_func_disjoint::s_singleton;
+
+Item*
+Create_func_disjoint::create(THD *thd, Item *arg1, Item *arg2)
{
- return new Item_func_monthname(a);
+ return new (thd->mem_root) Item_func_spatial_rel(arg1, arg2,
+ Item_func::SP_DISJOINT_FUNC);
}
+#endif
+
+
+Create_func_elt Create_func_elt::s_singleton;
-Item *create_func_month(Item* a)
+Item*
+Create_func_elt::create_native(THD *thd, LEX_STRING name,
+ List<Item> *item_list)
{
- return new Item_func_month(a);
+ int arg_count= 0;
+
+ if (item_list != NULL)
+ arg_count= item_list->elements;
+
+ if (arg_count < 2)
+ {
+ my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str);
+ return NULL;
+ }
+
+ return new (thd->mem_root) Item_func_elt(*item_list);
}
-Item *create_func_oct(Item *a)
+
+Create_func_encode Create_func_encode::s_singleton;
+
+Item*
+Create_func_encode::create(THD *thd, Item *arg1, Item *arg2)
{
- return new Item_func_conv(a,new Item_int((int32) 10,2),
- new Item_int((int32) 8,1));
+ return new (thd->mem_root) Item_func_encode(arg1, arg2);
}
-Item *create_func_period_add(Item* a, Item *b)
+
+Create_func_encrypt Create_func_encrypt::s_singleton;
+
+Item*
+Create_func_encrypt::create_native(THD *thd, LEX_STRING name,
+ List<Item> *item_list)
{
- return new Item_func_period_add(a,b);
+ Item *func= NULL;
+ int arg_count= 0;
+
+ if (item_list != NULL)
+ arg_count= item_list->elements;
+
+ switch (arg_count) {
+ case 1:
+ {
+ Item *param_1= item_list->pop();
+ func= new (thd->mem_root) Item_func_encrypt(param_1);
+ thd->lex->uncacheable(UNCACHEABLE_RAND);
+ break;
+ }
+ case 2:
+ {
+ Item *param_1= item_list->pop();
+ Item *param_2= item_list->pop();
+ func= new (thd->mem_root) Item_func_encrypt(param_1, param_2);
+ break;
+ }
+ default:
+ {
+ my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str);
+ break;
+ }
+ }
+
+ return func;
}
-Item *create_func_period_diff(Item* a, Item *b)
+
+#ifdef HAVE_SPATIAL
+Create_func_endpoint Create_func_endpoint::s_singleton;
+
+Item*
+Create_func_endpoint::create(THD *thd, Item *arg1)
{
- return new Item_func_period_diff(a,b);
+ return new (thd->mem_root) Item_func_spatial_decomp(arg1,
+ Item_func::SP_ENDPOINT);
}
+#endif
-Item *create_func_pi(void)
+
+#ifdef HAVE_SPATIAL
+Create_func_envelope Create_func_envelope::s_singleton;
+
+Item*
+Create_func_envelope::create(THD *thd, Item *arg1)
{
- return new Item_static_float_func("pi()", M_PI, 6, 8);
+ return new (thd->mem_root) Item_func_envelope(arg1);
}
+#endif
+
-Item *create_func_pow(Item* a, Item *b)
+#ifdef HAVE_SPATIAL
+Create_func_equals Create_func_equals::s_singleton;
+
+Item*
+Create_func_equals::create(THD *thd, Item *arg1, Item *arg2)
{
- return new Item_func_pow(a,b);
+ return new (thd->mem_root) Item_func_spatial_rel(arg1, arg2,
+ Item_func::SP_EQUALS_FUNC);
}
+#endif
+
-Item *create_func_radians(Item *a)
+Create_func_exp Create_func_exp::s_singleton;
+
+Item*
+Create_func_exp::create(THD *thd, Item *arg1)
{
- return new Item_func_units((char*) "radians",a,M_PI/180,0.0);
+ return new (thd->mem_root) Item_func_exp(arg1);
}
-Item *create_func_release_lock(Item* a)
+
+Create_func_export_set Create_func_export_set::s_singleton;
+
+Item*
+Create_func_export_set::create_native(THD *thd, LEX_STRING name,
+ List<Item> *item_list)
{
- current_thd->lex->uncacheable(UNCACHEABLE_SIDEEFFECT);
- return new Item_func_release_lock(a);
+ Item *func= NULL;
+ int arg_count= 0;
+
+ if (item_list != NULL)
+ arg_count= item_list->elements;
+
+ switch (arg_count) {
+ 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_export_set(param_1, param_2, param_3);
+ break;
+ }
+ case 4:
+ {
+ Item *param_1= item_list->pop();
+ Item *param_2= item_list->pop();
+ Item *param_3= item_list->pop();
+ Item *param_4= item_list->pop();
+ func= new (thd->mem_root) Item_func_export_set(param_1, param_2, param_3,
+ param_4);
+ break;
+ }
+ case 5:
+ {
+ Item *param_1= item_list->pop();
+ Item *param_2= item_list->pop();
+ Item *param_3= item_list->pop();
+ Item *param_4= item_list->pop();
+ Item *param_5= item_list->pop();
+ func= new (thd->mem_root) Item_func_export_set(param_1, param_2, param_3,
+ param_4, param_5);
+ break;
+ }
+ default:
+ {
+ my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str);
+ break;
+ }
+ }
+
+ return func;
}
-Item *create_func_repeat(Item* a, Item *b)
+
+#ifdef HAVE_SPATIAL
+Create_func_exteriorring Create_func_exteriorring::s_singleton;
+
+Item*
+Create_func_exteriorring::create(THD *thd, Item *arg1)
{
- return new Item_func_repeat(a,b);
+ return new (thd->mem_root) Item_func_spatial_decomp(arg1,
+ Item_func::SP_EXTERIORRING);
}
+#endif
+
-Item *create_func_reverse(Item* a)
+Create_func_field Create_func_field::s_singleton;
+
+Item*
+Create_func_field::create_native(THD *thd, LEX_STRING name,
+ List<Item> *item_list)
{
- return new Item_func_reverse(a);
+ int arg_count= 0;
+
+ if (item_list != NULL)
+ arg_count= item_list->elements;
+
+ if (arg_count < 2)
+ {
+ my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str);
+ return NULL;
+ }
+
+ return new (thd->mem_root) Item_func_field(*item_list);
}
-Item *create_func_rpad(Item* a, Item *b, Item *c)
+
+Create_func_find_in_set Create_func_find_in_set::s_singleton;
+
+Item*
+Create_func_find_in_set::create(THD *thd, Item *arg1, Item *arg2)
{
- return new Item_func_rpad(a,b,c);
+ return new (thd->mem_root) Item_func_find_in_set(arg1, arg2);
}
-Item *create_func_rtrim(Item* a)
+
+Create_func_floor Create_func_floor::s_singleton;
+
+Item*
+Create_func_floor::create(THD *thd, Item *arg1)
{
- return new Item_func_rtrim(a);
+ return new (thd->mem_root) Item_func_floor(arg1);
}
-Item *create_func_sec_to_time(Item* a)
+
+Create_func_format Create_func_format::s_singleton;
+
+Item*
+Create_func_format::create(THD *thd, Item *arg1, Item *arg2)
{
- return new Item_func_sec_to_time(a);
+ return new (thd->mem_root) Item_func_format(arg1, arg2);
}
-Item *create_func_sign(Item* a)
+
+Create_func_found_rows Create_func_found_rows::s_singleton;
+
+Item*
+Create_func_found_rows::create(THD *thd)
{
- return new Item_func_sign(a);
+ thd->lex->set_stmt_unsafe();
+ thd->lex->safe_to_cache_query= 0;
+ return new (thd->mem_root) Item_func_found_rows();
}
-Item *create_func_sin(Item* a)
+
+Create_func_from_days Create_func_from_days::s_singleton;
+
+Item*
+Create_func_from_days::create(THD *thd, Item *arg1)
{
- return new Item_func_sin(a);
+ return new (thd->mem_root) Item_func_from_days(arg1);
}
-Item *create_func_sha(Item* a)
+
+Create_func_from_unixtime Create_func_from_unixtime::s_singleton;
+
+Item*
+Create_func_from_unixtime::create_native(THD *thd, LEX_STRING name,
+ List<Item> *item_list)
{
- return new Item_func_sha(a);
+ Item *func= NULL;
+ int arg_count= 0;
+
+ if (item_list != NULL)
+ arg_count= item_list->elements;
+
+ switch (arg_count) {
+ case 1:
+ {
+ Item *param_1= item_list->pop();
+ func= new (thd->mem_root) Item_func_from_unixtime(param_1);
+ break;
+ }
+ case 2:
+ {
+ Item *param_1= item_list->pop();
+ Item *param_2= item_list->pop();
+ Item *ut= new (thd->mem_root) Item_func_from_unixtime(param_1);
+ func= new (thd->mem_root) Item_func_date_format(ut, param_2, 0);
+ break;
+ }
+ default:
+ {
+ my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str);
+ break;
+ }
+ }
+
+ return func;
}
-Item *create_func_sleep(Item* a)
+
+#ifdef HAVE_SPATIAL
+Create_func_geometry_from_text Create_func_geometry_from_text::s_singleton;
+
+Item*
+Create_func_geometry_from_text::create_native(THD *thd, LEX_STRING name,
+ List<Item> *item_list)
{
- current_thd->lex->uncacheable(UNCACHEABLE_SIDEEFFECT);
- return new Item_func_sleep(a);
+ Item *func= NULL;
+ int arg_count= 0;
+
+ if (item_list != NULL)
+ arg_count= item_list->elements;
+
+ switch (arg_count) {
+ case 1:
+ {
+ Item *param_1= item_list->pop();
+ func= new (thd->mem_root) Item_func_geometry_from_text(param_1);
+ thd->lex->uncacheable(UNCACHEABLE_RAND);
+ break;
+ }
+ case 2:
+ {
+ Item *param_1= item_list->pop();
+ Item *param_2= item_list->pop();
+ func= new (thd->mem_root) Item_func_geometry_from_text(param_1, param_2);
+ break;
+ }
+ default:
+ {
+ my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str);
+ break;
+ }
+ }
+
+ return func;
}
+#endif
+
-Item *create_func_space(Item *a)
+#ifdef HAVE_SPATIAL
+Create_func_geometry_from_wkb Create_func_geometry_from_wkb::s_singleton;
+
+Item*
+Create_func_geometry_from_wkb::create_native(THD *thd, LEX_STRING name,
+ List<Item> *item_list)
{
- CHARSET_INFO *cs= current_thd->variables.collation_connection;
- Item *sp;
+ Item *func= NULL;
+ int arg_count= 0;
- if (cs->mbminlen > 1)
+ if (item_list != NULL)
+ arg_count= item_list->elements;
+
+ switch (arg_count) {
+ case 1:
{
- uint dummy_errors;
- sp= new Item_string("", 0, cs, DERIVATION_COERCIBLE, MY_REPERTOIRE_ASCII);
- if (sp)
- sp->str_value.copy(" ", 1, &my_charset_latin1, cs, &dummy_errors);
+ Item *param_1= item_list->pop();
+ func= new (thd->mem_root) Item_func_geometry_from_wkb(param_1);
+ thd->lex->uncacheable(UNCACHEABLE_RAND);
+ break;
}
- else
+ case 2:
{
- sp= new Item_string(" ", 1, cs, DERIVATION_COERCIBLE, MY_REPERTOIRE_ASCII);
+ Item *param_1= item_list->pop();
+ Item *param_2= item_list->pop();
+ func= new (thd->mem_root) Item_func_geometry_from_wkb(param_1, param_2);
+ break;
}
- return sp ? new Item_func_repeat(sp, a) : 0;
+ default:
+ {
+ my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str);
+ break;
+ }
+ }
+
+ return func;
}
+#endif
-Item *create_func_soundex(Item* a)
+
+#ifdef HAVE_SPATIAL
+Create_func_geometry_type Create_func_geometry_type::s_singleton;
+
+Item*
+Create_func_geometry_type::create(THD *thd, Item *arg1)
{
- return new Item_func_soundex(a);
+ return new (thd->mem_root) Item_func_geometry_type(arg1);
}
+#endif
+
-Item *create_func_sqrt(Item* a)
+#ifdef HAVE_SPATIAL
+Create_func_geometryn Create_func_geometryn::s_singleton;
+
+Item*
+Create_func_geometryn::create(THD *thd, Item *arg1, Item *arg2)
{
- return new Item_func_sqrt(a);
+ return new (thd->mem_root) Item_func_spatial_decomp_n(arg1, arg2,
+ Item_func::SP_GEOMETRYN);
}
+#endif
+
+
+Create_func_get_lock Create_func_get_lock::s_singleton;
-Item *create_func_strcmp(Item* a, Item *b)
+Item*
+Create_func_get_lock::create(THD *thd, Item *arg1, Item *arg2)
{
- return new Item_func_strcmp(a,b);
+ thd->lex->uncacheable(UNCACHEABLE_SIDEEFFECT);
+ return new (thd->mem_root) Item_func_get_lock(arg1, arg2);
}
-Item *create_func_tan(Item* a)
+
+#ifdef HAVE_SPATIAL
+Create_func_glength Create_func_glength::s_singleton;
+
+Item*
+Create_func_glength::create(THD *thd, Item *arg1)
{
- return new Item_func_tan(a);
+ return new (thd->mem_root) Item_func_glength(arg1);
}
+#endif
+
-Item *create_func_time_format(Item *a, Item *b)
+Create_func_greatest Create_func_greatest::s_singleton;
+
+Item*
+Create_func_greatest::create_native(THD *thd, LEX_STRING name,
+ List<Item> *item_list)
{
- return new Item_func_date_format(a,b,1);
+ int arg_count= 0;
+
+ if (item_list != NULL)
+ arg_count= item_list->elements;
+
+ if (arg_count < 2)
+ {
+ my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str);
+ return NULL;
+ }
+
+ return new (thd->mem_root) Item_func_max(*item_list);
}
-Item *create_func_time_to_sec(Item* a)
+
+Create_func_hex Create_func_hex::s_singleton;
+
+Item*
+Create_func_hex::create(THD *thd, Item *arg1)
{
- return new Item_func_time_to_sec(a);
+ return new (thd->mem_root) Item_func_hex(arg1);
}
-Item *create_func_to_days(Item* a)
+
+Create_func_ifnull Create_func_ifnull::s_singleton;
+
+Item*
+Create_func_ifnull::create(THD *thd, Item *arg1, Item *arg2)
{
- return new Item_func_to_days(a);
+ return new (thd->mem_root) Item_func_ifnull(arg1, arg2);
}
-Item *create_func_ucase(Item* a)
+
+Create_func_inet_ntoa Create_func_inet_ntoa::s_singleton;
+
+Item*
+Create_func_inet_ntoa::create(THD *thd, Item *arg1)
{
- return new Item_func_ucase(a);
+ return new (thd->mem_root) Item_func_inet_ntoa(arg1);
}
-Item *create_func_unhex(Item* a)
+
+Create_func_inet_aton Create_func_inet_aton::s_singleton;
+
+Item*
+Create_func_inet_aton::create(THD *thd, Item *arg1)
{
- return new Item_func_unhex(a);
+ return new (thd->mem_root) Item_func_inet_aton(arg1);
}
-Item *create_func_uuid(void)
+
+Create_func_instr Create_func_instr::s_singleton;
+
+Item*
+Create_func_instr::create(THD *thd, Item *arg1, Item *arg2)
{
- current_thd->lex->safe_to_cache_query= 0;
- return new Item_func_uuid();
+ return new (thd->mem_root) Item_func_locate(arg1, arg2);
}
-Item *create_func_version(void)
+
+#ifdef HAVE_SPATIAL
+Create_func_interiorringn Create_func_interiorringn::s_singleton;
+
+Item*
+Create_func_interiorringn::create(THD *thd, Item *arg1, Item *arg2)
{
- return new Item_static_string_func("version()", server_version,
- (uint) strlen(server_version),
- system_charset_info, DERIVATION_SYSCONST);
+ return new (thd->mem_root) Item_func_spatial_decomp_n(arg1, arg2,
+ Item_func::SP_INTERIORRINGN);
}
+#endif
-Item *create_func_weekday(Item* a)
+
+#ifdef HAVE_SPATIAL
+Create_func_intersects Create_func_intersects::s_singleton;
+
+Item*
+Create_func_intersects::create(THD *thd, Item *arg1, Item *arg2)
{
- return new Item_func_weekday(a, 0);
+ return new (thd->mem_root) Item_func_spatial_rel(arg1, arg2,
+ Item_func::SP_INTERSECTS_FUNC);
}
+#endif
+
+
+Create_func_is_free_lock Create_func_is_free_lock::s_singleton;
-Item *create_func_year(Item* a)
+Item*
+Create_func_is_free_lock::create(THD *thd, Item *arg1)
{
- return new Item_func_year(a);
+ thd->lex->uncacheable(UNCACHEABLE_SIDEEFFECT);
+ return new (thd->mem_root) Item_func_is_free_lock(arg1);
}
-Item *create_load_file(Item* a)
+
+Create_func_is_used_lock Create_func_is_used_lock::s_singleton;
+
+Item*
+Create_func_is_used_lock::create(THD *thd, Item *arg1)
{
- current_thd->lex->uncacheable(UNCACHEABLE_SIDEEFFECT);
- return new Item_load_file(a);
+ thd->lex->uncacheable(UNCACHEABLE_SIDEEFFECT);
+ return new (thd->mem_root) Item_func_is_used_lock(arg1);
}
-Item *create_func_cast(Item *a, Cast_target cast_type,
- const char *c_len, const char *c_dec,
- CHARSET_INFO *cs)
+#ifdef HAVE_SPATIAL
+Create_func_isclosed Create_func_isclosed::s_singleton;
+
+Item*
+Create_func_isclosed::create(THD *thd, Item *arg1)
{
- Item *res;
- ulong len;
- uint dec;
- LINT_INIT(res);
+ return new (thd->mem_root) Item_func_isclosed(arg1);
+}
+#endif
- switch (cast_type) {
- case ITEM_CAST_BINARY: res= new Item_func_binary(a); break;
- case ITEM_CAST_SIGNED_INT: res= new Item_func_signed(a); break;
- case ITEM_CAST_UNSIGNED_INT: res= new Item_func_unsigned(a); break;
- case ITEM_CAST_DATE: res= new Item_date_typecast(a); break;
- case ITEM_CAST_TIME: res= new Item_time_typecast(a); break;
- case ITEM_CAST_DATETIME: res= new Item_datetime_typecast(a); break;
- case ITEM_CAST_DECIMAL:
- if (c_len == NULL)
- {
- len= 0;
- }
- else
- {
- ulong decoded_size;
- errno= 0;
- decoded_size= strtoul(c_len, NULL, 10);
- if (errno != 0)
- {
- my_error(ER_TOO_BIG_PRECISION, MYF(0), c_len, a->name,
- DECIMAL_MAX_PRECISION);
- return NULL;
- }
- len= decoded_size;
- }
- if (c_dec == NULL)
- {
- dec= 0;
- }
- else
- {
- ulong decoded_size;
- errno= 0;
- decoded_size= strtoul(c_dec, NULL, 10);
- if ((errno != 0) || (decoded_size > UINT_MAX))
- {
- my_error(ER_TOO_BIG_SCALE, MYF(0), c_dec, a->name,
- DECIMAL_MAX_SCALE);
- return NULL;
- }
- dec= decoded_size;
- }
+#ifdef HAVE_SPATIAL
+Create_func_isempty Create_func_isempty::s_singleton;
- my_decimal_trim(&len, &dec);
- if (len < dec)
- {
- my_error(ER_M_BIGGER_THAN_D, MYF(0), "");
- return 0;
- }
- if (len > DECIMAL_MAX_PRECISION)
- {
- my_error(ER_TOO_BIG_PRECISION, MYF(0), len, a->name,
- DECIMAL_MAX_PRECISION);
- return 0;
- }
- if (dec > DECIMAL_MAX_SCALE)
- {
- my_error(ER_TOO_BIG_SCALE, MYF(0), dec, a->name,
- DECIMAL_MAX_SCALE);
- return 0;
- }
- res= new Item_decimal_typecast(a, len, dec);
+Item*
+Create_func_isempty::create(THD *thd, Item *arg1)
+{
+ return new (thd->mem_root) Item_func_isempty(arg1);
+}
+#endif
+
+
+Create_func_isnull Create_func_isnull::s_singleton;
+
+Item*
+Create_func_isnull::create(THD *thd, Item *arg1)
+{
+ return new (thd->mem_root) Item_func_isnull(arg1);
+}
+
+
+#ifdef HAVE_SPATIAL
+Create_func_issimple Create_func_issimple::s_singleton;
+
+Item*
+Create_func_issimple::create(THD *thd, Item *arg1)
+{
+ return new (thd->mem_root) Item_func_issimple(arg1);
+}
+#endif
+
+
+Create_func_last_day Create_func_last_day::s_singleton;
+
+Item*
+Create_func_last_day::create(THD *thd, Item *arg1)
+{
+ return new (thd->mem_root) Item_func_last_day(arg1);
+}
+
+
+Create_func_last_insert_id Create_func_last_insert_id::s_singleton;
+
+Item*
+Create_func_last_insert_id::create_native(THD *thd, LEX_STRING name,
+ List<Item> *item_list)
+{
+ Item *func= NULL;
+ int arg_count= 0;
+
+ if (item_list != NULL)
+ arg_count= item_list->elements;
+
+ switch (arg_count) {
+ case 0:
+ {
+ func= new (thd->mem_root) Item_func_last_insert_id();
+ thd->lex->safe_to_cache_query= 0;
break;
+ }
+ case 1:
+ {
+ Item *param_1= item_list->pop();
+ func= new (thd->mem_root) Item_func_last_insert_id(param_1);
+ thd->lex->safe_to_cache_query= 0;
+ break;
+ }
+ default:
+ {
+ my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str);
+ break;
+ }
+ }
- case ITEM_CAST_CHAR:
- if (c_len == NULL)
- {
- len= LL(-1);
- }
- else
- {
- ulong decoded_size;
- errno= 0;
- decoded_size= strtoul(c_len, NULL, 10);
- if ((errno != 0) || (decoded_size > MAX_FIELD_BLOBLENGTH))
- {
- my_error(ER_TOO_BIG_DISPLAYWIDTH, MYF(0), "cast as char", MAX_FIELD_BLOBLENGTH);
- return NULL;
- }
- len= decoded_size;
- }
+ return func;
+}
+
+
+Create_func_lcase Create_func_lcase::s_singleton;
+
+Item*
+Create_func_lcase::create(THD *thd, Item *arg1)
+{
+ return new (thd->mem_root) Item_func_lcase(arg1);
+}
+
+
+Create_func_least Create_func_least::s_singleton;
+
+Item*
+Create_func_least::create_native(THD *thd, LEX_STRING name,
+ List<Item> *item_list)
+{
+ int arg_count= 0;
+
+ if (item_list != NULL)
+ arg_count= item_list->elements;
+
+ if (arg_count < 2)
+ {
+ my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str);
+ return NULL;
+ }
+
+ return new (thd->mem_root) Item_func_min(*item_list);
+}
+
+
+Create_func_length Create_func_length::s_singleton;
+
+Item*
+Create_func_length::create(THD *thd, Item *arg1)
+{
+ return new (thd->mem_root) Item_func_length(arg1);
+}
- res= new Item_char_typecast(a, len, cs ? cs :
- current_thd->variables.collation_connection);
+
+Create_func_ln Create_func_ln::s_singleton;
+
+Item*
+Create_func_ln::create(THD *thd, Item *arg1)
+{
+ return new (thd->mem_root) Item_func_ln(arg1);
+}
+
+
+Create_func_load_file Create_func_load_file::s_singleton;
+
+Item*
+Create_func_load_file::create(THD *thd, Item *arg1)
+{
+ thd->lex->uncacheable(UNCACHEABLE_SIDEEFFECT);
+ return new (thd->mem_root) Item_load_file(arg1);
+}
+
+
+Create_func_locate Create_func_locate::s_singleton;
+
+Item*
+Create_func_locate::create_native(THD *thd, LEX_STRING name,
+ List<Item> *item_list)
+{
+ Item *func= NULL;
+ int arg_count= 0;
+
+ if (item_list != NULL)
+ arg_count= item_list->elements;
+
+ switch (arg_count) {
+ case 2:
+ {
+ Item *param_1= item_list->pop();
+ Item *param_2= item_list->pop();
+ /* Yes, parameters in that order : 2, 1 */
+ func= new (thd->mem_root) Item_func_locate(param_2, param_1);
break;
}
- return res;
+ case 3:
+ {
+ Item *param_1= item_list->pop();
+ Item *param_2= item_list->pop();
+ Item *param_3= item_list->pop();
+ /* Yes, parameters in that order : 2, 1, 3 */
+ func= new (thd->mem_root) Item_func_locate(param_2, param_1, param_3);
+ break;
+ }
+ default:
+ {
+ my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str);
+ break;
+ }
+ }
+
+ return func;
+}
+
+
+Create_func_log Create_func_log::s_singleton;
+
+Item*
+Create_func_log::create_native(THD *thd, LEX_STRING name,
+ List<Item> *item_list)
+{
+ Item *func= NULL;
+ int arg_count= 0;
+
+ if (item_list != NULL)
+ arg_count= item_list->elements;
+
+ switch (arg_count) {
+ case 1:
+ {
+ Item *param_1= item_list->pop();
+ func= new (thd->mem_root) Item_func_log(param_1);
+ break;
+ }
+ case 2:
+ {
+ Item *param_1= item_list->pop();
+ Item *param_2= item_list->pop();
+ func= new (thd->mem_root) Item_func_log(param_1, param_2);
+ break;
+ }
+ default:
+ {
+ my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str);
+ break;
+ }
+ }
+
+ return func;
}
-Item *create_func_is_free_lock(Item* a)
+Create_func_log10 Create_func_log10::s_singleton;
+
+Item*
+Create_func_log10::create(THD *thd, Item *arg1)
{
- current_thd->lex->uncacheable(UNCACHEABLE_SIDEEFFECT);
- return new Item_func_is_free_lock(a);
+ return new (thd->mem_root) Item_func_log10(arg1);
}
-Item *create_func_is_used_lock(Item* a)
+
+Create_func_log2 Create_func_log2::s_singleton;
+
+Item*
+Create_func_log2::create(THD *thd, Item *arg1)
+{
+ return new (thd->mem_root) Item_func_log2(arg1);
+}
+
+
+Create_func_lpad Create_func_lpad::s_singleton;
+
+Item*
+Create_func_lpad::create(THD *thd, Item *arg1, Item *arg2, Item *arg3)
{
- current_thd->lex->uncacheable(UNCACHEABLE_SIDEEFFECT);
- return new Item_func_is_used_lock(a);
+ return new (thd->mem_root) Item_func_lpad(arg1, arg2, arg3);
}
-Item *create_func_quote(Item* a)
+
+Create_func_ltrim Create_func_ltrim::s_singleton;
+
+Item*
+Create_func_ltrim::create(THD *thd, Item *arg1)
+{
+ return new (thd->mem_root) Item_func_ltrim(arg1);
+}
+
+
+Create_func_makedate Create_func_makedate::s_singleton;
+
+Item*
+Create_func_makedate::create(THD *thd, Item *arg1, Item *arg2)
{
- return new Item_func_quote(a);
+ return new (thd->mem_root) Item_func_makedate(arg1, arg2);
}
+
+Create_func_maketime Create_func_maketime::s_singleton;
+
+Item*
+Create_func_maketime::create(THD *thd, Item *arg1, Item *arg2, Item *arg3)
+{
+ return new (thd->mem_root) Item_func_maketime(arg1, arg2, arg3);
+}
+
+
+Create_func_make_set Create_func_make_set::s_singleton;
+
+Item*
+Create_func_make_set::create_native(THD *thd, LEX_STRING name,
+ List<Item> *item_list)
+{
+ int arg_count= 0;
+
+ if (item_list != NULL)
+ arg_count= item_list->elements;
+
+ if (arg_count < 2)
+ {
+ my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str);
+ return NULL;
+ }
+
+ Item *param_1= item_list->pop();
+ return new (thd->mem_root) Item_func_make_set(param_1, *item_list);
+}
+
+
+Create_func_master_pos_wait Create_func_master_pos_wait::s_singleton;
+
+Item*
+Create_func_master_pos_wait::create_native(THD *thd, LEX_STRING name,
+ List<Item> *item_list)
+
+{
+ Item *func= NULL;
+ int arg_count= 0;
+
+ if (item_list != NULL)
+ arg_count= item_list->elements;
+
+ switch (arg_count) {
+ case 2:
+ {
+ Item *param_1= item_list->pop();
+ Item *param_2= item_list->pop();
+ func= new (thd->mem_root) Item_master_pos_wait(param_1, param_2);
+ thd->lex->safe_to_cache_query= 0;
+ break;
+ }
+ case 3:
+ {
+ Item *param_1= item_list->pop();
+ Item *param_2= item_list->pop();
+ Item *param_3= item_list->pop();
+ func= new (thd->mem_root) Item_master_pos_wait(param_1, param_2, param_3);
+ thd->lex->safe_to_cache_query= 0;
+ break;
+ }
+ default:
+ {
+ my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str);
+ break;
+ }
+ }
+
+ return func;
+}
+
+
+Create_func_md5 Create_func_md5::s_singleton;
+
+Item*
+Create_func_md5::create(THD *thd, Item *arg1)
+{
+ return new (thd->mem_root) Item_func_md5(arg1);
+}
+
+
+Create_func_monthname Create_func_monthname::s_singleton;
+
+Item*
+Create_func_monthname::create(THD *thd, Item *arg1)
+{
+ return new (thd->mem_root) Item_func_monthname(arg1);
+}
+
+
+Create_func_name_const Create_func_name_const::s_singleton;
+
+Item*
+Create_func_name_const::create(THD *thd, Item *arg1, Item *arg2)
+{
+ return new (thd->mem_root) Item_name_const(arg1, arg2);
+}
+
+
+Create_func_nullif Create_func_nullif::s_singleton;
+
+Item*
+Create_func_nullif::create(THD *thd, Item *arg1, Item *arg2)
+{
+ return new (thd->mem_root) Item_func_nullif(arg1, arg2);
+}
+
+
#ifdef HAVE_SPATIAL
-Item *create_func_as_wkt(Item *a)
+Create_func_numgeometries Create_func_numgeometries::s_singleton;
+
+Item*
+Create_func_numgeometries::create(THD *thd, Item *arg1)
{
- return new Item_func_as_wkt(a);
+ return new (thd->mem_root) Item_func_numgeometries(arg1);
}
+#endif
+
-Item *create_func_as_wkb(Item *a)
+#ifdef HAVE_SPATIAL
+Create_func_numinteriorring Create_func_numinteriorring::s_singleton;
+
+Item*
+Create_func_numinteriorring::create(THD *thd, Item *arg1)
+{
+ return new (thd->mem_root) Item_func_numinteriorring(arg1);
+}
+#endif
+
+
+#ifdef HAVE_SPATIAL
+Create_func_numpoints Create_func_numpoints::s_singleton;
+
+Item*
+Create_func_numpoints::create(THD *thd, Item *arg1)
+{
+ return new (thd->mem_root) Item_func_numpoints(arg1);
+}
+#endif
+
+
+Create_func_oct Create_func_oct::s_singleton;
+
+Item*
+Create_func_oct::create(THD *thd, Item *arg1)
+{
+ Item *i10= new (thd->mem_root) Item_int((int32) 10,2);
+ Item *i8= new (thd->mem_root) Item_int((int32) 8,1);
+ return new (thd->mem_root) Item_func_conv(arg1, i10, i8);
+}
+
+
+Create_func_ord Create_func_ord::s_singleton;
+
+Item*
+Create_func_ord::create(THD *thd, Item *arg1)
+{
+ return new (thd->mem_root) Item_func_ord(arg1);
+}
+
+
+#ifdef HAVE_SPATIAL
+Create_func_overlaps Create_func_overlaps::s_singleton;
+
+Item*
+Create_func_overlaps::create(THD *thd, Item *arg1, Item *arg2)
+{
+ return new (thd->mem_root) Item_func_spatial_rel(arg1, arg2,
+ Item_func::SP_OVERLAPS_FUNC);
+}
+#endif
+
+
+Create_func_period_add Create_func_period_add::s_singleton;
+
+Item*
+Create_func_period_add::create(THD *thd, Item *arg1, Item *arg2)
+{
+ return new (thd->mem_root) Item_func_period_add(arg1, arg2);
+}
+
+
+Create_func_period_diff Create_func_period_diff::s_singleton;
+
+Item*
+Create_func_period_diff::create(THD *thd, Item *arg1, Item *arg2)
+{
+ return new (thd->mem_root) Item_func_period_diff(arg1, arg2);
+}
+
+
+Create_func_pi Create_func_pi::s_singleton;
+
+Item*
+Create_func_pi::create(THD *thd)
+{
+ return new (thd->mem_root) Item_static_float_func("pi()", M_PI, 6, 8);
+}
+
+
+#ifdef HAVE_SPATIAL
+Create_func_pointn Create_func_pointn::s_singleton;
+
+Item*
+Create_func_pointn::create(THD *thd, Item *arg1, Item *arg2)
+{
+ return new (thd->mem_root) Item_func_spatial_decomp_n(arg1, arg2,
+ Item_func::SP_POINTN);
+}
+#endif
+
+
+Create_func_pow Create_func_pow::s_singleton;
+
+Item*
+Create_func_pow::create(THD *thd, Item *arg1, Item *arg2)
+{
+ return new (thd->mem_root) Item_func_pow(arg1, arg2);
+}
+
+
+Create_func_quote Create_func_quote::s_singleton;
+
+Item*
+Create_func_quote::create(THD *thd, Item *arg1)
+{
+ return new (thd->mem_root) Item_func_quote(arg1);
+}
+
+
+Create_func_radians Create_func_radians::s_singleton;
+
+Item*
+Create_func_radians::create(THD *thd, Item *arg1)
{
- return new Item_func_as_wkb(a);
+ return new (thd->mem_root) Item_func_units((char*) "radians", arg1,
+ M_PI/180, 0.0);
}
-Item *create_func_srid(Item *a)
+
+Create_func_rand Create_func_rand::s_singleton;
+
+Item*
+Create_func_rand::create_native(THD *thd, LEX_STRING name,
+ List<Item> *item_list)
+{
+ Item *func= NULL;
+ int arg_count= 0;
+
+ if (item_list != NULL)
+ arg_count= item_list->elements;
+
+ switch (arg_count) {
+ case 0:
+ {
+ func= new (thd->mem_root) Item_func_rand();
+ thd->lex->uncacheable(UNCACHEABLE_RAND);
+ break;
+ }
+ case 1:
+ {
+ Item *param_1= item_list->pop();
+ func= new (thd->mem_root) Item_func_rand(param_1);
+ thd->lex->uncacheable(UNCACHEABLE_RAND);
+ break;
+ }
+ default:
+ {
+ my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str);
+ break;
+ }
+ }
+
+ return func;
+}
+
+
+Create_func_release_lock Create_func_release_lock::s_singleton;
+
+Item*
+Create_func_release_lock::create(THD *thd, Item *arg1)
+{
+ thd->lex->uncacheable(UNCACHEABLE_SIDEEFFECT);
+ return new (thd->mem_root) Item_func_release_lock(arg1);
+}
+
+
+Create_func_reverse Create_func_reverse::s_singleton;
+
+Item*
+Create_func_reverse::create(THD *thd, Item *arg1)
+{
+ return new (thd->mem_root) Item_func_reverse(arg1);
+}
+
+
+Create_func_round Create_func_round::s_singleton;
+
+Item*
+Create_func_round::create_native(THD *thd, LEX_STRING name,
+ List<Item> *item_list)
+{
+ Item *func= NULL;
+ int arg_count= 0;
+
+ if (item_list != NULL)
+ arg_count= item_list->elements;
+
+ switch (arg_count) {
+ case 1:
+ {
+ Item *param_1= item_list->pop();
+ Item *i0 = new (thd->mem_root) Item_int((char*)"0", 0, 1);
+ func= new (thd->mem_root) Item_func_round(param_1, i0, 0);
+ break;
+ }
+ case 2:
+ {
+ Item *param_1= item_list->pop();
+ Item *param_2= item_list->pop();
+ func= new (thd->mem_root) Item_func_round(param_1, param_2, 0);
+ break;
+ }
+ default:
+ {
+ my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str);
+ break;
+ }
+ }
+
+ return func;
+}
+
+
+Create_func_row_count Create_func_row_count::s_singleton;
+
+Item*
+Create_func_row_count::create(THD *thd)
+{
+ thd->lex->set_stmt_unsafe();
+ thd->lex->safe_to_cache_query= 0;
+ return new (thd->mem_root) Item_func_row_count();
+}
+
+
+Create_func_rpad Create_func_rpad::s_singleton;
+
+Item*
+Create_func_rpad::create(THD *thd, Item *arg1, Item *arg2, Item *arg3)
{
- return new Item_func_srid(a);
+ return new (thd->mem_root) Item_func_rpad(arg1, arg2, arg3);
}
-Item *create_func_startpoint(Item *a)
+
+Create_func_rtrim Create_func_rtrim::s_singleton;
+
+Item*
+Create_func_rtrim::create(THD *thd, Item *arg1)
{
- return new Item_func_spatial_decomp(a, Item_func::SP_STARTPOINT);
+ return new (thd->mem_root) Item_func_rtrim(arg1);
}
-Item *create_func_endpoint(Item *a)
+
+Create_func_sec_to_time Create_func_sec_to_time::s_singleton;
+
+Item*
+Create_func_sec_to_time::create(THD *thd, Item *arg1)
{
- return new Item_func_spatial_decomp(a, Item_func::SP_ENDPOINT);
+ return new (thd->mem_root) Item_func_sec_to_time(arg1);
}
-Item *create_func_exteriorring(Item *a)
+
+Create_func_sha Create_func_sha::s_singleton;
+
+Item*
+Create_func_sha::create(THD *thd, Item *arg1)
{
- return new Item_func_spatial_decomp(a, Item_func::SP_EXTERIORRING);
+ return new (thd->mem_root) Item_func_sha(arg1);
}
-Item *create_func_pointn(Item *a, Item *b)
+
+Create_func_sign Create_func_sign::s_singleton;
+
+Item*
+Create_func_sign::create(THD *thd, Item *arg1)
{
- return new Item_func_spatial_decomp_n(a, b, Item_func::SP_POINTN);
+ return new (thd->mem_root) Item_func_sign(arg1);
}
-Item *create_func_interiorringn(Item *a, Item *b)
+
+Create_func_sin Create_func_sin::s_singleton;
+
+Item*
+Create_func_sin::create(THD *thd, Item *arg1)
{
- return new Item_func_spatial_decomp_n(a, b, Item_func::SP_INTERIORRINGN);
+ return new (thd->mem_root) Item_func_sin(arg1);
}
-Item *create_func_geometryn(Item *a, Item *b)
+
+Create_func_sleep Create_func_sleep::s_singleton;
+
+Item*
+Create_func_sleep::create(THD *thd, Item *arg1)
{
- return new Item_func_spatial_decomp_n(a, b, Item_func::SP_GEOMETRYN);
+ thd->lex->uncacheable(UNCACHEABLE_SIDEEFFECT);
+ return new (thd->mem_root) Item_func_sleep(arg1);
}
-Item *create_func_centroid(Item *a)
+
+Create_func_soundex Create_func_soundex::s_singleton;
+
+Item*
+Create_func_soundex::create(THD *thd, Item *arg1)
{
- return new Item_func_centroid(a);
+ return new (thd->mem_root) Item_func_soundex(arg1);
}
-Item *create_func_envelope(Item *a)
+
+Create_func_space Create_func_space::s_singleton;
+
+Item*
+Create_func_space::create(THD *thd, Item *arg1)
{
- return new Item_func_envelope(a);
+ /**
+ TODO: Fix Bug#23637
+ The parsed item tree should not depend on
+ <code>thd->variables.collation_connection</code>.
+ */
+ CHARSET_INFO *cs= thd->variables.collation_connection;
+ Item *sp;
+
+ if (cs->mbminlen > 1)
+ {
+ uint dummy_errors;
+ sp= new (thd->mem_root) Item_string("", 0, cs, DERIVATION_COERCIBLE, MY_REPERTOIRE_ASCII);
+ sp->str_value.copy(" ", 1, &my_charset_latin1, cs, &dummy_errors);
+ }
+ else
+ {
+ sp= new (thd->mem_root) Item_string(" ", 1, cs, DERIVATION_COERCIBLE, MY_REPERTOIRE_ASCII);
+ }
+
+ return new (thd->mem_root) Item_func_repeat(sp, arg1);
}
-Item *create_func_equals(Item *a, Item *b)
+
+Create_func_sqrt Create_func_sqrt::s_singleton;
+
+Item*
+Create_func_sqrt::create(THD *thd, Item *arg1)
{
- return new Item_func_spatial_rel(a, b, Item_func::SP_EQUALS_FUNC);
+ return new (thd->mem_root) Item_func_sqrt(arg1);
}
-Item *create_func_disjoint(Item *a, Item *b)
+
+#ifdef HAVE_SPATIAL
+Create_func_srid Create_func_srid::s_singleton;
+
+Item*
+Create_func_srid::create(THD *thd, Item *arg1)
{
- return new Item_func_spatial_rel(a, b, Item_func::SP_DISJOINT_FUNC);
+ return new (thd->mem_root) Item_func_srid(arg1);
}
+#endif
+
+
+#ifdef HAVE_SPATIAL
+Create_func_startpoint Create_func_startpoint::s_singleton;
-Item *create_func_intersects(Item *a, Item *b)
+Item*
+Create_func_startpoint::create(THD *thd, Item *arg1)
{
- return new Item_func_spatial_rel(a, b, Item_func::SP_INTERSECTS_FUNC);
+ return new (thd->mem_root) Item_func_spatial_decomp(arg1,
+ Item_func::SP_STARTPOINT);
}
+#endif
+
-Item *create_func_touches(Item *a, Item *b)
+Create_func_str_to_date Create_func_str_to_date::s_singleton;
+
+Item*
+Create_func_str_to_date::create(THD *thd, Item *arg1, Item *arg2)
{
- return new Item_func_spatial_rel(a, b, Item_func::SP_TOUCHES_FUNC);
+ return new (thd->mem_root) Item_func_str_to_date(arg1, arg2);
}
-Item *create_func_crosses(Item *a, Item *b)
+
+Create_func_strcmp Create_func_strcmp::s_singleton;
+
+Item*
+Create_func_strcmp::create(THD *thd, Item *arg1, Item *arg2)
{
- return new Item_func_spatial_rel(a, b, Item_func::SP_CROSSES_FUNC);
+ return new (thd->mem_root) Item_func_strcmp(arg1, arg2);
}
-Item *create_func_within(Item *a, Item *b)
+
+Create_func_substr_index Create_func_substr_index::s_singleton;
+
+Item*
+Create_func_substr_index::create(THD *thd, Item *arg1, Item *arg2, Item *arg3)
{
- return new Item_func_spatial_rel(a, b, Item_func::SP_WITHIN_FUNC);
+ return new (thd->mem_root) Item_func_substr_index(arg1, arg2, arg3);
}
-Item *create_func_contains(Item *a, Item *b)
+
+Create_func_subtime Create_func_subtime::s_singleton;
+
+Item*
+Create_func_subtime::create(THD *thd, Item *arg1, Item *arg2)
{
- return new Item_func_spatial_rel(a, b, Item_func::SP_CONTAINS_FUNC);
+ return new (thd->mem_root) Item_func_add_time(arg1, arg2, 0, 1);
}
-Item *create_func_overlaps(Item *a, Item *b)
+
+Create_func_tan Create_func_tan::s_singleton;
+
+Item*
+Create_func_tan::create(THD *thd, Item *arg1)
{
- return new Item_func_spatial_rel(a, b, Item_func::SP_OVERLAPS_FUNC);
+ return new (thd->mem_root) Item_func_tan(arg1);
}
-Item *create_func_isempty(Item *a)
+
+Create_func_time_format Create_func_time_format::s_singleton;
+
+Item*
+Create_func_time_format::create(THD *thd, Item *arg1, Item *arg2)
{
- return new Item_func_isempty(a);
+ return new (thd->mem_root) Item_func_date_format(arg1, arg2, 1);
}
-Item *create_func_issimple(Item *a)
+
+Create_func_time_to_sec Create_func_time_to_sec::s_singleton;
+
+Item*
+Create_func_time_to_sec::create(THD *thd, Item *arg1)
{
- return new Item_func_issimple(a);
+ return new (thd->mem_root) Item_func_time_to_sec(arg1);
}
-Item *create_func_isclosed(Item *a)
+
+Create_func_timediff Create_func_timediff::s_singleton;
+
+Item*
+Create_func_timediff::create(THD *thd, Item *arg1, Item *arg2)
{
- return new Item_func_isclosed(a);
+ return new (thd->mem_root) Item_func_timediff(arg1, arg2);
}
-Item *create_func_geometry_type(Item *a)
+
+Create_func_to_days Create_func_to_days::s_singleton;
+
+Item*
+Create_func_to_days::create(THD *thd, Item *arg1)
{
- return new Item_func_geometry_type(a);
+ return new (thd->mem_root) Item_func_to_days(arg1);
}
-Item *create_func_dimension(Item *a)
+
+#ifdef HAVE_SPATIAL
+Create_func_touches Create_func_touches::s_singleton;
+
+Item*
+Create_func_touches::create(THD *thd, Item *arg1, Item *arg2)
{
- return new Item_func_dimension(a);
+ return new (thd->mem_root) Item_func_spatial_rel(arg1, arg2,
+ Item_func::SP_TOUCHES_FUNC);
}
+#endif
-Item *create_func_x(Item *a)
+
+Create_func_ucase Create_func_ucase::s_singleton;
+
+Item*
+Create_func_ucase::create(THD *thd, Item *arg1)
{
- return new Item_func_x(a);
+ return new (thd->mem_root) Item_func_ucase(arg1);
}
-Item *create_func_y(Item *a)
+
+Create_func_uncompress Create_func_uncompress::s_singleton;
+
+Item*
+Create_func_uncompress::create(THD *thd, Item *arg1)
{
- return new Item_func_y(a);
+ return new (thd->mem_root) Item_func_uncompress(arg1);
}
-Item *create_func_numpoints(Item *a)
+
+Create_func_uncompressed_length Create_func_uncompressed_length::s_singleton;
+
+Item*
+Create_func_uncompressed_length::create(THD *thd, Item *arg1)
{
- return new Item_func_numpoints(a);
+ return new (thd->mem_root) Item_func_uncompressed_length(arg1);
}
-Item *create_func_numinteriorring(Item *a)
+
+Create_func_unhex Create_func_unhex::s_singleton;
+
+Item*
+Create_func_unhex::create(THD *thd, Item *arg1)
{
- return new Item_func_numinteriorring(a);
+ return new (thd->mem_root) Item_func_unhex(arg1);
}
-Item *create_func_numgeometries(Item *a)
+
+Create_func_unix_timestamp Create_func_unix_timestamp::s_singleton;
+
+Item*
+Create_func_unix_timestamp::create_native(THD *thd, LEX_STRING name,
+ List<Item> *item_list)
{
- return new Item_func_numgeometries(a);
+ Item *func= NULL;
+ int arg_count= 0;
+
+ if (item_list != NULL)
+ arg_count= item_list->elements;
+
+ switch (arg_count) {
+ case 0:
+ {
+ func= new (thd->mem_root) Item_func_unix_timestamp();
+ thd->lex->safe_to_cache_query= 0;
+ break;
+ }
+ case 1:
+ {
+ Item *param_1= item_list->pop();
+ func= new (thd->mem_root) Item_func_unix_timestamp(param_1);
+ break;
+ }
+ default:
+ {
+ my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str);
+ break;
+ }
+ }
+
+ return func;
}
-Item *create_func_area(Item *a)
+
+Create_func_uuid Create_func_uuid::s_singleton;
+
+Item*
+Create_func_uuid::create(THD *thd)
{
- return new Item_func_area(a);
+ thd->lex->set_stmt_unsafe();
+ thd->lex->safe_to_cache_query= 0;
+ return new (thd->mem_root) Item_func_uuid();
}
-Item *create_func_glength(Item *a)
+
+Create_func_uuid_short Create_func_uuid_short::s_singleton;
+
+Item*
+Create_func_uuid_short::create(THD *thd)
{
- return new Item_func_glength(a);
+ thd->lex->set_stmt_unsafe();
+ thd->lex->safe_to_cache_query= 0;
+ return new (thd->mem_root) Item_func_uuid_short();
}
-Item *create_func_point(Item *a, Item *b)
+
+Create_func_version Create_func_version::s_singleton;
+
+Item*
+Create_func_version::create(THD *thd)
{
- return new Item_func_point(a, b);
+ return new (thd->mem_root) Item_static_string_func("version()",
+ server_version,
+ (uint) strlen(server_version),
+ system_charset_info,
+ DERIVATION_SYSCONST);
}
-#endif /*HAVE_SPATIAL*/
-Item *create_func_crc32(Item* a)
+
+Create_func_weekday Create_func_weekday::s_singleton;
+
+Item*
+Create_func_weekday::create(THD *thd, Item *arg1)
{
- return new Item_func_crc32(a);
+ return new (thd->mem_root) Item_func_weekday(arg1, 0);
}
-Item *create_func_compress(Item* a)
+
+Create_func_weekofyear Create_func_weekofyear::s_singleton;
+
+Item*
+Create_func_weekofyear::create(THD *thd, Item *arg1)
{
- return new Item_func_compress(a);
+ Item *i1= new (thd->mem_root) Item_int((char*) "0", 3, 1);
+ return new (thd->mem_root) Item_func_week(arg1, i1);
}
-Item *create_func_uncompress(Item* a)
+
+#ifdef HAVE_SPATIAL
+Create_func_within Create_func_within::s_singleton;
+
+Item*
+Create_func_within::create(THD *thd, Item *arg1, Item *arg2)
{
- return new Item_func_uncompress(a);
+ return new (thd->mem_root) Item_func_spatial_rel(arg1, arg2,
+ Item_func::SP_WITHIN_FUNC);
}
+#endif
+
-Item *create_func_uncompressed_length(Item* a)
+#ifdef HAVE_SPATIAL
+Create_func_x Create_func_x::s_singleton;
+
+Item*
+Create_func_x::create(THD *thd, Item *arg1)
{
- return new Item_func_uncompressed_length(a);
+ return new (thd->mem_root) Item_func_x(arg1);
}
+#endif
+
+
+Create_func_xml_extractvalue Create_func_xml_extractvalue::s_singleton;
-Item *create_func_datediff(Item *a, Item *b)
+Item*
+Create_func_xml_extractvalue::create(THD *thd, Item *arg1, Item *arg2)
{
- return new Item_func_minus(new Item_func_to_days(a),
- new Item_func_to_days(b));
+ return new (thd->mem_root) Item_func_xml_extractvalue(arg1, arg2);
}
-Item *create_func_weekofyear(Item *a)
+
+Create_func_xml_update Create_func_xml_update::s_singleton;
+
+Item*
+Create_func_xml_update::create(THD *thd, Item *arg1, Item *arg2, Item *arg3)
{
- return new Item_func_week(a, new Item_int((char*) "0", 3, 1));
+ return new (thd->mem_root) Item_func_xml_update(arg1, arg2, arg3);
}
-Item *create_func_makedate(Item* a,Item* b)
+
+#ifdef HAVE_SPATIAL
+Create_func_y Create_func_y::s_singleton;
+
+Item*
+Create_func_y::create(THD *thd, Item *arg1)
{
- return new Item_func_makedate(a, b);
+ return new (thd->mem_root) Item_func_y(arg1);
}
+#endif
+
+
+Create_func_year_week Create_func_year_week::s_singleton;
-Item *create_func_addtime(Item* a,Item* b)
+Item*
+Create_func_year_week::create_native(THD *thd, LEX_STRING name,
+ List<Item> *item_list)
{
- return new Item_func_add_time(a, b, 0, 0);
+ Item *func= NULL;
+ int arg_count= 0;
+
+ if (item_list != NULL)
+ arg_count= item_list->elements;
+
+ switch (arg_count) {
+ case 1:
+ {
+ Item *param_1= item_list->pop();
+ Item *i0= new (thd->mem_root) Item_int((char*) "0", 0, 1);
+ func= new (thd->mem_root) Item_func_yearweek(param_1, i0);
+ break;
+ }
+ case 2:
+ {
+ Item *param_1= item_list->pop();
+ Item *param_2= item_list->pop();
+ func= new (thd->mem_root) Item_func_yearweek(param_1, param_2);
+ break;
+ }
+ default:
+ {
+ my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str);
+ break;
+ }
+ }
+
+ return func;
}
-Item *create_func_subtime(Item* a,Item* b)
+
+struct Native_func_registry
{
- return new Item_func_add_time(a, b, 0, 1);
+ LEX_STRING name;
+ Create_func *builder;
+};
+
+#define BUILDER(F) & F::s_singleton
+
+#ifdef HAVE_SPATIAL
+ #define GEOM_BUILDER(F) & F::s_singleton
+#else
+ #define GEOM_BUILDER(F) & Create_func_no_geom::s_singleton
+#endif
+
+/*
+ MySQL native functions.
+ MAINTAINER:
+ - Keep sorted for human lookup. At runtime, a hash table is used.
+ - do **NOT** conditionally (#ifdef, #ifndef) define a function *NAME*:
+ doing so will cause user code that works against a --without-XYZ binary
+ to fail with name collisions against a --with-XYZ binary.
+ Use something similar to GEOM_BUILDER instead.
+ - keep 1 line per entry, it makes grep | sort easier
+*/
+
+static Native_func_registry func_array[] =
+{
+ { { C_STRING_WITH_LEN("ABS") }, BUILDER(Create_func_abs)},
+ { { C_STRING_WITH_LEN("ACOS") }, BUILDER(Create_func_acos)},
+ { { C_STRING_WITH_LEN("ADDTIME") }, BUILDER(Create_func_addtime)},
+ { { C_STRING_WITH_LEN("AES_DECRYPT") }, BUILDER(Create_func_aes_decrypt)},
+ { { C_STRING_WITH_LEN("AES_ENCRYPT") }, BUILDER(Create_func_aes_encrypt)},
+ { { C_STRING_WITH_LEN("AREA") }, GEOM_BUILDER(Create_func_area)},
+ { { C_STRING_WITH_LEN("ASBINARY") }, GEOM_BUILDER(Create_func_as_wkb)},
+ { { C_STRING_WITH_LEN("ASIN") }, BUILDER(Create_func_asin)},
+ { { C_STRING_WITH_LEN("ASTEXT") }, GEOM_BUILDER(Create_func_as_wkt)},
+ { { C_STRING_WITH_LEN("ASWKB") }, GEOM_BUILDER(Create_func_as_wkb)},
+ { { C_STRING_WITH_LEN("ASWKT") }, GEOM_BUILDER(Create_func_as_wkt)},
+ { { C_STRING_WITH_LEN("ATAN") }, BUILDER(Create_func_atan)},
+ { { C_STRING_WITH_LEN("ATAN2") }, BUILDER(Create_func_atan)},
+ { { C_STRING_WITH_LEN("BENCHMARK") }, BUILDER(Create_func_benchmark)},
+ { { C_STRING_WITH_LEN("BIN") }, BUILDER(Create_func_bin)},
+ { { C_STRING_WITH_LEN("BIT_COUNT") }, BUILDER(Create_func_bit_count)},
+ { { C_STRING_WITH_LEN("BIT_LENGTH") }, BUILDER(Create_func_bit_length)},
+ { { C_STRING_WITH_LEN("CEIL") }, BUILDER(Create_func_ceiling)},
+ { { C_STRING_WITH_LEN("CEILING") }, BUILDER(Create_func_ceiling)},
+ { { C_STRING_WITH_LEN("CENTROID") }, GEOM_BUILDER(Create_func_centroid)},
+ { { C_STRING_WITH_LEN("CHARACTER_LENGTH") }, BUILDER(Create_func_char_length)},
+ { { C_STRING_WITH_LEN("CHAR_LENGTH") }, BUILDER(Create_func_char_length)},
+ { { C_STRING_WITH_LEN("COERCIBILITY") }, BUILDER(Create_func_coercibility)},
+ { { C_STRING_WITH_LEN("COMPRESS") }, BUILDER(Create_func_compress)},
+ { { C_STRING_WITH_LEN("CONCAT") }, BUILDER(Create_func_concat)},
+ { { C_STRING_WITH_LEN("CONCAT_WS") }, BUILDER(Create_func_concat_ws)},
+ { { C_STRING_WITH_LEN("CONNECTION_ID") }, BUILDER(Create_func_connection_id)},
+ { { C_STRING_WITH_LEN("CONV") }, BUILDER(Create_func_conv)},
+ { { C_STRING_WITH_LEN("CONVERT_TZ") }, BUILDER(Create_func_convert_tz)},
+ { { C_STRING_WITH_LEN("COS") }, BUILDER(Create_func_cos)},
+ { { C_STRING_WITH_LEN("COT") }, BUILDER(Create_func_cot)},
+ { { C_STRING_WITH_LEN("CRC32") }, BUILDER(Create_func_crc32)},
+ { { C_STRING_WITH_LEN("CROSSES") }, GEOM_BUILDER(Create_func_crosses)},
+ { { C_STRING_WITH_LEN("DATEDIFF") }, BUILDER(Create_func_datediff)},
+ { { C_STRING_WITH_LEN("DATE_FORMAT") }, BUILDER(Create_func_date_format)},
+ { { C_STRING_WITH_LEN("DAYNAME") }, BUILDER(Create_func_dayname)},
+ { { C_STRING_WITH_LEN("DAYOFMONTH") }, BUILDER(Create_func_dayofmonth)},
+ { { C_STRING_WITH_LEN("DAYOFWEEK") }, BUILDER(Create_func_dayofweek)},
+ { { C_STRING_WITH_LEN("DAYOFYEAR") }, BUILDER(Create_func_dayofyear)},
+ { { C_STRING_WITH_LEN("DECODE") }, BUILDER(Create_func_decode)},
+ { { C_STRING_WITH_LEN("DEGREES") }, BUILDER(Create_func_degrees)},
+ { { C_STRING_WITH_LEN("DES_DECRYPT") }, BUILDER(Create_func_des_decrypt)},
+ { { C_STRING_WITH_LEN("DES_ENCRYPT") }, BUILDER(Create_func_des_encrypt)},
+ { { C_STRING_WITH_LEN("DIMENSION") }, GEOM_BUILDER(Create_func_dimension)},
+ { { C_STRING_WITH_LEN("DISJOINT") }, GEOM_BUILDER(Create_func_disjoint)},
+ { { C_STRING_WITH_LEN("ELT") }, BUILDER(Create_func_elt)},
+ { { C_STRING_WITH_LEN("ENCODE") }, BUILDER(Create_func_encode)},
+ { { C_STRING_WITH_LEN("ENCRYPT") }, BUILDER(Create_func_encrypt)},
+ { { C_STRING_WITH_LEN("ENDPOINT") }, GEOM_BUILDER(Create_func_endpoint)},
+ { { C_STRING_WITH_LEN("ENVELOPE") }, GEOM_BUILDER(Create_func_envelope)},
+ { { C_STRING_WITH_LEN("EQUALS") }, GEOM_BUILDER(Create_func_equals)},
+ { { C_STRING_WITH_LEN("EXP") }, BUILDER(Create_func_exp)},
+ { { C_STRING_WITH_LEN("EXPORT_SET") }, BUILDER(Create_func_export_set)},
+ { { C_STRING_WITH_LEN("EXTERIORRING") }, GEOM_BUILDER(Create_func_exteriorring)},
+ { { C_STRING_WITH_LEN("EXTRACTVALUE") }, BUILDER(Create_func_xml_extractvalue)},
+ { { C_STRING_WITH_LEN("FIELD") }, BUILDER(Create_func_field)},
+ { { C_STRING_WITH_LEN("FIND_IN_SET") }, BUILDER(Create_func_find_in_set)},
+ { { C_STRING_WITH_LEN("FLOOR") }, BUILDER(Create_func_floor)},
+ { { C_STRING_WITH_LEN("FORMAT") }, BUILDER(Create_func_format)},
+ { { C_STRING_WITH_LEN("FOUND_ROWS") }, BUILDER(Create_func_found_rows)},
+ { { C_STRING_WITH_LEN("FROM_DAYS") }, BUILDER(Create_func_from_days)},
+ { { C_STRING_WITH_LEN("FROM_UNIXTIME") }, BUILDER(Create_func_from_unixtime)},
+ { { C_STRING_WITH_LEN("GEOMCOLLFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)},
+ { { C_STRING_WITH_LEN("GEOMCOLLFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)},
+ { { C_STRING_WITH_LEN("GEOMETRYCOLLECTIONFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)},
+ { { C_STRING_WITH_LEN("GEOMETRYCOLLECTIONFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)},
+ { { C_STRING_WITH_LEN("GEOMETRYFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)},
+ { { C_STRING_WITH_LEN("GEOMETRYFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)},
+ { { C_STRING_WITH_LEN("GEOMETRYN") }, GEOM_BUILDER(Create_func_geometryn)},
+ { { C_STRING_WITH_LEN("GEOMETRYTYPE") }, GEOM_BUILDER(Create_func_geometry_type)},
+ { { C_STRING_WITH_LEN("GEOMFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)},
+ { { C_STRING_WITH_LEN("GEOMFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)},
+ { { C_STRING_WITH_LEN("GET_LOCK") }, BUILDER(Create_func_get_lock)},
+ { { C_STRING_WITH_LEN("GLENGTH") }, GEOM_BUILDER(Create_func_glength)},
+ { { C_STRING_WITH_LEN("GREATEST") }, BUILDER(Create_func_greatest)},
+ { { C_STRING_WITH_LEN("HEX") }, BUILDER(Create_func_hex)},
+ { { C_STRING_WITH_LEN("IFNULL") }, BUILDER(Create_func_ifnull)},
+ { { C_STRING_WITH_LEN("INET_ATON") }, BUILDER(Create_func_inet_aton)},
+ { { C_STRING_WITH_LEN("INET_NTOA") }, BUILDER(Create_func_inet_ntoa)},
+ { { C_STRING_WITH_LEN("INSTR") }, BUILDER(Create_func_instr)},
+ { { C_STRING_WITH_LEN("INTERIORRINGN") }, GEOM_BUILDER(Create_func_interiorringn)},
+ { { C_STRING_WITH_LEN("INTERSECTS") }, GEOM_BUILDER(Create_func_intersects)},
+ { { C_STRING_WITH_LEN("ISCLOSED") }, GEOM_BUILDER(Create_func_isclosed)},
+ { { C_STRING_WITH_LEN("ISEMPTY") }, GEOM_BUILDER(Create_func_isempty)},
+ { { C_STRING_WITH_LEN("ISNULL") }, BUILDER(Create_func_isnull)},
+ { { C_STRING_WITH_LEN("ISSIMPLE") }, GEOM_BUILDER(Create_func_issimple)},
+ { { C_STRING_WITH_LEN("IS_FREE_LOCK") }, BUILDER(Create_func_is_free_lock)},
+ { { C_STRING_WITH_LEN("IS_USED_LOCK") }, BUILDER(Create_func_is_used_lock)},
+ { { C_STRING_WITH_LEN("LAST_DAY") }, BUILDER(Create_func_last_day)},
+ { { C_STRING_WITH_LEN("LAST_INSERT_ID") }, BUILDER(Create_func_last_insert_id)},
+ { { C_STRING_WITH_LEN("LCASE") }, BUILDER(Create_func_lcase)},
+ { { C_STRING_WITH_LEN("LEAST") }, BUILDER(Create_func_least)},
+ { { C_STRING_WITH_LEN("LENGTH") }, BUILDER(Create_func_length)},
+ { { C_STRING_WITH_LEN("LINEFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)},
+ { { C_STRING_WITH_LEN("LINEFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)},
+ { { C_STRING_WITH_LEN("LINESTRINGFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)},
+ { { C_STRING_WITH_LEN("LINESTRINGFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)},
+ { { C_STRING_WITH_LEN("LN") }, BUILDER(Create_func_ln)},
+ { { C_STRING_WITH_LEN("LOAD_FILE") }, BUILDER(Create_func_load_file)},
+ { { C_STRING_WITH_LEN("LOCATE") }, BUILDER(Create_func_locate)},
+ { { C_STRING_WITH_LEN("LOG") }, BUILDER(Create_func_log)},
+ { { C_STRING_WITH_LEN("LOG10") }, BUILDER(Create_func_log10)},
+ { { C_STRING_WITH_LEN("LOG2") }, BUILDER(Create_func_log2)},
+ { { C_STRING_WITH_LEN("LOWER") }, BUILDER(Create_func_lcase)},
+ { { C_STRING_WITH_LEN("LPAD") }, BUILDER(Create_func_lpad)},
+ { { C_STRING_WITH_LEN("LTRIM") }, BUILDER(Create_func_ltrim)},
+ { { C_STRING_WITH_LEN("MAKEDATE") }, BUILDER(Create_func_makedate)},
+ { { C_STRING_WITH_LEN("MAKETIME") }, BUILDER(Create_func_maketime)},
+ { { C_STRING_WITH_LEN("MAKE_SET") }, BUILDER(Create_func_make_set)},
+ { { C_STRING_WITH_LEN("MASTER_POS_WAIT") }, BUILDER(Create_func_master_pos_wait)},
+ { { C_STRING_WITH_LEN("MBRCONTAINS") }, GEOM_BUILDER(Create_func_contains)},
+ { { C_STRING_WITH_LEN("MBRDISJOINT") }, GEOM_BUILDER(Create_func_disjoint)},
+ { { C_STRING_WITH_LEN("MBREQUAL") }, GEOM_BUILDER(Create_func_equals)},
+ { { C_STRING_WITH_LEN("MBRINTERSECTS") }, GEOM_BUILDER(Create_func_intersects)},
+ { { C_STRING_WITH_LEN("MBROVERLAPS") }, GEOM_BUILDER(Create_func_overlaps)},
+ { { C_STRING_WITH_LEN("MBRTOUCHES") }, GEOM_BUILDER(Create_func_touches)},
+ { { C_STRING_WITH_LEN("MBRWITHIN") }, GEOM_BUILDER(Create_func_within)},
+ { { C_STRING_WITH_LEN("MD5") }, BUILDER(Create_func_md5)},
+ { { C_STRING_WITH_LEN("MLINEFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)},
+ { { C_STRING_WITH_LEN("MLINEFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)},
+ { { C_STRING_WITH_LEN("MONTHNAME") }, BUILDER(Create_func_monthname)},
+ { { C_STRING_WITH_LEN("MPOINTFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)},
+ { { C_STRING_WITH_LEN("MPOINTFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)},
+ { { C_STRING_WITH_LEN("MPOLYFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)},
+ { { C_STRING_WITH_LEN("MPOLYFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)},
+ { { C_STRING_WITH_LEN("MULTILINESTRINGFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)},
+ { { C_STRING_WITH_LEN("MULTILINESTRINGFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)},
+ { { C_STRING_WITH_LEN("MULTIPOINTFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)},
+ { { C_STRING_WITH_LEN("MULTIPOINTFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)},
+ { { C_STRING_WITH_LEN("MULTIPOLYGONFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)},
+ { { C_STRING_WITH_LEN("MULTIPOLYGONFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)},
+ { { C_STRING_WITH_LEN("NAME_CONST") }, BUILDER(Create_func_name_const)},
+ { { C_STRING_WITH_LEN("NULLIF") }, BUILDER(Create_func_nullif)},
+ { { C_STRING_WITH_LEN("NUMGEOMETRIES") }, GEOM_BUILDER(Create_func_numgeometries)},
+ { { C_STRING_WITH_LEN("NUMINTERIORRINGS") }, GEOM_BUILDER(Create_func_numinteriorring)},
+ { { C_STRING_WITH_LEN("NUMPOINTS") }, GEOM_BUILDER(Create_func_numpoints)},
+ { { C_STRING_WITH_LEN("OCT") }, BUILDER(Create_func_oct)},
+ { { C_STRING_WITH_LEN("OCTET_LENGTH") }, BUILDER(Create_func_length)},
+ { { C_STRING_WITH_LEN("ORD") }, BUILDER(Create_func_ord)},
+ { { C_STRING_WITH_LEN("OVERLAPS") }, GEOM_BUILDER(Create_func_overlaps)},
+ { { C_STRING_WITH_LEN("PERIOD_ADD") }, BUILDER(Create_func_period_add)},
+ { { C_STRING_WITH_LEN("PERIOD_DIFF") }, BUILDER(Create_func_period_diff)},
+ { { C_STRING_WITH_LEN("PI") }, BUILDER(Create_func_pi)},
+ { { C_STRING_WITH_LEN("POINTFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)},
+ { { C_STRING_WITH_LEN("POINTFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)},
+ { { C_STRING_WITH_LEN("POINTN") }, GEOM_BUILDER(Create_func_pointn)},
+ { { C_STRING_WITH_LEN("POLYFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)},
+ { { C_STRING_WITH_LEN("POLYFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)},
+ { { C_STRING_WITH_LEN("POLYGONFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)},
+ { { C_STRING_WITH_LEN("POLYGONFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)},
+ { { C_STRING_WITH_LEN("POW") }, BUILDER(Create_func_pow)},
+ { { C_STRING_WITH_LEN("POWER") }, BUILDER(Create_func_pow)},
+ { { C_STRING_WITH_LEN("QUOTE") }, BUILDER(Create_func_quote)},
+ { { C_STRING_WITH_LEN("RADIANS") }, BUILDER(Create_func_radians)},
+ { { C_STRING_WITH_LEN("RAND") }, BUILDER(Create_func_rand)},
+ { { C_STRING_WITH_LEN("RELEASE_LOCK") }, BUILDER(Create_func_release_lock)},
+ { { C_STRING_WITH_LEN("REVERSE") }, BUILDER(Create_func_reverse)},
+ { { C_STRING_WITH_LEN("ROUND") }, BUILDER(Create_func_round)},
+ { { C_STRING_WITH_LEN("ROW_COUNT") }, BUILDER(Create_func_row_count)},
+ { { C_STRING_WITH_LEN("RPAD") }, BUILDER(Create_func_rpad)},
+ { { C_STRING_WITH_LEN("RTRIM") }, BUILDER(Create_func_rtrim)},
+ { { C_STRING_WITH_LEN("SEC_TO_TIME") }, BUILDER(Create_func_sec_to_time)},
+ { { C_STRING_WITH_LEN("SHA") }, BUILDER(Create_func_sha)},
+ { { C_STRING_WITH_LEN("SHA1") }, BUILDER(Create_func_sha)},
+ { { C_STRING_WITH_LEN("SIGN") }, BUILDER(Create_func_sign)},
+ { { C_STRING_WITH_LEN("SIN") }, BUILDER(Create_func_sin)},
+ { { C_STRING_WITH_LEN("SLEEP") }, BUILDER(Create_func_sleep)},
+ { { C_STRING_WITH_LEN("SOUNDEX") }, BUILDER(Create_func_soundex)},
+ { { C_STRING_WITH_LEN("SPACE") }, BUILDER(Create_func_space)},
+ { { C_STRING_WITH_LEN("SQRT") }, BUILDER(Create_func_sqrt)},
+ { { C_STRING_WITH_LEN("SRID") }, GEOM_BUILDER(Create_func_srid)},
+ { { C_STRING_WITH_LEN("STARTPOINT") }, GEOM_BUILDER(Create_func_startpoint)},
+ { { C_STRING_WITH_LEN("STRCMP") }, BUILDER(Create_func_strcmp)},
+ { { C_STRING_WITH_LEN("STR_TO_DATE") }, BUILDER(Create_func_str_to_date)},
+ { { C_STRING_WITH_LEN("SUBSTRING_INDEX") }, BUILDER(Create_func_substr_index)},
+ { { C_STRING_WITH_LEN("SUBTIME") }, BUILDER(Create_func_subtime)},
+ { { C_STRING_WITH_LEN("TAN") }, BUILDER(Create_func_tan)},
+ { { C_STRING_WITH_LEN("TIMEDIFF") }, BUILDER(Create_func_timediff)},
+ { { C_STRING_WITH_LEN("TIME_FORMAT") }, BUILDER(Create_func_time_format)},
+ { { C_STRING_WITH_LEN("TIME_TO_SEC") }, BUILDER(Create_func_time_to_sec)},
+ { { C_STRING_WITH_LEN("TOUCHES") }, GEOM_BUILDER(Create_func_touches)},
+ { { C_STRING_WITH_LEN("TO_DAYS") }, BUILDER(Create_func_to_days)},
+ { { C_STRING_WITH_LEN("UCASE") }, BUILDER(Create_func_ucase)},
+ { { C_STRING_WITH_LEN("UNCOMPRESS") }, BUILDER(Create_func_uncompress)},
+ { { C_STRING_WITH_LEN("UNCOMPRESSED_LENGTH") }, BUILDER(Create_func_uncompressed_length)},
+ { { C_STRING_WITH_LEN("UNHEX") }, BUILDER(Create_func_unhex)},
+ { { C_STRING_WITH_LEN("UNIX_TIMESTAMP") }, BUILDER(Create_func_unix_timestamp)},
+ { { C_STRING_WITH_LEN("UPDATEXML") }, BUILDER(Create_func_xml_update)},
+ { { C_STRING_WITH_LEN("UPPER") }, BUILDER(Create_func_ucase)},
+ { { C_STRING_WITH_LEN("UUID") }, BUILDER(Create_func_uuid)},
+ { { C_STRING_WITH_LEN("UUID_SHORT") }, BUILDER(Create_func_uuid_short)},
+ { { C_STRING_WITH_LEN("VERSION") }, BUILDER(Create_func_version)},
+ { { C_STRING_WITH_LEN("WEEKDAY") }, BUILDER(Create_func_weekday)},
+ { { C_STRING_WITH_LEN("WEEKOFYEAR") }, BUILDER(Create_func_weekofyear)},
+ { { C_STRING_WITH_LEN("WITHIN") }, GEOM_BUILDER(Create_func_within)},
+ { { C_STRING_WITH_LEN("X") }, GEOM_BUILDER(Create_func_x)},
+ { { C_STRING_WITH_LEN("Y") }, GEOM_BUILDER(Create_func_y)},
+ { { C_STRING_WITH_LEN("YEARWEEK") }, BUILDER(Create_func_year_week)},
+
+ { {0, 0}, NULL}
+};
+
+static HASH native_functions_hash;
+
+extern "C" uchar*
+get_native_fct_hash_key(const uchar *buff, size_t *length,
+ my_bool /* unused */)
+{
+ Native_func_registry *func= (Native_func_registry*) buff;
+ *length= func->name.length;
+ return (uchar*) func->name.str;
+}
+
+/*
+ Load the hash table for native functions.
+ Note: this code is not thread safe, and is intended to be used at server
+ startup only (before going multi-threaded)
+*/
+
+int item_create_init()
+{
+ Native_func_registry *func;
+
+ 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)))
+ DBUG_RETURN(1);
+
+ for (func= func_array; func->builder != NULL; func++)
+ {
+ if (my_hash_insert(& native_functions_hash, (uchar*) func))
+ DBUG_RETURN(1);
+ }
+
+#ifndef DBUG_OFF
+ for (uint i=0 ; i < native_functions_hash.records ; i++)
+ {
+ func= (Native_func_registry*) hash_element(& native_functions_hash, i);
+ DBUG_PRINT("info", ("native function: %s length: %u",
+ func->name.str, (uint) func->name.length));
+ }
+#endif
+
+ DBUG_RETURN(0);
}
-Item *create_func_timediff(Item* a,Item* b)
+/*
+ Empty the hash table for native functions.
+ Note: this code is not thread safe, and is intended to be used at server
+ shutdown only (after thread requests have been executed).
+*/
+
+void item_create_cleanup()
{
- return new Item_func_timediff(a, b);
+ DBUG_ENTER("item_create_cleanup");
+ hash_free(& native_functions_hash);
+ DBUG_VOID_RETURN;
}
-Item *create_func_maketime(Item* a,Item* b,Item* c)
+Create_func *
+find_native_function_builder(THD *thd, LEX_STRING name)
{
- return new Item_func_maketime(a, b, c);
+ Native_func_registry *func;
+ Create_func *builder= NULL;
+
+ /* Thread safe */
+ func= (Native_func_registry*) hash_search(& native_functions_hash,
+ (uchar*) name.str,
+ name.length);
+
+ if (func)
+ {
+ builder= func->builder;
+ }
+
+ return builder;
}
-Item *create_func_str_to_date(Item* a,Item* b)
+Create_qfunc *
+find_qualified_function_builder(THD *thd)
{
- return new Item_func_str_to_date(a, b);
+ return & Create_sp_func::s_singleton;
}
-Item *create_func_last_day(Item *a)
+
+Item *
+create_func_cast(THD *thd, Item *a, Cast_target cast_type,
+ const char *c_len, const char *c_dec,
+ CHARSET_INFO *cs)
{
- return new Item_func_last_day(a);
+ Item *res;
+ ulong len;
+ uint dec;
+ LINT_INIT(res);
+
+ switch (cast_type) {
+ case ITEM_CAST_BINARY:
+ res= new (thd->mem_root) Item_func_binary(a);
+ break;
+ case ITEM_CAST_SIGNED_INT:
+ res= new (thd->mem_root) Item_func_signed(a);
+ break;
+ case ITEM_CAST_UNSIGNED_INT:
+ res= new (thd->mem_root) Item_func_unsigned(a);
+ break;
+ case ITEM_CAST_DATE:
+ res= new (thd->mem_root) Item_date_typecast(a);
+ break;
+ case ITEM_CAST_TIME:
+ res= new (thd->mem_root) Item_time_typecast(a);
+ break;
+ case ITEM_CAST_DATETIME:
+ res= new (thd->mem_root) Item_datetime_typecast(a);
+ break;
+ case ITEM_CAST_DECIMAL:
+ {
+ if (c_len == NULL)
+ {
+ len= 0;
+ }
+ else
+ {
+ ulong decoded_size;
+ errno= 0;
+ decoded_size= strtoul(c_len, NULL, 10);
+ if (errno != 0)
+ {
+ my_error(ER_TOO_BIG_PRECISION, MYF(0), c_len, a->name,
+ DECIMAL_MAX_PRECISION);
+ return NULL;
+ }
+ len= decoded_size;
+ }
+
+ if (c_dec == NULL)
+ {
+ dec= 0;
+ }
+ else
+ {
+ ulong decoded_size;
+ errno= 0;
+ decoded_size= strtoul(c_dec, NULL, 10);
+ if ((errno != 0) || (decoded_size > UINT_MAX))
+ {
+ my_error(ER_TOO_BIG_SCALE, MYF(0), c_dec, a->name,
+ DECIMAL_MAX_SCALE);
+ return NULL;
+ }
+ dec= decoded_size;
+ }
+ my_decimal_trim(&len, &dec);
+ if (len < dec)
+ {
+ my_error(ER_M_BIGGER_THAN_D, MYF(0), "");
+ return 0;
+ }
+ if (len > DECIMAL_MAX_PRECISION)
+ {
+ my_error(ER_TOO_BIG_PRECISION, MYF(0), len, a->name,
+ DECIMAL_MAX_PRECISION);
+ return 0;
+ }
+ if (dec > DECIMAL_MAX_SCALE)
+ {
+ my_error(ER_TOO_BIG_SCALE, MYF(0), dec, a->name,
+ DECIMAL_MAX_SCALE);
+ return 0;
+ }
+ res= new (thd->mem_root) Item_decimal_typecast(a, len, dec);
+ break;
+ }
+ case ITEM_CAST_CHAR:
+ {
+ CHARSET_INFO *real_cs= (cs ? cs : thd->variables.collation_connection);
+ if (c_len == NULL)
+ {
+ len= LL(-1);
+ }
+ else
+ {
+ ulong decoded_size;
+ errno= 0;
+ decoded_size= strtoul(c_len, NULL, 10);
+ if ((errno != 0) || (decoded_size > MAX_FIELD_BLOBLENGTH))
+ {
+ my_error(ER_TOO_BIG_DISPLAYWIDTH, MYF(0), "cast as char", MAX_FIELD_BLOBLENGTH);
+ return NULL;
+ }
+ len= decoded_size;
+ }
+ res= new (thd->mem_root) Item_char_typecast(a, len, real_cs);
+ break;
+ }
+ default:
+ {
+ DBUG_ASSERT(0);
+ res= 0;
+ break;
+ }
+ }
+ return res;
}
diff --git a/sql/item_create.h b/sql/item_create.h
index 46b209b3e49..a3ba6bd26a6 100644
--- a/sql/item_create.h
+++ b/sql/item_create.h
@@ -13,148 +13,155 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
-/* Functions to create an item. Used by lex.h */
-
-Item *create_func_abs(Item* a);
-Item *create_func_acos(Item* a);
-Item *create_func_aes_encrypt(Item* a, Item* b);
-Item *create_func_aes_decrypt(Item* a, Item* b);
-Item *create_func_ascii(Item* a);
-Item *create_func_asin(Item* a);
-Item *create_func_bin(Item* a);
-Item *create_func_bit_count(Item* a);
-Item *create_func_bit_length(Item* a);
-Item *create_func_coercibility(Item* a);
-Item *create_func_ceiling(Item* a);
-Item *create_func_char_length(Item* a);
-Item *create_func_cast(Item *a, Cast_target cast_type,
- const char *len, const char *dec,
- CHARSET_INFO *cs);
-Item *create_func_connection_id(void);
-Item *create_func_conv(Item* a, Item *b, Item *c);
-Item *create_func_cos(Item* a);
-Item *create_func_cot(Item* a);
-Item *create_func_crc32(Item* a);
-Item *create_func_date_format(Item* a,Item *b);
-Item *create_func_dayname(Item* a);
-Item *create_func_dayofmonth(Item* a);
-Item *create_func_dayofweek(Item* a);
-Item *create_func_dayofyear(Item* a);
-Item *create_func_degrees(Item *);
-Item *create_func_exp(Item* a);
-Item *create_func_find_in_set(Item* a, Item *b);
-Item *create_func_floor(Item* a);
-Item *create_func_found_rows(void);
-Item *create_func_from_days(Item* a);
-Item *create_func_get_lock(Item* a, Item *b);
-Item *create_func_hex(Item *a);
-Item *create_func_inet_aton(Item* a);
-Item *create_func_inet_ntoa(Item* a);
-
-Item *create_func_ifnull(Item* a, Item *b);
-Item *create_func_instr(Item* a, Item *b);
-Item *create_func_isnull(Item* a);
-Item *create_func_lcase(Item* a);
-Item *create_func_length(Item* a);
-Item *create_func_ln(Item* a);
-Item *create_func_locate(Item* a, Item *b);
-Item *create_func_log2(Item* a);
-Item *create_func_log10(Item* a);
-Item *create_func_lpad(Item* a, Item *b, Item *c);
-Item *create_func_ltrim(Item* a);
-Item *create_func_md5(Item* a);
-Item *create_func_mod(Item* a, Item *b);
-Item *create_func_monthname(Item* a);
-Item *create_func_name_const(Item *a, Item *b);
-Item *create_func_nullif(Item* a, Item *b);
-Item *create_func_oct(Item *);
-Item *create_func_ord(Item* a);
-Item *create_func_period_add(Item* a, Item *b);
-Item *create_func_period_diff(Item* a, Item *b);
-Item *create_func_pi(void);
-Item *create_func_pow(Item* a, Item *b);
-Item *create_func_radians(Item *a);
-Item *create_func_release_lock(Item* a);
-Item *create_func_repeat(Item* a, Item *b);
-Item *create_func_reverse(Item* a);
-Item *create_func_rpad(Item* a, Item *b, Item *c);
-Item *create_func_rtrim(Item* a);
-Item *create_func_sec_to_time(Item* a);
-Item *create_func_sign(Item* a);
-Item *create_func_sin(Item* a);
-Item *create_func_sha(Item* a);
-Item *create_func_sleep(Item* a);
-Item *create_func_soundex(Item* a);
-Item *create_func_space(Item *);
-Item *create_func_sqrt(Item* a);
-Item *create_func_strcmp(Item* a, Item *b);
-Item *create_func_tan(Item* a);
-Item *create_func_time_format(Item *a, Item *b);
-Item *create_func_time_to_sec(Item* a);
-Item *create_func_to_days(Item* a);
-Item *create_func_ucase(Item* a);
-Item *create_func_unhex(Item* a);
-Item *create_func_uuid(void);
-Item *create_func_version(void);
-Item *create_func_weekday(Item* a);
-Item *create_load_file(Item* a);
-Item *create_func_is_free_lock(Item* a);
-Item *create_func_is_used_lock(Item* a);
-Item *create_func_quote(Item* a);
-
-#ifdef HAVE_SPATIAL
-
-Item *create_func_geometry_from_text(Item *a);
-Item *create_func_as_wkt(Item *a);
-Item *create_func_as_wkb(Item *a);
-Item *create_func_srid(Item *a);
-Item *create_func_startpoint(Item *a);
-Item *create_func_endpoint(Item *a);
-Item *create_func_exteriorring(Item *a);
-Item *create_func_centroid(Item *a);
-Item *create_func_envelope(Item *a);
-Item *create_func_pointn(Item *a, Item *b);
-Item *create_func_interiorringn(Item *a, Item *b);
-Item *create_func_geometryn(Item *a, Item *b);
-
-Item *create_func_equals(Item *a, Item *b);
-Item *create_func_disjoint(Item *a, Item *b);
-Item *create_func_intersects(Item *a, Item *b);
-Item *create_func_touches(Item *a, Item *b);
-Item *create_func_crosses(Item *a, Item *b);
-Item *create_func_within(Item *a, Item *b);
-Item *create_func_contains(Item *a, Item *b);
-Item *create_func_overlaps(Item *a, Item *b);
-
-Item *create_func_isempty(Item *a);
-Item *create_func_issimple(Item *a);
-Item *create_func_isclosed(Item *a);
-
-Item *create_func_geometry_type(Item *a);
-Item *create_func_dimension(Item *a);
-Item *create_func_x(Item *a);
-Item *create_func_y(Item *a);
-Item *create_func_area(Item *a);
-Item *create_func_glength(Item *a);
-
-Item *create_func_numpoints(Item *a);
-Item *create_func_numinteriorring(Item *a);
-Item *create_func_numgeometries(Item *a);
-
-Item *create_func_point(Item *a, Item *b);
-
-#endif /*HAVE_SPATIAL*/
-
-Item *create_func_compress(Item *a);
-Item *create_func_uncompress(Item *a);
-Item *create_func_uncompressed_length(Item *a);
-
-Item *create_func_datediff(Item *a, Item *b);
-Item *create_func_weekofyear(Item *a);
-Item *create_func_makedate(Item* a,Item* b);
-Item *create_func_addtime(Item* a,Item* b);
-Item *create_func_subtime(Item* a,Item* b);
-Item *create_func_timediff(Item* a,Item* b);
-Item *create_func_maketime(Item* a,Item* b,Item* c);
-Item *create_func_str_to_date(Item* a,Item* b);
-Item *create_func_last_day(Item *a);
+/* Functions to create an item. Used by sql/sql_yacc.yy */
+
+#ifndef ITEM_CREATE_H
+#define ITEM_CREATE_H
+
+/**
+ Public function builder interface.
+ The parser (sql/sql_yacc.yy) uses a factory / builder pattern to
+ construct an <code>Item</code> object for each function call.
+ All the concrete function builders implements this interface,
+ either directly or indirectly with some adapter helpers.
+ Keeping the function creation separated from the bison grammar allows
+ to simplify the parser, and avoid the need to introduce a new token
+ for each function, which has undesirable side effects in the grammar.
+*/
+
+class Create_func
+{
+public:
+ /**
+ The builder create method.
+ Given the function name and list or arguments, this method creates
+ an <code>Item</code> that represents the function call.
+ In case or errors, a NULL item is returned, and an error is reported.
+ Note that the <code>thd</code> object may be modified by the builder.
+ In particular, the following members/methods can be set/called,
+ depending on the function called and the function possible side effects.
+ <ul>
+ <li><code>thd->lex->binlog_row_based_if_mixed</code></li>
+ <li><code>thd->lex->current_context()</code></li>
+ <li><code>thd->lex->safe_to_cache_query</code></li>
+ <li><code>thd->lex->uncacheable(UNCACHEABLE_SIDEEFFECT)</code></li>
+ <li><code>thd->lex->uncacheable(UNCACHEABLE_RAND)</code></li>
+ <li><code>thd->lex->add_time_zone_tables_to_query_tables(thd)</code></li>
+ </ul>
+ @param thd The current thread
+ @param name The function name
+ @param item_list The list of arguments to the function, can be NULL
+ @return An item representing the parsed function call, or NULL
+ */
+ virtual Item *create(THD *thd, LEX_STRING name, List<Item> *item_list) = 0;
+
+protected:
+ /** Constructor */
+ Create_func() {}
+ /** Destructor */
+ virtual ~Create_func() {}
+};
+
+
+/**
+ Function builder for qualified functions.
+ This builder is used with functions call using a qualified function name
+ syntax, as in <code>db.func(expr, expr, ...)</code>.
+*/
+
+class Create_qfunc : public Create_func
+{
+public:
+ /**
+ The builder create method, for unqualified functions.
+ This builder will use the current database for the database name.
+ @param thd The current thread
+ @param name The function name
+ @param item_list The list of arguments to the function, can be NULL
+ @return An item representing the parsed function call
+ */
+ virtual Item *create(THD *thd, LEX_STRING name, List<Item> *item_list);
+
+ /**
+ The builder create method, for qualified functions.
+ @param thd The current thread
+ @param db The database name
+ @param name The function name
+ @param use_explicit_name Should the function be represented as 'db.name'?
+ @param item_list The list of arguments to the function, can be NULL
+ @return An item representing the parsed function call
+ */
+ virtual Item* create(THD *thd, LEX_STRING db, LEX_STRING name,
+ bool use_explicit_name, List<Item> *item_list) = 0;
+
+protected:
+ /** Constructor. */
+ Create_qfunc() {}
+ /** Destructor. */
+ virtual ~Create_qfunc() {}
+};
+
+
+/**
+ Find the native function builder associated with a given function name.
+ @param thd The current thread
+ @param name The native function name
+ @return The native function builder associated with the name, or NULL
+*/
+extern Create_func * find_native_function_builder(THD *thd, LEX_STRING name);
+
+
+/**
+ Find the function builder for qualified functions.
+ @param thd The current thread
+ @return A function builder for qualified functions
+*/
+extern Create_qfunc * find_qualified_function_builder(THD *thd);
+
+
+#ifdef HAVE_DLOPEN
+/**
+ Function builder for User Defined Functions.
+*/
+
+class Create_udf_func : public Create_func
+{
+public:
+ virtual Item *create(THD *thd, LEX_STRING name, List<Item> *item_list);
+
+ /**
+ The builder create method, for User Defined Functions.
+ @param thd The current thread
+ @param fct The User Defined Function metadata
+ @param item_list The list of arguments to the function, can be NULL
+ @return An item representing the parsed function call
+ */
+ Item *create(THD *thd, udf_func *fct, List<Item> *item_list);
+
+ /** Singleton. */
+ static Create_udf_func s_singleton;
+
+protected:
+ /** Constructor. */
+ Create_udf_func() {}
+ /** Destructor. */
+ virtual ~Create_udf_func() {}
+};
+#endif
+
+
+/**
+ Builder for cast expressions.
+ @param thd The current thread
+ @param a The item to cast
+ @param cast_type the type casted into
+ @param len TODO
+ @param dec TODO
+ @param cs The character set
+*/
+Item *
+create_func_cast(THD *thd, Item *a, Cast_target cast_type,
+ const char *len, const char *dec,
+ CHARSET_INFO *cs);
+#endif
+
diff --git a/sql/item_func.cc b/sql/item_func.cc
index 8aa2565d928..ac96d4f6b24 100644
--- a/sql/item_func.cc
+++ b/sql/item_func.cc
@@ -1,4 +1,4 @@
-/* Copyright (C) 2000-2003 MySQL AB
+/* Copyright 2000-2008 MySQL AB, 2008 Sun Microsystems, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -14,7 +14,12 @@
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
-/* This file defines all numerical functions */
+/**
+ @file
+
+ @brief
+ This file defines all numerical functions
+*/
#ifdef USE_PRAGMA_IMPLEMENTATION
#pragma implementation // gcc: Class implementation
@@ -22,10 +27,12 @@
#include "mysql_priv.h"
#include "slave.h" // for wait_for_master_pos
+#include "rpl_mi.h"
#include <m_ctype.h>
#include <hash.h>
#include <time.h>
#include <ft_global.h>
+#include <my_bit.h>
#include "sp_head.h"
#include "sp_rcontext.h"
@@ -35,7 +42,6 @@
#define sp_restore_security_context(A,B) while (0) {}
#endif
-
bool check_reserved_words(LEX_STRING *name)
{
if (!my_strcasecmp(system_charset_info, name->str, "GLOBAL") ||
@@ -46,7 +52,10 @@ bool check_reserved_words(LEX_STRING *name)
}
-/* return TRUE if item is a constant */
+/**
+ @return
+ TRUE if item is a constant
+*/
bool
eval_const_cond(COND *cond)
@@ -142,7 +151,7 @@ Item_func::fix_fields(THD *thd, Item **ref)
DBUG_ASSERT(fixed == 0);
Item **arg,**arg_end;
#ifndef EMBEDDED_LIBRARY // Avoid compiler warning
- char buff[STACK_BUFF_ALLOC]; // Max argument in function
+ uchar buff[STACK_BUFF_ALLOC]; // Max argument in function
#endif
used_tables_cache= not_null_tables_cache= 0;
@@ -187,20 +196,22 @@ Item_func::fix_fields(THD *thd, Item **ref)
}
}
fix_length_and_dec();
- if (thd->net.report_error) // An error inside fix_length_and_dec occured
+ if (thd->is_error()) // An error inside fix_length_and_dec occured
return TRUE;
fixed= 1;
return FALSE;
}
-bool Item_func::walk (Item_processor processor, byte *argument)
+
+bool Item_func::walk(Item_processor processor, bool walk_subquery,
+ uchar *argument)
{
if (arg_count)
{
Item **arg,**arg_end;
for (arg= args, arg_end= args+arg_count; arg != arg_end; arg++)
{
- if ((*arg)->walk(processor, argument))
+ if ((*arg)->walk(processor, walk_subquery, argument))
return 1;
}
}
@@ -235,28 +246,24 @@ void Item_func::traverse_cond(Cond_traverser traverser,
}
-/*
- Transform an Item_func object with a transformer callback function
-
- SYNOPSIS
- transform()
- transformer the transformer callback function to be applied to the nodes
- of the tree of the object
- argument parameter to be passed to the transformer
-
- DESCRIPTION
+/**
+ Transform an Item_func object with a transformer callback function.
+
The function recursively applies the transform method to each
argument of the Item_func node.
If the call of the method for an argument item returns a new item
the old item is substituted for a new one.
After this the transformer is applied to the root node
of the Item_func object.
-
- RETURN VALUES
- Item returned as the result of transformation of the root node
+ @param transformer the transformer callback function to be applied to
+ the nodes of the tree of the object
+ @param argument parameter to be passed to the transformer
+
+ @return
+ Item returned as the result of transformation of the root node
*/
-Item *Item_func::transform(Item_transformer transformer, byte *argument)
+Item *Item_func::transform(Item_transformer transformer, uchar *argument)
{
DBUG_ASSERT(!current_thd->is_stmt_prepare());
@@ -283,19 +290,10 @@ Item *Item_func::transform(Item_transformer transformer, byte *argument)
}
-/*
- Compile Item_func object with a processor and a transformer callback functions
-
- SYNOPSIS
- compile()
- analyzer the analyzer callback function to be applied to the nodes
- of the tree of the object
- arg_p in/out parameter to be passed to the processor
- transformer the transformer callback function to be applied to the nodes
- of the tree of the object
- arg_t parameter to be passed to the transformer
-
- DESCRIPTION
+/**
+ Compile Item_func object with a processor and a transformer
+ callback functions.
+
First the function applies the analyzer to the root node of
the Item_func object. Then if the analizer succeeeds (returns TRUE)
the function recursively applies the compile method to each argument
@@ -304,13 +302,20 @@ Item *Item_func::transform(Item_transformer transformer, byte *argument)
the old item is substituted for a new one.
After this the transformer is applied to the root node
of the Item_func object.
-
- RETURN VALUES
- Item returned as the result of transformation of the root node
+
+ @param analyzer the analyzer callback function to be applied to the
+ nodes of the tree of the object
+ @param[in,out] arg_p parameter to be passed to the processor
+ @param transformer the transformer callback function to be applied to the
+ nodes of the tree of the object
+ @param arg_t parameter to be passed to the transformer
+
+ @return
+ Item returned as the result of transformation of the root node
*/
-Item *Item_func::compile(Item_analyzer analyzer, byte **arg_p,
- Item_transformer transformer, byte *arg_t)
+Item *Item_func::compile(Item_analyzer analyzer, uchar **arg_p,
+ Item_transformer transformer, uchar *arg_t)
{
if (!(this->*analyzer)(arg_p))
return 0;
@@ -323,7 +328,7 @@ Item *Item_func::compile(Item_analyzer analyzer, byte **arg_p,
The same parameter value of arg_p must be passed
to analyze any argument of the condition formula.
*/
- byte *arg_v= *arg_p;
+ uchar *arg_v= *arg_p;
Item *new_item= (*arg)->compile(analyzer, &arg_v, transformer, arg_t);
if (new_item && *arg != new_item)
current_thd->change_item_tree(arg, new_item);
@@ -332,7 +337,9 @@ Item *Item_func::compile(Item_analyzer analyzer, byte **arg_p,
return (this->*transformer)(arg_t);
}
-/* See comments in Item_cmp_func::split_sum_func() */
+/**
+ See comments in Item_cmp_func::split_sum_func()
+*/
void Item_func::split_sum_func(THD *thd, Item **ref_pointer_array,
List<Item> &fields)
@@ -368,37 +375,37 @@ table_map Item_func::not_null_tables() const
}
-void Item_func::print(String *str)
+void Item_func::print(String *str, enum_query_type query_type)
{
str->append(func_name());
str->append('(');
- print_args(str, 0);
+ print_args(str, 0, query_type);
str->append(')');
}
-void Item_func::print_args(String *str, uint from)
+void Item_func::print_args(String *str, uint from, enum_query_type query_type)
{
for (uint i=from ; i < arg_count ; i++)
{
if (i != from)
str->append(',');
- args[i]->print(str);
+ args[i]->print(str, query_type);
}
}
-void Item_func::print_op(String *str)
+void Item_func::print_op(String *str, enum_query_type query_type)
{
str->append('(');
for (uint i=0 ; i < arg_count-1 ; i++)
{
- args[i]->print(str);
+ args[i]->print(str, query_type);
str->append(' ');
str->append(func_name());
str->append(' ');
}
- args[arg_count-1]->print(str);
+ args[arg_count-1]->print(str, query_type);
str->append(')');
}
@@ -426,43 +433,44 @@ bool Item_func::eq(const Item *item, bool binary_cmp) const
}
-Field *Item_func::tmp_table_field(TABLE *t_arg)
+Field *Item_func::tmp_table_field(TABLE *table)
{
- Field *res;
- LINT_INIT(res);
+ Field *field;
+ LINT_INIT(field);
switch (result_type()) {
case INT_RESULT:
if (max_length > MY_INT32_NUM_DECIMAL_DIGITS)
- res= new Field_longlong(max_length, maybe_null, name, t_arg,
- unsigned_flag);
+ field= new Field_longlong(max_length, maybe_null, name, unsigned_flag);
else
- res= new Field_long(max_length, maybe_null, name, t_arg,
- unsigned_flag);
+ field= new Field_long(max_length, maybe_null, name, unsigned_flag);
break;
case REAL_RESULT:
- res= new Field_double(max_length, maybe_null, name, t_arg, decimals);
+ field= new Field_double(max_length, maybe_null, name, decimals);
break;
case STRING_RESULT:
- res= make_string_field(t_arg);
+ return make_string_field(table);
break;
case DECIMAL_RESULT:
- res= new Field_new_decimal(my_decimal_precision_to_length(decimal_precision(),
- decimals,
- unsigned_flag),
- maybe_null, name, t_arg, decimals, unsigned_flag);
+ field= new Field_new_decimal(my_decimal_precision_to_length(decimal_precision(),
+ decimals,
+ unsigned_flag),
+ maybe_null, name, decimals, unsigned_flag);
break;
case ROW_RESULT:
default:
// This case should never be chosen
DBUG_ASSERT(0);
+ field= 0;
break;
}
- return res;
+ if (field)
+ field->init(table);
+ return field;
}
-bool Item_func::is_expensive_processor(byte *arg)
+bool Item_func::is_expensive_processor(uchar *arg)
{
return is_expensive();
}
@@ -482,7 +490,7 @@ String *Item_real_func::val_str(String *str)
double nr= val_real();
if (null_value)
return 0; /* purecov: inspected */
- str->set(nr,decimals, &my_charset_bin);
+ str->set_real(nr,decimals, &my_charset_bin);
return str;
}
@@ -520,12 +528,9 @@ void Item_func_numhybrid::fix_num_length_and_dec()
{}
-/*
+/**
Set max_length/decimals of function if function is fixed point and
- result length/precision depends on argument ones
-
- SYNOPSIS
- Item_func::count_decimal_length()
+ result length/precision depends on argument ones.
*/
void Item_func::count_decimal_length()
@@ -545,11 +550,8 @@ void Item_func::count_decimal_length()
}
-/*
- Set max_length of if it is maximum length of its arguments
-
- SYNOPSIS
- Item_func::count_only_length()
+/**
+ Set max_length of if it is maximum length of its arguments.
*/
void Item_func::count_only_length()
@@ -564,12 +566,9 @@ void Item_func::count_only_length()
}
-/*
+/**
Set max_length/decimals of function if function is floating point and
- result length/precision depends on argument ones
-
- SYNOPSIS
- Item_func::count_real_length()
+ result length/precision depends on argument ones.
*/
void Item_func::count_real_length()
@@ -630,10 +629,7 @@ String *Item_int_func::val_str(String *str)
longlong nr=val_int();
if (null_value)
return 0;
- if (!unsigned_flag)
- str->set(nr,&my_charset_bin);
- else
- str->set((ulonglong) nr,&my_charset_bin);
+ str->set_int(nr, unsigned_flag, &my_charset_bin);
return str;
}
@@ -655,12 +651,9 @@ bool Item_func_connection_id::fix_fields(THD *thd, Item **ref)
}
-/*
+/**
Check arguments here to determine result's type for a numeric
function of two arguments.
-
- SYNOPSIS
- Item_num_op::find_num_type()
*/
void Item_num_op::find_num_type(void)
@@ -699,13 +692,10 @@ void Item_num_op::find_num_type(void)
}
-/*
+/**
Set result type for a numeric function of one argument
(can be also used by a numeric function of many arguments, if the result
type depends only on the first argument)
-
- SYNOPSIS
- Item_func_num1::find_num_type()
*/
void Item_func_num1::find_num_type()
@@ -767,10 +757,7 @@ String *Item_func_numhybrid::val_str(String *str)
longlong nr= int_op();
if (null_value)
return 0; /* purecov: inspected */
- if (!unsigned_flag)
- str->set(nr,&my_charset_bin);
- else
- str->set((ulonglong) nr,&my_charset_bin);
+ str->set_int(nr, unsigned_flag, &my_charset_bin);
break;
}
case REAL_RESULT:
@@ -778,7 +765,7 @@ String *Item_func_numhybrid::val_str(String *str)
double nr= real_op();
if (null_value)
return 0; /* purecov: inspected */
- str->set(nr,decimals,&my_charset_bin);
+ str->set_real(nr,decimals,&my_charset_bin);
break;
}
case STRING_RESULT:
@@ -898,10 +885,10 @@ my_decimal *Item_func_numhybrid::val_decimal(my_decimal *decimal_value)
}
-void Item_func_signed::print(String *str)
+void Item_func_signed::print(String *str, enum_query_type query_type)
{
str->append(STRING_WITH_LEN("cast("));
- args[0]->print(str);
+ args[0]->print(str, query_type);
str->append(STRING_WITH_LEN(" as signed)"));
}
@@ -969,10 +956,10 @@ longlong Item_func_signed::val_int()
}
-void Item_func_unsigned::print(String *str)
+void Item_func_unsigned::print(String *str, enum_query_type query_type)
{
str->append(STRING_WITH_LEN("cast("));
- args[0]->print(str);
+ args[0]->print(str, query_type);
str->append(STRING_WITH_LEN(" as unsigned)"));
}
@@ -1078,7 +1065,7 @@ err:
}
-void Item_decimal_typecast::print(String *str)
+void Item_decimal_typecast::print(String *str, enum_query_type query_type)
{
char len_buf[20*3 + 1];
char *end;
@@ -1086,7 +1073,7 @@ void Item_decimal_typecast::print(String *str)
uint precision= my_decimal_length_to_precision(max_length, decimals,
unsigned_flag);
str->append(STRING_WITH_LEN("cast("));
- args[0]->print(str);
+ args[0]->print(str, query_type);
str->append(STRING_WITH_LEN(" as decimal("));
end=int10_to_str(precision, len_buf,10);
@@ -1107,7 +1094,7 @@ double Item_func_plus::real_op()
double value= args[0]->val_real() + args[1]->val_real();
if ((null_value=args[0]->null_value || args[1]->null_value))
return 0.0;
- return value;
+ return fix_result(value);
}
@@ -1120,16 +1107,15 @@ longlong Item_func_plus::int_op()
}
-/*
- Calculate plus of two decimail's
+/**
+ Calculate plus of two decimals.
- SYNOPSIS
- decimal_op()
- decimal_value Buffer that can be used to store result
+ @param decimal_value Buffer that can be used to store result
- RETURN
- 0 Value was NULL; In this case null_value is set
- # Value of operation as a decimal
+ @retval
+ 0 Value was NULL; In this case null_value is set
+ @retval
+ \# Value of operation as a decimal
*/
my_decimal *Item_func_plus::decimal_op(my_decimal *decimal_value)
@@ -1147,11 +1133,8 @@ my_decimal *Item_func_plus::decimal_op(my_decimal *decimal_value)
return 0;
}
-/*
+/**
Set precision of results for additive operations (+ and -)
-
- SYNOPSIS
- Item_func_additive_op::result_precision()
*/
void Item_func_additive_op::result_precision()
{
@@ -1171,7 +1154,7 @@ void Item_func_additive_op::result_precision()
}
-/*
+/**
The following function is here to allow the user to force
subtraction of UNSIGNED BIGINT to return negative values.
*/
@@ -1190,7 +1173,7 @@ double Item_func_minus::real_op()
double value= args[0]->val_real() - args[1]->val_real();
if ((null_value=args[0]->null_value || args[1]->null_value))
return 0.0;
- return value;
+ return fix_result(value);
}
@@ -1203,7 +1186,9 @@ longlong Item_func_minus::int_op()
}
-/* See Item_func_plus::decimal_op for comments */
+/**
+ See Item_func_plus::decimal_op for comments.
+*/
my_decimal *Item_func_minus::decimal_op(my_decimal *decimal_value)
{
@@ -1228,7 +1213,7 @@ double Item_func_mul::real_op()
double value= args[0]->val_real() * args[1]->val_real();
if ((null_value=args[0]->null_value || args[1]->null_value))
return 0.0;
- return value;
+ return fix_result(value);
}
@@ -1242,7 +1227,7 @@ longlong Item_func_mul::int_op()
}
-/* See Item_func_plus::decimal_op for comments */
+/** See Item_func_plus::decimal_op for comments. */
my_decimal *Item_func_mul::decimal_op(my_decimal *decimal_value)
{
@@ -1286,7 +1271,7 @@ double Item_func_div::real_op()
signal_divide_by_null();
return 0.0;
}
- return value/val2;
+ return fix_result(value/val2);
}
@@ -1580,7 +1565,7 @@ void Item_func_abs::fix_length_and_dec()
}
-/* Gateway to natural LOG function */
+/** Gateway to natural LOG function. */
double Item_func_ln::val_real()
{
DBUG_ASSERT(fixed == 1);
@@ -1595,10 +1580,11 @@ double Item_func_ln::val_real()
return log(value);
}
-/*
- Extended but so slower LOG function
- We have to check if all values are > zero and first one is not one
- as these are the cases then result is not a number.
+/**
+ Extended but so slower LOG function.
+
+ We have to check if all values are > zero and first one is not one
+ as these are the cases then result is not a number.
*/
double Item_func_log::val_real()
{
@@ -1661,7 +1647,7 @@ double Item_func_exp::val_real()
double value= args[0]->val_real();
if ((null_value=args[0]->null_value))
return 0.0; /* purecov: inspected */
- return exp(value);
+ return fix_result(exp(value));
}
double Item_func_sqrt::val_real()
@@ -1680,7 +1666,7 @@ double Item_func_pow::val_real()
double val2= args[1]->val_real();
if ((null_value=(args[0]->null_value || args[1]->null_value)))
return 0.0; /* purecov: inspected */
- return pow(value,val2);
+ return fix_result(pow(value,val2));
}
// Trigonometric functions
@@ -1692,7 +1678,7 @@ double Item_func_acos::val_real()
volatile double value= args[0]->val_real();
if ((null_value=(args[0]->null_value || (value < -1.0 || value > 1.0))))
return 0.0;
- return fix_result(acos(value));
+ return acos(value);
}
double Item_func_asin::val_real()
@@ -1702,7 +1688,7 @@ double Item_func_asin::val_real()
volatile double value= args[0]->val_real();
if ((null_value=(args[0]->null_value || (value < -1.0 || value > 1.0))))
return 0.0;
- return fix_result(asin(value));
+ return asin(value);
}
double Item_func_atan::val_real()
@@ -1718,7 +1704,7 @@ double Item_func_atan::val_real()
return 0.0;
return fix_result(atan2(value,val2));
}
- return fix_result(atan(value));
+ return atan(value);
}
double Item_func_cos::val_real()
@@ -1727,7 +1713,7 @@ double Item_func_cos::val_real()
double value= args[0]->val_real();
if ((null_value=args[0]->null_value))
return 0.0;
- return fix_result(cos(value));
+ return cos(value);
}
double Item_func_sin::val_real()
@@ -1736,7 +1722,7 @@ double Item_func_sin::val_real()
double value= args[0]->val_real();
if ((null_value=args[0]->null_value))
return 0.0;
- return fix_result(sin(value));
+ return sin(value);
}
double Item_func_tan::val_real()
@@ -2127,6 +2113,18 @@ my_decimal *Item_func_round::decimal_op(my_decimal *decimal_value)
}
+void Item_func_rand::seed_random(Item *arg)
+{
+ /*
+ TODO: do not do reinit 'rand' for every execute of PS/SP if
+ args[0] is a constant.
+ */
+ uint32 tmp= (uint32) arg->val_int();
+ randominit(rand, (uint32) (tmp*0x10001L+55555555L),
+ (uint32) (tmp*0x10000001L));
+}
+
+
bool Item_func_rand::fix_fields(THD *thd,Item **ref)
{
if (Item_real_func::fix_fields(thd, ref))
@@ -2134,11 +2132,6 @@ bool Item_func_rand::fix_fields(THD *thd,Item **ref)
used_tables_cache|= RAND_TABLE_BIT;
if (arg_count)
{ // Only use argument once in query
- if (!args[0]->const_during_execution())
- {
- my_error(ER_WRONG_ARGUMENTS, MYF(0), "RAND");
- return TRUE;
- }
/*
Allocate rand structure once: we must use thd->stmt_arena
to create rand in proper mem_root if it's a prepared statement or
@@ -2150,20 +2143,9 @@ bool Item_func_rand::fix_fields(THD *thd,Item **ref)
if (!rand && !(rand= (struct rand_struct*)
thd->stmt_arena->alloc(sizeof(*rand))))
return TRUE;
- /*
- PARAM_ITEM is returned if we're in statement prepare and consequently
- no placeholder value is set yet.
- */
- if (args[0]->type() != PARAM_ITEM)
- {
- /*
- TODO: do not do reinit 'rand' for every execute of PS/SP if
- args[0] is a constant.
- */
- uint32 tmp= (uint32) args[0]->val_int();
- randominit(rand, (uint32) (tmp*0x10001L+55555555L),
- (uint32) (tmp*0x10000001L));
- }
+
+ if (args[0]->const_item())
+ seed_random (args[0]);
}
else
{
@@ -2193,6 +2175,8 @@ void Item_func_rand::update_used_tables()
double Item_func_rand::val_real()
{
DBUG_ASSERT(fixed == 1);
+ if (arg_count && !args[0]->const_item())
+ seed_random (args[0]);
return my_rnd(rand);
}
@@ -2321,10 +2305,7 @@ String *Item_func_min_max::val_str(String *str)
longlong nr=val_int();
if (null_value)
return 0;
- if (!unsigned_flag)
- str->set(nr,&my_charset_bin);
- else
- str->set((ulonglong) nr,&my_charset_bin);
+ str->set_int(nr, unsigned_flag, &my_charset_bin);
return str;
}
case DECIMAL_RESULT:
@@ -2340,7 +2321,7 @@ String *Item_func_min_max::val_str(String *str)
double nr= val_real();
if (null_value)
return 0; /* purecov: inspected */
- str->set(nr,decimals,&my_charset_bin);
+ str->set_real(nr,decimals,&my_charset_bin);
return str;
}
case STRING_RESULT:
@@ -2560,16 +2541,16 @@ longlong Item_func_locate::val_int()
}
-void Item_func_locate::print(String *str)
+void Item_func_locate::print(String *str, enum_query_type query_type)
{
str->append(STRING_WITH_LEN("locate("));
- args[1]->print(str);
+ args[1]->print(str, query_type);
str->append(',');
- args[0]->print(str);
+ args[0]->print(str, query_type);
if (arg_count == 3)
{
str->append(',');
- args[2]->print(str);
+ args[2]->print(str, query_type);
}
str->append(')');
}
@@ -2691,7 +2672,7 @@ void Item_func_find_in_set::fix_length_and_dec()
if (args[0]->const_item() && args[1]->type() == FIELD_ITEM)
{
Field *field= ((Item_field*) args[1])->field;
- if (field->real_type() == FIELD_TYPE_SET)
+ if (field->real_type() == MYSQL_TYPE_SET)
{
String *find=args[0]->val_str(&value);
if (find)
@@ -2821,7 +2802,7 @@ udf_handler::fix_fields(THD *thd, Item_result_field *func,
uint arg_count, Item **arguments)
{
#ifndef EMBEDDED_LIBRARY // Avoid compiler warning
- char buff[STACK_BUFF_ALLOC]; // Max argument in function
+ uchar buff[STACK_BUFF_ALLOC]; // Max argument in function
#endif
DBUG_ENTER("Item_udf_func::fix_fields");
@@ -2909,6 +2890,7 @@ udf_handler::fix_fields(THD *thd, Item_result_field *func,
if (u_d->func_init)
{
+ char init_msg_buff[MYSQL_ERRMSG_SIZE];
char *to=num_buffer;
for (uint i=0; i < arg_count; i++)
{
@@ -2959,12 +2941,11 @@ udf_handler::fix_fields(THD *thd, Item_result_field *func,
}
}
}
- thd->net.last_error[0]=0;
Udf_func_init init= u_d->func_init;
- if ((error=(uchar) init(&initid, &f_args, thd->net.last_error)))
+ if ((error=(uchar) init(&initid, &f_args, init_msg_buff)))
{
my_error(ER_CANT_INITIALIZE_UDF, MYF(0),
- u_d->name.str, thd->net.last_error);
+ u_d->name.str, init_msg_buff);
free_udf(u_d);
DBUG_RETURN(TRUE);
}
@@ -3037,8 +3018,10 @@ bool udf_handler::get_arguments()
return 0;
}
-/* This returns (String*) 0 in case of NULL values */
-
+/**
+ @return
+ (String*)NULL in case of NULL values
+*/
String *udf_handler::val_str(String *str,String *save_str)
{
uchar is_null_tmp=0;
@@ -3116,7 +3099,7 @@ void Item_udf_func::cleanup()
}
-void Item_udf_func::print(String *str)
+void Item_udf_func::print(String *str, enum_query_type query_type)
{
str->append(func_name());
str->append('(');
@@ -3124,7 +3107,7 @@ void Item_udf_func::print(String *str)
{
if (i != 0)
str->append(',');
- args[i]->print_item_w_name(str);
+ args[i]->print_item_w_name(str, query_type);
}
str->append(')');
}
@@ -3146,7 +3129,7 @@ String *Item_func_udf_float::val_str(String *str)
double nr= val_real();
if (null_value)
return 0; /* purecov: inspected */
- str->set(nr,decimals,&my_charset_bin);
+ str->set_real(nr,decimals,&my_charset_bin);
return str;
}
@@ -3165,10 +3148,7 @@ String *Item_func_udf_int::val_str(String *str)
longlong nr=val_int();
if (null_value)
return 0;
- if (!unsigned_flag)
- str->set(nr,&my_charset_bin);
- else
- str->set((ulonglong) nr,&my_charset_bin);
+ str->set_int(nr, unsigned_flag, &my_charset_bin);
return str;
}
@@ -3245,10 +3225,11 @@ String *Item_func_udf_str::val_str(String *str)
}
-/*
- This has to come last in the udf_handler methods, or C for AIX
- version 6.0.0.0 fails to compile with debugging enabled. (Yes, really.)
- */
+/**
+ @note
+ This has to come last in the udf_handler methods, or C for AIX
+ version 6.0.0.0 fails to compile with debugging enabled. (Yes, really.)
+*/
udf_handler::~udf_handler()
{
@@ -3269,26 +3250,26 @@ static HASH hash_user_locks;
class User_level_lock
{
- char *key;
- uint key_length;
+ uchar *key;
+ size_t key_length;
public:
int count;
bool locked;
pthread_cond_t cond;
- pthread_t thread;
- ulong thread_id;
+ my_thread_id thread_id;
+ void set_thread(THD *thd) { thread_id= thd->thread_id; }
- User_level_lock(const char *key_arg,uint length, ulong id)
+ User_level_lock(const uchar *key_arg,uint length, ulong id)
:key_length(length),count(1),locked(1), thread_id(id)
{
- key=(char*) my_memdup((byte*) key_arg,length,MYF(0));
+ key= (uchar*) my_memdup(key_arg,length,MYF(0));
pthread_cond_init(&cond,NULL);
if (key)
{
- if (my_hash_insert(&hash_user_locks,(byte*) this))
+ if (my_hash_insert(&hash_user_locks,(uchar*) this))
{
- my_free((gptr) key,MYF(0));
+ my_free(key,MYF(0));
key=0;
}
}
@@ -3297,22 +3278,22 @@ public:
{
if (key)
{
- hash_delete(&hash_user_locks,(byte*) this);
- my_free((gptr) key,MYF(0));
+ hash_delete(&hash_user_locks,(uchar*) this);
+ my_free(key, MYF(0));
}
pthread_cond_destroy(&cond);
}
inline bool initialized() { return key != 0; }
friend void item_user_lock_release(User_level_lock *ull);
- friend char *ull_get_key(const User_level_lock *ull, uint *length,
- my_bool not_used);
+ friend uchar *ull_get_key(const User_level_lock *ull, size_t *length,
+ my_bool not_used);
};
-char *ull_get_key(const User_level_lock *ull, uint *length,
- my_bool not_used __attribute__((unused)))
+uchar *ull_get_key(const User_level_lock *ull, size_t *length,
+ my_bool not_used __attribute__((unused)))
{
- *length=(uint) ull->key_length;
- return (char*) ull->key;
+ *length= ull->key_length;
+ return ull->key;
}
@@ -3346,10 +3327,10 @@ void item_user_lock_release(User_level_lock *ull)
delete ull;
}
-/*
- Wait until we are at or past the given position in the master binlog
- on the slave
- */
+/**
+ Wait until we are at or past the given position in the master binlog
+ on the slave.
+*/
longlong Item_master_pos_wait::val_int()
{
@@ -3382,8 +3363,8 @@ void debug_sync_point(const char* lock_name, uint lock_timeout)
THD* thd=current_thd;
User_level_lock* ull;
struct timespec abstime;
- int lock_name_len;
- lock_name_len=strlen(lock_name);
+ size_t lock_name_len;
+ lock_name_len= strlen(lock_name);
pthread_mutex_lock(&LOCK_user_locks);
if (thd->ull)
@@ -3398,8 +3379,9 @@ void debug_sync_point(const char* lock_name, uint lock_timeout)
this case, we will not be waiting, but rather, just waste CPU and
memory on the whole deal
*/
- if (!(ull= ((User_level_lock*) hash_search(&hash_user_locks, lock_name,
- lock_name_len))))
+ if (!(ull= ((User_level_lock*) hash_search(&hash_user_locks,
+ (uchar*) lock_name,
+ lock_name_len))))
{
pthread_mutex_unlock(&LOCK_user_locks);
return;
@@ -3410,7 +3392,7 @@ void debug_sync_point(const char* lock_name, uint lock_timeout)
Structure is now initialized. Try to get the lock.
Set up control struct to allow others to abort locks
*/
- thd->proc_info="User lock";
+ thd_proc_info(thd, "User lock");
thd->mysys_var->current_mutex= &LOCK_user_locks;
thd->mysys_var->current_cond= &ull->cond;
@@ -3430,12 +3412,12 @@ void debug_sync_point(const char* lock_name, uint lock_timeout)
else
{
ull->locked=1;
- ull->thread=thd->real_id;
+ ull->set_thread(thd);
thd->ull=ull;
}
pthread_mutex_unlock(&LOCK_user_locks);
pthread_mutex_lock(&thd->mysys_var->mutex);
- thd->proc_info=0;
+ thd_proc_info(thd, 0);
thd->mysys_var->current_mutex= 0;
thd->mysys_var->current_cond= 0;
pthread_mutex_unlock(&thd->mysys_var->mutex);
@@ -3450,11 +3432,15 @@ void debug_sync_point(const char* lock_name, uint lock_timeout)
#endif
-/*
- Get a user level lock. If the thread has an old lock this is first released.
- Returns 1: Got lock
- Returns 0: Timeout
- Returns NULL: Error
+/**
+ Get a user level lock. If the thread has an old lock this is first released.
+
+ @retval
+ 1 : Got lock
+ @retval
+ 0 : Timeout
+ @retval
+ NULL : Error
*/
longlong Item_func_get_lock::val_int()
@@ -3497,10 +3483,11 @@ longlong Item_func_get_lock::val_int()
}
if (!(ull= ((User_level_lock *) hash_search(&hash_user_locks,
- (byte*) res->ptr(),
- res->length()))))
+ (uchar*) res->ptr(),
+ (size_t) res->length()))))
{
- ull=new User_level_lock(res->ptr(),res->length(), thd->thread_id);
+ ull= new User_level_lock((uchar*) res->ptr(), (size_t) res->length(),
+ thd->thread_id);
if (!ull || !ull->initialized())
{
delete ull;
@@ -3508,8 +3495,7 @@ longlong Item_func_get_lock::val_int()
null_value=1; // Probably out of memory
DBUG_RETURN(0);
}
- ull->thread=thd->real_id;
- ull->thread_id=thd->thread_id;
+ ull->set_thread(thd);
thd->ull=ull;
pthread_mutex_unlock(&LOCK_user_locks);
DBUG_PRINT("info", ("made new lock"));
@@ -3522,7 +3508,7 @@ longlong Item_func_get_lock::val_int()
Structure is now initialized. Try to get the lock.
Set up control struct to allow others to abort locks.
*/
- thd->proc_info="User lock";
+ thd_proc_info(thd, "User lock");
thd->mysys_var->current_mutex= &LOCK_user_locks;
thd->mysys_var->current_cond= &ull->cond;
@@ -3556,7 +3542,7 @@ longlong Item_func_get_lock::val_int()
else // We got the lock
{
ull->locked=1;
- ull->thread=thd->real_id;
+ ull->set_thread(thd);
ull->thread_id= thd->thread_id;
thd->ull=ull;
error=0;
@@ -3565,7 +3551,7 @@ longlong Item_func_get_lock::val_int()
pthread_mutex_unlock(&LOCK_user_locks);
pthread_mutex_lock(&thd->mysys_var->mutex);
- thd->proc_info=0;
+ thd_proc_info(thd, 0);
thd->mysys_var->current_mutex= 0;
thd->mysys_var->current_cond= 0;
pthread_mutex_unlock(&thd->mysys_var->mutex);
@@ -3574,12 +3560,12 @@ longlong Item_func_get_lock::val_int()
}
-/*
+/**
Release a user level lock.
- Return:
- 1 if lock released
- 0 if lock wasn't held
- (SQL) NULL if no such lock
+ @return
+ - 1 if lock released
+ - 0 if lock wasn't held
+ - (SQL) NULL if no such lock
*/
longlong Item_func_release_lock::val_int()
@@ -3601,18 +3587,18 @@ longlong Item_func_release_lock::val_int()
result=0;
pthread_mutex_lock(&LOCK_user_locks);
if (!(ull= ((User_level_lock*) hash_search(&hash_user_locks,
- (const byte*) res->ptr(),
- res->length()))))
+ (const uchar*) res->ptr(),
+ (size_t) res->length()))))
{
null_value=1;
}
else
{
- DBUG_PRINT("info", ("ull->locked=%d ull->thread=%ld thd=%ld",
+ DBUG_PRINT("info", ("ull->locked=%d ull->thread=%lu thd=%lu",
(int) ull->locked,
- (long)ull->thread,
- (long)thd->real_id));
- if (ull->locked && pthread_equal(thd->real_id,ull->thread))
+ (long)ull->thread_id,
+ (long)thd->thread_id));
+ if (ull->locked && current_thd->thread_id == ull->thread_id)
{
DBUG_PRINT("info", ("release lock"));
result=1; // Release is ok
@@ -3625,35 +3611,6 @@ longlong Item_func_release_lock::val_int()
}
-bool Item_func_last_insert_id::fix_fields(THD *thd, Item **ref)
-{
- DBUG_ASSERT(fixed == 0);
-
- if (Item_int_func::fix_fields(thd, ref))
- return TRUE;
-
- if (arg_count == 0)
- {
- if (!thd->last_insert_id_used)
- {
- /*
- As this statement calls LAST_INSERT_ID(), set
- THD::last_insert_id_used and remember first generated insert
- id of the previous statement in THD::current_insert_id.
- */
- thd->last_insert_id_used= TRUE;
- thd->last_insert_id_used_bin_log= TRUE;
- thd->current_insert_id= thd->last_insert_id;
- }
- null_value= FALSE;
- }
-
- thd->lex->uncacheable(UNCACHEABLE_SIDEEFFECT);
-
- return FALSE;
-}
-
-
longlong Item_func_last_insert_id::val_int()
{
THD *thd= current_thd;
@@ -3661,12 +3618,26 @@ longlong Item_func_last_insert_id::val_int()
if (arg_count)
{
longlong value= args[0]->val_int();
- thd->insert_id(value);
null_value= args[0]->null_value;
+ /*
+ LAST_INSERT_ID(X) must affect the client's mysql_insert_id() as
+ documented in the manual. We don't want to touch
+ first_successful_insert_id_in_cur_stmt because it would make
+ LAST_INSERT_ID(X) take precedence over an generated auto_increment
+ value for this row.
+ */
+ thd->arg_of_last_insert_id_function= TRUE;
+ thd->first_successful_insert_id_in_prev_stmt= value;
return value;
}
+ return thd->read_first_successful_insert_id_in_prev_stmt();
+}
+
- return thd->current_insert_id;
+bool Item_func_last_insert_id::fix_fields(THD *thd, Item **ref)
+{
+ thd->lex->uncacheable(UNCACHEABLE_SIDEEFFECT);
+ return Item_int_func::fix_fields(thd, ref);
}
@@ -3679,21 +3650,41 @@ longlong Item_func_benchmark::val_int()
String tmp(buff,sizeof(buff), &my_charset_bin);
my_decimal tmp_decimal;
THD *thd=current_thd;
+ ulonglong loop_count;
+
+ loop_count= (ulonglong) args[0]->val_int();
+
+ if (args[0]->null_value ||
+ (!args[0]->unsigned_flag && (((longlong) loop_count) < 0)))
+ {
+ if (!args[0]->null_value)
+ {
+ char buff[22];
+ llstr(((longlong) loop_count), buff);
+ push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_ERROR,
+ ER_WRONG_VALUE_FOR_TYPE, ER(ER_WRONG_VALUE_FOR_TYPE),
+ "count", buff, "benchmark");
+ }
- for (ulong loop=0 ; loop < loop_count && !thd->killed; loop++)
+ null_value= 1;
+ return 0;
+ }
+
+ null_value=0;
+ for (ulonglong loop=0 ; loop < loop_count && !thd->killed; loop++)
{
- switch (args[0]->result_type()) {
+ switch (args[1]->result_type()) {
case REAL_RESULT:
- (void) args[0]->val_real();
+ (void) args[1]->val_real();
break;
case INT_RESULT:
- (void) args[0]->val_int();
+ (void) args[1]->val_int();
break;
case STRING_RESULT:
- (void) args[0]->val_str(&tmp);
+ (void) args[1]->val_str(&tmp);
break;
case DECIMAL_RESULT:
- (void) args[0]->val_decimal(&tmp_decimal);
+ (void) args[1]->val_decimal(&tmp_decimal);
break;
case ROW_RESULT:
default:
@@ -3706,21 +3697,17 @@ longlong Item_func_benchmark::val_int()
}
-void Item_func_benchmark::print(String *str)
+void Item_func_benchmark::print(String *str, enum_query_type query_type)
{
str->append(STRING_WITH_LEN("benchmark("));
- char buffer[20];
- // my_charset_bin is good enough for numbers
- String st(buffer, sizeof(buffer), &my_charset_bin);
- st.set((ulonglong)loop_count, &my_charset_bin);
- str->append(st);
+ args[0]->print(str, query_type);
str->append(',');
- args[0]->print(str);
+ args[1]->print(str, query_type);
str->append(')');
}
-/* This function is just used to create tests with time gaps */
+/** This function is just used to create tests with time gaps. */
longlong Item_func_sleep::val_int()
{
@@ -3749,6 +3736,7 @@ longlong Item_func_sleep::val_int()
pthread_cond_init(&cond, NULL);
pthread_mutex_lock(&LOCK_user_locks);
+ thd_proc_info(thd, "User sleep");
thd->mysys_var->current_mutex= &LOCK_user_locks;
thd->mysys_var->current_cond= &cond;
@@ -3760,6 +3748,7 @@ longlong Item_func_sleep::val_int()
break;
error= 0;
}
+ thd_proc_info(thd, 0);
pthread_mutex_unlock(&LOCK_user_locks);
pthread_mutex_lock(&thd->mysys_var->mutex);
thd->mysys_var->current_mutex= 0;
@@ -3779,7 +3768,7 @@ static user_var_entry *get_variable(HASH *hash, LEX_STRING &name,
{
user_var_entry *entry;
- if (!(entry = (user_var_entry*) hash_search(hash, (byte*) name.str,
+ if (!(entry = (user_var_entry*) hash_search(hash, (uchar*) name.str,
name.length)) &&
create_if_not_exists)
{
@@ -3809,7 +3798,7 @@ static user_var_entry *get_variable(HASH *hash, LEX_STRING &name,
entry->used_query_id=current_thd->query_id;
entry->type=STRING_RESULT;
memcpy(entry->name.str, name.str, name.length+1);
- if (my_hash_insert(hash,(byte*) entry))
+ if (my_hash_insert(hash,(uchar*) entry))
{
my_free((char*) entry,MYF(0));
return 0;
@@ -3818,6 +3807,35 @@ static user_var_entry *get_variable(HASH *hash, LEX_STRING &name,
return entry;
}
+
+void Item_func_set_user_var::cleanup()
+{
+ Item_func::cleanup();
+ entry= NULL;
+}
+
+
+bool Item_func_set_user_var::set_entry(THD *thd, bool create_if_not_exists)
+{
+ if (entry && thd->thread_id == entry_thread_id)
+ goto end; // update entry->update_query_id for PS
+ if (!(entry= get_variable(&thd->user_vars, name, create_if_not_exists)))
+ {
+ entry_thread_id= 0;
+ return TRUE;
+ }
+ entry_thread_id= thd->thread_id;
+ /*
+ Remember the last query which updated it, this way a query can later know
+ if this variable is a constant item in the query (it is if update_query_id
+ is different from query_id).
+ */
+end:
+ entry->update_query_id= thd->query_id;
+ return FALSE;
+}
+
+
/*
When a user variable is updated (in a SET command or a query like
SELECT @a:= ).
@@ -3827,15 +3845,8 @@ bool Item_func_set_user_var::fix_fields(THD *thd, Item **ref)
{
DBUG_ASSERT(fixed == 0);
/* fix_fields will call Item_func_set_user_var::fix_length_and_dec */
- if (Item_func::fix_fields(thd, ref) ||
- !(entry= get_variable(&thd->user_vars, name, 1)))
+ if (Item_func::fix_fields(thd, ref) || set_entry(thd, TRUE))
return TRUE;
- /*
- Remember the last query which updated it, this way a query can later know
- if this variable is a constant item in the query (it is if update_query_id
- is different from query_id).
- */
- entry->update_query_id= thd->query_id;
/*
As it is wrong and confusing to associate any
character set with NULL, @a should be latin2
@@ -3871,21 +3882,41 @@ Item_func_set_user_var::fix_length_and_dec()
/*
+ Mark field in read_map
+
+ NOTES
+ This is used by filesort to register used fields in a a temporary
+ column read set or to register used fields in a view
+*/
+
+bool Item_func_set_user_var::register_field_in_read_map(uchar *arg)
+{
+ if (result_field)
+ {
+ TABLE *table= (TABLE *) arg;
+ if (result_field->table == table || !table)
+ bitmap_set_bit(result_field->table->read_set, result_field->field_index);
+ }
+ return 0;
+}
+
+
+/**
Set value to user variable.
- SYNOPSYS
- update_hash()
- entry - pointer to structure representing variable
- set_null - should we set NULL value ?
- ptr - pointer to buffer with new value
- length - length of new value
- type - type of new value
- cs - charset info for new value
- dv - derivation for new value
- unsigned_arg - indiates if a value of type INT_RESULT is unsigned
-
- RETURN VALUE
- False - success, True - failure
+ @param entry pointer to structure representing variable
+ @param set_null should we set NULL value ?
+ @param ptr pointer to buffer with new value
+ @param length length of new value
+ @param type type of new value
+ @param cs charset info for new value
+ @param dv derivation for new value
+ @param unsigned_arg indiates if a value of type INT_RESULT is unsigned
+
+ @retval
+ false success
+ @retval
+ true failure
*/
static bool
@@ -3970,7 +4001,7 @@ Item_func_set_user_var::update_hash(void *ptr, uint length,
}
-/* Get the value of a variable as a double */
+/** Get the value of a variable as a double. */
double user_var_entry::val_real(my_bool *null_value)
{
@@ -3998,7 +4029,7 @@ double user_var_entry::val_real(my_bool *null_value)
}
-/* Get the value of a variable as an integer */
+/** Get the value of a variable as an integer. */
longlong user_var_entry::val_int(my_bool *null_value) const
{
@@ -4029,7 +4060,7 @@ longlong user_var_entry::val_int(my_bool *null_value) const
}
-/* Get the value of a variable as a string */
+/** Get the value of a variable as a string. */
String *user_var_entry::val_str(my_bool *null_value, String *str,
uint decimals)
@@ -4039,7 +4070,7 @@ String *user_var_entry::val_str(my_bool *null_value, String *str,
switch (type) {
case REAL_RESULT:
- str->set(*(double*) value, decimals, &my_charset_bin);
+ str->set_real(*(double*) value, decimals, &my_charset_bin);
break;
case INT_RESULT:
if (!unsigned_flag)
@@ -4060,7 +4091,7 @@ String *user_var_entry::val_str(my_bool *null_value, String *str,
return(str);
}
-/* Get the value of a variable as a decimal */
+/** Get the value of a variable as a decimal. */
my_decimal *user_var_entry::val_decimal(my_bool *null_value, my_decimal *val)
{
@@ -4087,18 +4118,17 @@ my_decimal *user_var_entry::val_decimal(my_bool *null_value, my_decimal *val)
return(val);
}
-/*
- This functions is invoked on SET @variable or @variable:= expression.
- Evaluate (and check expression), store results.
+/**
+ This functions is invoked on SET \@variable or
+ \@variable:= expression.
- SYNOPSYS
- Item_func_set_user_var::check()
+ Evaluate (and check expression), store results.
- NOTES
+ @note
For now it always return OK. All problem with value evaluating
- will be caught by thd->net.report_error check in sql_set_variables().
+ will be caught by thd->is_error() check in sql_set_variables().
- RETURN
+ @retval
FALSE OK.
*/
@@ -4147,18 +4177,17 @@ Item_func_set_user_var::check(bool use_result_field)
}
-/*
- This functions is invoked on SET @variable or @variable:= expression.
-
- SYNOPSIS
- Item_func_set_user_var::update()
+/**
+ This functions is invoked on
+ SET \@variable or \@variable:= expression.
- NOTES
+ @note
We have to store the expression as such in the variable, independent of
the value method used by the user
- RETURN
+ @retval
0 OK
+ @retval
1 EOM Error
*/
@@ -4294,22 +4323,23 @@ bool Item_func_set_user_var::is_null_result()
}
-void Item_func_set_user_var::print(String *str)
+void Item_func_set_user_var::print(String *str, enum_query_type query_type)
{
str->append(STRING_WITH_LEN("(@"));
str->append(name.str, name.length);
str->append(STRING_WITH_LEN(":="));
- args[0]->print(str);
+ args[0]->print(str, query_type);
str->append(')');
}
-void Item_func_set_user_var::print_as_stmt(String *str)
+void Item_func_set_user_var::print_as_stmt(String *str,
+ enum_query_type query_type)
{
str->append(STRING_WITH_LEN("set @"));
str->append(name.str, name.length);
str->append(STRING_WITH_LEN(":="));
- args[0]->print(str);
+ args[0]->print(str, query_type);
str->append(')');
}
@@ -4420,11 +4450,11 @@ int Item_func_set_user_var::save_in_field(Field *field, bool no_conversions,
else if (result_type() == DECIMAL_RESULT)
{
my_decimal decimal_value;
- my_decimal *value= entry->val_decimal(&null_value, &decimal_value);
+ my_decimal *val= entry->val_decimal(&null_value, &decimal_value);
if (null_value)
return set_field_to_null(field);
field->set_notnull();
- error=field->store_decimal(value);
+ error=field->store_decimal(val);
}
else
{
@@ -4476,24 +4506,23 @@ longlong Item_func_get_user_var::val_int()
}
-/*
+/**
Get variable by name and, if necessary, put the record of variable
use into the binary log.
-
- SYNOPSIS
- get_var_with_binlog()
- thd Current thread
- name Variable name
- out_entry [out] variable structure or NULL. The pointer is set
- regardless of whether function succeeded or not.
When a user variable is invoked from an update query (INSERT, UPDATE etc),
stores this variable and its value in thd->user_var_events, so that it can be
written to the binlog (will be written just before the query is written, see
log.cc).
- RETURN
+ @param thd Current thread
+ @param name Variable name
+ @param[out] out_entry variable structure or NULL. The pointer is set
+ regardless of whether function succeeded or not.
+
+ @retval
0 OK
+ @retval
1 Failed to put appropriate record into binary log
*/
@@ -4572,7 +4601,7 @@ int get_var_with_binlog(THD *thd, enum_sql_command sql_command,
> set @a:=1;
> insert into t1 values (@a), (@a:=@a+1), (@a:=@a+1);
We have to write to binlog value @a= 1.
-
+
We allocate the user_var_event on user_var_events_alloc pool, not on
the this-statement-execution pool because in SPs user_var_event objects
may need to be valid after current [SP] statement execution pool is
@@ -4582,7 +4611,7 @@ int get_var_with_binlog(THD *thd, enum_sql_command sql_command,
if (!(user_var_event= (BINLOG_USER_VAR_EVENT *)
alloc_root(thd->user_var_events_alloc, size)))
goto err;
-
+
user_var_event->value= (char*) user_var_event +
ALIGN_SIZE(sizeof(BINLOG_USER_VAR_EVENT));
user_var_event->user_var_event= var_entry;
@@ -4602,9 +4631,9 @@ int get_var_with_binlog(THD *thd, enum_sql_command sql_command,
}
/* Mark that this variable has been used by this query */
var_entry->used_query_id= thd->query_id;
- if (insert_dynamic(&thd->user_var_events, (gptr) &user_var_event))
+ if (insert_dynamic(&thd->user_var_events, (uchar*) &user_var_event))
goto err;
-
+
*out_entry= var_entry;
return 0;
@@ -4613,7 +4642,6 @@ err:
return 1;
}
-
void Item_func_get_user_var::fix_length_and_dec()
{
THD *thd=current_thd;
@@ -4624,12 +4652,19 @@ void Item_func_get_user_var::fix_length_and_dec()
error= get_var_with_binlog(thd, thd->lex->sql_command, name, &var_entry);
+ /*
+ If the variable didn't exist it has been created as a STRING-type.
+ 'var_entry' is NULL only if there occured an error during the call to
+ get_var_with_binlog.
+ */
if (var_entry)
{
+ m_cached_result_type= var_entry->type;
unsigned_flag= var_entry->unsigned_flag;
+ max_length= var_entry->length;
collation.set(var_entry->collation);
- switch (var_entry->type) {
+ switch(m_cached_result_type) {
case REAL_RESULT:
max_length= DBL_DIG + 8;
break;
@@ -4654,6 +4689,8 @@ void Item_func_get_user_var::fix_length_and_dec()
{
collation.set(&my_charset_bin, DERIVATION_IMPLICIT);
null_value= 1;
+ m_cached_result_type= STRING_RESULT;
+ max_length= MAX_BLOB_WIDTH;
}
if (error)
@@ -4671,16 +4708,11 @@ bool Item_func_get_user_var::const_item() const
enum Item_result Item_func_get_user_var::result_type() const
{
- user_var_entry *entry;
- if (!(entry = (user_var_entry*) hash_search(&current_thd->user_vars,
- (byte*) name.str,
- name.length)))
- return STRING_RESULT;
- return entry->type;
+ return m_cached_result_type;
}
-void Item_func_get_user_var::print(String *str)
+void Item_func_get_user_var::print(String *str, enum_query_type query_type)
{
str->append(STRING_WITH_LEN("(@"));
str->append(name.str,name.length);
@@ -4778,7 +4810,7 @@ my_decimal* Item_user_var_as_out_param::val_decimal(my_decimal *decimal_buffer)
}
-void Item_user_var_as_out_param::print(String *str)
+void Item_user_var_as_out_param::print(String *str, enum_query_type query_type)
{
str->append('@');
str->append(name.str,name.length);
@@ -4789,30 +4821,408 @@ Item_func_get_system_var::
Item_func_get_system_var(sys_var *var_arg, enum_var_type var_type_arg,
LEX_STRING *component_arg, const char *name_arg,
size_t name_len_arg)
- :var(var_arg), var_type(var_type_arg), component(*component_arg)
+ :var(var_arg), var_type(var_type_arg), orig_var_type(var_type_arg),
+ component(*component_arg), cache_present(0)
{
/* set_name() will allocate the name */
set_name(name_arg, (uint) name_len_arg, system_charset_info);
}
-bool
-Item_func_get_system_var::fix_fields(THD *thd, Item **ref)
+bool Item_func_get_system_var::is_written_to_binlog()
{
- Item *item;
- DBUG_ENTER("Item_func_get_system_var::fix_fields");
+ return var->is_written_to_binlog(var_type);
+}
- /*
- Evaluate the system variable and substitute the result (a basic constant)
- instead of this item. If the variable can not be evaluated,
- the error is reported in sys_var::item().
- */
- if (!(item= var->item(thd, var_type, &component)))
- DBUG_RETURN(1); // Impossible
- item->set_name(name, 0, system_charset_info); // don't allocate a new name
- thd->change_item_tree(ref, item);
- DBUG_RETURN(0);
+void Item_func_get_system_var::fix_length_and_dec()
+{
+ maybe_null=0;
+
+ if (var->check_type(var_type))
+ {
+ if (var_type != OPT_DEFAULT)
+ {
+ my_error(ER_INCORRECT_GLOBAL_LOCAL_VAR, MYF(0),
+ var->name, var_type == OPT_GLOBAL ? "SESSION" : "GLOBAL");
+ return;
+ }
+ /* As there was no local variable, return the global value */
+ var_type= OPT_GLOBAL;
+ }
+
+ switch (var->show_type())
+ {
+ case SHOW_LONG:
+ case SHOW_INT:
+ case SHOW_HA_ROWS:
+ unsigned_flag= TRUE;
+ max_length= MY_INT64_NUM_DECIMAL_DIGITS;
+ decimals=0;
+ break;
+ case SHOW_LONGLONG:
+ unsigned_flag= FALSE;
+ max_length= MY_INT64_NUM_DECIMAL_DIGITS;
+ decimals=0;
+ break;
+ case SHOW_CHAR:
+ case SHOW_CHAR_PTR:
+ collation.set(system_charset_info, DERIVATION_SYSCONST);
+ max_length= MAX_BLOB_WIDTH;
+ decimals=NOT_FIXED_DEC;
+ break;
+ case SHOW_BOOL:
+ case SHOW_MY_BOOL:
+ unsigned_flag= FALSE;
+ max_length= 1;
+ decimals=0;
+ break;
+ case SHOW_DOUBLE:
+ unsigned_flag= FALSE;
+ decimals= 6;
+ max_length= DBL_DIG + 6;
+ break;
+ default:
+ my_error(ER_VAR_CANT_BE_READ, MYF(0), var->name);
+ break;
+ }
+}
+
+
+void Item_func_get_system_var::print(String *str, enum_query_type query_type)
+{
+ str->append(name, name_length);
+}
+
+
+enum Item_result Item_func_get_system_var::result_type() const
+{
+ switch (var->show_type())
+ {
+ case SHOW_BOOL:
+ case SHOW_MY_BOOL:
+ case SHOW_INT:
+ case SHOW_LONG:
+ case SHOW_LONGLONG:
+ case SHOW_HA_ROWS:
+ return INT_RESULT;
+ case SHOW_CHAR:
+ case SHOW_CHAR_PTR:
+ return STRING_RESULT;
+ case SHOW_DOUBLE:
+ return REAL_RESULT;
+ default:
+ my_error(ER_VAR_CANT_BE_READ, MYF(0), var->name);
+ return STRING_RESULT; // keep the compiler happy
+ }
+}
+
+
+enum_field_types Item_func_get_system_var::field_type() const
+{
+ switch (var->show_type())
+ {
+ case SHOW_BOOL:
+ case SHOW_MY_BOOL:
+ case SHOW_INT:
+ case SHOW_LONG:
+ case SHOW_LONGLONG:
+ case SHOW_HA_ROWS:
+ return MYSQL_TYPE_LONGLONG;
+ case SHOW_CHAR:
+ case SHOW_CHAR_PTR:
+ return MYSQL_TYPE_VARCHAR;
+ case SHOW_DOUBLE:
+ return MYSQL_TYPE_DOUBLE;
+ default:
+ my_error(ER_VAR_CANT_BE_READ, MYF(0), var->name);
+ return MYSQL_TYPE_VARCHAR; // keep the compiler happy
+ }
+}
+
+
+/*
+ Uses var, var_type, component, cache_present, used_query_id, thd,
+ cached_llval, null_value, cached_null_value
+*/
+#define get_sys_var_safe(type) \
+do { \
+ type value; \
+ pthread_mutex_lock(&LOCK_global_system_variables); \
+ value= *(type*) var->value_ptr(thd, var_type, &component); \
+ pthread_mutex_unlock(&LOCK_global_system_variables); \
+ cache_present |= GET_SYS_VAR_CACHE_LONG; \
+ used_query_id= thd->query_id; \
+ cached_llval= null_value ? 0 : (longlong) value; \
+ cached_null_value= null_value; \
+ return cached_llval; \
+} while (0)
+
+
+longlong Item_func_get_system_var::val_int()
+{
+ THD *thd= current_thd;
+
+ if (cache_present && thd->query_id == used_query_id)
+ {
+ if (cache_present & GET_SYS_VAR_CACHE_LONG)
+ {
+ null_value= cached_null_value;
+ return cached_llval;
+ }
+ else if (cache_present & GET_SYS_VAR_CACHE_DOUBLE)
+ {
+ null_value= cached_null_value;
+ cached_llval= (longlong) cached_dval;
+ cache_present|= GET_SYS_VAR_CACHE_LONG;
+ return cached_llval;
+ }
+ else if (cache_present & GET_SYS_VAR_CACHE_STRING)
+ {
+ null_value= cached_null_value;
+ if (!null_value)
+ cached_llval= longlong_from_string_with_check (cached_strval.charset(),
+ cached_strval.c_ptr(),
+ cached_strval.c_ptr() +
+ cached_strval.length());
+ else
+ cached_llval= 0;
+ cache_present|= GET_SYS_VAR_CACHE_LONG;
+ return cached_llval;
+ }
+ }
+
+ switch (var->show_type())
+ {
+ case SHOW_INT: get_sys_var_safe (uint);
+ case SHOW_LONG: get_sys_var_safe (ulong);
+ case SHOW_LONGLONG: get_sys_var_safe (longlong);
+ case SHOW_HA_ROWS: get_sys_var_safe (ha_rows);
+ case SHOW_BOOL: get_sys_var_safe (bool);
+ case SHOW_MY_BOOL: get_sys_var_safe (my_bool);
+ case SHOW_DOUBLE:
+ {
+ double dval= val_real();
+
+ used_query_id= thd->query_id;
+ cached_llval= (longlong) dval;
+ cache_present|= GET_SYS_VAR_CACHE_LONG;
+ return cached_llval;
+ }
+ case SHOW_CHAR:
+ case SHOW_CHAR_PTR:
+ {
+ String *str_val= val_str(NULL);
+
+ if (str_val && str_val->length())
+ cached_llval= longlong_from_string_with_check (system_charset_info,
+ str_val->c_ptr(),
+ str_val->c_ptr() +
+ str_val->length());
+ else
+ {
+ null_value= TRUE;
+ cached_llval= 0;
+ }
+
+ cache_present|= GET_SYS_VAR_CACHE_LONG;
+ return cached_llval;
+ }
+
+ default:
+ my_error(ER_VAR_CANT_BE_READ, MYF(0), var->name);
+ return 0; // keep the compiler happy
+ }
+}
+
+
+String* Item_func_get_system_var::val_str(String* str)
+{
+ THD *thd= current_thd;
+
+ if (cache_present && thd->query_id == used_query_id)
+ {
+ if (cache_present & GET_SYS_VAR_CACHE_STRING)
+ {
+ null_value= cached_null_value;
+ return null_value ? NULL : &cached_strval;
+ }
+ else if (cache_present & GET_SYS_VAR_CACHE_LONG)
+ {
+ null_value= cached_null_value;
+ if (!null_value)
+ cached_strval.set (cached_llval, collation.collation);
+ cache_present|= GET_SYS_VAR_CACHE_STRING;
+ return null_value ? NULL : &cached_strval;
+ }
+ else if (cache_present & GET_SYS_VAR_CACHE_DOUBLE)
+ {
+ null_value= cached_null_value;
+ if (!null_value)
+ cached_strval.set_real (cached_dval, decimals, collation.collation);
+ cache_present|= GET_SYS_VAR_CACHE_STRING;
+ return null_value ? NULL : &cached_strval;
+ }
+ }
+
+ str= &cached_strval;
+ switch (var->show_type())
+ {
+ case SHOW_CHAR:
+ case SHOW_CHAR_PTR:
+ {
+ 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);
+ if (cptr)
+ {
+ if (str->copy(cptr, strlen(cptr), collation.collation))
+ {
+ null_value= TRUE;
+ str= NULL;
+ }
+ }
+ else
+ {
+ null_value= TRUE;
+ str= NULL;
+ }
+ pthread_mutex_unlock(&LOCK_global_system_variables);
+ break;
+ }
+
+ case SHOW_INT:
+ case SHOW_LONG:
+ case SHOW_LONGLONG:
+ case SHOW_HA_ROWS:
+ case SHOW_BOOL:
+ case SHOW_MY_BOOL:
+ str->set (val_int(), collation.collation);
+ break;
+ case SHOW_DOUBLE:
+ str->set_real (val_real(), decimals, collation.collation);
+ break;
+
+ default:
+ my_error(ER_VAR_CANT_BE_READ, MYF(0), var->name);
+ str= NULL;
+ break;
+ }
+
+ cache_present|= GET_SYS_VAR_CACHE_STRING;
+ used_query_id= thd->query_id;
+ cached_null_value= null_value;
+ return str;
+}
+
+
+double Item_func_get_system_var::val_real()
+{
+ THD *thd= current_thd;
+
+ if (cache_present && thd->query_id == used_query_id)
+ {
+ if (cache_present & GET_SYS_VAR_CACHE_DOUBLE)
+ {
+ null_value= cached_null_value;
+ return cached_dval;
+ }
+ else if (cache_present & GET_SYS_VAR_CACHE_LONG)
+ {
+ null_value= cached_null_value;
+ cached_dval= (double)cached_llval;
+ cache_present|= GET_SYS_VAR_CACHE_DOUBLE;
+ return cached_dval;
+ }
+ else if (cache_present & GET_SYS_VAR_CACHE_STRING)
+ {
+ null_value= cached_null_value;
+ if (!null_value)
+ cached_dval= double_from_string_with_check (cached_strval.charset(),
+ cached_strval.c_ptr(),
+ cached_strval.c_ptr() +
+ cached_strval.length());
+ else
+ cached_dval= 0;
+ cache_present|= GET_SYS_VAR_CACHE_DOUBLE;
+ return cached_dval;
+ }
+ }
+
+ switch (var->show_type())
+ {
+ case SHOW_DOUBLE:
+ pthread_mutex_lock(&LOCK_global_system_variables);
+ cached_dval= *(double*) var->value_ptr(thd, var_type, &component);
+ pthread_mutex_unlock(&LOCK_global_system_variables);
+ used_query_id= thd->query_id;
+ cached_null_value= null_value;
+ if (null_value)
+ cached_dval= 0;
+ cache_present|= GET_SYS_VAR_CACHE_DOUBLE;
+ return cached_dval;
+ case SHOW_CHAR:
+ case SHOW_CHAR_PTR:
+ {
+ char *cptr;
+
+ pthread_mutex_lock(&LOCK_global_system_variables);
+ cptr= var->show_type() == SHOW_CHAR ?
+ (char*) var->value_ptr(thd, var_type, &component) :
+ *(char**) var->value_ptr(thd, var_type, &component);
+ if (cptr)
+ cached_dval= double_from_string_with_check (system_charset_info,
+ cptr, cptr + strlen (cptr));
+ else
+ {
+ null_value= TRUE;
+ cached_dval= 0;
+ }
+ pthread_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_HA_ROWS:
+ case SHOW_BOOL:
+ case SHOW_MY_BOOL:
+ cached_dval= (double) val_int();
+ cache_present|= GET_SYS_VAR_CACHE_DOUBLE;
+ used_query_id= thd->query_id;
+ cached_null_value= null_value;
+ return cached_dval;
+ default:
+ my_error(ER_VAR_CANT_BE_READ, MYF(0), var->name);
+ return 0;
+ }
+}
+
+
+bool Item_func_get_system_var::eq(const Item *item, bool binary_cmp) const
+{
+ /* Assume we don't have rtti */
+ if (this == item)
+ return 1; // Same item is same.
+ /* Check if other type is also a get_user_var() object */
+ if (item->type() != FUNC_ITEM ||
+ ((Item_func*) item)->functype() != functype())
+ return 0;
+ Item_func_get_system_var *other=(Item_func_get_system_var*) item;
+ return (var == other->var && var_type == other->var_type);
+}
+
+
+void Item_func_get_system_var::cleanup()
+{
+ Item_func::cleanup();
+ cache_present= 0;
+ var_type= orig_var_type;
+ cached_strval.free();
}
@@ -4976,44 +5386,13 @@ bool Item_func_match::fix_fields(THD *thd, Item **ref)
my_error(ER_WRONG_ARGUMENTS,MYF(0),"MATCH");
return TRUE;
}
- /*
- With prepared statements Item_func_match::fix_fields is called twice.
- When it is called first time we have original item tree here and add
- conversion layer for character sets that do not have ctype array a few
- lines below. When it is called second time, we already have conversion
- layer in item tree.
- */
- table= (item->type() == Item::FIELD_ITEM) ?
- ((Item_field *)item)->field->table :
- ((Item_field *)((Item_func_conv *)item)->key_item())->field->table;
- if (!(table->file->table_flags() & HA_CAN_FULLTEXT))
+ 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;
- /* A workaround for ucs2 character set */
- if (!args[1]->collation.collation->ctype)
- {
- CHARSET_INFO *compatible_cs=
- get_compatible_charset_with_ctype(args[1]->collation.collation);
- bool rc= 1;
- if (compatible_cs)
- {
- Item_string *conv_item= new Item_string("", 0, compatible_cs,
- DERIVATION_EXPLICIT);
- item= args[0];
- args[0]= conv_item;
- rc= agg_item_charsets(cmp_collation, func_name(), args, arg_count,
- MY_COLL_ALLOW_SUPERSET_CONV |
- MY_COLL_ALLOW_COERCIBLE_CONV |
- MY_COLL_DISALLOW_NONE, 1);
- args[0]= item;
- }
- else
- my_error(ER_WRONG_ARGUMENTS, MYF(0), "MATCH");
- return rc;
- }
return agg_arg_collations_for_comparison(cmp_collation,
args+1, arg_count-1, 0);
}
@@ -5144,19 +5523,18 @@ double Item_func_match::val_real()
if ((null_value= (a == 0)) || !a->length())
DBUG_RETURN(0);
DBUG_RETURN(ft_handler->please->find_relevance(ft_handler,
- (byte *)a->ptr(), a->length()));
+ (uchar *)a->ptr(), a->length()));
}
- else
- DBUG_RETURN(ft_handler->please->find_relevance(ft_handler,
- table->record[0], 0));
+ DBUG_RETURN(ft_handler->please->find_relevance(ft_handler,
+ table->record[0], 0));
}
-void Item_func_match::print(String *str)
+void Item_func_match::print(String *str, enum_query_type query_type)
{
str->append(STRING_WITH_LEN("(match "));
- print_args(str, 1);
+ print_args(str, 1, query_type);
str->append(STRING_WITH_LEN(" against ("));
- args[0]->print(str);
+ args[0]->print(str, query_type);
if (flags & FT_BOOL)
str->append(STRING_WITH_LEN(" in boolean mode"));
else if (flags & FT_EXPAND)
@@ -5179,22 +5557,20 @@ longlong Item_func_bit_xor::val_int()
System variables
****************************************************************************/
-/*
- Return value of an system variable base[.name] as a constant item
+/**
+ Return value of an system variable base[.name] as a constant item.
- SYNOPSIS
- get_system_var()
- thd Thread handler
- var_type global / session
- name Name of base or system variable
- component Component.
+ @param thd Thread handler
+ @param var_type global / session
+ @param name Name of base or system variable
+ @param component Component.
- NOTES
+ @note
If component.str = 0 then the variable name is in 'name'
- RETURN
- 0 error
- # constant item
+ @return
+ - 0 : error
+ - # : constant item
*/
@@ -5215,7 +5591,7 @@ Item *get_system_var(THD *thd, enum_var_type var_type, LEX_STRING name,
component_name= &component; // Empty string
}
- if (!(var= find_sys_var(base_name->str, base_name->length)))
+ if (!(var= find_sys_var(thd, base_name->str, base_name->length)))
return 0;
if (component.str)
{
@@ -5234,16 +5610,15 @@ Item *get_system_var(THD *thd, enum_var_type var_type, LEX_STRING name,
}
-/*
+/**
Check a user level lock.
- SYNOPSIS:
- val_int()
+ Sets null_value=TRUE on error.
- RETURN VALUES
+ @retval
1 Available
- 0 Already taken
- NULL Error
+ @retval
+ 0 Already taken, or error
*/
longlong Item_func_is_free_lock::val_int()
@@ -5260,8 +5635,8 @@ longlong Item_func_is_free_lock::val_int()
}
pthread_mutex_lock(&LOCK_user_locks);
- ull= (User_level_lock *) hash_search(&hash_user_locks, (byte*) res->ptr(),
- res->length());
+ ull= (User_level_lock *) hash_search(&hash_user_locks, (uchar*) res->ptr(),
+ (size_t) res->length());
pthread_mutex_unlock(&LOCK_user_locks);
if (!ull || !ull->locked)
return 1;
@@ -5279,8 +5654,8 @@ longlong Item_func_is_used_lock::val_int()
return 0;
pthread_mutex_lock(&LOCK_user_locks);
- ull= (User_level_lock *) hash_search(&hash_user_locks, (byte*) res->ptr(),
- res->length());
+ ull= (User_level_lock *) hash_search(&hash_user_locks, (uchar*) res->ptr(),
+ (size_t) res->length());
pthread_mutex_unlock(&LOCK_user_locks);
if (!ull || !ull->locked)
return 0;
@@ -5306,7 +5681,8 @@ Item_func_sp::Item_func_sp(Name_resolution_context *context_arg, sp_name *name)
{
maybe_null= 1;
m_name->init_qname(current_thd);
- dummy_table= (TABLE*) sql_calloc(sizeof(TABLE));
+ dummy_table= (TABLE*) sql_calloc(sizeof(TABLE)+ sizeof(TABLE_SHARE));
+ dummy_table->s= (TABLE_SHARE*) (dummy_table+1);
}
@@ -5316,7 +5692,8 @@ Item_func_sp::Item_func_sp(Name_resolution_context *context_arg,
{
maybe_null= 1;
m_name->init_qname(current_thd);
- dummy_table= (TABLE*) sql_calloc(sizeof(TABLE));
+ dummy_table= (TABLE*) sql_calloc(sizeof(TABLE)+ sizeof(TABLE_SHARE));
+ dummy_table->s= (TABLE_SHARE*) (dummy_table+1);
}
@@ -5329,7 +5706,7 @@ Item_func_sp::cleanup()
sp_result_field= NULL;
}
m_sp= NULL;
- dummy_table->s= NULL;
+ dummy_table->alias= NULL;
Item_func::cleanup();
}
@@ -5377,17 +5754,16 @@ Item_func_sp::func_name() const
@retval TRUE is returned on an error
@retval FALSE is returned on success.
*/
+
bool
Item_func_sp::init_result_field(THD *thd)
{
- DBUG_ENTER("Item_func_sp::init_result_field");
-
- char *empty_name= (char *) "";
+ LEX_STRING empty_name= { C_STRING_WITH_LEN("") };
TABLE_SHARE *share;
+ DBUG_ENTER("Item_func_sp::init_result_field");
DBUG_ASSERT(m_sp == NULL);
DBUG_ASSERT(sp_result_field == NULL);
- DBUG_ASSERT(dummy_table->s == NULL);
if (!(m_sp= sp_find_routine(thd, TYPE_ENUM_FUNCTION, m_name,
&thd->sp_func_cache, TRUE)))
@@ -5402,38 +5778,43 @@ Item_func_sp::init_result_field(THD *thd)
Below we "create" a dummy table by initializing
the needed pointers.
*/
- dummy_table->s= share= &dummy_table->share_not_to_be_used;
- dummy_table->alias = empty_name;
+
+ share= dummy_table->s;
+ dummy_table->alias = "";
dummy_table->maybe_null = maybe_null;
dummy_table->in_use= thd;
dummy_table->copy_blobs= TRUE;
share->table_cache_key = empty_name;
share->table_name = empty_name;
- if (!(sp_result_field= m_sp->create_result_field(max_length, name, dummy_table)))
+ if (!(sp_result_field= m_sp->create_result_field(max_length, name,
+ dummy_table)))
{
DBUG_RETURN(TRUE);
}
if (sp_result_field->pack_length() > sizeof(result_buf))
{
- sp_result_field->move_field(sql_alloc(sp_result_field->pack_length()));
- } else {
- sp_result_field->move_field(result_buf);
+ void *tmp;
+ if (!(tmp= sql_alloc(sp_result_field->pack_length())))
+ DBUG_RETURN(TRUE);
+ sp_result_field->move_field((uchar*) tmp);
}
+ else
+ sp_result_field->move_field(result_buf);
sp_result_field->null_ptr= (uchar *) &null_value;
sp_result_field->null_bit= 1;
-
-
DBUG_RETURN(FALSE);
}
+
/**
@brief Initialize local members with values from the Field interface.
@note called from Item::fix_fields.
*/
+
void Item_func_sp::fix_length_and_dec()
{
DBUG_ENTER("Item_func_sp::fix_length_and_dec");
@@ -5448,6 +5829,7 @@ void Item_func_sp::fix_length_and_dec()
DBUG_VOID_RETURN;
}
+
/**
@brief Execute function & store value in field.
@@ -5461,12 +5843,6 @@ Item_func_sp::execute()
{
THD *thd= current_thd;
- /*
- Get field in virtual tmp table to store result. Create the field if
- invoked first time.
- */
-
-
/* Execute function and store the return value in the field. */
if (execute_impl(thd))
@@ -5518,6 +5894,18 @@ Item_func_sp::execute_impl(THD *thd)
goto error;
/*
+ Throw an error if a non-deterministic function is called while
+ statement-based replication (SBR) is active.
+ */
+ if (!m_sp->m_chistics->detistic && !trust_function_creators &&
+ (mysql_bin_log.is_open() &&
+ thd->variables.binlog_format == BINLOG_FORMAT_STMT))
+ {
+ my_error(ER_BINLOG_ROW_RBR_TO_SBR, MYF(0));
+ goto error;
+ }
+
+ /*
Disable the binlogging if this is not a SELECT statement. If this is a
SELECT, leave binlogging on, so execute_function() code writes the
function call into binlog.
@@ -5559,7 +5947,7 @@ Item_result
Item_func_sp::result_type() const
{
DBUG_ENTER("Item_func_sp::result_type");
- DBUG_PRINT("info", ("m_sp = %p", m_sp));
+ DBUG_PRINT("info", ("m_sp = %p", (void *) m_sp));
DBUG_ASSERT(sp_result_field);
DBUG_RETURN(sp_result_field->result_type());
}
@@ -5652,7 +6040,7 @@ Item_func_sp::fix_fields(THD *thd, Item **ref)
Security_context *save_secutiry_ctx;
res= set_routine_security_ctx(thd, m_sp, false, &save_secutiry_ctx);
if (!res)
- sp_restore_security_context(thd, save_secutiry_ctx);
+ m_sp->m_security_ctx.restore_security_context(thd, save_secutiry_ctx);
#endif /* ! NO_EMBEDDED_ACCESS_CHECKS */
}
@@ -5677,3 +6065,40 @@ void Item_func_sp::update_used_tables()
const_item_cache= FALSE;
}
}
+
+
+/*
+ uuid_short handling.
+
+ The short uuid is defined as a longlong that contains the following bytes:
+
+ Bytes Comment
+ 1 Server_id & 255
+ 4 Startup time of server in seconds
+ 3 Incrementor
+
+ This means that an uuid is guaranteed to be unique
+ even in a replication environment if the following holds:
+
+ - The last byte of the server id is unique
+ - If you between two shutdown of the server don't get more than
+ an average of 2^24 = 16M calls to uuid_short() per second.
+*/
+
+ulonglong uuid_value;
+
+void uuid_short_init()
+{
+ uuid_value= ((((ulonglong) server_id) << 56) +
+ (((ulonglong) server_start_time) << 24));
+}
+
+
+longlong Item_func_uuid_short::val_int()
+{
+ ulonglong val;
+ pthread_mutex_lock(&LOCK_uuid_generator);
+ val= uuid_value++;
+ pthread_mutex_unlock(&LOCK_uuid_generator);
+ return (longlong) val;
+}
diff --git a/sql/item_func.h b/sql/item_func.h
index 7e15a536cf2..d23d821baf6 100644
--- a/sql/item_func.h
+++ b/sql/item_func.h
@@ -1,4 +1,4 @@
-/* Copyright (C) 2000-2006 MySQL AB
+/* Copyright 2000-2008 MySQL AB, 2008 Sun Microsystems, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -55,7 +55,7 @@ public:
NOW_FUNC, TRIG_COND_FUNC,
SUSERVAR_FUNC, GUSERVAR_FUNC, COLLATE_FUNC,
EXTRACT_FUNC, CHAR_TYPECAST_FUNC, FUNC_SP, UDF_FUNC,
- NEG_FUNC };
+ NEG_FUNC, GSYSVAR_FUNC };
enum optimize_type { OPTIMIZE_NONE,OPTIMIZE_KEY,OPTIMIZE_OP, OPTIMIZE_NULL,
OPTIMIZE_EQUAL };
enum Type type() const { return FUNC_ITEM; }
@@ -141,9 +141,9 @@ public:
inline uint argument_count() const { return arg_count; }
inline void remove_arguments() { arg_count=0; }
void split_sum_func(THD *thd, Item **ref_pointer_array, List<Item> &fields);
- void print(String *str);
- void print_op(String *str);
- void print_args(String *str, uint from);
+ virtual void print(String *str, enum_query_type query_type);
+ void print_op(String *str, enum_query_type query_type);
+ void print_args(String *str, uint from, enum_query_type query_type);
virtual void fix_num_length_and_dec();
void count_only_length();
void count_real_length();
@@ -185,14 +185,21 @@ public:
{
return agg_item_charsets(c, func_name(), items, nitems, flags, item_sep);
}
- bool walk(Item_processor processor, byte *arg);
- Item *transform(Item_transformer transformer, byte *arg);
- Item* compile(Item_analyzer analyzer, byte **arg_p,
- Item_transformer transformer, byte *arg_t);
+ 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,
+ Item_transformer transformer, uchar *arg_t);
void traverse_cond(Cond_traverser traverser,
void * arg, traverse_order order);
- bool is_expensive_processor(byte *arg);
+ bool is_expensive_processor(uchar *arg);
virtual bool is_expensive() { return 0; }
+ inline double fix_result(double value)
+ {
+ if (isfinite(value))
+ return value;
+ null_value=1;
+ return 0.0;
+ }
};
@@ -294,7 +301,12 @@ class Item_num_op :public Item_func_numhybrid
public:
Item_num_op(Item *a,Item *b) :Item_func_numhybrid(a, b) {}
virtual void result_precision()= 0;
- void print(String *str) { print_op(str); }
+
+ virtual inline void print(String *str, enum_query_type query_type)
+ {
+ print_op(str, query_type);
+ }
+
void find_num_type();
String *str_op(String *str) { DBUG_ASSERT(0); return 0; }
};
@@ -339,9 +351,8 @@ public:
longlong val_int_from_str(int *error);
void fix_length_and_dec()
{ max_length=args[0]->max_length; unsigned_flag=0; }
- void print(String *str);
+ virtual void print(String *str, enum_query_type query_type);
uint decimal_precision() const { return args[0]->decimal_precision(); }
-
};
@@ -356,7 +367,7 @@ public:
unsigned_flag=1;
}
longlong val_int();
- void print(String *str);
+ virtual void print(String *str, enum_query_type query_type);
};
@@ -377,7 +388,7 @@ public:
enum_field_types field_type() const { return MYSQL_TYPE_NEWDECIMAL; }
void fix_length_and_dec() {};
const char *func_name() const { return "decimal_typecast"; }
- void print(String *);
+ virtual void print(String *str, enum_query_type query_type);
};
@@ -386,6 +397,7 @@ class Item_func_additive_op :public Item_num_op
public:
Item_func_additive_op(Item *a,Item *b) :Item_num_op(a,b) {}
void result_precision();
+ bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
};
@@ -420,6 +432,7 @@ public:
double real_op();
my_decimal *decimal_op(my_decimal *);
void result_precision();
+ bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
};
@@ -445,7 +458,13 @@ public:
longlong val_int();
const char *func_name() const { return "DIV"; }
void fix_length_and_dec();
- void print(String *str) { print_op(str); }
+
+ virtual inline void print(String *str, enum_query_type query_type)
+ {
+ print_op(str, query_type);
+ }
+
+ bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
};
@@ -459,6 +478,7 @@ public:
const char *func_name() const { return "%"; }
void result_precision();
void fix_length_and_dec();
+ bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
};
@@ -474,6 +494,7 @@ public:
void fix_length_and_dec();
void fix_num_length_and_dec();
uint decimal_precision() const { return args[0]->decimal_precision(); }
+ bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
};
@@ -486,6 +507,7 @@ public:
my_decimal *decimal_op(my_decimal *);
const char *func_name() const { return "abs"; }
void fix_length_and_dec();
+ bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
};
// A class to handle logarithmic and trigonometric functions
@@ -500,18 +522,6 @@ class Item_dec_func :public Item_real_func
decimals=NOT_FIXED_DEC; max_length=float_length(decimals);
maybe_null=1;
}
- inline double fix_result(double value)
- {
-#ifndef HAVE_FINITE
- return value;
-#else
- /* The following should be safe, even if we compare doubles */
- if (finite(value) && value != POSTFIX_ERROR)
- return value;
- null_value=1;
- return 0.0;
-#endif
- }
};
class Item_func_exp :public Item_dec_func
@@ -652,6 +662,7 @@ public:
longlong int_op();
double real_op();
my_decimal *decimal_op(my_decimal *);
+ bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
};
@@ -663,6 +674,7 @@ public:
longlong int_op();
double real_op();
my_decimal *decimal_op(my_decimal *);
+ bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
};
/* This handles round and truncate */
@@ -692,6 +704,8 @@ public:
bool const_item() const { return 0; }
void update_used_tables();
bool fix_fields(THD *thd, Item **ref);
+private:
+ void seed_random (Item * val);
};
@@ -840,7 +854,7 @@ public:
const char *func_name() const { return "locate"; }
longlong val_int();
void fix_length_and_dec();
- void print(String *str);
+ virtual void print(String *str, enum_query_type query_type);
};
@@ -897,7 +911,11 @@ public:
Item_func_bit(Item *a, Item *b) :Item_int_func(a, b) {}
Item_func_bit(Item *a) :Item_int_func(a) {}
void fix_length_and_dec() { unsigned_flag= 1; }
- void print(String *str) { print_op(str); }
+
+ virtual inline void print(String *str, enum_query_type query_type)
+ {
+ print_op(str, query_type);
+ }
};
class Item_func_bit_or :public Item_func_bit
@@ -947,7 +965,11 @@ public:
Item_func_bit_neg(Item *a) :Item_func_bit(a) {}
longlong val_int();
const char *func_name() const { return "~"; }
- void print(String *str) { Item_func::print(str); }
+
+ virtual inline void print(String *str, enum_query_type query_type)
+ {
+ Item_func::print(str, query_type);
+ }
};
@@ -969,15 +991,14 @@ public:
class Item_func_benchmark :public Item_int_func
{
- ulong loop_count;
public:
- Item_func_benchmark(ulong loop_count_arg,Item *expr)
- :Item_int_func(expr), loop_count(loop_count_arg)
+ Item_func_benchmark(Item *count_expr, Item *expr)
+ :Item_int_func(count_expr, expr)
{}
longlong val_int();
const char *func_name() const { return "benchmark"; }
void fix_length_and_dec() { max_length=1; maybe_null=0; }
- void print(String *str);
+ virtual void print(String *str, enum_query_type query_type);
};
@@ -1074,7 +1095,7 @@ public:
Item_result result_type () const { return udf.result_type(); }
table_map not_null_tables() const { return 0; }
bool is_expensive() { return 1; }
- void print(String *str);
+ virtual void print(String *str, enum_query_type query_type);
};
@@ -1276,6 +1297,17 @@ class Item_func_set_user_var :public Item_func
{
enum Item_result cached_result_type;
user_var_entry *entry;
+ /*
+ The entry_thread_id variable is used:
+ 1) to skip unnecessary updates of the entry field (see above);
+ 2) to reset the entry field that was initialized in the other thread
+ (for example, an item tree of a trigger that updates user variables
+ may be shared between several connections, and the entry_thread_id field
+ prevents updates of one connection user variables from a concurrent
+ connection calling the same trigger that initially updated some
+ user variable it the first connection context).
+ */
+ my_thread_id entry_thread_id;
char buffer[MAX_FIELD_WIDTH];
String value;
my_decimal decimal_buff;
@@ -1291,7 +1323,8 @@ class Item_func_set_user_var :public Item_func
public:
LEX_STRING name; // keep it public
Item_func_set_user_var(LEX_STRING a,Item *b)
- :Item_func(b), cached_result_type(INT_RESULT), name(a)
+ :Item_func(b), cached_result_type(INT_RESULT),
+ entry(NULL), entry_thread_id(0), name(a)
{}
enum Functype functype() const { return SUSERVAR_FUNC; }
double val_real();
@@ -1312,8 +1345,8 @@ public:
enum Item_result result_type () const { return cached_result_type; }
bool fix_fields(THD *thd, Item **ref);
void fix_length_and_dec();
- void print(String *str);
- void print_as_stmt(String *str);
+ virtual void print(String *str, enum_query_type query_type);
+ void print_as_stmt(String *str, enum_query_type query_type);
const char *func_name() const { return "set_user_var"; }
int save_in_field(Field *field, bool no_conversions,
bool can_use_result_field);
@@ -1322,6 +1355,9 @@ public:
return save_in_field(field, no_conversions, 1);
}
void save_org_in_field(Field *field) { (void)save_in_field(field, 1, 0); }
+ bool register_field_in_read_map(uchar *arg);
+ bool set_entry(THD *thd, bool create_if_not_exists);
+ void cleanup();
};
@@ -1329,11 +1365,12 @@ class Item_func_get_user_var :public Item_func,
private Settable_routine_parameter
{
user_var_entry *var_entry;
+ Item_result m_cached_result_type;
public:
LEX_STRING name; // keep it public
Item_func_get_user_var(LEX_STRING a):
- Item_func(), name(a) {}
+ Item_func(), m_cached_result_type(STRING_RESULT), name(a) {}
enum Functype functype() const { return GUSERVAR_FUNC; }
LEX_STRING get_name() { return name; }
double val_real();
@@ -1341,19 +1378,17 @@ public:
my_decimal *val_decimal(my_decimal*);
String *val_str(String* str);
void fix_length_and_dec();
- void print(String *str);
+ virtual void print(String *str, enum_query_type query_type);
enum Item_result result_type() const;
/*
We must always return variables as strings to guard against selects of type
select @t1:=1,@t1,@t:="hello",@t from foo where (@t1:= t2.b)
*/
- enum_field_types field_type() const { return MYSQL_TYPE_VARCHAR; }
const char *func_name() const { return "get_user_var"; }
bool const_item() const;
table_map used_tables() const
{ return const_item() ? 0 : RAND_TABLE_BIT; }
bool eq(const Item *item, bool binary_cmp) const;
-
private:
bool set_value(THD *thd, sp_rcontext *ctx, Item **it);
@@ -1388,7 +1423,7 @@ public:
my_decimal *val_decimal(my_decimal *decimal_buffer);
/* fix_fields() binds variable name with its entry structure */
bool fix_fields(THD *thd, Item **ref);
- void print(String *str);
+ virtual void print(String *str, enum_query_type query_type);
void set_null_value(CHARSET_INFO* cs);
void set_value(const char *str, uint length, CHARSET_INFO* cs);
};
@@ -1396,36 +1431,60 @@ public:
/* A system variable */
+#define GET_SYS_VAR_CACHE_LONG 1
+#define GET_SYS_VAR_CACHE_DOUBLE 2
+#define GET_SYS_VAR_CACHE_STRING 4
+
class Item_func_get_system_var :public Item_func
{
sys_var *var;
- enum_var_type var_type;
+ enum_var_type var_type, orig_var_type;
LEX_STRING component;
+ longlong cached_llval;
+ double cached_dval;
+ String cached_strval;
+ my_bool cached_null_value;
+ query_id_t used_query_id;
+ uchar cache_present;
+
public:
Item_func_get_system_var(sys_var *var_arg, enum_var_type var_type_arg,
LEX_STRING *component_arg, const char *name_arg,
size_t name_len_arg);
- bool fix_fields(THD *thd, Item **ref);
- /*
- Stubs for pure virtual methods. Should never be called: this
- item is always substituted with a constant in fix_fields().
- */
- double val_real() { DBUG_ASSERT(0); return 0.0; }
- longlong val_int() { DBUG_ASSERT(0); return 0; }
- String* val_str(String*) { DBUG_ASSERT(0); return 0; }
- void fix_length_and_dec() { DBUG_ASSERT(0); }
+ enum Functype functype() const { return GSYSVAR_FUNC; }
+ void fix_length_and_dec();
+ void print(String *str, enum_query_type query_type);
+ bool const_item() const { return true; }
+ table_map used_tables() const { return 0; }
+ enum Item_result result_type() const;
+ enum_field_types field_type() const;
+ double val_real();
+ longlong val_int();
+ String* val_str(String*);
/* TODO: fix to support views */
const char *func_name() const { return "get_system_var"; }
+ /**
+ Indicates whether this system variable is written to the binlog or not.
+
+ Variables are written to the binlog as part of "status_vars" in
+ Query_log_event, as an Intvar_log_event, or a Rand_log_event.
+
+ @return true if the variable is written to the binlog, false otherwise.
+ */
+ bool is_written_to_binlog();
+ bool eq(const Item *item, bool binary_cmp) const;
+
+ void cleanup();
};
class Item_func_inet_aton : public Item_int_func
{
public:
- Item_func_inet_aton(Item *a) :Item_int_func(a) {}
- longlong val_int();
- const char *func_name() const { return "inet_aton"; }
- void fix_length_and_dec() { decimals = 0; max_length = 21; maybe_null=1;unsigned_flag=1;}
+ Item_func_inet_aton(Item *a) :Item_int_func(a) {}
+ longlong val_int();
+ const char *func_name() const { return "inet_aton"; }
+ void fix_length_and_dec() { decimals= 0; max_length= 21; maybe_null= 1; unsigned_flag= 1;}
};
@@ -1466,7 +1525,7 @@ public:
/* The following should be safe, even if we compare doubles */
longlong val_int() { DBUG_ASSERT(fixed == 1); return val_real() != 0.0; }
double val_real();
- void print(String *str);
+ virtual void print(String *str, enum_query_type query_type);
bool fix_index();
void init_search(bool no_order);
@@ -1538,7 +1597,7 @@ private:
sp_name *m_name;
mutable sp_head *m_sp;
TABLE *dummy_table;
- char result_buf[64];
+ uchar result_buf[64];
/*
The result field of the concrete stored function.
*/
@@ -1612,7 +1671,7 @@ public:
return str;
}
- virtual bool change_context_processor(byte *cntx)
+ virtual bool change_context_processor(uchar *cntx)
{ context= (Name_resolution_context *)cntx; return FALSE; }
bool sp_check_access(THD * thd);
@@ -1621,6 +1680,11 @@ public:
bool fix_fields(THD *thd, Item **ref);
void fix_length_and_dec(void);
bool is_expensive() { return 1; }
+
+ inline Field *get_sp_result_field()
+ {
+ return sp_result_field;
+ }
};
@@ -1632,3 +1696,18 @@ public:
const char *func_name() const { return "found_rows"; }
void fix_length_and_dec() { decimals= 0; maybe_null=0; }
};
+
+
+void uuid_short_init();
+
+class Item_func_uuid_short :public Item_int_func
+{
+public:
+ Item_func_uuid_short() :Item_int_func() {}
+ const char *func_name() const { return "uuid_short"; }
+ longlong val_int();
+ void fix_length_and_dec()
+ { max_length= 21; unsigned_flag=1; }
+ bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
+};
+
diff --git a/sql/item_geofunc.cc b/sql/item_geofunc.cc
index d088f68fc0c..ac1b7738a27 100644
--- a/sql/item_geofunc.cc
+++ b/sql/item_geofunc.cc
@@ -14,7 +14,12 @@
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
-/* This file defines all spatial functions */
+/**
+ @file
+
+ @brief
+ This file defines all spatial functions
+*/
#ifdef USE_PRAGMA_IMPLEMENTATION
#pragma implementation // gcc: Class implementation
@@ -26,8 +31,11 @@
Field *Item_geometry_func::tmp_table_field(TABLE *t_arg)
{
- return new Field_geom(max_length, maybe_null, name, t_arg,
- get_geometry_type());
+ Field *result;
+ if ((result= new Field_geom(max_length, maybe_null, name, t_arg->s,
+ get_geometry_type())))
+ result->init(t_arg);
+ return result;
}
void Item_geometry_func::fix_length_and_dec()
@@ -353,7 +361,7 @@ String *Item_func_point::val_str(String *str)
}
-/*
+/**
Concatenates various items into various collections
with checkings for valid wkb type of items.
For example, MultiPoint can be a collection of Points only.
@@ -535,6 +543,10 @@ longlong Item_func_isempty::val_int()
}
+/**
+ @todo
+ Ramil or Holyfoot, add real IsSimple calculation
+*/
longlong Item_func_issimple::val_int()
{
DBUG_ASSERT(fixed == 1);
diff --git a/sql/item_geofunc.h b/sql/item_geofunc.h
index e99510f762f..edbe104e307 100644
--- a/sql/item_geofunc.h
+++ b/sql/item_geofunc.h
@@ -224,8 +224,13 @@ public:
DBUG_ASSERT(0); // Should never happened
return "sp_unknown";
}
- }
- void print(String *str) { Item_func::print(str); }
+ }
+
+ virtual inline void print(String *str, enum_query_type query_type)
+ {
+ Item_func::print(str, query_type);
+ }
+
void fix_length_and_dec() { maybe_null= 1; }
bool is_null() { (void) val_int(); return null_value; }
};
@@ -373,11 +378,11 @@ public:
void fix_length_and_dec() { max_length= 10; maybe_null= 1; }
};
-#define GEOM_NEW(obj_constructor) new obj_constructor
+#define GEOM_NEW(thd, obj_constructor) new (thd->mem_root) obj_constructor
#else /*HAVE_SPATIAL*/
-#define GEOM_NEW(obj_constructor) NULL
+#define GEOM_NEW(thd, obj_constructor) NULL
#endif
diff --git a/sql/item_row.cc b/sql/item_row.cc
index c037c092d89..28de03bf049 100644
--- a/sql/item_row.cc
+++ b/sql/item_row.cc
@@ -15,13 +15,18 @@
#include "mysql_priv.h"
-/*
+/**
Row items used for comparing rows and IN operations on rows:
+ @verbatim
(a, b, c) > (10, 10, 30)
(a, b, c) = (select c, d, e, from t1 where x=12)
(a, b, c) IN ((1,2,2), (3,4,5), (6,7,8)
(a, b, c) IN (select c, d, e, from t1)
+ @endverbatim
+
+ @todo
+ think placing 2-3 component items in item (as it done for function
*/
Item_row::Item_row(List<Item> &arg):
@@ -129,29 +134,31 @@ bool Item_row::check_cols(uint c)
return 0;
}
-void Item_row::print(String *str)
+void Item_row::print(String *str, enum_query_type query_type)
{
str->append('(');
for (uint i= 0; i < arg_count; i++)
{
if (i)
str->append(',');
- items[i]->print(str);
+ items[i]->print(str, query_type);
}
str->append(')');
}
-bool Item_row::walk(Item_processor processor, byte *arg)
+
+bool Item_row::walk(Item_processor processor, bool walk_subquery, uchar *arg)
{
for (uint i= 0; i < arg_count; i++)
{
- if (items[i]->walk(processor, arg))
+ if (items[i]->walk(processor, walk_subquery, arg))
return 1;
}
return (this->*processor)(arg);
}
-Item *Item_row::transform(Item_transformer transformer, byte *arg)
+
+Item *Item_row::transform(Item_transformer transformer, uchar *arg)
{
DBUG_ASSERT(!current_thd->is_stmt_prepare());
diff --git a/sql/item_row.h b/sql/item_row.h
index 8623b579e33..67441f49603 100644
--- a/sql/item_row.h
+++ b/sql/item_row.h
@@ -65,10 +65,10 @@ public:
bool const_item() const { return const_item_cache; };
enum Item_result result_type() const { return ROW_RESULT; }
void update_used_tables();
- void print(String *str);
+ virtual void print(String *str, enum_query_type query_type);
- bool walk(Item_processor processor, byte *arg);
- Item *transform(Item_transformer transformer, byte *arg);
+ bool walk(Item_processor processor, bool walk_subquery, uchar *arg);
+ Item *transform(Item_transformer transformer, uchar *arg);
uint cols() { return arg_count; }
Item* element_index(uint i) { return items[i]; }
diff --git a/sql/item_strfunc.cc b/sql/item_strfunc.cc
index a43ad8d8137..0595aa5bf7d 100644
--- a/sql/item_strfunc.cc
+++ b/sql/item_strfunc.cc
@@ -14,9 +14,15 @@
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
-/* This file defines all string functions
-** Warning: Some string functions doesn't always put and end-null on a String
-** (This shouldn't be needed)
+/**
+ @file
+
+ @brief
+ This file defines all string functions
+
+ @warning
+ Some string functions don't always put and end-null on a String.
+ (This shouldn't be needed)
*/
#ifdef USE_PRAGMA_IMPLEMENTATION
@@ -25,12 +31,10 @@
#include "mysql_priv.h"
#include <m_ctype.h>
-#ifdef HAVE_OPENSSL
-#include <openssl/des.h>
-#endif /* HAVE_OPENSSL */
#include "my_md5.h"
#include "sha1.h"
#include "my_aes.h"
+#include <zlib.h>
C_MODE_START
#include "../mysys/my_static.h" // For soundex_map
C_MODE_END
@@ -102,11 +106,11 @@ String *Item_func_md5::val_str(String *str)
if (sptr)
{
my_MD5_CTX context;
- unsigned char digest[16];
+ uchar digest[16];
null_value=0;
my_MD5Init (&context);
- my_MD5Update (&context,(unsigned char *) sptr->ptr(), sptr->length());
+ my_MD5Update (&context,(uchar *) sptr->ptr(), sptr->length());
my_MD5Final (digest, &context);
if (str->alloc(32)) // Ensure that memory is free
{
@@ -154,7 +158,7 @@ String *Item_func_sha::val_str(String *str)
mysql_sha1_reset(&context); /* We do not have to check for error here */
/* No need to check error as the only case would be too long message */
mysql_sha1_input(&context,
- (const unsigned char *) sptr->ptr(), sptr->length());
+ (const uchar *) sptr->ptr(), sptr->length());
/* Ensure that memory is free and we got result */
if (!( str->alloc(SHA1_HASH_SIZE*2) ||
(mysql_sha1_result(&context,digest))))
@@ -269,9 +273,9 @@ void Item_func_aes_decrypt::fix_length_and_dec()
}
-/*
+/**
Concatenate args with the following premises:
- If only one arg (which is ok), return value of arg
+ If only one arg (which is ok), return value of arg;
Don't reallocate val_str() if not absolute necessary.
*/
@@ -364,10 +368,35 @@ String *Item_func_concat::val_str(String *str)
}
else
{ // Two big const strings
- if (tmp_value.alloc(max_length) ||
- tmp_value.copy(*res) ||
- tmp_value.append(*res2))
+ /*
+ NOTE: We should be prudent in the initial allocation unit -- the
+ size of the arguments is a function of data distribution, which
+ can be any. Instead of overcommitting at the first row, we grow
+ the allocated amount by the factor of 2. This ensures that no
+ more than 25% of memory will be overcommitted on average.
+ */
+
+ uint concat_len= res->length() + res2->length();
+
+ if (tmp_value.alloced_length() < concat_len)
+ {
+ if (tmp_value.alloced_length() == 0)
+ {
+ if (tmp_value.alloc(concat_len))
+ goto null;
+ }
+ else
+ {
+ uint new_len = max(tmp_value.alloced_length() * 2, concat_len);
+
+ if (tmp_value.realloc(new_len))
+ goto null;
+ }
+ }
+
+ if (tmp_value.copy(*res) || tmp_value.append(*res2))
goto null;
+
res= &tmp_value;
use_as_buff=str;
}
@@ -408,13 +437,15 @@ void Item_func_concat::fix_length_and_dec()
max_length= (ulong) max_result_length;
}
-/*
+/**
+ @details
Function des_encrypt() by tonu@spam.ee & monty
Works only if compiled with OpenSSL library support.
- This returns a binary string where first character is CHAR(128 | key-number).
- If one uses a string key key_number is 127.
- Encryption result is longer than original by formula:
- new_length= org_length + (8-(org_length % 8))+1
+ @return
+ A binary string where first character is CHAR(128 | key-number).
+ If one uses a string key key_number is 127.
+ Encryption result is longer than original by formula:
+ @code new_length= org_length + (8-(org_length % 8))+1 @endcode
*/
String *Item_func_des_encrypt::val_str(String *str)
@@ -587,7 +618,7 @@ wrong_key:
}
-/*
+/**
concat with separator. First arg is the separator
concat_ws takes at least two arguments.
*/
@@ -687,8 +718,33 @@ String *Item_func_concat_ws::val_str(String *str)
}
else
{ // Two big const strings
- if (tmp_value.alloc(max_length) ||
- tmp_value.copy(*res) ||
+ /*
+ NOTE: We should be prudent in the initial allocation unit -- the
+ size of the arguments is a function of data distribution, which can
+ be any. Instead of overcommitting at the first row, we grow the
+ allocated amount by the factor of 2. This ensures that no more than
+ 25% of memory will be overcommitted on average.
+ */
+
+ uint concat_len= res->length() + sep_str->length() + res2->length();
+
+ if (tmp_value.alloced_length() < concat_len)
+ {
+ if (tmp_value.alloced_length() == 0)
+ {
+ if (tmp_value.alloc(concat_len))
+ goto null;
+ }
+ else
+ {
+ uint new_len = max(tmp_value.alloced_length() * 2, concat_len);
+
+ if (tmp_value.realloc(new_len))
+ goto null;
+ }
+ }
+
+ if (tmp_value.copy(*res) ||
tmp_value.append(*sep_str) ||
tmp_value.append(*res2))
goto null;
@@ -784,12 +840,14 @@ void Item_func_reverse::fix_length_and_dec()
max_length = args[0]->max_length;
}
-/*
-** Replace all occurences of string2 in string1 with string3.
-** Don't reallocate val_str() if not needed
-*/
+/**
+ Replace all occurences of string2 in string1 with string3.
+
+ Don't reallocate val_str() if not needed.
-/* TODO: Fix that this works with binary strings when using USE_MB */
+ @todo
+ Fix that this works with binary strings when using USE_MB
+*/
String *Item_func_replace::val_str(String *str)
{
@@ -949,8 +1007,8 @@ String *Item_func_insert::val_str(String *str)
length= res->length();
/* start and length are now sufficiently valid to pass to charpos function */
- start= res->charpos((int) start);
- length= res->charpos((int) length, (uint32) start);
+ start= res->charpos((int) start);
+ length= res->charpos((int) length, (uint32) start);
/* Re-testing with corrected params */
if (start > res->length())
@@ -1027,6 +1085,23 @@ String *Item_str_conv::val_str(String *str)
}
+void Item_func_lcase::fix_length_and_dec()
+{
+ collation.set(args[0]->collation);
+ multiply= collation.collation->casedn_multiply;
+ converter= collation.collation->cset->casedn;
+ max_length= args[0]->max_length * multiply;
+}
+
+void Item_func_ucase::fix_length_and_dec()
+{
+ collation.set(args[0]->collation);
+ multiply= collation.collation->caseup_multiply;
+ converter= collation.collation->cset->caseup;
+ max_length= args[0]->max_length * multiply;
+}
+
+
String *Item_func_left::val_str(String *str)
{
DBUG_ASSERT(fixed == 1);
@@ -1525,20 +1600,20 @@ void Item_func_trim::fix_length_and_dec()
}
}
-void Item_func_trim::print(String *str)
+void Item_func_trim::print(String *str, enum_query_type query_type)
{
if (arg_count == 1)
{
- Item_func::print(str);
+ Item_func::print(str, query_type);
return;
}
str->append(Item_func_trim::func_name());
str->append('(');
str->append(mode_name());
str->append(' ');
- args[1]->print(str);
+ args[1]->print(str, query_type);
str->append(STRING_WITH_LEN(" from "));
- args[0]->print(str);
+ args[0]->print(str, query_type);
str->append(')');
}
@@ -1640,51 +1715,62 @@ String *Item_func_encrypt::val_str(String *str)
void Item_func_encode::fix_length_and_dec()
{
max_length=args[0]->max_length;
- maybe_null=args[0]->maybe_null;
+ maybe_null=args[0]->maybe_null || args[1]->maybe_null;
collation.set(&my_charset_bin);
}
String *Item_func_encode::val_str(String *str)
{
- DBUG_ASSERT(fixed == 1);
String *res;
+ char pw_buff[80];
+ String tmp_pw_value(pw_buff, sizeof(pw_buff), system_charset_info);
+ String *password;
+ DBUG_ASSERT(fixed == 1);
+
if (!(res=args[0]->val_str(str)))
{
null_value=1; /* purecov: inspected */
return 0; /* purecov: inspected */
}
+
+ if (!(password=args[1]->val_str(& tmp_pw_value)))
+ {
+ null_value=1;
+ return 0;
+ }
+
null_value=0;
res=copy_if_not_alloced(str,res,res->length());
+ SQL_CRYPT sql_crypt(password->ptr());
sql_crypt.init();
sql_crypt.encode((char*) res->ptr(),res->length());
res->set_charset(&my_charset_bin);
return res;
}
-void Item_func_encode::print(String *str)
-{
- str->append(func_name());
- str->append('(');
- args[0]->print(str);
- str->append(',');
- str->append('\'');
- str->append(seed);
- str->append('\'');
- str->append(')');
-}
-
-
String *Item_func_decode::val_str(String *str)
{
- DBUG_ASSERT(fixed == 1);
String *res;
+ char pw_buff[80];
+ String tmp_pw_value(pw_buff, sizeof(pw_buff), system_charset_info);
+ String *password;
+ DBUG_ASSERT(fixed == 1);
+
if (!(res=args[0]->val_str(str)))
{
null_value=1; /* purecov: inspected */
return 0; /* purecov: inspected */
}
+
+ if (!(password=args[1]->val_str(& tmp_pw_value)))
+ {
+ null_value=1;
+ return 0;
+ }
+
null_value=0;
res=copy_if_not_alloced(str,res,res->length());
+ SQL_CRYPT sql_crypt(password->ptr());
sql_crypt.init();
sql_crypt.decode((char*) res->ptr(),res->length());
return res;
@@ -1732,8 +1818,9 @@ String *Item_func_database::val_str(String *str)
}
-/*
- TODO: make USER() replicate properly (currently it is replicated to "")
+/**
+ @todo
+ make USER() replicate properly (currently it is replicated to "")
*/
bool Item_func_user::init(const char *user, const char *host)
{
@@ -1793,7 +1880,7 @@ void Item_func_soundex::fix_length_and_dec()
}
-/*
+/**
If alpha, map input letter to soundex code.
If not alpha and remove_garbage is set then skip to next char
else return 0
@@ -1937,28 +2024,56 @@ String *Item_func_soundex::val_str(String *str)
}
-/*
-** Change a number to format '3,333,333,333.000'
-** This should be 'internationalized' sometimes.
+/**
+ Change a number to format '3,333,333,333.000'.
+
+ This should be 'internationalized' sometimes.
*/
-Item_func_format::Item_func_format(Item *org,int dec) :Item_str_func(org)
+const int FORMAT_MAX_DECIMALS= 30;
+
+Item_func_format::Item_func_format(Item *org, Item *dec)
+: Item_str_func(org, dec)
+{
+}
+
+void Item_func_format::fix_length_and_dec()
{
- decimals=(uint) set_zone(dec,0,30);
+ uint char_length= args[0]->max_length/args[0]->collation.collation->mbmaxlen;
+ uint 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;
}
-/*
- TODO: This needs to be fixed for multi-byte character set where numbers
+/**
+ @todo
+ This needs to be fixed for multi-byte character set where numbers
are stored in more than one byte
*/
String *Item_func_format::val_str(String *str)
{
- uint32 length, str_length ,dec;
+ 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;
DBUG_ASSERT(fixed == 1);
- dec= decimals ? decimals+1 : 0;
+
+ dec= (int) args[1]->val_int();
+ if (args[1]->null_value)
+ {
+ null_value=1;
+ return NULL;
+ }
+
+ dec= set_zone(dec, 0, FORMAT_MAX_DECIMALS);
+ dec_length= dec ? dec+1 : 0;
+ null_value=0;
if (args[0]->result_type() == DECIMAL_RESULT ||
args[0]->result_type() == INT_RESULT)
@@ -1967,7 +2082,7 @@ String *Item_func_format::val_str(String *str)
res= args[0]->val_decimal(&dec_val);
if ((null_value=args[0]->null_value))
return 0; /* purecov: inspected */
- my_decimal_round(E_DEC_FATAL_ERROR, res, decimals, false, &rnd_dec);
+ 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())
@@ -1978,9 +2093,9 @@ String *Item_func_format::val_str(String *str)
double nr= args[0]->val_real();
if ((null_value=args[0]->null_value))
return 0; /* purecov: inspected */
- nr= my_double_round(nr, (longlong) decimals, FALSE, FALSE);
+ nr= my_double_round(nr, (longlong) dec, FALSE, FALSE);
/* Here default_charset() is right as this is not an automatic conversion */
- str->set(nr,decimals, default_charset());
+ str->set_real(nr, dec, default_charset());
if (isnan(nr))
return str;
str_length=str->length();
@@ -1988,13 +2103,13 @@ String *Item_func_format::val_str(String *str)
str_length--; // Don't count sign
}
/* We need this test to handle 'nan' values */
- if (str_length >= dec+4)
+ if (str_length >= dec_length+4)
{
char *tmp,*pos;
- length= str->length()+(diff=((int)(str_length- dec-1))/3);
+ 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-1;
+ tmp= (char*) str->ptr()+length - dec_length-1;
for (pos= (char*) str->ptr()+length-1; pos != tmp; pos--)
pos[0]= pos[-diff];
while (diff)
@@ -2014,16 +2129,12 @@ String *Item_func_format::val_str(String *str)
}
-void Item_func_format::print(String *str)
+void Item_func_format::print(String *str, enum_query_type query_type)
{
str->append(STRING_WITH_LEN("format("));
- args[0]->print(str);
- str->append(',');
- // my_charset_bin is good enough for numbers
- char buffer[20];
- String st(buffer, sizeof(buffer), &my_charset_bin);
- st.set((ulonglong)decimals, &my_charset_bin);
- str->append(st);
+ args[0]->print(str, query_type);
+ str->append(',');
+ args[1]->print(str, query_type);
str->append(')');
}
@@ -2174,7 +2285,7 @@ String *Item_func_make_set::val_str(String *str)
}
-Item *Item_func_make_set::transform(Item_transformer transformer, byte *arg)
+Item *Item_func_make_set::transform(Item_transformer transformer, uchar *arg)
{
DBUG_ASSERT(!current_thd->is_stmt_prepare());
@@ -2194,14 +2305,14 @@ Item *Item_func_make_set::transform(Item_transformer transformer, byte *arg)
}
-void Item_func_make_set::print(String *str)
+void Item_func_make_set::print(String *str, enum_query_type query_type)
{
str->append(STRING_WITH_LEN("make_set("));
- item->print(str);
+ item->print(str, query_type);
if (arg_count)
{
str->append(',');
- print_args(str, 0);
+ print_args(str, 0, query_type);
}
str->append(')');
}
@@ -2285,9 +2396,9 @@ void Item_func_repeat::fix_length_and_dec()
}
}
-/*
-** Item_func_repeat::str is carefully written to avoid reallocs
-** as much as possible at the cost of a local buffer
+/**
+ Item_func_repeat::str is carefully written to avoid reallocs
+ as much as possible at the cost of a local buffer
*/
String *Item_func_repeat::val_str(String *str)
@@ -2398,7 +2509,7 @@ String *Item_func_rpad::val_str(String *str)
count= INT_MAX32;
if (count <= (res_char_length= res->numchars()))
{ // String to pad is big enough
- res->length(res->charpos((int) count)); // Shorten result if longer
+ res->length(res->charpos((int) count)); // Shorten result if longer
return (res);
}
pad_char_length= rpad->numchars();
@@ -2612,10 +2723,10 @@ void Item_func_conv_charset::fix_length_and_dec()
max_length = args[0]->max_length*conv_charset->mbmaxlen;
}
-void Item_func_conv_charset::print(String *str)
+void Item_func_conv_charset::print(String *str, enum_query_type query_type)
{
str->append(STRING_WITH_LEN("convert("));
- args[0]->print(str);
+ args[0]->print(str, query_type);
str->append(STRING_WITH_LEN(" using "));
str->append(conv_charset->csname);
str->append(')');
@@ -2683,10 +2794,10 @@ bool Item_func_set_collation::eq(const Item *item, bool binary_cmp) const
}
-void Item_func_set_collation::print(String *str)
+void Item_func_set_collation::print(String *str, enum_query_type query_type)
{
str->append('(');
- args[0]->print(str);
+ args[0]->print(str, query_type);
str->append(STRING_WITH_LEN(" collate "));
DBUG_ASSERT(args[1]->basic_const_item() &&
args[1]->type() == Item::STRING_ITEM);
@@ -2763,7 +2874,7 @@ String *Item_func_hex::val_str(String *str)
return &tmp_value;
}
- /* Convert given hex string to a binary string */
+ /** Convert given hex string to a binary string. */
String *Item_func_unhex::val_str(String *str)
{
@@ -2805,10 +2916,10 @@ String *Item_func_unhex::val_str(String *str)
}
-void Item_func_binary::print(String *str)
+void Item_func_binary::print(String *str, enum_query_type query_type)
{
str->append(STRING_WITH_LEN("cast("));
- args[0]->print(str);
+ args[0]->print(str, query_type);
str->append(STRING_WITH_LEN(" as binary)"));
}
@@ -2859,7 +2970,7 @@ String *Item_load_file::val_str(String *str)
goto err;
if ((file = my_open(file_name->c_ptr(), O_RDONLY, MYF(0))) < 0)
goto err;
- if (my_read(file, (byte*) tmp_value.ptr(), stat_info.st_size, MYF(MY_NABP)))
+ if (my_read(file, (uchar*) tmp_value.ptr(), stat_info.st_size, MYF(MY_NABP)))
{
my_close(file, MYF(0));
goto err;
@@ -2996,27 +3107,27 @@ String* Item_func_inet_ntoa::val_str(String* str)
}
-/*
+#define get_esc_bit(mask, num) (1 & (*((mask) + ((num) >> 3))) >> ((num) & 7))
+
+/**
QUOTE() function returns argument string in single quotes suitable for
using in a SQL statement.
- DESCRIPTION
- Adds a \ before all characters that needs to be escaped in a SQL string.
- We also escape '^Z' (END-OF-FILE in windows) to avoid probelms when
- running commands from a file in windows.
+ Adds a \\ before all characters that needs to be escaped in a SQL string.
+ We also escape '^Z' (END-OF-FILE in windows) to avoid probelms when
+ running commands from a file in windows.
- This function is very useful when you want to generate SQL statements
+ This function is very useful when you want to generate SQL statements.
- NOTE
+ @note
QUOTE(NULL) returns the string 'NULL' (4 letters, without quotes).
- RETURN VALUES
- str Quoted string
- NULL Out of memory.
+ @retval
+ str Quoted string
+ @retval
+ NULL Out of memory.
*/
-#define get_esc_bit(mask, num) (1 & (*((mask) + ((num) >> 3))) >> ((num) & 7))
-
String *Item_func_quote::val_str(String *str)
{
DBUG_ASSERT(fixed == 1);
@@ -3262,8 +3373,10 @@ static uint nanoseq;
static ulonglong uuid_time=0;
static char clock_seq_and_node_str[]="-0000-000000000000";
-/* number of 100-nanosecond intervals between
- 1582-10-15 00:00:00.00 and 1970-01-01 00:00:00.00 */
+/**
+ number of 100-nanosecond intervals between
+ 1582-10-15 00:00:00.00 and 1970-01-01 00:00:00.00.
+*/
#define UUID_TIME_OFFSET ((ulonglong) 141427 * 24 * 60 * 60 * \
1000 * 1000 * 10)
diff --git a/sql/item_strfunc.h b/sql/item_strfunc.h
index 9794a092648..81baf9a4c5f 100644
--- a/sql/item_strfunc.h
+++ b/sql/item_strfunc.h
@@ -144,8 +144,7 @@ class Item_str_conv :public Item_str_func
{
protected:
uint multiply;
- uint (*converter)(CHARSET_INFO *cs, char *src, uint srclen,
- char *dst, uint dstlen);
+ my_charset_conv_case converter;
String tmp_value;
public:
Item_str_conv(Item *item) :Item_str_func(item) {}
@@ -158,13 +157,7 @@ class Item_func_lcase :public Item_str_conv
public:
Item_func_lcase(Item *item) :Item_str_conv(item) {}
const char *func_name() const { return "lcase"; }
- void fix_length_and_dec()
- {
- collation.set(args[0]->collation);
- multiply= collation.collation->casedn_multiply;
- converter= collation.collation->cset->casedn;
- max_length= args[0]->max_length * multiply;
- }
+ void fix_length_and_dec();
};
class Item_func_ucase :public Item_str_conv
@@ -172,13 +165,7 @@ class Item_func_ucase :public Item_str_conv
public:
Item_func_ucase(Item *item) :Item_str_conv(item) {}
const char *func_name() const { return "ucase"; }
- void fix_length_and_dec()
- {
- collation.set(args[0]->collation);
- multiply= collation.collation->caseup_multiply;
- converter= collation.collation->cset->caseup;
- max_length= args[0]->max_length * multiply;
- }
+ void fix_length_and_dec();
};
@@ -238,7 +225,7 @@ public:
String *val_str(String *);
void fix_length_and_dec();
const char *func_name() const { return "trim"; }
- void print(String *str);
+ virtual void print(String *str, enum_query_type query_type);
virtual const char *mode_name() const { return "both"; }
};
@@ -355,26 +342,19 @@ public:
class Item_func_encode :public Item_str_func
{
- protected:
- SQL_CRYPT sql_crypt;
- String seed;
public:
- Item_func_encode(Item *a, char *seed_arg):
- Item_str_func(a), sql_crypt(seed_arg)
- {
- seed.copy(seed_arg, (uint) strlen(seed_arg), default_charset_info);
- }
+ Item_func_encode(Item *a, Item *seed):
+ Item_str_func(a, seed) {}
String *val_str(String *);
void fix_length_and_dec();
const char *func_name() const { return "encode"; }
- void print(String *str);
};
class Item_func_decode :public Item_func_encode
{
public:
- Item_func_decode(Item *a, char *seed_arg): Item_func_encode(a, seed_arg) {}
+ Item_func_decode(Item *a, Item *seed): Item_func_encode(a, seed) {}
String *val_str(String *);
const char *func_name() const { return "decode"; }
};
@@ -428,8 +408,8 @@ public:
bool fix_fields(THD *thd, Item **ref);
void fix_length_and_dec()
{
- max_length= ((USERNAME_LENGTH + HOSTNAME_LENGTH + 1) *
- system_charset_info->mbmaxlen);
+ max_length= (USERNAME_LENGTH +
+ (HOSTNAME_LENGTH + 1) * SYSTEM_CHARSET_MBMAXLEN);
}
const char *func_name() const { return "user"; }
const char *fully_qualified_func_name() const { return "user()"; }
@@ -496,13 +476,13 @@ public:
void update_used_tables();
const char *func_name() const { return "make_set"; }
- bool walk(Item_processor processor, byte *arg)
+ bool walk(Item_processor processor, bool walk_subquery, uchar *arg)
{
- return item->walk(processor, arg) ||
- Item_str_func::walk(processor, arg);
+ return item->walk(processor, walk_subquery, arg) ||
+ Item_str_func::walk(processor, walk_subquery, arg);
}
- Item *transform(Item_transformer transformer, byte *arg);
- void print(String *str);
+ Item *transform(Item_transformer transformer, uchar *arg);
+ virtual void print(String *str, enum_query_type query_type);
};
@@ -510,18 +490,11 @@ class Item_func_format :public Item_str_func
{
String tmp_str;
public:
- Item_func_format(Item *org,int dec);
+ Item_func_format(Item *org, Item *dec);
String *val_str(String *);
- void fix_length_and_dec()
- {
- collation.set(default_charset());
- uint char_length= args[0]->max_length/args[0]->collation.collation->mbmaxlen;
- uint max_sep_count= char_length/3 + (decimals ? 1 : 0) + /*sign*/1;
- max_length= (char_length + max_sep_count + decimals) *
- collation.collation->mbmaxlen;
- }
+ void fix_length_and_dec();
const char *func_name() const { return "format"; }
- void print(String *);
+ virtual void print(String *str, enum_query_type query_type);
};
@@ -644,7 +617,7 @@ public:
collation.set(&my_charset_bin);
max_length=args[0]->max_length;
}
- void print(String *str);
+ virtual void print(String *str, enum_query_type query_type);
const char *func_name() const { return "cast_as_binary"; }
};
@@ -746,7 +719,7 @@ public:
String *val_str(String *);
void fix_length_and_dec();
const char *func_name() const { return "convert"; }
- void print(String *str);
+ virtual void print(String *str, enum_query_type query_type);
};
class Item_func_set_collation :public Item_str_func
@@ -758,7 +731,7 @@ public:
bool eq(const Item *item, bool binary_cmp) const;
const char *func_name() const { return "collate"; }
enum Functype functype() const { return COLLATE_FUNC; }
- void print(String *str);
+ virtual void print(String *str, enum_query_type query_type);
Item_field *filed_for_view_update()
{
/* this function is transparent for view updating */
diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc
index 401751660fd..3981b91a27c 100644
--- a/sql/item_subselect.cc
+++ b/sql/item_subselect.cc
@@ -13,12 +13,15 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
-/*
- subselect Item
+/**
+ @file
+
+ @brief
+ subselect Item
-SUBSELECT TODO:
- - add function from mysql_select that use JOIN* as parameter to JOIN methods
- (sql_select.h/sql_select.cc)
+ @todo
+ - add function from mysql_select that use JOIN* as parameter to JOIN
+ methods (sql_select.h/sql_select.cc)
*/
#ifdef USE_PRAGMA_IMPLEMENTATION
@@ -82,7 +85,7 @@ void Item_subselect::init(st_select_lex *select_lex,
parsing_place= (outer_select->in_sum_expr ?
NO_MATTER :
outer_select->parsing_place);
- if (select_lex->next_select())
+ if (unit->is_union())
engine= new subselect_union_engine(unit, result, this);
else
engine= new subselect_single_select_engine(select_lex, result, this);
@@ -143,12 +146,13 @@ Item_subselect::select_transformer(JOIN *join)
bool Item_subselect::fix_fields(THD *thd_param, Item **ref)
{
char const *save_where= thd_param->where;
+ uint8 uncacheable;
bool res;
DBUG_ASSERT(fixed == 0);
engine->set_thd((thd= thd_param));
- if (check_stack_overrun(thd, STACK_MIN_SIZE, (gptr)&res))
+ if (check_stack_overrun(thd, STACK_MIN_SIZE, (uchar*)&res))
return TRUE;
res= engine->prepare();
@@ -188,26 +192,73 @@ bool Item_subselect::fix_fields(THD *thd_param, Item **ref)
fix_length_and_dec();
}
else
- return 1;
- uint8 uncacheable= engine->uncacheable();
- if (uncacheable)
+ goto err;
+
+ if ((uncacheable= engine->uncacheable()))
{
const_item_cache= 0;
if (uncacheable & UNCACHEABLE_RAND)
used_tables_cache|= RAND_TABLE_BIT;
}
fixed= 1;
+
+err:
thd->where= save_where;
return res;
}
+
+bool Item_subselect::walk(Item_processor processor, bool walk_subquery,
+ uchar *argument)
+{
+
+ if (walk_subquery)
+ {
+ for (SELECT_LEX *lex= unit->first_select(); lex; lex= lex->next_select())
+ {
+ List_iterator<Item> li(lex->item_list);
+ Item *item;
+ ORDER *order;
+
+ if (lex->where && (lex->where)->walk(processor, walk_subquery, argument))
+ return 1;
+ if (lex->having && (lex->having)->walk(processor, walk_subquery,
+ argument))
+ return 1;
+
+ while ((item=li++))
+ {
+ if (item->walk(processor, walk_subquery, argument))
+ return 1;
+ }
+ for (order= (ORDER*) lex->order_list.first ; order; order= order->next)
+ {
+ if ((*order->item)->walk(processor, walk_subquery, argument))
+ return 1;
+ }
+ for (order= (ORDER*) lex->group_list.first ; order; order= order->next)
+ {
+ if ((*order->item)->walk(processor, walk_subquery, argument))
+ return 1;
+ }
+ }
+ }
+ return (this->*processor)(argument);
+}
+
+
bool Item_subselect::exec()
{
int res;
- if (thd->net.report_error)
+ if (thd->is_error())
/* Do not execute subselect in case of a fatal error */
return 1;
+ /*
+ 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;);
res= engine->exec();
@@ -260,10 +311,10 @@ void Item_subselect::update_used_tables()
}
-void Item_subselect::print(String *str)
+void Item_subselect::print(String *str, enum_query_type query_type)
{
str->append('(');
- engine->print(str);
+ engine->print(str, query_type);
str->append(')');
}
@@ -345,10 +396,10 @@ void Item_maxmin_subselect::cleanup()
}
-void Item_maxmin_subselect::print(String *str)
+void Item_maxmin_subselect::print(String *str, enum_query_type query_type)
{
str->append(max?"<max>":"<min>", 5);
- Item_singlerow_subselect::print(str);
+ Item_singlerow_subselect::print(str, query_type);
}
@@ -360,6 +411,16 @@ void Item_singlerow_subselect::reset()
}
+/**
+ @todo
+ - We cant change name of Item_field or Item_ref, because it will
+ prevent it's correct resolving, but we should save name of
+ removed item => we do not make optimization if top item of
+ list is field or reference.
+ - switch off this optimization for prepare statement,
+ because we do not rollback this changes.
+ Make rollback for it, or special name resolving mode in 5.0.
+*/
Item_subselect::trans_res
Item_singlerow_subselect::select_transformer(JOIN *join)
{
@@ -369,7 +430,7 @@ Item_singlerow_subselect::select_transformer(JOIN *join)
SELECT_LEX *select_lex= join->select_lex;
Query_arena *arena= thd->stmt_arena;
- if (!select_lex->master_unit()->first_select()->next_select() &&
+ if (!select_lex->master_unit()->is_union() &&
!select_lex->table_list.elements &&
select_lex->item_list.elements == 1 &&
!select_lex->item_list.head()->with_sum_func &&
@@ -405,8 +466,8 @@ Item_singlerow_subselect::select_transformer(JOIN *join)
as far as we moved content to upper level, field which depend of
'upper' select is not really dependent => we remove this dependence
*/
- substitution->walk(&Item::remove_dependence_processor,
- (byte *) select_lex->outer_select());
+ substitution->walk(&Item::remove_dependence_processor, 0,
+ (uchar *) select_lex->outer_select());
return RES_REDUCE;
}
return RES_OK;
@@ -574,14 +635,14 @@ Item_exists_subselect::Item_exists_subselect(st_select_lex *select_lex):
}
-void Item_exists_subselect::print(String *str)
+void Item_exists_subselect::print(String *str, enum_query_type query_type)
{
str->append(STRING_WITH_LEN("exists"));
- Item_subselect::print(str);
+ Item_subselect::print(str, query_type);
}
-bool Item_in_subselect::test_limit(SELECT_LEX_UNIT *unit_arg)
+bool Item_in_subselect::test_limit(st_select_lex_unit *unit_arg)
{
if (unit_arg->fake_select_lex &&
unit_arg->fake_select_lex->test_limit())
@@ -663,27 +724,48 @@ longlong Item_exists_subselect::val_int()
return value;
}
+
+/**
+ Return the result of EXISTS as a string value
+
+ Converts the true/false result into a string value.
+ Note that currently this cannot be NULL, so if the query exection fails
+ it will return 0.
+
+ @param decimal_value[out] buffer to hold the resulting string value
+ @retval Pointer to the converted string.
+ Can't be a NULL pointer, as currently
+ EXISTS cannot return NULL.
+*/
+
String *Item_exists_subselect::val_str(String *str)
{
DBUG_ASSERT(fixed == 1);
if (exec())
- {
reset();
- return 0;
- }
str->set((ulonglong)value,&my_charset_bin);
return str;
}
+/**
+ Return the result of EXISTS as a decimal value
+
+ Converts the true/false result into a decimal value.
+ Note that currently this cannot be NULL, so if the query exection fails
+ it will return 0.
+
+ @param decimal_value[out] Buffer to hold the resulting decimal value
+ @retval Pointer to the converted decimal.
+ Can't be a NULL pointer, as currently
+ EXISTS cannot return NULL.
+*/
+
my_decimal *Item_exists_subselect::val_decimal(my_decimal *decimal_value)
{
DBUG_ASSERT(fixed == 1);
if (exec())
- {
reset();
- return 0;
- }
int2my_decimal(E_DEC_FATAL_ERROR, value, 0, decimal_value);
return decimal_value;
}
@@ -1110,7 +1192,7 @@ Item_in_subselect::single_value_transformer(JOIN *join,
else
{
bool tmp;
- if (select_lex->master_unit()->first_select()->next_select())
+ if (select_lex->master_unit()->is_union())
{
/*
comparison functions can't be changed during fix_fields()
@@ -1410,24 +1492,24 @@ Item_in_subselect::select_transformer(JOIN *join)
}
-/*
+/**
Prepare IN/ALL/ANY/SOME subquery transformation and call appropriate
- transformation function
+ transformation function.
- SYNOPSIS
- Item_in_subselect::select_in_like_transformer()
- join JOIN object of transforming subquery
- func creator of condition function of subquery
-
- DESCRIPTION
To decide which transformation procedure (scalar or row) applicable here
we have to call fix_fields() for left expression to be able to call
cols() method on it. Also this method make arena management for
underlying transformation methods.
- RETURN
+ @param join JOIN object of transforming subquery
+ @param func creator of condition function of subquery
+
+ @retval
RES_OK OK
- RES_REDUCE OK, and current subquery was reduced during transformation
+ @retval
+ RES_REDUCE OK, and current subquery was reduced during
+ transformation
+ @retval
RES_ERROR Error
*/
@@ -1519,16 +1601,16 @@ err:
}
-void Item_in_subselect::print(String *str)
+void Item_in_subselect::print(String *str, enum_query_type query_type)
{
if (transformed)
str->append(STRING_WITH_LEN("<exists>"));
else
{
- left_expr->print(str);
+ left_expr->print(str, query_type);
str->append(STRING_WITH_LEN(" in "));
}
- Item_subselect::print(str);
+ Item_subselect::print(str, query_type);
}
@@ -1553,18 +1635,18 @@ Item_allany_subselect::select_transformer(JOIN *join)
}
-void Item_allany_subselect::print(String *str)
+void Item_allany_subselect::print(String *str, enum_query_type query_type)
{
if (transformed)
str->append(STRING_WITH_LEN("<exists>"));
else
{
- left_expr->print(str);
+ left_expr->print(str, query_type);
str->append(' ');
str->append(func->symbol(all));
str->append(all ? " all " : " any ", 5);
}
- Item_subselect::print(str);
+ Item_subselect::print(str, query_type);
}
@@ -1730,7 +1812,7 @@ 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;
- res_field_type= FIELD_TYPE_VAR_STRING;
+ res_field_type= MYSQL_TYPE_VAR_STRING;
for (uint i= 0; (sel_item= li++); i++)
{
item->max_length= sel_item->max_length;
@@ -2109,10 +2191,11 @@ int subselect_uniquesubquery_engine::exec()
DBUG_RETURN(scan_table());
if (!table->file->inited)
- table->file->ha_index_init(tab->ref.key);
- error= table->file->index_read(table->record[0],
- tab->ref.key_buff,
- tab->ref.key_length,HA_READ_KEY_EXACT);
+ table->file->ha_index_init(tab->ref.key, 0);
+ error= table->file->index_read_map(table->record[0],
+ tab->ref.key_buff,
+ make_prev_keypart_map(tab->ref.key_parts),
+ HA_READ_KEY_EXACT);
if (error &&
error != HA_ERR_KEY_NOT_FOUND && error != HA_ERR_END_OF_FILE)
error= report_error(table, error);
@@ -2229,10 +2312,11 @@ int subselect_indexsubquery_engine::exec()
DBUG_RETURN(scan_table());
if (!table->file->inited)
- table->file->ha_index_init(tab->ref.key);
- error= table->file->index_read(table->record[0],
- tab->ref.key_buff,
- tab->ref.key_length,HA_READ_KEY_EXACT);
+ table->file->ha_index_init(tab->ref.key, 1);
+ error= table->file->index_read_map(table->record[0],
+ tab->ref.key_buff,
+ make_prev_keypart_map(tab->ref.key_parts),
+ HA_READ_KEY_EXACT);
if (error &&
error != HA_ERR_KEY_NOT_FOUND && error != HA_ERR_END_OF_FILE)
error= report_error(table, error);
@@ -2348,42 +2432,45 @@ table_map subselect_union_engine::upper_select_const_tables()
}
-void subselect_single_select_engine::print(String *str)
+void subselect_single_select_engine::print(String *str,
+ enum_query_type query_type)
{
- select_lex->print(thd, str);
+ select_lex->print(thd, str, query_type);
}
-void subselect_union_engine::print(String *str)
+void subselect_union_engine::print(String *str, enum_query_type query_type)
{
- unit->print(str);
+ unit->print(str, query_type);
}
-void subselect_uniquesubquery_engine::print(String *str)
+void subselect_uniquesubquery_engine::print(String *str,
+ enum_query_type query_type)
{
str->append(STRING_WITH_LEN("<primary_index_lookup>("));
- tab->ref.items[0]->print(str);
+ tab->ref.items[0]->print(str, query_type);
str->append(STRING_WITH_LEN(" in "));
- str->append(tab->table->s->table_name);
+ str->append(tab->table->s->table_name.str, tab->table->s->table_name.length);
KEY *key_info= tab->table->key_info+ tab->ref.key;
str->append(STRING_WITH_LEN(" on "));
str->append(key_info->name);
if (cond)
{
str->append(STRING_WITH_LEN(" where "));
- cond->print(str);
+ cond->print(str, query_type);
}
str->append(')');
}
-void subselect_indexsubquery_engine::print(String *str)
+void subselect_indexsubquery_engine::print(String *str,
+ enum_query_type query_type)
{
str->append(STRING_WITH_LEN("<index_lookup>("));
- tab->ref.items[0]->print(str);
+ tab->ref.items[0]->print(str, query_type);
str->append(STRING_WITH_LEN(" in "));
- str->append(tab->table->s->table_name);
+ str->append(tab->table->s->table_name.str, tab->table->s->table_name.length);
KEY *key_info= tab->table->key_info+ tab->ref.key;
str->append(STRING_WITH_LEN(" on "));
str->append(key_info->name);
@@ -2392,26 +2479,25 @@ void subselect_indexsubquery_engine::print(String *str)
if (cond)
{
str->append(STRING_WITH_LEN(" where "));
- cond->print(str);
+ cond->print(str, query_type);
}
if (having)
{
str->append(STRING_WITH_LEN(" having "));
- having->print(str);
+ having->print(str, query_type);
}
str->append(')');
}
-/*
- change select_result object of engine
+/**
+ change select_result object of engine.
- SYNOPSIS
- subselect_single_select_engine::change_result()
- si new subselect Item
- res new select_result object
+ @param si new subselect Item
+ @param res new select_result object
- RETURN
+ @retval
FALSE OK
+ @retval
TRUE error
*/
@@ -2424,16 +2510,15 @@ bool subselect_single_select_engine::change_result(Item_subselect *si,
}
-/*
- change select_result object of engine
+/**
+ change select_result object of engine.
- SYNOPSIS
- subselect_single_select_engine::change_result()
- si new subselect Item
- res new select_result object
+ @param si new subselect Item
+ @param res new select_result object
- RETURN
+ @retval
FALSE OK
+ @retval
TRUE error
*/
@@ -2447,16 +2532,15 @@ bool subselect_union_engine::change_result(Item_subselect *si,
}
-/*
- change select_result emulation, never should be called
+/**
+ change select_result emulation, never should be called.
- SYNOPSIS
- subselect_single_select_engine::change_result()
- si new subselect Item
- res new select_result object
+ @param si new subselect Item
+ @param res new select_result object
- RETURN
+ @retval
FALSE OK
+ @retval
TRUE error
*/
@@ -2468,14 +2552,12 @@ bool subselect_uniquesubquery_engine::change_result(Item_subselect *si,
}
-/*
- Report about presence of tables in subquery
-
- SYNOPSIS
- subselect_single_select_engine::no_tables()
+/**
+ Report about presence of tables in subquery.
- RETURN
+ @retval
TRUE there are not tables used in subquery
+ @retval
FALSE there are some tables in subquery
*/
bool subselect_single_select_engine::no_tables()
@@ -2500,14 +2582,12 @@ bool subselect_single_select_engine::may_be_null()
}
-/*
- Report about presence of tables in subquery
-
- SYNOPSIS
- subselect_union_engine::no_tables()
+/**
+ Report about presence of tables in subquery.
- RETURN
+ @retval
TRUE there are not tables used in subquery
+ @retval
FALSE there are some tables in subquery
*/
bool subselect_union_engine::no_tables()
@@ -2521,14 +2601,12 @@ bool subselect_union_engine::no_tables()
}
-/*
- Report about presence of tables in subquery
-
- SYNOPSIS
- subselect_uniquesubquery_engine::no_tables()
+/**
+ Report about presence of tables in subquery.
- RETURN
+ @retval
TRUE there are not tables used in subquery
+ @retval
FALSE there are some tables in subquery
*/
diff --git a/sql/item_subselect.h b/sql/item_subselect.h
index 51dcd3ca175..d4aa621c083 100644
--- a/sql/item_subselect.h
+++ b/sql/item_subselect.h
@@ -103,7 +103,7 @@ public:
inline bool get_const_item_cache() { return const_item_cache; }
Item *get_tmp_table_item(THD *thd);
void update_used_tables();
- void print(String *str);
+ virtual void print(String *str, enum_query_type query_type);
virtual bool have_guarded_conds() { return FALSE; }
bool change_engine(subselect_engine *eng)
{
@@ -125,6 +125,7 @@ public:
*/
virtual void reset_value_registration() {}
enum_parsing_place place() { return parsing_place; }
+ bool walk(Item_processor processor, bool walk_subquery, uchar *arg);
/**
Get the SELECT_LEX structure associated with this Item.
@@ -202,7 +203,7 @@ protected:
public:
Item_maxmin_subselect(THD *thd, Item_subselect *parent,
st_select_lex *select_lex, bool max);
- void print(String *str);
+ virtual void print(String *str, enum_query_type query_type);
void cleanup();
bool any_value() { return was_values; }
void register_value() { was_values= TRUE; }
@@ -233,7 +234,7 @@ public:
my_decimal *val_decimal(my_decimal *);
bool val_bool();
void fix_length_and_dec();
- void print(String *str);
+ virtual void print(String *str, enum_query_type query_type);
friend class select_exists_subselect;
friend class subselect_uniquesubquery_engine;
@@ -311,7 +312,7 @@ public:
void top_level_item() { abort_on_null=1; }
inline bool is_top_level_item() { return abort_on_null; }
bool test_limit(st_select_lex_unit *unit);
- void print(String *str);
+ virtual void print(String *str, enum_query_type query_type);
bool fix_fields(THD *thd, Item **ref);
friend class Item_ref_null_helper;
@@ -334,7 +335,7 @@ public:
// only ALL subquery has upper not
subs_type substype() { return all?ALL_SUBS:ANY_SUBS; }
trans_res select_transformer(JOIN *join);
- void print(String *str);
+ virtual void print(String *str, enum_query_type query_type);
};
@@ -355,7 +356,7 @@ public:
result= res;
item= si;
res_type= STRING_RESULT;
- res_field_type= FIELD_TYPE_VAR_STRING;
+ res_field_type= MYSQL_TYPE_VAR_STRING;
maybe_null= 0;
}
virtual ~subselect_engine() {}; // to satisfy compiler
@@ -398,7 +399,7 @@ public:
virtual bool may_be_null() { return maybe_null; };
virtual table_map upper_select_const_tables()= 0;
static table_map calc_const_tables(TABLE_LIST *);
- virtual void print(String *str)= 0;
+ virtual void print(String *str, enum_query_type query_type)= 0;
virtual bool change_result(Item_subselect *si, select_subselect *result)= 0;
virtual bool no_tables()= 0;
virtual bool is_executed() const { return FALSE; }
@@ -429,7 +430,7 @@ public:
uint8 uncacheable();
void exclude();
table_map upper_select_const_tables();
- void print (String *str);
+ virtual void print (String *str, enum_query_type query_type);
bool change_result(Item_subselect *si, select_subselect *result);
bool no_tables();
bool may_be_null();
@@ -453,7 +454,7 @@ public:
uint8 uncacheable();
void exclude();
table_map upper_select_const_tables();
- void print (String *str);
+ virtual void print (String *str, enum_query_type query_type);
bool change_result(Item_subselect *si, select_subselect *result);
bool no_tables();
bool is_executed() const;
@@ -510,7 +511,7 @@ public:
uint8 uncacheable() { return UNCACHEABLE_DEPENDENT; }
void exclude();
table_map upper_select_const_tables() { return 0; }
- void print (String *str);
+ virtual void print (String *str, enum_query_type query_type);
bool change_result(Item_subselect *si, select_subselect *result);
bool no_tables();
int scan_table();
@@ -564,7 +565,7 @@ public:
having(having_arg)
{}
int exec();
- void print (String *str);
+ virtual void print (String *str, enum_query_type query_type);
};
diff --git a/sql/item_sum.cc b/sql/item_sum.cc
index d33d92a5238..1821136cc9d 100644
--- a/sql/item_sum.cc
+++ b/sql/item_sum.cc
@@ -14,7 +14,12 @@
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
-/* Sum functions (COUNT, MIN...) */
+/**
+ @file
+
+ @brief
+ Sum functions (COUNT, MIN...)
+*/
#ifdef USE_PRAGMA_IMPLEMENTATION
#pragma implementation // gcc: Class implementation
@@ -23,28 +28,25 @@
#include "mysql_priv.h"
#include "sql_select.h"
-/*
- Prepare an aggregate function item for checking context conditions
-
- SYNOPSIS
- init_sum_func_check()
- thd reference to the thread context info
+/**
+ Prepare an aggregate function item for checking context conditions.
- DESCRIPTION
The function initializes the members of the Item_sum object created
for a set function that are used to check validity of the set function
occurrence.
If the set function is not allowed in any subquery where it occurs
an error is reported immediately.
- NOTES
+ @param thd reference to the thread context info
+
+ @note
This function is to be called for any item created for a set function
object when the traversal of trees built for expressions used in the query
is performed at the phase of context analysis. This function is to
be invoked at the descent of this traversal.
-
- RETURN
- TRUE if an error is reported
+ @retval
+ TRUE if an error is reported
+ @retval
FALSE otherwise
*/
@@ -70,15 +72,9 @@ bool Item_sum::init_sum_func_check(THD *thd)
return FALSE;
}
-/*
- Check constraints imposed on a usage of a set function
-
- SYNOPSIS
- check_sum_func()
- thd reference to the thread context info
- ref location of the pointer to this item in the embedding expression
+/**
+ Check constraints imposed on a usage of a set function.
- DESCRIPTION
The method verifies whether context conditions imposed on a usage
of any set function are met for this occurrence.
It checks whether the set function occurs in the position where it
@@ -90,13 +86,6 @@ bool Item_sum::init_sum_func_check(THD *thd)
adds it to the chain of items for such set functions that is attached
to the the st_select_lex structure for this subquery.
- NOTES
- This function is to be called for any item created for a set function
- object when the traversal of trees built for expressions used in the query
- is performed at the phase of context analysis. This function is to
- be invoked at the ascent of this traversal.
-
- IMPLEMENTATION
A number of designated members of the object are used to check the
conditions. They are specified in the comment before the Item_sum
class declaration.
@@ -107,16 +96,28 @@ bool Item_sum::init_sum_func_check(THD *thd)
of set functions are allowed (i.e either in the SELECT list or
in the HAVING clause of the corresponding subquery)
Consider the query:
- SELECT SUM(t1.b) FROM t1 GROUP BY t1.a
- HAVING t1.a IN (SELECT t2.c FROM t2 WHERE AVG(t1.b) > 20) AND
- t1.a > (SELECT MIN(t2.d) FROM t2);
+ @code
+ SELECT SUM(t1.b) FROM t1 GROUP BY t1.a
+ HAVING t1.a IN (SELECT t2.c FROM t2 WHERE AVG(t1.b) > 20) AND
+ t1.a > (SELECT MIN(t2.d) FROM t2);
+ @endcode
allow_sum_func will contain:
- for SUM(t1.b) - 1 at the first position
- for AVG(t1.b) - 1 at the first position, 0 at the second position
- for MIN(t2.d) - 1 at the first position, 1 at the second position.
+ - for SUM(t1.b) - 1 at the first position
+ - for AVG(t1.b) - 1 at the first position, 0 at the second position
+ - for MIN(t2.d) - 1 at the first position, 1 at the second position.
+
+ @param thd reference to the thread context info
+ @param ref location of the pointer to this item in the embedding expression
+
+ @note
+ This function is to be called for any item created for a set function
+ object when the traversal of trees built for expressions used in the query
+ is performed at the phase of context analysis. This function is to
+ be invoked at the ascent of this traversal.
- RETURN
- TRUE if an error is reported
+ @retval
+ TRUE if an error is reported
+ @retval
FALSE otherwise
*/
@@ -264,15 +265,9 @@ bool Item_sum::check_sum_func(THD *thd, Item **ref)
return FALSE;
}
-/*
- Attach a set function to the subquery where it must be aggregated
+/**
+ Attach a set function to the subquery where it must be aggregated.
- SYNOPSIS
- register_sum_func()
- thd reference to the thread context info
- ref location of the pointer to this item in the embedding expression
-
- DESCRIPTION
The function looks for an outer subquery where the set function must be
aggregated. If it finds such a subquery then aggr_level is set to
the nest level of this subquery and the item for the set function
@@ -280,14 +275,18 @@ bool Item_sum::check_sum_func(THD *thd, Item **ref)
inner_sum_func_list defined for each subquery. When the item is placed
there the field 'ref_by' is set to ref.
- NOTES.
+ @note
Now we 'register' only set functions that are aggregated in outer
subqueries. Actually it makes sense to link all set function for
a subquery in one chain. It would simplify the process of 'splitting'
for set functions.
- RETURN
+ @param thd reference to the thread context info
+ @param ref location of the pointer to this item in the embedding expression
+
+ @retval
FALSE if the executes without failures (currently always)
+ @retval
TRUE otherwise
*/
@@ -379,8 +378,8 @@ Item_sum::Item_sum(List<Item> &list) :arg_count(list.elements),
}
-/*
- Constructor used in processing select with temporary tebles
+/**
+ Constructor used in processing select with temporary tebles.
*/
Item_sum::Item_sum(THD *thd, Item_sum *item):
@@ -438,7 +437,7 @@ void Item_sum::make_field(Send_field *tmp_field)
}
-void Item_sum::print(String *str)
+void Item_sum::print(String *str, enum_query_type query_type)
{
Item **pargs= orig_args;
str->append(func_name());
@@ -446,7 +445,7 @@ void Item_sum::print(String *str)
{
if (i)
str->append(',');
- pargs[i]->print(str);
+ pargs[i]->print(str, query_type);
}
str->append(')');
}
@@ -481,14 +480,15 @@ Item *Item_sum::get_tmp_table_item(THD *thd)
}
-bool Item_sum::walk (Item_processor processor, byte *argument)
+bool Item_sum::walk (Item_processor processor, bool walk_subquery,
+ uchar *argument)
{
if (arg_count)
{
Item **arg,**arg_end;
for (arg= args, arg_end= args+arg_count; arg != arg_end; arg++)
{
- if ((*arg)->walk(processor, argument))
+ if ((*arg)->walk(processor, walk_subquery, argument))
return 1;
}
}
@@ -499,32 +499,35 @@ bool Item_sum::walk (Item_processor processor, byte *argument)
Field *Item_sum::create_tmp_field(bool group, TABLE *table,
uint convert_blob_length)
{
+ Field *field;
switch (result_type()) {
case REAL_RESULT:
- return new Field_double(max_length, maybe_null, name, table, decimals,
- TRUE);
+ field= new Field_double(max_length, maybe_null, name, decimals, TRUE);
+ break;
case INT_RESULT:
- return new Field_longlong(max_length,maybe_null,name,table,unsigned_flag);
+ field= new Field_longlong(max_length, maybe_null, name, unsigned_flag);
+ break;
case STRING_RESULT:
- /*
- Make sure that the blob fits into a Field_varstring which has
- 2-byte lenght.
- */
- if (max_length/collation.collation->mbmaxlen > 255 &&
- convert_blob_length <= Field_varstring::MAX_SIZE && convert_blob_length)
- return new Field_varstring(convert_blob_length, maybe_null,
- name, table,
- collation.collation);
- return make_string_field(table);
-case DECIMAL_RESULT:
- return new Field_new_decimal(max_length, maybe_null, name, table,
+ if (max_length/collation.collation->mbmaxlen <= 255 ||
+ convert_blob_length > Field_varstring::MAX_SIZE ||
+ !convert_blob_length)
+ return make_string_field(table);
+ field= new Field_varstring(convert_blob_length, maybe_null,
+ name, table->s, collation.collation);
+ break;
+ case DECIMAL_RESULT:
+ field= new Field_new_decimal(max_length, maybe_null, name,
decimals, unsigned_flag);
+ break;
case ROW_RESULT:
default:
// This case should never be choosen
DBUG_ASSERT(0);
return 0;
}
+ if (field)
+ field->init(table);
+ return field;
}
@@ -651,8 +654,7 @@ Item_sum_hybrid::fix_fields(THD *thd, Item **ref)
return TRUE;
// 'item' can be changed during fix_fields
- if (!item->fixed &&
- item->fix_fields(thd, args) ||
+ if ((!item->fixed && item->fix_fields(thd, args)) ||
(item= args[0])->check_cols(1))
return TRUE;
decimals=item->decimals;
@@ -701,9 +703,10 @@ Item_sum_hybrid::fix_fields(THD *thd, Item **ref)
Field *Item_sum_hybrid::create_tmp_field(bool group, TABLE *table,
uint convert_blob_length)
{
+ Field *field;
if (args[0]->type() == Item::FIELD_ITEM)
{
- Field *field= ((Item_field*) args[0])->field;
+ field= ((Item_field*) args[0])->field;
if ((field= create_tmp_field_from_field(current_thd, field, name, table,
NULL, convert_blob_length)))
@@ -717,16 +720,21 @@ Field *Item_sum_hybrid::create_tmp_field(bool group, TABLE *table,
*/
switch (args[0]->field_type()) {
case MYSQL_TYPE_DATE:
- return new Field_newdate(maybe_null, name, table, collation.collation);
+ field= new Field_newdate(maybe_null, name, collation.collation);
+ break;
case MYSQL_TYPE_TIME:
- return new Field_time(maybe_null, name, table, collation.collation);
+ field= new Field_time(maybe_null, name, collation.collation);
+ break;
case MYSQL_TYPE_TIMESTAMP:
case MYSQL_TYPE_DATETIME:
- return new Field_datetime(maybe_null, name, table, collation.collation);
- default:
+ field= new Field_datetime(maybe_null, name, collation.collation);
break;
+ default:
+ return Item_sum::create_tmp_field(group, table, convert_blob_length);
}
- return Item_sum::create_tmp_field(group, table, convert_blob_length);
+ if (field)
+ field->init(table);
+ return field;
}
@@ -734,6 +742,10 @@ Field *Item_sum_hybrid::create_tmp_field(bool group, TABLE *table,
** reset and add of sum_func
***********************************************************************/
+/**
+ @todo
+ check if the following assignments are really needed
+*/
Item_sum_sum::Item_sum_sum(THD *thd, Item_sum_sum *item)
:Item_sum_num(thd, item), hybrid_type(item->hybrid_type),
curr_dec_buff(item->curr_dec_buff)
@@ -884,7 +896,7 @@ static int simple_raw_key_cmp(void* arg, const void* key1, const void* key2)
static int item_sum_distinct_walk(void *element, element_count num_of_dups,
void *item)
{
- return ((Item_sum_distinct*) (item))->unique_walk_function(element);
+ return ((Item_sum_distinct*) (item))->unique_walk_function(element);
}
C_MODE_END
@@ -912,7 +924,7 @@ Item_sum_distinct::Item_sum_distinct(THD *thd, Item_sum_distinct *original)
}
-/*
+/**
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.
@@ -968,8 +980,8 @@ void Item_sum_distinct::fix_length_and_dec()
integers each <= 2^32.
*/
if (table_field_type == MYSQL_TYPE_INT24 ||
- table_field_type >= MYSQL_TYPE_TINY &&
- table_field_type <= MYSQL_TYPE_LONG)
+ (table_field_type >= MYSQL_TYPE_TINY &&
+ table_field_type <= MYSQL_TYPE_LONG))
{
val.traits= Hybrid_type_traits_fast_decimal::instance();
break;
@@ -989,10 +1001,14 @@ void Item_sum_distinct::fix_length_and_dec()
}
+/**
+ @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 */
+ 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)
@@ -1238,6 +1254,7 @@ Item *Item_sum_avg::copy_or_same(THD* thd)
Field *Item_sum_avg::create_tmp_field(bool group, TABLE *table,
uint convert_blob_len)
{
+ Field *field;
if (group)
{
/*
@@ -1245,14 +1262,18 @@ Field *Item_sum_avg::create_tmp_field(bool group, TABLE *table,
The easiest way is to do this is to store both value in a string
and unpack on access.
*/
- return new Field_string(((hybrid_type == DECIMAL_RESULT) ?
+ field= new Field_string(((hybrid_type == DECIMAL_RESULT) ?
dec_bin_size : sizeof(double)) + sizeof(longlong),
- 0, name, table, &my_charset_bin);
+ 0, name, &my_charset_bin);
}
- if (hybrid_type == DECIMAL_RESULT)
- return new Field_new_decimal(max_length, maybe_null, name, table,
+ else if (hybrid_type == DECIMAL_RESULT)
+ field= new Field_new_decimal(max_length, maybe_null, name,
decimals, unsigned_flag);
- return new Field_double(max_length, maybe_null, name, table, decimals, TRUE);
+ else
+ field= new Field_double(max_length, maybe_null, name, decimals, TRUE);
+ if (field)
+ field->init(table);
+ return field;
}
@@ -1445,6 +1466,7 @@ Item *Item_sum_variance::copy_or_same(THD* thd)
Field *Item_sum_variance::create_tmp_field(bool group, TABLE *table,
uint convert_blob_len)
{
+ Field *field;
if (group)
{
/*
@@ -1452,9 +1474,15 @@ Field *Item_sum_variance::create_tmp_field(bool group, TABLE *table,
The easiest way is to do this is to store both value in a string
and unpack on access.
*/
- return new Field_string(sizeof(double)*2 + sizeof(longlong), 0, name, table, &my_charset_bin);
+ field= new Field_string(sizeof(double)*2 + sizeof(longlong), 0, name, &my_charset_bin);
}
- return new Field_double(max_length, maybe_null, name, table, decimals, TRUE);
+ else
+ field= new Field_double(max_length, maybe_null, name, decimals, TRUE);
+
+ if (field != NULL)
+ field->init(table);
+
+ return field;
}
@@ -1511,7 +1539,7 @@ my_decimal *Item_sum_variance::val_decimal(my_decimal *dec_buf)
void Item_sum_variance::reset_field()
{
double nr;
- char *res= result_field->ptr;
+ uchar *res= result_field->ptr;
nr= args[0]->val_real(); /* sets null_value as side-effect */
@@ -1534,7 +1562,7 @@ void Item_sum_variance::reset_field()
void Item_sum_variance::update_field()
{
ulonglong field_count;
- char *res=result_field->ptr;
+ uchar *res=result_field->ptr;
double nr= args[0]->val_real(); /* sets null_value as side-effect */
@@ -1665,16 +1693,13 @@ Item_sum_hybrid::val_str(String *str)
case STRING_RESULT:
return &value;
case REAL_RESULT:
- str->set(sum,decimals, &my_charset_bin);
+ str->set_real(sum,decimals, &my_charset_bin);
break;
case DECIMAL_RESULT:
my_decimal2string(E_DEC_FATAL_ERROR, &sum_dec, 0, 0, 0, str);
return str;
case INT_RESULT:
- if (unsigned_flag)
- str->set((ulonglong) sum_int, &my_charset_bin);
- else
- str->set((longlong) sum_int, &my_charset_bin);
+ str->set_int(sum_int, unsigned_flag, &my_charset_bin);
break;
case ROW_RESULT:
default:
@@ -1901,7 +1926,7 @@ bool Item_sum_and::add()
void Item_sum_num::reset_field()
{
double nr= args[0]->val_real();
- char *res=result_field->ptr;
+ uchar *res=result_field->ptr;
if (maybe_null)
{
@@ -2023,7 +2048,7 @@ void Item_sum_sum::reset_field()
void Item_sum_count::reset_field()
{
- char *res=result_field->ptr;
+ uchar *res=result_field->ptr;
longlong nr=0;
if (!args[0]->maybe_null || !args[0]->is_null())
@@ -2034,7 +2059,7 @@ void Item_sum_count::reset_field()
void Item_sum_avg::reset_field()
{
- char *res=result_field->ptr;
+ uchar *res=result_field->ptr;
if (hybrid_type == DECIMAL_RESULT)
{
longlong tmp;
@@ -2075,15 +2100,15 @@ void Item_sum_bit::reset_field()
void Item_sum_bit::update_field()
{
- char *res=result_field->ptr;
+ uchar *res=result_field->ptr;
bits= uint8korr(res);
add();
int8store(res, bits);
}
-/*
-** calc next value and merge it with field_value
+/**
+ calc next value and merge it with field_value.
*/
void Item_sum_sum::update_field()
@@ -2110,7 +2135,7 @@ void Item_sum_sum::update_field()
else
{
double old_nr,nr;
- char *res=result_field->ptr;
+ uchar *res=result_field->ptr;
float8get(old_nr,res);
nr= args[0]->val_real();
@@ -2127,7 +2152,7 @@ void Item_sum_sum::update_field()
void Item_sum_count::update_field()
{
longlong nr;
- char *res=result_field->ptr;
+ uchar *res=result_field->ptr;
nr=sint8korr(res);
if (!args[0]->maybe_null || !args[0]->is_null())
@@ -2139,7 +2164,7 @@ void Item_sum_count::update_field()
void Item_sum_avg::update_field()
{
longlong field_count;
- char *res=result_field->ptr;
+ uchar *res=result_field->ptr;
if (hybrid_type == DECIMAL_RESULT)
{
my_decimal value, *arg_val= args[0]->val_decimal(&value);
@@ -2259,6 +2284,10 @@ Item_sum_hybrid::min_max_update_int_field()
}
+/**
+ @todo
+ optimize: do not get result_field in case of args[0] is NULL
+*/
void
Item_sum_hybrid::min_max_update_decimal_field()
{
@@ -2308,7 +2337,7 @@ double Item_avg_field::val_real()
// fix_fields() never calls for this Item
double nr;
longlong count;
- char *res;
+ uchar *res;
if (hybrid_type == DECIMAL_RESULT)
return val_real_from_decimal();
@@ -2441,20 +2470,20 @@ double Item_variance_field::val_real()
** COUNT(DISTINCT ...)
****************************************************************************/
-int simple_str_key_cmp(void* arg, byte* key1, byte* key2)
+int simple_str_key_cmp(void* arg, uchar* key1, uchar* key2)
{
Field *f= (Field*) arg;
- return f->cmp((const char*)key1, (const char*)key2);
+ 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, byte* key1, byte* key2)
+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;
@@ -2464,7 +2493,7 @@ int composite_key_cmp(void* arg, byte* key1, byte* key2)
{
Field* f = *field;
int len = *lengths++;
- int res = f->cmp((char *) key1, (char *) key2);
+ int res = f->cmp(key1, key2);
if (res)
return res;
key1 += len;
@@ -2514,7 +2543,10 @@ void Item_sum_count_distinct::cleanup()
}
-/* This is used by rollup to create a separate usable copy of the function */
+/**
+ This is used by rollup to create a separate usable copy of
+ the function.
+*/
void Item_sum_count_distinct::make_unique()
{
@@ -2589,7 +2621,7 @@ bool Item_sum_count_distinct::setup(THD *thd)
table->file->extra(HA_EXTRA_NO_ROWS); // Don't update rows
table->no_rows=1;
- if (table->s->db_type == DB_TYPE_HEAP)
+ if (table->s->db_type() == heap_hton)
{
/*
No blobs, otherwise it would have been MyISAM: set up a compare
@@ -2681,7 +2713,7 @@ void Item_sum_count_distinct::clear()
else if (table)
{
table->file->extra(HA_EXTRA_NO_CACHE);
- table->file->delete_all_rows();
+ table->file->ha_delete_all_rows();
table->file->extra(HA_EXTRA_WRITE_CACHE);
}
}
@@ -2709,9 +2741,8 @@ bool Item_sum_count_distinct::add()
*/
return tree->unique_add(table->record[0] + table->s->null_bytes);
}
- if ((error= table->file->write_row(table->record[0])) &&
- error != HA_ERR_FOUND_DUPP_KEY &&
- error != HA_ERR_FOUND_DUPP_UNIQUE)
+ if ((error= table->file->ha_write_row(table->record[0])) &&
+ table->file->is_fatal_error(error, HA_CHECK_DUP))
return TRUE;
return FALSE;
}
@@ -2743,7 +2774,7 @@ longlong Item_sum_count_distinct::val_int()
table->file->print_error(error, MYF(0));
}
- return table->file->records;
+ return table->file->stats.records;
}
@@ -2781,7 +2812,7 @@ void Item_udf_sum::cleanup()
}
-void Item_udf_sum::print(String *str)
+void Item_udf_sum::print(String *str, enum_query_type query_type)
{
str->append(func_name());
str->append('(');
@@ -2789,7 +2820,7 @@ void Item_udf_sum::print(String *str)
{
if (i)
str->append(',');
- args[i]->print(str);
+ args[i]->print(str, query_type);
}
str->append(')');
}
@@ -2883,7 +2914,7 @@ my_decimal *Item_sum_udf_int::val_decimal(my_decimal *dec)
}
-/* Default max_length is max argument length */
+/** Default max_length is max argument length. */
void Item_sum_udf_str::fix_length_and_dec()
{
@@ -2970,17 +3001,16 @@ int group_concat_key_cmp_with_distinct(void* arg, const void* key1,
*/
Field *field= item->get_tmp_table_field();
int res;
- uint offset= field->offset()-table->s->null_bytes;
- if((res= field->cmp((char*)key1 + offset, (char*)key2 + offset)))
+ uint offset= field->offset(field->table->record[0])-table->s->null_bytes;
+ if((res= field->cmp((uchar*)key1 + offset, (uchar*)key2 + offset)))
return res;
}
return 0;
}
-/*
- function of sort for syntax:
- GROUP_CONCAT(expr,... ORDER BY col,... )
+/**
+ function of sort for syntax: GROUP_CONCAT(expr,... ORDER BY col,... )
*/
int group_concat_key_cmp_with_order(void* arg, const void* key1,
@@ -3008,8 +3038,9 @@ int group_concat_key_cmp_with_order(void* arg, const void* key1,
if (field && !item->const_item())
{
int res;
- uint offset= field->offset() - table->s->null_bytes;
- if ((res= field->cmp((char *) key1 + offset, (char *) key2 + offset)))
+ uint offset= (field->offset(field->table->record[0]) -
+ table->s->null_bytes);
+ if ((res= field->cmp((uchar*)key1 + offset, (uchar*)key2 + offset)))
return (*order_item)->asc ? res : -res;
}
}
@@ -3022,11 +3053,11 @@ int group_concat_key_cmp_with_order(void* arg, const void* key1,
}
-/*
- Append data from current leaf to item->result
+/**
+ Append data from current leaf to item->result.
*/
-int dump_leaf_key(byte* key, element_count count __attribute__((unused)),
+int dump_leaf_key(uchar* key, element_count count __attribute__((unused)),
Item_func_group_concat *item)
{
TABLE *table= item->table;
@@ -3057,9 +3088,10 @@ int dump_leaf_key(byte* key, element_count count __attribute__((unused)),
because it contains both order and arg list fields.
*/
Field *field= (*arg)->get_tmp_table_field();
- uint offset= field->offset() - table->s->null_bytes;
+ uint offset= (field->offset(field->table->record[0]) -
+ table->s->null_bytes);
DBUG_ASSERT(offset < table->s->reclength);
- res= field->val_str(&tmp, (char *) key + offset);
+ res= field->val_str(&tmp, key + offset);
}
else
res= (*arg)->val_str(&tmp);
@@ -3093,12 +3125,13 @@ int dump_leaf_key(byte* key, element_count count __attribute__((unused)),
}
-/*
- Constructor of Item_func_group_concat
- distinct_arg - distinct
- select_list - list of expression for show values
- order_list - list of sort columns
- separator_arg - string value of separator
+/**
+ Constructor of Item_func_group_concat.
+
+ @param distinct_arg distinct
+ @param select_list list of expression for show values
+ @param order_list list of sort columns
+ @param separator_arg string value of separator.
*/
Item_func_group_concat::
@@ -3349,7 +3382,7 @@ Item_func_group_concat::fix_fields(THD *thd, Item **ref)
char *buf;
String *new_separator;
- if (!(buf= thd->stmt_arena->alloc(buflen)) ||
+ if (!(buf= (char*) thd->stmt_arena->alloc(buflen)) ||
!(new_separator= new(thd->stmt_arena->mem_root)
String(buf, buflen, collation.collation)))
return TRUE;
@@ -3530,7 +3563,7 @@ String* Item_func_group_concat::val_str(String* str)
}
-void Item_func_group_concat::print(String *str)
+void Item_func_group_concat::print(String *str, enum_query_type query_type)
{
str->append(STRING_WITH_LEN("group_concat("));
if (distinct)
@@ -3539,7 +3572,7 @@ void Item_func_group_concat::print(String *str)
{
if (i)
str->append(',');
- args[i]->print(str);
+ args[i]->print(str, query_type);
}
if (arg_count_order)
{
@@ -3548,7 +3581,7 @@ void Item_func_group_concat::print(String *str)
{
if (i)
str->append(',');
- (*order[i]->item)->print(str);
+ (*order[i]->item)->print(str, query_type);
if (order[i]->asc)
str->append(STRING_WITH_LEN(" ASC"));
else
diff --git a/sql/item_sum.h b/sql/item_sum.h
index 51a1eff9bbf..d991327d847 100644
--- a/sql/item_sum.h
+++ b/sql/item_sum.h
@@ -224,7 +224,7 @@ class Item_sum :public Item_result_field
public:
enum Sumfunctype
{ COUNT_FUNC, COUNT_DISTINCT_FUNC, SUM_FUNC, SUM_DISTINCT_FUNC, AVG_FUNC,
- AVG_DISTINCT_FUNC, MIN_FUNC, MAX_FUNC, UNIQUE_USERS_FUNC, STD_FUNC,
+ AVG_DISTINCT_FUNC, MIN_FUNC, MAX_FUNC, STD_FUNC,
VARIANCE_FUNC, SUM_BIT_FUNC, UDF_SUM_FUNC, GROUP_CONCAT_FUNC
};
@@ -356,7 +356,7 @@ public:
}
virtual bool const_item() const { return forced_const; }
void make_field(Send_field *field);
- void print(String *str);
+ virtual void print(String *str, enum_query_type query_type);
void fix_num_length_and_dec();
/*
@@ -374,7 +374,7 @@ public:
Item *get_tmp_table_item(THD *thd);
virtual Field *create_tmp_field(bool group, TABLE *table,
uint convert_blob_length);
- bool walk (Item_processor processor, byte *argument);
+ bool walk(Item_processor processor, bool walk_subquery, uchar *argument);
bool init_sum_func_check(THD *thd);
bool check_sum_func(THD *thd, Item **ref);
bool register_sum_func(THD *thd, Item **ref);
@@ -604,8 +604,8 @@ class Item_sum_count_distinct :public Item_sum_int
bool always_null; // Set to 1 if the result is always NULL
- friend int composite_key_cmp(void* arg, byte* key1, byte* key2);
- friend int simple_str_key_cmp(void* arg, byte* key1, byte* key2);
+ 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)
@@ -848,7 +848,7 @@ protected:
public:
Item_sum_hybrid(Item *item_par,int sign)
:Item_sum(item_par), sum(0.0), sum_int(0),
- hybrid_type(INT_RESULT), hybrid_field_type(FIELD_TYPE_LONGLONG),
+ hybrid_type(INT_RESULT), hybrid_field_type(MYSQL_TYPE_LONGLONG),
cmp_sign(sign), was_values(TRUE)
{ collation.set(&my_charset_bin); }
Item_sum_hybrid(THD *thd, Item_sum_hybrid *item);
@@ -1002,7 +1002,7 @@ public:
void reset_field() {};
void update_field() {};
void cleanup();
- void print(String *str);
+ virtual void print(String *str, enum_query_type query_type);
};
@@ -1223,7 +1223,7 @@ 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(byte* key,
+ friend int dump_leaf_key(uchar* key,
element_count count __attribute__((unused)),
Item_func_group_concat *group_concat_item);
@@ -1242,7 +1242,7 @@ public:
enum_field_types field_type() const
{
if (max_length/collation.collation->mbmaxlen > CONVERT_IF_BIGGER_TO_BLOB )
- return FIELD_TYPE_BLOB;
+ return MYSQL_TYPE_BLOB;
else
return MYSQL_TYPE_VARCHAR;
}
@@ -1275,7 +1275,7 @@ public:
String* val_str(String* str);
Item *copy_or_same(THD* thd);
void no_rows_in_result() {}
- void print(String *str);
- virtual bool change_context_processor(byte *cntx)
+ virtual void print(String *str, enum_query_type query_type);
+ virtual bool change_context_processor(uchar *cntx)
{ context= (Name_resolution_context *)cntx; return FALSE; }
};
diff --git a/sql/item_timefunc.cc b/sql/item_timefunc.cc
index 22ca8a925db..8caff22eab9 100644
--- a/sql/item_timefunc.cc
+++ b/sql/item_timefunc.cc
@@ -14,7 +14,15 @@
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
-/* This file defines all time functions */
+/**
+ @file
+
+ @brief
+ This file defines all time functions
+
+ @todo
+ Move month and days to language files
+*/
#ifdef USE_PRAGMA_IMPLEMENTATION
#pragma implementation // gcc: Class implementation
@@ -24,17 +32,16 @@
#include <m_ctype.h>
#include <time.h>
-/* TODO: Move month and days to language files */
-
-/* Day number for Dec 31st, 9999 */
+/** Day number for Dec 31st, 9999. */
#define MAX_DAY_NUMBER 3652424L
-/*
- OPTIMIZATION TODO:
- - Replace the switch with a function that should be called for each
- date type.
- - Remove sprintf and opencode the conversion, like we do in
- Field_datetime.
+/**
+ @todo
+ OPTIMIZATION
+ - Replace the switch with a function that should be called for each
+ date type.
+ - Remove sprintf and opencode the conversion, like we do in
+ Field_datetime.
The reason for this functions existence is that as we don't have a
way to know if a datetime/time value has microseconds in them
@@ -226,37 +233,37 @@ static DATE_TIME_FORMAT time_ampm_format= {{0}, '\0', 0,
static DATE_TIME_FORMAT time_24hrs_format= {{0}, '\0', 0,
{(char *)"%H:%i:%S", 8}};
-/*
+/**
Extract datetime value to MYSQL_TIME struct from string value
- according to format string.
+ according to format string.
- SYNOPSIS
- extract_date_time()
- format date/time format specification
- val String to decode
- length Length of string
- l_time Store result here
- cached_timestamp_type
- It uses to get an appropriate warning
- in the case when the value is truncated.
- sub_pattern_end if non-zero then we are parsing string which
- should correspond compound specifier (like %T or
- %r) and this parameter is pointer to place where
- pointer to end of string matching this specifier
- should be stored.
- NOTE
- Possibility to parse strings matching to patterns equivalent to compound
- specifiers is mainly intended for use from inside of this function in
- order to understand %T and %r conversion specifiers, so number of
- conversion specifiers that can be used in such sub-patterns is limited.
- Also most of checks are skipped in this case.
-
- If one adds new format specifiers to this function he should also
- consider adding them to get_date_time_result_type() function.
-
- RETURN
- 0 ok
- 1 error
+ @param format date/time format specification
+ @param val String to decode
+ @param length Length of string
+ @param l_time Store result here
+ @param cached_timestamp_type It uses to get an appropriate warning
+ in the case when the value is truncated.
+ @param sub_pattern_end if non-zero then we are parsing string which
+ should correspond compound specifier (like %T or
+ %r) and this parameter is pointer to place where
+ pointer to end of string matching this specifier
+ should be stored.
+
+ @note
+ Possibility to parse strings matching to patterns equivalent to compound
+ specifiers is mainly intended for use from inside of this function in
+ order to understand %T and %r conversion specifiers, so number of
+ conversion specifiers that can be used in such sub-patterns is limited.
+ Also most of checks are skipped in this case.
+
+ @note
+ If one adds new format specifiers to this function he should also
+ consider adding them to get_date_time_result_type() function.
+
+ @retval
+ 0 ok
+ @retval
+ 1 error
*/
static bool extract_date_time(DATE_TIME_FORMAT *format,
@@ -597,14 +604,14 @@ err:
strmake(buff, val_begin, min(length, sizeof(buff)-1));
push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_ERROR,
ER_WRONG_VALUE_FOR_TYPE, ER(ER_WRONG_VALUE_FOR_TYPE),
- date_time_type, buff, "str_to_time");
+ date_time_type, buff, "str_to_date");
}
DBUG_RETURN(1);
}
-/*
- Create a formated date/time value in a string
+/**
+ Create a formated date/time value in a string.
*/
bool make_date_time(DATE_TIME_FORMAT *format, MYSQL_TIME *l_time,
@@ -838,7 +845,8 @@ bool make_date_time(DATE_TIME_FORMAT *format, MYSQL_TIME *l_time,
}
-/*
+/**
+ @details
Get a array of positive numbers from a string object.
Each number is separated by 1 non digit character
Return error if there is too many numbers.
@@ -846,16 +854,14 @@ bool make_date_time(DATE_TIME_FORMAT *format, MYSQL_TIME *l_time,
from the high end. This allows one to give:
DAY_TO_SECOND as "D MM:HH:SS", "MM:HH:SS" "HH:SS" or as seconds.
- SYNOPSIS
- str: string value
- length: length of str
- cs: charset of str
- values: array of results
- count: count of elements in result array
- transform_msec: if value is true we suppose
- that the last part of string value is microseconds
- and we should transform value to six digit value.
- For example, '1.1' -> '1.100000'
+ @param length: length of str
+ @param cs: charset of str
+ @param values: array of results
+ @param count: count of elements in result array
+ @param transform_msec: if value is true we suppose
+ that the last part of string value is microseconds
+ and we should transform value to six digit value.
+ For example, '1.1' -> '1.100000'
*/
static bool get_interval_info(const char *str,uint length,CHARSET_INFO *cs,
@@ -886,9 +892,9 @@ static bool get_interval_info(const char *str,uint length,CHARSET_INFO *cs,
{
i++;
/* Change values[0...i-1] -> values[0...count-1] */
- bmove_upp((char*) (values+count), (char*) (values+i),
+ bmove_upp((uchar*) (values+count), (uchar*) (values+i),
sizeof(*values)*i);
- bzero((char*) values, sizeof(*values)*(count-i));
+ bzero((uchar*) values, sizeof(*values)*(count-i));
break;
}
}
@@ -896,81 +902,6 @@ static bool get_interval_info(const char *str,uint length,CHARSET_INFO *cs,
}
-/*
- Calculate difference between two datetime values as seconds + microseconds.
-
- SYNOPSIS
- calc_time_diff()
- l_time1 - TIME/DATE/DATETIME value
- l_time2 - TIME/DATE/DATETIME value
- l_sign - 1 absolute values are substracted,
- -1 absolute values are added.
- seconds_out - Out parameter where difference between
- l_time1 and l_time2 in seconds is stored.
- microseconds_out- Out parameter where microsecond part of difference
- between l_time1 and l_time2 is stored.
-
- NOTE
- This function calculates difference between l_time1 and l_time2 absolute
- values. So one should set l_sign and correct result if he want to take
- signs into account (i.e. for TIME values).
-
- RETURN VALUES
- Returns sign of difference.
- 1 means negative result
- 0 means positive result
-
-*/
-
-bool calc_time_diff(MYSQL_TIME *l_time1, MYSQL_TIME *l_time2, int l_sign,
- longlong *seconds_out, long *microseconds_out)
-{
- long days;
- bool neg;
- longlong microseconds;
-
- /*
- We suppose that if first argument is MYSQL_TIMESTAMP_TIME
- the second argument should be TIMESTAMP_TIME also.
- We should check it before calc_time_diff call.
- */
- if (l_time1->time_type == MYSQL_TIMESTAMP_TIME) // Time value
- days= (long)l_time1->day - l_sign * (long)l_time2->day;
- else
- {
- days= calc_daynr((uint) l_time1->year,
- (uint) l_time1->month,
- (uint) l_time1->day);
- if (l_time2->time_type == MYSQL_TIMESTAMP_TIME)
- days-= l_sign * (long)l_time2->day;
- else
- days-= l_sign*calc_daynr((uint) l_time2->year,
- (uint) l_time2->month,
- (uint) l_time2->day);
- }
-
- microseconds= ((longlong)days*LL(86400) +
- (longlong)(l_time1->hour*3600L +
- l_time1->minute*60L +
- l_time1->second) -
- l_sign*(longlong)(l_time2->hour*3600L +
- l_time2->minute*60L +
- l_time2->second)) * LL(1000000) +
- (longlong)l_time1->second_part -
- l_sign*(longlong)l_time2->second_part;
-
- neg= 0;
- if (microseconds < 0)
- {
- microseconds= -microseconds;
- neg= 1;
- }
- *seconds_out= microseconds/1000000L;
- *microseconds_out= (long) (microseconds%1000000L);
- return neg;
-}
-
-
longlong Item_func_period_add::val_int()
{
DBUG_ASSERT(fixed == 1);
@@ -1009,6 +940,72 @@ longlong Item_func_to_days::val_int()
return (longlong) calc_daynr(ltime.year,ltime.month,ltime.day);
}
+
+/*
+ Get information about this Item tree monotonicity
+
+ SYNOPSIS
+ Item_func_to_days::get_monotonicity_info()
+
+ DESCRIPTION
+ Get information about monotonicity of the function represented by this item
+ tree.
+
+ RETURN
+ See enum_monotonicity_info.
+*/
+
+enum_monotonicity_info Item_func_to_days::get_monotonicity_info() const
+{
+ if (args[0]->type() == Item::FIELD_ITEM)
+ {
+ if (args[0]->field_type() == MYSQL_TYPE_DATE)
+ return MONOTONIC_STRICT_INCREASING;
+ if (args[0]->field_type() == MYSQL_TYPE_DATETIME)
+ return MONOTONIC_INCREASING;
+ }
+ return NON_MONOTONIC;
+}
+
+
+longlong Item_func_to_days::val_int_endpoint(bool left_endp, bool *incl_endp)
+{
+ DBUG_ASSERT(fixed == 1);
+ MYSQL_TIME ltime;
+ longlong res;
+ if (get_arg0_date(&ltime, TIME_NO_ZERO_DATE))
+ {
+ /* got NULL, leave the incl_endp intact */
+ return LONGLONG_MIN;
+ }
+ res=(longlong) calc_daynr(ltime.year,ltime.month,ltime.day);
+
+ if (args[0]->field_type() == MYSQL_TYPE_DATE)
+ {
+ // TO_DAYS() is strictly monotonic for dates, leave incl_endp intact
+ return res;
+ }
+
+ /*
+ Handle the special but practically useful case of datetime values that
+ point to day bound ("strictly less" comparison stays intact):
+
+ col < '2007-09-15 00:00:00' -> TO_DAYS(col) < TO_DAYS('2007-09-15')
+
+ which is different from the general case ("strictly less" changes to
+ "less or equal"):
+
+ col < '2007-09-15 12:34:56' -> TO_DAYS(col) <= TO_DAYS('2007-09-15')
+ */
+ if (!left_endp && !(ltime.hour || ltime.minute || ltime.second ||
+ ltime.second_part))
+ ; /* do nothing */
+ else
+ *incl_endp= TRUE;
+ return res;
+}
+
+
longlong Item_func_dayofyear::val_int()
{
DBUG_ASSERT(fixed == 1);
@@ -1069,7 +1066,9 @@ String* Item_func_monthname::val_str(String* str)
}
-// Returns the quarter of the year
+/**
+ Returns the quarter of the year.
+*/
longlong Item_func_quarter::val_int()
{
@@ -1095,8 +1094,10 @@ longlong Item_func_minute::val_int()
(void) get_arg0_time(&ltime);
return ltime.minute;
}
-// Returns the second in time_exp in the range of 0 - 59
+/**
+ Returns the second in time_exp in the range of 0 - 59.
+*/
longlong Item_func_second::val_int()
{
DBUG_ASSERT(fixed == 1);
@@ -1114,7 +1115,8 @@ uint week_mode(uint mode)
return week_format;
}
-/*
+/**
+ @verbatim
The bits in week_format(for calc_week() function) has the following meaning:
WEEK_MONDAY_FIRST (0) If not set Sunday is first day of week
If set Monday is first day of week
@@ -1141,6 +1143,7 @@ uint week_mode(uint mode)
four or more days in the new year, then it is week 1;
Otherwise it is the last week of the previous year, and the
next week is week 1.
+ @endverbatim
*/
longlong Item_func_week::val_int()
@@ -1222,6 +1225,60 @@ longlong Item_func_year::val_int()
}
+/*
+ Get information about this Item tree monotonicity
+
+ SYNOPSIS
+ Item_func_year::get_monotonicity_info()
+
+ DESCRIPTION
+ Get information about monotonicity of the function represented by this item
+ tree.
+
+ RETURN
+ See enum_monotonicity_info.
+*/
+
+enum_monotonicity_info Item_func_year::get_monotonicity_info() const
+{
+ if (args[0]->type() == Item::FIELD_ITEM &&
+ (args[0]->field_type() == MYSQL_TYPE_DATE ||
+ args[0]->field_type() == MYSQL_TYPE_DATETIME))
+ return MONOTONIC_INCREASING;
+ return NON_MONOTONIC;
+}
+
+
+longlong Item_func_year::val_int_endpoint(bool left_endp, bool *incl_endp)
+{
+ DBUG_ASSERT(fixed == 1);
+ MYSQL_TIME ltime;
+ if (get_arg0_date(&ltime, TIME_FUZZY_DATE))
+ {
+ /* got NULL, leave the incl_endp intact */
+ return LONGLONG_MIN;
+ }
+
+ /*
+ Handle the special but practically useful case of datetime values that
+ point to year bound ("strictly less" comparison stays intact) :
+
+ col < '2007-01-01 00:00:00' -> YEAR(col) < 2007
+
+ which is different from the general case ("strictly less" changes to
+ "less or equal"):
+
+ col < '2007-09-15 23:00:00' -> YEAR(col) <= 2007
+ */
+ if (!left_endp && ltime.day == 1 && ltime.month == 1 &&
+ !(ltime.hour || ltime.minute || ltime.second || ltime.second_part))
+ ; /* do nothing */
+ else
+ *incl_endp= TRUE;
+ return ltime.year;
+}
+
+
longlong Item_func_unix_timestamp::val_int()
{
MYSQL_TIME ltime;
@@ -1233,7 +1290,7 @@ longlong Item_func_unix_timestamp::val_int()
if (args[0]->type() == FIELD_ITEM)
{ // Optimize timestamp field
Field *field=((Item_field*) args[0])->field;
- if (field->type() == FIELD_TYPE_TIMESTAMP)
+ if (field->type() == MYSQL_TYPE_TIMESTAMP)
return ((Field_timestamp*) field)->get_timestamp(&null_value);
}
@@ -1263,18 +1320,19 @@ longlong Item_func_time_to_sec::val_int()
}
-/*
- Convert a string to a interval value
+/**
+ Convert a string to a interval value.
+
To make code easy, allow interval objects without separators.
*/
-static bool get_interval_value(Item *args,interval_type int_type,
+bool get_interval_value(Item *args,interval_type int_type,
String *str_value, INTERVAL *interval)
{
ulonglong array[5];
longlong value;
const char *str;
- uint32 length;
+ size_t length;
CHARSET_INFO *cs=str_value->charset();
LINT_INIT(value);
@@ -1309,7 +1367,7 @@ static bool get_interval_value(Item *args,interval_type int_type,
interval->neg=1;
str++;
}
- length=(uint32) (end-str); // Set up pointers to new str
+ length= (size_t) (end-str); // Set up pointers to new str
}
switch (int_type) {
@@ -1416,6 +1474,9 @@ static bool get_interval_value(Item *args,interval_type int_type,
interval->second= array[0];
interval->second_part= array[1];
break;
+ case INTERVAL_LAST: /* purecov: begin deadcode */
+ DBUG_ASSERT(0);
+ break; /* purecov: end */
}
return 0;
}
@@ -1485,7 +1546,7 @@ String *Item_func_curdate::val_str(String *str)
return str;
}
-/*
+/**
Converts current time in my_time_t to MYSQL_TIME represenatation for local
time zone. Defines time zone (local) used for whole CURDATE function.
*/
@@ -1498,7 +1559,7 @@ void Item_func_curdate_local::store_now_in_TIME(MYSQL_TIME *now_time)
}
-/*
+/**
Converts current time in my_time_t to MYSQL_TIME represenatation for UTC
time zone. Defines time zone (UTC) used for whole UTC_DATE function.
*/
@@ -1542,7 +1603,7 @@ void Item_func_curtime::fix_length_and_dec()
}
-/*
+/**
Converts current time in my_time_t to MYSQL_TIME represenatation for local
time zone. Defines time zone (local) used for whole CURTIME function.
*/
@@ -1555,7 +1616,7 @@ void Item_func_curtime_local::store_now_in_TIME(MYSQL_TIME *now_time)
}
-/*
+/**
Converts current time in my_time_t to MYSQL_TIME represenatation for UTC
time zone. Defines time zone (UTC) used for whole UTC_TIME function.
*/
@@ -1591,7 +1652,7 @@ void Item_func_now::fix_length_and_dec()
}
-/*
+/**
Converts current time in my_time_t to MYSQL_TIME represenatation for local
time zone. Defines time zone (local) used for whole NOW function.
*/
@@ -1604,7 +1665,7 @@ void Item_func_now_local::store_now_in_TIME(MYSQL_TIME *now_time)
}
-/*
+/**
Converts current time in my_time_t to MYSQL_TIME represenatation for UTC
time zone. Defines time zone (UTC) used for whole UTC_TIMESTAMP function.
*/
@@ -1634,14 +1695,14 @@ int Item_func_now::save_in_field(Field *to, bool no_conversions)
}
-/*
+/**
Converts current time in my_time_t to MYSQL_TIME represenatation for local
time zone. Defines time zone (local) used for whole SYSDATE function.
*/
void Item_func_sysdate_local::store_now_in_TIME(MYSQL_TIME *now_time)
{
THD *thd= current_thd;
- thd->variables.time_zone->gmt_sec_to_TIME(now_time, (my_time_t) time(NULL));
+ thd->variables.time_zone->gmt_sec_to_TIME(now_time, (my_time_t) my_time(0));
thd->time_zone_used= 1;
}
@@ -1668,7 +1729,7 @@ double Item_func_sysdate_local::val_real()
{
DBUG_ASSERT(fixed == 1);
store_now_in_TIME(&ltime);
- return (double) TIME_to_ulonglong_datetime(&ltime);
+ return ulonglong2double(TIME_to_ulonglong_datetime(&ltime));
}
@@ -1991,19 +2052,6 @@ void Item_func_convert_tz::fix_length_and_dec()
}
-bool
-Item_func_convert_tz::fix_fields(THD *thd_arg, Item **ref)
-{
- String str;
- if (Item_date_func::fix_fields(thd_arg, ref))
- return TRUE;
-
- tz_tables= thd_arg->lex->time_zone_tables_used;
-
- return FALSE;
-}
-
-
String *Item_func_convert_tz::val_str(String *str)
{
MYSQL_TIME time_tmp;
@@ -2039,16 +2087,17 @@ bool Item_func_convert_tz::get_date(MYSQL_TIME *ltime,
{
my_time_t my_time_tmp;
String str;
+ THD *thd= current_thd;
if (!from_tz_cached)
{
- from_tz= my_tz_find(args[1]->val_str(&str), tz_tables);
+ from_tz= my_tz_find(thd, args[1]->val_str(&str));
from_tz_cached= args[1]->const_item();
}
if (!to_tz_cached)
{
- to_tz= my_tz_find(args[2]->val_str(&str), tz_tables);
+ to_tz= my_tz_find(thd, args[2]->val_str(&str));
to_tz_cached= args[2]->const_item();
}
@@ -2117,115 +2166,18 @@ void Item_date_add_interval::fix_length_and_dec()
bool Item_date_add_interval::get_date(MYSQL_TIME *ltime, uint fuzzy_date)
{
- long period,sign;
INTERVAL interval;
- ltime->neg= 0;
if (args[0]->get_date(ltime, TIME_NO_ZERO_DATE) ||
- get_interval_value(args[1],int_type,&value,&interval))
- goto null_date;
- sign= (interval.neg ? -1 : 1);
- if (date_sub_interval)
- sign = -sign;
+ get_interval_value(args[1], int_type, &value, &interval))
+ return (null_value=1);
- null_value=0;
- switch (int_type) {
- case INTERVAL_SECOND:
- case INTERVAL_SECOND_MICROSECOND:
- case INTERVAL_MICROSECOND:
- case INTERVAL_MINUTE:
- case INTERVAL_HOUR:
- case INTERVAL_MINUTE_MICROSECOND:
- case INTERVAL_MINUTE_SECOND:
- case INTERVAL_HOUR_MICROSECOND:
- case INTERVAL_HOUR_SECOND:
- case INTERVAL_HOUR_MINUTE:
- case INTERVAL_DAY_MICROSECOND:
- case INTERVAL_DAY_SECOND:
- case INTERVAL_DAY_MINUTE:
- case INTERVAL_DAY_HOUR:
- {
- longlong sec, days, daynr, microseconds, extra_sec;
- ltime->time_type= MYSQL_TIMESTAMP_DATETIME; // Return full date
- microseconds= ltime->second_part + sign*interval.second_part;
- extra_sec= microseconds/1000000L;
- microseconds= microseconds%1000000L;
-
- sec=((ltime->day-1)*3600*24L+ltime->hour*3600+ltime->minute*60+
- ltime->second +
- sign* (longlong) (interval.day*3600*24L +
- interval.hour*LL(3600)+interval.minute*LL(60)+
- interval.second))+ extra_sec;
- if (microseconds < 0)
- {
- microseconds+= LL(1000000);
- sec--;
- }
- days= sec/(3600*LL(24));
- sec-= days*3600*LL(24);
- if (sec < 0)
- {
- days--;
- sec+= 3600*LL(24);
- }
- ltime->second_part= (uint) microseconds;
- ltime->second= (uint) (sec % 60);
- ltime->minute= (uint) (sec/60 % 60);
- ltime->hour= (uint) (sec/3600);
- daynr= calc_daynr(ltime->year,ltime->month,1) + days;
- /* Day number from year 0 to 9999-12-31 */
- if ((ulonglong) daynr > MAX_DAY_NUMBER)
- goto invalid_date;
- get_date_from_daynr((long) daynr, &ltime->year, &ltime->month,
- &ltime->day);
- break;
- }
- case INTERVAL_DAY:
- case INTERVAL_WEEK:
- period= (calc_daynr(ltime->year,ltime->month,ltime->day) +
- sign * (long) interval.day);
- /* Daynumber from year 0 to 9999-12-31 */
- if ((ulong) period > MAX_DAY_NUMBER)
- goto invalid_date;
- get_date_from_daynr((long) period,&ltime->year,&ltime->month,&ltime->day);
- break;
- case INTERVAL_YEAR:
- ltime->year+= sign * (long) interval.year;
- if ((ulong) ltime->year >= 10000L)
- goto invalid_date;
- if (ltime->month == 2 && ltime->day == 29 &&
- calc_days_in_year(ltime->year) != 366)
- ltime->day=28; // Was leap-year
- break;
- case INTERVAL_YEAR_MONTH:
- case INTERVAL_QUARTER:
- case INTERVAL_MONTH:
- period= (ltime->year*12 + sign * (long) interval.year*12 +
- ltime->month-1 + sign * (long) interval.month);
- if ((ulong) period >= 120000L)
- goto invalid_date;
- ltime->year= (uint) (period / 12);
- ltime->month= (uint) (period % 12L)+1;
- /* Adjust day if the new month doesn't have enough days */
- if (ltime->day > days_in_month[ltime->month-1])
- {
- ltime->day = days_in_month[ltime->month-1];
- if (ltime->month == 2 && calc_days_in_year(ltime->year) == 366)
- ltime->day++; // Leap-year
- }
- break;
- default:
- goto null_date;
- }
- return 0; // Ok
+ if (date_sub_interval)
+ interval.neg = !interval.neg;
-invalid_date:
- push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
- ER_DATETIME_FUNCTION_OVERFLOW,
- ER(ER_DATETIME_FUNCTION_OVERFLOW),
- "datetime");
- null_date:
- return (null_value=1);
+ if ((null_value= date_add_interval(ltime, int_type, interval)))
+ return 1;
+ return 0;
}
@@ -2292,23 +2244,23 @@ static const char *interval_names[]=
"second_microsecond"
};
-void Item_date_add_interval::print(String *str)
+void Item_date_add_interval::print(String *str, enum_query_type query_type)
{
str->append('(');
- args[0]->print(str);
+ args[0]->print(str, query_type);
str->append(date_sub_interval?" - interval ":" + interval ");
- args[1]->print(str);
+ args[1]->print(str, query_type);
str->append(' ');
str->append(interval_names[int_type]);
str->append(')');
}
-void Item_extract::print(String *str)
+void Item_extract::print(String *str, enum_query_type query_type)
{
str->append(STRING_WITH_LEN("extract("));
str->append(interval_names[int_type]);
str->append(STRING_WITH_LEN(" from "));
- args[0]->print(str);
+ args[0]->print(str, query_type);
str->append(')');
}
@@ -2338,6 +2290,7 @@ void Item_extract::fix_length_and_dec()
case INTERVAL_HOUR_MICROSECOND: max_length=13; date_value=0; break;
case INTERVAL_MINUTE_MICROSECOND: max_length=11; date_value=0; break;
case INTERVAL_SECOND_MICROSECOND: max_length=9; date_value=0; break;
+ case INTERVAL_LAST: DBUG_ASSERT(0); break; /* purecov: deadcode */
}
}
@@ -2407,6 +2360,7 @@ longlong Item_extract::val_int()
ltime.second_part)*neg;
case INTERVAL_SECOND_MICROSECOND: return ((longlong)ltime.second*1000000L+
ltime.second_part)*neg;
+ case INTERVAL_LAST: DBUG_ASSERT(0); break; /* purecov: deadcode */
}
return 0; // Impossible
}
@@ -2447,20 +2401,20 @@ bool Item_char_typecast::eq(const Item *item, bool binary_cmp) const
return 1;
}
-void Item_typecast::print(String *str)
+void Item_typecast::print(String *str, enum_query_type query_type)
{
str->append(STRING_WITH_LEN("cast("));
- args[0]->print(str);
+ args[0]->print(str, query_type);
str->append(STRING_WITH_LEN(" as "));
str->append(cast_type());
str->append(')');
}
-void Item_char_typecast::print(String *str)
+void Item_char_typecast::print(String *str, enum_query_type query_type)
{
str->append(STRING_WITH_LEN("cast("));
- args[0]->print(str);
+ args[0]->print(str, query_type);
str->append(STRING_WITH_LEN(" as char"));
if (cast_length >= 0)
{
@@ -2707,7 +2661,7 @@ longlong Item_date_typecast::val_int()
return (longlong) (ltime.year * 10000L + ltime.month * 100 + ltime.day);
}
-/*
+/**
MAKEDATE(a,b) is a date function that creates a date value
from a year and day value.
@@ -2817,7 +2771,7 @@ void Item_func_add_time::fix_length_and_dec()
cached_field_type= MYSQL_TYPE_TIME;
}
-/*
+/**
ADDTIME(t,a) and SUBTIME(t,a) are time functions that calculate a
time/datetime value
@@ -2898,7 +2852,7 @@ null_date:
}
-void Item_func_add_time::print(String *str)
+void Item_func_add_time::print(String *str, enum_query_type query_type)
{
if (is_date)
{
@@ -2912,14 +2866,14 @@ void Item_func_add_time::print(String *str)
else
str->append(STRING_WITH_LEN("subtime("));
}
- args[0]->print(str);
+ args[0]->print(str, query_type);
str->append(',');
- args[1]->print(str);
+ args[1]->print(str, query_type);
str->append(')');
}
-/*
+/**
TIMEDIFF(t,s) is a time function that calculates the
time value between a start and end time.
@@ -2969,7 +2923,7 @@ null_date:
return 0;
}
-/*
+/**
MAKETIME(h,m,s) is a time function that calculates a time value
from the total number of hours, minutes, and seconds.
Result: Time value
@@ -3036,7 +2990,7 @@ String *Item_func_maketime::val_str(String *str)
}
-/*
+/**
MICROSECOND(a) is a function ( extraction) that extracts the microseconds
from a.
@@ -3159,7 +3113,7 @@ null_date:
}
-void Item_func_timestamp_diff::print(String *str)
+void Item_func_timestamp_diff::print(String *str, enum_query_type query_type)
{
str->append(func_name());
str->append('(');
@@ -3199,7 +3153,7 @@ void Item_func_timestamp_diff::print(String *str)
for (uint i=0 ; i < 2 ; i++)
{
str->append(',');
- args[i]->print(str);
+ args[i]->print(str, query_type);
}
str->append(')');
}
@@ -3239,7 +3193,7 @@ String *Item_func_get_format::val_str(String *str)
}
-void Item_func_get_format::print(String *str)
+void Item_func_get_format::print(String *str, enum_query_type query_type)
{
str->append(func_name());
str->append('(');
@@ -3257,30 +3211,33 @@ void Item_func_get_format::print(String *str)
default:
DBUG_ASSERT(0);
}
- args[0]->print(str);
+ args[0]->print(str, query_type);
str->append(')');
}
-/*
+/**
Get type of datetime value (DATE/TIME/...) which will be produced
according to format string.
- SYNOPSIS
- get_date_time_result_type()
- format - format string
- length - length of format string
+ @param format format string
+ @param length length of format string
- NOTE
+ @note
We don't process day format's characters('D', 'd', 'e') because day
may be a member of all date/time types.
+ @note
Format specifiers supported by this function should be in sync with
specifiers supported by extract_date_time() function.
- RETURN VALUE
+ @return
One of date_time_format_types values:
- DATE_TIME_MICROSECOND, DATE_TIME, DATE_ONLY, TIME_MICROSECOND, TIME_ONLY
+ - DATE_TIME_MICROSECOND
+ - DATE_TIME
+ - DATE_ONLY
+ - TIME_MICROSECOND
+ - TIME_ONLY
*/
static date_time_format_types
@@ -3328,18 +3285,6 @@ get_date_time_result_type(const char *format, uint length)
}
-Field *Item_func_str_to_date::tmp_table_field(TABLE *t_arg)
-{
- if (cached_field_type == MYSQL_TYPE_TIME)
- return (new Field_time(maybe_null, name, t_arg, &my_charset_bin));
- if (cached_field_type == MYSQL_TYPE_DATE)
- return (new Field_newdate(maybe_null, name, t_arg, &my_charset_bin));
- if (cached_field_type == MYSQL_TYPE_DATETIME)
- return (new Field_datetime(maybe_null, name, t_arg, &my_charset_bin));
- return (new Field_string(max_length, maybe_null, name, t_arg, &my_charset_bin));
-}
-
-
void Item_func_str_to_date::fix_length_and_dec()
{
maybe_null= 1;
diff --git a/sql/item_timefunc.h b/sql/item_timefunc.h
index 2c99d6044af..9e3c2e8c89f 100644
--- a/sql/item_timefunc.h
+++ b/sql/item_timefunc.h
@@ -25,6 +25,9 @@ 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);
+
class Item_func_period_add :public Item_int_func
{
public:
@@ -64,6 +67,9 @@ public:
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 *int_arg) {return FALSE;}
};
@@ -79,6 +85,7 @@ public:
max_length=2*MY_CHARSET_BIN_MB_MAXLEN;
maybe_null=1;
}
+ bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
};
@@ -103,6 +110,7 @@ public:
max_length=2*MY_CHARSET_BIN_MB_MAXLEN;
maybe_null=1;
}
+ bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
};
@@ -115,6 +123,7 @@ public:
String *val_str(String *str);
enum Item_result result_type () const { return STRING_RESULT; }
void fix_length_and_dec();
+ bool check_partition_func_processor(uchar *int_arg) {return TRUE;}
};
@@ -130,6 +139,7 @@ public:
max_length=3*MY_CHARSET_BIN_MB_MAXLEN;
maybe_null=1;
}
+ bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
};
@@ -145,6 +155,7 @@ public:
max_length=2*MY_CHARSET_BIN_MB_MAXLEN;
maybe_null=1;
}
+ bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
};
@@ -160,6 +171,7 @@ public:
max_length=2*MY_CHARSET_BIN_MB_MAXLEN;
maybe_null=1;
}
+ bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
};
@@ -175,6 +187,7 @@ public:
max_length=1*MY_CHARSET_BIN_MB_MAXLEN;
maybe_null=1;
}
+ bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
};
@@ -190,6 +203,7 @@ public:
max_length=2*MY_CHARSET_BIN_MB_MAXLEN;
maybe_null=1;
}
+ bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
};
@@ -219,6 +233,7 @@ public:
max_length=6*MY_CHARSET_BIN_MB_MAXLEN;
maybe_null=1;
}
+ bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
};
@@ -228,12 +243,15 @@ public:
Item_func_year(Item *a) :Item_int_func(a) {}
longlong val_int();
const char *func_name() const { return "year"; }
+ enum_monotonicity_info get_monotonicity_info() const;
+ longlong val_int_endpoint(bool left_endp, bool *incl_endp);
void fix_length_and_dec()
{
decimals=0;
max_length=4*MY_CHARSET_BIN_MB_MAXLEN;
maybe_null=1;
}
+ bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
};
@@ -263,6 +281,7 @@ public:
max_length=1*MY_CHARSET_BIN_MB_MAXLEN;
maybe_null=1;
}
+ bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
};
class Item_func_dayname :public Item_func_weekday
@@ -274,6 +293,7 @@ class Item_func_dayname :public Item_func_weekday
String *val_str(String *str);
enum Item_result result_type () const { return STRING_RESULT; }
void fix_length_and_dec();
+ bool check_partition_func_processor(uchar *int_arg) {return TRUE;}
};
@@ -304,6 +324,7 @@ public:
decimals=0;
max_length=10*MY_CHARSET_BIN_MB_MAXLEN;
}
+ bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
};
@@ -328,10 +349,10 @@ public:
decimals=0;
max_length=MAX_DATE_WIDTH*MY_CHARSET_BIN_MB_MAXLEN;
}
- Field *tmp_table_field(TABLE *t_arg)
+ Field *tmp_table_field(TABLE *table)
{
- return (new Field_newdate(maybe_null, name, t_arg, &my_charset_bin));
- }
+ return tmp_table_field_from_field_type(table, 0);
+ }
bool result_as_longlong() { return TRUE; }
my_decimal *val_decimal(my_decimal *decimal_value)
{
@@ -353,9 +374,9 @@ public:
Item_date_func(Item *a,Item *b) :Item_str_func(a,b) {}
Item_date_func(Item *a,Item *b, Item *c) :Item_str_func(a,b,c) {}
enum_field_types field_type() const { return MYSQL_TYPE_DATETIME; }
- Field *tmp_table_field(TABLE *t_arg)
+ Field *tmp_table_field(TABLE *table)
{
- return (new Field_datetime(maybe_null, name, t_arg, &my_charset_bin));
+ return tmp_table_field_from_field_type(table, 0);
}
bool result_as_longlong() { return TRUE; }
double val_real() { return (double) val_int(); }
@@ -384,9 +405,9 @@ public:
decimals= DATETIME_DEC;
max_length=MAX_TIME_WIDTH*MY_CHARSET_BIN_MB_MAXLEN;
}
- Field *tmp_table_field(TABLE *t_arg)
+ Field *tmp_table_field(TABLE *table)
{
- return (new Field_time(maybe_null, name, t_arg, &my_charset_bin));
+ return tmp_table_field_from_field_type(table, 0);
}
double val_real() { return val_real_from_decimal(); }
my_decimal *val_decimal(my_decimal *decimal_value)
@@ -417,10 +438,6 @@ public:
longlong val_int() { DBUG_ASSERT(fixed == 1); return value; }
String *val_str(String *str);
void fix_length_and_dec();
- Field *tmp_table_field(TABLE *t_arg)
- {
- return (new Field_time(maybe_null, name, t_arg, &my_charset_bin));
- }
/*
Abstract method that defines which time zone is used for conversion.
Converts time current time in my_time_t representation to broken-down
@@ -560,6 +577,7 @@ public:
Item_func_from_days(Item *a) :Item_date(a) {}
const char *func_name() const { return "from_days"; }
bool get_date(MYSQL_TIME *res, uint fuzzy_date);
+ bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
};
@@ -609,8 +627,6 @@ class Time_zone;
*/
class Item_func_convert_tz :public Item_date_func
{
- /* Cached pointer to list of pre-opened time zone tables. */
- TABLE_LIST *tz_tables;
/*
If time zone parameters are constants we are caching objects that
represent them (we use separate from_tz_cached/to_tz_cached members
@@ -625,7 +641,6 @@ class Item_func_convert_tz :public Item_date_func
longlong val_int();
String *val_str(String *str);
const char *func_name() const { return "convert_tz"; }
- bool fix_fields(THD *, Item **);
void fix_length_and_dec();
bool get_date(MYSQL_TIME *res, uint fuzzy_date);
void cleanup();
@@ -653,23 +668,6 @@ public:
bool result_as_longlong() { return TRUE; }
};
-/*
- 'interval_type' must be sorted so that simple intervals comes first,
- ie year, quarter, month, week, day, hour, etc. The order based on
- interval size is also important and the intervals should be kept in a
- large to smaller order. (get_interval_value() depends on this)
-*/
-
-enum interval_type
-{
- INTERVAL_YEAR, INTERVAL_QUARTER, INTERVAL_MONTH, INTERVAL_WEEK,
- INTERVAL_DAY, INTERVAL_HOUR, INTERVAL_MINUTE, INTERVAL_SECOND,
- INTERVAL_MICROSECOND, INTERVAL_YEAR_MONTH, INTERVAL_DAY_HOUR,
- INTERVAL_DAY_MINUTE, INTERVAL_DAY_SECOND, INTERVAL_HOUR_MINUTE,
- INTERVAL_HOUR_SECOND, INTERVAL_MINUTE_SECOND, INTERVAL_DAY_MICROSECOND,
- INTERVAL_HOUR_MICROSECOND, INTERVAL_MINUTE_MICROSECOND,
- INTERVAL_SECOND_MICROSECOND
-};
class Item_date_add_interval :public Item_date_func
{
@@ -688,7 +686,7 @@ public:
longlong val_int();
bool get_date(MYSQL_TIME *res, uint fuzzy_date);
bool eq(const Item *item, bool binary_cmp) const;
- void print(String *str);
+ virtual void print(String *str, enum_query_type query_type);
};
@@ -705,7 +703,8 @@ class Item_extract :public Item_int_func
const char *func_name() const { return "extract"; }
void fix_length_and_dec();
bool eq(const Item *item, bool binary_cmp) const;
- void print(String *str);
+ virtual void print(String *str, enum_query_type query_type);
+ bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
};
@@ -728,7 +727,7 @@ public:
max_length=args[0]->max_length;
}
virtual const char* cast_type() const= 0;
- void print(String *str);
+ virtual void print(String *str, enum_query_type query_type);
};
@@ -760,7 +759,7 @@ public:
const char* cast_type() const { return "char"; };
String *val_str(String *a);
void fix_length_and_dec();
- void print(String *str);
+ virtual void print(String *str, enum_query_type query_type);
};
@@ -774,9 +773,9 @@ public:
bool get_time(MYSQL_TIME *ltime);
const char *cast_type() const { return "date"; }
enum_field_types field_type() const { return MYSQL_TYPE_DATE; }
- Field *tmp_table_field(TABLE *t_arg)
+ Field *tmp_table_field(TABLE *table)
{
- return (new Field_newdate(maybe_null, name, t_arg, &my_charset_bin));
+ return tmp_table_field_from_field_type(table, 0);
}
void fix_length_and_dec()
{
@@ -808,9 +807,9 @@ public:
bool get_time(MYSQL_TIME *ltime);
const char *cast_type() const { return "time"; }
enum_field_types field_type() const { return MYSQL_TYPE_TIME; }
- Field *tmp_table_field(TABLE *t_arg)
+ Field *tmp_table_field(TABLE *table)
{
- return (new Field_time(maybe_null, name, t_arg, &my_charset_bin));
+ return tmp_table_field_from_field_type(table, 0);
}
bool result_as_longlong() { return TRUE; }
longlong val_int();
@@ -835,6 +834,10 @@ public:
String *val_str(String *str);
const char *cast_type() const { return "datetime"; }
enum_field_types field_type() const { return MYSQL_TYPE_DATETIME; }
+ Field *tmp_table_field(TABLE *table)
+ {
+ return tmp_table_field_from_field_type(table, 0);
+ }
void fix_length_and_dec()
{
collation.set(&my_charset_bin);
@@ -842,11 +845,6 @@ public:
max_length= MAX_DATETIME_FULL_WIDTH * MY_CHARSET_BIN_MB_MAXLEN;
decimals= DATETIME_DEC;
}
-
- Field *tmp_table_field(TABLE *t_arg)
- {
- return (new Field_datetime(maybe_null, name, t_arg, &my_charset_bin));
- }
bool result_as_longlong() { return TRUE; }
longlong val_int();
double val_real() { return val_real_from_decimal(); }
@@ -874,20 +872,7 @@ public:
decimals=0;
max_length=MAX_DATE_WIDTH*MY_CHARSET_BIN_MB_MAXLEN;
}
- Field *tmp_table_field(TABLE *t_arg)
- {
- return (new Field_newdate(maybe_null, name, t_arg, &my_charset_bin));
- }
longlong val_int();
- my_decimal *val_decimal(my_decimal *decimal_value)
- {
- DBUG_ASSERT(fixed == 1);
- return val_decimal_from_date(decimal_value);
- }
- int save_in_field(Field *field, bool no_conversions)
- {
- return save_date_in_field(field);
- }
};
@@ -904,20 +889,11 @@ public:
enum_field_types field_type() const { return cached_field_type; }
void fix_length_and_dec();
-/*
- TODO:
- Change this when we support
- microseconds in TIME/DATETIME
-*/
- Field *tmp_table_field(TABLE *t_arg)
+ Field *tmp_table_field(TABLE *table)
{
- if (cached_field_type == MYSQL_TYPE_TIME)
- return (new Field_time(maybe_null, name, t_arg, &my_charset_bin));
- else if (cached_field_type == MYSQL_TYPE_DATETIME)
- return (new Field_datetime(maybe_null, name, t_arg, &my_charset_bin));
- return (new Field_string(max_length, maybe_null, name, t_arg, &my_charset_bin));
+ return tmp_table_field_from_field_type(table, 0);
}
- void print(String *str);
+ virtual void print(String *str, enum_query_type query_type);
const char *func_name() const { return "add_time"; }
double val_real() { return val_real_from_decimal(); }
my_decimal *val_decimal(my_decimal *decimal_value)
@@ -976,6 +952,7 @@ public:
decimals=0;
maybe_null=1;
}
+ bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
};
@@ -992,7 +969,7 @@ public:
decimals=0;
maybe_null=1;
}
- void print(String *str);
+ virtual void print(String *str, enum_query_type query_type);
};
@@ -1016,7 +993,7 @@ public:
decimals=0;
max_length=17*MY_CHARSET_BIN_MB_MAXLEN;
}
- void print(String *str);
+ virtual void print(String *str, enum_query_type query_type);
};
@@ -1035,7 +1012,10 @@ public:
const char *func_name() const { return "str_to_date"; }
enum_field_types field_type() const { return cached_field_type; }
void fix_length_and_dec();
- Field *tmp_table_field(TABLE *t_arg);
+ Field *tmp_table_field(TABLE *table)
+ {
+ return tmp_table_field_from_field_type(table, 1);
+ }
};
diff --git a/sql/item_uniq.h b/sql/item_uniq.h
deleted file mode 100644
index ce43abe3f33..00000000000
--- a/sql/item_uniq.h
+++ /dev/null
@@ -1,62 +0,0 @@
-/* Copyright (C) 2000-2005 MySQL AB
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; version 2 of the License.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
-
-/* Compability file ; This file only contains dummy functions */
-
-#ifdef USE_PRAGMA_INTERFACE
-#pragma interface
-#endif
-
-#include <queues.h>
-
-class Item_func_unique_users :public Item_real_func
-{
-public:
- Item_func_unique_users(Item *name_arg,int start,int end,List<Item> &list)
- :Item_real_func(list) {}
- double val_real() { DBUG_ASSERT(fixed == 1); return 0.0; }
- void fix_length_and_dec() { decimals=0; max_length=6; }
- void print(String *str) { str->append(STRING_WITH_LEN("0.0")); }
- const char *func_name() const { return "unique_users"; }
-};
-
-
-class Item_sum_unique_users :public Item_sum_num
-{
-public:
- Item_sum_unique_users(Item *name_arg,int start,int end,Item *item_arg)
- :Item_sum_num(item_arg) {}
- Item_sum_unique_users(THD *thd, Item_sum_unique_users *item)
- :Item_sum_num(thd, item) {}
- double val_real() { DBUG_ASSERT(fixed == 1); return 0.0; }
- enum Sumfunctype sum_func () const {return UNIQUE_USERS_FUNC;}
- void clear() {}
- bool add() { return 0; }
- void reset_field() {}
- void update_field() {}
- bool fix_fields(THD *thd, Item **ref)
- {
- DBUG_ASSERT(fixed == 0);
- fixed= 1;
- return FALSE;
- }
- Item *copy_or_same(THD* thd)
- {
- return new Item_sum_unique_users(thd, this);
- }
- void print(String *str) { str->append(STRING_WITH_LEN("0.0")); }
- Field *create_tmp_field(bool group, TABLE *table, uint convert_blob_length);
- const char *func_name() const { return "sum_unique_users"; }
-};
diff --git a/sql/item_xmlfunc.cc b/sql/item_xmlfunc.cc
new file mode 100644
index 00000000000..5601a2b18c6
--- /dev/null
+++ b/sql/item_xmlfunc.cc
@@ -0,0 +1,2845 @@
+/* Copyright (C) 2005-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 St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#ifdef __GNUC__
+#pragma implementation
+#endif
+
+#include "mysql_priv.h"
+#include "my_xml.h"
+#include "sp_pcontext.h"
+
+/*
+ TODO: future development directions:
+ 1. add real constants for XPATH_NODESET_CMP and XPATH_NODESET
+ into enum Type in item.h.
+ 2. add nodeset_to_nodeset_comparator
+ 3. add lacking functions:
+ - name()
+ - lang()
+ - string()
+ - id()
+ - translate()
+ - local-name()
+ - starts-with()
+ - namespace-uri()
+ - substring-after()
+ - normalize-space()
+ - substring-before()
+ 4. add lacking axis:
+ - following-sibling
+ - following,
+ - preceding-sibling
+ - preceding
+*/
+
+
+/* Structure to store a parsed XML tree */
+typedef struct my_xml_node_st
+{
+ uint level; /* level in XML tree, 0 means root node */
+ enum my_xml_node_type type; /* node type: node, or attribute, or text */
+ uint parent; /* link to the parent */
+ const char *beg; /* beginning of the name or text */
+ const char *end; /* end of the name or text */
+ const char *tagend; /* where this tag ends */
+} MY_XML_NODE;
+
+
+/* Lexical analizer token */
+typedef struct my_xpath_lex_st
+{
+ int term; /* token type, see MY_XPATH_LEX_XXXXX below */
+ const char *beg; /* beginnign of the token */
+ const char *end; /* end of the token */
+} MY_XPATH_LEX;
+
+
+/* Structure to store nodesets */
+typedef struct my_xpath_flt_st
+{
+ uint num; /* absolute position in MY_XML_NODE array */
+ uint pos; /* relative position in context */
+ uint size; /* context size */
+} MY_XPATH_FLT;
+
+
+/* XPath function creator */
+typedef struct my_xpath_function_names_st
+{
+ const char *name; /* function name */
+ size_t length; /* function name length */
+ size_t minargs; /* min number of arguments */
+ size_t maxargs; /* max number of arguments */
+ Item *(*create)(struct my_xpath_st *xpath, Item **args, uint nargs);
+} MY_XPATH_FUNC;
+
+
+/* XPath query parser */
+typedef struct my_xpath_st
+{
+ int debug;
+ MY_XPATH_LEX query; /* Whole query */
+ MY_XPATH_LEX lasttok; /* last scanned token */
+ MY_XPATH_LEX prevtok; /* previous scanned token */
+ int axis; /* last scanned axis */
+ int extra; /* last scanned "extra", context dependent */
+ MY_XPATH_FUNC *func; /* last scanned function creator */
+ Item *item; /* current expression */
+ Item *context; /* last scanned context */
+ Item *rootelement; /* The root element */
+ String *context_cache; /* last context provider */
+ String *pxml; /* Parsed XML, an array of MY_XML_NODE */
+ CHARSET_INFO *cs; /* character set/collation string comparison */
+ int error;
+} MY_XPATH;
+
+
+/* Dynamic array of MY_XPATH_FLT */
+class XPathFilter :public String
+{
+public:
+ XPathFilter() :String() {}
+ inline bool append_element(MY_XPATH_FLT *flt)
+ {
+ String *str= this;
+ return str->append((const char*)flt, (uint32) sizeof(MY_XPATH_FLT));
+ }
+ inline bool append_element(uint32 num, uint32 pos)
+ {
+ MY_XPATH_FLT add;
+ add.num= num;
+ add.pos= pos;
+ add.size= 0;
+ return append_element(&add);
+ }
+ inline bool append_element(uint32 num, uint32 pos, uint32 size)
+ {
+ MY_XPATH_FLT add;
+ add.num= num;
+ add.pos= pos;
+ add.size= size;
+ return append_element(&add);
+ }
+ inline MY_XPATH_FLT *element(uint i)
+ {
+ return (MY_XPATH_FLT*) (ptr() + i * sizeof(MY_XPATH_FLT));
+ }
+ inline uint32 numelements()
+ {
+ return length() / sizeof(MY_XPATH_FLT);
+ }
+};
+
+
+/*
+ Common features of the functions returning a node set.
+*/
+class Item_nodeset_func :public Item_str_func
+{
+protected:
+ String tmp_value, tmp2_value;
+ MY_XPATH_FLT *fltbeg, *fltend;
+ MY_XML_NODE *nodebeg, *nodeend;
+ uint numnodes;
+public:
+ String *pxml;
+ String context_cache;
+ Item_nodeset_func(String *pxml_arg) :Item_str_func(), pxml(pxml_arg) {}
+ Item_nodeset_func(Item *a, String *pxml_arg)
+ :Item_str_func(a), pxml(pxml_arg) {}
+ Item_nodeset_func(Item *a, Item *b, String *pxml_arg)
+ :Item_str_func(a, b), pxml(pxml_arg) {}
+ Item_nodeset_func(Item *a, Item *b, Item *c, String *pxml_arg)
+ :Item_str_func(a,b,c), pxml(pxml_arg) {}
+ void prepare_nodes()
+ {
+ nodebeg= (MY_XML_NODE*) pxml->ptr();
+ nodeend= (MY_XML_NODE*) (pxml->ptr() + pxml->length());
+ numnodes= nodeend - nodebeg;
+ }
+ void prepare(String *nodeset)
+ {
+ prepare_nodes();
+ String *res= args[0]->val_nodeset(&tmp_value);
+ fltbeg= (MY_XPATH_FLT*) res->ptr();
+ fltend= (MY_XPATH_FLT*) (res->ptr() + res->length());
+ nodeset->length(0);
+ }
+ enum Type type() const { return XPATH_NODESET; }
+ String *val_str(String *str)
+ {
+ prepare_nodes();
+ String *res= val_nodeset(&tmp2_value);
+ fltbeg= (MY_XPATH_FLT*) res->ptr();
+ fltend= (MY_XPATH_FLT*) (res->ptr() + res->length());
+ String active;
+ active.alloc(numnodes);
+ bzero((char*) active.ptr(), numnodes);
+ for (MY_XPATH_FLT *flt= fltbeg; flt < fltend; flt++)
+ {
+ MY_XML_NODE *node;
+ uint j;
+ for (j=0, node= nodebeg ; j < numnodes; j++, node++)
+ {
+ if (node->type == MY_XML_NODE_TEXT &&
+ node->parent == flt->num)
+ active[j]= 1;
+ }
+ }
+
+ str->length(0);
+ str->set_charset(collation.collation);
+ for (uint i=0 ; i < numnodes; i++)
+ {
+ if(active[i])
+ {
+ if (str->length())
+ str->append(" ", 1, &my_charset_latin1);
+ str->append(nodebeg[i].beg, nodebeg[i].end - nodebeg[i].beg);
+ }
+ }
+ return str;
+ }
+ enum Item_result result_type () const { return STRING_RESULT; }
+ void fix_length_and_dec()
+ {
+ max_length= MAX_BLOB_WIDTH;
+ collation.collation= pxml->charset();
+ }
+ const char *func_name() const { return "nodeset"; }
+};
+
+
+/* Returns an XML root */
+class Item_nodeset_func_rootelement :public Item_nodeset_func
+{
+public:
+ Item_nodeset_func_rootelement(String *pxml): Item_nodeset_func(pxml) {}
+ const char *func_name() const { return "xpath_rootelement"; }
+ String *val_nodeset(String *nodeset);
+};
+
+
+/* Returns a Union of two node sets */
+class Item_nodeset_func_union :public Item_nodeset_func
+{
+public:
+ Item_nodeset_func_union(Item *a, Item *b, String *pxml)
+ :Item_nodeset_func(a, b, pxml) {}
+ const char *func_name() const { return "xpath_union"; }
+ String *val_nodeset(String *nodeset);
+};
+
+
+/* Makes one step towards the given axis */
+class Item_nodeset_func_axisbyname :public Item_nodeset_func
+{
+ const char *node_name;
+ uint node_namelen;
+public:
+ Item_nodeset_func_axisbyname(Item *a, const char *n_arg, uint l_arg,
+ String *pxml):
+ Item_nodeset_func(a, pxml), node_name(n_arg), node_namelen(l_arg) { }
+ const char *func_name() const { return "xpath_axisbyname"; }
+ bool validname(MY_XML_NODE *n)
+ {
+ if (node_name[0] == '*')
+ return 1;
+ return (node_namelen == (uint) (n->end - n->beg)) &&
+ !memcmp(node_name, n->beg, node_namelen);
+ }
+};
+
+
+/* Returns self */
+class Item_nodeset_func_selfbyname: public Item_nodeset_func_axisbyname
+{
+public:
+ Item_nodeset_func_selfbyname(Item *a, const char *n_arg, uint l_arg,
+ String *pxml):
+ Item_nodeset_func_axisbyname(a, n_arg, l_arg, pxml) {}
+ const char *func_name() const { return "xpath_selfbyname"; }
+ String *val_nodeset(String *nodeset);
+};
+
+
+/* Returns children */
+class Item_nodeset_func_childbyname: public Item_nodeset_func_axisbyname
+{
+public:
+ Item_nodeset_func_childbyname(Item *a, const char *n_arg, uint l_arg,
+ String *pxml):
+ Item_nodeset_func_axisbyname(a, n_arg, l_arg, pxml) {}
+ const char *func_name() const { return "xpath_childbyname"; }
+ String *val_nodeset(String *nodeset);
+};
+
+
+/* Returns descendants */
+class Item_nodeset_func_descendantbyname: public Item_nodeset_func_axisbyname
+{
+ bool need_self;
+public:
+ Item_nodeset_func_descendantbyname(Item *a, const char *n_arg, uint l_arg,
+ String *pxml, bool need_self_arg):
+ Item_nodeset_func_axisbyname(a, n_arg, l_arg, pxml),
+ need_self(need_self_arg) {}
+ const char *func_name() const { return "xpath_descendantbyname"; }
+ String *val_nodeset(String *nodeset);
+};
+
+
+/* Returns ancestors */
+class Item_nodeset_func_ancestorbyname: public Item_nodeset_func_axisbyname
+{
+ bool need_self;
+public:
+ Item_nodeset_func_ancestorbyname(Item *a, const char *n_arg, uint l_arg,
+ String *pxml, bool need_self_arg):
+ Item_nodeset_func_axisbyname(a, n_arg, l_arg, pxml),
+ need_self(need_self_arg) {}
+ const char *func_name() const { return "xpath_ancestorbyname"; }
+ String *val_nodeset(String *nodeset);
+};
+
+
+/* Returns parents */
+class Item_nodeset_func_parentbyname: public Item_nodeset_func_axisbyname
+{
+public:
+ Item_nodeset_func_parentbyname(Item *a, const char *n_arg, uint l_arg,
+ String *pxml):
+ Item_nodeset_func_axisbyname(a, n_arg, l_arg, pxml) {}
+ const char *func_name() const { return "xpath_parentbyname"; }
+ String *val_nodeset(String *nodeset);
+};
+
+
+/* Returns attributes */
+class Item_nodeset_func_attributebyname: public Item_nodeset_func_axisbyname
+{
+public:
+ Item_nodeset_func_attributebyname(Item *a, const char *n_arg, uint l_arg,
+ String *pxml):
+ Item_nodeset_func_axisbyname(a, n_arg, l_arg, pxml) {}
+ const char *func_name() const { return "xpath_attributebyname"; }
+ String *val_nodeset(String *nodeset);
+};
+
+
+/*
+ Condition iterator: goes through all nodes in the current
+ context and checks a condition, returning those nodes
+ giving TRUE condition result.
+*/
+class Item_nodeset_func_predicate :public Item_nodeset_func
+{
+public:
+ Item_nodeset_func_predicate(Item *a, Item *b, String *pxml):
+ Item_nodeset_func(a, b, pxml) {}
+ const char *func_name() const { return "xpath_predicate"; }
+ String *val_nodeset(String *nodeset);
+};
+
+
+/* Selects nodes with a given position in context */
+class Item_nodeset_func_elementbyindex :public Item_nodeset_func
+{
+public:
+ Item_nodeset_func_elementbyindex(Item *a, Item *b, String *pxml):
+ Item_nodeset_func(a, b, pxml) { }
+ const char *func_name() const { return "xpath_elementbyindex"; }
+ String *val_nodeset(String *nodeset);
+};
+
+
+/*
+ We need to distinguish a number from a boolean:
+ a[1] and a[true] are different things in XPath.
+*/
+class Item_bool :public Item_int
+{
+public:
+ Item_bool(int32 i): Item_int(i) {}
+ const char *func_name() const { return "xpath_bool"; }
+ bool is_bool_func() { return 1; }
+};
+
+
+/*
+ Converts its argument into a boolean value.
+ * a number is true if it is non-zero
+ * a node-set is true if and only if it is non-empty
+ * a string is true if and only if its length is non-zero
+*/
+class Item_xpath_cast_bool :public Item_int_func
+{
+ String *pxml;
+ String tmp_value;
+public:
+ Item_xpath_cast_bool(Item *a, String *pxml_arg)
+ :Item_int_func(a), pxml(pxml_arg) {}
+ const char *func_name() const { return "xpath_cast_bool"; }
+ bool is_bool_func() { return 1; }
+ longlong val_int()
+ {
+ if (args[0]->type() == XPATH_NODESET)
+ {
+ String *flt= args[0]->val_nodeset(&tmp_value);
+ return flt->length() == sizeof(MY_XPATH_FLT) ? 1 : 0;
+ }
+ return args[0]->val_real() ? 1 : 0;
+ }
+};
+
+
+/*
+ Converts its argument into a number
+*/
+class Item_xpath_cast_number :public Item_real_func
+{
+public:
+ Item_xpath_cast_number(Item *a): Item_real_func(a) {}
+ const char *func_name() const { return "xpath_cast_number"; }
+ virtual double val_real() { return args[0]->val_real(); }
+};
+
+
+/*
+ Context cache, for predicate
+*/
+class Item_nodeset_context_cache :public Item_nodeset_func
+{
+public:
+ String *string_cache;
+ Item_nodeset_context_cache(String *str_arg, String *pxml):
+ Item_nodeset_func(pxml), string_cache(str_arg) { }
+ String *val_nodeset(String *res)
+ { return string_cache; }
+ void fix_length_and_dec() { max_length= MAX_BLOB_WIDTH; }
+};
+
+
+class Item_func_xpath_position :public Item_int_func
+{
+ String *pxml;
+ String tmp_value;
+public:
+ Item_func_xpath_position(Item *a, String *p)
+ :Item_int_func(a), pxml(p) {}
+ const char *func_name() const { return "xpath_position"; }
+ void fix_length_and_dec() { max_length=10; }
+ longlong val_int()
+ {
+ String *flt= args[0]->val_nodeset(&tmp_value);
+ if (flt->length() == sizeof(MY_XPATH_FLT))
+ return ((MY_XPATH_FLT*)flt->ptr())->pos + 1;
+ return 0;
+ }
+};
+
+
+class Item_func_xpath_count :public Item_int_func
+{
+ String *pxml;
+ String tmp_value;
+public:
+ Item_func_xpath_count(Item *a, String *p)
+ :Item_int_func(a), pxml(p) {}
+ const char *func_name() const { return "xpath_count"; }
+ void fix_length_and_dec() { max_length=10; }
+ longlong val_int()
+ {
+ uint predicate_supplied_context_size;
+ String *res= args[0]->val_nodeset(&tmp_value);
+ if (res->length() == sizeof(MY_XPATH_FLT) &&
+ (predicate_supplied_context_size= ((MY_XPATH_FLT*)res->ptr())->size))
+ return predicate_supplied_context_size;
+ return res->length() / sizeof(MY_XPATH_FLT);
+ }
+};
+
+
+class Item_func_xpath_sum :public Item_real_func
+{
+ String *pxml;
+ String tmp_value;
+public:
+ Item_func_xpath_sum(Item *a, String *p)
+ :Item_real_func(a), pxml(p) {}
+
+ const char *func_name() const { return "xpath_sum"; }
+ double val_real()
+ {
+ double sum= 0;
+ String *res= args[0]->val_nodeset(&tmp_value);
+ MY_XPATH_FLT *fltbeg= (MY_XPATH_FLT*) res->ptr();
+ MY_XPATH_FLT *fltend= (MY_XPATH_FLT*) (res->ptr() + res->length());
+ uint numnodes= pxml->length() / sizeof(MY_XML_NODE);
+ MY_XML_NODE *nodebeg= (MY_XML_NODE*) pxml->ptr();
+
+ for (MY_XPATH_FLT *flt= fltbeg; flt < fltend; flt++)
+ {
+ MY_XML_NODE *self= &nodebeg[flt->num];
+ for (uint j= flt->num + 1; j < numnodes; j++)
+ {
+ MY_XML_NODE *node= &nodebeg[j];
+ if (node->level <= self->level)
+ break;
+ if ((node->parent == flt->num) &&
+ (node->type == MY_XML_NODE_TEXT))
+ {
+ char *end;
+ int err;
+ double add= my_strntod(collation.collation, (char*) node->beg,
+ node->end - node->beg, &end, &err);
+ if (!err)
+ sum+= add;
+ }
+ }
+ }
+ return sum;
+ }
+};
+
+
+class Item_nodeset_to_const_comparator :public Item_bool_func
+{
+ String *pxml;
+ String tmp_nodeset;
+public:
+ Item_nodeset_to_const_comparator(Item *nodeset, Item *cmpfunc, String *p)
+ :Item_bool_func(nodeset,cmpfunc), pxml(p) {}
+ enum Type type() const { return XPATH_NODESET_CMP; };
+ const char *func_name() const { return "xpath_nodeset_to_const_comparator"; }
+ bool is_bool_func() { return 1; }
+
+ longlong val_int()
+ {
+ Item_func *comp= (Item_func*)args[1];
+ Item_string *fake= (Item_string*)(comp->arguments()[0]);
+ String *res= args[0]->val_nodeset(&tmp_nodeset);
+ MY_XPATH_FLT *fltbeg= (MY_XPATH_FLT*) res->ptr();
+ MY_XPATH_FLT *fltend= (MY_XPATH_FLT*) (res->ptr() + res->length());
+ MY_XML_NODE *nodebeg= (MY_XML_NODE*) pxml->ptr();
+ uint numnodes= pxml->length() / sizeof(MY_XML_NODE);
+
+ for (MY_XPATH_FLT *flt= fltbeg; flt < fltend; flt++)
+ {
+ MY_XML_NODE *self= &nodebeg[flt->num];
+ for (uint j= flt->num + 1; j < numnodes; j++)
+ {
+ MY_XML_NODE *node= &nodebeg[j];
+ if (node->level <= self->level)
+ break;
+ if ((node->parent == flt->num) &&
+ (node->type == MY_XML_NODE_TEXT))
+ {
+ fake->str_value.set(node->beg, node->end - node->beg,
+ collation.collation);
+ if (args[1]->val_int())
+ return 1;
+ }
+ }
+ }
+ return 0;
+ }
+};
+
+
+String *Item_nodeset_func_rootelement::val_nodeset(String *nodeset)
+{
+ nodeset->length(0);
+ ((XPathFilter*)nodeset)->append_element(0, 0);
+ return nodeset;
+}
+
+
+String * Item_nodeset_func_union::val_nodeset(String *nodeset)
+{
+ uint num_nodes= pxml->length() / sizeof(MY_XML_NODE);
+ String set0, *s0= args[0]->val_nodeset(&set0);
+ String set1, *s1= args[1]->val_nodeset(&set1);
+ String both_str;
+ both_str.alloc(num_nodes);
+ char *both= (char*) both_str.ptr();
+ bzero((void*)both, num_nodes);
+ MY_XPATH_FLT *flt;
+
+ fltbeg= (MY_XPATH_FLT*) s0->ptr();
+ fltend= (MY_XPATH_FLT*) (s0->ptr() + s0->length());
+ for (flt= fltbeg; flt < fltend; flt++)
+ both[flt->num]= 1;
+
+ fltbeg= (MY_XPATH_FLT*) s1->ptr();
+ fltend= (MY_XPATH_FLT*) (s1->ptr() + s1->length());
+ for (flt= fltbeg; flt < fltend; flt++)
+ both[flt->num]= 1;
+
+ nodeset->length(0);
+ for (uint i= 0, pos= 0; i < num_nodes; i++)
+ {
+ if (both[i])
+ ((XPathFilter*)nodeset)->append_element(i, pos++);
+ }
+ return nodeset;
+}
+
+
+String *Item_nodeset_func_selfbyname::val_nodeset(String *nodeset)
+{
+ prepare(nodeset);
+ for (MY_XPATH_FLT *flt= fltbeg; flt < fltend; flt++)
+ {
+ uint pos= 0;
+ MY_XML_NODE *self= &nodebeg[flt->num];
+ if (validname(self))
+ ((XPathFilter*)nodeset)->append_element(flt->num,pos++);
+ }
+ return nodeset;
+}
+
+
+String *Item_nodeset_func_childbyname::val_nodeset(String *nodeset)
+{
+ prepare(nodeset);
+ for (MY_XPATH_FLT *flt= fltbeg; flt < fltend; flt++)
+ {
+ MY_XML_NODE *self= &nodebeg[flt->num];
+ for (uint pos= 0, j= flt->num + 1 ; j < numnodes; j++)
+ {
+ MY_XML_NODE *node= &nodebeg[j];
+ if (node->level <= self->level)
+ break;
+ if ((node->parent == flt->num) &&
+ (node->type == MY_XML_NODE_TAG) &&
+ validname(node))
+ ((XPathFilter*)nodeset)->append_element(j, pos++);
+ }
+ }
+ return nodeset;
+}
+
+
+String *Item_nodeset_func_descendantbyname::val_nodeset(String *nodeset)
+{
+ prepare(nodeset);
+ for (MY_XPATH_FLT *flt= fltbeg; flt < fltend; flt++)
+ {
+ uint pos= 0;
+ MY_XML_NODE *self= &nodebeg[flt->num];
+ if (need_self && validname(self))
+ ((XPathFilter*)nodeset)->append_element(flt->num,pos++);
+ for (uint j= flt->num + 1 ; j < numnodes ; j++)
+ {
+ MY_XML_NODE *node= &nodebeg[j];
+ if (node->level <= self->level)
+ break;
+ if ((node->type == MY_XML_NODE_TAG) && validname(node))
+ ((XPathFilter*)nodeset)->append_element(j,pos++);
+ }
+ }
+ return nodeset;
+}
+
+
+String *Item_nodeset_func_ancestorbyname::val_nodeset(String *nodeset)
+{
+ char *active;
+ String active_str;
+ prepare(nodeset);
+ active_str.alloc(numnodes);
+ active= (char*) active_str.ptr();
+ bzero((void*)active, numnodes);
+ uint pos= 0;
+
+ for (MY_XPATH_FLT *flt= fltbeg; flt < fltend; flt++)
+ {
+ /*
+ Go to the root and add all nodes on the way.
+ Don't add the root if context is the root itelf
+ */
+ MY_XML_NODE *self= &nodebeg[flt->num];
+ if (need_self && validname(self))
+ {
+ active[flt->num]= 1;
+ pos++;
+ }
+
+ for (uint j= self->parent; nodebeg[j].parent != j; j= nodebeg[j].parent)
+ {
+ if (flt->num && validname(&nodebeg[j]))
+ {
+ active[j]= 1;
+ pos++;
+ }
+ }
+ }
+
+ for (uint j= 0; j < numnodes ; j++)
+ {
+ if (active[j])
+ ((XPathFilter*)nodeset)->append_element(j, --pos);
+ }
+ return nodeset;
+}
+
+
+String *Item_nodeset_func_parentbyname::val_nodeset(String *nodeset)
+{
+ char *active;
+ String active_str;
+ prepare(nodeset);
+ active_str.alloc(numnodes);
+ active= (char*) active_str.ptr();
+ bzero((void*)active, numnodes);
+ for (MY_XPATH_FLT *flt= fltbeg; flt < fltend; flt++)
+ {
+ uint j= nodebeg[flt->num].parent;
+ if (flt->num && validname(&nodebeg[j]))
+ active[j]= 1;
+ }
+ for (uint j= 0, pos= 0; j < numnodes ; j++)
+ {
+ if (active[j])
+ ((XPathFilter*)nodeset)->append_element(j, pos++);
+ }
+ return nodeset;
+}
+
+
+String *Item_nodeset_func_attributebyname::val_nodeset(String *nodeset)
+{
+ prepare(nodeset);
+ for (MY_XPATH_FLT *flt= fltbeg; flt < fltend; flt++)
+ {
+ MY_XML_NODE *self= &nodebeg[flt->num];
+ for (uint pos=0, j= flt->num + 1 ; j < numnodes; j++)
+ {
+ MY_XML_NODE *node= &nodebeg[j];
+ if (node->level <= self->level)
+ break;
+ if ((node->parent == flt->num) &&
+ (node->type == MY_XML_NODE_ATTR) &&
+ validname(node))
+ ((XPathFilter*)nodeset)->append_element(j, pos++);
+ }
+ }
+ return nodeset;
+}
+
+
+String *Item_nodeset_func_predicate::val_nodeset(String *str)
+{
+ Item_nodeset_func *nodeset_func= (Item_nodeset_func*) args[0];
+ Item_func *comp_func= (Item_func*)args[1];
+ uint pos= 0, size;
+ prepare(str);
+ size= fltend - fltbeg;
+ for (MY_XPATH_FLT *flt= fltbeg; flt < fltend; flt++)
+ {
+ nodeset_func->context_cache.length(0);
+ ((XPathFilter*)(&nodeset_func->context_cache))->append_element(flt->num,
+ flt->pos,
+ size);
+ if (comp_func->val_int())
+ ((XPathFilter*)str)->append_element(flt->num, pos++);
+ }
+ return str;
+}
+
+
+String *Item_nodeset_func_elementbyindex::val_nodeset(String *nodeset)
+{
+ Item_nodeset_func *nodeset_func= (Item_nodeset_func*) args[0];
+ prepare(nodeset);
+ MY_XPATH_FLT *flt;
+ uint pos, size= fltend - fltbeg;
+ for (pos= 0, flt= fltbeg; flt < fltend; flt++)
+ {
+ nodeset_func->context_cache.length(0);
+ ((XPathFilter*)(&nodeset_func->context_cache))->append_element(flt->num,
+ flt->pos,
+ size);
+ int index= (int) (args[1]->val_int()) - 1;
+ if (index >= 0 && (flt->pos == (uint) index || args[1]->is_bool_func()))
+ ((XPathFilter*)nodeset)->append_element(flt->num, pos++);
+ }
+ return nodeset;
+}
+
+
+/*
+ If item is a node set, then casts it to boolean,
+ otherwise returns the item itself.
+*/
+static Item* nodeset2bool(MY_XPATH *xpath, Item *item)
+{
+ if (item->type() == Item::XPATH_NODESET)
+ return new Item_xpath_cast_bool(item, xpath->pxml);
+ return item;
+}
+
+
+/*
+ XPath lexical tokens
+*/
+#define MY_XPATH_LEX_DIGITS 'd'
+#define MY_XPATH_LEX_IDENT 'i'
+#define MY_XPATH_LEX_STRING 's'
+#define MY_XPATH_LEX_SLASH '/'
+#define MY_XPATH_LEX_LB '['
+#define MY_XPATH_LEX_RB ']'
+#define MY_XPATH_LEX_LP '('
+#define MY_XPATH_LEX_RP ')'
+#define MY_XPATH_LEX_EQ '='
+#define MY_XPATH_LEX_LESS '<'
+#define MY_XPATH_LEX_GREATER '>'
+#define MY_XPATH_LEX_AT '@'
+#define MY_XPATH_LEX_COLON ':'
+#define MY_XPATH_LEX_ASTERISK '*'
+#define MY_XPATH_LEX_DOT '.'
+#define MY_XPATH_LEX_VLINE '|'
+#define MY_XPATH_LEX_MINUS '-'
+#define MY_XPATH_LEX_PLUS '+'
+#define MY_XPATH_LEX_EXCL '!'
+#define MY_XPATH_LEX_COMMA ','
+#define MY_XPATH_LEX_DOLLAR '$'
+#define MY_XPATH_LEX_ERROR 'A'
+#define MY_XPATH_LEX_EOF 'B'
+#define MY_XPATH_LEX_AND 'C'
+#define MY_XPATH_LEX_OR 'D'
+#define MY_XPATH_LEX_DIV 'E'
+#define MY_XPATH_LEX_MOD 'F'
+#define MY_XPATH_LEX_FUNC 'G'
+#define MY_XPATH_LEX_NODETYPE 'H'
+#define MY_XPATH_LEX_AXIS 'I'
+#define MY_XPATH_LEX_LE 'J'
+#define MY_XPATH_LEX_GE 'K'
+
+
+/*
+ XPath axis type
+*/
+#define MY_XPATH_AXIS_ANCESTOR 0
+#define MY_XPATH_AXIS_ANCESTOR_OR_SELF 1
+#define MY_XPATH_AXIS_ATTRIBUTE 2
+#define MY_XPATH_AXIS_CHILD 3
+#define MY_XPATH_AXIS_DESCENDANT 4
+#define MY_XPATH_AXIS_DESCENDANT_OR_SELF 5
+#define MY_XPATH_AXIS_FOLLOWING 6
+#define MY_XPATH_AXIS_FOLLOWING_SIBLING 7
+#define MY_XPATH_AXIS_NAMESPACE 8
+#define MY_XPATH_AXIS_PARENT 9
+#define MY_XPATH_AXIS_PRECEDING 10
+#define MY_XPATH_AXIS_PRECEDING_SIBLING 11
+#define MY_XPATH_AXIS_SELF 12
+
+
+/*
+ Create scalar comparator
+
+ SYNOPSYS
+ Create a comparator function for scalar arguments,
+ for the given arguments and operation.
+
+ RETURN
+ The newly created item.
+*/
+static Item *eq_func(int oper, Item *a, Item *b)
+{
+ switch (oper)
+ {
+ case '=': return new Item_func_eq(a, b);
+ case '!': return new Item_func_ne(a, b);
+ case MY_XPATH_LEX_GE: return new Item_func_ge(a, b);
+ case MY_XPATH_LEX_LE: return new Item_func_le(a, b);
+ case MY_XPATH_LEX_GREATER: return new Item_func_gt(a, b);
+ case MY_XPATH_LEX_LESS: return new Item_func_lt(a, b);
+ }
+ return 0;
+}
+
+
+/*
+ Create scalar comparator
+
+ SYNOPSYS
+ Create a comparator function for scalar arguments,
+ for the given arguments and reverse operation, e.g.
+
+ A > B is converted into B < A
+
+ RETURN
+ The newly created item.
+*/
+static Item *eq_func_reverse(int oper, Item *a, Item *b)
+{
+ switch (oper)
+ {
+ case '=': return new Item_func_eq(a, b);
+ case '!': return new Item_func_ne(a, b);
+ case MY_XPATH_LEX_GE: return new Item_func_le(a, b);
+ case MY_XPATH_LEX_LE: return new Item_func_ge(a, b);
+ case MY_XPATH_LEX_GREATER: return new Item_func_lt(a, b);
+ case MY_XPATH_LEX_LESS: return new Item_func_gt(a, b);
+ }
+ return 0;
+}
+
+
+/*
+ Create a comparator
+
+ SYNOPSYS
+ Create a comparator for scalar or non-scalar arguments,
+ for the given arguments and operation.
+
+ RETURN
+ The newly created item.
+*/
+static Item *create_comparator(MY_XPATH *xpath,
+ int oper, MY_XPATH_LEX *context,
+ Item *a, Item *b)
+{
+ if (a->type() != Item::XPATH_NODESET &&
+ b->type() != Item::XPATH_NODESET)
+ {
+ return eq_func(oper, a, b); // two scalar arguments
+ }
+ else if (a->type() == Item::XPATH_NODESET &&
+ b->type() == Item::XPATH_NODESET)
+ {
+ uint len= xpath->query.end - context->beg;
+ set_if_smaller(len, 32);
+ my_printf_error(ER_UNKNOWN_ERROR,
+ "XPATH error: "
+ "comparison of two nodesets is not supported: '%.*s'",
+ MYF(0), len, context->beg);
+
+ return 0; // TODO: Comparison of two nodesets
+ }
+ else
+ {
+ /*
+ Compare a node set to a scalar value.
+ We just create a fake Item_string() argument,
+ which will be filled to the partular value
+ in a loop through all of the nodes in the node set.
+ */
+
+ Item *fake= new Item_string("", 0, xpath->cs);
+ Item_nodeset_func *nodeset;
+ Item *scalar, *comp;
+ if (a->type() == Item::XPATH_NODESET)
+ {
+ nodeset= (Item_nodeset_func*) a;
+ scalar= b;
+ comp= eq_func(oper, fake, scalar);
+ }
+ else
+ {
+ nodeset= (Item_nodeset_func*) b;
+ scalar= a;
+ comp= eq_func_reverse(oper, fake, scalar);
+ }
+ return new Item_nodeset_to_const_comparator(nodeset, comp, xpath->pxml);
+ }
+}
+
+
+/*
+ Create a step
+
+ SYNOPSYS
+ Create a step function for the given argument and axis.
+
+ RETURN
+ The newly created item.
+*/
+static Item* nametestfunc(MY_XPATH *xpath,
+ int type, Item *arg, const char *beg, uint len)
+{
+ DBUG_ASSERT(arg != 0);
+ DBUG_ASSERT(arg->type() == Item::XPATH_NODESET);
+ DBUG_ASSERT(beg != 0);
+ DBUG_ASSERT(len > 0);
+
+ Item *res;
+ switch (type)
+ {
+ case MY_XPATH_AXIS_ANCESTOR:
+ res= new Item_nodeset_func_ancestorbyname(arg, beg, len, xpath->pxml, 0);
+ break;
+ case MY_XPATH_AXIS_ANCESTOR_OR_SELF:
+ res= new Item_nodeset_func_ancestorbyname(arg, beg, len, xpath->pxml, 1);
+ break;
+ case MY_XPATH_AXIS_PARENT:
+ res= new Item_nodeset_func_parentbyname(arg, beg, len, xpath->pxml);
+ break;
+ case MY_XPATH_AXIS_DESCENDANT:
+ res= new Item_nodeset_func_descendantbyname(arg, beg, len, xpath->pxml, 0);
+ break;
+ case MY_XPATH_AXIS_DESCENDANT_OR_SELF:
+ res= new Item_nodeset_func_descendantbyname(arg, beg, len, xpath->pxml, 1);
+ break;
+ case MY_XPATH_AXIS_ATTRIBUTE:
+ res= new Item_nodeset_func_attributebyname(arg, beg, len, xpath->pxml);
+ break;
+ case MY_XPATH_AXIS_SELF:
+ res= new Item_nodeset_func_selfbyname(arg, beg, len, xpath->pxml);
+ break;
+ default:
+ res= new Item_nodeset_func_childbyname(arg, beg, len, xpath->pxml);
+ }
+ return res;
+}
+
+
+/*
+ Tokens consisting of one character, for faster lexical analizer.
+*/
+static char simpletok[128]=
+{
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+/*
+ ! " # $ % & ' ( ) * + , - . / 0 1 2 3 4 5 6 7 8 9 : ; < = > ?
+ @ A B C D E F G H I J K L M N O P Q R S T U V W X Y Z [ \ ] ^ _
+ ` a b c d e f g h i j k l m n o p q r s t u v w x y z { | } ~ €
+*/
+ 0,1,0,0,1,0,0,0,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,1,0,1,1,1,0,
+ 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0
+};
+
+
+/*
+ XPath keywords
+*/
+struct my_xpath_keyword_names_st
+{
+ int tok;
+ const char *name;
+ size_t length;
+ int extra;
+};
+
+
+static struct my_xpath_keyword_names_st my_keyword_names[] =
+{
+ {MY_XPATH_LEX_AND , "and" , 3, 0 },
+ {MY_XPATH_LEX_OR , "or" , 2, 0 },
+ {MY_XPATH_LEX_DIV , "div" , 3, 0 },
+ {MY_XPATH_LEX_MOD , "mod" , 3, 0 },
+ {0,NULL,0,0}
+};
+
+
+static struct my_xpath_keyword_names_st my_axis_names[]=
+{
+ {MY_XPATH_LEX_AXIS,"ancestor" , 8,MY_XPATH_AXIS_ANCESTOR },
+ {MY_XPATH_LEX_AXIS,"ancestor-or-self" ,16,MY_XPATH_AXIS_ANCESTOR_OR_SELF },
+ {MY_XPATH_LEX_AXIS,"attribute" , 9,MY_XPATH_AXIS_ATTRIBUTE },
+ {MY_XPATH_LEX_AXIS,"child" , 5,MY_XPATH_AXIS_CHILD },
+ {MY_XPATH_LEX_AXIS,"descendant" ,10,MY_XPATH_AXIS_DESCENDANT },
+ {MY_XPATH_LEX_AXIS,"descendant-or-self",18,MY_XPATH_AXIS_DESCENDANT_OR_SELF},
+ {MY_XPATH_LEX_AXIS,"following" , 9,MY_XPATH_AXIS_FOLLOWING },
+ {MY_XPATH_LEX_AXIS,"following-sibling" ,17,MY_XPATH_AXIS_FOLLOWING_SIBLING },
+ {MY_XPATH_LEX_AXIS,"namespace" , 9,MY_XPATH_AXIS_NAMESPACE },
+ {MY_XPATH_LEX_AXIS,"parent" , 6,MY_XPATH_AXIS_PARENT },
+ {MY_XPATH_LEX_AXIS,"preceding" , 9,MY_XPATH_AXIS_PRECEDING },
+ {MY_XPATH_LEX_AXIS,"preceding-sibling" ,17,MY_XPATH_AXIS_PRECEDING_SIBLING },
+ {MY_XPATH_LEX_AXIS,"self" , 4,MY_XPATH_AXIS_SELF },
+ {0,NULL,0,0}
+};
+
+
+static struct my_xpath_keyword_names_st my_nodetype_names[]=
+{
+ {MY_XPATH_LEX_NODETYPE, "comment" , 7, 0 },
+ {MY_XPATH_LEX_NODETYPE, "text" , 4, 0 },
+ {MY_XPATH_LEX_NODETYPE, "processing-instruction" , 22,0 },
+ {MY_XPATH_LEX_NODETYPE, "node" , 4, 0 },
+ {0,NULL,0,0}
+};
+
+
+/*
+ Lookup a keyword
+
+ SYNOPSYS
+ Check that the last scanned identifier is a keyword.
+
+ RETURN
+ - Token type, on lookup success.
+ - MY_XPATH_LEX_IDENT, on lookup failure.
+*/
+static int
+my_xpath_keyword(MY_XPATH *x,
+ struct my_xpath_keyword_names_st *keyword_names,
+ const char *beg, const char *end)
+{
+ struct my_xpath_keyword_names_st *k;
+ size_t length= end-beg;
+ for (k= keyword_names; k->name; k++)
+ {
+ if (length == k->length && !strncasecmp(beg, k->name, length))
+ {
+ x->extra= k->extra;
+ return k->tok;
+ }
+ }
+ return MY_XPATH_LEX_IDENT;
+}
+
+
+/*
+ Functions to create an item, a-la those in item_create.cc
+*/
+
+static Item *create_func_true(MY_XPATH *xpath, Item **args, uint nargs)
+{
+ return new Item_bool(1);
+}
+
+
+static Item *create_func_false(MY_XPATH *xpath, Item **args, uint nargs)
+{
+ return new Item_bool(0);
+}
+
+
+static Item *create_func_not(MY_XPATH *xpath, Item **args, uint nargs)
+{
+ return new Item_func_not(nodeset2bool(xpath, args[0]));
+}
+
+
+static Item *create_func_ceiling(MY_XPATH *xpath, Item **args, uint nargs)
+{
+ return new Item_func_ceiling(args[0]);
+}
+
+
+static Item *create_func_floor(MY_XPATH *xpath, Item **args, uint nargs)
+{
+ return new Item_func_floor(args[0]);
+}
+
+
+static Item *create_func_bool(MY_XPATH *xpath, Item **args, uint nargs)
+{
+ return new Item_xpath_cast_bool(args[0], xpath->pxml);
+}
+
+
+static Item *create_func_number(MY_XPATH *xpath, Item **args, uint nargs)
+{
+ return new Item_xpath_cast_number(args[0]);
+}
+
+
+static Item *create_func_string_length(MY_XPATH *xpath, Item **args, uint nargs)
+{
+ Item *arg= nargs ? args[0] : xpath->context;
+ return arg ? new Item_func_char_length(arg) : 0;
+}
+
+
+static Item *create_func_round(MY_XPATH *xpath, Item **args, uint nargs)
+{
+ return new Item_func_round(args[0], new Item_int((char*)"0",0,1),0);
+}
+
+
+static Item *create_func_last(MY_XPATH *xpath, Item **args, uint nargs)
+{
+ return xpath->context ?
+ new Item_func_xpath_count(xpath->context, xpath->pxml) : NULL;
+}
+
+
+static Item *create_func_position(MY_XPATH *xpath, Item **args, uint nargs)
+{
+ return xpath->context ?
+ new Item_func_xpath_position(xpath->context, xpath->pxml) : NULL;
+}
+
+
+static Item *create_func_contains(MY_XPATH *xpath, Item **args, uint nargs)
+{
+ return new Item_xpath_cast_bool(new Item_func_locate(args[0], args[1]),
+ xpath->pxml);
+}
+
+
+static Item *create_func_concat(MY_XPATH *xpath, Item **args, uint nargs)
+{
+ return new Item_func_concat(args[0], args[1]);
+}
+
+
+static Item *create_func_substr(MY_XPATH *xpath, Item **args, uint nargs)
+{
+ if (nargs == 2)
+ return new Item_func_substr(args[0], args[1]);
+ else
+ return new Item_func_substr(args[0], args[1], args[2]);
+}
+
+
+static Item *create_func_count(MY_XPATH *xpath, Item **args, uint nargs)
+{
+ if (args[0]->type() != Item::XPATH_NODESET)
+ return 0;
+ return new Item_func_xpath_count(args[0], xpath->pxml);
+}
+
+
+static Item *create_func_sum(MY_XPATH *xpath, Item **args, uint nargs)
+{
+ if (args[0]->type() != Item::XPATH_NODESET)
+ return 0;
+ return new Item_func_xpath_sum(args[0], xpath->pxml);
+}
+
+
+/*
+ Functions names. Separate lists for names with
+ lengths 3,4,5 and 6 for faster lookups.
+*/
+static MY_XPATH_FUNC my_func_names3[]=
+{
+ {"sum", 3, 1 , 1 , create_func_sum},
+ {"not", 3, 1 , 1 , create_func_not},
+ {0 , 0, 0 , 0, 0}
+};
+
+
+static MY_XPATH_FUNC my_func_names4[]=
+{
+ {"last", 4, 0, 0, create_func_last},
+ {"true", 4, 0, 0, create_func_true},
+ {"name", 4, 0, 1, 0},
+ {"lang", 4, 1, 1, 0},
+ {0 , 0, 0, 0, 0}
+};
+
+
+static MY_XPATH_FUNC my_func_names5[]=
+{
+ {"count", 5, 1, 1, create_func_count},
+ {"false", 5, 0, 0, create_func_false},
+ {"floor", 5, 1, 1, create_func_floor},
+ {"round", 5, 1, 1, create_func_round},
+ {0 , 0, 0, 0, 0}
+};
+
+
+static MY_XPATH_FUNC my_func_names6[]=
+{
+ {"concat", 6, 2, 255, create_func_concat},
+ {"number", 6, 0, 1 , create_func_number},
+ {"string", 6, 0, 1 , 0},
+ {0 , 0, 0, 0 , 0}
+};
+
+
+/* Other functions, with name longer than 6, all together */
+static MY_XPATH_FUNC my_func_names[] =
+{
+ {"id" , 2 , 1 , 1 , 0},
+ {"boolean" , 7 , 1 , 1 , create_func_bool},
+ {"ceiling" , 7 , 1 , 1 , create_func_ceiling},
+ {"position" , 8 , 0 , 0 , create_func_position},
+ {"contains" , 8 , 2 , 2 , create_func_contains},
+ {"substring" , 9 , 2 , 3 , create_func_substr},
+ {"translate" , 9 , 3 , 3 , 0},
+
+ {"local-name" , 10 , 0 , 1 , 0},
+ {"starts-with" , 11 , 2 , 2 , 0},
+ {"namespace-uri" , 13 , 0 , 1 , 0},
+ {"string-length" , 13 , 0 , 1 , create_func_string_length},
+ {"substring-after" , 15 , 2 , 2 , 0},
+ {"normalize-space" , 15 , 0 , 1 , 0},
+ {"substring-before" , 16 , 2 , 2 , 0},
+
+ {NULL,0,0,0,0}
+};
+
+
+/*
+ Lookup a function by name
+
+ SYNOPSYS
+ Lookup a function by its name.
+
+ RETURN
+ Pointer to a MY_XPATH_FUNC variable on success.
+ 0 - on failure.
+
+*/
+MY_XPATH_FUNC *
+my_xpath_function(const char *beg, const char *end)
+{
+ MY_XPATH_FUNC *k, *function_names;
+ uint length= end-beg;
+ switch (length)
+ {
+ case 1: return 0;
+ case 3: function_names= my_func_names3; break;
+ case 4: function_names= my_func_names4; break;
+ case 5: function_names= my_func_names5; break;
+ case 6: function_names= my_func_names6; break;
+ default: function_names= my_func_names;
+ }
+ for (k= function_names; k->name; k++)
+ if (k->create && length == k->length && !strncasecmp(beg, k->name, length))
+ return k;
+ return NULL;
+}
+
+
+/* Initialize a lex analizer token */
+static void
+my_xpath_lex_init(MY_XPATH_LEX *lex,
+ const char *str, const char *strend)
+{
+ lex->beg= str;
+ lex->end= strend;
+}
+
+
+/* Initialize an XPath query parser */
+static void
+my_xpath_init(MY_XPATH *xpath)
+{
+ bzero((void*)xpath, sizeof(xpath[0]));
+}
+
+
+static int
+my_xdigit(int c)
+{
+ return ((c) >= '0' && (c) <= '9');
+}
+
+
+/*
+ Scan the next token
+
+ SYNOPSYS
+ Scan the next token from the input.
+ lex->term is set to the scanned token type.
+ lex->beg and lex->end are set to the beginnig
+ and to the end of the token.
+ RETURN
+ N/A
+*/
+static void
+my_xpath_lex_scan(MY_XPATH *xpath,
+ MY_XPATH_LEX *lex, const char *beg, const char *end)
+{
+ int ch, ctype, length;
+ for ( ; beg < end && *beg == ' ' ; beg++); // skip leading spaces
+ lex->beg= beg;
+
+ if (beg >= end)
+ {
+ lex->end= beg;
+ lex->term= MY_XPATH_LEX_EOF; // end of line reached
+ return;
+ }
+
+ // Check ident, or a function call, or a keyword
+ if ((length= xpath->cs->cset->ctype(xpath->cs, &ctype,
+ (const uchar*) beg,
+ (const uchar*) end)) > 0 &&
+ ((ctype & (_MY_L | _MY_U)) || *beg == '_'))
+ {
+ // scan untill the end of the idenfitier
+ for (beg+= length;
+ (length= xpath->cs->cset->ctype(xpath->cs, &ctype,
+ (const uchar*) beg,
+ (const uchar*) end)) > 0 &&
+ ((ctype & (_MY_L | _MY_U | _MY_NMR)) ||
+ *beg == '_' || *beg == '-' || *beg == '.') ;
+ beg+= length) /* no op */;
+ lex->end= beg;
+
+ if (beg < end)
+ {
+ if (*beg == '(')
+ {
+ /*
+ check if a function call, e.g.: count(/a/b)
+ or a nodetype test, e.g.: /a/b/text()
+ */
+ if ((xpath->func= my_xpath_function(lex->beg, beg)))
+ lex->term= MY_XPATH_LEX_FUNC;
+ else
+ lex->term= my_xpath_keyword(xpath, my_nodetype_names,
+ lex->beg, beg);
+ return;
+ }
+ // check if an axis specifier, e.g.: /a/b/child::*
+ else if (*beg == ':' && beg + 1 < end && beg[1] == ':')
+ {
+ lex->term= my_xpath_keyword(xpath, my_axis_names,
+ lex->beg, beg);
+ return;
+ }
+ }
+ // check if a keyword
+ lex->term= my_xpath_keyword(xpath, my_keyword_names,
+ lex->beg, beg);
+ return;
+ }
+
+
+ ch= *beg++;
+
+ if (ch > 0 && ch < 128 && simpletok[ch])
+ {
+ // a token consisting of one character found
+ lex->end= beg;
+ lex->term= ch;
+ return;
+ }
+
+
+ if (my_xdigit(ch)) // a sequence of digits
+ {
+ for ( ; beg < end && my_xdigit(*beg) ; beg++);
+ lex->end= beg;
+ lex->term= MY_XPATH_LEX_DIGITS;
+ return;
+ }
+
+ if (ch == '"' || ch == '\'') // a string: either '...' or "..."
+ {
+ for ( ; beg < end && *beg != ch ; beg++);
+ if (beg < end)
+ {
+ lex->end= beg+1;
+ lex->term= MY_XPATH_LEX_STRING;
+ return;
+ }
+ else
+ {
+ // unexpected end-of-line, without closing quot sign
+ lex->end= end;
+ lex->term= MY_XPATH_LEX_ERROR;
+ return;
+ }
+ }
+
+ lex->end= beg;
+ lex->term= MY_XPATH_LEX_ERROR; // unknown character
+ return;
+}
+
+
+/*
+ Scan the given token
+
+ SYNOPSYS
+ Scan the given token and rotate lasttok to prevtok on success.
+
+ RETURN
+ 1 - success
+ 0 - failure
+*/
+static int
+my_xpath_parse_term(MY_XPATH *xpath, int term)
+{
+ if (xpath->lasttok.term == term && !xpath->error)
+ {
+ xpath->prevtok= xpath->lasttok;
+ my_xpath_lex_scan(xpath, &xpath->lasttok,
+ xpath->lasttok.end, xpath->query.end);
+ return 1;
+ }
+ return 0;
+}
+
+
+/*
+ Scan AxisName
+
+ SYNOPSYS
+ Scan an axis name and store the scanned axis type into xpath->axis.
+
+ RETURN
+ 1 - success
+ 0 - failure
+*/
+static int my_xpath_parse_AxisName(MY_XPATH *xpath)
+{
+ int rc= my_xpath_parse_term(xpath, MY_XPATH_LEX_AXIS);
+ xpath->axis= xpath->extra;
+ return rc;
+}
+
+
+/*********************************************
+** Grammar rules, according to http://www.w3.org/TR/xpath
+** Implemented using recursive descendant method.
+** All the following grammar processing functions accept
+** a signle "xpath" argument and return 1 on success and 0 on error.
+** They also modify "xpath" argument by creating new items.
+*/
+
+/* [9] PredicateExpr ::= Expr */
+#define my_xpath_parse_PredicateExpr(x) my_xpath_parse_Expr((x))
+
+/* [14] Expr ::= OrExpr */
+#define my_xpath_parse_Expr(x) my_xpath_parse_OrExpr((x))
+
+static int my_xpath_parse_LocationPath(MY_XPATH *xpath);
+static int my_xpath_parse_AbsoluteLocationPath(MY_XPATH *xpath);
+static int my_xpath_parse_RelativeLocationPath(MY_XPATH *xpath);
+static int my_xpath_parse_AbbreviatedStep(MY_XPATH *xpath);
+static int my_xpath_parse_Step(MY_XPATH *xpath);
+static int my_xpath_parse_AxisSpecifier(MY_XPATH *xpath);
+static int my_xpath_parse_NodeTest(MY_XPATH *xpath);
+static int my_xpath_parse_AbbreviatedAxisSpecifier(MY_XPATH *xpath);
+static int my_xpath_parse_NameTest(MY_XPATH *xpath);
+static int my_xpath_parse_FunctionCall(MY_XPATH *xpath);
+static int my_xpath_parse_Number(MY_XPATH *xpath);
+static int my_xpath_parse_FilterExpr(MY_XPATH *xpath);
+static int my_xpath_parse_PathExpr(MY_XPATH *xpath);
+static int my_xpath_parse_OrExpr(MY_XPATH *xpath);
+static int my_xpath_parse_UnaryExpr(MY_XPATH *xpath);
+static int my_xpath_parse_MultiplicativeExpr(MY_XPATH *xpath);
+static int my_xpath_parse_AdditiveExpr(MY_XPATH *xpath);
+static int my_xpath_parse_RelationalExpr(MY_XPATH *xpath);
+static int my_xpath_parse_AndExpr(MY_XPATH *xpath);
+static int my_xpath_parse_EqualityExpr(MY_XPATH *xpath);
+static int my_xpath_parse_VariableReference(MY_XPATH *xpath);
+
+
+/*
+ Scan LocationPath
+
+ SYNOPSYS
+
+ [1] LocationPath ::= RelativeLocationPath
+ | AbsoluteLocationPath
+
+ RETURN
+ 1 - success
+ 0 - failure
+*/
+static int my_xpath_parse_LocationPath(MY_XPATH *xpath)
+{
+ Item *context= xpath->context;
+
+ if (!xpath->context)
+ xpath->context= xpath->rootelement;
+ int rc= my_xpath_parse_RelativeLocationPath(xpath) ||
+ my_xpath_parse_AbsoluteLocationPath(xpath);
+
+ xpath->item= xpath->context;
+ xpath->context= context;
+ return rc;
+}
+
+
+/*
+ Scan Absolute Location Path
+
+ SYNOPSYS
+
+ [2] AbsoluteLocationPath ::= '/' RelativeLocationPath?
+ | AbbreviatedAbsoluteLocationPath
+ [10] AbbreviatedAbsoluteLocationPath ::= '//' RelativeLocationPath
+
+ We combine these two rules into one rule for better performance:
+
+ [2,10] AbsoluteLocationPath ::= '/' RelativeLocationPath?
+ | '//' RelativeLocationPath
+
+ RETURN
+ 1 - success
+ 0 - failure
+*/
+static int my_xpath_parse_AbsoluteLocationPath(MY_XPATH *xpath)
+{
+ if (!my_xpath_parse_term(xpath, MY_XPATH_LEX_SLASH))
+ return 0;
+
+ xpath->context= xpath->rootelement;
+
+ if (my_xpath_parse_term(xpath, MY_XPATH_LEX_SLASH))
+ {
+ xpath->context= new Item_nodeset_func_descendantbyname(xpath->context,
+ "*", 1,
+ xpath->pxml, 1);
+ return my_xpath_parse_RelativeLocationPath(xpath);
+ }
+
+ my_xpath_parse_RelativeLocationPath(xpath);
+
+ return (xpath->error == 0);
+}
+
+
+/*
+ Scan Relative Location Path
+
+ SYNOPSYS
+
+ For better performance we combine these two rules
+
+ [3] RelativeLocationPath ::= Step
+ | RelativeLocationPath '/' Step
+ | AbbreviatedRelativeLocationPath
+ [11] AbbreviatedRelativeLocationPath ::= RelativeLocationPath '//' Step
+
+
+ Into this one:
+
+ [3-11] RelativeLocationPath ::= Step
+ | RelativeLocationPath '/' Step
+ | RelativeLocationPath '//' Step
+ RETURN
+ 1 - success
+ 0 - failure
+*/
+static int my_xpath_parse_RelativeLocationPath(MY_XPATH *xpath)
+{
+ if (!my_xpath_parse_Step(xpath))
+ return 0;
+ while (my_xpath_parse_term(xpath, MY_XPATH_LEX_SLASH))
+ {
+ if (my_xpath_parse_term(xpath, MY_XPATH_LEX_SLASH))
+ xpath->context= new Item_nodeset_func_descendantbyname(xpath->context,
+ "*", 1,
+ xpath->pxml, 1);
+ if (!my_xpath_parse_Step(xpath))
+ {
+ xpath->error= 1;
+ return 0;
+ }
+ }
+ return 1;
+}
+
+
+/*
+ Scan non-abbreviated or abbreviated Step
+
+ SYNOPSYS
+
+ [4] Step ::= AxisSpecifier NodeTest Predicate*
+ | AbbreviatedStep
+ [8] Predicate ::= '[' PredicateExpr ']'
+
+ RETURN
+ 1 - success
+ 0 - failure
+*/
+static int
+my_xpath_parse_AxisSpecifier_NodeTest_opt_Predicate_list(MY_XPATH *xpath)
+{
+ if (!my_xpath_parse_AxisSpecifier(xpath))
+ return 0;
+
+ if (!my_xpath_parse_NodeTest(xpath))
+ return 0;
+
+ while (my_xpath_parse_term(xpath, MY_XPATH_LEX_LB))
+ {
+ Item *prev_context= xpath->context;
+ String *context_cache;
+ context_cache= &((Item_nodeset_func*)xpath->context)->context_cache;
+ xpath->context= new Item_nodeset_context_cache(context_cache, xpath->pxml);
+ xpath->context_cache= context_cache;
+
+ if(!my_xpath_parse_PredicateExpr(xpath))
+ {
+ xpath->error= 1;
+ return 0;
+ }
+
+ if (!my_xpath_parse_term(xpath, MY_XPATH_LEX_RB))
+ {
+ xpath->error= 1;
+ return 0;
+ }
+
+ xpath->item= nodeset2bool(xpath, xpath->item);
+
+ if (xpath->item->is_bool_func())
+ {
+ xpath->context= new Item_nodeset_func_predicate(prev_context,
+ xpath->item,
+ xpath->pxml);
+ }
+ else
+ {
+ xpath->context= new Item_nodeset_func_elementbyindex(prev_context,
+ xpath->item,
+ xpath->pxml);
+ }
+ }
+ return 1;
+}
+
+
+static int my_xpath_parse_Step(MY_XPATH *xpath)
+{
+ return
+ my_xpath_parse_AxisSpecifier_NodeTest_opt_Predicate_list(xpath) ||
+ my_xpath_parse_AbbreviatedStep(xpath);
+}
+
+
+/*
+ Scan Abbreviated Axis Specifier
+
+ SYNOPSYS
+ [5] AxisSpecifier ::= AxisName '::'
+ | AbbreviatedAxisSpecifier
+
+ RETURN
+ 1 - success
+ 0 - failure
+*/
+static int my_xpath_parse_AbbreviatedAxisSpecifier(MY_XPATH *xpath)
+{
+ if (my_xpath_parse_term(xpath, MY_XPATH_LEX_AT))
+ xpath->axis= MY_XPATH_AXIS_ATTRIBUTE;
+ else
+ xpath->axis= MY_XPATH_AXIS_CHILD;
+ return 1;
+}
+
+
+/*
+ Scan non-abbreviated axis specifier
+
+ SYNOPSYS
+
+ RETURN
+ 1 - success
+ 0 - failure
+*/
+static int my_xpath_parse_AxisName_colon_colon(MY_XPATH *xpath)
+{
+ return my_xpath_parse_AxisName(xpath) &&
+ my_xpath_parse_term(xpath, MY_XPATH_LEX_COLON) &&
+ my_xpath_parse_term(xpath, MY_XPATH_LEX_COLON);
+}
+
+
+/*
+ Scan Abbreviated AxisSpecifier
+
+ SYNOPSYS
+ [13] AbbreviatedAxisSpecifier ::= '@'?
+
+ RETURN
+ 1 - success
+ 0 - failure
+*/
+static int my_xpath_parse_AxisSpecifier(MY_XPATH *xpath)
+{
+ return my_xpath_parse_AxisName_colon_colon(xpath) ||
+ my_xpath_parse_AbbreviatedAxisSpecifier(xpath);
+}
+
+
+/*
+ Scan NodeType followed by parens
+
+ SYNOPSYS
+
+ RETURN
+ 1 - success
+ 0 - failure
+*/
+static int my_xpath_parse_NodeTest_lp_rp(MY_XPATH *xpath)
+{
+ return my_xpath_parse_term(xpath, MY_XPATH_LEX_NODETYPE) &&
+ my_xpath_parse_term(xpath, MY_XPATH_LEX_LP) &&
+ my_xpath_parse_term(xpath, MY_XPATH_LEX_RP);
+}
+
+
+/*
+ Scan NodeTest
+
+ SYNOPSYS
+
+ [7] NodeTest ::= NameTest
+ | NodeType '(' ')'
+ | 'processing-instruction' '(' Literal ')'
+ RETURN
+ 1 - success
+ 0 - failure
+*/
+static int my_xpath_parse_NodeTest(MY_XPATH *xpath)
+{
+ return my_xpath_parse_NameTest(xpath) ||
+ my_xpath_parse_NodeTest_lp_rp(xpath);
+}
+
+
+/*
+ Scan Abbreviated Step
+
+ SYNOPSYS
+
+ [12] AbbreviatedStep ::= '.' | '..'
+
+ RETURN
+ 1 - success
+ 0 - failure
+*/
+static int my_xpath_parse_AbbreviatedStep(MY_XPATH *xpath)
+{
+ if (!my_xpath_parse_term(xpath, MY_XPATH_LEX_DOT))
+ return 0;
+ if (my_xpath_parse_term(xpath, MY_XPATH_LEX_DOT))
+ xpath->context= new Item_nodeset_func_parentbyname(xpath->context, "*", 1,
+ xpath->pxml);
+ return 1;
+}
+
+
+/*
+ Scan Primary Expression
+
+ SYNOPSYS
+
+ [15] PrimaryExpr ::= VariableReference
+ | '(' Expr ')'
+ | Literal
+ | Number
+ | FunctionCall
+ RETURN
+ 1 - success
+ 0 - failure
+*/
+static int my_xpath_parse_lp_Expr_rp(MY_XPATH *xpath)
+{
+ return my_xpath_parse_term(xpath, MY_XPATH_LEX_LP) &&
+ my_xpath_parse_Expr(xpath) &&
+ my_xpath_parse_term(xpath, MY_XPATH_LEX_RP);
+}
+static int my_xpath_parse_PrimaryExpr_literal(MY_XPATH *xpath)
+{
+ if (!my_xpath_parse_term(xpath, MY_XPATH_LEX_STRING))
+ return 0;
+ xpath->item= new Item_string(xpath->prevtok.beg + 1,
+ xpath->prevtok.end - xpath->prevtok.beg - 2,
+ xpath->cs);
+ return 1;
+}
+static int my_xpath_parse_PrimaryExpr(MY_XPATH *xpath)
+{
+ return
+ my_xpath_parse_lp_Expr_rp(xpath) ||
+ my_xpath_parse_VariableReference(xpath) ||
+ my_xpath_parse_PrimaryExpr_literal(xpath) ||
+ my_xpath_parse_Number(xpath) ||
+ my_xpath_parse_FunctionCall(xpath);
+}
+
+
+/*
+ Scan Function Call
+
+ SYNOPSYS
+ [16] FunctionCall ::= FunctionName '(' ( Argument ( ',' Argument )* )? ')'
+ [17] Argument ::= Expr
+
+ RETURN
+ 1 - success
+ 0 - failure
+
+*/
+static int my_xpath_parse_FunctionCall(MY_XPATH *xpath)
+{
+ Item *args[256];
+ uint nargs;
+
+ if (!my_xpath_parse_term(xpath, MY_XPATH_LEX_FUNC))
+ return 0;
+
+ MY_XPATH_FUNC *func= xpath->func;
+
+ if (!my_xpath_parse_term(xpath, MY_XPATH_LEX_LP))
+ return 0;
+
+ for (nargs= 0 ; nargs < func->maxargs; )
+ {
+ if (!my_xpath_parse_Expr(xpath))
+ {
+ if (nargs < func->minargs)
+ return 0;
+ goto right_paren;
+ }
+ args[nargs++]= xpath->item;
+ if (!my_xpath_parse_term(xpath, MY_XPATH_LEX_COMMA))
+ {
+ if (nargs < func->minargs)
+ return 0;
+ else
+ break;
+ }
+ }
+
+right_paren:
+ if (!my_xpath_parse_term(xpath, MY_XPATH_LEX_RP))
+ return 0;
+
+ return ((xpath->item= func->create(xpath, args, nargs))) ? 1 : 0;
+}
+
+
+/*
+ Scan Union Expression
+
+ SYNOPSYS
+ [18] UnionExpr ::= PathExpr
+ | UnionExpr '|' PathExpr
+
+ RETURN
+ 1 - success
+ 0 - failure
+*/
+static int my_xpath_parse_UnionExpr(MY_XPATH *xpath)
+{
+ if (!my_xpath_parse_PathExpr(xpath))
+ return 0;
+
+ while (my_xpath_parse_term(xpath, MY_XPATH_LEX_VLINE))
+ {
+ Item *prev= xpath->item;
+ if (prev->type() != Item::XPATH_NODESET)
+ return 0;
+
+ if (!my_xpath_parse_PathExpr(xpath)
+ || xpath->item->type() != Item::XPATH_NODESET)
+ {
+ xpath->error= 1;
+ return 0;
+ }
+ xpath->item= new Item_nodeset_func_union(prev, xpath->item, xpath->pxml);
+ }
+ return 1;
+}
+
+
+/*
+ Scan Path Expression
+
+ SYNOPSYS
+
+ [19] PathExpr ::= LocationPath
+ | FilterExpr
+ | FilterExpr '/' RelativeLocationPath
+ | FilterExpr '//' RelativeLocationPath
+ RETURN
+ 1 - success
+ 0 - failure
+*/
+static int
+my_xpath_parse_FilterExpr_opt_slashes_RelativeLocationPath(MY_XPATH *xpath)
+{
+ if (!my_xpath_parse_FilterExpr(xpath))
+ return 0;
+
+ if (!my_xpath_parse_term(xpath, MY_XPATH_LEX_SLASH))
+ return 1;
+
+ if (xpath->item->type() != Item::XPATH_NODESET)
+ {
+ xpath->lasttok= xpath->prevtok;
+ xpath->error= 1;
+ return 0;
+ }
+
+ my_xpath_parse_term(xpath, MY_XPATH_LEX_SLASH);
+ return my_xpath_parse_RelativeLocationPath(xpath);
+}
+static int my_xpath_parse_PathExpr(MY_XPATH *xpath)
+{
+ return my_xpath_parse_LocationPath(xpath) ||
+ my_xpath_parse_FilterExpr_opt_slashes_RelativeLocationPath(xpath);
+}
+
+
+
+/*
+ Scan Filter Expression
+
+ SYNOPSYS
+ [20] FilterExpr ::= PrimaryExpr
+ | FilterExpr Predicate
+
+ or in other words:
+
+ [20] FilterExpr ::= PrimaryExpr Predicate*
+
+ RETURN
+ 1 - success
+ 0 - failure
+
+*/
+static int my_xpath_parse_FilterExpr(MY_XPATH *xpath)
+{
+ return my_xpath_parse_PrimaryExpr(xpath);
+}
+
+
+/*
+ Scan Or Expression
+
+ SYNOPSYS
+ [21] OrExpr ::= AndExpr
+ | OrExpr 'or' AndExpr
+
+ RETURN
+ 1 - success
+ 0 - failure
+*/
+static int my_xpath_parse_OrExpr(MY_XPATH *xpath)
+{
+ if (!my_xpath_parse_AndExpr(xpath))
+ return 0;
+
+ while (my_xpath_parse_term(xpath, MY_XPATH_LEX_OR))
+ {
+ Item *prev= xpath->item;
+ if (!my_xpath_parse_AndExpr(xpath))
+ {
+ return 0;
+ xpath->error= 1;
+ }
+ xpath->item= new Item_cond_or(nodeset2bool(xpath, prev),
+ nodeset2bool(xpath, xpath->item));
+ }
+ return 1;
+}
+
+
+/*
+ Scan And Expression
+
+ SYNOPSYS
+ [22] AndExpr ::= EqualityExpr
+ | AndExpr 'and' EqualityExpr
+
+ RETURN
+ 1 - success
+ 0 - failure
+*/
+static int my_xpath_parse_AndExpr(MY_XPATH *xpath)
+{
+ if (!my_xpath_parse_EqualityExpr(xpath))
+ return 0;
+
+ while (my_xpath_parse_term(xpath, MY_XPATH_LEX_AND))
+ {
+ Item *prev= xpath->item;
+ if (!my_xpath_parse_EqualityExpr(xpath))
+ {
+ xpath->error= 1;
+ return 0;
+ }
+
+ xpath->item= new Item_cond_and(nodeset2bool(xpath,prev),
+ nodeset2bool(xpath,xpath->item));
+ }
+ return 1;
+}
+
+
+/*
+ Scan Equality Expression
+
+ SYNOPSYS
+ [23] EqualityExpr ::= RelationalExpr
+ | EqualityExpr '=' RelationalExpr
+ | EqualityExpr '!=' RelationalExpr
+ or in other words:
+
+ [23] EqualityExpr ::= RelationalExpr ( EqualityOperator EqualityExpr )*
+
+ RETURN
+ 1 - success
+ 0 - failure
+*/
+static int my_xpath_parse_ne(MY_XPATH *xpath)
+{
+ MY_XPATH_LEX prevtok= xpath->prevtok;
+ if (!my_xpath_parse_term(xpath, MY_XPATH_LEX_EXCL))
+ return 0;
+ if (!my_xpath_parse_term(xpath, MY_XPATH_LEX_EQ))
+ {
+ /* Unget the exclamation mark */
+ xpath->lasttok= xpath->prevtok;
+ xpath->prevtok= prevtok;
+ return 0;
+ }
+ return 1;
+}
+static int my_xpath_parse_EqualityOperator(MY_XPATH *xpath)
+{
+ if (my_xpath_parse_ne(xpath))
+ {
+ xpath->extra= '!';
+ return 1;
+ }
+ if (my_xpath_parse_term(xpath, MY_XPATH_LEX_EQ))
+ {
+ xpath->extra= '=';
+ return 1;
+ }
+ return 0;
+}
+static int my_xpath_parse_EqualityExpr(MY_XPATH *xpath)
+{
+ MY_XPATH_LEX operator_context;
+ if (!my_xpath_parse_RelationalExpr(xpath))
+ return 0;
+
+ operator_context= xpath->lasttok;
+ while (my_xpath_parse_EqualityOperator(xpath))
+ {
+ Item *prev= xpath->item;
+ int oper= xpath->extra;
+ if (!my_xpath_parse_RelationalExpr(xpath))
+ {
+ xpath->error= 1;
+ return 0;
+ }
+
+ if (!(xpath->item= create_comparator(xpath, oper, &operator_context,
+ prev, xpath->item)))
+ return 0;
+
+ operator_context= xpath->lasttok;
+ }
+ return 1;
+}
+
+
+/*
+ Scan Relational Expression
+
+ SYNOPSYS
+
+ [24] RelationalExpr ::= AdditiveExpr
+ | RelationalExpr '<' AdditiveExpr
+ | RelationalExpr '>' AdditiveExpr
+ | RelationalExpr '<=' AdditiveExpr
+ | RelationalExpr '>=' AdditiveExpr
+ or in other words:
+
+ [24] RelationalExpr ::= AdditiveExpr (RelationalOperator RelationalExpr)*
+
+ RETURN
+ 1 - success
+ 0 - failure
+*/
+static int my_xpath_parse_RelationalOperator(MY_XPATH *xpath)
+{
+ if (my_xpath_parse_term(xpath, MY_XPATH_LEX_LESS))
+ {
+ xpath->extra= my_xpath_parse_term(xpath, MY_XPATH_LEX_EQ) ?
+ MY_XPATH_LEX_LE : MY_XPATH_LEX_LESS;
+ return 1;
+ }
+ else if (my_xpath_parse_term(xpath, MY_XPATH_LEX_GREATER))
+ {
+ xpath->extra= my_xpath_parse_term(xpath, MY_XPATH_LEX_EQ) ?
+ MY_XPATH_LEX_GE : MY_XPATH_LEX_GREATER;
+ return 1;
+ }
+ return 0;
+}
+static int my_xpath_parse_RelationalExpr(MY_XPATH *xpath)
+{
+ MY_XPATH_LEX operator_context;
+ if (!my_xpath_parse_AdditiveExpr(xpath))
+ return 0;
+ operator_context= xpath->lasttok;
+ while (my_xpath_parse_RelationalOperator(xpath))
+ {
+ Item *prev= xpath->item;
+ int oper= xpath->extra;
+
+ if (!my_xpath_parse_AdditiveExpr(xpath))
+ {
+ xpath->error= 1;
+ return 0;
+ }
+
+ if (!(xpath->item= create_comparator(xpath, oper, &operator_context,
+ prev, xpath->item)))
+ return 0;
+ operator_context= xpath->lasttok;
+ }
+ return 1;
+}
+
+
+/*
+ Scan Additive Expression
+
+ SYNOPSYS
+
+ [25] AdditiveExpr ::= MultiplicativeExpr
+ | AdditiveExpr '+' MultiplicativeExpr
+ | AdditiveExpr '-' MultiplicativeExpr
+ RETURN
+ 1 - success
+ 0 - failure
+*/
+static int my_xpath_parse_AdditiveOperator(MY_XPATH *xpath)
+{
+ return my_xpath_parse_term(xpath, MY_XPATH_LEX_PLUS) ||
+ my_xpath_parse_term(xpath, MY_XPATH_LEX_MINUS);
+}
+static int my_xpath_parse_AdditiveExpr(MY_XPATH *xpath)
+{
+ if (!my_xpath_parse_MultiplicativeExpr(xpath))
+ return 0;
+
+ while (my_xpath_parse_AdditiveOperator(xpath))
+ {
+ int oper= xpath->prevtok.term;
+ Item *prev= xpath->item;
+ if (!my_xpath_parse_MultiplicativeExpr(xpath))
+ {
+ xpath->error= 1;
+ return 0;
+ }
+
+ if (oper == MY_XPATH_LEX_PLUS)
+ xpath->item= new Item_func_plus(prev, xpath->item);
+ else
+ xpath->item= new Item_func_minus(prev, xpath->item);
+ };
+ return 1;
+}
+
+
+/*
+ Scan Multiplicative Expression
+
+ SYNOPSYS
+
+ [26] MultiplicativeExpr ::= UnaryExpr
+ | MultiplicativeExpr MultiplyOperator UnaryExpr
+ | MultiplicativeExpr 'div' UnaryExpr
+ | MultiplicativeExpr 'mod' UnaryExpr
+ or in other words:
+
+ [26] MultiplicativeExpr ::= UnaryExpr (MulOper MultiplicativeExpr)*
+
+ RETURN
+ 1 - success
+ 0 - failure
+*/
+static int my_xpath_parse_MultiplicativeOperator(MY_XPATH *xpath)
+{
+ return
+ my_xpath_parse_term(xpath, MY_XPATH_LEX_ASTERISK) ||
+ my_xpath_parse_term(xpath, MY_XPATH_LEX_DIV) ||
+ my_xpath_parse_term(xpath, MY_XPATH_LEX_MOD);
+}
+static int my_xpath_parse_MultiplicativeExpr(MY_XPATH *xpath)
+{
+ if (!my_xpath_parse_UnaryExpr(xpath))
+ return 0;
+
+ while (my_xpath_parse_MultiplicativeOperator(xpath))
+ {
+ int oper= xpath->prevtok.term;
+ Item *prev= xpath->item;
+ if (!my_xpath_parse_UnaryExpr(xpath))
+ {
+ xpath->error= 1;
+ return 0;
+ }
+ switch (oper)
+ {
+ case MY_XPATH_LEX_ASTERISK:
+ xpath->item= new Item_func_mul(prev, xpath->item);
+ break;
+ case MY_XPATH_LEX_DIV:
+ xpath->item= new Item_func_int_div(prev, xpath->item);
+ break;
+ case MY_XPATH_LEX_MOD:
+ xpath->item= new Item_func_mod(prev, xpath->item);
+ break;
+ }
+ }
+ return 1;
+}
+
+
+/*
+ Scan Unary Expression
+
+ SYNOPSYS
+
+ [27] UnaryExpr ::= UnionExpr
+ | '-' UnaryExpr
+ RETURN
+ 1 - success
+ 0 - failure
+*/
+static int my_xpath_parse_UnaryExpr(MY_XPATH *xpath)
+{
+ if (!my_xpath_parse_term(xpath, MY_XPATH_LEX_MINUS))
+ return my_xpath_parse_UnionExpr(xpath);
+ if (!my_xpath_parse_UnaryExpr(xpath))
+ return 0;
+ xpath->item= new Item_func_neg(xpath->item);
+ return 1;
+}
+
+
+/*
+ Scan Number
+
+ SYNOPSYS
+
+ [30] Number ::= Digits ('.' Digits?)? | '.' Digits)
+
+ or in other words:
+
+ [30] Number ::= Digits
+ | Digits '.'
+ | Digits '.' Digits
+ | '.' Digits
+
+ Note: the last rule is not supported yet,
+ as it is in conflict with abbreviated step.
+ 1 + .123 does not work,
+ 1 + 0.123 does.
+ Perhaps it is better to move this code into lex analizer.
+
+ RETURN
+ 1 - success
+ 0 - failure
+*/
+static int my_xpath_parse_Number(MY_XPATH *xpath)
+{
+ const char *beg;
+ if (!my_xpath_parse_term(xpath, MY_XPATH_LEX_DIGITS))
+ return 0;
+ beg= xpath->prevtok.beg;
+ if (!my_xpath_parse_term(xpath, MY_XPATH_LEX_DOT))
+ {
+ xpath->item= new Item_int(xpath->prevtok.beg,
+ xpath->prevtok.end - xpath->prevtok.beg);
+ return 1;
+ }
+ my_xpath_parse_term(xpath, MY_XPATH_LEX_DIGITS);
+
+ xpath->item= new Item_float(beg, xpath->prevtok.end - beg);
+ return 1;
+}
+
+
+/*
+ Scan NCName.
+
+ SYNOPSYS
+
+ The keywords AND, OR, MOD, DIV are valid identitiers
+ when they are in identifier context:
+
+ SELECT
+ ExtractValue('<and><or><mod><div>VALUE</div></mod></or></and>',
+ '/and/or/mod/div')
+ -> VALUE
+
+ RETURN
+ 1 - success
+ 0 - failure
+*/
+
+static int
+my_xpath_parse_NCName(MY_XPATH *xpath)
+{
+ return
+ my_xpath_parse_term(xpath, MY_XPATH_LEX_IDENT) ||
+ my_xpath_parse_term(xpath, MY_XPATH_LEX_AND) ||
+ my_xpath_parse_term(xpath, MY_XPATH_LEX_OR) ||
+ my_xpath_parse_term(xpath, MY_XPATH_LEX_MOD) ||
+ my_xpath_parse_term(xpath, MY_XPATH_LEX_DIV) ? 1 : 0;
+}
+
+
+/*
+ QName grammar can be found in a separate document
+ http://www.w3.org/TR/REC-xml-names/#NT-QName
+
+ [6] QName ::= (Prefix ':')? LocalPart
+ [7] Prefix ::= NCName
+ [8] LocalPart ::= NCName
+*/
+
+static int
+my_xpath_parse_QName(MY_XPATH *xpath)
+{
+ const char *beg;
+ if (!my_xpath_parse_NCName(xpath))
+ return 0;
+ beg= xpath->prevtok.beg;
+ if (!my_xpath_parse_term(xpath, MY_XPATH_LEX_COLON))
+ return 1; /* Non qualified name */
+ if (!my_xpath_parse_NCName(xpath))
+ return 0;
+ xpath->prevtok.beg= beg;
+ return 1;
+}
+
+
+/**
+ Scan Variable reference
+
+ @details Implements parsing of two syntax structures:
+
+ 1. Standard XPath syntax [36], for SP variables:
+
+ VariableReference ::= '$' QName
+
+ Finds a SP variable with the given name.
+ If outside of a SP context, or variable with
+ the given name doesn't exists, then error is returned.
+
+ 2. Non-standard syntax - MySQL extension for user variables:
+
+ VariableReference ::= '$' '@' QName
+
+ Item, corresponding to the variable, is returned
+ in xpath->item in both cases.
+
+ @param xpath pointer to XPath structure
+
+ @return Operation status
+ @retval 1 Success
+ @retval 0 Failure
+*/
+
+static int
+my_xpath_parse_VariableReference(MY_XPATH *xpath)
+{
+ LEX_STRING name;
+ int user_var;
+ const char *dollar_pos;
+ if (!my_xpath_parse_term(xpath, MY_XPATH_LEX_DOLLAR) ||
+ (!(dollar_pos= xpath->prevtok.beg)) ||
+ (!((user_var= my_xpath_parse_term(xpath, MY_XPATH_LEX_AT) &&
+ my_xpath_parse_term(xpath, MY_XPATH_LEX_IDENT))) &&
+ !my_xpath_parse_term(xpath, MY_XPATH_LEX_IDENT)))
+ return 0;
+
+ name.length= xpath->prevtok.end - xpath->prevtok.beg;
+ name.str= (char*) xpath->prevtok.beg;
+
+ if (user_var)
+ xpath->item= new Item_func_get_user_var(name);
+ else
+ {
+ sp_variable_t *spv;
+ sp_pcontext *spc;
+ LEX *lex;
+ if ((lex= current_thd->lex) &&
+ (spc= lex->spcont) &&
+ (spv= spc->find_variable(&name)))
+ {
+ Item_splocal *splocal= new Item_splocal(name, spv->offset, spv->type, 0);
+#ifndef DBUG_OFF
+ if (splocal)
+ splocal->m_sp= lex->sphead;
+#endif
+ xpath->item= (Item*) splocal;
+ }
+ else
+ {
+ xpath->item= NULL;
+ DBUG_ASSERT(xpath->query.end > dollar_pos);
+ uint len= xpath->query.end - dollar_pos;
+ set_if_smaller(len, 32);
+ my_printf_error(ER_UNKNOWN_ERROR, "Unknown XPATH variable at: '%.*s'",
+ MYF(0), len, dollar_pos);
+ }
+ }
+ return xpath->item ? 1 : 0;
+}
+
+
+/*
+ Scan Name Test
+
+ SYNOPSYS
+
+ [37] NameTest ::= '*'
+ | NCName ':' '*'
+ | QName
+ RETURN
+ 1 - success
+ 0 - failure
+*/
+static int
+my_xpath_parse_NodeTest_QName(MY_XPATH *xpath)
+{
+ if (!my_xpath_parse_QName(xpath))
+ return 0;
+ DBUG_ASSERT(xpath->context);
+ uint len= xpath->prevtok.end - xpath->prevtok.beg;
+ xpath->context= nametestfunc(xpath, xpath->axis, xpath->context,
+ xpath->prevtok.beg, len);
+ return 1;
+}
+static int
+my_xpath_parse_NodeTest_asterisk(MY_XPATH *xpath)
+{
+ if (!my_xpath_parse_term(xpath, MY_XPATH_LEX_ASTERISK))
+ return 0;
+ DBUG_ASSERT(xpath->context);
+ xpath->context= nametestfunc(xpath, xpath->axis, xpath->context, "*", 1);
+ return 1;
+}
+static int
+my_xpath_parse_NameTest(MY_XPATH *xpath)
+{
+ return my_xpath_parse_NodeTest_asterisk(xpath) ||
+ my_xpath_parse_NodeTest_QName(xpath);
+}
+
+
+/*
+ Scan an XPath expression
+
+ SYNOPSYS
+ Scan xpath expression.
+ The expression is returned in xpath->expr.
+
+ RETURN
+ 1 - success
+ 0 - failure
+*/
+static int
+my_xpath_parse(MY_XPATH *xpath, const char *str, const char *strend)
+{
+ my_xpath_lex_init(&xpath->query, str, strend);
+ my_xpath_lex_init(&xpath->prevtok, str, strend);
+ my_xpath_lex_scan(xpath, &xpath->lasttok, str, strend);
+
+ xpath->rootelement= new Item_nodeset_func_rootelement(xpath->pxml);
+
+ return
+ my_xpath_parse_Expr(xpath) &&
+ my_xpath_parse_term(xpath, MY_XPATH_LEX_EOF);
+}
+
+
+void Item_xml_str_func::fix_length_and_dec()
+{
+ String *xp, tmp;
+ MY_XPATH xpath;
+ int rc;
+
+ nodeset_func= 0;
+
+ if (agg_arg_charsets(collation, args, arg_count, MY_COLL_CMP_CONV, 1))
+ return;
+
+ if (collation.collation->mbminlen > 1)
+ {
+ /* UCS2 is not supported */
+ my_printf_error(ER_UNKNOWN_ERROR,
+ "Character set '%s' is not supported by XPATH",
+ MYF(0), collation.collation->csname);
+ return;
+ }
+
+ if (!args[1]->const_item())
+ {
+ my_printf_error(ER_UNKNOWN_ERROR,
+ "Only constant XPATH queries are supported", MYF(0));
+ return;
+ }
+
+ if (!(xp= args[1]->val_str(&tmp)))
+ return;
+ my_xpath_init(&xpath);
+ xpath.cs= collation.collation;
+ xpath.debug= 0;
+ xpath.pxml= &pxml;
+ pxml.set_charset(collation.collation);
+
+ rc= my_xpath_parse(&xpath, xp->ptr(), xp->ptr() + xp->length());
+
+ if (!rc)
+ {
+ uint clen= xpath.query.end - xpath.lasttok.beg;
+ set_if_smaller(clen, 32);
+ my_printf_error(ER_UNKNOWN_ERROR, "XPATH syntax error: '%.*s'",
+ MYF(0), clen, xpath.lasttok.beg);
+ return;
+ }
+
+ nodeset_func= xpath.item;
+ if (nodeset_func)
+ nodeset_func->fix_fields(current_thd, &nodeset_func);
+ max_length= MAX_BLOB_WIDTH;
+}
+
+
+#define MAX_LEVEL 256
+typedef struct
+{
+ uint level;
+ String *pxml; // parsed XML
+ uint pos[MAX_LEVEL]; // Tag position stack
+ uint parent; // Offset of the parent of the current node
+} MY_XML_USER_DATA;
+
+
+static bool
+append_node(String *str, MY_XML_NODE *node)
+{
+ /*
+ If "str" doesn't have space for a new node,
+ it will allocate two times more space that it has had so far.
+ (2*len+512) is a heuristic value,
+ which gave the best performance during tests.
+ The ideas behind this formula are:
+ - It allows to have a very small number of reallocs:
+ about 10 reallocs on a 1Mb-long XML value.
+ - At the same time, it avoids excessive memory use.
+ */
+ if (str->reserve(sizeof(MY_XML_NODE), 2 * str->length() + 512))
+ return TRUE;
+ str->q_append((const char*) node, sizeof(MY_XML_NODE));
+ return FALSE;
+}
+
+
+/*
+ Process tag beginning
+
+ SYNOPSYS
+
+ A call-back function executed when XML parser
+ is entering a tag or an attribue.
+ Appends the new node into data->pxml.
+ Increments data->level.
+
+ RETURN
+ Currently only MY_XML_OK
+*/
+extern "C" int xml_enter(MY_XML_PARSER *st,const char *attr, size_t len);
+
+int xml_enter(MY_XML_PARSER *st,const char *attr, size_t len)
+{
+ MY_XML_USER_DATA *data= (MY_XML_USER_DATA*)st->user_data;
+ uint numnodes= data->pxml->length() / sizeof(MY_XML_NODE);
+ MY_XML_NODE node;
+
+ node.parent= data->parent; // Set parent for the new node to old parent
+ data->parent= numnodes; // Remember current node as new parent
+ data->pos[data->level]= numnodes;
+ node.level= data->level++;
+ node.type= st->current_node_type; // TAG or ATTR
+ node.beg= attr;
+ node.end= attr + len;
+ return append_node(data->pxml, &node) ? MY_XML_ERROR : MY_XML_OK;
+}
+
+
+/*
+ Process text node
+
+ SYNOPSYS
+
+ A call-back function executed when XML parser
+ is entering into a tag or an attribue textual value.
+ The value is appended into data->pxml.
+
+ RETURN
+ Currently only MY_XML_OK
+*/
+extern "C" int xml_value(MY_XML_PARSER *st,const char *attr, size_t len);
+
+int xml_value(MY_XML_PARSER *st,const char *attr, size_t len)
+{
+ MY_XML_USER_DATA *data= (MY_XML_USER_DATA*)st->user_data;
+ MY_XML_NODE node;
+
+ node.parent= data->parent; // Set parent for the new text node to old parent
+ node.level= data->level;
+ node.type= MY_XML_NODE_TEXT;
+ node.beg= attr;
+ node.end= attr + len;
+ return append_node(data->pxml, &node) ? MY_XML_ERROR : MY_XML_OK;
+}
+
+
+/*
+ Leave a tag or an attribute
+
+ SYNOPSYS
+
+ A call-back function executed when XML parser
+ is leaving a tag or an attribue.
+ Decrements data->level.
+
+ RETURN
+ Currently only MY_XML_OK
+*/
+extern "C" int xml_leave(MY_XML_PARSER *st,const char *attr, size_t len);
+
+int xml_leave(MY_XML_PARSER *st,const char *attr, size_t len)
+{
+ MY_XML_USER_DATA *data= (MY_XML_USER_DATA*)st->user_data;
+ DBUG_ASSERT(data->level > 0);
+ data->level--;
+
+ MY_XML_NODE *nodes= (MY_XML_NODE*) data->pxml->ptr();
+ data->parent= nodes[data->parent].parent;
+ nodes+= data->pos[data->level];
+ nodes->tagend= st->cur;
+
+ return MY_XML_OK;
+}
+
+
+/*
+ Parse raw XML
+
+ SYNOPSYS
+
+
+ RETURN
+ Currently pointer to parsed XML on success
+ 0 on parse error
+*/
+String *Item_xml_str_func::parse_xml(String *raw_xml, String *parsed_xml_buf)
+{
+ MY_XML_PARSER p;
+ MY_XML_USER_DATA user_data;
+ int rc;
+
+ parsed_xml_buf->length(0);
+
+ /* Prepare XML parser */
+ my_xml_parser_create(&p);
+ p.flags= MY_XML_FLAG_RELATIVE_NAMES | MY_XML_FLAG_SKIP_TEXT_NORMALIZATION;
+ user_data.level= 0;
+ user_data.pxml= parsed_xml_buf;
+ user_data.parent= 0;
+ my_xml_set_enter_handler(&p, xml_enter);
+ my_xml_set_value_handler(&p, xml_value);
+ my_xml_set_leave_handler(&p, xml_leave);
+ my_xml_set_user_data(&p, (void*) &user_data);
+
+ /* Add root node */
+ p.current_node_type= MY_XML_NODE_TAG;
+ xml_enter(&p, raw_xml->ptr(), 0);
+
+ /* Execute XML parser */
+ if ((rc= my_xml_parse(&p, raw_xml->ptr(), raw_xml->length())) != MY_XML_OK)
+ {
+ char buf[128];
+ my_snprintf(buf, sizeof(buf)-1, "parse error at line %d pos %lu: %s",
+ my_xml_error_lineno(&p) + 1,
+ (ulong) my_xml_error_pos(&p) + 1,
+ my_xml_error_string(&p));
+ push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ ER_WRONG_VALUE,
+ ER(ER_WRONG_VALUE), "XML", buf);
+ }
+ my_xml_parser_free(&p);
+
+ return rc == MY_XML_OK ? parsed_xml_buf : 0;
+}
+
+
+String *Item_func_xml_extractvalue::val_str(String *str)
+{
+ String *res;
+ if (!nodeset_func ||
+ !(res= args[0]->val_str(str)) ||
+ !parse_xml(res, &pxml))
+ {
+ null_value= 1;
+ return 0;
+ }
+ res= nodeset_func->val_str(&tmp_value);
+ return res;
+}
+
+
+String *Item_func_xml_update::val_str(String *str)
+{
+ String *res, *nodeset, *rep;
+
+ if (!nodeset_func ||
+ !(res= args[0]->val_str(str)) ||
+ !(rep= args[2]->val_str(&tmp_value3)) ||
+ !parse_xml(res, &pxml) ||
+ !(nodeset= nodeset_func->val_nodeset(&tmp_value2)))
+ {
+ null_value= 1;
+ return 0;
+ }
+
+ MY_XML_NODE *nodebeg= (MY_XML_NODE*) pxml.ptr();
+ MY_XPATH_FLT *fltbeg= (MY_XPATH_FLT*) nodeset->ptr();
+ MY_XPATH_FLT *fltend= (MY_XPATH_FLT*) (nodeset->ptr() + nodeset->length());
+
+ /* Allow replacing of one tag only */
+ if (fltend - fltbeg != 1)
+ {
+ /* TODO: perhaps add a warning that more than one tag selected */
+ return res;
+ }
+
+ nodebeg+= fltbeg->num;
+
+ if (!nodebeg->level)
+ {
+ /*
+ Root element, without NameTest:
+ UpdateXML(xml, '/', 'replacement');
+ Just return the replacement string.
+ */
+ return rep;
+ }
+
+ tmp_value.length(0);
+ tmp_value.set_charset(collation.collation);
+ uint offs= nodebeg->type == MY_XML_NODE_TAG ? 1 : 0;
+ tmp_value.append(res->ptr(), nodebeg->beg - res->ptr() - offs);
+ tmp_value.append(rep->ptr(), rep->length());
+ const char *end= nodebeg->tagend + offs;
+ tmp_value.append(end, res->ptr() + res->length() - end);
+ return &tmp_value;
+}
diff --git a/sql/item_xmlfunc.h b/sql/item_xmlfunc.h
new file mode 100644
index 00000000000..dadbb5ccf42
--- /dev/null
+++ b/sql/item_xmlfunc.h
@@ -0,0 +1,63 @@
+/* Copyright (C) 2000-2005 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+
+/* This file defines all XML functions */
+
+
+#ifdef __GNUC__
+#pragma interface /* gcc class implementation */
+#endif
+
+
+class Item_xml_str_func: public Item_str_func
+{
+protected:
+ String tmp_value, pxml;
+ Item *nodeset_func;
+public:
+ Item_xml_str_func(Item *a, Item *b):
+ Item_str_func(a,b)
+ {
+ maybe_null= TRUE;
+ }
+ Item_xml_str_func(Item *a, Item *b, Item *c):
+ Item_str_func(a,b,c)
+ {
+ maybe_null= TRUE;
+ }
+ void fix_length_and_dec();
+ String *parse_xml(String *raw_xml, String *parsed_xml_buf);
+};
+
+
+class Item_func_xml_extractvalue: public Item_xml_str_func
+{
+public:
+ Item_func_xml_extractvalue(Item *a,Item *b) :Item_xml_str_func(a,b) {}
+ const char *func_name() const { return "extractvalue"; }
+ String *val_str(String *);
+};
+
+
+class Item_func_xml_update: public Item_xml_str_func
+{
+ String tmp_value2, tmp_value3;
+public:
+ Item_func_xml_update(Item *a,Item *b,Item *c) :Item_xml_str_func(a,b,c) {}
+ const char *func_name() const { return "updatexml"; }
+ String *val_str(String *);
+};
+
diff --git a/sql/key.cc b/sql/key.cc
index 1c044f3dc7d..5b2ae8029dd 100644
--- a/sql/key.cc
+++ b/sql/key.cc
@@ -17,41 +17,58 @@
/* Functions to handle keys and fields in forms */
#include "mysql_priv.h"
-#include "sql_trigger.h"
-
- /*
- ** Search after with key field is. If no key starts with field test
- ** if field is part of some key.
- **
- ** returns number of key. keylength is set to length of key before
- ** (not including) field
- ** Used when calculating key for NEXT_NUMBER
- */
-
-int find_ref_key(TABLE *table,Field *field, uint *key_length)
+
+/*
+ Search after a key that starts with 'field'
+
+ SYNOPSIS
+ find_ref_key()
+ key First key to check
+ key_count How many keys to check
+ record Start of record
+ field Field to search after
+ key_length On partial match, contains length of fields before
+ field
+ keypart key part # of a field
+
+ NOTES
+ Used when calculating key for NEXT_NUMBER
+
+ IMPLEMENTATION
+ If no key starts with field test if field is part of some key. If we find
+ one, then return first key and set key_length to the number of bytes
+ preceding 'field'.
+
+ RETURN
+ -1 field is not part of the key
+ # Key part for key matching key.
+ key_length is set to length of key before (not including) field
+*/
+
+int find_ref_key(KEY *key, uint key_count, uchar *record, Field *field,
+ uint *key_length, uint *keypart)
{
reg2 int i;
reg3 KEY *key_info;
uint fieldpos;
- fieldpos= field->offset();
-
- /* Test if some key starts as fieldpos */
+ fieldpos= field->offset(record);
- for (i= 0, key_info= table->key_info ;
- i < (int) table->s->keys ;
+ /* Test if some key starts as fieldpos */
+ for (i= 0, key_info= key ;
+ i < (int) key_count ;
i++, key_info++)
{
if (key_info->key_part[0].offset == fieldpos)
- { /* Found key. Calc keylength */
- *key_length=0;
- return(i); /* Use this key */
+ { /* Found key. Calc keylength */
+ *key_length= *keypart= 0;
+ return i; /* Use this key */
}
}
- /* Test if some key contains fieldpos */
- for (i= 0, key_info= table->key_info ;
- i < (int) table->s->keys ;
+ /* Test if some key contains fieldpos */
+ for (i= 0, key_info= key;
+ i < (int) key_count ;
i++, key_info++)
{
uint j;
@@ -62,36 +79,34 @@ int find_ref_key(TABLE *table,Field *field, uint *key_length)
j++, key_part++)
{
if (key_part->offset == fieldpos)
- return(i); /* Use this key */
- *key_length+=key_part->store_length;
+ {
+ *keypart= j;
+ return i; /* Use this key */
+ }
+ *key_length+= key_part->store_length;
}
}
return(-1); /* No key is ok */
}
-/*
+/**
Copy part of a record that forms a key or key prefix to a buffer.
- SYNOPSIS
- key_copy()
- to_key buffer that will be used as a key
- from_record full record to be copied from
- key_info descriptor of the index
- key_length specifies length of all keyparts that will be copied
-
- DESCRIPTION
The function takes a complete table record (as e.g. retrieved by
handler::index_read()), and a description of an index on the same table,
and extracts the first key_length bytes of the record which are part of a
key into to_key. If length == 0 then copy all bytes from the record that
form a key.
- RETURN
- None
+ @param to_key buffer that will be used as a key
+ @param from_record full record to be copied from
+ @param key_info descriptor of the index
+ @param key_length specifies length of all keyparts that will be copied
*/
-void key_copy(byte *to_key, byte *from_record, KEY *key_info, uint key_length)
+void key_copy(uchar *to_key, uchar *from_record, KEY *key_info,
+ uint key_length)
{
uint length;
KEY_PART_INFO *key_part;
@@ -111,7 +126,7 @@ void key_copy(byte *to_key, byte *from_record, KEY *key_info, uint key_length)
{
key_length-= HA_KEY_BLOB_LENGTH;
length= min(key_length, key_part->length);
- key_part->field->get_key_image((char*) to_key, length, Field::itRAW);
+ key_part->field->get_key_image(to_key, length, Field::itRAW);
to_key+= HA_KEY_BLOB_LENGTH;
}
else
@@ -119,7 +134,7 @@ void key_copy(byte *to_key, byte *from_record, KEY *key_info, uint key_length)
length= min(key_length, key_part->length);
Field *field= key_part->field;
CHARSET_INFO *cs= field->charset();
- uint bytes= field->get_key_image((char*) to_key, length, Field::itRAW);
+ uint bytes= field->get_key_image(to_key, length, Field::itRAW);
if (bytes < length)
cs->cset->fill(cs, (char*) to_key + bytes, length - bytes, ' ');
}
@@ -129,25 +144,19 @@ void key_copy(byte *to_key, byte *from_record, KEY *key_info, uint key_length)
}
-/*
+/**
Restore a key from some buffer to record.
- SYNOPSIS
- key_restore()
- to_record record buffer where the key will be restored to
- from_key buffer that contains a key
- key_info descriptor of the index
- key_length specifies length of all keyparts that will be restored
-
- DESCRIPTION
This function converts a key into record format. It can be used in cases
when we want to return a key as a result row.
- RETURN
- None
+ @param to_record record buffer where the key will be restored to
+ @param from_key buffer that contains a key
+ @param key_info descriptor of the index
+ @param key_length specifies length of all keyparts that will be restored
*/
-void key_restore(byte *to_record, byte *from_key, KEY *key_info,
+void key_restore(uchar *to_record, uchar *from_key, KEY *key_info,
uint key_length)
{
uint length;
@@ -159,6 +168,7 @@ void key_restore(byte *to_record, byte *from_key, KEY *key_info,
}
for (key_part= key_info->key_part ; (int) key_length > 0 ; key_part++)
{
+ uchar used_uneven_bits= 0;
if (key_part->null_bit)
{
if (*from_key++)
@@ -177,28 +187,47 @@ void key_restore(byte *to_record, byte *from_key, KEY *key_info,
set_rec_bits(bits, to_record + key_part->null_offset +
(key_part->null_bit == 128),
field->bit_ofs, field->bit_len);
+ /* we have now used the byte with 'uneven' bits */
+ used_uneven_bits= 1;
}
}
if (key_part->key_part_flag & HA_BLOB_PART)
{
+ /*
+ This in fact never happens, as we have only partial BLOB
+ keys yet anyway, so it's difficult to find any sence to
+ restore the part of a record.
+ Maybe this branch is to be removed, but now we
+ have to ignore GCov compaining.
+ */
uint blob_length= uint2korr(from_key);
+ Field_blob *field= (Field_blob*) key_part->field;
from_key+= HA_KEY_BLOB_LENGTH;
key_length-= HA_KEY_BLOB_LENGTH;
- ((Field_blob*) key_part->field)->set_ptr((ulong) blob_length,
- (char*) from_key);
+ field->set_ptr_offset(to_record - field->table->record[0],
+ (ulong) blob_length, from_key);
length= key_part->length;
}
else if (key_part->key_part_flag & HA_VAR_LENGTH_PART)
{
+ Field *field= key_part->field;
+ my_bitmap_map *old_map;
+ my_ptrdiff_t ptrdiff= to_record - field->table->record[0];
+ field->move_field_offset(ptrdiff);
key_length-= HA_KEY_BLOB_LENGTH;
length= min(key_length, key_part->length);
- key_part->field->set_key_image((char *) from_key, length);
+ old_map= dbug_tmp_use_all_columns(field->table, field->table->write_set);
+ field->set_key_image(from_key, length);
+ dbug_tmp_restore_column_map(field->table->write_set, old_map);
from_key+= HA_KEY_BLOB_LENGTH;
+ field->move_field_offset(-ptrdiff);
}
else
{
length= min(key_length, key_part->length);
- memcpy(to_record + key_part->offset, from_key, (size_t) length);
+ /* skip the byte with 'uneven' bits, if used */
+ memcpy(to_record + key_part->offset, from_key + used_uneven_bits
+ , (size_t) length - used_uneven_bits);
}
from_key+= length;
key_length-= length;
@@ -206,32 +235,31 @@ void key_restore(byte *to_record, byte *from_key, KEY *key_info,
}
-/*
- Compare if a key has changed
+/**
+ Compare if a key has changed.
- SYNOPSIS
- key_cmp_if_same()
- table TABLE
- key key to compare to row
- idx Index used
- key_length Length of key
+ @param table TABLE
+ @param key key to compare to row
+ @param idx Index used
+ @param key_length Length of key
- NOTES
+ @note
In theory we could just call field->cmp() for all field types,
but as we are only interested if a key has changed (not if the key is
larger or smaller than the previous value) we can do things a bit
faster by using memcmp() instead.
- RETURN
+ @retval
0 If key is equal
+ @retval
1 Key has changed
*/
-bool key_cmp_if_same(TABLE *table,const byte *key,uint idx,uint key_length)
+bool key_cmp_if_same(TABLE *table,const uchar *key,uint idx,uint key_length)
{
uint store_length;
KEY_PART_INFO *key_part;
- const byte *key_end= key + key_length;;
+ const uchar *key_end= key + key_length;;
for (key_part=table->key_info[idx].key_part;
key < key_end ;
@@ -263,7 +291,7 @@ bool key_cmp_if_same(TABLE *table,const byte *key,uint idx,uint key_length)
{
CHARSET_INFO *cs= key_part->field->charset();
uint char_length= key_part->length / cs->mbmaxlen;
- const byte *pos= table->record[0] + key_part->offset;
+ const uchar *pos= table->record[0] + key_part->offset;
if (length > char_length)
{
char_length= my_charpos(cs, pos, pos + length, char_length);
@@ -281,14 +309,26 @@ bool key_cmp_if_same(TABLE *table,const byte *key,uint idx,uint key_length)
return 0;
}
- /* unpack key-fields from record to some buffer */
- /* This is used to get a good error message */
+/*
+ unpack key-fields from record to some buffer.
+
+ This is used mainly to get a good error message. We temporary
+ change the column bitmap so that all columns are readable.
+
+ @param
+ to Store value here in an easy to read form
+ @param
+ table Table to use
+ @param
+ idx Key number
+*/
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");
to->length(0);
@@ -317,85 +357,66 @@ void key_unpack(String *to,TABLE *table,uint idx)
else
to->append(STRING_WITH_LEN("???"));
}
+ dbug_tmp_restore_column_map(table->read_set, old_map);
DBUG_VOID_RETURN;
}
/*
- Check if key uses field that is listed in passed field list or is
- automatically updated (like a timestamp) or can be updated by before
- update trigger defined on the table.
+ Check if key uses field that is marked in passed field bitmap.
SYNOPSIS
is_key_used()
table TABLE object with which keys and fields are associated.
idx Key to be checked.
- fields List of fields to be checked.
+ fields Bitmap of fields to be checked.
+
+ NOTE
+ This function uses TABLE::tmp_set bitmap so the caller should care
+ about saving/restoring its state if it also uses this bitmap.
RETURN VALUE
- TRUE Key uses field which meets one the above conditions
+ TRUE Key uses field from bitmap
FALSE Otherwise
*/
-bool is_key_used(TABLE *table, uint idx, List<Item> &fields)
+bool is_key_used(TABLE *table, uint idx, const MY_BITMAP *fields)
{
- Table_triggers_list *triggers= table->triggers;
- List_iterator_fast<Item> f(fields);
- KEY_PART_INFO *key_part,*key_part_end;
- for (key_part=table->key_info[idx].key_part,key_part_end=key_part+
- table->key_info[idx].key_parts ;
- key_part < key_part_end;
- key_part++)
- {
- Item_field *field;
-
- if (key_part->field == table->timestamp_field)
- return 1; // Can't be used for update
-
- f.rewind();
- while ((field=(Item_field*) f++))
- {
- if (key_part->field->eq(field->field))
- return 1;
- }
- if (triggers &&
- triggers->is_updated_in_before_update_triggers(key_part->field))
- return 1;
- }
+ bitmap_clear_all(&table->tmp_set);
+ table->mark_columns_used_by_index_no_reset(idx, &table->tmp_set);
+ if (bitmap_is_overlapping(&table->tmp_set, fields))
+ return 1;
/*
If table handler has primary key as part of the index, check that primary
key is not updated
*/
if (idx != table->s->primary_key && table->s->primary_key < MAX_KEY &&
- (table->file->table_flags() & HA_PRIMARY_KEY_IN_READ_INDEX))
+ (table->file->ha_table_flags() & HA_PRIMARY_KEY_IN_READ_INDEX))
return is_key_used(table, table->s->primary_key, fields);
return 0;
}
-/*
- Compare key in row to a given key
+/**
+ Compare key in row to a given key.
- SYNOPSIS
- key_cmp()
- key_part Key part handler
- key Key to compare to value in table->record[0]
- key_length length of 'key'
+ @param key_part Key part handler
+ @param key Key to compare to value in table->record[0]
+ @param key_length length of 'key'
- RETURN
+ @return
The return value is SIGN(key_in_row - range_key):
-
- 0 Key is equal to range or 'range' == 0 (no range)
- -1 Key is less than range
- 1 Key is larger than range
+ - 0 Key is equal to range or 'range' == 0 (no range)
+ - -1 Key is less than range
+ - 1 Key is larger than range
*/
-int key_cmp(KEY_PART_INFO *key_part, const byte *key, uint key_length)
+int key_cmp(KEY_PART_INFO *key_part, const uchar *key, uint key_length)
{
uint store_length;
- for (const byte *end=key + key_length;
+ for (const uchar *end=key + key_length;
key < end;
key+= store_length, key_part++)
{
@@ -418,10 +439,113 @@ int key_cmp(KEY_PART_INFO *key_part, const byte *key, uint key_length)
key++; // Skip null byte
store_length--;
}
- if ((cmp=key_part->field->key_cmp((byte*) key, key_part->length)) < 0)
+ if ((cmp=key_part->field->key_cmp(key, key_part->length)) < 0)
return -1;
if (cmp > 0)
return 1;
}
return 0; // Keys are equal
}
+
+
+/**
+ Compare two records in index order.
+
+ This method is set-up such that it can be called directly from the
+ priority queue and it is attempted to be optimised as much as possible
+ since this will be called O(N * log N) times while performing a merge
+ sort in various places in the code.
+
+ We retrieve the pointer to table->record[0] using the fact that key_parts
+ have an offset making it possible to calculate the start of the record.
+ We need to get the diff to the compared record since none of the records
+ being compared are stored in table->record[0].
+
+ We first check for NULL values, if there are no NULL values we use
+ a compare method that gets two field pointers and a max length
+ and return the result of the comparison.
+
+ key is a null terminated array, since in some cases (clustered
+ primary key) it must compare more than one index.
+
+ @param key Null terminated array of index information
+ @param first_rec Pointer to record compare with
+ @param second_rec Pointer to record compare against first_rec
+
+ @return Return value is SIGN(first_rec - second_rec)
+ @retval 0 Keys are equal
+ @retval -1 second_rec is greater than first_rec
+ @retval +1 first_rec is greater than second_rec
+*/
+
+int key_rec_cmp(void *key_p, uchar *first_rec, uchar *second_rec)
+{
+ KEY **key= (KEY**) key_p;
+ KEY *key_info= *(key++); // Start with first key
+ uint key_parts, key_part_num;
+ KEY_PART_INFO *key_part= key_info->key_part;
+ uchar *rec0= key_part->field->ptr - key_part->offset;
+ my_ptrdiff_t first_diff= first_rec - rec0, sec_diff= second_rec - rec0;
+ int result= 0;
+ Field *field;
+ DBUG_ENTER("key_rec_cmp");
+
+ /* loop over all given keys */
+ do
+ {
+ key_parts= key_info->key_parts;
+ key_part= key_info->key_part;
+ key_part_num= 0;
+
+ /* loop over every key part */
+ do
+ {
+ field= key_part->field;
+
+ if (key_part->null_bit)
+ {
+ /* The key_part can contain NULL values */
+ bool first_is_null= field->is_null_in_record_with_offset(first_diff);
+ bool sec_is_null= field->is_null_in_record_with_offset(sec_diff);
+ /*
+ NULL is smaller then everything so if first is NULL and the other
+ not then we know that we should return -1 and for the opposite
+ we should return +1. If both are NULL then we call it equality
+ although it is a strange form of equality, we have equally little
+ information of the real value.
+ */
+ if (!first_is_null)
+ {
+ if (!sec_is_null)
+ ; /* Fall through, no NULL fields */
+ else
+ {
+ DBUG_RETURN(+1);
+ }
+ }
+ else if (!sec_is_null)
+ {
+ DBUG_RETURN(-1);
+ }
+ else
+ goto next_loop; /* Both were NULL */
+ }
+ /*
+ No null values in the fields
+ We use the virtual method cmp_max with a max length parameter.
+ For most field types this translates into a cmp without
+ max length. The exceptions are the BLOB and VARCHAR field types
+ that take the max length into account.
+ */
+ if ((result= field->cmp_max(field->ptr+first_diff, field->ptr+sec_diff,
+ key_part->length)))
+ DBUG_RETURN(result);
+next_loop:
+ key_part++;
+ key_part_num++;
+ } while (key_part_num < key_parts); /* this key is done */
+
+ key_info= *(key++);
+ } while (key_info); /* no more keys to test */
+ DBUG_RETURN(0);
+}
diff --git a/sql/lex.h b/sql/lex.h
index 352d80da5c6..acb81dcf717 100644
--- a/sql/lex.h
+++ b/sql/lex.h
@@ -29,21 +29,16 @@ SYM_GROUP sym_group_rtree= {"RTree keys", "HAVE_RTREE_KEYS"};
#define SYM_OR_NULL(A) A
#endif
-#define SYM(A) SYM_OR_NULL(A),0,0,&sym_group_common
-#define F_SYM(A) SYM_OR_NULL(A)
-
-#define CREATE_FUNC(A) (void (*)())(SYM_OR_NULL(A)), &sym_group_common
-
-#ifdef HAVE_SPATIAL
-#define CREATE_FUNC_GEOM(A) (void (*)())(SYM_OR_NULL(A)), &sym_group_geom
-#else
-#define CREATE_FUNC_GEOM(A) 0, &sym_group_geom
-#endif
+#define SYM(A) SYM_OR_NULL(A),0,&sym_group_common
/*
Symbols are broken into separated arrays to allow field names with
same name as functions.
These are kept sorted for human lookup (the symbols are hashed).
+
+ NOTE! The symbol tables should be the same regardless of what features
+ are compiled into the server. Don't add ifdef'ed symbols to the
+ lists
*/
static SYMBOL symbols[] = {
@@ -58,6 +53,7 @@ static SYMBOL symbols[] = {
{ "<<", SYM(SHIFT_LEFT)},
{ ">>", SYM(SHIFT_RIGHT)},
{ "<=>", SYM(EQUAL_SYM)},
+ { "ACCESSIBLE", SYM(ACCESSIBLE_SYM)},
{ "ACTION", SYM(ACTION)},
{ "ADD", SYM(ADD)},
{ "AFTER", SYM(AFTER_SYM)},
@@ -73,20 +69,22 @@ static SYMBOL symbols[] = {
{ "ASC", SYM(ASC)},
{ "ASCII", SYM(ASCII_SYM)},
{ "ASENSITIVE", SYM(ASENSITIVE_SYM)},
+ { "AT", SYM(AT_SYM)},
+ { "AUTHORS", SYM(AUTHORS_SYM)},
{ "AUTO_INCREMENT", SYM(AUTO_INC)},
+ { "AUTOEXTEND_SIZE", SYM(AUTOEXTEND_SIZE_SYM)},
{ "AVG", SYM(AVG_SYM)},
{ "AVG_ROW_LENGTH", SYM(AVG_ROW_LENGTH)},
{ "BACKUP", SYM(BACKUP_SYM)},
- { "BDB", SYM(BERKELEY_DB_SYM)},
{ "BEFORE", SYM(BEFORE_SYM)},
{ "BEGIN", SYM(BEGIN_SYM)},
- { "BERKELEYDB", SYM(BERKELEY_DB_SYM)},
{ "BETWEEN", SYM(BETWEEN_SYM)},
{ "BIGINT", SYM(BIGINT)},
{ "BINARY", SYM(BINARY)},
{ "BINLOG", SYM(BINLOG_SYM)},
{ "BIT", SYM(BIT_SYM)},
{ "BLOB", SYM(BLOB_SYM)},
+ { "BLOCK", SYM(BLOCK_SYM)},
{ "BOOL", SYM(BOOL_SYM)},
{ "BOOLEAN", SYM(BOOLEAN_SYM)},
{ "BOTH", SYM(BOTH)},
@@ -109,6 +107,7 @@ static SYMBOL symbols[] = {
{ "CIPHER", SYM(CIPHER_SYM)},
{ "CLIENT", SYM(CLIENT_SYM)},
{ "CLOSE", SYM(CLOSE_SYM)},
+ { "COALESCE", SYM(COALESCE)},
{ "CODE", SYM(CODE_SYM)},
{ "COLLATE", SYM(COLLATE_SYM)},
{ "COLLATION", SYM(COLLATION_SYM)},
@@ -118,6 +117,7 @@ static SYMBOL symbols[] = {
{ "COMMIT", SYM(COMMIT_SYM)},
{ "COMMITTED", SYM(COMMITTED_SYM)},
{ "COMPACT", SYM(COMPACT_SYM)},
+ { "COMPLETION", SYM(COMPLETION_SYM)},
{ "COMPRESSED", SYM(COMPRESSED_SYM)},
{ "CONCURRENT", SYM(CONCURRENT)},
{ "CONDITION", SYM(CONDITION_SYM)},
@@ -125,8 +125,11 @@ static SYMBOL symbols[] = {
{ "CONSISTENT", SYM(CONSISTENT_SYM)},
{ "CONSTRAINT", SYM(CONSTRAINT)},
{ "CONTAINS", SYM(CONTAINS_SYM)},
+ { "CONTEXT", SYM(CONTEXT_SYM)},
{ "CONTINUE", SYM(CONTINUE_SYM)},
+ { "CONTRIBUTORS", SYM(CONTRIBUTORS_SYM)},
{ "CONVERT", SYM(CONVERT_SYM)},
+ { "CPU", SYM(CPU_SYM)},
{ "CREATE", SYM(CREATE)},
{ "CROSS", SYM(CROSS)},
{ "CUBE", SYM(CUBE_SYM)},
@@ -138,6 +141,7 @@ static SYMBOL symbols[] = {
{ "DATA", SYM(DATA_SYM)},
{ "DATABASE", SYM(DATABASE)},
{ "DATABASES", SYM(DATABASES)},
+ { "DATAFILE", SYM(DATAFILE_SYM)},
{ "DATE", SYM(DATE_SYM)},
{ "DATETIME", SYM(DATETIME)},
{ "DAY", SYM(DAY_SYM)},
@@ -161,6 +165,7 @@ static SYMBOL symbols[] = {
{ "DIRECTORY", SYM(DIRECTORY_SYM)},
{ "DISABLE", SYM(DISABLE_SYM)},
{ "DISCARD", SYM(DISCARD)},
+ { "DISK", SYM(DISK_SYM)},
{ "DISTINCT", SYM(DISTINCT)},
{ "DISTINCTROW", SYM(DISTINCT)}, /* Access likes this */
{ "DIV", SYM(DIV_SYM)},
@@ -177,21 +182,26 @@ static SYMBOL symbols[] = {
{ "ENABLE", SYM(ENABLE_SYM)},
{ "ENCLOSED", SYM(ENCLOSED)},
{ "END", SYM(END)},
+ { "ENDS", SYM(ENDS_SYM)},
{ "ENGINE", SYM(ENGINE_SYM)},
{ "ENGINES", SYM(ENGINES_SYM)},
{ "ENUM", SYM(ENUM)},
{ "ERRORS", SYM(ERRORS)},
{ "ESCAPE", SYM(ESCAPE_SYM)},
{ "ESCAPED", SYM(ESCAPED)},
+ { "EVENT", SYM(EVENT_SYM)},
{ "EVENTS", SYM(EVENTS_SYM)},
+ { "EVERY", SYM(EVERY_SYM)},
{ "EXECUTE", SYM(EXECUTE_SYM)},
{ "EXISTS", SYM(EXISTS)},
{ "EXIT", SYM(EXIT_SYM)},
{ "EXPANSION", SYM(EXPANSION_SYM)},
{ "EXPLAIN", SYM(DESCRIBE)},
{ "EXTENDED", SYM(EXTENDED_SYM)},
+ { "EXTENT_SIZE", SYM(EXTENT_SIZE_SYM)},
{ "FALSE", SYM(FALSE_SYM)},
{ "FAST", SYM(FAST_SYM)},
+ { "FAULTS", SYM(FAULTS_SYM)},
{ "FETCH", SYM(FETCH_SYM)},
{ "FIELDS", SYM(COLUMNS)},
{ "FILE", SYM(FILE_SYM)},
@@ -216,12 +226,13 @@ static SYMBOL symbols[] = {
{ "GLOBAL", SYM(GLOBAL_SYM)},
{ "GRANT", SYM(GRANT)},
{ "GRANTS", SYM(GRANTS)},
- { "GROUP", SYM(GROUP)},
+ { "GROUP", SYM(GROUP_SYM)},
{ "HANDLER", SYM(HANDLER_SYM)},
{ "HASH", SYM(HASH_SYM)},
{ "HAVING", SYM(HAVING)},
{ "HELP", SYM(HELP_SYM)},
{ "HIGH_PRIORITY", SYM(HIGH_PRIORITY)},
+ { "HOST", SYM(HOST_SYM)},
{ "HOSTS", SYM(HOSTS_SYM)},
{ "HOUR", SYM(HOUR_SYM)},
{ "HOUR_MICROSECOND", SYM(HOUR_MICROSECOND_SYM)},
@@ -235,6 +246,7 @@ static SYMBOL symbols[] = {
{ "INDEX", SYM(INDEX_SYM)},
{ "INDEXES", SYM(INDEXES)},
{ "INFILE", SYM(INFILE)},
+ { "INITIAL_SIZE", SYM(INITIAL_SIZE_SYM)},
{ "INNER", SYM(INNER_SYM)},
{ "INNOBASE", SYM(INNOBASE_SYM)},
{ "INNODB", SYM(INNOBASE_SYM)},
@@ -242,6 +254,7 @@ static SYMBOL symbols[] = {
{ "INSENSITIVE", SYM(INSENSITIVE_SYM)},
{ "INSERT", SYM(INSERT)},
{ "INSERT_METHOD", SYM(INSERT_METHOD)},
+ { "INSTALL", SYM(INSTALL_SYM)},
{ "INT", SYM(INT_SYM)},
{ "INT1", SYM(TINYINT)},
{ "INT2", SYM(SMALLINT)},
@@ -251,7 +264,9 @@ static SYMBOL symbols[] = {
{ "INTEGER", SYM(INT_SYM)},
{ "INTERVAL", SYM(INTERVAL_SYM)},
{ "INTO", SYM(INTO)},
+ { "IO", SYM(IO_SYM)},
{ "IO_THREAD", SYM(RELAY_THREAD)},
+ { "IPC", SYM(IPC_SYM)},
{ "IS", SYM(IS)},
{ "ISOLATION", SYM(ISOLATION)},
{ "ISSUER", SYM(ISSUER_SYM)},
@@ -260,6 +275,7 @@ static SYMBOL symbols[] = {
{ "JOIN", SYM(JOIN_SYM)},
{ "KEY", SYM(KEY_SYM)},
{ "KEYS", SYM(KEYS)},
+ { "KEY_BLOCK_SIZE", SYM(KEY_BLOCK_SIZE)},
{ "KILL", SYM(KILL_SYM)},
{ "LANGUAGE", SYM(LANGUAGE_SYM)},
{ "LAST", SYM(LAST_SYM)},
@@ -267,17 +283,21 @@ static SYMBOL symbols[] = {
{ "LEAVE", SYM(LEAVE_SYM)},
{ "LEAVES", SYM(LEAVES)},
{ "LEFT", SYM(LEFT)},
+ { "LESS", SYM(LESS_SYM)},
{ "LEVEL", SYM(LEVEL_SYM)},
{ "LIKE", SYM(LIKE)},
{ "LIMIT", SYM(LIMIT)},
+ { "LINEAR", SYM(LINEAR_SYM)},
{ "LINES", SYM(LINES)},
{ "LINESTRING", SYM(LINESTRING)},
+ { "LIST", SYM(LIST_SYM)},
{ "LOAD", SYM(LOAD)},
{ "LOCAL", SYM(LOCAL_SYM)},
{ "LOCALTIME", SYM(NOW_SYM)},
{ "LOCALTIMESTAMP", SYM(NOW_SYM)},
{ "LOCK", SYM(LOCK_SYM)},
{ "LOCKS", SYM(LOCKS_SYM)},
+ { "LOGFILE", SYM(LOGFILE_SYM)},
{ "LOGS", SYM(LOGS_SYM)},
{ "LONG", SYM(LONG_SYM)},
{ "LONGBLOB", SYM(LONGBLOB)},
@@ -298,17 +318,21 @@ static SYMBOL symbols[] = {
{ "MASTER_SSL_CERT", SYM(MASTER_SSL_CERT_SYM)},
{ "MASTER_SSL_CIPHER",SYM(MASTER_SSL_CIPHER_SYM)},
{ "MASTER_SSL_KEY", SYM(MASTER_SSL_KEY_SYM)},
+ { "MASTER_SSL_VERIFY_SERVER_CERT", SYM(MASTER_SSL_VERIFY_SERVER_CERT_SYM)},
{ "MASTER_USER", SYM(MASTER_USER_SYM)},
{ "MATCH", SYM(MATCH)},
{ "MAX_CONNECTIONS_PER_HOUR", SYM(MAX_CONNECTIONS_PER_HOUR)},
{ "MAX_QUERIES_PER_HOUR", SYM(MAX_QUERIES_PER_HOUR)},
{ "MAX_ROWS", SYM(MAX_ROWS)},
+ { "MAX_SIZE", SYM(MAX_SIZE_SYM)},
{ "MAX_UPDATES_PER_HOUR", SYM(MAX_UPDATES_PER_HOUR)},
{ "MAX_USER_CONNECTIONS", SYM(MAX_USER_CONNECTIONS_SYM)},
+ { "MAXVALUE", SYM(MAX_VALUE_SYM)},
{ "MEDIUM", SYM(MEDIUM_SYM)},
{ "MEDIUMBLOB", SYM(MEDIUMBLOB)},
{ "MEDIUMINT", SYM(MEDIUMINT)},
{ "MEDIUMTEXT", SYM(MEDIUMTEXT)},
+ { "MEMORY", SYM(MEMORY_SYM)},
{ "MERGE", SYM(MERGE_SYM)},
{ "MICROSECOND", SYM(MICROSECOND_SYM)},
{ "MIDDLEINT", SYM(MEDIUMINT)}, /* For powerbuilder */
@@ -336,6 +360,8 @@ static SYMBOL symbols[] = {
{ "NEW", SYM(NEW_SYM)},
{ "NEXT", SYM(NEXT_SYM)},
{ "NO", SYM(NO_SYM)},
+ { "NO_WAIT", SYM(NO_WAIT_SYM)},
+ { "NODEGROUP", SYM(NODEGROUP_SYM)},
{ "NONE", SYM(NONE_SYM)},
{ "NOT", SYM(NOT_SYM)},
{ "NO_WRITE_TO_BINLOG", SYM(NO_WRITE_TO_BINLOG)},
@@ -349,6 +375,7 @@ static SYMBOL symbols[] = {
{ "ONE_SHOT", SYM(ONE_SHOT_SYM)},
{ "OPEN", SYM(OPEN_SYM)},
{ "OPTIMIZE", SYM(OPTIMIZE)},
+ { "OPTIONS", SYM(OPTIONS_SYM)},
{ "OPTION", SYM(OPTION)},
{ "OPTIONALLY", SYM(OPTIONALLY)},
{ "OR", SYM(OR_SYM)},
@@ -356,32 +383,47 @@ static SYMBOL symbols[] = {
{ "OUT", SYM(OUT_SYM)},
{ "OUTER", SYM(OUTER)},
{ "OUTFILE", SYM(OUTFILE)},
+ { "OWNER", SYM(OWNER_SYM)},
{ "PACK_KEYS", SYM(PACK_KEYS_SYM)},
+ { "PARSER", SYM(PARSER_SYM)},
+ { "PAGE", SYM(PAGE_SYM)},
+ { "PAGE_CHECKSUM", SYM(PAGE_CHECKSUM_SYM)},
{ "PARTIAL", SYM(PARTIAL)},
+ { "PARTITION", SYM(PARTITION_SYM)},
+ { "PARTITIONING", SYM(PARTITIONING_SYM)},
+ { "PARTITIONS", SYM(PARTITIONS_SYM)},
{ "PASSWORD", SYM(PASSWORD)},
{ "PHASE", SYM(PHASE_SYM)},
+ { "PLUGIN", SYM(PLUGIN_SYM)},
+ { "PLUGINS", SYM(PLUGINS_SYM)},
{ "POINT", SYM(POINT_SYM)},
{ "POLYGON", SYM(POLYGON)},
+ { "PORT", SYM(PORT_SYM)},
{ "PRECISION", SYM(PRECISION)},
{ "PREPARE", SYM(PREPARE_SYM)},
+ { "PRESERVE", SYM(PRESERVE_SYM)},
{ "PREV", SYM(PREV_SYM)},
{ "PRIMARY", SYM(PRIMARY_SYM)},
{ "PRIVILEGES", SYM(PRIVILEGES)},
{ "PROCEDURE", SYM(PROCEDURE)},
{ "PROCESS" , SYM(PROCESS)},
{ "PROCESSLIST", SYM(PROCESSLIST_SYM)},
+ { "PROFILE", SYM(PROFILE_SYM)},
+ { "PROFILES", SYM(PROFILES_SYM)},
{ "PURGE", SYM(PURGE)},
{ "QUARTER", SYM(QUARTER_SYM)},
{ "QUERY", SYM(QUERY_SYM)},
{ "QUICK", SYM(QUICK)},
- { "RAID0", SYM(RAID_0_SYM)},
- { "RAID_CHUNKS", SYM(RAID_CHUNKS)},
- { "RAID_CHUNKSIZE", SYM(RAID_CHUNKSIZE)},
- { "RAID_TYPE", SYM(RAID_TYPE)},
+ { "RANGE", SYM(RANGE_SYM)},
{ "READ", SYM(READ_SYM)},
+ { "READ_ONLY", SYM(READ_ONLY_SYM)},
+ { "READ_WRITE", SYM(READ_WRITE_SYM)},
{ "READS", SYM(READS_SYM)},
{ "REAL", SYM(REAL)},
+ { "REBUILD", SYM(REBUILD_SYM)},
{ "RECOVER", SYM(RECOVER_SYM)},
+ { "REDO_BUFFER_SIZE", SYM(REDO_BUFFER_SIZE_SYM)},
+ { "REDOFILE", SYM(REDOFILE_SYM)},
{ "REDUNDANT", SYM(REDUNDANT_SYM)},
{ "REFERENCES", SYM(REFERENCES)},
{ "REGEXP", SYM(REGEXP)},
@@ -390,7 +432,9 @@ static SYMBOL symbols[] = {
{ "RELAY_THREAD", SYM(RELAY_THREAD)},
{ "RELEASE", SYM(RELEASE_SYM)},
{ "RELOAD", SYM(RELOAD)},
+ { "REMOVE", SYM(REMOVE_SYM)},
{ "RENAME", SYM(RENAME)},
+ { "REORGANIZE", SYM(REORGANIZE_SYM)},
{ "REPAIR", SYM(REPAIR)},
{ "REPEATABLE", SYM(REPEATABLE_SYM)},
{ "REPLACE", SYM(REPLACE)},
@@ -414,6 +458,7 @@ static SYMBOL symbols[] = {
{ "ROW_FORMAT", SYM(ROW_FORMAT_SYM)},
{ "RTREE", SYM(RTREE_SYM)},
{ "SAVEPOINT", SYM(SAVEPOINT_SYM)},
+ { "SCHEDULE", SYM(SCHEDULE_SYM)},
{ "SCHEMA", SYM(DATABASE)},
{ "SCHEMAS", SYM(DATABASES)},
{ "SECOND", SYM(SECOND_SYM)},
@@ -425,6 +470,7 @@ static SYMBOL symbols[] = {
{ "SERIAL", SYM(SERIAL_SYM)},
{ "SERIALIZABLE", SYM(SERIALIZABLE_SYM)},
{ "SESSION", SYM(SESSION_SYM)},
+ { "SERVER", SYM(SERVER_SYM)},
{ "SET", SYM(SET)},
{ "SHARE", SYM(SHARE_SYM)},
{ "SHOW", SYM(SHOW)},
@@ -434,9 +480,11 @@ static SYMBOL symbols[] = {
{ "SLAVE", SYM(SLAVE)},
{ "SNAPSHOT", SYM(SNAPSHOT_SYM)},
{ "SMALLINT", SYM(SMALLINT)},
+ { "SOCKET", SYM(SOCKET_SYM)},
{ "SOME", SYM(ANY_SYM)},
- { "SONAME", SYM(UDF_SONAME_SYM)},
+ { "SONAME", SYM(SONAME_SYM)},
{ "SOUNDS", SYM(SOUNDS_SYM)},
+ { "SOURCE", SYM(SOURCE_SYM)},
{ "SPATIAL", SYM(SPATIAL_SYM)},
{ "SPECIFIC", SYM(SPECIFIC_SYM)},
{ "SQL", SYM(SQL_SYM)},
@@ -462,22 +510,28 @@ static SYMBOL symbols[] = {
{ "SSL", SYM(SSL_SYM)},
{ "START", SYM(START_SYM)},
{ "STARTING", SYM(STARTING)},
+ { "STARTS", SYM(STARTS_SYM)},
{ "STATUS", SYM(STATUS_SYM)},
{ "STOP", SYM(STOP_SYM)},
{ "STORAGE", SYM(STORAGE_SYM)},
{ "STRAIGHT_JOIN", SYM(STRAIGHT_JOIN)},
{ "STRING", SYM(STRING_SYM)},
- { "STRIPED", SYM(RAID_STRIPED_SYM)},
{ "SUBJECT", SYM(SUBJECT_SYM)},
+ { "SUBPARTITION", SYM(SUBPARTITION_SYM)},
+ { "SUBPARTITIONS", SYM(SUBPARTITIONS_SYM)},
{ "SUPER", SYM(SUPER_SYM)},
{ "SUSPEND", SYM(SUSPEND_SYM)},
+ { "SWAPS", SYM(SWAPS_SYM)},
+ { "SWITCHES", SYM(SWITCHES_SYM)},
{ "TABLE", SYM(TABLE_SYM)},
{ "TABLES", SYM(TABLES)},
- { "TABLESPACE", SYM(TABLESPACE)},
+ { "TABLESPACE", SYM(TABLESPACE)},
+ { "TABLE_CHECKSUM", SYM(TABLE_CHECKSUM_SYM)},
{ "TEMPORARY", SYM(TEMPORARY)},
{ "TEMPTABLE", SYM(TEMPTABLE_SYM)},
{ "TERMINATED", SYM(TERMINATED)},
{ "TEXT", SYM(TEXT_SYM)},
+ { "THAN", SYM(THAN_SYM)},
{ "THEN", SYM(THEN_SYM)},
{ "TIME", SYM(TIME_SYM)},
{ "TIMESTAMP", SYM(TIMESTAMP)},
@@ -489,6 +543,7 @@ static SYMBOL symbols[] = {
{ "TO", SYM(TO_SYM)},
{ "TRAILING", SYM(TRAILING)},
{ "TRANSACTION", SYM(TRANSACTION_SYM)},
+ { "TRANSACTIONAL", SYM(TRANSACTIONAL_SYM)},
{ "TRIGGER", SYM(TRIGGER_SYM)},
{ "TRIGGERS", SYM(TRIGGERS_SYM)},
{ "TRUE", SYM(TRUE_SYM)},
@@ -497,12 +552,15 @@ static SYMBOL symbols[] = {
{ "TYPES", SYM(TYPES_SYM)},
{ "UNCOMMITTED", SYM(UNCOMMITTED_SYM)},
{ "UNDEFINED", SYM(UNDEFINED_SYM)},
+ { "UNDO_BUFFER_SIZE", SYM(UNDO_BUFFER_SIZE_SYM)},
+ { "UNDOFILE", SYM(UNDOFILE_SYM)},
{ "UNDO", SYM(UNDO_SYM)},
{ "UNICODE", SYM(UNICODE_SYM)},
{ "UNION", SYM(UNION_SYM)},
{ "UNIQUE", SYM(UNIQUE_SYM)},
{ "UNKNOWN", SYM(UNKNOWN_SYM)},
{ "UNLOCK", SYM(UNLOCK_SYM)},
+ { "UNINSTALL", SYM(UNINSTALL_SYM)},
{ "UNSIGNED", SYM(UNSIGNED)},
{ "UNTIL", SYM(UNTIL_SYM)},
{ "UPDATE", SYM(UPDATE_SYM)},
@@ -523,6 +581,7 @@ static SYMBOL symbols[] = {
{ "VARCHARACTER", SYM(VARCHAR)},
{ "VARIABLES", SYM(VARIABLES)},
{ "VARYING", SYM(VARYING)},
+ { "WAIT", SYM(WAIT_SYM)},
{ "WARNINGS", SYM(WARNINGS)},
{ "WEEK", SYM(WEEK_SYM)},
{ "WHEN", SYM(WHEN_SYM)},
@@ -531,6 +590,7 @@ static SYMBOL symbols[] = {
{ "VIEW", SYM(VIEW_SYM)},
{ "WITH", SYM(WITH)},
{ "WORK", SYM(WORK_SYM)},
+ { "WRAPPER", SYM(WRAPPER_SYM)},
{ "WRITE", SYM(WRITE_SYM)},
{ "X509", SYM(X509_SYM)},
{ "XOR", SYM(XOR)},
@@ -543,234 +603,36 @@ static SYMBOL symbols[] = {
static SYMBOL sql_functions[] = {
- { "ABS", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_abs)},
- { "ACOS", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_acos)},
{ "ADDDATE", SYM(ADDDATE_SYM)},
- { "ADDTIME", F_SYM(FUNC_ARG2),0,CREATE_FUNC(create_func_addtime)},
- { "AES_ENCRYPT", F_SYM(FUNC_ARG2),0,CREATE_FUNC(create_func_aes_encrypt)},
- { "AES_DECRYPT", F_SYM(FUNC_ARG2),0,CREATE_FUNC(create_func_aes_decrypt)},
- { "AREA", F_SYM(FUNC_ARG1),0,CREATE_FUNC_GEOM(create_func_area)},
- { "ASIN", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_asin)},
- { "ASBINARY", F_SYM(FUNC_ARG1),0,CREATE_FUNC_GEOM(create_func_as_wkb)},
- { "ASTEXT", F_SYM(FUNC_ARG1),0,CREATE_FUNC_GEOM(create_func_as_wkt)},
- { "ASWKB", F_SYM(FUNC_ARG1),0,CREATE_FUNC_GEOM(create_func_as_wkb)},
- { "ASWKT", F_SYM(FUNC_ARG1),0,CREATE_FUNC_GEOM(create_func_as_wkt)},
- { "ATAN", SYM(ATAN)},
- { "ATAN2", SYM(ATAN)},
- { "BENCHMARK", SYM(BENCHMARK_SYM)},
- { "BIN", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_bin)},
- { "BIT_COUNT", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_bit_count)},
- { "BIT_OR", SYM(BIT_OR)},
{ "BIT_AND", SYM(BIT_AND)},
+ { "BIT_OR", SYM(BIT_OR)},
{ "BIT_XOR", SYM(BIT_XOR)},
{ "CAST", SYM(CAST_SYM)},
- { "CEIL", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_ceiling)},
- { "CEILING", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_ceiling)},
- { "BIT_LENGTH", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_bit_length)},
- { "CENTROID", F_SYM(FUNC_ARG1),0,CREATE_FUNC_GEOM(create_func_centroid)},
- { "CHAR_LENGTH", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_char_length)},
- { "CHARACTER_LENGTH", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_char_length)},
- { "COALESCE", SYM(COALESCE)},
- { "COERCIBILITY", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_coercibility)},
- { "COMPRESS", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_compress)},
- { "CONCAT", SYM(CONCAT)},
- { "CONCAT_WS", SYM(CONCAT_WS)},
- { "CONNECTION_ID", F_SYM(FUNC_ARG0),0,CREATE_FUNC(create_func_connection_id)},
- { "CONV", F_SYM(FUNC_ARG3),0,CREATE_FUNC(create_func_conv)},
- { "CONVERT_TZ", SYM(CONVERT_TZ_SYM)},
{ "COUNT", SYM(COUNT_SYM)},
- { "COS", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_cos)},
- { "COT", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_cot)},
- { "CRC32", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_crc32)},
- { "CROSSES", F_SYM(FUNC_ARG2),0,CREATE_FUNC_GEOM(create_func_crosses)},
{ "CURDATE", SYM(CURDATE)},
{ "CURTIME", SYM(CURTIME)},
{ "DATE_ADD", SYM(DATE_ADD_INTERVAL)},
- { "DATEDIFF", F_SYM(FUNC_ARG2),0,CREATE_FUNC(create_func_datediff)},
- { "DATE_FORMAT", F_SYM(FUNC_ARG2),0,CREATE_FUNC(create_func_date_format)},
{ "DATE_SUB", SYM(DATE_SUB_INTERVAL)},
- { "DAYNAME", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_dayname)},
- { "DAYOFMONTH", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_dayofmonth)},
- { "DAYOFWEEK", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_dayofweek)},
- { "DAYOFYEAR", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_dayofyear)},
- { "DECODE", SYM(DECODE_SYM)},
- { "DEGREES", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_degrees)},
- { "DES_ENCRYPT", SYM(DES_ENCRYPT_SYM)},
- { "DES_DECRYPT", SYM(DES_DECRYPT_SYM)},
- { "DIMENSION", F_SYM(FUNC_ARG1),0,CREATE_FUNC_GEOM(create_func_dimension)},
- { "DISJOINT", F_SYM(FUNC_ARG2),0,CREATE_FUNC_GEOM(create_func_disjoint)},
- { "ELT", SYM(ELT_FUNC)},
- { "ENCODE", SYM(ENCODE_SYM)},
- { "ENCRYPT", SYM(ENCRYPT)},
- { "ENDPOINT", F_SYM(FUNC_ARG1),0,CREATE_FUNC_GEOM(create_func_endpoint)},
- { "ENVELOPE", F_SYM(FUNC_ARG1),0,CREATE_FUNC_GEOM(create_func_envelope)},
- { "EQUALS", F_SYM(FUNC_ARG2),0,CREATE_FUNC_GEOM(create_func_equals)},
- { "EXTERIORRING", F_SYM(FUNC_ARG1),0,CREATE_FUNC_GEOM(create_func_exteriorring)},
{ "EXTRACT", SYM(EXTRACT_SYM)},
- { "EXP", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_exp)},
- { "EXPORT_SET", SYM(EXPORT_SET)},
- { "FIELD", SYM(FIELD_FUNC)}, /* For compability */
- { "FIND_IN_SET", F_SYM(FUNC_ARG2),0,CREATE_FUNC(create_func_find_in_set)},
- { "FLOOR", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_floor)},
- { "FORMAT", SYM(FORMAT_SYM)},
- { "FOUND_ROWS", F_SYM(FUNC_ARG0),0,CREATE_FUNC(create_func_found_rows)},
- { "FROM_DAYS", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_from_days)},
- { "FROM_UNIXTIME", SYM(FROM_UNIXTIME)},
- { "GET_LOCK", F_SYM(FUNC_ARG2),0,CREATE_FUNC(create_func_get_lock)},
- { "GEOMETRYN", F_SYM(FUNC_ARG2),0,CREATE_FUNC_GEOM(create_func_geometryn)},
- { "GEOMETRYTYPE", F_SYM(FUNC_ARG1),0,CREATE_FUNC_GEOM(create_func_geometry_type)},
- { "GEOMCOLLFROMTEXT", SYM(GEOMCOLLFROMTEXT)},
- { "GEOMCOLLFROMWKB", SYM(GEOMFROMWKB)},
- { "GEOMETRYCOLLECTIONFROMTEXT",SYM(GEOMCOLLFROMTEXT)},
- { "GEOMETRYCOLLECTIONFROMWKB",SYM(GEOMFROMWKB)},
- { "GEOMETRYFROMTEXT", SYM(GEOMFROMTEXT)},
- { "GEOMETRYFROMWKB", SYM(GEOMFROMWKB)},
- { "GEOMFROMTEXT", SYM(GEOMFROMTEXT)},
- { "GEOMFROMWKB", SYM(GEOMFROMWKB)},
- { "GLENGTH", F_SYM(FUNC_ARG1),0,CREATE_FUNC_GEOM(create_func_glength)},
- { "GREATEST", SYM(GREATEST_SYM)},
{ "GROUP_CONCAT", SYM(GROUP_CONCAT_SYM)},
- { "GROUP_UNIQUE_USERS", SYM(GROUP_UNIQUE_USERS)},
- { "HEX", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_hex)},
- { "IFNULL", F_SYM(FUNC_ARG2),0,CREATE_FUNC(create_func_ifnull)},
- { "INET_ATON", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_inet_aton)},
- { "INET_NTOA", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_inet_ntoa)},
- { "INSTR", F_SYM(FUNC_ARG2),0,CREATE_FUNC(create_func_instr)},
- { "INTERIORRINGN", F_SYM(FUNC_ARG2),0,CREATE_FUNC_GEOM(create_func_interiorringn)},
- { "INTERSECTS", F_SYM(FUNC_ARG2),0,CREATE_FUNC_GEOM(create_func_intersects)},
- { "ISCLOSED", F_SYM(FUNC_ARG1),0,CREATE_FUNC_GEOM(create_func_isclosed)},
- { "ISEMPTY", F_SYM(FUNC_ARG1),0,CREATE_FUNC_GEOM(create_func_isempty)},
- { "ISNULL", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_isnull)},
- { "IS_FREE_LOCK", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_is_free_lock)},
- { "IS_USED_LOCK", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_is_used_lock)},
- { "LAST_INSERT_ID", SYM(LAST_INSERT_ID)},
- { "ISSIMPLE", F_SYM(FUNC_ARG1),0,CREATE_FUNC_GEOM(create_func_issimple)},
- { "LAST_DAY", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_last_day)},
- { "LCASE", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_lcase)},
- { "LEAST", SYM(LEAST_SYM)},
- { "LENGTH", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_length)},
- { "LN", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_ln)},
- { "LINEFROMTEXT", SYM(LINEFROMTEXT)},
- { "LINEFROMWKB", SYM(GEOMFROMWKB)},
- { "LINESTRINGFROMTEXT",SYM(LINEFROMTEXT)},
- { "LINESTRINGFROMWKB",SYM(GEOMFROMWKB)},
- { "LOAD_FILE", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_load_file)},
- { "LOCATE", SYM(LOCATE)},
- { "LOG", SYM(LOG_SYM)},
- { "LOG2", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_log2)},
- { "LOG10", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_log10)},
- { "LOWER", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_lcase)},
- { "LPAD", F_SYM(FUNC_ARG3),0,CREATE_FUNC(create_func_lpad)},
- { "LTRIM", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_ltrim)},
- { "MAKE_SET", SYM(MAKE_SET_SYM)},
- { "MAKEDATE", F_SYM(FUNC_ARG2),0,CREATE_FUNC(create_func_makedate)},
- { "MAKETIME", F_SYM(FUNC_ARG3),0,CREATE_FUNC(create_func_maketime)},
- { "MASTER_POS_WAIT", SYM(MASTER_POS_WAIT)},
{ "MAX", SYM(MAX_SYM)},
- { "MBRCONTAINS", F_SYM(FUNC_ARG2),0,CREATE_FUNC_GEOM(create_func_contains)},
- { "MBRDISJOINT", F_SYM(FUNC_ARG2),0,CREATE_FUNC_GEOM(create_func_disjoint)},
- { "MBREQUAL", F_SYM(FUNC_ARG2),0,CREATE_FUNC_GEOM(create_func_equals)},
- { "MBRINTERSECTS", F_SYM(FUNC_ARG2),0,CREATE_FUNC_GEOM(create_func_intersects)},
- { "MBROVERLAPS", F_SYM(FUNC_ARG2),0,CREATE_FUNC_GEOM(create_func_overlaps)},
- { "MBRTOUCHES", F_SYM(FUNC_ARG2),0,CREATE_FUNC_GEOM(create_func_touches)},
- { "MBRWITHIN", F_SYM(FUNC_ARG2),0,CREATE_FUNC_GEOM(create_func_within)},
- { "MD5", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_md5)},
{ "MID", SYM(SUBSTRING)}, /* unireg function */
{ "MIN", SYM(MIN_SYM)},
- { "MLINEFROMTEXT", SYM(MLINEFROMTEXT)},
- { "MLINEFROMWKB", SYM(GEOMFROMWKB)},
- { "MPOINTFROMTEXT", SYM(MPOINTFROMTEXT)},
- { "MPOINTFROMWKB", SYM(GEOMFROMWKB)},
- { "MPOLYFROMTEXT", SYM(MPOLYFROMTEXT)},
- { "MPOLYFROMWKB", SYM(GEOMFROMWKB)},
- { "MONTHNAME", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_monthname)},
- { "MULTILINESTRINGFROMTEXT",SYM(MLINEFROMTEXT)},
- { "MULTILINESTRINGFROMWKB",SYM(GEOMFROMWKB)},
- { "MULTIPOINTFROMTEXT",SYM(MPOINTFROMTEXT)},
- { "MULTIPOINTFROMWKB",SYM(GEOMFROMWKB)},
- { "MULTIPOLYGONFROMTEXT",SYM(MPOLYFROMTEXT)},
- { "MULTIPOLYGONFROMWKB",SYM(GEOMFROMWKB)},
- { "NAME_CONST", F_SYM(FUNC_ARG2),0,CREATE_FUNC(create_func_name_const)},
{ "NOW", SYM(NOW_SYM)},
- { "NULLIF", F_SYM(FUNC_ARG2),0,CREATE_FUNC(create_func_nullif)},
- { "NUMGEOMETRIES", F_SYM(FUNC_ARG1),0,CREATE_FUNC_GEOM(create_func_numgeometries)},
- { "NUMINTERIORRINGS", F_SYM(FUNC_ARG1),0,CREATE_FUNC_GEOM(create_func_numinteriorring)},
- { "NUMPOINTS", F_SYM(FUNC_ARG1),0,CREATE_FUNC_GEOM(create_func_numpoints)},
- { "OCTET_LENGTH", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_length)},
- { "OCT", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_oct)},
- { "ORD", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_ord)},
- { "OVERLAPS", F_SYM(FUNC_ARG2),0,CREATE_FUNC_GEOM(create_func_overlaps)},
- { "PERIOD_ADD", F_SYM(FUNC_ARG2),0,CREATE_FUNC(create_func_period_add)},
- { "PERIOD_DIFF", F_SYM(FUNC_ARG2),0,CREATE_FUNC(create_func_period_diff)},
- { "PI", F_SYM(FUNC_ARG0),0,CREATE_FUNC(create_func_pi)},
- { "POINTFROMTEXT", SYM(POINTFROMTEXT)},
- { "POINTFROMWKB", SYM(GEOMFROMWKB)},
- { "POINTN", F_SYM(FUNC_ARG2),0,CREATE_FUNC_GEOM(create_func_pointn)},
- { "POLYFROMTEXT", SYM(POLYFROMTEXT)},
- { "POLYFROMWKB", SYM(GEOMFROMWKB)},
- { "POLYGONFROMTEXT", SYM(POLYFROMTEXT)},
- { "POLYGONFROMWKB", SYM(GEOMFROMWKB)},
{ "POSITION", SYM(POSITION_SYM)},
- { "POW", F_SYM(FUNC_ARG2),0,CREATE_FUNC(create_func_pow)},
- { "POWER", F_SYM(FUNC_ARG2),0,CREATE_FUNC(create_func_pow)},
- { "QUOTE", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_quote)},
- { "RADIANS", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_radians)},
- { "RAND", SYM(RAND)},
- { "RELEASE_LOCK", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_release_lock)},
- { "REVERSE", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_reverse)},
- { "ROUND", SYM(ROUND)},
- { "ROW_COUNT", SYM(ROW_COUNT_SYM)},
- { "RPAD", F_SYM(FUNC_ARG3),0,CREATE_FUNC(create_func_rpad)},
- { "RTRIM", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_rtrim)},
- { "SEC_TO_TIME", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_sec_to_time)},
- { "SESSION_USER", SYM(USER)},
- { "SUBDATE", SYM(SUBDATE_SYM)},
- { "SIGN", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_sign)},
- { "SIN", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_sin)},
- { "SHA", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_sha)},
- { "SHA1", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_sha)},
- { "SLEEP", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_sleep)},
- { "SOUNDEX", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_soundex)},
- { "SPACE", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_space)},
- { "SQRT", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_sqrt)},
- { "SRID", F_SYM(FUNC_ARG1),0,CREATE_FUNC_GEOM(create_func_srid)},
- { "STARTPOINT", F_SYM(FUNC_ARG1),0,CREATE_FUNC_GEOM(create_func_startpoint)},
+ { "SESSION_USER", SYM(USER)},
{ "STD", SYM(STD_SYM)},
{ "STDDEV", SYM(STD_SYM)},
{ "STDDEV_POP", SYM(STD_SYM)},
{ "STDDEV_SAMP", SYM(STDDEV_SAMP_SYM)},
- { "STR_TO_DATE", F_SYM(FUNC_ARG2),0,CREATE_FUNC(create_func_str_to_date)},
- { "STRCMP", F_SYM(FUNC_ARG2),0,CREATE_FUNC(create_func_strcmp)},
+ { "SUBDATE", SYM(SUBDATE_SYM)},
{ "SUBSTR", SYM(SUBSTRING)},
{ "SUBSTRING", SYM(SUBSTRING)},
- { "SUBSTRING_INDEX", SYM(SUBSTRING_INDEX)},
- { "SUBTIME", F_SYM(FUNC_ARG2),0,CREATE_FUNC(create_func_subtime)},
{ "SUM", SYM(SUM_SYM)},
{ "SYSDATE", SYM(SYSDATE)},
- { "SYSTEM_USER", SYM(USER)},
- { "TAN", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_tan)},
- { "TIME_FORMAT", F_SYM(FUNC_ARG2),0,CREATE_FUNC(create_func_time_format)},
- { "TIME_TO_SEC", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_time_to_sec)},
- { "TIMEDIFF", F_SYM(FUNC_ARG2),0,CREATE_FUNC(create_func_timediff)},
- { "TO_DAYS", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_to_days)},
- { "TOUCHES", F_SYM(FUNC_ARG2),0,CREATE_FUNC_GEOM(create_func_touches)},
+ { "SYSTEM_USER", SYM(USER)},
{ "TRIM", SYM(TRIM)},
- { "UCASE", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_ucase)},
- { "UNCOMPRESS", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_uncompress)},
- { "UNCOMPRESSED_LENGTH", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_uncompressed_length)},
- { "UNHEX", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_unhex)},
- { "UNIQUE_USERS", SYM(UNIQUE_USERS)},
- { "UNIX_TIMESTAMP", SYM(UNIX_TIMESTAMP)},
- { "UPPER", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_ucase)},
- { "UUID", F_SYM(FUNC_ARG0),0,CREATE_FUNC(create_func_uuid)},
{ "VARIANCE", SYM(VARIANCE_SYM)},
{ "VAR_POP", SYM(VARIANCE_SYM)},
{ "VAR_SAMP", SYM(VAR_SAMP_SYM)},
- { "VERSION", F_SYM(FUNC_ARG0),0,CREATE_FUNC(create_func_version)},
- { "WEEKDAY", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_weekday)},
- { "WEEKOFYEAR", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_weekofyear)},
- { "WITHIN", F_SYM(FUNC_ARG2),0,CREATE_FUNC_GEOM(create_func_within)},
- { "X", F_SYM(FUNC_ARG1),0,CREATE_FUNC_GEOM(create_func_x)},
- { "Y", F_SYM(FUNC_ARG1),0,CREATE_FUNC_GEOM(create_func_y)},
- { "YEARWEEK", SYM(YEARWEEK)}
};
diff --git a/sql/lex_symbol.h b/sql/lex_symbol.h
index c87cdb4ec43..000c0709071 100644
--- a/sql/lex_symbol.h
+++ b/sql/lex_symbol.h
@@ -25,7 +25,6 @@ typedef struct st_symbol {
const char *name;
uint tok;
uint length;
- void (*create_func)();
struct st_sym_group *group;
} SYMBOL;
diff --git a/sql/lock.cc b/sql/lock.cc
index e22eecd9b73..322778b89c3 100644
--- a/sql/lock.cc
+++ b/sql/lock.cc
@@ -1,4 +1,4 @@
-/* Copyright (C) 2000-2006 MySQL AB
+/* Copyright 2000-2008 MySQL AB, 2008 Sun Microsystems, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -14,8 +14,11 @@
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
-/* locking functions for mysql */
-/*
+/**
+ @file
+
+ Locking functions for mysql.
+
Because of the new concurrent inserts, we must first get external locks
before getting internal locks. If we do it in the other order, the status
information is not up to date when called from the lock handler.
@@ -60,7 +63,12 @@
- When calling UNLOCK TABLES we call mysql_unlock_tables() for all
tables used in LOCK TABLES
-TODO:
+ If table_handler->external_lock(thd, locktype) fails, we call
+ table_handler->external_lock(thd, F_UNLCK) for each table that was locked,
+ excluding one that caused failure. That means handler must cleanup itself
+ in case external_lock() fails.
+
+ @todo
Change to use my_malloc() ONLY when using LOCK TABLES command or when
we are forced to use mysql_lock_merge.
*/
@@ -69,6 +77,11 @@ TODO:
#include <hash.h>
#include <assert.h>
+/**
+ @defgroup Locking Locking
+ @{
+*/
+
extern HASH open_cache;
/* flags for get_lock_data */
@@ -92,6 +105,7 @@ static void print_lock_error(int error, const char *);
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,
@@ -110,16 +124,90 @@ static void print_lock_error(int error, const char *);
static int thr_lock_errno_to_mysql[]=
{ 0, 1, ER_LOCK_WAIT_TIMEOUT, ER_LOCK_DEADLOCK };
+/**
+ Perform semantic checks for mysql_lock_tables.
+ @param thd The current thread
+ @param tables The tables to lock
+ @param count The number of tables to lock
+ @param flags Lock flags
+ @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)
+{
+ bool log_table_write_query;
+ uint system_count;
+ uint i;
+
+ DBUG_ENTER("mysql_lock_tables_check");
+
+ system_count= 0;
+ log_table_write_query= (is_log_table_write_query(thd->lex->sql_command)
+ || ((flags & MYSQL_LOCK_PERF_SCHEMA) != 0));
+
+ for (i=0 ; i<count; i++)
+ {
+ TABLE *t= tables[i];
+
+ /* Protect against 'fake' partially initialized TABLE_SHARE */
+ DBUG_ASSERT(t->s->table_category != TABLE_UNKNOWN_CATEGORY);
+
+ /*
+ Table I/O to performance schema tables is performed
+ only internally by the server implementation.
+ When a user is requesting a lock, the following
+ constraints are enforced:
+ */
+ if (t->s->require_write_privileges() &&
+ ! log_table_write_query)
+ {
+ /*
+ A user should not be able to prevent writes,
+ or hold any type of lock in a session,
+ since this would be a DOS attack.
+ */
+ if ((t->reginfo.lock_type >= TL_READ_NO_INSERT)
+ || (thd->lex->sql_command == SQLCOM_LOCK_TABLES))
+ {
+ my_error(ER_CANT_LOCK_LOG_TABLE, MYF(0));
+ DBUG_RETURN(1);
+ }
+ }
+
+ if ((t->s->table_category == TABLE_CATEGORY_SYSTEM) &&
+ (t->reginfo.lock_type >= TL_WRITE_ALLOW_WRITE))
+ {
+ system_count++;
+ }
+ }
+
+ /*
+ Locking of system tables is restricted:
+ locking a mix of system and non-system tables in the same lock
+ is prohibited, to prevent contention.
+ */
+ if ((system_count > 0) && (system_count < count))
+ {
+ my_error(ER_WRONG_LOCK_OF_SYSTEM_TABLE, MYF(0));
+ DBUG_RETURN(1);
+ }
+
+ DBUG_RETURN(0);
+}
+
MYSQL_LOCK *mysql_lock_tables(THD *thd, TABLE **tables, uint count,
uint flags, bool *need_reopen)
{
MYSQL_LOCK *sql_lock;
TABLE *write_lock_used;
int rc;
+
DBUG_ENTER("mysql_lock_tables");
*need_reopen= FALSE;
+ if (mysql_lock_tables_check(thd, tables, count, flags))
+ DBUG_RETURN (NULL);
+
for (;;)
{
if (! (sql_lock= get_lock_data(thd, tables, count, GET_LOCK_STORE_LOCKS,
@@ -137,7 +225,7 @@ MYSQL_LOCK *mysql_lock_tables(THD *thd, TABLE **tables, uint count,
{
/* Clear the lock type of all lock data to avoid reusage. */
reset_lock_data(sql_lock);
- my_free((gptr) sql_lock,MYF(0));
+ my_free((uchar*) sql_lock,MYF(0));
sql_lock=0;
break;
}
@@ -145,22 +233,41 @@ MYSQL_LOCK *mysql_lock_tables(THD *thd, TABLE **tables, uint count,
{
/* Clear the lock type of all lock data to avoid reusage. */
reset_lock_data(sql_lock);
- my_free((gptr) sql_lock,MYF(0));
+ my_free((uchar*) sql_lock,MYF(0));
goto retry;
}
}
- thd->proc_info="System lock";
+ if (!(flags & MYSQL_LOCK_IGNORE_GLOBAL_READ_ONLY) &&
+ write_lock_used &&
+ 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.
+ */
+ reset_lock_data(sql_lock);
+ my_free((uchar*) sql_lock, MYF(0));
+ sql_lock=0;
+ my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--read-only");
+ break;
+ }
+
+ thd_proc_info(thd, "System lock");
+ DBUG_PRINT("info", ("thd->proc_info %s", thd->proc_info));
if (sql_lock->table_count && lock_external(thd, sql_lock->table,
sql_lock->table_count))
{
/* Clear the lock type of all lock data to avoid reusage. */
reset_lock_data(sql_lock);
- my_free((gptr) sql_lock,MYF(0));
+ my_free((uchar*) sql_lock,MYF(0));
sql_lock=0;
break;
}
- thd->proc_info="Table lock";
+ thd_proc_info(thd, "Table lock");
+ DBUG_PRINT("info", ("thd->proc_info %s", thd->proc_info));
thd->locked=1;
/* Copy the lock data array. thr_multi_lock() reorders its contens. */
memcpy(sql_lock->locks + sql_lock->lock_count, sql_lock->locks,
@@ -175,7 +282,7 @@ MYSQL_LOCK *mysql_lock_tables(THD *thd, TABLE **tables, uint count,
if (sql_lock->table_count)
VOID(unlock_external(thd, sql_lock->table, sql_lock->table_count));
my_error(rc, MYF(0));
- my_free((gptr) sql_lock,MYF(0));
+ my_free((uchar*) sql_lock,MYF(0));
sql_lock= 0;
break;
}
@@ -193,6 +300,10 @@ MYSQL_LOCK *mysql_lock_tables(THD *thd, TABLE **tables, uint count,
}
else if (!thd->some_tables_deleted || (flags & MYSQL_LOCK_IGNORE_FLUSH))
{
+ /*
+ Thread was killed or lock aborted. Let upper level close all
+ used tables and retry or give error.
+ */
thd->locked=0;
break;
}
@@ -203,7 +314,7 @@ MYSQL_LOCK *mysql_lock_tables(THD *thd, TABLE **tables, uint count,
thd->locked=0;
break;
}
- thd->proc_info=0;
+ thd_proc_info(thd, 0);
/* some table was altered or deleted. reopen tables marked deleted */
mysql_unlock_tables(thd,sql_lock);
@@ -218,7 +329,7 @@ retry:
if (wait_for_tables(thd))
break; // Couldn't open tables
}
- thd->proc_info=0;
+ thd_proc_info(thd, 0);
if (thd->killed)
{
thd->send_kill_message();
@@ -229,7 +340,7 @@ retry:
}
}
- thd->lock_time();
+ thd->set_time_after_lock();
DBUG_RETURN (sql_lock);
}
@@ -240,6 +351,7 @@ static int lock_external(THD *thd, TABLE **tables, uint count)
int lock_type,error;
DBUG_ENTER("lock_external");
+ DBUG_PRINT("info", ("count %d", count));
for (i=1 ; i <= count ; i++, tables++)
{
DBUG_ASSERT((*tables)->reginfo.lock_type >= TL_READ);
@@ -249,11 +361,12 @@ static int lock_external(THD *thd, TABLE **tables, uint count)
(*tables)->reginfo.lock_type <= TL_READ_NO_INSERT))
lock_type=F_RDLCK;
- if ((error= (*tables)->file->ha_external_lock(thd,lock_type)))
+ if ((error=(*tables)->file->ha_external_lock(thd,lock_type)))
{
print_lock_error(error, (*tables)->file->table_type());
- for (; i-- ; tables--)
+ while (--i)
{
+ tables--;
(*tables)->file->ha_external_lock(thd, F_UNLCK);
(*tables)->current_lock=F_UNLCK;
}
@@ -276,14 +389,15 @@ void mysql_unlock_tables(THD *thd, MYSQL_LOCK *sql_lock)
thr_multi_unlock(sql_lock->locks,sql_lock->lock_count);
if (sql_lock->table_count)
VOID(unlock_external(thd,sql_lock->table,sql_lock->table_count));
- my_free((gptr) sql_lock,MYF(0));
+ my_free((uchar*) sql_lock,MYF(0));
DBUG_VOID_RETURN;
}
-/*
- Unlock some of the tables locked by mysql_lock_tables
+/**
+ Unlock some of the tables locked by mysql_lock_tables.
+
This will work even if get_lock_data fails (next unlock will free all)
- */
+*/
void mysql_unlock_some_tables(THD *thd, TABLE **table,uint count)
{
@@ -295,8 +409,8 @@ void mysql_unlock_some_tables(THD *thd, TABLE **table,uint count)
}
-/*
-** unlock all tables locked for read.
+/**
+ unlock all tables locked for read.
*/
void mysql_unlock_read_tables(THD *thd, MYSQL_LOCK *sql_lock)
@@ -369,6 +483,9 @@ void mysql_unlock_read_tables(THD *thd, MYSQL_LOCK *sql_lock)
unlock_external() we call handler::external_lock(F_UNLCK) only
if table->current_lock is not F_UNLCK.
+ @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.
*/
@@ -437,32 +554,51 @@ void mysql_lock_remove(THD *thd, MYSQL_LOCK *locked,TABLE *table,
}
}
-/* abort all other threads waiting to get lock in 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)
+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)))
{
for (uint i=0; i < locked->lock_count; i++)
- thr_abort_locks(locked->locks[i]->lock);
- my_free((gptr) locked,MYF(0));
+ thr_abort_locks(locked->locks[i]->lock, upgrade_lock);
+ my_free((uchar*) locked,MYF(0));
}
+ DBUG_VOID_RETURN;
}
-/*
- Abort one thread / table combination
+/**
+ Abort one thread / table combination.
- SYNOPSIS
- mysql_lock_abort_for_thread()
- thd Thread handler
- table Table that should be removed from lock queue
+ @param thd Thread handler
+ @param table Table that should be removed from lock queue
- RETURN
+ @retval
0 Table was not locked by another thread
+ @retval
1 Table was locked by at least one other thread
*/
@@ -479,10 +615,10 @@ bool mysql_lock_abort_for_thread(THD *thd, TABLE *table)
for (uint i=0; i < locked->lock_count; i++)
{
if (thr_abort_locks_for_thread(locked->locks[i]->lock,
- table->in_use->real_id))
+ table->in_use->thread_id))
result= TRUE;
}
- my_free((gptr) locked,MYF(0));
+ my_free((uchar*) locked,MYF(0));
}
DBUG_RETURN(result);
}
@@ -524,34 +660,33 @@ MYSQL_LOCK *mysql_lock_merge(MYSQL_LOCK *a,MYSQL_LOCK *b)
}
/* Delete old, not needed locks */
- my_free((gptr) a,MYF(0));
- my_free((gptr) b,MYF(0));
+ my_free((uchar*) a,MYF(0));
+ my_free((uchar*) b,MYF(0));
DBUG_RETURN(sql_lock);
}
-/*
+/**
Find duplicate lock in tables.
- SYNOPSIS
- mysql_lock_have_duplicate()
- thd The current thread.
- needle The table to check for duplicate lock.
- haystack The list of tables to search for the dup lock.
+ 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.
- NOTE
+ @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.
- 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.
-
- RETURN
- NULL No duplicate lock found.
- ! NULL First table from 'haystack' that matches a lock on 'needle'.
+ @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,
@@ -635,7 +770,7 @@ TABLE_LIST *mysql_lock_have_duplicate(THD *thd, TABLE_LIST *needle,
}
- /* unlock a set of external */
+/** Unlock a set of external. */
static int unlock_external(THD *thd, TABLE **table,uint count)
{
@@ -648,7 +783,7 @@ static int unlock_external(THD *thd, TABLE **table,uint count)
if ((*table)->current_lock != F_UNLCK)
{
(*table)->current_lock = F_UNLCK;
- if ((error= (*table)->file->ha_external_lock(thd, F_UNLCK)))
+ if ((error=(*table)->file->ha_external_lock(thd, F_UNLCK)))
{
error_code=error;
print_lock_error(error_code, (*table)->file->table_type());
@@ -660,21 +795,17 @@ static int unlock_external(THD *thd, TABLE **table,uint count)
}
-/*
- Get lock structures from table structs and initialize locks
-
- SYNOPSIS
- get_lock_data()
- thd Thread handler
- table_ptr Pointer to tables that should be locks
- flags One of:
- GET_LOCK_UNLOCK: If we should send TL_IGNORE to
- store lock
- GET_LOCK_STORE_LOCKS: Store lock info in TABLE
- write_lock_used Store pointer to last table with WRITE_ALLOW_WRITE
+/**
+ Get lock structures from table structs and initialize locks.
+
+ @param thd Thread handler
+ @param table_ptr Pointer to tables that should be locks
+ @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
*/
-
static MYSQL_LOCK *get_lock_data(THD *thd, TABLE **table_ptr, uint count,
uint flags, TABLE **write_lock_used)
{
@@ -684,27 +815,19 @@ static MYSQL_LOCK *get_lock_data(THD *thd, TABLE **table_ptr, uint count,
TABLE **to, **table_buf;
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++)
{
- if (table_ptr[i]->s->tmp_table != NON_TRANSACTIONAL_TMP_TABLE)
+ TABLE *t= table_ptr[i];
+
+ if (t->s->tmp_table != NON_TRANSACTIONAL_TMP_TABLE)
{
- tables+=table_ptr[i]->file->lock_count();
+ tables+= t->file->lock_count();
lock_count++;
}
- /*
- To be able to open and lock for reading system tables like 'mysql.proc',
- when we already have some tables opened and locked, and avoid deadlocks
- we have to disallow write-locking of these tables with any other tables.
- */
- if (table_ptr[i]->s->system_table &&
- table_ptr[i]->reginfo.lock_type >= TL_WRITE_ALLOW_WRITE &&
- count != 1)
- {
- my_error(ER_WRONG_LOCK_OF_SYSTEM_TABLE, MYF(0), table_ptr[i]->s->db,
- table_ptr[i]->s->table_name);
- DBUG_RETURN(0);
- }
}
/*
@@ -722,7 +845,6 @@ static MYSQL_LOCK *get_lock_data(THD *thd, TABLE **table_ptr, uint count,
locks= locks_buf= sql_lock->locks= (THR_LOCK_DATA**) (sql_lock + 1);
to= table_buf= sql_lock->table= (TABLE**) (locks + tables * 2);
sql_lock->table_count=lock_count;
- sql_lock->lock_count=tables;
for (i=0 ; i < count ; i++)
{
@@ -732,7 +854,7 @@ static MYSQL_LOCK *get_lock_data(THD *thd, TABLE **table_ptr, uint count,
if ((table=table_ptr[i])->s->tmp_table == NON_TRANSACTIONAL_TMP_TABLE)
continue;
lock_type= table->reginfo.lock_type;
- DBUG_ASSERT (lock_type != TL_WRITE_DEFAULT);
+ DBUG_ASSERT(lock_type != TL_WRITE_DEFAULT && lock_type != TL_READ_DEFAULT);
if (lock_type >= TL_WRITE_ALLOW_WRITE)
{
*write_lock_used=table;
@@ -742,7 +864,7 @@ static MYSQL_LOCK *get_lock_data(THD *thd, TABLE **table_ptr, uint count,
/* 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);
- my_free((gptr) sql_lock,MYF(0));
+ my_free((uchar*) sql_lock,MYF(0));
DBUG_RETURN(0);
}
}
@@ -762,35 +884,46 @@ static MYSQL_LOCK *get_lock_data(THD *thd, TABLE **table_ptr, uint count,
for ( ; org_locks != locks ; org_locks++)
(*org_locks)->debug_print_param= (void *) table;
}
+ /*
+ We do not use 'tables', because there are cases where store_lock()
+ returns less locks than lock_count() claimed. This can happen when
+ a FLUSH TABLES tries to abort locks from a MERGE table of another
+ thread. When that thread has just opened the table, but not yet
+ attached its children, it cannot return the locks. lock_count()
+ always returns the number of locks that an attached table has.
+ This is done to avoid the reverse situation: If lock_count() would
+ return 0 for a non-attached MERGE table, and that table becomes
+ attached between the calls to lock_count() and store_lock(), then
+ we would have allocated too little memory for the lock data. Now
+ we may allocate too much, but better safe than memory overrun.
+ And in the FLUSH case, the memory is released quickly anyway.
+ */
+ sql_lock->lock_count= locks - locks_buf;
+ DBUG_PRINT("info", ("sql_lock->table_count %d sql_lock->lock_count %d",
+ sql_lock->table_count, sql_lock->lock_count));
DBUG_RETURN(sql_lock);
}
-/*
+/**
Reset lock type in lock data.
- SYNOPSIS
- reset_lock_data()
- sql_lock The MySQL lock.
-
- DESCRIPTION
+ 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.
- 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.
+ 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.
- 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.
- Clear the lock type of all lock data. This ensures that the next
- lock request will set its lock type properly.
-
- RETURN
- void
+ @param sql_lock The MySQL lock.
*/
static void reset_lock_data(MYSQL_LOCK *sql_lock)
@@ -813,20 +946,19 @@ static void reset_lock_data(MYSQL_LOCK *sql_lock)
This is used when we need total access to a closed, not open table
*****************************************************************************/
-/*
+/**
Lock and wait for the named lock.
- SYNOPSIS
- lock_and_wait_for_table_name()
- thd Thread handler
- table_list Lock first table in this list
+ @param thd Thread handler
+ @param table_list Lock first table in this list
- NOTES
+ @note
Works together with global read lock.
- RETURN
+ @retval
0 ok
+ @retval
1 error
*/
@@ -839,7 +971,7 @@ int lock_and_wait_for_table_name(THD *thd, TABLE_LIST *table_list)
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)) < 0)
+ if ((lock_retcode = lock_table_name(thd, table_list, TRUE)) < 0)
goto end;
if (lock_retcode && wait_for_locked_table_names(thd, table_list))
{
@@ -855,33 +987,34 @@ end:
}
-/*
+/**
Put a not open table with an old refresh version in the table cache.
- SYNPOSIS
- lock_table_name()
- thd Thread handler
- table_list Lock first table in this list
+ @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
+
+ @note
+ One must have a lock on LOCK_open!
- WARNING
+ @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'
- NOTES
+ @note
This will force any other threads that uses the table to release it
as soon as possible.
- REQUIREMENTS
- One must have a lock on LOCK_open !
-
- RETURN:
+ @return
< 0 error
+ @return
== 0 table locked
+ @return
> 0 table locked, but someone is using it
*/
-int lock_table_name(THD *thd, TABLE_LIST *table_list)
+int lock_table_name(THD *thd, TABLE_LIST *table_list, bool check_in_use)
{
TABLE *table;
char key[MAX_DBKEY_LENGTH];
@@ -891,26 +1024,35 @@ int lock_table_name(THD *thd, TABLE_LIST *table_list)
DBUG_ENTER("lock_table_name");
DBUG_PRINT("enter",("db: %s name: %s", db, table_list->table_name));
- safe_mutex_assert_owner(&LOCK_open);
-
- key_length= (uint)(strmov(strmov(key, db) + 1, table_list->table_name) -
- key) + 1;
+ key_length= create_table_def_key(thd, key, table_list, 0);
- /* Only insert the table if we haven't insert it already */
- for (table=(TABLE*) hash_first(&open_cache, (byte*)key, key_length, &state);
- table ;
- table = (TABLE*) hash_next(&open_cache, (byte*)key, key_length, &state))
- if (table->in_use == thd)
- DBUG_RETURN(0);
+ if (check_in_use)
+ {
+ /* 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_list->table=table;
/* Return 1 if table is in use */
DBUG_RETURN(test(remove_table_from_cache(thd, db, table_list->table_name,
- RTFC_NO_FLAG)));
+ check_in_use ? RTFC_NO_FLAG : RTFC_WAIT_OTHER_THREAD_FLAG)));
}
@@ -918,7 +1060,7 @@ void unlock_table_name(THD *thd, TABLE_LIST *table_list)
{
if (table_list->table)
{
- hash_delete(&open_cache, (byte*) table_list->table);
+ hash_delete(&open_cache, (uchar*) table_list->table);
broadcast_refresh();
}
}
@@ -928,8 +1070,17 @@ static bool locked_named_table(THD *thd, TABLE_LIST *table_list)
{
for (; table_list ; table_list=table_list->next_local)
{
- if (table_list->table && table_is_used(table_list->table,0))
- return 1;
+ 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
}
@@ -939,6 +1090,7 @@ 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))
@@ -948,30 +1100,29 @@ bool wait_for_locked_table_names(THD *thd, TABLE_LIST *table_list)
result=1;
break;
}
- wait_for_refresh(thd);
+ 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
+/**
+ Lock all tables in list with a name lock.
- SYNOPSIS
- lock_table_names()
- thd Thread handle
- table_list Names of tables to 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
- NOTES
+ @note
If you are just locking one table, you should use
lock_and_wait_for_table_name().
- REQUIREMENTS
- One must have a lock on LOCK_open when calling this
-
- RETURN
+ @retval
0 ok
+ @retval
1 Fatal error (end of memory ?)
*/
@@ -983,7 +1134,7 @@ bool lock_table_names(THD *thd, TABLE_LIST *table_list)
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)) < 0)
+ 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
@@ -1000,34 +1151,139 @@ end:
}
-/*
- Unlock all tables in list with a name lock
+/**
+ Unlock all tables in list with a name lock.
- SYNOPSIS
- unlock_table_names()
+ @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
+
+ @retval TRUE An error occured.
+ @retval FALSE Name lock successfully acquired.
+*/
+
+bool lock_table_names_exclusively(THD *thd, TABLE_LIST *table_list)
+{
+ if (lock_table_names(thd, table_list))
+ 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;
+ }
+ 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
+*/
+
+bool
+is_table_name_exclusively_locked_by_this_thread(THD *thd,
+ TABLE_LIST *table_list)
+{
+ 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);
+}
+
+
+/**
+ 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 (table->in_use == thd &&
+ table->open_placeholder == 1 &&
+ table->s->version == 0)
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/**
+ Unlock all tables in list with a name lock.
+
+ @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)
+ (default 0, which will unlock all tables)
- NOTES
+ @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.
- RETURN
+ @retval
0 ok
+ @retval
1 Fatal error (end of memory ?)
*/
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;
}
@@ -1277,6 +1533,7 @@ void start_waiting_global_read_lock(THD *thd)
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);
@@ -1318,14 +1575,9 @@ bool make_global_read_lock_block_commit(THD *thd)
}
-/*
+/**
Broadcast COND_refresh and COND_global_read_lock.
- SYNOPSIS
- broadcast_refresh()
- void No parameters.
-
- DESCRIPTION
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
@@ -1337,12 +1589,9 @@ bool make_global_read_lock_block_commit(THD *thd)
in global read lock handling. But now it is necessary to signal
both conditions at the same time.
- NOTE
+ @note
When signalling COND_global_read_lock within the global read lock
handling, it is not necessary to also signal COND_refresh.
-
- RETURN
- void
*/
void broadcast_refresh(void)
@@ -1351,4 +1600,6 @@ void broadcast_refresh(void)
VOID(pthread_cond_broadcast(&COND_global_read_lock));
}
-
+/**
+ @} (end of group Locking)
+*/
diff --git a/sql/log.cc b/sql/log.cc
index d979dd101e0..74dc75702ae 100644
--- a/sql/log.cc
+++ b/sql/log.cc
@@ -1,4 +1,4 @@
-/* Copyright (C) 2000-2003 MySQL AB
+/* Copyright 2000-2008 MySQL AB, 2008 Sun Microsystems, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -14,15 +14,20 @@
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
-/* logging of commands */
-/* TODO: Abort logging when we get an error in reading or writing log files */
+/**
+ @file
-#ifdef __EMX__
-#include <io.h>
-#endif
+ @brief
+ logging of commands
+
+ @todo
+ Abort logging when we get an error in reading or writing log files
+*/
#include "mysql_priv.h"
#include "sql_repl.h"
+#include "rpl_filter.h"
+#include "rpl_rli.h"
#include <my_dir.h>
#include <stdarg.h>
@@ -32,92 +37,1415 @@
#include "message.h"
#endif
-MYSQL_LOG mysql_log, mysql_slow_log, mysql_bin_log;
-ulong sync_binlog_counter= 0;
+#include <mysql/plugin.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)
-static Muted_query_log_event invisible_commit;
+#define FLAGSTR(V,F) ((V)&(F)?#F" ":"")
+
+LOGGER logger;
+
+MYSQL_BIN_LOG mysql_bin_log;
+ulong sync_binlog_counter= 0;
static bool test_if_number(const char *str,
long *res, bool allow_wildcards);
-static bool binlog_init();
-static int binlog_close_connection(THD *thd);
-static int binlog_savepoint_set(THD *thd, void *sv);
-static int binlog_savepoint_rollback(THD *thd, void *sv);
-static int binlog_commit(THD *thd, bool all);
-static int binlog_rollback(THD *thd, bool all);
-static int binlog_prepare(THD *thd, bool all);
-
-handlerton binlog_hton = {
- "binlog",
- SHOW_OPTION_YES,
- "This is a meta storage engine to represent the binlog in a transaction",
- DB_TYPE_UNKNOWN, /* IGNORE for now */
- binlog_init,
- 0,
- sizeof(my_off_t), /* savepoint size = binlog offset */
- binlog_close_connection,
- binlog_savepoint_set,
- binlog_savepoint_rollback,
- NULL, /* savepoint_release */
- binlog_commit,
- binlog_rollback,
- binlog_prepare,
- NULL, /* recover */
- NULL, /* commit_by_xid */
- NULL, /* rollback_by_xid */
- NULL, /* create_cursor_read_view */
- NULL, /* set_cursor_read_view */
- NULL, /* close_cursor_read_view */
- HTON_HIDDEN
+static int binlog_init(void *p);
+static int binlog_close_connection(handlerton *hton, THD *thd);
+static int binlog_savepoint_set(handlerton *hton, THD *thd, void *sv);
+static int binlog_savepoint_rollback(handlerton *hton, THD *thd, void *sv);
+static int binlog_commit(handlerton *hton, THD *thd, bool all);
+static int binlog_rollback(handlerton *hton, THD *thd, bool all);
+static int binlog_prepare(handlerton *hton, THD *thd, bool all);
+
+/**
+ 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
+ handlers, so that the presence of logging does not interfere and affect
+ the logic of an application.
+*/
+class Silence_log_table_errors : public Internal_error_handler
+{
+ char m_message[MYSQL_ERRMSG_SIZE];
+public:
+ Silence_log_table_errors()
+ {
+ m_message[0]= '\0';
+ }
+
+ virtual ~Silence_log_table_errors() {}
+
+ virtual bool handle_error(uint sql_errno, const char *message,
+ MYSQL_ERROR::enum_warning_level level,
+ THD *thd);
+ const char *message() const { return m_message; }
+};
+
+bool
+Silence_log_table_errors::handle_error(uint /* sql_errno */,
+ const char *message_arg,
+ MYSQL_ERROR::enum_warning_level /* level */,
+ THD * /* thd */)
+{
+ strmake(m_message, message_arg, sizeof(m_message)-1);
+ return TRUE;
+}
+
+
+sql_print_message_func sql_print_message_handlers[3] =
+{
+ sql_print_information,
+ sql_print_warning,
+ sql_print_error
+};
+
+
+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));
+}
+
+/*
+ Helper class to hold a mutex for the duration of the
+ block.
+
+ Eliminates the need for explicit unlocking of mutexes on, e.g.,
+ error returns. On passing a null pointer, the sentry will not do
+ anything.
+ */
+class Mutex_sentry
+{
+public:
+ Mutex_sentry(pthread_mutex_t *mutex)
+ : m_mutex(mutex)
+ {
+ if (m_mutex)
+ pthread_mutex_lock(mutex);
+ }
+
+ ~Mutex_sentry()
+ {
+ if (m_mutex)
+ pthread_mutex_unlock(m_mutex);
+#ifndef DBUG_OFF
+ m_mutex= 0;
+#endif
+ }
+
+private:
+ pthread_mutex_t *m_mutex;
+
+ // It's not allowed to copy this object in any way
+ Mutex_sentry(Mutex_sentry const&);
+ void operator=(Mutex_sentry const&);
};
/*
+ Helper class to store binary log transaction data.
+*/
+class binlog_trx_data {
+public:
+ binlog_trx_data()
+ : at_least_one_stmt(0), m_pending(0), before_stmt_pos(MY_OFF_T_UNDEF)
+ {
+ trans_log.end_of_file= max_binlog_cache_size;
+ }
+
+ ~binlog_trx_data()
+ {
+ DBUG_ASSERT(pending() == NULL);
+ close_cached_file(&trans_log);
+ }
+
+ my_off_t position() const {
+ return my_b_tell(&trans_log);
+ }
+
+ bool empty() const
+ {
+ return pending() == NULL && my_b_tell(&trans_log) == 0;
+ }
+
+ /*
+ Truncate the transaction 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));
+ DBUG_PRINT("info", ("before_stmt_pos=%lu", (ulong) pos));
+ delete pending();
+ set_pending(0);
+ reinit_io_cache(&trans_log, WRITE_CACHE, pos, 0, 0);
+ if (pos < before_stmt_pos)
+ before_stmt_pos= MY_OFF_T_UNDEF;
+
+ /*
+ The only valid positions that can be truncated to are at the
+ beginning of a statement. We are relying on this fact to be able
+ to set the at_least_one_stmt flag correctly. In other word, if
+ we are truncating to the beginning of the transaction cache,
+ there will be no statements in the cache, otherwhise, we will
+ have at least one statement in the transaction cache.
+ */
+ at_least_one_stmt= (pos > 0);
+ }
+
+ /*
+ Reset the entire contents of the transaction cache, emptying it
+ completely.
+ */
+ void reset() {
+ if (!empty())
+ truncate(0);
+ before_stmt_pos= MY_OFF_T_UNDEF;
+ trans_log.end_of_file= max_binlog_cache_size;
+ DBUG_ASSERT(empty());
+ }
+
+ Rows_log_event *pending() const
+ {
+ return m_pending;
+ }
+
+ void set_pending(Rows_log_event *const pending)
+ {
+ m_pending= pending;
+ }
+
+ IO_CACHE trans_log; // The transaction cache
+
+ /**
+ Boolean that is true if there is at least one statement in the
+ transaction cache.
+ */
+ bool at_least_one_stmt;
+
+private:
+ /*
+ Pending binrows event. This event is the event where the rows are
+ currently written.
+ */
+ Rows_log_event *m_pending;
+
+public:
+ /*
+ Binlog position before the start of the current statement.
+ */
+ my_off_t before_stmt_pos;
+};
+
+handlerton *binlog_hton;
+
+bool LOGGER::is_log_table_enabled(uint log_table_type)
+{
+ switch (log_table_type) {
+ case QUERY_LOG_SLOW:
+ return (table_log_handler != NULL) && opt_slow_log;
+ case QUERY_LOG_GENERAL:
+ return (table_log_handler != NULL) && opt_log ;
+ default:
+ DBUG_ASSERT(0);
+ return FALSE; /* make compiler happy */
+ }
+}
+
+
+/* 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)
+{
+ if (db_len == 5 &&
+ !(lower_case_table_names ?
+ my_strcasecmp(system_charset_info, db, "mysql") :
+ strcmp(db, "mysql")))
+ {
+ if (table_name_len == 11 && !(lower_case_table_names ?
+ my_strcasecmp(system_charset_info,
+ table_name, "general_log") :
+ strcmp(table_name, "general_log")))
+ {
+ if (!check_if_opened || logger.is_log_table_enabled(QUERY_LOG_GENERAL))
+ return QUERY_LOG_GENERAL;
+ return 0;
+ }
+
+ if (table_name_len == 8 && !(lower_case_table_names ?
+ my_strcasecmp(system_charset_info, table_name, "slow_log") :
+ strcmp(table_name, "slow_log")))
+ {
+ if (!check_if_opened || logger.is_log_table_enabled(QUERY_LOG_SLOW))
+ return QUERY_LOG_SLOW;
+ return 0;
+ }
+ }
+ return 0;
+}
+
+
+Log_to_csv_event_handler::Log_to_csv_event_handler()
+{
+}
+
+
+Log_to_csv_event_handler::~Log_to_csv_event_handler()
+{
+}
+
+
+void Log_to_csv_event_handler::cleanup()
+{
+ logger.is_log_tables_initialized= FALSE;
+}
+
+/* log event handlers */
+
+/**
+ Log command to the general log table
+
+ Log given command to the general log table.
+
+ @param event_time command start timestamp
+ @param user_host the pointer to the string with user@host info
+ @param user_host_len length of the user_host string. this is computed
+ once and passed to all general log event handlers
+ @param thread_id Id of the thread, issued a query
+ @param command_type the type of the command being logged
+ @param command_type_len the length of the string above
+ @param sql_text the very text of the query being executed
+ @param sql_text_len the length of sql_text string
+
+
+ @return This function attempts to never call my_error(). This is
+ necessary, because general logging happens already after a statement
+ status has been sent to the client, so the client can not see the
+ error anyway. Besides, the error is not related to the statement
+ being executed and is internal, and thus should be handled
+ internally (@todo: how?).
+ If a write to the table has failed, the function attempts to
+ write to a short error message to the file. The failure is also
+ indicated in the return value.
+
+ @retval FALSE OK
+ @retval TRUE error occured
+*/
+
+bool Log_to_csv_event_handler::
+ log_general(THD *thd, time_t event_time, const char *user_host,
+ uint user_host_len, int thread_id,
+ const char *command_type, uint command_type_len,
+ const char *sql_text, uint sql_text_len,
+ CHARSET_INFO *client_cs)
+{
+ TABLE_LIST table_list;
+ TABLE *table;
+ bool result= TRUE;
+ bool need_close= FALSE;
+ bool need_pop= FALSE;
+ bool need_rnd_end= FALSE;
+ uint field_index;
+ Silence_log_table_errors error_handler;
+ Open_tables_state open_tables_backup;
+ ulonglong save_thd_options;
+ bool save_time_zone_used;
+
+ /*
+ CSV uses TIME_to_timestamp() internally if table needs to be repaired
+ which will set thd->time_zone_used
+ */
+ 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;
+
+ table_list.db= MYSQL_SCHEMA_NAME.str;
+ table_list.db_length= MYSQL_SCHEMA_NAME.length;
+
+ /*
+ 1) open_performance_schema_table generates an error of the
+ table can not be opened or is corrupted.
+ 2) "INSERT INTO general_log" can generate warning sometimes.
+
+ Suppress these warnings and errors, they can't be dealt with
+ properly anyway.
+
+ QQ: this problem needs to be studied in more detail.
+ Comment this 2 lines and run "cast.test" to see what's happening.
+ */
+ thd->push_internal_handler(& error_handler);
+ need_pop= TRUE;
+
+ if (!(table= open_performance_schema_table(thd, & table_list,
+ & open_tables_backup)))
+ goto err;
+
+ need_close= TRUE;
+
+ if (table->file->extra(HA_EXTRA_MARK_AS_LOG_TABLE) ||
+ table->file->ha_rnd_init(0))
+ goto err;
+
+ need_rnd_end= TRUE;
+
+ /* Honor next number columns if present */
+ table->next_number_field= table->found_next_number_field;
+
+ /*
+ NOTE: we do not call restore_record() here, as all fields are
+ filled by the Logger (=> no need to load default ones).
+ */
+
+ /*
+ We do not set a value for table->field[0], as it will use
+ default value (which is CURRENT_TIMESTAMP).
+ */
+
+ /* check that all columns exist */
+ if (table->s->fields < 6)
+ goto err;
+
+ DBUG_ASSERT(table->field[0]->type() == MYSQL_TYPE_TIMESTAMP);
+
+ ((Field_timestamp*) table->field[0])->store_timestamp((my_time_t)
+ event_time);
+
+ /* do a write */
+ if (table->field[1]->store(user_host, user_host_len, client_cs) ||
+ table->field[2]->store((longlong) thread_id, TRUE) ||
+ table->field[3]->store((longlong) server_id, TRUE) ||
+ table->field[4]->store(command_type, command_type_len, client_cs))
+ goto err;
+
+ /*
+ A positive return value in store() means truncation.
+ Still logging a message in the log in this case.
+ */
+ table->field[5]->flags|= FIELDFLAG_HEX_ESCAPE;
+ if (table->field[5]->store(sql_text, sql_text_len, client_cs) < 0)
+ goto err;
+
+ /* mark all fields as not null */
+ table->field[1]->set_notnull();
+ table->field[2]->set_notnull();
+ table->field[3]->set_notnull();
+ table->field[4]->set_notnull();
+ table->field[5]->set_notnull();
+
+ /* Set any extra columns to their default values */
+ for (field_index= 6 ; field_index < table->s->fields ; field_index++)
+ {
+ table->field[field_index]->set_default();
+ }
+
+ /* log table entries are not replicated */
+ if (table->file->ha_write_row(table->record[0]))
+ goto err;
+
+ result= FALSE;
+
+err:
+ if (result && !thd->killed)
+ sql_print_error("Failed to write to mysql.general_log: %s",
+ error_handler.message());
+
+ if (need_rnd_end)
+ {
+ table->file->ha_rnd_end();
+ table->file->ha_release_auto_increment();
+ }
+ if (need_pop)
+ thd->pop_internal_handler();
+ if (need_close)
+ close_performance_schema_table(thd, & open_tables_backup);
+
+ thd->options= save_thd_options;
+ thd->time_zone_used= save_time_zone_used;
+ return result;
+}
+
+
+/*
+ Log a query to the slow log table
+
+ SYNOPSIS
+ log_slow()
+ thd THD of the query
+ current_time current timestamp
+ query_start_arg command start timestamp
+ user_host the pointer to the string with user@host info
+ user_host_len length of the user_host string. this is computed once
+ and passed to all general log event handlers
+ query_time Amount of time the query took to execute (in microseconds)
+ lock_time Amount of time the query was locked (in microseconds)
+ is_command The flag, which determines, whether the sql_text is a
+ query or an administrator command (these are treated
+ differently by the old logging routines)
+ sql_text the very text of the query or administrator command
+ processed
+ sql_text_len the length of sql_text string
+
+ DESCRIPTION
+
+ Log a query to the slow log table
+
+ RETURN
+ FALSE - OK
+ TRUE - error occured
+*/
+
+bool Log_to_csv_event_handler::
+ log_slow(THD *thd, time_t current_time, time_t query_start_arg,
+ const char *user_host, uint user_host_len,
+ ulonglong query_utime, ulonglong lock_utime, bool is_command,
+ const char *sql_text, uint sql_text_len)
+{
+ TABLE_LIST table_list;
+ TABLE *table;
+ bool result= TRUE;
+ bool need_close= FALSE;
+ bool need_rnd_end= FALSE;
+ Silence_log_table_errors error_handler;
+ Open_tables_state open_tables_backup;
+ CHARSET_INFO *client_cs= thd->variables.character_set_client;
+ bool save_time_zone_used;
+ DBUG_ENTER("Log_to_csv_event_handler::log_slow");
+
+ thd->push_internal_handler(& error_handler);
+ /*
+ CSV uses TIME_to_timestamp() internally if table needs to be repaired
+ which will set thd->time_zone_used
+ */
+ 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.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)))
+ goto err;
+
+ need_close= TRUE;
+
+ if (table->file->extra(HA_EXTRA_MARK_AS_LOG_TABLE) ||
+ table->file->ha_rnd_init(0))
+ goto err;
+
+ need_rnd_end= TRUE;
+
+ /* Honor next number columns if present */
+ table->next_number_field= table->found_next_number_field;
+
+ restore_record(table, s->default_values); // Get empty record
+
+ /* check that all columns exist */
+ if (table->s->fields < 11)
+ goto err;
+
+ /* store the time and user values */
+ DBUG_ASSERT(table->field[0]->type() == MYSQL_TYPE_TIMESTAMP);
+ ((Field_timestamp*) table->field[0])->store_timestamp((my_time_t)
+ current_time);
+ if (table->field[1]->store(user_host, user_host_len, client_cs))
+ goto err;
+
+ if (query_start_arg)
+ {
+ longlong query_time= (longlong) (query_utime/1000000);
+ longlong lock_time= (longlong) (lock_utime/1000000);
+ /*
+ A TIME field can not hold the full longlong range; query_time or
+ lock_time may be truncated without warning here, if greater than
+ 839 hours (~35 days)
+ */
+ MYSQL_TIME t;
+ t.neg= 0;
+
+ /* fill in query_time field */
+ calc_time_from_sec(&t, (long) min(query_time, (longlong) TIME_MAX_VALUE_SECONDS), 0);
+ if (table->field[2]->store_time(&t, MYSQL_TIMESTAMP_TIME))
+ goto err;
+ /* lock_time */
+ calc_time_from_sec(&t, (long) min(lock_time, (longlong) TIME_MAX_VALUE_SECONDS), 0);
+ if (table->field[3]->store_time(&t, MYSQL_TIMESTAMP_TIME))
+ goto err;
+ /* rows_sent */
+ if (table->field[4]->store((longlong) thd->sent_row_count, TRUE))
+ goto err;
+ /* rows_examined */
+ if (table->field[5]->store((longlong) thd->examined_row_count, TRUE))
+ goto err;
+ }
+ else
+ {
+ table->field[2]->set_null();
+ table->field[3]->set_null();
+ table->field[4]->set_null();
+ table->field[5]->set_null();
+ }
+ /* fill database field */
+ if (thd->db)
+ {
+ if (table->field[6]->store(thd->db, thd->db_length, client_cs))
+ goto err;
+ table->field[6]->set_notnull();
+ }
+
+ if (thd->stmt_depends_on_first_successful_insert_id_in_prev_stmt)
+ {
+ if (table->
+ field[7]->store((longlong)
+ thd->first_successful_insert_id_in_prev_stmt_for_binlog,
+ TRUE))
+ goto err;
+ table->field[7]->set_notnull();
+ }
+
+ /*
+ Set value if we do an insert on autoincrement column. Note that for
+ some engines (those for which get_auto_increment() does not leave a
+ table lock until the statement ends), this is just the first value and
+ the next ones used may not be contiguous to it.
+ */
+ if (thd->auto_inc_intervals_in_cur_stmt_for_binlog.nb_elements() > 0)
+ {
+ if (table->
+ field[8]->store((longlong)
+ thd->auto_inc_intervals_in_cur_stmt_for_binlog.minimum(), TRUE))
+ goto err;
+ table->field[8]->set_notnull();
+ }
+
+ if (table->field[9]->store((longlong) server_id, TRUE))
+ goto err;
+ table->field[9]->set_notnull();
+
+ /*
+ Column sql_text.
+ A positive return value in store() means truncation.
+ Still logging a message in the log in this case.
+ */
+ if (table->field[10]->store(sql_text, sql_text_len, client_cs) < 0)
+ goto err;
+
+ /* log table entries are not replicated */
+ if (table->file->ha_write_row(table->record[0]))
+ goto err;
+
+ result= FALSE;
+
+err:
+ thd->pop_internal_handler();
+
+ if (result && !thd->killed)
+ sql_print_error("Failed to write to mysql.slow_log: %s",
+ error_handler.message());
+
+ if (need_rnd_end)
+ {
+ table->file->ha_rnd_end();
+ table->file->ha_release_auto_increment();
+ }
+ if (need_close)
+ close_performance_schema_table(thd, & open_tables_backup);
+ thd->time_zone_used= save_time_zone_used;
+ DBUG_RETURN(result);
+}
+
+int Log_to_csv_event_handler::
+ activate_log(THD *thd, uint log_table_type)
+{
+ TABLE_LIST table_list;
+ TABLE *table;
+ int result;
+ Open_tables_state 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;
+ }
+ 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;
+
+ table= open_performance_schema_table(thd, & table_list,
+ & open_tables_backup);
+ if (table)
+ {
+ result= 0;
+ close_performance_schema_table(thd, & open_tables_backup);
+ }
+ else
+ result= 1;
+
+ DBUG_RETURN(result);
+}
+
+bool Log_to_csv_event_handler::
+ log_error(enum loglevel level, const char *format, va_list args)
+{
+ /* No log table is implemented */
+ DBUG_ASSERT(0);
+ return FALSE;
+}
+
+bool Log_to_file_event_handler::
+ log_error(enum loglevel level, const char *format,
+ va_list args)
+{
+ return vprint_msg_to_log(level, format, args);
+}
+
+void Log_to_file_event_handler::init_pthread_objects()
+{
+ mysql_log.init_pthread_objects();
+ mysql_slow_log.init_pthread_objects();
+}
+
+
+/** Wrapper around MYSQL_LOG::write() for slow log. */
+
+bool Log_to_file_event_handler::
+ log_slow(THD *thd, time_t current_time, time_t query_start_arg,
+ const char *user_host, uint user_host_len,
+ ulonglong query_utime, ulonglong lock_utime, bool is_command,
+ const char *sql_text, uint sql_text_len)
+{
+ Silence_log_table_errors error_handler;
+ thd->push_internal_handler(&error_handler);
+ bool retval= mysql_slow_log.write(thd, current_time, query_start_arg,
+ user_host, user_host_len,
+ query_utime, lock_utime, is_command,
+ sql_text, sql_text_len);
+ thd->pop_internal_handler();
+ return retval;
+}
+
+
+/**
+ Wrapper around MYSQL_LOG::write() for general log. We need it since we
+ want all log event handlers to have the same signature.
+*/
+
+bool Log_to_file_event_handler::
+ log_general(THD *thd, time_t event_time, const char *user_host,
+ uint user_host_len, int thread_id,
+ const char *command_type, uint command_type_len,
+ const char *sql_text, uint sql_text_len,
+ CHARSET_INFO *client_cs)
+{
+ Silence_log_table_errors error_handler;
+ thd->push_internal_handler(&error_handler);
+ bool retval= mysql_log.write(event_time, user_host, user_host_len,
+ thread_id, command_type, command_type_len,
+ sql_text, sql_text_len);
+ thd->pop_internal_handler();
+ return retval;
+}
+
+
+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);
+
+ if (opt_log)
+ mysql_log.open_query_log(sys_var_general_log_path.value);
+
+ is_initialized= TRUE;
+ }
+
+ return FALSE;
+}
+
+
+void Log_to_file_event_handler::cleanup()
+{
+ mysql_log.cleanup();
+ mysql_slow_log.cleanup();
+}
+
+void Log_to_file_event_handler::flush()
+{
+ /* reopen log files */
+ if (opt_log)
+ mysql_log.reopen_file();
+ if (opt_slow_log)
+ mysql_slow_log.reopen_file();
+}
+
+/*
+ Log error with all enabled log event handlers
+
+ SYNOPSIS
+ error_log_print()
+
+ level The level of the error significance: NOTE,
+ WARNING or ERROR.
+ format format string for the error message
+ args list of arguments for the format string
+
+ RETURN
+ FALSE - OK
+ TRUE - error occured
+*/
+
+bool LOGGER::error_log_print(enum loglevel level, const char *format,
+ va_list args)
+{
+ bool error= FALSE;
+ Log_event_handler **current_handler;
+
+ /* currently we don't need locking here as there is no error_log table */
+ for (current_handler= error_log_handler_list ; *current_handler ;)
+ error= (*current_handler++)->log_error(level, format, args) || error;
+
+ return error;
+}
+
+
+void LOGGER::cleanup_base()
+{
+ DBUG_ASSERT(inited == 1);
+ rwlock_destroy(&LOCK_logger);
+ if (table_log_handler)
+ {
+ table_log_handler->cleanup();
+ delete table_log_handler;
+ }
+ if (file_log_handler)
+ file_log_handler->cleanup();
+}
+
+
+void LOGGER::cleanup_end()
+{
+ DBUG_ASSERT(inited == 1);
+ if (file_log_handler)
+ delete file_log_handler;
+}
+
+
+/**
+ Perform basic log initialization: create file-based log handler and
+ init error log.
+*/
+void LOGGER::init_base()
+{
+ DBUG_ASSERT(inited == 0);
+ inited= 1;
+
+ /*
+ Here we create file log handler. We don't do it for the table log handler
+ here as it cannot be created so early. The reason is THD initialization,
+ which depends on the system variables (parsed later).
+ */
+ if (!file_log_handler)
+ file_log_handler= new Log_to_file_event_handler;
+
+ /* by default we use traditional error log */
+ init_error_log(LOG_FILE);
+
+ file_log_handler->init_pthread_objects();
+ my_rwlock_init(&LOCK_logger, NULL);
+}
+
+
+void LOGGER::init_log_tables()
+{
+ if (!table_log_handler)
+ table_log_handler= new Log_to_csv_event_handler;
+
+ if (!is_log_tables_initialized &&
+ !table_log_handler->init() && !file_log_handler->init())
+ is_log_tables_initialized= TRUE;
+}
+
+
+bool LOGGER::flush_logs(THD *thd)
+{
+ int rc= 0;
+
+ /*
+ Now we lock logger, as nobody should be able to use logging routines while
+ log tables are closed
+ */
+ logger.lock_exclusive();
+
+ /* reopen log files */
+ file_log_handler->flush();
+
+ /* end of log flush */
+ logger.unlock();
+ return rc;
+}
+
+
+/*
+ Log slow query with all enabled log event handlers
+
+ SYNOPSIS
+ slow_log_print()
+
+ thd THD of the query being logged
+ query The query being logged
+ query_length The length of the query string
+ current_utime Current time in microseconds (from undefined start)
+
+ RETURN
+ FALSE OK
+ TRUE error occured
+*/
+
+bool LOGGER::slow_log_print(THD *thd, const char *query, uint query_length,
+ ulonglong current_utime)
+
+{
+ bool error= FALSE;
+ Log_event_handler **current_handler;
+ bool is_command= FALSE;
+ char user_host_buff[MAX_USER_HOST_SIZE];
+ Security_context *sctx= thd->security_ctx;
+ uint user_host_len= 0;
+ ulonglong query_utime, lock_utime;
+
+ /*
+ Print the message to the buffer if we have slow log enabled
+ */
+
+ if (*slow_log_handler_list)
+ {
+ time_t current_time;
+
+ /* do not log slow queries from replication threads */
+ if (thd->slave_thread && !opt_log_slow_slave_statements)
+ return 0;
+
+ lock_shared();
+ if (!opt_slow_log)
+ {
+ unlock();
+ return 0;
+ }
+
+ /* fill in user_host value: the format is "%s[%s] @ %s [%s]" */
+ 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_time_possible_from_micro(current_utime);
+ if (thd->start_utime)
+ {
+ query_utime= (current_utime - thd->start_utime);
+ lock_utime= (thd->utime_after_lock - thd->start_utime);
+ }
+ else
+ {
+ query_utime= lock_utime= 0;
+ }
+
+ if (!query)
+ {
+ is_command= TRUE;
+ query= command_name[thd->command].str;
+ query_length= command_name[thd->command].length;
+ }
+
+ for (current_handler= slow_log_handler_list; *current_handler ;)
+ error= (*current_handler++)->log_slow(thd, current_time, thd->start_time,
+ user_host_buff, user_host_len,
+ query_utime, lock_utime, is_command,
+ query, query_length) || error;
+
+ unlock();
+ }
+ return error;
+}
+
+bool LOGGER::general_log_write(THD *thd, enum enum_server_command command,
+ const char *query, uint query_length)
+{
+ bool error= FALSE;
+ Log_event_handler **current_handler= general_log_handler_list;
+ char user_host_buff[MAX_USER_HOST_SIZE];
+ Security_context *sctx= thd->security_ctx;
+ ulong id;
+ uint user_host_len= 0;
+ time_t current_time;
+
+ if (thd)
+ id= thd->thread_id; /* Normal thread */
+ else
+ id= 0; /* Log from connect handler */
+
+ lock_shared();
+ if (!opt_log)
+ {
+ 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_time(0);
+ while (*current_handler)
+ error|= (*current_handler++)->
+ log_general(thd, current_time, user_host_buff,
+ user_host_len, id,
+ command_name[(uint) command].str,
+ command_name[(uint) command].length,
+ query, query_length,
+ thd->variables.character_set_client) || error;
+ unlock();
+
+ return error;
+}
+
+bool LOGGER::general_log_print(THD *thd, enum enum_server_command command,
+ const char *format, va_list args)
+{
+ uint message_buff_len= 0;
+ char message_buff[MAX_LOG_BUFFER_SIZE];
+
+ /* prepare message */
+ if (format)
+ message_buff_len= my_vsnprintf(message_buff, sizeof(message_buff),
+ format, args);
+ else
+ message_buff[0]= '\0';
+
+ return general_log_write(thd, command, message_buff, message_buff_len);
+}
+
+void LOGGER::init_error_log(uint error_log_printer)
+{
+ if (error_log_printer & LOG_NONE)
+ {
+ error_log_handler_list[0]= 0;
+ return;
+ }
+
+ switch (error_log_printer) {
+ case LOG_FILE:
+ error_log_handler_list[0]= file_log_handler;
+ error_log_handler_list[1]= 0;
+ break;
+ /* these two are disabled for now */
+ case LOG_TABLE:
+ DBUG_ASSERT(0);
+ break;
+ case LOG_TABLE|LOG_FILE:
+ DBUG_ASSERT(0);
+ break;
+ }
+}
+
+void LOGGER::init_slow_log(uint slow_log_printer)
+{
+ if (slow_log_printer & LOG_NONE)
+ {
+ slow_log_handler_list[0]= 0;
+ return;
+ }
+
+ switch (slow_log_printer) {
+ case LOG_FILE:
+ slow_log_handler_list[0]= file_log_handler;
+ slow_log_handler_list[1]= 0;
+ break;
+ case LOG_TABLE:
+ slow_log_handler_list[0]= table_log_handler;
+ slow_log_handler_list[1]= 0;
+ break;
+ case LOG_TABLE|LOG_FILE:
+ slow_log_handler_list[0]= file_log_handler;
+ slow_log_handler_list[1]= table_log_handler;
+ slow_log_handler_list[2]= 0;
+ break;
+ }
+}
+
+void LOGGER::init_general_log(uint general_log_printer)
+{
+ if (general_log_printer & LOG_NONE)
+ {
+ general_log_handler_list[0]= 0;
+ return;
+ }
+
+ switch (general_log_printer) {
+ case LOG_FILE:
+ general_log_handler_list[0]= file_log_handler;
+ general_log_handler_list[1]= 0;
+ break;
+ case LOG_TABLE:
+ general_log_handler_list[0]= table_log_handler;
+ general_log_handler_list[1]= 0;
+ break;
+ case LOG_TABLE|LOG_FILE:
+ general_log_handler_list[0]= file_log_handler;
+ general_log_handler_list[1]= table_log_handler;
+ general_log_handler_list[2]= 0;
+ break;
+ }
+}
+
+
+bool LOGGER::activate_log_handler(THD* thd, uint log_type)
+{
+ MYSQL_QUERY_LOG *file_log;
+ bool res= FALSE;
+ lock_exclusive();
+ switch (log_type) {
+ case QUERY_LOG_SLOW:
+ if (!opt_slow_log)
+ {
+ file_log= file_log_handler->get_mysql_slow_log();
+
+ file_log->open_slow_log(sys_var_slow_log_path.value);
+ if (table_log_handler->activate_log(thd, QUERY_LOG_SLOW))
+ {
+ /* Error printed by open table in activate_log() */
+ res= TRUE;
+ file_log->close(0);
+ }
+ else
+ {
+ init_slow_log(log_output_options);
+ opt_slow_log= TRUE;
+ }
+ }
+ break;
+ case QUERY_LOG_GENERAL:
+ if (!opt_log)
+ {
+ file_log= file_log_handler->get_mysql_log();
+
+ file_log->open_query_log(sys_var_general_log_path.value);
+ if (table_log_handler->activate_log(thd, QUERY_LOG_GENERAL))
+ {
+ /* Error printed by open table in activate_log() */
+ res= TRUE;
+ file_log->close(0);
+ }
+ else
+ {
+ init_general_log(log_output_options);
+ opt_log= TRUE;
+ }
+ }
+ break;
+ default:
+ DBUG_ASSERT(0);
+ }
+ unlock();
+ return res;
+}
+
+
+void LOGGER::deactivate_log_handler(THD *thd, uint log_type)
+{
+ my_bool *tmp_opt= 0;
+ MYSQL_LOG *file_log;
+
+ switch (log_type) {
+ case QUERY_LOG_SLOW:
+ tmp_opt= &opt_slow_log;
+ file_log= file_log_handler->get_mysql_slow_log();
+ break;
+ case QUERY_LOG_GENERAL:
+ tmp_opt= &opt_log;
+ file_log= file_log_handler->get_mysql_log();
+ break;
+ default:
+ assert(0); // Impossible
+ }
+
+ if (!(*tmp_opt))
+ return;
+
+ lock_exclusive();
+ file_log->close(0);
+ *tmp_opt= FALSE;
+ unlock();
+}
+
+
+/* the parameters are unused for the log tables */
+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)
+{
+ /* error log table is not supported yet */
+ DBUG_ASSERT(error_log_printer < LOG_TABLE);
+
+ lock_exclusive();
+
+ if ((slow_log_printer & LOG_TABLE || general_log_printer & LOG_TABLE) &&
+ !is_log_tables_initialized)
+ {
+ slow_log_printer= (slow_log_printer & ~LOG_TABLE) | LOG_FILE;
+ general_log_printer= (general_log_printer & ~LOG_TABLE) | LOG_FILE;
+
+ sql_print_error("Failed to initialize log tables. "
+ "Falling back to the old-fashioned logs");
+ }
+
+ init_error_log(error_log_printer);
+ init_slow_log(slow_log_printer);
+ init_general_log(general_log_printer);
+
+ unlock();
+
+ return 0;
+}
+
+
+ /*
+ Save position of binary log transaction cache.
+
+ SYNPOSIS
+ binlog_trans_log_savepos()
+
+ thd The thread to take the binlog data from
+ pos Pointer to variable where the position will be stored
+
+ DESCRIPTION
+
+ Save the current position in the binary log transaction cache into
+ the variable pointed to by 'pos'
+ */
+
+static void
+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);
+ DBUG_ASSERT(mysql_bin_log.is_open());
+ *pos= trx_data->position();
+ DBUG_PRINT("return", ("*pos: %lu", (ulong) *pos));
+ DBUG_VOID_RETURN;
+}
+
+
+/*
+ Truncate the binary log transaction cache.
+
+ SYNPOSIS
+ binlog_trans_log_truncate()
+
+ thd The thread to take the binlog data from
+ pos Position to truncate to
+
+ DESCRIPTION
+
+ Truncate the binary log to the given position. Will not change
+ anything else.
+
+ */
+static void
+binlog_trans_log_truncate(THD *thd, my_off_t pos)
+{
+ DBUG_ENTER("binlog_trans_log_truncate");
+ DBUG_PRINT("enter", ("pos: %lu", (ulong) pos));
+
+ DBUG_ASSERT(thd_get_ha_data(thd, binlog_hton) != NULL);
+ /* 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);
+ DBUG_VOID_RETURN;
+}
+
+
+/*
this function is mostly a placeholder.
- conceptually, binlog initialization (now mostly done in MYSQL_LOG::open)
+ conceptually, binlog initialization (now mostly done in MYSQL_BIN_LOG::open)
should be moved here.
*/
-bool binlog_init()
+int binlog_init(void *p)
{
- return !opt_bin_log;
+ binlog_hton= (handlerton *)p;
+ binlog_hton->state=opt_bin_log ? SHOW_OPTION_YES : SHOW_OPTION_NO;
+ binlog_hton->db_type=DB_TYPE_BINLOG;
+ binlog_hton->savepoint_offset= sizeof(my_off_t);
+ binlog_hton->close_connection= binlog_close_connection;
+ binlog_hton->savepoint_set= binlog_savepoint_set;
+ binlog_hton->savepoint_rollback= binlog_savepoint_rollback;
+ binlog_hton->commit= binlog_commit;
+ binlog_hton->rollback= binlog_rollback;
+ binlog_hton->prepare= binlog_prepare;
+ binlog_hton->flags= HTON_NOT_USER_SELECTABLE | HTON_HIDDEN;
+ return 0;
}
-static int binlog_close_connection(THD *thd)
+static int binlog_close_connection(handlerton *hton, THD *thd)
{
- IO_CACHE *trans_log= (IO_CACHE*)thd->ha_data[binlog_hton.slot];
- DBUG_ASSERT(!my_b_tell(trans_log));
- close_cached_file(trans_log);
- my_free((gptr)trans_log, MYF(0));
+ binlog_trx_data *const trx_data=
+ (binlog_trx_data*) thd_get_ha_data(thd, binlog_hton);
+ DBUG_ASSERT(trx_data->empty());
+ thd_set_ha_data(thd, binlog_hton, NULL);
+ trx_data->~binlog_trx_data();
+ my_free((uchar*)trx_data, MYF(0));
return 0;
}
-static int binlog_end_trans(THD *thd, IO_CACHE *trans_log, Log_event *end_ev)
+/*
+ End a transaction.
+
+ SYNOPSIS
+ binlog_end_trans()
+
+ thd The thread whose transaction should be ended
+ trx_data Pointer to the transaction data to use
+ end_ev The end event to use, or NULL
+ all True if the entire transaction should be ended, false if
+ only the statement transaction should be ended.
+
+ DESCRIPTION
+
+ End the currently open transaction. The transaction can be either
+ a real transaction (if 'all' is true) or a statement transaction
+ (if 'all' is false).
+
+ If 'end_ev' is NULL, the transaction is a rollback of only
+ transactional tables, so 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).
+ */
+static int
+binlog_end_trans(THD *thd, binlog_trx_data *trx_data,
+ Log_event *end_ev, bool all)
{
- int error=0;
DBUG_ENTER("binlog_end_trans");
+ int error=0;
+ IO_CACHE *trans_log= &trx_data->trans_log;
+ DBUG_PRINT("enter", ("transaction: %s end_ev: 0x%lx",
+ all ? "all" : "stmt", (long) end_ev));
+ DBUG_PRINT("info", ("thd->options={ %s%s}",
+ FLAGSTR(thd->options, OPTION_NOT_AUTOCOMMIT),
+ FLAGSTR(thd->options, OPTION_BEGIN)));
- /* NULL denotes ROLLBACK with nothing to replicate */
+ /*
+ NULL denotes ROLLBACK with nothing to replicate: i.e., rollback of
+ only transactional tables. If the transaction contain changes to
+ any non-transactiona tables, we need write the transaction and log
+ a ROLLBACK last.
+ */
if (end_ev != NULL)
- error= mysql_bin_log.write(thd, trans_log, end_ev);
+ {
+ thd->binlog_flush_pending_rows_event(TRUE);
+ /*
+ 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(thd, &trx_data->trans_log, end_ev);
+ trx_data->reset();
- statistic_increment(binlog_cache_use, &LOCK_status);
- if (trans_log->disk_writes != 0)
+ /*
+ We need to step the table map version after writing the
+ transaction cache to disk.
+ */
+ mysql_bin_log.update_table_map_version();
+ statistic_increment(binlog_cache_use, &LOCK_status);
+ if (trans_log->disk_writes != 0)
+ {
+ statistic_increment(binlog_cache_disk_use, &LOCK_status);
+ trans_log->disk_writes= 0;
+ }
+ }
+ else
{
- statistic_increment(binlog_cache_disk_use, &LOCK_status);
- trans_log->disk_writes= 0;
+ /*
+ 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)))
+ trx_data->reset();
+ else // ...statement
+ trx_data->truncate(trx_data->before_stmt_pos);
+
+ /*
+ We need to step the table map version on a rollback to ensure
+ that a new table map event is generated instead of the one that
+ was written to the thrown-away transaction cache.
+ */
+ mysql_bin_log.update_table_map_version();
}
- reinit_io_cache(trans_log, WRITE_CACHE, (my_off_t) 0, 0, 1); // cannot fail
- trans_log->end_of_file= max_binlog_cache_size;
+
+ DBUG_ASSERT(thd->binlog_get_pending_rows_event() == NULL);
DBUG_RETURN(error);
}
-static int binlog_prepare(THD *thd, bool all)
+static int binlog_prepare(handlerton *hton, THD *thd, bool all)
{
/*
do nothing.
just pretend we can do 2pc, so that MySQL won't
switch to 1pc.
- real work will be done in MYSQL_LOG::log_xid()
+ real work will be done in MYSQL_BIN_LOG::log_xid()
*/
return 0;
}
@@ -128,42 +1456,57 @@ static int binlog_prepare(THD *thd, bool all)
It has the responsibility to flush the transaction cache to the
binlog file on commits.
+ @param hton The binlog handlerton.
@param thd The client thread that executes the transaction.
- @param all true if this is the last statement before a COMMIT
- statement; false if either this is a statement in a
- transaction but not the last, or if this is a statement
- not inside a BEGIN block and autocommit is on.
+ @param all This is @c true if this is a real transaction commit, and
+ @false otherwise.
@see handlerton::commit
*/
-static int binlog_commit(THD *thd, bool all)
+static int binlog_commit(handlerton *hton, THD *thd, bool all)
{
- IO_CACHE *trans_log= (IO_CACHE*)thd->ha_data[binlog_hton.slot];
+ int error= 0;
DBUG_ENTER("binlog_commit");
- DBUG_ASSERT(
- (all || !(thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN))));
+ binlog_trx_data *const trx_data=
+ (binlog_trx_data*) thd_get_ha_data(thd, binlog_hton);
- if (my_b_tell(trans_log) == 0)
+ if (trx_data->empty())
{
- // we're here because trans_log was flushed in MYSQL_LOG::log_xid()
+ // we're here because trans_log was flushed in MYSQL_BIN_LOG::log_xid()
+ trx_data->reset();
DBUG_RETURN(0);
}
+
/*
- Write commit event if at least one of the following holds:
- - the user sends an explicit COMMIT; or
- - the autocommit flag is on, and we are not inside a BEGIN.
- However, if the user has not sent an explicit COMMIT, and we are
- either inside a BEGIN or run with autocommit off, then this is not
- the end of a transaction and we should not write a commit event.
+ 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 statement
*/
- if (all || !(thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)))
+ ulonglong const in_transaction=
+ thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN);
+ DBUG_PRINT("debug",
+ ("all: %d, empty: %s, in_transaction: %s, all.modified_non_trans_table: %s, stmt.modified_non_trans_table: %s",
+ all,
+ YESNO(trx_data->empty()),
+ YESNO(in_transaction),
+ YESNO(thd->transaction.all.modified_non_trans_table),
+ YESNO(thd->transaction.stmt.modified_non_trans_table)));
+ if (!in_transaction || all)
{
Query_log_event qev(thd, STRING_WITH_LEN("COMMIT"), TRUE, FALSE);
qev.error_code= 0; // see comment in MYSQL_LOG::write(THD, IO_CACHE)
- DBUG_RETURN(binlog_end_trans(thd, trans_log, &qev));
+ error= binlog_end_trans(thd, trx_data, &qev, all);
+ goto end;
}
- else
- DBUG_RETURN(binlog_end_trans(thd, trans_log, &invisible_commit));
+
+end:
+ if (!all)
+ trx_data->before_stmt_pos = MY_OFF_T_UNDEF; // part of the stmt commit
+ DBUG_RETURN(error);
}
/**
@@ -174,46 +1517,66 @@ static int binlog_commit(THD *thd, bool all)
binlog file. However, if the transaction does not involve
non-transactional tables, nothing needs to be logged.
+ @param hton The binlog handlerton.
@param thd The client thread that executes the transaction.
- @param all true if this is the last statement before a COMMIT
- statement; false if either this is a statement in a
- transaction but not the last, or if this is a statement
- not inside a BEGIN block and autocommit is on.
+ @param all This is @c true if this is a real transaction rollback, and
+ @false otherwise.
@see handlerton::rollback
*/
-static int binlog_rollback(THD *thd, bool all)
+static int binlog_rollback(handlerton *hton, THD *thd, bool all)
{
- int error=0;
- IO_CACHE *trans_log= (IO_CACHE*)thd->ha_data[binlog_hton.slot];
DBUG_ENTER("binlog_rollback");
- /*
- First assert is guaranteed - see trans_register_ha() call below.
- The second must be true. If it is not, we're registering
- unnecessary, doing extra work. The cause should be found and eliminated
- */
- DBUG_ASSERT(all || !(thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)));
- DBUG_ASSERT(my_b_tell(trans_log));
- /*
- Update the binary log with a BEGIN/ROLLBACK block if we have
- cached some queries and we updated some non-transactional
- table. Such cases should be rare (updating a
- non-transactional table inside a transaction...)
- */
- if (unlikely(thd->transaction.all.modified_non_trans_table))
+ 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);
+ }
+
+ DBUG_PRINT("debug", ("all: %s, all.modified_non_trans_table: %s, stmt.modified_non_trans_table: %s",
+ YESNO(all),
+ YESNO(thd->transaction.all.modified_non_trans_table),
+ YESNO(thd->transaction.stmt.modified_non_trans_table)));
+ if (all && thd->transaction.all.modified_non_trans_table ||
+ !all && thd->transaction.stmt.modified_non_trans_table ||
+ (thd->options & OPTION_KEEP_LOG))
{
+ /*
+ We write the transaction cache with a rollback last if we have
+ modified any non-transactional table. We do this even if we are
+ committing a single statement that has modified a
+ non-transactional table since it can have modified a
+ transactional table in that statement as well, which needs to be
+ rolled back on the slave.
+ */
Query_log_event qev(thd, STRING_WITH_LEN("ROLLBACK"), TRUE, FALSE);
qev.error_code= 0; // see comment in MYSQL_LOG::write(THD, IO_CACHE)
- error= binlog_end_trans(thd, trans_log, &qev);
+ error= binlog_end_trans(thd, trx_data, &qev, all);
}
- else
- error= binlog_end_trans(thd, trans_log, 0);
+ else if (all && !thd->transaction.all.modified_non_trans_table ||
+ !all && !thd->transaction.stmt.modified_non_trans_table)
+ {
+ /*
+ If we have modified only transactional tables, we can truncate
+ the transaction cache without writing anything to the binary
+ log.
+ */
+ error= binlog_end_trans(thd, trx_data, 0, all);
+ }
+ if (!all)
+ trx_data->before_stmt_pos = MY_OFF_T_UNDEF; // part of the stmt rollback
DBUG_RETURN(error);
}
-/*
- NOTE: how do we handle this (unlikely but legal) case:
- [transaction] + [update to non-trans table] + [rollback to savepoint] ?
+/**
+ @note
+ How do we handle this (unlikely but legal) case:
+ @verbatim
+ [transaction] + [update to non-trans table] + [rollback to savepoint] ?
+ @endverbatim
The problem occurs when a savepoint is before the update to the
non-transactional table. Then when there's a rollback to the savepoint, if we
simply truncate the binlog cache, we lose the part of the binlog cache where
@@ -232,44 +1595,47 @@ static int binlog_rollback(THD *thd, bool all)
that case there is no need to have it in the binlog).
*/
-static int binlog_savepoint_set(THD *thd, void *sv)
+static int binlog_savepoint_set(handlerton *hton, THD *thd, void *sv)
{
- IO_CACHE *trans_log= (IO_CACHE*)thd->ha_data[binlog_hton.slot];
DBUG_ENTER("binlog_savepoint_set");
- DBUG_ASSERT(my_b_tell(trans_log));
- *(my_off_t *)sv= my_b_tell(trans_log);
+ binlog_trans_log_savepos(thd, (my_off_t*) sv);
/* Write it to the binary log */
- Query_log_event qinfo(thd, thd->query, thd->query_length, TRUE, FALSE);
- DBUG_RETURN(mysql_bin_log.write(&qinfo));
+
+ int const error=
+ thd->binlog_query(THD::STMT_QUERY_TYPE,
+ thd->query, thd->query_length, TRUE, FALSE);
+ DBUG_RETURN(error);
}
-static int binlog_savepoint_rollback(THD *thd, void *sv)
+static int binlog_savepoint_rollback(handlerton *hton, THD *thd, void *sv)
{
- IO_CACHE *trans_log= (IO_CACHE*)thd->ha_data[binlog_hton.slot];
DBUG_ENTER("binlog_savepoint_rollback");
- DBUG_ASSERT(my_b_tell(trans_log));
/*
Write ROLLBACK TO SAVEPOINT to the binlog cache if we have updated some
non-transactional table. Otherwise, truncate the binlog cache starting
from the SAVEPOINT command.
*/
- if (unlikely(thd->transaction.all.modified_non_trans_table))
+ if (unlikely(thd->transaction.all.modified_non_trans_table ||
+ (thd->options & OPTION_KEEP_LOG)))
{
- Query_log_event qinfo(thd, thd->query, thd->query_length, TRUE, FALSE);
- DBUG_RETURN(mysql_bin_log.write(&qinfo));
+ int error=
+ thd->binlog_query(THD::STMT_QUERY_TYPE,
+ thd->query, thd->query_length, TRUE, FALSE);
+ DBUG_RETURN(error);
}
- reinit_io_cache(trans_log, WRITE_CACHE, *(my_off_t *)sv, 0, 0);
+ binlog_trans_log_truncate(thd, *(my_off_t*)sv);
DBUG_RETURN(0);
}
+
int check_binlog_magic(IO_CACHE* log, const char** errmsg)
{
char magic[4];
DBUG_ASSERT(my_b_tell(log) == 0);
- if (my_b_read(log, (byte*) magic, sizeof(magic)))
+ if (my_b_read(log, (uchar*) magic, sizeof(magic)))
{
*errmsg = "I/O error reading the header from the binary log";
sql_print_error("%s, errno=%d, io cache code=%d", *errmsg, my_errno,
@@ -284,6 +1650,7 @@ int check_binlog_magic(IO_CACHE* log, const char** errmsg)
return 0;
}
+
File open_binlog(IO_CACHE *log, const char *log_file_name, const char **errmsg)
{
File file;
@@ -356,11 +1723,14 @@ static void setup_windows_event_source()
#endif /* __NT__ */
-/****************************************************************************
-** Find a uniq filename for 'filename.#'.
-** Set # to a number as low as possible
-** returns != 0 if not possible to get uniq filename
-****************************************************************************/
+/**
+ Find a unique filename for 'filename.#'.
+
+ Set '#' to a number as low as possible.
+
+ @return
+ nonzero if not possible to get unique filename
+*/
static int find_uniq_filename(char *name)
{
@@ -370,15 +1740,16 @@ static int find_uniq_filename(char *name)
struct st_my_dir *dir_info;
reg1 struct fileinfo *file_info;
ulong max_found=0;
-
+ size_t buf_length, length;
+ char *start, *end;
DBUG_ENTER("find_uniq_filename");
- uint length = dirname_part(buff,name);
- char *start = name + length;
- char *end = strend(start);
+ length= dirname_part(buff, name, &buf_length);
+ start= name + length;
+ end= strend(start);
*end='.';
- length= (uint) (end-start+1);
+ length= (size_t) (end-start+1);
if (!(dir_info = my_dir(buff,MYF(MY_DONT_SORT))))
{ // This shouldn't happen
@@ -388,7 +1759,7 @@ static int find_uniq_filename(char *name)
file_info= dir_info->dir_entry;
for (i=dir_info->number_off_files ; i-- ; file_info++)
{
- if (bcmp(file_info->name,start,length) == 0 &&
+ if (bcmp((uchar*) file_info->name, (uchar*) start, length) == 0 &&
test_if_number(file_info->name+length, &number,0))
{
set_if_bigger(max_found,(ulong) number);
@@ -402,11 +1773,120 @@ static int find_uniq_filename(char *name)
}
+void MYSQL_LOG::init(enum_log_type log_type_arg,
+ enum cache_type io_cache_type_arg)
+{
+ DBUG_ENTER("MYSQL_LOG::init");
+ log_type= log_type_arg;
+ io_cache_type= io_cache_type_arg;
+ DBUG_PRINT("info",("log_type: %d", log_type));
+ DBUG_VOID_RETURN;
+}
+
+
+/*
+ Open a (new) log file.
+
+ SYNOPSIS
+ open()
+
+ log_name The name of the log to open
+ log_type_arg The type of the log. E.g. LOG_NORMAL
+ new_name The new name for the logfile. This is only needed
+ when the method is used to open the binlog file.
+ io_cache_type_arg The type of the IO_CACHE to use for this log file
+
+ DESCRIPTION
+ Open the logfile, init IO_CACHE and write startup messages
+ (in case of general and slow query logs).
+
+ RETURN VALUES
+ 0 ok
+ 1 error
+*/
+
+bool MYSQL_LOG::open(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];
+ File file= -1;
+ int open_flags= O_CREAT | O_BINARY;
+ DBUG_ENTER("MYSQL_LOG::open");
+ DBUG_PRINT("enter", ("log_type: %d", (int) log_type_arg));
+
+ write_error= 0;
+
+ init(log_type_arg, io_cache_type_arg);
+
+ if (!(name= my_strdup(log_name, MYF(MY_WME))))
+ {
+ name= (char *)log_name; // for the error message
+ goto err;
+ }
+
+ if (new_name)
+ strmov(log_file_name, new_name);
+ else if (generate_new_name(log_file_name, name))
+ goto err;
+
+ if (io_cache_type == SEQ_READ_APPEND)
+ open_flags |= O_RDWR | O_APPEND;
+ else
+ open_flags |= O_WRONLY | (log_type == LOG_BIN ? 0 : O_APPEND);
+
+ db[0]= 0;
+
+ if ((file= my_open(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,
+ MYF(MY_WME | MY_NABP |
+ ((log_type == LOG_BIN) ? MY_WAIT_IF_FULL : 0))))
+ goto err;
+
+ if (log_type == LOG_NORMAL)
+ {
+ char *end;
+ int len=my_snprintf(buff, sizeof(buff), "%s, Version: %s (%s). "
+#ifdef EMBEDDED_LIBRARY
+ "embedded library\n",
+ my_progname, server_version, MYSQL_COMPILATION_COMMENT
+#elif __NT__
+ "started with:\nTCP Port: %d, Named Pipe: %s\n",
+ my_progname, server_version, MYSQL_COMPILATION_COMMENT,
+ mysqld_port, mysqld_unix_port
+#else
+ "started with:\nTcp port: %d Unix socket: %s\n",
+ my_progname, server_version, MYSQL_COMPILATION_COMMENT,
+ mysqld_port, mysqld_unix_port
+#endif
+ );
+ end= strnmov(buff + len, "Time Id Command Argument\n",
+ sizeof(buff) - len);
+ if (my_b_write(&log_file, (uchar*) buff, (uint) (end-buff)) ||
+ flush_io_cache(&log_file))
+ goto err;
+ }
+
+ log_state= LOG_OPENED;
+ DBUG_RETURN(0);
+
+err:
+ sql_print_error("Could not use %s for logging (error %d). \
+Turning logging off for the whole duration of the MySQL server process. \
+To turn it on again: fix the cause, \
+shutdown the MySQL server and restart it.", name, errno);
+ if (file >= 0)
+ my_close(file, MYF(0));
+ end_io_cache(&log_file);
+ safeFree(name);
+ log_state= LOG_CLOSED;
+ DBUG_RETURN(1);
+}
+
MYSQL_LOG::MYSQL_LOG()
- :bytes_written(0), last_time(0), query_start(0), name(0),
- prepared_xids(0), log_type(LOG_CLOSED), file_id(1), open_count(1),
- write_error(FALSE), inited(FALSE), need_start_event(TRUE),
- description_event_for_exec(0), description_event_for_queue(0)
+ : name(0), write_error(FALSE), inited(FALSE), log_type(LOG_UNKNOWN),
+ log_state(LOG_CLOSED)
{
/*
We don't want to initialize LOCK_Log here as such initialization depends on
@@ -414,13 +1894,57 @@ MYSQL_LOG::MYSQL_LOG()
called only in main(). Doing initialization here would make it happen
before main().
*/
- index_file_name[0] = 0;
- bzero((char*) &log_file,sizeof(log_file));
- bzero((char*) &index_file, sizeof(index_file));
- bzero((char*) &purge_temp, sizeof(purge_temp));
+ bzero((char*) &log_file, sizeof(log_file));
}
-/* this is called only once */
+void MYSQL_LOG::init_pthread_objects()
+{
+ DBUG_ASSERT(inited == 0);
+ inited= 1;
+ (void) pthread_mutex_init(&LOCK_log, MY_MUTEX_INIT_SLOW);
+}
+
+/*
+ Close the log file
+
+ 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.
+
+ NOTES
+ One can do an open on the object at once after doing a close.
+ The internal structures are not freed until cleanup() is called
+*/
+
+void MYSQL_LOG::close(uint exiting)
+{ // One can't set log_type here!
+ DBUG_ENTER("MYSQL_LOG::close");
+ DBUG_PRINT("enter",("exiting: %d", (int) exiting));
+ if (log_state == LOG_OPENED)
+ {
+ end_io_cache(&log_file);
+
+ if (my_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)
+ {
+ write_error= 1;
+ sql_print_error(ER(ER_ERROR_ON_WRITE), name, errno);
+ }
+ }
+
+ log_state= (exiting & LOG_CLOSE_TO_BE_OPENED) ? LOG_TO_BE_OPENED : LOG_CLOSED;
+ safeFree(name);
+ DBUG_VOID_RETURN;
+}
+
+/** This is called only once. */
void MYSQL_LOG::cleanup()
{
@@ -428,12 +1952,8 @@ void MYSQL_LOG::cleanup()
if (inited)
{
inited= 0;
- 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);
+ close(0);
}
DBUG_VOID_RETURN;
}
@@ -441,8 +1961,8 @@ void MYSQL_LOG::cleanup()
int MYSQL_LOG::generate_new_name(char *new_name, const char *log_name)
{
- fn_format(new_name,log_name,mysql_data_home,"",4);
- if (log_type != LOG_NORMAL)
+ fn_format(new_name, log_name, mysql_data_home, "", 4);
+ if (log_type == LOG_BIN)
{
if (!fn_ext(log_name)[0])
{
@@ -457,53 +1977,386 @@ int MYSQL_LOG::generate_new_name(char *new_name, const char *log_name)
}
-void MYSQL_LOG::init(enum_log_type log_type_arg,
- enum cache_type io_cache_type_arg,
- bool no_auto_events_arg,
- ulong max_size_arg)
+/*
+ Reopen the log file
+
+ SYNOPSIS
+ reopen_file()
+
+ DESCRIPTION
+ Reopen the log file. The method is used during FLUSH LOGS
+ and locks LOCK_log mutex
+*/
+
+
+void MYSQL_QUERY_LOG::reopen_file()
{
- DBUG_ENTER("MYSQL_LOG::init");
- log_type = log_type_arg;
- io_cache_type = io_cache_type_arg;
- no_auto_events = no_auto_events_arg;
- max_size=max_size_arg;
- DBUG_PRINT("info",("log_type: %d max_size: %lu", log_type, max_size));
+ char *save_name;
+
+ DBUG_ENTER("MYSQL_LOG::reopen_file");
+ if (!is_open())
+ {
+ DBUG_PRINT("info",("log is closed"));
+ DBUG_VOID_RETURN;
+ }
+
+ pthread_mutex_lock(&LOCK_log);
+
+ save_name= name;
+ name= 0; // Don't free name
+ close(LOG_CLOSE_TO_BE_OPENED);
+
+ /*
+ 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));
+
+ pthread_mutex_unlock(&LOCK_log);
+
DBUG_VOID_RETURN;
}
-void MYSQL_LOG::init_pthread_objects()
+/*
+ Write a command to traditional general log file
+
+ SYNOPSIS
+ write()
+
+ event_time command start timestamp
+ user_host the pointer to the string with user@host info
+ user_host_len length of the user_host string. this is computed once
+ and passed to all general log event handlers
+ thread_id Id of the thread, issued a query
+ command_type the type of the command being logged
+ command_type_len the length of the string above
+ sql_text the very text of the query being executed
+ sql_text_len the length of sql_text string
+
+ DESCRIPTION
+
+ Log given command to to normal (not rotable) log file
+
+ RETURN
+ FASE - OK
+ TRUE - error occured
+*/
+
+bool MYSQL_QUERY_LOG::write(time_t event_time, const char *user_host,
+ uint user_host_len, int thread_id,
+ const char *command_type, uint command_type_len,
+ const char *sql_text, uint sql_text_len)
{
- DBUG_ASSERT(inited == 0);
- inited= 1;
- (void) pthread_mutex_init(&LOCK_log,MY_MUTEX_INIT_SLOW);
- (void) pthread_mutex_init(&LOCK_index, MY_MUTEX_INIT_SLOW);
- (void) pthread_cond_init(&update_cond, 0);
+ char buff[32];
+ uint length= 0;
+ char local_time_buff[MAX_TIME_SIZE];
+ struct tm start;
+ uint time_buff_len= 0;
+
+ (void) pthread_mutex_lock(&LOCK_log);
+
+ /* Test if someone closed between the is_open test and lock */
+ if (is_open())
+ {
+ /* Note that my_b_write() assumes it knows the length for this */
+ if (event_time != last_time)
+ {
+ last_time= event_time;
+
+ localtime_r(&event_time, &start);
+
+ time_buff_len= my_snprintf(local_time_buff, MAX_TIME_SIZE,
+ "%02d%02d%02d %2d:%02d:%02d",
+ start.tm_year % 100, start.tm_mon + 1,
+ start.tm_mday, start.tm_hour,
+ start.tm_min, start.tm_sec);
+
+ if (my_b_write(&log_file, (uchar*) local_time_buff, time_buff_len))
+ goto err;
+ }
+ else
+ if (my_b_write(&log_file, (uchar*) "\t\t" ,2) < 0)
+ goto err;
+
+ /* command_type, thread_id */
+ length= my_snprintf(buff, 32, "%5ld ", (long) thread_id);
+
+ if (my_b_write(&log_file, (uchar*) buff, length))
+ goto err;
+
+ if (my_b_write(&log_file, (uchar*) command_type, command_type_len))
+ goto err;
+
+ if (my_b_write(&log_file, (uchar*) "\t", 1))
+ goto err;
+
+ /* sql_text */
+ if (my_b_write(&log_file, (uchar*) sql_text, sql_text_len))
+ goto err;
+
+ if (my_b_write(&log_file, (uchar*) "\n", 1) ||
+ flush_io_cache(&log_file))
+ goto err;
+ }
+
+ (void) pthread_mutex_unlock(&LOCK_log);
+ return FALSE;
+err:
+
+ if (!write_error)
+ {
+ write_error= 1;
+ sql_print_error(ER(ER_ERROR_ON_WRITE), name, errno);
+ }
+ (void) pthread_mutex_unlock(&LOCK_log);
+ return TRUE;
}
+
+/*
+ Log a query to the traditional slow log file
+
+ SYNOPSIS
+ write()
+
+ thd THD of the query
+ current_time current timestamp
+ query_start_arg command start timestamp
+ user_host the pointer to the string with user@host info
+ user_host_len length of the user_host string. this is computed once
+ and passed to all general log event handlers
+ query_utime Amount of time the query took to execute (in microseconds)
+ lock_utime Amount of time the query was locked (in microseconds)
+ is_command The flag, which determines, whether the sql_text is a
+ query or an administrator command.
+ sql_text the very text of the query or administrator command
+ processed
+ sql_text_len the length of sql_text string
+
+ DESCRIPTION
+
+ Log a query to the slow log file.
+
+ RETURN
+ FALSE - OK
+ TRUE - error occured
+*/
+
+bool MYSQL_QUERY_LOG::write(THD *thd, time_t current_time,
+ time_t query_start_arg, const char *user_host,
+ uint user_host_len, ulonglong query_utime,
+ ulonglong lock_utime, bool is_command,
+ const char *sql_text, uint sql_text_len)
+{
+ bool error= 0;
+ DBUG_ENTER("MYSQL_QUERY_LOG::write");
+
+ (void) pthread_mutex_lock(&LOCK_log);
+
+ if (!is_open())
+ {
+ (void) pthread_mutex_unlock(&LOCK_log);
+ DBUG_RETURN(0);
+ }
+
+ if (is_open())
+ { // Safety agains reopen
+ int tmp_errno= 0;
+ char buff[80], *end;
+ char query_time_buff[22+7], lock_time_buff[22+7];
+ uint buff_len;
+ end= buff;
+
+ if (!(specialflag & SPECIAL_SHORT_LOG_FORMAT))
+ {
+ if (current_time != last_time)
+ {
+ last_time= current_time;
+ struct tm start;
+ localtime_r(&current_time, &start);
+
+ buff_len= my_snprintf(buff, sizeof buff,
+ "# Time: %02d%02d%02d %2d:%02d:%02d\n",
+ start.tm_year % 100, start.tm_mon + 1,
+ start.tm_mday, start.tm_hour,
+ start.tm_min, start.tm_sec);
+
+ /* Note that my_b_write() assumes it knows the length for this */
+ if (my_b_write(&log_file, (uchar*) buff, buff_len))
+ tmp_errno= errno;
+ }
+ const uchar uh[]= "# User@Host: ";
+ if (my_b_write(&log_file, uh, sizeof(uh) - 1))
+ tmp_errno= errno;
+ if (my_b_write(&log_file, (uchar*) user_host, user_host_len))
+ tmp_errno= errno;
+ if (my_b_write(&log_file, (uchar*) "\n", 1))
+ tmp_errno= errno;
+ }
+ /* For slow query log */
+ sprintf(query_time_buff, "%.6f", ulonglong2double(query_utime)/1000000.0);
+ sprintf(lock_time_buff, "%.6f", ulonglong2double(lock_utime)/1000000.0);
+ if (my_b_printf(&log_file,
+ "# Query_time: %s Lock_time: %s"
+ " Rows_sent: %lu Rows_examined: %lu\n",
+ query_time_buff, lock_time_buff,
+ (ulong) thd->sent_row_count,
+ (ulong) thd->examined_row_count) == (uint) -1)
+ tmp_errno= errno;
+ if (thd->db && strcmp(thd->db, db))
+ { // Database changed
+ if (my_b_printf(&log_file,"use %s;\n",thd->db) == (uint) -1)
+ tmp_errno= errno;
+ strmov(db,thd->db);
+ }
+ if (thd->stmt_depends_on_first_successful_insert_id_in_prev_stmt)
+ {
+ end=strmov(end, ",last_insert_id=");
+ end=longlong10_to_str((longlong)
+ thd->first_successful_insert_id_in_prev_stmt_for_binlog,
+ end, -10);
+ }
+ // Save value if we do an insert.
+ if (thd->auto_inc_intervals_in_cur_stmt_for_binlog.nb_elements() > 0)
+ {
+ if (!(specialflag & SPECIAL_SHORT_LOG_FORMAT))
+ {
+ end=strmov(end,",insert_id=");
+ end=longlong10_to_str((longlong)
+ thd->auto_inc_intervals_in_cur_stmt_for_binlog.minimum(),
+ end, -10);
+ }
+ }
+
+ /*
+ This info used to show up randomly, depending on whether the query
+ checked the query start time or not. now we always write current
+ timestamp to the slow log
+ */
+ end= strmov(end, ",timestamp=");
+ end= int10_to_str((long) current_time, end, 10);
+
+ if (end != buff)
+ {
+ *end++=';';
+ *end='\n';
+ if (my_b_write(&log_file, (uchar*) "SET ", 4) ||
+ my_b_write(&log_file, (uchar*) buff + 1, (uint) (end-buff)))
+ tmp_errno= errno;
+ }
+ if (is_command)
+ {
+ end= strxmov(buff, "# administrator command: ", NullS);
+ buff_len= (ulong) (end - buff);
+ my_b_write(&log_file, (uchar*) buff, buff_len);
+ }
+ if (my_b_write(&log_file, (uchar*) sql_text, sql_text_len) ||
+ my_b_write(&log_file, (uchar*) ";\n",2) ||
+ flush_io_cache(&log_file))
+ tmp_errno= errno;
+ if (tmp_errno)
+ {
+ error= 1;
+ if (! write_error)
+ {
+ write_error= 1;
+ sql_print_error(ER(ER_ERROR_ON_WRITE), name, error);
+ }
+ }
+ }
+ (void) pthread_mutex_unlock(&LOCK_log);
+ DBUG_RETURN(error);
+}
+
+
+/**
+ @todo
+ The following should be using fn_format(); We just need to
+ first change fn_format() to cut the file name if it's too long.
+*/
const char *MYSQL_LOG::generate_name(const char *log_name,
- const char *suffix,
- bool strip_ext, char *buff)
+ const char *suffix,
+ bool strip_ext, char *buff)
{
if (!log_name || !log_name[0])
{
- strmake(buff, pidfile_name, (uint) (FN_REFLEN - strlen(suffix) - 1));
+ strmake(buff, pidfile_name, FN_REFLEN - strlen(suffix) - 1);
return (const char *)
fn_format(buff, buff, "", suffix, MYF(MY_REPLACE_EXT|MY_REPLACE_DIR));
-
}
// get rid of extension if the log is binary to avoid problems
if (strip_ext)
{
- char *p = fn_ext(log_name);
- uint length=(uint) (p-log_name);
- strmake(buff,log_name,min(length,FN_REFLEN));
+ char *p= fn_ext(log_name);
+ uint length= (uint) (p - log_name);
+ strmake(buff, log_name, min(length, FN_REFLEN));
return (const char*)buff;
}
return log_name;
}
-bool MYSQL_LOG::open_index_file(const char *index_file_name_arg,
+
+
+MYSQL_BIN_LOG::MYSQL_BIN_LOG()
+ :bytes_written(0), prepared_xids(0), file_id(1), open_count(1),
+ need_start_event(TRUE), m_table_map_version(0),
+ is_relay_log(0),
+ description_event_for_exec(0), description_event_for_queue(0)
+{
+ /*
+ We don't want to initialize locks here as such initialization depends on
+ safe_mutex (when using safe_mutex) which depends on MY_INIT(), which is
+ called only in main(). Doing initialization here would make it happen
+ before main().
+ */
+ index_file_name[0] = 0;
+ bzero((char*) &index_file, sizeof(index_file));
+ bzero((char*) &purge_temp, sizeof(purge_temp));
+}
+
+/* this is called only once */
+
+void MYSQL_BIN_LOG::cleanup()
+{
+ DBUG_ENTER("cleanup");
+ if (inited)
+ {
+ inited= 0;
+ 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);
+ }
+ DBUG_VOID_RETURN;
+}
+
+
+/* Init binlog-specific vars */
+void MYSQL_BIN_LOG::init(bool no_auto_events_arg, ulong max_size_arg)
+{
+ DBUG_ENTER("MYSQL_BIN_LOG::init");
+ no_auto_events= no_auto_events_arg;
+ max_size= max_size_arg;
+ DBUG_PRINT("info",("max_size: %lu", max_size));
+ DBUG_VOID_RETURN;
+}
+
+
+void MYSQL_BIN_LOG::init_pthread_objects()
+{
+ DBUG_ASSERT(inited == 0);
+ inited= 1;
+ (void) pthread_mutex_init(&LOCK_log, MY_MUTEX_INIT_SLOW);
+ (void) pthread_mutex_init(&LOCK_index, MY_MUTEX_INIT_SLOW);
+ (void) pthread_cond_init(&update_cond, 0);
+}
+
+
+bool MYSQL_BIN_LOG::open_index_file(const char *index_file_name_arg,
const char *log_name)
{
File index_file_nr= -1;
@@ -531,6 +2384,11 @@ bool MYSQL_LOG::open_index_file(const char *index_file_name_arg,
my_seek(index_file_nr,0L,MY_SEEK_END,MYF(0)),
0, MYF(MY_WME | MY_WAIT_IF_FULL)))
{
+ /*
+ 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().
+ */
if (index_file_nr >= 0)
my_close(index_file_nr,MYF(0));
return TRUE;
@@ -539,111 +2397,44 @@ bool MYSQL_LOG::open_index_file(const char *index_file_name_arg,
}
-/*
- Open a (new) log file.
+/**
+ Open a (new) binlog file.
- DESCRIPTION
- - If binary logs, also open the index file and register the new
- file name in it
+ - Open the log file and the index file. Register the new
+ file name in it
- When calling this when the file is in use, you must have a locks
- on LOCK_log and LOCK_index.
+ on LOCK_log and LOCK_index.
- RETURN VALUES
+ @retval
0 ok
+ @retval
1 error
*/
-bool MYSQL_LOG::open(const char *log_name,
- enum_log_type log_type_arg,
- const char *new_name,
- enum cache_type io_cache_type_arg,
- bool no_auto_events_arg,
- ulong max_size_arg,
- bool null_created_arg)
+bool MYSQL_BIN_LOG::open(const char *log_name,
+ enum_log_type log_type_arg,
+ const char *new_name,
+ enum cache_type io_cache_type_arg,
+ bool no_auto_events_arg,
+ ulong max_size_arg,
+ bool null_created_arg)
{
- char buff[FN_REFLEN];
File file= -1;
- int open_flags = O_CREAT | O_BINARY;
- DBUG_ENTER("MYSQL_LOG::open");
+ DBUG_ENTER("MYSQL_BIN_LOG::open");
DBUG_PRINT("enter",("log_type: %d",(int) log_type_arg));
- last_time=query_start=0;
write_error=0;
- init(log_type_arg,io_cache_type_arg,no_auto_events_arg,max_size_arg);
+ /* open the main log file */
+ if (MYSQL_LOG::open(log_name, log_type_arg, new_name, io_cache_type_arg))
+ DBUG_RETURN(1); /* all warnings issued */
- if (!(name=my_strdup(log_name,MYF(MY_WME))))
- {
- name= (char *)log_name; // for the error message
- goto err;
- }
- if (new_name)
- strmov(log_file_name,new_name);
- else if (generate_new_name(log_file_name, name))
- goto err;
+ init(no_auto_events_arg, max_size_arg);
- if (io_cache_type == SEQ_READ_APPEND)
- open_flags |= O_RDWR | O_APPEND;
- else
- open_flags |= O_WRONLY | (log_type == LOG_BIN ? 0 : O_APPEND);
-
- db[0]=0;
open_count++;
- if ((file=my_open(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,
- MYF(MY_WME | MY_NABP |
- ((log_type == LOG_BIN) ? MY_WAIT_IF_FULL : 0))))
- goto err;
- switch (log_type) {
- case LOG_NORMAL:
- {
- char *end;
- int len=my_snprintf(buff, sizeof(buff), "%s, Version: %s (%s). "
-#ifdef EMBEDDED_LIBRARY
- "embedded library\n",
- my_progname, server_version, MYSQL_COMPILATION_COMMENT
-#elif __NT__
- "started with:\nTCP Port: %d, Named Pipe: %s\n",
- my_progname, server_version, MYSQL_COMPILATION_COMMENT,
- mysqld_port, mysqld_unix_port
-#else
- "started with:\nTcp port: %d Unix socket: %s\n",
- my_progname, server_version, MYSQL_COMPILATION_COMMENT,
- mysqld_port, mysqld_unix_port
-#endif
- );
- end=strnmov(buff+len,"Time Id Command Argument\n",
- sizeof(buff)-len);
- if (my_b_write(&log_file, (byte*) buff,(uint) (end-buff)) ||
- flush_io_cache(&log_file))
- goto err;
- break;
- }
- case LOG_NEW:
- {
- uint len;
- time_t skr=time(NULL);
- struct tm tm_tmp;
-
- localtime_r(&skr,&tm_tmp);
- len= my_snprintf(buff,sizeof(buff),
- "# %s, Version: %s at %02d%02d%02d %2d:%02d:%02d\n",
- my_progname,server_version,
- tm_tmp.tm_year % 100,
- tm_tmp.tm_mon+1,
- tm_tmp.tm_mday,
- tm_tmp.tm_hour,
- tm_tmp.tm_min,
- tm_tmp.tm_sec);
- if (my_b_write(&log_file, (byte*) buff, len) ||
- flush_io_cache(&log_file))
- goto err;
- break;
- }
- case LOG_BIN:
+ DBUG_ASSERT(log_type == LOG_BIN);
+
{
bool write_file_name_to_index_file=0;
@@ -655,7 +2446,7 @@ bool MYSQL_LOG::open(const char *log_name,
an extension for the binary log files.
In this case we write a standard header to it.
*/
- if (my_b_safe_write(&log_file, (byte*) BINLOG_MAGIC,
+ if (my_b_safe_write(&log_file, (uchar*) BINLOG_MAGIC,
BIN_LOG_HEADER_SIZE))
goto err;
bytes_written+= BIN_LOG_HEADER_SIZE;
@@ -680,8 +2471,7 @@ bool MYSQL_LOG::open(const char *log_name,
s.flags|= LOG_EVENT_BINLOG_IN_USE_F;
if (!s.is_valid())
goto err;
- if (null_created_arg)
- s.created= 0;
+ s.dont_set_created= null_created_arg;
if (s.write(&log_file))
goto err;
bytes_written+= s.data_written;
@@ -707,11 +2497,11 @@ bool MYSQL_LOG::open(const char *log_name,
/*
Set 'created' to 0, so that in next relay logs this event does not
trigger cleaning actions on the slave in
- Format_description_log_event::exec_event().
+ Format_description_log_event::apply_event_impl().
*/
description_event_for_queue->created= 0;
/* Don't set log_pos in event header */
- description_event_for_queue->artificial_event=1;
+ description_event_for_queue->set_artificial_event();
if (description_event_for_queue->write(&log_file))
goto err;
@@ -727,20 +2517,16 @@ bool MYSQL_LOG::open(const char *log_name,
As this is a new log file, we write the file name to the index
file. As every time we write to the index file, we sync it.
*/
- if (my_b_write(&index_file, (byte*) log_file_name,
- (uint) strlen(log_file_name)) ||
- my_b_write(&index_file, (byte*) "\n", 1) ||
+ if (my_b_write(&index_file, (uchar*) log_file_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)))
goto err;
}
- break;
- }
- case LOG_CLOSED: // Impossible
- case LOG_TO_BE_OPENED:
- DBUG_ASSERT(1);
- break;
}
+ log_state= LOG_OPENED;
+
DBUG_RETURN(0);
err:
@@ -753,12 +2539,12 @@ shutdown the MySQL server and restart it.", name, errno);
end_io_cache(&log_file);
end_io_cache(&index_file);
safeFree(name);
- log_type= LOG_CLOSED;
+ log_state= LOG_CLOSED;
DBUG_RETURN(1);
}
-int MYSQL_LOG::get_current_log(LOG_INFO* linfo)
+int MYSQL_BIN_LOG::get_current_log(LOG_INFO* linfo)
{
pthread_mutex_lock(&LOCK_log);
int ret = raw_get_current_log(linfo);
@@ -766,31 +2552,27 @@ int MYSQL_LOG::get_current_log(LOG_INFO* linfo)
return ret;
}
-int MYSQL_LOG::raw_get_current_log(LOG_INFO* linfo)
+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);
linfo->pos = my_b_tell(&log_file);
return 0;
}
-/*
- Move all data up in a file in an filename index file
-
- SYNOPSIS
- copy_up_file_and_fill()
- index_file File to move
- offset Move everything from here to beginning
-
- NOTE
- File will be truncated to be 'offset' shorter or filled up with
- newlines
+/**
+ Move all data up in a file in an filename index file.
- IMPLEMENTATION
We do the copy outside of the IO_CACHE as the cache buffers would just
make things slower and more complicated.
In most cases the copy loop should only do one read.
- RETURN VALUES
+ @param index_file File to move
+ @param offset Move everything from here to beginning
+
+ @note
+ File will be truncated to be 'offset' shorter or filled up with newlines
+
+ @retval
0 ok
*/
@@ -801,7 +2583,7 @@ static bool copy_up_file_and_fill(IO_CACHE *index_file, my_off_t offset)
int bytes_read;
my_off_t init_offset= offset;
File file= index_file->file;
- byte io_buf[IO_SIZE*2];
+ uchar io_buf[IO_SIZE*2];
DBUG_ENTER("copy_up_file_and_fill");
for (;; offset+= bytes_read)
@@ -813,7 +2595,7 @@ static bool copy_up_file_and_fill(IO_CACHE *index_file, my_off_t offset)
if (!bytes_read)
break; // end of file
(void) my_seek(file, offset-init_offset, MY_SEEK_SET, MYF(0));
- if (my_write(file, (byte*) io_buf, bytes_read, MYF(MY_WME | MY_NABP)))
+ if (my_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' */
@@ -831,29 +2613,29 @@ err:
#endif /* HAVE_REPLICATION */
-/*
- Find the position in the log-index-file for the given log name
+/**
+ Find the position in the log-index-file for the given log name.
- SYNOPSIS
- find_log_pos()
- linfo Store here the found log file name and position to
- the NEXT log file name in the index file.
- log_name Filename to find in the index file.
- Is a null pointer if we want to read the first entry
- need_lock Set this to 1 if the parent doesn't already have a
- lock on LOCK_index
+ @param linfo Store here the found log file name and position to
+ the NEXT log file name in the index file.
+ @param log_name Filename to find in the index file.
+ Is a null pointer if we want to read the first entry
+ @param need_lock Set this to 1 if the parent doesn't already have a
+ lock on LOCK_index
- NOTE
+ @note
On systems without the truncate function the file will end with one or
more empty lines. These will be ignored when reading the file.
- RETURN VALUES
+ @retval
0 ok
- LOG_INFO_EOF End of log-index-file found
+ @retval
+ LOG_INFO_EOF End of log-index-file found
+ @retval
LOG_INFO_IO Got IO error while reading file
*/
-int MYSQL_LOG::find_log_pos(LOG_INFO *linfo, const char *log_name,
+int MYSQL_BIN_LOG::find_log_pos(LOG_INFO *linfo, const char *log_name,
bool need_lock)
{
int error= 0;
@@ -905,29 +2687,31 @@ int MYSQL_LOG::find_log_pos(LOG_INFO *linfo, const char *log_name,
}
-/*
- Find the position in the log-index-file for the given log name
+/**
+ Find the position in the log-index-file for the given log name.
- SYNOPSIS
- find_next_log()
+ @param
linfo Store here the next log file name and position to
the file name after that.
+ @param
need_lock Set this to 1 if the parent doesn't already have a
lock on LOCK_index
- NOTE
+ @note
- Before calling this function, one has to call find_log_pos()
- to set up 'linfo'
+ to set up 'linfo'
- Mutex needed because we need to make sure the file pointer does not move
- from under our feet
+ from under our feet
- RETURN VALUES
+ @retval
0 ok
- LOG_INFO_EOF End of log-index-file found
+ @retval
+ LOG_INFO_EOF End of log-index-file found
+ @retval
LOG_INFO_IO Got IO error while reading file
*/
-int MYSQL_LOG::find_next_log(LOG_INFO* linfo, bool need_lock)
+int MYSQL_BIN_LOG::find_next_log(LOG_INFO* linfo, bool need_lock)
{
int error= 0;
uint length;
@@ -957,32 +2741,31 @@ err:
}
-/*
- Delete all logs refered to in the index file
- Start writing to a new log file. The new index file will only contain
- this file.
+/**
+ Delete all logs refered to in the index file.
+ Start writing to a new log file.
- SYNOPSIS
- reset_logs()
- thd Thread
+ The new index file will only contain this file.
- NOTE
- If not called from slave thread, write start event to new log
+ @param thd Thread
+ @note
+ If not called from slave thread, write start event to new log
- RETURN VALUES
+ @retval
0 ok
+ @retval
1 error
*/
-bool MYSQL_LOG::reset_logs(THD* thd)
+bool MYSQL_BIN_LOG::reset_logs(THD* thd)
{
LOG_INFO linfo;
bool error=0;
const char* save_name;
- enum_log_type save_log_type;
DBUG_ENTER("reset_logs");
+ ha_reset_logs(thd);
/*
We need to get both locks to be sure that no one is trying to
write to the index log file.
@@ -996,12 +2779,11 @@ bool MYSQL_LOG::reset_logs(THD* thd)
thread. If the transaction involved MyISAM tables, it should go
into binlog even on rollback.
*/
- (void) pthread_mutex_lock(&LOCK_thread_count);
+ VOID(pthread_mutex_lock(&LOCK_thread_count));
/* Save variables so that we can reopen the log */
save_name=name;
name=0; // Protect against free
- save_log_type=log_type;
close(LOG_CLOSE_TO_BE_OPENED);
/* First delete all old log files */
@@ -1014,67 +2796,116 @@ bool MYSQL_LOG::reset_logs(THD* thd)
for (;;)
{
- my_delete_allow_opened(linfo.log_file_name, MYF(MY_WME));
+ if ((error= my_delete_allow_opened(linfo.log_file_name, MYF(0))) != 0)
+ {
+ if (my_errno == ENOENT)
+ {
+ push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ ER_LOG_PURGE_NO_FILE, ER(ER_LOG_PURGE_NO_FILE),
+ linfo.log_file_name);
+ sql_print_information("Failed to delete file '%s'",
+ linfo.log_file_name);
+ my_errno= 0;
+ error= 0;
+ }
+ else
+ {
+ push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_ERROR,
+ ER_BINLOG_PURGE_FATAL_ERR,
+ "a problem with deleting %s; "
+ "consider examining correspondence "
+ "of your binlog index file "
+ "to the actual binlog files",
+ linfo.log_file_name);
+ error= 1;
+ goto err;
+ }
+ }
if (find_next_log(&linfo, 0))
break;
}
/* Start logging with a new file */
close(LOG_CLOSE_INDEX);
- my_delete_allow_opened(index_file_name, MYF(MY_WME)); // Reset (open will update)
+ if ((error= my_delete_allow_opened(index_file_name, MYF(0)))) // Reset (open will update)
+ {
+ if (my_errno == ENOENT)
+ {
+ push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ ER_LOG_PURGE_NO_FILE, ER(ER_LOG_PURGE_NO_FILE),
+ index_file_name);
+ sql_print_information("Failed to delete file '%s'",
+ index_file_name);
+ my_errno= 0;
+ error= 0;
+ }
+ else
+ {
+ push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_ERROR,
+ ER_BINLOG_PURGE_FATAL_ERR,
+ "a problem with deleting %s; "
+ "consider examining correspondence "
+ "of your binlog index file "
+ "to the actual binlog files",
+ index_file_name);
+ error= 1;
+ goto err;
+ }
+ }
if (!thd->slave_thread)
need_start_event=1;
if (!open_index_file(index_file_name, 0))
- open(save_name, save_log_type, 0,
- io_cache_type, no_auto_events, max_size, 0);
- my_free((gptr) save_name, MYF(0));
+ open(save_name, log_type, 0, io_cache_type, no_auto_events, max_size, 0);
+ my_free((uchar*) save_name, MYF(0));
err:
- (void) pthread_mutex_unlock(&LOCK_thread_count);
+ VOID(pthread_mutex_unlock(&LOCK_thread_count));
pthread_mutex_unlock(&LOCK_index);
pthread_mutex_unlock(&LOCK_log);
DBUG_RETURN(error);
}
-/*
+/**
Delete relay log files prior to rli->group_relay_log_name
(i.e. all logs which are not involved in a non-finished group
- (transaction)), remove them from the index file and start on next relay log.
+ (transaction)), remove them from the index file and start on next
+ relay log.
- SYNOPSIS
- purge_first_log()
- rli Relay log information
- included If false, all relay logs that are strictly before
- rli->group_relay_log_name are deleted ; if true, the latter is
- deleted too (i.e. all relay logs
- read by the SQL slave thread are deleted).
-
- NOTE
+ IMPLEMENTATION
+ - Protects index file with LOCK_index
+ - Delete relevant relay log files
+ - Copy all file names after these ones to the front of the index file
+ - If the OS has truncate, truncate the file, else fill it with \n'
+ - Read the next file name from the index file and store in rli->linfo
+
+ @param rli Relay log information
+ @param included If false, all relay logs that are strictly before
+ rli->group_relay_log_name are deleted ; if true, the
+ latter is deleted too (i.e. all relay logs
+ read by the SQL slave thread are deleted).
+
+ @note
- This is only called from the slave-execute thread when it has read
- all commands from a relay log and want to switch to a new relay log.
+ all commands from a relay log and want to switch to a new relay log.
- When this happens, we can be in an active transaction as
- a transaction can span over two relay logs
- (although it is always written as a single block to the master's binary
- log, hence cannot span over two master's binary logs).
-
- IMPLEMENTATION
- - Protects index file with LOCK_index
- - Read the next file name from the index file and store in rli->linfo
- - Delete relevant relay log files
- - Copy all file names after these ones to the front of the index file
- - If the OS has truncate, truncate the file, else fill it with \n'
+ a transaction can span over two relay logs
+ (although it is always written as a single block to the master's binary
+ log, hence cannot span over two master's binary logs).
- RETURN VALUES
+ @retval
0 ok
- LOG_INFO_EOF End of log-index-file found
+ @retval
+ LOG_INFO_EOF End of log-index-file found
+ @retval
LOG_INFO_SEEK Could not allocate IO cache
+ @retval
LOG_INFO_IO Got IO error while reading file
*/
#ifdef HAVE_REPLICATION
-int MYSQL_LOG::purge_first_log(struct st_relay_log_info* rli, bool included)
+int MYSQL_BIN_LOG::purge_first_log(Relay_log_info* rli, bool included)
{
int error;
char *to_purge_if_included= NULL;
@@ -1166,11 +2997,11 @@ err:
DBUG_RETURN(error);
}
-/*
- Update log index_file
+/**
+ Update log index_file.
*/
-int MYSQL_LOG::update_log_index(LOG_INFO* log_info, bool need_update_threads)
+int MYSQL_BIN_LOG::update_log_index(LOG_INFO* log_info, bool need_update_threads)
{
if (copy_up_file_and_fill(&index_file, log_info->index_file_start_offset))
return LOG_INFO_IO;
@@ -1181,31 +3012,31 @@ int MYSQL_LOG::update_log_index(LOG_INFO* log_info, bool need_update_threads)
return 0;
}
-/*
+/**
Remove all logs before the given log from disk and from the index file.
- SYNOPSIS
- purge_logs()
- to_log Delete all log file name before this file.
- included If true, to_log is deleted too.
- need_mutex
- need_update_threads If we want to update the log coordinates of
- all threads. False for relay logs, true otherwise.
- freed_log_space If not null, decrement this variable of
- the amount of log space freed
+ @param to_log Delete all log file name before this file.
+ @param included If true, to_log is deleted too.
+ @param need_mutex
+ @param need_update_threads If we want to update the log coordinates of
+ all threads. False for relay logs, true otherwise.
+ @param freed_log_space If not null, decrement this variable of
+ the amount of log space freed
- NOTES
+ @note
If any of the logs before the deleted one is in use,
only purge logs up to this one.
- RETURN VALUES
- 0 ok
+ @retval
+ 0 ok
+ @retval
LOG_INFO_EOF to_log not found
+ LOG_INFO_EMFILE too many files opened
LOG_INFO_FATAL if any other than ENOENT error from
my_stat() or my_delete()
*/
-int MYSQL_LOG::purge_logs(const char *to_log,
+int MYSQL_BIN_LOG::purge_logs(const char *to_log,
bool included,
bool need_mutex,
bool need_update_threads,
@@ -1260,9 +3091,9 @@ int MYSQL_LOG::purge_logs(const char *to_log,
while ((strcmp(to_log,log_info.log_file_name) || (exit_loop=included)) &&
!log_in_use(log_info.log_file_name))
{
- if ((error=my_b_write(&purge_temp, (byte*)log_info.log_file_name,
+ if ((error=my_b_write(&purge_temp, (const uchar*)log_info.log_file_name,
strlen(log_info.log_file_name))) ||
- (error=my_b_write(&purge_temp, (byte*)"\n", 1)))
+ (error=my_b_write(&purge_temp, (const uchar*)"\n", 1)))
{
sql_print_error("MYSQL_LOG::purge_logs failed to copy %s to purge_temp",
log_info.log_file_name);
@@ -1313,6 +3144,8 @@ int MYSQL_LOG::purge_logs(const char *to_log,
/* Get rid of the trailing '\n' */
log_info.log_file_name[length-1]= 0;
+ ha_binlog_index_purge_file(current_thd, log_info.log_file_name);
+
MY_STAT s;
if (!my_stat(log_info.log_file_name, &s, MYF(0)))
{
@@ -1349,11 +3182,11 @@ int MYSQL_LOG::purge_logs(const char *to_log,
}
else
{
- sql_print_information("Failed to delete log file '%s'; "
- "consider examining correspondence "
- "of your binlog index file "
- "to the actual binlog files",
- log_info.log_file_name);
+ sql_print_information("Failed to delete log file '%s'; "
+ "consider examining correspondence "
+ "of your binlog index file "
+ "to the actual binlog files",
+ log_info.log_file_name);
}
error= LOG_INFO_FATAL;
goto err;
@@ -1401,6 +3234,13 @@ int MYSQL_LOG::purge_logs(const char *to_log,
"to the actual binlog files",
log_info.log_file_name);
}
+ if (my_errno == EMFILE)
+ {
+ DBUG_PRINT("info",
+ ("my_errno: %d, set ret = LOG_INFO_EMFILE", my_errno));
+ error= LOG_INFO_EMFILE;
+ goto err;
+ }
error= LOG_INFO_FATAL;
goto err;
}
@@ -1415,26 +3255,26 @@ err:
DBUG_RETURN(error);
}
-/*
+/**
Remove all logs before the given file date from disk and from the
index file.
- SYNOPSIS
- purge_logs_before_date()
- before_date Delete all log files before given date.
+ @param thd Thread pointer
+ @param purge_time Delete all log files before given date.
- NOTES
+ @note
If any of the logs before the deleted one is in use,
only purge logs up to this one.
- RETURN VALUES
+ @retval
0 ok
+ @retval
LOG_INFO_PURGE_NO_ROTATE Binary file that can't be rotated
LOG_INFO_FATAL if any other than ENOENT error from
my_stat() or my_delete()
*/
-int MYSQL_LOG::purge_logs_before_date(time_t purge_time)
+int MYSQL_BIN_LOG::purge_logs_before_date(time_t purge_time)
{
int error;
char to_log[FN_REFLEN];
@@ -1459,7 +3299,7 @@ int MYSQL_LOG::purge_logs_before_date(time_t purge_time)
{
/*
It's not fatal if we can't stat a log file that does not exist.
- */
+ */
if (thd)
{
push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
@@ -1516,18 +3356,16 @@ err:
#endif /* HAVE_REPLICATION */
-/*
- Create a new log file name
+/**
+ Create a new log file name.
- SYNOPSIS
- make_log_name()
- buf buf of at least FN_REFLEN where new name is stored
+ @param buf buf of at least FN_REFLEN where new name is stored
- NOTE
+ @note
If file name will be longer then FN_REFLEN it will be truncated
*/
-void MYSQL_LOG::make_log_name(char* buf, const char* log_ident)
+void MYSQL_BIN_LOG::make_log_name(char* buf, const char* log_ident)
{
uint dir_len = dirname_length(log_file_name);
if (dir_len >= FN_REFLEN)
@@ -1537,33 +3375,50 @@ void MYSQL_LOG::make_log_name(char* buf, const char* log_ident)
}
-/*
- Check if we are writing/reading to the given log file
+/**
+ Check if we are writing/reading to the given log file.
*/
-bool MYSQL_LOG::is_active(const char *log_file_name_arg)
+bool MYSQL_BIN_LOG::is_active(const char *log_file_name_arg)
{
return !strcmp(log_file_name, log_file_name_arg);
}
/*
- Start writing to a new log file or reopen the old file
+ Wrappers around new_file_impl to avoid using argument
+ to control locking. The argument 1) less readable 2) breaks
+ incapsulation 3) allows external access to the class without
+ a lock (which is not possible with private new_file_without_locking
+ method).
+*/
- SYNOPSIS
- new_file()
- need_lock Set to 1 if caller has not locked LOCK_log
+void MYSQL_BIN_LOG::new_file()
+{
+ new_file_impl(1);
+}
+
+
+void MYSQL_BIN_LOG::new_file_without_locking()
+{
+ new_file_impl(0);
+}
+
+
+/**
+ Start writing to a new log file or reopen the old file.
+
+ @param need_lock Set to 1 if caller has not locked LOCK_log
- NOTE
+ @note
The new file name is stored last in the index file
*/
-void MYSQL_LOG::new_file(bool need_lock)
+void MYSQL_BIN_LOG::new_file_impl(bool need_lock)
{
char new_name[FN_REFLEN], *new_name_ptr, *old_name;
- enum_log_type save_log_type;
- DBUG_ENTER("MYSQL_LOG::new_file");
+ DBUG_ENTER("MYSQL_BIN_LOG::new_file_impl");
if (!is_open())
{
DBUG_PRINT("info",("log is closed"));
@@ -1590,8 +3445,10 @@ void MYSQL_LOG::new_file(bool need_lock)
{
tc_log_page_waits++;
pthread_mutex_lock(&LOCK_prep_xids);
- while (prepared_xids)
+ while (prepared_xids) {
+ DBUG_PRINT("info", ("prepared_xids=%lu", prepared_xids));
pthread_cond_wait(&COND_prep_xids, &LOCK_prep_xids);
+ }
pthread_mutex_unlock(&LOCK_prep_xids);
}
@@ -1615,9 +3472,8 @@ void MYSQL_LOG::new_file(bool need_lock)
We log the whole file name for log file as the user may decide
to change base names at some point.
*/
- THD *thd = current_thd; /* may be 0 if we are reacting to SIGHUP */
- Rotate_log_event r(thd,new_name+dirname_length(new_name),
- 0, LOG_EVENT_OFFSET, 0);
+ Rotate_log_event r(new_name+dirname_length(new_name),
+ 0, LOG_EVENT_OFFSET, is_relay_log ? Rotate_log_event::RELAY_LOG : 0);
r.write(&log_file);
bytes_written += r.data_written;
}
@@ -1629,12 +3485,11 @@ void MYSQL_LOG::new_file(bool need_lock)
signal_update();
}
old_name=name;
- save_log_type=log_type;
name=0; // Don't free name
close(LOG_CLOSE_TO_BE_OPENED);
/*
- Note that at this point, log_type != LOG_CLOSED (important for is_open()).
+ Note that at this point, log_state != LOG_CLOSED (important for is_open()).
*/
/*
@@ -1646,7 +3501,7 @@ void MYSQL_LOG::new_file(bool need_lock)
trigger temp tables deletion on slaves.
*/
- open(old_name, save_log_type, new_name_ptr,
+ open(old_name, log_type, new_name_ptr,
io_cache_type, no_auto_events, max_size, 1);
my_free(old_name,MYF(0));
@@ -1659,11 +3514,11 @@ end:
}
-bool MYSQL_LOG::append(Log_event* ev)
+bool MYSQL_BIN_LOG::append(Log_event* ev)
{
bool error = 0;
pthread_mutex_lock(&LOCK_log);
- DBUG_ENTER("MYSQL_LOG::append");
+ DBUG_ENTER("MYSQL_BIN_LOG::append");
DBUG_ASSERT(log_file.type == SEQ_READ_APPEND);
/*
@@ -1678,7 +3533,7 @@ bool MYSQL_LOG::append(Log_event* ev)
bytes_written+= ev->data_written;
DBUG_PRINT("info",("max_size: %lu",max_size));
if ((uint) my_b_append_tell(&log_file) > max_size)
- new_file(0);
+ new_file_without_locking();
err:
pthread_mutex_unlock(&LOCK_log);
@@ -1687,10 +3542,10 @@ err:
}
-bool MYSQL_LOG::appendv(const char* buf, uint len,...)
+bool MYSQL_BIN_LOG::appendv(const char* buf, uint len,...)
{
bool error= 0;
- DBUG_ENTER("MYSQL_LOG::appendv");
+ DBUG_ENTER("MYSQL_BIN_LOG::appendv");
va_list(args);
va_start(args,len);
@@ -1699,7 +3554,7 @@ bool MYSQL_LOG::appendv(const char* buf, uint len,...)
safe_mutex_assert_owner(&LOCK_log);
do
{
- if (my_b_append(&log_file,(byte*) buf,len))
+ if (my_b_append(&log_file,(uchar*) buf,len))
{
error= 1;
goto err;
@@ -1708,7 +3563,7 @@ bool MYSQL_LOG::appendv(const char* buf, uint len,...)
} while ((buf=va_arg(args,const char*)) && (len=va_arg(args,uint)));
DBUG_PRINT("info",("max_size: %lu",max_size));
if ((uint) my_b_append_tell(&log_file) > max_size)
- new_file(0);
+ new_file_without_locking();
err:
if (!error)
@@ -1717,97 +3572,7 @@ err:
}
-/*
- Write to normal (not rotable) log
- This is the format for the 'normal' log.
-*/
-
-bool MYSQL_LOG::write(THD *thd,enum enum_server_command command,
- const char *format,...)
-{
- if (is_open() && (what_to_log & (1L << (uint) command)))
- {
- uint length;
- int error= 0;
- VOID(pthread_mutex_lock(&LOCK_log));
-
- /* Test if someone closed between the is_open test and lock */
- if (is_open())
- {
- time_t skr;
- ulong id;
- va_list args;
- va_start(args,format);
- char buff[32];
-
- if (thd)
- { // Normal thread
- if ((thd->options & OPTION_LOG_OFF)
-#ifndef NO_EMBEDDED_ACCESS_CHECKS
- && (thd->security_ctx->master_access & SUPER_ACL)
-#endif
-)
- {
- VOID(pthread_mutex_unlock(&LOCK_log));
- return 0; // No logging
- }
- id=thd->thread_id;
- if (thd->user_time || !(skr=thd->query_start()))
- skr=time(NULL); // Connected
- }
- else
- { // Log from connect handler
- skr=time(NULL);
- id=0;
- }
- if (skr != last_time)
- {
- last_time=skr;
- struct tm tm_tmp;
- struct tm *start;
- localtime_r(&skr,&tm_tmp);
- start=&tm_tmp;
- /* Note that my_b_write() assumes it knows the length for this */
- sprintf(buff,"%02d%02d%02d %2d:%02d:%02d\t",
- start->tm_year % 100,
- start->tm_mon+1,
- start->tm_mday,
- start->tm_hour,
- start->tm_min,
- start->tm_sec);
- if (my_b_write(&log_file, (byte*) buff,16))
- error=errno;
- }
- else if (my_b_write(&log_file, (byte*) "\t\t",2) < 0)
- error=errno;
- length=my_sprintf(buff,
- (buff, "%7ld %-11.11s", id,
- command_name[(uint) command]));
- if (my_b_write(&log_file, (byte*) buff,length))
- error=errno;
- if (format)
- {
- if (my_b_write(&log_file, (byte*) " ",1) ||
- my_b_vprintf(&log_file,format,args) == (uint) -1)
- error=errno;
- }
- if (my_b_write(&log_file, (byte*) "\n",1) ||
- flush_io_cache(&log_file))
- error=errno;
- if (error && ! write_error)
- {
- write_error=1;
- sql_print_error(ER(ER_ERROR_ON_WRITE),name,error);
- }
- va_end(args);
- }
- VOID(pthread_mutex_unlock(&LOCK_log));
- return error != 0;
- }
- return 0;
-}
-
-bool MYSQL_LOG::flush_and_sync()
+bool MYSQL_BIN_LOG::flush_and_sync()
{
int err=0, fd=log_file.file;
safe_mutex_assert_owner(&LOCK_log);
@@ -1821,7 +3586,7 @@ bool MYSQL_LOG::flush_and_sync()
return err;
}
-void MYSQL_LOG::start_union_events(THD *thd, query_id_t query_id_param)
+void MYSQL_BIN_LOG::start_union_events(THD *thd, query_id_t query_id_param)
{
DBUG_ASSERT(!thd->binlog_evt_union.do_union);
thd->binlog_evt_union.do_union= TRUE;
@@ -1830,27 +3595,307 @@ void MYSQL_LOG::start_union_events(THD *thd, query_id_t query_id_param)
thd->binlog_evt_union.first_query_id= query_id_param;
}
-void MYSQL_LOG::stop_union_events(THD *thd)
+void MYSQL_BIN_LOG::stop_union_events(THD *thd)
{
DBUG_ASSERT(thd->binlog_evt_union.do_union);
thd->binlog_evt_union.do_union= FALSE;
}
-bool MYSQL_LOG::is_query_in_union(THD *thd, query_id_t query_id_param)
+bool MYSQL_BIN_LOG::is_query_in_union(THD *thd, query_id_t query_id_param)
{
return (thd->binlog_evt_union.do_union &&
query_id_param >= thd->binlog_evt_union.first_query_id);
}
+
/*
- Write an event to the binary log
+ These functions are placed in this file since they need access to
+ binlog_hton, which has internal linkage.
*/
-bool MYSQL_LOG::write(Log_event *event_info)
+int 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);
+
+ if (trx_data)
+ DBUG_RETURN(0); // 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,
+ 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
+ }
+ thd_set_ha_data(this, binlog_hton, trx_data);
+
+ trx_data= new (thd_get_ha_data(this, binlog_hton)) binlog_trx_data;
+
+ DBUG_RETURN(0);
+}
+
+/*
+ Function to start a statement and optionally a transaction for the
+ binary log.
+
+ SYNOPSIS
+ binlog_start_trans_and_stmt()
+
+ DESCRIPTION
+
+ This function does three things:
+ - 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.
+
+ - Save the currrent binlog position so that we can roll back the
+ statement by truncating the transaction log.
+
+ We only update the saved position if the old one was undefined,
+ the reason is that there are some cases (e.g., for CREATE-SELECT)
+ where the position is saved twice (e.g., both in
+ select_create::prepare() and THD::binlog_write_table_map()) , but
+ we should use the first. This means that calls to this function
+ can be used to start the statement before the first table map
+ event, to include some extra events.
+ */
+
+void
+THD::binlog_start_trans_and_stmt()
+{
+ binlog_trx_data *trx_data= (binlog_trx_data*) 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 :
+ (ulong) 0)));
+
+ if (trx_data == NULL ||
+ trx_data->before_stmt_pos == MY_OFF_T_UNDEF)
+ {
+ this->binlog_set_stmt_begin();
+ if (options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN))
+ trans_register_ha(this, TRUE, binlog_hton);
+ trans_register_ha(this, FALSE, binlog_hton);
+ /*
+ Mark statement transaction as read/write. We never start
+ a binary log transaction and keep it read-only,
+ therefore it's best to mark the transaction read/write just
+ at the same time we start it.
+ Not necessary to mark the normal transaction read/write
+ since the statement-level flag will be propagated automatically
+ inside ha_commit_trans.
+ */
+ ha_data[binlog_hton->slot].ha_info[0].set_trx_read_write();
+ }
+ DBUG_VOID_RETURN;
+}
+
+void THD::binlog_set_stmt_begin() {
+ binlog_trx_data *trx_data=
+ (binlog_trx_data*) thd_get_ha_data(this, binlog_hton);
+
+ /*
+ The call to binlog_trans_log_savepos() might create the trx_data
+ 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).
+ */
+ 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;
+}
+
+
+/*
+ Write a table map to the binary log.
+ */
+
+int THD::binlog_write_table_map(TABLE *table, bool is_trans)
+{
+ int error;
+ DBUG_ENTER("THD::binlog_write_table_map");
+ DBUG_PRINT("enter", ("table: 0x%lx (%s: #%lu)",
+ (long) table, table->s->table_name.str,
+ table->s->table_map_id));
+
+ /* Pre-conditions */
+ DBUG_ASSERT(current_stmt_binlog_row_based && mysql_bin_log.is_open());
+ DBUG_ASSERT(table->s->table_map_id != ULONG_MAX);
+
+ Table_map_log_event::flag_set const
+ flags= Table_map_log_event::TM_NO_FLAGS;
+
+ Table_map_log_event
+ the_event(this, table, table->s->table_map_id, is_trans, flags);
+
+ if (is_trans && binlog_table_maps == 0)
+ binlog_start_trans_and_stmt();
+
+ if ((error= mysql_bin_log.write(&the_event)))
+ DBUG_RETURN(error);
+
+ binlog_table_maps++;
+ table->s->table_map_version= mysql_bin_log.table_map_version();
+ DBUG_RETURN(0);
+}
+
+Rows_log_event*
+THD::binlog_get_pending_rows_event() const
+{
+ binlog_trx_data *const trx_data=
+ (binlog_trx_data*) 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.
+ */
+ return trx_data ? trx_data->pending() : NULL;
+}
+
+void
+THD::binlog_set_pending_rows_event(Rows_log_event* ev)
+{
+ if (thd_get_ha_data(this, binlog_hton) == NULL)
+ binlog_setup_trx_data();
+
+ binlog_trx_data *const trx_data=
+ (binlog_trx_data*) thd_get_ha_data(this, binlog_hton);
+
+ DBUG_ASSERT(trx_data);
+ trx_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
+ no-op.
+ */
+int
+MYSQL_BIN_LOG::remove_pending_rows_event(THD *thd)
+{
+ 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);
+
+ DBUG_ASSERT(trx_data);
+
+ if (Rows_log_event* pending= trx_data->pending())
+ {
+ delete pending;
+ trx_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.
+*/
+int
+MYSQL_BIN_LOG::flush_and_set_pending_rows_event(THD *thd,
+ Rows_log_event* event)
+{
+ 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_trx_data *const trx_data=
+ (binlog_trx_data*) thd_get_ha_data(thd, binlog_hton);
+
+ DBUG_ASSERT(trx_data);
+
+ DBUG_PRINT("info", ("trx_data->pending(): 0x%lx", (long) trx_data->pending()));
+
+ if (Rows_log_event* pending= trx_data->pending())
+ {
+ IO_CACHE *file= &log_file;
+
+ /*
+ Decide if we should write to the log file directly or to the
+ transaction log.
+ */
+ if (pending->get_cache_stmt() || my_b_tell(&trx_data->trans_log))
+ file= &trx_data->trans_log;
+
+ /*
+ If we are writing to the log file directly, we could avoid
+ locking the log. This does not work since we need to step the
+ m_table_map_version below, and that change has to be protected
+ by the LOCK_log mutex.
+ */
+ pthread_mutex_lock(&LOCK_log);
+
+ /*
+ Write pending event to log file or transaction cache
+ */
+ if (pending->write(file))
+ {
+ pthread_mutex_unlock(&LOCK_log);
+ DBUG_RETURN(1);
+ }
+
+ /*
+ We step the table map version if we are writing an event
+ representing the end of a statement. We do this regardless of
+ wheather we write to the transaction cache or to directly to the
+ file.
+
+ In an ideal world, we could avoid stepping the table map version
+ if we were writing to a transaction cache, since we could then
+ reuse the table map that was written earlier in the transaction
+ cache. This does not work since STMT_END_F implies closing all
+ table mappings on the slave side.
+
+ TODO: Find a solution so that table maps does not have to be
+ written several times within a transaction.
+ */
+ if (pending->get_flags(Rows_log_event::STMT_END_F))
+ ++m_table_map_version;
+
+ delete pending;
+
+ if (file == &log_file)
+ {
+ error= flush_and_sync();
+ if (!error)
+ {
+ signal_update();
+ rotate_and_purge(RP_LOCK_LOG_IS_ALREADY_LOCKED);
+ }
+ }
+
+ pthread_mutex_unlock(&LOCK_log);
+ }
+
+ thd->binlog_set_pending_rows_event(event);
+
+ DBUG_RETURN(error);
+}
+
+/**
+ Write an event to the binary log.
+*/
+
+bool MYSQL_BIN_LOG::write(Log_event *event_info)
{
THD *thd= event_info->thd;
bool error= 1;
- DBUG_ENTER("MYSQL_LOG::write(Log_event *)");
+ DBUG_ENTER("MYSQL_BIN_LOG::write(Log_event *)");
if (thd->binlog_evt_union.do_union)
{
@@ -1862,7 +3907,21 @@ bool MYSQL_LOG::write(Log_event *event_info)
thd->binlog_evt_union.unioned_events_trans |= event_info->cache_stmt;
DBUG_RETURN(0);
}
-
+
+ /*
+ Flush the pending rows event to the transaction cache or to the
+ log file. Since this function potentially aquire the LOCK_log
+ mutex, we do this before aquiring the LOCK_log mutex in this
+ function.
+
+ We only end the statement if we are in a top-level statement. If
+ we are inside a stored function, we do not end the statement since
+ this will close all tables on the slave.
+ */
+ bool const end_stmt=
+ thd->prelocked_mode && thd->lex->requires_prelocking();
+ thd->binlog_flush_pending_rows_event(end_stmt);
+
pthread_mutex_lock(&LOCK_log);
/*
@@ -1881,15 +3940,14 @@ bool MYSQL_LOG::write(Log_event *event_info)
*/
const char *local_db= event_info->get_db();
if ((thd && !(thd->options & OPTION_BIN_LOG)) ||
- (!db_ok(local_db, binlog_do_db, binlog_ignore_db)))
+ (!binlog_filter->db_ok(local_db)))
{
VOID(pthread_mutex_unlock(&LOCK_log));
- DBUG_PRINT("error",("!db_ok('%s')", local_db));
DBUG_RETURN(0);
}
#endif /* HAVE_REPLICATION */
-#ifdef USING_TRANSACTIONS
+#if defined(USING_TRANSACTIONS)
/*
Should we write to the binlog cache or to the binlog on disk?
Write to the binlog cache if:
@@ -1901,40 +3959,31 @@ bool MYSQL_LOG::write(Log_event *event_info)
*/
if (opt_using_transactions && thd)
{
- IO_CACHE *trans_log= (IO_CACHE*)thd->ha_data[binlog_hton.slot];
+ if (thd->binlog_setup_trx_data())
+ goto err;
- if (event_info->get_cache_stmt())
+ 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() || trans_log_pos != 0)
{
- if (!trans_log)
- {
- thd->ha_data[binlog_hton.slot]= trans_log= (IO_CACHE *)
- my_malloc(sizeof(IO_CACHE), MYF(MY_ZEROFILL));
- if (!trans_log || open_cached_file(trans_log, mysql_tmpdir,
- LOG_PREFIX,
- binlog_cache_size, MYF(MY_WME)))
- {
- my_free((gptr)trans_log, MYF(MY_ALLOW_ZERO_PTR));
- thd->ha_data[binlog_hton.slot]= trans_log= 0;
- goto err;
- }
- trans_log->end_of_file= max_binlog_cache_size;
- trans_register_ha(thd,
- test(thd->options & (OPTION_NOT_AUTOCOMMIT |
- OPTION_BEGIN)),
- &binlog_hton);
- }
- else if (!my_b_tell(trans_log))
- trans_register_ha(thd,
- test(thd->options & (OPTION_NOT_AUTOCOMMIT |
- OPTION_BEGIN)),
- &binlog_hton);
+ DBUG_PRINT("info", ("Using trans_log: cache: %d, trans_log_pos: %lu",
+ event_info->get_cache_stmt(),
+ (ulong) trans_log_pos));
+ if (trans_log_pos == 0)
+ thd->binlog_start_trans_and_stmt();
file= trans_log;
}
- else if (trans_log && my_b_tell(trans_log))
- file= trans_log;
+ /*
+ TODO as Mats suggested, for all the cases above where we write to
+ trans_log, it sounds unnecessary to lock LOCK_log. We should rather
+ test first if we want to write to trans_log, and if not, lock
+ LOCK_log.
+ */
}
-#endif
- DBUG_PRINT("info",("event type=%d",event_info->get_type_code()));
+#endif /* USING_TRANSACTIONS */
+ DBUG_PRINT("info",("event type: %d",event_info->get_type_code()));
/*
No check for auto events flag here - this write method should
@@ -1946,42 +3995,54 @@ bool MYSQL_LOG::write(Log_event *event_info)
of the SQL command
*/
+ /*
+ If row-based binlogging, Insert_id, Rand and other kind of "setting
+ context" events are not needed.
+ */
if (thd)
{
- if (thd->last_insert_id_used_bin_log)
- {
- Intvar_log_event e(thd,(uchar) LAST_INSERT_ID_EVENT,
- thd->current_insert_id);
- if (e.write(file))
- goto err;
- }
- if (thd->insert_id_used)
- {
- Intvar_log_event e(thd,(uchar) INSERT_ID_EVENT,thd->last_insert_id);
- if (e.write(file))
- goto err;
- }
- if (thd->rand_used)
- {
- Rand_log_event e(thd,thd->rand_saved_seed1,thd->rand_saved_seed2);
- if (e.write(file))
- goto err;
- }
- if (thd->user_var_events.elements)
+ if (!thd->current_stmt_binlog_row_based)
{
- for (uint i= 0; i < thd->user_var_events.elements; i++)
- {
- BINLOG_USER_VAR_EVENT *user_var_event;
- get_dynamic(&thd->user_var_events,(gptr) &user_var_event, i);
- 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);
- if (e.write(file))
- goto err;
- }
+ 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);
+ if (e.write(file))
+ goto err;
+ }
+ if (thd->auto_inc_intervals_in_cur_stmt_for_binlog.nb_elements() > 0)
+ {
+ DBUG_PRINT("info",("number of auto_inc intervals: %u",
+ thd->auto_inc_intervals_in_cur_stmt_for_binlog.
+ nb_elements()));
+ Intvar_log_event e(thd, (uchar) INSERT_ID_EVENT,
+ thd->auto_inc_intervals_in_cur_stmt_for_binlog.
+ minimum());
+ if (e.write(file))
+ goto err;
+ }
+ if (thd->rand_used)
+ {
+ Rand_log_event e(thd,thd->rand_saved_seed1,thd->rand_saved_seed2);
+ if (e.write(file))
+ goto err;
+ }
+ if (thd->user_var_events.elements)
+ {
+ for (uint i= 0; i < thd->user_var_events.elements; i++)
+ {
+ BINLOG_USER_VAR_EVENT *user_var_event;
+ get_dynamic(&thd->user_var_events,(uchar*) &user_var_event, i);
+ 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);
+ if (e.write(file))
+ goto err;
+ }
+ }
}
}
@@ -2012,22 +4073,95 @@ err:
}
}
+ if (event_info->flags & LOG_EVENT_UPDATE_TABLE_MAP_VERSION_F)
+ ++m_table_map_version;
+
pthread_mutex_unlock(&LOCK_log);
DBUG_RETURN(error);
}
-void MYSQL_LOG::rotate_and_purge(uint flags)
+
+int error_log_print(enum loglevel level, const char *format,
+ va_list args)
+{
+ return logger.error_log_print(level, format, args);
+}
+
+
+bool slow_log_print(THD *thd, const char *query, uint query_length,
+ ulonglong current_utime)
+{
+ return logger.slow_log_print(thd, query, query_length, current_utime);
+}
+
+
+bool LOGGER::log_command(THD *thd, enum enum_server_command command)
+{
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+ Security_context *sctx= thd->security_ctx;
+#endif
+ /*
+ Log command if we have at least one log event handler enabled and want
+ to log this king of commands
+ */
+ if (*general_log_handler_list && (what_to_log & (1L << (uint) command)))
+ {
+ if ((thd->options & OPTION_LOG_OFF)
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+ && (sctx->master_access & SUPER_ACL)
+#endif
+ )
+ {
+ /* No logging */
+ return FALSE;
+ }
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+bool general_log_print(THD *thd, enum enum_server_command command,
+ const char *format, ...)
+{
+ va_list args;
+ uint error= 0;
+
+ /* Print the message to the buffer if we want to log this king of commands */
+ if (! logger.log_command(thd, command))
+ return FALSE;
+
+ va_start(args, format);
+ error= logger.general_log_print(thd, command, format, args);
+ va_end(args);
+
+ return error;
+}
+
+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))
+ return logger.general_log_write(thd, command, query, query_length);
+
+ return FALSE;
+}
+
+void MYSQL_BIN_LOG::rotate_and_purge(uint flags)
{
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))
{
- new_file(0);
+ new_file_without_locking();
#ifdef HAVE_REPLICATION
if (expire_logs_days)
{
- time_t purge_time= time(0) - expire_logs_days*24*60*60;
+ time_t purge_time= my_time(0) - expire_logs_days*24*60*60;
if (purge_time >= 0)
purge_logs_before_date(purge_time);
}
@@ -2037,7 +4171,7 @@ void MYSQL_LOG::rotate_and_purge(uint flags)
pthread_mutex_unlock(&LOCK_log);
}
-uint MYSQL_LOG::next_file_id()
+uint MYSQL_BIN_LOG::next_file_id()
{
uint res;
pthread_mutex_lock(&LOCK_log);
@@ -2048,205 +4182,239 @@ uint MYSQL_LOG::next_file_id()
/*
- Write a cached log entry to the binary log
+ Write the contents of a cache to the binary log.
SYNOPSIS
- write()
- thd
- cache The cache to copy to the binlog
-
- NOTE
- - We only come here if there is something in the cache.
- - The thing in the cache is always a complete transaction
- - 'cache' needs to be reinitialized after this functions returns.
+ write_cache()
+ cache Cache to write to the binary log
+ lock_log True if the LOCK_log mutex should be aquired, false otherwise
+ sync_log True if the log should be flushed and sync:ed
- IMPLEMENTATION
- - To support transaction over replication, we wrap the transaction
- with BEGIN/COMMIT or BEGIN/ROLLBACK in the binary log.
- If a transaction that only involves transactional tables is
- rolled back, we do not binlog it. However, we write a
- BEGIN/ROLLBACK block when a non-transactional table was updated
- in a transaction which was rolled back. This is to ensure that
- the same updates are run on the slave.
-*/
+ DESCRIPTION
+ Write the contents of the cache to the binary log. The cache will
+ be reset as a READ_CACHE to be able to read the contents from it.
+ */
-bool MYSQL_LOG::write(THD *thd, IO_CACHE *cache, Log_event *commit_event)
+int MYSQL_BIN_LOG::write_cache(IO_CACHE *cache, bool lock_log, bool sync_log)
{
- DBUG_ENTER("MYSQL_LOG::write(THD *, IO_CACHE *, Log_event *)");
- VOID(pthread_mutex_lock(&LOCK_log));
+ Mutex_sentry sentry(lock_log ? &LOCK_log : NULL);
- /* NULL would represent nothing to replicate after ROLLBACK */
- DBUG_ASSERT(commit_event != NULL);
+ 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;
+ long val;
+ uchar header[LOG_EVENT_HEADER_LEN];
- DBUG_ASSERT(is_open());
- if (likely(is_open())) // Should always be true
+ /*
+ The events in the buffer have incorrect end_log_pos data
+ (relative to beginning of group rather than absolute),
+ so we'll recalculate them in situ so the binlog is always
+ correct, even in the middle of a group. This is possible
+ because we now know the start position of the group (the
+ offset of this cache in the log, if you will); all we need
+ to do is to find all event-headers, and add the position of
+ the group to the end_log_pos of each event. This is pretty
+ straight forward, except that we read the cache in segments,
+ so an event-header might end up on the cache-border and get
+ split.
+ */
+
+ group= (uint)my_b_tell(&log_file);
+ hdr_offs= carry= 0;
+
+ do
{
- uint length, group, carry, hdr_offs, val;
- byte header[LOG_EVENT_HEADER_LEN];
/*
- Log "BEGIN" at the beginning of every transaction. Here, a
- transaction is either a BEGIN..COMMIT block or a single
- statement in autocommit mode.
- */
- Query_log_event qinfo(thd, STRING_WITH_LEN("BEGIN"), TRUE, FALSE);
- /*
- Imagine this is rollback due to net timeout, after all
- statements of the transaction succeeded. Then we want a
- zero-error code in BEGIN. In other words, if there was a
- really serious error code it's already in the statement's
- events, there is no need to put it also in this internally
- generated event, and as this event is generated late it would
- lead to false alarms.
-
- This is safer than thd->clear_error() against kills at shutdown.
+ if we only got a partial header in the last iteration,
+ get the other half now and process a full header.
*/
- qinfo.error_code= 0;
- /*
- Now this Query_log_event has artificial log_pos 0. It must be
- adjusted to reflect the real position in the log. Not doing it
- would confuse the slave: it would prevent this one from
- knowing where he is in the master's binlog, which would result
- in wrong positions being shown to the user, MASTER_POS_WAIT
- undue waiting etc.
- */
- if (qinfo.write(&log_file))
- goto err;
-
- /* Read from the file used to cache the queries .*/
- if (reinit_io_cache(cache, READ_CACHE, 0, 0, 0))
- goto err;
+ if (unlikely(carry > 0))
+ {
+ DBUG_ASSERT(carry < LOG_EVENT_HEADER_LEN);
- length= my_b_bytes_in_cache(cache);
- DBUG_EXECUTE_IF("half_binlogged_transaction", length-=100;);
+ /* assemble both halves */
+ memcpy(&header[carry], (char *)cache->read_pos, LOG_EVENT_HEADER_LEN - carry);
- /*
- The events in the buffer have incorrect end_log_pos data
- (relative to beginning of group rather than absolute),
- so we'll recalculate them in situ so the binlog is always
- correct, even in the middle of a group. This is possible
- because we now know the start position of the group (the
- offset of this cache in the log, if you will); all we need
- to do is to find all event-headers, and add the position of
- the group to the end_log_pos of each event. This is pretty
- straight forward, except that we read the cache in segments,
- so an event-header might end up on the cache-border and get
- split.
- */
+ /* fix end_log_pos */
+ val= uint4korr(&header[LOG_POS_OFFSET]) + group;
+ int4store(&header[LOG_POS_OFFSET], val);
- group= (uint)my_b_tell(&log_file);
- hdr_offs= carry= 0;
-
- do
- {
+ /* write the first half of the split header */
+ if (my_b_write(&log_file, header, carry))
+ return ER_ERROR_ON_WRITE;
/*
- if we only got a partial header in the last iteration,
- get the other half now and process a full header.
+ copy fixed second half of header to cache so the correct
+ version will be written later.
*/
- if (unlikely(carry > 0))
- {
- DBUG_ASSERT(carry < LOG_EVENT_HEADER_LEN);
+ memcpy((char *)cache->read_pos, &header[carry], LOG_EVENT_HEADER_LEN - carry);
- /* assemble both halves */
- memcpy(&header[carry], (char *)cache->read_pos, LOG_EVENT_HEADER_LEN - carry);
+ /* next event header at ... */
+ hdr_offs = uint4korr(&header[EVENT_LEN_OFFSET]) - carry;
- /* fix end_log_pos */
- val= uint4korr(&header[LOG_POS_OFFSET]) + group;
- int4store(&header[LOG_POS_OFFSET], val);
+ carry= 0;
+ }
- /* write the first half of the split header */
- if (my_b_write(&log_file, header, carry))
- goto err;
+ /* if there is anything to write, process it. */
+
+ if (likely(length > 0))
+ {
+ /*
+ process all event-headers in this (partial) cache.
+ if next header is beyond current read-buffer,
+ we'll get it later (though not necessarily in the
+ very next iteration, just "eventually").
+ */
+ while (hdr_offs < length)
+ {
/*
- copy fixed second half of header to cache so the correct
- version will be written later.
+ partial header only? save what we can get, process once
+ we get the rest.
*/
- memcpy((char *)cache->read_pos, &header[carry], LOG_EVENT_HEADER_LEN - carry);
- /* next event header at ... */
- hdr_offs = uint4korr(&header[EVENT_LEN_OFFSET]) - carry;
+ if (hdr_offs + LOG_EVENT_HEADER_LEN > length)
+ {
+ carry= length - hdr_offs;
+ memcpy(header, (char *)cache->read_pos + hdr_offs, carry);
+ length= hdr_offs;
+ }
+ else
+ {
+ /* we've got a full event-header, and it came in one piece */
- carry= 0;
- }
+ uchar *log_pos= (uchar *)cache->read_pos + hdr_offs + LOG_POS_OFFSET;
- /* if there is anything to write, process it. */
+ /* fix end_log_pos */
+ val= uint4korr(log_pos) + group;
+ int4store(log_pos, val);
- if (likely(length > 0))
- {
- /*
- process all event-headers in this (partial) cache.
- if next header is beyond current read-buffer,
- we'll get it later (though not necessarily in the
- very next iteration, just "eventually").
- */
+ /* next event header at ... */
+ log_pos= (uchar *)cache->read_pos + hdr_offs + EVENT_LEN_OFFSET;
+ hdr_offs += uint4korr(log_pos);
- while (hdr_offs < length)
- {
- /*
- partial header only? save what we can get, process once
- we get the rest.
- */
+ }
+ }
- if (hdr_offs + LOG_EVENT_HEADER_LEN > length)
- {
- carry= length - hdr_offs;
- memcpy(header, (char *)cache->read_pos + hdr_offs, carry);
- length= hdr_offs;
- }
- else
- {
- /* we've got a full event-header, and it came in one piece */
+ /*
+ Adjust hdr_offs. Note that it may still point beyond the segment
+ read in the next iteration; if the current event is very long,
+ it may take a couple of read-iterations (and subsequent adjustments
+ of hdr_offs) for it to point into the then-current segment.
+ If we have a split header (!carry), hdr_offs will be set at the
+ beginning of the next iteration, overwriting the value we set here:
+ */
+ hdr_offs -= length;
+ }
- uchar *log_pos= (uchar *)cache->read_pos + hdr_offs + LOG_POS_OFFSET;
+ /* Write data to the binary log file */
+ if (my_b_write(&log_file, cache->read_pos, length))
+ return ER_ERROR_ON_WRITE;
+ cache->read_pos=cache->read_end; // Mark buffer used up
+ } while ((length= my_b_fill(cache)));
- /* fix end_log_pos */
- val= uint4korr(log_pos) + group;
- int4store(log_pos, val);
+ DBUG_ASSERT(carry == 0);
- /* next event header at ... */
- log_pos= (uchar *)cache->read_pos + hdr_offs + EVENT_LEN_OFFSET;
- hdr_offs += uint4korr(log_pos);
+ if (sync_log)
+ flush_and_sync();
- }
- }
+ return 0; // All OK
+}
- /*
- Adjust hdr_offs. Note that it may still point beyond the segment
- read in the next iteration; if the current event is very long,
- it may take a couple of read-iterations (and subsequent adjustments
- of hdr_offs) for it to point into the then-current segment.
- If we have a split header (!carry), hdr_offs will be set at the
- beginning of the next iteration, overwriting the value we set here:
- */
- hdr_offs -= length;
- }
+/**
+ Write a cached log entry to the binary log.
+ - To support transaction over replication, we wrap the transaction
+ with BEGIN/COMMIT or BEGIN/ROLLBACK in the binary log.
+ We want to write a BEGIN/ROLLBACK block when a non-transactional table
+ was updated in a transaction which was rolled back. This is to ensure
+ that the same updates are run on the slave.
+
+ @param thd
+ @param cache The cache to copy to the binlog
+ @param commit_event The commit event to print after writing the
+ contents of the cache.
+
+ @note
+ We only come here if there is something in the cache.
+ @note
+ The thing in the cache is always a complete transaction.
+ @note
+ 'cache' needs to be reinitialized after this functions returns.
+*/
- /* Write data to the binary log file */
- if (my_b_write(&log_file, cache->read_pos, length))
- goto err;
- cache->read_pos=cache->read_end; // Mark buffer used up
- DBUG_EXECUTE_IF("half_binlogged_transaction", goto DBUG_skip_commit;);
- } while ((length=my_b_fill(cache)));
+bool MYSQL_BIN_LOG::write(THD *thd, IO_CACHE *cache, Log_event *commit_event)
+{
+ DBUG_ENTER("MYSQL_BIN_LOG::write(THD *, IO_CACHE *, Log_event *)");
+ VOID(pthread_mutex_lock(&LOCK_log));
- DBUG_ASSERT(carry == 0);
+ /* NULL would represent nothing to replicate after ROLLBACK */
+ DBUG_ASSERT(commit_event != NULL);
- if (commit_event->write(&log_file))
- goto err;
-#ifndef DBUG_OFF
-DBUG_skip_commit:
-#endif
- if (flush_and_sync())
- goto err;
- DBUG_EXECUTE_IF("half_binlogged_transaction", abort(););
- if (cache->error) // Error on read
+ DBUG_ASSERT(is_open());
+ if (likely(is_open())) // Should always be true
+ {
+ /*
+ We only bother to write to the binary log if there is anything
+ to write.
+ */
+ if (my_b_tell(cache) > 0)
{
- sql_print_error(ER(ER_ERROR_ON_READ), cache->file_name, errno);
- write_error=1; // Don't give more errors
- goto err;
+ /*
+ Log "BEGIN" at the beginning of every transaction. Here, a
+ transaction is either a BEGIN..COMMIT block or a single
+ statement in autocommit mode.
+ */
+ Query_log_event qinfo(thd, STRING_WITH_LEN("BEGIN"), TRUE, FALSE);
+ /*
+ Imagine this is rollback due to net timeout, after all
+ statements of the transaction succeeded. Then we want a
+ zero-error code in BEGIN. In other words, if there was a
+ really serious error code it's already in the statement's
+ events, there is no need to put it also in this internally
+ generated event, and as this event is generated late it would
+ lead to false alarms.
+
+ This is safer than thd->clear_error() against kills at shutdown.
+ */
+ qinfo.error_code= 0;
+ /*
+ Now this Query_log_event has artificial log_pos 0. It must be
+ adjusted to reflect the real position in the log. Not doing it
+ would confuse the slave: it would prevent this one from
+ knowing where he is in the master's binlog, which would result
+ in wrong positions being shown to the user, MASTER_POS_WAIT
+ undue waiting etc.
+ */
+ if (qinfo.write(&log_file))
+ goto err;
+
+ DBUG_EXECUTE_IF("crash_before_writing_xid",
+ {
+ if ((write_error= write_cache(cache, false, true)))
+ DBUG_PRINT("info", ("error writing binlog cache: %d",
+ write_error));
+ DBUG_PRINT("info", ("crashing before writing xid"));
+ abort();
+ });
+
+ if ((write_error= write_cache(cache, false, false)))
+ goto err;
+
+ if (commit_event && commit_event->write(&log_file))
+ goto err;
+ if (flush_and_sync())
+ goto err;
+ DBUG_EXECUTE_IF("half_binlogged_transaction", abort(););
+ if (cache->error) // Error on read
+ {
+ sql_print_error(ER(ER_ERROR_ON_READ), cache->file_name, errno);
+ write_error=1; // Don't give more errors
+ goto err;
+ }
+ signal_update();
}
- signal_update();
+
/*
if commit_event is Xid_log_event, increase the number of
prepared_xids (it's decreasd in ::unlog()). Binlog cannot be rotated
@@ -2255,7 +4423,7 @@ DBUG_skip_commit:
If the commit_event is not Xid_log_event (then it's a Query_log_event)
rotate binlog, if necessary.
*/
- if (commit_event->get_type_code() == XID_EVENT)
+ if (commit_event && commit_event->get_type_code() == XID_EVENT)
{
pthread_mutex_lock(&LOCK_prep_xids);
prepared_xids++;
@@ -2279,152 +4447,21 @@ err:
}
-/*
- Write to the slow query log.
-*/
-
-bool MYSQL_LOG::write(THD *thd,const char *query, uint query_length,
- time_t query_start_arg)
-{
- bool error=0;
- time_t current_time;
- if (!is_open())
- return 0;
- DBUG_ENTER("MYSQL_LOG::write");
-
- VOID(pthread_mutex_lock(&LOCK_log));
- if (is_open())
- { // Safety agains reopen
- int tmp_errno=0;
- char buff[80],*end;
- end=buff;
- if (!(thd->options & OPTION_UPDATE_LOG))
- {
- VOID(pthread_mutex_unlock(&LOCK_log));
- DBUG_RETURN(0);
- }
- if (!(specialflag & SPECIAL_SHORT_LOG_FORMAT) || query_start_arg)
- {
- Security_context *sctx= thd->security_ctx;
- current_time=time(NULL);
- if (current_time != last_time)
- {
- last_time=current_time;
- struct tm tm_tmp;
- struct tm *start;
- localtime_r(&current_time,&tm_tmp);
- start=&tm_tmp;
- /* Note that my_b_write() assumes it knows the length for this */
- sprintf(buff,"# Time: %02d%02d%02d %2d:%02d:%02d\n",
- start->tm_year % 100,
- start->tm_mon+1,
- start->tm_mday,
- start->tm_hour,
- start->tm_min,
- start->tm_sec);
- if (my_b_write(&log_file, (byte*) buff,24))
- tmp_errno=errno;
- }
- if (my_b_printf(&log_file, "# User@Host: %s[%s] @ %s [%s]\n",
- sctx->priv_user ?
- sctx->priv_user : "",
- sctx->user ? sctx->user : "",
- sctx->host ? sctx->host : "",
- sctx->ip ? sctx->ip : "") ==
- (uint) -1)
- tmp_errno=errno;
- }
- if (query_start_arg)
- {
- /* For slow query log */
- if (my_b_printf(&log_file,
- "# Query_time: %lu Lock_time: %lu Rows_sent: %lu Rows_examined: %lu\n",
- (ulong) (current_time - query_start_arg),
- (ulong) (thd->time_after_lock - query_start_arg),
- (ulong) thd->sent_row_count,
- (ulong) thd->examined_row_count) == (uint) -1)
- tmp_errno=errno;
- }
- if (thd->db && strcmp(thd->db,db))
- { // Database changed
- if (my_b_printf(&log_file,"use %s;\n",thd->db) == (uint) -1)
- tmp_errno=errno;
- strmov(db,thd->db);
- }
- if (thd->last_insert_id_used_bin_log)
- {
- end=strmov(end,",last_insert_id=");
- end=longlong10_to_str((longlong) thd->current_insert_id,end,-10);
- }
- // Save value if we do an insert.
- if (thd->insert_id_used)
- {
- if (!(specialflag & SPECIAL_SHORT_LOG_FORMAT))
- {
- end=strmov(end,",insert_id=");
- end=longlong10_to_str((longlong) thd->last_insert_id,end,-10);
- }
- }
- if (thd->query_start_used)
- {
- if (query_start_arg != thd->query_start())
- {
- query_start_arg=thd->query_start();
- end=strmov(end,",timestamp=");
- end=int10_to_str((long) query_start_arg,end,10);
- }
- }
- if (end != buff)
- {
- *end++=';';
- *end='\n';
- if (my_b_write(&log_file, (byte*) "SET ",4) ||
- my_b_write(&log_file, (byte*) buff+1,(uint) (end-buff)))
- tmp_errno=errno;
- }
- if (!query)
- {
- end=strxmov(buff, "# administrator command: ",
- command_name[thd->command], NullS);
- query_length=(ulong) (end-buff);
- query=buff;
- }
- if (my_b_write(&log_file, (byte*) query,query_length) ||
- my_b_write(&log_file, (byte*) ";\n",2) ||
- flush_io_cache(&log_file))
- tmp_errno=errno;
- if (tmp_errno)
- {
- error=1;
- if (! write_error)
- {
- write_error=1;
- sql_print_error(ER(ER_ERROR_ON_WRITE),name,error);
- }
- }
- }
- VOID(pthread_mutex_unlock(&LOCK_log));
- DBUG_RETURN(error);
-}
-
-
-/*
- Wait until we get a signal that the binary log has been updated
+/**
+ Wait until we get a signal that the binary log has been updated.
- SYNOPSIS
- wait_for_update()
- thd Thread variable
- 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.
+ @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.
- NOTES
+ @note
One must have a lock on LOCK_log before calling this function.
This lock will be released before return! That's required by
THD::enter_cond() (see NOTES in sql_class.h).
*/
-void MYSQL_LOG::wait_for_update(THD* thd, bool is_slave)
+void MYSQL_BIN_LOG::wait_for_update(THD* thd, bool is_slave)
{
const char *old_msg;
DBUG_ENTER("wait_for_update");
@@ -2441,27 +4478,25 @@ void MYSQL_LOG::wait_for_update(THD* thd, bool is_slave)
}
-/*
- Close the log file
+/**
+ Close the log file.
- SYNOPSIS
- close()
- exiting Bitmask for one or more of the following bits:
- LOG_CLOSE_INDEX if we should close the index file
- 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
+ @param exiting Bitmask for one or more of the following bits:
+ - LOG_CLOSE_INDEX : if we should close the index file
+ - 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
- NOTES
+ @note
One can do an open on the object at once after doing a close.
The internal structures are not freed until cleanup() is called
*/
-void MYSQL_LOG::close(uint exiting)
+void MYSQL_BIN_LOG::close(uint exiting)
{ // One can't set log_type here!
- DBUG_ENTER("MYSQL_LOG::close");
+ DBUG_ENTER("MYSQL_BIN_LOG::close");
DBUG_PRINT("enter",("exiting: %d", (int) exiting));
- if (log_type != LOG_CLOSED && log_type != LOG_TO_BE_OPENED)
+ if (log_state == LOG_OPENED)
{
#ifdef HAVE_REPLICATION
if (log_type == LOG_BIN && !no_auto_events &&
@@ -2473,26 +4508,25 @@ void MYSQL_LOG::close(uint exiting)
signal_update();
}
#endif /* HAVE_REPLICATION */
- end_io_cache(&log_file);
/* don't pwrite in a file opened with O_APPEND - it doesn't work */
if (log_file.type == WRITE_CACHE && log_type == LOG_BIN)
{
my_off_t offset= BIN_LOG_HEADER_SIZE + FLAGS_OFFSET;
- byte flags=0; // clearing LOG_EVENT_BINLOG_IN_USE_F
+ 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));
+ /*
+ 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
+ original position on system that doesn't support pwrite().
+ */
+ my_seek(log_file.file, org_position, MY_SEEK_SET, MYF(0));
}
- if (my_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)
- {
- write_error=1;
- sql_print_error(ER(ER_ERROR_ON_WRITE), name, errno);
- }
+ /* this will cleanup IO_CACHE, sync and close the file */
+ MYSQL_LOG::close(exiting);
}
/*
@@ -2509,13 +4543,13 @@ void MYSQL_LOG::close(uint exiting)
sql_print_error(ER(ER_ERROR_ON_WRITE), index_file_name, errno);
}
}
- log_type= (exiting & LOG_CLOSE_TO_BE_OPENED) ? LOG_TO_BE_OPENED : LOG_CLOSED;
+ log_state= (exiting & LOG_CLOSE_TO_BE_OPENED) ? LOG_TO_BE_OPENED : LOG_CLOSED;
safeFree(name);
DBUG_VOID_RETURN;
}
-void MYSQL_LOG::set_max_size(ulong max_size_arg)
+void MYSQL_BIN_LOG::set_max_size(ulong max_size_arg)
{
/*
We need to take locks, otherwise this may happen:
@@ -2524,7 +4558,7 @@ void MYSQL_LOG::set_max_size(ulong max_size_arg)
uses the old_max_size argument, so max_size_arg has been overwritten and
it's like if the SET command was never run.
*/
- DBUG_ENTER("MYSQL_LOG::set_max_size");
+ DBUG_ENTER("MYSQL_BIN_LOG::set_max_size");
pthread_mutex_lock(&LOCK_log);
if (is_open())
max_size= max_size_arg;
@@ -2533,21 +4567,20 @@ void MYSQL_LOG::set_max_size(ulong max_size_arg)
}
-/*
- Check if a string is a valid number
+/**
+ Check if a string is a valid number.
- SYNOPSIS
- test_if_number()
- str String to test
- res Store value here
- allow_wildcards Set to 1 if we should ignore '%' and '_'
+ @param str String to test
+ @param res Store value here
+ @param allow_wildcards Set to 1 if we should ignore '%' and '_'
- NOTE
+ @note
For the moment the allow_wildcards argument is not used
Should be move to some other file.
- RETURN VALUES
+ @retval
1 String is a number
+ @retval
0 Error
*/
@@ -2612,17 +4645,21 @@ bool flush_error_log()
(void) my_delete(err_temp, MYF(0));
if (freopen(err_temp,"a+",stdout))
{
+ int fd;
+ size_t bytes;
+ uchar buf[IO_SIZE];
+
freopen(err_temp,"a+",stderr);
(void) my_delete(err_renamed, MYF(0));
my_rename(log_error_file,err_renamed,MYF(0));
if (freopen(log_error_file,"a+",stdout))
freopen(log_error_file,"a+",stderr);
- int fd, bytes;
- char buf[IO_SIZE];
+
if ((fd = my_open(err_temp, O_RDONLY, MYF(0))) >= 0)
{
- while ((bytes = (int) my_read(fd, (byte*) buf, IO_SIZE, MYF(0))) > 0)
- my_fwrite(stderr, (byte*) buf, bytes, MYF(0));
+ while ((bytes= my_read(fd, buf, IO_SIZE, MYF(0))) &&
+ bytes != MY_FILE_ERROR)
+ my_fwrite(stderr, buf, bytes, MYF(0));
my_close(fd, MYF(0));
}
(void) my_delete(err_temp, MYF(0));
@@ -2641,9 +4678,9 @@ bool flush_error_log()
return result;
}
-void MYSQL_LOG::signal_update()
+void MYSQL_BIN_LOG::signal_update()
{
- DBUG_ENTER("MYSQL_LOG::signal_update");
+ DBUG_ENTER("MYSQL_BIN_LOG::signal_update");
pthread_cond_broadcast(&update_cond);
DBUG_VOID_RETURN;
}
@@ -2684,24 +4721,21 @@ static void print_buffer_to_nt_eventlog(enum loglevel level, char *buff,
#endif /* __NT__ */
-/*
+/**
Prints a printf style message to the error log and, under NT, to the
Windows event log.
- SYNOPSIS
- vprint_msg_to_log()
- event_type Type of event to write (Error, Warning, or Info)
- format Printf style format of message
- args va_list list of arguments for the message
+ This function prints the message into a buffer and then sends that buffer
+ to other functions to write that message to other logging sources.
- NOTE
-
- IMPLEMENTATION
- This function prints the message into a buffer and then sends that buffer
- to other functions to write that message to other logging sources.
+ @param event_type Type of event to write (Error, Warning, or Info)
+ @param format Printf style format of message
+ @param args va_list list of arguments for the message
- RETURN VALUES
- void
+ @returns
+ The function always returns 0. The return value is present in the
+ signature to be compatible with other logging routines, which could
+ return an error (e.g. logging to the log tables)
*/
#ifndef EMBEDDED_LIBRARY
@@ -2715,9 +4749,10 @@ static void print_buffer_to_file(enum loglevel level, const char *buffer)
VOID(pthread_mutex_lock(&LOCK_error_log));
- skr=time(NULL);
+ skr= my_time(0);
localtime_r(&skr, &tm_tmp);
start=&tm_tmp;
+
fprintf(stderr, "%02d%02d%02d %2d:%02d:%02d [%s] %s\n",
start->tm_year % 100,
start->tm_mon+1,
@@ -2736,7 +4771,7 @@ static void print_buffer_to_file(enum loglevel level, const char *buffer)
}
-void vprint_msg_to_log(enum loglevel level, const char *format, va_list args)
+int vprint_msg_to_log(enum loglevel level, const char *format, va_list args)
{
char buff[1024];
size_t length;
@@ -2749,7 +4784,7 @@ void vprint_msg_to_log(enum loglevel level, const char *format, va_list args)
print_buffer_to_nt_eventlog(level, buff, length, sizeof(buff));
#endif
- DBUG_VOID_RETURN;
+ DBUG_RETURN(0);
}
#endif /*EMBEDDED_LIBRARY*/
@@ -2760,7 +4795,7 @@ void sql_print_error(const char *format, ...)
DBUG_ENTER("sql_print_error");
va_start(args, format);
- vprint_msg_to_log(ERROR_LEVEL, format, args);
+ error_log_print(ERROR_LEVEL, format, args);
va_end(args);
DBUG_VOID_RETURN;
@@ -2773,7 +4808,7 @@ void sql_print_warning(const char *format, ...)
DBUG_ENTER("sql_print_warning");
va_start(args, format);
- vprint_msg_to_log(WARNING_LEVEL, format, args);
+ error_log_print(WARNING_LEVEL, format, args);
va_end(args);
DBUG_VOID_RETURN;
@@ -2786,7 +4821,7 @@ void sql_print_information(const char *format, ...)
DBUG_ENTER("sql_print_information");
va_start(args, format);
- vprint_msg_to_log(INFORMATION_LEVEL, format, args);
+ error_log_print(INFORMATION_LEVEL, format, args);
va_end(args);
DBUG_VOID_RETURN;
@@ -2943,16 +4978,18 @@ err:
return 1;
}
-/*
- there is no active page, let's got one from the pool
+/**
+ there is no active page, let's got one from the pool.
- two strategies here:
- 1. take the first from the pool
- 2. if there're waiters - take the one with the most free space
+ Two strategies here:
+ -# take the first from the pool
+ -# if there're waiters - take the one with the most free space.
- TODO page merging. try to allocate adjacent page first,
- so that they can be flushed both in one sync
+ @todo
+ TODO page merging. try to allocate adjacent page first,
+ so that they can be flushed both in one sync
*/
+
void TC_LOG_MMAP::get_active_from_pool()
{
PAGE **p, **best_p=0;
@@ -2995,6 +5032,10 @@ void TC_LOG_MMAP::get_active_from_pool()
pthread_mutex_unlock(&LOCK_pool);
}
+/**
+ @todo
+ perhaps, increase log size ?
+*/
int TC_LOG_MMAP::overflow()
{
/*
@@ -3007,10 +5048,9 @@ int TC_LOG_MMAP::overflow()
return 1; // always return 1
}
-/*
- Record that transaction XID is committed on the persistent storage
+/**
+ Record that transaction XID is committed on the persistent storage.
- NOTES
This function is called in the middle of two-phase commit:
First all resources prepare the transaction, then tc_log->log() is called,
then all resources commit the transaction, then tc_log->unlog() is called.
@@ -3021,18 +5061,18 @@ int TC_LOG_MMAP::overflow()
threads waiting for a page, but then all these threads will be waiting
for a fsync() anyway
- IMPLEMENTATION
If tc_log == MYSQL_LOG then tc_log writes transaction to binlog and
records XID in a special Xid_log_event.
If tc_log = TC_LOG_MMAP then xid is written in a special memory-mapped
log.
- RETURN
- 0 Error
- # "cookie", a number that will be passed as an argument
- to unlog() call. tc_log can define it any way it wants,
- and use for whatever purposes. TC_LOG_MMAP sets it
- to the position in memory where xid was logged to.
+ @retval
+ 0 - error
+ @retval
+ \# - otherwise, "cookie", a number that will be passed as an argument
+ to unlog() call. tc_log can define it any way it wants,
+ and use for whatever purposes. TC_LOG_MMAP sets it
+ to the position in memory where xid was logged to.
*/
int TC_LOG_MMAP::log_xid(THD *thd, my_xid xid)
@@ -3140,9 +5180,9 @@ int TC_LOG_MMAP::sync()
return err;
}
-/*
+/**
erase xid from the page, update page free space counters/pointers.
- cookie points directly to the memory where xid was logged
+ cookie points directly to the memory where xid was logged.
*/
void TC_LOG_MMAP::unlog(ulong cookie, my_xid xid)
@@ -3185,9 +5225,9 @@ void TC_LOG_MMAP::close()
pthread_cond_destroy(&pages[i].cond);
}
case 3:
- my_free((gptr)pages, MYF(0));
+ my_free((uchar*)pages, MYF(0));
case 2:
- my_munmap((byte*)data, (size_t)file_length);
+ my_munmap((char*)data, (size_t)file_length);
case 1:
my_close(fd, MYF(0));
}
@@ -3221,13 +5261,13 @@ int TC_LOG_MMAP::recover()
}
if (hash_init(&xids, &my_charset_bin, tc_log_page_size/3, 0,
- sizeof(my_xid), 0, 0, MYF(0)))
+ sizeof(my_xid), 0, 0, MYF(0)))
goto err1;
for ( ; p < end_p ; p++)
{
for (my_xid *x=p->start; x < p->end; x++)
- if (*x && my_hash_insert(&xids, (byte *)x))
+ if (*x && my_hash_insert(&xids, (uchar *)x))
goto err2; // OOM
}
@@ -3253,16 +5293,17 @@ TC_LOG *tc_log;
TC_LOG_DUMMY tc_log_dummy;
TC_LOG_MMAP tc_log_mmap;
-/*
- Perform heuristic recovery, if --tc-heuristic-recover was used
-
- RETURN VALUE
- 0 no heuristic recovery was requested
- 1 heuristic recovery was performed
+/**
+ Perform heuristic recovery, if --tc-heuristic-recover was used.
- NOTE
+ @note
no matter whether heuristic recovery was successful or not
mysqld must exit. So, return value is the same in both cases.
+
+ @retval
+ 0 no heuristic recovery was requested
+ @retval
+ 1 heuristic recovery was performed
*/
int TC_LOG::using_heuristic_recover()
@@ -3278,10 +5319,11 @@ int TC_LOG::using_heuristic_recover()
}
/****** transaction coordinator log for 2pc - binlog() based solution ******/
-#define TC_LOG_BINLOG MYSQL_LOG
+#define TC_LOG_BINLOG MYSQL_BIN_LOG
-/*
- TODO keep in-memory list of prepared transactions
+/**
+ @todo
+ keep in-memory list of prepared transactions
(add to list in log(), remove on unlog())
and copy it to the new binlog if rotated
but let's check the behaviour of tc_log_page_waits first!
@@ -3372,7 +5414,7 @@ err:
return error;
}
-/* this is called on shutdown, after ha_panic */
+/** This is called on shutdown, after ha_panic. */
void TC_LOG_BINLOG::close()
{
DBUG_ASSERT(prepared_xids==0);
@@ -3380,25 +5422,36 @@ void TC_LOG_BINLOG::close()
pthread_cond_destroy (&COND_prep_xids);
}
-/*
- TODO group commit
+/**
+ @todo
+ group commit
- RETURN
- 0 - error
- 1 - success
+ @retval
+ 0 error
+ @retval
+ 1 success
*/
int TC_LOG_BINLOG::log_xid(THD *thd, my_xid xid)
{
+ DBUG_ENTER("TC_LOG_BINLOG::log");
Xid_log_event xle(thd, xid);
- IO_CACHE *trans_log= (IO_CACHE*)thd->ha_data[binlog_hton.slot];
- return !binlog_end_trans(thd, trans_log, &xle); // invert return value
+ binlog_trx_data *trx_data=
+ (binlog_trx_data*) thd_get_ha_data(thd, binlog_hton);
+ /*
+ We always commit the entire transaction when writing an XID. Also
+ note that the return value is inverted.
+ */
+ DBUG_RETURN(!binlog_end_trans(thd, trx_data, &xle, TRUE));
}
void TC_LOG_BINLOG::unlog(ulong cookie, my_xid xid)
{
pthread_mutex_lock(&LOCK_prep_xids);
- if (--prepared_xids == 0)
+ DBUG_ASSERT(prepared_xids > 0);
+ if (--prepared_xids == 0) {
+ DBUG_PRINT("info", ("prepared_xids=%lu", prepared_xids));
pthread_cond_signal(&COND_prep_xids);
+ }
pthread_mutex_unlock(&LOCK_prep_xids);
rotate_and_purge(0); // as ::write() did not rotate
}
@@ -3411,7 +5464,7 @@ int TC_LOG_BINLOG::recover(IO_CACHE *log, Format_description_log_event *fdle)
if (! fdle->is_valid() ||
hash_init(&xids, &my_charset_bin, TC_LOG_PAGE_SIZE/3, 0,
- sizeof(my_xid), 0, 0, MYF(0)))
+ sizeof(my_xid), 0, 0, MYF(0)))
goto err1;
init_alloc_root(&mem_root, TC_LOG_PAGE_SIZE, TC_LOG_PAGE_SIZE);
@@ -3423,8 +5476,8 @@ int TC_LOG_BINLOG::recover(IO_CACHE *log, Format_description_log_event *fdle)
if (ev->get_type_code() == XID_EVENT)
{
Xid_log_event *xev=(Xid_log_event *)ev;
- byte *x=(byte *)memdup_root(&mem_root, (char *)& xev->xid,
- sizeof(xev->xid));
+ uchar *x= (uchar *) memdup_root(&mem_root, (uchar*) &xev->xid,
+ sizeof(xev->xid));
if (! x)
goto err2;
my_hash_insert(&xids, x);
@@ -3450,3 +5503,45 @@ err1:
return 1;
}
+
+#ifdef INNODB_COMPATIBILITY_HOOKS
+/**
+ Get the file name of the MySQL binlog.
+ @return the name of the binlog file
+*/
+extern "C"
+const char* mysql_bin_log_file_name(void)
+{
+ return mysql_bin_log.get_log_fname();
+}
+/**
+ Get the current position of the MySQL binlog.
+ @return byte offset from the beginning of the binlog
+*/
+extern "C"
+ulonglong mysql_bin_log_file_pos(void)
+{
+ return (ulonglong) mysql_bin_log.get_log_file()->pos_in_file;
+}
+#endif /* INNODB_COMPATIBILITY_HOOKS */
+
+
+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 */,
+ NULL, /* status variables */
+ NULL, /* system variables */
+ NULL /* config options */
+}
+mysql_declare_plugin_end;
diff --git a/sql/log.h b/sql/log.h
new file mode 100644
index 00000000000..d54df8add3b
--- /dev/null
+++ b/sql/log.h
@@ -0,0 +1,584 @@
+/* Copyright (C) 2005 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#ifndef LOG_H
+#define LOG_H
+
+class Relay_log_info;
+
+class Format_description_log_event;
+
+/*
+ Transaction Coordinator log - a base abstract class
+ for two different implementations
+*/
+class TC_LOG
+{
+ public:
+ int using_heuristic_recover();
+ TC_LOG() {}
+ virtual ~TC_LOG() {}
+
+ virtual int open(const char *opt_name)=0;
+ virtual void close()=0;
+ virtual int log_xid(THD *thd, my_xid xid)=0;
+ virtual void unlog(ulong cookie, my_xid xid)=0;
+};
+
+class TC_LOG_DUMMY: public TC_LOG // use it to disable the logging
+{
+public:
+ TC_LOG_DUMMY() {}
+ int open(const char *opt_name) { return 0; }
+ void close() { }
+ int log_xid(THD *thd, my_xid xid) { return 1; }
+ void unlog(ulong cookie, my_xid xid) { }
+};
+
+#ifdef HAVE_MMAP
+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
+ } PAGE_STATE;
+
+ private:
+ typedef struct st_page {
+ struct st_page *next; // page a linked in a fifo queue
+ my_xid *start, *end; // usable area of a page
+ my_xid *ptr; // next xid will be written here
+ 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
+ } PAGE;
+
+ char logname[FN_REFLEN];
+ File fd;
+ my_off_t file_length;
+ uint npages, inited;
+ uchar *data;
+ struct st_page *pages, *syncing, *active, *pool, *pool_last;
+ /*
+ note that, e.g. LOCK_active is only used to protect
+ 'active' pointer, to protect the content of the active page
+ 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;
+
+ public:
+ TC_LOG_MMAP(): inited(0) {}
+ int open(const char *opt_name);
+ void close();
+ int log_xid(THD *thd, my_xid xid);
+ void unlog(ulong cookie, my_xid xid);
+ int recover();
+
+ private:
+ void get_active_from_pool();
+ int sync();
+ int overflow();
+};
+#else
+#define TC_LOG_MMAP TC_LOG_DUMMY
+#endif
+
+extern TC_LOG *tc_log;
+extern TC_LOG_MMAP tc_log_mmap;
+extern TC_LOG_DUMMY tc_log_dummy;
+
+/* log info errors */
+#define LOG_INFO_EOF -1
+#define LOG_INFO_IO -2
+#define LOG_INFO_INVALID -3
+#define LOG_INFO_SEEK -4
+#define LOG_INFO_MEM -6
+#define LOG_INFO_FATAL -7
+#define LOG_INFO_IN_USE -8
+#define LOG_INFO_EMFILE -9
+
+
+/* bitmap to SQL_LOG::close() */
+#define LOG_CLOSE_INDEX 1
+#define LOG_CLOSE_TO_BE_OPENED 2
+#define LOG_CLOSE_STOP_EVENT 4
+
+class Relay_log_info;
+
+typedef struct st_log_info
+{
+ char log_file_name[FN_REFLEN];
+ 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),
+ pos(0), fatal(0)
+ {
+ log_file_name[0] = '\0';
+ pthread_mutex_init(&lock, MY_MUTEX_INIT_FAST);
+ }
+ ~st_log_info() { pthread_mutex_destroy(&lock);}
+} LOG_INFO;
+
+/*
+ Currently we have only 3 kinds of logging functions: old-fashioned
+ logs, stdout and csv logging routines.
+*/
+#define MAX_LOG_HANDLERS_NUM 3
+
+/* log event handler flags */
+#define LOG_NONE 1
+#define LOG_FILE 2
+#define LOG_TABLE 4
+
+class Log_event;
+class Rows_log_event;
+
+enum enum_log_type { LOG_UNKNOWN, LOG_NORMAL, LOG_BIN };
+enum enum_log_state { LOG_OPENED, LOG_CLOSED, LOG_TO_BE_OPENED };
+
+/*
+ TODO use mmap instead of IO_CACHE for binlog
+ (mmap+fsync is two times faster than write+fsync)
+*/
+
+class MYSQL_LOG
+{
+public:
+ MYSQL_LOG();
+ void init_pthread_objects();
+ void cleanup();
+ bool open(const char *log_name,
+ enum_log_type log_type,
+ const char *new_name,
+ enum cache_type io_cache_type_arg);
+ void init(enum_log_type log_type_arg,
+ enum cache_type io_cache_type_arg);
+ void close(uint exiting);
+ inline bool is_open() { return log_state != LOG_CLOSED; }
+ const char *generate_name(const char *log_name, const char *suffix,
+ bool strip_ext, char *buff);
+ 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;
+ char *name;
+ char log_file_name[FN_REFLEN];
+ char time_buff[20], db[NAME_LEN + 1];
+ bool write_error, inited;
+ IO_CACHE log_file;
+ enum_log_type log_type;
+ volatile enum_log_state log_state;
+ enum cache_type io_cache_type;
+ friend class Log_event;
+};
+
+class MYSQL_QUERY_LOG: public MYSQL_LOG
+{
+public:
+ MYSQL_QUERY_LOG() : last_time(0) {}
+ void reopen_file();
+ bool write(time_t event_time, const char *user_host,
+ uint user_host_len, int thread_id,
+ const char *command_type, uint command_type_len,
+ const char *sql_text, uint sql_text_len);
+ bool write(THD *thd, time_t current_time, time_t query_start_arg,
+ const char *user_host, uint user_host_len,
+ ulonglong query_utime, ulonglong lock_utime, bool is_command,
+ const char *sql_text, uint sql_text_len);
+ 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);
+ }
+ 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);
+ }
+
+private:
+ time_t last_time;
+};
+
+class MYSQL_BIN_LOG: public TC_LOG, private MYSQL_LOG
+{
+ private:
+ /* 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;
+ ulonglong bytes_written;
+ IO_CACHE index_file;
+ /*
+ purge_temp is a temp file used in purge_logs so that the index file
+ can be updated before deleting files from disk, yielding better crash
+ recovery. It is created on demand the first time purge_logs is called
+ and then reused for subsequent calls. It is cleaned up in cleanup().
+ */
+ IO_CACHE purge_temp;
+ char index_file_name[FN_REFLEN];
+ /*
+ The max size before rotation (usable only if log_type == LOG_BIN: binary
+ logs and relay logs).
+ For a binlog, max_size should be max_binlog_size.
+ For a relay log, it should be max_relay_log_size if this is non-zero,
+ max_binlog_size otherwise.
+ max_size is set in init(), and dynamically changed (when one does SET
+ GLOBAL MAX_BINLOG_SIZE|MAX_RELAY_LOG_SIZE) by fix_max_binlog_size and
+ fix_max_relay_log_size).
+ */
+ ulong max_size;
+ long prepared_xids; /* for tc log - number of xids to remember */
+ // current file sequence number for load data infile binary logging
+ uint file_id;
+ uint open_count; // For replication
+ int readers_count;
+ bool need_start_event;
+ /*
+ no_auto_events means we don't want any of these automatic events :
+ Start/Rotate/Stop. That is, in 4.x when we rotate a relay log, we don't
+ want a Rotate_log event to be written to the relay log. When we start a
+ relay log etc. So in 4.x this is 1 for relay logs, 0 for binlogs.
+ In 5.0 it's 0 for relay logs too!
+ */
+ bool no_auto_events;
+
+ ulonglong m_table_map_version;
+
+ int write_to_file(IO_CACHE *cache);
+ /*
+ This is used to start writing to a new log file. The difference from
+ new_file() is locking. new_file_without_locking() does not acquire
+ LOCK_log.
+ */
+ void new_file_without_locking();
+ void new_file_impl(bool need_lock);
+
+public:
+ MYSQL_LOG::generate_name;
+ MYSQL_LOG::is_open;
+
+ /* This is relay log */
+ bool is_relay_log;
+
+ /*
+ These describe the log's format. This is used only for relay logs.
+ _for_exec is used by the SQL thread, _for_queue by the I/O thread. It's
+ necessary to have 2 distinct objects, because the I/O thread may be reading
+ events in a different format from what the SQL thread is reading (consider
+ the case of a master which has been upgraded from 5.0 to 5.1 without doing
+ RESET MASTER, or from 4.x to 5.0).
+ */
+ Format_description_log_event *description_event_for_exec,
+ *description_event_for_queue;
+
+ MYSQL_BIN_LOG();
+ /*
+ 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
+ */
+
+ int open(const char *opt_name);
+ void close();
+ int log_xid(THD *thd, my_xid xid);
+ void unlog(ulong cookie, my_xid xid);
+ int recover(IO_CACHE *log, Format_description_log_event *fdle);
+#if !defined(MYSQL_CLIENT)
+ bool is_table_mapped(TABLE *table) const
+ {
+ return table->s->table_map_version == table_map_version();
+ }
+
+ ulonglong table_map_version() const { return m_table_map_version; }
+ void update_table_map_version() { ++m_table_map_version; }
+
+ int flush_and_set_pending_rows_event(THD *thd, Rows_log_event* event);
+ int remove_pending_rows_event(THD *thd);
+
+#endif /* !defined(MYSQL_CLIENT) */
+ void reset_bytes_written()
+ {
+ bytes_written = 0;
+ }
+ void harvest_bytes_written(ulonglong* counter)
+ {
+#ifndef DBUG_OFF
+ char buf1[22],buf2[22];
+#endif
+ DBUG_ENTER("harvest_bytes_written");
+ (*counter)+=bytes_written;
+ DBUG_PRINT("info",("counter: %s bytes_written: %s", llstr(*counter,buf1),
+ llstr(bytes_written,buf2)));
+ bytes_written=0;
+ DBUG_VOID_RETURN;
+ }
+ void set_max_size(ulong max_size_arg);
+ void signal_update();
+ void wait_for_update(THD* thd, bool master_or_slave);
+ void set_need_start_event() { need_start_event = 1; }
+ void init(bool no_auto_events_arg, ulong max_size);
+ void init_pthread_objects();
+ void cleanup();
+ bool open(const char *log_name,
+ enum_log_type log_type,
+ const char *new_name,
+ enum cache_type io_cache_type_arg,
+ bool no_auto_events_arg, ulong max_size,
+ bool null_created);
+ bool open_index_file(const char *index_file_name_arg,
+ const char *log_name);
+ /* Use this to start writing a new log file */
+ void new_file();
+
+ bool write(Log_event* event_info); // binary log write
+ bool write(THD *thd, IO_CACHE *cache, Log_event *commit_event);
+
+ int write_cache(IO_CACHE *cache, bool lock_log, bool flush_and_sync);
+
+ void start_union_events(THD *thd, query_id_t query_id_param);
+ void stop_union_events(THD *thd);
+ bool is_query_in_union(THD *thd, query_id_t query_id_param);
+
+ /*
+ v stands for vector
+ invoked as appendv(buf1,len1,buf2,len2,...,bufn,lenn,0)
+ */
+ bool appendv(const char* buf,uint len,...);
+ bool append(Log_event* ev);
+
+ 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);
+ void rotate_and_purge(uint flags);
+ bool flush_and_sync();
+ int purge_logs(const char *to_log, bool included,
+ bool need_mutex, bool need_update_threads,
+ ulonglong *decrease_log_space);
+ int purge_logs_before_date(time_t purge_time);
+ int purge_first_log(Relay_log_info* rli, bool included);
+ bool reset_logs(THD* thd);
+ void close(uint exiting);
+
+ // iterating through the log index file
+ int find_log_pos(LOG_INFO* linfo, const char* log_name,
+ bool need_mutex);
+ int find_next_log(LOG_INFO* linfo, bool need_mutex);
+ int get_current_log(LOG_INFO* linfo);
+ int raw_get_current_log(LOG_INFO* linfo);
+ uint next_file_id();
+ 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 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 IO_CACHE *get_index_file() { return &index_file;}
+ inline uint32 get_open_count() { return open_count; }
+};
+
+class Log_event_handler
+{
+public:
+ Log_event_handler() {}
+ virtual bool init()= 0;
+ virtual void cleanup()= 0;
+
+ virtual bool log_slow(THD *thd, time_t current_time,
+ time_t query_start_arg, const char *user_host,
+ uint user_host_len, ulonglong query_utime,
+ ulonglong lock_utime, bool is_command,
+ const char *sql_text, uint sql_text_len)= 0;
+ virtual bool log_error(enum loglevel level, const char *format,
+ va_list args)= 0;
+ virtual bool log_general(THD *thd, time_t event_time, const char *user_host,
+ uint user_host_len, int thread_id,
+ const char *command_type, uint command_type_len,
+ const char *sql_text, uint sql_text_len,
+ CHARSET_INFO *client_cs)= 0;
+ virtual ~Log_event_handler() {}
+};
+
+
+int check_if_log_table(uint db_len, const char *db, uint table_name_len,
+ const char *table_name, uint check_if_opened);
+
+class Log_to_csv_event_handler: public Log_event_handler
+{
+ friend class LOGGER;
+
+public:
+ Log_to_csv_event_handler();
+ ~Log_to_csv_event_handler();
+ virtual bool init();
+ virtual void cleanup();
+
+ virtual bool log_slow(THD *thd, time_t current_time,
+ time_t query_start_arg, const char *user_host,
+ uint user_host_len, ulonglong query_utime,
+ ulonglong lock_utime, bool is_command,
+ const char *sql_text, uint sql_text_len);
+ virtual bool log_error(enum loglevel level, const char *format,
+ va_list args);
+ virtual bool log_general(THD *thd, time_t event_time, const char *user_host,
+ uint user_host_len, int thread_id,
+ const char *command_type, uint command_type_len,
+ const char *sql_text, uint sql_text_len,
+ CHARSET_INFO *client_cs);
+
+ int activate_log(THD *thd, uint log_type);
+};
+
+
+/* type of the log table */
+#define QUERY_LOG_SLOW 1
+#define QUERY_LOG_GENERAL 2
+
+class Log_to_file_event_handler: public Log_event_handler
+{
+ MYSQL_QUERY_LOG mysql_log;
+ MYSQL_QUERY_LOG mysql_slow_log;
+ bool is_initialized;
+public:
+ Log_to_file_event_handler(): is_initialized(FALSE)
+ {}
+ virtual bool init();
+ virtual void cleanup();
+
+ virtual bool log_slow(THD *thd, time_t current_time,
+ time_t query_start_arg, const char *user_host,
+ uint user_host_len, ulonglong query_utime,
+ ulonglong lock_utime, bool is_command,
+ const char *sql_text, uint sql_text_len);
+ virtual bool log_error(enum loglevel level, const char *format,
+ va_list args);
+ virtual bool log_general(THD *thd, time_t event_time, const char *user_host,
+ uint user_host_len, int thread_id,
+ const char *command_type, uint command_type_len,
+ const char *sql_text, uint sql_text_len,
+ CHARSET_INFO *client_cs);
+ void flush();
+ void init_pthread_objects();
+ MYSQL_QUERY_LOG *get_mysql_slow_log() { return &mysql_slow_log; }
+ MYSQL_QUERY_LOG *get_mysql_log() { return &mysql_log; }
+};
+
+
+/* Class which manages slow, general and error log event handlers */
+class LOGGER
+{
+ rw_lock_t LOCK_logger;
+ /* flag to check whether logger mutex is initialized */
+ uint inited;
+
+ /* available log handlers */
+ Log_to_csv_event_handler *table_log_handler;
+ Log_to_file_event_handler *file_log_handler;
+
+ /* NULL-terminated arrays of log handlers */
+ Log_event_handler *error_log_handler_list[MAX_LOG_HANDLERS_NUM + 1];
+ Log_event_handler *slow_log_handler_list[MAX_LOG_HANDLERS_NUM + 1];
+ Log_event_handler *general_log_handler_list[MAX_LOG_HANDLERS_NUM + 1];
+
+public:
+
+ bool is_log_tables_initialized;
+
+ 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); }
+ bool is_log_table_enabled(uint log_table_type);
+ bool log_command(THD *thd, enum enum_server_command command);
+
+ /*
+ We want to initialize all log mutexes as soon as possible,
+ but we cannot do it in constructor, as safe_mutex relies on
+ initialization, performed by MY_INIT(). This why this is done in
+ this function.
+ */
+ void init_base();
+ void init_log_tables();
+ bool flush_logs(THD *thd);
+ /* 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 */
+ void cleanup_end();
+ bool error_log_print(enum loglevel level, const char *format,
+ va_list args);
+ bool slow_log_print(THD *thd, const char *query, uint query_length,
+ ulonglong current_utime);
+ bool general_log_print(THD *thd,enum enum_server_command command,
+ const char *format, va_list args);
+ bool general_log_write(THD *thd, enum enum_server_command command,
+ const char *query, uint query_length);
+
+ /* 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);
+ 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()
+ {
+ if (file_log_handler)
+ return file_log_handler->get_mysql_slow_log();
+ return NULL;
+ }
+ MYSQL_QUERY_LOG *get_log_file_handler()
+ {
+ if (file_log_handler)
+ return file_log_handler->get_mysql_log();
+ return NULL;
+ }
+};
+
+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
+};
+extern TYPELIB binlog_format_typelib;
+
+#endif /* LOG_H */
diff --git a/sql/log_event.cc b/sql/log_event.cc
index 2f4923c4b4f..35797b2cfdb 100644
--- a/sql/log_event.cc
+++ b/sql/log_event.cc
@@ -1,4 +1,4 @@
-/* Copyright (C) 2000-2004 MySQL AB
+/* Copyright 2000-2008 MySQL AB, 2008 Sun Microsystems, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -14,19 +14,227 @@
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
-#ifndef MYSQL_CLIENT
+#ifdef MYSQL_CLIENT
+
+#include "mysql_priv.h"
+
+#else
#ifdef USE_PRAGMA_IMPLEMENTATION
#pragma implementation // gcc: Class implementation
#endif
-#include "mysql_priv.h"
+#include "mysql_priv.h"
#include "slave.h"
+#include "rpl_rli.h"
+#include "rpl_mi.h"
+#include "rpl_filter.h"
+#include "rpl_utility.h"
+#include "rpl_record.h"
#include <my_dir.h>
+
#endif /* MYSQL_CLIENT */
+#include <base64.h>
+#include <my_bitmap.h>
+
#define log_cs &my_charset_latin1
+#define FLAGSTR(V,F) ((V)&(F)?#F" ":"")
+
+
+/*
+ Size of buffer for printing a double in format %.<PREC>g
+
+ optional '-' + optional zero + '.' + PREC digits + 'e' + sign +
+ exponent digits + '\0'
+*/
+#define FMT_G_BUFSIZE(PREC) (3 + (PREC) + 5 + 1)
+
+
+#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
+static int rows_event_stmt_cleanup(Relay_log_info const *rli, THD* thd);
+
+static const char *HA_ERR(int i)
+{
+ switch (i) {
+ case HA_ERR_KEY_NOT_FOUND: return "HA_ERR_KEY_NOT_FOUND";
+ case HA_ERR_FOUND_DUPP_KEY: return "HA_ERR_FOUND_DUPP_KEY";
+ case HA_ERR_RECORD_CHANGED: return "HA_ERR_RECORD_CHANGED";
+ case HA_ERR_WRONG_INDEX: return "HA_ERR_WRONG_INDEX";
+ case HA_ERR_CRASHED: return "HA_ERR_CRASHED";
+ case HA_ERR_WRONG_IN_RECORD: return "HA_ERR_WRONG_IN_RECORD";
+ case HA_ERR_OUT_OF_MEM: return "HA_ERR_OUT_OF_MEM";
+ case HA_ERR_NOT_A_TABLE: return "HA_ERR_NOT_A_TABLE";
+ case HA_ERR_WRONG_COMMAND: return "HA_ERR_WRONG_COMMAND";
+ case HA_ERR_OLD_FILE: return "HA_ERR_OLD_FILE";
+ case HA_ERR_NO_ACTIVE_RECORD: return "HA_ERR_NO_ACTIVE_RECORD";
+ case HA_ERR_RECORD_DELETED: return "HA_ERR_RECORD_DELETED";
+ case HA_ERR_RECORD_FILE_FULL: return "HA_ERR_RECORD_FILE_FULL";
+ case HA_ERR_INDEX_FILE_FULL: return "HA_ERR_INDEX_FILE_FULL";
+ case HA_ERR_END_OF_FILE: return "HA_ERR_END_OF_FILE";
+ case HA_ERR_UNSUPPORTED: return "HA_ERR_UNSUPPORTED";
+ case HA_ERR_TO_BIG_ROW: return "HA_ERR_TO_BIG_ROW";
+ case HA_WRONG_CREATE_OPTION: return "HA_WRONG_CREATE_OPTION";
+ case HA_ERR_FOUND_DUPP_UNIQUE: return "HA_ERR_FOUND_DUPP_UNIQUE";
+ case HA_ERR_UNKNOWN_CHARSET: return "HA_ERR_UNKNOWN_CHARSET";
+ case HA_ERR_WRONG_MRG_TABLE_DEF: return "HA_ERR_WRONG_MRG_TABLE_DEF";
+ case HA_ERR_CRASHED_ON_REPAIR: return "HA_ERR_CRASHED_ON_REPAIR";
+ case HA_ERR_CRASHED_ON_USAGE: return "HA_ERR_CRASHED_ON_USAGE";
+ case HA_ERR_LOCK_WAIT_TIMEOUT: return "HA_ERR_LOCK_WAIT_TIMEOUT";
+ case HA_ERR_LOCK_TABLE_FULL: return "HA_ERR_LOCK_TABLE_FULL";
+ case HA_ERR_READ_ONLY_TRANSACTION: return "HA_ERR_READ_ONLY_TRANSACTION";
+ case HA_ERR_LOCK_DEADLOCK: return "HA_ERR_LOCK_DEADLOCK";
+ case HA_ERR_CANNOT_ADD_FOREIGN: return "HA_ERR_CANNOT_ADD_FOREIGN";
+ case HA_ERR_NO_REFERENCED_ROW: return "HA_ERR_NO_REFERENCED_ROW";
+ case HA_ERR_ROW_IS_REFERENCED: return "HA_ERR_ROW_IS_REFERENCED";
+ case HA_ERR_NO_SAVEPOINT: return "HA_ERR_NO_SAVEPOINT";
+ case HA_ERR_NON_UNIQUE_BLOCK_SIZE: return "HA_ERR_NON_UNIQUE_BLOCK_SIZE";
+ case HA_ERR_NO_SUCH_TABLE: return "HA_ERR_NO_SUCH_TABLE";
+ case HA_ERR_TABLE_EXIST: return "HA_ERR_TABLE_EXIST";
+ case HA_ERR_NO_CONNECTION: return "HA_ERR_NO_CONNECTION";
+ case HA_ERR_NULL_IN_SPATIAL: return "HA_ERR_NULL_IN_SPATIAL";
+ case HA_ERR_TABLE_DEF_CHANGED: return "HA_ERR_TABLE_DEF_CHANGED";
+ case HA_ERR_NO_PARTITION_FOUND: return "HA_ERR_NO_PARTITION_FOUND";
+ case HA_ERR_RBR_LOGGING_FAILED: return "HA_ERR_RBR_LOGGING_FAILED";
+ case HA_ERR_DROP_INDEX_FK: return "HA_ERR_DROP_INDEX_FK";
+ case HA_ERR_FOREIGN_DUPLICATE_KEY: return "HA_ERR_FOREIGN_DUPLICATE_KEY";
+ case HA_ERR_TABLE_NEEDS_UPGRADE: return "HA_ERR_TABLE_NEEDS_UPGRADE";
+ case HA_ERR_TABLE_READONLY: return "HA_ERR_TABLE_READONLY";
+ case HA_ERR_AUTOINC_READ_FAILED: return "HA_ERR_AUTOINC_READ_FAILED";
+ case HA_ERR_AUTOINC_ERANGE: return "HA_ERR_AUTOINC_ERANGE";
+ case HA_ERR_GENERIC: return "HA_ERR_GENERIC";
+ case HA_ERR_RECORD_IS_THE_SAME: return "HA_ERR_RECORD_IS_THE_SAME";
+ case HA_ERR_LOGGING_IMPOSSIBLE: return "HA_ERR_LOGGING_IMPOSSIBLE";
+ case HA_ERR_CORRUPT_EVENT: return "HA_ERR_CORRUPT_EVENT";
+ case HA_ERR_ROWS_EVENT_APPLY : return "HA_ERR_ROWS_EVENT_APPLY";
+ }
+ return 0;
+}
+
+/**
+ Error reporting facility for Rows_log_event::do_apply_event
+
+ @param level error, warning or info
+ @param ha_error HA_ERR_ code
+ @param rli pointer to the active Relay_log_info instance
+ @param thd pointer to the slave thread's thd
+ @param table pointer to the event's table object
+ @param type the type of the event
+ @param log_name the master binlog file name
+ @param pos the master binlog file pos (the next after the event)
+
+*/
+static void inline slave_rows_error_report(enum loglevel level, int ha_error,
+ Relay_log_info const *rli, THD *thd,
+ TABLE *table, const char * type,
+ const char *log_name, ulong pos)
+{
+ const char *handler_error= HA_ERR(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);
+ MYSQL_ERROR *err;
+ buff[0]= 0;
+
+ for (err= it++, slider= buff; err && slider < buff_end - 1;
+ slider += len, err= it++)
+ {
+ len= my_snprintf(slider, buff_end - slider,
+ " %s, Error_code: %d;", err->msg, err->code);
+ }
+
+ rli->report(level, thd->is_error()? thd->main_da.sql_errno() : 0,
+ "Could not execute %s event on table %s.%s;"
+ "%s handler error %s; "
+ "the event's master log %s, end_log_pos %lu",
+ type, table->s->db.str,
+ table->s->table_name.str,
+ buff,
+ handler_error == NULL? "<unknown>" : handler_error,
+ log_name, pos);
+}
+#endif
+
+/*
+ Cache that will automatically be written to a dedicated file on
+ destruction.
+
+ DESCRIPTION
+
+ */
+class Write_on_release_cache
+{
+public:
+ enum flag
+ {
+ FLUSH_F
+ };
+
+ typedef unsigned short flag_set;
+
+ /*
+ Constructor.
+
+ SYNOPSIS
+ Write_on_release_cache
+ cache Pointer to cache to use
+ file File to write cache to upon destruction
+ flags Flags for the cache
+
+ DESCRIPTION
+
+ Class used to guarantee copy of cache to file before exiting the
+ current block. On successful copy of the cache, the cache will
+ be reinited as a WRITE_CACHE.
+
+ Currently, a pointer to the cache is provided in the
+ constructor, but it would be possible to create a subclass
+ holding the IO_CACHE itself.
+ */
+ Write_on_release_cache(IO_CACHE *cache, FILE *file, flag_set flags = 0)
+ : m_cache(cache), m_file(file), m_flags(flags)
+ {
+ reinit_io_cache(m_cache, WRITE_CACHE, 0L, FALSE, TRUE);
+ }
+
+ ~Write_on_release_cache()
+ {
+ copy_event_cache_to_file_and_reinit(m_cache, m_file);
+ if (m_flags | FLUSH_F)
+ fflush(m_file);
+ }
+
+ /*
+ Return a pointer to the internal IO_CACHE.
+
+ SYNOPSIS
+ operator&()
+
+ DESCRIPTION
+
+ Function to return a pointer to the internal cache, so that the
+ object can be treated as a IO_CACHE and used with the my_b_*
+ IO_CACHE functions
+
+ RETURN VALUE
+ A pointer to the internal IO_CACHE.
+ */
+ IO_CACHE *operator&()
+ {
+ return m_cache;
+ }
+
+private:
+ // Hidden, to prevent usage.
+ Write_on_release_cache(Write_on_release_cache const&);
+
+ IO_CACHE *m_cache;
+ FILE *m_file;
+ flag_set m_flags;
+};
+
#ifndef DBUG_OFF
uint debug_not_change_ts_if_art_event= 1; // bug#29309 simulation
#endif
@@ -36,48 +244,60 @@ uint debug_not_change_ts_if_art_event= 1; // bug#29309 simulation
*/
#ifdef MYSQL_CLIENT
-static void pretty_print_str(FILE* file, char* str, int len)
+static void pretty_print_str(IO_CACHE* cache, const char* str, int len)
{
- char* end = str + len;
- fputc('\'', file);
+ const char* end = str + len;
+ my_b_printf(cache, "\'");
while (str < end)
{
char c;
switch ((c=*str++)) {
- case '\n': fprintf(file, "\\n"); break;
- case '\r': fprintf(file, "\\r"); break;
- case '\\': fprintf(file, "\\\\"); break;
- case '\b': fprintf(file, "\\b"); break;
- case '\t': fprintf(file, "\\t"); break;
- case '\'': fprintf(file, "\\'"); break;
- case 0 : fprintf(file, "\\0"); break;
+ case '\n': my_b_printf(cache, "\\n"); break;
+ case '\r': my_b_printf(cache, "\\r"); break;
+ case '\\': my_b_printf(cache, "\\\\"); break;
+ case '\b': my_b_printf(cache, "\\b"); break;
+ case '\t': my_b_printf(cache, "\\t"); break;
+ case '\'': my_b_printf(cache, "\\'"); break;
+ case 0 : my_b_printf(cache, "\\0"); break;
default:
- fputc(c, file);
+ my_b_printf(cache, "%c", c);
break;
}
}
- fputc('\'', file);
+ my_b_printf(cache, "\'");
}
#endif /* MYSQL_CLIENT */
-
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
-static void clear_all_errors(THD *thd, struct st_relay_log_info *rli)
+static void clear_all_errors(THD *thd, Relay_log_info *rli)
{
- thd->query_error = 0;
+ thd->is_slave_error = 0;
thd->clear_error();
- *rli->last_slave_error = 0;
- rli->last_slave_errno = 0;
+ rli->clear_error();
}
-/*
- Ignore error code specified on command line
+/**
+ Ignore error code specified on command line.
*/
inline int ignored_error_code(int err_code)
{
+#ifdef HAVE_NDB_BINLOG
+ /*
+ The following error codes are hard-coded and will always be ignored.
+ */
+ switch (err_code)
+ {
+ case ER_DB_CREATE_EXISTS:
+ case ER_DB_DROP_EXISTS:
+ return 1;
+ default:
+ /* Nothing to do */
+ break;
+ }
+#endif
return ((err_code == ER_SLAVE_IGNORED_TABLE) ||
(use_slave_mask && bitmap_is_set(&slave_error_mask, err_code)));
}
@@ -89,9 +309,9 @@ inline int ignored_error_code(int err_code)
*/
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
-static char *pretty_print_str(char *packet, char *str, int len)
+static char *pretty_print_str(char *packet, const char *str, int len)
{
- char *end= str + len;
+ const char *end= str + len;
char *pos= packet;
*pos++= '\'';
while (str < end)
@@ -116,21 +336,20 @@ static char *pretty_print_str(char *packet, char *str, int len)
#endif /* !MYSQL_CLIENT */
-/*
- Creates a temporary name for load data infile:
+#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
- SYNOPSIS
- slave_load_file_stem()
- buf Store new filename here
- file_id File_id (part of file name)
- event_server_id Event_id (part of file name)
- ext Extension for file name
+/**
+ Creates a temporary name for load data infile:.
+
+ @param buf Store new filename here
+ @param file_id File_id (part of file name)
+ @param event_server_id Event_id (part of file name)
+ @param ext Extension for file name
- RETURN
+ @return
Pointer to start of extension
*/
-#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
static char *slave_load_file_stem(char *buf, uint file_id,
int event_server_id, const char *ext)
{
@@ -150,14 +369,12 @@ static char *slave_load_file_stem(char *buf, uint file_id,
#endif
-/*
- Delete all temporary files used for SQL_LOAD.
+#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
- SYNOPSIS
- cleanup_load_tmpdir()
+/**
+ Delete all temporary files used for SQL_LOAD.
*/
-#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
static void cleanup_load_tmpdir()
{
MY_DIR *dirp;
@@ -200,12 +417,12 @@ static void cleanup_load_tmpdir()
write_str()
*/
-static bool write_str(IO_CACHE *file, char *str, uint length)
+static bool write_str(IO_CACHE *file, const char *str, uint length)
{
- byte tmp[1];
- tmp[0]= (byte) length;
+ uchar tmp[1];
+ tmp[0]= (uchar) length;
return (my_b_safe_write(file, tmp, sizeof(tmp)) ||
- my_b_safe_write(file, (byte*) str, length));
+ my_b_safe_write(file, (uchar*) str, length));
}
@@ -213,8 +430,8 @@ static bool write_str(IO_CACHE *file, char *str, uint length)
read_str()
*/
-static inline int read_str(char **buf, char *buf_end, char **str,
- uint8 *len)
+static inline int read_str(const char **buf, const char *buf_end,
+ const char **str, uint8 *len)
{
if (*buf + ((uint) (uchar) **buf) >= buf_end)
return 1;
@@ -225,7 +442,7 @@ static inline int read_str(char **buf, char *buf_end, char **str,
}
-/*
+/**
Transforms a string into "" or its expression in 0x... form.
*/
@@ -242,12 +459,14 @@ char *str_to_hex(char *to, const char *from, uint len)
return to; // pointer to end 0 of 'to'
}
-/*
+#ifndef MYSQL_CLIENT
+
+/**
Append a version of the 'from' string suitable for use in a query to
the 'to' string. To generate a correct escaping, the character set
information in 'csinfo' is used.
- */
-#ifndef MYSQL_CLIENT
+*/
+
int
append_query_string(CHARSET_INFO *csinfo,
String const *from, String *to)
@@ -268,27 +487,28 @@ append_query_string(CHARSET_INFO *csinfo,
from->ptr(), from->length());
*ptr++='\'';
}
- to->length((uint) (orig_len + ptr - beg));
+ to->length(orig_len + ptr - beg);
return 0;
}
#endif
-/*
+/**
Prints a "session_var=value" string. Used by mysqlbinlog to print some SET
commands just before it prints a query.
*/
#ifdef MYSQL_CLIENT
-static void print_set_option(FILE* file, uint32 bits_changed, uint32 option,
- uint32 flags, const char* name, bool* need_comma)
+static void print_set_option(IO_CACHE* file, uint32 bits_changed,
+ uint32 option, uint32 flags, const char* name,
+ bool* need_comma)
{
if (bits_changed & option)
{
if (*need_comma)
- fprintf(file,", ");
- fprintf(file,"%s=%d", name, test(flags & option));
+ my_b_printf(file,", ");
+ my_b_printf(file,"%s=%d", name, test(flags & option));
*need_comma= 1;
}
}
@@ -298,13 +518,14 @@ static void print_set_option(FILE* file, uint32 bits_changed, uint32 option,
Log_event methods (= the parent class of all events)
**************************************************************************/
-/*
- Log_event::get_type_str()
+/**
+ @return
+ returns the human readable name of the event's type
*/
-const char* Log_event::get_type_str()
+const char* Log_event::get_type_str(Log_event_type type)
{
- switch(get_type_code()) {
+ switch(type) {
case START_EVENT_V3: return "Start_v3";
case STOP_EVENT: return "Stop";
case QUERY_EVENT: return "Query";
@@ -321,12 +542,25 @@ const char* Log_event::get_type_str()
case XID_EVENT: return "Xid";
case USER_VAR_EVENT: return "User var";
case FORMAT_DESCRIPTION_EVENT: return "Format_desc";
+ case TABLE_MAP_EVENT: return "Table_map";
+ case PRE_GA_WRITE_ROWS_EVENT: return "Write_rows_event_old";
+ case PRE_GA_UPDATE_ROWS_EVENT: return "Update_rows_event_old";
+ case PRE_GA_DELETE_ROWS_EVENT: return "Delete_rows_event_old";
+ case WRITE_ROWS_EVENT: return "Write_rows";
+ case UPDATE_ROWS_EVENT: return "Update_rows";
+ case DELETE_ROWS_EVENT: return "Delete_rows";
case BEGIN_LOAD_QUERY_EVENT: return "Begin_load_query";
case EXECUTE_LOAD_QUERY_EVENT: return "Execute_load_query";
+ case INCIDENT_EVENT: return "Incident";
default: return "Unknown"; /* impossible */
}
}
+const char* Log_event::get_type_str()
+{
+ return get_type_str(get_type_code());
+}
+
/*
Log_event::Log_event()
@@ -342,7 +576,7 @@ Log_event::Log_event(THD* thd_arg, uint16 flags_arg, bool using_trans)
}
-/*
+/**
This minimal constructor is for when you are not even sure that there
is a valid THD. For example in the server when we are shutting down or
flushing logs after receiving a SIGHUP (then we must write a Rotate to
@@ -354,7 +588,11 @@ Log_event::Log_event()
thd(0)
{
server_id= ::server_id;
- when= time(NULL);
+ /*
+ We can't call my_time() here as this would cause a call before
+ my_init() is called
+ */
+ when= 0;
log_pos= 0;
}
#endif /* !MYSQL_CLIENT */
@@ -435,97 +673,67 @@ Log_event::Log_event(const char* buf,
#ifndef MYSQL_CLIENT
#ifdef HAVE_REPLICATION
-/*
- Log_event::exec_event()
-*/
-
-int Log_event::exec_event(struct st_relay_log_info* rli)
+int Log_event::do_update_pos(Relay_log_info *rli)
{
- DBUG_ENTER("Log_event::exec_event");
-
/*
- rli is null when (as far as I (Guilhem) know)
- the caller is
- Load_log_event::exec_event *and* that one is called from
- Execute_load_log_event::exec_event.
- In this case, we don't do anything here ;
- Execute_load_log_event::exec_event will call Log_event::exec_event
- again later with the proper rli.
- Strictly speaking, if we were sure that rli is null
- only in the case discussed above, 'if (rli)' is useless here.
- But as we are not 100% sure, keep it for now.
+ rli is null when (as far as I (Guilhem) know) the caller is
+ Load_log_event::do_apply_event *and* that one is called from
+ Execute_load_log_event::do_apply_event. In this case, we don't
+ do anything here ; Execute_load_log_event::do_apply_event will
+ call Log_event::do_apply_event again later with the proper rli.
+ Strictly speaking, if we were sure that rli is null only in the
+ case discussed above, 'if (rli)' is useless here. But as we are
+ not 100% sure, keep it for now.
+
+ Matz: I don't think we will need this check with this refactoring.
*/
if (rli)
{
/*
- If in a transaction, and if the slave supports transactions, just
- inc_event_relay_log_pos(). We only have to check for OPTION_BEGIN
- (not OPTION_NOT_AUTOCOMMIT) as transactions are logged with
- BEGIN/COMMIT, not with SET AUTOCOMMIT= .
-
- CAUTION: opt_using_transactions means
- innodb || bdb ; suppose the master supports InnoDB and BDB,
- but the slave supports only BDB, problems
- will arise:
- - suppose an InnoDB table is created on the master,
- - then it will be MyISAM on the slave
- - but as opt_using_transactions is true, the slave will believe he
- is transactional with the MyISAM table. And problems will come
- when one does START SLAVE; STOP SLAVE; START SLAVE; (the slave
- will resume at BEGIN whereas there has not been any rollback).
- This is the problem of using opt_using_transactions instead of a
- finer "does the slave support
- _the_transactional_handler_used_on_the_master_".
-
- More generally, we'll have problems when a query mixes a
- transactional handler and MyISAM and STOP SLAVE is issued in the
- middle of the "transaction". START SLAVE will resume at BEGIN
- while the MyISAM table has already been updated.
+ bug#29309 simulation: resetting the flag to force
+ wrong behaviour of artificial event to update
+ rli->last_master_timestamp for only one time -
+ the first FLUSH LOGS in the test.
*/
- if ((thd->options & OPTION_BEGIN) && opt_using_transactions)
- rli->inc_event_relay_log_pos();
- else
- {
- /*
- bug#29309 simulation: resetting the flag to force
- wrong behaviour of artificial event to update
- rli->last_master_timestamp for only one time -
- the first FLUSH LOGS in the test.
- */
- DBUG_EXECUTE_IF("let_first_flush_log_change_timestamp",
- if (debug_not_change_ts_if_art_event == 1
- && is_artificial_event())
- {
- debug_not_change_ts_if_art_event= 0;
- });
- rli->inc_group_relay_log_pos(log_pos);
- flush_relay_log_info(rli);
- /*
- Note that Rotate_log_event::exec_event() does not call this
- function, so there is no chance that a fake rotate event resets
- last_master_timestamp.
- Note that we update without mutex (probably ok - except in some very
- rare cases, only consequence is that value may take some time to
- display in Seconds_Behind_Master - not critical).
- */
+ DBUG_EXECUTE_IF("let_first_flush_log_change_timestamp",
+ if (debug_not_change_ts_if_art_event == 1
+ && is_artificial_event())
+ {
+ debug_not_change_ts_if_art_event= 0;
+ });
#ifndef DBUG_OFF
- if (!(is_artificial_event() && debug_not_change_ts_if_art_event > 0))
+ rli->stmt_done(log_pos,
+ is_artificial_event() &&
+ debug_not_change_ts_if_art_event > 0 ? 0 : when);
#else
- if (!is_artificial_event())
+ rli->stmt_done(log_pos, is_artificial_event()? 0 : when);
#endif
- rli->last_master_timestamp= when;
- /*
- The flag is set back to be positive so that
- any further FLUSH LOGS will be handled as prescribed.
- */
- DBUG_EXECUTE_IF("let_first_flush_log_change_timestamp",
- if (debug_not_change_ts_if_art_event == 0)
- {
- debug_not_change_ts_if_art_event= 2;
- });
- }
+ DBUG_EXECUTE_IF("let_first_flush_log_change_timestamp",
+ if (debug_not_change_ts_if_art_event == 0)
+ {
+ debug_not_change_ts_if_art_event= 2;
+ });
}
- DBUG_RETURN(0);
+ return 0; // Cannot fail currently
+}
+
+
+Log_event::enum_skip_reason
+Log_event::do_shall_skip(Relay_log_info *rli)
+{
+ DBUG_PRINT("info", ("ev->server_id=%lu, ::server_id=%lu,"
+ " rli->replicate_same_server_id=%d,"
+ " rli->slave_skip_counter=%d",
+ (ulong) server_id, (ulong) ::server_id,
+ 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())
+ return EVENT_SKIP_IGNORE;
+ else if (rli->slave_skip_counter > 0)
+ return EVENT_SKIP_COUNT;
+ else
+ return EVENT_SKIP_NOT;
}
@@ -539,24 +747,21 @@ void Log_event::pack_info(Protocol *protocol)
}
-/*
- Log_event::net_send()
-
+/**
Only called by SHOW BINLOG EVENTS
*/
-
int Log_event::net_send(Protocol *protocol, const char* log_name, my_off_t pos)
{
const char *p= strrchr(log_name, FN_LIBCHAR);
const char *event_type;
if (p)
log_name = p + 1;
-
+
protocol->prepare_for_resend();
protocol->store(log_name, &my_charset_bin);
protocol->store((ulonglong) pos);
event_type = get_type_str();
- protocol->store(event_type, (uint) strlen(event_type), &my_charset_bin);
+ protocol->store(event_type, strlen(event_type), &my_charset_bin);
protocol->store((uint32) server_id);
protocol->store((ulonglong) log_pos);
pack_info(protocol);
@@ -565,8 +770,10 @@ int Log_event::net_send(Protocol *protocol, const char* log_name, my_off_t pos)
#endif /* HAVE_REPLICATION */
-/*
- Log_event::init_show_field_list()
+/**
+ init_show_field_list() prepares the column names and types for the
+ output of SHOW BINLOG EVENTS; it is used only by SHOW BINLOG
+ EVENTS.
*/
void Log_event::init_show_field_list(List<Item>* field_list)
@@ -590,7 +797,8 @@ void Log_event::init_show_field_list(List<Item>* field_list)
bool Log_event::write_header(IO_CACHE* file, ulong event_data_length)
{
- byte header[LOG_EVENT_HEADER_LEN];
+ uchar header[LOG_EVENT_HEADER_LEN];
+ ulong now;
DBUG_ENTER("Log_event::write_header");
/* Store number of bytes that will be written by this event */
@@ -604,9 +812,8 @@ bool Log_event::write_header(IO_CACHE* file, ulong event_data_length)
if (is_artificial_event())
{
/*
- We should not do any cleanup on slave when reading this. We
- mark this by setting log_pos to 0. Start_log_event_v3() will
- detect this on reading and set artificial_event=1 for the event.
+ Artificial events are automatically generated and do not exist
+ in master's binary log, so log_pos should be set to 0.
*/
log_pos= 0;
}
@@ -641,6 +848,8 @@ bool Log_event::write_header(IO_CACHE* file, ulong event_data_length)
log_pos= my_b_safe_tell(file)+data_written;
}
+ now= (ulong) get_time(); // Query start time
+
/*
Header will be of size LOG_EVENT_HEADER_LEN for all events, except for
FORMAT_DESCRIPTION_EVENT and ROTATE_EVENT, where it will be
@@ -648,7 +857,7 @@ bool Log_event::write_header(IO_CACHE* file, ulong event_data_length)
because we read them before knowing the format).
*/
- int4store(header, (ulong) when); // timestamp
+ int4store(header, now); // timestamp
header[EVENT_TYPE_OFFSET]= get_type_code();
int4store(header+ SERVER_ID_OFFSET, server_id);
int4store(header+ EVENT_LEN_OFFSET, data_written);
@@ -659,12 +868,9 @@ bool Log_event::write_header(IO_CACHE* file, ulong event_data_length)
}
-/*
- Log_event::read_log_event()
-
+/**
This needn't be format-tolerant, because we only read
LOG_EVENT_MINIMAL_HEADER_LEN (we just want to read the event's length).
-
*/
int Log_event::read_log_event(IO_CACHE* file, String* packet,
@@ -673,11 +879,11 @@ int Log_event::read_log_event(IO_CACHE* file, String* packet,
ulong data_len;
int result=0;
char buf[LOG_EVENT_MINIMAL_HEADER_LEN];
- DBUG_ENTER("read_log_event");
+ DBUG_ENTER("Log_event::read_log_event");
if (log_lock)
pthread_mutex_lock(log_lock);
- if (my_b_read(file, (byte*) buf, sizeof(buf)))
+ if (my_b_read(file, (uchar*) buf, sizeof(buf)))
{
/*
If the read hits eof, we must report it as eof so the caller
@@ -747,23 +953,22 @@ end:
#define LOCK_MUTEX
#endif
-/*
- Log_event::read_log_event()
-
- NOTE:
+#ifndef MYSQL_CLIENT
+/**
+ @note
Allocates memory; The caller is responsible for clean-up.
*/
-
-#ifndef MYSQL_CLIENT
Log_event* Log_event::read_log_event(IO_CACHE* file,
pthread_mutex_t* log_lock,
- const Format_description_log_event *description_event)
+ const Format_description_log_event
+ *description_event)
#else
Log_event* Log_event::read_log_event(IO_CACHE* file,
- const Format_description_log_event *description_event)
+ const Format_description_log_event
+ *description_event)
#endif
{
- DBUG_ENTER("Log_event::read_log_event(IO_CACHE *, Format_description_log_event *");
+ DBUG_ENTER("Log_event::read_log_event");
DBUG_ASSERT(description_event != 0);
char head[LOG_EVENT_MINIMAL_HEADER_LEN];
/*
@@ -778,7 +983,7 @@ Log_event* Log_event::read_log_event(IO_CACHE* file,
LOCK_MUTEX;
DBUG_PRINT("info", ("my_b_tell: %lu", (ulong) my_b_tell(file)));
- if (my_b_read(file, (byte *) head, header_size))
+ if (my_b_read(file, (uchar *) head, header_size))
{
DBUG_PRINT("info", ("Log_event::read_log_event(IO_CACHE*,Format_desc*) \
failed my_b_read"));
@@ -812,14 +1017,14 @@ failed my_b_read"));
}
// some events use the extra byte to null-terminate strings
- if (!(buf = my_malloc(data_len+1, MYF(MY_WME))))
+ if (!(buf = (char*) my_malloc(data_len+1, MYF(MY_WME))))
{
error = "Out of memory";
goto err;
}
buf[data_len] = 0;
memcpy(buf, head, header_size);
- if (my_b_read(file, (byte*) buf + header_size, data_len - header_size))
+ if (my_b_read(file, (uchar*) buf + header_size, data_len - header_size))
{
error = "read error";
goto err;
@@ -850,8 +1055,7 @@ err:
}
-/*
- Log_event::read_log_event()
+/**
Binlog format tolerance is in (buf, event_len, description_event)
constructors.
*/
@@ -864,76 +1068,151 @@ Log_event* Log_event::read_log_event(const char* buf, uint event_len,
DBUG_ENTER("Log_event::read_log_event(char*,...)");
DBUG_ASSERT(description_event != 0);
DBUG_PRINT("info", ("binlog_version: %d", description_event->binlog_version));
+ DBUG_DUMP("data", (unsigned char*) buf, event_len);
+
+ /* Check the integrity */
if (event_len < EVENT_LEN_OFFSET ||
+ buf[EVENT_TYPE_OFFSET] >= ENUM_END_EVENT ||
(uint) event_len != uint4korr(buf+EVENT_LEN_OFFSET))
{
*error="Sanity check failed"; // Needed to free buffer
DBUG_RETURN(NULL); // general sanity check - will fail on a partial read
}
- switch(buf[EVENT_TYPE_OFFSET]) {
- case QUERY_EVENT:
- ev = new Query_log_event(buf, event_len, description_event, QUERY_EVENT);
- break;
- case LOAD_EVENT:
- ev = new Load_log_event(buf, event_len, description_event);
- break;
- case NEW_LOAD_EVENT:
- ev = new Load_log_event(buf, event_len, description_event);
- break;
- case ROTATE_EVENT:
- ev = new Rotate_log_event(buf, event_len, description_event);
- break;
+ uint event_type= buf[EVENT_TYPE_OFFSET];
+ if (event_type > description_event->number_of_event_types &&
+ event_type != FORMAT_DESCRIPTION_EVENT)
+ {
+ /*
+ It is unsafe to use the description_event if its post_header_len
+ array does not include the event type.
+ */
+ DBUG_PRINT("error", ("event type %d found, but the current "
+ "Format_description_log_event supports only %d event "
+ "types", event_type,
+ description_event->number_of_event_types));
+ ev= NULL;
+ }
+ else
+ {
+ /*
+ In some previuos versions (see comment in
+ Format_description_log_event::Format_description_log_event(char*,...)),
+ event types were assigned different id numbers than in the
+ present version. In order to replicate from such versions to the
+ present version, we must map those event type id's to our event
+ type id's. The mapping is done with the event_type_permutation
+ array, which was set up when the Format_description_log_event
+ was read.
+ */
+ 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];
+ }
+
+ switch(event_type) {
+ case QUERY_EVENT:
+ ev = new Query_log_event(buf, event_len, description_event, QUERY_EVENT);
+ break;
+ case LOAD_EVENT:
+ ev = new Load_log_event(buf, event_len, description_event);
+ break;
+ case NEW_LOAD_EVENT:
+ ev = new Load_log_event(buf, event_len, description_event);
+ break;
+ case ROTATE_EVENT:
+ ev = new Rotate_log_event(buf, event_len, description_event);
+ break;
#ifdef HAVE_REPLICATION
- case SLAVE_EVENT: /* can never happen (unused event) */
- ev = new Slave_log_event(buf, event_len);
- break;
+ case SLAVE_EVENT: /* can never happen (unused event) */
+ ev = new Slave_log_event(buf, event_len);
+ break;
#endif /* HAVE_REPLICATION */
- case CREATE_FILE_EVENT:
- ev = new Create_file_log_event(buf, event_len, description_event);
- break;
- case APPEND_BLOCK_EVENT:
- ev = new Append_block_log_event(buf, event_len, description_event);
- break;
- case DELETE_FILE_EVENT:
- ev = new Delete_file_log_event(buf, event_len, description_event);
- break;
- case EXEC_LOAD_EVENT:
- ev = new Execute_load_log_event(buf, event_len, description_event);
- break;
- case START_EVENT_V3: /* this is sent only by MySQL <=4.x */
- ev = new Start_log_event_v3(buf, description_event);
- break;
- case STOP_EVENT:
- ev = new Stop_log_event(buf, description_event);
- break;
- case INTVAR_EVENT:
- ev = new Intvar_log_event(buf, description_event);
- break;
- case XID_EVENT:
- ev = new Xid_log_event(buf, description_event);
- break;
- case RAND_EVENT:
- ev = new Rand_log_event(buf, description_event);
- break;
- case USER_VAR_EVENT:
- ev = new User_var_log_event(buf, description_event);
- break;
- case FORMAT_DESCRIPTION_EVENT:
- ev = new Format_description_log_event(buf, event_len, description_event);
- break;
- case BEGIN_LOAD_QUERY_EVENT:
- ev = new Begin_load_query_log_event(buf, event_len, description_event);
- break;
- case EXECUTE_LOAD_QUERY_EVENT:
- ev = new Execute_load_query_log_event(buf, event_len, description_event);
- break;
- default:
- DBUG_PRINT("error",("Unknown evernt code: %d",(int) buf[EVENT_TYPE_OFFSET]));
- ev= NULL;
- break;
+ case CREATE_FILE_EVENT:
+ ev = new Create_file_log_event(buf, event_len, description_event);
+ break;
+ case APPEND_BLOCK_EVENT:
+ ev = new Append_block_log_event(buf, event_len, description_event);
+ break;
+ case DELETE_FILE_EVENT:
+ ev = new Delete_file_log_event(buf, event_len, description_event);
+ break;
+ case EXEC_LOAD_EVENT:
+ ev = new Execute_load_log_event(buf, event_len, description_event);
+ break;
+ case START_EVENT_V3: /* this is sent only by MySQL <=4.x */
+ ev = new Start_log_event_v3(buf, description_event);
+ break;
+ case STOP_EVENT:
+ ev = new Stop_log_event(buf, description_event);
+ break;
+ case INTVAR_EVENT:
+ ev = new Intvar_log_event(buf, description_event);
+ break;
+ case XID_EVENT:
+ ev = new Xid_log_event(buf, description_event);
+ break;
+ case RAND_EVENT:
+ ev = new Rand_log_event(buf, description_event);
+ break;
+ case USER_VAR_EVENT:
+ ev = new User_var_log_event(buf, description_event);
+ break;
+ case FORMAT_DESCRIPTION_EVENT:
+ ev = new Format_description_log_event(buf, event_len, description_event);
+ break;
+#if defined(HAVE_REPLICATION)
+ case PRE_GA_WRITE_ROWS_EVENT:
+ ev = new Write_rows_log_event_old(buf, event_len, description_event);
+ break;
+ case PRE_GA_UPDATE_ROWS_EVENT:
+ ev = new Update_rows_log_event_old(buf, event_len, description_event);
+ break;
+ case PRE_GA_DELETE_ROWS_EVENT:
+ ev = new Delete_rows_log_event_old(buf, event_len, description_event);
+ break;
+ case WRITE_ROWS_EVENT:
+ ev = new Write_rows_log_event(buf, event_len, description_event);
+ break;
+ case UPDATE_ROWS_EVENT:
+ ev = new Update_rows_log_event(buf, event_len, description_event);
+ break;
+ case DELETE_ROWS_EVENT:
+ ev = new Delete_rows_log_event(buf, event_len, description_event);
+ break;
+ case TABLE_MAP_EVENT:
+ ev = new Table_map_log_event(buf, event_len, description_event);
+ break;
+#endif
+ case BEGIN_LOAD_QUERY_EVENT:
+ ev = new Begin_load_query_log_event(buf, event_len, description_event);
+ break;
+ case EXECUTE_LOAD_QUERY_EVENT:
+ ev= new Execute_load_query_log_event(buf, event_len, description_event);
+ break;
+ case INCIDENT_EVENT:
+ ev = new Incident_log_event(buf, event_len, description_event);
+ break;
+ default:
+ DBUG_PRINT("error",("Unknown event code: %d",
+ (int) buf[EVENT_TYPE_OFFSET]));
+ ev= NULL;
+ break;
+ }
}
+ DBUG_PRINT("read_event", ("%s(type_code: %d; event_len: %d)",
+ ev ? ev->get_type_str() : "<unknown>",
+ buf[EVENT_TYPE_OFFSET],
+ event_len));
/*
is_valid() are small event-specific sanity tests which are
important; for example there are some my_malloc() in constructors
@@ -969,20 +1248,23 @@ Log_event* Log_event::read_log_event(const char* buf, uint event_len,
Log_event::print_header()
*/
-void Log_event::print_header(FILE* file, PRINT_EVENT_INFO* print_event_info)
+void Log_event::print_header(IO_CACHE* file,
+ PRINT_EVENT_INFO* print_event_info,
+ bool is_more __attribute__((unused)))
{
char llbuff[22];
my_off_t hexdump_from= print_event_info->hexdump_from;
+ DBUG_ENTER("Log_event::print_header");
- fputc('#', file);
+ my_b_printf(file, "#");
print_timestamp(file);
- fprintf(file, " server id %d end_log_pos %s ", server_id,
- llstr(log_pos,llbuff));
+ my_b_printf(file, " server id %d end_log_pos %s ", server_id,
+ llstr(log_pos,llbuff));
/* mysqlbinlog --hexdump */
if (print_event_info->hexdump_from)
{
- fprintf(file, "\n");
+ my_b_printf(file, "\n");
uchar *ptr= (uchar*)temp_buf;
my_off_t size=
uint4korr(ptr + EVENT_LEN_OFFSET) - LOG_EVENT_MINIMAL_HEADER_LEN;
@@ -995,15 +1277,21 @@ void Log_event::print_header(FILE* file, PRINT_EVENT_INFO* print_event_info)
/* Pretty-print event common header if header is exactly 19 bytes */
if (print_event_info->common_header_len == LOG_EVENT_MINIMAL_HEADER_LEN)
{
- fprintf(file, "# Position Timestamp Type Master ID "
- "Size Master Pos Flags \n");
- fprintf(file, "# %8.8lx %02x %02x %02x %02x %02x "
- "%02x %02x %02x %02x %02x %02x %02x %02x "
- "%02x %02x %02x %02x %02x %02x\n",
- (unsigned long) hexdump_from,
- ptr[0], ptr[1], ptr[2], ptr[3], ptr[4], ptr[5], ptr[6],
- ptr[7], ptr[8], ptr[9], ptr[10], ptr[11], ptr[12], ptr[13],
- ptr[14], ptr[15], ptr[16], ptr[17], ptr[18]);
+ char emit_buf[256]; // Enough for storing one line
+ my_b_printf(file, "# Position Timestamp Type Master ID "
+ "Size Master Pos Flags \n");
+ size_t const bytes_written=
+ my_snprintf(emit_buf, sizeof(emit_buf),
+ "# %8.8lx %02x %02x %02x %02x %02x "
+ "%02x %02x %02x %02x %02x %02x %02x %02x "
+ "%02x %02x %02x %02x %02x %02x\n",
+ (unsigned long) hexdump_from,
+ ptr[0], ptr[1], ptr[2], ptr[3], ptr[4], ptr[5], ptr[6],
+ ptr[7], ptr[8], ptr[9], ptr[10], ptr[11], ptr[12], ptr[13],
+ ptr[14], ptr[15], ptr[16], ptr[17], ptr[18]);
+ DBUG_ASSERT(bytes_written >= 0);
+ DBUG_ASSERT(static_cast<size_t>(bytes_written) < sizeof(emit_buf));
+ my_b_write(file, (uchar*) emit_buf, bytes_written);
ptr += LOG_EVENT_MINIMAL_HEADER_LEN;
hexdump_from += LOG_EVENT_MINIMAL_HEADER_LEN;
}
@@ -1020,9 +1308,21 @@ void Log_event::print_header(FILE* file, PRINT_EVENT_INFO* print_event_info)
if (i % 16 == 15)
{
- fprintf(file, "# %8.8lx %-48.48s |%16s|\n",
- (unsigned long) (hexdump_from + (i & 0xfffffff0)),
- hex_string, char_string);
+ /*
+ my_b_printf() does not support full printf() formats, so we
+ have to do it this way.
+
+ TODO: Rewrite my_b_printf() to support full printf() syntax.
+ */
+ char emit_buf[256];
+ size_t const bytes_written=
+ my_snprintf(emit_buf, sizeof(emit_buf),
+ "# %8.8lx %-48.48s |%16s|\n",
+ (unsigned long) (hexdump_from + (i & 0xfffffff0)),
+ hex_string, char_string);
+ DBUG_ASSERT(bytes_written >= 0);
+ DBUG_ASSERT(static_cast<size_t>(bytes_written) < sizeof(emit_buf));
+ my_b_write(file, (uchar*) emit_buf, bytes_written);
hex_string[0]= 0;
char_string[0]= 0;
c= char_string;
@@ -1034,14 +1334,630 @@ void Log_event::print_header(FILE* file, PRINT_EVENT_INFO* print_event_info)
if (hex_string[0])
{
- /* Non-full last line */
- fprintf(file, "# %8.8lx %-48.48s |%s|\n# ",
- (unsigned long) (hexdump_from + (i & 0xfffffff0)),
- hex_string, char_string);
+ char emit_buf[256];
+ size_t const bytes_written=
+ my_snprintf(emit_buf, sizeof(emit_buf),
+ "# %8.8lx %-48.48s |%s|\n",
+ (unsigned long) (hexdump_from + (i & 0xfffffff0)),
+ hex_string, char_string);
+ DBUG_ASSERT(bytes_written >= 0);
+ DBUG_ASSERT(static_cast<size_t>(bytes_written) < sizeof(emit_buf));
+ my_b_write(file, (uchar*) emit_buf, bytes_written);
+ }
+ /*
+ need a # to prefix the rest of printouts for example those of
+ Rows_log_event::print_helper().
+ */
+ my_b_write(file, reinterpret_cast<const uchar*>("# "), 2);
+ }
+ DBUG_VOID_RETURN;
+}
+
+
+/**
+ Prints a quoted string to io cache.
+ Control characters are displayed as hex sequence, e.g. \x00
+
+ @param[in] file IO cache
+ @param[in] prt Pointer to string
+ @param[in] length String length
+*/
+
+static void
+my_b_write_quoted(IO_CACHE *file, const uchar *ptr, uint length)
+{
+ const uchar *s;
+ my_b_printf(file, "'");
+ for (s= ptr; length > 0 ; s++, length--)
+ {
+ if (*s > 0x1F)
+ my_b_write(file, s, 1);
+ else
+ {
+ uchar hex[10];
+ size_t len= my_snprintf((char*) hex, sizeof(hex), "%s%02x", "\\x", *s);
+ my_b_write(file, hex, len);
+ }
+ }
+ my_b_printf(file, "'");
+}
+
+
+/**
+ Prints a bit string to io cache in format b'1010'.
+
+ @param[in] file IO cache
+ @param[in] ptr Pointer to string
+ @param[in] nbits Number of bits
+*/
+static void
+my_b_write_bit(IO_CACHE *file, const uchar *ptr, uint nbits)
+{
+ uint bitnum, nbits8= ((nbits + 7) / 8) * 8, skip_bits= nbits8 - nbits;
+ my_b_printf(file, "b'");
+ for (bitnum= skip_bits ; bitnum < nbits8; bitnum++)
+ {
+ int is_set= (ptr[(bitnum) / 8] >> (7 - bitnum % 8)) & 0x01;
+ my_b_write(file, (const uchar*) (is_set ? "1" : "0"), 1);
+ }
+ my_b_printf(file, "'");
+}
+
+
+/**
+ Prints a packed string to io cache.
+ The string consists of length packed to 1 or 2 bytes,
+ followed by string data itself.
+
+ @param[in] file IO cache
+ @param[in] ptr Pointer to string
+ @param[in] length String size
+
+ @retval - number of bytes scanned.
+*/
+static size_t
+my_b_write_quoted_with_length(IO_CACHE *file, const uchar *ptr, uint length)
+{
+ if (length < 256)
+ {
+ length= *ptr;
+ my_b_write_quoted(file, ptr + 1, length);
+ return length + 1;
+ }
+ else
+ {
+ length= uint2korr(ptr);
+ my_b_write_quoted(file, ptr + 2, length);
+ return length + 2;
+ }
+}
+
+
+/**
+ Prints a 32-bit number in both signed and unsigned representation
+
+ @param[in] file IO cache
+ @param[in] sl Signed number
+ @param[in] ul Unsigned number
+*/
+static void
+my_b_write_sint32_and_uint32(IO_CACHE *file, int32 si, uint32 ui)
+{
+ my_b_printf(file, "%d", si);
+ if (si < 0)
+ my_b_printf(file, " (%u)", ui);
+}
+
+
+/**
+ Print a packed value of the given SQL type into IO cache
+
+ @param[in] file IO cache
+ @param[in] ptr Pointer to string
+ @param[in] type Column type
+ @param[in] meta Column meta information
+ @param[out] typestr SQL type string buffer (for verbose output)
+ @param[out] typestr_length Size of typestr
+
+ @retval - number of bytes scanned from ptr.
+*/
+
+static size_t
+log_event_print_value(IO_CACHE *file, const uchar *ptr,
+ uint type, uint meta,
+ char *typestr, size_t typestr_length)
+{
+ uint32 length= 0;
+
+ if (type == MYSQL_TYPE_STRING)
+ {
+ if (meta >= 256)
+ {
+ uint byte0= meta >> 8;
+ uint byte1= meta & 0xFF;
+
+ if ((byte0 & 0x30) != 0x30)
+ {
+ /* a long CHAR() field: see #37426 */
+ length= byte1 | (((byte0 & 0x30) ^ 0x30) << 4);
+ type= byte0 | 0x30;
+ 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;
+ }
+
+
+beg:
+
+ switch (type) {
+ case MYSQL_TYPE_LONG:
+ {
+ int32 si= sint4korr(ptr);
+ uint32 ui= uint4korr(ptr);
+ my_b_write_sint32_and_uint32(file, si, ui);
+ my_snprintf(typestr, typestr_length, "INT");
+ return 4;
+ }
+
+ case MYSQL_TYPE_TINY:
+ {
+ my_b_write_sint32_and_uint32(file, (int) (signed char) *ptr,
+ (uint) (unsigned char) *ptr);
+ my_snprintf(typestr, typestr_length, "TINYINT");
+ return 1;
+ }
+
+ case MYSQL_TYPE_SHORT:
+ {
+ int32 si= (int32) sint2korr(ptr);
+ uint32 ui= (uint32) uint2korr(ptr);
+ my_b_write_sint32_and_uint32(file, si, ui);
+ my_snprintf(typestr, typestr_length, "SHORTINT");
+ return 2;
+ }
+
+ case MYSQL_TYPE_INT24:
+ {
+ int32 si= sint3korr(ptr);
+ uint32 ui= uint3korr(ptr);
+ my_b_write_sint32_and_uint32(file, si, ui);
+ my_snprintf(typestr, typestr_length, "MEDIUMINT");
+ return 3;
+ }
+
+ case MYSQL_TYPE_LONGLONG:
+ {
+ char tmp[64];
+ longlong si= sint8korr(ptr);
+ longlong10_to_str(si, tmp, -10);
+ my_b_printf(file, "%s", tmp);
+ if (si < 0)
+ {
+ ulonglong ui= uint8korr(ptr);
+ longlong10_to_str((longlong) ui, tmp, 10);
+ my_b_printf(file, " (%s)", tmp);
+ }
+ my_snprintf(typestr, typestr_length, "LONGINT");
+ return 8;
+ }
+
+ case MYSQL_TYPE_NEWDECIMAL:
+ {
+ uint precision= meta >> 8;
+ uint decimals= meta & 0xFF;
+ uint bin_size= my_decimal_get_binary_size(precision, decimals);
+ my_decimal dec;
+ binary2my_decimal(E_DEC_FATAL_ERROR, (uchar*) ptr, &dec,
+ precision, decimals);
+ int i, end;
+ char buff[512], *pos;
+ pos= buff;
+ pos+= my_sprintf(buff, (buff, "%s", dec.sign() ? "-" : ""));
+ 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", dec.buf[i]));
+ my_b_printf(file, "%s", buff);
+ my_snprintf(typestr, typestr_length, "DECIMAL(%d,%d)",
+ precision, decimals);
+ return bin_size;
+ }
+
+ case MYSQL_TYPE_FLOAT:
+ {
+ float fl;
+ float4get(fl, ptr);
+ char tmp[320];
+ sprintf(tmp, "%-20g", (double) fl);
+ my_b_printf(file, "%s", tmp); /* my_snprintf doesn't support %-20g */
+ my_snprintf(typestr, typestr_length, "FLOAT");
+ return 4;
+ }
+
+ case MYSQL_TYPE_DOUBLE:
+ {
+ double dbl;
+ float8get(dbl, ptr);
+ char tmp[320];
+ sprintf(tmp, "%-.20g", dbl); /* my_snprintf doesn't support %-20g */
+ my_b_printf(file, "%s", tmp);
+ strcpy(typestr, "DOUBLE");
+ return 8;
+ }
+
+ case MYSQL_TYPE_BIT:
+ {
+ /* Meta-data: bit_len, bytes_in_rec, 2 bytes */
+ uint nbits= ((meta >> 8) * 8) + (meta & 0xFF);
+ length= (nbits + 7) / 8;
+ my_b_write_bit(file, ptr, nbits);
+ my_snprintf(typestr, typestr_length, "BIT(%d)", nbits);
+ return length;
+ }
+
+ case MYSQL_TYPE_TIMESTAMP:
+ {
+ uint32 i32= uint4korr(ptr);
+ my_b_printf(file, "%d", i32);
+ my_snprintf(typestr, typestr_length, "TIMESTAMP");
+ return 4;
+ }
+
+ case MYSQL_TYPE_DATETIME:
+ {
+ size_t d, t;
+ uint64 i64= uint8korr(ptr); /* YYYYMMDDhhmmss */
+ d= i64 / 1000000;
+ t= i64 % 1000000;
+ my_b_printf(file, "%04d-%02d-%02d %02d:%02d:%02d",
+ d / 10000, (d % 10000) / 100, d % 100,
+ t / 10000, (t % 10000) / 100, t % 100);
+ my_snprintf(typestr, typestr_length, "DATETIME");
+ return 8;
+ }
+
+ case MYSQL_TYPE_TIME:
+ {
+ uint32 i32= uint3korr(ptr);
+ my_b_printf(file, "'%02d:%02d:%02d'",
+ i32 / 10000, (i32 % 10000) / 100, i32 % 100);
+ my_snprintf(typestr, typestr_length, "TIME");
+ return 3;
+ }
+
+ case MYSQL_TYPE_DATE:
+ {
+ uint i32= uint3korr(ptr);
+ my_b_printf(file , "'%04d:%02d:%02d'",
+ (i32 / (16L * 32L)), (i32 / 32L % 16L), (i32 % 32L));
+ my_snprintf(typestr, typestr_length, "DATE");
+ return 3;
+ }
+
+ case MYSQL_TYPE_YEAR:
+ {
+ uint32 i32= *ptr;
+ my_b_printf(file, "%04d", i32+ 1900);
+ my_snprintf(typestr, typestr_length, "YEAR");
+ return 1;
+ }
+
+ case MYSQL_TYPE_ENUM:
+ switch (length) {
+ case 1:
+ my_b_printf(file, "%d", (int) *ptr);
+ my_snprintf(typestr, typestr_length, "ENUM(1 byte)");
+ return 1;
+ case 2:
+ {
+ int32 i32= uint2korr(ptr);
+ my_b_printf(file, "%d", i32);
+ my_snprintf(typestr, typestr_length, "ENUM(2 bytes)");
+ return 2;
+ }
+ default:
+ my_b_printf(file, "!! Unknown ENUM packlen=%d", length);
+ 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;
+
+ case MYSQL_TYPE_BLOB:
+ switch (meta) {
+ case 1:
+ length= *ptr;
+ my_b_write_quoted(file, ptr + 1, length);
+ my_snprintf(typestr, typestr_length, "TINYBLOB/TINYTEXT");
+ return length + 1;
+ case 2:
+ length= uint2korr(ptr);
+ my_b_write_quoted(file, ptr + 2, length);
+ my_snprintf(typestr, typestr_length, "BLOB/TEXT");
+ return length + 2;
+ case 3:
+ length= uint3korr(ptr);
+ my_b_write_quoted(file, ptr + 3, length);
+ my_snprintf(typestr, typestr_length, "MEDIUMBLOB/MEDIUMTEXT");
+ return length + 3;
+ case 4:
+ length= uint4korr(ptr);
+ my_b_write_quoted(file, ptr + 4, length);
+ my_snprintf(typestr, typestr_length, "LONGBLOB/LONGTEXT");
+ return length + 4;
+ default:
+ my_b_printf(file, "!! Unknown BLOB packlen=%d", length);
+ return 0;
+ }
+
+ case MYSQL_TYPE_VARCHAR:
+ case MYSQL_TYPE_VAR_STRING:
+ length= meta;
+ my_snprintf(typestr, typestr_length, "VARSTRING(%d)", length);
+ return my_b_write_quoted_with_length(file, ptr, length);
+
+ case MYSQL_TYPE_STRING:
+ my_snprintf(typestr, typestr_length, "STRING(%d)", length);
+ return my_b_write_quoted_with_length(file, ptr, length);
+
+ 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);
+ }
+ break;
+ }
+ *typestr= 0;
+ return 0;
+}
+
+
+/**
+ Print a packed row into IO cache
+
+ @param[in] file IO cache
+ @param[in] td Table definition
+ @param[in] print_event_into Print parameters
+ @param[in] cols_bitmap Column bitmaps.
+ @param[in] value Pointer to packed row
+ @param[in] prefix Row's SQL clause ("SET", "WHERE", etc)
+
+ @retval - number of bytes scanned.
+*/
+
+
+size_t
+Rows_log_event::print_verbose_one_row(IO_CACHE *file, table_def *td,
+ PRINT_EVENT_INFO *print_event_info,
+ MY_BITMAP *cols_bitmap,
+ const uchar *value, const uchar *prefix)
+{
+ const uchar *value0= value;
+ const uchar *null_bits= value;
+ char typestr[64]= "";
+
+ value+= (m_width + 7) / 8;
+
+ my_b_printf(file, "%s", prefix);
+
+ for (size_t i= 0; i < td->size(); i ++)
+ {
+ int is_null= (null_bits[i / 8] >> (i % 8)) & 0x01;
+
+ if (bitmap_is_set(cols_bitmap, i) == 0)
+ continue;
+
+ if (is_null)
+ {
+ my_b_printf(file, "### @%d=NULL", i + 1);
}
else
- fprintf(file, "# ");
+ {
+ my_b_printf(file, "### @%d=", i + 1);
+ size_t size= log_event_print_value(file, value,
+ td->type(i), td->field_metadata(i),
+ typestr, sizeof(typestr));
+ if (!size)
+ return 0;
+
+ value+= size;
+ }
+
+ if (print_event_info->verbose > 1)
+ {
+ my_b_printf(file, " /* ");
+
+ if (typestr[0])
+ my_b_printf(file, "%s ", typestr);
+ else
+ my_b_printf(file, "type=%d ", td->type(i));
+
+ my_b_printf(file, "meta=%d nullable=%d is_null=%d ",
+ td->field_metadata(i),
+ td->maybe_null(i), is_null);
+ my_b_printf(file, "*/");
+ }
+
+ my_b_printf(file, "\n");
}
+ return value - value0;
+}
+
+
+/**
+ Print a row event into IO cache in human readable form (in SQL format)
+
+ @param[in] file IO cache
+ @param[in] print_event_into Print parameters
+*/
+void Rows_log_event::print_verbose(IO_CACHE *file,
+ PRINT_EVENT_INFO *print_event_info)
+{
+ Table_map_log_event *map;
+ table_def *td;
+ const char *sql_command, *sql_clause1, *sql_clause2;
+ Log_event_type type_code= get_type_code();
+
+ switch (type_code) {
+ case WRITE_ROWS_EVENT:
+ sql_command= "INSERT INTO";
+ sql_clause1= "### SET\n";
+ sql_clause2= NULL;
+ break;
+ case DELETE_ROWS_EVENT:
+ sql_command= "DELETE FROM";
+ sql_clause1= "### WHERE\n";
+ sql_clause2= NULL;
+ break;
+ case UPDATE_ROWS_EVENT:
+ sql_command= "UPDATE";
+ sql_clause1= "### WHERE\n";
+ sql_clause2= "### SET\n";
+ break;
+ default:
+ sql_command= sql_clause1= sql_clause2= NULL;
+ DBUG_ASSERT(0); /* Not possible */
+ }
+
+ if (!(map= print_event_info->m_table_map.get_table(m_table_id)) ||
+ !(td= map->create_table_def()))
+ {
+ my_b_printf(file, "### Row event for unknown table #%d", m_table_id);
+ return;
+ }
+
+ for (const uchar *value= m_rows_buf; value < m_rows_end; )
+ {
+ size_t length;
+ my_b_printf(file, "### %s %s.%s\n",
+ sql_command,
+ map->get_db_name(), map->get_table_name());
+ /* Print the first image */
+ if (!(length= print_verbose_one_row(file, td, print_event_info,
+ &m_cols, value,
+ (const uchar*) sql_clause1)))
+ goto end;
+ value+= length;
+
+ /* Print the second image (for UPDATE only) */
+ if (sql_clause2)
+ {
+ if (!(length= print_verbose_one_row(file, td, print_event_info,
+ &m_cols_ai, value,
+ (const uchar*) sql_clause2)))
+ goto end;
+ value+= length;
+ }
+ }
+
+end:
+ delete td;
+}
+
+#ifdef MYSQL_CLIENT
+void free_table_map_log_event(Table_map_log_event *event)
+{
+ delete event;
+}
+#endif
+
+void Log_event::print_base64(IO_CACHE* file,
+ PRINT_EVENT_INFO* print_event_info,
+ bool more)
+{
+ const uchar *ptr= (const uchar *)temp_buf;
+ uint32 size= uint4korr(ptr + EVENT_LEN_OFFSET);
+ DBUG_ENTER("Log_event::print_base64");
+
+ size_t const tmp_str_sz= base64_needed_encoded_length((int) size);
+ char *const tmp_str= (char *) my_malloc(tmp_str_sz, MYF(MY_WME));
+ if (!tmp_str) {
+ fprintf(stderr, "\nError: Out of memory. "
+ "Could not print correct binlog event.\n");
+ DBUG_VOID_RETURN;
+ }
+
+ if (base64_encode(ptr, (size_t) size, tmp_str))
+ {
+ DBUG_ASSERT(0);
+ }
+
+ if (print_event_info->base64_output_mode != BASE64_OUTPUT_DECODE_ROWS)
+ {
+ if (my_b_tell(file) == 0)
+ my_b_printf(file, "\nBINLOG '\n");
+
+ my_b_printf(file, "%s\n", tmp_str);
+
+ if (!more)
+ my_b_printf(file, "'%s\n", print_event_info->delimiter);
+ }
+
+ if (print_event_info->verbose)
+ {
+ Rows_log_event *ev= NULL;
+
+ if (ptr[4] == TABLE_MAP_EVENT)
+ {
+ Table_map_log_event *map;
+ map= new Table_map_log_event((const char*) ptr, size,
+ glob_description_event);
+ print_event_info->m_table_map.set_table(map->get_table_id(), map);
+ }
+ else if (ptr[4] == WRITE_ROWS_EVENT)
+ {
+ ev= new Write_rows_log_event((const char*) ptr, size,
+ glob_description_event);
+ }
+ else if (ptr[4] == DELETE_ROWS_EVENT)
+ {
+ ev= new Delete_rows_log_event((const char*) ptr, size,
+ glob_description_event);
+ }
+ else if (ptr[4] == UPDATE_ROWS_EVENT)
+ {
+ ev= new Update_rows_log_event((const char*) ptr, size,
+ glob_description_event);
+ }
+
+ if (ev)
+ {
+ ev->print_verbose(file, print_event_info);
+ delete ev;
+ }
+ }
+
+ my_free(tmp_str, MYF(0));
+ DBUG_VOID_RETURN;
}
@@ -1049,9 +1965,10 @@ void Log_event::print_header(FILE* file, PRINT_EVENT_INFO* print_event_info)
Log_event::print_timestamp()
*/
-void Log_event::print_timestamp(FILE* file, time_t* ts)
+void Log_event::print_timestamp(IO_CACHE* file, time_t* ts)
{
struct tm *res;
+ DBUG_ENTER("Log_event::print_timestamp");
if (!ts)
ts = &when;
#ifdef MYSQL_SERVER // This is always false
@@ -1061,37 +1978,49 @@ void Log_event::print_timestamp(FILE* file, time_t* ts)
res=localtime(ts);
#endif
- fprintf(file,"%02d%02d%02d %2d:%02d:%02d",
- res->tm_year % 100,
- res->tm_mon+1,
- res->tm_mday,
- res->tm_hour,
- res->tm_min,
- res->tm_sec);
+ my_b_printf(file,"%02d%02d%02d %2d:%02d:%02d",
+ res->tm_year % 100,
+ res->tm_mon+1,
+ res->tm_mday,
+ res->tm_hour,
+ res->tm_min,
+ res->tm_sec);
+ DBUG_VOID_RETURN;
}
#endif /* MYSQL_CLIENT */
+#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
+inline Log_event::enum_skip_reason
+Log_event::continue_group(Relay_log_info *rli)
+{
+ if (rli->slave_skip_counter == 1)
+ return Log_event::EVENT_SKIP_IGNORE;
+ return Log_event::do_shall_skip(rli);
+}
+#endif
+
/**************************************************************************
Query_log_event methods
**************************************************************************/
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
-/*
- Query_log_event::pack_info()
+/**
This (which is used only for SHOW BINLOG EVENTS) could be updated to
print SET @@session_var=. But this is not urgent, as SHOW BINLOG EVENTS is
only an information, it does not produce suitable queries to replay (for
example it does not print LOAD DATA INFILE).
+ @todo
+ show the catalog ??
*/
void Query_log_event::pack_info(Protocol *protocol)
{
// TODO: show the catalog ??
char *buf, *pos;
- if (!(buf= my_malloc(9 + db_len + q_len, MYF(MY_WME))))
+ if (!(buf= (char*) my_malloc(9 + db_len + q_len, MYF(MY_WME))))
return;
pos= buf;
if (!(flags & LOG_EVENT_SUPPRESS_USE_F)
@@ -1106,17 +2035,24 @@ void Query_log_event::pack_info(Protocol *protocol)
memcpy(pos, query, q_len);
pos+= q_len;
}
- protocol->store(buf, (uint) (pos - buf), &my_charset_bin);
+ protocol->store(buf, pos-buf, &my_charset_bin);
my_free(buf, MYF(MY_ALLOW_ZERO_PTR));
}
#endif
#ifndef MYSQL_CLIENT
-/* Utility function for the next method */
+/**
+ Utility function for the next method (Query_log_event::write()) .
+*/
static void write_str_with_code_and_len(char **dst, const char *src,
int len, uint code)
{
+ /*
+ only 1 byte to store the length of catalog, so it should not
+ surpass 255
+ */
+ DBUG_ASSERT(len <= 255);
DBUG_ASSERT(src);
*((*dst)++)= code;
*((*dst)++)= (uchar) len;
@@ -1125,10 +2061,10 @@ static void write_str_with_code_and_len(char **dst, const char *src,
}
-/*
- Query_log_event::write()
+/**
+ Query_log_event::write().
- NOTES:
+ @note
In this event we have to modify the header to have the correct
EVENT_LEN_OFFSET as we don't yet know how many status variables we
will print!
@@ -1136,16 +2072,8 @@ static void write_str_with_code_and_len(char **dst, const char *src,
bool Query_log_event::write(IO_CACHE* file)
{
- uchar buf[QUERY_HEADER_LEN+
- 1+4+ // code of flags2 and flags2
- 1+8+ // code of sql_mode and sql_mode
- 1+1+FN_REFLEN+ // code of catalog and catalog length and catalog
- 1+4+ // code of autoinc and the 2 autoinc variables
- 1+6+ // code of charset and charset
- 1+1+MAX_TIME_ZONE_NAME_LENGTH+ // code of tz and tz length and tz name
- 1+2+ // code of lc_time_names and lc_time_names_number
- 1+2 // code of charset_database and charset_database_number
- ], *start, *start_of_status;
+ uchar buf[QUERY_HEADER_LEN + MAX_SIZE_LOG_EVENT_STATUS];
+ uchar *start, *start_of_status;
ulong event_length;
if (!query)
@@ -1234,7 +2162,7 @@ bool Query_log_event::write(IO_CACHE* file)
recognize Q_CATALOG_CODE and have no problem.
*/
}
- if (auto_increment_increment != 1)
+ if (auto_increment_increment != 1 || auto_increment_offset != 1)
{
*start++= Q_AUTO_INCREMENT;
int2store(start, auto_increment_increment);
@@ -1251,10 +2179,8 @@ bool Query_log_event::write(IO_CACHE* file)
{
/* In the TZ sys table, column Name is of length 64 so this should be ok */
DBUG_ASSERT(time_zone_len <= MAX_TIME_ZONE_NAME_LENGTH);
- *start++= Q_TIME_ZONE_CODE;
- *start++= time_zone_len;
- memcpy(start, time_zone_str, time_zone_len);
- start+= time_zone_len;
+ write_str_with_code_and_len((char **)(&start),
+ time_zone_str, time_zone_len, Q_TIME_ZONE_CODE);
}
if (lc_time_names_number)
{
@@ -1270,7 +2196,17 @@ bool Query_log_event::write(IO_CACHE* file)
int2store(start, charset_database_number);
start+= 2;
}
+ if (table_map_for_update)
+ {
+ *start++= Q_TABLE_MAP_FOR_UPDATE_CODE;
+ int8store(start, table_map_for_update);
+ start+= 8;
+ }
/*
+ NOTE: When adding new status vars, please don't forget to update
+ the MAX_SIZE_LOG_EVENT_STATUS in log_event.h and update function
+ code_name in this file.
+
Here there could be code like
if (command-line-option-which-says-"log_this_variable" && inited)
{
@@ -1292,17 +2228,15 @@ bool Query_log_event::write(IO_CACHE* file)
event_length= (uint) (start-buf) + get_post_header_size_for_derived() + db_len + 1 + q_len;
return (write_header(file, event_length) ||
- my_b_safe_write(file, (byte*) buf, QUERY_HEADER_LEN) ||
+ my_b_safe_write(file, (uchar*) buf, QUERY_HEADER_LEN) ||
write_post_header_for_derived(file) ||
- my_b_safe_write(file, (byte*) start_of_status,
+ my_b_safe_write(file, (uchar*) start_of_status,
(uint) (start-start_of_status)) ||
- my_b_safe_write(file, (db) ? (byte*) db : (byte*)"", db_len + 1) ||
- my_b_safe_write(file, (byte*) query, q_len)) ? 1 : 0;
+ my_b_safe_write(file, (db) ? (uchar*) db : (uchar*)"", db_len + 1) ||
+ my_b_safe_write(file, (uchar*) query, q_len)) ? 1 : 0;
}
-/*
- Query_log_event::Query_log_event()
-
+/**
The simplest constructor that could possibly work. This is used for
creating static objects that have a special meaning and are invisible
to the log.
@@ -1316,7 +2250,7 @@ Query_log_event::Query_log_event()
/*
SYNOPSIS
Query_log_event::Query_log_event()
- thd - thread handle
+ thd_arg - thread handle
query_arg - array of char representing the query
query_length - size of the `query_arg' array
using_trans - there is a modified transactional table
@@ -1332,11 +2266,12 @@ 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, THD::killed_state killed_status_arg)
+ bool suppress_use,
+ THD::killed_state killed_status_arg)
:Log_event(thd_arg,
- ((thd_arg->tmp_table_used || thd_arg->thread_specific_used) ?
- LOG_EVENT_THREAD_SPECIFIC_F : 0) |
- (suppress_use ? LOG_EVENT_SUPPRESS_USE_F : 0),
+ (thd_arg->thread_specific_used ? LOG_EVENT_THREAD_SPECIFIC_F :
+ 0) |
+ (suppress_use ? LOG_EVENT_SUPPRESS_USE_F : 0),
using_trans),
data_buf(0), query(query_arg), catalog(thd_arg->catalog),
db(thd_arg->db), q_len((uint32) query_length),
@@ -1348,19 +2283,25 @@ Query_log_event::Query_log_event(THD* thd_arg, const char* query_arg,
auto_increment_increment(thd_arg->variables.auto_increment_increment),
auto_increment_offset(thd_arg->variables.auto_increment_offset),
lc_time_names_number(thd_arg->variables.lc_time_names->number),
- charset_database_number(0)
+ charset_database_number(0),
+ table_map_for_update((ulonglong)thd_arg->table_map_for_update)
{
time_t end_time;
if (killed_status_arg == THD::KILLED_NO_VALUE)
killed_status_arg= thd_arg->killed;
error_code=
- (killed_status_arg == THD::NOT_KILLED) ? thd_arg->net.last_errno :
+ (killed_status_arg == THD::NOT_KILLED) ?
+ (thd_arg->is_error() ? thd_arg->main_da.sql_errno() : 0) :
((thd_arg->system_thread & SYSTEM_THREAD_DELAYED_INSERT) ? 0 :
- thd->killed_errno());
+ thd_arg->killed_errno());
time(&end_time);
- exec_time = (ulong) (end_time - thd->start_time);
+ exec_time = (ulong) (end_time - thd_arg->start_time);
+ /**
+ @todo this means that if we have no catalog, then it is replicated
+ as an existing catalog of length zero. is that safe? /sven
+ */
catalog_len = (catalog) ? (uint32) strlen(catalog) : 0;
/* status_vars_len is set just before writing the event */
db_len = (db) ? (uint32) strlen(db) : 0;
@@ -1369,15 +2310,16 @@ Query_log_event::Query_log_event(THD* thd_arg, const char* query_arg,
/*
If we don't use flags2 for anything else than options contained in
- thd->options, it would be more efficient to flags2=thd_arg->options
- (OPTIONS_WRITTEN_TO_BINLOG would be used only at reading time).
+ thd_arg->options, it would be more efficient to flags2=thd_arg->options
+ (OPTIONS_WRITTEN_TO_BIN_LOG would be used only at reading time).
But it's likely that we don't want to use 32 bits for 3 bits; in the future
we will probably want to reclaim the 29 bits. So we need the &.
*/
flags2= (uint32) (thd_arg->options & OPTIONS_WRITTEN_TO_BIN_LOG);
- DBUG_ASSERT(thd->variables.character_set_client->number < 256*256);
- DBUG_ASSERT(thd->variables.collation_connection->number < 256*256);
- DBUG_ASSERT(thd->variables.collation_server->number < 256*256);
+ DBUG_ASSERT(thd_arg->variables.character_set_client->number < 256*256);
+ DBUG_ASSERT(thd_arg->variables.collation_connection->number < 256*256);
+ DBUG_ASSERT(thd_arg->variables.collation_server->number < 256*256);
+ DBUG_ASSERT(thd_arg->variables.character_set_client->mbminlen == 1);
int2store(charset, thd_arg->variables.character_set_client->number);
int2store(charset+2, thd_arg->variables.collation_connection->number);
int2store(charset+4, thd_arg->variables.collation_server->number);
@@ -1437,7 +2379,7 @@ get_str_len_and_pointer(const Log_event::Byte **src,
if (length > 0)
{
if (*src + length >= end)
- return (int) (*src + length - end + 1); // Number of bytes missing
+ return *src + length - end + 1; // Number of bytes missing
*dst= (char *)*src + 1; // Will be copied later
}
*len= length;
@@ -1471,6 +2413,7 @@ code_name(int code)
case Q_CATALOG_NZ_CODE: return "Q_CATALOG_NZ_CODE";
case Q_LC_TIME_NAMES_CODE: return "Q_LC_TIME_NAMES_CODE";
case Q_CHARSET_DATABASE_CODE: return "Q_CHARSET_DATABASE_CODE";
+ case Q_TABLE_MAP_FOR_UPDATE_CODE: return "Q_TABLE_MAP_FOR_UPDATE_CODE";
}
sprintf(buf, "CODE#%d", code);
return buf;
@@ -1495,19 +2438,20 @@ code_name(int code)
} \
} while (0)
-/*
- Query_log_event::Query_log_event()
+
+/**
This is used by the SQL slave thread to prepare the event before execution.
*/
-
Query_log_event::Query_log_event(const char* buf, uint event_len,
- const Format_description_log_event *description_event,
+ const Format_description_log_event
+ *description_event,
Log_event_type event_type)
:Log_event(buf, description_event), data_buf(0), query(NullS),
db(NullS), catalog_len(0), status_vars_len(0),
flags2_inited(0), sql_mode_inited(0), charset_inited(0),
auto_increment_increment(1), auto_increment_offset(1),
- time_zone_len(0), lc_time_names_number(0), charset_database_number(0)
+ time_zone_len(0), lc_time_names_number(0), charset_database_number(0),
+ table_map_for_update(0)
{
ulong data_len;
uint32 tmp;
@@ -1649,6 +2593,11 @@ Query_log_event::Query_log_event(const char* buf, uint event_len,
charset_database_number= uint2korr(pos);
pos+= 2;
break;
+ case Q_TABLE_MAP_FOR_UPDATE_CODE:
+ CHECK_SPACE(pos, end, 8);
+ table_map_for_update= uint8korr(pos);
+ pos+= 8;
+ break;
default:
/* That's why you must write status vars in growing order of code */
DBUG_PRINT("info",("Query_log_event has unknown status vars (first has\
@@ -1673,6 +2622,11 @@ Query_log_event::Query_log_event(const char* buf, uint event_len,
DBUG_VOID_RETURN;
if (catalog_len) // If catalog is given
{
+ /**
+ @todo we should clean up and do only copy_str_and_move; it
+ works for both cases. Then we can remove the catalog_nz
+ flag. /sven
+ */
if (likely(catalog_nz)) // true except if event comes from 5.0.0|1|2|3.
copy_str_and_move(&catalog, &start, catalog_len);
else
@@ -1685,6 +2639,13 @@ Query_log_event::Query_log_event(const char* buf, uint event_len,
if (time_zone_len)
copy_str_and_move(&time_zone_str, &start, time_zone_len);
+ /**
+ if time_zone_len or catalog_len are 0, then time_zone and catalog
+ are uninitialized at this point. shouldn't they point to the
+ zero-length null-terminated strings we allocated space for in the
+ my_alloc call above? /sven
+ */
+
/* A 2nd variable part; this is common to all versions */
memcpy((char*) start, end, data_len); // Copy db and query
start[data_len]= '\0'; // End query with \0 (For safetly)
@@ -1695,12 +2656,14 @@ Query_log_event::Query_log_event(const char* buf, uint event_len,
}
-/*
- Query_log_event::print()
-*/
-
#ifdef MYSQL_CLIENT
-void Query_log_event::print_query_header(FILE* file,
+/**
+ Query_log_event::print().
+
+ @todo
+ print the catalog ??
+*/
+void Query_log_event::print_query_header(IO_CACHE* file,
PRINT_EVENT_INFO* print_event_info)
{
// TODO: print the catalog ??
@@ -1710,26 +2673,35 @@ void Query_log_event::print_query_header(FILE* file,
if (!print_event_info->short_form)
{
- print_header(file, print_event_info);
- fprintf(file, "\t%s\tthread_id=%lu\texec_time=%lu\terror_code=%d\n",
- get_type_str(), (ulong) thread_id, (ulong) exec_time, error_code);
+ print_header(file, print_event_info, FALSE);
+ my_b_printf(file, "\t%s\tthread_id=%lu\texec_time=%lu\terror_code=%d\n",
+ get_type_str(), (ulong) thread_id, (ulong) exec_time,
+ error_code);
}
if (!(flags & LOG_EVENT_SUPPRESS_USE_F) && db)
{
- if (different_db= memcmp(print_event_info->db, db, db_len + 1))
+ if ((different_db= memcmp(print_event_info->db, db, db_len + 1)))
memcpy(print_event_info->db, db, db_len + 1);
if (db[0] && different_db)
- fprintf(file, "use %s%s\n", db, print_event_info->delimiter);
+ my_b_printf(file, "use %s%s\n", db, print_event_info->delimiter);
}
end=int10_to_str((long) when, strmov(buff,"SET TIMESTAMP="),10);
end= strmov(end, print_event_info->delimiter);
*end++='\n';
- my_fwrite(file, (byte*) buff, (uint) (end-buff),MYF(MY_NABP | MY_WME));
- if (flags & LOG_EVENT_THREAD_SPECIFIC_F)
- fprintf(file,"SET @@session.pseudo_thread_id=%lu%s\n",
- (ulong)thread_id, print_event_info->delimiter);
+ my_b_write(file, (uchar*) buff, (uint) (end-buff));
+ if ((!print_event_info->thread_id_printed ||
+ ((flags & LOG_EVENT_THREAD_SPECIFIC_F) &&
+ thread_id != print_event_info->thread_id)))
+ {
+ // If --short-form, print deterministic value instead of pseudo_thread_id.
+ my_b_printf(file,"SET @@session.pseudo_thread_id=%lu%s\n",
+ short_form ? 999999999 : (ulong)thread_id,
+ print_event_info->delimiter);
+ print_event_info->thread_id= thread_id;
+ print_event_info->thread_id_printed= 1;
+ }
/*
If flags2_inited==0, this is an event from 3.23 or 4.0; nothing to
@@ -1751,14 +2723,16 @@ void Query_log_event::print_query_header(FILE* file,
if (unlikely(tmp)) /* some bits have changed */
{
bool need_comma= 0;
- fprintf(file, "SET ");
+ my_b_printf(file, "SET ");
print_set_option(file, tmp, OPTION_NO_FOREIGN_KEY_CHECKS, ~flags2,
- "@@session.foreign_key_checks", &need_comma);
+ "@@session.foreign_key_checks", &need_comma);
print_set_option(file, tmp, OPTION_AUTO_IS_NULL, flags2,
- "@@session.sql_auto_is_null", &need_comma);
+ "@@session.sql_auto_is_null", &need_comma);
print_set_option(file, tmp, OPTION_RELAXED_UNIQUE_CHECKS, ~flags2,
- "@@session.unique_checks", &need_comma);
- fprintf(file,"%s\n", print_event_info->delimiter);
+ "@@session.unique_checks", &need_comma);
+ print_set_option(file, tmp, OPTION_NOT_AUTOCOMMIT, ~flags2,
+ "@@session.autocommit", &need_comma);
+ my_b_printf(file,"%s\n", print_event_info->delimiter);
print_event_info->flags2= flags2;
}
}
@@ -1776,84 +2750,74 @@ void Query_log_event::print_query_header(FILE* file,
gracefully). So this code should always be good.
*/
- if (likely(sql_mode_inited))
+ if (likely(sql_mode_inited) &&
+ (unlikely(print_event_info->sql_mode != sql_mode ||
+ !print_event_info->sql_mode_inited)))
{
- if (unlikely(!print_event_info->sql_mode_inited)) /* first Query event */
- {
- print_event_info->sql_mode_inited= 1;
- /* force a difference to force write */
- print_event_info->sql_mode= ~sql_mode;
- }
- if (unlikely(print_event_info->sql_mode != sql_mode))
- {
- fprintf(file,"SET @@session.sql_mode=%lu%s\n",
- (ulong)sql_mode, print_event_info->delimiter);
- print_event_info->sql_mode= sql_mode;
- }
+ my_b_printf(file,"SET @@session.sql_mode=%lu%s\n",
+ (ulong)sql_mode, print_event_info->delimiter);
+ print_event_info->sql_mode= sql_mode;
+ print_event_info->sql_mode_inited= 1;
}
if (print_event_info->auto_increment_increment != auto_increment_increment ||
print_event_info->auto_increment_offset != auto_increment_offset)
{
- fprintf(file,"SET @@session.auto_increment_increment=%lu, @@session.auto_increment_offset=%lu%s\n",
- auto_increment_increment,auto_increment_offset,
- print_event_info->delimiter);
+ my_b_printf(file,"SET @@session.auto_increment_increment=%lu, @@session.auto_increment_offset=%lu%s\n",
+ auto_increment_increment,auto_increment_offset,
+ print_event_info->delimiter);
print_event_info->auto_increment_increment= auto_increment_increment;
print_event_info->auto_increment_offset= auto_increment_offset;
}
/* TODO: print the catalog when we feature SET CATALOG */
- if (likely(charset_inited))
+ if (likely(charset_inited) &&
+ (unlikely(!print_event_info->charset_inited ||
+ bcmp((uchar*) print_event_info->charset, (uchar*) charset, 6))))
{
- if (unlikely(!print_event_info->charset_inited)) /* first Query event */
+ CHARSET_INFO *cs_info= get_charset(uint2korr(charset), MYF(MY_WME));
+ if (cs_info)
{
- print_event_info->charset_inited= 1;
- print_event_info->charset[0]= ~charset[0]; // force a difference to force write
- }
- if (unlikely(bcmp(print_event_info->charset, charset, 6)))
- {
- CHARSET_INFO *cs_info= get_charset(uint2korr(charset), MYF(MY_WME));
- if (cs_info)
- {
- /* for mysql client */
- fprintf(file, "/*!\\C %s */%s\n",
- cs_info->csname, print_event_info->delimiter);
- }
- fprintf(file,"SET "
- "@@session.character_set_client=%d,"
- "@@session.collation_connection=%d,"
- "@@session.collation_server=%d"
- "%s\n",
- uint2korr(charset),
- uint2korr(charset+2),
- uint2korr(charset+4),
- print_event_info->delimiter);
- memcpy(print_event_info->charset, charset, 6);
+ /* for mysql client */
+ my_b_printf(file, "/*!\\C %s */%s\n",
+ cs_info->csname, print_event_info->delimiter);
}
+ my_b_printf(file,"SET "
+ "@@session.character_set_client=%d,"
+ "@@session.collation_connection=%d,"
+ "@@session.collation_server=%d"
+ "%s\n",
+ uint2korr(charset),
+ uint2korr(charset+2),
+ uint2korr(charset+4),
+ print_event_info->delimiter);
+ memcpy(print_event_info->charset, charset, 6);
+ print_event_info->charset_inited= 1;
}
if (time_zone_len)
{
- if (bcmp(print_event_info->time_zone_str, time_zone_str, time_zone_len+1))
+ if (bcmp((uchar*) print_event_info->time_zone_str,
+ (uchar*) time_zone_str, time_zone_len+1))
{
- fprintf(file,"SET @@session.time_zone='%s'%s\n",
- time_zone_str, print_event_info->delimiter);
+ my_b_printf(file,"SET @@session.time_zone='%s'%s\n",
+ time_zone_str, print_event_info->delimiter);
memcpy(print_event_info->time_zone_str, time_zone_str, time_zone_len+1);
}
}
if (lc_time_names_number != print_event_info->lc_time_names_number)
{
- fprintf(file, "SET @@session.lc_time_names=%d%s\n",
- lc_time_names_number, print_event_info->delimiter);
+ my_b_printf(file, "SET @@session.lc_time_names=%d%s\n",
+ lc_time_names_number, print_event_info->delimiter);
print_event_info->lc_time_names_number= lc_time_names_number;
}
if (charset_database_number != print_event_info->charset_database_number)
{
if (charset_database_number)
- fprintf(file, "SET @@session.collation_database=%d%s\n",
- charset_database_number, print_event_info->delimiter);
+ my_b_printf(file, "SET @@session.collation_database=%d%s\n",
+ charset_database_number, print_event_info->delimiter);
else
- fprintf(file, "SET @@session.collation_database=DEFAULT%s\n",
- print_event_info->delimiter);
+ my_b_printf(file, "SET @@session.collation_database=DEFAULT%s\n",
+ print_event_info->delimiter);
print_event_info->charset_database_number= charset_database_number;
}
}
@@ -1861,54 +2825,60 @@ void Query_log_event::print_query_header(FILE* file,
void Query_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info)
{
- print_query_header(file, print_event_info);
- my_fwrite(file, (byte*) query, q_len, MYF(MY_NABP | MY_WME));
- fprintf(file, "\n%s\n", print_event_info->delimiter);
+ Write_on_release_cache cache(&print_event_info->head_cache, file);
+
+ print_query_header(&cache, print_event_info);
+ my_b_write(&cache, (uchar*) query, q_len);
+ my_b_printf(&cache, "\n%s\n", print_event_info->delimiter);
}
#endif /* MYSQL_CLIENT */
/*
- Query_log_event::exec_event()
+ Query_log_event::do_apply_event()
*/
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
-static const char *rewrite_db(const char *db)
+int Query_log_event::do_apply_event(Relay_log_info const *rli)
{
- if (replicate_rewrite_db.is_empty() || db == NULL)
- return db;
- I_List_iterator<i_string_pair> it(replicate_rewrite_db);
- i_string_pair* tmp;
-
- while ((tmp=it++))
- {
- if (strcmp(tmp->key, db) == 0)
- return tmp->val;
- }
- return db;
-}
-
-
-int Query_log_event::exec_event(struct st_relay_log_info* rli)
-{
- return exec_event(rli, query, q_len);
+ return do_apply_event(rli, query, q_len);
}
-int Query_log_event::exec_event(struct st_relay_log_info* rli,
- const char *query_arg, uint32 q_len_arg)
+/**
+ @todo
+ Compare the values of "affected rows" around here. Something
+ like:
+ @code
+ if ((uint32) affected_in_event != (uint32) affected_on_slave)
+ {
+ sql_print_error("Slave: did not get the expected number of affected \
+ rows running query from master - expected %d, got %d (this numbers \
+ should have matched modulo 4294967296).", 0, ...);
+ thd->query_error = 1;
+ }
+ @endcode
+ We may also want an option to tell the slave to ignore "affected"
+ mismatch. This mismatch could be implemented with a new ER_ code, and
+ to ignore it you would use --slave-skip-errors...
+*/
+int Query_log_event::do_apply_event(Relay_log_info const *rli,
+ const char *query_arg, uint32 q_len_arg)
{
- const char *new_db= rewrite_db(db);
+ LEX_STRING new_db;
int expected_error,actual_error= 0;
/*
- Colleagues: please never free(thd->catalog) in MySQL. This would lead to
- bugs as here thd->catalog is a part of an alloced block, not an entire
- alloced block (see Query_log_event::exec_event()). Same for thd->db.
- Thank you.
+ Colleagues: please never free(thd->catalog) in MySQL. This would
+ lead to bugs as here thd->catalog is a part of an alloced block,
+ not an entire alloced block (see
+ Query_log_event::do_apply_event()). Same for thd->db. Thank
+ you.
*/
thd->catalog= catalog_len ? (char *) catalog : (char *)"";
- thd->set_db(new_db, (uint) strlen(new_db)); /* allocates a copy of 'db' */
+ 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); /* allocates a copy of 'db' */
thd->variables.auto_increment_increment= auto_increment_increment;
thd->variables.auto_increment_offset= auto_increment_offset;
@@ -1922,10 +2892,41 @@ int Query_log_event::exec_event(struct st_relay_log_info* rli,
END of the current log event (COMMIT). We save it in rli so that InnoDB can
access it.
*/
- rli->future_group_master_log_pos= log_pos;
+ const_cast<Relay_log_info*>(rli)->future_group_master_log_pos= log_pos;
DBUG_PRINT("info", ("log_pos: %lu", (ulong) log_pos));
- clear_all_errors(thd, rli);
+ clear_all_errors(thd, const_cast<Relay_log_info*>(rli));
+ if (strcmp("COMMIT", query) == 0 && rli->tables_to_lock)
+ {
+ /*
+ Cleaning-up the last statement context:
+ the terminal event of the current statement flagged with
+ STMT_END_F got filtered out in ndb circular replication.
+ */
+ int error;
+ char llbuff[22];
+ 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; "
+ "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,
+ llbuff));
+ }
+ /*
+ Executing a part of rli->stmt_done() logics that does not deal
+ with group position change. The part is redundant now but is
+ future-change-proof addon, e.g if COMMIT handling will start checking
+ invariants like IN_STMT flag must be off at committing the transaction.
+ */
+ const_cast<Relay_log_info*>(rli)->inc_event_relay_log_pos();
+ const_cast<Relay_log_info*>(rli)->clear_flag(Relay_log_info::IN_STMT);
+ }
+ else
+ {
+ const_cast<Relay_log_info*>(rli)->clear_tables_to_lock();
+ }
/*
Note: We do not need to execute reset_one_shot_variables() if this
@@ -1934,10 +2935,10 @@ int Query_log_event::exec_event(struct st_relay_log_info* rli,
its companion query. If the SET is ignored because of
db_ok(), the companion query will also be ignored, and if
the companion query is ignored in the db_ok() test of
- ::exec_event(), then the companion SET also have so we
- don't need to reset_one_shot_variables().
+ ::do_apply_event(), then the companion SET also have so
+ we don't need to reset_one_shot_variables().
*/
- if (db_ok(thd->db, replicate_do_db, replicate_ignore_db))
+ if (rpl_filter->db_ok(thd->db))
{
thd->set_time((time_t)when);
thd->query_length= q_len_arg;
@@ -1953,21 +2954,22 @@ int Query_log_event::exec_event(struct st_relay_log_info* rli,
{
if (flags2_inited)
/*
- all bits of thd->options which are 1 in OPTIONS_WRITTEN_TO_BIN_LOG must
- take their value from flags2.
+ all bits of thd->options which are 1 in OPTIONS_WRITTEN_TO_BIN_LOG
+ must take their value from flags2.
*/
- thd->options= flags2|(thd->options & ~(ulong)OPTIONS_WRITTEN_TO_BIN_LOG);
+ thd->options= flags2|(thd->options & ~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 nothing to do.
+ Rotate_log_event which reset thd->options and sql_mode etc, so
+ nothing to do.
*/
/*
We do not replicate IGNORE_DIR_IN_CREATE. That is, if the master is a
slave which runs with SQL_MODE=IGNORE_DIR_IN_CREATE, this should not
force us to ignore the dir too. Imagine you are a ring of machines, and
- one has a disk problem so that you temporarily need IGNORE_DIR_IN_CREATE
- on this machine; you don't want it to propagate elsewhere (you don't want
- all slaves to start ignoring the dirs).
+ one has a disk problem so that you temporarily need
+ IGNORE_DIR_IN_CREATE on this machine; you don't want it to propagate
+ elsewhere (you don't want all slaves to start ignoring the dirs).
*/
if (sql_mode_inited)
thd->variables.sql_mode=
@@ -2000,8 +3002,7 @@ int Query_log_event::exec_event(struct st_relay_log_info* rli,
if (time_zone_len)
{
String tmp(time_zone_str, time_zone_len, &my_charset_bin);
- if (!(thd->variables.time_zone=
- my_tz_find_with_opening_tz_tables(thd, &tmp)))
+ if (!(thd->variables.time_zone= my_tz_find(thd, &tmp)))
{
my_error(ER_UNKNOWN_TIME_ZONE, MYF(0), tmp.c_ptr());
thd->variables.time_zone= global_system_variables.time_zone;
@@ -2036,10 +3037,12 @@ int Query_log_event::exec_event(struct st_relay_log_info* rli,
else
thd->variables.collation_database= thd->db_charset;
+ thd->table_map_for_update= (table_map)table_map_for_update;
+
/* 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);
}
else
{
@@ -2051,24 +3054,24 @@ int Query_log_event::exec_event(struct st_relay_log_info* rli,
to check/fix it.
*/
if (mysql_test_parse_for_slave(thd, thd->query, thd->query_length))
- clear_all_errors(thd, rli); /* Can ignore query */
+ clear_all_errors(thd, const_cast<Relay_log_info*>(rli)); /* Can ignore query */
else
{
- slave_print_error(rli,expected_error,
+ rli->report(ERROR_LEVEL, expected_error,
"\
Query partially completed on the master (error on master: %d) \
and was aborted. There is a chance that your master is inconsistent at this \
point. If you are sure that your master is ok, run this query manually on the \
slave and then restart the slave with SET GLOBAL SQL_SLAVE_SKIP_COUNTER=1; \
START SLAVE; . Query: '%s'", expected_error, thd->query);
- thd->query_error= 1;
+ thd->is_slave_error= 1;
}
goto end;
}
/* If the query was not ignored, it is printed to the general log */
- if (thd->net.last_errno != ER_SLAVE_IGNORED_TABLE)
- mysql_log.write(thd,COM_QUERY,"%s",thd->query);
+ if (!thd->is_error() || thd->main_da.sql_errno() != ER_SLAVE_IGNORED_TABLE)
+ general_log_write(thd, COM_QUERY, thd->query, thd->query_length);
compare_errors:
@@ -2076,24 +3079,25 @@ compare_errors:
If we expected a non-zero error code, and we don't get the same error
code, and none of them should be ignored.
*/
- DBUG_PRINT("info",("expected_error: %d last_errno: %d",
- expected_error, thd->net.last_errno));
- if ((expected_error != (actual_error= thd->net.last_errno)) &&
+ actual_error= thd->is_error() ? thd->main_da.sql_errno() : 0;
+ DBUG_PRINT("info",("expected_error: %d sql_errno: %d",
+ expected_error, actual_error));
+ if ((expected_error != actual_error) &&
expected_error &&
!ignored_error_code(actual_error) &&
!ignored_error_code(expected_error))
{
- slave_print_error(rli, 0,
- "\
-Query caused different errors on master and slave. \
+ rli->report(ERROR_LEVEL, 0,
+ "\
+Query caused different errors on master and slave. \
Error on master: '%s' (%d), Error on slave: '%s' (%d). \
Default database: '%s'. Query: '%s'",
- ER_SAFE(expected_error),
- expected_error,
- actual_error ? thd->net.last_error: "no error",
- actual_error,
- print_slave_db_safe(db), query_arg);
- thd->query_error= 1;
+ ER_SAFE(expected_error),
+ expected_error,
+ actual_error ? thd->main_da.message() : "no error",
+ actual_error,
+ print_slave_db_safe(db), query_arg);
+ thd->is_slave_error= 1;
}
/*
If we get the same error code as expected, or they should be ignored.
@@ -2102,20 +3106,20 @@ Default database: '%s'. Query: '%s'",
ignored_error_code(actual_error))
{
DBUG_PRINT("info",("error ignored"));
- clear_all_errors(thd, rli);
+ clear_all_errors(thd, const_cast<Relay_log_info*>(rli));
thd->killed= THD::NOT_KILLED;
}
/*
Other cases: mostly we expected no error and get one.
*/
- else if (thd->query_error || thd->is_fatal_error)
+ else if (thd->is_slave_error || thd->is_fatal_error)
{
- slave_print_error(rli,actual_error,
- "Error '%s' on query. Default database: '%s'. Query: '%s'",
- (actual_error ? thd->net.last_error :
- "unexpected success or fatal error"),
- print_slave_db_safe(thd->db), query_arg);
- thd->query_error= 1;
+ rli->report(ERROR_LEVEL, actual_error,
+ "Error '%s' on query. Default database: '%s'. Query: '%s'",
+ (actual_error ? thd->main_da.message() :
+ "unexpected success or fatal error"),
+ print_slave_db_safe(thd->db), query_arg);
+ thd->is_slave_error= 1;
}
/*
@@ -2126,7 +3130,7 @@ Default database: '%s'. Query: '%s'",
sql_print_error("Slave: did not get the expected number of affected \
rows running query from master - expected %d, got %d (this numbers \
should have matched modulo 4294967296).", 0, ...);
- thd->query_error = 1;
+ thd->is_slave_error = 1;
}
We may also want an option to tell the slave to ignore "affected"
mismatch. This mismatch could be implemented with a new ER_ code, and
@@ -2160,32 +3164,61 @@ end:
thd->query_length= 0;
VOID(pthread_mutex_unlock(&LOCK_thread_count));
close_thread_tables(thd);
- free_root(thd->mem_root,MYF(MY_KEEP_PREALLOC));
/*
- If there was an error we stop. Otherwise we increment positions. Note that
- we will not increment group* positions if we are just after a SET
- ONE_SHOT, because SET ONE_SHOT should not be separated from its following
- updating query.
+ As a disk space optimization, future masters will not log an event for
+ LAST_INSERT_ID() if that function returned 0 (and thus they will be able
+ to replace the THD::stmt_depends_on_first_successful_insert_id_in_prev_stmt
+ variable by (THD->first_successful_insert_id_in_prev_stmt > 0) ; with the
+ resetting below we are ready to support that.
*/
- return (thd->query_error ? thd->query_error :
- (thd->one_shot_set ? (rli->inc_event_relay_log_pos(),0) :
- Log_event::exec_event(rli)));
+ thd->first_successful_insert_id_in_prev_stmt_for_binlog= 0;
+ thd->first_successful_insert_id_in_prev_stmt= 0;
+ thd->stmt_depends_on_first_successful_insert_id_in_prev_stmt= 0;
+ free_root(thd->mem_root,MYF(MY_KEEP_PREALLOC));
+ return thd->is_slave_error;
}
-#endif
+int Query_log_event::do_update_pos(Relay_log_info *rli)
+{
+ /*
+ Note that we will not increment group* positions if we are just
+ after a SET ONE_SHOT, because SET ONE_SHOT should not be separated
+ from its following updating query.
+ */
+ if (thd->one_shot_set)
+ {
+ rli->inc_event_relay_log_pos();
+ return 0;
+ }
+ else
+ return Log_event::do_update_pos(rli);
+}
-/**************************************************************************
- Muted_query_log_event methods
-**************************************************************************/
-#ifndef MYSQL_CLIENT
-/*
- Muted_query_log_event::Muted_query_log_event()
-*/
-Muted_query_log_event::Muted_query_log_event()
- :Query_log_event()
+Log_event::enum_skip_reason
+Query_log_event::do_shall_skip(Relay_log_info *rli)
{
+ DBUG_ENTER("Query_log_event::do_shall_skip");
+ DBUG_PRINT("debug", ("query: %s; q_len: %d", query, q_len));
+ DBUG_ASSERT(query && q_len > 0);
+
+ if (rli->slave_skip_counter > 0)
+ {
+ if (strcmp("BEGIN", query) == 0)
+ {
+ thd->options|= OPTION_BEGIN;
+ DBUG_RETURN(Log_event::continue_group(rli));
+ }
+
+ if (strcmp("COMMIT", query) == 0 || strcmp("ROLLBACK", query) == 0)
+ {
+ thd->options&= ~OPTION_BEGIN;
+ DBUG_RETURN(Log_event::EVENT_SKIP_COUNT);
+ }
+ }
+ DBUG_RETURN(Log_event::do_shall_skip(rli));
}
+
#endif
@@ -2194,9 +3227,10 @@ Muted_query_log_event::Muted_query_log_event()
**************************************************************************/
#ifndef MYSQL_CLIENT
-Start_log_event_v3::Start_log_event_v3() :Log_event(), binlog_version(BINLOG_VERSION), artificial_event(0)
+Start_log_event_v3::Start_log_event_v3()
+ :Log_event(), created(0), binlog_version(BINLOG_VERSION),
+ dont_set_created(0)
{
- created= when;
memcpy(server_version, ::server_version, ST_SERVER_VER_LEN);
}
#endif
@@ -2225,20 +3259,25 @@ void Start_log_event_v3::pack_info(Protocol *protocol)
#ifdef MYSQL_CLIENT
void Start_log_event_v3::print(FILE* file, PRINT_EVENT_INFO* print_event_info)
{
+ DBUG_ENTER("Start_log_event_v3::print");
+
+ Write_on_release_cache cache(&print_event_info->head_cache, file,
+ Write_on_release_cache::FLUSH_F);
+
if (!print_event_info->short_form)
{
- print_header(file, print_event_info);
- fprintf(file, "\tStart: binlog v %d, server v %s created ", binlog_version,
- server_version);
- print_timestamp(file);
+ print_header(&cache, print_event_info, FALSE);
+ my_b_printf(&cache, "\tStart: binlog v %d, server v %s created ",
+ binlog_version, server_version);
+ print_timestamp(&cache);
if (created)
- fprintf(file," at startup");
- fputc('\n', file);
+ my_b_printf(&cache," at startup");
+ my_b_printf(&cache, "\n");
if (flags & LOG_EVENT_BINLOG_IN_USE_F)
- fprintf(file, "# Warning: this binlog was not closed properly. "
- "Most probably mysqld crashed writing it.\n");
+ my_b_printf(&cache, "# Warning: this binlog was not closed properly. "
+ "Most probably mysqld crashed writing it.\n");
}
- if (!artificial_event && created)
+ if (!is_artificial_event() && created)
{
#ifdef WHEN_WE_HAVE_THE_RESET_CONNECTION_SQL_COMMAND
/*
@@ -2247,12 +3286,21 @@ void Start_log_event_v3::print(FILE* file, PRINT_EVENT_INFO* print_event_info)
and rollback unfinished transaction.
Probably this can be done with RESET CONNECTION (syntax to be defined).
*/
- fprintf(file,"RESET CONNECTION%s\n", print_event_info->delimiter);
+ my_b_printf(&cache,"RESET CONNECTION%s\n", print_event_info->delimiter);
#else
- fprintf(file,"ROLLBACK%s\n", print_event_info->delimiter);
+ my_b_printf(&cache,"ROLLBACK%s\n", print_event_info->delimiter);
#endif
}
- fflush(file);
+ if (temp_buf &&
+ print_event_info->base64_output_mode != BASE64_OUTPUT_NEVER &&
+ !print_event_info->short_form)
+ {
+ if (print_event_info->base64_output_mode != BASE64_OUTPUT_DECODE_ROWS)
+ my_b_printf(&cache, "BINLOG '\n");
+ print_base64(&cache, print_event_info, FALSE);
+ print_event_info->printed_fd_event= TRUE;
+ }
+ DBUG_VOID_RETURN;
}
#endif /* MYSQL_CLIENT */
@@ -2261,7 +3309,8 @@ void Start_log_event_v3::print(FILE* file, PRINT_EVENT_INFO* print_event_info)
*/
Start_log_event_v3::Start_log_event_v3(const char* buf,
- const Format_description_log_event* description_event)
+ const Format_description_log_event
+ *description_event)
:Log_event(buf, description_event)
{
buf+= description_event->common_header_len;
@@ -2271,8 +3320,7 @@ Start_log_event_v3::Start_log_event_v3(const char* buf,
// prevent overrun if log is corrupted on disk
server_version[ST_SERVER_VER_LEN-1]= 0;
created= uint4korr(buf+ST_CREATED_OFFSET);
- /* We use log_pos to mark if this was an artificial event or not */
- artificial_event= (log_pos == 0);
+ dont_set_created= 1;
}
@@ -2286,36 +3334,38 @@ bool Start_log_event_v3::write(IO_CACHE* file)
char buff[START_V3_HEADER_LEN];
int2store(buff + ST_BINLOG_VER_OFFSET,binlog_version);
memcpy(buff + ST_SERVER_VER_OFFSET,server_version,ST_SERVER_VER_LEN);
+ if (!dont_set_created)
+ created= when= get_time();
int4store(buff + ST_CREATED_OFFSET,created);
return (write_header(file, sizeof(buff)) ||
- my_b_safe_write(file, (byte*) buff, sizeof(buff)));
+ my_b_safe_write(file, (uchar*) buff, sizeof(buff)));
}
#endif
-/*
- Start_log_event_v3::exec_event()
+#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
+/**
+ Start_log_event_v3::do_apply_event() .
The master started
- IMPLEMENTATION
+ IMPLEMENTATION
- To handle the case where the master died without having time to write
- DROP TEMPORARY TABLE, DO RELEASE_LOCK (prepared statements' deletion is
- TODO), we clean up all temporary tables that we got, if we are sure we
- can (see below).
+ DROP TEMPORARY TABLE, DO RELEASE_LOCK (prepared statements' deletion is
+ TODO), we clean up all temporary tables that we got, if we are sure we
+ can (see below).
- TODO
+ @todo
- Remove all active user locks.
- Guilhem 2003-06: this is true but not urgent: the worst it can cause is
- the use of a bit of memory for a user lock which will not be used
- anymore. If the user lock is later used, the old one will be released. In
- other words, no deadlock problem.
+ Guilhem 2003-06: this is true but not urgent: the worst it can cause is
+ the use of a bit of memory for a user lock which will not be used
+ anymore. If the user lock is later used, the old one will be released. In
+ other words, no deadlock problem.
*/
-#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
-int Start_log_event_v3::exec_event(struct st_relay_log_info* rli)
+int Start_log_event_v3::do_apply_event(Relay_log_info const *rli)
{
- DBUG_ENTER("Start_log_event_v3::exec_event");
+ DBUG_ENTER("Start_log_event_v3::do_apply_event");
switch (binlog_version)
{
case 3:
@@ -2331,6 +3381,17 @@ int Start_log_event_v3::exec_event(struct st_relay_log_info* rli)
close_temporary_tables(thd);
cleanup_load_tmpdir();
}
+ else
+ {
+ /*
+ Set all temporary tables thread references to the current thread
+ as they may point to the "old" SQL slave thread in case of its
+ restart.
+ */
+ TABLE *table;
+ for (table= thd->temporary_tables; table; table= table->next)
+ table->in_use= thd;
+ }
break;
/*
@@ -2357,7 +3418,7 @@ int Start_log_event_v3::exec_event(struct st_relay_log_info* rli)
/* this case is impossible */
DBUG_RETURN(1);
}
- DBUG_RETURN(Log_event::exec_event(rli));
+ DBUG_RETURN(0);
}
#endif /* defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT) */
@@ -2365,30 +3426,26 @@ int Start_log_event_v3::exec_event(struct st_relay_log_info* rli)
Format_description_log_event methods
****************************************************************************/
-/*
+/**
Format_description_log_event 1st ctor.
- SYNOPSIS
- Format_description_log_event::Format_description_log_event
- binlog_version the binlog version for which we want to build
- an event. Can be 1 (=MySQL 3.23), 3 (=4.0.x
- x>=2 and 4.1) or 4 (MySQL 5.0). Note that the
- old 4.0 (binlog version 2) is not supported;
- it should not be used for replication with
- 5.0.
-
- DESCRIPTION
Ctor. Can be used to create the event to write to the binary log (when the
server starts or when FLUSH LOGS), or to create artificial events to parse
binlogs from MySQL 3.23 or 4.x.
When in a client, only the 2nd use is possible.
+
+ @param binlog_version the binlog version for which we want to build
+ an event. Can be 1 (=MySQL 3.23), 3 (=4.0.x
+ x>=2 and 4.1) or 4 (MySQL 5.0). Note that the
+ old 4.0 (binlog version 2) is not supported;
+ it should not be used for replication with
+ 5.0.
*/
Format_description_log_event::
Format_description_log_event(uint8 binlog_ver, const char* server_ver)
- :Start_log_event_v3()
+ :Start_log_event_v3(), event_type_permutation(0)
{
- created= when;
binlog_version= binlog_ver;
switch (binlog_ver) {
case 4: /* MySQL 5.0 */
@@ -2399,7 +3456,8 @@ Format_description_log_event(uint8 binlog_ver, const char* server_ver)
number_of_event_types= LOG_EVENT_TYPES;
/* we'll catch my_malloc() error in is_valid() */
post_header_len=(uint8*) my_malloc(number_of_event_types*sizeof(uint8),
- MYF(MY_ZEROFILL));
+ MYF(0));
+
/*
This long list of assignments is not beautiful, but I see no way to
make it nicer, as the right members are #defines, not array members, so
@@ -2407,18 +3465,67 @@ Format_description_log_event(uint8 binlog_ver, const char* server_ver)
*/
if (post_header_len)
{
+ // 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)););
+
+ /* Note: all event types must explicitly fill in their lengths here. */
post_header_len[START_EVENT_V3-1]= START_V3_HEADER_LEN;
post_header_len[QUERY_EVENT-1]= QUERY_HEADER_LEN;
+ post_header_len[STOP_EVENT-1]= STOP_HEADER_LEN;
post_header_len[ROTATE_EVENT-1]= ROTATE_HEADER_LEN;
+ post_header_len[INTVAR_EVENT-1]= INTVAR_HEADER_LEN;
post_header_len[LOAD_EVENT-1]= LOAD_HEADER_LEN;
+ post_header_len[SLAVE_EVENT-1]= SLAVE_HEADER_LEN;
post_header_len[CREATE_FILE_EVENT-1]= CREATE_FILE_HEADER_LEN;
post_header_len[APPEND_BLOCK_EVENT-1]= APPEND_BLOCK_HEADER_LEN;
post_header_len[EXEC_LOAD_EVENT-1]= EXEC_LOAD_HEADER_LEN;
post_header_len[DELETE_FILE_EVENT-1]= DELETE_FILE_HEADER_LEN;
- post_header_len[NEW_LOAD_EVENT-1]= post_header_len[LOAD_EVENT-1];
+ post_header_len[NEW_LOAD_EVENT-1]= NEW_LOAD_HEADER_LEN;
+ post_header_len[RAND_EVENT-1]= RAND_HEADER_LEN;
+ post_header_len[USER_VAR_EVENT-1]= USER_VAR_HEADER_LEN;
post_header_len[FORMAT_DESCRIPTION_EVENT-1]= FORMAT_DESCRIPTION_HEADER_LEN;
- post_header_len[BEGIN_LOAD_QUERY_EVENT-1]= post_header_len[APPEND_BLOCK_EVENT-1];
+ post_header_len[XID_EVENT-1]= XID_HEADER_LEN;
+ post_header_len[BEGIN_LOAD_QUERY_EVENT-1]= BEGIN_LOAD_QUERY_HEADER_LEN;
post_header_len[EXECUTE_LOAD_QUERY_EVENT-1]= EXECUTE_LOAD_QUERY_HEADER_LEN;
+ /*
+ The PRE_GA events are never be written to any binlog, but
+ their lengths are included in Format_description_log_event.
+ Hence, we need to be assign some value here, to avoid reading
+ uninitialized memory when the array is written to disk.
+ */
+ post_header_len[PRE_GA_WRITE_ROWS_EVENT-1] = 0;
+ post_header_len[PRE_GA_UPDATE_ROWS_EVENT-1] = 0;
+ post_header_len[PRE_GA_DELETE_ROWS_EVENT-1] = 0;
+
+ post_header_len[TABLE_MAP_EVENT-1]= TABLE_MAP_HEADER_LEN;
+ post_header_len[WRITE_ROWS_EVENT-1]= ROWS_HEADER_LEN;
+ post_header_len[UPDATE_ROWS_EVENT-1]= ROWS_HEADER_LEN;
+ post_header_len[DELETE_ROWS_EVENT-1]= ROWS_HEADER_LEN;
+ /*
+ We here have the possibility to simulate a master of before we changed
+ the table map id to be stored in 6 bytes: when it was stored in 4
+ bytes (=> post_header_len was 6). This is used to test backward
+ compatibility.
+ This code can be removed after a few months (today is Dec 21st 2005),
+ when we know that the 4-byte masters are not deployed anymore (check
+ with Tomas Ulin first!), and the accompanying test (rpl_row_4_bytes)
+ too.
+ */
+ DBUG_EXECUTE_IF("old_row_based_repl_4_byte_map_id_master",
+ post_header_len[TABLE_MAP_EVENT-1]=
+ post_header_len[WRITE_ROWS_EVENT-1]=
+ post_header_len[UPDATE_ROWS_EVENT-1]=
+ post_header_len[DELETE_ROWS_EVENT-1]= 6;);
+ post_header_len[INCIDENT_EVENT-1]= INCIDENT_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);
+ });
}
break;
@@ -2470,18 +3577,20 @@ Format_description_log_event(uint8 binlog_ver, const char* server_ver)
}
-/*
+/**
The problem with this constructor is that the fixed header may have a
length different from this version, but we don't know this length as we
have not read the Format_description_log_event which says it, yet. This
length is in the post-header of the event, but we don't know where the
post-header starts.
+
So this type of event HAS to:
- either have the header's length at the beginning (in the header, at a
fixed position which will never be changed), not in the post-header. That
would make the header be "shifted" compared to other events.
- or have a header of size LOG_EVENT_MINIMAL_HEADER_LEN (19), in all future
versions, so that we know for sure.
+
I (Guilhem) chose the 2nd solution. Rotate has the same constraint (because
it is sent before Format_description_log_event).
*/
@@ -2492,7 +3601,7 @@ Format_description_log_event(const char* buf,
const
Format_description_log_event*
description_event)
- :Start_log_event_v3(buf, description_event)
+ :Start_log_event_v3(buf, description_event), event_type_permutation(0)
{
DBUG_ENTER("Format_description_log_event::Format_description_log_event(char*,...)");
buf+= LOG_EVENT_MINIMAL_HEADER_LEN;
@@ -2503,10 +3612,114 @@ Format_description_log_event(const char* buf,
DBUG_PRINT("info", ("common_header_len=%d number_of_event_types=%d",
common_header_len, number_of_event_types));
/* If alloc fails, we'll detect it in is_valid() */
- post_header_len= (uint8*) my_memdup((byte*)buf+ST_COMMON_HEADER_LEN_OFFSET+1,
+ post_header_len= (uint8*) my_memdup((uchar*)buf+ST_COMMON_HEADER_LEN_OFFSET+1,
number_of_event_types*
sizeof(*post_header_len), MYF(0));
calc_server_version_split();
+
+ /*
+ In some previous versions, the events were given other event type
+ id numbers than in the present version. When replicating from such
+ a version, we therefore set up an array that maps those id numbers
+ to the id numbers of the present server.
+
+ If post_header_len is null, it means malloc failed, and is_valid
+ will fail, so there is no need to do anything.
+
+ The trees in which events have wrong id's are:
+
+ mysql-5.1-wl1012.old mysql-5.1-wl2325-5.0-drop6p13-alpha
+ mysql-5.1-wl2325-5.0-drop6 mysql-5.1-wl2325-5.0
+ mysql-5.1-wl2325-no-dd
+
+ (this was found by grepping for two lines in sequence where the
+ first matches "FORMAT_DESCRIPTION_EVENT," and the second matches
+ "TABLE_MAP_EVENT," in log_event.h in all trees)
+
+ In these trees, the following server_versions existed since
+ TABLE_MAP_EVENT was introduced:
+
+ 5.1.1-a_drop5p3 5.1.1-a_drop5p4 5.1.1-alpha
+ 5.1.2-a_drop5p10 5.1.2-a_drop5p11 5.1.2-a_drop5p12
+ 5.1.2-a_drop5p13 5.1.2-a_drop5p14 5.1.2-a_drop5p15
+ 5.1.2-a_drop5p16 5.1.2-a_drop5p16b 5.1.2-a_drop5p16c
+ 5.1.2-a_drop5p17 5.1.2-a_drop5p4 5.1.2-a_drop5p5
+ 5.1.2-a_drop5p6 5.1.2-a_drop5p7 5.1.2-a_drop5p8
+ 5.1.2-a_drop5p9 5.1.3-a_drop5p17 5.1.3-a_drop5p17b
+ 5.1.3-a_drop5p17c 5.1.4-a_drop5p18 5.1.4-a_drop5p19
+ 5.1.4-a_drop5p20 5.1.4-a_drop6p0 5.1.4-a_drop6p1
+ 5.1.4-a_drop6p2 5.1.5-a_drop5p20 5.2.0-a_drop6p3
+ 5.2.0-a_drop6p4 5.2.0-a_drop6p5 5.2.0-a_drop6p6
+ 5.2.1-a_drop6p10 5.2.1-a_drop6p11 5.2.1-a_drop6p12
+ 5.2.1-a_drop6p6 5.2.1-a_drop6p7 5.2.1-a_drop6p8
+ 5.2.2-a_drop6p13 5.2.2-a_drop6p13-alpha 5.2.2-a_drop6p13b
+ 5.2.2-a_drop6p13c
+
+ (this was found by grepping for "mysql," in all historical
+ versions of configure.in in the trees listed above).
+
+ There are 5.1.1-alpha versions that use the new event id's, so we
+ do not test that version string. So replication from 5.1.1-alpha
+ with the other event id's to a new version does not work.
+ Moreover, we can safely ignore the part after drop[56]. This
+ allows us to simplify the big list above to the following regexes:
+
+ 5\.1\.[1-5]-a_drop5.*
+ 5\.1\.4-a_drop6.*
+ 5\.2\.[0-2]-a_drop6.*
+
+ This is what we test for in the 'if' below.
+ */
+ if (post_header_len &&
+ server_version[0] == '5' && server_version[1] == '.' &&
+ server_version[3] == '.' &&
+ strncmp(server_version + 5, "-a_drop", 7) == 0 &&
+ ((server_version[2] == '1' &&
+ server_version[4] >= '1' && server_version[4] <= '5' &&
+ server_version[12] == '5') ||
+ (server_version[2] == '1' &&
+ server_version[4] == '4' &&
+ server_version[12] == '6') ||
+ (server_version[2] == '2' &&
+ server_version[4] >= '0' && server_version[4] <= '2' &&
+ server_version[12] == '6')))
+ {
+ if (number_of_event_types != 22)
+ {
+ DBUG_PRINT("info", (" number_of_event_types=%d",
+ number_of_event_types));
+ /* this makes is_valid() return false. */
+ my_free(post_header_len, MYF(MY_ALLOW_ZERO_PTR));
+ post_header_len= NULL;
+ DBUG_VOID_RETURN;
+ }
+ static const uint8 perm[23]=
+ {
+ UNKNOWN_EVENT, START_EVENT_V3, QUERY_EVENT, STOP_EVENT, ROTATE_EVENT,
+ INTVAR_EVENT, LOAD_EVENT, SLAVE_EVENT, CREATE_FILE_EVENT,
+ APPEND_BLOCK_EVENT, EXEC_LOAD_EVENT, DELETE_FILE_EVENT,
+ NEW_LOAD_EVENT,
+ RAND_EVENT, USER_VAR_EVENT,
+ FORMAT_DESCRIPTION_EVENT,
+ TABLE_MAP_EVENT,
+ PRE_GA_WRITE_ROWS_EVENT,
+ PRE_GA_UPDATE_ROWS_EVENT,
+ PRE_GA_DELETE_ROWS_EVENT,
+ XID_EVENT,
+ BEGIN_LOAD_QUERY_EVENT,
+ EXECUTE_LOAD_QUERY_EVENT,
+ };
+ event_type_permutation= perm;
+ /*
+ Since we use (permuted) event id's to index the post_header_len
+ array, we need to permute the post_header_len array too.
+ */
+ uint8 post_header_len_temp[23];
+ for (int i= 1; i < 23; i++)
+ post_header_len_temp[perm[i] - 1]= post_header_len[i - 1];
+ for (int i= 0; i < 22; i++)
+ post_header_len[i] = post_header_len_temp[i];
+ }
DBUG_VOID_RETURN;
}
@@ -2517,94 +3730,105 @@ bool Format_description_log_event::write(IO_CACHE* file)
We don't call Start_log_event_v3::write() because this would make 2
my_b_safe_write().
*/
- byte buff[FORMAT_DESCRIPTION_HEADER_LEN];
+ uchar buff[FORMAT_DESCRIPTION_HEADER_LEN];
int2store(buff + ST_BINLOG_VER_OFFSET,binlog_version);
memcpy((char*) buff + ST_SERVER_VER_OFFSET,server_version,ST_SERVER_VER_LEN);
+ if (!dont_set_created)
+ created= when= get_time();
int4store(buff + ST_CREATED_OFFSET,created);
buff[ST_COMMON_HEADER_LEN_OFFSET]= LOG_EVENT_HEADER_LEN;
- memcpy((char*) buff+ST_COMMON_HEADER_LEN_OFFSET+1, (byte*) post_header_len,
+ memcpy((char*) buff+ST_COMMON_HEADER_LEN_OFFSET+1, (uchar*) post_header_len,
LOG_EVENT_TYPES);
return (write_header(file, sizeof(buff)) ||
my_b_safe_write(file, buff, sizeof(buff)));
}
#endif
-/*
- SYNOPSIS
- Format_description_log_event::exec_event()
-
- IMPLEMENTATION
- Save the information which describes the binlog's format, to be able to
- read all coming events.
- Call Start_log_event_v3::exec_event().
-*/
-
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
-int Format_description_log_event::exec_event(struct st_relay_log_info* rli)
+int Format_description_log_event::do_apply_event(Relay_log_info const *rli)
{
- DBUG_ENTER("Format_description_log_event::exec_event");
-
- /* save the information describing this binlog */
- delete rli->relay_log.description_event_for_exec;
- rli->relay_log.description_event_for_exec= this;
+ 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
while writing the transaction to the binary log, i.e. while
- flushing the binlog cache to the binlog. As the write was started,
- the transaction had been committed on the master, so we lack of
- information to replay this transaction on the slave; all we can do
- is stop with error.
+ flushing the binlog cache to the binlog. XA guarantees that master has
+ rolled back. So we roll back.
Note: this event could be sent by the master to inform us of the
format of its binlog; in other words maybe it is not at its
original place when it comes to us; we'll know this by checking
log_pos ("artificial" events have log_pos == 0).
*/
- if (!artificial_event && created && thd->transaction.all.nht)
+ if (!is_artificial_event() && created && thd->transaction.all.ha_list)
{
- slave_print_error(rli, 0, "Rolling back unfinished transaction (no "
- "COMMIT or ROLLBACK) from relay log. A probable cause "
- "is that the master died while writing the transaction "
- "to its binary log.");
- end_trans(thd, ROLLBACK);
+ /* This is not an error (XA is safe), just an information */
+ rli->report(INFORMATION_LEVEL, 0,
+ "Rolling back unfinished transaction (no COMMIT "
+ "or ROLLBACK in relay log). A probable cause is that "
+ "the master died while writing the transaction to "
+ "its binary log, thus rolled back too.");
+ const_cast<Relay_log_info*>(rli)->cleanup_context(thd, 1);
}
#endif
/*
- If this event comes from ourselves, there is no cleaning task to perform,
- we don't call Start_log_event_v3::exec_event() (this was just to update the
- log's description event).
+ If this event comes from ourselves, there is no cleaning task to
+ perform, we don't call Start_log_event_v3::do_apply_event()
+ (this was just to update the log's description event).
*/
- if (server_id == (uint32) ::server_id)
+ if (server_id != (uint32) ::server_id)
{
/*
- Do not modify rli->group_master_log_pos, as this event did not exist on
- the master. That is, just update the *relay log* coordinates; this is
- done by passing log_pos=0 to inc_group_relay_log_pos, like we do in
- Stop_log_event::exec_event().
- If in a transaction, don't touch group_* coordinates.
+ If the event was not requested by the slave i.e. the master sent
+ it while the slave asked for a position >4, the event will make
+ rli->group_master_log_pos advance. Say that the slave asked for
+ position 1000, and the Format_desc event's end is 96. Then in
+ the beginning of replication rli->group_master_log_pos will be
+ 0, then 96, then jump to first really asked event (which is
+ >96). So this is ok.
*/
- if (thd->options & OPTION_BEGIN)
- rli->inc_event_relay_log_pos();
- else
- {
- rli->inc_group_relay_log_pos(0);
- flush_relay_log_info(rli);
- }
- DBUG_RETURN(0);
+ DBUG_RETURN(Start_log_event_v3::do_apply_event(rli));
}
+ DBUG_RETURN(0);
+}
- /*
- If the event was not requested by the slave i.e. the master sent it while
- the slave asked for a position >4, the event will make
- rli->group_master_log_pos advance. Say that the slave asked for position
- 1000, and the Format_desc event's end is 96. Then in the beginning of
- replication rli->group_master_log_pos will be 0, then 96, then jump to
- first really asked event (which is >96). So this is ok.
- */
- DBUG_RETURN(Start_log_event_v3::exec_event(rli));
+int Format_description_log_event::do_update_pos(Relay_log_info *rli)
+{
+ /* save the information describing this binlog */
+ delete rli->relay_log.description_event_for_exec;
+ rli->relay_log.description_event_for_exec= this;
+
+ if (server_id == (uint32) ::server_id)
+ {
+ /*
+ We only increase the relay log position if we are skipping
+ events and do not touch any group_* variables, nor flush the
+ relay log info. If there is a crash, we will have to re-skip
+ the events again, but that is a minor issue.
+
+ If we do not skip stepping the group log position (and the
+ server id was changed when restarting the server), it might well
+ be that we start executing at a position that is invalid, e.g.,
+ at a Rows_log_event or a Query_log_event preceeded by a
+ Intvar_log_event instead of starting at a Table_map_log_event or
+ the Intvar_log_event respectively.
+ */
+ rli->inc_event_relay_log_pos();
+ return 0;
+ }
+ else
+ {
+ return Log_event::do_update_pos(rli);
+ }
}
+
+Log_event::enum_skip_reason
+Format_description_log_event::do_shall_skip(Relay_log_info *rli)
+{
+ return Log_event::EVENT_SKIP_NOT;
+}
+
#endif
@@ -2671,7 +3895,7 @@ uint Load_log_event::get_query_buffer_length()
21 + sql_ex.field_term_len*4 + 2 + // " FIELDS TERMINATED BY 'str'"
23 + sql_ex.enclosed_len*4 + 2 + // " OPTIONALLY ENCLOSED BY 'str'"
12 + sql_ex.escaped_len*4 + 2 + // " ESCAPED BY 'str'"
- 21 + sql_ex.line_term_len*4 + 2 + // " FIELDS TERMINATED BY 'str'"
+ 21 + sql_ex.line_term_len*4 + 2 + // " LINES TERMINATED BY 'str'"
19 + sql_ex.line_start_len*4 + 2 + // " LINES STARTING BY 'str'"
15 + 22 + // " IGNORE xxx LINES"
3 + (num_fields-1)*2 + field_block_len; // " (field1, field2, ...)"
@@ -2768,10 +3992,10 @@ void Load_log_event::pack_info(Protocol *protocol)
{
char *buf, *end;
- if (!(buf= my_malloc(get_query_buffer_length(), MYF(MY_WME))))
+ if (!(buf= (char*) my_malloc(get_query_buffer_length(), MYF(MY_WME))))
return;
print_query(TRUE, buf, &end, 0, 0);
- protocol->store(buf, (uint) (end - buf), &my_charset_bin);
+ protocol->store(buf, end-buf, &my_charset_bin);
my_free(buf, MYF(0));
}
#endif /* defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT) */
@@ -2792,7 +4016,7 @@ bool Load_log_event::write_data_header(IO_CACHE* file)
buf[L_TBL_LEN_OFFSET] = (char)table_name_len;
buf[L_DB_LEN_OFFSET] = (char)db_len;
int4store(buf + L_NUM_FIELDS_OFFSET, num_fields);
- return my_b_safe_write(file, (byte*)buf, LOAD_HEADER_LEN) != 0;
+ return my_b_safe_write(file, (uchar*)buf, LOAD_HEADER_LEN) != 0;
}
@@ -2806,13 +4030,13 @@ bool Load_log_event::write_data_body(IO_CACHE* file)
return 1;
if (num_fields && fields && field_lens)
{
- if (my_b_safe_write(file, (byte*)field_lens, num_fields) ||
- my_b_safe_write(file, (byte*)fields, field_block_len))
+ if (my_b_safe_write(file, (uchar*)field_lens, num_fields) ||
+ my_b_safe_write(file, (uchar*)fields, field_block_len))
return 1;
}
- return (my_b_safe_write(file, (byte*)table_name, table_name_len + 1) ||
- my_b_safe_write(file, (byte*)db, db_len + 1) ||
- my_b_safe_write(file, (byte*)fname, fname_len));
+ return (my_b_safe_write(file, (uchar*)table_name, table_name_len + 1) ||
+ my_b_safe_write(file, (uchar*)db, db_len + 1) ||
+ my_b_safe_write(file, (uchar*)fname, fname_len));
}
@@ -2826,8 +4050,7 @@ Load_log_event::Load_log_event(THD *thd_arg, sql_exchange *ex,
enum enum_duplicates handle_dup,
bool ignore, bool using_trans)
:Log_event(thd_arg,
- (thd_arg->tmp_table_used || thd_arg->thread_specific_used) ?
- LOG_EVENT_THREAD_SPECIFIC_F : 0,
+ thd_arg->thread_specific_used ? LOG_EVENT_THREAD_SPECIFIC_F : 0,
using_trans),
thread_id(thd_arg->thread_id),
slave_proxy_id(thd_arg->variables.pseudo_thread_id),
@@ -2906,14 +4129,11 @@ Load_log_event::Load_log_event(THD *thd_arg, sql_exchange *ex,
#endif /* !MYSQL_CLIENT */
-/*
- Load_log_event::Load_log_event()
-
- NOTE
+/**
+ @note
The caller must do buf[event_len] = 0 before he starts using the
constructed event.
*/
-
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),
@@ -2996,15 +4216,17 @@ void Load_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info)
}
-void Load_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info,
+void Load_log_event::print(FILE* file_arg, PRINT_EVENT_INFO* print_event_info,
bool commented)
{
+ Write_on_release_cache cache(&print_event_info->head_cache, file_arg);
+
DBUG_ENTER("Load_log_event::print");
if (!print_event_info->short_form)
{
- print_header(file, print_event_info);
- fprintf(file, "\tQuery\tthread_id=%ld\texec_time=%ld\n",
- thread_id, exec_time);
+ print_header(&cache, print_event_info, FALSE);
+ my_b_printf(&cache, "\tQuery\tthread_id=%ld\texec_time=%ld\n",
+ thread_id, exec_time);
}
bool different_db= 1;
@@ -3022,81 +4244,82 @@ void Load_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info,
}
if (db && db[0] && different_db)
- fprintf(file, "%suse %s%s\n",
+ my_b_printf(&cache, "%suse %s%s\n",
commented ? "# " : "",
db, print_event_info->delimiter);
if (flags & LOG_EVENT_THREAD_SPECIFIC_F)
- fprintf(file,"%sSET @@session.pseudo_thread_id=%lu%s\n",
+ my_b_printf(&cache,"%sSET @@session.pseudo_thread_id=%lu%s\n",
commented ? "# " : "", (ulong)thread_id,
print_event_info->delimiter);
- fprintf(file, "%sLOAD DATA ",
- commented ? "# " : "");
+ my_b_printf(&cache, "%sLOAD DATA ",
+ commented ? "# " : "");
if (check_fname_outside_temp_buf())
- fprintf(file, "LOCAL ");
- fprintf(file, "INFILE '%-*s' ", fname_len, fname);
+ my_b_printf(&cache, "LOCAL ");
+ my_b_printf(&cache, "INFILE '%-*s' ", fname_len, fname);
if (sql_ex.opt_flags & REPLACE_FLAG)
- fprintf(file," REPLACE ");
+ my_b_printf(&cache," REPLACE ");
else if (sql_ex.opt_flags & IGNORE_FLAG)
- fprintf(file," IGNORE ");
+ my_b_printf(&cache," IGNORE ");
- fprintf(file, "INTO TABLE `%s`", table_name);
- fprintf(file, " FIELDS TERMINATED BY ");
- pretty_print_str(file, sql_ex.field_term, sql_ex.field_term_len);
+ 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);
if (sql_ex.opt_flags & OPT_ENCLOSED_FLAG)
- fprintf(file," OPTIONALLY ");
- fprintf(file, " ENCLOSED BY ");
- pretty_print_str(file, sql_ex.enclosed, sql_ex.enclosed_len);
+ my_b_printf(&cache," OPTIONALLY ");
+ my_b_printf(&cache, " ENCLOSED BY ");
+ pretty_print_str(&cache, sql_ex.enclosed, sql_ex.enclosed_len);
- fprintf(file, " ESCAPED BY ");
- pretty_print_str(file, sql_ex.escaped, sql_ex.escaped_len);
+ my_b_printf(&cache, " ESCAPED BY ");
+ pretty_print_str(&cache, sql_ex.escaped, sql_ex.escaped_len);
- fprintf(file," LINES TERMINATED BY ");
- pretty_print_str(file, sql_ex.line_term, sql_ex.line_term_len);
+ my_b_printf(&cache," LINES TERMINATED BY ");
+ pretty_print_str(&cache, sql_ex.line_term, sql_ex.line_term_len);
if (sql_ex.line_start)
{
- fprintf(file," STARTING BY ");
- pretty_print_str(file, sql_ex.line_start, sql_ex.line_start_len);
+ my_b_printf(&cache," STARTING BY ");
+ pretty_print_str(&cache, sql_ex.line_start, sql_ex.line_start_len);
}
if ((long) skip_lines > 0)
- fprintf(file, " IGNORE %ld LINES", (long) skip_lines);
+ my_b_printf(&cache, " IGNORE %ld LINES", (long) skip_lines);
if (num_fields)
{
uint i;
const char* field = fields;
- fprintf(file, " (");
+ my_b_printf(&cache, " (");
for (i = 0; i < num_fields; i++)
{
if (i)
- fputc(',', file);
- fprintf(file, field);
+ my_b_printf(&cache, ",");
+ my_b_printf(&cache, field);
field += field_lens[i] + 1;
}
- fputc(')', file);
+ my_b_printf(&cache, ")");
}
- fprintf(file, "%s\n", print_event_info->delimiter);
+ my_b_printf(&cache, "%s\n", print_event_info->delimiter);
DBUG_VOID_RETURN;
}
#endif /* MYSQL_CLIENT */
+#ifndef MYSQL_CLIENT
-/*
+/**
Load_log_event::set_fields()
- Note that this function can not use the member variable
- for the database, since LOAD DATA INFILE on the slave
- can be for a different database than the current one.
- This is the reason for the affected_db argument to this method.
+ @note
+ This function can not use the member variable
+ for the database, since LOAD DATA INFILE on the slave
+ can be for a different database than the current one.
+ This is the reason for the affected_db argument to this method.
*/
-#ifndef MYSQL_CLIENT
void Load_log_event::set_fields(const char* affected_db,
List<Item> &field_list,
Name_resolution_context *context)
@@ -3114,41 +4337,49 @@ void Load_log_event::set_fields(const char* affected_db,
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
-/*
- Does the data loading job when executing a LOAD DATA on the slave
-
- SYNOPSIS
- Load_log_event::exec_event
- net
- rli
- use_rli_only_for_errors - if set to 1, rli is provided to
- Load_log_event::exec_event only for this
- function to have RPL_LOG_NAME and
- rli->last_slave_error, both being used by
- error reports. rli's position advancing
- is skipped (done by the caller which is
- Execute_load_log_event::exec_event).
- - if set to 0, rli is provided for full use,
- i.e. for error reports and position
- advancing.
-
- DESCRIPTION
- Does the data loading job when executing a LOAD DATA on the slave
-
- RETURN VALUE
- 0 Success
- 1 Failure
+/**
+ Does the data loading job when executing a LOAD DATA on the slave.
+
+ @param net
+ @param rli
+ @param use_rli_only_for_errors If set to 1, rli is provided to
+ Load_log_event::exec_event only for this
+ function to have RPL_LOG_NAME and
+ rli->last_slave_error, both being used by
+ error reports. rli's position advancing
+ is skipped (done by the caller which is
+ Execute_load_log_event::exec_event).
+ If set to 0, rli is provided for full use,
+ i.e. for error reports and position
+ advancing.
+
+ @todo
+ fix this; this can be done by testing rules in
+ Create_file_log_event::exec_event() and then discarding Append_block and
+ al.
+ @todo
+ this is a bug - this needs to be moved to the I/O thread
+
+ @retval
+ 0 Success
+ @retval
+ 1 Failure
*/
-int Load_log_event::exec_event(NET* net, struct st_relay_log_info* rli,
- bool use_rli_only_for_errors)
+int Load_log_event::do_apply_event(NET* net, Relay_log_info const *rli,
+ bool use_rli_only_for_errors)
{
- const char *new_db= rewrite_db(db);
- thd->set_db(new_db, (uint) strlen(new_db));
+ LEX_STRING new_db;
+ 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->query_length= 0; // Should not be needed
- thd->query_error= 0;
- clear_all_errors(thd, rli);
+ thd->is_slave_error= 0;
+ clear_all_errors(thd, const_cast<Relay_log_info*>(rli));
+
+ /* see Query_log_event::do_apply_event() and BUG#13360 */
+ DBUG_ASSERT(!rli->m_table_map.count());
/*
Usually lex_start() is called by mysql_parse(), but we need it here
as the present method does not call mysql_parse().
@@ -3158,22 +4389,26 @@ int Load_log_event::exec_event(NET* net, struct st_relay_log_info* rli,
if (!use_rli_only_for_errors)
{
- /* Saved for InnoDB, see comment in Query_log_event::exec_event() */
- rli->future_group_master_log_pos= log_pos;
+ /*
+ Saved for InnoDB, see comment in
+ Query_log_event::do_apply_event()
+ */
+ const_cast<Relay_log_info*>(rli)->future_group_master_log_pos= log_pos;
DBUG_PRINT("info", ("log_pos: %lu", (ulong) log_pos));
}
/*
- We test replicate_*_db rules. Note that we have already prepared the file
- to load, even if we are going to ignore and delete it now. So it is
- possible that we did a lot of disk writes for nothing. In other words, a
- big LOAD DATA INFILE on the master will still consume a lot of space on
- the slave (space in the relay log + space of temp files: twice the space
- of the file to load...) even if it will finally be ignored.
- TODO: fix this; this can be done by testing rules in
- Create_file_log_event::exec_event() and then discarding Append_block and
- al. Another way is do the filtering in the I/O thread (more efficient: no
- disk writes at all).
+ We test replicate_*_db rules. Note that we have already prepared
+ the file to load, even if we are going to ignore and delete it
+ now. So it is possible that we did a lot of disk writes for
+ nothing. In other words, a big LOAD DATA INFILE on the master will
+ still consume a lot of space on the slave (space in the relay log
+ + space of temp files: twice the space of the file to load...)
+ even if it will finally be ignored. TODO: fix this; this can be
+ done by testing rules in Create_file_log_event::do_apply_event()
+ and then discarding Append_block and al. Another way is do the
+ filtering in the I/O thread (more efficient: no disk writes at
+ all).
Note: We do not need to execute reset_one_shot_variables() if this
@@ -3182,10 +4417,10 @@ int Load_log_event::exec_event(NET* net, struct st_relay_log_info* rli,
its companion query. If the SET is ignored because of
db_ok(), the companion query will also be ignored, and if
the companion query is ignored in the db_ok() test of
- ::exec_event(), then the companion SET also have so we
- don't need to reset_one_shot_variables().
+ ::do_apply_event(), then the companion SET also have so
+ we don't need to reset_one_shot_variables().
*/
- if (db_ok(thd->db, replicate_do_db, replicate_ignore_db))
+ if (rpl_filter->db_ok(thd->db))
{
thd->set_time((time_t)when);
VOID(pthread_mutex_lock(&LOCK_thread_count));
@@ -3207,7 +4442,7 @@ int Load_log_event::exec_event(NET* net, struct st_relay_log_info* rli,
tables.updating= 1;
// the table will be opened in mysql_load
- if (table_rules_on && !tables_ok(thd, &tables))
+ if (rpl_filter->is_on() && !rpl_filter->tables_ok(thd->db, &tables))
{
// TODO: this is a bug - this needs to be moved to the I/O thread
if (net)
@@ -3237,7 +4472,7 @@ int Load_log_event::exec_event(NET* net, struct st_relay_log_info* rli,
print_query(FALSE, load_data_query, &end, (char **)&thd->lex->fname_start,
(char **)&thd->lex->fname_end);
*end= 0;
- thd->query_length= (uint) (end - load_data_query);
+ thd->query_length= end - load_data_query;
thd->query= load_data_query;
if (sql_ex.opt_flags & REPLACE_FLAG)
@@ -3314,7 +4549,7 @@ int Load_log_event::exec_event(NET* net, struct st_relay_log_info* rli,
List<Item> tmp_list;
if (mysql_load(thd, &ex, &tables, field_list, tmp_list, tmp_list,
handle_dup, ignore, net != 0))
- thd->query_error= 1;
+ thd->is_slave_error= 1;
if (thd->cuted_fields)
{
/* log_pos is the position of the LOAD event in the master log */
@@ -3351,35 +4586,48 @@ error:
thd->query_length= 0;
VOID(pthread_mutex_unlock(&LOCK_thread_count));
close_thread_tables(thd);
- if (thd->query_error)
+
+ DBUG_EXECUTE_IF("LOAD_DATA_INFILE_has_fatal_error",
+ thd->is_slave_error= 0; thd->is_fatal_error= 1;);
+
+ if (thd->is_slave_error)
{
/* this err/sql_errno code is copy-paste from net_send_error() */
const char *err;
int sql_errno;
- if ((err=thd->net.last_error)[0])
- sql_errno=thd->net.last_errno;
+ if (thd->is_error())
+ {
+ err= thd->main_da.message();
+ sql_errno= thd->main_da.sql_errno();
+ }
else
{
sql_errno=ER_UNKNOWN_ERROR;
err=ER(sql_errno);
}
- slave_print_error(rli,sql_errno,"\
+ rli->report(ERROR_LEVEL, sql_errno,"\
Error '%s' running LOAD DATA INFILE on table '%s'. Default database: '%s'",
- err, (char*)table_name, print_slave_db_safe(remember_db));
+ err, (char*)table_name, print_slave_db_safe(remember_db));
free_root(thd->mem_root,MYF(MY_KEEP_PREALLOC));
return 1;
}
free_root(thd->mem_root,MYF(MY_KEEP_PREALLOC));
-
+
if (thd->is_fatal_error)
{
- slave_print_error(rli,ER_UNKNOWN_ERROR, "\
-Fatal error running LOAD DATA INFILE on table '%s'. Default database: '%s'",
- (char*)table_name, print_slave_db_safe(remember_db));
+ char buf[256];
+ my_snprintf(buf, sizeof(buf),
+ "Running LOAD DATA INFILE on table '%-.64s'."
+ " Default database: '%-.64s'",
+ (char*)table_name,
+ print_slave_db_safe(remember_db));
+
+ rli->report(ERROR_LEVEL, ER_SLAVE_FATAL_ERROR,
+ ER(ER_SLAVE_FATAL_ERROR), buf);
return 1;
}
- return ( use_rli_only_for_errors ? 0 : Log_event::exec_event(rli) );
+ return ( use_rli_only_for_errors ? 0 : Log_event::do_apply_event(rli) );
}
#endif
@@ -3414,17 +4662,16 @@ void Rotate_log_event::pack_info(Protocol *protocol)
void Rotate_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info)
{
char buf[22];
+ Write_on_release_cache cache(&print_event_info->head_cache, file,
+ Write_on_release_cache::FLUSH_F);
if (print_event_info->short_form)
return;
- print_header(file, print_event_info);
- fprintf(file, "\tRotate to ");
+ print_header(&cache, print_event_info, FALSE);
+ my_b_printf(&cache, "\tRotate to ");
if (new_log_ident)
- my_fwrite(file, (byte*) new_log_ident, (uint)ident_len,
- MYF(MY_NABP | MY_WME));
- fprintf(file, " pos: %s", llstr(pos, buf));
- fputc('\n', file);
- fflush(file);
+ my_b_write(&cache, (uchar*) new_log_ident, (uint)ident_len);
+ my_b_printf(&cache, " pos: %s\n", llstr(pos, buf));
}
#endif /* MYSQL_CLIENT */
@@ -3436,8 +4683,7 @@ void Rotate_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info)
#ifndef MYSQL_CLIENT
-Rotate_log_event::Rotate_log_event(THD* thd_arg,
- const char* new_log_ident_arg,
+Rotate_log_event::Rotate_log_event(const char* new_log_ident_arg,
uint ident_len_arg, ulonglong pos_arg,
uint flags_arg)
:Log_event(), new_log_ident(new_log_ident_arg),
@@ -3446,13 +4692,14 @@ Rotate_log_event::Rotate_log_event(THD* thd_arg,
{
#ifndef DBUG_OFF
char buff[22];
- DBUG_ENTER("Rotate_log_event::Rotate_log_event(THD*,...)");
+ DBUG_ENTER("Rotate_log_event::Rotate_log_event(...,flags)");
DBUG_PRINT("enter",("new_log_ident: %s pos: %s flags: %lu", new_log_ident_arg,
llstr(pos_arg, buff), (ulong) flags));
#endif
if (flags & DUP_NAME)
- new_log_ident= my_strdup_with_length(new_log_ident_arg,
- ident_len, MYF(MY_WME));
+ new_log_ident= my_strndup(new_log_ident_arg, ident_len, MYF(MY_WME));
+ if (flags & RELAY_LOG)
+ set_relay_log_event();
DBUG_VOID_RETURN;
}
#endif
@@ -3475,9 +4722,8 @@ Rotate_log_event::Rotate_log_event(const char* buf, uint event_len,
(header_size+post_header_len));
ident_offset = post_header_len;
set_if_smaller(ident_len,FN_REFLEN-1);
- new_log_ident= my_strdup_with_length(buf + ident_offset,
- (uint) ident_len,
- MYF(MY_WME));
+ new_log_ident= my_strndup(buf + ident_offset, (uint) ident_len, MYF(MY_WME));
+ DBUG_PRINT("debug", ("new_log_ident: '%s'", new_log_ident));
DBUG_VOID_RETURN;
}
@@ -3492,61 +4738,74 @@ bool Rotate_log_event::write(IO_CACHE* file)
char buf[ROTATE_HEADER_LEN];
int8store(buf + R_POS_OFFSET, pos);
return (write_header(file, ROTATE_HEADER_LEN + ident_len) ||
- my_b_safe_write(file, (byte*)buf, ROTATE_HEADER_LEN) ||
- my_b_safe_write(file, (byte*)new_log_ident, (uint) ident_len));
+ my_b_safe_write(file, (uchar*)buf, ROTATE_HEADER_LEN) ||
+ my_b_safe_write(file, (uchar*)new_log_ident, (uint) ident_len));
}
#endif
-/*
- Rotate_log_event::exec_event()
- Got a rotate log event from the master
+#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
+
+/*
+ Got a rotate log event from the master.
- IMPLEMENTATION
- This is mainly used so that we can later figure out the logname and
- position for the master.
+ This is mainly used so that we can later figure out the logname and
+ position for the master.
- We can't rotate the slave's BINlog as this will cause infinitive rotations
- in a A -> B -> A setup.
- The NOTES below is a wrong comment which will disappear when 4.1 is merged.
+ We can't rotate the slave's BINlog as this will cause infinitive rotations
+ in a A -> B -> A setup.
+ The NOTES below is a wrong comment which will disappear when 4.1 is merged.
- RETURN VALUES
+ @retval
0 ok
*/
-
-#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
-int Rotate_log_event::exec_event(struct st_relay_log_info* rli)
+int Rotate_log_event::do_update_pos(Relay_log_info *rli)
{
- DBUG_ENTER("Rotate_log_event::exec_event");
+ DBUG_ENTER("Rotate_log_event::do_update_pos");
+#ifndef DBUG_OFF
+ char buf[32];
+#endif
+
+ DBUG_PRINT("info", ("server_id=%lu; ::server_id=%lu",
+ (ulong) this->server_id, (ulong) ::server_id));
+ DBUG_PRINT("info", ("new_log_ident: %s", this->new_log_ident));
+ DBUG_PRINT("info", ("pos: %s", llstr(this->pos, buf)));
- pthread_mutex_lock(&rli->data_lock);
- rli->event_relay_log_pos= my_b_tell(rli->cur_log);
/*
- If we are in a transaction: the only normal case is when the I/O thread was
- copying a big transaction, then it was stopped and restarted: we have this
- in the relay log:
+ If we are in a transaction or in a group: the only normal case is
+ when the I/O thread was copying a big transaction, then it was
+ stopped and restarted: we have this in the relay log:
+
BEGIN
...
ROTATE (a fake one)
...
COMMIT or ROLLBACK
- In that case, we don't want to touch the coordinates which correspond to
- the beginning of the transaction.
- Starting from 5.0.0, there also are some rotates from the slave itself, in
- the relay log.
+
+ In that case, we don't want to touch the coordinates which
+ correspond to the beginning of the transaction. Starting from
+ 5.0.0, there also are some rotates from the slave itself, in the
+ relay log, which shall not change the group positions.
*/
- if (!(thd->options & OPTION_BEGIN))
+ if ((server_id != ::server_id || rli->replicate_same_server_id) &&
+ !is_relay_log_event() &&
+ !rli->is_in_group())
{
+ pthread_mutex_lock(&rli->data_lock);
+ DBUG_PRINT("info", ("old group_master_log_name: '%s' "
+ "old group_master_log_pos: %lu",
+ rli->group_master_log_name,
+ (ulong) rli->group_master_log_pos));
memcpy(rli->group_master_log_name, new_log_ident, ident_len+1);
rli->notify_group_master_log_name_update();
- rli->group_master_log_pos= pos;
- strmake(rli->group_relay_log_name, rli->event_relay_log_name,
- sizeof(rli->group_relay_log_name) - 1);
- rli->group_relay_log_pos= rli->event_relay_log_pos;
- DBUG_PRINT("info", ("group_master_log_name: '%s' group_master_log_pos:\
-%lu",
+ rli->inc_group_relay_log_pos(pos, TRUE /* skip_lock */);
+ DBUG_PRINT("info", ("new group_master_log_name: '%s' "
+ "new group_master_log_pos: %lu",
rli->group_master_log_name,
(ulong) rli->group_master_log_pos));
+ pthread_mutex_unlock(&rli->data_lock);
+ flush_relay_log_info(rli);
+
/*
Reset thd->options and sql_mode etc, because this could be the signal of
a master's downgrade from 5.0 to 4.0.
@@ -3560,11 +4819,31 @@ int Rotate_log_event::exec_event(struct st_relay_log_info* rli)
thd->variables.auto_increment_increment=
thd->variables.auto_increment_offset= 1;
}
- pthread_mutex_unlock(&rli->data_lock);
- pthread_cond_broadcast(&rli->data_cond);
- flush_relay_log_info(rli);
+ else
+ rli->inc_event_relay_log_pos();
+
+
DBUG_RETURN(0);
}
+
+
+Log_event::enum_skip_reason
+Rotate_log_event::do_shall_skip(Relay_log_info *rli)
+{
+ enum_skip_reason reason= Log_event::do_shall_skip(rli);
+
+ switch (reason) {
+ case Log_event::EVENT_SKIP_NOT:
+ case Log_event::EVENT_SKIP_COUNT:
+ return Log_event::EVENT_SKIP_NOT;
+
+ case Log_event::EVENT_SKIP_IGNORE:
+ return Log_event::EVENT_SKIP_IGNORE;
+ }
+ DBUG_ASSERT(0);
+ return Log_event::EVENT_SKIP_NOT; // To keep compiler happy
+}
+
#endif
@@ -3596,7 +4875,9 @@ Intvar_log_event::Intvar_log_event(const char* buf,
const Format_description_log_event* description_event)
:Log_event(buf, description_event)
{
- buf+= description_event->common_header_len;
+ /* The Post-Header is empty. The Varible Data part begins immediately. */
+ buf+= description_event->common_header_len +
+ description_event->post_header_len[INTVAR_EVENT-1];
type= buf[I_TYPE_OFFSET];
val= uint8korr(buf+I_VAL_OFFSET);
}
@@ -3623,8 +4904,8 @@ const char* Intvar_log_event::get_var_type_name()
#ifndef MYSQL_CLIENT
bool Intvar_log_event::write(IO_CACHE* file)
{
- byte buf[9];
- buf[I_TYPE_OFFSET]= (byte) type;
+ uchar buf[9];
+ buf[I_TYPE_OFFSET]= (uchar) type;
int8store(buf + I_VAL_OFFSET, val);
return (write_header(file, sizeof(buf)) ||
my_b_safe_write(file, buf, sizeof(buf)));
@@ -3642,14 +4923,16 @@ void Intvar_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info)
char llbuff[22];
const char *msg;
LINT_INIT(msg);
+ Write_on_release_cache cache(&print_event_info->head_cache, file,
+ Write_on_release_cache::FLUSH_F);
if (!print_event_info->short_form)
{
- print_header(file, print_event_info);
- fprintf(file, "\tIntvar\n");
+ print_header(&cache, print_event_info, FALSE);
+ my_b_printf(&cache, "\tIntvar\n");
}
- fprintf(file, "SET ");
+ my_b_printf(&cache, "SET ");
switch (type) {
case LAST_INSERT_ID_EVENT:
msg="LAST_INSERT_ID";
@@ -3662,31 +4945,58 @@ void Intvar_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info)
msg="INVALID_INT";
break;
}
- fprintf(file, "%s=%s%s\n",
- msg, llstr(val,llbuff), print_event_info->delimiter);
- fflush(file);
+ my_b_printf(&cache, "%s=%s%s\n",
+ msg, llstr(val,llbuff), print_event_info->delimiter);
}
#endif
/*
- Intvar_log_event::exec_event()
+ Intvar_log_event::do_apply_event()
*/
#if defined(HAVE_REPLICATION)&& !defined(MYSQL_CLIENT)
-int Intvar_log_event::exec_event(struct st_relay_log_info* rli)
+int Intvar_log_event::do_apply_event(Relay_log_info const *rli)
{
+ /*
+ We are now in a statement until the associated query log event has
+ been processed.
+ */
+ const_cast<Relay_log_info*>(rli)->set_flag(Relay_log_info::IN_STMT);
+
switch (type) {
case LAST_INSERT_ID_EVENT:
- thd->last_insert_id = val;
+ thd->stmt_depends_on_first_successful_insert_id_in_prev_stmt= 1;
+ thd->first_successful_insert_id_in_prev_stmt= val;
break;
case INSERT_ID_EVENT:
- thd->next_insert_id = val;
+ thd->force_one_auto_inc_interval(val);
break;
}
+ return 0;
+}
+
+int Intvar_log_event::do_update_pos(Relay_log_info *rli)
+{
rli->inc_event_relay_log_pos();
return 0;
}
+
+
+Log_event::enum_skip_reason
+Intvar_log_event::do_shall_skip(Relay_log_info *rli)
+{
+ /*
+ It is a common error to set the slave skip counter to 1 instead of
+ 2 when recovering from an insert which used a auto increment,
+ rand, or user var. Therefore, if the slave skip counter is 1, we
+ just say that this event should be skipped by ignoring it, meaning
+ that we do not change the value of the slave skip counter since it
+ will be decreased by the following insert event.
+ */
+ return continue_group(rli);
+}
+
#endif
@@ -3711,7 +5021,9 @@ Rand_log_event::Rand_log_event(const char* buf,
const Format_description_log_event* description_event)
:Log_event(buf, description_event)
{
- buf+= description_event->common_header_len;
+ /* The Post-Header is empty. The Variable Data part begins immediately. */
+ buf+= description_event->common_header_len +
+ description_event->post_header_len[RAND_EVENT-1];
seed1= uint8korr(buf+RAND_SEED1_OFFSET);
seed2= uint8korr(buf+RAND_SEED2_OFFSET);
}
@@ -3720,7 +5032,7 @@ Rand_log_event::Rand_log_event(const char* buf,
#ifndef MYSQL_CLIENT
bool Rand_log_event::write(IO_CACHE* file)
{
- byte buf[16];
+ uchar buf[16];
int8store(buf + RAND_SEED1_OFFSET, seed1);
int8store(buf + RAND_SEED2_OFFSET, seed2);
return (write_header(file, sizeof(buf)) ||
@@ -3732,28 +5044,57 @@ bool Rand_log_event::write(IO_CACHE* file)
#ifdef MYSQL_CLIENT
void Rand_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info)
{
+ Write_on_release_cache cache(&print_event_info->head_cache, file,
+ Write_on_release_cache::FLUSH_F);
+
char llbuff[22],llbuff2[22];
if (!print_event_info->short_form)
{
- print_header(file, print_event_info);
- fprintf(file, "\tRand\n");
+ print_header(&cache, print_event_info, FALSE);
+ my_b_printf(&cache, "\tRand\n");
}
- fprintf(file, "SET @@RAND_SEED1=%s, @@RAND_SEED2=%s%s\n",
- llstr(seed1, llbuff),llstr(seed2, llbuff2),
- print_event_info->delimiter);
- fflush(file);
+ my_b_printf(&cache, "SET @@RAND_SEED1=%s, @@RAND_SEED2=%s%s\n",
+ llstr(seed1, llbuff),llstr(seed2, llbuff2),
+ print_event_info->delimiter);
}
#endif /* MYSQL_CLIENT */
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
-int Rand_log_event::exec_event(struct st_relay_log_info* rli)
+int Rand_log_event::do_apply_event(Relay_log_info const *rli)
{
+ /*
+ We are now in a statement until the associated query log event has
+ been processed.
+ */
+ const_cast<Relay_log_info*>(rli)->set_flag(Relay_log_info::IN_STMT);
+
thd->rand.seed1= (ulong) seed1;
thd->rand.seed2= (ulong) seed2;
+ return 0;
+}
+
+int Rand_log_event::do_update_pos(Relay_log_info *rli)
+{
rli->inc_event_relay_log_pos();
return 0;
}
+
+
+Log_event::enum_skip_reason
+Rand_log_event::do_shall_skip(Relay_log_info *rli)
+{
+ /*
+ It is a common error to set the slave skip counter to 1 instead of
+ 2 when recovering from an insert which used a auto increment,
+ rand, or user var. Therefore, if the slave skip counter is 1, we
+ just say that this event should be skipped by ignoring it, meaning
+ that we do not change the value of the slave skip counter since it
+ will be decreased by the following insert event.
+ */
+ return continue_group(rli);
+}
+
#endif /* !MYSQL_CLIENT */
@@ -3772,12 +5113,12 @@ void Xid_log_event::pack_info(Protocol *protocol)
}
#endif
-/*
- NOTE it's ok not to use int8store here,
+/**
+ @note
+ It's ok not to use int8store here,
as long as xid_t::set(ulonglong) and
- xid_t::get_my_xid doesn't do it either
-
- we don't care about actual values of xids as long as
+ xid_t::get_my_xid doesn't do it either.
+ We don't care about actual values of xids as long as
identical numbers compare identically
*/
@@ -3786,7 +5127,9 @@ Xid_log_event(const char* buf,
const Format_description_log_event *description_event)
:Log_event(buf, description_event)
{
- buf+= description_event->common_header_len;
+ /* The Post-Header is empty. The Variable Data part begins immediately. */
+ buf+= description_event->common_header_len +
+ description_event->post_header_len[XID_EVENT-1];
memcpy((char*) &xid, buf, sizeof(xid));
}
@@ -3796,7 +5139,7 @@ bool Xid_log_event::write(IO_CACHE* file)
{
DBUG_EXECUTE_IF("do_not_write_xid", return 0;);
return write_header(file, sizeof(xid)) ||
- my_b_safe_write(file, (byte*) &xid, sizeof(xid));
+ my_b_safe_write(file, (uchar*) &xid, sizeof(xid));
}
#endif
@@ -3804,26 +5147,40 @@ bool Xid_log_event::write(IO_CACHE* file)
#ifdef MYSQL_CLIENT
void Xid_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info)
{
+ Write_on_release_cache cache(&print_event_info->head_cache, file,
+ Write_on_release_cache::FLUSH_F);
+
if (!print_event_info->short_form)
{
char buf[64];
longlong10_to_str(xid, buf, 10);
- print_header(file, print_event_info);
- fprintf(file, "\tXid = %s\n", buf);
- fflush(file);
+ print_header(&cache, print_event_info, FALSE);
+ my_b_printf(&cache, "\tXid = %s\n", buf);
}
- fprintf(file, "COMMIT%s\n", print_event_info->delimiter);
+ my_b_printf(&cache, "COMMIT%s\n", print_event_info->delimiter);
}
#endif /* MYSQL_CLIENT */
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
-int Xid_log_event::exec_event(struct st_relay_log_info* rli)
+int Xid_log_event::do_apply_event(Relay_log_info const *rli)
{
/* For a slave Xid_log_event is COMMIT */
- mysql_log.write(thd,COM_QUERY,"COMMIT /* implicit, from Xid_log_event */");
- return end_trans(thd, COMMIT) || Log_event::exec_event(rli);
+ general_log_print(thd, COM_QUERY,
+ "COMMIT /* implicit, from Xid_log_event */");
+ return end_trans(thd, COMMIT);
+}
+
+Log_event::enum_skip_reason
+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;
+ DBUG_RETURN(Log_event::EVENT_SKIP_COUNT);
+ }
+ DBUG_RETURN(Log_event::do_shall_skip(rli));
}
#endif /* !MYSQL_CLIENT */
@@ -3841,7 +5198,8 @@ void User_var_log_event::pack_info(Protocol* protocol)
if (is_null)
{
- buf= my_malloc(val_offset + 5, MYF(MY_WME));
+ if (!(buf= (char*) my_malloc(val_offset + 5, MYF(MY_WME))))
+ return;
strmov(buf + val_offset, "NULL");
event_len= val_offset + 4;
}
@@ -3851,28 +5209,37 @@ void User_var_log_event::pack_info(Protocol* protocol)
case REAL_RESULT:
double real_val;
float8get(real_val, val);
- buf= my_malloc(val_offset + FLOATING_POINT_BUFFER, MYF(MY_WME));
+ if (!(buf= (char*) my_malloc(val_offset + FLOATING_POINT_BUFFER,
+ MYF(MY_WME))))
+ return;
event_len+= my_sprintf(buf + val_offset,
(buf + val_offset, "%.14g", real_val));
break;
case INT_RESULT:
- buf= my_malloc(val_offset + 22, MYF(MY_WME));
- event_len= (uint) (longlong10_to_str(uint8korr(val), buf + val_offset,-10) - buf);
+ if (!(buf= (char*) my_malloc(val_offset + 22, MYF(MY_WME))))
+ return;
+ event_len= longlong10_to_str(uint8korr(val), buf + val_offset,-10)-buf;
break;
case DECIMAL_RESULT:
{
- buf= my_malloc(val_offset + DECIMAL_MAX_STR_LENGTH, MYF(MY_WME));
+ if (!(buf= (char*) my_malloc(val_offset + DECIMAL_MAX_STR_LENGTH,
+ MYF(MY_WME))))
+ return;
String str(buf+val_offset, DECIMAL_MAX_STR_LENGTH, &my_charset_bin);
my_decimal dec;
- binary2my_decimal(E_DEC_FATAL_ERROR, val+2, &dec, val[0], val[1]);
+ binary2my_decimal(E_DEC_FATAL_ERROR, (uchar*) (val+2), &dec, val[0],
+ val[1]);
my_decimal2string(E_DEC_FATAL_ERROR, &dec, 0, 0, 0, &str);
event_len= str.length() + val_offset;
break;
}
case STRING_RESULT:
/* 15 is for 'COLLATE' and other chars */
- buf= my_malloc(event_len+val_len*2+1+2*MY_CS_NAME_SIZE+15, MYF(MY_WME));
+ buf= (char*) my_malloc(event_len+val_len*2+1+2*MY_CS_NAME_SIZE+15,
+ MYF(MY_WME));
CHARSET_INFO *cs;
+ if (!buf)
+ return;
if (!(cs= get_charset(charset_number, MYF(0))))
{
strmov(buf+val_offset, "???");
@@ -3883,7 +5250,7 @@ void User_var_log_event::pack_info(Protocol* protocol)
char *p= strxmov(buf + val_offset, "_", cs->csname, " ", NullS);
p= str_to_hex(p, val, val_len);
p= strxmov(p, " COLLATE ", cs->name, NullS);
- event_len= (uint) (p - buf);
+ event_len= p-buf;
}
break;
case ROW_RESULT:
@@ -3894,11 +5261,11 @@ void User_var_log_event::pack_info(Protocol* protocol)
}
buf[0]= '@';
buf[1]= '`';
+ memcpy(buf+2, name, name_len);
buf[2+name_len]= '`';
buf[3+name_len]= '=';
- memcpy(buf+2, name, name_len);
protocol->store(buf, event_len, &my_charset_bin);
- my_free(buf, MYF(MY_ALLOW_ZERO_PTR));
+ my_free(buf, MYF(0));
}
#endif /* !MYSQL_CLIENT */
@@ -3908,7 +5275,9 @@ User_var_log_event(const char* buf,
const Format_description_log_event* description_event)
:Log_event(buf, description_event)
{
- buf+= description_event->common_header_len;
+ /* The Post-Header is empty. The Variable Data part begins immediately. */
+ buf+= description_event->common_header_len +
+ description_event->post_header_len[USER_VAR_EVENT-1];
name_len= uint4korr(buf);
name= (char *) buf + UV_NAME_LEN_SIZE;
buf+= UV_NAME_LEN_SIZE + name_len;
@@ -3938,7 +5307,7 @@ bool User_var_log_event::write(IO_CACHE* file)
char buf[UV_NAME_LEN_SIZE];
char buf1[UV_VAL_IS_NULL + UV_VAL_TYPE_SIZE +
UV_CHARSET_NUMBER_SIZE + UV_VAL_LEN_SIZE];
- char buf2[max(8, DECIMAL_MAX_FIELD_SIZE + 2)], *pos= buf2;
+ uchar buf2[max(8, DECIMAL_MAX_FIELD_SIZE + 2)], *pos= buf2;
uint buf1_length;
ulong event_length;
@@ -3947,7 +5316,7 @@ bool User_var_log_event::write(IO_CACHE* file)
if ((buf1[0]= is_null))
{
buf1_length= 1;
- val_len= 0;
+ val_len= 0; // Length of 'pos'
}
else
{
@@ -3972,7 +5341,7 @@ bool User_var_log_event::write(IO_CACHE* file)
break;
}
case STRING_RESULT:
- pos= val;
+ pos= (uchar*) val;
break;
case ROW_RESULT:
default:
@@ -3987,10 +5356,10 @@ bool User_var_log_event::write(IO_CACHE* file)
event_length= sizeof(buf)+ name_len + buf1_length + val_len;
return (write_header(file, event_length) ||
- my_b_safe_write(file, (byte*) buf, sizeof(buf)) ||
- my_b_safe_write(file, (byte*) name, name_len) ||
- my_b_safe_write(file, (byte*) buf1, buf1_length) ||
- my_b_safe_write(file, (byte*) pos, val_len));
+ my_b_safe_write(file, (uchar*) buf, sizeof(buf)) ||
+ my_b_safe_write(file, (uchar*) name, name_len) ||
+ my_b_safe_write(file, (uchar*) buf1, buf1_length) ||
+ my_b_safe_write(file, pos, val_len));
}
#endif
@@ -4002,32 +5371,37 @@ bool User_var_log_event::write(IO_CACHE* file)
#ifdef MYSQL_CLIENT
void User_var_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info)
{
+ Write_on_release_cache cache(&print_event_info->head_cache, file,
+ Write_on_release_cache::FLUSH_F);
+
if (!print_event_info->short_form)
{
- print_header(file, print_event_info);
- fprintf(file, "\tUser_var\n");
+ print_header(&cache, print_event_info, FALSE);
+ my_b_printf(&cache, "\tUser_var\n");
}
- fprintf(file, "SET @`");
- my_fwrite(file, (byte*) name, (uint) (name_len), MYF(MY_NABP | MY_WME));
- fprintf(file, "`");
+ my_b_printf(&cache, "SET @`");
+ my_b_write(&cache, (uchar*) name, (uint) (name_len));
+ my_b_printf(&cache, "`");
if (is_null)
{
- fprintf(file, ":=NULL%s\n", print_event_info->delimiter);
+ my_b_printf(&cache, ":=NULL%s\n", print_event_info->delimiter);
}
else
{
switch (type) {
case REAL_RESULT:
double real_val;
+ char real_buf[FMT_G_BUFSIZE(14)];
float8get(real_val, val);
- fprintf(file, ":=%.14g%s\n", real_val, print_event_info->delimiter);
+ my_sprintf(real_buf, (real_buf, "%.14g", real_val));
+ my_b_printf(&cache, ":=%s%s\n", real_buf, print_event_info->delimiter);
break;
case INT_RESULT:
char int_buf[22];
longlong10_to_str(uint8korr(val), int_buf, -10);
- fprintf(file, ":=%s%s\n", int_buf, print_event_info->delimiter);
+ my_b_printf(&cache, ":=%s%s\n", int_buf, print_event_info->delimiter);
break;
case DECIMAL_RESULT:
{
@@ -4040,10 +5414,10 @@ void User_var_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info)
dec.len= 10;
dec.buf= dec_buf;
- bin2decimal(val+2, &dec, precision, scale);
+ bin2decimal((uchar*) val+2, &dec, precision, scale);
decimal2string(&dec, str_buf, &str_len, 0, 0, 0);
str_buf[str_len]= 0;
- fprintf(file, ":=%s%s\n",str_buf, print_event_info->delimiter);
+ my_b_printf(&cache, ":=%s%s\n", str_buf, print_event_info->delimiter);
break;
}
case STRING_RESULT:
@@ -4079,10 +5453,11 @@ void User_var_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info)
Generate an unusable command (=> syntax error) is probably the best
thing we can do here.
*/
- fprintf(file, ":=???%s\n", print_event_info->delimiter);
+ my_b_printf(&cache, ":=???%s\n", print_event_info->delimiter);
else
- fprintf(file, ":=_%s %s COLLATE `%s`%s\n",
- cs->csname, hex_str, cs->name, print_event_info->delimiter);
+ my_b_printf(&cache, ":=_%s %s COLLATE `%s`%s\n",
+ cs->csname, hex_str, cs->name,
+ print_event_info->delimiter);
my_afree(hex_str);
}
break;
@@ -4092,17 +5467,16 @@ void User_var_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info)
return;
}
}
- fflush(file);
}
#endif
/*
- User_var_log_event::exec_event()
+ User_var_log_event::do_apply_event()
*/
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
-int User_var_log_event::exec_event(struct st_relay_log_info* rli)
+int User_var_log_event::do_apply_event(Relay_log_info const *rli)
{
Item *it= 0;
CHARSET_INFO *charset;
@@ -4114,6 +5488,12 @@ int User_var_log_event::exec_event(struct st_relay_log_info* rli)
double real_val;
longlong int_val;
+ /*
+ We are now in a statement until the associated query log event has
+ been processed.
+ */
+ const_cast<Relay_log_info*>(rli)->set_flag(Relay_log_info::IN_STMT);
+
if (is_null)
{
it= new Item_null();
@@ -4123,7 +5503,7 @@ int User_var_log_event::exec_event(struct st_relay_log_info* rli)
switch (type) {
case REAL_RESULT:
float8get(real_val, val);
- it= new Item_float(real_val);
+ it= new Item_float(real_val, 0);
val= (char*) &real_val; // Pointer to value in native format
val_len= 8;
break;
@@ -4135,7 +5515,7 @@ int User_var_log_event::exec_event(struct st_relay_log_info* rli)
break;
case DECIMAL_RESULT:
{
- Item_decimal *dec= new Item_decimal(val+2, val[0], val[1]);
+ Item_decimal *dec= new Item_decimal((uchar*) val+2, val[0], val[1]);
it= dec;
val= (char *)dec->val_decimal(NULL);
val_len= sizeof(my_decimal);
@@ -4170,9 +5550,28 @@ int User_var_log_event::exec_event(struct st_relay_log_info* rli)
e.update_hash(val, val_len, type, charset, DERIVATION_IMPLICIT, 0);
free_root(thd->mem_root,0);
+ return 0;
+}
+
+int User_var_log_event::do_update_pos(Relay_log_info *rli)
+{
rli->inc_event_relay_log_pos();
return 0;
}
+
+Log_event::enum_skip_reason
+User_var_log_event::do_shall_skip(Relay_log_info *rli)
+{
+ /*
+ It is a common error to set the slave skip counter to 1 instead
+ of 2 when recovering from an insert which used a auto increment,
+ rand, or user var. Therefore, if the slave skip counter is 1, we
+ just say that this event should be skipped by ignoring it, meaning
+ that we do not change the value of the slave skip counter since it
+ will be decreased by the following insert event.
+ */
+ return continue_group(rli);
+}
#endif /* !MYSQL_CLIENT */
@@ -4182,13 +5581,14 @@ int User_var_log_event::exec_event(struct st_relay_log_info* rli)
#ifdef HAVE_REPLICATION
#ifdef MYSQL_CLIENT
-void Unknown_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info)
+void Unknown_log_event::print(FILE* file_arg, PRINT_EVENT_INFO* print_event_info)
{
+ Write_on_release_cache cache(&print_event_info->head_cache, file_arg);
+
if (print_event_info->short_form)
return;
- print_header(file, print_event_info);
- fputc('\n', file);
- fprintf(file, "# %s", "Unknown event\n");
+ print_header(&cache, print_event_info, FALSE);
+ my_b_printf(&cache, "\n# %s", "Unknown event\n");
}
#endif
@@ -4204,26 +5604,30 @@ void Slave_log_event::pack_info(Protocol *protocol)
pos= strmov(pos, master_log);
pos= strmov(pos, ",pos=");
pos= longlong10_to_str(master_pos, pos, 10);
- protocol->store(buf, (uint) (pos - buf), &my_charset_bin);
+ protocol->store(buf, pos-buf, &my_charset_bin);
}
#endif /* !MYSQL_CLIENT */
#ifndef MYSQL_CLIENT
+/**
+ @todo
+ re-write this better without holding both locks at the same time
+*/
Slave_log_event::Slave_log_event(THD* thd_arg,
- struct st_relay_log_info* rli)
+ Relay_log_info* rli)
:Log_event(thd_arg, 0, 0) , mem_pool(0), master_host(0)
{
DBUG_ENTER("Slave_log_event");
if (!rli->inited) // QQ When can this happen ?
DBUG_VOID_RETURN;
- MASTER_INFO* mi = rli->mi;
+ 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);
- master_host_len= (uint) strlen(mi->host);
- master_log_len= (uint) strlen(rli->group_master_log_name);
+ 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))))
@@ -4255,12 +5659,13 @@ Slave_log_event::~Slave_log_event()
#ifdef MYSQL_CLIENT
void Slave_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info)
{
+ Write_on_release_cache cache(&print_event_info->head_cache, file);
+
char llbuff[22];
if (print_event_info->short_form)
return;
- print_header(file, print_event_info);
- fputc('\n', file);
- fprintf(file, "\
+ print_header(&cache, print_event_info, FALSE);
+ my_b_printf(&cache, "\n\
Slave: master_host: '%s' master_port: %d master_log: '%s' master_pos: %s\n",
master_host, master_port, master_log, llstr(master_pos, llbuff));
}
@@ -4282,7 +5687,7 @@ bool Slave_log_event::write(IO_CACHE* file)
// log and host are already there
return (write_header(file, event_length) ||
- my_b_safe_write(file, (byte*) mem_pool, event_length));
+ my_b_safe_write(file, (uchar*) mem_pool, event_length));
}
#endif
@@ -4292,7 +5697,7 @@ void Slave_log_event::init_from_mem_pool(int data_size)
master_pos = uint8korr(mem_pool + SL_MASTER_POS_OFFSET);
master_port = uint2korr(mem_pool + SL_MASTER_PORT_OFFSET);
master_host = mem_pool + SL_MASTER_HOST_OFFSET;
- master_host_len= (uint) strlen(master_host);
+ master_host_len = (uint) strlen(master_host);
// safety
master_log = master_host + master_host_len + 1;
if (master_log > mem_pool + data_size)
@@ -4300,11 +5705,11 @@ void Slave_log_event::init_from_mem_pool(int data_size)
master_host = 0;
return;
}
- master_log_len= (uint) strlen(master_log);
+ master_log_len = (uint) strlen(master_log);
}
-/* This code is not used, so has not been updated to be format-tolerant */
+/** This code is not used, so has not been updated to be format-tolerant. */
Slave_log_event::Slave_log_event(const char* buf, uint event_len)
:Log_event(buf,0) /*unused event*/ ,mem_pool(0),master_host(0)
{
@@ -4320,11 +5725,11 @@ Slave_log_event::Slave_log_event(const char* buf, uint event_len)
#ifndef MYSQL_CLIENT
-int Slave_log_event::exec_event(struct st_relay_log_info* rli)
+int Slave_log_event::do_apply_event(Relay_log_info const *rli)
{
if (mysql_bin_log.is_open())
mysql_bin_log.write(this);
- return Log_event::exec_event(rli);
+ return 0;
}
#endif /* !MYSQL_CLIENT */
@@ -4340,32 +5745,31 @@ int Slave_log_event::exec_event(struct st_relay_log_info* rli)
#ifdef MYSQL_CLIENT
void Stop_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info)
{
+ Write_on_release_cache cache(&print_event_info->head_cache, file,
+ Write_on_release_cache::FLUSH_F);
+
if (print_event_info->short_form)
return;
- print_header(file, print_event_info);
- fprintf(file, "\tStop\n");
- fflush(file);
+ print_header(&cache, print_event_info, FALSE);
+ my_b_printf(&cache, "\tStop\n");
}
#endif /* MYSQL_CLIENT */
+#ifndef MYSQL_CLIENT
/*
- Stop_log_event::exec_event()
-
- The master stopped.
- We used to clean up all temporary tables but this is useless as, as the
- master has shut down properly, it has written all DROP TEMPORARY TABLE
- (prepared statements' deletion is TODO only when we binlog prep stmts).
- We used to clean up slave_load_tmpdir, but this is useless as it has been
- cleared at the end of LOAD DATA INFILE.
- So we have nothing to do here.
- The place were we must do this cleaning is in Start_log_event_v3::exec_event(),
- not here. Because if we come here, the master was sane.
+ The master stopped. We used to clean up all temporary tables but
+ this is useless as, as the master has shut down properly, it has
+ written all DROP TEMPORARY TABLE (prepared statements' deletion is
+ TODO only when we binlog prep stmts). We used to clean up
+ slave_load_tmpdir, but this is useless as it has been cleared at the
+ end of LOAD DATA INFILE. So we have nothing to do here. The place
+ were we must do this cleaning is in
+ Start_log_event_v3::do_apply_event(), not here. Because if we come
+ here, the master was sane.
*/
-
-#ifndef MYSQL_CLIENT
-int Stop_log_event::exec_event(struct st_relay_log_info* rli)
+int Stop_log_event::do_update_pos(Relay_log_info *rli)
{
/*
We do not want to update master_log pos because we get a rotate event
@@ -4383,6 +5787,7 @@ int Stop_log_event::exec_event(struct st_relay_log_info* rli)
}
return 0;
}
+
#endif /* !MYSQL_CLIENT */
#endif /* HAVE_REPLICATION */
@@ -4401,7 +5806,7 @@ 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,
bool ignore,
- char* block_arg, uint block_len_arg, bool using_trans)
+ 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),
fake_base(0), block(block_arg), event_buf(0), block_len(block_len_arg),
@@ -4422,8 +5827,8 @@ bool Create_file_log_event::write_data_body(IO_CACHE* file)
bool res;
if ((res= Load_log_event::write_data_body(file)) || fake_base)
return res;
- return (my_b_safe_write(file, (byte*) "", 1) ||
- my_b_safe_write(file, (byte*) block, block_len));
+ return (my_b_safe_write(file, (uchar*) "", 1) ||
+ my_b_safe_write(file, (uchar*) block, block_len));
}
@@ -4434,7 +5839,7 @@ bool Create_file_log_event::write_data_body(IO_CACHE* file)
bool Create_file_log_event::write_data_header(IO_CACHE* file)
{
bool res;
- byte buf[CREATE_FILE_HEADER_LEN];
+ uchar buf[CREATE_FILE_HEADER_LEN];
if ((res= Load_log_event::write_data_header(file)) || fake_base)
return res;
int4store(buf + CF_FILE_ID_OFFSET, file_id);
@@ -4470,7 +5875,7 @@ Create_file_log_event::Create_file_log_event(const char* buf, uint len,
uint header_len= description_event->common_header_len;
uint8 load_header_len= description_event->post_header_len[LOAD_EVENT-1];
uint8 create_file_header_len= description_event->post_header_len[CREATE_FILE_EVENT-1];
- if (!(event_buf= my_memdup((byte*) buf, len, MYF(MY_WME))) ||
+ if (!(event_buf= (char*) my_memdup(buf, len, MYF(MY_WME))) ||
copy_log_event(event_buf,len,
((buf[EVENT_TYPE_OFFSET] == LOAD_EVENT) ?
load_header_len + header_len :
@@ -4499,8 +5904,8 @@ Create_file_log_event::Create_file_log_event(const char* buf, uint len,
Load_log_event::get_data_size() +
create_file_header_len + 1);
if (len < block_offset)
- return;
- block = (char*)buf + block_offset;
+ DBUG_VOID_RETURN;
+ block = (uchar*)buf + block_offset;
block_len = len - block_offset;
}
else
@@ -4520,6 +5925,8 @@ Create_file_log_event::Create_file_log_event(const char* buf, uint len,
void Create_file_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info,
bool enable_local)
{
+ Write_on_release_cache cache(&print_event_info->head_cache, file);
+
if (print_event_info->short_form)
{
if (enable_local && check_fname_outside_temp_buf())
@@ -4535,10 +5942,10 @@ void Create_file_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info
That one is for "file_id: etc" below: in mysqlbinlog we want the #, in
SHOW BINLOG EVENTS we don't.
*/
- fprintf(file, "#");
+ my_b_printf(&cache, "#");
}
- fprintf(file, " file_id: %d block_len: %d\n", file_id, block_len);
+ my_b_printf(&cache, " file_id: %d block_len: %d\n", file_id, block_len);
}
@@ -4570,12 +5977,19 @@ void Create_file_log_event::pack_info(Protocol *protocol)
#endif /* defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT) */
-/*
- Create_file_log_event::exec_event()
+/**
+ Create_file_log_event::do_apply_event()
+ Constructor for Create_file_log_event to intantiate an event
+ from the relay log on the slave.
+
+ @retval
+ 0 Success
+ @retval
+ 1 Failure
*/
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
-int Create_file_log_event::exec_event(struct st_relay_log_info* rli)
+int Create_file_log_event::do_apply_event(Relay_log_info const *rli)
{
char proc_info[17+FN_REFLEN+10], *fname_buf;
char *ext;
@@ -4586,7 +6000,7 @@ int Create_file_log_event::exec_event(struct st_relay_log_info* rli)
bzero((char*)&file, sizeof(file));
fname_buf= strmov(proc_info, "Making temp file ");
ext= slave_load_file_stem(fname_buf, file_id, server_id, ".info");
- thd->proc_info= proc_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,
@@ -4594,9 +6008,9 @@ int Create_file_log_event::exec_event(struct st_relay_log_info* rli)
init_io_cache(&file, fd, IO_SIZE, WRITE_CACHE, (my_off_t)0, 0,
MYF(MY_WME|MY_NABP)))
{
- slave_print_error(rli,my_errno,
- "Error in Create_file event: could not open file '%s'",
- fname_buf);
+ rli->report(ERROR_LEVEL, my_errno,
+ "Error in Create_file event: could not open file '%s'",
+ fname_buf);
goto err;
}
@@ -4606,10 +6020,9 @@ int Create_file_log_event::exec_event(struct st_relay_log_info* rli)
if (write_base(&file))
{
strmov(ext, ".info"); // to have it right in the error message
- slave_print_error(rli,my_errno,
- "Error in Create_file event: could not write to file "
- "'%s'",
- fname_buf);
+ rli->report(ERROR_LEVEL, my_errno,
+ "Error in Create_file event: could not write to file '%s'",
+ fname_buf);
goto err;
}
end_io_cache(&file);
@@ -4621,16 +6034,16 @@ int Create_file_log_event::exec_event(struct st_relay_log_info* rli)
O_WRONLY | O_BINARY | O_EXCL | O_NOFOLLOW,
MYF(MY_WME))) < 0)
{
- slave_print_error(rli,my_errno,
- "Error in Create_file event: could not open file '%s'",
- fname_buf);
+ rli->report(ERROR_LEVEL, my_errno,
+ "Error in Create_file event: could not open file '%s'",
+ fname_buf);
goto err;
}
- if (my_write(fd, (byte*) block, block_len, MYF(MY_WME+MY_NABP)))
+ if (my_write(fd, (uchar*) block, block_len, MYF(MY_WME+MY_NABP)))
{
- slave_print_error(rli,my_errno,
- "Error in Create_file event: write to '%s' failed",
- fname_buf);
+ rli->report(ERROR_LEVEL, my_errno,
+ "Error in Create_file event: write to '%s' failed",
+ fname_buf);
goto err;
}
error=0; // Everything is ok
@@ -4640,8 +6053,8 @@ err:
end_io_cache(&file);
if (fd >= 0)
my_close(fd, MYF(0));
- thd->proc_info= 0;
- return error ? 1 : Log_event::exec_event(rli);
+ thd_proc_info(thd, 0);
+ return error != 0;
}
#endif /* defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT) */
@@ -4655,8 +6068,9 @@ err:
*/
#ifndef MYSQL_CLIENT
-Append_block_log_event::Append_block_log_event(THD* thd_arg, const char* db_arg,
- char* block_arg,
+Append_block_log_event::Append_block_log_event(THD *thd_arg,
+ const char *db_arg,
+ uchar *block_arg,
uint block_len_arg,
bool using_trans)
:Log_event(thd_arg,0, using_trans), block(block_arg),
@@ -4682,7 +6096,7 @@ Append_block_log_event::Append_block_log_event(const char* buf, uint len,
if (len < total_header_len)
DBUG_VOID_RETURN;
file_id= uint4korr(buf + common_header_len + AB_FILE_ID_OFFSET);
- block= (char*)buf + total_header_len;
+ block= (uchar*)buf + total_header_len;
block_len= len - total_header_len;
DBUG_VOID_RETURN;
}
@@ -4695,11 +6109,11 @@ Append_block_log_event::Append_block_log_event(const char* buf, uint len,
#ifndef MYSQL_CLIENT
bool Append_block_log_event::write(IO_CACHE* file)
{
- byte buf[APPEND_BLOCK_HEADER_LEN];
+ uchar buf[APPEND_BLOCK_HEADER_LEN];
int4store(buf + AB_FILE_ID_OFFSET, file_id);
return (write_header(file, APPEND_BLOCK_HEADER_LEN + block_len) ||
my_b_safe_write(file, buf, APPEND_BLOCK_HEADER_LEN) ||
- my_b_safe_write(file, (byte*) block, block_len));
+ my_b_safe_write(file, (uchar*) block, block_len));
}
#endif
@@ -4712,12 +6126,13 @@ bool Append_block_log_event::write(IO_CACHE* file)
void Append_block_log_event::print(FILE* file,
PRINT_EVENT_INFO* print_event_info)
{
+ Write_on_release_cache cache(&print_event_info->head_cache, file);
+
if (print_event_info->short_form)
return;
- print_header(file, print_event_info);
- fputc('\n', file);
- fprintf(file, "#%s: file_id: %d block_len: %d\n",
- get_type_str(), file_id, block_len);
+ print_header(&cache, print_event_info, FALSE);
+ my_b_printf(&cache, "\n#%s: file_id: %d block_len: %d\n",
+ get_type_str(), file_id, block_len);
}
#endif /* MYSQL_CLIENT */
@@ -4748,19 +6163,19 @@ int Append_block_log_event::get_create_or_append() const
}
/*
- Append_block_log_event::exec_event()
+ Append_block_log_event::do_apply_event()
*/
-int Append_block_log_event::exec_event(struct st_relay_log_info* rli)
+int Append_block_log_event::do_apply_event(Relay_log_info const *rli)
{
char proc_info[17+FN_REFLEN+10], *fname= proc_info+17;
int fd;
int error = 1;
- DBUG_ENTER("Append_block_log_event::exec_event");
+ DBUG_ENTER("Append_block_log_event::do_apply_event");
fname= strmov(proc_info, "Making temp file ");
slave_load_file_stem(fname, file_id, server_id, ".data");
- thd->proc_info= proc_info;
+ thd_proc_info(thd, proc_info);
if (get_create_or_append())
{
my_delete(fname, MYF(0)); // old copy may exist already
@@ -4768,25 +6183,25 @@ int Append_block_log_event::exec_event(struct st_relay_log_info* rli)
O_WRONLY | O_BINARY | O_EXCL | O_NOFOLLOW,
MYF(MY_WME))) < 0)
{
- slave_print_error(rli, my_errno,
- "Error in %s event: could not create file '%s'",
- get_type_str(), fname);
+ rli->report(ERROR_LEVEL, my_errno,
+ "Error in %s event: could not create file '%s'",
+ get_type_str(), fname);
goto err;
}
}
else if ((fd = my_open(fname, O_WRONLY | O_APPEND | O_BINARY | O_NOFOLLOW,
MYF(MY_WME))) < 0)
{
- slave_print_error(rli, my_errno,
- "Error in %s event: could not open file '%s'",
- get_type_str(), fname);
+ rli->report(ERROR_LEVEL, my_errno,
+ "Error in %s event: could not open file '%s'",
+ get_type_str(), fname);
goto err;
}
- if (my_write(fd, (byte*) block, block_len, MYF(MY_WME+MY_NABP)))
+ if (my_write(fd, (uchar*) block, block_len, MYF(MY_WME+MY_NABP)))
{
- slave_print_error(rli, my_errno,
- "Error in %s event: write to '%s' failed",
- get_type_str(), fname);
+ rli->report(ERROR_LEVEL, my_errno,
+ "Error in %s event: write to '%s' failed",
+ get_type_str(), fname);
goto err;
}
error=0;
@@ -4794,8 +6209,8 @@ int Append_block_log_event::exec_event(struct st_relay_log_info* rli)
err:
if (fd >= 0)
my_close(fd, MYF(0));
- thd->proc_info= 0;
- DBUG_RETURN(error ? error : Log_event::exec_event(rli));
+ thd_proc_info(thd, 0);
+ DBUG_RETURN(error);
}
#endif
@@ -4839,7 +6254,7 @@ Delete_file_log_event::Delete_file_log_event(const char* buf, uint len,
#ifndef MYSQL_CLIENT
bool Delete_file_log_event::write(IO_CACHE* file)
{
- byte buf[DELETE_FILE_HEADER_LEN];
+ uchar buf[DELETE_FILE_HEADER_LEN];
int4store(buf + DF_FILE_ID_OFFSET, file_id);
return (write_header(file, sizeof(buf)) ||
my_b_safe_write(file, buf, sizeof(buf)));
@@ -4855,11 +6270,12 @@ bool Delete_file_log_event::write(IO_CACHE* file)
void Delete_file_log_event::print(FILE* file,
PRINT_EVENT_INFO* print_event_info)
{
+ Write_on_release_cache cache(&print_event_info->head_cache, file);
+
if (print_event_info->short_form)
return;
- print_header(file, print_event_info);
- fputc('\n', file);
- fprintf(file, "#Delete_file: file_id=%u\n", file_id);
+ print_header(&cache, print_event_info, FALSE);
+ my_b_printf(&cache, "\n#Delete_file: file_id=%u\n", file_id);
}
#endif /* MYSQL_CLIENT */
@@ -4878,18 +6294,18 @@ void Delete_file_log_event::pack_info(Protocol *protocol)
#endif
/*
- Delete_file_log_event::exec_event()
+ Delete_file_log_event::do_apply_event()
*/
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
-int Delete_file_log_event::exec_event(struct st_relay_log_info* rli)
+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));
strmov(ext, ".info");
(void) my_delete(fname, MYF(MY_WME));
- return Log_event::exec_event(rli);
+ return 0;
}
#endif /* defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT) */
@@ -4903,7 +6319,8 @@ int Delete_file_log_event::exec_event(struct st_relay_log_info* rli)
*/
#ifndef MYSQL_CLIENT
-Execute_load_log_event::Execute_load_log_event(THD *thd_arg, const char* db_arg,
+Execute_load_log_event::Execute_load_log_event(THD *thd_arg,
+ const char* db_arg,
bool using_trans)
:Log_event(thd_arg, 0, using_trans), file_id(thd_arg->file_id), db(db_arg)
{
@@ -4934,7 +6351,7 @@ Execute_load_log_event::Execute_load_log_event(const char* buf, uint len,
#ifndef MYSQL_CLIENT
bool Execute_load_log_event::write(IO_CACHE* file)
{
- byte buf[EXEC_LOAD_HEADER_LEN];
+ uchar buf[EXEC_LOAD_HEADER_LEN];
int4store(buf + EL_FILE_ID_OFFSET, file_id);
return (write_header(file, sizeof(buf)) ||
my_b_safe_write(file, buf, sizeof(buf)));
@@ -4950,12 +6367,13 @@ bool Execute_load_log_event::write(IO_CACHE* file)
void Execute_load_log_event::print(FILE* file,
PRINT_EVENT_INFO* print_event_info)
{
+ Write_on_release_cache cache(&print_event_info->head_cache, file);
+
if (print_event_info->short_form)
return;
- print_header(file, print_event_info);
- fputc('\n', file);
- fprintf(file, "#Exec_load: file_id=%d\n",
- file_id);
+ print_header(&cache, print_event_info, FALSE);
+ my_b_printf(&cache, "\n#Exec_load: file_id=%d\n",
+ file_id);
}
#endif
@@ -4974,10 +6392,10 @@ void Execute_load_log_event::pack_info(Protocol *protocol)
/*
- Execute_load_log_event::exec_event()
+ Execute_load_log_event::do_apply_event()
*/
-int Execute_load_log_event::exec_event(struct st_relay_log_info* rli)
+int Execute_load_log_event::do_apply_event(Relay_log_info const *rli)
{
char fname[FN_REFLEN+10];
char *ext;
@@ -4992,9 +6410,9 @@ int Execute_load_log_event::exec_event(struct st_relay_log_info* rli)
init_io_cache(&file, fd, IO_SIZE, READ_CACHE, (my_off_t)0, 0,
MYF(MY_WME|MY_NABP)))
{
- slave_print_error(rli,my_errno,
- "Error in Exec_load event: could not open file '%s'",
- fname);
+ rli->report(ERROR_LEVEL, my_errno,
+ "Error in Exec_load event: could not open file '%s'",
+ fname);
goto err;
}
if (!(lev = (Load_log_event*)Log_event::read_log_event(&file,
@@ -5002,22 +6420,22 @@ int Execute_load_log_event::exec_event(struct st_relay_log_info* rli)
rli->relay_log.description_event_for_exec)) ||
lev->get_type_code() != NEW_LOAD_EVENT)
{
- slave_print_error(rli,0,
- "Error in Exec_load event: file '%s' appears corrupted",
- fname);
+ rli->report(ERROR_LEVEL, 0, "Error in Exec_load event: "
+ "file '%s' appears corrupted", fname);
goto err;
}
lev->thd = thd;
/*
- lev->exec_event should use rli only for errors
- i.e. should not advance rli's position.
- lev->exec_event is the place where the table is loaded (it calls
- mysql_load()).
+ lev->do_apply_event should use rli only for errors i.e. should
+ not advance rli's position.
+
+ lev->do_apply_event is the place where the table is loaded (it
+ calls mysql_load()).
*/
- rli->future_group_master_log_pos= log_pos;
- if (lev->exec_event(0,rli,1))
+ const_cast<Relay_log_info*>(rli)->future_group_master_log_pos= log_pos;
+ if (lev->do_apply_event(0,rli,1))
{
/*
We want to indicate the name of the file that could not be loaded
@@ -5027,13 +6445,11 @@ int Execute_load_log_event::exec_event(struct st_relay_log_info* rli)
don't want to overwrite it with the filename.
What we want instead is add the filename to the current error message.
*/
- char *tmp= my_strdup(rli->last_slave_error,MYF(MY_WME));
+ char *tmp= my_strdup(rli->last_error().message, MYF(MY_WME));
if (tmp)
{
- slave_print_error(rli,
- rli->last_slave_errno, /* ok to re-use error code */
- "%s. Failed executing load from '%s'",
- tmp, fname);
+ rli->report(ERROR_LEVEL, rli->last_error().number,
+ "%s. Failed executing load from '%s'", tmp, fname);
my_free(tmp,MYF(0));
}
goto err;
@@ -5060,7 +6476,7 @@ err:
my_close(fd, MYF(0));
end_io_cache(&file);
}
- return error ? error : Log_event::exec_event(rli);
+ return error;
}
#endif /* defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT) */
@@ -5072,7 +6488,7 @@ err:
#ifndef MYSQL_CLIENT
Begin_load_query_log_event::
-Begin_load_query_log_event(THD* thd_arg, const char* db_arg, char* block_arg,
+Begin_load_query_log_event(THD* thd_arg, const char* db_arg, uchar* block_arg,
uint block_len_arg, bool using_trans)
:Append_block_log_event(thd_arg, db_arg, block_arg, block_len_arg,
using_trans)
@@ -5098,6 +6514,19 @@ int Begin_load_query_log_event::get_create_or_append() const
#endif /* defined( HAVE_REPLICATION) && !defined(MYSQL_CLIENT) */
+#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
+Log_event::enum_skip_reason
+Begin_load_query_log_event::do_shall_skip(Relay_log_info *rli)
+{
+ /*
+ If the slave skip counter is 1, then we should not start executing
+ on the next event.
+ */
+ return continue_group(rli);
+}
+#endif
+
+
/**************************************************************************
Execute_load_query_log_event methods
**************************************************************************/
@@ -5105,7 +6534,7 @@ int Begin_load_query_log_event::get_create_or_append() const
#ifndef MYSQL_CLIENT
Execute_load_query_log_event::
-Execute_load_query_log_event(THD* thd_arg, const char* query_arg,
+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,
@@ -5153,12 +6582,12 @@ ulong Execute_load_query_log_event::get_post_header_size_for_derived()
bool
Execute_load_query_log_event::write_post_header_for_derived(IO_CACHE* file)
{
- char buf[EXECUTE_LOAD_QUERY_EXTRA_HEADER_LEN];
+ uchar buf[EXECUTE_LOAD_QUERY_EXTRA_HEADER_LEN];
int4store(buf, file_id);
int4store(buf + 4, fn_pos_start);
int4store(buf + 4 + 4, fn_pos_end);
- *(buf + 4 + 4 + 4)= (char)dup_handling;
- return my_b_safe_write(file, (byte*) buf, EXECUTE_LOAD_QUERY_EXTRA_HEADER_LEN);
+ *(buf + 4 + 4 + 4)= (uchar) dup_handling;
+ return my_b_safe_write(file, buf, EXECUTE_LOAD_QUERY_EXTRA_HEADER_LEN);
}
#endif
@@ -5170,34 +6599,37 @@ void Execute_load_query_log_event::print(FILE* file,
print(file, print_event_info, 0);
}
-
+/**
+ Prints the query as LOAD DATA LOCAL and with rewritten filename.
+*/
void Execute_load_query_log_event::print(FILE* file,
PRINT_EVENT_INFO* print_event_info,
const char *local_fname)
{
- print_query_header(file, print_event_info);
+ Write_on_release_cache cache(&print_event_info->head_cache, file);
+
+ print_query_header(&cache, print_event_info);
if (local_fname)
{
- my_fwrite(file, (byte*) query, fn_pos_start, MYF(MY_NABP | MY_WME));
- fprintf(file, " LOCAL INFILE \'");
- fprintf(file, local_fname);
- fprintf(file, "\'");
+ my_b_write(&cache, (uchar*) query, fn_pos_start);
+ my_b_printf(&cache, " LOCAL INFILE \'");
+ my_b_printf(&cache, local_fname);
+ my_b_printf(&cache, "\'");
if (dup_handling == LOAD_DUP_REPLACE)
- fprintf(file, " REPLACE");
- fprintf(file, " INTO");
- my_fwrite(file, (byte*) query + fn_pos_end, q_len-fn_pos_end,
- MYF(MY_NABP | MY_WME));
- fprintf(file, "\n%s\n", print_event_info->delimiter);
+ my_b_printf(&cache, " REPLACE");
+ my_b_printf(&cache, " INTO");
+ my_b_write(&cache, (uchar*) query + fn_pos_end, q_len-fn_pos_end);
+ my_b_printf(&cache, "\n%s\n", print_event_info->delimiter);
}
else
{
- my_fwrite(file, (byte*) query, q_len, MYF(MY_NABP | MY_WME));
- fprintf(file, "\n%s\n", print_event_info->delimiter);
+ my_b_write(&cache, (uchar*) query, q_len);
+ my_b_printf(&cache, "\n%s\n", print_event_info->delimiter);
}
if (!print_event_info->short_form)
- fprintf(file, "# file_id: %d \n", file_id);
+ my_b_printf(&cache, "# file_id: %d \n", file_id);
}
#endif
@@ -5206,7 +6638,7 @@ void Execute_load_query_log_event::print(FILE* file,
void Execute_load_query_log_event::pack_info(Protocol *protocol)
{
char *buf, *pos;
- if (!(buf= my_malloc(9 + db_len + q_len + 10 + 21, MYF(MY_WME))))
+ if (!(buf= (char*) my_malloc(9 + db_len + q_len + 10 + 21, MYF(MY_WME))))
return;
pos= buf;
if (db && db_len)
@@ -5222,13 +6654,13 @@ void Execute_load_query_log_event::pack_info(Protocol *protocol)
}
pos= strmov(pos, " ;file_id=");
pos= int10_to_str((long) file_id, pos, 10);
- protocol->store(buf, (uint) (pos - buf), &my_charset_bin);
+ protocol->store(buf, pos-buf, &my_charset_bin);
my_free(buf, MYF(MY_ALLOW_ZERO_PTR));
}
int
-Execute_load_query_log_event::exec_event(struct st_relay_log_info* rli)
+Execute_load_query_log_event::do_apply_event(Relay_log_info const *rli)
{
char *p;
char *buf;
@@ -5236,11 +6668,16 @@ Execute_load_query_log_event::exec_event(struct st_relay_log_info* rli)
char *fname_end;
int error;
+ 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;);
+
/* Replace filename and LOCAL keyword in query before executing it */
- if (!(buf = my_malloc(q_len + 1 - (fn_pos_end - fn_pos_start) +
- (FN_REFLEN + 10) + 10 + 8 + 5, MYF(MY_WME))))
+ if (buf == NULL)
{
- slave_print_error(rli, my_errno, "Not enough memory");
+ rli->report(ERROR_LEVEL, ER_SLAVE_FATAL_ERROR,
+ ER(ER_SLAVE_FATAL_ERROR), "Not enough memory");
return 1;
}
@@ -5265,7 +6702,7 @@ Execute_load_query_log_event::exec_event(struct st_relay_log_info* rli)
p= strmake(p, STRING_WITH_LEN(" INTO"));
p= strmake(p, query+fn_pos_end, q_len-fn_pos_end);
- error= Query_log_event::exec_event(rli, buf, (uint) (p - buf));
+ error= Query_log_event::do_apply_event(rli, buf, p-buf);
/* Forging file name for deletion in same buffer */
*fname_end= 0;
@@ -5300,10 +6737,14 @@ bool sql_ex_info::write_data(IO_CACHE* file)
write_str(file, line_term, (uint) line_term_len) ||
write_str(file, line_start, (uint) line_start_len) ||
write_str(file, escaped, (uint) escaped_len) ||
- my_b_safe_write(file,(byte*) &opt_flags,1));
+ my_b_safe_write(file,(uchar*) &opt_flags,1));
}
else
{
+ /**
+ @todo This is sensitive to field padding. We should write a
+ char[7], not an old_sql_ex. /sven
+ */
old_sql_ex old_ex;
old_ex.field_term= *field_term;
old_ex.enclosed= *enclosed;
@@ -5312,7 +6753,7 @@ bool sql_ex_info::write_data(IO_CACHE* file)
old_ex.escaped= *escaped;
old_ex.opt_flags= opt_flags;
old_ex.empty_flags=empty_flags;
- return my_b_safe_write(file, (byte*) &old_ex, sizeof(old_ex)) != 0;
+ return my_b_safe_write(file, (uchar*) &old_ex, sizeof(old_ex)) != 0;
}
}
@@ -5321,7 +6762,8 @@ bool sql_ex_info::write_data(IO_CACHE* file)
sql_ex_info::init()
*/
-char* sql_ex_info::init(char* buf,char* buf_end,bool use_new_format)
+const char *sql_ex_info::init(const char *buf, const char *buf_end,
+ bool use_new_format)
{
cached_new_format = use_new_format;
if (use_new_format)
@@ -5335,10 +6777,10 @@ char* sql_ex_info::init(char* buf,char* buf_end,bool use_new_format)
to read the actual file before we write out the Create_file event.
*/
if (read_str(&buf, buf_end, &field_term, &field_term_len) ||
- read_str(&buf, buf_end, &enclosed, &enclosed_len) ||
- read_str(&buf, buf_end, &line_term, &line_term_len) ||
- read_str(&buf, buf_end, &line_start, &line_start_len) ||
- read_str(&buf, buf_end, &escaped, &escaped_len))
+ read_str(&buf, buf_end, &enclosed, &enclosed_len) ||
+ read_str(&buf, buf_end, &line_term, &line_term_len) ||
+ read_str(&buf, buf_end, &line_start, &line_start_len) ||
+ read_str(&buf, buf_end, &escaped, &escaped_len))
return 0;
opt_flags = *buf++;
}
@@ -5365,3 +6807,2497 @@ char* sql_ex_info::init(char* buf,char* buf_end,bool use_new_format)
}
return buf;
}
+
+
+/**************************************************************************
+ Rows_log_event member functions
+**************************************************************************/
+
+#ifndef MYSQL_CLIENT
+Rows_log_event::Rows_log_event(THD *thd_arg, TABLE *tbl_arg, ulong tid,
+ MY_BITMAP const *cols, bool is_transactional)
+ : Log_event(thd_arg, 0, is_transactional),
+ m_row_count(0),
+ m_table(tbl_arg),
+ m_table_id(tid),
+ m_width(tbl_arg ? tbl_arg->s->fields : 1),
+ m_rows_buf(0), m_rows_cur(0), m_rows_end(0), m_flags(0)
+#ifdef HAVE_REPLICATION
+ , m_curr_row(NULL), m_curr_row_end(NULL), m_key(NULL)
+#endif
+{
+ /*
+ We allow a special form of dummy event when the table, and cols
+ are null and the table id is ~0UL. This is a temporary
+ solution, to be able to terminate a started statement in the
+ binary log: the extraneous events will be removed in the future.
+ */
+ DBUG_ASSERT(tbl_arg && tbl_arg->s && tid != ~0UL ||
+ !tbl_arg && !cols && tid == ~0UL);
+
+ if (thd_arg->options & OPTION_NO_FOREIGN_KEY_CHECKS)
+ set_flags(NO_FOREIGN_KEY_CHECKS_F);
+ if (thd_arg->options & OPTION_RELAXED_UNIQUE_CHECKS)
+ set_flags(RELAXED_UNIQUE_CHECKS_F);
+ /* if bitmap_init fails, caught in is_valid() */
+ if (likely(!bitmap_init(&m_cols,
+ m_width <= sizeof(m_bitbuf)*8 ? m_bitbuf : NULL,
+ m_width,
+ false)))
+ {
+ /* Cols can be zero if this is a dummy binrows event */
+ if (likely(cols != NULL))
+ {
+ memcpy(m_cols.bitmap, cols->bitmap, no_bytes_in_map(cols));
+ create_last_word_mask(&m_cols);
+ }
+ }
+ else
+ {
+ // Needed because bitmap_init() does not set it to null on failure
+ m_cols.bitmap= 0;
+ }
+}
+#endif
+
+Rows_log_event::Rows_log_event(const char *buf, uint event_len,
+ Log_event_type event_type,
+ const Format_description_log_event
+ *description_event)
+ : Log_event(buf, description_event),
+ m_row_count(0),
+#ifndef MYSQL_CLIENT
+ m_table(NULL),
+#endif
+ m_table_id(0), m_rows_buf(0), m_rows_cur(0), m_rows_end(0)
+#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
+ , m_curr_row(NULL), m_curr_row_end(NULL), m_key(NULL)
+#endif
+{
+ DBUG_ENTER("Rows_log_event::Rows_log_event(const char*,...)");
+ uint8 const common_header_len= description_event->common_header_len;
+ uint8 const post_header_len= description_event->post_header_len[event_type-1];
+
+ DBUG_PRINT("enter",("event_len: %u common_header_len: %d "
+ "post_header_len: %d",
+ event_len, common_header_len,
+ post_header_len));
+
+ const char *post_start= buf + common_header_len;
+ post_start+= RW_MAPID_OFFSET;
+ if (post_header_len == 6)
+ {
+ /* Master is of an intermediate source tree before 5.1.4. Id is 4 bytes */
+ m_table_id= uint4korr(post_start);
+ post_start+= 4;
+ }
+ else
+ {
+ m_table_id= (ulong) uint6korr(post_start);
+ post_start+= RW_FLAGS_OFFSET;
+ }
+
+ m_flags= uint2korr(post_start);
+
+ uchar const *const var_start=
+ (const uchar *)buf + common_header_len + post_header_len;
+ uchar const *const ptr_width= var_start;
+ uchar *ptr_after_width= (uchar*) ptr_width;
+ DBUG_PRINT("debug", ("Reading from %p", ptr_after_width));
+ m_width = net_field_length(&ptr_after_width);
+ DBUG_PRINT("debug", ("m_width=%lu", m_width));
+ /* if bitmap_init fails, catched in is_valid() */
+ if (likely(!bitmap_init(&m_cols,
+ m_width <= sizeof(m_bitbuf)*8 ? m_bitbuf : NULL,
+ m_width,
+ false)))
+ {
+ DBUG_PRINT("debug", ("Reading from %p", ptr_after_width));
+ memcpy(m_cols.bitmap, ptr_after_width, (m_width + 7) / 8);
+ create_last_word_mask(&m_cols);
+ ptr_after_width+= (m_width + 7) / 8;
+ DBUG_DUMP("m_cols", (uchar*) m_cols.bitmap, no_bytes_in_map(&m_cols));
+ }
+ else
+ {
+ // Needed because bitmap_init() does not set it to null on failure
+ m_cols.bitmap= NULL;
+ DBUG_VOID_RETURN;
+ }
+
+ m_cols_ai.bitmap= m_cols.bitmap; /* See explanation in is_valid() */
+
+ if (event_type == UPDATE_ROWS_EVENT)
+ {
+ DBUG_PRINT("debug", ("Reading from %p", ptr_after_width));
+
+ /* if bitmap_init fails, caught in is_valid() */
+ if (likely(!bitmap_init(&m_cols_ai,
+ m_width <= sizeof(m_bitbuf_ai)*8 ? m_bitbuf_ai : NULL,
+ m_width,
+ false)))
+ {
+ DBUG_PRINT("debug", ("Reading from %p", ptr_after_width));
+ memcpy(m_cols_ai.bitmap, ptr_after_width, (m_width + 7) / 8);
+ create_last_word_mask(&m_cols_ai);
+ ptr_after_width+= (m_width + 7) / 8;
+ DBUG_DUMP("m_cols_ai", (uchar*) m_cols_ai.bitmap,
+ no_bytes_in_map(&m_cols_ai));
+ }
+ else
+ {
+ // Needed because bitmap_init() does not set it to null on failure
+ m_cols_ai.bitmap= 0;
+ DBUG_VOID_RETURN;
+ }
+ }
+
+ const uchar* const ptr_rows_data= (const uchar*) ptr_after_width;
+
+ size_t const data_size= event_len - (ptr_rows_data - (const uchar *) buf);
+ DBUG_PRINT("info",("m_table_id: %lu m_flags: %d m_width: %lu data_size: %lu",
+ m_table_id, m_flags, m_width, (ulong) data_size));
+
+ m_rows_buf= (uchar*) my_malloc(data_size, MYF(MY_WME));
+ if (likely((bool)m_rows_buf))
+ {
+#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
+ m_curr_row= m_rows_buf;
+#endif
+ m_rows_end= m_rows_buf + data_size;
+ m_rows_cur= m_rows_end;
+ memcpy(m_rows_buf, ptr_rows_data, data_size);
+ }
+ else
+ m_cols.bitmap= 0; // to not free it
+
+ DBUG_VOID_RETURN;
+}
+
+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));
+}
+
+int Rows_log_event::get_data_size()
+{
+ int const type_code= get_type_code();
+
+ uchar buf[sizeof(m_width)+1];
+ uchar *end= net_store_length(buf, (m_width + 7) / 8);
+
+ DBUG_EXECUTE_IF("old_row_based_repl_4_byte_map_id_master",
+ return 6 + no_bytes_in_map(&m_cols) + (end - buf) +
+ (type_code == UPDATE_ROWS_EVENT ? no_bytes_in_map(&m_cols_ai) : 0) +
+ (m_rows_cur - m_rows_buf););
+ int data_size= ROWS_HEADER_LEN;
+ data_size+= no_bytes_in_map(&m_cols);
+ data_size+= (uint) (end - buf);
+
+ if (type_code == UPDATE_ROWS_EVENT)
+ data_size+= no_bytes_in_map(&m_cols_ai);
+
+ data_size+= (uint) (m_rows_cur - m_rows_buf);
+ return data_size;
+}
+
+
+#ifndef MYSQL_CLIENT
+int Rows_log_event::do_add_row_data(uchar *row_data, size_t length)
+{
+ /*
+ When the table has a primary key, we would probably want, by default, to
+ log only the primary key value instead of the entire "before image". This
+ would save binlog space. TODO
+ */
+ DBUG_ENTER("Rows_log_event::do_add_row_data");
+ DBUG_PRINT("enter", ("row_data: 0x%lx length: %lu", (ulong) row_data,
+ (ulong) length));
+ /*
+ Don't print debug messages when running valgrind since they can
+ trigger false warnings.
+ */
+#ifndef HAVE_purify
+ DBUG_DUMP("row_data", row_data, min(length, 32));
+#endif
+
+ DBUG_ASSERT(m_rows_buf <= m_rows_cur);
+ DBUG_ASSERT(!m_rows_buf || m_rows_end && m_rows_buf < m_rows_end);
+ DBUG_ASSERT(m_rows_cur <= m_rows_end);
+
+ /* The cast will always work since m_rows_cur <= m_rows_end */
+ if (static_cast<size_t>(m_rows_end - m_rows_cur) <= length)
+ {
+ size_t const block_size= 1024;
+ my_ptrdiff_t const cur_size= m_rows_cur - m_rows_buf;
+ my_ptrdiff_t const new_alloc=
+ block_size * ((cur_size + length + block_size - 1) / block_size);
+
+ uchar* const new_buf= (uchar*)my_realloc((uchar*)m_rows_buf, (uint) new_alloc,
+ MYF(MY_ALLOW_ZERO_PTR|MY_WME));
+ if (unlikely(!new_buf))
+ DBUG_RETURN(HA_ERR_OUT_OF_MEM);
+
+ /* If the memory moved, we need to move the pointers */
+ if (new_buf != m_rows_buf)
+ {
+ m_rows_buf= new_buf;
+ m_rows_cur= m_rows_buf + cur_size;
+ }
+
+ /*
+ The end pointer should always be changed to point to the end of
+ the allocated memory.
+ */
+ m_rows_end= m_rows_buf + new_alloc;
+ }
+
+ DBUG_ASSERT(m_rows_cur + length <= m_rows_end);
+ memcpy(m_rows_cur, row_data, length);
+ m_rows_cur+= length;
+ m_row_count++;
+ DBUG_RETURN(0);
+}
+#endif
+
+#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
+int Rows_log_event::do_apply_event(Relay_log_info const *rli)
+{
+ DBUG_ENTER("Rows_log_event::do_apply_event(Relay_log_info*)");
+ int error= 0;
+ /*
+ If m_table_id == ~0UL, then we have a dummy event that does not
+ contain any data. In that case, we just remove all tables in the
+ tables_to_lock list, close the thread tables, and return with
+ success.
+ */
+ if (m_table_id == ~0UL)
+ {
+ /*
+ This one is supposed to be set: just an extra check so that
+ nothing strange has happened.
+ */
+ DBUG_ASSERT(get_flags(STMT_END_F));
+
+ const_cast<Relay_log_info*>(rli)->clear_tables_to_lock();
+ close_thread_tables(thd);
+ thd->clear_error();
+ DBUG_RETURN(0);
+ }
+
+ /*
+ 'thd' has been set by exec_relay_log_event(), just before calling
+ do_apply_event(). We still check here to prevent future coding
+ errors.
+ */
+ DBUG_ASSERT(rli->sql_thd == thd);
+
+ /*
+ If there is no locks taken, this is the first binrow event seen
+ after the table map events. We should then lock all the tables
+ used in the transaction and proceed with execution of the actual
+ event.
+ */
+ if (!thd->lock)
+ {
+ /*
+ Lock_tables() reads the contents of thd->lex, so they must be
+ initialized.
+
+ We also call the 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
+ we need to do any changes to that value after this function.
+ */
+ lex_start(thd);
+ mysql_reset_thd_for_next_command(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.
+ */
+ if (!thd->current_stmt_binlog_row_based &&
+ mysql_bin_log.is_open() && (thd->options & OPTION_BIN_LOG))
+ {
+ thd->set_current_stmt_binlog_row_based();
+ }
+
+
+ /*
+ There are a few flags that are replicated with each row event.
+ Make sure to set/clear them before executing the main body of
+ the event.
+ */
+ if (get_flags(NO_FOREIGN_KEY_CHECKS_F))
+ thd->options|= OPTION_NO_FOREIGN_KEY_CHECKS;
+ else
+ thd->options&= ~OPTION_NO_FOREIGN_KEY_CHECKS;
+
+ if (get_flags(RELAXED_UNIQUE_CHECKS_F))
+ thd->options|= OPTION_RELAXED_UNIQUE_CHECKS;
+ else
+ thd->options&= ~OPTION_RELAXED_UNIQUE_CHECKS;
+ /* A small test to verify that objects have consistent types */
+ DBUG_ASSERT(sizeof(thd->options) == sizeof(OPTION_RELAXED_UNIQUE_CHECKS));
+
+ if (simple_open_n_lock_tables(thd, rli->tables_to_lock))
+ {
+ uint actual_error= thd->main_da.sql_errno();
+ 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)
+ */
+ rli->report(ERROR_LEVEL, actual_error,
+ "Error '%s' on opening tables",
+ (actual_error ? thd->main_da.message() :
+ "unexpected success or fatal error"));
+ thd->is_slave_error= 1;
+ }
+ const_cast<Relay_log_info*>(rli)->clear_tables_to_lock();
+ DBUG_RETURN(actual_error);
+ }
+
+ /*
+ When the open and locking succeeded, we check all tables to
+ ensure that they still have the correct type.
+
+ We can use a down cast here since we know that every table added
+ to the tables_to_lock is a RPL_TABLE_LIST.
+ */
+
+ {
+ RPL_TABLE_LIST *ptr= rli->tables_to_lock;
+ for ( ; ptr ; ptr= static_cast<RPL_TABLE_LIST*>(ptr->next_global))
+ {
+ if (ptr->m_tabledef.compatible_with(rli, ptr->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();
+ DBUG_RETURN(ERR_BAD_TABLE_DEF);
+ }
+ }
+ }
+
+ /*
+ ... and then we add all the tables to the table map and remove
+ them from tables to lock.
+
+ We also invalidate the query cache for all the tables, since
+ they will now be changed.
+
+ TODO [/Matz]: Maybe the query cache should not be invalidated
+ here? It might be that a table is not changed, even though it
+ was locked for the statement. We do know that each
+ Rows_log_event contain at least one row, so after processing one
+ Rows_log_event, we can invalidate the query cache for the
+ associated table.
+ */
+ for (TABLE_LIST *ptr= rli->tables_to_lock ; ptr ; ptr= ptr->next_global)
+ {
+ 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(rli->tables_to_lock);
+#endif
+ }
+
+ TABLE*
+ table=
+ m_table= const_cast<Relay_log_info*>(rli)->m_table_map.get_table(m_table_id);
+
+ DBUG_PRINT("debug", ("m_table: 0x%lx, m_table_id: %lu", (ulong) m_table, m_table_id));
+
+ if (table)
+ {
+ /*
+ table == NULL means that this table should not be replicated
+ (this was set up by Table_map_log_event::do_apply_event()
+ which tested replicate-* rules).
+ */
+
+ /*
+ It's not needed to set_time() but
+ 1) it continues the property that "Time" in SHOW PROCESSLIST shows how
+ much slave is behind
+ 2) it will be needed when we allow replication from a table with no
+ TIMESTAMP column to a table with one.
+ So we call set_time(), like in SBR. Presently it changes nothing.
+ */
+ thd->set_time((time_t)when);
+
+ /*
+ Now we are in a statement and will stay in a statement until we
+ see a STMT_END_F.
+
+ We set this flag here, before actually applying any rows, in
+ case the SQL thread is stopped and we need to detect that we're
+ inside a statement and halting abruptly might cause problems
+ when restarting.
+ */
+ const_cast<Relay_log_info*>(rli)->set_flag(Relay_log_info::IN_STMT);
+
+ if ( m_width == table->s->fields && bitmap_is_set_all(&m_cols))
+ set_flags(COMPLETE_ROWS_F);
+
+ /*
+ Set tables write and read sets.
+
+ Read_set contains all slave columns (in case we are going to fetch
+ a complete record from slave)
+
+ Write_set equals the m_cols bitmap sent from master but it can be
+ longer if slave has extra columns.
+ */
+
+ DBUG_PRINT_BITSET("debug", "Setting table's write_set from: %s", &m_cols);
+
+ bitmap_set_all(table->read_set);
+ bitmap_set_all(table->write_set);
+ if (!get_flags(COMPLETE_ROWS_F))
+ bitmap_intersect(table->write_set,&m_cols);
+
+ this->slave_exec_mode= slave_exec_mode_options; // fix the mode
+
+ // Do event specific preparations
+ error= do_before_row_operations(rli);
+
+ // row processing loop
+
+ while (error == 0 && m_curr_row < m_rows_end)
+ {
+ /* in_use can have been set to NULL in close_tables_for_reopen */
+ THD* old_thd= table->in_use;
+ if (!table->in_use)
+ table->in_use= thd;
+
+ error= do_exec_row(rli);
+
+ DBUG_PRINT("info", ("error: %s", HA_ERR(error)));
+ DBUG_ASSERT(error != HA_ERR_RECORD_DELETED);
+
+ table->in_use = old_thd;
+ switch (error)
+ {
+ case 0:
+ break;
+ /*
+ The following list of "idempotent" errors
+ means that an error from the list might happen
+ because of idempotent (more than once)
+ applying of a binlog file.
+ Notice, that binlog has a ddl operation its
+ second applying may cause
+
+ case HA_ERR_TABLE_DEF_CHANGED:
+ case HA_ERR_CANNOT_ADD_FOREIGN:
+
+ which are not included into to the list.
+
+ Note that HA_ERR_RECORD_DELETED is not in the list since
+ do_exec_row() should not return that error code.
+ */
+ case HA_ERR_RECORD_CHANGED:
+ case HA_ERR_KEY_NOT_FOUND:
+ case HA_ERR_END_OF_FILE:
+ case HA_ERR_FOUND_DUPP_KEY:
+ case HA_ERR_FOUND_DUPP_UNIQUE:
+ case HA_ERR_FOREIGN_DUPLICATE_KEY:
+ case HA_ERR_NO_REFERENCED_ROW:
+ case HA_ERR_ROW_IS_REFERENCED:
+
+ if (bit_is_set(slave_exec_mode, SLAVE_EXEC_MODE_IDEMPOTENT) == 1)
+ {
+ if (global_system_variables.log_warnings)
+ slave_rows_error_report(WARNING_LEVEL, error, rli, thd, table,
+ get_type_str(),
+ RPL_LOG_NAME, (ulong) log_pos);
+ error= 0;
+ }
+ break;
+
+ default:
+ thd->is_slave_error= 1;
+ break;
+ }
+
+ /*
+ If m_curr_row_end was not set during event execution (e.g., because
+ of errors) we can't proceed to the next row. If the error is transient
+ (i.e., error==0 at this point) we must call unpack_current_row() to set
+ m_curr_row_end.
+ */
+
+ DBUG_PRINT("info", ("curr_row: 0x%lu; curr_row_end: 0x%lu; rows_end: 0x%lu",
+ (ulong) m_curr_row, (ulong) m_curr_row_end, (ulong) m_rows_end));
+
+ if (!m_curr_row_end && !error)
+ unpack_current_row(rli);
+
+ // at this moment m_curr_row_end should be set
+ DBUG_ASSERT(error || m_curr_row_end != NULL);
+ DBUG_ASSERT(error || m_curr_row < m_curr_row_end);
+ DBUG_ASSERT(error || m_curr_row_end <= m_rows_end);
+
+ m_curr_row= m_curr_row_end;
+
+ } // row processing loop
+
+ DBUG_EXECUTE_IF("STOP_SLAVE_after_first_Rows_event",
+ const_cast<Relay_log_info*>(rli)->abort_slave= 1;);
+ error= do_after_row_operations(rli, error);
+ if (!cache_stmt)
+ {
+ DBUG_PRINT("info", ("Marked that we need to keep log"));
+ thd->options|= OPTION_KEEP_LOG;
+ }
+ } // 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 */
+ slave_rows_error_report(ERROR_LEVEL, error, rli, thd, table,
+ get_type_str(), RPL_LOG_NAME, (ulong) log_pos);
+ }
+ if (error)
+ {
+ /*
+ If one day we honour --skip-slave-errors in row-based replication, and
+ the error should be skipped, then we would clear mappings, rollback,
+ close tables, but the slave SQL thread would not stop and then may
+ assume the mapping is still available, the tables are still open...
+ So then we should clear mappings/rollback/close here only if this is a
+ STMT_END_F.
+ For now we code, knowing that error is not skippable and so slave SQL
+ thread is certainly going to stop.
+ rollback at the caller along with sbr.
+ */
+ thd->reset_current_stmt_binlog_row_based();
+ const_cast<Relay_log_info*>(rli)->cleanup_context(thd, error);
+ 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) &&
+ !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.
+ */
+ const_cast<Relay_log_info*>(rli)->last_event_start_time= my_time(0);
+ }
+
+ DBUG_RETURN(0);
+}
+
+Log_event::enum_skip_reason
+Rows_log_event::do_shall_skip(Relay_log_info *rli)
+{
+ /*
+ If the slave skip counter is 1 and this event does not end a
+ statement, then we should not start executing on the next event.
+ Otherwise, we defer the decision to the normal skipping logic.
+ */
+ if (rli->slave_skip_counter == 1 && !get_flags(STMT_END_F))
+ return Log_event::EVENT_SKIP_IGNORE;
+ else
+ return Log_event::do_shall_skip(rli);
+}
+
+/**
+ The function is called at Rows_log_event statement commit time,
+ normally from Rows_log_event::do_update_pos() and possibly from
+ Query_log_event::do_apply_event() of the COMMIT.
+ The function commits the last statement for engines, binlog and
+ releases resources have been allocated for the statement.
+
+ @retval 0 Ok.
+ @retval non-zero Error at the commit.
+ */
+
+static int rows_event_stmt_cleanup(Relay_log_info const *rli, THD * thd)
+{
+ int error;
+ {
+ /*
+ This is the end of a statement or transaction, so close (and
+ unlock) the tables we opened when processing the
+ Table_map_log_event starting the statement.
+
+ OBSERVER. This will clear *all* mappings, not only those that
+ are open for the table. There is not good handle for on-close
+ actions for tables.
+
+ NOTE. Even if we have no table ('table' == 0) we still need to be
+ here, so that we increase the group relay log position. If we didn't, we
+ could have a group relay log position which lags behind "forever"
+ (assume the last master's transaction is ignored by the slave because of
+ replicate-ignore rules).
+ */
+ thd->binlog_flush_pending_rows_event(true);
+
+ /*
+ If this event is not in a transaction, the call below will, if some
+ transactional storage engines are involved, commit the statement into
+ them and flush the pending event to binlog.
+ If this event is in a transaction, the call will do nothing, but a
+ Xid_log_event will come next which will, if some transactional engines
+ are involved, commit the transaction and flush the pending event to the
+ binlog.
+ */
+ error= ha_autocommit_or_rollback(thd, 0);
+
+ /*
+ Now what if this is not a transactional engine? we still need to
+ flush the pending event to the binlog; we did it with
+ thd->binlog_flush_pending_rows_event(). Note that we imitate
+ what is done for real queries: a call to
+ ha_autocommit_or_rollback() (sometimes only if involves a
+ transactional engine), and a call to be sure to have the pending
+ event flushed.
+ */
+
+ thd->reset_current_stmt_binlog_row_based();
+
+ const_cast<Relay_log_info*>(rli)->cleanup_context(thd, 0);
+ }
+ return error;
+}
+
+/**
+ The method either increments the relay log position or
+ commits the current statement and increments the master group
+ possition if the event is STMT_END_F flagged and
+ the statement corresponds to the autocommit query (i.e replicated
+ without wrapping in BEGIN/COMMIT)
+
+ @retval 0 Success
+ @retval non-zero Error in the statement commit
+ */
+int
+Rows_log_event::do_update_pos(Relay_log_info *rli)
+{
+ DBUG_ENTER("Rows_log_event::do_update_pos");
+ int error= 0;
+
+ DBUG_PRINT("info", ("flags: %s",
+ get_flags(STMT_END_F) ? "STMT_END_F " : ""));
+
+ if (get_flags(STMT_END_F))
+ {
+ if ((error= rows_event_stmt_cleanup(rli, thd)) == 0)
+ {
+ /*
+ Indicate that a statement is finished.
+ Step the group log position if we are not in a transaction,
+ otherwise increase the event log position.
+ */
+ rli->stmt_done(log_pos, when);
+
+ /*
+ Clear any errors pushed in thd->net.last_err* if for example "no key
+ found" (as this is allowed). This is a safety measure; apparently
+ those errors (e.g. when executing a Delete_rows_log_event of a
+ non-existing row, like in rpl_row_mystery22.test,
+ thd->net.last_error = "Can't find record in 't1'" and last_errno=1032)
+ do not become visible. We still prefer to wipe them out.
+ */
+ thd->clear_error();
+ }
+ else
+ {
+ rli->report(ERROR_LEVEL, error,
+ "Error in %s event: commit of row events failed, "
+ "table `%s`.`%s`",
+ get_type_str(), m_table->s->db.str,
+ m_table->s->table_name.str);
+ }
+ }
+ else
+ {
+ rli->inc_event_relay_log_pos();
+ }
+
+ DBUG_RETURN(error);
+}
+
+#endif /* !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) */
+
+#ifndef MYSQL_CLIENT
+bool Rows_log_event::write_data_header(IO_CACHE *file)
+{
+ uchar buf[ROWS_HEADER_LEN]; // No need to init the buffer
+ DBUG_ASSERT(m_table_id != ~0UL);
+ DBUG_EXECUTE_IF("old_row_based_repl_4_byte_map_id_master",
+ {
+ int4store(buf + 0, m_table_id);
+ int2store(buf + 4, m_flags);
+ return (my_b_safe_write(file, buf, 6));
+ });
+ int6store(buf + RW_MAPID_OFFSET, (ulonglong)m_table_id);
+ int2store(buf + RW_FLAGS_OFFSET, m_flags);
+ return (my_b_safe_write(file, buf, ROWS_HEADER_LEN));
+}
+
+bool Rows_log_event::write_data_body(IO_CACHE*file)
+{
+ /*
+ Note that this should be the number of *bits*, not the number of
+ bytes.
+ */
+ uchar sbuf[sizeof(m_width)];
+ my_ptrdiff_t const data_size= m_rows_cur - m_rows_buf;
+ bool res= false;
+ uchar *const sbuf_end= net_store_length(sbuf, (size_t) m_width);
+ DBUG_ASSERT(static_cast<size_t>(sbuf_end - sbuf) <= sizeof(sbuf));
+
+ DBUG_DUMP("m_width", sbuf, (size_t) (sbuf_end - sbuf));
+ res= res || my_b_safe_write(file, sbuf, (size_t) (sbuf_end - sbuf));
+
+ DBUG_DUMP("m_cols", (uchar*) m_cols.bitmap, no_bytes_in_map(&m_cols));
+ res= res || my_b_safe_write(file, (uchar*) m_cols.bitmap,
+ no_bytes_in_map(&m_cols));
+ /*
+ TODO[refactor write]: Remove the "down cast" here (and elsewhere).
+ */
+ if (get_type_code() == UPDATE_ROWS_EVENT)
+ {
+ DBUG_DUMP("m_cols_ai", (uchar*) m_cols_ai.bitmap,
+ no_bytes_in_map(&m_cols_ai));
+ res= res || my_b_safe_write(file, (uchar*) m_cols_ai.bitmap,
+ no_bytes_in_map(&m_cols_ai));
+ }
+ DBUG_DUMP("rows", m_rows_buf, data_size);
+ res= res || my_b_safe_write(file, m_rows_buf, (size_t) data_size);
+
+ return res;
+
+}
+#endif
+
+#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
+void Rows_log_event::pack_info(Protocol *protocol)
+{
+ char buf[256];
+ char const *const flagstr=
+ get_flags(STMT_END_F) ? " flags: STMT_END_F" : "";
+ size_t bytes= my_snprintf(buf, sizeof(buf),
+ "table_id: %lu%s", m_table_id, flagstr);
+ protocol->store(buf, bytes, &my_charset_bin);
+}
+#endif
+
+#ifdef MYSQL_CLIENT
+void Rows_log_event::print_helper(FILE *file,
+ PRINT_EVENT_INFO *print_event_info,
+ char const *const name)
+{
+ IO_CACHE *const head= &print_event_info->head_cache;
+ IO_CACHE *const body= &print_event_info->body_cache;
+ if (!print_event_info->short_form)
+ {
+ bool const last_stmt_event= get_flags(STMT_END_F);
+ print_header(head, print_event_info, !last_stmt_event);
+ my_b_printf(head, "\t%s: table id %lu%s\n",
+ name, m_table_id,
+ last_stmt_event ? " flags: STMT_END_F" : "");
+ print_base64(body, print_event_info, !last_stmt_event);
+ }
+
+ if (get_flags(STMT_END_F))
+ {
+ copy_event_cache_to_file_and_reinit(head, file);
+ copy_event_cache_to_file_and_reinit(body, file);
+ }
+}
+#endif
+
+/**************************************************************************
+ Table_map_log_event member functions and support functions
+**************************************************************************/
+
+/**
+ @page How replication of field metadata works.
+
+ When a table map is created, the master first calls
+ Table_map_log_event::save_field_metadata() which calculates how many
+ values will be in the field metadata. Only those fields that require the
+ extra data are added. The method also loops through all of the fields in
+ the table calling the method Field::save_field_metadata() which returns the
+ values for the field that will be saved in the metadata and replicated to
+ the slave. Once all fields have been processed, the table map is written to
+ the binlog adding the size of the field metadata and the field metadata to
+ the end of the body of the table map.
+
+ When a table map is read on the slave, the field metadata is read from the
+ table map and passed to the table_def class constructor which saves the
+ field metadata from the table map into an array based on the type of the
+ field. Field metadata values not present (those fields that do not use extra
+ data) in the table map are initialized as zero (0). The array size is the
+ same as the columns for the table on the slave.
+
+ Additionally, values saved for field metadata on the master are saved as a
+ string of bytes (uchar) in the binlog. A field may require 1 or more bytes
+ to store the information. In cases where values require multiple bytes
+ (e.g. values > 255), the endian-safe methods are used to properly encode
+ the values on the master and decode them on the slave. When the field
+ metadata values are captured on the slave, they are stored in an array of
+ type uint16. This allows the least number of casts to prevent casting bugs
+ when the field metadata is used in comparisons of field attributes. When
+ the field metadata is used for calculating addresses in pointer math, the
+ type used is uint32.
+*/
+
+#if !defined(MYSQL_CLIENT)
+/**
+ Save the field metadata based on the real_type of the field.
+ The metadata saved depends on the type of the field. Some fields
+ store a single byte for pack_length() while others store two bytes
+ for field_length (max length).
+
+ @retval 0 Ok.
+
+ @todo
+ We may want to consider changing the encoding of the information.
+ Currently, the code attempts to minimize the number of bytes written to
+ the tablemap. There are at least two other alternatives; 1) using
+ net_store_length() to store the data allowing it to choose the number of
+ bytes that are appropriate thereby making the code much easier to
+ maintain (only 1 place to change the encoding), or 2) use a fixed number
+ of bytes for each field. The problem with option 1 is that net_store_length()
+ will use one byte if the value < 251, but 3 bytes if it is > 250. Thus,
+ for fields like CHAR which can be no larger than 255 characters, the method
+ will use 3 bytes when the value is > 250. Further, every value that is
+ encoded using 2 parts (e.g., pack_length, field_length) will be numerically
+ > 250 therefore will use 3 bytes for eah value. The problem with option 2
+ is less wasteful for space but does waste 1 byte for every field that does
+ not encode 2 parts.
+*/
+int Table_map_log_event::save_field_metadata()
+{
+ DBUG_ENTER("Table_map_log_event::save_field_metadata");
+ int index= 0;
+ for (unsigned int i= 0 ; i < m_table->s->fields ; i++)
+ index+= m_table->s->field[i]->save_field_metadata(&m_field_metadata[index]);
+ DBUG_RETURN(index);
+}
+#endif /* !defined(MYSQL_CLIENT) */
+
+/*
+ Constructor used to build an event for writing to the binary log.
+ Mats says tbl->s lives longer than this event so it's ok to copy pointers
+ (tbl->s->db etc) and not pointer content.
+ */
+#if !defined(MYSQL_CLIENT)
+Table_map_log_event::Table_map_log_event(THD *thd, TABLE *tbl, ulong tid,
+ bool is_transactional, uint16 flags)
+ : Log_event(thd, 0, true),
+ m_table(tbl),
+ m_dbnam(tbl->s->db.str),
+ m_dblen(m_dbnam ? tbl->s->db.length : 0),
+ m_tblnam(tbl->s->table_name.str),
+ m_tbllen(tbl->s->table_name.length),
+ m_colcnt(tbl->s->fields),
+ m_memory(NULL),
+ m_table_id(tid),
+ m_flags(flags),
+ m_data_size(0),
+ m_field_metadata(0),
+ m_field_metadata_size(0),
+ m_null_bits(0),
+ m_meta_memory(NULL)
+{
+ DBUG_ASSERT(m_table_id != ~0UL);
+ /*
+ In TABLE_SHARE, "db" and "table_name" are 0-terminated (see this comment in
+ table.cc / alloc_table_share():
+ Use the fact the key is db/0/table_name/0
+ As we rely on this let's assert it.
+ */
+ DBUG_ASSERT((tbl->s->db.str == 0) ||
+ (tbl->s->db.str[tbl->s->db.length] == 0));
+ DBUG_ASSERT(tbl->s->table_name.str[tbl->s->table_name.length] == 0);
+
+
+ m_data_size= TABLE_MAP_HEADER_LEN;
+ DBUG_EXECUTE_IF("old_row_based_repl_4_byte_map_id_master", m_data_size= 6;);
+ m_data_size+= m_dblen + 2; // Include length and terminating \0
+ m_data_size+= m_tbllen + 2; // Include length and terminating \0
+ m_data_size+= 1 + m_colcnt; // COLCNT and column types
+
+ /* If malloc fails, caught in is_valid() */
+ if ((m_memory= (uchar*) my_malloc(m_colcnt, MYF(MY_WME))))
+ {
+ m_coltype= reinterpret_cast<uchar*>(m_memory);
+ for (unsigned int i= 0 ; i < m_table->s->fields ; ++i)
+ m_coltype[i]= m_table->field[i]->type();
+ }
+
+ /*
+ Calculate a bitmap for the results of maybe_null() for all columns.
+ The bitmap is used to determine when there is a column from the master
+ that is not on the slave and is null and thus not in the row data during
+ replication.
+ */
+ uint num_null_bytes= (m_table->s->fields + 7) / 8;
+ m_data_size+= num_null_bytes;
+ m_meta_memory= (uchar *)my_multi_malloc(MYF(MY_WME),
+ &m_null_bits, num_null_bytes,
+ &m_field_metadata, (m_colcnt * 2),
+ NULL);
+
+ bzero(m_field_metadata, (m_colcnt * 2));
+
+ /*
+ Create an array for the field metadata and store it.
+ */
+ m_field_metadata_size= save_field_metadata();
+ DBUG_ASSERT(m_field_metadata_size <= (m_colcnt * 2));
+
+ /*
+ Now set the size of the data to the size of the field metadata array
+ plus one or two bytes for number of elements in the field metadata array.
+ */
+ if (m_field_metadata_size > 255)
+ m_data_size+= m_field_metadata_size + 2;
+ else
+ m_data_size+= m_field_metadata_size + 1;
+
+ bzero(m_null_bits, num_null_bytes);
+ for (unsigned int i= 0 ; i < m_table->s->fields ; ++i)
+ if (m_table->field[i]->maybe_null())
+ m_null_bits[(i / 8)]+= 1 << (i % 8);
+
+}
+#endif /* !defined(MYSQL_CLIENT) */
+
+/*
+ Constructor used by slave to read the event from the binary log.
+ */
+#if defined(HAVE_REPLICATION)
+Table_map_log_event::Table_map_log_event(const char *buf, uint event_len,
+ const Format_description_log_event
+ *description_event)
+
+ : Log_event(buf, description_event),
+#ifndef MYSQL_CLIENT
+ m_table(NULL),
+#endif
+ m_dbnam(NULL), m_dblen(0), m_tblnam(NULL), m_tbllen(0),
+ m_colcnt(0), m_coltype(0),
+ m_memory(NULL), m_table_id(ULONG_MAX), m_flags(0),
+ m_data_size(0), m_field_metadata(0), m_field_metadata_size(0),
+ m_null_bits(0), m_meta_memory(NULL)
+{
+ unsigned int bytes_read= 0;
+ DBUG_ENTER("Table_map_log_event::Table_map_log_event(const char*,uint,...)");
+
+ uint8 common_header_len= description_event->common_header_len;
+ uint8 post_header_len= description_event->post_header_len[TABLE_MAP_EVENT-1];
+ DBUG_PRINT("info",("event_len: %u common_header_len: %d post_header_len: %d",
+ event_len, common_header_len, post_header_len));
+
+ /*
+ Don't print debug messages when running valgrind since they can
+ trigger false warnings.
+ */
+#ifndef HAVE_purify
+ DBUG_DUMP("event buffer", (uchar*) buf, event_len);
+#endif
+
+ /* Read the post-header */
+ const char *post_start= buf + common_header_len;
+
+ post_start+= TM_MAPID_OFFSET;
+ if (post_header_len == 6)
+ {
+ /* Master is of an intermediate source tree before 5.1.4. Id is 4 bytes */
+ m_table_id= uint4korr(post_start);
+ post_start+= 4;
+ }
+ else
+ {
+ DBUG_ASSERT(post_header_len == TABLE_MAP_HEADER_LEN);
+ m_table_id= (ulong) uint6korr(post_start);
+ post_start+= TM_FLAGS_OFFSET;
+ }
+
+ DBUG_ASSERT(m_table_id != ~0UL);
+
+ m_flags= uint2korr(post_start);
+
+ /* Read the variable part of the event */
+ const char *const vpart= buf + common_header_len + post_header_len;
+
+ /* Extract the length of the various parts from the buffer */
+ uchar const *const ptr_dblen= (uchar const*)vpart + 0;
+ m_dblen= *(uchar*) ptr_dblen;
+
+ /* Length of database name + counter + terminating null */
+ uchar const *const ptr_tbllen= ptr_dblen + m_dblen + 2;
+ m_tbllen= *(uchar*) ptr_tbllen;
+
+ /* Length of table name + counter + terminating null */
+ uchar const *const ptr_colcnt= ptr_tbllen + m_tbllen + 2;
+ uchar *ptr_after_colcnt= (uchar*) ptr_colcnt;
+ m_colcnt= net_field_length(&ptr_after_colcnt);
+
+ DBUG_PRINT("info",("m_dblen: %lu off: %ld m_tbllen: %lu off: %ld m_colcnt: %lu off: %ld",
+ (ulong) m_dblen, (long) (ptr_dblen-(const uchar*)vpart),
+ (ulong) m_tbllen, (long) (ptr_tbllen-(const uchar*)vpart),
+ m_colcnt, (long) (ptr_colcnt-(const uchar*)vpart)));
+
+ /* Allocate mem for all fields in one go. If fails, caught in is_valid() */
+ m_memory= (uchar*) my_multi_malloc(MYF(MY_WME),
+ &m_dbnam, (uint) m_dblen + 1,
+ &m_tblnam, (uint) m_tbllen + 1,
+ &m_coltype, (uint) m_colcnt,
+ NullS);
+
+ if (m_memory)
+ {
+ /* Copy the different parts into their memory */
+ strncpy(const_cast<char*>(m_dbnam), (const char*)ptr_dblen + 1, m_dblen + 1);
+ strncpy(const_cast<char*>(m_tblnam), (const char*)ptr_tbllen + 1, m_tbllen + 1);
+ memcpy(m_coltype, ptr_after_colcnt, m_colcnt);
+
+ ptr_after_colcnt= ptr_after_colcnt + m_colcnt;
+ bytes_read= (uint) (ptr_after_colcnt - (uchar *)buf);
+ DBUG_PRINT("info", ("Bytes read: %d.\n", bytes_read));
+ if (bytes_read < event_len)
+ {
+ m_field_metadata_size= net_field_length(&ptr_after_colcnt);
+ DBUG_ASSERT(m_field_metadata_size <= (m_colcnt * 2));
+ uint num_null_bytes= (m_colcnt + 7) / 8;
+ m_meta_memory= (uchar *)my_multi_malloc(MYF(MY_WME),
+ &m_null_bits, num_null_bytes,
+ &m_field_metadata, m_field_metadata_size,
+ NULL);
+ memcpy(m_field_metadata, ptr_after_colcnt, m_field_metadata_size);
+ ptr_after_colcnt= (uchar*)ptr_after_colcnt + m_field_metadata_size;
+ memcpy(m_null_bits, ptr_after_colcnt, num_null_bytes);
+ }
+ }
+
+ DBUG_VOID_RETURN;
+}
+#endif
+
+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));
+}
+
+/*
+ Return value is an error code, one of:
+
+ -1 Failure to open table [from open_tables()]
+ 0 Success
+ 1 No room for more tables [from set_table()]
+ 2 Out of memory [from set_table()]
+ 3 Wrong table definition
+ 4 Daisy-chaining RBR with SBR not possible
+ */
+
+#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
+int Table_map_log_event::do_apply_event(Relay_log_info const *rli)
+{
+ RPL_TABLE_LIST *table_list;
+ char *db_mem, *tname_mem;
+ size_t dummy_len;
+ void *memory;
+ DBUG_ENTER("Table_map_log_event::do_apply_event(Relay_log_info*)");
+ DBUG_ASSERT(rli->sql_thd == thd);
+
+ /* Step the query id to mark what columns that are actually used. */
+ pthread_mutex_lock(&LOCK_thread_count);
+ thd->query_id= next_query_id();
+ pthread_mutex_unlock(&LOCK_thread_count);
+
+ if (!(memory= my_multi_malloc(MYF(MY_WME),
+ &table_list, (uint) sizeof(RPL_TABLE_LIST),
+ &db_mem, (uint) NAME_LEN + 1,
+ &tname_mem, (uint) NAME_LEN + 1,
+ NullS)))
+ DBUG_RETURN(HA_ERR_OUT_OF_MEM);
+
+ 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;
+ table_list->table_id= m_table_id;
+ table_list->updating= 1;
+ strmov(table_list->db, rpl_filter->get_rewrite_db(m_dbnam, &dummy_len));
+ strmov(table_list->table_name, m_tblnam);
+
+ int error= 0;
+
+ if (rli->sql_thd->slave_thread /* filtering is for slave only */ &&
+ (!rpl_filter->db_ok(table_list->db) ||
+ (rpl_filter->is_on() && !rpl_filter->tables_ok("", table_list))))
+ {
+ my_free(memory, MYF(MY_WME));
+ }
+ else
+ {
+ DBUG_ASSERT(thd->lex->query_tables != table_list);
+
+ /*
+ Use placement new to construct the table_def instance in the
+ memory allocated for it inside table_list.
+
+ The memory allocated by the table_def structure (i.e., not the
+ memory allocated *for* the table_def structure) is released
+ inside Relay_log_info::clear_tables_to_lock() by calling the
+ table_def destructor explicitly.
+ */
+ new (&table_list->m_tabledef) table_def(m_coltype, m_colcnt,
+ m_field_metadata, m_field_metadata_size, m_null_bits);
+ table_list->m_tabledef_valid= TRUE;
+
+ /*
+ We record in the slave's information that the table should be
+ locked by linking the table into the list of tables to lock.
+ */
+ table_list->next_global= table_list->next_local= rli->tables_to_lock;
+ const_cast<Relay_log_info*>(rli)->tables_to_lock= table_list;
+ const_cast<Relay_log_info*>(rli)->tables_to_lock_count++;
+ /* 'memory' is freed in clear_tables_to_lock */
+ }
+
+ DBUG_RETURN(error);
+}
+
+Log_event::enum_skip_reason
+Table_map_log_event::do_shall_skip(Relay_log_info *rli)
+{
+ /*
+ If the slave skip counter is 1, then we should not start executing
+ on the next event.
+ */
+ return continue_group(rli);
+}
+
+int Table_map_log_event::do_update_pos(Relay_log_info *rli)
+{
+ rli->inc_event_relay_log_pos();
+ return 0;
+}
+
+#endif /* !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) */
+
+#ifndef MYSQL_CLIENT
+bool Table_map_log_event::write_data_header(IO_CACHE *file)
+{
+ DBUG_ASSERT(m_table_id != ~0UL);
+ uchar buf[TABLE_MAP_HEADER_LEN];
+ DBUG_EXECUTE_IF("old_row_based_repl_4_byte_map_id_master",
+ {
+ int4store(buf + 0, m_table_id);
+ int2store(buf + 4, m_flags);
+ return (my_b_safe_write(file, buf, 6));
+ });
+ int6store(buf + TM_MAPID_OFFSET, (ulonglong)m_table_id);
+ int2store(buf + TM_FLAGS_OFFSET, m_flags);
+ return (my_b_safe_write(file, buf, TABLE_MAP_HEADER_LEN));
+}
+
+bool Table_map_log_event::write_data_body(IO_CACHE *file)
+{
+ DBUG_ASSERT(m_dbnam != NULL);
+ DBUG_ASSERT(m_tblnam != NULL);
+ /* We use only one byte per length for storage in event: */
+ DBUG_ASSERT(m_dblen < 128);
+ DBUG_ASSERT(m_tbllen < 128);
+
+ uchar const dbuf[]= { (uchar) m_dblen };
+ uchar const tbuf[]= { (uchar) m_tbllen };
+
+ uchar cbuf[sizeof(m_colcnt)];
+ uchar *const cbuf_end= net_store_length(cbuf, (size_t) m_colcnt);
+ DBUG_ASSERT(static_cast<size_t>(cbuf_end - cbuf) <= sizeof(cbuf));
+
+ /*
+ Store the size of the field metadata.
+ */
+ uchar mbuf[sizeof(m_field_metadata_size)];
+ uchar *const mbuf_end= net_store_length(mbuf, m_field_metadata_size);
+
+ return (my_b_safe_write(file, dbuf, sizeof(dbuf)) ||
+ my_b_safe_write(file, (const uchar*)m_dbnam, m_dblen+1) ||
+ my_b_safe_write(file, tbuf, sizeof(tbuf)) ||
+ my_b_safe_write(file, (const uchar*)m_tblnam, m_tbllen+1) ||
+ my_b_safe_write(file, cbuf, (size_t) (cbuf_end - cbuf)) ||
+ my_b_safe_write(file, m_coltype, m_colcnt) ||
+ my_b_safe_write(file, mbuf, (size_t) (mbuf_end - mbuf)) ||
+ my_b_safe_write(file, m_field_metadata, m_field_metadata_size),
+ my_b_safe_write(file, m_null_bits, (m_colcnt + 7) / 8));
+ }
+#endif
+
+#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
+
+/*
+ Print some useful information for the SHOW BINARY LOG information
+ field.
+ */
+
+#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
+void Table_map_log_event::pack_info(Protocol *protocol)
+{
+ char buf[256];
+ size_t bytes= my_snprintf(buf, sizeof(buf),
+ "table_id: %lu (%s.%s)",
+ m_table_id, m_dbnam, m_tblnam);
+ protocol->store(buf, bytes, &my_charset_bin);
+}
+#endif
+
+
+#endif
+
+
+#ifdef MYSQL_CLIENT
+void Table_map_log_event::print(FILE *file, PRINT_EVENT_INFO *print_event_info)
+{
+ if (!print_event_info->short_form)
+ {
+ print_header(&print_event_info->head_cache, print_event_info, TRUE);
+ my_b_printf(&print_event_info->head_cache,
+ "\tTable_map: `%s`.`%s` mapped to number %lu\n",
+ m_dbnam, m_tblnam, m_table_id);
+ print_base64(&print_event_info->body_cache, print_event_info, TRUE);
+ }
+}
+#endif
+
+/**************************************************************************
+ Write_rows_log_event member functions
+**************************************************************************/
+
+/*
+ Constructor used to build an event for writing to the binary log.
+ */
+#if !defined(MYSQL_CLIENT)
+Write_rows_log_event::Write_rows_log_event(THD *thd_arg, TABLE *tbl_arg,
+ ulong tid_arg,
+ MY_BITMAP const *cols,
+ bool is_transactional)
+ : Rows_log_event(thd_arg, tbl_arg, tid_arg, cols, is_transactional)
+{
+}
+#endif
+
+/*
+ Constructor used by slave to read the event from the binary log.
+ */
+#ifdef HAVE_REPLICATION
+Write_rows_log_event::Write_rows_log_event(const char *buf, uint event_len,
+ const Format_description_log_event
+ *description_event)
+: Rows_log_event(buf, event_len, WRITE_ROWS_EVENT, description_event)
+{
+}
+#endif
+
+#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
+int
+Write_rows_log_event::do_before_row_operations(const Slave_reporting_capability *const)
+{
+ int error= 0;
+
+ /**
+ todo: to introduce a property for the event (handler?) which forces
+ applying the event in the replace (idempotent) fashion.
+ */
+ if (bit_is_set(slave_exec_mode, SLAVE_EXEC_MODE_IDEMPOTENT) == 1 ||
+ m_table->s->db_type()->db_type == DB_TYPE_NDBCLUSTER)
+ {
+ /*
+ We are using REPLACE semantics and not INSERT IGNORE semantics
+ when writing rows, that is: new rows replace old rows. We need to
+ inform the storage engine that it should use this behaviour.
+ */
+
+ /* Tell the storage engine that we are using REPLACE semantics. */
+ thd->lex->duplicates= DUP_REPLACE;
+
+ /*
+ Pretend we're executing a REPLACE command: this is needed for
+ InnoDB and NDB Cluster since they are not (properly) checking the
+ lex->duplicates flag.
+ */
+ thd->lex->sql_command= SQLCOM_REPLACE;
+ /*
+ Do not raise the error flag in case of hitting to an unique attribute
+ */
+ m_table->file->extra(HA_EXTRA_IGNORE_DUP_KEY);
+ /*
+ NDB specific: update from ndb master wrapped as Write_rows
+ so that the event should be applied to replace slave's row
+ */
+ m_table->file->extra(HA_EXTRA_WRITE_CAN_REPLACE);
+ /*
+ NDB specific: if update from ndb master wrapped as Write_rows
+ does not find the row it's assumed idempotent binlog applying
+ is taking place; don't raise the error.
+ */
+ m_table->file->extra(HA_EXTRA_IGNORE_NO_KEY);
+ /*
+ TODO: the cluster team (Tomas?) says that it's better if the engine knows
+ how many rows are going to be inserted, then it can allocate needed memory
+ from the start.
+ */
+ }
+
+ /*
+ We need TIMESTAMP_NO_AUTO_SET otherwise ha_write_row() will not use fill
+ any TIMESTAMP column with data from the row but instead will use
+ the event's current time.
+ As we replicate from TIMESTAMP to TIMESTAMP and slave has no extra
+ columns, we know that all TIMESTAMP columns on slave will receive explicit
+ data from the row, so TIMESTAMP_NO_AUTO_SET is ok.
+ When we allow a table without TIMESTAMP to be replicated to a table having
+ more columns including a TIMESTAMP column, or when we allow a TIMESTAMP
+ column to be replicated into a BIGINT column and the slave's table has a
+ TIMESTAMP column, then the slave's TIMESTAMP column will take its value
+ from set_time() which we called earlier (consistent with SBR). And then in
+ some cases we won't want TIMESTAMP_NO_AUTO_SET (will require some code to
+ analyze if explicit data is provided for slave's TIMESTAMP columns).
+ */
+ m_table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET;
+
+ /* Honor next number column if present */
+ m_table->next_number_field= m_table->found_next_number_field;
+ return error;
+}
+
+int
+Write_rows_log_event::do_after_row_operations(const Slave_reporting_capability *const,
+ int error)
+{
+ int local_error= 0;
+ m_table->next_number_field=0;
+ if (bit_is_set(slave_exec_mode, SLAVE_EXEC_MODE_IDEMPOTENT) == 1 ||
+ m_table->s->db_type()->db_type == DB_TYPE_NDBCLUSTER)
+ {
+ m_table->file->extra(HA_EXTRA_NO_IGNORE_DUP_KEY);
+ m_table->file->extra(HA_EXTRA_WRITE_CANNOT_REPLACE);
+ /*
+ resetting the extra with
+ table->file->extra(HA_EXTRA_NO_IGNORE_NO_KEY);
+ fires bug#27077
+ explanation: file->reset() performs this duty
+ ultimately. Still todo: fix
+ */
+ }
+ if ((local_error= m_table->file->ha_end_bulk_insert()))
+ {
+ m_table->file->print_error(local_error, MYF(0));
+ }
+ return error? error : local_error;
+}
+
+#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
+
+/*
+ Check if there are more UNIQUE keys after the given key.
+*/
+static int
+last_uniq_key(TABLE *table, uint keyno)
+{
+ while (++keyno < table->s->keys)
+ if (table->key_info[keyno].flags & HA_NOSAME)
+ return 0;
+ return 1;
+}
+
+/**
+ Check if an error is a duplicate key error.
+
+ This function is used to check if an error code is one of the
+ duplicate key error, i.e., and error code for which it is sensible
+ to do a <code>get_dup_key()</code> to retrieve the duplicate key.
+
+ @param errcode The error code to check.
+
+ @return <code>true</code> if the error code is such that
+ <code>get_dup_key()</code> will return true, <code>false</code>
+ otherwise.
+ */
+bool
+is_duplicate_key_error(int errcode)
+{
+ switch (errcode)
+ {
+ case HA_ERR_FOUND_DUPP_KEY:
+ case HA_ERR_FOUND_DUPP_UNIQUE:
+ return true;
+ }
+ return false;
+}
+
+/**
+ Write the current row into event's table.
+
+ The row is located in the row buffer, pointed by @c m_curr_row member.
+ Number of columns of the row is stored in @c m_width member (it can be
+ different from the number of columns in the table to which we insert).
+ Bitmap @c m_cols indicates which columns are present in the row. It is assumed
+ that event's table is already open and pointed by @c m_table.
+
+ If the same record already exists in the table it can be either overwritten
+ or an error is reported depending on the value of @c overwrite flag
+ (error reporting not yet implemented). Note that the matching record can be
+ different from the row we insert if we use primary keys to identify records in
+ the table.
+
+ The row to be inserted can contain values only for selected columns. The
+ missing columns are filled with default values using @c prepare_record()
+ function. If a matching record is found in the table and @c overwritte is
+ true, the missing columns are taken from it.
+
+ @param rli Relay log info (needed for row unpacking).
+ @param overwrite
+ Shall we overwrite if the row already exists or signal
+ error (currently ignored).
+
+ @returns Error code on failure, 0 on success.
+
+ This method, if successful, sets @c m_curr_row_end pointer to point at the
+ next row in the rows buffer. This is done when unpacking the row to be
+ inserted.
+
+ @note If a matching record is found, it is either updated using
+ @c ha_update_row() or first deleted and then new record written.
+*/
+
+int
+Rows_log_event::write_row(const Relay_log_info *const rli,
+ const bool overwrite)
+{
+ DBUG_ENTER("write_row");
+ DBUG_ASSERT(m_table != NULL && thd != NULL);
+
+ TABLE *table= m_table; // pointer to event's table
+ int error;
+ int keynum;
+ auto_afree_ptr<char> key(NULL);
+
+ /* fill table->record[0] with default values */
+
+ if ((error= prepare_record(table, m_width,
+ TRUE /* check if columns have def. values */)))
+ DBUG_RETURN(error);
+
+ /* unpack row into table->record[0] */
+ error= unpack_current_row(rli); // TODO: how to handle errors?
+ if (m_curr_row == m_rows_buf)
+ {
+ /* this is the first row to be inserted, we estimate the rows with
+ the size of the first row and use that value to initialize
+ storage engine for bulk insertion */
+ 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);
+ }
+
+
+#ifndef DBUG_OFF
+ DBUG_DUMP("record[0]", table->record[0], table->s->reclength);
+ DBUG_PRINT_BITSET("debug", "write_set = %s", table->write_set);
+ DBUG_PRINT_BITSET("debug", "read_set = %s", table->read_set);
+#endif
+
+ /*
+ Try to write record. If a corresponding record already exists in the table,
+ we try to change it using ha_update_row() if possible. Otherwise we delete
+ it and repeat the whole process again.
+
+ TODO: Add safety measures against infinite looping.
+ */
+
+ while ((error= table->file->ha_write_row(table->record[0])))
+ {
+ if (error == HA_ERR_LOCK_DEADLOCK ||
+ error == HA_ERR_LOCK_WAIT_TIMEOUT ||
+ (keynum= table->file->get_dup_key(error)) < 0 ||
+ !overwrite)
+ {
+ DBUG_PRINT("info",("get_dup_key returns %d)", keynum));
+ /*
+ Deadlock, waiting for lock or just an error from the handler
+ such as HA_ERR_FOUND_DUPP_KEY when overwrite is false.
+ Retrieval of the duplicate key number may fail
+ - either because the error was not "duplicate key" error
+ - or because the information which key is not available
+ */
+ table->file->print_error(error, MYF(0));
+ DBUG_RETURN(error);
+ }
+ /*
+ We need to retrieve the old row into record[1] to be able to
+ either update or delete the offending record. We either:
+
+ - use rnd_pos() with a row-id (available as dupp_row) to the
+ offending row, if that is possible (MyISAM and Blackhole), or else
+
+ - use index_read_idx() with the key that is duplicated, to
+ retrieve the offending row.
+ */
+ if (table->file->ha_table_flags() & HA_DUPLICATE_POS)
+ {
+ DBUG_PRINT("info",("Locating offending record using rnd_pos()"));
+ error= table->file->rnd_pos(table->record[1], table->file->dup_ref);
+ if (error)
+ {
+ DBUG_PRINT("info",("rnd_pos() returns error %d",error));
+ if (error == HA_ERR_RECORD_DELETED)
+ error= HA_ERR_KEY_NOT_FOUND;
+ table->file->print_error(error, MYF(0));
+ DBUG_RETURN(error);
+ }
+ }
+ else
+ {
+ DBUG_PRINT("info",("Locating offending record using index_read_idx()"));
+
+ if (table->file->extra(HA_EXTRA_FLUSH_CACHE))
+ {
+ DBUG_PRINT("info",("Error when setting HA_EXTRA_FLUSH_CACHE"));
+ DBUG_RETURN(my_errno);
+ }
+
+ if (key.get() == NULL)
+ {
+ key.assign(static_cast<char*>(my_alloca(table->s->max_unique_length)));
+ if (key.get() == NULL)
+ {
+ DBUG_PRINT("info",("Can't allocate key buffer"));
+ DBUG_RETURN(ENOMEM);
+ }
+ }
+
+ key_copy((uchar*)key.get(), table->record[0], table->key_info + keynum,
+ 0);
+ error= table->file->index_read_idx_map(table->record[1], keynum,
+ (const uchar*)key.get(),
+ HA_WHOLE_KEY,
+ HA_READ_KEY_EXACT);
+ if (error)
+ {
+ DBUG_PRINT("info",("index_read_idx() returns %s", HA_ERR(error)));
+ if (error == HA_ERR_RECORD_DELETED)
+ error= HA_ERR_KEY_NOT_FOUND;
+ table->file->print_error(error, MYF(0));
+ DBUG_RETURN(error);
+ }
+ }
+
+ /*
+ Now, record[1] should contain the offending row. That
+ will enable us to update it or, alternatively, delete it (so
+ that we can insert the new row afterwards).
+ */
+
+ /*
+ If row is incomplete we will use the record found to fill
+ missing columns.
+ */
+ if (!get_flags(COMPLETE_ROWS_F))
+ {
+ restore_record(table,record[1]);
+ error= unpack_current_row(rli);
+ }
+
+#ifndef DBUG_OFF
+ DBUG_PRINT("debug",("preparing for update: before and after image"));
+ DBUG_DUMP("record[1] (before)", table->record[1], table->s->reclength);
+ DBUG_DUMP("record[0] (after)", table->record[0], table->s->reclength);
+#endif
+
+ /*
+ REPLACE is defined as either INSERT or DELETE + INSERT. If
+ possible, we can replace it with an UPDATE, but that will not
+ work on InnoDB if FOREIGN KEY checks are necessary.
+
+ I (Matz) am not sure of the reason for the last_uniq_key()
+ check as, but I'm guessing that it's something along the
+ following lines.
+
+ Suppose that we got the duplicate key to be a key that is not
+ the last unique key for the table and we perform an update:
+ then there might be another key for which the unique check will
+ fail, so we're better off just deleting the row and inserting
+ the correct row.
+ */
+ if (last_uniq_key(table, keynum) &&
+ !table->file->referenced_by_foreign_key())
+ {
+ DBUG_PRINT("info",("Updating row using ha_update_row()"));
+ error=table->file->ha_update_row(table->record[1],
+ table->record[0]);
+ switch (error) {
+
+ case HA_ERR_RECORD_IS_THE_SAME:
+ DBUG_PRINT("info",("ignoring HA_ERR_RECORD_IS_THE_SAME error from"
+ " ha_update_row()"));
+ error= 0;
+
+ case 0:
+ break;
+
+ default:
+ DBUG_PRINT("info",("ha_update_row() returns error %d",error));
+ table->file->print_error(error, MYF(0));
+ }
+
+ DBUG_RETURN(error);
+ }
+ else
+ {
+ DBUG_PRINT("info",("Deleting offending row and trying to write new one again"));
+ if ((error= table->file->ha_delete_row(table->record[1])))
+ {
+ DBUG_PRINT("info",("ha_delete_row() returns error %d",error));
+ table->file->print_error(error, MYF(0));
+ DBUG_RETURN(error);
+ }
+ /* Will retry ha_write_row() with the offending row removed. */
+ }
+ }
+
+ DBUG_RETURN(error);
+}
+
+#endif
+
+int
+Write_rows_log_event::do_exec_row(const Relay_log_info *const rli)
+{
+ DBUG_ASSERT(m_table != NULL);
+ int error=
+ write_row(rli, /* if 1 then overwrite */
+ bit_is_set(slave_exec_mode, SLAVE_EXEC_MODE_IDEMPOTENT) == 1);
+
+ if (error && !thd->is_error())
+ {
+ DBUG_ASSERT(0);
+ my_error(ER_UNKNOWN_ERROR, MYF(0));
+ }
+
+ return error;
+}
+
+#endif /* !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) */
+
+#ifdef MYSQL_CLIENT
+void Write_rows_log_event::print(FILE *file, PRINT_EVENT_INFO* print_event_info)
+{
+ Rows_log_event::print_helper(file, print_event_info, "Write_rows");
+}
+#endif
+
+/**************************************************************************
+ Delete_rows_log_event member functions
+**************************************************************************/
+
+#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
+/*
+ Compares table->record[0] and table->record[1]
+
+ Returns TRUE if different.
+*/
+static bool record_compare(TABLE *table)
+{
+ /*
+ Need to set the X bit and the filler bits in both records since
+ there are engines that do not set it correctly.
+
+ In addition, since MyISAM checks that one hasn't tampered with the
+ record, it is necessary to restore the old bytes into the record
+ after doing the comparison.
+
+ TODO[record format ndb]: Remove it once NDB returns correct
+ records. Check that the other engines also return correct records.
+ */
+
+ DBUG_DUMP("record[0]", table->record[0], table->s->reclength);
+ DBUG_DUMP("record[1]", table->record[1], table->s->reclength);
+
+ bool result= FALSE;
+ uchar saved_x[2], saved_filler[2];
+
+ if (table->s->null_bytes > 0)
+ {
+ for (int i = 0 ; i < 2 ; ++i)
+ {
+ saved_x[i]= table->record[i][0];
+ saved_filler[i]= table->record[i][table->s->null_bytes - 1];
+ table->record[i][0]|= 1U;
+ table->record[i][table->s->null_bytes - 1]|=
+ 256U - (1U << table->s->last_null_bit_pos);
+ }
+ }
+
+ if (table->s->blob_fields + table->s->varchar_fields == 0)
+ {
+ result= cmp_record(table,record[1]);
+ goto record_compare_exit;
+ }
+
+ /* Compare null bits */
+ if (memcmp(table->null_flags,
+ table->null_flags+table->s->rec_buff_length,
+ table->s->null_bytes))
+ {
+ result= TRUE; // Diff in NULL value
+ goto record_compare_exit;
+ }
+
+ /* Compare updated fields */
+ for (Field **ptr=table->field ; *ptr ; ptr++)
+ {
+ if ((*ptr)->cmp_binary_offset(table->s->rec_buff_length))
+ {
+ result= TRUE;
+ goto record_compare_exit;
+ }
+ }
+
+record_compare_exit:
+ /*
+ Restore the saved bytes.
+
+ TODO[record format ndb]: Remove this code once NDB returns the
+ correct record format.
+ */
+ if (table->s->null_bytes > 0)
+ {
+ for (int i = 0 ; i < 2 ; ++i)
+ {
+ table->record[i][0]= saved_x[i];
+ table->record[i][table->s->null_bytes - 1]= saved_filler[i];
+ }
+ }
+
+ return result;
+}
+
+/**
+ Locate the current row in event's table.
+
+ The current row is pointed by @c m_curr_row. Member @c m_width tells how many
+ columns are there in the row (this can be differnet from the number of columns
+ in the table). It is assumed that event's table is already open and pointed
+ by @c m_table.
+
+ If a corresponding record is found in the table it is stored in
+ @c m_table->record[0]. Note that when record is located based on a primary
+ key, it is possible that the record found differs from the row being located.
+
+ If no key is specified or table does not have keys, a table scan is used to
+ find the row. In that case the row should be complete and contain values for
+ all columns. However, it can still be shorter than the table, i.e. the table
+ can contain extra columns not present in the row. It is also possible that
+ the table has fewer columns than the row being located.
+
+ @returns Error code on failure, 0 on success.
+
+ @post In case of success @c m_table->record[0] contains the record found.
+ Also, the internal "cursor" of the table is positioned at the record found.
+
+ @note If the engine allows random access of the records, a combination of
+ @c position() and @c rnd_pos() will be used.
+ */
+
+int Rows_log_event::find_row(const Relay_log_info *rli)
+{
+ DBUG_ENTER("Rows_log_event::find_row");
+
+ DBUG_ASSERT(m_table && m_table->in_use != NULL);
+
+ TABLE *table= m_table;
+ int error= 0;
+
+ /*
+ rpl_row_tabledefs.test specifies that
+ if the extra field on the slave does not have a default value
+ and this is okay with Delete or Update events.
+ Todo: fix wl3228 hld that requires defauls for all types of events
+ */
+
+ prepare_record(table, m_width, FALSE);
+ error= unpack_current_row(rli);
+
+#ifndef DBUG_OFF
+ DBUG_PRINT("info",("looking for the following record"));
+ DBUG_DUMP("record[0]", table->record[0], table->s->reclength);
+#endif
+
+ if ((table->file->ha_table_flags() & HA_PRIMARY_KEY_REQUIRED_FOR_POSITION) &&
+ table->s->primary_key < MAX_KEY)
+ {
+ /*
+ Use a more efficient method to fetch the record given by
+ table->record[0] if the engine allows it. We first compute a
+ row reference using the position() member function (it will be
+ stored in table->file->ref) and the use rnd_pos() to position
+ the "cursor" (i.e., record[0] in this case) at the correct row.
+
+ TODO: Add a check that the correct record has been fetched by
+ comparing with the original record. Take into account that the
+ record on the master and slave can be of different
+ length. Something along these lines should work:
+
+ ADD>>> store_record(table,record[1]);
+ int error= table->file->rnd_pos(table->record[0], table->file->ref);
+ ADD>>> DBUG_ASSERT(memcmp(table->record[1], table->record[0],
+ table->s->reclength) == 0);
+
+ */
+ DBUG_PRINT("info",("locating record using primary key (position)"));
+ int error= table->file->rnd_pos_by_record(table->record[0]);
+ if (error)
+ {
+ DBUG_PRINT("info",("rnd_pos returns error %d",error));
+ if (error == HA_ERR_RECORD_DELETED)
+ error= HA_ERR_KEY_NOT_FOUND;
+ table->file->print_error(error, MYF(0));
+ }
+ DBUG_RETURN(error);
+ }
+
+ // We can't use position() - try other methods.
+
+ /*
+ We need to retrieve all fields
+ TODO: Move this out from this function to main loop
+ */
+ table->use_all_columns();
+
+ /*
+ Save copy of the record in table->record[1]. It might be needed
+ later if linear search is used to find exact match.
+ */
+ store_record(table,record[1]);
+
+ if (table->s->keys > 0)
+ {
+ DBUG_PRINT("info",("locating record using primary key (index_read)"));
+
+ /* We have a key: search the table using the index */
+ if (!table->file->inited && (error= table->file->ha_index_init(0, FALSE)))
+ {
+ DBUG_PRINT("info",("ha_index_init returns error %d",error));
+ table->file->print_error(error, MYF(0));
+ goto err;
+ }
+
+ /* Fill key data for the row */
+
+ DBUG_ASSERT(m_key);
+ key_copy(m_key, table->record[0], table->key_info, 0);
+
+ /*
+ Don't print debug messages when running valgrind since they can
+ trigger false warnings.
+ */
+#ifndef HAVE_purify
+ DBUG_DUMP("key data", m_key, table->key_info->key_length);
+#endif
+
+ /*
+ We need to set the null bytes to ensure that the filler bit are
+ all set when returning. There are storage engines that just set
+ the necessary bits on the bytes and don't set the filler bits
+ correctly.
+ */
+ if (table->s->null_bytes > 0)
+ table->record[0][table->s->null_bytes - 1]|=
+ 256U - (1U << table->s->last_null_bit_pos);
+
+ if ((error= table->file->index_read_map(table->record[0], m_key,
+ HA_WHOLE_KEY,
+ HA_READ_KEY_EXACT)))
+ {
+ DBUG_PRINT("info",("no record matching the key found in the table"));
+ if (error == HA_ERR_RECORD_DELETED)
+ error= HA_ERR_KEY_NOT_FOUND;
+ table->file->print_error(error, MYF(0));
+ table->file->ha_index_end();
+ goto err;
+ }
+
+ /*
+ Don't print debug messages when running valgrind since they can
+ trigger false warnings.
+ */
+#ifndef HAVE_purify
+ DBUG_PRINT("info",("found first matching record"));
+ DBUG_DUMP("record[0]", table->record[0], table->s->reclength);
+#endif
+ /*
+ Below is a minor "optimization". If the key (i.e., key number
+ 0) has the HA_NOSAME flag set, we know that we have found the
+ correct record (since there can be no duplicates); otherwise, we
+ have to compare the record with the one found to see if it is
+ the correct one.
+
+ CAVEAT! This behaviour is essential for the replication of,
+ e.g., the mysql.proc table since the correct record *shall* be
+ found using the primary key *only*. There shall be no
+ comparison of non-PK columns to decide if the correct record is
+ found. I can see no scenario where it would be incorrect to
+ chose the row to change only using a PK or an UNNI.
+ */
+ if (table->key_info->flags & HA_NOSAME)
+ {
+ table->file->ha_index_end();
+ goto ok;
+ }
+
+ /*
+ In case key is not unique, we still have to iterate over records found
+ and find the one which is identical to the row given. A copy of the
+ record we are looking for is stored in record[1].
+ */
+ DBUG_PRINT("info",("non-unique index, scanning it to find matching record"));
+
+ while (record_compare(table))
+ {
+ /*
+ We need to set the null bytes to ensure that the filler bit
+ are all set when returning. There are storage engines that
+ just set the necessary bits on the bytes and don't set the
+ filler bits correctly.
+
+ TODO[record format ndb]: Remove this code once NDB returns the
+ correct record format.
+ */
+ if (table->s->null_bytes > 0)
+ {
+ table->record[0][table->s->null_bytes - 1]|=
+ 256U - (1U << table->s->last_null_bit_pos);
+ }
+
+ while ((error= table->file->index_next(table->record[0])))
+ {
+ /* We just skip records that has already been deleted */
+ if (error == HA_ERR_RECORD_DELETED)
+ continue;
+ DBUG_PRINT("info",("no record matching the given row found"));
+ table->file->print_error(error, MYF(0));
+ table->file->ha_index_end();
+ goto err;
+ }
+ }
+
+ /*
+ Have to restart the scan to be able to fetch the next row.
+ */
+ table->file->ha_index_end();
+ }
+ else
+ {
+ DBUG_PRINT("info",("locating record using table scan (rnd_next)"));
+
+ 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(1)))
+ {
+ DBUG_PRINT("info",("error initializing table scan"
+ " (ha_rnd_init returns %d)",error));
+ table->file->print_error(error, MYF(0));
+ goto err;
+ }
+
+ /* Continue until we find the right record or have made a full loop */
+ do
+ {
+ restart_rnd_next:
+ error= table->file->rnd_next(table->record[0]);
+
+ DBUG_PRINT("info", ("error: %s", HA_ERR(error)));
+ switch (error) {
+
+ case 0:
+ break;
+
+ /*
+ If the record was deleted, we pick the next one without doing
+ any comparisons.
+ */
+ case HA_ERR_RECORD_DELETED:
+ goto restart_rnd_next;
+
+ case HA_ERR_END_OF_FILE:
+ if (++restart_count < 2)
+ table->file->ha_rnd_init(1);
+ 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;
+ }
+ }
+ while (restart_count < 2 && 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:
+ table->default_column_bitmaps();
+ DBUG_RETURN(error);
+}
+
+#endif
+
+/*
+ Constructor used to build an event for writing to the binary log.
+ */
+
+#ifndef MYSQL_CLIENT
+Delete_rows_log_event::Delete_rows_log_event(THD *thd_arg, TABLE *tbl_arg,
+ ulong tid, MY_BITMAP const *cols,
+ bool is_transactional)
+ : Rows_log_event(thd_arg, tbl_arg, tid, cols, is_transactional)
+{
+}
+#endif /* #if !defined(MYSQL_CLIENT) */
+
+/*
+ Constructor used by slave to read the event from the binary log.
+ */
+#ifdef HAVE_REPLICATION
+Delete_rows_log_event::Delete_rows_log_event(const char *buf, uint event_len,
+ const Format_description_log_event
+ *description_event)
+ : Rows_log_event(buf, event_len, DELETE_ROWS_EVENT, description_event)
+{
+}
+#endif
+
+#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
+
+int
+Delete_rows_log_event::do_before_row_operations(const Slave_reporting_capability *const)
+{
+ if ((m_table->file->ha_table_flags() & HA_PRIMARY_KEY_REQUIRED_FOR_POSITION) &&
+ m_table->s->primary_key < MAX_KEY)
+ {
+ /*
+ We don't need to allocate any memory for m_key since it is not used.
+ */
+ return 0;
+ }
+
+ if (m_table->s->keys > 0)
+ {
+ // Allocate buffer for key searches
+ m_key= (uchar*)my_malloc(m_table->key_info->key_length, MYF(MY_WME));
+ if (!m_key)
+ return HA_ERR_OUT_OF_MEM;
+ }
+ return 0;
+}
+
+int
+Delete_rows_log_event::do_after_row_operations(const Slave_reporting_capability *const,
+ int error)
+{
+ /*error= ToDo:find out what this should really be, this triggers close_scan in nbd, returning error?*/
+ m_table->file->ha_index_or_rnd_end();
+ my_free(m_key, MYF(MY_ALLOW_ZERO_PTR));
+ m_key= NULL;
+
+ return error;
+}
+
+int Delete_rows_log_event::do_exec_row(const Relay_log_info *const rli)
+{
+ int error;
+ DBUG_ASSERT(m_table != NULL);
+
+ if (!(error= find_row(rli)))
+ {
+ /*
+ Delete the record found, located in record[0]
+ */
+ error= m_table->file->ha_delete_row(m_table->record[0]);
+ }
+ return error;
+}
+
+#endif /* !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) */
+
+#ifdef MYSQL_CLIENT
+void Delete_rows_log_event::print(FILE *file,
+ PRINT_EVENT_INFO* print_event_info)
+{
+ Rows_log_event::print_helper(file, print_event_info, "Delete_rows");
+}
+#endif
+
+
+/**************************************************************************
+ Update_rows_log_event member functions
+**************************************************************************/
+
+/*
+ Constructor used to build an event for writing to the binary log.
+ */
+#if !defined(MYSQL_CLIENT)
+Update_rows_log_event::Update_rows_log_event(THD *thd_arg, TABLE *tbl_arg,
+ ulong tid,
+ MY_BITMAP const *cols_bi,
+ MY_BITMAP const *cols_ai,
+ bool is_transactional)
+: Rows_log_event(thd_arg, tbl_arg, tid, cols_bi, is_transactional)
+{
+ init(cols_ai);
+}
+
+Update_rows_log_event::Update_rows_log_event(THD *thd_arg, TABLE *tbl_arg,
+ ulong tid,
+ MY_BITMAP const *cols,
+ bool is_transactional)
+: Rows_log_event(thd_arg, tbl_arg, tid, cols, is_transactional)
+{
+ init(cols);
+}
+
+void Update_rows_log_event::init(MY_BITMAP const *cols)
+{
+ /* if bitmap_init fails, caught in is_valid() */
+ if (likely(!bitmap_init(&m_cols_ai,
+ m_width <= sizeof(m_bitbuf_ai)*8 ? m_bitbuf_ai : NULL,
+ m_width,
+ false)))
+ {
+ /* Cols can be zero if this is a dummy binrows event */
+ if (likely(cols != NULL))
+ {
+ memcpy(m_cols_ai.bitmap, cols->bitmap, no_bytes_in_map(cols));
+ create_last_word_mask(&m_cols_ai);
+ }
+ }
+}
+#endif /* !defined(MYSQL_CLIENT) */
+
+
+Update_rows_log_event::~Update_rows_log_event()
+{
+ if (m_cols_ai.bitmap == m_bitbuf_ai) // no my_malloc happened
+ m_cols_ai.bitmap= 0; // so no my_free in bitmap_free
+ bitmap_free(&m_cols_ai); // To pair with bitmap_init().
+}
+
+
+/*
+ Constructor used by slave to read the event from the binary log.
+ */
+#ifdef HAVE_REPLICATION
+Update_rows_log_event::Update_rows_log_event(const char *buf, uint event_len,
+ const
+ Format_description_log_event
+ *description_event)
+ : Rows_log_event(buf, event_len, UPDATE_ROWS_EVENT, description_event)
+{
+}
+#endif
+
+#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
+
+int
+Update_rows_log_event::do_before_row_operations(const Slave_reporting_capability *const)
+{
+ if (m_table->s->keys > 0)
+ {
+ // Allocate buffer for key searches
+ m_key= (uchar*)my_malloc(m_table->key_info->key_length, MYF(MY_WME));
+ if (!m_key)
+ return HA_ERR_OUT_OF_MEM;
+ }
+
+ m_table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET;
+
+ return 0;
+}
+
+int
+Update_rows_log_event::do_after_row_operations(const Slave_reporting_capability *const,
+ int error)
+{
+ /*error= ToDo:find out what this should really be, this triggers close_scan in nbd, returning error?*/
+ m_table->file->ha_index_or_rnd_end();
+ my_free(m_key, MYF(MY_ALLOW_ZERO_PTR)); // Free for multi_malloc
+ m_key= NULL;
+
+ return error;
+}
+
+int
+Update_rows_log_event::do_exec_row(const Relay_log_info *const rli)
+{
+ DBUG_ASSERT(m_table != NULL);
+
+ int error= find_row(rli);
+ if (error)
+ {
+ /*
+ We need to read the second image in the event of error to be
+ able to skip to the next pair of updates
+ */
+ m_curr_row= m_curr_row_end;
+ unpack_current_row(rli);
+ return error;
+ }
+
+ /*
+ This is the situation after locating BI:
+
+ ===|=== before image ====|=== after image ===|===
+ ^ ^
+ m_curr_row m_curr_row_end
+
+ BI found in the table is stored in record[0]. We copy it to record[1]
+ and unpack AI to record[0].
+ */
+
+ store_record(m_table,record[1]);
+
+ m_curr_row= m_curr_row_end;
+ error= unpack_current_row(rli); // this also updates m_curr_row_end
+
+ /*
+ Now we have the right row to update. The old row (the one we're
+ looking for) is in record[1] and the new row is in record[0].
+ */
+#ifndef HAVE_purify
+ /*
+ Don't print debug messages when running valgrind since they can
+ trigger false warnings.
+ */
+ DBUG_PRINT("info",("Updating row in table"));
+ DBUG_DUMP("old record", m_table->record[1], m_table->s->reclength);
+ DBUG_DUMP("new values", m_table->record[0], m_table->s->reclength);
+#endif
+
+ error= m_table->file->ha_update_row(m_table->record[1], m_table->record[0]);
+ if (error == HA_ERR_RECORD_IS_THE_SAME)
+ error= 0;
+
+ return error;
+}
+
+#endif /* !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) */
+
+#ifdef MYSQL_CLIENT
+void Update_rows_log_event::print(FILE *file,
+ PRINT_EVENT_INFO* print_event_info)
+{
+ Rows_log_event::print_helper(file, print_event_info, "Update_rows");
+}
+#endif
+
+
+Incident_log_event::Incident_log_event(const char *buf, uint event_len,
+ const Format_description_log_event *descr_event)
+ : Log_event(buf, descr_event)
+{
+ DBUG_ENTER("Incident_log_event::Incident_log_event");
+ uint8 const common_header_len=
+ descr_event->common_header_len;
+ uint8 const post_header_len=
+ descr_event->post_header_len[INCIDENT_EVENT-1];
+
+ DBUG_PRINT("info",("event_len: %u; common_header_len: %d; post_header_len: %d",
+ event_len, common_header_len, post_header_len));
+
+ int incident_number= uint2korr(buf + common_header_len);
+ if (incident_number >= INCIDENT_COUNT ||
+ incident_number <= INCIDENT_NONE)
+ {
+ // If the incident is not recognized, this binlog event is
+ // invalid. If we set incident_number to INCIDENT_NONE, the
+ // invalidity will be detected by is_valid().
+ incident_number= INCIDENT_NONE;
+ DBUG_VOID_RETURN;
+ }
+ m_incident= static_cast<Incident>(incident_number);
+ char const *ptr= buf + common_header_len + post_header_len;
+ char const *const str_end= buf + event_len;
+ uint8 len= 0; // Assignment to keep compiler happy
+ const char *str= NULL; // Assignment to keep compiler happy
+ read_str(&ptr, str_end, &str, &len);
+ m_message.str= const_cast<char*>(str);
+ m_message.length= len;
+ DBUG_PRINT("info", ("m_incident: %d", m_incident));
+ DBUG_VOID_RETURN;
+}
+
+
+Incident_log_event::~Incident_log_event()
+{
+}
+
+
+const char *
+Incident_log_event::description() const
+{
+ static const char *const description[]= {
+ "NOTHING", // Not used
+ "LOST_EVENTS"
+ };
+
+ DBUG_PRINT("info", ("m_incident: %d", m_incident));
+
+ return description[m_incident];
+}
+
+
+#ifndef MYSQL_CLIENT
+void Incident_log_event::pack_info(Protocol *protocol)
+{
+ char buf[256];
+ size_t bytes;
+ if (m_message.length > 0)
+ bytes= my_snprintf(buf, sizeof(buf), "#%d (%s)",
+ m_incident, description());
+ else
+ bytes= my_snprintf(buf, sizeof(buf), "#%d (%s): %s",
+ m_incident, description(), m_message.str);
+ protocol->store(buf, bytes, &my_charset_bin);
+}
+#endif
+
+
+#ifdef MYSQL_CLIENT
+void
+Incident_log_event::print(FILE *file,
+ PRINT_EVENT_INFO *print_event_info)
+{
+ if (print_event_info->short_form)
+ return;
+
+ Write_on_release_cache cache(&print_event_info->head_cache, file);
+ print_header(&cache, print_event_info, FALSE);
+ my_b_printf(&cache, "\n# Incident: %s", description());
+}
+#endif
+
+#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
+int
+Incident_log_event::do_apply_event(Relay_log_info const *rli)
+{
+ DBUG_ENTER("Incident_log_event::do_apply_event");
+ rli->report(ERROR_LEVEL, ER_SLAVE_INCIDENT,
+ ER(ER_SLAVE_INCIDENT),
+ description(),
+ m_message.length > 0 ? m_message.str : "<none>");
+ DBUG_RETURN(1);
+}
+#endif
+
+bool
+Incident_log_event::write_data_header(IO_CACHE *file)
+{
+ DBUG_ENTER("Incident_log_event::write_data_header");
+ DBUG_PRINT("enter", ("m_incident: %d", m_incident));
+ uchar buf[sizeof(int16)];
+ int2store(buf, (int16) m_incident);
+ DBUG_RETURN(my_b_safe_write(file, buf, sizeof(buf)));
+}
+
+bool
+Incident_log_event::write_data_body(IO_CACHE *file)
+{
+ DBUG_ENTER("Incident_log_event::write_data_body");
+ DBUG_RETURN(write_str(file, m_message.str, (uint) m_message.length));
+}
+
+
+#ifdef MYSQL_CLIENT
+/**
+ The default values for these variables should be values that are
+ *incorrect*, i.e., values that cannot occur in an event. This way,
+ they will always be printed for the first event.
+*/
+st_print_event_info::st_print_event_info()
+ :flags2_inited(0), sql_mode_inited(0),
+ auto_increment_increment(0),auto_increment_offset(0), charset_inited(0),
+ lc_time_names_number(~0),
+ charset_database_number(ILLEGAL_CHARSET_INFO_NUMBER),
+ thread_id(0), thread_id_printed(false),
+ base64_output_mode(BASE64_OUTPUT_UNSPEC), printed_fd_event(FALSE)
+{
+ /*
+ Currently we only use static PRINT_EVENT_INFO objects, so zeroed at
+ program's startup, but these explicit bzero() is for the day someone
+ creates dynamic instances.
+ */
+ bzero(db, sizeof(db));
+ bzero(charset, sizeof(charset));
+ bzero(time_zone_str, sizeof(time_zone_str));
+ delimiter[0]= ';';
+ delimiter[1]= 0;
+ myf const flags = MYF(MY_WME | MY_NABP);
+ open_cached_file(&head_cache, NULL, NULL, 0, flags);
+ open_cached_file(&body_cache, NULL, NULL, 0, flags);
+}
+#endif
diff --git a/sql/log_event.h b/sql/log_event.h
index 5b065a33dd1..8f8fde4c9db 100644
--- a/sql/log_event.h
+++ b/sql/log_event.h
@@ -13,18 +13,57 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+/**
+ @addtogroup Replication
+ @{
+
+ @file
+
+ @brief Binary log event definitions. This includes generic code
+ common to all types of log events, as well as specific code for each
+ type of log event.
+*/
+
#ifndef _log_event_h
#define _log_event_h
-#ifdef __EMX__
-#undef write // remove pthread.h macro definition, conflict with write() class member
-#endif
-
#if defined(USE_PRAGMA_INTERFACE) && !defined(MYSQL_CLIENT)
#pragma interface /* gcc class implementation */
#endif
+#include <my_bitmap.h>
+#include "rpl_constants.h"
+
+#ifdef MYSQL_CLIENT
+#include "rpl_utility.h"
+#include "hash.h"
+#include "rpl_tblmap.h"
+#include "rpl_tblmap.cc"
+#endif
+
+#ifndef MYSQL_CLIENT
+#include "rpl_record.h"
+#include "rpl_reporting.h"
+#endif
+
+/**
+ Either assert or return an error.
+
+ In debug build, the condition will be checked, but in non-debug
+ builds, the error code given will be returned instead.
+
+ @param COND Condition to check
+ @param ERRNO Error number to return in non-debug builds
+*/
+#ifdef DBUG_OFF
+#define ASSERT_OR_RETURN_ERROR(COND, ERRNO) \
+ do { if (!(COND)) return ERRNO; } while (0)
+#else
+#define ASSERT_OR_RETURN_ERROR(COND, ERRNO) \
+ DBUG_ASSERT(COND)
+#endif
+
#define LOG_READ_EOF -1
#define LOG_READ_BOGUS -2
#define LOG_READ_IO -3
@@ -58,8 +97,8 @@
which increments every time we write an event to the binlog) (3 bytes).
Q: how do we handle when the counter is overflowed and restarts from 0 ?
- - Query and Load (Create or Execute) events may have a more precise timestamp
- (with microseconds), number of matched/affected/warnings rows
+ - Query and Load (Create or Execute) events may have a more precise
+ timestamp (with microseconds), number of matched/affected/warnings rows
and fields of session variables: SQL_MODE,
FOREIGN_KEY_CHECKS, UNIQUE_CHECKS, SQL_AUTO_IS_NULL, the collations and
charsets, the PASSWORD() version (old/new/...).
@@ -121,11 +160,11 @@ struct old_sql_ex
struct sql_ex_info
{
sql_ex_info() {} /* Remove gcc warning */
- char* field_term;
- char* enclosed;
- char* line_term;
- char* line_start;
- char* escaped;
+ const char* field_term;
+ const char* enclosed;
+ const char* line_term;
+ const char* line_start;
+ const char* escaped;
int cached_new_format;
uint8 field_term_len,enclosed_len,line_term_len,line_start_len, escaped_len;
char opt_flags;
@@ -140,7 +179,7 @@ struct sql_ex_info
line_start_len + escaped_len + 6 : 7);
}
bool write_data(IO_CACHE* file);
- char* init(char* buf,char* buf_end,bool use_new_format);
+ const char* init(const char* buf, const char* buf_end, bool use_new_format);
bool new_format()
{
return ((cached_new_format != -1) ? cached_new_format :
@@ -188,28 +227,41 @@ struct sql_ex_info
#define QUERY_HEADER_MINIMAL_LEN (4 + 4 + 1 + 2)
// where 5.0 differs: 2 for len of N-bytes vars.
#define QUERY_HEADER_LEN (QUERY_HEADER_MINIMAL_LEN + 2)
+#define STOP_HEADER_LEN 0
#define LOAD_HEADER_LEN (4 + 4 + 4 + 1 +1 + 4)
+#define SLAVE_HEADER_LEN 0
#define START_V3_HEADER_LEN (2 + ST_SERVER_VER_LEN + 4)
#define ROTATE_HEADER_LEN 8 // this is FROZEN (the Rotate post-header is frozen)
+#define INTVAR_HEADER_LEN 0
#define CREATE_FILE_HEADER_LEN 4
#define APPEND_BLOCK_HEADER_LEN 4
#define EXEC_LOAD_HEADER_LEN 4
#define DELETE_FILE_HEADER_LEN 4
+#define NEW_LOAD_HEADER_LEN LOAD_HEADER_LEN
+#define RAND_HEADER_LEN 0
+#define USER_VAR_HEADER_LEN 0
#define FORMAT_DESCRIPTION_HEADER_LEN (START_V3_HEADER_LEN+1+LOG_EVENT_TYPES)
+#define XID_HEADER_LEN 0
+#define BEGIN_LOAD_QUERY_HEADER_LEN APPEND_BLOCK_HEADER_LEN
+#define ROWS_HEADER_LEN 8
+#define TABLE_MAP_HEADER_LEN 8
#define EXECUTE_LOAD_QUERY_EXTRA_HEADER_LEN (4 + 4 + 4 + 1)
#define EXECUTE_LOAD_QUERY_HEADER_LEN (QUERY_HEADER_LEN + EXECUTE_LOAD_QUERY_EXTRA_HEADER_LEN)
-
+#define INCIDENT_HEADER_LEN 2
/*
Max number of possible extra bytes in a replication event compared to a
packet (i.e. a query) sent from client to master;
First, an auxiliary log_event status vars estimation:
*/
-#define MAX_SIZE_LOG_EVENT_STATUS (4 /* flags2 */ + \
- 8 /* sql mode */ + \
- 1 + 1 + 255 /* catalog */ + \
- 4 /* autoinc */ + \
- 6 /* charset */ + \
- MAX_TIME_ZONE_NAME_LENGTH)
+#define MAX_SIZE_LOG_EVENT_STATUS (1 + 4 /* type, flags2 */ + \
+ 1 + 8 /* type, sql_mode */ + \
+ 1 + 1 + 255 /* type, length, catalog */ + \
+ 1 + 4 /* type, auto_increment */ + \
+ 1 + 6 /* type, charset */ + \
+ 1 + 1 + 255 /* type, length, time_zone */ + \
+ 1 + 2 /* type, lc_time_names_number */ + \
+ 1 + 2 /* type, charset_database_number */ + \
+ 1 + 8 /* type, table_map_for_update */)
#define MAX_LOG_EVENT_HEADER ( /* in order of Query_log_event::write */ \
LOG_EVENT_HEADER_LEN + /* write_header */ \
QUERY_HEADER_LEN + /* write_data */ \
@@ -273,18 +325,18 @@ struct sql_ex_info
#define Q_LC_TIME_NAMES_CODE 7
#define Q_CHARSET_DATABASE_CODE 8
-/* Intvar event post-header */
+#define Q_TABLE_MAP_FOR_UPDATE_CODE 9
+
+/* Intvar event data */
#define I_TYPE_OFFSET 0
#define I_VAL_OFFSET 1
-/* Rand event post-header */
-
+/* Rand event data */
#define RAND_SEED1_OFFSET 0
#define RAND_SEED2_OFFSET 8
-/* User_var event post-header */
-
+/* User_var event data */
#define UV_VAL_LEN_SIZE 4
#define UV_VAL_IS_NULL 1
#define UV_VAL_TYPE_SIZE 1
@@ -292,7 +344,6 @@ struct sql_ex_info
#define UV_CHARSET_NUMBER_SIZE 4
/* Load event post-header */
-
#define L_THREAD_ID_OFFSET 0
#define L_EXEC_TIME_OFFSET 4
#define L_SKIP_LINES_OFFSET 8
@@ -303,7 +354,6 @@ struct sql_ex_info
#define L_DATA_OFFSET LOAD_HEADER_LEN
/* Rotate event post-header */
-
#define R_POS_OFFSET 0
#define R_IDENT_OFFSET 8
@@ -323,6 +373,14 @@ struct sql_ex_info
/* DF = "Delete File" */
#define DF_FILE_ID_OFFSET 0
+/* TM = "Table Map" */
+#define TM_MAPID_OFFSET 0
+#define TM_FLAGS_OFFSET 6
+
+/* RW = "RoWs" */
+#define RW_MAPID_OFFSET 0
+#define RW_FLAGS_OFFSET 6
+
/* ELQ = "Execute Load Query" */
#define ELQ_FILE_ID_OFFSET QUERY_HEADER_LEN
#define ELQ_FN_POS_START_OFFSET ELQ_FILE_ID_OFFSET + 4
@@ -372,15 +430,19 @@ struct sql_ex_info
#define LOG_EVENT_BINLOG_IN_USE_F 0x1
-/*
- If the query depends on the thread (for example: TEMPORARY TABLE).
- Currently this is used by mysqlbinlog to know it must print
- SET @@PSEUDO_THREAD_ID=xx; before the query (it would not hurt to print it
- for every query but this would be slow).
+/**
+ @def LOG_EVENT_THREAD_SPECIFIC_F
+
+ If the query depends on the thread (for example: TEMPORARY TABLE).
+ Currently this is used by mysqlbinlog to know it must print
+ SET @@PSEUDO_THREAD_ID=xx; before the query (it would not hurt to print it
+ for every query but this would be slow).
*/
#define LOG_EVENT_THREAD_SPECIFIC_F 0x4
-/*
+/**
+ @def LOG_EVENT_SUPPRESS_USE_F
+
Suppress the generation of 'USE' statements before the actual
statement. This flag should be set for any events that does not need
the current database set to function correctly. Most notable cases
@@ -394,30 +456,69 @@ struct sql_ex_info
#define LOG_EVENT_SUPPRESS_USE_F 0x8
/*
- OPTIONS_WRITTEN_TO_BIN_LOG are the bits of thd->options which must be
- written to the binlog. OPTIONS_WRITTEN_TO_BINLOG could be written
- into the Format_description_log_event, so that if later we don't want
- to replicate a variable we did replicate, or the contrary, it's
- doable. But it should not be too hard to decide once for all of what
- we replicate and what we don't, among the fixed 32 bits of
- thd->options.
- I (Guilhem) have read through every option's usage, and it looks like
- OPTION_AUTO_IS_NULL and OPTION_NO_FOREIGN_KEYS are the only ones
- which alter how the query modifies the table. It's good to replicate
- OPTION_RELAXED_UNIQUE_CHECKS too because otherwise, the slave may
- insert data slower than the master, in InnoDB.
- OPTION_BIG_SELECTS is not needed (the slave thread runs with
- max_join_size=HA_POS_ERROR) and OPTION_BIG_TABLES is not needed
- either, as the manual says (because a too big in-memory temp table is
- automatically written to disk).
+ The table map version internal to the log should be increased after
+ the event has been written to the binary log.
+ */
+#define LOG_EVENT_UPDATE_TABLE_MAP_VERSION_F 0x10
+
+/**
+ @def LOG_EVENT_ARTIFICIAL_F
+
+ Artificial events are created arbitarily and not written to binary
+ log
+
+ These events should not update the master log position when slave
+ SQL thread executes them.
*/
-#define OPTIONS_WRITTEN_TO_BIN_LOG (OPTION_AUTO_IS_NULL | \
-OPTION_NO_FOREIGN_KEY_CHECKS | OPTION_RELAXED_UNIQUE_CHECKS)
+#define LOG_EVENT_ARTIFICIAL_F 0x20
-#if OPTIONS_WRITTEN_TO_BIN_LOG != ((1L << 14) | (1L << 26) | (1L << 27))
+/**
+ @def LOG_EVENT_RELAY_LOG_F
+
+ Events with this flag set are created by slave IO thread and written
+ to relay log
+*/
+#define LOG_EVENT_RELAY_LOG_F 0x40
+
+/**
+ @def OPTIONS_WRITTEN_TO_BIN_LOG
+
+ OPTIONS_WRITTEN_TO_BIN_LOG are the bits of thd->options which must
+ be written to the binlog. OPTIONS_WRITTEN_TO_BIN_LOG could be
+ written into the Format_description_log_event, so that if later we
+ don't want to replicate a variable we did replicate, or the
+ contrary, it's doable. But it should not be too hard to decide once
+ for all of what we replicate and what we don't, among the fixed 32
+ bits of thd->options.
+
+ I (Guilhem) have read through every option's usage, and it looks
+ like OPTION_AUTO_IS_NULL and OPTION_NO_FOREIGN_KEYS are the only
+ ones which alter how the query modifies the table. It's good to
+ replicate OPTION_RELAXED_UNIQUE_CHECKS too because otherwise, the
+ slave may insert data slower than the master, in InnoDB.
+ OPTION_BIG_SELECTS is not needed (the slave thread runs with
+ max_join_size=HA_POS_ERROR) and OPTION_BIG_TABLES is not needed
+ either, as the manual says (because a too big in-memory temp table
+ is automatically written to disk).
+*/
+#define OPTIONS_WRITTEN_TO_BIN_LOG \
+ (OPTION_AUTO_IS_NULL | OPTION_NO_FOREIGN_KEY_CHECKS | \
+ OPTION_RELAXED_UNIQUE_CHECKS | OPTION_NOT_AUTOCOMMIT)
+
+/* Shouldn't be defined before */
+#define EXPECTED_OPTIONS \
+ ((ULL(1) << 14) | (ULL(1) << 26) | (ULL(1) << 27) | (ULL(1) << 19))
+
+#if OPTIONS_WRITTEN_TO_BIN_LOG != EXPECTED_OPTIONS
#error OPTIONS_WRITTEN_TO_BIN_LOG must NOT change their values!
#endif
+#undef EXPECTED_OPTIONS /* You shouldn't use this one */
+/**
+ @enum Log_event_type
+
+ Enumeration type for the different types of log events.
+*/
enum Log_event_type
{
/*
@@ -449,6 +550,28 @@ enum Log_event_type
BEGIN_LOAD_QUERY_EVENT= 17,
EXECUTE_LOAD_QUERY_EVENT= 18,
+ TABLE_MAP_EVENT = 19,
+
+ /*
+ These event numbers were used for 5.1.0 to 5.1.15 and are
+ therefore obsolete.
+ */
+ PRE_GA_WRITE_ROWS_EVENT = 20,
+ PRE_GA_UPDATE_ROWS_EVENT = 21,
+ PRE_GA_DELETE_ROWS_EVENT = 22,
+
+ /*
+ These event numbers are used from 5.1.16 and forward
+ */
+ WRITE_ROWS_EVENT = 23,
+ UPDATE_ROWS_EVENT = 24,
+ DELETE_ROWS_EVENT = 25,
+
+ /*
+ Something out of the ordinary happened on the master
+ */
+ INCIDENT_EVENT= 26,
+
/*
Add new events here - right above this comment!
Existing events (except ENUM_END_EVENT) should never change their numbers
@@ -472,15 +595,24 @@ enum Int_event_type
#ifndef MYSQL_CLIENT
class String;
-class MYSQL_LOG;
+class MYSQL_BIN_LOG;
class THD;
#endif
class Format_description_log_event;
-
-struct st_relay_log_info;
+class Relay_log_info;
#ifdef MYSQL_CLIENT
+enum enum_base64_output_mode {
+ BASE64_OUTPUT_NEVER= 0,
+ BASE64_OUTPUT_AUTO= 1,
+ BASE64_OUTPUT_ALWAYS= 2,
+ BASE64_OUTPUT_UNSPEC= 3,
+ BASE64_OUTPUT_DECODE_ROWS= 4,
+ /* insert new output modes here */
+ BASE64_OUTPUT_MODE_COUNT
+};
+
/*
A structure for mysqlbinlog to know how to print events
@@ -497,8 +629,9 @@ typedef struct st_print_event_info
{
/*
Settings for database, sql_mode etc that comes from the last event
- that was printed.
- */
+ that was printed. We cache these so that we don't have to print
+ them if they are unchanged.
+ */
// TODO: have the last catalog here ??
char db[FN_REFLEN+1]; // TODO: make this a LEX_STRING when thd->db is
bool flags2_inited;
@@ -511,42 +644,209 @@ typedef struct st_print_event_info
char time_zone_str[MAX_TIME_ZONE_NAME_LENGTH];
uint lc_time_names_number;
uint charset_database_number;
- st_print_event_info()
- :flags2_inited(0), sql_mode_inited(0),
- auto_increment_increment(1),auto_increment_offset(1), charset_inited(0),
- lc_time_names_number(0), charset_database_number(0)
- {
- /*
- Currently we only use static PRINT_EVENT_INFO objects, so zeroed at
- program's startup, but these explicit bzero() is for the day someone
- creates dynamic instances.
- */
- bzero(db, sizeof(db));
- bzero(charset, sizeof(charset));
- bzero(time_zone_str, sizeof(time_zone_str));
- strcpy(delimiter, ";");
- }
+ uint thread_id;
+ bool thread_id_printed;
+
+ st_print_event_info();
+
+ ~st_print_event_info() {
+ close_cached_file(&head_cache);
+ close_cached_file(&body_cache);
+ }
+ bool init_ok() /* tells if construction was successful */
+ { return my_b_inited(&head_cache) && my_b_inited(&body_cache); }
+
/* Settings on how to print the events */
bool short_form;
+ enum_base64_output_mode base64_output_mode;
+ /*
+ This is set whenever a Format_description_event is printed.
+ Later, when an event is printed in base64, this flag is tested: if
+ no Format_description_event has been seen, it is unsafe to print
+ the base64 event, so an error message is generated.
+ */
+ bool printed_fd_event;
my_off_t hexdump_from;
uint8 common_header_len;
char delimiter[16];
+#ifdef MYSQL_CLIENT
+ uint verbose;
+ table_mapping m_table_map;
+#endif
+
+ /*
+ These two caches are used by the row-based replication events to
+ collect the header information and the main body of the events
+ making up a statement.
+ */
+ IO_CACHE head_cache;
+ IO_CACHE body_cache;
} PRINT_EVENT_INFO;
#endif
-/*****************************************************************************
-
- Log_event class
+/**
+ @class Log_event
This is the abstract base class for binary log events.
-
- ****************************************************************************/
+
+ @section Log_event_binary_format Binary Format
+
+ Any @c Log_event saved on disk consists of the following three
+ components.
+
+ - Common-Header
+ - Post-Header
+ - Body
+
+ The Common-Header, documented in the table @ref Table_common_header
+ "below", always has the same form and length within one version of
+ MySQL. Each event type specifies a format and length of the
+ Post-Header. The length of the Common-Header is the same for all
+ events of the same type. The Body may be of different format and
+ length even for different events of the same type. The binary
+ formats of Post-Header and Body are documented separately in each
+ subclass. The binary format of Common-Header is as follows.
+
+ <table>
+ <caption>Common-Header</caption>
+
+ <tr>
+ <th>Name</th>
+ <th>Format</th>
+ <th>Description</th>
+ </tr>
+
+ <tr>
+ <td>timestamp</td>
+ <td>4 byte unsigned integer</td>
+ <td>The time when the query started, in seconds since 1970.
+ </td>
+ </tr>
+
+ <tr>
+ <td>type</td>
+ <td>1 byte enumeration</td>
+ <td>See enum #Log_event_type.</td>
+ </tr>
+
+ <tr>
+ <td>server_id</td>
+ <td>4 byte unsigned integer</td>
+ <td>Server ID of the server that created the event.</td>
+ </tr>
+
+ <tr>
+ <td>total_size</td>
+ <td>4 byte unsigned integer</td>
+ <td>The total size of this event, in bytes. In other words, this
+ is the sum of the sizes of Common-Header, Post-Header, and Body.
+ </td>
+ </tr>
+
+ <tr>
+ <td>master_position</td>
+ <td>4 byte unsigned integer</td>
+ <td>The position of the next event in the master binary log, in
+ bytes from the beginning of the file. In a binlog that is not a
+ relay log, this is just the position of the next event, in bytes
+ from the beginning of the file. In a relay log, this is
+ the position of the next event in the master's binlog.
+ </td>
+ </tr>
+
+ <tr>
+ <td>flags</td>
+ <td>2 byte bitfield</td>
+ <td>See Log_event::flags.</td>
+ </tr>
+ </table>
+
+ Summing up the numbers above, we see that the total size of the
+ common header is 19 bytes.
+
+ @subsection Log_event_format_of_atomic_primitives Format of Atomic Primitives
+
+ - All numbers, whether they are 16-, 24-, 32-, or 64-bit numbers,
+ are stored in little endian, i.e., the least significant byte first,
+ unless otherwise specified.
+
+ @anchor packed_integer
+ - Some events use a special format for efficient representation of
+ unsigned integers, called Packed Integer. A Packed Integer has the
+ capacity of storing up to 8-byte integers, while small integers
+ still can use 1, 3, or 4 bytes. The value of the first byte
+ determines how to read the number, according to the following table:
+
+ <table>
+ <caption>Format of Packed Integer</caption>
+
+ <tr>
+ <th>First byte</th>
+ <th>Format</th>
+ </tr>
+
+ <tr>
+ <td>0-250</td>
+ <td>The first byte is the number (in the range 0-250), and no more
+ bytes are used.</td>
+ </tr>
+
+ <tr>
+ <td>252</td>
+ <td>Two more bytes are used. The number is in the range
+ 251-0xffff.</td>
+ </tr>
+
+ <tr>
+ <td>253</td>
+ <td>Three more bytes are used. The number is in the range
+ 0xffff-0xffffff.</td>
+ </tr>
+
+ <tr>
+ <td>254</td>
+ <td>Eight more bytes are used. The number is in the range
+ 0xffffff-0xffffffffffffffff.</td>
+ </tr>
+
+ </table>
+
+ - Strings are stored in various formats. The format of each string
+ is documented separately.
+*/
class Log_event
{
public:
+ /**
+ Enumeration of what kinds of skipping (and non-skipping) that can
+ occur when the slave executes an event.
+
+ @see shall_skip
+ @see do_shall_skip
+ */
+ enum enum_skip_reason {
+ /**
+ Don't skip event.
+ */
+ EVENT_SKIP_NOT,
+
+ /**
+ Skip event by ignoring it.
+
+ This means that the slave skip counter will not be changed.
+ */
+ EVENT_SKIP_IGNORE,
+
+ /**
+ Skip event and decrease skip counter.
+ */
+ EVENT_SKIP_COUNT
+ };
+
+
/*
The following type definition is to be used whenever data is placed
and manipulated in a common buffer. Use this typedef for buffers
@@ -590,8 +890,8 @@ public:
*/
uint32 server_id;
- /*
- Some 16 flags. Look above for LOG_EVENT_TIME_F,
+ /**
+ 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.
*/
@@ -599,6 +899,12 @@ public:
bool cache_stmt;
+ /**
+ A storage to cache the global system variable's value.
+ Handling of a separate event will be governed its member.
+ */
+ ulong slave_exec_mode;
+
#ifndef MYSQL_CLIENT
THD* thd;
@@ -617,7 +923,8 @@ public:
*/
static Log_event* read_log_event(IO_CACHE* file,
pthread_mutex_t* log_lock,
- const Format_description_log_event *description_event);
+ const Format_description_log_event
+ *description_event);
static int read_log_event(IO_CACHE* file, String* packet,
pthread_mutex_t* log_lock);
/*
@@ -628,16 +935,14 @@ public:
static void init_show_field_list(List<Item>* field_list);
#ifdef HAVE_REPLICATION
int net_send(Protocol *protocol, const char* log_name, my_off_t pos);
+
/*
pack_info() is used by SHOW BINLOG EVENTS; as print() it prepares and sends
a string to display to the user, so it resembles print().
*/
+
virtual void pack_info(Protocol *protocol);
- /*
- The SQL slave thread calls exec_event() to execute the event; this is where
- the slave's data is modified.
- */
- virtual int exec_event(struct st_relay_log_info* rli);
+
#endif /* HAVE_REPLICATION */
virtual const char* get_db()
{
@@ -647,22 +952,31 @@ public:
Log_event() : temp_buf(0) {}
/* avoid having to link mysqlbinlog against libpthread */
static Log_event* read_log_event(IO_CACHE* file,
- const Format_description_log_event *description_event);
+ const Format_description_log_event
+ *description_event);
/* print*() functions are used by mysqlbinlog */
- virtual void print(FILE* file, PRINT_EVENT_INFO* print_event_info= 0) = 0;
- void print_timestamp(FILE* file, time_t *ts = 0);
- void print_header(FILE* file, PRINT_EVENT_INFO* print_event_info= 0);
+ virtual void print(FILE* file, PRINT_EVENT_INFO* print_event_info) = 0;
+ void print_timestamp(IO_CACHE* file, time_t *ts = 0);
+ void print_header(IO_CACHE* file, PRINT_EVENT_INFO* print_event_info,
+ bool is_more);
+ void print_base64(IO_CACHE* file, PRINT_EVENT_INFO* print_event_info,
+ bool is_more);
#endif
static void *operator new(size_t size)
{
return (void*) my_malloc((uint)size, MYF(MY_WME|MY_FAE));
}
+
static void operator delete(void *ptr, size_t size)
{
- my_free((gptr) ptr, MYF(MY_WME|MY_ALLOW_ZERO_PTR));
+ my_free((uchar*) ptr, MYF(MY_WME|MY_ALLOW_ZERO_PTR));
}
+ /* Placement version of the above operators */
+ static void *operator new(size_t, void* ptr) { return ptr; }
+ static void operator delete(void*, void*) { }
+
#ifndef MYSQL_CLIENT
bool write_header(IO_CACHE* file, ulong data_length);
virtual bool write(IO_CACHE* file)
@@ -675,12 +989,27 @@ public:
{ return 0; }
virtual bool write_data_body(IO_CACHE* file __attribute__((unused)))
{ return 0; }
+ inline time_t get_time()
+ {
+ THD *tmp_thd;
+ if (when)
+ return when;
+ if (thd)
+ return thd->start_time;
+ if ((tmp_thd= current_thd))
+ return tmp_thd->start_time;
+ return my_time(0);
+ }
#endif
virtual Log_event_type get_type_code() = 0;
virtual bool is_valid() const = 0;
- virtual bool is_artificial_event() { return 0; }
- inline bool get_cache_stmt() { return cache_stmt; }
- Log_event(const char* buf, const Format_description_log_event* description_event);
+ void set_artificial_event() { flags |= LOG_EVENT_ARTIFICIAL_F; }
+ void set_relay_log_event() { flags |= LOG_EVENT_RELAY_LOG_F; }
+ bool is_artificial_event() const { return flags & LOG_EVENT_ARTIFICIAL_F; }
+ bool is_relay_log_event() const { return flags & LOG_EVENT_RELAY_LOG_F; }
+ inline bool get_cache_stmt() const { return cache_stmt; }
+ Log_event(const char* buf, const Format_description_log_event
+ *description_event);
virtual ~Log_event() { free_temp_buf();}
void register_temp_buf(char* buf) { temp_buf = buf; }
void free_temp_buf()
@@ -700,10 +1029,160 @@ public:
const char **error,
const Format_description_log_event
*description_event);
- /* returns the human readable name of the event's type */
+ /**
+ Returns the human readable name of the given event type.
+ */
+ static const char* get_type_str(Log_event_type type);
+ /**
+ Returns the human readable name of this event's type.
+ */
const char* get_type_str();
+
+ /* Return start of query time or current time */
+
+#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
+public:
+
+ /**
+ Apply the event to the database.
+
+ This function represents the public interface for applying an
+ event.
+
+ @see do_apply_event
+ */
+ int apply_event(Relay_log_info const *rli)
+ {
+ return do_apply_event(rli);
+ }
+
+
+ /**
+ Update the relay log position.
+
+ This function represents the public interface for "stepping over"
+ the event and will update the relay log information.
+
+ @see do_update_pos
+ */
+ int update_pos(Relay_log_info *rli)
+ {
+ return do_update_pos(rli);
+ }
+
+ /**
+ Decide if the event shall be skipped, and the reason for skipping
+ it.
+
+ @see do_shall_skip
+ */
+ enum_skip_reason shall_skip(Relay_log_info *rli)
+ {
+ return do_shall_skip(rli);
+ }
+
+protected:
+
+ /**
+ Helper function to ignore an event w.r.t. the slave skip counter.
+
+ This function can be used inside do_shall_skip() for functions
+ that cannot end a group. If the slave skip counter is 1 when
+ seeing such an event, the event shall be ignored, the counter
+ left intact, and processing continue with the next event.
+
+ A typical usage is:
+ @code
+ enum_skip_reason do_shall_skip(Relay_log_info *rli) {
+ return continue_group(rli);
+ }
+ @endcode
+
+ @return Skip reason
+ */
+ enum_skip_reason continue_group(Relay_log_info *rli);
+
+ /**
+ Primitive to apply an event to the database.
+
+ This is where the change to the database is made.
+
+ @note The primitive is protected instead of private, since there
+ is a hierarchy of actions to be performed in some cases.
+
+ @see Format_description_log_event::do_apply_event()
+
+ @param rli Pointer to relay log info structure
+
+ @retval 0 Event applied successfully
+ @retval errno Error code if event application failed
+ */
+ virtual int do_apply_event(Relay_log_info const *rli)
+ {
+ return 0; /* Default implementation does nothing */
+ }
+
+
+ /**
+ Advance relay log coordinates.
+
+ This function is called to advance the relay log coordinates to
+ just after the event. It is essential that both the relay log
+ coordinate and the group log position is updated correctly, since
+ this function is used also for skipping events.
+
+ Normally, each implementation of do_update_pos() shall:
+
+ - Update the event position to refer to the position just after
+ the event.
+
+ - Update the group log position to refer to the position just
+ after the event <em>if the event is last in a group</em>
+
+ @param rli Pointer to relay log info structure
+
+ @retval 0 Coordinates changed successfully
+ @retval errno Error code if advancing failed (usually just
+ 1). Observe that handler errors are returned by the
+ do_apply_event() function, and not by this one.
+ */
+ virtual int do_update_pos(Relay_log_info *rli);
+
+
+ /**
+ Decide if this event shall be skipped or not and the reason for
+ skipping it.
+
+ The default implementation decide that the event shall be skipped
+ if either:
+
+ - the server id of the event is the same as the server id of the
+ server and <code>rli->replicate_same_server_id</code> is true,
+ or
+
+ - if <code>rli->slave_skip_counter</code> is greater than zero.
+
+ @see do_apply_event
+ @see do_update_pos
+
+ @retval Log_event::EVENT_SKIP_NOT
+ The event shall not be skipped and should be applied.
+
+ @retval Log_event::EVENT_SKIP_IGNORE
+ The event shall be skipped by just ignoring it, i.e., the slave
+ skip counter shall not be changed. This happends if, for example,
+ the originating server id of the event is the same as the server
+ id of the slave.
+
+ @retval Log_event::EVENT_SKIP_COUNT
+ The event shall be skipped because the slave skip counter was
+ non-zero. The caller shall decrease the counter by one.
+ */
+ virtual enum_skip_reason do_shall_skip(Relay_log_info *rli);
+#endif
};
+
/*
One class for each type of event.
Two constructors for each class:
@@ -717,13 +1196,346 @@ public:
mysqlbinlog. This constructor must be format-tolerant.
*/
-/*****************************************************************************
-
- Query Log Event class
-
- Logs SQL queries
+/**
+ @class Query_log_event
+
+ A @c Query_log_event is created for each query that modifies the
+ database, unless the query is logged row-based.
+
+ @section Query_log_event_binary_format Binary format
+
+ See @ref Log_event_binary_format "Binary format for log events" for
+ a general discussion and introduction to the binary format of binlog
+ events.
+
+ The Post-Header has five components:
+
+ <table>
+ <caption>Post-Header for Query_log_event</caption>
+
+ <tr>
+ <th>Name</th>
+ <th>Format</th>
+ <th>Description</th>
+ </tr>
+
+ <tr>
+ <td>slave_proxy_id</td>
+ <td>4 byte unsigned integer</td>
+ <td>An integer identifying the client thread that issued the
+ query. The id is unique per server. (Note, however, that two
+ threads on different servers may have the same slave_proxy_id.)
+ This is used when a client thread creates a temporary table local
+ to the client. The slave_proxy_id is used to distinguish
+ temporary tables that belong to different clients.
+ </td>
+ </tr>
+
+ <tr>
+ <td>exec_time</td>
+ <td>4 byte unsigned integer</td>
+ <td>The time from when the query started to when it was logged in
+ the binlog, in seconds.</td>
+ </tr>
+
+ <tr>
+ <td>db_len</td>
+ <td>1 byte integer</td>
+ <td>The length of the name of the currently selected database.</td>
+ </tr>
+
+ <tr>
+ <td>error_code</td>
+ <td>2 byte unsigned integer</td>
+ <td>Error code generated by the master. If the master fails, the
+ slave will fail with the same error code, except for the error
+ codes ER_DB_CREATE_EXISTS == 1007 and ER_DB_DROP_EXISTS == 1008.
+ </td>
+ </tr>
+
+ <tr>
+ <td>status_vars_len</td>
+ <td>2 byte unsigned integer</td>
+ <td>The length of the status_vars block of the Body, in bytes. See
+ @ref query_log_event_status_vars "below".
+ </td>
+ </tr>
+ </table>
+
+ The Body has the following components:
+
+ <table>
+ <caption>Body for Query_log_event</caption>
+
+ <tr>
+ <th>Name</th>
+ <th>Format</th>
+ <th>Description</th>
+ </tr>
+
+ <tr>
+ <td>@anchor query_log_event_status_vars status_vars</td>
+ <td>status_vars_len bytes</td>
+ <td>Zero or more status variables. Each status variable consists
+ of one byte identifying the variable stored, followed by the value
+ of the variable. The possible variables are listed separately in
+ the table @ref Table_query_log_event_status_vars "below". MySQL
+ always writes events in the order defined below; however, it is
+ capable of reading them in any order. </td>
+ </tr>
+
+ <tr>
+ <td>db</td>
+ <td>db_len+1</td>
+ <td>The currently selected database, as a null-terminated string.
+
+ (The trailing zero is redundant since the length is already known;
+ it is db_len from Post-Header.)
+ </td>
+ </tr>
+
+ <tr>
+ <td>query</td>
+ <td>variable length string without trailing zero, extending to the
+ end of the event (determined by the length field of the
+ Common-Header)
+ </td>
+ <td>The SQL query.</td>
+ </tr>
+ </table>
+
+ The following table lists the status variables that may appear in
+ the status_vars field.
+
+ @anchor Table_query_log_event_status_vars
+ <table>
+ <caption>Status variables for Query_log_event</caption>
+
+ <tr>
+ <th>Status variable</th>
+ <th>1 byte identifier</th>
+ <th>Format</th>
+ <th>Description</th>
+ </tr>
+
+ <tr>
+ <td>flags2</td>
+ <td>Q_FLAGS2_CODE == 0</td>
+ <td>4 byte bitfield</td>
+ <td>The flags in @c thd->options, binary AND-ed with @c
+ OPTIONS_WRITTEN_TO_BIN_LOG. The @c thd->options bitfield contains
+ options for "SELECT". @c OPTIONS_WRITTEN identifies those options
+ that need to be written to the binlog (not all do). Specifically,
+ @c OPTIONS_WRITTEN_TO_BIN_LOG equals (@c OPTION_AUTO_IS_NULL | @c
+ OPTION_NO_FOREIGN_KEY_CHECKS | @c OPTION_RELAXED_UNIQUE_CHECKS |
+ @c OPTION_NOT_AUTOCOMMIT), or 0x0c084000 in hex.
+
+ These flags correspond to the SQL variables SQL_AUTO_IS_NULL,
+ FOREIGN_KEY_CHECKS, UNIQUE_CHECKS, and AUTOCOMMIT, documented in
+ the "SET Syntax" section of the MySQL Manual.
+
+ This field is always written to the binlog in version >= 5.0, and
+ never written in version < 5.0.
+ </td>
+ </tr>
+
+ <tr>
+ <td>sql_mode</td>
+ <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
+ flags. Currently (2007-10-04), the following flags are available:
+ <pre>
+ MODE_REAL_AS_FLOAT==0x1
+ MODE_PIPES_AS_CONCAT==0x2
+ MODE_ANSI_QUOTES==0x4
+ MODE_IGNORE_SPACE==0x8
+ MODE_NOT_USED==0x10
+ MODE_ONLY_FULL_GROUP_BY==0x20
+ MODE_NO_UNSIGNED_SUBTRACTION==0x40
+ MODE_NO_DIR_IN_CREATE==0x80
+ MODE_POSTGRESQL==0x100
+ MODE_ORACLE==0x200
+ MODE_MSSQL==0x400
+ MODE_DB2==0x800
+ MODE_MAXDB==0x1000
+ MODE_NO_KEY_OPTIONS==0x2000
+ MODE_NO_TABLE_OPTIONS==0x4000
+ MODE_NO_FIELD_OPTIONS==0x8000
+ MODE_MYSQL323==0x10000
+ MODE_MYSQL323==0x20000
+ MODE_MYSQL40==0x40000
+ MODE_ANSI==0x80000
+ MODE_NO_AUTO_VALUE_ON_ZERO==0x100000
+ MODE_NO_BACKSLASH_ESCAPES==0x200000
+ MODE_STRICT_TRANS_TABLES==0x400000
+ MODE_STRICT_ALL_TABLES==0x800000
+ MODE_NO_ZERO_IN_DATE==0x1000000
+ MODE_NO_ZERO_DATE==0x2000000
+ MODE_INVALID_DATES==0x4000000
+ MODE_ERROR_FOR_DIVISION_BY_ZERO==0x8000000
+ MODE_TRADITIONAL==0x10000000
+ MODE_NO_AUTO_CREATE_USER==0x20000000
+ MODE_HIGH_NOT_PRECEDENCE==0x40000000
+ MODE_PAD_CHAR_TO_FULL_LENGTH==0x80000000
+ </pre>
+ All these flags are replicated from the server. However, all
+ flags except @c MODE_NO_DIR_IN_CREATE are honored by the slave;
+ the slave always preserves its old value of @c
+ MODE_NO_DIR_IN_CREATE. For a rationale, see comment in
+ @c Query_log_event::do_apply_event in @c log_event.cc.
+
+ This field is always written to the binlog.
+ </td>
+ </tr>
+
+ <tr>
+ <td>catalog</td>
+ <td>Q_CATALOG_NZ_CODE == 6</td>
+ <td>Variable-length string: the length in bytes (1 byte) followed
+ by the characters (at most 255 bytes)
+ </td>
+ <td>Stores the client's current catalog. Every database belongs
+ to a catalog, the same way that every table belongs to a
+ database. Currently, there is only one catalog, "std".
+
+ This field is written if the length of the catalog is > 0;
+ otherwise it is not written.
+ </td>
+ </tr>
+
+ <tr>
+ <td>auto_increment</td>
+ <td>Q_AUTO_INCREMENT == 3</td>
+ <td>two 2 byte unsigned integers, totally 2+2=4 bytes</td>
+
+ <td>The two variables auto_increment_increment and
+ auto_increment_offset, in that order. For more information, see
+ "System variables" in the MySQL manual.
+
+ This field is written if auto_increment > 1. Otherwise, it is not
+ written.
+ </td>
+ </tr>
+
+ <tr>
+ <td>charset</td>
+ <td>Q_CHARSET_CODE == 4</td>
+ <td>three 2 byte unsigned integers, totally 2+2+2=6 bytes</td>
+ <td>The three variables character_set_client,
+ collation_connection, and collation_server, in that order.
+ character_set_client is a code identifying the character set and
+ collation used by the client to encode the query.
+ collation_connection identifies the character set and collation
+ that the master converts the query to when it receives it; this is
+ useful when comparing literal strings. collation_server is the
+ default character set and collation used when a new database is
+ created.
+
+ See also "Connection Character Sets and Collations" in the MySQL
+ 5.1 manual.
+
+ All three variables are codes identifying a (character set,
+ collation) pair. To see which codes map to which pairs, run the
+ query "SELECT id, character_set_name, collation_name FROM
+ COLLATIONS".
+
+ Cf. Q_CHARSET_DATABASE_CODE below.
+
+ This field is always written.
+ </td>
+ </tr>
+
+ <tr>
+ <td>time_zone</td>
+ <td>Q_TIME_ZONE_CODE == 5</td>
+ <td>Variable-length string: the length in bytes (1 byte) followed
+ by the characters (at most 255 bytes).
+ <td>The time_zone of the master.
+
+ See also "System Variables" and "MySQL Server Time Zone Support"
+ in the MySQL manual.
+
+ This field is written if the length of the time zone string is >
+ 0; otherwise, it is not written.
+ </td>
+ </tr>
+
+ <tr>
+ <td>lc_time_names_number</td>
+ <td>Q_LC_TIME_NAMES_CODE == 7</td>
+ <td>2 byte integer</td>
+ <td>A code identifying a table of month and day names. The
+ mapping from codes to languages is defined in @c sql_locale.cc.
+
+ This field is written if it is not 0, i.e., if the locale is not
+ en_US.
+ </td>
+ </tr>
+
+ <tr>
+ <td>charset_database_number</td>
+ <td>Q_CHARSET_DATABASE_CODE == 8</td>
+ <td>2 byte integer</td>
+
+ <td>The value of the collation_database system variable (in the
+ source code stored in @c thd->variables.collation_database), which
+ holds the code for a (character set, collation) pair as described
+ above (see Q_CHARSET_CODE).
+
+ collation_database was used in old versions (???WHEN). Its value
+ was loaded when issuing a "use db" query and could be changed by
+ issuing a "SET collation_database=xxx" query. It used to affect
+ the "LOAD DATA INFILE" and "CREATE TABLE" commands.
+
+ In newer versions, "CREATE TABLE" has been changed to take the
+ character set from the database of the created table, rather than
+ the character set of the current database. This makes a
+ difference when creating a table in another database than the
+ current one. "LOAD DATA INFILE" has not yet changed to do this,
+ but there are plans to eventually do it, and to make
+ collation_database read-only.
+
+ This field is written if it is not 0.
+ </td>
+ </tr>
+ <tr>
+ <td>table_map_for_update</td>
+ <td>Q_TABLE_MAP_FOR_UPDATE_CODE == 9</td>
+ <td>8 byte integer</td>
+
+ <td>The value of the table map that is to be updated by the
+ multi-table update query statement. Every bit of this variable
+ represents a table, and is set to 1 if the corresponding table is
+ to be updated by this statement.
+
+ The value of this variable is set when executing a multi-table update
+ statement and used by slave to apply filter rules without opening
+ all the tables on slave. This is required because some tables may
+ not exist on slave because of the filter rules.
+ </td>
+ </tr>
+ </table>
+
+ @subsection Query_log_event_notes_on_previous_versions Notes on Previous Versions
+
+ * Status vars were introduced in version 5.0. To read earlier
+ versions correctly, check the length of the Post-Header.
+
+ * The status variable Q_CATALOG_CODE == 2 existed in MySQL 5.0.x,
+ where 0<=x<=3. It was identical to Q_CATALOG_CODE, except that the
+ string had a trailing '\0'. The '\0' was removed in 5.0.4 since it
+ was redundant (the string length is stored before the string). The
+ Q_CATALOG_CODE will never be written by a new master, but can still
+ be understood by a new slave.
+
+ * See Q_CHARSET_DATABASE_CODE in the table above.
+
+ * When adding new status vars, please don't forget to update the
+ MAX_SIZE_LOG_EVENT_STATUS, and update function code_name
- ****************************************************************************/
+*/
class Query_log_event: public Log_event
{
protected:
@@ -742,10 +1554,10 @@ public:
uint16 error_code;
ulong thread_id;
/*
- For events created by Query_log_event::exec_event (and
- Load_log_event::exec_event()) we need the *original* thread id, to be able
- to log the event with the original (=master's) thread id (fix for
- BUG#1686).
+ For events created by Query_log_event::do_apply_event (and
+ Load_log_event::do_apply_event()) we need the *original* thread
+ id, to be able to log the event with the original (=master's)
+ thread id (fix for BUG#1686).
*/
ulong slave_proxy_id;
@@ -781,7 +1593,7 @@ public:
/*
'flags2' is a second set of flags (on top of those in Log_event), for
session variables. These are thd->options which is & against a mask
- (OPTIONS_WRITTEN_TO_BINLOG).
+ (OPTIONS_WRITTEN_TO_BIN_LOG).
flags2_inited helps make a difference between flags2==0 (3.23 or 4.x
master, we don't know flags2, so use the slave server's global options) and
flags2==0 (5.0 master, we know this has a meaning of flags all down which
@@ -800,6 +1612,11 @@ public:
const char *time_zone_str;
uint lc_time_names_number; /* 0 means en_US */
uint charset_database_number;
+ /*
+ map for tables that will be updated for a multi-table update query
+ statement, for other query statements, this will be zero.
+ */
+ ulonglong table_map_for_update;
#ifndef MYSQL_CLIENT
@@ -809,13 +1626,10 @@ public:
const char* get_db() { return db; }
#ifdef HAVE_REPLICATION
void pack_info(Protocol* protocol);
- int exec_event(struct st_relay_log_info* rli);
- int exec_event(struct st_relay_log_info* rli, const char *query_arg,
- uint32 q_len_arg);
#endif /* HAVE_REPLICATION */
#else
- void print_query_header(FILE* file, PRINT_EVENT_INFO* print_event_info= 0);
- void print(FILE* file, PRINT_EVENT_INFO* print_event_info= 0);
+ void print_query_header(IO_CACHE* file, PRINT_EVENT_INFO* print_event_info);
+ void print(FILE* file, PRINT_EVENT_INFO* print_event_info);
#endif
Query_log_event();
@@ -825,7 +1639,7 @@ public:
~Query_log_event()
{
if (data_buf)
- my_free((gptr) data_buf, MYF(0));
+ my_free((uchar*) data_buf, MYF(0));
}
Log_event_type get_type_code() { return QUERY_EVENT; }
#ifndef MYSQL_CLIENT
@@ -840,40 +1654,70 @@ public:
*/
virtual ulong get_post_header_size_for_derived() { return 0; }
/* Writes derived event-specific part of post header. */
-};
-
-
-/*****************************************************************************
-
- Muted Query Log Event class
- Pretends to Log SQL queries, but doesn't actually do so.
+public: /* !!! Public in this patch to allow old usage */
+#if !defined(MYSQL_CLIENT) && 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);
- ****************************************************************************/
-class Muted_query_log_event: public Query_log_event
-{
-public:
-#ifndef MYSQL_CLIENT
- Muted_query_log_event();
-
- bool write(IO_CACHE* file) { return(false); };
- virtual bool write_post_header_for_derived(IO_CACHE* file) { return FALSE; }
-#else
- Muted_query_log_event() {}
-#endif
+ int do_apply_event(Relay_log_info const *rli,
+ const char *query_arg,
+ uint32 q_len_arg);
+#endif /* HAVE_REPLICATION */
};
#ifdef HAVE_REPLICATION
-/*****************************************************************************
+/**
+ @class Slave_log_event
- Slave Log Event class
Note that this class is currently not used at all; no code writes a
- Slave_log_event (though some code in repl_failsafe.cc reads Slave_log_event).
- So it's not a problem if this code is not maintained.
-
- ****************************************************************************/
+ @c Slave_log_event (though some code in @c repl_failsafe.cc reads @c
+ Slave_log_event). So it's not a problem if this code is not
+ maintained.
+
+ @section Slave_log_event_binary_format Binary Format
+
+ This event type has no Post-Header. The Body has the following
+ four components.
+
+ <table>
+ <caption>Body for Slave_log_event</caption>
+
+ <tr>
+ <th>Name</th>
+ <th>Format</th>
+ <th>Description</th>
+ </tr>
+
+ <tr>
+ <td>master_pos</td>
+ <td>8 byte integer</td>
+ <td>???TODO
+ </td>
+ </tr>
+
+ <tr>
+ <td>master_port</td>
+ <td>2 byte integer</td>
+ <td>???TODO</td>
+ </tr>
+
+ <tr>
+ <td>master_host</td>
+ <td>null-terminated string</td>
+ <td>???TODO</td>
+ </tr>
+
+ <tr>
+ <td>master_log</td>
+ <td>null-terminated string</td>
+ <td>???TODO</td>
+ </tr>
+ </table>
+*/
class Slave_log_event: public Log_event
{
protected:
@@ -888,11 +1732,10 @@ public:
uint16 master_port;
#ifndef MYSQL_CLIENT
- Slave_log_event(THD* thd_arg, struct st_relay_log_info* rli);
+ Slave_log_event(THD* thd_arg, Relay_log_info* rli);
void pack_info(Protocol* protocol);
- int exec_event(struct st_relay_log_info* rli);
#else
- void print(FILE* file, PRINT_EVENT_INFO* print_event_info= 0);
+ void print(FILE* file, PRINT_EVENT_INFO* print_event_info);
#endif
Slave_log_event(const char* buf, uint event_len);
@@ -903,16 +1746,215 @@ public:
#ifndef MYSQL_CLIENT
bool write(IO_CACHE* file);
#endif
+
+private:
+#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
+ virtual int do_apply_event(Relay_log_info const* rli);
+#endif
};
#endif /* HAVE_REPLICATION */
-/*****************************************************************************
-
- Load Log Event class
-
- ****************************************************************************/
+/**
+ @class Load_log_event
+
+ This log event corresponds to a "LOAD DATA INFILE" SQL query on the
+ following form:
+
+ @verbatim
+ (1) USE db;
+ (2) LOAD DATA [LOCAL] INFILE 'file_name'
+ (3) [REPLACE | IGNORE]
+ (4) INTO TABLE 'table_name'
+ (5) [FIELDS
+ (6) [TERMINATED BY 'field_term']
+ (7) [[OPTIONALLY] ENCLOSED BY 'enclosed']
+ (8) [ESCAPED BY 'escaped']
+ (9) ]
+ (10) [LINES
+ (11) [TERMINATED BY 'line_term']
+ (12) [LINES STARTING BY 'line_start']
+ (13) ]
+ (14) [IGNORE skip_lines LINES]
+ (15) (field_1, field_2, ..., field_n)@endverbatim
+
+ @section Load_log_event_binary_format Binary Format
+
+ The Post-Header consists of the following six components.
+
+ <table>
+ <caption>Post-Header for Load_log_event</caption>
+
+ <tr>
+ <th>Name</th>
+ <th>Format</th>
+ <th>Description</th>
+ </tr>
+
+ <tr>
+ <td>slave_proxy_id</td>
+ <td>4 byte unsigned integer</td>
+ <td>An integer identifying the client thread that issued the
+ query. The id is unique per server. (Note, however, that two
+ threads on different servers may have the same slave_proxy_id.)
+ This is used when a client thread creates a temporary table local
+ to the client. The slave_proxy_id is used to distinguish
+ temporary tables that belong to different clients.
+ </td>
+ </tr>
+
+ <tr>
+ <td>exec_time</td>
+ <td>4 byte unsigned integer</td>
+ <td>The time from when the query started to when it was logged in
+ the binlog, in seconds.</td>
+ </tr>
+
+ <tr>
+ <td>skip_lines</td>
+ <td>4 byte unsigned integer</td>
+ <td>The number on line (14) above, if present, or 0 if line (14)
+ is left out.
+ </td>
+ </tr>
+
+ <tr>
+ <td>table_name_len</td>
+ <td>1 byte unsigned integer</td>
+ <td>The length of 'table_name' on line (4) above.</td>
+ </tr>
+
+ <tr>
+ <td>db_len</td>
+ <td>1 byte unsigned integer</td>
+ <td>The length of 'db' on line (1) above.</td>
+ </tr>
+
+ <tr>
+ <td>num_fields</td>
+ <td>4 byte unsigned integer</td>
+ <td>The number n of fields on line (15) above.</td>
+ </tr>
+ </table>
+
+ The Body contains the following components.
+
+ <table>
+ <caption>Body of Load_log_event</caption>
+
+ <tr>
+ <th>Name</th>
+ <th>Format</th>
+ <th>Description</th>
+ </tr>
+
+ <tr>
+ <td>sql_ex</td>
+ <td>variable length</td>
+
+ <td>Describes the part of the query on lines (3) and
+ (5)&ndash;(13) above. More precisely, it stores the five strings
+ (on lines) field_term (6), enclosed (7), escaped (8), line_term
+ (11), and line_start (12); as well as a bitfield indicating the
+ presence of the keywords REPLACE (3), IGNORE (3), and OPTIONALLY
+ (7).
+
+ The data is stored in one of two formats, called "old" and "new".
+ The type field of Common-Header determines which of these two
+ formats is used: type LOAD_EVENT means that the old format is
+ used, and type NEW_LOAD_EVENT means that the new format is used.
+ When MySQL writes a Load_log_event, it uses the new format if at
+ least one of the five strings is two or more bytes long.
+ Otherwise (i.e., if all strings are 0 or 1 bytes long), the old
+ format is used.
+
+ The new and old format differ in the way the five strings are
+ stored.
+
+ <ul>
+ <li> In the new format, the strings are stored in the order
+ field_term, enclosed, escaped, line_term, line_start. Each string
+ consists of a length (1 byte), followed by a sequence of
+ characters (0-255 bytes). Finally, a boolean combination of the
+ following flags is stored in 1 byte: REPLACE_FLAG==0x4,
+ IGNORE_FLAG==0x8, and OPT_ENCLOSED_FLAG==0x2. If a flag is set,
+ it indicates the presence of the corresponding keyword in the SQL
+ query.
+
+ <li> In the old format, we know that each string has length 0 or
+ 1. Therefore, only the first byte of each string is stored. The
+ order of the strings is the same as in the new format. These five
+ bytes are followed by the same 1 byte bitfield as in the new
+ format. Finally, a 1 byte bitfield called empty_flags is stored.
+ The low 5 bits of empty_flags indicate which of the five strings
+ have length 0. For each of the following flags that is set, the
+ corresponding string has length 0; for the flags that are not set,
+ the string has length 1: FIELD_TERM_EMPTY==0x1,
+ ENCLOSED_EMPTY==0x2, LINE_TERM_EMPTY==0x4, LINE_START_EMPTY==0x8,
+ ESCAPED_EMPTY==0x10.
+ </ul>
+
+ Thus, the size of the new format is 6 bytes + the sum of the sizes
+ of the five strings. The size of the old format is always 7
+ bytes.
+ </td>
+ </tr>
+
+ <tr>
+ <td>field_lens</td>
+ <td>num_fields 1 byte unsigned integers</td>
+ <td>An array of num_fields integers representing the length of
+ each field in the query. (num_fields is from the Post-Header).
+ </td>
+ </tr>
+
+ <tr>
+ <td>fields</td>
+ <td>num_fields null-terminated strings</td>
+ <td>An array of num_fields null-terminated strings, each
+ representing a field in the query. (The trailing zero is
+ redundant, since the length are stored in the num_fields array.)
+ The total length of all strings equals to the sum of all
+ field_lens, plus num_fields bytes for all the trailing zeros.
+ </td>
+ </tr>
+
+ <tr>
+ <td>table_name</td>
+ <td>null-terminated string of length table_len+1 bytes</td>
+ <td>The 'table_name' from the query, as a null-terminated string.
+ (The trailing zero is actually redundant since the table_len is
+ known from Post-Header.)
+ </td>
+ </tr>
+
+ <tr>
+ <td>db</td>
+ <td>null-terminated string of length db_len+1 bytes</td>
+ <td>The 'db' from the query, as a null-terminated string.
+ (The trailing zero is actually redundant since the db_len is known
+ from Post-Header.)
+ </td>
+ </tr>
+
+ <tr>
+ <td>file_name</td>
+ <td>variable length string without trailing zero, extending to the
+ end of the event (determined by the length field of the
+ Common-Header)
+ </td>
+ <td>The 'file_name' from the query.
+ </td>
+ </tr>
+
+ </table>
+
+ @subsection Load_log_event_notes_on_previous_versions Notes on Previous Versions
+
+ This event type is understood by current versions, but only
+ generated by MySQL 3.23 and earlier.
+*/
class Load_log_event: public Log_event
{
private:
@@ -921,7 +1963,8 @@ private:
char **fn_start, char **fn_end);
protected:
int copy_log_event(const char *buf, ulong event_len,
- int body_offset, const Format_description_log_event* description_event);
+ int body_offset,
+ const Format_description_log_event* description_event);
public:
ulong thread_id;
@@ -972,15 +2015,9 @@ public:
const char* get_db() { return db; }
#ifdef HAVE_REPLICATION
void pack_info(Protocol* protocol);
- int exec_event(struct st_relay_log_info* rli)
- {
- return exec_event(thd->slave_net,rli,0);
- }
- int exec_event(NET* net, struct st_relay_log_info* rli,
- bool use_rli_only_for_errors);
#endif /* HAVE_REPLICATION */
#else
- void print(FILE* file, PRINT_EVENT_INFO* print_event_info = 0);
+ void print(FILE* file, PRINT_EVENT_INFO* print_event_info);
void print(FILE* file, PRINT_EVENT_INFO* print_event_info, bool commented);
#endif
@@ -1009,24 +2046,36 @@ public:
+ LOAD_HEADER_LEN
+ sql_ex.data_size() + field_block_len + num_fields);
}
+
+public: /* !!! Public in this patch to allow old usage */
+#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
+ virtual int do_apply_event(Relay_log_info const* rli)
+ {
+ return do_apply_event(thd->slave_net,rli,0);
+ }
+
+ int do_apply_event(NET *net, Relay_log_info const *rli,
+ bool use_rli_only_for_errors);
+#endif
};
extern char server_version[SERVER_VERSION_LENGTH];
-/*****************************************************************************
-
- Start Log Event_v3 class
+/**
+ @class Start_log_event_v3
Start_log_event_v3 is the Start_log_event of binlog format 3 (MySQL 3.23 and
4.x).
- Format_description_log_event derives from Start_log_event_v3; it is the
- Start_log_event of binlog format 4 (MySQL 5.0), that is, the event that
- describes the other events' header/postheader lengths. This event is sent by
- MySQL 5.0 whenever it starts sending a new binlog if the requested position
- is >4 (otherwise if ==4 the event will be sent naturally).
- ****************************************************************************/
+ Format_description_log_event derives from Start_log_event_v3; it is
+ the Start_log_event of binlog format 4 (MySQL 5.0), that is, the
+ event that describes the other events' Common-Header/Post-Header
+ lengths. This event is sent by MySQL 5.0 whenever it starts sending
+ a new binlog if the requested position is >4 (otherwise if ==4 the
+ event will be sent naturally).
+ @section Start_log_event_v3_binary_format Binary Format
+*/
class Start_log_event_v3: public Log_event
{
public:
@@ -1056,21 +2105,19 @@ public:
uint16 binlog_version;
char server_version[ST_SERVER_VER_LEN];
/*
- artifical_event is 1 in the case where this is a generated event that
- should not case any cleanup actions. We handle this in the log by
- setting log_event == 0 (for now).
+ We set this to 1 if we don't want to have the created time in the log,
+ which is the case when we rollover to a new log.
*/
- bool artificial_event;
+ bool dont_set_created;
#ifndef MYSQL_CLIENT
Start_log_event_v3();
#ifdef HAVE_REPLICATION
void pack_info(Protocol* protocol);
- int exec_event(struct st_relay_log_info* rli);
#endif /* HAVE_REPLICATION */
#else
Start_log_event_v3() {}
- void print(FILE* file, PRINT_EVENT_INFO* print_event_info= 0);
+ void print(FILE* file, PRINT_EVENT_INFO* print_event_info);
#endif
Start_log_event_v3(const char* buf,
@@ -1085,14 +2132,33 @@ public:
{
return START_V3_HEADER_LEN; //no variable-sized part
}
- virtual bool is_artificial_event() { return artificial_event; }
+
+protected:
+#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
+ virtual int do_apply_event(Relay_log_info const *rli);
+ virtual enum_skip_reason do_shall_skip(Relay_log_info*)
+ {
+ /*
+ Events from ourself should be skipped, but they should not
+ decrease the slave skip counter.
+ */
+ if (this->server_id == ::server_id)
+ return Log_event::EVENT_SKIP_IGNORE;
+ else
+ return Log_event::EVENT_SKIP_NOT;
+ }
+#endif
};
-/*
- For binlog version 4.
- This event is saved by threads which read it, as they need it for future
- use (to decode the ordinary events).
+/**
+ @class Format_description_log_event
+
+ For binlog version 4.
+ This event is saved by threads which read it, as they need it for future
+ use (to decode the ordinary events).
+
+ @section Format_description_log_event_binary_format Binary Format
*/
class Format_description_log_event: public Start_log_event_v3
@@ -1109,18 +2175,16 @@ public:
/* The list of post-headers' lengthes */
uint8 *post_header_len;
uchar server_version_split[3];
+ const uint8 *event_type_permutation;
Format_description_log_event(uint8 binlog_ver, const char* server_ver=0);
-
-#ifndef MYSQL_CLIENT
-#ifdef HAVE_REPLICATION
- int exec_event(struct st_relay_log_info* rli);
-#endif /* HAVE_REPLICATION */
-#endif
-
Format_description_log_event(const char* buf, uint event_len,
- const Format_description_log_event* description_event);
- ~Format_description_log_event() { my_free((gptr)post_header_len, MYF(0)); }
+ const Format_description_log_event
+ *description_event);
+ ~Format_description_log_event()
+ {
+ my_free((uchar*)post_header_len, MYF(MY_ALLOW_ZERO_PTR));
+ }
Log_event_type get_type_code() { return FORMAT_DESCRIPTION_EVENT;}
#ifndef MYSQL_CLIENT
bool write(IO_CACHE* file);
@@ -1140,18 +2204,56 @@ public:
*/
return FORMAT_DESCRIPTION_HEADER_LEN;
}
+
void calc_server_version_split();
+
+protected:
+#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
+ virtual int do_apply_event(Relay_log_info const *rli);
+ virtual int do_update_pos(Relay_log_info *rli);
+ virtual enum_skip_reason do_shall_skip(Relay_log_info *rli);
+#endif
};
-/*****************************************************************************
+/**
+ @class Intvar_log_event
- Intvar Log Event class
+ An Intvar_log_event will be created just before a Query_log_event,
+ if the query uses one of the variables LAST_INSERT_ID or INSERT_ID.
+ Each Intvar_log_event holds the value of one of these variables.
- Logs special variables such as auto_increment values
+ @section Intvar_log_event_binary_format Binary Format
- ****************************************************************************/
+ The Post-Header for this event type is empty. The Body has two
+ components:
+ <table>
+ <caption>Body for Intvar_log_event</caption>
+
+ <tr>
+ <th>Name</th>
+ <th>Format</th>
+ <th>Description</th>
+ </tr>
+
+ <tr>
+ <td>type</td>
+ <td>1 byte enumeration</td>
+ <td>One byte identifying the type of variable stored. Currently,
+ two identifiers are supported: LAST_INSERT_ID_EVENT==1 and
+ INSERT_ID_EVENT==2.
+ </td>
+ </tr>
+
+ <tr>
+ <td>value</td>
+ <td>8 byte unsigned integer</td>
+ <td>The value of the variable.</td>
+ </tr>
+
+ </table>
+*/
class Intvar_log_event: public Log_event
{
public:
@@ -1164,13 +2266,13 @@ public:
{}
#ifdef HAVE_REPLICATION
void pack_info(Protocol* protocol);
- int exec_event(struct st_relay_log_info* rli);
#endif /* HAVE_REPLICATION */
#else
- void print(FILE* file, PRINT_EVENT_INFO* print_event_info= 0);
+ void print(FILE* file, PRINT_EVENT_INFO* print_event_info);
#endif
- Intvar_log_event(const char* buf, const Format_description_log_event* description_event);
+ Intvar_log_event(const char* buf,
+ const Format_description_log_event *description_event);
~Intvar_log_event() {}
Log_event_type get_type_code() { return INTVAR_EVENT;}
const char* get_var_type_name();
@@ -1179,19 +2281,54 @@ public:
bool write(IO_CACHE* file);
#endif
bool is_valid() const { return 1; }
-};
+private:
+#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
+ virtual int do_apply_event(Relay_log_info const *rli);
+ virtual int do_update_pos(Relay_log_info *rli);
+ virtual enum_skip_reason do_shall_skip(Relay_log_info *rli);
+#endif
+};
-/*****************************************************************************
- Rand Log Event class
+/**
+ @class Rand_log_event
Logs random seed used by the next RAND(), and by PASSWORD() in 4.1.0.
4.1.1 does not need it (it's repeatable again) so this event needn't be
written in 4.1.1 for PASSWORD() (but the fact that it is written is just a
waste, it does not cause bugs).
- ****************************************************************************/
+ The state of the random number generation consists of 128 bits,
+ which are stored internally as two 64-bit numbers.
+
+ @section Rand_log_event_binary_format Binary Format
+
+ The Post-Header for this event type is empty. The Body has two
+ components:
+
+ <table>
+ <caption>Body for Rand_log_event</caption>
+
+ <tr>
+ <th>Name</th>
+ <th>Format</th>
+ <th>Description</th>
+ </tr>
+
+ <tr>
+ <td>seed1</td>
+ <td>8 byte unsigned integer</td>
+ <td>64 bit random seed1.</td>
+ </tr>
+
+ <tr>
+ <td>seed2</td>
+ <td>8 byte unsigned integer</td>
+ <td>64 bit random seed2.</td>
+ </tr>
+ </table>
+*/
class Rand_log_event: public Log_event
{
@@ -1205,13 +2342,13 @@ class Rand_log_event: public Log_event
{}
#ifdef HAVE_REPLICATION
void pack_info(Protocol* protocol);
- int exec_event(struct st_relay_log_info* rli);
#endif /* HAVE_REPLICATION */
#else
- void print(FILE* file, PRINT_EVENT_INFO* print_event_info= 0);
+ void print(FILE* file, PRINT_EVENT_INFO* print_event_info);
#endif
- Rand_log_event(const char* buf, const Format_description_log_event* description_event);
+ Rand_log_event(const char* buf,
+ const Format_description_log_event *description_event);
~Rand_log_event() {}
Log_event_type get_type_code() { return RAND_EVENT;}
int get_data_size() { return 16; /* sizeof(ulonglong) * 2*/ }
@@ -1219,16 +2356,23 @@ class Rand_log_event: public Log_event
bool write(IO_CACHE* file);
#endif
bool is_valid() const { return 1; }
-};
-/*****************************************************************************
+private:
+#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
+ virtual int do_apply_event(Relay_log_info const *rli);
+ virtual int do_update_pos(Relay_log_info *rli);
+ virtual enum_skip_reason do_shall_skip(Relay_log_info *rli);
+#endif
+};
- Xid Log Event class
+/**
+ @class Xid_log_event
Logs xid of the transaction-to-be-committed in the 2pc protocol.
Has no meaning in replication, slaves ignore it.
- ****************************************************************************/
+ @section Xid_log_event_binary_format Binary Format
+*/
#ifdef MYSQL_CLIENT
typedef ulonglong my_xid; // this line is the same as in handler.h
#endif
@@ -1242,13 +2386,13 @@ class Xid_log_event: public Log_event
Xid_log_event(THD* thd_arg, my_xid x): Log_event(thd_arg,0,0), xid(x) {}
#ifdef HAVE_REPLICATION
void pack_info(Protocol* protocol);
- int exec_event(struct st_relay_log_info* rli);
#endif /* HAVE_REPLICATION */
#else
- void print(FILE* file, PRINT_EVENT_INFO* print_event_info= 0);
+ void print(FILE* file, PRINT_EVENT_INFO* print_event_info);
#endif
- Xid_log_event(const char* buf, const Format_description_log_event* description_event);
+ Xid_log_event(const char* buf,
+ const Format_description_log_event *description_event);
~Xid_log_event() {}
Log_event_type get_type_code() { return XID_EVENT;}
int get_data_size() { return sizeof(xid); }
@@ -1256,16 +2400,22 @@ class Xid_log_event: public Log_event
bool write(IO_CACHE* file);
#endif
bool is_valid() const { return 1; }
-};
-/*****************************************************************************
+private:
+#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
+ virtual int do_apply_event(Relay_log_info const *rli);
+ enum_skip_reason do_shall_skip(Relay_log_info *rli);
+#endif
+};
- User var Log Event class
+/**
+ @class User_var_log_event
Every time a query uses the value of a user variable, a User_var_log_event is
written before the Query_log_event, to set the user variable.
- ****************************************************************************/
+ @section User_var_log_event_binary_format Binary Format
+*/
class User_var_log_event: public Log_event
{
@@ -1285,73 +2435,140 @@ public:
val_len(val_len_arg), type(type_arg), charset_number(charset_number_arg)
{ is_null= !val; }
void pack_info(Protocol* protocol);
- int exec_event(struct st_relay_log_info* rli);
#else
- void print(FILE* file, PRINT_EVENT_INFO* print_event_info= 0);
+ void print(FILE* file, PRINT_EVENT_INFO* print_event_info);
#endif
- User_var_log_event(const char* buf, const Format_description_log_event* description_event);
+ User_var_log_event(const char* buf,
+ const Format_description_log_event *description_event);
~User_var_log_event() {}
Log_event_type get_type_code() { return USER_VAR_EVENT;}
#ifndef MYSQL_CLIENT
bool write(IO_CACHE* file);
#endif
bool is_valid() const { return 1; }
+
+private:
+#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
+ virtual int do_apply_event(Relay_log_info const *rli);
+ virtual int do_update_pos(Relay_log_info *rli);
+ virtual enum_skip_reason do_shall_skip(Relay_log_info *rli);
+#endif
};
-/*****************************************************************************
+/**
+ @class Stop_log_event
- Stop Log Event class
+ @section Stop_log_event_binary_format Binary Format
- ****************************************************************************/
+ The Post-Header and Body for this event type are empty; it only has
+ the Common-Header.
+*/
class Stop_log_event: public Log_event
{
public:
#ifndef MYSQL_CLIENT
Stop_log_event() :Log_event()
{}
- int exec_event(struct st_relay_log_info* rli);
#else
- void print(FILE* file, PRINT_EVENT_INFO* print_event_info= 0);
+ void print(FILE* file, PRINT_EVENT_INFO* print_event_info);
#endif
- Stop_log_event(const char* buf, const Format_description_log_event* description_event):
+ Stop_log_event(const char* buf,
+ const Format_description_log_event *description_event):
Log_event(buf, description_event)
{}
~Stop_log_event() {}
Log_event_type get_type_code() { return STOP_EVENT;}
bool is_valid() const { return 1; }
-};
-/*****************************************************************************
+private:
+#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
+ virtual int do_update_pos(Relay_log_info *rli);
+ virtual enum_skip_reason do_shall_skip(Relay_log_info *rli)
+ {
+ /*
+ Events from ourself should be skipped, but they should not
+ decrease the slave skip counter.
+ */
+ if (this->server_id == ::server_id)
+ return Log_event::EVENT_SKIP_IGNORE;
+ else
+ return Log_event::EVENT_SKIP_NOT;
+ }
+#endif
+};
- Rotate Log Event class
+/**
+ @class Rotate_log_event
This will be deprecated when we move to using sequence ids.
- ****************************************************************************/
+ @section Rotate_log_event_binary_format Binary Format
+
+ The Post-Header has one component:
+
+ <table>
+ <caption>Post-Header for Rotate_log_event</caption>
+
+ <tr>
+ <th>Name</th>
+ <th>Format</th>
+ <th>Description</th>
+ </tr>
+
+ <tr>
+ <td>position</td>
+ <td>8 byte integer</td>
+ <td>The position within the binlog to rotate to.</td>
+ </tr>
+
+ </table>
+
+ The Body has one component:
+
+ <table>
+ <caption>Body for Rotate_log_event</caption>
+
+ <tr>
+ <th>Name</th>
+ <th>Format</th>
+ <th>Description</th>
+ </tr>
+
+ <tr>
+ <td>new_log</td>
+ <td>variable length string without trailing zero, extending to the
+ end of the event (determined by the length field of the
+ Common-Header)
+ </td>
+ <td>Name of the binlog to rotate to.</td>
+ </tr>
+
+ </table>
+*/
class Rotate_log_event: public Log_event
{
public:
enum {
- DUP_NAME= 2 // if constructor should dup the string argument
+ DUP_NAME= 2, // if constructor should dup the string argument
+ RELAY_LOG=4 // rotate event for relay log
};
const char* new_log_ident;
ulonglong pos;
uint ident_len;
uint flags;
#ifndef MYSQL_CLIENT
- Rotate_log_event(THD* thd_arg, const char* new_log_ident_arg,
+ Rotate_log_event(const char* new_log_ident_arg,
uint ident_len_arg,
ulonglong pos_arg, uint flags);
#ifdef HAVE_REPLICATION
void pack_info(Protocol* protocol);
- int exec_event(struct st_relay_log_info* rli);
#endif /* HAVE_REPLICATION */
#else
- void print(FILE* file, PRINT_EVENT_INFO* print_event_info= 0);
+ void print(FILE* file, PRINT_EVENT_INFO* print_event_info);
#endif
Rotate_log_event(const char* buf, uint event_len,
@@ -1359,7 +2576,7 @@ public:
~Rotate_log_event()
{
if (flags & DUP_NAME)
- my_free((gptr) new_log_ident, MYF(MY_ALLOW_ZERO_PTR));
+ my_free((uchar*) new_log_ident, MYF(MY_ALLOW_ZERO_PTR));
}
Log_event_type get_type_code() { return ROTATE_EVENT;}
int get_data_size() { return ident_len + ROTATE_HEADER_LEN;}
@@ -1367,14 +2584,22 @@ public:
#ifndef MYSQL_CLIENT
bool write(IO_CACHE* file);
#endif
+
+private:
+#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
+ virtual int do_update_pos(Relay_log_info *rli);
+ virtual enum_skip_reason do_shall_skip(Relay_log_info *rli);
+#endif
};
/* the classes below are for the new LOAD DATA INFILE logging */
-/*****************************************************************************
- Create File Log Event class
- ****************************************************************************/
+/**
+ @class Create_file_log_event
+
+ @section Create_file_log_event_binary_format Binary Format
+*/
class Create_file_log_event: public Load_log_event
{
@@ -1386,7 +2611,7 @@ protected:
*/
bool fake_base;
public:
- char* block;
+ uchar* block;
const char *event_buf;
uint block_len;
uint file_id;
@@ -1397,15 +2622,15 @@ public:
const char* table_name_arg,
List<Item>& fields_arg,
enum enum_duplicates handle_dup, bool ignore,
- char* block_arg, uint block_len_arg,
+ uchar* block_arg, uint block_len_arg,
bool using_trans);
#ifdef HAVE_REPLICATION
void pack_info(Protocol* protocol);
- int exec_event(struct st_relay_log_info* rli);
#endif /* HAVE_REPLICATION */
#else
- void print(FILE* file, PRINT_EVENT_INFO* print_event_info= 0);
- void print(FILE* file, PRINT_EVENT_INFO* print_event_info, bool enable_local);
+ void print(FILE* file, PRINT_EVENT_INFO* print_event_info);
+ void print(FILE* file, PRINT_EVENT_INFO* print_event_info,
+ bool enable_local);
#endif
Create_file_log_event(const char* buf, uint event_len,
@@ -1435,19 +2660,24 @@ public:
*/
bool write_base(IO_CACHE* file);
#endif
-};
+private:
+#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
+ virtual int do_apply_event(Relay_log_info const *rli);
+#endif
+};
-/*****************************************************************************
- Append Block Log Event class
+/**
+ @class Append_block_log_event
- ****************************************************************************/
+ @section Append_block_log_event_binary_format Binary Format
+*/
class Append_block_log_event: public Log_event
{
public:
- char* block;
+ uchar* block;
uint block_len;
uint file_id;
/*
@@ -1464,35 +2694,40 @@ public:
const char* db;
#ifndef MYSQL_CLIENT
- Append_block_log_event(THD* thd, const char* db_arg, char* block_arg,
+ Append_block_log_event(THD* thd, const char* db_arg, uchar* block_arg,
uint block_len_arg, bool using_trans);
#ifdef HAVE_REPLICATION
- int exec_event(struct st_relay_log_info* rli);
void pack_info(Protocol* protocol);
virtual int get_create_or_append() const;
#endif /* HAVE_REPLICATION */
#else
- void print(FILE* file, PRINT_EVENT_INFO* print_event_info= 0);
+ void print(FILE* file, PRINT_EVENT_INFO* print_event_info);
#endif
Append_block_log_event(const char* buf, uint event_len,
- const Format_description_log_event* description_event);
+ const Format_description_log_event
+ *description_event);
~Append_block_log_event() {}
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
bool write(IO_CACHE* file);
-#endif
const char* get_db() { return db; }
-};
+#endif
+private:
+#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
+ virtual int do_apply_event(Relay_log_info const *rli);
+#endif
+};
-/*****************************************************************************
- Delete File Log Event class
+/**
+ @class Delete_file_log_event
- ****************************************************************************/
+ @section Delete_file_log_event_binary_format Binary Format
+*/
class Delete_file_log_event: public Log_event
{
@@ -1504,11 +2739,11 @@ public:
Delete_file_log_event(THD* thd, const char* db_arg, bool using_trans);
#ifdef HAVE_REPLICATION
void pack_info(Protocol* protocol);
- int exec_event(struct st_relay_log_info* rli);
#endif /* HAVE_REPLICATION */
#else
- void print(FILE* file, PRINT_EVENT_INFO* print_event_info= 0);
- void print(FILE* file, PRINT_EVENT_INFO* print_event_info, bool enable_local);
+ void print(FILE* file, PRINT_EVENT_INFO* print_event_info);
+ void print(FILE* file, PRINT_EVENT_INFO* print_event_info,
+ bool enable_local);
#endif
Delete_file_log_event(const char* buf, uint event_len,
@@ -1519,16 +2754,21 @@ public:
bool is_valid() const { return file_id != 0; }
#ifndef MYSQL_CLIENT
bool write(IO_CACHE* file);
-#endif
const char* get_db() { return db; }
-};
+#endif
+private:
+#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
+ virtual int do_apply_event(Relay_log_info const *rli);
+#endif
+};
-/*****************************************************************************
- Execute Load Log Event class
+/**
+ @class Execute_load_log_event
- ****************************************************************************/
+ @section Delete_file_log_event_binary_format Binary Format
+*/
class Execute_load_log_event: public Log_event
{
@@ -1540,40 +2780,45 @@ public:
Execute_load_log_event(THD* thd, const char* db_arg, bool using_trans);
#ifdef HAVE_REPLICATION
void pack_info(Protocol* protocol);
- int exec_event(struct st_relay_log_info* rli);
#endif /* HAVE_REPLICATION */
#else
- void print(FILE* file, PRINT_EVENT_INFO* print_event_info= 0);
+ void print(FILE* file, PRINT_EVENT_INFO* print_event_info);
#endif
Execute_load_log_event(const char* buf, uint event_len,
- const Format_description_log_event* description_event);
+ const Format_description_log_event
+ *description_event);
~Execute_load_log_event() {}
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
bool write(IO_CACHE* file);
-#endif
const char* get_db() { return db; }
-};
+#endif
+private:
+#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
+ virtual int do_apply_event(Relay_log_info const *rli);
+#endif
+};
-/***************************************************************************
- Begin load query Log Event class
+/**
+ @class Begin_load_query_log_event
Event for the first block of file to be loaded, its only difference from
Append_block event is that this event creates or truncates existing file
before writing data.
-****************************************************************************/
+ @section Begin_load_query_log_event_binary_format Binary Format
+*/
class Begin_load_query_log_event: public Append_block_log_event
{
public:
#ifndef MYSQL_CLIENT
Begin_load_query_log_event(THD* thd_arg, const char *db_arg,
- char* block_arg, uint block_len_arg,
+ uchar* block_arg, uint block_len_arg,
bool using_trans);
#ifdef HAVE_REPLICATION
Begin_load_query_log_event(THD* thd);
@@ -1581,9 +2826,14 @@ public:
#endif /* HAVE_REPLICATION */
#endif
Begin_load_query_log_event(const char* buf, uint event_len,
- const Format_description_log_event* description_event);
+ const Format_description_log_event
+ *description_event);
~Begin_load_query_log_event() {}
Log_event_type get_type_code() { return BEGIN_LOAD_QUERY_EVENT; }
+private:
+#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
+ virtual enum_skip_reason do_shall_skip(Relay_log_info *rli);
+#endif
};
@@ -1593,15 +2843,15 @@ public:
enum enum_load_dup_handling { LOAD_DUP_ERROR= 0, LOAD_DUP_IGNORE,
LOAD_DUP_REPLACE };
-/****************************************************************************
-
- Execute load query Log Event class
+/**
+ @class Execute_load_query_log_event
Event responsible for LOAD DATA execution, it similar to Query_log_event
but before executing the query it substitutes original filename in LOAD DATA
query with name of temporary file.
-****************************************************************************/
+ @section Execute_load_query_log_event_binary_format Binary Format
+*/
class Execute_load_query_log_event: public Query_log_event
{
public:
@@ -1627,16 +2877,16 @@ public:
killed_err_arg= THD::KILLED_NO_VALUE);
#ifdef HAVE_REPLICATION
void pack_info(Protocol* protocol);
- int exec_event(struct st_relay_log_info* rli);
#endif /* HAVE_REPLICATION */
#else
- void print(FILE* file, PRINT_EVENT_INFO* print_event_info= 0);
+ void print(FILE* file, PRINT_EVENT_INFO* print_event_info);
/* Prints the query as LOAD DATA LOCAL and with rewritten filename */
void print(FILE* file, PRINT_EVENT_INFO* print_event_info,
const char *local_fname);
#endif
Execute_load_query_log_event(const char* buf, uint event_len,
- const Format_description_log_event *description_event);
+ const Format_description_log_event
+ *description_event);
~Execute_load_query_log_event() {}
Log_event_type get_type_code() { return EXECUTE_LOAD_QUERY_EVENT; }
@@ -1646,10 +2896,20 @@ public:
#ifndef MYSQL_CLIENT
bool write_post_header_for_derived(IO_CACHE* file);
#endif
- };
+
+private:
+#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
+ virtual int do_apply_event(Relay_log_info const *rli);
+#endif
+};
#ifdef MYSQL_CLIENT
+/**
+ @class Unknown_log_event
+
+ @section Unknown_log_event_binary_format Binary Format
+*/
class Unknown_log_event: public Log_event
{
public:
@@ -1658,14 +2918,1005 @@ public:
Log_event's ctor, this way we can extract maximum information from the
event's header (the unique ID for example).
*/
- Unknown_log_event(const char* buf, const Format_description_log_event* description_event):
+ Unknown_log_event(const char* buf,
+ const Format_description_log_event *description_event):
Log_event(buf, description_event)
{}
~Unknown_log_event() {}
- void print(FILE* file, PRINT_EVENT_INFO* print_event_info= 0);
+ void print(FILE* file, PRINT_EVENT_INFO* print_event_info);
Log_event_type get_type_code() { return UNKNOWN_EVENT;}
bool is_valid() const { return 1; }
};
#endif
char *str_to_hex(char *to, const char *from, uint len);
+
+/**
+ @class Table_map_log_event
+
+ In row-based mode, every row operation event is preceded by a
+ Table_map_log_event which maps a table definition to a number. The
+ table definition consists of database name, table name, and column
+ definitions.
+
+ @section Table_map_log_event_binary_format Binary Format
+
+ The Post-Header has the following components:
+
+ <table>
+ <caption>Post-Header for Table_map_log_event</caption>
+
+ <tr>
+ <th>Name</th>
+ <th>Format</th>
+ <th>Description</th>
+ </tr>
+
+ <tr>
+ <td>table_id</td>
+ <td>6 bytes unsigned integer</td>
+ <td>The number that identifies the table.</td>
+ </tr>
+
+ <tr>
+ <td>flags</td>
+ <td>2 byte bitfield</td>
+ <td>Reserved for future use; currently always 0.</td>
+ </tr>
+
+ </table>
+
+ The Body has the following components:
+
+ <table>
+ <caption>Body for Table_map_log_event</caption>
+
+ <tr>
+ <th>Name</th>
+ <th>Format</th>
+ <th>Description</th>
+ </tr>
+
+ <tr>
+ <td>database_name</td>
+ <td>one byte string length, followed by null-terminated string</td>
+ <td>The name of the database in which the table resides. The name
+ is represented as a one byte unsigned integer representing the
+ number of bytes in the name, followed by length bytes containing
+ the database name, followed by a terminating 0 byte. (Note the
+ redundancy in the representation of the length.) </td>
+ </tr>
+
+ <tr>
+ <td>table_name</td>
+ <td>one byte string length, followed by null-terminated string</td>
+ <td>The name of the table, encoded the same way as the database
+ name above.</td>
+ </tr>
+
+ <tr>
+ <td>column_count</td>
+ <td>@ref packed_integer "Packed Integer"</td>
+ <td>The number of columns in the table, represented as a packed
+ variable-length integer.</td>
+ </tr>
+
+ <tr>
+ <td>column_type</td>
+ <td>List of column_count 1 byte enumeration values</td>
+ <td>The type of each column in the table, listed from left to
+ right. Each byte is mapped to a column type according to the
+ enumeration type enum_field_types defined in mysql_com.h. The
+ mapping of types to numbers is listed in the table @ref
+ Table_table_map_log_event_column_types "below" (along with
+ description of the associated metadata field). </td>
+ </tr>
+
+ <tr>
+ <td>metadata_length</td>
+ <td>@ref packed_integer "Packed Integer"</td>
+ <td>The length of the following metadata block</td>
+ </tr>
+
+ <tr>
+ <td>metadata</td>
+ <td>list of metadata for each column</td>
+ <td>For each column from left to right, a chunk of data who's
+ length and semantics depends on the type of the column. The
+ length and semantics for the metadata for each column are listed
+ in the table @ref Table_table_map_log_event_column_types
+ "below".</td>
+ </tr>
+
+ <tr>
+ <td>null_bits</td>
+ <td>column_count bits, rounded up to nearest byte</td>
+ <td>For each column, a bit indicating whether data in the column
+ can be NULL or not. The number of bytes needed for this is
+ int((column_count+7)/8). The flag for the first column from the
+ left is in the least-significant bit of the first byte, the second
+ is in the second least significant bit of the first byte, the
+ ninth is in the least significant bit of the second byte, and so
+ on. </td>
+ </tr>
+
+ </table>
+
+ The table below lists all column types, along with the numerical
+ identifier for it and the size and interpretation of meta-data used
+ to describe the type.
+
+ @anchor Table_table_map_log_event_column_types
+ <table>
+ <caption>Table_map_log_event column types: numerical identifier and
+ metadata</caption>
+ <tr>
+ <th>Name</th>
+ <th>Identifier</th>
+ <th>Size of metadata in bytes</th>
+ <th>Description of metadata</th>
+ </tr>
+
+ <tr>
+ <td>MYSQL_TYPE_DECIMAL</td><td>0</td>
+ <td>0</td>
+ <td>No column metadata.</td>
+ </tr>
+
+ <tr>
+ <td>MYSQL_TYPE_TINY</td><td>1</td>
+ <td>0</td>
+ <td>No column metadata.</td>
+ </tr>
+
+ <tr>
+ <td>MYSQL_TYPE_SHORT</td><td>2</td>
+ <td>0</td>
+ <td>No column metadata.</td>
+ </tr>
+
+ <tr>
+ <td>MYSQL_TYPE_LONG</td><td>3</td>
+ <td>0</td>
+ <td>No column metadata.</td>
+ </tr>
+
+ <tr>
+ <td>MYSQL_TYPE_FLOAT</td><td>4</td>
+ <td>1 byte</td>
+ <td>1 byte unsigned integer, representing the "pack_length", which
+ is equal to sizeof(float) on the server from which the event
+ originates.</td>
+ </tr>
+
+ <tr>
+ <td>MYSQL_TYPE_DOUBLE</td><td>5</td>
+ <td>1 byte</td>
+ <td>1 byte unsigned integer, representing the "pack_length", which
+ is equal to sizeof(double) on the server from which the event
+ originates.</td>
+ </tr>
+
+ <tr>
+ <td>MYSQL_TYPE_NULL</td><td>6</td>
+ <td>0</td>
+ <td>No column metadata.</td>
+ </tr>
+
+ <tr>
+ <td>MYSQL_TYPE_TIMESTAMP</td><td>7</td>
+ <td>0</td>
+ <td>No column metadata.</td>
+ </tr>
+
+ <tr>
+ <td>MYSQL_TYPE_LONGLONG</td><td>8</td>
+ <td>0</td>
+ <td>No column metadata.</td>
+ </tr>
+
+ <tr>
+ <td>MYSQL_TYPE_INT24</td><td>9</td>
+ <td>0</td>
+ <td>No column metadata.</td>
+ </tr>
+
+ <tr>
+ <td>MYSQL_TYPE_DATE</td><td>10</td>
+ <td>0</td>
+ <td>No column metadata.</td>
+ </tr>
+
+ <tr>
+ <td>MYSQL_TYPE_TIME</td><td>11</td>
+ <td>0</td>
+ <td>No column metadata.</td>
+ </tr>
+
+ <tr>
+ <td>MYSQL_TYPE_DATETIME</td><td>12</td>
+ <td>0</td>
+ <td>No column metadata.</td>
+ </tr>
+
+ <tr>
+ <td>MYSQL_TYPE_YEAR</td><td>13</td>
+ <td>0</td>
+ <td>No column metadata.</td>
+ </tr>
+
+ <tr>
+ <td><i>MYSQL_TYPE_NEWDATE</i></td><td><i>14</i></td>
+ <td>&ndash;</td>
+ <td><i>This enumeration value is only used internally and cannot
+ exist in a binlog.</i></td>
+ </tr>
+
+ <tr>
+ <td>MYSQL_TYPE_VARCHAR</td><td>15</td>
+ <td>2 bytes</td>
+ <td>2 byte unsigned integer representing the maximum length of
+ the string.</td>
+ </tr>
+
+ <tr>
+ <td>MYSQL_TYPE_BIT</td><td>16</td>
+ <td>2 bytes</td>
+ <td>A 1 byte unsigned int representing the length in bits of the
+ bitfield (0 to 64), followed by a 1 byte unsigned int
+ representing the number of bytes occupied by the bitfield. The
+ number of bytes is either int((length+7)/8) or int(length/8).</td>
+ </tr>
+
+ <tr>
+ <td>MYSQL_TYPE_NEWDECIMAL</td><td>246</td>
+ <td>2 bytes</td>
+ <td>A 1 byte unsigned int representing the precision, followed
+ by a 1 byte unsigned int representing the number of decimals.</td>
+ </tr>
+
+ <tr>
+ <td><i>MYSQL_TYPE_ENUM</i></td><td><i>247</i></td>
+ <td>&ndash;</td>
+ <td><i>This enumeration value is only used internally and cannot
+ exist in a binlog.</i></td>
+ </tr>
+
+ <tr>
+ <td><i>MYSQL_TYPE_SET</i></td><td><i>248</i></td>
+ <td>&ndash;</td>
+ <td><i>This enumeration value is only used internally and cannot
+ exist in a binlog.</i></td>
+ </tr>
+
+ <tr>
+ <td>MYSQL_TYPE_TINY_BLOB</td><td>249</td>
+ <td>&ndash;</td>
+ <td><i>This enumeration value is only used internally and cannot
+ exist in a binlog.</i></td>
+ </tr>
+
+ <tr>
+ <td><i>MYSQL_TYPE_MEDIUM_BLOB</i></td><td><i>250</i></td>
+ <td>&ndash;</td>
+ <td><i>This enumeration value is only used internally and cannot
+ exist in a binlog.</i></td>
+ </tr>
+
+ <tr>
+ <td><i>MYSQL_TYPE_LONG_BLOB</i></td><td><i>251</i></td>
+ <td>&ndash;</td>
+ <td><i>This enumeration value is only used internally and cannot
+ exist in a binlog.</i></td>
+ </tr>
+
+ <tr>
+ <td>MYSQL_TYPE_BLOB</td><td>252</td>
+ <td>1 byte</td>
+ <td>The pack length, i.e., the number of bytes needed to represent
+ the length of the blob: 1, 2, 3, or 4.</td>
+ </tr>
+
+ <tr>
+ <td>MYSQL_TYPE_VAR_STRING</td><td>253</td>
+ <td>2 bytes</td>
+ <td>This is used to store both strings and enumeration values.
+ The first byte is a enumeration value storing the <i>real
+ type</i>, which may be either MYSQL_TYPE_VAR_STRING or
+ MYSQL_TYPE_ENUM. The second byte is a 1 byte unsigned integer
+ representing the field size, i.e., the number of bytes needed to
+ store the length of the string.</td>
+ </tr>
+
+ <tr>
+ <td>MYSQL_TYPE_STRING</td><td>254</td>
+ <td>2 bytes</td>
+ <td>The first byte is always MYSQL_TYPE_VAR_STRING (i.e., 253).
+ The second byte is the field size, i.e., the number of bytes in
+ the representation of size of the string: 3 or 4.</td>
+ </tr>
+
+ <tr>
+ <td>MYSQL_TYPE_GEOMETRY</td><td>255</td>
+ <td>1 byte</td>
+ <td>The pack length, i.e., the number of bytes needed to represent
+ the length of the geometry: 1, 2, 3, or 4.</td>
+ </tr>
+
+ </table>
+*/
+class Table_map_log_event : public Log_event
+{
+public:
+ /* Constants */
+ enum
+ {
+ TYPE_CODE = TABLE_MAP_EVENT
+ };
+
+ /**
+ Enumeration of the errors that can be returned.
+ */
+ enum enum_error
+ {
+ ERR_OPEN_FAILURE = -1, /**< Failure to open table */
+ ERR_OK = 0, /**< No error */
+ ERR_TABLE_LIMIT_EXCEEDED = 1, /**< No more room for tables */
+ ERR_OUT_OF_MEM = 2, /**< Out of memory */
+ ERR_BAD_TABLE_DEF = 3, /**< Table definition does not match */
+ ERR_RBR_TO_SBR = 4 /**< daisy-chanining RBR to SBR not allowed */
+ };
+
+ enum enum_flag
+ {
+ /*
+ Nothing here right now, but the flags support is there in
+ preparation for changes that are coming. Need to add a
+ constant to make it compile under HP-UX: aCC does not like
+ empty enumerations.
+ */
+ ENUM_FLAG_COUNT
+ };
+
+ typedef uint16 flag_set;
+
+ /* Special constants representing sets of flags */
+ enum
+ {
+ TM_NO_FLAGS = 0U
+ };
+
+ void set_flags(flag_set flag) { m_flags |= flag; }
+ void clear_flags(flag_set flag) { m_flags &= ~flag; }
+ flag_set get_flags(flag_set flag) const { return m_flags & flag; }
+
+#ifndef MYSQL_CLIENT
+ Table_map_log_event(THD *thd, TABLE *tbl, ulong tid,
+ bool is_transactional, uint16 flags);
+#endif
+#ifdef HAVE_REPLICATION
+ Table_map_log_event(const char *buf, uint event_len,
+ const Format_description_log_event *description_event);
+#endif
+
+ ~Table_map_log_event();
+
+#ifdef MYSQL_CLIENT
+ table_def *create_table_def()
+ {
+ return new table_def(m_coltype, m_colcnt, m_field_metadata,
+ m_field_metadata_size, m_null_bits);
+ }
+ 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; }
+#endif
+
+ 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
+ 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)
+ virtual void pack_info(Protocol *protocol);
+#endif
+
+#ifdef MYSQL_CLIENT
+ virtual void print(FILE *file, PRINT_EVENT_INFO *print_event_info);
+#endif
+
+
+private:
+#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
+ virtual int do_apply_event(Relay_log_info const *rli);
+ virtual int do_update_pos(Relay_log_info *rli);
+ virtual enum_skip_reason do_shall_skip(Relay_log_info *rli);
+#endif
+
+#ifndef MYSQL_CLIENT
+ TABLE *m_table;
+#endif
+ char const *m_dbnam;
+ size_t m_dblen;
+ char const *m_tblnam;
+ size_t m_tbllen;
+ ulong m_colcnt;
+ uchar *m_coltype;
+
+ uchar *m_memory;
+ ulong m_table_id;
+ flag_set m_flags;
+
+ size_t m_data_size;
+
+ uchar *m_field_metadata; // buffer for field metadata
+ /*
+ The size of field metadata buffer set by calling save_field_metadata()
+ */
+ ulong m_field_metadata_size;
+ uchar *m_null_bits;
+ uchar *m_meta_memory;
+};
+
+
+/**
+ @class Rows_log_event
+
+ Common base class for all row-containing log events.
+
+ RESPONSIBILITIES
+
+ Encode the common parts of all events containing rows, which are:
+ - Write data header and data body to an IO_CACHE.
+ - Provide an interface for adding an individual row to the event.
+
+ @section Rows_log_event_binary_format Binary Format
+*/
+
+
+class Rows_log_event : public Log_event
+{
+public:
+ /**
+ Enumeration of the errors that can be returned.
+ */
+ enum enum_error
+ {
+ ERR_OPEN_FAILURE = -1, /**< Failure to open table */
+ ERR_OK = 0, /**< No error */
+ ERR_TABLE_LIMIT_EXCEEDED = 1, /**< No more room for tables */
+ ERR_OUT_OF_MEM = 2, /**< Out of memory */
+ ERR_BAD_TABLE_DEF = 3, /**< Table definition does not match */
+ ERR_RBR_TO_SBR = 4 /**< daisy-chanining RBR to SBR not allowed */
+ };
+
+ /*
+ These definitions allow you to combine the flags into an
+ appropriate flag set using the normal bitwise operators. The
+ implicit conversion from an enum-constant to an integer is
+ accepted by the compiler, which is then used to set the real set
+ of flags.
+ */
+ enum enum_flag
+ {
+ /* Last event of a statement */
+ STMT_END_F = (1U << 0),
+
+ /* Value of the OPTION_NO_FOREIGN_KEY_CHECKS flag in thd->options */
+ NO_FOREIGN_KEY_CHECKS_F = (1U << 1),
+
+ /* Value of the OPTION_RELAXED_UNIQUE_CHECKS flag in thd->options */
+ RELAXED_UNIQUE_CHECKS_F = (1U << 2),
+
+ /**
+ Indicates that rows in this event are complete, that is contain
+ values for all columns of the table.
+ */
+ COMPLETE_ROWS_F = (1U << 3)
+ };
+
+ typedef uint16 flag_set;
+
+ /* Special constants representing sets of flags */
+ enum
+ {
+ RLE_NO_FLAGS = 0U
+ };
+
+ virtual ~Rows_log_event();
+
+ void set_flags(flag_set flags_arg) { m_flags |= flags_arg; }
+ 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)
+ virtual void pack_info(Protocol *protocol);
+#endif
+
+#ifdef MYSQL_CLIENT
+ /* not for direct call, each derived has its own ::print() */
+ virtual void print(FILE *file, PRINT_EVENT_INFO *print_event_info)= 0;
+ void print_verbose(IO_CACHE *file,
+ PRINT_EVENT_INFO *print_event_info);
+ size_t print_verbose_one_row(IO_CACHE *file, table_def *td,
+ PRINT_EVENT_INFO *print_event_info,
+ MY_BITMAP *cols_bitmap,
+ const uchar *ptr, const uchar *prefix);
+#endif
+
+#ifndef MYSQL_CLIENT
+ int add_row_data(uchar *data, size_t length)
+ {
+ return do_add_row_data(data,length);
+ }
+#endif
+
+ /* Member functions to implement superclass interface */
+ virtual int get_data_size();
+
+ MY_BITMAP const *get_cols() const { return &m_cols; }
+ size_t get_width() const { return m_width; }
+ ulong get_table_id() const { return m_table_id; }
+
+#ifndef MYSQL_CLIENT
+ 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; }
+#endif
+ /*
+ Check that malloc() succeeded in allocating memory for the rows
+ buffer and the COLS vector. Checking that an Update_rows_log_event
+ is valid is done in the Update_rows_log_event::is_valid()
+ function.
+ */
+ virtual bool is_valid() const
+ {
+ return m_rows_buf && m_cols.bitmap;
+ }
+
+ uint m_row_count; /* The number of rows added to the event */
+
+protected:
+ /*
+ The constructors are protected since you're supposed to inherit
+ this class, not create instances of this class.
+ */
+#ifndef MYSQL_CLIENT
+ Rows_log_event(THD*, TABLE*, ulong table_id,
+ MY_BITMAP const *cols, bool is_transactional);
+#endif
+ Rows_log_event(const char *row_data, uint event_len,
+ Log_event_type event_type,
+ const Format_description_log_event *description_event);
+
+#ifdef MYSQL_CLIENT
+ void print_helper(FILE *, PRINT_EVENT_INFO *, char const *const name);
+#endif
+
+#ifndef MYSQL_CLIENT
+ virtual int do_add_row_data(uchar *data, size_t length);
+#endif
+
+#ifndef MYSQL_CLIENT
+ TABLE *m_table; /* The table the rows belong to */
+#endif
+ ulong m_table_id; /* Table ID */
+ MY_BITMAP m_cols; /* Bitmap denoting columns available */
+ ulong m_width; /* The width of the columns bitmap */
+ /*
+ Bitmap for columns available in the after image, if present. These
+ fields are only available for Update_rows events. Observe that the
+ width of both the before image COLS vector and the after image
+ COLS vector is the same: the number of columns of the table on the
+ master.
+ */
+ MY_BITMAP m_cols_ai;
+
+ ulong m_master_reclength; /* Length of record on master side */
+
+ /* Bit buffers in the same memory as the class */
+ uint32 m_bitbuf[128/(sizeof(uint32)*8)];
+ uint32 m_bitbuf_ai[128/(sizeof(uint32)*8)];
+
+ uchar *m_rows_buf; /* The rows in packed format */
+ uchar *m_rows_cur; /* One-after the end of the data */
+ uchar *m_rows_end; /* One-after the end of the allocated space */
+
+ flag_set m_flags; /* Flags for row-level events */
+
+ /* helper functions */
+
+#if !defined(MYSQL_CLIENT) && 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 */
+
+ int find_row(const Relay_log_info *const);
+ int write_row(const Relay_log_info *const, const bool);
+
+ // Unpack the current row into m_table->record[0]
+ int unpack_current_row(const Relay_log_info *const rli)
+ {
+ 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_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;
+ }
+#endif
+
+private:
+
+#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
+ virtual int do_apply_event(Relay_log_info const *rli);
+ virtual int do_update_pos(Relay_log_info *rli);
+ virtual enum_skip_reason do_shall_skip(Relay_log_info *rli);
+
+ /*
+ Primitive to prepare for a sequence of row executions.
+
+ DESCRIPTION
+
+ Before doing a sequence of do_prepare_row() and do_exec_row()
+ calls, this member function should be called to prepare for the
+ entire sequence. Typically, this member function will allocate
+ space for any buffers that are needed for the two member
+ functions mentioned above.
+
+ RETURN VALUE
+
+ The member function will return 0 if all went OK, or a non-zero
+ error code otherwise.
+ */
+ virtual
+ int do_before_row_operations(const Slave_reporting_capability *const log) = 0;
+
+ /*
+ Primitive to clean up after a sequence of row executions.
+
+ DESCRIPTION
+
+ After doing a sequence of do_prepare_row() and do_exec_row(),
+ this member function should be called to clean up and release
+ any allocated buffers.
+
+ The error argument, if non-zero, indicates an error which happened during
+ row processing before this function was called. In this case, even if
+ function is successful, it should return the error code given in the argument.
+ */
+ virtual
+ int do_after_row_operations(const Slave_reporting_capability *const log,
+ int error) = 0;
+
+ /*
+ Primitive to do the actual execution necessary for a row.
+
+ DESCRIPTION
+ The member function will do the actual execution needed to handle a row.
+ The row is located at m_curr_row. When the function returns,
+ m_curr_row_end should point at the next row (one byte after the end
+ of the current row).
+
+ RETURN VALUE
+ 0 if execution succeeded, 1 if execution failed.
+
+ */
+ virtual int do_exec_row(const Relay_log_info *const rli) = 0;
+#endif /* !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) */
+
+ friend class Old_rows_log_event;
+};
+
+/**
+ @class Write_rows_log_event
+
+ Log row insertions and updates. The event contain several
+ insert/update rows for a table. Note that each event contains only
+ rows for one table.
+
+ @section Write_rows_log_event_binary_format Binary Format
+*/
+class Write_rows_log_event : public Rows_log_event
+{
+public:
+ enum
+ {
+ /* Support interface to THD::binlog_prepare_pending_rows_event */
+ TYPE_CODE = WRITE_ROWS_EVENT
+ };
+
+#if !defined(MYSQL_CLIENT)
+ Write_rows_log_event(THD*, TABLE*, ulong table_id,
+ MY_BITMAP const *cols, bool is_transactional);
+#endif
+#ifdef HAVE_REPLICATION
+ Write_rows_log_event(const char *buf, uint event_len,
+ const Format_description_log_event *description_event);
+#endif
+#if !defined(MYSQL_CLIENT)
+ static bool binlog_row_logging_function(THD *thd, TABLE *table,
+ bool is_transactional,
+ MY_BITMAP *cols,
+ uint fields,
+ const uchar *before_record
+ __attribute__((unused)),
+ const uchar *after_record)
+ {
+ return thd->binlog_write_row(table, is_transactional,
+ cols, fields, after_record);
+ }
+#endif
+
+private:
+ virtual Log_event_type get_type_code() { return (Log_event_type)TYPE_CODE; }
+
+#ifdef MYSQL_CLIENT
+ void print(FILE *file, PRINT_EVENT_INFO *print_event_info);
+#endif
+
+#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
+ virtual int do_before_row_operations(const Slave_reporting_capability *const);
+ virtual int do_after_row_operations(const Slave_reporting_capability *const,int);
+ virtual int do_exec_row(const Relay_log_info *const);
+#endif
+};
+
+
+/**
+ @class Update_rows_log_event
+
+ Log row updates with a before image. The event contain several
+ update rows for a table. Note that each event contains only rows for
+ one table.
+
+ Also note that the row data consists of pairs of row data: one row
+ for the old data and one row for the new data.
+
+ @section Update_rows_log_event_binary_format Binary Format
+*/
+class Update_rows_log_event : public Rows_log_event
+{
+public:
+ enum
+ {
+ /* Support interface to THD::binlog_prepare_pending_rows_event */
+ TYPE_CODE = UPDATE_ROWS_EVENT
+ };
+
+#ifndef MYSQL_CLIENT
+ Update_rows_log_event(THD*, TABLE*, ulong table_id,
+ MY_BITMAP const *cols_bi,
+ MY_BITMAP const *cols_ai,
+ bool is_transactional);
+
+ Update_rows_log_event(THD*, TABLE*, ulong table_id,
+ MY_BITMAP const *cols,
+ bool is_transactional);
+
+ void init(MY_BITMAP const *cols);
+#endif
+
+ virtual ~Update_rows_log_event();
+
+#ifdef HAVE_REPLICATION
+ Update_rows_log_event(const char *buf, uint event_len,
+ const Format_description_log_event *description_event);
+#endif
+
+#if !defined(MYSQL_CLIENT)
+ static bool binlog_row_logging_function(THD *thd, TABLE *table,
+ bool is_transactional,
+ MY_BITMAP *cols,
+ uint fields,
+ const uchar *before_record,
+ const uchar *after_record)
+ {
+ return thd->binlog_update_row(table, is_transactional,
+ cols, fields, before_record, after_record);
+ }
+#endif
+
+ virtual bool is_valid() const
+ {
+ return Rows_log_event::is_valid() && m_cols_ai.bitmap;
+ }
+
+protected:
+ virtual Log_event_type get_type_code() { return (Log_event_type)TYPE_CODE; }
+
+#ifdef MYSQL_CLIENT
+ void print(FILE *file, PRINT_EVENT_INFO *print_event_info);
+#endif
+
+#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
+ virtual int do_before_row_operations(const Slave_reporting_capability *const);
+ virtual int do_after_row_operations(const Slave_reporting_capability *const,int);
+ virtual int do_exec_row(const Relay_log_info *const);
+#endif /* !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) */
+};
+
+/**
+ @class Delete_rows_log_event
+
+ Log row deletions. The event contain several delete rows for a
+ table. Note that each event contains only rows for one table.
+
+ RESPONSIBILITIES
+
+ - Act as a container for rows that has been deleted on the master
+ and should be deleted on the slave.
+
+ COLLABORATION
+
+ Row_writer
+ Create the event and add rows to the event.
+ Row_reader
+ Extract the rows from the event.
+
+ @section Delete_rows_log_event_binary_format Binary Format
+*/
+class Delete_rows_log_event : public Rows_log_event
+{
+public:
+ enum
+ {
+ /* Support interface to THD::binlog_prepare_pending_rows_event */
+ TYPE_CODE = DELETE_ROWS_EVENT
+ };
+
+#ifndef MYSQL_CLIENT
+ Delete_rows_log_event(THD*, TABLE*, ulong,
+ MY_BITMAP const *cols, bool is_transactional);
+#endif
+#ifdef HAVE_REPLICATION
+ Delete_rows_log_event(const char *buf, uint event_len,
+ const Format_description_log_event *description_event);
+#endif
+#if !defined(MYSQL_CLIENT)
+ static bool binlog_row_logging_function(THD *thd, TABLE *table,
+ bool is_transactional,
+ MY_BITMAP *cols,
+ uint fields,
+ const uchar *before_record,
+ const uchar *after_record
+ __attribute__((unused)))
+ {
+ return thd->binlog_delete_row(table, is_transactional,
+ cols, fields, before_record);
+ }
+#endif
+
+protected:
+ virtual Log_event_type get_type_code() { return (Log_event_type)TYPE_CODE; }
+
+#ifdef MYSQL_CLIENT
+ void print(FILE *file, PRINT_EVENT_INFO *print_event_info);
+#endif
+
+#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
+ virtual int do_before_row_operations(const Slave_reporting_capability *const);
+ virtual int do_after_row_operations(const Slave_reporting_capability *const,int);
+ virtual int do_exec_row(const Relay_log_info *const);
+#endif
+};
+
+
+#include "log_event_old.h"
+
+/**
+ @class Incident_log_event
+
+ Class representing an incident, an occurance out of the ordinary,
+ that happened on the master.
+
+ The event is used to inform the slave that something out of the
+ ordinary happened on the master that might cause the database to be
+ in an inconsistent state.
+
+ <table id="IncidentFormat">
+ <caption>Incident event format</caption>
+ <tr>
+ <th>Symbol</th>
+ <th>Format</th>
+ <th>Description</th>
+ </tr>
+ <tr>
+ <td>INCIDENT</td>
+ <td align="right">2</td>
+ <td>Incident number as an unsigned integer</td>
+ </tr>
+ <tr>
+ <td>MSGLEN</td>
+ <td align="right">1</td>
+ <td>Message length as an unsigned integer</td>
+ </tr>
+ <tr>
+ <td>MESSAGE</td>
+ <td align="right">MSGLEN</td>
+ <td>The message, if present. Not null terminated.</td>
+ </tr>
+ </table>
+
+ @section Delete_rows_log_event_binary_format Binary Format
+*/
+class Incident_log_event : public Log_event {
+public:
+#ifndef MYSQL_CLIENT
+ Incident_log_event(THD *thd_arg, Incident incident)
+ : Log_event(thd_arg, 0, FALSE), m_incident(incident)
+ {
+ DBUG_ENTER("Incident_log_event::Incident_log_event");
+ DBUG_PRINT("enter", ("m_incident: %d", m_incident));
+ m_message.str= NULL; /* Just as a precaution */
+ m_message.length= 0;
+ DBUG_VOID_RETURN;
+ }
+
+ Incident_log_event(THD *thd_arg, Incident incident, LEX_STRING const msg)
+ : Log_event(thd_arg, 0, FALSE), m_incident(incident)
+ {
+ DBUG_ENTER("Incident_log_event::Incident_log_event");
+ DBUG_PRINT("enter", ("m_incident: %d", m_incident));
+ m_message= msg;
+ DBUG_VOID_RETURN;
+ }
+#endif
+
+#ifndef MYSQL_CLIENT
+ void pack_info(Protocol*);
+#endif
+
+ Incident_log_event(const char *buf, uint event_len,
+ const Format_description_log_event *descr_event);
+
+ virtual ~Incident_log_event();
+
+#ifdef MYSQL_CLIENT
+ virtual void print(FILE *file, PRINT_EVENT_INFO *print_event_info);
+#endif
+
+#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
+ virtual int do_apply_event(Relay_log_info const *rli);
+#endif
+
+ virtual bool write_data_header(IO_CACHE *file);
+ virtual bool write_data_body(IO_CACHE *file);
+
+ virtual Log_event_type get_type_code() { return INCIDENT_EVENT; }
+
+ virtual bool is_valid() const
+ {
+ return m_incident > INCIDENT_NONE && m_incident < INCIDENT_COUNT;
+ }
+ virtual int get_data_size() {
+ return INCIDENT_HEADER_LEN + 1 + (uint) m_message.length;
+ }
+
+private:
+ const char *description() const;
+
+ Incident m_incident;
+ LEX_STRING m_message;
+};
+
+static inline bool copy_event_cache_to_file_and_reinit(IO_CACHE *cache,
+ FILE *file)
+{
+ return
+ my_b_copy_to_file(cache, file) ||
+ reinit_io_cache(cache, WRITE_CACHE, 0, FALSE, TRUE);
+}
+
+/**
+ @} (end of group Replication)
+*/
+
#endif /* _log_event_h */
diff --git a/sql/log_event_old.cc b/sql/log_event_old.cc
new file mode 100644
index 00000000000..68cd2bf4673
--- /dev/null
+++ b/sql/log_event_old.cc
@@ -0,0 +1,2907 @@
+
+#include "mysql_priv.h"
+#ifndef MYSQL_CLIENT
+#include "rpl_rli.h"
+#include "rpl_utility.h"
+#endif
+#include "log_event_old.h"
+#include "rpl_record_old.h"
+
+#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
+
+// Old implementation of do_apply_event()
+int
+Old_rows_log_event::do_apply_event(Old_rows_log_event *ev, const Relay_log_info *rli)
+{
+ DBUG_ENTER("Old_rows_log_event::do_apply_event(st_relay_log_info*)");
+ int error= 0;
+ THD *thd= ev->thd;
+ uchar const *row_start= ev->m_rows_buf;
+
+ /*
+ If m_table_id == ~0UL, then we have a dummy event that does not
+ contain any data. In that case, we just remove all tables in the
+ tables_to_lock list, close the thread tables, and return with
+ success.
+ */
+ if (ev->m_table_id == ~0UL)
+ {
+ /*
+ This one is supposed to be set: just an extra check so that
+ nothing strange has happened.
+ */
+ 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(thd);
+ thd->clear_error();
+ DBUG_RETURN(0);
+ }
+
+ /*
+ 'thd' has been set by exec_relay_log_event(), just before calling
+ do_apply_event(). We still check here to prevent future coding
+ errors.
+ */
+ DBUG_ASSERT(rli->sql_thd == thd);
+
+ /*
+ If there is no locks taken, this is the first binrow event seen
+ after the table map events. We should then lock all the tables
+ used in the transaction and proceed with execution of the actual
+ event.
+ */
+ if (!thd->lock)
+ {
+ /*
+ Lock_tables() reads the contents of thd->lex, so they must be
+ initialized.
+
+ We also call the 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
+ we need to do any changes to that value after this function.
+ */
+ lex_start(thd);
+ mysql_reset_thd_for_next_command(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.
+ */
+ if (!thd->current_stmt_binlog_row_based &&
+ mysql_bin_log.is_open() && (thd->options & OPTION_BIN_LOG))
+ {
+ thd->set_current_stmt_binlog_row_based();
+ }
+
+ if (simple_open_n_lock_tables(thd, rli->tables_to_lock))
+ {
+ uint actual_error= thd->main_da.sql_errno();
+ 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)
+ */
+ rli->report(ERROR_LEVEL, actual_error,
+ "Error '%s' on opening tables",
+ (actual_error ? thd->main_da.message() :
+ "unexpected success or fatal error"));
+ thd->is_slave_error= 1;
+ }
+ const_cast<Relay_log_info*>(rli)->clear_tables_to_lock();
+ DBUG_RETURN(actual_error);
+ }
+
+ /*
+ When the open and locking succeeded, we check all tables to
+ ensure that they still have the correct type.
+
+ We can use a down cast here since we know that every table added
+ to the tables_to_lock is a RPL_TABLE_LIST.
+ */
+
+ {
+ RPL_TABLE_LIST *ptr= rli->tables_to_lock;
+ for ( ; ptr ; ptr= static_cast<RPL_TABLE_LIST*>(ptr->next_global))
+ {
+ if (ptr->m_tabledef.compatible_with(rli, ptr->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();
+ DBUG_RETURN(Old_rows_log_event::ERR_BAD_TABLE_DEF);
+ }
+ }
+ }
+
+ /*
+ ... and then we add all the tables to the table map and remove
+ them from tables to lock.
+
+ We also invalidate the query cache for all the tables, since
+ they will now be changed.
+
+ TODO [/Matz]: Maybe the query cache should not be invalidated
+ here? It might be that a table is not changed, even though it
+ was locked for the statement. We do know that each
+ Old_rows_log_event contain at least one row, so after processing one
+ 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)
+ {
+ 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(rli->tables_to_lock);
+#endif
+ }
+
+ TABLE* table= const_cast<Relay_log_info*>(rli)->m_table_map.get_table(ev->m_table_id);
+
+ if (table)
+ {
+ /*
+ table == NULL means that this table should not be replicated
+ (this was set up by Table_map_log_event::do_apply_event()
+ which tested replicate-* rules).
+ */
+
+ /*
+ It's not needed to set_time() but
+ 1) it continues the property that "Time" in SHOW PROCESSLIST shows how
+ much slave is behind
+ 2) it will be needed when we allow replication from a table with no
+ TIMESTAMP column to a table with one.
+ So we call set_time(), like in SBR. Presently it changes nothing.
+ */
+ thd->set_time((time_t)ev->when);
+ /*
+ There are a few flags that are replicated with each row event.
+ Make sure to set/clear them before executing the main body of
+ the event.
+ */
+ if (ev->get_flags(Old_rows_log_event::NO_FOREIGN_KEY_CHECKS_F))
+ thd->options|= OPTION_NO_FOREIGN_KEY_CHECKS;
+ else
+ thd->options&= ~OPTION_NO_FOREIGN_KEY_CHECKS;
+
+ if (ev->get_flags(Old_rows_log_event::RELAXED_UNIQUE_CHECKS_F))
+ thd->options|= OPTION_RELAXED_UNIQUE_CHECKS;
+ else
+ thd->options&= ~OPTION_RELAXED_UNIQUE_CHECKS;
+ /* A small test to verify that objects have consistent types */
+ DBUG_ASSERT(sizeof(thd->options) == sizeof(OPTION_RELAXED_UNIQUE_CHECKS));
+
+ /*
+ Now we are in a statement and will stay in a statement until we
+ see a STMT_END_F.
+
+ We set this flag here, before actually applying any rows, in
+ case the SQL thread is stopped and we need to detect that we're
+ inside a statement and halting abruptly might cause problems
+ when restarting.
+ */
+ const_cast<Relay_log_info*>(rli)->set_flag(Relay_log_info::IN_STMT);
+
+ error= do_before_row_operations(table);
+ while (error == 0 && row_start < ev->m_rows_end)
+ {
+ uchar const *row_end= NULL;
+ if ((error= do_prepare_row(thd, rli, table, row_start, &row_end)))
+ break; // We should perform the after-row operation even in
+ // the case of error
+
+ DBUG_ASSERT(row_end != NULL); // cannot happen
+ DBUG_ASSERT(row_end <= ev->m_rows_end);
+
+ /* in_use can have been set to NULL in close_tables_for_reopen */
+ THD* old_thd= table->in_use;
+ if (!table->in_use)
+ table->in_use= thd;
+ error= do_exec_row(table);
+ table->in_use = old_thd;
+ switch (error)
+ {
+ /* Some recoverable errors */
+ case HA_ERR_RECORD_CHANGED:
+ case HA_ERR_KEY_NOT_FOUND: /* Idempotency support: OK if
+ tuple does not exist */
+ error= 0;
+ case 0:
+ break;
+
+ default:
+ rli->report(ERROR_LEVEL, thd->main_da.sql_errno(),
+ "Error in %s event: row application failed. %s",
+ ev->get_type_str(),
+ thd->is_error() ? thd->main_da.message() : "");
+ thd->is_slave_error= 1;
+ break;
+ }
+
+ row_start= row_end;
+ }
+ DBUG_EXECUTE_IF("STOP_SLAVE_after_first_Rows_event",
+ const_cast<Relay_log_info*>(rli)->abort_slave= 1;);
+ error= do_after_row_operations(table, error);
+ if (!ev->cache_stmt)
+ {
+ DBUG_PRINT("info", ("Marked that we need to keep log"));
+ thd->options|= OPTION_KEEP_LOG;
+ }
+ }
+
+ /*
+ 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, thd->main_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,
+ thd->is_error() ? thd->main_da.message() : "");
+
+ /*
+ If one day we honour --skip-slave-errors in row-based replication, and
+ the error should be skipped, then we would clear mappings, rollback,
+ close tables, but the slave SQL thread would not stop and then may
+ assume the mapping is still available, the tables are still open...
+ So then we should clear mappings/rollback/close here only if this is a
+ STMT_END_F.
+ For now we code, knowing that error is not skippable and so slave SQL
+ thread is certainly going to stop.
+ rollback at the caller along with sbr.
+ */
+ thd->reset_current_stmt_binlog_row_based();
+ const_cast<Relay_log_info*>(rli)->cleanup_context(thd, error);
+ 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
+
+
+#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
+
+/*
+ Check if there are more UNIQUE keys after the given key.
+*/
+static int
+last_uniq_key(TABLE *table, uint keyno)
+{
+ while (++keyno < table->s->keys)
+ if (table->key_info[keyno].flags & HA_NOSAME)
+ return 0;
+ return 1;
+}
+
+
+/*
+ Compares table->record[0] and table->record[1]
+
+ Returns TRUE if different.
+*/
+static bool record_compare(TABLE *table)
+{
+ /*
+ Need to set the X bit and the filler bits in both records since
+ there are engines that do not set it correctly.
+
+ In addition, since MyISAM checks that one hasn't tampered with the
+ record, it is necessary to restore the old bytes into the record
+ after doing the comparison.
+
+ TODO[record format ndb]: Remove it once NDB returns correct
+ records. Check that the other engines also return correct records.
+ */
+
+ bool result= FALSE;
+ uchar saved_x[2], saved_filler[2];
+
+ if (table->s->null_bytes > 0)
+ {
+ for (int i = 0 ; i < 2 ; ++i)
+ {
+ saved_x[i]= table->record[i][0];
+ saved_filler[i]= table->record[i][table->s->null_bytes - 1];
+ table->record[i][0]|= 1U;
+ table->record[i][table->s->null_bytes - 1]|=
+ 256U - (1U << table->s->last_null_bit_pos);
+ }
+ }
+
+ if (table->s->blob_fields + table->s->varchar_fields == 0)
+ {
+ result= cmp_record(table,record[1]);
+ goto record_compare_exit;
+ }
+
+ /* Compare null bits */
+ if (memcmp(table->null_flags,
+ table->null_flags+table->s->rec_buff_length,
+ table->s->null_bytes))
+ {
+ result= TRUE; // Diff in NULL value
+ goto record_compare_exit;
+ }
+
+ /* Compare updated fields */
+ for (Field **ptr=table->field ; *ptr ; ptr++)
+ {
+ if ((*ptr)->cmp_binary_offset(table->s->rec_buff_length))
+ {
+ result= TRUE;
+ goto record_compare_exit;
+ }
+ }
+
+record_compare_exit:
+ /*
+ Restore the saved bytes.
+
+ TODO[record format ndb]: Remove this code once NDB returns the
+ correct record format.
+ */
+ if (table->s->null_bytes > 0)
+ {
+ for (int i = 0 ; i < 2 ; ++i)
+ {
+ table->record[i][0]= saved_x[i];
+ table->record[i][table->s->null_bytes - 1]= saved_filler[i];
+ }
+ }
+
+ return result;
+}
+
+
+/*
+ Copy "extra" columns from record[1] to record[0].
+
+ Copy the extra fields that are not present on the master but are
+ present on the slave from record[1] to record[0]. This is used
+ after fetching a record that are to be updated, either inside
+ replace_record() or as part of executing an update_row().
+ */
+static int
+copy_extra_record_fields(TABLE *table,
+ size_t master_reclength,
+ my_ptrdiff_t master_fields)
+{
+ DBUG_ENTER("copy_extra_record_fields(table, master_reclen, master_fields)");
+ DBUG_PRINT("info", ("Copying to 0x%lx "
+ "from field %lu at offset %lu "
+ "to field %d at offset %lu",
+ (long) table->record[0],
+ (ulong) master_fields, (ulong) master_reclength,
+ table->s->fields, table->s->reclength));
+ /*
+ Copying the extra fields of the slave that does not exist on
+ master into record[0] (which are basically the default values).
+ */
+
+ if (table->s->fields < (uint) master_fields)
+ DBUG_RETURN(0);
+
+ DBUG_ASSERT(master_reclength <= table->s->reclength);
+ if (master_reclength < table->s->reclength)
+ bmove_align(table->record[0] + master_reclength,
+ table->record[1] + master_reclength,
+ table->s->reclength - master_reclength);
+
+ /*
+ Bit columns are special. We iterate over all the remaining
+ columns and copy the "extra" bits to the new record. This is
+ not a very good solution: it should be refactored on
+ opportunity.
+
+ REFACTORING SUGGESTION (Matz). Introduce a member function
+ similar to move_field_offset() called copy_field_offset() to
+ copy field values and implement it for all Field subclasses. Use
+ this function to copy data from the found record to the record
+ that are going to be inserted.
+
+ The copy_field_offset() function need to be a virtual function,
+ which in this case will prevent copying an entire range of
+ fields efficiently.
+ */
+ {
+ Field **field_ptr= table->field + master_fields;
+ for ( ; *field_ptr ; ++field_ptr)
+ {
+ /*
+ Set the null bit according to the values in record[1]
+ */
+ if ((*field_ptr)->maybe_null() &&
+ (*field_ptr)->is_null_in_record(reinterpret_cast<uchar*>(table->record[1])))
+ (*field_ptr)->set_null();
+ else
+ (*field_ptr)->set_notnull();
+
+ /*
+ Do the extra work for special columns.
+ */
+ switch ((*field_ptr)->real_type())
+ {
+ default:
+ /* Nothing to do */
+ break;
+
+ case MYSQL_TYPE_BIT:
+ Field_bit *f= static_cast<Field_bit*>(*field_ptr);
+ if (f->bit_len > 0)
+ {
+ my_ptrdiff_t const offset= table->record[1] - table->record[0];
+ uchar const bits=
+ get_rec_bits(f->bit_ptr + offset, f->bit_ofs, f->bit_len);
+ set_rec_bits(bits, f->bit_ptr, f->bit_ofs, f->bit_len);
+ }
+ break;
+ }
+ }
+ }
+ DBUG_RETURN(0); // All OK
+}
+
+
+/*
+ Replace the provided record in the database.
+
+ SYNOPSIS
+ replace_record()
+ thd Thread context for writing the record.
+ table Table to which record should be written.
+ master_reclength
+ Offset to first column that is not present on the master,
+ alternatively the length of the record on the master
+ side.
+
+ RETURN VALUE
+ Error code on failure, 0 on success.
+
+ DESCRIPTION
+ Similar to how it is done in mysql_insert(), we first try to do
+ a ha_write_row() and of that fails due to duplicated keys (or
+ indices), we do an ha_update_row() or a ha_delete_row() instead.
+ */
+static int
+replace_record(THD *thd, TABLE *table,
+ ulong const master_reclength,
+ uint const master_fields)
+{
+ DBUG_ENTER("replace_record");
+ DBUG_ASSERT(table != NULL && thd != NULL);
+
+ int error;
+ int keynum;
+ auto_afree_ptr<char> key(NULL);
+
+#ifndef DBUG_OFF
+ DBUG_DUMP("record[0]", table->record[0], table->s->reclength);
+ DBUG_PRINT_BITSET("debug", "write_set = %s", table->write_set);
+ DBUG_PRINT_BITSET("debug", "read_set = %s", table->read_set);
+#endif
+
+ while ((error= table->file->ha_write_row(table->record[0])))
+ {
+ if (error == HA_ERR_LOCK_DEADLOCK || error == HA_ERR_LOCK_WAIT_TIMEOUT)
+ {
+ table->file->print_error(error, MYF(0)); /* to check at exec_relay_log_event */
+ DBUG_RETURN(error);
+ }
+ if ((keynum= table->file->get_dup_key(error)) < 0)
+ {
+ table->file->print_error(error, MYF(0));
+ /*
+ We failed to retrieve the duplicate key
+ - either because the error was not "duplicate key" error
+ - or because the information which key is not available
+ */
+ DBUG_RETURN(error);
+ }
+
+ /*
+ We need to retrieve the old row into record[1] to be able to
+ either update or delete the offending record. We either:
+
+ - use rnd_pos() with a row-id (available as dupp_row) to the
+ offending row, if that is possible (MyISAM and Blackhole), or else
+
+ - use index_read_idx() with the key that is duplicated, to
+ retrieve the offending row.
+ */
+ if (table->file->ha_table_flags() & HA_DUPLICATE_POS)
+ {
+ error= table->file->rnd_pos(table->record[1], table->file->dup_ref);
+ if (error)
+ {
+ DBUG_PRINT("info",("rnd_pos() returns error %d",error));
+ if (error == HA_ERR_RECORD_DELETED)
+ error= HA_ERR_KEY_NOT_FOUND;
+ table->file->print_error(error, MYF(0));
+ DBUG_RETURN(error);
+ }
+ }
+ else
+ {
+ if (table->file->extra(HA_EXTRA_FLUSH_CACHE))
+ {
+ DBUG_RETURN(my_errno);
+ }
+
+ if (key.get() == NULL)
+ {
+ key.assign(static_cast<char*>(my_alloca(table->s->max_unique_length)));
+ if (key.get() == NULL)
+ DBUG_RETURN(ENOMEM);
+ }
+
+ key_copy((uchar*)key.get(), table->record[0], table->key_info + keynum,
+ 0);
+ error= table->file->index_read_idx_map(table->record[1], keynum,
+ (const uchar*)key.get(),
+ HA_WHOLE_KEY,
+ HA_READ_KEY_EXACT);
+ if (error)
+ {
+ DBUG_PRINT("info", ("index_read_idx() returns error %d", error));
+ if (error == HA_ERR_RECORD_DELETED)
+ error= HA_ERR_KEY_NOT_FOUND;
+ table->file->print_error(error, MYF(0));
+ DBUG_RETURN(error);
+ }
+ }
+
+ /*
+ Now, table->record[1] should contain the offending row. That
+ will enable us to update it or, alternatively, delete it (so
+ that we can insert the new row afterwards).
+
+ First we copy the columns into table->record[0] that are not
+ present on the master from table->record[1], if there are any.
+ */
+ copy_extra_record_fields(table, master_reclength, master_fields);
+
+ /*
+ REPLACE is defined as either INSERT or DELETE + INSERT. If
+ possible, we can replace it with an UPDATE, but that will not
+ work on InnoDB if FOREIGN KEY checks are necessary.
+
+ I (Matz) am not sure of the reason for the last_uniq_key()
+ check as, but I'm guessing that it's something along the
+ following lines.
+
+ Suppose that we got the duplicate key to be a key that is not
+ the last unique key for the table and we perform an update:
+ then there might be another key for which the unique check will
+ fail, so we're better off just deleting the row and inserting
+ the correct row.
+ */
+ if (last_uniq_key(table, keynum) &&
+ !table->file->referenced_by_foreign_key())
+ {
+ error=table->file->ha_update_row(table->record[1],
+ table->record[0]);
+ if (error && error != HA_ERR_RECORD_IS_THE_SAME)
+ table->file->print_error(error, MYF(0));
+ else
+ error= 0;
+ DBUG_RETURN(error);
+ }
+ else
+ {
+ if ((error= table->file->ha_delete_row(table->record[1])))
+ {
+ table->file->print_error(error, MYF(0));
+ DBUG_RETURN(error);
+ }
+ /* Will retry ha_write_row() with the offending row removed. */
+ }
+ }
+
+ DBUG_RETURN(error);
+}
+
+
+/**
+ Find the row given by 'key', if the table has keys, or else use a table scan
+ to find (and fetch) the row.
+
+ If the engine allows random access of the records, a combination of
+ position() and rnd_pos() will be used.
+
+ @param table Pointer to table to search
+ @param key Pointer to key to use for search, if table has key
+
+ @pre <code>table->record[0]</code> shall contain the row to locate
+ and <code>key</code> shall contain a key to use for searching, if
+ the engine has a key.
+
+ @post If the return value is zero, <code>table->record[1]</code>
+ will contain the fetched row and the internal "cursor" will refer to
+ the row. If the return value is non-zero,
+ <code>table->record[1]</code> is undefined. In either case,
+ <code>table->record[0]</code> is undefined.
+
+ @return Zero if the row was successfully fetched into
+ <code>table->record[1]</code>, error code otherwise.
+ */
+
+static int find_and_fetch_row(TABLE *table, uchar *key)
+{
+ DBUG_ENTER("find_and_fetch_row(TABLE *table, uchar *key, uchar *record)");
+ DBUG_PRINT("enter", ("table: 0x%lx, key: 0x%lx record: 0x%lx",
+ (long) table, (long) key, (long) table->record[1]));
+
+ DBUG_ASSERT(table->in_use != NULL);
+
+ DBUG_DUMP("record[0]", table->record[0], table->s->reclength);
+
+ if ((table->file->ha_table_flags() & HA_PRIMARY_KEY_REQUIRED_FOR_POSITION) &&
+ table->s->primary_key < MAX_KEY)
+ {
+ /*
+ Use a more efficient method to fetch the record given by
+ table->record[0] if the engine allows it. We first compute a
+ row reference using the position() member function (it will be
+ stored in table->file->ref) and the use rnd_pos() to position
+ the "cursor" (i.e., record[0] in this case) at the correct row.
+
+ TODO: Add a check that the correct record has been fetched by
+ comparing with the original record. Take into account that the
+ record on the master and slave can be of different
+ length. Something along these lines should work:
+
+ ADD>>> store_record(table,record[1]);
+ int error= table->file->rnd_pos(table->record[0], table->file->ref);
+ ADD>>> DBUG_ASSERT(memcmp(table->record[1], table->record[0],
+ table->s->reclength) == 0);
+
+ */
+ table->file->position(table->record[0]);
+ int error= table->file->rnd_pos(table->record[0], table->file->ref);
+ /*
+ rnd_pos() returns the record in table->record[0], so we have to
+ move it to table->record[1].
+ */
+ bmove_align(table->record[1], table->record[0], table->s->reclength);
+ DBUG_RETURN(error);
+ }
+
+ /* We need to retrieve all fields */
+ /* TODO: Move this out from this function to main loop */
+ table->use_all_columns();
+
+ if (table->s->keys > 0)
+ {
+ int error;
+ /* We have a key: search the table using the index */
+ if (!table->file->inited && (error= table->file->ha_index_init(0, FALSE)))
+ DBUG_RETURN(error);
+
+ /*
+ Don't print debug messages when running valgrind since they can
+ trigger false warnings.
+ */
+#ifndef HAVE_purify
+ DBUG_DUMP("table->record[0]", table->record[0], table->s->reclength);
+ DBUG_DUMP("table->record[1]", table->record[1], table->s->reclength);
+#endif
+
+ /*
+ We need to set the null bytes to ensure that the filler bit are
+ all set when returning. There are storage engines that just set
+ the necessary bits on the bytes and don't set the filler bits
+ correctly.
+ */
+ my_ptrdiff_t const pos=
+ table->s->null_bytes > 0 ? table->s->null_bytes - 1 : 0;
+ table->record[1][pos]= 0xFF;
+ if ((error= table->file->index_read_map(table->record[1], key, HA_WHOLE_KEY,
+ HA_READ_KEY_EXACT)))
+ {
+ table->file->print_error(error, MYF(0));
+ table->file->ha_index_end();
+ DBUG_RETURN(error);
+ }
+
+ /*
+ Don't print debug messages when running valgrind since they can
+ trigger false warnings.
+ */
+#ifndef HAVE_purify
+ DBUG_DUMP("table->record[0]", table->record[0], table->s->reclength);
+ DBUG_DUMP("table->record[1]", table->record[1], table->s->reclength);
+#endif
+ /*
+ Below is a minor "optimization". If the key (i.e., key number
+ 0) has the HA_NOSAME flag set, we know that we have found the
+ correct record (since there can be no duplicates); otherwise, we
+ have to compare the record with the one found to see if it is
+ the correct one.
+
+ CAVEAT! This behaviour is essential for the replication of,
+ e.g., the mysql.proc table since the correct record *shall* be
+ found using the primary key *only*. There shall be no
+ comparison of non-PK columns to decide if the correct record is
+ found. I can see no scenario where it would be incorrect to
+ chose the row to change only using a PK or an UNNI.
+ */
+ if (table->key_info->flags & HA_NOSAME)
+ {
+ table->file->ha_index_end();
+ DBUG_RETURN(0);
+ }
+
+ while (record_compare(table))
+ {
+ int error;
+
+ /*
+ We need to set the null bytes to ensure that the filler bit
+ are all set when returning. There are storage engines that
+ just set the necessary bits on the bytes and don't set the
+ filler bits correctly.
+
+ TODO[record format ndb]: Remove this code once NDB returns the
+ correct record format.
+ */
+ if (table->s->null_bytes > 0)
+ {
+ table->record[1][table->s->null_bytes - 1]|=
+ 256U - (1U << table->s->last_null_bit_pos);
+ }
+
+ while ((error= table->file->index_next(table->record[1])))
+ {
+ /* We just skip records that has already been deleted */
+ if (error == HA_ERR_RECORD_DELETED)
+ continue;
+ table->file->print_error(error, MYF(0));
+ table->file->ha_index_end();
+ DBUG_RETURN(error);
+ }
+ }
+
+ /*
+ Have to restart the scan to be able to fetch the next row.
+ */
+ table->file->ha_index_end();
+ }
+ else
+ {
+ int restart_count= 0; // Number of times scanning has restarted from top
+ int error;
+
+ /* We don't have a key: search the table using rnd_next() */
+ if ((error= table->file->ha_rnd_init(1)))
+ return error;
+
+ /* Continue until we find the right record or have made a full loop */
+ do
+ {
+ restart_rnd_next:
+ error= table->file->rnd_next(table->record[1]);
+
+ DBUG_DUMP("record[0]", table->record[0], table->s->reclength);
+ DBUG_DUMP("record[1]", table->record[1], table->s->reclength);
+
+ switch (error) {
+ case 0:
+ break;
+
+ /*
+ If the record was deleted, we pick the next one without doing
+ any comparisons.
+ */
+ case HA_ERR_RECORD_DELETED:
+ goto restart_rnd_next;
+
+ case HA_ERR_END_OF_FILE:
+ if (++restart_count < 2)
+ table->file->ha_rnd_init(1);
+ break;
+
+ default:
+ table->file->print_error(error, MYF(0));
+ DBUG_PRINT("info", ("Record not found"));
+ table->file->ha_rnd_end();
+ DBUG_RETURN(error);
+ }
+ }
+ while (restart_count < 2 && record_compare(table));
+
+ /*
+ Have to restart the scan to be able to fetch the next row.
+ */
+ DBUG_PRINT("info", ("Record %sfound", restart_count == 2 ? "not " : ""));
+ table->file->ha_rnd_end();
+
+ DBUG_ASSERT(error == HA_ERR_END_OF_FILE || error == 0);
+ DBUG_RETURN(error);
+ }
+
+ DBUG_RETURN(0);
+}
+
+
+/**********************************************************
+ Row handling primitives for Write_rows_log_event_old
+ **********************************************************/
+
+int Write_rows_log_event_old::do_before_row_operations(TABLE *table)
+{
+ int error= 0;
+
+ /*
+ We are using REPLACE semantics and not INSERT IGNORE semantics
+ when writing rows, that is: new rows replace old rows. We need to
+ inform the storage engine that it should use this behaviour.
+ */
+
+ /* Tell the storage engine that we are using REPLACE semantics. */
+ thd->lex->duplicates= DUP_REPLACE;
+
+ /*
+ Pretend we're executing a REPLACE command: this is needed for
+ InnoDB and NDB Cluster since they are not (properly) checking the
+ lex->duplicates flag.
+ */
+ thd->lex->sql_command= SQLCOM_REPLACE;
+ /*
+ Do not raise the error flag in case of hitting to an unique attribute
+ */
+ table->file->extra(HA_EXTRA_IGNORE_DUP_KEY);
+ /*
+ NDB specific: update from ndb master wrapped as Write_rows
+ */
+ /*
+ so that the event should be applied to replace slave's row
+ */
+ table->file->extra(HA_EXTRA_WRITE_CAN_REPLACE);
+ /*
+ NDB specific: if update from ndb master wrapped as Write_rows
+ does not find the row it's assumed idempotent binlog applying
+ is taking place; don't raise the error.
+ */
+ table->file->extra(HA_EXTRA_IGNORE_NO_KEY);
+ /*
+ TODO: the cluster team (Tomas?) says that it's better if the engine knows
+ how many rows are going to be inserted, then it can allocate needed memory
+ from the start.
+ */
+ table->file->ha_start_bulk_insert(0);
+ /*
+ We need TIMESTAMP_NO_AUTO_SET otherwise ha_write_row() will not use fill
+ any TIMESTAMP column with data from the row but instead will use
+ the event's current time.
+ As we replicate from TIMESTAMP to TIMESTAMP and slave has no extra
+ columns, we know that all TIMESTAMP columns on slave will receive explicit
+ data from the row, so TIMESTAMP_NO_AUTO_SET is ok.
+ When we allow a table without TIMESTAMP to be replicated to a table having
+ more columns including a TIMESTAMP column, or when we allow a TIMESTAMP
+ column to be replicated into a BIGINT column and the slave's table has a
+ TIMESTAMP column, then the slave's TIMESTAMP column will take its value
+ from set_time() which we called earlier (consistent with SBR). And then in
+ some cases we won't want TIMESTAMP_NO_AUTO_SET (will require some code to
+ analyze if explicit data is provided for slave's TIMESTAMP columns).
+ */
+ table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET;
+ return error;
+}
+
+
+int Write_rows_log_event_old::do_after_row_operations(TABLE *table, int error)
+{
+ int local_error= 0;
+ table->file->extra(HA_EXTRA_NO_IGNORE_DUP_KEY);
+ table->file->extra(HA_EXTRA_WRITE_CANNOT_REPLACE);
+ /*
+ reseting the extra with
+ table->file->extra(HA_EXTRA_NO_IGNORE_NO_KEY);
+ fires bug#27077
+ todo: explain or fix
+ */
+ if ((local_error= table->file->ha_end_bulk_insert()))
+ {
+ table->file->print_error(local_error, MYF(0));
+ }
+ return error? error : local_error;
+}
+
+
+int
+Write_rows_log_event_old::do_prepare_row(THD *thd_arg,
+ Relay_log_info const *rli,
+ TABLE *table,
+ uchar const *row_start,
+ uchar const **row_end)
+{
+ DBUG_ASSERT(table != NULL);
+ DBUG_ASSERT(row_start && row_end);
+
+ int error;
+ error= unpack_row_old(const_cast<Relay_log_info*>(rli),
+ table, m_width, table->record[0],
+ row_start, &m_cols, row_end, &m_master_reclength,
+ table->write_set, PRE_GA_WRITE_ROWS_EVENT);
+ bitmap_copy(table->read_set, table->write_set);
+ return error;
+}
+
+
+int Write_rows_log_event_old::do_exec_row(TABLE *table)
+{
+ DBUG_ASSERT(table != NULL);
+ int error= replace_record(thd, table, m_master_reclength, m_width);
+ return error;
+}
+
+
+/**********************************************************
+ Row handling primitives for Delete_rows_log_event_old
+ **********************************************************/
+
+int Delete_rows_log_event_old::do_before_row_operations(TABLE *table)
+{
+ DBUG_ASSERT(m_memory == NULL);
+
+ if ((table->file->ha_table_flags() & HA_PRIMARY_KEY_REQUIRED_FOR_POSITION) &&
+ table->s->primary_key < MAX_KEY)
+ {
+ /*
+ We don't need to allocate any memory for m_after_image and
+ m_key since they are not used.
+ */
+ return 0;
+ }
+
+ int error= 0;
+
+ if (table->s->keys > 0)
+ {
+ m_memory= (uchar*) my_multi_malloc(MYF(MY_WME),
+ &m_after_image,
+ (uint) table->s->reclength,
+ &m_key,
+ (uint) table->key_info->key_length,
+ NullS);
+ }
+ else
+ {
+ m_after_image= (uchar*) my_malloc(table->s->reclength, MYF(MY_WME));
+ m_memory= (uchar*)m_after_image;
+ m_key= NULL;
+ }
+ if (!m_memory)
+ return HA_ERR_OUT_OF_MEM;
+
+ return error;
+}
+
+
+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
+ m_memory= NULL;
+ m_after_image= NULL;
+ m_key= NULL;
+
+ return error;
+}
+
+
+int
+Delete_rows_log_event_old::do_prepare_row(THD *thd_arg,
+ Relay_log_info const *rli,
+ TABLE *table,
+ uchar const *row_start,
+ uchar const **row_end)
+{
+ int error;
+ DBUG_ASSERT(row_start && row_end);
+ /*
+ This assertion actually checks that there is at least as many
+ columns on the slave as on the master.
+ */
+ DBUG_ASSERT(table->s->fields >= m_width);
+
+ error= unpack_row_old(const_cast<Relay_log_info*>(rli),
+ table, m_width, table->record[0],
+ row_start, &m_cols, row_end, &m_master_reclength,
+ table->read_set, PRE_GA_DELETE_ROWS_EVENT);
+ /*
+ If we will access rows using the random access method, m_key will
+ be set to NULL, so we do not need to make a key copy in that case.
+ */
+ if (m_key)
+ {
+ KEY *const key_info= table->key_info;
+
+ key_copy(m_key, table->record[0], key_info, 0);
+ }
+
+ return error;
+}
+
+
+int Delete_rows_log_event_old::do_exec_row(TABLE *table)
+{
+ int error;
+ DBUG_ASSERT(table != NULL);
+
+ if (!(error= ::find_and_fetch_row(table, m_key)))
+ {
+ /*
+ Now we should have the right row to delete. We are using
+ record[0] since it is guaranteed to point to a record with the
+ correct value.
+ */
+ error= table->file->ha_delete_row(table->record[0]);
+ }
+ return error;
+}
+
+
+/**********************************************************
+ Row handling primitives for Update_rows_log_event_old
+ **********************************************************/
+
+int Update_rows_log_event_old::do_before_row_operations(TABLE *table)
+{
+ DBUG_ASSERT(m_memory == NULL);
+
+ int error= 0;
+
+ if (table->s->keys > 0)
+ {
+ m_memory= (uchar*) my_multi_malloc(MYF(MY_WME),
+ &m_after_image,
+ (uint) table->s->reclength,
+ &m_key,
+ (uint) table->key_info->key_length,
+ NullS);
+ }
+ else
+ {
+ m_after_image= (uchar*) my_malloc(table->s->reclength, MYF(MY_WME));
+ m_memory= m_after_image;
+ m_key= NULL;
+ }
+ if (!m_memory)
+ return HA_ERR_OUT_OF_MEM;
+
+ table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET;
+
+ return error;
+}
+
+
+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));
+ m_memory= NULL;
+ m_after_image= NULL;
+ m_key= NULL;
+
+ return error;
+}
+
+
+int Update_rows_log_event_old::do_prepare_row(THD *thd_arg,
+ Relay_log_info const *rli,
+ TABLE *table,
+ uchar const *row_start,
+ uchar const **row_end)
+{
+ int error;
+ DBUG_ASSERT(row_start && row_end);
+ /*
+ This assertion actually checks that there is at least as many
+ columns on the slave as on the master.
+ */
+ DBUG_ASSERT(table->s->fields >= m_width);
+
+ /* record[0] is the before image for the update */
+ error= unpack_row_old(const_cast<Relay_log_info*>(rli),
+ table, m_width, table->record[0],
+ row_start, &m_cols, row_end, &m_master_reclength,
+ table->read_set, PRE_GA_UPDATE_ROWS_EVENT);
+ row_start = *row_end;
+ /* m_after_image is the after image for the update */
+ error= unpack_row_old(const_cast<Relay_log_info*>(rli),
+ table, m_width, m_after_image,
+ row_start, &m_cols, row_end, &m_master_reclength,
+ table->write_set, PRE_GA_UPDATE_ROWS_EVENT);
+
+ DBUG_DUMP("record[0]", table->record[0], table->s->reclength);
+ DBUG_DUMP("m_after_image", m_after_image, table->s->reclength);
+
+ /*
+ If we will access rows using the random access method, m_key will
+ be set to NULL, so we do not need to make a key copy in that case.
+ */
+ if (m_key)
+ {
+ KEY *const key_info= table->key_info;
+
+ key_copy(m_key, table->record[0], key_info, 0);
+ }
+
+ return error;
+}
+
+
+int Update_rows_log_event_old::do_exec_row(TABLE *table)
+{
+ DBUG_ASSERT(table != NULL);
+
+ int error= ::find_and_fetch_row(table, m_key);
+ if (error)
+ return error;
+
+ /*
+ We have to ensure that the new record (i.e., the after image) is
+ in record[0] and the old record (i.e., the before image) is in
+ record[1]. This since some storage engines require this (for
+ example, the partition engine).
+
+ Since find_and_fetch_row() puts the fetched record (i.e., the old
+ record) in record[1], we can keep it there. We put the new record
+ (i.e., the after image) into record[0], and copy the fields that
+ are on the slave (i.e., in record[1]) into record[0], effectively
+ overwriting the default values that where put there by the
+ unpack_row() function.
+ */
+ bmove_align(table->record[0], m_after_image, table->s->reclength);
+ copy_extra_record_fields(table, m_master_reclength, m_width);
+
+ /*
+ Now we have the right row to update. The old row (the one we're
+ looking for) is in record[1] and the new row has is in record[0].
+ We also have copied the original values already in the slave's
+ database into the after image delivered from the master.
+ */
+ error= table->file->ha_update_row(table->record[1], table->record[0]);
+ if (error == HA_ERR_RECORD_IS_THE_SAME)
+ error= 0;
+
+ return error;
+}
+
+#endif
+
+
+/**************************************************************************
+ Rows_log_event member functions
+**************************************************************************/
+
+#ifndef MYSQL_CLIENT
+Old_rows_log_event::Old_rows_log_event(THD *thd_arg, TABLE *tbl_arg, ulong tid,
+ MY_BITMAP const *cols,
+ bool is_transactional)
+ : Log_event(thd_arg, 0, is_transactional),
+ m_row_count(0),
+ m_table(tbl_arg),
+ m_table_id(tid),
+ m_width(tbl_arg ? tbl_arg->s->fields : 1),
+ m_rows_buf(0), m_rows_cur(0), m_rows_end(0), m_flags(0)
+#ifdef HAVE_REPLICATION
+ , m_curr_row(NULL), m_curr_row_end(NULL), m_key(NULL)
+#endif
+{
+
+ // This constructor should not be reached.
+ assert(0);
+
+ /*
+ We allow a special form of dummy event when the table, and cols
+ are null and the table id is ~0UL. This is a temporary
+ solution, to be able to terminate a started statement in the
+ binary log: the extraneous events will be removed in the future.
+ */
+ DBUG_ASSERT(tbl_arg && tbl_arg->s && tid != ~0UL ||
+ !tbl_arg && !cols && tid == ~0UL);
+
+ if (thd_arg->options & OPTION_NO_FOREIGN_KEY_CHECKS)
+ set_flags(NO_FOREIGN_KEY_CHECKS_F);
+ if (thd_arg->options & OPTION_RELAXED_UNIQUE_CHECKS)
+ set_flags(RELAXED_UNIQUE_CHECKS_F);
+ /* if bitmap_init fails, caught in is_valid() */
+ if (likely(!bitmap_init(&m_cols,
+ m_width <= sizeof(m_bitbuf)*8 ? m_bitbuf : NULL,
+ m_width,
+ false)))
+ {
+ /* Cols can be zero if this is a dummy binrows event */
+ if (likely(cols != NULL))
+ {
+ memcpy(m_cols.bitmap, cols->bitmap, no_bytes_in_map(cols));
+ create_last_word_mask(&m_cols);
+ }
+ }
+ else
+ {
+ // Needed because bitmap_init() does not set it to null on failure
+ m_cols.bitmap= 0;
+ }
+}
+#endif
+
+
+Old_rows_log_event::Old_rows_log_event(const char *buf, uint event_len,
+ Log_event_type event_type,
+ const Format_description_log_event
+ *description_event)
+ : Log_event(buf, description_event),
+ m_row_count(0),
+#ifndef MYSQL_CLIENT
+ m_table(NULL),
+#endif
+ m_table_id(0), m_rows_buf(0), m_rows_cur(0), m_rows_end(0)
+#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
+ , m_curr_row(NULL), m_curr_row_end(NULL), m_key(NULL)
+#endif
+{
+ DBUG_ENTER("Old_rows_log_event::Old_Rows_log_event(const char*,...)");
+ uint8 const common_header_len= description_event->common_header_len;
+ uint8 const post_header_len= description_event->post_header_len[event_type-1];
+
+ DBUG_PRINT("enter",("event_len: %u common_header_len: %d "
+ "post_header_len: %d",
+ event_len, common_header_len,
+ post_header_len));
+
+ const char *post_start= buf + common_header_len;
+ DBUG_DUMP("post_header", (uchar*) post_start, post_header_len);
+ post_start+= RW_MAPID_OFFSET;
+ if (post_header_len == 6)
+ {
+ /* Master is of an intermediate source tree before 5.1.4. Id is 4 bytes */
+ m_table_id= uint4korr(post_start);
+ post_start+= 4;
+ }
+ else
+ {
+ m_table_id= (ulong) uint6korr(post_start);
+ post_start+= RW_FLAGS_OFFSET;
+ }
+
+ m_flags= uint2korr(post_start);
+
+ uchar const *const var_start=
+ (const uchar *)buf + common_header_len + post_header_len;
+ uchar const *const ptr_width= var_start;
+ uchar *ptr_after_width= (uchar*) ptr_width;
+ DBUG_PRINT("debug", ("Reading from %p", ptr_after_width));
+ m_width = net_field_length(&ptr_after_width);
+ DBUG_PRINT("debug", ("m_width=%lu", m_width));
+ /* if bitmap_init fails, catched in is_valid() */
+ if (likely(!bitmap_init(&m_cols,
+ m_width <= sizeof(m_bitbuf)*8 ? m_bitbuf : NULL,
+ m_width,
+ false)))
+ {
+ DBUG_PRINT("debug", ("Reading from %p", ptr_after_width));
+ memcpy(m_cols.bitmap, ptr_after_width, (m_width + 7) / 8);
+ create_last_word_mask(&m_cols);
+ ptr_after_width+= (m_width + 7) / 8;
+ DBUG_DUMP("m_cols", (uchar*) m_cols.bitmap, no_bytes_in_map(&m_cols));
+ }
+ else
+ {
+ // Needed because bitmap_init() does not set it to null on failure
+ m_cols.bitmap= NULL;
+ DBUG_VOID_RETURN;
+ }
+
+ const uchar* const ptr_rows_data= (const uchar*) ptr_after_width;
+ size_t const data_size= event_len - (ptr_rows_data - (const uchar *) buf);
+ DBUG_PRINT("info",("m_table_id: %lu m_flags: %d m_width: %lu data_size: %lu",
+ m_table_id, m_flags, m_width, (ulong) data_size));
+ DBUG_DUMP("rows_data", (uchar*) ptr_rows_data, data_size);
+
+ m_rows_buf= (uchar*) my_malloc(data_size, MYF(MY_WME));
+ if (likely((bool)m_rows_buf))
+ {
+#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
+ m_curr_row= m_rows_buf;
+#endif
+ m_rows_end= m_rows_buf + data_size;
+ m_rows_cur= m_rows_end;
+ memcpy(m_rows_buf, ptr_rows_data, data_size);
+ }
+ else
+ m_cols.bitmap= 0; // to not free it
+
+ DBUG_VOID_RETURN;
+}
+
+
+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));
+}
+
+
+int Old_rows_log_event::get_data_size()
+{
+ uchar buf[sizeof(m_width)+1];
+ uchar *end= net_store_length(buf, (m_width + 7) / 8);
+
+ DBUG_EXECUTE_IF("old_row_based_repl_4_byte_map_id_master",
+ return 6 + no_bytes_in_map(&m_cols) + (end - buf) +
+ (m_rows_cur - m_rows_buf););
+ int data_size= ROWS_HEADER_LEN;
+ data_size+= no_bytes_in_map(&m_cols);
+ data_size+= (uint) (end - buf);
+
+ data_size+= (uint) (m_rows_cur - m_rows_buf);
+ return data_size;
+}
+
+
+#ifndef MYSQL_CLIENT
+int Old_rows_log_event::do_add_row_data(uchar *row_data, size_t length)
+{
+ /*
+ When the table has a primary key, we would probably want, by default, to
+ log only the primary key value instead of the entire "before image". This
+ would save binlog space. TODO
+ */
+ DBUG_ENTER("Old_rows_log_event::do_add_row_data");
+ DBUG_PRINT("enter", ("row_data: 0x%lx length: %lu", (ulong) row_data,
+ (ulong) length));
+ /*
+ Don't print debug messages when running valgrind since they can
+ trigger false warnings.
+ */
+#ifndef HAVE_purify
+ DBUG_DUMP("row_data", row_data, min(length, 32));
+#endif
+
+ DBUG_ASSERT(m_rows_buf <= m_rows_cur);
+ DBUG_ASSERT(!m_rows_buf || m_rows_end && m_rows_buf < m_rows_end);
+ DBUG_ASSERT(m_rows_cur <= m_rows_end);
+
+ /* The cast will always work since m_rows_cur <= m_rows_end */
+ if (static_cast<size_t>(m_rows_end - m_rows_cur) <= length)
+ {
+ size_t const block_size= 1024;
+ my_ptrdiff_t const cur_size= m_rows_cur - m_rows_buf;
+ my_ptrdiff_t const new_alloc=
+ block_size * ((cur_size + length + block_size - 1) / block_size);
+
+ uchar* const new_buf= (uchar*)my_realloc((uchar*)m_rows_buf, (uint) new_alloc,
+ MYF(MY_ALLOW_ZERO_PTR|MY_WME));
+ if (unlikely(!new_buf))
+ DBUG_RETURN(HA_ERR_OUT_OF_MEM);
+
+ /* If the memory moved, we need to move the pointers */
+ if (new_buf != m_rows_buf)
+ {
+ m_rows_buf= new_buf;
+ m_rows_cur= m_rows_buf + cur_size;
+ }
+
+ /*
+ The end pointer should always be changed to point to the end of
+ the allocated memory.
+ */
+ m_rows_end= m_rows_buf + new_alloc;
+ }
+
+ DBUG_ASSERT(m_rows_cur + length <= m_rows_end);
+ memcpy(m_rows_cur, row_data, length);
+ m_rows_cur+= length;
+ m_row_count++;
+ DBUG_RETURN(0);
+}
+#endif
+
+
+#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
+int Old_rows_log_event::do_apply_event(Relay_log_info const *rli)
+{
+ DBUG_ENTER("Old_rows_log_event::do_apply_event(Relay_log_info*)");
+ int error= 0;
+
+ /*
+ If m_table_id == ~0UL, then we have a dummy event that does not
+ contain any data. In that case, we just remove all tables in the
+ tables_to_lock list, close the thread tables, and return with
+ success.
+ */
+ if (m_table_id == ~0UL)
+ {
+ /*
+ This one is supposed to be set: just an extra check so that
+ nothing strange has happened.
+ */
+ DBUG_ASSERT(get_flags(STMT_END_F));
+
+ const_cast<Relay_log_info*>(rli)->clear_tables_to_lock();
+ close_thread_tables(thd);
+ thd->clear_error();
+ DBUG_RETURN(0);
+ }
+
+ /*
+ 'thd' has been set by exec_relay_log_event(), just before calling
+ do_apply_event(). We still check here to prevent future coding
+ errors.
+ */
+ DBUG_ASSERT(rli->sql_thd == thd);
+
+ /*
+ If there is no locks taken, this is the first binrow event seen
+ after the table map events. We should then lock all the tables
+ used in the transaction and proceed with execution of the actual
+ event.
+ */
+ if (!thd->lock)
+ {
+ 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
+ Table_map_log_event::do_apply_event() we don't call
+ mysql_init_query() as that may reset the binlog format.
+ */
+ lex_start(thd);
+
+ while ((error= lock_tables(thd, rli->tables_to_lock,
+ rli->tables_to_lock_count, &need_reopen)))
+ {
+ if (!need_reopen)
+ {
+ 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.
+ */
+ thd->binlog_flush_pending_rows_event(false);
+ 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)))
+ {
+ 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);
+ }
+ }
+
+ /*
+ When the open and locking succeeded, we check all tables to
+ ensure that they still have the correct type.
+
+ We can use a down cast here since we know that every table added
+ to the tables_to_lock is a RPL_TABLE_LIST.
+ */
+
+ {
+ RPL_TABLE_LIST *ptr= rli->tables_to_lock;
+ for ( ; ptr ; ptr= static_cast<RPL_TABLE_LIST*>(ptr->next_global))
+ {
+ if (ptr->m_tabledef.compatible_with(rli, ptr->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();
+ DBUG_RETURN(ERR_BAD_TABLE_DEF);
+ }
+ }
+ }
+
+ /*
+ ... and then we add all the tables to the table map and remove
+ them from tables to lock.
+
+ We also invalidate the query cache for all the tables, since
+ they will now be changed.
+
+ TODO [/Matz]: Maybe the query cache should not be invalidated
+ here? It might be that a table is not changed, even though it
+ was locked for the statement. We do know that each
+ Old_rows_log_event contain at least one row, so after processing one
+ 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)
+ {
+ 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(rli->tables_to_lock);
+#endif
+ }
+
+ TABLE*
+ table=
+ m_table= const_cast<Relay_log_info*>(rli)->m_table_map.get_table(m_table_id);
+
+ if (table)
+ {
+ /*
+ table == NULL means that this table should not be replicated
+ (this was set up by Table_map_log_event::do_apply_event()
+ which tested replicate-* rules).
+ */
+
+ /*
+ It's not needed to set_time() but
+ 1) it continues the property that "Time" in SHOW PROCESSLIST shows how
+ much slave is behind
+ 2) it will be needed when we allow replication from a table with no
+ TIMESTAMP column to a table with one.
+ So we call set_time(), like in SBR. Presently it changes nothing.
+ */
+ thd->set_time((time_t)when);
+ /*
+ There are a few flags that are replicated with each row event.
+ Make sure to set/clear them before executing the main body of
+ the event.
+ */
+ if (get_flags(NO_FOREIGN_KEY_CHECKS_F))
+ thd->options|= OPTION_NO_FOREIGN_KEY_CHECKS;
+ else
+ thd->options&= ~OPTION_NO_FOREIGN_KEY_CHECKS;
+
+ if (get_flags(RELAXED_UNIQUE_CHECKS_F))
+ thd->options|= OPTION_RELAXED_UNIQUE_CHECKS;
+ else
+ thd->options&= ~OPTION_RELAXED_UNIQUE_CHECKS;
+ /* A small test to verify that objects have consistent types */
+ DBUG_ASSERT(sizeof(thd->options) == sizeof(OPTION_RELAXED_UNIQUE_CHECKS));
+
+ /*
+ Now we are in a statement and will stay in a statement until we
+ see a STMT_END_F.
+
+ We set this flag here, before actually applying any rows, in
+ case the SQL thread is stopped and we need to detect that we're
+ inside a statement and halting abruptly might cause problems
+ when restarting.
+ */
+ const_cast<Relay_log_info*>(rli)->set_flag(Relay_log_info::IN_STMT);
+
+ if ( m_width == table->s->fields && bitmap_is_set_all(&m_cols))
+ set_flags(COMPLETE_ROWS_F);
+
+ /*
+ Set tables write and read sets.
+
+ Read_set contains all slave columns (in case we are going to fetch
+ a complete record from slave)
+
+ Write_set equals the m_cols bitmap sent from master but it can be
+ longer if slave has extra columns.
+ */
+
+ DBUG_PRINT_BITSET("debug", "Setting table's write_set from: %s", &m_cols);
+
+ bitmap_set_all(table->read_set);
+ bitmap_set_all(table->write_set);
+ if (!get_flags(COMPLETE_ROWS_F))
+ bitmap_intersect(table->write_set,&m_cols);
+
+ // Do event specific preparations
+
+ error= do_before_row_operations(rli);
+
+ // row processing loop
+
+ while (error == 0 && m_curr_row < m_rows_end)
+ {
+ /* in_use can have been set to NULL in close_tables_for_reopen */
+ THD* old_thd= table->in_use;
+ if (!table->in_use)
+ table->in_use= thd;
+
+ error= do_exec_row(rli);
+
+ DBUG_PRINT("info", ("error: %d", error));
+ DBUG_ASSERT(error != HA_ERR_RECORD_DELETED);
+
+ table->in_use = old_thd;
+ switch (error)
+ {
+ case 0:
+ break;
+
+ /* Some recoverable errors */
+ case HA_ERR_RECORD_CHANGED:
+ case HA_ERR_KEY_NOT_FOUND: /* Idempotency support: OK if
+ tuple does not exist */
+ error= 0;
+ break;
+
+ default:
+ rli->report(ERROR_LEVEL, thd->net.last_errno,
+ "Error in %s event: row application failed. %s",
+ get_type_str(),
+ thd->net.last_error ? thd->net.last_error : "");
+ thd->is_slave_error= 1;
+ break;
+ }
+
+ /*
+ If m_curr_row_end was not set during event execution (e.g., because
+ of errors) we can't proceed to the next row. If the error is transient
+ (i.e., error==0 at this point) we must call unpack_current_row() to set
+ m_curr_row_end.
+ */
+
+ DBUG_PRINT("info", ("error: %d", error));
+ DBUG_PRINT("info", ("curr_row: 0x%lu; curr_row_end: 0x%lu; rows_end: 0x%lu",
+ (ulong) m_curr_row, (ulong) m_curr_row_end, (ulong) m_rows_end));
+
+ if (!m_curr_row_end && !error)
+ unpack_current_row(rli);
+
+ // at this moment m_curr_row_end should be set
+ DBUG_ASSERT(error || m_curr_row_end != NULL);
+ DBUG_ASSERT(error || m_curr_row < m_curr_row_end);
+ DBUG_ASSERT(error || m_curr_row_end <= m_rows_end);
+
+ m_curr_row= m_curr_row_end;
+
+ } // row processing loop
+
+ DBUG_EXECUTE_IF("STOP_SLAVE_after_first_Rows_event",
+ const_cast<Relay_log_info*>(rli)->abort_slave= 1;);
+ error= do_after_row_operations(rli, error);
+ if (!cache_stmt)
+ {
+ DBUG_PRINT("info", ("Marked that we need to keep log"));
+ thd->options|= OPTION_KEEP_LOG;
+ }
+ } // 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,
+ "Error in %s event: error during transaction execution "
+ "on table %s.%s. %s",
+ get_type_str(), table->s->db.str,
+ table->s->table_name.str,
+ thd->net.last_error ? thd->net.last_error : "");
+
+ /*
+ If one day we honour --skip-slave-errors in row-based replication, and
+ the error should be skipped, then we would clear mappings, rollback,
+ close tables, but the slave SQL thread would not stop and then may
+ assume the mapping is still available, the tables are still open...
+ So then we should clear mappings/rollback/close here only if this is a
+ STMT_END_F.
+ For now we code, knowing that error is not skippable and so slave SQL
+ thread is certainly going to stop.
+ rollback at the caller along with sbr.
+ */
+ thd->reset_current_stmt_binlog_row_based();
+ const_cast<Relay_log_info*>(rli)->cleanup_context(thd, error);
+ 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) &&
+ !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 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
+ Relay_log_info::last_event_start_time and all its occurrences.
+ */
+ const_cast<Relay_log_info*>(rli)->last_event_start_time= my_time(0);
+ }
+
+ DBUG_RETURN(0);
+}
+
+
+Log_event::enum_skip_reason
+Old_rows_log_event::do_shall_skip(Relay_log_info *rli)
+{
+ /*
+ If the slave skip counter is 1 and this event does not end a
+ statement, then we should not start executing on the next event.
+ Otherwise, we defer the decision to the normal skipping logic.
+ */
+ if (rli->slave_skip_counter == 1 && !get_flags(STMT_END_F))
+ return Log_event::EVENT_SKIP_IGNORE;
+ else
+ return Log_event::do_shall_skip(rli);
+}
+
+int
+Old_rows_log_event::do_update_pos(Relay_log_info *rli)
+{
+ DBUG_ENTER("Old_rows_log_event::do_update_pos");
+ int error= 0;
+
+ DBUG_PRINT("info", ("flags: %s",
+ get_flags(STMT_END_F) ? "STMT_END_F " : ""));
+
+ if (get_flags(STMT_END_F))
+ {
+ /*
+ This is the end of a statement or transaction, so close (and
+ unlock) the tables we opened when processing the
+ Table_map_log_event starting the statement.
+
+ OBSERVER. This will clear *all* mappings, not only those that
+ are open for the table. There is not good handle for on-close
+ actions for tables.
+
+ NOTE. Even if we have no table ('table' == 0) we still need to be
+ here, so that we increase the group relay log position. If we didn't, we
+ could have a group relay log position which lags behind "forever"
+ (assume the last master's transaction is ignored by the slave because of
+ replicate-ignore rules).
+ */
+ thd->binlog_flush_pending_rows_event(true);
+
+ /*
+ If this event is not in a transaction, the call below will, if some
+ transactional storage engines are involved, commit the statement into
+ them and flush the pending event to binlog.
+ If this event is in a transaction, the call will do nothing, but a
+ Xid_log_event will come next which will, if some transactional engines
+ are involved, commit the transaction and flush the pending event to the
+ binlog.
+ */
+ error= ha_autocommit_or_rollback(thd, 0);
+
+ /*
+ Now what if this is not a transactional engine? we still need to
+ flush the pending event to the binlog; we did it with
+ thd->binlog_flush_pending_rows_event(). Note that we imitate
+ what is done for real queries: a call to
+ ha_autocommit_or_rollback() (sometimes only if involves a
+ transactional engine), and a call to be sure to have the pending
+ event flushed.
+ */
+
+ thd->reset_current_stmt_binlog_row_based();
+ rli->cleanup_context(thd, 0);
+ if (error == 0)
+ {
+ /*
+ Indicate that a statement is finished.
+ Step the group log position if we are not in a transaction,
+ otherwise increase the event log position.
+ */
+ rli->stmt_done(log_pos, when);
+
+ /*
+ Clear any errors pushed in thd->net.client_last_err* if for
+ example "no key found" (as this is allowed). This is a safety
+ measure; apparently those errors (e.g. when executing a
+ Delete_rows_log_event_old of a non-existing row, like in
+ rpl_row_mystery22.test, thd->net.last_error = "Can't
+ find record in 't1'" and last_errno=1032) do not become
+ visible. We still prefer to wipe them out.
+ */
+ thd->clear_error();
+ }
+ else
+ rli->report(ERROR_LEVEL, error,
+ "Error in %s event: commit of row events failed, "
+ "table `%s`.`%s`",
+ get_type_str(), m_table->s->db.str,
+ m_table->s->table_name.str);
+ }
+ else
+ {
+ rli->inc_event_relay_log_pos();
+ }
+
+ DBUG_RETURN(error);
+}
+
+#endif /* !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) */
+
+
+#ifndef MYSQL_CLIENT
+bool Old_rows_log_event::write_data_header(IO_CACHE *file)
+{
+ uchar buf[ROWS_HEADER_LEN]; // No need to init the buffer
+
+ // This method should not be reached.
+ assert(0);
+
+ DBUG_ASSERT(m_table_id != ~0UL);
+ DBUG_EXECUTE_IF("old_row_based_repl_4_byte_map_id_master",
+ {
+ int4store(buf + 0, m_table_id);
+ int2store(buf + 4, m_flags);
+ return (my_b_safe_write(file, buf, 6));
+ });
+ int6store(buf + RW_MAPID_OFFSET, (ulonglong)m_table_id);
+ int2store(buf + RW_FLAGS_OFFSET, m_flags);
+ return (my_b_safe_write(file, buf, ROWS_HEADER_LEN));
+}
+
+
+bool Old_rows_log_event::write_data_body(IO_CACHE*file)
+{
+ /*
+ Note that this should be the number of *bits*, not the number of
+ bytes.
+ */
+ uchar sbuf[sizeof(m_width)];
+ my_ptrdiff_t const data_size= m_rows_cur - m_rows_buf;
+
+ // This method should not be reached.
+ assert(0);
+
+ bool res= false;
+ uchar *const sbuf_end= net_store_length(sbuf, (size_t) m_width);
+ DBUG_ASSERT(static_cast<size_t>(sbuf_end - sbuf) <= sizeof(sbuf));
+
+ DBUG_DUMP("m_width", sbuf, (size_t) (sbuf_end - sbuf));
+ res= res || my_b_safe_write(file, sbuf, (size_t) (sbuf_end - sbuf));
+
+ DBUG_DUMP("m_cols", (uchar*) m_cols.bitmap, no_bytes_in_map(&m_cols));
+ res= res || my_b_safe_write(file, (uchar*) m_cols.bitmap,
+ no_bytes_in_map(&m_cols));
+ DBUG_DUMP("rows", m_rows_buf, data_size);
+ res= res || my_b_safe_write(file, m_rows_buf, (size_t) data_size);
+
+ return res;
+
+}
+#endif
+
+
+#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
+void Old_rows_log_event::pack_info(Protocol *protocol)
+{
+ char buf[256];
+ char const *const flagstr=
+ get_flags(STMT_END_F) ? " flags: STMT_END_F" : "";
+ size_t bytes= my_snprintf(buf, sizeof(buf),
+ "table_id: %lu%s", m_table_id, flagstr);
+ protocol->store(buf, bytes, &my_charset_bin);
+}
+#endif
+
+
+#ifdef MYSQL_CLIENT
+void Old_rows_log_event::print_helper(FILE *file,
+ PRINT_EVENT_INFO *print_event_info,
+ char const *const name)
+{
+ IO_CACHE *const head= &print_event_info->head_cache;
+ IO_CACHE *const body= &print_event_info->body_cache;
+ if (!print_event_info->short_form)
+ {
+ bool const last_stmt_event= get_flags(STMT_END_F);
+ print_header(head, print_event_info, !last_stmt_event);
+ my_b_printf(head, "\t%s: table id %lu%s\n",
+ name, m_table_id,
+ last_stmt_event ? " flags: STMT_END_F" : "");
+ print_base64(body, print_event_info, !last_stmt_event);
+ }
+
+ if (get_flags(STMT_END_F))
+ {
+ copy_event_cache_to_file_and_reinit(head, file);
+ copy_event_cache_to_file_and_reinit(body, file);
+ }
+}
+#endif
+
+
+#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
+/**
+ Write the current row into event's table.
+
+ The row is located in the row buffer, pointed by @c m_curr_row member.
+ Number of columns of the row is stored in @c m_width member (it can be
+ different from the number of columns in the table to which we insert).
+ Bitmap @c m_cols indicates which columns are present in the row. It is assumed
+ that event's table is already open and pointed by @c m_table.
+
+ If the same record already exists in the table it can be either overwritten
+ or an error is reported depending on the value of @c overwrite flag
+ (error reporting not yet implemented). Note that the matching record can be
+ different from the row we insert if we use primary keys to identify records in
+ the table.
+
+ The row to be inserted can contain values only for selected columns. The
+ missing columns are filled with default values using @c prepare_record()
+ function. If a matching record is found in the table and @c overwritte is
+ true, the missing columns are taken from it.
+
+ @param rli Relay log info (needed for row unpacking).
+ @param overwrite
+ Shall we overwrite if the row already exists or signal
+ error (currently ignored).
+
+ @returns Error code on failure, 0 on success.
+
+ This method, if successful, sets @c m_curr_row_end pointer to point at the
+ next row in the rows buffer. This is done when unpacking the row to be
+ inserted.
+
+ @note If a matching record is found, it is either updated using
+ @c ha_update_row() or first deleted and then new record written.
+*/
+
+int
+Old_rows_log_event::write_row(const Relay_log_info *const rli,
+ const bool overwrite)
+{
+ DBUG_ENTER("write_row");
+ DBUG_ASSERT(m_table != NULL && thd != NULL);
+
+ TABLE *table= m_table; // pointer to event's table
+ int error;
+ int keynum;
+ auto_afree_ptr<char> key(NULL);
+
+ /* fill table->record[0] with default values */
+
+ if ((error= prepare_record(table, m_width,
+ TRUE /* check if columns have def. values */)))
+ DBUG_RETURN(error);
+
+ /* unpack row into table->record[0] */
+ error= unpack_current_row(rli); // TODO: how to handle errors?
+
+#ifndef DBUG_OFF
+ DBUG_DUMP("record[0]", table->record[0], table->s->reclength);
+ DBUG_PRINT_BITSET("debug", "write_set = %s", table->write_set);
+ DBUG_PRINT_BITSET("debug", "read_set = %s", table->read_set);
+#endif
+
+ /*
+ Try to write record. If a corresponding record already exists in the table,
+ we try to change it using ha_update_row() if possible. Otherwise we delete
+ it and repeat the whole process again.
+
+ TODO: Add safety measures against infinite looping.
+ */
+
+ while ((error= table->file->ha_write_row(table->record[0])))
+ {
+ if (error == HA_ERR_LOCK_DEADLOCK || error == HA_ERR_LOCK_WAIT_TIMEOUT)
+ {
+ table->file->print_error(error, MYF(0)); /* to check at exec_relay_log_event */
+ DBUG_RETURN(error);
+ }
+ if ((keynum= table->file->get_dup_key(error)) < 0)
+ {
+ DBUG_PRINT("info",("Can't locate duplicate key (get_dup_key returns %d)",keynum));
+ table->file->print_error(error, MYF(0));
+ /*
+ We failed to retrieve the duplicate key
+ - either because the error was not "duplicate key" error
+ - or because the information which key is not available
+ */
+ DBUG_RETURN(error);
+ }
+
+ /*
+ We need to retrieve the old row into record[1] to be able to
+ either update or delete the offending record. We either:
+
+ - use rnd_pos() with a row-id (available as dupp_row) to the
+ offending row, if that is possible (MyISAM and Blackhole), or else
+
+ - use index_read_idx() with the key that is duplicated, to
+ retrieve the offending row.
+ */
+ if (table->file->ha_table_flags() & HA_DUPLICATE_POS)
+ {
+ DBUG_PRINT("info",("Locating offending record using rnd_pos()"));
+ error= table->file->rnd_pos(table->record[1], table->file->dup_ref);
+ if (error)
+ {
+ DBUG_PRINT("info",("rnd_pos() returns error %d",error));
+ if (error == HA_ERR_RECORD_DELETED)
+ error= HA_ERR_KEY_NOT_FOUND;
+ table->file->print_error(error, MYF(0));
+ DBUG_RETURN(error);
+ }
+ }
+ else
+ {
+ DBUG_PRINT("info",("Locating offending record using index_read_idx()"));
+
+ if (table->file->extra(HA_EXTRA_FLUSH_CACHE))
+ {
+ DBUG_PRINT("info",("Error when setting HA_EXTRA_FLUSH_CACHE"));
+ DBUG_RETURN(my_errno);
+ }
+
+ if (key.get() == NULL)
+ {
+ key.assign(static_cast<char*>(my_alloca(table->s->max_unique_length)));
+ if (key.get() == NULL)
+ {
+ DBUG_PRINT("info",("Can't allocate key buffer"));
+ DBUG_RETURN(ENOMEM);
+ }
+ }
+
+ key_copy((uchar*)key.get(), table->record[0], table->key_info + keynum,
+ 0);
+ error= table->file->index_read_idx_map(table->record[1], keynum,
+ (const uchar*)key.get(),
+ HA_WHOLE_KEY,
+ HA_READ_KEY_EXACT);
+ if (error)
+ {
+ DBUG_PRINT("info",("index_read_idx() returns error %d", error));
+ if (error == HA_ERR_RECORD_DELETED)
+ error= HA_ERR_KEY_NOT_FOUND;
+ table->file->print_error(error, MYF(0));
+ DBUG_RETURN(error);
+ }
+ }
+
+ /*
+ Now, record[1] should contain the offending row. That
+ will enable us to update it or, alternatively, delete it (so
+ that we can insert the new row afterwards).
+ */
+
+ /*
+ If row is incomplete we will use the record found to fill
+ missing columns.
+ */
+ if (!get_flags(COMPLETE_ROWS_F))
+ {
+ restore_record(table,record[1]);
+ error= unpack_current_row(rli);
+ }
+
+#ifndef DBUG_OFF
+ DBUG_PRINT("debug",("preparing for update: before and after image"));
+ DBUG_DUMP("record[1] (before)", table->record[1], table->s->reclength);
+ DBUG_DUMP("record[0] (after)", table->record[0], table->s->reclength);
+#endif
+
+ /*
+ REPLACE is defined as either INSERT or DELETE + INSERT. If
+ possible, we can replace it with an UPDATE, but that will not
+ work on InnoDB if FOREIGN KEY checks are necessary.
+
+ I (Matz) am not sure of the reason for the last_uniq_key()
+ check as, but I'm guessing that it's something along the
+ following lines.
+
+ Suppose that we got the duplicate key to be a key that is not
+ the last unique key for the table and we perform an update:
+ then there might be another key for which the unique check will
+ fail, so we're better off just deleting the row and inserting
+ the correct row.
+ */
+ if (last_uniq_key(table, keynum) &&
+ !table->file->referenced_by_foreign_key())
+ {
+ DBUG_PRINT("info",("Updating row using ha_update_row()"));
+ error=table->file->ha_update_row(table->record[1],
+ table->record[0]);
+ switch (error) {
+
+ case HA_ERR_RECORD_IS_THE_SAME:
+ DBUG_PRINT("info",("ignoring HA_ERR_RECORD_IS_THE_SAME error from"
+ " ha_update_row()"));
+ error= 0;
+
+ case 0:
+ break;
+
+ default:
+ DBUG_PRINT("info",("ha_update_row() returns error %d",error));
+ table->file->print_error(error, MYF(0));
+ }
+
+ DBUG_RETURN(error);
+ }
+ else
+ {
+ DBUG_PRINT("info",("Deleting offending row and trying to write new one again"));
+ if ((error= table->file->ha_delete_row(table->record[1])))
+ {
+ DBUG_PRINT("info",("ha_delete_row() returns error %d",error));
+ table->file->print_error(error, MYF(0));
+ DBUG_RETURN(error);
+ }
+ /* Will retry ha_write_row() with the offending row removed. */
+ }
+ }
+
+ DBUG_RETURN(error);
+}
+
+
+/**
+ Locate the current row in event's table.
+
+ The current row is pointed by @c m_curr_row. Member @c m_width tells how many
+ columns are there in the row (this can be differnet from the number of columns
+ in the table). It is assumed that event's table is already open and pointed
+ by @c m_table.
+
+ If a corresponding record is found in the table it is stored in
+ @c m_table->record[0]. Note that when record is located based on a primary
+ key, it is possible that the record found differs from the row being located.
+
+ If no key is specified or table does not have keys, a table scan is used to
+ find the row. In that case the row should be complete and contain values for
+ all columns. However, it can still be shorter than the table, i.e. the table
+ can contain extra columns not present in the row. It is also possible that
+ the table has fewer columns than the row being located.
+
+ @returns Error code on failure, 0 on success.
+
+ @post In case of success @c m_table->record[0] contains the record found.
+ Also, the internal "cursor" of the table is positioned at the record found.
+
+ @note If the engine allows random access of the records, a combination of
+ @c position() and @c rnd_pos() will be used.
+ */
+
+int Old_rows_log_event::find_row(const Relay_log_info *rli)
+{
+ DBUG_ENTER("find_row");
+
+ DBUG_ASSERT(m_table && m_table->in_use != NULL);
+
+ TABLE *table= m_table;
+ int error;
+
+ /* unpack row - missing fields get default values */
+
+ // TODO: shall we check and report errors here?
+ prepare_record(table, m_width, FALSE /* don't check errors */);
+ error= unpack_current_row(rli);
+
+#ifndef DBUG_OFF
+ DBUG_PRINT("info",("looking for the following record"));
+ DBUG_DUMP("record[0]", table->record[0], table->s->reclength);
+#endif
+
+ if ((table->file->ha_table_flags() & HA_PRIMARY_KEY_REQUIRED_FOR_POSITION) &&
+ table->s->primary_key < MAX_KEY)
+ {
+ /*
+ Use a more efficient method to fetch the record given by
+ table->record[0] if the engine allows it. We first compute a
+ row reference using the position() member function (it will be
+ stored in table->file->ref) and the use rnd_pos() to position
+ the "cursor" (i.e., record[0] in this case) at the correct row.
+
+ TODO: Add a check that the correct record has been fetched by
+ comparing with the original record. Take into account that the
+ record on the master and slave can be of different
+ length. Something along these lines should work:
+
+ ADD>>> store_record(table,record[1]);
+ int error= table->file->rnd_pos(table->record[0], table->file->ref);
+ ADD>>> DBUG_ASSERT(memcmp(table->record[1], table->record[0],
+ table->s->reclength) == 0);
+
+ */
+ DBUG_PRINT("info",("locating record using primary key (position)"));
+ int error= table->file->rnd_pos_by_record(table->record[0]);
+ if (error)
+ {
+ DBUG_PRINT("info",("rnd_pos returns error %d",error));
+ if (error == HA_ERR_RECORD_DELETED)
+ error= HA_ERR_KEY_NOT_FOUND;
+ table->file->print_error(error, MYF(0));
+ }
+ DBUG_RETURN(error);
+ }
+
+ // We can't use position() - try other methods.
+
+ /*
+ We need to retrieve all fields
+ TODO: Move this out from this function to main loop
+ */
+ table->use_all_columns();
+
+ /*
+ Save copy of the record in table->record[1]. It might be needed
+ later if linear search is used to find exact match.
+ */
+ store_record(table,record[1]);
+
+ if (table->s->keys > 0)
+ {
+ DBUG_PRINT("info",("locating record using primary key (index_read)"));
+
+ /* We have a key: search the table using the index */
+ if (!table->file->inited && (error= table->file->ha_index_init(0, FALSE)))
+ {
+ DBUG_PRINT("info",("ha_index_init returns error %d",error));
+ table->file->print_error(error, MYF(0));
+ DBUG_RETURN(error);
+ }
+
+ /* Fill key data for the row */
+
+ DBUG_ASSERT(m_key);
+ key_copy(m_key, table->record[0], table->key_info, 0);
+
+ /*
+ Don't print debug messages when running valgrind since they can
+ trigger false warnings.
+ */
+#ifndef HAVE_purify
+ DBUG_DUMP("key data", m_key, table->key_info->key_length);
+#endif
+
+ /*
+ We need to set the null bytes to ensure that the filler bit are
+ all set when returning. There are storage engines that just set
+ the necessary bits on the bytes and don't set the filler bits
+ correctly.
+ */
+ my_ptrdiff_t const pos=
+ table->s->null_bytes > 0 ? table->s->null_bytes - 1 : 0;
+ table->record[0][pos]= 0xFF;
+
+ if ((error= table->file->index_read_map(table->record[0], m_key,
+ HA_WHOLE_KEY,
+ HA_READ_KEY_EXACT)))
+ {
+ DBUG_PRINT("info",("no record matching the key found in the table"));
+ if (error == HA_ERR_RECORD_DELETED)
+ error= HA_ERR_KEY_NOT_FOUND;
+ table->file->print_error(error, MYF(0));
+ table->file->ha_index_end();
+ DBUG_RETURN(error);
+ }
+
+ /*
+ Don't print debug messages when running valgrind since they can
+ trigger false warnings.
+ */
+#ifndef HAVE_purify
+ DBUG_PRINT("info",("found first matching record"));
+ DBUG_DUMP("record[0]", table->record[0], table->s->reclength);
+#endif
+ /*
+ Below is a minor "optimization". If the key (i.e., key number
+ 0) has the HA_NOSAME flag set, we know that we have found the
+ correct record (since there can be no duplicates); otherwise, we
+ have to compare the record with the one found to see if it is
+ the correct one.
+
+ CAVEAT! This behaviour is essential for the replication of,
+ e.g., the mysql.proc table since the correct record *shall* be
+ found using the primary key *only*. There shall be no
+ comparison of non-PK columns to decide if the correct record is
+ found. I can see no scenario where it would be incorrect to
+ chose the row to change only using a PK or an UNNI.
+ */
+ if (table->key_info->flags & HA_NOSAME)
+ {
+ table->file->ha_index_end();
+ DBUG_RETURN(0);
+ }
+
+ /*
+ In case key is not unique, we still have to iterate over records found
+ and find the one which is identical to the row given. A copy of the
+ record we are looking for is stored in record[1].
+ */
+ DBUG_PRINT("info",("non-unique index, scanning it to find matching record"));
+
+ while (record_compare(table))
+ {
+ /*
+ We need to set the null bytes to ensure that the filler bit
+ are all set when returning. There are storage engines that
+ just set the necessary bits on the bytes and don't set the
+ filler bits correctly.
+
+ TODO[record format ndb]: Remove this code once NDB returns the
+ correct record format.
+ */
+ if (table->s->null_bytes > 0)
+ {
+ table->record[0][table->s->null_bytes - 1]|=
+ 256U - (1U << table->s->last_null_bit_pos);
+ }
+
+ while ((error= table->file->index_next(table->record[0])))
+ {
+ /* We just skip records that has already been deleted */
+ if (error == HA_ERR_RECORD_DELETED)
+ continue;
+ DBUG_PRINT("info",("no record matching the given row found"));
+ table->file->print_error(error, MYF(0));
+ table->file->ha_index_end();
+ DBUG_RETURN(error);
+ }
+ }
+
+ /*
+ Have to restart the scan to be able to fetch the next row.
+ */
+ table->file->ha_index_end();
+ }
+ else
+ {
+ DBUG_PRINT("info",("locating record using table scan (rnd_next)"));
+
+ 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(1)))
+ {
+ DBUG_PRINT("info",("error initializing table scan"
+ " (ha_rnd_init returns %d)",error));
+ table->file->print_error(error, MYF(0));
+ DBUG_RETURN(error);
+ }
+
+ /* Continue until we find the right record or have made a full loop */
+ do
+ {
+ restart_rnd_next:
+ error= table->file->rnd_next(table->record[0]);
+
+ switch (error) {
+
+ case 0:
+ break;
+
+ case HA_ERR_RECORD_DELETED:
+ goto restart_rnd_next;
+
+ case HA_ERR_END_OF_FILE:
+ if (++restart_count < 2)
+ table->file->ha_rnd_init(1);
+ 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();
+ DBUG_RETURN(error);
+ }
+ }
+ while (restart_count < 2 && 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);
+ DBUG_RETURN(error);
+ }
+
+ DBUG_RETURN(0);
+}
+
+#endif
+
+
+/**************************************************************************
+ Write_rows_log_event member functions
+**************************************************************************/
+
+/*
+ Constructor used to build an event for writing to the binary log.
+ */
+#if !defined(MYSQL_CLIENT)
+Write_rows_log_event_old::Write_rows_log_event_old(THD *thd_arg,
+ TABLE *tbl_arg,
+ ulong tid_arg,
+ MY_BITMAP const *cols,
+ bool is_transactional)
+ : Old_rows_log_event(thd_arg, tbl_arg, tid_arg, cols, is_transactional)
+{
+
+ // This constructor should not be reached.
+ assert(0);
+
+}
+#endif
+
+
+/*
+ Constructor used by slave to read the event from the binary log.
+ */
+#ifdef HAVE_REPLICATION
+Write_rows_log_event_old::Write_rows_log_event_old(const char *buf,
+ uint event_len,
+ const Format_description_log_event
+ *description_event)
+: Old_rows_log_event(buf, event_len, PRE_GA_WRITE_ROWS_EVENT,
+ description_event)
+{
+}
+#endif
+
+
+#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
+int
+Write_rows_log_event_old::do_before_row_operations(const Slave_reporting_capability *const)
+{
+ int error= 0;
+
+ /*
+ We are using REPLACE semantics and not INSERT IGNORE semantics
+ when writing rows, that is: new rows replace old rows. We need to
+ inform the storage engine that it should use this behaviour.
+ */
+
+ /* Tell the storage engine that we are using REPLACE semantics. */
+ thd->lex->duplicates= DUP_REPLACE;
+
+ /*
+ Pretend we're executing a REPLACE command: this is needed for
+ InnoDB and NDB Cluster since they are not (properly) checking the
+ lex->duplicates flag.
+ */
+ thd->lex->sql_command= SQLCOM_REPLACE;
+ /*
+ Do not raise the error flag in case of hitting to an unique attribute
+ */
+ m_table->file->extra(HA_EXTRA_IGNORE_DUP_KEY);
+ /*
+ NDB specific: update from ndb master wrapped as Write_rows
+ */
+ /*
+ so that the event should be applied to replace slave's row
+ */
+ m_table->file->extra(HA_EXTRA_WRITE_CAN_REPLACE);
+ /*
+ NDB specific: if update from ndb master wrapped as Write_rows
+ does not find the row it's assumed idempotent binlog applying
+ is taking place; don't raise the error.
+ */
+ m_table->file->extra(HA_EXTRA_IGNORE_NO_KEY);
+ /*
+ TODO: the cluster team (Tomas?) says that it's better if the engine knows
+ how many rows are going to be inserted, then it can allocate needed memory
+ from the start.
+ */
+ m_table->file->ha_start_bulk_insert(0);
+ /*
+ We need TIMESTAMP_NO_AUTO_SET otherwise ha_write_row() will not use fill
+ any TIMESTAMP column with data from the row but instead will use
+ the event's current time.
+ As we replicate from TIMESTAMP to TIMESTAMP and slave has no extra
+ columns, we know that all TIMESTAMP columns on slave will receive explicit
+ data from the row, so TIMESTAMP_NO_AUTO_SET is ok.
+ When we allow a table without TIMESTAMP to be replicated to a table having
+ more columns including a TIMESTAMP column, or when we allow a TIMESTAMP
+ column to be replicated into a BIGINT column and the slave's table has a
+ TIMESTAMP column, then the slave's TIMESTAMP column will take its value
+ from set_time() which we called earlier (consistent with SBR). And then in
+ some cases we won't want TIMESTAMP_NO_AUTO_SET (will require some code to
+ analyze if explicit data is provided for slave's TIMESTAMP columns).
+ */
+ m_table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET;
+ return error;
+}
+
+
+int
+Write_rows_log_event_old::do_after_row_operations(const Slave_reporting_capability *const,
+ int error)
+{
+ int local_error= 0;
+ m_table->file->extra(HA_EXTRA_NO_IGNORE_DUP_KEY);
+ m_table->file->extra(HA_EXTRA_WRITE_CANNOT_REPLACE);
+ /*
+ reseting the extra with
+ table->file->extra(HA_EXTRA_NO_IGNORE_NO_KEY);
+ fires bug#27077
+ todo: explain or fix
+ */
+ if ((local_error= m_table->file->ha_end_bulk_insert()))
+ {
+ m_table->file->print_error(local_error, MYF(0));
+ }
+ return error? error : local_error;
+}
+
+
+int
+Write_rows_log_event_old::do_exec_row(const Relay_log_info *const rli)
+{
+ DBUG_ASSERT(m_table != NULL);
+ int error= write_row(rli, TRUE /* overwrite */);
+
+ if (error && !thd->net.last_errno)
+ thd->net.last_errno= error;
+
+ return error;
+}
+
+#endif /* !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) */
+
+
+#ifdef MYSQL_CLIENT
+void Write_rows_log_event_old::print(FILE *file,
+ PRINT_EVENT_INFO* print_event_info)
+{
+ Old_rows_log_event::print_helper(file, print_event_info, "Write_rows_old");
+}
+#endif
+
+
+/**************************************************************************
+ Delete_rows_log_event member functions
+**************************************************************************/
+
+/*
+ Constructor used to build an event for writing to the binary log.
+ */
+
+#ifndef MYSQL_CLIENT
+Delete_rows_log_event_old::Delete_rows_log_event_old(THD *thd_arg,
+ TABLE *tbl_arg,
+ ulong tid,
+ MY_BITMAP const *cols,
+ bool is_transactional)
+ : Old_rows_log_event(thd_arg, tbl_arg, tid, cols, is_transactional),
+ m_after_image(NULL), m_memory(NULL)
+{
+
+ // This constructor should not be reached.
+ assert(0);
+
+}
+#endif /* #if !defined(MYSQL_CLIENT) */
+
+
+/*
+ Constructor used by slave to read the event from the binary log.
+ */
+#ifdef HAVE_REPLICATION
+Delete_rows_log_event_old::Delete_rows_log_event_old(const char *buf,
+ uint event_len,
+ const Format_description_log_event
+ *description_event)
+ : Old_rows_log_event(buf, event_len, PRE_GA_DELETE_ROWS_EVENT,
+ description_event),
+ m_after_image(NULL), m_memory(NULL)
+{
+}
+#endif
+
+
+#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
+
+int
+Delete_rows_log_event_old::do_before_row_operations(const Slave_reporting_capability *const)
+{
+ if ((m_table->file->ha_table_flags() & HA_PRIMARY_KEY_REQUIRED_FOR_POSITION) &&
+ m_table->s->primary_key < MAX_KEY)
+ {
+ /*
+ We don't need to allocate any memory for m_key since it is not used.
+ */
+ return 0;
+ }
+
+ if (m_table->s->keys > 0)
+ {
+ // Allocate buffer for key searches
+ m_key= (uchar*)my_malloc(m_table->key_info->key_length, MYF(MY_WME));
+ if (!m_key)
+ return HA_ERR_OUT_OF_MEM;
+ }
+ return 0;
+}
+
+
+int
+Delete_rows_log_event_old::do_after_row_operations(const Slave_reporting_capability *const,
+ int error)
+{
+ /*error= ToDo:find out what this should really be, this triggers close_scan in nbd, returning error?*/
+ m_table->file->ha_index_or_rnd_end();
+ my_free(m_key, MYF(MY_ALLOW_ZERO_PTR));
+ m_key= NULL;
+
+ return error;
+}
+
+
+int Delete_rows_log_event_old::do_exec_row(const Relay_log_info *const rli)
+{
+ int error;
+ DBUG_ASSERT(m_table != NULL);
+
+ if (!(error= find_row(rli)))
+ {
+ /*
+ Delete the record found, located in record[0]
+ */
+ error= m_table->file->ha_delete_row(m_table->record[0]);
+ }
+ return error;
+}
+
+#endif /* !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) */
+
+
+#ifdef MYSQL_CLIENT
+void Delete_rows_log_event_old::print(FILE *file,
+ PRINT_EVENT_INFO* print_event_info)
+{
+ Old_rows_log_event::print_helper(file, print_event_info, "Delete_rows_old");
+}
+#endif
+
+
+/**************************************************************************
+ Update_rows_log_event member functions
+**************************************************************************/
+
+/*
+ Constructor used to build an event for writing to the binary log.
+ */
+#if !defined(MYSQL_CLIENT)
+Update_rows_log_event_old::Update_rows_log_event_old(THD *thd_arg,
+ TABLE *tbl_arg,
+ ulong tid,
+ MY_BITMAP const *cols,
+ bool is_transactional)
+ : Old_rows_log_event(thd_arg, tbl_arg, tid, cols, is_transactional),
+ m_after_image(NULL), m_memory(NULL)
+{
+
+ // This constructor should not be reached.
+ assert(0);
+}
+#endif /* !defined(MYSQL_CLIENT) */
+
+
+/*
+ Constructor used by slave to read the event from the binary log.
+ */
+#ifdef HAVE_REPLICATION
+Update_rows_log_event_old::Update_rows_log_event_old(const char *buf,
+ uint event_len,
+ const
+ Format_description_log_event
+ *description_event)
+ : Old_rows_log_event(buf, event_len, PRE_GA_UPDATE_ROWS_EVENT,
+ description_event),
+ m_after_image(NULL), m_memory(NULL)
+{
+}
+#endif
+
+
+#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
+
+int
+Update_rows_log_event_old::do_before_row_operations(const Slave_reporting_capability *const)
+{
+ if (m_table->s->keys > 0)
+ {
+ // Allocate buffer for key searches
+ m_key= (uchar*)my_malloc(m_table->key_info->key_length, MYF(MY_WME));
+ if (!m_key)
+ return HA_ERR_OUT_OF_MEM;
+ }
+
+ m_table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET;
+
+ return 0;
+}
+
+
+int
+Update_rows_log_event_old::do_after_row_operations(const Slave_reporting_capability *const,
+ int error)
+{
+ /*error= ToDo:find out what this should really be, this triggers close_scan in nbd, returning error?*/
+ m_table->file->ha_index_or_rnd_end();
+ my_free(m_key, MYF(MY_ALLOW_ZERO_PTR)); // Free for multi_malloc
+ m_key= NULL;
+
+ return error;
+}
+
+
+int
+Update_rows_log_event_old::do_exec_row(const Relay_log_info *const rli)
+{
+ DBUG_ASSERT(m_table != NULL);
+
+ int error= find_row(rli);
+ if (error)
+ {
+ /*
+ We need to read the second image in the event of error to be
+ able to skip to the next pair of updates
+ */
+ m_curr_row= m_curr_row_end;
+ unpack_current_row(rli);
+ return error;
+ }
+
+ /*
+ This is the situation after locating BI:
+
+ ===|=== before image ====|=== after image ===|===
+ ^ ^
+ m_curr_row m_curr_row_end
+
+ BI found in the table is stored in record[0]. We copy it to record[1]
+ and unpack AI to record[0].
+ */
+
+ store_record(m_table,record[1]);
+
+ m_curr_row= m_curr_row_end;
+ error= unpack_current_row(rli); // this also updates m_curr_row_end
+
+ /*
+ Now we have the right row to update. The old row (the one we're
+ looking for) is in record[1] and the new row is in record[0].
+ */
+#ifndef HAVE_purify
+ /*
+ Don't print debug messages when running valgrind since they can
+ trigger false warnings.
+ */
+ DBUG_PRINT("info",("Updating row in table"));
+ DBUG_DUMP("old record", m_table->record[1], m_table->s->reclength);
+ DBUG_DUMP("new values", m_table->record[0], m_table->s->reclength);
+#endif
+
+ error= m_table->file->ha_update_row(m_table->record[1], m_table->record[0]);
+ if (error == HA_ERR_RECORD_IS_THE_SAME)
+ error= 0;
+
+ return error;
+}
+
+#endif /* !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) */
+
+
+#ifdef MYSQL_CLIENT
+void Update_rows_log_event_old::print(FILE *file,
+ PRINT_EVENT_INFO* print_event_info)
+{
+ Old_rows_log_event::print_helper(file, print_event_info, "Update_rows_old");
+}
+#endif
diff --git a/sql/log_event_old.h b/sql/log_event_old.h
new file mode 100644
index 00000000000..719802a80fb
--- /dev/null
+++ b/sql/log_event_old.h
@@ -0,0 +1,571 @@
+/* Copyright 2007 MySQL AB. All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#ifndef LOG_EVENT_OLD_H
+#define LOG_EVENT_OLD_H
+
+/*
+ Need to include this file at the proper position of log_event.h
+ */
+
+
+/**
+ @file
+
+ @brief This file contains classes handling old formats of row-based
+ binlog events.
+*/
+/*
+ Around 2007-10-31, I made these classes completely separated from
+ the new classes (before, there was a complex class hierarchy
+ involving multiple inheritance; see BUG#31581), by simply copying
+ and pasting the entire contents of Rows_log_event into
+ Old_rows_log_event and the entire contents of
+ {Write|Update|Delete}_rows_log_event into
+ {Write|Update|Delete}_rows_log_event_old. For clarity, I will keep
+ the comments marking which code was cut-and-pasted for some time.
+ With the classes collapsed into one, there is probably some
+ redundancy (maybe some methods can be simplified and/or removed),
+ but we keep them this way for now. /Sven
+*/
+
+
+/**
+ @class Old_rows_log_event
+
+ Base class for the three types of row-based events
+ {Write|Update|Delete}_row_log_event_old, with event type codes
+ PRE_GA_{WRITE|UPDATE|DELETE}_ROWS_EVENT. These events are never
+ created any more, except when reading a relay log created by an old
+ server.
+*/
+class Old_rows_log_event : public Log_event
+{
+ /********** BEGIN CUT & PASTE FROM Rows_log_event **********/
+public:
+ /**
+ Enumeration of the errors that can be returned.
+ */
+ enum enum_error
+ {
+ ERR_OPEN_FAILURE = -1, /**< Failure to open table */
+ ERR_OK = 0, /**< No error */
+ ERR_TABLE_LIMIT_EXCEEDED = 1, /**< No more room for tables */
+ ERR_OUT_OF_MEM = 2, /**< Out of memory */
+ ERR_BAD_TABLE_DEF = 3, /**< Table definition does not match */
+ ERR_RBR_TO_SBR = 4 /**< daisy-chanining RBR to SBR not allowed */
+ };
+
+ /*
+ These definitions allow you to combine the flags into an
+ appropriate flag set using the normal bitwise operators. The
+ implicit conversion from an enum-constant to an integer is
+ accepted by the compiler, which is then used to set the real set
+ of flags.
+ */
+ enum enum_flag
+ {
+ /* Last event of a statement */
+ STMT_END_F = (1U << 0),
+
+ /* Value of the OPTION_NO_FOREIGN_KEY_CHECKS flag in thd->options */
+ NO_FOREIGN_KEY_CHECKS_F = (1U << 1),
+
+ /* Value of the OPTION_RELAXED_UNIQUE_CHECKS flag in thd->options */
+ RELAXED_UNIQUE_CHECKS_F = (1U << 2),
+
+ /**
+ Indicates that rows in this event are complete, that is contain
+ values for all columns of the table.
+ */
+ COMPLETE_ROWS_F = (1U << 3)
+ };
+
+ typedef uint16 flag_set;
+
+ /* Special constants representing sets of flags */
+ enum
+ {
+ RLE_NO_FLAGS = 0U
+ };
+
+ virtual ~Old_rows_log_event();
+
+ void set_flags(flag_set flags_arg) { m_flags |= flags_arg; }
+ 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)
+ virtual void pack_info(Protocol *protocol);
+#endif
+
+#ifdef MYSQL_CLIENT
+ /* not for direct call, each derived has its own ::print() */
+ virtual void print(FILE *file, PRINT_EVENT_INFO *print_event_info)= 0;
+#endif
+
+#ifndef MYSQL_CLIENT
+ int add_row_data(uchar *data, size_t length)
+ {
+ return do_add_row_data(data,length);
+ }
+#endif
+
+ /* Member functions to implement superclass interface */
+ virtual int get_data_size();
+
+ MY_BITMAP const *get_cols() const { return &m_cols; }
+ size_t get_width() const { return m_width; }
+ ulong get_table_id() const { return m_table_id; }
+
+#ifndef MYSQL_CLIENT
+ 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; }
+#endif
+ /*
+ Check that malloc() succeeded in allocating memory for the rows
+ buffer and the COLS vector. Checking that an Update_rows_log_event_old
+ is valid is done in the Update_rows_log_event_old::is_valid()
+ function.
+ */
+ virtual bool is_valid() const
+ {
+ return m_rows_buf && m_cols.bitmap;
+ }
+
+ uint m_row_count; /* The number of rows added to the event */
+
+protected:
+ /*
+ The constructors are protected since you're supposed to inherit
+ this class, not create instances of this class.
+ */
+#ifndef MYSQL_CLIENT
+ Old_rows_log_event(THD*, TABLE*, ulong table_id,
+ MY_BITMAP const *cols, bool is_transactional);
+#endif
+ Old_rows_log_event(const char *row_data, uint event_len,
+ Log_event_type event_type,
+ const Format_description_log_event *description_event);
+
+#ifdef MYSQL_CLIENT
+ void print_helper(FILE *, PRINT_EVENT_INFO *, char const *const name);
+#endif
+
+#ifndef MYSQL_CLIENT
+ virtual int do_add_row_data(uchar *data, size_t length);
+#endif
+
+#ifndef MYSQL_CLIENT
+ TABLE *m_table; /* The table the rows belong to */
+#endif
+ ulong m_table_id; /* Table ID */
+ MY_BITMAP m_cols; /* Bitmap denoting columns available */
+ ulong m_width; /* The width of the columns bitmap */
+
+ ulong m_master_reclength; /* Length of record on master side */
+
+ /* Bit buffers in the same memory as the class */
+ uint32 m_bitbuf[128/(sizeof(uint32)*8)];
+ uint32 m_bitbuf_ai[128/(sizeof(uint32)*8)];
+
+ uchar *m_rows_buf; /* The rows in packed format */
+ uchar *m_rows_cur; /* One-after the end of the data */
+ uchar *m_rows_end; /* One-after the end of the allocated space */
+
+ flag_set m_flags; /* Flags for row-level events */
+
+ /* helper functions */
+
+#if !defined(MYSQL_CLIENT) && 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 */
+
+ int find_row(const Relay_log_info *const);
+ int write_row(const Relay_log_info *const, const bool);
+
+ // Unpack the current row into m_table->record[0]
+ int unpack_current_row(const Relay_log_info *const rli)
+ {
+ 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_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;
+ }
+#endif
+
+private:
+
+#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
+ virtual int do_apply_event(Relay_log_info const *rli);
+ virtual int do_update_pos(Relay_log_info *rli);
+ virtual enum_skip_reason do_shall_skip(Relay_log_info *rli);
+
+ /*
+ Primitive to prepare for a sequence of row executions.
+
+ DESCRIPTION
+
+ Before doing a sequence of do_prepare_row() and do_exec_row()
+ calls, this member function should be called to prepare for the
+ entire sequence. Typically, this member function will allocate
+ space for any buffers that are needed for the two member
+ functions mentioned above.
+
+ RETURN VALUE
+
+ The member function will return 0 if all went OK, or a non-zero
+ error code otherwise.
+ */
+ virtual
+ int do_before_row_operations(const Slave_reporting_capability *const log) = 0;
+
+ /*
+ Primitive to clean up after a sequence of row executions.
+
+ DESCRIPTION
+
+ After doing a sequence of do_prepare_row() and do_exec_row(),
+ this member function should be called to clean up and release
+ any allocated buffers.
+
+ The error argument, if non-zero, indicates an error which happened during
+ row processing before this function was called. In this case, even if
+ function is successful, it should return the error code given in the argument.
+ */
+ virtual
+ int do_after_row_operations(const Slave_reporting_capability *const log,
+ int error) = 0;
+
+ /*
+ Primitive to do the actual execution necessary for a row.
+
+ DESCRIPTION
+ The member function will do the actual execution needed to handle a row.
+ The row is located at m_curr_row. When the function returns,
+ m_curr_row_end should point at the next row (one byte after the end
+ of the current row).
+
+ RETURN VALUE
+ 0 if execution succeeded, 1 if execution failed.
+
+ */
+ virtual int do_exec_row(const Relay_log_info *const rli) = 0;
+#endif /* !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) */
+
+ /********** END OF CUT & PASTE FROM Rows_log_event **********/
+ protected:
+
+#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
+
+ int do_apply_event(Old_rows_log_event*,const Relay_log_info*);
+
+ /*
+ Primitive to prepare for a sequence of row executions.
+
+ DESCRIPTION
+
+ Before doing a sequence of do_prepare_row() and do_exec_row()
+ calls, this member function should be called to prepare for the
+ entire sequence. Typically, this member function will allocate
+ space for any buffers that are needed for the two member
+ functions mentioned above.
+
+ RETURN VALUE
+
+ The member function will return 0 if all went OK, or a non-zero
+ error code otherwise.
+ */
+ virtual int do_before_row_operations(TABLE *table) = 0;
+
+ /*
+ Primitive to clean up after a sequence of row executions.
+
+ DESCRIPTION
+
+ After doing a sequence of do_prepare_row() and do_exec_row(),
+ this member function should be called to clean up and release
+ any allocated buffers.
+ */
+ virtual int do_after_row_operations(TABLE *table, int error) = 0;
+
+ /*
+ Primitive to prepare for handling one row in a row-level event.
+
+ DESCRIPTION
+
+ The member function prepares for execution of operations needed for one
+ row in a row-level event by reading up data from the buffer containing
+ the row. No specific interpretation of the data is normally done here,
+ since SQL thread specific data is not available: that data is made
+ available for the do_exec function.
+
+ A pointer to the start of the next row, or NULL if the preparation
+ failed. Currently, preparation cannot fail, but don't rely on this
+ behavior.
+
+ RETURN VALUE
+ Error code, if something went wrong, 0 otherwise.
+ */
+ virtual int do_prepare_row(THD*, Relay_log_info const*, TABLE*,
+ uchar const *row_start,
+ uchar const **row_end) = 0;
+
+ /*
+ Primitive to do the actual execution necessary for a row.
+
+ DESCRIPTION
+ The member function will do the actual execution needed to handle a row.
+
+ RETURN VALUE
+ 0 if execution succeeded, 1 if execution failed.
+
+ */
+ virtual int do_exec_row(TABLE *table) = 0;
+
+#endif /* !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) */
+};
+
+
+/**
+ @class Write_rows_log_event_old
+
+ Old class for binlog events that write new rows to a table (event
+ type code PRE_GA_WRITE_ROWS_EVENT). Such events are never produced
+ by this version of the server, but they may be read from a relay log
+ created by an old server. New servers create events of class
+ Write_rows_log_event (event type code WRITE_ROWS_EVENT) instead.
+*/
+class Write_rows_log_event_old : public Old_rows_log_event
+{
+ /********** BEGIN CUT & PASTE FROM Write_rows_log_event **********/
+public:
+#if !defined(MYSQL_CLIENT)
+ Write_rows_log_event_old(THD*, TABLE*, ulong table_id,
+ MY_BITMAP const *cols, bool is_transactional);
+#endif
+#ifdef HAVE_REPLICATION
+ Write_rows_log_event_old(const char *buf, uint event_len,
+ const Format_description_log_event *description_event);
+#endif
+#if !defined(MYSQL_CLIENT)
+ static bool binlog_row_logging_function(THD *thd, TABLE *table,
+ bool is_transactional,
+ MY_BITMAP *cols,
+ uint fields,
+ const uchar *before_record
+ __attribute__((unused)),
+ const uchar *after_record)
+ {
+ return thd->binlog_write_row(table, is_transactional,
+ cols, fields, after_record);
+ }
+#endif
+
+private:
+#ifdef MYSQL_CLIENT
+ void print(FILE *file, PRINT_EVENT_INFO *print_event_info);
+#endif
+
+#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
+ virtual int do_before_row_operations(const Slave_reporting_capability *const);
+ virtual int do_after_row_operations(const Slave_reporting_capability *const,int);
+ virtual int do_exec_row(const Relay_log_info *const);
+#endif
+ /********** END OF CUT & PASTE FROM Write_rows_log_event **********/
+
+public:
+ enum
+ {
+ /* Support interface to THD::binlog_prepare_pending_rows_event */
+ TYPE_CODE = PRE_GA_WRITE_ROWS_EVENT
+ };
+
+private:
+ virtual Log_event_type get_type_code() { return (Log_event_type)TYPE_CODE; }
+
+#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
+ // use old definition of do_apply_event()
+ virtual int do_apply_event(const Relay_log_info *rli)
+ { return Old_rows_log_event::do_apply_event(this,rli); }
+
+ // primitives for old version of do_apply_event()
+ virtual int do_before_row_operations(TABLE *table);
+ virtual int do_after_row_operations(TABLE *table, int error);
+ virtual int do_prepare_row(THD*, Relay_log_info const*, TABLE*,
+ uchar const *row_start, uchar const **row_end);
+ virtual int do_exec_row(TABLE *table);
+
+#endif
+};
+
+
+/**
+ @class Update_rows_log_event_old
+
+ Old class for binlog events that modify existing rows to a table
+ (event type code PRE_GA_UPDATE_ROWS_EVENT). Such events are never
+ produced by this version of the server, but they may be read from a
+ relay log created by an old server. New servers create events of
+ class Update_rows_log_event (event type code UPDATE_ROWS_EVENT)
+ instead.
+*/
+class Update_rows_log_event_old : public Old_rows_log_event
+{
+ /********** BEGIN CUT & PASTE FROM Update_rows_log_event **********/
+public:
+#ifndef MYSQL_CLIENT
+ Update_rows_log_event_old(THD*, TABLE*, ulong table_id,
+ MY_BITMAP const *cols,
+ bool is_transactional);
+#endif
+
+#ifdef HAVE_REPLICATION
+ Update_rows_log_event_old(const char *buf, uint event_len,
+ const Format_description_log_event *description_event);
+#endif
+
+#if !defined(MYSQL_CLIENT)
+ static bool binlog_row_logging_function(THD *thd, TABLE *table,
+ bool is_transactional,
+ MY_BITMAP *cols,
+ uint fields,
+ const uchar *before_record,
+ const uchar *after_record)
+ {
+ return thd->binlog_update_row(table, is_transactional,
+ cols, fields, before_record, after_record);
+ }
+#endif
+
+protected:
+#ifdef MYSQL_CLIENT
+ void print(FILE *file, PRINT_EVENT_INFO *print_event_info);
+#endif
+
+#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
+ virtual int do_before_row_operations(const Slave_reporting_capability *const);
+ virtual int do_after_row_operations(const Slave_reporting_capability *const,int);
+ virtual int do_exec_row(const Relay_log_info *const);
+#endif /* !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) */
+ /********** END OF CUT & PASTE FROM Update_rows_log_event **********/
+
+ uchar *m_after_image, *m_memory;
+
+public:
+ enum
+ {
+ /* Support interface to THD::binlog_prepare_pending_rows_event */
+ TYPE_CODE = PRE_GA_UPDATE_ROWS_EVENT
+ };
+
+private:
+ virtual Log_event_type get_type_code() { return (Log_event_type)TYPE_CODE; }
+
+#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
+ // use old definition of do_apply_event()
+ virtual int do_apply_event(const Relay_log_info *rli)
+ { return Old_rows_log_event::do_apply_event(this,rli); }
+
+ // primitives for old version of do_apply_event()
+ virtual int do_before_row_operations(TABLE *table);
+ virtual int do_after_row_operations(TABLE *table, int error);
+ virtual int do_prepare_row(THD*, Relay_log_info const*, TABLE*,
+ uchar const *row_start, uchar const **row_end);
+ virtual int do_exec_row(TABLE *table);
+#endif /* !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) */
+};
+
+
+/**
+ @class Delete_rows_log_event_old
+
+ Old class for binlog events that delete existing rows from a table
+ (event type code PRE_GA_DELETE_ROWS_EVENT). Such events are never
+ produced by this version of the server, but they may be read from a
+ relay log created by an old server. New servers create events of
+ class Delete_rows_log_event (event type code DELETE_ROWS_EVENT)
+ instead.
+*/
+class Delete_rows_log_event_old : public Old_rows_log_event
+{
+ /********** BEGIN CUT & PASTE FROM Update_rows_log_event **********/
+public:
+#ifndef MYSQL_CLIENT
+ Delete_rows_log_event_old(THD*, TABLE*, ulong,
+ MY_BITMAP const *cols, bool is_transactional);
+#endif
+#ifdef HAVE_REPLICATION
+ Delete_rows_log_event_old(const char *buf, uint event_len,
+ const Format_description_log_event *description_event);
+#endif
+#if !defined(MYSQL_CLIENT)
+ static bool binlog_row_logging_function(THD *thd, TABLE *table,
+ bool is_transactional,
+ MY_BITMAP *cols,
+ uint fields,
+ const uchar *before_record,
+ const uchar *after_record
+ __attribute__((unused)))
+ {
+ return thd->binlog_delete_row(table, is_transactional,
+ cols, fields, before_record);
+ }
+#endif
+
+protected:
+#ifdef MYSQL_CLIENT
+ void print(FILE *file, PRINT_EVENT_INFO *print_event_info);
+#endif
+
+#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
+ virtual int do_before_row_operations(const Slave_reporting_capability *const);
+ virtual int do_after_row_operations(const Slave_reporting_capability *const,int);
+ virtual int do_exec_row(const Relay_log_info *const);
+#endif
+ /********** END CUT & PASTE FROM Delete_rows_log_event **********/
+
+ uchar *m_after_image, *m_memory;
+
+public:
+ enum
+ {
+ /* Support interface to THD::binlog_prepare_pending_rows_event */
+ TYPE_CODE = PRE_GA_DELETE_ROWS_EVENT
+ };
+
+private:
+ virtual Log_event_type get_type_code() { return (Log_event_type)TYPE_CODE; }
+
+#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
+ // use old definition of do_apply_event()
+ virtual int do_apply_event(const Relay_log_info *rli)
+ { return Old_rows_log_event::do_apply_event(this,rli); }
+
+ // primitives for old version of do_apply_event()
+ virtual int do_before_row_operations(TABLE *table);
+ virtual int do_after_row_operations(TABLE *table, int error);
+ virtual int do_prepare_row(THD*, Relay_log_info const*, TABLE*,
+ uchar const *row_start, uchar const **row_end);
+ virtual int do_exec_row(TABLE *table);
+#endif
+};
+
+
+#endif
diff --git a/sql/mf_iocache.cc b/sql/mf_iocache.cc
index f237f15dbc9..8c2d16c40b0 100644
--- a/sql/mf_iocache.cc
+++ b/sql/mf_iocache.cc
@@ -13,8 +13,11 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
-/*
- Cashing of files with only does (sequential) read or writes of fixed-
+/**
+ @file
+
+ @details
+ Caching of files with only does (sequential) read or writes of fixed-
length records. A read isn't allowed to go over file-length. A read is ok
if it ends at file-length and next read can try to read after file-length
(and get a EOF-error).
@@ -34,14 +37,18 @@
extern "C" {
- /*
- ** Read buffered from the net.
- ** Returns 1 if can't read requested characters
- ** Returns 0 if record read
- */
+/**
+ Read buffered from the net.
+
+ @retval
+ 1 if can't read requested characters
+ @retval
+ 0 if record read
+*/
+
-int _my_b_net_read(register IO_CACHE *info, byte *Buffer,
- uint Count __attribute__((unused)))
+int _my_b_net_read(register IO_CACHE *info, uchar *Buffer,
+ size_t Count __attribute__((unused)))
{
ulong read_length;
NET *net= &(current_thd)->net;
@@ -61,7 +68,7 @@ int _my_b_net_read(register IO_CACHE *info, byte *Buffer,
DBUG_RETURN(1);
}
/* to set up stuff for my_b_get (no _) */
- info->read_end = (info->read_pos = (byte*) net->read_pos) + read_length;
+ info->read_end = (info->read_pos = (uchar*) net->read_pos) + read_length;
Buffer[0] = info->read_pos[0]; /* length is always 1 */
/*
diff --git a/sql/my_decimal.cc b/sql/my_decimal.cc
index a235edbd73c..208ddefb890 100644
--- a/sql/my_decimal.cc
+++ b/sql/my_decimal.cc
@@ -18,17 +18,15 @@
#ifndef MYSQL_CLIENT
-/*
- report result of decimal operation
+/**
+ report result of decimal operation.
- SYNOPSIS
- decimal_operation_results()
- result decimal library return code (E_DEC_* see include/decimal.h)
+ @param result decimal library return code (E_DEC_* see include/decimal.h)
- TODO
+ @todo
Fix error messages
- RETURN
+ @return
result
*/
@@ -137,7 +135,7 @@ int my_decimal2string(uint mask, const my_decimal *d,
E_DEC_OVERFLOW
*/
-int my_decimal2binary(uint mask, const my_decimal *d, char *bin, int prec,
+int my_decimal2binary(uint mask, const my_decimal *d, uchar *bin, int prec,
int scale)
{
int err1= E_DEC_OK, err2;
@@ -227,25 +225,43 @@ my_decimal *date2my_decimal(MYSQL_TIME *ltime, my_decimal *dec)
}
+void my_decimal_trim(ulong *precision, uint *scale)
+{
+ if (!(*precision) && !(*scale))
+ {
+ *precision= 10;
+ *scale= 0;
+ return;
+ }
+}
+
+
#ifndef DBUG_OFF
/* routines for debugging print */
+#define DIG_PER_DEC1 9
+#define ROUND_UP(X) (((X)+DIG_PER_DEC1-1)/DIG_PER_DEC1)
+
/* print decimal */
void
print_decimal(const my_decimal *dec)
{
- fprintf(DBUG_FILE,
- "\nDecimal: sign: %d intg: %d frac: %d \n\
-%09d,%09d,%09d,%09d,%09d,%09d,%09d,%09d\n",
- dec->sign(), dec->intg, dec->frac,
- dec->buf[0], dec->buf[1], dec->buf[2], dec->buf[3],
- dec->buf[4], dec->buf[5], dec->buf[6], dec->buf[7]);
+ 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));
+ 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]));
+ fputs(buff, DBUG_FILE);
}
/* print decimal with its binary representation */
void
-print_decimal_buff(const my_decimal *dec, const byte* ptr, int length)
+print_decimal_buff(const my_decimal *dec, const uchar* ptr, int length)
{
print_decimal(dec);
fprintf(DBUG_FILE, "Record: ");
diff --git a/sql/my_decimal.h b/sql/my_decimal.h
index 6a0d05921ec..0e79f70ab4e 100644
--- a/sql/my_decimal.h
+++ b/sql/my_decimal.h
@@ -13,7 +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 */
-/*
+/**
+ @file
+
It is interface module to fixed precision decimals library.
Most functions use 'uint mask' as parameter, if during operation error
@@ -34,13 +36,14 @@ C_MODE_END
#define DECIMAL_LONG_DIGITS 10
#define DECIMAL_LONG3_DIGITS 8
-/* maximum length of buffer in our big digits (uint32) */
+/** maximum length of buffer in our big digits (uint32). */
#define DECIMAL_BUFF_LENGTH 9
/* the number of digits that my_decimal can possibly contain */
#define DECIMAL_MAX_POSSIBLE_PRECISION (DECIMAL_BUFF_LENGTH * 9)
-/*
+
+/**
maximum guaranteed precision of number in decimal digits (number of our
digits * number of decimal digits in one our big digit - number of decimal
digits in one our big digit decreased by 1 (because we always put decimal
@@ -50,13 +53,14 @@ C_MODE_END
#define DECIMAL_MAX_SCALE 30
#define DECIMAL_NOT_SPECIFIED 31
-/*
+/**
maximum length of string representation (number of maximum decimal
digits + 1 position for sign + 1 position for decimal point)
*/
#define DECIMAL_MAX_STR_LENGTH (DECIMAL_MAX_POSSIBLE_PRECISION + 2)
-/*
- maximum size of packet length
+
+/**
+ maximum size of packet length.
*/
#define DECIMAL_MAX_FIELD_SIZE DECIMAL_MAX_PRECISION
@@ -77,11 +81,12 @@ inline int my_decimal_int_part(uint precision, uint decimals)
}
-/*
- my_decimal class limits 'decimal_t' type to what we need in MySQL
+/**
+ my_decimal class limits 'decimal_t' type to what we need in MySQL.
+
It contains internally all necessary space needed by the instance so
no extra memory is needed. One should call fix_buffer_pointer() function
- when he moves my_decimal objects in memory
+ when he moves my_decimal objects in memory.
*/
class my_decimal :public decimal_t
@@ -109,12 +114,20 @@ public:
bool sign() const { return decimal_t::sign; }
void sign(bool s) { decimal_t::sign= s; }
uint precision() const { return intg + frac; }
+
+ /** Swap two my_decimal values */
+ void swap(my_decimal &rhs)
+ {
+ swap_variables(my_decimal, *this, rhs);
+ /* Swap the buffer pointers back */
+ swap_variables(decimal_digit_t *, buf, rhs.buf);
+ }
};
#ifndef DBUG_OFF
void print_decimal(const my_decimal *dec);
-void print_decimal_buff(const my_decimal *dec, const byte* ptr, int length);
+void print_decimal_buff(const my_decimal *dec, const uchar* ptr, int length);
const char *dbug_decimal_as_string(char *buff, const my_decimal *val);
#else
#define dbug_decimal_as_string(A) NULL
@@ -213,16 +226,15 @@ void my_decimal2decimal(const my_decimal *from, my_decimal *to)
}
-int my_decimal2binary(uint mask, const my_decimal *d, char *bin, int prec,
+int my_decimal2binary(uint mask, const my_decimal *d, uchar *bin, int prec,
int scale);
inline
-int binary2my_decimal(uint mask, const char *bin, my_decimal *d, int prec,
+int binary2my_decimal(uint mask, const uchar *bin, my_decimal *d, int prec,
int scale)
{
- return check_result(mask, bin2decimal((char *)bin, (decimal_t*) d, prec,
- scale));
+ return check_result(mask, bin2decimal(bin, (decimal_t*) d, prec, scale));
}
@@ -393,7 +405,10 @@ int my_decimal_mod(uint mask, my_decimal *res, const my_decimal *a,
}
-/* Returns -1 if a<b, 1 if a>b and 0 if a==b */
+/**
+ @return
+ -1 if a<b, 1 if a>b and 0 if a==b
+*/
inline
int my_decimal_cmp(const my_decimal *a, const my_decimal *b)
{
@@ -404,20 +419,11 @@ int my_decimal_cmp(const my_decimal *a, const my_decimal *b)
inline
int my_decimal_intg(const my_decimal *a)
{
- return decimal_intg((decimal_t*) a);
+ return decimal_intg((decimal_t*) a);
}
-inline
-void my_decimal_trim(ulong *precision, uint *scale)
-{
- if (!(*precision) && !(*scale))
- {
- *precision= 10;
- *scale= 0;
- return;
- }
-}
+void my_decimal_trim(ulong *precision, uint *scale);
#endif /*my_decimal_h*/
diff --git a/sql/my_lock.c b/sql/my_lock.c
index cbd00521a9b..f66d7282f72 100644
--- a/sql/my_lock.c
+++ b/sql/my_lock.c
@@ -13,7 +13,7 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
-#if defined(__EMX__) || defined(__NETWARE__)
+#if defined(__NETWARE__)
#include "../mysys/my_lock.c"
#else
diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h
index b855af9a8d3..015128cda1f 100644
--- a/sql/mysql_priv.h
+++ b/sql/mysql_priv.h
@@ -1,4 +1,4 @@
-/* Copyright (C) 2000-2003 MySQL AB
+/* Copyright 2000-2008 MySQL AB, 2008 Sun Microsystems, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -13,15 +13,18 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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,
+ 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_INCLUDED
-#define MYSQL_PRIV_H_INCLUDED
+#ifndef MYSQL_PRIV_H
+#define MYSQL_PRIV_H
#ifndef MYSQL_CLIENT
@@ -35,12 +38,26 @@
#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"
-#ifdef __EMX__
-#undef write /* remove pthread.h macro definition for EMX */
-#endif
+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).
+*/
+enum enum_query_type
+{
+ QT_ORDINARY,
+ QT_IS
+};
/* TODO convert all these three maps to Bitmap classes */
typedef ulonglong table_map; /* Used for table bits in join */
@@ -49,7 +66,6 @@ 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 key_part_map; /* Used for finding key parts */
typedef ulong nesting_map; /* Used for flags of nesting constructs */
/*
Used to identify NESTED_JOIN structures within a join (applicable only to
@@ -73,37 +89,37 @@ extern const char *primary_key_name;
#include "mysql_com.h"
#include <violite.h>
#include "unireg.h"
-#define IS_NUM(t) ((t) <= FIELD_TYPE_INT24 || (t) == FIELD_TYPE_YEAR || (t) == FIELD_TYPE_NEWDECIMAL)
void init_sql_alloc(MEM_ROOT *root, uint block_size, uint pre_alloc_size);
-gptr sql_alloc(unsigned size);
-gptr sql_calloc(unsigned size);
+void *sql_alloc(size_t);
+void *sql_calloc(size_t);
char *sql_strdup(const char *str);
-char *sql_strmake(const char *str,uint len);
-gptr sql_memdup(const void * ptr,unsigned size);
+char *sql_strmake(const char *str, size_t len);
+void *sql_memdup(const void * ptr, size_t size);
void sql_element_free(void *ptr);
-char *sql_strmake_with_convert(const char *str, uint32 arg_length,
+char *sql_strmake_with_convert(const char *str, size_t arg_length,
CHARSET_INFO *from_cs,
- uint32 max_res_length,
- CHARSET_INFO *to_cs, uint32 *result_length);
-void kill_one_thread(THD *thd, ulong id, bool only_kill_query);
+ size_t max_res_length,
+ CHARSET_INFO *to_cs, size_t *result_length);
+uint kill_one_thread(THD *thd, ulong id, bool only_kill_query);
+void sql_kill(THD *thd, ulong id, bool only_kill_query);
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((gptr) (A),MYF(MY_WME | MY_FAE | MY_ALLOW_ZERO_PTR)); }
-#define safeFree(x) { if(x) { my_free((gptr) x,MYF(0)); x = NULL; } }
+#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))
#define WARN_DEPRECATED(Thd,Ver,Old,New) \
do { \
DBUG_ASSERT(strncmp(Ver, MYSQL_SERVER_VERSION, sizeof(Ver)-1) > 0); \
- if (((gptr)Thd) != NULL) \
+ if (((uchar*)Thd) != NULL) \
push_warning_printf(((THD *)Thd), MYSQL_ERROR::WARN_LEVEL_WARN, \
- ER_WARN_DEPRECATED_SYNTAX, ER(ER_WARN_DEPRECATED_SYNTAX), \
+ ER_WARN_DEPRECATED_SYNTAX, ER(ER_WARN_DEPRECATED_SYNTAX_WITH_VER), \
(Old), (Ver), (New)); \
else \
- sql_print_warning("The syntax %s is deprecated and will be removed " \
+ sql_print_warning("The syntax '%s' is deprecated and will be removed " \
"in MySQL %s. Please use %s instead.", (Old), (Ver), (New)); \
} while(0)
@@ -157,6 +173,87 @@ 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
****************************************************************************/
@@ -169,6 +266,21 @@ MY_LOCALE *my_locale_by_number(uint number);
#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
/*
Value of 9236 discovered through binary search 2006-09-26 on Ubuntu Dapper
@@ -179,12 +291,12 @@ MY_LOCALE *my_locale_by_number(uint number);
Feel free to raise this by the smallest amount you can to get the
"execution_constants" test to pass.
*/
-#define STACK_MIN_SIZE 12000 // Abort if less stack during eval.
+#define STACK_MIN_SIZE 12000 ///< Abort if less stack during eval.
#define STACK_MIN_SIZE_FOR_OPEN 1024*80
-#define STACK_BUFF_ALLOC 352 // For stack overrun checks
+#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.
+#define MYSQLD_NET_RETRY_COUNT 10 ///< Abort read after this many int.
#endif
#define TEMP_POOL_SIZE 128
@@ -199,6 +311,8 @@ MY_LOCALE *my_locale_by_number(uint number);
#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
@@ -208,14 +322,14 @@ MY_LOCALE *my_locale_by_number(uint number);
#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.
*/
@@ -235,18 +349,20 @@ MY_LOCALE *my_locale_by_number(uint number);
#define DISK_SEEK_PROP_COST ((double)0.5/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
-/* Don't pack string keys shorter than this (if PACK_KEYS=1 isn't used) */
+/** 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' */
+/** 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
@@ -259,26 +375,21 @@ MY_LOCALE *my_locale_by_number(uint number);
/* The following can also be changed from the command line */
#define DEFAULT_CONCURRENCY 10
-#define DELAYED_LIMIT 100 /* pause after xxx inserts */
+#define DELAYED_LIMIT 100 /**< pause after xxx inserts */
#define DELAYED_QUEUE_SIZE 1000
-#define DELAYED_WAIT_TIMEOUT 5*60 /* Wait for delayed insert */
-#define FLUSH_TIME 0 /* Don't flush tables */
-#define MAX_CONNECT_ERRORS 10 // errors before disabling host
+#define DELAYED_WAIT_TIMEOUT 5*60 /**< Wait for delayed insert */
+#define FLUSH_TIME 0 /**< Don't flush tables */
+#define MAX_CONNECT_ERRORS 10 ///< errors before disabling host
-#ifdef HAVE_INNOBASE_DB
-#define IF_INNOBASE_DB(A, B) (A)
-#else
-#define IF_INNOBASE_DB(A, B) (B)
-#endif
#ifdef __NETWARE__
-#define IF_NETWARE(A,B) (A)
+#define IF_NETWARE(A,B) A
#else
-#define IF_NETWARE(A,B) (B)
+#define IF_NETWARE(A,B) B
#endif
-#if defined(__WIN__) || defined(OS2)
+#if defined(__WIN__)
#undef FLUSH_TIME
-#define FLUSH_TIME 1800 /* Flush every half hour */
+#define FLUSH_TIME 1800 /**< Flush every half hour */
#define INTERRUPT_PRIOR -2
#define CONNECT_PRIOR -1
@@ -297,13 +408,12 @@ MY_LOCALE *my_locale_by_number(uint number);
#define TEST_MIT_THREAD 4
#define TEST_BLOCKING 8
#define TEST_KEEP_TMP_TABLES 16
-#define TEST_NO_THREADS 32 /* For debugging under Linux */
-#define TEST_READCHECK 64 /* Force use of readcheck */
+#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_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
+#define TEST_SIGINT 1024 /**< Allow sigint on threads */
+#define TEST_SYNCHRONIZATION 2048 /**< get server to do sleep in
some places */
#endif
@@ -336,7 +446,7 @@ MY_LOCALE *my_locale_by_number(uint number);
#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_UPDATE_LOG (ULL(1) << 11) // THD, user, unused
+#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
@@ -348,36 +458,37 @@ MY_LOCALE *my_locale_by_number(uint number);
#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_QUOTE_SHOW_CREATE (ULL(1) << 23) // THD, user
+#define OPTION_KEEP_LOG (ULL(1) << 23) // THD, user
-/* Thr following is used to detect a conflict with DISTINCT
- in the user query has requested */
+/* 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'
+/** 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
+/** 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 */
+/** 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. */
+/** 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 72
+#define MAX_TIME_ZONE_NAME_LENGTH (NAME_LEN + 1)
/* The rest of the file is included in the server only */
#ifndef MYSQL_CLIENT
@@ -397,9 +508,9 @@ MY_LOCALE *my_locale_by_number(uint number);
#define MODE_DB2 2048
#define MODE_MAXDB 4096
#define MODE_NO_KEY_OPTIONS 8192
-#define MODE_NO_TABLE_OPTIONS 16384
+#define MODE_NO_TABLE_OPTIONS 16384
#define MODE_NO_FIELD_OPTIONS 32768
-#define MODE_MYSQL323 65536
+#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)
@@ -414,6 +525,8 @@ MY_LOCALE *my_locale_by_number(uint number);
#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)
+
/*
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
@@ -434,9 +547,9 @@ MY_LOCALE *my_locale_by_number(uint number);
#define UNCACHEABLE_DEPENDENT 1
#define UNCACHEABLE_RAND 2
#define UNCACHEABLE_SIDEEFFECT 4
-// forcing to save JOIN for explain
+/// forcing to save JOIN for explain
#define UNCACHEABLE_EXPLAIN 8
-/* Don't evaluate subqueries in prepare even if they're not correlated */
+/** Don't evaluate subqueries in prepare even if they're not correlated */
#define UNCACHEABLE_PREPARE 16
/* For uncorrelated SELECT in an UNION with some correlated SELECTs */
#define UNCACHEABLE_UNITED 32
@@ -445,7 +558,7 @@ MY_LOCALE *my_locale_by_number(uint number);
/* Used to check GROUP BY list in the MODE_ONLY_FULL_GROUP_BY mode */
#define UNDEF_POS (-1)
#ifdef EXTRA_DEBUG
-/*
+/**
Sync points allow us to force the server to reach a certain line of code
and block there until the client tells the server it is ok to go on.
The client tells the server to block with SELECT GET_LOCK()
@@ -481,7 +594,7 @@ void view_store_options(THD *thd, TABLE_LIST *table, String *buff);
#define portable_sizeof_char_ptr 8
-#define tmp_file_prefix "#sql" /* Prefix for tmp tables */
+#define tmp_file_prefix "#sql" /**< Prefix for tmp tables */
#define tmp_file_prefix_length 4
/* Flags for calc_week() function. */
@@ -491,6 +604,13 @@ void view_store_options(THD *thd, TABLE_LIST *table, String *buff);
#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,
@@ -501,14 +621,23 @@ enum enum_parsing_place
};
struct st_table;
+
+#define thd_proc_info(thd, msg) set_thd_proc_info(thd, msg, __func__, __FILE__, __LINE__)
class THD;
-/* Struct to handle simple linked lists */
+enum enum_check_fields
+{
+ CHECK_FIELD_IGNORE,
+ CHECK_FIELD_WARN,
+ CHECK_FIELD_ERROR_FOR_NULL
+};
+
+/** Struct to handle simple linked lists. */
typedef struct st_sql_list {
uint elements;
- byte *first;
- byte **next;
+ uchar *first;
+ uchar **next;
st_sql_list() {} /* Remove gcc warning */
inline void empty()
@@ -517,7 +646,7 @@ typedef struct st_sql_list {
first=0;
next= &first;
}
- inline void link_in_list(byte *element,byte **next_ptr)
+ inline void link_in_list(uchar *element,uchar **next_ptr)
{
elements++;
(*next)=element;
@@ -554,6 +683,41 @@ inline THD *_current_thd(void)
}
#define current_thd _current_thd()
+/**
+ The meat of thd_proc_info(THD*, char*), a macro that packs the last
+ three calling-info parameters.
+*/
+extern "C"
+const char *set_thd_proc_info(THD *thd, const char *info,
+ const char *calling_func,
+ const char *calling_file,
+ const unsigned int calling_line);
+
+/**
+ 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
*/
@@ -574,6 +738,9 @@ typedef my_bool (*qc_engine_callback)(THD *thd, char *table_key,
#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
@@ -581,8 +748,10 @@ 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;
@@ -590,17 +759,34 @@ extern my_decimal decimal_zero;
void free_items(Item *item);
void cleanup_items(Item *item);
class THD;
-void close_thread_tables(THD *thd, bool locked=0, bool skip_derived=0);
-bool check_one_table_access(THD *thd, ulong privilege,
- TABLE_LIST *tables);
+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);
+ 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_merge_table_access(THD *thd, char *db,
- TABLE_LIST *table_list);
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; }
+#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);
@@ -618,31 +804,55 @@ 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_length(LEX_STRING *str,
- const char *err_msg, uint max_length);
+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 */
-void sql_perror(const char *message);
-
-void vprint_msg_to_log(enum loglevel level, const char *format, va_list args);
+int vprint_msg_to_log(enum loglevel level, const char *format, va_list args);
void sql_print_error(const char *format, ...) ATTRIBUTE_FORMAT(printf, 1, 2);
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(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"
#ifdef HAVE_QUERY_CACHE
@@ -650,6 +860,7 @@ 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;
@@ -680,6 +891,11 @@ struct Query_cache_query_flags
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)
@@ -696,36 +912,153 @@ struct Query_cache_query_flags
#define query_cache_abort(A)
#define query_cache_end_of_result(A)
#define query_cache_invalidate_by_MyISAM_filename_ref NULL
+#define query_cache_maybe_disabled(T) 1
+#define query_cache_is_cacheable_query(L) 0
#endif /*HAVE_QUERY_CACHE*/
-uint build_table_path(char *buff, size_t bufflen, const char *db,
- const char *table, const char *ext);
+/*
+ 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_strict_keyword_ (dbug_str))
+ {
+ 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, (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, (abort(), 0))
+
+#endif
+
+void write_bin_log(THD *thd, bool clear_error,
+ char const *query, ulong query_length);
+
+/* sql_connect.cc */
+int check_user(THD *thd, enum enum_server_command command,
+ const char *passwd, uint passwd_len, const char *db,
+ bool check_count);
+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);
+void decrease_user_connections(USER_CONN *uc);
+void thd_init_client_charset(THD *thd, uint cs_number);
+bool setup_connection_thread_globals(THD *thd);
+
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);
-int mysql_rm_table_part2_with_lock(THD *thd, TABLE_LIST *tables,
- bool if_exists, bool drop_temporary,
- bool log_query);
-int quick_rm_table(enum db_type base,const char *db,
- const char *table_name);
+ 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 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, const char *inBuf, 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);
@@ -736,29 +1069,69 @@ bool multi_delete_set_locks_and_link_aux_tables(LEX *lex);
void init_max_user_conn(void);
void init_update_queries(void);
void free_max_user_conn(void);
-pthread_handler_t handle_one_connection(void *arg);
pthread_handler_t handle_bootstrap(void *arg);
-void end_thread(THD *thd,bool put_in_cache);
-void flush_thread_cache();
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 compare_record(TABLE *table, query_id_t query_id);
-
+bool compare_record(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);
-uint cached_tables(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,
bool *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,
- bool no_errors);
+ 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);
@@ -778,11 +1151,12 @@ bool mysql_assign_to_keycache(THD* thd, TABLE_LIST* table_list,
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);
+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);
@@ -821,15 +1195,20 @@ Field *create_tmp_field(THD *thd, TABLE *table,Item *item, Item::Type type,
bool table_cant_handle_bit_fields,
bool make_copy_field,
uint convert_blob_length);
-void sp_prepare_create_field(THD *thd, create_field *sql_field);
-int prepare_create_field(create_field *sql_field,
+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,
- uint table_flags);
+ 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,
@@ -837,13 +1216,12 @@ bool mysql_alter_table(THD *thd, char *new_db, char *new_name,
Alter_info *alter_info,
uint order_num, ORDER *order, bool ignore);
bool mysql_recreate_table(THD *thd, TABLE_LIST *table_list);
-bool mysql_create_like_table(THD *thd, TABLE_LIST *table, TABLE_LIST *src_table,
+bool mysql_create_like_table(THD *thd, TABLE_LIST *table,
+ TABLE_LIST *src_table,
HA_CREATE_INFO *create_info);
-bool mysql_rename_table(enum db_type base,
- const char *old_db,
- const char * old_name,
- const char *new_db,
- const char * new_name);
+bool mysql_rename_table(handlerton *base, const char *old_db,
+ const char * old_name, const char *new_db,
+ const char * new_name, uint flags);
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,
@@ -867,31 +1245,42 @@ bool mysql_insert(THD *thd,TABLE_LIST *table,List<Item> &fields,
bool ignore);
int check_that_all_fields_are_given_values(THD *thd, TABLE *entry,
TABLE_LIST *table_list);
-void prepare_triggers_for_insert_stmt(THD *thd, TABLE *table,
- enum_duplicates duplic);
-void mark_fields_used_by_triggers_for_insert_stmt(THD *thd, TABLE *table,
- enum_duplicates duplic);
+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_LIST *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);
-TABLE *open_ltable(THD *thd, TABLE_LIST *table_list, thr_lock_type update);
+uint create_table_def_key(THD *thd, char *key, TABLE_LIST *table_list,
+ bool tmp_table);
+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 table_cache_has_open_placeholder(THD *thd, const char *db,
- const char *table_name);
+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);
-bool reopen_table(TABLE *table,bool locked);
+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);
-bool close_data_tables(THD *thd,const char *db, const char *table_name);
+thr_lock_type read_lock_type_for_table(THD *thd, TABLE *table);
+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);
-bool drop_locked_tables(THD *thd,const char *db, const char *table_name);
+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);
@@ -917,6 +1306,10 @@ find_field_in_table_ref(THD *thd, TABLE_LIST *table_list,
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);
+
+#endif /* MYSQL_SERVER */
#ifdef HAVE_OPENSSL
#include <openssl/des.h>
@@ -935,6 +1328,7 @@ 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);
@@ -946,17 +1340,22 @@ bool mysqld_show_open_tables(THD *thd,const char *wild);
bool mysqld_show_logs(THD *thd);
void 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,
- const HA_CREATE_INFO *create);
+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);
@@ -965,12 +1364,20 @@ 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;
-LEX_STRING *make_lex_string(THD *thd, LEX_STRING *lex_str,
- const char* str, uint length,
- bool allocate_lex_string);
+/* 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,
@@ -985,7 +1392,6 @@ 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);
-bool schema_table_store_record(THD *thd, TABLE *table);
#define is_schema_db(X) \
!my_strcasecmp(system_charset_info, INFORMATION_SCHEMA_NAME.str, (X))
@@ -1008,17 +1414,14 @@ bool mysql_ha_open(THD *thd, TABLE_LIST *tables, bool 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);
-int mysql_ha_flush(THD *thd, TABLE_LIST *tables, uint mode_flags,
- bool is_locked);
-/* mysql_ha_flush mode_flags bits */
-#define MYSQL_HA_CLOSE_FINAL 0x00
-#define MYSQL_HA_REOPEN_ON_USAGE 0x01
-#define MYSQL_HA_FLUSH_ALL 0x02
+void mysql_ha_flush(THD *thd);
+void mysql_ha_rm_tables(THD *thd, TABLE_LIST *tables, bool is_locked);
+void mysql_ha_cleanup(THD *thd);
/* 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, char *field_name, enum enum_field_types type,
+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,
@@ -1026,7 +1429,7 @@ bool add_field_to_list(THD *thd, char *field_name, enum enum_field_types type,
char *change, List<String> *interval_list,
CHARSET_INFO *cs,
uint uint_geom_type);
-create_field * new_create_field(THD *thd, char *field_name, enum_field_types type,
+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,
@@ -1042,7 +1445,7 @@ 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);
-TABLE *unlink_open_table(THD *thd,TABLE *list,TABLE *find);
+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,
@@ -1090,28 +1493,29 @@ bool insert_fields(THD *thd, Name_resolution_context *context,
List_iterator<Item> *it, bool any_privileges);
bool setup_tables(THD *thd, Name_resolution_context *context,
List<TABLE_LIST> *from_clause, TABLE_LIST *tables,
- Item **conds, TABLE_LIST **leaves, bool select_insert);
-bool setup_tables_and_check_access (THD *thd,
- Name_resolution_context *context,
- List<TABLE_LIST> *from_clause,
- TABLE_LIST *tables, Item **conds,
- TABLE_LIST **leaves,
- bool select_insert,
- ulong want_access_first,
- ulong want_access);
+ TABLE_LIST **leaves, bool select_insert);
+bool setup_tables_and_check_access(THD *thd,
+ Name_resolution_context *context,
+ List<TABLE_LIST> *from_clause,
+ TABLE_LIST *tables,
+ TABLE_LIST **leaves,
+ bool select_insert,
+ ulong want_access_first,
+ ulong want_access);
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, bool set_query_id,
+ 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, bool set_query_id,
- List<Item> *sum_func_list,
- bool allow_sum_func)
+ 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, set_query_id, sum_func_list,
+ 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;
@@ -1120,15 +1524,30 @@ int setup_conds(THD *thd, TABLE_LIST *tables, TABLE_LIST *leaves,
COND **conds);
int setup_ftfuncs(SELECT_LEX* select);
int init_ftfuncs(THD *thd, SELECT_LEX* select, bool no_order);
-void wait_for_refresh(THD *thd);
+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);
-int simple_open_n_lock_tables(THD *thd,TABLE_LIST *tables);
-int open_and_lock_tables(THD *thd,TABLE_LIST *tables);
+/* 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);
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(enum db_type base, char *path);
+bool rm_temporary_table(handlerton *base, char *path);
void free_io_cache(TABLE *entry);
void intern_close_table(TABLE *entry);
bool close_thread_table(THD *thd, TABLE **table_ptr);
@@ -1140,14 +1559,35 @@ TABLE_LIST *find_table_in_list(TABLE_LIST *table,
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);
-bool close_temporary_table(THD *thd, const char *db, const char *table_name);
-void close_temporary(TABLE *table, bool delete_table);
+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);
+
+#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
@@ -1157,7 +1597,140 @@ bool is_equal(const LEX_STRING *a, const LEX_STRING *b);
bool remove_table_from_cache(THD *thd, const char *db, const char *table,
uint flags);
-bool close_cached_tables(THD *thd, bool wait_for_refresh, TABLE_LIST *tables);
+#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(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_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);
@@ -1202,16 +1775,15 @@ int mysql_load(THD *thd, sql_exchange *ex, TABLE_LIST *table_list,
int write_record(THD *thd, TABLE *table, COPY_INFO *info);
/* sql_manager.cc */
-/* bits set in manager_status */
-#define MANAGER_BERKELEY_LOG_CLEANUP (1L << 0)
-extern ulong volatile manager_status;
-extern bool volatile manager_thread_in_use, mqh_used;
-extern pthread_t manager_thread;
-pthread_handler_t handle_manager(void *arg);
+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);
+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,
@@ -1219,19 +1791,24 @@ void print_plan(JOIN* join,uint idx, double record_count, double read_time,
#endif
void mysql_print_status();
/* key.cc */
-int find_ref_key(TABLE *form,Field *field, uint *offset);
-void key_copy(byte *to_key, byte *from_record, KEY *key_info, uint key_length);
-void key_restore(byte *to_record, byte *from_key, KEY *key_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);
+void key_restore(uchar *to_record, uchar *from_key, KEY *key_info,
uint key_length);
-bool key_cmp_if_same(TABLE *form,const byte *key,uint index,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, List<Item> &fields);
-int key_cmp(KEY_PART_INFO *key_part, const byte *key, uint key_length);
+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);
+extern "C" int key_rec_cmp(void *key_info, uchar *a, uchar *b);
bool init_errmessage(void);
+#endif /* MYSQL_SERVER */
+void sql_perror(const char *message);
-bool fn_format_relative_to_data_home(my_string to, const char *name,
+bool fn_format_relative_to_data_home(char * to, const char *name,
const char *dir, const char *extension);
+#ifdef MYSQL_SERVER
File open_binlog(IO_CACHE *log, const char *log_file_name,
const char **errmsg);
@@ -1239,6 +1816,11 @@ File open_binlog(IO_CACHE *log, const char *log_file_name,
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);
@@ -1247,57 +1829,77 @@ extern enum_field_types agg_field_type(Item **items, uint nitems);
/* strfunc.cc */
ulonglong find_set(TYPELIB *lib, const char *x, uint length, CHARSET_INFO *cs,
char **err_pos, uint *err_len, bool *set_warning);
-uint find_type(TYPELIB *lib, const char *find, uint length, bool part_match);
-uint find_type2(TYPELIB *lib, const char *find, uint length, CHARSET_INFO *cs);
+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);
-void my_dbopt_free(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;
+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 char *mysql_data_home,server_version[SERVER_VERSION_LENGTH],
- mysql_real_data_home[], *opt_mysql_tmpdir, mysql_charsets_dir[],
- mysql_unpacked_real_data_home[],
+ mysql_real_data_home[], mysql_unpacked_real_data_home[];
+extern CHARSET_INFO *character_set_filesystem;
+#endif /* MYSQL_SERVER || INNODB_COMPATIBILITY_HOOKS */
+#ifdef MYSQL_SERVER
+extern char *opt_mysql_tmpdir, mysql_charsets_dir[],
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 MY_TMPDIR mysql_tmpdir_list;
-extern const char *command_name[];
+extern const LEX_STRING command_name[];
extern const char *first_keyword, *my_localhost, *delayed_user, *binary_keyword;
extern 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 triggers_file_ext;
-extern const char * const trigname_file_ext;
+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], reg_ext[FN_EXTLEN];
+extern char language[FN_REFLEN];
+#endif /* MYSQL_SERVER */
+#if defined MYSQL_SERVER || defined INNODB_COMPATIBILITY_HOOKS
+extern char reg_ext[FN_EXTLEN];
+extern 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;
extern ulonglong log_10_int[20];
extern ulonglong keybuff_size;
extern ulonglong thd_startup_options;
-extern ulong flush_version, thread_id;
+extern ulong thread_id;
extern ulong binlog_cache_use, binlog_cache_disk_use;
extern ulong aborted_threads,aborted_connects;
extern ulong delayed_insert_timeout;
@@ -1307,72 +1909,102 @@ 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;
+extern ulong table_cache_size, table_def_size;
extern ulong max_connections,max_connect_errors, connect_timeout;
extern ulong slave_net_timeout, slave_trans_retries;
extern uint max_user_connections;
extern ulong what_to_log,flush_time;
-extern ulong query_buff_size, thread_stack;
+extern ulong query_buff_size;
extern ulong max_prepared_stmt_count, prepared_stmt_count;
extern ulong binlog_cache_size, max_binlog_cache_size, open_files_limit;
extern ulong max_binlog_size, max_relay_log_size;
-extern ulong rpl_recovery_rank, thread_cache_size;
+extern ulong opt_binlog_rows_event_max_size;
+extern ulong rpl_recovery_rank, thread_cache_size, thread_pool_size;
extern ulong back_log;
-extern ulong specialflag, current_pid;
+#endif /* MYSQL_SERVER */
+#if defined MYSQL_SERVER || defined INNODB_COMPATIBILITY_HOOKS
+extern ulong 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 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 uint test_flags,select_errors,ha_open_options;
extern uint protocol_version, mysqld_port, dropping_tables;
-extern uint delay_key_write_options, lower_case_table_names;
+extern uint delay_key_write_options;
+#endif /* MYSQL_SERVER */
+#if defined MYSQL_SERVER || defined INNODB_COMPATIBILITY_HOOKS
+extern 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, mysqld_embedded;
-extern bool using_update_log, opt_large_files, server_id_supplied;
+extern bool opt_using_transactions;
+#endif /* MYSQL_SERVER */
+#if defined MYSQL_SERVER || defined INNODB_COMPATIBILITY_HOOKS
+extern 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, opt_log_queries_not_using_indexes;
+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_ignore_builtin_innodb;
extern my_bool opt_character_set_client_handshake;
-extern bool volatile abort_loop, shutdown_in_progress, grant_option;
+extern bool volatile abort_loop, shutdown_in_progress;
extern uint volatile thread_count, thread_running, global_read_lock;
+extern uint connection_count;
extern my_bool opt_sql_bin_update, opt_safe_user_create, opt_no_mix_types;
-extern my_bool opt_safe_show_db, opt_local_infile;
+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 my_bool opt_readonly, 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 my_bool opt_log_slow_admin_statements;
+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 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;
-
-extern char *opt_plugin_dir_ptr;
-extern char opt_plugin_dir[FN_REFLEN];
-
-extern MYSQL_LOG mysql_log,mysql_slow_log,mysql_bin_log;
+#endif /* MYSQL_SERVER || INNODB_COMPATIBILITY_HOOKS */
+#ifdef MYSQL_SERVER
+extern char *opt_logname, *opt_slow_logname;
+extern const char *log_output_str;
+
+extern 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;
extern pthread_key(MEM_ROOT**,THR_MALLOC);
-extern pthread_mutex_t LOCK_mysql_create_db,LOCK_Acl,LOCK_open,
+extern pthread_mutex_t LOCK_mysql_create_db,LOCK_Acl,LOCK_open, LOCK_lock_db,
LOCK_thread_count,LOCK_mapped_file,LOCK_user_locks, LOCK_status,
LOCK_error_log, LOCK_delayed_insert, LOCK_uuid_generator,
LOCK_delayed_status, LOCK_delayed_create, LOCK_crypt, LOCK_timezone,
LOCK_slave_list, LOCK_active_mi, LOCK_manager, LOCK_global_read_lock,
LOCK_global_system_variables, LOCK_user_conn,
LOCK_prepared_stmt_count,
- LOCK_bytes_sent, LOCK_bytes_received;
+ LOCK_bytes_sent, LOCK_bytes_received, LOCK_connection_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 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, COND_manager;
extern pthread_cond_t COND_global_read_lock;
extern pthread_attr_t connection_attrib;
@@ -1381,8 +2013,12 @@ extern I_List<NAMED_LIST> key_caches;
extern MY_BITMAP temp_pool;
extern String my_empty_string;
extern const String my_null_string;
-extern SHOW_VAR init_vars[],status_vars[], internal_vars[];
+extern SHOW_VAR status_vars[];
+#endif /* MYSQL_SERVER */
+#if defined MYSQL_SERVER || defined INNODB_COMPATIBILITY_HOOKS
extern 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 rand_struct sql_rand;
@@ -1391,75 +2027,31 @@ extern const char *opt_date_time_formats[];
extern KNOWN_DATE_TIME_FORMAT known_date_time_formats[];
extern String null_string;
-extern HASH open_cache;
+extern HASH open_cache, lock_db_cache;
extern TABLE *unused_tables;
-extern I_List<i_string> binlog_do_db, binlog_ignore_db;
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;
-#ifdef HAVE_INNOBASE_DB
-extern handlerton innobase_hton;
-#define have_innodb innobase_hton.state
-#else
-extern SHOW_COMP_OPTION have_innodb;
-#endif
-#ifdef HAVE_BERKELEY_DB
-extern handlerton berkeley_hton;
-#define have_berkeley_db berkeley_hton.state
-#else
-extern SHOW_COMP_OPTION have_berkeley_db;
-#endif
-#ifdef HAVE_EXAMPLE_DB
-extern handlerton example_hton;
-#define have_example_db example_hton.state
-#else
-extern SHOW_COMP_OPTION have_example_db;
-#endif
-#ifdef HAVE_ARCHIVE_DB
-extern handlerton archive_hton;
-#define have_archive_db archive_hton.state
-#else
-extern SHOW_COMP_OPTION have_archive_db;
-#endif
-#ifdef HAVE_CSV_DB
-extern handlerton tina_hton;
-#define have_csv_db tina_hton.state
-#else
-extern SHOW_COMP_OPTION have_csv_db;
-#endif
-#ifdef HAVE_FEDERATED_DB
-extern handlerton federated_hton;
-#define have_federated_db federated_hton.state
-#else
-extern SHOW_COMP_OPTION have_federated_db;
-#endif
-#ifdef HAVE_BLACKHOLE_DB
-extern handlerton blackhole_hton;
-#define have_blackhole_db blackhole_hton.state
-#else
-extern SHOW_COMP_OPTION have_blackhole_db;
-#endif
-#ifdef HAVE_NDBCLUSTER_DB
-extern handlerton ndbcluster_hton;
-#define have_ndbcluster ndbcluster_hton.state
-#else
-extern SHOW_COMP_OPTION have_ndbcluster;
-#endif
-
-/* MRG_MYISAM handler is always built, but may be skipped */
-extern handlerton myisammrg_hton;
-#define have_merge_db myisammrg_hton.state
+extern handlerton *partition_hton;
+extern handlerton *myisam_hton;
+extern handlerton *heap_hton;
-extern SHOW_COMP_OPTION have_isam;
-extern SHOW_COMP_OPTION have_raid, have_ssl, have_symlink, have_dlopen;
+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;
+
#ifndef __WIN__
extern pthread_t signal_thread;
#endif
@@ -1475,13 +2067,17 @@ MYSQL_LOCK *mysql_lock_tables(THD *thd, TABLE **table, uint count,
#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
void mysql_unlock_tables(THD *thd, MYSQL_LOCK *sql_lock);
void mysql_unlock_read_tables(THD *thd, MYSQL_LOCK *sql_lock);
void mysql_unlock_some_tables(THD *thd, TABLE **table,uint count);
void mysql_lock_remove(THD *thd, MYSQL_LOCK *locked,TABLE *table,
bool always_unlock);
-void mysql_lock_abort(THD *thd, TABLE *table);
+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,
@@ -1498,37 +2094,55 @@ void broadcast_refresh(void);
/* 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);
+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);
-bool mysql_create_frm(THD *thd, my_string file_name,
+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,
+ List<Create_field> &create_field,
uint key_count,KEY *key_info,handler *db_type);
-int rea_create_table(THD *thd, my_string file_name,
- const char *db, const char *table,
+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);
-int format_number(uint inputflag,uint max_length,my_string pos,uint length,
- my_string *errpos);
-int openfrm(THD *thd, const char *name,const char *alias,uint filestat,
- uint prgflag, uint ha_open_flags, TABLE *outparam);
-int readfrm(const char *name, const void** data, uint* length);
-int writefrm(const char* name, const void* data, uint len);
-int closefrm(TABLE *table);
-int read_string(File file, gptr *to, uint length);
+ 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);
+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);
@@ -1543,12 +2157,14 @@ 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 char *str_val,
- uint str_length, timestamp_type time_type,
- const char *field_name);
-
+ uint str_length, timestamp_type time_type,
+ const char *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);
+ 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,
@@ -1565,11 +2181,12 @@ void make_date(const DATE_TIME_FORMAT *format, const MYSQL_TIME *l_time,
String *str);
void make_time(const DATE_TIME_FORMAT *format, const MYSQL_TIME *l_time,
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);
int test_if_number(char *str,int *res,bool allow_wildcards);
-void change_byte(byte *,uint,char,char);
+void change_byte(uchar *,uint,char,char);
void 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);
@@ -1578,37 +2195,67 @@ void init_read_record_idx(READ_RECORD *info, THD *thd, TABLE *table,
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, ha_rows *examined_rows);
+ ha_rows max_rows, bool sort_positions,
+ ha_rows *examined_rows);
void filesort_free_buffers(TABLE *table, bool full);
-void change_double_for_sort(double nr,byte *to);
+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(my_string *typelibs, my_string *end);
+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, char *name, const char *db, const char *table,
- uint reclength,uchar *fileinfo,
+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(char *db);
+bool check_db_name(LEX_STRING *db);
bool check_column_name(const char *name);
bool check_table_name(const char *name, uint length);
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);
+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);
+#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);
+
+#define MYSQL50_TABLE_NAME_PREFIX "#mysql50#"
+#define MYSQL50_TABLE_NAME_PREFIX_LENGTH 9
+
+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;
-my_string ip_to_hostname(struct in_addr *in,uint *errors);
+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();
@@ -1633,7 +2280,6 @@ void free_list(I_List <i_string_pair> *list);
void free_list(I_List <i_string> *list);
/* sql_yacc.cc */
-extern int MYSQLparse(void *thd);
#ifndef DBUG_OFF
extern void turn_parser_debug_on();
#endif
@@ -1706,23 +2352,21 @@ Item * all_any_subquery_creator(Item *left_expr,
bool all,
SELECT_LEX *select_lex);
-/*
- clean/setup table fields and map
+/**
+ clean/setup table fields and map.
- SYNOPSYS
- setup_table_map()
- table - TABLE structure pointer (which should be setup)
- table_list TABLE_LIST structure pointer (owner of TABLE)
- tablenr - table number
+ @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->const_table= 0;
table->null_row= 0;
table->status= STATUS_NO_RECORD;
- table->keys_in_use_for_query= table->s->keys_in_use;
table->maybe_null= table_list->outer_join;
TABLE_LIST *embedding= table_list->embedding;
while (!table->maybe_null && embedding)
@@ -1733,13 +2377,13 @@ inline void setup_table_map(TABLE *table, TABLE_LIST *table_list, uint tablenr)
table->tablenr= tablenr;
table->map= (table_map) 1 << tablenr;
table->force_index= table_list->force_index;
+ table->covering_keys= table->s->keys_for_keyread;
+ table->merge_keys.clear_all();
}
-/*
- SYNOPSYS
- hexchar_to_int()
- convert a hex digit into number
+/**
+ convert a hex digit into number.
*/
inline int hexchar_to_int(char c)
@@ -1752,14 +2396,12 @@ inline int hexchar_to_int(char c)
return -1;
}
-/*
- is_user_table()
- return true if the table was created explicitly
+/**
+ return true if the table was created explicitly.
*/
-
inline bool is_user_table(TABLE * table)
{
- const char *name= table->s->table_name;
+ const char *name= table->s->table_name.str;
return strncmp(name, tmp_file_prefix, tmp_file_prefix_length);
}
@@ -1769,17 +2411,82 @@ inline bool is_user_table(TABLE * table)
*/
#ifndef EMBEDDED_LIBRARY
-extern "C" void unireg_abort(int exit_code);
+extern "C" void unireg_abort(int exit_code) __attribute__((noreturn));
void kill_delayed_threads(void);
-bool check_stack_overrun(THD *thd, long margin, char *dummy);
+bool check_stack_overrun(THD *thd, long margin, uchar *dummy);
#else
#define unireg_abort(exit_code) DBUG_RETURN(exit_code)
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);
+
+#endif /* MYSQL_SERVER */
extern "C" int test_if_data_home_dir(const char *dir);
#endif /* MYSQL_CLIENT */
-#endif
+#endif /* MYSQL_PRIV_H */
diff --git a/sql/mysql_priv.h.pp b/sql/mysql_priv.h.pp
new file mode 100644
index 00000000000..8bb31f64587
--- /dev/null
+++ b/sql/mysql_priv.h.pp
@@ -0,0 +1,10978 @@
+#include <my_global.h>
+#include <my_config.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <math.h>
+#include <limits.h>
+#include <float.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <sys/timeb.h>
+#include <sys/time.h>
+#include <time.h>
+#include <unistd.h>
+#include <alloca.h>
+#include <errno.h>
+#include <crypt.h>
+#include <assert.h>
+#include <my_attribute.h>
+int __cxa_pure_virtual () __attribute__ ((weak));
+#include <my_dbug.h>
+struct _db_code_state_;
+extern int _db_keyword_(struct _db_code_state_ *cs, const char *keyword);
+extern int _db_strict_keyword_(const char *keyword);
+extern int _db_explain_(struct _db_code_state_ *cs, char *buf, size_t len);
+extern int _db_explain_init_(char *buf, size_t len);
+extern void _db_setjmp_(void);
+extern void _db_longjmp_(void);
+extern void _db_process_(const char *name);
+extern void _db_push_(const char *control);
+extern void _db_pop_(void);
+extern void _db_set_(struct _db_code_state_ *cs, const char *control);
+extern void _db_set_init_(const char *control);
+extern void _db_enter_(const char *_func_,const char *_file_,uint _line_,
+ const char **_sfunc_,const char **_sfile_,
+ uint *_slevel_, char ***);
+extern void _db_return_(uint _line_,const char **_sfunc_,const char **_sfile_,
+ uint *_slevel_);
+extern void _db_pargs_(uint _line_,const char *keyword);
+extern void _db_doprnt_ (const char *format,...)
+ __attribute__((format(printf, 1, 2)));
+extern void _db_dump_(uint _line_,const char *keyword,
+ const unsigned char *memory, size_t length);
+extern void _db_end_(void);
+extern void _db_lock_file_(void);
+extern void _db_unlock_file_(void);
+extern FILE *_db_fp_(void);
+typedef int File;
+typedef int my_socket;
+typedef void (*sig_return)();
+typedef char pchar;
+typedef char puchar;
+typedef char pbool;
+typedef short pshort;
+typedef float pfloat;
+typedef int (*qsort_cmp)(const void *,const void *);
+typedef int (*qsort_cmp2)(void*, const void *,const void *);
+#include <sys/socket.h>
+typedef socklen_t size_socket;
+typedef long my_ptrdiff_t;
+typedef unsigned char uchar;
+typedef signed char int8;
+typedef unsigned char uint8;
+typedef short int16;
+typedef unsigned short uint16;
+typedef int int32;
+typedef unsigned int uint32;
+typedef unsigned long long int ulonglong;
+typedef long long int longlong;
+typedef longlong int64;
+typedef ulonglong uint64;
+typedef unsigned long long my_ulonglong;
+typedef int intptr;
+typedef ulonglong my_off_t;
+typedef off_t os_off_t;
+typedef uint8 int7;
+typedef short int15;
+typedef int myf;
+typedef char my_bool;
+typedef char bool;
+typedef union {
+ double v;
+ long m[2];
+} doubleget_union;
+#include <dlfcn.h>
+#include <mysql_version.h>
+#include <mysql_embed.h>
+#include <my_sys.h>
+#include <my_pthread.h>
+#include <pthread.h>
+#include <sched.h>
+extern int my_pthread_getprio(pthread_t thread_id);
+typedef void *(* pthread_handler)(void *);
+extern void my_pthread_setprio(pthread_t thread_id,int prior);
+extern void my_pthread_attr_setprio(pthread_attr_t *attr, int priority);
+typedef struct st_safe_mutex_t
+{
+ pthread_mutex_t global,mutex;
+ const char *file;
+ uint line,count;
+ pthread_t thread;
+} safe_mutex_t;
+int safe_mutex_init(safe_mutex_t *mp, const pthread_mutexattr_t *attr,
+ const char *file, uint line);
+int safe_mutex_lock(safe_mutex_t *mp, my_bool try_lock, const char *file, uint line);
+int safe_mutex_unlock(safe_mutex_t *mp,const char *file, uint line);
+int safe_mutex_destroy(safe_mutex_t *mp,const char *file, uint line);
+int safe_cond_wait(pthread_cond_t *cond, safe_mutex_t *mp,const char *file,
+ uint line);
+int safe_cond_timedwait(pthread_cond_t *cond, safe_mutex_t *mp,
+ struct timespec *abstime, const char *file, uint line);
+void safe_mutex_global_init(void);
+void safe_mutex_end(FILE *file);
+typedef ulong my_thread_id;
+extern my_bool my_thread_global_init(void);
+extern void my_thread_global_end(void);
+extern my_bool my_thread_init(void);
+extern void my_thread_end(void);
+extern const char *my_thread_name(void);
+extern my_thread_id my_thread_dbug_id(void);
+extern int pthread_no_free(void *);
+extern int pthread_dummy(int);
+struct st_my_thread_var
+{
+ int thr_errno;
+ pthread_cond_t suspend;
+ pthread_mutex_t mutex;
+ pthread_mutex_t * volatile current_mutex;
+ pthread_cond_t * volatile current_cond;
+ pthread_t pthread_self;
+ my_thread_id id;
+ int cmp_length;
+ int volatile abort;
+ my_bool init;
+ struct st_my_thread_var *next,**prev;
+ void *opt_info;
+ void *dbug;
+ char name[10 +1];
+};
+extern struct st_my_thread_var *_my_thread_var(void) __attribute__ ((const));
+extern uint my_thread_end_wait_time;
+extern uint thd_lib_detected;
+#include <m_ctype.h>
+#include <my_attribute.h>
+typedef struct unicase_info_st
+{
+ uint16 toupper;
+ uint16 tolower;
+ uint16 sort;
+} MY_UNICASE_INFO;
+extern MY_UNICASE_INFO *my_unicase_default[256];
+extern MY_UNICASE_INFO *my_unicase_turkish[256];
+typedef struct uni_ctype_st
+{
+ uchar pctype;
+ uchar *ctype;
+} MY_UNI_CTYPE;
+extern MY_UNI_CTYPE my_uni_ctype[256];
+typedef struct my_uni_idx_st
+{
+ uint16 from;
+ uint16 to;
+ uchar *tab;
+} MY_UNI_IDX;
+typedef struct
+{
+ uint beg;
+ uint end;
+ uint mb_len;
+} my_match_t;
+enum my_lex_states
+{
+ MY_LEX_START, MY_LEX_CHAR, MY_LEX_IDENT,
+ MY_LEX_IDENT_SEP, MY_LEX_IDENT_START,
+ MY_LEX_REAL, MY_LEX_HEX_NUMBER, MY_LEX_BIN_NUMBER,
+ MY_LEX_CMP_OP, MY_LEX_LONG_CMP_OP, MY_LEX_STRING, MY_LEX_COMMENT, MY_LEX_END,
+ MY_LEX_OPERATOR_OR_IDENT, MY_LEX_NUMBER_IDENT, MY_LEX_INT_OR_REAL,
+ MY_LEX_REAL_OR_POINT, MY_LEX_BOOL, MY_LEX_EOL, MY_LEX_ESCAPE,
+ MY_LEX_LONG_COMMENT, MY_LEX_END_LONG_COMMENT, MY_LEX_SEMICOLON,
+ MY_LEX_SET_VAR, MY_LEX_USER_END, MY_LEX_HOSTNAME, MY_LEX_SKIP,
+ MY_LEX_USER_VARIABLE_DELIMITER, MY_LEX_SYSTEM_VAR,
+ MY_LEX_IDENT_OR_KEYWORD,
+ MY_LEX_IDENT_OR_HEX, MY_LEX_IDENT_OR_BIN, MY_LEX_IDENT_OR_NCHAR,
+ MY_LEX_STRING_OR_DELIMITER
+};
+struct charset_info_st;
+typedef struct my_collation_handler_st
+{
+ my_bool (*init)(struct charset_info_st *, void *(*alloc)(size_t));
+ int (*strnncoll)(struct charset_info_st *,
+ const uchar *, size_t, const uchar *, size_t, my_bool);
+ int (*strnncollsp)(struct charset_info_st *,
+ const uchar *, size_t, const uchar *, size_t,
+ my_bool diff_if_only_endspace_difference);
+ size_t (*strnxfrm)(struct charset_info_st *,
+ uchar *, size_t, const uchar *, size_t);
+ size_t (*strnxfrmlen)(struct charset_info_st *, size_t);
+ my_bool (*like_range)(struct charset_info_st *,
+ const char *s, size_t s_length,
+ pchar w_prefix, pchar w_one, pchar w_many,
+ size_t res_length,
+ char *min_str, char *max_str,
+ size_t *min_len, size_t *max_len);
+ int (*wildcmp)(struct charset_info_st *,
+ const char *str,const char *str_end,
+ const char *wildstr,const char *wildend,
+ int escape,int w_one, int w_many);
+ int (*strcasecmp)(struct charset_info_st *, const char *, const char *);
+ uint (*instr)(struct charset_info_st *,
+ const char *b, size_t b_length,
+ const char *s, size_t s_length,
+ my_match_t *match, uint nmatch);
+ void (*hash_sort)(struct charset_info_st *cs, const uchar *key, size_t len,
+ ulong *nr1, ulong *nr2);
+ my_bool (*propagate)(struct charset_info_st *cs, const uchar *str, size_t len);
+} MY_COLLATION_HANDLER;
+extern MY_COLLATION_HANDLER my_collation_mb_bin_handler;
+extern MY_COLLATION_HANDLER my_collation_8bit_bin_handler;
+extern MY_COLLATION_HANDLER my_collation_8bit_simple_ci_handler;
+extern MY_COLLATION_HANDLER my_collation_ucs2_uca_handler;
+typedef int (*my_charset_conv_mb_wc)(struct charset_info_st *, ulong *,
+ const uchar *, const uchar *);
+typedef int (*my_charset_conv_wc_mb)(struct charset_info_st *, ulong,
+ uchar *, uchar *);
+typedef size_t (*my_charset_conv_case)(struct charset_info_st *,
+ char *, size_t, char *, size_t);
+typedef struct my_charset_handler_st
+{
+ my_bool (*init)(struct charset_info_st *, void *(*alloc)(size_t));
+ uint (*ismbchar)(struct charset_info_st *, const char *, const char *);
+ uint (*mbcharlen)(struct charset_info_st *, uint c);
+ size_t (*numchars)(struct charset_info_st *, const char *b, const char *e);
+ size_t (*charpos)(struct charset_info_st *, const char *b, const char *e,
+ size_t pos);
+ size_t (*well_formed_len)(struct charset_info_st *,
+ const char *b,const char *e,
+ size_t nchars, int *error);
+ size_t (*lengthsp)(struct charset_info_st *, const char *ptr, size_t length);
+ size_t (*numcells)(struct charset_info_st *, const char *b, const char *e);
+ my_charset_conv_mb_wc mb_wc;
+ my_charset_conv_wc_mb wc_mb;
+ int (*ctype)(struct charset_info_st *cs, int *ctype,
+ const uchar *s, const uchar *e);
+ size_t (*caseup_str)(struct charset_info_st *, char *);
+ size_t (*casedn_str)(struct charset_info_st *, char *);
+ my_charset_conv_case caseup;
+ my_charset_conv_case casedn;
+ size_t (*snprintf)(struct charset_info_st *, char *to, size_t n,
+ const char *fmt,
+ ...) __attribute__((format(printf, 4, 5)));
+ size_t (*long10_to_str)(struct charset_info_st *, char *to, size_t n,
+ int radix, long int val);
+ size_t (*longlong10_to_str)(struct charset_info_st *, char *to, size_t n,
+ int radix, longlong val);
+ void (*fill)(struct charset_info_st *, char *to, size_t len, int fill);
+ long (*strntol)(struct charset_info_st *, const char *s, size_t l,
+ int base, char **e, int *err);
+ ulong (*strntoul)(struct charset_info_st *, const char *s, size_t l,
+ int base, char **e, int *err);
+ longlong (*strntoll)(struct charset_info_st *, const char *s, size_t l,
+ int base, char **e, int *err);
+ ulonglong (*strntoull)(struct charset_info_st *, const char *s, size_t l,
+ int base, char **e, int *err);
+ double (*strntod)(struct charset_info_st *, char *s, size_t l, char **e,
+ int *err);
+ longlong (*strtoll10)(struct charset_info_st *cs,
+ const char *nptr, char **endptr, int *error);
+ ulonglong (*strntoull10rnd)(struct charset_info_st *cs,
+ const char *str, size_t length,
+ int unsigned_fl,
+ char **endptr, int *error);
+ size_t (*scan)(struct charset_info_st *, const char *b, const char *e,
+ int sq);
+} MY_CHARSET_HANDLER;
+extern MY_CHARSET_HANDLER my_charset_8bit_handler;
+extern MY_CHARSET_HANDLER my_charset_ucs2_handler;
+typedef struct charset_info_st
+{
+ uint number;
+ uint primary_number;
+ uint binary_number;
+ uint state;
+ const char *csname;
+ const char *name;
+ const char *comment;
+ const char *tailoring;
+ uchar *ctype;
+ uchar *to_lower;
+ uchar *to_upper;
+ uchar *sort_order;
+ uint16 *contractions;
+ uint16 **sort_order_big;
+ uint16 *tab_to_uni;
+ MY_UNI_IDX *tab_from_uni;
+ MY_UNICASE_INFO **caseinfo;
+ uchar *state_map;
+ uchar *ident_map;
+ uint strxfrm_multiply;
+ uchar caseup_multiply;
+ uchar casedn_multiply;
+ uint mbminlen;
+ uint mbmaxlen;
+ uint16 min_sort_char;
+ uint16 max_sort_char;
+ uchar pad_char;
+ my_bool escape_with_backslash_is_dangerous;
+ MY_CHARSET_HANDLER *cset;
+ MY_COLLATION_HANDLER *coll;
+} CHARSET_INFO;
+extern CHARSET_INFO my_charset_bin;
+extern CHARSET_INFO my_charset_big5_chinese_ci;
+extern CHARSET_INFO my_charset_big5_bin;
+extern CHARSET_INFO my_charset_cp932_japanese_ci;
+extern CHARSET_INFO my_charset_cp932_bin;
+extern CHARSET_INFO my_charset_eucjpms_japanese_ci;
+extern CHARSET_INFO my_charset_eucjpms_bin;
+extern CHARSET_INFO my_charset_euckr_korean_ci;
+extern CHARSET_INFO my_charset_euckr_bin;
+extern CHARSET_INFO my_charset_gb2312_chinese_ci;
+extern CHARSET_INFO my_charset_gb2312_bin;
+extern CHARSET_INFO my_charset_gbk_chinese_ci;
+extern CHARSET_INFO my_charset_gbk_bin;
+extern CHARSET_INFO my_charset_latin1;
+extern CHARSET_INFO my_charset_latin1_german2_ci;
+extern CHARSET_INFO my_charset_latin1_bin;
+extern CHARSET_INFO my_charset_latin2_czech_ci;
+extern CHARSET_INFO my_charset_sjis_japanese_ci;
+extern CHARSET_INFO my_charset_sjis_bin;
+extern CHARSET_INFO my_charset_tis620_thai_ci;
+extern CHARSET_INFO my_charset_tis620_bin;
+extern CHARSET_INFO my_charset_ucs2_general_ci;
+extern CHARSET_INFO my_charset_ucs2_bin;
+extern CHARSET_INFO my_charset_ucs2_unicode_ci;
+extern CHARSET_INFO my_charset_ujis_japanese_ci;
+extern CHARSET_INFO my_charset_ujis_bin;
+extern CHARSET_INFO my_charset_utf8_general_ci;
+extern CHARSET_INFO my_charset_utf8_unicode_ci;
+extern CHARSET_INFO my_charset_utf8_bin;
+extern CHARSET_INFO my_charset_cp1250_czech_ci;
+extern CHARSET_INFO my_charset_filename;
+extern size_t my_strnxfrm_simple(CHARSET_INFO *, uchar *, size_t,
+ const uchar *, size_t);
+size_t my_strnxfrmlen_simple(CHARSET_INFO *, size_t);
+extern int my_strnncoll_simple(CHARSET_INFO *, const uchar *, size_t,
+ const uchar *, size_t, my_bool);
+extern int my_strnncollsp_simple(CHARSET_INFO *, const uchar *, size_t,
+ const uchar *, size_t,
+ my_bool diff_if_only_endspace_difference);
+extern void my_hash_sort_simple(CHARSET_INFO *cs,
+ const uchar *key, size_t len,
+ ulong *nr1, ulong *nr2);
+extern size_t my_lengthsp_8bit(CHARSET_INFO *cs, const char *ptr, size_t length);
+extern uint my_instr_simple(struct charset_info_st *,
+ const char *b, size_t b_length,
+ const char *s, size_t s_length,
+ my_match_t *match, uint nmatch);
+extern size_t my_caseup_str_8bit(CHARSET_INFO *, char *);
+extern size_t my_casedn_str_8bit(CHARSET_INFO *, char *);
+extern size_t my_caseup_8bit(CHARSET_INFO *, char *src, size_t srclen,
+ char *dst, size_t dstlen);
+extern size_t my_casedn_8bit(CHARSET_INFO *, char *src, size_t srclen,
+ char *dst, size_t dstlen);
+extern int my_strcasecmp_8bit(CHARSET_INFO * cs, const char *, const char *);
+int my_mb_wc_8bit(CHARSET_INFO *cs,ulong *wc, const uchar *s,const uchar *e);
+int my_wc_mb_8bit(CHARSET_INFO *cs,ulong wc, uchar *s, uchar *e);
+int my_mb_ctype_8bit(CHARSET_INFO *,int *, const uchar *,const uchar *);
+int my_mb_ctype_mb(CHARSET_INFO *,int *, const uchar *,const uchar *);
+size_t my_scan_8bit(CHARSET_INFO *cs, const char *b, const char *e, int sq);
+size_t my_snprintf_8bit(struct charset_info_st *, char *to, size_t n,
+ const char *fmt, ...)
+ __attribute__((format(printf, 4, 5)));
+long my_strntol_8bit(CHARSET_INFO *, const char *s, size_t l, int base,
+ char **e, int *err);
+ulong my_strntoul_8bit(CHARSET_INFO *, const char *s, size_t l, int base,
+ char **e, int *err);
+longlong my_strntoll_8bit(CHARSET_INFO *, const char *s, size_t l, int base,
+ char **e, int *err);
+ulonglong my_strntoull_8bit(CHARSET_INFO *, const char *s, size_t l, int base,
+ char **e, int *err);
+double my_strntod_8bit(CHARSET_INFO *, char *s, size_t l,char **e,
+ int *err);
+size_t my_long10_to_str_8bit(CHARSET_INFO *, char *to, size_t l, int radix,
+ long int val);
+size_t my_longlong10_to_str_8bit(CHARSET_INFO *, char *to, size_t l, int radix,
+ longlong val);
+longlong my_strtoll10_8bit(CHARSET_INFO *cs,
+ const char *nptr, char **endptr, int *error);
+longlong my_strtoll10_ucs2(CHARSET_INFO *cs,
+ const char *nptr, char **endptr, int *error);
+ulonglong my_strntoull10rnd_8bit(CHARSET_INFO *cs,
+ const char *str, size_t length, int
+ unsigned_fl, char **endptr, int *error);
+ulonglong my_strntoull10rnd_ucs2(CHARSET_INFO *cs,
+ const char *str, size_t length,
+ int unsigned_fl, char **endptr, int *error);
+void my_fill_8bit(CHARSET_INFO *cs, char* to, size_t l, int fill);
+my_bool my_like_range_simple(CHARSET_INFO *cs,
+ const char *ptr, size_t ptr_length,
+ pbool escape, pbool w_one, pbool w_many,
+ size_t res_length,
+ char *min_str, char *max_str,
+ size_t *min_length, size_t *max_length);
+my_bool my_like_range_mb(CHARSET_INFO *cs,
+ const char *ptr, size_t ptr_length,
+ pbool escape, pbool w_one, pbool w_many,
+ size_t res_length,
+ char *min_str, char *max_str,
+ size_t *min_length, size_t *max_length);
+my_bool my_like_range_ucs2(CHARSET_INFO *cs,
+ const char *ptr, size_t ptr_length,
+ pbool escape, pbool w_one, pbool w_many,
+ size_t res_length,
+ char *min_str, char *max_str,
+ size_t *min_length, size_t *max_length);
+int my_wildcmp_8bit(CHARSET_INFO *,
+ const char *str,const char *str_end,
+ const char *wildstr,const char *wildend,
+ int escape, int w_one, int w_many);
+int my_wildcmp_bin(CHARSET_INFO *,
+ const char *str,const char *str_end,
+ const char *wildstr,const char *wildend,
+ int escape, int w_one, int w_many);
+size_t my_numchars_8bit(CHARSET_INFO *, const char *b, const char *e);
+size_t my_numcells_8bit(CHARSET_INFO *, const char *b, const char *e);
+size_t my_charpos_8bit(CHARSET_INFO *, const char *b, const char *e, size_t pos);
+size_t my_well_formed_len_8bit(CHARSET_INFO *, const char *b, const char *e,
+ size_t pos, int *error);
+uint my_mbcharlen_8bit(CHARSET_INFO *, uint c);
+extern size_t my_caseup_str_mb(CHARSET_INFO *, char *);
+extern size_t my_casedn_str_mb(CHARSET_INFO *, char *);
+extern size_t my_caseup_mb(CHARSET_INFO *, char *src, size_t srclen,
+ char *dst, size_t dstlen);
+extern size_t my_casedn_mb(CHARSET_INFO *, char *src, size_t srclen,
+ char *dst, size_t dstlen);
+extern int my_strcasecmp_mb(CHARSET_INFO * cs,const char *, const char *);
+int my_wildcmp_mb(CHARSET_INFO *,
+ const char *str,const char *str_end,
+ const char *wildstr,const char *wildend,
+ int escape, int w_one, int w_many);
+size_t my_numchars_mb(CHARSET_INFO *, const char *b, const char *e);
+size_t my_numcells_mb(CHARSET_INFO *, const char *b, const char *e);
+size_t my_charpos_mb(CHARSET_INFO *, const char *b, const char *e, size_t pos);
+size_t my_well_formed_len_mb(CHARSET_INFO *, const char *b, const char *e,
+ size_t pos, int *error);
+uint my_instr_mb(struct charset_info_st *,
+ const char *b, size_t b_length,
+ const char *s, size_t s_length,
+ my_match_t *match, uint nmatch);
+int my_wildcmp_unicode(CHARSET_INFO *cs,
+ const char *str, const char *str_end,
+ const char *wildstr, const char *wildend,
+ int escape, int w_one, int w_many,
+ MY_UNICASE_INFO **weights);
+extern my_bool my_parse_charset_xml(const char *bug, size_t len,
+ int (*add)(CHARSET_INFO *cs));
+extern char *my_strchr(CHARSET_INFO *cs, const char *str, const char *end,
+ pchar c);
+my_bool my_propagate_simple(CHARSET_INFO *cs, const uchar *str, size_t len);
+my_bool my_propagate_complex(CHARSET_INFO *cs, const uchar *str, size_t len);
+uint my_string_repertoire(CHARSET_INFO *cs, const char *str, ulong len);
+my_bool my_charset_is_ascii_based(CHARSET_INFO *cs);
+my_bool my_charset_is_8bit_pure_ascii(CHARSET_INFO *cs);
+#include <stdarg.h>
+#include <typelib.h>
+#include "my_alloc.h"
+typedef struct st_used_mem
+{
+ struct st_used_mem *next;
+ unsigned int left;
+ unsigned int size;
+} USED_MEM;
+typedef struct st_mem_root
+{
+ USED_MEM *free;
+ USED_MEM *used;
+ USED_MEM *pre_alloc;
+ size_t min_malloc;
+ size_t block_size;
+ unsigned int block_num;
+ unsigned int first_block_usage;
+ void (*error_handler)(void);
+} MEM_ROOT;
+typedef struct st_typelib {
+ unsigned int count;
+ const char *name;
+ const char **type_names;
+ unsigned int *type_lengths;
+} TYPELIB;
+extern my_ulonglong find_typeset(char *x, TYPELIB *typelib,int *error_position);
+extern int find_type_or_exit(const char *x, TYPELIB *typelib,
+ const char *option);
+extern int find_type(char *x, const TYPELIB *typelib, unsigned int full_name);
+extern void make_type(char *to,unsigned int nr,TYPELIB *typelib);
+extern const char *get_type(TYPELIB *typelib,unsigned int nr);
+extern TYPELIB *copy_typelib(MEM_ROOT *root, TYPELIB *from);
+extern TYPELIB sql_protocol_typelib;
+extern void *my_malloc(size_t Size,myf MyFlags);
+extern void *my_realloc(void *oldpoint, size_t Size, myf MyFlags);
+extern void my_no_flags_free(void *ptr);
+extern void *my_memdup(const void *from,size_t length,myf MyFlags);
+extern char *my_strdup(const char *from,myf MyFlags);
+extern char *my_strndup(const char *from, size_t length,
+ myf MyFlags);
+extern uint my_get_large_page_size(void);
+extern uchar * my_large_malloc(size_t size, myf my_flags);
+extern void my_large_free(uchar * ptr, myf my_flags);
+extern int errno;
+extern char errbuff[(2)][(256)];
+extern char *home_dir;
+extern const char *my_progname;
+extern char curr_dir[];
+extern int (*error_handler_hook)(uint my_err, const char *str,myf MyFlags);
+extern int (*fatal_error_handler_hook)(uint my_err, const char *str,
+ myf MyFlags);
+extern uint my_file_limit;
+extern ulong my_thread_stack_size;
+extern my_bool my_use_large_pages;
+extern uint my_large_page_size;
+extern CHARSET_INFO *default_charset_info;
+extern CHARSET_INFO *all_charsets[256];
+extern CHARSET_INFO compiled_charsets[];
+extern ulong my_file_opened,my_stream_opened, my_tmp_file_created;
+extern ulong my_file_total_opened;
+extern uint mysys_usage_id;
+extern my_bool my_init_done;
+extern void (*my_sigtstp_cleanup)(void),
+ (*my_sigtstp_restart)(void),
+ (*my_abort_hook)(int);
+extern int my_umask,
+ my_umask_dir,
+ my_recived_signals,
+ my_safe_to_handle_signal,
+ my_dont_interrupt;
+extern my_bool mysys_uses_curses, my_use_symdir;
+extern ulong sf_malloc_cur_memory, sf_malloc_max_memory;
+extern ulong my_default_record_cache_size;
+extern my_bool my_disable_locking, my_disable_async_io,
+ my_disable_flush_key_blocks, my_disable_symlinks;
+extern char wild_many,wild_one,wild_prefix;
+extern const char *charsets_dir;
+extern char *my_defaults_extra_file;
+extern const char *my_defaults_group_suffix;
+extern const char *my_defaults_file;
+extern my_bool timed_mutexes;
+typedef struct wild_file_pack
+{
+ uint wilds;
+ uint not_pos;
+ char * *wild;
+} WF_PACK;
+enum loglevel {
+ ERROR_LEVEL,
+ WARNING_LEVEL,
+ INFORMATION_LEVEL
+};
+enum cache_type
+{
+ TYPE_NOT_SET= 0, READ_CACHE, WRITE_CACHE,
+ SEQ_READ_APPEND ,
+ READ_FIFO, READ_NET,WRITE_NET};
+enum flush_type
+{
+ FLUSH_KEEP,
+ FLUSH_RELEASE,
+ FLUSH_IGNORE_CHANGED,
+ FLUSH_FORCE_WRITE
+};
+typedef struct st_record_cache
+{
+ File file;
+ int rc_seek,error,inited;
+ uint rc_length,read_length,reclength;
+ my_off_t rc_record_pos,end_of_file;
+ uchar *rc_buff,*rc_buff2,*rc_pos,*rc_end,*rc_request_pos;
+ enum cache_type type;
+} RECORD_CACHE;
+enum file_type
+{
+ UNOPEN = 0, FILE_BY_OPEN, FILE_BY_CREATE, STREAM_BY_FOPEN, STREAM_BY_FDOPEN,
+ FILE_BY_MKSTEMP, FILE_BY_DUP
+};
+struct st_my_file_info
+{
+ char * name;
+ enum file_type type;
+};
+extern struct st_my_file_info *my_file_info;
+typedef struct st_dynamic_array
+{
+ uchar *buffer;
+ uint elements,max_element;
+ uint alloc_increment;
+ uint size_of_element;
+} DYNAMIC_ARRAY;
+typedef struct st_my_tmpdir
+{
+ DYNAMIC_ARRAY full_list;
+ char **list;
+ uint cur, max;
+ pthread_mutex_t mutex;
+} MY_TMPDIR;
+typedef struct st_dynamic_string
+{
+ char *str;
+ size_t length,max_length,alloc_increment;
+} DYNAMIC_STRING;
+struct st_io_cache;
+typedef int (*IO_CACHE_CALLBACK)(struct st_io_cache*);
+typedef struct st_io_cache_share
+{
+ pthread_mutex_t mutex;
+ pthread_cond_t cond;
+ pthread_cond_t cond_writer;
+ my_off_t pos_in_file;
+ struct st_io_cache *source_cache;
+ uchar *buffer;
+ uchar *read_end;
+ int running_threads;
+ int total_threads;
+ int error;
+} IO_CACHE_SHARE;
+typedef struct st_io_cache
+{
+ my_off_t pos_in_file;
+ my_off_t end_of_file;
+ uchar *read_pos;
+ uchar *read_end;
+ uchar *buffer;
+ uchar *request_pos;
+ uchar *write_buffer;
+ uchar *append_read_pos;
+ uchar *write_pos;
+ uchar *write_end;
+ uchar **current_pos, **current_end;
+ pthread_mutex_t append_buffer_lock;
+ IO_CACHE_SHARE *share;
+ int (*read_function)(struct st_io_cache *,uchar *,size_t);
+ int (*write_function)(struct st_io_cache *,const uchar *,size_t);
+ enum cache_type type;
+ IO_CACHE_CALLBACK pre_read;
+ IO_CACHE_CALLBACK post_read;
+ IO_CACHE_CALLBACK pre_close;
+ ulong disk_writes;
+ void* arg;
+ char *file_name;
+ char *dir,*prefix;
+ File file;
+ int seek_not_done,error;
+ size_t buffer_length;
+ size_t read_length;
+ myf myflags;
+ my_bool alloced_buffer;
+} IO_CACHE;
+typedef int (*qsort2_cmp)(const void *, const void *, const void *);
+int my_b_copy_to_file(IO_CACHE *cache, FILE *file);
+my_off_t my_b_append_tell(IO_CACHE* info);
+my_off_t my_b_safe_tell(IO_CACHE* info);
+typedef uint32 ha_checksum;
+typedef int (*Process_option_func)(void *ctx, const char *group_name,
+ const char *option);
+#include <my_alloc.h>
+extern int my_copy(const char *from,const char *to,myf MyFlags);
+extern int my_append(const char *from,const char *to,myf MyFlags);
+extern int my_delete(const char *name,myf MyFlags);
+extern int my_getwd(char * buf,size_t size,myf MyFlags);
+extern int my_setwd(const char *dir,myf MyFlags);
+extern int my_lock(File fd,int op,my_off_t start, my_off_t length,myf MyFlags);
+extern void *my_once_alloc(size_t Size,myf MyFlags);
+extern void my_once_free(void);
+extern char *my_once_strdup(const char *src,myf myflags);
+extern void *my_once_memdup(const void *src, size_t len, myf myflags);
+extern File my_open(const char *FileName,int Flags,myf MyFlags);
+extern File my_register_filename(File fd, const char *FileName,
+ enum file_type type_of_file,
+ uint error_message_number, myf MyFlags);
+extern File my_create(const char *FileName,int CreateFlags,
+ int AccessFlags, myf MyFlags);
+extern int my_close(File Filedes,myf MyFlags);
+extern File my_dup(File file, myf MyFlags);
+extern int my_mkdir(const char *dir, int Flags, myf MyFlags);
+extern int my_readlink(char *to, const char *filename, myf MyFlags);
+extern int my_realpath(char *to, const char *filename, myf MyFlags);
+extern File my_create_with_symlink(const char *linkname, const char *filename,
+ int createflags, int access_flags,
+ myf MyFlags);
+extern int my_delete_with_symlink(const char *name, myf MyFlags);
+extern int my_rename_with_symlink(const char *from,const char *to,myf MyFlags);
+extern int my_symlink(const char *content, const char *linkname, myf MyFlags);
+extern size_t my_read(File Filedes,uchar *Buffer,size_t Count,myf MyFlags);
+extern size_t my_pread(File Filedes,uchar *Buffer,size_t Count,my_off_t offset,
+ myf MyFlags);
+extern int my_rename(const char *from,const char *to,myf MyFlags);
+extern my_off_t my_seek(File fd,my_off_t pos,int whence,myf MyFlags);
+extern my_off_t my_tell(File fd,myf MyFlags);
+extern size_t my_write(File Filedes,const uchar *Buffer,size_t Count,
+ myf MyFlags);
+extern size_t my_pwrite(File Filedes,const uchar *Buffer,size_t Count,
+ my_off_t offset,myf MyFlags);
+extern size_t my_fread(FILE *stream,uchar *Buffer,size_t Count,myf MyFlags);
+extern size_t my_fwrite(FILE *stream,const uchar *Buffer,size_t Count,
+ myf MyFlags);
+extern my_off_t my_fseek(FILE *stream,my_off_t pos,int whence,myf MyFlags);
+extern my_off_t my_ftell(FILE *stream,myf MyFlags);
+extern void *_mymalloc(size_t uSize,const char *sFile,
+ uint uLine, myf MyFlag);
+extern void *_myrealloc(void *pPtr,size_t uSize,const char *sFile,
+ uint uLine, myf MyFlag);
+extern void * my_multi_malloc (myf MyFlags, ...);
+extern void _myfree(void *pPtr,const char *sFile,uint uLine, myf MyFlag);
+extern int _sanity(const char *sFile, uint uLine);
+extern void *_my_memdup(const void *from, size_t length,
+ const char *sFile, uint uLine,myf MyFlag);
+extern char * _my_strdup(const char *from, const char *sFile, uint uLine,
+ myf MyFlag);
+extern char *_my_strndup(const char *from, size_t length,
+ const char *sFile, uint uLine,
+ myf MyFlag);
+extern void *my_memmem(const void *haystack, size_t haystacklen,
+ const void *needle, size_t needlelen);
+extern int check_if_legal_filename(const char *path);
+extern int check_if_legal_tablename(const char *path);
+extern void init_glob_errs(void);
+extern FILE *my_fopen(const char *FileName,int Flags,myf MyFlags);
+extern FILE *my_fdopen(File Filedes,const char *name, int Flags,myf MyFlags);
+extern int my_fclose(FILE *fd,myf MyFlags);
+extern int my_chsize(File fd,my_off_t newlength, int filler, myf MyFlags);
+extern int my_sync(File fd, myf my_flags);
+extern int my_sync_dir(const char *dir_name, myf my_flags);
+extern int my_sync_dir_by_file(const char *file_name, myf my_flags);
+extern int my_error (int nr,myf MyFlags, ...);
+extern int my_printf_error (uint my_err, const char *format, myf MyFlags, ...)
+ __attribute__((format(printf, 2, 4)));
+extern int my_error_register(const char **errmsgs, int first, int last);
+extern const char **my_error_unregister(int first, int last);
+extern int my_message(uint my_err, const char *str,myf MyFlags);
+extern int my_message_no_curses(uint my_err, const char *str,myf MyFlags);
+extern int my_message_curses(uint my_err, const char *str,myf MyFlags);
+extern my_bool my_init(void);
+extern void my_end(int infoflag);
+extern int my_redel(const char *from, const char *to, int MyFlags);
+extern int my_copystat(const char *from, const char *to, int MyFlags);
+extern char * my_filename(File fd);
+extern my_bool init_tmpdir(MY_TMPDIR *tmpdir, const char *pathlist);
+extern char *my_tmpdir(MY_TMPDIR *tmpdir);
+extern void free_tmpdir(MY_TMPDIR *tmpdir);
+extern void my_remember_signal(int signal_number,void (*func)(int));
+extern size_t dirname_part(char * to,const char *name, size_t *to_res_length);
+extern size_t dirname_length(const char *name);
+extern int test_if_hard_path(const char *dir_name);
+extern my_bool has_path(const char *name);
+extern char *convert_dirname(char *to, const char *from, const char *from_end);
+extern void to_unix_path(char * name);
+extern char * fn_ext(const char *name);
+extern char * fn_same(char * toname,const char *name,int flag);
+extern char * fn_format(char * to,const char *name,const char *dir,
+ const char *form, uint flag);
+extern size_t strlength(const char *str);
+extern void pack_dirname(char * to,const char *from);
+extern size_t unpack_dirname(char * to,const char *from);
+extern size_t cleanup_dirname(char * to,const char *from);
+extern size_t system_filename(char * to,const char *from);
+extern size_t unpack_filename(char * to,const char *from);
+extern char * intern_filename(char * to,const char *from);
+extern char * directory_file_name(char * dst, const char *src);
+extern int pack_filename(char * to, const char *name, size_t max_length);
+extern char * my_path(char * to,const char *progname,
+ const char *own_pathname_part);
+extern char * my_load_path(char * to, const char *path,
+ const char *own_path_prefix);
+extern int wild_compare(const char *str,const char *wildstr,
+ pbool str_is_pattern);
+extern WF_PACK *wf_comp(char * str);
+extern int wf_test(struct wild_file_pack *wf_pack,const char *name);
+extern void wf_end(struct wild_file_pack *buffer);
+extern size_t strip_sp(char * str);
+extern my_bool array_append_string_unique(const char *str,
+ const char **array, size_t size);
+extern void get_date(char * to,int timeflag,time_t use_time);
+extern void soundex(CHARSET_INFO *, char * out_pntr, char * in_pntr,
+ pbool remove_garbage);
+extern int init_record_cache(RECORD_CACHE *info,size_t cachesize,File file,
+ size_t reclength,enum cache_type type,
+ pbool use_async_io);
+extern int read_cache_record(RECORD_CACHE *info,uchar *to);
+extern int end_record_cache(RECORD_CACHE *info);
+extern int write_cache_record(RECORD_CACHE *info,my_off_t filepos,
+ const uchar *record,size_t length);
+extern int flush_write_cache(RECORD_CACHE *info);
+extern long my_clock(void);
+extern void sigtstp_handler(int signal_number);
+extern void handle_recived_signals(void);
+extern void my_set_alarm_variable(int signo);
+extern void my_string_ptr_sort(uchar *base,uint items,size_t size);
+extern void radixsort_for_str_ptr(uchar* base[], uint number_of_elements,
+ size_t size_of_element,uchar *buffer[]);
+extern void my_qsort(void *base_ptr, size_t total_elems, size_t size,
+ qsort_cmp cmp);
+extern void my_qsort2(void *base_ptr, size_t total_elems, size_t size,
+ qsort2_cmp cmp, void *cmp_argument);
+extern qsort2_cmp get_ptr_compare(size_t);
+void my_store_ptr(uchar *buff, size_t pack_length, my_off_t pos);
+my_off_t my_get_ptr(uchar *ptr, size_t pack_length);
+extern int init_io_cache(IO_CACHE *info,File file,size_t cachesize,
+ enum cache_type type,my_off_t seek_offset,
+ pbool use_async_io, myf cache_myflags);
+extern my_bool reinit_io_cache(IO_CACHE *info,enum cache_type type,
+ my_off_t seek_offset,pbool use_async_io,
+ pbool clear_cache);
+extern void setup_io_cache(IO_CACHE* info);
+extern int _my_b_read(IO_CACHE *info,uchar *Buffer,size_t Count);
+extern int _my_b_read_r(IO_CACHE *info,uchar *Buffer,size_t Count);
+extern void init_io_cache_share(IO_CACHE *read_cache, IO_CACHE_SHARE *cshare,
+ IO_CACHE *write_cache, uint num_threads);
+extern void remove_io_thread(IO_CACHE *info);
+extern int _my_b_seq_read(IO_CACHE *info,uchar *Buffer,size_t Count);
+extern int _my_b_net_read(IO_CACHE *info,uchar *Buffer,size_t Count);
+extern int _my_b_get(IO_CACHE *info);
+extern int _my_b_async_read(IO_CACHE *info,uchar *Buffer,size_t Count);
+extern int _my_b_write(IO_CACHE *info,const uchar *Buffer,size_t Count);
+extern int my_b_append(IO_CACHE *info,const uchar *Buffer,size_t Count);
+extern int my_b_safe_write(IO_CACHE *info,const uchar *Buffer,size_t Count);
+extern int my_block_write(IO_CACHE *info, const uchar *Buffer,
+ size_t Count, my_off_t pos);
+extern int my_b_flush_io_cache(IO_CACHE *info, int need_append_buffer_lock);
+extern int end_io_cache(IO_CACHE *info);
+extern size_t my_b_fill(IO_CACHE *info);
+extern void my_b_seek(IO_CACHE *info,my_off_t pos);
+extern size_t my_b_gets(IO_CACHE *info, char *to, size_t max_length);
+extern my_off_t my_b_filelength(IO_CACHE *info);
+extern size_t my_b_printf(IO_CACHE *info, const char* fmt, ...);
+extern size_t my_b_vprintf(IO_CACHE *info, const char* fmt, va_list ap);
+extern my_bool open_cached_file(IO_CACHE *cache,const char *dir,
+ const char *prefix, size_t cache_size,
+ myf cache_myflags);
+extern my_bool real_open_cached_file(IO_CACHE *cache);
+extern void close_cached_file(IO_CACHE *cache);
+File create_temp_file(char *to, const char *dir, const char *pfx,
+ int mode, myf MyFlags);
+extern my_bool init_dynamic_array2(DYNAMIC_ARRAY *array,uint element_size,
+ void *init_buffer, uint init_alloc,
+ uint alloc_increment
+ );
+extern my_bool init_dynamic_array(DYNAMIC_ARRAY *array,uint element_size,
+ uint init_alloc,uint alloc_increment
+ );
+extern my_bool insert_dynamic(DYNAMIC_ARRAY *array,uchar * element);
+extern uchar *alloc_dynamic(DYNAMIC_ARRAY *array);
+extern uchar *pop_dynamic(DYNAMIC_ARRAY*);
+extern my_bool set_dynamic(DYNAMIC_ARRAY *array,uchar * element,uint array_index);
+extern my_bool allocate_dynamic(DYNAMIC_ARRAY *array, uint max_elements);
+extern void get_dynamic(DYNAMIC_ARRAY *array,uchar * element,uint array_index);
+extern void delete_dynamic(DYNAMIC_ARRAY *array);
+extern void delete_dynamic_element(DYNAMIC_ARRAY *array, uint array_index);
+extern void freeze_size(DYNAMIC_ARRAY *array);
+extern int get_index_dynamic(DYNAMIC_ARRAY *array, uchar * element);
+extern my_bool init_dynamic_string(DYNAMIC_STRING *str, const char *init_str,
+ size_t init_alloc,size_t alloc_increment);
+extern my_bool dynstr_append(DYNAMIC_STRING *str, const char *append);
+my_bool dynstr_append_mem(DYNAMIC_STRING *str, const char *append,
+ size_t length);
+extern my_bool dynstr_append_os_quoted(DYNAMIC_STRING *str, const char *append,
+ ...);
+extern my_bool dynstr_set(DYNAMIC_STRING *str, const char *init_str);
+extern my_bool dynstr_realloc(DYNAMIC_STRING *str, size_t additional_size);
+extern my_bool dynstr_trunc(DYNAMIC_STRING *str, size_t n);
+extern void dynstr_free(DYNAMIC_STRING *str);
+extern void init_alloc_root(MEM_ROOT *mem_root, size_t block_size,
+ size_t pre_alloc_size);
+extern void *alloc_root(MEM_ROOT *mem_root, size_t Size);
+extern void *multi_alloc_root(MEM_ROOT *mem_root, ...);
+extern void free_root(MEM_ROOT *root, myf MyFLAGS);
+extern void set_prealloc_root(MEM_ROOT *root, char *ptr);
+extern void reset_root_defaults(MEM_ROOT *mem_root, size_t block_size,
+ size_t prealloc_size);
+extern char *strdup_root(MEM_ROOT *root,const char *str);
+extern char *strmake_root(MEM_ROOT *root,const char *str,size_t len);
+extern void *memdup_root(MEM_ROOT *root,const void *str, size_t len);
+extern int get_defaults_options(int argc, char **argv,
+ char **defaults, char **extra_defaults,
+ char **group_suffix);
+extern int load_defaults(const char *conf_file, const char **groups,
+ int *argc, char ***argv);
+extern int modify_defaults_file(const char *file_location, const char *option,
+ const char *option_value,
+ const char *section_name, int remove_option);
+extern int my_search_option_files(const char *conf_file, int *argc,
+ char ***argv, uint *args_used,
+ Process_option_func func, void *func_ctx);
+extern void free_defaults(char **argv);
+extern void my_print_default_files(const char *conf_file);
+extern void print_defaults(const char *conf_file, const char **groups);
+extern my_bool my_compress(uchar *, size_t *, size_t *);
+extern my_bool my_uncompress(uchar *, size_t , size_t *);
+extern uchar *my_compress_alloc(const uchar *packet, size_t *len,
+ size_t *complen);
+extern int packfrm(uchar *, size_t, uchar **, size_t *);
+extern int unpackfrm(uchar **, size_t *, const uchar *);
+extern ha_checksum my_checksum(ha_checksum crc, const uchar *mem,
+ size_t count);
+extern void my_sleep(ulong m_seconds);
+extern ulong crc32(ulong crc, const uchar *buf, uint len);
+extern uint my_set_max_open_files(uint files);
+void my_free_open_file_info(void);
+extern time_t my_time(myf flags);
+extern ulonglong my_getsystime(void);
+extern ulonglong my_micro_time();
+extern ulonglong my_micro_time_and_time(time_t *time_arg);
+time_t my_time_possible_from_micro(ulonglong microtime);
+extern my_bool my_gethwaddr(uchar *to);
+extern int my_getncpus();
+#include <sys/mman.h>
+int my_msync(int, void *, size_t, int);
+extern uint get_charset_number(const char *cs_name, uint cs_flags);
+extern uint get_collation_number(const char *name);
+extern const char *get_charset_name(uint cs_number);
+extern CHARSET_INFO *get_charset(uint cs_number, myf flags);
+extern CHARSET_INFO *get_charset_by_name(const char *cs_name, myf flags);
+extern CHARSET_INFO *get_charset_by_csname(const char *cs_name,
+ uint cs_flags, myf my_flags);
+extern my_bool resolve_charset(const char *cs_name,
+ CHARSET_INFO *default_cs,
+ CHARSET_INFO **cs);
+extern my_bool resolve_collation(const char *cl_name,
+ CHARSET_INFO *default_cl,
+ CHARSET_INFO **cl);
+extern void free_charsets(void);
+extern char *get_charsets_dir(char *buf);
+extern my_bool my_charset_same(CHARSET_INFO *cs1, CHARSET_INFO *cs2);
+extern my_bool init_compiled_charsets(myf flags);
+extern void add_compiled_collation(CHARSET_INFO *cs);
+extern size_t escape_string_for_mysql(CHARSET_INFO *charset_info,
+ char *to, size_t to_length,
+ const char *from, size_t length);
+extern size_t escape_quotes_for_mysql(CHARSET_INFO *charset_info,
+ char *to, size_t to_length,
+ const char *from, size_t length);
+extern void thd_increment_bytes_sent(ulong length);
+extern void thd_increment_bytes_received(ulong length);
+extern void thd_increment_net_big_packet_count(ulong length);
+#include <my_time.h>
+#include "my_global.h"
+#include "mysql_time.h"
+enum enum_mysql_timestamp_type
+{
+ MYSQL_TIMESTAMP_NONE= -2, MYSQL_TIMESTAMP_ERROR= -1,
+ MYSQL_TIMESTAMP_DATE= 0, MYSQL_TIMESTAMP_DATETIME= 1, MYSQL_TIMESTAMP_TIME= 2
+};
+typedef struct st_mysql_time
+{
+ unsigned int year, month, day, hour, minute, second;
+ unsigned long second_part;
+ my_bool neg;
+ enum enum_mysql_timestamp_type time_type;
+} MYSQL_TIME;
+extern ulonglong log_10_int[20];
+extern uchar days_in_month[];
+typedef long my_time_t;
+my_bool check_date(const MYSQL_TIME *ltime, my_bool not_zero_date,
+ ulong flags, int *was_cut);
+enum enum_mysql_timestamp_type
+str_to_datetime(const char *str, uint length, MYSQL_TIME *l_time,
+ uint flags, int *was_cut);
+longlong number_to_datetime(longlong nr, MYSQL_TIME *time_res,
+ uint flags, int *was_cut);
+ulonglong TIME_to_ulonglong_datetime(const MYSQL_TIME *);
+ulonglong TIME_to_ulonglong_date(const MYSQL_TIME *);
+ulonglong TIME_to_ulonglong_time(const MYSQL_TIME *);
+ulonglong TIME_to_ulonglong(const MYSQL_TIME *);
+my_bool str_to_time(const char *str,uint length, MYSQL_TIME *l_time,
+ int *warning);
+int check_time_range(struct st_mysql_time *, int *warning);
+long calc_daynr(uint year,uint month,uint day);
+uint calc_days_in_year(uint year);
+uint year_2000_handling(uint year);
+void my_init_time(void);
+static inline my_bool validate_timestamp_range(const MYSQL_TIME *t)
+{
+ if ((t->year > 2038 || t->year < (1900 + 70 - 1)) ||
+ (t->year == 2038 && (t->month > 1 || t->day > 19)) ||
+ (t->year == (1900 + 70 - 1) && (t->month < 12 || t->day < 31)))
+ return (0);
+ return (1);
+}
+my_time_t
+my_system_gmt_sec(const MYSQL_TIME *t, long *my_timezone,
+ my_bool *in_dst_time_gap);
+void set_zero_time(MYSQL_TIME *tm, enum enum_mysql_timestamp_type time_type);
+int my_time_to_str(const MYSQL_TIME *l_time, char *to);
+int my_date_to_str(const MYSQL_TIME *l_time, char *to);
+int my_datetime_to_str(const MYSQL_TIME *l_time, char *to);
+int my_TIME_to_str(const MYSQL_TIME *l_time, char *to);
+enum interval_type
+{
+ INTERVAL_YEAR, INTERVAL_QUARTER, INTERVAL_MONTH, INTERVAL_WEEK, INTERVAL_DAY,
+ INTERVAL_HOUR, INTERVAL_MINUTE, INTERVAL_SECOND, INTERVAL_MICROSECOND,
+ INTERVAL_YEAR_MONTH, INTERVAL_DAY_HOUR, INTERVAL_DAY_MINUTE,
+ INTERVAL_DAY_SECOND, INTERVAL_HOUR_MINUTE, INTERVAL_HOUR_SECOND,
+ INTERVAL_MINUTE_SECOND, INTERVAL_DAY_MICROSECOND, INTERVAL_HOUR_MICROSECOND,
+ INTERVAL_MINUTE_MICROSECOND, INTERVAL_SECOND_MICROSECOND, INTERVAL_LAST
+};
+#include <m_string.h>
+#include <strings.h>
+#include <string.h>
+#include <stdarg.h>
+#include <strings.h>
+#include <memory.h>
+extern void *(*my_str_malloc)(size_t);
+extern void (*my_str_free)(void *);
+extern char *stpcpy(char *, const char *);
+extern char _dig_vec_upper[];
+extern char _dig_vec_lower[];
+extern const double log_10[309];
+extern void bmove512(uchar *dst,const uchar *src,size_t len);
+extern void bmove_upp(uchar *dst,const uchar *src,size_t len);
+extern void bchange(uchar *dst,size_t old_len,const uchar *src,
+ size_t new_len,size_t tot_len);
+extern void strappend(char *s,size_t len,pchar fill);
+extern char *strend(const char *s);
+extern char *strcend(const char *, pchar);
+extern char *strfield(char *src,int fields,int chars,int blanks,
+ int tabch);
+extern char *strfill(char * s,size_t len,pchar fill);
+extern size_t strinstr(const char *str,const char *search);
+extern size_t r_strinstr(const char *str, size_t from, const char *search);
+extern char *strkey(char *dst,char *head,char *tail,char *flags);
+extern char *strmake(char *dst,const char *src,size_t length);
+extern char *strnmov(char *dst,const char *src,size_t n);
+extern char *strsuff(const char *src,const char *suffix);
+extern char *strcont(const char *src,const char *set);
+extern char *strxcat (char *dst,const char *src, ...);
+extern char *strxmov (char *dst,const char *src, ...);
+extern char *strxcpy (char *dst,const char *src, ...);
+extern char *strxncat (char *dst,size_t len, const char *src, ...);
+extern char *strxnmov (char *dst,size_t len, const char *src, ...);
+extern char *strxncpy (char *dst,size_t len, const char *src, ...);
+extern int is_prefix(const char *, const char *);
+double my_strtod(const char *str, char **end, int *error);
+double my_atof(const char *nptr);
+extern char *llstr(longlong value,char *buff);
+extern char *ullstr(longlong value,char *buff);
+extern char *int2str(long val, char *dst, int radix, int upcase);
+extern char *int10_to_str(long val,char *dst,int radix);
+extern char *str2int(const char *src,int radix,long lower,long upper,
+ long *val);
+longlong my_strtoll10(const char *nptr, char **endptr, int *error);
+extern char *longlong2str(longlong val,char *dst,int radix);
+extern char *longlong10_to_str(longlong val,char *dst,int radix);
+extern size_t my_vsnprintf(char *str, size_t n,
+ const char *format, va_list ap);
+extern size_t my_snprintf(char *to, size_t n, const char *fmt, ...)
+ __attribute__((format(printf, 3, 4)));
+struct st_mysql_lex_string
+{
+ char *str;
+ size_t length;
+};
+typedef struct st_mysql_lex_string LEX_STRING;
+#include <hash.h>
+typedef uchar *(*hash_get_key)(const uchar *,size_t*,my_bool);
+typedef void (*hash_free_key)(void *);
+typedef struct st_hash {
+ size_t key_offset,key_length;
+ size_t blength;
+ ulong records;
+ uint flags;
+ DYNAMIC_ARRAY array;
+ hash_get_key get_key;
+ void (*free)(void *);
+ CHARSET_INFO *charset;
+} HASH;
+typedef uint HASH_SEARCH_STATE;
+my_bool _hash_init(HASH *hash, uint growth_size,CHARSET_INFO *charset,
+ ulong default_array_elements, size_t key_offset,
+ size_t key_length, hash_get_key get_key,
+ void (*free_element)(void*), uint flags );
+void hash_free(HASH *tree);
+void my_hash_reset(HASH *hash);
+uchar *hash_element(HASH *hash,ulong idx);
+uchar *hash_search(const HASH *info, const uchar *key, size_t length);
+uchar *hash_first(const HASH *info, const uchar *key, size_t length,
+ HASH_SEARCH_STATE *state);
+uchar *hash_next(const HASH *info, const uchar *key, size_t length,
+ HASH_SEARCH_STATE *state);
+my_bool my_hash_insert(HASH *info,const uchar *data);
+my_bool hash_delete(HASH *hash,uchar *record);
+my_bool hash_update(HASH *hash,uchar *record,uchar *old_key,size_t old_key_length);
+void hash_replace(HASH *hash, HASH_SEARCH_STATE *state, uchar *new_row);
+my_bool hash_check(HASH *hash);
+#include <signal.h>
+#include <thr_lock.h>
+#include <my_pthread.h>
+#include <my_list.h>
+typedef struct st_list {
+ struct st_list *prev,*next;
+ void *data;
+} LIST;
+typedef int (*list_walk_action)(void *,void *);
+extern LIST *list_add(LIST *root,LIST *element);
+extern LIST *list_delete(LIST *root,LIST *element);
+extern LIST *list_cons(void *data,LIST *root);
+extern LIST *list_reverse(LIST *root);
+extern void list_free(LIST *root,unsigned int free_data);
+extern unsigned int list_length(LIST *);
+extern int list_walk(LIST *,list_walk_action action,unsigned char * argument);
+struct st_thr_lock;
+extern ulong locks_immediate,locks_waited ;
+enum thr_lock_type { TL_IGNORE=-1,
+ TL_UNLOCK,
+ TL_READ,
+ TL_READ_WITH_SHARED_LOCKS,
+ TL_READ_HIGH_PRIORITY,
+ TL_READ_NO_INSERT,
+ TL_WRITE_ALLOW_WRITE,
+ TL_WRITE_ALLOW_READ,
+ TL_WRITE_CONCURRENT_INSERT,
+ TL_WRITE_DELAYED,
+ TL_WRITE_DEFAULT,
+ TL_WRITE_LOW_PRIORITY,
+ TL_WRITE,
+ TL_WRITE_ONLY};
+enum enum_thr_lock_result { THR_LOCK_SUCCESS= 0, THR_LOCK_ABORTED= 1,
+ THR_LOCK_WAIT_TIMEOUT= 2, THR_LOCK_DEADLOCK= 3 };
+extern ulong max_write_lock_count;
+extern ulong table_lock_wait_timeout;
+extern my_bool thr_lock_inited;
+extern enum thr_lock_type thr_upgraded_concurrent_insert_lock;
+typedef struct st_thr_lock_info
+{
+ pthread_t thread;
+ my_thread_id thread_id;
+ ulong n_cursors;
+} THR_LOCK_INFO;
+typedef struct st_thr_lock_owner
+{
+ THR_LOCK_INFO *info;
+} THR_LOCK_OWNER;
+typedef struct st_thr_lock_data {
+ THR_LOCK_OWNER *owner;
+ struct st_thr_lock_data *next,**prev;
+ struct st_thr_lock *lock;
+ pthread_cond_t *cond;
+ enum thr_lock_type type;
+ void *status_param;
+ void *debug_print_param;
+} THR_LOCK_DATA;
+struct st_lock_list {
+ THR_LOCK_DATA *data,**last;
+};
+typedef struct st_thr_lock {
+ LIST list;
+ pthread_mutex_t mutex;
+ struct st_lock_list read_wait;
+ struct st_lock_list read;
+ struct st_lock_list write_wait;
+ struct st_lock_list write;
+ ulong write_lock_count;
+ uint read_no_write_count;
+ void (*get_status)(void*, int);
+ void (*copy_status)(void*,void*);
+ void (*update_status)(void*);
+ void (*restore_status)(void*);
+ my_bool (*check_status)(void *);
+} THR_LOCK;
+extern LIST *thr_lock_thread_list;
+extern pthread_mutex_t THR_LOCK_lock;
+my_bool init_thr_lock(void);
+void thr_lock_info_init(THR_LOCK_INFO *info);
+void thr_lock_init(THR_LOCK *lock);
+void thr_lock_delete(THR_LOCK *lock);
+void thr_lock_data_init(THR_LOCK *lock,THR_LOCK_DATA *data,
+ void *status_param);
+enum enum_thr_lock_result thr_lock(THR_LOCK_DATA *data,
+ THR_LOCK_OWNER *owner,
+ enum thr_lock_type lock_type);
+void thr_unlock(THR_LOCK_DATA *data);
+enum enum_thr_lock_result thr_multi_lock(THR_LOCK_DATA **data,
+ uint count, THR_LOCK_OWNER *owner);
+void thr_multi_unlock(THR_LOCK_DATA **data,uint count);
+void thr_abort_locks(THR_LOCK *lock, my_bool upgrade_lock);
+my_bool thr_abort_locks_for_thread(THR_LOCK *lock, my_thread_id thread);
+void thr_print_locks(void);
+my_bool thr_upgrade_write_delay_lock(THR_LOCK_DATA *data);
+void thr_downgrade_write_lock(THR_LOCK_DATA *data,
+ enum thr_lock_type new_lock_type);
+my_bool thr_reschedule_write_lock(THR_LOCK_DATA *data);
+#include <my_base.h>
+#include <my_global.h>
+#include <my_dir.h>
+#include <sys/stat.h>
+typedef struct fileinfo
+{
+ char *name;
+ struct stat *mystat;
+} FILEINFO;
+typedef struct st_my_dir
+{
+ struct fileinfo *dir_entry;
+ uint number_off_files;
+} MY_DIR;
+extern MY_DIR *my_dir(const char *path,myf MyFlags);
+extern void my_dirend(MY_DIR *buffer);
+extern struct stat *my_stat(const char *path, struct stat *stat_area, myf my_flags);
+extern int my_fstat(int filenr, struct stat *stat_area, myf MyFlags);
+#include <my_sys.h>
+#include <m_string.h>
+#include <errno.h>
+#include <my_list.h>
+enum ha_rkey_function {
+ HA_READ_KEY_EXACT,
+ HA_READ_KEY_OR_NEXT,
+ HA_READ_KEY_OR_PREV,
+ HA_READ_AFTER_KEY,
+ HA_READ_BEFORE_KEY,
+ HA_READ_PREFIX,
+ HA_READ_PREFIX_LAST,
+ HA_READ_PREFIX_LAST_OR_PREV,
+ HA_READ_MBR_CONTAIN,
+ HA_READ_MBR_INTERSECT,
+ HA_READ_MBR_WITHIN,
+ HA_READ_MBR_DISJOINT,
+ HA_READ_MBR_EQUAL
+};
+enum ha_key_alg {
+ HA_KEY_ALG_UNDEF= 0,
+ HA_KEY_ALG_BTREE= 1,
+ HA_KEY_ALG_RTREE= 2,
+ HA_KEY_ALG_HASH= 3,
+ HA_KEY_ALG_FULLTEXT= 4
+};
+enum ha_storage_media {
+ HA_SM_DEFAULT= 0,
+ HA_SM_DISK= 1,
+ HA_SM_MEMORY= 2
+};
+enum ha_extra_function {
+ HA_EXTRA_NORMAL=0,
+ HA_EXTRA_QUICK=1,
+ HA_EXTRA_NOT_USED=2,
+ HA_EXTRA_CACHE=3,
+ HA_EXTRA_NO_CACHE=4,
+ HA_EXTRA_NO_READCHECK=5,
+ HA_EXTRA_READCHECK=6,
+ HA_EXTRA_KEYREAD=7,
+ HA_EXTRA_NO_KEYREAD=8,
+ HA_EXTRA_NO_USER_CHANGE=9,
+ HA_EXTRA_KEY_CACHE=10,
+ HA_EXTRA_NO_KEY_CACHE=11,
+ HA_EXTRA_WAIT_LOCK=12,
+ HA_EXTRA_NO_WAIT_LOCK=13,
+ HA_EXTRA_WRITE_CACHE=14,
+ HA_EXTRA_FLUSH_CACHE=15,
+ HA_EXTRA_NO_KEYS=16,
+ HA_EXTRA_KEYREAD_CHANGE_POS=17,
+ HA_EXTRA_REMEMBER_POS=18,
+ HA_EXTRA_RESTORE_POS=19,
+ HA_EXTRA_REINIT_CACHE=20,
+ HA_EXTRA_FORCE_REOPEN=21,
+ HA_EXTRA_FLUSH,
+ HA_EXTRA_NO_ROWS,
+ HA_EXTRA_RESET_STATE,
+ HA_EXTRA_IGNORE_DUP_KEY,
+ HA_EXTRA_NO_IGNORE_DUP_KEY,
+ HA_EXTRA_PREPARE_FOR_DROP,
+ HA_EXTRA_PREPARE_FOR_UPDATE,
+ HA_EXTRA_PRELOAD_BUFFER_SIZE,
+ HA_EXTRA_CHANGE_KEY_TO_UNIQUE,
+ HA_EXTRA_CHANGE_KEY_TO_DUP,
+ HA_EXTRA_KEYREAD_PRESERVE_FIELDS,
+ HA_EXTRA_MMAP,
+ HA_EXTRA_IGNORE_NO_KEY,
+ HA_EXTRA_NO_IGNORE_NO_KEY,
+ HA_EXTRA_MARK_AS_LOG_TABLE,
+ HA_EXTRA_WRITE_CAN_REPLACE,
+ HA_EXTRA_WRITE_CANNOT_REPLACE,
+ HA_EXTRA_DELETE_CANNOT_BATCH,
+ HA_EXTRA_UPDATE_CANNOT_BATCH,
+ HA_EXTRA_INSERT_WITH_UPDATE,
+ HA_EXTRA_PREPARE_FOR_RENAME,
+ HA_EXTRA_ATTACH_CHILDREN,
+ HA_EXTRA_DETACH_CHILDREN
+};
+enum ha_panic_function {
+ HA_PANIC_CLOSE,
+ HA_PANIC_WRITE,
+ HA_PANIC_READ
+};
+enum ha_base_keytype {
+ HA_KEYTYPE_END=0,
+ HA_KEYTYPE_TEXT=1,
+ HA_KEYTYPE_BINARY=2,
+ HA_KEYTYPE_SHORT_INT=3,
+ HA_KEYTYPE_LONG_INT=4,
+ HA_KEYTYPE_FLOAT=5,
+ HA_KEYTYPE_DOUBLE=6,
+ HA_KEYTYPE_NUM=7,
+ HA_KEYTYPE_USHORT_INT=8,
+ HA_KEYTYPE_ULONG_INT=9,
+ HA_KEYTYPE_LONGLONG=10,
+ HA_KEYTYPE_ULONGLONG=11,
+ HA_KEYTYPE_INT24=12,
+ HA_KEYTYPE_UINT24=13,
+ HA_KEYTYPE_INT8=14,
+ HA_KEYTYPE_VARTEXT1=15,
+ HA_KEYTYPE_VARBINARY1=16,
+ HA_KEYTYPE_VARTEXT2=17,
+ HA_KEYTYPE_VARBINARY2=18,
+ HA_KEYTYPE_BIT=19
+};
+typedef ulong key_part_map;
+enum en_fieldtype {
+ FIELD_LAST=-1,FIELD_NORMAL,FIELD_SKIP_ENDSPACE,FIELD_SKIP_PRESPACE,
+ FIELD_SKIP_ZERO,FIELD_BLOB,FIELD_CONSTANT,FIELD_INTERVALL,FIELD_ZERO,
+ FIELD_VARCHAR,FIELD_CHECK,
+ FIELD_enum_val_count
+};
+enum data_file_type {
+ STATIC_RECORD, DYNAMIC_RECORD, COMPRESSED_RECORD, BLOCK_RECORD
+};
+typedef struct st_key_range
+{
+ const uchar *key;
+ uint length;
+ key_part_map keypart_map;
+ enum ha_rkey_function flag;
+} key_range;
+typedef struct st_key_multi_range
+{
+ key_range start_key;
+ key_range end_key;
+ char *ptr;
+ uint range_flag;
+} KEY_MULTI_RANGE;
+typedef my_off_t ha_rows;
+typedef void (* invalidator_by_filename)(const char * filename);
+#include <queues.h>
+typedef struct st_queue {
+ uchar **root;
+ void *first_cmp_arg;
+ uint elements;
+ uint max_elements;
+ uint offset_to_key;
+ int max_at_top;
+ int (*compare)(void *, uchar *,uchar *);
+ uint auto_extent;
+} QUEUE;
+typedef int (*queue_compare)(void *,uchar *, uchar *);
+int init_queue(QUEUE *queue,uint max_elements,uint offset_to_key,
+ pbool max_at_top, queue_compare compare,
+ void *first_cmp_arg);
+int init_queue_ex(QUEUE *queue,uint max_elements,uint offset_to_key,
+ pbool max_at_top, queue_compare compare,
+ void *first_cmp_arg, uint auto_extent);
+int reinit_queue(QUEUE *queue,uint max_elements,uint offset_to_key,
+ pbool max_at_top, queue_compare compare,
+ void *first_cmp_arg);
+int resize_queue(QUEUE *queue, uint max_elements);
+void delete_queue(QUEUE *queue);
+void queue_insert(QUEUE *queue,uchar *element);
+int queue_insert_safe(QUEUE *queue, uchar *element);
+uchar *queue_remove(QUEUE *queue,uint idx);
+void _downheap(QUEUE *queue,uint idx);
+void queue_fix(QUEUE *queue);
+#include "sql_bitmap.h"
+#include <my_bitmap.h>
+#include <m_string.h>
+typedef uint32 my_bitmap_map;
+typedef struct st_bitmap
+{
+ my_bitmap_map *bitmap;
+ uint n_bits;
+ my_bitmap_map last_word_mask;
+ my_bitmap_map *last_word_ptr;
+ pthread_mutex_t *mutex;
+} MY_BITMAP;
+extern void create_last_word_mask(MY_BITMAP *map);
+extern my_bool bitmap_init(MY_BITMAP *map, my_bitmap_map *buf, uint n_bits,
+ my_bool thread_safe);
+extern my_bool bitmap_is_clear_all(const MY_BITMAP *map);
+extern my_bool bitmap_is_prefix(const MY_BITMAP *map, uint prefix_size);
+extern my_bool bitmap_is_set_all(const MY_BITMAP *map);
+extern my_bool bitmap_is_subset(const MY_BITMAP *map1, const MY_BITMAP *map2);
+extern my_bool bitmap_is_overlapping(const MY_BITMAP *map1,
+ const MY_BITMAP *map2);
+extern my_bool bitmap_test_and_set(MY_BITMAP *map, uint bitmap_bit);
+extern my_bool bitmap_test_and_clear(MY_BITMAP *map, uint bitmap_bit);
+extern my_bool bitmap_fast_test_and_set(MY_BITMAP *map, uint bitmap_bit);
+extern uint bitmap_set_next(MY_BITMAP *map);
+extern uint bitmap_get_first(const MY_BITMAP *map);
+extern uint bitmap_get_first_set(const MY_BITMAP *map);
+extern uint bitmap_bits_set(const MY_BITMAP *map);
+extern void bitmap_free(MY_BITMAP *map);
+extern void bitmap_set_above(MY_BITMAP *map, uint from_byte, uint use_bit);
+extern void bitmap_set_prefix(MY_BITMAP *map, uint prefix_size);
+extern void bitmap_intersect(MY_BITMAP *map, const MY_BITMAP *map2);
+extern void bitmap_subtract(MY_BITMAP *map, const MY_BITMAP *map2);
+extern void bitmap_union(MY_BITMAP *map, const MY_BITMAP *map2);
+extern void bitmap_xor(MY_BITMAP *map, const MY_BITMAP *map2);
+extern void bitmap_invert(MY_BITMAP *map);
+extern void bitmap_copy(MY_BITMAP *map, const MY_BITMAP *map2);
+extern uint bitmap_lock_set_next(MY_BITMAP *map);
+extern void bitmap_lock_clear_bit(MY_BITMAP *map, uint bitmap_bit);
+static inline void
+bitmap_set_bit(MY_BITMAP *map,uint bit)
+{
+ assert(bit < (map)->n_bits);
+ (((uchar*)(map)->bitmap)[(bit) / 8] |= (1 << ((bit) & 7)));
+}
+static inline void
+bitmap_flip_bit(MY_BITMAP *map,uint bit)
+{
+ assert(bit < (map)->n_bits);
+ (((uchar*)(map)->bitmap)[(bit) / 8] ^= (1 << ((bit) & 7)));
+}
+static inline void
+bitmap_clear_bit(MY_BITMAP *map,uint bit)
+{
+ assert(bit < (map)->n_bits);
+ (((uchar*)(map)->bitmap)[(bit) / 8] &= ~ (1 << ((bit) & 7)));
+}
+static inline uint
+bitmap_is_set(const MY_BITMAP *map,uint bit)
+{
+ assert(bit < (map)->n_bits);
+ return (uint) (((uchar*)(map)->bitmap)[(bit) / 8] & (1 << ((bit) & 7)));
+}
+static inline my_bool bitmap_cmp(const MY_BITMAP *map1, const MY_BITMAP *map2)
+{
+ *(map1)->last_word_ptr|= (map1)->last_word_mask;
+ *(map2)->last_word_ptr|= (map2)->last_word_mask;
+ return memcmp((map1)->bitmap, (map2)->bitmap, 4*((((map1))->n_bits + 31)/32))==0;
+}
+template <uint default_width> class Bitmap
+{
+ MY_BITMAP map;
+ uint32 buffer[(default_width+31)/32];
+public:
+ Bitmap() { init(); }
+ Bitmap(const Bitmap& from) { *this=from; }
+ explicit Bitmap(uint prefix_to_set) { init(prefix_to_set); }
+ void init() { bitmap_init(&map, buffer, default_width, 0); }
+ void init(uint prefix_to_set) { init(); set_prefix(prefix_to_set); }
+ uint length() const { return default_width; }
+ Bitmap& operator=(const Bitmap& map2)
+ {
+ init();
+ memcpy(buffer, map2.buffer, sizeof(buffer));
+ return *this;
+ }
+ void set_bit(uint n) { bitmap_set_bit(&map, n); }
+ void clear_bit(uint n) { bitmap_clear_bit(&map, n); }
+ void set_prefix(uint n) { bitmap_set_prefix(&map, n); }
+ void set_all() { (memset((&map)->bitmap, 0xFF, 4*((((&map))->n_bits + 31)/32))); }
+ void clear_all() { { memset((&map)->bitmap, 0, 4*((((&map))->n_bits + 31)/32)); }; }
+ void intersect(Bitmap& map2) { bitmap_intersect(&map, &map2.map); }
+ void intersect(ulonglong map2buff)
+ {
+ MY_BITMAP map2;
+ bitmap_init(&map2, (uint32 *)&map2buff, sizeof(ulonglong)*8, 0);
+ bitmap_intersect(&map, &map2);
+ }
+ void intersect_extended(ulonglong map2buff)
+ {
+ intersect(map2buff);
+ if (map.n_bits > sizeof(ulonglong) * 8)
+ bitmap_set_above(&map, sizeof(ulonglong),
+ ((map2buff & (1LL << (sizeof(ulonglong) * 8 - 1))) ? 1 : 0));
+ }
+ void subtract(Bitmap& map2) { bitmap_subtract(&map, &map2.map); }
+ void merge(Bitmap& map2) { bitmap_union(&map, &map2.map); }
+ my_bool is_set(uint n) const { return bitmap_is_set(&map, n); }
+ my_bool is_prefix(uint n) const { return bitmap_is_prefix(&map, n); }
+ my_bool is_clear_all() const { return bitmap_is_clear_all(&map); }
+ my_bool is_set_all() const { return bitmap_is_set_all(&map); }
+ my_bool is_subset(const Bitmap& map2) const { return bitmap_is_subset(&map, &map2.map); }
+ my_bool is_overlapping(const Bitmap& map2) const { return bitmap_is_overlapping(&map, &map2.map); }
+ my_bool operator==(const Bitmap& map2) const { return bitmap_cmp(&map, &map2.map); }
+ char *print(char *buf) const
+ {
+ char *s=buf;
+ const uchar *e=(uchar *)buffer, *b=e+sizeof(buffer)-1;
+ while (!*b && b>e)
+ b--;
+ if ((*s=_dig_vec_upper[*b >> 4]) != '0')
+ s++;
+ *s++=_dig_vec_upper[*b & 15];
+ while (--b>=e)
+ {
+ *s++=_dig_vec_upper[*b >> 4];
+ *s++=_dig_vec_upper[*b & 15];
+ }
+ *s=0;
+ return buf;
+ }
+ ulonglong to_ulonglong() const
+ {
+ if (sizeof(buffer) >= 8)
+ return (*((ulonglong *) (buffer)));
+ assert(sizeof(buffer) >= 4);
+ return (ulonglong) (*((uint32 *) (buffer)));
+ }
+};
+template <> class Bitmap<64>
+{
+ ulonglong map;
+public:
+ Bitmap<64>() { }
+ explicit Bitmap<64>(uint prefix_to_set) { set_prefix(prefix_to_set); }
+ void init() { }
+ void init(uint prefix_to_set) { set_prefix(prefix_to_set); }
+ uint length() const { return 64; }
+ void set_bit(uint n) { map|= ((ulonglong)1) << n; }
+ void clear_bit(uint n) { map&= ~(((ulonglong)1) << n); }
+ void set_prefix(uint n)
+ {
+ if (n >= length())
+ set_all();
+ else
+ map= (((ulonglong)1) << n)-1;
+ }
+ void set_all() { map=~(ulonglong)0; }
+ void clear_all() { map=(ulonglong)0; }
+ void intersect(Bitmap<64>& map2) { map&= map2.map; }
+ void intersect(ulonglong map2) { map&= map2; }
+ void intersect_extended(ulonglong map2) { map&= map2; }
+ void subtract(Bitmap<64>& map2) { map&= ~map2.map; }
+ void merge(Bitmap<64>& map2) { map|= map2.map; }
+ my_bool is_set(uint n) const { return ((map & (((ulonglong)1) << n)) ? 1 : 0); }
+ my_bool is_prefix(uint n) const { return map == (((ulonglong)1) << n)-1; }
+ my_bool is_clear_all() const { return map == (ulonglong)0; }
+ my_bool is_set_all() const { return map == ~(ulonglong)0; }
+ my_bool is_subset(const Bitmap<64>& map2) const { return !(map & ~map2.map); }
+ my_bool is_overlapping(const Bitmap<64>& map2) const { return (map & map2.map)!= 0; }
+ my_bool operator==(const Bitmap<64>& map2) const { return map == map2.map; }
+ char *print(char *buf) const { longlong2str(map,buf,16); return buf; }
+ ulonglong to_ulonglong() const { return map; }
+};
+#include "sql_array.h"
+#include <my_sys.h>
+template <class Elem> class Dynamic_array
+{
+ DYNAMIC_ARRAY array;
+public:
+ Dynamic_array(uint prealloc=16, uint increment=16)
+ {
+ init_dynamic_array2(&array,sizeof(Elem),NULL,prealloc,increment );
+ }
+ Elem& at(int idx)
+ {
+ return *(((Elem*)array.buffer) + idx);
+ }
+ Elem *front()
+ {
+ return (Elem*)array.buffer;
+ }
+ Elem *back()
+ {
+ return ((Elem*)array.buffer) + array.elements;
+ }
+ In_C_you_should_use_my_bool_instead() append(Elem &el)
+ {
+ return (insert_dynamic(&array, (uchar*)&el));
+ }
+ int elements()
+ {
+ return array.elements;
+ }
+ ~Dynamic_array()
+ {
+ delete_dynamic(&array);
+ }
+ typedef int (*CMP_FUNC)(const Elem *el1, const Elem *el2);
+ void sort(CMP_FUNC cmp_func)
+ {
+ my_qsort(array.buffer, array.elements, sizeof(Elem), (qsort_cmp)cmp_func);
+ }
+};
+#include "sql_plugin.h"
+class sys_var;
+#include <mysql/plugin.h>
+typedef struct st_mysql_lex_string MYSQL_LEX_STRING;
+struct st_mysql_xid {
+ long formatID;
+ long gtrid_length;
+ long bqual_length;
+ char data[128];
+};
+typedef struct st_mysql_xid MYSQL_XID;
+enum enum_mysql_show_type
+{
+ SHOW_UNDEF, SHOW_BOOL, SHOW_INT, SHOW_LONG,
+ SHOW_LONGLONG, SHOW_CHAR, SHOW_CHAR_PTR,
+ SHOW_ARRAY, SHOW_FUNC, SHOW_KEY_CACHE_LONG, SHOW_KEY_CACHE_LONGLONG, SHOW_LONG_STATUS, SHOW_DOUBLE_STATUS, SHOW_HAVE, SHOW_MY_BOOL, SHOW_HA_ROWS, SHOW_SYS, SHOW_LONG_NOFLUSH, SHOW_LONGLONG_STATUS, SHOW_DOUBLE
+};
+struct st_mysql_show_var {
+ const char *name;
+ char *value;
+ enum enum_mysql_show_type type;
+};
+typedef int (*mysql_show_var_func)(void*, struct st_mysql_show_var*, char *);
+struct st_mysql_sys_var;
+struct st_mysql_value;
+typedef int (*mysql_var_check_func)(void* thd,
+ struct st_mysql_sys_var *var,
+ void *save, struct st_mysql_value *value);
+typedef void (*mysql_var_update_func)(void* thd,
+ struct st_mysql_sys_var *var,
+ void *var_ptr, const void *save);
+struct st_mysql_plugin
+{
+ int type;
+ void *info;
+ const char *name;
+ const char *author;
+ const char *descr;
+ int license;
+ int (*init)(void *);
+ int (*deinit)(void *);
+ unsigned int version;
+ struct st_mysql_show_var *status_vars;
+ struct st_mysql_sys_var **system_vars;
+ void * __reserved1;
+};
+enum enum_ftparser_mode
+{
+ MYSQL_FTPARSER_SIMPLE_MODE= 0,
+ MYSQL_FTPARSER_WITH_STOPWORDS= 1,
+ MYSQL_FTPARSER_FULL_BOOLEAN_INFO= 2
+};
+enum enum_ft_token_type
+{
+ FT_TOKEN_EOF= 0,
+ FT_TOKEN_WORD= 1,
+ FT_TOKEN_LEFT_PAREN= 2,
+ FT_TOKEN_RIGHT_PAREN= 3,
+ FT_TOKEN_STOPWORD= 4
+};
+typedef struct st_mysql_ftparser_boolean_info
+{
+ enum enum_ft_token_type type;
+ int yesno;
+ int weight_adjust;
+ char wasign;
+ char trunc;
+ char prev;
+ char *quot;
+} MYSQL_FTPARSER_BOOLEAN_INFO;
+typedef struct st_mysql_ftparser_param
+{
+ int (*mysql_parse)(struct st_mysql_ftparser_param *,
+ char *doc, int doc_len);
+ int (*mysql_add_word)(struct st_mysql_ftparser_param *,
+ char *word, int word_len,
+ MYSQL_FTPARSER_BOOLEAN_INFO *boolean_info);
+ void *ftparser_state;
+ void *mysql_ftparam;
+ struct charset_info_st *cs;
+ char *doc;
+ int length;
+ int flags;
+ enum enum_ftparser_mode mode;
+} MYSQL_FTPARSER_PARAM;
+struct st_mysql_ftparser
+{
+ int interface_version;
+ int (*parse)(MYSQL_FTPARSER_PARAM *param);
+ int (*init)(MYSQL_FTPARSER_PARAM *param);
+ int (*deinit)(MYSQL_FTPARSER_PARAM *param);
+};
+struct st_mysql_storage_engine
+{
+ int interface_version;
+};
+struct handlerton;
+struct st_mysql_daemon
+{
+ int interface_version;
+};
+struct st_mysql_information_schema
+{
+ int interface_version;
+};
+struct st_mysql_value
+{
+ int (*value_type)(struct st_mysql_value *);
+ const char *(*val_str)(struct st_mysql_value *, char *buffer, int *length);
+ int (*val_real)(struct st_mysql_value *, double *realbuf);
+ int (*val_int)(struct st_mysql_value *, long long *intbuf);
+};
+int thd_in_lock_tables(const void* thd);
+int thd_tablespace_op(const void* thd);
+long long thd_test_options(const void* thd, long long test_options);
+int thd_sql_command(const void* thd);
+const char *thd_proc_info(void* thd, const char *info);
+void **thd_ha_data(const void* thd, const struct handlerton *hton);
+int thd_tx_isolation(const void* thd);
+char *thd_security_context(void* thd, char *buffer, unsigned int length,
+ unsigned int max_query_len);
+void thd_inc_row_count(void* thd);
+int mysql_tmpfile(const char *prefix);
+int thd_killed(const void* thd);
+unsigned long thd_get_thread_id(const void* thd);
+void *thd_alloc(void* thd, unsigned int size);
+void *thd_calloc(void* thd, unsigned int size);
+char *thd_strdup(void* thd, const char *str);
+char *thd_strmake(void* thd, const char *str, unsigned int size);
+void *thd_memdup(void* thd, const void* str, unsigned int size);
+MYSQL_LEX_STRING *thd_make_lex_string(void* thd, MYSQL_LEX_STRING *lex_str,
+ const char *str, unsigned int size,
+ int allocate_lex_string);
+void thd_get_xid(const void* thd, MYSQL_XID *xid);
+void mysql_query_cache_invalidate4(void* thd,
+ const char *key, unsigned int key_length,
+ int using_trx);
+typedef enum enum_mysql_show_type SHOW_TYPE;
+typedef struct st_mysql_show_var SHOW_VAR;
+struct st_plugin_dl
+{
+ LEX_STRING dl;
+ void *handle;
+ struct st_mysql_plugin *plugins;
+ int version;
+ uint ref_count;
+};
+struct st_plugin_int
+{
+ LEX_STRING name;
+ struct st_mysql_plugin *plugin;
+ struct st_plugin_dl *plugin_dl;
+ uint state;
+ uint ref_count;
+ void *data;
+ MEM_ROOT mem_root;
+ sys_var *system_vars;
+};
+typedef struct st_plugin_int **plugin_ref;
+typedef int (*plugin_type_init)(struct st_plugin_int *);
+extern char *opt_plugin_load;
+extern char *opt_plugin_dir_ptr;
+extern char opt_plugin_dir[512];
+extern const LEX_STRING plugin_type_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);
+extern In_C_you_should_use_my_bool_instead() plugin_is_ready(const LEX_STRING *name, int type);
+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 );
+extern void plugin_unlock(THD *thd, plugin_ref plugin);
+extern void plugin_unlock_list(THD *thd, plugin_ref *list, uint count);
+extern In_C_you_should_use_my_bool_instead() mysql_install_plugin(THD *thd, const LEX_STRING *name,
+ const LEX_STRING *dl);
+extern In_C_you_should_use_my_bool_instead() mysql_uninstall_plugin(THD *thd, const LEX_STRING *name);
+extern In_C_you_should_use_my_bool_instead() plugin_register_builtin(struct st_mysql_plugin *plugin);
+extern void plugin_thdvar_init(THD *thd);
+extern void plugin_thdvar_cleanup(THD *thd);
+typedef my_bool (plugin_foreach_func)(THD *thd,
+ plugin_ref plugin,
+ void *arg);
+extern In_C_you_should_use_my_bool_instead() plugin_foreach_with_mask(THD *thd, plugin_foreach_func *func,
+ int type, uint state_mask, void *arg);
+#include "scheduler.h"
+class THD;
+class scheduler_functions
+{
+public:
+ uint max_threads;
+ In_C_you_should_use_my_bool_instead() (*init)(void);
+ In_C_you_should_use_my_bool_instead() (*init_new_connection_thread)(void);
+ void (*add_connection)(THD *thd);
+ void (*post_kill_notification)(THD *thd);
+ In_C_you_should_use_my_bool_instead() (*end_thread)(THD *thd, In_C_you_should_use_my_bool_instead() cache_thread);
+ void (*end)(void);
+ scheduler_functions();
+};
+enum scheduler_types
+{
+ SCHEDULER_ONE_THREAD_PER_CONNECTION=0,
+ SCHEDULER_NO_THREADS,
+ SCHEDULER_POOL_OF_THREADS
+};
+void one_thread_per_connection_scheduler(scheduler_functions* func);
+void one_thread_scheduler(scheduler_functions* func);
+enum pool_command_op
+{
+ NOT_IN_USE_OP= 0, NORMAL_OP= 1, CONNECT_OP, KILL_OP, DIE_OP
+};
+class thd_scheduler
+{};
+enum enum_query_type
+{
+ QT_ORDINARY,
+ QT_IS
+};
+typedef ulonglong table_map;
+typedef Bitmap<64> key_map;
+typedef ulong nesting_map;
+typedef ulonglong nested_join_map;
+typedef ulonglong query_id_t;
+extern query_id_t global_query_id;
+inline query_id_t next_query_id() { return global_query_id++; }
+extern const key_map key_map_empty;
+extern key_map key_map_full;
+extern const char *primary_key_name;
+#include "mysql_com.h"
+enum enum_server_command
+{
+ COM_SLEEP, COM_QUIT, COM_INIT_DB, COM_QUERY, COM_FIELD_LIST,
+ COM_CREATE_DB, COM_DROP_DB, COM_REFRESH, COM_SHUTDOWN, COM_STATISTICS,
+ COM_PROCESS_INFO, COM_CONNECT, COM_PROCESS_KILL, COM_DEBUG, COM_PING,
+ COM_TIME, COM_DELAYED_INSERT, COM_CHANGE_USER, COM_BINLOG_DUMP,
+ COM_TABLE_DUMP, COM_CONNECT_OUT, COM_REGISTER_SLAVE,
+ COM_STMT_PREPARE, COM_STMT_EXECUTE, COM_STMT_SEND_LONG_DATA, COM_STMT_CLOSE,
+ COM_STMT_RESET, COM_SET_OPTION, COM_STMT_FETCH, COM_DAEMON,
+ COM_END
+};
+struct st_vio;
+typedef struct st_vio Vio;
+typedef struct st_net {
+ Vio *vio;
+ unsigned char *buff,*buff_end,*write_pos,*read_pos;
+ my_socket fd;
+ unsigned long remain_in_buf,length, buf_length, where_b;
+ unsigned long max_packet,max_packet_size;
+ unsigned int pkt_nr,compress_pkt_nr;
+ unsigned int write_timeout, read_timeout, retry_count;
+ int fcntl;
+ unsigned int *return_status;
+ unsigned char reading_or_writing;
+ char save_char;
+ my_bool unused0;
+ my_bool unused;
+ my_bool compress;
+ my_bool unused1;
+ unsigned char *query_cache_query;
+ unsigned int last_errno;
+ unsigned char error;
+ my_bool unused2;
+ my_bool return_errno;
+ char last_error[512];
+ char sqlstate[5 +1];
+ void *extension;
+} NET;
+enum enum_field_types { MYSQL_TYPE_DECIMAL, MYSQL_TYPE_TINY,
+ MYSQL_TYPE_SHORT, MYSQL_TYPE_LONG,
+ MYSQL_TYPE_FLOAT, MYSQL_TYPE_DOUBLE,
+ MYSQL_TYPE_NULL, MYSQL_TYPE_TIMESTAMP,
+ MYSQL_TYPE_LONGLONG,MYSQL_TYPE_INT24,
+ MYSQL_TYPE_DATE, MYSQL_TYPE_TIME,
+ MYSQL_TYPE_DATETIME, MYSQL_TYPE_YEAR,
+ MYSQL_TYPE_NEWDATE, MYSQL_TYPE_VARCHAR,
+ MYSQL_TYPE_BIT,
+ MYSQL_TYPE_NEWDECIMAL=246,
+ MYSQL_TYPE_ENUM=247,
+ MYSQL_TYPE_SET=248,
+ MYSQL_TYPE_TINY_BLOB=249,
+ MYSQL_TYPE_MEDIUM_BLOB=250,
+ MYSQL_TYPE_LONG_BLOB=251,
+ MYSQL_TYPE_BLOB=252,
+ MYSQL_TYPE_VAR_STRING=253,
+ MYSQL_TYPE_STRING=254,
+ MYSQL_TYPE_GEOMETRY=255
+};
+enum mysql_enum_shutdown_level {
+ SHUTDOWN_DEFAULT = 0,
+ SHUTDOWN_WAIT_CONNECTIONS= (unsigned char)(1 << 0),
+ SHUTDOWN_WAIT_TRANSACTIONS= (unsigned char)(1 << 1),
+ SHUTDOWN_WAIT_UPDATES= (unsigned char)(1 << 3),
+ SHUTDOWN_WAIT_ALL_BUFFERS= ((unsigned char)(1 << 3) << 1),
+ SHUTDOWN_WAIT_CRITICAL_BUFFERS= ((unsigned char)(1 << 3) << 1) + 1,
+ KILL_QUERY= 254,
+ KILL_CONNECTION= 255
+};
+enum enum_cursor_type
+{
+ CURSOR_TYPE_NO_CURSOR= 0,
+ CURSOR_TYPE_READ_ONLY= 1,
+ CURSOR_TYPE_FOR_UPDATE= 2,
+ CURSOR_TYPE_SCROLLABLE= 4
+};
+enum enum_mysql_set_option
+{
+ MYSQL_OPTION_MULTI_STATEMENTS_ON,
+ MYSQL_OPTION_MULTI_STATEMENTS_OFF
+};
+my_bool my_net_init(NET *net, Vio* vio);
+void my_net_local_init(NET *net);
+void net_end(NET *net);
+ void net_clear(NET *net, my_bool clear_buffer);
+my_bool net_realloc(NET *net, size_t length);
+my_bool net_flush(NET *net);
+my_bool my_net_write(NET *net,const unsigned char *packet, size_t len);
+my_bool net_write_command(NET *net,unsigned char command,
+ const unsigned char *header, size_t head_len,
+ const unsigned char *packet, size_t len);
+int net_real_write(NET *net,const unsigned char *packet, size_t len);
+unsigned long my_net_read(NET *net);
+void my_net_set_write_timeout(NET *net, uint timeout);
+void my_net_set_read_timeout(NET *net, uint timeout);
+struct sockaddr;
+int my_connect(my_socket s, const struct sockaddr *name, unsigned int namelen,
+ unsigned int timeout);
+struct rand_struct {
+ unsigned long seed1,seed2,max_value;
+ double max_value_dbl;
+};
+enum Item_result {STRING_RESULT=0, REAL_RESULT, INT_RESULT, ROW_RESULT,
+ DECIMAL_RESULT};
+typedef struct st_udf_args
+{
+ unsigned int arg_count;
+ enum Item_result *arg_type;
+ char **args;
+ unsigned long *lengths;
+ char *maybe_null;
+ char **attributes;
+ unsigned long *attribute_lengths;
+ void *extension;
+} UDF_ARGS;
+typedef struct st_udf_init
+{
+ my_bool maybe_null;
+ unsigned int decimals;
+ unsigned long max_length;
+ char *ptr;
+ my_bool const_item;
+ void *extension;
+} UDF_INIT;
+void randominit(struct rand_struct *, unsigned long seed1,
+ unsigned long seed2);
+double my_rnd(struct rand_struct *);
+void create_random_string(char *to, unsigned int length, struct rand_struct *rand_st);
+void hash_password(unsigned long *to, const char *password, unsigned int password_len);
+void make_scrambled_password_323(char *to, const char *password);
+void scramble_323(char *to, const char *message, const char *password);
+my_bool check_scramble_323(const char *, const char *message,
+ unsigned long *salt);
+void get_salt_from_password_323(unsigned long *res, const char *password);
+void make_password_from_salt_323(char *to, const unsigned long *salt);
+void make_scrambled_password(char *to, const char *password);
+void scramble(char *to, const char *message, const char *password);
+my_bool check_scramble(const char *reply, const char *message,
+ const unsigned char *hash_stage2);
+void get_salt_from_password(unsigned char *res, const char *password);
+void make_password_from_salt(char *to, const unsigned char *hash_stage2);
+char *octet2hex(char *to, const char *str, unsigned int len);
+char *get_tty_password(const char *opt_message);
+const char *mysql_errno_to_sqlstate(unsigned int mysql_errno);
+my_bool my_thread_init(void);
+void my_thread_end(void);
+ulong net_field_length(uchar **packet);
+my_ulonglong net_field_length_ll(uchar **packet);
+uchar *net_store_length(uchar *pkg, ulonglong length);
+#include <violite.h>
+#include "my_net.h"
+#include <errno.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <sys/poll.h>
+#include <sys/ioctl.h>
+#include <netinet/in_systm.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <netinet/tcp.h>
+void my_inet_ntoa(struct in_addr in, char *buf);
+struct hostent;
+struct hostent *my_gethostbyname_r(const char *name,
+ struct hostent *result, char *buffer,
+ int buflen, int *h_errnop);
+enum enum_vio_type
+{
+ VIO_CLOSED, VIO_TYPE_TCPIP, VIO_TYPE_SOCKET, VIO_TYPE_NAMEDPIPE,
+ VIO_TYPE_SSL, VIO_TYPE_SHARED_MEMORY
+};
+Vio* vio_new(my_socket sd, enum enum_vio_type type, uint flags);
+void vio_delete(Vio* vio);
+int vio_close(Vio* vio);
+void vio_reset(Vio* vio, enum enum_vio_type type,
+ my_socket sd, void * hPipe, uint flags);
+size_t vio_read(Vio *vio, uchar * buf, size_t size);
+size_t vio_read_buff(Vio *vio, uchar * buf, size_t size);
+size_t vio_write(Vio *vio, const uchar * buf, size_t size);
+int vio_blocking(Vio *vio, my_bool onoff, my_bool *old_mode);
+my_bool vio_is_blocking(Vio *vio);
+int vio_fastsend(Vio *vio);
+int vio_keepalive(Vio *vio, my_bool onoff);
+my_bool vio_should_retry(Vio *vio);
+my_bool vio_was_interrupted(Vio *vio);
+const char* vio_description(Vio *vio);
+enum enum_vio_type vio_type(Vio* vio);
+int vio_errno(Vio*vio);
+my_socket vio_fd(Vio*vio);
+my_bool vio_peer_addr(Vio* vio, char *buf, uint16 *port);
+void vio_in_addr(Vio *vio, struct in_addr *in);
+my_bool vio_poll_read(Vio *vio,uint timeout);
+void vio_end(void);
+enum SSL_type
+{
+ SSL_TYPE_NOT_SPECIFIED= -1,
+ SSL_TYPE_NONE,
+ SSL_TYPE_ANY,
+ SSL_TYPE_X509,
+ SSL_TYPE_SPECIFIED
+};
+struct st_vio
+{
+ my_socket sd;
+ void * hPipe;
+ my_bool localhost;
+ int fcntl_mode;
+ struct sockaddr_in local;
+ struct sockaddr_in remote;
+ enum enum_vio_type type;
+ char desc[30];
+ char *read_buffer;
+ char *read_pos;
+ char *read_end;
+ void (*viodelete)(Vio*);
+ int (*vioerrno)(Vio*);
+ size_t (*read)(Vio*, uchar *, size_t);
+ size_t (*write)(Vio*, const uchar *, size_t);
+ int (*vioblocking)(Vio*, my_bool, my_bool *);
+ my_bool (*is_blocking)(Vio*);
+ int (*viokeepalive)(Vio*, my_bool);
+ int (*fastsend)(Vio*);
+ my_bool (*peer_addr)(Vio*, char *, uint16*);
+ void (*in_addr)(Vio*, struct in_addr*);
+ my_bool (*should_retry)(Vio*);
+ my_bool (*was_interrupted)(Vio*);
+ int (*vioclose)(Vio*);
+ void (*timeout)(Vio*, unsigned int which, unsigned int timeout);
+};
+#include "unireg.h"
+#include "mysqld_error.h"
+#include "structs.h"
+struct st_table;
+class Field;
+typedef struct st_date_time_format {
+ uchar positions[8];
+ char time_separator;
+ uint flag;
+ LEX_STRING format;
+} DATE_TIME_FORMAT;
+typedef struct st_keyfile_info {
+ uchar ref[8];
+ uchar dupp_ref[8];
+ uint ref_length;
+ uint block_size;
+ File filenr;
+ ha_rows records;
+ ha_rows deleted;
+ ulonglong data_file_length;
+ ulonglong max_data_file_length;
+ ulonglong index_file_length;
+ ulonglong max_index_file_length;
+ ulonglong delete_length;
+ ulonglong auto_increment_value;
+ int errkey,sortkey;
+ time_t create_time;
+ time_t check_time;
+ time_t update_time;
+ ulong mean_rec_length;
+} KEYFILE_INFO;
+typedef struct st_key_part_info {
+ Field *field;
+ uint offset;
+ uint null_offset;
+ uint16 length;
+ uint16 store_length;
+ uint16 key_type;
+ uint16 fieldnr;
+ uint16 key_part_flag;
+ uint8 type;
+ uint8 null_bit;
+} KEY_PART_INFO ;
+typedef struct st_key {
+ uint key_length;
+ ulong flags;
+ uint key_parts;
+ uint extra_length;
+ uint usable_key_parts;
+ uint block_size;
+ enum ha_key_alg algorithm;
+ union
+ {
+ plugin_ref parser;
+ LEX_STRING *parser_name;
+ };
+ KEY_PART_INFO *key_part;
+ char *name;
+ ulong *rec_per_key;
+ union {
+ int bdb_return_if_eq;
+ } handler;
+ struct st_table *table;
+} KEY;
+struct st_join_table;
+typedef struct st_reginfo {
+ struct st_join_table *join_tab;
+ enum thr_lock_type lock_type;
+ In_C_you_should_use_my_bool_instead() not_exists_optimize;
+ In_C_you_should_use_my_bool_instead() impossible_range;
+} REGINFO;
+struct st_read_record;
+class SQL_SELECT;
+class THD;
+class handler;
+typedef struct st_read_record {
+ struct st_table *table;
+ handler *file;
+ struct st_table **forms;
+ int (*read_record)(struct st_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;
+ uchar *record;
+ uchar *rec_buf;
+ uchar *cache,*cache_pos,*cache_end,*read_positions;
+ IO_CACHE *io_cache;
+ In_C_you_should_use_my_bool_instead() print_error, ignore_not_found_rows;
+} READ_RECORD;
+typedef enum enum_mysql_timestamp_type timestamp_type;
+typedef struct {
+ ulong year,month,day,hour;
+ ulonglong minute,second,second_part;
+ In_C_you_should_use_my_bool_instead() neg;
+} INTERVAL;
+typedef struct st_known_date_time_format {
+ const char *format_name;
+ const char *date_format;
+ const char *datetime_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 *);
+typedef struct st_lex_user {
+ LEX_STRING user, host, password;
+} LEX_USER;
+typedef struct user_resources {
+ uint questions;
+ uint updates;
+ uint conn_per_hour;
+ uint user_conn;
+ enum {QUERIES_PER_HOUR= 1, UPDATES_PER_HOUR= 2, CONNECTIONS_PER_HOUR= 4,
+ USER_CONNECTIONS= 8};
+ uint specified_limits;
+} USER_RESOURCES;
+typedef struct user_conn {
+ char *user;
+ char *host;
+ ulonglong reset_utime;
+ uint len;
+ uint connections;
+ uint conn_per_hour, updates, questions;
+ USER_RESOURCES user_resources;
+} USER_CONN;
+class Discrete_interval {
+private:
+ ulonglong interval_min;
+ ulonglong interval_values;
+ ulonglong interval_max;
+public:
+ Discrete_interval *next;
+ void replace(ulonglong start, ulonglong val, ulonglong incr)
+ {
+ interval_min= start;
+ interval_values= val;
+ interval_max= (val == ((unsigned long long)(~0ULL))) ? val : start + val * incr;
+ }
+ Discrete_interval(ulonglong start, ulonglong val, ulonglong incr) :
+ next(NULL) { replace(start, val, incr); };
+ Discrete_interval() : next(NULL) { replace(0, 0, 0); };
+ ulonglong minimum() const { return interval_min; };
+ ulonglong values() const { return interval_values; };
+ ulonglong maximum() const { return interval_max; };
+ In_C_you_should_use_my_bool_instead() merge_if_contiguous(ulonglong start, ulonglong val, ulonglong incr)
+ {
+ if (interval_max == start)
+ {
+ if (val == ((unsigned long long)(~0ULL)))
+ {
+ interval_values= interval_max= val;
+ }
+ else
+ {
+ interval_values+= val;
+ interval_max= start + val * incr;
+ }
+ return 0;
+ }
+ return 1;
+ };
+};
+class Discrete_intervals_list {
+private:
+ Discrete_interval *head;
+ Discrete_interval *tail;
+ Discrete_interval *current;
+ uint elements;
+ void copy_(const Discrete_intervals_list& from)
+ {
+ for (Discrete_interval *i= from.head; i; i= i->next)
+ {
+ Discrete_interval j= *i;
+ append(&j);
+ }
+ }
+public:
+ Discrete_intervals_list() : head(NULL), current(NULL), elements(0) {};
+ Discrete_intervals_list(const Discrete_intervals_list& from)
+ {
+ copy_(from);
+ }
+ void operator=(const Discrete_intervals_list& from)
+ {
+ empty();
+ copy_(from);
+ }
+ void empty_no_free()
+ {
+ head= current= NULL;
+ elements= 0;
+ }
+ void empty()
+ {
+ for (Discrete_interval *i= head; i;)
+ {
+ Discrete_interval *next= i->next;
+ delete i;
+ i= next;
+ }
+ empty_no_free();
+ }
+ const Discrete_interval* get_next()
+ {
+ Discrete_interval *tmp= current;
+ if (current != NULL)
+ current= current->next;
+ return tmp;
+ }
+ ~Discrete_intervals_list() { empty(); };
+ In_C_you_should_use_my_bool_instead() append(ulonglong start, ulonglong val, ulonglong incr);
+ In_C_you_should_use_my_bool_instead() append(Discrete_interval *interval);
+ ulonglong minimum() const { return (head ? head->minimum() : 0); };
+ ulonglong maximum() const { return (head ? tail->maximum() : 0); };
+ uint nb_elements() const { return elements; }
+};
+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);
+void sql_element_free(void *ptr);
+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);
+uint kill_one_thread(THD *thd, ulong id, In_C_you_should_use_my_bool_instead() only_kill_query);
+void sql_kill(THD *thd, ulong id, In_C_you_should_use_my_bool_instead() only_kill_query);
+In_C_you_should_use_my_bool_instead() net_request_file(NET* net, const char* fname);
+char* query_table_status(THD *thd,const char *db,const char *table_name);
+extern CHARSET_INFO *system_charset_info, *files_charset_info ;
+extern CHARSET_INFO *national_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 In_C_you_should_use_my_bool_instead() is_ascii;
+ TYPELIB *month_names;
+ TYPELIB *ab_month_names;
+ TYPELIB *day_names;
+ TYPELIB *ab_day_names;
+} 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);
+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()
+ { }
+};
+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:
+ CHARSET_INFO *m_client_cs;
+ CHARSET_INFO *m_connection_cl;
+};
+struct TABLE_LIST;
+class String;
+void view_store_options(THD *thd, TABLE_LIST *table, String *buff);
+enum enum_parsing_place
+{
+ NO_MATTER,
+ IN_HAVING,
+ SELECT_LIST,
+ IN_WHERE,
+ IN_ON
+};
+struct st_table;
+class THD;
+enum enum_check_fields
+{
+ CHECK_FIELD_IGNORE,
+ CHECK_FIELD_WARN,
+ CHECK_FIELD_ERROR_FOR_NULL
+};
+typedef struct st_sql_list {
+ uint elements;
+ uchar *first;
+ uchar **next;
+ st_sql_list() {}
+ inline void empty()
+ {
+ elements=0;
+ first=0;
+ next= &first;
+ }
+ inline void link_in_list(uchar *element,uchar **next_ptr)
+ {
+ elements++;
+ (*next)=element;
+ next= next_ptr;
+ *next=0;
+ }
+ inline void save_and_clear(struct st_sql_list *save)
+ {
+ *save= *this;
+ empty();
+ }
+ inline void push_front(struct st_sql_list *save)
+ {
+ *save->next= first;
+ first= save->first;
+ elements+= save->elements;
+ }
+ inline void push_back(struct st_sql_list *save)
+ {
+ if (save->first)
+ {
+ *next= save->first;
+ next= save->next;
+ elements+= save->elements;
+ }
+ }
+} SQL_LIST;
+extern pthread_key_t THR_THD;
+inline THD *_current_thd(void)
+{
+ return ((THD*) pthread_getspecific((THR_THD)));
+}
+extern "C"
+const char *set_thd_proc_info(THD *thd, const char *info,
+ const char *calling_func,
+ const char *calling_file,
+ const unsigned int calling_line);
+enum enum_table_ref_type
+{
+ TABLE_REF_NULL= 0,
+ TABLE_REF_VIEW,
+ TABLE_REF_BASE_TABLE,
+ TABLE_REF_I_S_TABLE,
+ TABLE_REF_TMP_TABLE
+};
+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"
+class String;
+int sortcmp(const String *a,const String *b, CHARSET_INFO *cs);
+String *copy_if_not_alloced(String *a,String *b,uint32 arg_length);
+uint32 copy_and_convert(char *to, uint32 to_length, CHARSET_INFO *to_cs,
+ const char *from, uint32 from_length,
+ CHARSET_INFO *from_cs, uint *errors);
+uint32 well_formed_copy_nchars(CHARSET_INFO *to_cs,
+ char *to, uint to_length,
+ CHARSET_INFO *from_cs,
+ const char *from, uint from_length,
+ uint nchars,
+ const char **well_formed_error_pos,
+ const char **cannot_convert_error_pos,
+ const char **from_end_pos);
+size_t my_copy_with_hex_escaping(CHARSET_INFO *cs,
+ char *dst, size_t dstlen,
+ const char *src, size_t srclen);
+class String
+{
+ char *Ptr;
+ uint32 str_length,Alloced_length;
+ In_C_you_should_use_my_bool_instead() alloced;
+ CHARSET_INFO *str_charset;
+public:
+ String()
+ {
+ Ptr=0; str_length=Alloced_length=0; alloced=0;
+ str_charset= &my_charset_bin;
+ }
+ String(uint32 length_arg)
+ {
+ alloced=0; Alloced_length=0; (void) real_alloc(length_arg);
+ str_charset= &my_charset_bin;
+ }
+ String(const char *str, CHARSET_INFO *cs)
+ {
+ Ptr=(char*) str; str_length=(uint) strlen(str); Alloced_length=0; alloced=0;
+ str_charset=cs;
+ }
+ String(const char *str,uint32 len, CHARSET_INFO *cs)
+ {
+ Ptr=(char*) str; str_length=len; Alloced_length=0; alloced=0;
+ str_charset=cs;
+ }
+ String(char *str,uint32 len, CHARSET_INFO *cs)
+ {
+ Ptr=(char*) str; Alloced_length=str_length=len; alloced=0;
+ str_charset=cs;
+ }
+ String(const String &str)
+ {
+ Ptr=str.Ptr ; str_length=str.str_length ;
+ Alloced_length=str.Alloced_length; alloced=0;
+ str_charset=str.str_charset;
+ }
+ static void *operator new(size_t size, MEM_ROOT *mem_root)
+ { return (void*) alloc_root(mem_root, (uint) size); }
+ static void operator delete(void *ptr_arg,size_t size)
+ { ; }
+ static void operator delete(void *ptr_arg, MEM_ROOT *mem_root)
+ { }
+ ~String() { free(); }
+ inline void set_charset(CHARSET_INFO *charset_arg)
+ { str_charset= charset_arg; }
+ inline CHARSET_INFO *charset() const { return str_charset; }
+ inline uint32 length() const { return str_length;}
+ inline uint32 alloced_length() const { return Alloced_length;}
+ inline char& operator [] (uint32 i) const { return Ptr[i]; }
+ inline void length(uint32 len) { str_length=len ; }
+ inline In_C_you_should_use_my_bool_instead() is_empty() { return (str_length == 0); }
+ inline void mark_as_const() { Alloced_length= 0;}
+ inline const char *ptr() const { return Ptr; }
+ inline char *c_ptr()
+ {
+ if (!Ptr || Ptr[str_length])
+ (void) realloc(str_length);
+ return Ptr;
+ }
+ inline char *c_ptr_quick()
+ {
+ if (Ptr && str_length < Alloced_length)
+ Ptr[str_length]=0;
+ return Ptr;
+ }
+ inline char *c_ptr_safe()
+ {
+ if (Ptr && str_length < Alloced_length)
+ Ptr[str_length]=0;
+ else
+ (void) realloc(str_length);
+ return Ptr;
+ }
+ void set(String &str,uint32 offset,uint32 arg_length)
+ {
+ assert(&str != this);
+ free();
+ Ptr=(char*) str.ptr()+offset; str_length=arg_length; alloced=0;
+ if (str.Alloced_length)
+ Alloced_length=str.Alloced_length-offset;
+ else
+ Alloced_length=0;
+ str_charset=str.str_charset;
+ }
+ inline void set(char *str,uint32 arg_length, CHARSET_INFO *cs)
+ {
+ free();
+ Ptr=(char*) str; str_length=Alloced_length=arg_length ; alloced=0;
+ str_charset=cs;
+ }
+ inline void set(const char *str,uint32 arg_length, CHARSET_INFO *cs)
+ {
+ free();
+ Ptr=(char*) str; str_length=arg_length; Alloced_length=0 ; alloced=0;
+ str_charset=cs;
+ }
+ In_C_you_should_use_my_bool_instead() set_ascii(const char *str, uint32 arg_length);
+ inline void set_quick(char *str,uint32 arg_length, CHARSET_INFO *cs)
+ {
+ if (!alloced)
+ {
+ Ptr=(char*) str; str_length=Alloced_length=arg_length;
+ }
+ str_charset=cs;
+ }
+ In_C_you_should_use_my_bool_instead() set_int(longlong num, In_C_you_should_use_my_bool_instead() unsigned_flag, CHARSET_INFO *cs);
+ In_C_you_should_use_my_bool_instead() set(longlong num, CHARSET_INFO *cs)
+ { return set_int(num, false, cs); }
+ In_C_you_should_use_my_bool_instead() set(ulonglong num, CHARSET_INFO *cs)
+ { return set_int((longlong)num, true, cs); }
+ In_C_you_should_use_my_bool_instead() set_real(double num,uint decimals, CHARSET_INFO *cs);
+ inline void chop()
+ {
+ Ptr[str_length--]= '\0';
+ }
+ inline void free()
+ {
+ if (alloced)
+ {
+ alloced=0;
+ Alloced_length=0;
+ ((void)(myf) (0),my_no_flags_free(Ptr));
+ Ptr=0;
+ str_length=0;
+ }
+ }
+ inline In_C_you_should_use_my_bool_instead() alloc(uint32 arg_length)
+ {
+ if (arg_length < Alloced_length)
+ return 0;
+ return real_alloc(arg_length);
+ }
+ In_C_you_should_use_my_bool_instead() real_alloc(uint32 arg_length);
+ In_C_you_should_use_my_bool_instead() realloc(uint32 arg_length);
+ inline void shrink(uint32 arg_length)
+ {
+ if (arg_length < Alloced_length)
+ {
+ char *new_ptr;
+ if (!(new_ptr=(char*) my_realloc(Ptr,arg_length,(myf) (0))))
+ {
+ Alloced_length = 0;
+ real_alloc(arg_length);
+ }
+ else
+ {
+ Ptr=new_ptr;
+ Alloced_length=arg_length;
+ }
+ }
+ }
+ In_C_you_should_use_my_bool_instead() is_alloced() { return alloced; }
+ inline String& operator = (const String &s)
+ {
+ if (&s != this)
+ {
+ assert(!s.uses_buffer_owned_by(this));
+ free();
+ Ptr=s.Ptr ; str_length=s.str_length ; Alloced_length=s.Alloced_length;
+ alloced=0;
+ }
+ return *this;
+ }
+ In_C_you_should_use_my_bool_instead() copy();
+ In_C_you_should_use_my_bool_instead() copy(const String &s);
+ In_C_you_should_use_my_bool_instead() copy(const char *s,uint32 arg_length, CHARSET_INFO *cs);
+ static In_C_you_should_use_my_bool_instead() needs_conversion(uint32 arg_length,
+ CHARSET_INFO *cs_from, CHARSET_INFO *cs_to,
+ uint32 *offset);
+ In_C_you_should_use_my_bool_instead() copy_aligned(const char *s, uint32 arg_length, uint32 offset,
+ CHARSET_INFO *cs);
+ In_C_you_should_use_my_bool_instead() set_or_copy_aligned(const char *s, uint32 arg_length, CHARSET_INFO *cs);
+ In_C_you_should_use_my_bool_instead() copy(const char*s,uint32 arg_length, CHARSET_INFO *csfrom,
+ CHARSET_INFO *csto, uint *errors);
+ In_C_you_should_use_my_bool_instead() append(const String &s);
+ In_C_you_should_use_my_bool_instead() append(const char *s);
+ In_C_you_should_use_my_bool_instead() append(const char *s,uint32 arg_length);
+ In_C_you_should_use_my_bool_instead() append(const char *s,uint32 arg_length, CHARSET_INFO *cs);
+ In_C_you_should_use_my_bool_instead() append(IO_CACHE* file, uint32 arg_length);
+ In_C_you_should_use_my_bool_instead() append_with_prefill(const char *s, uint32 arg_length,
+ uint32 full_length, char fill_char);
+ int strstr(const String &search,uint32 offset=0);
+ int strrstr(const String &search,uint32 offset=0);
+ In_C_you_should_use_my_bool_instead() replace(uint32 offset,uint32 arg_length,const char *to,uint32 length);
+ In_C_you_should_use_my_bool_instead() replace(uint32 offset,uint32 arg_length,const String &to);
+ inline In_C_you_should_use_my_bool_instead() append(char chr)
+ {
+ if (str_length < Alloced_length)
+ {
+ Ptr[str_length++]=chr;
+ }
+ else
+ {
+ if (realloc(str_length+1))
+ return 1;
+ Ptr[str_length++]=chr;
+ }
+ return 0;
+ }
+ In_C_you_should_use_my_bool_instead() fill(uint32 max_length,char fill);
+ void strip_sp();
+ friend int sortcmp(const String *a,const String *b, CHARSET_INFO *cs);
+ friend int stringcmp(const String *a,const String *b);
+ friend String *copy_if_not_alloced(String *a,String *b,uint32 arg_length);
+ uint32 numchars();
+ int charpos(int i,uint32 offset=0);
+ int reserve(uint32 space_needed)
+ {
+ return realloc(str_length + space_needed);
+ }
+ int reserve(uint32 space_needed, uint32 grow_by);
+ void q_append(const char c)
+ {
+ Ptr[str_length++] = c;
+ }
+ void q_append(const uint32 n)
+ {
+ *((long *) (Ptr + str_length))= (long) (n);
+ str_length += 4;
+ }
+ void q_append(double d)
+ {
+ do { *((long *) (Ptr + str_length)) = ((doubleget_union *)&(d))->m[0]; *(((long *) (Ptr + str_length))+1) = ((doubleget_union *)&(d))->m[1]; } while (0);
+ str_length += 8;
+ }
+ void q_append(double *d)
+ {
+ do { *((long *) (Ptr + str_length)) = ((doubleget_union *)&(*d))->m[0]; *(((long *) (Ptr + str_length))+1) = ((doubleget_union *)&(*d))->m[1]; } while (0);
+ str_length += 8;
+ }
+ void q_append(const char *data, uint32 data_len)
+ {
+ memcpy(Ptr + str_length, data, data_len);
+ str_length += data_len;
+ }
+ void write_at_position(int position, uint32 value)
+ {
+ *((long *) (Ptr + position))= (long) (value);
+ }
+ void qs_append(const char *str, uint32 len);
+ void qs_append(double d);
+ void qs_append(double *d);
+ inline void qs_append(const char c)
+ {
+ Ptr[str_length]= c;
+ str_length++;
+ }
+ void qs_append(int i);
+ void qs_append(uint i);
+ inline char *prep_append(uint32 arg_length, uint32 step_alloc)
+ {
+ uint32 new_length= arg_length + str_length;
+ if (new_length > Alloced_length)
+ {
+ if (realloc(new_length + step_alloc))
+ return 0;
+ }
+ uint32 old_length= str_length;
+ str_length+= arg_length;
+ return Ptr+ old_length;
+ }
+ inline In_C_you_should_use_my_bool_instead() append(const char *s, uint32 arg_length, uint32 step_alloc)
+ {
+ uint32 new_length= arg_length + str_length;
+ if (new_length > Alloced_length && realloc(new_length + step_alloc))
+ return (1);
+ memcpy(Ptr+str_length, s, arg_length);
+ str_length+= arg_length;
+ return (0);
+ }
+ void print(String *print);
+ void swap(String &s);
+ inline In_C_you_should_use_my_bool_instead() uses_buffer_owned_by(const String *s) const
+ {
+ return (s->alloced && Ptr >= s->Ptr && Ptr < s->Ptr + s->str_length);
+ }
+};
+static inline In_C_you_should_use_my_bool_instead() check_if_only_end_space(CHARSET_INFO *cs, char *str,
+ char *end)
+{
+ return str+ cs->cset->scan(cs, str, end, 2) == end;
+}
+#include "sql_list.h"
+class Sql_alloc
+{
+public:
+ static void *operator new(size_t size) throw ()
+ {
+ return sql_alloc(size);
+ }
+ static void *operator new[](size_t size)
+ {
+ return sql_alloc(size);
+ }
+ static void *operator new[](size_t size, MEM_ROOT *mem_root) throw ()
+ { return alloc_root(mem_root, size); }
+ static void *operator new(size_t size, MEM_ROOT *mem_root) throw ()
+ { return alloc_root(mem_root, size); }
+ static void operator delete(void *ptr, size_t size) { ; }
+ static void operator delete(void *ptr, MEM_ROOT *mem_root)
+ { }
+ static void operator delete[](void *ptr, MEM_ROOT *mem_root)
+ { }
+ static void operator delete[](void *ptr, size_t size) { ; }
+ inline Sql_alloc() {}
+ inline ~Sql_alloc() {}
+};
+struct list_node :public Sql_alloc
+{
+ list_node *next;
+ void *info;
+ list_node(void *info_par,list_node *next_par)
+ :next(next_par),info(info_par)
+ {}
+ list_node()
+ {
+ info= 0;
+ next= this;
+ }
+};
+extern list_node end_of_list;
+class base_list :public Sql_alloc
+{
+protected:
+ list_node *first,**last;
+public:
+ uint elements;
+ inline void empty() { elements=0; first= &end_of_list; last=&first;}
+ inline base_list() { empty(); }
+ inline base_list(const base_list &tmp) :Sql_alloc()
+ {
+ elements= tmp.elements;
+ first= tmp.first;
+ last= elements ? tmp.last : &first;
+ }
+ base_list(const base_list &rhs, MEM_ROOT *mem_root);
+ inline base_list(In_C_you_should_use_my_bool_instead() error) { }
+ inline In_C_you_should_use_my_bool_instead() push_back(void *info)
+ {
+ if (((*last)=new list_node(info, &end_of_list)))
+ {
+ last= &(*last)->next;
+ elements++;
+ return 0;
+ }
+ return 1;
+ }
+ inline In_C_you_should_use_my_bool_instead() push_back(void *info, MEM_ROOT *mem_root)
+ {
+ if (((*last)=new (mem_root) list_node(info, &end_of_list)))
+ {
+ last= &(*last)->next;
+ elements++;
+ return 0;
+ }
+ return 1;
+ }
+ inline In_C_you_should_use_my_bool_instead() push_front(void *info)
+ {
+ list_node *node=new list_node(info,first);
+ if (node)
+ {
+ if (last == &first)
+ last= &node->next;
+ first=node;
+ elements++;
+ return 0;
+ }
+ return 1;
+ }
+ void remove(list_node **prev)
+ {
+ list_node *node=(*prev)->next;
+ if (!--elements)
+ last= &first;
+ else if (last == &(*prev)->next)
+ last= prev;
+ delete *prev;
+ *prev=node;
+ }
+ inline void concat(base_list *list)
+ {
+ if (!list->is_empty())
+ {
+ *last= list->first;
+ last= list->last;
+ elements+= list->elements;
+ }
+ }
+ inline void *pop(void)
+ {
+ if (first == &end_of_list) return 0;
+ list_node *tmp=first;
+ first=first->next;
+ if (!--elements)
+ last= &first;
+ return tmp->info;
+ }
+ inline void disjoin(base_list *list)
+ {
+ list_node **prev= &first;
+ list_node *node= first;
+ list_node *list_first= list->first;
+ elements=0;
+ while (node && node != list_first)
+ {
+ prev= &node->next;
+ node= node->next;
+ elements++;
+ }
+ *prev= *last;
+ last= prev;
+ }
+ inline void prepand(base_list *list)
+ {
+ if (!list->is_empty())
+ {
+ *list->last= first;
+ first= list->first;
+ elements+= list->elements;
+ }
+ }
+ inline void swap(base_list &rhs)
+ {
+ { list_node * dummy; dummy= first; first= rhs.first; rhs.first= dummy; };
+ { list_node ** dummy; dummy= last; last= rhs.last; rhs.last= dummy; };
+ { uint dummy; dummy= elements; elements= rhs.elements; rhs.elements= dummy; };
+ }
+ inline list_node* last_node() { return *last; }
+ inline list_node* first_node() { return first;}
+ inline void *head() { return first->info; }
+ inline void **head_ref() { return first != &end_of_list ? &first->info : 0; }
+ inline In_C_you_should_use_my_bool_instead() is_empty() { return first == &end_of_list ; }
+ inline list_node *last_ref() { return &end_of_list; }
+ friend class base_list_iterator;
+ friend class error_list;
+ friend class error_list_iterator;
+protected:
+ void after(void *info,list_node *node)
+ {
+ list_node *new_node=new list_node(info,node->next);
+ node->next=new_node;
+ elements++;
+ if (last == &(node->next))
+ last= &new_node->next;
+ }
+};
+class base_list_iterator
+{
+protected:
+ base_list *list;
+ list_node **el,**prev,*current;
+ void sublist(base_list &ls, uint elm)
+ {
+ ls.first= *el;
+ ls.last= list->last;
+ ls.elements= elm;
+ }
+public:
+ base_list_iterator()
+ :list(0), el(0), prev(0), current(0)
+ {}
+ base_list_iterator(base_list &list_par)
+ { init(list_par); }
+ inline void init(base_list &list_par)
+ {
+ list= &list_par;
+ el= &list_par.first;
+ prev= 0;
+ current= 0;
+ }
+ inline void *next(void)
+ {
+ prev=el;
+ current= *el;
+ el= &current->next;
+ return current->info;
+ }
+ inline void *next_fast(void)
+ {
+ list_node *tmp;
+ tmp= *el;
+ el= &tmp->next;
+ return tmp->info;
+ }
+ inline void rewind(void)
+ {
+ el= &list->first;
+ }
+ inline void *replace(void *element)
+ {
+ void *tmp=current->info;
+ assert(current->info != 0);
+ current->info=element;
+ return tmp;
+ }
+ void *replace(base_list &new_list)
+ {
+ void *ret_value=current->info;
+ if (!new_list.is_empty())
+ {
+ *new_list.last=current->next;
+ current->info=new_list.first->info;
+ current->next=new_list.first->next;
+ if ((list->last == &current->next) && (new_list.elements > 1))
+ list->last= new_list.last;
+ list->elements+=new_list.elements-1;
+ }
+ return ret_value;
+ }
+ inline void remove(void)
+ {
+ list->remove(prev);
+ el=prev;
+ current=0;
+ }
+ void after(void *element)
+ {
+ list->after(element,current);
+ current=current->next;
+ el= &current->next;
+ }
+ inline void **ref(void)
+ {
+ return &current->info;
+ }
+ inline In_C_you_should_use_my_bool_instead() is_last(void)
+ {
+ return el == &list->last_ref()->next;
+ }
+ friend class error_list_iterator;
+};
+template <class T> class List :public base_list
+{
+public:
+ inline List() :base_list() {}
+ inline List(const List<T> &tmp) :base_list(tmp) {}
+ inline List(const List<T> &tmp, MEM_ROOT *mem_root) :
+ base_list(tmp, mem_root) {}
+ inline In_C_you_should_use_my_bool_instead() push_back(T *a) { return base_list::push_back(a); }
+ inline In_C_you_should_use_my_bool_instead() push_back(T *a, MEM_ROOT *mem_root)
+ { return base_list::push_back(a, mem_root); }
+ inline In_C_you_should_use_my_bool_instead() push_front(T *a) { return base_list::push_front(a); }
+ inline T* head() {return (T*) base_list::head(); }
+ inline T** head_ref() {return (T**) base_list::head_ref(); }
+ inline T* pop() {return (T*) base_list::pop(); }
+ inline void concat(List<T> *list) { base_list::concat(list); }
+ inline void disjoin(List<T> *list) { base_list::disjoin(list); }
+ inline void prepand(List<T> *list) { base_list::prepand(list); }
+ void delete_elements(void)
+ {
+ list_node *element,*next;
+ for (element=first; element != &end_of_list; element=next)
+ {
+ next=element->next;
+ delete (T*) element->info;
+ }
+ empty();
+ }
+};
+template <class T> class List_iterator :public base_list_iterator
+{
+public:
+ List_iterator(List<T> &a) : base_list_iterator(a) {}
+ List_iterator() : base_list_iterator() {}
+ inline void init(List<T> &a) { base_list_iterator::init(a); }
+ inline T* operator++(int) { return (T*) base_list_iterator::next(); }
+ inline T *replace(T *a) { return (T*) base_list_iterator::replace(a); }
+ inline T *replace(List<T> &a) { return (T*) base_list_iterator::replace(a); }
+ inline void rewind(void) { base_list_iterator::rewind(); }
+ inline void remove() { base_list_iterator::remove(); }
+ inline void after(T *a) { base_list_iterator::after(a); }
+ inline T** ref(void) { return (T**) base_list_iterator::ref(); }
+};
+template <class T> class List_iterator_fast :public base_list_iterator
+{
+protected:
+ inline T *replace(T *a) { return (T*) 0; }
+ inline T *replace(List<T> &a) { return (T*) 0; }
+ inline void remove(void) { }
+ inline void after(T *a) { }
+ inline T** ref(void) { return (T**) 0; }
+public:
+ inline List_iterator_fast(List<T> &a) : base_list_iterator(a) {}
+ inline List_iterator_fast() : base_list_iterator() {}
+ inline void init(List<T> &a) { base_list_iterator::init(a); }
+ inline T* operator++(int) { return (T*) base_list_iterator::next_fast(); }
+ inline void rewind(void) { base_list_iterator::rewind(); }
+ void sublist(List<T> &list_arg, uint el_arg)
+ {
+ base_list_iterator::sublist(list_arg, el_arg);
+ }
+};
+struct ilink
+{
+ struct ilink **prev,*next;
+ static void *operator new(size_t size)
+ {
+ return (void*)my_malloc((uint)size, (myf) (16 | 8));
+ }
+ static void operator delete(void* ptr_arg, size_t size)
+ {
+ ((void)(myf) (16|64),my_no_flags_free((uchar*)ptr_arg));
+ }
+ inline ilink()
+ {
+ prev=0; next=0;
+ }
+ inline void unlink()
+ {
+ if (prev) *prev= next;
+ if (next) next->prev=prev;
+ prev=0 ; next=0;
+ }
+ virtual ~ilink() { unlink(); }
+};
+class i_string: public ilink
+{
+public:
+ const char* ptr;
+ i_string():ptr(0) { }
+ i_string(const char* s) : ptr(s) {}
+};
+class i_string_pair: public ilink
+{
+public:
+ const char* key;
+ const char* val;
+ i_string_pair():key(0),val(0) { }
+ i_string_pair(const char* key_arg, const char* val_arg) :
+ key(key_arg),val(val_arg) {}
+};
+template <class T> class I_List_iterator;
+class base_ilist
+{
+public:
+ struct ilink *first,last;
+ inline void empty() { first= &last; last.prev= &first; }
+ base_ilist() { empty(); }
+ inline In_C_you_should_use_my_bool_instead() is_empty() { return first == &last; }
+ inline void append(ilink *a)
+ {
+ first->prev= &a->next;
+ a->next=first; a->prev= &first; first=a;
+ }
+ inline void push_back(ilink *a)
+ {
+ *last.prev= a;
+ a->next= &last;
+ a->prev= last.prev;
+ last.prev= &a->next;
+ }
+ inline struct ilink *get()
+ {
+ struct ilink *first_link=first;
+ if (first_link == &last)
+ return 0;
+ first_link->unlink();
+ return first_link;
+ }
+ inline struct ilink *head()
+ {
+ return (first != &last) ? first : 0;
+ }
+ friend class base_list_iterator;
+};
+class base_ilist_iterator
+{
+ base_ilist *list;
+ struct ilink **el,*current;
+public:
+ base_ilist_iterator(base_ilist &list_par) :list(&list_par),
+ el(&list_par.first),current(0) {}
+ void *next(void)
+ {
+ current= *el;
+ if (current == &list->last) return 0;
+ el= &current->next;
+ return current;
+ }
+};
+template <class T>
+class I_List :private base_ilist
+{
+public:
+ I_List() :base_ilist() {}
+ inline void empty() { base_ilist::empty(); }
+ inline In_C_you_should_use_my_bool_instead() 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(); }
+ friend class I_List_iterator<T>;
+};
+template <class T> class I_List_iterator :public base_ilist_iterator
+{
+public:
+ I_List_iterator(I_List<T> &a) : base_ilist_iterator(a) {}
+ inline T* operator++(int) { return (T*) base_ilist_iterator::next(); }
+};
+template <typename T>
+inline
+void
+list_copy_and_replace_each_value(List<T> &list, MEM_ROOT *mem_root)
+{
+ List_iterator<T> it(list);
+ T *el;
+ while ((el= it++))
+ it.replace(el->clone(mem_root));
+}
+#include "sql_map.h"
+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;
+ File file;
+ int error;
+ 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);
+ }
+ ~mapped_file()
+ {
+ unmap_file(file);
+ }
+ uchar *map()
+ {
+ return file->map;
+ }
+};
+#include "my_decimal.h"
+#include <decimal.h>
+typedef enum
+{TRUNCATE=0, HALF_EVEN, HALF_UP, CEILING, FLOOR}
+ decimal_round_mode;
+typedef int32 decimal_digit_t;
+typedef struct st_decimal_t {
+ int intg, frac, len;
+ my_bool sign;
+ decimal_digit_t *buf;
+} decimal_t;
+int internal_str2dec(const char *from, decimal_t *to, char **end,
+ my_bool fixed);
+int decimal2string(decimal_t *from, char *to, int *to_len,
+ int fixed_precision, int fixed_decimals,
+ char filler);
+int decimal2ulonglong(decimal_t *from, ulonglong *to);
+int ulonglong2decimal(ulonglong from, decimal_t *to);
+int decimal2longlong(decimal_t *from, longlong *to);
+int longlong2decimal(longlong from, decimal_t *to);
+int decimal2double(decimal_t *from, double *to);
+int double2decimal(double from, decimal_t *to);
+int decimal_actual_fraction(decimal_t *from);
+int decimal2bin(decimal_t *from, uchar *to, int precision, int scale);
+int bin2decimal(const uchar *from, decimal_t *to, int precision, int scale);
+int decimal_size(int precision, int scale);
+int decimal_bin_size(int precision, int scale);
+int decimal_result_size(decimal_t *from1, decimal_t *from2, char op,
+ int param);
+int decimal_intg(decimal_t *from);
+int decimal_add(decimal_t *from1, decimal_t *from2, decimal_t *to);
+int decimal_sub(decimal_t *from1, decimal_t *from2, decimal_t *to);
+int decimal_cmp(decimal_t *from1, decimal_t *from2);
+int decimal_mul(decimal_t *from1, decimal_t *from2, decimal_t *to);
+int decimal_div(decimal_t *from1, decimal_t *from2, decimal_t *to,
+ int scale_incr);
+int decimal_mod(decimal_t *from1, decimal_t *from2, decimal_t *to);
+int decimal_round(decimal_t *from, decimal_t *to, int new_scale,
+ decimal_round_mode mode);
+int decimal_is_zero(decimal_t *from);
+void max_decimal(int precision, int frac, decimal_t *to);
+inline uint my_decimal_size(uint precision, uint scale)
+{
+ return decimal_size(precision, scale) + 1;
+}
+inline int my_decimal_int_part(uint precision, uint decimals)
+{
+ return precision - ((decimals == 31) ? 0 : decimals);
+}
+class my_decimal :public decimal_t
+{
+ decimal_digit_t buffer[9];
+public:
+ void init()
+ {
+ len= 9;
+ buf= buffer;
+ for (uint i= 0; i < 9; i++)
+ buffer[i]= i;
+ }
+ my_decimal()
+ {
+ init();
+ }
+ void fix_buffer_pointer() { buf= buffer; }
+ In_C_you_should_use_my_bool_instead() sign() const { return decimal_t::sign; }
+ void sign(In_C_you_should_use_my_bool_instead() s) { decimal_t::sign= s; }
+ uint precision() const { return intg + frac; }
+ void swap(my_decimal &rhs)
+ {
+ { my_decimal dummy; dummy= *this; *this= rhs; rhs= dummy; };
+ { decimal_digit_t * dummy; dummy= buf; buf= rhs.buf; rhs.buf= dummy; };
+ }
+};
+void print_decimal(const my_decimal *dec);
+void print_decimal_buff(const my_decimal *dec, const uchar* ptr, int length);
+const char *dbug_decimal_as_string(char *buff, const my_decimal *val);
+int decimal_operation_results(int result);
+inline
+void max_my_decimal(my_decimal *to, int precision, int frac)
+{
+ assert((precision <= ((9 * 9) - 8*2))&& (frac <= 30));
+ max_decimal(precision, frac, (decimal_t*) to);
+}
+inline void max_internal_decimal(my_decimal *to)
+{
+ max_my_decimal(to, ((9 * 9) - 8*2), 0);
+}
+inline int check_result(uint mask, int result)
+{
+ if (result & mask)
+ decimal_operation_results(result);
+ return result;
+}
+inline int check_result_and_overflow(uint mask, int result, my_decimal *val)
+{
+ if (check_result(mask, result) & 2)
+ {
+ In_C_you_should_use_my_bool_instead() sign= val->sign();
+ val->fix_buffer_pointer();
+ max_internal_decimal(val);
+ val->sign(sign);
+ }
+ return result;
+}
+inline uint my_decimal_length_to_precision(uint length, uint scale,
+ In_C_you_should_use_my_bool_instead() unsigned_flag)
+{
+ assert(length || !scale);
+ return (uint) (length - (scale>0 ? 1:0) -
+ (unsigned_flag || !length ? 0:1));
+}
+inline uint32 my_decimal_precision_to_length(uint precision, uint8 scale,
+ In_C_you_should_use_my_bool_instead() unsigned_flag)
+{
+ assert(precision || !scale);
+ do { if ((precision) > (((9 * 9) - 8*2))) (precision)=(((9 * 9) - 8*2)); } while(0);
+ return (uint32)(precision + (scale>0 ? 1:0) +
+ (unsigned_flag || !precision ? 0:1));
+}
+inline
+int my_decimal_string_length(const my_decimal *d)
+{
+ return (((d)->intg ? (d)->intg : 1) + (d)->frac + ((d)->frac > 0) + 2);
+}
+inline
+int my_decimal_max_length(const my_decimal *d)
+{
+ return (((d)->intg ? (d)->intg : 1) + (d)->frac + ((d)->frac > 0) + 2) - 1;
+}
+inline
+int my_decimal_get_binary_size(uint precision, uint scale)
+{
+ return decimal_bin_size((int)precision, (int)scale);
+}
+inline
+void my_decimal2decimal(const my_decimal *from, my_decimal *to)
+{
+ *to= *from;
+ to->fix_buffer_pointer();
+}
+int my_decimal2binary(uint mask, const my_decimal *d, uchar *bin, int prec,
+ int scale);
+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));
+}
+inline
+int my_decimal_set_zero(my_decimal *d)
+{
+ do { (((decimal_t*) d))->buf[0]=0; (((decimal_t*) d))->intg=1; (((decimal_t*) d))->frac=0; (((decimal_t*) d))->sign=0; } while(0);
+ return 0;
+}
+inline
+In_C_you_should_use_my_bool_instead() my_decimal_is_zero(const my_decimal *decimal_value)
+{
+ return decimal_is_zero((decimal_t*) decimal_value);
+}
+inline
+int my_decimal_round(uint mask, const my_decimal *from, int scale,
+ In_C_you_should_use_my_bool_instead() truncate, my_decimal *to)
+{
+ return check_result(mask, decimal_round((decimal_t*) from, to, scale,
+ (truncate ? TRUNCATE : HALF_UP)));
+}
+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));
+}
+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));
+}
+int my_decimal2string(uint mask, const my_decimal *d, uint fixed_prec,
+ uint fixed_dec, char filler, String *str);
+inline
+int my_decimal2int(uint mask, const my_decimal *d, my_bool unsigned_flag,
+ longlong *l)
+{
+ my_decimal rounded;
+ decimal_round((decimal_t*)d, &rounded, 0, HALF_UP);
+ return check_result(mask, (unsigned_flag ?
+ decimal2ulonglong(&rounded, (ulonglong *)l) :
+ decimal2longlong(&rounded, l)));
+}
+inline
+int my_decimal2double(uint mask, const my_decimal *d, double *result)
+{
+ return decimal2double((decimal_t*) d, result);
+}
+inline
+int str2my_decimal(uint mask, const char *str, my_decimal *d, char **end)
+{
+ return check_result_and_overflow(mask, internal_str2dec((str), ((decimal_t*)d), (end), 0),
+ d);
+}
+int str2my_decimal(uint mask, const char *from, uint length,
+ CHARSET_INFO *charset, my_decimal *decimal_value);
+inline
+int double2my_decimal(uint mask, double val, my_decimal *d)
+{
+ return check_result_and_overflow(mask, double2decimal(val, (decimal_t*)d), d);
+}
+inline
+int int2my_decimal(uint mask, longlong i, my_bool unsigned_flag, my_decimal *d)
+{
+ return check_result(mask, (unsigned_flag ?
+ ulonglong2decimal((ulonglong)i, d) :
+ longlong2decimal(i, d)));
+}
+inline
+void my_decimal_neg(decimal_t *arg)
+{
+ if (decimal_is_zero(arg))
+ {
+ arg->sign= 0;
+ return;
+ }
+ do { (arg)->sign^=1; } while(0);
+}
+inline
+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),
+ res);
+}
+inline
+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),
+ res);
+}
+inline
+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),
+ res);
+}
+inline
+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),
+ res);
+}
+inline
+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),
+ res);
+}
+inline
+int my_decimal_cmp(const my_decimal *a, const my_decimal *b)
+{
+ return decimal_cmp((decimal_t*) a, (decimal_t*) b);
+}
+inline
+int my_decimal_intg(const my_decimal *a)
+{
+ return decimal_intg((decimal_t*) a);
+}
+void my_decimal_trim(ulong *precision, uint *scale);
+#include "handler.h"
+#include <my_handler.h>
+#include "myisampack.h"
+typedef struct st_HA_KEYSEG
+{
+ CHARSET_INFO *charset;
+ uint32 start;
+ uint32 null_pos;
+ uint16 bit_pos;
+ uint16 flag;
+ uint16 length;
+ uint8 type;
+ uint8 language;
+ uint8 null_bit;
+ uint8 bit_start,bit_end;
+ uint8 bit_length;
+} HA_KEYSEG;
+extern int ha_compare_text(CHARSET_INFO *, uchar *, uint, uchar *, uint ,
+ my_bool, my_bool);
+extern int ha_key_cmp(register HA_KEYSEG *keyseg, register uchar *a,
+ register uchar *b, uint key_length, uint nextflag,
+ uint *diff_pos);
+extern HA_KEYSEG *ha_find_null(HA_KEYSEG *keyseg, uchar *a);
+extern void my_handler_error_register(void);
+extern void my_handler_error_unregister(void);
+#include <ft_global.h>
+typedef struct st_ft_info FT_INFO;
+struct _ft_vft
+{
+ int (*read_next)(FT_INFO *, char *);
+ float (*find_relevance)(FT_INFO *, uchar *, uint);
+ void (*close_search)(FT_INFO *);
+ float (*get_relevance)(FT_INFO *);
+ void (*reinit_search)(FT_INFO *);
+};
+struct st_ft_info
+{
+ struct _ft_vft *please;
+};
+extern const char *ft_stopword_file;
+extern const char *ft_precompiled_stopwords[];
+extern ulong ft_min_word_len;
+extern ulong ft_max_word_len;
+extern ulong ft_query_expansion_limit;
+extern char ft_boolean_syntax[15];
+extern struct st_mysql_ftparser ft_default_parser;
+int ft_init_stopwords(void);
+void ft_free_stopwords(void);
+FT_INFO *ft_init_search(uint,void *, uint, uchar *, uint,CHARSET_INFO *, uchar *);
+my_bool ft_boolean_check_syntax_string(const uchar *);
+#include <keycache.h>
+struct st_block_link;
+typedef struct st_block_link BLOCK_LINK;
+struct st_keycache_page;
+typedef struct st_keycache_page KEYCACHE_PAGE;
+struct st_hash_link;
+typedef struct st_hash_link HASH_LINK;
+typedef struct st_keycache_wqueue
+{
+ struct st_my_thread_var *last_thread;
+} KEYCACHE_WQUEUE;
+typedef struct st_key_cache
+{
+ my_bool key_cache_inited;
+ my_bool in_resize;
+ my_bool resize_in_flush;
+ my_bool can_be_used;
+ size_t key_cache_mem_size;
+ uint key_cache_block_size;
+ ulong min_warm_blocks;
+ ulong age_threshold;
+ ulonglong keycache_time;
+ uint hash_entries;
+ int hash_links;
+ int hash_links_used;
+ int disk_blocks;
+ ulong blocks_used;
+ ulong blocks_unused;
+ ulong blocks_changed;
+ ulong warm_blocks;
+ ulong cnt_for_resize_op;
+ long blocks_available;
+ HASH_LINK **hash_root;
+ HASH_LINK *hash_link_root;
+ HASH_LINK *free_hash_list;
+ BLOCK_LINK *free_block_list;
+ BLOCK_LINK *block_root;
+ uchar *block_mem;
+ BLOCK_LINK *used_last;
+ BLOCK_LINK *used_ins;
+ pthread_mutex_t cache_lock;
+ KEYCACHE_WQUEUE resize_queue;
+ KEYCACHE_WQUEUE waiting_for_resize_cnt;
+ KEYCACHE_WQUEUE waiting_for_hash_link;
+ KEYCACHE_WQUEUE waiting_for_block;
+ BLOCK_LINK *changed_blocks[128];
+ BLOCK_LINK *file_blocks[128];
+ ulonglong param_buff_size;
+ ulong param_block_size;
+ ulong param_division_limit;
+ ulong param_age_threshold;
+ ulong global_blocks_changed;
+ ulonglong global_cache_w_requests;
+ ulonglong global_cache_write;
+ ulonglong global_cache_r_requests;
+ ulonglong global_cache_read;
+ int blocks;
+ my_bool in_init;
+} KEY_CACHE;
+extern KEY_CACHE dflt_key_cache_var, *dflt_key_cache;
+extern int init_key_cache(KEY_CACHE *keycache, uint key_cache_block_size,
+ size_t use_mem, uint division_limit,
+ uint age_threshold);
+extern int resize_key_cache(KEY_CACHE *keycache, uint key_cache_block_size,
+ size_t use_mem, uint division_limit,
+ uint age_threshold);
+extern void change_key_cache_param(KEY_CACHE *keycache, uint division_limit,
+ uint age_threshold);
+extern uchar *key_cache_read(KEY_CACHE *keycache,
+ File file, my_off_t filepos, int level,
+ uchar *buff, uint length,
+ uint block_length,int return_buffer);
+extern int key_cache_insert(KEY_CACHE *keycache,
+ File file, my_off_t filepos, int level,
+ uchar *buff, uint length);
+extern int key_cache_write(KEY_CACHE *keycache,
+ File file, my_off_t filepos, int level,
+ uchar *buff, uint length,
+ uint block_length,int force_write);
+extern int flush_key_blocks(KEY_CACHE *keycache,
+ int file, enum flush_type type);
+extern void end_key_cache(KEY_CACHE *keycache, my_bool cleanup);
+extern my_bool multi_keycache_init(void);
+extern void multi_keycache_free(void);
+extern KEY_CACHE *multi_key_cache_search(uchar *key, uint length);
+extern my_bool multi_key_cache_set(const uchar *key, uint length,
+ KEY_CACHE *key_cache);
+extern void multi_key_cache_change(KEY_CACHE *old_data,
+ KEY_CACHE *new_data);
+extern int reset_key_cache_counters(const char *name,
+ KEY_CACHE *key_cache);
+enum legacy_db_type
+{
+ DB_TYPE_UNKNOWN=0,DB_TYPE_DIAB_ISAM=1,
+ DB_TYPE_HASH,DB_TYPE_MISAM,DB_TYPE_PISAM,
+ DB_TYPE_RMS_ISAM, DB_TYPE_HEAP, DB_TYPE_ISAM,
+ DB_TYPE_MRG_ISAM, DB_TYPE_MYISAM, DB_TYPE_MRG_MYISAM,
+ DB_TYPE_BERKELEY_DB, DB_TYPE_INNODB,
+ DB_TYPE_GEMINI, DB_TYPE_NDBCLUSTER,
+ DB_TYPE_EXAMPLE_DB, DB_TYPE_ARCHIVE_DB, DB_TYPE_CSV_DB,
+ DB_TYPE_FEDERATED_DB,
+ DB_TYPE_BLACKHOLE_DB,
+ DB_TYPE_PARTITION_DB,
+ DB_TYPE_BINLOG,
+ DB_TYPE_SOLID,
+ DB_TYPE_PBXT,
+ DB_TYPE_TABLE_FUNCTION,
+ DB_TYPE_MEMCACHE,
+ DB_TYPE_FALCON,
+ DB_TYPE_MARIA,
+ DB_TYPE_FIRST_DYNAMIC=42,
+ DB_TYPE_DEFAULT=127
+};
+enum row_type { ROW_TYPE_NOT_USED=-1, ROW_TYPE_DEFAULT, ROW_TYPE_FIXED,
+ ROW_TYPE_DYNAMIC, ROW_TYPE_COMPRESSED,
+ ROW_TYPE_REDUNDANT, ROW_TYPE_COMPACT, ROW_TYPE_PAGE };
+enum enum_binlog_func {
+ BFN_RESET_LOGS= 1,
+ BFN_RESET_SLAVE= 2,
+ BFN_BINLOG_WAIT= 3,
+ BFN_BINLOG_END= 4,
+ BFN_BINLOG_PURGE_FILE= 5
+};
+enum enum_binlog_command {
+ LOGCOM_CREATE_TABLE,
+ LOGCOM_ALTER_TABLE,
+ LOGCOM_RENAME_TABLE,
+ LOGCOM_DROP_TABLE,
+ LOGCOM_CREATE_DB,
+ LOGCOM_ALTER_DB,
+ LOGCOM_DROP_DB
+};
+typedef ulonglong my_xid;
+struct xid_t {
+ long formatID;
+ long gtrid_length;
+ long bqual_length;
+ char data[128];
+ xid_t() {}
+ In_C_you_should_use_my_bool_instead() eq(struct xid_t *xid)
+ { return eq(xid->gtrid_length, xid->bqual_length, xid->data); }
+ In_C_you_should_use_my_bool_instead() eq(long g, long b, const char *d)
+ { return g == gtrid_length && b == bqual_length && !memcmp(d, data, g+b); }
+ void set(struct xid_t *xid)
+ { memcpy(this, xid, xid->length()); }
+ void set(long f, const char *g, long gl, const char *b, long bl)
+ {
+ formatID= f;
+ memcpy(data, g, gtrid_length= gl);
+ memcpy(data+gl, b, bqual_length= bl);
+ }
+ void set(ulonglong xid)
+ {
+ my_xid tmp;
+ formatID= 1;
+ set(8, 0, "MySQLXid");
+ memcpy(data+8, &server_id, sizeof(server_id));
+ tmp= xid;
+ memcpy(data+(8 +sizeof(server_id)), &tmp, sizeof(tmp));
+ gtrid_length=((8 +sizeof(server_id))+sizeof(my_xid));
+ }
+ void set(long g, long b, const char *d)
+ {
+ formatID= 1;
+ gtrid_length= g;
+ bqual_length= b;
+ memcpy(data, d, g+b);
+ }
+ In_C_you_should_use_my_bool_instead() is_null() { return formatID == -1; }
+ void null() { formatID= -1; }
+ my_xid quick_get_my_xid()
+ {
+ my_xid tmp;
+ memcpy(&tmp, data+(8 +sizeof(server_id)), sizeof(tmp));
+ return tmp;
+ }
+ my_xid get_my_xid()
+ {
+ return gtrid_length == ((8 +sizeof(server_id))+sizeof(my_xid)) && bqual_length == 0 &&
+ !memcmp(data+8, &server_id, sizeof(server_id)) &&
+ !memcmp(data, "MySQLXid", 8) ?
+ quick_get_my_xid() : 0;
+ }
+ uint length()
+ {
+ return sizeof(formatID)+sizeof(gtrid_length)+sizeof(bqual_length)+
+ gtrid_length+bqual_length;
+ }
+ uchar *key()
+ {
+ return (uchar *)&gtrid_length;
+ }
+ uint key_length()
+ {
+ return sizeof(gtrid_length)+sizeof(bqual_length)+gtrid_length+bqual_length;
+ }
+};
+typedef struct xid_t XID;
+enum ts_command_type
+{
+ TS_CMD_NOT_DEFINED = -1,
+ CREATE_TABLESPACE = 0,
+ ALTER_TABLESPACE = 1,
+ CREATE_LOGFILE_GROUP = 2,
+ ALTER_LOGFILE_GROUP = 3,
+ DROP_TABLESPACE = 4,
+ DROP_LOGFILE_GROUP = 5,
+ CHANGE_FILE_TABLESPACE = 6,
+ ALTER_ACCESS_MODE_TABLESPACE = 7
+};
+enum ts_alter_tablespace_type
+{
+ TS_ALTER_TABLESPACE_TYPE_NOT_DEFINED = -1,
+ ALTER_TABLESPACE_ADD_FILE = 1,
+ ALTER_TABLESPACE_DROP_FILE = 2
+};
+enum tablespace_access_mode
+{
+ TS_NOT_DEFINED= -1,
+ TS_READ_ONLY = 0,
+ TS_READ_WRITE = 1,
+ TS_NOT_ACCESSIBLE = 2
+};
+struct handlerton;
+class st_alter_tablespace : public Sql_alloc
+{
+ public:
+ const char *tablespace_name;
+ const char *logfile_group_name;
+ enum ts_command_type ts_cmd_type;
+ enum ts_alter_tablespace_type ts_alter_tablespace_type;
+ const char *data_file_name;
+ const char *undo_file_name;
+ const char *redo_file_name;
+ ulonglong extent_size;
+ ulonglong undo_buffer_size;
+ ulonglong redo_buffer_size;
+ ulonglong initial_size;
+ ulonglong autoextend_size;
+ ulonglong max_size;
+ uint nodegroup_id;
+ handlerton *storage_engine;
+ In_C_you_should_use_my_bool_instead() wait_until_completed;
+ const char *ts_comment;
+ enum tablespace_access_mode ts_access_mode;
+ st_alter_tablespace()
+ {
+ tablespace_name= NULL;
+ logfile_group_name= "DEFAULT_LG";
+ ts_cmd_type= TS_CMD_NOT_DEFINED;
+ data_file_name= NULL;
+ undo_file_name= NULL;
+ redo_file_name= NULL;
+ extent_size= 1024*1024;
+ undo_buffer_size= 8*1024*1024;
+ redo_buffer_size= 8*1024*1024;
+ initial_size= 128*1024*1024;
+ autoextend_size= 0;
+ max_size= 0;
+ storage_engine= NULL;
+ nodegroup_id= 65535;
+ wait_until_completed= (1);
+ ts_comment= NULL;
+ ts_access_mode= TS_NOT_DEFINED;
+ }
+};
+struct st_table;
+typedef struct st_table TABLE;
+typedef struct st_table_share TABLE_SHARE;
+struct st_foreign_key_info;
+typedef struct st_foreign_key_info FOREIGN_KEY_INFO;
+typedef In_C_you_should_use_my_bool_instead() (stat_print_fn)(THD *thd, const char *type, uint type_len,
+ const char *file, uint file_len,
+ const char *status, uint status_len);
+enum ha_stat_type { HA_ENGINE_STATUS, HA_ENGINE_LOGS, HA_ENGINE_MUTEX };
+extern st_plugin_int *hton2plugin[15];
+enum log_status
+{
+ HA_LOG_STATUS_FREE= 0,
+ HA_LOG_STATUS_INUSE= 1,
+ HA_LOG_STATUS_NOSUCHLOG= 2
+};
+void signal_log_not_needed(struct handlerton, char *log_file);
+struct handler_log_file_data {
+ LEX_STRING filename;
+ enum log_status status;
+};
+enum handler_iterator_type
+{
+ HA_TRANSACTLOG_ITERATOR= 1
+};
+enum handler_create_iterator_result
+{
+ HA_ITERATOR_OK,
+ HA_ITERATOR_UNSUPPORTED,
+ HA_ITERATOR_ERROR
+};
+struct handler_iterator {
+ int (*next)(struct handler_iterator *, void *iterator_object);
+ void (*destroy)(struct handler_iterator *);
+ void *buffer;
+};
+struct handlerton
+{
+ SHOW_COMP_OPTION state;
+ enum legacy_db_type db_type;
+ uint slot;
+ uint savepoint_offset;
+ int (*close_connection)(handlerton *hton, THD *thd);
+ int (*savepoint_set)(handlerton *hton, THD *thd, void *sv);
+ int (*savepoint_rollback)(handlerton *hton, THD *thd, void *sv);
+ int (*savepoint_release)(handlerton *hton, THD *thd, void *sv);
+ int (*commit)(handlerton *hton, THD *thd, In_C_you_should_use_my_bool_instead() all);
+ int (*rollback)(handlerton *hton, THD *thd, In_C_you_should_use_my_bool_instead() all);
+ int (*prepare)(handlerton *hton, THD *thd, In_C_you_should_use_my_bool_instead() all);
+ int (*recover)(handlerton *hton, XID *xid_list, uint len);
+ int (*commit_by_xid)(handlerton *hton, XID *xid);
+ int (*rollback_by_xid)(handlerton *hton, XID *xid);
+ void *(*create_cursor_read_view)(handlerton *hton, THD *thd);
+ void (*set_cursor_read_view)(handlerton *hton, THD *thd, void *read_view);
+ void (*close_cursor_read_view)(handlerton *hton, THD *thd, void *read_view);
+ handler *(*create)(handlerton *hton, TABLE_SHARE *table, MEM_ROOT *mem_root);
+ void (*drop_database)(handlerton *hton, char* path);
+ int (*panic)(handlerton *hton, enum ha_panic_function flag);
+ int (*start_consistent_snapshot)(handlerton *hton, THD *thd);
+ In_C_you_should_use_my_bool_instead() (*flush_logs)(handlerton *hton);
+ In_C_you_should_use_my_bool_instead() (*show_status)(handlerton *hton, THD *thd, stat_print_fn *print, enum ha_stat_type stat);
+ 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);
+ uint32 flags;
+ int (*binlog_func)(handlerton *hton, THD *thd, enum_binlog_func fn, void *arg);
+ void (*binlog_log_query)(handlerton *hton, THD *thd,
+ enum_binlog_command binlog_command,
+ const char *query, uint query_length,
+ const char *db, const char *table_name);
+ int (*release_temporary_latches)(handlerton *hton, THD *thd);
+ enum log_status (*get_log_status)(handlerton *hton, char *log);
+ enum handler_create_iterator_result
+ (*create_iterator)(handlerton *hton, enum handler_iterator_type type,
+ struct handler_iterator *fill_this_in);
+ int (*discover)(handlerton *hton, THD* thd, const char *db,
+ const char *name,
+ uchar **frmblob,
+ size_t *frmlen);
+ int (*find_files)(handlerton *hton, THD *thd,
+ const char *db,
+ const char *path,
+ const char *wild, In_C_you_should_use_my_bool_instead() dir, List<LEX_STRING> *files);
+ int (*table_exists_in_engine)(handlerton *hton, THD* thd, const char *db,
+ const char *name);
+ uint32 license;
+ void *data;
+};
+class Ha_trx_info;
+struct THD_TRANS
+{
+ In_C_you_should_use_my_bool_instead() no_2pc;
+ Ha_trx_info *ha_list;
+ In_C_you_should_use_my_bool_instead() modified_non_trans_table;
+ void reset() { no_2pc= (0); modified_non_trans_table= (0); }
+};
+class Ha_trx_info
+{
+public:
+ void register_ha(THD_TRANS *trans, handlerton *ht_arg)
+ {
+ assert(m_flags == 0);
+ assert(m_ht == NULL);
+ assert(m_next == NULL);
+ m_ht= ht_arg;
+ m_flags= (int) TRX_READ_ONLY;
+ m_next= trans->ha_list;
+ trans->ha_list= this;
+ }
+ void reset()
+ {
+ m_next= NULL;
+ m_ht= NULL;
+ m_flags= 0;
+ }
+ Ha_trx_info() { reset(); }
+ void set_trx_read_write()
+ {
+ assert(is_started());
+ m_flags|= (int) TRX_READ_WRITE;
+ }
+ In_C_you_should_use_my_bool_instead() is_trx_read_write() const
+ {
+ assert(is_started());
+ return m_flags & (int) TRX_READ_WRITE;
+ }
+ In_C_you_should_use_my_bool_instead() is_started() const { return m_ht != NULL; }
+ void coalesce_trx_with(const Ha_trx_info *stmt_trx)
+ {
+ assert(is_started());
+ if (stmt_trx->is_trx_read_write())
+ set_trx_read_write();
+ }
+ Ha_trx_info *next() const
+ {
+ assert(is_started());
+ return m_next;
+ }
+ handlerton *ht() const
+ {
+ assert(is_started());
+ return m_ht;
+ }
+private:
+ enum { TRX_READ_ONLY= 0, TRX_READ_WRITE= 1 };
+ Ha_trx_info *m_next;
+ handlerton *m_ht;
+ uchar m_flags;
+};
+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;
+ 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;
+class Item;
+struct st_table_log_memory_entry;
+class partition_info;
+struct st_partition_iter;
+enum ha_choice { HA_CHOICE_UNDEF, HA_CHOICE_NO, HA_CHOICE_YES };
+typedef struct st_ha_create_information
+{
+ CHARSET_INFO *table_charset, *default_table_charset;
+ LEX_STRING connect_string;
+ const char *password, *tablespace;
+ LEX_STRING comment;
+ const char *data_file_name, *index_file_name;
+ const char *alias;
+ ulonglong max_rows,min_rows;
+ ulonglong auto_increment_value;
+ ulong table_options;
+ ulong avg_row_length;
+ ulong used_fields;
+ ulong key_block_size;
+ SQL_LIST merge_list;
+ handlerton *db_type;
+ enum row_type row_type;
+ uint null_bits;
+ uint options;
+ uint merge_insert_method;
+ uint extra_size;
+ enum ha_choice transactional;
+ In_C_you_should_use_my_bool_instead() table_existed;
+ In_C_you_should_use_my_bool_instead() frm_only;
+ In_C_you_should_use_my_bool_instead() varchar;
+ enum ha_storage_media storage_media;
+ enum ha_choice page_checksum;
+} HA_CREATE_INFO;
+typedef struct st_key_create_information
+{
+ enum ha_key_alg algorithm;
+ ulong block_size;
+ LEX_STRING parser_name;
+} KEY_CREATE_INFO;
+class TABLEOP_HOOKS
+{
+public:
+ TABLEOP_HOOKS() {}
+ virtual ~TABLEOP_HOOKS() {}
+ inline void prelock(TABLE **tables, uint count)
+ {
+ do_prelock(tables, count);
+ }
+ inline int postlock(TABLE **tables, uint count)
+ {
+ return do_postlock(tables, count);
+ }
+private:
+ virtual void do_prelock(TABLE **tables, uint count)
+ {
+ }
+ virtual int do_postlock(TABLE **tables, uint count)
+ {
+ return 0;
+ }
+};
+typedef struct st_savepoint SAVEPOINT;
+extern ulong savepoint_alloc_size;
+extern KEY_CREATE_INFO default_key_create_info;
+typedef class Item COND;
+typedef struct st_ha_check_opt
+{
+ st_ha_check_opt() {}
+ ulong sort_buffer_size;
+ uint flags;
+ uint sql_flags;
+ KEY_CACHE *key_cache;
+ void init();
+} HA_CHECK_OPT;
+typedef struct st_handler_buffer
+{
+ const uchar *buffer;
+ const uchar *buffer_end;
+ uchar *end_of_used_area;
+} HANDLER_BUFFER;
+typedef struct system_status_var SSV;
+class ha_statistics
+{
+public:
+ ulonglong data_file_length;
+ ulonglong max_data_file_length;
+ ulonglong index_file_length;
+ ulonglong max_index_file_length;
+ ulonglong delete_length;
+ ulonglong auto_increment_value;
+ ha_rows records;
+ ha_rows deleted;
+ ulong mean_rec_length;
+ time_t create_time;
+ time_t check_time;
+ time_t update_time;
+ uint block_size;
+ ha_statistics():
+ data_file_length(0), max_data_file_length(0),
+ index_file_length(0), delete_length(0), auto_increment_value(0),
+ records(0), deleted(0), mean_rec_length(0), create_time(0),
+ check_time(0), update_time(0), block_size(0)
+ {}
+};
+uint calculate_key_len(TABLE *, uint, const uchar *, key_part_map);
+class handler :public Sql_alloc
+{
+public:
+ typedef ulonglong Table_flags;
+protected:
+ struct st_table_share *table_share;
+ struct st_table *table;
+ Table_flags cached_table_flags;
+ ha_rows estimation_rows_to_insert;
+public:
+ handlerton *ht;
+ uchar *ref;
+ uchar *dup_ref;
+ ha_statistics stats;
+ In_C_you_should_use_my_bool_instead() multi_range_sorted;
+ KEY_MULTI_RANGE *multi_range_curr;
+ KEY_MULTI_RANGE *multi_range_end;
+ HANDLER_BUFFER *multi_range_buffer;
+ key_range save_end_range, *end_range;
+ KEY_PART_INFO *range_key_part;
+ int key_compare_result_on_equal;
+ In_C_you_should_use_my_bool_instead() eq_range;
+ uint errkey;
+ uint key_used_on_scan;
+ uint active_index;
+ uint ref_length;
+ FT_INFO *ft_handler;
+ enum {NONE=0, INDEX, RND} inited;
+ In_C_you_should_use_my_bool_instead() locked;
+ In_C_you_should_use_my_bool_instead() implicit_emptied;
+ const COND *pushed_cond;
+ ulonglong next_insert_id;
+ ulonglong insert_id_for_cur_row;
+ Discrete_interval auto_inc_interval_for_cur_row;
+ handler(handlerton *ht_arg, TABLE_SHARE *share_arg)
+ :table_share(share_arg), table(0),
+ estimation_rows_to_insert(0), ht(ht_arg),
+ ref(0), key_used_on_scan(64), active_index(64),
+ ref_length(sizeof(my_off_t)),
+ ft_handler(0), inited(NONE),
+ locked((0)), implicit_emptied(0),
+ pushed_cond(0), next_insert_id(0), insert_id_for_cur_row(0)
+ {}
+ virtual ~handler(void)
+ {
+ assert(locked == (0));
+ }
+ virtual handler *clone(MEM_ROOT *mem_root);
+ void init()
+ {
+ cached_table_flags= table_flags();
+ }
+ int ha_open(TABLE *table, const char *name, int mode, int test_if_locked);
+ int ha_index_init(uint idx, In_C_you_should_use_my_bool_instead() sorted)
+ {
+ int result;
+ const char *_db_func_, *_db_file_; uint _db_level_; char **_db_framep_; _db_enter_ ("ha_index_init","./sql/handler.h",1159,&_db_func_,&_db_file_,&_db_level_, &_db_framep_);
+ assert(inited==NONE);
+ if (!(result= index_init(idx, sorted)))
+ inited=INDEX;
+ do {_db_return_ (1163, &_db_func_, &_db_file_, &_db_level_); return(result);} while(0);
+ }
+ int ha_index_end()
+ {
+ const char *_db_func_, *_db_file_; uint _db_level_; char **_db_framep_; _db_enter_ ("ha_index_end","./sql/handler.h",1167,&_db_func_,&_db_file_,&_db_level_, &_db_framep_);
+ assert(inited==INDEX);
+ inited=NONE;
+ do {_db_return_ (1170, &_db_func_, &_db_file_, &_db_level_); return(index_end());} while(0);
+ }
+ int ha_rnd_init(In_C_you_should_use_my_bool_instead() scan)
+ {
+ int result;
+ const char *_db_func_, *_db_file_; uint _db_level_; char **_db_framep_; _db_enter_ ("ha_rnd_init","./sql/handler.h",1175,&_db_func_,&_db_file_,&_db_level_, &_db_framep_);
+ assert(inited==NONE || (inited==RND && scan));
+ inited= (result= rnd_init(scan)) ? NONE: RND;
+ do {_db_return_ (1178, &_db_func_, &_db_file_, &_db_level_); return(result);} while(0);
+ }
+ int ha_rnd_end()
+ {
+ const char *_db_func_, *_db_file_; uint _db_level_; char **_db_framep_; _db_enter_ ("ha_rnd_end","./sql/handler.h",1182,&_db_func_,&_db_file_,&_db_level_, &_db_framep_);
+ assert(inited==RND);
+ inited=NONE;
+ do {_db_return_ (1185, &_db_func_, &_db_file_, &_db_level_); return(rnd_end());} while(0);
+ }
+ int ha_reset();
+ int ha_index_or_rnd_end()
+ {
+ return inited == INDEX ? ha_index_end() : inited == RND ? ha_rnd_end() : 0;
+ }
+ Table_flags ha_table_flags() const { return cached_table_flags; }
+ int ha_external_lock(THD *thd, int lock_type);
+ int ha_write_row(uchar * buf);
+ int ha_update_row(const uchar * old_data, uchar * new_data);
+ int ha_delete_row(const uchar * buf);
+ void ha_release_auto_increment();
+ int ha_check_for_upgrade(HA_CHECK_OPT *check_opt);
+ int ha_check(THD *thd, HA_CHECK_OPT *check_opt);
+ int ha_repair(THD* thd, HA_CHECK_OPT* check_opt);
+ void ha_start_bulk_insert(ha_rows rows)
+ {
+ estimation_rows_to_insert= rows;
+ start_bulk_insert(rows);
+ }
+ int ha_end_bulk_insert()
+ {
+ estimation_rows_to_insert= 0;
+ return end_bulk_insert();
+ }
+ int ha_bulk_update_row(const uchar *old_data, uchar *new_data,
+ uint *dup_key_found);
+ int ha_delete_all_rows();
+ 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);
+ In_C_you_should_use_my_bool_instead() ha_check_and_repair(THD *thd);
+ int ha_disable_indexes(uint mode);
+ int ha_enable_indexes(uint mode);
+ int ha_discard_or_import_tablespace(my_bool discard);
+ void ha_prepare_for_alter();
+ int ha_rename_table(const char *from, const char *to);
+ int ha_delete_table(const char *name);
+ void ha_drop_table(const char *name);
+ int ha_create(const char *name, TABLE *form, HA_CREATE_INFO *info);
+ int ha_create_handler_files(const char *name, const char *old_name,
+ int action_flag, HA_CREATE_INFO *info);
+ int ha_change_partitions(HA_CREATE_INFO *create_info,
+ const char *path,
+ ulonglong *copied,
+ ulonglong *deleted,
+ const uchar *pack_frm_data,
+ size_t pack_frm_len);
+ int ha_drop_partitions(const char *path);
+ int ha_rename_partitions(const char *path);
+ int ha_optimize_partitions(THD *thd);
+ int ha_analyze_partitions(THD *thd);
+ int ha_check_partitions(THD *thd);
+ int ha_repair_partitions(THD *thd);
+ void adjust_next_insert_id_after_explicit_value(ulonglong nr);
+ int update_auto_increment();
+ void print_keydup_error(uint key_nr, const char *msg);
+ virtual void print_error(int error, myf errflag);
+ virtual In_C_you_should_use_my_bool_instead() get_error_message(int error, String *buf);
+ uint get_dup_key(int error);
+ virtual void change_table_ptr(TABLE *table_arg, TABLE_SHARE *share)
+ {
+ table= table_arg;
+ table_share= share;
+ }
+ virtual double scan_time()
+ { return ((double) (ulonglong) (stats.data_file_length)) / 4096 + 2; }
+ virtual double read_time(uint index, uint ranges, ha_rows rows)
+ { return ((double) (ulonglong) (ranges+rows)); }
+ virtual const key_map *keys_to_use_for_scanning() { return &key_map_empty; }
+ In_C_you_should_use_my_bool_instead() has_transactions()
+ { return (ha_table_flags() & (1 << 0)) == 0; }
+ virtual uint extra_rec_buf_length() const { return 0; }
+ virtual In_C_you_should_use_my_bool_instead() is_fatal_error(int error, uint flags)
+ {
+ if (!error ||
+ ((flags & 1) &&
+ (error == 121 ||
+ error == 141)))
+ return (0);
+ return (1);
+ }
+ virtual ha_rows records() { return stats.records; }
+ virtual ha_rows estimate_rows_upper_bound()
+ { return stats.records+10; }
+ virtual enum row_type get_row_type() const { return ROW_TYPE_NOT_USED; }
+ virtual const char *index_type(uint key_number) { assert(0); return "";}
+ virtual void column_bitmaps_signal();
+ uint get_index(void) const { return active_index; }
+ virtual int close(void)=0;
+ virtual In_C_you_should_use_my_bool_instead() start_bulk_update() { return 1; }
+ virtual In_C_you_should_use_my_bool_instead() start_bulk_delete() { return 1; }
+ virtual int exec_bulk_update(uint *dup_key_found)
+ {
+ assert((0));
+ return 131;
+ }
+ virtual void end_bulk_update() { return; }
+ virtual int end_bulk_delete()
+ {
+ assert((0));
+ return 131;
+ }
+ virtual int index_read_map(uchar * buf, const uchar * key,
+ key_part_map keypart_map,
+ enum ha_rkey_function find_flag)
+ {
+ uint key_len= calculate_key_len(table, active_index, key, keypart_map);
+ return index_read(buf, key, key_len, find_flag);
+ }
+ virtual int index_read_idx_map(uchar * buf, uint index, const uchar * key,
+ key_part_map keypart_map,
+ enum ha_rkey_function find_flag);
+ virtual int index_next(uchar * buf)
+ { return 131; }
+ virtual int index_prev(uchar * buf)
+ { return 131; }
+ virtual int index_first(uchar * buf)
+ { return 131; }
+ virtual int index_last(uchar * buf)
+ { return 131; }
+ virtual int index_next_same(uchar *buf, const uchar *key, uint keylen);
+ virtual int index_read_last_map(uchar * buf, const uchar * key,
+ key_part_map keypart_map)
+ {
+ uint key_len= calculate_key_len(table, active_index, key, keypart_map);
+ return index_read_last(buf, key, key_len);
+ }
+ virtual int read_multi_range_first(KEY_MULTI_RANGE **found_range_p,
+ KEY_MULTI_RANGE *ranges, uint range_count,
+ In_C_you_should_use_my_bool_instead() sorted, HANDLER_BUFFER *buffer);
+ virtual int read_multi_range_next(KEY_MULTI_RANGE **found_range_p);
+ virtual int read_range_first(const key_range *start_key,
+ const key_range *end_key,
+ In_C_you_should_use_my_bool_instead() eq_range, In_C_you_should_use_my_bool_instead() sorted);
+ virtual int read_range_next();
+ int compare_key(key_range *range);
+ virtual int ft_init() { return 131; }
+ void ft_end() { ft_handler=NULL; }
+ virtual FT_INFO *ft_init_ext(uint flags, uint inx,String *key)
+ { return NULL; }
+ virtual int ft_read(uchar *buf) { return 131; }
+ virtual int rnd_next(uchar *buf)=0;
+ virtual int rnd_pos(uchar * buf, uchar *pos)=0;
+ virtual int rnd_pos_by_record(uchar *record)
+ {
+ position(record);
+ return rnd_pos(record, ref);
+ }
+ virtual int read_first_row(uchar *buf, uint primary_key);
+ virtual int restart_rnd_next(uchar *buf, uchar *pos)
+ { return 131; }
+ virtual int rnd_same(uchar *buf, uint inx)
+ { return 131; }
+ virtual ha_rows records_in_range(uint inx, key_range *min_key, key_range *max_key)
+ { return (ha_rows) 10; }
+ virtual void position(const uchar *record)=0;
+ virtual int info(uint)=0;
+ virtual void get_dynamic_partition_info(PARTITION_INFO *stat_info,
+ uint part_id);
+ virtual int extra(enum ha_extra_function operation)
+ { return 0; }
+ virtual int extra_opt(enum ha_extra_function operation, ulong cache_size)
+ { return extra(operation); }
+ virtual In_C_you_should_use_my_bool_instead() was_semi_consistent_read() { return 0; }
+ virtual void try_semi_consistent_read(In_C_you_should_use_my_bool_instead()) {}
+ virtual void unlock_row() {}
+ virtual int start_stmt(THD *thd, thr_lock_type lock_type) {return 0;}
+ virtual void get_auto_increment(ulonglong offset, ulonglong increment,
+ ulonglong nb_desired_values,
+ ulonglong *first_value,
+ ulonglong *nb_reserved_values);
+ void set_next_insert_id(ulonglong id)
+ {
+ do {_db_pargs_(1488,"info"); _db_doprnt_ ("auto_increment: next value %lu", (ulong)id);} while(0);
+ next_insert_id= id;
+ }
+ void restore_auto_increment(ulonglong prev_insert_id)
+ {
+ next_insert_id= (prev_insert_id > 0) ? prev_insert_id :
+ insert_id_for_cur_row;
+ }
+ virtual void update_create_info(HA_CREATE_INFO *create_info) {}
+ int check_old_types();
+ virtual int assign_to_keycache(THD* thd, HA_CHECK_OPT* check_opt)
+ { return -1; }
+ virtual int preload_keys(THD* thd, HA_CHECK_OPT* check_opt)
+ { return -1; }
+ virtual int dump(THD* thd, int fd = -1) { return 131; }
+ virtual int indexes_are_disabled(void) {return 0;}
+ virtual int net_read_dump(NET* net) { return 131; }
+ virtual char *update_table_comment(const char * comment)
+ { return (char*) comment;}
+ virtual void append_create_info(String *packet) {}
+ virtual In_C_you_should_use_my_bool_instead() is_fk_defined_on_table_or_index(uint index)
+ { return (0); }
+ virtual char* get_foreign_key_create_info()
+ { return(NULL);}
+ virtual char* get_tablespace_name(THD *thd, char *name, uint name_len)
+ { return(NULL);}
+ virtual In_C_you_should_use_my_bool_instead() can_switch_engines() { return 1; }
+ virtual int get_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()
+ { return; }
+ virtual void free_foreign_key_create_info(char* str) {}
+ virtual const char *table_type() const =0;
+ virtual const char **bas_ext() const =0;
+ virtual int get_default_no_partitions(HA_CREATE_INFO *info) { return 1;}
+ virtual void set_auto_partitions(partition_info *part_info) { return; }
+ virtual In_C_you_should_use_my_bool_instead() get_no_parts(const char *name,
+ uint *no_parts)
+ {
+ *no_parts= 0;
+ return 0;
+ }
+ virtual void set_part_info(partition_info *part_info) {return;}
+ virtual ulong index_flags(uint idx, uint part, In_C_you_should_use_my_bool_instead() all_parts) const =0;
+ virtual int add_index(TABLE *table_arg, KEY *key_info, uint num_of_keys)
+ { return (131); }
+ virtual int prepare_drop_index(TABLE *table_arg, uint *key_num,
+ uint num_of_keys)
+ { return (131); }
+ virtual int final_drop_index(TABLE *table_arg)
+ { return (131); }
+ uint max_record_length() const
+ { return ((65535) < (max_supported_record_length()) ? (65535) : (max_supported_record_length())); }
+ uint max_keys() const
+ { return ((64) < (max_supported_keys()) ? (64) : (max_supported_keys())); }
+ uint max_key_parts() const
+ { return ((16) < (max_supported_key_parts()) ? (16) : (max_supported_key_parts())); }
+ uint max_key_length() const
+ { return ((3072) < (max_supported_key_length()) ? (3072) : (max_supported_key_length())); }
+ uint max_key_part_length() const
+ { return ((3072) < (max_supported_key_part_length()) ? (3072) : (max_supported_key_part_length())); }
+ virtual uint max_supported_record_length() const { return 65535; }
+ virtual uint max_supported_keys() const { return 0; }
+ virtual uint max_supported_key_parts() const { return 16; }
+ virtual uint max_supported_key_length() const { return 3072; }
+ virtual uint max_supported_key_part_length() const { return 255; }
+ virtual uint min_record_length(uint options) const { return 1; }
+ virtual In_C_you_should_use_my_bool_instead() low_byte_first() const { return 1; }
+ virtual uint checksum() const { return 0; }
+ virtual In_C_you_should_use_my_bool_instead() is_crashed() const { return 0; }
+ virtual In_C_you_should_use_my_bool_instead() auto_repair() const { return 0; }
+ virtual uint lock_count(void) const { return 1; }
+ virtual THR_LOCK_DATA **store_lock(THD *thd,
+ THR_LOCK_DATA **to,
+ enum thr_lock_type lock_type)=0;
+ virtual uint8 table_cache_type() { return 0; }
+ 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= 0;
+ return (1);
+ }
+ virtual In_C_you_should_use_my_bool_instead() primary_key_is_clustered() { return (0); }
+ virtual int cmp_ref(const uchar *ref1, const uchar *ref2)
+ {
+ return memcmp(ref1, ref2, ref_length);
+ }
+ virtual const COND *cond_push(const COND *cond) { return cond; };
+ virtual void cond_pop() { return; };
+ virtual In_C_you_should_use_my_bool_instead() check_if_incompatible_data(HA_CREATE_INFO *create_info,
+ uint table_changes)
+ { return 1; }
+ virtual void use_hidden_primary_key();
+protected:
+ void ha_statistic_increment(ulong SSV::*offset) const;
+ void **ha_data(THD *) const;
+ THD *ha_thd(void) const;
+ virtual int rename_table(const char *from, const char *to);
+ virtual int delete_table(const char *name);
+private:
+ inline void mark_trx_read_write();
+private:
+ virtual int open(const char *name, int mode, uint test_if_locked)=0;
+ virtual int index_init(uint idx, In_C_you_should_use_my_bool_instead() sorted) { active_index= idx; return 0; }
+ virtual int index_end() { active_index= 64; return 0; }
+ virtual int rnd_init(In_C_you_should_use_my_bool_instead() scan)= 0;
+ virtual int rnd_end() { return 0; }
+ virtual int write_row(uchar *buf __attribute__((unused)))
+ {
+ return 131;
+ }
+ virtual int update_row(const uchar *old_data __attribute__((unused)),
+ uchar *new_data __attribute__((unused)))
+ {
+ return 131;
+ }
+ virtual int delete_row(const uchar *buf __attribute__((unused)))
+ {
+ return 131;
+ }
+ virtual int reset() { return 0; }
+ virtual Table_flags table_flags(void) const= 0;
+ virtual int external_lock(THD *thd __attribute__((unused)),
+ int lock_type __attribute__((unused)))
+ {
+ return 0;
+ }
+ virtual void release_auto_increment() { return; };
+ virtual int check_for_upgrade(HA_CHECK_OPT *check_opt)
+ { return 0; }
+ virtual int check(THD* thd, HA_CHECK_OPT* check_opt)
+ { return -1; }
+ virtual int repair(THD* thd, HA_CHECK_OPT* check_opt)
+ { return -1; }
+ 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,
+ enum ha_rkey_function find_flag)
+ { return 131; }
+ virtual int index_read_last(uchar * buf, const uchar * key, uint key_len)
+ { return ((_my_thread_var())->thr_errno= 131); }
+ virtual int bulk_update_row(const uchar *old_data, uchar *new_data,
+ uint *dup_key_found)
+ {
+ assert((0));
+ return 131;
+ }
+ virtual int delete_all_rows()
+ { return ((_my_thread_var())->thr_errno=131); }
+ virtual int reset_auto_increment(ulonglong value)
+ { return 131; }
+ virtual int backup(THD* thd, HA_CHECK_OPT* check_opt)
+ { return -1; }
+ virtual int restore(THD* thd, HA_CHECK_OPT* check_opt)
+ { return -1; }
+ virtual int optimize(THD* thd, HA_CHECK_OPT* check_opt)
+ { return -1; }
+ virtual int analyze(THD* thd, HA_CHECK_OPT* check_opt)
+ { return -1; }
+ virtual In_C_you_should_use_my_bool_instead() check_and_repair(THD *thd) { return (1); }
+ virtual int disable_indexes(uint mode) { return 131; }
+ virtual int enable_indexes(uint mode) { return 131; }
+ virtual int discard_or_import_tablespace(my_bool discard)
+ { return ((_my_thread_var())->thr_errno=131); }
+ virtual void prepare_for_alter() { return; }
+ virtual void drop_table(const char *name);
+ virtual int create(const char *name, TABLE *form, HA_CREATE_INFO *info)=0;
+ virtual int create_handler_files(const char *name, const char *old_name,
+ int action_flag, HA_CREATE_INFO *info)
+ { return (0); }
+ virtual int change_partitions(HA_CREATE_INFO *create_info,
+ const char *path,
+ ulonglong *copied,
+ ulonglong *deleted,
+ const uchar *pack_frm_data,
+ size_t pack_frm_len)
+ { return 131; }
+ virtual int drop_partitions(const char *path)
+ { return 131; }
+ virtual int rename_partitions(const char *path)
+ { return 131; }
+ virtual int optimize_partitions(THD *thd)
+ { return 131; }
+ virtual int analyze_partitions(THD *thd)
+ { return 131; }
+ virtual int check_partitions(THD *thd)
+ { return 131; }
+ virtual int repair_partitions(THD *thd)
+ { return 131; }
+};
+extern const char *ha_row_type[];
+extern const char *tx_isolation_names[];
+extern const char *binlog_format_names[];
+extern TYPELIB tx_isolation_typelib;
+extern TYPELIB myisam_stats_method_typelib;
+extern ulong total_ha, total_ha_2pc;
+handlerton *ha_default_handlerton(THD *thd);
+plugin_ref ha_resolve_by_name(THD *thd, const LEX_STRING *name);
+plugin_ref ha_lock_engine(THD *thd, handlerton *hton);
+handlerton *ha_resolve_by_legacy_type(THD *thd, enum legacy_db_type db_type);
+handler *get_new_handler(TABLE_SHARE *share, MEM_ROOT *alloc,
+ handlerton *db_type);
+handlerton *ha_checktype(THD *thd, enum legacy_db_type database_type,
+ In_C_you_should_use_my_bool_instead() no_substitute, In_C_you_should_use_my_bool_instead() report_error);
+static inline enum legacy_db_type ha_legacy_type(const handlerton *db_type)
+{
+ return (db_type == NULL) ? DB_TYPE_UNKNOWN : db_type->db_type;
+}
+static inline const char *ha_resolve_storage_engine_name(const handlerton *db_type)
+{
+ return db_type == NULL ? "UNKNOWN" : hton2plugin[db_type->slot]->name.str;
+}
+static inline In_C_you_should_use_my_bool_instead() ha_check_storage_engine_flag(const handlerton *db_type, uint32 flag)
+{
+ return db_type == NULL ? (0) : ((db_type->flags & flag) ? 1 : 0);
+}
+static inline In_C_you_should_use_my_bool_instead() ha_storage_engine_is_enabled(const handlerton *db_type)
+{
+ return (db_type && db_type->create) ?
+ (db_type->state == SHOW_OPTION_YES) : (0);
+}
+int ha_init_errors(void);
+int ha_init(void);
+int ha_end(void);
+int ha_initialize_handlerton(st_plugin_int *plugin);
+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);
+In_C_you_should_use_my_bool_instead() ha_flush_logs(handlerton *db_type);
+void ha_drop_database(char* path);
+int ha_create_table(THD *thd, const char *path,
+ const char *db, const char *table_name,
+ HA_CREATE_INFO *create_info,
+ In_C_you_should_use_my_bool_instead() update_create_info);
+int ha_delete_table(THD *thd, handlerton *db_type, const char *path,
+ const char *db, const char *alias, In_C_you_should_use_my_bool_instead() generate_warning);
+In_C_you_should_use_my_bool_instead() ha_show_status(THD *thd, handlerton *db_type, enum ha_stat_type stat);
+int ha_create_table_from_engine(THD* thd, const char *db, const char *name);
+int ha_discover(THD* thd, const char* dbname, const char* name,
+ uchar** frmblob, size_t* frmlen);
+int ha_find_files(THD *thd,const char *db,const char *path,
+ const char *wild, In_C_you_should_use_my_bool_instead() dir, List<LEX_STRING>* files);
+int ha_table_exists_in_engine(THD* thd, const char* db, const char* name);
+extern "C" int ha_init_key_cache(const char *name, KEY_CACHE *key_cache);
+int ha_resize_key_cache(KEY_CACHE *key_cache);
+int ha_change_key_cache_param(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);
+int ha_release_temporary_latches(THD *thd);
+int ha_start_consistent_snapshot(THD *thd);
+int ha_commit_or_rollback_by_xid(XID *xid, In_C_you_should_use_my_bool_instead() commit);
+int ha_commit_one_phase(THD *thd, In_C_you_should_use_my_bool_instead() all);
+int ha_rollback_trans(THD *thd, In_C_you_should_use_my_bool_instead() all);
+int ha_prepare(THD *thd);
+int ha_recover(HASH *commit_list);
+int ha_commit_trans(THD *thd, In_C_you_should_use_my_bool_instead() all);
+int ha_autocommit_or_rollback(THD *thd, int error);
+int ha_enable_transaction(THD *thd, In_C_you_should_use_my_bool_instead() on);
+int ha_rollback_to_savepoint(THD *thd, SAVEPOINT *sv);
+int ha_savepoint(THD *thd, SAVEPOINT *sv);
+int ha_release_savepoint(THD *thd, SAVEPOINT *sv);
+void trans_register_ha(THD *thd, In_C_you_should_use_my_bool_instead() all, handlerton *ht);
+#include "parse_file.h"
+enum file_opt_type {
+ FILE_OPTIONS_STRING,
+ FILE_OPTIONS_ESTRING,
+ FILE_OPTIONS_ULONGLONG,
+ FILE_OPTIONS_REV,
+ FILE_OPTIONS_TIMESTAMP,
+ FILE_OPTIONS_STRLIST,
+ FILE_OPTIONS_ULLLIST
+};
+struct File_option
+{
+ LEX_STRING name;
+ int offset;
+ file_opt_type type;
+};
+class Unknown_key_hook
+{
+public:
+ Unknown_key_hook() {}
+ virtual ~Unknown_key_hook() {}
+ virtual In_C_you_should_use_my_bool_instead() process_unknown_string(char *&unknown_key, uchar* base,
+ MEM_ROOT *mem_root, char *end)= 0;
+};
+class File_parser_dummy_hook: public Unknown_key_hook
+{
+public:
+ File_parser_dummy_hook() {}
+ virtual In_C_you_should_use_my_bool_instead() process_unknown_string(char *&unknown_key, uchar* base,
+ MEM_ROOT *mem_root, char *end);
+};
+extern File_parser_dummy_hook file_parser_dummy_hook;
+In_C_you_should_use_my_bool_instead() get_file_options_ulllist(char *&ptr, char *end, char *line,
+ uchar* base, File_option *parameter,
+ MEM_ROOT *mem_root);
+char *
+parse_escaped_string(char *ptr, char *end, MEM_ROOT *mem_root, LEX_STRING *str);
+class File_parser;
+File_parser *sql_parse_prepare(const LEX_STRING *file_name,
+ MEM_ROOT *mem_root, In_C_you_should_use_my_bool_instead() bad_format_errors);
+my_bool
+sql_create_definition_file(const LEX_STRING *dir, const LEX_STRING *file_name,
+ const LEX_STRING *type,
+ uchar* base, File_option *parameters, uint versions);
+my_bool rename_in_schema_file(const char *schema, const char *old_name,
+ const char *new_name, ulonglong revision,
+ uint num_view_backups);
+class File_parser: public Sql_alloc
+{
+ char *buff, *start, *end;
+ LEX_STRING file_type;
+ my_bool content_ok;
+public:
+ File_parser() :buff(0), start(0), end(0), content_ok(0)
+ { file_type.str= 0; file_type.length= 0; }
+ my_bool ok() { return content_ok; }
+ LEX_STRING *type() { return &file_type; }
+ my_bool parse(uchar* base, MEM_ROOT *mem_root,
+ struct File_option *parameters, uint required,
+ Unknown_key_hook *hook);
+ friend File_parser *sql_parse_prepare(const LEX_STRING *file_name,
+ MEM_ROOT *mem_root,
+ In_C_you_should_use_my_bool_instead() bad_format_errors);
+};
+#include "table.h"
+class Item;
+class Item_subselect;
+class GRANT_TABLE;
+class st_select_lex_unit;
+class st_select_lex;
+class partition_info;
+class COND_EQUAL;
+class Security_context;
+class View_creation_ctx : public Default_object_creation_ctx,
+ public Sql_alloc
+{
+public:
+ static View_creation_ctx *create(THD *thd);
+ static View_creation_ctx *create(THD *thd,
+ TABLE_LIST *view);
+private:
+ View_creation_ctx(THD *thd)
+ : Default_object_creation_ctx(thd)
+ { }
+};
+typedef struct st_order {
+ struct st_order *next;
+ Item **item;
+ Item *item_ptr;
+ Item **item_copy;
+ int counter;
+ In_C_you_should_use_my_bool_instead() asc;
+ In_C_you_should_use_my_bool_instead() free_me;
+ In_C_you_should_use_my_bool_instead() in_field_list;
+ In_C_you_should_use_my_bool_instead() counter_used;
+ Field *field;
+ char *buff;
+ table_map used, depend_map;
+} ORDER;
+typedef struct st_grant_info
+{
+ GRANT_TABLE *grant_table;
+ uint version;
+ ulong privilege;
+ ulong want_privilege;
+ ulong orig_want_privilege;
+} GRANT_INFO;
+enum tmp_table_type
+{
+ NO_TMP_TABLE, NON_TRANSACTIONAL_TMP_TABLE, TRANSACTIONAL_TMP_TABLE,
+ INTERNAL_TMP_TABLE, SYSTEM_TMP_TABLE
+};
+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 };
+typedef struct st_filesort_info
+{
+ IO_CACHE *io_cache;
+ uchar **sort_keys;
+ uchar *buffpek;
+ uint buffpek_len;
+ uchar *addon_buf;
+ size_t addon_length;
+ struct st_sort_addon_field *addon_field;
+ void (*unpack)(struct st_sort_addon_field *, uchar *);
+ uchar *record_pointers;
+ ha_rows found_records;
+} FILESORT_INFO;
+enum timestamp_auto_set_type
+{
+ TIMESTAMP_NO_AUTO_SET= 0, TIMESTAMP_AUTO_SET_ON_INSERT= 1,
+ TIMESTAMP_AUTO_SET_ON_UPDATE= 2, TIMESTAMP_AUTO_SET_ON_BOTH= 3
+};
+class Field_timestamp;
+class Field_blob;
+class Table_triggers_list;
+enum enum_table_category
+{
+ TABLE_UNKNOWN_CATEGORY=0,
+ TABLE_CATEGORY_TEMPORARY=1,
+ TABLE_CATEGORY_USER=2,
+ TABLE_CATEGORY_SYSTEM=3,
+ TABLE_CATEGORY_INFORMATION=4,
+ TABLE_CATEGORY_PERFORMANCE=5
+};
+typedef enum enum_table_category TABLE_CATEGORY;
+TABLE_CATEGORY get_table_category(const LEX_STRING *db,
+ const LEX_STRING *name);
+typedef struct st_table_share
+{
+ st_table_share() {}
+ TABLE_CATEGORY table_category;
+ HASH name_hash;
+ MEM_ROOT mem_root;
+ TYPELIB keynames;
+ TYPELIB fieldnames;
+ TYPELIB *intervals;
+ pthread_mutex_t mutex;
+ pthread_cond_t cond;
+ struct st_table_share *next,
+ **prev;
+ Field **field;
+ Field **found_next_number_field;
+ Field *timestamp_field;
+ KEY *key_info;
+ uint *blob_field;
+ uchar *default_values;
+ LEX_STRING comment;
+ CHARSET_INFO *table_charset;
+ MY_BITMAP all_set;
+ LEX_STRING table_cache_key;
+ LEX_STRING db;
+ LEX_STRING table_name;
+ LEX_STRING path;
+ LEX_STRING normalized_path;
+ LEX_STRING connect_string;
+ key_map keys_in_use;
+ key_map keys_for_keyread;
+ ha_rows min_rows, max_rows;
+ ulong avg_row_length;
+ ulong raid_chunksize;
+ ulong version, mysql_version;
+ ulong timestamp_offset;
+ ulong reclength;
+ plugin_ref db_plugin;
+ inline handlerton *db_type() const
+ {
+ return db_plugin ? ((handlerton*)((db_plugin)[0]->data)) : NULL;
+ }
+ enum row_type row_type;
+ enum tmp_table_type tmp_table;
+ enum ha_choice transactional;
+ enum ha_choice page_checksum;
+ uint ref_count;
+ uint open_count;
+ uint blob_ptr_size;
+ uint key_block_size;
+ uint null_bytes, last_null_bit_pos;
+ uint fields;
+ uint rec_buff_length;
+ uint keys, key_parts;
+ uint max_key_length, max_unique_length, total_key_length;
+ uint uniques;
+ uint null_fields;
+ uint blob_fields;
+ uint timestamp_field_offset;
+ uint varchar_fields;
+ uint db_create_options;
+ uint db_options_in_use;
+ uint db_record_offset;
+ uint raid_type, raid_chunks;
+ uint rowid_field_offset;
+ uint primary_key;
+ uint next_number_index;
+ uint next_number_key_offset;
+ uint next_number_keypart;
+ uint error, open_errno, errarg;
+ uint column_bitmap_size;
+ uchar frm_version;
+ In_C_you_should_use_my_bool_instead() null_field_first;
+ In_C_you_should_use_my_bool_instead() system;
+ In_C_you_should_use_my_bool_instead() crypted;
+ In_C_you_should_use_my_bool_instead() db_low_byte_first;
+ In_C_you_should_use_my_bool_instead() crashed;
+ In_C_you_should_use_my_bool_instead() is_view;
+ In_C_you_should_use_my_bool_instead() name_lock, replace_with_name_lock;
+ In_C_you_should_use_my_bool_instead() waiting_on_cond;
+ ulong table_map_id;
+ ulonglong table_map_version;
+ int cached_row_logging_check;
+ void set_table_cache_key(char *key_buff, uint key_length)
+ {
+ table_cache_key.str= key_buff;
+ table_cache_key.length= key_length;
+ db.str= table_cache_key.str;
+ db.length= strlen(db.str);
+ table_name.str= db.str + db.length + 1;
+ table_name.length= strlen(table_name.str);
+ }
+ void set_table_cache_key(char *key_buff, const char *key, uint key_length)
+ {
+ memcpy(key_buff, key, key_length);
+ set_table_cache_key(key_buff, key_length);
+ }
+ inline In_C_you_should_use_my_bool_instead() honor_global_locks()
+ {
+ return ((table_category == TABLE_CATEGORY_USER)
+ || (table_category == TABLE_CATEGORY_SYSTEM));
+ }
+ inline In_C_you_should_use_my_bool_instead() require_write_privileges()
+ {
+ return (table_category == TABLE_CATEGORY_PERFORMANCE);
+ }
+ inline ulong get_table_def_version()
+ {
+ return table_map_id;
+ }
+ enum enum_table_ref_type get_table_ref_type() const
+ {
+ if (is_view)
+ return TABLE_REF_VIEW;
+ switch (tmp_table) {
+ case NO_TMP_TABLE:
+ return TABLE_REF_BASE_TABLE;
+ case SYSTEM_TMP_TABLE:
+ return TABLE_REF_I_S_TABLE;
+ default:
+ return TABLE_REF_TMP_TABLE;
+ }
+ }
+ ulong get_table_ref_version() const
+ {
+ return (tmp_table == SYSTEM_TMP_TABLE || is_view) ? 0 : table_map_id;
+ }
+} TABLE_SHARE;
+extern ulong refresh_version;
+enum index_hint_type
+{
+ INDEX_HINT_IGNORE,
+ INDEX_HINT_USE,
+ INDEX_HINT_FORCE
+};
+struct st_table {
+ st_table() {}
+ TABLE_SHARE *s;
+ handler *file;
+ struct st_table *next, *prev;
+ struct st_table *parent;
+ TABLE_LIST *child_l;
+ TABLE_LIST **child_last_l;
+ THD *in_use;
+ Field **field;
+ uchar *record[2];
+ uchar *write_row_record;
+ uchar *insert_values;
+ key_map covering_keys;
+ key_map quick_keys, merge_keys;
+ key_map keys_in_use_for_query;
+ key_map keys_in_use_for_group_by;
+ key_map keys_in_use_for_order_by;
+ KEY *key_info;
+ Field *next_number_field;
+ Field *found_next_number_field;
+ Field_timestamp *timestamp_field;
+ Table_triggers_list *triggers;
+ TABLE_LIST *pos_in_table_list;
+ ORDER *group;
+ const char *alias;
+ uchar *null_flags;
+ my_bitmap_map *bitmap_init_value;
+ MY_BITMAP def_read_set, def_write_set, tmp_set;
+ MY_BITMAP *read_set, *write_set;
+ query_id_t query_id;
+ ha_rows quick_rows[64];
+ key_part_map const_key_parts[64];
+ uint quick_key_parts[64];
+ uint quick_n_ranges[64];
+ ha_rows quick_condition_rows;
+ timestamp_auto_set_type timestamp_field_type;
+ table_map map;
+ uint lock_position;
+ uint lock_data_start;
+ uint lock_count;
+ uint tablenr,used_fields;
+ uint temp_pool_slot;
+ uint status;
+ uint db_stat;
+ uint derived_select_number;
+ int current_lock;
+ my_bool copy_blobs;
+ uint maybe_null;
+ my_bool null_row;
+ my_bool force_index;
+ my_bool distinct,const_table,no_rows;
+ my_bool key_read, no_keyread;
+ my_bool open_placeholder;
+ my_bool locked_by_logger;
+ my_bool no_replicate;
+ my_bool locked_by_name;
+ my_bool fulltext_searched;
+ my_bool no_cache;
+ my_bool open_by_handler;
+ my_bool auto_increment_field_not_null;
+ my_bool insert_or_update;
+ my_bool alias_name_used;
+ my_bool get_fields_in_item_tree;
+ my_bool children_attached;
+ REGINFO reginfo;
+ MEM_ROOT mem_root;
+ GRANT_INFO grant;
+ FILESORT_INFO sort;
+ In_C_you_should_use_my_bool_instead() fill_item_list(List<Item> *item_list) const;
+ void reset_item_list(List<Item> *item_list) const;
+ void clear_column_bitmaps(void);
+ void prepare_for_position(void);
+ void mark_columns_used_by_index_no_reset(uint index, MY_BITMAP *map);
+ void mark_columns_used_by_index(uint index);
+ void restore_column_maps_after_mark_index();
+ void mark_auto_increment_column(void);
+ void mark_columns_needed_for_update(void);
+ void mark_columns_needed_for_delete(void);
+ void mark_columns_needed_for_insert(void);
+ inline void column_bitmaps_set(MY_BITMAP *read_set_arg,
+ MY_BITMAP *write_set_arg)
+ {
+ read_set= read_set_arg;
+ write_set= write_set_arg;
+ if (file)
+ file->column_bitmaps_signal();
+ }
+ inline void column_bitmaps_set_no_signal(MY_BITMAP *read_set_arg,
+ MY_BITMAP *write_set_arg)
+ {
+ read_set= read_set_arg;
+ write_set= write_set_arg;
+ }
+ inline void use_all_columns()
+ {
+ column_bitmaps_set(&s->all_set, &s->all_set);
+ }
+ inline void default_column_bitmaps()
+ {
+ read_set= &def_read_set;
+ write_set= &def_write_set;
+ }
+ inline In_C_you_should_use_my_bool_instead() is_name_opened() { return db_stat || open_placeholder; }
+ inline In_C_you_should_use_my_bool_instead() needs_reopen_or_name_lock()
+ { return s->version != refresh_version; }
+ In_C_you_should_use_my_bool_instead() is_children_attached(void);
+};
+enum enum_schema_table_state
+{
+ NOT_PROCESSED= 0,
+ PROCESSED_BY_CREATE_SORT_INDEX,
+ PROCESSED_BY_JOIN_EXEC
+};
+typedef struct st_foreign_key_info
+{
+ LEX_STRING *forein_id;
+ LEX_STRING *referenced_db;
+ LEX_STRING *referenced_table;
+ LEX_STRING *update_method;
+ LEX_STRING *delete_method;
+ LEX_STRING *referenced_key_name;
+ List<LEX_STRING> foreign_fields;
+ List<LEX_STRING> referenced_fields;
+} FOREIGN_KEY_INFO;
+enum enum_schema_tables
+{
+ SCH_CHARSETS= 0,
+ 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_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_TRIGGERS,
+ SCH_USER_PRIVILEGES,
+ SCH_VARIABLES,
+ SCH_VIEWS
+};
+typedef struct st_field_info
+{
+ const char* field_name;
+ uint field_length;
+ enum enum_field_types field_type;
+ int value;
+ uint field_flags;
+ const char* old_name;
+ uint open_method;
+} ST_FIELD_INFO;
+struct TABLE_LIST;
+typedef class Item COND;
+typedef struct st_schema_table
+{
+ const char* table_name;
+ ST_FIELD_INFO *fields_info;
+ TABLE *(*create_table) (THD *thd, TABLE_LIST *table_list);
+ int (*fill_table) (THD *thd, TABLE_LIST *tables, COND *cond);
+ int (*old_format) (THD *thd, struct st_schema_table *schema_table);
+ int (*process_table) (THD *thd, TABLE_LIST *tables, TABLE *table,
+ In_C_you_should_use_my_bool_instead() res, LEX_STRING *db_name, LEX_STRING *table_name);
+ int idx_field1, idx_field2;
+ In_C_you_should_use_my_bool_instead() hidden;
+ uint i_s_requested_object;
+} ST_SCHEMA_TABLE;
+struct st_lex;
+class select_union;
+class TMP_TABLE_PARAM;
+Item *create_view_field(THD *thd, TABLE_LIST *view, Item **field_ref,
+ const char *name);
+struct Field_translator
+{
+ Item *item;
+ const char *name;
+};
+class Natural_join_column: public Sql_alloc
+{
+public:
+ Field_translator *view_field;
+ Field *table_field;
+ TABLE_LIST *table_ref;
+ In_C_you_should_use_my_bool_instead() is_common;
+public:
+ Natural_join_column(Field_translator *field_param, TABLE_LIST *tab);
+ Natural_join_column(Field *field_param, TABLE_LIST *tab);
+ const char *name();
+ Item *create_item(THD *thd);
+ Field *field();
+ const char *table_name();
+ const char *db_name();
+ GRANT_INFO *grant();
+};
+class Index_hint;
+struct TABLE_LIST
+{
+ TABLE_LIST() {}
+ inline void init_one_table(const char *db_name_arg,
+ const char *table_name_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;
+ lock_type= lock_type_arg;
+ }
+ TABLE_LIST *next_local;
+ TABLE_LIST *next_global, **prev_global;
+ char *db, *alias, *table_name, *schema_table_name;
+ char *option;
+ Item *on_expr;
+ Item *prep_on_expr;
+ COND_EQUAL *cond_equal;
+ TABLE_LIST *natural_join;
+ In_C_you_should_use_my_bool_instead() is_natural_join;
+ List<String> *join_using_fields;
+ List<Natural_join_column> *join_columns;
+ In_C_you_should_use_my_bool_instead() is_join_columns_complete;
+ TABLE_LIST *next_name_resolution_table;
+ List<Index_hint> *index_hints;
+ TABLE *table;
+ uint table_id;
+ select_union *derived_result;
+ TABLE_LIST *correspondent_table;
+ st_select_lex_unit *derived;
+ ST_SCHEMA_TABLE *schema_table;
+ st_select_lex *schema_select_lex;
+ In_C_you_should_use_my_bool_instead() schema_table_reformed;
+ TMP_TABLE_PARAM *schema_table_param;
+ st_select_lex *select_lex;
+ st_lex *view;
+ Field_translator *field_translation;
+ Field_translator *field_translation_end;
+ TABLE_LIST *merge_underlying_list;
+ List<TABLE_LIST> *view_tables;
+ TABLE_LIST *belong_to_view;
+ TABLE_LIST *referencing_view;
+ TABLE_LIST *parent_l;
+ Security_context *security_ctx;
+ Security_context *view_sctx;
+ In_C_you_should_use_my_bool_instead() allowed_show;
+ TABLE_LIST *next_leaf;
+ Item *where;
+ Item *check_option;
+ LEX_STRING select_stmt;
+ LEX_STRING md5;
+ LEX_STRING source;
+ LEX_STRING view_db;
+ LEX_STRING view_name;
+ LEX_STRING timestamp;
+ st_lex_user definer;
+ ulonglong file_version;
+ ulonglong updatable_view;
+ ulonglong revision;
+ ulonglong algorithm;
+ ulonglong view_suid;
+ ulonglong with_check;
+ uint8 effective_with_check;
+ uint8 effective_algorithm;
+ GRANT_INFO grant;
+ ulonglong engine_data;
+ qc_engine_callback callback_func;
+ thr_lock_type lock_type;
+ uint outer_join;
+ uint shared;
+ size_t db_length;
+ size_t table_name_length;
+ In_C_you_should_use_my_bool_instead() updatable;
+ In_C_you_should_use_my_bool_instead() straight;
+ In_C_you_should_use_my_bool_instead() updating;
+ In_C_you_should_use_my_bool_instead() force_index;
+ In_C_you_should_use_my_bool_instead() ignore_leaves;
+ table_map dep_tables;
+ table_map on_expr_dep_tables;
+ struct st_nested_join *nested_join;
+ TABLE_LIST *embedding;
+ List<TABLE_LIST> *join_list;
+ In_C_you_should_use_my_bool_instead() cacheable_table;
+ In_C_you_should_use_my_bool_instead() table_in_first_from_clause;
+ In_C_you_should_use_my_bool_instead() skip_temporary;
+ In_C_you_should_use_my_bool_instead() contain_auto_increment;
+ In_C_you_should_use_my_bool_instead() multitable_view;
+ In_C_you_should_use_my_bool_instead() compact_view_format;
+ In_C_you_should_use_my_bool_instead() where_processed;
+ In_C_you_should_use_my_bool_instead() check_option_processed;
+ enum frm_type_enum required_type;
+ handlerton *db_type;
+ char timestamp_buffer[20];
+ In_C_you_should_use_my_bool_instead() prelocking_placeholder;
+ In_C_you_should_use_my_bool_instead() create;
+ In_C_you_should_use_my_bool_instead() internal_tmp_table;
+ View_creation_ctx *view_creation_ctx;
+ LEX_STRING view_client_cs_name;
+ LEX_STRING view_connection_cl_name;
+ LEX_STRING view_body_utf8;
+ uint8 trg_event_map;
+ uint i_s_requested_object;
+ In_C_you_should_use_my_bool_instead() has_db_lookup_value;
+ In_C_you_should_use_my_bool_instead() has_table_lookup_value;
+ uint table_open_method;
+ enum enum_schema_table_state schema_table_state;
+ void calc_md5(char *buffer);
+ void set_underlying_merge();
+ int view_check_option(THD *thd, In_C_you_should_use_my_bool_instead() ignore_failure);
+ In_C_you_should_use_my_bool_instead() setup_underlying(THD *thd);
+ void cleanup_items();
+ In_C_you_should_use_my_bool_instead() placeholder()
+ {
+ return derived || view || schema_table || create && !table->db_stat ||
+ !table;
+ }
+ void print(THD *thd, String *str, enum_query_type query_type);
+ In_C_you_should_use_my_bool_instead() check_single_table(TABLE_LIST **table, table_map map,
+ TABLE_LIST *view);
+ In_C_you_should_use_my_bool_instead() set_insert_values(MEM_ROOT *mem_root);
+ void hide_view_error(THD *thd);
+ TABLE_LIST *find_underlying_table(TABLE *table);
+ TABLE_LIST *first_leaf_for_name_resolution();
+ TABLE_LIST *last_leaf_for_name_resolution();
+ In_C_you_should_use_my_bool_instead() is_leaf_for_name_resolution();
+ inline TABLE_LIST *top_table()
+ { return belong_to_view ? belong_to_view : this; }
+ inline In_C_you_should_use_my_bool_instead() prepare_check_option(THD *thd)
+ {
+ In_C_you_should_use_my_bool_instead() res= (0);
+ if (effective_with_check)
+ res= prep_check_option(thd, effective_with_check);
+ return res;
+ }
+ inline In_C_you_should_use_my_bool_instead() prepare_where(THD *thd, Item **conds,
+ In_C_you_should_use_my_bool_instead() no_where_clause)
+ {
+ if (effective_algorithm == 2)
+ return prep_where(thd, conds, no_where_clause);
+ return (0);
+ }
+ void register_want_access(ulong want_access);
+ In_C_you_should_use_my_bool_instead() prepare_security(THD *thd);
+ Security_context *find_view_security_context(THD *thd);
+ In_C_you_should_use_my_bool_instead() prepare_view_securety_context(THD *thd);
+ void reinit_before_use(THD *thd);
+ Item_subselect *containing_subselect();
+ In_C_you_should_use_my_bool_instead() process_index_hints(TABLE *table);
+ 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;
+ }
+ inline
+ In_C_you_should_use_my_bool_instead() is_table_ref_id_equal(TABLE_SHARE *s) const
+ {
+ return (m_table_ref_type == s->get_table_ref_type() &&
+ m_table_ref_version == s->get_table_ref_version());
+ }
+ inline
+ void set_table_ref_id(TABLE_SHARE *s)
+ {
+ m_table_ref_type= s->get_table_ref_type();
+ m_table_ref_version= s->get_table_ref_version();
+ }
+private:
+ In_C_you_should_use_my_bool_instead() prep_check_option(THD *thd, uint8 check_opt_type);
+ In_C_you_should_use_my_bool_instead() prep_where(THD *thd, Item **conds, In_C_you_should_use_my_bool_instead() no_where_clause);
+ ulong child_def_version;
+ enum enum_table_ref_type m_table_ref_type;
+ ulong m_table_ref_version;
+};
+class Item;
+class Field_iterator: public Sql_alloc
+{
+public:
+ Field_iterator() {}
+ virtual ~Field_iterator() {}
+ virtual void set(TABLE_LIST *)= 0;
+ virtual void next()= 0;
+ virtual In_C_you_should_use_my_bool_instead() end_of_fields()= 0;
+ virtual const char *name()= 0;
+ virtual Item *create_item(THD *)= 0;
+ virtual Field *field()= 0;
+};
+class Field_iterator_table: public Field_iterator
+{
+ Field **ptr;
+public:
+ Field_iterator_table() :ptr(0) {}
+ void set(TABLE_LIST *table) { ptr= table->table->field; }
+ void set_table(TABLE *table) { ptr= table->field; }
+ void next() { ptr++; }
+ In_C_you_should_use_my_bool_instead() end_of_fields() { return *ptr == 0; }
+ const char *name();
+ Item *create_item(THD *thd);
+ Field *field() { return *ptr; }
+};
+class Field_iterator_view: public Field_iterator
+{
+ Field_translator *ptr, *array_end;
+ TABLE_LIST *view;
+public:
+ Field_iterator_view() :ptr(0), array_end(0) {}
+ void set(TABLE_LIST *table);
+ void next() { ptr++; }
+ In_C_you_should_use_my_bool_instead() end_of_fields() { return ptr == array_end; }
+ const char *name();
+ Item *create_item(THD *thd);
+ Item **item_ptr() {return &ptr->item; }
+ Field *field() { return 0; }
+ inline Item *item() { return ptr->item; }
+ Field_translator *field_translator() { return ptr; }
+};
+class Field_iterator_natural_join: public Field_iterator
+{
+ List_iterator_fast<Natural_join_column> column_ref_it;
+ Natural_join_column *cur_column_ref;
+public:
+ Field_iterator_natural_join() :cur_column_ref(NULL) {}
+ ~Field_iterator_natural_join() {}
+ void set(TABLE_LIST *table);
+ void next();
+ In_C_you_should_use_my_bool_instead() end_of_fields() { return !cur_column_ref; }
+ const char *name() { return cur_column_ref->name(); }
+ Item *create_item(THD *thd) { return cur_column_ref->create_item(thd); }
+ Field *field() { return cur_column_ref->field(); }
+ Natural_join_column *column_ref() { return cur_column_ref; }
+};
+class Field_iterator_table_ref: public Field_iterator
+{
+ TABLE_LIST *table_ref, *first_leaf, *last_leaf;
+ Field_iterator_table table_field_it;
+ Field_iterator_view view_field_it;
+ Field_iterator_natural_join natural_join_it;
+ Field_iterator *field_it;
+ void set_field_iterator();
+public:
+ Field_iterator_table_ref() :field_it(NULL) {}
+ void set(TABLE_LIST *table);
+ void next();
+ In_C_you_should_use_my_bool_instead() end_of_fields()
+ { return (table_ref == last_leaf && field_it->end_of_fields()); }
+ const char *name() { return field_it->name(); }
+ const char *table_name();
+ const char *db_name();
+ GRANT_INFO *grant();
+ Item *create_item(THD *thd) { return field_it->create_item(thd); }
+ Field *field() { return field_it->field(); }
+ Natural_join_column *get_or_create_column_ref(TABLE_LIST *parent_table_ref);
+ Natural_join_column *get_natural_column_ref();
+};
+typedef struct st_nested_join
+{
+ List<TABLE_LIST> join_list;
+ table_map used_tables;
+ table_map not_null_tables;
+ struct st_join_table *first_nested;
+ uint counter;
+ nested_join_map nj_map;
+} NESTED_JOIN;
+typedef struct st_changed_table_list
+{
+ struct st_changed_table_list *next;
+ char *key;
+ uint32 key_length;
+} CHANGED_TABLE_LIST;
+typedef struct st_open_table_list{
+ struct st_open_table_list *next;
+ char *db,*table;
+ uint32 in_use,locked;
+} OPEN_TABLE_LIST;
+typedef struct st_table_field_w_type
+{
+ LEX_STRING name;
+ LEX_STRING type;
+ LEX_STRING cset;
+} TABLE_FIELD_W_TYPE;
+my_bool
+table_check_intact(TABLE *table, const uint table_f_count,
+ const TABLE_FIELD_W_TYPE *table_def);
+static inline my_bitmap_map *tmp_use_all_columns(TABLE *table,
+ MY_BITMAP *bitmap)
+{
+ my_bitmap_map *old= bitmap->bitmap;
+ bitmap->bitmap= table->s->all_set.bitmap;
+ return old;
+}
+static inline void tmp_restore_column_map(MY_BITMAP *bitmap,
+ my_bitmap_map *old)
+{
+ bitmap->bitmap= old;
+}
+static inline my_bitmap_map *dbug_tmp_use_all_columns(TABLE *table,
+ MY_BITMAP *bitmap)
+{
+ return tmp_use_all_columns(table, bitmap);
+}
+static inline void dbug_tmp_restore_column_map(MY_BITMAP *bitmap,
+ my_bitmap_map *old)
+{
+ tmp_restore_column_map(bitmap, old);
+}
+size_t max_row_length(TABLE *table, const uchar *data);
+#include "sql_error.h"
+class MYSQL_ERROR: public Sql_alloc
+{
+public:
+ enum enum_warning_level
+ { WARN_LEVEL_NOTE, WARN_LEVEL_WARN, WARN_LEVEL_ERROR, WARN_LEVEL_END};
+ 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)
+ {
+ if (msg_arg)
+ set_msg(thd, msg_arg);
+ }
+ 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);
+void push_warning_printf(THD *thd, MYSQL_ERROR::enum_warning_level level,
+ uint code, const char *format, ...);
+void mysql_reset_errors(THD *thd, In_C_you_should_use_my_bool_instead() force);
+In_C_you_should_use_my_bool_instead() mysqld_show_warnings(THD *thd, ulong levels_to_show);
+extern const LEX_STRING warning_level_names[];
+#include "field.h"
+const uint32 max_field_size= (uint32) 4294967295U;
+class Send_field;
+class Protocol;
+class Create_field;
+struct st_cache_field;
+int field_conv(Field *to,Field *from);
+inline uint get_enum_pack_length(int elements)
+{
+ return elements < 256 ? 1 : 2;
+}
+inline uint get_set_pack_length(int elements)
+{
+ uint len= (elements + 7) / 8;
+ return len > 4 ? 8 : len;
+}
+class Field
+{
+ Field(const Item &);
+ void operator=(Field &);
+public:
+ static void *operator new(size_t size) {return sql_alloc(size); }
+ static void operator delete(void *ptr_arg, size_t size) { ; }
+ uchar *ptr;
+ uchar *null_ptr;
+ struct st_table *table;
+ struct st_table *orig_table;
+ const char **table_name, *field_name;
+ LEX_STRING comment;
+ key_map key_start, part_of_key, part_of_key_not_clustered;
+ key_map part_of_sortkey;
+ enum utype { NONE,DATE,SHIELD,NOEMPTY,CASEUP,PNR,BGNR,PGNR,YES,NO,REL,
+ CHECK,EMPTY,UNKNOWN_FIELD,CASEDN,NEXT_NUMBER,INTERVAL_FIELD,
+ BIT_FIELD, TIMESTAMP_OLD_FIELD, CAPITALIZE, BLOB_FIELD,
+ TIMESTAMP_DN_FIELD, TIMESTAMP_UN_FIELD, TIMESTAMP_DNUN_FIELD};
+ enum geometry_type
+ {
+ GEOM_GEOMETRY = 0, GEOM_POINT = 1, GEOM_LINESTRING = 2, GEOM_POLYGON = 3,
+ GEOM_MULTIPOINT = 4, GEOM_MULTILINESTRING = 5, GEOM_MULTIPOLYGON = 6,
+ GEOM_GEOMETRYCOLLECTION = 7
+ };
+ enum imagetype { itRAW, itMBR};
+ utype unireg_check;
+ uint32 field_length;
+ uint32 flags;
+ uint16 field_index;
+ uchar null_bit;
+ In_C_you_should_use_my_bool_instead() is_created_from_null_item;
+ Field(uchar *ptr_arg,uint32 length_arg,uchar *null_ptr_arg,
+ uchar null_bit_arg, utype unireg_check_arg,
+ const char *field_name_arg);
+ virtual ~Field() {}
+ virtual int store(const char *to, uint length,CHARSET_INFO *cs)=0;
+ virtual int store(double nr)=0;
+ virtual int store(longlong nr, In_C_you_should_use_my_bool_instead() unsigned_val)=0;
+ virtual int store_decimal(const my_decimal *d)=0;
+ virtual int store_time(MYSQL_TIME *ltime, timestamp_type t_type);
+ int store(const char *to, uint length, CHARSET_INFO *cs,
+ enum_check_fields check_level);
+ virtual double val_real(void)=0;
+ virtual longlong val_int(void)=0;
+ virtual my_decimal *val_decimal(my_decimal *);
+ inline String *val_str(String *str) { return val_str(str, str); }
+ virtual String *val_str(String*,String *)=0;
+ String *val_int_as_str(String *val_buffer, my_bool unsigned_flag);
+ virtual In_C_you_should_use_my_bool_instead() str_needs_quotes() { return (0); }
+ virtual Item_result result_type () const=0;
+ virtual Item_result cmp_type () const { return result_type(); }
+ virtual Item_result cast_to_int_type () const { return result_type(); }
+ static In_C_you_should_use_my_bool_instead() type_can_have_key_part(enum_field_types);
+ static enum_field_types field_type_merge(enum_field_types, enum_field_types);
+ static Item_result result_merge_type(enum_field_types);
+ virtual In_C_you_should_use_my_bool_instead() eq(Field *field)
+ {
+ return (ptr == field->ptr && null_ptr == field->null_ptr &&
+ null_bit == field->null_bit);
+ }
+ virtual In_C_you_should_use_my_bool_instead() eq_def(Field *field);
+ virtual uint32 pack_length() const { return (uint32) field_length; }
+ virtual uint32 pack_length_in_rec() const { return pack_length(); }
+ virtual int compatible_field_size(uint field_metadata);
+ virtual uint pack_length_from_metadata(uint field_metadata)
+ { 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); }
+ virtual uint32 data_length() { return pack_length(); }
+ virtual uint32 sort_length() const { return pack_length(); }
+ virtual uint32 max_data_length() const {
+ return pack_length();
+ };
+ virtual int reset(void) { bzero(ptr,pack_length()); return 0; }
+ virtual void reset_fields() {}
+ virtual void set_default()
+ {
+ my_ptrdiff_t l_offset= (my_ptrdiff_t) (table->s->default_values -
+ table->record[0]);
+ memcpy(ptr, ptr + l_offset, pack_length());
+ if (null_ptr)
+ *null_ptr= ((*null_ptr & (uchar) ~null_bit) |
+ null_ptr[l_offset] & null_bit);
+ }
+ virtual In_C_you_should_use_my_bool_instead() binary() const { return 1; }
+ virtual In_C_you_should_use_my_bool_instead() zero_pack() const { return 1; }
+ virtual enum ha_base_keytype key_type() const { return HA_KEYTYPE_BINARY; }
+ virtual uint32 key_length() const { return pack_length(); }
+ virtual enum_field_types type() const =0;
+ virtual enum_field_types real_type() const { return type(); }
+ inline int cmp(const uchar *str) { return cmp(ptr,str); }
+ virtual int cmp_max(const uchar *a, const uchar *b, uint max_len)
+ { return cmp(a, b); }
+ virtual int cmp(const uchar *,const uchar *)=0;
+ virtual int cmp_binary(const uchar *a,const uchar *b, uint32 max_length=~0L)
+ { return memcmp(a,b,pack_length()); }
+ virtual int cmp_offset(uint row_offset)
+ { return cmp(ptr,ptr+row_offset); }
+ virtual int cmp_binary_offset(uint row_offset)
+ { return cmp_binary(ptr, ptr+row_offset); };
+ virtual int key_cmp(const uchar *a,const uchar *b)
+ { return cmp(a, b); }
+ virtual int key_cmp(const uchar *str, uint length)
+ { return cmp(ptr,str); }
+ virtual uint decimals() const { return 0; }
+ virtual void sql_type(String &str) const =0;
+ virtual uint size_of() const =0;
+ inline In_C_you_should_use_my_bool_instead() is_null(my_ptrdiff_t row_offset= 0)
+ { return null_ptr ? (null_ptr[row_offset] & null_bit ? 1 : 0) : table->null_row; }
+ inline In_C_you_should_use_my_bool_instead() is_real_null(my_ptrdiff_t row_offset= 0)
+ { return null_ptr ? (null_ptr[row_offset] & null_bit ? 1 : 0) : 0; }
+ inline In_C_you_should_use_my_bool_instead() is_null_in_record(const uchar *record)
+ {
+ if (!null_ptr)
+ return 0;
+ return ((record[(uint) (null_ptr -table->record[0])] & null_bit) ? 1 : 0);
+ }
+ inline In_C_you_should_use_my_bool_instead() is_null_in_record_with_offset(my_ptrdiff_t offset)
+ {
+ if (!null_ptr)
+ return 0;
+ return ((null_ptr[offset] & null_bit) ? 1 : 0);
+ }
+ inline void set_null(my_ptrdiff_t row_offset= 0)
+ { if (null_ptr) null_ptr[row_offset]|= null_bit; }
+ inline void set_notnull(my_ptrdiff_t row_offset= 0)
+ { if (null_ptr) null_ptr[row_offset]&= (uchar) ~null_bit; }
+ inline In_C_you_should_use_my_bool_instead() maybe_null(void) { return null_ptr != 0 || table->maybe_null; }
+ inline In_C_you_should_use_my_bool_instead() real_maybe_null(void) { return null_ptr != 0; }
+ enum {
+ LAST_NULL_BYTE_UNDEF= 0
+ };
+ size_t last_null_byte() const {
+ size_t bytes= do_last_null_byte();
+ do {_db_pargs_(284,"debug"); _db_doprnt_ ("last_null_byte() ==> %ld", (long) bytes);} while(0);
+ assert(bytes <= table->s->null_bytes);
+ return bytes;
+ }
+ virtual void make_field(Send_field *);
+ virtual void sort_string(uchar *buff,uint length)=0;
+ virtual In_C_you_should_use_my_bool_instead() optimize_range(uint idx, uint part);
+ virtual In_C_you_should_use_my_bool_instead() can_be_compared_as_longlong() const { return (0); }
+ virtual void free() {}
+ virtual Field *new_field(MEM_ROOT *root, struct st_table *new_table,
+ In_C_you_should_use_my_bool_instead() keep_type);
+ virtual Field *new_key_field(MEM_ROOT *root, struct st_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);
+ 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;
+ }
+ inline void move_field(uchar *ptr_arg) { ptr=ptr_arg; }
+ virtual void move_field_offset(my_ptrdiff_t ptr_diff)
+ {
+ ptr=(uchar*) ((uchar*) (ptr)+ptr_diff);
+ if (null_ptr)
+ null_ptr=(uchar*) ((uchar*) (null_ptr)+ptr_diff);
+ }
+ virtual void get_image(uchar *buff, uint length, CHARSET_INFO *cs)
+ { memcpy(buff,ptr,length); }
+ virtual void set_image(const uchar *buff,uint length, CHARSET_INFO *cs)
+ { memcpy(ptr,buff,length); }
+ virtual uint get_key_image(uchar *buff, uint length, imagetype type)
+ {
+ get_image(buff, length, &my_charset_bin);
+ return length;
+ }
+ virtual void set_key_image(const uchar *buff,uint length)
+ { set_image(buff,length, &my_charset_bin); }
+ inline longlong val_int_offset(uint row_offset)
+ {
+ ptr+=row_offset;
+ longlong tmp=val_int();
+ ptr-=row_offset;
+ return tmp;
+ }
+ inline longlong val_int(const uchar *new_ptr)
+ {
+ uchar *old_ptr= ptr;
+ longlong return_value;
+ ptr= (uchar*) new_ptr;
+ return_value= val_int();
+ ptr= old_ptr;
+ return return_value;
+ }
+ inline String *val_str(String *str, const uchar *new_ptr)
+ {
+ uchar *old_ptr= ptr;
+ ptr= (uchar*) new_ptr;
+ val_str(str);
+ ptr= old_ptr;
+ return str;
+ }
+ virtual In_C_you_should_use_my_bool_instead() send_binary(Protocol *protocol);
+ virtual uchar *pack(uchar *to, const uchar *from,
+ uint max_length, In_C_you_should_use_my_bool_instead() low_byte_first);
+ uchar *pack(uchar *to, const uchar *from)
+ {
+ const char *_db_func_, *_db_file_; uint _db_level_; char **_db_framep_; _db_enter_ ("Field::pack","./sql/field.h",390,&_db_func_,&_db_file_,&_db_level_, &_db_framep_);
+ uchar *result= this->pack(to, from, UINT_MAX, table->s->db_low_byte_first);
+ do {_db_return_ (392, &_db_func_, &_db_file_, &_db_level_); return(result);} while(0);
+ }
+ virtual const uchar *unpack(uchar* to, const uchar *from,
+ uint param_data, In_C_you_should_use_my_bool_instead() low_byte_first);
+ const uchar *unpack(uchar* to, const uchar *from)
+ {
+ const char *_db_func_, *_db_file_; uint _db_level_; char **_db_framep_; _db_enter_ ("Field::unpack","./sql/field.h",402,&_db_func_,&_db_file_,&_db_level_, &_db_framep_);
+ const uchar *result= unpack(to, from, 0U, table->s->db_low_byte_first);
+ do {_db_return_ (404, &_db_func_, &_db_file_, &_db_level_); return(result);} while(0);
+ }
+ virtual uchar *pack_key(uchar* to, const uchar *from,
+ uint max_length, In_C_you_should_use_my_bool_instead() low_byte_first)
+ {
+ return pack(to, from, max_length, low_byte_first);
+ }
+ virtual uchar *pack_key_from_key_image(uchar* to, const uchar *from,
+ uint max_length, In_C_you_should_use_my_bool_instead() low_byte_first)
+ {
+ return pack(to, from, max_length, low_byte_first);
+ }
+ virtual const uchar *unpack_key(uchar* to, const uchar *from,
+ uint max_length, In_C_you_should_use_my_bool_instead() low_byte_first)
+ {
+ return unpack(to, from, max_length, low_byte_first);
+ }
+ 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,
+ my_bool insert_or_update)
+ { return cmp(a,b); }
+ virtual int pack_cmp(const uchar *b, uint key_length_arg,
+ my_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 In_C_you_should_use_my_bool_instead() get_date(MYSQL_TIME *ltime,uint fuzzydate);
+ virtual In_C_you_should_use_my_bool_instead() get_time(MYSQL_TIME *ltime);
+ virtual CHARSET_INFO *charset(void) const { return &my_charset_bin; }
+ virtual CHARSET_INFO *sort_charset(void) const { return charset(); }
+ virtual In_C_you_should_use_my_bool_instead() has_charset(void) const { return (0); }
+ virtual void set_charset(CHARSET_INFO *charset_arg) { }
+ virtual enum Derivation derivation(void) const
+ { return DERIVATION_IMPLICIT; }
+ virtual void set_derivation(enum Derivation derivation_arg) { }
+ In_C_you_should_use_my_bool_instead() 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 char *str, uint str_len,
+ timestamp_type ts_type, int cuted_increment);
+ void set_datetime_warning(MYSQL_ERROR::enum_warning_level, uint code,
+ longlong nr, timestamp_type ts_type,
+ int cuted_increment);
+ void set_datetime_warning(MYSQL_ERROR::enum_warning_level, const uint code,
+ double nr, timestamp_type ts_type);
+ inline In_C_you_should_use_my_bool_instead() check_overflow(int op_result)
+ {
+ return (op_result == 2);
+ }
+ int warn_if_overflow(int op_result);
+ void init(TABLE *table_arg)
+ {
+ orig_table= table= table_arg;
+ table_name= &table_arg->alias;
+ }
+ virtual uint32 max_display_length()= 0;
+ virtual uint is_equal(Create_field *new_field);
+ longlong convert_decimal2longlong(const my_decimal *val, In_C_you_should_use_my_bool_instead() unsigned_flag,
+ int *err);
+ inline uint32 char_length() const
+ {
+ return field_length / charset()->mbmaxlen;
+ }
+ virtual geometry_type get_geometry_type()
+ {
+ assert(0);
+ return GEOM_GEOMETRY;
+ }
+ virtual void hash(ulong *nr, ulong *nr2);
+ friend In_C_you_should_use_my_bool_instead() reopen_table(THD *,struct st_table *,In_C_you_should_use_my_bool_instead());
+ friend int cre_myisam(char * name, register TABLE *form, uint options,
+ ulonglong auto_increment_value);
+ friend class Copy_field;
+ friend class Item_avg_field;
+ friend class Item_std_field;
+ friend class Item_sum_num;
+ friend class Item_sum_sum;
+ friend class Item_sum_str;
+ friend class Item_sum_count;
+ friend class Item_sum_avg;
+ friend class Item_sum_std;
+ friend class Item_sum_min;
+ friend class Item_sum_max;
+ friend class Item_func_group_concat;
+private:
+ virtual size_t do_last_null_byte() const;
+ virtual int do_save_field_metadata(uchar *metadata_ptr)
+ { return 0; }
+};
+class Field_num :public Field {
+public:
+ const uint8 dec;
+ In_C_you_should_use_my_bool_instead() zerofill,unsigned_flag;
+ Field_num(uchar *ptr_arg,uint32 len_arg, uchar *null_ptr_arg,
+ uchar null_bit_arg, utype unireg_check_arg,
+ const char *field_name_arg,
+ uint8 dec_arg, In_C_you_should_use_my_bool_instead() zero_arg, In_C_you_should_use_my_bool_instead() unsigned_arg);
+ Item_result result_type () const { return REAL_RESULT; }
+ void prepend_zeros(String *value);
+ void add_zerofill_and_unsigned(String &res) const;
+ friend class Create_field;
+ void make_field(Send_field *);
+ uint decimals() const { return (uint) dec; }
+ uint size_of() const { return sizeof(*this); }
+ In_C_you_should_use_my_bool_instead() eq_def(Field *field);
+ int store_decimal(const my_decimal *);
+ my_decimal *val_decimal(my_decimal *);
+ uint is_equal(Create_field *new_field);
+ int check_int(CHARSET_INFO *cs, const char *str, int length,
+ const char *int_end, int error);
+ In_C_you_should_use_my_bool_instead() get_int(CHARSET_INFO *cs, const char *from, uint len,
+ longlong *rnd, ulonglong unsigned_max,
+ longlong signed_min, longlong signed_max);
+};
+class Field_str :public Field {
+protected:
+ CHARSET_INFO *field_charset;
+ enum Derivation field_derivation;
+public:
+ Field_str(uchar *ptr_arg,uint32 len_arg, uchar *null_ptr_arg,
+ uchar null_bit_arg, utype unireg_check_arg,
+ const char *field_name_arg, CHARSET_INFO *charset);
+ Item_result result_type () const { return STRING_RESULT; }
+ uint decimals() const { return 31; }
+ int store(double nr);
+ int store(longlong nr, In_C_you_should_use_my_bool_instead() 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); }
+ 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; }
+ virtual void set_derivation(enum Derivation derivation_arg)
+ { field_derivation= derivation_arg; }
+ In_C_you_should_use_my_bool_instead() binary() const { return field_charset == &my_charset_bin; }
+ uint32 max_display_length() { return field_length; }
+ friend class Create_field;
+ my_decimal *val_decimal(my_decimal *);
+ virtual In_C_you_should_use_my_bool_instead() str_needs_quotes() { return (1); }
+ In_C_you_should_use_my_bool_instead() compare_str_field_flags(Create_field *new_field, uint32 flags);
+ uint is_equal(Create_field *new_field);
+};
+class Field_longstr :public Field_str
+{
+protected:
+ int report_if_important_data(const char *ptr, const char *end,
+ In_C_you_should_use_my_bool_instead() count_spaces);
+public:
+ Field_longstr(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg,
+ uchar null_bit_arg, utype unireg_check_arg,
+ const char *field_name_arg, CHARSET_INFO *charset_arg)
+ :Field_str(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, unireg_check_arg,
+ field_name_arg, charset_arg)
+ {}
+ int store_decimal(const my_decimal *d);
+ uint32 max_data_length() const;
+};
+class Field_real :public Field_num {
+public:
+ my_bool not_fixed;
+ Field_real(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg,
+ uchar null_bit_arg, utype unireg_check_arg,
+ const char *field_name_arg,
+ uint8 dec_arg, In_C_you_should_use_my_bool_instead() zero_arg, In_C_you_should_use_my_bool_instead() unsigned_arg)
+ :Field_num(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, unireg_check_arg,
+ field_name_arg, dec_arg, zero_arg, unsigned_arg),
+ not_fixed(dec_arg >= 31)
+ {}
+ int store_decimal(const my_decimal *);
+ my_decimal *val_decimal(my_decimal *);
+ int truncate(double *nr, double max_length);
+ uint32 max_display_length() { return field_length; }
+ uint size_of() const { return sizeof(*this); }
+ virtual const uchar *unpack(uchar* to, const uchar *from,
+ uint param_data, In_C_you_should_use_my_bool_instead() low_byte_first);
+ virtual uchar *pack(uchar* to, const uchar *from,
+ uint max_length, In_C_you_should_use_my_bool_instead() low_byte_first);
+};
+class Field_decimal :public Field_real {
+public:
+ Field_decimal(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg,
+ uchar null_bit_arg,
+ enum utype unireg_check_arg, const char *field_name_arg,
+ uint8 dec_arg,In_C_you_should_use_my_bool_instead() zero_arg,In_C_you_should_use_my_bool_instead() unsigned_arg)
+ :Field_real(ptr_arg, len_arg, null_ptr_arg, null_bit_arg,
+ unireg_check_arg, field_name_arg,
+ dec_arg, zero_arg, unsigned_arg)
+ {}
+ enum_field_types type() const { return MYSQL_TYPE_DECIMAL;}
+ enum ha_base_keytype key_type() const
+ { return zerofill ? HA_KEYTYPE_BINARY : HA_KEYTYPE_NUM; }
+ int reset(void);
+ int store(const char *to,uint length,CHARSET_INFO *charset);
+ int store(double nr);
+ int store(longlong nr, In_C_you_should_use_my_bool_instead() unsigned_val);
+ double val_real(void);
+ longlong val_int(void);
+ String *val_str(String*,String *);
+ int cmp(const uchar *,const uchar *);
+ void sort_string(uchar *buff,uint length);
+ void overflow(In_C_you_should_use_my_bool_instead() negative);
+ In_C_you_should_use_my_bool_instead() zero_pack() const { return 0; }
+ void sql_type(String &str) const;
+ virtual const uchar *unpack(uchar* to, const uchar *from,
+ uint param_data, In_C_you_should_use_my_bool_instead() low_byte_first)
+ {
+ return Field::unpack(to, from, param_data, low_byte_first);
+ }
+ virtual uchar *pack(uchar* to, const uchar *from,
+ uint max_length, In_C_you_should_use_my_bool_instead() low_byte_first)
+ {
+ return Field::pack(to, from, max_length, low_byte_first);
+ }
+};
+class Field_new_decimal :public Field_num {
+private:
+ int do_save_field_metadata(uchar *first_byte);
+public:
+ uint precision;
+ uint bin_size;
+ Field_new_decimal(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg,
+ uchar null_bit_arg,
+ enum utype unireg_check_arg, const char *field_name_arg,
+ uint8 dec_arg, In_C_you_should_use_my_bool_instead() zero_arg, In_C_you_should_use_my_bool_instead() unsigned_arg);
+ Field_new_decimal(uint32 len_arg, In_C_you_should_use_my_bool_instead() maybe_null_arg,
+ const char *field_name_arg, uint8 dec_arg,
+ In_C_you_should_use_my_bool_instead() unsigned_arg);
+ enum_field_types type() const { return MYSQL_TYPE_NEWDECIMAL;}
+ enum ha_base_keytype key_type() const { return HA_KEYTYPE_BINARY; }
+ Item_result result_type () const { return DECIMAL_RESULT; }
+ int reset(void);
+ In_C_you_should_use_my_bool_instead() store_value(const my_decimal *decimal_value);
+ void set_value_on_overflow(my_decimal *decimal_value, In_C_you_should_use_my_bool_instead() sign);
+ int store(const char *to, uint length, CHARSET_INFO *charset);
+ int store(double nr);
+ int store(longlong nr, In_C_you_should_use_my_bool_instead() unsigned_val);
+ int store_time(MYSQL_TIME *ltime, timestamp_type t_type);
+ int store_decimal(const my_decimal *);
+ double val_real(void);
+ longlong val_int(void);
+ my_decimal *val_decimal(my_decimal *);
+ String *val_str(String*, String *);
+ int cmp(const uchar *, const uchar *);
+ void sort_string(uchar *buff, uint length);
+ In_C_you_should_use_my_bool_instead() zero_pack() const { return 0; }
+ void sql_type(String &str) const;
+ uint32 max_display_length() { return field_length; }
+ uint size_of() const { return sizeof(*this); }
+ uint32 pack_length() const { return (uint32) bin_size; }
+ uint pack_length_from_metadata(uint field_metadata);
+ uint row_pack_length() { return pack_length(); }
+ int compatible_field_size(uint field_metadata);
+ uint is_equal(Create_field *new_field);
+ virtual const uchar *unpack(uchar* to, const uchar *from,
+ uint param_data, In_C_you_should_use_my_bool_instead() low_byte_first);
+};
+class Field_tiny :public Field_num {
+public:
+ Field_tiny(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg,
+ uchar null_bit_arg,
+ enum utype unireg_check_arg, const char *field_name_arg,
+ In_C_you_should_use_my_bool_instead() zero_arg, In_C_you_should_use_my_bool_instead() unsigned_arg)
+ :Field_num(ptr_arg, len_arg, null_ptr_arg, null_bit_arg,
+ unireg_check_arg, field_name_arg,
+ 0, zero_arg,unsigned_arg)
+ {}
+ enum Item_result result_type () const { return INT_RESULT; }
+ enum_field_types type() const { return MYSQL_TYPE_TINY;}
+ enum ha_base_keytype key_type() const
+ { return unsigned_flag ? HA_KEYTYPE_BINARY : HA_KEYTYPE_INT8; }
+ int store(const char *to,uint length,CHARSET_INFO *charset);
+ int store(double nr);
+ int store(longlong nr, In_C_you_should_use_my_bool_instead() unsigned_val);
+ int reset(void) { ptr[0]=0; return 0; }
+ double val_real(void);
+ longlong val_int(void);
+ String *val_str(String*,String *);
+ In_C_you_should_use_my_bool_instead() send_binary(Protocol *protocol);
+ int cmp(const uchar *,const uchar *);
+ void sort_string(uchar *buff,uint length);
+ uint32 pack_length() const { return 1; }
+ void sql_type(String &str) const;
+ uint32 max_display_length() { return 4; }
+ virtual uchar *pack(uchar* to, const uchar *from,
+ uint max_length, In_C_you_should_use_my_bool_instead() low_byte_first)
+ {
+ *to= *from;
+ return to + 1;
+ }
+ virtual const uchar *unpack(uchar* to, const uchar *from,
+ uint param_data, In_C_you_should_use_my_bool_instead() low_byte_first)
+ {
+ *to= *from;
+ return from + 1;
+ }
+};
+class Field_short :public Field_num {
+public:
+ Field_short(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg,
+ uchar null_bit_arg,
+ enum utype unireg_check_arg, const char *field_name_arg,
+ In_C_you_should_use_my_bool_instead() zero_arg, In_C_you_should_use_my_bool_instead() unsigned_arg)
+ :Field_num(ptr_arg, len_arg, null_ptr_arg, null_bit_arg,
+ unireg_check_arg, field_name_arg,
+ 0, zero_arg,unsigned_arg)
+ {}
+ Field_short(uint32 len_arg,In_C_you_should_use_my_bool_instead() maybe_null_arg, const char *field_name_arg,
+ In_C_you_should_use_my_bool_instead() unsigned_arg)
+ :Field_num((uchar*) 0, len_arg, maybe_null_arg ? (uchar*) "": 0,0,
+ NONE, field_name_arg, 0, 0, unsigned_arg)
+ {}
+ enum Item_result result_type () const { return INT_RESULT; }
+ enum_field_types type() const { return MYSQL_TYPE_SHORT;}
+ enum ha_base_keytype key_type() const
+ { return unsigned_flag ? HA_KEYTYPE_USHORT_INT : HA_KEYTYPE_SHORT_INT;}
+ int store(const char *to,uint length,CHARSET_INFO *charset);
+ int store(double nr);
+ int store(longlong nr, In_C_you_should_use_my_bool_instead() unsigned_val);
+ int reset(void) { ptr[0]=ptr[1]=0; return 0; }
+ double val_real(void);
+ longlong val_int(void);
+ String *val_str(String*,String *);
+ In_C_you_should_use_my_bool_instead() send_binary(Protocol *protocol);
+ int cmp(const uchar *,const uchar *);
+ void sort_string(uchar *buff,uint length);
+ uint32 pack_length() const { return 2; }
+ void sql_type(String &str) const;
+ uint32 max_display_length() { return 6; }
+ virtual uchar *pack(uchar* to, const uchar *from,
+ uint max_length, In_C_you_should_use_my_bool_instead() low_byte_first)
+ {
+ int16 val;
+ do { val = (*((int16 *) (from))); } while(0);
+ *((uint16*) (to))= (uint16) (val);
+ return to + sizeof(val);
+ }
+ virtual const uchar *unpack(uchar* to, const uchar *from,
+ uint param_data, In_C_you_should_use_my_bool_instead() low_byte_first)
+ {
+ int16 val;
+ do { val = (*((int16 *) (from))); } while(0);
+ *((uint16*) (to))= (uint16) (val);
+ return from + sizeof(val);
+ }
+};
+class Field_medium :public Field_num {
+public:
+ Field_medium(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg,
+ uchar null_bit_arg,
+ enum utype unireg_check_arg, const char *field_name_arg,
+ In_C_you_should_use_my_bool_instead() zero_arg, In_C_you_should_use_my_bool_instead() unsigned_arg)
+ :Field_num(ptr_arg, len_arg, null_ptr_arg, null_bit_arg,
+ unireg_check_arg, field_name_arg,
+ 0, zero_arg,unsigned_arg)
+ {}
+ enum Item_result result_type () const { return INT_RESULT; }
+ enum_field_types type() const { return MYSQL_TYPE_INT24;}
+ enum ha_base_keytype key_type() const
+ { return unsigned_flag ? HA_KEYTYPE_UINT24 : HA_KEYTYPE_INT24; }
+ int store(const char *to,uint length,CHARSET_INFO *charset);
+ int store(double nr);
+ int store(longlong nr, In_C_you_should_use_my_bool_instead() unsigned_val);
+ int reset(void) { ptr[0]=ptr[1]=ptr[2]=0; return 0; }
+ double val_real(void);
+ longlong val_int(void);
+ String *val_str(String*,String *);
+ In_C_you_should_use_my_bool_instead() send_binary(Protocol *protocol);
+ int cmp(const uchar *,const uchar *);
+ void sort_string(uchar *buff,uint length);
+ uint32 pack_length() const { return 3; }
+ void sql_type(String &str) const;
+ uint32 max_display_length() { return 8; }
+ virtual uchar *pack(uchar* to, const uchar *from,
+ uint max_length, In_C_you_should_use_my_bool_instead() low_byte_first)
+ {
+ return Field::pack(to, from, max_length, low_byte_first);
+ }
+ virtual const uchar *unpack(uchar* to, const uchar *from,
+ uint param_data, In_C_you_should_use_my_bool_instead() low_byte_first)
+ {
+ return Field::unpack(to, from, param_data, low_byte_first);
+ }
+};
+class Field_long :public Field_num {
+public:
+ Field_long(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg,
+ uchar null_bit_arg,
+ enum utype unireg_check_arg, const char *field_name_arg,
+ In_C_you_should_use_my_bool_instead() zero_arg, In_C_you_should_use_my_bool_instead() unsigned_arg)
+ :Field_num(ptr_arg, len_arg, null_ptr_arg, null_bit_arg,
+ unireg_check_arg, field_name_arg,
+ 0, zero_arg,unsigned_arg)
+ {}
+ Field_long(uint32 len_arg,In_C_you_should_use_my_bool_instead() maybe_null_arg, const char *field_name_arg,
+ In_C_you_should_use_my_bool_instead() unsigned_arg)
+ :Field_num((uchar*) 0, len_arg, maybe_null_arg ? (uchar*) "": 0,0,
+ NONE, field_name_arg,0,0,unsigned_arg)
+ {}
+ enum Item_result result_type () const { return INT_RESULT; }
+ enum_field_types type() const { return MYSQL_TYPE_LONG;}
+ enum ha_base_keytype key_type() const
+ { return unsigned_flag ? HA_KEYTYPE_ULONG_INT : HA_KEYTYPE_LONG_INT; }
+ int store(const char *to,uint length,CHARSET_INFO *charset);
+ int store(double nr);
+ int store(longlong nr, In_C_you_should_use_my_bool_instead() unsigned_val);
+ int reset(void) { ptr[0]=ptr[1]=ptr[2]=ptr[3]=0; return 0; }
+ double val_real(void);
+ longlong val_int(void);
+ In_C_you_should_use_my_bool_instead() send_binary(Protocol *protocol);
+ String *val_str(String*,String *);
+ int cmp(const uchar *,const uchar *);
+ void sort_string(uchar *buff,uint length);
+ uint32 pack_length() const { return 4; }
+ void sql_type(String &str) const;
+ uint32 max_display_length() { return 11; }
+ virtual uchar *pack(uchar* to, const uchar *from,
+ uint max_length, In_C_you_should_use_my_bool_instead() low_byte_first)
+ {
+ int32 val;
+ do { val = (*((long *) (from))); } while(0);
+ *((long *) (to))= (long) (val);
+ return to + sizeof(val);
+ }
+ virtual const uchar *unpack(uchar* to, const uchar *from,
+ uint param_data, In_C_you_should_use_my_bool_instead() low_byte_first)
+ {
+ int32 val;
+ do { val = (*((long *) (from))); } while(0);
+ *((long *) (to))= (long) (val);
+ return from + sizeof(val);
+ }
+};
+class Field_longlong :public Field_num {
+public:
+ Field_longlong(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg,
+ uchar null_bit_arg,
+ enum utype unireg_check_arg, const char *field_name_arg,
+ In_C_you_should_use_my_bool_instead() zero_arg, In_C_you_should_use_my_bool_instead() unsigned_arg)
+ :Field_num(ptr_arg, len_arg, null_ptr_arg, null_bit_arg,
+ unireg_check_arg, field_name_arg,
+ 0, zero_arg,unsigned_arg)
+ {}
+ Field_longlong(uint32 len_arg,In_C_you_should_use_my_bool_instead() maybe_null_arg,
+ const char *field_name_arg,
+ In_C_you_should_use_my_bool_instead() unsigned_arg)
+ :Field_num((uchar*) 0, len_arg, maybe_null_arg ? (uchar*) "": 0,0,
+ NONE, field_name_arg,0,0,unsigned_arg)
+ {}
+ enum Item_result result_type () const { return INT_RESULT; }
+ enum_field_types type() const { return MYSQL_TYPE_LONGLONG;}
+ enum ha_base_keytype key_type() const
+ { return unsigned_flag ? HA_KEYTYPE_ULONGLONG : HA_KEYTYPE_LONGLONG; }
+ int store(const char *to,uint length,CHARSET_INFO *charset);
+ int store(double nr);
+ int store(longlong nr, In_C_you_should_use_my_bool_instead() unsigned_val);
+ int reset(void)
+ {
+ ptr[0]=ptr[1]=ptr[2]=ptr[3]=ptr[4]=ptr[5]=ptr[6]=ptr[7]=0;
+ return 0;
+ }
+ double val_real(void);
+ longlong val_int(void);
+ String *val_str(String*,String *);
+ In_C_you_should_use_my_bool_instead() send_binary(Protocol *protocol);
+ int cmp(const uchar *,const uchar *);
+ void sort_string(uchar *buff,uint length);
+ uint32 pack_length() const { return 8; }
+ void sql_type(String &str) const;
+ In_C_you_should_use_my_bool_instead() can_be_compared_as_longlong() const { return (1); }
+ uint32 max_display_length() { return 20; }
+ virtual uchar *pack(uchar* to, const uchar *from,
+ uint max_length, In_C_you_should_use_my_bool_instead() low_byte_first)
+ {
+ int64 val;
+ memcpy(((uchar*) &val),((uchar*) (from)),(sizeof(ulonglong)));
+ memcpy(((uchar*) (to)),((uchar*) &val),(sizeof(ulonglong)));
+ return to + sizeof(val);
+ }
+ virtual const uchar *unpack(uchar* to, const uchar *from,
+ uint param_data, In_C_you_should_use_my_bool_instead() low_byte_first)
+ {
+ int64 val;
+ memcpy(((uchar*) &val),((uchar*) (from)),(sizeof(ulonglong)));
+ memcpy(((uchar*) (to)),((uchar*) &val),(sizeof(ulonglong)));
+ return from + sizeof(val);
+ }
+};
+class Field_float :public Field_real {
+public:
+ Field_float(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg,
+ uchar null_bit_arg,
+ enum utype unireg_check_arg, const char *field_name_arg,
+ uint8 dec_arg,In_C_you_should_use_my_bool_instead() zero_arg,In_C_you_should_use_my_bool_instead() unsigned_arg)
+ :Field_real(ptr_arg, len_arg, null_ptr_arg, null_bit_arg,
+ unireg_check_arg, field_name_arg,
+ dec_arg, zero_arg, unsigned_arg)
+ {}
+ Field_float(uint32 len_arg, In_C_you_should_use_my_bool_instead() maybe_null_arg, const char *field_name_arg,
+ uint8 dec_arg)
+ :Field_real((uchar*) 0, len_arg, maybe_null_arg ? (uchar*) "": 0, (uint) 0,
+ NONE, field_name_arg, dec_arg, 0, 0)
+ {}
+ enum_field_types type() const { return MYSQL_TYPE_FLOAT;}
+ enum ha_base_keytype key_type() const { return HA_KEYTYPE_FLOAT; }
+ int store(const char *to,uint length,CHARSET_INFO *charset);
+ int store(double nr);
+ int store(longlong nr, In_C_you_should_use_my_bool_instead() unsigned_val);
+ int reset(void) { bzero(ptr,sizeof(float)); return 0; }
+ double val_real(void);
+ longlong val_int(void);
+ String *val_str(String*,String *);
+ In_C_you_should_use_my_bool_instead() send_binary(Protocol *protocol);
+ int cmp(const uchar *,const uchar *);
+ void sort_string(uchar *buff,uint length);
+ uint32 pack_length() const { return sizeof(float); }
+ uint row_pack_length() { return pack_length(); }
+ void sql_type(String &str) const;
+private:
+ int do_save_field_metadata(uchar *first_byte);
+};
+class Field_double :public Field_real {
+public:
+ Field_double(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg,
+ uchar null_bit_arg,
+ enum utype unireg_check_arg, const char *field_name_arg,
+ uint8 dec_arg,In_C_you_should_use_my_bool_instead() zero_arg,In_C_you_should_use_my_bool_instead() unsigned_arg)
+ :Field_real(ptr_arg, len_arg, null_ptr_arg, null_bit_arg,
+ unireg_check_arg, field_name_arg,
+ dec_arg, zero_arg, unsigned_arg)
+ {}
+ Field_double(uint32 len_arg, In_C_you_should_use_my_bool_instead() maybe_null_arg, const char *field_name_arg,
+ uint8 dec_arg)
+ :Field_real((uchar*) 0, len_arg, maybe_null_arg ? (uchar*) "" : 0, (uint) 0,
+ NONE, field_name_arg, dec_arg, 0, 0)
+ {}
+ Field_double(uint32 len_arg, In_C_you_should_use_my_bool_instead() maybe_null_arg, const char *field_name_arg,
+ uint8 dec_arg, my_bool not_fixed_arg)
+ :Field_real((uchar*) 0, len_arg, maybe_null_arg ? (uchar*) "" : 0, (uint) 0,
+ NONE, field_name_arg, dec_arg, 0, 0)
+ {not_fixed= not_fixed_arg; }
+ enum_field_types type() const { return MYSQL_TYPE_DOUBLE;}
+ enum ha_base_keytype key_type() const { return HA_KEYTYPE_DOUBLE; }
+ int store(const char *to,uint length,CHARSET_INFO *charset);
+ int store(double nr);
+ int store(longlong nr, In_C_you_should_use_my_bool_instead() unsigned_val);
+ int reset(void) { bzero(ptr,sizeof(double)); return 0; }
+ double val_real(void);
+ longlong val_int(void);
+ String *val_str(String*,String *);
+ In_C_you_should_use_my_bool_instead() send_binary(Protocol *protocol);
+ int cmp(const uchar *,const uchar *);
+ void sort_string(uchar *buff,uint length);
+ uint32 pack_length() const { return sizeof(double); }
+ uint row_pack_length() { return pack_length(); }
+ void sql_type(String &str) const;
+private:
+ int do_save_field_metadata(uchar *first_byte);
+};
+class Field_null :public Field_str {
+ static uchar null[1];
+public:
+ Field_null(uchar *ptr_arg, uint32 len_arg,
+ enum utype unireg_check_arg, const char *field_name_arg,
+ CHARSET_INFO *cs)
+ :Field_str(ptr_arg, len_arg, null, 1,
+ unireg_check_arg, field_name_arg, cs)
+ {}
+ enum_field_types type() const { return MYSQL_TYPE_NULL;}
+ int store(const char *to, uint length, CHARSET_INFO *cs)
+ { null[0]=1; return 0; }
+ int store(double nr) { null[0]=1; return 0; }
+ int store(longlong nr, In_C_you_should_use_my_bool_instead() unsigned_val) { null[0]=1; return 0; }
+ int store_decimal(const my_decimal *d) { null[0]=1; return 0; }
+ int reset(void) { return 0; }
+ double val_real(void) { return 0.0;}
+ longlong val_int(void) { return 0;}
+ my_decimal *val_decimal(my_decimal *) { return 0; }
+ String *val_str(String *value,String *value2)
+ { value2->length(0); return value2;}
+ int cmp(const uchar *a, const uchar *b) { return 0;}
+ void sort_string(uchar *buff, uint length) {}
+ uint32 pack_length() const { return 0; }
+ void sql_type(String &str) const;
+ uint size_of() const { return sizeof(*this); }
+ uint32 max_display_length() { return 4; }
+};
+class Field_timestamp :public Field_str {
+public:
+ Field_timestamp(uchar *ptr_arg, uint32 len_arg,
+ uchar *null_ptr_arg, uchar null_bit_arg,
+ enum utype unireg_check_arg, const char *field_name_arg,
+ TABLE_SHARE *share, CHARSET_INFO *cs);
+ Field_timestamp(In_C_you_should_use_my_bool_instead() maybe_null_arg, const char *field_name_arg,
+ CHARSET_INFO *cs);
+ enum_field_types type() const { return MYSQL_TYPE_TIMESTAMP;}
+ enum ha_base_keytype key_type() const { return HA_KEYTYPE_ULONG_INT; }
+ enum Item_result cmp_type () const { return INT_RESULT; }
+ int store(const char *to,uint length,CHARSET_INFO *charset);
+ int store(double nr);
+ int store(longlong nr, In_C_you_should_use_my_bool_instead() unsigned_val);
+ int reset(void) { ptr[0]=ptr[1]=ptr[2]=ptr[3]=0; return 0; }
+ double val_real(void);
+ longlong val_int(void);
+ String *val_str(String*,String *);
+ In_C_you_should_use_my_bool_instead() send_binary(Protocol *protocol);
+ int cmp(const uchar *,const uchar *);
+ void sort_string(uchar *buff,uint length);
+ uint32 pack_length() const { return 4; }
+ void sql_type(String &str) const;
+ In_C_you_should_use_my_bool_instead() can_be_compared_as_longlong() const { return (1); }
+ In_C_you_should_use_my_bool_instead() zero_pack() const { return 0; }
+ void set_time();
+ virtual void set_default()
+ {
+ if (table->timestamp_field == this &&
+ unireg_check != TIMESTAMP_UN_FIELD)
+ set_time();
+ else
+ Field::set_default();
+ }
+ inline long get_timestamp(my_bool *null_value)
+ {
+ if ((*null_value= is_null()))
+ return 0;
+ long tmp;
+ do { tmp = (*((long *) (ptr))); } while(0);
+ return tmp;
+ }
+ inline void store_timestamp(my_time_t timestamp)
+ {
+ *((long *) (ptr))= (long) ((uint32) timestamp);
+ }
+ In_C_you_should_use_my_bool_instead() get_date(MYSQL_TIME *ltime,uint fuzzydate);
+ In_C_you_should_use_my_bool_instead() get_time(MYSQL_TIME *ltime);
+ timestamp_auto_set_type get_auto_set_type() const;
+};
+class Field_year :public Field_tiny {
+public:
+ Field_year(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg,
+ uchar null_bit_arg,
+ enum utype unireg_check_arg, const char *field_name_arg)
+ :Field_tiny(ptr_arg, len_arg, null_ptr_arg, null_bit_arg,
+ unireg_check_arg, field_name_arg, 1, 1)
+ {}
+ enum_field_types type() const { return MYSQL_TYPE_YEAR;}
+ int store(const char *to,uint length,CHARSET_INFO *charset);
+ int store(double nr);
+ int store(longlong nr, In_C_you_should_use_my_bool_instead() unsigned_val);
+ double val_real(void);
+ longlong val_int(void);
+ String *val_str(String*,String *);
+ In_C_you_should_use_my_bool_instead() send_binary(Protocol *protocol);
+ void sql_type(String &str) const;
+ In_C_you_should_use_my_bool_instead() can_be_compared_as_longlong() const { return (1); }
+};
+class Field_date :public Field_str {
+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_str(ptr_arg, 10, null_ptr_arg, null_bit_arg,
+ unireg_check_arg, field_name_arg, cs)
+ {}
+ Field_date(In_C_you_should_use_my_bool_instead() maybe_null_arg, const char *field_name_arg,
+ CHARSET_INFO *cs)
+ :Field_str((uchar*) 0,10, maybe_null_arg ? (uchar*) "": 0,0,
+ NONE, 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; }
+ enum Item_result cmp_type () const { return INT_RESULT; }
+ int store(const char *to,uint length,CHARSET_INFO *charset);
+ int store(double nr);
+ int store(longlong nr, In_C_you_should_use_my_bool_instead() unsigned_val);
+ int reset(void) { ptr[0]=ptr[1]=ptr[2]=ptr[3]=0; return 0; }
+ double val_real(void);
+ longlong val_int(void);
+ String *val_str(String*,String *);
+ In_C_you_should_use_my_bool_instead() get_time(MYSQL_TIME *ltime);
+ In_C_you_should_use_my_bool_instead() send_binary(Protocol *protocol);
+ int cmp(const uchar *,const uchar *);
+ void sort_string(uchar *buff,uint length);
+ uint32 pack_length() const { return 4; }
+ void sql_type(String &str) const;
+ In_C_you_should_use_my_bool_instead() can_be_compared_as_longlong() const { return (1); }
+ In_C_you_should_use_my_bool_instead() zero_pack() const { return 1; }
+};
+class Field_newdate :public Field_str {
+public:
+ Field_newdate(uchar *ptr_arg, uchar *null_ptr_arg, uchar null_bit_arg,
+ enum utype unireg_check_arg, const char *field_name_arg,
+ CHARSET_INFO *cs)
+ :Field_str(ptr_arg, 10, null_ptr_arg, null_bit_arg,
+ unireg_check_arg, field_name_arg, cs)
+ {}
+ Field_newdate(In_C_you_should_use_my_bool_instead() maybe_null_arg, const char *field_name_arg,
+ CHARSET_INFO *cs)
+ :Field_str((uchar*) 0,10, maybe_null_arg ? (uchar*) "": 0,0,
+ NONE, field_name_arg, cs) {}
+ enum_field_types type() const { return MYSQL_TYPE_DATE;}
+ enum_field_types real_type() const { return MYSQL_TYPE_NEWDATE; }
+ enum ha_base_keytype key_type() const { return HA_KEYTYPE_UINT24; }
+ enum Item_result cmp_type () const { return INT_RESULT; }
+ int store(const char *to,uint length,CHARSET_INFO *charset);
+ int store(double nr);
+ int store(longlong nr, In_C_you_should_use_my_bool_instead() unsigned_val);
+ int store_time(MYSQL_TIME *ltime, timestamp_type type);
+ int reset(void) { ptr[0]=ptr[1]=ptr[2]=0; return 0; }
+ double val_real(void);
+ longlong val_int(void);
+ String *val_str(String*,String *);
+ In_C_you_should_use_my_bool_instead() send_binary(Protocol *protocol);
+ int cmp(const uchar *,const uchar *);
+ void sort_string(uchar *buff,uint length);
+ uint32 pack_length() const { return 3; }
+ void sql_type(String &str) const;
+ In_C_you_should_use_my_bool_instead() can_be_compared_as_longlong() const { return (1); }
+ In_C_you_should_use_my_bool_instead() zero_pack() const { return 1; }
+ In_C_you_should_use_my_bool_instead() get_date(MYSQL_TIME *ltime,uint fuzzydate);
+ In_C_you_should_use_my_bool_instead() get_time(MYSQL_TIME *ltime);
+};
+class Field_time :public Field_str {
+public:
+ Field_time(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_str(ptr_arg, 8, null_ptr_arg, null_bit_arg,
+ unireg_check_arg, field_name_arg, cs)
+ {}
+ Field_time(In_C_you_should_use_my_bool_instead() maybe_null_arg, const char *field_name_arg,
+ CHARSET_INFO *cs)
+ :Field_str((uchar*) 0,8, maybe_null_arg ? (uchar*) "": 0,0,
+ NONE, field_name_arg, cs) {}
+ enum_field_types type() const { return MYSQL_TYPE_TIME;}
+ enum ha_base_keytype key_type() const { return HA_KEYTYPE_INT24; }
+ enum Item_result cmp_type () const { return INT_RESULT; }
+ int store_time(MYSQL_TIME *ltime, timestamp_type type);
+ int store(const char *to,uint length,CHARSET_INFO *charset);
+ int store(double nr);
+ int store(longlong nr, In_C_you_should_use_my_bool_instead() unsigned_val);
+ int reset(void) { ptr[0]=ptr[1]=ptr[2]=0; return 0; }
+ double val_real(void);
+ longlong val_int(void);
+ String *val_str(String*,String *);
+ In_C_you_should_use_my_bool_instead() get_date(MYSQL_TIME *ltime, uint fuzzydate);
+ In_C_you_should_use_my_bool_instead() send_binary(Protocol *protocol);
+ In_C_you_should_use_my_bool_instead() get_time(MYSQL_TIME *ltime);
+ int cmp(const uchar *,const uchar *);
+ void sort_string(uchar *buff,uint length);
+ uint32 pack_length() const { return 3; }
+ void sql_type(String &str) const;
+ In_C_you_should_use_my_bool_instead() can_be_compared_as_longlong() const { return (1); }
+ In_C_you_should_use_my_bool_instead() zero_pack() const { return 1; }
+};
+class Field_datetime :public Field_str {
+public:
+ Field_datetime(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_str(ptr_arg, 19, null_ptr_arg, null_bit_arg,
+ unireg_check_arg, field_name_arg, cs)
+ {}
+ Field_datetime(In_C_you_should_use_my_bool_instead() maybe_null_arg, const char *field_name_arg,
+ CHARSET_INFO *cs)
+ :Field_str((uchar*) 0,19, maybe_null_arg ? (uchar*) "": 0,0,
+ NONE, field_name_arg, cs) {}
+ enum_field_types type() const { return MYSQL_TYPE_DATETIME;}
+ enum ha_base_keytype key_type() const { return HA_KEYTYPE_ULONGLONG; }
+ enum Item_result cmp_type () const { return INT_RESULT; }
+ uint decimals() const { return 6; }
+ int store(const char *to,uint length,CHARSET_INFO *charset);
+ int store(double nr);
+ int store(longlong nr, In_C_you_should_use_my_bool_instead() unsigned_val);
+ int store_time(MYSQL_TIME *ltime, timestamp_type type);
+ int reset(void)
+ {
+ ptr[0]=ptr[1]=ptr[2]=ptr[3]=ptr[4]=ptr[5]=ptr[6]=ptr[7]=0;
+ return 0;
+ }
+ double val_real(void);
+ longlong val_int(void);
+ String *val_str(String*,String *);
+ In_C_you_should_use_my_bool_instead() send_binary(Protocol *protocol);
+ int cmp(const uchar *,const uchar *);
+ void sort_string(uchar *buff,uint length);
+ uint32 pack_length() const { return 8; }
+ void sql_type(String &str) const;
+ In_C_you_should_use_my_bool_instead() can_be_compared_as_longlong() const { return (1); }
+ In_C_you_should_use_my_bool_instead() zero_pack() const { return 1; }
+ In_C_you_should_use_my_bool_instead() get_date(MYSQL_TIME *ltime,uint fuzzydate);
+ In_C_you_should_use_my_bool_instead() get_time(MYSQL_TIME *ltime);
+};
+class Field_string :public Field_longstr {
+public:
+ In_C_you_should_use_my_bool_instead() can_alter_field_type;
+ Field_string(uchar *ptr_arg, uint32 len_arg,uchar *null_ptr_arg,
+ uchar null_bit_arg,
+ enum utype unireg_check_arg, const char *field_name_arg,
+ CHARSET_INFO *cs)
+ :Field_longstr(ptr_arg, len_arg, null_ptr_arg, null_bit_arg,
+ unireg_check_arg, field_name_arg, cs),
+ can_alter_field_type(1) {};
+ Field_string(uint32 len_arg,In_C_you_should_use_my_bool_instead() maybe_null_arg, const char *field_name_arg,
+ CHARSET_INFO *cs)
+ :Field_longstr((uchar*) 0, len_arg, maybe_null_arg ? (uchar*) "": 0, 0,
+ NONE, field_name_arg, cs),
+ can_alter_field_type(1) {};
+ enum_field_types type() const
+ {
+ return ((can_alter_field_type && orig_table &&
+ orig_table->s->db_create_options & 1 &&
+ field_length >= 4) &&
+ orig_table->s->frm_version < (6 +4) ?
+ MYSQL_TYPE_VAR_STRING : MYSQL_TYPE_STRING);
+ }
+ enum ha_base_keytype key_type() const
+ { return binary() ? HA_KEYTYPE_BINARY : HA_KEYTYPE_TEXT; }
+ In_C_you_should_use_my_bool_instead() zero_pack() const { return 0; }
+ int reset(void)
+ {
+ charset()->cset->fill(charset(),(char*) ptr, field_length,
+ (has_charset() ? ' ' : 0));
+ return 0;
+ }
+ int store(const char *to,uint length,CHARSET_INFO *charset);
+ int store(longlong nr, In_C_you_should_use_my_bool_instead() unsigned_val);
+ int store(double nr) { return Field_str::store(nr); }
+ double val_real(void);
+ longlong val_int(void);
+ String *val_str(String*,String *);
+ my_decimal *val_decimal(my_decimal *);
+ int cmp(const uchar *,const uchar *);
+ void sort_string(uchar *buff,uint length);
+ void sql_type(String &str) const;
+ virtual uchar *pack(uchar *to, const uchar *from,
+ uint max_length, In_C_you_should_use_my_bool_instead() low_byte_first);
+ virtual const uchar *unpack(uchar* to, const uchar *from,
+ uint param_data, In_C_you_should_use_my_bool_instead() low_byte_first);
+ uint pack_length_from_metadata(uint field_metadata)
+ { return (field_metadata & 0x00ff); }
+ uint row_pack_length() { return (field_length + 1); }
+ int pack_cmp(const uchar *a,const uchar *b,uint key_length,
+ my_bool insert_or_update);
+ int pack_cmp(const uchar *b,uint key_length,my_bool insert_or_update);
+ uint packed_col_length(const uchar *to, uint length);
+ uint max_packed_col_length(uint max_length);
+ uint size_of() const { return sizeof(*this); }
+ enum_field_types real_type() const { return MYSQL_TYPE_STRING; }
+ In_C_you_should_use_my_bool_instead() has_charset(void) const
+ { return charset() == &my_charset_bin ? (0) : (1); }
+ Field *new_field(MEM_ROOT *root, struct st_table *new_table, In_C_you_should_use_my_bool_instead() keep_type);
+ virtual uint get_key_image(uchar *buff,uint length, imagetype type);
+private:
+ int do_save_field_metadata(uchar *first_byte);
+};
+class Field_varstring :public Field_longstr {
+public:
+ static const uint MAX_SIZE;
+ uint32 length_bytes;
+ Field_varstring(uchar *ptr_arg,
+ uint32 len_arg, uint length_bytes_arg,
+ uchar *null_ptr_arg, uchar null_bit_arg,
+ enum utype unireg_check_arg, const char *field_name_arg,
+ TABLE_SHARE *share, CHARSET_INFO *cs)
+ :Field_longstr(ptr_arg, len_arg, null_ptr_arg, null_bit_arg,
+ unireg_check_arg, field_name_arg, cs),
+ length_bytes(length_bytes_arg)
+ {
+ share->varchar_fields++;
+ }
+ Field_varstring(uint32 len_arg,In_C_you_should_use_my_bool_instead() maybe_null_arg,
+ const char *field_name_arg,
+ TABLE_SHARE *share, CHARSET_INFO *cs)
+ :Field_longstr((uchar*) 0,len_arg, maybe_null_arg ? (uchar*) "": 0, 0,
+ NONE, field_name_arg, cs),
+ length_bytes(len_arg < 256 ? 1 :2)
+ {
+ share->varchar_fields++;
+ }
+ enum_field_types type() const { return MYSQL_TYPE_VARCHAR; }
+ enum ha_base_keytype key_type() const;
+ uint row_pack_length() { return field_length; }
+ In_C_you_should_use_my_bool_instead() zero_pack() const { return 0; }
+ int reset(void) { bzero(ptr,field_length+length_bytes); return 0; }
+ uint32 pack_length() const { return (uint32) field_length+length_bytes; }
+ uint32 key_length() const { return (uint32) field_length; }
+ uint32 sort_length() const
+ {
+ return (uint32) field_length + (field_charset == &my_charset_bin ?
+ length_bytes : 0);
+ }
+ int store(const char *to,uint length,CHARSET_INFO *charset);
+ int store(longlong nr, In_C_you_should_use_my_bool_instead() unsigned_val);
+ int store(double nr) { return Field_str::store(nr); }
+ double val_real(void);
+ longlong val_int(void);
+ String *val_str(String*,String *);
+ my_decimal *val_decimal(my_decimal *);
+ int cmp_max(const uchar *, const uchar *, uint max_length);
+ int cmp(const uchar *a,const uchar *b)
+ {
+ return cmp_max(a, b, ~0L);
+ }
+ void sort_string(uchar *buff,uint length);
+ uint get_key_image(uchar *buff,uint length, imagetype type);
+ void set_key_image(const uchar *buff,uint length);
+ void sql_type(String &str) const;
+ virtual uchar *pack(uchar *to, const uchar *from,
+ uint max_length, In_C_you_should_use_my_bool_instead() low_byte_first);
+ uchar *pack_key(uchar *to, const uchar *from, uint max_length, In_C_you_should_use_my_bool_instead() low_byte_first);
+ uchar *pack_key_from_key_image(uchar* to, const uchar *from,
+ uint max_length, In_C_you_should_use_my_bool_instead() low_byte_first);
+ virtual const uchar *unpack(uchar* to, const uchar *from,
+ uint param_data, In_C_you_should_use_my_bool_instead() low_byte_first);
+ const uchar *unpack_key(uchar* to, const uchar *from,
+ uint max_length, In_C_you_should_use_my_bool_instead() low_byte_first);
+ int pack_cmp(const uchar *a, const uchar *b, uint key_length,
+ my_bool insert_or_update);
+ int pack_cmp(const uchar *b, uint key_length,my_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);
+ uint packed_col_length(const uchar *to, uint length);
+ uint max_packed_col_length(uint max_length);
+ uint32 data_length();
+ uint size_of() const { return sizeof(*this); }
+ enum_field_types real_type() const { return MYSQL_TYPE_VARCHAR; }
+ In_C_you_should_use_my_bool_instead() has_charset(void) const
+ { return charset() == &my_charset_bin ? (0) : (1); }
+ Field *new_field(MEM_ROOT *root, struct st_table *new_table, In_C_you_should_use_my_bool_instead() keep_type);
+ Field *new_key_field(MEM_ROOT *root, struct st_table *new_table,
+ uchar *new_ptr, uchar *new_null_ptr,
+ uint new_null_bit);
+ uint is_equal(Create_field *new_field);
+ void hash(ulong *nr, ulong *nr2);
+private:
+ int do_save_field_metadata(uchar *first_byte);
+};
+class Field_blob :public Field_longstr {
+protected:
+ uint packlength;
+ String value;
+public:
+ Field_blob(uchar *ptr_arg, uchar *null_ptr_arg, uchar null_bit_arg,
+ enum utype unireg_check_arg, const char *field_name_arg,
+ TABLE_SHARE *share, uint blob_pack_length, CHARSET_INFO *cs);
+ Field_blob(uint32 len_arg,In_C_you_should_use_my_bool_instead() maybe_null_arg, const char *field_name_arg,
+ CHARSET_INFO *cs)
+ :Field_longstr((uchar*) 0, len_arg, maybe_null_arg ? (uchar*) "": 0, 0,
+ NONE, field_name_arg, cs),
+ packlength(4)
+ {
+ flags|= 16;
+ }
+ Field_blob(uint32 len_arg,In_C_you_should_use_my_bool_instead() maybe_null_arg, const char *field_name_arg,
+ CHARSET_INFO *cs, In_C_you_should_use_my_bool_instead() set_packlength)
+ :Field_longstr((uchar*) 0,len_arg, maybe_null_arg ? (uchar*) "": 0, 0,
+ NONE, field_name_arg, cs)
+ {
+ flags|= 16;
+ packlength= 4;
+ if (set_packlength)
+ {
+ uint32 l_char_length= len_arg/cs->mbmaxlen;
+ packlength= l_char_length <= 255 ? 1 :
+ l_char_length <= 65535 ? 2 :
+ l_char_length <= 16777215 ? 3 : 4;
+ }
+ }
+ Field_blob(uint32 packlength_arg)
+ :Field_longstr((uchar*) 0, 0, (uchar*) "", 0, NONE, "temp", system_charset_info),
+ packlength(packlength_arg) {}
+ enum_field_types type() const { return MYSQL_TYPE_BLOB;}
+ 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);
+ int store(double nr);
+ int store(longlong nr, In_C_you_should_use_my_bool_instead() unsigned_val);
+ double val_real(void);
+ longlong val_int(void);
+ String *val_str(String*,String *);
+ my_decimal *val_decimal(my_decimal *);
+ int cmp_max(const uchar *, const uchar *, uint max_length);
+ int cmp(const uchar *a,const uchar *b)
+ { return cmp_max(a, b, ~0L); }
+ int cmp(const uchar *a, uint32 a_length, const uchar *b, uint32 b_length);
+ int cmp_binary(const uchar *a,const uchar *b, uint32 max_length=~0L);
+ int key_cmp(const uchar *,const uchar*);
+ int key_cmp(const uchar *str, uint length);
+ uint32 key_length() const { return 0; }
+ void sort_string(uchar *buff,uint length);
+ uint32 pack_length() const
+ { return (uint32) (packlength+table->s->blob_ptr_size); }
+ uint32 pack_length_no_ptr() const
+ { return (uint32) (packlength); }
+ uint row_pack_length() { return pack_length_no_ptr(); }
+ uint32 sort_length() const;
+ virtual uint32 max_data_length() const
+ {
+ return (uint32) (((ulonglong) 1 << (packlength*8)) -1);
+ }
+ int reset(void) { bzero(ptr, packlength+sizeof(uchar*)); return 0; }
+ void reset_fields() { bzero((uchar*) &value,sizeof(value)); }
+ static
+ void store_length(uchar *i_ptr, uint i_packlength, uint32 i_number, In_C_you_should_use_my_bool_instead() low_byte_first);
+ void store_length(uchar *i_ptr, uint i_packlength, uint32 i_number)
+ {
+ store_length(i_ptr, i_packlength, i_number, table->s->db_low_byte_first);
+ }
+ inline void store_length(uint32 number)
+ {
+ store_length(ptr, packlength, number);
+ }
+ uint32 get_packed_size(const uchar *ptr_arg, In_C_you_should_use_my_bool_instead() low_byte_first)
+ {return packlength + get_length(ptr_arg, packlength, low_byte_first);}
+ inline uint32 get_length(uint row_offset= 0)
+ { return get_length(ptr+row_offset, this->packlength, table->s->db_low_byte_first); }
+ uint32 get_length(const uchar *ptr, uint packlength, In_C_you_should_use_my_bool_instead() low_byte_first);
+ uint32 get_length(const uchar *ptr_arg)
+ { return get_length(ptr_arg, this->packlength, table->s->db_low_byte_first); }
+ void put_length(uchar *pos, uint32 length);
+ inline void get_ptr(uchar **str)
+ {
+ memcpy(((uchar*) str),(ptr+packlength),(sizeof(uchar*)));
+ }
+ inline void get_ptr(uchar **str, uint row_offset)
+ {
+ memcpy(((uchar*) str),(ptr+packlength+row_offset),(sizeof(char*)));
+ }
+ inline void set_ptr(uchar *length, uchar *data)
+ {
+ 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= (uchar*) ((uchar*) (ptr)+ptr_diff);
+ 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);
+ }
+ uint get_key_image(uchar *buff,uint length, imagetype type);
+ void set_key_image(const uchar *buff,uint length);
+ void sql_type(String &str) const;
+ inline In_C_you_should_use_my_bool_instead() copy()
+ {
+ uchar *tmp;
+ get_ptr(&tmp);
+ if (value.copy((char*) tmp, get_length(), charset()))
+ {
+ Field_blob::reset();
+ return 1;
+ }
+ tmp=(uchar*) value.ptr();
+ memcpy((ptr+packlength),(&tmp),(sizeof(char*)));
+ return 0;
+ }
+ virtual uchar *pack(uchar *to, const uchar *from,
+ uint max_length, In_C_you_should_use_my_bool_instead() low_byte_first);
+ uchar *pack_key(uchar *to, const uchar *from,
+ uint max_length, In_C_you_should_use_my_bool_instead() low_byte_first);
+ uchar *pack_key_from_key_image(uchar* to, const uchar *from,
+ uint max_length, In_C_you_should_use_my_bool_instead() low_byte_first);
+ virtual const uchar *unpack(uchar *to, const uchar *from,
+ uint param_data, In_C_you_should_use_my_bool_instead() low_byte_first);
+ const uchar *unpack_key(uchar* to, const uchar *from,
+ uint max_length, In_C_you_should_use_my_bool_instead() low_byte_first);
+ int pack_cmp(const uchar *a, const uchar *b, uint key_length,
+ my_bool insert_or_update);
+ int pack_cmp(const uchar *b, uint key_length,my_bool insert_or_update);
+ uint packed_col_length(const uchar *col_ptr, uint length);
+ uint max_packed_col_length(uint max_length);
+ void free() { value.free(); }
+ inline void clear_temporary() { bzero((uchar*) &value,sizeof(value)); }
+ friend int field_conv(Field *to,Field *from);
+ uint size_of() const { return sizeof(*this); }
+ In_C_you_should_use_my_bool_instead() has_charset(void) const
+ { return charset() == &my_charset_bin ? (0) : (1); }
+ uint32 max_display_length();
+ uint is_equal(Create_field *new_field);
+ inline In_C_you_should_use_my_bool_instead() in_read_set() { return bitmap_is_set(table->read_set, field_index); }
+ inline In_C_you_should_use_my_bool_instead() in_write_set() { return bitmap_is_set(table->write_set, field_index); }
+private:
+ int do_save_field_metadata(uchar *first_byte);
+};
+class Field_geom :public Field_blob {
+public:
+ enum geometry_type geom_type;
+ Field_geom(uchar *ptr_arg, uchar *null_ptr_arg, uint null_bit_arg,
+ enum utype unireg_check_arg, const char *field_name_arg,
+ TABLE_SHARE *share, uint blob_pack_length,
+ enum geometry_type geom_type_arg)
+ :Field_blob(ptr_arg, null_ptr_arg, null_bit_arg, unireg_check_arg,
+ field_name_arg, share, blob_pack_length, &my_charset_bin)
+ { geom_type= geom_type_arg; }
+ Field_geom(uint32 len_arg,In_C_you_should_use_my_bool_instead() maybe_null_arg, const char *field_name_arg,
+ TABLE_SHARE *share, enum geometry_type geom_type_arg)
+ :Field_blob(len_arg, maybe_null_arg, field_name_arg, &my_charset_bin)
+ { geom_type= geom_type_arg; }
+ enum ha_base_keytype key_type() const { return HA_KEYTYPE_VARBINARY2; }
+ enum_field_types type() const { return MYSQL_TYPE_GEOMETRY; }
+ void sql_type(String &str) const;
+ int store(const char *to, uint length, CHARSET_INFO *charset);
+ int store(double nr);
+ int store(longlong nr, In_C_you_should_use_my_bool_instead() unsigned_val);
+ int store_decimal(const my_decimal *);
+ uint size_of() const { return sizeof(*this); }
+ int reset(void) { return !maybe_null() || Field_blob::reset(); }
+ geometry_type get_geometry_type() { return geom_type; };
+};
+class Field_enum :public Field_str {
+protected:
+ uint packlength;
+public:
+ TYPELIB *typelib;
+ Field_enum(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg,
+ uchar null_bit_arg,
+ enum utype unireg_check_arg, const char *field_name_arg,
+ uint packlength_arg,
+ TYPELIB *typelib_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),
+ packlength(packlength_arg),typelib(typelib_arg)
+ {
+ flags|=256;
+ }
+ Field *new_field(MEM_ROOT *root, struct st_table *new_table, In_C_you_should_use_my_bool_instead() keep_type);
+ enum_field_types type() const { return MYSQL_TYPE_STRING; }
+ enum Item_result cmp_type () const { return INT_RESULT; }
+ enum Item_result cast_to_int_type () const { return INT_RESULT; }
+ enum ha_base_keytype key_type() const;
+ int store(const char *to,uint length,CHARSET_INFO *charset);
+ int store(double nr);
+ int store(longlong nr, In_C_you_should_use_my_bool_instead() unsigned_val);
+ double val_real(void);
+ longlong val_int(void);
+ String *val_str(String*,String *);
+ int cmp(const uchar *,const uchar *);
+ void sort_string(uchar *buff,uint length);
+ uint32 pack_length() const { return (uint32) packlength; }
+ void store_type(ulonglong value);
+ void sql_type(String &str) const;
+ uint size_of() const { return sizeof(*this); }
+ enum_field_types real_type() const { return MYSQL_TYPE_ENUM; }
+ uint pack_length_from_metadata(uint field_metadata)
+ { return (field_metadata & 0x00ff); }
+ uint row_pack_length() { return pack_length(); }
+ virtual In_C_you_should_use_my_bool_instead() zero_pack() const { return 0; }
+ In_C_you_should_use_my_bool_instead() optimize_range(uint idx, uint part) { return 0; }
+ In_C_you_should_use_my_bool_instead() eq_def(Field *field);
+ In_C_you_should_use_my_bool_instead() has_charset(void) const { return (1); }
+ CHARSET_INFO *sort_charset(void) const { return &my_charset_bin; }
+private:
+ int do_save_field_metadata(uchar *first_byte);
+};
+class Field_set :public Field_enum {
+public:
+ Field_set(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg,
+ uchar null_bit_arg,
+ enum utype unireg_check_arg, const char *field_name_arg,
+ uint32 packlength_arg,
+ TYPELIB *typelib_arg, CHARSET_INFO *charset_arg)
+ :Field_enum(ptr_arg, len_arg, null_ptr_arg, null_bit_arg,
+ unireg_check_arg, field_name_arg,
+ packlength_arg,
+ typelib_arg,charset_arg)
+ {
+ flags=(flags & ~256) | 2048;
+ }
+ int store(const char *to,uint length,CHARSET_INFO *charset);
+ int store(double nr) { return Field_set::store((longlong) nr, (0)); }
+ int store(longlong nr, In_C_you_should_use_my_bool_instead() unsigned_val);
+ virtual In_C_you_should_use_my_bool_instead() zero_pack() const { return 1; }
+ String *val_str(String*,String *);
+ void sql_type(String &str) const;
+ enum_field_types real_type() const { return MYSQL_TYPE_SET; }
+ In_C_you_should_use_my_bool_instead() has_charset(void) const { return (1); }
+};
+class Field_bit :public Field {
+public:
+ uchar *bit_ptr;
+ uchar bit_ofs;
+ uint bit_len;
+ uint bytes_in_rec;
+ Field_bit(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg,
+ uchar null_bit_arg, uchar *bit_ptr_arg, uchar bit_ofs_arg,
+ enum utype unireg_check_arg, const char *field_name_arg);
+ enum_field_types type() const { return MYSQL_TYPE_BIT; }
+ enum ha_base_keytype key_type() const { return HA_KEYTYPE_BIT; }
+ uint32 key_length() const { return (uint32) (field_length + 7) / 8; }
+ uint32 max_data_length() const { return (field_length + 7) / 8; }
+ uint32 max_display_length() { return field_length; }
+ uint size_of() const { return sizeof(*this); }
+ Item_result result_type () const { return INT_RESULT; }
+ int reset(void) { bzero(ptr, bytes_in_rec); return 0; }
+ int store(const char *to, uint length, CHARSET_INFO *charset);
+ int store(double nr);
+ int store(longlong nr, In_C_you_should_use_my_bool_instead() unsigned_val);
+ int store_decimal(const my_decimal *);
+ double val_real(void);
+ longlong val_int(void);
+ String *val_str(String*, String *);
+ virtual In_C_you_should_use_my_bool_instead() str_needs_quotes() { return (1); }
+ my_decimal *val_decimal(my_decimal *);
+ int cmp(const uchar *a, const uchar *b)
+ {
+ assert(ptr == a);
+ return Field_bit::key_cmp(b, bytes_in_rec+((bit_len) ? 1 : 0));
+ }
+ int cmp_binary_offset(uint row_offset)
+ { return cmp_offset(row_offset); }
+ int cmp_max(const uchar *a, const uchar *b, uint max_length);
+ int key_cmp(const uchar *a, const uchar *b)
+ { return cmp_binary((uchar *) a, (uchar *) b); }
+ int key_cmp(const uchar *str, uint length);
+ int cmp_offset(uint row_offset);
+ void get_image(uchar *buff, uint length, CHARSET_INFO *cs)
+ { get_key_image(buff, length, itRAW); }
+ void set_image(const uchar *buff,uint length, CHARSET_INFO *cs)
+ { Field_bit::store((char *) buff, length, cs); }
+ uint get_key_image(uchar *buff, uint length, imagetype type);
+ void set_key_image(const uchar *buff, uint length)
+ { Field_bit::store((char*) buff, length, &my_charset_bin); }
+ void sort_string(uchar *buff, uint length)
+ { get_key_image(buff, length, itRAW); }
+ uint32 pack_length() const { return (uint32) (field_length + 7) / 8; }
+ uint32 pack_length_in_rec() const { return bytes_in_rec; }
+ uint pack_length_from_metadata(uint field_metadata);
+ uint row_pack_length()
+ { return (bytes_in_rec + ((bit_len > 0) ? 1 : 0)); }
+ int compatible_field_size(uint field_metadata);
+ void sql_type(String &str) const;
+ virtual uchar *pack(uchar *to, const uchar *from,
+ uint max_length, In_C_you_should_use_my_bool_instead() low_byte_first);
+ virtual const uchar *unpack(uchar *to, const uchar *from,
+ uint param_data, In_C_you_should_use_my_bool_instead() low_byte_first);
+ virtual void set_default();
+ Field *new_key_field(MEM_ROOT *root, struct st_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)
+ {
+ bit_ptr= bit_ptr_arg;
+ bit_ofs= bit_ofs_arg;
+ }
+ In_C_you_should_use_my_bool_instead() eq(Field *field)
+ {
+ return (Field::eq(field) &&
+ field->type() == type() &&
+ bit_ptr == ((Field_bit *)field)->bit_ptr &&
+ bit_ofs == ((Field_bit *)field)->bit_ofs);
+ }
+ uint is_equal(Create_field *new_field);
+ void move_field_offset(my_ptrdiff_t ptr_diff)
+ {
+ Field::move_field_offset(ptr_diff);
+ bit_ptr= (uchar*) ((uchar*) (bit_ptr)+ptr_diff);
+ }
+ void hash(ulong *nr, ulong *nr2);
+private:
+ virtual size_t do_last_null_byte() const;
+ int do_save_field_metadata(uchar *first_byte);
+};
+class Field_bit_as_char: public Field_bit {
+public:
+ Field_bit_as_char(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg,
+ uchar null_bit_arg,
+ enum utype unireg_check_arg, const char *field_name_arg);
+ enum ha_base_keytype key_type() const { return HA_KEYTYPE_BINARY; }
+ uint size_of() const { return sizeof(*this); }
+ int store(const char *to, uint length, CHARSET_INFO *charset);
+ int store(double nr) { return Field_bit::store(nr); }
+ int store(longlong nr, In_C_you_should_use_my_bool_instead() unsigned_val)
+ { return Field_bit::store(nr, unsigned_val); }
+ void sql_type(String &str) const;
+};
+class Create_field :public Sql_alloc
+{
+public:
+ const char *field_name;
+ const char *change;
+ const char *after;
+ LEX_STRING comment;
+ Item *def;
+ enum enum_field_types sql_type;
+ ulong length;
+ uint32 char_length;
+ uint decimals, flags, pack_length, key_length;
+ Field::utype unireg_check;
+ TYPELIB *interval;
+ TYPELIB *save_interval;
+ List<String> interval_list;
+ CHARSET_INFO *charset;
+ Field::geometry_type geom_type;
+ Field *field;
+ uint8 row,col,sc_length,interval_id;
+ uint offset,pack_flag;
+ Create_field() :after(0) {}
+ Create_field(Field *field, Field *orig_field);
+ Create_field *clone(MEM_ROOT *mem_root) const
+ { return new (mem_root) Create_field(*this); }
+ void create_length_to_internal_length(void);
+ void init_for_tmp_table(enum_field_types sql_type_arg,
+ uint32 max_length, uint32 decimals,
+ In_C_you_should_use_my_bool_instead() maybe_null, In_C_you_should_use_my_bool_instead() is_unsigned);
+ In_C_you_should_use_my_bool_instead() init(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);
+};
+class Send_field {
+ public:
+ const char *db_name;
+ const char *table_name,*org_table_name;
+ const char *col_name,*org_col_name;
+ ulong length;
+ uint charsetnr, flags, decimals;
+ enum_field_types type;
+ Send_field() {}
+};
+class Copy_field :public Sql_alloc {
+ typedef void Copy_func(Copy_field*);
+ Copy_func *get_copy_func(Field *to, Field *from);
+public:
+ uchar *from_ptr,*to_ptr;
+ uchar *from_null_ptr,*to_null_ptr;
+ my_bool *null_row;
+ uint from_bit,to_bit;
+ uint from_length,to_length;
+ Field *from_field,*to_field;
+ String tmp;
+ Copy_field() {}
+ ~Copy_field() {}
+ void set(Field *to,Field *from,In_C_you_should_use_my_bool_instead() save);
+ void set(uchar *to,Field *from);
+ void (*do_copy)(Copy_field *);
+ void (*do_copy2)(Copy_field *);
+};
+Field *make_field(TABLE_SHARE *share, uchar *ptr, uint32 field_length,
+ uchar *null_pos, uchar null_bit,
+ uint pack_flag, enum_field_types field_type,
+ CHARSET_INFO *cs,
+ Field::geometry_type geom_type,
+ Field::utype unireg_check,
+ TYPELIB *interval, const char *field_name);
+uint pack_length_to_packflag(uint type);
+enum_field_types get_blob_type_from_length(ulong length);
+uint32 calc_pack_length(enum_field_types type,uint32 length);
+int set_field_to_null(Field *field);
+int set_field_to_null_with_conversions(Field *field, In_C_you_should_use_my_bool_instead() no_conversions);
+#include "protocol.h"
+class i_string;
+class THD;
+typedef struct st_mysql_field MYSQL_FIELD;
+typedef struct st_mysql_rows MYSQL_ROWS;
+class Protocol
+{
+protected:
+ THD *thd;
+ String *packet;
+ String *convert;
+ uint field_pos;
+ enum enum_field_types *field_types;
+ uint field_count;
+ In_C_you_should_use_my_bool_instead() net_store_data(const uchar *from, size_t length);
+ In_C_you_should_use_my_bool_instead() store_string_aux(const char *from, size_t length,
+ CHARSET_INFO *fromcs, CHARSET_INFO *tocs);
+public:
+ Protocol() {}
+ Protocol(THD *thd_arg) { init(thd_arg); }
+ virtual ~Protocol() {}
+ void init(THD* thd_arg);
+ enum { SEND_NUM_ROWS= 1, SEND_DEFAULTS= 2, SEND_EOF= 4 };
+ virtual In_C_you_should_use_my_bool_instead() send_fields(List<Item> *list, uint flags);
+ In_C_you_should_use_my_bool_instead() store(I_List<i_string> *str_list);
+ In_C_you_should_use_my_bool_instead() store(const char *from, CHARSET_INFO *cs);
+ String *storage_packet() { return packet; }
+ inline void free() { packet->free(); }
+ virtual In_C_you_should_use_my_bool_instead() write();
+ inline In_C_you_should_use_my_bool_instead() store(int from)
+ { return store_long((longlong) from); }
+ inline In_C_you_should_use_my_bool_instead() store(uint32 from)
+ { return store_long((longlong) from); }
+ inline In_C_you_should_use_my_bool_instead() store(longlong from)
+ { return store_longlong((longlong) from, 0); }
+ inline In_C_you_should_use_my_bool_instead() store(ulonglong from)
+ { return store_longlong((longlong) from, 1); }
+ inline In_C_you_should_use_my_bool_instead() store(String *str)
+ { return store((char*) str->ptr(), str->length(), str->charset()); }
+ virtual In_C_you_should_use_my_bool_instead() prepare_for_send(List<Item> *item_list)
+ {
+ field_count=item_list->elements;
+ return 0;
+ }
+ virtual In_C_you_should_use_my_bool_instead() flush();
+ virtual void end_partial_result_set(THD *thd);
+ virtual void prepare_for_resend()=0;
+ virtual In_C_you_should_use_my_bool_instead() store_null()=0;
+ virtual In_C_you_should_use_my_bool_instead() store_tiny(longlong from)=0;
+ virtual In_C_you_should_use_my_bool_instead() store_short(longlong from)=0;
+ virtual In_C_you_should_use_my_bool_instead() store_long(longlong from)=0;
+ virtual In_C_you_should_use_my_bool_instead() store_longlong(longlong from, In_C_you_should_use_my_bool_instead() unsigned_flag)=0;
+ virtual In_C_you_should_use_my_bool_instead() store_decimal(const my_decimal *)=0;
+ virtual In_C_you_should_use_my_bool_instead() store(const char *from, size_t length, CHARSET_INFO *cs)=0;
+ virtual In_C_you_should_use_my_bool_instead() store(const char *from, size_t length,
+ CHARSET_INFO *fromcs, CHARSET_INFO *tocs)=0;
+ virtual In_C_you_should_use_my_bool_instead() store(float from, uint32 decimals, String *buffer)=0;
+ virtual In_C_you_should_use_my_bool_instead() store(double from, uint32 decimals, String *buffer)=0;
+ virtual In_C_you_should_use_my_bool_instead() store(MYSQL_TIME *time)=0;
+ virtual In_C_you_should_use_my_bool_instead() store_date(MYSQL_TIME *time)=0;
+ virtual In_C_you_should_use_my_bool_instead() store_time(MYSQL_TIME *time)=0;
+ virtual In_C_you_should_use_my_bool_instead() store(Field *field)=0;
+ void remove_last_row() {}
+ enum enum_protocol_type
+ {
+ PROTOCOL_TEXT= 0, PROTOCOL_BINARY= 1
+ };
+ virtual enum enum_protocol_type type()= 0;
+};
+class Protocol_text :public Protocol
+{
+public:
+ Protocol_text() {}
+ Protocol_text(THD *thd_arg) :Protocol(thd_arg) {}
+ virtual void prepare_for_resend();
+ virtual In_C_you_should_use_my_bool_instead() store_null();
+ virtual In_C_you_should_use_my_bool_instead() store_tiny(longlong from);
+ virtual In_C_you_should_use_my_bool_instead() store_short(longlong from);
+ virtual In_C_you_should_use_my_bool_instead() store_long(longlong from);
+ virtual In_C_you_should_use_my_bool_instead() store_longlong(longlong from, In_C_you_should_use_my_bool_instead() unsigned_flag);
+ virtual In_C_you_should_use_my_bool_instead() store_decimal(const my_decimal *);
+ virtual In_C_you_should_use_my_bool_instead() store(const char *from, size_t length, CHARSET_INFO *cs);
+ virtual In_C_you_should_use_my_bool_instead() store(const char *from, size_t length,
+ CHARSET_INFO *fromcs, CHARSET_INFO *tocs);
+ virtual In_C_you_should_use_my_bool_instead() store(MYSQL_TIME *time);
+ virtual In_C_you_should_use_my_bool_instead() store_date(MYSQL_TIME *time);
+ virtual In_C_you_should_use_my_bool_instead() store_time(MYSQL_TIME *time);
+ virtual In_C_you_should_use_my_bool_instead() store(float nr, uint32 decimals, String *buffer);
+ virtual In_C_you_should_use_my_bool_instead() store(double from, uint32 decimals, String *buffer);
+ virtual In_C_you_should_use_my_bool_instead() store(Field *field);
+ virtual enum enum_protocol_type type() { return PROTOCOL_TEXT; };
+};
+class Protocol_binary :public Protocol
+{
+private:
+ uint bit_fields;
+public:
+ Protocol_binary() {}
+ Protocol_binary(THD *thd_arg) :Protocol(thd_arg) {}
+ virtual In_C_you_should_use_my_bool_instead() prepare_for_send(List<Item> *item_list);
+ virtual void prepare_for_resend();
+ virtual In_C_you_should_use_my_bool_instead() store_null();
+ virtual In_C_you_should_use_my_bool_instead() store_tiny(longlong from);
+ virtual In_C_you_should_use_my_bool_instead() store_short(longlong from);
+ virtual In_C_you_should_use_my_bool_instead() store_long(longlong from);
+ virtual In_C_you_should_use_my_bool_instead() store_longlong(longlong from, In_C_you_should_use_my_bool_instead() unsigned_flag);
+ virtual In_C_you_should_use_my_bool_instead() store_decimal(const my_decimal *);
+ virtual In_C_you_should_use_my_bool_instead() store(const char *from, size_t length, CHARSET_INFO *cs);
+ virtual In_C_you_should_use_my_bool_instead() store(const char *from, size_t length,
+ CHARSET_INFO *fromcs, CHARSET_INFO *tocs);
+ virtual In_C_you_should_use_my_bool_instead() store(MYSQL_TIME *time);
+ virtual In_C_you_should_use_my_bool_instead() store_date(MYSQL_TIME *time);
+ virtual In_C_you_should_use_my_bool_instead() store_time(MYSQL_TIME *time);
+ virtual In_C_you_should_use_my_bool_instead() store(float nr, uint32 decimals, String *buffer);
+ virtual In_C_you_should_use_my_bool_instead() store(double from, uint32 decimals, String *buffer);
+ virtual In_C_you_should_use_my_bool_instead() store(Field *field);
+ virtual enum enum_protocol_type type() { return PROTOCOL_BINARY; };
+};
+void send_warning(THD *thd, uint sql_errno, const char *err=0);
+void net_send_error(THD *thd, uint sql_errno=0, const char *err=0);
+void net_end_statement(THD *thd);
+In_C_you_should_use_my_bool_instead() send_old_password_request(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);
+#include "sql_udf.h"
+enum Item_udftype {UDFTYPE_FUNCTION=1,UDFTYPE_AGGREGATE};
+typedef void (*Udf_func_clear)(UDF_INIT *, uchar *, uchar *);
+typedef void (*Udf_func_add)(UDF_INIT *, UDF_ARGS *, uchar *, uchar *);
+typedef void (*Udf_func_deinit)(UDF_INIT*);
+typedef my_bool (*Udf_func_init)(UDF_INIT *, UDF_ARGS *, char *);
+typedef void (*Udf_func_any)();
+typedef double (*Udf_func_double)(UDF_INIT *, UDF_ARGS *, uchar *, uchar *);
+typedef longlong (*Udf_func_longlong)(UDF_INIT *, UDF_ARGS *, uchar *,
+ uchar *);
+typedef struct st_udf_func
+{
+ LEX_STRING name;
+ Item_result returns;
+ Item_udftype type;
+ char *dl;
+ void *dlhandle;
+ Udf_func_any func;
+ Udf_func_init func_init;
+ Udf_func_deinit func_deinit;
+ Udf_func_clear func_clear;
+ Udf_func_add func_add;
+ ulong usage_count;
+} udf_func;
+class Item_result_field;
+class udf_handler :public Sql_alloc
+{
+ protected:
+ udf_func *u_d;
+ String *buffers;
+ UDF_ARGS f_args;
+ UDF_INIT initid;
+ char *num_buffer;
+ uchar error, is_null;
+ In_C_you_should_use_my_bool_instead() initialized;
+ Item **args;
+ public:
+ table_map used_tables_cache;
+ In_C_you_should_use_my_bool_instead() const_item_cache;
+ In_C_you_should_use_my_bool_instead() not_original;
+ udf_handler(udf_func *udf_arg) :u_d(udf_arg), buffers(0), error(0),
+ is_null(0), initialized(0), not_original(0)
+ {}
+ ~udf_handler();
+ const char *name() const { return u_d ? u_d->name.str : "?"; }
+ Item_result result_type () const
+ { return u_d ? u_d->returns : STRING_RESULT;}
+ In_C_you_should_use_my_bool_instead() get_arguments();
+ In_C_you_should_use_my_bool_instead() fix_fields(THD *thd, Item_result_field *item,
+ uint arg_count, Item **args);
+ void cleanup();
+ double val(my_bool *null_value)
+ {
+ is_null= 0;
+ if (get_arguments())
+ {
+ *null_value=1;
+ return 0.0;
+ }
+ Udf_func_double func= (Udf_func_double) u_d->func;
+ double tmp=func(&initid, &f_args, &is_null, &error);
+ if (is_null || error)
+ {
+ *null_value=1;
+ return 0.0;
+ }
+ *null_value=0;
+ return tmp;
+ }
+ longlong val_int(my_bool *null_value)
+ {
+ is_null= 0;
+ if (get_arguments())
+ {
+ *null_value=1;
+ return 0LL;
+ }
+ Udf_func_longlong func= (Udf_func_longlong) u_d->func;
+ longlong tmp=func(&initid, &f_args, &is_null, &error);
+ if (is_null || error)
+ {
+ *null_value=1;
+ return 0LL;
+ }
+ *null_value=0;
+ return tmp;
+ }
+ my_decimal *val_decimal(my_bool *null_value, my_decimal *dec_buf);
+ void clear()
+ {
+ is_null= 0;
+ Udf_func_clear func= u_d->func_clear;
+ func(&initid, &is_null, &error);
+ }
+ void add(my_bool *null_value)
+ {
+ if (get_arguments())
+ {
+ *null_value=1;
+ return;
+ }
+ Udf_func_add func= u_d->func_add;
+ func(&initid, &f_args, &is_null, &error);
+ *null_value= (my_bool) (is_null || error);
+ }
+ String *val_str(String *str,String *save_str);
+};
+void udf_init(void),udf_free(void);
+udf_func *find_udf(const char *name, uint len=0,In_C_you_should_use_my_bool_instead() mark_used=0);
+void free_udf(udf_func *udf);
+int mysql_create_function(THD *thd,udf_func *udf);
+int mysql_drop_function(THD *thd,const LEX_STRING *name);
+#include "sql_profile.h"
+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);
+#include "sql_partition.h"
+#pragma interface
+typedef struct {
+ longlong list_value;
+ uint32 partition_id;
+} LIST_PART_ENTRY;
+typedef struct {
+ uint32 start_part;
+ uint32 end_part;
+} part_id_range;
+struct st_partition_iter;
+In_C_you_should_use_my_bool_instead() is_partition_in_list(char *part_name, List<char> list_part_names);
+char *are_partitions_in_table(partition_info *new_part_info,
+ partition_info *old_part_info);
+In_C_you_should_use_my_bool_instead() check_reorganise_list(partition_info *new_part_info,
+ partition_info *old_part_info,
+ List<char> list_part_names);
+handler *get_ha_partition(partition_info *part_info);
+int get_parts_for_update(const uchar *old_data, uchar *new_data,
+ const uchar *rec0, partition_info *part_info,
+ uint32 *old_part_id, uint32 *new_part_id,
+ longlong *func_value);
+int get_part_for_delete(const uchar *buf, const uchar *rec0,
+ partition_info *part_info, uint32 *part_id);
+void prune_partition_set(const TABLE *table, part_id_range *part_spec);
+In_C_you_should_use_my_bool_instead() 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);
+In_C_you_should_use_my_bool_instead() fix_partition_func(THD *thd, TABLE *table, In_C_you_should_use_my_bool_instead() create_table_ind);
+char *generate_partition_syntax(partition_info *part_info,
+ uint *buf_length, In_C_you_should_use_my_bool_instead() use_sql_alloc,
+ In_C_you_should_use_my_bool_instead() show_partition_options);
+In_C_you_should_use_my_bool_instead() 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);
+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);
+In_C_you_should_use_my_bool_instead() mysql_unpack_partition(THD *thd, const char *part_buf,
+ uint part_info_len,
+ const char *part_state, uint part_state_len,
+ TABLE *table, In_C_you_should_use_my_bool_instead() is_create_table_ind,
+ handlerton *default_db_type,
+ In_C_you_should_use_my_bool_instead() *work_part_info_used);
+void make_used_partitions_str(partition_info *part_info, String *parts_str);
+uint32 get_list_array_idx_for_endpoint(partition_info *part_info,
+ In_C_you_should_use_my_bool_instead() left_endpoint,
+ In_C_you_should_use_my_bool_instead() include_endpoint);
+uint32 get_partition_id_range_for_endpoint(partition_info *part_info,
+ In_C_you_should_use_my_bool_instead() left_endpoint,
+ In_C_you_should_use_my_bool_instead() include_endpoint);
+In_C_you_should_use_my_bool_instead() fix_fields_part_func(THD *thd, Item* func_expr, TABLE *table,
+ In_C_you_should_use_my_bool_instead() is_sub_part, In_C_you_should_use_my_bool_instead() is_field_to_be_setup);
+In_C_you_should_use_my_bool_instead() check_part_func_fields(Field **ptr, In_C_you_should_use_my_bool_instead() ok_with_charsets);
+In_C_you_should_use_my_bool_instead() field_is_partition_charset(Field *field);
+typedef uint32 (*partition_iter_func)(st_partition_iter* part_iter);
+typedef struct st_partition_iter
+{
+ partition_iter_func get_next;
+ In_C_you_should_use_my_bool_instead() ret_null_part, ret_null_part_orig;
+ struct st_part_num_range
+ {
+ uint32 start;
+ uint32 cur;
+ uint32 end;
+ };
+ struct st_field_value_range
+ {
+ longlong start;
+ longlong cur;
+ longlong end;
+ };
+ union
+ {
+ struct st_part_num_range part_nums;
+ struct st_field_value_range field_vals;
+ };
+ partition_info *part_info;
+} PARTITION_ITERATOR;
+typedef int (*get_partitions_in_range_iter)(partition_info *part_info,
+ In_C_you_should_use_my_bool_instead() is_subpart,
+ uchar *min_val, uchar *max_val,
+ uint flags,
+ PARTITION_ITERATOR *part_iter);
+#include "partition_info.h"
+#include "partition_element.h"
+enum partition_type {
+ NOT_A_PARTITION= 0,
+ RANGE_PARTITION,
+ HASH_PARTITION,
+ LIST_PARTITION
+};
+enum partition_state {
+ PART_NORMAL= 0,
+ PART_IS_DROPPED= 1,
+ PART_TO_BE_DROPPED= 2,
+ PART_TO_BE_ADDED= 3,
+ PART_TO_BE_REORGED= 4,
+ PART_REORGED_DROPPED= 5,
+ PART_CHANGED= 6,
+ PART_IS_CHANGED= 7,
+ PART_IS_ADDED= 8
+};
+typedef struct p_elem_val
+{
+ longlong value;
+ In_C_you_should_use_my_bool_instead() null_value;
+ In_C_you_should_use_my_bool_instead() unsigned_flag;
+} part_elem_value;
+struct st_ddl_log_memory_entry;
+class partition_element :public Sql_alloc {
+public:
+ List<partition_element> subpartitions;
+ List<part_elem_value> list_val_list;
+ ha_rows part_max_rows;
+ ha_rows part_min_rows;
+ longlong range_value;
+ char *partition_name;
+ char *tablespace_name;
+ struct st_ddl_log_memory_entry *log_entry;
+ char* part_comment;
+ char* data_file_name;
+ char* index_file_name;
+ handlerton *engine_type;
+ enum partition_state part_state;
+ uint16 nodegroup_id;
+ In_C_you_should_use_my_bool_instead() has_null_value;
+ In_C_you_should_use_my_bool_instead() signed_flag;
+ In_C_you_should_use_my_bool_instead() max_value;
+ partition_element()
+ : part_max_rows(0), part_min_rows(0), range_value(0),
+ partition_name(NULL), tablespace_name(NULL),
+ log_entry(NULL), part_comment(NULL),
+ data_file_name(NULL), index_file_name(NULL),
+ engine_type(NULL), part_state(PART_NORMAL),
+ nodegroup_id(65535), has_null_value((0)),
+ signed_flag((0)), max_value((0))
+ {
+ }
+ partition_element(partition_element *part_elem)
+ : part_max_rows(part_elem->part_max_rows),
+ part_min_rows(part_elem->part_min_rows),
+ range_value(0), partition_name(NULL),
+ tablespace_name(part_elem->tablespace_name),
+ part_comment(part_elem->part_comment),
+ data_file_name(part_elem->data_file_name),
+ index_file_name(part_elem->index_file_name),
+ engine_type(part_elem->engine_type),
+ part_state(part_elem->part_state),
+ nodegroup_id(part_elem->nodegroup_id),
+ has_null_value((0))
+ {
+ }
+ ~partition_element() {}
+};
+class partition_info;
+typedef int (*get_part_id_func)(partition_info *part_info,
+ uint32 *part_id,
+ longlong *func_value);
+typedef uint32 (*get_subpart_id_func)(partition_info *part_info);
+struct st_ddl_log_memory_entry;
+class partition_info : public Sql_alloc
+{
+public:
+ List<partition_element> partitions;
+ List<partition_element> temp_partitions;
+ List<char> part_field_list;
+ List<char> subpart_field_list;
+ get_part_id_func get_partition_id;
+ get_part_id_func get_part_partition_id;
+ get_subpart_id_func 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;
+ Field **part_field_array;
+ Field **subpart_field_array;
+ Field **part_charset_field_array;
+ Field **subpart_charset_field_array;
+ Field **full_part_field_array;
+ Field **full_part_charset_field_array;
+ MY_BITMAP full_part_field_set;
+ 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;
+ Item *item_free_list;
+ struct st_ddl_log_memory_entry *first_log_entry;
+ struct st_ddl_log_memory_entry *exec_log_entry;
+ struct st_ddl_log_memory_entry *frm_log_entry;
+ MY_BITMAP used_partitions;
+ union {
+ longlong *range_int_array;
+ LIST_PART_ENTRY *list_array;
+ };
+ get_partitions_in_range_iter get_part_iter_for_interval;
+ get_partitions_in_range_iter get_subpart_iter_for_interval;
+ longlong err_value;
+ char* part_info_string;
+ char *part_func_string;
+ char *subpart_func_string;
+ const char *part_state;
+ partition_element *curr_part_elem;
+ partition_element *current_partition;
+ key_map all_fields_in_PF, all_fields_in_PPF, all_fields_in_SPF;
+ 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 count_curr_subparts;
+ uint part_error_code;
+ uint no_list_values;
+ uint no_part_fields;
+ uint no_subpart_fields;
+ uint no_full_part_fields;
+ uint has_null_part_id;
+ uint16 linear_hash_mask;
+ In_C_you_should_use_my_bool_instead() use_default_partitions;
+ In_C_you_should_use_my_bool_instead() use_default_no_partitions;
+ In_C_you_should_use_my_bool_instead() use_default_subpartitions;
+ In_C_you_should_use_my_bool_instead() use_default_no_subpartitions;
+ In_C_you_should_use_my_bool_instead() default_partitions_setup;
+ In_C_you_should_use_my_bool_instead() defined_max_value;
+ In_C_you_should_use_my_bool_instead() list_of_part_fields;
+ In_C_you_should_use_my_bool_instead() list_of_subpart_fields;
+ In_C_you_should_use_my_bool_instead() linear_hash_ind;
+ In_C_you_should_use_my_bool_instead() fixed;
+ In_C_you_should_use_my_bool_instead() is_auto_partitioned;
+ In_C_you_should_use_my_bool_instead() from_openfrm;
+ In_C_you_should_use_my_bool_instead() has_null_value;
+ partition_info()
+ : get_partition_id(NULL), get_part_partition_id(NULL),
+ get_subpartition_id(NULL),
+ part_field_array(NULL), subpart_field_array(NULL),
+ 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),
+ 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_func_len(0), subpart_func_len(0),
+ no_parts(0), no_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((1)), use_default_no_partitions((1)),
+ use_default_subpartitions((1)), use_default_no_subpartitions((1)),
+ default_partitions_setup((0)), defined_max_value((0)),
+ list_of_part_fields((0)), list_of_subpart_fields((0)),
+ linear_hash_ind((0)), fixed((0)),
+ is_auto_partitioned((0)), from_openfrm((0)),
+ has_null_value((0))
+ {
+ all_fields_in_PF.clear_all();
+ all_fields_in_PPF.clear_all();
+ all_fields_in_SPF.clear_all();
+ some_fields_in_PF.clear_all();
+ partitions.empty();
+ temp_partitions.empty();
+ part_field_list.empty();
+ subpart_field_list.empty();
+ }
+ ~partition_info() {}
+ partition_info *get_clone();
+ In_C_you_should_use_my_bool_instead() is_sub_partitioned()
+ {
+ return (subpart_type == NOT_A_PARTITION ? (0) : (1));
+ }
+ uint get_tot_partitions()
+ {
+ return no_parts * (is_sub_partitioned() ? no_subparts : 1);
+ }
+ In_C_you_should_use_my_bool_instead() set_up_defaults_for_partitioning(handler *file, HA_CREATE_INFO *info,
+ uint start_no);
+ char *has_unique_names();
+ In_C_you_should_use_my_bool_instead() check_engine_mix(handlerton *engine_type, In_C_you_should_use_my_bool_instead() default_engine);
+ In_C_you_should_use_my_bool_instead() check_range_constants();
+ In_C_you_should_use_my_bool_instead() check_list_constants();
+ In_C_you_should_use_my_bool_instead() check_partition_info(THD *thd, handlerton **eng_type,
+ handler *file, HA_CREATE_INFO *info,
+ In_C_you_should_use_my_bool_instead() check_partition_function);
+ void print_no_partition_found(TABLE *table);
+ In_C_you_should_use_my_bool_instead() set_up_charset_field_preps();
+private:
+ static int list_part_cmp(const void* a, const void* b);
+ static int list_part_cmp_unsigned(const void* a, const void* b);
+ In_C_you_should_use_my_bool_instead() set_up_default_partitions(handler *file, HA_CREATE_INFO *info,
+ uint start_no);
+ In_C_you_should_use_my_bool_instead() set_up_default_subpartitions(handler *file, HA_CREATE_INFO *info);
+ char *create_default_partition_names(uint part_no, uint no_parts,
+ uint start_no);
+ char *create_subpartition_name(uint subpart_no, const char *part_name);
+ In_C_you_should_use_my_bool_instead() has_unique_name(partition_element *element);
+};
+uint32 get_next_partition_id_range(struct st_partition_iter* part_iter);
+In_C_you_should_use_my_bool_instead() check_partition_dirs(partition_info *part_info);
+static inline void init_single_partition_iterator(uint32 part_id,
+ PARTITION_ITERATOR *part_iter)
+{
+ part_iter->part_nums.start= part_iter->part_nums.cur= part_id;
+ part_iter->part_nums.end= part_id+1;
+ part_iter->get_next= get_next_partition_id_range;
+}
+static inline
+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->get_next= get_next_partition_id_range;
+}
+class user_var_entry;
+class Security_context;
+enum enum_var_type
+{
+ OPT_DEFAULT= 0, OPT_SESSION, OPT_GLOBAL
+};
+class sys_var;
+#include "item.h"
+class Protocol;
+struct TABLE_LIST;
+void item_init(void);
+class Item_field;
+class DTCollation {
+public:
+ CHARSET_INFO *collation;
+ enum Derivation derivation;
+ uint repertoire;
+ void set_repertoire_from_charset(CHARSET_INFO *cs)
+ {
+ repertoire= cs->state & 4096 ?
+ 1 : 3;
+ }
+ DTCollation()
+ {
+ collation= &my_charset_bin;
+ derivation= DERIVATION_NONE;
+ repertoire= 3;
+ }
+ DTCollation(CHARSET_INFO *collation_arg, Derivation derivation_arg)
+ {
+ collation= collation_arg;
+ derivation= derivation_arg;
+ set_repertoire_from_charset(collation_arg);
+ }
+ void set(DTCollation &dt)
+ {
+ collation= dt.collation;
+ derivation= dt.derivation;
+ repertoire= dt.repertoire;
+ }
+ void set(CHARSET_INFO *collation_arg, Derivation derivation_arg)
+ {
+ collation= collation_arg;
+ derivation= derivation_arg;
+ set_repertoire_from_charset(collation_arg);
+ }
+ void set(CHARSET_INFO *collation_arg,
+ Derivation derivation_arg,
+ uint repertoire_arg)
+ {
+ collation= collation_arg;
+ derivation= derivation_arg;
+ repertoire= repertoire_arg;
+ }
+ void set(CHARSET_INFO *collation_arg)
+ {
+ collation= collation_arg;
+ set_repertoire_from_charset(collation_arg);
+ }
+ void set(Derivation derivation_arg)
+ { derivation= derivation_arg; }
+ In_C_you_should_use_my_bool_instead() aggregate(DTCollation &dt, uint flags= 0);
+ In_C_you_should_use_my_bool_instead() set(DTCollation &dt1, DTCollation &dt2, uint flags= 0)
+ { set(dt1); return aggregate(dt2, flags); }
+ const char *derivation_name() const
+ {
+ switch(derivation)
+ {
+ case DERIVATION_IGNORABLE: return "IGNORABLE";
+ case DERIVATION_COERCIBLE: return "COERCIBLE";
+ case DERIVATION_IMPLICIT: return "IMPLICIT";
+ case DERIVATION_SYSCONST: return "SYSCONST";
+ case DERIVATION_EXPLICIT: return "EXPLICIT";
+ case DERIVATION_NONE: return "NONE";
+ default: return "UNKNOWN";
+ }
+ }
+};
+struct Hybrid_type_traits;
+struct Hybrid_type
+{
+ longlong integer;
+ double real;
+ my_decimal dec_buf[3];
+ int used_dec_buf_no;
+ const Hybrid_type_traits *traits;
+ Hybrid_type() {}
+ Hybrid_type(const Hybrid_type &rhs) :traits(rhs.traits) {}
+};
+struct Hybrid_type_traits
+{
+ virtual Item_result type() const { return REAL_RESULT; }
+ virtual void
+ fix_length_and_dec(Item *item, Item *arg) const;
+ virtual void set_zero(Hybrid_type *val) const { val->real= 0.0; }
+ virtual void add(Hybrid_type *val, Field *f) const
+ { val->real+= f->val_real(); }
+ virtual void div(Hybrid_type *val, ulonglong u) const
+ { val->real/= ((double) (ulonglong) (u)); }
+ virtual longlong val_int(Hybrid_type *val, In_C_you_should_use_my_bool_instead() unsigned_flag) const
+ { return (longlong) rint(val->real); }
+ virtual double val_real(Hybrid_type *val) const { return val->real; }
+ virtual my_decimal *val_decimal(Hybrid_type *val, my_decimal *buf) const;
+ virtual String *val_str(Hybrid_type *val, String *buf, uint8 decimals) const;
+ static const Hybrid_type_traits *instance();
+ Hybrid_type_traits() {}
+ virtual ~Hybrid_type_traits() {}
+};
+struct Hybrid_type_traits_decimal: public Hybrid_type_traits
+{
+ virtual Item_result type() const { return DECIMAL_RESULT; }
+ virtual void
+ fix_length_and_dec(Item *arg, Item *item) const;
+ virtual void set_zero(Hybrid_type *val) const;
+ virtual void add(Hybrid_type *val, Field *f) const;
+ virtual void div(Hybrid_type *val, ulonglong u) const;
+ virtual longlong val_int(Hybrid_type *val, In_C_you_should_use_my_bool_instead() unsigned_flag) const;
+ virtual double val_real(Hybrid_type *val) const;
+ virtual my_decimal *val_decimal(Hybrid_type *val, my_decimal *buf) const
+ { return &val->dec_buf[val->used_dec_buf_no]; }
+ virtual String *val_str(Hybrid_type *val, String *buf, uint8 decimals) const;
+ static const Hybrid_type_traits_decimal *instance();
+ Hybrid_type_traits_decimal() {};
+};
+struct Hybrid_type_traits_integer: public Hybrid_type_traits
+{
+ virtual Item_result type() const { return INT_RESULT; }
+ virtual void
+ fix_length_and_dec(Item *arg, Item *item) const;
+ virtual void set_zero(Hybrid_type *val) const
+ { val->integer= 0; }
+ virtual void add(Hybrid_type *val, Field *f) const
+ { val->integer+= f->val_int(); }
+ virtual void div(Hybrid_type *val, ulonglong u) const
+ { val->integer/= (longlong) u; }
+ virtual longlong val_int(Hybrid_type *val, In_C_you_should_use_my_bool_instead() unsigned_flag) const
+ { return val->integer; }
+ virtual double val_real(Hybrid_type *val) const
+ { return (double) val->integer; }
+ virtual my_decimal *val_decimal(Hybrid_type *val, my_decimal *buf) const
+ {
+ int2my_decimal(30, val->integer, 0, &val->dec_buf[2]);
+ return &val->dec_buf[2];
+ }
+ virtual String *val_str(Hybrid_type *val, String *buf, uint8 decimals) const
+ { buf->set(val->integer, &my_charset_bin); return buf;}
+ static const Hybrid_type_traits_integer *instance();
+ Hybrid_type_traits_integer() {};
+};
+void dummy_error_processor(THD *thd, void *data);
+void view_error_processor(THD *thd, void *data);
+struct Name_resolution_context: Sql_alloc
+{
+ Name_resolution_context *outer_context;
+ TABLE_LIST *table_list;
+ TABLE_LIST *first_name_resolution_table;
+ TABLE_LIST *last_name_resolution_table;
+ st_select_lex *select_lex;
+ void (*error_processor)(THD *, void *);
+ void *error_processor_data;
+ In_C_you_should_use_my_bool_instead() resolve_in_select_list;
+ Security_context *security_ctx;
+ Name_resolution_context()
+ :outer_context(0), table_list(0), select_lex(0),
+ error_processor_data(0),
+ security_ctx(0)
+ {}
+ void init()
+ {
+ resolve_in_select_list= (0);
+ error_processor= &dummy_error_processor;
+ first_name_resolution_table= NULL;
+ last_name_resolution_table= NULL;
+ }
+ void resolve_in_table_list_only(TABLE_LIST *tables)
+ {
+ table_list= first_name_resolution_table= tables;
+ resolve_in_select_list= (0);
+ }
+ void process_error(THD *thd)
+ {
+ (*error_processor)(thd, error_processor_data);
+ }
+};
+class Name_resolution_context_state
+{
+private:
+ TABLE_LIST *save_table_list;
+ TABLE_LIST *save_first_name_resolution_table;
+ TABLE_LIST *save_next_name_resolution_table;
+ In_C_you_should_use_my_bool_instead() save_resolve_in_select_list;
+ TABLE_LIST *save_next_local;
+public:
+ Name_resolution_context_state() {}
+public:
+ void save_state(Name_resolution_context *context, TABLE_LIST *table_list)
+ {
+ save_table_list= context->table_list;
+ save_first_name_resolution_table= context->first_name_resolution_table;
+ save_resolve_in_select_list= context->resolve_in_select_list;
+ save_next_local= table_list->next_local;
+ save_next_name_resolution_table= table_list->next_name_resolution_table;
+ }
+ void restore_state(Name_resolution_context *context, TABLE_LIST *table_list)
+ {
+ table_list->next_local= save_next_local;
+ table_list->next_name_resolution_table= save_next_name_resolution_table;
+ context->table_list= save_table_list;
+ context->first_name_resolution_table= save_first_name_resolution_table;
+ context->resolve_in_select_list= save_resolve_in_select_list;
+ }
+ TABLE_LIST *get_first_name_resolution_table()
+ {
+ return save_first_name_resolution_table;
+ }
+};
+typedef enum monotonicity_info
+{
+ NON_MONOTONIC,
+ MONOTONIC_INCREASING,
+ MONOTONIC_STRICT_INCREASING
+} enum_monotonicity_info;
+class sp_rcontext;
+class Settable_routine_parameter
+{
+public:
+ Settable_routine_parameter() {}
+ virtual ~Settable_routine_parameter() {}
+ virtual void set_required_privilege(In_C_you_should_use_my_bool_instead() rw) {};
+ virtual In_C_you_should_use_my_bool_instead() set_value(THD *thd, sp_rcontext *ctx, Item **it)= 0;
+};
+typedef In_C_you_should_use_my_bool_instead() (Item::*Item_processor) (uchar *arg);
+typedef In_C_you_should_use_my_bool_instead() (Item::*Item_analyzer) (uchar **argp);
+typedef Item* (Item::*Item_transformer) (uchar *arg);
+typedef void (*Cond_traverser) (const Item *item, void *arg);
+class Item {
+ Item(const Item &);
+ void operator=(Item &);
+public:
+ static void *operator new(size_t size)
+ { return sql_alloc(size); }
+ static void *operator new(size_t size, MEM_ROOT *mem_root)
+ { return alloc_root(mem_root, size); }
+ static void operator delete(void *ptr,size_t size) { ; }
+ static void operator delete(void *ptr, MEM_ROOT *mem_root) {}
+ enum Type {FIELD_ITEM= 0, FUNC_ITEM, SUM_FUNC_ITEM, STRING_ITEM,
+ INT_ITEM, REAL_ITEM, NULL_ITEM, VARBIN_ITEM,
+ COPY_STR_ITEM, FIELD_AVG_ITEM, DEFAULT_VALUE_ITEM,
+ PROC_ITEM,COND_ITEM, REF_ITEM, FIELD_STD_ITEM,
+ FIELD_VARIANCE_ITEM, INSERT_VALUE_ITEM,
+ SUBSELECT_ITEM, ROW_ITEM, CACHE_ITEM, TYPE_HOLDER,
+ PARAM_ITEM, TRIGGER_FIELD_ITEM, DECIMAL_ITEM,
+ XPATH_NODESET, XPATH_NODESET_CMP,
+ VIEW_FIXER_ITEM};
+ enum cond_result { COND_UNDEF,COND_OK,COND_TRUE,COND_FALSE };
+ enum traverse_order { POSTFIX, PREFIX };
+ uint rsize;
+ String str_value;
+ char * name;
+ char * orig_name;
+ Item *next;
+ uint32 max_length;
+ uint name_length;
+ int8 marker;
+ uint8 decimals;
+ my_bool maybe_null;
+ my_bool null_value;
+ my_bool unsigned_flag;
+ my_bool with_sum_func;
+ my_bool fixed;
+ my_bool is_autogenerated_name;
+ DTCollation collation;
+ my_bool with_subselect;
+ Item_result cmp_context;
+ Item();
+ Item(THD *thd, Item *item);
+ virtual ~Item()
+ {
+ }
+ void set_name(const char *str, uint length, CHARSET_INFO *cs);
+ void rename(char *new_name);
+ void init_make_field(Send_field *tmp_field,enum enum_field_types type);
+ virtual void cleanup();
+ virtual void make_field(Send_field *field);
+ Field *make_string_field(TABLE *table);
+ virtual In_C_you_should_use_my_bool_instead() fix_fields(THD *, Item **);
+ inline void quick_fix_field() { fixed= 1; }
+ int save_in_field_no_warnings(Field *field, In_C_you_should_use_my_bool_instead() no_conversions);
+ virtual int save_in_field(Field *field, In_C_you_should_use_my_bool_instead() no_conversions);
+ virtual void save_org_in_field(Field *field)
+ { (void) save_in_field(field, 1); }
+ virtual int save_safe_in_field(Field *field)
+ { return save_in_field(field, 1); }
+ virtual In_C_you_should_use_my_bool_instead() send(Protocol *protocol, String *str);
+ virtual In_C_you_should_use_my_bool_instead() eq(const Item *, In_C_you_should_use_my_bool_instead() binary_cmp) const;
+ virtual Item_result result_type() const { return REAL_RESULT; }
+ virtual Item_result cast_to_int_type() const { return result_type(); }
+ virtual enum_field_types string_field_type() const;
+ virtual enum_field_types field_type() const;
+ virtual enum Type type() const =0;
+ virtual enum_monotonicity_info get_monotonicity_info() const
+ { return NON_MONOTONIC; }
+ virtual longlong val_int_endpoint(In_C_you_should_use_my_bool_instead() left_endp, In_C_you_should_use_my_bool_instead() *incl_endp)
+ { assert(0); return 0; }
+ virtual double val_real()=0;
+ virtual longlong val_int()=0;
+ inline ulonglong val_uint() { return (ulonglong) val_int(); }
+ virtual String *val_str(String *str)=0;
+ virtual my_decimal *val_decimal(my_decimal *decimal_buffer)= 0;
+ virtual In_C_you_should_use_my_bool_instead() val_bool();
+ virtual String *val_nodeset(String*) { return 0; }
+ String *val_string_from_real(String *str);
+ String *val_string_from_int(String *str);
+ String *val_string_from_decimal(String *str);
+ my_decimal *val_decimal_from_real(my_decimal *decimal_value);
+ my_decimal *val_decimal_from_int(my_decimal *decimal_value);
+ my_decimal *val_decimal_from_string(my_decimal *decimal_value);
+ my_decimal *val_decimal_from_date(my_decimal *decimal_value);
+ my_decimal *val_decimal_from_time(my_decimal *decimal_value);
+ longlong val_int_from_decimal();
+ double val_real_from_decimal();
+ int save_time_in_field(Field *field);
+ int save_date_in_field(Field *field);
+ int save_str_value_in_field(Field *field, String *result);
+ virtual Field *get_tmp_table_field() { return 0; }
+ virtual Field *tmp_table_field(TABLE *t_arg) { return 0; }
+ virtual const char *full_name() const { return name ? name : "???"; }
+ virtual double val_result() { return val_real(); }
+ virtual longlong val_int_result() { return val_int(); }
+ virtual String *str_result(String* tmp) { return val_str(tmp); }
+ virtual my_decimal *val_decimal_result(my_decimal *val)
+ { return val_decimal(val); }
+ virtual In_C_you_should_use_my_bool_instead() val_bool_result() { return val_bool(); }
+ virtual table_map used_tables() const { return (table_map) 0L; }
+ virtual table_map not_null_tables() const { return used_tables(); }
+ virtual In_C_you_should_use_my_bool_instead() basic_const_item() const { return 0; }
+ virtual Item *clone_item() { return 0; }
+ virtual cond_result eq_cmp_result() const { return COND_OK; }
+ inline uint float_length(uint decimals_par) const
+ { return decimals != 31 ? (DBL_DIG+2+decimals_par) : DBL_DIG+8;}
+ virtual uint decimal_precision() const;
+ inline int decimal_int_part() const
+ { return my_decimal_int_part(decimal_precision(), decimals); }
+ virtual In_C_you_should_use_my_bool_instead() const_item() const { return used_tables() == 0; }
+ virtual In_C_you_should_use_my_bool_instead() const_during_execution() const
+ { return (used_tables() & ~(((table_map) 1) << (sizeof(table_map)*8-3))) == 0; }
+ virtual inline void print(String *str, enum_query_type query_type)
+ {
+ str->append(full_name());
+ }
+ void print_item_w_name(String *, enum_query_type query_type);
+ virtual void update_used_tables() {}
+ virtual void split_sum_func(THD *thd, Item **ref_pointer_array,
+ List<Item> &fields) {}
+ void split_sum_func2(THD *thd, Item **ref_pointer_array, List<Item> &fields,
+ Item **ref, In_C_you_should_use_my_bool_instead() skip_registered);
+ virtual In_C_you_should_use_my_bool_instead() get_date(MYSQL_TIME *ltime,uint fuzzydate);
+ virtual In_C_you_should_use_my_bool_instead() get_time(MYSQL_TIME *ltime);
+ virtual In_C_you_should_use_my_bool_instead() get_date_result(MYSQL_TIME *ltime,uint fuzzydate)
+ { return get_date(ltime,fuzzydate); }
+ virtual In_C_you_should_use_my_bool_instead() is_null() { return 0; }
+ virtual void update_null_value () { (void) val_int(); }
+ virtual void top_level_item() {}
+ virtual void set_result_field(Field *field) {}
+ virtual In_C_you_should_use_my_bool_instead() is_result_field() { return 0; }
+ virtual In_C_you_should_use_my_bool_instead() is_bool_func() { return 0; }
+ virtual void save_in_result_field(In_C_you_should_use_my_bool_instead() no_conversions) {}
+ virtual void no_rows_in_result() {}
+ virtual Item *copy_or_same(THD *thd) { return this; }
+ virtual Item *copy_andor_structure(THD *thd) { return this; }
+ virtual Item *real_item() { return this; }
+ virtual Item *get_tmp_table_item(THD *thd) { return copy_or_same(thd); }
+ static CHARSET_INFO *default_charset();
+ virtual CHARSET_INFO *compare_collation() { return NULL; }
+ virtual In_C_you_should_use_my_bool_instead() walk(Item_processor processor, In_C_you_should_use_my_bool_instead() walk_subquery, uchar *arg)
+ {
+ return (this->*processor)(arg);
+ }
+ virtual Item* transform(Item_transformer transformer, uchar *arg);
+ virtual Item* compile(Item_analyzer analyzer, uchar **arg_p,
+ Item_transformer transformer, uchar *arg_t)
+ {
+ if ((this->*analyzer) (arg_p))
+ return ((this->*transformer) (arg_t));
+ return 0;
+ }
+ virtual void traverse_cond(Cond_traverser traverser,
+ void *arg, traverse_order order)
+ {
+ (*traverser)(this, arg);
+ }
+ virtual In_C_you_should_use_my_bool_instead() remove_dependence_processor(uchar * arg) { return 0; }
+ virtual In_C_you_should_use_my_bool_instead() remove_fixed(uchar * arg) { fixed= 0; return 0; }
+ virtual In_C_you_should_use_my_bool_instead() cleanup_processor(uchar *arg);
+ virtual In_C_you_should_use_my_bool_instead() collect_item_field_processor(uchar * arg) { return 0; }
+ virtual In_C_you_should_use_my_bool_instead() find_item_in_field_list_processor(uchar *arg) { return 0; }
+ virtual In_C_you_should_use_my_bool_instead() change_context_processor(uchar *context) { return 0; }
+ virtual In_C_you_should_use_my_bool_instead() reset_query_id_processor(uchar *query_id_arg) { return 0; }
+ virtual In_C_you_should_use_my_bool_instead() is_expensive_processor(uchar *arg) { return 0; }
+ virtual In_C_you_should_use_my_bool_instead() register_field_in_read_map(uchar *arg) { return 0; }
+ virtual In_C_you_should_use_my_bool_instead() check_partition_func_processor(uchar *bool_arg) { return (1);}
+ virtual In_C_you_should_use_my_bool_instead() subst_argument_checker(uchar **arg)
+ {
+ if (*arg)
+ *arg= NULL;
+ return (1);
+ }
+ virtual Item *equal_fields_propagator(uchar * arg) { return this; }
+ virtual In_C_you_should_use_my_bool_instead() set_no_const_sub(uchar *arg) { return (0); }
+ virtual Item *replace_equal_field(uchar * arg) { return this; }
+ virtual Item *this_item() { return this; }
+ virtual const Item *this_item() const { return this; }
+ virtual Item **this_item_addr(THD *thd, Item **addr_arg) { return addr_arg; }
+ virtual uint cols() { return 1; }
+ virtual Item* element_index(uint i) { return this; }
+ virtual Item** addr(uint i) { return 0; }
+ virtual In_C_you_should_use_my_bool_instead() check_cols(uint c);
+ virtual In_C_you_should_use_my_bool_instead() null_inside() { return 0; }
+ virtual void bring_value() {}
+ Field *tmp_table_field_from_field_type(TABLE *table, In_C_you_should_use_my_bool_instead() fixed_length);
+ virtual Item_field *filed_for_view_update() { return 0; }
+ virtual Item *neg_transformer(THD *thd) { return NULL; }
+ virtual Item *update_value_transformer(uchar *select_arg) { return this; }
+ virtual Item *safe_charset_converter(CHARSET_INFO *tocs);
+ void delete_self()
+ {
+ cleanup();
+ delete this;
+ }
+ virtual In_C_you_should_use_my_bool_instead() is_splocal() { return 0; }
+ virtual Settable_routine_parameter *get_settable_routine_parameter()
+ {
+ return 0;
+ }
+ virtual In_C_you_should_use_my_bool_instead() result_as_longlong() { return (0); }
+ In_C_you_should_use_my_bool_instead() is_datetime();
+ virtual Field::geometry_type get_geometry_type() const
+ { return Field::GEOM_GEOMETRY; };
+ String *check_well_formed_result(String *str, In_C_you_should_use_my_bool_instead() send_error= 0);
+ In_C_you_should_use_my_bool_instead() eq_by_collation(Item *item, In_C_you_should_use_my_bool_instead() binary_cmp, CHARSET_INFO *cs);
+};
+class sp_head;
+class Item_basic_constant :public Item
+{
+public:
+ void cleanup()
+ {
+ if (orig_name)
+ name= orig_name;
+ }
+};
+class Item_sp_variable :public Item
+{
+protected:
+ THD *m_thd;
+public:
+ LEX_STRING m_name;
+public:
+ sp_head *m_sp;
+public:
+ Item_sp_variable(char *sp_var_name_str, uint sp_var_name_length);
+public:
+ In_C_you_should_use_my_bool_instead() fix_fields(THD *thd, Item **);
+ double val_real();
+ longlong val_int();
+ String *val_str(String *sp);
+ my_decimal *val_decimal(my_decimal *decimal_value);
+ In_C_you_should_use_my_bool_instead() is_null();
+public:
+ inline void make_field(Send_field *field);
+ inline In_C_you_should_use_my_bool_instead() const_item() const;
+ inline int save_in_field(Field *field, In_C_you_should_use_my_bool_instead() no_conversions);
+ inline In_C_you_should_use_my_bool_instead() send(Protocol *protocol, String *str);
+};
+inline void Item_sp_variable::make_field(Send_field *field)
+{
+ Item *it= this_item();
+ if (name)
+ it->set_name(name, (uint) strlen(name), system_charset_info);
+ else
+ it->set_name(m_name.str, m_name.length, system_charset_info);
+ it->make_field(field);
+}
+inline In_C_you_should_use_my_bool_instead() Item_sp_variable::const_item() const
+{
+ return (1);
+}
+inline int Item_sp_variable::save_in_field(Field *field, In_C_you_should_use_my_bool_instead() no_conversions)
+{
+ return this_item()->save_in_field(field, no_conversions);
+}
+inline In_C_you_should_use_my_bool_instead() Item_sp_variable::send(Protocol *protocol, String *str)
+{
+ return this_item()->send(protocol, str);
+}
+class Item_splocal :public Item_sp_variable,
+ private Settable_routine_parameter
+{
+ uint m_var_idx;
+ Type m_type;
+ Item_result m_result_type;
+ enum_field_types m_field_type;
+public:
+ uint pos_in_query;
+ uint len_in_query;
+ Item_splocal(const LEX_STRING &sp_var_name, uint sp_var_idx,
+ enum_field_types sp_var_type,
+ uint pos_in_q= 0, uint len_in_q= 0);
+ In_C_you_should_use_my_bool_instead() is_splocal() { return 1; }
+ Item *this_item();
+ const Item *this_item() const;
+ Item **this_item_addr(THD *thd, Item **);
+ virtual void print(String *str, enum_query_type query_type);
+public:
+ inline const LEX_STRING *my_name() const;
+ inline uint get_var_idx() const;
+ inline enum Type type() const;
+ inline Item_result result_type() const;
+ inline enum_field_types field_type() const { return m_field_type; }
+private:
+ In_C_you_should_use_my_bool_instead() set_value(THD *thd, sp_rcontext *ctx, Item **it);
+public:
+ Settable_routine_parameter *get_settable_routine_parameter()
+ {
+ return this;
+ }
+};
+inline const LEX_STRING *Item_splocal::my_name() const
+{
+ return &m_name;
+}
+inline uint Item_splocal::get_var_idx() const
+{
+ return m_var_idx;
+}
+inline enum Item::Type Item_splocal::type() const
+{
+ return m_type;
+}
+inline Item_result Item_splocal::result_type() const
+{
+ return m_result_type;
+}
+class Item_case_expr :public Item_sp_variable
+{
+public:
+ Item_case_expr(uint case_expr_id);
+public:
+ Item *this_item();
+ const Item *this_item() const;
+ Item **this_item_addr(THD *thd, Item **);
+ inline enum Type type() const;
+ inline Item_result result_type() const;
+public:
+ virtual void print(String *str, enum_query_type query_type);
+private:
+ uint m_case_expr_id;
+};
+inline enum Item::Type Item_case_expr::type() const
+{
+ return this_item()->type();
+}
+inline Item_result Item_case_expr::result_type() const
+{
+ return this_item()->result_type();
+}
+class Item_name_const : public Item
+{
+ Item *value_item;
+ Item *name_item;
+ In_C_you_should_use_my_bool_instead() valid_args;
+public:
+ Item_name_const(Item *name_arg, Item *val);
+ In_C_you_should_use_my_bool_instead() fix_fields(THD *, Item **);
+ enum Type type() const;
+ double val_real();
+ longlong val_int();
+ String *val_str(String *sp);
+ my_decimal *val_decimal(my_decimal *);
+ In_C_you_should_use_my_bool_instead() is_null();
+ virtual void print(String *str, enum_query_type query_type);
+ Item_result result_type() const
+ {
+ return value_item->result_type();
+ }
+ In_C_you_should_use_my_bool_instead() const_item() const
+ {
+ return (1);
+ }
+ int save_in_field(Field *field, In_C_you_should_use_my_bool_instead() no_conversions)
+ {
+ return value_item->save_in_field(field, no_conversions);
+ }
+ In_C_you_should_use_my_bool_instead() send(Protocol *protocol, String *str)
+ {
+ return value_item->send(protocol, str);
+ }
+};
+In_C_you_should_use_my_bool_instead() agg_item_collations(DTCollation &c, const char *name,
+ Item **items, uint nitems, uint flags, int item_sep);
+In_C_you_should_use_my_bool_instead() agg_item_collations_for_comparison(DTCollation &c, const char *name,
+ Item **items, uint nitems, uint flags);
+In_C_you_should_use_my_bool_instead() agg_item_charsets(DTCollation &c, const char *name,
+ Item **items, uint nitems, uint flags, int item_sep);
+class Item_num: public Item_basic_constant
+{
+public:
+ Item_num() {}
+ virtual Item_num *neg()= 0;
+ Item *safe_charset_converter(CHARSET_INFO *tocs);
+ In_C_you_should_use_my_bool_instead() check_partition_func_processor(uchar *int_arg) { return (0);}
+};
+class st_select_lex;
+class Item_ident :public Item
+{
+protected:
+ const char *orig_db_name;
+ const char *orig_table_name;
+ const char *orig_field_name;
+public:
+ Name_resolution_context *context;
+ const char *db_name;
+ const char *table_name;
+ const char *field_name;
+ In_C_you_should_use_my_bool_instead() alias_name_used;
+ uint cached_field_index;
+ TABLE_LIST *cached_table;
+ st_select_lex *depended_from;
+ Item_ident(Name_resolution_context *context_arg,
+ const char *db_name_arg, const char *table_name_arg,
+ const char *field_name_arg);
+ Item_ident(THD *thd, Item_ident *item);
+ const char *full_name() const;
+ void cleanup();
+ In_C_you_should_use_my_bool_instead() remove_dependence_processor(uchar * arg);
+ virtual void print(String *str, enum_query_type query_type);
+ virtual In_C_you_should_use_my_bool_instead() change_context_processor(uchar *cntx)
+ { context= (Name_resolution_context *)cntx; return (0); }
+ friend In_C_you_should_use_my_bool_instead() insert_fields(THD *thd, Name_resolution_context *context,
+ const char *db_name,
+ const char *table_name, List_iterator<Item> *it,
+ In_C_you_should_use_my_bool_instead() any_privileges);
+};
+class Item_ident_for_show :public Item
+{
+public:
+ Field *field;
+ const char *db_name;
+ const char *table_name;
+ Item_ident_for_show(Field *par_field, const char *db_arg,
+ const char *table_name_arg)
+ :field(par_field), db_name(db_arg), table_name(table_name_arg)
+ {}
+ enum Type type() const { return FIELD_ITEM; }
+ double val_real() { return field->val_real(); }
+ longlong val_int() { return field->val_int(); }
+ String *val_str(String *str) { return field->val_str(str); }
+ my_decimal *val_decimal(my_decimal *dec) { return field->val_decimal(dec); }
+ void make_field(Send_field *tmp_field);
+};
+class Item_equal;
+class COND_EQUAL;
+class Item_field :public Item_ident
+{
+protected:
+ void set_field(Field *field);
+public:
+ Field *field,*result_field;
+ Item_equal *item_equal;
+ In_C_you_should_use_my_bool_instead() no_const_subst;
+ uint have_privileges;
+ In_C_you_should_use_my_bool_instead() any_privileges;
+ Item_field(Name_resolution_context *context_arg,
+ const char *db_arg,const char *table_name_arg,
+ const char *field_name_arg);
+ Item_field(THD *thd, Item_field *item);
+ Item_field(THD *thd, Name_resolution_context *context_arg, Field *field);
+ Item_field(Field *field);
+ enum Type type() const { return FIELD_ITEM; }
+ In_C_you_should_use_my_bool_instead() eq(const Item *item, In_C_you_should_use_my_bool_instead() binary_cmp) const;
+ double val_real();
+ longlong val_int();
+ my_decimal *val_decimal(my_decimal *);
+ String *val_str(String*);
+ double val_result();
+ longlong val_int_result();
+ String *str_result(String* tmp);
+ my_decimal *val_decimal_result(my_decimal *);
+ In_C_you_should_use_my_bool_instead() val_bool_result();
+ In_C_you_should_use_my_bool_instead() send(Protocol *protocol, String *str_arg);
+ void reset_field(Field *f);
+ In_C_you_should_use_my_bool_instead() fix_fields(THD *, Item **);
+ void make_field(Send_field *tmp_field);
+ int save_in_field(Field *field,In_C_you_should_use_my_bool_instead() no_conversions);
+ void save_org_in_field(Field *field);
+ table_map used_tables() const;
+ enum Item_result result_type () const
+ {
+ return field->result_type();
+ }
+ Item_result cast_to_int_type() const
+ {
+ return field->cast_to_int_type();
+ }
+ enum_field_types field_type() const
+ {
+ return field->type();
+ }
+ enum_monotonicity_info get_monotonicity_info() const
+ {
+ return MONOTONIC_STRICT_INCREASING;
+ }
+ longlong val_int_endpoint(In_C_you_should_use_my_bool_instead() left_endp, In_C_you_should_use_my_bool_instead() *incl_endp);
+ Field *get_tmp_table_field() { return result_field; }
+ Field *tmp_table_field(TABLE *t_arg) { return result_field; }
+ In_C_you_should_use_my_bool_instead() get_date(MYSQL_TIME *ltime,uint fuzzydate);
+ In_C_you_should_use_my_bool_instead() get_date_result(MYSQL_TIME *ltime,uint fuzzydate);
+ In_C_you_should_use_my_bool_instead() get_time(MYSQL_TIME *ltime);
+ In_C_you_should_use_my_bool_instead() is_null() { return field->is_null(); }
+ void update_null_value();
+ Item *get_tmp_table_item(THD *thd);
+ In_C_you_should_use_my_bool_instead() collect_item_field_processor(uchar * arg);
+ In_C_you_should_use_my_bool_instead() find_item_in_field_list_processor(uchar *arg);
+ In_C_you_should_use_my_bool_instead() register_field_in_read_map(uchar *arg);
+ In_C_you_should_use_my_bool_instead() check_partition_func_processor(uchar *int_arg) {return (0);}
+ void cleanup();
+ In_C_you_should_use_my_bool_instead() result_as_longlong()
+ {
+ return field->can_be_compared_as_longlong();
+ }
+ Item_equal *find_item_equal(COND_EQUAL *cond_equal);
+ In_C_you_should_use_my_bool_instead() subst_argument_checker(uchar **arg);
+ Item *equal_fields_propagator(uchar *arg);
+ In_C_you_should_use_my_bool_instead() set_no_const_sub(uchar *arg);
+ Item *replace_equal_field(uchar *arg);
+ inline uint32 max_disp_length() { return field->max_display_length(); }
+ Item_field *filed_for_view_update() { return this; }
+ Item *safe_charset_converter(CHARSET_INFO *tocs);
+ 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);
+ Field::geometry_type get_geometry_type() const
+ {
+ assert(field_type() == MYSQL_TYPE_GEOMETRY);
+ return field->get_geometry_type();
+ }
+ friend class Item_default_value;
+ friend class Item_insert_value;
+ friend class st_select_lex_unit;
+};
+class Item_null :public Item_basic_constant
+{
+public:
+ Item_null(char *name_par=0)
+ {
+ maybe_null= null_value= (1);
+ max_length= 0;
+ name= name_par ? name_par : (char*) "NULL";
+ fixed= 1;
+ collation.set(&my_charset_bin, DERIVATION_IGNORABLE);
+ }
+ enum Type type() const { return NULL_ITEM; }
+ In_C_you_should_use_my_bool_instead() eq(const Item *item, In_C_you_should_use_my_bool_instead() binary_cmp) const;
+ double val_real();
+ longlong val_int();
+ String *val_str(String *str);
+ my_decimal *val_decimal(my_decimal *);
+ int save_in_field(Field *field, In_C_you_should_use_my_bool_instead() no_conversions);
+ int save_safe_in_field(Field *field);
+ In_C_you_should_use_my_bool_instead() send(Protocol *protocol, String *str);
+ enum Item_result result_type () const { return STRING_RESULT; }
+ enum_field_types field_type() const { return MYSQL_TYPE_NULL; }
+ In_C_you_should_use_my_bool_instead() basic_const_item() const { return 1; }
+ Item *clone_item() { return new Item_null(name); }
+ In_C_you_should_use_my_bool_instead() is_null() { return 1; }
+ virtual inline void print(String *str, enum_query_type query_type)
+ {
+ str->append(("NULL"), ((size_t) (sizeof("NULL") - 1)));
+ }
+ Item *safe_charset_converter(CHARSET_INFO *tocs);
+ In_C_you_should_use_my_bool_instead() check_partition_func_processor(uchar *int_arg) {return (0);}
+};
+class Item_null_result :public Item_null
+{
+public:
+ Field *result_field;
+ Item_null_result() : Item_null(), result_field(0) {}
+ In_C_you_should_use_my_bool_instead() is_result_field() { return result_field != 0; }
+ void save_in_result_field(In_C_you_should_use_my_bool_instead() no_conversions)
+ {
+ save_in_field(result_field, no_conversions);
+ }
+ In_C_you_should_use_my_bool_instead() check_partition_func_processor(uchar *int_arg) {return (1);}
+};
+class Item_param :public Item
+{
+ char cnvbuf[(255*3 +1)];
+ String cnvstr;
+ Item *cnvitem;
+public:
+ enum enum_item_param_state
+ {
+ NO_VALUE, NULL_VALUE, INT_VALUE, REAL_VALUE,
+ STRING_VALUE, TIME_VALUE, LONG_DATA_VALUE,
+ DECIMAL_VALUE
+ } state;
+ String str_value_ptr;
+ my_decimal decimal_value;
+ union
+ {
+ longlong integer;
+ double real;
+ struct CONVERSION_INFO
+ {
+ CHARSET_INFO *character_set_client;
+ CHARSET_INFO *character_set_of_placeholder;
+ CHARSET_INFO *final_character_set_of_str_value;
+ } cs_info;
+ MYSQL_TIME time;
+ } value;
+ enum Item_result item_result_type;
+ enum Type item_type;
+ enum enum_field_types param_type;
+ uint pos_in_query;
+ Item_param(uint pos_in_query_arg);
+ enum Item_result result_type () const { return item_result_type; }
+ enum Type type() const { return item_type; }
+ enum_field_types field_type() const { return param_type; }
+ double val_real();
+ longlong val_int();
+ my_decimal *val_decimal(my_decimal*);
+ String *val_str(String*);
+ In_C_you_should_use_my_bool_instead() get_time(MYSQL_TIME *tm);
+ In_C_you_should_use_my_bool_instead() get_date(MYSQL_TIME *tm, uint fuzzydate);
+ int save_in_field(Field *field, In_C_you_should_use_my_bool_instead() 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);
+ In_C_you_should_use_my_bool_instead() set_str(const char *str, ulong length);
+ In_C_you_should_use_my_bool_instead() set_longdata(const char *str, ulong length);
+ void set_time(MYSQL_TIME *tm, timestamp_type type, uint32 max_length_arg);
+ In_C_you_should_use_my_bool_instead() set_from_user_var(THD *thd, const user_var_entry *entry);
+ void reset();
+ void (*set_param_func)(Item_param *param, uchar **pos, ulong len);
+ const String *query_val_str(String *str) const;
+ In_C_you_should_use_my_bool_instead() convert_str_value(THD *thd);
+ virtual table_map used_tables() const
+ { return state != NO_VALUE ? (table_map)0 : (((table_map) 1) << (sizeof(table_map)*8-3)); }
+ virtual void print(String *str, enum_query_type query_type);
+ In_C_you_should_use_my_bool_instead() is_null()
+ { assert(state != NO_VALUE); return state == NULL_VALUE; }
+ In_C_you_should_use_my_bool_instead() basic_const_item() const;
+ Item *safe_charset_converter(CHARSET_INFO *tocs);
+ Item *clone_item();
+ In_C_you_should_use_my_bool_instead() eq(const Item *item, In_C_you_should_use_my_bool_instead() binary_cmp) const;
+ In_C_you_should_use_my_bool_instead() limit_clause_param;
+ void set_param_type_and_swap_value(Item_param *from);
+};
+class Item_int :public Item_num
+{
+public:
+ longlong value;
+ Item_int(int32 i,uint length= 11)
+ :value((longlong) i)
+ { max_length=length; fixed= 1; }
+ Item_int(longlong i,uint length= 21)
+ :value(i)
+ { max_length=length; fixed= 1; }
+ Item_int(ulonglong i, uint length= 21)
+ :value((longlong)i)
+ { max_length=length; fixed= 1; unsigned_flag= 1; }
+ Item_int(const char *str_arg,longlong i,uint length) :value(i)
+ { max_length=length; name=(char*) str_arg; fixed= 1; }
+ Item_int(const char *str_arg, uint length=64);
+ enum Type type() const { return INT_ITEM; }
+ enum Item_result result_type () const { return INT_RESULT; }
+ enum_field_types field_type() const { return MYSQL_TYPE_LONGLONG; }
+ longlong val_int() { assert(fixed == 1); return value; }
+ double val_real() { assert(fixed == 1); return (double) value; }
+ my_decimal *val_decimal(my_decimal *);
+ String *val_str(String*);
+ int save_in_field(Field *field, In_C_you_should_use_my_bool_instead() no_conversions);
+ In_C_you_should_use_my_bool_instead() basic_const_item() const { return 1; }
+ Item *clone_item() { return new Item_int(name,value,max_length); }
+ virtual void print(String *str, enum_query_type query_type);
+ Item_num *neg() { value= -value; return this; }
+ uint decimal_precision() const
+ { return (uint)(max_length - ((value < 0) ? 1 : 0)); }
+ In_C_you_should_use_my_bool_instead() eq(const Item *, In_C_you_should_use_my_bool_instead() binary_cmp) const;
+ In_C_you_should_use_my_bool_instead() check_partition_func_processor(uchar *bool_arg) { return (0);}
+};
+class Item_uint :public Item_int
+{
+public:
+ Item_uint(const char *str_arg, uint length);
+ Item_uint(ulonglong i) :Item_int((ulonglong) i, 10) {}
+ Item_uint(const char *str_arg, longlong i, uint length);
+ double val_real()
+ { assert(fixed == 1); return ((double) (ulonglong) ((ulonglong)value)); }
+ String *val_str(String*);
+ Item *clone_item() { return new Item_uint(name, value, max_length); }
+ int save_in_field(Field *field, In_C_you_should_use_my_bool_instead() no_conversions);
+ virtual void print(String *str, enum_query_type query_type);
+ Item_num *neg ();
+ uint decimal_precision() const { return max_length; }
+ In_C_you_should_use_my_bool_instead() check_partition_func_processor(uchar *bool_arg) { return (0);}
+};
+class Item_decimal :public Item_num
+{
+protected:
+ my_decimal decimal_value;
+public:
+ Item_decimal(const char *str_arg, uint length, CHARSET_INFO *charset);
+ Item_decimal(const char *str, const my_decimal *val_arg,
+ uint decimal_par, uint length);
+ Item_decimal(my_decimal *value_par);
+ Item_decimal(longlong val, In_C_you_should_use_my_bool_instead() unsig);
+ Item_decimal(double val, int precision, int scale);
+ Item_decimal(const uchar *bin, int precision, int scale);
+ enum Type type() const { return DECIMAL_ITEM; }
+ enum Item_result result_type () const { return DECIMAL_RESULT; }
+ enum_field_types field_type() const { return MYSQL_TYPE_NEWDECIMAL; }
+ longlong val_int();
+ double val_real();
+ String *val_str(String*);
+ my_decimal *val_decimal(my_decimal *val) { return &decimal_value; }
+ int save_in_field(Field *field, In_C_you_should_use_my_bool_instead() no_conversions);
+ In_C_you_should_use_my_bool_instead() basic_const_item() const { return 1; }
+ Item *clone_item()
+ {
+ return new Item_decimal(name, &decimal_value, decimals, max_length);
+ }
+ virtual void print(String *str, enum_query_type query_type);
+ Item_num *neg()
+ {
+ my_decimal_neg(&decimal_value);
+ unsigned_flag= !decimal_value.sign();
+ return this;
+ }
+ uint decimal_precision() const { return decimal_value.precision(); }
+ In_C_you_should_use_my_bool_instead() eq(const Item *, In_C_you_should_use_my_bool_instead() binary_cmp) const;
+ void set_decimal_value(my_decimal *value_par);
+ In_C_you_should_use_my_bool_instead() check_partition_func_processor(uchar *bool_arg) { return (0);}
+};
+class Item_float :public Item_num
+{
+ char *presentation;
+public:
+ double value;
+ Item_float(const char *str_arg, uint length);
+ Item_float(const char *str,double val_arg,uint decimal_par,uint length)
+ :value(val_arg)
+ {
+ presentation= name=(char*) str;
+ decimals=(uint8) decimal_par;
+ max_length=length;
+ fixed= 1;
+ }
+ Item_float(double value_par, uint decimal_par) :presentation(0), value(value_par)
+ {
+ decimals= (uint8) decimal_par;
+ fixed= 1;
+ }
+ int save_in_field(Field *field, In_C_you_should_use_my_bool_instead() no_conversions);
+ enum Type type() const { return REAL_ITEM; }
+ enum_field_types field_type() const { return MYSQL_TYPE_DOUBLE; }
+ double val_real() { assert(fixed == 1); return value; }
+ longlong val_int()
+ {
+ assert(fixed == 1);
+ if (value <= (double) ((long long) 0x8000000000000000LL))
+ {
+ return ((long long) 0x8000000000000000LL);
+ }
+ else if (value >= (double) (ulonglong) ((long long) 0x7FFFFFFFFFFFFFFFLL))
+ {
+ return ((long long) 0x7FFFFFFFFFFFFFFFLL);
+ }
+ return (longlong) rint(value);
+ }
+ String *val_str(String*);
+ my_decimal *val_decimal(my_decimal *);
+ In_C_you_should_use_my_bool_instead() basic_const_item() const { return 1; }
+ Item *clone_item()
+ { return new Item_float(name, value, decimals, max_length); }
+ Item_num *neg() { value= -value; return this; }
+ virtual void print(String *str, enum_query_type query_type);
+ In_C_you_should_use_my_bool_instead() eq(const Item *, In_C_you_should_use_my_bool_instead() binary_cmp) const;
+};
+class Item_static_float_func :public Item_float
+{
+ const char *func_name;
+public:
+ Item_static_float_func(const char *str, double val_arg, uint decimal_par,
+ uint length)
+ :Item_float((char *) 0, val_arg, decimal_par, length), func_name(str)
+ {}
+ virtual inline void print(String *str, enum_query_type query_type)
+ {
+ str->append(func_name);
+ }
+ Item *safe_charset_converter(CHARSET_INFO *tocs);
+};
+class Item_string :public Item_basic_constant
+{
+public:
+ Item_string(const char *str,uint length,
+ CHARSET_INFO *cs, Derivation dv= DERIVATION_COERCIBLE,
+ uint repertoire= 3)
+ : m_cs_specified((0))
+ {
+ str_value.set_or_copy_aligned(str, length, cs);
+ collation.set(cs, dv, repertoire);
+ max_length= str_value.numchars()*cs->mbmaxlen;
+ set_name(str, length, cs);
+ decimals=31;
+ fixed= 1;
+ }
+ Item_string(CHARSET_INFO *cs, Derivation dv= DERIVATION_COERCIBLE)
+ : m_cs_specified((0))
+ {
+ collation.set(cs, dv);
+ max_length= 0;
+ set_name(NULL, 0, cs);
+ decimals= 31;
+ fixed= 1;
+ }
+ Item_string(const char *name_par, const char *str, uint length,
+ CHARSET_INFO *cs, Derivation dv= DERIVATION_COERCIBLE,
+ uint repertoire= 3)
+ : m_cs_specified((0))
+ {
+ str_value.set_or_copy_aligned(str, length, cs);
+ collation.set(cs, dv, repertoire);
+ max_length= str_value.numchars()*cs->mbmaxlen;
+ set_name(name_par, 0, cs);
+ decimals=31;
+ fixed= 1;
+ }
+ void set_str_with_copy(const char *str_arg, uint length_arg)
+ {
+ str_value.copy(str_arg, length_arg, collation.collation);
+ max_length= str_value.numchars() * collation.collation->mbmaxlen;
+ }
+ void set_repertoire_from_value()
+ {
+ collation.repertoire= my_string_repertoire(str_value.charset(),
+ str_value.ptr(),
+ str_value.length());
+ }
+ enum Type type() const { return STRING_ITEM; }
+ double val_real();
+ longlong val_int();
+ String *val_str(String*)
+ {
+ assert(fixed == 1);
+ return (String*) &str_value;
+ }
+ my_decimal *val_decimal(my_decimal *);
+ int save_in_field(Field *field, In_C_you_should_use_my_bool_instead() no_conversions);
+ enum Item_result result_type () const { return STRING_RESULT; }
+ enum_field_types field_type() const { return MYSQL_TYPE_VARCHAR; }
+ In_C_you_should_use_my_bool_instead() basic_const_item() const { return 1; }
+ In_C_you_should_use_my_bool_instead() eq(const Item *item, In_C_you_should_use_my_bool_instead() binary_cmp) const;
+ Item *clone_item()
+ {
+ return new Item_string(name, str_value.ptr(),
+ str_value.length(), collation.collation);
+ }
+ Item *safe_charset_converter(CHARSET_INFO *tocs);
+ inline void append(char *str, uint length)
+ {
+ str_value.append(str, length);
+ max_length= str_value.numchars() * collation.collation->mbmaxlen;
+ }
+ virtual void print(String *str, enum_query_type query_type);
+ In_C_you_should_use_my_bool_instead() check_partition_func_processor(uchar *int_arg) {return (0);}
+ inline In_C_you_should_use_my_bool_instead() is_cs_specified() const
+ {
+ return m_cs_specified;
+ }
+ inline void set_cs_specified(In_C_you_should_use_my_bool_instead() cs_specified)
+ {
+ m_cs_specified= cs_specified;
+ }
+private:
+ In_C_you_should_use_my_bool_instead() m_cs_specified;
+};
+class Item_static_string_func :public Item_string
+{
+ const char *func_name;
+public:
+ Item_static_string_func(const char *name_par, const char *str, uint length,
+ CHARSET_INFO *cs,
+ Derivation dv= DERIVATION_COERCIBLE)
+ :Item_string((char *) 0, str, length, cs, dv), func_name(name_par)
+ {}
+ Item *safe_charset_converter(CHARSET_INFO *tocs);
+ virtual inline void print(String *str, enum_query_type query_type)
+ {
+ str->append(func_name);
+ }
+ In_C_you_should_use_my_bool_instead() check_partition_func_processor(uchar *int_arg) {return (1);}
+};
+class Item_partition_func_safe_string: public Item_string
+{
+public:
+ Item_partition_func_safe_string(const char *name, uint length,
+ CHARSET_INFO *cs= NULL):
+ Item_string(name, length, cs)
+ {}
+};
+class Item_return_date_time :public Item_partition_func_safe_string
+{
+ enum_field_types date_time_field_type;
+public:
+ Item_return_date_time(const char *name_arg, enum_field_types field_type_arg)
+ :Item_partition_func_safe_string(name_arg, 0, &my_charset_bin),
+ date_time_field_type(field_type_arg)
+ { }
+ enum_field_types field_type() const { return date_time_field_type; }
+};
+class Item_blob :public Item_partition_func_safe_string
+{
+public:
+ Item_blob(const char *name, uint length) :
+ Item_partition_func_safe_string(name, length, &my_charset_bin)
+ { max_length= length; }
+ enum Type type() const { return TYPE_HOLDER; }
+ enum_field_types field_type() const { return MYSQL_TYPE_BLOB; }
+};
+class Item_empty_string :public Item_partition_func_safe_string
+{
+public:
+ Item_empty_string(const char *header,uint length, CHARSET_INFO *cs= NULL) :
+ Item_partition_func_safe_string("",0, cs ? cs : &my_charset_utf8_general_ci)
+ { name=(char*) header; max_length= cs ? length * cs->mbmaxlen : length; }
+ void make_field(Send_field *field);
+};
+class Item_return_int :public Item_int
+{
+ enum_field_types int_field_type;
+public:
+ Item_return_int(const char *name_arg, uint length,
+ enum_field_types field_type_arg, longlong value= 0)
+ :Item_int(name_arg, value, length), int_field_type(field_type_arg)
+ {
+ unsigned_flag=1;
+ }
+ enum_field_types field_type() const { return int_field_type; }
+};
+class Item_hex_string: public Item_basic_constant
+{
+public:
+ Item_hex_string() {}
+ Item_hex_string(const char *str,uint str_length);
+ enum Type type() const { return VARBIN_ITEM; }
+ double val_real()
+ {
+ assert(fixed == 1);
+ return (double) (ulonglong) Item_hex_string::val_int();
+ }
+ longlong val_int();
+ In_C_you_should_use_my_bool_instead() basic_const_item() const { return 1; }
+ String *val_str(String*) { assert(fixed == 1); return &str_value; }
+ my_decimal *val_decimal(my_decimal *);
+ int save_in_field(Field *field, In_C_you_should_use_my_bool_instead() 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);
+ In_C_you_should_use_my_bool_instead() eq(const Item *item, In_C_you_should_use_my_bool_instead() binary_cmp) const;
+ virtual Item *safe_charset_converter(CHARSET_INFO *tocs);
+ In_C_you_should_use_my_bool_instead() check_partition_func_processor(uchar *int_arg) {return (0);}
+};
+class Item_bin_string: public Item_hex_string
+{
+public:
+ Item_bin_string(const char *str,uint str_length);
+};
+class Item_result_field :public Item
+{
+public:
+ Field *result_field;
+ Item_result_field() :result_field(0) {}
+ Item_result_field(THD *thd, Item_result_field *item):
+ Item(thd, item), result_field(item->result_field)
+ {}
+ ~Item_result_field() {}
+ Field *get_tmp_table_field() { return result_field; }
+ Field *tmp_table_field(TABLE *t_arg) { return result_field; }
+ table_map used_tables() const { return 1; }
+ virtual void fix_length_and_dec()=0;
+ void set_result_field(Field *field) { result_field= field; }
+ In_C_you_should_use_my_bool_instead() is_result_field() { return 1; }
+ void save_in_result_field(In_C_you_should_use_my_bool_instead() no_conversions)
+ {
+ save_in_field(result_field, no_conversions);
+ }
+ void cleanup();
+};
+class Item_ref :public Item_ident
+{
+protected:
+ void set_properties();
+public:
+ enum Ref_Type { REF, DIRECT_REF, VIEW_REF, OUTER_REF };
+ Field *result_field;
+ Item **ref;
+ Item_ref(Name_resolution_context *context_arg,
+ const char *db_arg, const char *table_name_arg,
+ const char *field_name_arg)
+ :Item_ident(context_arg, db_arg, table_name_arg, field_name_arg),
+ result_field(0), ref(0) {}
+ Item_ref(Name_resolution_context *context_arg, Item **item,
+ const char *table_name_arg, const char *field_name_arg,
+ In_C_you_should_use_my_bool_instead() alias_name_used_arg= (0));
+ Item_ref(THD *thd, Item_ref *item)
+ :Item_ident(thd, item), result_field(item->result_field), ref(item->ref) {}
+ enum Type type() const { return REF_ITEM; }
+ In_C_you_should_use_my_bool_instead() eq(const Item *item, In_C_you_should_use_my_bool_instead() binary_cmp) const
+ {
+ Item *it= ((Item *) item)->real_item();
+ return ref && (*ref)->eq(it, binary_cmp);
+ }
+ double val_real();
+ longlong val_int();
+ my_decimal *val_decimal(my_decimal *);
+ In_C_you_should_use_my_bool_instead() val_bool();
+ String *val_str(String* tmp);
+ In_C_you_should_use_my_bool_instead() is_null();
+ In_C_you_should_use_my_bool_instead() get_date(MYSQL_TIME *ltime,uint fuzzydate);
+ double val_result();
+ longlong val_int_result();
+ String *str_result(String* tmp);
+ my_decimal *val_decimal_result(my_decimal *);
+ In_C_you_should_use_my_bool_instead() val_bool_result();
+ In_C_you_should_use_my_bool_instead() send(Protocol *prot, String *tmp);
+ void make_field(Send_field *field);
+ In_C_you_should_use_my_bool_instead() fix_fields(THD *, Item **);
+ int save_in_field(Field *field, In_C_you_should_use_my_bool_instead() no_conversions);
+ void save_org_in_field(Field *field);
+ enum Item_result result_type () const { return (*ref)->result_type(); }
+ enum_field_types field_type() const { return (*ref)->field_type(); }
+ Field *get_tmp_table_field()
+ { return result_field ? result_field : (*ref)->get_tmp_table_field(); }
+ Item *get_tmp_table_item(THD *thd);
+ table_map used_tables() const
+ {
+ return depended_from ? (((table_map) 1) << (sizeof(table_map)*8-2)) : (*ref)->used_tables();
+ }
+ void update_used_tables()
+ {
+ if (!depended_from)
+ (*ref)->update_used_tables();
+ }
+ table_map not_null_tables() const { return (*ref)->not_null_tables(); }
+ void set_result_field(Field *field) { result_field= field; }
+ In_C_you_should_use_my_bool_instead() is_result_field() { return 1; }
+ void save_in_result_field(In_C_you_should_use_my_bool_instead() no_conversions)
+ {
+ (*ref)->save_in_field(result_field, no_conversions);
+ }
+ Item *real_item()
+ {
+ return ref ? (*ref)->real_item() : this;
+ }
+ In_C_you_should_use_my_bool_instead() walk(Item_processor processor, In_C_you_should_use_my_bool_instead() walk_subquery, uchar *arg)
+ { return (*ref)->walk(processor, walk_subquery, arg); }
+ virtual void print(String *str, enum_query_type query_type);
+ In_C_you_should_use_my_bool_instead() result_as_longlong()
+ {
+ return (*ref)->result_as_longlong();
+ }
+ void cleanup();
+ Item_field *filed_for_view_update()
+ { return (*ref)->filed_for_view_update(); }
+ virtual Ref_Type ref_type() { return REF; }
+ uint cols()
+ {
+ return ref && result_type() == ROW_RESULT ? (*ref)->cols() : 1;
+ }
+ Item* element_index(uint i)
+ {
+ return ref && result_type() == ROW_RESULT ? (*ref)->element_index(i) : this;
+ }
+ Item** addr(uint i)
+ {
+ return ref && result_type() == ROW_RESULT ? (*ref)->addr(i) : 0;
+ }
+ In_C_you_should_use_my_bool_instead() check_cols(uint c)
+ {
+ return ref && result_type() == ROW_RESULT ? (*ref)->check_cols(c)
+ : Item::check_cols(c);
+ }
+ In_C_you_should_use_my_bool_instead() null_inside()
+ {
+ return ref && result_type() == ROW_RESULT ? (*ref)->null_inside() : 0;
+ }
+ void bring_value()
+ {
+ if (ref && result_type() == ROW_RESULT)
+ (*ref)->bring_value();
+ }
+};
+class Item_direct_ref :public Item_ref
+{
+public:
+ Item_direct_ref(Name_resolution_context *context_arg, Item **item,
+ const char *table_name_arg,
+ const char *field_name_arg,
+ In_C_you_should_use_my_bool_instead() alias_name_used_arg= (0))
+ :Item_ref(context_arg, item, table_name_arg,
+ field_name_arg, alias_name_used_arg)
+ {}
+ Item_direct_ref(THD *thd, Item_direct_ref *item) : Item_ref(thd, item) {}
+ double val_real();
+ longlong val_int();
+ String *val_str(String* tmp);
+ my_decimal *val_decimal(my_decimal *);
+ In_C_you_should_use_my_bool_instead() val_bool();
+ In_C_you_should_use_my_bool_instead() is_null();
+ In_C_you_should_use_my_bool_instead() get_date(MYSQL_TIME *ltime,uint fuzzydate);
+ virtual Ref_Type ref_type() { return DIRECT_REF; }
+};
+class Item_direct_view_ref :public Item_direct_ref
+{
+public:
+ Item_direct_view_ref(Name_resolution_context *context_arg, Item **item,
+ const char *table_name_arg,
+ const char *field_name_arg)
+ :Item_direct_ref(context_arg, item, table_name_arg, field_name_arg) {}
+ Item_direct_view_ref(THD *thd, Item_direct_ref *item)
+ :Item_direct_ref(thd, item) {}
+ In_C_you_should_use_my_bool_instead() fix_fields(THD *, Item **);
+ In_C_you_should_use_my_bool_instead() eq(const Item *item, In_C_you_should_use_my_bool_instead() binary_cmp) const;
+ Item *get_tmp_table_item(THD *thd)
+ {
+ Item *item= Item_ref::get_tmp_table_item(thd);
+ item->name= name;
+ return item;
+ }
+ virtual Ref_Type ref_type() { return VIEW_REF; }
+};
+class Item_sum;
+class Item_outer_ref :public Item_direct_ref
+{
+public:
+ Item *outer_ref;
+ Item_sum *in_sum_func;
+ In_C_you_should_use_my_bool_instead() found_in_select_list;
+ Item_outer_ref(Name_resolution_context *context_arg,
+ Item_field *outer_field_arg)
+ :Item_direct_ref(context_arg, 0, outer_field_arg->table_name,
+ outer_field_arg->field_name),
+ outer_ref(outer_field_arg), in_sum_func(0),
+ found_in_select_list(0)
+ {
+ ref= &outer_ref;
+ set_properties();
+ fixed= 0;
+ }
+ Item_outer_ref(Name_resolution_context *context_arg, Item **item,
+ const char *table_name_arg, const char *field_name_arg,
+ In_C_you_should_use_my_bool_instead() alias_name_used_arg)
+ :Item_direct_ref(context_arg, item, table_name_arg, field_name_arg,
+ alias_name_used_arg),
+ outer_ref(0), in_sum_func(0), found_in_select_list(1)
+ {}
+ void save_in_result_field(In_C_you_should_use_my_bool_instead() no_conversions)
+ {
+ outer_ref->save_org_in_field(result_field);
+ }
+ In_C_you_should_use_my_bool_instead() fix_fields(THD *, Item **);
+ table_map used_tables() const
+ {
+ return (*ref)->const_item() ? 0 : (((table_map) 1) << (sizeof(table_map)*8-2));
+ }
+ virtual Ref_Type ref_type() { return OUTER_REF; }
+};
+class Item_in_subselect;
+class Item_ref_null_helper: public Item_ref
+{
+protected:
+ Item_in_subselect* owner;
+public:
+ Item_ref_null_helper(Name_resolution_context *context_arg,
+ Item_in_subselect* master, Item **item,
+ const char *table_name_arg, const char *field_name_arg)
+ :Item_ref(context_arg, item, table_name_arg, field_name_arg),
+ owner(master) {}
+ double val_real();
+ longlong val_int();
+ String* val_str(String* s);
+ my_decimal *val_decimal(my_decimal *);
+ In_C_you_should_use_my_bool_instead() val_bool();
+ In_C_you_should_use_my_bool_instead() get_date(MYSQL_TIME *ltime, uint fuzzydate);
+ virtual void print(String *str, enum_query_type query_type);
+ table_map used_tables() const
+ {
+ return (depended_from ?
+ (((table_map) 1) << (sizeof(table_map)*8-2)) :
+ (*ref)->used_tables() | (((table_map) 1) << (sizeof(table_map)*8-1)));
+ }
+};
+class Item_int_with_ref :public Item_int
+{
+ Item *ref;
+public:
+ Item_int_with_ref(longlong i, Item *ref_arg, my_bool unsigned_arg) :
+ Item_int(i), ref(ref_arg)
+ {
+ unsigned_flag= unsigned_arg;
+ }
+ int save_in_field(Field *field, In_C_you_should_use_my_bool_instead() no_conversions)
+ {
+ return ref->save_in_field(field, no_conversions);
+ }
+ Item *clone_item();
+ virtual Item *real_item() { return ref; }
+};
+class Item_copy_string :public Item
+{
+ enum enum_field_types cached_field_type;
+public:
+ Item *item;
+ Item_copy_string(Item *i) :item(i)
+ {
+ null_value=maybe_null=item->maybe_null;
+ decimals=item->decimals;
+ max_length=item->max_length;
+ name=item->name;
+ cached_field_type= item->field_type();
+ }
+ enum Type type() const { return COPY_STR_ITEM; }
+ enum Item_result result_type () const { return STRING_RESULT; }
+ enum_field_types field_type() const { return cached_field_type; }
+ double val_real()
+ {
+ int err_not_used;
+ char *end_not_used;
+ return (null_value ? 0.0 :
+ ((str_value.charset())->cset->strntod((str_value.charset()),((char*) str_value.ptr()),(str_value.length()),(&end_not_used),(&err_not_used))));
+ }
+ longlong val_int()
+ {
+ int err;
+ return null_value ? 0LL : ((str_value.charset())->cset->strntoll((str_value.charset()),(str_value.ptr()),(str_value.length()),(10),((char**) 0),(&err)));
+ }
+ String *val_str(String*);
+ my_decimal *val_decimal(my_decimal *);
+ void make_field(Send_field *field) { item->make_field(field); }
+ void copy();
+ int save_in_field(Field *field, In_C_you_should_use_my_bool_instead() no_conversions)
+ {
+ return save_str_value_in_field(field, &str_value);
+ }
+ table_map used_tables() const { return (table_map) 1L; }
+ In_C_you_should_use_my_bool_instead() const_item() const { return 0; }
+ In_C_you_should_use_my_bool_instead() is_null() { return null_value; }
+};
+class Cached_item :public Sql_alloc
+{
+public:
+ my_bool null_value;
+ Cached_item() :null_value(0) {}
+ virtual In_C_you_should_use_my_bool_instead() cmp(void)=0;
+ virtual ~Cached_item();
+};
+class Cached_item_str :public Cached_item
+{
+ Item *item;
+ String value,tmp_value;
+public:
+ Cached_item_str(THD *thd, Item *arg);
+ In_C_you_should_use_my_bool_instead() cmp(void);
+ ~Cached_item_str();
+};
+class Cached_item_real :public Cached_item
+{
+ Item *item;
+ double value;
+public:
+ Cached_item_real(Item *item_par) :item(item_par),value(0.0) {}
+ In_C_you_should_use_my_bool_instead() cmp(void);
+};
+class Cached_item_int :public Cached_item
+{
+ Item *item;
+ longlong value;
+public:
+ Cached_item_int(Item *item_par) :item(item_par),value(0) {}
+ In_C_you_should_use_my_bool_instead() cmp(void);
+};
+class Cached_item_decimal :public Cached_item
+{
+ Item *item;
+ my_decimal value;
+public:
+ Cached_item_decimal(Item *item_par);
+ In_C_you_should_use_my_bool_instead() cmp(void);
+};
+class Cached_item_field :public Cached_item
+{
+ uchar *buff;
+ Field *field;
+ uint length;
+public:
+ Cached_item_field(Item_field *item)
+ {
+ field= item->field;
+ buff= (uchar*) sql_calloc(length=field->pack_length());
+ }
+ In_C_you_should_use_my_bool_instead() cmp(void);
+};
+class Item_default_value : public Item_field
+{
+public:
+ Item *arg;
+ Item_default_value(Name_resolution_context *context_arg)
+ :Item_field(context_arg, (const char *)NULL, (const char *)NULL,
+ (const char *)NULL),
+ arg(NULL) {}
+ Item_default_value(Name_resolution_context *context_arg, Item *a)
+ :Item_field(context_arg, (const char *)NULL, (const char *)NULL,
+ (const char *)NULL),
+ arg(a) {}
+ enum Type type() const { return DEFAULT_VALUE_ITEM; }
+ In_C_you_should_use_my_bool_instead() eq(const Item *item, In_C_you_should_use_my_bool_instead() binary_cmp) const;
+ In_C_you_should_use_my_bool_instead() fix_fields(THD *, Item **);
+ virtual void print(String *str, enum_query_type query_type);
+ int save_in_field(Field *field_arg, In_C_you_should_use_my_bool_instead() no_conversions);
+ table_map used_tables() const { return (table_map)0L; }
+ In_C_you_should_use_my_bool_instead() walk(Item_processor processor, In_C_you_should_use_my_bool_instead() walk_subquery, uchar *args)
+ {
+ return arg->walk(processor, walk_subquery, args) ||
+ (this->*processor)(args);
+ }
+ Item *transform(Item_transformer transformer, uchar *args);
+};
+class Item_insert_value : public Item_field
+{
+public:
+ Item *arg;
+ Item_insert_value(Name_resolution_context *context_arg, Item *a)
+ :Item_field(context_arg, (const char *)NULL, (const char *)NULL,
+ (const char *)NULL),
+ arg(a) {}
+ In_C_you_should_use_my_bool_instead() eq(const Item *item, In_C_you_should_use_my_bool_instead() binary_cmp) const;
+ In_C_you_should_use_my_bool_instead() fix_fields(THD *, Item **);
+ virtual void print(String *str, enum_query_type query_type);
+ int save_in_field(Field *field_arg, In_C_you_should_use_my_bool_instead() no_conversions)
+ {
+ return Item_field::save_in_field(field_arg, no_conversions);
+ }
+ table_map used_tables() const { return (((table_map) 1) << (sizeof(table_map)*8-1)); }
+ In_C_you_should_use_my_bool_instead() walk(Item_processor processor, In_C_you_should_use_my_bool_instead() walk_subquery, uchar *args)
+ {
+ return arg->walk(processor, walk_subquery, args) ||
+ (this->*processor)(args);
+ }
+};
+enum trg_action_time_type
+{
+ TRG_ACTION_BEFORE= 0, TRG_ACTION_AFTER= 1, TRG_ACTION_MAX
+};
+class Table_triggers_list;
+class Item_trigger_field : public Item_field,
+ private Settable_routine_parameter
+{
+public:
+ enum row_version_type {OLD_ROW, NEW_ROW};
+ row_version_type row_version;
+ Item_trigger_field *next_trg_field;
+ uint field_idx;
+ Table_triggers_list *triggers;
+ Item_trigger_field(Name_resolution_context *context_arg,
+ row_version_type row_ver_arg,
+ const char *field_name_arg,
+ ulong priv, const In_C_you_should_use_my_bool_instead() ro)
+ :Item_field(context_arg,
+ (const char *)NULL, (const char *)NULL, field_name_arg),
+ row_version(row_ver_arg), field_idx((uint)-1), original_privilege(priv),
+ want_privilege(priv), table_grants(NULL), read_only (ro)
+ {}
+ void setup_field(THD *thd, TABLE *table, GRANT_INFO *table_grant_info);
+ enum Type type() const { return TRIGGER_FIELD_ITEM; }
+ In_C_you_should_use_my_bool_instead() eq(const Item *item, In_C_you_should_use_my_bool_instead() binary_cmp) const;
+ In_C_you_should_use_my_bool_instead() fix_fields(THD *, Item **);
+ virtual void print(String *str, enum_query_type query_type);
+ table_map used_tables() const { return (table_map)0L; }
+ Field *get_tmp_table_field() { return 0; }
+ Item *copy_or_same(THD *thd) { return this; }
+ Item *get_tmp_table_item(THD *thd) { return copy_or_same(thd); }
+ void cleanup();
+private:
+ void set_required_privilege(In_C_you_should_use_my_bool_instead() rw);
+ In_C_you_should_use_my_bool_instead() set_value(THD *thd, sp_rcontext *ctx, Item **it);
+public:
+ Settable_routine_parameter *get_settable_routine_parameter()
+ {
+ return (read_only ? 0 : this);
+ }
+ In_C_you_should_use_my_bool_instead() set_value(THD *thd, Item **it)
+ {
+ return set_value(thd, NULL, it);
+ }
+private:
+ ulong original_privilege;
+ ulong want_privilege;
+ GRANT_INFO *table_grants;
+ In_C_you_should_use_my_bool_instead() read_only;
+};
+class Item_cache: public Item_basic_constant
+{
+protected:
+ Item *example;
+ table_map used_table_map;
+ Field *cached_field;
+ enum enum_field_types cached_field_type;
+public:
+ Item_cache():
+ example(0), used_table_map(0), cached_field(0), cached_field_type(MYSQL_TYPE_STRING)
+ {
+ fixed= 1;
+ 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)
+ {
+ fixed= 1;
+ null_value= 1;
+ }
+ void set_used_tables(table_map map) { used_table_map= map; }
+ virtual In_C_you_should_use_my_bool_instead() allocate(uint i) { return 0; }
+ virtual In_C_you_should_use_my_bool_instead() setup(Item *item)
+ {
+ example= item;
+ max_length= item->max_length;
+ decimals= item->decimals;
+ collation.set(item->collation);
+ unsigned_flag= item->unsigned_flag;
+ if (item->type() == FIELD_ITEM)
+ cached_field= ((Item_field *)item)->field;
+ return 0;
+ };
+ virtual void store(Item *)= 0;
+ enum Type type() const { return CACHE_ITEM; }
+ enum_field_types field_type() const { return cached_field_type; }
+ static Item_cache* get_cache(const Item *item);
+ table_map used_tables() const { return used_table_map; }
+ virtual void keep_array() {}
+ virtual void print(String *str, enum_query_type query_type);
+ In_C_you_should_use_my_bool_instead() eq_def(Field *field)
+ {
+ return cached_field ? cached_field->eq_def (field) : (0);
+ }
+ In_C_you_should_use_my_bool_instead() eq(const Item *item, In_C_you_should_use_my_bool_instead() binary_cmp) const
+ {
+ return this == item;
+ }
+};
+class Item_cache_int: public Item_cache
+{
+protected:
+ longlong value;
+public:
+ Item_cache_int(): Item_cache(), value(0) {}
+ Item_cache_int(enum_field_types field_type_arg):
+ Item_cache(field_type_arg), value(0) {}
+ void store(Item *item);
+ void store(Item *item, longlong val_arg);
+ double val_real() { assert(fixed == 1); return (double) value; }
+ longlong val_int() { assert(fixed == 1); return value; }
+ String* val_str(String *str);
+ my_decimal *val_decimal(my_decimal *);
+ enum Item_result result_type() const { return INT_RESULT; }
+ In_C_you_should_use_my_bool_instead() result_as_longlong() { return (1); }
+};
+class Item_cache_real: public Item_cache
+{
+ double value;
+public:
+ Item_cache_real(): Item_cache(), value(0) {}
+ void store(Item *item);
+ double val_real() { assert(fixed == 1); return value; }
+ longlong val_int();
+ String* val_str(String *str);
+ my_decimal *val_decimal(my_decimal *);
+ enum Item_result result_type() const { return REAL_RESULT; }
+};
+class Item_cache_decimal: public Item_cache
+{
+protected:
+ my_decimal decimal_value;
+public:
+ Item_cache_decimal(): Item_cache() {}
+ void store(Item *item);
+ double val_real();
+ longlong val_int();
+ String* val_str(String *str);
+ my_decimal *val_decimal(my_decimal *);
+ enum Item_result result_type() const { return DECIMAL_RESULT; }
+};
+class Item_cache_str: public Item_cache
+{
+ char buffer[80];
+ String *value, value_buff;
+ In_C_you_should_use_my_bool_instead() is_varbinary;
+public:
+ Item_cache_str(const Item *item) :
+ Item_cache(), value(0),
+ is_varbinary(item->type() == FIELD_ITEM &&
+ ((const Item_field *) item)->field->type() ==
+ MYSQL_TYPE_VARCHAR &&
+ !((const Item_field *) item)->field->has_charset())
+ {}
+ void store(Item *item);
+ double val_real();
+ longlong val_int();
+ String* val_str(String *) { assert(fixed == 1); return value; }
+ my_decimal *val_decimal(my_decimal *);
+ enum Item_result result_type() const { return STRING_RESULT; }
+ CHARSET_INFO *charset() const { return value->charset(); };
+ int save_in_field(Field *field, In_C_you_should_use_my_bool_instead() no_conversions);
+};
+class Item_cache_row: public Item_cache
+{
+ Item_cache **values;
+ uint item_count;
+ In_C_you_should_use_my_bool_instead() save_array;
+public:
+ Item_cache_row()
+ :Item_cache(), values(0), item_count(2), save_array(0) {}
+ In_C_you_should_use_my_bool_instead() allocate(uint num);
+ In_C_you_should_use_my_bool_instead() setup(Item *item);
+ void store(Item *item);
+ void illegal_method_call(const char *);
+ void make_field(Send_field *)
+ {
+ illegal_method_call((const char*)"make_field");
+ };
+ double val_real()
+ {
+ illegal_method_call((const char*)"val");
+ return 0;
+ };
+ longlong val_int()
+ {
+ illegal_method_call((const char*)"val_int");
+ return 0;
+ };
+ String *val_str(String *)
+ {
+ illegal_method_call((const char*)"val_str");
+ return 0;
+ };
+ my_decimal *val_decimal(my_decimal *val)
+ {
+ illegal_method_call((const char*)"val_decimal");
+ return 0;
+ };
+ enum Item_result result_type() const { return ROW_RESULT; }
+ uint cols() { return item_count; }
+ Item *element_index(uint i) { return values[i]; }
+ Item **addr(uint i) { return (Item **) (values + i); }
+ In_C_you_should_use_my_bool_instead() check_cols(uint c);
+ In_C_you_should_use_my_bool_instead() null_inside();
+ void bring_value();
+ void keep_array() { save_array= 1; }
+ void cleanup()
+ {
+ const char *_db_func_, *_db_file_; uint _db_level_; char **_db_framep_; _db_enter_ ("Item_cache_row::cleanup","./sql/item.h",2898,&_db_func_,&_db_file_,&_db_level_, &_db_framep_);
+ Item_cache::cleanup();
+ if (save_array)
+ bzero(values, item_count*sizeof(Item**));
+ else
+ values= 0;
+ do {_db_return_ (2904, &_db_func_, &_db_file_, &_db_level_); return;} while(0);
+ }
+};
+class Item_type_holder: public Item
+{
+protected:
+ TYPELIB *enum_set_typelib;
+ enum_field_types fld_type;
+ Field::geometry_type geometry_type;
+ void get_full_info(Item *item);
+ int prev_decimal_int_part;
+public:
+ Item_type_holder(THD*, Item*);
+ Item_result result_type() const;
+ enum_field_types field_type() const { return fld_type; };
+ enum Type type() const { return TYPE_HOLDER; }
+ double val_real();
+ longlong val_int();
+ my_decimal *val_decimal(my_decimal *);
+ String *val_str(String*);
+ In_C_you_should_use_my_bool_instead() join_types(THD *thd, Item *);
+ Field *make_field_by_type(TABLE *table);
+ static uint32 display_length(Item *item);
+ static enum_field_types get_real_type(Item *);
+ Field::geometry_type get_geometry_type() const { return geometry_type; };
+};
+class st_select_lex;
+void mark_select_range_as_dependent(THD *thd,
+ st_select_lex *last_select,
+ st_select_lex *current_sel,
+ Field *found_field, Item *found_item,
+ Item_ident *resolved_item);
+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 In_C_you_should_use_my_bool_instead() field_is_equal_to_item(Field *field,Item *item);
+extern my_decimal decimal_zero;
+void free_items(Item *item);
+void cleanup_items(Item *item);
+class THD;
+void close_thread_tables(THD *thd);
+In_C_you_should_use_my_bool_instead() check_one_table_access(THD *thd, ulong privilege, TABLE_LIST *tables);
+In_C_you_should_use_my_bool_instead() check_single_table_access(THD *thd, ulong privilege,
+ TABLE_LIST *tables, In_C_you_should_use_my_bool_instead() no_errors);
+In_C_you_should_use_my_bool_instead() check_routine_access(THD *thd,ulong want_access,char *db,char *name,
+ In_C_you_should_use_my_bool_instead() is_proc, In_C_you_should_use_my_bool_instead() no_errors);
+In_C_you_should_use_my_bool_instead() check_some_access(THD *thd, ulong want_access, TABLE_LIST *table);
+In_C_you_should_use_my_bool_instead() check_some_routine_access(THD *thd, const char *db, const char *name, In_C_you_should_use_my_bool_instead() is_proc);
+In_C_you_should_use_my_bool_instead() multi_update_precheck(THD *thd, TABLE_LIST *tables);
+In_C_you_should_use_my_bool_instead() multi_delete_precheck(THD *thd, TABLE_LIST *tables);
+int mysql_multi_update_prepare(THD *thd);
+int mysql_multi_delete_prepare(THD *thd);
+In_C_you_should_use_my_bool_instead() mysql_insert_select_prepare(THD *thd);
+In_C_you_should_use_my_bool_instead() update_precheck(THD *thd, TABLE_LIST *tables);
+In_C_you_should_use_my_bool_instead() delete_precheck(THD *thd, TABLE_LIST *tables);
+In_C_you_should_use_my_bool_instead() insert_precheck(THD *thd, TABLE_LIST *tables);
+In_C_you_should_use_my_bool_instead() create_table_precheck(THD *thd, TABLE_LIST *tables,
+ TABLE_LIST *create_table);
+int append_query_string(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);
+In_C_you_should_use_my_bool_instead() check_string_byte_length(LEX_STRING *str, const char *err_msg,
+ uint max_byte_length);
+In_C_you_should_use_my_bool_instead() check_string_char_length(LEX_STRING *str, const char *err_msg,
+ uint max_char_length, CHARSET_INFO *cs,
+ In_C_you_should_use_my_bool_instead() no_error);
+In_C_you_should_use_my_bool_instead() test_if_data_home_dir(const char *dir);
+In_C_you_should_use_my_bool_instead() parse_sql(THD *thd,
+ class Lex_input_stream *lip,
+ class 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
+};
+In_C_you_should_use_my_bool_instead() begin_trans(THD *thd);
+In_C_you_should_use_my_bool_instead() end_active_trans(THD *thd);
+int end_trans(THD *thd, enum enum_mysql_completiontype completion);
+Item *negate_expression(THD *thd, Item *expr);
+int vprint_msg_to_log(enum loglevel level, const char *format, va_list args);
+void sql_print_error(const char *format, ...) __attribute__((format(printf, 1, 2)));
+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(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);
+In_C_you_should_use_my_bool_instead() slow_log_print(THD *thd, const char *query, uint query_length,
+ ulonglong current_utime);
+In_C_you_should_use_my_bool_instead() general_log_print(THD *thd, enum enum_server_command command,
+ const char *format,...);
+In_C_you_should_use_my_bool_instead() general_log_write(THD *thd, enum enum_server_command command,
+ const char *query, uint query_length);
+#include "sql_class.h"
+#include "log.h"
+class Relay_log_info;
+class Format_description_log_event;
+class TC_LOG
+{
+ public:
+ int using_heuristic_recover();
+ TC_LOG() {}
+ virtual ~TC_LOG() {}
+ virtual int open(const char *opt_name)=0;
+ virtual void close()=0;
+ virtual int log_xid(THD *thd, my_xid xid)=0;
+ virtual void unlog(ulong cookie, my_xid xid)=0;
+};
+class TC_LOG_DUMMY: public TC_LOG
+{
+public:
+ TC_LOG_DUMMY() {}
+ int open(const char *opt_name) { return 0; }
+ void close() { }
+ int log_xid(THD *thd, my_xid xid) { return 1; }
+ void unlog(ulong cookie, my_xid xid) { }
+};
+class TC_LOG_MMAP: public TC_LOG
+{
+ public:
+ typedef enum {
+ POOL,
+ ERROR,
+ DIRTY
+ } PAGE_STATE;
+ private:
+ typedef struct st_page {
+ struct st_page *next;
+ my_xid *start, *end;
+ my_xid *ptr;
+ int size, free;
+ int waiters;
+ PAGE_STATE state;
+ pthread_mutex_t lock;
+ pthread_cond_t cond;
+ } PAGE;
+ char logname[512];
+ File fd;
+ my_off_t file_length;
+ uint npages, inited;
+ uchar *data;
+ struct st_page *pages, *syncing, *active, *pool, *pool_last;
+ pthread_mutex_t LOCK_active, LOCK_pool, LOCK_sync;
+ pthread_cond_t COND_pool, COND_active;
+ public:
+ TC_LOG_MMAP(): inited(0) {}
+ int open(const char *opt_name);
+ void close();
+ int log_xid(THD *thd, my_xid xid);
+ void unlog(ulong cookie, my_xid xid);
+ int recover();
+ private:
+ void get_active_from_pool();
+ int sync();
+ int overflow();
+};
+extern TC_LOG *tc_log;
+extern TC_LOG_MMAP tc_log_mmap;
+extern TC_LOG_DUMMY tc_log_dummy;
+class Relay_log_info;
+typedef struct st_log_info
+{
+ char log_file_name[512];
+ my_off_t index_file_offset, index_file_start_offset;
+ my_off_t pos;
+ In_C_you_should_use_my_bool_instead() fatal;
+ pthread_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, NULL);
+ }
+ ~st_log_info() { pthread_mutex_destroy(&lock);}
+} LOG_INFO;
+class Log_event;
+class Rows_log_event;
+enum enum_log_type { LOG_UNKNOWN, LOG_NORMAL, LOG_BIN };
+enum enum_log_state { LOG_OPENED, LOG_CLOSED, LOG_TO_BE_OPENED };
+class MYSQL_LOG
+{
+public:
+ MYSQL_LOG();
+ void init_pthread_objects();
+ void cleanup();
+ In_C_you_should_use_my_bool_instead() open(const char *log_name,
+ enum_log_type log_type,
+ const char *new_name,
+ enum cache_type io_cache_type_arg);
+ void init(enum_log_type log_type_arg,
+ enum cache_type io_cache_type_arg);
+ void close(uint exiting);
+ inline In_C_you_should_use_my_bool_instead() is_open() { return log_state != LOG_CLOSED; }
+ const char *generate_name(const char *log_name, const char *suffix,
+ In_C_you_should_use_my_bool_instead() strip_ext, char *buff);
+ int generate_new_name(char *new_name, const char *log_name);
+ protected:
+ pthread_mutex_t LOCK_log;
+ char *name;
+ char log_file_name[512];
+ char time_buff[20], db[(64*3) + 1];
+ In_C_you_should_use_my_bool_instead() write_error, inited;
+ IO_CACHE log_file;
+ enum_log_type log_type;
+ volatile enum_log_state log_state;
+ enum cache_type io_cache_type;
+ friend class Log_event;
+};
+class MYSQL_QUERY_LOG: public MYSQL_LOG
+{
+public:
+ MYSQL_QUERY_LOG() : last_time(0) {}
+ void reopen_file();
+ In_C_you_should_use_my_bool_instead() write(time_t event_time, const char *user_host,
+ uint user_host_len, int thread_id,
+ const char *command_type, uint command_type_len,
+ const char *sql_text, uint sql_text_len);
+ In_C_you_should_use_my_bool_instead() write(THD *thd, time_t current_time, time_t query_start_arg,
+ const char *user_host, uint user_host_len,
+ ulonglong query_utime, ulonglong lock_utime, In_C_you_should_use_my_bool_instead() is_command,
+ const char *sql_text, uint sql_text_len);
+ In_C_you_should_use_my_bool_instead() open_slow_log(const char *log_name)
+ {
+ char buf[512];
+ return open(generate_name(log_name, "-slow.log", 0, buf), LOG_NORMAL, 0,
+ WRITE_CACHE);
+ }
+ In_C_you_should_use_my_bool_instead() open_query_log(const char *log_name)
+ {
+ char buf[512];
+ return open(generate_name(log_name, ".log", 0, buf), LOG_NORMAL, 0,
+ WRITE_CACHE);
+ }
+private:
+ time_t last_time;
+};
+class MYSQL_BIN_LOG: public TC_LOG, private MYSQL_LOG
+{
+ private:
+ pthread_mutex_t LOCK_index;
+ pthread_mutex_t LOCK_prep_xids;
+ pthread_cond_t COND_prep_xids;
+ pthread_cond_t update_cond;
+ ulonglong bytes_written;
+ IO_CACHE index_file;
+ char index_file_name[512];
+ ulong max_size;
+ long prepared_xids;
+ uint file_id;
+ uint open_count;
+ int readers_count;
+ In_C_you_should_use_my_bool_instead() need_start_event;
+ In_C_you_should_use_my_bool_instead() no_auto_events;
+ ulonglong m_table_map_version;
+ int write_to_file(IO_CACHE *cache);
+ void new_file_without_locking();
+ void new_file_impl(In_C_you_should_use_my_bool_instead() need_lock);
+public:
+ MYSQL_LOG::generate_name;
+ MYSQL_LOG::is_open;
+ Format_description_log_event *description_event_for_exec,
+ *description_event_for_queue;
+ MYSQL_BIN_LOG();
+ int open(const char *opt_name);
+ void close();
+ int log_xid(THD *thd, my_xid xid);
+ void unlog(ulong cookie, my_xid xid);
+ int recover(IO_CACHE *log, Format_description_log_event *fdle);
+ In_C_you_should_use_my_bool_instead() is_table_mapped(TABLE *table) const
+ {
+ return table->s->table_map_version == table_map_version();
+ }
+ ulonglong table_map_version() const { return m_table_map_version; }
+ void update_table_map_version() { ++m_table_map_version; }
+ int flush_and_set_pending_rows_event(THD *thd, Rows_log_event* event);
+ void reset_bytes_written()
+ {
+ bytes_written = 0;
+ }
+ void harvest_bytes_written(ulonglong* counter)
+ {
+ char buf1[22],buf2[22];
+ const char *_db_func_, *_db_file_; uint _db_level_; char **_db_framep_; _db_enter_ ("harvest_bytes_written","./sql/log.h",321,&_db_func_,&_db_file_,&_db_level_, &_db_framep_);
+ (*counter)+=bytes_written;
+ do {_db_pargs_(324,"info"); _db_doprnt_ ("counter: %s bytes_written: %s", llstr(*counter,buf1), llstr(bytes_written,buf2));} while(0);
+ bytes_written=0;
+ do {_db_return_ (326, &_db_func_, &_db_file_, &_db_level_); return;} while(0);
+ }
+ void set_max_size(ulong max_size_arg);
+ void signal_update();
+ void wait_for_update(THD* thd, In_C_you_should_use_my_bool_instead() master_or_slave);
+ void set_need_start_event() { need_start_event = 1; }
+ void init(In_C_you_should_use_my_bool_instead() no_auto_events_arg, ulong max_size);
+ void init_pthread_objects();
+ void cleanup();
+ In_C_you_should_use_my_bool_instead() open(const char *log_name,
+ enum_log_type log_type,
+ const char *new_name,
+ enum cache_type io_cache_type_arg,
+ In_C_you_should_use_my_bool_instead() no_auto_events_arg, ulong max_size,
+ In_C_you_should_use_my_bool_instead() null_created);
+ In_C_you_should_use_my_bool_instead() open_index_file(const char *index_file_name_arg,
+ const char *log_name);
+ void new_file();
+ In_C_you_should_use_my_bool_instead() write(Log_event* event_info);
+ In_C_you_should_use_my_bool_instead() write(THD *thd, IO_CACHE *cache, Log_event *commit_event);
+ int write_cache(IO_CACHE *cache, In_C_you_should_use_my_bool_instead() lock_log, In_C_you_should_use_my_bool_instead() flush_and_sync);
+ void start_union_events(THD *thd, query_id_t query_id_param);
+ void stop_union_events(THD *thd);
+ In_C_you_should_use_my_bool_instead() is_query_in_union(THD *thd, query_id_t query_id_param);
+ In_C_you_should_use_my_bool_instead() appendv(const char* buf,uint len,...);
+ In_C_you_should_use_my_bool_instead() append(Log_event* ev);
+ void make_log_name(char* buf, const char* log_ident);
+ In_C_you_should_use_my_bool_instead() is_active(const char* log_file_name);
+ int update_log_index(LOG_INFO* linfo, In_C_you_should_use_my_bool_instead() need_update_threads);
+ void rotate_and_purge(uint flags);
+ In_C_you_should_use_my_bool_instead() flush_and_sync();
+ int purge_logs(const char *to_log, In_C_you_should_use_my_bool_instead() included,
+ In_C_you_should_use_my_bool_instead() need_mutex, In_C_you_should_use_my_bool_instead() need_update_threads,
+ ulonglong *decrease_log_space);
+ int purge_logs_before_date(time_t purge_time);
+ int purge_first_log(Relay_log_info* rli, In_C_you_should_use_my_bool_instead() included);
+ In_C_you_should_use_my_bool_instead() reset_logs(THD* thd);
+ void close(uint exiting);
+ int find_log_pos(LOG_INFO* linfo, const char* log_name,
+ In_C_you_should_use_my_bool_instead() need_mutex);
+ int find_next_log(LOG_INFO* linfo, In_C_you_should_use_my_bool_instead() need_mutex);
+ int get_current_log(LOG_INFO* linfo);
+ int raw_get_current_log(LOG_INFO* linfo);
+ uint next_file_id();
+ 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 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 IO_CACHE *get_index_file() { return &index_file;}
+ inline uint32 get_open_count() { return open_count; }
+};
+class Log_event_handler
+{
+public:
+ Log_event_handler() {}
+ virtual In_C_you_should_use_my_bool_instead() init()= 0;
+ virtual void cleanup()= 0;
+ virtual In_C_you_should_use_my_bool_instead() log_slow(THD *thd, time_t current_time,
+ time_t query_start_arg, const char *user_host,
+ uint user_host_len, ulonglong query_utime,
+ ulonglong lock_utime, In_C_you_should_use_my_bool_instead() is_command,
+ const char *sql_text, uint sql_text_len)= 0;
+ virtual In_C_you_should_use_my_bool_instead() log_error(enum loglevel level, const char *format,
+ va_list args)= 0;
+ virtual In_C_you_should_use_my_bool_instead() log_general(THD *thd, time_t event_time, const char *user_host,
+ uint user_host_len, int thread_id,
+ const char *command_type, uint command_type_len,
+ const char *sql_text, uint sql_text_len,
+ CHARSET_INFO *client_cs)= 0;
+ virtual ~Log_event_handler() {}
+};
+int check_if_log_table(uint db_len, const char *db, uint table_name_len,
+ const char *table_name, uint check_if_opened);
+class Log_to_csv_event_handler: public Log_event_handler
+{
+ friend class LOGGER;
+public:
+ Log_to_csv_event_handler();
+ ~Log_to_csv_event_handler();
+ virtual In_C_you_should_use_my_bool_instead() init();
+ virtual void cleanup();
+ virtual In_C_you_should_use_my_bool_instead() log_slow(THD *thd, time_t current_time,
+ time_t query_start_arg, const char *user_host,
+ uint user_host_len, ulonglong query_utime,
+ ulonglong lock_utime, In_C_you_should_use_my_bool_instead() is_command,
+ const char *sql_text, uint sql_text_len);
+ virtual In_C_you_should_use_my_bool_instead() log_error(enum loglevel level, const char *format,
+ va_list args);
+ virtual In_C_you_should_use_my_bool_instead() log_general(THD *thd, time_t event_time, const char *user_host,
+ uint user_host_len, int thread_id,
+ const char *command_type, uint command_type_len,
+ const char *sql_text, uint sql_text_len,
+ CHARSET_INFO *client_cs);
+ int activate_log(THD *thd, uint log_type);
+};
+class Log_to_file_event_handler: public Log_event_handler
+{
+ MYSQL_QUERY_LOG mysql_log;
+ MYSQL_QUERY_LOG mysql_slow_log;
+ In_C_you_should_use_my_bool_instead() is_initialized;
+public:
+ Log_to_file_event_handler(): is_initialized((0))
+ {}
+ virtual In_C_you_should_use_my_bool_instead() init();
+ virtual void cleanup();
+ virtual In_C_you_should_use_my_bool_instead() log_slow(THD *thd, time_t current_time,
+ time_t query_start_arg, const char *user_host,
+ uint user_host_len, ulonglong query_utime,
+ ulonglong lock_utime, In_C_you_should_use_my_bool_instead() is_command,
+ const char *sql_text, uint sql_text_len);
+ virtual In_C_you_should_use_my_bool_instead() log_error(enum loglevel level, const char *format,
+ va_list args);
+ virtual In_C_you_should_use_my_bool_instead() log_general(THD *thd, time_t event_time, const char *user_host,
+ uint user_host_len, int thread_id,
+ const char *command_type, uint command_type_len,
+ const char *sql_text, uint sql_text_len,
+ CHARSET_INFO *client_cs);
+ void flush();
+ void init_pthread_objects();
+ MYSQL_QUERY_LOG *get_mysql_slow_log() { return &mysql_slow_log; }
+ MYSQL_QUERY_LOG *get_mysql_log() { return &mysql_log; }
+};
+class LOGGER
+{
+ pthread_rwlock_t LOCK_logger;
+ uint inited;
+ Log_to_csv_event_handler *table_log_handler;
+ Log_to_file_event_handler *file_log_handler;
+ Log_event_handler *error_log_handler_list[3 + 1];
+ Log_event_handler *slow_log_handler_list[3 + 1];
+ Log_event_handler *general_log_handler_list[3 + 1];
+public:
+ In_C_you_should_use_my_bool_instead() is_log_tables_initialized;
+ LOGGER() : inited(0), table_log_handler(NULL),
+ file_log_handler(NULL), is_log_tables_initialized((0))
+ {}
+ void lock_shared() { pthread_rwlock_rdlock(&LOCK_logger); }
+ void lock_exclusive() { pthread_rwlock_wrlock(&LOCK_logger); }
+ void unlock() { pthread_rwlock_unlock(&LOCK_logger); }
+ In_C_you_should_use_my_bool_instead() is_log_table_enabled(uint log_table_type);
+ In_C_you_should_use_my_bool_instead() log_command(THD *thd, enum enum_server_command command);
+ void init_base();
+ void init_log_tables();
+ In_C_you_should_use_my_bool_instead() flush_logs(THD *thd);
+ void cleanup_base();
+ void cleanup_end();
+ In_C_you_should_use_my_bool_instead() error_log_print(enum loglevel level, const char *format,
+ va_list args);
+ In_C_you_should_use_my_bool_instead() slow_log_print(THD *thd, const char *query, uint query_length,
+ ulonglong current_utime);
+ In_C_you_should_use_my_bool_instead() general_log_print(THD *thd,enum enum_server_command command,
+ const char *format, va_list args);
+ In_C_you_should_use_my_bool_instead() general_log_write(THD *thd, enum enum_server_command command,
+ const char *query, uint query_length);
+ 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);
+ void deactivate_log_handler(THD* thd, uint log_type);
+ In_C_you_should_use_my_bool_instead() activate_log_handler(THD* thd, uint log_type);
+ MYSQL_QUERY_LOG *get_slow_log_file_handler()
+ {
+ if (file_log_handler)
+ return file_log_handler->get_mysql_slow_log();
+ return NULL;
+ }
+ MYSQL_QUERY_LOG *get_log_file_handler()
+ {
+ if (file_log_handler)
+ return file_log_handler->get_mysql_log();
+ return NULL;
+ }
+};
+enum enum_binlog_format {
+ BINLOG_FORMAT_MIXED= 0,
+ BINLOG_FORMAT_STMT= 1,
+ BINLOG_FORMAT_ROW= 2,
+ BINLOG_FORMAT_UNSPEC= 3
+};
+extern TYPELIB binlog_format_typelib;
+#include "rpl_tblmap.h"
+struct st_table;
+typedef st_table TABLE;
+class table_mapping {
+private:
+ MEM_ROOT m_mem_root;
+public:
+ enum enum_error {
+ ERR_NO_ERROR = 0,
+ ERR_LIMIT_EXCEEDED,
+ ERR_MEMORY_ALLOCATION
+ };
+ table_mapping();
+ ~table_mapping();
+ TABLE* get_table(ulong table_id);
+ int set_table(ulong table_id, TABLE* table);
+ int remove_table(ulong table_id);
+ void clear_tables();
+ ulong count() const { return m_table_ids.records; }
+private:
+ struct entry {
+ ulong table_id;
+ union {
+ TABLE *table;
+ entry *next;
+ };
+ };
+ entry *find_entry(ulong table_id)
+ {
+ return (entry *)hash_search(&m_table_ids,
+ (uchar*)&table_id,
+ sizeof(table_id));
+ }
+ int expand();
+ entry *m_free;
+ HASH m_table_ids;
+};
+class Reprepare_observer
+{
+public:
+ In_C_you_should_use_my_bool_instead() report_error(THD *thd);
+ In_C_you_should_use_my_bool_instead() is_invalidated() const { return m_invalidated; }
+ void reset_reprepare_observer() { m_invalidated= (0); }
+private:
+ In_C_you_should_use_my_bool_instead() m_invalidated;
+};
+class Relay_log_info;
+class Query_log_event;
+class Load_log_event;
+class Slave_log_event;
+class sp_rcontext;
+class sp_cache;
+class Lex_input_stream;
+class Rows_log_event;
+enum enum_enable_or_disable { LEAVE_AS_IS, ENABLE, DISABLE };
+enum enum_ha_read_modes { RFIRST, RNEXT, RPREV, RLAST, RKEY, RNEXT_SAME };
+enum enum_duplicates { DUP_ERROR, DUP_REPLACE, DUP_UPDATE };
+enum enum_delay_key_write { DELAY_KEY_WRITE_NONE, DELAY_KEY_WRITE_ON,
+ DELAY_KEY_WRITE_ALL };
+enum enum_slave_exec_mode { SLAVE_EXEC_MODE_STRICT,
+ SLAVE_EXEC_MODE_IDEMPOTENT,
+ SLAVE_EXEC_MODE_LAST_BIT};
+enum enum_mark_columns
+{ MARK_COLUMNS_NONE, MARK_COLUMNS_READ, MARK_COLUMNS_WRITE};
+extern char internal_table_name[2];
+extern char empty_c_string[1];
+extern const char **errmesg;
+extern uint tc_heuristic_recover;
+typedef struct st_user_var_events
+{
+ user_var_entry *user_var_event;
+ char *value;
+ ulong length;
+ Item_result type;
+ uint charset_number;
+} BINLOG_USER_VAR_EVENT;
+typedef struct st_copy_info {
+ ha_rows records;
+ ha_rows deleted;
+ ha_rows updated;
+ ha_rows copied;
+ ha_rows error_count;
+ ha_rows touched;
+ enum enum_duplicates handle_duplicates;
+ int escape_char, last_errno;
+ In_C_you_should_use_my_bool_instead() ignore;
+ List<Item> *update_fields;
+ List<Item> *update_values;
+ TABLE_LIST *view;
+} COPY_INFO;
+class Key_part_spec :public Sql_alloc {
+public:
+ const char *field_name;
+ uint length;
+ Key_part_spec(const char *name,uint len=0) :field_name(name), length(len) {}
+ In_C_you_should_use_my_bool_instead() operator==(const Key_part_spec& other) const;
+ Key_part_spec *clone(MEM_ROOT *mem_root) const
+ { return new (mem_root) Key_part_spec(*this); }
+};
+class Alter_drop :public Sql_alloc {
+public:
+ enum drop_type {KEY, COLUMN };
+ const char *name;
+ enum drop_type type;
+ Alter_drop(enum drop_type par_type,const char *par_name)
+ :name(par_name), type(par_type) {}
+ Alter_drop *clone(MEM_ROOT *mem_root) const
+ { return new (mem_root) Alter_drop(*this); }
+};
+class Alter_column :public Sql_alloc {
+public:
+ const char *name;
+ Item *def;
+ Alter_column(const char *par_name,Item *literal)
+ :name(par_name), def(literal) {}
+ Alter_column *clone(MEM_ROOT *mem_root) const
+ { return new (mem_root) Alter_column(*this); }
+};
+class Key :public Sql_alloc {
+public:
+ enum Keytype { PRIMARY, UNIQUE, MULTIPLE, FULLTEXT, SPATIAL, FOREIGN_KEY};
+ enum Keytype type;
+ KEY_CREATE_INFO key_create_info;
+ List<Key_part_spec> columns;
+ const char *name;
+ In_C_you_should_use_my_bool_instead() generated;
+ Key(enum Keytype type_par, const char *name_arg,
+ KEY_CREATE_INFO *key_info_arg,
+ In_C_you_should_use_my_bool_instead() generated_arg, List<Key_part_spec> &cols)
+ :type(type_par), key_create_info(*key_info_arg), columns(cols),
+ name(name_arg), generated(generated_arg)
+ {}
+ Key(const Key &rhs, MEM_ROOT *mem_root);
+ virtual ~Key() {}
+ friend In_C_you_should_use_my_bool_instead() foreign_key_prefix(Key *a, Key *b);
+ virtual Key *clone(MEM_ROOT *mem_root) const
+ { return new (mem_root) Key(*this, mem_root); }
+};
+class Table_ident;
+class Foreign_key: public Key {
+public:
+ enum fk_match_opt { FK_MATCH_UNDEF, FK_MATCH_FULL,
+ FK_MATCH_PARTIAL, FK_MATCH_SIMPLE};
+ enum fk_option { FK_OPTION_UNDEF, FK_OPTION_RESTRICT, FK_OPTION_CASCADE,
+ FK_OPTION_SET_NULL, FK_OPTION_NO_ACTION, FK_OPTION_DEFAULT};
+ Table_ident *ref_table;
+ List<Key_part_spec> ref_columns;
+ uint delete_opt, update_opt, match_opt;
+ Foreign_key(const char *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),
+ ref_table(table), ref_columns(ref_cols),
+ delete_opt(delete_opt_arg), update_opt(update_opt_arg),
+ match_opt(match_opt_arg)
+ {}
+ Foreign_key(const Foreign_key &rhs, MEM_ROOT *mem_root);
+ virtual Key *clone(MEM_ROOT *mem_root) const
+ { return new (mem_root) Foreign_key(*this, mem_root); }
+};
+typedef struct st_mysql_lock
+{
+ TABLE **table;
+ uint table_count,lock_count;
+ THR_LOCK_DATA **locks;
+} MYSQL_LOCK;
+class LEX_COLUMN : public Sql_alloc
+{
+public:
+ String column;
+ uint rights;
+ LEX_COLUMN (const String& x,const uint& y ): column (x),rights (y) {}
+};
+#include "sql_lex.h"
+class Table_ident;
+class sql_exchange;
+class LEX_COLUMN;
+class sp_head;
+class sp_name;
+class sp_instr;
+class sp_pcontext;
+class st_alter_tablespace;
+class partition_info;
+class Event_parse_data;
+enum enum_sql_command {
+ SQLCOM_SELECT, SQLCOM_CREATE_TABLE, SQLCOM_CREATE_INDEX, SQLCOM_ALTER_TABLE,
+ SQLCOM_UPDATE, SQLCOM_INSERT, SQLCOM_INSERT_SELECT,
+ SQLCOM_DELETE, SQLCOM_TRUNCATE, SQLCOM_DROP_TABLE, SQLCOM_DROP_INDEX,
+ SQLCOM_SHOW_DATABASES, SQLCOM_SHOW_TABLES, SQLCOM_SHOW_FIELDS,
+ SQLCOM_SHOW_KEYS, SQLCOM_SHOW_VARIABLES, SQLCOM_SHOW_STATUS,
+ SQLCOM_SHOW_ENGINE_LOGS, SQLCOM_SHOW_ENGINE_STATUS, SQLCOM_SHOW_ENGINE_MUTEX,
+ SQLCOM_SHOW_PROCESSLIST, SQLCOM_SHOW_MASTER_STAT, SQLCOM_SHOW_SLAVE_STAT,
+ SQLCOM_SHOW_GRANTS, SQLCOM_SHOW_CREATE, SQLCOM_SHOW_CHARSETS,
+ SQLCOM_SHOW_COLLATIONS, SQLCOM_SHOW_CREATE_DB, SQLCOM_SHOW_TABLE_STATUS,
+ SQLCOM_SHOW_TRIGGERS,
+ SQLCOM_LOAD,SQLCOM_SET_OPTION,SQLCOM_LOCK_TABLES,SQLCOM_UNLOCK_TABLES,
+ SQLCOM_GRANT,
+ SQLCOM_CHANGE_DB, SQLCOM_CREATE_DB, SQLCOM_DROP_DB, SQLCOM_ALTER_DB,
+ SQLCOM_REPAIR, SQLCOM_REPLACE, SQLCOM_REPLACE_SELECT,
+ SQLCOM_CREATE_FUNCTION, SQLCOM_DROP_FUNCTION,
+ SQLCOM_REVOKE,SQLCOM_OPTIMIZE, SQLCOM_CHECK,
+ SQLCOM_ASSIGN_TO_KEYCACHE, SQLCOM_PRELOAD_KEYS,
+ SQLCOM_FLUSH, SQLCOM_KILL, SQLCOM_ANALYZE,
+ SQLCOM_ROLLBACK, SQLCOM_ROLLBACK_TO_SAVEPOINT,
+ SQLCOM_COMMIT, SQLCOM_SAVEPOINT, SQLCOM_RELEASE_SAVEPOINT,
+ SQLCOM_SLAVE_START, SQLCOM_SLAVE_STOP,
+ SQLCOM_BEGIN, SQLCOM_LOAD_MASTER_TABLE, SQLCOM_CHANGE_MASTER,
+ SQLCOM_RENAME_TABLE, SQLCOM_BACKUP_TABLE, SQLCOM_RESTORE_TABLE,
+ SQLCOM_RESET, SQLCOM_PURGE, SQLCOM_PURGE_BEFORE, SQLCOM_SHOW_BINLOGS,
+ SQLCOM_SHOW_OPEN_TABLES, SQLCOM_LOAD_MASTER_DATA,
+ 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_WARNS, SQLCOM_EMPTY_QUERY, SQLCOM_SHOW_ERRORS,
+ SQLCOM_SHOW_COLUMN_TYPES, SQLCOM_SHOW_STORAGE_ENGINES, SQLCOM_SHOW_PRIVILEGES,
+ SQLCOM_HELP, SQLCOM_CREATE_USER, SQLCOM_DROP_USER, SQLCOM_RENAME_USER,
+ SQLCOM_REVOKE_ALL, SQLCOM_CHECKSUM,
+ SQLCOM_CREATE_PROCEDURE, SQLCOM_CREATE_SPFUNCTION, SQLCOM_CALL,
+ SQLCOM_DROP_PROCEDURE, SQLCOM_ALTER_PROCEDURE,SQLCOM_ALTER_FUNCTION,
+ SQLCOM_SHOW_CREATE_PROC, SQLCOM_SHOW_CREATE_FUNC,
+ SQLCOM_SHOW_STATUS_PROC, SQLCOM_SHOW_STATUS_FUNC,
+ SQLCOM_PREPARE, SQLCOM_EXECUTE, SQLCOM_DEALLOCATE_PREPARE,
+ SQLCOM_CREATE_VIEW, SQLCOM_DROP_VIEW,
+ SQLCOM_CREATE_TRIGGER, SQLCOM_DROP_TRIGGER,
+ SQLCOM_XA_START, SQLCOM_XA_END, SQLCOM_XA_PREPARE,
+ SQLCOM_XA_COMMIT, SQLCOM_XA_ROLLBACK, SQLCOM_XA_RECOVER,
+ SQLCOM_SHOW_PROC_CODE, SQLCOM_SHOW_FUNC_CODE,
+ SQLCOM_ALTER_TABLESPACE,
+ SQLCOM_INSTALL_PLUGIN, SQLCOM_UNINSTALL_PLUGIN,
+ SQLCOM_SHOW_AUTHORS, SQLCOM_BINLOG_BASE64_EVENT,
+ SQLCOM_SHOW_PLUGINS,
+ SQLCOM_SHOW_CONTRIBUTORS,
+ SQLCOM_CREATE_SERVER, SQLCOM_DROP_SERVER, SQLCOM_ALTER_SERVER,
+ SQLCOM_CREATE_EVENT, SQLCOM_ALTER_EVENT, SQLCOM_DROP_EVENT,
+ SQLCOM_SHOW_CREATE_EVENT, SQLCOM_SHOW_EVENTS,
+ SQLCOM_SHOW_CREATE_TRIGGER,
+ SQLCOM_ALTER_DB_UPGRADE,
+ SQLCOM_SHOW_PROFILE, SQLCOM_SHOW_PROFILES,
+ SQLCOM_END
+};
+class Delayed_insert;
+class select_result;
+class Time_zone;
+struct system_variables
+{
+ ulong dynamic_variables_version;
+ char* dynamic_variables_ptr;
+ uint dynamic_variables_head;
+ uint dynamic_variables_size;
+ ulonglong myisam_max_extra_sort_file_size;
+ ulonglong myisam_max_sort_file_size;
+ ulonglong max_heap_table_size;
+ ulonglong tmp_table_size;
+ ulonglong long_query_time;
+ ha_rows select_limit;
+ ha_rows max_join_size;
+ ulong auto_increment_increment, auto_increment_offset;
+ ulong bulk_insert_buff_size;
+ ulong join_buff_size;
+ ulong max_allowed_packet;
+ ulong max_error_count;
+ ulong max_length_for_sort_data;
+ ulong max_sort_length;
+ ulong max_tmp_tables;
+ ulong max_insert_delayed_threads;
+ ulong min_examined_row_limit;
+ ulong multi_range_count;
+ ulong myisam_repair_threads;
+ ulong myisam_sort_buff_size;
+ ulong myisam_stats_method;
+ ulong net_buffer_length;
+ ulong net_interactive_timeout;
+ ulong net_read_timeout;
+ ulong net_retry_count;
+ ulong net_wait_timeout;
+ ulong net_write_timeout;
+ ulong optimizer_prune_level;
+ ulong optimizer_search_depth;
+ ulong preload_buff_size;
+ ulong profiling_history_size;
+ ulong query_cache_type;
+ ulong read_buff_size;
+ ulong read_rnd_buff_size;
+ ulong div_precincrement;
+ ulong sortbuff_size;
+ ulong thread_handling;
+ ulong tx_isolation;
+ ulong completion_type;
+ ulong sql_mode;
+ ulong max_sp_recursion_depth;
+ ulong updatable_views_with_limit;
+ ulong default_week_format;
+ ulong max_seeks_for_key;
+ ulong range_alloc_block_size;
+ ulong query_alloc_block_size;
+ ulong query_prealloc_size;
+ ulong trans_alloc_block_size;
+ ulong trans_prealloc_size;
+ ulong log_warnings;
+ ulong group_concat_max_len;
+ ulong ndb_autoincrement_prefetch_sz;
+ ulong ndb_index_stat_cache_entries;
+ ulong ndb_index_stat_update_freq;
+ ulong binlog_format;
+ my_thread_id pseudo_thread_id;
+ my_bool low_priority_updates;
+ my_bool new_mode;
+ 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_alter_table;
+ my_bool old_passwords;
+ plugin_ref table_plugin;
+ CHARSET_INFO *character_set_filesystem;
+ CHARSET_INFO *character_set_client;
+ CHARSET_INFO *character_set_results;
+ CHARSET_INFO *collation_server;
+ CHARSET_INFO *collation_database;
+ CHARSET_INFO *collation_connection;
+ MY_LOCALE *lc_time_names;
+ Time_zone *time_zone;
+ DATE_TIME_FORMAT *date_format;
+ DATE_TIME_FORMAT *datetime_format;
+ DATE_TIME_FORMAT *time_format;
+ my_bool sysdate_is_now;
+};
+typedef struct system_status_var
+{
+ ulonglong bytes_received;
+ ulonglong bytes_sent;
+ ulong com_other;
+ ulong com_stat[(uint) SQLCOM_END];
+ ulong created_tmp_disk_tables;
+ ulong created_tmp_tables;
+ ulong ha_commit_count;
+ ulong ha_delete_count;
+ ulong ha_read_first_count;
+ ulong ha_read_last_count;
+ ulong ha_read_key_count;
+ ulong ha_read_next_count;
+ ulong ha_read_prev_count;
+ ulong ha_read_rnd_count;
+ ulong ha_read_rnd_next_count;
+ ulong ha_rollback_count;
+ ulong ha_update_count;
+ ulong ha_write_count;
+ ulong ha_prepare_count;
+ ulong ha_discover_count;
+ ulong ha_savepoint_count;
+ ulong ha_savepoint_rollback_count;
+ 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;
+ ulong net_big_packet_count;
+ ulong opened_tables;
+ ulong opened_shares;
+ 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 long_query_count;
+ ulong filesort_merge_passes;
+ ulong filesort_range_count;
+ ulong filesort_rows;
+ ulong filesort_scan_count;
+ ulong com_stmt_prepare;
+ ulong com_stmt_reprepare;
+ ulong com_stmt_execute;
+ ulong com_stmt_send_long_data;
+ ulong com_stmt_fetch;
+ ulong com_stmt_reset;
+ ulong com_stmt_close;
+ double last_query_cost;
+} STATUS_VAR;
+void mark_transaction_to_rollback(THD *thd, In_C_you_should_use_my_bool_instead() all);
+#include "sql_acl.h"
+#include "slave.h"
+#include "log.h"
+#include "my_list.h"
+#include "rpl_filter.h"
+#include "mysql.h"
+#include "mysql_version.h"
+#include "mysql_com.h"
+#include "mysql_time.h"
+#include "my_list.h"
+extern unsigned int mysql_port;
+extern char *mysql_unix_port;
+typedef struct st_mysql_field {
+ char *name;
+ char *org_name;
+ char *table;
+ char *org_table;
+ char *db;
+ char *catalog;
+ char *def;
+ unsigned long length;
+ unsigned long max_length;
+ unsigned int name_length;
+ unsigned int org_name_length;
+ unsigned int table_length;
+ unsigned int org_table_length;
+ unsigned int db_length;
+ unsigned int catalog_length;
+ unsigned int def_length;
+ unsigned int flags;
+ unsigned int decimals;
+ unsigned int charsetnr;
+ enum enum_field_types type;
+ void *extension;
+} MYSQL_FIELD;
+typedef char **MYSQL_ROW;
+typedef unsigned int MYSQL_FIELD_OFFSET;
+#include "typelib.h"
+typedef struct st_mysql_rows {
+ struct st_mysql_rows *next;
+ MYSQL_ROW data;
+ unsigned long length;
+} MYSQL_ROWS;
+typedef MYSQL_ROWS *MYSQL_ROW_OFFSET;
+#include "my_alloc.h"
+typedef struct embedded_query_result EMBEDDED_QUERY_RESULT;
+typedef struct st_mysql_data {
+ MYSQL_ROWS *data;
+ struct embedded_query_result *embedded_info;
+ MEM_ROOT alloc;
+ my_ulonglong rows;
+ unsigned int fields;
+ void *extension;
+} MYSQL_DATA;
+enum mysql_option
+{
+ MYSQL_OPT_CONNECT_TIMEOUT, MYSQL_OPT_COMPRESS, MYSQL_OPT_NAMED_PIPE,
+ MYSQL_INIT_COMMAND, MYSQL_READ_DEFAULT_FILE, MYSQL_READ_DEFAULT_GROUP,
+ MYSQL_SET_CHARSET_DIR, MYSQL_SET_CHARSET_NAME, MYSQL_OPT_LOCAL_INFILE,
+ MYSQL_OPT_PROTOCOL, MYSQL_SHARED_MEMORY_BASE_NAME, MYSQL_OPT_READ_TIMEOUT,
+ MYSQL_OPT_WRITE_TIMEOUT, MYSQL_OPT_USE_RESULT,
+ MYSQL_OPT_USE_REMOTE_CONNECTION, MYSQL_OPT_USE_EMBEDDED_CONNECTION,
+ MYSQL_OPT_GUESS_CONNECTION, MYSQL_SET_CLIENT_IP, MYSQL_SECURE_AUTH,
+ MYSQL_REPORT_DATA_TRUNCATION, MYSQL_OPT_RECONNECT,
+ MYSQL_OPT_SSL_VERIFY_SERVER_CERT
+};
+struct st_mysql_options {
+ unsigned int connect_timeout, read_timeout, write_timeout;
+ unsigned int port, protocol;
+ unsigned long client_flag;
+ char *host,*user,*password,*unix_socket,*db;
+ struct st_dynamic_array *init_commands;
+ char *my_cnf_file,*my_cnf_group, *charset_dir, *charset_name;
+ char *ssl_key;
+ char *ssl_cert;
+ char *ssl_ca;
+ char *ssl_capath;
+ char *ssl_cipher;
+ char *shared_memory_base_name;
+ unsigned long max_allowed_packet;
+ my_bool use_ssl;
+ my_bool compress,named_pipe;
+ my_bool rpl_probe;
+ my_bool rpl_parse;
+ my_bool no_master_reads;
+ my_bool separate_thread;
+ enum mysql_option methods_to_use;
+ char *client_ip;
+ my_bool secure_auth;
+ my_bool report_data_truncation;
+ int (*local_infile_init)(void **, const char *, void *);
+ int (*local_infile_read)(void *, char *, unsigned int);
+ void (*local_infile_end)(void *);
+ int (*local_infile_error)(void *, char *, unsigned int);
+ void *local_infile_userdata;
+ void *extension;
+};
+enum mysql_status
+{
+ MYSQL_STATUS_READY,MYSQL_STATUS_GET_RESULT,MYSQL_STATUS_USE_RESULT
+};
+enum mysql_protocol_type
+{
+ MYSQL_PROTOCOL_DEFAULT, MYSQL_PROTOCOL_TCP, MYSQL_PROTOCOL_SOCKET,
+ MYSQL_PROTOCOL_PIPE, MYSQL_PROTOCOL_MEMORY
+};
+enum mysql_rpl_type
+{
+ MYSQL_RPL_MASTER, MYSQL_RPL_SLAVE, MYSQL_RPL_ADMIN
+};
+typedef struct character_set
+{
+ unsigned int number;
+ unsigned int state;
+ const char *csname;
+ const char *name;
+ const char *comment;
+ const char *dir;
+ unsigned int mbminlen;
+ unsigned int mbmaxlen;
+} MY_CHARSET_INFO;
+struct st_mysql_methods;
+struct st_mysql_stmt;
+typedef struct st_mysql
+{
+ NET net;
+ unsigned char *connector_fd;
+ char *host,*user,*passwd,*unix_socket,*server_version,*host_info;
+ char *info, *db;
+ struct charset_info_st *charset;
+ MYSQL_FIELD *fields;
+ MEM_ROOT field_alloc;
+ my_ulonglong affected_rows;
+ my_ulonglong insert_id;
+ my_ulonglong extra_info;
+ unsigned long thread_id;
+ unsigned long packet_length;
+ unsigned int port;
+ unsigned long client_flag,server_capabilities;
+ unsigned int protocol_version;
+ unsigned int field_count;
+ unsigned int server_status;
+ unsigned int server_language;
+ unsigned int warning_count;
+ struct st_mysql_options options;
+ enum mysql_status status;
+ my_bool free_me;
+ my_bool reconnect;
+ char scramble[20 +1];
+ my_bool rpl_pivot;
+ struct st_mysql* master, *next_slave;
+ struct st_mysql* last_used_slave;
+ struct st_mysql* last_used_con;
+ LIST *stmts;
+ const struct st_mysql_methods *methods;
+ void *thd;
+ my_bool *unbuffered_fetch_owner;
+ char *info_buffer;
+ void *extension;
+} MYSQL;
+typedef struct st_mysql_res {
+ my_ulonglong row_count;
+ MYSQL_FIELD *fields;
+ MYSQL_DATA *data;
+ MYSQL_ROWS *data_cursor;
+ unsigned long *lengths;
+ MYSQL *handle;
+ const struct st_mysql_methods *methods;
+ MYSQL_ROW row;
+ MYSQL_ROW current_row;
+ MEM_ROOT field_alloc;
+ unsigned int field_count, current_field;
+ my_bool eof;
+ my_bool unbuffered_fetch_cancelled;
+ void *extension;
+} MYSQL_RES;
+typedef struct st_mysql_manager
+{
+ NET net;
+ char *host, *user, *passwd;
+ char *net_buf, *net_buf_pos, *net_data_end;
+ unsigned int port;
+ int cmd_status;
+ int last_errno;
+ int net_buf_size;
+ my_bool free_me;
+ my_bool eof;
+ char last_error[256];
+ void *extension;
+} MYSQL_MANAGER;
+typedef struct st_mysql_parameters
+{
+ unsigned long *p_max_allowed_packet;
+ unsigned long *p_net_buffer_length;
+ void *extension;
+} MYSQL_PARAMETERS;
+int mysql_server_init(int argc, char **argv, char **groups);
+void mysql_server_end(void);
+MYSQL_PARAMETERS * mysql_get_parameters(void);
+my_bool mysql_thread_init(void);
+void mysql_thread_end(void);
+my_ulonglong mysql_num_rows(MYSQL_RES *res);
+unsigned int mysql_num_fields(MYSQL_RES *res);
+my_bool mysql_eof(MYSQL_RES *res);
+MYSQL_FIELD * mysql_fetch_field_direct(MYSQL_RES *res,
+ unsigned int fieldnr);
+MYSQL_FIELD * mysql_fetch_fields(MYSQL_RES *res);
+MYSQL_ROW_OFFSET mysql_row_tell(MYSQL_RES *res);
+MYSQL_FIELD_OFFSET mysql_field_tell(MYSQL_RES *res);
+unsigned int mysql_field_count(MYSQL *mysql);
+my_ulonglong mysql_affected_rows(MYSQL *mysql);
+my_ulonglong mysql_insert_id(MYSQL *mysql);
+unsigned int mysql_errno(MYSQL *mysql);
+const char * mysql_error(MYSQL *mysql);
+const char * mysql_sqlstate(MYSQL *mysql);
+unsigned int mysql_warning_count(MYSQL *mysql);
+const char * mysql_info(MYSQL *mysql);
+unsigned long mysql_thread_id(MYSQL *mysql);
+const char * mysql_character_set_name(MYSQL *mysql);
+int mysql_set_character_set(MYSQL *mysql, const char *csname);
+MYSQL * mysql_init(MYSQL *mysql);
+my_bool mysql_ssl_set(MYSQL *mysql, const char *key,
+ const char *cert, const char *ca,
+ const char *capath, const char *cipher);
+const char * mysql_get_ssl_cipher(MYSQL *mysql);
+my_bool mysql_change_user(MYSQL *mysql, const char *user,
+ const char *passwd, const char *db);
+MYSQL * mysql_real_connect(MYSQL *mysql, const char *host,
+ const char *user,
+ const char *passwd,
+ const char *db,
+ unsigned int port,
+ const char *unix_socket,
+ unsigned long clientflag);
+int mysql_select_db(MYSQL *mysql, const char *db);
+int mysql_query(MYSQL *mysql, const char *q);
+int mysql_send_query(MYSQL *mysql, const char *q,
+ unsigned long length);
+int mysql_real_query(MYSQL *mysql, const char *q,
+ unsigned long length);
+MYSQL_RES * mysql_store_result(MYSQL *mysql);
+MYSQL_RES * mysql_use_result(MYSQL *mysql);
+my_bool mysql_master_query(MYSQL *mysql, const char *q,
+ unsigned long length);
+my_bool mysql_master_send_query(MYSQL *mysql, const char *q,
+ unsigned long length);
+my_bool mysql_slave_query(MYSQL *mysql, const char *q,
+ unsigned long length);
+my_bool mysql_slave_send_query(MYSQL *mysql, const char *q,
+ unsigned long length);
+void mysql_get_character_set_info(MYSQL *mysql,
+ MY_CHARSET_INFO *charset);
+void
+mysql_set_local_infile_handler(MYSQL *mysql,
+ int (*local_infile_init)(void **, const char *,
+ void *),
+ int (*local_infile_read)(void *, char *,
+ unsigned int),
+ void (*local_infile_end)(void *),
+ int (*local_infile_error)(void *, char*,
+ unsigned int),
+ void *);
+void
+mysql_set_local_infile_default(MYSQL *mysql);
+void mysql_enable_rpl_parse(MYSQL* mysql);
+void mysql_disable_rpl_parse(MYSQL* mysql);
+int mysql_rpl_parse_enabled(MYSQL* mysql);
+void mysql_enable_reads_from_master(MYSQL* mysql);
+void mysql_disable_reads_from_master(MYSQL* mysql);
+my_bool mysql_reads_from_master_enabled(MYSQL* mysql);
+enum mysql_rpl_type mysql_rpl_query_type(const char* q, int len);
+my_bool mysql_rpl_probe(MYSQL* mysql);
+int mysql_set_master(MYSQL* mysql, const char* host,
+ unsigned int port,
+ const char* user,
+ const char* passwd);
+int mysql_add_slave(MYSQL* mysql, const char* host,
+ unsigned int port,
+ const char* user,
+ const char* passwd);
+int mysql_shutdown(MYSQL *mysql,
+ enum mysql_enum_shutdown_level
+ shutdown_level);
+int mysql_dump_debug_info(MYSQL *mysql);
+int mysql_refresh(MYSQL *mysql,
+ unsigned int refresh_options);
+int mysql_kill(MYSQL *mysql,unsigned long pid);
+int mysql_set_server_option(MYSQL *mysql,
+ enum enum_mysql_set_option
+ option);
+int mysql_ping(MYSQL *mysql);
+const char * mysql_stat(MYSQL *mysql);
+const char * mysql_get_server_info(MYSQL *mysql);
+const char * mysql_get_client_info(void);
+unsigned long mysql_get_client_version(void);
+const char * mysql_get_host_info(MYSQL *mysql);
+unsigned long mysql_get_server_version(MYSQL *mysql);
+unsigned int mysql_get_proto_info(MYSQL *mysql);
+MYSQL_RES * mysql_list_dbs(MYSQL *mysql,const char *wild);
+MYSQL_RES * mysql_list_tables(MYSQL *mysql,const char *wild);
+MYSQL_RES * mysql_list_processes(MYSQL *mysql);
+int mysql_options(MYSQL *mysql,enum mysql_option option,
+ const void *arg);
+void mysql_free_result(MYSQL_RES *result);
+void mysql_data_seek(MYSQL_RES *result,
+ my_ulonglong offset);
+MYSQL_ROW_OFFSET mysql_row_seek(MYSQL_RES *result,
+ MYSQL_ROW_OFFSET offset);
+MYSQL_FIELD_OFFSET mysql_field_seek(MYSQL_RES *result,
+ MYSQL_FIELD_OFFSET offset);
+MYSQL_ROW mysql_fetch_row(MYSQL_RES *result);
+unsigned long * mysql_fetch_lengths(MYSQL_RES *result);
+MYSQL_FIELD * mysql_fetch_field(MYSQL_RES *result);
+MYSQL_RES * mysql_list_fields(MYSQL *mysql, const char *table,
+ const char *wild);
+unsigned long mysql_escape_string(char *to,const char *from,
+ unsigned long from_length);
+unsigned long mysql_hex_string(char *to,const char *from,
+ unsigned long from_length);
+unsigned long mysql_real_escape_string(MYSQL *mysql,
+ char *to,const char *from,
+ unsigned long length);
+void mysql_debug(const char *debug);
+void myodbc_remove_escape(MYSQL *mysql,char *name);
+unsigned int mysql_thread_safe(void);
+my_bool mysql_embedded(void);
+MYSQL_MANAGER* mysql_manager_init(MYSQL_MANAGER* con);
+MYSQL_MANAGER* mysql_manager_connect(MYSQL_MANAGER* con,
+ const char* host,
+ const char* user,
+ const char* passwd,
+ unsigned int port);
+void mysql_manager_close(MYSQL_MANAGER* con);
+int mysql_manager_command(MYSQL_MANAGER* con,
+ const char* cmd, int cmd_len);
+int mysql_manager_fetch_line(MYSQL_MANAGER* con,
+ char* res_buf,
+ int res_buf_size);
+my_bool mysql_read_query_result(MYSQL *mysql);
+enum enum_mysql_stmt_state
+{
+ MYSQL_STMT_INIT_DONE= 1, MYSQL_STMT_PREPARE_DONE, MYSQL_STMT_EXECUTE_DONE,
+ MYSQL_STMT_FETCH_DONE
+};
+typedef struct st_mysql_bind
+{
+ unsigned long *length;
+ my_bool *is_null;
+ void *buffer;
+ my_bool *error;
+ unsigned char *row_ptr;
+ void (*store_param_func)(NET *net, struct st_mysql_bind *param);
+ void (*fetch_result)(struct st_mysql_bind *, MYSQL_FIELD *,
+ unsigned char **row);
+ void (*skip_result)(struct st_mysql_bind *, MYSQL_FIELD *,
+ unsigned char **row);
+ unsigned long buffer_length;
+ unsigned long offset;
+ unsigned long length_value;
+ unsigned int param_number;
+ unsigned int pack_length;
+ enum enum_field_types buffer_type;
+ my_bool error_value;
+ my_bool is_unsigned;
+ my_bool long_data_used;
+ my_bool is_null_value;
+ void *extension;
+} MYSQL_BIND;
+typedef struct st_mysql_stmt
+{
+ MEM_ROOT mem_root;
+ LIST list;
+ MYSQL *mysql;
+ MYSQL_BIND *params;
+ MYSQL_BIND *bind;
+ MYSQL_FIELD *fields;
+ MYSQL_DATA result;
+ MYSQL_ROWS *data_cursor;
+ int (*read_row_func)(struct st_mysql_stmt *stmt,
+ unsigned char **row);
+ my_ulonglong affected_rows;
+ my_ulonglong insert_id;
+ unsigned long stmt_id;
+ unsigned long flags;
+ unsigned long prefetch_rows;
+ unsigned int server_status;
+ unsigned int last_errno;
+ unsigned int param_count;
+ unsigned int field_count;
+ enum enum_mysql_stmt_state state;
+ char last_error[512];
+ char sqlstate[5 +1];
+ my_bool send_types_to_server;
+ my_bool bind_param_done;
+ unsigned char bind_result_done;
+ my_bool unbuffered_fetch_cancelled;
+ my_bool update_max_length;
+ void *extension;
+} MYSQL_STMT;
+enum enum_stmt_attr_type
+{
+ STMT_ATTR_UPDATE_MAX_LENGTH,
+ STMT_ATTR_CURSOR_TYPE,
+ STMT_ATTR_PREFETCH_ROWS
+};
+typedef struct st_mysql_methods
+{
+ my_bool (*read_query_result)(MYSQL *mysql);
+ my_bool (*advanced_command)(MYSQL *mysql,
+ enum enum_server_command command,
+ const unsigned char *header,
+ unsigned long header_length,
+ const unsigned char *arg,
+ unsigned long arg_length,
+ my_bool skip_check,
+ MYSQL_STMT *stmt);
+ MYSQL_DATA *(*read_rows)(MYSQL *mysql,MYSQL_FIELD *mysql_fields,
+ unsigned int fields);
+ MYSQL_RES * (*use_result)(MYSQL *mysql);
+ void (*fetch_lengths)(unsigned long *to,
+ MYSQL_ROW column, unsigned int field_count);
+ void (*flush_use_result)(MYSQL *mysql);
+ MYSQL_FIELD * (*list_fields)(MYSQL *mysql);
+ my_bool (*read_prepare_result)(MYSQL *mysql, MYSQL_STMT *stmt);
+ int (*stmt_execute)(MYSQL_STMT *stmt);
+ int (*read_binary_rows)(MYSQL_STMT *stmt);
+ int (*unbuffered_fetch)(MYSQL *mysql, char **row);
+ void (*free_embedded_thd)(MYSQL *mysql);
+ const char *(*read_statistics)(MYSQL *mysql);
+ my_bool (*next_result)(MYSQL *mysql);
+ int (*read_change_user_result)(MYSQL *mysql, char *buff, const char *passwd);
+ int (*read_rows_from_cursor)(MYSQL_STMT *stmt);
+} MYSQL_METHODS;
+MYSQL_STMT * mysql_stmt_init(MYSQL *mysql);
+int mysql_stmt_prepare(MYSQL_STMT *stmt, const char *query,
+ unsigned long length);
+int mysql_stmt_execute(MYSQL_STMT *stmt);
+int mysql_stmt_fetch(MYSQL_STMT *stmt);
+int mysql_stmt_fetch_column(MYSQL_STMT *stmt, MYSQL_BIND *bind_arg,
+ unsigned int column,
+ unsigned long offset);
+int mysql_stmt_store_result(MYSQL_STMT *stmt);
+unsigned long mysql_stmt_param_count(MYSQL_STMT * stmt);
+my_bool mysql_stmt_attr_set(MYSQL_STMT *stmt,
+ enum enum_stmt_attr_type attr_type,
+ const void *attr);
+my_bool mysql_stmt_attr_get(MYSQL_STMT *stmt,
+ enum enum_stmt_attr_type attr_type,
+ void *attr);
+my_bool mysql_stmt_bind_param(MYSQL_STMT * stmt, MYSQL_BIND * bnd);
+my_bool mysql_stmt_bind_result(MYSQL_STMT * stmt, MYSQL_BIND * bnd);
+my_bool mysql_stmt_close(MYSQL_STMT * stmt);
+my_bool mysql_stmt_reset(MYSQL_STMT * stmt);
+my_bool mysql_stmt_free_result(MYSQL_STMT *stmt);
+my_bool mysql_stmt_send_long_data(MYSQL_STMT *stmt,
+ unsigned int param_number,
+ const char *data,
+ unsigned long length);
+MYSQL_RES * mysql_stmt_result_metadata(MYSQL_STMT *stmt);
+MYSQL_RES * mysql_stmt_param_metadata(MYSQL_STMT *stmt);
+unsigned int mysql_stmt_errno(MYSQL_STMT * stmt);
+const char * mysql_stmt_error(MYSQL_STMT * stmt);
+const char * mysql_stmt_sqlstate(MYSQL_STMT * stmt);
+MYSQL_ROW_OFFSET mysql_stmt_row_seek(MYSQL_STMT *stmt,
+ MYSQL_ROW_OFFSET offset);
+MYSQL_ROW_OFFSET mysql_stmt_row_tell(MYSQL_STMT *stmt);
+void mysql_stmt_data_seek(MYSQL_STMT *stmt, my_ulonglong offset);
+my_ulonglong mysql_stmt_num_rows(MYSQL_STMT *stmt);
+my_ulonglong mysql_stmt_affected_rows(MYSQL_STMT *stmt);
+my_ulonglong mysql_stmt_insert_id(MYSQL_STMT *stmt);
+unsigned int mysql_stmt_field_count(MYSQL_STMT *stmt);
+my_bool mysql_commit(MYSQL * mysql);
+my_bool mysql_rollback(MYSQL * mysql);
+my_bool mysql_autocommit(MYSQL * mysql, my_bool auto_mode);
+my_bool mysql_more_results(MYSQL *mysql);
+int mysql_next_result(MYSQL *mysql);
+void mysql_close(MYSQL *sock);
+typedef struct st_table_rule_ent
+{
+ char* db;
+ char* tbl_name;
+ uint key_len;
+} TABLE_RULE_ENT;
+class Rpl_filter
+{
+public:
+ Rpl_filter();
+ ~Rpl_filter();
+ Rpl_filter(Rpl_filter const&);
+ Rpl_filter& operator=(Rpl_filter const&);
+ In_C_you_should_use_my_bool_instead() tables_ok(const char* db, TABLE_LIST* tables);
+ In_C_you_should_use_my_bool_instead() db_ok(const char* db);
+ In_C_you_should_use_my_bool_instead() db_ok_with_wild_table(const char *db);
+ In_C_you_should_use_my_bool_instead() is_on();
+ int add_do_table(const char* table_spec);
+ int add_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);
+ void add_db_rewrite(const char* from_db, const char* to_db);
+ void get_do_table(String* str);
+ void get_ignore_table(String* str);
+ void get_wild_do_table(String* str);
+ void get_wild_ignore_table(String* str);
+ const char* get_rewrite_db(const char* db, size_t *new_len);
+ I_List<i_string>* get_do_db();
+ I_List<i_string>* get_ignore_db();
+private:
+ In_C_you_should_use_my_bool_instead() table_rules_on;
+ void init_table_rule_hash(HASH* h, In_C_you_should_use_my_bool_instead()* h_inited);
+ void init_table_rule_array(DYNAMIC_ARRAY* a, In_C_you_should_use_my_bool_instead()* a_inited);
+ int add_table_rule(HASH* h, const char* table_spec);
+ int add_wild_table_rule(DYNAMIC_ARRAY* a, const char* table_spec);
+ void free_string_array(DYNAMIC_ARRAY *a);
+ void table_rule_ent_hash_to_str(String* s, HASH* h, In_C_you_should_use_my_bool_instead() inited);
+ void table_rule_ent_dynamic_array_to_str(String* s, DYNAMIC_ARRAY* a,
+ In_C_you_should_use_my_bool_instead() inited);
+ TABLE_RULE_ENT* find_wild(DYNAMIC_ARRAY *a, const char* key, int len);
+ HASH do_table;
+ HASH ignore_table;
+ DYNAMIC_ARRAY wild_do_table;
+ DYNAMIC_ARRAY wild_ignore_table;
+ In_C_you_should_use_my_bool_instead() do_table_inited;
+ In_C_you_should_use_my_bool_instead() ignore_table_inited;
+ In_C_you_should_use_my_bool_instead() wild_do_table_inited;
+ In_C_you_should_use_my_bool_instead() wild_ignore_table_inited;
+ I_List<i_string> do_db;
+ I_List<i_string> ignore_db;
+ I_List<i_string_pair> rewrite_db;
+};
+extern Rpl_filter *rpl_filter;
+extern Rpl_filter *binlog_filter;
+#include "rpl_tblmap.h"
+class Relay_log_info;
+class Master_info;
+extern ulong master_retry_count;
+extern MY_BITMAP slave_error_mask;
+extern In_C_you_should_use_my_bool_instead() use_slave_mask;
+extern char *slave_load_tmpdir;
+extern char *master_info_file, *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 ulonglong relay_log_space_limit;
+int init_slave();
+void init_slave_skip_errors(const char* arg);
+In_C_you_should_use_my_bool_instead() flush_relay_log_info(Relay_log_info* rli);
+int register_slave_on_master(MYSQL* mysql);
+int terminate_slave_threads(Master_info* mi, int thread_mask,
+ In_C_you_should_use_my_bool_instead() skip_lock = 0);
+int start_slave_threads(In_C_you_should_use_my_bool_instead() need_slave_mutex, In_C_you_should_use_my_bool_instead() wait_for_start,
+ Master_info* mi, const char* master_info_fname,
+ const char* slave_info_fname, int thread_mask);
+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,
+ In_C_you_should_use_my_bool_instead() high_priority);
+int mysql_table_dump(THD* thd, const char* db,
+ const char* tbl_name, int fd = -1);
+int fetch_master_table(THD* thd, const char* db_name, const char* table_name,
+ Master_info* mi, MYSQL* mysql, In_C_you_should_use_my_bool_instead() overwrite);
+In_C_you_should_use_my_bool_instead() show_master_info(THD* thd, Master_info* mi);
+In_C_you_should_use_my_bool_instead() show_binlog_info(THD* thd);
+In_C_you_should_use_my_bool_instead() rpl_master_has_bug(Relay_log_info *rli, uint bug_id, In_C_you_should_use_my_bool_instead() report=(1));
+In_C_you_should_use_my_bool_instead() rpl_master_erroneous_autoinc(THD* thd);
+const char *print_slave_db_safe(const char *db);
+int check_expected_error(THD* thd, Relay_log_info const *rli, int error_code);
+void skip_load_data_infile(NET* net);
+void end_slave();
+void clear_until_condition(Relay_log_info* rli);
+void clear_slave_error(Relay_log_info* rli);
+void end_relay_log_info(Relay_log_info* rli);
+void lock_slave_threads(Master_info* mi);
+void unlock_slave_threads(Master_info* mi);
+void init_thread_mask(int* mask,Master_info* mi,In_C_you_should_use_my_bool_instead() inverse);
+int init_relay_log_pos(Relay_log_info* rli,const char* log,ulonglong pos,
+ In_C_you_should_use_my_bool_instead() need_data_lock, const char** errmsg,
+ In_C_you_should_use_my_bool_instead() look_for_description_event);
+int purge_relay_logs(Relay_log_info* rli, THD *thd, In_C_you_should_use_my_bool_instead() just_reset,
+ const char** errmsg);
+void set_slave_thread_options(THD* thd);
+void set_slave_thread_default_charset(THD *thd, Relay_log_info const *rli);
+void rotate_relay_log(Master_info* mi);
+int apply_event_and_update_pos(Log_event* ev, THD* thd, Relay_log_info* rli,
+ In_C_you_should_use_my_bool_instead() skip);
+ void * handle_slave_io(void *arg);
+ void * handle_slave_sql(void *arg);
+extern In_C_you_should_use_my_bool_instead() volatile abort_loop;
+extern Master_info main_mi, *active_mi;
+extern LIST master_list;
+extern my_bool replicate_same_server_id;
+extern int disconnect_slave_event_count, abort_slave_event_count ;
+extern uint master_port, master_connect_retry, report_port;
+extern char * master_user, *master_password, *master_host;
+extern char *master_info_file, *relay_log_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;
+enum mysql_db_table_field
+{
+ MYSQL_DB_FIELD_HOST = 0,
+ MYSQL_DB_FIELD_DB,
+ MYSQL_DB_FIELD_USER,
+ MYSQL_DB_FIELD_SELECT_PRIV,
+ MYSQL_DB_FIELD_INSERT_PRIV,
+ MYSQL_DB_FIELD_UPDATE_PRIV,
+ MYSQL_DB_FIELD_DELETE_PRIV,
+ MYSQL_DB_FIELD_CREATE_PRIV,
+ MYSQL_DB_FIELD_DROP_PRIV,
+ MYSQL_DB_FIELD_GRANT_PRIV,
+ MYSQL_DB_FIELD_REFERENCES_PRIV,
+ MYSQL_DB_FIELD_INDEX_PRIV,
+ MYSQL_DB_FIELD_ALTER_PRIV,
+ MYSQL_DB_FIELD_CREATE_TMP_TABLE_PRIV,
+ MYSQL_DB_FIELD_LOCK_TABLES_PRIV,
+ MYSQL_DB_FIELD_CREATE_VIEW_PRIV,
+ MYSQL_DB_FIELD_SHOW_VIEW_PRIV,
+ MYSQL_DB_FIELD_CREATE_ROUTINE_PRIV,
+ MYSQL_DB_FIELD_ALTER_ROUTINE_PRIV,
+ MYSQL_DB_FIELD_EXECUTE_PRIV,
+ MYSQL_DB_FIELD_EVENT_PRIV,
+ MYSQL_DB_FIELD_TRIGGER_PRIV,
+ MYSQL_DB_FIELD_COUNT
+};
+extern TABLE_FIELD_W_TYPE mysql_db_table_fields[];
+extern time_t mysql_db_table_last_check;
+struct acl_host_and_ip
+{
+ char *hostname;
+ long ip,ip_mask;
+};
+class ACL_ACCESS {
+public:
+ ulong sort;
+ ulong access;
+};
+class ACL_HOST :public ACL_ACCESS
+{
+public:
+ acl_host_and_ip host;
+ char *db;
+};
+class ACL_USER :public ACL_ACCESS
+{
+public:
+ acl_host_and_ip host;
+ uint hostname_length;
+ USER_RESOURCES user_resource;
+ char *user;
+ uint8 salt[20 +1];
+ uint8 salt_len;
+ enum SSL_type ssl_type;
+ const char *ssl_cipher, *x509_issuer, *x509_subject;
+};
+class ACL_DB :public ACL_ACCESS
+{
+public:
+ acl_host_and_ip host;
+ char *user,*db;
+};
+In_C_you_should_use_my_bool_instead() hostname_requires_resolving(const char *hostname);
+my_bool acl_init(In_C_you_should_use_my_bool_instead() dont_read_acl_tables);
+my_bool acl_reload(THD *thd);
+void acl_free(In_C_you_should_use_my_bool_instead() end=0);
+ulong acl_get(const char *host, const char *ip,
+ const char *user, const char *db, my_bool db_is_pattern);
+int acl_getroot(THD *thd, USER_RESOURCES *mqh, const char *passwd,
+ uint passwd_len);
+In_C_you_should_use_my_bool_instead() acl_getroot_no_password(Security_context *sctx, char *user, char *host,
+ char *ip, char *db);
+In_C_you_should_use_my_bool_instead() acl_check_host(const char *host, const char *ip);
+int check_change_password(THD *thd, const char *host, const char *user,
+ char *password, uint password_len);
+In_C_you_should_use_my_bool_instead() change_password(THD *thd, const char *host, const char *user,
+ char *password);
+In_C_you_should_use_my_bool_instead() mysql_grant(THD *thd, const char *db, List <LEX_USER> &user_list,
+ ulong rights, In_C_you_should_use_my_bool_instead() revoke);
+int mysql_table_grant(THD *thd, TABLE_LIST *table, List <LEX_USER> &user_list,
+ List <LEX_COLUMN> &column_list, ulong rights,
+ In_C_you_should_use_my_bool_instead() revoke);
+In_C_you_should_use_my_bool_instead() mysql_routine_grant(THD *thd, TABLE_LIST *table, In_C_you_should_use_my_bool_instead() is_proc,
+ List <LEX_USER> &user_list, ulong rights,
+ In_C_you_should_use_my_bool_instead() revoke, In_C_you_should_use_my_bool_instead() no_error);
+my_bool grant_init();
+void grant_free(void);
+my_bool grant_reload(THD *thd);
+In_C_you_should_use_my_bool_instead() check_grant(THD *thd, ulong want_access, TABLE_LIST *tables,
+ uint show_command, uint number, In_C_you_should_use_my_bool_instead() dont_print_error);
+In_C_you_should_use_my_bool_instead() check_grant_column (THD *thd, GRANT_INFO *grant,
+ const char *db_name, const char *table_name,
+ const char *name, uint length, Security_context *sctx);
+In_C_you_should_use_my_bool_instead() check_column_grant_in_table_ref(THD *thd, TABLE_LIST * table_ref,
+ const char *name, uint length);
+In_C_you_should_use_my_bool_instead() check_grant_all_columns(THD *thd, ulong want_access,
+ Field_iterator_table_ref *fields);
+In_C_you_should_use_my_bool_instead() check_grant_routine(THD *thd, ulong want_access,
+ TABLE_LIST *procs, In_C_you_should_use_my_bool_instead() is_proc, In_C_you_should_use_my_bool_instead() no_error);
+In_C_you_should_use_my_bool_instead() check_grant_db(THD *thd,const char *db);
+ulong get_table_grant(THD *thd, TABLE_LIST *table);
+ulong get_column_grant(THD *thd, GRANT_INFO *grant,
+ const char *db_name, const char *table_name,
+ const char *field_name);
+In_C_you_should_use_my_bool_instead() mysql_show_grants(THD *thd, LEX_USER *user);
+void get_privilege_desc(char *to, uint max_length, ulong access);
+void get_mqh(const char *user, const char *host, USER_CONN *uc);
+In_C_you_should_use_my_bool_instead() mysql_create_user(THD *thd, List <LEX_USER> &list);
+In_C_you_should_use_my_bool_instead() mysql_drop_user(THD *thd, List <LEX_USER> &list);
+In_C_you_should_use_my_bool_instead() mysql_rename_user(THD *thd, List <LEX_USER> &list);
+In_C_you_should_use_my_bool_instead() mysql_revoke_all(THD *thd, List <LEX_USER> &list);
+void fill_effective_table_privileges(THD *thd, GRANT_INFO *grant,
+ const char *db, const char *table);
+In_C_you_should_use_my_bool_instead() sp_revoke_privileges(THD *thd, const char *sp_db, const char *sp_name,
+ In_C_you_should_use_my_bool_instead() is_proc);
+int sp_grant_privileges(THD *thd, const char *sp_db, const char *sp_name,
+ In_C_you_should_use_my_bool_instead() is_proc);
+In_C_you_should_use_my_bool_instead() check_routine_level_acl(THD *thd, const char *db, const char *name,
+ In_C_you_should_use_my_bool_instead() is_proc);
+In_C_you_should_use_my_bool_instead() is_acl_user(const char *host, const char *user);
+#include "tztime.h"
+class Time_zone: public Sql_alloc
+{
+public:
+ Time_zone() {}
+ virtual my_time_t TIME_to_gmt_sec(const MYSQL_TIME *t,
+ my_bool *in_dst_time_gap) const = 0;
+ virtual void gmt_sec_to_TIME(MYSQL_TIME *tmp, my_time_t t) const = 0;
+ virtual const String * get_name() const = 0;
+ virtual ~Time_zone() {};
+};
+extern Time_zone * my_tz_UTC;
+extern Time_zone * my_tz_SYSTEM;
+extern Time_zone * my_tz_OFFSET0;
+extern Time_zone * my_tz_find(THD *thd, const String *name);
+extern my_bool my_tz_init(THD *org_thd, const char *default_tzname, my_bool bootstrap);
+extern void my_tz_free();
+extern my_time_t sec_since_epoch_TIME(MYSQL_TIME *t);
+static const int MY_TZ_TABLES_COUNT= 4;
+In_C_you_should_use_my_bool_instead() check_global_access(THD *thd, ulong want_access);
+int get_quote_char_for_identifier(THD *thd, const char *name, uint length);
+void sql_perror(const char *message);
+In_C_you_should_use_my_bool_instead() fn_format_relative_to_data_home(char * to, const char *name,
+ const char *dir, const char *extension);
+extern uint mysql_data_home_len;
+extern char *mysql_data_home,server_version[60],
+ mysql_real_data_home[], mysql_unpacked_real_data_home[];
+extern CHARSET_INFO *character_set_filesystem;
+extern char reg_ext[20];
+extern uint reg_ext_length;
+extern ulong specialflag;
+extern uint lower_case_table_names;
+extern In_C_you_should_use_my_bool_instead() mysqld_embedded;
+extern my_bool opt_large_pages;
+extern uint opt_large_page_size;
+extern struct system_variables global_system_variables;
+uint strconvert(CHARSET_INFO *from_cs, const char *from,
+ CHARSET_INFO *to_cs, char *to, uint to_length, uint *errors);
+uint filename_to_tablename(const char *from, char *to, uint to_length);
+uint tablename_to_filename(const char *from, char *to, uint to_length);
diff --git a/sql/mysqld.cc b/sql/mysqld.cc
index f43bc9ea1a6..0213eea889b 100644
--- a/sql/mysqld.cc
+++ b/sql/mysqld.cc
@@ -1,4 +1,4 @@
-/* Copyright (C) 2000-2003 MySQL AB
+/* Copyright 2000-2008 MySQL AB, 2008 Sun Microsystems, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -16,42 +16,32 @@
#include "mysql_priv.h"
#include <m_ctype.h>
#include <my_dir.h>
+#include <my_bit.h>
#include "slave.h"
+#include "rpl_mi.h"
#include "sql_repl.h"
+#include "rpl_filter.h"
#include "repl_failsafe.h"
-#include "stacktrace.h"
+#include <my_stacktrace.h>
#include "mysqld_suffix.h"
#include "mysys_err.h"
-#ifdef HAVE_BERKELEY_DB
-#include "ha_berkeley.h"
-#endif
-#ifdef HAVE_INNOBASE_DB
-#include "ha_innodb.h"
-#endif
-#include "ha_myisam.h"
-#ifdef HAVE_NDBCLUSTER_DB
-#include "ha_ndbcluster.h"
-#endif
+#include "events.h"
+
+#include "../storage/myisam/ha_myisam.h"
+
+#include "rpl_injector.h"
+
#ifdef HAVE_SYS_PRCTL_H
#include <sys/prctl.h>
#endif
-#ifdef HAVE_INNOBASE_DB
-#define OPT_INNODB_DEFAULT 1
-#else
-#define OPT_INNODB_DEFAULT 0
-#endif
-#define OPT_BDB_DEFAULT 0
-#ifdef HAVE_NDBCLUSTER_DB
-#define OPT_NDBCLUSTER_DEFAULT 0
+#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
-#else
-#define OPT_NDBCLUSTER_DEFAULT 0
#endif
#ifndef DEFAULT_SKIP_THREAD_PRIORITY
@@ -66,10 +56,6 @@
#define mysqld_charset &my_charset_latin1
-#ifndef DBUG_OFF
-#define ONE_THREAD
-#endif
-
#ifdef HAVE_purify
#define IF_PURIFY(A,B) (A)
#else
@@ -113,9 +99,7 @@ extern "C" { // Because of SCO 3.2V4.2
#endif
#include <my_net.h>
-#if defined(OS2)
-# include <sys/un.h>
-#elif !defined(__WIN__)
+#if !defined(__WIN__)
# ifndef __NETWARE__
#include <sys/resource.h>
# endif /* __NETWARE__ */
@@ -150,6 +134,13 @@ extern "C" { // Because of SCO 3.2V4.2
#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>
@@ -175,7 +166,7 @@ static void registerwithneb();
static void getvolumename();
static void getvolumeID(BYTE *volumeName);
#endif /* __NETWARE__ */
-
+
#ifdef _AIX41
int initgroups(const char *,unsigned int);
@@ -187,10 +178,10 @@ int initgroups(const char *,unsigned int);
typedef fp_except fp_except_t;
#endif
- /* We can't handle floating point exceptions with threads, so disable
- this on freebsd
- */
-
+/**
+ We can't handle floating point exceptions with threads, so disable
+ this on freebsd.
+*/
inline void set_proper_floating_point_mode()
{
/* Don't fall for overflow, underflow,divide-by-zero or loss of precision */
@@ -236,9 +227,20 @@ extern "C" int gethostname(char *name, int namelen);
extern "C" sig_handler handle_segfault(int sig);
+#if defined(__linux__)
+#define ENABLE_TEMP_POOL 1
+#else
+#define ENABLE_TEMP_POOL 0
+#endif
+
/* Constants */
const char *show_comp_option_name[]= {"YES", "NO", "DISABLED"};
+/*
+ WARNING: When adding new SQL modes don't forget to update the
+ tables definitions that stores it's value.
+ (ie: mysql.event, mysql.proc)
+*/
static const char *sql_mode_names[]=
{
"REAL_AS_FLOAT", "PIPES_AS_CONCAT", "ANSI_QUOTES", "IGNORE_SPACE",
@@ -252,8 +254,10 @@ static const char *sql_mode_names[]=
"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,
@@ -286,8 +290,10 @@ static const unsigned int sql_mode_names_len[]=
/*TRADITIONAL*/ 11,
/*NO_AUTO_CREATE_USER*/ 19,
/*HIGH_NOT_PRECEDENCE*/ 19,
- /*NO_ENGINE_SUBSTITUTION*/ 22
+ /*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 };
@@ -300,6 +306,20 @@ static TYPELIB tc_heuristic_recover_typelib=
array_elements(tc_heuristic_recover_names)-1,"",
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 *first_keyword= "first", *binary_keyword= "BINARY";
const char *my_localhost= "localhost", *delayed_user= "DELAYED";
#if SIZEOF_OFF_T > 4 && defined(BIG_TABLES)
@@ -322,21 +342,24 @@ arg_cmp_func Arg_comparator::comparator_matrix[5][2] =
{&Arg_comparator::compare_row, &Arg_comparator::compare_e_row},
{&Arg_comparator::compare_decimal, &Arg_comparator::compare_e_decimal}};
-/* static variables */
+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};
-char opt_plugin_dir[FN_REFLEN];
-char *opt_plugin_dir_ptr;
+/* static variables */
+/* the default log output is log tables */
static bool lower_case_table_names_used= 0;
static bool volatile select_thread_in_use, signal_thread_in_use;
static bool volatile ready_to_exit;
static my_bool opt_debugging= 0, opt_external_locking= 0, opt_console= 0;
-static my_bool opt_bdb, opt_isam, opt_ndbcluster, opt_merge, opt_federated;
static my_bool opt_short_log_format= 0;
static uint kill_cached_threads, wake_thread;
static ulong killed_threads, thread_created;
static ulong max_used_connections;
-static ulong my_bind_addr; /* the address we bind to */
+static ulong my_bind_addr; /**< the address we bind to */
static volatile ulong cached_thread_count= 0;
static const char *sql_mode_str= "OFF";
static char *mysqld_user, *mysqld_chroot, *log_error_file_ptr;
@@ -345,34 +368,27 @@ static char *default_character_set_name;
static char *character_set_filesystem_name;
static char *lc_time_names_name;
static char *my_bind_addr_str;
-static char *default_collation_name;
+static char *default_collation_name;
+static char *default_storage_engine_str;
static char compiled_default_collation_name[]= MYSQL_DEFAULT_COLLATION_NAME;
-static char mysql_data_home_buff[2];
static I_List<THD> thread_cache;
-
-#ifndef EMBEDDED_LIBRARY
-static struct passwd *user_info;
-static pthread_t select_thread;
-static uint thr_kill_signal;
-#endif
+static double long_query_time;
static pthread_cond_t COND_thread_cache, COND_flush_thread_cache;
-#ifdef HAVE_BERKELEY_DB
-static my_bool opt_sync_bdb_logs;
-#endif
-
/* Global variables */
-bool opt_update_log, opt_bin_log;
-my_bool opt_log, opt_slow_log, opt_log_queries_not_using_indexes= 0;
+bool opt_update_log, opt_bin_log, opt_ignore_builtin_innodb= 0;
+my_bool opt_log, opt_slow_log;
+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;
my_bool opt_character_set_client_handshake= 1;
bool server_id_supplied = 0;
bool opt_endinfo, using_udf_functions;
my_bool locked_in_memory;
-bool opt_using_transactions, using_update_log;
+bool opt_using_transactions;
bool volatile abort_loop;
bool volatile shutdown_in_progress;
/**
@@ -384,33 +400,51 @@ bool volatile shutdown_in_progress;
*/
bool volatile grant_option;
-my_bool opt_skip_slave_start = 0; // If set, slave is not autostarted
+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_log_slave_updates= 0;
-my_bool opt_innodb;
bool slave_warning_issued = false;
-#ifdef HAVE_NDBCLUSTER_DB
+/*
+ Legacy global handlerton. These will be removed (please do not add more).
+*/
+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];
+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 opt_sync_frm, opt_allow_suspicious_udfs;
my_bool opt_secure_auth= 0;
char* opt_secure_file_priv= 0;
my_bool opt_log_slow_admin_statements= 0;
+my_bool opt_log_slow_slave_statements= 0;
my_bool lower_case_file_system= 0;
my_bool opt_large_pages= 0;
+my_bool opt_myisam_use_mmap= 0;
uint opt_large_page_size= 0;
my_bool opt_old_style_user_limits= 0, trust_function_creators= 0;
/*
@@ -422,8 +456,15 @@ volatile bool mqh_used = 0;
my_bool opt_noacl;
my_bool sp_automatic_privileges= 1;
+ulong opt_binlog_rows_event_max_size;
+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
-static bool calling_initgroups= FALSE; /* Used in SIGSEGV handler. */
+static bool calling_initgroups= FALSE; /**< Used in SIGSEGV handler. */
#endif
uint mysqld_port, test_flags, select_errors, dropping_tables, ha_open_options;
uint mysqld_port_timeout;
@@ -433,13 +474,17 @@ uint tc_heuristic_recover= 0;
uint volatile thread_count, thread_running;
ulonglong thd_startup_options;
ulong back_log, connect_timeout, concurrency, server_id;
-ulong table_cache_size, thread_stack, what_to_log;
+ulong table_cache_size, table_def_size;
+ulong what_to_log;
ulong query_buff_size, 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 thread_cache_size=0, binlog_cache_size=0, max_binlog_cache_size=0;
+ulong slave_exec_mode_options;
+const char *slave_exec_mode_str= "STRICT";
+ulong thread_cache_size=0, thread_pool_size= 0;
+ulong binlog_cache_size=0, max_binlog_cache_size=0;
ulong query_cache_size=0;
-ulong refresh_version, flush_version; /* Increments on each reload */
+ulong refresh_version; /* Increments on each reload */
query_id_t global_query_id;
ulong aborted_threads, aborted_connects;
ulong delayed_insert_timeout, delayed_insert_limit, delayed_queue_size;
@@ -449,12 +494,12 @@ ulong specialflag=0;
ulong binlog_cache_use= 0, binlog_cache_disk_use= 0;
ulong max_connections, max_connect_errors;
uint max_user_connections= 0;
-/*
+/**
Limit of the total number of prepared statements in the server.
Is necessary to protect the server against out-of-memory attacks.
*/
ulong max_prepared_stmt_count;
-/*
+/**
Current total number of prepared statements in the server. This number
is exact, and therefore may not be equal to the difference between
`com_stmt_prepare' and `com_stmt_close' (global status variables), as
@@ -469,8 +514,9 @@ ulong thread_id=1L,current_pid;
ulong slow_launch_threads = 0, sync_binlog_period;
ulong expire_logs_days = 0;
ulong rpl_recovery_rank=0;
+const char *log_output_str= "FILE";
-time_t server_start_time;
+time_t server_start_time, flush_status_time;
char mysql_home[FN_REFLEN], pidfile_name[FN_REFLEN], system_time_zone[30];
char *default_tz_name;
@@ -478,23 +524,26 @@ char log_error_file[FN_REFLEN], glob_hostname[FN_REFLEN];
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,
- mysql_unpacked_real_data_home[FN_REFLEN],
def_ft_boolean_syntax[sizeof(ft_boolean_syntax)];
+char mysql_unpacked_real_data_home[FN_REFLEN];
int mysql_unpacked_real_data_home_len;
-char *mysql_data_home= mysql_real_data_home;
+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];
+
+uint mysql_data_home_len;
+char mysql_data_home_buff[2], *mysql_data_home=mysql_real_data_home;
char server_version[SERVER_VERSION_LENGTH];
char *mysqld_unix_port, *opt_mysql_tmpdir;
-const char **errmesg; /* Error messages */
+const char **errmesg; /**< Error messages */
const char *myisam_recover_options_str="OFF";
const char *myisam_stats_method_str="nulls_unequal";
-/* name of reference on left espression in rewritten IN subquery */
+/** name of reference on left espression in rewritten IN subquery */
const char *in_left_expr_name= "<left expr>";
-/* name of additional condition */
+/** name of additional condition */
const char *in_additional_cond= "<IN COND>";
const char *in_having_cond= "<IN HAVING>";
@@ -507,17 +556,14 @@ Lt_creator lt_creator;
Ge_creator ge_creator;
Le_creator le_creator;
-
FILE *bootstrap_file;
int bootstrap_error;
FILE *stderror_file=0;
-I_List<i_string_pair> replicate_rewrite_db;
-I_List<i_string> replicate_do_db, replicate_ignore_db;
-// allow the user to tell us which db to replicate and which to ignore
-I_List<i_string> binlog_do_db, binlog_ignore_db;
I_List<THD> threads;
I_List<NAMED_LIST> key_caches;
+Rpl_filter* rpl_filter;
+Rpl_filter* binlog_filter;
struct system_variables global_system_variables;
struct system_variables max_system_variables;
@@ -532,10 +578,10 @@ CHARSET_INFO *character_set_filesystem;
MY_LOCALE *my_default_lc_time_names;
-SHOW_COMP_OPTION have_isam;
-SHOW_COMP_OPTION have_raid, have_ssl, have_symlink, have_query_cache;
-SHOW_COMP_OPTION have_geometry, have_rtree_keys, have_dlopen;
+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;
/* Thread specific variables */
@@ -547,8 +593,9 @@ pthread_mutex_t LOCK_mysql_create_db, LOCK_Acl, LOCK_open, LOCK_thread_count,
LOCK_delayed_insert, LOCK_delayed_status, LOCK_delayed_create,
LOCK_crypt, LOCK_bytes_sent, LOCK_bytes_received,
LOCK_global_system_variables,
- LOCK_user_conn, LOCK_slave_list, LOCK_active_mi;
-/*
+ LOCK_user_conn, LOCK_slave_list, LOCK_active_mi,
+ LOCK_connection_count;
+/**
The below lock protects access to two global server variables:
max_prepared_stmt_count and prepared_stmt_count. These variables
set the limit and hold the current total number of prepared statements
@@ -560,9 +607,14 @@ pthread_mutex_t LOCK_prepared_stmt_count;
pthread_mutex_t LOCK_des_key_file;
#endif
rw_lock_t LOCK_grant, LOCK_sys_init_connect, LOCK_sys_init_slave;
-pthread_cond_t COND_refresh,COND_thread_count, COND_global_read_lock;
+rw_lock_t LOCK_system_variables_hash;
+pthread_cond_t COND_refresh, COND_thread_count, COND_global_read_lock;
pthread_t signal_thread;
pthread_attr_t connection_attrib;
+pthread_mutex_t LOCK_server_started;
+pthread_cond_t COND_server_started;
+
+int mysqld_server_started= 0;
File_parser_dummy_hook file_parser_dummy_hook;
@@ -576,21 +628,32 @@ 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;
/* Static variables */
static bool kill_in_progress, segfaulted;
-static my_bool opt_do_pstack, opt_bootstrap, opt_myisam_log;
+#ifdef HAVE_STACK_TRACE_ON_SEGV
+static my_bool opt_do_pstack;
+#endif /* HAVE_STACK_TRACE_ON_SEGV */
+static my_bool opt_bootstrap, opt_myisam_log;
static int cleanup_done;
static ulong opt_specialflag, opt_myisam_block_size;
-static char *opt_logname, *opt_update_logname, *opt_binlog_index_name;
-static char *opt_slow_logname, *opt_tc_heuristic_recover;
+static char *opt_update_logname, *opt_binlog_index_name;
+static char *opt_tc_heuristic_recover;
static char *mysql_home_ptr, *pidfile_name_ptr;
+static int defaults_argc;
static char **defaults_argv;
static char *opt_bin_logname;
static my_socket unix_sock,ip_sock;
-struct rand_struct sql_rand; // used by sql_class.cc:THD::THD()
+struct rand_struct sql_rand; ///< used by sql_class.cc:THD::THD()
+
+#ifndef EMBEDDED_LIBRARY
+struct passwd *user_info;
+static pthread_t select_thread;
+static uint thr_kill_signal;
+#endif
/* OS specific variables */
@@ -608,7 +671,7 @@ static char **opt_argv;
static HANDLE hEventShutdown;
static char shutdown_event_name[40];
#include "nt_servc.h"
-static NTService Service; // Service object for WinNT
+static NTService Service; ///< Service object for WinNT
#endif /* EMBEDDED_LIBRARY */
#endif /* __WIN__ */
@@ -619,10 +682,6 @@ static SECURITY_DESCRIPTOR sdPipeDescriptor;
static HANDLE hPipe = INVALID_HANDLE_VALUE;
#endif
-#ifdef OS2
-pthread_cond_t eventShutdown;
-#endif
-
#ifndef EMBEDDED_LIBRARY
bool mysqld_embedded=0;
#else
@@ -648,6 +707,8 @@ my_bool opt_enable_shared_memory;
HANDLE smem_event_connect_request= 0;
#endif
+scheduler_functions thread_scheduler;
+
#define SSL_VARS_NOT_STATIC
#include "sslopt-vars.h"
#ifdef HAVE_OPENSSL
@@ -669,12 +730,18 @@ char *des_key_file;
struct st_VioSSLFd *ssl_acceptor_fd;
#endif /* HAVE_OPENSSL */
+/**
+ Number of currently active user connections. The variable is protected by
+ LOCK_connection_count.
+*/
+uint connection_count= 0;
/* Function declarations */
pthread_handler_t signal_hand(void *arg);
-static void mysql_init_variables(void);
-static void get_options(int argc,char **argv);
+static int mysql_init_variables(void);
+static void get_options(int *argc,char **argv);
+extern "C" my_bool mysqld_get_one_option(int, const struct my_option *, char *);
static void set_server_version(void);
static int init_thread_environment();
static char *get_relative_path(const char *path);
@@ -691,15 +758,19 @@ 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);
static void clean_up(bool print_message);
static int test_if_case_insensitive(const char *dir_name);
#ifndef EMBEDDED_LIBRARY
+static void usage(void);
static void start_signal_handler(void);
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();
#endif
@@ -719,18 +790,10 @@ static void close_connections(void)
kill_cached_threads++;
flush_thread_cache();
- /* kill flush thread */
- (void) pthread_mutex_lock(&LOCK_manager);
- if (manager_thread_in_use)
- {
- DBUG_PRINT("quit",("killing manager thread: 0x%lx",manager_thread));
- (void) pthread_cond_signal(&COND_manager);
- }
- (void) pthread_mutex_unlock(&LOCK_manager);
-
/* kill connection thread */
-#if !defined(__WIN__) && !defined(__EMX__) && !defined(OS2) && !defined(__NETWARE__)
- DBUG_PRINT("quit",("waiting for select thread: 0x%lx",select_thread));
+#if !defined(__WIN__) && !defined(__NETWARE__)
+ DBUG_PRINT("quit", ("waiting for select thread: 0x%lx",
+ (ulong) select_thread));
(void) pthread_mutex_lock(&LOCK_thread_count);
while (select_thread_in_use)
@@ -822,11 +885,12 @@ static void close_connections(void)
{
DBUG_PRINT("quit",("Informing thread %ld that it's time to die",
tmp->thread_id));
- /* We skip slave threads on this first loop through. */
+ /* We skip slave threads & scheduler on this first loop through. */
if (tmp->slave_thread)
continue;
tmp->killed= THD::KILL_CONNECTION;
+ thread_scheduler.post_kill_notification(tmp);
if (tmp->mysys_var)
{
tmp->mysys_var->abort=1;
@@ -842,6 +906,7 @@ static void close_connections(void)
}
(void) pthread_mutex_unlock(&LOCK_thread_count); // For unlink from list
+ Events::deinit();
end_slave();
if (thread_count)
@@ -959,8 +1024,6 @@ void kill_mysql(void)
*/
}
#endif
-#elif defined(OS2)
- pthread_cond_signal(&eventShutdown); // post semaphore
#elif defined(HAVE_PTHREAD_KILL)
if (pthread_kill(signal_thread, MYSQL_KILL_SIGNAL))
{
@@ -984,22 +1047,18 @@ void kill_mysql(void)
DBUG_VOID_RETURN;
}
-/*
- Force server down. Kill all connections and threads and exit
-
- SYNOPSIS
- kill_server
+/**
+ Force server down. Kill all connections and threads and exit.
- sig_ptr Signal number that caused kill_server to be called.
+ @param sig_ptr Signal number that caused kill_server to be called.
- NOTE!
+ @note
A signal number of 0 mean that the function was not called
from a signal handler and there is thus no signal to block
or stop, we just want to kill the server.
-
*/
-#if defined(OS2) || defined(__NETWARE__)
+#if defined(__NETWARE__)
extern "C" void kill_server(int sig_ptr)
#define RETURN_FROM_KILL_SERVER DBUG_VOID_RETURN
#elif !defined(__WIN__)
@@ -1025,21 +1084,18 @@ static void __cdecl kill_server(int sig_ptr)
else
sql_print_error(ER(ER_GOT_SIGNAL),my_progname,sig); /* purecov: inspected */
-#if defined(HAVE_SMEM) && defined(__WIN__)
- /*
- Send event to smem_event_connect_request for aborting
- */
- if (!SetEvent(smem_event_connect_request))
- {
+#if defined(HAVE_SMEM) && defined(__WIN__)
+ /*
+ Send event to smem_event_connect_request for aborting
+ */
+ if (!SetEvent(smem_event_connect_request))
+ {
DBUG_PRINT("error",
("Got error: %ld from SetEvent of smem_event_connect_request",
- GetLastError()));
+ GetLastError()));
}
-#endif
-
-#if defined(__NETWARE__) || (defined(USE_ONE_SIGNAL_HAND) && !defined(__WIN__) && !defined(OS2))
- my_thread_init(); // If this is a new thread
-#endif
+#endif
+
close_connections();
if (sig != MYSQL_KILL_SIGNAL &&
sig != 0)
@@ -1047,16 +1103,15 @@ static void __cdecl kill_server(int sig_ptr)
else
unireg_end();
+ /* purecov: begin deadcode */
#ifdef __NETWARE__
if (!event_flag)
pthread_join(select_thread, NULL); // wait for main thread
#endif /* __NETWARE__ */
-#if defined(__NETWARE__) || (defined(USE_ONE_SIGNAL_HAND) && !defined(__WIN__) && !defined(OS2))
my_thread_end();
-#endif
-
- pthread_exit(0); /* purecov: deadcode */
+ pthread_exit(0);
+ /* purecov: end */
#endif /* EMBEDDED_LIBRARY */
RETURN_FROM_KILL_SERVER;
@@ -1068,40 +1123,40 @@ pthread_handler_t kill_server_thread(void *arg __attribute__((unused)))
{
my_thread_init(); // Initialize new thread
kill_server(0);
- my_thread_end(); // Normally never reached
+ /* purecov: begin deadcode */
+ my_thread_end();
+ pthread_exit(0);
return 0;
+ /* purecov: end */
}
#endif
+
extern "C" sig_handler print_signal_warning(int sig)
{
if (global_system_variables.log_warnings)
- sql_print_warning("Got signal %d from thread %ld",
- sig, my_thread_id());
+ sql_print_warning("Got signal %d from thread %ld", sig,my_thread_id());
#ifdef DONT_REMEMBER_SIGNAL
my_sigset(sig,print_signal_warning); /* int. thread system calls */
#endif
-#if !defined(__WIN__) && !defined(OS2) && !defined(__NETWARE__)
+#if !defined(__WIN__) && !defined(__NETWARE__)
if (sig == SIGALRM)
alarm(2); /* reschedule alarm */
#endif
}
-/*
- cleanup all memory and end program nicely
-
- SYNOPSIS
- unireg_end()
+#ifndef EMBEDDED_LIBRARY
- NOTES
- This function never returns.
+/**
+ cleanup all memory and end program nicely.
If SIGNALS_DONT_BREAK_READ is defined, this function is called
by the main thread. To get MySQL to shut down nicely in this case
(Mac OS X) we have to call exit() instead if pthread_exit().
-*/
-#ifndef EMBEDDED_LIBRARY
+ @note
+ This function never returns.
+*/
void unireg_end(void)
{
clean_up(1);
@@ -1116,9 +1171,12 @@ void unireg_end(void)
extern "C" void unireg_abort(int exit_code)
{
DBUG_ENTER("unireg_abort");
+
if (exit_code)
sql_print_error("Aborting\n");
- clean_up(exit_code || !opt_bootstrap); /* purecov: inspected */
+ else if (opt_help)
+ usage();
+ clean_up(!opt_help && (exit_code || !opt_bootstrap)); /* purecov: inspected */
DBUG_PRINT("quit",("done with cleanup in unireg_abort"));
wait_for_signal_thread_to_end();
clean_up_mutexes();
@@ -1134,8 +1192,18 @@ void clean_up(bool print_message)
if (cleanup_done++)
return; /* purecov: inspected */
- mysql_log.cleanup();
- mysql_slow_log.cleanup();
+ stop_handle_manager();
+ release_ddl_log();
+
+ /*
+ make sure that handlers finish up
+ what they have that is dependent on the binlog
+ */
+ ha_binlog_end(current_thd);
+
+ logger.cleanup_base();
+
+ injector::free_instance();
mysql_bin_log.cleanup();
#ifdef HAVE_REPLICATION
@@ -1143,32 +1211,36 @@ void clean_up(bool print_message)
bitmap_free(&slave_error_mask);
#endif
my_tz_free();
- my_dbopt_free();
+ my_database_names_free();
#ifndef NO_EMBEDDED_ACCESS_CHECKS
+ servers_free(1);
acl_free(1);
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();
free_charsets();
-#ifdef HAVE_DLOPEN
if (!opt_noacl)
+ {
+#ifdef HAVE_DLOPEN
udf_free();
#endif
- (void) ha_panic(HA_PANIC_CLOSE); /* close all tables and logs */
+ }
+ plugin_shutdown();
+ ha_end();
if (tc_log)
tc_log->close();
xid_cache_free();
- delete_elements(&key_caches, (void (*)(const char*, gptr)) free_key_cache);
+ delete_elements(&key_caches, (void (*)(const char*, uchar*)) free_key_cache);
multi_keycache_free();
+ free_status_vars();
end_thr_alarm(1); /* Free allocated memory */
-#ifdef USE_RAID
- end_raid();
-#endif
my_free_open_file_info();
my_free((char*) global_system_variables.date_format,
MYF(MY_ALLOW_ZERO_PTR));
@@ -1180,6 +1252,8 @@ void clean_up(bool print_message)
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));
@@ -1191,42 +1265,37 @@ void clean_up(bool print_message)
free_max_user_conn();
#ifdef HAVE_REPLICATION
end_slave_list();
- free_list(&replicate_do_db);
- free_list(&replicate_ignore_db);
- free_list(&binlog_do_db);
- free_list(&binlog_ignore_db);
- free_list(&replicate_rewrite_db);
#endif
-#ifdef HAVE_OPENSSL
- if (ssl_acceptor_fd)
- {
- SSL_CTX_free(ssl_acceptor_fd->ssl_context);
- my_free((gptr) ssl_acceptor_fd, MYF(0));
- }
-#endif /* HAVE_OPENSSL */
+ delete binlog_filter;
+ delete rpl_filter;
+#ifndef EMBEDDED_LIBRARY
+ end_ssl();
+#endif
vio_end();
-
#ifdef USE_REGEX
my_regex_end();
#endif
- if (print_message && errmesg)
- sql_print_information(ER(ER_SHUTDOWN_COMPLETE),my_progname);
#if !defined(EMBEDDED_LIBRARY)
if (!opt_bootstrap)
(void) my_delete(pidfile_name,MYF(0)); // This may not always exist
#endif
+ if (print_message && errmesg && server_start_time)
+ sql_print_information(ER(ER_SHUTDOWN_COMPLETE),my_progname);
+ thread_scheduler.end();
finish_client_errs();
- my_free((gptr) my_error_unregister(ER_ERROR_FIRST, ER_ERROR_LAST),
+ my_free((uchar*) my_error_unregister(ER_ERROR_FIRST, ER_ERROR_LAST),
MYF(MY_WME | MY_FAE | MY_ALLOW_ZERO_PTR));
DBUG_PRINT("quit", ("Error messages freed"));
/* Tell main we are ready */
+ logger.cleanup_end();
(void) pthread_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);
+
/*
The following lines may never be executed as the main thread may have
killed us
@@ -1237,11 +1306,10 @@ void clean_up(bool print_message)
#ifndef EMBEDDED_LIBRARY
-/*
+/**
This is mainly needed when running with purify, but it's still nice to
- know that all child threads have died when mysqld exits
+ know that all child threads have died when mysqld exits.
*/
-
static void wait_for_signal_thread_to_end()
{
#ifndef __NETWARE__
@@ -1263,6 +1331,7 @@ static void wait_for_signal_thread_to_end()
static void 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);
@@ -1278,6 +1347,8 @@ static void clean_up_mutexes()
(void) pthread_mutex_destroy(&LOCK_bytes_sent);
(void) pthread_mutex_destroy(&LOCK_bytes_received);
(void) pthread_mutex_destroy(&LOCK_user_conn);
+ (void) pthread_mutex_destroy(&LOCK_connection_count);
+ Events::destroy_mutexes();
#ifdef HAVE_OPENSSL
(void) pthread_mutex_destroy(&LOCK_des_key_file);
#ifndef HAVE_YASSL
@@ -1294,6 +1365,7 @@ static void clean_up_mutexes()
(void) rwlock_destroy(&LOCK_sys_init_connect);
(void) rwlock_destroy(&LOCK_sys_init_slave);
(void) pthread_mutex_destroy(&LOCK_global_system_variables);
+ (void) rwlock_destroy(&LOCK_system_variables_hash);
(void) pthread_mutex_destroy(&LOCK_global_read_lock);
(void) pthread_mutex_destroy(&LOCK_uuid_generator);
(void) pthread_mutex_destroy(&LOCK_prepared_stmt_count);
@@ -1312,12 +1384,12 @@ static void clean_up_mutexes()
** Init IP and UNIX socket
****************************************************************************/
+#ifndef EMBEDDED_LIBRARY
static void set_ports()
{
char *env;
if (!mysqld_port && !opt_disable_networking)
{ // Get port if not from commandline
- struct servent *serv_ptr;
mysqld_port= MYSQL_PORT;
/*
@@ -1331,6 +1403,7 @@ static void set_ports()
*/
#if MYSQL_PORT_DEFAULT == 0
+ struct servent *serv_ptr;
if ((serv_ptr= getservbyname("mysql", "tcp")))
mysqld_port= ntohs((u_short) serv_ptr->s_port); /* purecov: inspected */
#endif
@@ -1349,12 +1422,11 @@ static void set_ports()
}
}
-#ifndef EMBEDDED_LIBRARY
/* Change to run as another user if started with --user */
static struct passwd *check_user(const char *user)
{
-#if !defined(__WIN__) && !defined(OS2) && !defined(__NETWARE__)
+#if !defined(__WIN__) && !defined(__NETWARE__)
struct passwd *tmp_user_info;
uid_t user_id= geteuid();
@@ -1370,7 +1442,7 @@ static struct passwd *check_user(const char *user)
global_system_variables.log_warnings)
sql_print_warning(
"One can only use the --user switch if running as root\n");
- /* purecov: end */
+ /* purecov: end */
}
return NULL;
}
@@ -1396,12 +1468,9 @@ static struct passwd *check_user(const char *user)
goto err;
if (!(tmp_user_info= getpwuid(atoi(user))))
goto err;
- else
- return tmp_user_info;
}
- else
- return tmp_user_info;
- /* purecov: end */
+ 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);
@@ -1422,7 +1491,7 @@ err:
static void set_user(const char *user, struct passwd *user_info_arg)
{
/* purecov: begin tested */
-#if !defined(__WIN__) && !defined(OS2) && !defined(__NETWARE__)
+#if !defined(__WIN__) && !defined(__NETWARE__)
DBUG_ASSERT(user_info_arg != 0);
#ifdef HAVE_INITGROUPS
/*
@@ -1446,13 +1515,13 @@ static void set_user(const char *user, struct passwd *user_info_arg)
unireg_abort(1);
}
#endif
- /* purecov: end */
+ /* purecov: end */
}
static void set_effective_user(struct passwd *user_info_arg)
{
-#if !defined(__WIN__) && !defined(OS2) && !defined(__NETWARE__)
+#if !defined(__WIN__) && !defined(__NETWARE__)
DBUG_ASSERT(user_info_arg != 0);
if (setregid((gid_t)-1, user_info_arg->pw_gid) == -1)
{
@@ -1468,11 +1537,10 @@ static void set_effective_user(struct passwd *user_info_arg)
}
-/* Change root user if started with --chroot */
-
+/** Change root user if started with @c --chroot . */
static void set_root(const char *path)
{
-#if !defined(__WIN__) && !defined(__EMX__) && !defined(OS2) && !defined(__NETWARE__)
+#if !defined(__WIN__) && !defined(__NETWARE__)
if (chroot(path) == -1)
{
sql_perror("chroot");
@@ -1496,6 +1564,9 @@ static void network_init(void)
DBUG_ENTER("network_init");
LINT_INIT(ret);
+ if (thread_scheduler.init())
+ unireg_abort(1); /* purecov: inspected */
+
set_ports();
if (mysqld_port != 0 && !opt_disable_networking && !opt_bootstrap)
@@ -1560,7 +1631,7 @@ static void network_init(void)
if (Service.IsNT() && mysqld_unix_port[0] && !opt_bootstrap &&
opt_enable_named_pipe)
{
-
+
pipe_name[sizeof(pipe_name)-1]= 0; /* Safety if too long string */
strxnmov(pipe_name, sizeof(pipe_name)-1, "\\\\.\\pipe\\",
mysqld_unix_port, NullS);
@@ -1654,19 +1725,16 @@ static void network_init(void)
#ifndef EMBEDDED_LIBRARY
-/*
- Close a connection
+/**
+ Close a connection.
- SYNOPSIS
- close_connection()
- thd Thread handle
- errcode Error code to print to console
- lock 1 if we have have to lock LOCK_thread_count
+ @param thd Thread handle
+ @param errcode Error code to print to console
+ @param lock 1 if we have have to lock LOCK_thread_count
- NOTES
+ @note
For the connection that is doing shutdown, this is called twice
*/
-
void close_connection(THD *thd, uint errcode, bool lock)
{
st_vio *vio;
@@ -1691,9 +1759,8 @@ void close_connection(THD *thd, uint errcode, bool lock)
#endif /* EMBEDDED_LIBRARY */
- /* Called when a thread is aborted */
- /* ARGSUSED */
-
+/** Called when a thread is aborted. */
+/* ARGSUSED */
extern "C" sig_handler end_thread_signal(int sig __attribute__((unused)))
{
THD *thd=current_thd;
@@ -1701,21 +1768,60 @@ extern "C" sig_handler end_thread_signal(int sig __attribute__((unused)))
if (thd && ! thd->bootstrap)
{
statistic_increment(killed_threads, &LOCK_status);
- end_thread(thd,0);
+ thread_scheduler.end_thread(thd,0); /* purecov: inspected */
}
DBUG_VOID_RETURN; /* purecov: deadcode */
}
-void end_thread(THD *thd, bool put_in_cache)
+/*
+ Unlink thd from global list of available connections and free thd
+
+ SYNOPSIS
+ unlink_thd()
+ thd Thread handler
+
+ NOTES
+ LOCK_thread_count is locked and left locked
+*/
+
+void unlink_thd(THD *thd)
{
- DBUG_ENTER("end_thread");
+ DBUG_ENTER("unlink_thd");
+ DBUG_PRINT("enter", ("thd: 0x%lx", (long) thd));
thd->cleanup();
+
+ pthread_mutex_lock(&LOCK_connection_count);
+ --connection_count;
+ pthread_mutex_unlock(&LOCK_connection_count);
+
(void) pthread_mutex_lock(&LOCK_thread_count);
thread_count--;
delete thd;
+ DBUG_VOID_RETURN;
+}
- if (put_in_cache && cached_thread_count < thread_cache_size &&
+
+/*
+ Store thread in cache for reuse by new connections
+
+ SYNOPSIS
+ cache_thread()
+
+ NOTES
+ LOCK_thread_count has to be locked
+
+ RETURN
+ 0 Thread was not put in cache
+ 1 Thread is to be reused by new connection.
+ (ie, caller should return, not abort with pthread_exit())
+*/
+
+
+static bool cache_thread()
+{
+ safe_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 */
@@ -1728,9 +1834,9 @@ void end_thread(THD *thd, bool put_in_cache)
pthread_cond_signal(&COND_flush_thread_cache);
if (wake_thread)
{
+ THD *thd;
wake_thread--;
- thd=thread_cache.get();
- thd->real_id=pthread_self();
+ thd= thread_cache.get();
thd->thread_stack= (char*) &thd; // For store_globals
(void) thd->store_globals();
/*
@@ -1739,26 +1845,51 @@ void end_thread(THD *thd, bool put_in_cache)
this thread for handling of new THD object/connection.
*/
thd->mysys_var->abort= 0;
- thd->thr_create_time= time(NULL);
+ thd->thr_create_utime= my_micro_time();
threads.append(thd);
- pthread_mutex_unlock(&LOCK_thread_count);
- DBUG_VOID_RETURN;
+ return(1);
}
}
+ return(0);
+}
+
+
+/*
+ End thread for the current connection
+
+ SYNOPSIS
+ one_thread_per_connection_end()
+ thd Thread handler
+ put_in_cache Store thread in cache, if there is room in it
+ Normally this is true in all cases except when we got
+ out of resources initializing the current thread
+
+ NOTES
+ If thread is cached, we will wait until thread is scheduled to be
+ reused and then we will return.
+ If thread is not cached, we end the thread.
+
+ RETURN
+ 0 Signal to handle_one_connection to reuse connection
+*/
+
+bool one_thread_per_connection_end(THD *thd, bool put_in_cache)
+{
+ DBUG_ENTER("one_thread_per_connection_end");
+ unlink_thd(thd);
+ if (put_in_cache)
+ put_in_cache= cache_thread();
+ pthread_mutex_unlock(&LOCK_thread_count);
+ if (put_in_cache)
+ DBUG_RETURN(0); // Thread is reused
- /* Tell main we are ready */
- (void) pthread_mutex_unlock(&LOCK_thread_count);
/* It's safe to broadcast outside a lock (COND... is not deleted here) */
DBUG_PRINT("signal", ("Broadcasting COND_thread_count"));
+ my_thread_end();
(void) pthread_cond_broadcast(&COND_thread_count);
-#ifdef ONE_THREAD
- if (!(test_flags & TEST_NO_THREADS)) // For debugging under Linux
-#endif
- {
- my_thread_end();
- pthread_exit(0);
- }
- DBUG_VOID_RETURN;
+
+ pthread_exit(0);
+ DBUG_RETURN(0); // Impossible
}
@@ -1776,13 +1907,13 @@ void flush_thread_cache()
}
-/*
- Aborts a thread nicely. Commes here on SIGPIPE
- TODO: One should have to fix that thr_alarm know about this
- thread too.
-*/
-
#ifdef THREAD_SPECIFIC_SIGPIPE
+/**
+ Aborts a thread nicely. Comes here on SIGPIPE.
+
+ @todo
+ One should have to fix that thr_alarm know about this thread too.
+*/
extern "C" sig_handler abort_thread(int sig __attribute__((unused)))
{
THD *thd=current_thd;
@@ -1793,13 +1924,13 @@ extern "C" sig_handler abort_thread(int sig __attribute__((unused)))
}
#endif
+
/******************************************************************************
Setup a signal thread with handles all signals.
Because Linux doesn't support schemas use a mutex to check that
the signal thread is ready before continuing
******************************************************************************/
-
#if defined(__WIN__)
@@ -1815,6 +1946,7 @@ extern "C" sig_handler abort_thread(int sig __attribute__((unused)))
static BOOL WINAPI console_event_handler( DWORD type )
{
DBUG_ENTER("console_event_handler");
+#ifndef EMBEDDED_LIBRARY
if(type == CTRL_C_EVENT)
{
/*
@@ -1823,12 +1955,15 @@ static BOOL WINAPI console_event_handler( DWORD type )
between main thread doing initialization and CTRL-C thread doing
cleanup, which can result into crash.
*/
+#ifndef EMBEDDED_LIBRARY
if(hEventShutdown)
kill_mysql();
else
+#endif
sql_print_warning("CTRL-C ignored during startup");
DBUG_RETURN(TRUE);
}
+#endif
DBUG_RETURN(FALSE);
}
@@ -1912,7 +2047,7 @@ LONG WINAPI my_unhandler_exception_filter(EXCEPTION_POINTERS *ex_pointers)
#endif /* DEBUG_UNHANDLED_EXCEPTION_FILTER */
__try
{
- set_exception_pointers(ex_pointers);
+ my_set_exception_pointers(ex_pointers);
handle_segfault(ex_pointers->ExceptionRecord->ExceptionCode);
}
__except(EXCEPTION_EXECUTE_HANDLER)
@@ -1959,20 +2094,24 @@ static void init_signals(void)
SetUnhandledExceptionFilter(my_unhandler_exception_filter);
}
+
static void start_signal_handler(void)
{
+#ifndef EMBEDDED_LIBRARY
// Save vm id of this process
if (!opt_bootstrap)
create_pid_file();
+#endif /* EMBEDDED_LIBRARY */
}
+
static void check_data_home(const char *path)
{}
#elif defined(__NETWARE__)
-// down server event callback
+/// down server event callback.
void mysql_down_server_cb(void *, void *)
{
event_flag= TRUE;
@@ -1980,7 +2119,7 @@ void mysql_down_server_cb(void *, void *)
}
-// destroy callback resources
+/// destroy callback resources.
void mysql_cb_destroy(void *)
{
UnRegisterEventNotification(eh); // cleanup down event notification
@@ -1992,7 +2131,7 @@ void mysql_cb_destroy(void *)
}
-// initialize callbacks
+/// initialize callbacks.
void mysql_cb_init()
{
// register for down server event
@@ -2015,8 +2154,7 @@ void mysql_cb_init()
}
-/* To get the name of the NetWare volume having MySQL data folder */
-
+/** To get the name of the NetWare volume having MySQL data folder. */
static void getvolumename()
{
char *p;
@@ -2030,15 +2168,15 @@ static void getvolumename()
}
-/*
- Registering with NEB for NSS Volume Deactivation event
+/**
+ 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));
@@ -2054,7 +2192,7 @@ static void registerwithneb()
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.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
@@ -2079,12 +2217,12 @@ static void registerwithneb()
Get the NSS volume ID of the MySQL Data volume.
Volume ID is stored in a global variable
*/
- getvolumeID((BYTE*) datavolname);
+ getvolumeID((BYTE*) datavolname);
}
-/*
- Callback for NSS Volume Deactivation event
+/**
+ Callback for NSS Volume Deactivation event.
*/
ulong neb_event_callback(struct EventBlock *eblock)
@@ -2116,12 +2254,11 @@ ulong neb_event_callback(struct EventBlock *eblock)
}
-/*
- Function to get NSS volume ID of the MySQL data
-*/
-
#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];
@@ -2144,7 +2281,7 @@ static void getvolumeID(BYTE *volumeName)
strxmov(path, (const char *) ADMIN_VOL_PATH, (const char *) volumeName,
NullS);
- if ((status= zOpen(rootKey, zNSS_TASK, zNSPACE_LONG|zMODE_UTF8,
+ 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);
@@ -2152,7 +2289,7 @@ static void getvolumeID(BYTE *volumeName)
}
getInfoMask= zGET_IDS | zGET_VOLUME_INFO ;
- if ((status= zGetInfo(fileKey, getInfoMask, sizeof(info),
+ if ((status= zGetInfo(fileKey, getInfoMask, sizeof(info),
zINFO_VERSION_A, &info)) != zOK)
{
consoleprintf("\nGetNSSVolumeProperties - Failed in zGetInfo, status: %d\n.", (int) status);
@@ -2196,10 +2333,10 @@ static void start_signal_handler(void)
}
-/*
- Warn if the data is on a Traditional volume
+/**
+ Warn if the data is on a Traditional volume.
- NOTE
+ @note
Already done by mysqld_safe
*/
@@ -2207,50 +2344,22 @@ static void check_data_home(const char *path)
{
}
-#elif defined(__EMX__)
-static void sig_reload(int signo)
-{
- // Flush everything
- bool not_used;
- reload_acl_and_cache((THD*) 0,REFRESH_LOG, (TABLE_LIST*) 0, &not_used);
- signal(signo, SIG_ACK);
-}
+#endif /*__WIN__ || __NETWARE */
-static void sig_kill(int signo)
-{
- if (!kill_in_progress)
- {
- abort_loop=1; // mark abort for threads
- kill_server((void*) signo);
- }
- signal(signo, SIG_ACK);
-}
+#ifdef HAVE_LINUXTHREADS
+#define UNSAFE_DEFAULT_LINUX_THREADS 200
+#endif
-static void init_signals(void)
+
+#if BACKTRACE_DEMANGLE
+#include <cxxabi.h>
+extern "C" char *my_demangle(const char *mangled_name, int *status)
{
- signal(SIGQUIT, sig_kill);
- signal(SIGKILL, sig_kill);
- signal(SIGTERM, sig_kill);
- signal(SIGINT, sig_kill);
- signal(SIGHUP, sig_reload); // Flush everything
- signal(SIGALRM, SIG_IGN);
- signal(SIGBREAK,SIG_IGN);
- signal_thread = pthread_self();
+ return abi::__cxa_demangle(mangled_name, NULL, NULL, status);
}
-
-
-static void start_signal_handler(void)
-{}
-
-static void check_data_home(const char *path)
-{}
-#endif /*__WIN__ || __NETWARE || __EMX__*/
-
-
-#ifdef HAVE_LINUXTHREADS
-#define UNSAFE_DEFAULT_LINUX_THREADS 200
#endif
+
extern "C" sig_handler handle_segfault(int sig)
{
time_t curr_time;
@@ -2271,7 +2380,7 @@ extern "C" sig_handler handle_segfault(int sig)
segfaulted = 1;
- curr_time= time(NULL);
+ curr_time= my_time(0);
localtime_r(&curr_time, &tm);
fprintf(stderr,"\
@@ -2290,14 +2399,15 @@ and this may fail.\n\n");
(ulong) dflt_key_cache->key_cache_mem_size);
fprintf(stderr, "read_buffer_size=%ld\n", (long) global_system_variables.read_buff_size);
fprintf(stderr, "max_used_connections=%lu\n", max_used_connections);
- fprintf(stderr, "max_connections=%lu\n", max_connections);
+ fprintf(stderr, "max_threads=%u\n", thread_scheduler.max_threads);
fprintf(stderr, "threads_connected=%u\n", thread_count);
fprintf(stderr, "It is possible that mysqld could use up to \n\
-key_buffer_size + (read_buffer_size + sort_buffer_size)*max_connections = %lu K\n\
+key_buffer_size + (read_buffer_size + sort_buffer_size)*max_threads = %lu K\n\
bytes of memory\n", ((ulong) dflt_key_cache->key_cache_mem_size +
(global_system_variables.read_buff_size +
global_system_variables.sortbuff_size) *
- max_connections)/ 1024);
+ thread_scheduler.max_threads +
+ max_connections * sizeof(THD)) / 1024);
fprintf(stderr, "Hope that's ok; if not, decrease some variables in the equation.\n\n");
#if defined(HAVE_LINUXTHREADS)
@@ -2315,20 +2425,39 @@ the thread stack. Please read http://dev.mysql.com/doc/mysql/en/linux.html\n\n",
#ifdef HAVE_STACKTRACE
if (!(test_flags & TEST_NO_STACKTRACE))
{
- fprintf(stderr,"thd=%p\n",thd);
+ fprintf(stderr,"thd: 0x%lx\n",(long) thd);
fprintf(stderr,"\
Attempting backtrace. You can use the following information to find out\n\
where mysqld died. If you see no messages after this, something went\n\
-terribly wrong...\n");
- print_stacktrace(thd ? (gptr) thd->thread_stack : (gptr) 0,
- thread_stack);
+terribly wrong...\n");
+ my_print_stacktrace(thd ? (uchar*) thd->thread_stack : NULL,
+ my_thread_stack_size);
}
if (thd)
{
+ const char *kreason= "UNKNOWN";
+ switch (thd->killed) {
+ case THD::NOT_KILLED:
+ kreason= "NOT_KILLED";
+ break;
+ case THD::KILL_BAD_DATA:
+ kreason= "KILL_BAD_DATA";
+ break;
+ case THD::KILL_CONNECTION:
+ kreason= "KILL_CONNECTION";
+ break;
+ case THD::KILL_QUERY:
+ kreason= "KILL_QUERY";
+ break;
+ case THD::KILLED_NO_VALUE:
+ kreason= "KILLED_NO_VALUE";
+ break;
+ }
fprintf(stderr, "Trying to get some variables.\n\
Some pointers may be invalid and cause the dump to abort...\n");
- safe_print_str("thd->query", thd->query, 1024);
+ my_safe_print_str("thd->query", thd->query, 1024);
fprintf(stderr, "thd->thread_id=%lu\n", (ulong) thd->thread_id);
+ fprintf(stderr, "thd->killed=%s\n", kreason);
}
fprintf(stderr, "\
The manual page at http://dev.mysql.com/doc/mysql/en/crashing.html contains\n\
@@ -2373,7 +2502,7 @@ bugs.\n");
{
fprintf(stderr, "Writing a core file\n");
fflush(stderr);
- write_core(sig);
+ my_write_core(sig);
}
#endif
@@ -2383,7 +2512,7 @@ bugs.\n");
#endif
}
-#if !defined(__WIN__) && !defined(__NETWARE__) && !defined(__EMX__)
+#if !defined(__WIN__) && !defined(__NETWARE__)
#ifndef SA_RESETHAND
#define SA_RESETHAND 0
#endif
@@ -2407,7 +2536,9 @@ static void init_signals(void)
sigemptyset(&sa.sa_mask);
sigprocmask(SIG_SETMASK,&sa.sa_mask,NULL);
- init_stacktrace();
+#ifdef HAVE_STACKTRACE
+ my_init_stacktrace();
+#endif
#if defined(__amiga__)
sa.sa_handler=(void(*)())handle_segfault;
#else
@@ -2485,9 +2616,9 @@ static void start_signal_handler(void)
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,thread_stack*2);
+ pthread_attr_setstacksize(&thr_attr,my_thread_stack_size*2);
#else
- pthread_attr_setstacksize(&thr_attr,thread_stack);
+ pthread_attr_setstacksize(&thr_attr,my_thread_stack_size);
#endif
#endif
@@ -2506,8 +2637,7 @@ static void start_signal_handler(void)
}
-/* This threads handles all signals and alarms */
-
+/** This threads handles all signals and alarms. */
/* ARGSUSED */
pthread_handler_t signal_hand(void *arg __attribute__((unused)))
{
@@ -2522,7 +2652,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(max_connections +
+ init_thr_alarm(thread_scheduler.max_threads +
global_system_variables.max_insert_delayed_threads + 10);
if (thd_lib_detected != THD_LIB_LT && (test_flags & TEST_SIGINT))
{
@@ -2589,6 +2719,9 @@ pthread_handler_t signal_hand(void *arg __attribute__((unused)))
#ifdef EXTRA_DEBUG
sql_print_information("Got signal %d to shutdown mysqld",sig);
#endif
+ /* switch to the old log message processing */
+ logger.set_handlers(LOG_FILE, opt_slow_log ? LOG_FILE:LOG_NONE,
+ opt_log ? LOG_FILE:LOG_NONE);
DBUG_PRINT("info",("Got signal: %d abort_loop: %d",sig,abort_loop));
if (!abort_loop)
{
@@ -2616,6 +2749,19 @@ pthread_handler_t signal_hand(void *arg __attribute__((unused)))
REFRESH_THREADS | REFRESH_HOSTS),
(TABLE_LIST*) 0, &not_used); // Flush logs
}
+ /* reenable logs after the options were reloaded */
+ if (log_output_options & LOG_NONE)
+ {
+ logger.set_handlers(LOG_FILE,
+ opt_slow_log ? LOG_TABLE : LOG_NONE,
+ opt_log ? LOG_TABLE : LOG_NONE);
+ }
+ else
+ {
+ logger.set_handlers(LOG_FILE,
+ opt_slow_log ? log_output_options : LOG_NONE,
+ opt_log ? log_output_options : LOG_NONE);
+ }
break;
#ifdef USE_ONE_SIGNAL_HAND
case THR_SERVER_ALARM:
@@ -2639,47 +2785,48 @@ static void check_data_home(const char *path)
#endif /* __WIN__*/
-/*
+/**
All global error messages are sent here where the first one is stored
- for the client
+ for the client.
*/
-
-
/* ARGSUSED */
-static int my_message_sql(uint error, const char *str, myf MyFlags)
+extern "C" int my_message_sql(uint error, const char *str, myf MyFlags);
+
+int my_message_sql(uint error, const char *str, myf MyFlags)
{
THD *thd;
DBUG_ENTER("my_message_sql");
DBUG_PRINT("error", ("error: %u message: '%s'", error, str));
+
+ DBUG_ASSERT(str != NULL);
/*
- Put here following assertion when situation with EE_* error codes
- will be fixed
+ 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);
+ error= ER_UNKNOWN_ERROR;
+ }
+
if ((thd= current_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,
+ if (thd->handle_error(error, str,
MYSQL_ERROR::WARN_LEVEL_ERROR))
DBUG_RETURN(0);
- if (thd->spcont &&
- thd->spcont->handle_error(error, MYSQL_ERROR::WARN_LEVEL_ERROR, thd))
- {
- DBUG_RETURN(0);
- }
-
- thd->query_error= 1; // needed to catch query errors during replication
-
- if (!thd->no_warnings_for_error)
- {
- thd->no_warnings_for_error= TRUE;
- push_warning(thd, MYSQL_ERROR::WARN_LEVEL_ERROR, error, str);
- thd->no_warnings_for_error= FALSE;
- }
+ thd->is_slave_error= 1; // needed to catch query errors during replication
/*
thd->lex->current_select == 0 if lex structure is not inited
@@ -2688,21 +2835,46 @@ static int my_message_sql(uint error, const char *str, myf MyFlags)
if (thd->lex->current_select &&
thd->lex->current_select->no_error && !thd->is_fatal_error)
{
- DBUG_PRINT("error", ("Error converted to warning: current_select: no_error %d fatal_error: %d",
- (thd->lex->current_select ?
- thd->lex->current_select->no_error : 0),
- (int) thd->is_fatal_error));
+ DBUG_PRINT("error",
+ ("Error converted to warning: current_select: no_error %d "
+ "fatal_error: %d",
+ (thd->lex->current_select ?
+ thd->lex->current_select->no_error : 0),
+ (int) thd->is_fatal_error));
}
else
{
- NET *net= &thd->net;
- net->report_error= 1;
- query_cache_abort(net);
- if (!net->last_error[0]) // Return only first message
+ if (! thd->main_da.is_error()) // Return only first message
{
- strmake(net->last_error, str, sizeof(net->last_error)-1);
- net->last_errno= error ? error : ER_UNKNOWN_ERROR;
+ 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);
+ }
+
+ 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 (!thd || MyFlags & ME_NOREFRESH)
@@ -2712,20 +2884,24 @@ static int my_message_sql(uint error, const char *str, myf MyFlags)
#ifndef EMBEDDED_LIBRARY
-static void *my_str_malloc_mysqld(size_t size)
+extern "C" void *my_str_malloc_mysqld(size_t size);
+extern "C" void my_str_free_mysqld(void *ptr);
+
+void *my_str_malloc_mysqld(size_t size)
{
return my_malloc(size, MYF(MY_FAE));
}
-static void my_str_free_mysqld(void *ptr)
+void my_str_free_mysqld(void *ptr)
{
- my_free((gptr)ptr, MYF(MY_FAE));
+ my_free((uchar*)ptr, MYF(MY_FAE));
}
#endif /* EMBEDDED_LIBRARY */
#ifdef __WIN__
+
pthread_handler_t handle_shutdown(void *arg)
{
MSG msg;
@@ -2741,59 +2917,33 @@ pthread_handler_t handle_shutdown(void *arg)
}
#endif
-
-#ifdef OS2
-pthread_handler_t handle_shutdown(void *arg)
-{
- my_thread_init();
-
- // wait semaphore
- pthread_cond_wait(&eventShutdown, NULL);
-
- // close semaphore and kill server
- pthread_cond_destroy(&eventShutdown);
-
- /*
- Exit main loop on main thread, so kill will be done from
- main thread (this is thread 2)
- */
- abort_loop = 1;
-
- // unblock select()
- so_cancel(ip_sock);
- so_cancel(unix_sock);
-
- return 0;
-}
-#endif
-
-
+#if !defined(EMBEDDED_LIBRARY)
static const char *load_default_groups[]= {
-#ifdef HAVE_NDBCLUSTER_DB
+#ifdef WITH_NDBCLUSTER_STORAGE_ENGINE
"mysql_cluster",
#endif
"mysqld","server", MYSQL_BASE_VERSION, 0, 0};
-#if defined(__WIN__) && !defined(EMBEDDED_LIBRARY)
+#if defined(__WIN__)
static const int load_default_groups_sz=
sizeof(load_default_groups)/sizeof(load_default_groups[0]);
#endif
+#endif /*!EMBEDDED_LIBRARY*/
-/*
- Initialize one of the global date/time format variables
+/**
+ Initialize one of the global date/time format variables.
- SYNOPSIS
- init_global_datetime_format()
- format_type What kind of format should be supported
- var_ptr Pointer to variable that should be updated
+ @param format_type What kind of format should be supported
+ @param var_ptr Pointer to variable that should be updated
- NOTES
+ @note
The default value is taken from either opt_date_time_formats[] or
the ISO format (ANSI SQL)
- RETURN
+ @retval
0 ok
+ @retval
1 error
*/
@@ -2813,7 +2963,7 @@ static bool init_global_datetime_format(timestamp_type format_type,
*/
opt_date_time_formats[format_type]= str;
}
- if (!(*var_ptr= date_time_format_make(format_type, str, (uint) strlen(str))))
+ if (!(*var_ptr= date_time_format_make(format_type, str, strlen(str))))
{
fprintf(stderr, "Wrong date/time format specifier: %s\n", str);
return 1;
@@ -2821,28 +2971,180 @@ static bool init_global_datetime_format(timestamp_type format_type,
return 0;
}
+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},
+ {"alter_function", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_ALTER_FUNCTION]), SHOW_LONG_STATUS},
+ {"alter_procedure", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_ALTER_PROCEDURE]), SHOW_LONG_STATUS},
+ {"alter_server", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_ALTER_SERVER]), SHOW_LONG_STATUS},
+ {"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},
+ {"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},
+ {"change_db", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_CHANGE_DB]), SHOW_LONG_STATUS},
+ {"change_master", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_CHANGE_MASTER]), SHOW_LONG_STATUS},
+ {"check", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_CHECK]), SHOW_LONG_STATUS},
+ {"checksum", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_CHECKSUM]), SHOW_LONG_STATUS},
+ {"commit", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_COMMIT]), SHOW_LONG_STATUS},
+ {"create_db", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_CREATE_DB]), SHOW_LONG_STATUS},
+ {"create_event", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_CREATE_EVENT]), SHOW_LONG_STATUS},
+ {"create_function", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_CREATE_SPFUNCTION]), SHOW_LONG_STATUS},
+ {"create_index", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_CREATE_INDEX]), SHOW_LONG_STATUS},
+ {"create_procedure", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_CREATE_PROCEDURE]), SHOW_LONG_STATUS},
+ {"create_server", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_CREATE_SERVER]), SHOW_LONG_STATUS},
+ {"create_table", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_CREATE_TABLE]), SHOW_LONG_STATUS},
+ {"create_trigger", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_CREATE_TRIGGER]), SHOW_LONG_STATUS},
+ {"create_udf", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_CREATE_FUNCTION]), SHOW_LONG_STATUS},
+ {"create_user", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_CREATE_USER]), SHOW_LONG_STATUS},
+ {"create_view", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_CREATE_VIEW]), SHOW_LONG_STATUS},
+ {"dealloc_sql", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_DEALLOCATE_PREPARE]), SHOW_LONG_STATUS},
+ {"delete", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_DELETE]), SHOW_LONG_STATUS},
+ {"delete_multi", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_DELETE_MULTI]), SHOW_LONG_STATUS},
+ {"do", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_DO]), SHOW_LONG_STATUS},
+ {"drop_db", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_DROP_DB]), SHOW_LONG_STATUS},
+ {"drop_event", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_DROP_EVENT]), SHOW_LONG_STATUS},
+ {"drop_function", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_DROP_FUNCTION]), SHOW_LONG_STATUS},
+ {"drop_index", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_DROP_INDEX]), SHOW_LONG_STATUS},
+ {"drop_procedure", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_DROP_PROCEDURE]), SHOW_LONG_STATUS},
+ {"drop_server", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_DROP_SERVER]), SHOW_LONG_STATUS},
+ {"drop_table", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_DROP_TABLE]), SHOW_LONG_STATUS},
+ {"drop_trigger", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_DROP_TRIGGER]), SHOW_LONG_STATUS},
+ {"drop_user", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_DROP_USER]), SHOW_LONG_STATUS},
+ {"drop_view", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_DROP_VIEW]), SHOW_LONG_STATUS},
+ {"empty_query", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_EMPTY_QUERY]), SHOW_LONG_STATUS},
+ {"execute_sql", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_EXECUTE]), SHOW_LONG_STATUS},
+ {"flush", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_FLUSH]), SHOW_LONG_STATUS},
+ {"grant", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_GRANT]), SHOW_LONG_STATUS},
+ {"ha_close", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_HA_CLOSE]), SHOW_LONG_STATUS},
+ {"ha_open", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_HA_OPEN]), SHOW_LONG_STATUS},
+ {"ha_read", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_HA_READ]), SHOW_LONG_STATUS},
+ {"help", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_HELP]), SHOW_LONG_STATUS},
+ {"insert", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_INSERT]), SHOW_LONG_STATUS},
+ {"insert_select", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_INSERT_SELECT]), SHOW_LONG_STATUS},
+ {"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},
+ {"prepare_sql", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_PREPARE]), SHOW_LONG_STATUS},
+ {"purge", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_PURGE]), SHOW_LONG_STATUS},
+ {"purge_before_date", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_PURGE_BEFORE]), SHOW_LONG_STATUS},
+ {"release_savepoint", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_RELEASE_SAVEPOINT]), SHOW_LONG_STATUS},
+ {"rename_table", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_RENAME_TABLE]), SHOW_LONG_STATUS},
+ {"rename_user", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_RENAME_USER]), SHOW_LONG_STATUS},
+ {"repair", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_REPAIR]), SHOW_LONG_STATUS},
+ {"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},
+ {"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},
+ {"rollback_to_savepoint",(char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_ROLLBACK_TO_SAVEPOINT]), SHOW_LONG_STATUS},
+ {"savepoint", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SAVEPOINT]), SHOW_LONG_STATUS},
+ {"select", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SELECT]), SHOW_LONG_STATUS},
+ {"set_option", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SET_OPTION]), SHOW_LONG_STATUS},
+ {"show_authors", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_AUTHORS]), SHOW_LONG_STATUS},
+ {"show_binlog_events", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_BINLOG_EVENTS]), SHOW_LONG_STATUS},
+ {"show_binlogs", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_BINLOGS]), SHOW_LONG_STATUS},
+ {"show_charsets", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_CHARSETS]), 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},
+ {"show_create_func", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_CREATE_FUNC]), SHOW_LONG_STATUS},
+ {"show_create_proc", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_CREATE_PROC]), SHOW_LONG_STATUS},
+ {"show_create_table", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_CREATE]), SHOW_LONG_STATUS},
+ {"show_create_trigger", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_CREATE_TRIGGER]), SHOW_LONG_STATUS},
+ {"show_databases", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_DATABASES]), SHOW_LONG_STATUS},
+ {"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_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_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},
+#ifndef DBUG_OFF
+ {"show_procedure_code", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_PROC_CODE]), SHOW_LONG_STATUS},
+#endif
+ {"show_procedure_status",(char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_STATUS_PROC]), SHOW_LONG_STATUS},
+ {"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_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},
+ {"show_storage_engines", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_STORAGE_ENGINES]), SHOW_LONG_STATUS},
+ {"show_table_status", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_TABLE_STATUS]), SHOW_LONG_STATUS},
+ {"show_tables", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_TABLES]), SHOW_LONG_STATUS},
+ {"show_triggers", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_TRIGGERS]), 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},
+ {"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},
+ {"stmt_execute", (char*) offsetof(STATUS_VAR, com_stmt_execute), SHOW_LONG_STATUS},
+ {"stmt_fetch", (char*) offsetof(STATUS_VAR, com_stmt_fetch), SHOW_LONG_STATUS},
+ {"stmt_prepare", (char*) offsetof(STATUS_VAR, com_stmt_prepare), SHOW_LONG_STATUS},
+ {"stmt_reprepare", (char*) offsetof(STATUS_VAR, com_stmt_reprepare), SHOW_LONG_STATUS},
+ {"stmt_reset", (char*) offsetof(STATUS_VAR, com_stmt_reset), SHOW_LONG_STATUS},
+ {"stmt_send_long_data", (char*) offsetof(STATUS_VAR, com_stmt_send_long_data), SHOW_LONG_STATUS},
+ {"truncate", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_TRUNCATE]), SHOW_LONG_STATUS},
+ {"uninstall_plugin", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_UNINSTALL_PLUGIN]), SHOW_LONG_STATUS},
+ {"unlock_tables", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_UNLOCK_TABLES]), SHOW_LONG_STATUS},
+ {"update", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_UPDATE]), SHOW_LONG_STATUS},
+ {"update_multi", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_UPDATE_MULTI]), SHOW_LONG_STATUS},
+ {"xa_commit", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_XA_COMMIT]),SHOW_LONG_STATUS},
+ {"xa_end", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_XA_END]),SHOW_LONG_STATUS},
+ {"xa_prepare", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_XA_PREPARE]),SHOW_LONG_STATUS},
+ {"xa_recover", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_XA_RECOVER]),SHOW_LONG_STATUS},
+ {"xa_rollback", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_XA_ROLLBACK]),SHOW_LONG_STATUS},
+ {"xa_start", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_XA_START]),SHOW_LONG_STATUS},
+ {NullS, NullS, SHOW_LONG}
+};
static int init_common_variables(const char *conf_file_name, int argc,
char **argv, const char **groups)
{
+ char buff[FN_REFLEN], *s;
umask(((~my_umask) & 0666));
my_decimal_set_zero(&decimal_zero); // set decimal_zero constant;
tzset(); // Set tzname
max_system_variables.pseudo_thread_id= (ulong)~0;
- server_start_time= time((time_t*) 0);
- if (init_thread_environment())
- return 1;
- mysql_init_variables();
-
-#ifdef OS2
+ server_start_time= flush_status_time= my_time(0);
+ rpl_filter= new Rpl_filter;
+ binlog_filter= new Rpl_filter;
+ if (!rpl_filter || !binlog_filter)
{
- // fix timezone for daylight saving
- struct tm *ts = localtime(&start_time);
- if (ts->tm_isdst > 0)
- _timezone -= 3600;
+ sql_perror("Could not allocate replication and binlog filters");
+ return 1;
}
-#endif
+
+ if (init_thread_environment() ||
+ mysql_init_variables())
+ return 1;
+
#ifdef HAVE_TZNAME
{
struct tm tm_tmp;
@@ -2861,13 +3163,11 @@ static int init_common_variables(const char *conf_file_name, int argc,
global_system_variables.time_zone= my_tz_SYSTEM;
/*
- Init mutexes for the global MYSQL_LOG objects.
+ 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
- global MYSQL_LOGs in their constructors, because then they would be inited
- before MY_INIT(). So we do it here.
+ global MYSQL_BIN_LOGs in their constructors, because then they would be
+ inited before MY_INIT(). So we do it here.
*/
- mysql_log.init_pthread_objects();
- mysql_slow_log.init_pthread_objects();
mysql_bin_log.init_pthread_objects();
if (gethostname(glob_hostname,sizeof(glob_hostname)) < 0)
@@ -2878,12 +3178,48 @@ static int init_common_variables(const char *conf_file_name, int argc,
strmake(pidfile_name, STRING_WITH_LEN("mysql"));
}
else
- strmake(pidfile_name, glob_hostname, sizeof(pidfile_name)-5);
+ strmake(pidfile_name, glob_hostname, sizeof(pidfile_name)-5);
strmov(fn_ext(pidfile_name),".pid"); // Add proper extension
+ /*
+ Add server status variables to the dynamic list of
+ status variables that is shown by SHOW STATUS.
+ Later, in plugin_init, and mysql_install_plugin
+ new entries could be added to that list.
+ */
+ if (add_status_vars(status_vars))
+ return 1; // an error was already reported
+
+#ifndef DBUG_OFF
+ /*
+ We have few debug-only commands in com_status_vars, only visible in debug
+ builds. for simplicity we enable the assert only in debug builds
+
+ There are 8 Com_ variables which don't have corresponding SQLCOM_ values:
+ (TODO strictly speaking they shouldn't be here, should not have Com_ prefix
+ that is. Perhaps Stmt_ ? Comstmt_ ? Prepstmt_ ?)
+
+ Com_admin_commands => com_other
+ Com_stmt_close => com_stmt_close
+ Com_stmt_execute => com_stmt_execute
+ Com_stmt_fetch => com_stmt_fetch
+ Com_stmt_prepare => com_stmt_prepare
+ Com_stmt_reprepare => com_stmt_reprepare
+ Com_stmt_reset => com_stmt_reset
+ Com_stmt_send_long_data => com_stmt_send_long_data
+
+ With this correction the number of Com_ variables (number of elements in
+ the array, excluding the last element - terminator) must match the number
+ of SQLCOM_ constants.
+ */
+ compile_time_assert(sizeof(com_status_vars)/sizeof(com_status_vars[0]) - 1 ==
+ SQLCOM_END + 8);
+#endif
+
load_defaults(conf_file_name, groups, &argc, &argv);
defaults_argv=argv;
- get_options(argc,argv);
+ defaults_argc=argc;
+ get_options(&defaults_argc, defaults_argv);
set_server_version();
DBUG_PRINT("info",("%s Ver %s for %s on %s\n",my_progname,
@@ -2895,10 +3231,6 @@ static int init_common_variables(const char *conf_file_name, int argc,
{
my_use_large_pages= 1;
my_large_page_size= opt_large_page_size;
-#ifdef HAVE_INNOBASE_DB
- innobase_use_large_pages= 1;
- innobase_large_page_size= opt_large_page_size;
-#endif
}
#endif /* HAVE_LARGE_PAGES */
@@ -2940,7 +3272,7 @@ static int init_common_variables(const char *conf_file_name, int argc,
*/
table_cache_size= (ulong) min(max((files-10-max_connections)/2,
TABLE_OPEN_CACHE_MIN),
- table_cache_size);
+ table_cache_size);
DBUG_PRINT("warning",
("Changed limits: max_open_files: %u max_connections: %ld table_cache: %ld",
files, max_connections, table_cache_size));
@@ -2958,8 +3290,15 @@ static int init_common_variables(const char *conf_file_name, int argc,
return 1;
init_client_errs();
lex_init();
+ if (item_create_init())
+ return 1;
item_init();
- set_var_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
my_regex_init(&my_charset_latin1);
@@ -3016,7 +3355,7 @@ static int init_common_variables(const char *conf_file_name, int argc,
global_system_variables.character_set_results= default_charset_info;
global_system_variables.character_set_client= default_charset_info;
- if (!(character_set_filesystem=
+ if (!(character_set_filesystem=
get_charset_by_csname(character_set_filesystem_name,
MY_CS_PRIMARY, MYF(MY_WME))))
return 1;
@@ -3039,14 +3378,40 @@ static int init_common_variables(const char *conf_file_name, int argc,
sys_init_slave.value_length= 0;
if ((sys_init_slave.value= opt_init_slave))
- sys_init_slave.value_length= (uint) strlen(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 && !(log_output_options & LOG_FILE) &&
+ !(log_output_options & LOG_NONE))
+ sql_print_warning("Although a path was specified for the "
+ "--log option, log tables are used. "
+ "To enable logging to files use the --log-output option.");
+
+ if (opt_slow_log && opt_slow_logname && !(log_output_options & LOG_FILE)
+ && !(log_output_options & LOG_NONE))
+ sql_print_warning("Although a path was specified for the "
+ "--log_slow_queries option, log tables are used. "
+ "To enable logging to files use the --log-output=file option.");
+
+ s= 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 : 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 (ENABLE_TEMP_POOL)
if (use_temp_pool && bitmap_init(&temp_pool,0,1024,1))
return 1;
- if (my_dbopt_init())
+#else
+ use_temp_pool= 0;
+#endif
+
+ if (my_database_names_init())
return 1;
/*
@@ -3104,8 +3469,9 @@ 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_open, NULL);
(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);
@@ -3120,16 +3486,18 @@ static int init_thread_environment()
(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_uuid_generator, MY_MUTEX_INIT_FAST);
+ (void) pthread_mutex_init(&LOCK_connection_count, MY_MUTEX_INIT_FAST);
#ifdef HAVE_OPENSSL
(void) pthread_mutex_init(&LOCK_des_key_file,MY_MUTEX_INIT_FAST);
#ifndef HAVE_YASSL
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);
+ (void) my_rwlock_init(&openssl_stdlocks[i].lock, NULL);
CRYPTO_set_dynlock_create_callback(openssl_dynlock_create);
CRYPTO_set_dynlock_destroy_callback(openssl_dynlock_destroy);
CRYPTO_set_dynlock_lock_callback(openssl_lock);
@@ -3150,7 +3518,12 @@ static int init_thread_environment()
(void) pthread_mutex_init(&LOCK_rpl_status, MY_MUTEX_INIT_FAST);
(void) pthread_cond_init(&COND_rpl_status, NULL);
#endif
+ (void) pthread_mutex_init(&LOCK_server_started, MY_MUTEX_INIT_FAST);
+ (void) pthread_cond_init(&COND_server_started,NULL);
sp_cache_init();
+#ifdef HAVE_EVENT_SCHEDULER
+ Events::init_mutexes();
+#endif
/* Parameter for threads created for connections */
(void) pthread_attr_init(&connection_attrib);
(void) pthread_attr_setdetachstate(&connection_attrib,
@@ -3171,20 +3544,20 @@ static int init_thread_environment()
#if defined(HAVE_OPENSSL) && !defined(HAVE_YASSL)
static unsigned long openssl_id_function()
-{
+{
return (unsigned long) pthread_self();
-}
+}
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);
return lock;
}
-static void openssl_dynlock_destroy(openssl_lock_t *lock, const char *file,
+static void openssl_dynlock_destroy(openssl_lock_t *lock, const char *file,
int line)
{
rwlock_destroy(&lock->lock);
@@ -3204,7 +3577,7 @@ static void openssl_lock_function(int mode, int n, const char *file, int line)
}
-static void openssl_lock(int mode, openssl_lock_t *lock, const char *file,
+static void openssl_lock(int mode, openssl_lock_t *lock, const char *file,
int line)
{
int err;
@@ -3229,7 +3602,7 @@ static void openssl_lock(int mode, openssl_lock_t *lock, const char *file,
sql_print_error("Fatal: OpenSSL interface problem (mode=0x%x)", mode);
abort();
}
- if (err)
+ if (err)
{
sql_print_error("Fatal: can't %s OpenSSL lock", what);
abort();
@@ -3266,12 +3639,29 @@ static void init_ssl()
#endif /* HAVE_OPENSSL */
}
+
+static void end_ssl()
+{
+#ifdef HAVE_OPENSSL
+ if (ssl_acceptor_fd)
+ {
+ free_vio_ssl_acceptor_fd(ssl_acceptor_fd);
+ ssl_acceptor_fd= 0;
+ }
+#endif /* HAVE_OPENSSL */
+}
+
#endif /* EMBEDDED_LIBRARY */
+
static int init_server_components()
{
DBUG_ENTER("init_server_components");
- if (table_cache_init() || hostname_cache_init())
+ /*
+ 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())
unireg_abort(1);
query_cache_result_size_limit(query_cache_limit);
@@ -3284,9 +3674,40 @@ static int init_server_components()
#ifdef HAVE_REPLICATION
init_slave_list();
#endif
- /* Setup log files */
- if (opt_log)
- mysql_log.open_query_log(opt_logname);
+
+ /* Setup logs */
+
+ /*
+ Enable old-fashioned error log, except when the user has requested
+ help information. Since the implementation of plugin server
+ variables the help output is now written much later.
+ */
+ if (opt_error_log && !opt_help)
+ {
+ if (!log_error_file_ptr[0])
+ fn_format(log_error_file, pidfile_name, mysql_data_home, ".err",
+ MY_REPLACE_EXT); /* replace '.<domain>' by '.err', bug#4997 */
+ else
+ fn_format(log_error_file, log_error_file_ptr, mysql_data_home, ".err",
+ MY_UNPACK_FILENAME | MY_SAFE_PATH);
+ if (!log_error_file[0])
+ opt_error_log= 1; // Too long file name
+ else
+ {
+#ifndef EMBEDDED_LIBRARY
+ if (freopen(log_error_file, "a+", stdout))
+#endif
+ freopen(log_error_file, "a+", stderr);
+ }
+ }
+
+ if (xid_cache_init())
+ {
+ sql_print_error("Out of memory");
+ unireg_abort(1);
+ }
+
+ /* need to configure logging before initializing storage engines */
if (opt_update_log)
{
/*
@@ -3328,7 +3749,10 @@ version 5.0 and above. It is replaced by the binary log.");
{
/* as opt_bin_log==0, no need to free opt_bin_logname */
if (!(opt_bin_logname= my_strdup(opt_update_logname, MYF(MY_WME))))
- exit(EXIT_OUT_OF_MEMORY);
+ {
+ 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);
@@ -3341,13 +3765,34 @@ with --log-bin instead.");
}
if (opt_log_slave_updates && !opt_bin_log)
{
- sql_print_warning("You need to use --log-bin to make "
- "--log-slave-updates work.");
+ sql_print_error("You need to use --log-bin to make "
+ "--log-slave-updates work.");
+ unireg_abort(1);
+ }
+ if (!opt_bin_log)
+ {
+ 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;
+ }
}
+ 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);
+ }
- if (opt_slow_log)
- mysql_slow_log.open_slow_log(opt_slow_logname);
+ /* Check that we have not let the format to unspecified at this point */
+ DBUG_ASSERT((uint)global_system_variables.binlog_format <=
+ array_elements(binlog_format_names)-1);
#ifdef HAVE_REPLICATION
if (opt_log_slave_updates && replicate_same_server_id)
@@ -3360,25 +3805,6 @@ server.");
}
#endif
- if (opt_error_log)
- {
- if (!log_error_file_ptr[0])
- fn_format(log_error_file, pidfile_name, mysql_data_home, ".err",
- MY_REPLACE_EXT); /* replace '.<domain>' by '.err', bug#4997 */
- else
- fn_format(log_error_file, log_error_file_ptr, mysql_data_home, ".err",
- MY_UNPACK_FILENAME | MY_SAFE_PATH);
- if (!log_error_file[0])
- opt_error_log= 1; // Too long file name
- else
- {
-#ifndef EMBEDDED_LIBRARY
- if (freopen(log_error_file, "a+", stdout))
-#endif
- stderror_file= freopen(log_error_file, "a+", stderr);
- }
- }
-
if (opt_bin_log)
{
char buf[FN_REFLEN];
@@ -3408,39 +3834,143 @@ server.");
{
unireg_abort(1);
}
+ }
+
+ /* call ha_init_key_cache() on all key caches to init them */
+ process_key_caches(&ha_init_key_cache);
+
+ /* Allow storage engine to give real error messages */
+ 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);
+ }
+
+ if (opt_help)
+ unireg_abort(0);
+ /* we do want to exit if there are any other unknown options */
+ if (defaults_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}
+ };
/*
- Used to specify which type of lock we need to use for queries of type
- INSERT ... SELECT. This will change when we have row level logging.
+ 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.
*/
- using_update_log=1;
+ my_getopt_skip_unknown= 0;
+
+ if ((ho_error= handle_options(&defaults_argc, &tmp_argv, no_opts,
+ mysqld_get_one_option)))
+ unireg_abort(ho_error);
+
+ if (defaults_argc)
+ {
+ fprintf(stderr, "%s: Too many arguments (first extra is '%s').\n"
+ "Use --verbose --help to get a list of available options\n",
+ my_progname, *tmp_argv);
+ unireg_abort(1);
+ }
}
- if (xid_cache_init())
- {
- sql_print_error("Out of memory");
+ /* if the errmsg.sys is not loaded, terminate to maintain behaviour */
+ if (!errmesg[0][0])
unireg_abort(1);
- }
+
+ /* We have to initialize the storage engines before CSV logging */
if (ha_init())
{
sql_print_error("Can't init databases");
unireg_abort(1);
}
+#ifdef WITH_CSV_STORAGE_ENGINE
+ if (opt_bootstrap)
+ log_output_options= LOG_FILE;
+ else
+ logger.init_log_tables();
+
+ if (log_output_options & LOG_NONE)
+ {
+ /*
+ Issue a warining if there were specified additional options to the
+ log-output along with NONE. Probably this wasn't what user wanted.
+ */
+ if ((log_output_options & LOG_NONE) && (log_output_options & ~LOG_NONE))
+ sql_print_warning("There were other values specified to "
+ "log-output besides NONE. Disabling slow "
+ "and general logs anyway.");
+ logger.set_handlers(LOG_FILE, LOG_NONE, LOG_NONE);
+ }
+ else
+ {
+ /* fall back to the log files if tables are not present */
+ LEX_STRING csv_name={C_STRING_WITH_LEN("csv")};
+ if (!plugin_is_ready(&csv_name, MYSQL_STORAGE_ENGINE_PLUGIN))
+ {
+ /* purecov: begin inspected */
+ sql_print_error("CSV engine is not present, falling back to the "
+ "log files");
+ log_output_options= (log_output_options & ~LOG_TABLE) | LOG_FILE;
+ /* purecov: end */
+ }
+
+ logger.set_handlers(LOG_FILE, opt_slow_log ? log_output_options:LOG_NONE,
+ opt_log ? log_output_options:LOG_NONE);
+ }
+#else
+ logger.set_handlers(LOG_FILE, opt_slow_log ? LOG_FILE:LOG_NONE,
+ opt_log ? LOG_FILE:LOG_NONE);
+#endif
+
/*
Check that the default storage engine is actually available.
*/
- if (!ha_storage_engine_is_enabled((enum db_type)
- global_system_variables.table_type))
- {
- if (!opt_bootstrap)
+ if (default_storage_engine_str)
+ {
+ 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("Default storage engine (%s) is not available",
- ha_get_storage_engine((enum db_type)
- global_system_variables.table_type));
+ sql_print_error("Unknown/unsupported table type: %s",
+ default_storage_engine_str);
unireg_abort(1);
}
- global_system_variables.table_type= DB_TYPE_MYISAM;
+ 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()
+ */
+ plugin_unlock(0, global_system_variables.table_plugin);
+ global_system_variables.table_plugin= plugin;
+ }
}
tc_log= (total_ha_2pc > 1 ? (opt_bin_log ?
@@ -3466,18 +3996,19 @@ server.");
#ifdef HAVE_REPLICATION
if (opt_bin_log && expire_logs_days)
{
- time_t purge_time= time(0) - expire_logs_days*24*60*60;
+ time_t purge_time= server_start_time - expire_logs_days*24*60*60;
if (purge_time >= 0)
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);
- /* call ha_init_key_cache() on all key caches to init them */
- process_key_caches(&ha_init_key_cache);
-
#if defined(HAVE_MLOCKALL) && defined(MCL_CURRENT) && !defined(EMBEDDED_LIBRARY)
if (locked_in_memory && !getuid())
{
@@ -3508,20 +4039,6 @@ server.");
#ifndef EMBEDDED_LIBRARY
-static void create_maintenance_thread()
-{
- if (
-#ifdef HAVE_BERKELEY_DB
- (have_berkeley_db == SHOW_OPTION_YES) ||
-#endif
- (flush_time && flush_time != ~(ulong) 0L))
- {
- pthread_t hThread;
- if (pthread_create(&hThread,&connection_attrib,handle_manager,0))
- sql_print_warning("Can't create thread to manage maintenance");
- }
-}
-
static void create_shutdown_thread()
{
@@ -3533,18 +4050,13 @@ static void create_shutdown_thread()
// On "Stop Service" we have to do regular shutdown
Service.SetShutdownEvent(hEventShutdown);
-#endif
-#ifdef OS2
- pthread_cond_init(&eventShutdown, NULL);
- pthread_t hThread;
- if (pthread_create(&hThread,&connection_attrib,handle_shutdown,0))
- sql_print_warning("Can't create thread to handle shutdown requests");
-#endif
+#endif /* __WIN__ */
}
#endif /* EMBEDDED_LIBRARY */
-#if defined(__NT__) || defined(HAVE_SMEM)
+
+#if (defined(__NT__) || defined(HAVE_SMEM)) && !defined(EMBEDDED_LIBRARY)
static void handle_connections_methods()
{
pthread_t hThread;
@@ -3595,7 +4107,7 @@ static void handle_connections_methods()
handler_count--;
}
}
-#endif
+#endif
while (handler_count > 0)
pthread_cond_wait(&COND_handler_count,&LOCK_thread_count);
@@ -3662,7 +4174,7 @@ int main(int argc, char **argv)
#endif
{
MY_INIT(argv[0]); // init my_sys library & pthreads
- /* ^^^ Nothing should be before this line! */
+ /* nothing should come before this line ^^^ */
/* Set signal used to kill MySQL */
#if defined(SIGUSR2)
@@ -3670,7 +4182,13 @@ int main(int argc, char **argv)
#else
thr_kill_signal= SIGINT;
#endif
-
+
+ /*
+ Perform basic logger initialization logger. Should be called after
+ MY_INIT, as it initializes mutexes. Log tables are inited later.
+ */
+ logger.init_base();
+
#ifdef _CUSTOMSTARTUPCONFIG_
if (_cust_check_startup())
{
@@ -3707,9 +4225,9 @@ int main(int argc, char **argv)
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,thread_stack*2);
+ pthread_attr_setstacksize(&connection_attrib,my_thread_stack_size*2);
#else
- pthread_attr_setstacksize(&connection_attrib,thread_stack);
+ pthread_attr_setstacksize(&connection_attrib,my_thread_stack_size);
#endif
#ifdef HAVE_PTHREAD_ATTR_GETSTACKSIZE
{
@@ -3720,22 +4238,21 @@ int main(int argc, char **argv)
stack_size/= 2;
#endif
/* We must check if stack_size = 0 as Solaris 2.9 can return 0 here */
- if (stack_size && stack_size < thread_stack)
+ 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",
- thread_stack, (long) stack_size);
+ my_thread_stack_size, (long) stack_size);
#if defined(__ia64__) || defined(__ia64)
- thread_stack= stack_size*2;
+ my_thread_stack_size= stack_size*2;
#else
- thread_stack= stack_size;
+ 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
@@ -3758,13 +4275,12 @@ 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,MYF(MY_WME)))
- {
+ if (my_setwd(mysql_real_data_home,MYF(MY_WME)) && !opt_help)
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)))
{
@@ -3776,7 +4292,6 @@ int main(int argc, char **argv)
set_user(mysqld_user, user_info);
}
-
if (opt_bin_log && !server_id)
{
server_id= !master_host ? 1 : 2;
@@ -3798,7 +4313,7 @@ we force server id to 2, but this MySQL server will not act as a slave.");
}
if (init_server_components())
- exit(1);
+ unireg_abort(1);
network_init();
@@ -3823,6 +4338,7 @@ we force server id to 2, but this MySQL server will not act as a slave.");
*/
error_handler_hook= my_message_sql;
start_signal_handler(); // Creates pidfile
+
if (mysql_rm_tmp_tables() || acl_init(opt_noacl) ||
my_tz_init((THD *)0, default_tz_name, opt_bootstrap))
{
@@ -3842,10 +4358,17 @@ we force server id to 2, but this MySQL server will not act as a slave.");
if (!opt_noacl)
(void) grant_init();
-#ifdef HAVE_DLOPEN
+ if (!opt_bootstrap)
+ servers_init(0);
+
if (!opt_noacl)
+ {
+#ifdef HAVE_DLOPEN
udf_init();
#endif
+ }
+
+ init_status_vars();
if (opt_bootstrap) /* If running with bootstrap, do not start replication. */
opt_skip_slave_start= 1;
/*
@@ -3856,7 +4379,6 @@ we force server id to 2, but this MySQL server will not act as a slave.");
*/
if (init_slave() && !active_mi)
{
- end_thr_alarm(1); // Don't allow alarms
unireg_abort(1);
}
@@ -3864,20 +4386,20 @@ we force server id to 2, but this MySQL server will not act as a slave.");
{
select_thread_in_use= 0; // Allow 'kill' to work
bootstrap(stdin);
- end_thr_alarm(1); // Don't allow alarms
unireg_abort(bootstrap_error ? 1 : 0);
}
if (opt_init_file)
{
if (read_init_file(opt_init_file))
- {
- end_thr_alarm(1); // Don't allow alarms
unireg_abort(1);
- }
}
+ execute_ddl_log_recovery();
create_shutdown_thread();
- create_maintenance_thread();
+ start_handle_manager();
+
+ if (Events::init(opt_noacl))
+ unireg_abort(1);
sql_print_information(ER(ER_STARTUP),my_progname,server_version,
((unix_sock == INVALID_SOCKET) ? (char*) ""
@@ -3888,6 +4410,13 @@ we force server id to 2, but this MySQL server will not act as a slave.");
Service.SetRunning();
#endif
+
+ /* Signal threads waiting for server to be started */
+ pthread_mutex_lock(&LOCK_server_started);
+ mysqld_server_started= 1;
+ pthread_cond_signal(&COND_server_started);
+ pthread_mutex_unlock(&LOCK_server_started);
+
#if defined(__NT__) || defined(HAVE_SMEM)
handle_connections_methods();
#else
@@ -3902,7 +4431,7 @@ we force server id to 2, but this MySQL server will not act as a slave.");
#endif /* __NT__ */
/* (void) pthread_attr_destroy(&connection_attrib); */
-
+
DBUG_PRINT("quit",("Exiting main thread"));
#ifndef __WIN__
@@ -3935,6 +4464,7 @@ 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);
@@ -3969,26 +4499,25 @@ static char *add_quoted_string(char *to, const char *from, char *to_end)
uint length= (uint) (to_end-to);
if (!strchr(from, ' '))
- return strnmov(to, from, length);
- return strxnmov(to, length, "\"", from, "\"", NullS);
+ return strmake(to, from, length-1);
+ return strxnmov(to, length-1, "\"", from, "\"", NullS);
}
-/*
- Handle basic handling of services, like installation and removal
+/**
+ Handle basic handling of services, like installation and removal.
- SYNOPSIS
- default_service_handling()
- argv Pointer to argument list
- servicename Internal name of service
- displayname Display name of service (in taskbar ?)
- file_path Path to this program
- startup_option Startup option to mysqld
-
- RETURN VALUES
+ @param argv Pointer to argument list
+ @param servicename Internal name of service
+ @param displayname Display name of service (in taskbar ?)
+ @param file_path Path to this program
+ @param startup_option Startup option to mysqld
+
+ @retval
0 option handled
+ @retval
1 Could not handle option
- */
+*/
static bool
default_service_handling(char **argv,
@@ -4036,7 +4565,6 @@ default_service_handling(char **argv,
int main(int argc, char **argv)
{
-
/*
When several instances are running on the same machine, we
need to have an unique named hEventShudown through the
@@ -4138,7 +4666,7 @@ int main(int argc, char **argv)
#endif
-/*
+/**
Execute all commands from a file. Used by the mysql_install_db script to
create MySQL privilege tables without having to start a full MySQL server.
*/
@@ -4152,7 +4680,7 @@ static void bootstrap(FILE *file)
my_net_init(&thd->net,(st_vio*) 0);
thd->max_client_packet_length= thd->net.max_packet;
thd->security_ctx->master_access= ~(ulong)0;
- thd->thread_id=thread_id++;
+ thd->thread_id= thd->variables.pseudo_thread_id= thread_id++;
thread_count++;
bootstrap_file=file;
@@ -4195,23 +4723,94 @@ static bool read_init_file(char *file_name)
#ifndef EMBEDDED_LIBRARY
+
/*
- Create new thread to handle incoming connection.
+ Simple scheduler that use the main thread to handle the request
+
+ NOTES
+ This is only used for debugging, when starting mysqld with
+ --thread-handling=no-threads or --one-thread
+
+ When we enter this function, LOCK_thread_count is hold!
+*/
+
+void handle_connection_in_main_thread(THD *thd)
+{
+ safe_mutex_assert_owner(&LOCK_thread_count);
+ thread_cache_size=0; // Safety
+ threads.append(thd);
+ (void) pthread_mutex_unlock(&LOCK_thread_count);
+ handle_one_connection((void*) thd);
+}
- SYNOPSIS
- create_new_thread()
- thd in/out Thread handle of future thread.
- DESCRIPTION
+/*
+ Scheduler that uses one thread per connection
+*/
+
+void create_thread_to_handle_connection(THD *thd)
+{
+ if (cached_thread_count > wake_thread)
+ {
+ /* Get thread from cache */
+ thread_cache.append(thd);
+ wake_thread++;
+ pthread_cond_signal(&COND_thread_cache);
+ }
+ else
+ {
+ char error_message_buff[MYSQL_ERRMSG_SIZE];
+ /* Create new thread to handle connection */
+ int error;
+ thread_created++;
+ threads.append(thd);
+ DBUG_PRINT("info",(("creating thread %lu"), thd->thread_id));
+ thd->connect_utime= thd->start_utime= my_micro_time();
+ if ((error=pthread_create(&thd->real_id,&connection_attrib,
+ handle_one_connection,
+ (void*) thd)))
+ {
+ /* purify: begin inspected */
+ DBUG_PRINT("error",
+ ("Can't create thread to handle request (error %d)",
+ error));
+ thread_count--;
+ thd->killed= THD::KILL_CONNECTION; // Safety
+ (void) pthread_mutex_unlock(&LOCK_thread_count);
+
+ pthread_mutex_lock(&LOCK_connection_count);
+ --connection_count;
+ pthread_mutex_unlock(&LOCK_connection_count);
+
+ statistic_increment(aborted_connects,&LOCK_status);
+ /* Can't use my_error() since store_globals has not been called. */
+ my_snprintf(error_message_buff, sizeof(error_message_buff),
+ 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,0,0);
+ delete thd;
+ (void) pthread_mutex_unlock(&LOCK_thread_count);
+ return;
+ /* purecov: end */
+ }
+ }
+ (void) pthread_mutex_unlock(&LOCK_thread_count);
+ DBUG_PRINT("info",("Thread created"));
+}
+
+
+/**
+ Create new thread to handle incoming connection.
+
This function will create new thread to handle the incoming
connection. If there are idle cached threads one will be used.
'thd' will be pushed into 'threads'.
- In single-threaded mode (#define ONE_THREAD) connection will be
+ In single-threaded mode (\#define ONE_THREAD) connection will be
handled inside this function.
- RETURN VALUE
- none
+ @param[in,out] thd Thread handle of future thread.
*/
static void create_new_thread(THD *thd)
@@ -4222,73 +4821,45 @@ static void create_new_thread(THD *thd)
if (protocol_version > 9)
net->return_errno=1;
- /* don't allow too many connections */
- if (thread_count - delayed_insert_threads >= max_connections+1 || abort_loop)
+ /*
+ Don't allow too many connections. We roughly check here that we allow
+ only (max_connections + 1) connections.
+ */
+
+ pthread_mutex_lock(&LOCK_connection_count);
+
+ if (connection_count >= max_connections + 1 || abort_loop)
{
+ pthread_mutex_unlock(&LOCK_connection_count);
+
DBUG_PRINT("error",("Too many connections"));
close_connection(thd, ER_CON_COUNT_ERROR, 1);
delete thd;
DBUG_VOID_RETURN;
}
+
+ ++connection_count;
+
+ if (connection_count > max_used_connections)
+ max_used_connections= connection_count;
+
+ pthread_mutex_unlock(&LOCK_connection_count);
+
+ /* Start a new thread to handle connection. */
+
pthread_mutex_lock(&LOCK_thread_count);
- thd->thread_id=thread_id++;
- thd->real_id=pthread_self(); // Keep purify happy
+ /*
+ The initialization of thread_id is done in create_embedded_thd() for
+ the embedded library.
+ TODO: refactor this to avoid code duplication there
+ */
+ thd->thread_id= thd->variables.pseudo_thread_id= thread_id++;
- /* Start a new thread to handle connection */
thread_count++;
-#ifdef ONE_THREAD
- if (test_flags & TEST_NO_THREADS) // For debugging under Linux
- {
- thread_cache_size=0; // Safety
- threads.append(thd);
- thd->real_id=pthread_self();
- (void) pthread_mutex_unlock(&LOCK_thread_count);
- handle_one_connection((void*) thd);
- }
- else
-#endif
- {
- if (thread_count-delayed_insert_threads > max_used_connections)
- max_used_connections=thread_count-delayed_insert_threads;
-
- if (cached_thread_count > wake_thread)
- {
- thread_cache.append(thd);
- wake_thread++;
- pthread_cond_signal(&COND_thread_cache);
- }
- else
- {
- int error;
- thread_created++;
- threads.append(thd);
- DBUG_PRINT("info",(("creating thread %lu"), thd->thread_id));
- thd->connect_time = time(NULL);
- if ((error=pthread_create(&thd->real_id,&connection_attrib,
- handle_one_connection,
- (void*) thd)))
- {
- DBUG_PRINT("error",
- ("Can't create thread to handle request (error %d)",
- error));
- thread_count--;
- thd->killed= THD::KILL_CONNECTION; // Safety
- (void) pthread_mutex_unlock(&LOCK_thread_count);
- statistic_increment(aborted_connects,&LOCK_status);
- net_printf_error(thd, ER_CANT_CREATE_THREAD, error);
- (void) pthread_mutex_lock(&LOCK_thread_count);
- close_connection(thd,0,0);
- delete thd;
- (void) pthread_mutex_unlock(&LOCK_thread_count);
- DBUG_VOID_RETURN;
- }
- }
- (void) pthread_mutex_unlock(&LOCK_thread_count);
+ thread_scheduler.add_connection(thd);
- }
- DBUG_PRINT("info",("Thread created"));
DBUG_VOID_RETURN;
}
#endif /* EMBEDDED_LIBRARY */
@@ -4403,7 +4974,7 @@ pthread_handler_t handle_connections_sockets(void *arg __attribute__((unused)))
size_socket length=sizeof(struct sockaddr_in);
new_sock = accept(sock, my_reinterpret_cast(struct sockaddr *) (&cAddr),
&length);
-#ifdef __NETWARE__
+#ifdef __NETWARE__
// TODO: temporary fix, waiting for TCP/IP fix - DEFECT000303149
if ((new_sock == INVALID_SOCKET) && (socket_errno == EINVAL))
{
@@ -4521,10 +5092,6 @@ pthread_handler_t handle_connections_sockets(void *arg __attribute__((unused)))
create_new_thread(thd);
}
-#ifdef OS2
- // kill server must be invoked from thread 1!
- kill_server(MYSQL_KILL_SIGNAL);
-#endif
decrement_handler_count();
DBUG_RETURN(0);
}
@@ -4613,15 +5180,13 @@ pthread_handler_t handle_connections_namedpipes(void *arg)
#endif /* __NT__ */
-/*
- Thread of shared memory's service
+#ifdef HAVE_SMEM
- SYNOPSIS
- handle_connections_shared_memory()
- arg Arguments of thread
-*/
+/**
+ Thread of shared memory's service.
-#ifdef HAVE_SMEM
+ @param arg Arguments of thread
+*/
pthread_handler_t handle_connections_shared_memory(void *arg)
{
/* file-mapping object, use for create shared memory */
@@ -4813,7 +5378,7 @@ errorconn:
NullS);
sql_perror(buff);
}
- if (handle_client_file_map)
+ if (handle_client_file_map)
CloseHandle(handle_client_file_map);
if (handle_client_map)
UnmapViewOfFile(handle_client_map);
@@ -4861,8 +5426,8 @@ error:
enum options_mysqld
{
- OPT_ISAM_LOG=256, OPT_SKIP_NEW,
- OPT_SKIP_GRANT, OPT_SKIP_LOCK,
+ OPT_ISAM_LOG=256, OPT_SKIP_NEW,
+ OPT_SKIP_GRANT, OPT_SKIP_LOCK,
OPT_ENABLE_LOCK, OPT_USE_LOCKING,
OPT_SOCKET, OPT_UPDATE_LOG,
OPT_BIN_LOG, OPT_SKIP_RESOLVE,
@@ -4877,10 +5442,6 @@ enum options_mysqld
OPT_STORAGE_ENGINE, OPT_INIT_FILE,
OPT_DELAY_KEY_WRITE_ALL, OPT_SLOW_QUERY_LOG,
OPT_DELAY_KEY_WRITE, OPT_CHARSETS_DIR,
- OPT_BDB_HOME, OPT_BDB_LOG,
- OPT_BDB_TMP, OPT_BDB_SYNC,
- OPT_BDB_LOCK, OPT_BDB,
- OPT_BDB_NO_RECOVER, OPT_BDB_SHARED,
OPT_MASTER_HOST, OPT_MASTER_USER,
OPT_MASTER_PASSWORD, OPT_MASTER_PORT,
OPT_MASTER_INFO_FILE, OPT_MASTER_CONNECT_RETRY,
@@ -4891,42 +5452,39 @@ enum options_mysqld
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,
+#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_SKIP_INNOBASE,
+ 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_INNODB_DATA_HOME_DIR,
- OPT_INNODB_DATA_FILE_PATH,
- OPT_INNODB_LOG_GROUP_HOME_DIR,
- OPT_INNODB_LOG_ARCH_DIR,
- OPT_INNODB_LOG_ARCHIVE,
- OPT_INNODB_FLUSH_LOG_AT_TRX_COMMIT,
- OPT_INNODB_FLUSH_METHOD,
- OPT_INNODB_DOUBLEWRITE,
- OPT_INNODB_CHECKSUMS,
- OPT_INNODB_FAST_SHUTDOWN,
- OPT_INNODB_FILE_PER_TABLE, OPT_CRASH_BINLOG_INNODB,
- OPT_INNODB_LOCKS_UNSAFE_FOR_BINLOG,
OPT_LOG_BIN_TRUST_FUNCTION_CREATORS,
- OPT_SAFE_SHOW_DB, OPT_INNODB_SAFE_BINLOG,
- OPT_INNODB, OPT_ISAM,
- OPT_ENGINE_CONDITION_PUSHDOWN, OPT_NDBCLUSTER, OPT_NDB_CONNECTSTRING,
+ 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_SKIP_SAFEMALLOC,
OPT_TEMP_POOL, OPT_TX_ISOLATION, OPT_COMPLETION_TYPE,
OPT_SKIP_STACK_TRACE, 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_REPORT_HOST,
+ 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,
@@ -4956,6 +5514,7 @@ enum options_mysqld
OPT_MAX_ERROR_COUNT, OPT_MULTI_RANGE_COUNT, 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_STATS_METHOD,
OPT_NET_BUFFER_LENGTH, OPT_NET_RETRY_COUNT,
OPT_NET_READ_TIMEOUT, OPT_NET_WRITE_TIMEOUT,
@@ -4967,35 +5526,10 @@ enum options_mysqld
OPT_RELAY_LOG_PURGE,
OPT_SLAVE_NET_TIMEOUT, OPT_SLAVE_COMPRESSED_PROTOCOL, OPT_SLOW_LAUNCH_TIME,
OPT_SLAVE_TRANS_RETRIES, OPT_READONLY, OPT_DEBUGGING,
- OPT_SORT_BUFFER, OPT_TABLE_CACHE,
+ 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_MYISAM_REPAIR_THREADS,
- OPT_INNODB_MIRRORED_LOG_GROUPS,
- OPT_INNODB_LOG_FILES_IN_GROUP,
- OPT_INNODB_LOG_FILE_SIZE,
- OPT_INNODB_LOG_BUFFER_SIZE,
- OPT_INNODB_BUFFER_POOL_SIZE,
- OPT_INNODB_BUFFER_POOL_AWE_MEM_MB,
- OPT_INNODB_ADDITIONAL_MEM_POOL_SIZE,
- OPT_INNODB_MAX_PURGE_LAG,
- OPT_INNODB_FILE_IO_THREADS,
- OPT_INNODB_LOCK_WAIT_TIMEOUT,
- OPT_INNODB_THREAD_CONCURRENCY,
- OPT_INNODB_COMMIT_CONCURRENCY,
- OPT_INNODB_FORCE_RECOVERY,
- OPT_INNODB_STATUS_FILE,
- OPT_INNODB_MAX_DIRTY_PAGES_PCT,
- OPT_INNODB_TABLE_LOCKS,
- OPT_INNODB_SUPPORT_XA,
- OPT_INNODB_OPEN_FILES,
- OPT_INNODB_AUTOEXTEND_INCREMENT,
- OPT_INNODB_SYNC_SPIN_LOOPS,
- OPT_INNODB_CONCURRENCY_TICKETS,
- OPT_INNODB_THREAD_SLEEP_DELAY,
- OPT_BDB_CACHE_SIZE,
- OPT_BDB_LOG_BUFFER_SIZE,
- OPT_BDB_MAX_LOCK,
+ OPT_WAIT_TIMEOUT,
OPT_ERROR_LOG_FILE,
OPT_DEFAULT_WEEK_FORMAT,
OPT_RANGE_ALLOC_BLOCK_SIZE, OPT_ALLOW_SUSPICIOUS_UDFS,
@@ -5005,10 +5539,10 @@ enum options_mysqld
OPT_SYNC_REPLICATION,
OPT_SYNC_REPLICATION_SLAVE_ID,
OPT_SYNC_REPLICATION_TIMEOUT,
- OPT_BDB_NOSYNC,
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,
@@ -5035,14 +5569,24 @@ enum options_mysqld
OPT_OLD_STYLE_USER_LIMITS,
OPT_LOG_SLOW_ADMIN_STATEMENTS,
OPT_TABLE_LOCK_WAIT_TIMEOUT,
+ OPT_PLUGIN_LOAD,
OPT_PLUGIN_DIR,
+ OPT_LOG_OUTPUT,
OPT_PORT_OPEN_TIMEOUT,
- OPT_MERGE,
+ OPT_PROFILING,
+ OPT_KEEP_FILES_ON_CREATE,
+ OPT_GENERAL_LOG,
+ OPT_SLOW_LOG,
+ OPT_THREAD_HANDLING,
OPT_INNODB_ROLLBACK_ON_TIMEOUT,
OPT_SECURE_FILE_PRIV,
- OPT_KEEP_FILES_ON_CREATE,
- OPT_INNODB_ADAPTIVE_HASH_INDEX,
- OPT_FEDERATED
+ OPT_MIN_EXAMINED_ROW_LIMIT,
+ OPT_LOG_SLOW_SLAVE_STATEMENTS,
+ OPT_OLD_MODE,
+ OPT_SLAVE_EXEC_MODE,
+ OPT_GENERAL_LOG_FILE,
+ OPT_SLOW_QUERY_LOG_FILE,
+ OPT_IGNORE_BUILTIN_INNODB
};
@@ -5050,13 +5594,13 @@ enum options_mysqld
struct my_option my_long_options[] =
{
- {"help", '?', "Display this help and exit.",
- (gptr*) &opt_help, (gptr*) &opt_help, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0,
+ {"help", '?', "Display this help and exit.",
+ (uchar**) &opt_help, (uchar**) &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.",
- (gptr*) &abort_slave_event_count, (gptr*) &abort_slave_event_count,
+ (uchar**) &abort_slave_event_count, (uchar**) &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,
@@ -5064,125 +5608,126 @@ struct my_option my_long_options[] =
"without corresponding xxx_init() or xxx_deinit(). That also means "
"that one can load any function from any library, for example exit() "
"from libc.so",
- (gptr*) &opt_allow_suspicious_udfs, (gptr*) &opt_allow_suspicious_udfs,
+ (uchar**) &opt_allow_suspicious_udfs, (uchar**) &opt_allow_suspicious_udfs,
0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
{"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",
- (gptr*) &global_system_variables.auto_increment_increment,
- (gptr*) &max_system_variables.auto_increment_increment, 0, GET_ULONG,
+ (uchar**) &global_system_variables.auto_increment_increment,
+ (uchar**) &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",
- (gptr*) &global_system_variables.auto_increment_offset,
- (gptr*) &max_system_variables.auto_increment_offset, 0, GET_ULONG, OPT_ARG,
+ (uchar**) &global_system_variables.auto_increment_offset,
+ (uchar**) &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.",
- (gptr*) &sp_automatic_privileges, (gptr*) &sp_automatic_privileges,
+ (uchar**) &sp_automatic_privileges, (uchar**) &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.",
- (gptr*) &mysql_home_ptr, (gptr*) &mysql_home_ptr, 0, GET_STR, REQUIRED_ARG,
+ (uchar**) &mysql_home_ptr, (uchar**) &mysql_home_ptr, 0, GET_STR, REQUIRED_ARG,
0, 0, 0, 0, 0, 0},
- {"bdb", OPT_BDB, "Enable Berkeley DB (if this version of MySQL supports it). \
-Disable with --skip-bdb (will save memory).",
- (gptr*) &opt_bdb, (gptr*) &opt_bdb, 0, GET_BOOL, NO_ARG, OPT_BDB_DEFAULT, 0, 0,
- 0, 0, 0},
-#ifdef HAVE_BERKELEY_DB
- {"bdb-home", OPT_BDB_HOME, "Berkeley home directory.", (gptr*) &berkeley_home,
- (gptr*) &berkeley_home, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
- {"bdb-lock-detect", OPT_BDB_LOCK,
- "Berkeley lock detect (DEFAULT, OLDEST, RANDOM or YOUNGEST, # sec).",
- 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
- {"bdb-logdir", OPT_BDB_LOG, "Berkeley DB log file directory.",
- (gptr*) &berkeley_logdir, (gptr*) &berkeley_logdir, 0, GET_STR,
- REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
- {"bdb-no-recover", OPT_BDB_NO_RECOVER,
- "Don't try to recover Berkeley DB tables on start.", 0, 0, 0, GET_NO_ARG,
- NO_ARG, 0, 0, 0, 0, 0, 0},
- {"bdb-no-sync", OPT_BDB_NOSYNC,
- "This option is deprecated, use --skip-sync-bdb-logs instead",
- 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
- {"bdb-shared-data", OPT_BDB_SHARED,
- "Start Berkeley DB in multi-process mode.", 0, 0, 0, GET_NO_ARG, NO_ARG, 0,
- 0, 0, 0, 0, 0},
- {"bdb-tmpdir", OPT_BDB_TMP, "Berkeley DB tempfile name.",
- (gptr*) &berkeley_tmpdir, (gptr*) &berkeley_tmpdir, 0, GET_STR,
- REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
-#endif /* HAVE_BERKELEY_DB */
{"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},
{"bind-address", OPT_BIND_ADDRESS, "IP address to bind to.",
- (gptr*) &my_bind_addr_str, (gptr*) &my_bind_addr_str, 0, GET_STR,
+ (uchar**) &my_bind_addr_str, (uchar**) &my_bind_addr_str, 0, GET_STR,
REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+ {"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, or '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. "
+#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
+ ,(uchar**) &opt_binlog_format, (uchar**) &opt_binlog_format,
+ 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
{"binlog-do-db", OPT_BINLOG_DO_DB,
"Tells the master it should log updates for the specified database, and exclude all others not explicitly mentioned.",
0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
{"binlog-ignore-db", OPT_BINLOG_IGNORE_DB,
"Tells the master that updates to the given database should not be logged tothe 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,
+ "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.",
+ (uchar**) &opt_binlog_rows_event_max_size,
+ (uchar**) &opt_binlog_rows_event_max_size, 0,
+ GET_ULONG, REQUIRED_ARG,
+ /* def_value */ 1024, /* min_value */ 256, /* max_value */ ULONG_MAX,
+ /* sub_size */ 0, /* block_size */ 256,
+ /* app_type */ 0
+ },
#ifndef DISABLE_GRANT_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,
"Don't ignore client side character set value sent during handshake.",
- (gptr*) &opt_character_set_client_handshake,
- (gptr*) &opt_character_set_client_handshake,
+ (uchar**) &opt_character_set_client_handshake,
+ (uchar**) &opt_character_set_client_handshake,
0, GET_BOOL, NO_ARG, 1, 0, 0, 0, 0, 0},
{"character-set-filesystem", OPT_CHARACTER_SET_FILESYSTEM,
"Set the filesystem character set.",
- (gptr*) &character_set_filesystem_name,
- (gptr*) &character_set_filesystem_name,
+ (uchar**) &character_set_filesystem_name,
+ (uchar**) &character_set_filesystem_name,
0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 },
{"character-set-server", 'C', "Set the default character set.",
- (gptr*) &default_character_set_name, (gptr*) &default_character_set_name,
+ (uchar**) &default_character_set_name, (uchar**) &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.", (gptr*) &charsets_dir,
- (gptr*) &charsets_dir, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+ "Directory where character sets are.", (uchar**) &charsets_dir,
+ (uchar**) &charsets_dir, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
{"chroot", 'r', "Chroot mysqld daemon during startup.",
- (gptr*) &mysqld_chroot, (gptr*) &mysqld_chroot, 0, GET_STR, REQUIRED_ARG,
+ (uchar**) &mysqld_chroot, (uchar**) &mysqld_chroot, 0, GET_STR, REQUIRED_ARG,
0, 0, 0, 0, 0, 0},
{"collation-server", OPT_DEFAULT_COLLATION, "Set the default collation.",
- (gptr*) &default_collation_name, (gptr*) &default_collation_name,
+ (uchar**) &default_collation_name, (uchar**) &default_collation_name,
0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 },
{"completion-type", OPT_COMPLETION_TYPE, "Default completion type.",
- (gptr*) &global_system_variables.completion_type,
- (gptr*) &max_system_variables.completion_type, 0, GET_ULONG,
+ (uchar**) &global_system_variables.completion_type,
+ (uchar**) &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",
- (gptr*) &myisam_concurrent_insert, (gptr*) &myisam_concurrent_insert,
+ (uchar**) &myisam_concurrent_insert, (uchar**) &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.",
- (gptr*) &opt_console, (gptr*) &opt_console, 0, GET_BOOL, NO_ARG, 0, 0, 0,
+ (uchar**) &opt_console, (uchar**) &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.", (gptr*) &mysql_data_home,
- (gptr*) &mysql_data_home, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+ {"datadir", 'h', "Path to the database root.", (uchar**) &mysql_data_home,
+ (uchar**) &mysql_data_home, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
#ifndef DBUG_OFF
- {"debug", '#', "Debug log.", (gptr*) &default_dbug_option,
- (gptr*) &default_dbug_option, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0},
+ {"debug", '#', "Debug log.", (uchar**) &default_dbug_option,
+ (uchar**) &default_dbug_option, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0},
#endif
{"default-character-set", 'C', "Set the default character set (deprecated option, use --character-set-server instead).",
- (gptr*) &default_character_set_name, (gptr*) &default_character_set_name,
+ (uchar**) &default_character_set_name, (uchar**) &default_character_set_name,
0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 },
{"default-collation", OPT_DEFAULT_COLLATION, "Set the default collation (deprecated option, use --collation-server instead).",
- (gptr*) &default_collation_name, (gptr*) &default_collation_name,
+ (uchar**) &default_collation_name, (uchar**) &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.", 0, 0,
+ "Set the default storage engine (table type) for tables.",
+ (uchar**)&default_storage_engine_str, (uchar**)&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.", 0, 0,
+ "(deprecated) Use --default-storage-engine.",
+ (uchar**)&default_storage_engine_str, (uchar**)&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.",
- (gptr*) &default_tz_name, (gptr*) &default_tz_name,
+ (uchar**) &default_tz_name, (uchar**) &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},
@@ -5192,203 +5737,107 @@ Disable with --skip-bdb (will save memory).",
#ifdef HAVE_OPENSSL
{"des-key-file", OPT_DES_KEY_FILE,
"Load keys for des_encrypt() and des_encrypt from given file.",
- (gptr*) &des_key_file, (gptr*) &des_key_file, 0, GET_STR, REQUIRED_ARG,
+ (uchar**) &des_key_file, (uchar**) &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.",
- (gptr*) &disconnect_slave_event_count,
- (gptr*) &disconnect_slave_event_count, 0, GET_INT, REQUIRED_ARG, 0, 0, 0,
+ (uchar**) &disconnect_slave_event_count,
+ (uchar**) &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.",
- (gptr*) &opt_external_locking, (gptr*) &opt_external_locking,
+ (uchar**) &opt_external_locking, (uchar**) &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).",
- (gptr*) &opt_enable_named_pipe, (gptr*) &opt_enable_named_pipe, 0, GET_BOOL,
+ (uchar**) &opt_enable_named_pipe, (uchar**) &opt_enable_named_pipe, 0, GET_BOOL,
NO_ARG, 0, 0, 0, 0, 0, 0},
#endif
+#ifdef HAVE_STACK_TRACE_ON_SEGV
{"enable-pstack", OPT_DO_PSTACK, "Print a symbolic stack trace on failure.",
- (gptr*) &opt_do_pstack, (gptr*) &opt_do_pstack, 0, GET_BOOL, NO_ARG, 0, 0,
+ (uchar**) &opt_do_pstack, (uchar**) &opt_do_pstack, 0, GET_BOOL, NO_ARG, 0, 0,
0, 0, 0, 0},
+#endif /* HAVE_STACK_TRACE_ON_SEGV */
{"engine-condition-pushdown",
OPT_ENGINE_CONDITION_PUSHDOWN,
"Push supported query conditions to the storage engine.",
- (gptr*) &global_system_variables.engine_condition_pushdown,
- (gptr*) &global_system_variables.engine_condition_pushdown,
- 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
+ (uchar**) &global_system_variables.engine_condition_pushdown,
+ (uchar**) &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.",
- (gptr*) &opt_external_locking, (gptr*) &opt_external_locking,
+ {"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.",
+ (uchar**) &opt_external_locking, (uchar**) &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},
- {"federated", OPT_FEDERATED, "Enable Federated storage engine. Disable with \
---skip-federated.",
- (gptr*) &opt_federated, (gptr*) &opt_federated, 0, GET_BOOL, NO_ARG, 1, 0, 0, 0, 0, 0},
/* We must always support the next option to make scripts like mysqltest
easier to do */
{"gdb", OPT_DEBUGGING,
"Set up signals usable for debugging",
- (gptr*) &opt_debugging, (gptr*) &opt_debugging,
+ (uchar**) &opt_debugging, (uchar**) &opt_debugging,
0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
+ {"general_log", OPT_GENERAL_LOG,
+ "Enable|disable general log", (uchar**) &opt_log,
+ (uchar**) &opt_log, 0, GET_BOOL, OPT_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.",
- (gptr*) &opt_large_pages, (gptr*) &opt_large_pages, 0, GET_BOOL, NO_ARG, 0, 0, 0,
+ (uchar**) &opt_large_pages, (uchar**) &opt_large_pages, 0, GET_BOOL, NO_ARG, 0, 0, 0,
0, 0, 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",
- (gptr*) &opt_init_connect, (gptr*) &opt_init_connect, 0, GET_STR_ALLOC,
+ (uchar**) &opt_init_connect, (uchar**) &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.",
- (gptr*) &opt_init_file, (gptr*) &opt_init_file, 0, GET_STR, REQUIRED_ARG,
+ (uchar**) &opt_init_file, (uchar**) &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 when a slave connects to this master",
- (gptr*) &opt_init_slave, (gptr*) &opt_init_slave, 0, GET_STR_ALLOC,
+ (uchar**) &opt_init_slave, (uchar**) &opt_init_slave, 0, GET_STR_ALLOC,
REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
- {"innodb", OPT_INNODB, "Enable InnoDB (if this version of MySQL supports it). \
-Disable with --skip-innodb (will save memory).",
- (gptr*) &opt_innodb, (gptr*) &opt_innodb, 0, GET_BOOL, NO_ARG, OPT_INNODB_DEFAULT, 0, 0,
- 0, 0, 0},
-#ifdef HAVE_INNOBASE_DB
- {"innodb_checksums", OPT_INNODB_CHECKSUMS, "Enable InnoDB checksums validation (enabled by default). \
-Disable with --skip-innodb-checksums.", (gptr*) &innobase_use_checksums,
- (gptr*) &innobase_use_checksums, 0, GET_BOOL, NO_ARG, 1, 0, 0, 0, 0, 0},
-#endif
- {"innodb_data_file_path", OPT_INNODB_DATA_FILE_PATH,
- "Path to individual files and their sizes.",
- 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
-#ifdef HAVE_INNOBASE_DB
- {"innodb_data_home_dir", OPT_INNODB_DATA_HOME_DIR,
- "The common part for InnoDB table spaces.", (gptr*) &innobase_data_home_dir,
- (gptr*) &innobase_data_home_dir, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0,
- 0},
- {"innodb_adaptive_hash_index", OPT_INNODB_ADAPTIVE_HASH_INDEX,
- "Enable InnoDB adaptive hash index (enabled by default). "
- "Disable with --skip-innodb-adaptive-hash-index.",
- (gptr*) &innobase_adaptive_hash_index,
- (gptr*) &innobase_adaptive_hash_index,
- 0, GET_BOOL, NO_ARG, 1, 0, 0, 0, 0, 0},
- {"innodb_doublewrite", OPT_INNODB_DOUBLEWRITE, "Enable InnoDB doublewrite buffer (enabled by default). \
-Disable with --skip-innodb-doublewrite.", (gptr*) &innobase_use_doublewrite,
- (gptr*) &innobase_use_doublewrite, 0, GET_BOOL, NO_ARG, 1, 0, 0, 0, 0, 0},
- {"innodb_fast_shutdown", OPT_INNODB_FAST_SHUTDOWN,
- "Speeds up the shutdown process of the InnoDB storage engine. Possible "
- "values are 0, 1 (faster)"
- /*
- NetWare can't close unclosed files, can't automatically kill remaining
- threads, etc, so on this OS we disable the crash-like InnoDB shutdown.
- */
-#ifndef __NETWARE__
- " or 2 (fastest - crash-like)"
-#endif
- ".",
- (gptr*) &innobase_fast_shutdown,
- (gptr*) &innobase_fast_shutdown, 0, GET_ULONG, OPT_ARG, 1, 0,
- IF_NETWARE(1,2), 0, 0, 0},
- {"innodb_file_per_table", OPT_INNODB_FILE_PER_TABLE,
- "Stores each InnoDB table to an .ibd file in the database dir.",
- (gptr*) &innobase_file_per_table,
- (gptr*) &innobase_file_per_table, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
- {"innodb_flush_log_at_trx_commit", OPT_INNODB_FLUSH_LOG_AT_TRX_COMMIT,
- "Set to 0 (write and flush once per second), 1 (write and flush at each commit) or 2 (write at commit, flush once per second).",
- (gptr*) &srv_flush_log_at_trx_commit,
- (gptr*) &srv_flush_log_at_trx_commit,
- 0, GET_ULONG, OPT_ARG, 1, 0, 2, 0, 0, 0},
- {"innodb_flush_method", OPT_INNODB_FLUSH_METHOD,
- "With which method to flush data.", (gptr*) &innobase_unix_file_flush_method,
- (gptr*) &innobase_unix_file_flush_method, 0, GET_STR, REQUIRED_ARG, 0, 0, 0,
- 0, 0, 0},
- {"innodb_locks_unsafe_for_binlog", OPT_INNODB_LOCKS_UNSAFE_FOR_BINLOG,
- "Force InnoDB not to use next-key locking. Instead use only row-level locking",
- (gptr*) &innobase_locks_unsafe_for_binlog,
- (gptr*) &innobase_locks_unsafe_for_binlog, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
- {"innodb_log_arch_dir", OPT_INNODB_LOG_ARCH_DIR,
- "Where full logs should be archived.", (gptr*) &innobase_log_arch_dir,
- (gptr*) &innobase_log_arch_dir, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
- {"innodb_log_archive", OPT_INNODB_LOG_ARCHIVE,
- "Set to 1 if you want to have logs archived.", 0, 0, 0, GET_LONG, OPT_ARG,
- 0, 0, 0, 0, 0, 0},
- {"innodb_log_group_home_dir", OPT_INNODB_LOG_GROUP_HOME_DIR,
- "Path to InnoDB log files.", (gptr*) &innobase_log_group_home_dir,
- (gptr*) &innobase_log_group_home_dir, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0,
- 0, 0},
- {"innodb_max_dirty_pages_pct", OPT_INNODB_MAX_DIRTY_PAGES_PCT,
- "Percentage of dirty pages allowed in bufferpool.", (gptr*) &srv_max_buf_pool_modified_pct,
- (gptr*) &srv_max_buf_pool_modified_pct, 0, GET_ULONG, REQUIRED_ARG, 90, 0, 100, 0, 0, 0},
- {"innodb_max_purge_lag", OPT_INNODB_MAX_PURGE_LAG,
- "Desired maximum length of the purge queue (0 = no limit)",
- (gptr*) &srv_max_purge_lag,
- (gptr*) &srv_max_purge_lag, 0, GET_ULONG, REQUIRED_ARG, 0, 0, ULONG_MAX,
- 0, 1L, 0},
- {"innodb_rollback_on_timeout", OPT_INNODB_ROLLBACK_ON_TIMEOUT,
- "Roll back the complete transaction on lock wait timeout, for 4.x compatibility (disabled by default)",
- (gptr*) &innobase_rollback_on_timeout, (gptr*) &innobase_rollback_on_timeout,
- 0, GET_BOOL, OPT_ARG, 0, 0, 0, 0, 0, 0},
- {"innodb_status_file", OPT_INNODB_STATUS_FILE,
- "Enable SHOW INNODB STATUS output in the innodb_status.<pid> file",
- (gptr*) &innobase_create_status_file, (gptr*) &innobase_create_status_file,
- 0, GET_BOOL, OPT_ARG, 0, 0, 0, 0, 0, 0},
- {"innodb_support_xa", OPT_INNODB_SUPPORT_XA,
- "Enable InnoDB support for the XA two-phase commit",
- (gptr*) &global_system_variables.innodb_support_xa,
- (gptr*) &global_system_variables.innodb_support_xa,
- 0, GET_BOOL, OPT_ARG, 1, 0, 0, 0, 0, 0},
- {"innodb_table_locks", OPT_INNODB_TABLE_LOCKS,
- "Enable InnoDB locking in LOCK TABLES",
- (gptr*) &global_system_variables.innodb_table_locks,
- (gptr*) &global_system_variables.innodb_table_locks,
- 0, GET_BOOL, OPT_ARG, 1, 0, 0, 0, 0, 0},
-#endif /* End HAVE_INNOBASE_DB */
- {"isam", OPT_ISAM, "Obsolete. ISAM storage engine is no longer supported.",
- (gptr*) &opt_isam, (gptr*) &opt_isam, 0, GET_BOOL, NO_ARG, 0, 0, 0,
- 0, 0, 0},
- {"language", 'L',
+ {"language", 'L',
"Client error messages in given language. May be given as a full path.",
- (gptr*) &language_ptr, (gptr*) &language_ptr, 0, GET_STR, REQUIRED_ARG,
+ (uchar**) &language_ptr, (uchar**) &language_ptr, 0, GET_STR, REQUIRED_ARG,
0, 0, 0, 0, 0, 0},
{"lc-time-names", OPT_LC_TIME_NAMES,
"Set the language used for the month names and the days of the week.",
- (gptr*) &lc_time_names_name,
- (gptr*) &lc_time_names_name,
+ (uchar**) &lc_time_names_name,
+ (uchar**) &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|0).",
- (gptr*) &opt_local_infile,
- (gptr*) &opt_local_infile, 0, GET_BOOL, OPT_ARG,
+ (uchar**) &opt_local_infile,
+ (uchar**) &opt_local_infile, 0, GET_BOOL, OPT_ARG,
1, 0, 0, 0, 0, 0},
- {"log", 'l', "Log connections and queries to file.", (gptr*) &opt_logname,
- (gptr*) &opt_logname, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0},
+ {"log", 'l', "Log connections and queries to file (deprecated option, use "
+ "--general_log/--general_log_file instead).", (uchar**) &opt_logname,
+ (uchar**) &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.", (uchar**) &opt_logname,
+ (uchar**) &opt_logname, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
{"log-bin", OPT_BIN_LOG,
"Log update queries in binary format. Optional (but strongly recommended "
"to avoid replication problems if server's hostname changes) argument "
"should be the chosen location for the binary log files.",
- (gptr*) &opt_bin_logname, (gptr*) &opt_bin_logname, 0, GET_STR_ALLOC,
+ (uchar**) &opt_bin_logname, (uchar**) &opt_bin_logname, 0, GET_STR_ALLOC,
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.",
- (gptr*) &opt_binlog_index_name, (gptr*) &opt_binlog_index_name, 0, GET_STR,
+ (uchar**) &opt_binlog_index_name, (uchar**) &opt_binlog_index_name, 0, GET_STR,
REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
- /*
- 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 is allowed only to users having the SUPER privilege and"
- " only if this function may not break binary logging.",
- (gptr*) &trust_function_creators, (gptr*) &trust_function_creators, 0,
- GET_BOOL, NO_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
@@ -5398,230 +5847,292 @@ Disable with --skip-innodb-doublewrite.", (gptr*) &innobase_use_doublewrite,
*/
{"log-bin-trust-routine-creators", OPT_LOG_BIN_TRUST_FUNCTION_CREATORS,
"(deprecated) Use log-bin-trust-function-creators.",
- (gptr*) &trust_function_creators, (gptr*) &trust_function_creators, 0,
+ (uchar**) &trust_function_creators, (uchar**) &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."
+ ,(uchar**) &trust_function_creators, (uchar**) &trust_function_creators, 0,
+ GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
{"log-error", OPT_ERROR_LOG_FILE, "Error log file.",
- (gptr*) &log_error_file_ptr, (gptr*) &log_error_file_ptr, 0, GET_STR,
+ (uchar**) &log_error_file_ptr, (uchar**) &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.",
- (gptr*) &myisam_log_filename, (gptr*) &myisam_log_filename, 0, GET_STR,
+ (uchar**) &myisam_log_filename, (uchar**) &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.",
+ "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},
+#ifdef WITH_CSV_STORAGE_ENGINE
+ {"log-output", OPT_LOG_OUTPUT,
+ "Syntax: log-output[=value[,value...]], where \"value\" could be TABLE, "
+ "FILE or NONE.",
+ (uchar**) &log_output_str, (uchar**) &log_output_str, 0,
+ GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0},
+#endif
{"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.",
- (gptr*) &opt_log_queries_not_using_indexes, (gptr*) &opt_log_queries_not_using_indexes,
+ (uchar**) &opt_log_queries_not_using_indexes, (uchar**) &opt_log_queries_not_using_indexes,
0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
{"log-short-format", OPT_SHORT_LOG_FORMAT,
"Don't log extra information to update and slow-query logs.",
- (gptr*) &opt_short_log_format, (gptr*) &opt_short_log_format,
+ (uchar**) &opt_short_log_format, (uchar**) &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.",
- (gptr*) &opt_log_slave_updates, (gptr*) &opt_log_slave_updates, 0, GET_BOOL,
+ (uchar**) &opt_log_slave_updates, (uchar**) &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.",
- (gptr*) &opt_log_slow_admin_statements,
- (gptr*) &opt_log_slow_admin_statements,
+ (uchar**) &opt_log_slow_admin_statements,
+ (uchar**) &opt_log_slow_admin_statements,
0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
- {"log-slow-queries", OPT_SLOW_QUERY_LOG,
- "Log slow queries to this log file. Defaults logging to hostname-slow.log file. Must be enabled to activate other slow log options.",
- (gptr*) &opt_slow_logname, (gptr*) &opt_slow_logname, 0, GET_STR, OPT_ARG,
+ {"log-slow-slave-statements", OPT_LOG_SLOW_SLAVE_STATEMENTS,
+ "Log slow statements executed by slave thread to the slow log if it is open.",
+ (uchar**) &opt_log_slow_slave_statements,
+ (uchar**) &opt_log_slow_slave_statements,
+ 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
+ {"log_slow_queries", OPT_SLOW_QUERY_LOG,
+ "Log slow queries to a table or log file. Defaults logging to table "
+ "mysql.slow_log or hostname-slow.log if --log-output=file is used. "
+ "Must be enabled to activate other slow log options. "
+ "(deprecated option, use --slow_query_log/--slow_query_log_file instead)",
+ (uchar**) &opt_slow_logname, (uchar**) &opt_slow_logname, 0, GET_STR, OPT_ARG,
0, 0, 0, 0, 0, 0},
+ {"slow_query_log_file", OPT_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.",
+ (uchar**) &opt_slow_logname, (uchar**) &opt_slow_logname, 0, GET_STR,
+ REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
{"log-tc", OPT_LOG_TC,
"Path to transaction coordinator log (used for transactions that affect "
"more than one storage engine, when binary log is disabled)",
- (gptr*) &opt_tc_log_file, (gptr*) &opt_tc_log_file, 0, GET_STR,
+ (uchar**) &opt_tc_log_file, (uchar**) &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.",
- (gptr*) &opt_tc_log_size, (gptr*) &opt_tc_log_size, 0, GET_ULONG,
+ (uchar**) &opt_tc_log_size, (uchar**) &opt_tc_log_size, 0, GET_ULONG,
REQUIRED_ARG, TC_LOG_MIN_SIZE, TC_LOG_MIN_SIZE, 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 justs turns on --log-bin instead.",
- (gptr*) &opt_update_logname, (gptr*) &opt_update_logname, 0, GET_STR,
+ (uchar**) &opt_update_logname, (uchar**) &opt_update_logname, 0, GET_STR,
OPT_ARG, 0, 0, 0, 0, 0, 0},
{"log-warnings", 'W', "Log some not critical warnings to the log file.",
- (gptr*) &global_system_variables.log_warnings,
- (gptr*) &max_system_variables.log_warnings, 0, GET_ULONG, OPT_ARG, 1, 0, 0,
+ (uchar**) &global_system_variables.log_warnings,
+ (uchar**) &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.",
- (gptr*) &global_system_variables.low_priority_updates,
- (gptr*) &max_system_variables.low_priority_updates,
+ (uchar**) &global_system_variables.low_priority_updates,
+ (uchar**) &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.",
- (gptr*) &master_connect_retry, (gptr*) &master_connect_retry, 0, GET_UINT,
+ (uchar**) &master_connect_retry, (uchar**) &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.",
- (gptr*) &master_host, (gptr*) &master_host, 0, GET_STR, REQUIRED_ARG, 0, 0,
+ (uchar**) &master_host, (uchar**) &master_host, 0, GET_STR, REQUIRED_ARG, 0, 0,
0, 0, 0, 0},
{"master-info-file", OPT_MASTER_INFO_FILE,
"The location and name of the file that remembers the master and where the I/O replication \
thread is in the master's binlogs.",
- (gptr*) &master_info_file, (gptr*) &master_info_file, 0, GET_STR,
+ (uchar**) &master_info_file, (uchar**) &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.",
- (gptr*)&master_password, (gptr*)&master_password, 0,
+ (uchar**)&master_password, (uchar**)&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.",
- (gptr*) &master_port, (gptr*) &master_port, 0, GET_UINT, REQUIRED_ARG,
+ (uchar**) &master_port, (uchar**) &master_port, 0, GET_UINT, REQUIRED_ARG,
MYSQL_PORT, 0, 0, 0, 0, 0},
{"master-retry-count", OPT_MASTER_RETRY_COUNT,
"The number of tries the slave will make to connect to the master before giving up.",
- (gptr*) &master_retry_count, (gptr*) &master_retry_count, 0, GET_ULONG,
+ (uchar**) &master_retry_count, (uchar**) &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.",
- (gptr*) &master_ssl, (gptr*) &master_ssl, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0,
+ (uchar**) &master_ssl, (uchar**) &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.",
- (gptr*) &master_ssl_ca, (gptr*) &master_ssl_ca, 0, GET_STR, OPT_ARG,
+ (uchar**) &master_ssl_ca, (uchar**) &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.",
- (gptr*) &master_ssl_capath, (gptr*) &master_ssl_capath, 0, GET_STR, OPT_ARG,
+ (uchar**) &master_ssl_capath, (uchar**) &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",
- (gptr*) &master_ssl_cert, (gptr*) &master_ssl_cert, 0, GET_STR, OPT_ARG,
+ (uchar**) &master_ssl_cert, (uchar**) &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.",
- (gptr*) &master_ssl_cipher, (gptr*) &master_ssl_capath, 0, GET_STR, OPT_ARG,
+ (uchar**) &master_ssl_cipher, (uchar**) &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.",
- (gptr*) &master_ssl_key, (gptr*) &master_ssl_key, 0, GET_STR, OPT_ARG,
+ (uchar**) &master_ssl_key, (uchar**) &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.",
- (gptr*) &master_user, (gptr*) &master_user, 0, GET_STR, REQUIRED_ARG, 0, 0,
+ (uchar**) &master_user, (uchar**) &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.",
- (gptr*) &max_binlog_dump_events, (gptr*) &max_binlog_dump_events, 0,
+ (uchar**) &max_binlog_dump_events, (uchar**) &max_binlog_dump_events, 0,
GET_INT, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
#endif /* HAVE_REPLICATION */
- {"memlock", OPT_MEMLOCK, "Lock mysqld in memory.", (gptr*) &locked_in_memory,
- (gptr*) &locked_in_memory, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
- {"merge", OPT_MERGE, "Enable Merge storage engine. Disable with \
---skip-merge.",
- (gptr*) &opt_merge, (gptr*) &opt_merge, 0, GET_BOOL, NO_ARG, 1, 0, 0, 0, 0, 0},
+ {"memlock", OPT_MEMLOCK, "Lock mysqld in memory.", (uchar**) &locked_in_memory,
+ (uchar**) &locked_in_memory, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
{"myisam-recover", OPT_MYISAM_RECOVER,
"Syntax: myisam-recover[=option[,option...]], where option can be DEFAULT, BACKUP, FORCE or QUICK.",
- (gptr*) &myisam_recover_options_str, (gptr*) &myisam_recover_options_str, 0,
+ (uchar**) &myisam_recover_options_str, (uchar**) &myisam_recover_options_str, 0,
GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0},
- {"ndbcluster", OPT_NDBCLUSTER, "Enable NDB Cluster (if this version of MySQL supports it). \
-Disable with --skip-ndbcluster (will save memory).",
- (gptr*) &opt_ndbcluster, (gptr*) &opt_ndbcluster, 0, GET_BOOL, NO_ARG,
- OPT_NDBCLUSTER_DEFAULT, 0, 0, 0, 0, 0},
-#ifdef HAVE_NDBCLUSTER_DB
+#ifdef WITH_NDBCLUSTER_STORAGE_ENGINE
{"ndb-connectstring", OPT_NDB_CONNECTSTRING,
"Connect string for ndbcluster.",
- (gptr*) &opt_ndb_connectstring,
- (gptr*) &opt_ndb_connectstring,
+ (uchar**) &opt_ndb_connectstring,
+ (uchar**) &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]",
- (gptr*) &opt_ndb_mgmd,
- (gptr*) &opt_ndb_mgmd,
+ (uchar**) &opt_ndb_mgmd,
+ (uchar**) &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.",
- (gptr*) &opt_ndb_nodeid,
- (gptr*) &opt_ndb_nodeid,
+ (uchar**) &opt_ndb_nodeid,
+ (uchar**) &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.",
- (gptr*) &global_system_variables.ndb_autoincrement_prefetch_sz,
- (gptr*) &max_system_variables.ndb_autoincrement_prefetch_sz,
+ (uchar**) &global_system_variables.ndb_autoincrement_prefetch_sz,
+ (uchar**) &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.",
- (gptr*) &global_system_variables.ndb_force_send,
- (gptr*) &global_system_variables.ndb_force_send,
+ (uchar**) &global_system_variables.ndb_force_send,
+ (uchar**) &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.",
- (gptr*) &global_system_variables.ndb_force_send,
- (gptr*) &global_system_variables.ndb_force_send,
+ (uchar**) &global_system_variables.ndb_force_send,
+ (uchar**) &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.",
+ (uchar**) &ndb_extra_logging,
+ (uchar**) &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.",
+ (uchar**) &ndb_report_thresh_binlog_epoch_slip,
+ (uchar**) &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.",
+ (uchar**) &ndb_report_thresh_binlog_mem_usage,
+ (uchar**) &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.",
- (gptr*) &global_system_variables.ndb_use_exact_count,
- (gptr*) &global_system_variables.ndb_use_exact_count,
+ (uchar**) &global_system_variables.ndb_use_exact_count,
+ (uchar**) &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.",
- (gptr*) &global_system_variables.ndb_use_exact_count,
- (gptr*) &global_system_variables.ndb_use_exact_count,
+ (uchar**) &global_system_variables.ndb_use_exact_count,
+ (uchar**) &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",
- (gptr*) &global_system_variables.ndb_use_transactions,
- (gptr*) &global_system_variables.ndb_use_transactions,
+ (uchar**) &global_system_variables.ndb_use_transactions,
+ (uchar**) &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.",
- (gptr*) &global_system_variables.ndb_use_transactions,
- (gptr*) &global_system_variables.ndb_use_transactions,
+ (uchar**) &global_system_variables.ndb_use_transactions,
+ (uchar**) &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.",
- (gptr*) &opt_ndb_shm,
- (gptr*) &opt_ndb_shm,
+ (uchar**) &opt_ndb_shm,
+ (uchar**) &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.",
- (gptr*) &opt_ndb_optimized_node_selection,
- (gptr*) &opt_ndb_optimized_node_selection,
+ (uchar**) &opt_ndb_optimized_node_selection,
+ (uchar**) &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 millisecons interval, invalidate the query cache if another MySQL server in the cluster has changed the data in the database.",
- (gptr*) &opt_ndb_cache_check_time, (gptr*) &opt_ndb_cache_check_time, 0, GET_ULONG, REQUIRED_ARG,
+ (uchar**) &opt_ndb_cache_check_time, (uchar**) &opt_ndb_cache_check_time, 0, GET_ULONG, REQUIRED_ARG,
0, 0, LONG_TIMEOUT, 0, 1, 0},
-#endif
+ {"ndb-index-stat-enable", OPT_NDB_INDEX_STAT_ENABLE,
+ "Use ndb index statistics in query optimization.",
+ (uchar**) &global_system_variables.ndb_index_stat_enable,
+ (uchar**) &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).",
+ (uchar**) &global_system_variables.ndb_use_copying_alter_table,
+ (uchar**) &global_system_variables.ndb_use_copying_alter_table,
+ 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
{"new", 'n', "Use very new possible 'unsafe' functions.",
- (gptr*) &global_system_variables.new_mode,
- (gptr*) &max_system_variables.new_mode,
+ (uchar**) &global_system_variables.new_mode,
+ (uchar**) &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 with uses two different table types.",
- (gptr*) &opt_no_mix_types, (gptr*) &opt_no_mix_types, 0, GET_BOOL, NO_ARG,
+ (uchar**) &opt_no_mix_types, (uchar**) &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.",
+ (uchar**) &global_system_variables.old_alter_table,
+ (uchar**) &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).",
- (gptr*) &global_system_variables.old_passwords,
- (gptr*) &max_system_variables.old_passwords, 0, GET_BOOL, NO_ARG,
+ (uchar**) &global_system_variables.old_passwords,
+ (uchar**) &max_system_variables.old_passwords, 0, GET_BOOL, NO_ARG,
0, 0, 0, 0, 0, 0},
-#ifdef ONE_THREAD
{"one-thread", OPT_ONE_THREAD,
- "Only use one thread (for debugging under Linux).", 0, 0, 0, GET_NO_ARG,
- NO_ARG, 0, 0, 0, 0, 0, 0},
-#endif
+ "(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,
"Enable old-style user limits (before 5.0.3 user resources were counted per each user+host vs. per account)",
- (gptr*) &opt_old_style_user_limits, (gptr*) &opt_old_style_user_limits,
+ (uchar**) &opt_old_style_user_limits, (uchar**) &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.",
- (gptr*) &pidfile_name_ptr, (gptr*) &pidfile_name_ptr, 0, GET_STR,
+ (uchar**) &pidfile_name_ptr, (uchar**) &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, "
@@ -5629,25 +6140,31 @@ Disable with --skip-ndbcluster (will save memory).",
"/etc/services, "
#endif
"built-in default (" STRINGIFY_ARG(MYSQL_PORT) ").",
- (gptr*) &mysqld_port,
- (gptr*) &mysqld_port, 0, GET_UINT, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+ (uchar**) &mysqld_port,
+ (uchar**) &mysqld_port, 0, GET_UINT, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
{"port-open-timeout", OPT_PORT_OPEN_TIMEOUT,
"Maximum time in seconds to wait for the port to become free. "
- "(Default: no wait)", (gptr*) &mysqld_port_timeout,
- (gptr*) &mysqld_port_timeout, 0, GET_UINT, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+ "(Default: no wait)", (uchar**) &mysqld_port_timeout,
+ (uchar**) &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",
+ (uchar**) &global_system_variables.profiling_history_size,
+ (uchar**) &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.",
- (gptr*) &opt_relay_logname, (gptr*) &opt_relay_logname, 0,
+ (uchar**) &opt_relay_logname, (uchar**) &opt_relay_logname, 0,
GET_STR_ALLOC, 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.",
- (gptr*) &opt_relaylog_index_name, (gptr*) &opt_relaylog_index_name, 0,
+ (uchar**) &opt_relaylog_index_name, (uchar**) &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.",
- (gptr*) &relay_log_info_file, (gptr*) &relay_log_info_file, 0, GET_STR,
+ (uchar**) &relay_log_info_file, (uchar**) &relay_log_info_file, 0, GET_STR,
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, once for each database. Note that this will only work if you do not use cross-database queries such as UPDATE some_db.some_table SET foo='bar' while having selected a different or no database. If you need cross database updates to work, make sure you have 3.23.28 or later, and use replicate-wild-do-table=db_name.%.",
@@ -5669,8 +6186,8 @@ thread is in the relay logs.",
"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.",
- (gptr*) &replicate_same_server_id,
- (gptr*) &replicate_same_server_id,
+ (uchar**) &replicate_same_server_id,
+ (uchar**) &replicate_same_server_id,
0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
#endif
{"replicate-wild-do-table", OPT_REPLICATE_WILD_DO_TABLE,
@@ -5682,19 +6199,19 @@ Can't be set to 1 if --log-slave-updates is used.",
// 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 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.",
- (gptr*) &report_host, (gptr*) &report_host, 0, GET_STR, REQUIRED_ARG, 0, 0,
+ (uchar**) &report_host, (uchar**) &report_host, 0, GET_STR, REQUIRED_ARG, 0, 0,
0, 0, 0, 0},
{"report-password", OPT_REPORT_PASSWORD, "Undocumented.",
- (gptr*) &report_password, (gptr*) &report_password, 0, GET_STR,
+ (uchar**) &report_password, (uchar**) &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.",
- (gptr*) &report_port, (gptr*) &report_port, 0, GET_UINT, REQUIRED_ARG,
+ (uchar**) &report_port, (uchar**) &report_port, 0, GET_UINT, REQUIRED_ARG,
MYSQL_PORT, 0, 0, 0, 0, 0},
- {"report-user", OPT_REPORT_USER, "Undocumented.", (gptr*) &report_user,
- (gptr*) &report_user, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+ {"report-user", OPT_REPORT_USER, "Undocumented.", (uchar**) &report_user,
+ (uchar**) &report_user, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
{"rpl-recovery-rank", OPT_RPL_RECOVERY_RANK, "Undocumented.",
- (gptr*) &rpl_recovery_rank, (gptr*) &rpl_recovery_rank, 0, GET_ULONG,
+ (uchar**) &rpl_recovery_rank, (uchar**) &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},
@@ -5705,43 +6222,43 @@ Can't be set to 1 if --log-slave-updates is used.",
#endif
{"safe-user-create", OPT_SAFE_USER_CREATE,
"Don't allow new user creation by the user who has no write privileges to the mysql.user table.",
- (gptr*) &opt_safe_user_create, (gptr*) &opt_safe_user_create, 0, GET_BOOL,
+ (uchar**) &opt_safe_user_create, (uchar**) &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.",
- (gptr*) &opt_secure_auth, (gptr*) &opt_secure_auth, 0, GET_BOOL, NO_ARG,
+ (uchar**) &opt_secure_auth, (uchar**) &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",
- (gptr*) &opt_secure_file_priv, (gptr*) &opt_secure_file_priv, 0,
+ (uchar**) &opt_secure_file_priv, (uchar**) &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.",
- (gptr*) &server_id, (gptr*) &server_id, 0, GET_ULONG, REQUIRED_ARG, 0, 0, 0,
+ (uchar**) &server_id, (uchar**) &server_id, 0, GET_ULONG, REQUIRED_ARG, 0, 0, 0,
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.",(gptr*) &opt_enable_shared_memory, (gptr*) &opt_enable_shared_memory,
+ "Enable the shared memory.",(uchar**) &opt_enable_shared_memory, (uchar**) &opt_enable_shared_memory,
0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
#endif
#ifdef HAVE_SMEM
{"shared-memory-base-name",OPT_SHARED_MEMORY_BASE_NAME,
- "Base name of shared memory.", (gptr*) &shared_memory_base_name, (gptr*) &shared_memory_base_name,
+ "Base name of shared memory.", (uchar**) &shared_memory_base_name, (uchar**) &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 user and password in SHOW SLAVE HOSTS on this master",
- (gptr*) &opt_show_slave_auth_info, (gptr*) &opt_show_slave_auth_info, 0,
+ (uchar**) &opt_show_slave_auth_info, (uchar**) &opt_show_slave_auth_info, 0,
GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
#ifndef DISABLE_GRANT_OPTIONS
{"skip-grant-tables", OPT_SKIP_GRANT,
"Start without grant tables. This gives all users FULL ACCESS to all tables!",
- (gptr*) &opt_noacl, (gptr*) &opt_noacl, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0,
+ (uchar**) &opt_noacl, (uchar**) &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,
@@ -5768,34 +6285,40 @@ Can't be set to 1 if --log-slave-updates is used.",
"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,
- "If set, slave is not autostarted.", (gptr*) &opt_skip_slave_start,
- (gptr*) &opt_skip_slave_start, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
+ "If set, slave is not autostarted.", (uchar**) &opt_skip_slave_start,
+ (uchar**) &opt_skip_slave_start, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
{"skip-stack-trace", OPT_SKIP_STACK_TRACE,
"Don't print a stack trace on failure.", 0, 0, 0, GET_NO_ARG, 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.", 0, 0, 0, GET_NO_ARG, NO_ARG,
+ "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.",
- (gptr*) &slave_load_tmpdir, (gptr*) &slave_load_tmpdir, 0, GET_STR_ALLOC,
+ (uchar**) &slave_load_tmpdir, (uchar**) &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 returns an error from the provided list.",
+ "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.",
+ (uchar**) &slave_exec_mode_str, (uchar**) &slave_exec_mode_str, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
#endif
+ {"slow-query-log", OPT_SLOW_LOG,
+ "Enable|disable slow query log", (uchar**) &opt_slow_log,
+ (uchar**) &opt_slow_log, 0, GET_BOOL, OPT_ARG, 0, 0, 0, 0, 0, 0},
{"socket", OPT_SOCKET, "Socket file to use for connection.",
- (gptr*) &mysqld_unix_port, (gptr*) &mysqld_unix_port, 0, GET_STR,
+ (uchar**) &mysqld_unix_port, (uchar**) &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.",
- (gptr*) &opt_sporadic_binlog_dump_fail,
- (gptr*) &opt_sporadic_binlog_dump_fail, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0,
+ (uchar**) &opt_sporadic_binlog_dump_fail,
+ (uchar**) &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,
@@ -5804,7 +6327,7 @@ 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.",
- (gptr*) &sql_mode_str, (gptr*) &sql_mode_str, 0, GET_STR, REQUIRED_ARG, 0,
+ (uchar**) &sql_mode_str, (uchar**) &sql_mode_str, 0, GET_STR, REQUIRED_ARG, 0,
0, 0, 0, 0, 0},
#ifdef HAVE_OPENSSL
#include "sslopt-longopts.h"
@@ -5815,132 +6338,119 @@ log and this option does nothing anymore.",
NO_ARG, 0, 0, 0, 0, 0, 0},
#endif
{"symbolic-links", 's', "Enable symbolic link support.",
- (gptr*) &my_use_symdir, (gptr*) &my_use_symdir, 0, GET_BOOL, NO_ARG,
+ (uchar**) &my_use_symdir, (uchar**) &my_use_symdir, 0, GET_BOOL, NO_ARG,
+ /*
+ The system call realpath() produces warnings under valgrind and
+ purify. These are not suppressed: instead we disable symlinks
+ option if compiled with valgrind support.
+ */
IF_PURIFY(0,1), 0, 0, 0, 0, 0},
{"sysdate-is-now", OPT_SYSDATE_IS_NOW,
"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.",
- (gptr*) &global_system_variables.sysdate_is_now,
+ (uchar**) &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,
"Decision to use in heuristic recover process. Possible values are COMMIT or ROLLBACK.",
- (gptr*) &opt_tc_heuristic_recover, (gptr*) &opt_tc_heuristic_recover,
+ (uchar**) &opt_tc_heuristic_recover, (uchar**) &opt_tc_heuristic_recover,
0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
{"temp-pool", OPT_TEMP_POOL,
+#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.",
- (gptr*) &use_temp_pool, (gptr*) &use_temp_pool, 0, GET_BOOL, NO_ARG, 1,
+#else
+ "This option is ignored on this OS.",
+#endif
+ (uchar**) &use_temp_pool, (uchar**) &use_temp_pool, 0, GET_BOOL, NO_ARG, 1,
0, 0, 0, 0, 0},
+
{"timed_mutexes", OPT_TIMED_MUTEXES,
"Specify whether to time mutexes (only InnoDB mutexes are currently supported)",
- (gptr*) &timed_mutexes, (gptr*) &timed_mutexes, 0, GET_BOOL, NO_ARG, 0,
+ (uchar**) &timed_mutexes, (uchar**) &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(OS2) || defined(__NETWARE__)
+#if defined(__WIN__) || defined(__NETWARE__)
"semicolon (;)"
#else
"colon (:)"
#endif
", in this case they are used in a round-robin fashion.",
- (gptr*) &opt_mysql_tmpdir,
- (gptr*) &opt_mysql_tmpdir, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+ (uchar**) &opt_mysql_tmpdir,
+ (uchar**) &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", 's', "Enable symbolic link support. Deprecated option; use --symbolic-links instead.",
- (gptr*) &my_use_symdir, (gptr*) &my_use_symdir, 0, GET_BOOL, NO_ARG,
- /*
- The system call realpath() produces warnings under valgrind and
- purify. These are not suppressed: instead we disable symlinks
- option if compiled with valgrind support.
- */
+ (uchar**) &my_use_symdir, (uchar**) &my_use_symdir, 0, GET_BOOL, NO_ARG,
IF_PURIFY(0,1), 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",
- (gptr*) &opt_verbose, (gptr*) &opt_verbose, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0,
+ (uchar**) &opt_verbose, (uchar**) &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", 'W', "Deprecated; use --log-warnings instead.",
- (gptr*) &global_system_variables.log_warnings,
- (gptr*) &max_system_variables.log_warnings, 0, GET_ULONG, OPT_ARG,
+ (uchar**) &global_system_variables.log_warnings,
+ (uchar**) &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.",
- (gptr*) &back_log, (gptr*) &back_log, 0, GET_ULONG,
+ (uchar**) &back_log, (uchar**) &back_log, 0, GET_ULONG,
REQUIRED_ARG, 50, 1, 65535, 0, 1, 0 },
-#ifdef HAVE_BERKELEY_DB
- { "bdb_cache_size", OPT_BDB_CACHE_SIZE,
- "The buffer that is allocated to cache index and rows for BDB tables.",
- (gptr*) &berkeley_cache_size, (gptr*) &berkeley_cache_size, 0, GET_ULONG,
- REQUIRED_ARG, KEY_CACHE_SIZE, 20*1024, ULONG_MAX, 0, IO_SIZE, 0},
- /* QQ: The following should be removed soon! (bdb_max_lock preferred) */
- {"bdb_lock_max", OPT_BDB_MAX_LOCK, "Synonym for bdb_max_lock.",
- (gptr*) &berkeley_max_lock, (gptr*) &berkeley_max_lock, 0, GET_ULONG,
- REQUIRED_ARG, 10000, 0, ULONG_MAX, 0, 1, 0},
- {"bdb_log_buffer_size", OPT_BDB_LOG_BUFFER_SIZE,
- "The buffer that is allocated to cache index and rows for BDB tables.",
- (gptr*) &berkeley_log_buffer_size, (gptr*) &berkeley_log_buffer_size, 0,
- GET_ULONG, REQUIRED_ARG, 256*1024L, 256*1024L, ULONG_MAX, 0, 1024, 0},
- {"bdb_max_lock", OPT_BDB_MAX_LOCK,
- "The maximum number of locks you can have active on a BDB table.",
- (gptr*) &berkeley_max_lock, (gptr*) &berkeley_max_lock, 0, GET_ULONG,
- REQUIRED_ARG, 10000, 0, ULONG_MAX, 0, 1, 0},
-#endif /* HAVE_BERKELEY_DB */
{"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.",
- (gptr*) &binlog_cache_size, (gptr*) &binlog_cache_size, 0, GET_ULONG,
+ (uchar**) &binlog_cache_size, (uchar**) &binlog_cache_size, 0, GET_ULONG,
REQUIRED_ARG, 32*1024L, IO_SIZE, ULONG_MAX, 0, IO_SIZE, 0},
{"bulk_insert_buffer_size", OPT_BULK_INSERT_BUFFER_SIZE,
"Size of tree cache used in bulk insert optimisation. Note that this is a limit per thread!",
- (gptr*) &global_system_variables.bulk_insert_buff_size,
- (gptr*) &max_system_variables.bulk_insert_buff_size,
+ (uchar**) &global_system_variables.bulk_insert_buff_size,
+ (uchar**) &max_system_variables.bulk_insert_buff_size,
0, GET_ULONG, REQUIRED_ARG, 8192*1024, 0, 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'.",
- (gptr*) &connect_timeout, (gptr*) &connect_timeout,
+ (uchar**) &connect_timeout, (uchar**) &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).",
- (gptr*) &opt_date_time_formats[MYSQL_TIMESTAMP_DATE],
- (gptr*) &opt_date_time_formats[MYSQL_TIMESTAMP_DATE],
+ (uchar**) &opt_date_time_formats[MYSQL_TIMESTAMP_DATE],
+ (uchar**) &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).",
- (gptr*) &opt_date_time_formats[MYSQL_TIMESTAMP_DATETIME],
- (gptr*) &opt_date_time_formats[MYSQL_TIMESTAMP_DATETIME],
+ (uchar**) &opt_date_time_formats[MYSQL_TIMESTAMP_DATETIME],
+ (uchar**) &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.",
- (gptr*) &global_system_variables.default_week_format,
- (gptr*) &max_system_variables.default_week_format,
+ (uchar**) &global_system_variables.default_week_format,
+ (uchar**) &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.",
- (gptr*) &delayed_insert_limit, (gptr*) &delayed_insert_limit, 0, GET_ULONG,
+ (uchar**) &delayed_insert_limit, (uchar**) &delayed_insert_limit, 0, GET_ULONG,
REQUIRED_ARG, DELAYED_LIMIT, 1, 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.",
- (gptr*) &delayed_insert_timeout, (gptr*) &delayed_insert_timeout, 0,
+ (uchar**) &delayed_insert_timeout, (uchar**) &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.",
- (gptr*) &delayed_queue_size, (gptr*) &delayed_queue_size, 0, GET_ULONG,
+ (uchar**) &delayed_queue_size, (uchar**) &delayed_queue_size, 0, GET_ULONG,
REQUIRED_ARG, DELAYED_QUEUE_SIZE, 1, ULONG_MAX, 0, 1, 0},
{"div_precision_increment", OPT_DIV_PRECINCREMENT,
"Precision of the result of '/' operator will be increased on that value.",
- (gptr*) &global_system_variables.div_precincrement,
- (gptr*) &max_system_variables.div_precincrement, 0, GET_ULONG,
+ (uchar**) &global_system_variables.div_precincrement,
+ (uchar**) &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.",
- (gptr*) &expire_logs_days,
- (gptr*) &expire_logs_days, 0, GET_ULONG,
+ (uchar**) &expire_logs_days,
+ (uchar**) &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.",
- (gptr*) &flush_time, (gptr*) &flush_time, 0, GET_ULONG, REQUIRED_ARG,
+ (uchar**) &flush_time, (uchar**) &flush_time, 0, GET_ULONG, REQUIRED_ARG,
FLUSH_TIME, 0, LONG_TIMEOUT, 0, 1, 0},
{ "ft_boolean_syntax", OPT_FT_BOOLEAN_SYNTAX,
"List of operators for MATCH ... AGAINST ( ... IN BOOLEAN MODE)",
@@ -5948,157 +6458,75 @@ log and this option does nothing anymore.",
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.",
- (gptr*) &ft_max_word_len, (gptr*) &ft_max_word_len, 0, GET_ULONG,
+ (uchar**) &ft_max_word_len, (uchar**) &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.",
- (gptr*) &ft_min_word_len, (gptr*) &ft_min_word_len, 0, GET_ULONG,
+ (uchar**) &ft_min_word_len, (uchar**) &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",
- (gptr*) &ft_query_expansion_limit, (gptr*) &ft_query_expansion_limit, 0, GET_ULONG,
+ (uchar**) &ft_query_expansion_limit, (uchar**) &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.",
- (gptr*) &ft_stopword_file, (gptr*) &ft_stopword_file, 0, GET_STR,
+ (uchar**) &ft_stopword_file, (uchar**) &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.",
- (gptr*) &global_system_variables.group_concat_max_len,
- (gptr*) &max_system_variables.group_concat_max_len, 0, GET_ULONG,
+ "The maximum length of the result of function group_concat.",
+ (uchar**) &global_system_variables.group_concat_max_len,
+ (uchar**) &max_system_variables.group_concat_max_len, 0, GET_ULONG,
REQUIRED_ARG, 1024, 4, ULONG_MAX, 0, 1, 0},
-#ifdef HAVE_INNOBASE_DB
- {"innodb_additional_mem_pool_size", OPT_INNODB_ADDITIONAL_MEM_POOL_SIZE,
- "Size of a memory pool InnoDB uses to store data dictionary information and other internal data structures.",
- (gptr*) &innobase_additional_mem_pool_size,
- (gptr*) &innobase_additional_mem_pool_size, 0, GET_LONG, REQUIRED_ARG,
- 1*1024*1024L, 512*1024L, LONG_MAX, 0, 1024, 0},
- {"innodb_autoextend_increment", OPT_INNODB_AUTOEXTEND_INCREMENT,
- "Data file autoextend increment in megabytes",
- (gptr*) &srv_auto_extend_increment,
- (gptr*) &srv_auto_extend_increment,
- 0, GET_ULONG, REQUIRED_ARG, 8L, 1L, 1000L, 0, 1L, 0},
- {"innodb_buffer_pool_awe_mem_mb", OPT_INNODB_BUFFER_POOL_AWE_MEM_MB,
- "If Windows AWE is used, the size of InnoDB buffer pool allocated from the AWE memory.",
- (gptr*) &innobase_buffer_pool_awe_mem_mb, (gptr*) &innobase_buffer_pool_awe_mem_mb, 0,
- GET_LONG, REQUIRED_ARG, 0, 0, 63000, 0, 1, 0},
- {"innodb_buffer_pool_size", OPT_INNODB_BUFFER_POOL_SIZE,
- "The size of the memory buffer InnoDB uses to cache data and indexes of its tables.",
- (gptr*) &innobase_buffer_pool_size, (gptr*) &innobase_buffer_pool_size, 0,
- GET_LL, REQUIRED_ARG, 8*1024*1024L, 1024*1024L, LONGLONG_MAX, 0,
- 1024*1024L, 0},
- {"innodb_commit_concurrency", OPT_INNODB_COMMIT_CONCURRENCY,
- "Helps in performance tuning in heavily concurrent environments.",
- (gptr*) &srv_commit_concurrency, (gptr*) &srv_commit_concurrency,
- 0, GET_ULONG, REQUIRED_ARG, 0, 0, 1000, 0, 1, 0},
- {"innodb_concurrency_tickets", OPT_INNODB_CONCURRENCY_TICKETS,
- "Number of times a thread is allowed to enter InnoDB within the same \
- SQL query after it has once got the ticket",
- (gptr*) &srv_n_free_tickets_to_enter,
- (gptr*) &srv_n_free_tickets_to_enter,
- 0, GET_ULONG, REQUIRED_ARG, 500L, 1L, ULONG_MAX, 0, 1L, 0},
- {"innodb_file_io_threads", OPT_INNODB_FILE_IO_THREADS,
- "Number of file I/O threads in InnoDB.", (gptr*) &innobase_file_io_threads,
- (gptr*) &innobase_file_io_threads, 0, GET_LONG, REQUIRED_ARG, 4, 4, 64, 0,
- 1, 0},
- {"innodb_force_recovery", OPT_INNODB_FORCE_RECOVERY,
- "Helps to save your data in case the disk image of the database becomes corrupt.",
- (gptr*) &innobase_force_recovery, (gptr*) &innobase_force_recovery, 0,
- GET_LONG, REQUIRED_ARG, 0, 0, 6, 0, 1, 0},
- {"innodb_lock_wait_timeout", OPT_INNODB_LOCK_WAIT_TIMEOUT,
- "Timeout in seconds an InnoDB transaction may wait for a lock before being rolled back.",
- (gptr*) &innobase_lock_wait_timeout, (gptr*) &innobase_lock_wait_timeout,
- 0, GET_LONG, REQUIRED_ARG, 50, 1, 1024 * 1024 * 1024, 0, 1, 0},
- {"innodb_log_buffer_size", OPT_INNODB_LOG_BUFFER_SIZE,
- "The size of the buffer which InnoDB uses to write log to the log files on disk.",
- (gptr*) &innobase_log_buffer_size, (gptr*) &innobase_log_buffer_size, 0,
- GET_LONG, REQUIRED_ARG, 1024*1024L, 256*1024L, LONG_MAX, 0, 1024, 0},
- {"innodb_log_file_size", OPT_INNODB_LOG_FILE_SIZE,
- "Size of each log file in a log group.",
- (gptr*) &innobase_log_file_size, (gptr*) &innobase_log_file_size, 0,
- GET_LL, REQUIRED_ARG, 5*1024*1024L, 1*1024*1024L, LONGLONG_MAX, 0,
- 1024*1024L, 0},
- {"innodb_log_files_in_group", OPT_INNODB_LOG_FILES_IN_GROUP,
- "Number of log files in the log group. InnoDB writes to the files in a circular fashion. Value 3 is recommended here.",
- (gptr*) &innobase_log_files_in_group, (gptr*) &innobase_log_files_in_group,
- 0, GET_LONG, REQUIRED_ARG, 2, 2, 100, 0, 1, 0},
- {"innodb_mirrored_log_groups", OPT_INNODB_MIRRORED_LOG_GROUPS,
- "Number of identical copies of log groups we keep for the database. Currently this should be set to 1.",
- (gptr*) &innobase_mirrored_log_groups,
- (gptr*) &innobase_mirrored_log_groups, 0, GET_LONG, REQUIRED_ARG, 1, 1, 10,
- 0, 1, 0},
- {"innodb_open_files", OPT_INNODB_OPEN_FILES,
- "How many files at the maximum InnoDB keeps open at the same time.",
- (gptr*) &innobase_open_files, (gptr*) &innobase_open_files, 0,
- GET_LONG, REQUIRED_ARG, 300L, 10L, LONG_MAX, 0, 1L, 0},
- {"innodb_sync_spin_loops", OPT_INNODB_SYNC_SPIN_LOOPS,
- "Count of spin-loop rounds in InnoDB mutexes",
- (gptr*) &srv_n_spin_wait_rounds,
- (gptr*) &srv_n_spin_wait_rounds,
- 0, GET_ULONG, REQUIRED_ARG, 20L, 0L, ULONG_MAX, 0, 1L, 0},
- {"innodb_thread_concurrency", OPT_INNODB_THREAD_CONCURRENCY,
- "Helps in performance tuning in heavily concurrent environments. "
- "Sets the maximum number of threads allowed inside InnoDB. Value 0"
- " will disable the thread throttling.",
- (gptr*) &srv_thread_concurrency, (gptr*) &srv_thread_concurrency,
- 0, GET_ULONG, REQUIRED_ARG, 8, 0, 1000, 0, 1, 0},
- {"innodb_thread_sleep_delay", OPT_INNODB_THREAD_SLEEP_DELAY,
- "Time of innodb thread sleeping before joining InnoDB queue (usec). Value 0"
- " disable a sleep",
- (gptr*) &srv_thread_sleep_delay,
- (gptr*) &srv_thread_sleep_delay,
- 0, GET_ULONG, REQUIRED_ARG, 10000L, 0L, ULONG_MAX, 0, 1L, 0},
-#endif /* HAVE_INNOBASE_DB */
{"interactive_timeout", OPT_INTERACTIVE_TIMEOUT,
"The number of seconds the server waits for activity on an interactive connection before closing it.",
- (gptr*) &global_system_variables.net_interactive_timeout,
- (gptr*) &max_system_variables.net_interactive_timeout, 0,
+ (uchar**) &global_system_variables.net_interactive_timeout,
+ (uchar**) &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 full joins.",
- (gptr*) &global_system_variables.join_buff_size,
- (gptr*) &max_system_variables.join_buff_size, 0, GET_ULONG,
- REQUIRED_ARG, 128*1024L, IO_SIZE*2+MALLOC_OVERHEAD, UINT_MAX32, MALLOC_OVERHEAD,
- IO_SIZE, 0},
+ (uchar**) &global_system_variables.join_buff_size,
+ (uchar**) &max_system_variables.join_buff_size, 0, GET_ULONG,
+ REQUIRED_ARG, 128*1024L, IO_SIZE*2+MALLOC_OVERHEAD, ULONG_MAX,
+ MALLOC_OVERHEAD, IO_SIZE, 0},
{"keep_files_on_create", OPT_KEEP_FILES_ON_CREATE,
"Don't overwrite stale .MYD and .MYI even if no directory is specified.",
- (gptr*) &global_system_variables.keep_files_on_create,
- (gptr*) &max_system_variables.keep_files_on_create,
+ (uchar**) &global_system_variables.keep_files_on_create,
+ (uchar**) &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; 64M on a 256M machine that mainly runs MySQL is quite common.",
- (gptr*) &dflt_key_cache_var.param_buff_size,
- (gptr*) 0,
+ (uchar**) &dflt_key_cache_var.param_buff_size,
+ (uchar**) 0,
0, (GET_ULL | GET_ASK_ADDR),
- REQUIRED_ARG, KEY_CACHE_SIZE, MALLOC_OVERHEAD, ~(ulong) 0, MALLOC_OVERHEAD,
+ REQUIRED_ARG, KEY_CACHE_SIZE, MALLOC_OVERHEAD, 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",
- (gptr*) &dflt_key_cache_var.param_age_threshold,
- (gptr*) 0,
- 0, (GET_ULONG | GET_ASK_ADDR), REQUIRED_ARG,
+ (uchar**) &dflt_key_cache_var.param_age_threshold,
+ (uchar**) 0,
+ 0, (GET_ULONG | GET_ASK_ADDR), REQUIRED_ARG,
300, 100, ULONG_MAX, 0, 100, 0},
{"key_cache_block_size", OPT_KEY_CACHE_BLOCK_SIZE,
"The default size of key cache blocks",
- (gptr*) &dflt_key_cache_var.param_block_size,
- (gptr*) 0,
+ (uchar**) &dflt_key_cache_var.param_block_size,
+ (uchar**) 0,
0, (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",
- (gptr*) &dflt_key_cache_var.param_division_limit,
- (gptr*) 0,
+ (uchar**) &dflt_key_cache_var.param_division_limit,
+ (uchar**) 0,
0, (GET_ULONG | GET_ASK_ADDR) , REQUIRED_ARG, 100,
1, 100, 0, 1, 0},
{"long_query_time", OPT_LONG_QUERY_TIME,
- "Log all queries that have taken more than long_query_time seconds to execute to file.",
- (gptr*) &global_system_variables.long_query_time,
- (gptr*) &max_system_variables.long_query_time, 0, GET_ULONG,
- REQUIRED_ARG, 10, 1, LONG_TIMEOUT, 0, 1, 0},
+ "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.",
+ (uchar**) &long_query_time, (uchar**) &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",
- (gptr*) &lower_case_table_names,
- (gptr*) &lower_case_table_names, 0, GET_UINT, OPT_ARG,
+ (uchar**) &lower_case_table_names,
+ (uchar**) &lower_case_table_names, 0, GET_UINT, OPT_ARG,
#ifdef FN_NO_CASE_SENCE
1
#else
@@ -6107,473 +6535,765 @@ log and this option does nothing anymore.",
, 0, 2, 0, 1, 0},
{"max_allowed_packet", OPT_MAX_ALLOWED_PACKET,
"Max packetlength to send/receive from to server.",
- (gptr*) &global_system_variables.max_allowed_packet,
- (gptr*) &max_system_variables.max_allowed_packet, 0, GET_ULONG,
+ (uchar**) &global_system_variables.max_allowed_packet,
+ (uchar**) &max_system_variables.max_allowed_packet, 0, GET_ULONG,
REQUIRED_ARG, 1024*1024L, 1024, 1024L*1024L*1024L, 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.",
- (gptr*) &max_binlog_cache_size, (gptr*) &max_binlog_cache_size, 0,
+ (uchar**) &max_binlog_cache_size, (uchar**) &max_binlog_cache_size, 0,
GET_ULONG, REQUIRED_ARG, ULONG_MAX, IO_SIZE, ULONG_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.",
- (gptr*) &max_binlog_size, (gptr*) &max_binlog_size, 0, GET_ULONG,
+ (uchar**) &max_binlog_size, (uchar**) &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.",
- (gptr*) &max_connect_errors, (gptr*) &max_connect_errors, 0, GET_ULONG,
+ (uchar**) &max_connect_errors, (uchar**) &max_connect_errors, 0, GET_ULONG,
REQUIRED_ARG, MAX_CONNECT_ERRORS, 1, 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.", (gptr*) &max_connections,
- (gptr*) &max_connections, 0, GET_ULONG, REQUIRED_ARG, 100, 1, 16384, 0, 1,
+ "The number of simultaneous clients allowed.", (uchar**) &max_connections,
+ (uchar**) &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.",
- (gptr*) &global_system_variables.max_insert_delayed_threads,
- (gptr*) &max_system_variables.max_insert_delayed_threads,
+ (uchar**) &global_system_variables.max_insert_delayed_threads,
+ (uchar**) &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.",
- (gptr*) &global_system_variables.max_error_count,
- (gptr*) &max_system_variables.max_error_count,
+ (uchar**) &global_system_variables.max_error_count,
+ (uchar**) &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.",
- (gptr*) &global_system_variables.max_heap_table_size,
- (gptr*) &max_system_variables.max_heap_table_size, 0, GET_ULL,
+ (uchar**) &global_system_variables.max_heap_table_size,
+ (uchar**) &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.",
- (gptr*) &global_system_variables.max_join_size,
- (gptr*) &max_system_variables.max_join_size, 0, GET_HA_ROWS, REQUIRED_ARG,
+ (uchar**) &global_system_variables.max_join_size,
+ (uchar**) &max_system_variables.max_join_size, 0, GET_HA_ROWS, REQUIRED_ARG,
~0L, 1, ~0L, 0, 1, 0},
{"max_length_for_sort_data", OPT_MAX_LENGTH_FOR_SORT_DATA,
"Max number of bytes in sorted records.",
- (gptr*) &global_system_variables.max_length_for_sort_data,
- (gptr*) &max_system_variables.max_length_for_sort_data, 0, GET_ULONG,
+ (uchar**) &global_system_variables.max_length_for_sort_data,
+ (uchar**) &max_system_variables.max_length_for_sort_data, 0, GET_ULONG,
REQUIRED_ARG, 1024, 4, 8192*1024L, 0, 1, 0},
{"max_prepared_stmt_count", OPT_MAX_PREPARED_STMT_COUNT,
"Maximum number of prepared statements in the server.",
- (gptr*) &max_prepared_stmt_count, (gptr*) &max_prepared_stmt_count,
+ (uchar**) &max_prepared_stmt_count, (uchar**) &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.",
- (gptr*) &max_relay_log_size, (gptr*) &max_relay_log_size, 0, GET_ULONG,
+ (uchar**) &max_relay_log_size, (uchar**) &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",
- (gptr*) &global_system_variables.max_seeks_for_key,
- (gptr*) &max_system_variables.max_seeks_for_key, 0, GET_ULONG,
+ (uchar**) &global_system_variables.max_seeks_for_key,
+ (uchar**) &max_system_variables.max_seeks_for_key, 0, GET_ULONG,
REQUIRED_ARG, 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).",
- (gptr*) &global_system_variables.max_sort_length,
- (gptr*) &max_system_variables.max_sort_length, 0, GET_ULONG,
+ (uchar**) &global_system_variables.max_sort_length,
+ (uchar**) &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).",
- (gptr*) &global_system_variables.max_sp_recursion_depth,
- (gptr*) &max_system_variables.max_sp_recursion_depth, 0, GET_ULONG,
+ (uchar**) &global_system_variables.max_sp_recursion_depth,
+ (uchar**) &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.",
- (gptr*) &global_system_variables.max_tmp_tables,
- (gptr*) &max_system_variables.max_tmp_tables, 0, GET_ULONG,
+ (uchar**) &global_system_variables.max_tmp_tables,
+ (uchar**) &max_system_variables.max_tmp_tables, 0, GET_ULONG,
REQUIRED_ARG, 32, 1, 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).",
- (gptr*) &max_user_connections, (gptr*) &max_user_connections, 0, GET_UINT,
- REQUIRED_ARG, 0, 0, (uint) ~0, 0, 1, 0},
+ (uchar**) &max_user_connections, (uchar**) &max_user_connections, 0, GET_UINT,
+ REQUIRED_ARG, 0, 0, UINT_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.",
- (gptr*) &max_write_lock_count, (gptr*) &max_write_lock_count, 0, GET_ULONG,
+ (uchar**) &max_write_lock_count, (uchar**) &max_write_lock_count, 0, GET_ULONG,
REQUIRED_ARG, 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.",
+ (uchar**) &global_system_variables.min_examined_row_limit,
+ (uchar**) &max_system_variables.min_examined_row_limit, 0, GET_ULONG,
+ REQUIRED_ARG, 0, 0, ULONG_MAX, 0, 1L, 0},
{"multi_range_count", OPT_MULTI_RANGE_COUNT,
"Number of key ranges to request at once.",
- (gptr*) &global_system_variables.multi_range_count,
- (gptr*) &max_system_variables.multi_range_count, 0,
+ (uchar**) &global_system_variables.multi_range_count,
+ (uchar**) &max_system_variables.multi_range_count, 0,
GET_ULONG, REQUIRED_ARG, 256, 1, ULONG_MAX, 0, 1, 0},
{"myisam_block_size", OPT_MYISAM_BLOCK_SIZE,
"Block size to be used for MyISAM index pages.",
- (gptr*) &opt_myisam_block_size,
- (gptr*) &opt_myisam_block_size, 0, GET_ULONG, REQUIRED_ARG,
+ (uchar**) &opt_myisam_block_size,
+ (uchar**) &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.",
- (gptr*) &myisam_data_pointer_size,
- (gptr*) &myisam_data_pointer_size, 0, GET_ULONG, REQUIRED_ARG,
+ (uchar**) &myisam_data_pointer_size,
+ (uchar**) &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,
"Deprecated option",
- (gptr*) &global_system_variables.myisam_max_extra_sort_file_size,
- (gptr*) &max_system_variables.myisam_max_extra_sort_file_size,
+ (uchar**) &global_system_variables.myisam_max_extra_sort_file_size,
+ (uchar**) &max_system_variables.myisam_max_extra_sort_file_size,
0, GET_ULL, REQUIRED_ARG, (ulonglong) MI_MAX_TEMP_LENGTH,
0, (ulonglong) 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.",
- (gptr*) &global_system_variables.myisam_max_sort_file_size,
- (gptr*) &max_system_variables.myisam_max_sort_file_size, 0,
+ (uchar**) &global_system_variables.myisam_max_sort_file_size,
+ (uchar**) &max_system_variables.myisam_max_sort_file_size, 0,
GET_ULL, REQUIRED_ARG, (longlong) LONG_MAX, 0, (ulonglong) MAX_FILE_SIZE,
0, 1024*1024, 0},
{"myisam_repair_threads", OPT_MYISAM_REPAIR_THREADS,
"Number of threads to use when repairing MyISAM tables. The value of 1 disables parallel repair.",
- (gptr*) &global_system_variables.myisam_repair_threads,
- (gptr*) &max_system_variables.myisam_repair_threads, 0,
+ (uchar**) &global_system_variables.myisam_repair_threads,
+ (uchar**) &max_system_variables.myisam_repair_threads, 0,
GET_ULONG, REQUIRED_ARG, 1, 1, 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.",
- (gptr*) &global_system_variables.myisam_sort_buff_size,
- (gptr*) &max_system_variables.myisam_sort_buff_size, 0,
- GET_ULONG, REQUIRED_ARG, 8192*1024, 4, UINT_MAX32, 0, 1, 0},
+ (uchar**) &global_system_variables.myisam_sort_buff_size,
+ (uchar**) &max_system_variables.myisam_sort_buff_size, 0,
+ GET_ULONG, REQUIRED_ARG, 8192*1024, 4, ~0L, 0, 1, 0},
+ {"myisam_use_mmap", OPT_MYISAM_USE_MMAP,
+ "Use memory mapping for reading and writing MyISAM tables",
+ (uchar**) &opt_myisam_use_mmap,
+ (uchar**) &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\".",
- (gptr*) &myisam_stats_method_str, (gptr*) &myisam_stats_method_str, 0,
+ (uchar**) &myisam_stats_method_str, (uchar**) &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.",
- (gptr*) &global_system_variables.net_buffer_length,
- (gptr*) &max_system_variables.net_buffer_length, 0, GET_ULONG,
+ (uchar**) &global_system_variables.net_buffer_length,
+ (uchar**) &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.",
- (gptr*) &global_system_variables.net_read_timeout,
- (gptr*) &max_system_variables.net_read_timeout, 0, GET_ULONG,
+ (uchar**) &global_system_variables.net_read_timeout,
+ (uchar**) &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.",
- (gptr*) &global_system_variables.net_retry_count,
- (gptr*) &max_system_variables.net_retry_count,0,
+ (uchar**) &global_system_variables.net_retry_count,
+ (uchar**) &max_system_variables.net_retry_count,0,
GET_ULONG, REQUIRED_ARG, MYSQLD_NET_RETRY_COUNT, 1, 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.",
- (gptr*) &global_system_variables.net_write_timeout,
- (gptr*) &max_system_variables.net_write_timeout, 0, GET_ULONG,
+ (uchar**) &global_system_variables.net_write_timeout,
+ (uchar**) &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.",
+ (uchar**) &global_system_variables.old_mode,
+ (uchar**) &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.",
- (gptr*) &open_files_limit, (gptr*) &open_files_limit, 0, GET_ULONG,
+ (uchar**) &open_files_limit, (uchar**) &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.",
- (gptr*) &global_system_variables.optimizer_prune_level,
- (gptr*) &max_system_variables.optimizer_prune_level,
+ (uchar**) &global_system_variables.optimizer_prune_level,
+ (uchar**) &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).",
- (gptr*) &global_system_variables.optimizer_search_depth,
- (gptr*) &max_system_variables.optimizer_search_depth,
+ (uchar**) &global_system_variables.optimizer_search_depth,
+ (uchar**) &max_system_variables.optimizer_search_depth,
0, GET_ULONG, OPT_ARG, MAX_TABLES+1, 0, MAX_TABLES+2, 0, 1, 0},
{"plugin_dir", OPT_PLUGIN_DIR,
"Directory for plugins.",
- (gptr*) &opt_plugin_dir_ptr, (gptr*) &opt_plugin_dir_ptr, 0,
+ (uchar**) &opt_plugin_dir_ptr, (uchar**) &opt_plugin_dir_ptr, 0,
GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
- {"preload_buffer_size", OPT_PRELOAD_BUFFER_SIZE,
- "The size of the buffer that is allocated when preloading indexes",
- (gptr*) &global_system_variables.preload_buff_size,
- (gptr*) &max_system_variables.preload_buff_size, 0, GET_ULONG,
- REQUIRED_ARG, 32*1024L, 1024, 1024*1024*1024L, 0, 1, 0},
+ {"plugin-load", OPT_PLUGIN_LOAD,
+ "Optional colon-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.",
+ (uchar**) &opt_plugin_load, (uchar**) &opt_plugin_load, 0,
+ GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+ {"preload_buffer_size", OPT_PRELOAD_BUFFER_SIZE,
+ "The size of the buffer that is allocated when preloading indexes",
+ (uchar**) &global_system_variables.preload_buff_size,
+ (uchar**) &max_system_variables.preload_buff_size, 0, GET_ULONG,
+ REQUIRED_ARG, 32*1024L, 1024, 1024*1024*1024L, 0, 1, 0},
{"query_alloc_block_size", OPT_QUERY_ALLOC_BLOCK_SIZE,
"Allocation block size for query parsing and execution",
- (gptr*) &global_system_variables.query_alloc_block_size,
- (gptr*) &max_system_variables.query_alloc_block_size, 0, GET_ULONG,
+ (uchar**) &global_system_variables.query_alloc_block_size,
+ (uchar**) &max_system_variables.query_alloc_block_size, 0, GET_ULONG,
REQUIRED_ARG, QUERY_ALLOC_BLOCK_SIZE, 1024, 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.",
- (gptr*) &query_cache_limit, (gptr*) &query_cache_limit, 0, GET_ULONG,
- REQUIRED_ARG, 1024*1024L, 0, (longlong) ULONG_MAX, 0, 1, 0},
+ (uchar**) &query_cache_limit, (uchar**) &query_cache_limit, 0, GET_ULONG,
+ REQUIRED_ARG, 1024*1024L, 0, ULONG_MAX, 0, 1, 0},
{"query_cache_min_res_unit", OPT_QUERY_CACHE_MIN_RES_UNIT,
"minimal size of unit in wich space for results is allocated (last unit will be trimed after writing all result data.",
- (gptr*) &query_cache_min_res_unit, (gptr*) &query_cache_min_res_unit,
+ (uchar**) &query_cache_min_res_unit, (uchar**) &query_cache_min_res_unit,
0, GET_ULONG, REQUIRED_ARG, QUERY_CACHE_MIN_RESULT_DATA_SIZE,
- 0, (longlong) ULONG_MAX, 0, 1, 0},
+ 0, 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.",
- (gptr*) &query_cache_size, (gptr*) &query_cache_size, 0, GET_ULONG,
+ (uchar**) &query_cache_size, (uchar**) &query_cache_size, 0, GET_ULONG,
REQUIRED_ARG, 0, 0, (longlong) ULONG_MAX, 0, 1024, 0},
#ifdef HAVE_QUERY_CACHE
{"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.",
- (gptr*) &global_system_variables.query_cache_type,
- (gptr*) &max_system_variables.query_cache_type,
+ (uchar**) &global_system_variables.query_cache_type,
+ (uchar**) &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",
- (gptr*) &global_system_variables.query_cache_wlock_invalidate,
- (gptr*) &max_system_variables.query_cache_wlock_invalidate,
+ (uchar**) &global_system_variables.query_cache_wlock_invalidate,
+ (uchar**) &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",
- (gptr*) &global_system_variables.query_prealloc_size,
- (gptr*) &max_system_variables.query_prealloc_size, 0, GET_ULONG,
+ (uchar**) &global_system_variables.query_prealloc_size,
+ (uchar**) &max_system_variables.query_prealloc_size, 0, GET_ULONG,
REQUIRED_ARG, QUERY_ALLOC_PREALLOC_SIZE, QUERY_ALLOC_PREALLOC_SIZE,
ULONG_MAX, 0, 1024, 0},
{"range_alloc_block_size", OPT_RANGE_ALLOC_BLOCK_SIZE,
"Allocation block size for storing ranges during optimization",
- (gptr*) &global_system_variables.range_alloc_block_size,
- (gptr*) &max_system_variables.range_alloc_block_size, 0, GET_ULONG,
+ (uchar**) &global_system_variables.range_alloc_block_size,
+ (uchar**) &max_system_variables.range_alloc_block_size, 0, GET_ULONG,
REQUIRED_ARG, RANGE_ALLOC_BLOCK_SIZE, RANGE_ALLOC_BLOCK_SIZE, ULONG_MAX,
0, 1024, 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.",
- (gptr*) &global_system_variables.read_buff_size,
- (gptr*) &max_system_variables.read_buff_size,0, GET_ULONG, REQUIRED_ARG,
+ (uchar**) &global_system_variables.read_buff_size,
+ (uchar**) &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 for replication (slave) threads and users with the SUPER privilege",
- (gptr*) &opt_readonly,
- (gptr*) &opt_readonly,
+ (uchar**) &opt_readonly,
+ (uchar**) &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 a disk seeks. If not set, then it's set to the value of record_buffer.",
- (gptr*) &global_system_variables.read_rnd_buff_size,
- (gptr*) &max_system_variables.read_rnd_buff_size, 0,
+ (uchar**) &global_system_variables.read_rnd_buff_size,
+ (uchar**) &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,
"Alias for read_buffer_size",
- (gptr*) &global_system_variables.read_buff_size,
- (gptr*) &max_system_variables.read_buff_size,0, GET_ULONG, REQUIRED_ARG,
+ (uchar**) &global_system_variables.read_buff_size,
+ (uchar**) &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.",
- (gptr*) &relay_log_purge,
- (gptr*) &relay_log_purge, 0, GET_BOOL, NO_ARG,
+ (uchar**) &relay_log_purge,
+ (uchar**) &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.",
- (gptr*) &relay_log_space_limit,
- (gptr*) &relay_log_space_limit, 0, GET_ULL, REQUIRED_ARG, 0L, 0L,
+ (uchar**) &relay_log_space_limit,
+ (uchar**) &relay_log_space_limit, 0, GET_ULL, REQUIRED_ARG, 0L, 0L,
(longlong) ULONG_MAX, 0, 1, 0},
{"slave_compressed_protocol", OPT_SLAVE_COMPRESSED_PROTOCOL,
"Use compression on master/slave protocol.",
- (gptr*) &opt_slave_compressed_protocol,
- (gptr*) &opt_slave_compressed_protocol,
+ (uchar**) &opt_slave_compressed_protocol,
+ (uchar**) &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.",
- (gptr*) &slave_net_timeout, (gptr*) &slave_net_timeout, 0,
+ (uchar**) &slave_net_timeout, (uchar**) &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.",
- (gptr*) &slave_trans_retries, (gptr*) &slave_trans_retries, 0,
+ (uchar**) &slave_trans_retries, (uchar**) &slave_trans_retries, 0,
GET_ULONG, REQUIRED_ARG, 10L, 0L, (longlong) 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.",
- (gptr*) &slow_launch_time, (gptr*) &slow_launch_time, 0, GET_ULONG,
+ (uchar**) &slow_launch_time, (uchar**) &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.",
- (gptr*) &global_system_variables.sortbuff_size,
- (gptr*) &max_system_variables.sortbuff_size, 0, GET_ULONG, REQUIRED_ARG,
- MAX_SORT_MEMORY, MIN_SORT_MEMORY+MALLOC_OVERHEAD*2, UINT_MAX32, MALLOC_OVERHEAD,
+ (uchar**) &global_system_variables.sortbuff_size,
+ (uchar**) &max_system_variables.sortbuff_size, 0, GET_ULONG, REQUIRED_ARG,
+ MAX_SORT_MEMORY, MIN_SORT_MEMORY+MALLOC_OVERHEAD*2, ~0L, MALLOC_OVERHEAD,
1, 0},
-#ifdef HAVE_BERKELEY_DB
- {"sync-bdb-logs", OPT_BDB_SYNC,
- "Synchronously flush Berkeley DB logs. Enabled by default",
- (gptr*) &opt_sync_bdb_logs, (gptr*) &opt_sync_bdb_logs, 0, GET_BOOL,
- NO_ARG, 1, 0, 0, 0, 0, 0},
-#endif /* HAVE_BERKELEY_DB */
{"sync-binlog", OPT_SYNC_BINLOG,
"Synchronously flush binary log to disk after every #th event. "
"Use 0 (default) to disable synchronous flushing.",
- (gptr*) &sync_binlog_period, (gptr*) &sync_binlog_period, 0, GET_ULONG,
+ (uchar**) &sync_binlog_period, (uchar**) &sync_binlog_period, 0, GET_ULONG,
REQUIRED_ARG, 0, 0, ULONG_MAX, 0, 1, 0},
{"sync-frm", OPT_SYNC_FRM, "Sync .frm to disk on create. Enabled by default.",
- (gptr*) &opt_sync_frm, (gptr*) &opt_sync_frm, 0, GET_BOOL, NO_ARG, 1, 0,
+ (uchar**) &opt_sync_frm, (uchar**) &opt_sync_frm, 0, GET_BOOL, NO_ARG, 1, 0,
0, 0, 0, 0},
- {"table_cache", OPT_TABLE_CACHE,
- "The number of open tables for all threads.", (gptr*) &table_cache_size,
- (gptr*) &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.",
- (gptr*) &table_lock_wait_timeout, (gptr*) &table_lock_wait_timeout,
+ {"table_cache", OPT_TABLE_OPEN_CACHE,
+ "Deprecated; use --table_open_cache instead.",
+ (uchar**) &table_cache_size, (uchar**) &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.",
+ (uchar**) &table_def_size, (uchar**) &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.",
+ (uchar**) &table_cache_size, (uchar**) &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.",
+ (uchar**) &table_lock_wait_timeout, (uchar**) &table_lock_wait_timeout,
0, GET_ULONG, REQUIRED_ARG, 50, 1, 1024 * 1024 * 1024, 0, 1, 0},
{"thread_cache_size", OPT_THREAD_CACHE_SIZE,
"How many threads we should keep in a cache for reuse.",
- (gptr*) &thread_cache_size, (gptr*) &thread_cache_size, 0, GET_ULONG,
+ (uchar**) &thread_cache_size, (uchar**) &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.",
- (gptr*) &concurrency, (gptr*) &concurrency, 0, GET_ULONG, REQUIRED_ARG,
+ (uchar**) &concurrency, (uchar**) &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'",
+ (uchar**) &thread_pool_size, (uchar**) &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.", (gptr*) &thread_stack,
- (gptr*) &thread_stack, 0, GET_ULONG, REQUIRED_ARG,DEFAULT_THREAD_STACK,
+ "The stack size for each thread.", (uchar**) &my_thread_stack_size,
+ (uchar**) &my_thread_stack_size, 0, GET_ULONG, REQUIRED_ARG,DEFAULT_THREAD_STACK,
1024L*128L, ULONG_MAX, 0, 1024, 0},
{ "time_format", OPT_TIME_FORMAT,
"The TIME format (for future).",
- (gptr*) &opt_date_time_formats[MYSQL_TIMESTAMP_TIME],
- (gptr*) &opt_date_time_formats[MYSQL_TIMESTAMP_TIME],
+ (uchar**) &opt_date_time_formats[MYSQL_TIMESTAMP_TIME],
+ (uchar**) &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 table.",
- (gptr*) &global_system_variables.tmp_table_size,
- (gptr*) &max_system_variables.tmp_table_size, 0, GET_ULL,
- REQUIRED_ARG, 32*1024*1024L, 1024, MAX_MEM_TABLE_SIZE, 0, 1, 0},
+ (uchar**) &global_system_variables.tmp_table_size,
+ (uchar**) &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 various transaction-related structures",
- (gptr*) &global_system_variables.trans_alloc_block_size,
- (gptr*) &max_system_variables.trans_alloc_block_size, 0, GET_ULONG,
+ "Allocation block size for transactions to be stored in binary log",
+ (uchar**) &global_system_variables.trans_alloc_block_size,
+ (uchar**) &max_system_variables.trans_alloc_block_size, 0, GET_ULONG,
REQUIRED_ARG, QUERY_ALLOC_BLOCK_SIZE, 1024, ULONG_MAX, 0, 1024, 0},
{"transaction_prealloc_size", OPT_TRANS_PREALLOC_SIZE,
- "Persistent buffer for various transaction-related structures",
- (gptr*) &global_system_variables.trans_prealloc_size,
- (gptr*) &max_system_variables.trans_prealloc_size, 0, GET_ULONG,
+ "Persistent buffer for transactions to be stored in binary log",
+ (uchar**) &global_system_variables.trans_prealloc_size,
+ (uchar**) &max_system_variables.trans_prealloc_size, 0, GET_ULONG,
REQUIRED_ARG, TRANS_ALLOC_PREALLOC_SIZE, 1024, ULONG_MAX, 0, 1024, 0},
+ {"thread_handling", OPT_THREAD_HANDLING,
+ "Define threads usage for handling queries: "
+ "one-thread-per-connection or no-threads", 0, 0,
+ 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).",
- (gptr*) &global_system_variables.updatable_views_with_limit,
- (gptr*) &max_system_variables.updatable_views_with_limit,
+ (uchar**) &global_system_variables.updatable_views_with_limit,
+ (uchar**) &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.",
- (gptr*) &global_system_variables.net_wait_timeout,
- (gptr*) &max_system_variables.net_wait_timeout, 0, GET_ULONG,
+ (uchar**) &global_system_variables.net_wait_timeout,
+ (uchar**) &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},
{0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}
};
+static int show_queries(THD *thd, SHOW_VAR *var, char *buff)
+{
+ var->type= SHOW_LONGLONG;
+ var->value= (char *)&thd->query_id;
+ return 0;
+}
+
+
+static int show_net_compression(THD *thd, SHOW_VAR *var, char *buff)
+{
+ var->type= SHOW_MY_BOOL;
+ var->value= (char *)&thd->net.compress;
+ return 0;
+}
+
+static int show_starttime(THD *thd, SHOW_VAR *var, char *buff)
+{
+ var->type= SHOW_LONG;
+ var->value= buff;
+ *((long *)buff)= (long) (thd->query_start() - server_start_time);
+ return 0;
+}
+
+#ifdef COMMUNITY_SERVER
+static int show_flushstatustime(THD *thd, SHOW_VAR *var, char *buff)
+{
+ var->type= SHOW_LONG;
+ var->value= buff;
+ *((long *)buff)= (long) (thd->query_start() - flush_status_time);
+ return 0;
+}
+#endif
+
+#ifdef HAVE_REPLICATION
+static int show_rpl_status(THD *thd, SHOW_VAR *var, char *buff)
+{
+ var->type= SHOW_CHAR;
+ var->value= const_cast<char*>(rpl_status_type[(int)rpl_status]);
+ return 0;
+}
+
+static int show_slave_running(THD *thd, SHOW_VAR *var, char *buff)
+{
+ var->type= SHOW_MY_BOOL;
+ pthread_mutex_lock(&LOCK_active_mi);
+ var->value= buff;
+ *((my_bool *)buff)= (my_bool) (active_mi && active_mi->slave_running &&
+ active_mi->rli.slave_running);
+ pthread_mutex_unlock(&LOCK_active_mi);
+ return 0;
+}
+
+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);
+ if (active_mi)
+ {
+ var->type= SHOW_LONG;
+ var->value= buff;
+ pthread_mutex_lock(&active_mi->rli.data_lock);
+ *((long *)buff)= (long)active_mi->rli.retried_trans;
+ pthread_mutex_unlock(&active_mi->rli.data_lock);
+ }
+ else
+ var->type= SHOW_UNDEF;
+ pthread_mutex_unlock(&LOCK_active_mi);
+ return 0;
+}
+#endif /* HAVE_REPLICATION */
+
+static int show_open_tables(THD *thd, SHOW_VAR *var, char *buff)
+{
+ var->type= SHOW_LONG;
+ var->value= buff;
+ *((long *)buff)= (long)cached_open_tables();
+ return 0;
+}
+
+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);
+ *((long *)buff)= (long)prepared_stmt_count;
+ pthread_mutex_unlock(&LOCK_prepared_stmt_count);
+ return 0;
+}
+
+static int show_table_definitions(THD *thd, SHOW_VAR *var, char *buff)
+{
+ var->type= SHOW_LONG;
+ var->value= buff;
+ *((long *)buff)= (long)cached_table_definitions();
+ return 0;
+}
+
+#ifdef HAVE_OPENSSL
+/* Functions relying on CTX */
+static int show_ssl_ctx_sess_accept(THD *thd, SHOW_VAR *var, char *buff)
+{
+ var->type= SHOW_LONG;
+ var->value= buff;
+ *((long *)buff)= (!ssl_acceptor_fd ? 0 :
+ SSL_CTX_sess_accept(ssl_acceptor_fd->ssl_context));
+ return 0;
+}
+
+static int show_ssl_ctx_sess_accept_good(THD *thd, SHOW_VAR *var, char *buff)
+{
+ var->type= SHOW_LONG;
+ var->value= buff;
+ *((long *)buff)= (!ssl_acceptor_fd ? 0 :
+ SSL_CTX_sess_accept_good(ssl_acceptor_fd->ssl_context));
+ return 0;
+}
+
+static int show_ssl_ctx_sess_connect_good(THD *thd, SHOW_VAR *var, char *buff)
+{
+ var->type= SHOW_LONG;
+ var->value= buff;
+ *((long *)buff)= (!ssl_acceptor_fd ? 0 :
+ SSL_CTX_sess_connect_good(ssl_acceptor_fd->ssl_context));
+ return 0;
+}
+
+static int show_ssl_ctx_sess_accept_renegotiate(THD *thd, SHOW_VAR *var, char *buff)
+{
+ var->type= SHOW_LONG;
+ var->value= buff;
+ *((long *)buff)= (!ssl_acceptor_fd ? 0 :
+ SSL_CTX_sess_accept_renegotiate(ssl_acceptor_fd->ssl_context));
+ return 0;
+}
+
+static int show_ssl_ctx_sess_connect_renegotiate(THD *thd, SHOW_VAR *var, char *buff)
+{
+ var->type= SHOW_LONG;
+ var->value= buff;
+ *((long *)buff)= (!ssl_acceptor_fd ? 0 :
+ SSL_CTX_sess_connect_renegotiate(ssl_acceptor_fd->ssl_context));
+ return 0;
+}
+
+static int show_ssl_ctx_sess_cb_hits(THD *thd, SHOW_VAR *var, char *buff)
+{
+ var->type= SHOW_LONG;
+ var->value= buff;
+ *((long *)buff)= (!ssl_acceptor_fd ? 0 :
+ SSL_CTX_sess_cb_hits(ssl_acceptor_fd->ssl_context));
+ return 0;
+}
+
+static int show_ssl_ctx_sess_hits(THD *thd, SHOW_VAR *var, char *buff)
+{
+ var->type= SHOW_LONG;
+ var->value= buff;
+ *((long *)buff)= (!ssl_acceptor_fd ? 0 :
+ SSL_CTX_sess_hits(ssl_acceptor_fd->ssl_context));
+ return 0;
+}
+
+static int show_ssl_ctx_sess_cache_full(THD *thd, SHOW_VAR *var, char *buff)
+{
+ var->type= SHOW_LONG;
+ var->value= buff;
+ *((long *)buff)= (!ssl_acceptor_fd ? 0 :
+ SSL_CTX_sess_cache_full(ssl_acceptor_fd->ssl_context));
+ return 0;
+}
+
+static int show_ssl_ctx_sess_misses(THD *thd, SHOW_VAR *var, char *buff)
+{
+ var->type= SHOW_LONG;
+ var->value= buff;
+ *((long *)buff)= (!ssl_acceptor_fd ? 0 :
+ SSL_CTX_sess_misses(ssl_acceptor_fd->ssl_context));
+ return 0;
+}
+
+static int show_ssl_ctx_sess_timeouts(THD *thd, SHOW_VAR *var, char *buff)
+{
+ var->type= SHOW_LONG;
+ var->value= buff;
+ *((long *)buff)= (!ssl_acceptor_fd ? 0 :
+ SSL_CTX_sess_timeouts(ssl_acceptor_fd->ssl_context));
+ return 0;
+}
+
+static int show_ssl_ctx_sess_number(THD *thd, SHOW_VAR *var, char *buff)
+{
+ var->type= SHOW_LONG;
+ var->value= buff;
+ *((long *)buff)= (!ssl_acceptor_fd ? 0 :
+ SSL_CTX_sess_number(ssl_acceptor_fd->ssl_context));
+ return 0;
+}
+
+static int show_ssl_ctx_sess_connect(THD *thd, SHOW_VAR *var, char *buff)
+{
+ var->type= SHOW_LONG;
+ var->value= buff;
+ *((long *)buff)= (!ssl_acceptor_fd ? 0 :
+ SSL_CTX_sess_connect(ssl_acceptor_fd->ssl_context));
+ return 0;
+}
+
+static int show_ssl_ctx_sess_get_cache_size(THD *thd, SHOW_VAR *var, char *buff)
+{
+ var->type= SHOW_LONG;
+ var->value= buff;
+ *((long *)buff)= (!ssl_acceptor_fd ? 0 :
+ SSL_CTX_sess_get_cache_size(ssl_acceptor_fd->ssl_context));
+ return 0;
+}
+
+static int show_ssl_ctx_get_verify_mode(THD *thd, SHOW_VAR *var, char *buff)
+{
+ var->type= SHOW_LONG;
+ var->value= buff;
+ *((long *)buff)= (!ssl_acceptor_fd ? 0 :
+ SSL_CTX_get_verify_mode(ssl_acceptor_fd->ssl_context));
+ return 0;
+}
+
+static int show_ssl_ctx_get_verify_depth(THD *thd, SHOW_VAR *var, char *buff)
+{
+ var->type= SHOW_LONG;
+ var->value= buff;
+ *((long *)buff)= (!ssl_acceptor_fd ? 0 :
+ SSL_CTX_get_verify_depth(ssl_acceptor_fd->ssl_context));
+ return 0;
+}
+
+static int show_ssl_ctx_get_session_cache_mode(THD *thd, SHOW_VAR *var, char *buff)
+{
+ var->type= SHOW_CHAR;
+ if (!ssl_acceptor_fd)
+ var->value= const_cast<char*>("NONE");
+ else
+ switch (SSL_CTX_get_session_cache_mode(ssl_acceptor_fd->ssl_context))
+ {
+ case SSL_SESS_CACHE_OFF:
+ var->value= const_cast<char*>("OFF"); break;
+ case SSL_SESS_CACHE_CLIENT:
+ var->value= const_cast<char*>("CLIENT"); break;
+ case SSL_SESS_CACHE_SERVER:
+ var->value= const_cast<char*>("SERVER"); break;
+ case SSL_SESS_CACHE_BOTH:
+ var->value= const_cast<char*>("BOTH"); break;
+ case SSL_SESS_CACHE_NO_AUTO_CLEAR:
+ var->value= const_cast<char*>("NO_AUTO_CLEAR"); break;
+ case SSL_SESS_CACHE_NO_INTERNAL_LOOKUP:
+ var->value= const_cast<char*>("NO_INTERNAL_LOOKUP"); break;
+ default:
+ var->value= const_cast<char*>("Unknown"); break;
+ }
+ return 0;
+}
+
+/*
+ Functions relying on SSL
+ Note: In the show_ssl_* functions, we need to check if we have a
+ valid vio-object since this isn't always true, specifically
+ when session_status or global_status is requested from
+ inside an Event.
+ */
+static int show_ssl_get_version(THD *thd, SHOW_VAR *var, char *buff)
+{
+ var->type= SHOW_CHAR;
+ if( thd->vio_ok() && thd->net.vio->ssl_arg )
+ var->value= const_cast<char*>(SSL_get_version((SSL*) thd->net.vio->ssl_arg));
+ else
+ var->value= (char *)"";
+ return 0;
+}
+
+static int show_ssl_session_reused(THD *thd, SHOW_VAR *var, char *buff)
+{
+ var->type= SHOW_LONG;
+ var->value= buff;
+ if( thd->vio_ok() && thd->net.vio->ssl_arg )
+ *((long *)buff)= (long)SSL_session_reused((SSL*) thd->net.vio->ssl_arg);
+ else
+ *((long *)buff)= 0;
+ return 0;
+}
+
+static int show_ssl_get_default_timeout(THD *thd, SHOW_VAR *var, char *buff)
+{
+ var->type= SHOW_LONG;
+ var->value= buff;
+ if( thd->vio_ok() && thd->net.vio->ssl_arg )
+ *((long *)buff)= (long)SSL_get_default_timeout((SSL*)thd->net.vio->ssl_arg);
+ else
+ *((long *)buff)= 0;
+ return 0;
+}
+
+static int show_ssl_get_verify_mode(THD *thd, SHOW_VAR *var, char *buff)
+{
+ var->type= SHOW_LONG;
+ var->value= buff;
+ if( thd->net.vio && thd->net.vio->ssl_arg )
+ *((long *)buff)= (long)SSL_get_verify_mode((SSL*)thd->net.vio->ssl_arg);
+ else
+ *((long *)buff)= 0;
+ return 0;
+}
+
+static int show_ssl_get_verify_depth(THD *thd, SHOW_VAR *var, char *buff)
+{
+ var->type= SHOW_LONG;
+ var->value= buff;
+ if( thd->vio_ok() && thd->net.vio->ssl_arg )
+ *((long *)buff)= (long)SSL_get_verify_depth((SSL*)thd->net.vio->ssl_arg);
+ else
+ *((long *)buff)= 0;
+ return 0;
+}
+
+static int show_ssl_get_cipher(THD *thd, SHOW_VAR *var, char *buff)
+{
+ var->type= SHOW_CHAR;
+ if( thd->vio_ok() && thd->net.vio->ssl_arg )
+ var->value= const_cast<char*>(SSL_get_cipher((SSL*) thd->net.vio->ssl_arg));
+ else
+ var->value= (char *)"";
+ return 0;
+}
+
+static int show_ssl_get_cipher_list(THD *thd, SHOW_VAR *var, char *buff)
+{
+ var->type= SHOW_CHAR;
+ var->value= buff;
+ if (thd->vio_ok() && thd->net.vio->ssl_arg)
+ {
+ int i;
+ const char *p;
+ char *end= buff + SHOW_VAR_FUNC_BUFF_SIZE;
+ for (i=0; (p= SSL_get_cipher_list((SSL*) thd->net.vio->ssl_arg,i)) &&
+ buff < end; i++)
+ {
+ buff= strnmov(buff, p, end-buff-1);
+ *buff++= ':';
+ }
+ if (i)
+ buff--;
+ }
+ *buff=0;
+ return 0;
+}
+
+#endif /* HAVE_OPENSSL */
+
+
/*
Variables shown by SHOW STATUS in alphabetical order
*/
-struct show_var_st status_vars[]= {
+SHOW_VAR status_vars[]= {
{"Aborted_clients", (char*) &aborted_threads, SHOW_LONG},
{"Aborted_connects", (char*) &aborted_connects, SHOW_LONG},
{"Binlog_cache_disk_use", (char*) &binlog_cache_disk_use, SHOW_LONG},
{"Binlog_cache_use", (char*) &binlog_cache_use, SHOW_LONG},
{"Bytes_received", (char*) offsetof(STATUS_VAR, bytes_received), SHOW_LONGLONG_STATUS},
{"Bytes_sent", (char*) offsetof(STATUS_VAR, bytes_sent), SHOW_LONGLONG_STATUS},
- {"Com_admin_commands", (char*) offsetof(STATUS_VAR, com_other), SHOW_LONG_STATUS},
- {"Com_alter_db", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_ALTER_DB]), SHOW_LONG_STATUS},
- {"Com_alter_table", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_ALTER_TABLE]), SHOW_LONG_STATUS},
- {"Com_analyze", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_ANALYZE]), SHOW_LONG_STATUS},
- {"Com_backup_table", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_BACKUP_TABLE]), SHOW_LONG_STATUS},
- {"Com_begin", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_BEGIN]), SHOW_LONG_STATUS},
- {"Com_call_procedure", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_CALL]), SHOW_LONG_STATUS},
- {"Com_change_db", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_CHANGE_DB]), SHOW_LONG_STATUS},
- {"Com_change_master", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_CHANGE_MASTER]), SHOW_LONG_STATUS},
- {"Com_check", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_CHECK]), SHOW_LONG_STATUS},
- {"Com_checksum", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_CHECKSUM]), SHOW_LONG_STATUS},
- {"Com_commit", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_COMMIT]), SHOW_LONG_STATUS},
- {"Com_create_db", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_CREATE_DB]), SHOW_LONG_STATUS},
- {"Com_create_function", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_CREATE_FUNCTION]), SHOW_LONG_STATUS},
- {"Com_create_index", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_CREATE_INDEX]), SHOW_LONG_STATUS},
- {"Com_create_table", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_CREATE_TABLE]), SHOW_LONG_STATUS},
- {"Com_create_user", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_CREATE_USER]), SHOW_LONG_STATUS},
- {"Com_dealloc_sql", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_DEALLOCATE_PREPARE]), SHOW_LONG_STATUS},
- {"Com_delete", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_DELETE]), SHOW_LONG_STATUS},
- {"Com_delete_multi", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_DELETE_MULTI]), SHOW_LONG_STATUS},
- {"Com_do", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_DO]), SHOW_LONG_STATUS},
- {"Com_drop_db", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_DROP_DB]), SHOW_LONG_STATUS},
- {"Com_drop_function", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_DROP_FUNCTION]), SHOW_LONG_STATUS},
- {"Com_drop_index", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_DROP_INDEX]), SHOW_LONG_STATUS},
- {"Com_drop_table", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_DROP_TABLE]), SHOW_LONG_STATUS},
- {"Com_drop_user", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_DROP_USER]), SHOW_LONG_STATUS},
- {"Com_execute_sql", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_EXECUTE]), SHOW_LONG_STATUS},
- {"Com_flush", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_FLUSH]), SHOW_LONG_STATUS},
- {"Com_grant", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_GRANT]), SHOW_LONG_STATUS},
- {"Com_ha_close", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_HA_CLOSE]), SHOW_LONG_STATUS},
- {"Com_ha_open", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_HA_OPEN]), SHOW_LONG_STATUS},
- {"Com_ha_read", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_HA_READ]), SHOW_LONG_STATUS},
- {"Com_help", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_HELP]), SHOW_LONG_STATUS},
- {"Com_insert", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_INSERT]), SHOW_LONG_STATUS},
- {"Com_insert_select", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_INSERT_SELECT]), SHOW_LONG_STATUS},
- {"Com_kill", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_KILL]), SHOW_LONG_STATUS},
- {"Com_load", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_LOAD]), SHOW_LONG_STATUS},
- {"Com_load_master_data", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_LOAD_MASTER_DATA]), SHOW_LONG_STATUS},
- {"Com_load_master_table", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_LOAD_MASTER_TABLE]), SHOW_LONG_STATUS},
- {"Com_lock_tables", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_LOCK_TABLES]), SHOW_LONG_STATUS},
- {"Com_optimize", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_OPTIMIZE]), SHOW_LONG_STATUS},
- {"Com_preload_keys", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_PRELOAD_KEYS]), SHOW_LONG_STATUS},
- {"Com_prepare_sql", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_PREPARE]), SHOW_LONG_STATUS},
- {"Com_purge", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_PURGE]), SHOW_LONG_STATUS},
- {"Com_purge_before_date", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_PURGE_BEFORE]), SHOW_LONG_STATUS},
- {"Com_rename_table", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_RENAME_TABLE]), SHOW_LONG_STATUS},
- {"Com_repair", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_REPAIR]), SHOW_LONG_STATUS},
- {"Com_replace", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_REPLACE]), SHOW_LONG_STATUS},
- {"Com_replace_select", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_REPLACE_SELECT]), SHOW_LONG_STATUS},
- {"Com_reset", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_RESET]), SHOW_LONG_STATUS},
- {"Com_restore_table", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_RESTORE_TABLE]), SHOW_LONG_STATUS},
- {"Com_revoke", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_REVOKE]), SHOW_LONG_STATUS},
- {"Com_revoke_all", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_REVOKE_ALL]), SHOW_LONG_STATUS},
- {"Com_rollback", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_ROLLBACK]), SHOW_LONG_STATUS},
- {"Com_savepoint", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SAVEPOINT]), SHOW_LONG_STATUS},
- {"Com_select", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SELECT]), SHOW_LONG_STATUS},
- {"Com_set_option", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SET_OPTION]), SHOW_LONG_STATUS},
- {"Com_show_binlog_events", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_BINLOG_EVENTS]), SHOW_LONG_STATUS},
- {"Com_show_binlogs", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_BINLOGS]), SHOW_LONG_STATUS},
- {"Com_show_charsets", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_CHARSETS]), SHOW_LONG_STATUS},
- {"Com_show_collations", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_COLLATIONS]), SHOW_LONG_STATUS},
- {"Com_show_column_types", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_COLUMN_TYPES]), SHOW_LONG_STATUS},
- {"Com_show_create_db", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_CREATE_DB]), SHOW_LONG_STATUS},
- {"Com_show_create_table", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_CREATE]), SHOW_LONG_STATUS},
- {"Com_show_databases", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_DATABASES]), SHOW_LONG_STATUS},
- {"Com_show_errors", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_ERRORS]), SHOW_LONG_STATUS},
- {"Com_show_fields", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_FIELDS]), SHOW_LONG_STATUS},
- {"Com_show_grants", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_GRANTS]), SHOW_LONG_STATUS},
- {"Com_show_innodb_status", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_INNODB_STATUS]), SHOW_LONG_STATUS},
- {"Com_show_keys", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_KEYS]), SHOW_LONG_STATUS},
- {"Com_show_logs", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_LOGS]), SHOW_LONG_STATUS},
- {"Com_show_master_status", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_MASTER_STAT]), SHOW_LONG_STATUS},
- {"Com_show_ndb_status", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_NDBCLUSTER_STATUS]), SHOW_LONG_STATUS},
- {"Com_show_new_master", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_NEW_MASTER]), SHOW_LONG_STATUS},
- {"Com_show_open_tables", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_OPEN_TABLES]), SHOW_LONG_STATUS},
- {"Com_show_privileges", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_PRIVILEGES]), SHOW_LONG_STATUS},
- {"Com_show_processlist", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_PROCESSLIST]), SHOW_LONG_STATUS},
- {"Com_show_slave_hosts", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_SLAVE_HOSTS]), SHOW_LONG_STATUS},
- {"Com_show_slave_status", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_SLAVE_STAT]), SHOW_LONG_STATUS},
- {"Com_show_status", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_STATUS]), SHOW_LONG_STATUS},
- {"Com_show_storage_engines", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_STORAGE_ENGINES]), SHOW_LONG_STATUS},
- {"Com_show_tables", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_TABLES]), SHOW_LONG_STATUS},
- {"Com_show_triggers", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_TRIGGERS]), SHOW_LONG_STATUS},
- {"Com_show_variables", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_VARIABLES]), SHOW_LONG_STATUS},
- {"Com_show_warnings", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_WARNS]), SHOW_LONG_STATUS},
- {"Com_slave_start", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SLAVE_START]), SHOW_LONG_STATUS},
- {"Com_slave_stop", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SLAVE_STOP]), SHOW_LONG_STATUS},
- {"Com_stmt_close", (char*) offsetof(STATUS_VAR, com_stmt_close), SHOW_LONG_STATUS},
- {"Com_stmt_execute", (char*) offsetof(STATUS_VAR, com_stmt_execute), SHOW_LONG_STATUS},
- {"Com_stmt_fetch", (char*) offsetof(STATUS_VAR, com_stmt_fetch), SHOW_LONG_STATUS},
- {"Com_stmt_prepare", (char*) offsetof(STATUS_VAR, com_stmt_prepare), SHOW_LONG_STATUS},
- {"Com_stmt_reset", (char*) offsetof(STATUS_VAR, com_stmt_reset), SHOW_LONG_STATUS},
- {"Com_stmt_send_long_data", (char*) offsetof(STATUS_VAR, com_stmt_send_long_data), SHOW_LONG_STATUS},
- {"Com_truncate", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_TRUNCATE]), SHOW_LONG_STATUS},
- {"Com_unlock_tables", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_UNLOCK_TABLES]), SHOW_LONG_STATUS},
- {"Com_update", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_UPDATE]), SHOW_LONG_STATUS},
- {"Com_update_multi", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_UPDATE_MULTI]), SHOW_LONG_STATUS},
- {"Com_xa_commit", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_XA_COMMIT]),SHOW_LONG_STATUS},
- {"Com_xa_end", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_XA_END]),SHOW_LONG_STATUS},
- {"Com_xa_prepare", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_XA_PREPARE]),SHOW_LONG_STATUS},
- {"Com_xa_recover", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_XA_RECOVER]),SHOW_LONG_STATUS},
- {"Com_xa_rollback", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_XA_ROLLBACK]),SHOW_LONG_STATUS},
- {"Com_xa_start", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_XA_START]),SHOW_LONG_STATUS},
- {"Compression", (char*) 0, SHOW_NET_COMPRESSION},
- {"Connections", (char*) &thread_id, SHOW_LONG_CONST},
+ {"Com", (char*) com_status_vars, SHOW_ARRAY},
+ {"Compression", (char*) &show_net_compression, SHOW_FUNC},
+ {"Connections", (char*) &thread_id, SHOW_LONG_NOFLUSH},
{"Created_tmp_disk_tables", (char*) offsetof(STATUS_VAR, created_tmp_disk_tables), SHOW_LONG_STATUS},
{"Created_tmp_files", (char*) &my_tmp_file_created, SHOW_LONG},
{"Created_tmp_tables", (char*) offsetof(STATUS_VAR, created_tmp_tables), SHOW_LONG_STATUS},
{"Delayed_errors", (char*) &delayed_insert_errors, SHOW_LONG},
- {"Delayed_insert_threads", (char*) &delayed_insert_threads, SHOW_LONG_CONST},
+ {"Delayed_insert_threads", (char*) &delayed_insert_threads, SHOW_LONG_NOFLUSH},
{"Delayed_writes", (char*) &delayed_insert_writes, SHOW_LONG},
- {"Flush_commands", (char*) &refresh_version, SHOW_LONG_CONST},
+ {"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},
@@ -6589,50 +7309,49 @@ struct show_var_st status_vars[]= {
{"Handler_savepoint_rollback",(char*) offsetof(STATUS_VAR, ha_savepoint_rollback_count), SHOW_LONG_STATUS},
{"Handler_update", (char*) offsetof(STATUS_VAR, ha_update_count), SHOW_LONG_STATUS},
{"Handler_write", (char*) offsetof(STATUS_VAR, ha_write_count), SHOW_LONG_STATUS},
-#ifdef HAVE_INNOBASE_DB
- {"Innodb_", (char*) &innodb_status_variables, SHOW_VARS},
-#endif /*HAVE_INNOBASE_DB*/
- {"Key_blocks_not_flushed", (char*) &dflt_key_cache_var.global_blocks_changed, SHOW_KEY_CACHE_LONG},
- {"Key_blocks_unused", (char*) &dflt_key_cache_var.blocks_unused, SHOW_KEY_CACHE_CONST_LONG},
- {"Key_blocks_used", (char*) &dflt_key_cache_var.blocks_used, SHOW_KEY_CACHE_CONST_LONG},
- {"Key_read_requests", (char*) &dflt_key_cache_var.global_cache_r_requests, SHOW_KEY_CACHE_LONGLONG},
- {"Key_reads", (char*) &dflt_key_cache_var.global_cache_read, SHOW_KEY_CACHE_LONGLONG},
- {"Key_write_requests", (char*) &dflt_key_cache_var.global_cache_w_requests, SHOW_KEY_CACHE_LONGLONG},
- {"Key_writes", (char*) &dflt_key_cache_var.global_cache_write, SHOW_KEY_CACHE_LONGLONG},
+ {"Key_blocks_not_flushed", (char*) offsetof(KEY_CACHE, global_blocks_changed), SHOW_KEY_CACHE_LONG},
+ {"Key_blocks_unused", (char*) offsetof(KEY_CACHE, blocks_unused), SHOW_KEY_CACHE_LONG},
+ {"Key_blocks_used", (char*) offsetof(KEY_CACHE, blocks_used), SHOW_KEY_CACHE_LONG},
+ {"Key_read_requests", (char*) offsetof(KEY_CACHE, global_cache_r_requests), SHOW_KEY_CACHE_LONGLONG},
+ {"Key_reads", (char*) offsetof(KEY_CACHE, global_cache_read), SHOW_KEY_CACHE_LONGLONG},
+ {"Key_write_requests", (char*) offsetof(KEY_CACHE, global_cache_w_requests), SHOW_KEY_CACHE_LONGLONG},
+ {"Key_writes", (char*) offsetof(KEY_CACHE, global_cache_write), SHOW_KEY_CACHE_LONGLONG},
{"Last_query_cost", (char*) offsetof(STATUS_VAR, last_query_cost), SHOW_DOUBLE_STATUS},
{"Max_used_connections", (char*) &max_used_connections, SHOW_LONG},
-#ifdef HAVE_NDBCLUSTER_DB
- {"Ndb_", (char*) &ndb_status_variables, SHOW_VARS},
-#endif /*HAVE_NDBCLUSTER_DB*/
- {"Not_flushed_delayed_rows", (char*) &delayed_rows_in_use, SHOW_LONG_CONST},
- {"Open_files", (char*) &my_file_opened, SHOW_LONG_CONST},
- {"Open_streams", (char*) &my_stream_opened, SHOW_LONG_CONST},
- {"Open_tables", (char*) 0, SHOW_OPENTABLES},
+ {"Not_flushed_delayed_rows", (char*) &delayed_rows_in_use, SHOW_LONG_NOFLUSH},
+ {"Open_files", (char*) &my_file_opened, SHOW_LONG_NOFLUSH},
+ {"Open_streams", (char*) &my_stream_opened, SHOW_LONG_NOFLUSH},
+ {"Open_table_definitions", (char*) &show_table_definitions, SHOW_FUNC},
+ {"Open_tables", (char*) &show_open_tables, SHOW_FUNC},
+ {"Opened_files", (char*) &my_file_total_opened, SHOW_LONG_NOFLUSH},
{"Opened_tables", (char*) offsetof(STATUS_VAR, opened_tables), SHOW_LONG_STATUS},
- {"Prepared_stmt_count", (char*) &prepared_stmt_count, SHOW_LONG_CONST},
+ {"Opened_table_definitions", (char*) offsetof(STATUS_VAR, opened_shares), SHOW_LONG_STATUS},
+ {"Prepared_stmt_count", (char*) &show_prepared_stmt_count, SHOW_FUNC},
#ifdef HAVE_QUERY_CACHE
- {"Qcache_free_blocks", (char*) &query_cache.free_memory_blocks, SHOW_LONG_CONST},
- {"Qcache_free_memory", (char*) &query_cache.free_memory, SHOW_LONG_CONST},
+ {"Qcache_free_blocks", (char*) &query_cache.free_memory_blocks, SHOW_LONG_NOFLUSH},
+ {"Qcache_free_memory", (char*) &query_cache.free_memory, SHOW_LONG_NOFLUSH},
{"Qcache_hits", (char*) &query_cache.hits, SHOW_LONG},
{"Qcache_inserts", (char*) &query_cache.inserts, SHOW_LONG},
{"Qcache_lowmem_prunes", (char*) &query_cache.lowmem_prunes, SHOW_LONG},
{"Qcache_not_cached", (char*) &query_cache.refused, SHOW_LONG},
- {"Qcache_queries_in_cache", (char*) &query_cache.queries_in_cache, SHOW_LONG_CONST},
- {"Qcache_total_blocks", (char*) &query_cache.total_blocks, SHOW_LONG_CONST},
+ {"Qcache_queries_in_cache", (char*) &query_cache.queries_in_cache, SHOW_LONG_NOFLUSH},
+ {"Qcache_total_blocks", (char*) &query_cache.total_blocks, SHOW_LONG_NOFLUSH},
#endif /*HAVE_QUERY_CACHE*/
- {"Queries", (char*) 0, SHOW_QUERIES},
- {"Questions", (char*) offsetof(STATUS_VAR, questions),
- SHOW_LONG_STATUS},
-
- {"Rpl_status", (char*) 0, SHOW_RPL_STATUS},
+ {"Queries", (char*) &show_queries, SHOW_FUNC},
+ {"Questions", (char*) offsetof(STATUS_VAR, questions), SHOW_LONG_STATUS},
+#ifdef HAVE_REPLICATION
+ {"Rpl_status", (char*) &show_rpl_status, SHOW_FUNC},
+#endif
{"Select_full_join", (char*) offsetof(STATUS_VAR, select_full_join_count), SHOW_LONG_STATUS},
{"Select_full_range_join", (char*) offsetof(STATUS_VAR, select_full_range_join_count), SHOW_LONG_STATUS},
{"Select_range", (char*) offsetof(STATUS_VAR, select_range_count), SHOW_LONG_STATUS},
{"Select_range_check", (char*) offsetof(STATUS_VAR, select_range_check_count), SHOW_LONG_STATUS},
{"Select_scan", (char*) offsetof(STATUS_VAR, select_scan_count), SHOW_LONG_STATUS},
{"Slave_open_temp_tables", (char*) &slave_open_temp_tables, SHOW_LONG},
- {"Slave_retried_transactions",(char*) 0, SHOW_SLAVE_RETRIED_TRANS},
- {"Slave_running", (char*) 0, SHOW_SLAVE_RUNNING},
+#ifdef HAVE_REPLICATION
+ {"Slave_retried_transactions",(char*) &show_slave_retried_trans, SHOW_FUNC},
+ {"Slave_running", (char*) &show_slave_running, SHOW_FUNC},
+#endif
{"Slow_launch_threads", (char*) &slow_launch_threads, SHOW_LONG},
{"Slow_queries", (char*) offsetof(STATUS_VAR, long_query_count), SHOW_LONG_STATUS},
{"Sort_merge_passes", (char*) offsetof(STATUS_VAR, filesort_merge_passes), SHOW_LONG_STATUS},
@@ -6640,29 +7359,29 @@ struct show_var_st 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
- {"Ssl_accept_renegotiates", (char*) 0, SHOW_SSL_CTX_SESS_ACCEPT_RENEGOTIATE},
- {"Ssl_accepts", (char*) 0, SHOW_SSL_CTX_SESS_ACCEPT},
- {"Ssl_callback_cache_hits", (char*) 0, SHOW_SSL_CTX_SESS_CB_HITS},
- {"Ssl_cipher", (char*) 0, SHOW_SSL_GET_CIPHER},
- {"Ssl_cipher_list", (char*) 0, SHOW_SSL_GET_CIPHER_LIST},
- {"Ssl_client_connects", (char*) 0, SHOW_SSL_CTX_SESS_CONNECT},
- {"Ssl_connect_renegotiates", (char*) 0, SHOW_SSL_CTX_SESS_CONNECT_RENEGOTIATE},
- {"Ssl_ctx_verify_depth", (char*) 0, SHOW_SSL_CTX_GET_VERIFY_DEPTH},
- {"Ssl_ctx_verify_mode", (char*) 0, SHOW_SSL_CTX_GET_VERIFY_MODE},
- {"Ssl_default_timeout", (char*) 0, SHOW_SSL_GET_DEFAULT_TIMEOUT},
- {"Ssl_finished_accepts", (char*) 0, SHOW_SSL_CTX_SESS_ACCEPT_GOOD},
- {"Ssl_finished_connects", (char*) 0, SHOW_SSL_CTX_SESS_CONNECT_GOOD},
- {"Ssl_session_cache_hits", (char*) 0, SHOW_SSL_CTX_SESS_HITS},
- {"Ssl_session_cache_misses", (char*) 0, SHOW_SSL_CTX_SESS_MISSES},
- {"Ssl_session_cache_mode", (char*) 0, SHOW_SSL_CTX_GET_SESSION_CACHE_MODE},
- {"Ssl_session_cache_overflows", (char*) 0, SHOW_SSL_CTX_SESS_CACHE_FULL},
- {"Ssl_session_cache_size", (char*) 0, SHOW_SSL_CTX_SESS_GET_CACHE_SIZE},
- {"Ssl_session_cache_timeouts", (char*) 0, SHOW_SSL_CTX_SESS_TIMEOUTS},
- {"Ssl_sessions_reused", (char*) 0, SHOW_SSL_SESSION_REUSED},
- {"Ssl_used_session_cache_entries",(char*) 0, SHOW_SSL_CTX_SESS_NUMBER},
- {"Ssl_verify_depth", (char*) 0, SHOW_SSL_GET_VERIFY_DEPTH},
- {"Ssl_verify_mode", (char*) 0, SHOW_SSL_GET_VERIFY_MODE},
- {"Ssl_version", (char*) 0, SHOW_SSL_GET_VERSION},
+ {"Ssl_accept_renegotiates", (char*) &show_ssl_ctx_sess_accept_renegotiate, SHOW_FUNC},
+ {"Ssl_accepts", (char*) &show_ssl_ctx_sess_accept, SHOW_FUNC},
+ {"Ssl_callback_cache_hits", (char*) &show_ssl_ctx_sess_cb_hits, SHOW_FUNC},
+ {"Ssl_cipher", (char*) &show_ssl_get_cipher, SHOW_FUNC},
+ {"Ssl_cipher_list", (char*) &show_ssl_get_cipher_list, SHOW_FUNC},
+ {"Ssl_client_connects", (char*) &show_ssl_ctx_sess_connect, SHOW_FUNC},
+ {"Ssl_connect_renegotiates", (char*) &show_ssl_ctx_sess_connect_renegotiate, SHOW_FUNC},
+ {"Ssl_ctx_verify_depth", (char*) &show_ssl_ctx_get_verify_depth, SHOW_FUNC},
+ {"Ssl_ctx_verify_mode", (char*) &show_ssl_ctx_get_verify_mode, SHOW_FUNC},
+ {"Ssl_default_timeout", (char*) &show_ssl_get_default_timeout, SHOW_FUNC},
+ {"Ssl_finished_accepts", (char*) &show_ssl_ctx_sess_accept_good, SHOW_FUNC},
+ {"Ssl_finished_connects", (char*) &show_ssl_ctx_sess_connect_good, SHOW_FUNC},
+ {"Ssl_session_cache_hits", (char*) &show_ssl_ctx_sess_hits, SHOW_FUNC},
+ {"Ssl_session_cache_misses", (char*) &show_ssl_ctx_sess_misses, SHOW_FUNC},
+ {"Ssl_session_cache_mode", (char*) &show_ssl_ctx_get_session_cache_mode, SHOW_FUNC},
+ {"Ssl_session_cache_overflows", (char*) &show_ssl_ctx_sess_cache_full, SHOW_FUNC},
+ {"Ssl_session_cache_size", (char*) &show_ssl_ctx_sess_get_cache_size, SHOW_FUNC},
+ {"Ssl_session_cache_timeouts", (char*) &show_ssl_ctx_sess_timeouts, SHOW_FUNC},
+ {"Ssl_sessions_reused", (char*) &show_ssl_session_reused, SHOW_FUNC},
+ {"Ssl_used_session_cache_entries",(char*) &show_ssl_ctx_sess_number, SHOW_FUNC},
+ {"Ssl_verify_depth", (char*) &show_ssl_get_verify_depth, SHOW_FUNC},
+ {"Ssl_verify_mode", (char*) &show_ssl_get_verify_mode, SHOW_FUNC},
+ {"Ssl_version", (char*) &show_ssl_get_version, SHOW_FUNC},
#endif /* HAVE_OPENSSL */
{"Table_locks_immediate", (char*) &locks_immediate, SHOW_LONG},
{"Table_locks_waited", (char*) &locks_waited, SHOW_LONG},
@@ -6671,17 +7390,25 @@ struct show_var_st status_vars[]= {
{"Tc_log_page_size", (char*) &tc_log_page_size, SHOW_LONG},
{"Tc_log_page_waits", (char*) &tc_log_page_waits, SHOW_LONG},
#endif
- {"Threads_cached", (char*) &cached_thread_count, SHOW_LONG_CONST},
- {"Threads_connected", (char*) &thread_count, SHOW_INT_CONST},
- {"Threads_created", (char*) &thread_created, SHOW_LONG_CONST},
- {"Threads_running", (char*) &thread_running, SHOW_INT_CONST},
- {"Uptime", (char*) 0, SHOW_STARTTIME},
+ {"Threads_cached", (char*) &cached_thread_count, SHOW_LONG_NOFLUSH},
+ {"Threads_connected", (char*) &thread_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
+ {"Uptime_since_flush_status",(char*) &show_flushstatustime, SHOW_FUNC},
+#endif
{NullS, NullS, SHOW_LONG}
};
+#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);
}
@@ -6703,7 +7430,7 @@ Starts the MySQL database server\n");
printf("Usage: %s [OPTIONS]\n", my_progname);
if (!opt_verbose)
- puts("\nFor more help options (several pages), use mysqld --verbose --help\n");
+ puts("\nFor more help options (several pages), use mysqld --verbose --help");
else
{
#ifdef __WIN__
@@ -6721,26 +7448,26 @@ Starts the MySQL database server\n");
#endif
print_defaults(MYSQL_CONFIG_NAME,load_default_groups);
puts("");
- fix_paths();
set_ports();
- my_print_help(my_long_options);
- my_print_variables(my_long_options);
+ /* Print out all the options including plugin supplied options */
+ my_print_help_inc_plugins(my_long_options, sizeof(my_long_options)/sizeof(my_option));
puts("\n\
To see what values a running MySQL server is using, type\n\
-'mysqladmin variables' instead of 'mysqld --verbose --help'.\n");
+'mysqladmin variables' instead of 'mysqld --verbose --help'.");
}
}
+#endif /*!EMBEDDED_LIBRARY*/
-/*
- Initialize all MySQL global variables to default values
+/**
+ Initialize all MySQL global variables to default values.
- SYNOPSIS
- mysql_init_variables()
+ We don't need to set numeric variables refered to in my_long_options
+ as these are initialized by my_getopt.
- NOTES
+ @note
The reason to set a lot of global variables to zero is to allow one to
restart the embedded server with a clean environment
It's also needed on some exotic platforms where global variables are
@@ -6750,7 +7477,7 @@ To see what values a running MySQL server is using, type\n\
as these are initialized by my_getopt.
*/
-static void mysql_init_variables(void)
+static int mysql_init_variables(void)
{
/* Things reset to zero */
opt_skip_slave_start= opt_reckless_slave = 0;
@@ -6758,8 +7485,10 @@ static void mysql_init_variables(void)
myisam_test_invalid_symlink= test_if_data_home_dir;
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_disable_networking= opt_skip_show_db=0;
+ opt_ignore_builtin_innodb= 0;
opt_logname= opt_update_logname= opt_binlog_index_name= opt_slow_logname= 0;
opt_tc_log_file= (char *)"tc.log"; // no hostname in tc_log file name !
opt_secure_auth= 0;
@@ -6768,6 +7497,7 @@ static void mysql_init_variables(void)
mqh_used= 0;
segfaulted= 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;
@@ -6775,7 +7505,7 @@ static void mysql_init_variables(void)
slave_open_temp_tables= 0;
cached_thread_count= 0;
opt_endinfo= using_udf_functions= 0;
- opt_using_transactions= using_update_log= 0;
+ opt_using_transactions= 0;
abort_loop= select_thread_in_use= signal_thread_in_use= 0;
ready_to_exit= shutdown_in_progress= grant_option= 0;
aborted_threads= aborted_connects= 0;
@@ -6788,7 +7518,7 @@ static void mysql_init_variables(void)
prepared_stmt_count= 0;
errmesg= 0;
mysqld_unix_port= opt_mysql_tmpdir= my_bind_addr_str= NullS;
- bzero((gptr) &mysql_tmpdir_list, sizeof(mysql_tmpdir_list));
+ bzero((uchar*) &mysql_tmpdir_list, sizeof(mysql_tmpdir_list));
bzero((char *) &global_status_var, sizeof(global_status_var));
opt_large_pages= 0;
key_map_full.set_all();
@@ -6804,6 +7534,9 @@ static void mysql_init_variables(void)
/* Things with default values that are not zero */
delay_key_write_options= (uint) DELAY_KEY_WRITE_ON;
+ slave_exec_mode_options= 0;
+ slave_exec_mode_options= (uint)
+ find_bit_type_or_exit(slave_exec_mode_str, &slave_exec_mode_typelib, NULL);
opt_specialflag= SPECIAL_ENGLISH;
unix_sock= ip_sock= INVALID_SOCKET;
mysql_home_ptr= mysql_home;
@@ -6811,12 +7544,11 @@ static void mysql_init_variables(void)
log_error_file_ptr= log_error_file;
language_ptr= language;
mysql_data_home= mysql_real_data_home;
- thd_startup_options= (OPTION_UPDATE_LOG | OPTION_AUTO_IS_NULL |
- OPTION_BIN_LOG | OPTION_QUOTE_SHOW_CREATE |
- OPTION_SQL_NOTES);
+ 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= flush_version= 1L; /* Increments on each reload */
+ refresh_version= 1L; /* Increments on each reload */
global_query_id= thread_id= 1L;
strmov(server_version, MYSQL_SERVER_VERSION);
myisam_recover_options_str= sql_mode_str= "OFF";
@@ -6826,16 +7558,13 @@ static void mysql_init_variables(void)
thread_cache.empty();
key_caches.empty();
if (!(dflt_key_cache= get_or_create_key_cache(default_key_cache_base.str,
- default_key_cache_base.length)))
- exit(1);
- multi_keycache_init(); /* set key_cache_hash.default_value = dflt_key_cache */
-
- /* Initialize structures that is used when processing options */
- replicate_rewrite_db.empty();
- replicate_do_db.empty();
- replicate_ignore_db.empty();
- binlog_do_db.empty();
- binlog_ignore_db.empty();
+ default_key_cache_base.length)))
+ {
+ sql_print_error("Cannot allocate the keycache");
+ return 1;
+ }
+ /* set key_cache_hash.default_value = dflt_key_cache */
+ multi_keycache_init();
/* Set directory paths */
strmake(language, LANGUAGE, sizeof(language)-1);
@@ -6843,6 +7572,7 @@ static void mysql_init_variables(void)
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;
/* Replication parameters */
master_user= (char*) "test";
@@ -6861,16 +7591,17 @@ static void mysql_init_variables(void)
sys_charset_system.value= (char*) system_charset_info->csname;
character_set_filesystem_name= (char*) "binary";
lc_time_names_name= (char*) "en_US";
-
/* Set default values for some option variables */
- global_system_variables.table_type= DB_TYPE_MYISAM;
+ 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;
/*
Default behavior for 4.1 and 5.0 is to treat NULL values as unequal
when collecting index statistics for MyISAM tables.
@@ -6883,52 +7614,17 @@ static void mysql_init_variables(void)
"d:t:i:o,/tmp/mysqld.trace");
#endif
opt_error_log= IF_WIN(1,0);
-#ifdef HAVE_BERKELEY_DB
- have_berkeley_db= SHOW_OPTION_YES;
-#else
- have_berkeley_db= SHOW_OPTION_NO;
-#endif
-#ifdef HAVE_INNOBASE_DB
- have_innodb=SHOW_OPTION_YES;
-#else
- have_innodb=SHOW_OPTION_NO;
-#endif
- have_isam=SHOW_OPTION_NO;
-#ifdef HAVE_EXAMPLE_DB
- have_example_db= SHOW_OPTION_YES;
-#else
- have_example_db= SHOW_OPTION_NO;
-#endif
-#if defined(HAVE_ARCHIVE_DB)
- have_archive_db= SHOW_OPTION_YES;
+#ifdef COMMUNITY_SERVER
+ have_community_features = SHOW_OPTION_YES;
#else
- have_archive_db= SHOW_OPTION_NO;
-#endif
-#ifdef HAVE_BLACKHOLE_DB
- have_blackhole_db= SHOW_OPTION_YES;
-#else
- have_blackhole_db= SHOW_OPTION_NO;
-#endif
-#ifdef HAVE_FEDERATED_DB
- have_federated_db= SHOW_OPTION_YES;
-#else
- have_federated_db= SHOW_OPTION_NO;
-#endif
-#ifdef HAVE_CSV_DB
- have_csv_db= SHOW_OPTION_YES;
-#else
- have_csv_db= SHOW_OPTION_NO;
-#endif
-#ifdef HAVE_NDBCLUSTER_DB
- have_ndbcluster=SHOW_OPTION_DISABLED;
-#else
- have_ndbcluster=SHOW_OPTION_NO;
-#endif
-#ifdef USE_RAID
- have_raid=SHOW_OPTION_YES;
-#else
- have_raid=SHOW_OPTION_NO;
-#endif
+ 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_ssl=SHOW_OPTION_YES;
#else
@@ -6987,6 +7683,18 @@ static void mysql_init_variables(void)
/* Allow Win32 and NetWare 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");
strcat(prg_dev,"/../"); // Remove 'bin' to get base dir
cleanup_dirname(mysql_home,prg_dev);
@@ -6997,12 +7705,14 @@ static void mysql_init_variables(void)
tmpenv = DEFAULT_MYSQL_HOME;
(void) strmake(mysql_home, tmpenv, sizeof(mysql_home)-1);
#endif
+ return 0;
}
-static my_bool
-get_one_option(int optid, const struct my_option *opt __attribute__((unused)),
- char *argument)
+my_bool
+mysqld_get_one_option(int optid,
+ const struct my_option *opt __attribute__((unused)),
+ char *argument)
{
switch(optid) {
case '#':
@@ -7023,12 +7733,14 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)),
default_collation_name= 0;
break;
case 'l':
+ WARN_DEPRECATED(NULL, "7.0", "--log", "'--general_log'/'--general_log_file'");
opt_log=1;
break;
case 'h':
strmake(mysql_real_data_home,argument, sizeof(mysql_real_data_home)-1);
/* Correct pointer set by my_getopt (for embedded library) */
mysql_data_home= mysql_real_data_home;
+ mysql_data_home_len= strlen(mysql_data_home);
break;
case 'u':
if (!mysqld_user || !strcmp(mysqld_user, argument))
@@ -7043,6 +7755,10 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)),
case OPT_SLAVE_SKIP_ERRORS:
init_slave_skip_errors(argument);
break;
+ case OPT_SLAVE_EXEC_MODE:
+ slave_exec_mode_options= (uint)
+ find_bit_type_or_exit(argument, &slave_exec_mode_typelib, "");
+ break;
#endif
case OPT_SAFEMALLOC_MEM_LIMIT:
#if !defined(DBUG_OFF) && defined(SAFEMALLOC)
@@ -7050,9 +7766,11 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)),
#endif
break;
#include <sslopt-case.h>
+#ifndef EMBEDDED_LIBRARY
case 'V':
print_version();
exit(0);
+#endif /*EMBEDDED_LIBRARY*/
case 'W':
if (!argument)
global_system_variables.log_warnings++;
@@ -7063,12 +7781,14 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)),
break;
case 'T':
test_flags= argument ? (uint) atoi(argument) : 0;
- test_flags&= ~TEST_NO_THREADS;
opt_endinfo=1;
break;
case (int) OPT_BIG_TABLES:
thd_startup_options|=OPTION_BIG_TABLES;
break;
+ case (int) OPT_IGNORE_BUILTIN_INNODB:
+ opt_ignore_builtin_innodb= 1;
+ break;
case (int) OPT_ISAM_LOG:
opt_myisam_log=1;
break;
@@ -7085,24 +7805,18 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)),
case (int) OPT_INIT_RPL_ROLE:
{
int role;
- if ((role=find_type(argument, &rpl_role_typelib, 2)) <= 0)
- {
- fprintf(stderr, "Unknown replication role: %s\n", argument);
- exit(1);
- }
+ role= find_type_or_exit(argument, &rpl_role_typelib, opt->name);
rpl_status = (role == 1) ? RPL_AUTH_MASTER : RPL_IDLE_SLAVE;
break;
}
case (int)OPT_REPLICATE_IGNORE_DB:
{
- i_string *db = new i_string(argument);
- replicate_ignore_db.push_back(db);
+ rpl_filter->add_ignore_db(argument);
break;
}
case (int)OPT_REPLICATE_DO_DB:
{
- i_string *db = new i_string(argument);
- replicate_do_db.push_back(db);
+ rpl_filter->add_do_db(argument);
break;
}
case (int)OPT_REPLICATE_REWRITE_DB:
@@ -7135,76 +7849,92 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)),
exit(1);
}
- i_string_pair *db_pair = new i_string_pair(key, val);
- replicate_rewrite_db.push_back(db_pair);
+ rpl_filter->add_db_rewrite(key, val);
break;
}
case (int)OPT_BINLOG_IGNORE_DB:
{
- i_string *db = new i_string(argument);
- binlog_ignore_db.push_back(db);
+ binlog_filter->add_ignore_db(argument);
+ break;
+ }
+ case OPT_BINLOG_FORMAT:
+ {
+ int id;
+ id= find_type_or_exit(argument, &binlog_format_typelib, opt->name);
+ global_system_variables.binlog_format= opt_binlog_format_id= id - 1;
break;
}
case (int)OPT_BINLOG_DO_DB:
{
- i_string *db = new i_string(argument);
- binlog_do_db.push_back(db);
+ binlog_filter->add_do_db(argument);
break;
}
case (int)OPT_REPLICATE_DO_TABLE:
{
- if (!do_table_inited)
- init_table_rule_hash(&replicate_do_table, &do_table_inited);
- if (add_table_rule(&replicate_do_table, argument))
+ if (rpl_filter->add_do_table(argument))
{
fprintf(stderr, "Could not add do table rule '%s'!\n", argument);
exit(1);
}
- table_rules_on = 1;
break;
}
case (int)OPT_REPLICATE_WILD_DO_TABLE:
{
- if (!wild_do_table_inited)
- init_table_rule_array(&replicate_wild_do_table,
- &wild_do_table_inited);
- if (add_wild_table_rule(&replicate_wild_do_table, argument))
+ if (rpl_filter->add_wild_do_table(argument))
{
fprintf(stderr, "Could not add do table rule '%s'!\n", argument);
exit(1);
}
- table_rules_on = 1;
break;
}
case (int)OPT_REPLICATE_WILD_IGNORE_TABLE:
{
- if (!wild_ignore_table_inited)
- init_table_rule_array(&replicate_wild_ignore_table,
- &wild_ignore_table_inited);
- if (add_wild_table_rule(&replicate_wild_ignore_table, argument))
+ if (rpl_filter->add_wild_ignore_table(argument))
{
fprintf(stderr, "Could not add ignore table rule '%s'!\n", argument);
exit(1);
}
- table_rules_on = 1;
break;
}
case (int)OPT_REPLICATE_IGNORE_TABLE:
{
- if (!ignore_table_inited)
- init_table_rule_hash(&replicate_ignore_table, &ignore_table_inited);
- if (add_table_rule(&replicate_ignore_table, argument))
+ if (rpl_filter->add_ignore_table(argument))
{
fprintf(stderr, "Could not add ignore table rule '%s'!\n", argument);
exit(1);
}
- table_rules_on = 1;
break;
}
#endif /* HAVE_REPLICATION */
case (int) OPT_SLOW_QUERY_LOG:
- opt_slow_log=1;
+ WARN_DEPRECATED(NULL, "7.0", "--log_slow_queries", "'--slow_query_log'/'--slow_query_log_file'");
+ opt_slow_log= 1;
+ break;
+#ifdef WITH_CSV_STORAGE_ENGINE
+ 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);
+ }
+ break;
+ }
+#endif
+ case OPT_EVENT_SCHEDULER:
+#ifndef HAVE_EVENT_SCHEDULER
+ sql_perror("Event scheduler is not supported in embedded build.");
+#else
+ if (Events::set_opt_event_scheduler(argument))
+ exit(1);
+#endif
break;
case (int) OPT_SKIP_NEW:
opt_specialflag|= SPECIAL_NO_NEW_FUNC;
@@ -7226,6 +7956,9 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)),
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:
opt_external_locking=0;
@@ -7248,11 +7981,6 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)),
opt_skip_show_db=1;
opt_specialflag|=SPECIAL_SKIP_SHOW_DB;
break;
-#ifdef ONE_THREAD
- case (int) OPT_ONE_THREAD:
- test_flags |= TEST_NO_THREADS;
-#endif
- break;
case (int) OPT_WANT_CORE:
test_flags |= TEST_CORE_ON_SIGNAL;
break;
@@ -7274,14 +8002,14 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)),
if (gethostname(myhostname,sizeof(myhostname)) < 0)
{
sql_perror("Can't start server: cannot get my own hostname!");
- exit(1);
+ return 1;
}
ent=gethostbyname(myhostname);
}
if (!ent)
{
sql_perror("Can't start server: cannot resolve hostname!");
- exit(1);
+ return 1;
}
my_bind_addr = (ulong) ((in_addr*)ent->h_addr_list[0])->s_addr;
}
@@ -7331,17 +8059,6 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)),
case OPT_BOOTSTRAP:
opt_noacl=opt_bootstrap=1;
break;
- case OPT_STORAGE_ENGINE:
- {
- if ((enum db_type)((global_system_variables.table_type=
- ha_resolve_by_name(argument, (uint) strlen(argument)))) ==
- DB_TYPE_UNKNOWN)
- {
- fprintf(stderr,"Unknown/unsupported table type: %s\n",argument);
- exit(1);
- }
- break;
- }
case OPT_SERVER_ID:
server_id_supplied = 1;
break;
@@ -7357,11 +8074,7 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)),
else
{
int type;
- if ((type=find_type(argument, &delay_key_write_typelib, 2)) <= 0)
- {
- fprintf(stderr,"Unknown delay_key_write type: %s\n",argument);
- exit(1);
- }
+ type= find_type_or_exit(argument, &delay_key_write_typelib, opt->name);
delay_key_write_options= (uint) type-1;
}
break;
@@ -7372,85 +8085,11 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)),
case OPT_TX_ISOLATION:
{
int type;
- if ((type=find_type(argument, &tx_isolation_typelib, 2)) <= 0)
- {
- fprintf(stderr,"Unknown transaction isolation type: %s\n",argument);
- exit(1);
- }
+ type= find_type_or_exit(argument, &tx_isolation_typelib, opt->name);
global_system_variables.tx_isolation= (type-1);
break;
}
- case OPT_MERGE:
- if (opt_merge)
- have_merge_db= SHOW_OPTION_YES;
- else
- have_merge_db= SHOW_OPTION_DISABLED;
- break;
-#ifdef HAVE_FEDERATED_DB
- case OPT_FEDERATED:
- if (opt_federated)
- have_federated_db= SHOW_OPTION_YES;
- else
- have_federated_db= SHOW_OPTION_DISABLED;
- break;
-#endif
-#ifdef HAVE_BERKELEY_DB
- case OPT_BDB_NOSYNC:
- /* Deprecated option */
- opt_sync_bdb_logs= 0;
- /* Fall through */
- case OPT_BDB_SYNC:
- if (!opt_sync_bdb_logs)
- berkeley_env_flags|= DB_TXN_NOSYNC;
- else
- berkeley_env_flags&= ~DB_TXN_NOSYNC;
- break;
- case OPT_BDB_NO_RECOVER:
- berkeley_init_flags&= ~(DB_RECOVER);
- break;
- case OPT_BDB_LOCK:
- {
- int type;
- if ((type=find_type(argument, &berkeley_lock_typelib, 2)) > 0)
- berkeley_lock_type=berkeley_lock_types[type-1];
- else
- {
- int err;
- char *end;
- uint length= strlen(argument);
- long value= my_strntol(&my_charset_latin1, argument, length, 10, &end, &err);
- if (end == argument+length)
- berkeley_lock_scan_time= value;
- else
- {
- fprintf(stderr,"Unknown lock type: %s\n",argument);
- exit(1);
- }
- }
- break;
- }
- case OPT_BDB_SHARED:
- berkeley_init_flags&= ~(DB_PRIVATE);
- berkeley_shared_data= 1;
- break;
-#endif /* HAVE_BERKELEY_DB */
- case OPT_BDB:
-#ifdef HAVE_BERKELEY_DB
- if (opt_bdb)
- have_berkeley_db= SHOW_OPTION_YES;
- else
- have_berkeley_db= SHOW_OPTION_DISABLED;
-#endif
- break;
- case OPT_NDBCLUSTER:
-#ifdef HAVE_NDBCLUSTER_DB
- if (opt_ndbcluster)
- have_ndbcluster= SHOW_OPTION_YES;
- else
- have_ndbcluster= SHOW_OPTION_DISABLED;
-#endif
- break;
-#ifdef HAVE_NDBCLUSTER_DB
+#ifdef WITH_NDBCLUSTER_STORAGE_ENGINE
case OPT_NDB_MGMD:
case OPT_NDB_NODEID:
{
@@ -7474,25 +8113,20 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)),
opt_ndb_constrbuf[opt_ndb_constrbuf_len]= 0;
opt_ndbcluster_connectstring= opt_ndb_constrbuf;
break;
-#endif
- case OPT_INNODB:
-#ifdef HAVE_INNOBASE_DB
- if (opt_innodb)
- have_innodb= SHOW_OPTION_YES;
+ case OPT_NDB_DISTRIBUTION:
+ int id;
+ id= find_type_or_exit(argument, &ndb_distribution_typelib, opt->name);
+ 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
- have_innodb= SHOW_OPTION_DISABLED;
-#endif
+ ndb_extra_logging= atoi(argument);
break;
- case OPT_INNODB_DATA_FILE_PATH:
-#ifdef HAVE_INNOBASE_DB
- innobase_data_file_path= argument;
#endif
- break;
-#ifdef HAVE_INNOBASE_DB
- case OPT_INNODB_LOG_ARCHIVE:
- innobase_log_archive= argument ? test(atoi(argument)) : 1;
- break;
-#endif /* HAVE_INNOBASE_DB */
case OPT_MYISAM_RECOVER:
{
if (!argument)
@@ -7508,12 +8142,8 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)),
else
{
myisam_recover_options_str=argument;
- if ((myisam_recover_options=
- find_bit_type(argument, &myisam_recover_typelib)) == ~(ulong) 0)
- {
- fprintf(stderr, "Unknown option to myisam-recover: %s\n",argument);
- exit(1);
- }
+ myisam_recover_options=
+ find_bit_type_or_exit(argument, &myisam_recover_typelib, opt->name);
}
ha_open_options|=HA_OPEN_ABORT_IF_CRASHED;
break;
@@ -7526,15 +8156,10 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)),
myisam_concurrent_insert= 0; /* --skip-concurrent-insert */
break;
case OPT_TC_HEURISTIC_RECOVER:
- {
- if ((tc_heuristic_recover=find_type(argument,
- &tc_heuristic_recover_typelib, 2)) <=0)
- {
- fprintf(stderr, "Unknown option to tc-heuristic-recover: %s\n",argument);
- exit(1);
- }
+ tc_heuristic_recover= find_type_or_exit(argument,
+ &tc_heuristic_recover_typelib,
+ opt->name);
break;
- }
case OPT_MYISAM_STATS_METHOD:
{
ulong method_conv;
@@ -7542,11 +8167,8 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)),
LINT_INIT(method_conv);
myisam_stats_method_str= argument;
- if ((method=find_type(argument, &myisam_stats_method_typelib, 2)) <= 0)
- {
- fprintf(stderr, "Invalid value of myisam_stats_method: %s.\n", argument);
- exit(1);
- }
+ method= find_type_or_exit(argument, &myisam_stats_method_typelib,
+ opt->name);
switch (method-1) {
case 2:
method_conv= MI_STATS_METHOD_IGNORE_NULLS;
@@ -7565,21 +8187,27 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)),
case OPT_SQL_MODE:
{
sql_mode_str= argument;
- if ((global_system_variables.sql_mode=
- find_bit_type(argument, &sql_mode_typelib)) == ~(ulong) 0)
- {
- fprintf(stderr, "Unknown option to sql-mode: %s\n", argument);
- exit(1);
- }
+ global_system_variables.sql_mode=
+ find_bit_type_or_exit(argument, &sql_mode_typelib, opt->name);
global_system_variables.sql_mode= fix_sql_mode(global_system_variables.
sql_mode);
break;
}
+ case OPT_ONE_THREAD:
+ global_system_variables.thread_handling=
+ SCHEDULER_ONE_THREAD_PER_CONNECTION;
+ break;
+ case OPT_THREAD_HANDLING:
+ {
+ global_system_variables.thread_handling=
+ find_type_or_exit(argument, &thread_handling_typelib, opt->name)-1;
+ break;
+ }
case OPT_FT_BOOLEAN_SYNTAX:
- if (ft_boolean_check_syntax_string((byte*) argument))
+ if (ft_boolean_check_syntax_string((uchar*) argument))
{
- fprintf(stderr, "Invalid ft-boolean-syntax string: %s\n", argument);
- exit(1);
+ sql_print_error("Invalid ft-boolean-syntax string: %s\n", argument);
+ return 1;
}
strmake(ft_boolean_syntax, argument, sizeof(ft_boolean_syntax)-1);
break;
@@ -7595,9 +8223,14 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)),
}
return 0;
}
- /* Initiates DEBUG - but no debugging here ! */
-static gptr *
+
+/** Handle arguments for multiple key caches. */
+
+extern "C" uchar **mysql_getopt_value(const char *keyname, uint key_length,
+ const struct my_option *option);
+
+uchar* *
mysql_getopt_value(const char *keyname, uint key_length,
const struct my_option *option)
{
@@ -7612,30 +8245,42 @@ mysql_getopt_value(const char *keyname, uint key_length,
exit(1);
switch (option->id) {
case OPT_KEY_BUFFER_SIZE:
- return (gptr*) &key_cache->param_buff_size;
+ return (uchar**) &key_cache->param_buff_size;
case OPT_KEY_CACHE_BLOCK_SIZE:
- return (gptr*) &key_cache->param_block_size;
+ return (uchar**) &key_cache->param_block_size;
case OPT_KEY_CACHE_DIVISION_LIMIT:
- return (gptr*) &key_cache->param_division_limit;
+ return (uchar**) &key_cache->param_division_limit;
case OPT_KEY_CACHE_AGE_THRESHOLD:
- return (gptr*) &key_cache->param_age_threshold;
+ return (uchar**) &key_cache->param_age_threshold;
}
}
}
- return option->value;
+ return option->value;
}
-static void option_error_reporter(enum loglevel level, const char *format, ...)
+extern "C" void option_error_reporter(enum loglevel level, const char *format, ...);
+
+void option_error_reporter(enum loglevel level, const char *format, ...)
{
va_list args;
va_start(args, format);
- vprint_msg_to_log(level, format, args);
+
+ /* Don't print warnings for --loose options during bootstrap */
+ if (level == ERROR_LEVEL || !opt_bootstrap ||
+ global_system_variables.log_warnings)
+ {
+ vprint_msg_to_log(level, format, args);
+ }
va_end(args);
}
-static void get_options(int argc,char **argv)
+/**
+ @todo
+ - FIXME add EXIT_TOO_MANY_ARGUMENTS to "mysys_err.h" and return that code?
+*/
+static void get_options(int *argc,char **argv)
{
int ho_error;
@@ -7643,42 +8288,21 @@ static void get_options(int argc,char **argv)
strmake(def_ft_boolean_syntax, ft_boolean_syntax,
sizeof(ft_boolean_syntax)-1);
my_getopt_error_reporter= option_error_reporter;
- if ((ho_error= handle_options(&argc, &argv, my_long_options,
- get_one_option)))
+
+ /* 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,
+ mysqld_get_one_option)))
exit(ho_error);
+ (*argc)++; /* add back one for the progname handle_options removes */
+ /* no need to do this for argv as we are discarding it. */
-#ifndef HAVE_NDBCLUSTER_DB
- if (opt_ndbcluster)
- sql_print_warning("this binary does not contain NDBCLUSTER storage engine");
-#endif
-#ifndef HAVE_INNOBASE_DB
- if (opt_innodb)
- sql_print_warning("this binary does not contain INNODB storage engine");
-#endif
-#ifndef HAVE_ISAM
- if (opt_isam)
- sql_print_warning("this binary does not contain ISAM storage engine");
-#endif
-#ifndef HAVE_BERKELEY_DB
- if (opt_bdb)
- sql_print_warning("this binary does not contain BDB storage engine");
-#endif
- if ((opt_log_slow_admin_statements || opt_log_queries_not_using_indexes) &&
+ if ((opt_log_slow_admin_statements || opt_log_queries_not_using_indexes ||
+ opt_log_slow_slave_statements) &&
!opt_slow_log)
- sql_print_warning("options --log-slow-admin-statements and --log-queries-not-using-indexes have no effect if --log-slow-queries is not set");
+ sql_print_warning("options --log-slow-admin-statements, --log-queries-not-using-indexes and --log-slow-slave-statements have no effect if --log_slow_queries is not set");
- if (argc > 0)
- {
- fprintf(stderr, "%s: Too many arguments (first extra is '%s').\nUse --help to get a list of available options\n", my_progname, *argv);
- /* FIXME add EXIT_TOO_MANY_ARGUMENTS to "mysys_err.h" and return that code? */
- exit(1);
- }
-
- if (opt_help)
- {
- usage();
- exit(0);
- }
#if defined(HAVE_BROKEN_REALPATH)
my_use_symdir=0;
my_disable_symlinks=1;
@@ -7698,11 +8322,14 @@ static void get_options(int argc,char **argv)
}
/* 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(OPT_GLOBAL);
#ifndef EMBEDDED_LIBRARY
if (mysqld_chroot)
set_root(mysqld_chroot);
#else
+ global_system_variables.thread_handling = SCHEDULER_NO_THREADS;
max_allowed_packet= global_system_variables.max_allowed_packet;
net_buffer_length= global_system_variables.net_buffer_length;
#endif
@@ -7720,6 +8347,10 @@ static void get_options(int argc,char **argv)
/* Set global variables based on startup options */
myisam_block_size=(uint) 1 << my_bit_log2(opt_myisam_block_size);
+ /* long_query_time is in microseconds */
+ global_system_variables.long_query_time= max_system_variables.long_query_time=
+ (longlong) (long_query_time * 1000000.0);
+
if (opt_short_log_format)
opt_specialflag|= SPECIAL_SHORT_LOG_FORMAT;
@@ -7730,6 +8361,18 @@ static void get_options(int argc,char **argv)
init_global_datetime_format(MYSQL_TIMESTAMP_DATETIME,
&global_system_variables.datetime_format))
exit(1);
+
+#ifdef EMBEDDED_LIBRARY
+ one_thread_scheduler(&thread_scheduler);
+#else
+ if (global_system_variables.thread_handling <=
+ SCHEDULER_ONE_THREAD_PER_CONNECTION)
+ one_thread_per_connection_scheduler(&thread_scheduler);
+ else if (global_system_variables.thread_handling == SCHEDULER_NO_THREADS)
+ one_thread_scheduler(&thread_scheduler);
+ else
+ pool_of_threads_scheduler(&thread_scheduler); /* purecov: tested */
+#endif
}
@@ -7770,14 +8413,15 @@ static char *get_relative_path(const char *path)
}
-/*
+/**
Fix filename and replace extension where 'dir' is relative to
mysql_real_data_home.
- Return 1 if len(path) > FN_REFLEN
+ @return
+ 1 if len(path) > FN_REFLEN
*/
bool
-fn_format_relative_to_data_home(my_string to, const char *name,
+fn_format_relative_to_data_home(char * to, const char *name,
const char *dir, const char *extension)
{
char tmp_path[FN_REFLEN];
@@ -7788,7 +8432,7 @@ fn_format_relative_to_data_home(my_string to, const char *name,
dir=tmp_path;
}
return !fn_format(to, name, dir, extension,
- MY_REPLACE_EXT | MY_UNPACK_FILENAME | MY_SAFE_PATH);
+ MY_APPEND_EXT | MY_UNPACK_FILENAME | MY_SAFE_PATH);
}
@@ -7817,7 +8461,7 @@ static void fix_paths(void)
(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);
opt_plugin_dir_ptr= opt_plugin_dir;
char *sharedir=get_relative_path(SHAREDIR);
@@ -7860,9 +8504,35 @@ static void fix_paths(void)
}
-/*
- Return a bitfield from a string of substrings separated by ','
- returns ~(ulong) 0 on error.
+static ulong find_bit_type_or_exit(const char *x, TYPELIB *bit_lib,
+ const char *option)
+{
+ ulong res;
+
+ const char **ptr;
+
+ if ((res= find_bit_type(x, bit_lib)) == ~(ulong) 0)
+ {
+ ptr= bit_lib->type_names;
+ if (!*x)
+ fprintf(stderr, "No option given to %s\n", option);
+ else
+ fprintf(stderr, "Wrong option to %s. Option(s) given: %s\n", option, x);
+ fprintf(stderr, "Alternatives are: '%s'", *ptr);
+ while (*++ptr)
+ fprintf(stderr, ",'%s'", *ptr);
+ fprintf(stderr, "\n");
+ exit(1);
+ }
+ return res;
+}
+
+
+/**
+ @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)
@@ -7877,7 +8547,7 @@ static ulong find_bit_type(const char *x, TYPELIB *bit_lib)
found=0;
found_end= 0;
- pos=(my_string) x;
+ pos=(char *) x;
while (*pos == ' ') pos++;
found_end= *pos == 0;
while (!found_end)
@@ -7921,16 +8591,16 @@ skip: ;
} /* find_bit_type */
-/*
- Check if file system used for databases is case insensitive
+/**
+ Check if file system used for databases is case insensitive.
- SYNOPSIS
- test_if_case_sensitive()
- dir_name Directory to test
+ @param dir_name Directory to test
- RETURN
+ @retval
-1 Don't know (Test failed)
+ @retval
0 File system is case sensitive
+ @retval
1 File system is case insensitive
*/
@@ -7961,10 +8631,11 @@ static int test_if_case_insensitive(const char *dir_name)
}
-/* Create file to store pid number */
-
#ifndef EMBEDDED_LIBRARY
+/**
+ Create file to store pid number.
+*/
static void create_pid_file()
{
File file;
@@ -7974,7 +8645,7 @@ static void create_pid_file()
char buff[21], *end;
end= int10_to_str((long) getpid(), buff, 10);
*end++= '\n';
- if (!my_write(file, (byte*) buff, (uint) (end-buff), MYF(MY_WME | MY_NABP)))
+ if (!my_write(file, (uchar*) buff, (uint) (end-buff), MYF(MY_WME | MY_NABP)))
{
(void) my_close(file, MYF(0));
return;
@@ -7986,7 +8657,7 @@ static void create_pid_file()
}
#endif /* EMBEDDED_LIBRARY */
-/* Clear most status variables */
+/** Clear most status variables. */
void refresh_status(THD *thd)
{
pthread_mutex_lock(&LOCK_status);
@@ -7995,17 +8666,16 @@ void refresh_status(THD *thd)
add_to_status(&global_status_var, &thd->status_var);
/* Reset thread's status variables */
- bzero((char*) &thd->status_var, sizeof(thd->status_var));
+ bzero((uchar*) &thd->status_var, sizeof(thd->status_var));
/* Reset some global variables */
- for (struct show_var_st *ptr=status_vars; ptr->name; ptr++)
- {
- if (ptr->type == SHOW_LONG)
- *(ulong*) ptr->value= 0;
- }
+ reset_status_vars();
/* Reset the counters of all key caches (default and named). */
process_key_caches(reset_key_cache_counters);
+#ifdef COMMUNITY_SERVER
+ flush_status_time= time((time_t*) 0);
+#endif
pthread_mutex_unlock(&LOCK_status);
/*
@@ -8021,28 +8691,14 @@ void refresh_status(THD *thd)
/*****************************************************************************
- Instantiate have_xyx for missing storage engines
+ Instantiate variables for missing storage engines
+ This section should go away soon
*****************************************************************************/
-#undef have_berkeley_db
-#undef have_innodb
-#undef have_ndbcluster
-#undef have_example_db
-#undef have_archive_db
-#undef have_csv_db
-#undef have_federated_db
-#undef have_partition_db
-#undef have_blackhole_db
-
-SHOW_COMP_OPTION have_berkeley_db= SHOW_OPTION_NO;
-SHOW_COMP_OPTION have_innodb= SHOW_OPTION_NO;
-SHOW_COMP_OPTION have_ndbcluster= SHOW_OPTION_NO;
-SHOW_COMP_OPTION have_example_db= SHOW_OPTION_NO;
-SHOW_COMP_OPTION have_archive_db= SHOW_OPTION_NO;
-SHOW_COMP_OPTION have_csv_db= SHOW_OPTION_NO;
-SHOW_COMP_OPTION have_federated_db= SHOW_OPTION_NO;
-SHOW_COMP_OPTION have_partition_db= SHOW_OPTION_NO;
-SHOW_COMP_OPTION have_blackhole_db= SHOW_OPTION_NO;
+#ifndef WITH_NDBCLUSTER_STORAGE_ENGINE
+ulong ndb_cache_check_time;
+ulong ndb_extra_logging;
+#endif
/*****************************************************************************
Instantiate templates
diff --git a/sql/mysqld_suffix.h b/sql/mysqld_suffix.h
index b348f272db1..654d7cf88c1 100644
--- a/sql/mysqld_suffix.h
+++ b/sql/mysqld_suffix.h
@@ -13,8 +13,11 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
-/*
- Set MYSQL_SERVER_SUFFIX_STR
+/**
+ @file
+
+ Set MYSQL_SERVER_SUFFIX_STR.
+
The following code is quite ugly as there is no portable way to easily set a
string to the value of a macro
*/
diff --git a/sql/net_serv.cc b/sql/net_serv.cc
index 627a5fae5e3..0a8720bae64 100644
--- a/sql/net_serv.cc
+++ b/sql/net_serv.cc
@@ -13,15 +13,16 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
-/*
+/**
+ @file
+
This file is the net layer API for the MySQL client/server protocol,
which is a tightly coupled, proprietary protocol owned by MySQL AB.
+ @note
Any re-implementations of this protocol must also be under GPL
unless one has got an license from MySQL AB stating otherwise.
-*/
-/*
- Write and read of logical packets to/from socket
+ Write and read of logical packets to/from socket.
Writes are cached into net_buffer_length big packets.
Read packets are reallocated dynamicly when reading big packets.
@@ -36,9 +37,6 @@
HFTODO this must be hidden if we don't want client capabilities in
embedded library
*/
-#ifdef __WIN__
-#include <winsock.h>
-#endif
#include <my_global.h>
#include <mysql.h>
#include <mysql_embed.h>
@@ -50,7 +48,6 @@
#include <violite.h>
#include <signal.h>
#include <errno.h>
-
#ifdef __NETWARE__
#include <sys/select.h>
#endif
@@ -112,22 +109,21 @@ extern void query_cache_insert(NET *net, const char *packet, ulong length);
#define TEST_BLOCKING 8
#define MAX_PACKET_LENGTH (256L*256L*256L-1)
-static my_bool net_write_buff(NET *net,const char *packet,ulong len);
+static my_bool net_write_buff(NET *net,const uchar *packet,ulong len);
- /* Init with packet info */
+/** Init with packet info. */
my_bool my_net_init(NET *net, Vio* vio)
{
DBUG_ENTER("my_net_init");
net->vio = vio;
my_net_local_init(net); /* Set some limits */
- if (!(net->buff=(uchar*) my_malloc((uint32) net->max_packet+
+ if (!(net->buff=(uchar*) my_malloc((size_t) net->max_packet+
NET_HEADER_SIZE + COMP_HEADER_SIZE,
MYF(MY_WME))))
DBUG_RETURN(1);
net->buff_end=net->buff+net->max_packet;
- net->no_send_ok= net->no_send_eof= net->no_send_error= 0;
net->error=0; net->return_errno=0; net->return_status=0;
net->pkt_nr=net->compress_pkt_nr=0;
net->write_pos=net->read_pos = net->buff;
@@ -140,12 +136,11 @@ my_bool my_net_init(NET *net, Vio* vio)
#else
net->query_cache_query= 0;
#endif
- net->report_error= 0;
if (vio != 0) /* If real connection */
{
net->fd = vio_fd(vio); /* For perl DBI/DBD */
-#if defined(MYSQL_SERVER) && !defined(__WIN__) && !defined(__EMX__) && !defined(OS2)
+#if defined(MYSQL_SERVER) && !defined(__WIN__)
if (!(test_flags & TEST_BLOCKING))
{
my_bool old_mode;
@@ -161,28 +156,31 @@ my_bool my_net_init(NET *net, Vio* vio)
void net_end(NET *net)
{
DBUG_ENTER("net_end");
- my_free((gptr) net->buff,MYF(MY_ALLOW_ZERO_PTR));
+ my_free(net->buff,MYF(MY_ALLOW_ZERO_PTR));
net->buff=0;
DBUG_VOID_RETURN;
}
-/* Realloc the packet buffer */
+/** Realloc the packet buffer. */
-my_bool net_realloc(NET *net, ulong length)
+my_bool net_realloc(NET *net, size_t length)
{
uchar *buff;
- ulong pkt_length;
+ size_t pkt_length;
DBUG_ENTER("net_realloc");
- DBUG_PRINT("enter",("length: %lu", length));
+ DBUG_PRINT("enter",("length: %lu", (ulong) length));
if (length >= net->max_packet_size)
{
DBUG_PRINT("error", ("Packet too large. Max size: %lu",
- net->max_packet_size));
+ net->max_packet_size));
+ /* @todo: 1 and 2 codes are identical. */
net->error= 1;
- net->report_error= 1;
net->last_errno= ER_NET_PACKET_TOO_LARGE;
+#ifdef MYSQL_SERVER
+ my_error(ER_NET_PACKET_TOO_LARGE, MYF(0));
+#endif
DBUG_RETURN(1);
}
pkt_length = (length+IO_SIZE-1) & ~(IO_SIZE-1);
@@ -190,35 +188,33 @@ my_bool net_realloc(NET *net, ulong length)
We must allocate some extra bytes for the end 0 and to be able to
read big compressed blocks
*/
- if (!(buff=(uchar*) my_realloc((char*) net->buff, (uint32) pkt_length +
- NET_HEADER_SIZE + COMP_HEADER_SIZE,
- MYF(MY_WME))))
+ if (!(buff= (uchar*) my_realloc((char*) net->buff, pkt_length +
+ NET_HEADER_SIZE + COMP_HEADER_SIZE,
+ MYF(MY_WME))))
{
+ /* @todo: 1 and 2 codes are identical. */
net->error= 1;
- net->report_error= 1;
net->last_errno= ER_OUT_OF_RESOURCES;
+ /* In the server the error is reported by MY_WME flag. */
DBUG_RETURN(1);
}
net->buff=net->write_pos=buff;
- net->buff_end=buff+(net->max_packet=pkt_length);
+ net->buff_end=buff+(net->max_packet= (ulong) pkt_length);
DBUG_RETURN(0);
}
-/*
- Check if there is any data to be read from the socket
-
- SYNOPSIS
- net_data_is_ready()
- sd socket descriptor
+/**
+ Check if there is any data to be read from the socket.
- DESCRIPTION
- Check if there is any data to be read from the socket.
+ @param sd socket descriptor
- RETURN VALUES
- 0 No data to read
- 1 Data or EOF to read
- -1 Don't know if data is ready or not
+ @retval
+ 0 No data to read
+ @retval
+ 1 Data or EOF to read
+ @retval
+ -1 Don't know if data is ready or not
*/
#if !defined(EMBEDDED_LIBRARY)
@@ -262,15 +258,10 @@ static int net_data_is_ready(my_socket sd)
#endif /* EMBEDDED_LIBRARY */
-/*
+/**
Remove unwanted characters from connection
- and check if disconnected
-
- SYNOPSIS
- net_clear()
- net NET handler
+ and check if disconnected.
- DESCRIPTION
Read from socket until there is nothing more to read. Discard
what is read.
@@ -281,60 +272,66 @@ static int net_data_is_ready(my_socket sd)
a FIN packet), then select() considers a socket "ready to read",
in the sense that there's EOF to read, but read() returns 0.
+ @param net NET handler
+ @param clear_buffer if <> 0, then clear all data from comm buff
*/
-void net_clear(NET *net)
+void net_clear(NET *net, my_bool clear_buffer)
{
#if !defined(EMBEDDED_LIBRARY)
- int count, ready;
+ size_t count;
+ int ready;
#endif
DBUG_ENTER("net_clear");
#if !defined(EMBEDDED_LIBRARY)
- while((ready= net_data_is_ready(net->vio->sd)) > 0)
+ if (clear_buffer)
{
- /* The socket is ready */
- if ((count= vio_read(net->vio, (char*) (net->buff),
- (uint32) net->max_packet)) > 0)
+ while ((ready= net_data_is_ready(net->vio->sd)) > 0)
{
- DBUG_PRINT("info",("skipped %d bytes from file: %s",
- count, vio_description(net->vio)));
-#if defined(EXTRA_DEBUG) && (MYSQL_VERSION_ID < 51000)
- fprintf(stderr,"skipped %d bytes from file: %s\n",
- count, vio_description(net->vio));
+ /* The socket is ready */
+ if ((long) (count= vio_read(net->vio, net->buff,
+ (size_t) net->max_packet)) > 0)
+ {
+ 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",
+ (long) count, vio_description(net->vio));
#endif
+ }
+ else
+ {
+ DBUG_PRINT("info",("socket ready but only EOF to read - disconnected"));
+ net->error= 2;
+ break;
+ }
}
- else
- {
- DBUG_PRINT("info",("socket ready but only EOF to read - disconnected"));
- net->error= 2;
- break;
- }
- }
#ifdef NET_DATA_IS_READY_CAN_RETURN_MINUS_ONE
- /* 'net_data_is_ready' returned "don't know" */
- if (ready == -1)
- {
- /* Read unblocking to clear net */
- my_bool old_mode;
- if (!vio_blocking(net->vio, FALSE, &old_mode))
+ /* 'net_data_is_ready' returned "don't know" */
+ if (ready == -1)
{
- while ((count= vio_read(net->vio, (char*) (net->buff),
- (uint32) net->max_packet)) > 0)
- DBUG_PRINT("info",("skipped %d bytes from file: %s",
- count, vio_description(net->vio)));
- vio_blocking(net->vio, TRUE, &old_mode);
+ /* Read unblocking to clear net */
+ my_bool old_mode;
+ if (!vio_blocking(net->vio, FALSE, &old_mode))
+ {
+ while ((long) (count= vio_read(net->vio, net->buff,
+ (size_t) net->max_packet)) > 0)
+ DBUG_PRINT("info",("skipped %ld bytes from file: %s",
+ (long) count, vio_description(net->vio)));
+ vio_blocking(net->vio, TRUE, &old_mode);
+ }
}
+#endif /* NET_DATA_IS_READY_CAN_RETURN_MINUS_ONE */
}
-#endif
-#endif
+#endif /* EMBEDDED_LIBRARY */
net->pkt_nr=net->compress_pkt_nr=0; /* Ready for new command */
net->write_pos=net->buff;
DBUG_VOID_RETURN;
}
- /* Flush write_buffer if not empty. */
+/** Flush write_buffer if not empty. */
my_bool net_flush(NET *net)
{
@@ -342,8 +339,8 @@ my_bool net_flush(NET *net)
DBUG_ENTER("net_flush");
if (net->buff != net->write_pos)
{
- error=test(net_real_write(net,(char*) net->buff,
- (ulong) (net->write_pos - net->buff)));
+ error=test(net_real_write(net, net->buff,
+ (size_t) (net->write_pos - net->buff)));
net->write_pos=net->buff;
}
/* Sync packet number if using compression */
@@ -357,17 +354,18 @@ my_bool net_flush(NET *net)
** Write something to server/client buffer
*****************************************************************************/
-/*
- Write a logical packet with packet header
+/**
+ Write a logical packet with packet header.
+
Format: Packet length (3 bytes), packet number(1 byte)
When compression is used a 3 byte compression length is added
- NOTE
+ @note
If compression is used the original package is modified!
*/
my_bool
-my_net_write(NET *net,const char *packet,ulong len)
+my_net_write(NET *net,const uchar *packet,size_t len)
{
uchar buff[NET_HEADER_SIZE];
if (unlikely(!net->vio)) /* nowhere to write */
@@ -382,7 +380,7 @@ my_net_write(NET *net,const char *packet,ulong len)
const ulong z_size = MAX_PACKET_LENGTH;
int3store(buff, z_size);
buff[3]= (uchar) net->pkt_nr++;
- if (net_write_buff(net, (char*) buff, NET_HEADER_SIZE) ||
+ if (net_write_buff(net, buff, NET_HEADER_SIZE) ||
net_write_buff(net, packet, z_size))
return 1;
packet += z_size;
@@ -391,7 +389,7 @@ my_net_write(NET *net,const char *packet,ulong len)
/* Write last packet */
int3store(buff,len);
buff[3]= (uchar) net->pkt_nr++;
- if (net_write_buff(net,(char*) buff,NET_HEADER_SIZE))
+ if (net_write_buff(net, buff, NET_HEADER_SIZE))
return 1;
#ifndef DEBUG_DATA_PACKETS
DBUG_DUMP("packet_header", buff, NET_HEADER_SIZE);
@@ -399,19 +397,9 @@ my_net_write(NET *net,const char *packet,ulong len)
return test(net_write_buff(net,packet,len));
}
-/*
+/**
Send a command to the server.
- SYNOPSIS
- net_write_command()
- net NET handler
- command Command in MySQL server (enum enum_server_command)
- header Header to write after command
- head_len Length of header
- packet Query or parameter to query
- len Length of packet
-
- DESCRIPTION
The reason for having both header and packet is so that libmysql
can easy add a header to a special command (like prepared statements)
without having to re-alloc the string.
@@ -419,24 +407,33 @@ my_net_write(NET *net,const char *packet,ulong len)
As the command is part of the first data packet, we have to do some data
juggling to put the command in there, without having to create a new
packet.
+
This function will split big packets into sub-packets if needed.
(Each sub packet can only be 2^24 bytes)
- RETURN VALUES
+ @param net NET handler
+ @param command Command in MySQL server (enum enum_server_command)
+ @param header Header to write after command
+ @param head_len Length of header
+ @param packet Query or parameter to query
+ @param len Length of packet
+
+ @retval
0 ok
+ @retval
1 error
*/
my_bool
net_write_command(NET *net,uchar command,
- const char *header, ulong head_len,
- const char *packet, ulong len)
+ const uchar *header, size_t head_len,
+ const uchar *packet, size_t len)
{
- ulong length=len+1+head_len; /* 1 extra byte for 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;
DBUG_ENTER("net_write_command");
- DBUG_PRINT("enter",("length: %lu", len));
+ DBUG_PRINT("enter",("length: %lu", (ulong) len));
buff[4]=command; /* For first packet */
@@ -448,7 +445,7 @@ net_write_command(NET *net,uchar command,
{
int3store(buff, MAX_PACKET_LENGTH);
buff[3]= (uchar) net->pkt_nr++;
- if (net_write_buff(net,(char*) buff, header_size) ||
+ if (net_write_buff(net, buff, header_size) ||
net_write_buff(net, header, head_len) ||
net_write_buff(net, packet, len))
DBUG_RETURN(1);
@@ -462,42 +459,39 @@ net_write_command(NET *net,uchar command,
}
int3store(buff,length);
buff[3]= (uchar) net->pkt_nr++;
- DBUG_RETURN(test(net_write_buff(net, (char*) buff, header_size) ||
- (head_len && net_write_buff(net, (char*) header, head_len)) ||
- net_write_buff(net, packet, len) || net_flush(net)));
+ 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)));
}
-/*
+/**
Caching the data in a local buffer before sending it.
- SYNOPSIS
- net_write_buff()
- net Network handler
- packet Packet to send
- len Length of packet
-
- DESCRIPTION
- Fill up net->buffer and send it to the client when full.
+ Fill up net->buffer and send it to the client when full.
If the rest of the to-be-sent-packet is bigger than buffer,
send it in one big block (to avoid copying to internal buffer).
If not, copy the rest of the data to the buffer and return without
sending data.
- NOTES
- The cached buffer can be sent as it is with 'net_flush()'.
+ @param net Network handler
+ @param packet Packet to send
+ @param len Length of packet
+ @note
+ The cached buffer can be sent as it is with 'net_flush()'.
In this code we have to be careful to not send a packet longer than
MAX_PACKET_LENGTH to net_real_write() if we are using the compressed
protocol as we store the length of the compressed packet in 3 bytes.
- RETURN
- 0 ok
- 1
+ @retval
+ 0 ok
+ @retval
+ 1
*/
static my_bool
-net_write_buff(NET *net,const char *packet,ulong len)
+net_write_buff(NET *net, const uchar *packet, ulong len)
{
ulong left_length;
if (net->compress && net->max_packet > MAX_PACKET_LENGTH)
@@ -514,8 +508,8 @@ net_write_buff(NET *net,const char *packet,ulong len)
{
/* Fill up already used packet and write it */
memcpy((char*) net->write_pos,packet,left_length);
- if (net_real_write(net,(char*) net->buff,
- (ulong) (net->write_pos - net->buff) + left_length))
+ if (net_real_write(net, net->buff,
+ (size_t) (net->write_pos - net->buff) + left_length))
return 1;
net->write_pos= net->buff;
packet+= left_length;
@@ -546,16 +540,19 @@ net_write_buff(NET *net,const char *packet,ulong len)
}
-/*
+/**
Read and write one packet using timeouts.
If needed, the packet is compressed before sending.
+
+ @todo
+ - TODO is it needed to set this variable if we have no socket
*/
int
-net_real_write(NET *net,const char *packet,ulong len)
+net_real_write(NET *net,const uchar *packet, size_t len)
{
- long int length;
- char *pos,*end;
+ size_t length;
+ const uchar *pos,*end;
thr_alarm_t alarmed;
#ifndef NO_ALARM
ALARM alarm_buff;
@@ -565,7 +562,7 @@ net_real_write(NET *net,const char *packet,ulong len)
DBUG_ENTER("net_real_write");
#if defined(MYSQL_SERVER) && defined(USE_QUERY_CACHE)
- query_cache_insert(net, packet, len);
+ query_cache_insert(net, (char*) packet, len);
#endif
if (net->error == 2)
@@ -575,35 +572,32 @@ net_real_write(NET *net,const char *packet,ulong len)
#ifdef HAVE_COMPRESS
if (net->compress)
{
- ulong complen;
+ size_t complen;
uchar *b;
uint header_length=NET_HEADER_SIZE+COMP_HEADER_SIZE;
- if (!(b=(uchar*) my_malloc((uint32) len + NET_HEADER_SIZE +
- COMP_HEADER_SIZE, MYF(MY_WME))))
+ if (!(b= (uchar*) my_malloc(len + NET_HEADER_SIZE +
+ COMP_HEADER_SIZE, MYF(MY_WME))))
{
-#ifdef MYSQL_SERVER
- net->last_errno= ER_OUT_OF_RESOURCES;
net->error= 2;
- /* TODO is it needed to set this variable if we have no socket */
- net->report_error= 1;
-#endif
+ net->last_errno= ER_OUT_OF_RESOURCES;
+ /* In the server, the error is reported by MY_WME flag. */
net->reading_or_writing= 0;
DBUG_RETURN(1);
}
memcpy(b+header_length,packet,len);
- if (my_compress((byte*) b+header_length,&len,&complen))
+ if (my_compress(b+header_length, &len, &complen))
complen=0;
int3store(&b[NET_HEADER_SIZE],complen);
int3store(b,len);
b[3]=(uchar) (net->compress_pkt_nr++);
len+= header_length;
- packet= (char*) b;
+ packet= b;
}
#endif /* HAVE_COMPRESS */
#ifdef DEBUG_DATA_PACKETS
- DBUG_DUMP("data",packet,len);
+ DBUG_DUMP("data", packet, len);
#endif
#ifndef NO_ALARM
@@ -615,14 +609,15 @@ net_real_write(NET *net,const char *packet,ulong len)
/* Write timeout is set in my_net_set_write_timeout */
#endif /* NO_ALARM */
- pos=(char*) packet; end=pos+len;
+ pos= packet;
+ end=pos+len;
while (pos != end)
{
- if ((long) (length=vio_write(net->vio,pos,(uint32) (end-pos))) <= 0)
+ if ((long) (length= vio_write(net->vio,pos,(size_t) (end-pos))) <= 0)
{
my_bool interrupted = vio_should_retry(net->vio);
-#if (!defined(__WIN__) && !defined(__EMX__) && !defined(OS2))
- if ((interrupted || length==0) && !thr_alarm_in_use(&alarmed))
+#if !defined(__WIN__)
+ if ((interrupted || length == 0) && !thr_alarm_in_use(&alarmed))
{
if (!thr_alarm(&alarmed, net->write_timeout, &alarm_buff))
{ /* Always true for client */
@@ -636,11 +631,11 @@ net_real_write(NET *net,const char *packet,ulong len)
"%s: my_net_write: fcntl returned error %d, aborting thread\n",
my_progname,vio_errno(net->vio));
#endif /* EXTRA_DEBUG */
-#ifdef MYSQL_SERVER
- net->last_errno= ER_NET_ERROR_ON_WRITE;
-#endif
net->error= 2; /* Close socket */
- net->report_error= 1;
+ net->last_errno= ER_NET_PACKET_TOO_LARGE;
+#ifdef MYSQL_SERVER
+ my_error(ER_NET_PACKET_TOO_LARGE, MYF(0));
+#endif
goto end;
}
retry_count=0;
@@ -648,7 +643,7 @@ net_real_write(NET *net,const char *packet,ulong len)
}
}
else
-#endif /* (!defined(__WIN__) && !defined(__EMX__)) */
+#endif /* !defined(__WIN__) */
if (thr_alarm_in_use(&alarmed) && !thr_got_alarm(&alarmed) &&
interrupted)
{
@@ -667,10 +662,10 @@ net_real_write(NET *net,const char *packet,ulong len)
}
#endif /* defined(THREAD_SAFE_CLIENT) && !defined(MYSQL_SERVER) */
net->error= 2; /* Close socket */
- net->report_error= 1;
-#ifdef MYSQL_SERVER
net->last_errno= (interrupted ? ER_NET_WRITE_INTERRUPTED :
- ER_NET_ERROR_ON_WRITE);
+ ER_NET_ERROR_ON_WRITE);
+#ifdef MYSQL_SERVER
+ my_error(net->last_errno, MYF(0));
#endif /* MYSQL_SERVER */
break;
}
@@ -701,14 +696,14 @@ net_real_write(NET *net,const char *packet,ulong len)
#ifndef NO_ALARM
-static my_bool net_safe_read(NET *net, char *buff, uint32 length,
+static my_bool net_safe_read(NET *net, uchar *buff, size_t length,
thr_alarm_t *alarmed)
{
uint retry_count=0;
while (length > 0)
{
- int tmp;
- if ((tmp=vio_read(net->vio,(char*) net->buff, length)) <= 0)
+ size_t tmp;
+ if ((long) (tmp= vio_read(net->vio, buff, length)) <= 0)
{
my_bool interrupted = vio_should_retry(net->vio);
if (!thr_got_alarm(alarmed) && interrupted)
@@ -719,22 +714,22 @@ static my_bool net_safe_read(NET *net, char *buff, uint32 length,
return 1;
}
length-= tmp;
+ buff+= tmp;
}
return 0;
}
-/*
+/**
Help function to clear the commuication buffer when we get a too big packet.
- SYNOPSIS
- my_net_skip_rest()
- net Communication handle
- remain Bytes to read
- alarmed Parameter for thr_alarm()
- alarm_buff Parameter for thr_alarm()
+ @param net Communication handle
+ @param remain Bytes to read
+ @param alarmed Parameter for thr_alarm()
+ @param alarm_buff Parameter for thr_alarm()
- RETURN VALUES
+ @retval
0 Was able to read the whole packet
+ @retval
1 Got mailformed packet from client
*/
@@ -759,15 +754,15 @@ static my_bool my_net_skip_rest(NET *net, uint32 remain, thr_alarm_t *alarmed,
{
while (remain > 0)
{
- uint length= min(remain, net->max_packet);
- if (net_safe_read(net, (char*) net->buff, length, alarmed))
+ size_t length= min(remain, net->max_packet);
+ if (net_safe_read(net, net->buff, length, alarmed))
DBUG_RETURN(1);
update_statistics(thd_increment_bytes_received(length));
remain -= (uint32) length;
}
if (old != MAX_PACKET_LENGTH)
break;
- if (net_safe_read(net, (char*) net->buff, NET_HEADER_SIZE, alarmed))
+ if (net_safe_read(net, net->buff, NET_HEADER_SIZE, alarmed))
DBUG_RETURN(1);
old=remain= uint3korr(net->buff);
net->pkt_nr++;
@@ -777,17 +772,20 @@ static my_bool my_net_skip_rest(NET *net, uint32 remain, thr_alarm_t *alarmed,
#endif /* NO_ALARM */
-/*
- Reads one packet to net->buff + net->where_b
- Returns length of packet. Long packets are handled by my_net_read().
+/**
+ Reads one packet to net->buff + net->where_b.
+ Long packets are handled by my_net_read().
This function reallocates the net->buff buffer if necessary.
+
+ @return
+ Returns length of packet.
*/
static ulong
-my_real_read(NET *net, ulong *complen)
+my_real_read(NET *net, size_t *complen)
{
uchar *pos;
- long length;
+ size_t length;
uint i,retry_count=0;
ulong len=packet_error;
thr_alarm_t alarmed;
@@ -814,13 +812,13 @@ my_real_read(NET *net, ulong *complen)
while (remain > 0)
{
/* First read is done with non blocking mode */
- if ((int) (length=vio_read(net->vio,(char*) pos,remain)) <= 0L)
+ if ((long) (length= vio_read(net->vio, pos, remain)) <= 0L)
{
my_bool interrupted = vio_should_retry(net->vio);
DBUG_PRINT("info",("vio_read returned %ld errno: %d",
- length, vio_errno(net->vio)));
-#if (!defined(__WIN__) && !defined(__EMX__) && !defined(OS2)) || defined(MYSQL_SERVER)
+ (long) length, vio_errno(net->vio)));
+#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
@@ -846,9 +844,9 @@ my_real_read(NET *net, ulong *complen)
#endif /* EXTRA_DEBUG */
len= packet_error;
net->error= 2; /* Close socket */
- net->report_error= 1;
+ net->last_errno= ER_NET_FCNTL_ERROR;
#ifdef MYSQL_SERVER
- net->last_errno= ER_NET_FCNTL_ERROR;
+ my_error(ER_NET_FCNTL_ERROR, MYF(0));
#endif
goto end;
}
@@ -856,7 +854,7 @@ my_real_read(NET *net, ulong *complen)
continue;
}
}
-#endif /* (!defined(__WIN__) && !defined(__EMX__)) || defined(MYSQL_SERVER) */
+#endif /* (!defined(__WIN__) || defined(MYSQL_SERVER) */
if (thr_alarm_in_use(&alarmed) && !thr_got_alarm(&alarmed) &&
interrupted)
{ /* Probably in MIT threads */
@@ -875,18 +873,19 @@ my_real_read(NET *net, ulong *complen)
}
#endif
DBUG_PRINT("error",("Couldn't read packet: remain: %u errno: %d length: %ld",
- remain, vio_errno(net->vio), length));
+ remain, vio_errno(net->vio), (long) length));
len= packet_error;
net->error= 2; /* Close socket */
- net->report_error= 1;
+ net->last_errno= (vio_was_interrupted(net->vio) ?
+ ER_NET_READ_INTERRUPTED :
+ ER_NET_READ_ERROR);
#ifdef MYSQL_SERVER
- net->last_errno= (vio_was_interrupted(net->vio) ? ER_NET_READ_INTERRUPTED :
- ER_NET_READ_ERROR);
+ my_error(net->last_errno, MYF(0));
#endif
goto end;
}
remain -= (uint32) length;
- pos+= (ulong) length;
+ pos+= length;
update_statistics(thd_increment_bytes_received(length));
}
if (i == 0)
@@ -903,15 +902,18 @@ my_real_read(NET *net, ulong *complen)
(int) net->buff[net->where_b + 3],
net->pkt_nr));
#ifdef EXTRA_DEBUG
- fprintf(stderr,"Packets out of order (Found: %d, expected %d)\n",
+ fflush(stdout);
+ 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);
+ DBUG_ASSERT(0);
#endif
}
len= packet_error;
- net->report_error= 1;
+ /* Not a NET error on the client. XXX: why? */
#ifdef MYSQL_SERVER
- net->last_errno=ER_NET_PACKETS_OUT_OF_ORDER;
+ my_error(ER_NET_PACKETS_OUT_OF_ORDER, MYF(0));
#endif
goto end;
}
@@ -960,21 +962,24 @@ end:
net->reading_or_writing=0;
#ifdef DEBUG_DATA_PACKETS
if (len != packet_error)
- DBUG_DUMP("data",(char*) net->buff+net->where_b, len);
+ DBUG_DUMP("data", net->buff+net->where_b, len);
#endif
return(len);
}
-/*
+/**
Read a packet from the client/server and return it without the internal
package header.
+
If the packet is the first packet of a multi-packet packet
(which is indicated by the length of the packet = 0xffffff) then
all sub packets are read and concatenated.
+
If the packet was compressed, its uncompressed and the length of the
uncompressed packet is returned.
+ @return
The function returns the length of the found packet or packet_error.
net->read_pos points to the read data.
*/
@@ -982,7 +987,7 @@ end:
ulong
my_net_read(NET *net)
{
- ulong len,complen;
+ size_t len, complen;
#ifdef HAVE_COMPRESS
if (!net->compress)
@@ -993,7 +998,7 @@ my_net_read(NET *net)
{
/* First packet of a multi-packet. Concatenate the packets */
ulong save_pos = net->where_b;
- ulong total_length=0;
+ size_t total_length= 0;
do
{
net->where_b += len;
@@ -1091,17 +1096,17 @@ my_net_read(NET *net)
net->where_b=buf_length;
if ((packet_len = my_real_read(net,&complen)) == packet_error)
return packet_error;
- if (my_uncompress((byte*) net->buff + net->where_b, &packet_len,
+ if (my_uncompress(net->buff + net->where_b, packet_len,
&complen))
{
net->error= 2; /* caller will close socket */
- net->report_error= 1;
+ net->last_errno= ER_NET_UNCOMPRESS_ERROR;
#ifdef MYSQL_SERVER
- net->last_errno=ER_NET_UNCOMPRESS_ERROR;
+ my_error(ER_NET_UNCOMPRESS_ERROR, MYF(0));
#endif
return packet_error;
}
- buf_length+=packet_len;
+ buf_length+= complen;
}
net->read_pos= net->buff+ first_packet_offset + NET_HEADER_SIZE;
diff --git a/sql/nt_servc.cc b/sql/nt_servc.cc
index e570898f373..f41fa08f828 100644
--- a/sql/nt_servc.cc
+++ b/sql/nt_servc.cc
@@ -1,8 +1,12 @@
-/* ------------------------------------------------------------------------
- Windows NT Service class library
- Copyright Abandoned 1998 Irena Pancirov - Irnet Snc
- This file is public domain and comes with NO WARRANTY of any kind
- -------------------------------------------------------------------------- */
+/**
+ @file
+
+ @brief
+ Windows NT Service class library.
+
+ Copyright Abandoned 1998 Irena Pancirov - Irnet Snc
+ This file is public domain and comes with NO WARRANTY of any kind
+*/
#include <windows.h>
#include <process.h>
#include <stdio.h>
@@ -73,12 +77,13 @@ BOOL NTService::GetOS()
}
-/* ------------------------------------------------------------------------
- Init() Registers the main service thread with the service manager
+/**
+ Registers the main service thread with the service manager.
+
+ @param ServiceThread pointer to the main programs entry function
+ when the service is started
+*/
- ServiceThread - pointer to the main programs entry function
- when the service is started
- -------------------------------------------------------------------------- */
long NTService::Init(LPCSTR szInternName,void *ServiceThread)
{
@@ -99,13 +104,15 @@ long NTService::Init(LPCSTR szInternName,void *ServiceThread)
}
-/* ------------------------------------------------------------------------
- Install() - Installs the service with Service manager
+/**
+ Installs the service with Service manager.
+
nError values:
- 0 success
- 1 Can't open the Service manager
- 2 Failed to create service
- -------------------------------------------------------------------------- */
+ - 0 success
+ - 1 Can't open the Service manager
+ - 2 Failed to create service.
+*/
+
BOOL NTService::Install(int startType, LPCSTR szInternName,
LPCSTR szDisplayName,
@@ -155,14 +162,16 @@ BOOL NTService::Install(int startType, LPCSTR szInternName,
}
-/* ------------------------------------------------------------------------
- Remove() - Removes the service
+/**
+ Removes the service.
+
nError values:
- 0 success
- 1 Can't open the Service manager
- 2 Failed to locate service
- 3 Failed to delete service
- -------------------------------------------------------------------------- */
+ - 0 success
+ - 1 Can't open the Service manager
+ - 2 Failed to locate service
+ - 3 Failed to delete service.
+*/
+
BOOL NTService::Remove(LPCSTR szInternName)
{
@@ -199,10 +208,10 @@ BOOL NTService::Remove(LPCSTR szInternName)
return ret_value;
}
-/* ------------------------------------------------------------------------
- Stop() - this function should be called before the app. exits to stop
- the service
- -------------------------------------------------------------------------- */
+/**
+ this function should be called before the app. exits to stop
+ the service
+*/
void NTService::Stop(void)
{
SetStatus(SERVICE_STOP_PENDING,NO_ERROR, 0, 1, 60000);
@@ -210,10 +219,11 @@ void NTService::Stop(void)
SetStatus(SERVICE_STOPPED, NO_ERROR, 0, 1, 1000);
}
-/* ------------------------------------------------------------------------
- ServiceMain() - This is the function that is called from the
- service manager to start the service
- -------------------------------------------------------------------------- */
+/**
+ This is the function that is called from the
+ service manager to start the service.
+*/
+
void NTService::ServiceMain(DWORD argc, LPTSTR *argv)
{
@@ -261,6 +271,7 @@ error:
}
+
void NTService::SetRunning()
{
if (pService)
@@ -269,7 +280,7 @@ void NTService::SetRunning()
/* ------------------------------------------------------------------------
- StartService() - starts the appliaction thread
+ StartService() - starts the application thread
-------------------------------------------------------------------------- */
BOOL NTService::StartService()
diff --git a/sql/nt_servc.h b/sql/nt_servc.h
index 9b689e434e1..2f0d07df543 100644
--- a/sql/nt_servc.h
+++ b/sql/nt_servc.h
@@ -1,8 +1,12 @@
-/* ------------------------------------------------------------------------
- Windows NT Service class library
- Copyright Abandoned 1998 Irena Pancirov - Irnet Snc
- This file is public domain and comes with NO WARRANTY of any kind
- -------------------------------------------------------------------------- */
+/**
+ @file
+
+ @brief
+ Windows NT Service class library
+
+ Copyright Abandoned 1998 Irena Pancirov - Irnet Snc
+ This file is public domain and comes with NO WARRANTY of any kind
+*/
// main application thread
typedef void (*THREAD_FC)(void *);
@@ -13,7 +17,7 @@ class NTService
NTService();
~NTService();
- BOOL bOsNT; // true if OS is NT, false for Win95
+ BOOL bOsNT; ///< true if OS is NT, false for Win95
//install optinos
DWORD dwDesiredAccess;
DWORD dwServiceType;
diff --git a/sql/opt_range.cc b/sql/opt_range.cc
index 938254f9ce2..9f8f17eb4ba 100644
--- a/sql/opt_range.cc
+++ b/sql/opt_range.cc
@@ -1,4 +1,4 @@
-/* Copyright (C) 2000-2006 MySQL AB
+/* Copyright 2000-2008 MySQL AB, 2008 Sun Microsystems, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -23,16 +23,42 @@
*/
/*
- Classes in this file are used in the following way:
- 1. For a selection condition a tree of SEL_IMERGE/SEL_TREE/SEL_ARG objects
- is created. #of rows in table and index statistics are ignored at this
- step.
- 2. Created SEL_TREE and index stats data are used to construct a
- TABLE_READ_PLAN-derived object (TRP_*). Several 'candidate' table read
- plans may be created.
- 3. The least expensive table read plan is used to create a tree of
- QUICK_SELECT_I-derived objects which are later used for row retrieval.
- QUICK_RANGEs are also created in this step.
+ This file contains:
+
+ RangeAnalysisModule
+ A module that accepts a condition, index (or partitioning) description,
+ and builds lists of intervals (in index/partitioning space), such that
+ all possible records that match the condition are contained within the
+ intervals.
+ The entry point for the range analysis module is get_mm_tree() function.
+
+ The lists are returned in form of complicated structure of interlinked
+ SEL_TREE/SEL_IMERGE/SEL_ARG objects.
+ See check_quick_keys, find_used_partitions for examples of how to walk
+ this structure.
+ All direct "users" of this module are located within this file, too.
+
+
+ PartitionPruningModule
+ A module that accepts a partitioned table, condition, and finds which
+ partitions we will need to use in query execution. Search down for
+ "PartitionPruningModule" for description.
+ The module has single entry point - prune_partitions() function.
+
+
+ Range/index_merge/groupby-minmax optimizer module
+ A module that accepts a table, condition, and returns
+ - a QUICK_*_SELECT object that can be used to retrieve rows that match
+ the specified condition, or a "no records will match the condition"
+ statement.
+
+ The module entry points are
+ test_quick_select()
+ get_quick_select_for_ref()
+
+
+ Record retrieval code for range/index_merge/groupby-min-max.
+ Implementations of QUICK_*_SELECT classes.
*/
#ifdef USE_PRAGMA_IMPLEMENTATION
@@ -54,11 +80,11 @@
*/
#define double2rows(x) ((ha_rows)(x))
-static int sel_cmp(Field *f,char *a,char *b,uint8 a_flag,uint8 b_flag);
-
-static char is_null_string[2]= {1,0};
+static int sel_cmp(Field *f,uchar *a,uchar *b,uint8 a_flag,uint8 b_flag);
+static uchar is_null_string[2]= {1,0};
+class RANGE_OPT_PARAM;
/*
A construction block of the SEL_ARG-graph.
@@ -248,7 +274,7 @@ public:
ulong use_count;
Field *field;
- char *min_value,*max_value; // Pointer to range
+ uchar *min_value,*max_value; // Pointer to range
/*
eq_tree() requires that left == right == 0 if the type is MAYBE_KEY.
@@ -264,8 +290,8 @@ public:
SEL_ARG() {}
SEL_ARG(SEL_ARG &);
- SEL_ARG(Field *,const char *,const char *);
- SEL_ARG(Field *field, uint8 part, char *min_value, char *max_value,
+ SEL_ARG(Field *,const uchar *, const uchar *);
+ SEL_ARG(Field *field, uint8 part, uchar *min_value, uchar *max_value,
uint8 min_flag, uint8 max_flag, uint8 maybe_flag);
SEL_ARG(enum Type type_arg)
:min_flag(0),elements(1),use_count(1),left(0),right(0),next_key_part(0),
@@ -301,7 +327,7 @@ public:
}
SEL_ARG *clone_and(SEL_ARG* arg)
{ // Get overlapping range
- char *new_min,*new_max;
+ uchar *new_min,*new_max;
uint8 flag_min,flag_max;
if (cmp_min_to_min(arg) >= 0)
{
@@ -333,8 +359,7 @@ public:
return new SEL_ARG(field, part, min_value, arg->max_value,
min_flag, arg->max_flag, maybe_flag | arg->maybe_flag);
}
- SEL_ARG *clone(struct st_qsel_param *param, SEL_ARG *new_parent,
- SEL_ARG **next);
+ SEL_ARG *clone(RANGE_OPT_PARAM *param, SEL_ARG *new_parent, SEL_ARG **next);
bool copy_min(SEL_ARG* arg)
{ // Get overlapping range
@@ -375,7 +400,8 @@ public:
min_value=arg->max_value;
min_flag=arg->max_flag & NEAR_MAX ? 0 : NEAR_MIN;
}
- void store_min(uint length,char **min_key,uint min_key_flag)
+ /* returns a number of keypart values (0 or 1) appended to the key buffer */
+ int store_min(uint length, uchar **min_key,uint min_key_flag)
{
if ((min_flag & GEOM_FLAG) ||
(!(min_flag & NO_MIN_RANGE) &&
@@ -389,12 +415,13 @@ public:
else
memcpy(*min_key,min_value,length);
(*min_key)+= length;
+ return 1;
}
+ return 0;
}
- void store(uint length,char **min_key,uint min_key_flag,
- char **max_key, uint max_key_flag)
+ /* returns a number of keypart values (0 or 1) appended to the key buffer */
+ int store_max(uint length, uchar **max_key, uint max_key_flag)
{
- store_min(length, min_key, min_key_flag);
if (!(max_flag & NO_MAX_RANGE) &&
!(max_key_flag & (NO_MAX_RANGE | NEAR_MAX)))
{
@@ -406,33 +433,41 @@ public:
else
memcpy(*max_key,max_value,length);
(*max_key)+= length;
+ return 1;
}
+ return 0;
}
- void store_min_key(KEY_PART *key,char **range_key, uint *range_key_flag)
+ /* 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)
{
SEL_ARG *key_tree= first();
- key_tree->store(key[key_tree->part].store_length,
- range_key,*range_key_flag,range_key,NO_MAX_RANGE);
+ uint res= key_tree->store_min(key[key_tree->part].store_length,
+ range_key, *range_key_flag);
*range_key_flag|= key_tree->min_flag;
if (key_tree->next_key_part &&
key_tree->next_key_part->part == key_tree->part+1 &&
!(*range_key_flag & (NO_MIN_RANGE | NEAR_MIN)) &&
key_tree->next_key_part->type == SEL_ARG::KEY_RANGE)
- 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);
+ return res;
}
- void store_max_key(KEY_PART *key,char **range_key, uint *range_key_flag)
+ /* 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)
{
SEL_ARG *key_tree= last();
- key_tree->store(key[key_tree->part].store_length,
- range_key, NO_MIN_RANGE, range_key,*range_key_flag);
+ uint res=key_tree->store_max(key[key_tree->part].store_length,
+ range_key, *range_key_flag);
(*range_key_flag)|= key_tree->max_flag;
if (key_tree->next_key_part &&
key_tree->next_key_part->part == key_tree->part+1 &&
!(*range_key_flag & (NO_MAX_RANGE | NEAR_MAX)) &&
key_tree->next_key_part->type == SEL_ARG::KEY_RANGE)
- 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);
+ return res;
}
SEL_ARG *insert(SEL_ARG *key);
@@ -476,7 +511,49 @@ public:
{
return parent->left == this ? &parent->left : &parent->right;
}
- SEL_ARG *clone_tree(struct st_qsel_param *param);
+
+
+ /*
+ Check if this SEL_ARG object represents a single-point interval
+
+ SYNOPSIS
+ is_singlepoint()
+
+ DESCRIPTION
+ Check if this SEL_ARG object (not tree) represents a single-point
+ interval, i.e. if it represents a "keypart = const" or
+ "keypart IS NULL".
+
+ RETURN
+ TRUE This SEL_ARG object represents a singlepoint interval
+ FALSE Otherwise
+ */
+
+ bool is_singlepoint()
+ {
+ /*
+ Check for NEAR_MIN ("strictly less") and NO_MIN_RANGE (-inf < field)
+ flags, and the same for right edge.
+ */
+ if (min_flag || max_flag)
+ return FALSE;
+ uchar *min_val= min_value;
+ uchar *max_val= max_value;
+
+ if (maybe_null)
+ {
+ /* First byte is a NULL value indicator */
+ if (*min_val != *max_val)
+ return FALSE;
+
+ if (*min_val)
+ return TRUE; /* This "x IS NULL" */
+ min_val++;
+ max_val++;
+ }
+ return !field->key_cmp(min_val, max_val);
+ }
+ SEL_ARG *clone_tree(RANGE_OPT_PARAM *param);
};
class SEL_IMERGE;
@@ -485,6 +562,11 @@ class SEL_IMERGE;
class SEL_TREE :public Sql_alloc
{
public:
+ /*
+ Starting an effort to document this field:
+ (for some i, keys[i]->type == SEL_ARG::IMPOSSIBLE) =>
+ (type == SEL_TREE::IMPOSSIBLE)
+ */
enum Type { IMPOSSIBLE, ALWAYS, MAYBE, KEY, KEY_SMALLER } type;
SEL_TREE(enum Type type_arg) :type(type_arg) {}
SEL_TREE() :type(KEY)
@@ -492,7 +574,13 @@ public:
keys_map.clear_all();
bzero((char*) keys,sizeof(keys));
}
- SEL_TREE(SEL_TREE *arg, struct st_qsel_param *param);
+ SEL_TREE(SEL_TREE *arg, RANGE_OPT_PARAM *param);
+ /*
+ Note: there may exist SEL_TREE objects with sel_tree->type=KEY and
+ keys[i]=0 for all i. (SergeyP: it is not clear whether there is any
+ merit in range analyzer functions (e.g. get_mm_parts) returning a
+ pointer to such SEL_TREE instead of NULL)
+ */
SEL_ARG *keys[MAX_KEY];
key_map keys_map; /* bitmask of non-NULL elements in keys */
@@ -511,25 +599,55 @@ public:
/* Note that #records for each key scan is stored in table->quick_rows */
};
+class RANGE_OPT_PARAM
+{
+public:
+ THD *thd; /* Current thread handle */
+ TABLE *table; /* Table being analyzed */
+ COND *cond; /* Used inside get_mm_tree(). */
+ table_map prev_tables;
+ table_map read_tables;
+ table_map current_table; /* Bit of the table being analyzed */
+
+ /* Array of parts of all keys for which range analysis is performed */
+ KEY_PART *key_parts;
+ KEY_PART *key_parts_end;
+ MEM_ROOT *mem_root; /* Memory that will be freed when range analysis completes */
+ MEM_ROOT *old_root; /* Memory that will last until the query end */
+ /*
+ Number of indexes used in range analysis (In SEL_TREE::keys only first
+ #keys elements are not empty)
+ */
+ uint keys;
+
+ /*
+ If true, the index descriptions describe real indexes (and it is ok to
+ call field->optimize_range(real_keynr[...], ...).
+ Otherwise index description describes fake indexes.
+ */
+ bool using_real_indexes;
+
+ bool remove_jump_scans;
+
+ /*
+ used_key_no -> table_key_no translation table. Only makes sense if
+ using_real_indexes==TRUE
+ */
+ uint real_keynr[MAX_KEY];
+ /* Number of SEL_ARG objects allocated by SEL_ARG::clone_tree operations */
+ uint alloced_sel_args;
+};
-typedef struct st_qsel_param {
- THD *thd;
- TABLE *table;
- KEY_PART *key_parts,*key_parts_end;
+class PARAM : public RANGE_OPT_PARAM
+{
+public:
KEY_PART *key[MAX_KEY]; /* First key parts of keys used in the query */
- MEM_ROOT *mem_root, *old_root;
- table_map prev_tables,read_tables,current_table;
- uint baseflag, max_key_part, range_count;
+ longlong baseflag;
+ uint max_key_part, range_count;
- uint keys; /* number of keys used in the query */
-
- /* used_key_no -> table_key_no translation table */
- uint real_keynr[MAX_KEY];
-
- char min_key[MAX_KEY_LENGTH+MAX_FIELD_WIDTH],
+ uchar min_key[MAX_KEY_LENGTH+MAX_FIELD_WIDTH],
max_key[MAX_KEY_LENGTH+MAX_FIELD_WIDTH];
bool quick; // Don't calulate possible keys
- COND *cond;
uint fields_bitmap_size;
MY_BITMAP needed_fields; /* bitmask of fields needed by the query */
@@ -545,9 +663,7 @@ typedef struct st_qsel_param {
/* Number of ranges in the last checked tree->key */
uint n_ranges;
uint8 first_null_comp; /* first null component if any, 0 - otherwise */
- /* Number of SEL_ARG objects allocated by SEL_ARG::clone_tree operations */
- uint alloced_sel_args;
-} PARAM;
+};
class TABLE_READ_PLAN;
class TRP_RANGE;
@@ -558,25 +674,27 @@ class TABLE_READ_PLAN;
struct st_ror_scan_info;
-static SEL_TREE * get_mm_parts(PARAM *param,COND *cond_func,Field *field,
+static SEL_TREE * get_mm_parts(RANGE_OPT_PARAM *param,COND *cond_func,Field *field,
Item_func::Functype type,Item *value,
Item_result cmp_type);
-static SEL_ARG *get_mm_leaf(PARAM *param,COND *cond_func,Field *field,
+static SEL_ARG *get_mm_leaf(RANGE_OPT_PARAM *param,COND *cond_func,Field *field,
KEY_PART *key_part,
Item_func::Functype type,Item *value);
-static SEL_TREE *get_mm_tree(PARAM *param,COND *cond);
+static SEL_TREE *get_mm_tree(RANGE_OPT_PARAM *param,COND *cond);
static bool is_key_scan_ror(PARAM *param, uint keynr, uint8 nparts);
-static ha_rows check_quick_select(PARAM *param,uint index,SEL_ARG *key_tree);
+static ha_rows check_quick_select(PARAM *param,uint index,SEL_ARG *key_tree,
+ bool update_tbl_stats);
static ha_rows check_quick_keys(PARAM *param,uint index,SEL_ARG *key_tree,
- char *min_key,uint min_key_flag,
- char *max_key, uint max_key_flag);
+ uchar *min_key, uint min_key_flag, int,
+ uchar *max_key, uint max_key_flag, int);
QUICK_RANGE_SELECT *get_quick_select(PARAM *param,uint index,
SEL_ARG *key_tree,
MEM_ROOT *alloc = NULL);
static TRP_RANGE *get_key_scans_params(PARAM *param, SEL_TREE *tree,
bool index_read_must_be_used,
+ bool update_tbl_stats,
double read_time);
static
TRP_ROR_INTERSECT *get_best_ror_intersect(const PARAM *param, SEL_TREE *tree,
@@ -603,21 +721,22 @@ static void print_ror_scans_arr(TABLE *table, const char *msg,
static void print_quick(QUICK_SELECT_I *quick, const key_map *needed_reg);
#endif
-static SEL_TREE *tree_and(PARAM *param,SEL_TREE *tree1,SEL_TREE *tree2);
-static SEL_TREE *tree_or(PARAM *param,SEL_TREE *tree1,SEL_TREE *tree2);
+static SEL_TREE *tree_and(RANGE_OPT_PARAM *param,SEL_TREE *tree1,SEL_TREE *tree2);
+static SEL_TREE *tree_or(RANGE_OPT_PARAM *param,SEL_TREE *tree1,SEL_TREE *tree2);
static SEL_ARG *sel_add(SEL_ARG *key1,SEL_ARG *key2);
-static SEL_ARG *key_or(PARAM *param, SEL_ARG *key1,SEL_ARG *key2);
-static SEL_ARG *key_and(PARAM *param, SEL_ARG *key1,SEL_ARG *key2,uint clone_flag);
+static SEL_ARG *key_or(RANGE_OPT_PARAM *param, SEL_ARG *key1, SEL_ARG *key2);
+static SEL_ARG *key_and(RANGE_OPT_PARAM *param, SEL_ARG *key1, SEL_ARG *key2,
+ uint clone_flag);
static bool get_range(SEL_ARG **e1,SEL_ARG **e2,SEL_ARG *root1);
bool get_quick_keys(PARAM *param,QUICK_RANGE_SELECT *quick,KEY_PART *key,
- SEL_ARG *key_tree,char *min_key,uint min_key_flag,
- char *max_key,uint max_key_flag);
+ SEL_ARG *key_tree, uchar *min_key,uint min_key_flag,
+ uchar *max_key,uint max_key_flag);
static bool eq_tree(SEL_ARG* a,SEL_ARG *b);
static SEL_ARG null_element(SEL_ARG::IMPOSSIBLE);
-static bool null_part_in_key(KEY_PART *key_part, const char *key,
+static bool null_part_in_key(KEY_PART *key_part, const uchar *key,
uint length);
-bool sel_trees_can_be_ored(SEL_TREE *tree1, SEL_TREE *tree2, PARAM* param);
+bool sel_trees_can_be_ored(SEL_TREE *tree1, SEL_TREE *tree2, RANGE_OPT_PARAM* param);
/*
@@ -649,10 +768,10 @@ public:
trees_next(trees),
trees_end(trees + PREALLOCED_TREES)
{}
- SEL_IMERGE (SEL_IMERGE *arg, PARAM *param);
- int or_sel_tree(PARAM *param, SEL_TREE *tree);
- int or_sel_tree_with_checks(PARAM *param, SEL_TREE *new_tree);
- int or_sel_imerge_with_checks(PARAM *param, SEL_IMERGE* imerge);
+ SEL_IMERGE (SEL_IMERGE *arg, RANGE_OPT_PARAM *param);
+ int or_sel_tree(RANGE_OPT_PARAM *param, SEL_TREE *tree);
+ int or_sel_tree_with_checks(RANGE_OPT_PARAM *param, SEL_TREE *new_tree);
+ int or_sel_imerge_with_checks(RANGE_OPT_PARAM *param, SEL_IMERGE* imerge);
};
@@ -668,12 +787,12 @@ public:
-1 - Out of memory.
*/
-int SEL_IMERGE::or_sel_tree(PARAM *param, SEL_TREE *tree)
+int SEL_IMERGE::or_sel_tree(RANGE_OPT_PARAM *param, SEL_TREE *tree)
{
if (trees_next == trees_end)
{
const int realloc_ratio= 2; /* Double size for next round */
- uint old_elements= (uint) (trees_end - trees);
+ uint old_elements= (trees_end - trees);
uint old_size= sizeof(SEL_TREE**) * old_elements;
uint new_size= old_size * realloc_ratio;
SEL_TREE **new_trees;
@@ -719,7 +838,7 @@ int SEL_IMERGE::or_sel_tree(PARAM *param, SEL_TREE *tree)
-1 An error occurred.
*/
-int SEL_IMERGE::or_sel_tree_with_checks(PARAM *param, SEL_TREE *new_tree)
+int SEL_IMERGE::or_sel_tree_with_checks(RANGE_OPT_PARAM *param, SEL_TREE *new_tree)
{
for (SEL_TREE** tree = trees;
tree != trees_next;
@@ -753,7 +872,7 @@ int SEL_IMERGE::or_sel_tree_with_checks(PARAM *param, SEL_TREE *new_tree)
-1 - An error occurred
*/
-int SEL_IMERGE::or_sel_imerge_with_checks(PARAM *param, SEL_IMERGE* imerge)
+int SEL_IMERGE::or_sel_imerge_with_checks(RANGE_OPT_PARAM *param, SEL_IMERGE* imerge)
{
for (SEL_TREE** tree= imerge->trees;
tree != imerge->trees_next;
@@ -766,7 +885,7 @@ int SEL_IMERGE::or_sel_imerge_with_checks(PARAM *param, SEL_IMERGE* imerge)
}
-SEL_TREE::SEL_TREE(SEL_TREE *arg, PARAM *param): Sql_alloc()
+SEL_TREE::SEL_TREE(SEL_TREE *arg, RANGE_OPT_PARAM *param): Sql_alloc()
{
keys_map= arg->keys_map;
type= arg->type;
@@ -790,7 +909,7 @@ SEL_TREE::SEL_TREE(SEL_TREE *arg, PARAM *param): Sql_alloc()
}
-SEL_IMERGE::SEL_IMERGE (SEL_IMERGE *arg, PARAM *param) : Sql_alloc()
+SEL_IMERGE::SEL_IMERGE (SEL_IMERGE *arg, RANGE_OPT_PARAM *param) : Sql_alloc()
{
uint elements= (arg->trees_end - arg->trees);
if (elements > PREALLOCED_TREES)
@@ -854,7 +973,7 @@ inline void imerge_list_and_list(List<SEL_IMERGE> *im1, List<SEL_IMERGE> *im2)
other Error, both passed lists are unusable
*/
-int imerge_list_or_list(PARAM *param,
+int imerge_list_or_list(RANGE_OPT_PARAM *param,
List<SEL_IMERGE> *im1,
List<SEL_IMERGE> *im2)
{
@@ -874,7 +993,7 @@ int imerge_list_or_list(PARAM *param,
other Error
*/
-int imerge_list_or_tree(PARAM *param,
+int imerge_list_or_tree(RANGE_OPT_PARAM *param,
List<SEL_IMERGE> *im1,
SEL_TREE *tree)
{
@@ -939,7 +1058,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((gptr) (head->sort.io_cache),MYF(0));
+ my_free(head->sort.io_cache, MYF(0));
head->sort.io_cache=0;
}
DBUG_RETURN(select);
@@ -983,6 +1102,10 @@ QUICK_RANGE_SELECT::QUICK_RANGE_SELECT(THD *thd, TABLE *table, uint key_nr,
bool no_alloc, MEM_ROOT *parent_alloc)
:dont_free(0),error(0),free_file(0),in_range(0),cur_range(NULL),last_range(0)
{
+ my_bitmap_map *bitmap;
+ DBUG_ENTER("QUICK_RANGE_SELECT::QUICK_RANGE_SELECT");
+
+ in_ror_merged_scan= 0;
sorted= 0;
index= key_nr;
head= table;
@@ -1006,6 +1129,19 @@ 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 */
+ if (!(bitmap= (my_bitmap_map*) my_malloc(head->s->column_bitmap_size,
+ MYF(MY_WME))))
+ {
+ column_bitmap.bitmap= 0;
+ error= 1;
+ }
+ else
+ bitmap_init(&column_bitmap, bitmap, head->s->fields, FALSE);
+ DBUG_VOID_RETURN;
}
@@ -1044,18 +1180,18 @@ QUICK_RANGE_SELECT::~QUICK_RANGE_SELECT()
{
DBUG_PRINT("info", ("Freeing separate handler 0x%lx (free: %d)", (long) file,
free_file));
- file->reset();
file->ha_external_lock(current_thd, F_UNLCK);
file->close();
+ delete file;
}
}
delete_dynamic(&ranges); /* ranges are allocated in alloc */
free_root(&alloc,MYF(0));
+ my_free((char*) column_bitmap.bitmap, MYF(MY_ALLOW_ZERO_PTR));
}
- if (multi_range)
- my_free((char*) multi_range, MYF(0));
- if (multi_range_buff)
- my_free((char*) multi_range_buff, MYF(0));
+ head->column_bitmaps_set(save_read_set, save_write_set);
+ x_free(multi_range);
+ x_free(multi_range_buff);
DBUG_VOID_RETURN;
}
@@ -1131,8 +1267,8 @@ QUICK_ROR_INTERSECT_SELECT::QUICK_ROR_INTERSECT_SELECT(THD *thd_param,
init_sql_alloc(&alloc, thd->variables.range_alloc_block_size, 0);
else
bzero(&alloc, sizeof(MEM_ROOT));
- last_rowid= (byte*)alloc_root(parent_alloc? parent_alloc : &alloc,
- head->file->ref_length);
+ last_rowid= (uchar*) alloc_root(parent_alloc? parent_alloc : &alloc,
+ head->file->ref_length);
}
@@ -1178,23 +1314,20 @@ int QUICK_ROR_INTERSECT_SELECT::init()
int QUICK_RANGE_SELECT::init_ror_merged_scan(bool reuse_handler)
{
- handler *save_file= file;
+ handler *save_file= file, *org_file;
+ THD *thd;
DBUG_ENTER("QUICK_RANGE_SELECT::init_ror_merged_scan");
+ in_ror_merged_scan= 1;
if (reuse_handler)
{
- DBUG_PRINT("info", ("Reusing handler %p", file));
- if (!head->no_keyread)
- {
- head->key_read= 1;
- file->extra(HA_EXTRA_KEYREAD);
- }
- if (file->extra(HA_EXTRA_RETRIEVE_PRIMARY_KEY) ||
- init() || reset())
+ DBUG_PRINT("info", ("Reusing handler 0x%lx", (long) file));
+ if (init() || reset())
{
DBUG_RETURN(1);
}
- DBUG_RETURN(0);
+ head->column_bitmaps_set(&column_bitmap, &column_bitmap);
+ goto end;
}
/* Create a separate handler object for this quick select */
@@ -1204,7 +1337,7 @@ int QUICK_RANGE_SELECT::init_ror_merged_scan(bool reuse_handler)
DBUG_RETURN(0);
}
- THD *thd= current_thd;
+ thd= head->in_use;
if (!(file= head->file->clone(thd->mem_root)))
{
/*
@@ -1214,19 +1347,17 @@ int QUICK_RANGE_SELECT::init_ror_merged_scan(bool reuse_handler)
the storage engine calls in question happen to never fail with the
existing storage engines.
*/
- thd->net.report_error= 1; /* purecov: inspected */
+ my_error(ER_OUT_OF_RESOURCES, MYF(0)); /* purecov: inspected */
/* Caller will free the memory */
goto failure; /* purecov: inspected */
}
+
+ head->column_bitmaps_set(&column_bitmap, &column_bitmap);
+
if (file->ha_external_lock(thd, F_RDLCK))
goto failure;
- if (!head->no_keyread)
- {
- head->key_read= 1;
- file->extra(HA_EXTRA_KEYREAD);
- }
- if (file->extra(HA_EXTRA_RETRIEVE_PRIMARY_KEY) ||
- init() || reset())
+
+ if (init() || reset())
{
file->ha_external_lock(thd, F_UNLCK);
file->close();
@@ -1234,9 +1365,32 @@ int QUICK_RANGE_SELECT::init_ror_merged_scan(bool reuse_handler)
}
free_file= TRUE;
last_rowid= file->ref;
+
+end:
+ /*
+ 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
+ head->read_set and head->write_set to use this bitmap.
+ The now bitmap is stored in 'column_bitmap' which is used in ::get_next()
+ */
+ org_file= head->file;
+ head->file= file;
+ /* We don't have to set 'head->keyread' here as the 'file' is unique */
+ if (!head->no_keyread)
+ {
+ head->key_read= 1;
+ head->mark_columns_used_by_index(index);
+ }
+ head->prepare_for_position();
+ head->file= org_file;
+ bitmap_copy(&column_bitmap, head->read_set);
+ head->column_bitmaps_set(&column_bitmap, &column_bitmap);
+
DBUG_RETURN(0);
failure:
+ head->column_bitmaps_set(save_read_set, save_write_set);
+ delete file;
file= save_file;
DBUG_RETURN(1);
}
@@ -1379,7 +1533,7 @@ int QUICK_ROR_UNION_SELECT::init()
DBUG_RETURN(1);
}
- if (!(cur_rowid= (byte*)alloc_root(&alloc, 2*head->file->ref_length)))
+ if (!(cur_rowid= (uchar*) alloc_root(&alloc, 2*head->file->ref_length)))
DBUG_RETURN(1);
prev_rowid= cur_rowid + head->file->ref_length;
DBUG_RETURN(0);
@@ -1397,7 +1551,7 @@ int QUICK_ROR_UNION_SELECT::init()
val2 Second merged select
*/
-int QUICK_ROR_UNION_SELECT::queue_cmp(void *arg, byte *val1, byte *val2)
+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,
@@ -1448,7 +1602,7 @@ int QUICK_ROR_UNION_SELECT::reset()
DBUG_RETURN(error);
}
quick->save_last_pos();
- queue_insert(&queue, (byte*)quick);
+ queue_insert(&queue, (uchar*)quick);
}
if (head->file->ha_rnd_init(1))
@@ -1481,7 +1635,8 @@ QUICK_ROR_UNION_SELECT::~QUICK_ROR_UNION_SELECT()
QUICK_RANGE::QUICK_RANGE()
:min_key(0),max_key(0),min_length(0),max_length(0),
- flag(NO_MIN_RANGE | NO_MAX_RANGE)
+ flag(NO_MIN_RANGE | NO_MAX_RANGE),
+ min_keypart_map(0), max_keypart_map(0)
{}
SEL_ARG::SEL_ARG(SEL_ARG &arg) :Sql_alloc()
@@ -1508,16 +1663,18 @@ inline void SEL_ARG::make_root()
use_count=0; elements=1;
}
-SEL_ARG::SEL_ARG(Field *f,const char *min_value_arg,const char *max_value_arg)
+SEL_ARG::SEL_ARG(Field *f,const uchar *min_value_arg,
+ const uchar *max_value_arg)
:min_flag(0), max_flag(0), maybe_flag(0), maybe_null(f->real_maybe_null()),
- elements(1), use_count(1), field(f), min_value((char*) min_value_arg),
- max_value((char*) max_value_arg), next(0),prev(0),
+ elements(1), use_count(1), field(f), min_value((uchar*) min_value_arg),
+ max_value((uchar*) max_value_arg), next(0),prev(0),
next_key_part(0),color(BLACK),type(KEY_RANGE)
{
left=right= &null_element;
}
-SEL_ARG::SEL_ARG(Field *field_,uint8 part_,char *min_value_,char *max_value_,
+SEL_ARG::SEL_ARG(Field *field_,uint8 part_,
+ uchar *min_value_, uchar *max_value_,
uint8 min_flag_,uint8 max_flag_,uint8 maybe_flag_)
:min_flag(min_flag_),max_flag(max_flag_),maybe_flag(maybe_flag_),
part(part_),maybe_null(field_->real_maybe_null()), elements(1),use_count(1),
@@ -1527,7 +1684,8 @@ SEL_ARG::SEL_ARG(Field *field_,uint8 part_,char *min_value_,char *max_value_,
left=right= &null_element;
}
-SEL_ARG *SEL_ARG::clone(PARAM *param, SEL_ARG *new_parent, SEL_ARG **next_arg)
+SEL_ARG *SEL_ARG::clone(RANGE_OPT_PARAM *param, SEL_ARG *new_parent,
+ SEL_ARG **next_arg)
{
SEL_ARG *tmp;
@@ -1594,7 +1752,8 @@ SEL_ARG *SEL_ARG::last()
Returns -2 or 2 if the ranges where 'joined' like < 2 and >= 2
*/
-static int sel_cmp(Field *field, char *a,char *b,uint8 a_flag,uint8 b_flag)
+static int sel_cmp(Field *field, uchar *a, uchar *b, uint8 a_flag,
+ uint8 b_flag)
{
int cmp;
/* First check if there was a compare to a min or max element */
@@ -1618,7 +1777,7 @@ static int sel_cmp(Field *field, char *a,char *b,uint8 a_flag,uint8 b_flag)
goto end; // NULL where equal
a++; b++; // Skip NULL marker
}
- cmp=field->key_cmp((byte*) a,(byte*) b);
+ cmp=field->key_cmp(a , b);
if (cmp) return cmp < 0 ? -1 : 1; // The values differed
// Check if the compared equal arguments was defined with open/closed range
@@ -1637,7 +1796,7 @@ static int sel_cmp(Field *field, char *a,char *b,uint8 a_flag,uint8 b_flag)
}
-SEL_ARG *SEL_ARG::clone_tree(PARAM *param)
+SEL_ARG *SEL_ARG::clone_tree(RANGE_OPT_PARAM *param)
{
SEL_ARG tmp_link,*next_arg,*root;
next_arg= &tmp_link;
@@ -1898,7 +2057,7 @@ private:
KEY *index_info;
uint index;
uint key_infix_len;
- byte key_infix[MAX_KEY_LENGTH];
+ uchar key_infix[MAX_KEY_LENGTH];
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. */
@@ -1911,7 +2070,7 @@ public:
uint group_prefix_len_arg, uint used_key_parts_arg,
uint group_key_parts_arg, KEY *index_info_arg,
uint index_arg, uint key_infix_len_arg,
- byte *key_infix_arg,
+ uchar *key_infix_arg,
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),
@@ -1949,33 +2108,27 @@ public:
static int fill_used_fields_bitmap(PARAM *param)
{
TABLE *table= param->table;
- param->fields_bitmap_size= (table->s->fields/8 + 1);
- uchar *tmp;
+ my_bitmap_map *tmp;
uint pk;
param->tmp_covered_fields.bitmap= 0;
- if (!(tmp= (uchar*)alloc_root(param->mem_root,param->fields_bitmap_size)) ||
- bitmap_init(&param->needed_fields, tmp, param->fields_bitmap_size*8,
- FALSE))
+ param->fields_bitmap_size= table->s->column_bitmap_size;
+ if (!(tmp= (my_bitmap_map*) alloc_root(param->mem_root,
+ param->fields_bitmap_size)) ||
+ bitmap_init(&param->needed_fields, tmp, table->s->fields, FALSE))
return 1;
- bitmap_clear_all(&param->needed_fields);
- for (uint i= 0; i < table->s->fields; i++)
- {
- if (param->thd->query_id == table->field[i]->query_id)
- bitmap_set_bit(&param->needed_fields, i+1);
- }
+ bitmap_copy(&param->needed_fields, table->read_set);
+ bitmap_union(&param->needed_fields, table->write_set);
pk= param->table->s->primary_key;
- if (param->table->file->primary_key_is_clustered() && pk != MAX_KEY)
+ if (pk != MAX_KEY && param->table->file->primary_key_is_clustered())
{
/* The table uses clustered PK and it is not internally generated */
KEY_PART_INFO *key_part= param->table->key_info[pk].key_part;
KEY_PART_INFO *key_part_end= key_part +
param->table->key_info[pk].key_parts;
for (;key_part != key_part_end; ++key_part)
- {
- bitmap_clear_bit(&param->needed_fields, key_part->fieldnr);
- }
+ bitmap_clear_bit(&param->needed_fields, key_part->fieldnr-1);
}
return 0;
}
@@ -2000,16 +2153,46 @@ static int fill_used_fields_bitmap(PARAM *param)
quick - Parameter to use when reading records.
In the table struct the following information is updated:
- quick_keys - Which keys can be used
- quick_rows - How many rows the key matches
+ quick_keys - Which keys can be used
+ quick_rows - How many rows the key matches
+ quick_condition_rows - E(# rows that will satisfy the table condition)
+
+ IMPLEMENTATION
+ quick_condition_rows value is obtained as follows:
+
+ It is a minimum of E(#output rows) for all considered table access
+ methods (range and index_merge accesses over various indexes).
+
+ The obtained value is not a true E(#rows that satisfy table condition)
+ but rather a pessimistic estimate. To obtain a true E(#...) one would
+ need to combine estimates of various access methods, taking into account
+ correlations between sets of rows they will return.
+
+ For example, if values of tbl.key1 and tbl.key2 are independent (a right
+ assumption if we have no information about their correlation) then the
+ correct estimate will be:
+
+ E(#rows("tbl.key1 < c1 AND tbl.key2 < c2")) =
+ = E(#rows(tbl.key1 < c1)) / total_rows(tbl) * E(#rows(tbl.key2 < c2)
+
+ which is smaller than
+
+ MIN(E(#rows(tbl.key1 < c1), E(#rows(tbl.key2 < c2)))
+
+ which is currently produced.
TODO
- Check if this function really needs to modify keys_to_use, and change the
- code to pass it by reference if it doesn't.
+ * Change the value returned in quick_condition_rows from a pessimistic
+ estimate to true E(#rows that satisfy table condition).
+ (we can re-use some of E(#rows) calcuation code from index_merge/intersection
+ for this)
+
+ * Check if this function really needs to modify keys_to_use, and change the
+ code to pass it by reference if it doesn't.
- In addition to force_quick_range other means can be (an usually are) used
- to make this function prefer range over full table scan. Figure out if
- force_quick_range is really needed.
+ * In addition to force_quick_range other means can be (an usually are) used
+ to make this function prefer range over full table scan. Figure out if
+ force_quick_range is really needed.
RETURN
-1 if impossible select (i.e. certainly no rows will be selected)
@@ -2027,17 +2210,14 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use,
DBUG_PRINT("enter",("keys_to_use: %lu prev_tables: %lu const_tables: %lu",
(ulong) keys_to_use.to_ulonglong(), (ulong) prev_tables,
(ulong) const_tables));
- DBUG_PRINT("info", ("records: %lu", (ulong) head->file->records));
+ DBUG_PRINT("info", ("records: %lu", (ulong) head->file->stats.records));
delete quick;
quick=0;
needed_reg.clear_all();
quick_keys.clear_all();
- if ((specialflag & SPECIAL_SAFE_MODE) && ! force_quick_range ||
- !limit)
- DBUG_RETURN(0); /* purecov: inspected */
if (keys_to_use.is_clear_all())
DBUG_RETURN(0);
- records= head->file->records;
+ records= head->file->stats.records;
if (!records)
records++; /* purecov: inspected */
scan_time= (double) records / TIME_FOR_COMPARE + 1;
@@ -2055,7 +2235,7 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use,
if (!keys_to_use.is_clear_all())
{
#ifndef EMBEDDED_LIBRARY // Avoid compiler warning
- char buff[STACK_BUFF_ALLOC];
+ uchar buff[STACK_BUFF_ALLOC];
#endif
MEM_ROOT alloc;
SEL_TREE *tree= NULL;
@@ -2068,7 +2248,7 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use,
/* set up parameter that is passed to all functions */
param.thd= thd;
- param.baseflag=head->file->table_flags();
+ param.baseflag= head->file->ha_table_flags();
param.prev_tables=prev_tables | const_tables;
param.read_tables=read_tables;
param.current_table= head->map;
@@ -2078,6 +2258,8 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use,
param.old_root= thd->mem_root;
param.needed_reg= &needed_reg;
param.imerge_cost_buff_size= 0;
+ param.using_real_indexes= TRUE;
+ param.remove_jump_scans= TRUE;
thd->no_errors=1; // Don't warn about NULL
init_sql_alloc(&alloc, thd->variables.range_alloc_block_size, 0);
@@ -2119,6 +2301,7 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use,
key_parts->null_bit= key_part_info->null_bit;
key_parts->image_type =
(key_info->flags & HA_SPATIAL) ? Field::itMBR : Field::itRAW;
+ /* Only HA_PART_KEY_SEG is used */
key_parts->flag= (uint8) key_part_info->key_part_flag;
}
param.real_keynr[param.keys++]=idx;
@@ -2127,9 +2310,9 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use,
param.alloced_sel_args= 0;
/* Calculate cost of full index read for the shortest covering index */
- if (!head->used_keys.is_clear_all())
+ if (!head->covering_keys.is_clear_all())
{
- int key_for_use= find_shortest_key(head, &head->used_keys);
+ int key_for_use= find_shortest_key(head, &head->covering_keys);
double key_read_time= (get_index_only_read_time(&param, records,
key_for_use) +
(double) records / TIME_FOR_COMPARE);
@@ -2153,9 +2336,12 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use,
read_time= (double) HA_POS_ERROR;
goto free_mem;
}
- if (tree->type != SEL_TREE::KEY &&
- tree->type != SEL_TREE::KEY_SMALLER)
- goto free_mem;
+ /*
+ If the tree can't be used for range scans, proceed anyway, as we
+ can construct a group-min-max quick select
+ */
+ if (tree->type != SEL_TREE::KEY && tree->type != SEL_TREE::KEY_SMALLER)
+ tree= NULL;
}
}
@@ -2164,10 +2350,15 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use,
Notice that it can be constructed no matter if there is a range tree.
*/
group_trp= get_best_group_min_max(&param, tree);
- if (group_trp && group_trp->read_cost < best_read_time)
+ if (group_trp)
{
- best_trp= group_trp;
- best_read_time= best_trp->read_cost;
+ param.table->quick_condition_rows= min(group_trp->records,
+ head->file->stats.records);
+ if (group_trp->read_cost < best_read_time)
+ {
+ best_trp= group_trp;
+ best_read_time= best_trp->read_cost;
+ }
}
if (tree)
@@ -2183,7 +2374,7 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use,
bool can_build_covering= FALSE;
/* Get best 'range' plan and prepare data for making other plans */
- if ((range_trp= get_key_scans_params(&param, tree, FALSE,
+ if ((range_trp= get_key_scans_params(&param, tree, FALSE, TRUE,
best_read_time)))
{
best_trp= range_trp;
@@ -2196,9 +2387,6 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use,
table deletes.
*/
if ((thd->lex->sql_command != SQLCOM_DELETE))
-#ifdef NOT_USED
- if ((thd->lex->sql_command != SQLCOM_UPDATE))
-#endif
{
/*
Get best non-covering ROR-intersection plan and prepare data for
@@ -2226,13 +2414,15 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use,
SEL_IMERGE *imerge;
TABLE_READ_PLAN *best_conj_trp= NULL, *new_conj_trp;
LINT_INIT(new_conj_trp); /* no empty index_merge lists possible */
-
DBUG_PRINT("info",("No range reads possible,"
" trying to construct index_merge"));
List_iterator_fast<SEL_IMERGE> it(tree->merges);
while ((imerge= it++))
{
new_conj_trp= get_best_disjunct_quick(&param, imerge, best_read_time);
+ if (new_conj_trp)
+ set_if_smaller(param.table->quick_condition_rows,
+ new_conj_trp->records);
if (!best_conj_trp || (new_conj_trp && new_conj_trp->read_cost <
best_conj_trp->read_cost))
best_conj_trp= new_conj_trp;
@@ -2270,6 +2460,1109 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use,
DBUG_RETURN(records ? test(quick) : -1);
}
+/****************************************************************************
+ * Partition pruning module
+ ****************************************************************************/
+#ifdef WITH_PARTITION_STORAGE_ENGINE
+
+/*
+ PartitionPruningModule
+
+ This part of the code does partition pruning. Partition pruning solves the
+ following problem: given a query over partitioned tables, find partitions
+ that we will not need to access (i.e. partitions that we can assume to be
+ empty) when executing the query.
+ The set of partitions to prune doesn't depend on which query execution
+ plan will be used to execute the query.
+
+ HOW IT WORKS
+
+ Partition pruning module makes use of RangeAnalysisModule. The following
+ examples show how the problem of partition pruning can be reduced to the
+ range analysis problem:
+
+ EXAMPLE 1
+ Consider a query:
+
+ SELECT * FROM t1 WHERE (t1.a < 5 OR t1.a = 10) AND t1.a > 3 AND t1.b='z'
+
+ where table t1 is partitioned using PARTITION BY RANGE(t1.a). An apparent
+ way to find the used (i.e. not pruned away) partitions is as follows:
+
+ 1. analyze the WHERE clause and extract the list of intervals over t1.a
+ for the above query we will get this list: {(3 < t1.a < 5), (t1.a=10)}
+
+ 2. for each interval I
+ {
+ find partitions that have non-empty intersection with I;
+ mark them as used;
+ }
+
+ EXAMPLE 2
+ Suppose the table is partitioned by HASH(part_func(t1.a, t1.b)). Then
+ we need to:
+
+ 1. Analyze the WHERE clause and get a list of intervals over (t1.a, t1.b).
+ The list of intervals we'll obtain will look like this:
+ ((t1.a, t1.b) = (1,'foo')),
+ ((t1.a, t1.b) = (2,'bar')),
+ ((t1,a, t1.b) > (10,'zz'))
+
+ 2. for each interval I
+ {
+ if (the interval has form "(t1.a, t1.b) = (const1, const2)" )
+ {
+ calculate HASH(part_func(t1.a, t1.b));
+ find which partition has records with this hash value and mark
+ it as used;
+ }
+ else
+ {
+ mark all partitions as used;
+ break;
+ }
+ }
+
+ For both examples the step #1 is exactly what RangeAnalysisModule could
+ be used to do, if it was provided with appropriate index description
+ (array of KEY_PART structures).
+ In example #1, we need to provide it with description of index(t1.a),
+ in example #2, we need to provide it with description of index(t1.a, t1.b).
+
+ These index descriptions are further called "partitioning index
+ descriptions". Note that it doesn't matter if such indexes really exist,
+ as range analysis module only uses the description.
+
+ Putting it all together, partitioning module works as follows:
+
+ prune_partitions() {
+ call create_partition_index_description();
+
+ call get_mm_tree(); // invoke the RangeAnalysisModule
+
+ // analyze the obtained interval list and get used partitions
+ call find_used_partitions();
+ }
+
+*/
+
+struct st_part_prune_param;
+struct st_part_opt_info;
+
+typedef void (*mark_full_part_func)(partition_info*, uint32);
+
+/*
+ Partition pruning operation context
+*/
+typedef struct st_part_prune_param
+{
+ RANGE_OPT_PARAM range_param; /* Range analyzer parameters */
+
+ /***************************************************************
+ Following fields are filled in based solely on partitioning
+ definition and not modified after that:
+ **************************************************************/
+ partition_info *part_info; /* Copy of table->part_info */
+ /* Function to get partition id from partitioning fields only */
+ get_part_id_func get_top_partition_id_func;
+ /* Function to mark a partition as used (w/all subpartitions if they exist)*/
+ mark_full_part_func mark_full_partition_used;
+
+ /* Partitioning 'index' description, array of key parts */
+ KEY_PART *key;
+
+ /*
+ Number of fields in partitioning 'index' definition created for
+ partitioning (0 if partitioning 'index' doesn't include partitioning
+ fields)
+ */
+ uint part_fields;
+ uint subpart_fields; /* Same as above for subpartitioning */
+
+ /*
+ Number of the last partitioning field keypart in the index, or -1 if
+ partitioning index definition doesn't include partitioning fields.
+ */
+ int last_part_partno;
+ int last_subpart_partno; /* Same as above for supartitioning */
+
+ /*
+ is_part_keypart[i] == test(keypart #i in partitioning index is a member
+ used in partitioning)
+ Used to maintain current values of cur_part_fields and cur_subpart_fields
+ */
+ my_bool *is_part_keypart;
+ /* Same as above for subpartitioning */
+ my_bool *is_subpart_keypart;
+
+ /***************************************************************
+ Following fields form find_used_partitions() recursion context:
+ **************************************************************/
+ SEL_ARG **arg_stack; /* "Stack" of SEL_ARGs */
+ SEL_ARG **arg_stack_end; /* Top of the stack */
+ /* Number of partitioning fields for which we have a SEL_ARG* in arg_stack */
+ uint cur_part_fields;
+ /* Same as cur_part_fields, but for subpartitioning */
+ uint cur_subpart_fields;
+
+ /* Iterator to be used to obtain the "current" set of used partitions */
+ PARTITION_ITERATOR part_iter;
+
+ /* Initialized bitmap of no_subparts size */
+ MY_BITMAP subparts_bitmap;
+} PART_PRUNE_PARAM;
+
+static bool create_partition_index_description(PART_PRUNE_PARAM *prune_par);
+static int find_used_partitions(PART_PRUNE_PARAM *ppar, SEL_ARG *key_tree);
+static int find_used_partitions_imerge(PART_PRUNE_PARAM *ppar,
+ SEL_IMERGE *imerge);
+static int find_used_partitions_imerge_list(PART_PRUNE_PARAM *ppar,
+ List<SEL_IMERGE> &merges);
+static void mark_all_partitions_as_used(partition_info *part_info);
+
+#ifndef DBUG_OFF
+static void print_partitioning_index(KEY_PART *parts, KEY_PART *parts_end);
+static void dbug_print_field(Field *field);
+static void dbug_print_segment_range(SEL_ARG *arg, KEY_PART *part);
+static void dbug_print_singlepoint_range(SEL_ARG **start, uint num);
+#endif
+
+
+/*
+ Perform partition pruning for a given table and condition.
+
+ SYNOPSIS
+ prune_partitions()
+ thd Thread handle
+ table Table to perform partition pruning for
+ pprune_cond Condition to use for partition pruning
+
+ DESCRIPTION
+ This function assumes that all partitions are marked as unused when it
+ is invoked. The function analyzes the condition, finds partitions that
+ need to be used to retrieve the records that match the condition, and
+ marks them as used by setting appropriate bit in part_info->used_partitions
+ In the worst case all partitions are marked as used.
+
+ NOTE
+ This function returns promptly if called for non-partitioned table.
+
+ RETURN
+ TRUE We've inferred that no partitions need to be used (i.e. no table
+ records will satisfy pprune_cond)
+ FALSE Otherwise
+*/
+
+bool prune_partitions(THD *thd, TABLE *table, Item *pprune_cond)
+{
+ bool retval= FALSE;
+ partition_info *part_info = table->part_info;
+ DBUG_ENTER("prune_partitions");
+
+ if (!part_info)
+ DBUG_RETURN(FALSE); /* not a partitioned table */
+
+ if (!pprune_cond)
+ {
+ mark_all_partitions_as_used(part_info);
+ DBUG_RETURN(FALSE);
+ }
+
+ PART_PRUNE_PARAM prune_param;
+ MEM_ROOT alloc;
+ RANGE_OPT_PARAM *range_par= &prune_param.range_param;
+ my_bitmap_map *old_sets[2];
+
+ prune_param.part_info= part_info;
+ init_sql_alloc(&alloc, thd->variables.range_alloc_block_size, 0);
+ range_par->mem_root= &alloc;
+ range_par->old_root= thd->mem_root;
+
+ if (create_partition_index_description(&prune_param))
+ {
+ mark_all_partitions_as_used(part_info);
+ free_root(&alloc,MYF(0)); // Return memory & allocator
+ DBUG_RETURN(FALSE);
+ }
+
+ dbug_tmp_use_all_columns(table, old_sets,
+ table->read_set, table->write_set);
+ range_par->thd= thd;
+ range_par->table= table;
+ /* range_par->cond doesn't need initialization */
+ range_par->prev_tables= range_par->read_tables= 0;
+ range_par->current_table= table->map;
+
+ range_par->keys= 1; // one index
+ range_par->using_real_indexes= FALSE;
+ range_par->remove_jump_scans= FALSE;
+ range_par->real_keynr[0]= 0;
+ range_par->alloced_sel_args= 0;
+
+ thd->no_errors=1; // Don't warn about NULL
+ thd->mem_root=&alloc;
+
+ bitmap_clear_all(&part_info->used_partitions);
+
+ prune_param.key= prune_param.range_param.key_parts;
+ SEL_TREE *tree;
+ int res;
+
+ tree= get_mm_tree(range_par, pprune_cond);
+ if (!tree)
+ goto all_used;
+
+ if (tree->type == SEL_TREE::IMPOSSIBLE)
+ {
+ retval= TRUE;
+ goto end;
+ }
+
+ if (tree->type != SEL_TREE::KEY && tree->type != SEL_TREE::KEY_SMALLER)
+ goto all_used;
+
+ if (tree->merges.is_empty())
+ {
+ /* Range analysis has produced a single list of intervals. */
+ prune_param.arg_stack_end= prune_param.arg_stack;
+ prune_param.cur_part_fields= 0;
+ prune_param.cur_subpart_fields= 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]))))
+ goto all_used;
+ }
+ else
+ {
+ if (tree->merges.elements == 1)
+ {
+ /*
+ Range analysis has produced a "merge" of several intervals lists, a
+ SEL_TREE that represents an expression in form
+ sel_imerge = (tree1 OR tree2 OR ... OR treeN)
+ that cannot be reduced to one tree. This can only happen when
+ partitioning index has several keyparts and the condition is OR of
+ conditions that refer to different key parts. For example, we'll get
+ here for "partitioning_field=const1 OR subpartitioning_field=const2"
+ */
+ if (-1 == (res= find_used_partitions_imerge(&prune_param,
+ tree->merges.head())))
+ goto all_used;
+ }
+ else
+ {
+ /*
+ Range analysis has produced a list of several imerges, i.e. a
+ structure that represents a condition in form
+ imerge_list= (sel_imerge1 AND sel_imerge2 AND ... AND sel_imergeN)
+ This is produced for complicated WHERE clauses that range analyzer
+ can't really analyze properly.
+ */
+ if (-1 == (res= find_used_partitions_imerge_list(&prune_param,
+ tree->merges)))
+ goto all_used;
+ }
+ }
+
+ /*
+ res == 0 => no used partitions => retval=TRUE
+ res == 1 => some used partitions => retval=FALSE
+ res == -1 - we jump over this line to all_used:
+ */
+ retval= test(!res);
+ goto end;
+
+all_used:
+ retval= FALSE; // some partitions are used
+ mark_all_partitions_as_used(prune_param.part_info);
+end:
+ dbug_tmp_restore_column_maps(table->read_set, table->write_set, old_sets);
+ thd->no_errors=0;
+ thd->mem_root= range_par->old_root;
+ free_root(&alloc,MYF(0)); // Return memory & allocator
+ DBUG_RETURN(retval);
+}
+
+
+/*
+ Store field key image to table record
+
+ SYNOPSIS
+ store_key_image_to_rec()
+ field Field which key image should be stored
+ ptr Field value in key format
+ len Length of the value, in bytes
+
+ DESCRIPTION
+ Copy the field value from its key image to the table record. The source
+ is the value in key image format, occupying len bytes in buffer pointed
+ by ptr. The destination is table record, in "field value in table record"
+ format.
+*/
+
+void store_key_image_to_rec(Field *field, uchar *ptr, uint len)
+{
+ /* Do the same as print_key() does */
+ my_bitmap_map *old_map;
+
+ if (field->real_maybe_null())
+ {
+ if (*ptr)
+ {
+ field->set_null();
+ return;
+ }
+ field->set_notnull();
+ ptr++;
+ }
+ old_map= dbug_tmp_use_all_columns(field->table,
+ field->table->write_set);
+ field->set_key_image(ptr, len);
+ dbug_tmp_restore_column_map(field->table->write_set, old_map);
+}
+
+
+/*
+ For SEL_ARG* array, store sel_arg->min values into table record buffer
+
+ SYNOPSIS
+ store_selargs_to_rec()
+ ppar Partition pruning context
+ start Array of SEL_ARG* for which the minimum values should be stored
+ num Number of elements in the array
+
+ DESCRIPTION
+ For each SEL_ARG* interval in the specified array, store the left edge
+ field value (sel_arg->min, key image format) into the table record.
+*/
+
+static void store_selargs_to_rec(PART_PRUNE_PARAM *ppar, SEL_ARG **start,
+ int num)
+{
+ KEY_PART *parts= ppar->range_param.key_parts;
+ for (SEL_ARG **end= start + num; start != end; start++)
+ {
+ SEL_ARG *sel_arg= (*start);
+ store_key_image_to_rec(sel_arg->field, sel_arg->min_value,
+ parts[sel_arg->part].length);
+ }
+}
+
+
+/* Mark a partition as used in the case when there are no subpartitions */
+static void mark_full_partition_used_no_parts(partition_info* part_info,
+ uint32 part_id)
+{
+ DBUG_ENTER("mark_full_partition_used_no_parts");
+ DBUG_PRINT("enter", ("Mark partition %u as used", part_id));
+ bitmap_set_bit(&part_info->used_partitions, part_id);
+ DBUG_VOID_RETURN;
+}
+
+
+/* Mark a partition as used in the case when there are subpartitions */
+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;
+ DBUG_ENTER("mark_full_partition_used_with_parts");
+
+ for (; start != end; start++)
+ {
+ DBUG_PRINT("info", ("1:Mark subpartition %u as used", start));
+ bitmap_set_bit(&part_info->used_partitions, start);
+ }
+ DBUG_VOID_RETURN;
+}
+
+/*
+ Find the set of used partitions for List<SEL_IMERGE>
+ SYNOPSIS
+ find_used_partitions_imerge_list
+ ppar Partition pruning context.
+ key_tree Intervals tree to perform pruning for.
+
+ DESCRIPTION
+ List<SEL_IMERGE> represents "imerge1 AND imerge2 AND ...".
+ The set of used partitions is an intersection of used partitions sets
+ for imerge_{i}.
+ We accumulate this intersection in a separate bitmap.
+
+ RETURN
+ See find_used_partitions()
+*/
+
+static int find_used_partitions_imerge_list(PART_PRUNE_PARAM *ppar,
+ List<SEL_IMERGE> &merges)
+{
+ MY_BITMAP all_merges;
+ uint bitmap_bytes;
+ my_bitmap_map *bitmap_buf;
+ uint n_bits= ppar->part_info->used_partitions.n_bits;
+ bitmap_bytes= bitmap_buffer_size(n_bits);
+ if (!(bitmap_buf= (my_bitmap_map*) alloc_root(ppar->range_param.mem_root,
+ bitmap_bytes)))
+ {
+ /*
+ Fallback, process just the first SEL_IMERGE. This can leave us with more
+ partitions marked as used then actually needed.
+ */
+ return find_used_partitions_imerge(ppar, merges.head());
+ }
+ bitmap_init(&all_merges, bitmap_buf, n_bits, FALSE);
+ bitmap_set_prefix(&all_merges, n_bits);
+
+ List_iterator<SEL_IMERGE> it(merges);
+ SEL_IMERGE *imerge;
+ while ((imerge=it++))
+ {
+ int res= find_used_partitions_imerge(ppar, imerge);
+ if (!res)
+ {
+ /* no used partitions on one ANDed imerge => no used partitions at all */
+ return 0;
+ }
+
+ if (res != -1)
+ bitmap_intersect(&all_merges, &ppar->part_info->used_partitions);
+
+ if (bitmap_is_clear_all(&all_merges))
+ return 0;
+
+ bitmap_clear_all(&ppar->part_info->used_partitions);
+ }
+ memcpy(ppar->part_info->used_partitions.bitmap, all_merges.bitmap,
+ bitmap_bytes);
+ return 1;
+}
+
+
+/*
+ Find the set of used partitions for SEL_IMERGE structure
+ SYNOPSIS
+ find_used_partitions_imerge()
+ ppar Partition pruning context.
+ key_tree Intervals tree to perform pruning for.
+
+ DESCRIPTION
+ SEL_IMERGE represents "tree1 OR tree2 OR ...". The implementation is
+ trivial - just use mark used partitions for each tree and bail out early
+ if for some tree_{i} all partitions are used.
+
+ RETURN
+ See find_used_partitions().
+*/
+
+static
+int find_used_partitions_imerge(PART_PRUNE_PARAM *ppar, SEL_IMERGE *imerge)
+{
+ int res= 0;
+ for (SEL_TREE **ptree= imerge->trees; ptree < imerge->trees_next; ptree++)
+ {
+ ppar->arg_stack_end= ppar->arg_stack;
+ ppar->cur_part_fields= 0;
+ ppar->cur_subpart_fields= 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))))
+ return -1;
+ }
+ return res;
+}
+
+
+/*
+ Collect partitioning ranges for the SEL_ARG tree and mark partitions as used
+
+ SYNOPSIS
+ find_used_partitions()
+ ppar Partition pruning context.
+ key_tree SEL_ARG range tree to perform pruning for
+
+ DESCRIPTION
+ This function
+ * recursively walks the SEL_ARG* tree collecting partitioning "intervals"
+ * finds the partitions one needs to use to get rows in these intervals
+ * marks these partitions as used.
+ The next session desribes the process in greater detail.
+
+ IMPLEMENTATION
+ TYPES OF RESTRICTIONS THAT WE CAN OBTAIN PARTITIONS FOR
+ We can find out which [sub]partitions to use if we obtain restrictions on
+ [sub]partitioning fields in the following form:
+ 1. "partition_field1=const1 AND ... AND partition_fieldN=constN"
+ 1.1 Same as (1) but for subpartition fields
+
+ If partitioning supports interval analysis (i.e. partitioning is a
+ function of a single table field, and partition_info::
+ get_part_iter_for_interval != NULL), then we can also use condition in
+ this form:
+ 2. "const1 <=? partition_field <=? const2"
+ 2.1 Same as (2) but for subpartition_field
+
+ INFERRING THE RESTRICTIONS FROM SEL_ARG TREE
+
+ The below is an example of what SEL_ARG tree may represent:
+
+ (start)
+ | $
+ | Partitioning keyparts $ subpartitioning keyparts
+ | $
+ | ... ... $
+ | | | $
+ | +---------+ +---------+ $ +-----------+ +-----------+
+ \-| par1=c1 |--| par2=c2 |-----| subpar1=c3|--| subpar2=c5|
+ +---------+ +---------+ $ +-----------+ +-----------+
+ | $ | |
+ | $ | +-----------+
+ | $ | | subpar2=c6|
+ | $ | +-----------+
+ | $ |
+ | $ +-----------+ +-----------+
+ | $ | subpar1=c4|--| subpar2=c8|
+ | $ +-----------+ +-----------+
+ | $
+ | $
+ +---------+ $ +------------+ +------------+
+ | par1=c2 |------------------| subpar1=c10|--| subpar2=c12|
+ +---------+ $ +------------+ +------------+
+ | $
+ ... $
+
+ The up-down connections are connections via SEL_ARG::left and
+ SEL_ARG::right. A horizontal connection to the right is the
+ SEL_ARG::next_key_part connection.
+
+ find_used_partitions() traverses the entire tree via recursion on
+ * SEL_ARG::next_key_part (from left to right on the picture)
+ * SEL_ARG::left|right (up/down on the pic). Left-right recursion is
+ performed for each depth level.
+
+ Recursion descent on SEL_ARG::next_key_part is used to accumulate (in
+ ppar->arg_stack) constraints on partitioning and subpartitioning fields.
+ For the example in the above picture, one of stack states is:
+ in find_used_partitions(key_tree = "subpar2=c5") (***)
+ in find_used_partitions(key_tree = "subpar1=c3")
+ in find_used_partitions(key_tree = "par2=c2") (**)
+ in find_used_partitions(key_tree = "par1=c1")
+ in prune_partitions(...)
+ We apply partitioning limits as soon as possible, e.g. when we reach the
+ depth (**), we find which partition(s) correspond to "par1=c1 AND par2=c2",
+ and save them in ppar->part_iter.
+ When we reach the depth (***), we find which subpartition(s) correspond to
+ "subpar1=c3 AND subpar2=c5", and then mark appropriate subpartitions in
+ appropriate subpartitions as used.
+
+ It is possible that constraints on some partitioning fields are missing.
+ For the above example, consider this stack state:
+ in find_used_partitions(key_tree = "subpar2=c12") (***)
+ in find_used_partitions(key_tree = "subpar1=c10")
+ in find_used_partitions(key_tree = "par1=c2")
+ in prune_partitions(...)
+ Here we don't have constraints for all partitioning fields. Since we've
+ never set the ppar->part_iter to contain used set of partitions, we use
+ its default "all partitions" value. We get subpartition id for
+ "subpar1=c3 AND subpar2=c5", and mark that subpartition as used in every
+ partition.
+
+ The inverse is also possible: we may get constraints on partitioning
+ fields, but not constraints on subpartitioning fields. In that case,
+ calls to find_used_partitions() with depth below (**) will return -1,
+ and we will mark entire partition as used.
+
+ TODO
+ Replace recursion on SEL_ARG::left and SEL_ARG::right with a loop
+
+ RETURN
+ 1 OK, one or more [sub]partitions are marked as used.
+ 0 The passed condition doesn't match any partitions
+ -1 Couldn't infer any partition pruning "intervals" from the passed
+ SEL_ARG* tree (which means that all partitions should be marked as
+ used) Marking partitions as used is the responsibility of the caller.
+*/
+
+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;
+ bool set_full_part_if_bad_ret= FALSE;
+
+ if (key_tree->left != &null_element)
+ {
+ if (-1 == (left_res= find_used_partitions(ppar,key_tree->left)))
+ return -1;
+ }
+
+ 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
+ */
+ 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 (res == -1)
+ {
+ //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
+ */
+ set_full_part_if_bad_ret= TRUE;
+ goto process_next_key_part;
+ }
+
+ if (partno == 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););
+ res= ppar->part_info->
+ get_subpart_iter_for_interval(ppar->part_info,
+ TRUE,
+ key_tree->min_value,
+ key_tree->max_value,
+ 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 */
+
+ uint32 subpart_id;
+ bitmap_clear_all(&ppar->subparts_bitmap);
+ while ((subpart_id= subpart_iter.get_next(&subpart_iter)) !=
+ NOT_A_PARTITION_ID)
+ bitmap_set_bit(&ppar->subparts_bitmap, subpart_id);
+
+ /* Mark each partition as used in each subpartition. */
+ uint32 part_id;
+ 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++)
+ if (bitmap_is_set(&ppar->subparts_bitmap, i))
+ bitmap_set_bit(&ppar->part_info->used_partitions,
+ part_id * ppar->part_info->no_subparts + i);
+ }
+ goto 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)
+ {
+ /*
+ Ok, we've got "fieldN<=>constN"-type SEL_ARGs for all partitioning
+ fields. Save all constN constants into table record buffer.
+ */
+ store_selargs_to_rec(ppar, ppar->arg_stack, ppar->part_fields);
+ DBUG_EXECUTE("info", dbug_print_singlepoint_range(ppar->arg_stack,
+ ppar->part_fields););
+ uint32 part_id;
+ longlong func_value;
+ /* Find in which partition the {const1, ...,constN} tuple goes */
+ if (ppar->get_top_partition_id_func(ppar->part_info, &part_id,
+ &func_value))
+ {
+ res= 0; /* No satisfying partitions */
+ goto pop_and_go_right;
+ }
+ /* Rembember the limit we got - single partition #part_id */
+ init_single_partition_iterator(part_id, &ppar->part_iter);
+
+ /*
+ If there are no subpartitions/we fail to get any limit for them,
+ then we'll mark full partition as used.
+ */
+ set_full_part_if_bad_ret= TRUE;
+ goto process_next_key_part;
+ }
+
+ if (partno == ppar->last_subpart_partno &&
+ ppar->cur_subpart_fields == ppar->subpart_fields)
+ {
+ /*
+ Ok, we've got "fieldN<=>constN"-type SEL_ARGs for all subpartitioning
+ fields. Save all constN constants into table record buffer.
+ */
+ store_selargs_to_rec(ppar, ppar->arg_stack_end - ppar->subpart_fields,
+ ppar->subpart_fields);
+ DBUG_EXECUTE("info", dbug_print_singlepoint_range(ppar->arg_stack_end-
+ ppar->subpart_fields,
+ ppar->subpart_fields););
+ /* Find the subpartition (it's HASH/KEY so we always have one) */
+ partition_info *part_info= ppar->part_info;
+ uint32 part_id, subpart_id;
+
+ if (part_info->get_subpartition_id(part_info, &subpart_id))
+ return 0;
+
+ /* Mark this partition as used in each subpartition. */
+ while ((part_id= ppar->part_iter.get_next(&ppar->part_iter)) !=
+ NOT_A_PARTITION_ID)
+ {
+ bitmap_set_bit(&part_info->used_partitions,
+ part_id * part_info->no_subparts + subpart_id);
+ }
+ res= 1; /* Some partitions were marked as used */
+ goto pop_and_go_right;
+ }
+ }
+ else
+ {
+ /*
+ Can't handle condition on current key part. If we're that deep that
+ 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;
+ }
+ }
+
+process_next_key_part:
+ if (key_tree->next_key_part)
+ res= find_used_partitions(ppar, key_tree->next_key_part);
+ else
+ res= -1;
+
+ if (set_full_part_if_bad_ret)
+ {
+ if (res == -1)
+ {
+ /* Got "full range" for subpartitioning fields */
+ uint32 part_id;
+ bool found= FALSE;
+ while ((part_id= ppar->part_iter.get_next(&ppar->part_iter)) !=
+ NOT_A_PARTITION_ID)
+ {
+ ppar->mark_full_partition_used(ppar->part_info, part_id);
+ found= TRUE;
+ }
+ res= test(found);
+ }
+ /*
+ Restore the "used partitions iterator" to the default setting that
+ specifies iteration over all partitions.
+ */
+ 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];
+ }
+
+ if (res == -1)
+ return -1;
+go_right:
+ if (key_tree->right != &null_element)
+ {
+ if (-1 == (right_res= find_used_partitions(ppar,key_tree->right)))
+ return -1;
+ }
+ return (left_res || right_res || res);
+}
+
+
+static void mark_all_partitions_as_used(partition_info *part_info)
+{
+ bitmap_set_all(&part_info->used_partitions);
+}
+
+
+/*
+ Check if field types allow to construct partitioning index description
+
+ SYNOPSIS
+ fields_ok_for_partition_index()
+ pfield NULL-terminated array of pointers to fields.
+
+ DESCRIPTION
+ For an array of fields, check if we can use all of the fields to create
+ partitioning index description.
+
+ We can't process GEOMETRY fields - for these fields singlepoint intervals
+ cant be generated, and non-singlepoint are "special" kinds of intervals
+ to which our processing logic can't be applied.
+
+ It is not known if we could process ENUM fields, so they are disabled to be
+ on the safe side.
+
+ RETURN
+ TRUE Yes, fields can be used in partitioning index
+ FALSE Otherwise
+*/
+
+static bool fields_ok_for_partition_index(Field **pfield)
+{
+ if (!pfield)
+ return FALSE;
+ for (; (*pfield); pfield++)
+ {
+ enum_field_types ftype= (*pfield)->real_type();
+ if (ftype == MYSQL_TYPE_ENUM || ftype == MYSQL_TYPE_GEOMETRY)
+ return FALSE;
+ }
+ return TRUE;
+}
+
+
+/*
+ Create partition index description and fill related info in the context
+ struct
+
+ SYNOPSIS
+ create_partition_index_description()
+ prune_par INOUT Partition pruning context
+
+ DESCRIPTION
+ Create partition index description. Partition index description is:
+
+ part_index(used_fields_list(part_expr), used_fields_list(subpart_expr))
+
+ If partitioning/sub-partitioning uses BLOB or Geometry fields, then
+ corresponding fields_list(...) is not included into index description
+ and we don't perform partition pruning for partitions/subpartitions.
+
+ RETURN
+ TRUE Out of memory or can't do partition pruning at all
+ FALSE OK
+*/
+
+static bool create_partition_index_description(PART_PRUNE_PARAM *ppar)
+{
+ RANGE_OPT_PARAM *range_par= &(ppar->range_param);
+ partition_info *part_info= ppar->part_info;
+ 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;
+ used_subpart_fields=
+ fields_ok_for_partition_index(part_info->subpart_field_array)?
+ part_info->no_subpart_fields : 0;
+
+ uint total_parts= used_part_fields + used_subpart_fields;
+
+ ppar->part_fields= used_part_fields;
+ ppar->last_part_partno= (int)used_part_fields - 1;
+
+ ppar->subpart_fields= used_subpart_fields;
+ ppar->last_subpart_partno=
+ used_subpart_fields?(int)(used_part_fields + used_subpart_fields - 1): -1;
+
+ if (part_info->is_sub_partitioned())
+ {
+ ppar->mark_full_partition_used= mark_full_partition_used_with_parts;
+ ppar->get_top_partition_id_func= part_info->get_part_partition_id;
+ }
+ else
+ {
+ ppar->mark_full_partition_used= mark_full_partition_used_no_parts;
+ ppar->get_top_partition_id_func= part_info->get_partition_id;
+ }
+
+ KEY_PART *key_part;
+ MEM_ROOT *alloc= range_par->mem_root;
+ if (!total_parts ||
+ !(key_part= (KEY_PART*)alloc_root(alloc, sizeof(KEY_PART)*
+ total_parts)) ||
+ !(ppar->arg_stack= (SEL_ARG**)alloc_root(alloc, sizeof(SEL_ARG*)*
+ total_parts)) ||
+ !(ppar->is_part_keypart= (my_bool*)alloc_root(alloc, sizeof(my_bool)*
+ total_parts)) ||
+ !(ppar->is_subpart_keypart= (my_bool*)alloc_root(alloc, sizeof(my_bool)*
+ total_parts)))
+ return TRUE;
+
+ if (ppar->subpart_fields)
+ {
+ my_bitmap_map *buf;
+ uint32 bufsize= bitmap_buffer_size(ppar->part_info->no_subparts);
+ if (!(buf= (my_bitmap_map*) alloc_root(alloc, bufsize)))
+ return TRUE;
+ bitmap_init(&ppar->subparts_bitmap, buf, ppar->part_info->no_subparts,
+ FALSE);
+ }
+ range_par->key_parts= key_part;
+ Field **field= (ppar->part_fields)? part_info->part_field_array :
+ part_info->subpart_field_array;
+ bool in_subpart_fields= FALSE;
+ for (uint part= 0; part < total_parts; part++, key_part++)
+ {
+ 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;
+
+ DBUG_PRINT("info", ("part %u length %u store_length %u", part,
+ key_part->length, key_part->store_length));
+
+ key_part->field= (*field);
+ key_part->image_type = Field::itRAW;
+ /*
+ We set keypart flag to 0 here as the only HA_PART_KEY_SEG is checked
+ in the RangeAnalysisModule.
+ */
+ key_part->flag= 0;
+ /* We don't set key_parts->null_bit as it will not be used */
+
+ ppar->is_part_keypart[part]= !in_subpart_fields;
+ ppar->is_subpart_keypart[part]= in_subpart_fields;
+
+ /*
+ Check if this was last field in this array, in this case we
+ switch to subpartitioning fields. (This will only happens if
+ there are subpartitioning fields to cater for).
+ */
+ if (!*(++field))
+ {
+ field= part_info->subpart_field_array;
+ in_subpart_fields= TRUE;
+ }
+ }
+ range_par->key_parts_end= key_part;
+
+ DBUG_EXECUTE("info", print_partitioning_index(range_par->key_parts,
+ range_par->key_parts_end););
+ return FALSE;
+}
+
+
+#ifndef DBUG_OFF
+
+static void print_partitioning_index(KEY_PART *parts, KEY_PART *parts_end)
+{
+ DBUG_ENTER("print_partitioning_index");
+ DBUG_LOCK_FILE;
+ fprintf(DBUG_FILE, "partitioning INDEX(");
+ for (KEY_PART *p=parts; p != parts_end; p++)
+ {
+ fprintf(DBUG_FILE, "%s%s", p==parts?"":" ,", p->field->field_name);
+ }
+ fputs(");\n", DBUG_FILE);
+ DBUG_UNLOCK_FILE;
+ DBUG_VOID_RETURN;
+}
+
+/* Print field value into debug trace, in NULL-aware way. */
+static void dbug_print_field(Field *field)
+{
+ if (field->is_real_null())
+ fprintf(DBUG_FILE, "NULL");
+ else
+ {
+ char buf[256];
+ String str(buf, sizeof(buf), &my_charset_bin);
+ str.length(0);
+ String *pstr;
+ pstr= field->val_str(&str);
+ fprintf(DBUG_FILE, "'%s'", pstr->c_ptr_safe());
+ }
+}
+
+
+/* Print a "c1 < keypartX < c2" - type interval into debug trace. */
+static void dbug_print_segment_range(SEL_ARG *arg, KEY_PART *part)
+{
+ DBUG_ENTER("dbug_print_segment_range");
+ DBUG_LOCK_FILE;
+ if (!(arg->min_flag & NO_MIN_RANGE))
+ {
+ store_key_image_to_rec(part->field, arg->min_value, part->length);
+ dbug_print_field(part->field);
+ if (arg->min_flag & NEAR_MIN)
+ fputs(" < ", DBUG_FILE);
+ else
+ fputs(" <= ", DBUG_FILE);
+ }
+
+ fprintf(DBUG_FILE, "%s", part->field->field_name);
+
+ if (!(arg->max_flag & NO_MAX_RANGE))
+ {
+ if (arg->max_flag & NEAR_MAX)
+ fputs(" < ", DBUG_FILE);
+ else
+ fputs(" <= ", DBUG_FILE);
+ store_key_image_to_rec(part->field, arg->max_value, part->length);
+ dbug_print_field(part->field);
+ }
+ fputs("\n", DBUG_FILE);
+ DBUG_UNLOCK_FILE;
+ DBUG_VOID_RETURN;
+}
+
+
+/*
+ Print a singlepoint multi-keypart range interval to debug trace
+
+ SYNOPSIS
+ dbug_print_singlepoint_range()
+ start Array of SEL_ARG* ptrs representing conditions on key parts
+ num Number of elements in the array.
+
+ DESCRIPTION
+ This function prints a "keypartN=constN AND ... AND keypartK=constK"-type
+ interval to debug trace.
+*/
+
+static void dbug_print_singlepoint_range(SEL_ARG **start, uint num)
+{
+ DBUG_ENTER("dbug_print_singlepoint_range");
+ DBUG_LOCK_FILE;
+ SEL_ARG **end= start + num;
+
+ for (SEL_ARG **arg= start; arg != end; arg++)
+ {
+ Field *field= (*arg)->field;
+ fprintf(DBUG_FILE, "%s%s=", (arg==start)?"":", ", field->field_name);
+ dbug_print_field(field);
+ }
+ fputs("\n", DBUG_FILE);
+ DBUG_UNLOCK_FILE;
+ DBUG_VOID_RETURN;
+}
+#endif
+
+/****************************************************************************
+ * Partition pruning code ends
+ ****************************************************************************/
+#endif
+
/*
Get cost of 'sweep' full records retrieval.
@@ -2293,7 +3586,8 @@ double get_sweep_read_cost(const PARAM *param, ha_rows records)
else
{
double n_blocks=
- ceil(ulonglong2double(param->table->file->data_file_length) / IO_SIZE);
+ ceil(ulonglong2double(param->table->file->stats.data_file_length) /
+ IO_SIZE);
double busy_blocks=
n_blocks * (1.0 - pow(1.0 - 1.0/n_blocks, rows2double(records)));
if (busy_blocks < 1.0)
@@ -2398,7 +3692,7 @@ TABLE_READ_PLAN *get_best_disjunct_quick(PARAM *param, SEL_IMERGE *imerge,
{
SEL_TREE **ptree;
TRP_INDEX_MERGE *imerge_trp= NULL;
- uint n_child_scans= (uint) (imerge->trees_next - imerge->trees);
+ uint n_child_scans= imerge->trees_next - imerge->trees;
TRP_RANGE **range_scans;
TRP_RANGE **cur_child;
TRP_RANGE **cpk_scan= NULL;
@@ -2433,7 +3727,7 @@ TABLE_READ_PLAN *get_best_disjunct_quick(PARAM *param, SEL_IMERGE *imerge,
{
DBUG_EXECUTE("info", print_sel_tree(param, *ptree, &(*ptree)->keys_map,
"tree in SEL_IMERGE"););
- if (!(*cur_child= get_key_scans_params(param, *ptree, TRUE, read_time)))
+ if (!(*cur_child= get_key_scans_params(param, *ptree, TRUE, FALSE, read_time)))
{
/*
One of index scans in this index_merge is more expensive than entire
@@ -2462,7 +3756,7 @@ TABLE_READ_PLAN *get_best_disjunct_quick(PARAM *param, SEL_IMERGE *imerge,
DBUG_PRINT("info", ("index_merge scans cost %g", imerge_cost));
if (imerge_too_expensive || (imerge_cost > read_time) ||
- (non_cpk_scan_records+cpk_scan_records >= param->table->file->records) &&
+ (non_cpk_scan_records+cpk_scan_records >= param->table->file->stats.records) &&
read_time != DBL_MAX)
{
/*
@@ -2520,7 +3814,7 @@ TABLE_READ_PLAN *get_best_disjunct_quick(PARAM *param, SEL_IMERGE *imerge,
imerge_trp->read_cost= imerge_cost;
imerge_trp->records= non_cpk_scan_records + cpk_scan_records;
imerge_trp->records= min(imerge_trp->records,
- param->table->file->records);
+ param->table->file->stats.records);
imerge_trp->range_scans= range_scans;
imerge_trp->range_scans_end= range_scans + n_child_scans;
read_time= imerge_cost;
@@ -2581,7 +3875,7 @@ skip_to_ror_scan:
((TRP_ROR_INTERSECT*)(*cur_roru_plan))->index_scan_costs;
roru_total_records += (*cur_roru_plan)->records;
roru_intersect_part *= (*cur_roru_plan)->records /
- param->table->file->records;
+ param->table->file->stats.records;
}
/*
@@ -2591,7 +3885,7 @@ skip_to_ror_scan:
in disjunction do not share key parts.
*/
roru_total_records -= (ha_rows)(roru_intersect_part*
- param->table->file->records);
+ param->table->file->stats.records);
/* ok, got a ROR read plan for each of the disjuncts
Calculate cost:
cost(index_union_scan(scan_1, ... scan_n)) =
@@ -2652,7 +3946,7 @@ static double get_index_only_read_time(const PARAM* param, ha_rows records,
int keynr)
{
double read_time;
- uint keys_per_block= (param->table->file->block_size/2/
+ uint keys_per_block= (param->table->file->stats.block_size/2/
(param->table->key_info[keynr].key_length+
param->table->file->ref_length) + 1);
read_time=((double) (records+keys_per_block-1)/
@@ -2704,7 +3998,7 @@ static
ROR_SCAN_INFO *make_ror_scan(const PARAM *param, int idx, SEL_ARG *sel_arg)
{
ROR_SCAN_INFO *ror_scan;
- uchar *bitmap_buf;
+ my_bitmap_map *bitmap_buf;
uint keynr;
DBUG_ENTER("make_ror_scan");
@@ -2719,12 +4013,12 @@ ROR_SCAN_INFO *make_ror_scan(const PARAM *param, int idx, SEL_ARG *sel_arg)
ror_scan->sel_arg= sel_arg;
ror_scan->records= param->table->quick_rows[keynr];
- if (!(bitmap_buf= (uchar*)alloc_root(param->mem_root,
- param->fields_bitmap_size)))
+ if (!(bitmap_buf= (my_bitmap_map*) alloc_root(param->mem_root,
+ param->fields_bitmap_size)))
DBUG_RETURN(NULL);
if (bitmap_init(&ror_scan->covered_fields, bitmap_buf,
- param->fields_bitmap_size*8, FALSE))
+ param->table->s->fields, FALSE))
DBUG_RETURN(NULL);
bitmap_clear_all(&ror_scan->covered_fields);
@@ -2733,8 +4027,8 @@ ROR_SCAN_INFO *make_ror_scan(const PARAM *param, int idx, SEL_ARG *sel_arg)
param->table->key_info[keynr].key_parts;
for (;key_part != key_part_end; ++key_part)
{
- if (bitmap_is_set(&param->needed_fields, key_part->fieldnr))
- bitmap_set_bit(&ror_scan->covered_fields, key_part->fieldnr);
+ if (bitmap_is_set(&param->needed_fields, key_part->fieldnr-1))
+ bitmap_set_bit(&ror_scan->covered_fields, key_part->fieldnr-1);
}
ror_scan->index_read_cost=
get_index_only_read_time(param, param->table->quick_rows[ror_scan->keynr],
@@ -2834,20 +4128,21 @@ static
ROR_INTERSECT_INFO* ror_intersect_init(const PARAM *param)
{
ROR_INTERSECT_INFO *info;
- uchar* buf;
+ my_bitmap_map* buf;
if (!(info= (ROR_INTERSECT_INFO*)alloc_root(param->mem_root,
sizeof(ROR_INTERSECT_INFO))))
return NULL;
info->param= param;
- if (!(buf= (uchar*)alloc_root(param->mem_root, param->fields_bitmap_size)))
+ if (!(buf= (my_bitmap_map*) alloc_root(param->mem_root,
+ param->fields_bitmap_size)))
return NULL;
- if (bitmap_init(&info->covered_fields, buf, param->fields_bitmap_size*8,
+ if (bitmap_init(&info->covered_fields, buf, param->table->s->fields,
FALSE))
return NULL;
info->is_covering= FALSE;
info->index_scan_costs= 0.0;
info->index_records= 0;
- info->out_rows= (double) param->table->file->records;
+ info->out_rows= (double) param->table->file->stats.records;
bitmap_clear_all(&info->covered_fields);
return info;
}
@@ -2856,7 +4151,7 @@ void ror_intersect_cpy(ROR_INTERSECT_INFO *dst, const ROR_INTERSECT_INFO *src)
{
dst->param= src->param;
memcpy(dst->covered_fields.bitmap, src->covered_fields.bitmap,
- src->covered_fields.bitmap_size);
+ no_bytes_in_map(&src->covered_fields));
dst->out_rows= src->out_rows;
dst->is_covering= src->is_covering;
dst->index_records= src->index_records;
@@ -2932,9 +4227,9 @@ void ror_intersect_cpy(ROR_INTERSECT_INFO *dst, const ROR_INTERSECT_INFO *src)
The calculation is conducted as follows:
Lets denote #records(keypart1, ... keypartK) as n_k. We need to calculate
- n_{k1} n_{k_2}
+ n_{k1} n_{k2}
--------- * --------- * .... (3)
- n_{k1-1} n_{k2_1}
+ n_{k1-1} n_{k2-1}
where k1,k2,... are key parts which fields were not yet marked as fixed
( this is result of application of option b) of the recursion step for
@@ -2942,9 +4237,9 @@ void ror_intersect_cpy(ROR_INTERSECT_INFO *dst, const ROR_INTERSECT_INFO *src)
Since it is reasonable to expect that most of the fields are not marked
as fixed, we calculate (3) as
- n_{i1} n_{i_2}
+ n_{i1} n_{i2}
(3) = n_{max_key_part} / ( --------- * --------- * .... )
- n_{i1-1} n_{i2_1}
+ n_{i1-1} n_{i2-1}
where i1,i2, .. are key parts that were already marked as fixed.
@@ -2953,7 +4248,6 @@ void ror_intersect_cpy(ROR_INTERSECT_INFO *dst, const ROR_INTERSECT_INFO *src)
RETURN
Selectivity of given ROR scan.
-
*/
static double ror_scan_selectivity(const ROR_INTERSECT_INFO *info,
@@ -2961,27 +4255,28 @@ static double ror_scan_selectivity(const ROR_INTERSECT_INFO *info,
{
double selectivity_mult= 1.0;
KEY_PART_INFO *key_part= info->param->table->key_info[scan->keynr].key_part;
- byte key_val[MAX_KEY_LENGTH+MAX_FIELD_WIDTH]; /* key values tuple */
- char *key_ptr= (char*) key_val;
+ uchar key_val[MAX_KEY_LENGTH+MAX_FIELD_WIDTH]; /* key values tuple */
+ uchar *key_ptr= key_val;
SEL_ARG *sel_arg, *tuple_arg= NULL;
+ key_part_map keypart_map= 0;
bool cur_covered;
bool prev_covered= test(bitmap_is_set(&info->covered_fields,
- key_part->fieldnr));
+ key_part->fieldnr-1));
key_range min_range;
key_range max_range;
- min_range.key= (byte*) key_val;
+ min_range.key= key_val;
min_range.flag= HA_READ_KEY_EXACT;
- max_range.key= (byte*) key_val;
+ max_range.key= key_val;
max_range.flag= HA_READ_AFTER_KEY;
- ha_rows prev_records= info->param->table->file->records;
- DBUG_ENTER("ror_intersect_selectivity");
+ ha_rows prev_records= info->param->table->file->stats.records;
+ DBUG_ENTER("ror_scan_selectivity");
for (sel_arg= scan->sel_arg; sel_arg;
sel_arg= sel_arg->next_key_part)
{
DBUG_PRINT("info",("sel_arg step"));
cur_covered= test(bitmap_is_set(&info->covered_fields,
- key_part[sel_arg->part].fieldnr));
+ key_part[sel_arg->part].fieldnr-1));
if (cur_covered != prev_covered)
{
/* create (part1val, ..., part{n-1}val) tuple. */
@@ -2991,13 +4286,17 @@ static double ror_scan_selectivity(const ROR_INTERSECT_INFO *info,
tuple_arg= scan->sel_arg;
/* Here we use the length of the first key part */
tuple_arg->store_min(key_part->store_length, &key_ptr, 0);
+ keypart_map= 1;
}
while (tuple_arg->next_key_part != sel_arg)
{
tuple_arg= tuple_arg->next_key_part;
- tuple_arg->store_min(key_part[tuple_arg->part].store_length, &key_ptr, 0);
+ tuple_arg->store_min(key_part[tuple_arg->part].store_length,
+ &key_ptr, 0);
+ keypart_map= (keypart_map << 1) | 1;
}
- min_range.length= max_range.length= (uint) ((char*) key_ptr - (char*) key_val);
+ min_range.length= max_range.length= (size_t) (key_ptr - key_val);
+ min_range.keypart_map= max_range.keypart_map= keypart_map;
records= (info->param->table->file->
records_in_range(scan->keynr, &min_range, &max_range));
if (cur_covered)
@@ -3084,7 +4383,6 @@ static bool ror_intersect_add(ROR_INTERSECT_INFO *info,
}
info->out_rows *= selectivity_mult;
- DBUG_PRINT("info", ("info->total_cost= %g", info->total_cost));
if (is_cpk_scan)
{
@@ -3110,15 +4408,15 @@ static bool ror_intersect_add(ROR_INTERSECT_INFO *info,
}
info->total_cost= info->index_scan_costs;
- DBUG_PRINT("info", ("info->total_cost= %g", info->total_cost));
+ DBUG_PRINT("info", ("info->total_cost: %g", info->total_cost));
if (!info->is_covering)
{
info->total_cost +=
get_sweep_read_cost(info->param, double2rows(info->out_rows));
DBUG_PRINT("info", ("info->total_cost= %g", info->total_cost));
}
- DBUG_PRINT("info", ("New out_rows= %g", info->out_rows));
- DBUG_PRINT("info", ("New cost= %g, %scovering", info->total_cost,
+ DBUG_PRINT("info", ("New out_rows: %g", info->out_rows));
+ DBUG_PRINT("info", ("New cost: %g, %scovering", info->total_cost,
info->is_covering?"" : "non-"));
DBUG_RETURN(TRUE);
}
@@ -3197,7 +4495,7 @@ TRP_ROR_INTERSECT *get_best_ror_intersect(const PARAM *param, SEL_TREE *tree,
double min_cost= DBL_MAX;
DBUG_ENTER("get_best_ror_intersect");
- if ((tree->n_ror_scans < 2) || !param->table->file->records)
+ if ((tree->n_ror_scans < 2) || !param->table->file->stats.records)
DBUG_RETURN(NULL);
/*
@@ -3297,7 +4595,7 @@ TRP_ROR_INTERSECT *get_best_ror_intersect(const PARAM *param, SEL_TREE *tree,
intersect_scans_best););
*are_all_covering= intersect->is_covering;
- uint best_num= (uint) (intersect_scans_best - intersect_scans);
+ uint best_num= intersect_scans_best - intersect_scans;
ror_intersect_cpy(intersect, intersect_best);
/*
@@ -3333,6 +4631,7 @@ TRP_ROR_INTERSECT *get_best_ror_intersect(const PARAM *param, SEL_TREE *tree,
ha_rows best_rows = double2rows(intersect_best->out_rows);
if (!best_rows)
best_rows= 1;
+ set_if_smaller(param->table->quick_condition_rows, best_rows);
trp->records= best_rows;
trp->index_scan_costs= intersect_best->index_scan_costs;
trp->cpk_scan= cpk_scan_used? cpk_scan: NULL;
@@ -3366,7 +4665,8 @@ TRP_ROR_INTERSECT *get_best_ror_intersect(const PARAM *param, SEL_TREE *tree,
F=set of all fields to cover
S={}
- do {
+ do
+ {
Order I by (#covered fields in F desc,
#components asc,
number of first not covered component asc);
@@ -3384,7 +4684,6 @@ TRP_ROR_INTERSECT *get_best_covering_ror_intersect(PARAM *param,
ROR_SCAN_INFO **ror_scan_mark;
ROR_SCAN_INFO **ror_scans_end= tree->ror_scans_end;
DBUG_ENTER("get_best_covering_ror_intersect");
- uint nbits= param->fields_bitmap_size*8;
for (ROR_SCAN_INFO **scan= tree->ror_scans; scan != ror_scans_end; ++scan)
(*scan)->key_components=
@@ -3400,10 +4699,11 @@ TRP_ROR_INTERSECT *get_best_covering_ror_intersect(PARAM *param,
MY_BITMAP *covered_fields= &param->tmp_covered_fields;
if (!covered_fields->bitmap)
- covered_fields->bitmap= (uchar*)alloc_root(param->mem_root,
+ covered_fields->bitmap= (my_bitmap_map*)alloc_root(param->mem_root,
param->fields_bitmap_size);
if (!covered_fields->bitmap ||
- bitmap_init(covered_fields, covered_fields->bitmap, nbits, FALSE))
+ bitmap_init(covered_fields, covered_fields->bitmap,
+ param->table->s->fields, FALSE))
DBUG_RETURN(0);
bitmap_clear_all(covered_fields);
@@ -3415,7 +4715,8 @@ TRP_ROR_INTERSECT *get_best_covering_ror_intersect(PARAM *param,
DBUG_EXECUTE("info", print_ror_scans_arr(param->table,
"building covering ROR-I",
ror_scan_mark, ror_scans_end););
- do {
+ do
+ {
/*
Update changed sorting info:
#covered fields,
@@ -3474,7 +4775,7 @@ TRP_ROR_INTERSECT *get_best_covering_ror_intersect(PARAM *param,
TRP_ROR_INTERSECT *trp;
if (!(trp= new (param->mem_root) TRP_ROR_INTERSECT))
DBUG_RETURN(trp);
- uint best_num= (uint) (ror_scan_mark - tree->ror_scans);
+ uint best_num= (ror_scan_mark - tree->ror_scans);
if (!(trp->first_scan= (ROR_SCAN_INFO**)alloc_root(param->mem_root,
sizeof(ROR_SCAN_INFO*)*
best_num)))
@@ -3485,6 +4786,7 @@ TRP_ROR_INTERSECT *get_best_covering_ror_intersect(PARAM *param,
trp->read_cost= total_cost;
trp->records= records;
trp->cpk_scan= NULL;
+ set_if_smaller(param->table->quick_condition_rows, records);
DBUG_PRINT("info",
("Returning covering ROR-intersect plan: cost %g, records %lu",
@@ -3509,7 +4811,8 @@ TRP_ROR_INTERSECT *get_best_covering_ror_intersect(PARAM *param,
*/
static TRP_RANGE *get_key_scans_params(PARAM *param, SEL_TREE *tree,
- bool index_read_must_be_used,
+ bool index_read_must_be_used,
+ bool update_tbl_stats,
double read_time)
{
int idx;
@@ -3542,9 +4845,9 @@ static TRP_RANGE *get_key_scans_params(PARAM *param, SEL_TREE *tree,
param->needed_reg->set_bit(keynr);
bool read_index_only= index_read_must_be_used ? TRUE :
- (bool) param->table->used_keys.is_set(keynr);
+ (bool) param->table->covering_keys.is_set(keynr);
- found_records= check_quick_select(param, idx, *key);
+ found_records= check_quick_select(param, idx, *key, update_tbl_stats);
if (param->is_ror_scan)
{
tree->n_ror_scans++;
@@ -3579,8 +4882,7 @@ static TRP_RANGE *get_key_scans_params(PARAM *param, SEL_TREE *tree,
param->table->key_info[keynr].name, found_read_time,
read_time));
- if (read_time > found_read_time && found_records != HA_POS_ERROR
- /*|| read_time == DBL_MAX*/ )
+ if (read_time > found_read_time && found_records != HA_POS_ERROR)
{
read_time= found_read_time;
best_records= found_records;
@@ -3594,7 +4896,7 @@ static TRP_RANGE *get_key_scans_params(PARAM *param, SEL_TREE *tree,
"ROR scans"););
if (key_to_read)
{
- idx= (uint) (key_to_read - tree->keys);
+ idx= key_to_read - tree->keys;
if ((read_plan= new (param->mem_root) TRP_RANGE(*key_to_read, idx)))
{
read_plan->records= best_records;
@@ -3651,7 +4953,8 @@ QUICK_SELECT_I *TRP_ROR_INTERSECT::make_quick(PARAM *param,
if ((quick_intrsect=
new QUICK_ROR_INTERSECT_SELECT(param->thd, param->table,
- retrieve_full_rows? (!is_covering):FALSE,
+ (retrieve_full_rows? (!is_covering) :
+ FALSE),
parent_alloc)))
{
DBUG_EXECUTE("info", print_ror_scans_arr(param->table,
@@ -3730,7 +5033,7 @@ QUICK_SELECT_I *TRP_ROR_UNION::make_quick(PARAM *param,
0 on error
*/
-static SEL_TREE *get_ne_mm_tree(PARAM *param, Item_func *cond_func,
+static SEL_TREE *get_ne_mm_tree(RANGE_OPT_PARAM *param, Item_func *cond_func,
Field *field,
Item *lt_value, Item *gt_value,
Item_result cmp_type)
@@ -3765,7 +5068,7 @@ static SEL_TREE *get_ne_mm_tree(PARAM *param, Item_func *cond_func,
Pointer to the tree built tree
*/
-static SEL_TREE *get_func_mm_tree(PARAM *param, Item_func *cond_func,
+static SEL_TREE *get_func_mm_tree(RANGE_OPT_PARAM *param, Item_func *cond_func,
Field *field, Item *value,
Item_result cmp_type, bool inv)
{
@@ -3814,9 +5117,17 @@ static SEL_TREE *get_func_mm_tree(PARAM *param, Item_func *cond_func,
{
Item_func_in *func=(Item_func_in*) cond_func;
+ /*
+ Array for IN() is constructed when all values have the same result
+ type. Tree won't be built for values with different result types,
+ so we check it here to avoid unnecessary work.
+ */
+ if (!func->arg_types_compatible)
+ break;
+
if (inv)
{
- if (func->array && func->cmp_type != ROW_RESULT)
+ if (func->array && func->array->result_type() != ROW_RESULT)
{
/*
We get here for conditions in form "t.key NOT IN (c1, c2, ...)",
@@ -4051,7 +5362,8 @@ static SEL_TREE *get_func_mm_tree(PARAM *param, Item_func *cond_func,
Pointer to the tree representing the built conjunction of SEL_TREEs
*/
-static SEL_TREE *get_full_func_mm_tree(PARAM *param, Item_func *cond_func,
+static SEL_TREE *get_full_func_mm_tree(RANGE_OPT_PARAM *param,
+ Item_func *cond_func,
Item_field *field_item, Item *value,
bool inv)
{
@@ -4094,7 +5406,7 @@ static SEL_TREE *get_full_func_mm_tree(PARAM *param, Item_func *cond_func,
/* make a select tree of all keys in condition */
-static SEL_TREE *get_mm_tree(PARAM *param,COND *cond)
+static SEL_TREE *get_mm_tree(RANGE_OPT_PARAM *param,COND *cond)
{
SEL_TREE *tree=0;
SEL_TREE *ftree= 0;
@@ -4193,12 +5505,11 @@ static SEL_TREE *get_mm_tree(PARAM *param,COND *cond)
*/
for (uint i= 1 ; i < cond_func->arg_count ; i++)
{
-
if (cond_func->arguments()[i]->real_item()->type() == Item::FIELD_ITEM)
{
field_item= (Item_field*) (cond_func->arguments()[i]->real_item());
SEL_TREE *tmp= get_full_func_mm_tree(param, cond_func,
- field_item, (Item*) i, inv);
+ field_item, (Item*)(intptr)i, inv);
if (inv)
tree= !tree ? tmp : tree_or(param, tree, tmp);
else
@@ -4266,7 +5577,7 @@ static SEL_TREE *get_mm_tree(PARAM *param,COND *cond)
static SEL_TREE *
-get_mm_parts(PARAM *param, COND *cond_func, Field *field,
+get_mm_parts(RANGE_OPT_PARAM *param, COND *cond_func, Field *field,
Item_func::Functype type,
Item *value, Item_result cmp_type)
{
@@ -4318,14 +5629,14 @@ get_mm_parts(PARAM *param, COND *cond_func, Field *field,
static SEL_ARG *
-get_mm_leaf(PARAM *param, COND *conf_func, Field *field, KEY_PART *key_part,
- Item_func::Functype type,Item *value)
+get_mm_leaf(RANGE_OPT_PARAM *param, COND *conf_func, Field *field,
+ KEY_PART *key_part, Item_func::Functype type,Item *value)
{
uint maybe_null=(uint) field->real_maybe_null();
bool optimize_range;
SEL_ARG *tree= 0;
MEM_ROOT *alloc= param->mem_root;
- char *str;
+ uchar *str;
ulong orig_sql_mode;
int err;
DBUG_ENTER("get_mm_leaf");
@@ -4378,15 +5689,19 @@ get_mm_leaf(PARAM *param, COND *conf_func, Field *field, KEY_PART *key_part,
!(conf_func->compare_collation()->state & MY_CS_BINSORT))
goto end;
- optimize_range= field->optimize_range(param->real_keynr[key_part->key],
- key_part->part);
+ if (param->using_real_indexes)
+ optimize_range= field->optimize_range(param->real_keynr[key_part->key],
+ key_part->part);
+ else
+ optimize_range= TRUE;
if (type == Item_func::LIKE_FUNC)
{
bool like_error;
- char buff1[MAX_FIELD_WIDTH],*min_str,*max_str;
+ char buff1[MAX_FIELD_WIDTH];
+ uchar *min_str,*max_str;
String tmp(buff1,sizeof(buff1),value->collation.collation),*res;
- uint length,offset,min_length,max_length;
+ size_t length, offset, min_length, max_length;
uint field_length= field->pack_length()+maybe_null;
if (!optimize_range)
@@ -4433,7 +5748,7 @@ get_mm_leaf(PARAM *param, COND *conf_func, Field *field, KEY_PART *key_part,
field_length= length;
}
length+=offset;
- if (!(min_str= (char*) alloc_root(alloc, length*2)))
+ if (!(min_str= (uchar*) alloc_root(alloc, length*2)))
goto end;
max_str=min_str+length;
@@ -4446,7 +5761,7 @@ get_mm_leaf(PARAM *param, COND *conf_func, Field *field, KEY_PART *key_part,
((Item_func_like*)(param->cond))->escape,
wild_one, wild_many,
field_length,
- min_str+offset, max_str+offset,
+ (char*) min_str+offset, (char*) max_str+offset,
&min_length, &max_length);
if (like_error) // Can't optimize with LIKE
goto end;
@@ -4476,8 +5791,8 @@ get_mm_leaf(PARAM *param, COND *conf_func, Field *field, KEY_PART *key_part,
/* For comparison purposes allow invalid dates like 2000-01-32 */
orig_sql_mode= field->table->in_use->variables.sql_mode;
if (value->real_item()->type() == Item::STRING_ITEM &&
- (field->type() == FIELD_TYPE_DATE ||
- field->type() == FIELD_TYPE_DATETIME))
+ (field->type() == MYSQL_TYPE_DATE ||
+ field->type() == MYSQL_TYPE_DATETIME))
field->table->in_use->variables.sql_mode|= MODE_INVALID_DATES;
err= value->save_in_field_no_warnings(field, 1);
if (err > 0)
@@ -4551,12 +5866,13 @@ get_mm_leaf(PARAM *param, COND *conf_func, Field *field, KEY_PART *key_part,
goto end;
}
field->table->in_use->variables.sql_mode= orig_sql_mode;
- str= (char*) alloc_root(alloc, key_part->store_length+1);
+ str= (uchar*) alloc_root(alloc, key_part->store_length+1);
if (!str)
goto end;
if (maybe_null)
- *str= (char) field->is_real_null(); // Set to 1 if null
- field->get_key_image(str+maybe_null, key_part->length, key_part->image_type);
+ *str= (uchar) field->is_real_null(); // Set to 1 if null
+ field->get_key_image(str+maybe_null, key_part->length,
+ key_part->image_type);
if (!(tree= new (alloc) SEL_ARG(field, str, str)))
goto end; // out of memory
@@ -4712,7 +6028,7 @@ sel_add(SEL_ARG *key1,SEL_ARG *key2)
static SEL_TREE *
-tree_and(PARAM *param,SEL_TREE *tree1,SEL_TREE *tree2)
+tree_and(RANGE_OPT_PARAM *param,SEL_TREE *tree1,SEL_TREE *tree2)
{
DBUG_ENTER("tree_and");
if (!tree1)
@@ -4755,7 +6071,7 @@ tree_and(PARAM *param,SEL_TREE *tree1,SEL_TREE *tree2)
tree1->type= SEL_TREE::IMPOSSIBLE;
DBUG_RETURN(tree1);
}
- result_keys.set_bit((uint) (key1 - tree1->keys));
+ result_keys.set_bit(key1 - tree1->keys);
#ifdef EXTRA_DEBUG
if (*key1 && param->alloced_sel_args < SEL_ARG::MAX_SEL_ARGS)
(*key1)->test_use_count(*key1);
@@ -4782,7 +6098,8 @@ tree_and(PARAM *param,SEL_TREE *tree1,SEL_TREE *tree2)
using index_merge.
*/
-bool sel_trees_can_be_ored(SEL_TREE *tree1, SEL_TREE *tree2, PARAM* param)
+bool sel_trees_can_be_ored(SEL_TREE *tree1, SEL_TREE *tree2,
+ RANGE_OPT_PARAM* param)
{
key_map common_keys= tree1->keys_map;
DBUG_ENTER("sel_trees_can_be_ored");
@@ -4808,8 +6125,84 @@ bool sel_trees_can_be_ored(SEL_TREE *tree1, SEL_TREE *tree2, PARAM* param)
DBUG_RETURN(FALSE);
}
+
+/*
+ Remove the trees that are not suitable for record retrieval.
+ SYNOPSIS
+ param Range analysis parameter
+ tree Tree to be processed, tree->type is KEY or KEY_SMALLER
+
+ DESCRIPTION
+ This function walks through tree->keys[] and removes the SEL_ARG* trees
+ that are not "maybe" trees (*) and cannot be used to construct quick range
+ selects.
+ (*) - have type MAYBE or MAYBE_KEY. Perhaps we should remove trees of
+ these types here as well.
+
+ A SEL_ARG* tree cannot be used to construct quick select if it has
+ tree->part != 0. (e.g. it could represent "keypart2 < const").
+
+ WHY THIS FUNCTION IS NEEDED
+
+ Normally we allow construction of SEL_TREE objects that have SEL_ARG
+ trees that do not allow quick range select construction. For example for
+ " keypart1=1 AND keypart2=2 " the execution will proceed as follows:
+ tree1= SEL_TREE { SEL_ARG{keypart1=1} }
+ tree2= SEL_TREE { SEL_ARG{keypart2=2} } -- can't make quick range select
+ from this
+ call tree_and(tree1, tree2) -- this joins SEL_ARGs into a usable SEL_ARG
+ tree.
+
+ There is an exception though: when we construct index_merge SEL_TREE,
+ any SEL_ARG* tree that cannot be used to construct quick range select can
+ be removed, because current range analysis code doesn't provide any way
+ that tree could be later combined with another tree.
+ Consider an example: we should not construct
+ st1 = SEL_TREE {
+ merges = SEL_IMERGE {
+ SEL_TREE(t.key1part1 = 1),
+ SEL_TREE(t.key2part2 = 2) -- (*)
+ }
+ };
+ because
+ - (*) cannot be used to construct quick range select,
+ - There is no execution path that would cause (*) to be converted to
+ a tree that could be used.
+
+ The latter is easy to verify: first, notice that the only way to convert
+ (*) into a usable tree is to call tree_and(something, (*)).
+
+ Second look at what tree_and/tree_or function would do when passed a
+ SEL_TREE that has the structure like st1 tree has, and conlcude that
+ tree_and(something, (*)) will not be called.
+
+ RETURN
+ 0 Ok, some suitable trees left
+ 1 No tree->keys[] left.
+*/
+
+static bool remove_nonrange_trees(RANGE_OPT_PARAM *param, SEL_TREE *tree)
+{
+ bool res= FALSE;
+ for (uint i=0; i < param->keys; i++)
+ {
+ if (tree->keys[i])
+ {
+ if (tree->keys[i]->part)
+ {
+ tree->keys[i]= NULL;
+ tree->keys_map.clear_bit(i);
+ }
+ else
+ res= TRUE;
+ }
+ }
+ return !res;
+}
+
+
static SEL_TREE *
-tree_or(PARAM *param,SEL_TREE *tree1,SEL_TREE *tree2)
+tree_or(RANGE_OPT_PARAM *param,SEL_TREE *tree1,SEL_TREE *tree2)
{
DBUG_ENTER("tree_or");
if (!tree1 || !tree2)
@@ -4837,7 +6230,7 @@ tree_or(PARAM *param,SEL_TREE *tree1,SEL_TREE *tree2)
if (*key1)
{
result=tree1; // Added to tree1
- result_keys.set_bit((uint) (key1 - tree1->keys));
+ result_keys.set_bit(key1 - tree1->keys);
#ifdef EXTRA_DEBUG
if (param->alloced_sel_args < SEL_ARG::MAX_SEL_ARGS)
(*key1)->test_use_count(*key1);
@@ -4852,6 +6245,13 @@ tree_or(PARAM *param,SEL_TREE *tree1,SEL_TREE *tree2)
/* ok, two trees have KEY type but cannot be used without index merge */
if (tree1->merges.is_empty() && tree2->merges.is_empty())
{
+ if (param->remove_jump_scans)
+ {
+ bool no_trees= remove_nonrange_trees(param, tree1);
+ no_trees= no_trees || remove_nonrange_trees(param, tree2);
+ if (no_trees)
+ DBUG_RETURN(new SEL_TREE(SEL_TREE::ALWAYS));
+ }
SEL_IMERGE *merge;
/* both trees are "range" trees, produce new index merge structure */
if (!(result= new SEL_TREE()) || !(merge= new SEL_IMERGE()) ||
@@ -4874,7 +6274,9 @@ tree_or(PARAM *param,SEL_TREE *tree1,SEL_TREE *tree2)
/* one tree is index merge tree and another is range tree */
if (tree1->merges.is_empty())
swap_variables(SEL_TREE*, tree1, tree2);
-
+
+ if (param->remove_jump_scans && remove_nonrange_trees(param, tree2))
+ DBUG_RETURN(new SEL_TREE(SEL_TREE::ALWAYS));
/* add tree2 to tree1->merges, checking if it collapses to ALWAYS */
if (imerge_list_or_tree(param, &tree1->merges, tree2))
result= new SEL_TREE(SEL_TREE::ALWAYS);
@@ -4889,7 +6291,8 @@ tree_or(PARAM *param,SEL_TREE *tree1,SEL_TREE *tree2)
/* And key trees where key1->part < key2 -> part */
static SEL_ARG *
-and_all_keys(PARAM *param, SEL_ARG *key1, SEL_ARG *key2, uint clone_flag)
+and_all_keys(RANGE_OPT_PARAM *param, SEL_ARG *key1, SEL_ARG *key2,
+ uint clone_flag)
{
SEL_ARG *next;
ulong use_count=key1->use_count;
@@ -4935,8 +6338,10 @@ and_all_keys(PARAM *param, SEL_ARG *key1, SEL_ARG *key2, uint clone_flag)
SYNOPSIS
key_and()
- key1 First argument, root of its RB-tree
- key2 Second argument, root of its RB-tree
+ param Range analysis context (needed to track if we have allocated
+ too many SEL_ARGs)
+ key1 First argument, root of its RB-tree
+ key2 Second argument, root of its RB-tree
RETURN
RB-tree root of the resulting SEL_ARG graph.
@@ -4944,7 +6349,7 @@ and_all_keys(PARAM *param, SEL_ARG *key1, SEL_ARG *key2, uint clone_flag)
*/
static SEL_ARG *
-key_and(PARAM *param, SEL_ARG *key1, SEL_ARG *key2, uint clone_flag)
+key_and(RANGE_OPT_PARAM *param, SEL_ARG *key1, SEL_ARG *key2, uint clone_flag)
{
if (!key1)
return key2;
@@ -5083,7 +6488,7 @@ get_range(SEL_ARG **e1,SEL_ARG **e2,SEL_ARG *root1)
static SEL_ARG *
-key_or(PARAM *param, SEL_ARG *key1,SEL_ARG *key2)
+key_or(RANGE_OPT_PARAM *param, SEL_ARG *key1,SEL_ARG *key2)
{
if (!key1)
{
@@ -5371,7 +6776,8 @@ SEL_ARG *
SEL_ARG::insert(SEL_ARG *key)
{
SEL_ARG *element,**par,*last_element;
- LINT_INIT(par); LINT_INIT(last_element);
+ LINT_INIT(par);
+ LINT_INIT(last_element);
for (element= this; element != &null_element ; )
{
@@ -5846,9 +7252,12 @@ void SEL_ARG::test_use_count(SEL_ARG *root)
SYNOPSIS
check_quick_select
param Parameter from test_quick_select
- idx Number of index to use in PARAM::key SEL_TREE::key
- tree Transformed selection condition, tree->key[idx] holds intervals
- tree to be used for scanning.
+ idx Number of index to use in tree->keys
+ tree Transformed selection condition, tree->keys[idx]
+ holds the range tree to be used for scanning.
+ update_tbl_stats If true, update table->quick_keys with information
+ about range scan we've evaluated.
+
NOTES
param->is_ror_scan is set to reflect if the key scan is a ROR (see
is_key_scan_ror function for more info)
@@ -5862,7 +7271,7 @@ void SEL_ARG::test_use_count(SEL_ARG *root)
*/
static ha_rows
-check_quick_select(PARAM *param,uint idx,SEL_ARG *tree)
+check_quick_select(PARAM *param,uint idx,SEL_ARG *tree, bool update_tbl_stats)
{
ha_rows records;
bool cpk_scan;
@@ -5901,13 +7310,24 @@ check_quick_select(PARAM *param,uint idx,SEL_ARG *tree)
}
param->n_ranges= 0;
- records=check_quick_keys(param,idx,tree,param->min_key,0,param->max_key,0);
+ records= check_quick_keys(param, idx, tree,
+ param->min_key, 0, -1,
+ param->max_key, 0, -1);
if (records != HA_POS_ERROR)
{
- param->table->quick_keys.set_bit(key);
+ if (update_tbl_stats)
+ {
+ param->table->quick_keys.set_bit(key);
+ param->table->quick_key_parts[key]=param->max_key_part+1;
+ param->table->quick_n_ranges[key]= param->n_ranges;
+ param->table->quick_condition_rows=
+ min(param->table->quick_condition_rows, records);
+ }
+ /*
+ Need to save quick_rows in any case as it is used when calculating
+ cost of ROR intersection:
+ */
param->table->quick_rows[key]=records;
- param->table->quick_key_parts[key]=param->max_key_part+1;
- param->table->quick_n_ranges[key]= param->n_ranges;
if (cpk_scan)
param->is_ror_scan= TRUE;
}
@@ -5960,13 +7380,14 @@ check_quick_select(PARAM *param,uint idx,SEL_ARG *tree)
*/
static ha_rows
-check_quick_keys(PARAM *param,uint idx,SEL_ARG *key_tree,
- char *min_key,uint min_key_flag, char *max_key,
- uint max_key_flag)
+check_quick_keys(PARAM *param, uint idx, SEL_ARG *key_tree,
+ uchar *min_key, uint min_key_flag, int min_keypart,
+ uchar *max_key, uint max_key_flag, int max_keypart)
{
ha_rows records=0, tmp;
uint tmp_min_flag, tmp_max_flag, keynr, min_key_length, max_key_length;
- char *tmp_min_key, *tmp_max_key;
+ uint tmp_min_keypart= min_keypart, tmp_max_keypart= max_keypart;
+ uchar *tmp_min_key, *tmp_max_key;
uint8 save_first_null_comp= param->first_null_comp;
param->max_key_part=max(param->max_key_part,key_tree->part);
@@ -5979,18 +7400,21 @@ check_quick_keys(PARAM *param,uint idx,SEL_ARG *key_tree,
This is not a ROR scan if the key is not Clustered Primary Key.
*/
param->is_ror_scan= FALSE;
- records=check_quick_keys(param,idx,key_tree->left,min_key,min_key_flag,
- max_key,max_key_flag);
+ records=check_quick_keys(param, idx, key_tree->left,
+ min_key, min_key_flag, min_keypart,
+ max_key, max_key_flag, max_keypart);
if (records == HA_POS_ERROR) // Impossible
return records;
}
tmp_min_key= min_key;
tmp_max_key= max_key;
- key_tree->store(param->key[idx][key_tree->part].store_length,
- &tmp_min_key,min_key_flag,&tmp_max_key,max_key_flag);
- min_key_length= (uint) (tmp_min_key- param->min_key);
- max_key_length= (uint) (tmp_max_key- param->max_key);
+ tmp_min_keypart+= key_tree->store_min(param->key[idx][key_tree->part].store_length,
+ &tmp_min_key, min_key_flag);
+ tmp_max_keypart+= key_tree->store_max(param->key[idx][key_tree->part].store_length,
+ &tmp_max_key, max_key_flag);
+ min_key_length= (uint) (tmp_min_key - param->min_key);
+ max_key_length= (uint) (tmp_max_key - param->max_key);
if (param->is_ror_scan)
{
@@ -6013,12 +7437,13 @@ check_quick_keys(PARAM *param,uint idx,SEL_ARG *key_tree,
key_tree->next_key_part->type == SEL_ARG::KEY_RANGE)
{ // const key as prefix
if (min_key_length == max_key_length &&
- !memcmp(min_key,max_key, (uint) (tmp_max_key - max_key)) &&
+ !memcmp(min_key, max_key, (uint) (tmp_max_key - max_key)) &&
!key_tree->min_flag && !key_tree->max_flag)
{
- tmp=check_quick_keys(param,idx,key_tree->next_key_part,
- tmp_min_key, min_key_flag | key_tree->min_flag,
- tmp_max_key, max_key_flag | key_tree->max_flag);
+ tmp=check_quick_keys(param,idx,key_tree->next_key_part, tmp_min_key,
+ min_key_flag | key_tree->min_flag, tmp_min_keypart,
+ tmp_max_key, max_key_flag | key_tree->max_flag,
+ tmp_max_keypart);
goto end; // Ugly, but efficient
}
else
@@ -6030,28 +7455,32 @@ check_quick_keys(PARAM *param,uint idx,SEL_ARG *key_tree,
tmp_min_flag=key_tree->min_flag;
tmp_max_flag=key_tree->max_flag;
if (!tmp_min_flag)
+ tmp_min_keypart+=
key_tree->next_key_part->store_min_key(param->key[idx], &tmp_min_key,
&tmp_min_flag);
if (!tmp_max_flag)
+ tmp_max_keypart+=
key_tree->next_key_part->store_max_key(param->key[idx], &tmp_max_key,
&tmp_max_flag);
- min_key_length= (uint) (tmp_min_key- param->min_key);
- max_key_length= (uint) (tmp_max_key- param->max_key);
+ min_key_length= (uint) (tmp_min_key - param->min_key);
+ max_key_length= (uint) (tmp_max_key - param->max_key);
}
else
{
- tmp_min_flag=min_key_flag | key_tree->min_flag;
- tmp_max_flag=max_key_flag | key_tree->max_flag;
+ tmp_min_flag= min_key_flag | key_tree->min_flag;
+ tmp_max_flag= max_key_flag | key_tree->max_flag;
}
+ if (unlikely(param->thd->killed != 0))
+ return HA_POS_ERROR;
+
keynr=param->real_keynr[idx];
param->range_count++;
if (!tmp_min_flag && ! tmp_max_flag &&
(uint) key_tree->part+1 == param->table->key_info[keynr].key_parts &&
(param->table->key_info[keynr].flags & (HA_NOSAME | HA_END_SPACE_KEY)) ==
- HA_NOSAME &&
- min_key_length == max_key_length &&
- !memcmp(param->min_key,param->max_key,min_key_length) &&
+ HA_NOSAME && min_key_length == max_key_length &&
+ !memcmp(param->min_key, param->max_key, min_key_length) &&
!param->first_null_comp)
{
tmp=1; // Max one record
@@ -6071,7 +7500,7 @@ check_quick_keys(PARAM *param,uint idx,SEL_ARG *key_tree,
first members of clustered primary key.
*/
if (!(min_key_length == max_key_length &&
- !memcmp(min_key,max_key, (uint) (tmp_max_key - max_key)) &&
+ !memcmp(min_key, max_key, (uint) (tmp_max_key - max_key)) &&
!key_tree->min_flag && !key_tree->max_flag &&
is_key_scan_ror(param, keynr, key_tree->part + 1)))
param->is_ror_scan= FALSE;
@@ -6081,26 +7510,29 @@ check_quick_keys(PARAM *param,uint idx,SEL_ARG *key_tree,
if (tmp_min_flag & GEOM_FLAG)
{
key_range min_range;
- min_range.key= (byte*) param->min_key;
+ min_range.key= param->min_key;
min_range.length= min_key_length;
+ min_range.keypart_map= make_keypart_map(tmp_min_keypart);
/* In this case tmp_min_flag contains the handler-read-function */
min_range.flag= (ha_rkey_function) (tmp_min_flag ^ GEOM_FLAG);
- tmp= param->table->file->records_in_range(keynr, &min_range,
- (key_range*) 0);
+ tmp= param->table->file->records_in_range(keynr,
+ &min_range, (key_range*) 0);
}
else
{
key_range min_range, max_range;
- min_range.key= (byte*) param->min_key;
+ min_range.key= param->min_key;
min_range.length= min_key_length;
min_range.flag= (tmp_min_flag & NEAR_MIN ? HA_READ_AFTER_KEY :
HA_READ_KEY_EXACT);
- max_range.key= (byte*) param->max_key;
+ min_range.keypart_map= make_keypart_map(tmp_min_keypart);
+ max_range.key= param->max_key;
max_range.length= max_key_length;
max_range.flag= (tmp_max_flag & NEAR_MAX ?
HA_READ_BEFORE_KEY : HA_READ_AFTER_KEY);
+ max_range.keypart_map= make_keypart_map(tmp_max_keypart);
tmp=param->table->file->records_in_range(keynr,
(min_key_length ? &min_range :
(key_range*) 0),
@@ -6121,8 +7553,9 @@ check_quick_keys(PARAM *param,uint idx,SEL_ARG *key_tree,
This is not a ROR scan if the key is not Clustered Primary Key.
*/
param->is_ror_scan= FALSE;
- tmp=check_quick_keys(param,idx,key_tree->right,min_key,min_key_flag,
- max_key,max_key_flag);
+ tmp=check_quick_keys(param, idx, key_tree->right,
+ min_key, min_key_flag, min_keypart,
+ max_key, max_key_flag, max_keypart);
if (tmp == HA_POS_ERROR)
return tmp;
records+=tmp;
@@ -6261,11 +7694,13 @@ get_quick_select(PARAM *param,uint idx,SEL_ARG *key_tree,
*/
bool
get_quick_keys(PARAM *param,QUICK_RANGE_SELECT *quick,KEY_PART *key,
- SEL_ARG *key_tree,char *min_key,uint min_key_flag,
- char *max_key, uint max_key_flag)
+ SEL_ARG *key_tree, uchar *min_key,uint min_key_flag,
+ uchar *max_key, uint max_key_flag)
{
QUICK_RANGE *range;
uint flag;
+ int min_part= key_tree->part-1, // # of keypart values in min_key buffer
+ max_part= key_tree->part-1; // # of keypart values in max_key buffer
if (key_tree->left != &null_element)
{
@@ -6273,17 +7708,19 @@ get_quick_keys(PARAM *param,QUICK_RANGE_SELECT *quick,KEY_PART *key,
min_key,min_key_flag, max_key, max_key_flag))
return 1;
}
- char *tmp_min_key=min_key,*tmp_max_key=max_key;
- key_tree->store(key[key_tree->part].store_length,
- &tmp_min_key,min_key_flag,&tmp_max_key,max_key_flag);
+ uchar *tmp_min_key=min_key,*tmp_max_key=max_key;
+ min_part+= key_tree->store_min(key[key_tree->part].store_length,
+ &tmp_min_key,min_key_flag);
+ max_part+= key_tree->store_max(key[key_tree->part].store_length,
+ &tmp_max_key,max_key_flag);
if (key_tree->next_key_part &&
key_tree->next_key_part->part == key_tree->part+1 &&
key_tree->next_key_part->type == SEL_ARG::KEY_RANGE)
{ // const key as prefix
- if (!((tmp_min_key - min_key) != (tmp_max_key - max_key) ||
- memcmp(min_key,max_key, (uint) (tmp_max_key - max_key)) ||
- key_tree->min_flag || key_tree->max_flag))
+ 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==0 && key_tree->max_flag==0)
{
if (get_quick_keys(param,quick,key,key_tree->next_key_part,
tmp_min_key, min_key_flag | key_tree->min_flag,
@@ -6294,11 +7731,11 @@ 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)
- 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);
if (!tmp_max_flag)
- 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);
flag=tmp_min_flag | tmp_max_flag;
}
}
@@ -6346,17 +7783,19 @@ get_quick_keys(PARAM *param,QUICK_RANGE_SELECT *quick,KEY_PART *key,
}
/* Get range for retrieving rows in QUICK_SELECT::get_next */
- if (!(range= new QUICK_RANGE((const char *) param->min_key,
+ if (!(range= new QUICK_RANGE(param->min_key,
(uint) (tmp_min_key - param->min_key),
- (const char *) param->max_key,
+ min_part >=0 ? make_keypart_map(min_part) : 0,
+ param->max_key,
(uint) (tmp_max_key - param->max_key),
+ max_part >=0 ? make_keypart_map(max_part) : 0,
flag)))
return 1; // out of memory
- set_if_bigger(quick->max_used_key_length,range->min_length);
- set_if_bigger(quick->max_used_key_length,range->max_length);
+ set_if_bigger(quick->max_used_key_length, range->min_length);
+ set_if_bigger(quick->max_used_key_length, range->max_length);
set_if_bigger(quick->used_key_parts, (uint) key_tree->part+1);
- if (insert_dynamic(&quick->ranges, (gptr)&range))
+ if (insert_dynamic(&quick->ranges, (uchar*) &range))
return 1;
end:
@@ -6389,9 +7828,9 @@ bool QUICK_RANGE_SELECT::unique_key_range()
/* Returns TRUE if any part of the key is NULL */
-static bool null_part_in_key(KEY_PART *key_part, const char *key, uint length)
+static bool null_part_in_key(KEY_PART *key_part, const uchar *key, uint length)
{
- for (const char *end=key+length ;
+ for (const uchar *end=key+length ;
key < end;
key+= key_part++->store_length)
{
@@ -6402,36 +7841,36 @@ static bool null_part_in_key(KEY_PART *key_part, const char *key, uint length)
}
-bool QUICK_SELECT_I::is_keys_used(List<Item> *fields)
+bool QUICK_SELECT_I::is_keys_used(const MY_BITMAP *fields)
{
- return is_key_used(head, index, *fields);
+ return is_key_used(head, index, fields);
}
-bool QUICK_INDEX_MERGE_SELECT::is_keys_used(List<Item> *fields)
+bool QUICK_INDEX_MERGE_SELECT::is_keys_used(const MY_BITMAP *fields)
{
QUICK_RANGE_SELECT *quick;
List_iterator_fast<QUICK_RANGE_SELECT> it(quick_selects);
while ((quick= it++))
{
- if (is_key_used(head, quick->index, *fields))
+ if (is_key_used(head, quick->index, fields))
return 1;
}
return 0;
}
-bool QUICK_ROR_INTERSECT_SELECT::is_keys_used(List<Item> *fields)
+bool QUICK_ROR_INTERSECT_SELECT::is_keys_used(const MY_BITMAP *fields)
{
QUICK_RANGE_SELECT *quick;
List_iterator_fast<QUICK_RANGE_SELECT> it(quick_selects);
while ((quick= it++))
{
- if (is_key_used(head, quick->index, *fields))
+ if (is_key_used(head, quick->index, fields))
return 1;
}
return 0;
}
-bool QUICK_ROR_UNION_SELECT::is_keys_used(List<Item> *fields)
+bool QUICK_ROR_UNION_SELECT::is_keys_used(const MY_BITMAP *fields)
{
QUICK_SELECT_I *quick;
List_iterator_fast<QUICK_SELECT_I> it(quick_selects);
@@ -6452,13 +7891,13 @@ bool QUICK_ROR_UNION_SELECT::is_keys_used(List<Item> *fields)
thd Thread handle
table Table to access
ref ref[_or_null] scan parameters
- records Estimate of number of records (needed only to construct
+ records Estimate of number of records (needed only to construct
quick select)
NOTES
This allocates things in a new memory root, as this may be called many
times during a query.
-
- RETURN
+
+ RETURN
Quick select that retrieves the same rows as passed ref scan
NULL on error.
*/
@@ -6490,12 +7929,14 @@ QUICK_RANGE_SELECT *get_quick_select_for_ref(THD *thd, TABLE *table,
goto err;
quick->records= records;
- if (cp_buffer_from_ref(thd,ref) && thd->is_fatal_error ||
+ if (cp_buffer_from_ref(thd, table, ref) && thd->is_fatal_error ||
!(range= new(alloc) QUICK_RANGE()))
goto err; // out of memory
- range->min_key=range->max_key=(char*) ref->key_buff;
- range->min_length=range->max_length=ref->key_length;
+ range->min_key= range->max_key= ref->key_buff;
+ 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 &&
(key_info->flags & HA_END_SPACE_KEY) == 0) ? EQ_RANGE : 0);
@@ -6507,12 +7948,12 @@ QUICK_RANGE_SELECT *get_quick_select_for_ref(THD *thd, TABLE *table,
{
key_part->part=part;
key_part->field= key_info->key_part[part].field;
- key_part->length= key_info->key_part[part].length;
+ key_part->length= key_info->key_part[part].length;
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;
}
- if (insert_dynamic(&quick->ranges,(gptr)&range))
+ if (insert_dynamic(&quick->ranges,(uchar*)&range))
goto err;
/*
@@ -6526,14 +7967,14 @@ QUICK_RANGE_SELECT *get_quick_select_for_ref(THD *thd, TABLE *table,
QUICK_RANGE *null_range;
*ref->null_ref_key= 1; // Set null byte then create a range
- if (!(null_range= new (alloc) QUICK_RANGE((char*)ref->key_buff,
- ref->key_length,
- (char*)ref->key_buff,
- ref->key_length,
- EQ_RANGE)))
+ if (!(null_range= new (alloc)
+ QUICK_RANGE(ref->key_buff, ref->key_length,
+ make_prev_keypart_map(ref->key_parts),
+ ref->key_buff, ref->key_length,
+ make_prev_keypart_map(ref->key_parts), EQ_RANGE)))
goto err;
*ref->null_ref_key= 0; // Clear null byte
- if (insert_dynamic(&quick->ranges,(gptr)&null_range))
+ if (insert_dynamic(&quick->ranges,(uchar*)&null_range))
goto err;
}
@@ -6553,10 +7994,9 @@ err:
rowids into Unique, get the sorted sequence and destroy the Unique.
If table has a clustered primary key that covers all rows (TRUE for bdb
- and innodb currently) and one of the index_merge scans is a scan on PK,
- then
- rows that will be retrieved by PK scan are not put into Unique and
- primary key scan is not performed here, it is performed later separately.
+ and innodb currently) and one of the index_merge scans is a scan on PK,
+ then rows that will be retrieved by PK scan are not put into Unique and
+ primary key scan is not performed here, it is performed later separately.
RETURN
0 OK
@@ -6569,21 +8009,12 @@ int QUICK_INDEX_MERGE_SELECT::read_keys_and_merge()
QUICK_RANGE_SELECT* cur_quick;
int result;
Unique *unique;
+ handler *file= head->file;
DBUG_ENTER("QUICK_INDEX_MERGE_SELECT::read_keys_and_merge");
/* We're going to just read rowids. */
- if (head->file->extra(HA_EXTRA_KEYREAD))
- DBUG_RETURN(1);
-
- /*
- Make innodb retrieve all PK member fields, so
- * ha_innobase::position (which uses them) call works.
- * We can filter out rows that will be retrieved by clustered PK.
- (This also creates a deficiency - it is possible that we will retrieve
- parts of key that are not used by current query at all.)
- */
- if (head->file->extra(HA_EXTRA_RETRIEVE_PRIMARY_KEY))
- DBUG_RETURN(1);
+ file->extra(HA_EXTRA_KEYREAD);
+ head->prepare_for_position();
cur_quick_it.rewind();
cur_quick= cur_quick_it++;
@@ -6596,8 +8027,8 @@ int QUICK_INDEX_MERGE_SELECT::read_keys_and_merge()
if (cur_quick->init() || cur_quick->reset())
DBUG_RETURN(1);
- unique= new Unique(refpos_order_cmp, (void *)head->file,
- head->file->ref_length,
+ unique= new Unique(refpos_order_cmp, (void *)file,
+ file->ref_length,
thd->variables.sortbuff_size);
if (!unique)
DBUG_RETURN(1);
@@ -6648,11 +8079,9 @@ int QUICK_INDEX_MERGE_SELECT::read_keys_and_merge()
result= unique->get(head);
delete unique;
doing_pk_scan= FALSE;
-
- /* Start the rnd_pos() scan. */
+ /* index_merge currently doesn't support "using index" at all */
+ file->extra(HA_EXTRA_NO_KEYREAD);
init_read_record(&read_record, thd, head, (SQL_SELECT*) 0, 1 , 1, TRUE);
- head->file->extra(HA_EXTRA_NO_KEYREAD);
-
DBUG_RETURN(result);
}
@@ -6674,9 +8103,7 @@ int QUICK_INDEX_MERGE_SELECT::get_next()
if (doing_pk_scan)
DBUG_RETURN(pk_quick_select->get_next());
- result= read_record.read_record(&read_record);
-
- if (result == -1)
+ if ((result= read_record.read_record(&read_record)) == -1)
{
result= HA_ERR_END_OF_FILE;
end_read_record(&read_record);
@@ -6685,7 +8112,8 @@ int QUICK_INDEX_MERGE_SELECT::get_next()
if (pk_quick_select)
{
doing_pk_scan= TRUE;
- if ((result= pk_quick_select->init()) || (result= pk_quick_select->reset()))
+ if ((result= pk_quick_select->init()) ||
+ (result= pk_quick_select->reset()))
DBUG_RETURN(result);
DBUG_RETURN(pk_quick_select->get_next());
}
@@ -6723,64 +8151,65 @@ int QUICK_ROR_INTERSECT_SELECT::get_next()
uint last_rowid_count=0;
DBUG_ENTER("QUICK_ROR_INTERSECT_SELECT::get_next");
- /* Get a rowid for first quick and save it as a 'candidate' */
- quick= quick_it++;
- if (cpk_quick)
+ do
{
- do {
- error= quick->get_next();
- }while (!error && !cpk_quick->row_in_ranges());
- }
- else
+ /* Get a rowid for first quick and save it as a 'candidate' */
+ quick= quick_it++;
error= quick->get_next();
-
- if (error)
- DBUG_RETURN(error);
-
- quick->file->position(quick->record);
- memcpy(last_rowid, quick->file->ref, head->file->ref_length);
- last_rowid_count= 1;
-
- while (last_rowid_count < quick_selects.elements)
- {
- if (!(quick= quick_it++))
+ if (cpk_quick)
{
- quick_it.rewind();
- quick= quick_it++;
+ while (!error && !cpk_quick->row_in_ranges())
+ error= quick->get_next();
}
+ if (error)
+ DBUG_RETURN(error);
- do {
- if ((error= quick->get_next()))
- DBUG_RETURN(error);
- quick->file->position(quick->record);
- cmp= head->file->cmp_ref(quick->file->ref, last_rowid);
- } while (cmp < 0);
+ quick->file->position(quick->record);
+ memcpy(last_rowid, quick->file->ref, head->file->ref_length);
+ last_rowid_count= 1;
- /* Ok, current select 'caught up' and returned ref >= cur_ref */
- if (cmp > 0)
+ while (last_rowid_count < quick_selects.elements)
{
- /* Found a row with ref > cur_ref. Make it a new 'candidate' */
- if (cpk_quick)
+ if (!(quick= quick_it++))
+ {
+ quick_it.rewind();
+ quick= quick_it++;
+ }
+
+ do
+ {
+ if ((error= quick->get_next()))
+ DBUG_RETURN(error);
+ quick->file->position(quick->record);
+ cmp= head->file->cmp_ref(quick->file->ref, last_rowid);
+ } while (cmp < 0);
+
+ /* Ok, current select 'caught up' and returned ref >= cur_ref */
+ if (cmp > 0)
{
- while (!cpk_quick->row_in_ranges())
+ /* Found a row with ref > cur_ref. Make it a new 'candidate' */
+ if (cpk_quick)
{
- if ((error= quick->get_next()))
- DBUG_RETURN(error);
+ while (!cpk_quick->row_in_ranges())
+ {
+ if ((error= quick->get_next()))
+ DBUG_RETURN(error);
+ }
}
+ memcpy(last_rowid, quick->file->ref, head->file->ref_length);
+ last_rowid_count= 1;
+ }
+ else
+ {
+ /* current 'candidate' row confirmed by this select */
+ last_rowid_count++;
}
- memcpy(last_rowid, quick->file->ref, head->file->ref_length);
- last_rowid_count= 1;
- }
- else
- {
- /* current 'candidate' row confirmed by this select */
- last_rowid_count++;
}
- }
- /* We get here iff we got the same row ref in all scans. */
- if (need_to_fetch_row)
- error= head->file->rnd_pos(head->record[0], last_rowid);
+ /* We get here if we got the same row ref in all scans. */
+ if (need_to_fetch_row)
+ error= head->file->rnd_pos(head->record[0], last_rowid);
+ } while (error == HA_ERR_RECORD_DELETED);
DBUG_RETURN(error);
}
@@ -6804,60 +8233,64 @@ int QUICK_ROR_UNION_SELECT::get_next()
{
int error, dup_row;
QUICK_SELECT_I *quick;
- byte *tmp;
+ uchar *tmp;
DBUG_ENTER("QUICK_ROR_UNION_SELECT::get_next");
do
{
- if (!queue.elements)
- DBUG_RETURN(HA_ERR_END_OF_FILE);
- /* Ok, we have a queue with >= 1 scans */
+ do
+ {
+ if (!queue.elements)
+ DBUG_RETURN(HA_ERR_END_OF_FILE);
+ /* Ok, we have a queue with >= 1 scans */
- quick= (QUICK_SELECT_I*)queue_top(&queue);
- memcpy(cur_rowid, quick->last_rowid, rowid_length);
+ quick= (QUICK_SELECT_I*)queue_top(&queue);
+ memcpy(cur_rowid, quick->last_rowid, rowid_length);
- /* put into queue rowid from the same stream as top element */
- if ((error= quick->get_next()))
- {
- if (error != HA_ERR_END_OF_FILE)
- DBUG_RETURN(error);
- queue_remove(&queue, 0);
- }
- else
- {
- quick->save_last_pos();
- queue_replaced(&queue);
- }
+ /* put into queue rowid from the same stream as top element */
+ if ((error= quick->get_next()))
+ {
+ if (error != HA_ERR_END_OF_FILE)
+ DBUG_RETURN(error);
+ queue_remove(&queue, 0);
+ }
+ else
+ {
+ quick->save_last_pos();
+ queue_replaced(&queue);
+ }
- if (!have_prev_rowid)
- {
- /* No rows have been returned yet */
- dup_row= FALSE;
- have_prev_rowid= TRUE;
- }
- else
- dup_row= !head->file->cmp_ref(cur_rowid, prev_rowid);
- }while (dup_row);
+ if (!have_prev_rowid)
+ {
+ /* No rows have been returned yet */
+ dup_row= FALSE;
+ have_prev_rowid= TRUE;
+ }
+ else
+ dup_row= !head->file->cmp_ref(cur_rowid, prev_rowid);
+ } while (dup_row);
- tmp= cur_rowid;
- cur_rowid= prev_rowid;
- prev_rowid= tmp;
+ tmp= cur_rowid;
+ cur_rowid= prev_rowid;
+ prev_rowid= tmp;
- error= head->file->rnd_pos(quick->record, prev_rowid);
+ error= head->file->rnd_pos(quick->record, prev_rowid);
+ } while (error == HA_ERR_RECORD_DELETED);
DBUG_RETURN(error);
}
+
int QUICK_RANGE_SELECT::reset()
{
uint mrange_bufsiz;
- byte *mrange_buff;
+ uchar *mrange_buff;
DBUG_ENTER("QUICK_RANGE_SELECT::reset");
next=0;
last_range= NULL;
in_range= FALSE;
cur_range= (QUICK_RANGE**) ranges.buffer;
- if (file->inited == handler::NONE && (error= file->ha_index_init(index)))
+ if (file->inited == handler::NONE && (error= file->ha_index_init(index,1)))
DBUG_RETURN(error);
/* Do not allocate the buffers twice. */
@@ -6886,15 +8319,16 @@ int QUICK_RANGE_SELECT::reset()
}
/* Allocate the handler buffer if necessary. */
- if (file->table_flags() & HA_NEED_READ_RANGE_BUFFER)
+ if (file->ha_table_flags() & HA_NEED_READ_RANGE_BUFFER)
{
mrange_bufsiz= min(multi_range_bufsiz,
((uint)QUICK_SELECT_I::records + 1)* head->s->reclength);
while (mrange_bufsiz &&
! my_multi_malloc(MYF(MY_WME),
- &multi_range_buff, sizeof(*multi_range_buff),
- &mrange_buff, mrange_bufsiz,
+ &multi_range_buff,
+ (uint) sizeof(*multi_range_buff),
+ &mrange_buff, (uint) mrange_bufsiz,
NullS))
{
/* Try to shrink the buffers until both are 0. */
@@ -6912,6 +8346,14 @@ int QUICK_RANGE_SELECT::reset()
multi_range_buff->buffer= mrange_buff;
multi_range_buff->buffer_end= mrange_buff + mrange_bufsiz;
multi_range_buff->end_of_used_area= mrange_buff;
+#ifdef HAVE_purify
+ /*
+ We need this until ndb will use the buffer efficiently
+ (Now ndb stores complete row in here, instead of only the used fields
+ which gives us valgrind warnings in compare_record[])
+ */
+ bzero((char*) mrange_buff, mrange_bufsiz);
+#endif
}
DBUG_RETURN(0);
}
@@ -6943,6 +8385,15 @@ int QUICK_RANGE_SELECT::get_next()
(cur_range >= (QUICK_RANGE**) ranges.buffer) &&
(cur_range <= (QUICK_RANGE**) ranges.buffer + ranges.elements));
+ if (in_ror_merged_scan)
+ {
+ /*
+ We don't need to signal the bitmap change as the bitmap is always the
+ same for this head->file
+ */
+ head->column_bitmaps_set_no_signal(&column_bitmap, &column_bitmap);
+ }
+
for (;;)
{
if (in_range)
@@ -6950,18 +8401,17 @@ int QUICK_RANGE_SELECT::get_next()
/* We did already start to read this key. */
result= file->read_multi_range_next(&mrange);
if (result != HA_ERR_END_OF_FILE)
- {
- in_range= ! result;
- DBUG_RETURN(result);
- }
+ goto end;
}
- uint count= min(multi_range_length, (uint) (ranges.elements -
- (cur_range - (QUICK_RANGE**) ranges.buffer)));
+ uint count= min(multi_range_length, ranges.elements -
+ (cur_range - (QUICK_RANGE**) ranges.buffer));
if (count == 0)
{
/* Ranges have already been used up before. None is left for read. */
in_range= FALSE;
+ if (in_ror_merged_scan)
+ head->column_bitmaps_set_no_signal(save_read_set, save_write_set);
DBUG_RETURN(HA_ERR_END_OF_FILE);
}
KEY_MULTI_RANGE *mrange_slot, *mrange_end;
@@ -6973,12 +8423,13 @@ int QUICK_RANGE_SELECT::get_next()
end_key= &mrange_slot->end_key;
last_range= *(cur_range++);
- start_key->key= (const byte*) last_range->min_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);
- end_key->key= (const byte*) last_range->max_key;
+ start_key->keypart_map= last_range->min_keypart_map;
+ end_key->key= (const uchar*) last_range->max_key;
end_key->length= last_range->max_length;
/*
We use HA_READ_AFTER_KEY here because if we are reading on a key
@@ -6986,6 +8437,7 @@ int QUICK_RANGE_SELECT::get_next()
*/
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;
mrange_slot->range_flag= last_range->flag;
}
@@ -6993,12 +8445,18 @@ int QUICK_RANGE_SELECT::get_next()
result= file->read_multi_range_first(&mrange, multi_range, count,
sorted, multi_range_buff);
if (result != HA_ERR_END_OF_FILE)
- {
- in_range= ! result;
- DBUG_RETURN(result);
- }
+ goto end;
in_range= FALSE; /* No matching rows; go to next set of ranges. */
}
+
+end:
+ in_range= ! result;
+ if (in_ror_merged_scan)
+ {
+ /* Restore bitmaps set on entry */
+ head->column_bitmaps_set_no_signal(save_read_set, save_write_set);
+ }
+ DBUG_RETURN(result);
}
/*
@@ -7029,7 +8487,9 @@ int QUICK_RANGE_SELECT::get_next()
other if some error occurred
*/
-int QUICK_RANGE_SELECT::get_next_prefix(uint prefix_length, byte *cur_prefix)
+int QUICK_RANGE_SELECT::get_next_prefix(uint prefix_length,
+ key_part_map keypart_map,
+ uchar *cur_prefix)
{
DBUG_ENTER("QUICK_RANGE_SELECT::get_next_prefix");
@@ -7041,13 +8501,13 @@ int QUICK_RANGE_SELECT::get_next_prefix(uint prefix_length, byte *cur_prefix)
{
/* Read the next record in the same range with prefix after cur_prefix. */
DBUG_ASSERT(cur_prefix != 0);
- result= file->index_read(record, cur_prefix, prefix_length,
- HA_READ_AFTER_KEY);
+ result= file->index_read_map(record, cur_prefix, keypart_map,
+ HA_READ_AFTER_KEY);
if (result || (file->compare_key(file->end_range) <= 0))
DBUG_RETURN(result);
}
- uint count= (uint) (ranges.elements - (cur_range - (QUICK_RANGE**) ranges.buffer));
+ uint count= ranges.elements - (cur_range - (QUICK_RANGE**) ranges.buffer);
if (count == 0)
{
/* Ranges have already been used up before. None is left for read. */
@@ -7056,13 +8516,15 @@ int QUICK_RANGE_SELECT::get_next_prefix(uint prefix_length, byte *cur_prefix)
}
last_range= *(cur_range++);
- start_key.key= (const byte*) last_range->min_key;
+ start_key.key= (const uchar*) last_range->min_key;
start_key.length= min(last_range->min_length, prefix_length);
+ start_key.keypart_map= last_range->min_keypart_map & keypart_map;
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);
- end_key.key= (const byte*) last_range->max_key;
+ end_key.key= (const uchar*) last_range->max_key;
end_key.length= min(last_range->max_length, prefix_length);
+ end_key.keypart_map= last_range->max_keypart_map & keypart_map;
/*
We use READ_AFTER_KEY here because if we are reading on a key
prefix we want to find all keys with this prefix
@@ -7070,8 +8532,8 @@ int QUICK_RANGE_SELECT::get_next_prefix(uint prefix_length, byte *cur_prefix)
end_key.flag= (last_range->flag & NEAR_MAX ? HA_READ_BEFORE_KEY :
HA_READ_AFTER_KEY);
- result= file->read_range_first(last_range->min_length ? &start_key : 0,
- last_range->max_length ? &end_key : 0,
+ result= file->read_range_first(last_range->min_keypart_map ? &start_key : 0,
+ last_range->max_keypart_map ? &end_key : 0,
test(last_range->flag & EQ_RANGE),
sorted);
if (last_range->flag == (UNIQUE_RANGE | EQ_RANGE))
@@ -7096,13 +8558,13 @@ int QUICK_RANGE_SELECT_GEOM::get_next()
if (last_range)
{
// Already read through key
- result= file->index_next_same(record, (byte*) last_range->min_key,
+ result= file->index_next_same(record, last_range->min_key,
last_range->min_length);
if (result != HA_ERR_END_OF_FILE)
DBUG_RETURN(result);
}
- uint count= (uint) (ranges.elements - (cur_range - (QUICK_RANGE**) ranges.buffer));
+ uint count= ranges.elements - (cur_range - (QUICK_RANGE**) ranges.buffer);
if (count == 0)
{
/* Ranges have already been used up before. None is left for read. */
@@ -7111,11 +8573,11 @@ int QUICK_RANGE_SELECT_GEOM::get_next()
}
last_range= *(cur_range++);
- result= file->index_read(record,
- (byte*) last_range->min_key,
- last_range->min_length,
- (ha_rkey_function)(last_range->flag ^ GEOM_FLAG));
- if (result != HA_ERR_KEY_NOT_FOUND)
+ result= file->index_read_map(record, last_range->min_key,
+ last_range->min_keypart_map,
+ (ha_rkey_function)(last_range->flag ^
+ GEOM_FLAG));
+ if (result != HA_ERR_KEY_NOT_FOUND && result != HA_ERR_END_OF_FILE)
DBUG_RETURN(result);
last_range= 0; // Not found, to next range
}
@@ -7174,7 +8636,7 @@ bool QUICK_RANGE_SELECT::row_in_ranges()
QUICK_SELECT_DESC::QUICK_SELECT_DESC(QUICK_RANGE_SELECT *q,
uint used_key_parts_arg)
- : QUICK_RANGE_SELECT(*q), rev_it(rev_ranges),
+ :QUICK_RANGE_SELECT(*q), rev_it(rev_ranges),
used_key_parts (used_key_parts_arg)
{
QUICK_RANGE *r;
@@ -7226,7 +8688,7 @@ int QUICK_SELECT_DESC::get_next()
{ // Already read through key
result = ((last_range->flag & EQ_RANGE &&
used_key_parts <= head->key_info[index].key_parts) ?
- file->index_next_same(record, (byte*) last_range->min_key,
+ file->index_next_same(record, last_range->min_key,
last_range->min_length) :
file->index_prev(record));
if (!result)
@@ -7256,8 +8718,9 @@ int QUICK_SELECT_DESC::get_next()
used_key_parts <= head->key_info[index].key_parts)
{
- result= file->index_read(record, (byte*) last_range->max_key,
- last_range->max_length, HA_READ_KEY_EXACT);
+ result = file->index_read_map(record, last_range->max_key,
+ last_range->max_keypart_map,
+ HA_READ_KEY_EXACT);
}
else
{
@@ -7265,15 +8728,15 @@ int QUICK_SELECT_DESC::get_next()
(last_range->flag & EQ_RANGE &&
used_key_parts > head->key_info[index].key_parts) ||
range_reads_after_key(last_range));
- result=file->index_read(record, (byte*) last_range->max_key,
- last_range->max_length,
- ((last_range->flag & NEAR_MAX) ?
- HA_READ_BEFORE_KEY :
- HA_READ_PREFIX_LAST_OR_PREV));
+ result=file->index_read_map(record, last_range->max_key,
+ last_range->max_keypart_map,
+ ((last_range->flag & NEAR_MAX) ?
+ HA_READ_BEFORE_KEY :
+ HA_READ_PREFIX_LAST_OR_PREV));
}
if (result)
{
- if (result != HA_ERR_KEY_NOT_FOUND)
+ if (result != HA_ERR_KEY_NOT_FOUND && result != HA_ERR_END_OF_FILE)
DBUG_RETURN(result);
last_range= 0; // Not found, to next range
continue;
@@ -7302,7 +8765,7 @@ int QUICK_RANGE_SELECT::cmp_next(QUICK_RANGE *range_arg)
KEY_PART *key_part=key_parts;
uint store_length;
- for (char *key=range_arg->max_key, *end=key+range_arg->max_length;
+ for (uchar *key=range_arg->max_key, *end=key+range_arg->max_length;
key < end;
key+= store_length, key_part++)
{
@@ -7321,7 +8784,7 @@ int QUICK_RANGE_SELECT::cmp_next(QUICK_RANGE *range_arg)
key++; // Skip null byte
store_length--;
}
- if ((cmp=key_part->field->key_cmp((byte*) key, key_part->length)) < 0)
+ if ((cmp=key_part->field->key_cmp(key, key_part->length)) < 0)
return 0;
if (cmp > 0)
return 1;
@@ -7340,7 +8803,7 @@ int QUICK_RANGE_SELECT::cmp_prev(QUICK_RANGE *range_arg)
if (range_arg->flag & NO_MIN_RANGE)
return 0; /* key can't be to small */
- cmp= key_cmp(key_part_info, (byte*) range_arg->min_key,
+ cmp= key_cmp(key_part_info, range_arg->min_key,
range_arg->min_length);
if (cmp > 0 || cmp == 0 && !(range_arg->flag & NEAR_MIN))
return 0;
@@ -7435,18 +8898,18 @@ void QUICK_RANGE_SELECT::add_keys_and_lengths(String *key_names,
String *used_lengths)
{
char buf[64];
- size_t length;
+ uint length;
KEY *key_info= head->key_info + index;
key_names->append(key_info->name);
length= longlong2str(max_used_key_length, buf, 10) - buf;
- used_lengths->append(buf, (uint) length);
+ used_lengths->append(buf, length);
}
void QUICK_INDEX_MERGE_SELECT::add_keys_and_lengths(String *key_names,
String *used_lengths)
{
char buf[64];
- size_t length;
+ uint length;
bool first= TRUE;
QUICK_RANGE_SELECT *quick;
@@ -7464,7 +8927,7 @@ void QUICK_INDEX_MERGE_SELECT::add_keys_and_lengths(String *key_names,
KEY *key_info= head->key_info + quick->index;
key_names->append(key_info->name);
length= longlong2str(quick->max_used_key_length, buf, 10) - buf;
- used_lengths->append(buf, (uint) length);
+ used_lengths->append(buf, length);
}
if (pk_quick_select)
{
@@ -7473,7 +8936,7 @@ void QUICK_INDEX_MERGE_SELECT::add_keys_and_lengths(String *key_names,
key_names->append(key_info->name);
length= longlong2str(pk_quick_select->max_used_key_length, buf, 10) - buf;
used_lengths->append(',');
- used_lengths->append(buf, (uint) length);
+ used_lengths->append(buf, length);
}
}
@@ -7481,7 +8944,7 @@ void QUICK_ROR_INTERSECT_SELECT::add_keys_and_lengths(String *key_names,
String *used_lengths)
{
char buf[64];
- size_t length;
+ uint length;
bool first= TRUE;
QUICK_RANGE_SELECT *quick;
List_iterator_fast<QUICK_RANGE_SELECT> it(quick_selects);
@@ -7497,7 +8960,7 @@ void QUICK_ROR_INTERSECT_SELECT::add_keys_and_lengths(String *key_names,
}
key_names->append(key_info->name);
length= longlong2str(quick->max_used_key_length, buf, 10) - buf;
- used_lengths->append(buf, (uint) length);
+ used_lengths->append(buf, length);
}
if (cpk_quick)
@@ -7507,7 +8970,7 @@ void QUICK_ROR_INTERSECT_SELECT::add_keys_and_lengths(String *key_names,
key_names->append(key_info->name);
length= longlong2str(cpk_quick->max_used_key_length, buf, 10) - buf;
used_lengths->append(',');
- used_lengths->append(buf, (uint) length);
+ used_lengths->append(buf, length);
}
}
@@ -7538,12 +9001,11 @@ 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_constant_key_infix(KEY *index_info, SEL_ARG *index_range_tree,
+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,
KEY_PART_INFO *last_part, THD *thd,
- byte *key_infix, uint *key_infix_len,
+ uchar *key_infix, uint *key_infix_len,
KEY_PART_INFO **first_non_infix_part);
static bool
check_group_min_max_predicates(COND *cond, Item_field *min_max_arg_item,
@@ -7617,9 +9079,10 @@ cost_group_min_max(TABLE* table, KEY *index_info, uint used_key_parts,
groups, and thus can be applied after the grouping.
GA4. There are no expressions among G_i, just direct column references.
NGA1.If in the index I there is a gap between the last GROUP attribute G_k,
- and the MIN/MAX attribute C, then NGA must consist of exactly the index
- attributes that constitute the gap. As a result there is a permutation
- of NGA that coincides with the gap in the index <B_1, ..., B_m>.
+ and the MIN/MAX attribute C, then NGA must consist of exactly the
+ index attributes that constitute the gap. As a result there is a
+ permutation of NGA that coincides with the gap in the index
+ <B_1, ..., B_m>.
NGA2.If BA <> {}, then the WHERE clause must contain a conjunction EQ of
equality conditions for all NG_i of the form (NG_i = const) or
(const = NG_i), such that each NG_i is referenced in exactly one
@@ -7627,9 +9090,10 @@ cost_group_min_max(TABLE* table, KEY *index_info, uint used_key_parts,
gap in the index.
WA1. There are no other attributes in the WHERE clause except the ones
referenced in predicates RNG, PA, PC, EQ defined above. Therefore
- WA is subset of (GA union NGA union C) for GA,NGA,C that pass the 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).
+ WA is subset of (GA union NGA union C) for GA,NGA,C that pass the
+ 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).
C) Overall query form:
SELECT EXPR([A_1,...,A_k], [B_1,...,B_m], [MIN(C)], [MAX(C)])
@@ -7691,14 +9155,14 @@ get_best_group_min_max(PARAM *param, SEL_TREE *tree)
TABLE *table= param->table;
bool have_min= FALSE; /* TRUE if there is a MIN function. */
bool have_max= FALSE; /* TRUE if there is a MAX function. */
- Item_field *min_max_arg_item= NULL;/* The argument of all MIN/MAX functions.*/
+ Item_field *min_max_arg_item= NULL; // The argument of all MIN/MAX functions
KEY_PART_INFO *min_max_arg_part= NULL; /* The corresponding keypart. */
uint group_prefix_len= 0; /* Length (in bytes) of the key prefix. */
KEY *index_info= NULL; /* The index chosen for data access. */
uint index= 0; /* The id of the chosen index. */
- uint group_key_parts= 0; /* Number of index key parts in the group prefix. */
+ uint group_key_parts= 0; // Number of index key parts in the group prefix.
uint used_key_parts= 0; /* Number of index key parts used for access. */
- byte key_infix[MAX_KEY_LENGTH]; /* Constants from equality predicates.*/
+ uchar key_infix[MAX_KEY_LENGTH]; /* Constants from equality predicates.*/
uint key_infix_len= 0; /* Length of key_infix. */
TRP_GROUP_MIN_MAX *read_plan= NULL; /* The eventually constructed TRP. */
uint key_part_nr;
@@ -7802,7 +9266,7 @@ get_best_group_min_max(PARAM *param, SEL_TREE *tree)
cur_index_info++, cur_index++)
{
/* Check (B1) - if current index is covering. */
- if (!table->used_keys.is_set(cur_index))
+ if (!table->covering_keys.is_set(cur_index))
goto next_index;
/*
@@ -7815,28 +9279,19 @@ get_best_group_min_max(PARAM *param, SEL_TREE *tree)
we check that all query fields are indeed covered by 'cur_index'.
*/
if (pk < MAX_KEY && cur_index != pk &&
- (table->file->table_flags() & HA_PRIMARY_KEY_IN_READ_INDEX))
+ (table->file->ha_table_flags() & HA_PRIMARY_KEY_IN_READ_INDEX))
{
/* For each table field */
for (uint i= 0; i < table->s->fields; i++)
{
Field *cur_field= table->field[i];
/*
- If the field is used in the current query, check that the
- field is covered by some keypart of the current index.
+ If the field is used in the current query ensure that it's
+ part of 'cur_index'
*/
- if (thd->query_id == cur_field->query_id)
- {
- KEY_PART_INFO *key_part= cur_index_info->key_part;
- KEY_PART_INFO *key_part_end= key_part + cur_index_info->key_parts;
- for (;;)
- {
- if (key_part->field == cur_field)
- break;
- if (++key_part == key_part_end)
- goto next_index; // Field was not part of key
- }
- }
+ if (bitmap_is_set(table->read_set, cur_field->field_index) &&
+ !cur_field->part_of_key_not_clustered.is_set(cur_index))
+ goto next_index; // Field was not part of key
}
}
@@ -7945,7 +9400,7 @@ get_best_group_min_max(PARAM *param, SEL_TREE *tree)
NULL;
first_non_infix_part= min_max_arg_part ?
(min_max_arg_part < last_part) ?
- min_max_arg_part + 1 :
+ min_max_arg_part :
NULL :
NULL;
if (first_non_group_part &&
@@ -7990,8 +9445,8 @@ get_best_group_min_max(PARAM *param, SEL_TREE *tree)
key_part_range[1]= last_part;
/* Check if cur_part is referenced in the WHERE clause. */
- if (join->conds->walk(&Item::find_item_in_field_list_processor,
- (byte*) key_part_range))
+ if (join->conds->walk(&Item::find_item_in_field_list_processor, 0,
+ (uchar*) key_part_range))
goto next_index;
}
}
@@ -8002,15 +9457,17 @@ get_best_group_min_max(PARAM *param, SEL_TREE *tree)
*/
if (first_non_infix_part)
{
- for (cur_part= first_non_infix_part; cur_part != last_part; cur_part++)
+ cur_part= first_non_infix_part +
+ (min_max_arg_part && (min_max_arg_part < last_part));
+ for (; cur_part != last_part; cur_part++)
{
- if (cur_part->field->query_id == thd->query_id)
+ if (bitmap_is_set(table->read_set, cur_part->field->field_index))
goto next_index;
}
}
/* If we got to this point, cur_index_info passes the test. */
- key_infix_parts= key_infix_len ? (uint)
+ key_infix_parts= key_infix_len ?
(first_non_infix_part - first_non_group_part) : 0;
used_key_parts= cur_group_key_parts + key_infix_parts;
@@ -8022,7 +9479,7 @@ get_best_group_min_max(PARAM *param, SEL_TREE *tree)
&cur_param_idx);
/* Check if this range tree can be used for prefix retrieval. */
cur_quick_prefix_records= check_quick_select(param, cur_param_idx,
- cur_index_tree);
+ cur_index_tree, TRUE);
}
cost_group_min_max(table, cur_index_info, used_key_parts,
cur_group_key_parts, tree, cur_index_tree,
@@ -8259,7 +9716,7 @@ 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,
KEY_PART_INFO *last_part, THD *thd,
- byte *key_infix, uint *key_infix_len,
+ uchar *key_infix, uint *key_infix_len,
KEY_PART_INFO **first_non_infix_part)
{
SEL_ARG *cur_range;
@@ -8268,7 +9725,7 @@ get_constant_key_infix(KEY *index_info, SEL_ARG *index_range_tree,
KEY_PART_INFO *end_part= min_max_arg_part ? min_max_arg_part : last_part;
*key_infix_len= 0;
- byte *key_ptr= key_infix;
+ uchar *key_ptr= key_infix;
for (cur_part= first_non_group_part; cur_part != end_part; cur_part++)
{
/*
@@ -8302,10 +9759,10 @@ get_constant_key_infix(KEY *index_info, SEL_ARG *index_range_tree,
uint field_length= cur_part->store_length;
if ((cur_range->maybe_null &&
- cur_range->min_value[0] && cur_range->max_value[0])
- ||
- (memcmp(cur_range->min_value, cur_range->max_value, field_length) == 0))
- { /* cur_range specifies 'IS NULL' or an equality condition. */
+ cur_range->min_value[0] && cur_range->max_value[0]) ||
+ !memcmp(cur_range->min_value, cur_range->max_value, field_length))
+ {
+ /* cur_range specifies 'IS NULL' or an equality condition. */
memcpy(key_ptr, cur_range->min_value, field_length);
key_ptr+= field_length;
*key_infix_len+= field_length;
@@ -8346,7 +9803,7 @@ get_field_keypart(KEY *index, Field *field)
for (part= index->key_part, end= part + index->key_parts; part < end; part++)
{
if (field->eq(part->field))
- return (uint) (part - index->key_part + 1);
+ return part - index->key_part + 1;
}
return 0;
}
@@ -8469,8 +9926,8 @@ void cost_group_min_max(TABLE* table, KEY *index_info, uint used_key_parts,
double cpu_cost= 0; /* TODO: CPU cost of index_read calls? */
DBUG_ENTER("cost_group_min_max");
- table_records= table->file->records;
- keys_per_block= (table->file->block_size / 2 /
+ table_records= table->file->stats.records;
+ keys_per_block= (table->file->stats.block_size / 2 /
(index_info->key_length + table->file->ref_length)
+ 1);
num_blocks= (uint)(table_records / keys_per_block) + 1;
@@ -8525,7 +9982,7 @@ void cost_group_min_max(TABLE* table, KEY *index_info, uint used_key_parts,
DBUG_PRINT("info",
("table rows: %lu keys/block: %u keys/group: %u result rows: %lu blocks: %u",
- (ulong)table_records, keys_per_block, keys_per_group,
+ (ulong)table_records, keys_per_block, keys_per_group,
(ulong) *records, num_blocks));
DBUG_VOID_RETURN;
}
@@ -8549,7 +10006,7 @@ void cost_group_min_max(TABLE* table, KEY *index_info, uint used_key_parts,
RETURN
New QUICK_GROUP_MIN_MAX_SELECT object if successfully created,
- NULL o/w.
+ NULL otherwise.
*/
QUICK_SELECT_I *
@@ -8562,10 +10019,10 @@ 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,
- group_prefix_len, used_key_parts,
- index_info, index, read_cost, records,
- key_infix_len, key_infix,
- parent_alloc);
+ group_prefix_len, group_key_parts,
+ used_key_parts, index_info, index,
+ read_cost, records, key_infix_len,
+ key_infix, parent_alloc);
if (!quick)
DBUG_RETURN(NULL);
@@ -8654,13 +10111,14 @@ QUICK_GROUP_MIN_MAX_SELECT::
QUICK_GROUP_MIN_MAX_SELECT(TABLE *table, JOIN *join_arg, bool have_min_arg,
bool have_max_arg,
KEY_PART_INFO *min_max_arg_part_arg,
- uint group_prefix_len_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,
- byte *key_infix_arg, MEM_ROOT *parent_alloc)
+ uchar *key_infix_arg, MEM_ROOT *parent_alloc)
:join(join_arg), index_info(index_info_arg),
- group_prefix_len(group_prefix_len_arg), have_min(have_min_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),
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),
@@ -8674,6 +10132,7 @@ QUICK_GROUP_MIN_MAX_SELECT(TABLE *table, JOIN *join_arg, bool have_min_arg,
read_time= read_cost_arg;
records= records_arg;
used_key_parts= used_key_parts_arg;
+ real_key_parts= used_key_parts_arg;
real_prefix_len= group_prefix_len + key_infix_len;
group_prefix= NULL;
min_max_arg_len= min_max_arg_part ? min_max_arg_part->store_length : 0;
@@ -8715,13 +10174,13 @@ int QUICK_GROUP_MIN_MAX_SELECT::init()
if (group_prefix) /* Already initialized. */
return 0;
- if (!(last_prefix= (byte*) alloc_root(&alloc, group_prefix_len)))
+ if (!(last_prefix= (uchar*) alloc_root(&alloc, group_prefix_len)))
return 1;
/*
We may use group_prefix to store keys with all select fields, so allocate
enough space for it.
*/
- if (!(group_prefix= (byte*) alloc_root(&alloc,
+ if (!(group_prefix= (uchar*) alloc_root(&alloc,
real_prefix_len + min_max_arg_len)))
return 1;
@@ -8731,7 +10190,7 @@ int QUICK_GROUP_MIN_MAX_SELECT::init()
The memory location pointed to by key_infix will be deleted soon, so
allocate a new buffer and copy the key_infix into it.
*/
- byte *tmp_key_infix= (byte*) alloc_root(&alloc, key_infix_len);
+ uchar *tmp_key_infix= (uchar*) alloc_root(&alloc, key_infix_len);
if (!tmp_key_infix)
return 1;
memcpy(tmp_key_infix, this->key_infix, key_infix_len);
@@ -8840,11 +10299,13 @@ bool QUICK_GROUP_MIN_MAX_SELECT::add_range(SEL_ARG *sel_range)
range_flag|= EQ_RANGE; /* equality condition */
}
range= new QUICK_RANGE(sel_range->min_value, min_max_arg_len,
+ make_keypart_map(sel_range->part),
sel_range->max_value, min_max_arg_len,
+ make_keypart_map(sel_range->part),
range_flag);
if (!range)
return TRUE;
- if (insert_dynamic(&min_max_ranges, (gptr)&range))
+ if (insert_dynamic(&min_max_ranges, (uchar*)&range))
return TRUE;
return FALSE;
}
@@ -8879,7 +10340,7 @@ void QUICK_GROUP_MIN_MAX_SELECT::adjust_prefix_ranges ()
{
QUICK_RANGE *range;
- get_dynamic(arr, (gptr)&range, inx);
+ get_dynamic(arr, (uchar*)&range, inx);
range->flag &= ~(NEAR_MIN | NEAR_MAX);
}
}
@@ -8915,7 +10376,7 @@ void QUICK_GROUP_MIN_MAX_SELECT::update_key_stat()
QUICK_RANGE *cur_range;
if (have_min)
{ /* Check if the right-most range has a lower boundary. */
- get_dynamic(&min_max_ranges, (gptr)&cur_range,
+ get_dynamic(&min_max_ranges, (uchar*)&cur_range,
min_max_ranges.elements - 1);
if (!(cur_range->flag & NO_MIN_RANGE))
{
@@ -8926,7 +10387,7 @@ void QUICK_GROUP_MIN_MAX_SELECT::update_key_stat()
}
if (have_max)
{ /* Check if the left-most range has an upper boundary. */
- get_dynamic(&min_max_ranges, (gptr)&cur_range, 0);
+ get_dynamic(&min_max_ranges, (uchar*)&cur_range, 0);
if (!(cur_range->flag & NO_MAX_RANGE))
{
max_used_key_length+= min_max_arg_len;
@@ -8973,7 +10434,7 @@ int QUICK_GROUP_MIN_MAX_SELECT::reset(void)
DBUG_ENTER("QUICK_GROUP_MIN_MAX_SELECT::reset");
file->extra(HA_EXTRA_KEYREAD); /* We need only the key attributes */
- if ((result= file->ha_index_init(index)))
+ if ((result= file->ha_index_init(index,1)))
DBUG_RETURN(result);
if (quick_prefix_select && quick_prefix_select->reset())
DBUG_RETURN(1);
@@ -9079,14 +10540,16 @@ int QUICK_GROUP_MIN_MAX_SELECT::get_next()
first sub-group with the extended prefix.
*/
if (!have_min && !have_max && key_infix_len > 0)
- result= file->index_read(record, group_prefix, real_prefix_len,
- HA_READ_KEY_EXACT);
+ result= file->index_read_map(record, group_prefix,
+ make_prev_keypart_map(real_key_parts),
+ HA_READ_KEY_EXACT);
result= have_min ? min_res : have_max ? max_res : result;
- }
- while (result == HA_ERR_KEY_NOT_FOUND && is_last_prefix != 0);
+ } 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
@@ -9094,6 +10557,7 @@ int QUICK_GROUP_MIN_MAX_SELECT::get_next()
other fields in non-ANSI SQL mode).
*/
copy_fields(&join->tmp_table_param);
+ }
else if (result == HA_ERR_KEY_NOT_FOUND)
result= HA_ERR_END_OF_FILE;
@@ -9120,6 +10584,7 @@ int QUICK_GROUP_MIN_MAX_SELECT::get_next()
RETURN
0 on success
HA_ERR_KEY_NOT_FOUND if no MIN key was found that fulfills all conditions.
+ HA_ERR_END_OF_FILE - "" -
other if some error occurred
*/
@@ -9139,8 +10604,9 @@ int QUICK_GROUP_MIN_MAX_SELECT::next_min()
/* Apply the constant equality conditions to the non-group select fields */
if (key_infix_len > 0)
{
- if ((result= file->index_read(record, group_prefix, real_prefix_len,
- HA_READ_KEY_EXACT)))
+ if ((result= file->index_read_map(record, group_prefix,
+ make_prev_keypart_map(real_key_parts),
+ HA_READ_KEY_EXACT)))
DBUG_RETURN(result);
}
@@ -9155,9 +10621,9 @@ int QUICK_GROUP_MIN_MAX_SELECT::next_min()
{
/* Find the first subsequent record without NULL in the MIN/MAX field. */
key_copy(tmp_record, record, index_info, 0);
- result= file->index_read(record, tmp_record,
- real_prefix_len + min_max_arg_len,
- HA_READ_AFTER_KEY);
+ result= file->index_read_map(record, tmp_record,
+ make_keypart_map(real_key_parts),
+ HA_READ_AFTER_KEY);
/*
Check if the new record belongs to the current group by comparing its
prefix with the group's prefix. If it is from the next group, then the
@@ -9173,7 +10639,7 @@ int QUICK_GROUP_MIN_MAX_SELECT::next_min()
if (key_cmp(index_info->key_part, group_prefix, real_prefix_len))
key_restore(record, tmp_record, index_info, 0);
}
- else if (result == HA_ERR_KEY_NOT_FOUND)
+ else if (result == HA_ERR_KEY_NOT_FOUND || result == HA_ERR_END_OF_FILE)
result= 0; /* There is a result in any case. */
}
}
@@ -9198,6 +10664,7 @@ int QUICK_GROUP_MIN_MAX_SELECT::next_min()
RETURN
0 on success
HA_ERR_KEY_NOT_FOUND if no MAX key was found that fulfills all conditions.
+ HA_ERR_END_OF_FILE - "" -
other if some error occurred
*/
@@ -9211,8 +10678,9 @@ int QUICK_GROUP_MIN_MAX_SELECT::next_max()
if (min_max_ranges.elements > 0)
result= next_max_in_range();
else
- result= file->index_read(record, group_prefix, real_prefix_len,
- HA_READ_PREFIX_LAST);
+ result= file->index_read_map(record, group_prefix,
+ make_prev_keypart_map(real_key_parts),
+ HA_READ_PREFIX_LAST);
DBUG_RETURN(result);
}
@@ -9245,9 +10713,9 @@ int QUICK_GROUP_MIN_MAX_SELECT::next_prefix()
if (quick_prefix_select)
{
- byte *cur_prefix= seen_first_key ? group_prefix : NULL;
+ uchar *cur_prefix= seen_first_key ? group_prefix : NULL;
if ((result= quick_prefix_select->get_next_prefix(group_prefix_len,
- cur_prefix)))
+ make_prev_keypart_map(group_key_parts), cur_prefix)))
DBUG_RETURN(result);
seen_first_key= TRUE;
}
@@ -9263,8 +10731,9 @@ int QUICK_GROUP_MIN_MAX_SELECT::next_prefix()
else
{
/* Load the first key in this group into record. */
- result= file->index_read(record, group_prefix, group_prefix_len,
- HA_READ_AFTER_KEY);
+ result= file->index_read_map(record, group_prefix,
+ make_prev_keypart_map(group_key_parts),
+ HA_READ_AFTER_KEY);
if (result)
DBUG_RETURN(result);
}
@@ -9298,13 +10767,14 @@ int QUICK_GROUP_MIN_MAX_SELECT::next_prefix()
0 on success
HA_ERR_KEY_NOT_FOUND if there is no key with the given prefix in any of
the ranges
+ HA_ERR_END_OF_FILE - "" -
other if some error
*/
int QUICK_GROUP_MIN_MAX_SELECT::next_min_in_range()
{
ha_rkey_function find_flag;
- uint search_prefix_len;
+ key_part_map keypart_map;
QUICK_RANGE *cur_range;
bool found_null= FALSE;
int result= HA_ERR_KEY_NOT_FOUND;
@@ -9313,40 +10783,40 @@ int QUICK_GROUP_MIN_MAX_SELECT::next_min_in_range()
for (uint range_idx= 0; range_idx < min_max_ranges.elements; range_idx++)
{ /* Search from the left-most range to the right. */
- get_dynamic(&min_max_ranges, (gptr)&cur_range, range_idx);
+ get_dynamic(&min_max_ranges, (uchar*)&cur_range, range_idx);
/*
If the current value for the min/max argument is bigger than the right
boundary of cur_range, there is no need to check this range.
*/
if (range_idx != 0 && !(cur_range->flag & NO_MAX_RANGE) &&
- (key_cmp(min_max_arg_part, (const byte*) cur_range->max_key,
+ (key_cmp(min_max_arg_part, (const uchar*) cur_range->max_key,
min_max_arg_len) == 1))
continue;
if (cur_range->flag & NO_MIN_RANGE)
{
+ keypart_map= make_prev_keypart_map(real_key_parts);
find_flag= HA_READ_KEY_EXACT;
- search_prefix_len= real_prefix_len;
}
else
{
/* Extend the search key with the lower boundary for this range. */
memcpy(group_prefix + real_prefix_len, cur_range->min_key,
cur_range->min_length);
- search_prefix_len= real_prefix_len + min_max_arg_len;
+ keypart_map= make_keypart_map(real_key_parts);
find_flag= (cur_range->flag & (EQ_RANGE | NULL_RANGE)) ?
HA_READ_KEY_EXACT : (cur_range->flag & NEAR_MIN) ?
HA_READ_AFTER_KEY : HA_READ_KEY_OR_NEXT;
}
- result= file->index_read(record, group_prefix, search_prefix_len,
- find_flag);
- if ((result == HA_ERR_KEY_NOT_FOUND) &&
- (cur_range->flag & (EQ_RANGE | NULL_RANGE)))
- continue; /* Check the next range. */
- else if (result)
+ result= file->index_read_map(record, group_prefix, keypart_map, find_flag);
+ if (result)
{
+ if ((result == HA_ERR_KEY_NOT_FOUND || result == HA_ERR_END_OF_FILE) &&
+ (cur_range->flag & (EQ_RANGE | NULL_RANGE)))
+ continue; /* Check the next range. */
+
/*
In all other cases (HA_ERR_*, HA_READ_KEY_EXACT with NO_MIN_RANGE,
HA_READ_AFTER_KEY, HA_READ_KEY_OR_NEXT) if the lookup failed for this
@@ -9373,7 +10843,7 @@ int QUICK_GROUP_MIN_MAX_SELECT::next_min_in_range()
/* Check if record belongs to the current group. */
if (key_cmp(index_info->key_part, group_prefix, real_prefix_len))
{
- result = HA_ERR_KEY_NOT_FOUND;
+ result= HA_ERR_KEY_NOT_FOUND;
continue;
}
@@ -9381,7 +10851,7 @@ int QUICK_GROUP_MIN_MAX_SELECT::next_min_in_range()
if ( !(cur_range->flag & NO_MAX_RANGE) )
{
/* Compose the MAX key for the range. */
- byte *max_key= (byte*) my_alloca(real_prefix_len + min_max_arg_len);
+ uchar *max_key= (uchar*) my_alloca(real_prefix_len + min_max_arg_len);
memcpy(max_key, group_prefix, real_prefix_len);
memcpy(max_key + real_prefix_len, cur_range->max_key,
cur_range->max_length);
@@ -9391,7 +10861,7 @@ int QUICK_GROUP_MIN_MAX_SELECT::next_min_in_range()
if (!((cur_range->flag & NEAR_MAX) && (cmp_res == -1) ||
(cmp_res <= 0)))
{
- result = HA_ERR_KEY_NOT_FOUND;
+ result= HA_ERR_KEY_NOT_FOUND;
continue;
}
}
@@ -9430,13 +10900,14 @@ int QUICK_GROUP_MIN_MAX_SELECT::next_min_in_range()
0 on success
HA_ERR_KEY_NOT_FOUND if there is no key with the given prefix in any of
the ranges
+ HA_ERR_END_OF_FILE - "" -
other if some error
*/
int QUICK_GROUP_MIN_MAX_SELECT::next_max_in_range()
{
ha_rkey_function find_flag;
- uint search_prefix_len;
+ key_part_map keypart_map;
QUICK_RANGE *cur_range;
int result;
@@ -9444,7 +10915,7 @@ int QUICK_GROUP_MIN_MAX_SELECT::next_max_in_range()
for (uint range_idx= min_max_ranges.elements; range_idx > 0; range_idx--)
{ /* Search from the right-most range to the left. */
- get_dynamic(&min_max_ranges, (gptr)&cur_range, range_idx - 1);
+ get_dynamic(&min_max_ranges, (uchar*)&cur_range, range_idx - 1);
/*
If the current value for the min/max argument is smaller than the left
@@ -9452,33 +10923,34 @@ int QUICK_GROUP_MIN_MAX_SELECT::next_max_in_range()
*/
if (range_idx != min_max_ranges.elements &&
!(cur_range->flag & NO_MIN_RANGE) &&
- (key_cmp(min_max_arg_part, (const byte*) cur_range->min_key,
+ (key_cmp(min_max_arg_part, (const uchar*) cur_range->min_key,
min_max_arg_len) == -1))
continue;
if (cur_range->flag & NO_MAX_RANGE)
{
+ keypart_map= make_prev_keypart_map(real_key_parts);
find_flag= HA_READ_PREFIX_LAST;
- search_prefix_len= real_prefix_len;
}
else
{
/* Extend the search key with the upper boundary for this range. */
memcpy(group_prefix + real_prefix_len, cur_range->max_key,
cur_range->max_length);
- search_prefix_len= real_prefix_len + min_max_arg_len;
+ keypart_map= make_keypart_map(real_key_parts);
find_flag= (cur_range->flag & EQ_RANGE) ?
HA_READ_KEY_EXACT : (cur_range->flag & NEAR_MAX) ?
HA_READ_BEFORE_KEY : HA_READ_PREFIX_LAST_OR_PREV;
}
- result= file->index_read(record, group_prefix, search_prefix_len,
- find_flag);
+ result= file->index_read_map(record, group_prefix, keypart_map, find_flag);
- if ((result == HA_ERR_KEY_NOT_FOUND) && (cur_range->flag & EQ_RANGE))
- continue; /* Check the next range. */
if (result)
{
+ if ((result == HA_ERR_KEY_NOT_FOUND || result == HA_ERR_END_OF_FILE) &&
+ (cur_range->flag & EQ_RANGE))
+ continue; /* Check the next range. */
+
/*
In no key was found with this upper bound, there certainly are no keys
in the ranges to the left.
@@ -9497,7 +10969,7 @@ int QUICK_GROUP_MIN_MAX_SELECT::next_max_in_range()
if ( !(cur_range->flag & NO_MIN_RANGE) )
{
/* Compose the MIN key for the range. */
- byte *min_key= (byte*) my_alloca(real_prefix_len + min_max_arg_len);
+ uchar *min_key= (uchar*) my_alloca(real_prefix_len + min_max_arg_len);
memcpy(min_key, group_prefix, real_prefix_len);
memcpy(min_key + real_prefix_len, cur_range->min_key,
cur_range->min_length);
@@ -9601,7 +11073,7 @@ void QUICK_GROUP_MIN_MAX_SELECT::add_keys_and_lengths(String *key_names,
char buf[64];
uint length;
key_names->append(index_info->name);
- length= (uint) (longlong2str(max_used_key_length, buf, 10) - buf);
+ length= longlong2str(max_used_key_length, buf, 10) - buf;
used_lengths->append(buf, length);
}
@@ -9633,7 +11105,7 @@ static void print_sel_tree(PARAM *param, SEL_TREE *tree, key_map *tree_map,
if (!tmp.length())
tmp.append(STRING_WITH_LEN("(empty)"));
- DBUG_PRINT("info", ("SEL_TREE %p (%s) scans:%s", tree, msg, tmp.ptr()));
+ DBUG_PRINT("info", ("SEL_TREE: 0x%lx (%s) scans: %s", (long) tree, msg, tmp.ptr()));
DBUG_VOID_RETURN;
}
@@ -9643,7 +11115,7 @@ static void print_ror_scans_arr(TABLE *table, const char *msg,
struct st_ror_scan_info **start,
struct st_ror_scan_info **end)
{
- DBUG_ENTER("print_ror_scans");
+ DBUG_ENTER("print_ror_scans_arr");
char buff[1024];
String tmp(buff,sizeof(buff),&my_charset_bin);
@@ -9668,12 +11140,16 @@ static void print_ror_scans_arr(TABLE *table, const char *msg,
*****************************************************************************/
static void
-print_key(KEY_PART *key_part,const char *key,uint used_length)
+print_key(KEY_PART *key_part, const uchar *key, uint used_length)
{
char buff[1024];
- const char *key_end= key+used_length;
+ const uchar *key_end= key+used_length;
String tmp(buff,sizeof(buff),&my_charset_bin);
uint store_length;
+ TABLE *table= key_part->field->table;
+ my_bitmap_map *old_sets[2];
+
+ dbug_tmp_use_all_columns(table, old_sets, table->read_set, table->write_set);
for (; key < key_end; key+=store_length, key_part++)
{
@@ -9690,7 +11166,7 @@ print_key(KEY_PART *key_part,const char *key,uint used_length)
key++; // Skip null byte
store_length--;
}
- field->set_key_image((char*) key, key_part->length);
+ field->set_key_image(key, key_part->length);
if (field->type() == MYSQL_TYPE_BIT)
(void) field->val_int_as_str(&tmp, 1);
else
@@ -9699,18 +11175,25 @@ print_key(KEY_PART *key_part,const char *key,uint used_length)
if (key+store_length < key_end)
fputc('/',DBUG_FILE);
}
+ dbug_tmp_restore_column_maps(table->read_set, table->write_set, old_sets);
}
static void print_quick(QUICK_SELECT_I *quick, const key_map *needed_reg)
{
char buf[MAX_KEY/8+1];
+ TABLE *table;
+ my_bitmap_map *old_sets[2];
DBUG_ENTER("print_quick");
if (!quick)
DBUG_VOID_RETURN;
DBUG_LOCK_FILE;
+ table= quick->head;
+ dbug_tmp_use_all_columns(table, old_sets, table->read_set, table->write_set);
quick->dbug_dump(0, TRUE);
+ dbug_tmp_restore_column_maps(table->read_set, table->write_set, old_sets);
+
fprintf(DBUG_FILE,"other_keys: 0x%s:\n", needed_reg->print(buf));
DBUG_UNLOCK_FILE;
@@ -9735,7 +11218,7 @@ void QUICK_RANGE_SELECT::dbug_dump(int indent, bool verbose)
range= *pr;
if (!(range->flag & NO_MIN_RANGE))
{
- print_key(key_parts,range->min_key,range->min_length);
+ print_key(key_parts, range->min_key, range->min_length);
if (range->flag & NEAR_MIN)
fputs(" < ",DBUG_FILE);
else
@@ -9749,7 +11232,7 @@ void QUICK_RANGE_SELECT::dbug_dump(int indent, bool verbose)
fputs(" < ",DBUG_FILE);
else
fputs(" <= ",DBUG_FILE);
- print_key(key_parts,range->max_key,range->max_length);
+ print_key(key_parts, range->max_key, range->max_length);
}
fputs("\n",DBUG_FILE);
}
@@ -9848,7 +11331,7 @@ void QUICK_GROUP_MIN_MAX_SELECT::dbug_dump(int indent, bool verbose)
#endif /* NOT_USED */
/*****************************************************************************
-** Instantiate templates
+** Instantiate templates
*****************************************************************************/
#ifdef HAVE_EXPLICIT_TEMPLATE_INSTANTIATION
diff --git a/sql/opt_range.h b/sql/opt_range.h
index d43f920be93..8d2ba1bb0a6 100644
--- a/sql/opt_range.h
+++ b/sql/opt_range.h
@@ -24,8 +24,15 @@
#endif
typedef struct st_key_part {
- uint16 key,part, store_length, length;
- uint8 null_bit, flag;
+ uint16 key,part;
+ /* See KEY_PART_INFO for meaning of the next two: */
+ uint16 store_length, length;
+ uint8 null_bit;
+ /*
+ Keypart flags (0 when this structure is used by partition pruning code
+ for fake partitioning index description)
+ */
+ uint8 flag;
Field *field;
Field::imagetype image_type;
} KEY_PART;
@@ -33,20 +40,26 @@ typedef struct st_key_part {
class QUICK_RANGE :public Sql_alloc {
public:
- char *min_key,*max_key;
+ uchar *min_key,*max_key;
uint16 min_length,max_length,flag;
+ key_part_map min_keypart_map, // bitmap of used keyparts in min_key
+ max_keypart_map; // bitmap of used keyparts in max_key
#ifdef HAVE_purify
uint16 dummy; /* Avoid warnings on 'flag' */
#endif
QUICK_RANGE(); /* Full range */
- QUICK_RANGE(const char *min_key_arg,uint min_length_arg,
- const char *max_key_arg,uint max_length_arg,
+ QUICK_RANGE(const uchar *min_key_arg, uint min_length_arg,
+ key_part_map min_keypart_map_arg,
+ const uchar *max_key_arg, uint max_length_arg,
+ key_part_map max_keypart_map_arg,
uint flag_arg)
- : min_key((char*) sql_memdup(min_key_arg,min_length_arg+1)),
- max_key((char*) sql_memdup(max_key_arg,max_length_arg+1)),
+ : min_key((uchar*) sql_memdup(min_key_arg,min_length_arg+1)),
+ max_key((uchar*) sql_memdup(max_key_arg,max_length_arg+1)),
min_length((uint16) min_length_arg),
max_length((uint16) max_length_arg),
- flag((uint16) flag_arg)
+ flag((uint16) flag_arg),
+ min_keypart_map(min_keypart_map_arg),
+ max_keypart_map(max_keypart_map_arg)
{
#ifdef HAVE_purify
dummy=0;
@@ -58,11 +71,11 @@ class QUICK_RANGE :public Sql_alloc {
/*
Quick select interface.
This class is a parent for all QUICK_*_SELECT and FT_SELECT classes.
-
+
The usage scenario is as follows:
1. Create quick select
quick= new QUICK_XXX_SELECT(...);
-
+
2. Perform lightweight initialization. This can be done in 2 ways:
2.a: Regular initialization
if (quick->init())
@@ -73,29 +86,29 @@ class QUICK_RANGE :public Sql_alloc {
2.b: Special initialization for quick selects merged by QUICK_ROR_*_SELECT
if (quick->init_ror_merged_scan())
delete quick;
-
+
3. Perform zero, one, or more scans.
while (...)
{
// initialize quick select for scan. This may allocate
- // buffers and/or prefetch rows.
+ // buffers and/or prefetch rows.
if (quick->reset())
{
//the only valid action after failed reset() call is delete
delete quick;
//abort query
}
-
+
// perform the scan
do
{
res= quick->get_next();
} while (res && ...)
}
-
+
4. Delete the select:
delete quick;
-
+
*/
class QUICK_SELECT_I
@@ -121,6 +134,8 @@ public:
Max. number of (first) key parts this quick select uses for retrieval.
eg. for "(key1p1=c1 AND key1p2=c2) OR key1p1=c2" used_key_parts == 2.
Applicable if index!= MAX_KEY.
+
+ For QUICK_GROUP_MIN_MAX_SELECT it includes MIN/MAX argument keyparts.
*/
uint used_key_parts;
@@ -191,8 +206,9 @@ public:
function is called.
SYNOPSIS
init_ror_merged_scan()
- reuse_handler If true, the quick select may use table->handler, otherwise
- it must create and use a separate handler object.
+ reuse_handler If true, the quick select may use table->handler,
+ otherwise it must create and use a separate handler
+ object.
RETURN
0 Ok
other Error
@@ -222,22 +238,20 @@ public:
virtual void add_info_string(String *str) {};
/*
Return 1 if any index used by this quick select
- a) uses field that is listed in passed field list or
- b) is automatically updated (like a timestamp)
- c) can be updated by one of before update triggers defined on table
+ uses field which is marked in passed bitmap.
*/
- virtual bool is_keys_used(List<Item> *fields);
+ virtual bool is_keys_used(const MY_BITMAP *fields);
/*
rowid of last row retrieved by this quick select. This is used only when
doing ROR-index_merge selects
*/
- byte *last_rowid;
+ uchar *last_rowid;
/*
Table record buffer used by this quick select.
*/
- byte *record;
+ uchar *record;
#ifndef DBUG_OFF
/*
Print quick select information to DBUG_FILE. Caller is responsible
@@ -249,6 +263,7 @@ public:
struct st_qsel_param;
+class PARAM;
class SEL_ARG;
/*
@@ -258,7 +273,7 @@ class SEL_ARG;
class QUICK_RANGE_SELECT : public QUICK_SELECT_I
{
protected:
- bool next,dont_free;
+ bool next,dont_free,in_ror_merged_scan;
public:
int error;
protected:
@@ -276,19 +291,19 @@ protected:
freed by QUICK_RANGE_SELECT) */
HANDLER_BUFFER *multi_range_buff; /* the handler buffer (allocated and
freed by QUICK_RANGE_SELECT) */
+ MY_BITMAP column_bitmap, *save_read_set, *save_write_set;
-protected:
friend class TRP_ROR_INTERSECT;
friend
QUICK_RANGE_SELECT *get_quick_select_for_ref(THD *thd, TABLE *table,
struct st_table_ref *ref,
ha_rows records);
- friend bool get_quick_keys(struct st_qsel_param *param,
+ friend bool get_quick_keys(PARAM *param,
QUICK_RANGE_SELECT *quick,KEY_PART *key,
SEL_ARG *key_tree,
- char *min_key, uint min_key_flag,
- char *max_key, uint max_key_flag);
- friend QUICK_RANGE_SELECT *get_quick_select(struct st_qsel_param*,uint idx,
+ uchar *min_key, uint min_key_flag,
+ uchar *max_key, uint max_key_flag);
+ friend QUICK_RANGE_SELECT *get_quick_select(PARAM*,uint idx,
SEL_ARG *key_tree,
MEM_ROOT *alloc);
friend class QUICK_SELECT_DESC;
@@ -316,7 +331,8 @@ public:
int reset(void);
int get_next();
void range_end();
- int get_next_prefix(uint prefix_length, byte *cur_prefix);
+ int get_next_prefix(uint prefix_length, key_part_map keypart_map,
+ uchar *cur_prefix);
bool reverse_sorted() { return 0; }
bool unique_key_range();
int init_ror_merged_scan(bool reuse_handler);
@@ -417,7 +433,7 @@ public:
int get_type() { return QS_TYPE_INDEX_MERGE; }
void add_keys_and_lengths(String *key_names, String *used_lengths);
void add_info_string(String *str);
- bool is_keys_used(List<Item> *fields);
+ bool is_keys_used(const MY_BITMAP *fields);
#ifndef DBUG_OFF
void dbug_dump(int indent, bool verbose);
#endif
@@ -476,7 +492,7 @@ public:
int get_type() { return QS_TYPE_ROR_INTERSECT; }
void add_keys_and_lengths(String *key_names, String *used_lengths);
void add_info_string(String *str);
- bool is_keys_used(List<Item> *fields);
+ bool is_keys_used(const MY_BITMAP *fields);
#ifndef DBUG_OFF
void dbug_dump(int indent, bool verbose);
#endif
@@ -530,7 +546,7 @@ public:
int get_type() { return QS_TYPE_ROR_UNION; }
void add_keys_and_lengths(String *key_names, String *used_lengths);
void add_info_string(String *str);
- bool is_keys_used(List<Item> *fields);
+ bool is_keys_used(const MY_BITMAP *fields);
#ifndef DBUG_OFF
void dbug_dump(int indent, bool verbose);
#endif
@@ -543,12 +559,12 @@ public:
MEM_ROOT alloc; /* Memory pool for this and merged quick selects data. */
THD *thd; /* current thread */
- byte *cur_rowid; /* buffer used in get_next() */
- byte *prev_rowid; /* rowid of last row returned by get_next() */
+ uchar *cur_rowid; /* buffer used in get_next() */
+ uchar *prev_rowid; /* rowid of last row returned by get_next() */
bool have_prev_rowid; /* true if prev_rowid has valid data */
uint rowid_length; /* table rowid length */
private:
- static int queue_cmp(void *arg, byte *val1, byte *val2);
+ static int queue_cmp(void *arg, uchar *val1, uchar *val2);
bool scans_inited;
};
@@ -592,21 +608,23 @@ private:
handler *file; /* The handler used to get data. */
JOIN *join; /* Descriptor of the current query */
KEY *index_info; /* The index chosen for data access */
- byte *record; /* Buffer where the next record is returned. */
- byte *tmp_record; /* Temporary storage for next_min(), next_max(). */
- byte *group_prefix; /* Key prefix consisting of the GROUP fields. */
+ uchar *record; /* Buffer where the next record is returned. */
+ uchar *tmp_record; /* Temporary storage for next_min(), next_max(). */
+ uchar *group_prefix; /* Key prefix consisting of the GROUP fields. */
uint group_prefix_len; /* Length of the group prefix. */
- byte *last_prefix; /* Prefix of the last group for detecting EOF. */
+ uint group_key_parts; /* A number of keyparts in the group prefix */
+ 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 seen_first_key; /* Denotes whether the first key was retrieved.*/
KEY_PART_INFO *min_max_arg_part; /* The keypart of the only argument field */
/* of all MIN/MAX functions. */
uint min_max_arg_len; /* The length of the MIN/MAX argument field */
- byte *key_infix; /* Infix of constants from equality predicates. */
+ uchar *key_infix; /* Infix of constants from equality predicates. */
uint key_infix_len;
DYNAMIC_ARRAY min_max_ranges; /* Array of range ptrs for the MIN/MAX field. */
uint real_prefix_len; /* Length of key prefix extended with key_infix. */
+ uint real_key_parts; /* A number of keyparts in the above value. */
List<Item_sum> *min_functions;
List<Item_sum> *max_functions;
List_iterator<Item_sum> *min_functions_it;
@@ -629,10 +647,11 @@ private:
public:
QUICK_GROUP_MIN_MAX_SELECT(TABLE *table, JOIN *join, bool have_min,
bool have_max, KEY_PART_INFO *min_max_arg_part,
- uint group_prefix_len, uint used_key_parts,
- KEY *index_info, uint use_index, double read_cost,
- ha_rows records, uint key_infix_len,
- byte *key_infix, MEM_ROOT *parent_alloc);
+ 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);
~QUICK_GROUP_MIN_MAX_SELECT();
bool add_range(SEL_ARG *sel_range);
void update_key_stat();
@@ -711,4 +730,9 @@ QUICK_RANGE_SELECT *get_quick_select_for_ref(THD *thd, TABLE *table,
ha_rows records);
uint get_index_for_order(TABLE *table, ORDER *order, ha_rows limit);
+#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
+
#endif
diff --git a/sql/opt_sum.cc b/sql/opt_sum.cc
index f8603f06fa0..3ccc1e5cf41 100644
--- a/sql/opt_sum.cc
+++ b/sql/opt_sum.cc
@@ -14,7 +14,9 @@
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
-/*
+/**
+ @file
+
Optimising of MIN(), MAX() and COUNT(*) queries without 'group by' clause
by replacing the aggregate expression with a constant.
@@ -22,6 +24,7 @@
types of queries are optimised (assuming the table handler supports
the required methods)
+ @verbatim
SELECT COUNT(*) FROM t1[,t2,t3,...]
SELECT MIN(b) FROM t1 WHERE a=const
SELECT MAX(c) FROM t1 WHERE a=const AND b=const
@@ -29,6 +32,7 @@
SELECT MIN(b) FROM t1 WHERE a=const AND b>const
SELECT MIN(b) FROM t1 WHERE a=const AND b BETWEEN const AND const
SELECT MAX(b) FROM t1 WHERE a=const AND b BETWEEN const AND const
+ @endverbatim
Instead of '<' one can use '<=', '>', '>=' and '=' as well.
Instead of 'a=const' the condition 'a IS NULL' can be used.
@@ -36,10 +40,11 @@
If all selected fields are replaced then we will also remove all
involved tables and return the answer without any join. Thus, the
following query will be replaced with a row of two constants:
+ @verbatim
SELECT MAX(b), MIN(d) FROM t1,t2
WHERE a=const AND b<const AND d>const
+ @endverbatim
(assuming a index for column d of table t2 is defined)
-
*/
#include "mysql_priv.h"
@@ -54,24 +59,54 @@ static int maxmin_in_range(bool max_fl, Field* field, COND *cond);
/*
- Substitutes constants for some COUNT(), MIN() and MAX() functions.
+ Get exact count of rows in all tables
SYNOPSIS
- opt_sum_query()
- tables list of leaves of join table tree
- all_fields All fields to be returned
- conds WHERE clause
+ get_exact_records()
+ tables List of tables
+
+ NOTES
+ When this is called, we know all table handlers supports HA_HAS_RECORDS
+ or HA_STATS_RECORDS_IS_EXACT
+
+ RETURN
+ ULONGLONG_MAX Error: Could not calculate number of rows
+ # Multiplication of number of rows in all tables
+*/
+
+static ulonglong get_exact_record_count(TABLE_LIST *tables)
+{
+ ulonglong count= 1;
+ for (TABLE_LIST *tl= tables; tl; tl= tl->next_leaf)
+ {
+ ha_rows tmp= tl->table->file->records();
+ if ((tmp == HA_POS_ERROR))
+ return ULONGLONG_MAX;
+ count*= tmp;
+ }
+ return count;
+}
+
+
+/**
+ Substitutes constants for some COUNT(), MIN() and MAX() functions.
- NOTE:
+ @param tables list of leaves of join table tree
+ @param all_fields All fields to be returned
+ @param conds WHERE clause
+
+ @note
This function is only called for queries with sum functions and no
GROUP BY part.
- RETURN VALUES
+ @retval
0 no errors
+ @retval
1 if all items were resolved
+ @retval
HA_ERR_KEY_NOT_FOUND on impossible conditions
- OR an error number from my_base.h HA_ERR_... if a deadlock or a lock
- wait timeout happens, for example
+ @retval
+ HA_ERR_... if a deadlock or a lock wait timeout happens, for example
*/
int opt_sum_query(TABLE_LIST *tables, List<Item> &all_fields,COND *conds)
@@ -79,8 +114,8 @@ int opt_sum_query(TABLE_LIST *tables, List<Item> &all_fields,COND *conds)
List_iterator_fast<Item> it(all_fields);
int const_result= 1;
bool recalc_const_item= 0;
- longlong count= 1;
- bool is_exact_count= TRUE;
+ ulonglong count= 1;
+ bool is_exact_count= TRUE, maybe_exact_count= TRUE;
table_map removed_tables= 0, outer_tables= 0, used_tables= 0;
table_map where_tables= 0;
Item *item;
@@ -119,15 +154,18 @@ int opt_sum_query(TABLE_LIST *tables, List<Item> &all_fields,COND *conds)
used_tables|= tl->table->map;
/*
- If the storage manager of 'tl' gives exact row count, compute the total
- number of rows. If there are no outer table dependencies, this count
- may be used as the real count.
+ If the storage manager of 'tl' gives exact row count as part of
+ statistics (cheap), compute the total number of rows. If there are
+ no outer table dependencies, this count may be used as the real count.
Schema tables are filled after this function is invoked, so we can't
get row count
*/
- if ((tl->table->file->table_flags() & HA_NOT_EXACT_COUNT) ||
+ if (!(tl->table->file->ha_table_flags() & HA_STATS_RECORDS_IS_EXACT) ||
tl->schema_table)
{
+ maybe_exact_count&= test(!tl->schema_table &&
+ (tl->table->file->ha_table_flags() &
+ HA_HAS_RECORDS));
is_exact_count= FALSE;
count= 1; // ensure count != 0
}
@@ -137,9 +175,10 @@ int opt_sum_query(TABLE_LIST *tables, List<Item> &all_fields,COND *conds)
if(error)
{
tl->table->file->print_error(error, MYF(0));
+ tl->table->in_use->fatal_error();
return error;
}
- count*= tl->table->file->records;
+ count*= tl->table->file->stats.records;
}
}
@@ -161,9 +200,19 @@ int opt_sum_query(TABLE_LIST *tables, List<Item> &all_fields,COND *conds)
there are no outer joins.
*/
if (!conds && !((Item_sum_count*) item)->get_arg(0)->maybe_null &&
- !outer_tables && is_exact_count)
+ !outer_tables && maybe_exact_count)
{
- ((Item_sum_count*) item)->make_const(count);
+ if (!is_exact_count)
+ {
+ if ((count= get_exact_record_count(tables)) == ULONGLONG_MAX)
+ {
+ /* Error from handler in counting rows. Don't optimize count() */
+ const_result= 0;
+ continue;
+ }
+ is_exact_count= 1; // count is now exact
+ }
+ ((Item_sum_count*) item)->make_const((longlong) count);
recalc_const_item= 1;
}
else
@@ -179,7 +228,7 @@ int opt_sum_query(TABLE_LIST *tables, List<Item> &all_fields,COND *conds)
Item *expr=item_sum->get_arg(0);
if (expr->real_item()->type() == Item::FIELD_ITEM)
{
- byte key_buff[MAX_KEY_LENGTH];
+ uchar key_buff[MAX_KEY_LENGTH];
TABLE_REF ref;
uint range_fl, prefix_len;
@@ -202,7 +251,7 @@ int opt_sum_query(TABLE_LIST *tables, List<Item> &all_fields,COND *conds)
const_result= 0;
break;
}
- error= table->file->ha_index_init((uint) ref.key);
+ error= table->file->ha_index_init((uint) ref.key, 1);
if (!ref.key_length)
error= table->file->index_first(table->record[0]);
@@ -227,9 +276,10 @@ int opt_sum_query(TABLE_LIST *tables, List<Item> &all_fields,COND *conds)
Closed interval: Either The MIN argument is non-nullable, or
we have a >= predicate for the MIN argument.
*/
- error= table->file->index_read(table->record[0], ref.key_buff,
- ref.key_length,
- HA_READ_KEY_OR_NEXT);
+ error= table->file->index_read_map(table->record[0],
+ ref.key_buff,
+ make_prev_keypart_map(ref.key_parts),
+ HA_READ_KEY_OR_NEXT);
else
{
/*
@@ -238,8 +288,10 @@ int opt_sum_query(TABLE_LIST *tables, List<Item> &all_fields,COND *conds)
2) there is a > predicate on it, nullability is irrelevant.
We need to scan the next bigger record first.
*/
- error= table->file->index_read(table->record[0], ref.key_buff,
- ref.key_length, HA_READ_AFTER_KEY);
+ error= table->file->index_read_map(table->record[0],
+ ref.key_buff,
+ make_prev_keypart_map(ref.key_parts),
+ HA_READ_AFTER_KEY);
/*
If the found record is outside the group formed by the search
prefix, or there is no such record at all, check if all
@@ -261,9 +313,11 @@ int opt_sum_query(TABLE_LIST *tables, List<Item> &all_fields,COND *conds)
(error == HA_ERR_KEY_NOT_FOUND ||
key_cmp_if_same(table, ref.key_buff, ref.key, prefix_len)))
{
- error= table->file->index_read(table->record[0], ref.key_buff,
- ref.key_length,
- HA_READ_KEY_EXACT);
+ DBUG_ASSERT(item_field->field->real_maybe_null());
+ error= table->file->index_read_map(table->record[0],
+ ref.key_buff,
+ make_prev_keypart_map(ref.key_parts),
+ HA_READ_KEY_EXACT);
}
}
}
@@ -322,7 +376,7 @@ int opt_sum_query(TABLE_LIST *tables, List<Item> &all_fields,COND *conds)
Item *expr=item_sum->get_arg(0);
if (expr->real_item()->type() == Item::FIELD_ITEM)
{
- byte key_buff[MAX_KEY_LENGTH];
+ uchar key_buff[MAX_KEY_LENGTH];
TABLE_REF ref;
uint range_fl, prefix_len;
@@ -345,17 +399,17 @@ int opt_sum_query(TABLE_LIST *tables, List<Item> &all_fields,COND *conds)
const_result= 0;
break;
}
- error= table->file->ha_index_init((uint) ref.key);
+ error= table->file->ha_index_init((uint) ref.key, 1);
if (!ref.key_length)
error= table->file->index_last(table->record[0]);
else
- error= table->file->index_read(table->record[0], key_buff,
- ref.key_length,
- range_fl & NEAR_MAX ?
- HA_READ_BEFORE_KEY :
- HA_READ_PREFIX_LAST_OR_PREV);
- if (!error && reckey_in_range(1, &ref, item_field->field,
+ error= table->file->index_read_map(table->record[0], key_buff,
+ make_prev_keypart_map(ref.key_parts),
+ range_fl & NEAR_MAX ?
+ HA_READ_BEFORE_KEY :
+ HA_READ_PREFIX_LAST_OR_PREV);
+ if (!error && reckey_in_range(1, &ref, item_field->field,
conds, range_fl, prefix_len))
error= HA_ERR_KEY_NOT_FOUND;
if (table->key_read)
@@ -370,6 +424,7 @@ int opt_sum_query(TABLE_LIST *tables, List<Item> &all_fields,COND *conds)
return HA_ERR_KEY_NOT_FOUND; // No rows matching WHERE
/* HA_ERR_LOCK_DEADLOCK or some other error */
table->file->print_error(error, MYF(0));
+ table->in_use->fatal_error();
return(error);
}
removed_tables|= table->map;
@@ -425,19 +480,18 @@ int opt_sum_query(TABLE_LIST *tables, List<Item> &all_fields,COND *conds)
}
-/*
- Test if the predicate compares a field with constants
+/**
+ Test if the predicate compares a field with constants.
- SYNOPSIS
- simple_pred()
- func_item Predicate item
- args out: Here we store the field followed by constants
- inv_order out: Is set to 1 if the predicate is of the form
- 'const op field'
+ @param func_item Predicate item
+ @param[out] args Here we store the field followed by constants
+ @param[out] inv_order Is set to 1 if the predicate is of the form
+ 'const op field'
- RETURN
+ @retval
0 func_item is a simple predicate: a field is compared with
- constants
+ constants
+ @retval
1 Otherwise
*/
@@ -509,34 +563,33 @@ bool simple_pred(Item_func *func_item, Item **args, bool *inv_order)
}
-/*
- Check whether a condition matches a key to get {MAX|MIN}(field):
+/**
+ Check whether a condition matches a key to get {MAX|MIN}(field):.
- SYNOPSIS
- matching_cond()
- max_fl in: Set to 1 if we are optimising MAX()
- ref in/out: Reference to the structure we store the key value
- keyinfo in Reference to the key info
- field_part in: Pointer to the key part for the field
- cond in WHERE condition
- key_part_used in/out: Map of matchings parts
- range_fl in/out: Says whether including key will be used
- prefix_len out: Length of common key part for the range
- where MAX/MIN is searched for
-
- DESCRIPTION
For the index specified by the keyinfo parameter, index that
contains field as its component (field_part), the function
checks whether the condition cond is a conjunction and all its
conjuncts referring to the columns of the same table as column
field are one of the following forms:
- f_i= const_i or const_i= f_i or f_i is null,
- where f_i is part of the index
+ where f_i is part of the index
- field {<|<=|>=|>|=} const or const {<|<=|>=|>|=} field
- field between const1 and const2
- RETURN
+ @param[in] max_fl Set to 1 if we are optimising MAX()
+ @param[in,out] ref Reference to the structure we store the key
+ value
+ @param[in] keyinfo Reference to the key info
+ @param[in] field_part Pointer to the key part for the field
+ @param[in] cond WHERE condition
+ @param[in,out] key_part_used Map of matchings parts
+ @param[in,out] range_fl Says whether including key will be used
+ @param[out] prefix_len Length of common key part for the range
+ where MAX/MIN is searched for
+
+ @retval
0 Index can't be used.
+ @retval
1 We can use index to get MIN/MAX value
*/
@@ -616,17 +669,15 @@ static bool matching_cond(bool max_fl, TABLE_REF *ref, KEY *keyinfo,
less_fl= 1-less_fl; // Convert '<' -> '>' (etc)
/* Check if field is part of the tested partial key */
- byte *key_ptr= ref->key_buff;
+ uchar *key_ptr= ref->key_buff;
KEY_PART_INFO *part;
- for (part= keyinfo->key_part;
- ;
- key_ptr+= part++->store_length)
+ for (part= keyinfo->key_part; ; key_ptr+= part++->store_length)
{
if (part > field_part)
return 0; // Field is beyond the tested parts
if (part->field->eq(((Item_field*) args[0])->field))
- break; // Found a part od the key for the field
+ break; // Found a part of the key for the field
}
bool is_field_part= part == field_part;
@@ -636,12 +687,15 @@ static bool matching_cond(bool max_fl, TABLE_REF *ref, KEY *keyinfo,
key_part_map org_key_part_used= *key_part_used;
if (eq_type || between || max_fl == less_fl)
{
- size_t length= (key_ptr-ref->key_buff)+part->store_length;
+ uint length= (key_ptr-ref->key_buff)+part->store_length;
if (ref->key_length < length)
+ {
/* Ultimately ref->key_length will contain the length of the search key */
- ref->key_length= (uint) length;
+ ref->key_length= length;
+ ref->key_parts= (part - keyinfo->key_part) + 1;
+ }
if (!*prefix_len && part+1 == field_part)
- *prefix_len= (uint) length;
+ *prefix_len= length;
if (is_field_part && eq_type)
*prefix_len= ref->key_length;
@@ -664,15 +718,15 @@ static bool matching_cond(bool max_fl, TABLE_REF *ref, KEY *keyinfo,
if (is_null)
{
part->field->set_null();
- *key_ptr= (byte) 1;
+ *key_ptr= (uchar) 1;
}
else
{
store_val_in_field(part->field, args[between && max_fl ? 2 : 1],
CHECK_FIELD_IGNORE);
if (part->null_bit)
- *key_ptr++= (byte) test(part->field->is_null());
- part->field->get_key_image((char*) key_ptr, part->length, Field::itRAW);
+ *key_ptr++= (uchar) test(part->field->is_null());
+ part->field->get_key_image(key_ptr, part->length, Field::itRAW);
}
if (is_field_part)
{
@@ -700,31 +754,21 @@ static bool matching_cond(bool max_fl, TABLE_REF *ref, KEY *keyinfo,
}
-/*
+/**
Check whether we can get value for {max|min}(field) by using a key.
- SYNOPSIS
- find_key_for_maxmin()
- max_fl in: 0 for MIN(field) / 1 for MAX(field)
- ref in/out Reference to the structure we store the key value
- field in: Field used inside MIN() / MAX()
- cond in: WHERE condition
- range_fl out: Bit flags for how to search if key is ok
- prefix_len out: Length of prefix for the search range
-
- DESCRIPTION
- If where condition is not a conjunction of 0 or more conjuct the
- function returns false, otherwise it checks whether there is an
- index including field as its k-th component/part such that:
-
- 1. for each previous component f_i there is one and only one conjunct
+ If where-condition is not a conjunction of 0 or more conjuct the
+ function returns false, otherwise it checks whether there is an
+ index including field as its k-th component/part such that:
+
+ -# for each previous component f_i there is one and only one conjunct
of the form: f_i= const_i or const_i= f_i or f_i is null
- 2. references to field occur only in conjucts of the form:
+ -# references to field occur only in conjucts of the form:
field {<|<=|>=|>|=} const or const {<|<=|>=|>|=} field or
field BETWEEN const1 AND const2
- 3. all references to the columns from the same table as column field
+ -# all references to the columns from the same table as column field
occur only in conjucts mentioned above.
- 4. each of k first components the index is not partial, i.e. is not
+ -# each of k first components the index is not partial, i.e. is not
defined on a fixed length proper prefix of the field.
If such an index exists the function through the ref parameter
@@ -732,17 +776,26 @@ static bool matching_cond(bool max_fl, TABLE_REF *ref, KEY *keyinfo,
the length of first (k-1) components of the key and flags saying
how to apply the key for the search max/min value.
(if we have a condition field = const, prefix_len contains the length
- of the whole search key)
+ of the whole search key)
+
+ @param[in] max_fl 0 for MIN(field) / 1 for MAX(field)
+ @param[in,out] ref Reference to the structure we store the key value
+ @param[in] field Field used inside MIN() / MAX()
+ @param[in] cond WHERE condition
+ @param[out] range_fl Bit flags for how to search if key is ok
+ @param[out] prefix_len Length of prefix for the search range
- NOTE
+ @note
This function may set table->key_read to 1, which must be reset after
index is used! (This can only happen when function returns 1)
- RETURN
+ @retval
0 Index can not be used to optimize MIN(field)/MAX(field)
- 1 Can use key to optimize MIN()/MAX()
- In this case ref, range_fl and prefix_len are updated
-*/
+ @retval
+ 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,
@@ -786,6 +839,7 @@ static bool find_key_for_maxmin(bool max_fl, TABLE_REF *ref,
{
ref->key= idx;
ref->key_length= 0;
+ ref->key_parts= 0;
key_part_map key_part_used= 0;
*range_fl= NO_MIN_RANGE | NO_MAX_RANGE;
if (matching_cond(max_fl, ref, keyinfo, part, cond,
@@ -811,6 +865,8 @@ static bool find_key_for_maxmin(bool max_fl, TABLE_REF *ref,
/* Set the first byte of key_part_k to 1, that means NULL */
ref->key_buff[ref->key_length]= 1;
ref->key_length+= part->store_length;
+ ref->key_parts++;
+ DBUG_ASSERT(ref->key_parts == jdx+1);
*range_fl&= ~NO_MIN_RANGE;
*range_fl|= NEAR_MIN; // Open interval
}
@@ -832,20 +888,19 @@ static bool find_key_for_maxmin(bool max_fl, TABLE_REF *ref,
}
-/*
- Check whether found key is in range specified by conditions
+/**
+ Check whether found key is in range specified by conditions.
- SYNOPSIS
- reckey_in_range()
- max_fl in: 0 for MIN(field) / 1 for MAX(field)
- ref in: Reference to the key value and info
- field in: Field used the MIN/MAX expression
- cond in: WHERE condition
- range_fl in: Says whether there is a condition to to be checked
- prefix_len in: Length of the constant part of the key
+ @param[in] max_fl 0 for MIN(field) / 1 for MAX(field)
+ @param[in] ref Reference to the key value and info
+ @param[in] field Field used the MIN/MAX expression
+ @param[in] cond WHERE condition
+ @param[in] range_fl Says whether there is a condition to to be checked
+ @param[in] prefix_len Length of the constant part of the key
- RETURN
+ @retval
0 ok
+ @retval
1 WHERE was not true for the found row
*/
@@ -860,16 +915,16 @@ static int reckey_in_range(bool max_fl, TABLE_REF *ref, Field* field,
}
-/*
- Check whether {MAX|MIN}(field) is in range specified by conditions
- SYNOPSIS
- maxmin_in_range()
- max_fl in: 0 for MIN(field) / 1 for MAX(field)
- field in: Field used the MIN/MAX expression
- cond in: WHERE condition
+/**
+ Check whether {MAX|MIN}(field) is in range specified by conditions.
- RETURN
+ @param[in] max_fl 0 for MIN(field) / 1 for MAX(field)
+ @param[in] field Field used the MIN/MAX expression
+ @param[in] cond WHERE condition
+
+ @retval
0 ok
+ @retval
1 WHERE was not true for the found row
*/
diff --git a/sql/parse_file.cc b/sql/parse_file.cc
index 03e0d25b885..07ea434e8e0 100644
--- a/sql/parse_file.cc
+++ b/sql/parse_file.cc
@@ -13,7 +13,12 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
-// Text .frm files management routines
+/**
+ @file
+
+ @brief
+ Text .frm files management routines
+*/
#include "mysql_priv.h"
#include <errno.h>
@@ -25,17 +30,16 @@
extern long mysql_rm_arc_files(THD *thd, MY_DIR *dirp, const char *org_path);
-/*
- write string with escaping
+/**
+ Write string with escaping.
- SYNOPSIS
- write_escaped_string()
- file - IO_CACHE for record
- val_s - string for writing
+ @param file IO_CACHE for record
+ @param val_s string for writing
- RETURN
- FALSE - OK
- TRUE - error
+ @retval
+ FALSE OK
+ @retval
+ TRUE error
*/
static my_bool
@@ -52,27 +56,27 @@ write_escaped_string(IO_CACHE *file, LEX_STRING *val_s)
*/
switch(*ptr) {
case '\\': // escape character
- if (my_b_append(file, (const byte *)STRING_WITH_LEN("\\\\")))
+ if (my_b_append(file, (const uchar *)STRING_WITH_LEN("\\\\")))
return TRUE;
break;
case '\n': // parameter value delimiter
- if (my_b_append(file, (const byte *)STRING_WITH_LEN("\\n")))
+ if (my_b_append(file, (const uchar *)STRING_WITH_LEN("\\n")))
return TRUE;
break;
case '\0': // problem for some string processing utilities
- if (my_b_append(file, (const byte *)STRING_WITH_LEN("\\0")))
+ if (my_b_append(file, (const uchar *)STRING_WITH_LEN("\\0")))
return TRUE;
break;
case 26: // problem for windows utilities (Ctrl-Z)
- if (my_b_append(file, (const byte *)STRING_WITH_LEN("\\z")))
+ if (my_b_append(file, (const uchar *)STRING_WITH_LEN("\\z")))
return TRUE;
break;
case '\'': // list of string delimiter
- if (my_b_append(file, (const byte *)STRING_WITH_LEN("\\\'")))
+ if (my_b_append(file, (const uchar *)STRING_WITH_LEN("\\\'")))
return TRUE;
break;
default:
- if (my_b_append(file, (const byte *)ptr, 1))
+ if (my_b_append(file, (const uchar *)ptr, 1))
return TRUE;
}
}
@@ -80,22 +84,22 @@ write_escaped_string(IO_CACHE *file, LEX_STRING *val_s)
}
-/*
- write parameter value to IO_CACHE
+/**
+ Write parameter value to IO_CACHE.
- SYNOPSIS
- write_parameter()
- file pointer to IO_CACHE structure for writing
- base pointer to data structure
- parameter pointer to parameter descriptor
+ @param file pointer to IO_CACHE structure for writing
+ @param base pointer to data structure
+ @param parameter pointer to parameter descriptor
- RETURN
- FALSE - OK
- TRUE - error
+ @retval
+ FALSE OK
+ @retval
+ TRUE error
*/
+
static my_bool
-write_parameter(IO_CACHE *file, gptr base, File_option *parameter)
+write_parameter(IO_CACHE *file, uchar* base, File_option *parameter)
{
char num_buf[20]; // buffer for numeric operations
// string for numeric operations
@@ -106,7 +110,7 @@ write_parameter(IO_CACHE *file, gptr base, File_option *parameter)
case FILE_OPTIONS_STRING:
{
LEX_STRING *val_s= (LEX_STRING *)(base + parameter->offset);
- if (my_b_append(file, (const byte *)val_s->str, val_s->length))
+ if (my_b_append(file, (const uchar *)val_s->str, val_s->length))
DBUG_RETURN(TRUE);
break;
}
@@ -119,7 +123,7 @@ write_parameter(IO_CACHE *file, gptr base, File_option *parameter)
case FILE_OPTIONS_ULONGLONG:
{
num.set(*((ulonglong *)(base + parameter->offset)), &my_charset_bin);
- if (my_b_append(file, (const byte *)num.ptr(), num.length()))
+ if (my_b_append(file, (const uchar *)num.ptr(), num.length()))
DBUG_RETURN(TRUE);
break;
}
@@ -127,12 +131,12 @@ write_parameter(IO_CACHE *file, gptr base, File_option *parameter)
{
/* string have to be allocated already */
LEX_STRING *val_s= (LEX_STRING *)(base + parameter->offset);
- time_t tm= time(NULL);
+ time_t tm= my_time(0);
get_date(val_s->str, GETDATE_DATE_TIME|GETDATE_GMT|GETDATE_FIXEDLENGTH,
tm);
val_s->length= PARSE_FILE_TIMESTAMPLENGTH;
- if (my_b_append(file, (const byte *)val_s->str,
+ if (my_b_append(file, (const uchar *)val_s->str,
PARSE_FILE_TIMESTAMPLENGTH))
DBUG_RETURN(TRUE);
break;
@@ -146,10 +150,10 @@ write_parameter(IO_CACHE *file, gptr base, File_option *parameter)
while ((str= it++))
{
// We need ' ' after string to detect list continuation
- if ((!first && my_b_append(file, (const byte *)STRING_WITH_LEN(" "))) ||
- my_b_append(file, (const byte *)STRING_WITH_LEN("\'")) ||
+ if ((!first && my_b_append(file, (const uchar *)STRING_WITH_LEN(" "))) ||
+ my_b_append(file, (const uchar *)STRING_WITH_LEN("\'")) ||
write_escaped_string(file, str) ||
- my_b_append(file, (const byte *)STRING_WITH_LEN("\'")))
+ my_b_append(file, (const uchar *)STRING_WITH_LEN("\'")))
{
DBUG_RETURN(TRUE);
}
@@ -167,8 +171,8 @@ write_parameter(IO_CACHE *file, gptr base, File_option *parameter)
{
num.set(*val, &my_charset_bin);
// We need ' ' after string to detect list continuation
- if ((!first && my_b_append(file, (const byte *)STRING_WITH_LEN(" "))) ||
- my_b_append(file, (const byte *)num.ptr(), num.length()))
+ if ((!first && my_b_append(file, (const uchar *)STRING_WITH_LEN(" "))) ||
+ my_b_append(file, (const uchar *)num.ptr(), num.length()))
{
DBUG_RETURN(TRUE);
}
@@ -183,27 +187,27 @@ write_parameter(IO_CACHE *file, gptr base, File_option *parameter)
}
-/*
- write new .frm
+/**
+ Write new .frm.
- SYNOPSIS
- sql_create_definition_file()
- dir directory where put .frm
- file .frm file name
- type .frm type string (VIEW, TABLE)
- base base address for parameter reading (structure like
- TABLE)
- parameters parameters description
+ @param dir directory where put .frm
+ @param file_name .frm file name
+ @param type .frm type string (VIEW, TABLE)
+ @param base base address for parameter reading (structure like
+ TABLE)
+ @param parameters parameters description
- RETURN
- FALSE - OK
- TRUE - error
+ @retval
+ FALSE OK
+ @retval
+ TRUE error
*/
+
my_bool
sql_create_definition_file(const LEX_STRING *dir, const LEX_STRING *file_name,
const LEX_STRING *type,
- gptr base, File_option *parameters)
+ uchar* base, File_option *parameters)
{
File handler;
IO_CACHE file;
@@ -212,10 +216,23 @@ sql_create_definition_file(const LEX_STRING *dir, const LEX_STRING *file_name,
File_option *param;
DBUG_ENTER("sql_create_definition_file");
DBUG_PRINT("enter", ("Dir: %s, file: %s, base 0x%lx",
- dir->str, file_name->str, (ulong) base));
+ dir ? dir->str : "(null)",
+ file_name->str, (ulong) base));
- fn_format(path, file_name->str, dir->str, 0, MY_UNPACK_FILENAME);
- path_end= (uint) strlen(path);
+ if (dir)
+ {
+ fn_format(path, file_name->str, dir->str, "", MY_UNPACK_FILENAME);
+ path_end= strlen(path);
+ }
+ else
+ {
+ /*
+ if not dir is passed, it means file_name is a full path,
+ including dir name, file name itself, and an extension,
+ and with unpack_filename() executed over it.
+ */
+ path_end= strxnmov(path, FN_REFLEN, file_name->str, NullS) - path;
+ }
// temporary file name
path[path_end]='~';
@@ -230,19 +247,19 @@ sql_create_definition_file(const LEX_STRING *dir, const LEX_STRING *file_name,
goto err_w_file;
// write header (file signature)
- if (my_b_append(&file, (const byte *)STRING_WITH_LEN("TYPE=")) ||
- my_b_append(&file, (const byte *)type->str, type->length) ||
- my_b_append(&file, (const byte *)STRING_WITH_LEN("\n")))
+ if (my_b_append(&file, (const uchar *)STRING_WITH_LEN("TYPE=")) ||
+ my_b_append(&file, (const uchar *)type->str, type->length) ||
+ my_b_append(&file, (const uchar *)STRING_WITH_LEN("\n")))
goto err_w_file;
// write parameters to temporary file
for (param= parameters; param->name.str; param++)
{
- if (my_b_append(&file, (const byte *)param->name.str,
+ if (my_b_append(&file, (const uchar *)param->name.str,
param->name.length) ||
- my_b_append(&file, (const byte *)STRING_WITH_LEN("=")) ||
+ my_b_append(&file, (const uchar *)STRING_WITH_LEN("=")) ||
write_parameter(&file, base, param) ||
- my_b_append(&file, (const byte *)STRING_WITH_LEN("\n")))
+ my_b_append(&file, (const uchar *)STRING_WITH_LEN("\n")))
goto err_w_cache;
}
@@ -279,20 +296,18 @@ err_w_file:
DBUG_RETURN(TRUE);
}
-/*
- Renames a frm file (including backups) in same schema
+/**
+ Renames a frm file (including backups) in same schema.
- SYNOPSIS
- rename_in_schema_file
- thd thread handler
- schema name of given schema
- old_name original file name
- new_name new file name
-
- RETURN
- 0 - OK
- 1 - Error (only if renaming of frm failed)
+ @thd thread handler
+ @param schema name of given schema
+ @param old_name original file name
+ @param new_name new file name
+ @retval
+ 0 OK
+ @retval
+ 1 Error (only if renaming of frm failed)
*/
my_bool rename_in_schema_file(THD *thd,
const char *schema, const char *old_name,
@@ -300,20 +315,16 @@ my_bool rename_in_schema_file(THD *thd,
{
char old_path[FN_REFLEN], new_path[FN_REFLEN], arc_path[FN_REFLEN];
- strxnmov(old_path, FN_REFLEN, mysql_data_home, "/", schema, "/",
- old_name, reg_ext, NullS);
- (void) unpack_filename(old_path, old_path);
-
- strxnmov(new_path, FN_REFLEN, mysql_data_home, "/", schema, "/",
- new_name, reg_ext, NullS);
- (void) unpack_filename(new_path, new_path);
+ build_table_filename(old_path, sizeof(old_path) - 1,
+ schema, old_name, reg_ext, 0);
+ build_table_filename(new_path, sizeof(new_path) - 1,
+ schema, new_name, reg_ext, 0);
if (my_rename(old_path, new_path, MYF(MY_WME)))
return 1;
/* check if arc_dir exists: disabled unused feature (see bug #17823). */
- strxnmov(arc_path, FN_REFLEN, mysql_data_home, "/", schema, "/arc", NullS);
- (void) unpack_filename(arc_path, arc_path);
+ build_table_filename(arc_path, sizeof(arc_path) - 1, schema, "arc", "", 0);
{ // remove obsolete 'arc' directory and files if any
MY_DIR *new_dirp;
@@ -326,21 +337,20 @@ my_bool rename_in_schema_file(THD *thd,
return 0;
}
-/*
- Prepare frm to parse (read to memory)
+/**
+ Prepare frm to parse (read to memory).
- SYNOPSIS
- sql_parse_prepare()
- file_name - path & filename to .frm file
- mem_root - MEM_ROOT for buffer allocation
- bad_format_errors - send errors on bad content
+ @param file_name path & filename to .frm file
+ @param mem_root MEM_ROOT for buffer allocation
+ @param bad_format_errors send errors on bad content
- RETURN
+ @note
+ returned pointer + 1 will be type of .frm
+
+ @return
0 - error
+ @return
parser object
-
- NOTE
- returned pointer + 1 will be type of .frm
*/
File_parser *
@@ -348,11 +358,11 @@ sql_parse_prepare(const LEX_STRING *file_name, MEM_ROOT *mem_root,
bool bad_format_errors)
{
MY_STAT stat_info;
- uint len;
+ size_t len;
char *end, *sign;
File_parser *parser;
File file;
- DBUG_ENTER("sql__parse_prepare");
+ DBUG_ENTER("sql_parse_prepare");
if (!my_stat(file_name->str, &stat_info, MYF(MY_WME)))
{
@@ -370,7 +380,7 @@ sql_parse_prepare(const LEX_STRING *file_name, MEM_ROOT *mem_root,
DBUG_RETURN(0);
}
- if (!(parser->buff= alloc_root(mem_root, stat_info.st_size+1)))
+ if (!(parser->buff= (char*) alloc_root(mem_root, stat_info.st_size+1)))
{
DBUG_RETURN(0);
}
@@ -380,7 +390,7 @@ sql_parse_prepare(const LEX_STRING *file_name, MEM_ROOT *mem_root,
DBUG_RETURN(0);
}
- if ((len= my_read(file, (byte *)parser->buff,
+ if ((len= my_read(file, (uchar *)parser->buff,
stat_info.st_size, MYF(MY_WME))) ==
MY_FILE_ERROR)
{
@@ -411,7 +421,7 @@ sql_parse_prepare(const LEX_STRING *file_name, MEM_ROOT *mem_root,
sign++;
if (*sign != '\n')
goto frm_error;
- parser->file_type.length= (uint) (sign - parser->file_type.str);
+ parser->file_type.length= sign - parser->file_type.str;
// EOS for file signature just for safety
*sign= '\0';
@@ -431,22 +441,22 @@ frm_error:
}
-/*
- parse LEX_STRING
+/**
+ parse LEX_STRING.
- SYNOPSIS
- parse_string()
- ptr - pointer on string beginning
- end - pointer on symbol after parsed string end (still owned
- by buffer and can be accessed
- mem_root - MEM_ROOT for parameter allocation
- str - pointer on string, where results should be stored
+ @param ptr pointer on string beginning
+ @param end pointer on symbol after parsed string end (still owned
+ by buffer and can be accessed
+ @param mem_root MEM_ROOT for parameter allocation
+ @param str pointer on string, where results should be stored
- RETURN
- 0 - error
- # - pointer on symbol after string
+ @retval
+ 0 error
+ @retval
+ \# pointer on symbol after string
*/
+
static char *
parse_string(char *ptr, char *end, MEM_ROOT *mem_root, LEX_STRING *str)
{
@@ -456,29 +466,25 @@ parse_string(char *ptr, char *end, MEM_ROOT *mem_root, LEX_STRING *str)
if (eol >= end)
return 0;
- str->length= (uint) (eol - ptr);
+ str->length= eol - ptr;
- if (!(str->str= alloc_root(mem_root, str->length+1)))
+ if (!(str->str= strmake_root(mem_root, ptr, str->length)))
return 0;
-
- memcpy(str->str, ptr, str->length);
- str->str[str->length]= '\0'; // just for safety
return eol+1;
}
-/*
- read escaped string from ptr to eol in already allocated str
+/**
+ read escaped string from ptr to eol in already allocated str.
- SYNOPSIS
- read_escaped_string()
- ptr - pointer on string beginning
- eol - pointer on character after end of string
- str - target string
+ @param ptr pointer on string beginning
+ @param eol pointer on character after end of string
+ @param str target string
- RETURN
- FALSE - OK
- TRUE - error
+ @retval
+ FALSE OK
+ @retval
+ TRUE error
*/
my_bool
@@ -521,34 +527,34 @@ read_escaped_string(char *ptr, char *eol, LEX_STRING *str)
else
*write_pos= c;
}
- str->str[str->length= (uint) (write_pos - str->str)]= '\0'; // just for safety
+ str->str[str->length= write_pos-str->str]= '\0'; // just for safety
return FALSE;
}
-/*
- parse \n delimited escaped string
+/**
+ parse \\n delimited escaped string.
- SYNOPSIS
- parse_escaped_string()
- ptr - pointer on string beginning
- end - pointer on symbol after parsed string end (still owned
- by buffer and can be accessed
- mem_root - MEM_ROOT for parameter allocation
- str - pointer on string, where results should be stored
+ @param ptr pointer on string beginning
+ @param end pointer on symbol after parsed string end (still owned
+ by buffer and can be accessed
+ @param mem_root MEM_ROOT for parameter allocation
+ @param str pointer on string, where results should be stored
- RETURN
- 0 - error
- # - pointer on symbol after string
+ @retval
+ 0 error
+ @retval
+ \# pointer on symbol after string
*/
+
char *
parse_escaped_string(char *ptr, char *end, MEM_ROOT *mem_root, LEX_STRING *str)
{
char *eol= strchr(ptr, '\n');
if (eol == 0 || eol >= end ||
- !(str->str= alloc_root(mem_root, (uint) ((eol - ptr) + 1))) ||
+ !(str->str= (char*) alloc_root(mem_root, (eol - ptr) + 1)) ||
read_escaped_string(ptr, eol, str))
return 0;
@@ -556,20 +562,19 @@ parse_escaped_string(char *ptr, char *end, MEM_ROOT *mem_root, LEX_STRING *str)
}
-/*
- parse '' delimited escaped string
+/**
+ parse '' delimited escaped string.
- SYNOPSIS
- parse_quoted_escaped_string()
- ptr - pointer on string beginning
- end - pointer on symbol after parsed string end (still owned
- by buffer and can be accessed
- mem_root - MEM_ROOT for parameter allocation
- str - pointer on string, where results should be stored
+ @param ptr pointer on string beginning
+ @param end pointer on symbol after parsed string end (still owned
+ by buffer and can be accessed
+ @param mem_root MEM_ROOT for parameter allocation
+ @param str pointer on string, where results should be stored
- RETURN
- 0 - error
- # - pointer on symbol after string
+ @retval
+ 0 error
+ @retval
+ \# pointer on symbol after string
*/
static char *
@@ -593,7 +598,7 @@ parse_quoted_escaped_string(char *ptr, char *end,
// process string
if (eol >= end ||
- !(str->str= alloc_root(mem_root, result_len + 1)) ||
+ !(str->str= (char*) alloc_root(mem_root, result_len + 1)) ||
read_escaped_string(ptr, eol, str))
return 0;
@@ -601,22 +606,20 @@ parse_quoted_escaped_string(char *ptr, char *end,
}
-/*
+/**
Parser for FILE_OPTIONS_ULLLIST type value.
- SYNOPSIS
- get_file_options_ulllist()
- ptr [in/out] pointer to parameter
- end [in] end of the configuration
- line [in] pointer to the line begining
- base [in] base address for parameter writing (structure
- like TABLE)
- parameter [in] description
- mem_root [in] MEM_ROOT for parameters allocation
+ @param[in,out] ptr pointer to parameter
+ @param[in] end end of the configuration
+ @param[in] line pointer to the line begining
+ @param[in] base base address for parameter writing (structure
+ like TABLE)
+ @param[in] parameter description
+ @param[in] mem_root MEM_ROOT for parameters allocation
*/
bool get_file_options_ulllist(char *&ptr, char *end, char *line,
- gptr base, File_option *parameter,
+ uchar* base, File_option *parameter,
MEM_ROOT *mem_root)
{
List<ulonglong> *nlist= (List<ulonglong>*)(base + parameter->offset);
@@ -656,30 +659,30 @@ nlist_err:
}
-/*
- parse parameters
-
- SYNOPSIS
- File_parser::parse()
- base base address for parameter writing (structure like
- TABLE)
- mem_root MEM_ROOT for parameters allocation
- parameters parameters description
- required number of parameters in the above list. If the file
- contains more parameters than "required", they will
- be ignored. If the file contains less parameters
- then "required", non-existing parameters will
- remain their values.
- hook hook called for unknown keys
- hook_data some data specific for the hook
-
- RETURN
- FALSE - OK
- TRUE - error
+/**
+ parse parameters.
+
+ @param base base address for parameter writing (structure like
+ TABLE)
+ @param mem_root MEM_ROOT for parameters allocation
+ @param parameters parameters description
+ @param required number of required parameters in above list. If the file
+ contains more parameters than "required", they will
+ be ignored. If the file contains less parameters
+ then "required", non-existing parameters will
+ remain their values.
+ @param hook hook called for unknown keys
+ @param hook_data some data specific for the hook
+
+ @retval
+ FALSE OK
+ @retval
+ TRUE error
*/
+
my_bool
-File_parser::parse(gptr base, MEM_ROOT *mem_root,
+File_parser::parse(uchar* base, MEM_ROOT *mem_root,
struct File_option *parameters, uint required,
Unknown_key_hook *hook)
{
@@ -862,32 +865,31 @@ list_err:
}
-/*
- Dummy unknown key hook
+/**
+ Dummy unknown key hook.
- SYNOPSIS
- File_parser_dummy_hook::process_unknown_string()
- unknown_key [in/out] reference on the line with unknown
- parameter and the parsing point
- base [in] base address for parameter writing (structure like
- TABLE)
- mem_root [in] MEM_ROOT for parameters allocation
- end [in] the end of the configuration
+ @param[in,out] unknown_key reference on the line with unknown
+ parameter and the parsing point
+ @param[in] base base address for parameter writing
+ (structure like TABLE)
+ @param[in] mem_root MEM_ROOT for parameters allocation
+ @param[in] end the end of the configuration
- NOTE
+ @note
This hook used to catch no longer supported keys and process them for
backward compatibility, but it will not slow down processing of modern
format files.
This hook does nothing except debug output.
- RETURN
+ @retval
FALSE OK
+ @retval
TRUE Error
*/
bool
File_parser_dummy_hook::process_unknown_string(char *&unknown_key,
- gptr base, MEM_ROOT *mem_root,
+ uchar* base, MEM_ROOT *mem_root,
char *end)
{
DBUG_ENTER("file_parser_dummy_hook::process_unknown_string");
diff --git a/sql/parse_file.h b/sql/parse_file.h
index ad7d1629e0a..cfac69cc471 100644
--- a/sql/parse_file.h
+++ b/sql/parse_file.h
@@ -20,26 +20,26 @@
#define PARSE_FILE_TIMESTAMPLENGTH 19
enum file_opt_type {
- FILE_OPTIONS_STRING, /* String (LEX_STRING) */
- FILE_OPTIONS_ESTRING, /* Escaped string (LEX_STRING) */
- FILE_OPTIONS_ULONGLONG, /* ulonglong parameter (ulonglong) */
- FILE_OPTIONS_TIMESTAMP, /* timestamp (LEX_STRING have to be
+ FILE_OPTIONS_STRING, /**< String (LEX_STRING) */
+ FILE_OPTIONS_ESTRING, /**< Escaped string (LEX_STRING) */
+ FILE_OPTIONS_ULONGLONG, /**< ulonglong parameter (ulonglong) */
+ FILE_OPTIONS_TIMESTAMP, /**< timestamp (LEX_STRING have to be
allocated with length 20 (19+1) */
- FILE_OPTIONS_STRLIST, /* list of escaped strings
+ FILE_OPTIONS_STRLIST, /**< list of escaped strings
(List<LEX_STRING>) */
- FILE_OPTIONS_ULLLIST /* list of ulonglong values
+ FILE_OPTIONS_ULLLIST /**< list of ulonglong values
(List<ulonglong>) */
};
struct File_option
{
- LEX_STRING name; /* Name of the option */
- int offset; /* offset to base address of value */
- file_opt_type type; /* Option type */
+ LEX_STRING name; /**< Name of the option */
+ int offset; /**< offset to base address of value */
+ file_opt_type type; /**< Option type */
};
-/*
+/**
This hook used to catch no longer supported keys and process them for
backward compatibility.
*/
@@ -49,25 +49,25 @@ class Unknown_key_hook
public:
Unknown_key_hook() {} /* Remove gcc warning */
virtual ~Unknown_key_hook() {} /* Remove gcc warning */
- virtual bool process_unknown_string(char *&unknown_key, gptr base,
+ virtual bool process_unknown_string(char *&unknown_key, uchar* base,
MEM_ROOT *mem_root, char *end)= 0;
};
-/* Dummy hook for parsers which do not need hook for unknown keys */
+/** Dummy hook for parsers which do not need hook for unknown keys. */
class File_parser_dummy_hook: public Unknown_key_hook
{
public:
File_parser_dummy_hook() {} /* Remove gcc warning */
- virtual bool process_unknown_string(char *&unknown_key, gptr base,
+ virtual bool process_unknown_string(char *&unknown_key, uchar* base,
MEM_ROOT *mem_root, char *end);
};
extern File_parser_dummy_hook file_parser_dummy_hook;
bool get_file_options_ulllist(char *&ptr, char *end, char *line,
- gptr base, File_option *parameter,
+ uchar* base, File_option *parameter,
MEM_ROOT *mem_root);
char *
@@ -80,7 +80,7 @@ File_parser *sql_parse_prepare(const LEX_STRING *file_name,
my_bool
sql_create_definition_file(const LEX_STRING *dir, const LEX_STRING *file_name,
const LEX_STRING *type,
- gptr base, File_option *parameters);
+ uchar* base, File_option *parameters);
my_bool rename_in_schema_file(THD *thd,
const char *schema, const char *old_name,
const char *new_name);
@@ -96,7 +96,7 @@ public:
my_bool ok() { return content_ok; }
LEX_STRING *type() { return &file_type; }
- my_bool parse(gptr base, MEM_ROOT *mem_root,
+ my_bool parse(uchar* base, MEM_ROOT *mem_root,
struct File_option *parameters, uint required,
Unknown_key_hook *hook);
@@ -104,21 +104,4 @@ public:
MEM_ROOT *mem_root,
bool bad_format_errors);
};
-
-
-/*
- Custom version of standard offsetof() macro which can be used to get
- offsets of members in class for non-POD types (according to the current
- version of C++ standard offsetof() macro can't be used in such cases and
- attempt to do so causes warnings to be emitted, OTOH in many cases it is
- still OK to assume that all instances of the class has the same offsets
- for the same members).
-
- This is temporary solution which should be removed once File_parser class
- and related routines are refactored.
-*/
-
-#define my_offsetof(TYPE, MEMBER) \
- ((size_t)((char *)&(((TYPE *)0x10)->MEMBER) - (char*)0x10))
-
#endif /* _PARSE_FILE_H_ */
diff --git a/sql/partition_element.h b/sql/partition_element.h
new file mode 100644
index 00000000000..905bc38165b
--- /dev/null
+++ b/sql/partition_element.h
@@ -0,0 +1,99 @@
+/* Copyright (C) 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 St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+/**
+ * An enum and a struct to handle partitioning and subpartitioning.
+ */
+enum partition_type {
+ NOT_A_PARTITION= 0,
+ RANGE_PARTITION,
+ HASH_PARTITION,
+ LIST_PARTITION
+};
+
+enum partition_state {
+ PART_NORMAL= 0,
+ PART_IS_DROPPED= 1,
+ PART_TO_BE_DROPPED= 2,
+ PART_TO_BE_ADDED= 3,
+ PART_TO_BE_REORGED= 4,
+ PART_REORGED_DROPPED= 5,
+ PART_CHANGED= 6,
+ PART_IS_CHANGED= 7,
+ PART_IS_ADDED= 8
+};
+
+/*
+ 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
+ NULL or not.
+*/
+
+typedef struct p_elem_val
+{
+ longlong value;
+ bool null_value;
+ bool unsigned_flag;
+} part_elem_value;
+
+struct st_ddl_log_memory_entry;
+
+class partition_element :public Sql_alloc {
+public:
+ List<partition_element> subpartitions;
+ List<part_elem_value> list_val_list;
+ ha_rows part_max_rows;
+ ha_rows part_min_rows;
+ longlong range_value;
+ char *partition_name;
+ char *tablespace_name;
+ struct st_ddl_log_memory_entry *log_entry;
+ char* part_comment;
+ char* data_file_name;
+ char* index_file_name;
+ handlerton *engine_type;
+ 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 */
+
+ partition_element()
+ : part_max_rows(0), part_min_rows(0), range_value(0),
+ partition_name(NULL), tablespace_name(NULL),
+ log_entry(NULL), part_comment(NULL),
+ data_file_name(NULL), index_file_name(NULL),
+ engine_type(NULL), part_state(PART_NORMAL),
+ nodegroup_id(UNDEF_NODEGROUP), has_null_value(FALSE),
+ signed_flag(FALSE), max_value(FALSE)
+ {
+ }
+ partition_element(partition_element *part_elem)
+ : part_max_rows(part_elem->part_max_rows),
+ part_min_rows(part_elem->part_min_rows),
+ range_value(0), partition_name(NULL),
+ tablespace_name(part_elem->tablespace_name),
+ part_comment(part_elem->part_comment),
+ data_file_name(part_elem->data_file_name),
+ index_file_name(part_elem->index_file_name),
+ engine_type(part_elem->engine_type),
+ part_state(part_elem->part_state),
+ nodegroup_id(part_elem->nodegroup_id),
+ has_null_value(FALSE)
+ {
+ }
+ ~partition_element() {}
+};
diff --git a/sql/partition_info.cc b/sql/partition_info.cc
new file mode 100644
index 00000000000..e2027d3571e
--- /dev/null
+++ b/sql/partition_info.cc
@@ -0,0 +1,1327 @@
+/* Copyright (C) 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+/* Some general useful functions */
+
+#ifdef USE_PRAGMA_IMPLEMENTATION
+#pragma implementation
+#endif
+
+#include "mysql_priv.h"
+
+#ifdef WITH_PARTITION_STORAGE_ENGINE
+#include "ha_partition.h"
+
+
+partition_info *partition_info::get_clone()
+{
+ if (!this)
+ return 0;
+ List_iterator<partition_element> part_it(partitions);
+ partition_element *part;
+ partition_info *clone= new partition_info();
+ if (!clone)
+ {
+ mem_alloc_error(sizeof(partition_info));
+ return NULL;
+ }
+ memcpy(clone, this, sizeof(partition_info));
+ clone->partitions.empty();
+
+ while ((part= (part_it++)))
+ {
+ List_iterator<partition_element> subpart_it(part->subpartitions);
+ partition_element *subpart;
+ partition_element *part_clone= new partition_element();
+ if (!part_clone)
+ {
+ mem_alloc_error(sizeof(partition_element));
+ return NULL;
+ }
+ memcpy(part_clone, part, sizeof(partition_element));
+ part_clone->subpartitions.empty();
+ while ((subpart= (subpart_it++)))
+ {
+ partition_element *subpart_clone= new partition_element();
+ if (!subpart_clone)
+ {
+ mem_alloc_error(sizeof(partition_element));
+ return NULL;
+ }
+ memcpy(subpart_clone, subpart, sizeof(partition_element));
+ part_clone->subpartitions.push_back(subpart_clone);
+ }
+ clone->partitions.push_back(part_clone);
+ }
+ return clone;
+}
+
+/*
+ Create a memory area where default partition names are stored and fill it
+ up with the names.
+
+ SYNOPSIS
+ create_default_partition_names()
+ part_no Partition number for subparts
+ no_parts Number of partitions
+ start_no Starting partition number
+ subpart Is it subpartitions
+
+ RETURN VALUE
+ A pointer to the memory area of the default partition names
+
+ DESCRIPTION
+ A support routine for the partition code where default values are
+ generated.
+ The external routine needing this code is check_partition_info
+*/
+
+#define MAX_PART_NAME_SIZE 8
+
+char *partition_info::create_default_partition_names(uint part_no,
+ uint no_parts_arg,
+ uint start_no)
+{
+ char *ptr= (char*) sql_calloc(no_parts_arg*MAX_PART_NAME_SIZE);
+ char *move_ptr= ptr;
+ uint i= 0;
+ DBUG_ENTER("create_default_partition_names");
+
+ if (likely(ptr != 0))
+ {
+ do
+ {
+ my_sprintf(move_ptr, (move_ptr,"p%u", (start_no + i)));
+ move_ptr+=MAX_PART_NAME_SIZE;
+ } while (++i < no_parts_arg);
+ }
+ else
+ {
+ mem_alloc_error(no_parts_arg*MAX_PART_NAME_SIZE);
+ }
+ DBUG_RETURN(ptr);
+}
+
+
+/*
+ Create a unique name for the subpartition as part_name'sp''subpart_no'
+ SYNOPSIS
+ create_subpartition_name()
+ subpart_no Number of subpartition
+ part_name Name of partition
+ RETURN VALUES
+ >0 A reference to the created name string
+ 0 Memory allocation error
+*/
+
+char *partition_info::create_subpartition_name(uint subpart_no,
+ const char *part_name)
+{
+ uint size_alloc= strlen(part_name) + MAX_PART_NAME_SIZE;
+ char *ptr= (char*) sql_calloc(size_alloc);
+ DBUG_ENTER("create_subpartition_name");
+
+ if (likely(ptr != NULL))
+ {
+ my_sprintf(ptr, (ptr, "%ssp%u", part_name, subpart_no));
+ }
+ else
+ {
+ mem_alloc_error(size_alloc);
+ }
+ DBUG_RETURN(ptr);
+}
+
+
+/*
+ Set up all the default partitions not set-up by the user in the SQL
+ statement. Also perform a number of checks that the user hasn't tried
+ to use default values where no defaults exists.
+
+ SYNOPSIS
+ set_up_default_partitions()
+ file A reference to a handler of the table
+ info Create info
+ start_no Starting partition number
+
+ RETURN VALUE
+ TRUE Error, attempted default values not possible
+ FALSE Ok, default partitions set-up
+
+ DESCRIPTION
+ The routine uses the underlying handler of the partitioning to define
+ the default number of partitions. For some handlers this requires
+ knowledge of the maximum number of rows to be stored in the table.
+ This routine only accepts HASH and KEY partitioning and thus there is
+ no subpartitioning if this routine is successful.
+ The external routine needing this code is check_partition_info
+*/
+
+bool partition_info::set_up_default_partitions(handler *file,
+ HA_CREATE_INFO *info,
+ uint start_no)
+{
+ uint i;
+ char *default_name;
+ bool result= TRUE;
+ DBUG_ENTER("partition_info::set_up_default_partitions");
+
+ if (part_type != HASH_PARTITION)
+ {
+ const char *error_string;
+ if (part_type == RANGE_PARTITION)
+ error_string= partition_keywords[PKW_RANGE].str;
+ else
+ error_string= partition_keywords[PKW_LIST].str;
+ my_error(ER_PARTITIONS_MUST_BE_DEFINED_ERROR, MYF(0), error_string);
+ goto end;
+ }
+
+ if ((no_parts == 0) &&
+ ((no_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))
+ {
+ my_error(ER_TOO_MANY_PARTITIONS_ERROR, MYF(0));
+ goto end;
+ }
+ if (unlikely((!(default_name= create_default_partition_names(0, no_parts,
+ start_no)))))
+ goto end;
+ i= 0;
+ do
+ {
+ partition_element *part_elem= new partition_element();
+ if (likely(part_elem != 0 &&
+ (!partitions.push_back(part_elem))))
+ {
+ part_elem->engine_type= default_engine_type;
+ part_elem->partition_name= default_name;
+ default_name+=MAX_PART_NAME_SIZE;
+ }
+ else
+ {
+ mem_alloc_error(sizeof(partition_element));
+ goto end;
+ }
+ } while (++i < no_parts);
+ result= FALSE;
+end:
+ DBUG_RETURN(result);
+}
+
+
+/*
+ Set up all the default subpartitions not set-up by the user in the SQL
+ statement. Also perform a number of checks that the default partitioning
+ becomes an allowed partitioning scheme.
+
+ SYNOPSIS
+ set_up_default_subpartitions()
+ file A reference to a handler of the table
+ info Create info
+
+ RETURN VALUE
+ TRUE Error, attempted default values not possible
+ FALSE Ok, default partitions set-up
+
+ DESCRIPTION
+ The routine uses the underlying handler of the partitioning to define
+ the default number of partitions. For some handlers this requires
+ knowledge of the maximum number of rows to be stored in the table.
+ This routine is only called for RANGE or LIST partitioning and those
+ need to be specified so only subpartitions are specified.
+ The external routine needing this code is check_partition_info
+*/
+
+bool partition_info::set_up_default_subpartitions(handler *file,
+ HA_CREATE_INFO *info)
+{
+ uint i, j;
+ bool result= TRUE;
+ partition_element *part_elem;
+ 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))
+ {
+ my_error(ER_TOO_MANY_PARTITIONS_ERROR, MYF(0));
+ goto end;
+ }
+ i= 0;
+ do
+ {
+ part_elem= part_it++;
+ j= 0;
+ do
+ {
+ partition_element *subpart_elem= new partition_element(part_elem);
+ if (likely(subpart_elem != 0 &&
+ (!part_elem->subpartitions.push_back(subpart_elem))))
+ {
+ char *ptr= create_subpartition_name(j, part_elem->partition_name);
+ if (!ptr)
+ goto end;
+ subpart_elem->engine_type= default_engine_type;
+ subpart_elem->partition_name= ptr;
+ }
+ else
+ {
+ mem_alloc_error(sizeof(partition_element));
+ goto end;
+ }
+ } while (++j < no_subparts);
+ } while (++i < no_parts);
+ result= FALSE;
+end:
+ DBUG_RETURN(result);
+}
+
+
+/*
+ Support routine for check_partition_info
+
+ SYNOPSIS
+ set_up_defaults_for_partitioning()
+ file A reference to a handler of the table
+ info Create info
+ start_no Starting partition number
+
+ RETURN VALUE
+ TRUE Error, attempted default values not possible
+ FALSE Ok, default partitions set-up
+
+ DESCRIPTION
+ Set up defaults for partition or subpartition (cannot set-up for both,
+ this will return an error.
+*/
+
+bool partition_info::set_up_defaults_for_partitioning(handler *file,
+ HA_CREATE_INFO *info,
+ uint start_no)
+{
+ DBUG_ENTER("partition_info::set_up_defaults_for_partitioning");
+
+ if (!default_partitions_setup)
+ {
+ default_partitions_setup= TRUE;
+ if (use_default_partitions)
+ DBUG_RETURN(set_up_default_partitions(file, info, start_no));
+ if (is_sub_partitioned() &&
+ use_default_subpartitions)
+ DBUG_RETURN(set_up_default_subpartitions(file, info));
+ }
+ DBUG_RETURN(FALSE);
+}
+
+
+/*
+ A support function to check if a partition element's name is unique
+
+ SYNOPSIS
+ has_unique_name()
+ partition_element element to check
+
+ RETURN VALUES
+ TRUE Has unique name
+ FALSE Doesn't
+*/
+
+bool partition_info::has_unique_name(partition_element *element)
+{
+ DBUG_ENTER("partition_info::has_unique_name");
+
+ const char *name_to_check= element->partition_name;
+ List_iterator<partition_element> parts_it(partitions);
+
+ partition_element *el;
+ while ((el= (parts_it++)))
+ {
+ if (!(my_strcasecmp(system_charset_info, el->partition_name,
+ name_to_check)) && el != element)
+ DBUG_RETURN(FALSE);
+
+ if (!el->subpartitions.is_empty())
+ {
+ partition_element *sub_el;
+ List_iterator<partition_element> subparts_it(el->subpartitions);
+ while ((sub_el= (subparts_it++)))
+ {
+ if (!(my_strcasecmp(system_charset_info, sub_el->partition_name,
+ name_to_check)) && sub_el != element)
+ DBUG_RETURN(FALSE);
+ }
+ }
+ }
+ DBUG_RETURN(TRUE);
+}
+
+
+/*
+ A support function to check partition names for duplication in a
+ partitioned table
+
+ SYNOPSIS
+ has_unique_names()
+
+ RETURN VALUES
+ TRUE Has unique part and subpart names
+ FALSE Doesn't
+
+ DESCRIPTION
+ Checks that the list of names in the partitions doesn't contain any
+ duplicated names.
+*/
+
+char *partition_info::has_unique_names()
+{
+ DBUG_ENTER("partition_info::has_unique_names");
+
+ List_iterator<partition_element> parts_it(partitions);
+
+ partition_element *el;
+ while ((el= (parts_it++)))
+ {
+ if (! has_unique_name(el))
+ DBUG_RETURN(el->partition_name);
+
+ if (!el->subpartitions.is_empty())
+ {
+ List_iterator<partition_element> subparts_it(el->subpartitions);
+ partition_element *subel;
+ while ((subel= (subparts_it++)))
+ {
+ if (! has_unique_name(subel))
+ DBUG_RETURN(subel->partition_name);
+ }
+ }
+ }
+ DBUG_RETURN(NULL);
+}
+
+
+/*
+ Check that the partition/subpartition is setup to use the correct
+ storage engine
+ SYNOPSIS
+ check_engine_condition()
+ p_elem Partition element
+ table_engine_set Have user specified engine on table level
+ inout::engine_type Current engine used
+ inout::first Is it first partition
+ RETURN VALUE
+ TRUE Failed check
+ FALSE Ok
+ DESCRIPTION
+ Specified engine for table and partitions p0 and pn
+ Must be correct both on CREATE and ALTER commands
+ table p0 pn res (0 - OK, 1 - FAIL)
+ - - - 0
+ - - x 1
+ - x - 1
+ - x x 0
+ x - - 0
+ x - x 0
+ x x - 0
+ x x x 0
+ i.e:
+ - All subpartitions must use the same engine
+ AND it must be the same as the partition.
+ - All partitions must use the same engine
+ AND it must be the same as the table.
+ - if one does NOT specify an engine on the table level
+ then one must either NOT specify any engine on any
+ partition/subpartition OR for ALL partitions/subpartitions
+ Note:
+ When ALTER a table, the engines are already set for all levels
+ (table, all partitions and subpartitions). So if one want to
+ change the storage engine, one must specify it on the table level
+
+*/
+
+static bool check_engine_condition(partition_element *p_elem,
+ bool table_engine_set,
+ handlerton **engine_type,
+ bool *first)
+{
+ DBUG_ENTER("check_engine_condition");
+
+ DBUG_PRINT("enter", ("p_eng %s t_eng %s t_eng_set %u first %u state %u",
+ ha_resolve_storage_engine_name(p_elem->engine_type),
+ ha_resolve_storage_engine_name(*engine_type),
+ table_engine_set, *first, p_elem->part_state));
+ if (*first && !table_engine_set)
+ {
+ *engine_type= p_elem->engine_type;
+ DBUG_PRINT("info", ("setting table_engine = %s",
+ ha_resolve_storage_engine_name(*engine_type)));
+ }
+ *first= FALSE;
+ if ((table_engine_set &&
+ (p_elem->engine_type != (*engine_type) &&
+ p_elem->engine_type)) ||
+ (!table_engine_set &&
+ p_elem->engine_type != (*engine_type)))
+ {
+ DBUG_RETURN(TRUE);
+ }
+ else
+ {
+ DBUG_RETURN(FALSE);
+ }
+}
+
+
+/*
+ Check engine mix that it is correct
+ Current limitation is that all partitions and subpartitions
+ must use the same storage engine.
+ SYNOPSIS
+ check_engine_mix()
+ inout::engine_type Current engine used
+ table_engine_set Have user specified engine on table level
+ RETURN VALUE
+ TRUE Error, mixed engines
+ FALSE Ok, no mixed engines
+ DESCRIPTION
+ Current check verifies only that all handlers are the same.
+ Later this check will be more sophisticated.
+ (specified partition handler ) specified table handler
+ (NDB, NDB) NDB OK
+ (MYISAM, MYISAM) - OK
+ (MYISAM, -) - NOT OK
+ (MYISAM, -) MYISAM OK
+ (- , MYISAM) - NOT OK
+ (- , -) MYISAM OK
+ (-,-) - OK
+ (NDB, MYISAM) * NOT OK
+*/
+
+bool partition_info::check_engine_mix(handlerton *engine_type,
+ bool table_engine_set)
+{
+ handlerton *old_engine_type= engine_type;
+ bool first= TRUE;
+ uint no_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)
+ {
+ List_iterator<partition_element> part_it(partitions);
+ uint i= 0;
+ do
+ {
+ partition_element *part_elem= part_it++;
+ DBUG_PRINT("info", ("part = %d engine = %s table_engine_set %u",
+ i, ha_resolve_storage_engine_name(part_elem->engine_type),
+ table_engine_set));
+ if (is_sub_partitioned() &&
+ part_elem->subpartitions.elements)
+ {
+ uint no_subparts= part_elem->subpartitions.elements;
+ uint j= 0;
+ List_iterator<partition_element> sub_it(part_elem->subpartitions);
+ do
+ {
+ partition_element *sub_elem= sub_it++;
+ DBUG_PRINT("info", ("sub = %d engine = %s table_engie_set %u",
+ j, ha_resolve_storage_engine_name(sub_elem->engine_type),
+ table_engine_set));
+ if (check_engine_condition(sub_elem, table_engine_set,
+ &engine_type, &first))
+ goto error;
+ } while (++j < no_subparts);
+ /* ensure that the partition also has correct engine */
+ if (check_engine_condition(part_elem, table_engine_set,
+ &engine_type, &first))
+ goto error;
+ }
+ else if (check_engine_condition(part_elem, table_engine_set,
+ &engine_type, &first))
+ goto error;
+ } while (++i < no_parts);
+ }
+ DBUG_PRINT("info", ("engine_type = %s",
+ ha_resolve_storage_engine_name(engine_type)));
+ if (!engine_type)
+ engine_type= old_engine_type;
+ if (engine_type->flags & HTON_NO_PARTITION)
+ {
+ my_error(ER_PARTITION_MERGE_ERROR, MYF(0));
+ DBUG_RETURN(TRUE);
+ }
+ DBUG_PRINT("info", ("out: engine_type = %s",
+ ha_resolve_storage_engine_name(engine_type)));
+ DBUG_ASSERT(engine_type != partition_hton);
+ DBUG_RETURN(FALSE);
+error:
+ /*
+ Mixed engines not yet supported but when supported it will need
+ the partition handler
+ */
+ DBUG_RETURN(TRUE);
+}
+
+
+/*
+ This routine allocates an array for all range constants to achieve a fast
+ check what partition a certain value belongs to. At the same time it does
+ also check that the range constants are defined in increasing order and
+ that the expressions are constant integer expressions.
+
+ SYNOPSIS
+ check_range_constants()
+
+ RETURN VALUE
+ TRUE An error occurred during creation of range constants
+ FALSE Successful creation of range constant mapping
+
+ DESCRIPTION
+ This routine is called from check_partition_info to get a quick error
+ before we came too far into the CREATE TABLE process. It is also called
+ from fix_partition_func every time we open the .frm file. It is only
+ called for RANGE PARTITIONed tables.
+*/
+
+bool partition_info::check_range_constants()
+{
+ 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;
+ DBUG_ENTER("partition_info::check_range_constants");
+ DBUG_PRINT("enter", ("INT_RESULT with %d parts", no_parts));
+
+ 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
+ {
+ part_def= it++;
+ if ((i != (no_parts - 1)) || !defined_max_value)
+ {
+ part_range_value= part_def->range_value;
+ if (!signed_flag)
+ part_range_value-= 0x8000000000000000ULL;
+ }
+ else
+ part_range_value= LONGLONG_MAX;
+ if (first)
+ {
+ current_largest= part_range_value;
+ range_int_array[0]= part_range_value;
+ first= FALSE;
+ }
+ else
+ {
+ if (likely(current_largest < part_range_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;
+ }
+ else
+ {
+ my_error(ER_RANGE_NOT_INCREASING_ERROR, MYF(0));
+ goto end;
+ }
+ }
+ } while (++i < no_parts);
+ result= FALSE;
+end:
+ DBUG_RETURN(result);
+}
+
+
+/*
+ Support routines for check_list_constants used by qsort to sort the
+ constant list expressions. One routine for unsigned and one for signed.
+
+ SYNOPSIS
+ list_part_cmp()
+ a First list constant to compare with
+ b Second list constant to compare with
+
+ RETURN VALUE
+ +1 a > b
+ 0 a == b
+ -1 a < b
+*/
+
+int partition_info::list_part_cmp(const void* a, const void* b)
+{
+ longlong a1= ((LIST_PART_ENTRY*)a)->list_value;
+ longlong b1= ((LIST_PART_ENTRY*)b)->list_value;
+ if (a1 < b1)
+ return -1;
+ else if (a1 > b1)
+ return +1;
+ else
+ return 0;
+}
+
+
+/*
+ This routine allocates an array for all list constants to achieve a fast
+ check what partition a certain value belongs to. At the same time it does
+ also check that there are no duplicates among the list constants and that
+ that the list expressions are constant integer expressions.
+
+ SYNOPSIS
+ check_list_constants()
+
+ RETURN VALUE
+ TRUE An error occurred during creation of list constants
+ FALSE Successful creation of list constant mapping
+
+ DESCRIPTION
+ This routine is called from check_partition_info to get a quick error
+ before we came too far into the CREATE TABLE process. It is also called
+ from fix_partition_func every time we open the .frm file. It is only
+ called for LIST PARTITIONed tables.
+*/
+
+bool partition_info::check_list_constants()
+{
+ uint i;
+ uint list_index= 0;
+ part_elem_value *list_value;
+ bool result= TRUE;
+ longlong curr_value, prev_value, type_add, calc_value;
+ partition_element* part_def;
+ bool found_null= FALSE;
+ List_iterator<partition_element> list_func_it(partitions);
+ DBUG_ENTER("partition_info::check_list_constants");
+
+ part_result_type= INT_RESULT;
+ no_list_values= 0;
+ /*
+ We begin by calculating the number of list values that have been
+ defined in the first step.
+
+ We use this number to allocate a properly sized array of structs
+ to keep the partition id and the value to use in that partition.
+ In the second traversal we assign them values in the struct array.
+
+ Finally we sort the array of structs in order of values to enable
+ a quick binary search for the proper value to discover the
+ partition id.
+ After sorting the array we check that there are no duplicates in the
+ list.
+ */
+
+ i= 0;
+ do
+ {
+ part_def= list_func_it++;
+ if (part_def->has_null_value)
+ {
+ if (found_null)
+ {
+ my_error(ER_MULTIPLE_DEF_CONST_IN_LIST_PART_ERROR, MYF(0));
+ goto end;
+ }
+ has_null_value= TRUE;
+ has_null_part_id= i;
+ found_null= TRUE;
+ }
+ List_iterator<part_elem_value> list_val_it1(part_def->list_val_list);
+ while (list_val_it1++)
+ no_list_values++;
+ } while (++i < no_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))
+ {
+ mem_alloc_error(no_list_values * sizeof(LIST_PART_ENTRY));
+ 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 ?
+ 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++))
+ {
+ 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)
+ {
+ bool first= TRUE;
+ my_qsort((void*)list_array, no_list_values, sizeof(LIST_PART_ENTRY),
+ &list_part_cmp);
+
+ 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))
+ {
+ prev_value= curr_value;
+ first= FALSE;
+ }
+ else
+ {
+ my_error(ER_MULTIPLE_DEF_CONST_IN_LIST_PART_ERROR, MYF(0));
+ goto end;
+ }
+ } while (++i < no_list_values);
+ }
+ result= FALSE;
+end:
+ DBUG_RETURN(result);
+}
+
+
+/*
+ This code is used early in the CREATE TABLE and ALTER TABLE process.
+
+ SYNOPSIS
+ check_partition_info()
+ 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
+
+ RETURN VALUE
+ TRUE Error, something went wrong
+ FALSE Ok, full partition data structures are now generated
+
+ DESCRIPTION
+ We will check that the partition info requested is possible to set-up in
+ this version. This routine is an extension of the parser one could say.
+ If defaults were used we will generate default data structures for all
+ partitions.
+
+*/
+
+bool partition_info::check_partition_info(THD *thd, handlerton **eng_type,
+ handler *file, HA_CREATE_INFO *info,
+ bool check_partition_function)
+{
+ handlerton *table_engine= default_engine_type;
+ uint i, tot_partitions;
+ bool result= TRUE, table_engine_set;
+ char *same_name;
+ DBUG_ENTER("partition_info::check_partition_info");
+ DBUG_ASSERT(default_engine_type != partition_hton);
+
+ DBUG_PRINT("info", ("default table_engine = %s",
+ ha_resolve_storage_engine_name(table_engine)));
+ if (check_partition_function)
+ {
+ int err= 0;
+
+ if (part_type != HASH_PARTITION || !list_of_part_fields)
+ {
+ DBUG_ASSERT(part_expr);
+ err= part_expr->walk(&Item::check_partition_func_processor, 0,
+ NULL);
+ if (!err && is_sub_partitioned() && !list_of_subpart_fields)
+ err= subpart_expr->walk(&Item::check_partition_func_processor, 0,
+ NULL);
+ }
+ if (err)
+ {
+ my_error(ER_PARTITION_FUNCTION_IS_NOT_ALLOWED, MYF(0));
+ goto end;
+ }
+ }
+ if (unlikely(!is_sub_partitioned() &&
+ !(use_default_subpartitions && use_default_no_subpartitions)))
+ {
+ my_error(ER_SUBPARTITION_ERROR, MYF(0));
+ goto end;
+ }
+ if (unlikely(is_sub_partitioned() &&
+ (!(part_type == RANGE_PARTITION ||
+ part_type == LIST_PARTITION))))
+ {
+ /* Only RANGE and LIST partitioning can be subpartitioned */
+ my_error(ER_SUBPARTITION_ERROR, MYF(0));
+ goto end;
+ }
+ if (unlikely(set_up_defaults_for_partitioning(file, info, (uint)0)))
+ goto end;
+ if (!(tot_partitions= get_tot_partitions()))
+ {
+ my_error(ER_PARTITION_NOT_DEFINED_ERROR, MYF(0), "partitions");
+ goto end;
+ }
+ if (unlikely(tot_partitions > MAX_PARTITIONS))
+ {
+ my_error(ER_TOO_MANY_PARTITIONS_ERROR, MYF(0));
+ goto end;
+ }
+ /*
+ if NOT specified ENGINE = <engine>:
+ If Create, always use create_info->db_type
+ else, use previous tables db_type
+ either ALL or NONE partition should be set to
+ default_engine_type when not table_engine_set
+ Note: after a table is created its storage engines for
+ the table and all partitions/subpartitions are set.
+ So when ALTER it is already set on table level
+ */
+ if (info && info->used_fields & HA_CREATE_USED_ENGINE)
+ {
+ table_engine_set= TRUE;
+ table_engine= info->db_type;
+ /* if partition_hton, use thd->lex->create_info */
+ if (table_engine == partition_hton)
+ table_engine= thd->lex->create_info.db_type;
+ DBUG_ASSERT(table_engine != partition_hton);
+ DBUG_PRINT("info", ("Using table_engine = %s",
+ ha_resolve_storage_engine_name(table_engine)));
+ }
+ else
+ {
+ table_engine_set= FALSE;
+ if (thd->lex->sql_command != SQLCOM_CREATE_TABLE)
+ {
+ table_engine_set= TRUE;
+ DBUG_PRINT("info", ("No create, table_engine = %s",
+ ha_resolve_storage_engine_name(table_engine)));
+ DBUG_ASSERT(table_engine && table_engine != partition_hton);
+ }
+ }
+
+ if ((same_name= has_unique_names()))
+ {
+ my_error(ER_SAME_NAME_PARTITION, MYF(0), same_name);
+ goto end;
+ }
+ i= 0;
+ {
+ List_iterator<partition_element> part_it(partitions);
+ uint no_parts_not_set= 0;
+ uint prev_no_subparts_not_set= no_subparts + 1;
+ do
+ {
+ partition_element *part_elem= part_it++;
+#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;
+ }
+ if (!is_sub_partitioned())
+ {
+ if (part_elem->engine_type == NULL)
+ {
+ no_parts_not_set++;
+ part_elem->engine_type= default_engine_type;
+ }
+ if (check_table_name(part_elem->partition_name,
+ strlen(part_elem->partition_name)))
+ {
+ my_error(ER_WRONG_PARTITION_NAME, MYF(0));
+ goto end;
+ }
+ DBUG_PRINT("info", ("part = %d engine = %s",
+ i, ha_resolve_storage_engine_name(part_elem->engine_type)));
+ }
+ else
+ {
+ uint j= 0;
+ uint no_subparts_not_set= 0;
+ List_iterator<partition_element> sub_it(part_elem->subpartitions);
+ partition_element *sub_elem;
+ do
+ {
+ sub_elem= sub_it++;
+ if (check_table_name(sub_elem->partition_name,
+ strlen(sub_elem->partition_name)))
+ {
+ my_error(ER_WRONG_PARTITION_NAME, MYF(0));
+ goto end;
+ }
+ if (sub_elem->engine_type == NULL)
+ {
+ if (part_elem->engine_type != NULL)
+ sub_elem->engine_type= part_elem->engine_type;
+ else
+ {
+ sub_elem->engine_type= default_engine_type;
+ no_subparts_not_set++;
+ }
+ }
+ DBUG_PRINT("info", ("part = %d sub = %d engine = %s", i, j,
+ ha_resolve_storage_engine_name(sub_elem->engine_type)));
+ } while (++j < no_subparts);
+
+ if (prev_no_subparts_not_set == (no_subparts + 1) &&
+ (no_subparts_not_set == 0 || no_subparts_not_set == no_subparts))
+ prev_no_subparts_not_set= no_subparts_not_set;
+
+ if (!table_engine_set &&
+ prev_no_subparts_not_set != no_subparts_not_set)
+ {
+ DBUG_PRINT("info", ("no_subparts_not_set = %u no_subparts = %u",
+ no_subparts_not_set, no_subparts));
+ my_error(ER_MIX_HANDLER_ERROR, MYF(0));
+ goto end;
+ }
+
+ if (part_elem->engine_type == NULL)
+ {
+ if (no_subparts_not_set == 0)
+ part_elem->engine_type= sub_elem->engine_type;
+ else
+ {
+ no_parts_not_set++;
+ part_elem->engine_type= default_engine_type;
+ }
+ }
+ }
+ } while (++i < no_parts);
+ if (!table_engine_set &&
+ no_parts_not_set != 0 &&
+ no_parts_not_set != no_parts)
+ {
+ DBUG_PRINT("info", ("no_parts_not_set = %u no_parts = %u",
+ no_parts_not_set, no_subparts));
+ my_error(ER_MIX_HANDLER_ERROR, MYF(0));
+ goto end;
+ }
+ }
+ if (unlikely(check_engine_mix(table_engine, table_engine_set)))
+ {
+ my_error(ER_MIX_HANDLER_ERROR, MYF(0));
+ goto end;
+ }
+
+ DBUG_ASSERT(table_engine != partition_hton &&
+ default_engine_type == table_engine);
+ if (eng_type)
+ *eng_type= table_engine;
+
+
+ /*
+ We need to check all constant expressions that they are of the correct
+ type and that they are increasing for ranges and not overlapping for
+ list constants.
+ */
+
+ if (fixed)
+ {
+ if (unlikely((part_type == RANGE_PARTITION && check_range_constants()) ||
+ (part_type == LIST_PARTITION && check_list_constants())))
+ goto end;
+ }
+ result= FALSE;
+end:
+ DBUG_RETURN(result);
+}
+
+
+/*
+ Print error for no partition found
+
+ SYNOPSIS
+ print_no_partition_found()
+ table Table object
+
+ RETURN VALUES
+*/
+
+void partition_info::print_no_partition_found(TABLE *table)
+{
+ 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;
+
+ 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));
+ else
+ {
+ my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->read_set);
+ if (part_expr->null_value)
+ buf_ptr= (char*)"NULL";
+ else
+ longlong2str(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);
+ }
+}
+/*
+ Set up buffers and arrays for fields requiring preparation
+ SYNOPSIS
+ set_up_charset_field_preps()
+
+ RETURN VALUES
+ TRUE Memory Allocation error
+ FALSE Success
+
+ DESCRIPTION
+ Set up arrays and buffers for fields that require special care for
+ calculation of partition id. This is used for string fields with
+ variable length or string fields with fixed length that isn't using
+ the binary collation.
+*/
+
+bool partition_info::set_up_charset_field_preps()
+{
+ Field *field, **ptr;
+ uchar **char_ptrs;
+ unsigned i;
+ size_t size;
+ uint tot_fields= 0;
+ uint tot_part_fields= 0;
+ uint tot_subpart_fields= 0;
+ DBUG_ENTER("set_up_charset_field_preps");
+
+ if (!(part_type == HASH_PARTITION &&
+ list_of_part_fields) &&
+ check_part_func_fields(part_field_array, FALSE))
+ {
+ ptr= part_field_array;
+ /* Set up arrays and buffers for those fields */
+ while ((field= *(ptr++)))
+ {
+ if (field_is_partition_charset(field))
+ {
+ tot_part_fields++;
+ tot_fields++;
+ }
+ }
+ size= tot_part_fields * sizeof(char*);
+ if (!(char_ptrs= (uchar**)sql_calloc(size)))
+ goto error;
+ part_field_buffers= char_ptrs;
+ if (!(char_ptrs= (uchar**)sql_calloc(size)))
+ goto error;
+ restore_part_field_ptrs= char_ptrs;
+ size= (tot_part_fields + 1) * sizeof(Field*);
+ if (!(char_ptrs= (uchar**)sql_alloc(size)))
+ goto error;
+ part_charset_field_array= (Field**)char_ptrs;
+ ptr= part_field_array;
+ i= 0;
+ while ((field= *(ptr++)))
+ {
+ if (field_is_partition_charset(field))
+ {
+ uchar *field_buf;
+ size= field->pack_length();
+ if (!(field_buf= (uchar*) sql_calloc(size)))
+ goto error;
+ part_charset_field_array[i]= field;
+ part_field_buffers[i++]= field_buf;
+ }
+ }
+ part_charset_field_array[i]= NULL;
+ }
+ if (is_sub_partitioned() && !list_of_subpart_fields &&
+ check_part_func_fields(subpart_field_array, FALSE))
+ {
+ /* Set up arrays and buffers for those fields */
+ ptr= subpart_field_array;
+ while ((field= *(ptr++)))
+ {
+ if (field_is_partition_charset(field))
+ {
+ tot_subpart_fields++;
+ tot_fields++;
+ }
+ }
+ size= tot_subpart_fields * sizeof(char*);
+ if (!(char_ptrs= (uchar**) sql_calloc(size)))
+ goto error;
+ subpart_field_buffers= char_ptrs;
+ if (!(char_ptrs= (uchar**) sql_calloc(size)))
+ goto error;
+ restore_subpart_field_ptrs= char_ptrs;
+ size= (tot_subpart_fields + 1) * sizeof(Field*);
+ if (!(char_ptrs= (uchar**) sql_alloc(size)))
+ goto error;
+ subpart_charset_field_array= (Field**)char_ptrs;
+ ptr= subpart_field_array;
+ i= 0;
+ while ((field= *(ptr++)))
+ {
+ CHARSET_INFO *cs;
+ uchar *field_buf;
+ LINT_INIT(field_buf);
+
+ if (!field_is_partition_charset(field))
+ continue;
+ cs= ((Field_str*)field)->charset();
+ size= field->pack_length();
+ if (!(field_buf= (uchar*) sql_calloc(size)))
+ goto error;
+ subpart_charset_field_array[i]= field;
+ subpart_field_buffers[i++]= field_buf;
+ }
+ 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);
+ DBUG_RETURN(TRUE);
+}
+
+
+/*
+ Check if path does not contain mysql data home directory
+ for partition elements with data directory and index directory
+
+ SYNOPSIS
+ check_partition_dirs()
+ part_info partition_info struct
+
+ RETURN VALUES
+ 0 ok
+ 1 error
+*/
+
+bool check_partition_dirs(partition_info *part_info)
+{
+ if (!part_info)
+ return 0;
+
+ partition_element *part_elem;
+ List_iterator<partition_element> part_it(part_info->partitions);
+ while ((part_elem= part_it++))
+ {
+ if (part_elem->subpartitions.elements)
+ {
+ List_iterator<partition_element> sub_it(part_elem->subpartitions);
+ partition_element *subpart_elem;
+ while ((subpart_elem= sub_it++))
+ {
+ if (test_if_data_home_dir(subpart_elem->data_file_name))
+ goto dd_err;
+ if (test_if_data_home_dir(subpart_elem->index_file_name))
+ goto id_err;
+ }
+ }
+ else
+ {
+ if (test_if_data_home_dir(part_elem->data_file_name))
+ goto dd_err;
+ if (test_if_data_home_dir(part_elem->index_file_name))
+ goto id_err;
+ }
+ }
+ return 0;
+
+dd_err:
+ my_error(ER_WRONG_ARGUMENTS,MYF(0),"DATA DIRECTORY");
+ return 1;
+
+id_err:
+ my_error(ER_WRONG_ARGUMENTS,MYF(0),"INDEX DIRECTORY");
+ return 1;
+}
+
+
+#endif /* WITH_PARTITION_STORAGE_ENGINE */
diff --git a/sql/partition_info.h b/sql/partition_info.h
new file mode 100644
index 00000000000..415f955d5d4
--- /dev/null
+++ b/sql/partition_info.h
@@ -0,0 +1,314 @@
+/* Copyright 2006-2008 MySQL AB, 2008 Sun Microsystems, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#ifdef USE_PRAGMA_INTERFACE
+#pragma interface /* gcc class implementation */
+#endif
+
+#include "partition_element.h"
+
+class partition_info;
+
+/* Some function typedefs */
+typedef int (*get_part_id_func)(partition_info *part_info,
+ uint32 *part_id,
+ longlong *func_value);
+typedef int (*get_subpart_id_func)(partition_info *part_info,
+ uint32 *part_id);
+
+struct st_ddl_log_memory_entry;
+
+class partition_info : public Sql_alloc
+{
+public:
+ /*
+ * Here comes a set of definitions needed for partitioned table handlers.
+ */
+ List<partition_element> partitions;
+ List<partition_element> temp_partitions;
+
+ List<char> part_field_list;
+ List<char> subpart_field_list;
+
+ /*
+ If there is no subpartitioning, use only this func to get partition ids.
+ If there is subpartitioning, use the this func to get partition id when
+ you have both partition and subpartition fields.
+ */
+ get_part_id_func get_partition_id;
+
+ /* Get partition id when we don't have subpartition fields */
+ get_part_id_func get_part_partition_id;
+
+ /*
+ Get subpartition id when we have don't have partition fields by we do
+ have subpartition ids.
+ Mikael said that for given constant tuple
+ {subpart_field1, ..., subpart_fieldN} the subpartition id will be the
+ same in all subpartitions
+ */
+ get_subpart_id_func get_subpartition_id;
+
+ /*
+ 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.
+ */
+ 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;
+
+ /* NULL-terminated array of fields used in partitioned expression */
+ Field **part_field_array;
+ Field **subpart_field_array;
+ Field **part_charset_field_array;
+ Field **subpart_charset_field_array;
+ /*
+ Array of all fields used in partition and subpartition expression,
+ 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
+ updating. We need to set all bits in read_set because the row may
+ need to be inserted in a different [sub]partition.
+ */
+ MY_BITMAP full_part_field_set;
+
+ /*
+ When we have a field that requires transformation before calling the
+ partition functions we must allocate field buffers for the field of
+ the fields in the partition function.
+ */
+ 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;
+
+ Item *item_free_list;
+
+ struct st_ddl_log_memory_entry *first_log_entry;
+ struct st_ddl_log_memory_entry *exec_log_entry;
+ struct st_ddl_log_memory_entry *frm_log_entry;
+
+ /*
+ A bitmap of partitions used by the current query.
+ Usage pattern:
+ * The handler->extra(HA_EXTRA_RESET) call at query start/end sets all
+ partitions to be unused.
+ * Before index/rnd_init(), partition pruning code sets the bits for used
+ partitions.
+ */
+ MY_BITMAP used_partitions;
+
+ union {
+ longlong *range_int_array;
+ LIST_PART_ENTRY *list_array;
+ };
+
+ /********************************************
+ * INTERVAL ANALYSIS
+ ********************************************/
+ /*
+ Partitioning interval analysis function for partitioning, or NULL if
+ interval analysis is not supported for this kind of partitioning.
+ */
+ get_partitions_in_range_iter get_part_iter_for_interval;
+ /*
+ Partitioning interval analysis function for subpartitioning, or NULL if
+ interval analysis is not supported for this kind of partitioning.
+ */
+ get_partitions_in_range_iter get_subpart_iter_for_interval;
+
+ /********************************************
+ * INTERVAL ANALYSIS ENDS
+ ********************************************/
+
+ longlong err_value;
+ char* part_info_string;
+
+ char *part_func_string;
+ char *subpart_func_string;
+
+ const char *part_state;
+
+ partition_element *curr_part_elem;
+ partition_element *current_partition;
+ /*
+ These key_map's are used for Partitioning to enable quick decisions
+ on whether we can derive more information about which partition to
+ scan just by looking at what index is used.
+ */
+ key_map all_fields_in_PF, all_fields_in_PPF, all_fields_in_SPF;
+ 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 count_curr_subparts;
+
+ uint part_error_code;
+
+ uint no_list_values;
+
+ uint no_part_fields;
+ uint no_subpart_fields;
+ uint no_full_part_fields;
+
+ uint has_null_part_id;
+ /*
+ This variable is used to calculate the partition id when using
+ LINEAR KEY/HASH. This functionality is kept in the MySQL Server
+ but mainly of use to handlers supporting partitioning.
+ */
+ uint16 linear_hash_mask;
+
+ bool use_default_partitions;
+ bool use_default_no_partitions;
+ bool use_default_subpartitions;
+ bool use_default_no_subpartitions;
+ bool default_partitions_setup;
+ bool defined_max_value;
+ bool list_of_part_fields;
+ bool list_of_subpart_fields;
+ bool linear_hash_ind;
+ bool fixed;
+ bool is_auto_partitioned;
+ bool from_openfrm;
+ bool has_null_value;
+
+
+ partition_info()
+ : get_partition_id(NULL), get_part_partition_id(NULL),
+ get_subpartition_id(NULL),
+ part_field_array(NULL), subpart_field_array(NULL),
+ 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),
+ 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_func_len(0), subpart_func_len(0),
+ no_parts(0), no_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),
+ 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)
+ {
+ all_fields_in_PF.clear_all();
+ all_fields_in_PPF.clear_all();
+ all_fields_in_SPF.clear_all();
+ some_fields_in_PF.clear_all();
+ partitions.empty();
+ temp_partitions.empty();
+ part_field_list.empty();
+ subpart_field_list.empty();
+ }
+ ~partition_info() {}
+
+ partition_info *get_clone();
+ /* Answers the question if subpartitioning is used for a certain table */
+ bool is_sub_partitioned()
+ {
+ return (subpart_type == NOT_A_PARTITION ? FALSE : TRUE);
+ }
+
+ /* Returns the total number of partitions on the leaf level */
+ uint get_tot_partitions()
+ {
+ return no_parts * (is_sub_partitioned() ? no_subparts : 1);
+ }
+
+ bool set_up_defaults_for_partitioning(handler *file, HA_CREATE_INFO *info,
+ uint start_no);
+ char *has_unique_names();
+ bool check_engine_mix(handlerton *engine_type, bool default_engine);
+ bool check_range_constants();
+ bool check_list_constants();
+ bool check_partition_info(THD *thd, handlerton **eng_type,
+ handler *file, HA_CREATE_INFO *info,
+ bool check_partition_function);
+ void print_no_partition_found(TABLE *table);
+ bool set_up_charset_field_preps();
+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,
+ uint start_no);
+ char *create_subpartition_name(uint subpart_no, const char *part_name);
+ bool has_unique_name(partition_element *element);
+};
+
+uint32 get_next_partition_id_range(struct st_partition_iter* part_iter);
+bool check_partition_dirs(partition_info *part_info);
+
+/* Initialize the iterator to return a single partition with given part_id */
+
+static inline void init_single_partition_iterator(uint32 part_id,
+ PARTITION_ITERATOR *part_iter)
+{
+ part_iter->part_nums.start= part_iter->part_nums.cur= part_id;
+ part_iter->part_nums.end= part_id+1;
+ part_iter->get_next= get_next_partition_id_range;
+}
+
+/* Initialize the iterator to enumerate all partitions */
+static inline
+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->get_next= get_next_partition_id_range;
+}
diff --git a/sql/password.c b/sql/password.c
index 57ed3e6ab0f..1ff67888ea4 100644
--- a/sql/password.c
+++ b/sql/password.c
@@ -322,7 +322,7 @@ void create_random_string(char *to, uint length, struct rand_struct *rand_st)
char *octet2hex(char *to, const char *str, uint len)
{
- const byte *str_end= str + len;
+ const char *str_end= str + len;
for (; str != str_end; ++str)
{
*to++= _dig_vec_upper[((uchar) *str) >> 4];
@@ -405,7 +405,7 @@ make_scrambled_password(char *to, const char *password)
mysql_sha1_result(&sha1_context, hash_stage2);
/* convert hash_stage2 to hex string */
*to++= PVERSION41_CHAR;
- octet2hex(to, (char*) hash_stage2, SHA1_HASH_SIZE);
+ octet2hex(to, (const char*) hash_stage2, SHA1_HASH_SIZE);
}
@@ -520,5 +520,5 @@ void get_salt_from_password(uint8 *hash_stage2, const char *password)
void make_password_from_salt(char *to, const uint8 *hash_stage2)
{
*to++= PVERSION41_CHAR;
- octet2hex(to, (char*) hash_stage2, SHA1_HASH_SIZE);
+ octet2hex(to, (const char*) hash_stage2, SHA1_HASH_SIZE);
}
diff --git a/sql/procedure.cc b/sql/procedure.cc
index bbfabc46608..c993ba976ac 100644
--- a/sql/procedure.cc
+++ b/sql/procedure.cc
@@ -68,10 +68,13 @@ my_decimal *Item_proc_real::val_decimal(my_decimal *decimal_value)
}
-/*****************************************************************************
-** Setup handling of procedure
-** Return 0 if everything is ok
-*****************************************************************************/
+/**
+ Setup handling of procedure.
+
+ @return
+ Return 0 if everything is ok
+*/
+
Procedure *
setup_procedure(THD *thd,ORDER *param,select_result *result,
diff --git a/sql/procedure.h b/sql/procedure.h
index f1fb433a6ee..ceb586766b1 100644
--- a/sql/procedure.h
+++ b/sql/procedure.h
@@ -20,8 +20,8 @@
#pragma interface /* gcc class implementation */
#endif
-#define PROC_NO_SORT 1 /* Bits in flags */
-#define PROC_GROUP 2 /* proc must have group */
+#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 */
@@ -67,11 +67,11 @@ public:
longlong val_int() { return (longlong) value; }
String *val_str(String *s)
{
- s->set(value,decimals,default_charset());
+ s->set_real(value,decimals,default_charset());
return s;
}
my_decimal *val_decimal(my_decimal *);
- unsigned int size_of() { return sizeof(*this);}
+ unsigned int size_of() { return sizeof(*this);}
};
class Item_proc_int :public Item_proc
@@ -90,7 +90,7 @@ public:
longlong val_int() { return value; }
String *val_str(String *s) { s->set(value, default_charset()); return s; }
my_decimal *val_decimal(my_decimal *);
- unsigned int size_of() { return sizeof(*this);}
+ unsigned int size_of() { return sizeof(*this);}
};
@@ -101,12 +101,12 @@ public:
{ this->max_length=length; }
enum Item_result result_type () const { return STRING_RESULT; }
enum_field_types field_type() const { return MYSQL_TYPE_VARCHAR; }
- void set(double nr) { str_value.set(nr, 2, default_charset()); }
+ void set(double nr) { str_value.set_real(nr, 2, default_charset()); }
void set(longlong nr) { str_value.set(nr, default_charset()); }
void set(const char *str, uint length, CHARSET_INFO *cs)
{ str_value.copy(str,length,cs); }
double val_real()
- {
+ {
int err_not_used;
char *end_not_used;
CHARSET_INFO *cs= str_value.charset();
diff --git a/sql/protocol.cc b/sql/protocol.cc
index 996ede5d6a2..4177cd0054d 100644
--- a/sql/protocol.cc
+++ b/sql/protocol.cc
@@ -13,8 +13,10 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
-/*
- Low level functions for storing data to be send to the MySQL client
+/**
+ @file
+
+ Low level functions for storing data to be send to the MySQL client.
The actual communction is handled by the net_xxx functions in net_serv.cc
*/
@@ -23,19 +25,22 @@
#endif
#include "mysql_priv.h"
-#include "sp_rcontext.h"
#include <stdarg.h>
static const unsigned int PACKET_BUFFER_EXTRA_ALLOC= 1024;
+/* Declared non-static only because of the embedded library. */
void net_send_error_packet(THD *thd, uint sql_errno, const char *err);
+void net_send_ok(THD *, uint, uint, ha_rows, ulonglong, const char *);
+void net_send_eof(THD *thd, uint server_status, uint total_warn_count);
#ifndef EMBEDDED_LIBRARY
-static void write_eof_packet(THD *thd, NET *net);
+static void write_eof_packet(THD *thd, NET *net,
+ uint server_status, uint total_warn_count);
#endif
#ifndef EMBEDDED_LIBRARY
-bool Protocol::net_store_data(const char *from, uint length)
+bool Protocol::net_store_data(const uchar *from, size_t length)
#else
-bool Protocol_prep::net_store_data(const char *from, uint length)
+bool Protocol_binary::net_store_data(const uchar *from, size_t length)
#endif
{
ulong packet_length=packet->length();
@@ -46,238 +51,86 @@ bool Protocol_prep::net_store_data(const char *from, uint length)
if (packet_length+9+length > packet->alloced_length() &&
packet->realloc(packet_length+9+length))
return 1;
- char *to=(char*) net_store_length((char*) packet->ptr()+packet_length,
- length);
+ uchar *to= net_store_length((uchar*) packet->ptr()+packet_length, length);
memcpy(to,from,length);
- packet->length((uint) (to+length-packet->ptr()));
+ packet->length((uint) (to+length-(uchar*) packet->ptr()));
return 0;
}
-/*
- Send a error string to client
+/**
+ Send a error string to client.
+
+ Design note:
- Design note:
+ net_printf_error and net_send_error are low-level functions
+ that shall be used only when a new connection is being
+ established or at server startup.
- net_printf_error and net_send_error are low-level functions
- that shall be used only when a new connection is being
- established or at server startup.
- For SIGNAL/RESIGNAL and GET DIAGNOSTICS functionality it's
- critical that every error that can be intercepted is issued in one
- place only, my_message_sql.
+ For SIGNAL/RESIGNAL and GET DIAGNOSTICS functionality it's
+ critical that every error that can be intercepted is issued in one
+ place only, my_message_sql.
*/
void net_send_error(THD *thd, uint sql_errno, const char *err)
{
- NET *net= &thd->net;
- bool generate_warning= thd->killed != THD::KILL_CONNECTION;
DBUG_ENTER("net_send_error");
- DBUG_PRINT("enter",("sql_errno: %d err: %s", sql_errno,
- err ? err : net->last_error[0] ?
- net->last_error : "NULL"));
DBUG_ASSERT(!thd->spcont);
+ DBUG_ASSERT(sql_errno);
+ DBUG_ASSERT(err && err[0]);
- if (net && net->no_send_error)
- {
- thd->clear_error();
- DBUG_PRINT("info", ("sending error messages prohibited"));
- DBUG_VOID_RETURN;
- }
-
- thd->query_error= 1; // needed to catch query errors during replication
- if (!err)
- {
- if (sql_errno)
- err=ER(sql_errno);
- else
- {
- if ((err=net->last_error)[0])
- {
- sql_errno=net->last_errno;
- generate_warning= 0; // This warning has already been given
- }
- else
- {
- sql_errno=ER_UNKNOWN_ERROR;
- err=ER(sql_errno); /* purecov: inspected */
- }
- }
- }
+ DBUG_PRINT("enter",("sql_errno: %d err: %s", sql_errno, err));
- if (generate_warning)
- {
- /* Error that we have not got with my_error() */
- push_warning(thd, MYSQL_ERROR::WARN_LEVEL_ERROR, sql_errno, err);
- }
+ /*
+ 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;
/* Abort multi-result sets */
thd->server_status&= ~SERVER_MORE_RESULTS_EXISTS;
net_send_error_packet(thd, sql_errno, err);
- thd->is_fatal_error=0; // Error message is given
- thd->net.report_error= 0;
-
- DBUG_VOID_RETURN;
-}
-
-/*
- Write error package and flush to client
- It's a little too low level, but I don't want to use another buffer for
- this
-
- Design note:
-
- net_printf_error and net_send_error are low-level functions
- that shall be used only when a new connection is being
- established or at server startup.
- For SIGNAL/RESIGNAL and GET DIAGNOSTICS functionality it's
- critical that every error that can be intercepted is issued in one
- place only, my_message_sql.
-*/
-
-void
-net_printf_error(THD *thd, uint errcode, ...)
-{
- va_list args;
- uint length,offset;
- const char *format;
-#ifndef EMBEDDED_LIBRARY
- const char *text_pos;
- int head_length= NET_HEADER_SIZE;
-#else
- char text_pos[1024];
-#endif
- NET *net= &thd->net;
-
- DBUG_ENTER("net_printf_error");
- DBUG_PRINT("enter",("message: %u",errcode));
-
- DBUG_ASSERT(!thd->spcont);
-
- if (net && net->no_send_error)
- {
- thd->clear_error();
- DBUG_PRINT("info", ("sending error messages prohibited"));
- DBUG_VOID_RETURN;
- }
-
- thd->query_error= 1; // needed to catch query errors during replication
-#ifndef EMBEDDED_LIBRARY
- query_cache_abort(net); // Safety
-#endif
- va_start(args,errcode);
- /*
- The following is needed to make net_printf_error() work with 0 argument
- for errorcode and use the argument after that as the format string. This
- is useful for rare errors that are not worth the hassle to put in
- errmsg.sys, but at the same time, the message is not fixed text
- */
- if (errcode)
- format= ER(errcode);
- else
- {
- format=va_arg(args,char*);
- errcode= ER_UNKNOWN_ERROR;
- }
- offset= (net->return_errno ?
- ((thd->client_capabilities & CLIENT_PROTOCOL_41) ?
- 2+SQLSTATE_LENGTH+1 : 2) : 0);
-#ifndef EMBEDDED_LIBRARY
- text_pos=(char*) net->buff + head_length + offset + 1;
- length= (uint) ((char*)net->buff_end - text_pos);
-#else
- length=sizeof(text_pos)-1;
-#endif
- length=my_vsnprintf(my_const_cast(char*) (text_pos),
- min(length, sizeof(net->last_error)),
- format,args);
- va_end(args);
-
- /* Replication slave relies on net->last_* to see if there was error */
- net->last_errno= errcode;
- strmake(net->last_error, text_pos, sizeof(net->last_error)-1);
+ thd->main_da.can_overwrite_status= FALSE;
-#ifndef EMBEDDED_LIBRARY
- if (net->vio == 0)
- {
- if (thd->bootstrap)
- {
- /*
- In bootstrap it's ok to print on stderr
- This may also happen when we get an error from a slave thread
- */
- fprintf(stderr,"ERROR: %d %s\n",errcode,text_pos);
- thd->fatal_error();
- }
- DBUG_VOID_RETURN;
- }
-
- int3store(net->buff,length+1+offset);
- net->buff[3]= (net->compress) ? 0 : (uchar) (net->pkt_nr++);
- net->buff[head_length]=(uchar) 255; // Error package
- if (offset)
- {
- uchar *pos= net->buff+head_length+1;
- int2store(pos, errcode);
- if (thd->client_capabilities & CLIENT_PROTOCOL_41)
- {
- pos[2]= '#'; /* To make the protocol backward compatible */
- memcpy(pos+3, mysql_errno_to_sqlstate(errcode), SQLSTATE_LENGTH);
- }
- }
- VOID(net_real_write(net,(char*) net->buff,length+head_length+1+offset));
-#else
- net->last_errno= errcode;
- strmake(net->last_error, text_pos, length);
- strmake(net->sqlstate, mysql_errno_to_sqlstate(errcode), SQLSTATE_LENGTH);
-#endif
- if (thd->killed != THD::KILL_CONNECTION)
- push_warning(thd, MYSQL_ERROR::WARN_LEVEL_ERROR, errcode,
- text_pos ? text_pos : ER(errcode));
- thd->is_fatal_error=0; // Error message is given
DBUG_VOID_RETURN;
}
-/*
+/**
Return ok to the client.
- SYNOPSIS
- send_ok()
- thd Thread handler
- affected_rows Number of rows changed by statement
- id Auto_increment id for first row (if used)
- message Message to send to the client (Used by mysql_status)
-
- DESCRIPTION
- The ok packet has the following structure
-
- 0 Marker (1 byte)
- affected_rows Stored in 1-9 bytes
- id Stored in 1-9 bytes
- server_status Copy of thd->server_status; Can be used by client
- to check if we are inside an transaction
- New in 4.0 protocol
- warning_count Stored in 2 bytes; New in 4.1 protocol
- message Stored as packed length (1-9 bytes) + message
- Is not stored if no message
-
- If net->no_send_ok return without sending packet
-*/
+ The ok packet has the following structure:
+
+ - 0 : Marker (1 byte)
+ - affected_rows : Stored in 1-9 bytes
+ - id : Stored in 1-9 bytes
+ - server_status : Copy of thd->server_status; Can be used by client
+ to check if we are inside an transaction.
+ New in 4.0 protocol
+ - warning_count : Stored in 2 bytes; New in 4.1 protocol
+ - message : Stored as packed length (1-9 bytes) + message.
+ Is not stored if no message.
+
+ @param thd Thread handler
+ @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)
+*/
#ifndef EMBEDDED_LIBRARY
void
-send_ok(THD *thd, ha_rows affected_rows, ulonglong id, const char *message)
+net_send_ok(THD *thd,
+ uint server_status, uint total_warn_count,
+ ha_rows affected_rows, ulonglong id, const char *message)
{
NET *net= &thd->net;
- char buff[MYSQL_ERRMSG_SIZE+10],*pos;
- DBUG_ENTER("send_ok");
+ uchar buff[MYSQL_ERRMSG_SIZE+10],*pos;
+ DBUG_ENTER("my_ok");
- if (net->no_send_ok || !net->vio) // hack for re-parsing queries
+ if (! net->vio) // hack for re-parsing queries
{
- DBUG_PRINT("info", ("no send ok: %s, vio present: %s",
- (net->no_send_ok ? "YES" : "NO"),
- (net->vio ? "YES" : "NO")));
+ DBUG_PRINT("info", ("vio present: NO"));
DBUG_VOID_RETURN;
}
@@ -290,79 +143,81 @@ send_ok(THD *thd, ha_rows affected_rows, ulonglong id, const char *message)
("affected_rows: %lu id: %lu status: %u warning_count: %u",
(ulong) affected_rows,
(ulong) id,
- (uint) (thd->server_status & 0xffff),
- (uint) thd->total_warn_count));
- int2store(pos,thd->server_status);
+ (uint) (server_status & 0xffff),
+ (uint) total_warn_count));
+ int2store(pos, server_status);
pos+=2;
/* We can only return up to 65535 warnings in two bytes */
- uint tmp= min(thd->total_warn_count, 65535);
+ uint tmp= min(total_warn_count, 65535);
int2store(pos, tmp);
pos+= 2;
}
else if (net->return_status) // For 4.0 protocol
{
- int2store(pos,thd->server_status);
+ int2store(pos, server_status);
pos+=2;
}
- if (message)
- pos=net_store_data((char*) pos, message, (uint) strlen(message));
- VOID(my_net_write(net,buff,(uint) (pos-buff)));
+ thd->main_da.can_overwrite_status= TRUE;
+
+ if (message && message[0])
+ pos= net_store_data(pos, (uchar*) message, strlen(message));
+ VOID(my_net_write(net, buff, (size_t) (pos-buff)));
VOID(net_flush(net));
- /* We can't anymore send an error to the client */
- thd->net.report_error= 0;
- thd->net.no_send_error= 1;
+
+ thd->main_da.can_overwrite_status= FALSE;
DBUG_PRINT("info", ("OK sent, so no more error sending allowed"));
DBUG_VOID_RETURN;
}
-static char eof_buff[1]= { (char) 254 }; /* Marker for end of fields */
+static uchar eof_buff[1]= { (uchar) 254 }; /* Marker for end of fields */
-/*
- Send eof (= end of result set) to the client
+/**
+ Send eof (= end of result set) to the client.
- SYNOPSIS
- send_eof()
- thd Thread handler
- no_flush Set to 1 if there will be more data to the client,
- like in send_fields().
+ The eof packet has the following structure:
- DESCRIPTION
- The eof packet has the following structure
+ - 254 : Marker (1 byte)
+ - warning_count : Stored in 2 bytes; New in 4.1 protocol
+ - status_flag : Stored in 2 bytes;
+ For flags like SERVER_MORE_RESULTS_EXISTS.
- 254 Marker (1 byte)
- warning_count Stored in 2 bytes; New in 4.1 protocol
- status_flag Stored in 2 bytes;
- For flags like SERVER_MORE_RESULTS_EXISTS
+ Note that the warning count will not be sent if 'no_flush' is set as
+ we don't want to report the warning count until all data is sent to the
+ client.
- Note that the warning count will not be sent if 'no_flush' is set as
- we don't want to report the warning count until all data is sent to the
- client.
+ @param thd Thread handler
+ @param no_flush Set to 1 if there will be more data to the client,
+ like in send_fields().
*/
void
-send_eof(THD *thd)
+net_send_eof(THD *thd, uint server_status, uint total_warn_count)
{
NET *net= &thd->net;
- DBUG_ENTER("send_eof");
- if (net->vio != 0 && !net->no_send_eof)
+ DBUG_ENTER("net_send_eof");
+ /* Set to TRUE if no active vio, to work well in case of --init-file */
+ if (net->vio != 0)
{
- write_eof_packet(thd, net);
+ thd->main_da.can_overwrite_status= TRUE;
+ write_eof_packet(thd, net, server_status, total_warn_count);
VOID(net_flush(net));
- thd->net.no_send_error= 1;
+ thd->main_da.can_overwrite_status= FALSE;
DBUG_PRINT("info", ("EOF sent, so no more error sending allowed"));
}
DBUG_VOID_RETURN;
}
-/*
+/**
Format EOF packet according to the current protocol and
write it to the network output buffer.
*/
-static void write_eof_packet(THD *thd, NET *net)
+static void write_eof_packet(THD *thd, NET *net,
+ uint server_status,
+ uint total_warn_count)
{
if (thd->client_capabilities & CLIENT_PROTOCOL_41)
{
@@ -371,7 +226,7 @@ static void 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= (thd->spcont ? 0 : min(thd->total_warn_count, 65535));
+ uint tmp= min(total_warn_count, 65535);
buff[0]= 254;
int2store(buff+1, tmp);
/*
@@ -380,22 +235,22 @@ static void write_eof_packet(THD *thd, NET *net)
other queries (see the if test in dispatch_command / COM_QUERY)
*/
if (thd->is_fatal_error)
- thd->server_status&= ~SERVER_MORE_RESULTS_EXISTS;
- int2store(buff+3, thd->server_status);
- VOID(my_net_write(net, (char*) buff, 5));
+ server_status&= ~SERVER_MORE_RESULTS_EXISTS;
+ int2store(buff + 3, server_status);
+ VOID(my_net_write(net, buff, 5));
}
else
VOID(my_net_write(net, eof_buff, 1));
}
-/*
- Please client to send scrambled_password in old format.
- SYNOPSYS
- send_old_password_request()
- thd thread handle
+/**
+ Please client to send scrambled_password in old format.
- RETURN VALUE
+ @param thd thread handle
+
+ @retval
0 ok
+ @retval
!0 error
*/
@@ -413,7 +268,7 @@ void net_send_error_packet(THD *thd, uint sql_errno, const char *err)
/*
buff[]: sql_errno:2 + ('#':1 + SQLSTATE_LENGTH:5) + MYSQL_ERRMSG_SIZE:512
*/
- char buff[2+1+SQLSTATE_LENGTH+MYSQL_ERRMSG_SIZE], *pos;
+ uchar buff[2+1+SQLSTATE_LENGTH+MYSQL_ERRMSG_SIZE], *pos;
DBUG_ENTER("send_error_packet");
@@ -435,61 +290,153 @@ void net_send_error_packet(THD *thd, uint sql_errno, const char *err)
{
/* The first # is to make the protocol backward compatible */
buff[2]= '#';
- pos= strmov(buff+3, mysql_errno_to_sqlstate(sql_errno));
+ pos= (uchar*) strmov((char*) buff+3, mysql_errno_to_sqlstate(sql_errno));
}
- length= (uint) (strmake(pos, err, MYSQL_ERRMSG_SIZE-1) - buff);
- err=buff;
+ length= (uint) (strmake((char*) pos, err, MYSQL_ERRMSG_SIZE-1) -
+ (char*) buff);
+ err= (char*) buff;
}
else
{
length=(uint) strlen(err);
set_if_smaller(length,MYSQL_ERRMSG_SIZE-1);
}
- VOID(net_write_command(net,(uchar) 255, "", 0, (char*) err,length));
+ VOID(net_write_command(net,(uchar) 255, (uchar*) "", 0, (uchar*) err,
+ length));
DBUG_VOID_RETURN;
}
#endif /* EMBEDDED_LIBRARY */
-/*
+/**
Faster net_store_length when we know that length is less than 65536.
We keep a separate version for that range because it's widely used in
libmysql.
+
uint is used as agrument type because of MySQL type conventions:
- uint for 0..65536
- ulong for 0..4294967296
- ulonglong for bigger numbers.
+ - uint for 0..65536
+ - ulong for 0..4294967296
+ - ulonglong for bigger numbers.
*/
-static char *net_store_length_fast(char *pkg, uint length)
+static uchar *net_store_length_fast(uchar *packet, uint length)
{
- uchar *packet=(uchar*) pkg;
if (length < 251)
{
*packet=(uchar) length;
- return (char*) packet+1;
+ return packet+1;
}
*packet++=252;
int2store(packet,(uint) length);
- return (char*) packet+2;
+ return packet+2;
+}
+
+/**
+ Send the status of the current statement execution over network.
+
+ @param thd in fact, carries two parameters, NET for the transport and
+ Diagnostics_area as the source of status information.
+
+ In MySQL, there are two types of SQL statements: those that return
+ a result set and those that return status information only.
+
+ If a statement returns a result set, it consists of 3 parts:
+ - result set meta-data
+ - variable number of result set rows (can be 0)
+ - followed and terminated by EOF or ERROR packet
+
+ Once the client has seen the meta-data information, it always
+ expects an EOF or ERROR to terminate the result set. If ERROR is
+ received, the result set rows are normally discarded (this is up
+ to the client implementation, libmysql at least does discard them).
+ EOF, on the contrary, means "successfully evaluated the entire
+ result set". Since we don't know how many rows belong to a result
+ set until it's evaluated, EOF/ERROR is the indicator of the end
+ of the row stream. Note, that we can not buffer result set rows
+ on the server -- there may be an arbitrary number of rows. But
+ we do buffer the last packet (EOF/ERROR) in the Diagnostics_area and
+ delay sending it till the very end of execution (here), to be able to
+ change EOF to an ERROR if commit failed or some other error occurred
+ during the last cleanup steps taken after execution.
+
+ A statement that does not return a result set doesn't send result
+ set meta-data either. Instead it returns one of:
+ - OK packet
+ - ERROR packet.
+ Similarly to the EOF/ERROR of the previous statement type, OK/ERROR
+ packet is "buffered" in the diagnostics area and sent to the client
+ in the end of statement.
+
+ @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.
+
+ @post The status information is encoded to protocol format and sent to the
+ client.
+
+ @return We conventionally return void, since the only type of error
+ that can happen here is a NET (transport) error, and that one
+ will become visible when we attempt to read from the NET the
+ next command.
+ Diagnostics_area::is_sent is set for debugging purposes only.
+*/
+
+void net_end_statement(THD *thd)
+{
+ DBUG_ASSERT(! thd->main_da.is_sent);
+
+ /* Can not be true, but do not take chances in production. */
+ if (thd->main_da.is_sent)
+ return;
+
+ switch (thd->main_da.status()) {
+ case Diagnostics_area::DA_ERROR:
+ /* The query failed, send error to log and abort bootstrap. */
+ net_send_error(thd,
+ thd->main_da.sql_errno(),
+ thd->main_da.message());
+ break;
+ case Diagnostics_area::DA_EOF:
+ net_send_eof(thd,
+ thd->main_da.server_status(),
+ thd->main_da.total_warn_count());
+ break;
+ case Diagnostics_area::DA_OK:
+ 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());
+ break;
+ case Diagnostics_area::DA_DISABLED:
+ break;
+ case Diagnostics_area::DA_EMPTY:
+ default:
+ DBUG_ASSERT(0);
+ net_send_ok(thd, thd->server_status, thd->total_warn_count,
+ 0, 0, NULL);
+ break;
+ }
+ thd->main_da.is_sent= TRUE;
}
/****************************************************************************
- Functions used by the protocol functions (like send_ok) to store strings
- and numbers in the header result packet.
+ Functions used by the protocol functions (like net_send_ok) to store
+ strings and numbers in the header result packet.
****************************************************************************/
/* The following will only be used for short strings < 65K */
-char *net_store_data(char *to,const char *from, uint length)
+uchar *net_store_data(uchar *to, const uchar *from, size_t length)
{
to=net_store_length_fast(to,length);
memcpy(to,from,length);
return to+length;
}
-char *net_store_data(char *to,int32 from)
+uchar *net_store_data(uchar *to,int32 from)
{
char buff[20];
uint length=(uint) (int10_to_str(from,buff,10)-buff);
@@ -498,7 +445,7 @@ char *net_store_data(char *to,int32 from)
return to+length;
}
-char *net_store_data(char *to,longlong from)
+uchar *net_store_data(uchar *to,longlong from)
{
char buff[22];
uint length=(uint) (longlong10_to_str(from,buff,10)-buff);
@@ -522,6 +469,17 @@ void Protocol::init(THD *thd_arg)
#endif
}
+/**
+ Finish the result set with EOF packet, as is expected by the client,
+ if there is an error evaluating the next row and a continue handler
+ for the error.
+*/
+
+void Protocol::end_partial_result_set(THD *thd)
+{
+ net_send_eof(thd, thd->server_status, 0 /* no warnings, we're inside SP */);
+}
+
bool Protocol::flush()
{
@@ -532,42 +490,41 @@ bool Protocol::flush()
#endif
}
-/*
+#ifndef EMBEDDED_LIBRARY
+
+/**
Send name and type of result to client.
- SYNOPSIS
- send_fields()
- THD Thread data object
- list List of items to send to client
- flag Bit mask with the following functions:
- 1 send number of rows
- 2 send default values
- 4 don't write eof packet
+ Sum fields has table name empty and field_name.
- DESCRIPTION
- Sum fields has table name empty and field_name.
+ @param THD Thread data object
+ @param list List of items to send to client
+ @param flag Bit mask with the following functions:
+ - 1 send number of rows
+ - 2 send default values
+ - 4 don't write eof packet
- RETURN VALUES
+ @retval
0 ok
- 1 Error (Note that in this case the error is not sent to the client)
+ @retval
+ 1 Error (Note that in this case the error is not sent to the
+ client)
*/
-
-#ifndef EMBEDDED_LIBRARY
bool Protocol::send_fields(List<Item> *list, uint flags)
{
List_iterator_fast<Item> it(*list);
Item *item;
- char buff[80];
+ uchar buff[80];
String tmp((char*) buff,sizeof(buff),&my_charset_bin);
- Protocol_simple prot(thd);
+ Protocol_text prot(thd);
String *local_packet= prot.storage_packet();
CHARSET_INFO *thd_charset= thd->variables.character_set_results;
DBUG_ENTER("send_fields");
if (flags & SEND_NUM_ROWS)
{ // Packet with number of elements
- char *pos=net_store_length(buff, list->elements);
- (void) my_net_write(&thd->net, buff,(uint) (pos-buff));
+ uchar *pos= net_store_length(buff, list->elements);
+ (void) my_net_write(&thd->net, buff, (size_t) (pos-buff));
}
#ifndef DBUG_OFF
@@ -687,7 +644,14 @@ bool Protocol::send_fields(List<Item> *list, uint flags)
}
if (flags & SEND_EOF)
- write_eof_packet(thd, &thd->net);
+ {
+ /*
+ Mark the end of meta-data result set, and store thd->server_status,
+ to show that there is no cursor.
+ Send no warning information, as it will be sent at statement end.
+ */
+ write_eof_packet(thd, &thd->net, thd->server_status, thd->total_warn_count);
+ }
DBUG_RETURN(prepare_for_send(list));
err:
@@ -700,23 +664,23 @@ err:
bool Protocol::write()
{
DBUG_ENTER("Protocol::write");
- DBUG_RETURN(my_net_write(&thd->net, packet->ptr(), packet->length()));
+ DBUG_RETURN(my_net_write(&thd->net, (uchar*) packet->ptr(),
+ packet->length()));
}
#endif /* EMBEDDED_LIBRARY */
-/*
- Send \0 end terminated string
+/**
+ Send \\0 end terminated string.
- SYNOPSIS
- store()
- from NullS or \0 terminated string
+ @param from NullS or \\0 terminated string
- NOTES
+ @note
In most cases one should use store(from, length) instead of this function
- RETURN VALUES
+ @retval
0 ok
+ @retval
1 error
*/
@@ -724,13 +688,13 @@ bool Protocol::store(const char *from, CHARSET_INFO *cs)
{
if (!from)
return store_null();
- size_t length= strlen(from);
- return store(from, (uint) length, cs);
+ uint length= strlen(from);
+ return store(from, length, cs);
}
-/*
- Send a set of strings as one long string with ',' in between
+/**
+ Send a set of strings as one long string with ',' in between.
*/
bool Protocol::store(I_List<i_string>* str_list)
@@ -762,7 +726,7 @@ bool Protocol::store(I_List<i_string>* str_list)
****************************************************************************/
#ifndef EMBEDDED_LIBRARY
-void Protocol_simple::prepare_for_resend()
+void Protocol_text::prepare_for_resend()
{
packet->length(0);
#ifndef DBUG_OFF
@@ -770,7 +734,7 @@ void Protocol_simple::prepare_for_resend()
#endif
}
-bool Protocol_simple::store_null()
+bool Protocol_text::store_null()
{
#ifndef DBUG_OFF
field_pos++;
@@ -782,12 +746,12 @@ bool Protocol_simple::store_null()
#endif
-/*
+/**
Auxilary function to convert string to the given character set
and store in network buffer.
*/
-bool Protocol::store_string_aux(const char *from, uint length,
+bool Protocol::store_string_aux(const char *from, size_t length,
CHARSET_INFO *fromcs, CHARSET_INFO *tocs)
{
/* 'tocs' is set 0 when client issues SET character_set_results=NULL */
@@ -796,15 +760,15 @@ bool Protocol::store_string_aux(const char *from, uint length,
tocs != &my_charset_bin)
{
uint dummy_errors;
- return convert->copy(from, length, fromcs, tocs, &dummy_errors) ||
- net_store_data(convert->ptr(), convert->length());
+ return (convert->copy(from, length, fromcs, tocs, &dummy_errors) ||
+ net_store_data((uchar*) convert->ptr(), convert->length()));
}
- return net_store_data(from, length);
+ return net_store_data((uchar*) from, length);
}
-bool Protocol_simple::store(const char *from, uint length,
- CHARSET_INFO *fromcs, CHARSET_INFO *tocs)
+bool Protocol_text::store(const char *from, size_t length,
+ CHARSET_INFO *fromcs, CHARSET_INFO *tocs)
{
#ifndef DBUG_OFF
DBUG_ASSERT(field_types == 0 ||
@@ -819,11 +783,14 @@ bool Protocol_simple::store(const char *from, uint length,
}
-bool Protocol_simple::store(const char *from, uint length,
- CHARSET_INFO *fromcs)
+bool Protocol_text::store(const char *from, size_t length,
+ CHARSET_INFO *fromcs)
{
CHARSET_INFO *tocs= this->thd->variables.character_set_results;
#ifndef DBUG_OFF
+ DBUG_PRINT("info", ("Protocol_text::store field %u (%u): %s", field_pos,
+ field_count, from));
+ DBUG_ASSERT(field_pos < field_count);
DBUG_ASSERT(field_types == 0 ||
field_types[field_pos] == MYSQL_TYPE_DECIMAL ||
field_types[field_pos] == MYSQL_TYPE_BIT ||
@@ -837,19 +804,19 @@ bool Protocol_simple::store(const char *from, uint length,
}
-bool Protocol_simple::store_tiny(longlong from)
+bool Protocol_text::store_tiny(longlong from)
{
#ifndef DBUG_OFF
DBUG_ASSERT(field_types == 0 || field_types[field_pos] == MYSQL_TYPE_TINY);
field_pos++;
#endif
char buff[20];
- return net_store_data((char*) buff,
- (uint) (int10_to_str((int) from,buff, -10)-buff));
+ return net_store_data((uchar*) buff,
+ (size_t) (int10_to_str((int) from, buff, -10) - buff));
}
-bool Protocol_simple::store_short(longlong from)
+bool Protocol_text::store_short(longlong from)
{
#ifndef DBUG_OFF
DBUG_ASSERT(field_types == 0 ||
@@ -858,12 +825,13 @@ bool Protocol_simple::store_short(longlong from)
field_pos++;
#endif
char buff[20];
- return net_store_data((char*) buff,
- (uint) (int10_to_str((int) from,buff, -10)-buff));
+ return net_store_data((uchar*) buff,
+ (size_t) (int10_to_str((int) from, buff, -10) -
+ buff));
}
-bool Protocol_simple::store_long(longlong from)
+bool Protocol_text::store_long(longlong from)
{
#ifndef DBUG_OFF
DBUG_ASSERT(field_types == 0 ||
@@ -872,12 +840,13 @@ bool Protocol_simple::store_long(longlong from)
field_pos++;
#endif
char buff[20];
- return net_store_data((char*) buff,
- (uint) (int10_to_str((long int)from,buff, (from <0)?-10:10)-buff));
+ return net_store_data((uchar*) buff,
+ (size_t) (int10_to_str((long int)from, buff,
+ (from <0)?-10:10)-buff));
}
-bool Protocol_simple::store_longlong(longlong from, bool unsigned_flag)
+bool Protocol_text::store_longlong(longlong from, bool unsigned_flag)
{
#ifndef DBUG_OFF
DBUG_ASSERT(field_types == 0 ||
@@ -885,14 +854,14 @@ bool Protocol_simple::store_longlong(longlong from, bool unsigned_flag)
field_pos++;
#endif
char buff[22];
- return net_store_data((char*) buff,
- (uint) (longlong10_to_str(from,buff,
- unsigned_flag ? 10 : -10)-
- buff));
+ return net_store_data((uchar*) buff,
+ (size_t) (longlong10_to_str(from,buff,
+ unsigned_flag ? 10 : -10)-
+ buff));
}
-bool Protocol_simple::store_decimal(const my_decimal *d)
+bool Protocol_text::store_decimal(const my_decimal *d)
{
#ifndef DBUG_OFF
DBUG_ASSERT(field_types == 0 ||
@@ -902,35 +871,35 @@ bool Protocol_simple::store_decimal(const my_decimal *d)
char buff[DECIMAL_MAX_STR_LENGTH];
String str(buff, sizeof(buff), &my_charset_bin);
(void) my_decimal2string(E_DEC_FATAL_ERROR, d, 0, 0, 0, &str);
- return net_store_data(str.ptr(), str.length());
+ return net_store_data((uchar*) str.ptr(), str.length());
}
-bool Protocol_simple::store(float from, uint32 decimals, String *buffer)
+bool Protocol_text::store(float from, uint32 decimals, String *buffer)
{
#ifndef DBUG_OFF
DBUG_ASSERT(field_types == 0 ||
field_types[field_pos] == MYSQL_TYPE_FLOAT);
field_pos++;
#endif
- buffer->set((double) from, decimals, thd->charset());
- return net_store_data((char*) buffer->ptr(), buffer->length());
+ buffer->set_real((double) from, decimals, thd->charset());
+ return net_store_data((uchar*) buffer->ptr(), buffer->length());
}
-bool Protocol_simple::store(double from, uint32 decimals, String *buffer)
+bool Protocol_text::store(double from, uint32 decimals, String *buffer)
{
#ifndef DBUG_OFF
DBUG_ASSERT(field_types == 0 ||
field_types[field_pos] == MYSQL_TYPE_DOUBLE);
field_pos++;
#endif
- buffer->set(from, decimals, thd->charset());
- return net_store_data((char*) buffer->ptr(), buffer->length());
+ buffer->set_real(from, decimals, thd->charset());
+ return net_store_data((uchar*) buffer->ptr(), buffer->length());
}
-bool Protocol_simple::store(Field *field)
+bool Protocol_text::store(Field *field)
{
if (field->is_null())
return store_null();
@@ -940,20 +909,30 @@ bool Protocol_simple::store(Field *field)
char buff[MAX_FIELD_WIDTH];
String str(buff,sizeof(buff), &my_charset_bin);
CHARSET_INFO *tocs= this->thd->variables.character_set_results;
+#ifndef DBUG_OFF
+ TABLE *table= field->table;
+ my_bitmap_map *old_map= 0;
+ if (table->file)
+ old_map= dbug_tmp_use_all_columns(table, table->read_set);
+#endif
field->val_str(&str);
+#ifndef DBUG_OFF
+ if (old_map)
+ dbug_tmp_restore_column_map(table->read_set, old_map);
+#endif
+
return store_string_aux(str.ptr(), str.length(), str.charset(), tocs);
}
-/*
- TODO:
- Second_part format ("%06") needs to change when
- we support 0-6 decimals for time.
+/**
+ @todo
+ Second_part format ("%06") needs to change when
+ we support 0-6 decimals for time.
*/
-
-bool Protocol_simple::store(MYSQL_TIME *tm)
+bool Protocol_text::store(MYSQL_TIME *tm)
{
#ifndef DBUG_OFF
DBUG_ASSERT(field_types == 0 ||
@@ -971,12 +950,13 @@ bool Protocol_simple::store(MYSQL_TIME *tm)
(int) tm->minute,
(int) tm->second));
if (tm->second_part)
- length+= my_sprintf(buff+length,(buff+length, ".%06d", (int)tm->second_part));
- return net_store_data((char*) buff, length);
+ length+= my_sprintf(buff+length,(buff+length, ".%06d",
+ (int)tm->second_part));
+ return net_store_data((uchar*) buff, length);
}
-bool Protocol_simple::store_date(MYSQL_TIME *tm)
+bool Protocol_text::store_date(MYSQL_TIME *tm)
{
#ifndef DBUG_OFF
DBUG_ASSERT(field_types == 0 ||
@@ -984,18 +964,18 @@ bool Protocol_simple::store_date(MYSQL_TIME *tm)
field_pos++;
#endif
char buff[MAX_DATE_STRING_REP_LENGTH];
- int length= my_date_to_str(tm, buff);
- return net_store_data(buff, (uint) length);
+ size_t length= my_date_to_str(tm, buff);
+ return net_store_data((uchar*) buff, length);
}
-/*
- TODO:
- Second_part format ("%06") needs to change when
- we support 0-6 decimals for time.
+/**
+ @todo
+ Second_part format ("%06") needs to change when
+ we support 0-6 decimals for time.
*/
-bool Protocol_simple::store_time(MYSQL_TIME *tm)
+bool Protocol_text::store_time(MYSQL_TIME *tm)
{
#ifndef DBUG_OFF
DBUG_ASSERT(field_types == 0 ||
@@ -1012,7 +992,7 @@ bool Protocol_simple::store_time(MYSQL_TIME *tm)
(int) tm->second));
if (tm->second_part)
length+= my_sprintf(buff+length,(buff+length, ".%06d", (int)tm->second_part));
- return net_store_data((char*) buff, length);
+ return net_store_data((uchar*) buff, length);
}
@@ -1035,7 +1015,7 @@ bool Protocol_simple::store_time(MYSQL_TIME *tm)
[..]..[[length]data] data
****************************************************************************/
-bool Protocol_prep::prepare_for_send(List<Item> *item_list)
+bool Protocol_binary::prepare_for_send(List<Item> *item_list)
{
Protocol::prepare_for_send(item_list);
bit_fields= (field_count+9)/8;
@@ -1046,29 +1026,30 @@ bool Protocol_prep::prepare_for_send(List<Item> *item_list)
}
-void Protocol_prep::prepare_for_resend()
+void Protocol_binary::prepare_for_resend()
{
packet->length(bit_fields+1);
- bzero((char*) packet->ptr(), 1+bit_fields);
+ bzero((uchar*) packet->ptr(), 1+bit_fields);
field_pos=0;
}
-bool Protocol_prep::store(const char *from, uint length, CHARSET_INFO *fromcs)
+bool Protocol_binary::store(const char *from, size_t length,
+ CHARSET_INFO *fromcs)
{
CHARSET_INFO *tocs= thd->variables.character_set_results;
field_pos++;
return store_string_aux(from, length, fromcs, tocs);
}
-bool Protocol_prep::store(const char *from,uint length,
- CHARSET_INFO *fromcs, CHARSET_INFO *tocs)
+bool Protocol_binary::store(const char *from, size_t length,
+ CHARSET_INFO *fromcs, CHARSET_INFO *tocs)
{
field_pos++;
return store_string_aux(from, length, fromcs, tocs);
}
-bool Protocol_prep::store_null()
+bool Protocol_binary::store_null()
{
uint offset= (field_pos+2)/8+1, bit= (1 << ((field_pos+2) & 7));
/* Room for this as it's allocated in prepare_for_send */
@@ -1079,7 +1060,7 @@ bool Protocol_prep::store_null()
}
-bool Protocol_prep::store_tiny(longlong from)
+bool Protocol_binary::store_tiny(longlong from)
{
char buff[1];
field_pos++;
@@ -1088,7 +1069,7 @@ bool Protocol_prep::store_tiny(longlong from)
}
-bool Protocol_prep::store_short(longlong from)
+bool Protocol_binary::store_short(longlong from)
{
field_pos++;
char *to= packet->prep_append(2, PACKET_BUFFER_EXTRA_ALLOC);
@@ -1099,7 +1080,7 @@ bool Protocol_prep::store_short(longlong from)
}
-bool Protocol_prep::store_long(longlong from)
+bool Protocol_binary::store_long(longlong from)
{
field_pos++;
char *to= packet->prep_append(4, PACKET_BUFFER_EXTRA_ALLOC);
@@ -1110,7 +1091,7 @@ bool Protocol_prep::store_long(longlong from)
}
-bool Protocol_prep::store_longlong(longlong from, bool unsigned_flag)
+bool Protocol_binary::store_longlong(longlong from, bool unsigned_flag)
{
field_pos++;
char *to= packet->prep_append(8, PACKET_BUFFER_EXTRA_ALLOC);
@@ -1120,7 +1101,7 @@ bool Protocol_prep::store_longlong(longlong from, bool unsigned_flag)
return 0;
}
-bool Protocol_prep::store_decimal(const my_decimal *d)
+bool Protocol_binary::store_decimal(const my_decimal *d)
{
#ifndef DBUG_OFF
DBUG_ASSERT(field_types == 0 ||
@@ -1133,7 +1114,7 @@ bool Protocol_prep::store_decimal(const my_decimal *d)
return store(str.ptr(), str.length(), str.charset());
}
-bool Protocol_prep::store(float from, uint32 decimals, String *buffer)
+bool Protocol_binary::store(float from, uint32 decimals, String *buffer)
{
field_pos++;
char *to= packet->prep_append(4, PACKET_BUFFER_EXTRA_ALLOC);
@@ -1144,7 +1125,7 @@ bool Protocol_prep::store(float from, uint32 decimals, String *buffer)
}
-bool Protocol_prep::store(double from, uint32 decimals, String *buffer)
+bool Protocol_binary::store(double from, uint32 decimals, String *buffer)
{
field_pos++;
char *to= packet->prep_append(8, PACKET_BUFFER_EXTRA_ALLOC);
@@ -1155,7 +1136,7 @@ bool Protocol_prep::store(double from, uint32 decimals, String *buffer)
}
-bool Protocol_prep::store(Field *field)
+bool Protocol_binary::store(Field *field)
{
/*
We should not increment field_pos here as send_binary() will call another
@@ -1167,7 +1148,7 @@ bool Protocol_prep::store(Field *field)
}
-bool Protocol_prep::store(MYSQL_TIME *tm)
+bool Protocol_binary::store(MYSQL_TIME *tm)
{
char buff[12],*pos;
uint length;
@@ -1193,15 +1174,15 @@ bool Protocol_prep::store(MYSQL_TIME *tm)
return packet->append(buff, length+1, PACKET_BUFFER_EXTRA_ALLOC);
}
-bool Protocol_prep::store_date(MYSQL_TIME *tm)
+bool Protocol_binary::store_date(MYSQL_TIME *tm)
{
tm->hour= tm->minute= tm->second=0;
tm->second_part= 0;
- return Protocol_prep::store(tm);
+ return Protocol_binary::store(tm);
}
-bool Protocol_prep::store_time(MYSQL_TIME *tm)
+bool Protocol_binary::store_time(MYSQL_TIME *tm)
{
char buff[13], *pos;
uint length;
diff --git a/sql/protocol.h b/sql/protocol.h
index f8e7f490d1b..a4770e9b6e3 100644
--- a/sql/protocol.h
+++ b/sql/protocol.h
@@ -35,14 +35,14 @@ protected:
#endif
uint field_count;
#ifndef EMBEDDED_LIBRARY
- bool net_store_data(const char *from, uint length);
+ bool net_store_data(const uchar *from, size_t length);
#else
- virtual bool net_store_data(const char *from, uint length);
+ virtual bool net_store_data(const uchar *from, size_t length);
char **next_field;
MYSQL_FIELD *next_mysql_field;
MEM_ROOT *alloc;
#endif
- bool store_string_aux(const char *from, uint length,
+ bool store_string_aux(const char *from, size_t length,
CHARSET_INFO *fromcs, CHARSET_INFO *tocs);
public:
Protocol() {}
@@ -58,6 +58,8 @@ public:
String *storage_packet() { return packet; }
inline void free() { packet->free(); }
virtual bool write();
+ inline bool store(int from)
+ { return store_long((longlong) from); }
inline bool store(uint32 from)
{ return store_long((longlong) from); }
inline bool store(longlong from)
@@ -73,6 +75,7 @@ public:
return 0;
}
virtual bool flush();
+ virtual void end_partial_result_set(THD *thd);
virtual void prepare_for_resend()=0;
virtual bool store_null()=0;
@@ -81,8 +84,8 @@ public:
virtual bool store_long(longlong from)=0;
virtual bool store_longlong(longlong from, bool unsigned_flag)=0;
virtual bool store_decimal(const my_decimal *)=0;
- virtual bool store(const char *from, uint length, CHARSET_INFO *cs)=0;
- virtual bool store(const char *from, uint length,
+ virtual bool store(const char *from, size_t length, CHARSET_INFO *cs)=0;
+ virtual bool store(const char *from, size_t length,
CHARSET_INFO *fromcs, CHARSET_INFO *tocs)=0;
virtual bool store(float from, uint32 decimals, String *buffer)=0;
virtual bool store(double from, uint32 decimals, String *buffer)=0;
@@ -96,16 +99,25 @@ public:
#else
void remove_last_row() {}
#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.
+ */
+ };
+ virtual enum enum_protocol_type type()= 0;
};
-/* Class used for the old (MySQL 4.0 protocol) */
+/** Class used for the old (MySQL 4.0 protocol). */
-class Protocol_simple :public Protocol
+class Protocol_text :public Protocol
{
public:
- Protocol_simple() {}
- Protocol_simple(THD *thd_arg) :Protocol(thd_arg) {}
+ Protocol_text() {}
+ Protocol_text(THD *thd_arg) :Protocol(thd_arg) {}
virtual void prepare_for_resend();
virtual bool store_null();
virtual bool store_tiny(longlong from);
@@ -113,8 +125,8 @@ public:
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, uint length, CHARSET_INFO *cs);
- virtual bool store(const char *from, uint length,
+ 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);
virtual bool store_date(MYSQL_TIME *time);
@@ -125,21 +137,22 @@ public:
#ifdef EMBEDDED_LIBRARY
void remove_last_row();
#endif
+ virtual enum enum_protocol_type type() { return PROTOCOL_TEXT; };
};
-class Protocol_prep :public Protocol
+class Protocol_binary :public Protocol
{
private:
uint bit_fields;
public:
- Protocol_prep() {}
- Protocol_prep(THD *thd_arg) :Protocol(thd_arg) {}
+ Protocol_binary() {}
+ Protocol_binary(THD *thd_arg) :Protocol(thd_arg) {}
virtual bool prepare_for_send(List<Item> *item_list);
virtual void prepare_for_resend();
#ifdef EMBEDDED_LIBRARY
virtual bool write();
- bool net_store_data(const char *from, uint length);
+ bool net_store_data(const uchar *from, size_t length);
#endif
virtual bool store_null();
virtual bool store_tiny(longlong from);
@@ -147,8 +160,8 @@ public:
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,uint length, CHARSET_INFO *cs);
- virtual bool store(const char *from, uint length,
+ 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);
virtual bool store_date(MYSQL_TIME *time);
@@ -156,16 +169,14 @@ 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 enum enum_protocol_type type() { return PROTOCOL_BINARY; };
};
void send_warning(THD *thd, uint sql_errno, const char *err=0);
-void net_printf_error(THD *thd, uint sql_errno, ...);
void net_send_error(THD *thd, uint sql_errno=0, const char *err=0);
-void send_ok(THD *thd, ha_rows affected_rows=0L, ulonglong id=0L,
- const char *info=0);
-void send_eof(THD *thd);
+void net_end_statement(THD *thd);
bool send_old_password_request(THD *thd);
-char *net_store_data(char *to,const char *from, uint length);
-char *net_store_data(char *to,int32 from);
-char *net_store_data(char *to,longlong from);
+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);
diff --git a/sql/records.cc b/sql/records.cc
index d5c3a421cd9..9e040de3fda 100644
--- a/sql/records.cc
+++ b/sql/records.cc
@@ -14,7 +14,12 @@
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
-/* Functions for easy reading of records, possible through a cache */
+/**
+ @file
+
+ @brief
+ Functions for easy reading of records, possible through a cache
+*/
#include "mysql_priv.h"
@@ -31,30 +36,26 @@ static int rr_index_first(READ_RECORD *info);
static int rr_index(READ_RECORD *info);
-/*
- Initialize READ_RECORD structure to perform full index scan
-
- SYNOPSIS
- init_read_record_idx()
- info READ_RECORD structure to initialize.
- thd Thread handle
- table Table to be accessed
- print_error If true, call table->file->print_error() if an error
- occurs (except for end-of-records error)
- idx index to scan
-
- DESCRIPTION
- 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 forward
+ 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
join_read_first/next functions.
+
+ @param info READ_RECORD structure to initialize.
+ @param thd Thread handle
+ @param table Table to be accessed
+ @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
*/
void init_read_record_idx(READ_RECORD *info, THD *thd, TABLE *table,
bool print_error, uint idx)
{
+ empty_record(table);
bzero((char*) info,sizeof(*info));
info->table= table;
info->file= table->file;
@@ -63,18 +64,28 @@ void init_read_record_idx(READ_RECORD *info, THD *thd, TABLE *table,
table->status=0; /* And it's always found */
if (!table->file->inited)
- {
- table->file->ha_index_init(idx);
- table->file->extra(HA_EXTRA_RETRIEVE_PRIMARY_KEY);
- }
+ table->file->ha_index_init(idx, 1);
/* read_record will be changed to rr_index in rr_index_first */
info->read_record= rr_index_first;
}
/*
- init struct for read with info->read_record
+ init_read_record is used to scan by using a number of different methods.
+ Which method to use is set-up in this call so that later calls to
+ the info->read_record will call the appropriate method using a function
+ pointer.
+ There are five methods that relate completely to the sort function
+ filesort. The result of a filesort is retrieved using read_record
+ calls. The other two methods are used for normal table access.
+
+ The filesort will produce references to the records sorted, these
+ references can be stored in memory or in a temporary file.
+
+ The temporary file is normally used when the references doesn't fit into
+ a properly sized memory buffer. For most small queries the references
+ are stored in the memory buffer.
SYNOPSIS
init_read_record()
info OUT read structure
@@ -93,22 +104,57 @@ void init_read_record_idx(READ_RECORD *info, THD *thd, TABLE *table,
DESCRIPTION
This function sets up reading data via one of the methods:
- rr_unpack_from_tempfile Unpack full records from sequential file
- rr_unpack_from_buffer ... or from buffer
-
- rr_from_tempfile Read rowids from tempfile and get full records
- with handler->rnd_pos() calls.
- rr_from_pointers ... or get rowids from buffer
-
- rr_from_cache Read a bunch of rowids from file, sort them,
- get records in rowid order, return, repeat.
-
- rr_quick Get data from QUICK_*_SELECT
-
- rr_sequential Sequentially scan the table using
- handler->rnd_next() calls
+ The temporary file is also used when performing an update where a key is
+ modified.
+
+ Methods used when ref's are in memory (using rr_from_pointers):
+ rr_unpack_from_buffer:
+ ----------------------
+ This method is used when table->sort.addon_field is allocated.
+ This is allocated for most SELECT queries not involving any BLOB's.
+ In this case the records are fetched from a memory buffer.
+ rr_from_pointers:
+ -----------------
+ Used when the above is not true, UPDATE, DELETE and so forth and
+ SELECT's involving BLOB's. It is also used when the addon_field
+ buffer is not allocated due to that its size was bigger than the
+ session variable max_length_for_sort_data.
+ In this case the record data is fetched from the handler using the
+ saved reference using the rnd_pos handler call.
+
+ Methods used when ref's are in a temporary file (using rr_from_tempfile)
+ rr_unpack_from_tempfile:
+ ------------------------
+ Same as rr_unpack_from_buffer except that references are fetched from
+ temporary file. Should obviously not really happen other than in
+ strange configurations.
+
+ rr_from_tempfile:
+ -----------------
+ Same as rr_from_pointers except that references are fetched from
+ temporary file instead of from
+ rr_from_cache:
+ --------------
+ This is a special variant of rr_from_tempfile that can be used for
+ handlers that is not using the HA_FAST_KEY_READ table flag. Instead
+ of reading the references one by one from the temporary file it reads
+ a set of them, sorts them and reads all of them into a buffer which
+ is then used for a number of subsequent calls to rr_from_cache.
+ It is only used for SELECT queries and a number of other conditions
+ on table size.
+
+ All other accesses use either index access methods (rr_quick) or a full
+ table scan (rr_sequential).
+ rr_quick:
+ ---------
+ rr_quick uses one of the QUICK_SELECT classes in opt_range.cc to
+ perform an index scan. There are loads of functionality hidden
+ in these quick classes. It handles all index scans of various kinds.
+ rr_sequential:
+ --------------
+ This is the most basic access method of a table using rnd_init,
+ rnd_next and rnd_end. No indexes are used.
*/
-
void init_read_record(READ_RECORD *info,THD *thd, TABLE *table,
SQL_SELECT *select,
int use_record_cache, bool print_error,
@@ -122,6 +168,11 @@ void init_read_record(READ_RECORD *info,THD *thd, TABLE *table,
info->table=table;
info->file= table->file;
info->forms= &info->table; /* Only one table */
+
+ if (table->s->tmp_table == NON_TRANSACTIONAL_TMP_TABLE &&
+ !table->sort.addon_field)
+ VOID(table->file->extra(HA_EXTRA_MMAP));
+
if (table->sort.addon_field)
{
info->rec_buf= table->sort.addon_buf;
@@ -129,6 +180,7 @@ void init_read_record(READ_RECORD *info,THD *thd, TABLE *table,
}
else
{
+ empty_record(table);
info->record= table->record[0];
info->ref_length= table->file->ref_length;
}
@@ -161,11 +213,11 @@ void init_read_record(READ_RECORD *info,THD *thd, TABLE *table,
!table->sort.addon_field &&
! (specialflag & SPECIAL_SAFE_MODE) &&
thd->variables.read_rnd_buff_size &&
- !(table->file->table_flags() & HA_FAST_KEY_READ) &&
+ !(table->file->ha_table_flags() & HA_FAST_KEY_READ) &&
(table->db_stat & HA_READ_ONLY ||
table->reginfo.lock_type <= TL_READ_NO_INSERT) &&
- (ulonglong) table->s->reclength* (table->file->records+
- table->file->deleted) >
+ (ulonglong) table->s->reclength* (table->file->stats.records+
+ table->file->stats.deleted) >
(ulonglong) MIN_FILE_LENGTH_TO_USE_ROW_CACHE &&
info->io_cache->end_of_file/info->ref_length * table->s->reclength >
(my_off_t) MIN_ROWS_TO_USE_TABLE_CACHE &&
@@ -205,7 +257,7 @@ void init_read_record(READ_RECORD *info,THD *thd, TABLE *table,
(int) table->reginfo.lock_type <= (int) TL_READ_HIGH_PRIORITY ||
!(table->s->db_options_in_use & HA_OPTION_PACK_RECORD) ||
(use_record_cache < 0 &&
- !(table->file->table_flags() & HA_NOT_DELETE_WITH_CACHE))))
+ !(table->file->ha_table_flags() & HA_NOT_DELETE_WITH_CACHE))))
VOID(table->file->extra_opt(HA_EXTRA_CACHE,
thd->variables.read_buff_size));
}
@@ -253,7 +305,7 @@ static int rr_handle_error(READ_RECORD *info, int error)
}
- /* Read a record from head-database */
+/** Read a record from head-database. */
static int rr_quick(READ_RECORD *info)
{
@@ -275,20 +327,19 @@ static int rr_quick(READ_RECORD *info)
}
-/*
- Reads first row in an index scan
+/**
+ Reads first row in an index scan.
- SYNOPSIS
- rr_index_first()
- info Scan info
-
- RETURN
+ @param info Scan info
+
+ @retval
0 Ok
- -1 End of records
- 1 Error
+ @retval
+ -1 End of records
+ @retval
+ 1 Error
*/
-
static int rr_index_first(READ_RECORD *info)
{
int tmp= info->file->index_first(info->record);
@@ -299,24 +350,22 @@ static int rr_index_first(READ_RECORD *info)
}
-/*
- Reads index sequentially after first row
+/**
+ Reads index sequentially after first row.
- SYNOPSIS
- rr_index()
- info Scan info
-
- DESCRIPTION
- Read the next index record (in forward direction) and translate return
- value.
-
- RETURN
+ Read the next index record (in forward direction) and translate return
+ value.
+
+ @param info Scan info
+
+ @retval
0 Ok
- -1 End of records
- 1 Error
+ @retval
+ -1 End of records
+ @retval
+ 1 Error
*/
-
static int rr_index(READ_RECORD *info)
{
int tmp= info->file->index_next(info->record);
@@ -370,22 +419,20 @@ static int rr_from_tempfile(READ_RECORD *info)
} /* rr_from_tempfile */
-/*
- Read a result set record from a temporary file after sorting
+/**
+ Read a result set record from a temporary file after sorting.
- SYNOPSIS
- rr_unpack_from_tempfile()
- info Reference to the context including record descriptors
+ The function first reads the next sorted record from the temporary file.
+ into a buffer. If a success it calls a callback function that unpacks
+ the fields values use in the result set from this buffer into their
+ positions in the regular record buffer.
- DESCRIPTION
- The function first reads the next sorted record from the temporary file.
- into a buffer. If a success it calls a callback function that unpacks
- the fields values use in the result set from this buffer into their
- positions in the regular record buffer.
-
- RETURN
- 0 - Record successfully read.
- -1 - There is no record to be read anymore.
+ @param info Reference to the context including record descriptors
+
+ @retval
+ 0 Record successfully read.
+ @retval
+ -1 There is no record to be read anymore.
*/
static int rr_unpack_from_tempfile(READ_RECORD *info)
@@ -401,7 +448,7 @@ static int rr_unpack_from_tempfile(READ_RECORD *info)
static int rr_from_pointers(READ_RECORD *info)
{
int tmp;
- byte *cache_pos;
+ uchar *cache_pos;
for (;;)
{
@@ -423,22 +470,20 @@ static int rr_from_pointers(READ_RECORD *info)
return tmp;
}
-/*
- Read a result set record from a buffer after sorting
+/**
+ Read a result set record from a buffer after sorting.
- SYNOPSIS
- rr_unpack_from_buffer()
- info Reference to the context including record descriptors
+ The function first reads the next sorted record from the sort buffer.
+ If a success it calls a callback function that unpacks
+ the fields values use in the result set from this buffer into their
+ positions in the regular record buffer.
- DESCRIPTION
- The function first reads the next sorted record from the sort buffer.
- If a success it calls a callback function that unpacks
- the fields values use in the result set from this buffer into their
- positions in the regular record buffer.
-
- RETURN
- 0 - Record successfully read.
- -1 - There is no record to be read anymore.
+ @param info Reference to the context including record descriptors
+
+ @retval
+ 0 Record successfully read.
+ @retval
+ -1 There is no record to be read anymore.
*/
static int rr_unpack_from_buffer(READ_RECORD *info)
@@ -471,7 +516,7 @@ static int init_rr_cache(THD *thd, READ_RECORD *info)
// We have to allocate one more byte to use uint3korr (see comments for it)
if (info->cache_records <= 2 ||
- !(info->cache=(byte*) my_malloc_lock(rec_cache_size+info->cache_records*
+ !(info->cache=(uchar*) my_malloc_lock(rec_cache_size+info->cache_records*
info->struct_length+1,
MYF(0))))
DBUG_RETURN(1);
@@ -492,7 +537,7 @@ static int rr_from_cache(READ_RECORD *info)
ulong length;
my_off_t rest_of_file;
int16 error;
- byte *position,*ref_position,*record_pos;
+ uchar *position,*ref_position,*record_pos;
ulong record;
for (;;)
@@ -529,7 +574,7 @@ static int rr_from_cache(READ_RECORD *info)
ref_position=info->read_positions;
for (i=0 ; i < length ; i++,position+=info->ref_length)
{
- memcpy(ref_position,position,(size_s) info->ref_length);
+ memcpy(ref_position,position,(size_t) info->ref_length);
ref_position+=MAX_REFLENGTH;
int3store(ref_position,(long) i);
ref_position+=3;
@@ -540,7 +585,7 @@ static int rr_from_cache(READ_RECORD *info)
position=info->read_positions;
for (i=0 ; i < length ; i++)
{
- memcpy(info->ref_pos,position,(size_s) info->ref_length);
+ memcpy(info->ref_pos,position,(size_t) info->ref_length);
position+=MAX_REFLENGTH;
record=uint3korr(position);
position+=3;
diff --git a/sql/repl_failsafe.cc b/sql/repl_failsafe.cc
index 7f98dd2d64a..582348608de 100644
--- a/sql/repl_failsafe.cc
+++ b/sql/repl_failsafe.cc
@@ -13,12 +13,24 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+/**
+ @file
+
+ All of the functions defined in this file which are not used (the ones to
+ handle failsafe) are not used; their code has not been updated for more
+ than one year now so should be considered as BADLY BROKEN. Do not enable
+ it. The used functions (to handle LOAD DATA FROM MASTER, plus some small
+ functions like register_slave()) are working.
+*/
+
#include "mysql_priv.h"
#ifdef HAVE_REPLICATION
#include "repl_failsafe.h"
#include "sql_repl.h"
#include "slave.h"
+#include "rpl_mi.h"
+#include "rpl_filter.h"
#include "log_event.h"
#include <mysql.h>
@@ -60,39 +72,36 @@ static Slave_log_event* find_slave_event(IO_CACHE* log,
static int init_failsafe_rpl_thread(THD* thd)
{
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->system_thread = thd->bootstrap = 1;
+ 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 = thread_id++;
+ 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);
- end_thread(thd,0);
+ one_thread_per_connection_end(thd,0);
DBUG_RETURN(-1);
+ /* purecov: end */
}
-#if !defined(__WIN__) && !defined(OS2) && !defined(__NETWARE__)
- sigset_t set;
- VOID(sigemptyset(&set)); // Get mask in use
- VOID(pthread_sigmask(SIG_UNBLOCK,&set,&thd->block_signals));
-#endif
-
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="Thread initialized";
+ thd_proc_info(thd, "Thread initialized");
thd->version=refresh_version;
thd->set_time();
DBUG_RETURN(0);
@@ -138,9 +147,9 @@ void unregister_slave(THD* thd, bool only_mine, bool need_mutex)
SLAVE_INFO* old_si;
if ((old_si = (SLAVE_INFO*)hash_search(&slave_list,
- (byte*)&thd->server_id, 4)) &&
+ (uchar*)&thd->server_id, 4)) &&
(!only_mine || old_si->thd == thd))
- hash_delete(&slave_list, (byte*)old_si);
+ hash_delete(&slave_list, (uchar*)old_si);
if (need_mutex)
pthread_mutex_unlock(&LOCK_slave_list);
@@ -148,12 +157,13 @@ void unregister_slave(THD* thd, bool only_mine, bool need_mutex)
}
-/*
- Register slave in 'slave_list' hash table
+/**
+ Register slave in 'slave_list' hash table.
- RETURN VALUES
- 0 ok
- 1 Error. Error message sent to client
+ @return
+ 0 ok
+ @return
+ 1 Error. Error message sent to client
*/
int register_slave(THD* thd, uchar* packet, uint packet_length)
@@ -185,19 +195,19 @@ int register_slave(THD* thd, uchar* packet, uint packet_length)
pthread_mutex_lock(&LOCK_slave_list);
unregister_slave(thd,0,0);
- res= my_hash_insert(&slave_list, (byte*) si);
+ res= my_hash_insert(&slave_list, (uchar*) si);
pthread_mutex_unlock(&LOCK_slave_list);
return res;
err:
- my_free((gptr) si, MYF(MY_WME));
+ my_free(si, MYF(MY_WME));
my_message(ER_UNKNOWN_ERROR, errmsg, MYF(0)); /* purecov: inspected */
err2:
return 1;
}
extern "C" uint32
-*slave_list_key(SLAVE_INFO* si, uint* len,
+*slave_list_key(SLAVE_INFO* si, size_t *len,
my_bool not_used __attribute__((unused)))
{
*len = 4;
@@ -206,7 +216,7 @@ extern "C" uint32
extern "C" void slave_info_free(void *s)
{
- my_free((gptr) s, MYF(MY_WME));
+ my_free(s, MYF(MY_WME));
}
void init_slave_list()
@@ -256,7 +266,8 @@ static int find_target_pos(LEX_MASTER_INFO *mi, IO_CACHE *log, char *errmsg)
/* Impossible */
}
-/*
+/**
+ @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
@@ -392,8 +403,8 @@ err:
}
-/*
- Caller must delete result when done
+/**
+ Caller must delete result when done.
*/
static Slave_log_event* find_slave_event(IO_CACHE* log,
@@ -432,9 +443,11 @@ static Slave_log_event* find_slave_event(IO_CACHE* log,
return (Slave_log_event*)ev;
}
-/*
- This function is broken now. See comment for translate_master().
- */
+/**
+ This function is broken now.
+
+ @seealso translate_master()
+*/
bool show_new_master(THD* thd)
{
@@ -465,25 +478,24 @@ bool show_new_master(THD* thd)
protocol->store((ulonglong) lex_mi->pos);
if (protocol->write())
DBUG_RETURN(TRUE);
- send_eof(thd);
+ my_eof(thd);
DBUG_RETURN(FALSE);
}
}
-/*
+/**
Asks the master for the list of its other connected slaves.
- This is for failsafe replication:
+
+ 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.
- SYNOPSIS
- update_slave_list()
- mysql pre-existing connection to the master
- mi master info
+ @param mysql pre-existing connection to the master
+ @param mi master info
- NOTES
+ @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.
@@ -491,12 +503,13 @@ bool show_new_master(THD* thd)
REPLICATION SLAVE privilege, it will pop in this function because
SHOW SLAVE HOSTS will fail on the master.
- RETURN VALUES
+ @retval
1 error
+ @retval
0 success
- */
+*/
-int update_slave_list(MYSQL* mysql, MASTER_INFO* mi)
+int update_slave_list(MYSQL* mysql, Master_info* mi)
{
MYSQL_RES* res=0;
MYSQL_ROW row;
@@ -535,7 +548,7 @@ HOSTS";
SLAVE_INFO* si, *old_si;
log_server_id = atoi(row[0]);
if ((old_si= (SLAVE_INFO*)hash_search(&slave_list,
- (byte*)&log_server_id,4)))
+ (uchar*)&log_server_id,4)))
si = old_si;
else
{
@@ -546,7 +559,7 @@ HOSTS";
goto err;
}
si->server_id = log_server_id;
- my_hash_insert(&slave_list, (byte*)si);
+ my_hash_insert(&slave_list, (uchar*)si);
}
strmake(si->host, row[1], sizeof(si->host)-1);
si->port = atoi(row[port_ind]);
@@ -565,8 +578,8 @@ err:
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'",
+ 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);
}
@@ -603,7 +616,7 @@ pthread_handler_t handle_failsafe_rpl(void *arg)
{
bool break_req_chain = 0;
pthread_cond_wait(&COND_rpl_status, &LOCK_rpl_status);
- thd->proc_info="Processing request";
+ thd_proc_info(thd, "Processing request");
while (!break_req_chain)
{
switch (rpl_status) {
@@ -631,6 +644,16 @@ err:
}
#endif
+
+/**
+ Execute a SHOW SLAVE HOSTS statement.
+
+ @param thd Pointer to THD object for the client thread executing the
+ statement.
+
+ @retval FALSE success
+ @retval TRUE failure
+*/
bool show_slave_hosts(THD* thd)
{
List<Item> field_list;
@@ -678,12 +701,12 @@ bool show_slave_hosts(THD* thd)
}
}
pthread_mutex_unlock(&LOCK_slave_list);
- send_eof(thd);
+ my_eof(thd);
DBUG_RETURN(FALSE);
}
-int connect_to_master(THD *thd, MYSQL* mysql, MASTER_INFO* mi)
+int connect_to_master(THD *thd, MYSQL* mysql, Master_info* mi)
{
DBUG_ENTER("connect_to_master");
@@ -697,12 +720,16 @@ int connect_to_master(THD *thd, MYSQL* mysql, MASTER_INFO* mi)
#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);
@@ -728,7 +755,7 @@ static inline void cleanup_mysql_results(MYSQL_RES* db_res,
static int fetch_db_tables(THD *thd, MYSQL *mysql, const char *db,
- MYSQL_RES *table_res, MASTER_INFO *mi)
+ MYSQL_RES *table_res, Master_info *mi)
{
MYSQL_ROW row;
for (row = mysql_fetch_row(table_res); row;
@@ -737,14 +764,14 @@ static int fetch_db_tables(THD *thd, MYSQL *mysql, const char *db,
TABLE_LIST table;
const char* table_name= row[0];
int error;
- if (table_rules_on)
+ 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 (!tables_ok(thd, &table))
+ if (!rpl_filter->tables_ok(thd->db, &table))
continue;
}
/* download master's table and overwrite slave's table */
@@ -754,11 +781,16 @@ static int fetch_db_tables(THD *thd, MYSQL *mysql, const char *db,
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)
+ - 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)
@@ -863,8 +895,8 @@ bool load_master_data(THD* thd)
data from master
*/
- if (!db_ok(db, replicate_do_db, replicate_ignore_db) ||
- !db_ok_with_wild_table(db) ||
+ if (!rpl_filter->db_ok(db) ||
+ !rpl_filter->db_ok_with_wild_table(db) ||
!strcmp(db,"mysql") ||
is_schema_db(db))
{
@@ -880,6 +912,8 @@ bool load_master_data(THD* thd)
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")) ||
@@ -947,7 +981,7 @@ bool load_master_data(THD* thd)
goto err;
}
}
- thd->proc_info="purging old relay logs";
+ thd_proc_info(thd, "purging old relay logs");
if (purge_relay_logs(&active_mi->rli,thd,
0 /* not only reset, but also reinit */,
&errmsg))
@@ -965,7 +999,7 @@ bool load_master_data(THD* thd)
Cancel the previous START SLAVE UNTIL, as the fact to download
a new copy logically makes UNTIL irrelevant.
*/
- clear_until_condition(&active_mi->rli);
+ active_mi->rli.clear_until_condition();
/*
No need to update rli.event* coordinates, they will be when the slave
@@ -974,7 +1008,7 @@ bool load_master_data(THD* thd)
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 = "starting slave";
+ thd_proc_info(thd, "starting slave");
if (restart_thread_mask)
{
error=start_slave_threads(0 /* mutex not needed */,
@@ -986,11 +1020,11 @@ bool load_master_data(THD* thd)
err:
unlock_slave_threads(active_mi);
pthread_mutex_unlock(&LOCK_active_mi);
- thd->proc_info = 0;
+ thd_proc_info(thd, 0);
mysql_close(&mysql); // safe to call since we always do mysql_init()
if (!error)
- send_ok(thd);
+ my_ok(thd);
return error;
}
diff --git a/sql/repl_failsafe.h b/sql/repl_failsafe.h
index 561db00d841..6ff78067aca 100644
--- a/sql/repl_failsafe.h
+++ b/sql/repl_failsafe.h
@@ -33,12 +33,12 @@ 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);
int find_recovery_captain(THD* thd, MYSQL* mysql);
-int update_slave_list(MYSQL* mysql, MASTER_INFO* mi);
+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);
+int connect_to_master(THD *thd, MYSQL* mysql, Master_info* mi);
bool show_new_master(THD* thd);
bool show_slave_hosts(THD* thd);
diff --git a/sql/rpl_constants.h b/sql/rpl_constants.h
new file mode 100644
index 00000000000..32fb4b8a7f2
--- /dev/null
+++ b/sql/rpl_constants.h
@@ -0,0 +1,18 @@
+#ifndef RPL_CONSTANTS_H
+#define RPL_CONSTANTS_H
+
+/**
+ Enumeration of the incidents that can occur for the server.
+ */
+enum Incident {
+ /** No incident */
+ INCIDENT_NONE = 0,
+
+ /** There are possibly lost events in the replication stream */
+ INCIDENT_LOST_EVENTS = 1,
+
+ /** Shall be last event of the enumeration */
+ INCIDENT_COUNT
+};
+
+#endif /* RPL_CONSTANTS_H */
diff --git a/sql/rpl_filter.cc b/sql/rpl_filter.cc
new file mode 100644
index 00000000000..fb609e12dcb
--- /dev/null
+++ b/sql/rpl_filter.cc
@@ -0,0 +1,547 @@
+/* Copyright (C) 2000-2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#include "mysql_priv.h"
+#include "rpl_filter.h"
+
+#define TABLE_RULE_HASH_SIZE 16
+#define TABLE_RULE_ARR_SIZE 16
+
+Rpl_filter::Rpl_filter() :
+ table_rules_on(0), do_table_inited(0), ignore_table_inited(0),
+ wild_do_table_inited(0), wild_ignore_table_inited(0)
+{
+ do_db.empty();
+ ignore_db.empty();
+ rewrite_db.empty();
+}
+
+
+Rpl_filter::~Rpl_filter()
+{
+ if (do_table_inited)
+ hash_free(&do_table);
+ if (ignore_table_inited)
+ 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_list(&rewrite_db);
+}
+
+
+/*
+ Returns true if table should be logged/replicated
+
+ SYNOPSIS
+ tables_ok()
+ db db to use if db in TABLE_LIST is undefined for a table
+ tables list of tables to check
+
+ NOTES
+ Changing table order in the list can lead to different results.
+
+ Note also order of precedence of do/ignore rules (see code). For
+ that reason, users should not set conflicting rules because they
+ may get unpredicted results (precedence order is explained in the
+ manual).
+
+ If no table in the list is marked "updating", then we always
+ return 0, because there is no reason to execute this statement on
+ slave if it updates nothing. (Currently, this can only happen if
+ statement is a multi-delete (SQLCOM_DELETE_MULTI) and "tables" are
+ the tables in the FROM):
+
+ In the case of SQLCOM_DELETE_MULTI, there will be a second call to
+ tables_ok(), with tables having "updating==TRUE" (those after the
+ DELETE), so this second call will make the decision (because
+ all_tables_not_ok() = !tables_ok(1st_list) &&
+ !tables_ok(2nd_list)).
+
+ TODO
+ "Include all tables like "abc.%" except "%.EFG"". (Can't be done now.)
+ If we supported Perl regexps, we could do it with pattern: /^abc\.(?!EFG)/
+ (I could not find an equivalent in the regex library MySQL uses).
+
+ RETURN VALUES
+ 0 should not be logged/replicated
+ 1 should be logged/replicated
+*/
+
+bool
+Rpl_filter::tables_ok(const char* db, TABLE_LIST* tables)
+{
+ bool some_tables_updating= 0;
+ DBUG_ENTER("Rpl_filter::tables_ok");
+
+ for (; tables; tables= tables->next_global)
+ {
+ char hash_key[2*NAME_LEN+2];
+ char *end;
+ uint len;
+
+ if (!tables->updating)
+ continue;
+ some_tables_updating= 1;
+ end= strmov(hash_key, tables->db ? tables->db : db);
+ *end++= '.';
+ 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))
+ DBUG_RETURN(1);
+ }
+ if (ignore_table_inited) // if there are any ignores
+ {
+ if (hash_search(&ignore_table, (uchar*) hash_key, len))
+ DBUG_RETURN(0);
+ }
+ if (wild_do_table_inited &&
+ find_wild(&wild_do_table, hash_key, len))
+ DBUG_RETURN(1);
+ if (wild_ignore_table_inited &&
+ find_wild(&wild_ignore_table, hash_key, len))
+ DBUG_RETURN(0);
+ }
+
+ /*
+ If no table was to be updated, ignore statement (no reason we play it on
+ slave, slave is supposed to replicate _changes_ only).
+ If no explicit rule found and there was a do list, do not replicate.
+ If there was no do list, go ahead
+ */
+ DBUG_RETURN(some_tables_updating &&
+ !do_table_inited && !wild_do_table_inited);
+}
+
+
+/*
+ Checks whether a db matches some do_db and ignore_db rules
+
+ SYNOPSIS
+ db_ok()
+ db name of the db to check
+
+ RETURN VALUES
+ 0 should not be logged/replicated
+ 1 should be logged/replicated
+*/
+
+bool
+Rpl_filter::db_ok(const char* db)
+{
+ DBUG_ENTER("Rpl_filter::db_ok");
+
+ if (do_db.is_empty() && ignore_db.is_empty())
+ DBUG_RETURN(1); // Ok to replicate if the user puts no constraints
+
+ /*
+ If the user has specified restrictions on which databases to replicate
+ and db was not selected, do not replicate.
+ */
+ if (!db)
+ DBUG_RETURN(0);
+
+ if (!do_db.is_empty()) // if the do's are not empty
+ {
+ I_List_iterator<i_string> it(do_db);
+ i_string* tmp;
+
+ while ((tmp=it++))
+ {
+ if (!strcmp(tmp->ptr, db))
+ DBUG_RETURN(1); // match
+ }
+ DBUG_RETURN(0);
+ }
+ else // there are some elements in the don't, otherwise we cannot get here
+ {
+ I_List_iterator<i_string> it(ignore_db);
+ i_string* tmp;
+
+ while ((tmp=it++))
+ {
+ if (!strcmp(tmp->ptr, db))
+ DBUG_RETURN(0); // match
+ }
+ DBUG_RETURN(1);
+ }
+}
+
+
+/*
+ Checks whether a db matches wild_do_table and wild_ignore_table
+ rules (for replication)
+
+ SYNOPSIS
+ db_ok_with_wild_table()
+ db name of the db to check.
+ Is tested with check_db_name() before calling this function.
+
+ NOTES
+ Here is the reason for this function.
+ We advise users who want to exclude a database 'db1' safely to do it
+ with replicate_wild_ignore_table='db1.%' instead of binlog_ignore_db or
+ replicate_ignore_db because the two lasts only check for the selected db,
+ which won't work in that case:
+ USE db2;
+ UPDATE db1.t SET ... #this will be replicated and should not
+ whereas replicate_wild_ignore_table will work in all cases.
+ With replicate_wild_ignore_table, we only check tables. When
+ one does 'DROP DATABASE db1', tables are not involved and the
+ statement will be replicated, while users could expect it would not (as it
+ rougly means 'DROP db1.first_table, DROP db1.second_table...').
+ In other words, we want to interpret 'db1.%' as "everything touching db1".
+ That is why we want to match 'db1' against 'db1.%' wild table rules.
+
+ RETURN VALUES
+ 0 should not be logged/replicated
+ 1 should be logged/replicated
+*/
+
+bool
+Rpl_filter::db_ok_with_wild_table(const char *db)
+{
+ DBUG_ENTER("Rpl_filter::db_ok_with_wild_table");
+
+ char hash_key[NAME_LEN+2];
+ char *end;
+ int len;
+ end= strmov(hash_key, db);
+ *end++= '.';
+ len= end - hash_key ;
+ if (wild_do_table_inited && find_wild(&wild_do_table, hash_key, len))
+ {
+ DBUG_PRINT("return",("1"));
+ DBUG_RETURN(1);
+ }
+ if (wild_ignore_table_inited && find_wild(&wild_ignore_table, hash_key, len))
+ {
+ DBUG_PRINT("return",("0"));
+ DBUG_RETURN(0);
+ }
+
+ /*
+ If no explicit rule found and there was a do list, do not replicate.
+ If there was no do list, go ahead
+ */
+ DBUG_PRINT("return",("db=%s,retval=%d", db, !wild_do_table_inited));
+ DBUG_RETURN(!wild_do_table_inited);
+}
+
+
+bool
+Rpl_filter::is_on()
+{
+ return table_rules_on;
+}
+
+
+int
+Rpl_filter::add_do_table(const char* table_spec)
+{
+ DBUG_ENTER("Rpl_filter::add_do_table");
+ if (!do_table_inited)
+ init_table_rule_hash(&do_table, &do_table_inited);
+ table_rules_on= 1;
+ DBUG_RETURN(add_table_rule(&do_table, table_spec));
+}
+
+
+int
+Rpl_filter::add_ignore_table(const char* table_spec)
+{
+ DBUG_ENTER("Rpl_filter::add_ignore_table");
+ if (!ignore_table_inited)
+ init_table_rule_hash(&ignore_table, &ignore_table_inited);
+ table_rules_on= 1;
+ DBUG_RETURN(add_table_rule(&ignore_table, table_spec));
+}
+
+
+int
+Rpl_filter::add_wild_do_table(const char* table_spec)
+{
+ DBUG_ENTER("Rpl_filter::add_wild_do_table");
+ if (!wild_do_table_inited)
+ init_table_rule_array(&wild_do_table, &wild_do_table_inited);
+ table_rules_on= 1;
+ DBUG_RETURN(add_wild_table_rule(&wild_do_table, table_spec));
+}
+
+
+int
+Rpl_filter::add_wild_ignore_table(const char* table_spec)
+{
+ DBUG_ENTER("Rpl_filter::add_wild_ignore_table");
+ if (!wild_ignore_table_inited)
+ init_table_rule_array(&wild_ignore_table, &wild_ignore_table_inited);
+ table_rules_on= 1;
+ DBUG_RETURN(add_wild_table_rule(&wild_ignore_table, table_spec));
+}
+
+
+void
+Rpl_filter::add_db_rewrite(const char* from_db, const char* to_db)
+{
+ i_string_pair *db_pair = new i_string_pair(from_db, to_db);
+ rewrite_db.push_back(db_pair);
+}
+
+
+int
+Rpl_filter::add_table_rule(HASH* h, const char* table_spec)
+{
+ const char* dot = strchr(table_spec, '.');
+ if (!dot) return 1;
+ // len is always > 0 because we know the there exists a '.'
+ uint len = (uint)strlen(table_spec);
+ TABLE_RULE_ENT* e = (TABLE_RULE_ENT*)my_malloc(sizeof(TABLE_RULE_ENT)
+ + len, MYF(MY_WME));
+ if (!e) return 1;
+ e->db= (char*)e + sizeof(TABLE_RULE_ENT);
+ e->tbl_name= e->db + (dot - table_spec) + 1;
+ e->key_len= len;
+ memcpy(e->db, table_spec, len);
+
+ return my_hash_insert(h, (uchar*)e);
+}
+
+
+/*
+ Add table expression with wildcards to dynamic array
+*/
+
+int
+Rpl_filter::add_wild_table_rule(DYNAMIC_ARRAY* a, const char* table_spec)
+{
+ const char* dot = strchr(table_spec, '.');
+ if (!dot) return 1;
+ uint len = (uint)strlen(table_spec);
+ TABLE_RULE_ENT* e = (TABLE_RULE_ENT*)my_malloc(sizeof(TABLE_RULE_ENT)
+ + len, MYF(MY_WME));
+ if (!e) return 1;
+ e->db= (char*)e + sizeof(TABLE_RULE_ENT);
+ e->tbl_name= e->db + (dot - table_spec) + 1;
+ e->key_len= len;
+ memcpy(e->db, table_spec, len);
+ insert_dynamic(a, (uchar*)&e);
+ return 0;
+}
+
+
+void
+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);
+}
+
+
+void
+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);
+}
+
+extern "C" uchar *get_table_key(const uchar *, size_t *, my_bool);
+extern "C" void free_table_ent(void* a);
+
+uchar *get_table_key(const uchar* a, size_t *len,
+ my_bool __attribute__((unused)))
+{
+ TABLE_RULE_ENT *e= (TABLE_RULE_ENT *) a;
+
+ *len= e->key_len;
+ return (uchar*)e->db;
+}
+
+
+void free_table_ent(void* a)
+{
+ TABLE_RULE_ENT *e= (TABLE_RULE_ENT *) a;
+
+ my_free((uchar*) e, MYF(0));
+}
+
+
+void
+Rpl_filter::init_table_rule_hash(HASH* h, bool* h_inited)
+{
+ hash_init(h, system_charset_info,TABLE_RULE_HASH_SIZE,0,0,
+ get_table_key, free_table_ent, 0);
+ *h_inited = 1;
+}
+
+
+void
+Rpl_filter::init_table_rule_array(DYNAMIC_ARRAY* a, bool* a_inited)
+{
+ my_init_dynamic_array(a, sizeof(TABLE_RULE_ENT*), TABLE_RULE_ARR_SIZE,
+ TABLE_RULE_ARR_SIZE);
+ *a_inited = 1;
+}
+
+
+TABLE_RULE_ENT*
+Rpl_filter::find_wild(DYNAMIC_ARRAY *a, const char* key, int len)
+{
+ uint i;
+ const char* key_end= key + len;
+
+ for (i= 0; i < a->elements; i++)
+ {
+ TABLE_RULE_ENT* e ;
+ get_dynamic(a, (uchar*)&e, i);
+ if (!my_wildcmp(system_charset_info, key, key_end,
+ (const char*)e->db,
+ (const char*)(e->db + e->key_len),
+ '\\',wild_one,wild_many))
+ return e;
+ }
+
+ return 0;
+}
+
+
+void
+Rpl_filter::free_string_array(DYNAMIC_ARRAY *a)
+{
+ uint i;
+ for (i= 0; i < a->elements; i++)
+ {
+ char* p;
+ get_dynamic(a, (uchar*) &p, i);
+ my_free(p, MYF(MY_WME));
+ }
+ delete_dynamic(a);
+}
+
+
+/*
+ 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.
+
+ SYNOPSIS
+ table_rule_ent_hash_to_str()
+ s pointer to the String to fill
+ h pointer to the HASH to read
+
+ RETURN VALUES
+ none
+*/
+
+void
+Rpl_filter::table_rule_ent_hash_to_str(String* s, HASH* h, bool inited)
+{
+ s->length(0);
+ if (inited)
+ {
+ for (uint i= 0; i < h->records; i++)
+ {
+ TABLE_RULE_ENT* e= (TABLE_RULE_ENT*) hash_element(h, i);
+ if (s->length())
+ s->append(',');
+ s->append(e->db,e->key_len);
+ }
+ }
+}
+
+
+void
+Rpl_filter::table_rule_ent_dynamic_array_to_str(String* s, DYNAMIC_ARRAY* a,
+ bool inited)
+{
+ s->length(0);
+ if (inited)
+ {
+ for (uint i= 0; i < a->elements; i++)
+ {
+ TABLE_RULE_ENT* e;
+ get_dynamic(a, (uchar*)&e, i);
+ if (s->length())
+ s->append(',');
+ s->append(e->db,e->key_len);
+ }
+ }
+}
+
+
+void
+Rpl_filter::get_do_table(String* str)
+{
+ table_rule_ent_hash_to_str(str, &do_table, do_table_inited);
+}
+
+
+void
+Rpl_filter::get_ignore_table(String* str)
+{
+ table_rule_ent_hash_to_str(str, &ignore_table, ignore_table_inited);
+}
+
+
+void
+Rpl_filter::get_wild_do_table(String* str)
+{
+ table_rule_ent_dynamic_array_to_str(str, &wild_do_table, wild_do_table_inited);
+}
+
+
+void
+Rpl_filter::get_wild_ignore_table(String* str)
+{
+ table_rule_ent_dynamic_array_to_str(str, &wild_ignore_table, wild_ignore_table_inited);
+}
+
+
+const char*
+Rpl_filter::get_rewrite_db(const char* db, size_t *new_len)
+{
+ if (rewrite_db.is_empty() || !db)
+ return db;
+ I_List_iterator<i_string_pair> it(rewrite_db);
+ i_string_pair* tmp;
+
+ while ((tmp=it++))
+ {
+ if (!strcmp(tmp->key, db))
+ {
+ *new_len= strlen(tmp->val);
+ return tmp->val;
+ }
+ }
+ return db;
+}
+
+
+I_List<i_string>*
+Rpl_filter::get_do_db()
+{
+ return &do_db;
+}
+
+
+I_List<i_string>*
+Rpl_filter::get_ignore_db()
+{
+ return &ignore_db;
+}
diff --git a/sql/rpl_filter.h b/sql/rpl_filter.h
new file mode 100644
index 00000000000..ff7e4081bb1
--- /dev/null
+++ b/sql/rpl_filter.h
@@ -0,0 +1,116 @@
+/* Copyright (C) 2000-2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#ifndef RPL_FILTER_H
+#define RPL_FILTER_H
+
+#include "mysql.h"
+
+typedef struct st_table_rule_ent
+{
+ char* db;
+ char* tbl_name;
+ uint key_len;
+} TABLE_RULE_ENT;
+
+/*
+ Rpl_filter
+
+ Inclusion and exclusion rules of tables and databases.
+ Also handles rewrites of db.
+ Used for replication and binlogging.
+ */
+class Rpl_filter
+{
+public:
+ Rpl_filter();
+ ~Rpl_filter();
+ Rpl_filter(Rpl_filter const&);
+ Rpl_filter& operator=(Rpl_filter const&);
+
+ /* Checks - returns true if ok to replicate/log */
+
+ bool tables_ok(const char* db, TABLE_LIST* tables);
+ bool db_ok(const char* db);
+ bool db_ok_with_wild_table(const char *db);
+
+ bool is_on();
+
+ /* Setters - add filtering rules */
+
+ int add_do_table(const char* table_spec);
+ int add_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);
+
+ void add_db_rewrite(const char* from_db, const char* to_db);
+
+ /* Getters - to get information about current rules */
+
+ void get_do_table(String* str);
+ void get_ignore_table(String* str);
+
+ void get_wild_do_table(String* str);
+ void get_wild_ignore_table(String* str);
+
+ const char* get_rewrite_db(const char* db, size_t *new_len);
+
+ I_List<i_string>* get_do_db();
+ I_List<i_string>* get_ignore_db();
+
+private:
+ bool table_rules_on;
+
+ void init_table_rule_hash(HASH* h, bool* h_inited);
+ void init_table_rule_array(DYNAMIC_ARRAY* a, bool* a_inited);
+
+ int add_table_rule(HASH* h, const char* table_spec);
+ int add_wild_table_rule(DYNAMIC_ARRAY* a, const char* table_spec);
+
+ void free_string_array(DYNAMIC_ARRAY *a);
+
+ 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);
+ TABLE_RULE_ENT* find_wild(DYNAMIC_ARRAY *a, const char* key, int len);
+
+ /*
+ Those 4 structures below are uninitialized memory unless the
+ corresponding *_inited variables are "true".
+ */
+ HASH do_table;
+ HASH ignore_table;
+ DYNAMIC_ARRAY wild_do_table;
+ DYNAMIC_ARRAY wild_ignore_table;
+
+ bool do_table_inited;
+ bool ignore_table_inited;
+ bool wild_do_table_inited;
+ bool wild_ignore_table_inited;
+
+ I_List<i_string> do_db;
+ I_List<i_string> ignore_db;
+
+ I_List<i_string_pair> rewrite_db;
+};
+
+extern Rpl_filter *rpl_filter;
+extern Rpl_filter *binlog_filter;
+
+#endif // RPL_FILTER_H
diff --git a/sql/rpl_injector.cc b/sql/rpl_injector.cc
new file mode 100644
index 00000000000..684655d1c3b
--- /dev/null
+++ b/sql/rpl_injector.cc
@@ -0,0 +1,236 @@
+/* Copyright (C) 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 St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "mysql_priv.h"
+#include "rpl_injector.h"
+
+/*
+ injector::transaction - member definitions
+*/
+
+/* inline since it's called below */
+inline
+injector::transaction::transaction(MYSQL_BIN_LOG *log, THD *thd)
+ : m_state(START_STATE), m_thd(thd)
+{
+ /*
+ Default initialization of m_start_pos (which initializes it to garbage).
+ We need to fill it in using the code below.
+ */
+ LOG_INFO log_info;
+ log->get_current_log(&log_info);
+ /* !!! binlog_pos does not follow RAII !!! */
+ m_start_pos.m_file_name= my_strdup(log_info.log_file_name, MYF(0));
+ m_start_pos.m_file_pos= log_info.pos;
+
+ begin_trans(m_thd);
+
+ thd->set_current_stmt_binlog_row_based();
+}
+
+injector::transaction::~transaction()
+{
+ if (!good())
+ return;
+
+ /* Needed since my_free expects a 'char*' (instead of 'void*'). */
+ char* const the_memory= const_cast<char*>(m_start_pos.m_file_name);
+
+ /*
+ We set the first character to null just to give all the copies of the
+ start position a (minimal) chance of seening that the memory is lost.
+ All assuming the my_free does not step over the memory, of course.
+ */
+ *the_memory= '\0';
+
+ my_free(the_memory, MYF(0));
+}
+
+int injector::transaction::commit()
+{
+ DBUG_ENTER("injector::transaction::commit()");
+ m_thd->binlog_flush_pending_rows_event(true);
+ /*
+ Cluster replication does not preserve statement or
+ transaction boundaries of the master. Instead, a new
+ transaction on replication slave is started when a new GCI
+ (global checkpoint identifier) is issued, and is committed
+ when the last event of the check point has been received and
+ processed. This ensures consistency of each cluster in
+ cluster replication, and there is no requirement for stronger
+ consistency: MySQL replication is asynchronous with other
+ engines as well.
+
+ A practical consequence of that is that row level replication
+ stream passed through the injector thread never contains
+ COMMIT events.
+ Here we should preserve the server invariant that there is no
+ outstanding statement transaction when the normal transaction
+ is committed by committing the statement transaction
+ explicitly.
+ */
+ ha_autocommit_or_rollback(m_thd, 0);
+ end_trans(m_thd, COMMIT);
+ DBUG_RETURN(0);
+}
+
+int injector::transaction::use_table(server_id_type sid, table tbl)
+{
+ DBUG_ENTER("injector::transaction::use_table");
+
+ int error;
+
+ if ((error= check_state(TABLE_STATE)))
+ DBUG_RETURN(error);
+
+ server_id_type save_id= m_thd->server_id;
+ m_thd->set_server_id(sid);
+ error= m_thd->binlog_write_table_map(tbl.get_table(),
+ tbl.is_transactional());
+ m_thd->set_server_id(save_id);
+ DBUG_RETURN(error);
+}
+
+
+int injector::transaction::write_row (server_id_type sid, table tbl,
+ MY_BITMAP const* cols, size_t colcnt,
+ record_type record)
+{
+ DBUG_ENTER("injector::transaction::write_row(...)");
+
+ if (int error= check_state(ROW_STATE))
+ DBUG_RETURN(error);
+
+ server_id_type save_id= m_thd->server_id;
+ m_thd->set_server_id(sid);
+ m_thd->binlog_write_row(tbl.get_table(), tbl.is_transactional(),
+ cols, colcnt, record);
+ m_thd->set_server_id(save_id);
+ DBUG_RETURN(0);
+}
+
+
+int injector::transaction::delete_row(server_id_type sid, table tbl,
+ MY_BITMAP const* cols, size_t colcnt,
+ record_type record)
+{
+ DBUG_ENTER("injector::transaction::delete_row(...)");
+
+ if (int error= check_state(ROW_STATE))
+ DBUG_RETURN(error);
+
+ server_id_type save_id= m_thd->server_id;
+ m_thd->set_server_id(sid);
+ m_thd->binlog_delete_row(tbl.get_table(), tbl.is_transactional(),
+ cols, colcnt, record);
+ m_thd->set_server_id(save_id);
+ DBUG_RETURN(0);
+}
+
+
+int injector::transaction::update_row(server_id_type sid, table tbl,
+ MY_BITMAP const* cols, size_t colcnt,
+ record_type before, record_type after)
+{
+ DBUG_ENTER("injector::transaction::update_row(...)");
+
+ if (int error= check_state(ROW_STATE))
+ DBUG_RETURN(error);
+
+ server_id_type save_id= m_thd->server_id;
+ m_thd->set_server_id(sid);
+ m_thd->binlog_update_row(tbl.get_table(), tbl.is_transactional(),
+ cols, colcnt, before, after);
+ m_thd->set_server_id(save_id);
+ DBUG_RETURN(0);
+}
+
+
+injector::transaction::binlog_pos injector::transaction::start_pos() const
+{
+ return m_start_pos;
+}
+
+
+/*
+ injector - member definitions
+*/
+
+/* This constructor is called below */
+inline injector::injector()
+{
+}
+
+static injector *s_injector= 0;
+injector *injector::instance()
+{
+ if (s_injector == 0)
+ s_injector= new injector;
+ /* "There can be only one [instance]" */
+ return s_injector;
+}
+
+void injector::free_instance()
+{
+ injector *inj = s_injector;
+
+ if (inj != 0)
+ {
+ s_injector= 0;
+ delete inj;
+ }
+}
+
+
+injector::transaction injector::new_trans(THD *thd)
+{
+ DBUG_ENTER("injector::new_trans(THD*)");
+ /*
+ Currently, there is no alternative to using 'mysql_bin_log' since that
+ is hardcoded into the way the handler is using the binary log.
+ */
+ DBUG_RETURN(transaction(&mysql_bin_log, thd));
+}
+
+void injector::new_trans(THD *thd, injector::transaction *ptr)
+{
+ DBUG_ENTER("injector::new_trans(THD *, transaction *)");
+ /*
+ Currently, there is no alternative to using 'mysql_bin_log' since that
+ is hardcoded into the way the handler is using the binary log.
+ */
+ transaction trans(&mysql_bin_log, thd);
+ ptr->swap(trans);
+
+ DBUG_VOID_RETURN;
+}
+
+int injector::record_incident(THD *thd, Incident incident)
+{
+ Incident_log_event ev(thd, incident);
+ if (int error= mysql_bin_log.write(&ev))
+ return error;
+ mysql_bin_log.rotate_and_purge(RP_FORCE_ROTATE);
+ return 0;
+}
+
+int injector::record_incident(THD *thd, Incident incident, LEX_STRING const message)
+{
+ Incident_log_event ev(thd, incident, message);
+ if (int error= mysql_bin_log.write(&ev))
+ return error;
+ mysql_bin_log.rotate_and_purge(RP_FORCE_ROTATE);
+ return 0;
+}
diff --git a/sql/rpl_injector.h b/sql/rpl_injector.h
new file mode 100644
index 00000000000..4ece092c5b8
--- /dev/null
+++ b/sql/rpl_injector.h
@@ -0,0 +1,337 @@
+/* Copyright (C) 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 St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#ifndef INJECTOR_H
+#define INJECTOR_H
+
+/* Pull in 'byte', 'my_off_t', and 'uint32' */
+#include <my_global.h>
+#include <my_bitmap.h>
+
+#include "rpl_constants.h"
+
+/* Forward declarations */
+class handler;
+class MYSQL_BIN_LOG;
+struct st_table;
+
+typedef st_table TABLE;
+
+/*
+ Injector to inject rows into the MySQL server.
+
+ The injector class is used to notify the MySQL server of new rows that have
+ appeared outside of MySQL control.
+
+ The original purpose of this is to allow clusters---which handle replication
+ inside the cluster through other means---to insert new rows into binary log.
+ Note, however, that the injector should be used whenever rows are altered in
+ any manner that is outside of MySQL server visibility and which therefore
+ are not seen by the MySQL server.
+ */
+class injector
+{
+public:
+
+ /*
+ Get an instance of the injector.
+
+ DESCRIPTION
+ The injector is a Singleton, so this static function return the
+ available instance of the injector.
+
+ RETURN VALUE
+ A pointer to the available injector object.
+ */
+ static injector *instance();
+
+ /*
+ Delete the singleton instance (if allocated). Used during server shutdown.
+ */
+ static void free_instance();
+
+ /*
+ A transaction where rows can be added.
+
+ DESCRIPTION
+ The transaction class satisfy the **CopyConstructible** and
+ **Assignable** requirements. Note that the transaction is *not*
+ default constructible.
+ */
+ class transaction {
+ friend class injector;
+ public:
+ /* Convenience definitions */
+ typedef uchar* record_type;
+ typedef uint32 server_id_type;
+
+ /*
+ Table reference.
+
+ RESPONSIBILITY
+
+ The class contains constructors to handle several forms of
+ references to tables. The constructors can implicitly be used to
+ construct references from, e.g., strings containing table names.
+
+ EXAMPLE
+
+ The class is intended to be used *by value*. Please, do not try to
+ construct objects of this type using 'new'; instead construct an
+ object, possibly a temporary object. For example:
+
+ injector::transaction::table tbl(share->table, true);
+ MY_BITMAP cols;
+ bitmap_init(&cols, NULL, (i + 7) / 8, false);
+ inj->write_row(::server_id, tbl, &cols, row_data);
+
+ or
+
+ MY_BITMAP cols;
+ bitmap_init(&cols, NULL, (i + 7) / 8, false);
+ inj->write_row(::server_id,
+ injector::transaction::table(share->table, true),
+ &cols, row_data);
+
+ This will work, be more efficient, and have greater chance of
+ inlining, not run the risk of losing pointers.
+
+ COLLABORATION
+
+ injector::transaction
+ Provide a flexible interface to the representation of tables.
+
+ */
+ class table
+ {
+ public:
+ table(TABLE *table, bool is_transactional)
+ : m_table(table), m_is_transactional(is_transactional)
+ {
+ }
+
+ char const *db_name() const { return m_table->s->db.str; }
+ char const *table_name() const { return m_table->s->table_name.str; }
+ TABLE *get_table() const { return m_table; }
+ bool is_transactional() const { return m_is_transactional; }
+
+ private:
+ TABLE *m_table;
+ bool m_is_transactional;
+ };
+
+ /*
+ Binlog position as a structure.
+ */
+ class binlog_pos {
+ friend class transaction;
+ public:
+ char const *file_name() const { return m_file_name; }
+ my_off_t file_pos() const { return m_file_pos; }
+
+ private:
+ char const *m_file_name;
+ my_off_t m_file_pos;
+ };
+
+ transaction() : m_thd(NULL) { }
+ transaction(transaction const&);
+ ~transaction();
+
+ /* Clear transaction, i.e., make calls to 'good()' return false. */
+ void clear() { m_thd= NULL; }
+
+ /* Is the transaction in a good state? */
+ bool good() const { return m_thd != NULL; }
+
+ /* Default assignment operator: standard implementation */
+ transaction& operator=(transaction t) {
+ swap(t);
+ return *this;
+ }
+
+ /*
+
+ DESCRIPTION
+
+ Register table for use within the transaction. All tables
+ that are going to be used need to be registered before being
+ used below. The member function will fail with an error if
+ use_table() is called after any *_row() function has been
+ called for the transaction.
+
+ RETURN VALUE
+
+ 0 All OK
+ >0 Failure
+
+ */
+ int use_table(server_id_type sid, table tbl);
+
+ /*
+ Add a 'write row' entry to the transaction.
+ */
+ int write_row (server_id_type sid, table tbl,
+ MY_BITMAP const *cols, size_t colcnt,
+ record_type record);
+
+ /*
+ Add a 'delete row' entry to the transaction.
+ */
+ int delete_row(server_id_type sid, table tbl,
+ MY_BITMAP const *cols, size_t colcnt,
+ record_type record);
+
+ /*
+ Add an 'update row' entry to the transaction.
+ */
+ int update_row(server_id_type sid, table tbl,
+ MY_BITMAP const *cols, size_t colcnt,
+ record_type before, record_type after);
+
+ /*
+ Commit a transaction.
+
+ This member function will clean up after a sequence of *_row calls by,
+ for example, releasing resource and unlocking files.
+ */
+ int commit();
+
+ /*
+ Get the position for the start of the transaction.
+
+ Returns the position in the binary log of the first event in this
+ transaction. If no event is yet written, the position where the event
+ *will* be written is returned. This position is known, since a
+ new_transaction() will lock the binary log and prevent any other
+ writes to the binary log.
+ */
+ binlog_pos start_pos() const;
+
+ private:
+ /* Only the injector may construct these object */
+ transaction(MYSQL_BIN_LOG *, THD *);
+
+ void swap(transaction& o) {
+ /* std::swap(m_start_pos, o.m_start_pos); */
+ {
+ binlog_pos const tmp= m_start_pos;
+ m_start_pos= o.m_start_pos;
+ o.m_start_pos= tmp;
+ }
+
+ /* std::swap(m_thd, o.m_thd); */
+ {
+ THD* const tmp= m_thd;
+ m_thd= o.m_thd;
+ o.m_thd= tmp;
+ }
+ {
+ enum_state const tmp= m_state;
+ m_state= o.m_state;
+ o.m_state= tmp;
+ }
+ }
+
+ enum enum_state
+ {
+ START_STATE, /* Start state */
+ TABLE_STATE, /* At least one table has been registered */
+ ROW_STATE, /* At least one row has been registered */
+ STATE_COUNT /* State count and sink state */
+ } m_state;
+
+ /*
+ Check and update the state.
+
+ PARAMETER(S)
+
+ target_state
+ The state we are moving to: TABLE_STATE if we are
+ writing a table and ROW_STATE if we are writing a row.
+
+ DESCRIPTION
+
+ The internal state will be updated to the target state if
+ and only if it is a legal move. The only legal moves are:
+
+ START_STATE -> START_STATE
+ START_STATE -> TABLE_STATE
+ TABLE_STATE -> TABLE_STATE
+ TABLE_STATE -> ROW_STATE
+
+ That is:
+ - It is not possible to write any row before having written at
+ least one table
+ - It is not possible to write a table after at least one row
+ has been written
+
+ RETURN VALUE
+
+ 0 All OK
+ -1 Incorrect call sequence
+ */
+ int check_state(enum_state const target_state)
+ {
+#ifndef DBUG_OFF
+ static char const *state_name[] = {
+ "START_STATE", "TABLE_STATE", "ROW_STATE", "STATE_COUNT"
+ };
+
+ DBUG_ASSERT(0 <= target_state && target_state <= STATE_COUNT);
+ DBUG_PRINT("info", ("In state %s", state_name[m_state]));
+#endif
+
+ if (m_state <= target_state && target_state <= m_state + 1 &&
+ m_state < STATE_COUNT)
+ m_state= target_state;
+ else
+ m_state= STATE_COUNT;
+ return m_state == STATE_COUNT ? 1 : 0;
+ }
+
+
+ binlog_pos m_start_pos;
+ THD *m_thd;
+ };
+
+ /*
+ Create a new transaction. This member function will prepare for a
+ sequence of *_row calls by, for example, reserving resources and
+ locking files. There are two overloaded alternatives: one returning a
+ transaction by value and one using placement semantics. The following
+ two calls are equivalent, with the exception that the latter will
+ overwrite the transaction.
+
+ injector::transaction trans1= inj->new_trans(thd);
+
+ injector::transaction trans2;
+ inj->new_trans(thd, &trans);
+ */
+ transaction new_trans(THD *);
+ void new_trans(THD *, transaction *);
+
+ int record_incident(THD*, Incident incident);
+ int record_incident(THD*, Incident incident, LEX_STRING const message);
+
+private:
+ explicit injector();
+ ~injector() { } /* Nothing needs to be done */
+ injector(injector const&); /* You're not allowed to copy injector
+ instances.
+ */
+};
+
+#endif /* INJECTOR_H */
diff --git a/sql/rpl_mi.cc b/sql/rpl_mi.cc
new file mode 100644
index 00000000000..5e46837e948
--- /dev/null
+++ b/sql/rpl_mi.cc
@@ -0,0 +1,414 @@
+/* Copyright (C) 2000-2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#include <my_global.h> // For HAVE_REPLICATION
+#include "mysql_priv.h"
+#include <my_dir.h>
+
+#include "rpl_mi.h"
+
+#ifdef HAVE_REPLICATION
+
+
+// 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);
+
+Master_info::Master_info()
+ :Slave_reporting_capability("I/O"),
+ ssl(0), ssl_verify_server_cert(0), fd(-1), io_thd(0), inited(0),
+ abort_slave(0),slave_running(0),
+ slave_run_id(0)
+{
+ host[0] = 0; user[0] = 0; password[0] = 0;
+ ssl_ca[0]= 0; ssl_capath[0]= 0; ssl_cert[0]= 0;
+ ssl_cipher[0]= 0; ssl_key[0]= 0;
+
+ bzero((char*) &file, sizeof(file));
+ pthread_mutex_init(&run_lock, MY_MUTEX_INIT_FAST);
+ pthread_mutex_init(&data_lock, MY_MUTEX_INIT_FAST);
+ pthread_cond_init(&data_cond, NULL);
+ pthread_cond_init(&start_cond, NULL);
+ pthread_cond_init(&stop_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);
+}
+
+
+void init_master_info_with_options(Master_info* mi)
+{
+ DBUG_ENTER("init_master_info_with_options");
+
+ 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;
+ DBUG_VOID_RETURN;
+}
+
+
+enum {
+ LINES_IN_MASTER_INFO_WITH_SSL= 14,
+
+ /* 5.1.16 added value of master_ssl_verify_server_cert */
+ LINE_FOR_MASTER_SSL_VERIFY_SERVER_CERT= 15,
+
+ /* Number of lines currently used when saving master info file */
+ LINES_IN_MASTER_INFO= LINE_FOR_MASTER_SSL_VERIFY_SERVER_CERT
+};
+
+int init_master_info(Master_info* mi, const char* master_info_fname,
+ const char* slave_info_fname,
+ bool abort_if_no_master_info_file,
+ int thread_mask)
+{
+ int fd,error;
+ char fname[FN_REFLEN+128];
+ DBUG_ENTER("init_master_info");
+
+ if (mi->inited)
+ {
+ /*
+ We have to reset read position of relay-log-bin as we may have
+ already been reading from 'hotlog' when the slave was stopped
+ last time. If this case pos_in_file would be set and we would
+ get a crash when trying to read the signature for the binary
+ relay log.
+
+ We only rewind the read position if we are starting the SQL
+ thread. The handle_slave_sql thread assumes that the read
+ position is at the beginning of the file, and will read the
+ "signature" and then fast-forward to the last position read.
+ */
+ if (thread_mask & SLAVE_SQL)
+ {
+ my_b_seek(mi->rli.cur_log, (my_off_t) 0);
+ }
+ DBUG_RETURN(0);
+ }
+
+ mi->mysql=0;
+ mi->file_id=1;
+ fn_format(fname, master_info_fname, mysql_data_home, "", 4+32);
+
+ /*
+ We need a mutex while we are changing master info parameters to
+ keep other threads from reading bogus info
+ */
+
+ pthread_mutex_lock(&mi->data_lock);
+ fd = mi->fd;
+
+ /* does master.info exist ? */
+
+ if (access(fname,F_OK))
+ {
+ if (abort_if_no_master_info_file)
+ {
+ pthread_mutex_unlock(&mi->data_lock);
+ DBUG_RETURN(0);
+ }
+ /*
+ if someone removed the file from underneath our feet, just close
+ 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 )
+ {
+ sql_print_error("Failed to create a new master info file (\
+file '%s', errno %d)", fname, my_errno);
+ goto err;
+ }
+ if (init_io_cache(&mi->file, fd, IO_SIZE*2, READ_CACHE, 0L,0,
+ MYF(MY_WME)))
+ {
+ sql_print_error("Failed to create a cache on master info file (\
+file '%s')", fname);
+ goto err;
+ }
+
+ mi->fd = fd;
+ init_master_info_with_options(mi);
+
+ }
+ else // file exists
+ {
+ if (fd >= 0)
+ reinit_io_cache(&mi->file, READ_CACHE, 0L,0,0);
+ else
+ {
+ if ((fd = my_open(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);
+ goto err;
+ }
+ if (init_io_cache(&mi->file, fd, IO_SIZE*2, READ_CACHE, 0L,
+ 0, MYF(MY_WME)))
+ {
+ sql_print_error("Failed to create a cache on master info file (\
+file '%s')", fname);
+ goto err;
+ }
+ }
+
+ mi->fd = fd;
+ int port, connect_retry, master_log_pos, lines;
+ int ssl= 0, ssl_verify_server_cert= 0;
+ char *first_non_digit;
+
+ /*
+ Starting from 4.1.x master.info has new format. Now its
+ first line contains number of lines in file. By reading this
+ number we will be always distinguish to which version our
+ master.info corresponds to. We can't simply count lines in
+ file since versions before 4.1.x could generate files with more
+ lines than needed.
+ If first line doesn't contain a number or contain number less than
+ LINES_IN_MASTER_INFO_WITH_SSL then such file is treated like file
+ from pre 4.1.1 version.
+ There is no ambiguity when reading an old master.info, as before
+ 4.1.1, the first line contained the binlog's name, which is either
+ empty or has an extension (contains a '.'), so can't be confused
+ with an integer.
+
+ So we're just reading first line and trying to figure which version
+ is this.
+ */
+
+ /*
+ The first row is temporarily stored in mi->master_log_name,
+ if it is line count and not binlog name (new format) it will be
+ overwritten by the second row later.
+ */
+ if (init_strvar_from_file(mi->master_log_name,
+ sizeof(mi->master_log_name), &mi->file,
+ ""))
+ goto errwithmsg;
+
+ lines= strtoul(mi->master_log_name, &first_non_digit, 10);
+
+ if (mi->master_log_name[0]!='\0' &&
+ *first_non_digit=='\0' && lines >= LINES_IN_MASTER_INFO_WITH_SSL)
+ {
+ /* Seems to be new format => read master log name from next line */
+ if (init_strvar_from_file(mi->master_log_name,
+ sizeof(mi->master_log_name), &mi->file, ""))
+ goto errwithmsg;
+ }
+ else
+ 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->password, SCRAMBLED_PASSWORD_CHAR_LENGTH+1,
+ &mi->file, master_password) ||
+ init_intvar_from_file(&port, &mi->file, master_port) ||
+ init_intvar_from_file(&connect_retry, &mi->file,
+ master_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
+ 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) ||
+ init_strvar_from_file(mi->ssl_ca, sizeof(mi->ssl_ca),
+ &mi->file, master_ssl_ca) ||
+ init_strvar_from_file(mi->ssl_capath, sizeof(mi->ssl_capath),
+ &mi->file, master_ssl_capath) ||
+ init_strvar_from_file(mi->ssl_cert, sizeof(mi->ssl_cert),
+ &mi->file, master_ssl_cert) ||
+ init_strvar_from_file(mi->ssl_cipher, sizeof(mi->ssl_cipher),
+ &mi->file, master_ssl_cipher) ||
+ init_strvar_from_file(mi->ssl_key, sizeof(mi->ssl_key),
+ &mi->file, master_ssl_key))
+ goto errwithmsg;
+
+ /*
+ Starting from 5.1.16 ssl_verify_server_cert might be
+ in the file
+ */
+ if (lines >= LINE_FOR_MASTER_SSL_VERIFY_SERVER_CERT &&
+ init_intvar_from_file(&ssl_verify_server_cert, &mi->file, 0))
+ 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);
+#endif /* HAVE_OPENSSL */
+
+ /*
+ This has to be handled here as init_intvar_from_file can't handle
+ my_off_t types
+ */
+ mi->master_log_pos= (my_off_t) master_log_pos;
+ mi->port= (uint) port;
+ mi->connect_retry= (uint) connect_retry;
+ mi->ssl= (my_bool) ssl;
+ mi->ssl_verify_server_cert= ssl_verify_server_cert;
+ }
+ DBUG_PRINT("master_info",("log_file_name: %s position: %ld",
+ mi->master_log_name,
+ (ulong) mi->master_log_pos));
+
+ mi->rli.mi = mi;
+ if (init_relay_log_info(&mi->rli, slave_info_fname))
+ goto err;
+
+ mi->inited = 1;
+ // 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, 1))))
+ sql_print_error("Failed to flush master info file");
+ pthread_mutex_unlock(&mi->data_lock);
+ DBUG_RETURN(error);
+
+errwithmsg:
+ sql_print_error("Error reading master configuration");
+
+err:
+ if (fd >= 0)
+ {
+ my_close(fd, MYF(0));
+ end_io_cache(&mi->file);
+ }
+ mi->fd= -1;
+ pthread_mutex_unlock(&mi->data_lock);
+ DBUG_RETURN(1);
+}
+
+
+/*
+ RETURN
+ 2 - flush relay log failed
+ 1 - flush master info failed
+ 0 - all ok
+*/
+int flush_master_info(Master_info* mi, bool flush_relay_log_cache)
+{
+ IO_CACHE* file = &mi->file;
+ char lbuf[22];
+
+ DBUG_ENTER("flush_master_info");
+ DBUG_PRINT("enter",("master_pos: %ld", (long) mi->master_log_pos));
+
+ /*
+ Flush the relay log to disk. If we don't do it, then the relay log while
+ have some part (its last kilobytes) in memory only, so if the slave server
+ dies now, with, say, from master's position 100 to 150 in memory only (not
+ on disk), and with position 150 in master.info, then when the slave
+ restarts, the I/O thread will fetch binlogs from 150, so in the relay log
+ we will have "[0, 100] U [150, infinity[" and nobody will notice it, so the
+ SQL thread will jump from 100 to 150, and replication will silently break.
+
+ When we come to this place in code, relay log may or not be initialized;
+ the caller is responsible for setting 'flush_relay_log_cache' accordingly.
+ */
+ if (flush_relay_log_cache &&
+ flush_io_cache(mi->rli.relay_log.get_log_file()))
+ DBUG_RETURN(2);
+
+ /*
+ We flushed the relay log BEFORE the master.info file, because if we crash
+ now, we will get a duplicate event in the relay log at restart. If we
+ flushed in the other order, we would get a hole in the relay log.
+ And duplicate is better than hole (with a duplicate, in later versions we
+ can add detection and scrap one event; with a hole there's nothing we can
+ do).
+ */
+
+ /*
+ In certain cases this code may create master.info files that seems
+ corrupted, because of extra lines filled with garbage in the end
+ file (this happens if new contents take less space than previous
+ contents of file). But because of number of lines in the first line
+ of file we don't care about this garbage.
+ */
+
+ 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",
+ 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));
+}
+
+
+void end_master_info(Master_info* mi)
+{
+ DBUG_ENTER("end_master_info");
+
+ if (!mi->inited)
+ DBUG_VOID_RETURN;
+ end_relay_log_info(&mi->rli);
+ if (mi->fd >= 0)
+ {
+ end_io_cache(&mi->file);
+ (void)my_close(mi->fd, MYF(MY_WME));
+ mi->fd = -1;
+ }
+ mi->inited = 0;
+
+ DBUG_VOID_RETURN;
+}
+
+
+#endif /* HAVE_REPLICATION */
diff --git a/sql/rpl_mi.h b/sql/rpl_mi.h
new file mode 100644
index 00000000000..93fb0a98198
--- /dev/null
+++ b/sql/rpl_mi.h
@@ -0,0 +1,114 @@
+/* Copyright (C) 2000-2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#ifndef RPL_MI_H
+#define RPL_MI_H
+
+#ifdef HAVE_REPLICATION
+
+#include "rpl_rli.h"
+#include "rpl_reporting.h"
+
+
+/*****************************************************************************
+
+ Replication IO Thread
+
+ Master_info contains:
+ - information about how to connect to a master
+ - current master log name
+ - current master log offset
+ - misc control variables
+
+ Master_info is initialized once from the master.info file if such
+ exists. Otherwise, data members corresponding to master.info fields
+ are initialized with defaults specified by master-* options. The
+ initialization is done through init_master_info() call.
+
+ The format of master.info file:
+
+ log_name
+ log_pos
+ master_host
+ master_user
+ master_pass
+ master_port
+ master_connect_retry
+
+ To write out the contents of master.info file to disk ( needed every
+ time we read and queue data from the master ), a call to
+ flush_master_info() is required.
+
+ To clean up, call end_master_info()
+
+*****************************************************************************/
+
+class Master_info : public Slave_reporting_capability
+{
+ public:
+ Master_info();
+ ~Master_info();
+
+ /* 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 user[USERNAME_LENGTH+1];
+ char password[MAX_PASSWORD_LENGTH+1];
+ my_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];
+ my_bool ssl_verify_server_cert;
+
+ my_off_t master_log_pos;
+ 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;
+ THD *io_thd;
+ MYSQL* mysql;
+ uint32 file_id; /* for 3.23 load data infile */
+ Relay_log_info rli;
+ uint port;
+ uint connect_retry;
+#ifndef DBUG_OFF
+ int events_till_disconnect;
+#endif
+ bool inited;
+ volatile bool abort_slave;
+ volatile uint slave_running;
+ volatile ulong slave_run_id;
+ /*
+ The difference in seconds between the clock of the master and the clock of
+ the slave (second - first). It must be signed as it may be <0 or >0.
+ clock_diff_with_master is computed when the I/O thread starts; for this the
+ I/O thread does a SELECT UNIX_TIMESTAMP() on the master.
+ "how late the slave is compared to the master" is computed like this:
+ clock_of_slave - last_timestamp_executed_by_SQL_thread - clock_diff_with_master
+
+ */
+ long clock_diff_with_master;
+};
+
+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,
+ int thread_mask);
+void end_master_info(Master_info* mi);
+int flush_master_info(Master_info* mi, bool flush_relay_log_cache);
+
+#endif /* HAVE_REPLICATION */
+#endif /* RPL_MI_H */
diff --git a/sql/rpl_record.cc b/sql/rpl_record.cc
new file mode 100644
index 00000000000..7c74dcba5a0
--- /dev/null
+++ b/sql/rpl_record.cc
@@ -0,0 +1,348 @@
+/* Copyright 2007 MySQL AB. All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#include "mysql_priv.h"
+#include "rpl_rli.h"
+#include "rpl_record.h"
+#include "slave.h" // Need to pull in slave_print_msg
+#include "rpl_utility.h"
+#include "rpl_rli.h"
+
+/**
+ Pack a record of data for a table into a format suitable for
+ transfer via the binary log.
+
+ The format for a row in transfer with N fields is the following:
+
+ ceil(N/8) null bytes:
+ One null bit for every column *regardless of whether it can be
+ null or not*. This simplifies the decoding. Observe that the
+ number of null bits is equal to the number of set bits in the
+ @c cols bitmap. The number of null bytes is the smallest number
+ of bytes necessary to store the null bits.
+
+ Padding bits are 1.
+
+ N packets:
+ Each field is stored in packed format.
+
+
+ @param table Table describing the format of the record
+
+ @param cols Bitmap with a set bit for each column that should
+ be stored in the row
+
+ @param row_data Pointer to memory where row will be written
+
+ @param record Pointer to record that should be packed. It is
+ assumed that the pointer refers to either @c
+ record[0] or @c record[1], but no such check is
+ made since the code does not rely on that.
+
+ @return The number of bytes written at @c row_data.
+ */
+#if !defined(MYSQL_CLIENT)
+size_t
+pack_row(TABLE *table, MY_BITMAP const* cols,
+ uchar *row_data, const uchar *record)
+{
+ Field **p_field= table->field, *field;
+ int const null_byte_count= (bitmap_bits_set(cols) + 7) / 8;
+ uchar *pack_ptr = row_data + null_byte_count;
+ uchar *null_ptr = row_data;
+ my_ptrdiff_t const rec_offset= record - table->record[0];
+ my_ptrdiff_t const def_offset= table->s->default_values - table->record[0];
+
+ DBUG_ENTER("pack_row");
+
+ /*
+ We write the null bits and the packed records using one pass
+ through all the fields. The null bytes are written little-endian,
+ i.e., the first fields are in the first byte.
+ */
+ unsigned int null_bits= (1U << 8) - 1;
+ // Mask to mask out the correct but among the null bits
+ 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;
+ if (field->is_null(rec_offset))
+ {
+ offset= def_offset;
+ null_bits |= null_mask;
+ }
+ else
+ {
+ offset= rec_offset;
+ null_bits &= ~null_mask;
+
+ /*
+ We only store the data of the field if it is non-null
+
+ For big-endian machines, we have to make sure that the
+ length is stored in little-endian format, since this is the
+ format used for the binlog.
+ */
+#ifndef DBUG_OFF
+ const uchar *old_pack_ptr= pack_ptr;
+#endif
+ pack_ptr= field->pack(pack_ptr, field->ptr + offset,
+ field->max_data_length(), TRUE);
+ DBUG_PRINT("debug", ("field: %s; pack_ptr: 0x%lx;"
+ " pack_ptr':0x%lx; bytes: %d",
+ field->field_name, (ulong) old_pack_ptr,
+ (ulong) pack_ptr,
+ (int) (pack_ptr - old_pack_ptr)));
+ }
+
+ null_mask <<= 1;
+ if ((null_mask & 0xFF) == 0)
+ {
+ DBUG_ASSERT(null_ptr < row_data + null_byte_count);
+ null_mask = 1U;
+ *null_ptr++ = null_bits;
+ null_bits= (1U << 8) - 1;
+ }
+ }
+ }
+
+ /*
+ Write the last (partial) byte, if there is one
+ */
+ if ((null_mask & 0xFF) > 1)
+ {
+ DBUG_ASSERT(null_ptr < row_data + null_byte_count);
+ *null_ptr++ = null_bits;
+ }
+
+ /*
+ The null pointer should now point to the first byte of the
+ packed data. If it doesn't, something is very wrong.
+ */
+ DBUG_ASSERT(null_ptr == row_data + null_byte_count);
+ DBUG_DUMP("row_data", row_data, pack_ptr - row_data);
+ DBUG_RETURN(static_cast<size_t>(pack_ptr - row_data));
+}
+#endif
+
+
+/**
+ Unpack a row into @c table->record[0].
+
+ The function will always unpack into the @c table->record[0]
+ record. This is because there are too many dependencies on where
+ 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.
+
+ 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
+ @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 master_reclength
+ Pointer to variable that will be set to the length of the
+ record on the master side
+
+ @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)
+
+ */
+#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, MY_BITMAP const *cols,
+ uchar const **const row_end, ulong *const master_reclength)
+{
+ DBUG_ENTER("unpack_row");
+ DBUG_ASSERT(row_data);
+ size_t const master_null_byte_count= (bitmap_bits_set(cols) + 7) / 8;
+ int error= 0;
+
+ uchar const *null_ptr= row_data;
+ uchar const *pack_ptr= row_data + master_null_byte_count;
+
+ Field **const begin_ptr = table->field;
+ Field **field_ptr;
+ Field **const end_ptr= begin_ptr + colcnt;
+
+ DBUG_ASSERT(null_ptr < row_data + master_null_byte_count);
+
+ // Mask to mask out the correct bit among the null bits
+ unsigned int null_mask= 1U;
+ // The "current" null bits
+ unsigned int null_bits= *null_ptr++;
+ uint i= 0;
+ table_def *tabledef= ((Relay_log_info*)rli)->get_tabledef(table);
+ for (field_ptr= begin_ptr ; field_ptr < end_ptr && *field_ptr ; ++field_ptr)
+ {
+ Field *const f= *field_ptr;
+
+ /*
+ No need to bother about columns that does not exist: they have
+ gotten default values when being emptied above.
+ */
+ if (bitmap_is_set(cols, field_ptr - begin_ptr))
+ {
+ if ((null_mask & 0xFF) == 0)
+ {
+ DBUG_ASSERT(null_ptr < row_data + master_null_byte_count);
+ null_mask= 1U;
+ null_bits= *null_ptr++;
+ }
+
+ DBUG_ASSERT(null_mask & 0xFF); // One of the 8 LSB should be set
+
+ /* Field...::unpack() cannot return 0 */
+ DBUG_ASSERT(pack_ptr != NULL);
+
+ if ((null_bits & null_mask) && f->maybe_null())
+ f->set_null();
+ else
+ {
+ f->set_notnull();
+
+ /*
+ We only unpack the field if it was non-null.
+ Use the master's size information if available else call
+ normal unpack operation.
+ */
+ uint16 const metadata= tabledef->field_metadata(i);
+#ifndef DBUG_OFF
+ uchar const *const old_pack_ptr= pack_ptr;
+#endif
+ pack_ptr= f->unpack(f->ptr, pack_ptr, metadata, TRUE);
+ DBUG_PRINT("debug", ("field: %s; metadata: 0x%x;"
+ " pack_ptr: 0x%lx; pack_ptr': 0x%lx; bytes: %d",
+ f->field_name, metadata,
+ (ulong) old_pack_ptr, (ulong) pack_ptr,
+ (int) (pack_ptr - old_pack_ptr)));
+ }
+
+ null_mask <<= 1;
+ }
+ i++;
+ }
+
+ /*
+ throw away master's extra fields
+ */
+ uint max_cols= min(tabledef->size(), cols->n_bits);
+ for (; i < max_cols; i++)
+ {
+ if (bitmap_is_set(cols, i))
+ {
+ if ((null_mask & 0xFF) == 0)
+ {
+ DBUG_ASSERT(null_ptr < row_data + master_null_byte_count);
+ null_mask= 1U;
+ null_bits= *null_ptr++;
+ }
+ 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);
+ null_mask <<= 1;
+ }
+ }
+
+ /*
+ We should now have read all the null bytes, otherwise something is
+ really wrong.
+ */
+ DBUG_ASSERT(null_ptr == row_data + master_null_byte_count);
+
+ DBUG_DUMP("row_data", row_data, pack_ptr - row_data);
+
+ *row_end = pack_ptr;
+ if (master_reclength)
+ {
+ if (*field_ptr)
+ *master_reclength = (*field_ptr)->ptr - table->record[0];
+ else
+ *master_reclength = table->s->reclength;
+ }
+
+ DBUG_RETURN(error);
+}
+
+/**
+ Fills @c table->record[0] with default values.
+
+ First @c empty_record() is called and then, additionally, fields are
+ initialized explicitly with a call to @c set_default().
+
+ For optimization reasons, the explicit initialization can be skipped for
+ first @c skip fields. This is useful if later we are going to fill these
+ fields from other source (e.g. from a Rows replication event).
+
+ If @c check is true, fields are explicitly initialized only if they have
+ default value or can be NULL. Otherwise error is reported.
+
+ @param table Table whose record[0] buffer is prepared.
+ @param skip Number of columns for which default value initialization
+ should be skipped.
+ @param check Indicates if errors should be checked when setting default
+ values.
+
+ @returns 0 on success or a handler level error code
+ */
+int prepare_record(TABLE *const table,
+ const uint skip, const bool check)
+{
+ DBUG_ENTER("prepare_record");
+
+ int error= 0;
+ empty_record(table);
+
+ if (skip >= table->s->fields) // nothing to do
+ DBUG_RETURN(0);
+
+ /* Explicit initialization of fields */
+
+ for (Field **field_ptr= table->field+skip ; *field_ptr ; ++field_ptr)
+ {
+ uint32 const mask= NOT_NULL_FLAG | NO_DEFAULT_VALUE_FLAG;
+ Field *const f= *field_ptr;
+
+ if (check && ((f->flags & mask) == mask))
+ {
+ my_error(ER_NO_DEFAULT_FOR_FIELD, MYF(0), f->field_name);
+ error = HA_ERR_ROWS_EVENT_APPLY;
+ }
+ else
+ f->set_default();
+ }
+
+ DBUG_RETURN(error);
+}
+
+#endif // HAVE_REPLICATION
diff --git a/sql/matherr.c b/sql/rpl_record.h
index 4998d8b4961..f9e64f0ab1d 100644
--- a/sql/matherr.c
+++ b/sql/rpl_record.h
@@ -1,4 +1,4 @@
-/* Copyright (C) 2000-2001 MySQL AB
+/* Copyright 2007 MySQL AB. 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,30 +13,24 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
-/* Fix that we got POSTFIX_ERROR when doing unreasonable math (not core) */
-
-#include <my_global.h>
-#include <errno.h>
-
- /* Fix that we gets POSTFIX_ERROR when error in math */
-
-#if defined(HAVE_MATHERR)
-int matherr(struct exception *x)
-{
- if (x->type != PLOSS)
- x->retval=POSTFIX_ERROR;
- switch (x->type) {
- case DOMAIN:
- case SING:
- my_errno=EDOM;
- break;
- case OVERFLOW:
- case UNDERFLOW:
- my_errno=ERANGE;
- break;
- default:
- break;
- }
- return(1); /* Take no other action */
-}
+#ifndef RPL_RECORD_H
+#define RPL_RECORD_H
+
+#include <rpl_reporting.h>
+
+#if !defined(MYSQL_CLIENT)
+size_t pack_row(TABLE* table, MY_BITMAP const* cols,
+ uchar *row_data, const uchar *data);
+#endif
+
+#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, MY_BITMAP const *cols,
+ uchar const **const row_end, ulong *const master_reclength);
+
+// Fill table's record[0] with default values.
+int prepare_record(TABLE *const, const uint =0, const bool =FALSE);
+#endif
+
#endif
diff --git a/sql/rpl_record_old.cc b/sql/rpl_record_old.cc
new file mode 100644
index 00000000000..ab4e993ce41
--- /dev/null
+++ b/sql/rpl_record_old.cc
@@ -0,0 +1,174 @@
+
+#include "mysql_priv.h"
+#include "rpl_rli.h"
+#include "rpl_record_old.h"
+
+size_t
+pack_row_old(TABLE *table, MY_BITMAP const* cols,
+ uchar *row_data, const uchar *record)
+{
+ Field **p_field= table->field, *field;
+ int n_null_bytes= table->s->null_bytes;
+ uchar *ptr;
+ uint i;
+ my_ptrdiff_t const rec_offset= record - table->record[0];
+ my_ptrdiff_t const def_offset= table->s->default_values - table->record[0];
+ memcpy(row_data, record, n_null_bytes);
+ ptr= row_data+n_null_bytes;
+
+ for (i= 0 ; (field= *p_field) ; i++, p_field++)
+ {
+ if (bitmap_is_set(cols,i))
+ {
+ my_ptrdiff_t const offset=
+ field->is_null(rec_offset) ? def_offset : rec_offset;
+ field->move_field_offset(offset);
+ ptr= field->pack(ptr, field->ptr);
+ field->move_field_offset(-offset);
+ }
+ }
+ return (static_cast<size_t>(ptr - row_data));
+}
+
+
+/*
+ Unpack a row into a record.
+
+ SYNOPSIS
+ unpack_row()
+ rli Relay log info
+ table Table to unpack into
+ colcnt Number of columns to read from record
+ record Record where the data should be unpacked
+ row Packed row data
+ cols Pointer to columns data to fill in
+ row_end Pointer to variable that will hold the value of the
+ one-after-end position for the row
+ master_reclength
+ Pointer to variable that will be set to the length of the
+ record on the master side
+ rw_set Pointer to bitmap that holds either the read_set or the
+ write_set of the table
+
+ DESCRIPTION
+
+ The row is assumed to only consist of the fields for which the
+ bitset represented by 'arr' and 'bits'; the other parts of the
+ record are left alone.
+
+ At most 'colcnt' columns are read: if the table is larger than
+ that, the remaining fields are not filled in.
+
+ RETURN VALUE
+
+ Error code, or zero if no error. The following error codes can
+ be returned:
+
+ 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)
+ */
+#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
+int
+unpack_row_old(Relay_log_info *rli,
+ TABLE *table, uint const colcnt, uchar *record,
+ uchar const *row, MY_BITMAP const *cols,
+ uchar const **row_end, ulong *master_reclength,
+ MY_BITMAP* const rw_set, Log_event_type const event_type)
+{
+ DBUG_ASSERT(record && row);
+ my_ptrdiff_t const offset= record - (uchar*) table->record[0];
+ size_t master_null_bytes= table->s->null_bytes;
+
+ if (colcnt != table->s->fields)
+ {
+ Field **fptr= &table->field[colcnt-1];
+ do
+ master_null_bytes= (*fptr)->last_null_byte();
+ while (master_null_bytes == Field::LAST_NULL_BYTE_UNDEF &&
+ fptr-- > table->field);
+
+ /*
+ If master_null_bytes is LAST_NULL_BYTE_UNDEF (0) at this time,
+ there were no nullable fields nor BIT fields at all in the
+ columns that are common to the master and the slave. In that
+ case, there is only one null byte holding the X bit.
+
+ OBSERVE! There might still be nullable columns following the
+ common columns, so table->s->null_bytes might be greater than 1.
+ */
+ if (master_null_bytes == Field::LAST_NULL_BYTE_UNDEF)
+ master_null_bytes= 1;
+ }
+
+ DBUG_ASSERT(master_null_bytes <= table->s->null_bytes);
+ memcpy(record, row, master_null_bytes); // [1]
+ int error= 0;
+
+ bitmap_set_all(rw_set);
+
+ Field **const begin_ptr = table->field;
+ Field **field_ptr;
+ uchar const *ptr= row + master_null_bytes;
+ Field **const end_ptr= begin_ptr + colcnt;
+ for (field_ptr= begin_ptr ; field_ptr < end_ptr ; ++field_ptr)
+ {
+ Field *const f= *field_ptr;
+
+ if (bitmap_is_set(cols, field_ptr - begin_ptr))
+ {
+ f->move_field_offset(offset);
+ ptr= f->unpack(f->ptr, ptr);
+ f->move_field_offset(-offset);
+ /* Field...::unpack() cannot return 0 */
+ DBUG_ASSERT(ptr != NULL);
+ }
+ else
+ bitmap_clear_bit(rw_set, field_ptr - begin_ptr);
+ }
+
+ *row_end = ptr;
+ if (master_reclength)
+ {
+ if (*field_ptr)
+ *master_reclength = (*field_ptr)->ptr - table->record[0];
+ else
+ *master_reclength = table->s->reclength;
+ }
+
+ /*
+ Set properties for remaining columns, if there are any. We let the
+ corresponding bit in the write_set be set, to write the value if
+ it was not there already. We iterate over all remaining columns,
+ even if there were an error, to get as many error messages as
+ possible. We are still able to return a pointer to the next row,
+ so redo that.
+
+ This generation of error messages is only relevant when inserting
+ new rows.
+ */
+ for ( ; *field_ptr ; ++field_ptr)
+ {
+ uint32 const mask= NOT_NULL_FLAG | NO_DEFAULT_VALUE_FLAG;
+
+ DBUG_PRINT("debug", ("flags = 0x%x, mask = 0x%x, flags & mask = 0x%x",
+ (*field_ptr)->flags, mask,
+ (*field_ptr)->flags & mask));
+
+ if (event_type == WRITE_ROWS_EVENT &&
+ ((*field_ptr)->flags & mask) == mask)
+ {
+ rli->report(ERROR_LEVEL, ER_NO_DEFAULT_FOR_FIELD,
+ "Field `%s` of table `%s`.`%s` "
+ "has no default value and cannot be NULL",
+ (*field_ptr)->field_name, table->s->db.str,
+ table->s->table_name.str);
+ error = ER_NO_DEFAULT_FOR_FIELD;
+ }
+ else
+ (*field_ptr)->set_default();
+ }
+
+ return error;
+}
+#endif
diff --git a/sql/item_uniq.cc b/sql/rpl_record_old.h
index 1a5524eb1e0..bdaedd56741 100644
--- a/sql/item_uniq.cc
+++ b/sql/rpl_record_old.h
@@ -1,4 +1,4 @@
-/* Copyright (C) 2000-2001, 2005 MySQL AB
+/* Copyright 2007 MySQL AB. 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 +13,20 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
-/* Compability file */
+#ifndef RPL_RECORD_OLD_H
+#define RPL_RECORD_OLD_H
-#ifdef USE_PRAGMA_IMPLEMENTATION
-#pragma implementation // gcc: Class implementation
-#endif
-
-#include "mysql_priv.h"
+#ifndef MYSQL_CLIENT
+size_t pack_row_old(TABLE *table, MY_BITMAP const* cols,
+ uchar *row_data, const uchar *record);
-Field *Item_sum_unique_users::create_tmp_field(bool group, TABLE *table,
- uint convert_blob_length)
-{
- return new Field_long(9,maybe_null,name,table,1);
-}
+#ifdef HAVE_REPLICATION
+int unpack_row_old(Relay_log_info *rli,
+ TABLE *table, uint const colcnt, uchar *record,
+ uchar const *row, MY_BITMAP const *cols,
+ uchar const **row_end, ulong *master_reclength,
+ MY_BITMAP* const rw_set,
+ Log_event_type const event_type);
+#endif
+#endif
+#endif
diff --git a/sql/rpl_reporting.cc b/sql/rpl_reporting.cc
new file mode 100644
index 00000000000..28f257790c7
--- /dev/null
+++ b/sql/rpl_reporting.cc
@@ -0,0 +1,48 @@
+
+#include "mysql_priv.h"
+#include "rpl_reporting.h"
+
+void
+Slave_reporting_capability::report(loglevel level, int err_code,
+ const char *msg, ...) const
+{
+ void (*report_function)(const char *, ...);
+ char buff[MAX_SLAVE_ERRMSG];
+ char *pbuff= buff;
+ uint pbuffsize= sizeof(buff);
+ va_list args;
+ va_start(args, msg);
+
+ switch (level)
+ {
+ case ERROR_LEVEL:
+ /*
+ It's an error, it must be reported in Last_error and Last_errno in SHOW
+ SLAVE STATUS.
+ */
+ pbuff= m_last_error.message;
+ pbuffsize= sizeof(m_last_error.message);
+ m_last_error.number = err_code;
+ report_function= sql_print_error;
+ break;
+ case WARNING_LEVEL:
+ report_function= sql_print_warning;
+ break;
+ case INFORMATION_LEVEL:
+ report_function= sql_print_information;
+ break;
+ default:
+ DBUG_ASSERT(0); // should not come here
+ return; // don't crash production builds, just do nothing
+ }
+
+ my_vsnprintf(pbuff, pbuffsize, msg, args);
+
+ va_end(args);
+
+ /* If the msg string ends with '.', do not add a ',' it would be ugly */
+ report_function("Slave %s: %s%s Error_code: %d",
+ m_thread_name, pbuff,
+ (pbuff[0] && *(strend(pbuff)-1) == '.') ? "" : ",",
+ err_code);
+}
diff --git a/sql/rpl_reporting.h b/sql/rpl_reporting.h
new file mode 100644
index 00000000000..2e3fa3cea83
--- /dev/null
+++ b/sql/rpl_reporting.h
@@ -0,0 +1,85 @@
+#ifndef RPL_REPORTING_H
+#define RPL_REPORTING_H
+
+/**
+ Maximum size of an error message from a slave thread.
+ */
+#define MAX_SLAVE_ERRMSG 1024
+
+/**
+ Mix-in to handle the message logging and reporting for relay log
+ info and master log info structures.
+
+ By inheriting from this class, the class is imbued with
+ capabilities to do slave reporting.
+ */
+class Slave_reporting_capability
+{
+public:
+ /**
+ 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)
+ {
+ }
+
+ /**
+ Writes a message and, if it's an error message, to Last_Error
+ (which will be displayed by SHOW SLAVE STATUS).
+
+ @param level The severity level
+ @param err_code The error code
+ @param msg The message (usually related to the error
+ code, but can contain more information), in
+ printf() format.
+ */
+ void report(loglevel level, int err_code, const char *msg, ...) const
+ ATTRIBUTE_FORMAT(printf, 4, 5);
+
+ /**
+ Clear errors. They will not show up under <code>SHOW SLAVE
+ STATUS</code>.
+ */
+ void clear_error() {
+ m_last_error.clear();
+ }
+
+ /**
+ Error information structure.
+ */
+ class Error {
+ friend class Slave_reporting_capability;
+ public:
+ Error()
+ {
+ clear();
+ }
+
+ void clear()
+ {
+ number= 0;
+ message[0]= '\0';
+ }
+
+ /** Error code */
+ uint32 number;
+ /** Error message */
+ char message[MAX_SLAVE_ERRMSG];
+ };
+
+ Error const& last_error() const { return m_last_error; }
+
+private:
+ /**
+ Last error produced by the I/O or SQL thread respectively.
+ */
+ mutable Error m_last_error;
+
+ char const *const m_thread_name;
+};
+
+#endif // RPL_REPORTING_H
+
diff --git a/sql/rpl_rli.cc b/sql/rpl_rli.cc
new file mode 100644
index 00000000000..3a0f784d195
--- /dev/null
+++ b/sql/rpl_rli.cc
@@ -0,0 +1,1200 @@
+/* Copyright (C) 2000-2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#include "mysql_priv.h"
+
+#include "rpl_mi.h"
+#include "rpl_rli.h"
+#include <my_dir.h> // For MY_STAT
+#include "sql_repl.h" // For check_binlog_magic
+#include "rpl_utility.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()
+ :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),
+#if HAVE_purify
+ is_fake(FALSE),
+#endif
+ cur_log_old_open_count(0), group_relay_log_pos(0), event_relay_log_pos(0),
+ group_master_log_pos(0), log_space_total(0), ignore_log_space_limit(0),
+ last_master_timestamp(0), slave_skip_counter(0),
+ abort_pos_wait(0), slave_run_id(0), sql_thd(0),
+ inited(0), abort_slave(0), slave_running(0), until_condition(UNTIL_NONE),
+ until_log_pos(0), retried_trans(0),
+ tables_to_lock(0), tables_to_lock_count(0),
+ last_event_start_time(0), m_flags(0)
+{
+ DBUG_ENTER("Relay_log_info::Relay_log_info");
+
+ 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);
+ relay_log.init_pthread_objects();
+ DBUG_VOID_RETURN;
+}
+
+
+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);
+ relay_log.cleanup();
+ DBUG_VOID_RETURN;
+}
+
+
+int init_relay_log_info(Relay_log_info* rli,
+ const char* info_fname)
+{
+ char fname[FN_REFLEN+128];
+ int info_fd;
+ const char* msg = 0;
+ int error = 0;
+ DBUG_ENTER("init_relay_log_info");
+ DBUG_ASSERT(!rli->no_storage); // Don't init if there is no storage
+
+ 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);
+ info_fd = rli->info_fd;
+ rli->cur_log_fd = -1;
+ rli->slave_skip_counter=0;
+ rli->abort_pos_wait=0;
+ rli->log_space_limit= relay_log_space_limit;
+ rli->log_space_total= 0;
+ rli->tables_to_lock= 0;
+ rli->tables_to_lock_count= 0;
+
+ /*
+ The relay log will now be opened, as a SEQ_READ_APPEND IO_CACHE.
+ Note that the I/O thread flushes it to disk after writing every
+ event, in flush_master_info(mi, 1).
+ */
+
+ /*
+ For the maximum log size, we choose max_relay_log_size if it is
+ non-zero, max_binlog_size otherwise. If later the user does SET
+ GLOBAL on one of these variables, fix_max_binlog_size and
+ fix_max_relay_log_size will reconsider the choice (for example
+ if the user changes max_relay_log_size to zero, we have to
+ switch to using max_binlog_size for the relay log) and update
+ rli->relay_log.max_size (and mysql_bin_log.max_size).
+ */
+ {
+ char buf[FN_REFLEN];
+ const char *ln;
+ static bool name_warning_sent= 0;
+ 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)
+ {
+ /*
+ User didn't give us info to name the relay log index file.
+ Picking `hostname`-relay-bin.index like we do, causes replication to
+ fail if this slave's hostname is changed later. So, we would like to
+ instead require a name. But as we don't want to break many existing
+ setups, we only give warning, not error.
+ */
+ sql_print_warning("Neither --relay-log nor --relay-log-index were used;"
+ " so replication "
+ "may break when this MySQL server acts as a "
+ "slave and has his hostname changed!! Please "
+ "use '--relay-log=%s' to avoid this problem.", ln);
+ name_warning_sent= 1;
+ }
+ /*
+ note, that if open() fails, we'll still have index file open
+ but a destructor will take care of that
+ */
+ if (rli->relay_log.open_index_file(opt_relaylog_index_name, ln) ||
+ rli->relay_log.open(ln, LOG_BIN, 0, SEQ_READ_APPEND, 0,
+ (max_relay_log_size ? max_relay_log_size :
+ max_binlog_size), 1))
+ {
+ pthread_mutex_unlock(&rli->data_lock);
+ sql_print_error("Failed in open_log() called from init_relay_log_info()");
+ DBUG_RETURN(1);
+ }
+ rli->relay_log.is_relay_log= TRUE;
+ }
+
+ /* if file does not exist */
+ if (access(fname,F_OK))
+ {
+ /*
+ If someone removed the file from underneath our feet, just close
+ 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)
+ {
+ 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();
+ goto err;
+ }
+ if (init_io_cache(&rli->info_file, info_fd, IO_SIZE*2, READ_CACHE, 0L,0,
+ MYF(MY_WME)))
+ {
+ sql_print_error("Failed to create a cache on relay log info file '%s'",
+ fname);
+ msg= current_thd->main_da.message();
+ goto err;
+ }
+
+ /* Init relay log with first entry in the relay index file */
+ if (init_relay_log_pos(rli,NullS,BIN_LOG_HEADER_SIZE,0 /* no data lock */,
+ &msg, 0))
+ {
+ sql_print_error("Failed to open the relay log 'FIRST' (relay_log_pos 4)");
+ goto err;
+ }
+ rli->group_master_log_name[0]= 0;
+ rli->group_master_log_pos= 0;
+ rli->info_fd= info_fd;
+ }
+ else // file exists
+ {
+ if (info_fd >= 0)
+ reinit_io_cache(&rli->info_file, READ_CACHE, 0L,0,0);
+ else
+ {
+ int error=0;
+ if ((info_fd = my_open(fname, O_RDWR|O_BINARY, MYF(MY_WME))) < 0)
+ {
+ sql_print_error("\
+Failed to open the existing relay log info file '%s' (errno %d)",
+ fname, my_errno);
+ error= 1;
+ }
+ else if (init_io_cache(&rli->info_file, info_fd,
+ IO_SIZE*2, READ_CACHE, 0L, 0, MYF(MY_WME)))
+ {
+ sql_print_error("Failed to create a cache on relay log info file '%s'",
+ fname);
+ error= 1;
+ }
+ if (error)
+ {
+ if (info_fd >= 0)
+ my_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);
+ DBUG_RETURN(1);
+ }
+ }
+
+ rli->info_fd = info_fd;
+ int relay_log_pos, master_log_pos;
+ if (init_strvar_from_file(rli->group_relay_log_name,
+ sizeof(rli->group_relay_log_name),
+ &rli->info_file, "") ||
+ init_intvar_from_file(&relay_log_pos,
+ &rli->info_file, BIN_LOG_HEADER_SIZE) ||
+ init_strvar_from_file(rli->group_master_log_name,
+ sizeof(rli->group_master_log_name),
+ &rli->info_file, "") ||
+ init_intvar_from_file(&master_log_pos, &rli->info_file, 0))
+ {
+ 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);
+ rli->group_relay_log_pos= rli->event_relay_log_pos= relay_log_pos;
+ rli->group_master_log_pos= master_log_pos;
+
+ if (init_relay_log_pos(rli,
+ rli->group_relay_log_name,
+ rli->group_relay_log_pos,
+ 0 /* no data lock*/,
+ &msg, 0))
+ {
+ char llbuf[22];
+ sql_print_error("Failed to open the relay log '%s' (relay_log_pos %s)",
+ rli->group_relay_log_name,
+ llstr(rli->group_relay_log_pos, llbuf));
+ goto err;
+ }
+ }
+
+#ifndef DBUG_OFF
+ {
+ char llbuf1[22], llbuf2[22];
+ DBUG_PRINT("info", ("my_b_tell(rli->cur_log)=%s rli->event_relay_log_pos=%s",
+ llstr(my_b_tell(rli->cur_log),llbuf1),
+ llstr(rli->event_relay_log_pos,llbuf2)));
+ DBUG_ASSERT(rli->event_relay_log_pos >= BIN_LOG_HEADER_SIZE);
+ DBUG_ASSERT(my_b_tell(rli->cur_log) == rli->event_relay_log_pos);
+ }
+#endif
+
+ /*
+ Now change the cache from READ to WRITE - must do this
+ before flush_relay_log_info
+ */
+ 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");
+ if (count_relay_log_space(rli))
+ {
+ msg="Error counting relay log space";
+ goto err;
+ }
+ rli->inited= 1;
+ pthread_mutex_unlock(&rli->data_lock);
+ DBUG_RETURN(error);
+
+err:
+ sql_print_error(msg);
+ end_io_cache(&rli->info_file);
+ if (info_fd >= 0)
+ my_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);
+ DBUG_RETURN(1);
+}
+
+
+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)))
+ {
+ sql_print_error("log %s listed in the index, but failed to stat",
+ linfo->log_file_name);
+ DBUG_RETURN(1);
+ }
+ rli->log_space_total += s.st_size;
+#ifndef DBUG_OFF
+ char buf[22];
+ DBUG_PRINT("info",("log_space_total: %s", llstr(rli->log_space_total,buf)));
+#endif
+ DBUG_RETURN(0);
+}
+
+
+static int count_relay_log_space(Relay_log_info* rli)
+{
+ LOG_INFO linfo;
+ DBUG_ENTER("count_relay_log_space");
+ rli->log_space_total= 0;
+ if (rli->relay_log.find_log_pos(&linfo, NullS, 1))
+ {
+ sql_print_error("Could not find first log while counting relay log space");
+ DBUG_RETURN(1);
+ }
+ do
+ {
+ if (add_relay_log(rli,&linfo))
+ DBUG_RETURN(1);
+ } while (!rli->relay_log.find_next_log(&linfo, 1));
+ /*
+ As we have counted everything, including what may have written in a
+ preceding write, we must reset bytes_written, or we may count some space
+ twice.
+ */
+ rli->relay_log.reset_bytes_written();
+ DBUG_RETURN(0);
+}
+
+
+/*
+ Reset UNTIL condition for Relay_log_info
+
+ SYNOPSYS
+ clear_until_condition()
+ rli - Relay_log_info structure where UNTIL condition should be reset
+ */
+
+void Relay_log_info::clear_until_condition()
+{
+ DBUG_ENTER("clear_until_condition");
+
+ until_condition= Relay_log_info::UNTIL_NONE;
+ until_log_name[0]= 0;
+ until_log_pos= 0;
+ DBUG_VOID_RETURN;
+}
+
+
+/*
+ Open the given relay log
+
+ SYNOPSIS
+ init_relay_log_pos()
+ rli Relay information (will be initialized)
+ log Name of relay log file to read from. NULL = First log
+ pos Position in relay log file
+ need_data_lock Set to 1 if this functions should do mutex locks
+ errmsg Store pointer to error message here
+ look_for_description_event
+ 1 if we should look for such an event. We only need
+ this when the SQL thread starts and opens an existing
+ relay log and has to execute it (possibly from an
+ offset >4); then we need to read the first event of
+ the relay log to be able to parse the events we have
+ to execute.
+
+ DESCRIPTION
+ - Close old open relay log files.
+ - If we are using the same relay log as the running IO-thread, then set
+ rli->cur_log to point to the same IO_CACHE entry.
+ - If not, open the 'log' binary file.
+
+ TODO
+ - check proper initialization of group_master_log_name/group_master_log_pos
+
+ RETURN VALUES
+ 0 ok
+ 1 error. errmsg is set to point to the error message
+*/
+
+int init_relay_log_pos(Relay_log_info* rli,const char* log,
+ ulonglong pos, bool need_data_lock,
+ const char** errmsg,
+ bool look_for_description_event)
+{
+ DBUG_ENTER("init_relay_log_pos");
+ DBUG_PRINT("info", ("pos: %lu", (ulong) pos));
+
+ *errmsg=0;
+ pthread_mutex_t *log_lock=rli->relay_log.get_log_lock();
+
+ if (need_data_lock)
+ pthread_mutex_lock(&rli->data_lock);
+
+ /*
+ Slave threads are not the only users of init_relay_log_pos(). CHANGE MASTER
+ is, too, and init_slave() too; these 2 functions allocate a description
+ event in init_relay_log_pos, which is not freed by the terminating SQL slave
+ thread as that thread is not started by these functions. So we have to free
+ the description_event here, in case, so that there is no memory leak in
+ running, say, CHANGE MASTER.
+ */
+ delete rli->relay_log.description_event_for_exec;
+ /*
+ By default the relay log is in binlog format 3 (4.0).
+ Even if format is 4, this will work enough to read the first event
+ (Format_desc) (remember that format 4 is just lenghtened compared to format
+ 3; format 3 is a prefix of format 4).
+ */
+ rli->relay_log.description_event_for_exec= new
+ Format_description_log_event(3);
+
+ pthread_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));
+ rli->cur_log_fd = -1;
+ }
+
+ rli->group_relay_log_pos = rli->event_relay_log_pos = pos;
+
+ /*
+ Test to see if the previous run was with the skip of purging
+ If yes, we do not purge when we restart
+ */
+ if (rli->relay_log.find_log_pos(&rli->linfo, NullS, 1))
+ {
+ *errmsg="Could not find first log during relay log initialization";
+ goto err;
+ }
+
+ if (log && rli->relay_log.find_log_pos(&rli->linfo, log, 1))
+ {
+ *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);
+ if (rli->relay_log.is_active(rli->linfo.log_file_name))
+ {
+ /*
+ The IO thread is using this log file.
+ In this case, we will use the same IO_CACHE pointer to
+ read data as the IO thread is using to write data.
+ */
+ my_b_seek((rli->cur_log=rli->relay_log.get_log_file()), (off_t)0);
+ if (check_binlog_magic(rli->cur_log,errmsg))
+ goto err;
+ rli->cur_log_old_open_count=rli->relay_log.get_open_count();
+ }
+ else
+ {
+ /*
+ Open the relay log and set rli->cur_log to point at this one
+ */
+ if ((rli->cur_log_fd=open_binlog(&rli->cache_buf,
+ rli->linfo.log_file_name,errmsg)) < 0)
+ goto err;
+ rli->cur_log = &rli->cache_buf;
+ }
+ /*
+ In all cases, check_binlog_magic() has been called so we're at offset 4 for
+ sure.
+ */
+ if (pos > BIN_LOG_HEADER_SIZE) /* If pos<=4, we stay at 4 */
+ {
+ Log_event* ev;
+ while (look_for_description_event)
+ {
+ /*
+ Read the possible Format_description_log_event; if position
+ was 4, no need, it will be read naturally.
+ */
+ DBUG_PRINT("info",("looking for a Format_description_log_event"));
+
+ if (my_b_tell(rli->cur_log) >= pos)
+ break;
+
+ /*
+ Because of we have rli->data_lock and log_lock, we can safely read an
+ event
+ */
+ if (!(ev=Log_event::read_log_event(rli->cur_log,0,
+ rli->relay_log.description_event_for_exec)))
+ {
+ DBUG_PRINT("info",("could not read event, rli->cur_log->error=%d",
+ rli->cur_log->error));
+ if (rli->cur_log->error) /* not EOF */
+ {
+ *errmsg= "I/O error reading event at position 4";
+ goto err;
+ }
+ break;
+ }
+ else if (ev->get_type_code() == FORMAT_DESCRIPTION_EVENT)
+ {
+ DBUG_PRINT("info",("found Format_description_log_event"));
+ delete rli->relay_log.description_event_for_exec;
+ rli->relay_log.description_event_for_exec= (Format_description_log_event*) ev;
+ /*
+ As ev was returned by read_log_event, it has passed is_valid(), so
+ my_malloc() in ctor worked, no need to check again.
+ */
+ /*
+ Ok, we found a Format_description event. But it is not sure that this
+ describes the whole relay log; indeed, one can have this sequence
+ (starting from position 4):
+ Format_desc (of slave)
+ Rotate (of master)
+ Format_desc (of master)
+ So the Format_desc which really describes the rest of the relay log
+ is the 3rd event (it can't be further than that, because we rotate
+ the relay log when we queue a Rotate event from the master).
+ But what describes the Rotate is the first Format_desc.
+ So what we do is:
+ go on searching for Format_description events, until you exceed the
+ position (argument 'pos') or until you find another event than Rotate
+ or Format_desc.
+ */
+ }
+ else
+ {
+ DBUG_PRINT("info",("found event of another type=%d",
+ ev->get_type_code()));
+ look_for_description_event= (ev->get_type_code() == ROTATE_EVENT);
+ delete ev;
+ }
+ }
+ my_b_seek(rli->cur_log,(off_t)pos);
+#ifndef DBUG_OFF
+ {
+ char llbuf1[22], llbuf2[22];
+ DBUG_PRINT("info", ("my_b_tell(rli->cur_log)=%s rli->event_relay_log_pos=%s",
+ llstr(my_b_tell(rli->cur_log),llbuf1),
+ llstr(rli->event_relay_log_pos,llbuf2)));
+ }
+#endif
+
+ }
+
+err:
+ /*
+ If we don't purge, we can't honour relay_log_space_limit ;
+ silently discard it
+ */
+ if (!relay_log_purge)
+ rli->log_space_limit= 0;
+ pthread_cond_broadcast(&rli->data_cond);
+
+ pthread_mutex_unlock(log_lock);
+
+ if (need_data_lock)
+ pthread_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";
+
+ DBUG_RETURN ((*errmsg) ? 1 : 0);
+}
+
+
+/*
+ Waits until the SQL thread reaches (has executed up to) the
+ log/position or timed out.
+
+ SYNOPSIS
+ wait_for_pos()
+ thd client thread that sent SELECT MASTER_POS_WAIT
+ log_name log name to wait for
+ log_pos position to wait for
+ timeout timeout in seconds before giving up waiting
+
+ NOTES
+ timeout is longlong whereas it should be ulong ; but this is
+ to catch if the user submitted a negative timeout.
+
+ RETURN VALUES
+ -2 improper arguments (log_pos<0)
+ or slave not running, or master info changed
+ during the function's execution,
+ or client thread killed. -2 is translated to NULL by caller
+ -1 timed out
+ >=0 number of log events the function had to wait
+ before reaching the desired log/position
+ */
+
+int Relay_log_info::wait_for_pos(THD* thd, String* log_name,
+ longlong log_pos,
+ longlong timeout)
+{
+ int event_count = 0;
+ ulong init_abort_pos_wait;
+ int error=0;
+ struct timespec abstime; // for timeout checking
+ const char *msg;
+ DBUG_ENTER("Relay_log_info::wait_for_pos");
+
+ if (!inited)
+ DBUG_RETURN(-2);
+
+ DBUG_PRINT("enter",("log_name: '%s' log_pos: %lu timeout: %lu",
+ log_name->c_ptr(), (ulong) log_pos, (ulong) timeout));
+
+ set_timespec(abstime,timeout);
+ pthread_mutex_lock(&data_lock);
+ msg= thd->enter_cond(&data_cond, &data_lock,
+ "Waiting for the slave SQL thread to "
+ "advance position");
+ /*
+ This function will abort when it notices that some CHANGE MASTER or
+ RESET MASTER has changed the master info.
+ To catch this, these commands modify abort_pos_wait ; We just monitor
+ abort_pos_wait and see if it has changed.
+ Why do we have this mechanism instead of simply monitoring slave_running
+ in the loop (we do this too), as CHANGE MASTER/RESET SLAVE require that
+ the SQL thread be stopped?
+ This is becasue if someones does:
+ STOP SLAVE;CHANGE MASTER/RESET SLAVE; START SLAVE;
+ the change may happen very quickly and we may not notice that
+ slave_running briefly switches between 1/0/1.
+ */
+ init_abort_pos_wait= abort_pos_wait;
+
+ /*
+ We'll need to
+ handle all possible log names comparisons (e.g. 999 vs 1000).
+ We use ulong for string->number conversion ; this is no
+ stronger limitation than in find_uniq_filename in sql/log.cc
+ */
+ ulong log_name_extension;
+ char log_name_tmp[FN_REFLEN]; //make a char[] from String
+
+ strmake(log_name_tmp, log_name->ptr(), min(log_name->length(), FN_REFLEN-1));
+
+ char *p= fn_ext(log_name_tmp);
+ char *p_end;
+ if (!*p || log_pos<0)
+ {
+ error= -2; //means improper arguments
+ goto err;
+ }
+ // Convert 0-3 to 4
+ log_pos= max(log_pos, BIN_LOG_HEADER_SIZE);
+ /* p points to '.' */
+ log_name_extension= strtoul(++p, &p_end, 10);
+ /*
+ p_end points to the first invalid character.
+ If it equals to p, no digits were found, error.
+ If it contains '\0' it means conversion went ok.
+ */
+ if (p_end==p || *p_end)
+ {
+ error= -2;
+ goto err;
+ }
+
+ /* The "compare and wait" main loop */
+ while (!thd->killed &&
+ init_abort_pos_wait == abort_pos_wait &&
+ slave_running)
+ {
+ bool pos_reached;
+ int cmp_result= 0;
+
+ DBUG_PRINT("info",
+ ("init_abort_pos_wait: %ld abort_pos_wait: %ld",
+ init_abort_pos_wait, abort_pos_wait));
+ DBUG_PRINT("info",("group_master_log_name: '%s' pos: %lu",
+ group_master_log_name, (ulong) group_master_log_pos));
+
+ /*
+ group_master_log_name can be "", if we are just after a fresh
+ replication start or after a CHANGE MASTER TO MASTER_HOST/PORT
+ (before we have executed one Rotate event from the master) or
+ (rare) if the user is doing a weird slave setup (see next
+ paragraph). If group_master_log_name is "", we assume we don't
+ have enough info to do the comparison yet, so we just wait until
+ more data. In this case master_log_pos is always 0 except if
+ somebody (wrongly) sets this slave to be a slave of itself
+ without using --replicate-same-server-id (an unsupported
+ configuration which does nothing), then group_master_log_pos
+ will grow and group_master_log_name will stay "".
+ */
+ if (*group_master_log_name)
+ {
+ char *basename= (group_master_log_name +
+ dirname_length(group_master_log_name));
+ /*
+ First compare the parts before the extension.
+ Find the dot in the master's log basename,
+ and protect against user's input error :
+ if the names do not match up to '.' included, return error
+ */
+ char *q= (char*)(fn_ext(basename)+1);
+ if (strncmp(basename, log_name_tmp, (int)(q-basename)))
+ {
+ error= -2;
+ break;
+ }
+ // Now compare extensions.
+ char *q_end;
+ ulong group_master_log_name_extension= strtoul(q, &q_end, 10);
+ if (group_master_log_name_extension < log_name_extension)
+ cmp_result= -1 ;
+ else
+ cmp_result= (group_master_log_name_extension > log_name_extension) ? 1 : 0 ;
+
+ pos_reached= ((!cmp_result && group_master_log_pos >= (ulonglong)log_pos) ||
+ cmp_result > 0);
+ if (pos_reached || thd->killed)
+ break;
+ }
+
+ //wait for master update, with optional timeout.
+
+ DBUG_PRINT("info",("Waiting for master update"));
+ /*
+ We are going to pthread_cond_(timed)wait(); if the SQL thread stops it
+ will wake us up.
+ */
+ if (timeout > 0)
+ {
+ /*
+ Note that pthread_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
+ even if its condition is always immediately signaled (case of a loaded
+ master).
+ */
+ error=pthread_cond_timedwait(&data_cond, &data_lock, &abstime);
+ }
+ else
+ pthread_cond_wait(&data_cond, &data_lock);
+ DBUG_PRINT("info",("Got signal of master update or timed out"));
+ if (error == ETIMEDOUT || error == ETIME)
+ {
+ error= -1;
+ break;
+ }
+ error=0;
+ event_count++;
+ DBUG_PRINT("info",("Testing if killed or SQL thread not running"));
+ }
+
+err:
+ thd->exit_cond(msg);
+ DBUG_PRINT("exit",("killed: %d abort: %d slave_running: %d \
+improper_arguments: %d timed_out: %d",
+ thd->killed_errno(),
+ (int) (init_abort_pos_wait != abort_pos_wait),
+ (int) slave_running,
+ (int) (error == -2),
+ (int) (error == -1)));
+ if (thd->killed || init_abort_pos_wait != abort_pos_wait ||
+ !slave_running)
+ {
+ error= -2;
+ }
+ DBUG_RETURN( error ? error : event_count );
+}
+
+
+void Relay_log_info::inc_group_relay_log_pos(ulonglong log_pos,
+ bool skip_lock)
+{
+ DBUG_ENTER("Relay_log_info::inc_group_relay_log_pos");
+
+ if (!skip_lock)
+ pthread_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);
+
+ notify_group_relay_log_name_update();
+
+ /*
+ If the slave does not support transactions and replicates a transaction,
+ users should not trust group_master_log_pos (which they can display with
+ SHOW SLAVE STATUS or read from relay-log.info), because to compute
+ group_master_log_pos the slave relies on log_pos stored in the master's
+ binlog, but if we are in a master's transaction these positions are always
+ the BEGIN's one (excepted for the COMMIT), so group_master_log_pos does
+ not advance as it should on the non-transactional slave (it advances by
+ big leaps, whereas it should advance by small leaps).
+ */
+ /*
+ In 4.x we used the event's len to compute the positions here. This is
+ wrong if the event was 3.23/4.0 and has been converted to 5.0, because
+ then the event's len is not what is was in the master's binlog, so this
+ will make a wrong group_master_log_pos (yes it's a bug in 3.23->4.0
+ replication: Exec_master_log_pos is wrong). Only way to solve this is to
+ have the original offset of the end of the event the relay log. This is
+ what we do in 5.0: log_pos has become "end_log_pos" (because the real use
+ of log_pos in 4.0 was to compute the end_log_pos; so better to store
+ end_log_pos instead of begin_log_pos.
+ If we had not done this fix here, the problem would also have appeared
+ when the slave and master are 5.0 but with different event length (for
+ example the slave is more recent than the master and features the event
+ UID). It would give false MASTER_POS_WAIT, false Exec_master_log_pos in
+ SHOW SLAVE STATUS, and so the user would do some CHANGE MASTER using this
+ value which would lead to badly broken replication.
+ Even the relay_log_pos will be corrupted in this case, because the len is
+ the relay log is not "val".
+ With the end_log_pos solution, we avoid computations involving lengthes.
+ */
+ DBUG_PRINT("info", ("log_pos: %lu group_master_log_pos: %lu",
+ (long) log_pos, (long) group_master_log_pos));
+ if (log_pos) // 3.23 binlogs don't have log_posx
+ {
+ group_master_log_pos= log_pos;
+ }
+ pthread_cond_broadcast(&data_cond);
+ if (!skip_lock)
+ pthread_mutex_unlock(&data_lock);
+ DBUG_VOID_RETURN;
+}
+
+
+void Relay_log_info::close_temporary_tables()
+{
+ TABLE *table,*next;
+ DBUG_ENTER("Relay_log_info::close_temporary_tables");
+
+ for (table=save_temporary_tables ; table ; table=next)
+ {
+ next=table->next;
+ /*
+ Don't ask for disk deletion. For now, anyway they will be deleted when
+ slave restarts, but it is a better intention to not delete them.
+ */
+ DBUG_PRINT("info", ("table: 0x%lx", (long) table));
+ close_temporary(table, 1, 0);
+ }
+ save_temporary_tables= 0;
+ slave_open_temp_tables= 0;
+ DBUG_VOID_RETURN;
+}
+
+/*
+ purge_relay_logs()
+
+ NOTES
+ Assumes to have a run lock on rli and that no slave thread are running.
+*/
+
+int purge_relay_logs(Relay_log_info* rli, THD *thd, bool just_reset,
+ const char** errmsg)
+{
+ int error=0;
+ DBUG_ENTER("purge_relay_logs");
+
+ /*
+ Even if rli->inited==0, we still try to empty rli->master_log_* variables.
+ Indeed, rli->inited==0 does not imply that they already are empty.
+ It could be that slave's info initialization partly succeeded :
+ for example if relay-log.info existed but *relay-bin*.*
+ have been manually removed, init_relay_log_info reads the old
+ relay-log.info and fills rli->master_log_*, then init_relay_log_info
+ checks for the existence of the relay log, this fails and
+ init_relay_log_info leaves rli->inited to 0.
+ In that pathological case, rli->master_log_pos* will be properly reinited
+ at the next START SLAVE (as RESET SLAVE or CHANGE
+ MASTER, the callers of purge_relay_logs, will delete bogus *.info files
+ or replace them with correct files), however if the user does SHOW SLAVE
+ STATUS before START SLAVE, he will see old, confusing rli->master_log_*.
+ In other words, we reinit rli->master_log_* for SHOW SLAVE STATUS
+ to display fine in any case.
+ */
+
+ rli->group_master_log_name[0]= 0;
+ rli->group_master_log_pos= 0;
+
+ if (!rli->inited)
+ {
+ DBUG_PRINT("info", ("rli->inited == 0"));
+ DBUG_RETURN(0);
+ }
+
+ DBUG_ASSERT(rli->slave_running == 0);
+ DBUG_ASSERT(rli->mi->slave_running == 0);
+
+ rli->slave_skip_counter=0;
+ pthread_mutex_lock(&rli->data_lock);
+
+ /*
+ we close the relay log fd possibly left open by the slave SQL thread,
+ to be able to delete it; the relay log fd possibly left open by the slave
+ I/O thread will be closed naturally in reset_logs() by the
+ close(LOG_CLOSE_TO_BE_OPENED) call
+ */
+ if (rli->cur_log_fd >= 0)
+ {
+ end_io_cache(&rli->cache_buf);
+ my_close(rli->cur_log_fd, MYF(MY_WME));
+ rli->cur_log_fd= -1;
+ }
+
+ if (rli->relay_log.reset_logs(thd))
+ {
+ *errmsg = "Failed during log reset";
+ error=1;
+ 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);
+ rli->group_relay_log_pos= rli->event_relay_log_pos= BIN_LOG_HEADER_SIZE;
+ if (count_relay_log_space(rli))
+ {
+ *errmsg= "Error counting relay log space";
+ goto err;
+ }
+ if (!just_reset)
+ error= init_relay_log_pos(rli, rli->group_relay_log_name,
+ rli->group_relay_log_pos,
+ 0 /* do not need data lock */, errmsg, 0);
+
+err:
+#ifndef DBUG_OFF
+ char buf[22];
+#endif
+ DBUG_PRINT("info",("log_space_total: %s",llstr(rli->log_space_total,buf)));
+ pthread_mutex_unlock(&rli->data_lock);
+ DBUG_RETURN(error);
+}
+
+
+/*
+ Check if condition stated in UNTIL clause of START SLAVE is reached.
+ SYNOPSYS
+ Relay_log_info::is_until_satisfied()
+ master_beg_pos position of the beginning of to be executed event
+ (not log_pos member of the event that points to the
+ beginning of the following event)
+
+
+ DESCRIPTION
+ Checks if UNTIL condition is reached. Uses caching result of last
+ comparison of current log file name and target log file name. So cached
+ value should be invalidated if current log file name changes
+ (see Relay_log_info::notify_... functions).
+
+ This caching is needed to avoid of expensive string comparisons and
+ strtol() conversions needed for log names comparison. We don't need to
+ compare them each time this function is called, we only need to do this
+ when current log name changes. If we have UNTIL_MASTER_POS condition we
+ need to do this only after Rotate_log_event::do_apply_event() (which is
+ rare, so caching gives real benifit), and if we have UNTIL_RELAY_POS
+ condition then we should invalidate cached comarison value after
+ inc_group_relay_log_pos() which called for each group of events (so we
+ have some benefit if we have something like queries that use
+ autoincrement or if we have transactions).
+
+ Should be called ONLY if until_condition != UNTIL_NONE !
+ RETURN VALUE
+ true - condition met or error happened (condition seems to have
+ bad log file name)
+ false - condition not met
+*/
+
+bool Relay_log_info::is_until_satisfied(my_off_t master_beg_pos)
+{
+ const char *log_name;
+ ulonglong log_pos;
+ DBUG_ENTER("Relay_log_info::is_until_satisfied");
+
+ DBUG_ASSERT(until_condition != UNTIL_NONE);
+
+ if (until_condition == UNTIL_MASTER_POS)
+ {
+ log_name= group_master_log_name;
+ log_pos= master_beg_pos;
+ }
+ else
+ { /* until_condition == UNTIL_RELAY_POS */
+ log_name= group_relay_log_name;
+ log_pos= group_relay_log_pos;
+ }
+
+#ifndef DBUG_OFF
+ {
+ char buf[32];
+ DBUG_PRINT("info", ("group_master_log_name='%s', group_master_log_pos=%s",
+ group_master_log_name, llstr(group_master_log_pos, buf)));
+ DBUG_PRINT("info", ("group_relay_log_name='%s', group_relay_log_pos=%s",
+ group_relay_log_name, llstr(group_relay_log_pos, buf)));
+ DBUG_PRINT("info", ("(%s) log_name='%s', log_pos=%s",
+ until_condition == UNTIL_MASTER_POS ? "master" : "relay",
+ log_name, llstr(log_pos, buf)));
+ DBUG_PRINT("info", ("(%s) until_log_name='%s', until_log_pos=%s",
+ until_condition == UNTIL_MASTER_POS ? "master" : "relay",
+ until_log_name, llstr(until_log_pos, buf)));
+ }
+#endif
+
+ if (until_log_names_cmp_result == UNTIL_LOG_NAMES_CMP_UNKNOWN)
+ {
+ /*
+ We have no cached comparison results so we should compare log names
+ and cache result.
+ If we are after RESET SLAVE, and the SQL slave thread has not processed
+ any event yet, it could be that group_master_log_name is "". In that case,
+ just wait for more events (as there is no sensible comparison to do).
+ */
+
+ if (*log_name)
+ {
+ const char *basename= log_name + dirname_length(log_name);
+
+ const char *q= (const char*)(fn_ext(basename)+1);
+ if (strncmp(basename, until_log_name, (int)(q-basename)) == 0)
+ {
+ /* Now compare extensions. */
+ char *q_end;
+ ulong log_name_extension= strtoul(q, &q_end, 10);
+ if (log_name_extension < until_log_name_extension)
+ until_log_names_cmp_result= UNTIL_LOG_NAMES_CMP_LESS;
+ else
+ until_log_names_cmp_result=
+ (log_name_extension > until_log_name_extension) ?
+ UNTIL_LOG_NAMES_CMP_GREATER : UNTIL_LOG_NAMES_CMP_EQUAL ;
+ }
+ else
+ {
+ /* Probably error so we aborting */
+ sql_print_error("Slave SQL thread is stopped because UNTIL "
+ "condition is bad.");
+ DBUG_RETURN(TRUE);
+ }
+ }
+ else
+ DBUG_RETURN(until_log_pos == 0);
+ }
+
+ DBUG_RETURN(((until_log_names_cmp_result == UNTIL_LOG_NAMES_CMP_EQUAL &&
+ log_pos >= until_log_pos) ||
+ until_log_names_cmp_result == UNTIL_LOG_NAMES_CMP_GREATER));
+}
+
+
+void Relay_log_info::cached_charset_invalidate()
+{
+ DBUG_ENTER("Relay_log_info::cached_charset_invalidate");
+
+ /* Full of zeroes means uninitialized. */
+ bzero(cached_charset, sizeof(cached_charset));
+ DBUG_VOID_RETURN;
+}
+
+
+bool Relay_log_info::cached_charset_compare(char *charset) const
+{
+ DBUG_ENTER("Relay_log_info::cached_charset_compare");
+
+ if (bcmp((uchar*) cached_charset, (uchar*) charset,
+ sizeof(cached_charset)))
+ {
+ memcpy(const_cast<char*>(cached_charset), charset, sizeof(cached_charset));
+ DBUG_RETURN(1);
+ }
+ DBUG_RETURN(0);
+}
+
+
+void Relay_log_info::stmt_done(my_off_t event_master_log_pos,
+ time_t event_creation_time)
+{
+#ifndef DBUG_OFF
+ extern uint debug_not_change_ts_if_art_event;
+#endif
+ clear_flag(IN_STMT);
+
+ /*
+ If in a transaction, and if the slave supports transactions, just
+ inc_event_relay_log_pos(). We only have to check for OPTION_BEGIN
+ (not OPTION_NOT_AUTOCOMMIT) as transactions are logged with
+ BEGIN/COMMIT, not with SET AUTOCOMMIT= .
+
+ CAUTION: opt_using_transactions means innodb || bdb ; suppose the
+ master supports InnoDB and BDB, but the slave supports only BDB,
+ problems will arise: - suppose an InnoDB table is created on the
+ master, - then it will be MyISAM on the slave - but as
+ opt_using_transactions is true, the slave will believe he is
+ transactional with the MyISAM table. And problems will come when
+ one does START SLAVE; STOP SLAVE; START SLAVE; (the slave will
+ resume at BEGIN whereas there has not been any rollback). This is
+ the problem of using opt_using_transactions instead of a finer
+ "does the slave support _transactional handler used on the
+ master_".
+
+ More generally, we'll have problems when a query mixes a
+ transactional handler and MyISAM and STOP SLAVE is issued in the
+ 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)
+ inc_event_relay_log_pos();
+ else
+ {
+ inc_group_relay_log_pos(event_master_log_pos);
+ flush_relay_log_info(this);
+ /*
+ Note that Rotate_log_event::do_apply_event() does not call this
+ function, so there is no chance that a fake rotate event resets
+ last_master_timestamp. Note that we update without mutex
+ (probably ok - except in some very rare cases, only consequence
+ is that value may take some time to display in
+ Seconds_Behind_Master - not critical).
+ */
+#ifndef DBUG_OFF
+ if (!(event_creation_time == 0 && debug_not_change_ts_if_art_event > 0))
+#else
+ if (event_creation_time != 0)
+#endif
+ last_master_timestamp= event_creation_time;
+ }
+}
+
+#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
+void Relay_log_info::cleanup_context(THD *thd, bool error)
+{
+ DBUG_ENTER("Relay_log_info::cleanup_context");
+
+ DBUG_ASSERT(sql_thd == thd);
+ /*
+ 1) Instances of Table_map_log_event, if ::do_apply_event() was called on them,
+ may have opened tables, which we cannot be sure have been closed (because
+ maybe the Rows_log_event have not been found or will not be, because slave
+ SQL thread is stopping, or relay log has a missing tail etc). So we close
+ all thread's tables. And so the table mappings have to be cancelled.
+ 2) Rows_log_event::do_apply_event() may even have started statements or
+ transactions on them, which we need to rollback in case of error.
+ 3) If finding a Format_description_log_event after a BEGIN, we also need
+ to rollback before continuing with the next events.
+ 4) so we need this "context cleanup" function.
+ */
+ if (error)
+ {
+ ha_autocommit_or_rollback(thd, 1); // if a "statement transaction"
+ end_trans(thd, ROLLBACK); // if a "real transaction"
+ }
+ m_table_map.clear_tables();
+ close_thread_tables(thd);
+ clear_tables_to_lock();
+ 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;
+ DBUG_VOID_RETURN;
+}
+
+void Relay_log_info::clear_tables_to_lock()
+{
+ while (tables_to_lock)
+ {
+ uchar* to_free= reinterpret_cast<uchar*>(tables_to_lock);
+ if (tables_to_lock->m_tabledef_valid)
+ {
+ tables_to_lock->m_tabledef.table_def::~table_def();
+ tables_to_lock->m_tabledef_valid= FALSE;
+ }
+ tables_to_lock=
+ static_cast<RPL_TABLE_LIST*>(tables_to_lock->next_global);
+ tables_to_lock_count--;
+ my_free(to_free, MYF(MY_WME));
+ }
+ DBUG_ASSERT(tables_to_lock == NULL && tables_to_lock_count == 0);
+}
+
+#endif
diff --git a/sql/rpl_rli.h b/sql/rpl_rli.h
new file mode 100644
index 00000000000..e13ea93842c
--- /dev/null
+++ b/sql/rpl_rli.h
@@ -0,0 +1,418 @@
+/* Copyright (C) 2005 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#ifndef RPL_RLI_H
+#define RPL_RLI_H
+
+#include "rpl_tblmap.h"
+#include "rpl_reporting.h"
+#include "rpl_utility.h"
+
+struct RPL_TABLE_LIST;
+class Master_info;
+
+/****************************************************************************
+
+ Replication SQL Thread
+
+ Relay_log_info contains:
+ - the current relay log
+ - the current relay log offset
+ - master log name
+ - master log sequence corresponding to the last update
+ - misc information specific to the SQL thread
+
+ Relay_log_info is initialized from the slave.info file if such
+ exists. Otherwise, data members are intialized with defaults. The
+ initialization is done with init_relay_log_info() call.
+
+ The format of slave.info file:
+
+ relay_log_name
+ relay_log_pos
+ master_log_name
+ master_log_pos
+
+ To clean up, call end_relay_log_info()
+
+*****************************************************************************/
+
+class Relay_log_info : public Slave_reporting_capability
+{
+public:
+ /**
+ Flags for the state of the replication.
+ */
+ enum enum_state_flag {
+ /** The replication thread is inside a statement */
+ IN_STMT,
+
+ /** Flag counter. Should always be last */
+ STATE_FLAGS_COUNT
+ };
+
+ /*
+ If flag set, then rli does not store its state in any info file.
+ This is the case only when we execute BINLOG SQL commands inside
+ a client, non-replication thread.
+ */
+ bool no_storage;
+
+ /*
+ If true, events with the same server id should be replicated. This
+ field is set on creation of a relay log info structure by copying
+ the value of ::replicate_same_server_id and can be overridden if
+ necessary. For example of when this is done, check sql_binlog.cc,
+ where the BINLOG statement can be used to execute "raw" events.
+ */
+ bool replicate_same_server_id;
+
+ /*** The following variables can only be read when protect by data lock ****/
+
+ /*
+ info_fd - file descriptor of the info file. set only during
+ initialization or clean up - safe to read anytime
+ cur_log_fd - file descriptor of the current read relay log
+ */
+ File info_fd,cur_log_fd;
+
+ /*
+ Protected with internal locks.
+ Must get data_lock when resetting the logs.
+ */
+ MYSQL_BIN_LOG relay_log;
+ LOG_INFO linfo;
+ IO_CACHE cache_buf,*cur_log;
+
+ /* The following variables are safe to read any time */
+
+ /* IO_CACHE of the info file - set only during init or end */
+ IO_CACHE info_file;
+
+ /*
+ When we restart slave thread we need to have access to the previously
+ created temporary tables. Modified only on init/end and by the SQL
+ thread, read only by SQL thread.
+ */
+ TABLE *save_temporary_tables;
+
+ /*
+ standard lock acquistion order to avoid deadlocks:
+ run_lock, data_lock, relay_log.LOCK_log, relay_log.LOCK_index
+ */
+ pthread_mutex_t data_lock,run_lock;
+
+ /*
+ start_cond is broadcast when SQL thread is started
+ stop_cond - when stopped
+ data_cond - when data protected by data_lock changes
+ */
+ pthread_cond_t start_cond, stop_cond, data_cond;
+
+ /* parent Master_info structure */
+ Master_info *mi;
+
+ /*
+ Needed to deal properly with cur_log getting closed and re-opened with
+ a different log under our feet
+ */
+ uint32 cur_log_old_open_count;
+
+ /*
+ Let's call a group (of events) :
+ - a transaction
+ or
+ - an autocommiting query + its associated events (INSERT_ID,
+ TIMESTAMP...)
+ We need these rli coordinates :
+ - relay log name and position of the beginning of the group we currently are
+ executing. Needed to know where we have to restart when replication has
+ stopped in the middle of a group (which has been rolled back by the slave).
+ - relay log name and position just after the event we have just
+ executed. This event is part of the current group.
+ Formerly we only had the immediately above coordinates, plus a 'pending'
+ variable, but this dealt wrong with the case of a transaction starting on a
+ relay log and finishing (commiting) on another relay log. Case which can
+ happen when, for example, the relay log gets rotated because of
+ max_binlog_size.
+ */
+ char group_relay_log_name[FN_REFLEN];
+ ulonglong group_relay_log_pos;
+ char event_relay_log_name[FN_REFLEN];
+ ulonglong event_relay_log_pos;
+ ulonglong future_event_relay_log_pos;
+
+#ifdef HAVE_purify
+ bool is_fake; /* Mark that this is a fake relay log info structure */
+#endif
+
+ /*
+ Original log name and position of the group we're currently executing
+ (whose coordinates are group_relay_log_name/pos in the relay log)
+ in the master's binlog. These concern the *group*, because in the master's
+ binlog the log_pos that comes with each event is the position of the
+ beginning of the group.
+ */
+ char group_master_log_name[FN_REFLEN];
+ volatile my_off_t group_master_log_pos;
+
+ /*
+ Handling of the relay_log_space_limit optional constraint.
+ ignore_log_space_limit is used to resolve a deadlock between I/O and SQL
+ threads, the SQL thread sets it to unblock the I/O thread and make it
+ temporarily forget about the constraint.
+ */
+ ulonglong log_space_limit,log_space_total;
+ bool ignore_log_space_limit;
+
+ /*
+ When it commits, InnoDB internally stores the master log position it has
+ processed so far; the position to store is the one of the end of the
+ committing event (the COMMIT query event, or the event if in autocommit
+ mode).
+ */
+#if MYSQL_VERSION_ID < 40100
+ ulonglong future_master_log_pos;
+#else
+ ulonglong future_group_master_log_pos;
+#endif
+
+ time_t last_master_timestamp;
+
+ void clear_until_condition();
+
+ /*
+ Needed for problems when slave stops and we want to restart it
+ skipping one or more events in the master log that have caused
+ errors, and have been manually applied by DBA already.
+ */
+ 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;
+ THD * sql_thd;
+#ifndef DBUG_OFF
+ int events_till_abort;
+#endif
+
+ /* if not set, the value of other members of the structure are undefined */
+ bool inited;
+ volatile bool abort_slave;
+ volatile uint slave_running;
+
+ /*
+ Condition and its parameters from START SLAVE UNTIL clause.
+
+ UNTIL condition is tested with is_until_satisfied() method that is
+ called by exec_relay_log_event(). is_until_satisfied() caches the result
+ of the comparison of log names because log names don't change very often;
+ this cache is invalidated by parts of code which change log names with
+ notify_*_log_name_updated() methods. (They need to be called only if SQL
+ thread is running).
+ */
+
+ enum {UNTIL_NONE= 0, UNTIL_MASTER_POS, UNTIL_RELAY_POS} until_condition;
+ char until_log_name[FN_REFLEN];
+ ulonglong until_log_pos;
+ /* extension extracted from log_name and converted to int */
+ ulong until_log_name_extension;
+ /*
+ Cached result of comparison of until_log_name and current log name
+ -2 means unitialised, -1,0,1 are comarison results
+ */
+ enum
+ {
+ UNTIL_LOG_NAMES_CMP_UNKNOWN= -2, UNTIL_LOG_NAMES_CMP_LESS= -1,
+ UNTIL_LOG_NAMES_CMP_EQUAL= 0, UNTIL_LOG_NAMES_CMP_GREATER= 1
+ } until_log_names_cmp_result;
+
+ char cached_charset[6];
+ /*
+ trans_retries varies between 0 to slave_transaction_retries and counts how
+ many times the slave has retried the present transaction; gets reset to 0
+ when the transaction finally succeeds. retried_trans is a cumulative
+ counter: how many times the slave has retried a transaction (any) since
+ slave started.
+ */
+ ulong trans_retries, retried_trans;
+
+ /*
+ If the end of the hot relay log is made of master's events ignored by the
+ slave I/O thread, these two keep track of the coords (in the master's
+ binlog) of the last of these events seen by the slave I/O thread. If not,
+ ign_master_log_name_end[0] == 0.
+ As they are like a Rotate event read/written from/to the relay log, they
+ are both protected by rli->relay_log.LOCK_log.
+ */
+ char ign_master_log_name_end[FN_REFLEN];
+ ulonglong ign_master_log_pos_end;
+
+ Relay_log_info();
+ ~Relay_log_info();
+
+ /*
+ Invalidate cached until_log_name and group_relay_log_name comparison
+ result. Should be called after any update of group_realy_log_name if
+ there chances that sql_thread is running.
+ */
+ inline void notify_group_relay_log_name_update()
+ {
+ if (until_condition==UNTIL_RELAY_POS)
+ until_log_names_cmp_result= UNTIL_LOG_NAMES_CMP_UNKNOWN;
+ }
+
+ /*
+ The same as previous but for group_master_log_name.
+ */
+ inline void notify_group_master_log_name_update()
+ {
+ if (until_condition==UNTIL_MASTER_POS)
+ until_log_names_cmp_result= UNTIL_LOG_NAMES_CMP_UNKNOWN;
+ }
+
+ inline void inc_event_relay_log_pos()
+ {
+ event_relay_log_pos= future_event_relay_log_pos;
+ }
+
+ void inc_group_relay_log_pos(ulonglong log_pos,
+ bool skip_lock=0);
+
+ int wait_for_pos(THD* thd, String* log_name, longlong log_pos,
+ longlong timeout);
+ void close_temporary_tables();
+
+ /* Check if UNTIL condition is satisfied. See slave.cc for more. */
+ bool is_until_satisfied(my_off_t master_beg_pos);
+ inline ulonglong until_pos()
+ {
+ return ((until_condition == UNTIL_MASTER_POS) ? group_master_log_pos :
+ group_relay_log_pos);
+ }
+
+ RPL_TABLE_LIST *tables_to_lock; /* RBR: Tables to lock */
+ uint tables_to_lock_count; /* RBR: Count of tables to lock */
+ table_mapping m_table_map; /* RBR: Mapping table-id to table */
+
+ inline table_def *get_tabledef(TABLE *tbl)
+ {
+ 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);
+ }
+
+ /*
+ Last charset (6 bytes) seen by slave SQL thread is cached here; it helps
+ the thread save 3 get_charset() per Query_log_event if the charset is not
+ changing from event to event (common situation).
+ When the 6 bytes are equal to 0 is used to mean "cache is invalidated".
+ */
+ void cached_charset_invalidate();
+ bool cached_charset_compare(char *charset) const;
+
+ void cleanup_context(THD *, bool);
+ void 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).
+ */
+ time_t last_event_start_time;
+
+ /**
+ Helper function to do after statement completion.
+
+ This function is called from an event to complete the group by
+ either stepping the group position, if the "statement" is not
+ inside a transaction; or increase the event position, if the
+ "statement" is inside a transaction.
+
+ @param event_log_pos
+ Master log position of the event. The position is recorded in the
+ relay log info and used to produce information for <code>SHOW
+ SLAVE STATUS</code>.
+
+ @param event_creation_time
+ Timestamp for the creation of the event on the master side. The
+ time stamp is recorded in the relay log info and used to compute
+ the <code>Seconds_behind_master</code> field.
+ */
+ void stmt_done(my_off_t event_log_pos,
+ time_t event_creation_time);
+
+
+ /**
+ Set the value of a replication state flag.
+
+ @param flag Flag to set
+ */
+ void set_flag(enum_state_flag flag)
+ {
+ m_flags |= (1UL << flag);
+ }
+
+ /**
+ Get the value of a replication state flag.
+
+ @param flag Flag to get value of
+
+ @return @c true if the flag was set, @c false otherwise.
+ */
+ bool get_flag(enum_state_flag flag)
+ {
+ return m_flags & (1UL << flag);
+ }
+
+ /**
+ Clear the value of a replication state flag.
+
+ @param flag Flag to clear
+ */
+ void clear_flag(enum_state_flag flag)
+ {
+ m_flags &= ~(1UL << flag);
+ }
+
+ /**
+ Is the replication inside a group?
+
+ Replication is inside a group if either:
+ - The OPTION_BEGIN flag is set, meaning we're inside a transaction
+ - The RLI_IN_STMT flag is set, meaning we're inside a statement
+
+ @retval true Replication thread is currently inside a group
+ @retval false Replication thread is currently not inside a group
+ */
+ bool is_in_group() const {
+ return (sql_thd->options & OPTION_BEGIN) ||
+ (m_flags & (1UL << IN_STMT));
+ }
+
+private:
+ uint32 m_flags;
+};
+
+
+// Defined in rpl_rli.cc
+int init_relay_log_info(Relay_log_info* rli, const char* info_fname);
+
+
+#endif /* RPL_RLI_H */
diff --git a/sql/rpl_tblmap.cc b/sql/rpl_tblmap.cc
new file mode 100644
index 00000000000..a004c354263
--- /dev/null
+++ b/sql/rpl_tblmap.cc
@@ -0,0 +1,164 @@
+/* Copyright (C) 2005 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#include "mysql_priv.h"
+
+#ifdef HAVE_REPLICATION
+
+#include "rpl_tblmap.h"
+
+#ifdef MYSQL_CLIENT
+#define MAYBE_TABLE_NAME(T) ("")
+#else
+#define MAYBE_TABLE_NAME(T) ((T) ? (T)->s->table_name.str : "<>")
+#endif
+#define TABLE_ID_HASH_SIZE 32
+#define TABLE_ID_CHUNK 256
+
+table_mapping::table_mapping()
+ : m_free(0)
+{
+ /*
+ 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.
+ */
+ (void) 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);
+}
+
+table_mapping::~table_mapping()
+{
+#ifdef MYSQL_CLIENT
+ clear_tables();
+#endif
+ hash_free(&m_table_ids);
+ free_root(&m_mem_root, MYF(0));
+}
+
+TABLE* table_mapping::get_table(ulong table_id)
+{
+ DBUG_ENTER("table_mapping::get_table(ulong)");
+ DBUG_PRINT("enter", ("table_id: %lu", table_id));
+ entry *e= find_entry(table_id);
+ if (e)
+ {
+ DBUG_PRINT("info", ("tid %lu -> table 0x%lx (%s)",
+ table_id, (long) e->table,
+ MAYBE_TABLE_NAME(e->table)));
+ DBUG_RETURN(e->table);
+ }
+
+ DBUG_PRINT("info", ("tid %lu is not mapped!", table_id));
+ DBUG_RETURN(NULL);
+}
+
+/*
+ Called when we are out of table id entries. Creates TABLE_ID_CHUNK
+ new entries, chain them and attach them at the head of the list of free
+ (free for use) entries.
+*/
+int table_mapping::expand()
+{
+ /*
+ If we wanted to use "tmp= new (&m_mem_root) entry[TABLE_ID_CHUNK]",
+ we would have to make "entry" derive from Sql_alloc but then it would not
+ be a POD anymore and we want it to be (see rpl_tblmap.h). So we allocate
+ in C.
+ */
+ entry *tmp= (entry *)alloc_root(&m_mem_root, TABLE_ID_CHUNK*sizeof(entry));
+ if (tmp == NULL)
+ return ERR_MEMORY_ALLOCATION; // Memory allocation failed
+
+ /* Find the end of this fresh new array of free entries */
+ entry *e_end= tmp+TABLE_ID_CHUNK-1;
+ for (entry *e= tmp; e < e_end; e++)
+ e->next= e+1;
+ e_end->next= m_free;
+ m_free= tmp;
+ return 0;
+}
+
+int table_mapping::set_table(ulong table_id, TABLE* table)
+{
+ DBUG_ENTER("table_mapping::set_table(ulong,TABLE*)");
+ DBUG_PRINT("enter", ("table_id: %lu table: 0x%lx (%s)",
+ table_id,
+ (long) table, MAYBE_TABLE_NAME(table)));
+ entry *e= find_entry(table_id);
+ if (e == 0)
+ {
+ if (m_free == 0 && expand())
+ DBUG_RETURN(ERR_MEMORY_ALLOCATION); // Memory allocation failed
+ e= m_free;
+ m_free= m_free->next;
+ }
+ else
+ {
+#ifdef MYSQL_CLIENT
+ free_table_map_log_event(e->table);
+#endif
+ hash_delete(&m_table_ids,(uchar *)e);
+ }
+ e->table_id= table_id;
+ e->table= table;
+ my_hash_insert(&m_table_ids,(uchar *)e);
+
+ DBUG_PRINT("info", ("tid %lu -> table 0x%lx (%s)",
+ table_id, (long) e->table,
+ MAYBE_TABLE_NAME(e->table)));
+ DBUG_RETURN(0); // All OK
+}
+
+int table_mapping::remove_table(ulong table_id)
+{
+ entry *e= find_entry(table_id);
+ if (e)
+ {
+ 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;
+ return 0; // All OK
+ }
+ return 1; // No table to remove
+}
+
+/*
+ Puts all entries into the list of free-for-use entries (does not free any
+ memory), and empties the hash.
+*/
+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);
+#ifdef MYSQL_CLIENT
+ free_table_map_log_event(e->table);
+#endif
+ e->next= m_free;
+ m_free= e;
+ }
+ my_hash_reset(&m_table_ids);
+ DBUG_VOID_RETURN;
+}
+
+#endif
diff --git a/sql/rpl_tblmap.h b/sql/rpl_tblmap.h
new file mode 100644
index 00000000000..3b5b10be580
--- /dev/null
+++ b/sql/rpl_tblmap.h
@@ -0,0 +1,111 @@
+/* Copyright (C) 2005 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#ifndef TABLE_MAPPING_H
+#define TABLE_MAPPING_H
+
+/* Forward declarations */
+#ifndef MYSQL_CLIENT
+struct st_table;
+typedef st_table TABLE;
+#else
+class Table_map_log_event;
+typedef Table_map_log_event TABLE;
+void free_table_map_log_event(TABLE *table);
+#endif
+
+
+/*
+ CLASS table_mapping
+
+ RESPONSIBILITIES
+ The table mapping is used to map table id's to table pointers
+
+ COLLABORATION
+ RELAY_LOG For mapping table id:s to tables when receiving events.
+ */
+
+/*
+ Guilhem to Mats:
+ in the table_mapping class, the memory is allocated and never freed (until
+ destruction). So this is a good candidate for allocating inside a MEM_ROOT:
+ it gives the efficient allocation in chunks (like in expand()). So I have
+ introduced a MEM_ROOT.
+
+ Note that inheriting from Sql_alloc had no effect: it has effects only when
+ "ptr= new table_mapping" is called, and this is never called. And it would
+ then allocate from thd->mem_root which is a highly volatile object (reset
+ from example after executing each query, see dispatch_command(), it has a
+ free_root() at end); as the table_mapping object is supposed to live longer
+ than a query, it was dangerous.
+ A dedicated MEM_ROOT needs to be used, see below.
+*/
+
+class table_mapping {
+
+private:
+ MEM_ROOT m_mem_root;
+
+public:
+
+ enum enum_error {
+ ERR_NO_ERROR = 0,
+ ERR_LIMIT_EXCEEDED,
+ ERR_MEMORY_ALLOCATION
+ };
+
+ table_mapping();
+ ~table_mapping();
+
+ TABLE* get_table(ulong table_id);
+
+ int set_table(ulong table_id, TABLE* table);
+ int remove_table(ulong table_id);
+ void clear_tables();
+ ulong count() const { return m_table_ids.records; }
+
+private:
+ /*
+ This is a POD (Plain Old Data). Keep it that way (we apply offsetof() to
+ it, which only works for PODs)
+ */
+ struct entry {
+ ulong table_id;
+ union {
+ TABLE *table;
+ entry *next;
+ };
+ };
+
+ entry *find_entry(ulong table_id)
+ {
+ return (entry *)hash_search(&m_table_ids,
+ (uchar*)&table_id,
+ sizeof(table_id));
+ }
+ int expand();
+
+ /*
+ Head of the list of free entries; "free" in the sense that it's an
+ allocated entry free for use, NOT in the sense that it's freed
+ memory.
+ */
+ entry *m_free;
+
+ /* Correspondance between an id (a number) and a TABLE object */
+ HASH m_table_ids;
+};
+
+#endif
diff --git a/sql/rpl_utility.cc b/sql/rpl_utility.cc
new file mode 100644
index 00000000000..e34f8561051
--- /dev/null
+++ b/sql/rpl_utility.cc
@@ -0,0 +1,226 @@
+/* Copyright (C) 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 St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "rpl_utility.h"
+#include "rpl_rli.h"
+
+/*********************************************************************
+ * table_def member definitions *
+ *********************************************************************/
+
+/*
+ This function returns the field size in raw bytes based on the type
+ and the encoded field data from the master's raw data.
+*/
+uint32 table_def::calc_field_size(uint col, uchar *master_data) const
+{
+ uint32 length;
+
+ switch (type(col)) {
+ case MYSQL_TYPE_NEWDECIMAL:
+ length= my_decimal_get_binary_size(m_field_metadata[col] >> 8,
+ m_field_metadata[col] & 0xff);
+ break;
+ case MYSQL_TYPE_DECIMAL:
+ case MYSQL_TYPE_FLOAT:
+ case MYSQL_TYPE_DOUBLE:
+ length= m_field_metadata[col];
+ break;
+ /*
+ The cases for SET and ENUM are include for completeness, however
+ both are mapped to type MYSQL_TYPE_STRING and their real types
+ are encoded in the field metadata.
+ */
+ case MYSQL_TYPE_SET:
+ case MYSQL_TYPE_ENUM:
+ case MYSQL_TYPE_STRING:
+ {
+ uchar type= m_field_metadata[col] >> 8U;
+ if ((type == MYSQL_TYPE_SET) || (type == MYSQL_TYPE_ENUM))
+ length= m_field_metadata[col] & 0x00ff;
+ else
+ {
+ /*
+ We are reading the actual size from the master_data record
+ because this field has the actual lengh stored in the first
+ byte.
+ */
+ length= (uint) *master_data + 1;
+ DBUG_ASSERT(length != 0);
+ }
+ break;
+ }
+ case MYSQL_TYPE_YEAR:
+ case MYSQL_TYPE_TINY:
+ length= 1;
+ break;
+ case MYSQL_TYPE_SHORT:
+ length= 2;
+ break;
+ case MYSQL_TYPE_INT24:
+ length= 3;
+ break;
+ case MYSQL_TYPE_LONG:
+ length= 4;
+ break;
+#ifdef HAVE_LONG_LONG
+ case MYSQL_TYPE_LONGLONG:
+ length= 8;
+ break;
+#endif
+ case MYSQL_TYPE_NULL:
+ length= 0;
+ break;
+ case MYSQL_TYPE_NEWDATE:
+ length= 3;
+ break;
+ case MYSQL_TYPE_DATE:
+ case MYSQL_TYPE_TIME:
+ length= 3;
+ break;
+ case MYSQL_TYPE_TIMESTAMP:
+ length= 4;
+ break;
+ case MYSQL_TYPE_DATETIME:
+ length= 8;
+ break;
+ case MYSQL_TYPE_BIT:
+ {
+ /*
+ Decode the size of the bit field from the master.
+ from_len is the length in bytes from the master
+ from_bit_len is the number of extra bits stored in the master record
+ If from_bit_len is not 0, add 1 to the length to account for accurate
+ number of bytes needed.
+ */
+ uint from_len= (m_field_metadata[col] >> 8U) & 0x00ff;
+ uint from_bit_len= m_field_metadata[col] & 0x00ff;
+ DBUG_ASSERT(from_bit_len <= 7);
+ length= from_len + ((from_bit_len > 0) ? 1 : 0);
+ break;
+ }
+ 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;
+ }
+ case MYSQL_TYPE_TINY_BLOB:
+ case MYSQL_TYPE_MEDIUM_BLOB:
+ case MYSQL_TYPE_LONG_BLOB:
+ 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, TRUE);
+#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
+ packlength using the internal 'table' pointer) and replication
+ is using a fixed format for storing data in the binlog.
+ */
+ switch (m_field_metadata[col]) {
+ case 1:
+ length= *master_data;
+ break;
+ case 2:
+ length= uint2korr(master_data);
+ break;
+ case 3:
+ length= uint3korr(master_data);
+ break;
+ case 4:
+ length= uint4korr(master_data);
+ break;
+ default:
+ DBUG_ASSERT(0); // Should not come here
+ break;
+ }
+
+ length+= m_field_metadata[col];
+#endif
+ break;
+ }
+ default:
+ length= ~(uint32) 0;
+ }
+ return length;
+}
+
+/*
+ Is the definition compatible with a table?
+
+*/
+int
+table_def::compatible_with(Relay_log_info const *rli_arg, TABLE *table)
+ 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;
+
+ for (uint col= 0 ; col < cols_to_check ; ++col)
+ {
+ Field *const field= table->field[col];
+ if (field->type() != type(col))
+ {
+ 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);
+ }
+ /*
+ Check the slave's field size against that of the master.
+ */
+ if (!error &&
+ !field->compatible_field_size(field_metadata(col), rli_arg))
+ {
+ 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);
+ }
+ }
+
+ return error;
+}
diff --git a/sql/rpl_utility.h b/sql/rpl_utility.h
new file mode 100644
index 00000000000..8e2f4a7374f
--- /dev/null
+++ b/sql/rpl_utility.h
@@ -0,0 +1,306 @@
+/* Copyright (C) 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 St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#ifndef RPL_UTILITY_H
+#define RPL_UTILITY_H
+
+#ifndef __cplusplus
+#error "Don't include this C++ header file from a non-C++ file!"
+#endif
+
+#include "mysql_priv.h"
+
+class Relay_log_info;
+
+
+/**
+ A table definition from the master.
+
+ The responsibilities of this class is:
+ - Extract and decode table definition data from the table map event
+ - Check if table definition in table map is compatible with table
+ definition on slave
+
+ Currently, the only field type data available is an array of the
+ type operators that are present in the table map event.
+
+ @todo Add type operands to this structure to allow detection of
+ difference between, e.g., BIT(5) and BIT(10).
+ */
+
+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 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)
+ : m_size(size), m_type(0), m_field_metadata_size(metadata_size),
+ m_field_metadata(0), m_null_bits(0), 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:
+ {
+ /*
+ 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() {
+ my_free(m_memory, MYF(0));
+#ifndef DBUG_OFF
+ m_type= 0;
+ m_size= 0;
+#endif
+ }
+
+ /**
+ Return the number of fields there is type data for.
+
+ @return The number of fields that there is type data for.
+ */
+ ulong size() const { return m_size; }
+
+
+ /*
+ Return a representation of the type data for one field.
+
+ @param index Field index to return data for
+
+ @return Will return a representation of the type data for field
+ <code>index</code>. Currently, only the type identifier is
+ returned.
+ */
+ field_type type(ulong index) const
+ {
+ DBUG_ASSERT(index < m_size);
+ return m_type[index];
+ }
+
+
+ /*
+ This function allows callers to get the extra field data from the
+ table map for a given field. If there is no metadata for that field
+ or there is no extra metadata at all, the function returns 0.
+
+ The function returns the value for the field metadata for column at
+ position indicated by index. As mentioned, if the field was a type
+ that stores field metadata, that value is returned else zero (0) is
+ returned. This method is used in the unpack() methods of the
+ corresponding fields to properly extract the data from the binary log
+ in the event that the master's field is smaller than the slave.
+ */
+ uint16 field_metadata(uint index) const
+ {
+ DBUG_ASSERT(index < m_size);
+ if (m_field_metadata_size)
+ return m_field_metadata[index];
+ else
+ return 0;
+ }
+
+ /*
+ This function returns whether the field on the master can be null.
+ This value is derived from field->maybe_null().
+ */
+ my_bool maybe_null(uint index) const
+ {
+ DBUG_ASSERT(index < m_size);
+ return ((m_null_bits[(index / 8)] &
+ (1 << (index % 8))) == (1 << (index %8)));
+ }
+
+ /*
+ This function returns the field size in raw bytes based on the type
+ and the encoded field data from the master's raw data. This method can
+ be used for situations where the slave needs to skip a column (e.g.,
+ WL#3915) or needs to advance the pointer for the fields in the raw
+ data from the master to a specific column.
+ */
+ uint32 calc_field_size(uint col, uchar *master_data) const;
+
+ /**
+ Decide if the table definition is compatible with a table.
+
+ Compare the definition with a table to see if it is compatible
+ 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
+
+ @param rli Pointer to relay log info
+ @param table Pointer to table to compare with.
+
+ @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;
+#endif
+
+private:
+ ulong m_size; // Number of elements in the types array
+ field_type *m_type; // Array of type descriptors
+ uint m_field_metadata_size;
+ uint16 *m_field_metadata;
+ uchar *m_null_bits;
+ uchar *m_memory;
+};
+
+
+#ifndef MYSQL_CLIENT
+/**
+ Extend the normal table list with a few new fields needed by the
+ slave thread, but nowhere else.
+ */
+struct RPL_TABLE_LIST
+ : public TABLE_LIST
+{
+ bool m_tabledef_valid;
+ table_def m_tabledef;
+};
+
+
+/* Anonymous namespace for template functions/classes */
+namespace {
+
+ /*
+ Smart pointer that will automatically call my_afree (a macro) when
+ the pointer goes out of scope. This is used so that I do not have
+ to remember to call my_afree() before each return. There is no
+ overhead associated with this, since all functions are inline.
+
+ I (Matz) would prefer to use the free function as a template
+ parameter, but that is not possible when the "function" is a
+ macro.
+ */
+ template <class Obj>
+ class auto_afree_ptr
+ {
+ Obj* m_ptr;
+ public:
+ auto_afree_ptr(Obj* ptr) : m_ptr(ptr) { }
+ ~auto_afree_ptr() { if (m_ptr) my_afree(m_ptr); }
+ void assign(Obj* ptr) {
+ /* Only to be called if it hasn't been given a value before. */
+ DBUG_ASSERT(m_ptr == NULL);
+ m_ptr= ptr;
+ }
+ Obj* get() { return m_ptr; }
+ };
+
+}
+#endif
+
+#define DBUG_PRINT_BITSET(N,FRM,BS) \
+ do { \
+ char buf[256]; \
+ for (uint i = 0 ; i < (BS)->n_bits ; ++i) \
+ buf[i] = bitmap_is_set((BS), i) ? '1' : '0'; \
+ buf[(BS)->n_bits] = '\0'; \
+ DBUG_PRINT((N), ((FRM), buf)); \
+ } while (0)
+
+#endif /* RPL_UTILITY_H */
diff --git a/sql/scheduler.cc b/sql/scheduler.cc
new file mode 100644
index 00000000000..b05bdf4756f
--- /dev/null
+++ b/sql/scheduler.cc
@@ -0,0 +1,88 @@
+/* Copyright (C) 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+/*
+ Implementation for the thread scheduler
+*/
+
+#ifdef USE_PRAGMA_INTERFACE
+#pragma implementation
+#endif
+
+#include <mysql_priv.h>
+
+/*
+ '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)
+{}
+
+
+/*
+ End connection, in case when we are using 'no-threads'
+*/
+
+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;
+#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)
+{
+ func->max_threads= max_connections;
+ func->add_connection= create_thread_to_handle_connection;
+ func->end_thread= one_thread_per_connection_end;
+}
+#endif /* EMBEDDED_LIBRARY */
diff --git a/sql/scheduler.h b/sql/scheduler.h
new file mode 100644
index 00000000000..46bbd300cbb
--- /dev/null
+++ b/sql/scheduler.h
@@ -0,0 +1,60 @@
+/* Copyright (C) 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+/*
+ Classes for the thread scheduler
+*/
+
+#ifdef USE_PRAGMA_INTERFACE
+#pragma interface
+#endif
+
+class THD;
+
+/* Functions used when manipulating threads */
+
+class scheduler_functions
+{
+public:
+ uint max_threads;
+ bool (*init)(void);
+ bool (*init_new_connection_thread)(void);
+ void (*add_connection)(THD *thd);
+ void (*post_kill_notification)(THD *thd);
+ bool (*end_thread)(THD *thd, bool cache_thread);
+ void (*end)(void);
+ scheduler_functions();
+};
+
+enum scheduler_types
+{
+ SCHEDULER_ONE_THREAD_PER_CONNECTION=0,
+ SCHEDULER_NO_THREADS,
+ SCHEDULER_POOL_OF_THREADS
+};
+
+void one_thread_per_connection_scheduler(scheduler_functions* func);
+void one_thread_scheduler(scheduler_functions* func);
+
+enum pool_command_op
+{
+ NOT_IN_USE_OP= 0, NORMAL_OP= 1, CONNECT_OP, KILL_OP, DIE_OP
+};
+
+#define HAVE_POOL_OF_THREADS 0 /* For easyer tests */
+#define pool_of_threads_scheduler(A) one_thread_per_connection_scheduler(A)
+
+class thd_scheduler
+{};
diff --git a/sql/set_var.cc b/sql/set_var.cc
index 735bb5279bd..f14068fcfcb 100644
--- a/sql/set_var.cc
+++ b/sql/set_var.cc
@@ -1,4 +1,4 @@
-/* Copyright (C) 2000-2003 MySQL AB
+/* Copyright 2000-2008 MySQL AB, 2008 Sun Microsystems, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -13,38 +13,38 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
-/*
+/**
+ @file
+
+ @brief
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 should be changeable or one should be able to access it
- with @@variable_name, it should be added to the 'list of all variables'
- list (sys_variables) 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!
- - If the variable should show up in 'show variables' add it to the
- init_vars[] struct in this file
- NOTES:
- - Be careful with var->save_result: sys_var::check() only updates
+ @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).
-
- TODO:
- - Add full support for the variable character_set (for 4.1)
-
- - 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.
*/
#ifdef USE_PRAGMA_IMPLEMENTATION
@@ -54,21 +54,31 @@
#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>
+#include <my_dir.h>
-#ifdef HAVE_BERKELEY_DB
-#include "ha_berkeley.h"
-#endif
-#ifdef HAVE_INNOBASE_DB
-#include "ha_innodb.h"
+#include "events.h"
+
+/* 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_NDBCLUSTER_DB
-#include "ha_ndbcluster.h"
+
+#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;
+
+
static HASH system_variable_hash;
+
const char *bool_type_names[]= { "OFF", "ON", NullS };
TYPELIB bool_typelib=
{
@@ -82,6 +92,16 @@ TYPELIB delay_key_write_typelib=
delay_key_write_type_names, NULL
};
+const char *slave_exec_mode_names[]=
+{ "STRICT", "IDEMPOTENT", NullS };
+static const 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, (unsigned int *) 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);
@@ -94,8 +114,9 @@ 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);
-static bool set_log_bin(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);
@@ -115,1029 +136,761 @@ static void fix_trans_mem_root(THD *thd, enum_var_type type);
static void fix_server_id(THD *thd, enum_var_type type);
static ulonglong fix_unsigned(THD *, ulonglong, const struct my_option *);
static bool get_unsigned(THD *thd, set_var *var);
-static void throw_bounds_warning(THD *thd, const char *name, ulonglong num);
+bool throw_bounds_warning(THD *thd, bool fixed, bool unsignd,
+ const char *name, longlong val);
static KEY_CACHE *create_key_cache(const char *name, uint length);
void fix_sql_mode_var(THD *thd, enum_var_type type);
-static byte *get_error_count(THD *thd);
-static byte *get_warning_count(THD *thd);
-static byte *get_have_innodb(THD *thd);
-static byte *get_tmpdir(THD *thd);
+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);
/*
Variable definition list
These are variables that can be set from the command line, in
- alphabetic order
+ alphabetic order.
+
+ The variables are linked into the list. A variable is added to
+ it in the constructor (see sys_var class for details).
*/
-sys_var_thd_ulong sys_auto_increment_increment("auto_increment_increment",
- &SV::auto_increment_increment);
-sys_var_thd_ulong sys_auto_increment_offset("auto_increment_offset",
- &SV::auto_increment_offset);
+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);
-sys_var_bool_ptr sys_automatic_sp_privileges("automatic_sp_privileges",
+static sys_var_bool_ptr sys_automatic_sp_privileges(&vars, "automatic_sp_privileges",
&sp_automatic_privileges);
-sys_var_const_os_str sys_basedir("basedir", mysql_home);
-sys_var_long_ptr sys_binlog_cache_size("binlog_cache_size",
+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_long_ptr sys_binlog_cache_size(&vars, "binlog_cache_size",
&binlog_cache_size);
-sys_var_thd_ulong sys_bulk_insert_buff_size("bulk_insert_buffer_size",
+static sys_var_thd_binlog_format sys_binlog_format(&vars, "binlog_format",
+ &SV::binlog_format);
+static sys_var_thd_ulong sys_bulk_insert_buff_size(&vars, "bulk_insert_buffer_size",
&SV::bulk_insert_buff_size);
-sys_var_character_set_server sys_character_set_server("character_set_server");
-sys_var_const_str sys_charset_system("character_set_system",
+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);
-sys_var_character_set_database sys_character_set_database("character_set_database");
-sys_var_character_set_client sys_character_set_client("character_set_client");
-sys_var_character_set_connection sys_character_set_connection("character_set_connection");
-sys_var_character_set_results sys_character_set_results("character_set_results");
-sys_var_character_set_filesystem sys_character_set_filesystem("character_set_filesystem");
-sys_var_const_os_str sys_character_sets_dir("character_sets_dir",
- mysql_charsets_dir);
-sys_var_thd_ulong sys_completion_type("completion_type",
+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);
-sys_var_collation_connection sys_collation_connection("collation_connection");
-sys_var_collation_database sys_collation_database("collation_database");
-sys_var_collation_server sys_collation_server("collation_server");
-sys_var_long_ptr sys_concurrent_insert("concurrent_insert",
+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);
-sys_var_long_ptr sys_connect_timeout("connect_timeout",
+static sys_var_long_ptr sys_connect_timeout(&vars, "connect_timeout",
&connect_timeout);
-sys_var_const_os_str sys_datadir("datadir", mysql_real_data_home);
-sys_var_enum sys_delay_key_write("delay_key_write",
+static sys_var_const_os_str sys_datadir(&vars, "datadir", mysql_real_data_home);
+#ifndef DBUG_OFF
+static sys_var_thd_dbug sys_dbug(&vars, "debug");
+#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);
-sys_var_long_ptr sys_delayed_insert_limit("delayed_insert_limit",
+static sys_var_long_ptr sys_delayed_insert_limit(&vars, "delayed_insert_limit",
&delayed_insert_limit);
-sys_var_long_ptr sys_delayed_insert_timeout("delayed_insert_timeout",
+static sys_var_long_ptr sys_delayed_insert_timeout(&vars, "delayed_insert_timeout",
&delayed_insert_timeout);
-sys_var_long_ptr sys_delayed_queue_size("delayed_queue_size",
+static sys_var_long_ptr sys_delayed_queue_size(&vars, "delayed_queue_size",
&delayed_queue_size);
-sys_var_long_ptr sys_expire_logs_days("expire_logs_days",
+
+#ifdef HAVE_EVENT_SCHEDULER
+static sys_var_event_scheduler sys_event_scheduler(&vars, "event_scheduler");
+#endif
+
+static sys_var_long_ptr sys_expire_logs_days(&vars, "expire_logs_days",
&expire_logs_days);
-sys_var_bool_ptr sys_flush("flush", &myisam_flush);
-sys_var_long_ptr sys_flush_time("flush_time", &flush_time);
-sys_var_str sys_ft_boolean_syntax("ft_boolean_syntax",
- sys_check_ftb_syntax,
- sys_update_ftb_syntax,
- sys_default_ftb_syntax,
- ft_boolean_syntax);
-sys_var_str sys_init_connect("init_connect", 0,
+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);
-sys_var_str sys_init_slave("init_slave", 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);
-sys_var_thd_ulong sys_interactive_timeout("interactive_timeout",
+static sys_var_thd_ulong sys_interactive_timeout(&vars, "interactive_timeout",
&SV::net_interactive_timeout);
-sys_var_thd_ulong sys_join_buffer_size("join_buffer_size",
+static sys_var_thd_ulong sys_join_buffer_size(&vars, "join_buffer_size",
&SV::join_buff_size);
-sys_var_key_buffer_size sys_key_buffer_size("key_buffer_size");
-sys_var_key_cache_long sys_key_cache_block_size("key_cache_block_size",
+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));
-sys_var_key_cache_long sys_key_cache_division_limit("key_cache_division_limit",
+static sys_var_key_cache_long sys_key_cache_division_limit(&vars, "key_cache_division_limit",
offsetof(KEY_CACHE,
param_division_limit));
-sys_var_key_cache_long sys_key_cache_age_threshold("key_cache_age_threshold",
+static sys_var_key_cache_long sys_key_cache_age_threshold(&vars, "key_cache_age_threshold",
offsetof(KEY_CACHE,
param_age_threshold));
-sys_var_bool_ptr sys_local_infile("local_infile",
+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);
-sys_var_bool_const_ptr sys_log("log", &opt_log);
-sys_var_trust_routine_creators
-sys_trust_routine_creators("log_bin_trust_routine_creators",
+#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);
-sys_var_bool_ptr
-sys_trust_function_creators("log_bin_trust_function_creators",
+static sys_var_bool_ptr
+sys_trust_function_creators(&vars, "log_bin_trust_function_creators",
&trust_function_creators);
-sys_var_bool_ptr
- sys_log_queries_not_using_indexes("log_queries_not_using_indexes",
+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);
-sys_var_thd_ulong sys_log_warnings("log_warnings", &SV::log_warnings);
-sys_var_thd_ulong sys_long_query_time("long_query_time",
- &SV::long_query_time);
-sys_var_bool_const_ptr sys_log_slow("log_slow_queries", &opt_slow_log);
-sys_var_thd_bool sys_low_priority_updates("low_priority_updates",
+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_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 */
-sys_var_thd_bool sys_sql_low_priority_updates("sql_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
-sys_var_thd_ulong sys_max_allowed_packet("max_allowed_packet",
+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);
-sys_var_long_ptr sys_max_binlog_cache_size("max_binlog_cache_size",
+static sys_var_long_ptr sys_max_binlog_cache_size(&vars, "max_binlog_cache_size",
&max_binlog_cache_size);
-sys_var_long_ptr sys_max_binlog_size("max_binlog_size",
+static sys_var_long_ptr sys_max_binlog_size(&vars, "max_binlog_size",
&max_binlog_size,
fix_max_binlog_size);
-sys_var_long_ptr sys_max_connections("max_connections",
+static sys_var_long_ptr sys_max_connections(&vars, "max_connections",
&max_connections,
fix_max_connections);
-sys_var_long_ptr sys_max_connect_errors("max_connect_errors",
+static sys_var_long_ptr sys_max_connect_errors(&vars, "max_connect_errors",
&max_connect_errors);
-sys_var_thd_ulong sys_max_insert_delayed_threads("max_insert_delayed_threads",
+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);
-sys_var_thd_ulong sys_max_delayed_threads("max_delayed_threads",
+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);
-sys_var_thd_ulong sys_max_error_count("max_error_count",
+static sys_var_thd_ulong sys_max_error_count(&vars, "max_error_count",
&SV::max_error_count);
-sys_var_thd_ulonglong sys_max_heap_table_size("max_heap_table_size",
+static sys_var_thd_ulonglong sys_max_heap_table_size(&vars, "max_heap_table_size",
&SV::max_heap_table_size);
-sys_var_thd_ulong sys_pseudo_thread_id("pseudo_thread_id",
- &SV::pseudo_thread_id,
- check_pseudo_thread_id, 0);
-sys_var_thd_ha_rows sys_max_join_size("max_join_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);
-sys_var_thd_ulong sys_max_seeks_for_key("max_seeks_for_key",
+static sys_var_thd_ulong sys_max_seeks_for_key(&vars, "max_seeks_for_key",
&SV::max_seeks_for_key);
-sys_var_thd_ulong sys_max_length_for_sort_data("max_length_for_sort_data",
+static sys_var_thd_ulong sys_max_length_for_sort_data(&vars, "max_length_for_sort_data",
&SV::max_length_for_sort_data);
#ifndef TO_BE_DELETED /* Alias for max_join_size */
-sys_var_thd_ha_rows sys_sql_max_join_size("sql_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("max_prepared_stmt_count",
+sys_max_prepared_stmt_count(&vars, "max_prepared_stmt_count",
&max_prepared_stmt_count,
&LOCK_prepared_stmt_count);
-sys_var_long_ptr sys_max_relay_log_size("max_relay_log_size",
+static sys_var_long_ptr sys_max_relay_log_size(&vars, "max_relay_log_size",
&max_relay_log_size,
fix_max_relay_log_size);
-sys_var_thd_ulong sys_max_sort_length("max_sort_length",
+static sys_var_thd_ulong sys_max_sort_length(&vars, "max_sort_length",
&SV::max_sort_length);
-sys_var_thd_ulong sys_max_sp_recursion_depth("max_sp_recursion_depth",
+static sys_var_thd_ulong sys_max_sp_recursion_depth(&vars, "max_sp_recursion_depth",
&SV::max_sp_recursion_depth);
-sys_var_max_user_conn sys_max_user_connections("max_user_connections");
-sys_var_thd_ulong sys_max_tmp_tables("max_tmp_tables",
+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);
-sys_var_long_ptr sys_max_write_lock_count("max_write_lock_count",
+static sys_var_long_ptr sys_max_write_lock_count(&vars, "max_write_lock_count",
&max_write_lock_count);
-sys_var_thd_ulong sys_multi_range_count("multi_range_count",
+static sys_var_thd_ulong sys_min_examined_row_limit(&vars, "min_examined_row_limit",
+ &SV::min_examined_row_limit);
+static sys_var_thd_ulong sys_multi_range_count(&vars, "multi_range_count",
&SV::multi_range_count);
-sys_var_long_ptr sys_myisam_data_pointer_size("myisam_data_pointer_size",
+static sys_var_long_ptr sys_myisam_data_pointer_size(&vars, "myisam_data_pointer_size",
&myisam_data_pointer_size);
-sys_var_thd_ulonglong sys_myisam_max_sort_file_size("myisam_max_sort_file_size", &SV::myisam_max_sort_file_size, fix_myisam_max_sort_file_size, 1);
-sys_var_thd_ulong sys_myisam_repair_threads("myisam_repair_threads", &SV::myisam_repair_threads);
-sys_var_thd_ulong sys_myisam_sort_buffer_size("myisam_sort_buffer_size", &SV::myisam_sort_buff_size);
-
-sys_var_thd_enum sys_myisam_stats_method("myisam_stats_method",
+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);
-sys_var_thd_ulong sys_net_buffer_length("net_buffer_length",
+#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);
-sys_var_thd_ulong sys_net_read_timeout("net_read_timeout",
+static sys_var_thd_ulong sys_net_read_timeout(&vars, "net_read_timeout",
&SV::net_read_timeout,
0, fix_net_read_timeout);
-sys_var_thd_ulong sys_net_write_timeout("net_write_timeout",
+static sys_var_thd_ulong sys_net_write_timeout(&vars, "net_write_timeout",
&SV::net_write_timeout,
0, fix_net_write_timeout);
-sys_var_thd_ulong sys_net_retry_count("net_retry_count",
+static sys_var_thd_ulong sys_net_retry_count(&vars, "net_retry_count",
&SV::net_retry_count,
0, fix_net_retry_count);
-sys_var_thd_bool sys_new_mode("new", &SV::new_mode);
-sys_var_thd_bool sys_old_passwords("old_passwords", &SV::old_passwords);
-sys_var_thd_ulong sys_optimizer_prune_level("optimizer_prune_level",
+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);
-sys_var_thd_ulong sys_optimizer_search_depth("optimizer_search_depth",
+static sys_var_thd_ulong sys_optimizer_search_depth(&vars, "optimizer_search_depth",
&SV::optimizer_search_depth);
-sys_var_const_os_str sys_plugin_dir("plugin_dir", opt_plugin_dir);
-sys_var_thd_ulong sys_preload_buff_size("preload_buffer_size",
+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);
-sys_var_thd_ulong sys_read_buff_size("read_buffer_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);
-sys_var_bool_ptr sys_readonly("read_only", &opt_readonly);
-sys_var_thd_ulong sys_read_rnd_buff_size("read_rnd_buffer_size",
+static sys_var_opt_readonly sys_readonly(&vars, "read_only", &opt_readonly);
+static sys_var_thd_ulong sys_read_rnd_buff_size(&vars, "read_rnd_buffer_size",
&SV::read_rnd_buff_size);
-sys_var_thd_ulong sys_div_precincrement("div_precision_increment",
+static sys_var_thd_ulong sys_div_precincrement(&vars, "div_precision_increment",
&SV::div_precincrement);
-#ifdef HAVE_REPLICATION
-sys_var_bool_ptr sys_relay_log_purge("relay_log_purge",
- &relay_log_purge);
-#endif
-sys_var_long_ptr sys_rpl_recovery_rank("rpl_recovery_rank",
+static sys_var_long_ptr sys_rpl_recovery_rank(&vars, "rpl_recovery_rank",
&rpl_recovery_rank);
-sys_var_long_ptr sys_query_cache_size("query_cache_size",
+static sys_var_long_ptr sys_query_cache_size(&vars, "query_cache_size",
&query_cache_size,
fix_query_cache_size);
-sys_var_thd_ulong sys_range_alloc_block_size("range_alloc_block_size",
+static sys_var_thd_ulong sys_range_alloc_block_size(&vars, "range_alloc_block_size",
&SV::range_alloc_block_size);
-sys_var_thd_ulong sys_query_alloc_block_size("query_alloc_block_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);
-sys_var_thd_ulong sys_query_prealloc_size("query_prealloc_size",
+static sys_var_thd_ulong sys_query_prealloc_size(&vars, "query_prealloc_size",
&SV::query_prealloc_size,
0, fix_thd_mem_root);
-sys_var_readonly_os sys_tmpdir("tmpdir", OPT_GLOBAL, SHOW_CHAR, get_tmpdir);
-sys_var_thd_ulong sys_trans_alloc_block_size("transaction_alloc_block_size",
+#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);
+#ifdef HAVE_SYS_UN_H
+static sys_var_const sys_socket(&vars, "socket",
+ OPT_GLOBAL, SHOW_CHAR_PTR,
+ (uchar*) &mysqld_unix_port);
+#endif
+#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);
-sys_var_thd_ulong sys_trans_prealloc_size("transaction_prealloc_size",
+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",
+ &SV::thread_handling,
+ &thread_handling_typelib,
+ NULL);
#ifdef HAVE_QUERY_CACHE
-sys_var_long_ptr sys_query_cache_limit("query_cache_limit",
+static sys_var_long_ptr sys_query_cache_limit(&vars, "query_cache_limit",
&query_cache.query_cache_limit);
-sys_var_long_ptr sys_query_cache_min_res_unit("query_cache_min_res_unit",
+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);
-sys_var_thd_enum sys_query_cache_type("query_cache_type",
+static sys_var_thd_enum sys_query_cache_type(&vars, "query_cache_type",
&SV::query_cache_type,
&query_cache_type_typelib);
-sys_var_thd_bool
-sys_query_cache_wlock_invalidate("query_cache_wlock_invalidate",
+static sys_var_thd_bool
+sys_query_cache_wlock_invalidate(&vars, "query_cache_wlock_invalidate",
&SV::query_cache_wlock_invalidate);
#endif /* HAVE_QUERY_CACHE */
-sys_var_bool_ptr sys_secure_auth("secure_auth", &opt_secure_auth);
-sys_var_const_str_ptr sys_secure_file_priv("secure_file_priv",
+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);
-sys_var_long_ptr sys_server_id("server_id", &server_id, fix_server_id);
-#ifdef HAVE_REPLICATION
-sys_var_bool_ptr sys_slave_compressed_protocol("slave_compressed_protocol",
+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);
-sys_var_const_os_str_ptr sys_slave_load_tmpdir("slave_load_tmpdir",
- &slave_load_tmpdir);
-sys_var_long_ptr sys_slave_net_timeout("slave_net_timeout",
- &slave_net_timeout);
-sys_var_long_ptr sys_slave_trans_retries("slave_transaction_retries",
- &slave_trans_retries);
-#endif
-sys_var_long_ptr sys_slow_launch_time("slow_launch_time",
+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);
-sys_var_thd_ulong sys_sort_buffer("sort_buffer_size",
+static sys_var_thd_ulong sys_sort_buffer(&vars, "sort_buffer_size",
&SV::sortbuff_size);
-sys_var_thd_sql_mode sys_sql_mode("sql_mode",
- &SV::sql_mode);
+/*
+ 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;
-sys_var_const_os_str_ptr sys_ssl_ca("ssl_ca", &opt_ssl_ca);
-sys_var_const_os_str_ptr sys_ssl_capath("ssl_capath", &opt_ssl_capath);
-sys_var_const_os_str_ptr sys_ssl_cert("ssl_cert", &opt_ssl_cert);
-sys_var_const_os_str_ptr sys_ssl_cipher("ssl_cipher", &opt_ssl_cipher);
-sys_var_const_os_str_ptr sys_ssl_key("ssl_key", &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
-sys_var_const_os_str sys_ssl_ca("ssl_ca", NULL);
-sys_var_const_os_str sys_ssl_capath("ssl_capath", NULL);
-sys_var_const_os_str sys_ssl_cert("ssl_cert", NULL);
-sys_var_const_os_str sys_ssl_cipher("ssl_cipher", NULL);
-sys_var_const_os_str sys_ssl_key("ssl_key", NULL);
+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
-sys_var_thd_enum
-sys_updatable_views_with_limit("updatable_views_with_limit",
+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);
-sys_var_thd_table_type sys_table_type("table_type",
- &SV::table_type);
-sys_var_thd_storage_engine sys_storage_engine("storage_engine",
- &SV::table_type);
-#ifdef HAVE_REPLICATION
-sys_var_sync_binlog_period sys_sync_binlog_period("sync_binlog", &sync_binlog_period);
-#endif
-sys_var_bool_ptr sys_sync_frm("sync_frm", &opt_sync_frm);
-sys_var_const_str sys_system_time_zone("system_time_zone",
+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);
-sys_var_long_ptr sys_table_cache_size("table_cache",
+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);
-sys_var_long_ptr sys_table_lock_wait_timeout("table_lock_wait_timeout",
+static sys_var_long_ptr sys_table_lock_wait_timeout(&vars, "table_lock_wait_timeout",
&table_lock_wait_timeout);
-sys_var_long_ptr sys_thread_cache_size("thread_cache_size",
+static sys_var_long_ptr sys_thread_cache_size(&vars, "thread_cache_size",
&thread_cache_size);
-sys_var_thd_enum sys_tx_isolation("tx_isolation",
+#if HAVE_POOL_OF_THREADS == 1
+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);
-sys_var_thd_ulonglong sys_tmp_table_size("tmp_table_size",
+ fix_tx_isolation,
+ check_tx_isolation);
+static sys_var_thd_ulonglong sys_tmp_table_size(&vars, "tmp_table_size",
&SV::tmp_table_size);
-sys_var_bool_ptr sys_timed_mutexes("timed_mutexes",
+static sys_var_bool_ptr sys_timed_mutexes(&vars, "timed_mutexes",
&timed_mutexes);
-sys_var_const_str sys_version("version", server_version);
-#ifdef HAVE_BERKELEY_DB
-sys_var_const_str sys_version_bdb("version_bdb", DB_VERSION_STRING);
-#endif
-sys_var_const_str sys_version_comment("version_comment",
+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);
-sys_var_const_str sys_version_compile_machine("version_compile_machine",
+static sys_var_const_str sys_version_compile_machine(&vars, "version_compile_machine",
MACHINE_TYPE);
-sys_var_const_str sys_version_compile_os("version_compile_os",
+static sys_var_const_str sys_version_compile_os(&vars, "version_compile_os",
SYSTEM_TYPE);
-sys_var_thd_ulong sys_net_wait_timeout("wait_timeout",
+static sys_var_thd_ulong sys_net_wait_timeout(&vars, "wait_timeout",
&SV::net_wait_timeout);
-#ifdef HAVE_INNOBASE_DB
-sys_var_long_ptr sys_innodb_fast_shutdown("innodb_fast_shutdown",
- &innobase_fast_shutdown);
-sys_var_long_ptr sys_innodb_max_dirty_pages_pct("innodb_max_dirty_pages_pct",
- &srv_max_buf_pool_modified_pct);
-sys_var_long_ptr sys_innodb_max_purge_lag("innodb_max_purge_lag",
- &srv_max_purge_lag);
-sys_var_thd_bool sys_innodb_table_locks("innodb_table_locks",
- &SV::innodb_table_locks);
-sys_var_thd_bool sys_innodb_support_xa("innodb_support_xa",
- &SV::innodb_support_xa);
-sys_var_long_ptr sys_innodb_autoextend_increment("innodb_autoextend_increment",
- &srv_auto_extend_increment);
-sys_var_long_ptr sys_innodb_sync_spin_loops("innodb_sync_spin_loops",
- &srv_n_spin_wait_rounds);
-sys_var_long_ptr sys_innodb_concurrency_tickets("innodb_concurrency_tickets",
- &srv_n_free_tickets_to_enter);
-sys_var_long_ptr sys_innodb_thread_sleep_delay("innodb_thread_sleep_delay",
- &srv_thread_sleep_delay);
-sys_var_long_ptr sys_innodb_thread_concurrency("innodb_thread_concurrency",
- &srv_thread_concurrency);
-sys_var_long_ptr sys_innodb_commit_concurrency("innodb_commit_concurrency",
- &srv_commit_concurrency);
-sys_var_long_ptr sys_innodb_flush_log_at_trx_commit(
- "innodb_flush_log_at_trx_commit",
- &srv_flush_log_at_trx_commit);
-sys_var_const_os_str_ptr sys_innodb_data_file_path("innodb_data_file_path",
- &innobase_data_file_path);
-sys_var_const_os_str_ptr sys_innodb_data_home_dir("innodb_data_home_dir",
- &innobase_data_home_dir);
-sys_var_const_os_str_ptr sys_innodb_log_arch_dir("innodb_log_arch_dir",
- &innobase_log_arch_dir);
-sys_var_const_os_str_ptr sys_innodb_log_group_home_dir("innodb_log_group_home_dir",
- &innobase_log_group_home_dir);
-#endif
-
/* Condition pushdown to storage engine */
-sys_var_thd_bool
-sys_engine_condition_pushdown("engine_condition_pushdown",
+static sys_var_thd_bool
+sys_engine_condition_pushdown(&vars, "engine_condition_pushdown",
&SV::engine_condition_pushdown);
-#ifdef HAVE_NDBCLUSTER_DB
+#ifdef WITH_NDBCLUSTER_STORAGE_ENGINE
/* ndb thread specific variable settings */
-sys_var_thd_ulong
-sys_ndb_autoincrement_prefetch_sz("ndb_autoincrement_prefetch_sz",
+static sys_var_thd_ulong
+sys_ndb_autoincrement_prefetch_sz(&vars, "ndb_autoincrement_prefetch_sz",
&SV::ndb_autoincrement_prefetch_sz);
-sys_var_thd_bool
-sys_ndb_force_send("ndb_force_send", &SV::ndb_force_send);
-sys_var_thd_bool
-sys_ndb_use_exact_count("ndb_use_exact_count", &SV::ndb_use_exact_count);
-sys_var_thd_bool
-sys_ndb_use_transactions("ndb_use_transactions", &SV::ndb_use_transactions);
-sys_var_long_ptr
-sys_ndb_cache_check_time("ndb_cache_check_time", &ndb_cache_check_time);
-sys_var_const_str
-sys_ndb_connectstring("ndb_connectstring", opt_ndb_constrbuf);
+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 */
-sys_var_thd_date_time_format sys_time_format("time_format",
+static sys_var_thd_date_time_format sys_time_format(&vars, "time_format",
&SV::time_format,
MYSQL_TIMESTAMP_TIME);
-sys_var_thd_date_time_format sys_date_format("date_format",
+static sys_var_thd_date_time_format sys_date_format(&vars, "date_format",
&SV::date_format,
MYSQL_TIMESTAMP_DATE);
-sys_var_thd_date_time_format sys_datetime_format("datetime_format",
+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("autocommit", 0,
+sys_var_thd_bit sys_autocommit(&vars, "autocommit", 0,
set_option_autocommit,
OPTION_NOT_AUTOCOMMIT,
1);
-static sys_var_thd_bit sys_big_tables("big_tables", 0,
+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("sql_big_tables", 0,
+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("sql_big_selects", 0,
+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("sql_log_off",
+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("sql_log_update",
+static sys_var_thd_bit sys_log_update(&vars, "sql_log_update",
check_log_update,
set_log_update,
- OPTION_UPDATE_LOG);
-static sys_var_thd_bit sys_log_binlog("sql_log_bin",
+ OPTION_BIN_LOG);
+static sys_var_thd_bit sys_log_binlog(&vars, "sql_log_bin",
check_log_update,
- set_log_bin,
+ set_option_bit,
OPTION_BIN_LOG);
-static sys_var_thd_bit sys_sql_warnings("sql_warnings", 0,
+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("sql_notes", 0,
+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("sql_auto_is_null", 0,
+static sys_var_thd_bit sys_auto_is_null(&vars, "sql_auto_is_null", 0,
set_option_bit,
- OPTION_AUTO_IS_NULL);
-static sys_var_thd_bit sys_safe_updates("sql_safe_updates", 0,
+ 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("sql_buffer_result", 0,
+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("sql_quote_show_create", 0,
+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("foreign_key_checks", 0,
+static sys_var_thd_bit sys_foreign_key_checks(&vars, "foreign_key_checks", 0,
set_option_bit,
OPTION_NO_FOREIGN_KEY_CHECKS,
- 1);
-static sys_var_thd_bit sys_unique_checks("unique_checks", 0,
+ 1, sys_var::SESSION_VARIABLE_IN_BINLOG);
+static sys_var_thd_bit sys_unique_checks(&vars, "unique_checks", 0,
set_option_bit,
OPTION_RELAXED_UNIQUE_CHECKS,
- 1);
+ 1,
+ sys_var::SESSION_VARIABLE_IN_BINLOG);
+#if defined(ENABLED_PROFILING) && defined(COMMUNITY_SERVER)
+static sys_var_thd_bit sys_profiling(&vars, "profiling", NULL,
+ set_option_bit,
+ 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("sql_select_limit",
+static sys_var_thd_ha_rows sys_select_limit(&vars, "sql_select_limit",
&SV::select_limit);
-static sys_var_timestamp sys_timestamp("timestamp");
-static sys_var_last_insert_id sys_last_insert_id("last_insert_id");
-static sys_var_last_insert_id sys_identity("identity");
+static sys_var_timestamp sys_timestamp(&vars, "timestamp",
+ sys_var::SESSION_VARIABLE_IN_BINLOG);
+static sys_var_last_insert_id
+sys_last_insert_id(&vars, "last_insert_id",
+ sys_var::SESSION_VARIABLE_IN_BINLOG);
+/*
+ identity is an alias for last_insert_id(), so that we are compatible
+ with Sybase
+*/
+static sys_var_last_insert_id
+sys_identity(&vars, "identity", sys_var::SESSION_VARIABLE_IN_BINLOG);
-static sys_var_thd_lc_time_names sys_lc_time_names("lc_time_names");
+static sys_var_thd_lc_time_names
+sys_lc_time_names(&vars, "lc_time_names", sys_var::SESSION_VARIABLE_IN_BINLOG);
-static sys_var_insert_id sys_insert_id("insert_id");
-static sys_var_readonly sys_error_count("error_count",
+/*
+ insert_id should *not* be marked as written to the binlog (i.e., it
+ should *not* have binlog_status==SESSION_VARIABLE_IN_BINLOG),
+ because we want any statement that refers to insert_id explicitly to
+ be unsafe. (By "explicitly", we mean using @@session.insert_id,
+ whereas insert_id is used "implicitly" when NULL value is inserted
+ into an auto_increment column).
+
+ We want statements referring explicitly to @@session.insert_id to be
+ unsafe, because insert_id is modified internally by the slave sql
+ thread when NULL values are inserted in an AUTO_INCREMENT column.
+ This modification interfers with the value of the
+ @@session.insert_id variable if @@session.insert_id is referred
+ explicitly by an insert statement (as is seen by executing "SET
+ @@session.insert_id=0; CREATE TABLE t (a INT, b INT KEY
+ AUTO_INCREMENT); INSERT INTO t(a) VALUES (@@session.insert_id);" in
+ statement-based logging mode: t will be different on master and
+ slave).
+*/
+static sys_var_insert_id sys_insert_id(&vars, "insert_id");
+static sys_var_readonly sys_error_count(&vars, "error_count",
OPT_SESSION,
SHOW_LONG,
get_error_count);
-static sys_var_readonly sys_warning_count("warning_count",
+static sys_var_readonly sys_warning_count(&vars, "warning_count",
OPT_SESSION,
SHOW_LONG,
get_warning_count);
-/* alias for last_insert_id() to be compatible with Sybase */
-#ifdef HAVE_REPLICATION
-static sys_var_slave_skip_counter sys_slave_skip_counter("sql_slave_skip_counter");
-#endif
-static sys_var_rand_seed1 sys_rand_seed1("rand_seed1");
-static sys_var_rand_seed2 sys_rand_seed2("rand_seed2");
+static sys_var_rand_seed1 sys_rand_seed1(&vars, "rand_seed1",
+ sys_var::SESSION_VARIABLE_IN_BINLOG);
+static sys_var_rand_seed2 sys_rand_seed2(&vars, "rand_seed2",
+ sys_var::SESSION_VARIABLE_IN_BINLOG);
-static sys_var_thd_ulong sys_default_week_format("default_week_format",
+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("group_concat_max_len",
+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("time_zone");
-
-/* Read only variables */
-
-sys_var_readonly sys_have_innodb("have_innodb", OPT_GLOBAL,
- SHOW_CHAR, get_have_innodb);
-/* Global read-only variable describing server license */
-sys_var_const_str sys_license("license", STRINGIFY_ARG(LICENSE));
+sys_var_thd_time_zone sys_time_zone(&vars, "time_zone",
+ sys_var::SESSION_VARIABLE_IN_BINLOG);
/* Global read-only variable containing hostname */
-sys_var_const_str sys_hostname("hostname", glob_hostname);
-
-sys_var_thd_bool sys_keep_files_on_create("keep_files_on_create",
- &SV::keep_files_on_create);
-
+static sys_var_const_str sys_hostname(&vars, "hostname", glob_hostname);
+#ifndef EMBEDDED_LIBRARY
+static sys_var_const_str_ptr sys_repl_report_host(&vars, "report_host", &report_host);
+static sys_var_const_str_ptr sys_repl_report_user(&vars, "report_user", &report_user);
+static sys_var_const_str_ptr sys_repl_report_password(&vars, "report_password", &report_password);
+static uchar *slave_get_report_port(THD *thd)
+{
+ thd->sys_var_tmp.long_value= report_port;
+ return (uchar*) &thd->sys_var_tmp.long_value;
+}
-/*
- List of all variables for initialisation and storage in hash
- This is sorted in alphabetical order to make it easy to add new variables
+static sys_var_readonly sys_repl_report_port(&vars, "report_port", OPT_GLOBAL, SHOW_LONG, slave_get_report_port);
- If the variable is not in this list, it can't be changed with
- SET variable_name=
-*/
-
-sys_var *sys_variables[]=
-{
- &sys_auto_is_null,
- &sys_auto_increment_increment,
- &sys_auto_increment_offset,
- &sys_autocommit,
- &sys_automatic_sp_privileges,
- &sys_basedir,
- &sys_big_tables,
- &sys_big_selects,
- &sys_binlog_cache_size,
- &sys_buffer_results,
- &sys_bulk_insert_buff_size,
- &sys_character_set_server,
- &sys_character_set_database,
- &sys_character_set_client,
- &sys_character_set_connection,
- &sys_character_set_results,
- &sys_character_set_filesystem,
- &sys_charset_system,
- &sys_collation_connection,
- &sys_collation_database,
- &sys_collation_server,
- &sys_completion_type,
- &sys_concurrent_insert,
- &sys_connect_timeout,
- &sys_datadir,
- &sys_date_format,
- &sys_datetime_format,
- &sys_div_precincrement,
- &sys_default_week_format,
- &sys_delay_key_write,
- &sys_delayed_insert_limit,
- &sys_delayed_insert_timeout,
- &sys_delayed_queue_size,
- &sys_keep_files_on_create,
- &sys_error_count,
- &sys_expire_logs_days,
- &sys_flush,
- &sys_flush_time,
- &sys_ft_boolean_syntax,
- &sys_foreign_key_checks,
- &sys_group_concat_max_len,
- &sys_have_innodb,
- &sys_hostname,
- &sys_identity,
- &sys_init_connect,
- &sys_init_slave,
- &sys_insert_id,
- &sys_interactive_timeout,
- &sys_join_buffer_size,
- &sys_key_buffer_size,
- &sys_key_cache_block_size,
- &sys_key_cache_division_limit,
- &sys_key_cache_age_threshold,
- &sys_last_insert_id,
- &sys_lc_time_names,
- &sys_license,
- &sys_local_infile,
- &sys_log,
- &sys_log_binlog,
- &sys_log_off,
- &sys_log_queries_not_using_indexes,
- &sys_log_slow,
- &sys_log_update,
- &sys_log_warnings,
- &sys_long_query_time,
- &sys_low_priority_updates,
- &sys_max_allowed_packet,
- &sys_max_binlog_cache_size,
- &sys_max_binlog_size,
- &sys_max_connect_errors,
- &sys_max_connections,
- &sys_max_delayed_threads,
- &sys_max_error_count,
- &sys_max_insert_delayed_threads,
- &sys_max_heap_table_size,
- &sys_max_join_size,
- &sys_max_length_for_sort_data,
- &sys_max_prepared_stmt_count,
- &sys_max_relay_log_size,
- &sys_max_seeks_for_key,
- &sys_max_sort_length,
- &sys_max_sp_recursion_depth,
- &sys_max_tmp_tables,
- &sys_max_user_connections,
- &sys_max_write_lock_count,
- &sys_multi_range_count,
- &sys_myisam_data_pointer_size,
- &sys_myisam_max_sort_file_size,
- &sys_myisam_repair_threads,
- &sys_myisam_sort_buffer_size,
- &sys_myisam_stats_method,
- &sys_net_buffer_length,
- &sys_net_read_timeout,
- &sys_net_retry_count,
- &sys_net_wait_timeout,
- &sys_net_write_timeout,
- &sys_new_mode,
- &sys_old_passwords,
- &sys_optimizer_prune_level,
- &sys_optimizer_search_depth,
- &sys_preload_buff_size,
- &sys_pseudo_thread_id,
- &sys_query_alloc_block_size,
- &sys_query_cache_size,
- &sys_query_prealloc_size,
-#ifdef HAVE_QUERY_CACHE
- &sys_query_cache_limit,
- &sys_query_cache_min_res_unit,
- &sys_query_cache_type,
- &sys_query_cache_wlock_invalidate,
-#endif /* HAVE_QUERY_CACHE */
- &sys_quote_show_create,
- &sys_rand_seed1,
- &sys_rand_seed2,
- &sys_range_alloc_block_size,
- &sys_readonly,
- &sys_read_buff_size,
- &sys_read_rnd_buff_size,
-#ifdef HAVE_REPLICATION
- &sys_relay_log_purge,
-#endif
- &sys_rpl_recovery_rank,
- &sys_safe_updates,
- &sys_secure_auth,
- &sys_secure_file_priv,
- &sys_select_limit,
- &sys_server_id,
-#ifdef HAVE_REPLICATION
- &sys_slave_compressed_protocol,
- &sys_slave_net_timeout,
- &sys_slave_trans_retries,
- &sys_slave_skip_counter,
-#endif
- &sys_slow_launch_time,
- &sys_sort_buffer,
- &sys_sql_big_tables,
- &sys_sql_low_priority_updates,
- &sys_sql_max_join_size,
- &sys_sql_mode,
- &sys_sql_warnings,
- &sys_sql_notes,
- &sys_ssl_ca,
- &sys_ssl_capath,
- &sys_ssl_cert,
- &sys_ssl_cipher,
- &sys_ssl_key,
- &sys_storage_engine,
-#ifdef HAVE_REPLICATION
- &sys_sync_binlog_period,
-#endif
- &sys_sync_frm,
- &sys_system_time_zone,
- &sys_table_cache_size,
- &sys_table_lock_wait_timeout,
- &sys_table_type,
- &sys_thread_cache_size,
- &sys_time_format,
- &sys_timed_mutexes,
- &sys_timestamp,
- &sys_time_zone,
- &sys_tmpdir,
- &sys_tmp_table_size,
- &sys_trans_alloc_block_size,
- &sys_trans_prealloc_size,
- &sys_tx_isolation,
- &sys_version,
-#ifdef HAVE_BERKELEY_DB
- &sys_version_bdb,
-#endif
- &sys_version_comment,
- &sys_version_compile_machine,
- &sys_version_compile_os,
-#ifdef HAVE_INNOBASE_DB
- &sys_innodb_fast_shutdown,
- &sys_innodb_max_dirty_pages_pct,
- &sys_innodb_max_purge_lag,
- &sys_innodb_table_locks,
- &sys_innodb_support_xa,
- &sys_innodb_autoextend_increment,
- &sys_innodb_sync_spin_loops,
- &sys_innodb_concurrency_tickets,
- &sys_innodb_thread_sleep_delay,
- &sys_innodb_thread_concurrency,
- &sys_innodb_commit_concurrency,
- &sys_innodb_flush_log_at_trx_commit,
-#endif
- &sys_trust_routine_creators,
- &sys_trust_function_creators,
- &sys_engine_condition_pushdown,
-#ifdef HAVE_NDBCLUSTER_DB
- &sys_ndb_autoincrement_prefetch_sz,
- &sys_ndb_cache_check_time,
- &sys_ndb_connectstring,
- &sys_ndb_force_send,
- &sys_ndb_use_exact_count,
- &sys_ndb_use_transactions,
#endif
- &sys_unique_checks,
- &sys_updatable_views_with_limit,
- &sys_warning_count
-};
-
-
-/*
- Variables shown by SHOW VARIABLES in alphabetical order
-*/
-struct show_var_st init_vars[]= {
- {"auto_increment_increment", (char*) &sys_auto_increment_increment, SHOW_SYS},
- {"auto_increment_offset", (char*) &sys_auto_increment_offset, SHOW_SYS},
- {sys_automatic_sp_privileges.name,(char*) &sys_automatic_sp_privileges, SHOW_SYS},
- {"back_log", (char*) &back_log, SHOW_LONG},
- {sys_basedir.name, (char*) &sys_basedir, SHOW_SYS},
-#ifdef HAVE_BERKELEY_DB
- {"bdb_cache_size", (char*) &berkeley_cache_size, SHOW_LONG},
- {"bdb_home", (char*) &berkeley_home, SHOW_CHAR_PTR},
- {"bdb_log_buffer_size", (char*) &berkeley_log_buffer_size, SHOW_LONG},
- {"bdb_logdir", (char*) &berkeley_logdir, SHOW_CHAR_PTR},
- {"bdb_max_lock", (char*) &berkeley_max_lock, SHOW_LONG},
- {"bdb_shared_data", (char*) &berkeley_shared_data, SHOW_BOOL},
- {"bdb_tmpdir", (char*) &berkeley_tmpdir, SHOW_CHAR_PTR},
-#endif
- {sys_binlog_cache_size.name,(char*) &sys_binlog_cache_size, SHOW_SYS},
- {sys_bulk_insert_buff_size.name,(char*) &sys_bulk_insert_buff_size,SHOW_SYS},
- {sys_character_set_client.name,(char*) &sys_character_set_client, SHOW_SYS},
- {sys_character_set_connection.name,(char*) &sys_character_set_connection,SHOW_SYS},
- {sys_character_set_database.name, (char*) &sys_character_set_database,SHOW_SYS},
- {sys_character_set_filesystem.name,(char*) &sys_character_set_filesystem, SHOW_SYS},
- {sys_character_set_results.name,(char*) &sys_character_set_results, SHOW_SYS},
- {sys_character_set_server.name, (char*) &sys_character_set_server,SHOW_SYS},
- {sys_charset_system.name, (char*) &sys_charset_system, SHOW_SYS},
- {sys_character_sets_dir.name, (char *) &sys_character_sets_dir, SHOW_SYS},
- {sys_collation_connection.name,(char*) &sys_collation_connection, SHOW_SYS},
- {sys_collation_database.name,(char*) &sys_collation_database, SHOW_SYS},
- {sys_collation_server.name,(char*) &sys_collation_server, SHOW_SYS},
- {sys_completion_type.name, (char*) &sys_completion_type, SHOW_SYS},
- {sys_concurrent_insert.name,(char*) &sys_concurrent_insert, SHOW_SYS},
- {sys_connect_timeout.name, (char*) &sys_connect_timeout, SHOW_SYS},
- {sys_datadir.name, (char*) &sys_datadir, SHOW_SYS},
- {sys_date_format.name, (char*) &sys_date_format, SHOW_SYS},
- {sys_datetime_format.name, (char*) &sys_datetime_format, SHOW_SYS},
- {sys_default_week_format.name, (char*) &sys_default_week_format, SHOW_SYS},
- {sys_delay_key_write.name, (char*) &sys_delay_key_write, SHOW_SYS},
- {sys_delayed_insert_limit.name, (char*) &sys_delayed_insert_limit,SHOW_SYS},
- {sys_delayed_insert_timeout.name, (char*) &sys_delayed_insert_timeout, SHOW_SYS},
- {sys_delayed_queue_size.name,(char*) &sys_delayed_queue_size, SHOW_SYS},
- {sys_div_precincrement.name,(char*) &sys_div_precincrement,SHOW_SYS},
- {sys_keep_files_on_create.name,(char*) &sys_keep_files_on_create, SHOW_SYS},
- {sys_engine_condition_pushdown.name,
- (char*) &sys_engine_condition_pushdown, SHOW_SYS},
- {sys_expire_logs_days.name, (char*) &sys_expire_logs_days, SHOW_SYS},
- {sys_flush.name, (char*) &sys_flush, SHOW_SYS},
- {sys_flush_time.name, (char*) &sys_flush_time, SHOW_SYS},
- {sys_ft_boolean_syntax.name,(char*) &ft_boolean_syntax, SHOW_CHAR},
- {"ft_max_word_len", (char*) &ft_max_word_len, SHOW_LONG},
- {"ft_min_word_len", (char*) &ft_min_word_len, SHOW_LONG},
- {"ft_query_expansion_limit",(char*) &ft_query_expansion_limit, SHOW_LONG},
- {"ft_stopword_file", (char*) &ft_stopword_file, SHOW_CHAR_PTR},
- {sys_group_concat_max_len.name, (char*) &sys_group_concat_max_len, SHOW_SYS},
- {"have_archive", (char*) &have_archive_db, SHOW_HAVE},
- {"have_bdb", (char*) &have_berkeley_db, SHOW_HAVE},
- {"have_blackhole_engine", (char*) &have_blackhole_db, SHOW_HAVE},
- {"have_compress", (char*) &have_compress, SHOW_HAVE},
- {"have_crypt", (char*) &have_crypt, SHOW_HAVE},
- {"have_csv", (char*) &have_csv_db, SHOW_HAVE},
- {"have_dynamic_loading", (char*) &have_dlopen, SHOW_HAVE},
- {"have_example_engine", (char*) &have_example_db, SHOW_HAVE},
- {"have_federated_engine", (char*) &have_federated_db, SHOW_HAVE},
- {"have_geometry", (char*) &have_geometry, SHOW_HAVE},
- {"have_innodb", (char*) &have_innodb, SHOW_HAVE},
- {"have_isam", (char*) &have_isam, SHOW_HAVE},
- {"have_merge_engine", (char*) &have_merge_db, SHOW_HAVE},
- {"have_ndbcluster", (char*) &have_ndbcluster, SHOW_HAVE},
- /* have_openssl is just and alias for have_ssl */
- {"have_openssl", (char*) &have_ssl, SHOW_HAVE},
- {"have_ssl", (char*) &have_ssl, SHOW_HAVE},
- {"have_query_cache", (char*) &have_query_cache, SHOW_HAVE},
- {"have_raid", (char*) &have_raid, SHOW_HAVE},
- {"have_rtree_keys", (char*) &have_rtree_keys, SHOW_HAVE},
- {"have_symlink", (char*) &have_symlink, SHOW_HAVE},
- {sys_hostname.name, (char*) &sys_hostname, SHOW_SYS},
- {"init_connect", (char*) &sys_init_connect, SHOW_SYS},
- {"init_file", (char*) &opt_init_file, SHOW_CHAR_PTR},
- {"init_slave", (char*) &sys_init_slave, SHOW_SYS},
-#ifdef HAVE_INNOBASE_DB
- {"innodb_additional_mem_pool_size", (char*) &innobase_additional_mem_pool_size, SHOW_LONG },
- {sys_innodb_autoextend_increment.name, (char*) &sys_innodb_autoextend_increment, SHOW_SYS},
- {"innodb_buffer_pool_awe_mem_mb", (char*) &innobase_buffer_pool_awe_mem_mb, SHOW_LONG },
- {"innodb_buffer_pool_size", (char*) &innobase_buffer_pool_size, SHOW_LONGLONG },
- {"innodb_checksums", (char*) &innobase_use_checksums, SHOW_MY_BOOL},
- {sys_innodb_commit_concurrency.name, (char*) &sys_innodb_commit_concurrency, SHOW_SYS},
- {sys_innodb_concurrency_tickets.name, (char*) &sys_innodb_concurrency_tickets, SHOW_SYS},
- {sys_innodb_data_file_path.name, (char*) &sys_innodb_data_file_path, SHOW_SYS},
- {sys_innodb_data_home_dir.name, (char*) &sys_innodb_data_home_dir, SHOW_SYS},
- {"innodb_adaptive_hash_index", (char*) &innobase_adaptive_hash_index, SHOW_MY_BOOL},
- {"innodb_doublewrite", (char*) &innobase_use_doublewrite, SHOW_MY_BOOL},
- {sys_innodb_fast_shutdown.name,(char*) &sys_innodb_fast_shutdown, SHOW_SYS},
- {"innodb_file_io_threads", (char*) &innobase_file_io_threads, SHOW_LONG },
- {"innodb_file_per_table", (char*) &innobase_file_per_table, SHOW_MY_BOOL},
- {sys_innodb_flush_log_at_trx_commit.name, (char*) &sys_innodb_flush_log_at_trx_commit, SHOW_SYS},
- {"innodb_flush_method", (char*) &innobase_unix_file_flush_method, SHOW_CHAR_PTR},
- {"innodb_force_recovery", (char*) &innobase_force_recovery, SHOW_LONG },
- {"innodb_lock_wait_timeout", (char*) &innobase_lock_wait_timeout, SHOW_LONG },
- {"innodb_locks_unsafe_for_binlog", (char*) &innobase_locks_unsafe_for_binlog, SHOW_MY_BOOL},
- {sys_innodb_log_arch_dir.name, (char*) &sys_innodb_log_arch_dir, SHOW_SYS},
- {"innodb_log_archive", (char*) &innobase_log_archive, SHOW_MY_BOOL},
- {"innodb_log_buffer_size", (char*) &innobase_log_buffer_size, SHOW_LONG },
- {"innodb_log_file_size", (char*) &innobase_log_file_size, SHOW_LONGLONG},
- {"innodb_log_files_in_group", (char*) &innobase_log_files_in_group, SHOW_LONG},
- {sys_innodb_log_group_home_dir.name, (char*) &sys_innodb_log_group_home_dir, SHOW_SYS},
- {sys_innodb_max_dirty_pages_pct.name, (char*) &sys_innodb_max_dirty_pages_pct, SHOW_SYS},
- {sys_innodb_max_purge_lag.name, (char*) &sys_innodb_max_purge_lag, SHOW_SYS},
- {"innodb_mirrored_log_groups", (char*) &innobase_mirrored_log_groups, SHOW_LONG},
- {"innodb_open_files", (char*) &innobase_open_files, SHOW_LONG },
- {"innodb_rollback_on_timeout", (char*) &innobase_rollback_on_timeout, SHOW_MY_BOOL},
- {sys_innodb_support_xa.name, (char*) &sys_innodb_support_xa, SHOW_SYS},
- {sys_innodb_sync_spin_loops.name, (char*) &sys_innodb_sync_spin_loops, SHOW_SYS},
- {sys_innodb_table_locks.name, (char*) &sys_innodb_table_locks, SHOW_SYS},
- {sys_innodb_thread_concurrency.name, (char*) &sys_innodb_thread_concurrency, SHOW_SYS},
- {sys_innodb_thread_sleep_delay.name, (char*) &sys_innodb_thread_sleep_delay, SHOW_SYS},
-#endif
- {sys_interactive_timeout.name,(char*) &sys_interactive_timeout, SHOW_SYS},
- {sys_join_buffer_size.name, (char*) &sys_join_buffer_size, SHOW_SYS},
- {sys_key_buffer_size.name, (char*) &sys_key_buffer_size, SHOW_SYS},
- {sys_key_cache_age_threshold.name, (char*) &sys_key_cache_age_threshold,
- SHOW_SYS},
- {sys_key_cache_block_size.name, (char*) &sys_key_cache_block_size,
- SHOW_SYS},
- {sys_key_cache_division_limit.name, (char*) &sys_key_cache_division_limit,
- SHOW_SYS},
- {"language", language, SHOW_CHAR},
- {"large_files_support", (char*) &opt_large_files, SHOW_BOOL},
- {"large_page_size", (char*) &opt_large_page_size, SHOW_INT},
- {"large_pages", (char*) &opt_large_pages, SHOW_MY_BOOL},
- {sys_lc_time_names.name, (char*) &sys_lc_time_names, SHOW_SYS},
- {sys_license.name, (char*) &sys_license, SHOW_SYS},
- {sys_local_infile.name, (char*) &sys_local_infile, SHOW_SYS},
-#ifdef HAVE_MLOCKALL
- {"locked_in_memory", (char*) &locked_in_memory, SHOW_BOOL},
-#endif
- {sys_log.name, (char*) &sys_log, SHOW_SYS},
- {"log_bin", (char*) &opt_bin_log, SHOW_BOOL},
- {sys_trust_function_creators.name,(char*) &sys_trust_function_creators, SHOW_SYS},
- {"log_error", (char*) log_error_file, SHOW_CHAR},
- {sys_log_queries_not_using_indexes.name,
- (char*) &sys_log_queries_not_using_indexes, SHOW_SYS},
-#ifdef HAVE_REPLICATION
- {"log_slave_updates", (char*) &opt_log_slave_updates, SHOW_MY_BOOL},
-#endif
- {sys_log_slow.name, (char*) &sys_log_slow, SHOW_SYS},
- {sys_log_warnings.name, (char*) &sys_log_warnings, SHOW_SYS},
- {sys_long_query_time.name, (char*) &sys_long_query_time, SHOW_SYS},
- {sys_low_priority_updates.name, (char*) &sys_low_priority_updates, SHOW_SYS},
- {"lower_case_file_system", (char*) &lower_case_file_system, SHOW_MY_BOOL},
- {"lower_case_table_names", (char*) &lower_case_table_names, SHOW_INT},
- {sys_max_allowed_packet.name,(char*) &sys_max_allowed_packet, SHOW_SYS},
- {sys_max_binlog_cache_size.name,(char*) &sys_max_binlog_cache_size, SHOW_SYS},
- {sys_max_binlog_size.name, (char*) &sys_max_binlog_size, SHOW_SYS},
- {sys_max_connect_errors.name, (char*) &sys_max_connect_errors, SHOW_SYS},
- {sys_max_connections.name, (char*) &sys_max_connections, SHOW_SYS},
- {sys_max_delayed_threads.name,(char*) &sys_max_delayed_threads, SHOW_SYS},
- {sys_max_error_count.name, (char*) &sys_max_error_count, SHOW_SYS},
- {sys_max_heap_table_size.name,(char*) &sys_max_heap_table_size, SHOW_SYS},
- {sys_max_insert_delayed_threads.name,
- (char*) &sys_max_insert_delayed_threads, SHOW_SYS},
- {sys_max_join_size.name, (char*) &sys_max_join_size, SHOW_SYS},
- {sys_max_length_for_sort_data.name, (char*) &sys_max_length_for_sort_data,
- SHOW_SYS},
- {sys_max_prepared_stmt_count.name, (char*) &sys_max_prepared_stmt_count,
- SHOW_SYS},
- {sys_max_relay_log_size.name, (char*) &sys_max_relay_log_size, SHOW_SYS},
- {sys_max_seeks_for_key.name, (char*) &sys_max_seeks_for_key, SHOW_SYS},
- {sys_max_sort_length.name, (char*) &sys_max_sort_length, SHOW_SYS},
- {sys_max_sp_recursion_depth.name,
- (char*) &sys_max_sp_recursion_depth, SHOW_SYS},
- {sys_max_tmp_tables.name, (char*) &sys_max_tmp_tables, SHOW_SYS},
- {sys_max_user_connections.name,(char*) &sys_max_user_connections, SHOW_SYS},
- {sys_max_write_lock_count.name, (char*) &sys_max_write_lock_count,SHOW_SYS},
- {sys_multi_range_count.name, (char*) &sys_multi_range_count, SHOW_SYS},
- {sys_myisam_data_pointer_size.name, (char*) &sys_myisam_data_pointer_size, SHOW_SYS},
- {sys_myisam_max_sort_file_size.name, (char*) &sys_myisam_max_sort_file_size,
- SHOW_SYS},
- {"myisam_recover_options", (char*) &myisam_recover_options_str, SHOW_CHAR_PTR},
- {sys_myisam_repair_threads.name, (char*) &sys_myisam_repair_threads,
- SHOW_SYS},
- {sys_myisam_sort_buffer_size.name, (char*) &sys_myisam_sort_buffer_size, SHOW_SYS},
-
- {sys_myisam_stats_method.name, (char*) &sys_myisam_stats_method, SHOW_SYS},
+sys_var_thd_bool sys_keep_files_on_create(&vars, "keep_files_on_create",
+ &SV::keep_files_on_create);
+/* Read only variables */
-#ifdef __NT__
- {"named_pipe", (char*) &opt_enable_named_pipe, SHOW_MY_BOOL},
-#endif
-#ifdef HAVE_NDBCLUSTER_DB
- {sys_ndb_autoincrement_prefetch_sz.name,
- (char*) &sys_ndb_autoincrement_prefetch_sz, SHOW_SYS},
- {sys_ndb_force_send.name, (char*) &sys_ndb_force_send, SHOW_SYS},
- {sys_ndb_use_exact_count.name,(char*) &sys_ndb_use_exact_count, SHOW_SYS},
- {sys_ndb_use_transactions.name,(char*) &sys_ndb_use_transactions, SHOW_SYS},
- {sys_ndb_cache_check_time.name,(char*) &sys_ndb_cache_check_time, SHOW_SYS},
- {sys_ndb_connectstring.name,(char*) &sys_ndb_connectstring, SHOW_SYS},
-#endif
- {sys_net_buffer_length.name,(char*) &sys_net_buffer_length, SHOW_SYS},
- {sys_net_read_timeout.name, (char*) &sys_net_read_timeout, SHOW_SYS},
- {sys_net_retry_count.name, (char*) &sys_net_retry_count, SHOW_SYS},
- {sys_net_write_timeout.name,(char*) &sys_net_write_timeout, SHOW_SYS},
- {sys_new_mode.name, (char*) &sys_new_mode, SHOW_SYS},
- {sys_old_passwords.name, (char*) &sys_old_passwords, SHOW_SYS},
- {"open_files_limit", (char*) &open_files_limit, SHOW_LONG},
- {sys_optimizer_prune_level.name, (char*) &sys_optimizer_prune_level,
- SHOW_SYS},
- {sys_optimizer_search_depth.name,(char*) &sys_optimizer_search_depth,
- SHOW_SYS},
- {"pid_file", (char*) pidfile_name, SHOW_CHAR},
- {sys_plugin_dir.name, (char*) &sys_plugin_dir, SHOW_SYS},
- {"port", (char*) &mysqld_port, SHOW_INT},
- {sys_preload_buff_size.name, (char*) &sys_preload_buff_size, SHOW_SYS},
- {"protocol_version", (char*) &protocol_version, SHOW_INT},
- {sys_query_alloc_block_size.name, (char*) &sys_query_alloc_block_size,
- SHOW_SYS},
-#ifdef HAVE_QUERY_CACHE
- {sys_query_cache_limit.name,(char*) &sys_query_cache_limit, SHOW_SYS},
- {sys_query_cache_min_res_unit.name, (char*) &sys_query_cache_min_res_unit,
- SHOW_SYS},
- {sys_query_cache_size.name, (char*) &sys_query_cache_size, SHOW_SYS},
- {sys_query_cache_type.name, (char*) &sys_query_cache_type, SHOW_SYS},
- {sys_query_cache_wlock_invalidate.name,
- (char *) &sys_query_cache_wlock_invalidate, SHOW_SYS},
-#endif /* HAVE_QUERY_CACHE */
- {sys_query_prealloc_size.name, (char*) &sys_query_prealloc_size, SHOW_SYS},
- {sys_range_alloc_block_size.name, (char*) &sys_range_alloc_block_size,
- SHOW_SYS},
- {sys_read_buff_size.name, (char*) &sys_read_buff_size, SHOW_SYS},
- {sys_readonly.name, (char*) &sys_readonly, SHOW_SYS},
- {sys_read_rnd_buff_size.name,(char*) &sys_read_rnd_buff_size, SHOW_SYS},
-#ifdef HAVE_REPLICATION
- {"relay_log" , (char*) &opt_relay_logname, SHOW_CHAR_PTR},
- {"relay_log_index", (char*) &opt_relaylog_index_name, SHOW_CHAR_PTR},
- {"relay_log_info_file", (char*) &relay_log_info_file, SHOW_CHAR_PTR},
- {sys_relay_log_purge.name, (char*) &sys_relay_log_purge, SHOW_SYS},
- {"relay_log_space_limit", (char*) &relay_log_space_limit, SHOW_LONGLONG},
-#endif
- {sys_rpl_recovery_rank.name,(char*) &sys_rpl_recovery_rank, SHOW_SYS},
- {"secure_auth", (char*) &sys_secure_auth, SHOW_SYS},
- {"secure_file_priv", (char*) &sys_secure_file_priv, SHOW_SYS},
-#ifdef HAVE_SMEM
- {"shared_memory", (char*) &opt_enable_shared_memory, SHOW_MY_BOOL},
- {"shared_memory_base_name", (char*) &shared_memory_base_name, SHOW_CHAR_PTR},
-#endif
- {sys_server_id.name, (char*) &sys_server_id, SHOW_SYS},
- {"skip_external_locking", (char*) &my_disable_locking, SHOW_MY_BOOL},
- {"skip_networking", (char*) &opt_disable_networking, SHOW_BOOL},
- {"skip_show_database", (char*) &opt_skip_show_db, SHOW_BOOL},
-#ifdef HAVE_REPLICATION
- {sys_slave_compressed_protocol.name,
- (char*) &sys_slave_compressed_protocol, SHOW_SYS},
- {sys_slave_load_tmpdir.name,(char*) &sys_slave_load_tmpdir, SHOW_SYS},
- {sys_slave_net_timeout.name,(char*) &sys_slave_net_timeout, SHOW_SYS},
- {"slave_skip_errors", (char*) &slave_error_mask, SHOW_SLAVE_SKIP_ERRORS},
- {sys_slave_trans_retries.name,(char*) &sys_slave_trans_retries, SHOW_SYS},
-#endif
- {sys_slow_launch_time.name, (char*) &sys_slow_launch_time, SHOW_SYS},
-#ifdef HAVE_SYS_UN_H
- {"socket", (char*) &mysqld_unix_port, SHOW_CHAR_PTR},
-#endif
- {sys_sort_buffer.name, (char*) &sys_sort_buffer, SHOW_SYS},
- {sys_big_selects.name, (char*) &sys_big_selects, SHOW_SYS},
- {sys_sql_mode.name, (char*) &sys_sql_mode, SHOW_SYS},
- {"sql_notes", (char*) &sys_sql_notes, SHOW_SYS},
- {"sql_warnings", (char*) &sys_sql_warnings, SHOW_SYS},
- {sys_ssl_ca.name, (char*) &sys_ssl_ca, SHOW_SYS},
- {sys_ssl_capath.name, (char*) &sys_ssl_capath, SHOW_SYS},
- {sys_ssl_cert.name, (char*) &sys_ssl_cert, SHOW_SYS},
- {sys_ssl_cipher.name, (char*) &sys_ssl_cipher, SHOW_SYS},
- {sys_ssl_key.name, (char*) &sys_ssl_key, SHOW_SYS},
- {sys_storage_engine.name, (char*) &sys_storage_engine, SHOW_SYS},
-#ifdef HAVE_REPLICATION
- {sys_sync_binlog_period.name,(char*) &sys_sync_binlog_period, SHOW_SYS},
-#endif
- {sys_sync_frm.name, (char*) &sys_sync_frm, SHOW_SYS},
-#ifdef HAVE_TZNAME
- {"system_time_zone", system_time_zone, SHOW_CHAR},
-#endif
- {"table_cache", (char*) &table_cache_size, SHOW_LONG},
- {"table_lock_wait_timeout", (char*) &table_lock_wait_timeout, SHOW_LONG },
- {sys_table_type.name, (char*) &sys_table_type, SHOW_SYS},
- {sys_thread_cache_size.name,(char*) &sys_thread_cache_size, SHOW_SYS},
-#ifdef HAVE_THR_SETCONCURRENCY
- {"thread_concurrency", (char*) &concurrency, SHOW_LONG},
-#endif
- {"thread_stack", (char*) &thread_stack, SHOW_LONG},
- {sys_time_format.name, (char*) &sys_time_format, SHOW_SYS},
- {"time_zone", (char*) &sys_time_zone, SHOW_SYS},
- {sys_timed_mutexes.name, (char*) &sys_timed_mutexes, SHOW_SYS},
- {sys_tmp_table_size.name, (char*) &sys_tmp_table_size, SHOW_SYS},
- {sys_tmpdir.name, (char*) &sys_tmpdir, SHOW_SYS},
- {sys_trans_alloc_block_size.name, (char*) &sys_trans_alloc_block_size,
- SHOW_SYS},
- {sys_trans_prealloc_size.name, (char*) &sys_trans_prealloc_size, SHOW_SYS},
- {sys_tx_isolation.name, (char*) &sys_tx_isolation, SHOW_SYS},
- {sys_updatable_views_with_limit.name,
- (char*) &sys_updatable_views_with_limit,SHOW_SYS},
- {sys_version.name, (char*) &sys_version, SHOW_SYS},
-#ifdef HAVE_BERKELEY_DB
- {sys_version_bdb.name, (char*) &sys_version_bdb, SHOW_SYS},
-#endif
- {sys_version_comment.name, (char*) &sys_version_comment, SHOW_SYS},
- {sys_version_compile_machine.name, (char*) &sys_version_compile_machine,
- SHOW_SYS},
- {sys_version_compile_os.name, (char*) &sys_version_compile_os, SHOW_SYS},
- {sys_net_wait_timeout.name, (char*) &sys_net_wait_timeout, SHOW_SYS},
- {NullS, NullS, SHOW_LONG}
-};
+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);
bool sys_var::check(THD *thd, set_var *var)
@@ -1178,7 +931,7 @@ bool update_sys_var_str(sys_var_str *var_str, rw_lock_t *var_mutex,
uint new_length= (var ? var->value->str_value.length() : 0);
if (!old_value)
old_value= (char*) "";
- if (!(res= my_strdup_with_length(old_value, new_length, MYF(0))))
+ 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
@@ -1221,7 +974,7 @@ static void sys_default_init_slave(THD* thd, enum_var_type type)
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((byte*)
+ return (ft_boolean_check_syntax_string((uchar*)
var->value->str_value.c_ptr()) ?
-1 : 0);
else
@@ -1250,9 +1003,9 @@ static void sys_default_ftb_syntax(THD *thd, enum_var_type type)
}
-/*
+/**
If one sets the LOW_PRIORIY UPDATES flag, we also must change the
- used lock type
+ used lock type.
*/
static void fix_low_priority_updates(THD *thd, enum_var_type type)
@@ -1274,8 +1027,8 @@ fix_myisam_max_sort_file_size(THD *thd, enum_var_type type)
(my_off_t) global_system_variables.myisam_max_sort_file_size;
}
-/*
- Set the OPTION_BIG_SELECTS flag if max_join_size == HA_POS_ERROR
+/**
+ Set the OPTION_BIG_SELECTS flag if max_join_size == HA_POS_ERROR.
*/
static void fix_max_join_size(THD *thd, enum_var_type type)
@@ -1290,11 +1043,24 @@ static void fix_max_join_size(THD *thd, enum_var_type type)
}
+/**
+ 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
+ is only active for the next command.
*/
-
static void fix_tx_isolation(THD *thd, enum_var_type type)
{
if (type == OPT_SESSION)
@@ -1357,12 +1123,19 @@ static void fix_net_retry_count(THD *thd __attribute__((unused)),
static void fix_query_cache_size(THD *thd, enum_var_type type)
{
#ifdef HAVE_QUERY_CACHE
- ulong requested= query_cache_size;
- query_cache.resize(query_cache_size);
- if (requested != query_cache_size)
+ 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),
- requested, query_cache_size);
+ query_cache_size, new_cache_size);
+
+ query_cache_size= new_cache_size;
#endif
}
@@ -1370,7 +1143,7 @@ static void fix_query_cache_size(THD *thd, enum_var_type type)
#ifdef HAVE_QUERY_CACHE
static void fix_query_cache_min_res_unit(THD *thd, enum_var_type type)
{
- query_cache_min_res_unit=
+ query_cache_min_res_unit=
query_cache.set_min_res_unit(query_cache_min_res_unit);
}
#endif
@@ -1392,6 +1165,136 @@ extern void fix_delay_key_write(THD *thd, enum_var_type type)
}
}
+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)
+{
+ 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(enum_names->type_names[i],
+ enum_names->type_lengths[i]);
+ tmp.append(',');
+ }
+ }
+
+ 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= 0;
+ bit_do_set(slave_exec_mode_options, SLAVE_EXEC_MODE_STRICT);
+}
+
+bool sys_var_set_slave_mode::check(THD *thd, set_var *var)
+{
+ bool rc= sys_var_set::check(thd, var);
+ if (!rc &&
+ bit_is_set(var->save_result.ulong_value, SLAVE_EXEC_MODE_STRICT) == 1 &&
+ bit_is_set(var->save_result.ulong_value, SLAVE_EXEC_MODE_IDEMPOTENT) == 1)
+ {
+ rc= true;
+ my_error(ER_SLAVE_AMBIGOUS_EXEC_MODE, MYF(0), "");
+ }
+ 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;
+}
+
+void fix_slave_exec_mode(enum_var_type type)
+{
+ DBUG_ENTER("fix_slave_exec_mode");
+ compile_time_assert(sizeof(slave_exec_mode_options) * CHAR_BIT
+ > SLAVE_EXEC_MODE_LAST_BIT - 1);
+ if (bit_is_set(slave_exec_mode_options, SLAVE_EXEC_MODE_STRICT) == 1 &&
+ bit_is_set(slave_exec_mode_options, SLAVE_EXEC_MODE_IDEMPOTENT) == 1)
+ {
+ sql_print_error("Ambiguous slave modes combination."
+ " STRICT will be used");
+ bit_do_clear(slave_exec_mode_options, SLAVE_EXEC_MODE_IDEMPOTENT);
+ }
+ if (bit_is_set(slave_exec_mode_options, SLAVE_EXEC_MODE_IDEMPOTENT) == 0)
+ bit_do_set(slave_exec_mode_options, SLAVE_EXEC_MODE_STRICT);
+}
+
+
+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;
+}
+
+
+bool sys_var_thd_binlog_format::is_readonly() const
+{
+ /*
+ 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)
+ {
+ 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");
@@ -1434,7 +1337,7 @@ static int check_max_delayed_threads(THD *thd, set_var *var)
static void fix_max_connections(THD *thd, enum_var_type type)
{
#ifndef EMBEDDED_LIBRARY
- resize_thr_alarm(max_connections +
+ resize_thr_alarm(max_connections +
global_system_variables.max_insert_delayed_threads + 10);
#endif
}
@@ -1467,23 +1370,38 @@ static void fix_server_id(THD *thd, enum_var_type type)
}
-static void throw_bounds_warning(THD *thd, const char *name, ulonglong num)
+bool throw_bounds_warning(THD *thd, bool fixed, bool unsignd,
+ const char *name, longlong val)
{
- char buf[22];
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
- ER_TRUNCATED_WRONG_VALUE,
- ER(ER_TRUNCATED_WRONG_VALUE), name,
- ullstr(num, buf));
+ if (fixed)
+ {
+ char buf[22];
+
+ if (unsignd)
+ ullstr((ulonglong) val, buf);
+ else
+ llstr(val, buf);
+
+ if (thd->variables.sql_mode & MODE_STRICT_ALL_TABLES)
+ {
+ my_error(ER_WRONG_VALUE_FOR_VAR, MYF(0), name, buf);
+ return TRUE;
+ }
+
+ push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ ER_TRUNCATED_WRONG_VALUE,
+ ER(ER_TRUNCATED_WRONG_VALUE), name, buf);
+ }
+ return FALSE;
}
static ulonglong fix_unsigned(THD *thd, ulonglong num,
const struct my_option *option_limits)
{
- bool fixed= FALSE;
+ my_bool fixed= FALSE;
ulonglong out= getopt_ull_limit_value(num, option_limits, &fixed);
- if (fixed)
- throw_bounds_warning(thd, option_limits->name, num);
+ throw_bounds_warning(thd, fixed, TRUE, option_limits->name, (longlong) num);
return out;
}
@@ -1501,9 +1419,9 @@ static bool get_unsigned(THD *thd, set_var *var)
sys_var_long_ptr::
-sys_var_long_ptr(const char *name_arg, ulong *value_ptr_arg,
+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(name_arg, value_ptr_arg,
+ :sys_var_long_ptr_global(chain, name_arg, value_ptr_arg,
&LOCK_global_system_variables, after_update_arg)
{}
@@ -1526,7 +1444,8 @@ bool sys_var_long_ptr_global::update(THD *thd, set_var *var)
if (tmp > ULONG_MAX)
{
tmp= ULONG_MAX;
- throw_bounds_warning(thd, name, var->save_result.ulonglong_value);
+ throw_bounds_warning(thd, TRUE, TRUE, name,
+ (longlong) var->save_result.ulonglong_value);
}
#endif
*value= (ulong) tmp;
@@ -1539,7 +1458,7 @@ bool sys_var_long_ptr_global::update(THD *thd, set_var *var)
void sys_var_long_ptr_global::set_default(THD *thd, enum_var_type type)
{
- bool not_used;
+ my_bool not_used;
pthread_mutex_lock(guard);
*value= (ulong) getopt_ull_limit_value((ulong) option_limits->def_value,
option_limits, &not_used);
@@ -1562,7 +1481,7 @@ bool sys_var_ulonglong_ptr::update(THD *thd, set_var *var)
void sys_var_ulonglong_ptr::set_default(THD *thd, enum_var_type type)
{
- bool not_used;
+ 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);
@@ -1590,9 +1509,16 @@ bool sys_var_enum::update(THD *thd, set_var *var)
}
-byte *sys_var_enum::value_ptr(THD *thd, enum_var_type type, LEX_STRING *base)
+uchar *sys_var_enum::value_ptr(THD *thd, enum_var_type type, LEX_STRING *base)
+{
+ return (uchar*) enum_names->type_names[*value];
+}
+
+
+uchar *sys_var_enum_const::value_ptr(THD *thd, enum_var_type type,
+ LEX_STRING *base)
{
- return (byte*) enum_names->type_names[*value];
+ return (uchar*) enum_names->type_names[global_system_variables.*offset];
}
bool sys_var_thd_ulong::check(THD *thd, set_var *var)
@@ -1606,22 +1532,23 @@ bool sys_var_thd_ulong::update(THD *thd, set_var *var)
ulonglong tmp= var->save_result.ulonglong_value;
/* Don't use bigger value than given with --maximum-variable-name=.. */
- if ((ulong) tmp > max_system_variables.*offset)
+ if (tmp > max_system_variables.*offset)
{
- throw_bounds_warning(thd, name, tmp);
+ throw_bounds_warning(thd, TRUE, TRUE, name, (longlong) tmp);
tmp= max_system_variables.*offset;
}
if (option_limits)
- tmp= (ulong) fix_unsigned(thd, tmp, option_limits);
+ tmp= fix_unsigned(thd, tmp, option_limits);
#if SIZEOF_LONG < SIZEOF_LONG_LONG
else if (tmp > ULONG_MAX)
{
tmp= ULONG_MAX;
- throw_bounds_warning(thd, name, var->save_result.ulonglong_value);
+ throw_bounds_warning(thd, TRUE, TRUE, name, (longlong) var->save_result.ulonglong_value);
}
#endif
+ DBUG_ASSERT(tmp <= ULONG_MAX);
if (var->type == OPT_GLOBAL)
global_system_variables.*offset= (ulong) tmp;
else
@@ -1635,7 +1562,7 @@ void sys_var_thd_ulong::set_default(THD *thd, enum_var_type type)
{
if (type == OPT_GLOBAL)
{
- bool not_used;
+ 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,
@@ -1646,12 +1573,12 @@ void sys_var_thd_ulong::set_default(THD *thd, enum_var_type type)
}
-byte *sys_var_thd_ulong::value_ptr(THD *thd, enum_var_type type,
+uchar *sys_var_thd_ulong::value_ptr(THD *thd, enum_var_type type,
LEX_STRING *base)
{
if (type == OPT_GLOBAL)
- return (byte*) &(global_system_variables.*offset);
- return (byte*) &(thd->variables.*offset);
+ return (uchar*) &(global_system_variables.*offset);
+ return (uchar*) &(thd->variables.*offset);
}
@@ -1668,7 +1595,7 @@ 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);
+ pthread_mutex_lock(&LOCK_global_system_variables);
global_system_variables.*offset= (ha_rows) tmp;
pthread_mutex_unlock(&LOCK_global_system_variables);
}
@@ -1682,7 +1609,7 @@ void sys_var_thd_ha_rows::set_default(THD *thd, enum_var_type type)
{
if (type == OPT_GLOBAL)
{
- bool not_used;
+ 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=
@@ -1695,12 +1622,12 @@ void sys_var_thd_ha_rows::set_default(THD *thd, enum_var_type type)
}
-byte *sys_var_thd_ha_rows::value_ptr(THD *thd, enum_var_type type,
+uchar *sys_var_thd_ha_rows::value_ptr(THD *thd, enum_var_type type,
LEX_STRING *base)
{
if (type == OPT_GLOBAL)
- return (byte*) &(global_system_variables.*offset);
- return (byte*) &(thd->variables.*offset);
+ return (uchar*) &(global_system_variables.*offset);
+ return (uchar*) &(thd->variables.*offset);
}
bool sys_var_thd_ulonglong::check(THD *thd, set_var *var)
@@ -1734,7 +1661,7 @@ void sys_var_thd_ulonglong::set_default(THD *thd, enum_var_type type)
{
if (type == OPT_GLOBAL)
{
- bool not_used;
+ my_bool not_used;
pthread_mutex_lock(&LOCK_global_system_variables);
global_system_variables.*offset=
getopt_ull_limit_value((ulonglong) option_limits->def_value,
@@ -1746,12 +1673,12 @@ void sys_var_thd_ulonglong::set_default(THD *thd, enum_var_type type)
}
-byte *sys_var_thd_ulonglong::value_ptr(THD *thd, enum_var_type type,
+uchar *sys_var_thd_ulonglong::value_ptr(THD *thd, enum_var_type type,
LEX_STRING *base)
{
if (type == OPT_GLOBAL)
- return (byte*) &(global_system_variables.*offset);
- return (byte*) &(thd->variables.*offset);
+ return (uchar*) &(global_system_variables.*offset);
+ return (uchar*) &(thd->variables.*offset);
}
@@ -1774,16 +1701,16 @@ void sys_var_thd_bool::set_default(THD *thd, enum_var_type type)
}
-byte *sys_var_thd_bool::value_ptr(THD *thd, enum_var_type type,
+uchar *sys_var_thd_bool::value_ptr(THD *thd, enum_var_type type,
LEX_STRING *base)
{
if (type == OPT_GLOBAL)
- return (byte*) &(global_system_variables.*offset);
- return (byte*) &(thd->variables.*offset);
+ return (uchar*) &(global_system_variables.*offset);
+ return (uchar*) &(thd->variables.*offset);
}
-bool sys_var::check_enum(THD *thd, set_var *var, TYPELIB *enum_names)
+bool sys_var::check_enum(THD *thd, set_var *var, const TYPELIB *enum_names)
{
char buff[STRING_BUFFER_USUAL_SIZE];
const char *value;
@@ -1833,6 +1760,14 @@ bool sys_var::check_set(THD *thd, set_var *var, TYPELIB *enum_names)
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(),
res->length(),
@@ -1848,10 +1783,19 @@ bool sys_var::check_set(THD *thd, set_var *var, TYPELIB *enum_names)
else
{
ulonglong tmp= var->value->val_int();
- /*
- For when the enum is made to contain 64 elements, as 1ULL<<64 is
- undefined, we guard with a "count<64" test.
- */
+
+ 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)))
{
@@ -1868,82 +1812,6 @@ err:
}
-/*
- Return an Item for a variable. Used with @@[global.]variable_name
- If type is not given, return local value if exists, else global
-*/
-
-Item *sys_var::item(THD *thd, enum_var_type var_type, LEX_STRING *base)
-{
- if (check_type(var_type))
- {
- if (var_type != OPT_DEFAULT)
- {
- my_error(ER_INCORRECT_GLOBAL_LOCAL_VAR, MYF(0),
- name, var_type == OPT_GLOBAL ? "SESSION" : "GLOBAL");
- return 0;
- }
- /* As there was no local variable, return the global value */
- var_type= OPT_GLOBAL;
- }
- switch (show_type()) {
- case SHOW_INT:
- {
- uint value;
- pthread_mutex_lock(&LOCK_global_system_variables);
- value= *(uint*) value_ptr(thd, var_type, base);
- pthread_mutex_unlock(&LOCK_global_system_variables);
- return new Item_uint((ulonglong) value);
- }
- case SHOW_LONG:
- {
- ulong value;
- pthread_mutex_lock(&LOCK_global_system_variables);
- value= *(ulong*) value_ptr(thd, var_type, base);
- pthread_mutex_unlock(&LOCK_global_system_variables);
- return new Item_uint((ulonglong) value);
- }
- case SHOW_LONGLONG:
- {
- longlong value;
- pthread_mutex_lock(&LOCK_global_system_variables);
- value= *(longlong*) value_ptr(thd, var_type, base);
- pthread_mutex_unlock(&LOCK_global_system_variables);
- return new Item_int(value);
- }
- case SHOW_HA_ROWS:
- {
- ha_rows value;
- pthread_mutex_lock(&LOCK_global_system_variables);
- value= *(ha_rows*) value_ptr(thd, var_type, base);
- pthread_mutex_unlock(&LOCK_global_system_variables);
- return new Item_int((longlong) value);
- }
- case SHOW_MY_BOOL:
- return new Item_int((int32) *(my_bool*) value_ptr(thd, var_type, base),1);
- case SHOW_CHAR:
- {
- Item *tmp;
- pthread_mutex_lock(&LOCK_global_system_variables);
- char *str= (char*) value_ptr(thd, var_type, base);
- if (str)
- tmp= new Item_string(str, (uint) strlen(str),
- charset(thd), DERIVATION_SYSCONST);
- else
- {
- tmp= new Item_null();
- tmp->collation.set(charset(thd), DERIVATION_SYSCONST);
- }
- pthread_mutex_unlock(&LOCK_global_system_variables);
- return tmp;
- }
- default:
- my_error(ER_VAR_CANT_BE_READ, MYF(0), name);
- }
- return 0;
-}
-
-
CHARSET_INFO *sys_var::charset(THD *thd)
{
return is_os_charset ? thd->variables.character_set_filesystem :
@@ -1970,13 +1838,13 @@ void sys_var_thd_enum::set_default(THD *thd, enum_var_type type)
}
-byte *sys_var_thd_enum::value_ptr(THD *thd, enum_var_type type,
+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 (byte*) enum_names->type_names[tmp];
+ return (uchar*) enum_names->type_names[tmp];
}
bool sys_var_thd_bit::check(THD *thd, set_var *var)
@@ -1992,7 +1860,7 @@ bool sys_var_thd_bit::update(THD *thd, set_var *var)
}
-byte *sys_var_thd_bit::value_ptr(THD *thd, enum_var_type type,
+uchar *sys_var_thd_bit::value_ptr(THD *thd, enum_var_type type,
LEX_STRING *base)
{
/*
@@ -2001,18 +1869,18 @@ byte *sys_var_thd_bit::value_ptr(THD *thd, enum_var_type type,
*/
thd->sys_var_tmp.my_bool_value= ((thd->options & bit_flag) ?
!reverse : reverse);
- return (byte*) &thd->sys_var_tmp.my_bool_value;
+ return (uchar*) &thd->sys_var_tmp.my_bool_value;
}
-/* Update a date_time format variable based on given 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", new_value->positions,
+ DBUG_DUMP("positions", (uchar*) new_value->positions,
sizeof(new_value->positions));
if (type == OPT_GLOBAL)
@@ -2060,7 +1928,7 @@ bool sys_var_thd_date_time_format::check(THD *thd, set_var *var)
my_error(ER_WRONG_VALUE_FOR_VAR, MYF(0), name, res->c_ptr());
return 1;
}
-
+
/*
We must copy result to thread space to not get a memory leak if
update is aborted
@@ -2079,7 +1947,7 @@ void sys_var_thd_date_time_format::set_default(THD *thd, enum_var_type type)
{
const char *format;
if ((format= opt_date_time_formats[date_time_type]))
- res= date_time_format_make(date_time_type, format, (uint) strlen(format));
+ res= date_time_format_make(date_time_type, format, strlen(format));
}
else
{
@@ -2092,7 +1960,7 @@ void sys_var_thd_date_time_format::set_default(THD *thd, enum_var_type type)
}
-byte *sys_var_thd_date_time_format::value_ptr(THD *thd, enum_var_type type,
+uchar *sys_var_thd_date_time_format::value_ptr(THD *thd, enum_var_type type,
LEX_STRING *base)
{
if (type == OPT_GLOBAL)
@@ -2105,9 +1973,9 @@ byte *sys_var_thd_date_time_format::value_ptr(THD *thd, enum_var_type type,
*/
res= thd->strmake((global_system_variables.*offset)->format.str,
(global_system_variables.*offset)->format.length);
- return (byte*) res;
+ return (uchar*) res;
}
- return (byte*) (thd->variables.*offset)->format.str;
+ return (uchar*) (thd->variables.*offset)->format.str;
}
@@ -2117,7 +1985,7 @@ typedef struct old_names_map_st
const char *new_name;
} my_old_conv;
-static my_old_conv old_conv[]=
+static my_old_conv old_conv[]=
{
{ "cp1251_koi8" , "cp1251" },
{ "cp1250_latin2" , "cp1250" },
@@ -2135,7 +2003,7 @@ static my_old_conv old_conv[]=
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))
@@ -2148,6 +2016,7 @@ CHARSET_INFO *get_old_charset_by_name(const char *name)
bool sys_var_collation::check(THD *thd, set_var *var)
{
CHARSET_INFO *tmp;
+ LINT_INIT(tmp);
if (var->value->result_type() == STRING_RESULT)
{
@@ -2182,6 +2051,7 @@ bool sys_var_collation::check(THD *thd, set_var *var)
bool sys_var_character_set::check(THD *thd, set_var *var)
{
CHARSET_INFO *tmp;
+ LINT_INIT(tmp);
if (var->value->result_type() == STRING_RESULT)
{
@@ -2226,145 +2096,48 @@ bool sys_var_character_set::update(THD *thd, set_var *var)
}
-byte *sys_var_character_set::value_ptr(THD *thd, enum_var_type type,
+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 ? (byte*) cs->csname : (byte*) NULL;
+ return cs ? (uchar*) cs->csname : (uchar*) NULL;
}
-CHARSET_INFO ** sys_var_character_set_connection::ci_ptr(THD *thd,
- enum_var_type type)
+void sys_var_character_set_sv::set_default(THD *thd, enum_var_type type)
{
if (type == OPT_GLOBAL)
- return &global_system_variables.collation_connection;
+ global_system_variables.*offset= *global_default;
else
- return &thd->variables.collation_connection;
-}
-
-
-void sys_var_character_set_connection::set_default(THD *thd,
- enum_var_type type)
-{
- if (type == OPT_GLOBAL)
- global_system_variables.collation_connection= default_charset_info;
- else
- {
- thd->variables.collation_connection= global_system_variables.collation_connection;
- thd->update_charset();
- }
-}
-
-
-CHARSET_INFO ** sys_var_character_set_client::ci_ptr(THD *thd,
- enum_var_type type)
-{
- if (type == OPT_GLOBAL)
- return &global_system_variables.character_set_client;
- else
- return &thd->variables.character_set_client;
-}
-
-
-void sys_var_character_set_client::set_default(THD *thd, enum_var_type type)
-{
- if (type == OPT_GLOBAL)
- global_system_variables.character_set_client= default_charset_info;
- else
- {
- thd->variables.character_set_client= (global_system_variables.
- character_set_client);
- thd->update_charset();
- }
+ {
+ thd->variables.*offset= global_system_variables.*offset;
+ thd->update_charset();
+ }
}
-
-
-CHARSET_INFO **
-sys_var_character_set_filesystem::ci_ptr(THD *thd, enum_var_type type)
+CHARSET_INFO **sys_var_character_set_sv::ci_ptr(THD *thd, enum_var_type type)
{
if (type == OPT_GLOBAL)
- return &global_system_variables.character_set_filesystem;
+ return &(global_system_variables.*offset);
else
- return &thd->variables.character_set_filesystem;
-}
-
-
-extern CHARSET_INFO *character_set_filesystem;
-
-void
-sys_var_character_set_filesystem::set_default(THD *thd, enum_var_type type)
-{
- if (type == OPT_GLOBAL)
- global_system_variables.character_set_filesystem= character_set_filesystem;
- else
- {
- thd->variables.character_set_filesystem= (global_system_variables.
- character_set_filesystem);
- thd->update_charset();
- }
+ return &(thd->variables.*offset);
}
bool sys_var_character_set_client::check(THD *thd, set_var *var)
{
- if (sys_var_character_set::check(thd, var))
+ if (sys_var_character_set_sv::check(thd, var))
return 1;
/* Currently, UCS-2 cannot be used as a client character set */
if (var->save_result.charset->mbminlen > 1)
{
my_error(ER_WRONG_VALUE_FOR_VAR, MYF(0), name,
- var->save_result.charset->csname);
+ var->save_result.charset->csname);
return 1;
}
return 0;
}
-CHARSET_INFO **
-sys_var_character_set_results::ci_ptr(THD *thd, enum_var_type type)
-{
- if (type == OPT_GLOBAL)
- return &global_system_variables.character_set_results;
- else
- return &thd->variables.character_set_results;
-}
-
-
-void sys_var_character_set_results::set_default(THD *thd, enum_var_type type)
-{
- if (type == OPT_GLOBAL)
- global_system_variables.character_set_results= default_charset_info;
- else
- {
- thd->variables.character_set_results= (global_system_variables.
- character_set_results);
- thd->update_charset();
- }
-}
-
-
-CHARSET_INFO **
-sys_var_character_set_server::ci_ptr(THD *thd, enum_var_type type)
-{
- if (type == OPT_GLOBAL)
- return &global_system_variables.collation_server;
- else
- return &thd->variables.collation_server;
-}
-
-
-void sys_var_character_set_server::set_default(THD *thd, enum_var_type type)
-{
- if (type == OPT_GLOBAL)
- global_system_variables.collation_server= default_charset_info;
- else
- {
- thd->variables.collation_server= global_system_variables.collation_server;
- thd->update_charset();
- }
-}
-
CHARSET_INFO ** sys_var_character_set_database::ci_ptr(THD *thd,
enum_var_type type)
{
@@ -2387,110 +2160,37 @@ void sys_var_character_set_database::set_default(THD *thd, enum_var_type type)
}
-bool sys_var_collation_connection::update(THD *thd, set_var *var)
-{
- if (var->type == OPT_GLOBAL)
- global_system_variables.collation_connection= var->save_result.charset;
- else
- {
- thd->variables.collation_connection= var->save_result.charset;
- thd->update_charset();
- }
- return 0;
-}
-
-
-byte *sys_var_collation_connection::value_ptr(THD *thd, enum_var_type type,
- LEX_STRING *base)
-{
- CHARSET_INFO *cs= ((type == OPT_GLOBAL) ?
- global_system_variables.collation_connection :
- thd->variables.collation_connection);
- return cs ? (byte*) cs->name : (byte*) "NULL";
-}
-
-
-void sys_var_collation_connection::set_default(THD *thd, enum_var_type type)
-{
- if (type == OPT_GLOBAL)
- global_system_variables.collation_connection= default_charset_info;
- else
- {
- thd->variables.collation_connection= (global_system_variables.
- collation_connection);
- thd->update_charset();
- }
-}
-
-bool sys_var_collation_database::update(THD *thd, set_var *var)
+bool sys_var_collation_sv::update(THD *thd, set_var *var)
{
if (var->type == OPT_GLOBAL)
- global_system_variables.collation_database= var->save_result.charset;
+ global_system_variables.*offset= var->save_result.charset;
else
{
- thd->variables.collation_database= var->save_result.charset;
+ thd->variables.*offset= var->save_result.charset;
thd->update_charset();
}
return 0;
}
-byte *sys_var_collation_database::value_ptr(THD *thd, enum_var_type type,
- LEX_STRING *base)
-{
- CHARSET_INFO *cs= ((type == OPT_GLOBAL) ?
- global_system_variables.collation_database :
- thd->variables.collation_database);
- return cs ? (byte*) cs->name : (byte*) "NULL";
-}
-
-
-void sys_var_collation_database::set_default(THD *thd, enum_var_type type)
+void sys_var_collation_sv::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= (global_system_variables.
- collation_database);
- thd->update_charset();
- }
-}
-
-
-bool sys_var_collation_server::update(THD *thd, set_var *var)
-{
- if (var->type == OPT_GLOBAL)
- global_system_variables.collation_server= var->save_result.charset;
+ if (type == OPT_GLOBAL)
+ global_system_variables.*offset= *global_default;
else
{
- thd->variables.collation_server= var->save_result.charset;
+ thd->variables.*offset= global_system_variables.*offset;
thd->update_charset();
}
- return 0;
}
-byte *sys_var_collation_server::value_ptr(THD *thd, enum_var_type type,
- LEX_STRING *base)
+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.collation_server :
- thd->variables.collation_server);
- return cs ? (byte*) cs->name : (byte*) "NULL";
-}
-
-
-void sys_var_collation_server::set_default(THD *thd, enum_var_type type)
-{
- if (type == OPT_GLOBAL)
- global_system_variables.collation_server= default_charset_info;
- else
- {
- thd->variables.collation_server= (global_system_variables.
- collation_server);
- thd->update_charset();
- }
+ global_system_variables.*offset : thd->variables.*offset);
+ return cs ? (uchar*) cs->name : (uchar*) "NULL";
}
@@ -2508,13 +2208,13 @@ KEY_CACHE *get_key_cache(LEX_STRING *cache_name)
}
-byte *sys_var_key_cache_param::value_ptr(THD *thd, enum_var_type type,
+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 (byte*) key_cache + offset ;
+ return (uchar*) key_cache + offset ;
}
@@ -2531,7 +2231,7 @@ bool sys_var_key_buffer_size::update(THD *thd, set_var *var)
pthread_mutex_lock(&LOCK_global_system_variables);
key_cache= get_key_cache(base_name);
-
+
if (!key_cache)
{
/* Key cache didn't exists */
@@ -2568,7 +2268,7 @@ bool sys_var_key_buffer_size::update(THD *thd, set_var *var)
Move tables using this key cache to the default key cache
and clear the old key cache.
*/
- NAMED_LIST *list;
+ NAMED_LIST *list;
key_cache= (KEY_CACHE *) find_named(&key_caches, base_name->str,
base_name->length, &list);
key_cache->in_init= 1;
@@ -2597,7 +2297,7 @@ bool sys_var_key_buffer_size::update(THD *thd, set_var *var)
error= (bool)(ha_resize_key_cache(key_cache));
pthread_mutex_lock(&LOCK_global_system_variables);
- key_cache->in_init= 0;
+ key_cache->in_init= 0;
end:
pthread_mutex_unlock(&LOCK_global_system_variables);
@@ -2605,6 +2305,12 @@ end:
}
+/**
+ @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)
{
ulong tmp= (ulong) var->value->val_int();
@@ -2646,7 +2352,7 @@ bool sys_var_key_cache_long::update(THD *thd, set_var *var)
error= (bool) (ha_resize_key_cache(key_cache));
pthread_mutex_lock(&LOCK_global_system_variables);
- key_cache->in_init= 0;
+ key_cache->in_init= 0;
end:
pthread_mutex_unlock(&LOCK_global_system_variables);
@@ -2654,6 +2360,243 @@ end:
}
+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'");
+
+ pthread_mutex_lock(&LOCK_global_system_variables);
+ if (!var->save_result.ulong_value)
+ {
+ logger.deactivate_log_handler(thd, log_type);
+ res= false;
+ }
+ else
+ res= logger.activate_log_handler(thd, log_type);
+ pthread_mutex_unlock(&LOCK_global_system_variables);
+ 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'");
+
+ pthread_mutex_lock(&LOCK_global_system_variables);
+ logger.deactivate_log_handler(thd, log_type);
+ pthread_mutex_unlock(&LOCK_global_system_variables);
+}
+
+
+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;
+ size_t path_length;
+
+ if (!(res= var->value->val_str(&str)))
+ goto err;
+
+ log_file_str= res->c_ptr();
+ 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,
+ res ? 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;
+ 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:
+ assert(0); // Impossible
+ }
+
+ 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;
+ }
+
+ pthread_mutex_lock(&LOCK_global_system_variables);
+ logger.lock_exclusive();
+
+ if (file_log && log_state)
+ file_log->close(0);
+ old_value= var_str->value;
+ var_str->value= res;
+ var_str->value_length= str_length;
+ my_free(old_value, MYF(MY_ALLOW_ZERO_PTR));
+ if (file_log && log_state)
+ {
+ switch (log_type) {
+ case QUERY_LOG_SLOW:
+ file_log->open_slow_log(sys_var_slow_log_path.value);
+ break;
+ case QUERY_LOG_GENERAL:
+ file_log->open_query_log(sys_var_general_log_path.value);
+ break;
+ default:
+ DBUG_ASSERT(0);
+ }
+ }
+
+ logger.unlock();
+ 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)
+{
+ pthread_mutex_lock(&LOCK_global_system_variables);
+ 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();
+ pthread_mutex_unlock(&LOCK_global_system_variables);
+ return 0;
+}
+
+
+void sys_var_log_output::set_default(THD *thd, enum_var_type type)
+{
+ pthread_mutex_lock(&LOCK_global_system_variables);
+ logger.lock_exclusive();
+ logger.init_slow_log(LOG_FILE);
+ logger.init_general_log(LOG_FILE);
+ *value= LOG_FILE;
+ logger.unlock();
+ pthread_mutex_unlock(&LOCK_global_system_variables);
+}
+
+
+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
*****************************************************************************/
@@ -2676,8 +2619,8 @@ int set_var_collation_client::update(THD *thd)
thd->variables.character_set_results= character_set_results;
thd->variables.collation_connection= collation_connection;
thd->update_charset();
- thd->protocol_simple.init(thd);
- thd->protocol_prep.init(thd);
+ thd->protocol_text.init(thd);
+ thd->protocol_binary.init(thd);
return 0;
}
@@ -2696,99 +2639,51 @@ void sys_var_timestamp::set_default(THD *thd, enum_var_type type)
}
-byte *sys_var_timestamp::value_ptr(THD *thd, enum_var_type type,
+uchar *sys_var_timestamp::value_ptr(THD *thd, enum_var_type type,
LEX_STRING *base)
{
thd->sys_var_tmp.long_value= (long) thd->start_time;
- return (byte*) &thd->sys_var_tmp.long_value;
+ return (uchar*) &thd->sys_var_tmp.long_value;
}
bool sys_var_last_insert_id::update(THD *thd, set_var *var)
{
- thd->insert_id(var->save_result.ulonglong_value);
+ thd->first_successful_insert_id_in_prev_stmt=
+ var->save_result.ulonglong_value;
return 0;
}
-byte *sys_var_last_insert_id::value_ptr(THD *thd, enum_var_type type,
+uchar *sys_var_last_insert_id::value_ptr(THD *thd, enum_var_type type,
LEX_STRING *base)
{
- if (!thd->last_insert_id_used)
- {
- /*
- As this statement reads @@LAST_INSERT_ID, set
- THD::last_insert_id_used and remember first generated insert id
- of the previous statement in THD::current_insert_id.
- */
- thd->last_insert_id_used= TRUE;
- thd->last_insert_id_used_bin_log= TRUE;
- thd->current_insert_id= thd->last_insert_id;
- }
- return (byte*) &thd->current_insert_id;
+ /*
+ 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->next_insert_id= var->save_result.ulonglong_value;
+ thd->force_one_auto_inc_interval(var->save_result.ulonglong_value);
return 0;
}
-byte *sys_var_insert_id::value_ptr(THD *thd, enum_var_type type,
+uchar *sys_var_insert_id::value_ptr(THD *thd, enum_var_type type,
LEX_STRING *base)
{
- return (byte*) &thd->next_insert_id;
-}
-
-
-#ifdef HAVE_REPLICATION
-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;
+ thd->sys_var_tmp.ulonglong_value=
+ thd->auto_inc_intervals_forced.minimum();
+ return (uchar*) &thd->sys_var_tmp.ulonglong_value;
}
-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;
-}
-#endif /* HAVE_REPLICATION */
-
bool sys_var_rand_seed1::update(THD *thd, set_var *var)
{
thd->rand.seed1= (ulong) var->save_result.ulonglong_value;
@@ -2808,8 +2703,7 @@ bool sys_var_thd_time_zone::check(THD *thd, set_var *var)
String str(buff, sizeof(buff), &my_charset_latin1);
String *res= var->value->val_str(&str);
- if (!(var->save_result.time_zone=
- my_tz_find(res, thd->lex->time_zone_tables_used)))
+ 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;
@@ -2833,15 +2727,15 @@ bool sys_var_thd_time_zone::update(THD *thd, set_var *var)
}
-byte *sys_var_thd_time_zone::value_ptr(THD *thd, enum_var_type type,
+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 (byte *)(global_system_variables.time_zone->get_name()->ptr());
+ return (uchar *)(global_system_variables.time_zone->get_name()->ptr());
else
{
/*
@@ -2853,7 +2747,7 @@ byte *sys_var_thd_time_zone::value_ptr(THD *thd, enum_var_type type,
(binlog code stores session value only).
*/
thd->time_zone_used= 1;
- return (byte *)(thd->variables.time_zone->get_name()->ptr());
+ return (uchar *)(thd->variables.time_zone->get_name()->ptr());
}
}
@@ -2870,8 +2764,7 @@ void sys_var_thd_time_zone::set_default(THD *thd, enum_var_type type)
We are guaranteed to find this time zone since its existence
is checked during start-up.
*/
- global_system_variables.time_zone=
- my_tz_find(&str, thd->lex->time_zone_tables_used);
+ global_system_variables.time_zone= my_tz_find(thd, &str);
}
else
global_system_variables.time_zone= my_tz_SYSTEM;
@@ -2890,7 +2783,7 @@ bool sys_var_max_user_conn::check(THD *thd, set_var *var)
{
/*
Per-session values of max_user_connections can't be set directly.
- QQ: May be we should have a separate error message for this?
+ May be we should have a separate error message for this?
*/
my_error(ER_GLOBAL_VARIABLE, MYF(0), name);
return TRUE;
@@ -2916,13 +2809,25 @@ void sys_var_max_user_conn::set_default(THD *thd, enum_var_type type)
}
-byte *sys_var_max_user_conn::value_ptr(THD *thd, enum_var_type type,
+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 (byte*) &(thd->user_connect->user_resources.user_conn);
- return (byte*) &(max_user_connections);
+ 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);
}
@@ -2973,12 +2878,12 @@ bool sys_var_thd_lc_time_names::update(THD *thd, set_var *var)
}
-byte *sys_var_thd_lc_time_names::value_ptr(THD *thd, enum_var_type type,
+uchar *sys_var_thd_lc_time_names::value_ptr(THD *thd, enum_var_type type,
LEX_STRING *base)
{
return type == OPT_GLOBAL ?
- (byte *) global_system_variables.lc_time_names->name :
- (byte *) thd->variables.lc_time_names->name;
+ (uchar *) global_system_variables.lc_time_names->name :
+ (uchar *) thd->variables.lc_time_names->name;
}
@@ -2991,6 +2896,60 @@ void sys_var_thd_lc_time_names::set_default(THD *thd, enum_var_type type)
}
/*
+ Handling of microseoncds 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->tmp_double_value= (double) ((type == OPT_GLOBAL) ?
+ global_system_variables.*offset :
+ thd->variables.*offset) / 1000000.0;
+ return (uchar*) &thd->tmp_double_value;
+}
+
+
+/*
Functions to update thd->options bits
*/
@@ -3021,7 +2980,7 @@ static bool set_option_autocommit(THD *thd, set_var *var)
if ((org_options & OPTION_NOT_AUTOCOMMIT))
{
/* We changed to auto_commit mode */
- thd->options&= ~(ulong) OPTION_BEGIN;
+ thd->options&= ~(ulonglong) (OPTION_BEGIN | OPTION_KEEP_LOG);
thd->transaction.all.modified_non_trans_table= FALSE;
thd->server_status|= SERVER_STATUS_AUTOCOMMIT;
if (ha_commit(thd))
@@ -3058,8 +3017,6 @@ static bool set_log_update(THD *thd, set_var *var)
if (opt_sql_bin_update)
{
- ((sys_var_thd_bit*) var->var)->bit_flag|= (OPTION_BIN_LOG |
- OPTION_UPDATE_LOG);
push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
ER_UPDATE_LOG_DEPRECATED_TRANSLATED,
ER(ER_UPDATE_LOG_DEPRECATED_TRANSLATED));
@@ -3072,14 +3029,6 @@ static bool set_log_update(THD *thd, set_var *var)
return 0;
}
-static bool set_log_bin(THD *thd, set_var *var)
-{
- if (opt_sql_bin_update)
- ((sys_var_thd_bit*) var->var)->bit_flag|= (OPTION_BIN_LOG |
- OPTION_UPDATE_LOG);
- set_option_bit(thd, var);
- return 0;
-}
static int check_pseudo_thread_id(THD *thd, set_var *var)
{
@@ -3097,51 +3046,42 @@ static int check_pseudo_thread_id(THD *thd, set_var *var)
#endif
}
-static byte *get_warning_count(THD *thd)
+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 (byte*) &thd->sys_var_tmp.long_value;
+ return (uchar*) &thd->sys_var_tmp.long_value;
}
-static byte *get_error_count(THD *thd)
+static uchar *get_error_count(THD *thd)
{
- thd->sys_var_tmp.long_value=
+ thd->sys_var_tmp.long_value=
thd->warn_count[(uint) MYSQL_ERROR::WARN_LEVEL_ERROR];
- return (byte*) &thd->sys_var_tmp.long_value;
+ return (uchar*) &thd->sys_var_tmp.long_value;
}
-static byte *get_have_innodb(THD *thd)
-{
- return (byte*) show_comp_option_name[have_innodb];
-}
-
+/**
+ Get the tmpdir that was specified or chosen by default.
-/*
- Get the tmpdir that was specified or chosen by default
-
- SYNOPSIS
- get_tmpdir()
- thd thread handle
+ 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.
- DESCRIPTION
- 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
- RETURN VALUES
+ @retval
ptr pointer to NUL-terminated string
- */
-static byte *get_tmpdir(THD *thd)
+*/
+static uchar *get_tmpdir(THD *thd)
{
if (opt_mysql_tmpdir)
- return (byte *)opt_mysql_tmpdir;
- return (byte*)mysql_tmpdir;
+ return (uchar *)opt_mysql_tmpdir;
+ return (uchar*)mysql_tmpdir;
}
/****************************************************************************
@@ -3151,25 +3091,25 @@ static byte *get_tmpdir(THD *thd)
- Update loop
****************************************************************************/
-/*
- Find variable name in option my_getopt structure used for command line args
+/**
+ Find variable name in option my_getopt structure used for
+ command line args.
- SYNOPSIS
- find_option()
- opt option structure array to search in
- name variable name
+ @param opt option structure array to search in
+ @param name variable name
- RETURN VALUES
+ @retval
0 Error
+ @retval
ptr pointer to option structure
*/
-static struct my_option *find_option(struct my_option *opt, const char *name)
+static struct my_option *find_option(struct my_option *opt, const char *name)
{
- size_t length=strlen(name);
+ uint length=strlen(name);
for (; opt->name; opt++)
{
- if (!getopt_compare_strings(opt->name, name, (uint) length) &&
+ if (!getopt_compare_strings(opt->name, name, length) &&
!opt->name[length])
{
/*
@@ -3183,37 +3123,157 @@ static struct my_option *find_option(struct my_option *opt, const char *name)
}
-/*
- Return variable name and length for hashing of variables
+/**
+ Return variable name and length for hashing of variables.
*/
-static byte *get_sys_var_length(const sys_var *var, uint *length,
- my_bool first)
+static uchar *get_sys_var_length(const sys_var *var, size_t *length,
+ my_bool first)
{
*length= var->name_length;
- return (byte*) var->name;
+ return (uchar*) var->name;
}
/*
- Initialises sys variables and put them in system_variable_hash
+ 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
+ otherwise FAILURE
*/
-void set_var_init()
+int mysql_add_sys_var_chain(sys_var *first, struct my_option *long_options)
{
- hash_init(&system_variable_hash, system_charset_info,
- array_elements(sys_variables),0,0,
- (hash_get_key) get_sys_var_length,0,0);
- sys_var **var, **end;
- for (var= sys_variables, end= sys_variables+array_elements(sys_variables) ;
- var < end;
- var++)
+ sys_var *var;
+
+ /* A write lock should be held on LOCK_system_variables_hash */
+
+ for (var= first; var; var= var->next)
{
- (*var)->name_length= (uint) strlen((*var)->name);
- (*var)->option_limits= find_option(my_long_options, (*var)->name);
- my_hash_insert(&system_variable_hash, (byte*) *var);
+ 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))
+ 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);
+ 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 */
+
+ for (sys_var *var= first; var; var= var->next)
+ result|= hash_delete(&system_variable_hash, (uchar*) var);
+
+ 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
+ pointer Array of SHOW_VAR elements for display
+ NULL FAILURE
+*/
+
+SHOW_VAR* enumerate_sys_vars(THD *thd, bool sorted)
+{
+ int count= system_variable_hash.records, i;
+ int size= sizeof(SHOW_VAR) * (count + 1);
+ SHOW_VAR *result= (SHOW_VAR*) thd->alloc(size);
+
+ if (result)
+ {
+ SHOW_VAR *show= result;
+
+ for (i= 0; i < count; i++)
+ {
+ sys_var *var= (sys_var*) hash_element(&system_variable_hash, i);
+ show->name= var->name;
+ show->value= (char*) var;
+ show->type= SHOW_SYS;
+ show++;
+ }
+
+ /* sort into order */
+ if (sorted)
+ my_qsort(result, count, 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
@@ -3221,6 +3281,12 @@ void set_var_init()
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);
}
@@ -3230,51 +3296,54 @@ void set_var_free()
}
-/*
- Find a user set-table variable
+/**
+ Find a user set-table variable.
- SYNOPSIS
- find_sys_var()
- str Name of system variable to find
- length Length of variable. zero means that we should use strlen()
- on the variable
+ @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.
- RETURN VALUES
+ @retval
pointer pointer to variable definitions
+ @retval
0 Unknown variable (error message is given)
*/
-sys_var *find_sys_var(const char *str, uint length)
+sys_var *intern_find_sys_var(const char *str, uint length, bool no_error)
{
- sys_var *var= (sys_var*) hash_search(&system_variable_hash,
- (byte*) str,
- length ? length :
- (uint) strlen(str));
- if (!var)
+ sys_var *var;
+
+ /*
+ 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);
+
return var;
}
-/*
- Execute update of all variables
-
- SYNOPSIS
+/**
+ Execute update of all variables.
- sql_set
- THD Thread id
- set_var List of variables to update
+ First run a check of all variables that all updates will go ok.
+ If yes, then execute all updates, returning an error if any one failed.
- DESCRIPTION
- First run a check of all variables that all updates will go ok.
- If yes, then execute all updates, returning an error if any one failed.
+ This should ensure that in all normal cases none all or variables are
+ updated.
- This should ensure that in all normal cases none all or variables are
- updated
+ @param THD Thread id
+ @param var_list List of variables to update
- RETURN VALUE
+ @retval
0 ok
+ @retval
1 ERROR, message sent (normally no variables was updated)
+ @retval
-1 ERROR, message not sent
*/
@@ -3290,7 +3359,7 @@ int sql_set_variables(THD *thd, List<set_var_base> *var_list)
if ((error= var->check(thd)))
goto err;
}
- if (!(error= test(thd->net.report_error)))
+ if (!(error= test(thd->is_error())))
{
it.rewind();
while ((var= it++))
@@ -3303,20 +3372,19 @@ err:
}
-/*
- Say if all variables set by a SET support the ONE_SHOT keyword (currently,
- only character set and collation do; later timezones will).
+/**
+ Say if all variables set by a SET support the ONE_SHOT keyword
+ (currently, only character set and collation do; later timezones
+ will).
- SYNOPSIS
+ @param var_list List of variables to update
- not_all_support_one_shot
- set_var List of variables to update
-
- NOTES
+ @note
It has a "not_" because it makes faster tests (no need to "!")
- RETURN VALUE
+ @retval
0 all variables of the list support ONE_SHOT
+ @retval
1 at least one does not support ONE_SHOT
*/
@@ -3375,17 +3443,17 @@ int set_var::check(THD *thd)
}
-/*
- Check variable, but without assigning value (used by PS)
+/**
+ Check variable, but without assigning value (used by PS).
- SYNOPSIS
- set_var::light_check()
- thd thread handler
+ @param thd thread handler
- RETURN VALUE
+ @retval
0 ok
+ @retval
1 ERROR, message sent (normally no variables was updated)
- -1 ERROR, message not sent
+ @retval
+ -1 ERROR, message not sent
*/
int set_var::light_check(THD *thd)
{
@@ -3404,7 +3472,18 @@ int set_var::light_check(THD *thd)
return 0;
}
+/**
+ Update variable
+
+ @param thd thread handler
+ @returns 0|1 ok or ERROR
+ @note ERROR can be only due to abnormal operations involving
+ the server's execution evironment such as
+ out of memory, hard disk failure or the computer blows up.
+ Consider set_var::check() method if there is a need to return
+ an error due to logics.
+*/
int set_var::update(THD *thd)
{
if (!value)
@@ -3432,17 +3511,17 @@ int set_var_user::check(THD *thd)
}
-/*
- Check variable, but without assigning value (used by PS)
+/**
+ Check variable, but without assigning value (used by PS).
- SYNOPSIS
- set_var_user::light_check()
- thd thread handler
+ @param thd thread handler
- RETURN VALUE
+ @retval
0 ok
+ @retval
1 ERROR, message sent (normally no variables was updated)
- -1 ERROR, message not sent
+ @retval
+ -1 ERROR, message not sent
*/
int set_var_user::light_check(THD *thd)
{
@@ -3479,7 +3558,7 @@ int set_var_password::check(THD *thd)
if (*thd->security_ctx->priv_host != 0)
{
user->host.str= (char *) thd->security_ctx->priv_host;
- user->host.length= (uint) strlen(thd->security_ctx->priv_host);
+ user->host.length= strlen(thd->security_ctx->priv_host);
}
else
{
@@ -3495,7 +3574,7 @@ int set_var_password::check(THD *thd)
}
/* Returns 1 as the function sends error to client */
return check_change_password(thd, user->host.str, user->user.str,
- password, (uint) strlen(password)) ? 1 : 0;
+ password, strlen(password)) ? 1 : 0;
#else
return 0;
#endif
@@ -3524,13 +3603,17 @@ bool sys_var_thd_storage_engine::check(THD *thd, set_var *var)
const char *value;
String str(buff, sizeof(buff), &my_charset_latin1), *res;
+ var->save_result.plugin= NULL;
if (var->value->result_type() == STRING_RESULT)
{
- enum db_type db_type;
+ LEX_STRING engine_name;
+ handlerton *hton;
if (!(res=var->value->val_str(&str)) ||
- !(var->save_result.ulong_value=
- (ulong) (db_type= ha_resolve_by_name(res->ptr(), res->length()))) ||
- ha_checktype(thd, db_type, 1, 0) != db_type)
+ !(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;
@@ -3545,41 +3628,61 @@ err:
}
-byte *sys_var_thd_storage_engine::value_ptr(THD *thd, enum_var_type type,
+uchar *sys_var_thd_storage_engine::value_ptr(THD *thd, enum_var_type type,
LEX_STRING *base)
{
- ulong val;
- val= ((type == OPT_GLOBAL) ? global_system_variables.*offset :
- thd->variables.*offset);
- const char *table_type= ha_get_storage_engine((enum db_type)val);
- return (byte *) table_type;
+ 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= &hton2plugin[hton->slot]->name;
+ 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)
- global_system_variables.*offset= (ulong) DB_TYPE_MYISAM;
+ {
+ value= &(global_system_variables.*offset);
+ new_value= ha_lock_engine(NULL, myisam_hton);
+ }
else
- thd->variables.*offset= (ulong) (global_system_variables.*offset);
+ {
+ 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)
{
- if (var->type == OPT_GLOBAL)
- global_system_variables.*offset= var->save_result.ulong_value;
- else
- thd->variables.*offset= var->save_result.ulong_value;
+ 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)
{
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
- ER_WARN_DEPRECATED_SYNTAX,
- ER(ER_WARN_DEPRECATED_SYNTAX), "table_type",
- "storage_engine");
+ WARN_DEPRECATED(thd, "5.2", "@@table_type", "'@@storage_engine'");
}
void sys_var_thd_table_type::set_default(THD *thd, enum_var_type type)
@@ -3599,26 +3702,26 @@ bool sys_var_thd_table_type::update(THD *thd, set_var *var)
Functions to handle sql_mode
****************************************************************************/
-/*
- Make string representation of mode
+/**
+ Make string representation of mode.
- SYNOPSIS
- thd in thread handler
- val in sql_mode value
- len out pointer on length of string
+ @param[in] thd thread handler
+ @param[in] val sql_mode value
+ @param[out] len pointer on length of string
- RETURN
+ @return
pointer to string with sql_mode representation
*/
-byte *sys_var_thd_sql_mode::symbolic_mode_representation(THD *thd, ulong val,
- ulong *len)
+bool
+sys_var_thd_sql_mode::
+symbolic_mode_representation(THD *thd, ulonglong val, LEX_STRING *rep)
{
- char buff[256];
+ char buff[STRING_BUFFER_USUAL_SIZE*8];
String tmp(buff, sizeof(buff), &my_charset_latin1);
- ulong length;
tmp.length(0);
+
for (uint i= 0; val; val>>= 1, i++)
{
if (val & 1)
@@ -3629,20 +3732,25 @@ byte *sys_var_thd_sql_mode::symbolic_mode_representation(THD *thd, ulong val,
}
}
- if ((length= tmp.length()))
- length--;
- *len= length;
- return (byte*) thd->strmake(tmp.ptr(), length);
+ 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();
}
-byte *sys_var_thd_sql_mode::value_ptr(THD *thd, enum_var_type type,
+uchar *sys_var_thd_sql_mode::value_ptr(THD *thd, enum_var_type type,
LEX_STRING *base)
{
- ulong val= ((type == OPT_GLOBAL) ? global_system_variables.*offset :
- thd->variables.*offset);
- ulong length_unused;
- return symbolic_mode_representation(thd, val, &length_unused);
+ LEX_STRING sql_mode;
+ ulonglong val= ((type == OPT_GLOBAL) ? global_system_variables.*offset :
+ thd->variables.*offset);
+ (void) symbolic_mode_representation(thd, val, &sql_mode);
+ return (uchar *) sql_mode.str;
}
@@ -3673,12 +3781,12 @@ void fix_sql_mode_var(THD *thd, enum_var_type type)
}
}
-/* Map database specific bits to function bits */
+/** Map database specific bits to function bits. */
ulong fix_sql_mode(ulong sql_mode)
{
/*
- Note that we dont set
+ 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.
*/
@@ -3687,7 +3795,7 @@ ulong fix_sql_mode(ulong sql_mode)
{
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).
*/
@@ -3733,7 +3841,7 @@ ulong fix_sql_mode(ulong sql_mode)
Named list handling
****************************************************************************/
-gptr find_named(I_List<NAMED_LIST> *list, const char *name, uint length,
+uchar* find_named(I_List<NAMED_LIST> *list, const char *name, uint length,
NAMED_LIST **found)
{
I_List_iterator<NAMED_LIST> it(*list);
@@ -3752,7 +3860,7 @@ gptr find_named(I_List<NAMED_LIST> *list, const char *name, uint length,
void delete_elements(I_List<NAMED_LIST> *list,
- void (*free_element)(const char *name, gptr))
+ void (*free_element)(const char *name, uchar*))
{
NAMED_LIST *element;
DBUG_ENTER("delete_elements");
@@ -3772,11 +3880,11 @@ 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, (gptr) key_cache))
+ if (!new NAMED_LIST(&key_caches, name, length, (uchar*) key_cache))
{
my_free((char*) key_cache, MYF(0));
key_cache= 0;
@@ -3820,7 +3928,7 @@ void free_key_cache(const char *name, KEY_CACHE *key_cache)
}
-bool process_key_caches(int (* func) (const char *name, KEY_CACHE *))
+bool process_key_caches(process_key_cache_t func)
{
I_List_iterator<NAMED_LIST> it(key_caches);
NAMED_LIST *element;
@@ -3836,10 +3944,8 @@ bool process_key_caches(int (* func) (const char *name, KEY_CACHE *))
void sys_var_trust_routine_creators::warn_deprecated(THD *thd)
{
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
- ER_WARN_DEPRECATED_SYNTAX,
- ER(ER_WARN_DEPRECATED_SYNTAX), "log_bin_trust_routine_creators",
- "log_bin_trust_function_creators");
+ WARN_DEPRECATED(thd, "5.2", "@@log_bin_trust_routine_creators",
+ "'@@log_bin_trust_function_creators'");
}
void sys_var_trust_routine_creators::set_default(THD *thd, enum_var_type type)
@@ -3854,6 +3960,143 @@ bool sys_var_trust_routine_creators::update(THD *thd, set_var *var)
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(thd, NULL, FALSE, TRUE, TRUE))
+ 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);
+}
+
+
+/* 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)
+{
+ if (var->type == OPT_GLOBAL)
+ DBUG_SET_INITIAL(var ? var->value->str_value.c_ptr() : "");
+ else
+ DBUG_SET(var ? var->value->str_value.c_ptr() : "");
+
+ return 0;
+}
+
+
+uchar *sys_var_thd_dbug::value_ptr(THD *thd, enum_var_type type, LEX_STRING *b)
+{
+ char buf[256];
+ if (type == OPT_GLOBAL)
+ DBUG_EXPLAIN_INITIAL(buf, sizeof(buf));
+ else
+ DBUG_EXPLAIN(buf, sizeof(buf));
+ return (uchar*) thd->strdup(buf);
+}
+
+#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
+
/****************************************************************************
Used templates
****************************************************************************/
diff --git a/sql/set_var.h b/sql/set_var.h
index f43d3b75cee..b6c67d1ab4a 100644
--- a/sql/set_var.h
+++ b/sql/set_var.h
@@ -26,24 +26,49 @@
class sys_var;
class set_var;
+class sys_var_pluginvar; /* opaque */
typedef struct system_variables SV;
typedef struct my_locale_st MY_LOCALE;
-extern TYPELIB bool_typelib, delay_key_write_typelib, sql_mode_typelib;
+extern TYPELIB bool_typelib, delay_key_write_typelib, sql_mode_typelib,
+ slave_exec_mode_typelib;
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 byte *(*sys_value_ptr_func)(THD *thd);
+typedef uchar *(*sys_value_ptr_func)(THD *thd);
+
+struct sys_var_chain
+{
+ sys_var *first;
+ sys_var *last;
+};
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;
/*
@@ -54,18 +79,34 @@ public:
later.
*/
bool is_os_charset;
- sys_var(const char *name_arg, sys_after_update_func func= NULL)
- :name(name_arg), after_update(func)
- , no_support_one_shot(1), is_os_charset(FALSE)
+ 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, TYPELIB *enum_names);
+ bool check_enum(THD *thd, set_var *var, const TYPELIB *enum_names);
bool check_set(THD *thd, set_var *var, TYPELIB *enum_names);
+ bool is_written_to_binlog(enum_var_type type)
+ {
+ return (type == OPT_SESSION || type == OPT_DEFAULT) &&
+ (binlog_status == SESSION_VARIABLE_IN_BINLOG);
+ }
virtual bool update(THD *thd, set_var *var)=0;
virtual void set_default(THD *thd_arg, enum_var_type type) {}
virtual SHOW_TYPE show_type() { return SHOW_UNDEF; }
- virtual byte *value_ptr(THD *thd, enum_var_type type, LEX_STRING *base)
+ 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 */
@@ -73,10 +114,21 @@ public:
{ return type != INT_RESULT; } /* Assume INT */
virtual bool check_default(enum_var_type type)
{ return option_limits == 0; }
- Item *item(THD *thd, enum_var_type type, LEX_STRING *base);
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;
};
@@ -105,18 +157,19 @@ class sys_var_long_ptr_global: public sys_var_global
{
public:
ulong *value;
- sys_var_long_ptr_global(const char *name_arg, ulong *value_ptr_arg,
- pthread_mutex_t *guard_arg,
- sys_after_update_func after_update_arg= NULL)
+ 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; }
- byte *value_ptr(THD *thd, enum_var_type type, LEX_STRING *base)
- { return (byte*) value; }
+ uchar *value_ptr(THD *thd, enum_var_type type, LEX_STRING *base)
+ { return (uchar*) value; }
};
@@ -127,7 +180,7 @@ public:
class sys_var_long_ptr :public sys_var_long_ptr_global
{
public:
- sys_var_long_ptr(const char *name_arg, ulong *value_ptr,
+ sys_var_long_ptr(sys_var_chain *chain, const char *name_arg, ulong *value_ptr,
sys_after_update_func after_update_arg= NULL);
};
@@ -136,16 +189,18 @@ class sys_var_ulonglong_ptr :public sys_var
{
public:
ulonglong *value;
- sys_var_ulonglong_ptr(const char *name_arg, ulonglong *value_ptr_arg)
- :sys_var(name_arg),value(value_ptr_arg) {}
- sys_var_ulonglong_ptr(const char *name_arg, ulonglong *value_ptr_arg,
+ 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) {}
+ :sys_var(name_arg,func), value(value_ptr_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; }
- byte *value_ptr(THD *thd, enum_var_type type, LEX_STRING *base)
- { return (byte*) value; }
+ uchar *value_ptr(THD *thd, enum_var_type type, LEX_STRING *base)
+ { return (uchar*) value; }
};
@@ -153,9 +208,9 @@ class sys_var_bool_ptr :public sys_var
{
public:
my_bool *value;
- sys_var_bool_ptr(const char *name_arg, my_bool *value_arg)
+ 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);
@@ -163,34 +218,23 @@ public:
bool update(THD *thd, set_var *var);
void set_default(THD *thd, enum_var_type type);
SHOW_TYPE show_type() { return SHOW_MY_BOOL; }
- byte *value_ptr(THD *thd, enum_var_type type, LEX_STRING *base)
- { return (byte*) value; }
+ 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_const_ptr : public sys_var
+class sys_var_bool_ptr_readonly :public sys_var_bool_ptr
{
public:
- my_bool *value;
- sys_var_bool_const_ptr(const char *name_arg, my_bool *value_arg)
- :sys_var(name_arg),value(value_arg)
+ 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 check(THD *thd, set_var *var)
- {
- return 1;
- }
- bool update(THD *thd, set_var *var)
- {
- return 1;
- }
- SHOW_TYPE show_type() { return SHOW_MY_BOOL; }
- byte *value_ptr(THD *thd, enum_var_type type, LEX_STRING *base)
- { return (byte*) value; }
- bool check_update_type(Item_result type) { return 0; }
bool is_readonly() const { return 1; }
};
+
class sys_var_str :public sys_var
{
public:
@@ -199,14 +243,14 @@ public:
sys_check_func check_func;
sys_update_func update_func;
sys_set_default_func set_default_func;
- sys_var_str(const char *name_arg,
+ 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)
{
@@ -217,8 +261,8 @@ public:
(*set_default_func)(thd, type);
}
SHOW_TYPE show_type() { return SHOW_CHAR; }
- byte *value_ptr(THD *thd, enum_var_type type, LEX_STRING *base)
- { return (byte*) value; }
+ 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 */
@@ -231,9 +275,10 @@ class sys_var_const_str :public sys_var
{
public:
char *value; // Pointer to const value
- sys_var_const_str(const char *name_arg, const char *value_arg)
- :sys_var(name_arg),value((char*) value_arg)
- {}
+ sys_var_const_str(sys_var_chain *chain, const char *name_arg,
+ const char *value_arg)
+ :sys_var(name_arg), value((char*) value_arg)
+ { chain_sys_var(chain); }
bool check(THD *thd, set_var *var)
{
return 1;
@@ -243,9 +288,9 @@ public:
return 1;
}
SHOW_TYPE show_type() { return SHOW_CHAR; }
- byte *value_ptr(THD *thd, enum_var_type type, LEX_STRING *base)
+ uchar *value_ptr(THD *thd, enum_var_type type, LEX_STRING *base)
{
- return (byte*) value;
+ return (uchar*) value;
}
bool check_update_type(Item_result type)
{
@@ -259,8 +304,9 @@ public:
class sys_var_const_os_str: public sys_var_const_str
{
public:
- sys_var_const_os_str(const char *name_arg, const char *value_arg)
- :sys_var_const_str(name_arg, value_arg)
+ 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;
}
@@ -271,9 +317,9 @@ class sys_var_const_str_ptr :public sys_var
{
public:
char **value; // Pointer to const value
- sys_var_const_str_ptr(const char *name_arg, char **value_arg)
+ 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;
@@ -283,9 +329,9 @@ public:
return 1;
}
SHOW_TYPE show_type() { return SHOW_CHAR; }
- byte *value_ptr(THD *thd, enum_var_type type, LEX_STRING *base)
+ uchar *value_ptr(THD *thd, enum_var_type type, LEX_STRING *base)
{
- return (byte*) *value;
+ return (uchar*) *value;
}
bool check_update_type(Item_result type)
{
@@ -299,8 +345,9 @@ public:
class sys_var_const_os_str_ptr :public sys_var_const_str_ptr
{
public:
- sys_var_const_os_str_ptr(const char *name_arg, char **value_arg)
- :sys_var_const_str_ptr(name_arg, value_arg)
+ 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;
}
@@ -309,29 +356,49 @@ public:
class sys_var_enum :public sys_var
{
- uint *value;
+ uint *value;
TYPELIB *enum_names;
public:
- sys_var_enum(const char *name_arg, uint *value_arg,
+ 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; }
- byte *value_ptr(THD *thd, enum_var_type type, LEX_STRING *base);
+ 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
+{
+ ulong SV::*offset;
+ TYPELIB *enum_names;
+public:
+ sys_var_enum_const(sys_var_chain *chain, const char *name_arg, ulong SV::*offset_arg,
+ TYPELIB *typelib, sys_after_update_func func)
+ :sys_var(name_arg,func), offset(offset_arg), enum_names(typelib)
+ { 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; }
+ bool check_update_type(Item_result type) { return 1; }
+ bool is_readonly() const { return 1; }
+ uchar *value_ptr(THD *thd, enum_var_type type, LEX_STRING *base);
+};
+
+
class sys_var_thd :public sys_var
{
public:
- sys_var_thd(const char *name_arg, sys_after_update_func func= NULL)
- :sys_var(name_arg,func)
+ sys_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)
@@ -346,18 +413,19 @@ class sys_var_thd_ulong :public sys_var_thd
sys_check_func check_func;
public:
ulong SV::*offset;
- sys_var_thd_ulong(const char *name_arg, ulong SV::*offset_arg)
- :sys_var_thd(name_arg), check_func(0), offset(offset_arg)
- {}
- sys_var_thd_ulong(const char *name_arg, ulong SV::*offset_arg,
- sys_check_func c_func, sys_after_update_func au_func)
- :sys_var_thd(name_arg,au_func), check_func(c_func), offset(offset_arg)
- {}
+ sys_var_thd_ulong(sys_var_chain *chain, const char *name_arg,
+ ulong SV::*offset_arg,
+ sys_check_func c_func= NULL,
+ sys_after_update_func au_func= NULL,
+ Binlog_status_enum binlog_status_arg= NOT_IN_BINLOG)
+ :sys_var_thd(name_arg, au_func, binlog_status_arg), check_func(c_func),
+ offset(offset_arg)
+ { chain_sys_var(chain); }
bool check(THD *thd, set_var *var);
bool update(THD *thd, set_var *var);
void set_default(THD *thd, enum_var_type type);
SHOW_TYPE show_type() { return SHOW_LONG; }
- byte *value_ptr(THD *thd, enum_var_type type, LEX_STRING *base);
+ uchar *value_ptr(THD *thd, enum_var_type type, LEX_STRING *base);
};
@@ -365,17 +433,19 @@ class sys_var_thd_ha_rows :public sys_var_thd
{
public:
ha_rows SV::*offset;
- sys_var_thd_ha_rows(const char *name_arg, ha_rows SV::*offset_arg)
+ 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)
- {}
- sys_var_thd_ha_rows(const char *name_arg, ha_rows SV::*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 update(THD *thd, set_var *var);
void set_default(THD *thd, enum_var_type type);
SHOW_TYPE show_type() { return SHOW_HA_ROWS; }
- byte *value_ptr(THD *thd, enum_var_type type, LEX_STRING *base);
+ uchar *value_ptr(THD *thd, enum_var_type type, LEX_STRING *base);
};
@@ -384,18 +454,20 @@ class sys_var_thd_ulonglong :public sys_var_thd
public:
ulonglong SV::*offset;
bool only_global;
- sys_var_thd_ulonglong(const char *name_arg, ulonglong SV::*offset_arg)
+ sys_var_thd_ulonglong(sys_var_chain *chain, const char *name_arg,
+ ulonglong SV::*offset_arg)
:sys_var_thd(name_arg), offset(offset_arg)
- {}
- sys_var_thd_ulonglong(const char *name_arg, ulonglong SV::*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; }
- byte *value_ptr(THD *thd, enum_var_type type, LEX_STRING *base);
+ 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)
{
@@ -412,17 +484,17 @@ class sys_var_thd_bool :public sys_var_thd
{
public:
my_bool SV::*offset;
- sys_var_thd_bool(const char *name_arg, my_bool SV::*offset_arg)
+ 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)
- {}
- sys_var_thd_bool(const char *name_arg, my_bool SV::*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; }
- byte *value_ptr(THD *thd, enum_var_type type, LEX_STRING *base);
+ 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);
@@ -436,24 +508,26 @@ 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(const char *name_arg, ulong SV::*offset_arg,
- TYPELIB *typelib)
- :sys_var_thd(name_arg), offset(offset_arg), enum_names(typelib)
- {}
- sys_var_thd_enum(const char *name_arg, ulong SV::*offset_arg,
- TYPELIB *typelib,
- sys_after_update_func func)
- :sys_var_thd(name_arg,func), offset(offset_arg), enum_names(typelib)
- {}
+ 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)
{
- return check_enum(thd, var, enum_names);
+ int ret= 0;
+ if (check_func)
+ ret= (*check_func)(thd, var);
+ return ret ? ret : check_enum(thd, var, enum_names);
}
bool update(THD *thd, set_var *var);
void set_default(THD *thd, enum_var_type type);
SHOW_TYPE show_type() { return SHOW_CHAR; }
- byte *value_ptr(THD *thd, enum_var_type type, LEX_STRING *base);
+ uchar *value_ptr(THD *thd, enum_var_type type, LEX_STRING *base);
bool check_update_type(Item_result type) { return 0; }
};
@@ -463,29 +537,31 @@ extern void fix_sql_mode_var(THD *thd, enum_var_type type);
class sys_var_thd_sql_mode :public sys_var_thd_enum
{
public:
- sys_var_thd_sql_mode(const char *name_arg, ulong SV::*offset_arg)
- :sys_var_thd_enum(name_arg, offset_arg, &sql_mode_typelib,
- fix_sql_mode_var)
+ sys_var_thd_sql_mode(sys_var_chain *chain, const char *name_arg,
+ ulong SV::*offset_arg)
+ :sys_var_thd_enum(chain, name_arg, offset_arg, &sql_mode_typelib,
+ fix_sql_mode_var)
{}
bool check(THD *thd, set_var *var)
{
return check_set(thd, var, enum_names);
}
void set_default(THD *thd, enum_var_type type);
- byte *value_ptr(THD *thd, enum_var_type type, LEX_STRING *base);
- static byte *symbolic_mode_representation(THD *thd, ulong sql_mode,
- ulong *length);
+ 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);
};
class sys_var_thd_storage_engine :public sys_var_thd
{
protected:
- ulong SV::*offset;
+ plugin_ref SV::*offset;
public:
- sys_var_thd_storage_engine(const char *name_arg, ulong SV::*offset_arg)
+ 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)
@@ -494,14 +570,15 @@ public:
}
void set_default(THD *thd, enum_var_type type);
bool update(THD *thd, set_var *var);
- byte *value_ptr(THD *thd, enum_var_type type, LEX_STRING *base);
+ 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(const char *name_arg, ulong SV::*offset_arg)
- :sys_var_thd_storage_engine(name_arg, offset_arg)
+ 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);
@@ -515,85 +592,89 @@ class sys_var_thd_bit :public sys_var_thd
public:
ulonglong bit_flag;
bool reverse;
- sys_var_thd_bit(const char *name_arg,
+ sys_var_thd_bit(sys_var_chain *chain, const char *name_arg,
sys_check_func c_func, sys_update_func u_func,
- ulonglong bit, bool reverse_arg=0)
- :sys_var_thd(name_arg), check_func(c_func), update_func(u_func),
- bit_flag(bit), reverse(reverse_arg)
- {}
+ ulonglong bit, bool reverse_arg=0,
+ Binlog_status_enum binlog_status_arg= NOT_IN_BINLOG)
+ :sys_var_thd(name_arg, NULL, binlog_status_arg), check_func(c_func),
+ update_func(u_func), bit_flag(bit), reverse(reverse_arg)
+ { chain_sys_var(chain); }
bool check(THD *thd, set_var *var);
bool update(THD *thd, set_var *var);
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; }
- byte *value_ptr(THD *thd, enum_var_type type, LEX_STRING *base);
+ uchar *value_ptr(THD *thd, enum_var_type type, LEX_STRING *base);
};
+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);
+};
+
+
/* some variables that require special handling */
class sys_var_timestamp :public sys_var
{
public:
- sys_var_timestamp(const char *name_arg) :sys_var(name_arg) {}
+ sys_var_timestamp(sys_var_chain *chain, const char *name_arg,
+ Binlog_status_enum binlog_status_arg= NOT_IN_BINLOG)
+ :sys_var(name_arg, NULL, binlog_status_arg)
+ { chain_sys_var(chain); }
bool update(THD *thd, set_var *var);
void set_default(THD *thd, enum_var_type type);
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_LONG; }
- byte *value_ptr(THD *thd, enum_var_type type, LEX_STRING *base);
+ uchar *value_ptr(THD *thd, enum_var_type type, LEX_STRING *base);
};
class sys_var_last_insert_id :public sys_var
{
public:
- sys_var_last_insert_id(const char *name_arg) :sys_var(name_arg) {}
+ sys_var_last_insert_id(sys_var_chain *chain, const char *name_arg,
+ Binlog_status_enum binlog_status_arg= NOT_IN_BINLOG)
+ :sys_var(name_arg, NULL, binlog_status_arg)
+ { chain_sys_var(chain); }
bool update(THD *thd, set_var *var);
bool check_type(enum_var_type type) { return type == OPT_GLOBAL; }
SHOW_TYPE show_type() { return SHOW_LONGLONG; }
- byte *value_ptr(THD *thd, enum_var_type type, LEX_STRING *base);
+ 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(const char *name_arg) :sys_var(name_arg) {}
+ 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; }
- byte *value_ptr(THD *thd, enum_var_type type, LEX_STRING *base);
-};
-
-
-#ifdef HAVE_REPLICATION
-class sys_var_slave_skip_counter :public sys_var
-{
-public:
- sys_var_slave_skip_counter(const char *name_arg) :sys_var(name_arg) {}
- 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()
- */
+ uchar *value_ptr(THD *thd, enum_var_type type, LEX_STRING *base);
};
-class sys_var_sync_binlog_period :public sys_var_long_ptr
-{
-public:
- sys_var_sync_binlog_period(const char *name_arg, ulong *value_ptr_arg)
- :sys_var_long_ptr(name_arg,value_ptr_arg) {}
- bool update(THD *thd, set_var *var);
-};
-#endif
class sys_var_rand_seed1 :public sys_var
{
public:
- sys_var_rand_seed1(const char *name_arg) :sys_var(name_arg) {}
+ sys_var_rand_seed1(sys_var_chain *chain, const char *name_arg,
+ Binlog_status_enum binlog_status_arg= NOT_IN_BINLOG)
+ :sys_var(name_arg, NULL, binlog_status_arg)
+ { chain_sys_var(chain); }
bool update(THD *thd, set_var *var);
bool check_type(enum_var_type type) { return type == OPT_GLOBAL; }
};
@@ -601,7 +682,10 @@ public:
class sys_var_rand_seed2 :public sys_var
{
public:
- sys_var_rand_seed2(const char *name_arg) :sys_var(name_arg) {}
+ sys_var_rand_seed2(sys_var_chain *chain, const char *name_arg,
+ Binlog_status_enum binlog_status_arg= NOT_IN_BINLOG)
+ :sys_var(name_arg, NULL, binlog_status_arg)
+ { chain_sys_var(chain); }
bool update(THD *thd, set_var *var);
bool check_type(enum_var_type type) { return type == OPT_GLOBAL; }
};
@@ -610,10 +694,12 @@ public:
class sys_var_collation :public sys_var_thd
{
public:
- sys_var_collation(const char *name_arg) :sys_var_thd(name_arg)
- {
+ sys_var_collation(const char *name_arg,
+ Binlog_status_enum binlog_status_arg= NOT_IN_BINLOG)
+ :sys_var_thd(name_arg, NULL, binlog_status_arg)
+ {
no_support_one_shot= 0;
- }
+ }
bool check(THD *thd, set_var *var);
SHOW_TYPE show_type() { return SHOW_CHAR; }
bool check_update_type(Item_result type)
@@ -628,10 +714,10 @@ class sys_var_character_set :public sys_var_thd
{
public:
bool nullable;
- sys_var_character_set(const char *name_arg) :
- sys_var_thd(name_arg)
+ 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)
{
- nullable= 0;
/*
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.
@@ -646,92 +732,72 @@ public:
}
bool check_default(enum_var_type type) { return 0; }
bool update(THD *thd, set_var *var);
- byte *value_ptr(THD *thd, enum_var_type type, LEX_STRING *base);
+ 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_filesystem :public sys_var_character_set
+class sys_var_character_set_sv :public sys_var_character_set
{
+ CHARSET_INFO *SV::*offset;
+ CHARSET_INFO **global_default;
public:
- sys_var_character_set_filesystem(const char *name_arg) :
- sys_var_character_set(name_arg) {}
+ 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
-{
-public:
- sys_var_character_set_client(const char *name_arg) :
- sys_var_character_set(name_arg) {}
- void set_default(THD *thd, enum_var_type type);
- CHARSET_INFO **ci_ptr(THD *thd, enum_var_type type);
- bool check(THD *thd, set_var *var);
-};
-class sys_var_character_set_results :public sys_var_character_set
+class sys_var_character_set_client: public sys_var_character_set_sv
{
public:
- sys_var_character_set_results(const char *name_arg) :
- sys_var_character_set(name_arg)
- { nullable= 1; }
- void set_default(THD *thd, enum_var_type type);
- CHARSET_INFO **ci_ptr(THD *thd, enum_var_type type);
+ 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_server :public sys_var_character_set
-{
-public:
- sys_var_character_set_server(const char *name_arg) :
- sys_var_character_set(name_arg) {}
- void set_default(THD *thd, enum_var_type type);
- CHARSET_INFO **ci_ptr(THD *thd, enum_var_type type);
-};
class sys_var_character_set_database :public sys_var_character_set
{
public:
- sys_var_character_set_database(const char *name_arg) :
- sys_var_character_set(name_arg) {}
+ sys_var_character_set_database(sys_var_chain *chain, const char *name_arg,
+ Binlog_status_enum binlog_status_arg=
+ NOT_IN_BINLOG)
+ : sys_var_character_set(name_arg, 0, binlog_status_arg)
+ { chain_sys_var(chain); }
void set_default(THD *thd, enum_var_type type);
CHARSET_INFO **ci_ptr(THD *thd, enum_var_type type);
};
-class sys_var_character_set_connection :public sys_var_character_set
-{
-public:
- sys_var_character_set_connection(const char *name_arg) :
- sys_var_character_set(name_arg) {}
- void set_default(THD *thd, enum_var_type type);
- CHARSET_INFO **ci_ptr(THD *thd, enum_var_type type);
-};
-
-class sys_var_collation_connection :public sys_var_collation
-{
-public:
- sys_var_collation_connection(const char *name_arg) :sys_var_collation(name_arg) {}
- bool update(THD *thd, set_var *var);
- void set_default(THD *thd, enum_var_type type);
- byte *value_ptr(THD *thd, enum_var_type type, LEX_STRING *base);
-};
-
-class sys_var_collation_server :public sys_var_collation
+class sys_var_collation_sv :public sys_var_collation
{
+ CHARSET_INFO *SV::*offset;
+ CHARSET_INFO **global_default;
public:
- sys_var_collation_server(const char *name_arg) :sys_var_collation(name_arg) {}
- bool update(THD *thd, set_var *var);
- void set_default(THD *thd, enum_var_type type);
- byte *value_ptr(THD *thd, enum_var_type type, LEX_STRING *base);
-};
-
-class sys_var_collation_database :public sys_var_collation
-{
-public:
- sys_var_collation_database(const char *name_arg) :sys_var_collation(name_arg) {}
+ 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);
- byte *value_ptr(THD *thd, enum_var_type type, LEX_STRING *base);
+ uchar *value_ptr(THD *thd, enum_var_type type, LEX_STRING *base);
};
@@ -740,10 +806,11 @@ class sys_var_key_cache_param :public sys_var
protected:
size_t offset;
public:
- sys_var_key_cache_param(const char *name_arg, size_t offset_arg)
+ sys_var_key_cache_param(sys_var_chain *chain, const char *name_arg,
+ size_t offset_arg)
:sys_var(name_arg), offset(offset_arg)
- {}
- byte *value_ptr(THD *thd, enum_var_type type, LEX_STRING *base);
+ { 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; }
};
@@ -752,8 +819,9 @@ public:
class sys_var_key_buffer_size :public sys_var_key_cache_param
{
public:
- sys_var_key_buffer_size(const char *name_arg)
- :sys_var_key_cache_param(name_arg, offsetof(KEY_CACHE, param_buff_size))
+ 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 update(THD *thd, set_var *var);
SHOW_TYPE show_type() { return SHOW_LONGLONG; }
@@ -763,8 +831,8 @@ public:
class sys_var_key_cache_long :public sys_var_key_cache_param
{
public:
- sys_var_key_cache_long(const char *name_arg, size_t offset_arg)
- :sys_var_key_cache_param(name_arg, offset_arg)
+ 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 update(THD *thd, set_var *var);
SHOW_TYPE show_type() { return SHOW_LONG; }
@@ -776,12 +844,12 @@ 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(const char *name_arg,
+ 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)
{
@@ -791,11 +859,83 @@ public:
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);
- byte *value_ptr(THD *thd, enum_var_type type, LEX_STRING *base);
+ 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
@@ -804,17 +944,17 @@ public:
enum_var_type var_type;
SHOW_TYPE show_type_value;
sys_value_ptr_func value_ptr_func;
- sys_var_readonly(const char *name_arg, enum_var_type type,
+ 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; }
- byte *value_ptr(THD *thd, enum_var_type type, LEX_STRING *base)
+ uchar *value_ptr(THD *thd, enum_var_type type, LEX_STRING *base)
{
return (*value_ptr_func)(thd);
}
@@ -826,23 +966,123 @@ public:
class sys_var_readonly_os: public sys_var_readonly
{
public:
- sys_var_readonly_os(const char *name_arg, enum_var_type type,
+ 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(name_arg, type, show_type_arg, 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;
+ }
+ 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)
+ {
+ is_os_charset= TRUE;
+ }
+};
+
+
+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(const char *name_arg):
- sys_var_thd(name_arg)
+ sys_var_thd_time_zone(sys_var_chain *chain, const char *name_arg,
+ Binlog_status_enum binlog_status_arg= NOT_IN_BINLOG)
+ :sys_var_thd(name_arg, NULL, binlog_status_arg)
{
no_support_one_shot= 0;
+ chain_sys_var(chain);
}
bool check(THD *thd, set_var *var);
SHOW_TYPE show_type() { return SHOW_CHAR; }
@@ -852,7 +1092,7 @@ public:
}
bool check_default(enum_var_type type) { return 0; }
bool update(THD *thd, set_var *var);
- byte *value_ptr(THD *thd, enum_var_type type, LEX_STRING *base);
+ uchar *value_ptr(THD *thd, enum_var_type type, LEX_STRING *base);
virtual void set_default(THD *thd, enum_var_type type);
};
@@ -860,8 +1100,9 @@ public:
class sys_var_max_user_conn : public sys_var_thd
{
public:
- sys_var_max_user_conn(const char *name_arg):
- sys_var_thd(name_arg) {}
+ 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)
@@ -870,30 +1111,92 @@ public:
}
void set_default(THD *thd, enum_var_type type);
SHOW_TYPE show_type() { return SHOW_INT; }
- byte *value_ptr(THD *thd, enum_var_type type, LEX_STRING *base);
+ 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(const char *name_arg, my_bool *value_arg) :
- sys_var_bool_ptr(name_arg, value_arg) {};
+ 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);
+};
+
+
class sys_var_thd_lc_time_names :public sys_var_thd
{
public:
- sys_var_thd_lc_time_names(const char *name_arg):
- sys_var_thd(name_arg)
+ sys_var_thd_lc_time_names(sys_var_chain *chain, const char *name_arg,
+ Binlog_status_enum binlog_status_arg= NOT_IN_BINLOG)
+ : sys_var_thd(name_arg, NULL, binlog_status_arg)
{
#if MYSQL_VERSION_ID < 50000
no_support_one_shot= 0;
#endif
+ chain_sys_var(chain);
}
bool check(THD *thd, set_var *var);
SHOW_TYPE show_type() { return SHOW_CHAR; }
@@ -903,10 +1206,43 @@ public:
}
bool check_default(enum_var_type type) { return 0; }
bool update(THD *thd, set_var *var);
- byte *value_ptr(THD *thd, enum_var_type type, LEX_STRING *base);
+ uchar *value_ptr(THD *thd, enum_var_type type, LEX_STRING *base);
virtual void set_default(THD *thd, enum_var_type type);
};
+#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;
+ }
+};
+#endif
+
+extern void fix_binlog_format_after_update(THD *thd, enum_var_type type);
+
+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
****************************************************************************/
@@ -937,6 +1273,7 @@ public:
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;
@@ -1018,6 +1355,11 @@ public:
};
+extern "C"
+{
+ typedef int (*process_key_cache_t) (const char *, KEY_CACHE *);
+}
+
/* Named lists (used for keycaches) */
class NAMED_LIST :public ilink
@@ -1025,13 +1367,13 @@ class NAMED_LIST :public ilink
const char *name;
uint name_length;
public:
- gptr data;
+ uchar* data;
NAMED_LIST(I_List<NAMED_LIST> *links, const char *name_arg,
- uint name_length_arg, gptr data_arg)
+ uint name_length_arg, uchar* data_arg)
:name_length(name_length_arg), data(data_arg)
{
- name= my_strdup_with_length(name_arg, name_length, MYF(MY_WME));
+ name= my_strndup(name_arg, name_length, MYF(MY_WME));
links->push_back(this);
}
inline bool cmp(const char *name_cmp, uint length)
@@ -1040,16 +1382,16 @@ public:
}
~NAMED_LIST()
{
- my_free((char*) name, MYF(0));
+ my_free((uchar*) name, MYF(0));
}
- friend bool process_key_caches(int (* func) (const char *name,
- KEY_CACHE *));
+ friend bool process_key_caches(process_key_cache_t func);
friend void delete_elements(I_List<NAMED_LIST> *list,
- void (*free_element)(const char*, gptr));
+ 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;
@@ -1064,12 +1406,16 @@ struct sys_var_with_base
Prototypes for helper functions
*/
-void set_var_init();
+int set_var_init();
void set_var_free();
-sys_var *find_sys_var(const char *str, uint length=0);
+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);
+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(enum_var_type type);
ulong fix_sql_mode(ulong sql_mode);
extern sys_var_const_str sys_charset_system;
extern sys_var_str sys_init_connect;
@@ -1077,13 +1423,15 @@ extern sys_var_str sys_init_slave;
extern sys_var_thd_time_zone sys_time_zone;
extern sys_var_thd_bit sys_autocommit;
CHARSET_INFO *get_old_charset_by_name(const char *old_name);
-gptr find_named(I_List<NAMED_LIST> *list, const char *name, uint length,
+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;
+
/* 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(int (* func) (const char *name, KEY_CACHE *));
+bool process_key_caches(process_key_cache_t func);
void delete_elements(I_List<NAMED_LIST> *list,
- void (*free_element)(const char*, gptr));
+ void (*free_element)(const char*, uchar*));
diff --git a/sql/share/charsets/Index.xml b/sql/share/charsets/Index.xml
index ae72daa8ca2..80b844e2f19 100644
--- a/sql/share/charsets/Index.xml
+++ b/sql/share/charsets/Index.xml
@@ -1,6 +1,6 @@
<?xml version='1.0' encoding="utf-8"?>
-<charsets max-id="98">
+<charsets max-id="99">
<copyright>
Copyright (C) 2003 MySQL AB
@@ -372,6 +372,9 @@ To make maintaining easier please:
<collation name="cp1250_croatian_ci" id="44">
<order>Croatian</order>
</collation>
+ <collation name="cp1250_polish_ci" id="99">
+ <order>Polish</order>
+ </collation>
<collation name="cp1250_czech_cs" id="34" order="Czech">
<flag>compiled</flag>
</collation>
diff --git a/sql/share/charsets/cp1250.xml b/sql/share/charsets/cp1250.xml
index 0bda643c910..bd0d7d3f3c0 100644
--- a/sql/share/charsets/cp1250.xml
+++ b/sql/share/charsets/cp1250.xml
@@ -152,6 +152,27 @@ BE BF C0 54 C1 C2 C3 C4 C5 41 5F C6 54 C7 54 6B
</map>
</collation>
+<collation name="cp1250_polish_ci">
+<map>
+00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
+10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F
+20 21 22 23 24 25 26 27 28 29 2A 2B 2C 2D 2E 2F
+30 31 32 33 34 35 36 37 38 39 3A 3B 3C 3D 3E 3F
+40 41 43 44 48 4B 4D 4E 4F 50 52 53 54 56 57 59
+5B 5C 5D 5F 62 64 66 67 68 69 6B 90 91 92 93 94
+95 41 43 44 48 4B 4D 4E 4F 50 52 53 54 56 57 59
+5B 5C 5D 5F 62 64 66 67 68 69 6B 96 97 98 99 9A
+9B 9C 9E 9F A0 A1 A2 A3 A4 A5 5F A6 60 62 6B 6C
+A7 A8 A9 AA AB AC AD AE AF B0 5F B1 60 62 6B 6C
+B2 B3 B4 55 B5 42 B6 B7 B8 B9 5F BA BB BC BD 6D
+BE BF C0 55 C1 C2 C3 C4 C5 42 5F C6 54 C7 54 6D
+5D 41 41 41 41 54 47 44 44 4B 4C 4B 4B 50 50 48
+48 58 57 5A 59 59 59 C8 5D 64 64 64 64 69 62 5F
+5D 41 41 41 41 54 47 44 44 4B 4C 4B 4B 50 50 48
+48 58 57 5A 59 59 59 C9 5D 64 64 64 64 69 62 FF
+</map>
+</collation>
+
<collation name="cp1250_czech_ci"/>
<collation name="cp1250_bin" flag="binary"/>
diff --git a/sql/share/errmsg.txt b/sql/share/errmsg.txt
index c688ba88b7b..befd064e3e3 100644
--- a/sql/share/errmsg.txt
+++ b/sql/share/errmsg.txt
@@ -5,5634 +5,5653 @@ default-language eng
start-error-number 1000
ER_HASHCHK
- eng "hashchk"
+ eng "hashchk"
ER_NISAMCHK
- eng "isamchk"
+ 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 "î¶"
+ 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 "ôáë"
+ 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)"
+ 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)"
+ 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 '%-.64s' (chybový kód: %d)"
- dan "Kan ikke oprette databasen '%-.64s' (Fejlkode: %d)"
- nla "Kan database '%-.64s' niet aanmaken (Errcode: %d)"
- eng "Can't create database '%-.64s' (errno: %d)"
- jps "'%-.64s' ƒf[ƒ^ƒx[ƒX‚ªì‚ê‚Ü‚¹‚ñ (errno: %d)",
- est "Ei suuda luua andmebaasi '%-.64s' (veakood: %d)"
- fre "Ne peut créer la base '%-.64s' (Erreur %d)"
- ger "Kann Datenbank '%-.64s' nicht erzeugen (Fehler: %d)"
- greek "Áäýíáôç ç äçìéïõñãßá ôçò âÜóçò äåäïìÝíùí '%-.64s' (êùäéêüò ëÜèïõò: %d)"
- hun "Az '%-.64s' adatbazis nem hozhato letre (hibakod: %d)"
- ita "Impossibile creare il database '%-.64s' (errno: %d)"
- jpn "'%-.64s' ¥Ç¡¼¥¿¥Ù¡¼¥¹¤¬ºî¤ì¤Þ¤»¤ó (errno: %d)"
- kor "µ¥ÀÌŸº£À̽º '%-.64s'¸¦ ¸¸µéÁö ¸øÇß½À´Ï´Ù.. (¿¡·¯¹øÈ£: %d)"
- nor "Kan ikke opprette databasen '%-.64s' (Feilkode: %d)"
- norwegian-ny "Kan ikkje opprette databasen '%-.64s' (Feilkode: %d)"
- pol "Nie mo¿na stworzyæ bazy danych '%-.64s' (Kod b³êdu: %d)"
- por "Não pode criar o banco de dados '%-.64s' (erro no. %d)"
- rum "Nu pot sa creez baza de date '%-.64s' (Eroare: %d)"
- rus "îÅ×ÏÚÍÏÖÎÏ ÓÏÚÄÁÔØ ÂÁÚÕ ÄÁÎÎÙÈ '%-.64s' (ÏÛÉÂËÁ: %d)"
- serbian "Ne mogu da kreiram bazu '%-.64s' (errno: %d)"
- slo "Nemô¾em vytvori» databázu '%-.64s' (chybový kód: %d)"
- spa "No puedo crear base de datos '%-.64s' (Error: %d)"
- swe "Kan inte skapa databasen '%-.64s' (Felkod: %d)"
- ukr "îÅ ÍÏÖÕ ÓÔ×ÏÒÉÔÉ ÂÁÚÕ ÄÁÎÎÉÈ '%-.64s' (ÐÏÍÉÌËÁ: %d)"
+ 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 '%-.64s'; databáze ji¾ existuje"
- dan "Kan ikke oprette databasen '%-.64s'; databasen eksisterer"
- nla "Kan database '%-.64s' niet aanmaken; database bestaat reeds"
- eng "Can't create database '%-.64s'; database exists"
- jps "'%-.64s' ƒf[ƒ^ƒx[ƒX‚ªì‚ê‚Ü‚¹‚ñ.Šù‚É‚»‚̃f[ƒ^ƒx[ƒX‚ª‘¶Ý‚µ‚Ü‚·",
- est "Ei suuda luua andmebaasi '%-.64s': andmebaas juba eksisteerib"
- fre "Ne peut créer la base '%-.64s'; elle existe déjà"
- ger "Kann Datenbank '%-.64s' nicht erzeugen. Datenbank existiert bereits"
- greek "Áäýíáôç ç äçìéïõñãßá ôçò âÜóçò äåäïìÝíùí '%-.64s'; Ç âÜóç äåäïìÝíùí õðÜñ÷åé Þäç"
- hun "Az '%-.64s' adatbazis nem hozhato letre Az adatbazis mar letezik"
- ita "Impossibile creare il database '%-.64s'; il database esiste"
- jpn "'%-.64s' ¥Ç¡¼¥¿¥Ù¡¼¥¹¤¬ºî¤ì¤Þ¤»¤ó.´û¤Ë¤½¤Î¥Ç¡¼¥¿¥Ù¡¼¥¹¤¬Â¸ºß¤·¤Þ¤¹"
- kor "µ¥ÀÌŸº£À̽º '%-.64s'¸¦ ¸¸µéÁö ¸øÇß½À´Ï´Ù.. µ¥ÀÌŸº£À̽º°¡ Á¸ÀçÇÔ"
- nor "Kan ikke opprette databasen '%-.64s'; databasen eksisterer"
- norwegian-ny "Kan ikkje opprette databasen '%-.64s'; databasen eksisterer"
- pol "Nie mo¿na stworzyæ bazy danych '%-.64s'; baza danych ju¿ istnieje"
- por "Não pode criar o banco de dados '%-.64s'; este banco de dados já existe"
- rum "Nu pot sa creez baza de date '%-.64s'; baza de date exista deja"
- rus "îÅ×ÏÚÍÏÖÎÏ ÓÏÚÄÁÔØ ÂÁÚÕ ÄÁÎÎÙÈ '%-.64s'. âÁÚÁ ÄÁÎÎÙÈ ÕÖÅ ÓÕÝÅÓÔ×ÕÅÔ"
- serbian "Ne mogu da kreiram bazu '%-.64s'; baza veæ postoji."
- slo "Nemô¾em vytvori» databázu '%-.64s'; databáza existuje"
- spa "No puedo crear base de datos '%-.64s'; la base de datos ya existe"
- swe "Databasen '%-.64s' existerar redan"
- ukr "îÅ ÍÏÖÕ ÓÔ×ÏÒÉÔÉ ÂÁÚÕ ÄÁÎÎÉÈ '%-.64s'. âÁÚÁ ÄÁÎÎÉÈ ¦ÓÎÕ¤"
+ 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 '%-.64s', databáze neexistuje"
- dan "Kan ikke slette (droppe) '%-.64s'; databasen eksisterer ikke"
- nla "Kan database '%-.64s' niet verwijderen; database bestaat niet"
- eng "Can't drop database '%-.64s'; database doesn't exist"
- jps "'%-.64s' ƒf[ƒ^ƒx[ƒX‚ð”jŠü‚Å‚«‚Ü‚¹‚ñ. ‚»‚̃f[ƒ^ƒx[ƒX‚ª‚È‚¢‚Ì‚Å‚·.",
- est "Ei suuda kustutada andmebaasi '%-.64s': andmebaasi ei eksisteeri"
- fre "Ne peut effacer la base '%-.64s'; elle n'existe pas"
- ger "Kann Datenbank '%-.64s' nicht löschen; Datenbank nicht vorhanden"
- greek "Áäýíáôç ç äéáãñáöÞ ôçò âÜóçò äåäïìÝíùí '%-.64s'. Ç âÜóç äåäïìÝíùí äåí õðÜñ÷åé"
- hun "A(z) '%-.64s' adatbazis nem szuntetheto meg. Az adatbazis nem letezik"
- ita "Impossibile cancellare '%-.64s'; il database non esiste"
- jpn "'%-.64s' ¥Ç¡¼¥¿¥Ù¡¼¥¹¤òÇË´þ¤Ç¤­¤Þ¤»¤ó. ¤½¤Î¥Ç¡¼¥¿¥Ù¡¼¥¹¤¬¤Ê¤¤¤Î¤Ç¤¹."
- kor "µ¥ÀÌŸº£À̽º '%-.64s'¸¦ Á¦°ÅÇÏÁö ¸øÇß½À´Ï´Ù. µ¥ÀÌŸº£À̽º°¡ Á¸ÀçÇÏÁö ¾ÊÀ½ "
- nor "Kan ikke fjerne (drop) '%-.64s'; databasen eksisterer ikke"
- norwegian-ny "Kan ikkje fjerne (drop) '%-.64s'; databasen eksisterer ikkje"
- pol "Nie mo¿na usun?æ bazy danych '%-.64s'; baza danych nie istnieje"
- por "Não pode eliminar o banco de dados '%-.64s'; este banco de dados não existe"
- rum "Nu pot sa drop baza de date '%-.64s'; baza da date este inexistenta"
- rus "îÅ×ÏÚÍÏÖÎÏ ÕÄÁÌÉÔØ ÂÁÚÕ ÄÁÎÎÙÈ '%-.64s'. ôÁËÏÊ ÂÁÚÙ ÄÁÎÎÙÈ ÎÅÔ"
- serbian "Ne mogu da izbrišem bazu '%-.64s'; baza ne postoji."
- slo "Nemô¾em zmaza» databázu '%-.64s'; databáza neexistuje"
- spa "No puedo eliminar base de datos '%-.64s'; la base de datos no existe"
- swe "Kan inte radera databasen '%-.64s'; databasen finns inte"
- ukr "îÅ ÍÏÖÕ ×ÉÄÁÌÉÔÉ ÂÁÚÕ ÄÁÎÎÉÈ '%-.64s'. âÁÚÁ ÄÁÎÎÉÈ ÎÅ ¦ÓÎÕ¤"
+ 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 '%-.64s', chyba %d)"
- dan "Fejl ved sletning (drop) af databasen (kan ikke slette '%-.64s', Fejlkode %d)"
- nla "Fout bij verwijderen database (kan '%-.64s' niet verwijderen, Errcode: %d)"
- eng "Error dropping database (can't delete '%-.64s', errno: %d)"
- jps "ƒf[ƒ^ƒx[ƒX”jŠüƒGƒ‰[ ('%-.64s' ‚ð휂ł«‚Ü‚¹‚ñ, errno: %d)",
- est "Viga andmebaasi kustutamisel (ei suuda kustutada faili '%-.64s', veakood: %d)"
- fre "Ne peut effacer la base '%-.64s' (erreur %d)"
- ger "Fehler beim Löschen der Datenbank ('%-.64s' kann nicht gelöscht werden, Fehler: %d)"
- greek "ÐáñïõóéÜóôçêå ðñüâëçìá êáôÜ ôç äéáãñáöÞ ôçò âÜóçò äåäïìÝíùí (áäýíáôç ç äéáãñáöÞ '%-.64s', êùäéêüò ëÜèïõò: %d)"
- hun "Adatbazis megszuntetesi hiba ('%-.64s' nem torolheto, hibakod: %d)"
- ita "Errore durante la cancellazione del database (impossibile cancellare '%-.64s', errno: %d)"
- jpn "¥Ç¡¼¥¿¥Ù¡¼¥¹ÇË´þ¥¨¥é¡¼ ('%-.64s' ¤òºï½ü¤Ç¤­¤Þ¤»¤ó, errno: %d)"
- kor "µ¥ÀÌŸº£À̽º Á¦°Å ¿¡·¯('%-.64s'¸¦ »èÁ¦ÇÒ ¼ö ¾øÀ¾´Ï´Ù, ¿¡·¯¹øÈ£: %d)"
- nor "Feil ved fjerning (drop) av databasen (kan ikke slette '%-.64s', feil %d)"
- norwegian-ny "Feil ved fjerning (drop) av databasen (kan ikkje slette '%-.64s', feil %d)"
- pol "B³?d podczas usuwania bazy danych (nie mo¿na usun?æ '%-.64s', b³?d %d)"
- por "Erro ao eliminar banco de dados (não pode eliminar '%-.64s' - erro no. %d)"
- rum "Eroare dropuind baza de date (nu pot sa sterg '%-.64s', Eroare: %d)"
- rus "ïÛÉÂËÁ ÐÒÉ ÕÄÁÌÅÎÉÉ ÂÁÚÙ ÄÁÎÎÙÈ (ÎÅ×ÏÚÍÏÖÎÏ ÕÄÁÌÉÔØ '%-.64s', ÏÛÉÂËÁ: %d)"
- serbian "Ne mogu da izbrišem bazu (ne mogu da izbrišem '%-.64s', errno: %d)"
- slo "Chyba pri mazaní databázy (nemô¾em zmaza» '%-.64s', chybový kód: %d)"
- spa "Error eliminando la base de datos(no puedo borrar '%-.64s', error %d)"
- swe "Fel vid radering av databasen (Kan inte radera '%-.64s'. Felkod: %d)"
- ukr "îÅ ÍÏÖÕ ×ÉÄÁÌÉÔÉ ÂÁÚÕ ÄÁÎÎÉÈ (îÅ ÍÏÖÕ ×ÉÄÁÌÉÔÉ '%-.64s', ÐÏÍÉÌËÁ: %d)"
+ 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áø '%-.64s', chyba %d)"
- dan "Fejl ved sletting af database (kan ikke slette folderen '%-.64s', Fejlkode %d)"
- nla "Fout bij verwijderen database (kan rmdir '%-.64s' niet uitvoeren, Errcode: %d)"
- eng "Error dropping database (can't rmdir '%-.64s', errno: %d)"
- jps "ƒf[ƒ^ƒx[ƒX”jŠüƒGƒ‰[ ('%-.64s' ‚ð rmdir ‚Å‚«‚Ü‚¹‚ñ, errno: %d)",
- est "Viga andmebaasi kustutamisel (ei suuda kustutada kataloogi '%-.64s', veakood: %d)"
- fre "Erreur en effaçant la base (rmdir '%-.64s', erreur %d)"
- ger "Fehler beim Löschen der Datenbank (Verzeichnis '%-.64s' kann nicht gelöscht werden, Fehler: %d)"
- greek "ÐáñïõóéÜóôçêå ðñüâëçìá êáôÜ ôç äéáãñáöÞ ôçò âÜóçò äåäïìÝíùí (áäýíáôç ç äéáãñáöÞ ôïõ öáêÝëëïõ '%-.64s', êùäéêüò ëÜèïõò: %d)"
- hun "Adatbazis megszuntetesi hiba ('%-.64s' nem szuntetheto meg, hibakod: %d)"
- ita "Errore durante la cancellazione del database (impossibile rmdir '%-.64s', errno: %d)"
- jpn "¥Ç¡¼¥¿¥Ù¡¼¥¹ÇË´þ¥¨¥é¡¼ ('%-.64s' ¤ò rmdir ¤Ç¤­¤Þ¤»¤ó, errno: %d)"
- kor "µ¥ÀÌŸº£À̽º Á¦°Å ¿¡·¯(rmdir '%-.64s'¸¦ ÇÒ ¼ö ¾øÀ¾´Ï´Ù, ¿¡·¯¹øÈ£: %d)"
- nor "Feil ved sletting av database (kan ikke slette katalogen '%-.64s', feil %d)"
- norwegian-ny "Feil ved sletting av database (kan ikkje slette katalogen '%-.64s', feil %d)"
- pol "B³?d podczas usuwania bazy danych (nie mo¿na wykonaæ rmdir '%-.64s', b³?d %d)"
- por "Erro ao eliminar banco de dados (não pode remover diretório '%-.64s' - erro no. %d)"
- rum "Eroare dropuind baza de date (nu pot sa rmdir '%-.64s', Eroare: %d)"
- rus "îÅ×ÏÚÍÏÖÎÏ ÕÄÁÌÉÔØ ÂÁÚÕ ÄÁÎÎÙÈ (ÎÅ×ÏÚÍÏÖÎÏ ÕÄÁÌÉÔØ ËÁÔÁÌÏÇ '%-.64s', ÏÛÉÂËÁ: %d)"
- serbian "Ne mogu da izbrišem bazu (ne mogu da izbrišem direktorijum '%-.64s', errno: %d)"
- slo "Chyba pri mazaní databázy (nemô¾em vymaza» adresár '%-.64s', chybový kód: %d)"
- spa "Error eliminando la base de datos (No puedo borrar directorio '%-.64s', error %d)"
- swe "Fel vid radering av databasen (Kan inte radera biblioteket '%-.64s'. Felkod: %d)"
- ukr "îÅ ÍÏÖÕ ×ÉÄÁÌÉÔÉ ÂÁÚÕ ÄÁÎÎÉÈ (îÅ ÍÏÖÕ ×ÉÄÁÌÉÔÉ ÔÅËÕ '%-.64s', ÐÏÍÉÌËÁ: %d)"
+ 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 '%-.64s' (chybový kód: %d)"
- dan "Fejl ved sletning af '%-.64s' (Fejlkode: %d)"
- nla "Fout bij het verwijderen van '%-.64s' (Errcode: %d)"
- eng "Error on delete of '%-.64s' (errno: %d)"
- jps "'%-.64s' ‚Ì휂ªƒGƒ‰[ (errno: %d)",
- est "Viga '%-.64s' kustutamisel (veakood: %d)"
- fre "Erreur en effaçant '%-.64s' (Errcode: %d)"
- ger "Fehler beim Löschen von '%-.64s' (Fehler: %d)"
- greek "ÐáñïõóéÜóôçêå ðñüâëçìá êáôÜ ôç äéáãñáöÞ '%-.64s' (êùäéêüò ëÜèïõò: %d)"
- hun "Torlesi hiba: '%-.64s' (hibakod: %d)"
- ita "Errore durante la cancellazione di '%-.64s' (errno: %d)"
- jpn "'%-.64s' ¤Îºï½ü¤¬¥¨¥é¡¼ (errno: %d)"
- kor "'%-.64s' »èÁ¦ Áß ¿¡·¯ (¿¡·¯¹øÈ£: %d)"
- nor "Feil ved sletting av '%-.64s' (Feilkode: %d)"
- norwegian-ny "Feil ved sletting av '%-.64s' (Feilkode: %d)"
- pol "B³?d podczas usuwania '%-.64s' (Kod b³êdu: %d)"
- por "Erro na remoção de '%-.64s' (erro no. %d)"
- rum "Eroare incercind sa delete '%-.64s' (Eroare: %d)"
- rus "ïÛÉÂËÁ ÐÒÉ ÕÄÁÌÅÎÉÉ '%-.64s' (ÏÛÉÂËÁ: %d)"
- serbian "Greška pri brisanju '%-.64s' (errno: %d)"
- slo "Chyba pri mazaní '%-.64s' (chybový kód: %d)"
- spa "Error en el borrado de '%-.64s' (Error: %d)"
- swe "Kan inte radera filen '%-.64s' (Felkod: %d)"
- ukr "îÅ ÍÏÖÕ ×ÉÄÁÌÉÔÉ '%-.64s' (ÐÏÍÉÌËÁ: %d)"
+ 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 "îÅ ÍÏÖÕ ÚÞÉÔÁÔÉ ÚÁÐÉÓ Ú ÓÉÓÔÅÍÎϧ ÔÁÂÌÉæ"
+ 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)"
+ 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)"
+ 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)"
+ 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)"
+ 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)"
+ 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áø '%-.64s' (chybový kód: %d)"
- dan "Kan ikke læse folder '%-.64s' (Fejlkode: %d)"
- nla "Kan de directory niet lezen van '%-.64s' (Errcode: %d)"
- eng "Can't read dir of '%-.64s' (errno: %d)"
- jps "'%-.64s' ƒfƒBƒŒƒNƒgƒŠ‚ª“Ç‚ß‚Ü‚¹‚ñ.(errno: %d)",
- est "Ei suuda lugeda kataloogi '%-.64s' (veakood: %d)"
- fre "Ne peut lire le répertoire de '%-.64s' (Errcode: %d)"
- ger "Verzeichnis von '%-.64s' nicht lesbar (Fehler: %d)"
- greek "Äåí åßíáé äõíáôü íá äéáâáóôåß ï öÜêåëëïò ôïõ '%-.64s' (êùäéêüò ëÜèïõò: %d)"
- hun "A(z) '%-.64s' konyvtar nem olvashato. (hibakod: %d)"
- ita "Impossibile leggere la directory di '%-.64s' (errno: %d)"
- jpn "'%-.64s' ¥Ç¥£¥ì¥¯¥È¥ê¤¬Æɤá¤Þ¤»¤ó.(errno: %d)"
- kor "'%-.64s'µð·ºÅ丮¸¦ ÀÐÁö ¸øÇß½À´Ï´Ù. (¿¡·¯¹øÈ£: %d)"
- nor "Kan ikke lese katalogen '%-.64s' (Feilkode: %d)"
- norwegian-ny "Kan ikkje lese katalogen '%-.64s' (Feilkode: %d)"
- pol "Nie mo¿na odczytaæ katalogu '%-.64s' (Kod b³êdu: %d)"
- por "Não pode ler o diretório de '%-.64s' (erro no. %d)"
- rum "Nu pot sa citesc directorul '%-.64s' (Eroare: %d)"
- rus "îÅ×ÏÚÍÏÖÎÏ ÐÒÏÞÉÔÁÔØ ËÁÔÁÌÏÇ '%-.64s' (ÏÛÉÂËÁ: %d)"
- serbian "Ne mogu da proèitam direktorijum '%-.64s' (errno: %d)"
- slo "Nemô¾em èíta» adresár '%-.64s' (chybový kód: %d)"
- spa "No puedo leer el directorio de '%-.64s' (Error: %d)"
- swe "Kan inte läsa från bibliotek '%-.64s' (Felkod: %d)"
- ukr "îÅ ÍÏÖÕ ÐÒÏÞÉÔÁÔÉ ÔÅËÕ '%-.64s' (ÐÏÍÉÌËÁ: %d)"
+ 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 '%-.64s' (chybový kód: %d)"
- dan "Kan ikke skifte folder til '%-.64s' (Fejlkode: %d)"
- nla "Kan de directory niet veranderen naar '%-.64s' (Errcode: %d)"
- eng "Can't change dir to '%-.64s' (errno: %d)"
- jps "'%-.64s' ƒfƒBƒŒƒNƒgƒŠ‚É chdir ‚Å‚«‚Ü‚¹‚ñ.(errno: %d)",
- est "Ei suuda siseneda kataloogi '%-.64s' (veakood: %d)"
- fre "Ne peut changer le répertoire pour '%-.64s' (Errcode: %d)"
- ger "Kann nicht in das Verzeichnis '%-.64s' wechseln (Fehler: %d)"
- greek "Áäýíáôç ç áëëáãÞ ôïõ ôñÝ÷ïíôïò êáôáëüãïõ óå '%-.64s' (êùäéêüò ëÜèïõò: %d)"
- hun "Konyvtarvaltas nem lehetseges a(z) '%-.64s'-ba. (hibakod: %d)"
- ita "Impossibile cambiare la directory in '%-.64s' (errno: %d)"
- jpn "'%-.64s' ¥Ç¥£¥ì¥¯¥È¥ê¤Ë chdir ¤Ç¤­¤Þ¤»¤ó.(errno: %d)"
- kor "'%-.64s'µð·ºÅ丮·Î À̵¿ÇÒ ¼ö ¾ø¾ú½À´Ï´Ù. (¿¡·¯¹øÈ£: %d)"
- nor "Kan ikke skifte katalog til '%-.64s' (Feilkode: %d)"
- norwegian-ny "Kan ikkje skifte katalog til '%-.64s' (Feilkode: %d)"
- pol "Nie mo¿na zmieniæ katalogu na '%-.64s' (Kod b³êdu: %d)"
- por "Não pode mudar para o diretório '%-.64s' (erro no. %d)"
- rum "Nu pot sa schimb directorul '%-.64s' (Eroare: %d)"
- rus "îÅ×ÏÚÍÏÖÎÏ ÐÅÒÅÊÔÉ × ËÁÔÁÌÏÇ '%-.64s' (ÏÛÉÂËÁ: %d)"
- serbian "Ne mogu da promenim direktorijum na '%-.64s' (errno: %d)"
- slo "Nemô¾em vojs» do adresára '%-.64s' (chybový kód: %d)"
- spa "No puedo cambiar al directorio de '%-.64s' (Error: %d)"
- swe "Kan inte byta till '%-.64s' (Felkod: %d)"
- ukr "îÅ ÍÏÖÕ ÐÅÒÅÊÔÉ Õ ÔÅËÕ '%-.64s' (ÐÏÍÉÌËÁ: %d)"
+ 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 '%-.64s'"
- dan "Posten er ændret siden sidste læsning '%-.64s'"
- nla "Record is veranderd sinds de laatste lees activiteit in de tabel '%-.64s'"
- eng "Record has changed since last read in table '%-.64s'"
- est "Kirje tabelis '%-.64s' on muutunud viimasest lugemisest saadik"
- fre "Enregistrement modifié depuis sa dernière lecture dans la table '%-.64s'"
- ger "Datensatz hat sich seit dem letzten Zugriff auf Tabelle '%-.64s' geändert"
- greek "Ç åããñáöÞ Ý÷åé áëëÜîåé áðü ôçí ôåëåõôáßá öïñÜ ðïõ áíáóýñèçêå áðü ôïí ðßíáêá '%-.64s'"
- hun "A(z) '%-.64s' tablaban talalhato rekord megvaltozott az utolso olvasas ota"
- ita "Il record e` cambiato dall'ultima lettura della tabella '%-.64s'"
- kor "Å×À̺í '%-.64s'¿¡¼­ ¸¶Áö¸·À¸·Î ÀÐÀº ÈÄ Record°¡ º¯°æµÇ¾ú½À´Ï´Ù."
- nor "Posten har blitt endret siden den ble lest '%-.64s'"
- norwegian-ny "Posten har vorte endra sidan den sist vart lesen '%-.64s'"
- pol "Rekord zosta³ zmieniony od ostaniego odczytania z tabeli '%-.64s'"
- por "Registro alterado desde a última leitura da tabela '%-.64s'"
- rum "Cimpul a fost schimbat de la ultima citire a tabelei '%-.64s'"
- rus "úÁÐÉÓØ ÉÚÍÅÎÉÌÁÓØ Ó ÍÏÍÅÎÔÁ ÐÏÓÌÅÄÎÅÊ ×ÙÂÏÒËÉ × ÔÁÂÌÉÃÅ '%-.64s'"
- serbian "Slog je promenjen od zadnjeg èitanja tabele '%-.64s'"
- slo "Záznam bol zmenený od posledného èítania v tabuµke '%-.64s'"
- spa "El registro ha cambiado desde la ultima lectura de la tabla '%-.64s'"
- swe "Posten har förändrats sedan den lästes i register '%-.64s'"
- ukr "úÁÐÉÓ ÂÕÌÏ ÚͦÎÅÎÏ Ú ÞÁÓÕ ÏÓÔÁÎÎØÏÇÏ ÞÉÔÁÎÎÑ Ú ÔÁÂÌÉæ '%-.64s'"
+ 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). ÷ÉÞÉËÕÀ, ÄÏËÉ ÚצÌØÎÉÔØÓÑ ÔÒÏÈÉ Í¦ÓÃÑ..."
+ 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 '%-.64s'"
- dan "Kan ikke skrive, flere ens nøgler i tabellen '%-.64s'"
- nla "Kan niet schrijven, dubbele zoeksleutel in tabel '%-.64s'"
- eng "Can't write; duplicate key in table '%-.64s'"
- jps "table '%-.64s' ‚É key ‚ªd•¡‚µ‚Ä‚¢‚Ä‘‚«‚±‚ß‚Ü‚¹‚ñ",
- est "Ei saa kirjutada, korduv võti tabelis '%-.64s'"
- fre "Ecriture impossible, doublon dans une clé de la table '%-.64s'"
- ger "Kann nicht speichern, Grund: doppelter Schlüssel in Tabelle '%-.64s'"
- greek "Äåí åßíáé äõíáôÞ ç êáôá÷þñçóç, ç ôéìÞ õðÜñ÷åé Þäç óôïí ðßíáêá '%-.64s'"
- hun "Irasi hiba, duplikalt kulcs a '%-.64s' tablaban."
- ita "Scrittura impossibile: chiave duplicata nella tabella '%-.64s'"
- jpn "table '%-.64s' ¤Ë key ¤¬½ÅÊ£¤·¤Æ¤¤¤Æ½ñ¤­¤³¤á¤Þ¤»¤ó"
- kor "±â·ÏÇÒ ¼ö ¾øÀ¾´Ï´Ù., Å×À̺í '%-.64s'¿¡¼­ Áߺ¹ Å°"
- nor "Kan ikke skrive, flere like nøkler i tabellen '%-.64s'"
- norwegian-ny "Kan ikkje skrive, flere like nyklar i tabellen '%-.64s'"
- pol "Nie mo¿na zapisaæ, powtórzone klucze w tabeli '%-.64s'"
- por "Não pode gravar. Chave duplicada na tabela '%-.64s'"
- rum "Nu pot sa scriu (can't write), cheie duplicata in tabela '%-.64s'"
- rus "îÅ×ÏÚÍÏÖÎÏ ÐÒÏÉÚ×ÅÓÔÉ ÚÁÐÉÓØ, ÄÕÂÌÉÒÕÀÝÉÊÓÑ ËÌÀÞ × ÔÁÂÌÉÃÅ '%-.64s'"
- serbian "Ne mogu da pišem pošto postoji duplirani kljuè u tabeli '%-.64s'"
- slo "Nemô¾em zapísa», duplikát kµúèa v tabuµke '%-.64s'"
- spa "No puedo escribir, clave duplicada en la tabla '%-.64s'"
- swe "Kan inte skriva, dubbel söknyckel i register '%-.64s'"
- ukr "îÅ ÍÏÖÕ ÚÁÐÉÓÁÔÉ, ÄÕÂÌÀÀÞÉÊÓÑ ËÌÀÞ × ÔÁÂÌÉæ '%-.64s'"
+ 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í '%-.64s' (chybový kód: %d)"
- dan "Fejl ved lukning af '%-.64s' (Fejlkode: %d)"
- nla "Fout bij het sluiten van '%-.64s' (Errcode: %d)"
- eng "Error on close of '%-.64s' (errno: %d)"
- est "Viga faili '%-.64s' sulgemisel (veakood: %d)"
- fre "Erreur a la fermeture de '%-.64s' (Errcode: %d)"
- ger "Fehler beim Schließen von '%-.64s' (Fehler: %d)"
- greek "ÐáñïõóéÜóôçêå ðñüâëçìá êëåßíïíôáò ôï '%-.64s' (êùäéêüò ëÜèïõò: %d)"
- hun "Hiba a(z) '%-.64s' zarasakor. (hibakod: %d)"
- ita "Errore durante la chiusura di '%-.64s' (errno: %d)"
- kor "'%-.64s'´Ý´Â Áß ¿¡·¯ (¿¡·¯¹øÈ£: %d)"
- nor "Feil ved lukking av '%-.64s' (Feilkode: %d)"
- norwegian-ny "Feil ved lukking av '%-.64s' (Feilkode: %d)"
- pol "B³?d podczas zamykania '%-.64s' (Kod b³êdu: %d)"
- por "Erro ao fechar '%-.64s' (erro no. %d)"
- rum "Eroare inchizind '%-.64s' (errno: %d)"
- rus "ïÛÉÂËÁ ÐÒÉ ÚÁËÒÙÔÉÉ '%-.64s' (ÏÛÉÂËÁ: %d)"
- serbian "Greška pri zatvaranju '%-.64s' (errno: %d)"
- slo "Chyba pri zatváraní '%-.64s' (chybový kód: %d)"
- spa "Error en el cierre de '%-.64s' (Error: %d)"
- swe "Fick fel vid stängning av '%-.64s' (Felkod: %d)"
- ukr "îÅ ÍÏÖÕ ÚÁËÒÉÔÉ '%-.64s' (ÐÏÍÉÌËÁ: %d)"
+ 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)"
+ 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í '%-.150s' na '%-.150s' (chybový kód: %d)"
- dan "Fejl ved omdøbning af '%-.150s' til '%-.150s' (Fejlkode: %d)"
- nla "Fout bij het hernoemen van '%-.150s' naar '%-.150s' (Errcode: %d)"
- eng "Error on rename of '%-.150s' to '%-.150s' (errno: %d)"
- jps "'%-.150s' ‚ð '%-.150s' ‚É rename ‚Å‚«‚Ü‚¹‚ñ (errno: %d)",
- est "Viga faili '%-.150s' ümbernimetamisel '%-.150s'-ks (veakood: %d)"
- fre "Erreur en renommant '%-.150s' en '%-.150s' (Errcode: %d)"
- ger "Fehler beim Umbenennen von '%-.150s' in '%-.150s' (Fehler: %d)"
- greek "Ðñüâëçìá êáôÜ ôçí ìåôïíïìáóßá ôïõ áñ÷åßïõ '%-.150s' to '%-.150s' (êùäéêüò ëÜèïõò: %d)"
- hun "Hiba a '%-.150s' file atnevezesekor '%-.150s'. (hibakod: %d)"
- ita "Errore durante la rinominazione da '%-.150s' a '%-.150s' (errno: %d)"
- jpn "'%-.150s' ¤ò '%-.150s' ¤Ë rename ¤Ç¤­¤Þ¤»¤ó (errno: %d)"
- kor "'%-.150s'¸¦ '%-.150s'·Î À̸§ º¯°æÁß ¿¡·¯ (¿¡·¯¹øÈ£: %d)"
- nor "Feil ved omdøping av '%-.150s' til '%-.150s' (Feilkode: %d)"
- norwegian-ny "Feil ved omdøyping av '%-.150s' til '%-.150s' (Feilkode: %d)"
- pol "B³?d podczas zmieniania nazwy '%-.150s' na '%-.150s' (Kod b³êdu: %d)"
- por "Erro ao renomear '%-.150s' para '%-.150s' (erro no. %d)"
- rum "Eroare incercind sa renumesc '%-.150s' in '%-.150s' (errno: %d)"
- rus "ïÛÉÂËÁ ÐÒÉ ÐÅÒÅÉÍÅÎÏ×ÁÎÉÉ '%-.150s' × '%-.150s' (ÏÛÉÂËÁ: %d)"
- serbian "Greška pri promeni imena '%-.150s' na '%-.150s' (errno: %d)"
- slo "Chyba pri premenovávaní '%-.150s' na '%-.150s' (chybový kód: %d)"
- spa "Error en el renombrado de '%-.150s' a '%-.150s' (Error: %d)"
- swe "Kan inte byta namn från '%-.150s' till '%-.150s' (Felkod: %d)"
- ukr "îÅ ÍÏÖÕ ÐÅÒÅÊÍÅÎÕ×ÁÔÉ '%-.150s' Õ '%-.150s' (ÐÏÍÉÌËÁ: %d)"
+ 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)"
+ 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 "'%-.64s' je zam-Bèen proti zmìnám"
- dan "'%-.64s' er låst mod opdateringer"
- nla "'%-.64s' is geblokeerd tegen veranderingen"
- eng "'%-.64s' is locked against change"
- jps "'%-.64s' ‚̓ƒbƒN‚³‚ê‚Ä‚¢‚Ü‚·",
- est "'%-.64s' on lukustatud muudatuste vastu"
- fre "'%-.64s' est verrouillé contre les modifications"
- ger "'%-.64s' ist für Änderungen gesperrt"
- greek "'%-.64s' äåí åðéôñÝðïíôáé áëëáãÝò"
- hun "'%-.64s' a valtoztatas ellen zarolva"
- ita "'%-.64s' e` soggetto a lock contro i cambiamenti"
- jpn "'%-.64s' ¤Ï¥í¥Ã¥¯¤µ¤ì¤Æ¤¤¤Þ¤¹"
- kor "'%-.64s'°¡ º¯°æÇÒ ¼ö ¾øµµ·Ï Àá°ÜÀÖÀ¾´Ï´Ù."
- nor "'%-.64s' er låst mot oppdateringer"
- norwegian-ny "'%-.64s' er låst mot oppdateringar"
- pol "'%-.64s' jest zablokowany na wypadek zmian"
- por "'%-.64s' está com travamento contra alterações"
- rum "'%-.64s' este blocat pentry schimbari (loccked against change)"
- rus "'%-.64s' ÚÁÂÌÏËÉÒÏ×ÁÎ ÄÌÑ ÉÚÍÅÎÅÎÉÊ"
- serbian "'%-.64s' je zakljuèan za upis"
- slo "'%-.64s' je zamknutý proti zmenám"
- spa "'%-.64s' esta bloqueado contra cambios"
- swe "'%-.64s' är låst mot användning"
- ukr "'%-.64s' ÚÁÂÌÏËÏ×ÁÎÉÊ ÎÁ ×ÎÅÓÅÎÎÑ ÚͦÎ"
+ 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 "óÏÒÔÕ×ÁÎÎÑ ÐÅÒÅÒ×ÁÎÏ"
+ 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 '%-.64s' pro '%-.64s' neexistuje"
- dan "View '%-.64s' eksisterer ikke for '%-.64s'"
- nla "View '%-.64s' bestaat niet voor '%-.64s'"
- eng "View '%-.64s' doesn't exist for '%-.64s'"
- jps "View '%-.64s' ‚ª '%-.64s' ‚É’è‹`‚³‚ê‚Ä‚¢‚Ü‚¹‚ñ",
- est "Vaade '%-.64s' ei eksisteeri '%-.64s' jaoks"
- fre "La vue (View) '%-.64s' n'existe pas pour '%-.64s'"
- ger "View '%-.64s' existiert für '%-.64s' nicht"
- greek "Ôï View '%-.64s' äåí õðÜñ÷åé ãéá '%-.64s'"
- hun "A(z) '%-.64s' nezet nem letezik a(z) '%-.64s'-hoz"
- ita "La view '%-.64s' non esiste per '%-.64s'"
- jpn "View '%-.64s' ¤¬ '%-.64s' ¤ËÄêµÁ¤µ¤ì¤Æ¤¤¤Þ¤»¤ó"
- kor "ºä '%-.64s'°¡ '%-.64s'¿¡¼­´Â Á¸ÀçÇÏÁö ¾ÊÀ¾´Ï´Ù."
- nor "View '%-.64s' eksisterer ikke for '%-.64s'"
- norwegian-ny "View '%-.64s' eksisterar ikkje for '%-.64s'"
- pol "Widok '%-.64s' nie istnieje dla '%-.64s'"
- por "Visão '%-.64s' não existe para '%-.64s'"
- rum "View '%-.64s' nu exista pentru '%-.64s'"
- rus "ðÒÅÄÓÔÁ×ÌÅÎÉÅ '%-.64s' ÎÅ ÓÕÝÅÓÔ×ÕÅÔ ÄÌÑ '%-.64s'"
- serbian "View '%-.64s' ne postoji za '%-.64s'"
- slo "Pohµad '%-.64s' neexistuje pre '%-.64s'"
- spa "La vista '%-.64s' no existe para '%-.64s'"
- swe "Formulär '%-.64s' finns inte i '%-.64s'"
- ukr "÷ÉÇÌÑÄ '%-.64s' ÎÅ ¦ÓÎÕ¤ ÄÌÑ '%-.64s'"
+ 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 ×¦Ä ÄÅÓËÒÉÐÔÏÒÁ ÔÁÂÌÉæ"
+ 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 '%-.64s' nem-Bá tento parametr"
- dan "Denne mulighed eksisterer ikke for tabeltypen '%-.64s'"
- nla "Tabel handler voor '%-.64s' heeft deze optie niet"
- eng "Table storage engine for '%-.64s' doesn't have this option"
- est "Tabeli '%-.64s' handler ei toeta antud operatsiooni"
- fre "Le handler de la table '%-.64s' n'a pas cette option"
- ger "Diese Option gibt es nicht (Speicher-Engine für '%-.64s')"
- greek "Ï ÷åéñéóôÞò ðßíáêá (table handler) ãéá '%-.64s' äåí äéáèÝôåé áõôÞ ôçí åðéëïãÞ"
- hun "A(z) '%-.64s' tablakezelonek nincs ilyen opcioja"
- ita "Il gestore delle tabelle per '%-.64s' non ha questa opzione"
- jpn "Table handler for '%-.64s' doesn't have this option"
- kor "'%-.64s'ÀÇ Å×À̺í handler´Â ÀÌ·¯ÇÑ ¿É¼ÇÀ» Á¦°øÇÏÁö ¾ÊÀ¾´Ï´Ù."
- nor "Tabell håndtereren for '%-.64s' har ikke denne muligheten"
- norwegian-ny "Tabell håndteraren for '%-.64s' har ikkje denne moglegheita"
- pol "Obs³uga tabeli '%-.64s' nie posiada tej opcji"
- por "Manipulador de tabela para '%-.64s' não tem esta opção"
- rum "Handlerul tabelei pentru '%-.64s' nu are aceasta optiune"
- rus "ïÂÒÁÂÏÔÞÉË ÔÁÂÌÉÃÙ '%-.64s' ÎÅ ÐÏÄÄÅÒÖÉ×ÁÅÔ ÜÔÕ ×ÏÚÍÏÖÎÏÓÔØ"
- serbian "Handler tabela za '%-.64s' nema ovu opciju"
- slo "Obsluha tabuµky '%-.64s' nemá tento parameter"
- spa "El manejador de la tabla de '%-.64s' no tiene esta opcion"
- swe "Tabellhanteraren for tabell '%-.64s' stödjer ej detta"
- ukr "äÅÓËÒÉÐÔÏÒ ÔÁÂÌÉæ '%-.64s' ÎÅ ÍÁ¤ 椧 ×ÌÁÓÔÉ×ÏÓÔ¦"
+ 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 '%-.64s'"
- dan "Kan ikke finde posten i '%-.64s'"
- nla "Kan record niet vinden in '%-.64s'"
- eng "Can't find record in '%-.64s'"
- jps "'%-.64s'‚Ì‚È‚©‚ɃŒƒR[ƒh‚ªŒ©•t‚©‚è‚Ü‚¹‚ñ",
- est "Ei suuda leida kirjet '%-.64s'-s"
- fre "Ne peut trouver l'enregistrement dans '%-.64s'"
- ger "Kann Datensatz in '%-.64s' nicht finden"
- greek "Áäýíáôç ç áíåýñåóç åããñáöÞò óôï '%-.64s'"
- hun "Nem talalhato a rekord '%-.64s'-ben"
- ita "Impossibile trovare il record in '%-.64s'"
- jpn "'%-.64s'¤Î¤Ê¤«¤Ë¥ì¥³¡¼¥É¤¬¸«ÉÕ¤«¤ê¤Þ¤»¤ó"
- kor "'%-.64s'¿¡¼­ ·¹Äڵ带 ãÀ» ¼ö ¾øÀ¾´Ï´Ù."
- nor "Kan ikke finne posten i '%-.64s'"
- norwegian-ny "Kan ikkje finne posten i '%-.64s'"
- pol "Nie mo¿na znale¥æ rekordu w '%-.64s'"
- por "Não pode encontrar registro em '%-.64s'"
- rum "Nu pot sa gasesc recordul in '%-.64s'"
- rus "îÅ×ÏÚÍÏÖÎÏ ÎÁÊÔÉ ÚÁÐÉÓØ × '%-.64s'"
- serbian "Ne mogu da pronaðem slog u '%-.64s'"
- slo "Nemô¾em nájs» záznam v '%-.64s'"
- spa "No puedo encontrar el registro en '%-.64s'"
- swe "Hittar inte posten '%-.64s'"
- ukr "îÅ ÍÏÖÕ ÚÁÐÉÓÁÔÉ Õ '%-.64s'"
+ 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'"
+ 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'; óÐÒÏÂÕÊÔÅ ÊÏÇÏ ×¦ÄÎÏ×ÉÔÉ"
+ 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 '%-.64s'; opravte ho."
- dan "Gammel indeksfil for tabellen '%-.64s'; reparer den"
- nla "Oude zoeksleutel file voor tabel '%-.64s'; repareer het!"
- eng "Old key file for table '%-.64s'; repair it!"
- jps "'%-.64s' ƒe[ƒuƒ‹‚͌¢Œ`Ž®‚Ì key file ‚̂悤‚Å‚·; C•œ‚ð‚µ‚Ä‚­‚¾‚³‚¢",
- est "Tabeli '%-.64s' võtmefail on aegunud; paranda see!"
- fre "Vieux fichier d'index pour la table '%-.64s'; réparez le!"
- ger "Alte Index-Datei für Tabelle '%-.64s'. Bitte reparieren"
- greek "Ðáëáéü áñ÷åßï ôáîéíüìéóçò (key file) ãéá ôïí ðßíáêá '%-.64s'; Ðáñáêáëþ, äéïñèþóôå ôï!"
- hun "Regi kulcsfile a '%-.64s'tablahoz; probalja kijavitani!"
- ita "File chiave vecchio per la tabella '%-.64s'; riparalo!"
- jpn "'%-.64s' ¥Æ¡¼¥Ö¥ë¤Ï¸Å¤¤·Á¼°¤Î key file ¤Î¤è¤¦¤Ç¤¹; ½¤Éü¤ò¤·¤Æ¤¯¤À¤µ¤¤"
- kor "'%-.64s' Å×À̺íÀÇ ÀÌÀü¹öÁ¯ÀÇ Å° Á¸Àç. ¼öÁ¤ÇϽÿÀ!"
- nor "Gammel nøkkelfil for tabellen '%-.64s'; reparer den!"
- norwegian-ny "Gammel nykkelfil for tabellen '%-.64s'; reparer den!"
- pol "Plik kluczy dla tabeli '%-.64s' jest starego typu; napraw go!"
- por "Arquivo de índice desatualizado para tabela '%-.64s'; repare-o!"
- rum "Cheia fisierului e veche pentru tabela '%-.64s'; repar-o!"
- rus "óÔÁÒÙÊ ÉÎÄÅËÓÎÙÊ ÆÁÊÌ ÄÌÑ ÔÁÂÌÉÃÙ '%-.64s'; ÏÔÒÅÍÏÎÔÉÒÕÊÔÅ ÅÇÏ!"
- serbian "Zastareo key file za tabelu '%-.64s'; ispravite ga"
- slo "Starý kµúèový súbor pre '%-.64s'; opravte ho!"
- spa "Clave de archivo antigua para la tabla '%-.64s'; reparelo!"
- swe "Gammal nyckelfil '%-.64s'; reparera registret"
- ukr "óÔÁÒÉÊ ÆÁÊÌ ËÌÀÞÅÊ ÄÌÑ ÔÁÂÌÉæ '%-.64s'; ÷¦ÄÎÏצÔØ ÊÏÇÏ!"
+ 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 "'%-.64s' je jen pro -Bètení"
- dan "'%-.64s' er skrivebeskyttet"
- nla "'%-.64s' is alleen leesbaar"
- eng "Table '%-.64s' is read only"
- jps "'%-.64s' ‚Í“Ç‚Ýž‚Ýê—p‚Å‚·",
- est "Tabel '%-.64s' on ainult lugemiseks"
- fre "'%-.64s' est en lecture seulement"
- ger "Tabelle '%-.64s' ist nur lesbar"
- greek "'%-.64s' åðéôñÝðåôáé ìüíï ç áíÜãíùóç"
- hun "'%-.64s' irasvedett"
- ita "'%-.64s' e` di sola lettura"
- jpn "'%-.64s' ¤ÏÆɤ߹þ¤ßÀìÍѤǤ¹"
- kor "Å×À̺í '%-.64s'´Â ÀбâÀü¿ë ÀÔ´Ï´Ù."
- nor "'%-.64s' er skrivebeskyttet"
- norwegian-ny "'%-.64s' er skrivetryggja"
- pol "'%-.64s' jest tylko do odczytu"
- por "Tabela '%-.64s' é somente para leitura"
- rum "Tabela '%-.64s' e read-only"
- rus "ôÁÂÌÉÃÁ '%-.64s' ÐÒÅÄÎÁÚÎÁÞÅÎÁ ÔÏÌØËÏ ÄÌÑ ÞÔÅÎÉÑ"
- serbian "Tabelu '%-.64s' je dozvoljeno samo èitati"
- slo "'%-.64s' is èíta» only"
- spa "'%-.64s' es de solo lectura"
- swe "'%-.64s' är skyddad mot förändring"
- ukr "ôÁÂÌÉÃÑ '%-.64s' Ô¦ÌØËÉ ÄÌÑ ÞÉÔÁÎÎÑ"
+ 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 ÂÁÊÔ¦×)"
+ 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 "âÒÁË ÐÁÍ'ÑÔ¦ ÄÌÑ ÓÏÒÔÕ×ÁÎÎÑ. ôÒÅÂÁ Ú¦ÌØÛÉÔÉ ÒÏÚÍ¦Ò ÂÕÆÅÒÁ ÓÏÒÔÕ×ÁÎÎÑ Õ ÓÅÒ×ÅÒÁ"
+ 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í '%-.64s' (chybový kód: %d)"
- dan "Uventet afslutning på fil (eof) ved læsning af filen '%-.64s' (Fejlkode: %d)"
- nla "Onverwachte eof gevonden tijdens het lezen van file '%-.64s' (Errcode: %d)"
- eng "Unexpected EOF found when reading file '%-.64s' (errno: %d)"
- jps "'%-.64s' ƒtƒ@ƒCƒ‹‚ð“Ç‚Ýž‚Ý’†‚É EOF ‚ª—\Šú‚¹‚ÊŠ‚ÅŒ»‚ê‚Ü‚µ‚½. (errno: %d)",
- est "Ootamatu faililõpumärgend faili '%-.64s' lugemisel (veakood: %d)"
- fre "Fin de fichier inattendue en lisant '%-.64s' (Errcode: %d)"
- ger "Unerwartetes Ende beim Lesen der Datei '%-.64s' (Fehler: %d)"
- greek "ÊáôÜ ôç äéÜñêåéá ôçò áíÜãíùóçò, âñÝèçêå áðñïóäüêçôá ôï ôÝëïò ôïõ áñ÷åßïõ '%-.64s' (êùäéêüò ëÜèïõò: %d)"
- hun "Varatlan filevege-jel a '%-.64s'olvasasakor. (hibakod: %d)"
- ita "Fine del file inaspettata durante la lettura del file '%-.64s' (errno: %d)"
- jpn "'%-.64s' ¥Õ¥¡¥¤¥ë¤òÆɤ߹þ¤ßÃæ¤Ë EOF ¤¬Í½´ü¤»¤Ì½ê¤Ç¸½¤ì¤Þ¤·¤¿. (errno: %d)"
- kor "'%-.64s' È­ÀÏÀ» Àд µµÁß À߸øµÈ eofÀ» ¹ß°ß (¿¡·¯¹øÈ£: %d)"
- nor "Uventet slutt på fil (eof) ved lesing av filen '%-.64s' (Feilkode: %d)"
- norwegian-ny "Uventa slutt på fil (eof) ved lesing av fila '%-.64s' (Feilkode: %d)"
- pol "Nieoczekiwany 'eof' napotkany podczas czytania z pliku '%-.64s' (Kod b³êdu: %d)"
- por "Encontrado fim de arquivo inesperado ao ler arquivo '%-.64s' (erro no. %d)"
- rum "Sfirsit de fisier neasteptat in citirea fisierului '%-.64s' (errno: %d)"
- rus "îÅÏÖÉÄÁÎÎÙÊ ËÏÎÅà ÆÁÊÌÁ '%-.64s' (ÏÛÉÂËÁ: %d)"
- serbian "Neoèekivani kraj pri èitanju file-a '%-.64s' (errno: %d)"
- slo "Neoèakávaný koniec súboru pri èítaní '%-.64s' (chybový kód: %d)"
- spa "Inesperado fin de ficheroU mientras leiamos el archivo '%-.64s' (Error: %d)"
- swe "Oväntat filslut vid läsning från '%-.64s' (Felkod: %d)"
- ukr "èÉÂÎÉÊ Ë¦ÎÅÃØ ÆÁÊÌÕ '%-.64s' (ÐÏÍÉÌËÁ: %d)"
+ 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 connections"
- 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 "úÁÂÁÇÁÔÏ Ú'¤ÄÎÁÎØ"
+ 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 ×ÉËÏÒÉÓÔÏ×Õ×ÁÔÉ Â¦ÌØÛÅ ÐÁÍ'ÑÔ¦ ÁÂÏ ×É ÍÏÖÅÔÅ ÄÏÄÁÔÉ Â¦ÌØÛŠͦÓÃÑ Ð¦Ä Ó×ÁÐ"
+ 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 "îÅ ÍÏÖÕ ×ÉÚÎÁÞÉÔÉ ¦Í'Ñ ÈÏÓÔÕ ÄÌÑ ×ÁÛϧ ÁÄÒÅÓÉ"
+ 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 "îÅצÒÎÁ ÕÓÔÁÎÏ×ËÁ Ú×'ÑÚËÕ"
+ 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 '%-.32s'@'%-.64s' k databázi '%-.64s' není povolen"
- dan "Adgang nægtet bruger: '%-.32s'@'%-.64s' til databasen '%-.64s'"
- nla "Toegang geweigerd voor gebruiker: '%-.32s'@'%-.64s' naar database '%-.64s'"
- eng "Access denied for user '%-.32s'@'%-.64s' to database '%-.64s'"
- jps "ƒ†[ƒU[ '%-.32s'@'%-.64s' ‚Ì '%-.64s' ƒf[ƒ^ƒx[ƒX‚ւ̃AƒNƒZƒX‚ð‹‘”Û‚µ‚Ü‚·",
- est "Ligipääs keelatud kasutajale '%-.32s'@'%-.64s' andmebaasile '%-.64s'"
- fre "Accès refusé pour l'utilisateur: '%-.32s'@'@%-.64s'. Base '%-.64s'"
- ger "Benutzer '%-.32s'@'%-.64s' hat keine Zugriffsberechtigung für Datenbank '%-.64s'"
- greek "Äåí åðéôÝñåôáé ç ðñüóâáóç óôï ÷ñÞóôç: '%-.32s'@'%-.64s' óôç âÜóç äåäïìÝíùí '%-.64s'"
- hun "A(z) '%-.32s'@'%-.64s' felhasznalo szamara tiltott eleres az '%-.64s' adabazishoz."
- ita "Accesso non consentito per l'utente: '%-.32s'@'%-.64s' al database '%-.64s'"
- jpn "¥æ¡¼¥¶¡¼ '%-.32s'@'%-.64s' ¤Î '%-.64s' ¥Ç¡¼¥¿¥Ù¡¼¥¹¤Ø¤Î¥¢¥¯¥»¥¹¤òµñÈݤ·¤Þ¤¹"
- kor "'%-.32s'@'%-.64s' »ç¿ëÀÚ´Â '%-.64s' µ¥ÀÌŸº£À̽º¿¡ Á¢±ÙÀÌ °ÅºÎ µÇ¾ú½À´Ï´Ù."
- nor "Tilgang nektet for bruker: '%-.32s'@'%-.64s' til databasen '%-.64s' nektet"
- norwegian-ny "Tilgang ikkje tillate for brukar: '%-.32s'@'%-.64s' til databasen '%-.64s' nekta"
- por "Acesso negado para o usuário '%-.32s'@'%-.64s' ao banco de dados '%-.64s'"
- rum "Acces interzis pentru utilizatorul: '%-.32s'@'%-.64s' la baza de date '%-.64s'"
- rus "äÌÑ ÐÏÌØÚÏ×ÁÔÅÌÑ '%-.32s'@'%-.64s' ÄÏÓÔÕÐ Ë ÂÁÚÅ ÄÁÎÎÙÈ '%-.64s' ÚÁËÒÙÔ"
- serbian "Pristup je zabranjen korisniku '%-.32s'@'%-.64s' za bazu '%-.64s'"
- slo "Zakázaný prístup pre u¾ívateµa: '%-.32s'@'%-.64s' k databázi '%-.64s'"
- spa "Acceso negado para usuario: '%-.32s'@'%-.64s' para la base de datos '%-.64s'"
- swe "Användare '%-.32s'@'%-.64s' är ej berättigad att använda databasen %-.64s"
- ukr "äÏÓÔÕÐ ÚÁÂÏÒÏÎÅÎÏ ÄÌÑ ËÏÒÉÓÔÕ×ÁÞÁ: '%-.32s'@'%-.64s' ÄÏ ÂÁÚÉ ÄÁÎÎÉÈ '%-.64s'"
+ 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 '%-.32s'@'%-.64s' (s heslem %s)"
- dan "Adgang nægtet bruger: '%-.32s'@'%-.64s' (Bruger adgangskode: %s)"
- nla "Toegang geweigerd voor gebruiker: '%-.32s'@'%-.64s' (Wachtwoord gebruikt: %s)"
- eng "Access denied for user '%-.32s'@'%-.64s' (using password: %s)"
- jps "ƒ†[ƒU[ '%-.32s'@'%-.64s' ‚ð‹‘”Û‚µ‚Ü‚·.uUsing password: %s)",
- est "Ligipääs keelatud kasutajale '%-.32s'@'%-.64s' (kasutab parooli: %s)"
- fre "Accès refusé pour l'utilisateur: '%-.32s'@'@%-.64s' (mot de passe: %s)"
- ger "Benutzer '%-.32s'@'%-.64s' hat keine Zugriffsberechtigung (verwendetes Passwort: %s)"
- greek "Äåí åðéôÝñåôáé ç ðñüóâáóç óôï ÷ñÞóôç: '%-.32s'@'%-.64s' (÷ñÞóç password: %s)"
- hun "A(z) '%-.32s'@'%-.64s' felhasznalo szamara tiltott eleres. (Hasznalja a jelszot: %s)"
- ita "Accesso non consentito per l'utente: '%-.32s'@'%-.64s' (Password: %s)"
- jpn "¥æ¡¼¥¶¡¼ '%-.32s'@'%-.64s' ¤òµñÈݤ·¤Þ¤¹.uUsing password: %s)"
- kor "'%-.32s'@'%-.64s' »ç¿ëÀÚ´Â Á¢±ÙÀÌ °ÅºÎ µÇ¾ú½À´Ï´Ù. (using password: %s)"
- nor "Tilgang nektet for bruker: '%-.32s'@'%-.64s' (Bruker passord: %s)"
- norwegian-ny "Tilgang ikke tillate for brukar: '%-.32s'@'%-.64s' (Brukar passord: %s)"
- por "Acesso negado para o usuário '%-.32s'@'%-.64s' (senha usada: %s)"
- rum "Acces interzis pentru utilizatorul: '%-.32s'@'%-.64s' (Folosind parola: %s)"
- rus "äÏÓÔÕÐ ÚÁËÒÙÔ ÄÌÑ ÐÏÌØÚÏ×ÁÔÅÌÑ '%-.32s'@'%-.64s' (ÂÙÌ ÉÓÐÏÌØÚÏ×ÁÎ ÐÁÒÏÌØ: %s)"
- serbian "Pristup je zabranjen korisniku '%-.32s'@'%-.64s' (koristi lozinku: '%s')"
- slo "Zakázaný prístup pre u¾ívateµa: '%-.32s'@'%-.64s' (pou¾itie hesla: %s)"
- spa "Acceso negado para usuario: '%-.32s'@'%-.64s' (Usando clave: %s)"
- swe "Användare '%-.32s'@'%-.64s' är ej berättigad att logga in (Använder lösen: %s)"
- ukr "äÏÓÔÕÐ ÚÁÂÏÒÏÎÅÎÏ ÄÌÑ ËÏÒÉÓÔÕ×ÁÞÁ: '%-.32s'@'%-.64s' (÷ÉËÏÒÉÓÔÁÎÏ ÐÁÒÏÌØ: %s)"
+ 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 "âÁÚÕ ÄÁÎÎÉÈ ÎÅ ×ÉÂÒÁÎÏ"
+ 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 "îÅצÄÏÍÁ ËÏÍÁÎÄÁ"
+ 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 '%-.64s' nem-Bù¾e být null"
- dan "Kolonne '%-.64s' kan ikke være NULL"
- nla "Kolom '%-.64s' kan niet null zijn"
- eng "Column '%-.64s' cannot be null"
- jps "Column '%-.64s' ‚Í null ‚É‚Í‚Å‚«‚È‚¢‚Ì‚Å‚·",
- est "Tulp '%-.64s' ei saa omada nullväärtust"
- fre "Le champ '%-.64s' ne peut être vide (null)"
- ger "Feld '%-.64s' darf nicht NULL sein"
- greek "Ôï ðåäßï '%-.64s' äåí ìðïñåß íá åßíáé êåíü (null)"
- hun "A(z) '%-.64s' oszlop erteke nem lehet nulla"
- ita "La colonna '%-.64s' non puo` essere nulla"
- jpn "Column '%-.64s' ¤Ï null ¤Ë¤Ï¤Ç¤­¤Ê¤¤¤Î¤Ç¤¹"
- kor "Ä®·³ '%-.64s'´Â ³Î(Null)ÀÌ µÇ¸é ¾ÈµË´Ï´Ù. "
- nor "Kolonne '%-.64s' kan ikke vere null"
- norwegian-ny "Kolonne '%-.64s' kan ikkje vere null"
- pol "Kolumna '%-.64s' nie mo¿e byæ null"
- por "Coluna '%-.64s' não pode ser vazia"
- rum "Coloana '%-.64s' nu poate sa fie null"
- rus "óÔÏÌÂÅà '%-.64s' ÎÅ ÍÏÖÅÔ ÐÒÉÎÉÍÁÔØ ×ÅÌÉÞÉÎÕ NULL"
- serbian "Kolona '%-.64s' ne može biti NULL"
- slo "Pole '%-.64s' nemô¾e by» null"
- spa "La columna '%-.64s' no puede ser nula"
- swe "Kolumn '%-.64s' får inte vara NULL"
- ukr "óÔÏ×ÂÅÃØ '%-.64s' ÎÅ ÍÏÖÅ ÂÕÔÉ ÎÕÌØÏ×ÉÍ"
+ 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 '%-.64s'"
- dan "Ukendt database '%-.64s'"
- nla "Onbekende database '%-.64s'"
- eng "Unknown database '%-.64s'"
- jps "'%-.64s' ‚È‚ñ‚ăf[ƒ^ƒx[ƒX‚Í’m‚è‚Ü‚¹‚ñ.",
- est "Tundmatu andmebaas '%-.64s'"
- fre "Base '%-.64s' inconnue"
- ger "Unbekannte Datenbank '%-.64s'"
- greek "Áãíùóôç âÜóç äåäïìÝíùí '%-.64s'"
- hun "Ervenytelen adatbazis: '%-.64s'"
- ita "Database '%-.64s' sconosciuto"
- jpn "'%-.64s' ¤Ê¤ó¤Æ¥Ç¡¼¥¿¥Ù¡¼¥¹¤ÏÃΤê¤Þ¤»¤ó."
- kor "µ¥ÀÌŸº£À̽º '%-.64s'´Â ¾Ë¼ö ¾øÀ½"
- nor "Ukjent database '%-.64s'"
- norwegian-ny "Ukjent database '%-.64s'"
- pol "Nieznana baza danych '%-.64s'"
- por "Banco de dados '%-.64s' desconhecido"
- rum "Baza de data invalida '%-.64s'"
- rus "îÅÉÚ×ÅÓÔÎÁÑ ÂÁÚÁ ÄÁÎÎÙÈ '%-.64s'"
- serbian "Nepoznata baza '%-.64s'"
- slo "Neznáma databáza '%-.64s'"
- spa "Base de datos desconocida '%-.64s'"
- swe "Okänd databas: '%-.64s'"
- ukr "îÅצÄÏÍÁ ÂÁÚÁ ÄÁÎÎÉÈ '%-.64s'"
+ 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 '%-.64s' ji-B¾ existuje"
- dan "Tabellen '%-.64s' findes allerede"
- nla "Tabel '%-.64s' bestaat al"
- eng "Table '%-.64s' already exists"
- jps "Table '%-.64s' ‚ÍŠù‚É‚ ‚è‚Ü‚·",
- est "Tabel '%-.64s' juba eksisteerib"
- fre "La table '%-.64s' existe déjà"
- ger "Tabelle '%-.64s' bereits vorhanden"
- greek "Ï ðßíáêáò '%-.64s' õðÜñ÷åé Þäç"
- hun "A(z) '%-.64s' tabla mar letezik"
- ita "La tabella '%-.64s' esiste gia`"
- jpn "Table '%-.64s' ¤Ï´û¤Ë¤¢¤ê¤Þ¤¹"
- kor "Å×À̺í '%-.64s'´Â ÀÌ¹Ì Á¸ÀçÇÔ"
- nor "Tabellen '%-.64s' eksisterer allerede"
- norwegian-ny "Tabellen '%-.64s' eksisterar allereide"
- pol "Tabela '%-.64s' ju¿ istnieje"
- por "Tabela '%-.64s' já existe"
- rum "Tabela '%-.64s' exista deja"
- rus "ôÁÂÌÉÃÁ '%-.64s' ÕÖÅ ÓÕÝÅÓÔ×ÕÅÔ"
- serbian "Tabela '%-.64s' veæ postoji"
- slo "Tabuµka '%-.64s' u¾ existuje"
- spa "La tabla '%-.64s' ya existe"
- swe "Tabellen '%-.64s' finns redan"
- ukr "ôÁÂÌÉÃÑ '%-.64s' ×ÖÅ ¦ÓÎÕ¤"
+ 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'"
+ 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 '%-.64s' v %-.64s nen-Bí zcela jasný"
- dan "Felt: '%-.64s' i tabel %-.64s er ikke entydigt"
- nla "Kolom: '%-.64s' in %-.64s is niet eenduidig"
- eng "Column '%-.64s' in %-.64s is ambiguous"
- est "Väli '%-.64s' %-.64s-s ei ole ühene"
- fre "Champ: '%-.64s' dans %-.64s est ambigu"
- ger "Feld '%-.64s' in %-.64s ist nicht eindeutig"
- greek "Ôï ðåäßï: '%-.64s' óå %-.64s äåí Ý÷åé êáèïñéóôåß"
- hun "A(z) '%-.64s' oszlop %-.64s-ben ketertelmu"
- ita "Colonna: '%-.64s' di %-.64s e` ambigua"
- jpn "Column: '%-.64s' in %-.64s is ambiguous"
- kor "Ä®·³: '%-.64s' in '%-.64s' ÀÌ ¸ðÈ£ÇÔ"
- nor "Felt: '%-.64s' i tabell %-.64s er ikke entydig"
- norwegian-ny "Kolonne: '%-.64s' i tabell %-.64s er ikkje eintydig"
- pol "Kolumna: '%-.64s' w %-.64s jest dwuznaczna"
- por "Coluna '%-.64s' em '%-.64s' é ambígua"
- rum "Coloana: '%-.64s' in %-.64s este ambigua"
- rus "óÔÏÌÂÅÃ '%-.64s' × %-.64s ÚÁÄÁÎ ÎÅÏÄÎÏÚÎÁÞÎÏ"
- serbian "Kolona '%-.64s' u %-.64s nije jedinstvena u kontekstu"
- slo "Pole: '%-.64s' v %-.64s je nejasné"
- spa "La columna: '%-.64s' en %-.64s es ambigua"
- swe "Kolumn '%-.64s' i %-.64s är inte unik"
- ukr "óÔÏ×ÂÅÃØ '%-.64s' Õ %-.64s ×ÉÚÎÁÞÅÎÉÊ ÎÅÏÄÎÏÚÎÁÞÎÏ"
+ 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 "úÁ×ÅÒÛÕ¤ÔØÓÑ ÒÁÂÏÔÁ ÓÅÒ×ÅÒÁ"
+ 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 '%-.64s' v %-.64s"
- dan "Ukendt kolonne '%-.64s' i tabel %-.64s"
- nla "Onbekende kolom '%-.64s' in %-.64s"
- eng "Unknown column '%-.64s' in '%-.64s'"
- jps "'%-.64s' column ‚Í '%-.64s' ‚É‚Í‚ ‚è‚Ü‚¹‚ñ.",
- est "Tundmatu tulp '%-.64s' '%-.64s'-s"
- fre "Champ '%-.64s' inconnu dans %-.64s"
- ger "Unbekanntes Tabellenfeld '%-.64s' in %-.64s"
- greek "Áãíùóôï ðåäßï '%-.64s' óå '%-.64s'"
- hun "A(z) '%-.64s' oszlop ervenytelen '%-.64s'-ben"
- ita "Colonna sconosciuta '%-.64s' in '%-.64s'"
- jpn "'%-.64s' column ¤Ï '%-.64s' ¤Ë¤Ï¤¢¤ê¤Þ¤»¤ó."
- kor "Unknown Ä®·³ '%-.64s' in '%-.64s'"
- nor "Ukjent kolonne '%-.64s' i tabell %-.64s"
- norwegian-ny "Ukjent felt '%-.64s' i tabell %-.64s"
- pol "Nieznana kolumna '%-.64s' w %-.64s"
- por "Coluna '%-.64s' desconhecida em '%-.64s'"
- rum "Coloana invalida '%-.64s' in '%-.64s'"
- rus "îÅÉÚ×ÅÓÔÎÙÊ ÓÔÏÌÂÅà '%-.64s' × '%-.64s'"
- serbian "Nepoznata kolona '%-.64s' u '%-.64s'"
- slo "Neznáme pole '%-.64s' v '%-.64s'"
- spa "La columna '%-.64s' en %-.64s es desconocida"
- swe "Okänd kolumn '%-.64s' i %-.64s"
- ukr "îÅצÄÏÍÉÊ ÓÔÏ×ÂÅÃØ '%-.64s' Õ '%-.64s'"
+ 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é '%-.64s' nebylo v group by"
- dan "Brugte '%-.64s' som ikke var i group by"
- nla "Opdracht gebruikt '%-.64s' dat niet in de GROUP BY voorkomt"
- eng "'%-.64s' isn't in GROUP BY"
- jps "'%-.64s' isn't in GROUP BY",
- est "'%-.64s' puudub GROUP BY klauslis"
- fre "'%-.64s' n'est pas dans 'group by'"
- ger "'%-.64s' ist nicht in GROUP BY vorhanden"
- greek "×ñçóéìïðïéÞèçêå '%-.64s' ðïõ äåí õðÞñ÷å óôï group by"
- hun "Used '%-.64s' with wasn't in group by"
- ita "Usato '%-.64s' che non e` nel GROUP BY"
- kor "'%-.64s'Àº GROUP BY¼Ó¿¡ ¾øÀ½"
- nor "Brukte '%-.64s' som ikke var i group by"
- norwegian-ny "Brukte '%-.64s' som ikkje var i group by"
- pol "U¿yto '%-.64s' bez umieszczenia w group by"
- por "'%-.64s' não está em 'GROUP BY'"
- rum "'%-.64s' nu exista in clauza GROUP BY"
- rus "'%-.64s' ÎÅ ÐÒÉÓÕÔÓÔ×ÕÅÔ × GROUP BY"
- serbian "Entitet '%-.64s' nije naveden u komandi 'GROUP BY'"
- slo "Pou¾ité '%-.64s' nebolo v 'group by'"
- spa "Usado '%-.64s' el cual no esta group by"
- swe "'%-.64s' finns inte i GROUP BY"
- ukr "'%-.64s' ÎÅ ¤ Õ GROUP BY"
+ 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 '%-.64s'"
- dan "Kan ikke gruppere på '%-.64s'"
- nla "Kan '%-.64s' niet groeperen"
- eng "Can't group on '%-.64s'"
- est "Ei saa grupeerida '%-.64s' järgi"
- fre "Ne peut regrouper '%-.64s'"
- ger "Gruppierung über '%-.64s' nicht möglich"
- greek "Áäýíáôç ç ïìáäïðïßçóç (group on) '%-.64s'"
- hun "A group nem hasznalhato: '%-.64s'"
- ita "Impossibile raggruppare per '%-.64s'"
- kor "'%-.64s'¸¦ ±×·ìÇÒ ¼ö ¾øÀ½"
- nor "Kan ikke gruppere på '%-.64s'"
- norwegian-ny "Kan ikkje gruppere på '%-.64s'"
- pol "Nie mo¿na grupowaæ po '%-.64s'"
- por "Não pode agrupar em '%-.64s'"
- rum "Nu pot sa grupez pe (group on) '%-.64s'"
- rus "îÅ×ÏÚÍÏÖÎÏ ÐÒÏÉÚ×ÅÓÔÉ ÇÒÕÐÐÉÒÏ×ËÕ ÐÏ '%-.64s'"
- serbian "Ne mogu da grupišem po '%-.64s'"
- slo "Nemô¾em pou¾i» 'group' na '%-.64s'"
- spa "No puedo agrupar por '%-.64s'"
- swe "Kan inte använda GROUP BY med '%-.64s'"
- ukr "îÅ ÍÏÖÕ ÇÒÕÐÕ×ÁÔÉ ÐÏ '%-.64s'"
+ 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 "õ ×ÉÒÁÚ¦ ×ÉËÏÒÉÓÔÁÎÏ Ð¦ÄÓÕÍÏ×ÕÀÞ¦ ÆÕÎËæ§ ÐÏÒÑÄ Ú ¦ÍÅÎÁÍÉ ÓÔÏ×Âæ×"
+ 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 "ë¦ÌØ˦ÓÔØ ÓÔÏ×ÂÃ¦× ÎÅ ÓЦ×ÐÁÄÁ¤ Ú Ë¦ÌØ˦ÓÔÀ ÚÎÁÞÅÎØ"
+ 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' ÚÁÄÏ×ÇÅ"
+ 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 '%-.64s'"
- dan "Feltnavnet '%-.64s' findes allerede"
- nla "Dubbele kolom naam '%-.64s'"
- eng "Duplicate column name '%-.64s'"
- jps "'%-.64s' ‚Æ‚¢‚¤ column –¼‚Íd•¡‚µ‚Ä‚Ü‚·",
- est "Kattuv tulba nimi '%-.64s'"
- fre "Nom du champ '%-.64s' déjà utilisé"
- ger "Doppelter Spaltenname: '%-.64s'"
- greek "ÅðáíÜëçøç column name '%-.64s'"
- hun "Duplikalt oszlopazonosito: '%-.64s'"
- ita "Nome colonna duplicato '%-.64s'"
- jpn "'%-.64s' ¤È¤¤¤¦ column ̾¤Ï½ÅÊ£¤·¤Æ¤Þ¤¹"
- kor "Áߺ¹µÈ Ä®·³ À̸§: '%-.64s'"
- nor "Feltnavnet '%-.64s' eksisterte fra før"
- norwegian-ny "Feltnamnet '%-.64s' eksisterte frå før"
- pol "Powtórzona nazwa kolumny '%-.64s'"
- por "Nome da coluna '%-.64s' duplicado"
- rum "Numele coloanei '%-.64s' e duplicat"
- rus "äÕÂÌÉÒÕÀÝÅÅÓÑ ÉÍÑ ÓÔÏÌÂÃÁ '%-.64s'"
- serbian "Duplirano ime kolone '%-.64s'"
- slo "Opakované meno poµa '%-.64s'"
- spa "Nombre de columna duplicado '%-.64s'"
- swe "Kolumnnamn '%-.64s finns flera gånger"
- ukr "äÕÂÌÀÀÞÅ ¦Í'Ñ ÓÔÏ×ÂÃÑ '%-.64s'"
+ 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 '%-.64s'"
- dan "Indeksnavnet '%-.64s' findes allerede"
- nla "Dubbele zoeksleutel naam '%-.64s'"
- eng "Duplicate key name '%-.64s'"
- jps "'%-.64s' ‚Æ‚¢‚¤ key ‚Ì–¼‘O‚Íd•¡‚µ‚Ä‚¢‚Ü‚·",
- est "Kattuv võtme nimi '%-.64s'"
- fre "Nom de clef '%-.64s' déjà utilisé"
- ger "Doppelter Name für Schlüssel vorhanden: '%-.64s'"
- greek "ÅðáíÜëçøç key name '%-.64s'"
- hun "Duplikalt kulcsazonosito: '%-.64s'"
- ita "Nome chiave duplicato '%-.64s'"
- jpn "'%-.64s' ¤È¤¤¤¦ key ¤Î̾Á°¤Ï½ÅÊ£¤·¤Æ¤¤¤Þ¤¹"
- kor "Áߺ¹µÈ Å° À̸§ : '%-.64s'"
- nor "Nøkkelnavnet '%-.64s' eksisterte fra før"
- norwegian-ny "Nøkkelnamnet '%-.64s' eksisterte frå før"
- pol "Powtórzony nazwa klucza '%-.64s'"
- por "Nome da chave '%-.64s' duplicado"
- rum "Numele cheiei '%-.64s' e duplicat"
- rus "äÕÂÌÉÒÕÀÝÅÅÓÑ ÉÍÑ ËÌÀÞÁ '%-.64s'"
- serbian "Duplirano ime kljuèa '%-.64s'"
- slo "Opakované meno kµúèa '%-.64s'"
- spa "Nombre de clave duplicado '%-.64s'"
- swe "Nyckelnamn '%-.64s' finns flera gånger"
- ukr "äÕÂÌÀÀÞÅ ¦Í'Ñ ËÌÀÞÁ '%-.64s'"
+ 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íè '%-.64s' (èíslo klíèe %d)"
- dan "Ens værdier '%-.64s' for indeks %d"
- nla "Dubbele ingang '%-.64s' voor zoeksleutel %d"
- eng "Duplicate entry '%-.64s' for key %d"
- jps "'%-.64s' ‚Í key %d ‚É‚¨‚¢‚Äd•¡‚µ‚Ä‚¢‚Ü‚·",
- est "Kattuv väärtus '%-.64s' võtmele %d"
- fre "Duplicata du champ '%-.64s' pour la clef %d"
- ger "Doppelter Eintrag '%-.64s' für Schlüssel %d"
- greek "ÄéðëÞ åããñáöÞ '%-.64s' ãéá ôï êëåéäß %d"
- hun "Duplikalt bejegyzes '%-.64s' a %d kulcs szerint."
- ita "Valore duplicato '%-.64s' per la chiave %d"
- jpn "'%-.64s' ¤Ï key %d ¤Ë¤ª¤¤¤Æ½ÅÊ£¤·¤Æ¤¤¤Þ¤¹"
- kor "Áߺ¹µÈ ÀÔ·Â °ª '%-.64s': key %d"
- nor "Like verdier '%-.64s' for nøkkel %d"
- norwegian-ny "Like verdiar '%-.64s' for nykkel %d"
- pol "Powtórzone wyst?pienie '%-.64s' dla klucza %d"
- por "Entrada '%-.64s' duplicada para a chave %d"
- rum "Cimpul '%-.64s' e duplicat pentru cheia %d"
- rus "äÕÂÌÉÒÕÀÝÁÑÓÑ ÚÁÐÉÓØ '%-.64s' ÐÏ ËÌÀÞÕ %d"
- serbian "Dupliran unos '%-.64s' za kljuè '%d'"
- slo "Opakovaný kµúè '%-.64s' (èíslo kµúèa %d)"
- spa "Entrada duplicada '%-.64s' para la clave %d"
- swe "Dubbel nyckel '%-.64s' för nyckel %d"
- ukr "äÕÂÌÀÀÞÉÊ ÚÁÐÉÓ '%-.64s' ÄÌÑ ËÌÀÞÁ %d"
+ 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 '%-.64s'"
- dan "Forkert kolonnespecifikaton for felt '%-.64s'"
- nla "Verkeerde kolom specificatie voor kolom '%-.64s'"
- eng "Incorrect column specifier for column '%-.64s'"
- est "Vigane tulba kirjeldus tulbale '%-.64s'"
- fre "Mauvais paramètre de champ pour le champ '%-.64s'"
- ger "Falsche Spezifikation für Feld '%-.64s'"
- greek "ÅóöáëìÝíï column specifier ãéá ôï ðåäßï '%-.64s'"
- hun "Rossz oszlopazonosito: '%-.64s'"
- ita "Specifica errata per la colonna '%-.64s'"
- kor "Ä®·³ '%-.64s'ÀÇ ºÎÁ¤È®ÇÑ Ä®·³ Á¤ÀÇÀÚ"
- nor "Feil kolonne spesifikator for felt '%-.64s'"
- norwegian-ny "Feil kolonne spesifikator for kolonne '%-.64s'"
- pol "B³êdna specyfikacja kolumny dla kolumny '%-.64s'"
- por "Especificador de coluna incorreto para a coluna '%-.64s'"
- rum "Specificandul coloanei '%-.64s' este incorect"
- rus "îÅËÏÒÒÅËÔÎÙÊ ÏÐÒÅÄÅÌÉÔÅÌØ ÓÔÏÌÂÃÁ ÄÌÑ ÓÔÏÌÂÃÁ '%-.64s'"
- serbian "Pogrešan naziv kolone za kolonu '%-.64s'"
- slo "Chyba v ¹pecifikácii poµa '%-.64s'"
- spa "Especificador de columna erroneo para la columna '%-.64s'"
- swe "Felaktigt kolumntyp för kolumn '%-.64s'"
- ukr "îÅצÒÎÉÊ ÓÐÅÃÉƦËÁÔÏÒ ÓÔÏ×ÂÃÑ '%-.64s'"
-ER_PARSE_ERROR 42000
- 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"
+ 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 "ðÕÓÔÉÊ ÚÁÐÉÔ"
+ 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: '%-.64s'"
- dan "Tabellen/aliaset: '%-.64s' er ikke unikt"
- nla "Niet unieke waarde tabel/alias: '%-.64s'"
- eng "Not unique table/alias: '%-.64s'"
- jps "'%-.64s' ‚͈êˆÓ‚Ì table/alias –¼‚Å‚Í‚ ‚è‚Ü‚¹‚ñ",
- est "Ei ole unikaalne tabel/alias '%-.64s'"
- fre "Table/alias: '%-.64s' non unique"
- ger "Tabellenname/Alias '%-.64s' nicht eindeutig"
- greek "Áäýíáôç ç áíåýñåóç unique table/alias: '%-.64s'"
- hun "Nem egyedi tabla/alias: '%-.64s'"
- ita "Tabella/alias non unico: '%-.64s'"
- jpn "'%-.64s' ¤Ï°ì°Õ¤Î table/alias ̾¤Ç¤Ï¤¢¤ê¤Þ¤»¤ó"
- kor "Unique ÇÏÁö ¾ÊÀº Å×À̺í/alias: '%-.64s'"
- nor "Ikke unikt tabell/alias: '%-.64s'"
- norwegian-ny "Ikkje unikt tabell/alias: '%-.64s'"
- pol "Tabela/alias nie s? unikalne: '%-.64s'"
- por "Tabela/alias '%-.64s' não única"
- rum "Tabela/alias: '%-.64s' nu este unic"
- rus "ðÏ×ÔÏÒÑÀÝÁÑÓÑ ÔÁÂÌÉÃÁ/ÐÓÅ×ÄÏÎÉÍ '%-.64s'"
- serbian "Tabela ili alias nisu bili jedinstveni: '%-.64s'"
- slo "Nie jednoznaèná tabuµka/alias: '%-.64s'"
- spa "Tabla/alias: '%-.64s' es no unica"
- swe "Icke unikt tabell/alias: '%-.64s'"
- ukr "îÅÕΦËÁÌØÎÁ ÔÁÂÌÉÃÑ/ÐÓÅ×ÄÏΦÍ: '%-.64s'"
+ 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 '%-.64s'"
- dan "Ugyldig standardværdi for '%-.64s'"
- nla "Foutieve standaard waarde voor '%-.64s'"
- eng "Invalid default value for '%-.64s'"
- est "Vigane vaikeväärtus '%-.64s' jaoks"
- fre "Valeur par défaut invalide pour '%-.64s'"
- ger "Fehlerhafter Vorgabewert (DEFAULT) für '%-.64s'"
- greek "ÅóöáëìÝíç ðñïêáèïñéóìÝíç ôéìÞ (default value) ãéá '%-.64s'"
- hun "Ervenytelen ertek: '%-.64s'"
- ita "Valore di default non valido per '%-.64s'"
- kor "'%-.64s'ÀÇ À¯È¿ÇÏÁö ¸øÇÑ µðÆúÆ® °ªÀ» »ç¿ëÇϼ̽À´Ï´Ù."
- nor "Ugyldig standardverdi for '%-.64s'"
- norwegian-ny "Ugyldig standardverdi for '%-.64s'"
- pol "Niew³a?ciwa warto?æ domy?lna dla '%-.64s'"
- por "Valor padrão (default) inválido para '%-.64s'"
- rum "Valoarea de default este invalida pentru '%-.64s'"
- rus "îÅËÏÒÒÅËÔÎÏÅ ÚÎÁÞÅÎÉÅ ÐÏ ÕÍÏÌÞÁÎÉÀ ÄÌÑ '%-.64s'"
- serbian "Loša default vrednost za '%-.64s'"
- slo "Chybná implicitná hodnota pre '%-.64s'"
- spa "Valor por defecto invalido para '%-.64s'"
- swe "Ogiltigt DEFAULT värde för '%-.64s'"
- ukr "îÅצÒÎÅ ÚÎÁÞÅÎÎÑ ÐÏ ÚÁÍÏ×ÞÕ×ÁÎÎÀ ÄÌÑ '%-.64s'"
+ 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 "ðÅÒ×ÉÎÎÏÇÏ ËÌÀÞÁ ×ÉÚÎÁÞÅÎÏ ÎÅÏÄÎÏÒÁÚÏ×Ï"
+ 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 ËÌÀÞ¦×"
+ 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 ÞÁÓÔÉÎ"
+ 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 ÂÁÊÔ¦×"
+ 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 '%-.64s' v tabulce neexistuje"
- dan "Nøglefeltet '%-.64s' eksisterer ikke i tabellen"
- nla "Zoeksleutel kolom '%-.64s' bestaat niet in tabel"
- eng "Key column '%-.64s' doesn't exist in table"
- jps "Key column '%-.64s' ‚ªƒe[ƒuƒ‹‚É‚ ‚è‚Ü‚¹‚ñ.",
- est "Võtme tulp '%-.64s' puudub tabelis"
- fre "La clé '%-.64s' n'existe pas dans la table"
- ger "In der Tabelle gibt es kein Schlüsselfeld '%-.64s'"
- greek "Ôï ðåäßï êëåéäß '%-.64s' äåí õðÜñ÷åé óôïí ðßíáêá"
- hun "A(z) '%-.64s'kulcsoszlop nem letezik a tablaban"
- ita "La colonna chiave '%-.64s' non esiste nella tabella"
- jpn "Key column '%-.64s' ¤¬¥Æ¡¼¥Ö¥ë¤Ë¤¢¤ê¤Þ¤»¤ó."
- kor "Key Ä®·³ '%-.64s'´Â Å×ÀÌºí¿¡ Á¸ÀçÇÏÁö ¾Ê½À´Ï´Ù."
- nor "Nøkkel felt '%-.64s' eksiterer ikke i tabellen"
- norwegian-ny "Nykkel kolonne '%-.64s' eksiterar ikkje i tabellen"
- pol "Kolumna '%-.64s' zdefiniowana w kluczu nie istnieje w tabeli"
- por "Coluna chave '%-.64s' não existe na tabela"
- rum "Coloana cheie '%-.64s' nu exista in tabela"
- rus "ëÌÀÞÅ×ÏÊ ÓÔÏÌÂÅà '%-.64s' × ÔÁÂÌÉÃÅ ÎÅ ÓÕÝÅÓÔ×ÕÅÔ"
- serbian "Kljuèna kolona '%-.64s' ne postoji u tabeli"
- slo "Kµúèový ståpec '%-.64s' v tabuµke neexistuje"
- spa "La columna clave '%-.64s' no existe en la tabla"
- swe "Nyckelkolumn '%-.64s' finns inte"
- ukr "ëÌÀÞÏ×ÉÊ ÓÔÏ×ÂÅÃØ '%-.64s' ÎÅ ¦ÓÎÕ¤ Õ ÔÁÂÌÉæ"
+ 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 '%-.64s' nem-Bù¾e být pou¾it jako klíè"
- dan "BLOB feltet '%-.64s' kan ikke bruges ved specifikation af indeks"
- nla "BLOB kolom '%-.64s' kan niet gebruikt worden bij zoeksleutel specificatie"
- eng "BLOB column '%-.64s' can't be used in key specification with the used table type"
- est "BLOB-tüüpi tulpa '%-.64s' ei saa kasutada võtmena"
- fre "Champ BLOB '%-.64s' ne peut être utilisé dans une clé"
- ger "BLOB-Feld '%-.64s' kann beim verwendeten Tabellentyp nicht als Schlüssel verwendet werden"
- greek "Ðåäßï ôýðïõ Blob '%-.64s' äåí ìðïñåß íá ÷ñçóéìïðïéçèåß óôïí ïñéóìü åíüò êëåéäéïý (key specification)"
- hun "Blob objektum '%-.64s' nem hasznalhato kulcskent"
- ita "La colonna BLOB '%-.64s' non puo` essere usata nella specifica della chiave"
- kor "BLOB Ä®·³ '%-.64s'´Â Å° Á¤ÀÇ¿¡¼­ »ç¿ëµÉ ¼ö ¾ø½À´Ï´Ù."
- nor "Blob felt '%-.64s' kan ikke brukes ved spesifikasjon av nøkler"
- norwegian-ny "Blob kolonne '%-.64s' kan ikkje brukast ved spesifikasjon av nyklar"
- pol "Kolumna typu Blob '%-.64s' nie mo¿e byæ u¿yta w specyfikacji klucza"
- por "Coluna BLOB '%-.64s' não pode ser utilizada na especificação de chave para o tipo de tabela usado"
- rum "Coloana de tip BLOB '%-.64s' nu poate fi folosita in specificarea cheii cu tipul de tabla folosit"
- rus "óÔÏÌÂÅà ÔÉÐÁ BLOB '%-.64s' ÎÅ ÍÏÖÅÔ ÂÙÔØ ÉÓÐÏÌØÚÏ×ÁÎ ËÁË ÚÎÁÞÅÎÉÅ ËÌÀÞÁ × ÔÁÂÌÉÃÅ ÔÁËÏÇÏ ÔÉÐÁ"
- serbian "BLOB kolona '%-.64s' ne može biti upotrebljena za navoðenje kljuèa sa tipom tabele koji se trenutno koristi"
- slo "Blob pole '%-.64s' nemô¾e by» pou¾ité ako kµúè"
- spa "La columna Blob '%-.64s' no puede ser usada en una declaracion de clave"
- swe "En BLOB '%-.64s' kan inte vara nyckel med den använda tabelltypen"
- ukr "BLOB ÓÔÏ×ÂÅÃØ '%-.64s' ÎÅ ÍÏÖÅ ÂÕÔÉ ×ÉËÏÒÉÓÔÁÎÉÊ Õ ×ÉÚÎÁÞÅÎΦ ËÌÀÞÁ × ÃØÏÍÕ ÔÉЦ ÔÁÂÌÉæ"
+ 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 '%-.64s' (nejvíce %lu). Pou¾ijte BLOB"
- dan "For stor feltlængde for kolonne '%-.64s' (maks = %lu). Brug BLOB i stedet"
- nla "Te grote kolomlengte voor '%-.64s' (max = %lu). Maak hiervoor gebruik van het type BLOB"
- eng "Column length too big for column '%-.64s' (max = %lu); use BLOB or TEXT instead"
- jps "column '%-.64s' ‚Í,Šm•Û‚·‚é column ‚Ì‘å‚«‚³‚ª‘½‚·‚¬‚Ü‚·. (Å‘å %lu ‚Ü‚Å). BLOB ‚ð‚©‚í‚è‚ÉŽg—p‚µ‚Ä‚­‚¾‚³‚¢.",
- est "Tulba '%-.64s' pikkus on liiga pikk (maksimaalne pikkus: %lu). Kasuta BLOB väljatüüpi"
- fre "Champ '%-.64s' trop long (max = %lu). Utilisez un BLOB"
- ger "Feldlänge für Feld '%-.64s' zu groß (maximal %lu). BLOB- oder TEXT-Spaltentyp verwenden!"
- greek "Ðïëý ìåãÜëï ìÞêïò ãéá ôï ðåäßï '%-.64s' (max = %lu). Ðáñáêáëþ ÷ñçóéìïðïéåßóôå ôïí ôýðï BLOB"
- hun "A(z) '%-.64s' oszlop tul hosszu. (maximum = %lu). Hasznaljon BLOB tipust inkabb."
- ita "La colonna '%-.64s' e` troppo grande (max=%lu). Utilizza un BLOB."
- jpn "column '%-.64s' ¤Ï,³ÎÊݤ¹¤ë column ¤ÎÂ礭¤µ¤¬Â¿¤¹¤®¤Þ¤¹. (ºÇÂç %lu ¤Þ¤Ç). BLOB ¤ò¤«¤ï¤ê¤Ë»ÈÍѤ·¤Æ¤¯¤À¤µ¤¤."
- kor "Ä®·³ '%-.64s'ÀÇ Ä®·³ ±æÀÌ°¡ ³Ê¹« ±é´Ï´Ù (ÃÖ´ë = %lu). ´ë½Å¿¡ BLOB¸¦ »ç¿ëÇϼ¼¿ä."
- nor "For stor nøkkellengde for kolonne '%-.64s' (maks = %lu). Bruk BLOB istedenfor"
- norwegian-ny "For stor nykkellengde for felt '%-.64s' (maks = %lu). Bruk BLOB istadenfor"
- pol "Zbyt du¿a d³ugo?æ kolumny '%-.64s' (maks. = %lu). W zamian u¿yj typu BLOB"
- por "Comprimento da coluna '%-.64s' grande demais (max = %lu); use BLOB em seu lugar"
- rum "Lungimea coloanei '%-.64s' este prea lunga (maximum = %lu). Foloseste BLOB mai bine"
- rus "óÌÉÛËÏÍ ÂÏÌØÛÁÑ ÄÌÉÎÁ ÓÔÏÌÂÃÁ '%-.64s' (ÍÁËÓÉÍÕÍ = %lu). éÓÐÏÌØÚÕÊÔÅ ÔÉÐ BLOB ÉÌÉ TEXT ×ÍÅÓÔÏ ÔÅËÕÝÅÇÏ"
- serbian "Previše podataka za kolonu '%-.64s' (maksimum je %lu). Upotrebite BLOB polje"
- slo "Príli¹ veµká då¾ka pre pole '%-.64s' (maximum = %lu). Pou¾ite BLOB"
- spa "Longitud de columna demasiado grande para la columna '%-.64s' (maximo = %lu).Usar BLOB en su lugar"
- swe "För stor kolumnlängd angiven för '%-.64s' (max= %lu). Använd en BLOB instället"
- ukr "úÁÄÏ×ÇÁ ÄÏ×ÖÉÎÁ ÓÔÏ×ÂÃÑ '%-.64s' (max = %lu). ÷ÉËÏÒÉÓÔÁÊÔÅ ÔÉÐ BLOB"
+ 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 "îÅצÒÎÅ ×ÉÚÎÁÞÅÎÎÑ ÔÁÂÌÉæ; íÏÖÅ ÂÕÔÉ ÌÉÛÅ ÏÄÉÎ Á×ÔÏÍÁÔÉÞÎÉÊ ÓÔÏ×ÂÅÃØ, ÝÏ ÐÏ×ÉÎÅÎ ÂÕÔÉ ×ÉÚÎÁÞÅÎÉÊ ÑË ËÌÀÞ"
+ 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 connections\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"
+ 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"
+ 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"
+ 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"
+ 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 '%-.32s'\n"
- dan "%s: Forceret nedlukning af tråd: %ld bruger: '%-.32s'\n"
- nla "%s: Afsluiten afgedwongen van thread %ld gebruiker: '%-.32s'\n"
- eng "%s: Forcing close of thread %ld user: '%-.32s'\n"
- jps "%s: ƒXƒŒƒbƒh %ld ‹­§I—¹ user: '%-.32s'\n",
- est "%s: Sulgen jõuga lõime %ld kasutaja: '%-.32s'\n"
- fre "%s: Arrêt forcé de la tâche (thread) %ld utilisateur: '%-.32s'\n"
- ger "%s: Thread %ld zwangsweise beendet. Benutzer: '%-.32s'\n"
- greek "%s: Ôï thread èá êëåßóåé %ld user: '%-.32s'\n"
- hun "%s: A(z) %ld thread kenyszeritett zarasa. Felhasznalo: '%-.32s'\n"
- ita "%s: Forzata la chiusura del thread %ld utente: '%-.32s'\n"
- jpn "%s: ¥¹¥ì¥Ã¥É %ld ¶¯À©½ªÎ» user: '%-.32s'\n"
- kor "%s: thread %ldÀÇ °­Á¦ Á¾·á user: '%-.32s'\n"
- nor "%s: Påtvinget avslutning av tråd %ld bruker: '%-.32s'\n"
- norwegian-ny "%s: Påtvinga avslutning av tråd %ld brukar: '%-.32s'\n"
- pol "%s: Wymuszenie zamkniêcia w?tku %ld u¿ytkownik: '%-.32s'\n"
- por "%s: Forçando finalização da 'thread' %ld - usuário '%-.32s'\n"
- rum "%s: Terminare fortata a thread-ului %ld utilizatorului: '%-.32s'\n"
- rus "%s: ðÒÉÎÕÄÉÔÅÌØÎÏ ÚÁËÒÙ×ÁÅÍ ÐÏÔÏË %ld ÐÏÌØÚÏ×ÁÔÅÌÑ: '%-.32s'\n"
- serbian "%s: Usiljeno gašenje thread-a %ld koji pripada korisniku: '%-.32s'\n"
- slo "%s: násilné ukonèenie vlákna %ld u¾ívateµa '%-.32s'\n"
- spa "%s: Forzando a cerrar el thread %ld usuario: '%-.32s'\n"
- swe "%s: Stänger av tråd %ld; användare: '%-.32s'\n"
- ukr "%s: ðÒÉÓËÏÒÀÀ ÚÁËÒÉÔÔÑ Ç¦ÌËÉ %ld ËÏÒÉÓÔÕ×ÁÞÁ: '%-.32s'\n"
+ 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 connection 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 ÒÏÚ'¤Í"
+ 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 '%-.64s' nem-Bá index odpovídající CREATE INDEX. Vytvoøte tabulku znovu"
- dan "Tabellen '%-.64s' har ikke den nøgle, som blev brugt i CREATE INDEX. Genopret tabellen"
- nla "Tabel '%-.64s' heeft geen INDEX zoals deze gemaakt worden met CREATE INDEX. Maak de tabel opnieuw"
- eng "Table '%-.64s' has no index like the one used in CREATE INDEX; recreate the table"
- jps "Table '%-.64s' ‚Í‚»‚̂悤‚È index ‚ðŽ‚Á‚Ä‚¢‚Ü‚¹‚ñ(CREATE INDEX ŽÀsŽž‚ÉŽw’肳‚ê‚Ä‚¢‚Ü‚¹‚ñ). ƒe[ƒuƒ‹‚ðì‚è’¼‚µ‚Ä‚­‚¾‚³‚¢",
- est "Tabelil '%-.64s' puuduvad võtmed. Loo tabel uuesti"
- fre "La table '%-.64s' n'a pas d'index comme celle utilisée dans CREATE INDEX. Recréez la table"
- ger "Tabelle '%-.64s' besitzt keinen wie den in CREATE INDEX verwendeten Index. Tabelle neu anlegen"
- greek "Ï ðßíáêáò '%-.64s' äåí Ý÷åé åõñåôÞñéï (index) óáí áõôü ðïõ ÷ñçóéìïðïéåßôå óôçí CREATE INDEX. Ðáñáêáëþ, îáíáäçìéïõñãÞóôå ôïí ðßíáêá"
- hun "A(z) '%-.64s' tablahoz nincs meg a CREATE INDEX altal hasznalt index. Alakitsa at a tablat"
- ita "La tabella '%-.64s' non ha nessun indice come quello specificatato dalla CREATE INDEX. Ricrea la tabella"
- jpn "Table '%-.64s' ¤Ï¤½¤Î¤è¤¦¤Ê index ¤ò»ý¤Ã¤Æ¤¤¤Þ¤»¤ó(CREATE INDEX ¼Â¹Ô»þ¤Ë»ØÄꤵ¤ì¤Æ¤¤¤Þ¤»¤ó). ¥Æ¡¼¥Ö¥ë¤òºî¤êľ¤·¤Æ¤¯¤À¤µ¤¤"
- kor "Å×À̺í '%-.64s'´Â À妽º¸¦ ¸¸µéÁö ¾Ê¾Ò½À´Ï´Ù. alter Å×À̺í¸í·ÉÀ» ÀÌ¿ëÇÏ¿© Å×À̺íÀ» ¼öÁ¤Çϼ¼¿ä..."
- nor "Tabellen '%-.64s' har ingen index som den som er brukt i CREATE INDEX. Gjenopprett tabellen"
- norwegian-ny "Tabellen '%-.64s' har ingen index som den som er brukt i CREATE INDEX. Oprett tabellen på nytt"
- pol "Tabela '%-.64s' nie ma indeksu takiego jak w CREATE INDEX. Stwórz tabelê"
- por "Tabela '%-.64s' não possui um índice como o usado em CREATE INDEX. Recrie a tabela"
- rum "Tabela '%-.64s' nu are un index ca acela folosit in CREATE INDEX. Re-creeaza tabela"
- rus "÷ ÔÁÂÌÉÃÅ '%-.64s' ÎÅÔ ÔÁËÏÇÏ ÉÎÄÅËÓÁ, ËÁË × CREATE INDEX. óÏÚÄÁÊÔÅ ÔÁÂÌÉÃÕ ÚÁÎÏ×Ï"
- serbian "Tabela '%-.64s' nema isti indeks kao onaj upotrebljen pri komandi 'CREATE INDEX'. Napravite tabelu ponovo"
- slo "Tabuµka '%-.64s' nemá index zodpovedajúci CREATE INDEX. Vytvorte tabulku znova"
- spa "La tabla '%-.64s' no tiene indice como el usado en CREATE INDEX. Crea de nuevo la tabla"
- swe "Tabellen '%-.64s' har inget index som motsvarar det angivna i CREATE INDEX. Skapa om tabellen"
- ukr "ôÁÂÌÉÃÑ '%-.64s' ÍÁ¤ ¦ÎÄÅËÓ, ÝÏ ÎÅ ÓЦ×ÐÁÄÁ¤ Ú ×ËÁÚÁÎÎÉÍ Õ CREATE INDEX. óÔ×ÏÒ¦ÔØ ÔÁÂÌÉÃÀ ÚÎÏ×Õ"
+ 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 "èÉÂÎÉÊ ÒÏÚĦÌÀ×ÁÞ ÐÏ̦×. ðÏÞÉÔÁÊÔÅ ÄÏËÕÍÅÎÔÁæÀ"
+ 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'"
+ 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 og 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' ÐÏ×ÉÎÅÎ ÂÕÔÉ Õ ÔÅæ ÂÁÚÉ ÄÁÎÎÉÈ ÁÂÏ ÍÁÔÉ ×ÓÔÁÎÏ×ÌÅÎÅ ÐÒÁ×Ï ÎÁ ÞÉÔÁÎÎÑ ÄÌÑ ÕÓ¦È"
+ 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 og 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' ×ÖÅ ¦ÓÎÕ¤"
+ 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"
+ 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"
+ 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 sub part key; the used key part isn't a string, the used length is longer than the key part, or the storage engine doesn't support unique sub keys"
- est "Vigane võtme osa. Kasutatud võtmeosa ei ole string tüüpi, määratud pikkus on pikem kui võtmeosa või tabelihandler ei toeta seda tüüpi võtmeid"
- fre "Mauvaise sous-clef. Ce n'est pas un 'string' ou la longueur dépasse celle définie dans la clef"
- ger "Falscher Unterteilschlüssel. Der verwendete Schlüsselteil ist entweder kein String, die verwendete Länge ist länger als der Teilschlüssel oder die Speicher-Engine unterstützt keine Unterteilschlüssel"
- greek "ÅóöáëìÝíï sub part key. Ôï ÷ñçóéìïðïéïýìåíï key part äåí åßíáé string Þ ôï ìÞêïò ôïõ åßíáé ìåãáëýôåñï"
- hun "Rossz alkulcs. A hasznalt kulcsresz nem karaktersorozat vagy hosszabb, mint a kulcsresz"
- ita "Sotto-parte della chiave errata. La parte di chiave utilizzata non e` una stringa o la lunghezza e` maggiore della parte di chiave."
- jpn "Incorrect sub part key; the used key part isn't a string or the used length is longer than the key part"
- 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 sub part 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 "îÅצÒÎÁ ÞÁÓÔÉÎÁ ËÌÀÞÁ. ÷ÉËÏÒÉÓÔÁÎÁ ÞÁÓÔÉÎÁ ËÌÀÞÁ ÎÅ ¤ ÓÔÒÏËÏÀ, ÚÁÄÏ×ÇÁ ÁÂÏ ×ËÁÚ¦×ÎÉË ÔÁÂÌÉæ ΊЦÄÔÒÉÍÕ¤ ÕΦËÁÌØÎÉÈ ÞÁÓÔÉÎ ËÌÀÞÅÊ"
+ 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"
+ 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 '%-.64s' (provést DROP). Zkontrolujte, zda neexistují záznamy/klíèe"
- dan "Kan ikke udføre DROP '%-.64s'. Undersøg om feltet/nøglen eksisterer."
- nla "Kan '%-.64s' niet weggooien. Controleer of het veld of de zoeksleutel daadwerkelijk bestaat."
- eng "Can't DROP '%-.64s'; check that column/key exists"
- jps "'%-.64s' ‚ð”jŠü‚Å‚«‚Ü‚¹‚ñ‚Å‚µ‚½; check that column/key exists",
- est "Ei suuda kustutada '%-.64s'. Kontrolli kas tulp/võti eksisteerib"
- fre "Ne peut effacer (DROP) '%-.64s'. Vérifiez s'il existe"
- ger "Kann '%-.64s' nicht löschen. Existiert die Spalte oder der Schlüssel?"
- greek "Áäýíáôç ç äéáãñáöÞ (DROP) '%-.64s'. Ðáñáêáëþ åëÝãîôå áí ôï ðåäßï/êëåéäß õðÜñ÷åé"
- hun "A DROP '%-.64s' nem lehetseges. Ellenorizze, hogy a mezo/kulcs letezik-e"
- ita "Impossibile cancellare '%-.64s'. Controllare che il campo chiave esista"
- jpn "'%-.64s' ¤òÇË´þ¤Ç¤­¤Þ¤»¤ó¤Ç¤·¤¿; check that column/key exists"
- kor "'%-.64s'¸¦ DROPÇÒ ¼ö ¾ø½À´Ï´Ù. Ä®·³À̳ª Å°°¡ Á¸ÀçÇÏ´ÂÁö äũÇϼ¼¿ä."
- nor "Kan ikke DROP '%-.64s'. Undersøk om felt/nøkkel eksisterer."
- norwegian-ny "Kan ikkje DROP '%-.64s'. Undersøk om felt/nøkkel eksisterar."
- pol "Nie mo¿na wykonaæ operacji DROP '%-.64s'. Sprawd¥, czy to pole/klucz istnieje"
- por "Não se pode fazer DROP '%-.64s'. Confira se esta coluna/chave existe"
- rum "Nu pot sa DROP '%-.64s'. Verifica daca coloana/cheia exista"
- rus "îÅ×ÏÚÍÏÖÎÏ ÕÄÁÌÉÔØ (DROP) '%-.64s'. õÂÅÄÉÔÅÓØ ÞÔÏ ÓÔÏÌÂÅÃ/ËÌÀÞ ÄÅÊÓÔ×ÉÔÅÌØÎÏ ÓÕÝÅÓÔ×ÕÅÔ"
- serbian "Ne mogu da izvršim komandu drop 'DROP' na '%-.64s'. Proverite da li ta kolona (odnosno kljuè) postoji"
- slo "Nemô¾em zru¹i» (DROP) '%-.64s'. Skontrolujte, èi neexistujú záznamy/kµúèe"
- spa "No puedo ELIMINAR '%-.64s'. compuebe que el campo/clave existe"
- swe "Kan inte ta bort '%-.64s'. Kontrollera att fältet/nyckel finns"
- ukr "îÅ ÍÏÖÕ DROP '%-.64s'. ðÅÒÅצÒÔÅ, ÞÉ ÃÅÊ ÓÔÏ×ÂÅÃØ/ËÌÀÞ ¦ÓÎÕ¤"
+ 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"
+ 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 '%-.64s' for update in FROM clause"
- ger "Die Verwendung der zu aktualisierenden Zieltabelle '%-.64s' ist in der FROM-Klausel nicht zulässig."
- rus "îÅ ÄÏÐÕÓËÁÅÔÓÑ ÕËÁÚÁÎÉÅ ÔÁÂÌÉÃÙ '%-.64s' × ÓÐÉÓËÅ ÔÁÂÌÉà FROM ÄÌÑ ×ÎÅÓÅÎÉÑ × ÎÅÅ ÉÚÍÅÎÅÎÉÊ"
- swe "INSERT-table '%-.64s' får inte finnas i FROM tabell-listan"
- ukr "ôÁÂÌÉÃÑ '%-.64s' ÝÏ ÚͦÎÀ¤ÔØÓÑ ÎÅ ÄÏÚ×ÏÌÅÎÁ Õ ÐÅÒÅ̦ËÕ ÔÁÂÌÉÃØ FROM"
+ 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"
+ 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"
+ 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 "îÅ ×ÉËÏÒÉÓÔÁÎÏ ÔÁÂÌÉÃØ"
+ 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 %-.64s a SET"
- dan "For mange tekststrenge til specifikationen af SET i kolonne %-.64s"
- nla "Teveel strings voor kolom %-.64s en SET"
- eng "Too many strings for column %-.64s and SET"
- est "Liiga palju string tulbale %-.64s tüübile SET"
- fre "Trop de chaînes dans la colonne %-.64s avec SET"
- ger "Zu viele Strings für Feld %-.64s und SET angegeben"
- greek "ÐÜñá ðïëëÜ strings ãéá ôï ðåäßï %-.64s êáé SET"
- hun "Tul sok karakter: %-.64s es SET"
- ita "Troppe stringhe per la colonna %-.64s e la SET"
- kor "Ä®·³ %-.64s¿Í SET¿¡¼­ ½ºÆ®¸µÀÌ ³Ê¹« ¸¹½À´Ï´Ù."
- nor "For mange tekststrenger kolonne %-.64s og SET"
- norwegian-ny "For mange tekststrengar felt %-.64s og SET"
- pol "Zbyt wiele ³añcuchów dla kolumny %-.64s i polecenia SET"
- por "'Strings' demais para coluna '%-.64s' e SET"
- rum "Prea multe siruri pentru coloana %-.64s si SET"
- rus "óÌÉÛËÏÍ ÍÎÏÇÏ ÚÎÁÞÅÎÉÊ ÄÌÑ ÓÔÏÌÂÃÁ %-.64s × SET"
- serbian "Previše string-ova za kolonu '%-.64s' i komandu 'SET'"
- slo "Príli¹ mnoho re»azcov pre pole %-.64s a SET"
- spa "Muchas strings para columna %-.64s y SET"
- swe "För många alternativ till kolumn %-.64s för SET"
- ukr "úÁÂÁÇÁÔÏ ÓÔÒÏË ÄÌÑ ÓÔÏ×ÂÃÑ %-.64s ÔÁ 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"
+ 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 '%-.64s' byla zam-Bèena s READ a nemù¾e být zmìnìna"
- dan "Tabellen '%-.64s' var låst med READ lås og kan ikke opdateres"
- nla "Tabel '%-.64s' was gelocked met een lock om te lezen. Derhalve kunnen geen wijzigingen worden opgeslagen."
- eng "Table '%-.64s' was locked with a READ lock and can't be updated"
- jps "Table '%-.64s' ‚Í READ lock ‚É‚È‚Á‚Ä‚¢‚ÄAXV‚Í‚Å‚«‚Ü‚¹‚ñ",
- est "Tabel '%-.64s' on lukustatud READ lukuga ning ei ole muudetav"
- fre "Table '%-.64s' verrouillée lecture (READ): modification impossible"
- ger "Tabelle '%-.64s' ist mit Lesesperre versehen und kann nicht aktualisiert werden"
- greek "Ï ðßíáêáò '%-.64s' Ý÷åé êëåéäùèåß ìå READ lock êáé äåí åðéôñÝðïíôáé áëëáãÝò"
- hun "A(z) '%-.64s' tabla zarolva lett (READ lock) es nem lehet frissiteni"
- ita "La tabella '%-.64s' e` soggetta a lock in lettura e non puo` essere aggiornata"
- jpn "Table '%-.64s' ¤Ï READ lock ¤Ë¤Ê¤Ã¤Æ¤¤¤Æ¡¢¹¹¿·¤Ï¤Ç¤­¤Þ¤»¤ó"
- kor "Å×À̺í '%-.64s'´Â READ ¶ôÀÌ Àá°ÜÀ־ °»½ÅÇÒ ¼ö ¾ø½À´Ï´Ù."
- nor "Tabellen '%-.64s' var låst med READ lås og kan ikke oppdateres"
- norwegian-ny "Tabellen '%-.64s' var låst med READ lås og kan ikkje oppdaterast"
- pol "Tabela '%-.64s' zosta³a zablokowana przez READ i nie mo¿e zostaæ zaktualizowana"
- por "Tabela '%-.64s' foi travada com trava de leitura e não pode ser atualizada"
- rum "Tabela '%-.64s' a fost locked cu un READ lock si nu poate fi actualizata"
- rus "ôÁÂÌÉÃÁ '%-.64s' ÚÁÂÌÏËÉÒÏ×ÁÎÁ ÕÒÏ×ÎÅÍ READ lock É ÎÅ ÍÏÖÅÔ ÂÙÔØ ÉÚÍÅÎÅÎÁ"
- serbian "Tabela '%-.64s' je zakljuèana READ lock-om; iz nje se može samo èitati ali u nju se ne može pisati"
- slo "Tabuµka '%-.64s' bola zamknutá s READ a nemô¾e by» zmenená"
- spa "Tabla '%-.64s' fue trabada con un READ lock y no puede ser actualizada"
- swe "Tabell '%-.64s' kan inte uppdateras emedan den är låst för läsning"
- ukr "ôÁÂÌÉÃÀ '%-.64s' ÚÁÂÌÏËÏ×ÁÎÏ Ô¦ÌØËÉ ÄÌÑ ÞÉÔÁÎÎÑ, ÔÏÍÕ §§ ÎÅ ÍÏÖÎÁ ÏÎÏ×ÉÔÉ"
+ 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 '%-.64s' nebyla zam-Bèena s LOCK TABLES"
- dan "Tabellen '%-.64s' var ikke låst med LOCK TABLES"
- nla "Tabel '%-.64s' was niet gelocked met LOCK TABLES"
- eng "Table '%-.64s' was not locked with LOCK TABLES"
- jps "Table '%-.64s' ‚Í LOCK TABLES ‚É‚æ‚Á‚ăƒbƒN‚³‚ê‚Ä‚¢‚Ü‚¹‚ñ",
- est "Tabel '%-.64s' ei ole lukustatud käsuga LOCK TABLES"
- fre "Table '%-.64s' non verrouillée: utilisez LOCK TABLES"
- ger "Tabelle '%-.64s' wurde nicht mit LOCK TABLES gesperrt"
- greek "Ï ðßíáêáò '%-.64s' äåí Ý÷åé êëåéäùèåß ìå LOCK TABLES"
- hun "A(z) '%-.64s' tabla nincs zarolva a LOCK TABLES-szel"
- ita "Non e` stato impostato il lock per la tabella '%-.64s' con LOCK TABLES"
- jpn "Table '%-.64s' ¤Ï LOCK TABLES ¤Ë¤è¤Ã¤Æ¥í¥Ã¥¯¤µ¤ì¤Æ¤¤¤Þ¤»¤ó"
- kor "Å×À̺í '%-.64s'´Â LOCK TABLES ¸í·ÉÀ¸·Î Àá±âÁö ¾Ê¾Ò½À´Ï´Ù."
- nor "Tabellen '%-.64s' var ikke låst med LOCK TABLES"
- norwegian-ny "Tabellen '%-.64s' var ikkje låst med LOCK TABLES"
- pol "Tabela '%-.64s' nie zosta³a zablokowana poleceniem LOCK TABLES"
- por "Tabela '%-.64s' não foi travada com LOCK TABLES"
- rum "Tabela '%-.64s' nu a fost locked cu LOCK TABLES"
- rus "ôÁÂÌÉÃÁ '%-.64s' ÎÅ ÂÙÌÁ ÚÁÂÌÏËÉÒÏ×ÁÎÁ Ó ÐÏÍÏÝØÀ LOCK TABLES"
- serbian "Tabela '%-.64s' nije bila zakljuèana komandom 'LOCK TABLES'"
- slo "Tabuµka '%-.64s' nebola zamknutá s LOCK TABLES"
- spa "Tabla '%-.64s' no fue trabada con LOCK TABLES"
- swe "Tabell '%-.64s' är inte låst med LOCK TABLES"
- ukr "ôÁÂÌÉÃÀ '%-.64s' ÎÅ ÂÕÌÏ ÂÌÏËÏ×ÁÎÏ Ú LOCK TABLES"
+ 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 '%-.64s' nemù¾e mít defaultní hodnotu"
- dan "BLOB feltet '%-.64s' kan ikke have en standard værdi"
- nla "Blob veld '%-.64s' can geen standaardwaarde bevatten"
- eng "BLOB/TEXT column '%-.64s' can't have a default value"
- est "BLOB-tüüpi tulp '%-.64s' ei saa omada vaikeväärtust"
- fre "BLOB '%-.64s' ne peut avoir de valeur par défaut"
- ger "BLOB/TEXT-Feld '%-.64s' darf keinen Vorgabewert (DEFAULT) haben"
- greek "Ôá Blob ðåäßá '%-.64s' äåí ìðïñïýí íá Ý÷ïõí ðñïêáèïñéóìÝíåò ôéìÝò (default value)"
- hun "A(z) '%-.64s' blob objektumnak nem lehet alapertelmezett erteke"
- ita "Il campo BLOB '%-.64s' non puo` avere un valore di default"
- jpn "BLOB column '%-.64s' can't have a default value"
- kor "BLOB Ä®·³ '%-.64s' ´Â µðÆúÆ® °ªÀ» °¡Áú ¼ö ¾ø½À´Ï´Ù."
- nor "Blob feltet '%-.64s' kan ikke ha en standard verdi"
- norwegian-ny "Blob feltet '%-.64s' kan ikkje ha ein standard verdi"
- pol "Pole typu blob '%-.64s' nie mo¿e mieæ domy?lnej warto?ci"
- por "Coluna BLOB '%-.64s' não pode ter um valor padrão (default)"
- rum "Coloana BLOB '%-.64s' nu poate avea o valoare default"
- rus "îÅ×ÏÚÍÏÖÎÏ ÕËÁÚÙ×ÁÔØ ÚÎÁÞÅÎÉÅ ÐÏ ÕÍÏÌÞÁÎÉÀ ÄÌÑ ÓÔÏÌÂÃÁ BLOB '%-.64s'"
- serbian "BLOB kolona '%-.64s' ne može imati default vrednost"
- slo "Pole BLOB '%-.64s' nemô¾e ma» implicitnú hodnotu"
- spa "Campo Blob '%-.64s' no puede tener valores patron"
- swe "BLOB fält '%-.64s' kan inte ha ett DEFAULT-värde"
- ukr "óÔÏ×ÂÅÃØ BLOB '%-.64s' ÎÅ ÍÏÖÅ ÍÁÔÉ ÚÎÁÞÅÎÎÑ ÐÏ ÚÁÍÏ×ÞÕ×ÁÎÎÀ"
+ 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'"
+ 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'"
+ 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 SQL_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 SQL_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 ¤ צÒÎÉÍ"
+ 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 SQL_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 SQL_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 "îÅצÄÏÍÁ ÐÏÍÉÌËÁ"
+ 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 %-.64s"
- dan "Ukendt procedure %-.64s"
- nla "Onbekende procedure %-.64s"
- eng "Unknown procedure '%-.64s'"
- est "Tundmatu protseduur '%-.64s'"
- fre "Procédure %-.64s inconnue"
- ger "Unbekannte Prozedur '%-.64s'"
- greek "Áãíùóôç äéáäéêáóßá '%-.64s'"
- hun "Ismeretlen eljaras: '%-.64s'"
- ita "Procedura '%-.64s' sconosciuta"
- kor "¾Ë¼ö ¾ø´Â ¼öÇ๮ : '%-.64s'"
- nor "Ukjent prosedyre %-.64s"
- norwegian-ny "Ukjend prosedyre %-.64s"
- pol "Unkown procedure %-.64s"
- por "'Procedure' '%-.64s' desconhecida"
- rum "Procedura unknown '%-.64s'"
- rus "îÅÉÚ×ÅÓÔÎÁÑ ÐÒÏÃÅÄÕÒÁ '%-.64s'"
- serbian "Nepoznata procedura '%-.64s'"
- slo "Neznámá procedúra '%-.64s'"
- spa "Procedimiento desconocido %-.64s"
- swe "Okänd procedur: %-.64s"
- ukr "îÅצÄÏÍÁ ÐÒÏÃÅÄÕÒÁ '%-.64s'"
+ 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 %-.64s"
- dan "Forkert antal parametre til proceduren %-.64s"
- nla "Foutief aantal parameters doorgegeven aan procedure %-.64s"
- eng "Incorrect parameter count to procedure '%-.64s'"
- est "Vale parameetrite hulk protseduurile '%-.64s'"
- fre "Mauvais nombre de paramètres pour la procedure %-.64s"
- ger "Falsche Parameterzahl für Prozedur '%-.64s'"
- greek "ËÜèïò áñéèìüò ðáñáìÝôñùí óôç äéáäéêáóßá '%-.64s'"
- hun "Rossz parameter a(z) '%-.64s'eljaras szamitasanal"
- ita "Numero di parametri errato per la procedura '%-.64s'"
- kor "'%-.64s' ¼öÇ๮¿¡ ´ëÇÑ ºÎÁ¤È®ÇÑ ÆĶó¸ÞÅÍ"
- nor "Feil parameter antall til prosedyren %-.64s"
- norwegian-ny "Feil parameter tal til prosedyra %-.64s"
- pol "Incorrect parameter count to procedure %-.64s"
- por "Número de parâmetros incorreto para a 'procedure' '%-.64s'"
- rum "Procedura '%-.64s' are un numar incorect de parametri"
- rus "îÅËÏÒÒÅËÔÎÏÅ ËÏÌÉÞÅÓÔ×Ï ÐÁÒÁÍÅÔÒÏ× ÄÌÑ ÐÒÏÃÅÄÕÒÙ '%-.64s'"
- serbian "Pogrešan broj parametara za proceduru '%-.64s'"
- slo "Chybný poèet parametrov procedúry '%-.64s'"
- spa "Equivocado parametro count para procedimiento %-.64s"
- swe "Felaktigt antal parametrar till procedur %-.64s"
- ukr "èÉÂÎÁ ˦ÌØ˦ÓÔØ ÐÁÒÁÍÅÔÒ¦× ÐÒÏÃÅÄÕÒÉ '%-.64s'"
+ 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 %-.64s"
- dan "Forkert(e) parametre til proceduren %-.64s"
- nla "Foutieve parameters voor procedure %-.64s"
- eng "Incorrect parameters to procedure '%-.64s'"
- est "Vigased parameetrid protseduurile '%-.64s'"
- fre "Paramètre erroné pour la procedure %-.64s"
- ger "Falsche Parameter für Prozedur '%-.64s'"
- greek "ËÜèïò ðáñÜìåôñïé óôçí äéáäéêáóßá '%-.64s'"
- hun "Rossz parameter a(z) '%-.64s' eljarasban"
- ita "Parametri errati per la procedura '%-.64s'"
- kor "'%-.64s' ¼öÇ๮¿¡ ´ëÇÑ ºÎÁ¤È®ÇÑ ÆĶó¸ÞÅÍ"
- nor "Feil parametre til prosedyren %-.64s"
- norwegian-ny "Feil parameter til prosedyra %-.64s"
- pol "Incorrect parameters to procedure %-.64s"
- por "Parâmetros incorretos para a 'procedure' '%-.64s'"
- rum "Procedura '%-.64s' are parametrii incorecti"
- rus "îÅËÏÒÒÅËÔÎÙÅ ÐÁÒÁÍÅÔÒÙ ÄÌÑ ÐÒÏÃÅÄÕÒÙ '%-.64s'"
- serbian "Pogrešni parametri prosleðeni proceduri '%-.64s'"
- slo "Chybné parametre procedúry '%-.64s'"
- spa "Equivocados parametros para procedimiento %-.64s"
- swe "Felaktiga parametrar till procedur %-.64s"
- ukr "èÉÂÎÉÊ ÐÁÒÁÍÅÔÅÒ ÐÒÏÃÅÄÕÒÉ '%-.64s'"
+ 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 '%-.64s' v %-.32s"
- dan "Ukendt tabel '%-.64s' i %-.32s"
- nla "Onbekende tabel '%-.64s' in %-.32s"
- eng "Unknown table '%-.64s' in %-.32s"
- est "Tundmatu tabel '%-.64s' %-.32s-s"
- fre "Table inconnue '%-.64s' dans %-.32s"
- ger "Unbekannte Tabelle '%-.64s' in '%-.32s'"
- greek "Áãíùóôïò ðßíáêáò '%-.64s' óå %-.32s"
- hun "Ismeretlen tabla: '%-.64s' %-.32s-ban"
- ita "Tabella '%-.64s' sconosciuta in %-.32s"
- jpn "Unknown table '%-.64s' in %-.32s"
- kor "¾Ë¼ö ¾ø´Â Å×À̺í '%-.64s' (µ¥ÀÌŸº£À̽º %-.32s)"
- nor "Ukjent tabell '%-.64s' i %-.32s"
- norwegian-ny "Ukjend tabell '%-.64s' i %-.32s"
- pol "Unknown table '%-.64s' in %-.32s"
- por "Tabela '%-.64s' desconhecida em '%-.32s'"
- rum "Tabla '%-.64s' invalida in %-.32s"
- rus "îÅÉÚ×ÅÓÔÎÁÑ ÔÁÂÌÉÃÁ '%-.64s' × %-.32s"
- serbian "Nepoznata tabela '%-.64s' u '%-.32s'"
- slo "Neznáma tabuµka '%-.64s' v %-.32s"
- spa "Tabla desconocida '%-.64s' in %-.32s"
- swe "Okänd tabell '%-.64s' i '%-.32s'"
- ukr "îÅצÄÏÍÁ ÔÁÂÌÉÃÑ '%-.64s' Õ %-.32s"
+ 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 '%-.64s' je zadána dvakrát"
- dan "Feltet '%-.64s' er anvendt to gange"
- nla "Veld '%-.64s' is dubbel gespecificeerd"
- eng "Column '%-.64s' specified twice"
- est "Tulp '%-.64s' on määratletud topelt"
- fre "Champ '%-.64s' spécifié deux fois"
- ger "Feld '%-.64s' wurde zweimal angegeben"
- greek "Ôï ðåäßï '%-.64s' Ý÷åé ïñéóèåß äýï öïñÝò"
- hun "A(z) '%-.64s' mezot ketszer definialta"
- ita "Campo '%-.64s' specificato 2 volte"
- kor "Ä®·³ '%-.64s'´Â µÎ¹ø Á¤ÀǵǾî ÀÖÀ¾´Ï´Ù."
- nor "Feltet '%-.64s' er spesifisert to ganger"
- norwegian-ny "Feltet '%-.64s' er spesifisert to gangar"
- pol "Field '%-.64s' specified twice"
- por "Coluna '%-.64s' especificada duas vezes"
- rum "Coloana '%-.64s' specificata de doua ori"
- rus "óÔÏÌÂÅà '%-.64s' ÕËÁÚÁÎ Ä×ÁÖÄÙ"
- serbian "Kolona '%-.64s' je navedena dva puta"
- slo "Pole '%-.64s' je zadané dvakrát"
- spa "Campo '%-.64s' especificado dos veces"
- swe "Fält '%-.64s' är redan använt"
- ukr "óÔÏ×ÂÅÃØ '%-.64s' ÚÁÚÎÁÞÅÎÏ Äצަ"
+ 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 "èÉÂÎÅ ×ÉËÏÒÉÓÔÁÎÎÑ ÆÕÎËæ§ ÇÒÕÐÕ×ÁÎÎÑ"
+ 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 '%-.64s' pou-B¾ívá roz¹íøení, které v této verzi MySQL není"
- dan "Tabellen '%-.64s' bruger et filtypenavn som ikke findes i denne MySQL version"
- nla "Tabel '%-.64s' gebruikt een extensie, die niet in deze MySQL-versie voorkomt."
- eng "Table '%-.64s' uses an extension that doesn't exist in this MySQL version"
- est "Tabel '%-.64s' kasutab laiendust, mis ei eksisteeri antud MySQL versioonis"
- fre "Table '%-.64s' : utilise une extension invalide pour cette version de MySQL"
- ger "Tabelle '%-.64s' verwendet eine Erweiterung, die in dieser MySQL-Version nicht verfügbar ist"
- greek "Ï ðßíáêò '%-.64s' ÷ñçóéìïðïéåß êÜðïéï extension ðïõ äåí õðÜñ÷åé óôçí Ýêäïóç áõôÞ ôçò MySQL"
- hun "A(z) '%-.64s' tabla olyan bovitest hasznal, amely nem letezik ebben a MySQL versioban."
- ita "La tabella '%-.64s' usa un'estensione che non esiste in questa versione di MySQL"
- kor "Å×À̺í '%-.64s'´Â È®Àå¸í·ÉÀ» ÀÌ¿ëÇÏÁö¸¸ ÇöÀçÀÇ MySQL ¹öÁ¯¿¡¼­´Â Á¸ÀçÇÏÁö ¾Ê½À´Ï´Ù."
- nor "Table '%-.64s' uses a extension that doesn't exist in this MySQL version"
- norwegian-ny "Table '%-.64s' uses a extension that doesn't exist in this MySQL version"
- pol "Table '%-.64s' uses a extension that doesn't exist in this MySQL version"
- por "Tabela '%-.64s' usa uma extensão que não existe nesta versão do MySQL"
- rum "Tabela '%-.64s' foloseste o extensire inexistenta in versiunea curenta de MySQL"
- rus "÷ ÔÁÂÌÉÃÅ '%-.64s' ÉÓÐÏÌØÚÕÀÔÓÑ ×ÏÚÍÏÖÎÏÓÔÉ, ÎÅ ÐÏÄÄÅÒÖÉ×ÁÅÍÙÅ × ÜÔÏÊ ×ÅÒÓÉÉ MySQL"
- serbian "Tabela '%-.64s' koristi ekstenziju koje ne postoji u ovoj verziji MySQL-a"
- slo "Tabuµka '%-.64s' pou¾íva roz¹írenie, ktoré v tejto verzii MySQL nie je"
- spa "Tabla '%-.64s' usa una extensión que no existe en esta MySQL versión"
- swe "Tabell '%-.64s' har en extension som inte finns i denna version av MySQL"
- ukr "ôÁÂÌÉÃÑ '%-.64s' ×ÉËÏÒÉÓÔÏ×Õ¤ ÒÏÚÛÉÒÅÎÎÑ, ÝÏ ÎÅ ¦ÓÎÕ¤ Õ Ã¦Ê ×ÅÒÓ¦§ MySQL"
+ 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 "ôÁÂÌÉÃÑ ÐÏ×ÉÎÎÁ ÍÁÔÉ ÈÏÞÁ ÏÄÉÎ ÓÔÏ×ÂÅÃØ"
+ 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 '%-.64s' je pln-Bá"
- dan "Tabellen '%-.64s' er fuld"
- nla "De tabel '%-.64s' is vol"
- eng "The table '%-.64s' is full"
- jps "table '%-.64s' ‚Í‚¢‚Á‚Ï‚¢‚Å‚·",
- est "Tabel '%-.64s' on täis"
- fre "La table '%-.64s' est pleine"
- ger "Tabelle '%-.64s' ist voll"
- greek "Ï ðßíáêáò '%-.64s' åßíáé ãåìÜôïò"
- hun "A '%-.64s' tabla megtelt"
- ita "La tabella '%-.64s' e` piena"
- jpn "table '%-.64s' ¤Ï¤¤¤Ã¤Ñ¤¤¤Ç¤¹"
- kor "Å×À̺í '%-.64s'°¡ full³µ½À´Ï´Ù. "
- por "Tabela '%-.64s' está cheia"
- rum "Tabela '%-.64s' e plina"
- rus "ôÁÂÌÉÃÁ '%-.64s' ÐÅÒÅÐÏÌÎÅÎÁ"
- serbian "Tabela '%-.64s' je popunjena do kraja"
- slo "Tabuµka '%-.64s' je plná"
- spa "La tabla '%-.64s' está llena"
- swe "Tabellen '%-.64s' är full"
- ukr "ôÁÂÌÉÃÑ '%-.64s' ÚÁÐÏ×ÎÅÎÁ"
+ 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'"
+ 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 ÔÁÂÌÉÃØ Õ ÏÂ'¤ÄÎÁÎΦ"
+ 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 "úÁÂÁÇÁÔÏ ÓÔÏ×Âæ×"
+ 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"
+ 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=#' ÁÂÉ ÚÁÚÎÁÞÉÔÉ Â¦ÌØÛÉÊ ÓÔÅË, ÑËÝÏ ÎÅÏÂȦÄÎÏ"
+ 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"
+ 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
- cze "Sloupec '%-.64s' je pou-B¾it s UNIQUE nebo INDEX, ale není definován jako NOT NULL"
- dan "Kolonne '%-.64s' bruges som UNIQUE eller INDEX men er ikke defineret som NOT NULL"
- nla "Kolom '%-.64s' wordt gebruikt met UNIQUE of INDEX maar is niet gedefinieerd als NOT NULL"
- eng "Column '%-.64s' is used with UNIQUE or INDEX but is not defined as NOT NULL"
- jps "Column '%-.64s' ‚ª UNIQUE ‚© INDEX ‚ÅŽg—p‚³‚ê‚Ü‚µ‚½. ‚±‚̃Jƒ‰ƒ€‚Í NOT NULL ‚Æ’è‹`‚³‚ê‚Ä‚¢‚Ü‚¹‚ñ.",
- est "Tulp '%-.64s' on kasutusel indeksina, kuid ei ole määratletud kui NOT NULL"
- fre "La colonne '%-.64s' fait partie d'un index UNIQUE ou INDEX mais n'est pas définie comme NOT NULL"
- ger "Spalte '%-.64s' wurde mit UNIQUE oder INDEX benutzt, ist aber nicht als NOT NULL definiert"
- greek "Ôï ðåäßï '%-.64s' ÷ñçóéìïðïéåßôáé óáí UNIQUE Þ INDEX áëëÜ äåí Ý÷åé ïñéóèåß óáí NOT NULL"
- hun "A(z) '%-.64s' oszlop INDEX vagy UNIQUE (egyedi), de a definicioja szerint nem NOT NULL"
- ita "La colonna '%-.64s' e` usata con UNIQUE o INDEX ma non e` definita come NOT NULL"
- jpn "Column '%-.64s' ¤¬ UNIQUE ¤« INDEX ¤Ç»ÈÍѤµ¤ì¤Þ¤·¤¿. ¤³¤Î¥«¥é¥à¤Ï NOT NULL ¤ÈÄêµÁ¤µ¤ì¤Æ¤¤¤Þ¤»¤ó."
- kor "'%-.64s' Ä®·³ÀÌ UNIQUE³ª INDEX¸¦ »ç¿ëÇÏ¿´Áö¸¸ NOT NULLÀÌ Á¤ÀǵÇÁö ¾Ê¾Ò±º¿ä..."
- nor "Column '%-.64s' is used with UNIQUE or INDEX but is not defined as NOT NULL"
- norwegian-ny "Column '%-.64s' is used with UNIQUE or INDEX but is not defined as NOT NULL"
- pol "Column '%-.64s' is used with UNIQUE or INDEX but is not defined as NOT NULL"
- por "Coluna '%-.64s' é usada com única (UNIQUE) ou índice (INDEX), mas não está definida como não-nula (NOT NULL)"
- rum "Coloana '%-.64s' e folosita cu UNIQUE sau INDEX dar fara sa fie definita ca NOT NULL"
- rus "óÔÏÌÂÅÃ '%-.64s' ÉÓÐÏÌØÚÕÅÔÓÑ × UNIQUE ÉÌÉ × INDEX, ÎÏ ÎÅ ÏÐÒÅÄÅÌÅÎ ËÁË NOT NULL"
- serbian "Kolona '%-.64s' je upotrebljena kao 'UNIQUE' ili 'INDEX' ali nije definisana kao 'NOT NULL'"
- slo "Pole '%-.64s' je pou¾ité s UNIQUE alebo INDEX, ale nie je zadefinované ako NOT NULL"
- spa "Columna '%-.64s' es usada con UNIQUE o INDEX pero no está definida como NOT NULL"
- swe "Kolumn '%-.64s' är använd med UNIQUE eller INDEX men är inte definerad med NOT NULL"
- ukr "óÔÏ×ÂÅÃØ '%-.64s' ×ÉËÏÒÉÓÔÏ×Õ¤ÔØÓÑ Ú UNIQUE ÁÂÏ INDEX, ÁÌÅ ÎÅ ×ÉÚÎÁÞÅÎÉÊ ÑË NOT NULL"
+ 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 '%-.64s'"
- dan "Kan ikke læse funktionen '%-.64s'"
- nla "Kan functie '%-.64s' niet laden"
- eng "Can't load function '%-.64s'"
- jps "function '%-.64s' ‚ð ƒ[ƒh‚Å‚«‚Ü‚¹‚ñ",
- est "Ei suuda avada funktsiooni '%-.64s'"
- fre "Imposible de charger la fonction '%-.64s'"
- ger "Kann Funktion '%-.64s' nicht laden"
- greek "Äåí åßíáé äõíáôÞ ç äéáäéêáóßá load ãéá ôç óõíÜñôçóç '%-.64s'"
- hun "A(z) '%-.64s' fuggveny nem toltheto be"
- ita "Impossibile caricare la funzione '%-.64s'"
- jpn "function '%-.64s' ¤ò ¥í¡¼¥É¤Ç¤­¤Þ¤»¤ó"
- kor "'%-.64s' ÇÔ¼ö¸¦ ·ÎµåÇÏÁö ¸øÇß½À´Ï´Ù."
- por "Não pode carregar a função '%-.64s'"
- rum "Nu pot incarca functia '%-.64s'"
- rus "îÅ×ÏÚÍÏÖÎÏ ÚÁÇÒÕÚÉÔØ ÆÕÎËÃÉÀ '%-.64s'"
- serbian "Ne mogu da uèitam funkciju '%-.64s'"
- slo "Nemô¾em naèíta» funkciu '%-.64s'"
- spa "No puedo cargar función '%-.64s'"
- swe "Kan inte ladda funktionen '%-.64s'"
- ukr "îÅ ÍÏÖÕ ÚÁ×ÁÎÔÁÖÉÔÉ ÆÕÎËæÀ '%-.64s'"
+ 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 '%-.64s'; %-.80s"
- dan "Kan ikke starte funktionen '%-.64s'; %-.80s"
- nla "Kan functie '%-.64s' niet initialiseren; %-.80s"
- eng "Can't initialize function '%-.64s'; %-.80s"
- jps "function '%-.64s' ‚ð‰Šú‰»‚Å‚«‚Ü‚¹‚ñ; %-.80s",
- est "Ei suuda algväärtustada funktsiooni '%-.64s'; %-.80s"
- fre "Impossible d'initialiser la fonction '%-.64s'; %-.80s"
- ger "Kann Funktion '%-.64s' nicht initialisieren: %-.80s"
- greek "Äåí åßíáé äõíáôÞ ç Ýíáñîç ôçò óõíÜñôçóçò '%-.64s'; %-.80s"
- hun "A(z) '%-.64s' fuggveny nem inicializalhato; %-.80s"
- ita "Impossibile inizializzare la funzione '%-.64s'; %-.80s"
- jpn "function '%-.64s' ¤ò½é´ü²½¤Ç¤­¤Þ¤»¤ó; %-.80s"
- kor "'%-.64s' ÇÔ¼ö¸¦ ÃʱâÈ­ ÇÏÁö ¸øÇß½À´Ï´Ù.; %-.80s"
- por "Não pode inicializar a função '%-.64s' - '%-.80s'"
- rum "Nu pot initializa functia '%-.64s'; %-.80s"
- rus "îÅ×ÏÚÍÏÖÎÏ ÉÎÉÃÉÁÌÉÚÉÒÏ×ÁÔØ ÆÕÎËÃÉÀ '%-.64s'; %-.80s"
- serbian "Ne mogu da inicijalizujem funkciju '%-.64s'; %-.80s"
- slo "Nemô¾em inicializova» funkciu '%-.64s'; %-.80s"
- spa "No puedo inicializar función '%-.64s'; %-.80s"
- swe "Kan inte initialisera funktionen '%-.64s'; '%-.80s'"
- ukr "îÅ ÍÏÖÕ ¦Î¦Ã¦Á̦ÚÕ×ÁÔÉ ÆÕÎËæÀ '%-.64s'; %-.80s"
+ 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 "îÅ ÄÏÚ×ÏÌÅÎÏ ×ÉËÏÒÉÓÔÏ×Õ×ÁÔÉ ÐÕÔ¦ ÄÌÑ ÒÏÚĦÌÀ×ÁÎÉÈ Â¦Â̦ÏÔÅË"
+ 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 '%-.64s' ji-B¾ existuje"
- dan "Funktionen '%-.64s' findes allerede"
- nla "Functie '%-.64s' bestaat reeds"
- eng "Function '%-.64s' already exists"
- jps "Function '%-.64s' ‚ÍŠù‚É’è‹`‚³‚ê‚Ä‚¢‚Ü‚·",
- est "Funktsioon '%-.64s' juba eksisteerib"
- fre "La fonction '%-.64s' existe déjà"
- ger "Funktion '%-.64s' existiert schon"
- greek "Ç óõíÜñôçóç '%-.64s' õðÜñ÷åé Þäç"
- hun "A '%-.64s' fuggveny mar letezik"
- ita "La funzione '%-.64s' esiste gia`"
- jpn "Function '%-.64s' ¤Ï´û¤ËÄêµÁ¤µ¤ì¤Æ¤¤¤Þ¤¹"
- kor "'%-.64s' ÇÔ¼ö´Â ÀÌ¹Ì Á¸ÀçÇÕ´Ï´Ù."
- por "Função '%-.64s' já existe"
- rum "Functia '%-.64s' exista deja"
- rus "æÕÎËÃÉÑ '%-.64s' ÕÖÅ ÓÕÝÅÓÔ×ÕÅÔ"
- serbian "Funkcija '%-.64s' veæ postoji"
- slo "Funkcia '%-.64s' u¾ existuje"
- spa "Función '%-.64s' ya existe"
- swe "Funktionen '%-.64s' finns redan"
- ukr "æÕÎËÃ¦Ñ '%-.64s' ×ÖÅ ¦ÓÎÕ¤"
+ 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 '%-.64s' (errno: %d %-.128s)"
- dan "Kan ikke åbne delt bibliotek '%-.64s' (errno: %d %-.128s)"
- nla "Kan shared library '%-.64s' niet openen (Errcode: %d %-.128s)"
- eng "Can't open shared library '%-.64s' (errno: %d %-.128s)"
- jps "shared library '%-.64s' ‚ðŠJ‚­Ž–‚ª‚Å‚«‚Ü‚¹‚ñ (errno: %d %-.128s)",
- est "Ei suuda avada jagatud teeki '%-.64s' (veakood: %d %-.128s)"
- fre "Impossible d'ouvrir la bibliothèque partagée '%-.64s' (errno: %d %-.128s)"
- ger "Kann Shared Library '%-.64s' nicht öffnen (Fehler: %d %-.128s)"
- greek "Äåí åßíáé äõíáôÞ ç áíÜãíùóç ôçò shared library '%-.64s' (êùäéêüò ëÜèïõò: %d %-.128s)"
- hun "A(z) '%-.64s' megosztott konyvtar nem hasznalhato (hibakod: %d %-.128s)"
- ita "Impossibile aprire la libreria condivisa '%-.64s' (errno: %d %-.128s)"
- jpn "shared library '%-.64s' ¤ò³«¤¯»ö¤¬¤Ç¤­¤Þ¤»¤ó (errno: %d %-.128s)"
- kor "'%-.64s' °øÀ¯ ¶óÀ̹ö·¯¸®¸¦ ¿­¼ö ¾ø½À´Ï´Ù.(¿¡·¯¹øÈ£: %d %-.128s)"
- nor "Can't open shared library '%-.64s' (errno: %d %-.128s)"
- norwegian-ny "Can't open shared library '%-.64s' (errno: %d %-.128s)"
- pol "Can't open shared library '%-.64s' (errno: %d %-.128s)"
- por "Não pode abrir biblioteca compartilhada '%-.64s' (erro no. '%d' - '%-.128s')"
- rum "Nu pot deschide libraria shared '%-.64s' (Eroare: %d %-.128s)"
- rus "îÅ×ÏÚÍÏÖÎÏ ÏÔËÒÙÔØ ÄÉÎÁÍÉÞÅÓËÕÀ ÂÉÂÌÉÏÔÅËÕ '%-.64s' (ÏÛÉÂËÁ: %d %-.128s)"
- serbian "Ne mogu da otvorim share-ovanu biblioteku '%-.64s' (errno: %d %-.128s)"
- slo "Nemô¾em otvori» zdieµanú kni¾nicu '%-.64s' (chybový kód: %d %-.128s)"
- spa "No puedo abrir libraria conjugada '%-.64s' (errno: %d %-.128s)"
- swe "Kan inte öppna det dynamiska biblioteket '%-.64s' (Felkod: %d %-.128s)"
- ukr "îÅ ÍÏÖÕ ×¦ÄËÒÉÔÉ ÒÏÚĦÌÀ×ÁÎÕ Â¦Â̦ÏÔÅËÕ '%-.64s' (ÐÏÍÉÌËÁ: %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 function '%-.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' Õ Â¦Â̦ÏÔÅæ"
+ 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 '%-.64s' nen-Bí definována"
- dan "Funktionen '%-.64s' er ikke defineret"
- nla "Functie '%-.64s' is niet gedefinieerd"
- eng "Function '%-.64s' is not defined"
- jps "Function '%-.64s' ‚Í’è‹`‚³‚ê‚Ä‚¢‚Ü‚¹‚ñ",
- est "Funktsioon '%-.64s' ei ole defineeritud"
- fre "La fonction '%-.64s' n'est pas définie"
- ger "Funktion '%-.64s' ist nicht definiert"
- greek "Ç óõíÜñôçóç '%-.64s' äåí Ý÷åé ïñéóèåß"
- hun "A '%-.64s' fuggveny nem definialt"
- ita "La funzione '%-.64s' non e` definita"
- jpn "Function '%-.64s' ¤ÏÄêµÁ¤µ¤ì¤Æ¤¤¤Þ¤»¤ó"
- kor "'%-.64s' ÇÔ¼ö°¡ Á¤ÀǵǾî ÀÖÁö ¾Ê½À´Ï´Ù."
- por "Função '%-.64s' não está definida"
- rum "Functia '%-.64s' nu e definita"
- rus "æÕÎËÃÉÑ '%-.64s' ÎÅ ÏÐÒÅÄÅÌÅÎÁ"
- serbian "Funkcija '%-.64s' nije definisana"
- slo "Funkcia '%-.64s' nie je definovaná"
- spa "Función '%-.64s' no está definida"
- swe "Funktionen '%-.64s' är inte definierad"
- ukr "æÕÎËæÀ '%-.64s' ÎÅ ×ÉÚÎÁÞÅÎÏ"
+ 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 connection. 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'"
+ 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"
+ 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 ÑË ÁÎÏΦÍÎÉÊ ËÏÒÉÓÔÕ×ÁÞ, ÔÏÍÕ ×ÁÍ ÎÅ ÄÏÚ×ÏÌÅÎÏ ÚͦÎÀ×ÁÔÉ ÐÁÒÏ̦"
+ 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, ÁÂÉ ÍÁÔÉ ÍÏÖÌÉצÓÔØ ÚͦÎÀ×ÁÔÉ ÐÁÒÏÌØ ¦ÎÛÉÍ"
+ 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 "îÅ ÍÏÖÕ ÚÎÁÊÔÉ ×¦ÄÐÏצÄÎÉÈ ÚÁÐÉÓ¦× Õ ÔÁÂÌÉæ ËÏÒÉÓÔÕ×ÁÞÁ"
+ 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"
+ 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). ñËÝÏ ×É ÎÅ ×ÉËÏÒÉÓÔÁÌÉ ÕÓÀ ÐÁÍ'ÑÔØ, ÔÏ ÐÒÏÞÉÔÁÊÔÅ ÄÏËÕÍÅÎÔÁæÀ ÄÏ ×ÁÛϧ ïó - ÍÏÖÌÉ×Ï ÃÅ ÐÏÍÉÌËÁ ïó"
+ 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 %ld"
- dan "Kolonne antallet stemmer ikke overens med antallet af værdier i post %ld"
- nla "Kolom aantal komt niet overeen met waarde aantal in rij %ld"
- eng "Column count doesn't match value count at row %ld"
- est "Tulpade hulk erineb väärtuste hulgast real %ld"
- ger "Anzahl der Felder stimmt nicht mit der Anzahl der Werte in Zeile %ld überein"
- hun "Az oszlopban talalhato ertek nem egyezik meg a %ld sorban szamitott ertekkel"
- ita "Il numero delle colonne non corrisponde al conteggio alla riga %ld"
- kor "Row %ld¿¡¼­ Ä®·³ Ä«¿îÆ®¿Í value Ä«¿îÅÍ¿Í ÀÏÄ¡ÇÏÁö ¾Ê½À´Ï´Ù."
- por "Contagem de colunas não confere com a contagem de valores na linha %ld"
- rum "Numarul de coloane nu corespunde cu numarul de valori la linia %ld"
- rus "ëÏÌÉÞÅÓÔ×Ï ÓÔÏÌÂÃÏ× ÎÅ ÓÏ×ÐÁÄÁÅÔ Ó ËÏÌÉÞÅÓÔ×ÏÍ ÚÎÁÞÅÎÉÊ × ÚÁÐÉÓÉ %ld"
- serbian "Broj kolona ne odgovara broju vrednosti u slogu %ld"
- spa "El número de columnas no corresponde al número en la línea %ld"
- swe "Antalet kolumner motsvarar inte antalet värden på rad: %ld"
- ukr "ë¦ÌØ˦ÓÔØ ÓÔÏ×ÂÃ¦× ÎÅ ÓЦ×ÐÁÄÁ¤ Ú Ë¦ÌØ˦ÓÔÀ ÚÎÁÞÅÎØ Õ ÓÔÒÏæ %ld"
+ cze "Po-Bèet sloupcù neodpovídá poètu hodnot na øádku %ld"
+ dan "Kolonne antallet stemmer ikke overens med antallet af værdier i post %ld"
+ nla "Kolom aantal komt niet overeen met waarde aantal in rij %ld"
+ eng "Column count doesn't match value count at row %ld"
+ est "Tulpade hulk erineb väärtuste hulgast real %ld"
+ ger "Anzahl der Felder stimmt nicht mit der Anzahl der Werte in Zeile %ld überein"
+ hun "Az oszlopban talalhato ertek nem egyezik meg a %ld sorban szamitott ertekkel"
+ ita "Il numero delle colonne non corrisponde al conteggio alla riga %ld"
+ kor "Row %ld¿¡¼­ Ä®·³ Ä«¿îÆ®¿Í value Ä«¿îÅÍ¿Í ÀÏÄ¡ÇÏÁö ¾Ê½À´Ï´Ù."
+ por "Contagem de colunas não confere com a contagem de valores na linha %ld"
+ rum "Numarul de coloane nu corespunde cu numarul de valori la linia %ld"
+ rus "ëÏÌÉÞÅÓÔ×Ï ÓÔÏÌÂÃÏ× ÎÅ ÓÏ×ÐÁÄÁÅÔ Ó ËÏÌÉÞÅÓÔ×ÏÍ ÚÎÁÞÅÎÉÊ × ÚÁÐÉÓÉ %ld"
+ serbian "Broj kolona ne odgovara broju vrednosti u slogu %ld"
+ spa "El número de columnas no corresponde al número en la línea %ld"
+ swe "Antalet kolumner motsvarar inte antalet värden på rad: %ld"
+ ukr "ë¦ÌØ˦ÓÔØ ÓÔÏ×ÂÃ¦× ÎÅ ÓЦ×ÐÁÄÁ¤ Ú Ë¦ÌØ˦ÓÔÀ ÚÎÁÞÅÎØ Õ ÓÔÒÏæ %ld"
ER_CANT_REOPEN_TABLE
- cze "Nemohu znovuotev-Bøít tabulku: '%-.64s"
- dan "Kan ikke genåbne tabel '%-.64s"
- nla "Kan tabel niet opnieuw openen: '%-.64s"
- eng "Can't reopen table: '%-.64s'"
- est "Ei suuda taasavada tabelit '%-.64s'"
- fre "Impossible de réouvrir la table: '%-.64s"
- ger "Kann Tabelle'%-.64s' nicht erneut öffnen"
- hun "Nem lehet ujra-megnyitni a tablat: '%-.64s"
- ita "Impossibile riaprire la tabella: '%-.64s'"
- kor "Å×À̺íÀ» ´Ù½Ã ¿­¼ö ¾ø±º¿ä: '%-.64s"
- nor "Can't reopen table: '%-.64s"
- norwegian-ny "Can't reopen table: '%-.64s"
- pol "Can't reopen table: '%-.64s"
- por "Não pode reabrir a tabela '%-.64s"
- rum "Nu pot redeschide tabela: '%-.64s'"
- rus "îÅ×ÏÚÍÏÖÎÏ ÚÁÎÏ×Ï ÏÔËÒÙÔØ ÔÁÂÌÉÃÕ '%-.64s'"
- serbian "Ne mogu da ponovo otvorim tabelu '%-.64s'"
- slo "Can't reopen table: '%-.64s"
- spa "No puedo reabrir tabla: '%-.64s"
- swe "Kunde inte stänga och öppna tabell '%-.64s"
- ukr "îÅ ÍÏÖÕ ÐÅÒÅצÄËÒÉÔÉ ÔÁÂÌÉÃÀ: '%-.64s'"
+ 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"
+ 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' ×¦Ä ÒÅÇÕÌÑÒÎÏÇÏ ×ÉÒÁÚÕ"
+ 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"
+ 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 '%-.64s' na stroji '%-.64s'"
- dan "Denne tilladelse findes ikke for brugeren '%-.64s' på vært '%-.64s'"
- nla "Deze toegang (GRANT) is niet toegekend voor gebruiker '%-.64s' op host '%-.64s'"
- eng "There is no such grant defined for user '%-.64s' on host '%-.64s'"
- jps "ƒ†[ƒU[ '%-.64s' (ƒzƒXƒg '%-.64s' ‚̃†[ƒU[) ‚Í‹–‰Â‚³‚ê‚Ä‚¢‚Ü‚¹‚ñ",
- est "Sellist õigust ei ole defineeritud kasutajale '%-.64s' masinast '%-.64s'"
- fre "Un tel droit n'est pas défini pour l'utilisateur '%-.64s' sur l'hôte '%-.64s'"
- ger "Für Benutzer '%-.64s' auf Host '%-.64s' gibt es keine solche Berechtigung"
- hun "A '%-.64s' felhasznalonak nincs ilyen joga a '%-.64s' host-on"
- ita "GRANT non definita per l'utente '%-.64s' dalla macchina '%-.64s'"
- jpn "¥æ¡¼¥¶¡¼ '%-.64s' (¥Û¥¹¥È '%-.64s' ¤Î¥æ¡¼¥¶¡¼) ¤Ïµö²Ä¤µ¤ì¤Æ¤¤¤Þ¤»¤ó"
- kor "»ç¿ëÀÚ '%-.64s' (È£½ºÆ® '%-.64s')¸¦ À§ÇÏ¿© Á¤ÀÇµÈ ±×·± ½ÂÀÎÀº ¾ø½À´Ï´Ù."
- por "Não existe tal permissão (grant) definida para o usuário '%-.64s' no 'host' '%-.64s'"
- rum "Nu exista un astfel de grant definit pentru utilzatorul '%-.64s' de pe host-ul '%-.64s'"
- rus "ôÁËÉÅ ÐÒÁ×Á ÎÅ ÏÐÒÅÄÅÌÅÎÙ ÄÌÑ ÐÏÌØÚÏ×ÁÔÅÌÑ '%-.64s' ÎÁ ÈÏÓÔÅ '%-.64s'"
- serbian "Ne postoji odobrenje za pristup korisniku '%-.64s' na host-u '%-.64s'"
- spa "No existe permiso definido para usuario '%-.64s' en el servidor '%-.64s'"
- swe "Det finns inget privilegium definierat för användare '%-.64s' på '%-.64s'"
- ukr "ðÏ×ÎÏ×ÁÖÅÎØ ÎÅ ×ÉÚÎÁÞÅÎÏ ÄÌÑ ËÏÒÉÓÔÕ×ÁÞÁ '%-.64s' Ú ÈÏÓÔÕ '%-.64s'"
+ 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: '%-.32s'@'%-.64s' pro tabulku '%-.64s'"
- dan "%-.16s-kommandoen er ikke tilladt for brugeren '%-.32s'@'%-.64s' for tabellen '%-.64s'"
- nla "%-.16s commando geweigerd voor gebruiker: '%-.32s'@'%-.64s' voor tabel '%-.64s'"
- eng "%-.16s command denied to user '%-.32s'@'%-.64s' for table '%-.64s'"
- jps "ƒRƒ}ƒ“ƒh %-.16s ‚Í ƒ†[ƒU[ '%-.32s'@'%-.64s' ,ƒe[ƒuƒ‹ '%-.64s' ‚ɑ΂µ‚Ä‹–‰Â‚³‚ê‚Ä‚¢‚Ü‚¹‚ñ",
- est "%-.16s käsk ei ole lubatud kasutajale '%-.32s'@'%-.64s' tabelis '%-.64s'"
- fre "La commande '%-.16s' est interdite à l'utilisateur: '%-.32s'@'@%-.64s' sur la table '%-.64s'"
- ger "%-.16s Befehl nicht erlaubt für Benutzer '%-.32s'@'%-.64s' auf Tabelle '%-.64s'"
- hun "%-.16s parancs a '%-.32s'@'%-.64s' felhasznalo szamara nem engedelyezett a '%-.64s' tablaban"
- ita "Comando %-.16s negato per l'utente: '%-.32s'@'%-.64s' sulla tabella '%-.64s'"
- jpn "¥³¥Þ¥ó¥É %-.16s ¤Ï ¥æ¡¼¥¶¡¼ '%-.32s'@'%-.64s' ,¥Æ¡¼¥Ö¥ë '%-.64s' ¤ËÂФ·¤Æµö²Ä¤µ¤ì¤Æ¤¤¤Þ¤»¤ó"
- kor "'%-.16s' ¸í·ÉÀº ´ÙÀ½ »ç¿ëÀÚ¿¡°Ô °ÅºÎµÇ¾ú½À´Ï´Ù. : '%-.32s'@'%-.64s' for Å×À̺í '%-.64s'"
- por "Comando '%-.16s' negado para o usuário '%-.32s'@'%-.64s' na tabela '%-.64s'"
- rum "Comanda %-.16s interzisa utilizatorului: '%-.32s'@'%-.64s' pentru tabela '%-.64s'"
- rus "ëÏÍÁÎÄÁ %-.16s ÚÁÐÒÅÝÅÎÁ ÐÏÌØÚÏ×ÁÔÅÌÀ '%-.32s'@'%-.64s' ÄÌÑ ÔÁÂÌÉÃÙ '%-.64s'"
- serbian "%-.16s komanda zabranjena za korisnika '%-.32s'@'%-.64s' za tabelu '%-.64s'"
- spa "%-.16s comando negado para usuario: '%-.32s'@'%-.64s' para tabla '%-.64s'"
- swe "%-.16s ej tillåtet för '%-.32s'@'%-.64s' för tabell '%-.64s'"
- ukr "%-.16s ËÏÍÁÎÄÁ ÚÁÂÏÒÏÎÅÎÁ ËÏÒÉÓÔÕ×ÁÞÕ: '%-.32s'@'%-.64s' Õ ÔÁÂÌÉæ '%-.64s'"
+ 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: '%-.32s'@'%-.64s' pro sloupec '%-.64s' v tabulce '%-.64s'"
- dan "%-.16s-kommandoen er ikke tilladt for brugeren '%-.32s'@'%-.64s' for kolonne '%-.64s' in tabellen '%-.64s'"
- nla "%-.16s commando geweigerd voor gebruiker: '%-.32s'@'%-.64s' voor kolom '%-.64s' in tabel '%-.64s'"
- eng "%-.16s command denied to user '%-.32s'@'%-.64s' for column '%-.64s' in table '%-.64s'"
- jps "ƒRƒ}ƒ“ƒh %-.16s ‚Í ƒ†[ƒU[ '%-.32s'@'%-.64s'\n ƒJƒ‰ƒ€ '%-.64s' ƒe[ƒuƒ‹ '%-.64s' ‚ɑ΂µ‚Ä‹–‰Â‚³‚ê‚Ä‚¢‚Ü‚¹‚ñ",
- est "%-.16s käsk ei ole lubatud kasutajale '%-.32s'@'%-.64s' tulbale '%-.64s' tabelis '%-.64s'"
- fre "La commande '%-.16s' est interdite à l'utilisateur: '%-.32s'@'@%-.64s' sur la colonne '%-.64s' de la table '%-.64s'"
- ger "%-.16s Befehl nicht erlaubt für Benutzer '%-.32s'@'%-.64s' und Feld '%-.64s' in Tabelle '%-.64s'"
- hun "%-.16s parancs a '%-.32s'@'%-.64s' felhasznalo szamara nem engedelyezett a '%-.64s' mezo eseten a '%-.64s' tablaban"
- ita "Comando %-.16s negato per l'utente: '%-.32s'@'%-.64s' sulla colonna '%-.64s' della tabella '%-.64s'"
- jpn "¥³¥Þ¥ó¥É %-.16s ¤Ï ¥æ¡¼¥¶¡¼ '%-.32s'@'%-.64s'\n ¥«¥é¥à '%-.64s' ¥Æ¡¼¥Ö¥ë '%-.64s' ¤ËÂФ·¤Æµö²Ä¤µ¤ì¤Æ¤¤¤Þ¤»¤ó"
- kor "'%-.16s' ¸í·ÉÀº ´ÙÀ½ »ç¿ëÀÚ¿¡°Ô °ÅºÎµÇ¾ú½À´Ï´Ù. : '%-.32s'@'%-.64s' for Ä®·³ '%-.64s' in Å×À̺í '%-.64s'"
- por "Comando '%-.16s' negado para o usuário '%-.32s'@'%-.64s' na coluna '%-.64s', na tabela '%-.64s'"
- rum "Comanda %-.16s interzisa utilizatorului: '%-.32s'@'%-.64s' pentru coloana '%-.64s' in tabela '%-.64s'"
- rus "ëÏÍÁÎÄÁ %-.16s ÚÁÐÒÅÝÅÎÁ ÐÏÌØÚÏ×ÁÔÅÌÀ '%-.32s'@'%-.64s' ÄÌÑ ÓÔÏÌÂÃÁ '%-.64s' × ÔÁÂÌÉÃÅ '%-.64s'"
- serbian "%-.16s komanda zabranjena za korisnika '%-.32s'@'%-.64s' za kolonu '%-.64s' iz tabele '%-.64s'"
- spa "%-.16s comando negado para usuario: '%-.32s'@'%-.64s' para columna '%-.64s' en la tabla '%-.64s'"
- swe "%-.16s ej tillåtet för '%-.32s'@'%-.64s' för kolumn '%-.64s' i tabell '%-.64s'"
- ukr "%-.16s ËÏÍÁÎÄÁ ÚÁÂÏÒÏÎÅÎÁ ËÏÒÉÓÔÕ×ÁÞÕ: '%-.32s'@'%-.64s' ÄÌÑ ÓÔÏ×ÂÃÑ '%-.64s' Õ ÔÁÂÌÉæ '%-.64s'"
+ 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 ËÏÍÁÎÄÁ; ÐÒÏÞÉÔÁÊÔÅ ÄÏËÕÍÅÎÔÁæÀ ÓÔÏÓÏ×ÎÏ ÔÏÇÏ, Ñ˦ ÐÒÁ×Á ÍÏÖÎÁ ×ÉËÏÒÉÓÔÏ×Õ×ÁÔÉ"
+ 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 ÚÁÄÏ×ÇÉÊ"
+ 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 '%-.64s.%-.64s' neexistuje"
- dan "Tabellen '%-.64s.%-.64s' eksisterer ikke"
- nla "Tabel '%-.64s.%-.64s' bestaat niet"
- eng "Table '%-.64s.%-.64s' doesn't exist"
- est "Tabelit '%-.64s.%-.64s' ei eksisteeri"
- fre "La table '%-.64s.%-.64s' n'existe pas"
- ger "Tabelle '%-.64s.%-.64s' existiert nicht"
- hun "A '%-.64s.%-.64s' tabla nem letezik"
- ita "La tabella '%-.64s.%-.64s' non esiste"
- jpn "Table '%-.64s.%-.64s' doesn't exist"
- kor "Å×À̺í '%-.64s.%-.64s' ´Â Á¸ÀçÇÏÁö ¾Ê½À´Ï´Ù."
- nor "Table '%-.64s.%-.64s' doesn't exist"
- norwegian-ny "Table '%-.64s.%-.64s' doesn't exist"
- pol "Table '%-.64s.%-.64s' doesn't exist"
- por "Tabela '%-.64s.%-.64s' não existe"
- rum "Tabela '%-.64s.%-.64s' nu exista"
- rus "ôÁÂÌÉÃÁ '%-.64s.%-.64s' ÎÅ ÓÕÝÅÓÔ×ÕÅÔ"
- serbian "Tabela '%-.64s.%-.64s' ne postoji"
- slo "Table '%-.64s.%-.64s' doesn't exist"
- spa "Tabla '%-.64s.%-.64s' no existe"
- swe "Det finns ingen tabell som heter '%-.64s.%-.64s'"
- ukr "ôÁÂÌÉÃÑ '%-.64s.%-.64s' ÎÅ ¦ÓÎÕ¤"
+ 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 '%-.32s' na stroji '%-.64s' pro tabulku '%-.64s'"
- dan "Denne tilladelse eksisterer ikke for brugeren '%-.32s' på vært '%-.64s' for tabellen '%-.64s'"
- nla "Deze toegang (GRANT) is niet toegekend voor gebruiker '%-.32s' op host '%-.64s' op tabel '%-.64s'"
- eng "There is no such grant defined for user '%-.32s' on host '%-.64s' on table '%-.64s'"
- est "Sellist õigust ei ole defineeritud kasutajale '%-.32s' masinast '%-.64s' tabelile '%-.64s'"
- fre "Un tel droit n'est pas défini pour l'utilisateur '%-.32s' sur l'hôte '%-.64s' sur la table '%-.64s'"
- ger "Eine solche Berechtigung ist für User '%-.32s' auf Host '%-.64s' an Tabelle '%-.64s' nicht definiert"
- hun "A '%-.32s' felhasznalo szamara a '%-.64s' host '%-.64s' tablajaban ez a parancs nem engedelyezett"
- ita "GRANT non definita per l'utente '%-.32s' dalla macchina '%-.64s' sulla tabella '%-.64s'"
- kor "»ç¿ëÀÚ '%-.32s'(È£½ºÆ® '%-.64s')´Â Å×À̺í '%-.64s'¸¦ »ç¿ëÇϱâ À§ÇÏ¿© Á¤ÀÇµÈ ½ÂÀÎÀº ¾ø½À´Ï´Ù. "
- por "Não existe tal permissão (grant) definido para o usuário '%-.32s' no 'host' '%-.64s', na tabela '%-.64s'"
- rum "Nu exista un astfel de privilegiu (grant) definit pentru utilizatorul '%-.32s' de pe host-ul '%-.64s' pentru tabela '%-.64s'"
- rus "ôÁËÉÅ ÐÒÁ×Á ÎÅ ÏÐÒÅÄÅÌÅÎÙ ÄÌÑ ÐÏÌØÚÏ×ÁÔÅÌÑ '%-.32s' ÎÁ ËÏÍÐØÀÔÅÒÅ '%-.64s' ÄÌÑ ÔÁÂÌÉÃÙ '%-.64s'"
- serbian "Ne postoji odobrenje za pristup korisniku '%-.32s' na host-u '%-.64s' tabeli '%-.64s'"
- spa "No existe tal permiso definido para usuario '%-.32s' en el servidor '%-.64s' en la tabla '%-.64s'"
- swe "Det finns inget privilegium definierat för användare '%-.32s' på '%-.64s' för tabell '%-.64s'"
- ukr "ðÏ×ÎÏ×ÁÖÅÎØ ÎÅ ×ÉÚÎÁÞÅÎÏ ÄÌÑ ËÏÒÉÓÔÕ×ÁÞÁ '%-.32s' Ú ÈÏÓÔÕ '%-.64s' ÄÌÑ ÔÁÂÌÉæ '%-.64s'"
+ 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"
+ 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"
+ 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 %-.64s"
- dan "Forsinket indsættelse tråden (delayed insert thread) kunne ikke opnå lås på tabellen %-.64s"
- nla "'Delayed insert' thread kon de aangevraagde 'lock' niet krijgen voor tabel %-.64s"
- eng "Delayed insert thread couldn't get requested lock for table %-.64s"
- est "INSERT DELAYED lõim ei suutnud saada soovitud lukku tabelile %-.64s"
- fre "La tâche 'delayed insert' n'a pas pu obtenir le verrou démandé sur la table %-.64s"
- ger "Verzögerter (DELAYED) Einfüge-Thread konnte die angeforderte Sperre für Tabelle '%-.64s' nicht erhalten"
- hun "A kesleltetett beillesztes (delayed insert) thread nem kapott zatolast a %-.64s tablahoz"
- ita "Il thread di inserimento ritardato non riesce ad ottenere il lock per la tabella %-.64s"
- kor "Áö¿¬µÈ insert ¾²·¹µå°¡ Å×À̺í %-.64sÀÇ ¿ä±¸µÈ ¶ôÅ·À» ó¸®ÇÒ ¼ö ¾ø¾ú½À´Ï´Ù."
- por "'Thread' de inserção retardada (atrasada) pois não conseguiu obter a trava solicitada para tabela '%-.64s'"
- rum "Thread-ul pentru inserarea aminata nu a putut obtine lacatul (lock) pentru tabela %-.64s"
- rus "ðÏÔÏË, ÏÂÓÌÕÖÉ×ÁÀÝÉÊ ÏÔÌÏÖÅÎÎÕÀ ×ÓÔÁ×ËÕ (delayed insert), ÎÅ ÓÍÏÇ ÐÏÌÕÞÉÔØ ÚÁÐÒÁÛÉ×ÁÅÍÕÀ ÂÌÏËÉÒÏ×ËÕ ÎÁ ÔÁÂÌÉÃÕ %-.64s"
- serbian "Prolongirani 'INSERT' thread nije mogao da dobije traženo zakljuèavanje tabele '%-.64s'"
- spa "Thread de inserción retarda no pudiendo bloquear para la tabla %-.64s"
- swe "DELAYED INSERT-tråden kunde inte låsa tabell '%-.64s'"
- ukr "ç¦ÌËÁ ÄÌÑ INSERT DELAYED ÎÅ ÍÏÖÅ ÏÔÒÉÍÁÔÉ ÂÌÏËÕ×ÁÎÎÑ ÄÌÑ ÔÁÂÌÉæ %-.64s"
+ 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 "úÁÂÁÇÁÔÏ ÚÁÔÒÉÍÁÎÉÈ Ç¦ÌÏË ×ÉËÏÒÉÓÔÏ×Õ¤ÔØÓÑ"
+ 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: '%-.64s' u¾ivatel: '%-.32s' (%-.64s)"
- dan "Afbrudt forbindelse %ld til database: '%-.64s' bruger: '%-.32s' (%-.64s)"
- nla "Afgebroken verbinding %ld naar db: '%-.64s' gebruiker: '%-.32s' (%-.64s)"
- eng "Aborted connection %ld to db: '%-.64s' user: '%-.32s' (%-.64s)"
- est "Ühendus katkestatud %ld andmebaasile: '%-.64s' kasutajale: '%-.32s' (%-.64s)"
- fre "Connection %ld avortée vers la bd: '%-.64s' utilisateur: '%-.32s' (%-.64s)"
- ger "Abbruch der Verbindung %ld zur Datenbank '%-.64s'. Benutzer: '%-.32s' (%-.64s)"
- hun "Megszakitott kapcsolat %ld db: '%-.64s' adatbazishoz, felhasznalo: '%-.32s' (%-.64s)"
- ita "Interrotta la connessione %ld al db: '%-.64s' utente: '%-.32s' (%-.64s)"
- jpn "Aborted connection %ld to db: '%-.64s' user: '%-.32s' (%-.64s)"
- kor "µ¥ÀÌŸº£À̽º Á¢¼ÓÀ» À§ÇÑ ¿¬°á %ld°¡ Áß´ÜµÊ : '%-.64s' »ç¿ëÀÚ: '%-.32s' (%-.64s)"
- nor "Aborted connection %ld to db: '%-.64s' user: '%-.32s' (%-.64s)"
- norwegian-ny "Aborted connection %ld to db: '%-.64s' user: '%-.32s' (%-.64s)"
- pol "Aborted connection %ld to db: '%-.64s' user: '%-.32s' (%-.64s)"
- por "Conexão %ld abortou para o banco de dados '%-.64s' - usuário '%-.32s' (%-.64s)"
- rum "Conectie terminata %ld la baza de date: '%-.64s' utilizator: '%-.32s' (%-.64s)"
- rus "ðÒÅÒ×ÁÎÏ ÓÏÅÄÉÎÅÎÉÅ %ld Ë ÂÁÚÅ ÄÁÎÎÙÈ '%-.64s' ÐÏÌØÚÏ×ÁÔÅÌÑ '%-.32s' (%-.64s)"
- serbian "Prekinuta konekcija broj %ld ka bazi: '%-.64s' korisnik je bio: '%-.32s' (%-.64s)"
- slo "Aborted connection %ld to db: '%-.64s' user: '%-.32s' (%-.64s)"
- spa "Conexión abortada %ld para db: '%-.64s' usuario: '%-.32s' (%-.64s)"
- swe "Avbröt länken för tråd %ld till db '%-.64s', användare '%-.32s' (%-.64s)"
- ukr "ðÅÒÅÒ×ÁÎÏ Ú'¤ÄÎÁÎÎÑ %ld ÄÏ ÂÁÚÉ ÄÁÎÎÉÈ: '%-.64s' ËÏÒÉÓÔÕ×ÁÞÁ: '%-.32s' (%-.64s)"
+ 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"
+ 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 connection"
- 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 "ïÔÒÉÍÁÎÏ ÐÏÍÉÌËÕ ÞÉÔÁÎÎÑ Ú ËÏÍÕΦËÁæÊÎÏÇÏ ËÁÎÁÌÕ"
+ 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()"
+ 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 "ïÔÒÉÍÁÎÏ ÐÁËÅÔÉ Õ ÎÅÎÁÌÅÖÎÏÍÕ ÐÏÒÑÄËÕ"
+ 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 "îÅ ÍÏÖÕ ÄÅËÏÍÐÒÅÓÕ×ÁÔÉ ËÏÍÕΦËÁæÊÎÉÊ ÐÁËÅÔ"
+ 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 "ïÔÒÉÍÁÎÏ ÐÏÍÉÌËÕ ÞÉÔÁÎÎÑ ËÏÍÕΦËÁæÊÎÉÈ ÐÁËÅÔ¦×"
+ 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 "ïÔÒÉÍÁÎÏ ÚÁÔÒÉÍËÕ ÞÉÔÁÎÎÑ ËÏÍÕΦËÁæÊÎÉÈ ÐÁËÅÔ¦×"
+ 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 "ïÔÒÉÍÁÎÏ ÐÏÍÉÌËÕ ÚÁÐÉÓÕ ËÏÍÕΦËÁæÊÎÉÈ ÐÁËÅÔ¦×"
+ 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 "ïÔÒÉÍÁÎÏ ÚÁÔÒÉÍËÕ ÚÁÐÉÓÕ ËÏÍÕΦËÁæÊÎÉÈ ÐÁËÅÔ¦×"
+ 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"
+ 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 ÓÔÏ×Âæ"
+ 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 ÓÔÏ×Âæ"
+ 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 '%-.64s' pou¾ít, proto¾e je zamèená pomocí LOCK TABLES"
- dan "INSERT DELAYED kan ikke bruges med tabellen '%-.64s', fordi tabellen er låst med LOCK TABLES"
- nla "INSERT DELAYED kan niet worden gebruikt bij table '%-.64s', vanwege een 'lock met LOCK TABLES"
- eng "INSERT DELAYED can't be used with table '%-.64s' because it is locked with LOCK TABLES"
- est "INSERT DELAYED ei saa kasutada tabeli '%-.64s' peal, kuna see on lukustatud LOCK TABLES käsuga"
- fre "INSERT DELAYED ne peut être utilisé avec la table '%-.64s', car elle est verrouée avec LOCK TABLES"
- ger "INSERT DELAYED kann für Tabelle '%-.64s' nicht verwendet werden, da sie mit LOCK TABLES gesperrt ist"
- greek "INSERT DELAYED can't be used with table '%-.64s', because it is locked with LOCK TABLES"
- hun "Az INSERT DELAYED nem hasznalhato a '%-.64s' tablahoz, mert a tabla zarolt (LOCK TABLES)"
- ita "L'inserimento ritardato (INSERT DELAYED) non puo` essere usato con la tabella '%-.64s', perche` soggetta a lock da 'LOCK TABLES'"
- jpn "INSERT DELAYED can't be used with table '%-.64s', because it is locked with LOCK TABLES"
- kor "INSERT DELAYED can't be used with table '%-.64s', because it is locked with LOCK TABLES"
- nor "INSERT DELAYED can't be used with table '%-.64s', because it is locked with LOCK TABLES"
- norwegian-ny "INSERT DELAYED can't be used with table '%-.64s', because it is locked with LOCK TABLES"
- pol "INSERT DELAYED can't be used with table '%-.64s', because it is locked with LOCK TABLES"
- por "INSERT DELAYED não pode ser usado com a tabela '%-.64s', porque ela está travada com LOCK TABLES"
- rum "INSERT DELAYED nu poate fi folosit cu tabela '%-.64s', deoarece este locked folosing LOCK TABLES"
- rus "îÅÌØÚÑ ÉÓÐÏÌØÚÏ×ÁÔØ INSERT DELAYED ÄÌÑ ÔÁÂÌÉÃÙ '%-.64s', ÐÏÔÏÍÕ ÞÔÏ ÏÎÁ ÚÁÂÌÏËÉÒÏ×ÁÎÁ Ó ÐÏÍÏÝØÀ LOCK TABLES"
- serbian "Komanda 'INSERT DELAYED' ne može biti iskorištena u tabeli '%-.64s', zbog toga što je zakljuèana komandom 'LOCK TABLES'"
- slo "INSERT DELAYED can't be used with table '%-.64s', because it is locked with LOCK TABLES"
- spa "INSERT DELAYED no puede ser usado con tablas '%-.64s', porque esta bloqueada con LOCK TABLES"
- swe "INSERT DELAYED kan inte användas med tabell '%-.64s', emedan den är låst med LOCK TABLES"
- ukr "INSERT DELAYED ÎÅ ÍÏÖÅ ÂÕÔÉ ×ÉËÏÒÉÓÔÁÎÏ Ú ÔÁÂÌÉÃÅÀ '%-.64s', ÔÏÍÕ ÝÏ §§ ÚÁÂÌÏËÏ×ÁÎÏ Ú LOCK TABLES"
+ 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'"
+ 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 '%-.64s'"
- dan "Den brugte tabeltype kan ikke indeksere kolonnen '%-.64s'"
- nla "De gebruikte tabel 'handler' kan kolom '%-.64s' niet indexeren"
- eng "The used storage engine can't index column '%-.64s'"
- est "Tabelihandler ei oska indekseerida tulpa '%-.64s'"
- fre "Le handler de la table ne peut indexé la colonne '%-.64s'"
- ger "Die verwendete Speicher-Engine kann die Spalte '%-.64s' nicht indizieren"
- greek "The used table handler can't index column '%-.64s'"
- hun "A hasznalt tablakezelo nem tudja a '%-.64s' mezot indexelni"
- ita "Il gestore delle tabelle non puo` indicizzare la colonna '%-.64s'"
- jpn "The used table handler can't index column '%-.64s'"
- kor "The used table handler can't index column '%-.64s'"
- nor "The used table handler can't index column '%-.64s'"
- norwegian-ny "The used table handler can't index column '%-.64s'"
- pol "The used table handler can't index column '%-.64s'"
- por "O manipulador de tabela usado não pode indexar a coluna '%-.64s'"
- rum "Handler-ul tabelei folosite nu poate indexa coloana '%-.64s'"
- rus "éÓÐÏÌØÚÏ×ÁÎÎÙÊ ÏÂÒÁÂÏÔÞÉË ÔÁÂÌÉÃÙ ÎÅ ÍÏÖÅÔ ÐÒÏÉÎÄÅËÓÉÒÏ×ÁÔØ ÓÔÏÌÂÅà '%-.64s'"
- serbian "Handler tabele ne može da indeksira kolonu '%-.64s'"
- slo "The used table handler can't index column '%-.64s'"
- spa "El manipulador de tabla usado no puede indexar columna '%-.64s'"
- swe "Den använda tabelltypen kan inte indexera kolumn '%-.64s'"
- ukr "÷ÉËÏÒÉÓÔÁÎÉÊ ×ËÁÚ¦×ÎÉË ÔÁÂÌÉæ ÎÅ ÍÏÖÅ ¦ÎÄÅËÓÕ×ÁÔÉ ÓÔÏ×ÂÅÃØ '%-.64s'"
+ 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 ÍÁÀÔØ Ò¦ÚÎÕ ÓÔÒÕËÔÕÒÕ"
+ 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 '%-.64s'"
- dan "Kan ikke skrive til tabellen '%-.64s' fordi det vil bryde CONSTRAINT regler"
- nla "Kan niet opslaan naar table '%-.64s' vanwege 'unique' beperking"
- eng "Can't write, because of unique constraint, to table '%-.64s'"
- est "Ei suuda kirjutada tabelisse '%-.64s', kuna see rikub ühesuse kitsendust"
- fre "Écriture impossible à cause d'un index UNIQUE sur la table '%-.64s'"
- ger "Schreiben in Tabelle '%-.64s' nicht möglich wegen einer Eindeutigkeitsbeschränkung (unique constraint)"
- hun "A '%-.64s' nem irhato, az egyedi mezok miatt"
- ita "Impossibile scrivere nella tabella '%-.64s' per limitazione di unicita`"
- por "Não pode gravar, devido à restrição UNIQUE, na tabela '%-.64s'"
- rum "Nu pot scrie pe hard-drive, din cauza constraintului unic (unique constraint) pentru tabela '%-.64s'"
- rus "îÅ×ÏÚÍÏÖÎÏ ÚÁÐÉÓÁÔØ × ÔÁÂÌÉÃÕ '%-.64s' ÉÚ-ÚÁ ÏÇÒÁÎÉÞÅÎÉÊ ÕÎÉËÁÌØÎÏÇÏ ËÌÀÞÁ"
- serbian "Zbog provere jedinstvenosti ne mogu da upišem podatke u tabelu '%-.64s'"
- spa "No puedo escribir, debido al único constraint, para tabla '%-.64s'"
- swe "Kan inte skriva till tabell '%-.64s'; UNIQUE-test"
- ukr "îÅ ÍÏÖÕ ÚÁÐÉÓÁÔÉ ÄÏ ÔÁÂÌÉæ '%-.64s', Ú ÐÒÉÞÉÎÉ ×ÉÍÏÇ ÕΦËÁÌØÎÏÓÔ¦"
+ 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 '%-.64s' je pou-B¾it ve specifikaci klíèe bez délky"
- dan "BLOB kolonnen '%-.64s' brugt i nøglespecifikation uden nøglelængde"
- nla "BLOB kolom '%-.64s' gebruikt in zoeksleutel specificatie zonder zoeksleutel lengte"
- eng "BLOB/TEXT column '%-.64s' used in key specification without a key length"
- est "BLOB-tüüpi tulp '%-.64s' on kasutusel võtmes ilma pikkust määratlemata"
- fre "La colonne '%-.64s' de type BLOB est utilisée dans une définition d'index sans longueur d'index"
- ger "BLOB- oder TEXT-Spalte '%-.64s' wird in der Schlüsseldefinition ohne Schlüssellängenangabe verwendet"
- greek "BLOB column '%-.64s' used in key specification without a key length"
- hun "BLOB mezo '%-.64s' hasznalt a mezo specifikacioban, a mezohossz megadasa nelkul"
- ita "La colonna '%-.64s' di tipo BLOB e` usata in una chiave senza specificarne la lunghezza"
- jpn "BLOB column '%-.64s' used in key specification without a key length"
- kor "BLOB column '%-.64s' used in key specification without a key length"
- nor "BLOB column '%-.64s' used in key specification without a key length"
- norwegian-ny "BLOB column '%-.64s' used in key specification without a key length"
- pol "BLOB column '%-.64s' used in key specification without a key length"
- por "Coluna BLOB '%-.64s' usada na especificação de chave sem o comprimento da chave"
- rum "Coloana BLOB '%-.64s' este folosita in specificarea unei chei fara ca o lungime de cheie sa fie folosita"
- rus "óÔÏÌÂÅÃ ÔÉÐÁ BLOB '%-.64s' ÂÙÌ ÕËÁÚÁÎ × ÏÐÒÅÄÅÌÅÎÉÉ ËÌÀÞÁ ÂÅÚ ÕËÁÚÁÎÉÑ ÄÌÉÎÙ ËÌÀÞÁ"
- serbian "BLOB kolona '%-.64s' je upotrebljena u specifikaciji kljuèa bez navoðenja dužine kljuèa"
- slo "BLOB column '%-.64s' used in key specification without a key length"
- spa "Columna BLOB column '%-.64s' usada en especificación de clave sin tamaño de la clave"
- swe "Du har inte angett någon nyckellängd för BLOB '%-.64s'"
- ukr "óÔÏ×ÂÅÃØ BLOB '%-.64s' ×ÉËÏÒÉÓÔÁÎÏ Õ ×ÉÚÎÁÞÅÎΦ ËÌÀÞÁ ÂÅÚ ×ËÁÚÁÎÎÑ ÄÏ×ÖÉÎÉ ËÌÀÞÁ"
+ 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"
+ 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 "òÅÚÕÌØÔÁÔ ÚÎÁÈÏÄÉÔØÓÑ Õ Â¦ÌØÛÅ Î¦Ö ÏÄÎ¦Ê ÓÔÒÏæ"
+ 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 "ãÅÊ ÔÉÐ ÔÁÂÌÉæ ÐÏÔÒÅÂÕ¤ ÐÅÒ×ÉÎÎÏÇÏ ËÌÀÞÁ"
+ 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"
+ 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
- cze "Kl-Bíè '%-.64s' v tabulce '%-.64s' neexistuje"
- dan "Nøglen '%-.64s' eksisterer ikke i tabellen '%-.64s'"
- nla "Zoeksleutel '%-.64s' bestaat niet in tabel '%-.64s'"
- eng "Key '%-.64s' doesn't exist in table '%-.64s'"
- est "Võti '%-.64s' ei eksisteeri tabelis '%-.64s'"
- fre "L'index '%-.64s' n'existe pas sur la table '%-.64s'"
- ger "Schlüssel '%-.64s' existiert in der Tabelle '%-.64s' nicht"
- hun "A '%-.64s' kulcs nem letezik a '%-.64s' tablaban"
- ita "La chiave '%-.64s' non esiste nella tabella '%-.64s'"
- por "Chave '%-.64s' não existe na tabela '%-.64s'"
- rus "ëÌÀÞ '%-.64s' ÎÅ ÓÕÝÅÓÔ×ÕÅÔ × ÔÁÂÌÉÃÅ '%-.64s'"
- serbian "Kljuè '%-.64s' ne postoji u tabeli '%-.64s'"
- spa "Clave '%-.64s' no existe en la tabla '%-.64s'"
- swe "Nyckel '%-.64s' finns inte in tabell '%-.64s'"
- ukr "ëÌÀÞ '%-.64s' ÎÅ ¦ÓÎÕ¤ × ÔÁÂÌÉæ '%-.64s'"
+ 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 "îÅ ÍÏÖÕ ×¦ÄËÒÉÔÉ ÔÁÂÌÉÃÀ"
+ 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"
+ 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 "÷ÁÍ ÎÅ ÄÏÚ×ÏÌÅÎÏ ×ÉËÏÎÕ×ÁÔÉ ÃÀ ËÏÍÁÎÄÕ × ÔÒÁÎÚÁËæ§"
+ 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"
+ 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"
+ 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"
+ 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"
+ 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: '%-.64s' u¾ivatel: '%-.32s' stroj: '%-.64s' (%-.64s) bylo pøeru¹eno"
- dan "Afbrød forbindelsen %ld til databasen '%-.64s' bruger: '%-.32s' vært: '%-.64s' (%-.64s)"
- nla "Afgebroken verbinding %ld naar db: '%-.64s' gebruiker: '%-.32s' host: '%-.64s' (%-.64s)"
- eng "Aborted connection %ld to db: '%-.64s' user: '%-.32s' host: '%-.64s' (%-.64s)"
- est "Ühendus katkestatud %ld andmebaas: '%-.64s' kasutaja: '%-.32s' masin: '%-.64s' (%-.64s)"
- fre "Connection %ld avortée vers la bd: '%-.64s' utilisateur: '%-.32s' hôte: '%-.64s' (%-.64s)"
- ger "Abbruch der Verbindung %ld zur Datenbank '%-.64s'. Benutzer: '%-.32s', Host: '%-.64s' (%-.64s)"
- ita "Interrotta la connessione %ld al db: ''%-.64s' utente: '%-.32s' host: '%-.64s' (%-.64s)"
- por "Conexão %ld abortada para banco de dados '%-.64s' - usuário '%-.32s' - 'host' '%-.64s' ('%-.64s')"
- rus "ðÒÅÒ×ÁÎÏ ÓÏÅÄÉÎÅÎÉÅ %ld Ë ÂÁÚÅ ÄÁÎÎÙÈ '%-.64s' ÐÏÌØÚÏ×ÁÔÅÌÑ '%-.32s' Ó ÈÏÓÔÁ '%-.64s' (%-.64s)"
- serbian "Prekinuta konekcija broj %ld ka bazi: '%-.64s' korisnik je bio: '%-.32s' a host: '%-.64s' (%-.64s)"
- spa "Abortada conexión %ld para db: '%-.64s' usuario: '%-.32s' servidor: '%-.64s' (%-.64s)"
- swe "Avbröt länken för tråd %ld till db '%-.64s', användare '%-.32s', host '%-.64s' (%-.64s)"
- ukr "ðÅÒÅÒ×ÁÎÏ Ú'¤ÄÎÁÎÎÑ %ld ÄÏ ÂÁÚÉ ÄÁÎÎÉÈ: '%-.64s' ËÏÒÉÓÔÕ×ÁÞ: '%-.32s' ÈÏÓÔ: '%-.64s' (%-.64s)"
+ 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 "ãÅÊ ÔÉÐ ÔÁÂÌÉæ ΊЦÄÔÒÉÍÕ¤ ¦ÎÁÒÎÕ ÐÅÒÅÄÁÞÕ ÔÁÂÌÉæ"
+ 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"
+ 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 '%-.64s' nebylo úspì¹né"
- dan "Kunne ikke genopbygge indekset for den dumpede tabel '%-.64s'"
- nla "Gefaald tijdens heropbouw index van gedumpte tabel '%-.64s'"
- eng "Failed rebuilding the index of dumped table '%-.64s'"
- fre "La reconstruction de l'index de la table copiée '%-.64s' a échoué"
- ger "Neuerstellung des Index der Dump-Tabelle '%-.64s' fehlgeschlagen"
- greek "Failed rebuilding the index of dumped table '%-.64s'"
- hun "Failed rebuilding the index of dumped table '%-.64s'"
- ita "Fallita la ricostruzione dell'indice della tabella copiata '%-.64s'"
- por "Falhou na reconstrução do índice da tabela 'dumped' '%-.64s'"
- rus "ïÛÉÂËÁ ÐÅÒÅÓÔÒÏÊËÉ ÉÎÄÅËÓÁ ÓÏÈÒÁÎÅÎÎÏÊ ÔÁÂÌÉÃÙ '%-.64s'"
- serbian "Izgradnja indeksa dump-ovane tabele '%-.64s' nije uspela"
- spa "Falla reconstruyendo el indice de la tabla dumped '%-.64s'"
- ukr "îÅ×ÄÁÌŠצÄÎÏ×ÌÅÎÎÑ ¦ÎÄÅËÓÁ ÐÅÒÅÄÁÎϧ ÔÁÂÌÉæ '%-.64s'"
+ 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'"
+ 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 "íÅÒÅÖÅ×Á ÐÏÍÉÌËÁ ÞÉÔÁÎÎÑ ×¦Ä ÇÏÌÏ×ÎÏÇÏ"
+ 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 "íÅÒÅÖÅ×Á ÐÏÍÉÌËÁ ÚÁÐÉÓÕ ÄÏ ÇÏÌÏ×ÎÏÇÏ"
+ 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 ¦ÎÄÅËÓ, ÝÏ ×¦ÄÐÏצÄÁ¤ ÐÅÒÅ̦ËÕ ÓÔÏ×Âæ×"
+ 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 "îÅ ÍÏÖÕ ×ÉËÏÎÁÔÉ ÐÏÄÁÎÕ ËÏÍÁÎÄÕ ÔÏÍÕ, ÝÏ ÔÁÂÌÉÃÑ ÚÁÂÌÏËÏ×ÁÎÁ ÁÂÏ ×ÉËÏÎÕ¤ÔØÓÑ ÔÒÁÎÚÁËæÑ"
+ 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'"
+ 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 '%-.64s' je ozna-Bèena jako poru¹ená a mìla by být opravena"
- dan "Tabellen '%-.64s' er markeret med fejl og bør repareres"
- nla "Tabel '%-.64s' staat als gecrashed gemarkeerd en dient te worden gerepareerd"
- eng "Table '%-.64s' is marked as crashed and should be repaired"
- est "Tabel '%-.64s' on märgitud vigaseks ja tuleb parandada"
- fre "La table '%-.64s' est marquée 'crashed' et devrait être réparée"
- ger "Tabelle '%-.64s' ist als defekt markiert und sollte repariert werden"
- ita "La tabella '%-.64s' e` segnalata come corrotta e deve essere riparata"
- por "Tabela '%-.64s' está marcada como danificada e deve ser reparada"
- rus "ôÁÂÌÉÃÁ '%-.64s' ÐÏÍÅÞÅÎÁ ËÁË ÉÓÐÏÒÞÅÎÎÁÑ É ÄÏÌÖÎÁ ÐÒÏÊÔÉ ÐÒÏ×ÅÒËÕ É ÒÅÍÏÎÔ"
- serbian "Tabela '%-.64s' je markirana kao ošteæena i trebala bi biti popravljena"
- spa "Tabla '%-.64s' está marcada como crashed y debe ser reparada"
- swe "Tabell '%-.64s' är trasig och bör repareras med REPAIR TABLE"
- ukr "ôÁÂÌÉÃÀ '%-.64s' ÍÁÒËÏ×ÁÎÏ ÑË Ú¦ÐÓÏ×ÁÎÕ ÔÁ §§ ÐÏÔÒ¦ÂÎÏ ×¦ÄÎÏ×ÉÔÉ"
+ 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 '%-.64s' je ozna-Bèena jako poru¹ená a poslední (automatická?) oprava se nezdaøila"
- dan "Tabellen '%-.64s' er markeret med fejl og sidste (automatiske?) REPAIR fejlede"
- nla "Tabel '%-.64s' staat als gecrashed gemarkeerd en de laatste (automatische?) reparatie poging mislukte"
- eng "Table '%-.64s' is marked as crashed and last (automatic?) repair failed"
- est "Tabel '%-.64s' on märgitud vigaseks ja viimane (automaatne?) parandus ebaõnnestus"
- fre "La table '%-.64s' est marquée 'crashed' et le dernier 'repair' a échoué"
- ger "Tabelle '%-.64s' ist als defekt markiert und der letzte (automatische?) Reparaturversuch schlug fehl"
- ita "La tabella '%-.64s' e` segnalata come corrotta e l'ultima ricostruzione (automatica?) e` fallita"
- por "Tabela '%-.64s' está marcada como danificada e a última reparação (automática?) falhou"
- rus "ôÁÂÌÉÃÁ '%-.64s' ÐÏÍÅÞÅÎÁ ËÁË ÉÓÐÏÒÞÅÎÎÁÑ É ÐÏÓÌÅÄÎÉÊ (Á×ÔÏÍÁÔÉÞÅÓËÉÊ?) ÒÅÍÏÎÔ ÎÅ ÂÙÌ ÕÓÐÅÛÎÙÍ"
- serbian "Tabela '%-.64s' je markirana kao ošteæena, a zadnja (automatska?) popravka je bila neuspela"
- spa "Tabla '%-.64s' está marcada como crashed y la última reparación (automactica?) falló"
- swe "Tabell '%-.64s' är trasig och senast (automatiska?) reparation misslyckades"
- ukr "ôÁÂÌÉÃÀ '%-.64s' ÍÁÒËÏ×ÁÎÏ ÑË Ú¦ÐÓÏ×ÁÎÕ ÔÁ ÏÓÔÁÎΤ (Á×ÔÏÍÁÔÉÞÎÅ?) צÄÎÏ×ÌÅÎÎÑ ÎÅ ×ÄÁÌÏÓÑ"
+ 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 "úÁÓÔÅÒÅÖÅÎÎÑ: äÅÑ˦ ÎÅÔÒÁÎÚÁËæÊΦ ÚͦÎÉ ÔÁÂÌÉÃØ ÎÅ ÍÏÖÎÁ ÂÕÄÅ ÐÏ×ÅÒÎÕÔÉ"
+ 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 ÔÁ ÓÐÒÏÂÕÊÔÅ ÚÎÏ×Õ"
+ 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"
+ 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"
+ 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"
+ 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"
+ 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 "îÅ ÍÏÖÕ ÓÔ×ÏÒÉÔÉ Ð¦ÄÌÅÇÌÕ Ç¦ÌËÕ, ÐÅÒÅצÒÔÅ ÓÉÓÔÅÍΦ ÒÅÓÕÒÓÉ"
+ 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' connections 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' ÁËÔÉ×ÎÉÈ Ú'¤ÄÎÁÎØ"
+ 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"
+ 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 "úÁÔÒÉÍËÕ ÏÞ¦ËÕ×ÁÎÎÑ ÂÌÏËÕ×ÁÎÎÑ ×ÉÞÅÒÐÁÎÏ"
+ 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 "úÁÇÁÌØÎÁ ˦ÌØ˦ÓÔØ ÂÌÏËÕ×ÁÎØ ÐÅÒÅ×ÉÝÉÌÁ ÒÏÚÍ¦Ò ÂÌÏËÕ×ÁÎØ ÄÌÑ ÔÁÂÌÉæ"
+ 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"
+ 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 ÎÅ ÄÏÚ×ÏÌÅÎÏ ÄÏËÉ Ç¦ÌËÁ ÐÅÒÅÂÕ×Á¤ Ð¦Ä ÚÁÇÁÌØÎÉÍ ÂÌÏËÕ×ÁÎÎÑÍ ÞÉÔÁÎÎÑ"
+ 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 ÎÅ ÄÏÚ×ÏÌÅÎÏ ÄÏËÉ Ç¦ÌËÁ ÐÅÒÅÂÕ×Á¤ Ð¦Ä ÚÁÇÁÌØÎÉÍ ÂÌÏËÕ×ÁÎÎÑÍ ÞÉÔÁÎÎÑ"
+ 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"
+ 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 "'%-.32s'@'%-.64s' mag geen nieuwe gebruikers creeren"
- eng "'%-.32s'@'%-.64s' is not allowed to create new users"
- est "Kasutajal '%-.32s'@'%-.64s' ei ole lubatud luua uusi kasutajaid"
- fre "'%-.32s'@'%-.64s' n'est pas autorisé à créer de nouveaux utilisateurs"
- ger "'%-.32s'@'%-.64s' ist nicht berechtigt, neue Benutzer hinzuzufügen"
- ita "A '%-.32s'@'%-.64s' non e' permesso creare nuovi utenti"
- por "Não é permitido a '%-.32s'@'%-.64s' criar novos usuários"
- rus "'%-.32s'@'%-.64s' ÎÅ ÒÁÚÒÅÛÁÅÔÓÑ ÓÏÚÄÁ×ÁÔØ ÎÏ×ÙÈ ÐÏÌØÚÏ×ÁÔÅÌÅÊ"
- serbian "Korisniku '%-.32s'@'%-.64s' nije dozvoljeno da kreira nove korisnike"
- spa "'%-.32s`@`%-.64s` no es permitido para crear nuevos usuarios"
- swe "'%-.32s'@'%-.64s' har inte rättighet att skapa nya användare"
- ukr "ëÏÒÉÓÔÕ×ÁÞÕ '%-.32s'@'%-.64s' ÎÅ ÄÏÚ×ÏÌÅÎÏ ÓÔ×ÏÒÀ×ÁÔÉ ÎÏ×ÉÈ ËÏÒÉÓÔÕ×ÁÞ¦×"
+ 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"
+ 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"
+ 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 ¦ÎÄÅËÓ¦×"
+ 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'"
+ 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"
+ 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"
+ 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"
+ 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"
+ 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"
+ 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"
+ 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"
+ 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"
+ 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"
+ 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"
+ 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)"
+ 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"
+ 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"
+ 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"
+ 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"
+ 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 '%-.64s'"
- eng "Variable '%-.64s' can't be set to the value of '%-.64s'"
- ger "Variable '%-.64s' kann nicht auf '%-.64s' gesetzt werden"
- ita "Alla variabile '%-.64s' non puo' essere assegato il valore '%-.64s'"
- por "Variável '%-.64s' não pode ser configurada para o valor de '%-.64s'"
- rus "ðÅÒÅÍÅÎÎÁÑ '%-.64s' ÎÅ ÍÏÖÅÔ ÂÙÔØ ÕÓÔÁÎÏ×ÌÅÎÁ × ÚÎÁÞÅÎÉÅ '%-.64s'"
- spa "Variable '%-.64s' no puede ser configurada para el valor de '%-.64s'"
- swe "Variabel '%-.64s' kan inte sättas till '%-.64s'"
+ 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'"
+ 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"
+ 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'"
+ 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'"
+ 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: '%-.128s' from master when reading data from binary log"
- 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"
+ nla "Kreeg fatale fout %d: '%-.128s' van master tijdens lezen van data uit binaire log"
+ eng "Got fatal error %d: '%-.128s' from master when reading data from binary log"
+ 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"
- 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"
+ 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 '%-.64s' is a %s variable"
- serbian "Promenljiva '%-.64s' je %s promenljiva"
- ger "Variable '%-.64s' ist eine %s-Variable"
- spa "Variable '%-.64s' es una %s variable"
- swe "Variabel '%-.64s' är av typ %s"
+ 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 '%-.64s': %s"
- ger "Falsche Fremdschlüssel-Definition für '%-.64s': %s"
- por "Definição errada da chave estrangeira para '%-.64s': %s"
- spa "Equivocada definición de llave extranjera para '%-.64s': %s"
- swe "Felaktig FOREIGN KEY-definition för '%-.64s': %s"
+ 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"
- 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"
+ 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"
- rus "ïÐÅÒÁÎÄ ÄÏÌÖÅÎ ÓÏÄÅÒÖÁÔØ %d ËÏÌÏÎÏË"
- spa "Operando debe tener %d columna(s)"
- ukr "ïÐÅÒÁÎÄ ÍÁ¤ ÓËÌÁÄÁÔÉÓÑ Ú %d ÓÔÏ×Âæ×"
+ 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"
- 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 ÚÁÐÉÓ"
+ 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"
- 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"
+ 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"
- 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"
+ 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"
- por "Referência cíclica em subconsultas"
- rus "ãÉËÌÉÞÅÓËÁÑ ÓÓÙÌËÁ ÎÁ ÐÏÄÚÁÐÒÏÓ"
- spa "Cíclica referencia en subconsultas"
- swe "Cyklisk referens i subqueries"
- ukr "ãÉË̦ÞÎÅ ÐÏÓÉÌÁÎÎÑ ÎÁ ЦÄÚÁÐÉÔ"
+ 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"
- 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"
+ 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)"
- 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)"
+ 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"
- 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"
+ 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"
- 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"
+ 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 '%-.64s' from one of the SELECTs cannot be used in %-.32s"
- ger "Tabelle '%-.64s', die in einem der SELECT-Befehle verwendet wurde, kann nicht in %-.32s verwendet werden"
- por "Tabela '%-.64s' de um dos SELECTs não pode ser usada em %-.32s"
- spa "Tabla '%-.64s' de uno de los SELECT no puede ser usada en %-.32s"
- swe "Tabell '%-.64s' från en SELECT kan inte användas i %-.32s"
+ 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"
- 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."
+ 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"
- 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"
+ 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"
- 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'"
+ 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"
- por "O slave já está rodando"
- spa "Slave ya está funcionando"
- swe "Slaven har redan startat"
+ 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"
- por "O slave já está parado"
- spa "Slave ya fué parado"
- swe "Slaven har redan stoppat"
+ 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)"
- 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)"
+ 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"
- por "ZLIB: Não suficiente memória disponível"
- spa "Z_MEM_ERROR: No suficiente memoria para zlib"
+ 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)"
- 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)"
+ 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"
- por "ZLIB: Dados de entrada está corrupto"
- spa "ZLIB: Dato de entrada fué corrompido para zlib"
+ 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"
- 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()"
+ 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 %ld doesn't contain data for all columns"
- ger "Zeile %ld enthält nicht für alle Felder Daten"
- por "Conta de registro é menor que a conta de coluna na linha %ld"
- spa "Línea %ld no contiene datos para todas las columnas"
+ eng "Row %ld doesn't contain data for all columns"
+ ger "Zeile %ld enthält nicht für alle Felder Daten"
+ nla "Rij %ld bevat niet de data voor alle kolommen"
+ por "Conta de registro é menor que a conta de coluna na linha %ld"
+ spa "Línea %ld no contiene datos para todas las columnas"
ER_WARN_TOO_MANY_RECORDS 01000
- eng "Row %ld was truncated; it contained more data than there were input columns"
- ger "Zeile %ld gekürzt, die Zeile enthielt mehr Daten, als es Eingabefelder gibt"
- por "Conta de registro é maior que a conta de coluna na linha %ld"
- spa "Línea %ld fué truncada; La misma contine mas datos que las que existen en las columnas de entrada"
+ eng "Row %ld was truncated; it contained more data than there were input columns"
+ ger "Zeile %ld gekürzt, die Zeile enthielt mehr Daten, als es Eingabefelder gibt"
+ nla "Regel %ld ingekort, bevatte meer data dan invoer kolommen"
+ por "Conta de registro é maior que a conta de coluna na linha %ld"
+ spa "Línea %ld fué truncada; La misma contine mas datos que las que existen en las columnas de entrada"
ER_WARN_NULL_TO_NOTNULL 22004
- eng "Column was set to data type implicit default; NULL supplied for NOT NULL column '%s' at row %ld"
- ger "Feld auf Datentyp-spezifischen Vorgabewert gesetzt; da NULL für NOT-NULL-Feld '%s' in Zeile %ld angegeben"
- por "Dado truncado, NULL fornecido para NOT NULL coluna '%s' na linha %ld"
- spa "Datos truncado, NULL suministrado para NOT NULL columna '%s' en la línea %ld"
+ eng "Column set to default value; NULL supplied to NOT NULL column '%s' at row %ld"
+ ger "Feld auf Vorgabewert gesetzt, da NULL für NOT-NULL-Feld '%s' in Zeile %ld angegeben"
+ por "Dado truncado, NULL fornecido para NOT NULL coluna '%s' na linha %ld"
+ spa "Datos truncado, NULL suministrado para NOT NULL columna '%s' en la línea %ld"
ER_WARN_DATA_OUT_OF_RANGE 22003
- eng "Out of range value adjusted for column '%s' at row %ld"
- ger "Daten abgeschnitten, außerhalb des Wertebereichs für Feld '%s' in Zeile %ld"
- por "Dado truncado, fora de alcance para coluna '%s' na linha %ld"
- spa "Datos truncados, fuera de gama para columna '%s' en la línea %ld"
+ eng "Out of range value for column '%s' at row %ld"
WARN_DATA_TRUNCATED 01000
- eng "Data truncated for column '%s' at row %ld"
- ger "Daten abgeschnitten für Feld '%s' in Zeile %ld"
- por "Dado truncado para coluna '%s' na linha %ld"
- spa "Datos truncados para columna '%s' en la línea %ld"
+ eng "Data truncated for column '%s' at row %ld"
+ ger "Daten abgeschnitten für Feld '%s' in Zeile %ld"
+ por "Dado truncado para coluna '%s' na linha %ld"
+ spa "Datos truncados para columna '%s' en la línea %ld"
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'"
+ 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'"
+ 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"
+ 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"
+ 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'"
+ 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'"
+ 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)"
+ 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'"
+ 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"
+ 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"
+ 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 '%-.64s%s%-.64s%s%-.64s' of SELECT #%d was resolved in SELECT #%d"
- ger "Feld oder Verweis '%-.64s%s%-.64s%s%-.64s' im SELECT-Befehl Nr. %d wurde im SELECT-Befehl Nr. %d aufgelöst"
- por "Campo ou referência '%-.64s%s%-.64s%s%-.64s' de SELECT #%d foi resolvido em SELECT #%d"
- rus "ðÏÌÅ ÉÌÉ ÓÓÙÌËÁ '%-.64s%s%-.64s%s%-.64s' ÉÚ SELECTÁ #%d ÂÙÌÁ ÎÁÊÄÅÎÁ × SELECTÅ #%d"
- spa "Campo o referencia '%-.64s%s%-.64s%s%-.64s' de SELECT #%d fue resolvido en SELECT #%d"
- ukr "óÔÏ×ÂÅÃØ ÁÂÏ ÐÏÓÉÌÁÎÎÑ '%-.64s%s%-.64s%s%-.64s' ¦Ú SELECTÕ #%d ÂÕÌÏ ÚÎÁÊÄÅÎÅ Õ SELECT¦ #%d"
+ 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"
+ 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"
+ 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"
+ 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'"
+ 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'"
+ 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"
+ 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 '%-.64s' cannot be part of FULLTEXT index"
- ger "Feld '%-.64s' kann nicht Teil eines FULLTEXT-Index sein"
- por "Coluna '%-.64s' não pode ser parte de índice FULLTEXT"
- spa "Columna '%-.64s' no puede ser parte de FULLTEXT index"
- swe "Kolumn '%-.64s' kan inte vara del av ett FULLTEXT index"
+ 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'"
+ 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"
+ 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'"
+ 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; 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"
+ eng "'%s' is deprecated; 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 ÎÅ ÍÏÖÅ ÏÎÏ×ÌÀ×ÁÔÉÓØ"
+ 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"
+ 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"
+ 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"
+ 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'"
+ 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"
+ 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 '%-.64s' column"
- ger "Ungültige ON-UPDATE-Klausel für Spalte '%-.64s'"
- por "Inválida cláusula ON UPDATE para campo '%-.64s'"
- spa "Inválido ON UPDATE cláusula para campo '%-.64s'"
+ 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"
+ 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"
+ 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"
+ 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'"
+ 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 %ld"
- ger "Ungültiger TIMESTAMP-Wert in Feld '%s', Zeile %ld"
+ eng "Invalid TIMESTAMP value in column '%s' at row %ld"
+ ger "Ungültiger TIMESTAMP-Wert in Feld '%s', Zeile %ld"
ER_INVALID_CHARACTER_STRING
- eng "Invalid %s character string: '%.64s'"
- ger "Ungültiger %s-Zeichen-String: '%.64s'"
+ 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"
+ 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'"
+ 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"
+ 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"
+ 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"
+ 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"
+ 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"
+ 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"
+ 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"
+ 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"
+ 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"
+ 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"
+ 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"
+ 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"
+ 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"
- ger "Das Update-Log ist veraltet und wurde durch das Binär-Log ersetzt. SET SQL_LOG_UPDATE wird ignoriert"
+ eng "The update log is deprecated and replaced by the binary log; SET SQL_LOG_UPDATE has been ignored"
+ ger "Das Update-Log ist veraltet und wurde durch das Binär-Log ersetzt. SET SQL_LOG_UPDATE wird ignoriert"
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"
- 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"
+ eng "The update log is deprecated and replaced by the binary log; SET SQL_LOG_UPDATE has been translated to SET SQL_LOG_BIN"
+ 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"
ER_QUERY_INTERRUPTED 70100
- eng "Query execution was interrupted"
- ger "Ausführung der Abfrage wurde unterbrochen"
+ 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"
+ 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"
+ 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"
+ 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"
+ 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"
+ 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"
+ 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"
+ 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"
+ 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"
+ 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"
+ 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"
+ 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"
+ 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"
+ eng "Duplicate parameter: %s"
+ ger "Doppelter Parameter: %s"
ER_SP_DUP_VAR 42000
- eng "Duplicate variable: %s"
- ger "Doppelte Variable: %s"
+ eng "Duplicate variable: %s"
+ ger "Doppelte Variable: %s"
ER_SP_DUP_COND 42000
- eng "Duplicate condition: %s"
- ger "Doppelte Bedingung: %s"
+ eng "Duplicate condition: %s"
+ ger "Doppelte Bedingung: %s"
ER_SP_DUP_CURS 42000
- eng "Duplicate cursor: %s"
- ger "Doppelter Cursor: %s"
+ eng "Duplicate cursor: %s"
+ ger "Doppelter Cursor: %s"
ER_SP_CANT_ALTER
- eng "Failed to ALTER %s %s"
- ger "ALTER %s %s fehlgeschlagen"
+ eng "Failed to ALTER %s %s"
+ ger "ALTER %s %s fehlgeschlagen"
ER_SP_SUBSELECT_NYI 0A000
- eng "Subselect value not supported"
- ger "Subselect-Wert wird nicht unterstützt"
+ 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"
+ 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"
+ 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"
+ 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"
+ eng "Case not found for CASE statement"
+ ger "Fall für CASE-Anweisung nicht gefunden"
ER_FPARSER_TOO_BIG_FILE
- eng "Configuration file '%-.64s' is too big"
- ger "Konfigurationsdatei '%-.64s' ist zu groß"
- rus "óÌÉÛËÏÍ ÂÏÌØÛÏÊ ËÏÎÆÉÇÕÒÁÃÉÏÎÎÙÊ ÆÁÊÌ '%-.64s'"
- ukr "úÁÎÁÄÔÏ ×ÅÌÉËÉÊ ËÏÎƦÇÕÒÁæÊÎÉÊ ÆÁÊÌ '%-.64s'"
+ 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 '%-.64s'"
- ger "Nicht wohlgeformter Dateityp-Header in Datei '%-.64s'"
- rus "îÅ×ÅÒÎÙÊ ÚÁÇÏÌÏ×ÏË ÔÉÐÁ ÆÁÊÌÁ '%-.64s'"
- ukr "îÅצÒÎÉÊ ÚÁÇÏÌÏ×ÏË ÔÉÐÕ Õ ÆÁÊ̦ '%-.64s'"
+ 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'"
+ 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 '%-.64s' (line: '%-.64s')"
- ger "Fehler beim Parsen des Parameters '%-.64s' (Zeile: '%-.64s')"
- rus "ïÛÉÂËÁ ÐÒÉ ÒÁÓÐÏÚÎÁ×ÁÎÉÉ ÐÁÒÁÍÅÔÒÁ '%-.64s' (ÓÔÒÏËÁ: '%-.64s')"
- ukr "ðÏÍÉÌËÁ × ÒÏÓЦÚÎÁ×ÁÎΦ ÐÁÒÁÍÅÔÒÕ '%-.64s' (ÒÑÄÏË: '%-.64s')"
+ 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 '%-.64s'"
- ger "Unerwartetes Dateiende beim Überspringen des unbekannten Parameters '%-.64s'"
- rus "îÅÏÖÉÄÁÎÎÙÊ ËÏÎÅà ÆÁÊÌÁ ÐÒÉ ÐÒÏÐÕÓËÅ ÎÅÉÚ×ÅÓÔÎÏÇÏ ÐÁÒÁÍÅÔÒÁ '%-.64s'"
- ukr "îÅÓÐÏĦ×ÁÎÎÉÊ Ë¦ÎÅÃØ ÆÁÊÌÕ Õ ÓÐÒϦ ÐÒÏÍÉÎÕÔÉ ÎÅצÄÏÍÉÊ ÐÁÒÁÍÅÔÒ '%-.64s'"
+ 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 ÎÅ ÍÏÖÅ ÂÕÔÉ ×¦ËÏÎÁÎÏ; ÎÅÍÁ¤ ÐÒÁ× ÎÁ ÔÉÂÌÉæ ÚÁÐÉÔÕ"
+ 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 '%-.64s' has unknown type '%-.64s' in its header"
- ger "Datei '%-.64s' hat unbekannten Typ '%-.64s' im Header"
- rus "æÁÊÌ '%-.64s' ÓÏÄÅÒÖÉÔ ÎÅÉÚ×ÅÓÔÎÙÊ ÔÉÐ '%-.64s' × ÚÁÇÏÌÏ×ËÅ"
- ukr "æÁÊÌ '%-.64s' ÍÁ¤ ÎÅצÄÏÍÉÊ ÔÉÐ '%-.64s' Õ ÚÁÇÏÌÏ×ËÕ"
+ 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 "'%-.64s.%-.64s' is not %s"
- ger "'%-.64s.%-.64s' ist nicht %s"
- rus "'%-.64s.%-.64s' - ÎÅ %s"
- ukr "'%-.64s.%-.64s' ÎÅ ¤ %s"
+ 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 '%-.64s' is not updatable"
- ger "Feld '%-.64s' ist nicht aktualisierbar"
- rus "óÔÏÌÂÅà '%-.64s' ÎÅ ÏÂÎÏ×ÌÑÅÍÙÊ"
- ukr "óÔÏ×ÂÅÃØ '%-.64s' ÎÅ ÍÏÖÅ ÂÕÔÉ ÚÍÉÎÅÎÉÊ"
+ 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"
+ 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'"
+ 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 ÍÁ¤ ÚÍÉÎÎÕ ÁÂÏ ÐÁÒÁÍÅÔÅÒ"
+ 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 '%-.64s'"
- ger "SELECT der View verweist auf eine temporäre Tabelle '%-.64s'"
- rus "View SELECT ÓÏÄÅÒÖÉÔ ÓÓÙÌËÕ ÎÁ ×ÒÅÍÅÎÎÕÀ ÔÁÂÌÉÃÕ '%-.64s'"
- ukr "View SELECT ×ÉËÏÒÉÓÔÏ×Õ¤ ÔÉÍÞÁÓÏ×Õ ÔÁÂÌÉÃÀ '%-.64s'"
+ 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 ÍÁÀÔØ Ò¦ÚÎÕ Ë¦ÌØ˦ÓÔØ ÓËÏ×Âæ×"
+ 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 ÎÅ ÍÏÖÅ ÂÕÔÉ ×ÉËÏÒÉÓÔÁÎÉÊ ÚÁÒÁÚ (ÁÌÇÏÒÉÔÍ ÂÕÄÅ ÎÅ×ÉÚÎÁÞÅÎÉÊ)"
+ 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, ÝÏ ÏÎÏ×ÌÀÅÔØÓÑ, ΊͦÓÔÉÔØ ÐÏ×ÎÏÇÏ ËÌÀÞÁ ÔÁÂÌÉæ(Ø), ÝÏ ×ÉËÏÒ¦ÓÔÁÎÁ × ÎØÀÏÍÕ"
+ 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 '%-.64s.%-.64s' references invalid table(s) or column(s) or function(s) or definer/invoker of view lack rights to use them"
+ 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"
+ 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"
+ 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"
+ eng "Trigger already exists"
+ ger "Trigger existiert bereits"
ER_TRG_DOES_NOT_EXIST
- eng "Trigger does not exist"
- ger "Trigger existiert nicht"
+ eng "Trigger does not exist"
+ ger "Trigger existiert nicht"
ER_TRG_ON_VIEW_OR_TEMP_TABLE
- eng "Trigger's '%-.64s' is view or temporary table"
- ger "'%-.64s' des Triggers ist View oder temporäre Tabelle"
+ 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"
+ 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"
+ 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 '%-.64s' doesn't have a default value"
- ger "Feld '%-.64s' hat keinen Vorgabewert"
+ 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"
+ eng "Division by 0"
+ ger "Division durch 0"
ER_TRUNCATED_WRONG_VALUE_FOR_FIELD
- eng "Incorrect %-.32s value: '%-.128s' for column '%.64s' at row %ld"
- ger "Falscher %-.32s-Wert: '%-.128s' für Feld '%.64s' in Zeile %ld"
+ eng "Incorrect %-.32s value: '%-.128s' for column '%.192s' at row %ld"
+ ger "Falscher %-.32s-Wert: '%-.128s' für Feld '%.192s' in Zeile %ld"
ER_ILLEGAL_VALUE_FOR_TYPE 22007
- eng "Illegal %s '%-.64s' value found during parsing"
- ger "Nicht zulässiger %s-Wert '%-.64s' beim Parsen gefunden"
+ 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 '%-.64s.%-.64s'"
- ger "CHECK OPTION auf nicht-aktualisierbarem View '%-.64s.%-.64s'"
- rus "CHECK OPTION ÄÌÑ ÎÅÏÂÎÏ×ÌÑÅÍÏÇÏ VIEW '%-.64s.%-.64s'"
- ukr "CHECK OPTION ÄÌÑ VIEW '%-.64s.%-.64s' ÝÏ ÎÅ ÍÏÖÅ ÂÕÔÉ ÏÎÏ×ÌÅÎÎÉÍ"
+ 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 '%-.64s.%-.64s'"
- ger "CHECK OPTION fehlgeschlagen: '%-.64s.%-.64s'"
- rus "ÐÒÏ×ÅÒËÁ CHECK OPTION ÄÌÑ VIEW '%-.64s.%-.64s' ÐÒÏ×ÁÌÉÌÁÓØ"
- ukr "ðÅÒÅצÒËÁ CHECK OPTION ÄÌÑ VIEW '%-.64s.%-.64s' ÎÅ ÐÒÏÊÛÌÁ"
+ 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 '%-.32s'@'%-.64s' for routine '%-.64s'"
- ger "Befehl %-.16s nicht zulässig für Benutzer '%-.32s'@'%-.64s' in Routine '%-.64s'"
+ 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"
+ 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"
+ 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"
+ 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"
+ 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"
+ 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"
+ 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"
+ 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"
+ 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"
+ 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"
+ 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"
+ 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"
+ 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"
+ 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"
+ 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"
+ 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"
+ 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"
+ eng "Binary logging and replication forbid changing the global server %s"
+ ger "Binärlogs und Replikation verhindern Wechsel des globalen Servers %s"
ER_NO_FILE_MAPPING
- eng "Can't map file: %-.200s, errno: %d"
- ger "Kann Datei nicht abbilden: %-.200s, Fehler: %d"
+ eng "Can't map file: %-.200s, errno: %d"
+ ger "Kann Datei nicht abbilden: %-.200s, Fehler: %d"
ER_WRONG_MAGIC
- eng "Wrong magic in %-.64s"
- ger "Falsche magische Zahlen in %-.64s"
+ 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"
+ eng "Prepared statement contains too many placeholders"
+ ger "Vorbereitete Anweisung enthält zu viele Platzhalter"
ER_KEY_PART_0
- eng "Key part '%-.64s' length cannot be 0"
- ger "Länge des Schlüsselteils '%-.64s' kann nicht 0 sein"
+ 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 ÎÅ ÐÒÏÊÛÌÁ"
+ 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 '%-.64s.%-.64s'"
- ger "Kann nicht mehr als eine Basistabelle über Join-View '%-.64s.%-.64s' ändern"
- rus "îÅÌØÚÑ ÉÚÍÅÎÉÔØ ÂÏÌØÛÅ ÞÅÍ ÏÄÎÕ ÂÁÚÏ×ÕÀ ÔÁÂÌÉÃÕ ÉÓÐÏÌØÚÕÑ ÍÎÏÇÏÔÁÂÌÉÞÎÙÊ VIEW '%-.64s.%-.64s'"
- ukr "îÅÍÏÖÌÉ×Ï ÏÎÏ×ÉÔÉ Â¦ÌØÛ ÎÉÖ ÏÄÎÕ ÂÁÚÏ×Õ ÔÁÂÌÉÃÀ ×ÙËÏÒÉÓÔÏ×ÕÀÞÉ VIEW '%-.64s.%-.64s', ÝÏ Í¦ÓÔ¦ÔØ ÄÅ˦ÌØËÁ ÔÁÂÌÉÃØ"
+ 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 '%-.64s.%-.64s' without fields list"
- ger "Kann nicht ohne Feldliste in Join-View '%-.64s.%-.64s' einfügen"
- rus "îÅÌØÚÑ ×ÓÔÁ×ÌÑÔØ ÚÁÐÉÓÉ × ÍÎÏÇÏÔÁÂÌÉÞÎÙÊ VIEW '%-.64s.%-.64s' ÂÅÚ ÓÐÉÓËÁ ÐÏÌÅÊ"
- ukr "îÅÍÏÖÌÉ×Ï ÕÓÔÁ×ÉÔÉ ÒÑÄËÉ Õ VIEW '%-.64s.%-.64s', ÝÏ Í¦ÓÔÉÔØ ÄÅ˦ÌØËÁ ÔÁÂÌÉÃØ, ÂÅÚ ÓÐÉÓËÕ ÓÔÏ×Âæ×"
+ 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 '%-.64s.%-.64s'"
- ger "Kann nicht aus Join-View '%-.64s.%-.64s' löschen"
- rus "îÅÌØÚÑ ÕÄÁÌÑÔØ ÉÚ ÍÎÏÇÏÔÁÂÌÉÞÎÏÇÏ VIEW '%-.64s.%-.64s'"
- ukr "îÅÍÏÖÌÉ×Ï ×ÉÄÁÌÉÔÉ ÒÑÄËÉ Õ VIEW '%-.64s.%-.64s', ÝÏ Í¦ÓÔÉÔØ ÄÅ˦ÌØËÁ ÔÁÂÌÉÃØ"
+ 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'"
+ 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"
+ 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)"
+ 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"
+ 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"
+ 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"
+ ger "XA_RBROLLBACK: Transaktionszweig wurde zurückgerollt"
ER_NONEXISTING_PROC_GRANT 42000
- eng "There is no such grant defined for user '%-.32s' on host '%-.64s' on routine '%-.64s'"
- ger "Es gibt diese Berechtigung für Benutzer '%-.32s' auf Host '%-.64s' für Routine '%-.64s' nicht"
+ 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"
+ 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"
+ 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 %ld"
- ger "Daten zu lang für Feld '%s' in Zeile %ld"
+ eng "Data too long for column '%s' at row %ld"
+ ger "Daten zu lang für Feld '%s' in Zeile %ld"
ER_SP_BAD_SQLSTATE 42000
- eng "Bad SQLSTATE: '%s'"
- ger "Ungültiger SQLSTATE: '%s'"
+ 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"
+ 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"
+ 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"
+ 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"
+ 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"
+ 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"
+ 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"
+ 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"
+ 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"
+ 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"
+ 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)"
+ 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)"
+ 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"
+ 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"
+ 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"
+ ger "Explizites oder implizites Commit ist in gespeicherten Funktionen und in Triggern nicht erlaubt"
ER_NO_DEFAULT_FOR_VIEW_FIELD
- eng "Field of view '%-.64s.%-.64s' underlying table doesn't have a default value"
- ger "Ein Feld der dem View '%-.64s.%-.64s' zugrundeliegenden Tabelle hat keinen Vorgabewert"
+ 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"
+ ger "Rekursive gespeicherte Routinen und Triggers sind nicht erlaubt"
ER_TOO_BIG_SCALE 42000 S1009
- eng "Too big scale %lu specified for column '%-.64s'. Maximum is %d."
- ger "Zu großer Skalierungsfaktor %lu für Feld '%-.64s' angegeben. Maximum ist %d"
+ eng "Too big scale %d specified for column '%-.192s'. Maximum is %lu."
+ ger "Zu großer Skalierungsfaktor %d für Feld '%-.192s' angegeben. Maximum ist %lu"
ER_TOO_BIG_PRECISION 42000 S1009
- eng "Too big precision %lu specified for column '%-.64s'. Maximum is %lu."
- ger "Zu große Genauigkeit %lu für Feld '%-.64s' angegeben. Maximum ist %lu"
+ eng "Too big precision %d specified for column '%-.192s'. Maximum is %lu."
+ ger "Zu große Genauigkeit %d für Feld '%-.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 '%-.64s')."
- ger "Für FLOAT(M,D), DOUBLE(M,D) oder DECIMAL(M,D) muss M >= D sein (Feld '%-.64s')"
+ 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 '%-.64s.%-.64s' table with other tables"
- ger "Sie können Schreibsperren auf der Systemtabelle '%-.64s.%-.64s' nicht mit anderen Tabellen kombinieren"
+ 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"
+ 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"
+ 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"
+ 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"
+ 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"
+ 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"
+ 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"
+ 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"
+ 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"
+ 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 column '%-.64s' (max = %lu)"
- ger "Anzeigebreite außerhalb des zulässigen Bereichs für Spalte '%-.64s' (Maximum: %lu)"
+ eng "Display width out of range for column '%-.192s' (max = %lu)"
+ ger "Anzeigebreite außerhalb des zulässigen Bereichs für Spalte '%-.192s' (Maximum: %lu)"
ER_XAER_DUPID XAE08
eng "XAER_DUPID: The XID already exists"
- ger "XAER_DUPID: Die XID existiert bereits"
+ ger "XAER_DUPID: Die XID existiert bereits"
ER_DATETIME_FUNCTION_OVERFLOW 22008
eng "Datetime function: %-.32s field overflow"
- ger "Datetime-Funktion: %-.32s Feldüberlauf"
+ ger "Datetime-Funktion: %-.32s Feldüberlauf"
ER_CANT_UPDATE_USED_TABLE_IN_SF_OR_TRG
- eng "Can't update table '%-.64s' in stored function/trigger because it is already used by statement which invoked this stored function/trigger."
- ger "Kann Tabelle '%-.64s' in gespeicherter Funktion oder Trigger nicht aktualisieren, weil sie bereits von der Anweisung verwendet wird, die diese gespeicherte Funktion oder den Trigger aufrief"
+ 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 '%-.64s' prevents operation %.64s on table '%-.64s'."
- ger "Die Definition der Tabelle '%-.64s' verhindert die Operation %.64s auf Tabelle '%-.64s'"
+ 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"
+ 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"
+ 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"
+ eng "Definer is not fully qualified"
+ ger "Definierer des View ist nicht vollständig spezifiziert"
ER_VIEW_FRM_NO_USER
- eng "View '%-.64s'.'%-.64s' has no definer information (old table format). Current user is used as definer. Please recreate the view!"
- ger "View '%-.64s'.'%-.64s' hat keine Definierer-Information (altes Tabellenformat). Der aktuelle Benutzer wird als Definierer verwendet. Bitte erstellen Sie den View neu"
+ 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 '%-.64s'@'%-.64s' definer"
- ger "Sie brauchen die SUPER-Berechtigung, um einen View mit dem Definierer '%-.64s'@'%-.64s' zu erzeugen"
+ eng "You need the SUPER privilege for creation view with '%-.192s'@'%-.192s' definer"
+ ger "Sie brauchen die SUPER-Berechtigung, um einen View mit dem Definierer '%-.192s'@'%-.192s' zu erzeugen"
ER_NO_SUCH_USER
- eng "There is no '%-.64s'@'%-.64s' registered"
- ger "'%-.64s'@'%-.64s' ist nicht registriert"
+ eng "The user specified as a definer ('%-.64s'@'%-.64s') does not exist"
ER_FORBID_SCHEMA_CHANGE
- eng "Changing schema from '%-.64s' to '%-.64s' is not allowed."
- ger "Wechsel des Schemas von '%-.64s' auf '%-.64s' ist nicht erlaubt"
+ 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)"
+ 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)"
+ 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"
+ 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 '%-.64s'.'%-.64s'. 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 '%-.64s'.'%-.64s'. Der Trigger wird mit der Autorisierung des Aufrufers aktiviert, der möglicherweise keine zureichenden Berechtigungen hat. Bitte legen Sie den Trigger neu an."
+ 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 "'%-.64s' has an old format, you should re-create the '%s' object(s)"
- ger "'%-.64s' hat altes Format, Sie sollten die '%s'-Objekt(e) neu erzeugen"
+ 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 %.64s"
- ger "Rekursionsgrenze %d (durch Variable max_sp_recursion_depth gegeben) wurde für Routine %.64s überschritten"
+ 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 %s. The table mysql.proc is missing, corrupt, or contains bad data (internal code %d)"
+ 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 '%-.64s'"
+ eng "Incorrect routine name '%-.192s'"
+ ger "Ungültiger Routinenname '%-.192s'"
ER_TABLE_NEEDS_UPGRADE
- eng "Table upgrade required. Please do \"REPAIR TABLE `%-.32s`\" to fix it!"
+ eng "Table upgrade required. Please do \"REPAIR TABLE `%-.32s`\" 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"
+ 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 "`%-.64s`.`%-.64s` contains view recursion"
+ eng "`%-.192s`.`%-.192s` contains view recursion"
+ ger "`%-.192s`.`%-.192s` enthält View-Rekursion"
ER_NON_GROUPING_FIELD_USED 42000
- eng "non-grouping field '%-.64s' is used in %-.64s clause"
+ 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"
+ 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"
+ eng "user name"
+ ger "Benutzername"
ER_HOSTNAME
- eng "host name"
+ eng "host name"
+ ger "Hostname"
ER_WRONG_STRING_LENGTH
- eng "String '%-.70s' is too long for %s (should be no longer than %d)"
+ 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"
+ 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
@@ -5641,11 +5660,497 @@ 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_CONST_EXPR_IN_PARTITION_FUNC_ERROR
+ eng "Constant/Random expression in (sub)partitioning function is 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 condition is not yet supported in conjunction with partitioning"
+ ger "Fremdschlüssel-Beschränkungen sind im Zusammenhang mit Partitionierung nicht zulässig"
+ swe "Foreign key villkor ä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 "REORGANISE PARTITION can only be used to reorganise partitions not to change their numbers"
+ ger "REORGANIZE PARTITION kann nur zur Reorganisation von Partitionen verwendet werden, nicht, um ihre Nummern zu ändern"
+ swe "REORGANISE PARTITION kan bara användas för att omorganisera partitioner, inte för att ändra deras antal"
+ER_REORG_NO_PARAM_ERROR
+ eng "REORGANISE 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 "REORGANISE 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 reorganise 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 reorganising 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 is not 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"
diff --git a/sql/slave.cc b/sql/slave.cc
index cc82710dec7..22c61b3ec6c 100644
--- a/sql/slave.cc
+++ b/sql/slave.cc
@@ -13,14 +13,26 @@
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"
-#ifdef HAVE_REPLICATION
+/**
+ @addtogroup Replication
+ @{
+
+ @file
+
+ @brief Code to run the io thread and the sql thread on the
+ replication slave.
+*/
+
+#include "mysql_priv.h"
#include <mysql.h>
#include <myisam.h>
#include "slave.h"
+#include "rpl_mi.h"
+#include "rpl_rli.h"
#include "sql_repl.h"
+#include "rpl_filter.h"
#include "repl_failsafe.h"
#include <thr_alarm.h>
#include <my_dir.h>
@@ -28,20 +40,21 @@
#include <errmsg.h>
#include <mysys_err.h>
+#ifdef HAVE_REPLICATION
+
+#include "rpl_tblmap.h"
+
+#define FLAGSTR(V,F) ((V)&(F)?#F" ":"")
+
#define MAX_SLAVE_RETRY_PAUSE 5
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*);
-volatile bool slave_sql_running = 0, slave_io_running = 0;
char* slave_load_tmpdir = 0;
-MASTER_INFO *active_mi;
-HASH replicate_do_table, replicate_ignore_table;
-DYNAMIC_ARRAY replicate_wild_do_table, replicate_wild_ignore_table;
-bool do_table_inited = 0, ignore_table_inited = 0;
-bool wild_do_table_inited = 0, wild_ignore_table_inited = 0;
-bool table_rules_on= 0;
+Master_info *active_mi= 0;
my_bool replicate_same_server_id;
ulonglong relay_log_space_limit = 0;
@@ -49,58 +62,116 @@ ulonglong relay_log_space_limit = 0;
When slave thread exits, we need to remember the temporary tables so we
can re-use them on slave start.
- TODO: move the vars below under MASTER_INFO
+ TODO: move the vars below under Master_info
*/
int disconnect_slave_event_count = 0, abort_slave_event_count = 0;
int events_till_abort = -1;
-#ifndef DBUG_OFF
-static int events_till_disconnect = -1;
-#endif
+
+enum enum_slave_reconnect_actions
+{
+ SLAVE_RECON_ACT_REG= 0,
+ SLAVE_RECON_ACT_DUMP= 1,
+ SLAVE_RECON_ACT_EVENT= 2,
+ SLAVE_RECON_ACT_MAX
+};
+
+enum enum_slave_reconnect_messages
+{
+ SLAVE_RECON_MSG_WAIT= 0,
+ SLAVE_RECON_MSG_KILLED_WAITING= 1,
+ SLAVE_RECON_MSG_AFTER= 2,
+ SLAVE_RECON_MSG_FAILED= 3,
+ SLAVE_RECON_MSG_COMMAND= 4,
+ SLAVE_RECON_MSG_KILLED_AFTER= 5,
+ SLAVE_RECON_MSG_MAX
+};
+
+static const char *reconnect_messages[SLAVE_RECON_ACT_MAX][SLAVE_RECON_MSG_MAX]=
+{
+ {
+ "Waiting to reconnect after a failed registration on master",
+ "Slave I/O thread killed while waitnig to reconnect after a failed \
+registration on master",
+ "Reconnecting after a failed registration on master",
+ "failed registering on master, reconnecting to try again, \
+log '%s' at postion %s",
+ "COM_REGISTER_SLAVE",
+ "Slave I/O thread killed during or after reconnect"
+ },
+ {
+ "Waiting to reconnect after a failed binlog dump request",
+ "Slave I/O thread killed while retrying master dump",
+ "Reconnecting after a failed binlog dump request",
+ "failed dump request, reconnecting to try again, log '%s' at postion %s",
+ "COM_BINLOG_DUMP",
+ "Slave I/O thread killed during or after reconnect"
+ },
+ {
+ "Waiting to reconnect after a failed master event read",
+ "Slave I/O thread killed while waiting to reconnect after a failed read",
+ "Reconnecting after a failed master event read",
+ "Slave I/O thread: Failed reading log event, reconnecting to retry, \
+log '%s' at postion %s",
+ "",
+ "Slave I/O thread killed during or after a reconnect done to recover from \
+failed read"
+ }
+};
+
typedef enum { SLAVE_THD_IO, SLAVE_THD_SQL} SLAVE_THD_TYPE;
-static int process_io_rotate(MASTER_INFO* mi, Rotate_log_event* rev);
-static int process_io_create_file(MASTER_INFO* mi, Create_file_log_event* cev);
-static bool wait_for_relay_log_space(RELAY_LOG_INFO* rli);
-static inline bool io_slave_killed(THD* thd,MASTER_INFO* mi);
-static inline bool sql_slave_killed(THD* thd,RELAY_LOG_INFO* rli);
-static int count_relay_log_space(RELAY_LOG_INFO* rli);
+static int process_io_rotate(Master_info* mi, Rotate_log_event* rev);
+static int process_io_create_file(Master_info* mi, Create_file_log_event* cev);
+static bool wait_for_relay_log_space(Relay_log_info* rli);
+static inline bool io_slave_killed(THD* thd,Master_info* mi);
+static inline bool sql_slave_killed(THD* thd,Relay_log_info* rli);
static int init_slave_thread(THD* thd, SLAVE_THD_TYPE thd_type);
-static int safe_connect(THD* thd, MYSQL* mysql, MASTER_INFO* mi);
-static int safe_reconnect(THD* thd, MYSQL* mysql, MASTER_INFO* mi,
- bool suppress_warnings);
-static int connect_to_master(THD* thd, MYSQL* mysql, MASTER_INFO* mi,
- bool reconnect, bool suppress_warnings);
+static int safe_connect(THD* thd, MYSQL* mysql, Master_info* mi);
+static int safe_reconnect(THD* thd, MYSQL* mysql, Master_info* mi,
+ bool suppress_warnings);
+static int connect_to_master(THD* thd, MYSQL* mysql, Master_info* mi,
+ bool reconnect, bool suppress_warnings);
static int safe_sleep(THD* thd, int sec, CHECK_KILLED_FUNC thread_killed,
- void* thread_killed_arg);
+ 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);
+ 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,
+ volatile uint *slave_running,
+ bool skip_lock);
+static bool check_io_slave_killed(THD *thd, Master_info *mi, const char *info);
/*
Find out which replications threads are running
SYNOPSIS
init_thread_mask()
- mask Return value here
- mi master_info for slave
- inverse If set, returns which threads are not running
+ mask Return value here
+ mi master_info for slave
+ inverse If set, returns which threads are not running
IMPLEMENTATION
Get a bit mask for which threads are running so that we can later restart
these threads.
RETURN
- mask If inverse == 0, running threads
- If inverse == 1, stopped threads
+ mask If inverse == 0, running threads
+ If inverse == 1, stopped threads
*/
-void init_thread_mask(int* mask,MASTER_INFO* mi,bool inverse)
+void init_thread_mask(int* mask,Master_info* mi,bool inverse)
{
bool set_io = mi->slave_running, set_sql = mi->rli.slave_running;
register int tmp_mask=0;
+ DBUG_ENTER("init_thread_mask");
+
if (set_io)
tmp_mask |= SLAVE_IO;
if (set_sql)
@@ -108,6 +179,7 @@ void init_thread_mask(int* mask,MASTER_INFO* mi,bool inverse)
if (inverse)
tmp_mask^= (SLAVE_IO | SLAVE_SQL);
*mask = tmp_mask;
+ DBUG_VOID_RETURN;
}
@@ -115,11 +187,14 @@ void init_thread_mask(int* mask,MASTER_INFO* mi,bool inverse)
lock_slave_threads()
*/
-void lock_slave_threads(MASTER_INFO* mi)
+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);
+ DBUG_VOID_RETURN;
}
@@ -127,11 +202,14 @@ void lock_slave_threads(MASTER_INFO* mi)
unlock_slave_threads()
*/
-void unlock_slave_threads(MASTER_INFO* mi)
+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);
+ DBUG_VOID_RETURN;
}
@@ -151,7 +229,7 @@ int init_slave()
TODO: re-write this to interate through the list of files
for multi-master
*/
- active_mi= new MASTER_INFO;
+ active_mi= new Master_info;
/*
If master_host is not specified, try to read it from the master_info file.
@@ -165,7 +243,7 @@ int init_slave()
}
if (init_master_info(active_mi,master_info_file,relay_log_info_file,
- !master_host, (SLAVE_IO | SLAVE_SQL)))
+ !master_host, (SLAVE_IO | SLAVE_SQL)))
{
sql_print_error("Failed to initialize the master info structure");
goto err;
@@ -179,11 +257,11 @@ int init_slave()
if (master_host && !opt_skip_slave_start)
{
if (start_slave_threads(1 /* need mutex */,
- 0 /* no wait for start*/,
- active_mi,
- master_info_file,
- relay_log_info_file,
- SLAVE_IO | SLAVE_SQL))
+ 0 /* no wait for start*/,
+ active_mi,
+ master_info_file,
+ relay_log_info_file,
+ SLAVE_IO | SLAVE_SQL))
{
sql_print_error("Failed to create slave threads");
goto err;
@@ -198,243 +276,70 @@ err:
}
-static void free_table_ent(TABLE_RULE_ENT* e)
-{
- my_free((gptr) e, MYF(0));
-}
-
-
-static byte* get_table_key(TABLE_RULE_ENT* e, uint* len,
- my_bool not_used __attribute__((unused)))
-{
- *len = e->key_len;
- return (byte*)e->db;
-}
-
-
-/*
- Open the given relay log
-
- SYNOPSIS
- init_relay_log_pos()
- rli Relay information (will be initialized)
- log Name of relay log file to read from. NULL = First log
- pos Position in relay log file
- need_data_lock Set to 1 if this functions should do mutex locks
- errmsg Store pointer to error message here
- look_for_description_event
- 1 if we should look for such an event. We only need
- this when the SQL thread starts and opens an existing
- relay log and has to execute it (possibly from an
- offset >4); then we need to read the first event of
- the relay log to be able to parse the events we have
- to execute.
-
- DESCRIPTION
- - Close old open relay log files.
- - If we are using the same relay log as the running IO-thread, then set
- rli->cur_log to point to the same IO_CACHE entry.
- - If not, open the 'log' binary file.
-
- TODO
- - check proper initialization of group_master_log_name/group_master_log_pos
-
- RETURN VALUES
- 0 ok
- 1 error. errmsg is set to point to the error message
+/**
+ Convert slave skip errors bitmap into a printable string.
*/
-int init_relay_log_pos(RELAY_LOG_INFO* rli,const char* log,
- ulonglong pos, bool need_data_lock,
- const char** errmsg,
- bool look_for_description_event)
+static void print_slave_skip_errors(void)
{
- DBUG_ENTER("init_relay_log_pos");
- DBUG_PRINT("info", ("pos: %lu", (long) pos));
-
- *errmsg=0;
- pthread_mutex_t *log_lock=rli->relay_log.get_log_lock();
-
- if (need_data_lock)
- pthread_mutex_lock(&rli->data_lock);
-
- /*
- Slave threads are not the only users of init_relay_log_pos(). CHANGE MASTER
- is, too, and init_slave() too; these 2 functions allocate a description
- event in init_relay_log_pos, which is not freed by the terminating SQL slave
- thread as that thread is not started by these functions. So we have to free
- the description_event here, in case, so that there is no memory leak in
- running, say, CHANGE MASTER.
- */
- delete rli->relay_log.description_event_for_exec;
- /*
- By default the relay log is in binlog format 3 (4.0).
- Even if format is 4, this will work enough to read the first event
- (Format_desc) (remember that format 4 is just lenghtened compared to format
- 3; format 3 is a prefix of format 4).
- */
- rli->relay_log.description_event_for_exec= new
- Format_description_log_event(3);
-
- pthread_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));
- rli->cur_log_fd = -1;
- }
-
- rli->group_relay_log_pos = rli->event_relay_log_pos = pos;
-
/*
- Test to see if the previous run was with the skip of purging
- If yes, we do not purge when we restart
+ To be safe, we want 10 characters of room in the buffer for a number
+ plus terminators. Also, we need some space for constant strings.
+ 10 characters must be sufficient for a number plus {',' | '...'}
+ plus a NUL terminator. That is a max 6 digit number.
*/
- if (rli->relay_log.find_log_pos(&rli->linfo, NullS, 1))
- {
- *errmsg="Could not find first log during relay log initialization";
- goto err;
- }
+ const size_t MIN_ROOM= 10;
+ DBUG_ENTER("print_slave_skip_errors");
+ DBUG_ASSERT(sizeof(slave_skip_error_names) > MIN_ROOM);
+ DBUG_ASSERT(MAX_SLAVE_ERROR <= 999999); // 6 digits
- if (log && rli->relay_log.find_log_pos(&rli->linfo, log, 1))
+ if (!use_slave_mask || bitmap_is_clear_all(&slave_error_mask))
{
- *errmsg="Could not find target log during relay log initialization";
- goto err;
+ /* purecov: begin tested */
+ memcpy(slave_skip_error_names, STRING_WITH_LEN("OFF"));
+ /* purecov: end */
}
- 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);
- if (rli->relay_log.is_active(rli->linfo.log_file_name))
+ else if (bitmap_is_set_all(&slave_error_mask))
{
- /*
- The IO thread is using this log file.
- In this case, we will use the same IO_CACHE pointer to
- read data as the IO thread is using to write data.
- */
- my_b_seek((rli->cur_log=rli->relay_log.get_log_file()), (off_t)0);
- if (check_binlog_magic(rli->cur_log,errmsg))
- goto err;
- rli->cur_log_old_open_count=rli->relay_log.get_open_count();
+ /* purecov: begin tested */
+ memcpy(slave_skip_error_names, STRING_WITH_LEN("ALL"));
+ /* purecov: end */
}
else
{
- /*
- Open the relay log and set rli->cur_log to point at this one
- */
- if ((rli->cur_log_fd=open_binlog(&rli->cache_buf,
- rli->linfo.log_file_name,errmsg)) < 0)
- goto err;
- rli->cur_log = &rli->cache_buf;
- }
- /*
- In all cases, check_binlog_magic() has been called so we're at offset 4 for
- sure.
- */
- if (pos > BIN_LOG_HEADER_SIZE) /* If pos<=4, we stay at 4 */
- {
- Log_event* ev;
- while (look_for_description_event)
- {
- /*
- Read the possible Format_description_log_event; if position
- was 4, no need, it will be read naturally.
- */
- DBUG_PRINT("info",("looking for a Format_description_log_event"));
-
- if (my_b_tell(rli->cur_log) >= pos)
- break;
+ char *buff= slave_skip_error_names;
+ char *bend= buff + sizeof(slave_skip_error_names);
+ int errnum;
- /*
- Because of we have rli->data_lock and log_lock, we can safely read an
- event
- */
- if (!(ev=Log_event::read_log_event(rli->cur_log,0,
- rli->relay_log.description_event_for_exec)))
- {
- DBUG_PRINT("info",("could not read event, rli->cur_log->error=%d",
- rli->cur_log->error));
- if (rli->cur_log->error) /* not EOF */
- {
- *errmsg= "I/O error reading event at position 4";
- goto err;
- }
- break;
- }
- else if (ev->get_type_code() == FORMAT_DESCRIPTION_EVENT)
- {
- DBUG_PRINT("info",("found Format_description_log_event"));
- delete rli->relay_log.description_event_for_exec;
- rli->relay_log.description_event_for_exec= (Format_description_log_event*) ev;
- /*
- As ev was returned by read_log_event, it has passed is_valid(), so
- my_malloc() in ctor worked, no need to check again.
- */
- /*
- Ok, we found a Format_description event. But it is not sure that this
- describes the whole relay log; indeed, one can have this sequence
- (starting from position 4):
- Format_desc (of slave)
- Rotate (of master)
- Format_desc (of master)
- So the Format_desc which really describes the rest of the relay log
- is the 3rd event (it can't be further than that, because we rotate
- the relay log when we queue a Rotate event from the master).
- But what describes the Rotate is the first Format_desc.
- So what we do is:
- go on searching for Format_description events, until you exceed the
- position (argument 'pos') or until you find another event than Rotate
- or Format_desc.
- */
- }
- else
+ for (errnum= 1; errnum < MAX_SLAVE_ERROR; errnum++)
+ {
+ if (bitmap_is_set(&slave_error_mask, errnum))
{
- DBUG_PRINT("info",("found event of another type=%d",
- ev->get_type_code()));
- look_for_description_event= (ev->get_type_code() == ROTATE_EVENT);
- delete ev;
+ if (buff + MIN_ROOM >= bend)
+ break; /* purecov: tested */
+ buff= int10_to_str(errnum, buff, 10);
+ *buff++= ',';
}
}
- my_b_seek(rli->cur_log,(off_t)pos);
-#ifndef DBUG_OFF
- {
- char llbuf1[22], llbuf2[22];
- DBUG_PRINT("info", ("my_b_tell(rli->cur_log)=%s rli->event_relay_log_pos=%s",
- llstr(my_b_tell(rli->cur_log),llbuf1),
- llstr(rli->event_relay_log_pos,llbuf2)));
- }
-#endif
-
+ if (buff != slave_skip_error_names)
+ buff--; // Remove last ','
+ if (errnum < MAX_SLAVE_ERROR)
+ {
+ /* Couldn't show all errors */
+ buff= strmov(buff, "..."); /* purecov: tested */
+ }
+ *buff=0;
}
-
-err:
- /*
- If we don't purge, we can't honour relay_log_space_limit ;
- silently discard it
- */
- if (!relay_log_purge)
- rli->log_space_limit= 0;
- pthread_cond_broadcast(&rli->data_cond);
-
- pthread_mutex_unlock(log_lock);
-
- if (need_data_lock)
- pthread_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";
-
- DBUG_RETURN ((*errmsg) ? 1 : 0);
+ DBUG_PRINT("init", ("error_names: '%s'", slave_skip_error_names));
+ DBUG_VOID_RETURN;
}
-
/*
Init function to set up array for errors that should be skipped for slave
SYNOPSIS
init_slave_skip_errors()
- arg List of errors numbers to skip, separated with ','
+ arg List of errors numbers to skip, separated with ','
NOTES
Called from get_options() in mysqld.cc on start-up
@@ -443,6 +348,8 @@ err:
void init_slave_skip_errors(const char* arg)
{
const char *p;
+ DBUG_ENTER("init_slave_skip_errors");
+
if (bitmap_init(&slave_error_mask,0,MAX_SLAVE_ERROR,0))
{
fprintf(stderr, "Badly out of memory, please check your system status\n");
@@ -454,7 +361,7 @@ void init_slave_skip_errors(const char* arg)
if (!my_strnncoll(system_charset_info,(uchar*)arg,4,(const uchar*)"all",4))
{
bitmap_set_all(&slave_error_mask);
- return;
+ DBUG_VOID_RETURN;
}
for (p= arg ; *p; )
{
@@ -466,237 +373,107 @@ void init_slave_skip_errors(const char* arg)
while (!my_isdigit(system_charset_info,*p) && *p)
p++;
}
+ /* Convert slave skip errors bitmap into a printable string. */
+ print_slave_skip_errors();
+ DBUG_VOID_RETURN;
}
-void st_relay_log_info::inc_group_relay_log_pos(ulonglong log_pos,
- bool skip_lock)
-{
- if (!skip_lock)
- pthread_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);
-
- notify_group_relay_log_name_update();
-
- /*
- If the slave does not support transactions and replicates a transaction,
- users should not trust group_master_log_pos (which they can display with
- SHOW SLAVE STATUS or read from relay-log.info), because to compute
- group_master_log_pos the slave relies on log_pos stored in the master's
- binlog, but if we are in a master's transaction these positions are always
- the BEGIN's one (excepted for the COMMIT), so group_master_log_pos does
- not advance as it should on the non-transactional slave (it advances by
- big leaps, whereas it should advance by small leaps).
- */
- /*
- In 4.x we used the event's len to compute the positions here. This is
- wrong if the event was 3.23/4.0 and has been converted to 5.0, because
- then the event's len is not what is was in the master's binlog, so this
- will make a wrong group_master_log_pos (yes it's a bug in 3.23->4.0
- replication: Exec_master_log_pos is wrong). Only way to solve this is to
- have the original offset of the end of the event the relay log. This is
- what we do in 5.0: log_pos has become "end_log_pos" (because the real use
- of log_pos in 4.0 was to compute the end_log_pos; so better to store
- end_log_pos instead of begin_log_pos.
- If we had not done this fix here, the problem would also have appeared
- when the slave and master are 5.0 but with different event length (for
- example the slave is more recent than the master and features the event
- UID). It would give false MASTER_POS_WAIT, false Exec_master_log_pos in
- SHOW SLAVE STATUS, and so the user would do some CHANGE MASTER using this
- value which would lead to badly broken replication.
- Even the relay_log_pos will be corrupted in this case, because the len is
- the relay log is not "val".
- With the end_log_pos solution, we avoid computations involving lengthes.
- */
- DBUG_PRINT("info", ("log_pos: %lu group_master_log_pos: %lu",
- (long) log_pos, (long) group_master_log_pos));
- if (log_pos) // 3.23 binlogs don't have log_posx
- {
- group_master_log_pos= log_pos;
- }
- pthread_cond_broadcast(&data_cond);
- if (!skip_lock)
- pthread_mutex_unlock(&data_lock);
-}
-
-
-void st_relay_log_info::close_temporary_tables()
-{
- TABLE *table,*next;
-
- for (table=save_temporary_tables ; table ; table=next)
- {
- next=table->next;
- /*
- Don't ask for disk deletion. For now, anyway they will be deleted when
- slave restarts, but it is a better intention to not delete them.
- */
- close_temporary(table, 0);
- }
- save_temporary_tables= 0;
- slave_open_temp_tables= 0;
-}
-
-/*
- purge_relay_logs()
-
- NOTES
- Assumes to have a run lock on rli and that no slave thread are running.
-*/
-
-int purge_relay_logs(RELAY_LOG_INFO* rli, THD *thd, bool just_reset,
- const char** errmsg)
+int terminate_slave_threads(Master_info* mi,int thread_mask,bool skip_lock)
{
- int error=0;
- DBUG_ENTER("purge_relay_logs");
-
- /*
- Even if rli->inited==0, we still try to empty rli->master_log_* variables.
- Indeed, rli->inited==0 does not imply that they already are empty.
- It could be that slave's info initialization partly succeeded :
- for example if relay-log.info existed but *relay-bin*.*
- have been manually removed, init_relay_log_info reads the old
- relay-log.info and fills rli->master_log_*, then init_relay_log_info
- checks for the existence of the relay log, this fails and
- init_relay_log_info leaves rli->inited to 0.
- In that pathological case, rli->master_log_pos* will be properly reinited
- at the next START SLAVE (as RESET SLAVE or CHANGE
- MASTER, the callers of purge_relay_logs, will delete bogus *.info files
- or replace them with correct files), however if the user does SHOW SLAVE
- STATUS before START SLAVE, he will see old, confusing rli->master_log_*.
- In other words, we reinit rli->master_log_* for SHOW SLAVE STATUS
- to display fine in any case.
- */
-
- rli->group_master_log_name[0]= 0;
- rli->group_master_log_pos= 0;
-
- if (!rli->inited)
- {
- DBUG_PRINT("info", ("rli->inited == 0"));
- DBUG_RETURN(0);
- }
-
- DBUG_ASSERT(rli->slave_running == 0);
- DBUG_ASSERT(rli->mi->slave_running == 0);
-
- rli->slave_skip_counter=0;
- pthread_mutex_lock(&rli->data_lock);
-
- /*
- we close the relay log fd possibly left open by the slave SQL thread,
- to be able to delete it; the relay log fd possibly left open by the slave
- I/O thread will be closed naturally in reset_logs() by the
- close(LOG_CLOSE_TO_BE_OPENED) call
- */
- if (rli->cur_log_fd >= 0)
- {
- end_io_cache(&rli->cache_buf);
- my_close(rli->cur_log_fd, MYF(MY_WME));
- rli->cur_log_fd= -1;
- }
-
- if (rli->relay_log.reset_logs(thd))
- {
- *errmsg = "Failed during log reset";
- error=1;
- 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);
- rli->group_relay_log_pos= rli->event_relay_log_pos= BIN_LOG_HEADER_SIZE;
- if (count_relay_log_space(rli))
- {
- *errmsg= "Error counting relay log space";
- goto err;
- }
- if (!just_reset)
- error= init_relay_log_pos(rli, rli->group_relay_log_name,
- rli->group_relay_log_pos,
- 0 /* do not need data lock */, errmsg, 0);
-
-err:
-#ifndef DBUG_OFF
- char buf[22];
-#endif
- DBUG_PRINT("info",("log_space_total: %s",llstr(rli->log_space_total,buf)));
- pthread_mutex_unlock(&rli->data_lock);
- DBUG_RETURN(error);
-}
-
+ DBUG_ENTER("terminate_slave_threads");
-int terminate_slave_threads(MASTER_INFO* mi,int thread_mask,bool skip_lock)
-{
if (!mi->inited)
- return 0; /* successfully do nothing */
+ 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;
- pthread_mutex_t *sql_cond_lock,*io_cond_lock;
- DBUG_ENTER("terminate_slave_threads");
- sql_cond_lock=sql_lock;
- io_cond_lock=io_lock;
-
- if (skip_lock)
- {
- sql_lock = io_lock = 0;
- }
- if ((thread_mask & (SLAVE_IO|SLAVE_FORCE_ALL)) && mi->slave_running)
+ if ((thread_mask & (SLAVE_IO|SLAVE_FORCE_ALL)))
{
DBUG_PRINT("info",("Terminating IO thread"));
mi->abort_slave=1;
if ((error=terminate_slave_thread(mi->io_thd,io_lock,
- io_cond_lock,
- &mi->stop_cond,
- &mi->slave_running)) &&
- !force_all)
+ &mi->stop_cond,
+ &mi->slave_running,
+ skip_lock)) &&
+ !force_all)
DBUG_RETURN(error);
}
- if ((thread_mask & (SLAVE_SQL|SLAVE_FORCE_ALL)) && mi->rli.slave_running)
+ if ((thread_mask & (SLAVE_SQL|SLAVE_FORCE_ALL)))
{
DBUG_PRINT("info",("Terminating SQL thread"));
- DBUG_ASSERT(mi->rli.sql_thd != 0) ;
mi->rli.abort_slave=1;
if ((error=terminate_slave_thread(mi->rli.sql_thd,sql_lock,
- sql_cond_lock,
- &mi->rli.stop_cond,
- &mi->rli.slave_running)) &&
- !force_all)
+ &mi->rli.stop_cond,
+ &mi->rli.slave_running,
+ skip_lock)) &&
+ !force_all)
DBUG_RETURN(error);
}
DBUG_RETURN(0);
}
-int terminate_slave_thread(THD* thd, pthread_mutex_t* term_lock,
- pthread_mutex_t *cond_lock,
- pthread_cond_t* term_cond,
- volatile uint *slave_running)
+/**
+ Wait for a slave thread to terminate.
+
+ This function is called after requesting the thread to terminate
+ (by setting @c abort_slave member of @c Relay_log_info or @c
+ Master_info structure to 1). Termination of the thread is
+ controlled with the the predicate <code>*slave_running</code>.
+
+ Function will acquire @c term_lock before waiting on the condition
+ unless @c skip_lock is true in which case the mutex should be owned
+ by the caller of this function and will remain acquired after
+ return from the function.
+
+ @param term_lock
+ Associated lock to use when waiting for @c term_cond
+
+ @param term_cond
+ Condition that is signalled when the thread has terminated
+
+ @param slave_running
+ Pointer to predicate to check for slave thread termination
+
+ @param skip_lock
+ If @c true the lock will not be acquired before waiting on
+ the condition. In this case, it is assumed that the calling
+ function acquires the lock before calling this function.
+
+ @retval 0 All OK
+ */
+static int
+terminate_slave_thread(THD *thd,
+ pthread_mutex_t* term_lock,
+ pthread_cond_t* term_cond,
+ volatile uint *slave_running,
+ bool skip_lock)
{
+ int error;
+
DBUG_ENTER("terminate_slave_thread");
- if (term_lock)
- {
+
+ if (!skip_lock)
pthread_mutex_lock(term_lock);
- if (!*slave_running)
- {
+
+ safe_mutex_assert_owner(term_lock);
+
+ if (!*slave_running)
+ {
+ if (!skip_lock)
pthread_mutex_unlock(term_lock);
- DBUG_RETURN(ER_SLAVE_NOT_RUNNING);
- }
+ DBUG_RETURN(ER_SLAVE_NOT_RUNNING);
}
DBUG_ASSERT(thd != 0);
THD_CHECK_SENTRY(thd);
+
/*
Is is critical to test if the slave is running. Otherwise, we might
be referening freed memory trying to kick it
*/
- while (*slave_running) // Should always be true
+ while (*slave_running) // Should always be true
{
DBUG_PRINT("loop", ("killing slave thread"));
@@ -719,27 +496,32 @@ int terminate_slave_thread(THD* thd, pthread_mutex_t* term_lock,
*/
struct timespec abstime;
set_timespec(abstime,2);
- pthread_cond_timedwait(term_cond, cond_lock, &abstime);
+ error= pthread_cond_timedwait(term_cond, term_lock, &abstime);
+ DBUG_ASSERT(error == ETIMEDOUT || error == 0);
}
- if (term_lock)
+
+ DBUG_ASSERT(*slave_running == 0);
+
+ if (!skip_lock)
pthread_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,
- volatile uint *slave_running,
- volatile ulong *slave_run_id,
- MASTER_INFO* mi,
+ 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)
{
pthread_t th;
ulong start_id;
- DBUG_ASSERT(mi->inited);
DBUG_ENTER("start_slave_thread");
+ DBUG_ASSERT(mi->inited);
+
if (start_lock)
pthread_mutex_lock(start_lock);
if (!server_id)
@@ -751,7 +533,7 @@ int start_slave_thread(pthread_handler h_func, pthread_mutex_t *start_lock,
sql_print_error("Server id not set, will not start slave");
DBUG_RETURN(ER_BAD_SLAVE);
}
-
+
if (*slave_running)
{
if (start_cond)
@@ -777,12 +559,12 @@ int start_slave_thread(pthread_handler h_func, pthread_mutex_t *start_lock,
{
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");
+ "Waiting for slave thread to start");
pthread_cond_wait(start_cond,cond_lock);
thd->exit_cond(old_msg);
pthread_mutex_lock(cond_lock); // re-acquire it as exit_cond() released
if (thd->killed)
- DBUG_RETURN(thd->killed_errno());
+ DBUG_RETURN(thd->killed_errno());
}
}
if (start_lock)
@@ -801,14 +583,14 @@ int start_slave_thread(pthread_handler h_func, pthread_mutex_t *start_lock,
*/
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)
+ 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;
int error=0;
DBUG_ENTER("start_slave_threads");
-
+
if (need_slave_mutex)
{
lock_io = &mi->run_lock;
@@ -824,15 +606,15 @@ 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
+ cond_io,
+ &mi->slave_running, &mi->slave_run_id,
+ mi, 1); //high priority, to read the most possible
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);
+ cond_sql,
+ &mi->rli.slave_running, &mi->rli.slave_run_id,
+ mi, 0);
if (error)
terminate_slave_threads(mi, thread_mask & SLAVE_IO, 0);
}
@@ -840,242 +622,13 @@ int start_slave_threads(bool need_slave_mutex, bool wait_for_start,
}
-void init_table_rule_hash(HASH* h, bool* h_inited)
-{
- hash_init(h, system_charset_info,TABLE_RULE_HASH_SIZE,0,0,
- (hash_get_key) get_table_key,
- (hash_free_key) free_table_ent, 0);
- *h_inited = 1;
-}
-
-
-void init_table_rule_array(DYNAMIC_ARRAY* a, bool* a_inited)
-{
- my_init_dynamic_array(a, sizeof(TABLE_RULE_ENT*), TABLE_RULE_ARR_SIZE,
- TABLE_RULE_ARR_SIZE);
- *a_inited = 1;
-}
-
-
-static TABLE_RULE_ENT* find_wild(DYNAMIC_ARRAY *a, const char* key, int len)
-{
- uint i;
- const char* key_end = key + len;
-
- for (i = 0; i < a->elements; i++)
- {
- TABLE_RULE_ENT* e ;
- get_dynamic(a, (gptr)&e, i);
- if (!my_wildcmp(system_charset_info, key, key_end,
- (const char*)e->db,
- (const char*)(e->db + e->key_len),
- '\\',wild_one,wild_many))
- return e;
- }
-
- return 0;
-}
-
-
-/*
- Checks whether tables match some (wild_)do_table and (wild_)ignore_table
- rules (for replication)
-
- SYNOPSIS
- tables_ok()
- thd thread (SQL slave thread normally). Mustn't be null.
- tables list of tables to check
-
- NOTES
- Note that changing the order of the tables in the list can lead to
- different results. Note also the order of precedence of the do/ignore
- rules (see code below). For that reason, users should not set conflicting
- rules because they may get unpredicted results (precedence order is
- explained in the manual).
-
- Thought which arose from a question of a big customer "I want to include
- all tables like "abc.%" except the "%.EFG"". This can't be done now. If we
- supported Perl regexps we could do it with this pattern: /^abc\.(?!EFG)/
- (I could not find an equivalent in the regex library MySQL uses).
-
- RETURN VALUES
- 0 should not be logged/replicated
- 1 should be logged/replicated
-*/
-
-bool tables_ok(THD* thd, TABLE_LIST* tables)
-{
- bool some_tables_updating= 0;
- DBUG_ENTER("tables_ok");
-
- /*
- In routine, can't reliably pick and choose substatements, so always
- replicate.
- We can't reliably know if one substatement should be executed or not:
- consider the case of this substatement: a SELECT on a non-replicated
- constant table; if we don't execute it maybe it was going to fill a
- variable which was going to be used by the next substatement to update
- a replicated table? If we execute it maybe the constant non-replicated
- table does not exist (and so we'll fail) while there was no need to
- execute this as this SELECT does not influence replicated tables in the
- rest of the routine? In other words: users are used to replicate-*-table
- specifying how to handle updates to tables, these options don't say
- anything about reads to tables; we can't guess.
- */
- if (thd->spcont)
- DBUG_RETURN(1);
-
- for (; tables; tables= tables->next_global)
- {
- char hash_key[2*NAME_LEN+2];
- char *end;
- uint len;
-
- if (!tables->updating)
- continue;
- some_tables_updating= 1;
- end= strmov(hash_key, tables->db ? tables->db : thd->db);
- *end++= '.';
- len= (uint) (strmov(end, tables->table_name) - hash_key);
- if (do_table_inited) // if there are any do's
- {
- if (hash_search(&replicate_do_table, (byte*) hash_key, len))
- DBUG_RETURN(1);
- }
- if (ignore_table_inited) // if there are any ignores
- {
- if (hash_search(&replicate_ignore_table, (byte*) hash_key, len))
- DBUG_RETURN(0);
- }
- if (wild_do_table_inited && find_wild(&replicate_wild_do_table,
- hash_key, len))
- DBUG_RETURN(1);
- if (wild_ignore_table_inited && find_wild(&replicate_wild_ignore_table,
- hash_key, len))
- DBUG_RETURN(0);
- }
-
- /*
- If no table was to be updated, ignore statement (no reason we play it on
- slave, slave is supposed to replicate _changes_ only).
- If no explicit rule found and there was a do list, do not replicate.
- If there was no do list, go ahead
- */
- DBUG_RETURN(some_tables_updating &&
- !do_table_inited && !wild_do_table_inited);
-}
-
-
-/*
- Checks whether a db matches wild_do_table and wild_ignore_table
- rules (for replication)
-
- SYNOPSIS
- db_ok_with_wild_table()
- db name of the db to check.
- Is tested with check_db_name() before calling this function.
-
- NOTES
- Here is the reason for this function.
- We advise users who want to exclude a database 'db1' safely to do it
- with replicate_wild_ignore_table='db1.%' instead of binlog_ignore_db or
- replicate_ignore_db because the two lasts only check for the selected db,
- which won't work in that case:
- USE db2;
- UPDATE db1.t SET ... #this will be replicated and should not
- whereas replicate_wild_ignore_table will work in all cases.
- With replicate_wild_ignore_table, we only check tables. When
- one does 'DROP DATABASE db1', tables are not involved and the
- statement will be replicated, while users could expect it would not (as it
- rougly means 'DROP db1.first_table, DROP db1.second_table...').
- In other words, we want to interpret 'db1.%' as "everything touching db1".
- That is why we want to match 'db1' against 'db1.%' wild table rules.
-
- RETURN VALUES
- 0 should not be logged/replicated
- 1 should be logged/replicated
- */
-
-int db_ok_with_wild_table(const char *db)
-{
- char hash_key[NAME_LEN+2];
- char *end;
- int len;
- end= strmov(hash_key, db);
- *end++= '.';
- len= (uint) (end - hash_key);
- if (wild_do_table_inited && find_wild(&replicate_wild_do_table,
- hash_key, len))
- return 1;
- if (wild_ignore_table_inited && find_wild(&replicate_wild_ignore_table,
- hash_key, len))
- return 0;
-
- /*
- If no explicit rule found and there was a do list, do not replicate.
- If there was no do list, go ahead
- */
- return !wild_do_table_inited;
-}
-
-
-int add_table_rule(HASH* h, const char* table_spec)
-{
- const char* dot = strchr(table_spec, '.');
- if (!dot) return 1;
- // len is always > 0 because we know the there exists a '.'
- uint len = (uint)strlen(table_spec);
- TABLE_RULE_ENT* e = (TABLE_RULE_ENT*)my_malloc(sizeof(TABLE_RULE_ENT)
- + len, MYF(MY_WME));
- if (!e) return 1;
- e->db = (char*)e + sizeof(TABLE_RULE_ENT);
- e->tbl_name = e->db + (dot - table_spec) + 1;
- e->key_len = len;
- memcpy(e->db, table_spec, len);
- (void)my_hash_insert(h, (byte*)e);
- return 0;
-}
-
-
-/*
- Add table expression with wildcards to dynamic array
-*/
-
-int add_wild_table_rule(DYNAMIC_ARRAY* a, const char* table_spec)
-{
- const char* dot = strchr(table_spec, '.');
- if (!dot) return 1;
- uint len = (uint)strlen(table_spec);
- TABLE_RULE_ENT* e = (TABLE_RULE_ENT*)my_malloc(sizeof(TABLE_RULE_ENT)
- + len, MYF(MY_WME));
- if (!e) return 1;
- e->db = (char*)e + sizeof(TABLE_RULE_ENT);
- e->tbl_name = e->db + (dot - table_spec) + 1;
- e->key_len = len;
- memcpy(e->db, table_spec, len);
- insert_dynamic(a, (gptr)&e);
- return 0;
-}
-
-
-static void free_string_array(DYNAMIC_ARRAY *a)
-{
- uint i;
- for (i = 0; i < a->elements; i++)
- {
- char* p;
- get_dynamic(a, (gptr) &p, i);
- my_free(p, MYF(MY_WME));
- }
- delete_dynamic(a);
-}
-
-
#ifdef NOT_USED_YET
-static int end_slave_on_walk(MASTER_INFO* mi, gptr /*unused*/)
+static int end_slave_on_walk(Master_info* mi, uchar* /*unused*/)
{
+ DBUG_ENTER("end_slave_on_walk");
+
end_master_info(mi);
- return 0;
+ DBUG_RETURN(0);
}
#endif
@@ -1089,6 +642,8 @@ static int end_slave_on_walk(MASTER_INFO* mi, gptr /*unused*/)
void end_slave()
{
+ DBUG_ENTER("end_slave");
+
/*
This is called when the server terminates, in close_connections().
It terminates slave threads. However, some CHANGE MASTER etc may still be
@@ -1106,72 +661,62 @@ void end_slave()
*/
terminate_slave_threads(active_mi,SLAVE_FORCE_ALL);
end_master_info(active_mi);
- if (do_table_inited)
- hash_free(&replicate_do_table);
- if (ignore_table_inited)
- hash_free(&replicate_ignore_table);
- if (wild_do_table_inited)
- free_string_array(&replicate_wild_do_table);
- if (wild_ignore_table_inited)
- free_string_array(&replicate_wild_ignore_table);
delete active_mi;
active_mi= 0;
}
pthread_mutex_unlock(&LOCK_active_mi);
+ DBUG_VOID_RETURN;
}
-static bool io_slave_killed(THD* thd, MASTER_INFO* mi)
+static bool io_slave_killed(THD* thd, Master_info* mi)
{
+ DBUG_ENTER("io_slave_killed");
+
DBUG_ASSERT(mi->io_thd == thd);
DBUG_ASSERT(mi->slave_running); // tracking buffer overrun
- return mi->abort_slave || abort_loop || thd->killed;
+ DBUG_RETURN(mi->abort_slave || abort_loop || thd->killed);
}
-static bool sql_slave_killed(THD* thd, RELAY_LOG_INFO* rli)
+static bool sql_slave_killed(THD* thd, Relay_log_info* rli)
{
+ DBUG_ENTER("sql_slave_killed");
+
DBUG_ASSERT(rli->sql_thd == thd);
DBUG_ASSERT(rli->slave_running == 1);// tracking buffer overrun
- return rli->abort_slave || abort_loop || thd->killed;
+ if (abort_loop || thd->killed || rli->abort_slave)
+ {
+ /*
+ 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)
+ {
+ 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);
+ }
+ }
+ DBUG_RETURN(0);
}
/*
- Writes an error message to rli->last_slave_error and rli->last_slave_errno
- (which will be displayed by SHOW SLAVE STATUS), and prints it to stderr.
-
- SYNOPSIS
- slave_print_error()
- rli
- err_code The error code
- msg The error message (usually related to the error code, but can
- contain more information).
- ... (this is printf-like format, with % symbols in msg)
-
- RETURN VALUES
- void
-*/
-
-void slave_print_error(RELAY_LOG_INFO* rli, int err_code, const char* msg, ...)
-{
- va_list args;
- va_start(args,msg);
- my_vsnprintf(rli->last_slave_error,
- sizeof(rli->last_slave_error), msg, args);
- rli->last_slave_errno = err_code;
- /* If the error string ends with '.', do not add a ',' it would be ugly */
- if (rli->last_slave_error[0] &&
- (*(strend(rli->last_slave_error)-1) == '.'))
- sql_print_error("Slave: %s Error_code: %d", rli->last_slave_error,
- err_code);
- else
- sql_print_error("Slave: %s, Error_code: %d", rli->last_slave_error,
- err_code);
-
-}
-
-/*
skip_load_data_infile()
NOTES
@@ -1180,19 +725,22 @@ void slave_print_error(RELAY_LOG_INFO* rli, int err_code, const char* msg, ...)
void skip_load_data_infile(NET *net)
{
+ DBUG_ENTER("skip_load_data_infile");
+
(void)net_request_file(net, "/dev/null");
- (void)my_net_read(net); // discard response
- (void)net_write_command(net, 0, "", 0, "", 0); // Send ok
+ (void)my_net_read(net); // discard response
+ (void)net_write_command(net, 0, (uchar*) "", 0, (uchar*) "", 0); // ok
+ DBUG_VOID_RETURN;
}
bool net_request_file(NET* net, const char* fname)
{
DBUG_ENTER("net_request_file");
- DBUG_RETURN(net_write_command(net, 251, fname, (uint) strlen(fname), "", 0));
+ DBUG_RETURN(net_write_command(net, 251, (uchar*) fname, strlen(fname),
+ (uchar*) "", 0));
}
-
/*
From other comments and tests in code, it looks like
sometimes Query_log_event and Load_log_event can have db == 0
@@ -1202,68 +750,17 @@ bool net_request_file(NET* net, const char* fname)
const char *print_slave_db_safe(const char* db)
{
- return (db ? db : "");
-}
-
-/*
- Checks whether a db matches some do_db and ignore_db rules
- (for logging or replication)
-
- SYNOPSIS
- db_ok()
- db name of the db to check
- do_list either binlog_do_db or replicate_do_db
- ignore_list either binlog_ignore_db or replicate_ignore_db
-
- RETURN VALUES
- 0 should not be logged/replicated
- 1 should be logged/replicated
-*/
-
-int db_ok(const char* db, I_List<i_string> &do_list,
- I_List<i_string> &ignore_list )
-{
- if (do_list.is_empty() && ignore_list.is_empty())
- return 1; // ok to replicate if the user puts no constraints
-
- /*
- If the user has specified restrictions on which databases to replicate
- and db was not selected, do not replicate.
- */
- if (!db)
- return 0;
-
- if (!do_list.is_empty()) // if the do's are not empty
- {
- I_List_iterator<i_string> it(do_list);
- i_string* tmp;
+ DBUG_ENTER("*print_slave_db_safe");
- while ((tmp=it++))
- {
- if (!strcmp(tmp->ptr, db))
- return 1; // match
- }
- return 0;
- }
- else // there are some elements in the don't, otherwise we cannot get here
- {
- I_List_iterator<i_string> it(ignore_list);
- i_string* tmp;
-
- while ((tmp=it++))
- {
- if (!strcmp(tmp->ptr, db))
- return 0; // match
- }
- return 1;
- }
+ DBUG_RETURN((db ? db : ""));
}
-
-static int init_strvar_from_file(char *var, int max_size, IO_CACHE *f,
- const char *default_val)
+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;
@@ -1272,38 +769,40 @@ static int init_strvar_from_file(char *var, int max_size, IO_CACHE *f,
else
{
/*
- If we truncated a line or stopped on last char, remove all chars
- up to and including newline.
+ 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));
}
- return 0;
+ DBUG_RETURN(0);
}
else if (default_val)
{
strmake(var, default_val, max_size-1);
- return 0;
+ DBUG_RETURN(0);
}
- return 1;
+ DBUG_RETURN(1);
}
-static int init_intvar_from_file(int* var, IO_CACHE* f, int default_val)
+int init_intvar_from_file(int* var, IO_CACHE* f, int default_val)
{
char buf[32];
-
- if (my_b_gets(f, buf, sizeof(buf)))
+ DBUG_ENTER("init_intvar_from_file");
+
+
+ if (my_b_gets(f, buf, sizeof(buf)))
{
*var = atoi(buf);
- return 0;
+ DBUG_RETURN(0);
}
else if (default_val)
{
*var = default_val;
- return 0;
+ DBUG_RETURN(0);
}
- return 1;
+ DBUG_RETURN(1);
}
/*
@@ -1314,15 +813,20 @@ static int init_intvar_from_file(int* var, IO_CACHE* f, int default_val)
when people upgrade a 3.23 master to 4.0 without doing RESET MASTER: 4.0
slaves are fooled. So we do this only to distinguish between 3.23 and more
recent masters (it's too late to change things for 3.23).
-
+
RETURNS
0 ok
1 error
*/
-static int get_master_version_and_clock(MYSQL* mysql, MASTER_INFO* mi)
+static int get_master_version_and_clock(MYSQL* mysql, Master_info* mi)
{
+ char err_buff[MAX_SLAVE_ERRMSG];
const char* errmsg= 0;
+ int err_code= 0;
+ MYSQL_RES *master_res= 0;
+ MYSQL_ROW master_row;
+ DBUG_ENTER("get_master_version_and_clock");
/*
Free old description_event_for_queue (that is needed if we are in
@@ -1330,30 +834,36 @@ static int get_master_version_and_clock(MYSQL* mysql, MASTER_INFO* mi)
*/
delete mi->rli.relay_log.description_event_for_queue;
mi->rli.relay_log.description_event_for_queue= 0;
-
+
if (!my_isdigit(&my_charset_bin,*mysql->server_version))
+ {
errmsg = "Master reported unrecognized MySQL version";
+ err_code= ER_SLAVE_FATAL_ERROR;
+ sprintf(err_buff, ER(err_code), errmsg);
+ }
else
{
/*
Note the following switch will bug when we have MySQL branch 30 ;)
*/
- switch (*mysql->server_version)
+ switch (*mysql->server_version)
{
case '0':
case '1':
case '2':
errmsg = "Master reported unrecognized MySQL version";
+ err_code= ER_SLAVE_FATAL_ERROR;
+ sprintf(err_buff, ER(err_code), errmsg);
break;
case '3':
mi->rli.relay_log.description_event_for_queue= new
- Format_description_log_event(1, mysql->server_version);
+ Format_description_log_event(1, mysql->server_version);
break;
case '4':
mi->rli.relay_log.description_event_for_queue= new
- Format_description_log_event(3, mysql->server_version);
+ Format_description_log_event(3, mysql->server_version);
break;
- default:
+ default:
/*
Master is MySQL >=5.0. Give a default Format_desc event, so that we can
take the early steps (like tests for "is this a 3.23 master") which we
@@ -1363,54 +873,53 @@ static int get_master_version_and_clock(MYSQL* mysql, MASTER_INFO* mi)
master is 3.23, 4.0, etc.
*/
mi->rli.relay_log.description_event_for_queue= new
- Format_description_log_event(4, mysql->server_version);
+ Format_description_log_event(4, mysql->server_version);
break;
}
}
-
- /*
+
+ /*
This does not mean that a 5.0 slave will be able to read a 6.0 master; but
as we don't know yet, we don't want to forbid this for now. If a 5.0 slave
can't read a 6.0 master, this will show up when the slave can't read some
events sent by the master, and there will be error messages.
*/
-
+
if (errmsg)
- {
- sql_print_error(errmsg);
- return 1;
- }
+ goto err;
/* as we are here, we tried to allocate the event */
if (!mi->rli.relay_log.description_event_for_queue)
{
- sql_print_error("Slave I/O thread failed to create a default Format_description_log_event");
- return 1;
+ errmsg= "default Format_description_log_event";
+ err_code= ER_SLAVE_CREATE_EVENT_FAILURE;
+ sprintf(err_buff, ER(err_code), errmsg);
+ goto err;
}
/*
Compare the master and slave's clock. Do not die if master's clock is
unavailable (very old master not supporting UNIX_TIMESTAMP()?).
*/
- MYSQL_RES *master_res= 0;
- MYSQL_ROW master_row;
-
+
if (!mysql_real_query(mysql, STRING_WITH_LEN("SELECT UNIX_TIMESTAMP()")) &&
(master_res= mysql_store_result(mysql)) &&
(master_row= mysql_fetch_row(master_res)))
{
- mi->clock_diff_with_master=
+ mi->clock_diff_with_master=
(long) (time((time_t*) 0) - strtoul(master_row[0], 0, 10));
}
- else
+ else if (!check_io_slave_killed(mi->io_thd, mi, NULL))
{
mi->clock_diff_with_master= 0; /* The "most sensible" value */
- sql_print_warning("\"SELECT UNIX_TIMESTAMP()\" failed on master, \
-do not trust column Seconds_Behind_Master of SHOW SLAVE STATUS");
+ sql_print_warning("\"SELECT UNIX_TIMESTAMP()\" failed on master, "
+ "do not trust column Seconds_Behind_Master of SHOW "
+ "SLAVE STATUS. Error: %s (%d)",
+ mysql_error(mysql), mysql_errno(mysql));
}
if (master_res)
- mysql_free_result(master_res);
-
+ mysql_free_result(master_res);
+
/*
Check that the master's server id and ours are different. Because if they
are equal (which can result from a simple copy of master's datadir to slave,
@@ -1427,12 +936,18 @@ do not trust column Seconds_Behind_Master of SHOW SLAVE STATUS");
{
if ((master_row= mysql_fetch_row(master_res)) &&
(::server_id == strtoul(master_row[1], 0, 10)) &&
- !replicate_same_server_id)
+ !mi->rli.replicate_same_server_id)
+ {
errmsg= "The slave I/O thread stops because master and slave have equal \
MySQL server ids; these ids must be different for replication to work (or \
the --replicate-same-server-id option must be used on slave but this does \
not always make sense; please check the manual before using it).";
+ err_code= ER_SLAVE_FATAL_ERROR;
+ sprintf(err_buff, ER(err_code), errmsg);
+ }
mysql_free_result(master_res);
+ if (errmsg)
+ goto err;
}
/*
@@ -1463,10 +978,16 @@ not always make sense; please check the manual before using it).";
{
if ((master_row= mysql_fetch_row(master_res)) &&
strcmp(master_row[0], global_system_variables.collation_server->name))
+ {
errmsg= "The slave I/O thread stops because master and slave have \
different values for the COLLATION_SERVER global variable. The values must \
be equal for replication to work";
+ err_code= ER_SLAVE_FATAL_ERROR;
+ sprintf(err_buff, ER(err_code), errmsg);
+ }
mysql_free_result(master_res);
+ if (errmsg)
+ goto err;
}
/*
@@ -1477,9 +998,9 @@ be equal for replication to work";
time and so could differ for slave and master even if they are really
in the same system time zone. So we are omiting this check and just
relying on documentation. Also according to Monty there are many users
- who are using replication between servers in various time zones. Hence
- such check will broke everything for them. (And now everything will
- work for them because by default both their master and slave will have
+ who are using replication between servers in various time zones. Hence
+ such check will broke everything for them. (And now everything will
+ work for them because by default both their master and slave will have
'SYSTEM' time zone).
This check is only necessary for 4.x masters (and < 5.0.4 masters but
those were alpha).
@@ -1489,22 +1010,30 @@ be equal for replication to work";
(master_res= mysql_store_result(mysql)))
{
if ((master_row= mysql_fetch_row(master_res)) &&
- strcmp(master_row[0],
+ strcmp(master_row[0],
global_system_variables.time_zone->get_name()->ptr()))
+ {
errmsg= "The slave I/O thread stops because master and slave have \
different values for the TIME_ZONE global variable. The values must \
be equal for replication to work";
+ err_code= ER_SLAVE_FATAL_ERROR;
+ sprintf(err_buff, ER(err_code), errmsg);
+ }
mysql_free_result(master_res);
+
+ if (errmsg)
+ goto err;
}
err:
if (errmsg)
{
- sql_print_error(errmsg);
- return 1;
+ DBUG_ASSERT(err_code != 0);
+ mi->report(ERROR_LEVEL, err_code, err_buff);
+ DBUG_RETURN(1);
}
- return 0;
+ DBUG_RETURN(0);
}
/*
@@ -1519,7 +1048,7 @@ err:
*/
static int create_table_from_dump(THD* thd, MYSQL *mysql, const char* db,
- const char* table_name, bool overwrite)
+ const char* table_name, bool overwrite)
{
ulong packet_len;
char *query, *save_db;
@@ -1532,7 +1061,7 @@ static int create_table_from_dump(THD* thd, MYSQL *mysql, const char* db,
ulonglong save_options;
NET *net= &mysql->net;
const char *found_semicolon= NULL;
- DBUG_ENTER("create_table_from_dump");
+ DBUG_ENTER("create_table_from_dump");
packet_len= my_net_read(net); // read create table statement
if (packet_len == packet_error)
@@ -1542,10 +1071,10 @@ static int create_table_from_dump(THD* thd, MYSQL *mysql, const char* db,
}
if (net->read_pos[0] == 255) // error from master
{
- char *err_msg;
+ char *err_msg;
err_msg= (char*) net->read_pos + ((mysql->server_capabilities &
- CLIENT_PROTOCOL_41) ?
- 3+SQLSTATE_LENGTH+1 : 3);
+ CLIENT_PROTOCOL_41) ?
+ 3+SQLSTATE_LENGTH+1 : 3);
my_error(ER_MASTER, MYF(0), err_msg);
DBUG_RETURN(1);
}
@@ -1559,47 +1088,62 @@ static int create_table_from_dump(THD* thd, MYSQL *mysql, const char* db,
DBUG_RETURN(1);
}
thd->query= query;
- thd->query_error = 0;
- thd->net.no_send_ok = 1;
+ 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 && mysql_rm_table(thd,&tables,1,0)) /* drop if exists */
+ if (overwrite)
{
- sql_print_error("create_table_from_dump: failed to drop the table");
- goto err;
+ 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 &= ~(ulong) (OPTION_BIN_LOG);
- thd->proc_info = "Creating table from master dump";
+ 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;
- DBUG_ASSERT(db != 0);
- thd->reset_db((char*)db, (uint) strlen(db));
+ thd->db = (char*)db;
+ DBUG_ASSERT(thd->db != 0);
+ thd->db_length= strlen(thd->db);
mysql_parse(thd, thd->query, packet_len, &found_semicolon); // run create table
- thd->db = save_db; // leave things the way the were before
+ thd->db = save_db; // leave things the way the were before
thd->db_length= save_db_length;
thd->options = save_options;
-
- if (thd->query_error)
- goto err; // mysql_parse took care of the error send
- thd->proc_info = "Opening master dump table";
+ 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))
+ 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 = "Reading master dump table data";
+ thd_proc_info(thd, "Reading master dump table data");
/* Copy the data file */
if (file->net_read_dump(net))
{
@@ -1611,7 +1155,7 @@ static int create_table_from_dump(THD* thd, MYSQL *mysql, const char* db,
check_opt.init();
check_opt.flags|= T_VERY_SILENT | T_CALC_CHECKSUM | T_QUICK;
- thd->proc_info = "Rebuilding the index on master dump table";
+ 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
@@ -1623,27 +1167,26 @@ static int create_table_from_dump(THD* thd, MYSQL *mysql, const char* db,
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);
+ my_error(ER_INDEX_REBUILD, MYF(0), tables.table->s->table_name.str);
err:
close_thread_tables(thd);
- thd->net.no_send_ok = 0;
- DBUG_RETURN(error);
+ DBUG_RETURN(error);
}
int fetch_master_table(THD *thd, const char *db_name, const char *table_name,
- MASTER_INFO *mi, MYSQL *mysql, bool overwrite)
+ 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));
+ db_name,table_name));
if (!called_connected)
- {
+ {
if (!(mysql = mysql_init(NULL)))
{
DBUG_RETURN(1);
@@ -1674,288 +1217,34 @@ int fetch_master_table(THD *thd, const char *db_name, const char *table_name,
goto err;
}
if (create_table_from_dump(thd, mysql, db_name,
- table_name, overwrite))
+ table_name, overwrite))
goto err; // create_table_from_dump have sent the error already
error = 0;
err:
- thd->net.no_send_ok = 0; // Clear up garbage after create_table_from_dump
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
+ DBUG_RETURN(test(error)); // Return 1 on error
}
-void end_master_info(MASTER_INFO* mi)
-{
- DBUG_ENTER("end_master_info");
-
- if (!mi->inited)
- DBUG_VOID_RETURN;
- end_relay_log_info(&mi->rli);
- if (mi->fd >= 0)
- {
- end_io_cache(&mi->file);
- (void)my_close(mi->fd, MYF(MY_WME));
- mi->fd = -1;
- }
- mi->inited = 0;
-
- DBUG_VOID_RETURN;
-}
-
-
-static int init_relay_log_info(RELAY_LOG_INFO* rli,
- const char* info_fname)
-{
- char fname[FN_REFLEN+128];
- int info_fd;
- const char* msg = 0;
- int error;
- DBUG_ENTER("init_relay_log_info");
-
- 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);
- info_fd = rli->info_fd;
- rli->cur_log_fd = -1;
- rli->slave_skip_counter=0;
- rli->abort_pos_wait=0;
- rli->log_space_limit= relay_log_space_limit;
- rli->log_space_total= 0;
-
- /*
- The relay log will now be opened, as a SEQ_READ_APPEND IO_CACHE.
- Note that the I/O thread flushes it to disk after writing every
- event, in flush_master_info(mi, 1).
- */
-
- /*
- For the maximum log size, we choose max_relay_log_size if it is
- non-zero, max_binlog_size otherwise. If later the user does SET
- GLOBAL on one of these variables, fix_max_binlog_size and
- fix_max_relay_log_size will reconsider the choice (for example
- if the user changes max_relay_log_size to zero, we have to
- switch to using max_binlog_size for the relay log) and update
- rli->relay_log.max_size (and mysql_bin_log.max_size).
- */
- {
- char buf[FN_REFLEN];
- const char *ln;
- static bool name_warning_sent= 0;
- 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)
- {
- /*
- User didn't give us info to name the relay log index file.
- Picking `hostname`-relay-bin.index like we do, causes replication to
- fail if this slave's hostname is changed later. So, we would like to
- instead require a name. But as we don't want to break many existing
- setups, we only give warning, not error.
- */
- sql_print_warning("Neither --relay-log nor --relay-log-index were used;"
- " so replication "
- "may break when this MySQL server acts as a "
- "slave and has his hostname changed!! Please "
- "use '--relay-log=%s' to avoid this problem.", ln);
- name_warning_sent= 1;
- }
- /*
- note, that if open() fails, we'll still have index file open
- but a destructor will take care of that
- */
- if (rli->relay_log.open_index_file(opt_relaylog_index_name, ln) ||
- rli->relay_log.open(ln, LOG_BIN, 0, SEQ_READ_APPEND, 0,
- (max_relay_log_size ? max_relay_log_size :
- max_binlog_size), 1))
- {
- pthread_mutex_unlock(&rli->data_lock);
- sql_print_error("Failed in open_log() called from init_relay_log_info()");
- DBUG_RETURN(1);
- }
- }
-
- /* if file does not exist */
- if (access(fname,F_OK))
- {
- /*
- If someone removed the file from underneath our feet, just close
- 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)
- {
- sql_print_error("Failed to create a new relay log info file (\
-file '%s', errno %d)", fname, my_errno);
- msg= current_thd->net.last_error;
- goto err;
- }
- if (init_io_cache(&rli->info_file, info_fd, IO_SIZE*2, READ_CACHE, 0L,0,
- MYF(MY_WME)))
- {
- sql_print_error("Failed to create a cache on relay log info file '%s'",
- fname);
- msg= current_thd->net.last_error;
- goto err;
- }
-
- /* Init relay log with first entry in the relay index file */
- if (init_relay_log_pos(rli,NullS,BIN_LOG_HEADER_SIZE,0 /* no data lock */,
- &msg, 0))
- {
- sql_print_error("Failed to open the relay log 'FIRST' (relay_log_pos 4)");
- goto err;
- }
- rli->group_master_log_name[0]= 0;
- rli->group_master_log_pos= 0;
- rli->info_fd= info_fd;
- }
- else // file exists
- {
- error= 0;
- if (info_fd >= 0)
- reinit_io_cache(&rli->info_file, READ_CACHE, 0L,0,0);
- else
- {
- if ((info_fd = my_open(fname, O_RDWR|O_BINARY, MYF(MY_WME))) < 0)
- {
- sql_print_error("\
-Failed to open the existing relay log info file '%s' (errno %d)",
- fname, my_errno);
- error= 1;
- }
- else if (init_io_cache(&rli->info_file, info_fd,
- IO_SIZE*2, READ_CACHE, 0L, 0, MYF(MY_WME)))
- {
- sql_print_error("Failed to create a cache on relay log info file '%s'",
- fname);
- error= 1;
- }
- if (error)
- {
- if (info_fd >= 0)
- my_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);
- DBUG_RETURN(1);
- }
- }
-
- rli->info_fd = info_fd;
- int relay_log_pos, master_log_pos;
- if (init_strvar_from_file(rli->group_relay_log_name,
- sizeof(rli->group_relay_log_name),
- &rli->info_file, "") ||
- init_intvar_from_file(&relay_log_pos,
- &rli->info_file, BIN_LOG_HEADER_SIZE) ||
- init_strvar_from_file(rli->group_master_log_name,
- sizeof(rli->group_master_log_name),
- &rli->info_file, "") ||
- init_intvar_from_file(&master_log_pos, &rli->info_file, 0))
- {
- 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);
- rli->group_relay_log_pos= rli->event_relay_log_pos= relay_log_pos;
- rli->group_master_log_pos= master_log_pos;
-
- if (init_relay_log_pos(rli,
- rli->group_relay_log_name,
- rli->group_relay_log_pos,
- 0 /* no data lock*/,
- &msg, 0))
- {
- char llbuf[22];
- sql_print_error("Failed to open the relay log '%s' (relay_log_pos %s)",
- rli->group_relay_log_name,
- llstr(rli->group_relay_log_pos, llbuf));
- goto err;
- }
- }
-
-#ifndef DBUG_OFF
- {
- char llbuf1[22], llbuf2[22];
- DBUG_PRINT("info", ("my_b_tell(rli->cur_log)=%s rli->event_relay_log_pos=%s",
- llstr(my_b_tell(rli->cur_log),llbuf1),
- llstr(rli->event_relay_log_pos,llbuf2)));
- DBUG_ASSERT(rli->event_relay_log_pos >= BIN_LOG_HEADER_SIZE);
- DBUG_ASSERT(my_b_tell(rli->cur_log) == rli->event_relay_log_pos);
- }
-#endif
-
- /*
- Now change the cache from READ to WRITE - must do this
- before flush_relay_log_info
- */
- 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");
- if (count_relay_log_space(rli))
- {
- msg="Error counting relay log space";
- goto err;
- }
- rli->inited= 1;
- pthread_mutex_unlock(&rli->data_lock);
- DBUG_RETURN(error);
-
-err:
- sql_print_error(msg);
- end_io_cache(&rli->info_file);
- if (info_fd >= 0)
- my_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);
- DBUG_RETURN(1);
-}
-
-
-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)))
- {
- sql_print_error("log %s listed in the index, but failed to stat",
- linfo->log_file_name);
- DBUG_RETURN(1);
- }
- rli->log_space_total += s.st_size;
-#ifndef DBUG_OFF
- char buf[22];
- DBUG_PRINT("info",("log_space_total: %s", llstr(rli->log_space_total,buf)));
-#endif
- DBUG_RETURN(0);
-}
-
-
-static bool wait_for_relay_log_space(RELAY_LOG_INFO* rli)
+static bool wait_for_relay_log_space(Relay_log_info* rli)
{
bool slave_killed=0;
- MASTER_INFO* mi = rli->mi;
+ Master_info* mi = rli->mi;
const char *save_proc_info;
THD* thd = mi->io_thd;
-
DBUG_ENTER("wait_for_relay_log_space");
pthread_mutex_lock(&rli->log_space_lock);
save_proc_info= thd->enter_cond(&rli->log_space_cond,
- &rli->log_space_lock,
- "\
+ &rli->log_space_lock,
+ "\
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)) &&
+ !(slave_killed=io_slave_killed(thd,mi)) &&
!rli->ignore_log_space_limit)
pthread_cond_wait(&rli->log_space_cond, &rli->log_space_lock);
thd->exit_cond(save_proc_info);
@@ -1963,31 +1252,6 @@ Waiting for the slave SQL thread to free enough relay log space");
}
-static int count_relay_log_space(RELAY_LOG_INFO* rli)
-{
- LOG_INFO linfo;
- DBUG_ENTER("count_relay_log_space");
- rli->log_space_total= 0;
- if (rli->relay_log.find_log_pos(&linfo, NullS, 1))
- {
- sql_print_error("Could not find first log while counting relay log space");
- DBUG_RETURN(1);
- }
- do
- {
- if (add_relay_log(rli,&linfo))
- DBUG_RETURN(1);
- } while (!rli->relay_log.find_next_log(&linfo, 1));
- /*
- As we have counted everything, including what may have written in a
- preceding write, we must reset bytes_written, or we may count some space
- twice.
- */
- rli->relay_log.reset_bytes_written();
- DBUG_RETURN(0);
-}
-
-
/*
Builds a Rotate from the ignored events' info and writes it to relay log.
@@ -2001,16 +1265,18 @@ static int count_relay_log_space(RELAY_LOG_INFO* rli)
ignored events' end position for the use of the slave SQL thread, by
calling this function. Only that thread can call it (see assertion).
*/
-static void write_ignored_events_info_to_relay_log(THD *thd, MASTER_INFO *mi)
+static void write_ignored_events_info_to_relay_log(THD *thd, Master_info *mi)
{
- RELAY_LOG_INFO *rli= &mi->rli;
+ Relay_log_info *rli= &mi->rli;
pthread_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);
if (rli->ign_master_log_name_end[0])
{
DBUG_PRINT("info",("writing a Rotate event to track down ignored events"));
- Rotate_log_event *ev= new Rotate_log_event(thd, rli->ign_master_log_name_end,
+ Rotate_log_event *ev= new Rotate_log_event(rli->ign_master_log_name_end,
0, rli->ign_master_log_pos_end,
Rotate_log_event::DUP_NAME);
rli->ign_master_log_name_end[0]= 0;
@@ -2020,374 +1286,89 @@ static void write_ignored_events_info_to_relay_log(THD *thd, MASTER_INFO *mi)
{
ev->server_id= 0; // don't be ignored by slave SQL thread
if (unlikely(rli->relay_log.append(ev)))
- sql_print_error("Slave I/O thread failed to write a Rotate event"
- " to the relay log, "
- "SHOW SLAVE STATUS may be inaccurate");
+ mi->report(ERROR_LEVEL, ER_SLAVE_RELAY_LOG_WRITE_FAILURE,
+ ER(ER_SLAVE_RELAY_LOG_WRITE_FAILURE),
+ "failed to write a Rotate event"
+ " to the relay log, SHOW SLAVE STATUS may be"
+ " inaccurate");
rli->relay_log.harvest_bytes_written(&rli->log_space_total);
if (flush_master_info(mi, 1))
sql_print_error("Failed to flush master info file");
delete ev;
}
else
- sql_print_error("Slave I/O thread failed to create a Rotate event"
- " (out of memory?), "
- "SHOW SLAVE STATUS may be inaccurate");
+ mi->report(ERROR_LEVEL, ER_SLAVE_CREATE_EVENT_FAILURE,
+ ER(ER_SLAVE_CREATE_EVENT_FAILURE),
+ "Rotate_event (out of memory?),"
+ " SHOW SLAVE STATUS may be inaccurate");
}
else
pthread_mutex_unlock(log_lock);
+ DBUG_VOID_RETURN;
}
-void init_master_info_with_options(MASTER_INFO* mi)
-{
- 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);
-}
-
-void clear_slave_error(RELAY_LOG_INFO* rli)
-{
- /* Clear the errors displayed by SHOW SLAVE STATUS */
- rli->last_slave_error[0]= 0;
- rli->last_slave_errno= 0;
-}
-
-/*
- Reset UNTIL condition for RELAY_LOG_INFO
- SYNOPSYS
- clear_until_condition()
- rli - RELAY_LOG_INFO structure where UNTIL condition should be reset
- */
-void clear_until_condition(RELAY_LOG_INFO* rli)
-{
- rli->until_condition= RELAY_LOG_INFO::UNTIL_NONE;
- rli->until_log_name[0]= 0;
- rli->until_log_pos= 0;
-}
-
-
-#define LINES_IN_MASTER_INFO_WITH_SSL 14
-
-
-int init_master_info(MASTER_INFO* mi, const char* master_info_fname,
- const char* slave_info_fname,
- bool abort_if_no_master_info_file,
- int thread_mask)
-{
- int fd,error;
- char fname[FN_REFLEN+128];
- DBUG_ENTER("init_master_info");
-
- if (mi->inited)
- {
- /*
- We have to reset read position of relay-log-bin as we may have
- already been reading from 'hotlog' when the slave was stopped
- last time. If this case pos_in_file would be set and we would
- get a crash when trying to read the signature for the binary
- relay log.
-
- We only rewind the read position if we are starting the SQL
- thread. The handle_slave_sql thread assumes that the read
- position is at the beginning of the file, and will read the
- "signature" and then fast-forward to the last position read.
- */
- if (thread_mask & SLAVE_SQL)
- {
- my_b_seek(mi->rli.cur_log, (my_off_t) 0);
- }
- DBUG_RETURN(0);
- }
-
- mi->mysql=0;
- mi->file_id=1;
- fn_format(fname, master_info_fname, mysql_data_home, "", 4+32);
-
- /*
- We need a mutex while we are changing master info parameters to
- keep other threads from reading bogus info
- */
-
- pthread_mutex_lock(&mi->data_lock);
- fd = mi->fd;
-
- /* does master.info exist ? */
-
- if (access(fname,F_OK))
- {
- if (abort_if_no_master_info_file)
- {
- pthread_mutex_unlock(&mi->data_lock);
- DBUG_RETURN(0);
- }
- /*
- if someone removed the file from underneath our feet, just close
- 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 )
- {
- sql_print_error("Failed to create a new master info file (\
-file '%s', errno %d)", fname, my_errno);
- goto err;
- }
- if (init_io_cache(&mi->file, fd, IO_SIZE*2, READ_CACHE, 0L,0,
- MYF(MY_WME)))
- {
- sql_print_error("Failed to create a cache on master info file (\
-file '%s')", fname);
- goto err;
- }
-
- mi->fd = fd;
- init_master_info_with_options(mi);
-
- }
- else // file exists
- {
- if (fd >= 0)
- reinit_io_cache(&mi->file, READ_CACHE, 0L,0,0);
- else
- {
- if ((fd = my_open(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);
- goto err;
- }
- if (init_io_cache(&mi->file, fd, IO_SIZE*2, READ_CACHE, 0L,
- 0, MYF(MY_WME)))
- {
- sql_print_error("Failed to create a cache on master info file (\
-file '%s')", fname);
- goto err;
- }
- }
-
- mi->fd = fd;
- int port, connect_retry, master_log_pos, ssl= 0, lines;
- char *first_non_digit;
-
- /*
- Starting from 4.1.x master.info has new format. Now its
- first line contains number of lines in file. By reading this
- number we will be always distinguish to which version our
- master.info corresponds to. We can't simply count lines in
- file since versions before 4.1.x could generate files with more
- lines than needed.
- If first line doesn't contain a number or contain number less than
- 14 then such file is treated like file from pre 4.1.1 version.
- There is no ambiguity when reading an old master.info, as before
- 4.1.1, the first line contained the binlog's name, which is either
- empty or has an extension (contains a '.'), so can't be confused
- with an integer.
-
- So we're just reading first line and trying to figure which version
- is this.
- */
-
- /*
- The first row is temporarily stored in mi->master_log_name,
- if it is line count and not binlog name (new format) it will be
- overwritten by the second row later.
- */
- if (init_strvar_from_file(mi->master_log_name,
- sizeof(mi->master_log_name), &mi->file,
- ""))
- goto errwithmsg;
-
- lines= strtoul(mi->master_log_name, &first_non_digit, 10);
-
- if (mi->master_log_name[0]!='\0' &&
- *first_non_digit=='\0' && lines >= LINES_IN_MASTER_INFO_WITH_SSL)
- { // Seems to be new format
- if (init_strvar_from_file(mi->master_log_name,
- sizeof(mi->master_log_name), &mi->file, ""))
- goto errwithmsg;
- }
- else
- 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->password, SCRAMBLED_PASSWORD_CHAR_LENGTH+1,
- &mi->file, master_password) ||
- init_intvar_from_file(&port, &mi->file, master_port) ||
- init_intvar_from_file(&connect_retry, &mi->file,
- master_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
- slave will try connect to master, so in this case warning
- is printed.
- */
- if (lines >= LINES_IN_MASTER_INFO_WITH_SSL &&
- (init_intvar_from_file(&ssl, &mi->file, master_ssl) ||
- init_strvar_from_file(mi->ssl_ca, sizeof(mi->ssl_ca),
- &mi->file, master_ssl_ca) ||
- init_strvar_from_file(mi->ssl_capath, sizeof(mi->ssl_capath),
- &mi->file, master_ssl_capath) ||
- init_strvar_from_file(mi->ssl_cert, sizeof(mi->ssl_cert),
- &mi->file, master_ssl_cert) ||
- init_strvar_from_file(mi->ssl_cipher, sizeof(mi->ssl_cipher),
- &mi->file, master_ssl_cipher) ||
- init_strvar_from_file(mi->ssl_key, sizeof(mi->ssl_key),
- &mi->file, master_ssl_key)))
- 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);
-#endif /* HAVE_OPENSSL */
-
- /*
- This has to be handled here as init_intvar_from_file can't handle
- my_off_t types
- */
- mi->master_log_pos= (my_off_t) master_log_pos;
- mi->port= (uint) port;
- mi->connect_retry= (uint) connect_retry;
- mi->ssl= (my_bool) ssl;
- }
- DBUG_PRINT("master_info",("log_file_name: %s position: %ld",
- mi->master_log_name,
- (ulong) mi->master_log_pos));
-
- mi->rli.mi = mi;
- if (init_relay_log_info(&mi->rli, slave_info_fname))
- goto err;
-
- mi->inited = 1;
- // 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, 1))))
- sql_print_error("Failed to flush master info file");
- pthread_mutex_unlock(&mi->data_lock);
- DBUG_RETURN(error);
-
-errwithmsg:
- sql_print_error("Error reading master configuration");
-
-err:
- if (fd >= 0)
- {
- my_close(fd, MYF(0));
- end_io_cache(&mi->file);
- }
- mi->fd= -1;
- pthread_mutex_unlock(&mi->data_lock);
- DBUG_RETURN(1);
-}
-
-
-int register_slave_on_master(MYSQL* mysql)
+int register_slave_on_master(MYSQL* mysql, Master_info *mi,
+ bool *suppress_warnings)
{
- char buf[1024], *pos= buf;
+ uchar buf[1024], *pos= buf;
uint report_host_len, report_user_len=0, report_password_len=0;
+ DBUG_ENTER("register_slave_on_master");
+ *suppress_warnings= FALSE;
if (!report_host)
- return 0;
- report_host_len= (uint) strlen(report_host);
+ DBUG_RETURN(0);
+ report_host_len= strlen(report_host);
if (report_user)
- report_user_len= (uint) strlen(report_user);
+ report_user_len= strlen(report_user);
if (report_password)
- report_password_len= (uint) strlen(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))
- return 0; // safety
+ DBUG_RETURN(0); // safety
int4store(pos, server_id); pos+= 4;
- pos= net_store_data(pos, report_host, report_host_len);
- pos= net_store_data(pos, report_user, report_user_len);
- pos= net_store_data(pos, report_password, report_password_len);
+ 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;
+ int4store(pos, rpl_recovery_rank); pos+= 4;
/* The master will fill in master_id */
- int4store(pos, 0); pos+= 4;
+ int4store(pos, 0); pos+= 4;
- if (simple_command(mysql, COM_REGISTER_SLAVE, (char*) buf,
- (uint) (pos- buf), 0))
+ if (simple_command(mysql, COM_REGISTER_SLAVE, buf, (size_t) (pos- buf), 0))
{
- sql_print_error("Error on COM_REGISTER_SLAVE: %d '%s'",
- mysql_errno(mysql),
- mysql_error(mysql));
- return 1;
+ if (mysql_errno(mysql) == ER_NET_READ_INTERRUPTED)
+ {
+ *suppress_warnings= TRUE; // Suppress reconnect warning
+ }
+ else if (!check_io_slave_killed(mi->io_thd, mi, NULL))
+ {
+ char buf[256];
+ my_snprintf(buf, sizeof(buf), "%s (Errno: %d)", mysql_error(mysql),
+ mysql_errno(mysql));
+ mi->report(ERROR_LEVEL, ER_SLAVE_MASTER_COM_FAILURE,
+ ER(ER_SLAVE_MASTER_COM_FAILURE), "COM_REGISTER_SLAVE", buf);
+ }
+ DBUG_RETURN(1);
}
- return 0;
+ DBUG_RETURN(0);
}
-/*
- 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.
+/**
+ Execute a SHOW SLAVE STATUS statement.
- SYNOPSIS
- table_rule_ent_hash_to_str()
- s pointer to the String to fill
- h pointer to the HASH to read
+ @param thd Pointer to THD object for the client thread executing the
+ statement.
- RETURN VALUES
- none
-*/
+ @param mi Pointer to Master_info object for the IO thread.
-void table_rule_ent_hash_to_str(String* s, HASH* h)
-{
- s->length(0);
- for (uint i=0 ; i < h->records ; i++)
- {
- TABLE_RULE_ENT* e= (TABLE_RULE_ENT*) hash_element(h, i);
- if (s->length())
- s->append(',');
- s->append(e->db,e->key_len);
- }
-}
-
-/*
- Mostly the same thing as above
+ @retval FALSE success
+ @retval TRUE failure
*/
-
-void table_rule_ent_dynamic_array_to_str(String* s, DYNAMIC_ARRAY* a)
-{
- s->length(0);
- for (uint i=0 ; i < a->elements ; i++)
- {
- TABLE_RULE_ENT* e;
- get_dynamic(a, (gptr)&e, i);
- if (s->length())
- s->append(',');
- s->append(e->db,e->key_len);
- }
-}
-
-bool show_master_info(THD* thd, MASTER_INFO* mi)
+bool show_master_info(THD* thd, Master_info* mi)
{
// TODO: fix this for multi-master
List<Item> field_list;
@@ -2395,25 +1376,25 @@ bool show_master_info(THD* thd, MASTER_INFO* mi)
DBUG_ENTER("show_master_info");
field_list.push_back(new Item_empty_string("Slave_IO_State",
- 14));
+ 14));
field_list.push_back(new Item_empty_string("Master_Host",
- sizeof(mi->host)));
+ sizeof(mi->host)));
field_list.push_back(new Item_empty_string("Master_User",
- sizeof(mi->user)));
+ sizeof(mi->user)));
field_list.push_back(new Item_return_int("Master_Port", 7,
- MYSQL_TYPE_LONG));
+ MYSQL_TYPE_LONG));
field_list.push_back(new Item_return_int("Connect_Retry", 10,
- MYSQL_TYPE_LONG));
+ MYSQL_TYPE_LONG));
field_list.push_back(new Item_empty_string("Master_Log_File",
- FN_REFLEN));
+ FN_REFLEN));
field_list.push_back(new Item_return_int("Read_Master_Log_Pos", 10,
- MYSQL_TYPE_LONGLONG));
+ MYSQL_TYPE_LONGLONG));
field_list.push_back(new Item_empty_string("Relay_Log_File",
- FN_REFLEN));
+ FN_REFLEN));
field_list.push_back(new Item_return_int("Relay_Log_Pos", 10,
- MYSQL_TYPE_LONGLONG));
+ MYSQL_TYPE_LONGLONG));
field_list.push_back(new Item_empty_string("Relay_Master_Log_File",
- FN_REFLEN));
+ FN_REFLEN));
field_list.push_back(new Item_empty_string("Slave_IO_Running", 3));
field_list.push_back(new Item_empty_string("Slave_SQL_Running", 3));
field_list.push_back(new Item_empty_string("Replicate_Do_DB", 20));
@@ -2422,33 +1403,39 @@ bool show_master_info(THD* thd, MASTER_INFO* mi)
field_list.push_back(new Item_empty_string("Replicate_Ignore_Table", 23));
field_list.push_back(new Item_empty_string("Replicate_Wild_Do_Table", 24));
field_list.push_back(new Item_empty_string("Replicate_Wild_Ignore_Table",
- 28));
+ 28));
field_list.push_back(new Item_return_int("Last_Errno", 4, MYSQL_TYPE_LONG));
field_list.push_back(new Item_empty_string("Last_Error", 20));
field_list.push_back(new Item_return_int("Skip_Counter", 10,
- MYSQL_TYPE_LONG));
+ MYSQL_TYPE_LONG));
field_list.push_back(new Item_return_int("Exec_Master_Log_Pos", 10,
- MYSQL_TYPE_LONGLONG));
+ MYSQL_TYPE_LONGLONG));
field_list.push_back(new Item_return_int("Relay_Log_Space", 10,
- MYSQL_TYPE_LONGLONG));
+ MYSQL_TYPE_LONGLONG));
field_list.push_back(new Item_empty_string("Until_Condition", 6));
field_list.push_back(new Item_empty_string("Until_Log_File", FN_REFLEN));
- field_list.push_back(new Item_return_int("Until_Log_Pos", 10,
+ field_list.push_back(new Item_return_int("Until_Log_Pos", 10,
MYSQL_TYPE_LONGLONG));
field_list.push_back(new Item_empty_string("Master_SSL_Allowed", 7));
field_list.push_back(new Item_empty_string("Master_SSL_CA_File",
sizeof(mi->ssl_ca)));
- field_list.push_back(new Item_empty_string("Master_SSL_CA_Path",
+ field_list.push_back(new Item_empty_string("Master_SSL_CA_Path",
sizeof(mi->ssl_capath)));
- field_list.push_back(new Item_empty_string("Master_SSL_Cert",
+ field_list.push_back(new Item_empty_string("Master_SSL_Cert",
sizeof(mi->ssl_cert)));
- field_list.push_back(new Item_empty_string("Master_SSL_Cipher",
+ field_list.push_back(new Item_empty_string("Master_SSL_Cipher",
sizeof(mi->ssl_cipher)));
- field_list.push_back(new Item_empty_string("Master_SSL_Key",
+ field_list.push_back(new Item_empty_string("Master_SSL_Key",
sizeof(mi->ssl_key)));
field_list.push_back(new Item_return_int("Seconds_Behind_Master", 10,
MYSQL_TYPE_LONGLONG));
-
+ field_list.push_back(new Item_empty_string("Master_SSL_Verify_Server_Cert",
+ 3));
+ field_list.push_back(new Item_return_int("Last_IO_Errno", 4, MYSQL_TYPE_LONG));
+ 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));
+
if (protocol->send_fields(&field_list,
Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
DBUG_RETURN(TRUE);
@@ -2458,7 +1445,7 @@ bool show_master_info(THD* thd, MASTER_INFO* mi)
DBUG_PRINT("info",("host is set: '%s'", mi->host));
String *packet= &thd->packet;
protocol->prepare_for_resend();
-
+
/*
slave_running can be accessed without run_lock but not other
non-volotile members like mi->io_thd, which is guarded by the mutex.
@@ -2476,46 +1463,41 @@ bool show_master_info(THD* thd, MASTER_INFO* mi)
protocol->store(mi->master_log_name, &my_charset_bin);
protocol->store((ulonglong) mi->master_log_pos);
protocol->store(mi->rli.group_relay_log_name +
- dirname_length(mi->rli.group_relay_log_name),
- &my_charset_bin);
+ dirname_length(mi->rli.group_relay_log_name),
+ &my_charset_bin);
protocol->store((ulonglong) mi->rli.group_relay_log_pos);
protocol->store(mi->rli.group_master_log_name, &my_charset_bin);
protocol->store(mi->slave_running == MYSQL_SLAVE_RUN_CONNECT ?
"Yes" : "No", &my_charset_bin);
protocol->store(mi->rli.slave_running ? "Yes":"No", &my_charset_bin);
- protocol->store(&replicate_do_db);
- protocol->store(&replicate_ignore_db);
- /*
- We can't directly use some protocol->store for
- replicate_*_table,
- as Protocol doesn't know the TABLE_RULE_ENT struct.
- We first build Strings and then pass them to protocol->store.
- */
+ protocol->store(rpl_filter->get_do_db());
+ protocol->store(rpl_filter->get_ignore_db());
+
char buf[256];
String tmp(buf, sizeof(buf), &my_charset_bin);
- table_rule_ent_hash_to_str(&tmp, &replicate_do_table);
+ rpl_filter->get_do_table(&tmp);
protocol->store(&tmp);
- table_rule_ent_hash_to_str(&tmp, &replicate_ignore_table);
+ rpl_filter->get_ignore_table(&tmp);
protocol->store(&tmp);
- table_rule_ent_dynamic_array_to_str(&tmp, &replicate_wild_do_table);
+ rpl_filter->get_wild_do_table(&tmp);
protocol->store(&tmp);
- table_rule_ent_dynamic_array_to_str(&tmp, &replicate_wild_ignore_table);
+ rpl_filter->get_wild_ignore_table(&tmp);
protocol->store(&tmp);
- protocol->store((uint32) mi->rli.last_slave_errno);
- protocol->store(mi->rli.last_slave_error, &my_charset_bin);
+ protocol->store(mi->rli.last_error().number);
+ protocol->store(mi->rli.last_error().message, &my_charset_bin);
protocol->store((uint32) mi->rli.slave_skip_counter);
protocol->store((ulonglong) mi->rli.group_master_log_pos);
protocol->store((ulonglong) mi->rli.log_space_total);
protocol->store(
- mi->rli.until_condition==RELAY_LOG_INFO::UNTIL_NONE ? "None":
- ( mi->rli.until_condition==RELAY_LOG_INFO::UNTIL_MASTER_POS? "Master":
+ mi->rli.until_condition==Relay_log_info::UNTIL_NONE ? "None":
+ ( mi->rli.until_condition==Relay_log_info::UNTIL_MASTER_POS? "Master":
"Relay"), &my_charset_bin);
protocol->store(mi->rli.until_log_name, &my_charset_bin);
protocol->store((ulonglong) mi->rli.until_log_pos);
-
-#ifdef HAVE_OPENSSL
+
+#ifdef HAVE_OPENSSL
protocol->store(mi->ssl? "Yes":"No", &my_charset_bin);
#else
protocol->store(mi->ssl? "Ignored":"No", &my_charset_bin);
@@ -2533,8 +1515,7 @@ bool show_master_info(THD* thd, MASTER_INFO* mi)
if ((mi->slave_running == MYSQL_SLAVE_RUN_CONNECT) &&
mi->rli.slave_running)
{
- long time_diff= ((long)((time_t)time((time_t*) 0)
- - mi->rli.last_master_timestamp)
+ long time_diff= ((long)(time(0) - mi->rli.last_master_timestamp)
- mi->clock_diff_with_master);
/*
Apparently on some systems time_diff can be <0. Here are possible
@@ -2560,318 +1541,34 @@ bool show_master_info(THD* thd, MASTER_INFO* mi)
max(0, time_diff) : 0));
}
else
+ {
protocol->store_null();
+ }
+ protocol->store(mi->ssl_verify_server_cert? "Yes":"No", &my_charset_bin);
+
+ // Last_IO_Errno
+ protocol->store(mi->last_error().number);
+ // Last_IO_Error
+ protocol->store(mi->last_error().message, &my_charset_bin);
+ // Last_SQL_Errno
+ protocol->store(mi->rli.last_error().number);
+ // Last_SQL_Error
+ protocol->store(mi->rli.last_error().message, &my_charset_bin);
pthread_mutex_unlock(&mi->rli.data_lock);
pthread_mutex_unlock(&mi->data_lock);
- if (my_net_write(&thd->net, (char*)thd->packet.ptr(), packet->length()))
+ if (my_net_write(&thd->net, (uchar*) thd->packet.ptr(), packet->length()))
DBUG_RETURN(TRUE);
}
- send_eof(thd);
+ my_eof(thd);
DBUG_RETURN(FALSE);
}
-/*
- RETURN
- 2 - flush relay log failed
- 1 - flush master info failed
- 0 - all ok
-*/
-int flush_master_info(MASTER_INFO* mi, bool flush_relay_log_cache)
-{
- IO_CACHE* file = &mi->file;
- char lbuf[22];
- DBUG_ENTER("flush_master_info");
- DBUG_PRINT("enter",("master_pos: %ld", (long) mi->master_log_pos));
-
- /*
- Flush the relay log to disk. If we don't do it, then the relay log while
- have some part (its last kilobytes) in memory only, so if the slave server
- dies now, with, say, from master's position 100 to 150 in memory only (not
- on disk), and with position 150 in master.info, then when the slave
- restarts, the I/O thread will fetch binlogs from 150, so in the relay log
- we will have "[0, 100] U [150, infinity[" and nobody will notice it, so the
- SQL thread will jump from 100 to 150, and replication will silently break.
-
- When we come to this place in code, relay log may or not be initialized;
- the caller is responsible for setting 'flush_relay_log_cache' accordingly.
- */
- if (flush_relay_log_cache &&
- flush_io_cache(mi->rli.relay_log.get_log_file()))
- DBUG_RETURN(2);
-
- /*
- We flushed the relay log BEFORE the master.info file, because if we crash
- now, we will get a duplicate event in the relay log at restart. If we
- flushed in the other order, we would get a hole in the relay log.
- And duplicate is better than hole (with a duplicate, in later versions we
- can add detection and scrap one event; with a hole there's nothing we can
- do).
- */
-
- /*
- In certain cases this code may create master.info files that seems
- corrupted, because of extra lines filled with garbage in the end
- file (this happens if new contents take less space than previous
- contents of file). But because of number of lines in the first line
- of file we don't care about this garbage.
- */
-
- 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",
- LINES_IN_MASTER_INFO_WITH_SSL,
- 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);
- DBUG_RETURN(-flush_io_cache(file));
-}
-
-
-st_relay_log_info::st_relay_log_info()
- :info_fd(-1), cur_log_fd(-1), save_temporary_tables(0),
- cur_log_old_open_count(0), group_master_log_pos(0), log_space_total(0),
- ignore_log_space_limit(0), last_master_timestamp(0), slave_skip_counter(0),
- abort_pos_wait(0), slave_run_id(0), sql_thd(0), last_slave_errno(0),
- inited(0), abort_slave(0), slave_running(0), until_condition(UNTIL_NONE),
- until_log_pos(0), retried_trans(0)
-{
- group_relay_log_name[0]= event_relay_log_name[0]=
- group_master_log_name[0]= 0;
- last_slave_error[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);
- relay_log.init_pthread_objects();
-}
-
-
-st_relay_log_info::~st_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);
- relay_log.cleanup();
-}
-
-/*
- Waits until the SQL thread reaches (has executed up to) the
- log/position or timed out.
-
- SYNOPSIS
- wait_for_pos()
- thd client thread that sent SELECT MASTER_POS_WAIT
- log_name log name to wait for
- log_pos position to wait for
- timeout timeout in seconds before giving up waiting
-
- NOTES
- timeout is longlong whereas it should be ulong ; but this is
- to catch if the user submitted a negative timeout.
-
- RETURN VALUES
- -2 improper arguments (log_pos<0)
- or slave not running, or master info changed
- during the function's execution,
- or client thread killed. -2 is translated to NULL by caller
- -1 timed out
- >=0 number of log events the function had to wait
- before reaching the desired log/position
- */
-
-int st_relay_log_info::wait_for_pos(THD* thd, String* log_name,
- longlong log_pos,
- longlong timeout)
-{
- if (!inited)
- return -2;
- int event_count = 0;
- ulong init_abort_pos_wait;
- int error=0;
- struct timespec abstime; // for timeout checking
- const char *msg;
- DBUG_ENTER("wait_for_pos");
- DBUG_PRINT("enter",("log_name: '%s' log_pos: %lu timeout: %lu",
- log_name->c_ptr(), (ulong) log_pos, (ulong) timeout));
-
- set_timespec(abstime,timeout);
- pthread_mutex_lock(&data_lock);
- msg= thd->enter_cond(&data_cond, &data_lock,
- "Waiting for the slave SQL thread to "
- "advance position");
- /*
- This function will abort when it notices that some CHANGE MASTER or
- RESET MASTER has changed the master info.
- To catch this, these commands modify abort_pos_wait ; We just monitor
- abort_pos_wait and see if it has changed.
- Why do we have this mechanism instead of simply monitoring slave_running
- in the loop (we do this too), as CHANGE MASTER/RESET SLAVE require that
- the SQL thread be stopped?
- This is becasue if someones does:
- STOP SLAVE;CHANGE MASTER/RESET SLAVE; START SLAVE;
- the change may happen very quickly and we may not notice that
- slave_running briefly switches between 1/0/1.
- */
- init_abort_pos_wait= abort_pos_wait;
-
- /*
- We'll need to
- handle all possible log names comparisons (e.g. 999 vs 1000).
- We use ulong for string->number conversion ; this is no
- stronger limitation than in find_uniq_filename in sql/log.cc
- */
- ulong log_name_extension;
- char log_name_tmp[FN_REFLEN]; //make a char[] from String
-
- strmake(log_name_tmp, log_name->ptr(), min(log_name->length(), FN_REFLEN-1));
-
- char *p= fn_ext(log_name_tmp);
- char *p_end;
- if (!*p || log_pos<0)
- {
- error= -2; //means improper arguments
- goto err;
- }
- // Convert 0-3 to 4
- log_pos= max(log_pos, BIN_LOG_HEADER_SIZE);
- /* p points to '.' */
- log_name_extension= strtoul(++p, &p_end, 10);
- /*
- p_end points to the first invalid character.
- If it equals to p, no digits were found, error.
- If it contains '\0' it means conversion went ok.
- */
- if (p_end==p || *p_end)
- {
- error= -2;
- goto err;
- }
-
- /* The "compare and wait" main loop */
- while (!thd->killed &&
- init_abort_pos_wait == abort_pos_wait &&
- slave_running)
- {
- bool pos_reached;
- int cmp_result= 0;
-
- DBUG_PRINT("info",
- ("init_abort_pos_wait: %ld abort_pos_wait: %ld",
- init_abort_pos_wait, abort_pos_wait));
- DBUG_PRINT("info",("group_master_log_name: '%s' pos: %lu",
- group_master_log_name, (ulong) group_master_log_pos));
-
- /*
- group_master_log_name can be "", if we are just after a fresh
- replication start or after a CHANGE MASTER TO MASTER_HOST/PORT
- (before we have executed one Rotate event from the master) or
- (rare) if the user is doing a weird slave setup (see next
- paragraph). If group_master_log_name is "", we assume we don't
- have enough info to do the comparison yet, so we just wait until
- more data. In this case master_log_pos is always 0 except if
- somebody (wrongly) sets this slave to be a slave of itself
- without using --replicate-same-server-id (an unsupported
- configuration which does nothing), then group_master_log_pos
- will grow and group_master_log_name will stay "".
- */
- if (*group_master_log_name)
- {
- char *basename= (group_master_log_name +
- dirname_length(group_master_log_name));
- /*
- First compare the parts before the extension.
- Find the dot in the master's log basename,
- and protect against user's input error :
- if the names do not match up to '.' included, return error
- */
- char *q= (char*)(fn_ext(basename)+1);
- if (strncmp(basename, log_name_tmp, (int)(q-basename)))
- {
- error= -2;
- break;
- }
- // Now compare extensions.
- char *q_end;
- ulong group_master_log_name_extension= strtoul(q, &q_end, 10);
- if (group_master_log_name_extension < log_name_extension)
- cmp_result= -1 ;
- else
- cmp_result= (group_master_log_name_extension > log_name_extension) ? 1 : 0 ;
-
- pos_reached= ((!cmp_result && group_master_log_pos >= (ulonglong)log_pos) ||
- cmp_result > 0);
- if (pos_reached || thd->killed)
- break;
- }
-
- //wait for master update, with optional timeout.
-
- DBUG_PRINT("info",("Waiting for master update"));
- /*
- We are going to pthread_cond_(timed)wait(); if the SQL thread stops it
- will wake us up.
- */
- if (timeout > 0)
- {
- /*
- Note that pthread_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
- even if its condition is always immediately signaled (case of a loaded
- master).
- */
- error=pthread_cond_timedwait(&data_cond, &data_lock, &abstime);
- }
- else
- pthread_cond_wait(&data_cond, &data_lock);
- DBUG_PRINT("info",("Got signal of master update or timed out"));
- if (error == ETIMEDOUT || error == ETIME)
- {
- error= -1;
- break;
- }
- error=0;
- event_count++;
- DBUG_PRINT("info",("Testing if killed or SQL thread not running"));
- }
-
-err:
- thd->exit_cond(msg);
- DBUG_PRINT("exit",("killed: %d abort: %d slave_running: %d \
-improper_arguments: %d timed_out: %d",
- thd->killed_errno(),
- (int) (init_abort_pos_wait != abort_pos_wait),
- (int) slave_running,
- (int) (error == -2),
- (int) (error == -1)));
- if (thd->killed || init_abort_pos_wait != abort_pos_wait ||
- !slave_running)
- {
- error= -2;
- }
- DBUG_RETURN( error ? error : event_count );
-}
void set_slave_thread_options(THD* thd)
{
+ DBUG_ENTER("set_slave_thread_options");
/*
It's nonsense to constrain the slave threads with max_join_size; if a
query succeeded on master, we HAVE to execute it. So set
@@ -2888,10 +1585,13 @@ void set_slave_thread_options(THD* thd)
options&= ~OPTION_BIN_LOG;
thd->options= options;
thd->variables.completion_type= 0;
+ DBUG_VOID_RETURN;
}
-void set_slave_thread_default_charset(THD* thd, RELAY_LOG_INFO *rli)
+void set_slave_thread_default_charset(THD* thd, Relay_log_info const *rli)
{
+ DBUG_ENTER("set_slave_thread_default_charset");
+
thd->variables.character_set_client=
global_system_variables.character_set_client;
thd->variables.collation_connection=
@@ -2899,7 +1599,15 @@ void set_slave_thread_default_charset(THD* thd, RELAY_LOG_INFO *rli)
thd->variables.collation_server=
global_system_variables.collation_server;
thd->update_charset();
- rli->cached_charset_invalidate();
+
+ /*
+ We use a const cast here since the conceptual (and externally
+ visible) behavior of the function is to set the default charset of
+ the thread. That the cache has to be invalidated is a secondary
+ effect.
+ */
+ const_cast<Relay_log_info*>(rli)->cached_charset_invalidate();
+ DBUG_VOID_RETURN;
}
/*
@@ -2913,7 +1621,7 @@ static int init_slave_thread(THD* thd, SLAVE_THD_TYPE thd_type)
int simulate_error= 0;
#endif
thd->system_thread = (thd_type == SLAVE_THD_SQL) ?
- SYSTEM_THREAD_SLAVE_SQL : SYSTEM_THREAD_SLAVE_IO;
+ SYSTEM_THREAD_SLAVE_SQL : SYSTEM_THREAD_SLAVE_IO;
thd->security_ctx->skip_grants();
my_net_init(&thd->net, 0);
/*
@@ -2924,11 +1632,11 @@ static int init_slave_thread(THD* thd, SLAVE_THD_TYPE thd_type)
thd->variables.max_allowed_packet= global_system_variables.max_allowed_packet
+ MAX_LOG_EVENT_HEADER; /* note, incr over the global not session var */
thd->slave_thread = 1;
+ thd->enable_slow_log= opt_log_slow_slave_statements;
set_slave_thread_options(thd);
thd->client_capabilities = CLIENT_LOCAL_FILES;
- thd->real_id=pthread_self();
pthread_mutex_lock(&LOCK_thread_count);
- thd->thread_id = thread_id++;
+ thd->thread_id= thd->variables.pseudo_thread_id= thread_id++;
pthread_mutex_unlock(&LOCK_thread_count);
DBUG_EXECUTE_IF("simulate_io_slave_error_on_init",
@@ -2944,17 +1652,12 @@ static int init_slave_thread(THD* thd, SLAVE_THD_TYPE thd_type)
thd->cleanup();
DBUG_RETURN(-1);
}
-
-#if !defined(__WIN__) && !defined(OS2) && !defined(__NETWARE__)
- sigset_t set;
- VOID(sigemptyset(&set)); // Get mask in use
- VOID(pthread_sigmask(SIG_UNBLOCK,&set,&thd->block_signals));
-#endif
+ lex_start(thd);
if (thd_type == SLAVE_THD_SQL)
- thd->proc_info= "Waiting for the next event in relay log";
+ thd_proc_info(thd, "Waiting for the next event in relay log");
else
- thd->proc_info= "Waiting for master update";
+ thd_proc_info(thd, "Waiting for master update");
thd->version=refresh_version;
thd->set_time();
DBUG_RETURN(0);
@@ -2962,12 +1665,14 @@ static int init_slave_thread(THD* thd, SLAVE_THD_TYPE thd_type)
static int safe_sleep(THD* thd, int sec, CHECK_KILLED_FUNC thread_killed,
- void* thread_killed_arg)
+ void* thread_killed_arg)
{
int nap_time;
thr_alarm_t alarmed;
+ DBUG_ENTER("safe_sleep");
+
thr_alarm_init(&alarmed);
- time_t start_time= time((time_t*) 0);
+ time_t start_time= my_time(0);
time_t end_time= start_time+sec;
while ((nap_time= (int) (end_time - start_time)) > 0)
@@ -2981,23 +1686,25 @@ static int safe_sleep(THD* thd, int sec, CHECK_KILLED_FUNC thread_killed,
thr_alarm(&alarmed, 2 * nap_time, &alarm_buff);
sleep(nap_time);
thr_end_alarm(&alarmed);
-
+
if ((*thread_killed)(thd,thread_killed_arg))
- return 1;
- start_time=time((time_t*) 0);
+ DBUG_RETURN(1);
+ start_time= my_time(0);
}
- return 0;
+ DBUG_RETURN(0);
}
-static int request_dump(MYSQL* mysql, MASTER_INFO* mi,
- bool *suppress_warnings)
+static int request_dump(MYSQL* mysql, Master_info* mi,
+ bool *suppress_warnings)
{
- char buf[FN_REFLEN + 10];
+ uchar buf[FN_REFLEN + 10];
int len;
int binlog_flags = 0; // for now
char* logname = mi->master_log_name;
DBUG_ENTER("request_dump");
+
+ *suppress_warnings= FALSE;
// TODO if big log files: Change next to int8store()
int4store(buf, (ulong) mi->master_log_pos);
@@ -3013,11 +1720,11 @@ static int request_dump(MYSQL* mysql, MASTER_INFO* mi,
now we just fill up the error log :-)
*/
if (mysql_errno(mysql) == ER_NET_READ_INTERRUPTED)
- *suppress_warnings= 1; // Suppress reconnect warning
+ *suppress_warnings= TRUE; // Suppress reconnect warning
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);
+ mysql_errno(mysql), mysql_error(mysql),
+ master_connect_retry);
DBUG_RETURN(1);
}
@@ -3027,82 +1734,82 @@ static int request_dump(MYSQL* mysql, MASTER_INFO* mi,
static int request_table_dump(MYSQL* mysql, const char* db, const char* table)
{
- char buf[1024];
- char * p = buf;
+ 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");
- return 1;
- }
-
+ 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, (uint) (p - buf + table_len), 1))
+
+ 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");
- return 1;
+ DBUG_RETURN(1);
}
- return 0;
+ DBUG_RETURN(0);
}
/*
Read one event from the master
-
+
SYNOPSIS
read_event()
- mysql MySQL connection
- mi Master connection information
- suppress_warnings TRUE when a normal net read timeout has caused us to
- try a reconnect. We do not want to print anything to
- the error log in this case because this a anormal
- event in an idle server.
+ mysql MySQL connection
+ mi Master connection information
+ suppress_warnings TRUE when a normal net read timeout has caused us to
+ try a reconnect. We do not want to print anything to
+ the error log in this case because this a anormal
+ event in an idle server.
RETURN VALUES
- 'packet_error' Error
- number Length of packet
+ 'packet_error' Error
+ number Length of packet
*/
-static ulong read_event(MYSQL* mysql, MASTER_INFO *mi, bool* suppress_warnings)
+static ulong read_event(MYSQL* mysql, Master_info *mi, bool* suppress_warnings)
{
ulong len;
+ DBUG_ENTER("read_event");
- *suppress_warnings= 0;
+ *suppress_warnings= FALSE;
/*
my_real_read() will time us out
We check if we were told to die, and if not, try reading again
-
- TODO: Move 'events_till_disconnect' to the MASTER_INFO structure
*/
#ifndef DBUG_OFF
- if (disconnect_slave_event_count && !(events_till_disconnect--))
- return packet_error;
+ if (disconnect_slave_event_count && !(mi->events_till_disconnect--))
+ DBUG_RETURN(packet_error);
#endif
-
+
len = cli_safe_read(mysql);
if (len == packet_error || (long) len < 1)
{
if (mysql_errno(mysql) == ER_NET_READ_INTERRUPTED)
{
/*
- We are trying a normal reconnect after a read timeout;
- we suppress prints to .err file as long as the reconnect
- happens without problems
+ We are trying a normal reconnect after a read timeout;
+ we suppress prints to .err file as long as the reconnect
+ happens without problems
*/
*suppress_warnings= TRUE;
}
else
sql_print_error("Error reading packet from server: %s ( server_errno=%d)",
- mysql_error(mysql), mysql_errno(mysql));
- return packet_error;
+ mysql_error(mysql), mysql_errno(mysql));
+ DBUG_RETURN(packet_error);
}
/* Check if eof packet */
@@ -3110,146 +1817,274 @@ static ulong read_event(MYSQL* mysql, MASTER_INFO *mi, bool* suppress_warnings)
{
sql_print_information("Slave: received end packet from server, apparent "
"master shutdown: %s",
- mysql_error(mysql));
- return packet_error;
+ mysql_error(mysql));
+ DBUG_RETURN(packet_error);
}
-
- DBUG_PRINT("info",( "len: %lu net->read_pos[4]: %d\n",
- len, mysql->net.read_pos[4]));
- return len - 1;
+
+ DBUG_PRINT("exit", ("len: %lu net->read_pos[4]: %d",
+ len, mysql->net.read_pos[4]));
+ DBUG_RETURN(len - 1);
}
-int check_expected_error(THD* thd, RELAY_LOG_INFO* rli, int expected_error)
+int check_expected_error(THD* thd, Relay_log_info const *rli,
+ int expected_error)
{
+ DBUG_ENTER("check_expected_error");
+
switch (expected_error) {
case ER_NET_READ_ERROR:
- case ER_NET_ERROR_ON_WRITE:
+ case ER_NET_ERROR_ON_WRITE:
case ER_QUERY_INTERRUPTED:
- case ER_SERVER_SHUTDOWN:
+ case ER_SERVER_SHUTDOWN:
case ER_NEW_ABORTING_CONNECTION:
- return 1;
+ DBUG_RETURN(1);
default:
- return 0;
+ DBUG_RETURN(0);
}
}
+
/*
- Check if condition stated in UNTIL clause of START SLAVE is reached.
- SYNOPSYS
- st_relay_log_info::is_until_satisfied()
- master_beg_pos position of the beginning of to be executed event
- (not log_pos member of the event that points to the
- beginning of the following event)
-
-
- DESCRIPTION
- Checks if UNTIL condition is reached. Uses caching result of last
- comparison of current log file name and target log file name. So cached
- value should be invalidated if current log file name changes
- (see st_relay_log_info::notify_... functions).
-
- This caching is needed to avoid of expensive string comparisons and
- strtol() conversions needed for log names comparison. We don't need to
- compare them each time this function is called, we only need to do this
- when current log name changes. If we have UNTIL_MASTER_POS condition we
- need to do this only after Rotate_log_event::exec_event() (which is
- rare, so caching gives real benifit), and if we have UNTIL_RELAY_POS
- condition then we should invalidate cached comarison value after
- inc_group_relay_log_pos() which called for each group of events (so we
- have some benefit if we have something like queries that use
- autoincrement or if we have transactions).
-
- Should be called ONLY if until_condition != UNTIL_NONE !
- RETURN VALUE
- true - condition met or error happened (condition seems to have
- bad log file name)
- false - condition not met
+ Check if the current error is of temporary nature of not.
+ Some errors are temporary in nature, such as
+ ER_LOCK_DEADLOCK and ER_LOCK_WAIT_TIMEOUT. Ndb also signals
+ that the error is temporary by pushing a warning with the error code
+ ER_GET_TEMPORARY_ERRMSG, if the originating error is temporary.
*/
+static int has_temporary_error(THD *thd)
+{
+ DBUG_ENTER("has_temporary_error");
+
+ if (thd->is_fatal_error)
+ DBUG_RETURN(0);
+
+ DBUG_EXECUTE_IF("all_errors_are_temporary_errors",
+ if (thd->main_da.is_error())
+ {
+ thd->clear_error();
+ my_error(ER_LOCK_DEADLOCK, MYF(0));
+ });
+
+ /*
+ If there is no message in THD, we can't say if it's a temporary
+ error or not. This is currently the case for Incident_log_event,
+ which sets no message. Return FALSE.
+ */
+ if (!thd->is_error())
+ DBUG_RETURN(0);
+
+ /*
+ Temporary error codes:
+ currently, InnoDB deadlock detected by InnoDB or lock
+ wait timeout (innodb_lock_wait_timeout exceeded
+ */
+ if (thd->main_da.sql_errno() == ER_LOCK_DEADLOCK ||
+ thd->main_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);
+ MYSQL_ERROR *err;
+ while ((err= it++))
+ {
+ DBUG_PRINT("info", ("has warning %d %s", err->code, err->msg));
+ switch (err->code)
+ {
+ case ER_GET_TEMPORARY_ERRMSG:
+ DBUG_RETURN(1);
+ default:
+ break;
+ }
+ }
+#endif
+ DBUG_RETURN(0);
+}
+
+
+/**
+ Applies the given event and advances the relay log position.
+
+ In essence, this function does:
+
+ @code
+ ev->apply_event(rli);
+ ev->update_pos(rli);
+ @endcode
-bool st_relay_log_info::is_until_satisfied(my_off_t master_beg_pos)
+ But it also does some maintainance, such as skipping events if
+ needed and reporting errors.
+
+ If the @c skip flag is set, then it is tested whether the event
+ should be skipped, by looking at the slave_skip_counter and the
+ server id. The skip flag should be set when calling this from a
+ replication thread but not set when executing an explicit BINLOG
+ statement.
+
+ @retval 0 OK.
+
+ @retval 1 Error calling ev->apply_event().
+
+ @retval 2 No error calling ev->apply_event(), but error calling
+ ev->update_pos().
+*/
+int apply_event_and_update_pos(Log_event* ev, THD* thd, Relay_log_info* rli,
+ bool skip)
{
- const char *log_name;
- ulonglong log_pos;
+ int exec_res= 0;
- DBUG_ASSERT(until_condition != UNTIL_NONE);
-
- if (until_condition == UNTIL_MASTER_POS)
+ DBUG_ENTER("apply_event_and_update_pos");
+
+ DBUG_PRINT("exec_event",("%s(type_code: %d; server_id: %d)",
+ ev->get_type_str(), ev->get_type_code(),
+ ev->server_id));
+ DBUG_PRINT("info", ("thd->options: %s%s; rli->last_event_start_time: %lu",
+ FLAGSTR(thd->options, OPTION_NOT_AUTOCOMMIT),
+ FLAGSTR(thd->options, OPTION_BEGIN),
+ rli->last_event_start_time));
+
+ /*
+ Execute the event to change the database and update the binary
+ log coordinates, but first we set some data that is needed for
+ the thread.
+
+ The event will be executed unless it is supposed to be skipped.
+
+ Queries originating from this server must be skipped. Low-level
+ events (Format_description_log_event, Rotate_log_event,
+ Stop_log_event) from this server must also be skipped. But for
+ those we don't want to modify 'group_master_log_pos', because
+ these events did not exist on the master.
+ Format_description_log_event is not completely skipped.
+
+ Skip queries specified by the user in 'slave_skip_counter'. We
+ can't however skip events that has something to do with the log
+ files themselves.
+
+ Filtering on own server id is extremely important, to ignore
+ execution of events created by the creation/rotation of the relay
+ log (remember that now the relay log starts with its Format_desc,
+ has a Rotate etc).
+ */
+
+ thd->server_id = ev->server_id; // use the original server id for logging
+ thd->set_time(); // time the query
+ thd->lex->current_select= 0;
+ if (!ev->when)
+ ev->when= my_time(0);
+ ev->thd = thd; // because up to this point, ev->thd == 0
+
+ if (skip)
{
- log_name= group_master_log_name;
- log_pos= master_beg_pos;
+ int reason= ev->shall_skip(rli);
+ if (reason == Log_event::EVENT_SKIP_COUNT)
+ --rli->slave_skip_counter;
+ pthread_mutex_unlock(&rli->data_lock);
+ if (reason == Log_event::EVENT_SKIP_NOT)
+ exec_res= ev->apply_event(rli);
+#ifndef DBUG_OFF
+ /*
+ This only prints information to the debug trace.
+
+ TODO: Print an informational message to the error log?
+ */
+ static const char *const explain[] = {
+ // EVENT_SKIP_NOT,
+ "not skipped",
+ // EVENT_SKIP_IGNORE,
+ "skipped because event should be ignored",
+ // EVENT_SKIP_COUNT
+ "skipped because event skip counter was non-zero"
+ };
+ DBUG_PRINT("info", ("OPTION_BEGIN: %d; IN_STMT: %d",
+ thd->options & OPTION_BEGIN ? 1 : 0,
+ rli->get_flag(Relay_log_info::IN_STMT)));
+ DBUG_PRINT("skip_event", ("%s event was %s",
+ ev->get_type_str(), explain[reason]));
+#endif
}
else
- { /* until_condition == UNTIL_RELAY_POS */
- log_name= group_relay_log_name;
- log_pos= group_relay_log_pos;
- }
-
- if (until_log_names_cmp_result == UNTIL_LOG_NAMES_CMP_UNKNOWN)
+ exec_res= ev->apply_event(rli);
+
+ DBUG_PRINT("info", ("apply_event error = %d", exec_res));
+ if (exec_res == 0)
{
+ int error= ev->update_pos(rli);
+#ifdef HAVE_purify
+ if (!rli->is_fake)
+#endif
+ {
+#ifndef DBUG_OFF
+ char buf[22];
+#endif
+ DBUG_PRINT("info", ("update_pos error = %d", error));
+ DBUG_PRINT("info", ("group %s %s",
+ llstr(rli->group_relay_log_pos, buf),
+ rli->group_relay_log_name));
+ DBUG_PRINT("info", ("event %s %s",
+ llstr(rli->event_relay_log_pos, buf),
+ rli->event_relay_log_name));
+ }
/*
- We have no cached comparison results so we should compare log names
- and cache result.
- If we are after RESET SLAVE, and the SQL slave thread has not processed
- any event yet, it could be that group_master_log_name is "". In that case,
- just wait for more events (as there is no sensible comparison to do).
- */
+ The update should not fail, so print an error message and
+ return an error code.
- if (*log_name)
+ TODO: Replace this with a decent error message when merged
+ with BUG#24954 (which adds several new error message).
+ */
+ if (error)
{
- const char *basename= log_name + dirname_length(log_name);
-
- const char *q= (const char*)(fn_ext(basename)+1);
- if (strncmp(basename, until_log_name, (int)(q-basename)) == 0)
- {
- /* Now compare extensions. */
- char *q_end;
- ulong log_name_extension= strtoul(q, &q_end, 10);
- if (log_name_extension < until_log_name_extension)
- until_log_names_cmp_result= UNTIL_LOG_NAMES_CMP_LESS;
- else
- until_log_names_cmp_result=
- (log_name_extension > until_log_name_extension) ?
- UNTIL_LOG_NAMES_CMP_GREATER : UNTIL_LOG_NAMES_CMP_EQUAL ;
- }
- else
- {
- /* Probably error so we aborting */
- sql_print_error("Slave SQL thread is stopped because UNTIL "
- "condition is bad.");
- return TRUE;
- }
+ char buf[22];
+ rli->report(ERROR_LEVEL, ER_UNKNOWN_ERROR,
+ "It was not possible to update the positions"
+ " of the relay log information: the slave may"
+ " be in an inconsistent state."
+ " Stopped in %s position %s",
+ rli->group_relay_log_name,
+ llstr(rli->group_relay_log_pos, buf));
+ DBUG_RETURN(2);
}
- else
- return until_log_pos == 0;
}
-
- return ((until_log_names_cmp_result == UNTIL_LOG_NAMES_CMP_EQUAL &&
- log_pos >= until_log_pos) ||
- until_log_names_cmp_result == UNTIL_LOG_NAMES_CMP_GREATER);
+
+ DBUG_RETURN(exec_res ? 1 : 0);
}
-void st_relay_log_info::cached_charset_invalidate()
-{
- /* Full of zeroes means uninitialized. */
- bzero(cached_charset, sizeof(cached_charset));
-}
+/**
+ Top-level function for executing the next event from the relay log.
+ This function reads the event from the relay log, executes it, and
+ advances the relay log position. It also handles errors, etc.
-bool st_relay_log_info::cached_charset_compare(char *charset)
-{
- if (bcmp(cached_charset, charset, sizeof(cached_charset)))
- {
- memcpy(cached_charset, charset, sizeof(cached_charset));
- return 1;
- }
- return 0;
-}
+ This function may fail to apply the event for the following reasons:
+
+ - The position specfied by the UNTIL condition of the START SLAVE
+ command is reached.
+
+ - It was not possible to read the event from the log.
+
+ - The slave is killed.
+
+ - An error occurred when applying the event, and the event has been
+ tried slave_trans_retries times. If the event has been retried
+ fewer times, 0 is returned.
+
+ - init_master_info or init_relay_log_pos failed. (These are called
+ if a failure occurs when applying the event.)
+
+ - An error occurred when updating the binlog position.
+ @retval 0 The event was applied.
-static int exec_relay_log_event(THD* thd, RELAY_LOG_INFO* rli)
+ @retval 1 The event was not applied.
+*/
+static int exec_relay_log_event(THD* thd, Relay_log_info* rli)
{
+ DBUG_ENTER("exec_relay_log_event");
+
/*
We acquire this mutex since we need it for all operations except
event execution. But we will release it in places where we will
@@ -3265,19 +2100,18 @@ static int exec_relay_log_event(THD* thd, RELAY_LOG_INFO* rli)
{
pthread_mutex_unlock(&rli->data_lock);
delete ev;
- return 1;
+ DBUG_RETURN(1);
}
if (ev)
{
- int type_code = ev->get_type_code();
int exec_res;
/*
This tests if the position of the beginning of the current event
hits the UNTIL barrier.
*/
- if (rli->until_condition != RELAY_LOG_INFO::UNTIL_NONE &&
- rli->is_until_satisfied((thd->options & OPTION_BEGIN || !ev->log_pos) ?
+ if (rli->until_condition != Relay_log_info::UNTIL_NONE &&
+ rli->is_until_satisfied((rli->is_in_group() || !ev->log_pos) ?
rli->group_master_log_pos :
ev->log_pos - ev->data_written))
{
@@ -3291,157 +2125,40 @@ static int exec_relay_log_event(THD* thd, RELAY_LOG_INFO* rli)
rli->abort_slave= 1;
pthread_mutex_unlock(&rli->data_lock);
delete ev;
- return 1;
- }
- /*
- Queries originating from this server must be skipped.
- Low-level events (Format_desc, Rotate, Stop) from this server
- must also be skipped. But for those we don't want to modify
- group_master_log_pos, because these events did not exist on the master.
- Format_desc is not completely skipped.
- Skip queries specified by the user in slave_skip_counter.
- We can't however skip events that has something to do with the
- log files themselves.
- Filtering on own server id is extremely important, to ignore execution of
- events created by the creation/rotation of the relay log (remember that
- now the relay log starts with its Format_desc, has a Rotate etc).
- */
-
- DBUG_PRINT("info",("type_code: %d; server_id: %d; slave_skip_counter: %d",
- type_code, ev->server_id, rli->slave_skip_counter));
-
- /*
- If the slave skip counter is positive, we still need to set the
- OPTION_BEGIN flag correctly and not skip the log events that
- start or end a transaction. If we do this, the slave will not
- notice that it is inside a transaction, and happily start
- executing from inside the transaction.
-
- Note that the code block below is strictly 5.0.
- */
-#if MYSQL_VERSION_ID < 50100
- if (unlikely(rli->slave_skip_counter > 0))
- {
- switch (type_code)
- {
- case QUERY_EVENT:
- {
- Query_log_event* const qev= (Query_log_event*) ev;
- DBUG_PRINT("info", ("QUERY_EVENT { query: '%s', q_len: %u }",
- qev->query, qev->q_len));
- if (memcmp("BEGIN", qev->query, qev->q_len+1) == 0)
- thd->options|= OPTION_BEGIN;
- else if (memcmp("COMMIT", qev->query, qev->q_len+1) == 0 ||
- memcmp("ROLLBACK", qev->query, qev->q_len+1) == 0)
- thd->options&= ~OPTION_BEGIN;
- }
- break;
-
- case XID_EVENT:
- DBUG_PRINT("info", ("XID_EVENT"));
- thd->options&= ~OPTION_BEGIN;
- break;
- }
- }
-#endif
-
- if ((ev->server_id == (uint32) ::server_id &&
- !replicate_same_server_id &&
- type_code != FORMAT_DESCRIPTION_EVENT) ||
- (rli->slave_skip_counter &&
- type_code != ROTATE_EVENT && type_code != STOP_EVENT &&
- type_code != START_EVENT_V3 && type_code!= FORMAT_DESCRIPTION_EVENT))
- {
- DBUG_PRINT("info", ("event skipped"));
- if (thd->options & OPTION_BEGIN)
- rli->inc_event_relay_log_pos();
- else
- {
- rli->inc_group_relay_log_pos((type_code == ROTATE_EVENT ||
- type_code == STOP_EVENT ||
- type_code == FORMAT_DESCRIPTION_EVENT) ?
- LL(0) : ev->log_pos,
- 1/* skip lock*/);
- flush_relay_log_info(rli);
- }
-
- DBUG_PRINT("info", ("thd->options: %s",
- (thd->options & OPTION_BEGIN) ? "OPTION_BEGIN" : ""));
-
- /*
- Protect against common user error of setting the counter to 1
- instead of 2 while recovering from an insert which used auto_increment,
- rand or user var.
- */
- if (rli->slave_skip_counter &&
- !((type_code == INTVAR_EVENT ||
- type_code == RAND_EVENT ||
- type_code == USER_VAR_EVENT ||
- type_code == BEGIN_LOAD_QUERY_EVENT ||
- type_code == APPEND_BLOCK_EVENT ||
- type_code == CREATE_FILE_EVENT) &&
- rli->slave_skip_counter == 1) &&
-#if MYSQL_VERSION_ID < 50100
- /*
- Decrease the slave skip counter only if we are not inside
- a transaction or the slave skip counter is more than
- 1. The slave skip counter will be decreased from 1 to 0
- when reaching the final ROLLBACK, COMMIT, or XID_EVENT.
- */
- (!(thd->options & OPTION_BEGIN) || rli->slave_skip_counter > 1) &&
-#endif
- /*
- The events from ourselves which have something to do with the relay
- log itself must be skipped, true, but they mustn't decrement
- rli->slave_skip_counter, because the user is supposed to not see
- these events (they are not in the master's binlog) and if we
- decremented, START SLAVE would for example decrement when it sees
- the Rotate, so the event which the user probably wanted to skip
- would not be skipped.
- */
- !(ev->server_id == (uint32) ::server_id &&
- (type_code == ROTATE_EVENT ||
- type_code == STOP_EVENT ||
- type_code == START_EVENT_V3 ||
- type_code == FORMAT_DESCRIPTION_EVENT)))
- --rli->slave_skip_counter;
- pthread_mutex_unlock(&rli->data_lock);
- delete ev;
- return 0; // avoid infinite update loops
+ DBUG_RETURN(1);
}
- pthread_mutex_unlock(&rli->data_lock);
+ exec_res= apply_event_and_update_pos(ev, thd, rli, TRUE);
- thd->server_id = ev->server_id; // use the original server id for logging
- thd->set_time(); // time the query
- thd->lex->current_select= 0;
- if (!ev->when)
- ev->when = time(NULL);
- ev->thd = thd;
- exec_res = ev->exec_event(rli);
- DBUG_ASSERT(rli->sql_thd==thd);
/*
- Format_description_log_event should not be deleted because it will be
- used to read info about the relay log's format; it will be deleted when
- the SQL thread does not need it, i.e. when this thread terminates.
+ Format_description_log_event should not be deleted because it will be
+ used to read info about the relay log's format; it will be deleted when
+ the SQL thread does not need it, i.e. when this thread terminates.
*/
if (ev->get_type_code() != FORMAT_DESCRIPTION_EVENT)
{
DBUG_PRINT("info", ("Deleting the event after it has been executed"));
delete ev;
}
+
+ /*
+ update_log_pos failed: this should not happen, so we don't
+ retry.
+ */
+ if (exec_res == 2)
+ DBUG_RETURN(1);
+
if (slave_trans_retries)
{
- if (exec_res &&
- (thd->net.last_errno == ER_LOCK_DEADLOCK ||
- thd->net.last_errno == ER_LOCK_WAIT_TIMEOUT) &&
- !thd->is_fatal_error)
+ int temp_err;
+ if (exec_res && (temp_err= has_temporary_error(thd)))
{
const char *errmsg;
/*
We were in a transaction which has been rolled back because of a
- Sonera deadlock. if lock wait timeout (innodb_lock_wait_timeout exceeded)
+ temporary error;
+ let's seek back to BEGIN log event and retry it all again.
+ Note, if lock wait timeout (innodb_lock_wait_timeout exceeded)
there is no rollback since 5.0.13 (ref: manual).
- let's seek back to BEGIN log event and retry it all again.
We have to not only seek but also
a) init_master_info(), to seek back to hot relay log's start for later
(for when we will come back to this hot log after re-processing the
@@ -3463,17 +2180,17 @@ static int exec_relay_log_event(THD* thd, RELAY_LOG_INFO* rli)
else
{
exec_res= 0;
- end_trans(thd, ROLLBACK);
- /* chance for concurrent connection to get more locks */
+ end_trans(thd, ROLLBACK);
+ /* 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);
+ (CHECK_KILLED_FUNC)sql_slave_killed, (void*)rli);
pthread_mutex_lock(&rli->data_lock); // because of SHOW STATUS
- rli->trans_retries++;
+ rli->trans_retries++;
rli->retried_trans++;
pthread_mutex_unlock(&rli->data_lock);
DBUG_PRINT("info", ("Slave retries transaction "
"rli->trans_retries: %lu", rli->trans_retries));
- }
+ }
}
else
sql_print_error("Slave SQL thread retried transaction %lu time(s) "
@@ -3481,23 +2198,26 @@ static int exec_relay_log_event(THD* thd, RELAY_LOG_INFO* rli)
"the slave_transaction_retries variable.",
slave_trans_retries);
}
- else if (!((thd->options & OPTION_BEGIN) && opt_using_transactions))
+ else if (exec_res && !temp_err ||
+ (opt_using_transactions &&
+ rli->group_relay_log_pos == rli->event_relay_log_pos))
{
/*
- Only reset the retry counter if the event succeeded or
- failed with a non-transient error. On a successful event,
- the execution will proceed as usual; in the case of a
+ Only reset the retry counter if the entire group succeeded
+ or failed with a non-transient error. On a successful
+ event, the execution will proceed as usual; in the case of a
non-transient error, the slave will stop with an error.
- */
+ */
rli->trans_retries= 0; // restart from fresh
+ DBUG_PRINT("info", ("Resetting retry counter, rli->trans_retries: %lu",
+ rli->trans_retries));
}
}
- return exec_res;
+ DBUG_RETURN(exec_res);
}
- else
- {
- pthread_mutex_unlock(&rli->data_lock);
- slave_print_error(rli, 0, "\
+ pthread_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 \
binary log is corrupted (you can check this by running 'mysqlbinlog' on the \
binary log), the slave's relay log is corrupted (you can check this by running \
@@ -3506,29 +2226,122 @@ or slave's MySQL code. If you want to check the master's binary log or slave's \
relay log, you will be able to know their names by issuing 'SHOW SLAVE STATUS' \
on this slave.\
");
+ DBUG_RETURN(1);
+}
+
+
+static bool check_io_slave_killed(THD *thd, Master_info *mi, const char *info)
+{
+ if (io_slave_killed(thd, mi))
+ {
+ if (info && global_system_variables.log_warnings)
+ sql_print_information(info);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+
+/**
+ @brief Try to reconnect slave IO thread.
+
+ @details Terminates current connection to master, sleeps for
+ @c mi->connect_retry msecs and initiates new connection with
+ @c safe_reconnect(). Variable pointed by @c retry_count is increased -
+ if it exceeds @c master_retry_count then connection is not re-established
+ and function signals error.
+ Unless @c suppres_warnings is TRUE, a warning is put in the server error log
+ when reconnecting. The warning message and messages used to report errors
+ are taken from @c messages array. In case @c master_retry_count is exceeded,
+ no messages are added to the log.
+
+ @param[in] thd Thread context.
+ @param[in] mysql MySQL connection.
+ @param[in] mi Master connection information.
+ @param[in,out] retry_count Number of attempts to reconnect.
+ @param[in] suppress_warnings TRUE when a normal net read timeout
+ has caused to reconnecting.
+ @param[in] messages Messages to print/log, see
+ reconnect_messages[] array.
+
+ @retval 0 OK.
+ @retval 1 There was an error.
+*/
+
+static int try_to_reconnect(THD *thd, MYSQL *mysql, Master_info *mi,
+ uint *retry_count, bool suppress_warnings,
+ const char *messages[SLAVE_RECON_MSG_MAX])
+{
+ mi->slave_running= MYSQL_SLAVE_RUN_NOT_CONNECT;
+ thd->proc_info= messages[SLAVE_RECON_MSG_WAIT];
+#ifdef SIGNAL_WITH_VIO_CLOSE
+ thd->clear_active_vio();
+#endif
+ end_server(mysql);
+ if ((*retry_count)++)
+ {
+ 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);
+ }
+ if (check_io_slave_killed(thd, mi, messages[SLAVE_RECON_MSG_KILLED_WAITING]))
+ return 1;
+ thd->proc_info = messages[SLAVE_RECON_MSG_AFTER];
+ if (!suppress_warnings)
+ {
+ char buf[256], llbuff[22];
+ my_snprintf(buf, sizeof(buf), messages[SLAVE_RECON_MSG_FAILED],
+ IO_RPL_LOG_NAME, llstr(mi->master_log_pos, llbuff));
+ /*
+ Raise a warining during registering on master/requesting dump.
+ Log a message reading event.
+ */
+ if (messages[SLAVE_RECON_MSG_COMMAND][0])
+ {
+ mi->report(WARNING_LEVEL, ER_SLAVE_MASTER_COM_FAILURE,
+ ER(ER_SLAVE_MASTER_COM_FAILURE),
+ messages[SLAVE_RECON_MSG_COMMAND], buf);
+ }
+ else
+ {
+ sql_print_information(buf);
+ }
+ }
+ if (safe_reconnect(thd, mysql, mi, 1) || io_slave_killed(thd, mi))
+ {
+ if (global_system_variables.log_warnings)
+ sql_print_information(messages[SLAVE_RECON_MSG_KILLED_AFTER]);
return 1;
}
+ return 0;
}
-/* Slave I/O Thread entry point */
+/**
+ Slave IO thread entry point.
+
+ @param arg Pointer to Master_info struct that holds information for
+ the IO thread.
+ @return Always 0.
+*/
pthread_handler_t handle_slave_io(void *arg)
{
THD *thd; // needs to be first for thread_stack
MYSQL *mysql;
- MASTER_INFO *mi = (MASTER_INFO*)arg;
- RELAY_LOG_INFO *rli= &mi->rli;
+ Master_info *mi = (Master_info*)arg;
+ Relay_log_info *rli= &mi->rli;
char llbuff[22];
uint retry_count;
-
+ bool suppress_warnings;
+#ifndef DBUG_OFF
+ uint retry_count_reg= 0, retry_count_dump= 0, retry_count_event= 0;
+#endif
// needs to call my_thread_init(), otherwise we get a coredump in DBUG_ stuff
my_thread_init();
DBUG_ENTER("handle_slave_io");
-#ifndef DBUG_OFF
-slave_begin:
-#endif
DBUG_ASSERT(mi->inited);
mysql= NULL ;
retry_count= 0;
@@ -3538,7 +2351,7 @@ slave_begin:
mi->slave_run_id++;
#ifndef DBUG_OFF
- mi->events_till_abort = abort_slave_event_count;
+ mi->events_till_disconnect = disconnect_slave_event_count;
#endif
thd= new THD; // note that contructor of THD uses DBUG_ !
@@ -3563,22 +2376,23 @@ slave_begin:
pthread_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)));
+ mi->master_log_name,
+ llstr(mi->master_log_pos,llbuff)));
if (!(mi->mysql = mysql = mysql_init(NULL)))
{
- sql_print_error("Slave I/O thread: error in mysql_init()");
+ mi->report(ERROR_LEVEL, ER_SLAVE_FATAL_ERROR,
+ ER(ER_SLAVE_FATAL_ERROR), "error in mysql_init()");
goto err;
}
- thd->proc_info = "Connecting to master";
+ thd_proc_info(thd, "Connecting to master");
// we can get killed during safe_connect
if (!safe_connect(thd, mysql, mi))
{
- sql_print_information("Slave I/O thread: connected to master '%s@%s:%d',\
- replication started in log '%s' at position %s", mi->user,
- mi->host, mi->port,
+ sql_print_information("Slave I/O thread: connected to master '%s@%s:%d',"
+ "replication started in log '%s' at position %s",
+ mi->user, mi->host, mi->port,
IO_RPL_LOG_NAME,
llstr(mi->master_log_pos,llbuff));
/*
@@ -3599,7 +2413,7 @@ connected:
// TODO: the assignment below should be under mutex (5.0)
mi->slave_running= MYSQL_SLAVE_RUN_CONNECT;
thd->slave_net = &mysql->net;
- thd->proc_info = "Checking master version";
+ thd_proc_info(thd, "Checking master version");
if (get_master_version_and_clock(mysql, mi))
goto err;
@@ -3607,89 +2421,83 @@ connected:
{
/*
Register ourselves with the master.
- If fails, this is not fatal - we just print the error message and go
- on with life.
*/
- thd->proc_info = "Registering slave on master";
- if (register_slave_on_master(mysql))
- goto err;
+ thd_proc_info(thd, "Registering slave on master");
+ if (register_slave_on_master(mysql, mi, &suppress_warnings))
+ {
+ if (!check_io_slave_killed(thd, mi, "Slave I/O thread killed "
+ "while registering slave on master"))
+ {
+ sql_print_error("Slave I/O thread couldn't register on master");
+ if (try_to_reconnect(thd, mysql, mi, &retry_count, suppress_warnings,
+ reconnect_messages[SLAVE_RECON_ACT_REG]))
+ goto err;
+ }
+ else
+ goto err;
+ goto connected;
+ }
+ DBUG_EXECUTE_IF("FORCE_SLAVE_TO_RECONNECT_REG",
+ if (!retry_count_reg)
+ {
+ retry_count_reg++;
+ sql_print_information("Forcing to reconnect slave I/O thread");
+ if (try_to_reconnect(thd, mysql, mi, &retry_count, suppress_warnings,
+ reconnect_messages[SLAVE_RECON_ACT_REG]))
+ goto err;
+ goto connected;
+ });
}
DBUG_PRINT("info",("Starting reading binary log from master"));
while (!io_slave_killed(thd,mi))
{
- bool suppress_warnings= 0;
- thd->proc_info = "Requesting binlog dump";
+ thd_proc_info(thd, "Requesting binlog dump");
if (request_dump(mysql, mi, &suppress_warnings))
{
sql_print_error("Failed on request_dump()");
- if (io_slave_killed(thd,mi))
- {
- sql_print_information("Slave I/O thread killed while requesting master \
-dump");
- goto err;
- }
-
- mi->slave_running= MYSQL_SLAVE_RUN_NOT_CONNECT;
- thd->proc_info= "Waiting to reconnect after a failed binlog dump request";
-#ifdef SIGNAL_WITH_VIO_CLOSE
- thd->clear_active_vio();
-#endif
- end_server(mysql);
- /*
- First time retry immediately, assuming that we can recover
- right away - if first time fails, sleep between re-tries
- hopefuly the admin can fix the problem sometime
- */
- if (retry_count++)
- {
- if (retry_count > master_retry_count)
- goto err; // Don't retry forever
- safe_sleep(thd,mi->connect_retry,(CHECK_KILLED_FUNC)io_slave_killed,
- (void*)mi);
- }
- if (io_slave_killed(thd,mi))
- {
- sql_print_information("Slave I/O thread killed while retrying master \
-dump");
- goto err;
- }
-
- thd->proc_info = "Reconnecting after a failed binlog dump request";
- if (!suppress_warnings)
- sql_print_error("Slave I/O thread: failed dump request, \
-reconnecting to try again, log '%s' at postion %s", IO_RPL_LOG_NAME,
- llstr(mi->master_log_pos,llbuff));
- if (safe_reconnect(thd, mysql, mi, suppress_warnings) ||
- io_slave_killed(thd,mi))
- {
- sql_print_information("Slave I/O thread killed during or \
-after reconnect");
- goto err;
- }
-
+ if (check_io_slave_killed(thd, mi, "Slave I/O thread killed while \
+requesting master dump") ||
+ try_to_reconnect(thd, mysql, mi, &retry_count, suppress_warnings,
+ reconnect_messages[SLAVE_RECON_ACT_DUMP]))
+ goto err;
goto connected;
}
+ DBUG_EXECUTE_IF("FORCE_SLAVE_TO_RECONNECT_DUMP",
+ if (!retry_count_dump)
+ {
+ retry_count_dump++;
+ sql_print_information("Forcing to reconnect slave I/O thread");
+ if (try_to_reconnect(thd, mysql, mi, &retry_count, suppress_warnings,
+ reconnect_messages[SLAVE_RECON_ACT_DUMP]))
+ goto err;
+ goto connected;
+ });
while (!io_slave_killed(thd,mi))
{
ulong event_len;
-
- suppress_warnings= 0;
/*
We say "waiting" because read_event() will wait if there's nothing to
read. But if there's something to read, it will not wait. The
important thing is to not confuse users by saying "reading" whereas
we're in fact receiving nothing.
*/
- thd->proc_info= "Waiting for master to send event";
+ thd_proc_info(thd, "Waiting for master to send event");
event_len= read_event(mysql, mi, &suppress_warnings);
- if (io_slave_killed(thd,mi))
- {
- if (global_system_variables.log_warnings)
- sql_print_information("Slave I/O thread killed while reading event");
- goto err;
- }
+ if (check_io_slave_killed(thd, mi, "Slave I/O thread killed while \
+reading event"))
+ goto err;
+ DBUG_EXECUTE_IF("FORCE_SLAVE_TO_RECONNECT_EVENT",
+ if (!retry_count_event)
+ {
+ retry_count_event++;
+ sql_print_information("Forcing to reconnect slave I/O thread");
+ if (try_to_reconnect(thd, mysql, mi, &retry_count, suppress_warnings,
+ reconnect_messages[SLAVE_RECON_ACT_EVENT]))
+ goto err;
+ goto connected;
+ });
if (event_len == packet_error)
{
@@ -3712,49 +2520,21 @@ max_allowed_packet",
Stopping slave I/O thread due to out-of-memory error from master");
goto err;
}
- mi->slave_running= MYSQL_SLAVE_RUN_NOT_CONNECT;
- thd->proc_info = "Waiting to reconnect after a failed master event read";
-#ifdef SIGNAL_WITH_VIO_CLOSE
- thd->clear_active_vio();
-#endif
- end_server(mysql);
- if (retry_count++)
- {
- if (retry_count > master_retry_count)
- goto err; // Don't retry forever
- safe_sleep(thd,mi->connect_retry,(CHECK_KILLED_FUNC)io_slave_killed,
- (void*) mi);
- }
- if (io_slave_killed(thd,mi))
- {
- if (global_system_variables.log_warnings)
- sql_print_information("Slave I/O thread killed while waiting to \
-reconnect after a failed read");
- goto err;
- }
- thd->proc_info = "Reconnecting after a failed master event read";
- if (!suppress_warnings)
- sql_print_information("Slave I/O thread: Failed reading log event, \
-reconnecting to retry, log '%s' position %s", IO_RPL_LOG_NAME,
- llstr(mi->master_log_pos, llbuff));
- if (safe_reconnect(thd, mysql, mi, suppress_warnings) ||
- io_slave_killed(thd,mi))
- {
- if (global_system_variables.log_warnings)
- sql_print_information("Slave I/O thread killed during or after a \
-reconnect done to recover from failed read");
- goto err;
- }
- goto connected;
+ if (try_to_reconnect(thd, mysql, mi, &retry_count, suppress_warnings,
+ reconnect_messages[SLAVE_RECON_ACT_EVENT]))
+ goto err;
+ goto connected;
} // if (event_len == packet_error)
- retry_count=0; // ok event, reset retry counter
- thd->proc_info = "Queueing master event to the relay log";
+ 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_len))
{
- sql_print_error("Slave I/O thread could not queue event from master");
- goto err;
+ 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 (flush_master_info(mi, 1))
{
@@ -3780,39 +2560,30 @@ reconnect done to recover from failed read");
ignore_log_space_limit=%d",
llstr(rli->log_space_limit,llbuf1),
llstr(rli->log_space_total,llbuf2),
- (int) rli->ignore_log_space_limit));
+ (int) rli->ignore_log_space_limit));
}
#endif
if (rli->log_space_limit && rli->log_space_limit <
- rli->log_space_total &&
+ rli->log_space_total &&
!rli->ignore_log_space_limit)
- if (wait_for_relay_log_space(rli))
- {
- sql_print_error("Slave I/O thread aborted while waiting for relay \
+ if (wait_for_relay_log_space(rli))
+ {
+ sql_print_error("Slave I/O thread aborted while waiting for relay \
log space");
- goto err;
- }
- // TODO: check debugging abort code
-#ifndef DBUG_OFF
- if (abort_slave_event_count && !--events_till_abort)
- {
- sql_print_error("Slave I/O thread: debugging abort");
- goto err;
- }
-#endif
- }
+ goto err;
+ }
+ }
}
// error = 0;
err:
// print the current replication position
sql_print_information("Slave I/O thread exiting, read up to log '%s', position %s",
- IO_RPL_LOG_NAME, llstr(mi->master_log_pos,llbuff));
+ IO_RPL_LOG_NAME, llstr(mi->master_log_pos,llbuff));
VOID(pthread_mutex_lock(&LOCK_thread_count));
- thd->query= 0; // extra safety
- thd->query_length= 0;
- thd->reset_db(NULL, 0);
+ thd->query = thd->db = 0; // extra safety
+ thd->query_length= thd->db_length= 0;
VOID(pthread_mutex_unlock(&LOCK_thread_count));
if (mysql)
{
@@ -3831,17 +2602,17 @@ err:
mi->mysql=0;
}
write_ignored_events_info_to_relay_log(thd, mi);
- thd->proc_info = "Waiting for slave mutex on exit";
+ thd_proc_info(thd, "Waiting for slave mutex on exit");
pthread_mutex_lock(&mi->run_lock);
/* Forget the relay log's format */
delete mi->rli.relay_log.description_event_for_queue;
mi->rli.relay_log.description_event_for_queue= 0;
- // TODO: make rpl_status part of MASTER_INFO
+ // TODO: make rpl_status part of Master_info
change_rpl_status(RPL_ACTIVE_SLAVE,RPL_IDLE_SLAVE);
DBUG_ASSERT(thd->net.buff != 0);
net_end(&thd->net); // destructor will not free it, because net.vio is 0
- close_thread_tables(thd, 0);
+ close_thread_tables(thd);
pthread_mutex_lock(&LOCK_thread_count);
THD_CHECK_SENTRY(thd);
delete thd;
@@ -3856,40 +2627,39 @@ err:
*/
pthread_cond_broadcast(&mi->stop_cond); // tell the world we are done
pthread_mutex_unlock(&mi->run_lock);
-#ifndef DBUG_OFF
- if (abort_slave_event_count && !events_till_abort)
- goto slave_begin;
-#endif
my_thread_end();
pthread_exit(0);
- DBUG_RETURN(0); // Can't return anything here
+ DBUG_RETURN(0); // Can't return anything here
}
-/* Slave SQL Thread entry point */
+/**
+ Slave SQL thread entry point.
+
+ @param arg Pointer to Relay_log_info object that holds information
+ for the SQL thread.
+ @return Always 0.
+*/
pthread_handler_t handle_slave_sql(void *arg)
{
- THD *thd; /* needs to be first for thread_stack */
+ THD *thd; /* needs to be first for thread_stack */
char llbuff[22],llbuff1[22];
- RELAY_LOG_INFO* rli = &((MASTER_INFO*)arg)->rli;
+
+ Relay_log_info* rli = &((Master_info*)arg)->rli;
const char *errmsg;
// needs to call my_thread_init(), otherwise we get a coredump in DBUG_ stuff
my_thread_init();
DBUG_ENTER("handle_slave_sql");
-#ifndef DBUG_OFF
-slave_begin:
-#endif
-
DBUG_ASSERT(rli->inited);
pthread_mutex_lock(&rli->run_lock);
DBUG_ASSERT(!rli->slave_running);
errmsg= 0;
-#ifndef DBUG_OFF
+#ifndef DBUG_OFF
rli->events_till_abort = abort_slave_event_count;
-#endif
+#endif
thd = new THD; // note that contructor of THD uses DBUG_ !
thd->thread_stack = (char*)&thd; // remember where our stack is
@@ -3938,22 +2708,23 @@ slave_begin:
now.
But the master timestamp is reset by RESET SLAVE & CHANGE MASTER.
*/
- clear_slave_error(rli);
+ 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);
rli->ignore_log_space_limit= 0;
pthread_mutex_unlock(&rli->log_space_lock);
rli->trans_retries= 0; // start from "no error"
+ DBUG_PRINT("info", ("rli->trans_retries: %lu", rli->trans_retries));
if (init_relay_log_pos(rli,
- rli->group_relay_log_name,
- rli->group_relay_log_pos,
- 1 /*need data lock*/, &errmsg,
+ rli->group_relay_log_name,
+ rli->group_relay_log_pos,
+ 1 /*need data lock*/, &errmsg,
1 /*look for a description_event*/))
{
sql_print_error("Error initializing relay log position: %s",
- errmsg);
+ errmsg);
goto err;
}
THD_CHECK_SENTRY(thd);
@@ -3961,7 +2732,7 @@ slave_begin:
{
char llbuf1[22], llbuf2[22];
DBUG_PRINT("info", ("my_b_tell(rli->cur_log)=%s rli->event_relay_log_pos=%s",
- llstr(my_b_tell(rli->cur_log),llbuf1),
+ llstr(my_b_tell(rli->cur_log),llbuf1),
llstr(rli->event_relay_log_pos,llbuf2)));
DBUG_ASSERT(rli->event_relay_log_pos >= BIN_LOG_HEADER_SIZE);
/*
@@ -3984,19 +2755,19 @@ slave_begin:
DBUG_ASSERT(rli->sql_thd == thd);
DBUG_PRINT("master_info",("log_file_name: %s position: %s",
- rli->group_master_log_name,
- llstr(rli->group_master_log_pos,llbuff)));
+ rli->group_master_log_name,
+ llstr(rli->group_master_log_pos,llbuff)));
if (global_system_variables.log_warnings)
sql_print_information("Slave SQL thread initialized, starting replication in \
log '%s' at position %s, relay log '%s' position: %s", RPL_LOG_NAME,
- llstr(rli->group_master_log_pos,llbuff),rli->group_relay_log_name,
- llstr(rli->group_relay_log_pos,llbuff1));
+ llstr(rli->group_master_log_pos,llbuff),rli->group_relay_log_name,
+ llstr(rli->group_relay_log_pos,llbuff1));
/* execute init_slave variable */
if (sys_init_slave.value_length)
{
execute_init_command(thd, &sys_init_slave, &LOCK_sys_init_slave);
- if (thd->query_error)
+ if (thd->is_slave_error)
{
sql_print_error("\
Slave SQL thread aborted. Can't execute init_slave query");
@@ -4009,7 +2780,7 @@ Slave SQL thread aborted. Can't execute init_slave query");
do not want to wait for next event in this case.
*/
pthread_mutex_lock(&rli->data_lock);
- if (rli->until_condition != RELAY_LOG_INFO::UNTIL_NONE &&
+ if (rli->until_condition != Relay_log_info::UNTIL_NONE &&
rli->is_until_satisfied(rli->group_master_log_pos))
{
char buf[22];
@@ -4024,45 +2795,100 @@ Slave SQL thread aborted. Can't execute init_slave query");
while (!sql_slave_killed(thd,rli))
{
- thd->proc_info = "Reading event from the relay log";
+ thd_proc_info(thd, "Reading event from the relay log");
DBUG_ASSERT(rli->sql_thd == thd);
THD_CHECK_SENTRY(thd);
if (exec_relay_log_event(thd,rli))
{
+ DBUG_PRINT("info", ("exec_relay_log_event() failed"));
// do not scare the user if SQL thread was simply killed or stopped
if (!sql_slave_killed(thd,rli))
- sql_print_error("\
+ {
+ /*
+ retrieve as much info as possible from the thd and, error
+ codes and warnings and print this to the error log as to
+ allow the user to locate the error
+ */
+ uint32 const last_errno= rli->last_error().number;
+
+ if (thd->is_error())
+ {
+ char const *const errmsg= thd->main_da.message();
+
+ DBUG_PRINT("info",
+ ("thd->main_da.sql_errno()=%d; rli->last_error.number=%d",
+ thd->main_da.sql_errno(), last_errno));
+ if (last_errno == 0)
+ {
+ rli->report(ERROR_LEVEL, thd->main_da.sql_errno(), errmsg);
+ }
+ else if (last_errno != thd->main_da.sql_errno())
+ {
+ sql_print_error("Slave (additional info): %s Error_code: %d",
+ errmsg, thd->main_da.sql_errno());
+ }
+ }
+
+ /* Print any warnings issued */
+ List_iterator_fast<MYSQL_ERROR> it(thd->warn_list);
+ MYSQL_ERROR *err;
+ /*
+ Added controlled slave thread cancel for replication
+ of user-defined variables.
+ */
+ bool udf_error = false;
+ while ((err= it++))
+ {
+ if (err->code == ER_CANT_OPEN_LIBRARY)
+ udf_error = true;
+ sql_print_warning("Slave: %s Error_code: %d",err->msg, err->code);
+ }
+ if (udf_error)
+ sql_print_error("Error loading user-defined library, slave SQL "
+ "thread aborted. Install the missing library, and restart the "
+ "slave SQL thread with \"SLAVE START\". We stopped at log '%s' "
+ "position %s", RPL_LOG_NAME, llstr(rli->group_master_log_pos,
+ llbuff));
+ else
+ sql_print_error("\
Error running query, slave SQL thread aborted. Fix the problem, and restart \
the slave SQL thread with \"SLAVE START\". We stopped at log \
'%s' position %s", RPL_LOG_NAME, llstr(rli->group_master_log_pos, llbuff));
+ }
goto err;
}
}
/* Thread stopped. Print the current replication position to the log */
sql_print_information("Slave SQL thread exiting, replication stopped in log "
- "'%s' at position %s",
- RPL_LOG_NAME, llstr(rli->group_master_log_pos,llbuff));
+ "'%s' at position %s",
+ RPL_LOG_NAME, llstr(rli->group_master_log_pos,llbuff));
err:
+
+ /*
+ Some events set some playgrounds, which won't be cleared because thread
+ stops. Stopping of this thread may not be known to these events ("stop"
+ request is detected only by the present function, not by events), so we
+ must "proactively" clear playgrounds:
+ */
+ rli->cleanup_context(thd, 1);
VOID(pthread_mutex_lock(&LOCK_thread_count));
/*
Some extra safety, which should not been needed (normally, event deletion
should already have done these assignments (each event which sets these
variables is supposed to set them to 0 before terminating)).
*/
- thd->catalog= 0;
- thd->reset_db(NULL, 0);
- thd->query= 0;
- thd->query_length= 0;
+ thd->query= thd->db= thd->catalog= 0;
+ thd->query_length= thd->db_length= 0;
VOID(pthread_mutex_unlock(&LOCK_thread_count));
- thd->proc_info = "Waiting for slave mutex on exit";
+ thd_proc_info(thd, "Waiting for slave mutex on exit");
pthread_mutex_lock(&rli->run_lock);
/* We need data_lock, at least to wake up any waiting master_pos_wait() */
pthread_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;
+ rli->slave_running= 0;
/* Forget the relay log's format */
delete rli->relay_log.description_event_for_exec;
rli->relay_log.description_event_for_exec= 0;
@@ -4089,29 +2915,17 @@ the slave SQL thread with \"SLAVE START\". We stopped at log \
THD_CHECK_SENTRY(thd);
delete thd;
pthread_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);
-#ifndef DBUG_OFF
- /*
- Bug #19938 Valgrind error (race) in handle_slave_sql()
- Read the value of rli->event_till_abort before releasing the mutex
- */
- const int eta= rli->events_till_abort;
-#endif
pthread_mutex_unlock(&rli->run_lock); // tell the world we are done
-#ifndef DBUG_OFF // TODO: reconsider the code below
- if (abort_slave_event_count && !eta)
- goto slave_begin;
-#endif
my_thread_end();
pthread_exit(0);
- DBUG_RETURN(0); // Can't return anything here
+ DBUG_RETURN(0); // Can't return anything here
}
@@ -4119,7 +2933,7 @@ the slave SQL thread with \"SLAVE START\". We stopped at log \
process_io_create_file()
*/
-static int process_io_create_file(MASTER_INFO* mi, Create_file_log_event* cev)
+static int process_io_create_file(Master_info* mi, Create_file_log_event* cev)
{
int error = 1;
ulong num_bytes;
@@ -4130,10 +2944,8 @@ static int process_io_create_file(MASTER_INFO* mi, Create_file_log_event* cev)
if (unlikely(!cev->is_valid()))
DBUG_RETURN(1);
- /*
- TODO: fix to honor table rules, not only db rules
- */
- if (!db_ok(cev->db, replicate_do_db, replicate_ignore_db))
+
+ if (!rpl_filter->db_ok(cev->db))
{
skip_load_data_infile(net);
DBUG_RETURN(0);
@@ -4142,11 +2954,11 @@ static int process_io_create_file(MASTER_INFO* mi, Create_file_log_event* cev)
thd->file_id = cev->file_id = mi->file_id++;
thd->server_id = cev->server_id;
cev_not_written = 1;
-
+
if (unlikely(net_request_file(net,cev->fname)))
{
sql_print_error("Slave I/O: failed requesting download of '%s'",
- cev->fname);
+ cev->fname);
goto err;
}
@@ -4157,18 +2969,19 @@ static int process_io_create_file(MASTER_INFO* mi, Create_file_log_event* cev)
*/
{
Append_block_log_event aev(thd,0,0,0,0);
-
+
for (;;)
{
if (unlikely((num_bytes=my_net_read(net)) == packet_error))
{
- sql_print_error("Network read error downloading '%s' from master",
- cev->fname);
- goto err;
+ sql_print_error("Network read error downloading '%s' from master",
+ cev->fname);
+ goto err;
}
if (unlikely(!num_bytes)) /* eof */
{
- net_write_command(net, 0, "", 0, "", 0);/* 3.23 master wants it */
+ /* 3.23 master wants it */
+ net_write_command(net, 0, (uchar*) "", 0, (uchar*) "", 0);
/*
If we wrote Create_file_log_event, then we need to write
Execute_load_log_event. If we did not write Create_file_log_event,
@@ -4176,43 +2989,46 @@ static int process_io_create_file(MASTER_INFO* mi, Create_file_log_event* cev)
INFILE had not existed, i.e. write nothing.
*/
if (unlikely(cev_not_written))
- break;
- Execute_load_log_event xev(thd,0,0);
- xev.log_pos = cev->log_pos;
- if (unlikely(mi->rli.relay_log.append(&xev)))
- {
- sql_print_error("Slave I/O: error writing Exec_load event to \
-relay log");
- goto err;
- }
- mi->rli.relay_log.harvest_bytes_written(&mi->rli.log_space_total);
- break;
+ break;
+ Execute_load_log_event xev(thd,0,0);
+ xev.log_pos = cev->log_pos;
+ if (unlikely(mi->rli.relay_log.append(&xev)))
+ {
+ mi->report(ERROR_LEVEL, ER_SLAVE_RELAY_LOG_WRITE_FAILURE,
+ ER(ER_SLAVE_RELAY_LOG_WRITE_FAILURE),
+ "error writing Exec_load event to relay log");
+ goto err;
+ }
+ mi->rli.relay_log.harvest_bytes_written(&mi->rli.log_space_total);
+ break;
}
if (unlikely(cev_not_written))
{
- cev->block = (char*)net->read_pos;
- cev->block_len = num_bytes;
- if (unlikely(mi->rli.relay_log.append(cev)))
- {
- sql_print_error("Slave I/O: error writing Create_file event to \
-relay log");
- goto err;
- }
- cev_not_written=0;
- mi->rli.relay_log.harvest_bytes_written(&mi->rli.log_space_total);
+ cev->block = net->read_pos;
+ cev->block_len = num_bytes;
+ if (unlikely(mi->rli.relay_log.append(cev)))
+ {
+ mi->report(ERROR_LEVEL, ER_SLAVE_RELAY_LOG_WRITE_FAILURE,
+ ER(ER_SLAVE_RELAY_LOG_WRITE_FAILURE),
+ "error writing Create_file event to relay log");
+ goto err;
+ }
+ cev_not_written=0;
+ mi->rli.relay_log.harvest_bytes_written(&mi->rli.log_space_total);
}
else
{
- aev.block = (char*)net->read_pos;
- aev.block_len = num_bytes;
- aev.log_pos = cev->log_pos;
- if (unlikely(mi->rli.relay_log.append(&aev)))
- {
- sql_print_error("Slave I/O: error writing Append_block event to \
-relay log");
- goto err;
- }
- mi->rli.relay_log.harvest_bytes_written(&mi->rli.log_space_total) ;
+ aev.block = net->read_pos;
+ aev.block_len = num_bytes;
+ aev.log_pos = cev->log_pos;
+ if (unlikely(mi->rli.relay_log.append(&aev)))
+ {
+ mi->report(ERROR_LEVEL, ER_SLAVE_RELAY_LOG_WRITE_FAILURE,
+ ER(ER_SLAVE_RELAY_LOG_WRITE_FAILURE),
+ "error writing Append_block event to relay log");
+ goto err;
+ }
+ mi->rli.relay_log.harvest_bytes_written(&mi->rli.log_space_total) ;
}
}
}
@@ -4227,8 +3043,8 @@ err:
SYNOPSIS
process_io_rotate()
- mi master_info for the slave
- rev The rotate log event read from the binary log
+ mi master_info for the slave
+ rev The rotate log event read from the binary log
DESCRIPTION
Updates the master info with the place in the next binary
@@ -4239,12 +3055,12 @@ err:
We assume we already locked mi->data_lock
RETURN VALUES
- 0 ok
- 1 Log event is illegal
+ 0 ok
+ 1 Log event is illegal
*/
-static int process_io_rotate(MASTER_INFO *mi, Rotate_log_event *rev)
+static int process_io_rotate(Master_info *mi, Rotate_log_event *rev)
{
DBUG_ENTER("process_io_rotate");
safe_mutex_assert_owner(&mi->data_lock);
@@ -4256,14 +3072,14 @@ static int process_io_rotate(MASTER_INFO *mi, Rotate_log_event *rev)
memcpy(mi->master_log_name, rev->new_log_ident, rev->ident_len+1);
mi->master_log_pos= rev->pos;
DBUG_PRINT("info", ("master_log_pos: '%s' %lu",
- mi->master_log_name, (ulong) mi->master_log_pos));
+ mi->master_log_name, (ulong) mi->master_log_pos));
#ifndef DBUG_OFF
/*
If we do not do this, we will be getting the first
rotate event forever, so we need to not disconnect after one.
*/
if (disconnect_slave_event_count)
- events_till_disconnect++;
+ mi->events_till_disconnect++;
#endif
/*
@@ -4292,14 +3108,14 @@ static int process_io_rotate(MASTER_INFO *mi, Rotate_log_event *rev)
Reads a 3.23 event and converts it to the slave's format. This code was
copied from MySQL 4.0.
*/
-static int queue_binlog_ver_1_event(MASTER_INFO *mi, const char *buf,
- ulong event_len)
+static int queue_binlog_ver_1_event(Master_info *mi, const char *buf,
+ ulong event_len)
{
const char *errmsg = 0;
ulong inc_pos;
bool ignore_event= 0;
char *tmp_buf = 0;
- RELAY_LOG_INFO *rli= &mi->rli;
+ Relay_log_info *rli= &mi->rli;
DBUG_ENTER("queue_binlog_ver_1_event");
/*
@@ -4310,7 +3126,8 @@ static int queue_binlog_ver_1_event(MASTER_INFO *mi, const char *buf,
{
if (unlikely(!(tmp_buf=(char*)my_malloc(event_len+1,MYF(MY_WME)))))
{
- sql_print_error("Slave I/O: out of memory for Load event");
+ mi->report(ERROR_LEVEL, ER_SLAVE_FATAL_ERROR,
+ ER(ER_SLAVE_FATAL_ERROR), "Memory allocation failed");
DBUG_RETURN(1);
}
memcpy(tmp_buf,buf,event_len);
@@ -4337,10 +3154,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);
+ errmsg);
my_free((char*) tmp_buf, MYF(MY_ALLOW_ZERO_PTR));
DBUG_RETURN(1);
}
+
pthread_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()) {
@@ -4383,8 +3201,8 @@ static int queue_binlog_ver_1_event(MASTER_INFO *mi, const char *buf,
}
if (likely(!ignore_event))
{
- if (ev->log_pos)
- /*
+ if (ev->log_pos)
+ /*
Don't do it for fake Rotate events (see comment in
Log_event::Log_event(const char* buf...) in log_event.cc).
*/
@@ -4408,13 +3226,13 @@ static int queue_binlog_ver_1_event(MASTER_INFO *mi, const char *buf,
Reads a 4.0 event and converts it to the slave's format. This code was copied
from queue_binlog_ver_1_event(), with some affordable simplifications.
*/
-static int queue_binlog_ver_3_event(MASTER_INFO *mi, const char *buf,
- ulong event_len)
+static int queue_binlog_ver_3_event(Master_info *mi, const char *buf,
+ ulong event_len)
{
const char *errmsg = 0;
ulong inc_pos;
char *tmp_buf = 0;
- RELAY_LOG_INFO *rli= &mi->rli;
+ Relay_log_info *rli= &mi->rli;
DBUG_ENTER("queue_binlog_ver_3_event");
/* read_log_event() will adjust log_pos to be end_log_pos */
@@ -4424,7 +3242,7 @@ 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);
+ errmsg);
my_free((char*) tmp_buf, MYF(MY_ALLOW_ZERO_PTR));
DBUG_RETURN(1);
}
@@ -4467,24 +3285,26 @@ err:
(exactly, slave's) format. To do the conversion, we create a 5.0 event from
the 3.23/4.0 bytes, then write this event to the relay log.
- TODO:
+ TODO:
Test this code before release - it has to be tested on a separate
setup with 3.23 master or 4.0 master
*/
-static int queue_old_event(MASTER_INFO *mi, const char *buf,
- ulong event_len)
+static int queue_old_event(Master_info *mi, const char *buf,
+ ulong event_len)
{
+ DBUG_ENTER("queue_old_event");
+
switch (mi->rli.relay_log.description_event_for_queue->binlog_version)
{
case 1:
- return queue_binlog_ver_1_event(mi,buf,event_len);
+ DBUG_RETURN(queue_binlog_ver_1_event(mi,buf,event_len));
case 3:
- return queue_binlog_ver_3_event(mi,buf,event_len);
+ DBUG_RETURN(queue_binlog_ver_3_event(mi,buf,event_len));
default: /* unsupported format; eg version 2 */
DBUG_PRINT("info",("unsupported binlog format %d in queue_old_event()",
- mi->rli.relay_log.description_event_for_queue->binlog_version));
- return 1;
+ mi->rli.relay_log.description_event_for_queue->binlog_version));
+ DBUG_RETURN(1);
}
}
@@ -4498,11 +3318,11 @@ static int queue_old_event(MASTER_INFO *mi, const char *buf,
any >=5.0.0 format.
*/
-int queue_event(MASTER_INFO* mi,const char* buf, ulong event_len)
+static int queue_event(Master_info* mi,const char* buf, ulong event_len)
{
int error= 0;
ulong inc_pos;
- RELAY_LOG_INFO *rli= &mi->rli;
+ Relay_log_info *rli= &mi->rli;
pthread_mutex_t *log_lock= rli->relay_log.get_log_lock();
DBUG_ENTER("queue_event");
@@ -4512,6 +3332,7 @@ int queue_event(MASTER_INFO* mi,const char* buf, ulong event_len)
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);
switch (buf[EVENT_TYPE_OFFSET]) {
@@ -4522,7 +3343,7 @@ int queue_event(MASTER_INFO* mi,const char* buf, ulong event_len)
cleaning is already done on a per-master-thread basis (as the master
server is shutting down cleanly, it has written all DROP TEMPORARY TABLE
prepared statements' deletion are TODO only when we binlog prep stmts).
-
+
We don't even increment mi->master_log_pos, because we may be just after
a Rotate event. Btw, in a few milliseconds we are going to have a Start
event from the next binlog (unless the master is presently running
@@ -4531,7 +3352,7 @@ int queue_event(MASTER_INFO* mi,const char* buf, ulong event_len)
goto err;
case ROTATE_EVENT:
{
- Rotate_log_event rev(buf,event_len,mi->rli.relay_log.description_event_for_queue);
+ Rotate_log_event rev(buf,event_len,mi->rli.relay_log.description_event_for_queue);
if (unlikely(process_io_rotate(mi,&rev)))
{
error= 1;
@@ -4566,17 +3387,17 @@ int queue_event(MASTER_INFO* mi,const char* buf, ulong event_len)
}
delete mi->rli.relay_log.description_event_for_queue;
mi->rli.relay_log.description_event_for_queue= tmp;
- /*
+ /*
Though this does some conversion to the slave's format, this will
- preserve the master's binlog format version, and number of event types.
+ preserve the master's binlog format version, and number of event types.
*/
- /*
+ /*
If the event was not requested by the slave (the slave did not ask for
- it), i.e. has end_log_pos=0, we do not increment mi->master_log_pos
+ it), i.e. has end_log_pos=0, we do not increment mi->master_log_pos
*/
inc_pos= uint4korr(buf+LOG_POS_OFFSET) ? event_len : 0;
DBUG_PRINT("info",("binlog format is now %d",
- mi->rli.relay_log.description_event_for_queue->binlog_version));
+ mi->rli.relay_log.description_event_for_queue->binlog_version));
}
break;
@@ -4585,8 +3406,8 @@ int queue_event(MASTER_INFO* mi,const char* buf, ulong event_len)
break;
}
- /*
- If this event is originating from this server, don't queue it.
+ /*
+ 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
server id (we must also keep this test in the SQL thread, in case somebody
@@ -4601,7 +3422,7 @@ int queue_event(MASTER_INFO* mi,const char* buf, ulong event_len)
pthread_mutex_lock(log_lock);
if ((uint4korr(buf + SERVER_ID_OFFSET) == ::server_id) &&
- !replicate_same_server_id)
+ !mi->rli.replicate_same_server_id)
{
/*
Do not write it to the relay log.
@@ -4627,9 +3448,9 @@ 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",
+ DBUG_PRINT("info", ("master_log_pos: %lu, event originating from the same server, ignored",
(ulong) mi->master_log_pos));
- }
+ }
else
{
/* write the event to the relay log */
@@ -4653,7 +3474,7 @@ err:
}
-void end_relay_log_info(RELAY_LOG_INFO* rli)
+void end_relay_log_info(Relay_log_info* rli)
{
DBUG_ENTER("end_relay_log_info");
@@ -4688,18 +3509,20 @@ void end_relay_log_info(RELAY_LOG_INFO* rli)
SYNPOSIS
safe_connect()
- thd Thread handler for slave
- mysql MySQL connection handle
- mi Replication handle
+ thd Thread handler for slave
+ mysql MySQL connection handle
+ mi Replication handle
RETURN
- 0 ok
- # Error
+ 0 ok
+ # Error
*/
-static int safe_connect(THD* thd, MYSQL* mysql, MASTER_INFO* mi)
+static int safe_connect(THD* thd, MYSQL* mysql, Master_info* mi)
{
- return connect_to_master(thd, mysql, mi, 0, 0);
+ DBUG_ENTER("safe_connect");
+
+ DBUG_RETURN(connect_to_master(thd, mysql, mi, 0, 0));
}
@@ -4712,33 +3535,37 @@ static int safe_connect(THD* thd, MYSQL* mysql, MASTER_INFO* mi)
master_retry_count times
*/
-static int connect_to_master(THD* thd, MYSQL* mysql, MASTER_INFO* mi,
- bool reconnect, bool suppress_warnings)
+static int connect_to_master(THD* thd, MYSQL* mysql, Master_info* mi,
+ bool reconnect, bool suppress_warnings)
{
int slave_was_killed;
- int last_errno= -2; // impossible error
+ int last_errno= -2; // impossible error
ulong err_count=0;
char llbuff[22];
DBUG_ENTER("connect_to_master");
#ifndef DBUG_OFF
- events_till_disconnect = disconnect_slave_event_count;
+ mi->events_till_disconnect = disconnect_slave_event_count;
#endif
ulong client_flag= CLIENT_REMEMBER_OPTIONS;
if (opt_slave_compressed_protocol)
- client_flag=CLIENT_COMPRESS; /* We will use compression */
+ client_flag=CLIENT_COMPRESS; /* We will use compression */
mysql_options(mysql, MYSQL_OPT_CONNECT_TIMEOUT, (char *) &slave_net_timeout);
mysql_options(mysql, MYSQL_OPT_READ_TIMEOUT, (char *) &slave_net_timeout);
-
+
#ifdef HAVE_OPENSSL
if (mi->ssl)
- mysql_ssl_set(mysql,
+ {
+ mysql_ssl_set(mysql,
mi->ssl_key[0]?mi->ssl_key:0,
- mi->ssl_cert[0]?mi->ssl_cert: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);
@@ -4746,23 +3573,21 @@ static int connect_to_master(THD* thd, MYSQL* mysql, MASTER_INFO* mi,
mysql_options(mysql, MYSQL_SET_CHARSET_DIR, (char *) charsets_dir);
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,
- mi->port, 0, client_flag) == 0))
+ (reconnect ? mysql_reconnect(mysql) != 0 :
+ mysql_real_connect(mysql, mi->host, mi->user, mi->password, 0,
+ mi->port, 0, client_flag) == 0))
{
/* Don't repeat last error */
if ((int)mysql_errno(mysql) != last_errno)
{
last_errno=mysql_errno(mysql);
suppress_warnings= 0;
- sql_print_error("Slave I/O thread: error %s to master \
-'%s@%s:%d': \
-Error: '%s' errno: %d retry-time: %d retries: %lu",
- (reconnect ? "reconnecting" : "connecting"),
- mi->user,mi->host,mi->port,
- mysql_error(mysql), last_errno,
- mi->connect_retry,
- master_retry_count);
+ mi->report(ERROR_LEVEL, last_errno,
+ "error %s to master '%s@%s:%d'"
+ " - retry-time: %d retries: %lu",
+ (reconnect ? "reconnecting" : "connecting"),
+ mi->user, mi->host, mi->port,
+ mi->connect_retry, master_retry_count);
}
/*
By default we try forever. The reason is that failure will trigger
@@ -4778,29 +3603,29 @@ Error: '%s' errno: %d retry-time: %d retries: %lu",
break;
}
safe_sleep(thd,mi->connect_retry,(CHECK_KILLED_FUNC)io_slave_killed,
- (void*)mi);
+ (void*)mi);
}
if (!slave_was_killed)
{
if (reconnect)
- {
+ {
if (!suppress_warnings && global_system_variables.log_warnings)
- sql_print_information("Slave: connected to master '%s@%s:%d',\
+ sql_print_information("Slave: connected to master '%s@%s:%d',\
replication resumed in log '%s' at position %s", mi->user,
- mi->host, mi->port,
- IO_RPL_LOG_NAME,
- llstr(mi->master_log_pos,llbuff));
+ mi->host, mi->port,
+ IO_RPL_LOG_NAME,
+ llstr(mi->master_log_pos,llbuff));
}
else
{
change_rpl_status(RPL_IDLE_SLAVE,RPL_ACTIVE_SLAVE);
- mysql_log.write(thd, COM_CONNECT_OUT, "%s@%s:%d",
- mi->user, mi->host, mi->port);
+ general_log_print(thd, COM_CONNECT_OUT, "%s@%s:%d",
+ mi->user, mi->host, mi->port);
}
#ifdef SIGNAL_WITH_VIO_CLOSE
thd->set_active_vio(mysql->net.vio);
-#endif
+#endif
}
mysql->reconnect= 1;
DBUG_PRINT("exit",("slave_was_killed: %d", slave_was_killed));
@@ -4816,8 +3641,8 @@ replication resumed in log '%s' at position %s", mi->user,
master_retry_count times
*/
-static int safe_reconnect(THD* thd, MYSQL* mysql, MASTER_INFO* mi,
- bool suppress_warnings)
+static int safe_reconnect(THD* thd, MYSQL* mysql, Master_info* mi,
+ bool suppress_warnings)
{
DBUG_ENTER("safe_reconnect");
DBUG_RETURN(connect_to_master(thd, mysql, mi, 1, suppress_warnings));
@@ -4830,7 +3655,7 @@ static int safe_reconnect(THD* thd, MYSQL* mysql, MASTER_INFO* mi,
SYNOPSIS
flush_relay_log_info()
- rli Relay log information
+ rli Relay log information
NOTES
- As this is only called by the slave thread, we don't need to
@@ -4849,13 +3674,18 @@ static int safe_reconnect(THD* thd, MYSQL* mysql, MASTER_INFO* mi,
longlong2str.
RETURN VALUES
- 0 ok
- 1 write error
+ 0 ok
+ 1 write error
*/
-bool flush_relay_log_info(RELAY_LOG_INFO* rli)
+bool flush_relay_log_info(Relay_log_info* rli)
{
bool error=0;
+ DBUG_ENTER("flush_relay_log_info");
+
+ if (unlikely(rli->no_storage))
+ DBUG_RETURN(0);
+
IO_CACHE *file = &rli->info_file;
char buff[FN_REFLEN*2+22*2+4], *pos;
@@ -4868,12 +3698,13 @@ bool flush_relay_log_info(RELAY_LOG_INFO* rli)
*pos++='\n';
pos=longlong2str(rli->group_master_log_pos, pos, 10);
*pos='\n';
- if (my_b_write(file, (byte*) buff, (ulong) (pos-buff)+1))
+ if (my_b_write(file, (uchar*) buff, (size_t) (pos-buff)+1))
error=1;
if (flush_io_cache(file))
error=1;
+
/* Flushing the relay log is done by the slave I/O thread */
- return error;
+ DBUG_RETURN(error);
}
@@ -4881,20 +3712,20 @@ bool flush_relay_log_info(RELAY_LOG_INFO* rli)
Called when we notice that the current "hot" log got rotated under our feet.
*/
-static IO_CACHE *reopen_relay_log(RELAY_LOG_INFO *rli, const char **errmsg)
+static IO_CACHE *reopen_relay_log(Relay_log_info *rli, const char **errmsg)
{
+ DBUG_ENTER("reopen_relay_log");
DBUG_ASSERT(rli->cur_log != &rli->cache_buf);
DBUG_ASSERT(rli->cur_log_fd == -1);
- DBUG_ENTER("reopen_relay_log");
IO_CACHE *cur_log = rli->cur_log=&rli->cache_buf;
if ((rli->cur_log_fd=open_binlog(cur_log,rli->event_relay_log_name,
- errmsg)) <0)
+ errmsg)) <0)
DBUG_RETURN(0);
/*
We want to start exactly where we was before:
- relay_log_pos Current log pos
- pending Number of bytes already processed from the event
+ relay_log_pos Current log pos
+ pending Number of bytes already processed from the event
*/
rli->event_relay_log_pos= max(rli->event_relay_log_pos, BIN_LOG_HEADER_SIZE);
my_b_seek(cur_log,rli->event_relay_log_pos);
@@ -4902,17 +3733,32 @@ static IO_CACHE *reopen_relay_log(RELAY_LOG_INFO *rli, const char **errmsg)
}
-Log_event* next_event(RELAY_LOG_INFO* rli)
+/**
+ Reads next event from the relay log. Should be called from the
+ slave IO thread.
+
+ @param rli Relay_log_info structure for the slave IO thread.
+
+ @return The event read, or NULL on error. If an error occurs, the
+ error is reported through the sql_print_information() or
+ sql_print_error() functions.
+*/
+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();
+ pthread_mutex_t *log_lock = rli->relay_log.get_log_lock();
const char* errmsg=0;
THD* thd = rli->sql_thd;
-
DBUG_ENTER("next_event");
+
DBUG_ASSERT(thd != 0);
+#ifndef DBUG_OFF
+ if (abort_slave_event_count && !rli->events_till_abort--)
+ DBUG_RETURN(0);
+#endif
+
/*
For most operations we need to protect rli members with data_lock,
so we assume calling function acquired this mutex for us and we will
@@ -4921,7 +3767,7 @@ Log_event* next_event(RELAY_LOG_INFO* rli)
pthread_cond_wait() with the non-data_lock mutex
*/
safe_mutex_assert_owner(&rli->data_lock);
-
+
while (!sql_slave_killed(thd,rli))
{
/*
@@ -4942,17 +3788,17 @@ Log_event* next_event(RELAY_LOG_INFO* rli)
pthread_mutex_lock(log_lock);
/*
- Reading xxx_file_id is safe because the log will only
- be rotated when we hold relay_log.LOCK_log
+ Reading xxx_file_id is safe because the log will only
+ be rotated when we hold relay_log.LOCK_log
*/
if (rli->relay_log.get_open_count() != rli->cur_log_old_open_count)
{
- // 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);
- if (!cur_log) // No more log files
- goto err;
- hot_log=0; // Using old binary log
+ // 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);
+ if (!cur_log) // No more log files
+ goto err;
+ hot_log=0; // Using old binary log
}
}
/*
@@ -4983,7 +3829,7 @@ Log_event* next_event(RELAY_LOG_INFO* rli)
When the relay log is created when the I/O thread starts, easy: the
master will send the description event and we will queue it.
But if the relay log is created by new_file(): then the solution is:
- MYSQL_LOG::open() will write the buffered description event.
+ MYSQL_BIN_LOG::open() will write the buffered description event.
*/
if ((ev=Log_event::read_log_event(cur_log,0,
rli->relay_log.description_event_for_exec)))
@@ -4996,25 +3842,25 @@ 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);
+ pthread_mutex_unlock(log_lock);
DBUG_RETURN(ev);
}
DBUG_ASSERT(thd==rli->sql_thd);
- if (opt_reckless_slave) // For mysql-test
+ if (opt_reckless_slave) // For mysql-test
cur_log->error = 0;
if (cur_log->error < 0)
{
errmsg = "slave SQL thread aborted because of I/O error";
if (hot_log)
- pthread_mutex_unlock(log_lock);
+ pthread_mutex_unlock(log_lock);
goto err;
}
if (!cur_log->error) /* EOF */
{
/*
- On a hot log, EOF means that there are no more updates to
- process and we must block until I/O thread adds some and
- signals us to continue
+ On a hot log, EOF means that there are no more updates to
+ process and we must block until I/O thread adds some and
+ signals us to continue
*/
if (hot_log)
{
@@ -5042,14 +3888,14 @@ Log_event* next_event(RELAY_LOG_INFO* rli)
time_t save_timestamp= rli->last_master_timestamp;
rli->last_master_timestamp= 0;
- DBUG_ASSERT(rli->relay_log.get_open_count() ==
+ DBUG_ASSERT(rli->relay_log.get_open_count() ==
rli->cur_log_old_open_count);
if (rli->ign_master_log_name_end[0])
{
/* We generate and return a Rotate, to make our positions advance */
DBUG_PRINT("info",("seeing an ignored end segment"));
- ev= new Rotate_log_event(thd, rli->ign_master_log_name_end,
+ ev= new Rotate_log_event(rli->ign_master_log_name_end,
0, rli->ign_master_log_pos_end,
Rotate_log_event::DUP_NAME);
rli->ign_master_log_name_end[0]= 0;
@@ -5064,14 +3910,14 @@ Log_event* next_event(RELAY_LOG_INFO* rli)
DBUG_RETURN(ev);
}
- /*
- 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);
+ /*
+ 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);
/*
- Possible deadlock :
+ Possible deadlock :
- the I/O thread has reached log_space_limit
- the SQL thread has read all relay logs, but cannot purge for some
reason:
@@ -5083,10 +3929,10 @@ Log_event* next_event(RELAY_LOG_INFO* rli)
the I/O thread to temporarily ignore the log_space_limit
constraint, because we do not want the I/O thread to block because of
space (it's ok if it blocks for any other reason (e.g. because the
- master does not send anything). Then the I/O thread stops waiting
+ master does not send anything). Then the I/O thread stops waiting
and reads more events.
The SQL thread decides when the I/O thread should take log_space_limit
- into account again : ignore_log_space_limit is reset to 0
+ into account again : ignore_log_space_limit is reset to 0
in purge_first_log (when the SQL thread purges the just-read relay
log), and also when the SQL thread starts. We should also reset
ignore_log_space_limit to 0 when the user does RESET SLAVE, but in
@@ -5096,12 +3942,12 @@ Log_event* next_event(RELAY_LOG_INFO* rli)
*/
pthread_mutex_lock(&rli->log_space_lock);
// prevent the I/O thread from blocking next times
- rli->ignore_log_space_limit= 1;
+ rli->ignore_log_space_limit= 1;
/*
- If the I/O thread is blocked, unblock it.
- Ok to broadcast after unlock, because the mutex is only destroyed in
- ~st_relay_log_info(), i.e. when rli is destroyed, and rli will not be
- destroyed before we exit the present function.
+ If the I/O thread is blocked, unblock it. Ok to broadcast
+ after unlock, because the mutex is only destroyed in
+ ~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);
@@ -5110,21 +3956,21 @@ Log_event* next_event(RELAY_LOG_INFO* rli)
// re-acquire data lock since we released it earlier
pthread_mutex_lock(&rli->data_lock);
rli->last_master_timestamp= save_timestamp;
- continue;
+ continue;
}
/*
- If the log was not hot, we need to move to the next log in
- sequence. The next log could be hot or cold, we deal with both
- cases separately after doing some common initialization
+ If the log was not hot, we need to move to the next log in
+ sequence. The next log could be hot or cold, we deal with both
+ cases separately after doing some common initialization
*/
end_io_cache(cur_log);
DBUG_ASSERT(rli->cur_log_fd >= 0);
my_close(rli->cur_log_fd, MYF(MY_WME));
rli->cur_log_fd = -1;
-
+
if (relay_log_purge)
{
- /*
+ /*
purge_first_log will properly set up relay log coordinates in rli.
If the group's coordinates are equal to the event's coordinates
(i.e. the relay log was not rotated in the middle of a group),
@@ -5135,33 +3981,33 @@ Log_event* next_event(RELAY_LOG_INFO* rli)
- I see no better detection method
- purge_first_log is not called that often
*/
- if (rli->relay_log.purge_first_log
+ if (rli->relay_log.purge_first_log
(rli,
rli->group_relay_log_pos == rli->event_relay_log_pos
&& !strcmp(rli->group_relay_log_name,rli->event_relay_log_name)))
- {
- errmsg = "Error purging processed logs";
- goto err;
- }
+ {
+ errmsg = "Error purging processed logs";
+ goto err;
+ }
}
else
{
- /*
- If hot_log is set, then we already have a lock on
- LOCK_log. If not, we have to get the lock.
-
- According to Sasha, the only time this code will ever be executed
- is if we are recovering from a bug.
- */
- if (rli->relay_log.find_next_log(&rli->linfo, !hot_log))
- {
- errmsg = "error switching to the next log";
- 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);
- flush_relay_log_info(rli);
+ /*
+ If hot_log is set, then we already have a lock on
+ LOCK_log. If not, we have to get the lock.
+
+ According to Sasha, the only time this code will ever be executed
+ is if we are recovering from a bug.
+ */
+ if (rli->relay_log.find_next_log(&rli->linfo, !hot_log))
+ {
+ errmsg = "error switching to the next log";
+ 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);
+ flush_relay_log_info(rli);
}
/*
@@ -5180,66 +4026,66 @@ Log_event* next_event(RELAY_LOG_INFO* rli)
if (rli->relay_log.is_active(rli->linfo.log_file_name))
{
#ifdef EXTRA_DEBUG
- if (global_system_variables.log_warnings)
- sql_print_information("next log '%s' is currently active",
+ if (global_system_variables.log_warnings)
+ sql_print_information("next log '%s' is currently active",
rli->linfo.log_file_name);
-#endif
- rli->cur_log= cur_log= rli->relay_log.get_log_file();
- rli->cur_log_old_open_count= rli->relay_log.get_open_count();
- DBUG_ASSERT(rli->cur_log_fd == -1);
-
- /*
- Read pointer has to be at the start since we are the only
- reader.
+#endif
+ rli->cur_log= cur_log= rli->relay_log.get_log_file();
+ rli->cur_log_old_open_count= rli->relay_log.get_open_count();
+ DBUG_ASSERT(rli->cur_log_fd == -1);
+
+ /*
+ Read pointer has to be at the start since we are the only
+ reader.
We must keep the LOCK_log to read the 4 first bytes, as this is a hot
log (same as when we call read_log_event() above: for a hot log we
take the mutex).
- */
- if (check_binlog_magic(cur_log,&errmsg))
+ */
+ if (check_binlog_magic(cur_log,&errmsg))
{
if (!hot_log) pthread_mutex_unlock(log_lock);
- goto err;
+ goto err;
}
if (!hot_log) pthread_mutex_unlock(log_lock);
- continue;
+ continue;
}
if (!hot_log) pthread_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
- from hot to cold, but not from cold to hot). No need for LOCK_log.
+ 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
+ from hot to cold, but not from cold to hot). No need for LOCK_log.
*/
#ifdef EXTRA_DEBUG
if (global_system_variables.log_warnings)
- sql_print_information("next log '%s' is not active",
+ sql_print_information("next log '%s' is not active",
rli->linfo.log_file_name);
-#endif
+#endif
// open_binlog() will check the magic header
if ((rli->cur_log_fd=open_binlog(cur_log,rli->linfo.log_file_name,
- &errmsg)) <0)
- goto err;
+ &errmsg)) <0)
+ goto err;
}
else
{
/*
- Read failed with a non-EOF error.
- TODO: come up with something better to handle this error
+ Read failed with a non-EOF error.
+ TODO: come up with something better to handle this error
*/
if (hot_log)
- pthread_mutex_unlock(log_lock);
+ pthread_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);
+ my_errno,cur_log->error);
// set read position to the beginning of the event
my_b_seek(cur_log,rli->event_relay_log_pos);
/* otherwise, we have had a partial read */
errmsg = "Aborting slave SQL thread because of partial event read";
- break; // To end of function
+ break; // To end of function
}
}
if (!errmsg && global_system_variables.log_warnings)
{
- sql_print_information("Error reading relay log event: %s",
+ sql_print_information("Error reading relay log event: %s",
"slave SQL thread was killed");
DBUG_RETURN(0);
}
@@ -5253,19 +4099,20 @@ err:
/*
Rotate a relay log (this is used only by FLUSH LOGS; the automatic rotation
because of size is simpler because when we do it we already have all relevant
- locks; here we don't, so this function is mainly taking locks).
- Returns nothing as we cannot catch any error (MYSQL_LOG::new_file() is void).
+ locks; here we don't, so this function is mainly taking locks).
+ Returns nothing as we cannot catch any error (MYSQL_BIN_LOG::new_file()
+ is void).
*/
-void rotate_relay_log(MASTER_INFO* mi)
+void rotate_relay_log(Master_info* mi)
{
DBUG_ENTER("rotate_relay_log");
- RELAY_LOG_INFO* rli= &mi->rli;
+ Relay_log_info* rli= &mi->rli;
/* We don't lock rli->run_lock. This would lead to deadlocks. */
pthread_mutex_lock(&mi->run_lock);
- /*
+ /*
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).
*/
@@ -5276,7 +4123,7 @@ void rotate_relay_log(MASTER_INFO* mi)
}
/* If the relay log is closed, new_file() will do nothing. */
- rli->relay_log.new_file(1);
+ rli->relay_log.new_file();
/*
We harvest now, because otherwise BIN_LOG_HEADER_SIZE will not immediately
@@ -5301,11 +4148,20 @@ end:
/**
Detects, based on master's version (as found in the relay log), if master
has a certain bug.
- @param rli RELAY_LOG_INFO which tells the master's version
+ @param rli Relay_log_info which tells the master's version
@param bug_id Number of the bug as found in bugs.mysql.com
+ @param report bool report error message, default TRUE
+
+ @param pred Predicate function that will be called with @c param to
+ check for the bug. If the function return @c true, the bug is present,
+ otherwise, it is not.
+
+ @param param State passed to @c pred function.
+
@return TRUE if master has the bug, FALSE if it does not.
*/
-bool rpl_master_has_bug(RELAY_LOG_INFO *rli, uint bug_id)
+bool rpl_master_has_bug(const Relay_log_info *rli, uint bug_id, bool report,
+ bool (*pred)(const void *), const void *param)
{
struct st_version_range_for_one_bug {
uint bug_id;
@@ -5315,7 +4171,10 @@ bool rpl_master_has_bug(RELAY_LOG_INFO *rli, uint bug_id)
static struct st_version_range_for_one_bug versions_for_all_bugs[]=
{
{24432, { 5, 0, 24 }, { 5, 0, 38 } },
- {24432, { 5, 1, 12 }, { 5, 1, 17 } }
+ {24432, { 5, 1, 12 }, { 5, 1, 17 } },
+ {33029, { 5, 0, 0 }, { 5, 0, 58 } },
+ {33029, { 5, 1, 0 }, { 5, 1, 12 } },
+ {37426, { 5, 1, 0 }, { 5, 1, 26 } },
};
const uchar *master_ver=
rli->relay_log.description_event_for_exec->server_version_split;
@@ -5329,43 +4188,70 @@ bool rpl_master_has_bug(RELAY_LOG_INFO *rli, uint bug_id)
*fixed_in= versions_for_all_bugs[i].fixed_in;
if ((versions_for_all_bugs[i].bug_id == bug_id) &&
(memcmp(introduced_in, master_ver, 3) <= 0) &&
- (memcmp(fixed_in, master_ver, 3) > 0))
+ (memcmp(fixed_in, master_ver, 3) > 0) &&
+ (pred == NULL || (*pred)(param)))
{
- // a verbose message for the error log
- slave_print_error(rli, ER_UNKNOWN_ERROR,
- "According to the master's version ('%s'),"
- " it is probable that master suffers from this bug:"
- " http://bugs.mysql.com/bug.php?id=%u"
- " and thus replicating the current binary log event"
- " may make the slave's data become different from the"
- " master's data."
- " To take no risk, slave refuses to replicate"
- " this event and stops."
- " We recommend that all updates be stopped on the"
- " master and slave, that the data of both be"
- " manually synchronized,"
- " that master's binary logs be deleted,"
- " that master be upgraded to a version at least"
- " equal to '%d.%d.%d'. Then replication can be"
- " restarted.",
- rli->relay_log.description_event_for_exec->server_version,
- bug_id,
- fixed_in[0], fixed_in[1], fixed_in[2]);
+ if (!report)
+ return TRUE;
// a short message for SHOW SLAVE STATUS (message length constraints)
my_printf_error(ER_UNKNOWN_ERROR, "master may suffer from"
" http://bugs.mysql.com/bug.php?id=%u"
" so slave stops; check error log on slave"
" for more info", MYF(0), bug_id);
+ // a verbose message for the error log
+ rli->report(ERROR_LEVEL, ER_UNKNOWN_ERROR,
+ "According to the master's version ('%s'),"
+ " it is probable that master suffers from this bug:"
+ " http://bugs.mysql.com/bug.php?id=%u"
+ " and thus replicating the current binary log event"
+ " may make the slave's data become different from the"
+ " master's data."
+ " To take no risk, slave refuses to replicate"
+ " this event and stops."
+ " We recommend that all updates be stopped on the"
+ " master and slave, that the data of both be"
+ " manually synchronized,"
+ " that master's binary logs be deleted,"
+ " that master be upgraded to a version at least"
+ " equal to '%d.%d.%d'. Then replication can be"
+ " restarted.",
+ rli->relay_log.description_event_for_exec->server_version,
+ bug_id,
+ fixed_in[0], fixed_in[1], fixed_in[2]);
return TRUE;
}
}
return FALSE;
}
+/**
+ BUG#33029, For all 5.0 up to 5.0.58 exclusive, and 5.1 up to 5.1.12
+ exclusive, if one statement in a SP generated AUTO_INCREMENT value
+ by the top statement, all statements after it would be considered
+ generated AUTO_INCREMENT value by the top statement, and a
+ erroneous INSERT_ID value might be associated with these statement,
+ which could cause duplicate entry error and stop the slave.
+
+ Detect buggy master to work around.
+ */
+bool rpl_master_erroneous_autoinc(THD *thd)
+{
+ if (active_mi && active_mi->rli.sql_thd == thd)
+ {
+ Relay_log_info *rli= &active_mi->rli;
+ DBUG_EXECUTE_IF("simulate_bug33029", return TRUE;);
+ return rpl_master_has_bug(rli, 33029, FALSE, NULL, NULL);
+ }
+ return FALSE;
+}
+
#ifdef HAVE_EXPLICIT_TEMPLATE_INSTANTIATION
template class I_List_iterator<i_string>;
template class I_List_iterator<i_string_pair>;
#endif
+/**
+ @} (end of group Replication)
+*/
#endif /* HAVE_REPLICATION */
diff --git a/sql/slave.h b/sql/slave.h
index da548e145d3..abd63315e62 100644
--- a/sql/slave.h
+++ b/sql/slave.h
@@ -13,17 +13,32 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
-#ifdef HAVE_REPLICATION
-
#ifndef SLAVE_H
#define SLAVE_H
-#include "mysql.h"
+/**
+ @defgroup Replication Replication
+ @{
+
+ @file
+*/
+#ifdef HAVE_REPLICATION
+
+#include "log.h"
#include "my_list.h"
+#include "rpl_filter.h"
+#include "rpl_tblmap.h"
+
#define SLAVE_NET_TIMEOUT 3600
-#define MAX_SLAVE_ERRMSG 1024
+
#define MAX_SLAVE_ERROR 2000
+
+// Forward declarations
+class Relay_log_info;
+class Master_info;
+
+
/*****************************************************************************
MySQL Replication
@@ -33,11 +48,11 @@
I/O Thread - One of these threads is started for each master server.
They maintain a connection to their master server, read log
events from the master as they arrive, and queues them into
- a single, shared relay log file. A MASTER_INFO struct
+ a single, shared relay log file. A Master_info
represents each of these threads.
SQL Thread - One of these threads is started and reads from the relay log
- file, executing each event. A RELAY_LOG_INFO struct
+ file, executing each event. A Relay_log_info
represents this thread.
Buffering in the relay log file makes it unnecessary to reread events from
@@ -60,45 +75,45 @@
mi->run_lock, keeps rli->run_lock, and tries to re-acquire mi->run_lock).
Currently active_mi never moves (it's created at startup and deleted at
- shutdown, and not changed: it always points to the same MASTER_INFO struct),
+ shutdown, and not changed: it always points to the same Master_info struct),
because we don't have multimaster. So for the moment, mi does not move, and
mi->rli does not either.
- In MASTER_INFO: run_lock, data_lock
+ In Master_info: run_lock, data_lock
run_lock protects all information about the run state: slave_running, thd
and the existence of the I/O thread to stop/start it, you need this mutex).
data_lock protects some moving members of the struct: counters (log name,
- position) and relay log (MYSQL_LOG object).
+ position) and relay log (MYSQL_BIN_LOG object).
- In RELAY_LOG_INFO: run_lock, data_lock
- see MASTER_INFO
+ In Relay_log_info: run_lock, data_lock
+ see Master_info
Order of acquisition: if you want to have LOCK_active_mi and a run_lock, you
must acquire LOCK_active_mi first.
- In MYSQL_LOG: LOCK_log, LOCK_index of the binlog and the relay log
+ In MYSQL_BIN_LOG: LOCK_log, LOCK_index of the binlog and the relay log
LOCK_log: when you write to it. LOCK_index: when you create/delete a binlog
(so that you have to update the .index file).
*/
extern ulong master_retry_count;
extern MY_BITMAP slave_error_mask;
+extern char slave_skip_error_names[];
extern bool use_slave_mask;
-extern char* slave_load_tmpdir;
-extern my_string master_info_file,relay_log_info_file;
-extern my_string opt_relay_logname, opt_relaylog_index_name;
+extern char *slave_load_tmpdir;
+extern char *master_info_file, *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 ulonglong relay_log_space_limit;
-struct st_master_info;
/*
- 3 possible values for MASTER_INFO::slave_running and
- RELAY_LOG_INFO::slave_running.
+ 3 possible values for Master_info::slave_running and
+ Relay_log_info::slave_running.
The values 0,1,2 are very important: to keep the diff small, I didn't
substitute places where we use 0/1 with the newly defined symbols. So don't change
these values.
- The same way, code is assuming that in RELAY_LOG_INFO we use only values
+ The same way, code is assuming that in Relay_log_info we use only values
0/1.
I started with using an enum, but
enum_variable=1; is not legal so would have required many line changes.
@@ -107,390 +122,11 @@ struct st_master_info;
#define MYSQL_SLAVE_RUN_NOT_CONNECT 1
#define MYSQL_SLAVE_RUN_CONNECT 2
-/****************************************************************************
-
- Replication SQL Thread
-
- st_relay_log_info contains:
- - the current relay log
- - the current relay log offset
- - master log name
- - master log sequence corresponding to the last update
- - misc information specific to the SQL thread
-
- st_relay_log_info is initialized from the slave.info file if such exists.
- Otherwise, data members are intialized with defaults. The initialization is
- done with init_relay_log_info() call.
-
- The format of slave.info file:
-
- relay_log_name
- relay_log_pos
- master_log_name
- master_log_pos
-
- To clean up, call end_relay_log_info()
-
-*****************************************************************************/
-
-typedef struct st_relay_log_info
-{
- /*** The following variables can only be read when protect by data lock ****/
-
- /*
- info_fd - file descriptor of the info file. set only during
- initialization or clean up - safe to read anytime
- cur_log_fd - file descriptor of the current read relay log
- */
- File info_fd,cur_log_fd;
-
- /*
- Protected with internal locks.
- Must get data_lock when resetting the logs.
- */
- MYSQL_LOG relay_log;
- LOG_INFO linfo;
- IO_CACHE cache_buf,*cur_log;
-
- /* The following variables are safe to read any time */
-
- /* IO_CACHE of the info file - set only during init or end */
- IO_CACHE info_file;
-
- /*
- When we restart slave thread we need to have access to the previously
- created temporary tables. Modified only on init/end and by the SQL
- thread, read only by SQL thread.
- */
- TABLE *save_temporary_tables;
-
- /*
- standard lock acquistion order to avoid deadlocks:
- run_lock, data_lock, relay_log.LOCK_log, relay_log.LOCK_index
- */
- pthread_mutex_t data_lock,run_lock;
-
- /*
- start_cond is broadcast when SQL thread is started
- stop_cond - when stopped
- data_cond - when data protected by data_lock changes
- */
- pthread_cond_t start_cond, stop_cond, data_cond;
-
- /* parent master info structure */
- struct st_master_info *mi;
-
- /*
- Needed to deal properly with cur_log getting closed and re-opened with
- a different log under our feet
- */
- uint32 cur_log_old_open_count;
-
- /*
- Let's call a group (of events) :
- - a transaction
- or
- - an autocommiting query + its associated events (INSERT_ID,
- TIMESTAMP...)
- We need these rli coordinates :
- - relay log name and position of the beginning of the group we currently are
- executing. Needed to know where we have to restart when replication has
- stopped in the middle of a group (which has been rolled back by the slave).
- - relay log name and position just after the event we have just
- executed. This event is part of the current group.
- Formerly we only had the immediately above coordinates, plus a 'pending'
- variable, but this dealt wrong with the case of a transaction starting on a
- relay log and finishing (commiting) on another relay log. Case which can
- happen when, for example, the relay log gets rotated because of
- max_binlog_size.
- */
- char group_relay_log_name[FN_REFLEN];
- ulonglong group_relay_log_pos;
- char event_relay_log_name[FN_REFLEN];
- ulonglong event_relay_log_pos;
- ulonglong future_event_relay_log_pos;
-
- /*
- Original log name and position of the group we're currently executing
- (whose coordinates are group_relay_log_name/pos in the relay log)
- in the master's binlog. These concern the *group*, because in the master's
- binlog the log_pos that comes with each event is the position of the
- beginning of the group.
- */
- char group_master_log_name[FN_REFLEN];
- volatile my_off_t group_master_log_pos;
-
- /*
- Handling of the relay_log_space_limit optional constraint.
- ignore_log_space_limit is used to resolve a deadlock between I/O and SQL
- threads, the SQL thread sets it to unblock the I/O thread and make it
- temporarily forget about the constraint.
- */
- ulonglong log_space_limit,log_space_total;
- bool ignore_log_space_limit;
-
- /*
- When it commits, InnoDB internally stores the master log position it has
- processed so far; the position to store is the one of the end of the
- committing event (the COMMIT query event, or the event if in autocommit
- mode).
- */
-#if MYSQL_VERSION_ID < 40100
- ulonglong future_master_log_pos;
-#else
- ulonglong future_group_master_log_pos;
-#endif
-
- time_t last_master_timestamp;
-
- /*
- Needed for problems when slave stops and we want to restart it
- skipping one or more events in the master log that have caused
- errors, and have been manually applied by DBA already.
- */
- 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;
- THD * sql_thd;
- int last_slave_errno;
-#ifndef DBUG_OFF
- int events_till_abort;
-#endif
- char last_slave_error[MAX_SLAVE_ERRMSG];
-
- /* if not set, the value of other members of the structure are undefined */
- bool inited;
- volatile bool abort_slave;
- volatile uint slave_running;
-
- /*
- Condition and its parameters from START SLAVE UNTIL clause.
-
- UNTIL condition is tested with is_until_satisfied() method that is
- called by exec_relay_log_event(). is_until_satisfied() caches the result
- of the comparison of log names because log names don't change very often;
- this cache is invalidated by parts of code which change log names with
- notify_*_log_name_updated() methods. (They need to be called only if SQL
- thread is running).
- */
-
- enum {UNTIL_NONE= 0, UNTIL_MASTER_POS, UNTIL_RELAY_POS} until_condition;
- char until_log_name[FN_REFLEN];
- ulonglong until_log_pos;
- /* extension extracted from log_name and converted to int */
- ulong until_log_name_extension;
- /*
- Cached result of comparison of until_log_name and current log name
- -2 means unitialised, -1,0,1 are comarison results
- */
- enum
- {
- UNTIL_LOG_NAMES_CMP_UNKNOWN= -2, UNTIL_LOG_NAMES_CMP_LESS= -1,
- UNTIL_LOG_NAMES_CMP_EQUAL= 0, UNTIL_LOG_NAMES_CMP_GREATER= 1
- } until_log_names_cmp_result;
-
- char cached_charset[6];
- /*
- trans_retries varies between 0 to slave_transaction_retries and counts how
- many times the slave has retried the present transaction; gets reset to 0
- when the transaction finally succeeds. retried_trans is a cumulative
- counter: how many times the slave has retried a transaction (any) since
- slave started.
- */
- ulong trans_retries, retried_trans;
-
- /*
- If the end of the hot relay log is made of master's events ignored by the
- slave I/O thread, these two keep track of the coords (in the master's
- binlog) of the last of these events seen by the slave I/O thread. If not,
- ign_master_log_name_end[0] == 0.
- As they are like a Rotate event read/written from/to the relay log, they
- are both protected by rli->relay_log.LOCK_log.
- */
- char ign_master_log_name_end[FN_REFLEN];
- ulonglong ign_master_log_pos_end;
-
- st_relay_log_info();
- ~st_relay_log_info();
-
- /*
- Invalidate cached until_log_name and group_relay_log_name comparison
- result. Should be called after any update of group_realy_log_name if
- there chances that sql_thread is running.
- */
- inline void notify_group_relay_log_name_update()
- {
- if (until_condition==UNTIL_RELAY_POS)
- until_log_names_cmp_result= UNTIL_LOG_NAMES_CMP_UNKNOWN;
- }
-
- /*
- The same as previous but for group_master_log_name.
- */
- inline void notify_group_master_log_name_update()
- {
- if (until_condition==UNTIL_MASTER_POS)
- until_log_names_cmp_result= UNTIL_LOG_NAMES_CMP_UNKNOWN;
- }
-
- inline void inc_event_relay_log_pos()
- {
- event_relay_log_pos= future_event_relay_log_pos;
- }
-
- void inc_group_relay_log_pos(ulonglong log_pos,
- bool skip_lock=0);
-
- int wait_for_pos(THD* thd, String* log_name, longlong log_pos,
- longlong timeout);
- void close_temporary_tables();
-
- /* Check if UNTIL condition is satisfied. See slave.cc for more. */
- bool is_until_satisfied(my_off_t master_beg_pos);
- inline ulonglong until_pos()
- {
- return ((until_condition == UNTIL_MASTER_POS) ? group_master_log_pos :
- group_relay_log_pos);
- }
- /*
- Last charset (6 bytes) seen by slave SQL thread is cached here; it helps
- the thread save 3 get_charset() per Query_log_event if the charset is not
- changing from event to event (common situation).
- When the 6 bytes are equal to 0 is used to mean "cache is invalidated".
- */
- void cached_charset_invalidate();
- bool cached_charset_compare(char *charset);
-} RELAY_LOG_INFO;
-
-
-Log_event* next_event(RELAY_LOG_INFO* rli);
-
-/*****************************************************************************
-
- Replication IO Thread
-
- st_master_info contains:
- - information about how to connect to a master
- - current master log name
- - current master log offset
- - misc control variables
-
- st_master_info is initialized once from the master.info file if such
- exists. Otherwise, data members corresponding to master.info fields
- are initialized with defaults specified by master-* options. The
- initialization is done through init_master_info() call.
-
- The format of master.info file:
-
- log_name
- log_pos
- master_host
- master_user
- master_pass
- master_port
- master_connect_retry
-
- To write out the contents of master.info file to disk ( needed every
- time we read and queue data from the master ), a call to
- flush_master_info() is required.
-
- To clean up, call end_master_info()
-
-*****************************************************************************/
-
-typedef struct st_master_info
-{
- /* 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 user[USERNAME_LENGTH+1];
- char password[MAX_PASSWORD_LENGTH+1];
- my_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];
-
- my_off_t master_log_pos;
- 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;
- THD *io_thd;
- MYSQL* mysql;
- uint32 file_id; /* for 3.23 load data infile */
- RELAY_LOG_INFO rli;
- uint port;
- uint connect_retry;
-#ifndef DBUG_OFF
- int events_till_abort;
-#endif
- bool inited;
- volatile bool abort_slave;
- volatile uint slave_running;
- volatile ulong slave_run_id;
- /*
- The difference in seconds between the clock of the master and the clock of
- the slave (second - first). It must be signed as it may be <0 or >0.
- clock_diff_with_master is computed when the I/O thread starts; for this the
- I/O thread does a SELECT UNIX_TIMESTAMP() on the master.
- "how late the slave is compared to the master" is computed like this:
- clock_of_slave - last_timestamp_executed_by_SQL_thread - clock_diff_with_master
-
- */
- long clock_diff_with_master;
-
- st_master_info()
- :ssl(0), fd(-1), io_thd(0), inited(0),
- abort_slave(0),slave_running(0), slave_run_id(0)
- {
- host[0] = 0; user[0] = 0; password[0] = 0;
- ssl_ca[0]= 0; ssl_capath[0]= 0; ssl_cert[0]= 0;
- ssl_cipher[0]= 0; ssl_key[0]= 0;
-
- bzero((char*) &file, sizeof(file));
- pthread_mutex_init(&run_lock, MY_MUTEX_INIT_FAST);
- pthread_mutex_init(&data_lock, MY_MUTEX_INIT_FAST);
- pthread_cond_init(&data_cond, NULL);
- pthread_cond_init(&start_cond, NULL);
- pthread_cond_init(&stop_cond, NULL);
- }
-
- ~st_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);
- }
-
-} MASTER_INFO;
-
-
-int queue_event(MASTER_INFO* mi,const char* buf,ulong event_len);
-
-typedef struct st_table_rule_ent
-{
- char* db;
- char* tbl_name;
- uint key_len;
-} TABLE_RULE_ENT;
-
-#define TABLE_RULE_HASH_SIZE 16
-#define TABLE_RULE_ARR_SIZE 16
-#define MAX_SLAVE_ERRMSG 1024
-
#define RPL_LOG_NAME (rli->group_master_log_name[0] ? rli->group_master_log_name :\
"FIRST")
#define IO_RPL_LOG_NAME (mi->master_log_name[0] ? mi->master_log_name :\
"FIRST")
-/* masks for start/stop operations on io and sql slave threads */
-#define SLAVE_IO 1
-#define SLAVE_SQL 2
-
/*
If the following is set, if first gives an error, second will be
tried. Otherwise, if first fails, we fail.
@@ -499,17 +135,12 @@ typedef struct st_table_rule_ent
int init_slave();
void init_slave_skip_errors(const char* arg);
-int flush_master_info(MASTER_INFO* mi, bool flush_relay_log_cache);
-bool flush_relay_log_info(RELAY_LOG_INFO* rli);
+bool flush_relay_log_info(Relay_log_info* rli);
int register_slave_on_master(MYSQL* mysql);
-int terminate_slave_threads(MASTER_INFO* mi, int thread_mask,
+int terminate_slave_threads(Master_info* mi, int thread_mask,
bool skip_lock = 0);
-int terminate_slave_thread(THD* thd, pthread_mutex_t* term_mutex,
- pthread_mutex_t* cond_lock,
- pthread_cond_t* term_cond,
- volatile uint* slave_running);
int start_slave_threads(bool need_slave_mutex, bool wait_for_start,
- MASTER_INFO* mi, const char* master_info_fname,
+ Master_info* mi, const char* master_info_fname,
const char* slave_info_fname, int thread_mask);
/*
cond_lock is usually same as start_lock. It is needed for the case when
@@ -522,93 +153,75 @@ int start_slave_thread(pthread_handler h_func, pthread_mutex_t* start_lock,
pthread_cond_t* start_cond,
volatile uint *slave_running,
volatile ulong *slave_run_id,
- MASTER_INFO* mi,
+ Master_info* mi,
bool high_priority);
+/* If fd is -1, dump to NET */
+int mysql_table_dump(THD* thd, const char* db,
+ const char* tbl_name, int fd = -1);
+
/* retrieve table from master and copy to slave*/
int fetch_master_table(THD* thd, const char* db_name, const char* table_name,
- MASTER_INFO* mi, MYSQL* mysql, bool overwrite);
+ Master_info* mi, MYSQL* mysql, bool overwrite);
-void table_rule_ent_hash_to_str(String* s, HASH* h);
-void table_rule_ent_dynamic_array_to_str(String* s, DYNAMIC_ARRAY* a);
-bool show_master_info(THD* thd, MASTER_INFO* mi);
+bool show_master_info(THD* thd, Master_info* mi);
bool show_binlog_info(THD* thd);
-bool rpl_master_has_bug(RELAY_LOG_INFO *rli, uint bug_id);
+bool rpl_master_has_bug(const Relay_log_info *rli, uint bug_id, bool report,
+ bool (*pred)(const void *), const void *param);
+bool rpl_master_erroneous_autoinc(THD* thd);
-/* See if the query uses any tables that should not be replicated */
-bool tables_ok(THD* thd, TABLE_LIST* tables);
-
-/*
- Check to see if the database is ok to operate on with respect to the
- do and ignore lists - used in replication
-*/
-int db_ok(const char* db, I_List<i_string> &do_list,
- I_List<i_string> &ignore_list );
-int db_ok_with_wild_table(const char *db);
-
-int add_table_rule(HASH* h, const char* table_spec);
-int add_wild_table_rule(DYNAMIC_ARRAY* a, const char* table_spec);
-void init_table_rule_hash(HASH* h, bool* h_inited);
-void init_table_rule_array(DYNAMIC_ARRAY* a, bool* a_inited);
const char *print_slave_db_safe(const char *db);
-int check_expected_error(THD* thd, RELAY_LOG_INFO* rli, int error_code);
+int check_expected_error(THD* thd, Relay_log_info const *rli, int error_code);
void skip_load_data_infile(NET* net);
-void slave_print_error(RELAY_LOG_INFO *rli, int err_code, const char *msg, ...)
- ATTRIBUTE_FORMAT(printf, 3, 4);
void end_slave(); /* clean up */
-void init_master_info_with_options(MASTER_INFO* mi);
-void clear_until_condition(RELAY_LOG_INFO* rli);
-void clear_slave_error(RELAY_LOG_INFO* rli);
-int init_master_info(MASTER_INFO* mi, const char* master_info_fname,
- const char* slave_info_fname,
- bool abort_if_no_master_info_file,
- int thread_mask);
-void end_master_info(MASTER_INFO* mi);
-void end_relay_log_info(RELAY_LOG_INFO* rli);
-void lock_slave_threads(MASTER_INFO* mi);
-void unlock_slave_threads(MASTER_INFO* mi);
-void init_thread_mask(int* mask,MASTER_INFO* mi,bool inverse);
-int init_relay_log_pos(RELAY_LOG_INFO* rli,const char* log,ulonglong pos,
+void clear_until_condition(Relay_log_info* rli);
+void clear_slave_error(Relay_log_info* rli);
+void end_relay_log_info(Relay_log_info* rli);
+void lock_slave_threads(Master_info* mi);
+void unlock_slave_threads(Master_info* mi);
+void init_thread_mask(int* mask,Master_info* mi,bool inverse);
+int init_relay_log_pos(Relay_log_info* rli,const char* log,ulonglong pos,
bool need_data_lock, const char** errmsg,
bool look_for_description_event);
-int purge_relay_logs(RELAY_LOG_INFO* rli, THD *thd, bool just_reset,
+int purge_relay_logs(Relay_log_info* rli, THD *thd, bool just_reset,
const char** errmsg);
void set_slave_thread_options(THD* thd);
-void set_slave_thread_default_charset(THD* thd, RELAY_LOG_INFO *rli);
-void rotate_relay_log(MASTER_INFO* mi);
+void set_slave_thread_default_charset(THD *thd, Relay_log_info const *rli);
+void rotate_relay_log(Master_info* mi);
+int apply_event_and_update_pos(Log_event* ev, THD* thd, Relay_log_info* rli,
+ bool skip);
pthread_handler_t handle_slave_io(void *arg);
pthread_handler_t handle_slave_sql(void *arg);
extern bool volatile abort_loop;
-extern MASTER_INFO main_mi, *active_mi; /* active_mi for multi-master */
+extern Master_info main_mi, *active_mi; /* active_mi for multi-master */
extern LIST master_list;
-extern HASH replicate_do_table, replicate_ignore_table;
-extern DYNAMIC_ARRAY replicate_wild_do_table, replicate_wild_ignore_table;
-extern bool do_table_inited, ignore_table_inited,
- wild_do_table_inited, wild_ignore_table_inited;
-extern bool table_rules_on;
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 my_string master_user, master_password, master_host,
- master_info_file, relay_log_info_file, report_user, report_host,
- report_password;
+extern char * master_user, *master_password, *master_host;
+extern char *master_info_file, *relay_log_info_file, *report_user;
+extern char *report_host, *report_password;
extern my_bool master_ssl;
-extern my_string master_ssl_ca, master_ssl_capath, master_ssl_cert,
- master_ssl_cipher, master_ssl_key;
+extern char *master_ssl_ca, *master_ssl_capath, *master_ssl_cert;
+extern char *master_ssl_cipher, *master_ssl_key;
-extern I_List<i_string> replicate_do_db, replicate_ignore_db;
-extern I_List<i_string_pair> replicate_rewrite_db;
extern I_List<THD> threads;
-#endif
-#else
+#endif /* HAVE_REPLICATION */
+
+/* masks for start/stop operations on io and sql slave threads */
#define SLAVE_IO 1
#define SLAVE_SQL 2
-#endif /* HAVE_REPLICATION */
+
+/**
+ @} (end of group Replication)
+*/
+
+#endif
diff --git a/sql/sp.cc b/sql/sp.cc
index 3af51b82521..cc545992857 100644
--- a/sql/sp.cc
+++ b/sql/sp.cc
@@ -13,7 +13,6 @@
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 "sp.h"
#include "sp_head.h"
@@ -37,7 +36,8 @@ static int
db_load_routine(THD *thd, int type, sp_name *name, sp_head **sphp,
ulong sql_mode, const char *params, const char *returns,
const char *body, st_sp_chistics &chistics,
- const char *definer, longlong created, longlong modified);
+ const char *definer, longlong created, longlong modified,
+ Stored_program_creation_ctx *creation_ctx);
/*
*
@@ -49,7 +49,7 @@ enum
{
MYSQL_PROC_FIELD_DB = 0,
MYSQL_PROC_FIELD_NAME,
- MYSQL_PROC_FIELD_TYPE,
+ MYSQL_PROC_MYSQL_TYPE,
MYSQL_PROC_FIELD_SPECIFIC_NAME,
MYSQL_PROC_FIELD_LANGUAGE,
MYSQL_PROC_FIELD_ACCESS,
@@ -63,143 +63,273 @@ enum
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
};
/* Tells what SP_DEFAULT_ACCESS should be mapped to */
#define SP_DEFAULT_ACCESS_MAPPING SP_CONTAINS_SQL
+/*************************************************************************/
-/*
- Close mysql.proc, opened with open_proc_table_for_read().
-
- SYNOPSIS
- close_proc_table()
- thd Thread context
- backup Pointer to Open_tables_state instance which holds
- information about tables which were open before we
- decided to access mysql.proc.
+/**
+ Stored_routine_creation_ctx -- creation context of stored routines
+ (stored procedures and functions).
*/
-void close_proc_table(THD *thd, Open_tables_state *backup)
+class Stored_routine_creation_ctx : public Stored_program_creation_ctx,
+ public Sql_alloc
{
- close_thread_tables(thd);
- thd->restore_backup_open_tables_state(backup);
+public:
+ static Stored_routine_creation_ctx *
+ load_from_db(THD *thd, const sp_name *name, TABLE *proc_tbl);
+
+public:
+ virtual Stored_program_creation_ctx *clone(MEM_ROOT *mem_root)
+ {
+ return new (mem_root) Stored_routine_creation_ctx(m_client_cs,
+ m_connection_cl,
+ m_db_cl);
+ }
+
+protected:
+ virtual Object_creation_ctx *create_backup_ctx(THD *thd) const
+ {
+ DBUG_ENTER("Stored_routine_creation_ctx::create_backup_ctx");
+ DBUG_RETURN(new Stored_routine_creation_ctx(thd));
+ }
+
+private:
+ Stored_routine_creation_ctx(THD *thd)
+ : Stored_program_creation_ctx(thd)
+ { }
+
+ Stored_routine_creation_ctx(CHARSET_INFO *client_cs,
+ CHARSET_INFO *connection_cl,
+ CHARSET_INFO *db_cl)
+ : Stored_program_creation_ctx(client_cs, connection_cl, db_cl)
+ { }
+};
+
+/**************************************************************************
+ Stored_routine_creation_ctx implementation.
+**************************************************************************/
+
+bool load_charset(MEM_ROOT *mem_root,
+ Field *field,
+ CHARSET_INFO *dflt_cs,
+ CHARSET_INFO **cs)
+{
+ String cs_name;
+
+ if (get_field(mem_root, field, &cs_name))
+ {
+ *cs= dflt_cs;
+ return TRUE;
+ }
+
+ *cs= get_charset_by_csname(cs_name.c_ptr(), MY_CS_PRIMARY, MYF(0));
+
+ if (*cs == NULL)
+ {
+ *cs= dflt_cs;
+ return TRUE;
+ }
+
+ return FALSE;
}
+/*************************************************************************/
-/*
- Open the mysql.proc table for read.
+bool load_collation(MEM_ROOT *mem_root,
+ Field *field,
+ CHARSET_INFO *dflt_cl,
+ CHARSET_INFO **cl)
+{
+ String cl_name;
- SYNOPSIS
- open_proc_table_for_read()
- thd Thread context
- backup Pointer to Open_tables_state instance where information about
- currently open tables will be saved, and from which will be
- restored when we will end work with mysql.proc.
-
- NOTES
- Thanks to restrictions which we put on opening and locking of
- this table for writing, we can open and lock it for reading
- even when we already have some other tables open and locked.
- One must call close_proc_table() to close table opened with
- this call.
-
- RETURN
- 0 Error
- # Pointer to TABLE object of mysql.proc
-*/
+ if (get_field(mem_root, field, &cl_name))
+ {
+ *cl= dflt_cl;
+ return TRUE;
+ }
-TABLE *open_proc_table_for_read(THD *thd, Open_tables_state *backup)
+ *cl= get_charset_by_name(cl_name.c_ptr(), MYF(0));
+
+ if (*cl == NULL)
+ {
+ *cl= dflt_cl;
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/*************************************************************************/
+
+Stored_routine_creation_ctx *
+Stored_routine_creation_ctx::load_from_db(THD *thd,
+ const sp_name *name,
+ TABLE *proc_tbl)
{
- TABLE_LIST tables;
- TABLE *table;
- bool not_used;
- DBUG_ENTER("open_proc_table");
+ /* Load character set/collation attributes. */
+
+ CHARSET_INFO *client_cs;
+ CHARSET_INFO *connection_cl;
+ CHARSET_INFO *db_cl;
- thd->reset_n_backup_open_tables_state(backup);
+ const char *db_name= thd->strmake(name->m_db.str, name->m_db.length);
+ const char *sr_name= thd->strmake(name->m_name.str, name->m_name.length);
- bzero((char*) &tables, sizeof(tables));
- tables.db= (char*) "mysql";
- tables.table_name= tables.alias= (char*)"proc";
- if (!(table= open_table(thd, &tables, thd->mem_root, &not_used,
- MYSQL_LOCK_IGNORE_FLUSH)))
+ bool invalid_creation_ctx= FALSE;
+
+ if (load_charset(thd->mem_root,
+ proc_tbl->field[MYSQL_PROC_FIELD_CHARACTER_SET_CLIENT],
+ thd->variables.character_set_client,
+ &client_cs))
{
- thd->restore_backup_open_tables_state(backup);
- DBUG_RETURN(0);
+ sql_print_warning("Stored routine '%s'.'%s': invalid value "
+ "in column mysql.proc.character_set_client.",
+ (const char *) db_name,
+ (const char *) sr_name);
+
+ invalid_creation_ctx= TRUE;
+ }
+
+ if (load_collation(thd->mem_root,
+ proc_tbl->field[MYSQL_PROC_FIELD_COLLATION_CONNECTION],
+ thd->variables.collation_connection,
+ &connection_cl))
+ {
+ sql_print_warning("Stored routine '%s'.'%s': invalid value "
+ "in column mysql.proc.collation_connection.",
+ (const char *) db_name,
+ (const char *) sr_name);
+
+ invalid_creation_ctx= TRUE;
+ }
+
+ if (load_collation(thd->mem_root,
+ proc_tbl->field[MYSQL_PROC_FIELD_DB_COLLATION],
+ NULL,
+ &db_cl))
+ {
+ sql_print_warning("Stored routine '%s'.'%s': invalid value "
+ "in column mysql.proc.db_collation.",
+ (const char *) db_name,
+ (const char *) sr_name);
+
+ invalid_creation_ctx= TRUE;
}
- DBUG_ASSERT(table->s->system_table);
+ if (invalid_creation_ctx)
+ {
+ push_warning_printf(thd,
+ MYSQL_ERROR::WARN_LEVEL_WARN,
+ ER_SR_INVALID_CREATION_CTX,
+ ER(ER_SR_INVALID_CREATION_CTX),
+ (const char *) db_name,
+ (const char *) sr_name);
+ }
- table->reginfo.lock_type= TL_READ;
/*
- We have to ensure we are not blocked by a flush tables, as this
- could lead to a deadlock if we have other tables opened.
+ If we failed to retrieve the database collation, load the default one
+ from the disk.
*/
- if (!(thd->lock= mysql_lock_tables(thd, &table, 1,
- MYSQL_LOCK_IGNORE_FLUSH, &not_used)))
- {
- close_proc_table(thd, backup);
+
+ if (!db_cl)
+ db_cl= get_default_db_collation(thd, name->m_db.str);
+
+ /* Create the context. */
+
+ return new Stored_routine_creation_ctx(client_cs, connection_cl, db_cl);
+}
+
+/*************************************************************************/
+
+/**
+ Open the mysql.proc table for read.
+
+ @param thd Thread context
+ @param backup Pointer to Open_tables_state instance where information about
+ currently open tables will be saved, and from which will be
+ restored when we will end work with mysql.proc.
+
+ @retval
+ 0 Error
+ @retval
+ \# Pointer to TABLE object of mysql.proc
+*/
+
+TABLE *open_proc_table_for_read(THD *thd, Open_tables_state *backup)
+{
+ DBUG_ENTER("open_proc_table_for_read");
+
+ TABLE_LIST table;
+ bzero((char*) &table, sizeof(table));
+ table.db= (char*) "mysql";
+ table.table_name= table.alias= (char*)"proc";
+ table.lock_type= TL_READ;
+
+ if (!open_system_tables_for_read(thd, &table, backup))
+ DBUG_RETURN(table.table);
+ else
DBUG_RETURN(0);
- }
- DBUG_RETURN(table);
}
-/*
+/**
Open the mysql.proc table for update.
- SYNOPSIS
- open_proc_table_for_update()
- thd Thread context
+ @param thd Thread context
- NOTES
+ @note
Table opened with this call should closed using close_thread_tables().
- RETURN
+ @retval
0 Error
- # Pointer to TABLE object of mysql.proc
+ @retval
+ \# Pointer to TABLE object of mysql.proc
*/
static TABLE *open_proc_table_for_update(THD *thd)
{
- TABLE_LIST tables;
- TABLE *table;
- DBUG_ENTER("open_proc_table");
+ DBUG_ENTER("open_proc_table_for_update");
- bzero((char*) &tables, sizeof(tables));
- tables.db= (char*) "mysql";
- tables.table_name= tables.alias= (char*)"proc";
- tables.lock_type= TL_WRITE;
+ TABLE_LIST table;
+ bzero((char*) &table, sizeof(table));
+ table.db= (char*) "mysql";
+ table.table_name= table.alias= (char*)"proc";
+ table.lock_type= TL_WRITE;
- table= open_ltable(thd, &tables, TL_WRITE);
-
- DBUG_RETURN(table);
+ DBUG_RETURN(open_system_table_for_update(thd, &table));
}
-/*
+/**
Find row in open mysql.proc table representing stored routine.
- SYNOPSIS
- db_find_routine_aux()
- thd Thread context
- type Type of routine to find (function or procedure)
- name Name of routine
- table TABLE object for open mysql.proc table.
+ @param thd Thread context
+ @param type Type of routine to find (function or procedure)
+ @param name Name of routine
+ @param table TABLE object for open mysql.proc table.
- RETURN VALUE
- SP_OK - Routine found
- SP_KEY_NOT_FOUND- No routine with given name
+ @retval
+ SP_OK Routine found
+ @retval
+ SP_KEY_NOT_FOUND No routine with given name
*/
static int
db_find_routine_aux(THD *thd, int type, sp_name *name, TABLE *table)
{
- byte key[MAX_KEY_LENGTH]; // db, name, optional key length type
+ uchar key[MAX_KEY_LENGTH]; // db, name, optional key length type
DBUG_ENTER("db_find_routine_aux");
- DBUG_PRINT("enter", ("type: %d name: %.*s",
- type, name->m_name.length, name->m_name.str));
+ DBUG_PRINT("enter", ("type: %d name: %.*s",
+ type, (int) name->m_name.length, name->m_name.str));
/*
Create key to find row. We have to use field->store() to be able to
@@ -217,34 +347,32 @@ db_find_routine_aux(THD *thd, int type, sp_name *name, TABLE *table)
key_copy(key, table->record[0], table->key_info,
table->key_info->key_length);
- if (table->file->index_read_idx(table->record[0], 0,
- key, table->key_info->key_length,
- HA_READ_KEY_EXACT))
+ if (table->file->index_read_idx_map(table->record[0], 0, key, HA_WHOLE_KEY,
+ HA_READ_KEY_EXACT))
DBUG_RETURN(SP_KEY_NOT_FOUND);
DBUG_RETURN(SP_OK);
}
-/*
+/**
Find routine definition in mysql.proc table and create corresponding
sp_head object for it.
- SYNOPSIS
- db_find_routine()
- thd Thread context
- type Type of routine (TYPE_ENUM_PROCEDURE/...)
- name Name of routine
- sphp Out parameter in which pointer to created sp_head
- object is returned (0 in case of error).
+ @param thd Thread context
+ @param type Type of routine (TYPE_ENUM_PROCEDURE/...)
+ @param name Name of routine
+ @param sphp Out parameter in which pointer to created sp_head
+ object is returned (0 in case of error).
- NOTE
+ @note
This function may damage current LEX during execution, so it is good
idea to create temporary LEX and make it active before calling it.
- RETURN VALUE
- 0 - Success
- non-0 - Error (may be one of special codes like SP_KEY_NOT_FOUND)
+ @retval
+ 0 Success
+ @retval
+ non-0 Error (may be one of special codes like SP_KEY_NOT_FOUND)
*/
static int
@@ -261,17 +389,22 @@ db_find_routine(THD *thd, int type, sp_name *name, sp_head **sphp)
uint length;
char buff[65];
String str(buff, sizeof(buff), &my_charset_bin);
- ulong sql_mode;
bool saved_time_zone_used= thd->time_zone_used;
+ ulong sql_mode, saved_mode= thd->variables.sql_mode;
Open_tables_state open_tables_state_backup;
+ Stored_program_creation_ctx *creation_ctx;
+
DBUG_ENTER("db_find_routine");
DBUG_PRINT("enter", ("type: %d name: %.*s",
- type, name->m_name.length, name->m_name.str));
+ type, (int) name->m_name.length, name->m_name.str));
*sphp= 0; // In case of errors
if (!(table= open_proc_table_for_read(thd, &open_tables_state_backup)))
DBUG_RETURN(SP_OPEN_TABLE_FAILED);
+ /* Reset sql_mode during data dictionary operations. */
+ thd->variables.sql_mode= 0;
+
if ((ret= db_find_routine_aux(thd, type, name, table)) != SP_OK)
goto done;
@@ -364,13 +497,14 @@ db_find_routine(THD *thd, int type, sp_name *name, sp_head **sphp)
chistics.comment.str= ptr;
chistics.comment.length= length;
- close_proc_table(thd, &open_tables_state_backup);
+ creation_ctx= Stored_routine_creation_ctx::load_from_db(thd, name, table);
+
+ close_system_tables(thd, &open_tables_state_backup);
table= 0;
ret= db_load_routine(thd, type, name, sphp,
sql_mode, params, returns, body, chistics,
- definer, created, modified);
-
+ definer, created, modified, creation_ctx);
done:
/*
Restore the time zone flag as the timezone usage in proc table
@@ -378,34 +512,62 @@ db_find_routine(THD *thd, int type, sp_name *name, sp_head **sphp)
*/
thd->time_zone_used= saved_time_zone_used;
if (table)
- close_proc_table(thd, &open_tables_state_backup);
+ close_system_tables(thd, &open_tables_state_backup);
+ thd->variables.sql_mode= saved_mode;
DBUG_RETURN(ret);
}
+/**
+ Silence DEPRECATED SYNTAX warnings when loading a stored procedure
+ into the cache.
+*/
+struct Silence_deprecated_warning : public Internal_error_handler
+{
+public:
+ virtual bool handle_error(uint sql_errno, const char *message,
+ MYSQL_ERROR::enum_warning_level level,
+ THD *thd);
+};
+
+bool
+Silence_deprecated_warning::handle_error(uint sql_errno, const char *message,
+ MYSQL_ERROR::enum_warning_level level,
+ THD *thd)
+{
+ if (sql_errno == ER_WARN_DEPRECATED_SYNTAX &&
+ level == MYSQL_ERROR::WARN_LEVEL_WARN)
+ return TRUE;
+
+ return FALSE;
+}
+
+
static int
db_load_routine(THD *thd, int type, sp_name *name, sp_head **sphp,
ulong sql_mode, const char *params, const char *returns,
const char *body, st_sp_chistics &chistics,
- const char *definer, longlong created, longlong modified)
+ const char *definer, longlong created, longlong modified,
+ Stored_program_creation_ctx *creation_ctx)
{
LEX *old_lex= thd->lex, newlex;
String defstr;
- char old_db_buf[NAME_LEN+1];
- LEX_STRING old_db= { old_db_buf, sizeof(old_db_buf) };
- bool dbchanged;
+ char saved_cur_db_name_buf[NAME_LEN+1];
+ 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;
+
char definer_user_name_holder[USERNAME_LENGTH + 1];
- LEX_STRING_WITH_INIT definer_user_name(definer_user_name_holder,
- USERNAME_LENGTH);
+ LEX_STRING definer_user_name= { definer_user_name_holder,
+ USERNAME_LENGTH };
char definer_host_name_holder[HOSTNAME_LENGTH + 1];
- LEX_STRING_WITH_INIT definer_host_name(definer_host_name_holder,
- HOSTNAME_LENGTH);
-
+ LEX_STRING definer_host_name= { definer_host_name_holder, HOSTNAME_LENGTH };
+
int ret;
thd->variables.sql_mode= sql_mode;
@@ -414,11 +576,11 @@ db_load_routine(THD *thd, int type, sp_name *name, sp_head **sphp,
thd->lex= &newlex;
newlex.current_select= NULL;
- parse_user(definer, (uint) strlen(definer),
+ parse_user(definer, strlen(definer),
definer_user_name.str, &definer_user_name.length,
definer_host_name.str, &definer_host_name.length);
- defstr.set_charset(system_charset_info);
+ defstr.set_charset(creation_ctx->get_client_cs());
/*
We have to add DEFINER clause and provide proper routine characterstics in
@@ -439,49 +601,66 @@ db_load_routine(THD *thd, int type, sp_name *name, sp_head **sphp,
goto end;
}
- if ((ret= sp_use_new_db(thd, name->m_db, &old_db, 1, &dbchanged)))
+ /*
+ Change the current database (if needed).
+
+ TODO: why do we force switch here?
+ */
+
+ if (mysql_opt_change_db(thd, &name->m_db, &saved_cur_db_name, TRUE,
+ &cur_db_changed))
+ {
+ ret= SP_INTERNAL_ERROR;
goto end;
+ }
+
+ thd->spcont= NULL;
{
Parser_state parser_state(thd, defstr.c_ptr(), defstr.length());
- thd->m_parser_state= &parser_state;
+
lex_start(thd);
- thd->spcont= NULL;
- ret= MYSQLparse(thd);
- thd->m_parser_state= NULL;
- if (ret == 0)
- {
- /*
- Not strictly necessary to invoke this method here, since we know
- that we've parsed CREATE PROCEDURE/FUNCTION and not an
- UPDATE/DELETE/INSERT/REPLACE/LOAD/CREATE TABLE, but we try to
- maintain the invariant that this method is called for each
- distinct statement, in case its logic is extended with other
- types of analyses in future.
- */
- newlex.set_trg_event_type_for_tables();
- }
- }
+ thd->push_internal_handler(&warning_handler);
+ ret= parse_sql(thd, & parser_state, creation_ctx) || newlex.sphead == NULL;
+ thd->pop_internal_handler();
- if (ret || thd->is_fatal_error || newlex.sphead == NULL)
- {
- sp_head *sp= newlex.sphead;
+ /*
+ Force switching back to the saved current database (if changed),
+ because it may be NULL. In this case, mysql_change_db() would
+ generate an error.
+ */
- if (dbchanged && (ret= mysql_change_db(thd, &old_db, TRUE)))
+ if (cur_db_changed && mysql_change_db(thd, &saved_cur_db_name, TRUE))
+ {
+ delete newlex.sphead;
+ ret= SP_INTERNAL_ERROR;
goto end;
- delete sp;
- ret= SP_PARSE_ERROR;
- }
- else
- {
- if (dbchanged && (ret= mysql_change_db(thd, &old_db, TRUE)))
+ }
+
+ if (ret)
+ {
+ 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);
(*sphp)->optimize();
+ /*
+ Not strictly necessary to invoke this method here, since we know
+ that we've parsed CREATE PROCEDURE/FUNCTION and not an
+ UPDATE/DELETE/INSERT/REPLACE/LOAD/CREATE TABLE, but we try to
+ maintain the invariant that this method is called for each
+ distinct statement, in case its logic is extended with other
+ types of analyses in future.
+ */
+ newlex.set_trg_event_type_for_tables();
}
+
end:
lex_end(thd->lex);
thd->spcont= old_spcont;
@@ -496,10 +675,12 @@ static void
sp_returns_type(THD *thd, String &result, sp_head *sp)
{
TABLE table;
+ TABLE_SHARE share;
Field *field;
- bzero(&table, sizeof(table));
+ bzero((char*) &table, sizeof(table));
+ bzero((char*) &share, sizeof(share));
table.in_use= thd;
- table.s = &table.share_not_to_be_used;
+ table.s = &share;
field= sp->create_result_field(0, 0, &table);
field->sql_type(result);
@@ -512,17 +693,65 @@ sp_returns_type(THD *thd, String &result, sp_head *sp)
delete field;
}
-static int
-db_create_routine(THD *thd, int type, sp_head *sp)
+
+/**
+ Write stored-routine object into mysql.proc.
+
+ This operation stores attributes of the stored procedure/function into
+ the mysql.proc.
+
+ @param thd Thread context.
+ @param type Stored routine type
+ (TYPE_ENUM_PROCEDURE or TYPE_ENUM_FUNCTION).
+ @param sp Stored routine object to store.
+
+ @note Opens and closes the thread tables. Therefore assumes
+ that there are no locked tables in this thread at the time of
+ invocation.
+ Unlike some other DDL statements, *does* close the tables
+ in the end, since the call to this function is normally
+ followed by an implicit grant (sp_grant_privileges())
+ and this subsequent call opens and closes mysql.procs_priv.
+
+ @return Error code. SP_OK is returned on success. Other
+ SP_ constants are used to indicate about errors.
+*/
+
+int
+sp_create_routine(THD *thd, int type, sp_head *sp)
{
int ret;
TABLE *table;
char definer[USER_HOST_BUFF_SIZE];
- DBUG_ENTER("db_create_routine");
- DBUG_PRINT("enter", ("type: %d name: %.*s",type,sp->m_name.length,
+ ulong saved_mode= thd->variables.sql_mode;
+
+ CHARSET_INFO *db_cs= get_default_db_collation(thd, sp->m_db.str);
+
+ enum_check_fields saved_count_cuted_fields;
+
+ bool store_failed= FALSE;
+
+ DBUG_ENTER("sp_create_routine");
+ DBUG_PRINT("enter", ("type: %d name: %.*s",type, (int) sp->m_name.length,
sp->m_name.str));
String retstr(64);
+ DBUG_ASSERT(type == TYPE_ENUM_PROCEDURE ||
+ type == TYPE_ENUM_FUNCTION);
+
+ /* Reset sql_mode during data dictionary operations. */
+ thd->variables.sql_mode= 0;
+
+ /*
+ This statement will be replicated as a statement, even when using
+ row-based replication. The flag will be reset at the end of the
+ statement.
+ */
+ thd->clear_current_stmt_binlog_row_based();
+
+ saved_count_cuted_fields= thd->count_cuted_fields;
+ thd->count_cuted_fields= CHECK_FIELD_WARN;
+
if (!(table= open_proc_table_for_update(thd)))
ret= SP_OPEN_TABLE_FAILED;
else
@@ -530,7 +759,7 @@ db_create_routine(THD *thd, int type, sp_head *sp)
restore_record(table, s->default_values); // Get default values for fields
/* NOTE: all needed privilege checks have been already done. */
- strxmov(definer, thd->lex->definer->user.str, "@",
+ strxnmov(definer, sizeof(definer)-1, thd->lex->definer->user.str, "@",
thd->lex->definer->host.str, NullS);
if (table->s->fields < MYSQL_PROC_FIELD_COUNT)
@@ -552,42 +781,76 @@ db_create_routine(THD *thd, int type, sp_head *sp)
ret= SP_BODY_TOO_LONG;
goto done;
}
- table->field[MYSQL_PROC_FIELD_DB]->
- store(sp->m_db.str, sp->m_db.length, system_charset_info);
- table->field[MYSQL_PROC_FIELD_NAME]->
- store(sp->m_name.str, sp->m_name.length, system_charset_info);
- table->field[MYSQL_PROC_FIELD_TYPE]->
- store((longlong)type, 1);
- table->field[MYSQL_PROC_FIELD_SPECIFIC_NAME]->
- store(sp->m_name.str, sp->m_name.length, system_charset_info);
+
+ store_failed=
+ table->field[MYSQL_PROC_FIELD_DB]->
+ store(sp->m_db.str, sp->m_db.length, system_charset_info);
+
+ store_failed= store_failed ||
+ table->field[MYSQL_PROC_FIELD_NAME]->
+ store(sp->m_name.str, sp->m_name.length, system_charset_info);
+
+ store_failed= store_failed ||
+ table->field[MYSQL_PROC_MYSQL_TYPE]->
+ store((longlong)type, TRUE);
+
+ store_failed= store_failed ||
+ table->field[MYSQL_PROC_FIELD_SPECIFIC_NAME]->
+ store(sp->m_name.str, sp->m_name.length, system_charset_info);
+
if (sp->m_chistics->daccess != SP_DEFAULT_ACCESS)
- table->field[MYSQL_PROC_FIELD_ACCESS]->
- store((longlong)sp->m_chistics->daccess, 1);
- table->field[MYSQL_PROC_FIELD_DETERMINISTIC]->
- store((longlong)(sp->m_chistics->detistic ? 1 : 2), 1);
+ {
+ store_failed= store_failed ||
+ table->field[MYSQL_PROC_FIELD_ACCESS]->
+ store((longlong)sp->m_chistics->daccess, TRUE);
+ }
+
+ store_failed= store_failed ||
+ table->field[MYSQL_PROC_FIELD_DETERMINISTIC]->
+ store((longlong)(sp->m_chistics->detistic ? 1 : 2), TRUE);
+
if (sp->m_chistics->suid != SP_IS_DEFAULT_SUID)
- table->field[MYSQL_PROC_FIELD_SECURITY_TYPE]->
- store((longlong)sp->m_chistics->suid, 1);
- table->field[MYSQL_PROC_FIELD_PARAM_LIST]->
- store(sp->m_params.str, sp->m_params.length, system_charset_info);
+ {
+ store_failed= store_failed ||
+ table->field[MYSQL_PROC_FIELD_SECURITY_TYPE]->
+ store((longlong)sp->m_chistics->suid, TRUE);
+ }
+
+ store_failed= store_failed ||
+ table->field[MYSQL_PROC_FIELD_PARAM_LIST]->
+ store(sp->m_params.str, sp->m_params.length, system_charset_info);
+
if (sp->m_type == TYPE_ENUM_FUNCTION)
{
sp_returns_type(thd, retstr, sp);
- table->field[MYSQL_PROC_FIELD_RETURNS]->
- store(retstr.ptr(), retstr.length(), system_charset_info);
+
+ store_failed= store_failed ||
+ table->field[MYSQL_PROC_FIELD_RETURNS]->
+ store(retstr.ptr(), retstr.length(), system_charset_info);
}
- table->field[MYSQL_PROC_FIELD_BODY]->
- store(sp->m_body.str, sp->m_body.length, system_charset_info);
- table->field[MYSQL_PROC_FIELD_DEFINER]->
- store(definer, (uint)strlen(definer), system_charset_info);
+
+ store_failed= store_failed ||
+ table->field[MYSQL_PROC_FIELD_BODY]->
+ store(sp->m_body.str, sp->m_body.length, system_charset_info);
+
+ store_failed= store_failed ||
+ table->field[MYSQL_PROC_FIELD_DEFINER]->
+ store(definer, (uint)strlen(definer), system_charset_info);
+
((Field_timestamp *)table->field[MYSQL_PROC_FIELD_CREATED])->set_time();
((Field_timestamp *)table->field[MYSQL_PROC_FIELD_MODIFIED])->set_time();
- table->field[MYSQL_PROC_FIELD_SQL_MODE]->
- store((longlong)thd->variables.sql_mode, 1);
+
+ store_failed= store_failed ||
+ table->field[MYSQL_PROC_FIELD_SQL_MODE]->
+ store((longlong)saved_mode, TRUE);
+
if (sp->m_chistics->comment.str)
- table->field[MYSQL_PROC_FIELD_COMMENT]->
- store(sp->m_chistics->comment.str, sp->m_chistics->comment.length,
- system_charset_info);
+ {
+ store_failed= store_failed ||
+ table->field[MYSQL_PROC_FIELD_COMMENT]->
+ store(sp->m_chistics->comment.str, sp->m_chistics->comment.length,
+ system_charset_info);
+ }
if ((sp->m_type == TYPE_ENUM_FUNCTION) &&
!trust_function_creators && mysql_bin_log.is_open())
@@ -619,8 +882,38 @@ db_create_routine(THD *thd, int type, sp_head *sp)
}
}
+ table->field[MYSQL_PROC_FIELD_CHARACTER_SET_CLIENT]->set_notnull();
+ store_failed= store_failed ||
+ table->field[MYSQL_PROC_FIELD_CHARACTER_SET_CLIENT]->store(
+ thd->charset()->csname,
+ strlen(thd->charset()->csname),
+ system_charset_info);
+
+ table->field[MYSQL_PROC_FIELD_COLLATION_CONNECTION]->set_notnull();
+ store_failed= store_failed ||
+ table->field[MYSQL_PROC_FIELD_COLLATION_CONNECTION]->store(
+ thd->variables.collation_connection->name,
+ strlen(thd->variables.collation_connection->name),
+ system_charset_info);
+
+ table->field[MYSQL_PROC_FIELD_DB_COLLATION]->set_notnull();
+ store_failed= store_failed ||
+ table->field[MYSQL_PROC_FIELD_DB_COLLATION]->store(
+ db_cs->name, strlen(db_cs->name), system_charset_info);
+
+ table->field[MYSQL_PROC_FIELD_BODY_UTF8]->set_notnull();
+ store_failed= store_failed ||
+ table->field[MYSQL_PROC_FIELD_BODY_UTF8]->store(
+ sp->m_body_utf8.str, sp->m_body_utf8.length, system_charset_info);
+
+ if (store_failed)
+ {
+ ret= SP_FLD_STORE_FAILED;
+ goto done;
+ }
+
ret= SP_OK;
- if (table->file->write_row(table->record[0]))
+ if (table->file->ha_write_row(table->record[0]))
ret= SP_WRITE_ROW_FAILED;
else if (mysql_bin_log.is_open())
{
@@ -645,44 +938,67 @@ db_create_routine(THD *thd, int type, sp_head *sp)
}
/* Such a statement can always go directly to binlog, no trans cache */
- Query_log_event qinfo(thd, log_query.c_ptr(), log_query.length(), 0,
- FALSE);
- mysql_bin_log.write(&qinfo);
+ thd->binlog_query(THD::MYSQL_QUERY_TYPE,
+ log_query.c_ptr(), log_query.length(), FALSE, FALSE);
}
}
done:
+ thd->count_cuted_fields= saved_count_cuted_fields;
+ thd->variables.sql_mode= saved_mode;
+
close_thread_tables(thd);
DBUG_RETURN(ret);
}
-static int
-db_drop_routine(THD *thd, int type, sp_name *name)
+/**
+ Delete the record for the stored routine object from mysql.proc.
+
+ The operation deletes the record for the stored routine specified by name
+ from the mysql.proc table and invalidates the stored-routine cache.
+
+ @param thd Thread context.
+ @param type Stored routine type
+ (TYPE_ENUM_PROCEDURE or TYPE_ENUM_FUNCTION)
+ @param name Stored routine name.
+
+ @return Error code. SP_OK is returned on success. Other SP_ constants are
+ used to indicate about errors.
+*/
+
+int
+sp_drop_routine(THD *thd, int type, sp_name *name)
{
TABLE *table;
int ret;
- DBUG_ENTER("db_drop_routine");
- DBUG_PRINT("enter", ("type: %d name: %.*s",
- type, name->m_name.length, name->m_name.str));
+ DBUG_ENTER("sp_drop_routine");
+ DBUG_PRINT("enter", ("type: %d name: %.*s",
+ type, (int) name->m_name.length, name->m_name.str));
+
+ DBUG_ASSERT(type == TYPE_ENUM_PROCEDURE ||
+ type == TYPE_ENUM_FUNCTION);
+
+ /*
+ 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.
+ */
+ thd->clear_current_stmt_binlog_row_based();
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->delete_row(table->record[0]))
+ if (table->file->ha_delete_row(table->record[0]))
ret= SP_DELETE_ROW_FAILED;
}
if (ret == SP_OK)
{
- if (mysql_bin_log.is_open())
- {
- thd->clear_error();
- Query_log_event qinfo(thd, thd->query, thd->query_length, 0, FALSE);
- mysql_bin_log.write(&qinfo);
- }
+ write_bin_log(thd, TRUE, thd->query, thd->query_length);
+ sp_cache_invalidate();
}
close_thread_tables(thd);
@@ -690,14 +1006,40 @@ db_drop_routine(THD *thd, int type, sp_name *name)
}
-static int
-db_update_routine(THD *thd, int type, sp_name *name, st_sp_chistics *chistics)
+/**
+ Find and updated the record for the stored routine object in mysql.proc.
+
+ The operation finds the record for the stored routine specified by name
+ in the mysql.proc table and updates it with new attributes. After
+ successful update, the cache is invalidated.
+
+ @param thd Thread context.
+ @param type Stored routine type
+ (TYPE_ENUM_PROCEDURE or TYPE_ENUM_FUNCTION)
+ @param name Stored routine name.
+ @param chistics New values of stored routine attributes to write.
+
+ @return Error code. SP_OK is returned on success. Other SP_ constants are
+ used to indicate about errors.
+*/
+
+int
+sp_update_routine(THD *thd, int type, sp_name *name, st_sp_chistics *chistics)
{
TABLE *table;
int ret;
- DBUG_ENTER("db_update_routine");
- DBUG_PRINT("enter", ("type: %d name: %.*s",
- type, name->m_name.length, name->m_name.str));
+ DBUG_ENTER("sp_update_routine");
+ DBUG_PRINT("enter", ("type: %d name: %.*s",
+ type, (int) name->m_name.length, name->m_name.str));
+
+ DBUG_ASSERT(type == TYPE_ENUM_PROCEDURE ||
+ type == TYPE_ENUM_FUNCTION);
+ /*
+ 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.
+ */
+ thd->clear_current_stmt_binlog_row_based();
if (!(table= open_proc_table_for_update(thd)))
DBUG_RETURN(SP_OPEN_TABLE_FAILED);
@@ -708,26 +1050,25 @@ db_update_routine(THD *thd, int type, sp_name *name, st_sp_chistics *chistics)
((Field_timestamp *)table->field[MYSQL_PROC_FIELD_MODIFIED])->set_time();
if (chistics->suid != SP_IS_DEFAULT_SUID)
table->field[MYSQL_PROC_FIELD_SECURITY_TYPE]->
- store((longlong)chistics->suid, 1);
+ store((longlong)chistics->suid, TRUE);
if (chistics->daccess != SP_DEFAULT_ACCESS)
table->field[MYSQL_PROC_FIELD_ACCESS]->
- store((longlong)chistics->daccess, 1);
+ store((longlong)chistics->daccess, TRUE);
if (chistics->comment.str)
table->field[MYSQL_PROC_FIELD_COMMENT]->store(chistics->comment.str,
chistics->comment.length,
system_charset_info);
- if ((table->file->update_row(table->record[1],table->record[0])))
+ if ((ret= table->file->ha_update_row(table->record[1],table->record[0])) &&
+ ret != HA_ERR_RECORD_IS_THE_SAME)
ret= SP_WRITE_ROW_FAILED;
+ else
+ ret= 0;
}
if (ret == SP_OK)
{
- if (mysql_bin_log.is_open())
- {
- thd->clear_error();
- Query_log_event qinfo(thd, thd->query, thd->query_length, 0, FALSE);
- mysql_bin_log.write(&qinfo);
- }
+ write_bin_log(thd, TRUE, thd->query, thd->query_length);
+ sp_cache_invalidate();
}
close_thread_tables(thd);
@@ -735,187 +1076,13 @@ db_update_routine(THD *thd, int type, sp_name *name, st_sp_chistics *chistics)
}
-struct st_used_field
-{
- const char *field_name;
- uint field_length;
- enum enum_field_types field_type;
- Field *field;
-};
-
-static struct st_used_field init_fields[]=
-{
- { "Db", NAME_LEN, MYSQL_TYPE_STRING, 0},
- { "Name", NAME_LEN, MYSQL_TYPE_STRING, 0},
- { "Type", 9, MYSQL_TYPE_STRING, 0},
- { "Definer", 77, MYSQL_TYPE_STRING, 0},
- { "Modified", 0, MYSQL_TYPE_TIMESTAMP, 0},
- { "Created", 0, MYSQL_TYPE_TIMESTAMP, 0},
- { "Security_type", 1, MYSQL_TYPE_STRING, 0},
- { "Comment", NAME_LEN, MYSQL_TYPE_STRING, 0},
- { 0, 0, MYSQL_TYPE_STRING, 0}
-};
-
-
-static int
-print_field_values(THD *thd, TABLE *table,
- struct st_used_field *used_fields,
- int type, const char *wild)
-{
- Protocol *protocol= thd->protocol;
-
- if (table->field[MYSQL_PROC_FIELD_TYPE]->val_int() == type)
- {
- String db_string;
- String name_string;
- struct st_used_field *used_field= used_fields;
-
- if (get_field(thd->mem_root, used_field->field, &db_string))
- db_string.set_ascii("", 0);
- used_field+= 1;
- get_field(thd->mem_root, used_field->field, &name_string);
-
- if (!wild || !wild[0] || !wild_compare(name_string.ptr(), wild, 0))
- {
- protocol->prepare_for_resend();
- protocol->store(&db_string);
- protocol->store(&name_string);
- for (used_field++;
- used_field->field_name;
- used_field++)
- {
- switch (used_field->field_type) {
- case MYSQL_TYPE_TIMESTAMP:
- {
- MYSQL_TIME tmp_time;
-
- bzero((char *)&tmp_time, sizeof(tmp_time));
- ((Field_timestamp *) used_field->field)->get_time(&tmp_time);
- protocol->store(&tmp_time);
- }
- break;
- default:
- {
- String tmp_string;
-
- get_field(thd->mem_root, used_field->field, &tmp_string);
- protocol->store(&tmp_string);
- }
- break;
- }
- }
- if (protocol->write())
- return SP_INTERNAL_ERROR;
- }
- }
-
- return SP_OK;
-}
-
-
-static int
-db_show_routine_status(THD *thd, int type, const char *wild)
-{
- TABLE *table;
- TABLE_LIST tables;
- int res;
- DBUG_ENTER("db_show_routine_status");
-
- memset(&tables, 0, sizeof(tables));
- tables.db= (char*)"mysql";
- tables.table_name= tables.alias= (char*)"proc";
-
- if (! (table= open_ltable(thd, &tables, TL_READ)))
- {
- res= SP_OPEN_TABLE_FAILED;
- goto done;
- }
- else
- {
- Item *item;
- List<Item> field_list;
- struct st_used_field *used_field;
- TABLE_LIST *leaves= 0;
- st_used_field used_fields[array_elements(init_fields)];
-
- memcpy((char*) used_fields, (char*) init_fields, sizeof(used_fields));
- /* Init header */
- for (used_field= &used_fields[0];
- used_field->field_name;
- used_field++)
- {
- switch (used_field->field_type) {
- case MYSQL_TYPE_TIMESTAMP:
- field_list.push_back(item=new Item_datetime(used_field->field_name));
- break;
- default:
- field_list.push_back(item=new Item_empty_string(used_field->field_name,
- used_field->
- field_length));
- break;
- }
- }
- /* Print header */
- if (thd->protocol->send_fields(&field_list, Protocol::SEND_NUM_ROWS |
- Protocol::SEND_EOF))
- {
- res= SP_INTERNAL_ERROR;
- goto err_case;
- }
-
- /*
- Init fields
-
- tables is not VIEW for sure => we can pass 0 as condition
- */
- thd->lex->select_lex.context.resolve_in_table_list_only(&tables);
- setup_tables(thd, &thd->lex->select_lex.context,
- &thd->lex->select_lex.top_join_list,
- &tables, 0, &leaves, FALSE);
- for (used_field= &used_fields[0];
- used_field->field_name;
- used_field++)
- {
- Item_field *field= new Item_field(&thd->lex->select_lex.context,
- "mysql", "proc",
- used_field->field_name);
- if (!field ||
- !(used_field->field= find_field_in_tables(thd, field, &tables, NULL,
- 0, REPORT_ALL_ERRORS, 1,
- TRUE)))
- {
- res= SP_INTERNAL_ERROR;
- goto err_case1;
- }
- }
-
- table->file->ha_index_init(0);
- if ((res= table->file->index_first(table->record[0])))
- {
- res= (res == HA_ERR_END_OF_FILE) ? 0 : SP_INTERNAL_ERROR;
- goto err_case1;
- }
- if ((res= print_field_values(thd, table, used_fields, type, wild)))
- goto err_case1;
- while (!table->file->index_next(table->record[0]))
- {
- if ((res= print_field_values(thd, table, used_fields, type, wild)))
- goto err_case1;
- }
- res= SP_OK;
- }
-
-err_case1:
- send_eof(thd);
-err_case:
- table->file->ha_index_end();
- close_thread_tables(thd);
-done:
- DBUG_RETURN(res);
-}
+/**
+ Drop all routines in database 'db'
+ @note Close the thread tables, the calling code might want to
+ delete from other system tables afterwards.
+*/
-/* Drop all routines in database 'db' */
int
sp_drop_db_routines(THD *thd, char *db)
{
@@ -929,21 +1096,21 @@ sp_drop_db_routines(THD *thd, char *db)
if (!(table= open_proc_table_for_update(thd)))
goto err;
- table->field[MYSQL_PROC_FIELD_DB]->store(db, (uint) strlen(db), system_charset_info);
+ table->field[MYSQL_PROC_FIELD_DB]->store(db, strlen(db), system_charset_info);
key_len= table->key_info->key_part[0].store_length;
ret= SP_OK;
- table->file->ha_index_init(0);
- if (! table->file->index_read(table->record[0],
- (byte *)table->field[MYSQL_PROC_FIELD_DB]->ptr,
- key_len, HA_READ_KEY_EXACT))
+ table->file->ha_index_init(0, 1);
+ if (! table->file->index_read_map(table->record[0],
+ (uchar *)table->field[MYSQL_PROC_FIELD_DB]->ptr,
+ (key_part_map)1, HA_READ_KEY_EXACT))
{
int nxtres;
bool deleted= FALSE;
do
{
- if (! table->file->delete_row(table->record[0]))
+ if (! table->file->ha_delete_row(table->record[0]))
deleted= TRUE; /* We deleted something */
else
{
@@ -952,7 +1119,7 @@ sp_drop_db_routines(THD *thd, char *db)
break;
}
} while (! (nxtres= table->file->index_next_same(table->record[0],
- (byte *)table->field[MYSQL_PROC_FIELD_DB]->ptr,
+ (uchar *)table->field[MYSQL_PROC_FIELD_DB]->ptr,
key_len)));
if (nxtres != HA_ERR_END_OF_FILE)
ret= SP_KEY_NOT_FOUND;
@@ -968,26 +1135,78 @@ err:
}
-/*****************************************************************************
- PROCEDURE
-******************************************************************************/
+/**
+ Implement SHOW CREATE statement for stored routines.
-/*
+ The operation finds the stored routine object specified by name and then
+ calls sp_head::show_create_routine() for the object.
+
+ @param thd Thread context.
+ @param type Stored routine type
+ (TYPE_ENUM_PROCEDURE or TYPE_ENUM_FUNCTION)
+ @param name Stored routine name.
+
+ @return Error status.
+ @retval FALSE on success
+ @retval TRUE on error
+*/
+
+bool
+sp_show_create_routine(THD *thd, int 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",
+ (int) name->m_name.length,
+ name->m_name.str));
+
+ DBUG_ASSERT(type == TYPE_ENUM_PROCEDURE ||
+ type == TYPE_ENUM_FUNCTION);
+
+ if (type == TYPE_ENUM_PROCEDURE)
+ {
+ /*
+ 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 ((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);
+}
+
+
+/**
Obtain object representing stored procedure/function by its name from
stored procedures cache and looking into mysql.proc if needed.
- SYNOPSIS
- sp_find_routine()
- thd - thread context
- type - type of object (TYPE_ENUM_FUNCTION or TYPE_ENUM_PROCEDURE)
- name - name of procedure
- cp - hash to look routine in
- cache_only - if true perform cache-only lookup
- (Don't look in mysql.proc).
-
- RETURN VALUE
- Non-0 pointer to sp_head object for the procedure, or
- 0 - in case of error.
+ @param thd thread context
+ @param type type of object (TYPE_ENUM_FUNCTION or TYPE_ENUM_PROCEDURE)
+ @param name name of procedure
+ @param cp hash to look routine in
+ @param cache_only if true perform cache-only lookup
+ (Don't look in mysql.proc).
+
+ @retval
+ NonNULL pointer to sp_head object for the procedure
+ @retval
+ NULL in case of error.
*/
sp_head *
@@ -999,9 +1218,9 @@ sp_find_routine(THD *thd, int type, sp_name *name, sp_cache **cp,
thd->variables.max_sp_recursion_depth :
0);
DBUG_ENTER("sp_find_routine");
- DBUG_PRINT("enter", ("name: %.*s.%.*s, type: %d, cache only %d",
- name->m_db.length, name->m_db.str,
- name->m_name.length, name->m_name.str,
+ DBUG_PRINT("enter", ("name: %.*s.%.*s type: %d cache only %d",
+ (int) name->m_db.length, name->m_db.str,
+ (int) name->m_name.length, name->m_name.str,
type, cache_only));
if ((sp= sp_cache_lookup(cp, name)))
@@ -1020,7 +1239,7 @@ sp_find_routine(THD *thd, int type, sp_name *name, sp_cache **cp,
DBUG_PRINT("info", ("found: 0x%lx", (ulong)sp));
if (sp->m_first_free_instance)
{
- DBUG_PRINT("info", ("first free: 0x%lx, level: %lu, flags %x",
+ DBUG_PRINT("info", ("first free: 0x%lx level: %lu flags %x",
(ulong)sp->m_first_free_instance,
sp->m_first_free_instance->m_recursion_level,
sp->m_first_free_instance->m_flags));
@@ -1055,7 +1274,8 @@ sp_find_routine(THD *thd, int type, sp_name *name, sp_cache **cp,
if (db_load_routine(thd, type, name, &new_sp,
sp->m_sql_mode, sp->m_params.str, returns,
sp->m_body.str, *sp->m_chistics, definer,
- sp->m_created, sp->m_modified) == SP_OK)
+ sp->m_created, sp->m_modified,
+ sp->get_creation_ctx()) == SP_OK)
{
sp->m_last_cached_sp->m_next_cached_sp= new_sp;
new_sp->m_recursion_level= level;
@@ -1082,7 +1302,7 @@ sp_find_routine(THD *thd, int type, sp_name *name, sp_cache **cp,
}
-/*
+/**
This is used by sql_acl.cc:mysql_routine_grant() and is used to find
the routines in 'routines'.
*/
@@ -1099,8 +1319,8 @@ sp_exist_routines(THD *thd, TABLE_LIST *routines, bool any, bool no_error)
sp_name *name;
LEX_STRING lex_db;
LEX_STRING lex_name;
- lex_db.length= (uint) strlen(routine->db);
- lex_name.length= (uint) strlen(routine->table_name);
+ lex_db.length= strlen(routine->db);
+ lex_name.length= strlen(routine->table_name);
lex_db.str= thd->strmake(routine->db, lex_db.length);
lex_name.str= thd->strmake(routine->table_name, lex_name.length);
name= new sp_name(lex_db, lex_name, true);
@@ -1131,18 +1351,17 @@ sp_exist_routines(THD *thd, TABLE_LIST *routines, bool any, bool no_error)
}
-/*
+/**
Check if a routine exists in the mysql.proc table, without actually
- parsing the definition. (Used for dropping)
+ parsing the definition. (Used for dropping).
- SYNOPSIS
- sp_routine_exists_in_table()
- thd - thread context
- name - name of procedure
+ @param thd thread context
+ @param name name of procedure
- RETURN VALUE
- 0 - Success
- non-0 - Error; SP_OPEN_TABLE_FAILED or SP_KEY_NOT_FOUND
+ @retval
+ 0 Success
+ @retval
+ non-0 Error; SP_OPEN_TABLE_FAILED or SP_KEY_NOT_FOUND
*/
int
@@ -1158,158 +1377,13 @@ sp_routine_exists_in_table(THD *thd, int type, sp_name *name)
{
if ((ret= db_find_routine_aux(thd, type, name, table)) != SP_OK)
ret= SP_KEY_NOT_FOUND;
- close_proc_table(thd, &open_tables_state_backup);
+ close_system_tables(thd, &open_tables_state_backup);
}
return ret;
}
-int
-sp_create_procedure(THD *thd, sp_head *sp)
-{
- int ret;
- DBUG_ENTER("sp_create_procedure");
- DBUG_PRINT("enter", ("name: %.*s", sp->m_name.length, sp->m_name.str));
-
- ret= db_create_routine(thd, TYPE_ENUM_PROCEDURE, sp);
- DBUG_RETURN(ret);
-}
-
-
-int
-sp_drop_procedure(THD *thd, sp_name *name)
-{
- int ret;
- DBUG_ENTER("sp_drop_procedure");
- DBUG_PRINT("enter", ("name: %.*s", name->m_name.length, name->m_name.str));
-
- ret= db_drop_routine(thd, TYPE_ENUM_PROCEDURE, name);
- if (!ret)
- sp_cache_invalidate();
- DBUG_RETURN(ret);
-}
-
-
-int
-sp_update_procedure(THD *thd, sp_name *name, st_sp_chistics *chistics)
-{
- int ret;
- DBUG_ENTER("sp_update_procedure");
- DBUG_PRINT("enter", ("name: %.*s", name->m_name.length, name->m_name.str));
-
- ret= db_update_routine(thd, TYPE_ENUM_PROCEDURE, name, chistics);
- if (!ret)
- sp_cache_invalidate();
- DBUG_RETURN(ret);
-}
-
-
-int
-sp_show_create_procedure(THD *thd, sp_name *name)
-{
- int ret= SP_KEY_NOT_FOUND;
- sp_head *sp;
- DBUG_ENTER("sp_show_create_procedure");
- DBUG_PRINT("enter", ("name: %.*s", name->m_name.length, name->m_name.str));
-
- /*
- Increase the recursion limit for this statement. SHOW CREATE PROCEDURE
- does not do actual recursion.
- */
- thd->variables.max_sp_recursion_depth++;
- if ((sp= sp_find_routine(thd, TYPE_ENUM_PROCEDURE, name,
- &thd->sp_proc_cache, FALSE)))
- ret= sp->show_create_procedure(thd);
-
- thd->variables.max_sp_recursion_depth--;
- DBUG_RETURN(ret);
-}
-
-
-int
-sp_show_status_procedure(THD *thd, const char *wild)
-{
- int ret;
- DBUG_ENTER("sp_show_status_procedure");
-
- ret= db_show_routine_status(thd, TYPE_ENUM_PROCEDURE, wild);
- DBUG_RETURN(ret);
-}
-
-
-/*****************************************************************************
- FUNCTION
-******************************************************************************/
-
-int
-sp_create_function(THD *thd, sp_head *sp)
-{
- int ret;
- DBUG_ENTER("sp_create_function");
- DBUG_PRINT("enter", ("name: %.*s", sp->m_name.length, sp->m_name.str));
-
- ret= db_create_routine(thd, TYPE_ENUM_FUNCTION, sp);
- DBUG_RETURN(ret);
-}
-
-
-int
-sp_drop_function(THD *thd, sp_name *name)
-{
- int ret;
- DBUG_ENTER("sp_drop_function");
- DBUG_PRINT("enter", ("name: %.*s", name->m_name.length, name->m_name.str));
-
- ret= db_drop_routine(thd, TYPE_ENUM_FUNCTION, name);
- if (!ret)
- sp_cache_invalidate();
- DBUG_RETURN(ret);
-}
-
-
-int
-sp_update_function(THD *thd, sp_name *name, st_sp_chistics *chistics)
-{
- int ret;
- DBUG_ENTER("sp_update_procedure");
- DBUG_PRINT("enter", ("name: %.*s", name->m_name.length, name->m_name.str));
-
- ret= db_update_routine(thd, TYPE_ENUM_FUNCTION, name, chistics);
- if (!ret)
- sp_cache_invalidate();
- DBUG_RETURN(ret);
-}
-
-
-int
-sp_show_create_function(THD *thd, sp_name *name)
-{
- sp_head *sp;
- DBUG_ENTER("sp_show_create_function");
- DBUG_PRINT("enter", ("name: %.*s", name->m_name.length, name->m_name.str));
-
- if ((sp= sp_find_routine(thd, TYPE_ENUM_FUNCTION, name,
- &thd->sp_func_cache, FALSE)))
- {
- int ret= sp->show_create_function(thd);
-
- DBUG_RETURN(ret);
- }
- DBUG_RETURN(SP_KEY_NOT_FOUND);
-}
-
-
-int
-sp_show_status_function(THD *thd, const char *wild)
-{
- int ret;
- DBUG_ENTER("sp_show_status_function");
- ret= db_show_routine_status(thd, TYPE_ENUM_FUNCTION, wild);
- DBUG_RETURN(ret);
-}
-
-
-/*
+/**
Structure that represents element in the set of stored routines
used by statement or routine.
*/
@@ -1317,14 +1391,16 @@ struct Sroutine_hash_entry;
struct Sroutine_hash_entry
{
- /* Set key consisting of one-byte routine type and quoted routine name. */
+ /**
+ 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.
@@ -1333,32 +1409,31 @@ struct Sroutine_hash_entry
};
-extern "C" byte* sp_sroutine_key(const byte *ptr, uint *plen, my_bool first)
+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 (byte *)rn->key.str;
+ 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.
- SYNOPSIS
- sp_get_prelocking_info()
- thd Current thread, thd->lex is the statement to be
- checked.
- need_prelocking OUT TRUE - prelocked mode should be activated
- before executing the statement
- FALSE - Don't activate prelocking
- first_no_prelocking OUT TRUE - Tables used by first routine in
- thd->lex->sroutines_list should be
- prelocked.
- FALSE - Otherwise.
- NOTES
+ @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.
@@ -1378,36 +1453,37 @@ void sp_get_prelocking_info(THD *thd, bool *need_prelocking,
}
-/*
+/**
Auxilary function that adds new element to the set of stored routines
used by statement.
- SYNOPSIS
- add_used_routine()
- lex LEX representing statement
- arena Arena in which memory for new element will be allocated
- key Key for the hash representing set
- belong_to_view Uppermost view which uses this routine
- (0 if routine is not used by view)
+ 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.
- NOTES
- Will also add element to end of 'LEX::sroutines_list' list.
+ @param lex LEX representing statement
+ @param arena Arena in which memory for new element will be
+ allocated
+ @param key Key for the hash representing set
+ @param belong_to_view Uppermost view which uses this routine
+ (0 if routine is not used by view)
- 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.
+ @note
+ Will also add element to end of 'LEX::sroutines_list' list.
- TODO
+ @todo
When we will got rid of these accesses on re-executions we will be
able to allocate memory for hash elements in non-persitent arena
and directly use key values from sp_head::m_sroutines sets instead
of making their copies.
- RETURN VALUE
- TRUE - new element was added.
- FALSE - element was not added (because it is already present in the set).
+ @retval
+ TRUE new element was added.
+ @retval
+ FALSE element was not added (because it is already present in
+ the set).
*/
static bool add_used_routine(LEX *lex, Query_arena *arena,
@@ -1418,7 +1494,7 @@ static bool add_used_routine(LEX *lex, Query_arena *arena,
Query_tables_list::START_SROUTINES_HASH_SIZE,
0, 0, sp_sroutine_key, 0, 0);
- if (!hash_search(&lex->sroutines, (byte *)key->str, key->length))
+ if (!hash_search(&lex->sroutines, (uchar *)key->str, key->length))
{
Sroutine_hash_entry *rn=
(Sroutine_hash_entry *)arena->alloc(sizeof(Sroutine_hash_entry) +
@@ -1428,8 +1504,8 @@ static bool add_used_routine(LEX *lex, Query_arena *arena,
rn->key.length= key->length;
rn->key.str= (char *)rn + sizeof(Sroutine_hash_entry);
memcpy(rn->key.str, key->str, key->length + 1);
- my_hash_insert(&lex->sroutines, (byte *)rn);
- lex->sroutines_list.link_in_list((byte *)rn, (byte **)&rn->next);
+ my_hash_insert(&lex->sroutines, (uchar *)rn);
+ lex->sroutines_list.link_in_list((uchar *)rn, (uchar **)&rn->next);
rn->belong_to_view= belong_to_view;
return TRUE;
}
@@ -1437,24 +1513,22 @@ static bool add_used_routine(LEX *lex, Query_arena *arena,
}
-/*
+/**
Add routine which is explicitly used by statement to the set of stored
routines used by this statement.
- SYNOPSIS
- sp_add_used_routine()
- lex - LEX representing statement
- arena - arena in which memory for new element of the set
- will be allocated
- rt - routine name
- rt_type - routine type (one of TYPE_ENUM_PROCEDURE/...)
+ 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/...)
- NOTES
+ @note
Will also add element to end of 'LEX::sroutines_list' list (and will
take into account that this is explicitly used routine).
-
- To be friendly towards prepared statements one should pass
- persistent arena as second argument.
*/
void sp_add_used_routine(LEX *lex, Query_arena *arena,
@@ -1467,13 +1541,11 @@ 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.
- SYNOPSIS
- sp_remove_not_own_routines()
- lex LEX representing statement
+ @param lex LEX representing statement
*/
void sp_remove_not_own_routines(LEX *lex)
@@ -1487,7 +1559,7 @@ 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, (byte *)not_own_rt);
+ hash_delete(&lex->sroutines, (uchar *)not_own_rt);
}
*(Sroutine_hash_entry **)lex->sroutines_list_own_last= NULL;
@@ -1496,16 +1568,14 @@ void sp_remove_not_own_routines(LEX *lex)
}
-/*
+/**
Merge contents of two hashes representing sets of routines used
by statements or by other routines.
- SYNOPSIS
- sp_update_sp_used_routines()
- dst - hash to which elements should be added
- src - hash from which elements merged
+ @param dst hash to which elements should be added
+ @param src hash from which elements merged
- NOTE
+ @note
This procedure won't create new Sroutine_hash_entry objects,
instead it will simply add elements from source to destination
hash. Thus time of life of elements in destination hash becomes
@@ -1519,24 +1589,23 @@ void 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, (byte *)rt->key.str, rt->key.length))
- my_hash_insert(dst, (byte *)rt);
+ if (!hash_search(dst, (uchar *)rt->key.str, rt->key.length))
+ my_hash_insert(dst, (uchar *)rt);
}
}
-/*
+/**
Add contents of hash representing set of routines to the set of
routines used by statement.
- SYNOPSIS
- sp_update_stmt_used_routines()
- thd Thread context
- lex LEX representing statement
- src Hash representing set from which routines will be added
- belong_to_view Uppermost view which uses these routines, 0 if none
+ @param thd Thread context
+ @param lex LEX representing 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
+ @note
It will also add elements to end of 'LEX::sroutines_list' list.
*/
@@ -1552,18 +1621,17 @@ sp_update_stmt_used_routines(THD *thd, LEX *lex, HASH *src,
}
-/*
+/**
Add contents of list representing set of routines to the set of
routines used by statement.
- SYNOPSIS
- sp_update_stmt_used_routines()
- thd Thread context
- lex LEX representing statement
- src List representing set from which routines will be added
- belong_to_view Uppermost view which uses these routines, 0 if none
+ @param thd Thread context
+ @param lex LEX representing 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
+ @note
It will also add elements to end of 'LEX::sroutines_list' list.
*/
@@ -1576,37 +1644,36 @@ static void sp_update_stmt_used_routines(THD *thd, LEX *lex, SQL_LIST *src,
}
-/*
+/**
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.
- SYNOPSIS
- sp_cache_routines_and_add_tables_aux()
- thd - thread context
- lex - LEX representing statement
- start - first routine from the list of routines to be cached
- (this list defines mentioned sub-set).
- 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.
- tabs_changed - Set to TRUE some tables were added, FALSE otherwise
- NOTE
+ @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.
- RETURN VALUE
- 0 - success
- non-0 - failure
+ @retval
+ 0 success
+ @retval
+ non-0 failure
*/
static int
sp_cache_routines_and_add_tables_aux(THD *thd, LEX *lex,
Sroutine_hash_entry *start,
- bool first_no_prelock, bool *tabs_changed)
+ bool first_no_prelock)
{
int ret= 0;
- bool tabschnd= 0; /* Set if tables changed */
bool first= TRUE;
DBUG_ENTER("sp_cache_routines_and_add_tables_aux");
@@ -1646,7 +1713,7 @@ sp_cache_routines_and_add_tables_aux(THD *thd, LEX *lex,
an error with it's return value without calling my_error(), we
set the generic "mysql.proc table corrupt" error here.
*/
- if (!thd->net.report_error)
+ if (! thd->is_error())
{
/*
SP allows full NAME_LEN chars thus he have to allocate enough
@@ -1655,10 +1722,8 @@ sp_cache_routines_and_add_tables_aux(THD *thd, LEX *lex,
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.)
-
- !! Change 3 with SYSTEM_CHARSET_MBMAXLEN when it's defined.
*/
- char n[NAME_LEN*3*2+2];
+ char n[NAME_LEN*2+2];
/* m_qname.str is not always \0 terminated */
memcpy(n, name.m_qname.str, name.m_qname.length);
@@ -1674,61 +1739,57 @@ sp_cache_routines_and_add_tables_aux(THD *thd, LEX *lex,
{
sp_update_stmt_used_routines(thd, lex, &sp->m_sroutines,
rt->belong_to_view);
- tabschnd|=
- sp->add_used_tables_to_table_list(thd, &lex->query_tables_last,
- 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;
}
- if (tabs_changed) /* it can be NULL */
- *tabs_changed= tabschnd;
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.
- SYNOPSIS
- sp_cache_routines_and_add_tables()
- thd - thread context
- lex - LEX representing statement
- first_no_prelock - If true, don't add tables or cache routines used by
- the body of the first routine (i.e. *start)
- tabs_changed - Set to TRUE some tables were added, FALSE otherwise
-
- RETURN VALUE
- 0 - success
- non-0 - failure
+ @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
*/
int
-sp_cache_routines_and_add_tables(THD *thd, LEX *lex, bool first_no_prelock,
- bool *tabs_changed)
+sp_cache_routines_and_add_tables(THD *thd, LEX *lex, bool first_no_prelock)
{
return sp_cache_routines_and_add_tables_aux(thd, lex,
(Sroutine_hash_entry *)lex->sroutines_list.first,
- first_no_prelock, tabs_changed);
+ first_no_prelock);
}
-/*
- Add all routines used by view to the set of routines used by statement.
+/**
+ 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.
- SYNOPSIS
- sp_cache_routines_and_add_tables_for_view()
- thd Thread context
- lex LEX representing statement
- view Table list element representing view
+ @param thd Thread context
+ @param lex LEX representing statement
+ @param view Table list element representing view
- RETURN VALUE
- 0 - success
- non-0 - failure
+ @retval
+ 0 success
+ @retval
+ non-0 failure
*/
int
@@ -1738,26 +1799,24 @@ sp_cache_routines_and_add_tables_for_view(THD *thd, LEX *lex, TABLE_LIST *view)
(Sroutine_hash_entry **)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,
- NULL);
+ 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.
- SYNOPSIS
- sp_cache_routines_and_add_tables_for_triggers()
- thd thread context
- lex LEX respresenting statement
- table Table list element for table with trigger
+ @param thd thread context
+ @param lex LEX respresenting statement
+ @param table Table list element for table with trigger
- RETURN VALUE
- 0 - success
- non-0 - failure
+ @retval
+ 0 success
+ @retval
+ non-0 failure
*/
int
@@ -1787,6 +1846,7 @@ sp_cache_routines_and_add_tables_for_triggers(THD *thd, LEX *lex,
{
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);
@@ -1797,15 +1857,17 @@ sp_cache_routines_and_add_tables_for_triggers(THD *thd, LEX *lex,
}
ret= sp_cache_routines_and_add_tables_aux(thd, lex,
*last_cached_routine_ptr,
- FALSE, NULL);
+ FALSE);
return ret;
}
-/*
- * Generates the CREATE... string from the table information.
- * Returns TRUE on success, FALSE on (alloc) failure.
- */
+/**
+ Generates the CREATE... string from the table information.
+
+ @return
+ Returns TRUE on success, FALSE on (alloc) failure.
+*/
static bool
create_string(THD *thd, String *buf,
int type,
@@ -1873,70 +1935,3 @@ create_string(THD *thd, String *buf,
buf->append(body, bodylen);
return TRUE;
}
-
-
-
-/*
- Change the current database if needed.
-
- SYNOPSIS
- sp_use_new_db()
- thd thread handle
- new_db new database name (a string and its length)
- old_db [IN] str points to a buffer where to store the old
- database, length contains the size of the buffer
- [OUT] if old db was not NULL, its name is copied
- to the buffer pointed at by str and length is updated
- accordingly. Otherwise str[0] is set to '\0' and length
- is set to 0. The out parameter should be used only if
- the database name has been changed (see dbchangedp).
- dbchangedp [OUT] is set to TRUE if the current database is changed,
- FALSE otherwise. A database is not changed if the old
- name is the same as the new one, both names are empty,
- or an error has occurred.
-
- RETURN VALUE
- 0 success
- 1 access denied or out of memory (the error message is
- set in THD)
-*/
-
-int
-sp_use_new_db(THD *thd, LEX_STRING new_db, LEX_STRING *old_db,
- bool no_access_check, bool *dbchangedp)
-{
- int ret;
- DBUG_ENTER("sp_use_new_db");
- DBUG_PRINT("enter", ("newdb: %s", new_db.str));
-
- /*
- A stored routine always belongs to some database. The
- old database (old_db) might be NULL, but to restore the
- old database we will use mysql_change_db.
- */
- DBUG_ASSERT(new_db.str && new_db.length);
-
- if (thd->db)
- {
- old_db->length= (uint) (strmake(old_db->str, thd->db, old_db->length - 1) -
- old_db->str);
- }
- else
- {
- old_db->str[0]= '\0';
- old_db->length= 0;
- }
-
- /* Don't change the database if the new name is the same as the old one. */
- if (my_strcasecmp(system_charset_info, old_db->str, new_db.str) == 0)
- {
- *dbchangedp= FALSE;
- DBUG_RETURN(0);
- }
-
- ret= mysql_change_db(thd, &new_db, no_access_check);
-
- *dbchangedp= ret == 0;
- DBUG_RETURN(ret);
-}
-
diff --git a/sql/sp.h b/sql/sp.h
index 38b7d43c08f..75088ea0b83 100644
--- a/sql/sp.h
+++ b/sql/sp.h
@@ -29,6 +29,7 @@
#define SP_NO_DB_ERROR -8
#define SP_BAD_IDENTIFIER -9
#define SP_BODY_TOO_LONG -10
+#define SP_FLD_STORE_FAILED -11
/* Drop all routines in database 'db' */
int
@@ -44,37 +45,17 @@ sp_exist_routines(THD *thd, TABLE_LIST *procs, bool any, bool no_error);
int
sp_routine_exists_in_table(THD *thd, int type, sp_name *name);
-int
-sp_create_procedure(THD *thd, sp_head *sp);
-
-int
-sp_drop_procedure(THD *thd, sp_name *name);
-
-
-int
-sp_update_procedure(THD *thd, sp_name *name, st_sp_chistics *chistics);
+bool
+sp_show_create_routine(THD *thd, int type, sp_name *name);
int
-sp_show_create_procedure(THD *thd, sp_name *name);
+sp_create_routine(THD *thd, int type, sp_head *sp);
int
-sp_show_status_procedure(THD *thd, const char *wild);
+sp_update_routine(THD *thd, int type, sp_name *name, st_sp_chistics *chistics);
int
-sp_create_function(THD *thd, sp_head *sp);
-
-int
-sp_drop_function(THD *thd, sp_name *name);
-
-int
-sp_update_function(THD *thd, sp_name *name, st_sp_chistics *chistics);
-
-int
-sp_show_create_function(THD *thd, sp_name *name);
-
-int
-sp_show_status_function(THD *thd, const char *wild);
-
+sp_drop_routine(THD *thd, int type, sp_name *name);
/*
Procedures for pre-caching of stored routines and building table list
@@ -87,31 +68,19 @@ void sp_add_used_routine(LEX *lex, Query_arena *arena,
void sp_remove_not_own_routines(LEX *lex);
void sp_update_sp_used_routines(HASH *dst, HASH *src);
int sp_cache_routines_and_add_tables(THD *thd, LEX *lex,
- bool first_no_prelock,
- bool *tabs_changed);
+ 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);
-extern "C" byte* sp_sroutine_key(const byte *ptr, uint *plen, my_bool first);
+extern "C" uchar* sp_sroutine_key(const uchar *ptr, size_t *plen,
+ my_bool first);
/*
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);
-void close_proc_table(THD *thd, Open_tables_state *backup);
-
-
-/*
- Do a "use new_db". The current db is stored at old_db. If new_db is the
- same as the current one, nothing is changed. dbchangedp is set to true if
- the db was actually changed.
-*/
-
-int
-sp_use_new_db(THD *thd, LEX_STRING new_db, LEX_STRING *old_db,
- bool no_access_check, bool *dbchangedp);
#endif /* _SP_H_ */
diff --git a/sql/sp_cache.cc b/sql/sp_cache.cc
index de4e1efd496..64898915b7e 100644
--- a/sql/sp_cache.cc
+++ b/sql/sp_cache.cc
@@ -39,12 +39,12 @@ public:
inline void insert(sp_head *sp)
{
/* TODO: why don't we check return value? */
- my_hash_insert(&m_hashtable, (const byte *)sp);
+ my_hash_insert(&m_hashtable, (const uchar *)sp);
}
inline sp_head *lookup(char *name, uint namelen)
{
- return (sp_head *)hash_search(&m_hashtable, (const byte *)name, namelen);
+ return (sp_head *)hash_search(&m_hashtable, (const uchar *)name, namelen);
}
#ifdef NOT_USED
@@ -53,7 +53,7 @@ public:
sp_head *sp= lookup(name, namelen);
if (sp)
{
- hash_delete(&m_hashtable, (byte *)sp);
+ hash_delete(&m_hashtable, (uchar *)sp);
return TRUE;
}
return FALSE;
@@ -130,7 +130,7 @@ void sp_cache_insert(sp_cache **cp, sp_head *sp)
return; // End of memory error
c->version= Cversion; // No need to lock when reading long variable
}
- DBUG_PRINT("info",("sp_cache: inserting: %.*s", sp->m_qname.length,
+ DBUG_PRINT("info",("sp_cache: inserting: %.*s", (int) sp->m_qname.length,
sp->m_qname.str));
c->insert(sp);
*cp= c; // Update *cp if it was NULL
@@ -210,21 +210,37 @@ void sp_cache_flush_obsolete(sp_cache **cp)
}
+/**
+ Return the current version of the cache.
+*/
+
+ulong sp_cache_version(sp_cache **cp)
+{
+ sp_cache *c= *cp;
+ if (c)
+ return c->version;
+ return 0;
+}
+
+
/*************************************************************************
Internal functions
*************************************************************************/
-static byte *hash_get_key_for_sp_head(const byte *ptr, uint *plen,
- my_bool first)
+extern "C" uchar *hash_get_key_for_sp_head(const uchar *ptr, size_t *plen,
+ my_bool first);
+extern "C" void hash_free_sp_head(void *p);
+
+uchar *hash_get_key_for_sp_head(const uchar *ptr, size_t *plen,
+ my_bool first)
{
sp_head *sp= (sp_head *)ptr;
*plen= sp->m_qname.length;
- return (byte*) sp->m_qname.str;
+ return (uchar*) sp->m_qname.str;
}
-static void
-hash_free_sp_head(void *p)
+void hash_free_sp_head(void *p)
{
sp_head *sp= (sp_head *)p;
delete sp;
diff --git a/sql/sp_cache.h b/sql/sp_cache.h
index 9d34c9a2fb5..f4d44a1f29f 100644
--- a/sql/sp_cache.h
+++ b/sql/sp_cache.h
@@ -58,5 +58,6 @@ 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);
#endif /* _SP_CACHE_H_ */
diff --git a/sql/sp_head.cc b/sql/sp_head.cc
index b51d97e66c5..ef6cb556f4c 100644
--- a/sql/sp_head.cc
+++ b/sql/sp_head.cc
@@ -1,4 +1,4 @@
-/* Copyright (C) 2002 MySQL AB
+/* Copyright 2002-2008 MySQL AB, 2008 Sun Microsystems, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -32,6 +32,8 @@
#include <my_user.h>
+extern "C" uchar *sp_table_key(const uchar *ptr, size_t *plen, my_bool first);
+
Item_result
sp_map_result_type(enum enum_field_types type)
{
@@ -78,19 +80,20 @@ sp_map_item_type(enum enum_field_types type)
}
-/*
+/**
Return a string representation of the Item value.
- NOTE: If the item has a string result type, the string is escaped
- according to its character set.
+ @param thd thread handle
+ @param str string buffer for representation of the value
- SYNOPSIS
- item a pointer to the Item
- str string buffer for representation of the value
+ @note
+ If the item has a string result type, the string is escaped
+ according to its character set.
- RETURN
- NULL on error
- a pointer to valid a valid string on success
+ @retval
+ NULL on error
+ @retval
+ non-NULL a pointer to valid a valid string on success
*/
static String *
@@ -139,16 +142,12 @@ sp_get_item_value(THD *thd, Item *item, String *str)
}
-/*
- SYNOPSIS
- sp_get_flags_for_command()
-
- DESCRIPTION
- Returns a combination of:
- * sp_head::MULTI_RESULTS: added if the 'cmd' is a command that might
- result in multiple result sets being sent back.
- * sp_head::CONTAINS_DYNAMIC_SQL: added if 'cmd' is one of PREPARE,
- EXECUTE, DEALLOCATE.
+/**
+ Returns a combination of:
+ - sp_head::MULTI_RESULTS: added if the 'cmd' is a command that might
+ result in multiple result sets being sent back.
+ - sp_head::CONTAINS_DYNAMIC_SQL: added if 'cmd' is one of PREPARE,
+ EXECUTE, DEALLOCATE.
*/
uint
@@ -165,34 +164,42 @@ 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:
case SQLCOM_CHECKSUM:
case SQLCOM_CHECK:
case SQLCOM_HA_READ:
+ case SQLCOM_SHOW_AUTHORS:
case SQLCOM_SHOW_BINLOGS:
case SQLCOM_SHOW_BINLOG_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:
case SQLCOM_SHOW_CREATE_FUNC:
case SQLCOM_SHOW_CREATE_PROC:
+ case SQLCOM_SHOW_CREATE_EVENT:
+ case SQLCOM_SHOW_CREATE_TRIGGER:
case SQLCOM_SHOW_DATABASES:
case SQLCOM_SHOW_ERRORS:
case SQLCOM_SHOW_FIELDS:
+ case SQLCOM_SHOW_FUNC_CODE:
case SQLCOM_SHOW_GRANTS:
- case SQLCOM_SHOW_INNODB_STATUS:
+ case SQLCOM_SHOW_ENGINE_STATUS:
+ case SQLCOM_SHOW_ENGINE_LOGS:
+ case SQLCOM_SHOW_ENGINE_MUTEX:
+ case SQLCOM_SHOW_EVENTS:
case SQLCOM_SHOW_KEYS:
- case SQLCOM_SHOW_LOGS:
case SQLCOM_SHOW_MASTER_STAT:
- case SQLCOM_SHOW_MUTEX_STATUS:
case SQLCOM_SHOW_NEW_MASTER:
case SQLCOM_SHOW_OPEN_TABLES:
case SQLCOM_SHOW_PRIVILEGES:
case SQLCOM_SHOW_PROCESSLIST:
+ case SQLCOM_SHOW_PROC_CODE:
case SQLCOM_SHOW_SLAVE_HOSTS:
case SQLCOM_SHOW_SLAVE_STAT:
case SQLCOM_SHOW_STATUS:
@@ -202,10 +209,7 @@ sp_get_flags_for_command(LEX *lex)
case SQLCOM_SHOW_TABLES:
case SQLCOM_SHOW_VARIABLES:
case SQLCOM_SHOW_WARNS:
- case SQLCOM_SHOW_PROC_CODE:
- case SQLCOM_SHOW_FUNC_CODE:
case SQLCOM_REPAIR:
- case SQLCOM_BACKUP_TABLE:
case SQLCOM_RESTORE_TABLE:
flags= sp_head::MULTI_RESULTS;
break;
@@ -246,11 +250,14 @@ sp_get_flags_for_command(LEX *lex)
case SQLCOM_CREATE_TRIGGER:
case SQLCOM_CREATE_USER:
case SQLCOM_ALTER_TABLE:
+ case SQLCOM_GRANT:
+ case SQLCOM_REVOKE:
case SQLCOM_BEGIN:
case SQLCOM_RENAME_TABLE:
case SQLCOM_RENAME_USER:
case SQLCOM_DROP_INDEX:
case SQLCOM_DROP_DB:
+ case SQLCOM_REVOKE_ALL:
case SQLCOM_DROP_USER:
case SQLCOM_DROP_VIEW:
case SQLCOM_DROP_TRIGGER:
@@ -266,6 +273,11 @@ sp_get_flags_for_command(LEX *lex)
case SQLCOM_ALTER_FUNCTION:
case SQLCOM_DROP_PROCEDURE:
case SQLCOM_DROP_FUNCTION:
+ case SQLCOM_CREATE_EVENT:
+ case SQLCOM_ALTER_EVENT:
+ case SQLCOM_DROP_EVENT:
+ case SQLCOM_INSTALL_PLUGIN:
+ case SQLCOM_UNINSTALL_PLUGIN:
flags= sp_head::HAS_COMMIT_OR_ROLLBACK;
break;
default:
@@ -275,18 +287,16 @@ sp_get_flags_for_command(LEX *lex)
return flags;
}
-
-/*
+/**
Prepare an Item for evaluation (call of fix_fields).
- SYNOPSIS
- sp_prepare_func_item()
- thd thread handler
- it_addr pointer on item refernce
+ @param thd thread handler
+ @param it_addr pointer on item refernce
- RETURN
- NULL error
- prepared item
+ @retval
+ NULL error
+ @retval
+ non-NULL prepared item
*/
Item *
@@ -306,17 +316,16 @@ sp_prepare_func_item(THD* thd, Item **it_addr)
}
-/*
+/**
Evaluate an expression and store the result in the field.
- SYNOPSIS
- sp_eval_expr()
- thd - current thread object
- expr_item - the root item of the expression
- result_field - the field to store the result
+ @param thd current thread object
+ @param result_field the field to store the result
+ @param expr_item_ptr the root item of the expression
- RETURN VALUES
+ @retval
FALSE on success
+ @retval
TRUE on error
*/
@@ -327,6 +336,9 @@ sp_eval_expr(THD *thd, Field *result_field, Item **expr_item_ptr)
DBUG_ENTER("sp_eval_expr");
+ if (!*expr_item_ptr)
+ DBUG_RETURN(TRUE);
+
if (!(expr_item= sp_prepare_func_item(thd, expr_item_ptr)))
DBUG_RETURN(TRUE);
@@ -357,7 +369,7 @@ sp_eval_expr(THD *thd, Field *result_field, Item **expr_item_ptr)
thd->abort_on_warning= save_abort_on_warning;
thd->transaction.stmt.modified_non_trans_table= save_stmt_modified_non_trans_table;
- if (thd->net.report_error)
+ if (thd->is_error())
{
/* Return error status if something went wrong. */
err_status= TRUE;
@@ -381,7 +393,7 @@ sp_name::sp_name(THD *thd, char *key, uint key_len)
m_qname.length= key_len - 1;
if ((m_name.str= strchr(m_qname.str, '.')))
{
- m_db.length= (uint) (m_name.str - key);
+ 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;
@@ -396,41 +408,58 @@ sp_name::sp_name(THD *thd, char *key, uint key_len)
m_explicit_name= false;
}
+
+/**
+ Init the qualified name from the db and name.
+*/
void
sp_name::init_qname(THD *thd)
{
const uint dot= !!m_db.length;
/* m_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= thd->alloc(m_sroutines_key.length + 1)))
+ if (!(m_sroutines_key.str= (char*) thd->alloc(m_sroutines_key.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",
- m_db.length, (m_db.length ? m_db.str : ""),
- dot, ".", m_name.length, m_name.str);
+ (int) m_db.length, (m_db.length ? m_db.str : ""),
+ dot, ".",
+ (int) m_name.length, m_name.str);
}
-/*
- Check that the name 'ident' is ok. It's assumed to be an 'ident'
+/**
+ Check that the name 'ident' is ok. It's assumed to be an 'ident'
from the parser, so we only have to check length and trailing spaces.
The former is a standard requirement (and 'show status' assumes a
non-empty name), the latter is a mysql:ism as trailing spaces are
removed by get_field().
-
- RETURN
- TRUE - bad name
- FALSE - name is ok
+
+ @retval
+ TRUE bad name
+ @retval
+ FALSE name is ok
*/
bool
-check_routine_name(LEX_STRING ident)
+check_routine_name(LEX_STRING *ident)
{
- return (!ident.str || !ident.str[0] || ident.str[ident.length-1] == ' ');
-}
+ if (!ident || !ident->str || !ident->str[0] ||
+ ident->str[ident->length-1] == ' ')
+ {
+ my_error(ER_SP_WRONG_NAME, MYF(0), ident->str);
+ return TRUE;
+ }
+ if (check_string_char_length(ident, "", NAME_CHAR_LEN,
+ system_charset_info, 1))
+ {
+ my_error(ER_TOO_LONG_IDENT, MYF(0), ident->str);
+ return TRUE;
+ }
-/* ------------------------------------------------------------------ */
+ return FALSE;
+}
/*
@@ -447,7 +476,7 @@ sp_head::operator new(size_t size) throw()
sp_head *sp;
init_sql_alloc(&own_root, MEM_ROOT_BLOCK_SIZE, MEM_ROOT_PREALLOC);
- sp= (sp_head *) alloc_root(&own_root, (uint) size);
+ sp= (sp_head *) alloc_root(&own_root, size);
if (sp == NULL)
DBUG_RETURN(NULL);
sp->main_mem_root= own_root;
@@ -481,14 +510,20 @@ sp_head::sp_head()
m_flags(0), m_recursion_level(0), m_next_cached_sp(0),
m_cont_level(0)
{
+ const LEX_STRING str_reset= { NULL, 0 };
+
m_first_instance= this;
m_first_free_instance= this;
m_last_cached_sp= this;
m_return_field_def.charset = NULL;
+ /*
+ FIXME: the only use case when name is NULL is events, and it should
+ be rewritten soon. Remove the else part and replace 'if' with
+ an assert when this is done.
+ */
+ m_db= m_name= m_qname= str_reset;
- extern byte *
- sp_table_key(const byte *ptr, uint *plen, my_bool first);
DBUG_ENTER("sp_head::sp_head");
m_backpatch.empty();
@@ -496,6 +531,10 @@ sp_head::sp_head()
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);
+
+ m_body_utf8.str= NULL;
+ m_body_utf8.length= 0;
+
DBUG_VOID_RETURN;
}
@@ -575,9 +614,9 @@ sp_head::init_sp_name(THD *thd, sp_name *spname)
spname->init_qname(thd);
m_sroutines_key.length= spname->m_sroutines_key.length;
- m_sroutines_key.str= memdup_root(thd->mem_root,
- spname->m_sroutines_key.str,
- spname->m_sroutines_key.length + 1);
+ 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;
@@ -588,43 +627,63 @@ sp_head::init_sp_name(THD *thd, sp_name *spname)
void
-sp_head::init_strings(THD *thd, LEX *lex)
+sp_head::set_body_start(THD *thd, const char *begin_ptr)
+{
+ m_body_begin= begin_ptr;
+ thd->m_parser_state->m_lip.body_utf8_start(thd, begin_ptr);
+}
+
+
+void
+sp_head::set_stmt_end(THD *thd)
{
- DBUG_ENTER("sp_head::init_strings");
- const char *endp; /* Used to trim the end */
- /* During parsing, we must use thd->mem_root */
- MEM_ROOT *root= thd->mem_root;
- Lex_input_stream *lip= & thd->m_parser_state->m_lip;
+ Lex_input_stream *lip= & thd->m_parser_state->m_lip; /* shortcut */
+ const char *end_ptr= lip->get_cpp_ptr(); /* shortcut */
+
+ /* Make the string of parameters. */
if (m_param_begin && m_param_end)
{
- m_params.length= (uint) (m_param_end - m_param_begin);
- m_params.str= strmake_root(root,
- (char *)m_param_begin, m_params.length);
+ m_params.length= m_param_end - m_param_begin;
+ m_params.str= thd->strmake(m_param_begin, m_params.length);
}
- /* If ptr has overrun end_of_query then end_of_query is the end */
- endp= (lip->ptr > lip->end_of_query ? lip->end_of_query : lip->ptr);
+ /* Remember end pointer for further dumping of whole statement. */
+
+ thd->lex->stmt_definition_end= end_ptr;
+
+ /* Make the string of body (in the original character set). */
+
+ m_body.length= end_ptr - m_body_begin;
+ m_body.str= thd->strmake(m_body_begin, m_body.length);
+ trim_whitespace(thd->charset(), & m_body);
+
+ /* Make the string of UTF-body. */
+
+ lip->body_utf8_append(end_ptr);
+
+ m_body_utf8.length= lip->get_body_utf8_length();
+ m_body_utf8.str= thd->strmake(lip->get_body_utf8_str(), m_body_utf8.length);
+ trim_whitespace(thd->charset(), & m_body_utf8);
+
/*
- Trim "garbage" at the end. This is sometimes needed with the
- "/ * ! VERSION... * /" wrapper in dump files.
+ Make the string of whole stored-program-definition query (in the
+ original character set).
*/
- endp= skip_rear_comments(thd->charset(), (char*) m_body_begin, (char*) endp);
- m_body.length= (uint) (endp - m_body_begin);
- m_body.str= strmake_root(root, m_body_begin, m_body.length);
- m_defstr.length= (uint) (endp - lip->buf);
- m_defstr.str= strmake_root(root, lip->buf, m_defstr.length);
- DBUG_VOID_RETURN;
+ m_defstr.length= end_ptr - lip->get_cpp_buf();
+ m_defstr.str= thd->strmake(lip->get_cpp_buf(), m_defstr.length);
+ trim_whitespace(thd->charset(), & m_defstr);
}
static TYPELIB *
-create_typelib(MEM_ROOT *mem_root, create_field *field_def, List<String> *src)
+create_typelib(MEM_ROOT *mem_root, Create_field *field_def, List<String> *src)
{
TYPELIB *result= NULL;
CHARSET_INFO *cs= field_def->charset;
DBUG_ENTER("create_typelib");
+
if (src->elements)
{
result= (TYPELIB*) alloc_root(mem_root, sizeof(TYPELIB));
@@ -633,7 +692,7 @@ create_typelib(MEM_ROOT *mem_root, create_field *field_def, List<String> *src)
if (!(result->type_names=(const char **)
alloc_root(mem_root,(sizeof(char *)+sizeof(int))*(result->count+1))))
DBUG_RETURN(0);
- result->type_lengths= (unsigned int *)(result->type_names + result->count+1);
+ result->type_lengths= (uint*)(result->type_names + result->count+1);
List_iterator<String> it(*src);
String conv;
for (uint i=0; i < result->count; i++)
@@ -674,25 +733,20 @@ int
sp_head::create(THD *thd)
{
DBUG_ENTER("sp_head::create");
- int ret;
-
DBUG_PRINT("info", ("type: %d name: %s params: %s body: %s",
m_type, m_name.str, m_params.str, m_body.str));
- if (m_type == TYPE_ENUM_FUNCTION)
- ret= sp_create_function(thd, this);
- else
- ret= sp_create_procedure(thd, this);
-
- DBUG_RETURN(ret);
+ DBUG_RETURN(sp_create_routine(thd, m_type, this));
}
sp_head::~sp_head()
{
+ DBUG_ENTER("sp_head::~sp_head");
destroy();
delete m_next_cached_sp;
if (m_thd)
restore_thd_mem_root(m_thd);
+ DBUG_VOID_RETURN;
}
void
@@ -731,7 +785,7 @@ sp_head::destroy()
}
-/*
+/**
This is only used for result fields from functions (both during
fix_length_and_dec() and evaluation).
*/
@@ -748,7 +802,8 @@ sp_head::create_result_field(uint field_max_length, const char *field_name,
field_length= !m_return_field_def.length ?
field_max_length : m_return_field_def.length;
- field= ::make_field((char*) 0, /* field ptr */
+ field= ::make_field(table->s, /* TABLE_SHARE ptr */
+ (uchar*) 0, /* field ptr */
field_length, /* field [max] length */
(uchar*) "", /* null ptr */
0, /* null bit */
@@ -758,8 +813,10 @@ sp_head::create_result_field(uint field_max_length, const char *field_name,
m_return_field_def.geom_type,
Field::NONE, /* unreg check */
m_return_field_def.interval,
- field_name ? field_name : (const char *) m_name.str,
- table);
+ field_name ? field_name : (const char *) m_name.str);
+
+ if (field)
+ field->init(table);
DBUG_RETURN(field);
}
@@ -773,6 +830,9 @@ int cmp_splocal_locations(Item_splocal * const *a, Item_splocal * const *b)
/*
StoredRoutinesBinlogging
+ This paragraph applies only to statement-based binlogging. Row-based
+ binlogging does not need anything special like this.
+
Top-down overview:
1. Statements
@@ -786,7 +846,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
@@ -807,7 +867,7 @@ int cmp_splocal_locations(Item_splocal * const *a, Item_splocal * const *b)
function execution (grep for start_union_events and stop_union_events)
If the answers are No and Yes, we write the function call into the binary
- log as "SELECT spfunc(<param1value>, <param2value>, ...)".
+ log as "SELECT spfunc(<param1value>, <param2value>, ...)"
4. Miscellaneous issues.
@@ -820,7 +880,7 @@ int cmp_splocal_locations(Item_splocal * const *a, Item_splocal * const *b)
This set is produced by tracking user variable reads during statement
execution.
- Fo SPs, this has the following implications:
+ 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
order to achieve that, we
@@ -833,32 +893,34 @@ int cmp_splocal_locations(Item_splocal * const *a, Item_splocal * const *b)
reset_dynamic(&thd->user_var_events);
calls in several different places. (TODO cosider moving this into
mysql_bin_log.write() function)
+
+ 4.2 Auto_increment storage in binlog
+
+ As we may write two statements to binlog from one single logical statement
+ (case of "SELECT func1(),func2()": it is binlogged as "SELECT func1()" and
+ then "SELECT func2()"), we need to reset auto_increment binlog variables
+ after each binlogged SELECT. Otherwise, the auto_increment value of the
+ first SELECT would be used for the second too.
*/
-/*
- Replace thd->query{_length} with a string that one can write to the binlog
- or the query cache.
-
- SYNOPSIS
- subst_spvars()
- thd Current thread.
- instr Instruction (we look for Item_splocal instances in
- instr->free_list)
- query_str Original query string
-
- DESCRIPTION
+/**
+ 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
- variables with NAME_CONST('sp_var_name', value) calls. To make this string
- suitable for the query cache this function allocates some additional space
- for the query cache flags.
-
- RETURN
- FALSE on success
- thd->query{_length} either has been appropriately replaced or there
- is no need for replacements.
- TRUE out of memory error.
+ variables with NAME_CONST('sp_var_name', value) calls.
+
+ @param thd Current thread.
+ @param instr Instruction (we look for Item_splocal instances in
+ instr->free_list)
+ @param query_str Original query string
+
+ @return
+ - FALSE on success.
+ thd->query{_length} either has been appropriately replaced or there
+ is no need for replacements.
+ - TRUE out of memory error.
*/
static bool
@@ -918,7 +980,7 @@ subst_spvars(THD *thd, sp_instr *instr, LEX_STRING *query_str)
break;
val= (*splocal)->this_item();
- DBUG_PRINT("info", ("print %p", val));
+ DBUG_PRINT("info", ("print 0x%lx", (long) val));
str_value= sp_get_item_value(thd, val, &str_value_holder);
if (str_value)
res|= qbuf.append(*str_value);
@@ -937,7 +999,7 @@ subst_spvars(THD *thd, sp_instr *instr, LEX_STRING *query_str)
query_cache_send_result_to_client function.
*/
buf_len= qbuf.length() + thd->db_length + 1 + QUERY_CACHE_FLAGS_SIZE + 1;
- if ((pbuf= alloc_root(thd->mem_root, buf_len)))
+ if ((pbuf= (char *) alloc_root(thd->mem_root, buf_len)))
{
memcpy(pbuf, qbuf.ptr(), qbuf.length());
pbuf[qbuf.length()]= 0;
@@ -977,23 +1039,27 @@ void sp_head::recursion_level_error(THD *thd)
}
-/*
- Execute the routine. The main instruction jump loop is there
+/**
+ Execute the routine. The main instruction jump loop is there.
Assume the parameters already set.
-
- RETURN
+ @todo
+ - Will write this SP statement into binlog separately
+ (TODO: consider changing the condition to "not inside event union")
+
+ @retval
FALSE on success
+ @retval
TRUE on error
-
*/
bool
sp_head::execute(THD *thd)
{
DBUG_ENTER("sp_head::execute");
- char old_db_buf[NAME_LEN+1];
- LEX_STRING old_db= { old_db_buf, sizeof(old_db_buf) };
- bool dbchanged;
+ char saved_cur_db_name_buf[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;
bool err_status= FALSE;
uint ip= 0;
@@ -1009,9 +1075,12 @@ sp_head::execute(THD *thd)
LEX *old_lex;
Item_change_list old_change_list;
String old_packet;
+ Reprepare_observer *save_reprepare_observer= thd->m_reprepare_observer;
+
+ Object_creation_ctx *saved_creation_ctx;
/* Use some extra margin for possible SP recursion and functions */
- if (check_stack_overrun(thd, 8 * STACK_MIN_SIZE, (char*)&old_packet))
+ if (check_stack_overrun(thd, 8 * STACK_MIN_SIZE, (uchar*)&old_packet))
DBUG_RETURN(TRUE);
/* init per-instruction memroot */
@@ -1046,15 +1115,24 @@ sp_head::execute(THD *thd)
*/
if (m_db.length &&
- (err_status= sp_use_new_db(thd, m_db, &old_db, 0, &dbchanged)))
+ (err_status= mysql_opt_change_db(thd, &m_db, &saved_cur_db_name, FALSE,
+ &cur_db_changed)))
+ {
goto done;
+ }
if ((ctx= thd->spcont))
ctx->clear_handler();
- thd->query_error= 0;
+ thd->is_slave_error= 0;
old_arena= thd->stmt_arena;
/*
+ Switch query context. This has to be done early as this is sometimes
+ allocated trough sql_alloc
+ */
+ saved_creation_ctx= m_creation_ctx->set_n_backup(thd);
+
+ /*
We have to save/restore this info when we are changing call level to
be able properly do close_thread_tables() in instructions.
*/
@@ -1065,6 +1143,25 @@ sp_head::execute(THD *thd)
thd->variables.sql_mode= m_sql_mode;
save_abort_on_warning= thd->abort_on_warning;
thd->abort_on_warning= 0;
+ /**
+ When inside a substatement (a stored function or trigger
+ statement), clear the metadata observer in THD, if any.
+ Remember the value of the observer here, to be able
+ to restore it when leaving the substatement.
+
+ We reset the observer to suppress errors when a substatement
+ uses temporary tables. If a temporary table does not exist
+ at start of the main statement, it's not prelocked
+ and thus is not validated with other prelocked tables.
+
+ Later on, when the temporary table is opened, metadata
+ versions mismatch, expectedly.
+
+ The proper solution for the problem is to re-validate tables
+ of substatements (Bug#12257, Bug#27011, Bug#32868, Bug#33000),
+ but it's not implemented yet.
+ */
+ thd->m_reprepare_observer= 0;
/*
It is also more efficient to save/restore current thd->lex once when
@@ -1099,15 +1196,36 @@ sp_head::execute(THD *thd)
*/
thd->spcont->callers_arena= &backup_arena;
+#if defined(ENABLED_PROFILING) && defined(COMMUNITY_SERVER)
+ /* 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)
+ /*
+ 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.
+ */
+ thd->profiling.finish_current_query();
+ thd->profiling.start_new_query("continuing inside routine");
+#endif
+
i = get_instr(ip); // Returns NULL when we're done.
if (i == NULL)
+ {
+#if defined(ENABLED_PROFILING) && defined(COMMUNITY_SERVER)
+ thd->profiling.discard_current_query();
+#endif
break;
+ }
+
DBUG_PRINT("execute", ("Instruction %u", ip));
+
/* Don't change NOW() in FUNCTION or TRIGGER */
if (!thd->in_sub_stmt)
thd->set_time(); // Make current_time() et al work
@@ -1129,12 +1247,6 @@ sp_head::execute(THD *thd)
err_status= i->execute(thd, &ip);
- /*
- If this SP instruction have sent eof, it has caused no_send_error to be
- set. Clear it back to allow the next instruction to send error. (multi-
- statement execution code clears no_send_error between statements too)
- */
- thd->net.no_send_error= 0;
if (i->free_list)
cleanup_items(i->free_list);
@@ -1177,6 +1289,7 @@ sp_head::execute(THD *thd)
ctx->clear_handler();
ctx->enter_handler(hip);
thd->clear_error();
+ thd->is_fatal_error= 0;
thd->killed= THD::NOT_KILLED;
thd->mysys_var->abort= 0;
continue;
@@ -1184,6 +1297,17 @@ sp_head::execute(THD *thd)
}
} while (!err_status && !thd->killed);
+#if defined(ENABLED_PROFILING) && defined(COMMUNITY_SERVER)
+ thd->profiling.finish_current_query();
+ thd->profiling.start_new_query("tail end of routine");
+#endif
+
+ /* Restore query context. */
+
+ m_creation_ctx->restore_env(thd, saved_creation_ctx);
+
+ /* Restore arena. */
+
thd->restore_active_arena(&execute_arena, &backup_arena);
thd->spcont->pop_all_cursors(); // To avoid memory leaks after an error
@@ -1200,13 +1324,15 @@ sp_head::execute(THD *thd)
thd->derived_tables= old_derived_tables;
thd->variables.sql_mode= save_sql_mode;
thd->abort_on_warning= save_abort_on_warning;
+ thd->m_reprepare_observer= save_reprepare_observer;
thd->stmt_arena= old_arena;
state= EXECUTED;
done:
- DBUG_PRINT("info", ("err_status: %d killed: %d query_error: %d",
- err_status, thd->killed, thd->query_error));
+ DBUG_PRINT("info", ("err_status: %d killed: %d is_slave_error: %d report_error: %d",
+ err_status, thd->killed, thd->is_slave_error,
+ thd->is_error()));
if (thd->killed)
err_status= TRUE;
@@ -1214,14 +1340,14 @@ 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 (dbchanged)
+ if (cur_db_changed && !thd->killed)
{
/*
- No access check when changing back to where we came from.
- (It would generate an error from mysql_change_db() when old_db=="")
+ Force switching back to the saved current database, because it may be
+ NULL. In this case, mysql_change_db() would generate an error.
*/
- if (! thd->killed)
- err_status|= mysql_change_db(thd, &old_db, TRUE);
+
+ err_status|= mysql_change_db(thd, &saved_cur_db_name, TRUE);
}
m_flags&= ~IS_INVOKED;
DBUG_PRINT("info",
@@ -1254,22 +1380,26 @@ sp_head::execute(THD *thd)
#ifndef NO_EMBEDDED_ACCESS_CHECKS
-/*
+/**
set_routine_security_ctx() changes routine security context, and
checks if there is an EXECUTE privilege in new context. If there is
no EXECUTE privilege, it changes the context back and returns a
error.
- SYNOPSIS
- set_routine_security_ctx()
- thd thread handle
- sp stored routine to change the context for
- is_proc TRUE is procedure, FALSE if function
- save_ctx pointer to an old security context
-
- RETURN
- TRUE if there was a error, and the context wasn't changed.
- FALSE if the context was changed.
+ @param thd thread handle
+ @param sp stored routine to change the context for
+ @param is_proc TRUE is procedure, FALSE if function
+ @param save_ctx pointer to an old security context
+
+ @todo
+ - Cache if the definer has the right to use the object on the
+ first usage and only reset the cache if someone does a GRANT
+ statement that 'may' affect this.
+
+ @retval
+ TRUE if there was a error, and the context wasn't changed.
+ @retval
+ FALSE if the context was changed.
*/
bool
@@ -1277,7 +1407,11 @@ set_routine_security_ctx(THD *thd, sp_head *sp, bool is_proc,
Security_context **save_ctx)
{
*save_ctx= 0;
- if (sp_change_security_context(thd, sp, save_ctx))
+ if (sp->m_chistics->suid != SP_IS_NOT_SUID &&
+ sp->m_security_ctx.change_security_context(thd, &sp->m_definer_user,
+ &sp->m_definer_host,
+ &sp->m_db,
+ save_ctx))
return TRUE;
/*
@@ -1294,7 +1428,7 @@ set_routine_security_ctx(THD *thd, sp_head *sp, bool is_proc,
check_routine_access(thd, EXECUTE_ACL,
sp->m_db.str, sp->m_name.str, is_proc, FALSE))
{
- sp_restore_security_context(thd, *save_ctx);
+ sp->m_security_ctx.restore_security_context(thd, *save_ctx);
*save_ctx= 0;
return TRUE;
}
@@ -1304,30 +1438,36 @@ set_routine_security_ctx(THD *thd, sp_head *sp, bool is_proc,
#endif // ! NO_EMBEDDED_ACCESS_CHECKS
-/*
- Execute a trigger:
- - changes security context for triggers
- - switch to new memroot
- - call sp_head::execute
- - restore old memroot
- - restores security context
+/**
+ Execute trigger stored program.
+
+ - changes security context for triggers
+ - switch to new memroot
+ - call sp_head::execute
+ - restore old memroot
+ - restores security context
+
+ @param thd Thread handle
+ @param db database name
+ @param table table name
+ @param grant_info GRANT_INFO structure to be filled with
+ information about definer's privileges
+ on subject table
+
+ @todo
+ - TODO: we should create sp_rcontext once per command and reuse it
+ on subsequent executions of a trigger.
- SYNOPSIS
- sp_head::execute_trigger()
- thd Thread handle
- db database name
- table table name
- grant_info GRANT_INFO structure to be filled with
- information about definer's privileges
- on subject table
-
- RETURN
+ @retval
FALSE on success
+ @retval
TRUE on error
*/
bool
-sp_head::execute_trigger(THD *thd, const char *db, const char *table,
+sp_head::execute_trigger(THD *thd,
+ const LEX_STRING *db_name,
+ const LEX_STRING *table_name,
GRANT_INFO *grant_info)
{
sp_rcontext *octx = thd->spcont;
@@ -1341,26 +1481,43 @@ sp_head::execute_trigger(THD *thd, const char *db, const char *table,
DBUG_PRINT("info", ("trigger %s", m_name.str));
#ifndef NO_EMBEDDED_ACCESS_CHECKS
- Security_context *save_ctx;
- if (sp_change_security_context(thd, this, &save_ctx))
+ Security_context *save_ctx= NULL;
+
+
+ if (m_chistics->suid != SP_IS_NOT_SUID &&
+ m_security_ctx.change_security_context(thd,
+ &m_definer_user,
+ &m_definer_host,
+ &m_db,
+ &save_ctx))
DBUG_RETURN(TRUE);
/*
- NOTE: TRIGGER_ACL should be used here.
+ Fetch information about table-level privileges for subject table into
+ GRANT_INFO instance. The access check itself will happen in
+ Item_trigger_field, where this information will be used along with
+ information about column-level privileges.
*/
- if (check_global_access(thd, SUPER_ACL))
+
+ fill_effective_table_privileges(thd,
+ grant_info,
+ db_name->str,
+ table_name->str);
+
+ /* Check that the definer has TRIGGER privilege on the subject table. */
+
+ if (!(grant_info->privilege & TRIGGER_ACL))
{
- sp_restore_security_context(thd, save_ctx);
+ char priv_desc[128];
+ get_privilege_desc(priv_desc, sizeof(priv_desc), TRIGGER_ACL);
+
+ my_error(ER_TABLEACCESS_DENIED_ERROR, MYF(0), priv_desc,
+ thd->security_ctx->priv_user, thd->security_ctx->host_or_ip,
+ table_name->str);
+
+ m_security_ctx.restore_security_context(thd, save_ctx);
DBUG_RETURN(TRUE);
}
-
- /*
- Fetch information about table-level privileges to GRANT_INFO
- structure for subject table. Check of privileges that will use it
- and information about column-level privileges will happen in
- Item_trigger_field::fix_fields().
- */
- fill_effective_table_privileges(thd, grant_info, db, table);
#endif // NO_EMBEDDED_ACCESS_CHECKS
/*
@@ -1395,9 +1552,11 @@ sp_head::execute_trigger(THD *thd, const char *db, const char *table,
err_with_cleanup:
thd->restore_active_arena(&call_arena, &backup_arena);
+
#ifndef NO_EMBEDDED_ACCESS_CHECKS
- sp_restore_security_context(thd, save_ctx);
+ m_security_ctx.restore_security_context(thd, save_ctx);
#endif // NO_EMBEDDED_ACCESS_CHECKS
+
delete nctx;
call_arena.free_items();
free_root(&call_mem_root, MYF(0));
@@ -1410,8 +1569,9 @@ err_with_cleanup:
}
-/*
- Execute a function:
+/**
+ Execute a function.
+
- evaluate parameters
- changes security context for SUID routines
- switch to new memroot
@@ -1420,17 +1580,25 @@ err_with_cleanup:
- evaluate the return value
- restores security context
- SYNOPSIS
- sp_head::execute_function()
- thd Thread handle
- argp Passed arguments (these are items from containing
- statement?)
- argcount Number of passed arguments. We need to check if this is
- correct.
- return_value_fld Save result here.
-
- RETURN
+ @param thd Thread handle
+ @param argp Passed arguments (these are items from containing
+ statement?)
+ @param argcount Number of passed arguments. We need to check if
+ this is correct.
+ @param return_value_fld Save result here.
+
+ @todo
+ We should create sp_rcontext once per command and reuse
+ it on subsequent executions of a function/trigger.
+
+ @todo
+ In future we should associate call arena/mem_root with
+ sp_rcontext and allocate all these objects (and sp_rcontext
+ itself) on it directly rather than juggle with arenas.
+
+ @retval
FALSE on success
+ @retval
TRUE on error
*/
@@ -1449,10 +1617,11 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount,
MEM_ROOT call_mem_root;
Query_arena call_arena(&call_mem_root, Query_arena::INITIALIZED_FOR_SP);
Query_arena backup_arena;
-
DBUG_ENTER("sp_head::execute_function");
DBUG_PRINT("info", ("function %s", m_name.str));
+ LINT_INIT(binlog_save_options);
+
/*
Check that the function is called with all specified arguments.
@@ -1513,7 +1682,12 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount,
goto err_with_cleanup;
}
- need_binlog_call= mysql_bin_log.is_open() && (thd->options & OPTION_BIN_LOG);
+ /*
+ If row-based binlogging, we don't need to binlog the function's call, let
+ 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;
/*
Remember the original arguments for unrolled replication of functions
@@ -1556,7 +1730,6 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount,
}
#endif
- binlog_save_options= thd->options;
if (need_binlog_call)
{
query_id_t q;
@@ -1577,6 +1750,8 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount,
q= global_query_id;
VOID(pthread_mutex_unlock(&LOCK_thread_count));
mysql_bin_log.start_union_events(thd, q + 1);
+ binlog_save_options= thd->options;
+ thd->options&= ~OPTION_BIN_LOG;
}
/*
@@ -1589,27 +1764,30 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount,
*/
thd->set_n_backup_active_arena(&call_arena, &backup_arena);
- thd->options&= ~OPTION_BIN_LOG;
err_status= execute(thd);
- thd->options= binlog_save_options;
thd->restore_active_arena(&call_arena, &backup_arena);
if (need_binlog_call)
- mysql_bin_log.stop_union_events(thd);
-
- if (need_binlog_call && thd->binlog_evt_union.unioned_events)
{
- Query_log_event qinfo(thd, binlog_buf.ptr(), binlog_buf.length(),
- thd->binlog_evt_union.unioned_events_trans, FALSE);
- if (mysql_bin_log.write(&qinfo) &&
- thd->binlog_evt_union.unioned_events_trans)
+ mysql_bin_log.stop_union_events(thd);
+ thd->options= binlog_save_options;
+ if (thd->binlog_evt_union.unioned_events)
{
- push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_UNKNOWN_ERROR,
- "Invoked ROUTINE modified a transactional table but MySQL "
- "failed to reflect this change in the binary log");
+ Query_log_event qinfo(thd, binlog_buf.ptr(), binlog_buf.length(),
+ thd->binlog_evt_union.unioned_events_trans, FALSE);
+ if (mysql_bin_log.write(&qinfo) &&
+ thd->binlog_evt_union.unioned_events_trans)
+ {
+ push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_UNKNOWN_ERROR,
+ "Invoked ROUTINE modified a transactional table but MySQL "
+ "failed to reflect this change in the binary log");
+ }
+ reset_dynamic(&thd->user_var_events);
+ /* Forget those values, in case more function calls are binlogged: */
+ thd->stmt_depends_on_first_successful_insert_id_in_prev_stmt= 0;
+ thd->auto_inc_intervals_in_cur_stmt_for_binlog.empty();
}
- reset_dynamic(&thd->user_var_events);
}
if (!err_status)
@@ -1624,7 +1802,7 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount,
}
#ifndef NO_EMBEDDED_ACCESS_CHECKS
- sp_restore_security_context(thd, save_security_ctx);
+ m_security_ctx.restore_security_context(thd, save_security_ctx);
#endif
err_with_cleanup:
@@ -1637,14 +1815,8 @@ err_with_cleanup:
}
-/*
+/**
Execute a procedure.
- SYNOPSIS
- sp_head::execute_procedure()
- thd Thread handle
- args List of values passed as arguments.
-
- DESCRIPTION
The function does the following steps:
- Set all parameters
@@ -1653,8 +1825,12 @@ err_with_cleanup:
- copy back values of INOUT and OUT parameters
- restores security context
- RETURN
+ @param thd Thread handle
+ @param args List of values passed as arguments.
+
+ @retval
FALSE on success
+ @retval
TRUE on error
*/
@@ -1665,6 +1841,8 @@ sp_head::execute_procedure(THD *thd, List<Item> *args)
uint params = m_pcont->context_var_count();
sp_rcontext *save_spcont, *octx;
sp_rcontext *nctx = NULL;
+ bool save_enable_slow_log= false;
+ bool save_log_general= false;
DBUG_ENTER("sp_head::execute_procedure");
DBUG_PRINT("info", ("procedure %s", m_name.str));
@@ -1709,7 +1887,7 @@ sp_head::execute_procedure(THD *thd, List<Item> *args)
{
List_iterator<Item> it_args(*args);
- DBUG_PRINT("info",(" %.*s: eval args", m_name.length, m_name.str));
+ DBUG_PRINT("info",(" %.*s: eval args", (int) m_name.length, m_name.str));
for (uint i= 0 ; i < params ; i++)
{
@@ -1771,9 +1949,22 @@ sp_head::execute_procedure(THD *thd, List<Item> *args)
thd->rollback_item_tree_changes();
}
- DBUG_PRINT("info",(" %.*s: eval args done", m_name.length, m_name.str));
+ DBUG_PRINT("info",(" %.*s: eval args done", (int) m_name.length,
+ m_name.str));
+ }
+ if (!(m_flags & LOG_SLOW_STATEMENTS) && thd->enable_slow_log)
+ {
+ DBUG_PRINT("info", ("Disabling slow log for the execution"));
+ save_enable_slow_log= true;
+ thd->enable_slow_log= FALSE;
+ }
+ if (!(m_flags & LOG_GENERAL_LOG) && !(thd->options & 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->spcont= nctx;
#ifndef NO_EMBEDDED_ACCESS_CHECKS
@@ -1785,6 +1976,10 @@ sp_head::execute_procedure(THD *thd, List<Item> *args)
if (!err_status)
err_status= execute(thd);
+ if (save_log_general)
+ thd->options &= ~OPTION_LOG_OFF;
+ if (save_enable_slow_log)
+ thd->enable_slow_log= true;
/*
In the case when we weren't able to employ reuse mechanism for
OUT/INOUT paranmeters, we should reallocate memory. This
@@ -1828,7 +2023,7 @@ sp_head::execute_procedure(THD *thd, List<Item> *args)
#ifndef NO_EMBEDDED_ACCESS_CHECKS
if (save_security_ctx)
- sp_restore_security_context(thd, save_security_ctx);
+ m_security_ctx.restore_security_context(thd, save_security_ctx);
#endif
if (!save_spcont)
@@ -1842,7 +2037,7 @@ sp_head::execute_procedure(THD *thd, List<Item> *args)
/**
- @brief Reset lex during parsing, before we parse a sub statement.
+ Reset lex during parsing, before we parse a sub statement.
@param thd Thread handler.
@@ -1876,8 +2071,6 @@ sp_head::reset_lex(THD *thd)
sublex->trg_table_fields.empty();
sublex->sp_lex_in_use= FALSE;
- sublex->in_comment= oldlex->in_comment;
-
/* Reset type info. */
sublex->charset= NULL;
@@ -1889,7 +2082,7 @@ sp_head::reset_lex(THD *thd)
DBUG_RETURN(FALSE);
}
-// Restore lex during parsing, after we have parsed a sub statement.
+/// Restore lex during parsing, after we have parsed a sub statement.
void
sp_head::restore_lex(THD *thd)
{
@@ -1906,6 +2099,14 @@ sp_head::restore_lex(THD *thd)
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;
+
+ /*
Add routines which are used by statement to respective set for
this routine.
*/
@@ -1924,6 +2125,9 @@ sp_head::restore_lex(THD *thd)
DBUG_VOID_RETURN;
}
+/**
+ Put the instruction on the backpatch list, associated with the label.
+*/
void
sp_head::push_backpatch(sp_instr *i, sp_label_t *lab)
{
@@ -1937,6 +2141,10 @@ sp_head::push_backpatch(sp_instr *i, sp_label_t *lab)
}
}
+/**
+ Update all instruction with this label in the backpatch list to
+ the current position.
+*/
void
sp_head::backpatch(sp_label_t *lab)
{
@@ -1957,39 +2165,35 @@ sp_head::backpatch(sp_label_t *lab)
DBUG_VOID_RETURN;
}
-/*
- Prepare an instance of create_field for field creation (fill all necessary
+/**
+ Prepare an instance of Create_field for field creation (fill all necessary
attributes).
- SYNOPSIS
- sp_head::fill_field_definition()
- thd [IN] Thread handle
- lex [IN] Yacc parsing context
- field_type [IN] Field type
- field_def [OUT] An instance of create_field to be filled
+ @param[in] thd Thread handle
+ @param[in] lex Yacc parsing context
+ @param[in] field_type Field type
+ @param[out] field_def An instance of create_field to be filled
- RETURN
+ @retval
FALSE on success
+ @retval
TRUE on error
*/
bool
sp_head::fill_field_definition(THD *thd, LEX *lex,
enum enum_field_types field_type,
- create_field *field_def)
+ Create_field *field_def)
{
- HA_CREATE_INFO sp_db_info;
LEX_STRING cmt = { 0, 0 };
uint unused1= 0;
int unused2= 0;
- load_db_opt_by_name(thd, m_db.str, &sp_db_info);
-
if (field_def->init(thd, (char*) "", field_type, lex->length, lex->dec,
lex->type, (Item*) 0, (Item*) 0, &cmt, 0,
&lex->interval_list,
- (lex->charset ? lex->charset :
- sp_db_info.default_table_charset),
+ lex->charset ? lex->charset :
+ thd->variables.collation_database,
lex->uint_geom_type))
return TRUE;
@@ -2064,10 +2268,10 @@ void
sp_head::set_definer(const char *definer, uint definerlen)
{
char user_name_holder[USERNAME_LENGTH + 1];
- LEX_STRING_WITH_INIT user_name(user_name_holder, USERNAME_LENGTH);
+ LEX_STRING user_name= { user_name_holder, USERNAME_LENGTH };
char host_name_holder[HOSTNAME_LENGTH + 1];
- LEX_STRING_WITH_INIT host_name(host_name_holder, HOSTNAME_LENGTH);
+ LEX_STRING host_name= { host_name_holder, HOSTNAME_LENGTH };
parse_user(definer, definerlen, user_name.str, &user_name.length,
host_name.str, &host_name.length);
@@ -2118,18 +2322,17 @@ sp_head::restore_thd_mem_root(THD *thd)
}
-/*
- Check if a user has access right to a routine
-
- SYNOPSIS
- check_show_routine_access()
- thd Thread handler
- sp SP
- full_access Set to 1 if the user has SELECT right to the
- 'mysql.proc' able or is the owner of the routine
- RETURN
- 0 ok
- 1 error
+/**
+ 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
+ @retval
+ false ok
+ @retval
+ true error
*/
bool check_show_routine_access(THD *thd, sp_head *sp, bool *full_access)
@@ -2138,7 +2341,7 @@ bool check_show_routine_access(THD *thd, sp_head *sp, bool *full_access)
bzero((char*) &tables,sizeof(tables));
tables.db= (char*) "mysql";
tables.table_name= tables.alias= (char*) "proc";
- *full_access= (!check_table_access(thd, SELECT_ACL, &tables, 1) ||
+ *full_access= (!check_table_access(thd, SELECT_ACL, &tables, 1, TRUE) ||
(!strcmp(sp->m_definer_user.str,
thd->security_ctx->priv_user) &&
!strcmp(sp->m_definer_host.str,
@@ -2150,61 +2353,116 @@ bool check_show_routine_access(THD *thd, sp_head *sp, bool *full_access)
}
-int
-sp_head::show_create_procedure(THD *thd)
+/**
+ Implement SHOW CREATE statement for stored routines.
+
+ @param thd Thread context.
+ @param type Stored routine type
+ (TYPE_ENUM_PROCEDURE or TYPE_ENUM_FUNCTION)
+
+ @return Error status.
+ @retval FALSE on success
+ @retval TRUE on error
+*/
+
+bool
+sp_head::show_create_routine(THD *thd, int type)
{
+ const char *col1_caption= type == TYPE_ENUM_PROCEDURE ?
+ "Procedure" : "Function";
+
+ const char *col3_caption= type == TYPE_ENUM_PROCEDURE ?
+ "Create Procedure" : "Create Function";
+
+ bool err_status;
+
Protocol *protocol= thd->protocol;
- char buff[2048];
- String buffer(buff, sizeof(buff), system_charset_info);
- int res;
- List<Item> field_list;
- byte *sql_mode_str;
- ulong sql_mode_len;
+ List<Item> fields;
+
+ LEX_STRING sql_mode;
+
bool full_access;
- DBUG_ENTER("sp_head::show_create_procedure");
- DBUG_PRINT("info", ("procedure %s", m_name.str));
- LINT_INIT(sql_mode_str);
- LINT_INIT(sql_mode_len);
+ DBUG_ENTER("sp_head::show_create_routine");
+ DBUG_PRINT("info", ("routine %s", m_name.str));
+
+ DBUG_ASSERT(type == TYPE_ENUM_PROCEDURE ||
+ type == TYPE_ENUM_FUNCTION);
if (check_show_routine_access(thd, this, &full_access))
- DBUG_RETURN(1);
+ DBUG_RETURN(TRUE);
- sql_mode_str=
- sys_var_thd_sql_mode::symbolic_mode_representation(thd,
- m_sql_mode,
- &sql_mode_len);
- field_list.push_back(new Item_empty_string("Procedure", NAME_LEN));
- field_list.push_back(new Item_empty_string("sql_mode", sql_mode_len));
- // 1024 is for not to confuse old clients
- Item_empty_string *definition=
- new Item_empty_string("Create Procedure", max(buffer.length(),1024));
- definition->maybe_null= TRUE;
- field_list.push_back(definition);
+ sys_var_thd_sql_mode::symbolic_mode_representation(
+ thd, m_sql_mode, &sql_mode);
+
+ /* Send header. */
+
+ fields.push_back(new Item_empty_string(col1_caption, NAME_CHAR_LEN));
+ fields.push_back(new Item_empty_string("sql_mode", sql_mode.length));
+
+ {
+ /*
+ NOTE: SQL statement field must be not less than 1024 in order not to
+ confuse old clients.
+ */
+
+ Item_empty_string *stmt_fld=
+ new Item_empty_string(col3_caption,
+ max(m_defstr.length, 1024));
+
+ stmt_fld->maybe_null= TRUE;
+
+ fields.push_back(stmt_fld);
+ }
+
+ fields.push_back(new Item_empty_string("character_set_client",
+ MY_CS_NAME_SIZE));
+
+ fields.push_back(new Item_empty_string("collation_connection",
+ MY_CS_NAME_SIZE));
+
+ fields.push_back(new Item_empty_string("Database Collation",
+ MY_CS_NAME_SIZE));
+
+ if (protocol->send_fields(&fields,
+ Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
+ {
+ DBUG_RETURN(TRUE);
+ }
+
+ /* Send data. */
- if (protocol->send_fields(&field_list, Protocol::SEND_NUM_ROWS |
- Protocol::SEND_EOF))
- DBUG_RETURN(1);
protocol->prepare_for_resend();
+
protocol->store(m_name.str, m_name.length, system_charset_info);
- protocol->store((char*) sql_mode_str, sql_mode_len, system_charset_info);
+ protocol->store(sql_mode.str, sql_mode.length, system_charset_info);
+
if (full_access)
- protocol->store(m_defstr.str, m_defstr.length, system_charset_info);
+ protocol->store(m_defstr.str, m_defstr.length,
+ m_creation_ctx->get_client_cs());
else
protocol->store_null();
- res= protocol->write();
- send_eof(thd);
- DBUG_RETURN(res);
+
+ protocol->store(m_creation_ctx->get_client_cs()->csname, system_charset_info);
+ protocol->store(m_creation_ctx->get_connection_cl()->name, system_charset_info);
+ protocol->store(m_creation_ctx->get_db_cl()->name, system_charset_info);
+
+ err_status= protocol->write();
+
+ if (!err_status)
+ my_eof(thd);
+
+ DBUG_RETURN(err_status);
}
-/*
- Add instruction to SP
- SYNOPSIS
- sp_head::add_instr()
- instr Instruction
+
+/**
+ Add instruction to SP.
+
+ @param instr Instruction
*/
void sp_head::add_instr(sp_instr *instr)
@@ -2218,71 +2476,24 @@ void sp_head::add_instr(sp_instr *instr)
entire stored procedure, as their life span is equal.
*/
instr->mem_root= &main_mem_root;
- insert_dynamic(&m_instr, (gptr)&instr);
-}
-
-
-int
-sp_head::show_create_function(THD *thd)
-{
- Protocol *protocol= thd->protocol;
- char buff[2048];
- String buffer(buff, sizeof(buff), system_charset_info);
- int res;
- List<Item> field_list;
- byte *sql_mode_str;
- ulong sql_mode_len;
- bool full_access;
- DBUG_ENTER("sp_head::show_create_function");
- DBUG_PRINT("info", ("procedure %s", m_name.str));
- LINT_INIT(sql_mode_str);
- LINT_INIT(sql_mode_len);
-
- if (check_show_routine_access(thd, this, &full_access))
- DBUG_RETURN(1);
-
- sql_mode_str=
- sys_var_thd_sql_mode::symbolic_mode_representation(thd,
- m_sql_mode,
- &sql_mode_len);
- field_list.push_back(new Item_empty_string("Function",NAME_LEN));
- field_list.push_back(new Item_empty_string("sql_mode", sql_mode_len));
- Item_empty_string *definition=
- new Item_empty_string("Create Function", max(buffer.length(),1024));
- definition->maybe_null= TRUE;
- field_list.push_back(definition);
-
- if (protocol->send_fields(&field_list,
- Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
- DBUG_RETURN(1);
- protocol->prepare_for_resend();
- protocol->store(m_name.str, m_name.length, system_charset_info);
- protocol->store((char*) sql_mode_str, sql_mode_len, system_charset_info);
- if (full_access)
- protocol->store(m_defstr.str, m_defstr.length, system_charset_info);
- else
- protocol->store_null();
- res= protocol->write();
- send_eof(thd);
-
- DBUG_RETURN(res);
+ insert_dynamic(&m_instr, (uchar*)&instr);
}
-/*
+/**
Do some minimal optimization of the code:
- 1) Mark used instructions
- 1.1) While doing this, shortcut jumps to jump instructions
- 2) Compact the code, removing unused instructions
+ -# Mark used instructions
+ -# While doing this, shortcut jumps to jump instructions
+ -# Compact the code, removing unused instructions.
This is the main mark and move loop; it relies on the following methods
in sp_instr and its subclasses:
- opt_mark() Mark instruction as reachable
- opt_shortcut_jump() Shortcut jumps to the final destination;
- used by opt_mark().
- opt_move() Update moved instruction
- set_destination() Set the new destination (jump instructions only)
+ - opt_mark() : Mark instruction as reachable
+ - opt_shortcut_jump(): Shortcut jumps to the final destination;
+ used by opt_mark().
+ - opt_move() : Update moved instruction
+ - set_destination() : Set the new destination (jump instructions only)
*/
void sp_head::optimize()
@@ -2309,7 +2520,7 @@ void sp_head::optimize()
sp_instr *ibp;
List_iterator_fast<sp_instr> li(bp);
- set_dynamic(&m_instr, (gptr)&i, dst);
+ set_dynamic(&m_instr, (uchar*)&i, dst);
while ((ibp= li++))
{
sp_instr_opt_meta *im= static_cast<sp_instr_opt_meta *>(ibp);
@@ -2373,9 +2584,10 @@ sp_head::opt_mark()
#ifndef DBUG_OFF
-/*
+/**
Return the routine instructions as a result set.
- Returns 0 if ok, !=0 on error.
+ @return
+ 0 if ok, !=0 on error.
*/
int
sp_head::show_routine_code(THD *thd)
@@ -2429,34 +2641,34 @@ sp_head::show_routine_code(THD *thd)
if ((res= protocol->write()))
break;
}
- send_eof(thd);
+
+ if (!res)
+ my_eof(thd);
DBUG_RETURN(res);
}
#endif // ifndef DBUG_OFF
-/*
+/**
Prepare LEX and thread for execution of instruction, if requested open
and lock LEX's tables, execute instruction's core function, perform
cleanup afterwards.
- SYNOPSIS
- reset_lex_and_exec_core()
- thd - thread context
- nextp - out - next instruction
- open_tables - if TRUE then check read access to tables in LEX's table
- list and open and lock them (used in instructions which
- need to calculate some expression and don't execute
- complete statement).
- sp_instr - instruction for which we prepare context, and which core
- function execute by calling its exec_core() method.
+ @param thd thread context
+ @param nextp out - next instruction
+ @param open_tables if TRUE then check read access to tables in LEX's table
+ list and open and lock them (used in instructions which
+ need to calculate some expression and don't execute
+ complete statement).
+ @param sp_instr instruction for which we prepare context, and which core
+ function execute by calling its exec_core() method.
- NOTE
+ @note
We are not saving/restoring some parts of THD which may need this because
we do this once for whole routine execution in sp_head::execute().
- RETURN VALUE
+ @return
0/non-0 - Success/Failure
*/
@@ -2465,6 +2677,8 @@ sp_lex_keeper::reset_lex_and_exec_core(THD *thd, uint *nextp,
bool open_tables, sp_instr* instr)
{
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.
@@ -2510,13 +2724,17 @@ sp_lex_keeper::reset_lex_and_exec_core(THD *thd, uint *nextp,
res= instr->exec_open_and_lock_tables(thd, m_lex->query_tables);
if (!res)
+ {
res= instr->exec_core(thd, nextp);
+ DBUG_PRINT("info",("exec_core returned: %d", res));
+ }
m_lex->unit.cleanup();
- thd->proc_info="closing tables";
+ thd_proc_info(thd, "closing tables");
+ /* Here we also commit or rollback the current statement. */
close_thread_tables(thd);
- thd->proc_info= 0;
+ thd_proc_info(thd, 0);
if (m_lex->query_tables_own_last)
{
@@ -2552,7 +2770,7 @@ sp_lex_keeper::reset_lex_and_exec_core(THD *thd, uint *nextp,
cleanup_items() is called in sp_head::execute()
*/
- return res || thd->net.report_error;
+ DBUG_RETURN(res || thd->is_error());
}
@@ -2568,7 +2786,7 @@ int sp_instr::exec_open_and_lock_tables(THD *thd, TABLE_LIST *tables)
Check whenever we have access to tables for this statement
and open and lock them before executing instructions core function.
*/
- if (check_table_access(thd, SELECT_ACL, tables, 0)
+ if (check_table_access(thd, SELECT_ACL, tables, UINT_MAX, FALSE)
|| open_and_lock_tables(thd, tables))
result= -1;
else
@@ -2604,23 +2822,40 @@ sp_instr_stmt::execute(THD *thd, uint *nextp)
query= thd->query;
query_length= thd->query_length;
- if (!(res= alloc_query(thd, m_query.str, m_query.length+1)) &&
+#if defined(ENABLED_PROFILING) && defined(COMMUNITY_SERVER)
+ /* This s-p instr is profilable and will be captured. */
+ thd->profiling.set_query_source(m_query.str, m_query.length);
+#endif
+ if (!(res= alloc_query(thd, m_query.str, m_query.length)) &&
!(res=subst_spvars(thd, this, &m_query)))
{
/*
(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);
+
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);
+
query_cache_end_of_result(thd);
+
+ if (!res && unlikely(thd->enable_slow_log))
+ log_slow_statement(thd);
}
else
*nextp= m_ip+1;
thd->query= query;
thd->query_length= query_length;
+
+ if (!thd->is_error())
+ thd->main_da.reset_diagnostics_area();
}
DBUG_RETURN(res);
}
@@ -2730,7 +2965,7 @@ sp_instr_set::print(String *str)
}
str->qs_append(m_offset);
str->qs_append(' ');
- m_value->print(str);
+ m_value->print(str, QT_ORDINARY);
}
@@ -2758,9 +2993,9 @@ void
sp_instr_set_trigger_field::print(String *str)
{
str->append(STRING_WITH_LEN("set_trigger_field "));
- trigger_field->print(str);
+ trigger_field->print(str, QT_ORDINARY);
str->append(STRING_WITH_LEN(":="));
- value->print(str);
+ value->print(str, QT_ORDINARY);
}
/*
@@ -2886,7 +3121,7 @@ sp_instr_jump_if_not::print(String *str)
str->qs_append('(');
str->qs_append(m_cont_dest);
str->qs_append(STRING_WITH_LEN(") "));
- m_expr->print(str);
+ m_expr->print(str, QT_ORDINARY);
}
@@ -2974,7 +3209,7 @@ sp_instr_freturn::print(String *str)
str->qs_append(STRING_WITH_LEN("freturn "));
str->qs_append((uint)m_type);
str->qs_append(' ');
- m_value->print(str);
+ m_value->print(str, QT_ORDINARY);
}
/*
@@ -3200,6 +3435,11 @@ sp_instr_cpop::print(String *str)
sp_instr_copen class functions
*/
+/**
+ @todo
+ Assert that we either have an error or a cursor
+*/
+
int
sp_instr_copen::execute(THD *thd, uint *nextp)
{
@@ -3457,7 +3697,7 @@ sp_instr_set_case_expr::print(String *str)
str->qs_append(STRING_WITH_LEN(") "));
str->qs_append(m_case_expr_id);
str->qs_append(' ');
- m_case_expr->print(str);
+ m_case_expr->print(str, QT_ORDINARY);
}
uint
@@ -3488,44 +3728,6 @@ sp_instr_set_case_expr::opt_move(uint dst, List<sp_instr> *bp)
/* ------------------------------------------------------------------ */
-/*
- Security context swapping
-*/
-
-#ifndef NO_EMBEDDED_ACCESS_CHECKS
-bool
-sp_change_security_context(THD *thd, sp_head *sp, Security_context **backup)
-{
- *backup= 0;
- if (sp->m_chistics->suid != SP_IS_NOT_SUID &&
- (strcmp(sp->m_definer_user.str,
- thd->security_ctx->priv_user) ||
- my_strcasecmp(system_charset_info, sp->m_definer_host.str,
- thd->security_ctx->priv_host)))
- {
- if (acl_getroot_no_password(&sp->m_security_ctx, sp->m_definer_user.str,
- sp->m_definer_host.str,
- sp->m_definer_host.str,
- sp->m_db.str))
- {
- my_error(ER_NO_SUCH_USER, MYF(0), sp->m_definer_user.str,
- sp->m_definer_host.str);
- return TRUE;
- }
- *backup= thd->security_ctx;
- thd->security_ctx= &sp->m_security_ctx;
- }
- return FALSE;
-}
-
-void
-sp_restore_security_context(THD *thd, Security_context *backup)
-{
- if (backup)
- thd->security_ctx= backup;
-}
-
-#endif /* NO_EMBEDDED_ACCESS_CHECKS */
/*
Structure that represent all instances of one table
@@ -3550,33 +3752,32 @@ typedef struct st_sp_table
uint8 trg_event_map;
} SP_TABLE;
-byte *
-sp_table_key(const byte *ptr, uint *plen, my_bool first)
+
+uchar *sp_table_key(const uchar *ptr, size_t *plen, my_bool first)
{
SP_TABLE *tab= (SP_TABLE *)ptr;
*plen= tab->qname.length;
- return (byte *)tab->qname.str;
+ return (uchar *)tab->qname.str;
}
-/*
+/**
Merge the list of tables used by some query into the multi-set of
tables used by routine.
- SYNOPSIS
- merge_table_list()
- thd - thread context
- table - table list
- lex_for_tmp_check - LEX of the query for which we are merging
- table list.
+ @param thd thread context
+ @param table table list
+ @param lex_for_tmp_check LEX of the query for which we are merging
+ table list.
- NOTE
+ @note
This method will use LEX provided to check whenever we are creating
temporary table and mark it as such in target multi-set.
- RETURN VALUE
- TRUE - Success
- FALSE - Error
+ @retval
+ TRUE Success
+ @retval
+ FALSE Error
*/
bool
@@ -3606,7 +3807,7 @@ sp_head::merge_table_list(THD *thd, TABLE_LIST *table, LEX *lex_for_tmp_check)
memcpy(tname+tlen, table->table_name, table->table_name_length);
tlen+= table->table_name_length;
tname[tlen++]= '\0';
- alen= (uint) strlen(table->alias);
+ alen= strlen(table->alias);
memcpy(tname+tlen, table->alias, alen);
tlen+= alen;
tname[tlen]= '\0';
@@ -3624,8 +3825,8 @@ 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, (byte *)tname, tlen)) ||
- ((tab= (SP_TABLE *)hash_search(&m_sptabs, (byte *)tname,
+ if ((tab= (SP_TABLE *)hash_search(&m_sptabs, (uchar *)tname, tlen)) ||
+ ((tab= (SP_TABLE *)hash_search(&m_sptabs, (uchar *)tname,
tlen - alen - 1)) &&
tab->temp))
{
@@ -3657,34 +3858,33 @@ 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;
- my_hash_insert(&m_sptabs, (byte *)tab);
+ my_hash_insert(&m_sptabs, (uchar *)tab);
}
}
return TRUE;
}
-/*
+/**
Add tables used by routine to the table list.
- SYNOPSIS
- add_used_tables_to_table_list()
- thd [in] Thread context
- query_tables_last_ptr [in/out] Pointer to the next_global member of
- last element of the list where tables
- will be added (or to its root).
- belong_to_view [in] Uppermost view which uses this routine,
- 0 if none.
-
- DESCRIPTION
Converts multi-set of tables used by this routine to table list and adds
this list to the end of table list specified by 'query_tables_last_ptr'.
Elements of list will be allocated in PS memroot, so this list will be
persistent between PS executions.
- RETURN VALUE
- TRUE - if some elements were added, FALSE - otherwise.
+ @param[in] thd Thread context
+ @param[in,out] query_tables_last_ptr Pointer to the next_global member of
+ last element of the list where tables
+ will be added (or to its root).
+ @param[in] belong_to_view Uppermost view which uses this routine,
+ 0 if none.
+
+ @retval
+ TRUE if some elements were added
+ @retval
+ FALSE otherwise.
*/
bool
@@ -3754,7 +3954,7 @@ sp_head::add_used_tables_to_table_list(THD *thd,
}
-/*
+/**
Simple function for adding an explicetly named (systems) table to
the global table list, e.g. "mysql", "proc".
*/
@@ -3771,13 +3971,13 @@ sp_add_to_query_tables(THD *thd, LEX *lex,
thd->fatal_error();
return NULL;
}
- table->db_length= (uint) strlen(db);
+ table->db_length= strlen(db);
table->db= thd->strmake(db, table->db_length);
- table->table_name_length= (uint) strlen(name);
+ table->table_name_length= strlen(name);
table->table_name= thd->strmake(name, table->table_name_length);
table->alias= thd->strdup(name);
table->lock_type= locktype;
- table->select_lex= lex->current_select; // QQ?
+ table->select_lex= lex->current_select;
table->cacheable_table= 1;
lex->add_to_query_tables(table);
diff --git a/sql/sp_head.h b/sql/sp_head.h
index 91f465a4e2a..c17b67f962a 100644
--- a/sql/sp_head.h
+++ b/sql/sp_head.h
@@ -23,6 +23,11 @@
#include <stddef.h>
+/**
+ @defgroup Stored_Routines Stored Routines
+ @ingroup Runtime_Environment
+ @{
+*/
// Values for the type enum. This reflects the order of the enum declaration
// in the CREATE TABLE command.
#define TYPE_ENUM_FUNCTION 1
@@ -45,6 +50,58 @@ class sp_instr_jump_if_not;
struct sp_cond_type;
struct sp_variable;
+/*************************************************************************/
+
+/**
+ Stored_program_creation_ctx -- base class for creation context of stored
+ programs (stored routines, triggers, events).
+*/
+
+class Stored_program_creation_ctx :public Default_object_creation_ctx
+{
+public:
+ CHARSET_INFO *get_db_cl()
+ {
+ return m_db_cl;
+ }
+
+public:
+ virtual Stored_program_creation_ctx *clone(MEM_ROOT *mem_root) = 0;
+
+protected:
+ Stored_program_creation_ctx(THD *thd)
+ : Default_object_creation_ctx(thd),
+ m_db_cl(thd->variables.collation_database)
+ { }
+
+ Stored_program_creation_ctx(CHARSET_INFO *client_cs,
+ CHARSET_INFO *connection_cl,
+ CHARSET_INFO *db_cl)
+ : Default_object_creation_ctx(client_cs, connection_cl),
+ m_db_cl(db_cl)
+ { }
+
+protected:
+ virtual void change_env(THD *thd) const
+ {
+ thd->variables.collation_database= m_db_cl;
+
+ Default_object_creation_ctx::change_env(thd);
+ }
+
+protected:
+ /**
+ db_cl stores the value of the database collation. Both character set
+ and collation attributes are used.
+
+ Database collation is included into the context because it defines the
+ default collation for stored-program variables.
+ */
+ CHARSET_INFO *m_db_cl;
+};
+
+/*************************************************************************/
+
class sp_name : public Sql_alloc
{
public:
@@ -52,7 +109,7 @@ 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
@@ -68,7 +125,7 @@ public:
m_qname.length= m_sroutines_key.length= 0;
}
- /*
+ /**
Creates temporary sp_name object from key, used mainly
for SP-cache lookups.
*/
@@ -88,16 +145,16 @@ public:
bool
-check_routine_name(LEX_STRING name);
+check_routine_name(LEX_STRING *ident);
class sp_head :private Query_arena
{
- sp_head(const sp_head &); /* Prevent use of these */
+ sp_head(const sp_head &); /**< Prevent use of these */
void operator=(sp_head &);
MEM_ROOT main_mem_root;
public:
- /* Possible values of m_flags */
+ /** Possible values of m_flags */
enum {
HAS_RETURN= 1, // For FUNCTIONs only: is set if has RETURN
MULTI_RESULTS= 8, // Is set if a procedure with SELECT(s)
@@ -106,24 +163,27 @@ public:
HAS_SET_AUTOCOMMIT_STMT= 64,// Is set if a procedure with 'set autocommit'
/* Is set if a procedure with COMMIT (implicit or explicit) | ROLLBACK */
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
};
- /* TYPE_ENUM_FUNCTION, TYPE_ENUM_PROCEDURE or TYPE_ENUM_TRIGGER */
+ /** TYPE_ENUM_FUNCTION, TYPE_ENUM_PROCEDURE or TYPE_ENUM_TRIGGER */
int m_type;
uint m_flags; // Boolean attributes of a stored routine
- create_field m_return_field_def; /* This is used for FUNCTIONs only. */
+ Create_field m_return_field_def; /**< This is used for FUNCTIONs only. */
- const char *m_tmp_query; // Temporary pointer to sub query string
+ 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
- LEX_STRING m_qname; // db.name
- bool m_explicit_name; /**< Prepend the db name? */
+ ulong 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\0
+ [routine_type]db.name
@sa sp_name::m_sroutines_key
*/
LEX_STRING m_sroutines_key;
@@ -131,25 +191,41 @@ public:
LEX_STRING m_name;
LEX_STRING m_params;
LEX_STRING m_body;
+ LEX_STRING m_body_utf8;
LEX_STRING m_defstr;
LEX_STRING m_definer_user;
LEX_STRING m_definer_host;
+
+private:
+ Stored_program_creation_ctx *m_creation_ctx;
+
+public:
+ inline Stored_program_creation_ctx *get_creation_ctx()
+ {
+ return m_creation_ctx;
+ }
+
+ inline void set_creation_ctx(Stored_program_creation_ctx *creation_ctx)
+ {
+ m_creation_ctx= creation_ctx->clone(mem_root);
+ }
+
longlong m_created;
longlong m_modified;
- /* Recursion level of the current SP instance. The levels are numbered from 0 */
+ /** Recursion level of the current SP instance. The levels are numbered from 0 */
ulong m_recursion_level;
- /*
+ /**
A list of diferent recursion level instances for the same procedure.
For every recursion level we have a sp_head instance. This instances
connected in the list. The list ordered by increasing recursion level
(m_recursion_level).
*/
sp_head *m_next_cached_sp;
- /*
+ /**
Pointer to the first element of the above list
*/
sp_head *m_first_instance;
- /*
+ /**
Pointer to the first free (non-INVOKED) routine in the list of
cached instances for this SP. This pointer is set only for the first
SP in the list of instences (see above m_first_cached_sp pointer).
@@ -157,12 +233,12 @@ public:
For non-first instance value of this pointer meanless (point to itself);
*/
sp_head *m_first_free_instance;
- /*
+ /**
Pointer to the last element in the list of instances of the SP.
For non-first instance value of this pointer meanless (point to itself);
*/
sp_head *m_last_cached_sp;
- /*
+ /**
Set containing names of stored routines used by this routine.
Note that unlike elements of similar set for statement elements of this
set are not linked in one list. Because of this we are able save memory
@@ -173,8 +249,11 @@ public:
// Pointers set during parsing
const char *m_param_begin;
const char *m_param_end;
+
+private:
const char *m_body_begin;
+public:
/*
Security context for stored routine which should be run under
definer privileges.
@@ -189,30 +268,36 @@ public:
sp_head();
- // Initialize after we have reset mem_root
+ /// Initialize after we have reset mem_root
void
init(LEX *lex);
- /* Copy sp name from parser. */
+ /** Copy sp name from parser. */
void
init_sp_name(THD *thd, sp_name *spname);
- // Initialize strings after parsing header
+ /** Set the body-definition start position. */
void
- init_strings(THD *thd, LEX *lex);
+ set_body_start(THD *thd, const char *begin_ptr);
+
+ /** Set the statement-definition (body-definition) end position. */
+ void
+ set_stmt_end(THD *thd);
int
create(THD *thd);
virtual ~sp_head();
- // Free memory
+ /// Free memory
void
destroy();
bool
- execute_trigger(THD *thd, const char *db, const char *table,
- GRANT_INFO *grant_onfo);
+ execute_trigger(THD *thd,
+ const LEX_STRING *db_name,
+ const LEX_STRING *table_name,
+ GRANT_INFO *grant_info);
bool
execute_function(THD *thd, Item **args, uint argcount, Field *return_fld);
@@ -220,11 +305,8 @@ public:
bool
execute_procedure(THD *thd, List<Item> *args);
- int
- show_create_procedure(THD *thd);
-
- int
- show_create_function(THD *thd);
+ bool
+ show_create_routine(THD *thd, int type);
void
add_instr(sp_instr *instr);
@@ -240,44 +322,52 @@ public:
{
sp_instr *i;
- get_dynamic(&m_instr, (gptr)&i, m_instr.elements-1);
+ get_dynamic(&m_instr, (uchar*)&i, m_instr.elements-1);
return i;
}
- // Resets lex in 'thd' and keeps a copy of the old one.
+ /*
+ Resets lex in 'thd' and keeps a copy of the old one.
+
+ @todo Conflicting comment in sp_head.cc
+ */
bool
reset_lex(THD *thd);
- // Restores lex in 'thd' from our copy, but keeps some status from the
- // one in 'thd', like ptr, tables, fields, etc.
+ /**
+ Restores lex in 'thd' from our copy, but keeps some status from the
+ one in 'thd', like ptr, tables, fields, etc.
+
+ @todo Conflicting comment in sp_head.cc
+ */
void
restore_lex(THD *thd);
- // Put the instruction on the backpatch list, associated with the label.
+ /// Put the instruction on the backpatch list, associated with the label.
void
push_backpatch(sp_instr *, struct sp_label *);
- // Update all instruction with this label in the backpatch list to
- // the current position.
+ /// Update all instruction with this label in the backpatch list to
+ /// the current position.
void
backpatch(struct sp_label *);
- // Start a new cont. backpatch level. If 'i' is NULL, the level is just incr.
+ /// Start a new cont. backpatch level. If 'i' is NULL, the level is just incr.
void
new_cont_backpatch(sp_instr_opt_meta *i);
- // Add an instruction to the current level
+ /// Add an instruction to the current level
void
add_cont_backpatch(sp_instr_opt_meta *i);
- // Backpatch (and pop) the current level to the current position.
+ /// Backpatch (and pop) the current level to the current position.
void
do_cont_backpatch();
char *name(uint *lenp = 0) const
{
if (lenp)
- *lenp= m_name.length;
+ *lenp= (uint) m_name.length;
return m_name.str;
}
@@ -288,7 +378,7 @@ public:
bool fill_field_definition(THD *thd, LEX *lex,
enum enum_field_types field_type,
- create_field *field_def);
+ Create_field *field_def);
void set_info(longlong created, longlong modified,
st_sp_chistics *chistics, ulong sql_mode);
@@ -322,7 +412,7 @@ public:
sp_instr *ip;
if (i < m_instr.elements)
- get_dynamic(&m_instr, (gptr)&ip, i);
+ get_dynamic(&m_instr, (uchar*)&ip, i);
else
ip= NULL;
return ip;
@@ -333,7 +423,7 @@ public:
TABLE_LIST ***query_tables_last_ptr,
TABLE_LIST *belong_to_view);
- /*
+ /**
Check if this stored routine contains statements disallowed
in a stored function or trigger, and set an appropriate error message
if this is the case.
@@ -362,22 +452,39 @@ public:
int show_routine_code(THD *thd);
#endif
+ /*
+ 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).
+ */
+ void propagate_attributes(LEX *lex)
+ {
+ /*
+ 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();
+ }
+
private:
- MEM_ROOT *m_thd_root; // Temp. store for thd's mem_root
- THD *m_thd; // Set if we have reset mem_root
+ MEM_ROOT *m_thd_root; ///< Temp. store for thd's mem_root
+ THD *m_thd; ///< Set if we have reset mem_root
- sp_pcontext *m_pcont; // Parse context
- List<LEX> m_lex; // Temp. store for the other lex
- DYNAMIC_ARRAY m_instr; // The "instructions"
+ sp_pcontext *m_pcont; ///< Parse context
+ List<LEX> m_lex; ///< Temp. store for the other lex
+ DYNAMIC_ARRAY m_instr; ///< The "instructions"
typedef struct
{
struct sp_label *lab;
sp_instr *instr;
} bp_t;
- List<bp_t> m_backpatch; // Instructions needing backpatching
- /*
+ List<bp_t> m_backpatch; ///< Instructions needing backpatching
+ /**
We need a special list for backpatching of instructions with a continue
destination (in the case of a continue handler catching an error in
the test), since it would otherwise interfere with the normal backpatch
@@ -385,15 +492,16 @@ private:
which are to be patched differently.
Since these occur in a more restricted way (always the same "level" in
the code), we don't need the label.
- */
+ */
List<sp_instr_opt_meta> m_cont_backpatch;
uint m_cont_level; // The current cont. backpatch level
- /*
+ /**
Multi-set representing optimized list of tables to be locked by this
routine. Does not include tables which are used by invoked routines.
- Note: for prelocking-free SPs this multiset is constructed too.
+ @note
+ For prelocking-free SPs this multiset is constructed too.
We do so because the same instance of sp_head may be called both
in prelocked mode and in non-prelocked mode.
*/
@@ -408,7 +516,7 @@ private:
*/
void opt_mark();
- /*
+ /**
Merge the list of tables used by query into the multi-set of tables used
by routine.
*/
@@ -422,16 +530,16 @@ private:
class sp_instr :public Query_arena, public Sql_alloc
{
- sp_instr(const sp_instr &); /* Prevent use of these */
+ sp_instr(const sp_instr &); /**< Prevent use of these */
void operator=(sp_instr &);
public:
uint marked;
- uint m_ip; // My index
- sp_pcontext *m_ctx; // My parse context
+ uint m_ip; ///< My index
+ sp_pcontext *m_ctx; ///< My parse context
- // Should give each a name or type code for debugging purposes?
+ /// 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)
{}
@@ -440,21 +548,19 @@ public:
{ free_items(); }
- /*
+ /**
Execute this instruction
- SYNOPSIS
- execute()
- thd Thread handle
- nextp OUT index of the next instruction to execute. (For most
- instructions this will be the instruction following this
- one). Note that this parameter is undefined in case of
- errors, use get_cont_dest() to find the continuation
- instruction for CONTINUE error handlers.
-
- RETURN
- 0 on success,
- other if some error occurred
+
+ @param thd Thread handle
+ @param[out] nextp index of the next instruction to execute. (For most
+ instructions this will be the instruction following this
+ one). Note that this parameter is undefined in case of
+ errors, use get_cont_dest() to find the continuation
+ instruction for CONTINUE error handlers.
+
+ @retval 0 on success,
+ @retval other if some error occured
*/
virtual int execute(THD *thd, uint *nextp) = 0;
@@ -492,7 +598,7 @@ public:
virtual void backpatch(uint dest, sp_pcontext *dst_ctx)
{}
- /*
+ /**
Mark this instruction as reachable during optimization and return the
index to the next instruction. Jump instruction will add their
destination to the leads list.
@@ -503,7 +609,7 @@ public:
return m_ip+1;
}
- /*
+ /**
Short-cut jumps to jumps during optimization. This is used by the
jump instructions' opt_mark() methods. 'start' is the starting point,
used to prevent the mark sweep from looping for ever. Return the
@@ -514,7 +620,7 @@ public:
return m_ip;
}
- /*
+ /**
Inform the instruction that it has been moved during optimization.
Most instructions will simply update its index, but jump instructions
must also take care of their destination pointers. Forward jumps get
@@ -528,7 +634,7 @@ public:
}; // class sp_instr : public Sql_alloc
-/*
+/**
Auxilary class to which instructions delegate responsibility
for handling LEX and preparations before executing statement
or calculating complex expression.
@@ -536,13 +642,14 @@ public:
Exist mainly to avoid having double hierarchy between instruction
classes.
- TODO: Add ability to not store LEX and do any preparations if
- expression used is simple.
+ @todo
+ Add ability to not store LEX and do any preparations if
+ expression used is simple.
*/
class sp_lex_keeper
{
- /* Prevent use of these */
+ /** Prevent use of these */
sp_lex_keeper(const sp_lex_keeper &);
void operator=(sp_lex_keeper &);
public:
@@ -562,10 +669,12 @@ public:
}
}
- /*
+ /**
Prepare execution of instruction using LEX, if requested check whenever
we have read access to tables used and open/lock them, call instruction's
exec_core() method, perform cleanup afterwards.
+
+ @todo Conflicting comment in sp_head.cc
*/
int reset_lex_and_exec_core(THD *thd, uint *nextp, bool open_tables,
sp_instr* instr);
@@ -582,7 +691,7 @@ public:
private:
LEX *m_lex;
- /*
+ /**
Indicates whenever this sp_lex_keeper instance responsible
for LEX deletion.
*/
@@ -595,13 +704,13 @@ private:
prelocked mode itself.
*/
- /*
+ /**
List of additional tables this statement needs to lock when it
enters/leaves prelocked mode on its own.
*/
TABLE_LIST *prelocking_tables;
- /*
+ /**
The value m_lex->query_tables_own_last should be set to this when the
statement enters/leaves prelocked mode on its own.
*/
@@ -609,17 +718,17 @@ private:
};
-//
-// Call out to some prepared SQL statement.
-//
+/**
+ Call out to some prepared SQL statement.
+*/
class sp_instr_stmt : public sp_instr
{
- sp_instr_stmt(const sp_instr_stmt &); /* Prevent use of these */
+ sp_instr_stmt(const sp_instr_stmt &); /**< Prevent use of these */
void operator=(sp_instr_stmt &);
public:
- LEX_STRING m_query; // For thd->query
+ LEX_STRING m_query; ///< For thd->query
sp_instr_stmt(uint ip, sp_pcontext *ctx, LEX *lex)
: sp_instr(ip, ctx), m_lex_keeper(lex, TRUE)
@@ -646,7 +755,7 @@ private:
class sp_instr_set : public sp_instr
{
- sp_instr_set(const sp_instr_set &); /* Prevent use of these */
+ sp_instr_set(const sp_instr_set &); /**< Prevent use of these */
void operator=(sp_instr_set &);
public:
@@ -669,15 +778,15 @@ public:
private:
- uint m_offset; // Frame offset
+ uint m_offset; ///< Frame offset
Item *m_value;
- enum enum_field_types m_type; // The declared type
+ enum enum_field_types m_type; ///< The declared type
sp_lex_keeper m_lex_keeper;
}; // class sp_instr_set : public sp_instr
-/*
+/**
Set NEW/OLD row field value instruction. Used in triggers.
*/
class sp_instr_set_trigger_field : public sp_instr
@@ -711,18 +820,19 @@ private:
}; // class sp_instr_trigger_field : public sp_instr
-/*
+/**
An abstract class for all instructions with destinations that
needs to be updated by the optimizer.
+
Even if not all subclasses will use both the normal destination and
the continuation destination, we put them both here for simplicity.
- */
+*/
class sp_instr_opt_meta : public sp_instr
{
public:
- uint m_dest; // Where we will go
- uint m_cont_dest; // Where continue handlers will go
+ uint m_dest; ///< Where we will go
+ uint m_cont_dest; ///< Where continue handlers will go
sp_instr_opt_meta(uint ip, sp_pcontext *ctx)
: sp_instr(ip, ctx),
@@ -744,14 +854,14 @@ public:
protected:
- sp_instr *m_optdest; // Used during optimization
- sp_instr *m_cont_optdest; // Used during optimization
+ sp_instr *m_optdest; ///< Used during optimization
+ sp_instr *m_cont_optdest; ///< Used during optimization
}; // class sp_instr_opt_meta : public sp_instr
class sp_instr_jump : public sp_instr_opt_meta
{
- sp_instr_jump(const sp_instr_jump &); /* Prevent use of these */
+ sp_instr_jump(const sp_instr_jump &); /**< Prevent use of these */
void operator=(sp_instr_jump &);
public:
@@ -784,7 +894,7 @@ public:
m_dest= dest;
}
- /*
+ /**
Update the destination; used by the optimizer.
*/
virtual void set_destination(uint old_dest, uint new_dest)
@@ -798,7 +908,7 @@ public:
class sp_instr_jump_if_not : public sp_instr_jump
{
- sp_instr_jump_if_not(const sp_instr_jump_if_not &); /* Prevent use of these */
+ sp_instr_jump_if_not(const sp_instr_jump_if_not &); /**< Prevent use of these */
void operator=(sp_instr_jump_if_not &);
public:
@@ -824,7 +934,7 @@ public:
virtual uint opt_mark(sp_head *sp, List<sp_instr> *leads);
- /* Override sp_instr_jump's shortcut; we stop here */
+ /** Override sp_instr_jump's shortcut; we stop here */
virtual uint opt_shortcut_jump(sp_head *sp, sp_instr *start)
{
return m_ip;
@@ -841,7 +951,7 @@ public:
private:
- Item *m_expr; // The condition
+ Item *m_expr; ///< The condition
sp_lex_keeper m_lex_keeper;
}; // class sp_instr_jump_if_not : public sp_instr_jump
@@ -849,7 +959,7 @@ private:
class sp_instr_freturn : public sp_instr
{
- sp_instr_freturn(const sp_instr_freturn &); /* Prevent use of these */
+ sp_instr_freturn(const sp_instr_freturn &); /**< Prevent use of these */
void operator=(sp_instr_freturn &);
public:
@@ -886,7 +996,7 @@ protected:
class sp_instr_hpush_jump : public sp_instr_jump
{
- sp_instr_hpush_jump(const sp_instr_hpush_jump &); /* Prevent use of these */
+ sp_instr_hpush_jump(const sp_instr_hpush_jump &); /**< Prevent use of these */
void operator=(sp_instr_hpush_jump &);
public:
@@ -908,7 +1018,7 @@ public:
virtual uint opt_mark(sp_head *sp, List<sp_instr> *leads);
- /* Override sp_instr_jump's shortcut; we stop here. */
+ /** Override sp_instr_jump's shortcut; we stop here. */
virtual uint opt_shortcut_jump(sp_head *sp, sp_instr *start)
{
return m_ip;
@@ -921,7 +1031,7 @@ public:
private:
- int m_type; // Handler type
+ int m_type; ///< Handler type
uint m_frame;
List<struct sp_cond_type> m_cond;
@@ -930,7 +1040,7 @@ private:
class sp_instr_hpop : public sp_instr
{
- sp_instr_hpop(const sp_instr_hpop &); /* Prevent use of these */
+ sp_instr_hpop(const sp_instr_hpop &); /**< Prevent use of these */
void operator=(sp_instr_hpop &);
public:
@@ -955,7 +1065,7 @@ private:
class sp_instr_hreturn : public sp_instr_jump
{
- sp_instr_hreturn(const sp_instr_hreturn &); /* Prevent use of these */
+ sp_instr_hreturn(const sp_instr_hreturn &); /**< Prevent use of these */
void operator=(sp_instr_hreturn &);
public:
@@ -986,10 +1096,10 @@ private:
}; // class sp_instr_hreturn : public sp_instr_jump
-/* This is DECLARE CURSOR */
+/** This is DECLARE CURSOR */
class sp_instr_cpush : public sp_instr
{
- sp_instr_cpush(const sp_instr_cpush &); /* Prevent use of these */
+ sp_instr_cpush(const sp_instr_cpush &); /**< Prevent use of these */
void operator=(sp_instr_cpush &);
public:
@@ -1005,7 +1115,7 @@ public:
virtual void print(String *str);
- /*
+ /**
This call is used to cleanup the instruction when a sensitive
cursor is closed. For now stored procedures always use materialized
cursors and the call is not used.
@@ -1014,14 +1124,14 @@ public:
private:
sp_lex_keeper m_lex_keeper;
- uint m_cursor; /* Frame offset (for debugging) */
+ uint m_cursor; /**< Frame offset (for debugging) */
}; // class sp_instr_cpush : public sp_instr
class sp_instr_cpop : public sp_instr
{
- sp_instr_cpop(const sp_instr_cpop &); /* Prevent use of these */
+ sp_instr_cpop(const sp_instr_cpop &); /**< Prevent use of these */
void operator=(sp_instr_cpop &);
public:
@@ -1046,7 +1156,7 @@ private:
class sp_instr_copen : public sp_instr
{
- sp_instr_copen(const sp_instr_copen &); /* Prevent use of these */
+ sp_instr_copen(const sp_instr_copen &); /**< Prevent use of these */
void operator=(sp_instr_copen &);
public:
@@ -1066,14 +1176,14 @@ public:
private:
- uint m_cursor; // Stack index
+ uint m_cursor; ///< Stack index
}; // class sp_instr_copen : public sp_instr_stmt
class sp_instr_cclose : public sp_instr
{
- sp_instr_cclose(const sp_instr_cclose &); /* Prevent use of these */
+ sp_instr_cclose(const sp_instr_cclose &); /**< Prevent use of these */
void operator=(sp_instr_cclose &);
public:
@@ -1098,7 +1208,7 @@ private:
class sp_instr_cfetch : public sp_instr
{
- sp_instr_cfetch(const sp_instr_cfetch &); /* Prevent use of these */
+ sp_instr_cfetch(const sp_instr_cfetch &); /**< Prevent use of these */
void operator=(sp_instr_cfetch &);
public:
@@ -1131,7 +1241,7 @@ private:
class sp_instr_error : public sp_instr
{
- sp_instr_error(const sp_instr_error &); /* Prevent use of these */
+ sp_instr_error(const sp_instr_error &); /**< Prevent use of these */
void operator=(sp_instr_error &);
public:
@@ -1221,4 +1331,8 @@ sp_prepare_func_item(THD* thd, Item **it_addr);
bool
sp_eval_expr(THD *thd, Field *result_field, Item **expr_item_ptr);
+/**
+ @} (end of group Stored_Routines)
+*/
+
#endif /* _SP_HEAD_H_ */
diff --git a/sql/sp_pcontext.cc b/sql/sp_pcontext.cc
index 780243cc79f..414ea12cd7a 100644
--- a/sql/sp_pcontext.cc
+++ b/sql/sp_pcontext.cc
@@ -51,6 +51,8 @@ sp_cond_check(LEX_STRING *sqlstate)
(c < 'A' || 'Z' < c))
return FALSE;
}
+ if (strcmp(sqlstate->str, "00000") == 0)
+ return FALSE;
return TRUE;
}
@@ -211,7 +213,7 @@ sp_pcontext::find_variable(LEX_STRING *name, my_bool scoped)
{
sp_variable_t *p;
- get_dynamic(&m_vars, (gptr)&p, i);
+ get_dynamic(&m_vars, (uchar*)&p, i);
if (my_strnncoll(system_charset_info,
(const uchar *)name->str, name->length,
(const uchar *)p->name.str, p->name.length) == 0)
@@ -238,7 +240,7 @@ sp_pcontext::find_variable(uint offset)
{ // This frame
sp_variable_t *p;
- get_dynamic(&m_vars, (gptr)&p, offset - m_var_offset);
+ get_dynamic(&m_vars, (uchar*)&p, offset - m_var_offset);
return p;
}
if (m_parent)
@@ -263,7 +265,7 @@ sp_pcontext::push_variable(LEX_STRING *name, enum enum_field_types type,
p->mode= mode;
p->offset= current_var_count();
p->dflt= NULL;
- insert_dynamic(&m_vars, (gptr)&p);
+ insert_dynamic(&m_vars, (uchar*)&p);
return p;
}
@@ -318,7 +320,7 @@ sp_pcontext::push_cond(LEX_STRING *name, sp_cond_type_t *val)
p->name.str= name->str;
p->name.length= name->length;
p->val= val;
- insert_dynamic(&m_conds, (gptr)&p);
+ insert_dynamic(&m_conds, (uchar*)&p);
}
}
@@ -334,7 +336,7 @@ sp_pcontext::find_cond(LEX_STRING *name, my_bool scoped)
{
sp_cond_t *p;
- get_dynamic(&m_conds, (gptr)&p, i);
+ get_dynamic(&m_conds, (uchar*)&p, i);
if (my_strnncoll(system_charset_info,
(const uchar *)name->str, name->length,
(const uchar *)p->name.str, p->name.length) == 0)
@@ -361,7 +363,7 @@ sp_pcontext::find_handler(sp_cond_type_t *cond)
{
sp_cond_type_t *p;
- get_dynamic(&m_handlers, (gptr)&p, i);
+ get_dynamic(&m_handlers, (uchar*)&p, i);
if (cond->type == p->type)
{
switch (p->type)
@@ -391,7 +393,7 @@ sp_pcontext::push_cursor(LEX_STRING *name)
m_max_cursor_index+= 1;
n.str= name->str;
n.length= name->length;
- insert_dynamic(&m_cursors, (gptr)&n);
+ insert_dynamic(&m_cursors, (uchar*)&n);
}
/*
@@ -406,7 +408,7 @@ sp_pcontext::find_cursor(LEX_STRING *name, uint *poff, my_bool scoped)
{
LEX_STRING n;
- get_dynamic(&m_cursors, (gptr)&n, i);
+ get_dynamic(&m_cursors, (uchar*)&n, i);
if (my_strnncoll(system_charset_info,
(const uchar *)name->str, name->length,
(const uchar *)n.str, n.length) == 0)
@@ -422,14 +424,14 @@ sp_pcontext::find_cursor(LEX_STRING *name, uint *poff, my_bool scoped)
void
-sp_pcontext::retrieve_field_definitions(List<create_field> *field_def_lst)
+sp_pcontext::retrieve_field_definitions(List<Create_field> *field_def_lst)
{
/* Put local/context fields in the result list. */
for (uint i = 0; i < m_vars.elements; ++i)
{
sp_variable_t *var_def;
- get_dynamic(&m_vars, (gptr) &var_def, i);
+ get_dynamic(&m_vars, (uchar*) &var_def, i);
field_def_lst->push_back(&var_def->field_def);
}
@@ -453,7 +455,7 @@ sp_pcontext::find_cursor(uint offset, LEX_STRING *n)
if (m_cursor_offset <= offset &&
offset < m_cursor_offset + m_cursors.elements)
{ // This frame
- get_dynamic(&m_cursors, (gptr)n, offset - m_cursor_offset);
+ get_dynamic(&m_cursors, (uchar*)n, offset - m_cursor_offset);
return TRUE;
}
if (m_parent)
diff --git a/sql/sp_pcontext.h b/sql/sp_pcontext.h
index 5bffda79f98..aefc501b3b0 100644
--- a/sql/sp_pcontext.h
+++ b/sql/sp_pcontext.h
@@ -43,7 +43,7 @@ typedef struct sp_variable
uint offset;
Item *dflt;
- create_field field_def;
+ Create_field field_def;
} sp_variable_t;
@@ -234,7 +234,7 @@ public:
children.
*/
void
- retrieve_field_definitions(List<create_field> *field_def_lst);
+ retrieve_field_definitions(List<Create_field> *field_def_lst);
// Find by name
sp_variable_t *
@@ -273,7 +273,7 @@ public:
inline bool
push_case_expr_id(int case_expr_id)
{
- return insert_dynamic(&m_case_expr_id_lst, (gptr) &case_expr_id);
+ return insert_dynamic(&m_case_expr_id_lst, (uchar*) &case_expr_id);
}
inline void
@@ -287,7 +287,7 @@ public:
{
int case_expr_id;
- get_dynamic((DYNAMIC_ARRAY*)&m_case_expr_id_lst, (gptr) &case_expr_id,
+ get_dynamic((DYNAMIC_ARRAY*)&m_case_expr_id_lst, (uchar*) &case_expr_id,
m_case_expr_id_lst.elements - 1);
return case_expr_id;
@@ -343,7 +343,7 @@ public:
inline void
push_handler(sp_cond_type_t *cond)
{
- insert_dynamic(&m_handlers, (gptr)&cond);
+ insert_dynamic(&m_handlers, (uchar*)&cond);
}
bool
diff --git a/sql/sp_rcontext.cc b/sql/sp_rcontext.cc
index 129aaa46de6..9b237b3e7cc 100644
--- a/sql/sp_rcontext.cc
+++ b/sql/sp_rcontext.cc
@@ -105,7 +105,7 @@ bool sp_rcontext::init(THD *thd)
bool
sp_rcontext::init_var_table(THD *thd)
{
- List<create_field> field_def_lst;
+ List<Create_field> field_def_lst;
if (!m_root_parsing_ctx->max_var_index())
return FALSE;
@@ -287,7 +287,6 @@ sp_rcontext::find_handler(THD *thd, uint sql_errno,
sql_errno The error code
level Warning level
thd The current thread
- - thd->net.report_error is an optional output.
RETURN
TRUE if a handler was found.
@@ -298,7 +297,6 @@ sp_rcontext::handle_error(uint sql_errno,
MYSQL_ERROR::enum_warning_level level,
THD *thd)
{
- bool handled= FALSE;
MYSQL_ERROR::enum_warning_level elevated_level= level;
@@ -310,25 +308,7 @@ sp_rcontext::handle_error(uint sql_errno,
elevated_level= MYSQL_ERROR::WARN_LEVEL_ERROR;
}
- if (find_handler(thd, sql_errno, elevated_level))
- {
- if (elevated_level == MYSQL_ERROR::WARN_LEVEL_ERROR)
- {
- /*
- Forces to abort the current instruction execution.
- NOTE: This code is altering the original meaning of
- the net.report_error flag (send an error to the client).
- In the context of stored procedures with error handlers,
- the flag is reused to cause error propagation,
- until the error handler is reached.
- No messages will be sent to the client in that context.
- */
- thd->net.report_error= 1;
- }
- handled= TRUE;
- }
-
- return handled;
+ return find_handler(thd, sql_errno, elevated_level);
}
void
diff --git a/sql/spatial.cc b/sql/spatial.cc
index 80506f16d0f..97e5fcfa27a 100644
--- a/sql/spatial.cc
+++ b/sql/spatial.cc
@@ -54,8 +54,11 @@ static Geometry::Class_info **ci_collection_end=
Geometry::Class_info::Class_info(const char *name, int type_id,
void(*create_func)(void *)):
- m_name(name, (uint) strlen(name)), m_type_id(type_id), m_create_func(create_func)
+ m_type_id(type_id), m_create_func(create_func)
{
+ m_name.str= (char *) name;
+ m_name.length= strlen(name);
+
ci_collection[type_id]= this;
}
@@ -847,7 +850,6 @@ int Gis_polygon::area(double *ar, const char **end_of_data) const
double x, y;
get_point(&x, &y, data);
data+= (SIZEOF_STORED_DOUBLE*2);
- /* QQ: Is the following prev_x+x right ? */
lr_area+= (prev_x + x)* (prev_y - y);
prev_x= x;
prev_y= y;
@@ -948,6 +950,8 @@ int Gis_polygon::centroid_xy(double *x, double *y) const
n_linear_rings= uint4korr(data);
data+= 4;
+ DBUG_ASSERT(n_linear_rings > 0);
+
while (n_linear_rings--)
{
uint32 n_points, org_n_points;
diff --git a/sql/spatial.h b/sql/spatial.h
index 837ae153310..dbf5da6665b 100644
--- a/sql/spatial.h
+++ b/sql/spatial.h
@@ -116,12 +116,12 @@ struct MBR
int touches(const MBR *mbr)
{
/* The following should be safe, even if we compare doubles */
- return ((((mbr->xmin == xmax) || (mbr->xmax == xmin)) &&
- ((mbr->ymin >= ymin) && (mbr->ymin <= ymax) ||
- (mbr->ymax >= ymin) && (mbr->ymax <= ymax))) ||
+ return ((((mbr->xmin == xmax) || (mbr->xmax == xmin)) &&
+ (((mbr->ymin >= ymin) && (mbr->ymin <= ymax)) ||
+ ((mbr->ymax >= ymin) && (mbr->ymax <= ymax)))) ||
(((mbr->ymin == ymax) || (mbr->ymax == ymin)) &&
- ((mbr->xmin >= xmin) && (mbr->xmin <= xmax) ||
- (mbr->xmax >= xmin) && (mbr->xmax <= xmax))));
+ (((mbr->xmin >= xmin) && (mbr->xmin <= xmax)) ||
+ ((mbr->xmax >= xmin) && (mbr->xmax <= xmax)))));
}
int within(const MBR *mbr)
@@ -230,7 +230,7 @@ public:
class Class_info
{
public:
- LEX_STRING_WITH_INIT m_name;
+ LEX_STRING m_name;
int m_type_id;
void (*m_create_func)(void *);
Class_info(const char *name, int type_id, void(*create_func)(void *));
@@ -281,7 +281,7 @@ public:
uint32 len, String *res);
int as_wkt(String *wkt, const char **end)
{
- uint32 len= get_class_info()->m_name.length;
+ uint32 len= (uint) get_class_info()->m_name.length;
if (wkt->reserve(len + 2, 512))
return 1;
wkt->qs_append(get_class_info()->m_name.str, len);
diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc
index 44407691abb..bd940608a07 100644
--- a/sql/sql_acl.cc
+++ b/sql/sql_acl.cc
@@ -26,16 +26,131 @@
#include "mysql_priv.h"
#include "hash_filo.h"
-#ifdef HAVE_REPLICATION
-#include "sql_repl.h" //for tables_ok()
-#endif
#include <m_ctype.h>
#include <stdarg.h>
#include "sp_head.h"
#include "sp.h"
+time_t mysql_db_table_last_check= 0L;
+
+TABLE_FIELD_W_TYPE mysql_db_table_fields[MYSQL_DB_FIELD_COUNT] = {
+ {
+ { C_STRING_WITH_LEN("Host") },
+ { C_STRING_WITH_LEN("char(60)") },
+ {NULL, 0}
+ },
+ {
+ { C_STRING_WITH_LEN("Db") },
+ { C_STRING_WITH_LEN("char(64)") },
+ {NULL, 0}
+ },
+ {
+ { C_STRING_WITH_LEN("User") },
+ { C_STRING_WITH_LEN("char(16)") },
+ {NULL, 0}
+ },
+ {
+ { C_STRING_WITH_LEN("Select_priv") },
+ { C_STRING_WITH_LEN("enum('N','Y')") },
+ { C_STRING_WITH_LEN("utf8") }
+ },
+ {
+ { C_STRING_WITH_LEN("Insert_priv") },
+ { C_STRING_WITH_LEN("enum('N','Y')") },
+ { C_STRING_WITH_LEN("utf8") }
+ },
+ {
+ { C_STRING_WITH_LEN("Update_priv") },
+ { C_STRING_WITH_LEN("enum('N','Y')") },
+ { C_STRING_WITH_LEN("utf8") }
+ },
+ {
+ { C_STRING_WITH_LEN("Delete_priv") },
+ { C_STRING_WITH_LEN("enum('N','Y')") },
+ { C_STRING_WITH_LEN("utf8") }
+ },
+ {
+ { C_STRING_WITH_LEN("Create_priv") },
+ { C_STRING_WITH_LEN("enum('N','Y')") },
+ { C_STRING_WITH_LEN("utf8") }
+ },
+ {
+ { C_STRING_WITH_LEN("Drop_priv") },
+ { C_STRING_WITH_LEN("enum('N','Y')") },
+ { C_STRING_WITH_LEN("utf8") }
+ },
+ {
+ { C_STRING_WITH_LEN("Grant_priv") },
+ { C_STRING_WITH_LEN("enum('N','Y')") },
+ { C_STRING_WITH_LEN("utf8") }
+ },
+ {
+ { C_STRING_WITH_LEN("References_priv") },
+ { C_STRING_WITH_LEN("enum('N','Y')") },
+ { C_STRING_WITH_LEN("utf8") }
+ },
+ {
+ { C_STRING_WITH_LEN("Index_priv") },
+ { C_STRING_WITH_LEN("enum('N','Y')") },
+ { C_STRING_WITH_LEN("utf8") }
+ },
+ {
+ { C_STRING_WITH_LEN("Alter_priv") },
+ { C_STRING_WITH_LEN("enum('N','Y')") },
+ { C_STRING_WITH_LEN("utf8") }
+ },
+ {
+ { C_STRING_WITH_LEN("Create_tmp_table_priv") },
+ { C_STRING_WITH_LEN("enum('N','Y')") },
+ { C_STRING_WITH_LEN("utf8") }
+ },
+ {
+ { C_STRING_WITH_LEN("Lock_tables_priv") },
+ { C_STRING_WITH_LEN("enum('N','Y')") },
+ { C_STRING_WITH_LEN("utf8") }
+ },
+ {
+ { C_STRING_WITH_LEN("Create_view_priv") },
+ { C_STRING_WITH_LEN("enum('N','Y')") },
+ { C_STRING_WITH_LEN("utf8") }
+ },
+ {
+ { C_STRING_WITH_LEN("Show_view_priv") },
+ { C_STRING_WITH_LEN("enum('N','Y')") },
+ { C_STRING_WITH_LEN("utf8") }
+ },
+ {
+ { C_STRING_WITH_LEN("Create_routine_priv") },
+ { C_STRING_WITH_LEN("enum('N','Y')") },
+ { C_STRING_WITH_LEN("utf8") }
+ },
+ {
+ { C_STRING_WITH_LEN("Alter_routine_priv") },
+ { C_STRING_WITH_LEN("enum('N','Y')") },
+ { C_STRING_WITH_LEN("utf8") }
+ },
+ {
+ { C_STRING_WITH_LEN("Execute_priv") },
+ { C_STRING_WITH_LEN("enum('N','Y')") },
+ { C_STRING_WITH_LEN("utf8") }
+ },
+ {
+ { C_STRING_WITH_LEN("Event_priv") },
+ { C_STRING_WITH_LEN("enum('N','Y')") },
+ { C_STRING_WITH_LEN("utf8") }
+ },
+ {
+ { C_STRING_WITH_LEN("Trigger_priv") },
+ { C_STRING_WITH_LEN("enum('N','Y')") },
+ { C_STRING_WITH_LEN("utf8") }
+ }
+};
+
+
#ifndef NO_EMBEDDED_ACCESS_CHECKS
+#define FIRST_NON_YN_FIELD 26
+
class acl_entry :public hash_filo_element
{
public:
@@ -45,11 +160,11 @@ public:
};
-static byte* acl_entry_get_key(acl_entry *entry,uint *length,
- my_bool not_used __attribute__((unused)))
+static uchar* acl_entry_get_key(acl_entry *entry, size_t *length,
+ my_bool not_used __attribute__((unused)))
{
*length=(uint) entry->length;
- return (byte*) entry->key;
+ return (uchar*) entry->key;
}
#define IP_ADDR_STRLEN (3+1+3+1+3+1+3)
@@ -77,7 +192,7 @@ static void update_hostname(acl_host_and_ip *host, const char *hostname);
static bool compare_hostname(const acl_host_and_ip *host,const char *hostname,
const char *ip);
static my_bool acl_load(THD *thd, TABLE_LIST *tables);
-static my_bool grant_load(TABLE_LIST *tables);
+static my_bool grant_load(THD *thd, TABLE_LIST *tables);
/*
Convert scrambled password to binary form, according to scramble type,
@@ -162,6 +277,7 @@ 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
@@ -198,8 +314,11 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables)
bool check_no_resolve= specialflag & SPECIAL_NO_RESOLVE;
char tmp_name[NAME_LEN+1];
int password_length;
+ ulong old_sql_mode= thd->variables.sql_mode;
DBUG_ENTER("acl_load");
+ thd->variables.sql_mode&= ~MODE_PAD_CHAR_TO_FULL_LENGTH;
+
grant_version++; /* Privileges updated */
acl_cache->clear(1); // Clear locked hostname cache
@@ -207,6 +326,7 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables)
init_sql_alloc(&mem, ACL_ALLOC_BLOCK_SIZE, 0);
init_read_record(&read_record_info,thd,table= tables[0].table,NULL,1,0,
FALSE);
+ table->use_all_columns();
VOID(my_init_dynamic_array(&acl_hosts,sizeof(ACL_HOST),20,50));
while (!(read_record_info.read_record(&read_record_info)))
{
@@ -247,14 +367,15 @@ 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,(gptr) &host));
+ VOID(push_dynamic(&acl_hosts,(uchar*) &host));
}
- my_qsort((gptr) dynamic_element(&acl_hosts,0,ACL_HOST*),acl_hosts.elements,
- sizeof(ACL_HOST),(qsort_cmp) acl_compare);
+ my_qsort((uchar*) dynamic_element(&acl_hosts,0,ACL_HOST*),acl_hosts.elements,
+ sizeof(ACL_HOST),(qsort_cmp) acl_compare);
end_read_record(&read_record_info);
freeze_size(&acl_hosts);
init_read_record(&read_record_info,thd,table=tables[1].table,NULL,1,0,FALSE);
+ table->use_all_columns();
VOID(my_init_dynamic_array(&acl_users,sizeof(ACL_USER),50,100));
password_length= table->field[2]->field_length /
table->field[2]->charset()->mbmaxlen;
@@ -313,8 +434,8 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables)
}
const char *password= get_field(thd->mem_root, table->field[2]);
- size_t password_len= password ? strlen(password) : 0;
- set_user_salt(&user, password, (uint) password_len);
+ uint password_len= password ? strlen(password) : 0;
+ set_user_salt(&user, password, password_len);
if (user.salt_len == 0 && password_len != 0)
{
switch (password_len) {
@@ -358,6 +479,20 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables)
if (table->s->fields <= 36 && (user.access & GRANT_ACL))
user.access|= CREATE_USER_ACL;
+
+ /*
+ if it is pre 5.1.6 privilege table then map CREATE privilege on
+ CREATE|ALTER|DROP|EXECUTE EVENT
+ */
+ if (table->s->fields <= 37 && (user.access & SUPER_ACL))
+ user.access|= EVENT_ACL;
+
+ /*
+ if it is pre 5.1.6 privilege then map TRIGGER privilege on CREATE.
+ */
+ if (table->s->fields <= 38 && (user.access & SUPER_ACL))
+ user.access|= TRIGGER_ACL;
+
user.sort= get_sort(2,user.host.hostname,user.user);
user.hostname_length= (user.host.hostname ?
(uint) strlen(user.host.hostname) : 0);
@@ -416,30 +551,31 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables)
user.access|= SUPER_ACL | EXECUTE_ACL;
#endif
}
- VOID(push_dynamic(&acl_users,(gptr) &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
}
}
- my_qsort((gptr) dynamic_element(&acl_users,0,ACL_USER*),acl_users.elements,
- sizeof(ACL_USER),(qsort_cmp) acl_compare);
+ my_qsort((uchar*) dynamic_element(&acl_users,0,ACL_USER*),acl_users.elements,
+ sizeof(ACL_USER),(qsort_cmp) acl_compare);
end_read_record(&read_record_info);
freeze_size(&acl_users);
init_read_record(&read_record_info,thd,table=tables[2].table,NULL,1,0,FALSE);
+ table->use_all_columns();
VOID(my_init_dynamic_array(&acl_dbs,sizeof(ACL_DB),50,100));
while (!(read_record_info.read_record(&read_record_info)))
{
ACL_DB db;
- update_hostname(&db.host,get_field(&mem, table->field[0]));
- db.db=get_field(&mem, table->field[1]);
+ update_hostname(&db.host,get_field(&mem, table->field[MYSQL_DB_FIELD_HOST]));
+ db.db=get_field(&mem, table->field[MYSQL_DB_FIELD_DB]);
if (!db.db)
{
sql_print_warning("Found an entry in the 'db' table with empty database name; Skipped");
continue;
}
- db.user=get_field(&mem, table->field[2]);
+ db.user=get_field(&mem, table->field[MYSQL_DB_FIELD_USER]);
if (check_no_resolve && hostname_requires_resolving(db.host.hostname))
{
sql_print_warning("'db' entry '%s %s@%s' "
@@ -478,10 +614,10 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables)
db.access|=REFERENCES_ACL | INDEX_ACL | ALTER_ACL;
}
#endif
- VOID(push_dynamic(&acl_dbs,(gptr) &db));
+ VOID(push_dynamic(&acl_dbs,(uchar*) &db));
}
- my_qsort((gptr) dynamic_element(&acl_dbs,0,ACL_DB*),acl_dbs.elements,
- sizeof(ACL_DB),(qsort_cmp) acl_compare);
+ 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);
init_check_host();
@@ -490,6 +626,7 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables)
return_val=0;
end:
+ thd->variables.sql_mode= old_sql_mode;
DBUG_RETURN(return_val);
}
@@ -559,11 +696,13 @@ my_bool acl_reload(THD *thd)
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))
{
sql_print_error("Fatal error: Can't open and lock privilege tables: %s",
- thd->net.last_error);
+ thd->main_da.message());
goto end;
}
@@ -629,7 +768,7 @@ static ulong get_access(TABLE *form, uint fieldnr, uint *next_field)
Field **pos;
for (pos=form->field+fieldnr, bit=1;
- *pos && (*pos)->real_type() == FIELD_TYPE_ENUM &&
+ *pos && (*pos)->real_type() == MYSQL_TYPE_ENUM &&
((Field_enum*) (*pos))->typelib->count == 2 ;
pos++, fieldnr++, bit<<=1)
{
@@ -1024,11 +1163,11 @@ bool acl_getroot_no_password(Security_context *sctx, char *user, char *host,
DBUG_RETURN(res);
}
-static byte* check_get_key(ACL_USER *buff,uint *length,
- my_bool not_used __attribute__((unused)))
+static uchar* check_get_key(ACL_USER *buff, size_t *length,
+ my_bool not_used __attribute__((unused)))
{
*length=buff->hostname_length;
- return (byte*) buff->host.hostname;
+ return (uchar*) buff->host.hostname;
}
@@ -1109,12 +1248,12 @@ static void acl_insert_user(const char *user, const char *host,
set_user_salt(&acl_user, password, password_len);
- VOID(push_dynamic(&acl_users,(gptr) &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 */
- my_qsort((gptr) dynamic_element(&acl_users,0,ACL_USER*),acl_users.elements,
- sizeof(ACL_USER),(qsort_cmp) acl_compare);
+ my_qsort((uchar*) dynamic_element(&acl_users,0,ACL_USER*),acl_users.elements,
+ sizeof(ACL_USER),(qsort_cmp) acl_compare);
/* Rebuild 'acl_check_hosts' since 'acl_users' has been modified */
rebuild_check_host();
@@ -1175,9 +1314,9 @@ static void acl_insert_db(const char *user, const char *host, const char *db,
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,(gptr) &acl_db));
- my_qsort((gptr) dynamic_element(&acl_dbs,0,ACL_DB*),acl_dbs.elements,
- sizeof(ACL_DB),(qsort_cmp) acl_compare);
+ 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);
}
@@ -1193,7 +1332,8 @@ ulong acl_get(const char *host, const char *ip,
const char *user, const char *db, my_bool db_is_pattern)
{
ulong host_access= ~(ulong)0, db_access= 0;
- uint i,key_length;
+ uint i;
+ size_t key_length;
char key[ACL_KEY_LENGTH],*tmp_db,*end;
acl_entry *entry;
DBUG_ENTER("acl_get");
@@ -1205,8 +1345,9 @@ ulong acl_get(const char *host, const char *ip,
my_casedn_str(files_charset_info, tmp_db);
db=tmp_db;
}
- key_length=(uint) (end-key);
- if (!db_is_pattern && (entry=(acl_entry*) acl_cache->search(key,key_length)))
+ key_length= (size_t) (end-key);
+ 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));
@@ -1260,7 +1401,7 @@ exit:
{
entry->access=(db_access & host_access);
entry->length=key_length;
- memcpy((gptr) entry->key,key,key_length);
+ memcpy((uchar*) entry->key,key,key_length);
acl_cache->add(entry);
}
VOID(pthread_mutex_unlock(&acl_cache->lock));
@@ -1302,12 +1443,12 @@ static void init_check_host(void)
break; // already stored
}
if (j == acl_wild_hosts.elements) // If new
- (void) push_dynamic(&acl_wild_hosts,(char*) &acl_user->host);
+ (void) push_dynamic(&acl_wild_hosts,(uchar*) &acl_user->host);
}
- else if (!hash_search(&acl_check_hosts,(byte*) acl_user->host.hostname,
- (uint) strlen(acl_user->host.hostname)))
+ else if (!hash_search(&acl_check_hosts,(uchar*) acl_user->host.hostname,
+ strlen(acl_user->host.hostname)))
{
- if (my_hash_insert(&acl_check_hosts,(byte*) acl_user))
+ if (my_hash_insert(&acl_check_hosts,(uchar*) acl_user))
{ // End of memory
allow_all_hosts=1; // Should never happen
DBUG_VOID_RETURN;
@@ -1345,8 +1486,8 @@ bool acl_check_host(const char *host, const char *ip)
return 0;
VOID(pthread_mutex_lock(&acl_cache->lock));
- if (host && hash_search(&acl_check_hosts,(byte*) host,(uint) strlen(host)) ||
- ip && hash_search(&acl_check_hosts,(byte*) ip,(uint) strlen(ip)))
+ if (host && hash_search(&acl_check_hosts,(uchar*) host,strlen(host)) ||
+ ip && hash_search(&acl_check_hosts,(uchar*) ip, strlen(ip)))
{
VOID(pthread_mutex_unlock(&acl_cache->lock));
return 0; // Found host
@@ -1439,14 +1580,14 @@ bool change_password(THD *thd, const char *host, const char *user,
/* Buffer should be extended when password length is extended. */
char buff[512];
ulong query_length;
- size_t new_password_len= strlen(new_password);
+ uint new_password_len= (uint) strlen(new_password);
bool result= 1;
DBUG_ENTER("change_password");
DBUG_PRINT("enter",("host: '%s' user: '%s' new_password: '%s'",
host,user,new_password));
DBUG_ASSERT(host != 0); // Ensured by parent
- if (check_change_password(thd, host, user, new_password, (uint) new_password_len))
+ if (check_change_password(thd, host, user, new_password, new_password_len))
DBUG_RETURN(1);
bzero((char*) &tables, sizeof(tables));
@@ -1458,7 +1599,7 @@ bool change_password(THD *thd, const char *host, const char *user,
GRANT and REVOKE are applied the slave in/exclusion rules as they are
some kind of updates to the mysql.% tables.
*/
- if (thd->slave_thread && table_rules_on)
+ if (thd->slave_thread && rpl_filter->is_on())
{
/*
The tables must be marked "updating" so that tables_ok() takes them into
@@ -1466,12 +1607,12 @@ bool change_password(THD *thd, const char *host, const char *user,
*/
tables.updating= 1;
/* Thanks to bzero, tables.next==0 */
- if (!tables_ok(thd, &tables))
+ if (!(thd->spcont || rpl_filter->tables_ok(0, &tables)))
DBUG_RETURN(0);
}
#endif
- if (!(table= open_ltable(thd, &tables, TL_WRITE)))
+ if (!(table= open_ltable(thd, &tables, TL_WRITE, 0)))
DBUG_RETURN(1);
VOID(pthread_mutex_lock(&acl_cache->lock));
@@ -1483,12 +1624,12 @@ bool change_password(THD *thd, const char *host, const char *user,
goto end;
}
/* update loaded acl entry: */
- set_user_salt(acl_user, new_password, (uint) new_password_len);
+ set_user_salt(acl_user, new_password, new_password_len);
if (update_user_table(thd, table,
acl_user->host.hostname ? acl_user->host.hostname : "",
acl_user->user ? acl_user->user : "",
- new_password, (uint) new_password_len))
+ new_password, new_password_len))
{
VOID(pthread_mutex_unlock(&acl_cache->lock)); /* purecov: deadcode */
goto end;
@@ -1506,8 +1647,7 @@ bool change_password(THD *thd, const char *host, const char *user,
acl_user->host.hostname ? acl_user->host.hostname : "",
new_password));
thd->clear_error();
- Query_log_event qinfo(thd, buff, query_length, 0, FALSE);
- mysql_bin_log.write(&qinfo);
+ thd->binlog_query(THD::MYSQL_QUERY_TYPE, buff, query_length, FALSE, FALSE);
}
end:
close_thread_tables(thd);
@@ -1644,8 +1784,8 @@ bool hostname_requires_resolving(const char *hostname)
size_t namelen= strlen(hostname);
size_t lhlen= strlen(my_localhost);
if ((namelen == lhlen) &&
- !my_strnncoll(system_charset_info, (const uchar *)hostname, (uint) namelen,
- (const uchar *)my_localhost, (uint) strlen(my_localhost)))
+ !my_strnncoll(system_charset_info, (const uchar *)hostname, namelen,
+ (const uchar *)my_localhost, strlen(my_localhost)))
return FALSE;
for (; (cur=*hostname); hostname++)
{
@@ -1679,15 +1819,15 @@ static bool update_user_table(THD *thd, TABLE *table,
DBUG_ENTER("update_user_table");
DBUG_PRINT("enter",("user: %s host: %s",user,host));
+ table->use_all_columns();
table->field[0]->store(host,(uint) strlen(host), system_charset_info);
table->field[1]->store(user,(uint) strlen(user), system_charset_info);
- key_copy((byte *) user_key, table->record[0], table->key_info,
+ key_copy((uchar *) user_key, table->record[0], table->key_info,
table->key_info->key_length);
- table->file->extra(HA_EXTRA_RETRIEVE_ALL_COLS);
- if (table->file->index_read_idx(table->record[0], 0,
- (byte *) user_key, table->key_info->key_length,
- HA_READ_KEY_EXACT))
+ if (table->file->index_read_idx_map(table->record[0], 0,
+ (uchar *) user_key, HA_WHOLE_KEY,
+ HA_READ_KEY_EXACT))
{
my_message(ER_PASSWORD_NO_MATCH, ER(ER_PASSWORD_NO_MATCH),
MYF(0)); /* purecov: deadcode */
@@ -1695,7 +1835,8 @@ static bool update_user_table(THD *thd, TABLE *table,
}
store_record(table,record[1]);
table->field[2]->store(new_password, new_password_len, system_charset_info);
- if ((error=table->file->update_row(table->record[1],table->record[0])))
+ if ((error=table->file->ha_update_row(table->record[1],table->record[0])) &&
+ error != HA_ERR_RECORD_IS_THE_SAME)
{
table->file->print_error(error,MYF(0)); /* purecov: deadcode */
DBUG_RETURN(1);
@@ -1751,7 +1892,7 @@ static int replace_user_table(THD *thd, TABLE *table, const LEX_USER &combo,
const char *password= "";
uint password_len= 0;
char what= (revoke_grant) ? 'N' : 'Y';
- byte user_key[MAX_KEY_LENGTH];
+ uchar user_key[MAX_KEY_LENGTH];
LEX *lex= thd->lex;
DBUG_ENTER("replace_user_table");
@@ -1769,15 +1910,17 @@ static int replace_user_table(THD *thd, TABLE *table, const LEX_USER &combo,
password=combo.password.str;
}
- table->field[0]->store(combo.host.str,combo.host.length, system_charset_info);
- table->field[1]->store(combo.user.str,combo.user.length, system_charset_info);
+ table->use_all_columns();
+ table->field[0]->store(combo.host.str,combo.host.length,
+ system_charset_info);
+ table->field[1]->store(combo.user.str,combo.user.length,
+ system_charset_info);
key_copy(user_key, table->record[0], table->key_info,
table->key_info->key_length);
- table->file->extra(HA_EXTRA_RETRIEVE_ALL_COLS);
- if (table->file->index_read_idx(table->record[0], 0,
- user_key, table->key_info->key_length,
- HA_READ_KEY_EXACT))
+ if (table->file->index_read_idx_map(table->record[0], 0, user_key,
+ HA_WHOLE_KEY,
+ HA_READ_KEY_EXACT))
{
/* what == 'N' means revoke */
if (what == 'N')
@@ -1838,7 +1981,7 @@ static int replace_user_table(THD *thd, TABLE *table, const LEX_USER &combo,
ulong priv;
uint next_field;
for (tmp_field= table->field+3, priv = SELECT_ACL;
- *tmp_field && (*tmp_field)->real_type() == FIELD_TYPE_ENUM &&
+ *tmp_field && (*tmp_field)->real_type() == MYSQL_TYPE_ENUM &&
((Field_enum*) (*tmp_field))->typelib->count == 2 ;
tmp_field++, priv <<= 1)
{
@@ -1873,13 +2016,13 @@ static int replace_user_table(THD *thd, TABLE *table, const LEX_USER &combo,
table->field[next_field+3]->store("", 0, &my_charset_latin1);
if (lex->ssl_cipher)
table->field[next_field+1]->store(lex->ssl_cipher,
- (uint) strlen(lex->ssl_cipher), system_charset_info);
+ strlen(lex->ssl_cipher), system_charset_info);
if (lex->x509_issuer)
table->field[next_field+2]->store(lex->x509_issuer,
- (uint) strlen(lex->x509_issuer), system_charset_info);
+ strlen(lex->x509_issuer), system_charset_info);
if (lex->x509_subject)
table->field[next_field+3]->store(lex->x509_subject,
- (uint) strlen(lex->x509_subject), system_charset_info);
+ strlen(lex->x509_subject), system_charset_info);
break;
case SSL_TYPE_NOT_SPECIFIED:
break;
@@ -1910,19 +2053,23 @@ static int replace_user_table(THD *thd, TABLE *table, const LEX_USER &combo,
We should NEVER delete from the user table, as a uses can still
use mysqld even if he doesn't have any privileges in the user table!
*/
- table->file->extra(HA_EXTRA_RETRIEVE_ALL_COLS);
- if (cmp_record(table,record[1]) &&
- (error=table->file->update_row(table->record[1],table->record[0])))
- { // This should never happen
- table->file->print_error(error,MYF(0)); /* purecov: deadcode */
- error= -1; /* purecov: deadcode */
- goto end; /* purecov: deadcode */
+ if (cmp_record(table,record[1]))
+ {
+ if ((error=
+ table->file->ha_update_row(table->record[1],table->record[0])) &&
+ error != HA_ERR_RECORD_IS_THE_SAME)
+ { // This should never happen
+ table->file->print_error(error,MYF(0)); /* purecov: deadcode */
+ error= -1; /* purecov: deadcode */
+ goto end; /* purecov: deadcode */
+ }
+ else
+ error= 0;
}
}
- else if ((error=table->file->write_row(table->record[0]))) // insert
+ else if ((error=table->file->ha_write_row(table->record[0]))) // insert
{ // This should never happen
- if (error && error != HA_ERR_FOUND_DUPP_KEY &&
- error != HA_ERR_FOUND_DUPP_UNIQUE) /* purecov: inspected */
+ if (table->file->is_fatal_error(error, HA_CHECK_DUP))
{
table->file->print_error(error,MYF(0)); /* purecov: deadcode */
error= -1; /* purecov: deadcode */
@@ -1970,7 +2117,7 @@ static int replace_db_table(TABLE *table, const char *db,
bool old_row_exists=0;
int error;
char what= (revoke_grant) ? 'N' : 'Y';
- byte user_key[MAX_KEY_LENGTH];
+ uchar user_key[MAX_KEY_LENGTH];
DBUG_ENTER("replace_db_table");
if (!initialized)
@@ -1986,16 +2133,18 @@ static int replace_db_table(TABLE *table, const char *db,
DBUG_RETURN(-1);
}
- table->field[0]->store(combo.host.str,combo.host.length, system_charset_info);
+ table->use_all_columns();
+ table->field[0]->store(combo.host.str,combo.host.length,
+ system_charset_info);
table->field[1]->store(db,(uint) strlen(db), system_charset_info);
- table->field[2]->store(combo.user.str,combo.user.length, system_charset_info);
+ table->field[2]->store(combo.user.str,combo.user.length,
+ system_charset_info);
key_copy(user_key, table->record[0], table->key_info,
table->key_info->key_length);
- table->file->extra(HA_EXTRA_RETRIEVE_ALL_COLS);
- if (table->file->index_read_idx(table->record[0],0,
- user_key, table->key_info->key_length,
- HA_READ_KEY_EXACT))
+ if (table->file->index_read_idx_map(table->record[0],0, user_key,
+ HA_WHOLE_KEY,
+ HA_READ_KEY_EXACT))
{
if (what == 'N')
{ // no row, no revoke
@@ -2004,9 +2153,11 @@ static int replace_db_table(TABLE *table, const char *db,
}
old_row_exists = 0;
restore_record(table, s->default_values);
- table->field[0]->store(combo.host.str,combo.host.length, system_charset_info);
+ table->field[0]->store(combo.host.str,combo.host.length,
+ system_charset_info);
table->field[1]->store(db,(uint) strlen(db), system_charset_info);
- table->field[2]->store(combo.user.str,combo.user.length, system_charset_info);
+ table->field[2]->store(combo.user.str,combo.user.length,
+ system_charset_info);
}
else
{
@@ -2028,19 +2179,20 @@ static int replace_db_table(TABLE *table, const char *db,
/* update old existing row */
if (rights)
{
- table->file->extra(HA_EXTRA_RETRIEVE_ALL_COLS);
- if ((error=table->file->update_row(table->record[1],table->record[0])))
+ if ((error= table->file->ha_update_row(table->record[1],
+ table->record[0])) &&
+ error != HA_ERR_RECORD_IS_THE_SAME)
goto table_error; /* purecov: deadcode */
}
else /* must have been a revoke of all privileges */
{
- if ((error = table->file->delete_row(table->record[1])))
+ if ((error= table->file->ha_delete_row(table->record[1])))
goto table_error; /* purecov: deadcode */
}
}
- else if (rights && (error=table->file->write_row(table->record[0])))
+ else if (rights && (error= table->file->ha_write_row(table->record[0])))
{
- if (error && error != HA_ERR_FOUND_DUPP_KEY) /* purecov: inspected */
+ if (table->file->is_fatal_error(error, HA_CHECK_DUP_KEY))
goto table_error; /* purecov: deadcode */
}
@@ -2069,16 +2221,16 @@ public:
uint key_length;
GRANT_COLUMN(String &c, ulong y) :rights (y)
{
- column= memdup_root(&memex,c.ptr(), key_length=c.length());
+ column= (char*) memdup_root(&memex,c.ptr(), key_length=c.length());
}
};
-static byte* get_key_column(GRANT_COLUMN *buff,uint *length,
+static uchar* get_key_column(GRANT_COLUMN *buff, size_t *length,
my_bool not_used __attribute__((unused)))
{
*length=buff->key_length;
- return (byte*) buff->column;
+ return (uchar*) buff->column;
}
@@ -2089,7 +2241,7 @@ public:
char *db, *user, *tname, *hash_key;
ulong privs;
ulong sort;
- uint key_length;
+ size_t key_length;
GRANT_NAME(const char *h, const char *d,const char *u,
const char *t, ulong p);
GRANT_NAME (TABLE *form);
@@ -2128,8 +2280,8 @@ GRANT_NAME::GRANT_NAME(const char *h, const char *d,const char *u,
my_casedn_str(files_charset_info, db);
my_casedn_str(files_charset_info, tname);
}
- key_length =(uint) strlen(d)+(uint) strlen(u)+(uint) strlen(t)+3;
- hash_key = (char*) alloc_root(&memex,key_length);
+ key_length= strlen(d) + strlen(u)+ strlen(t)+3;
+ hash_key= (char*) alloc_root(&memex,key_length);
strmov(strmov(strmov(hash_key,user)+1,db)+1,tname);
}
@@ -2138,7 +2290,7 @@ GRANT_TABLE::GRANT_TABLE(const char *h, const char *d,const char *u,
const char *t, ulong p, ulong c)
:GRANT_NAME(h,d,u,t,p), cols(c)
{
- (void) hash_init(&hash_columns,system_charset_info,
+ (void) hash_init2(&hash_columns,4,system_charset_info,
0,0,0, (hash_get_key) get_key_column,0,0);
}
@@ -2163,9 +2315,8 @@ GRANT_NAME::GRANT_NAME(TABLE *form)
my_casedn_str(files_charset_info, db);
my_casedn_str(files_charset_info, tname);
}
- key_length = ((uint) strlen(db) + (uint) strlen(user) +
- (uint) strlen(tname) + 3);
- hash_key = (char*) alloc_root(&memex,key_length);
+ key_length= (strlen(db) + strlen(user) + strlen(tname) + 3);
+ hash_key= (char*) alloc_root(&memex, key_length);
strmov(strmov(strmov(hash_key,user)+1,db)+1,tname);
privs = (ulong) form->field[6]->val_int();
privs = fix_rights_for_table(privs);
@@ -2175,7 +2326,7 @@ GRANT_NAME::GRANT_NAME(TABLE *form)
GRANT_TABLE::GRANT_TABLE(TABLE *form, TABLE *col_privs)
:GRANT_NAME(form)
{
- byte key[MAX_KEY_LENGTH];
+ uchar key[MAX_KEY_LENGTH];
if (!db || !tname)
{
@@ -2187,14 +2338,15 @@ GRANT_TABLE::GRANT_TABLE(TABLE *form, TABLE *col_privs)
cols= (ulong) form->field[7]->val_int();
cols = fix_rights_for_column(cols);
- (void) hash_init(&hash_columns,system_charset_info,
+ (void) hash_init2(&hash_columns,4,system_charset_info,
0,0,0, (hash_get_key) get_key_column,0,0);
if (cols)
{
uint key_prefix_len;
KEY_PART_INFO *key_part= col_privs->key_info->key_part;
col_privs->field[0]->store(host.hostname,
- host.hostname ? (uint) strlen(host.hostname) : 0,
+ host.hostname ? (uint) strlen(host.hostname) :
+ 0,
system_charset_info);
col_privs->field[1]->store(db,(uint) strlen(db), system_charset_info);
col_privs->field[2]->store(user,(uint) strlen(user), system_charset_info);
@@ -2207,10 +2359,9 @@ 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);
- if (col_privs->file->index_read(col_privs->record[0],
- (byte*) key,
- key_prefix_len, HA_READ_KEY_EXACT))
+ col_privs->file->ha_index_init(0, 1);
+ if (col_privs->file->index_read_map(col_privs->record[0], (uchar*) key,
+ (key_part_map)15, HA_READ_KEY_EXACT))
{
cols = 0; /* purecov: deadcode */
col_privs->file->ha_index_end();
@@ -2230,7 +2381,7 @@ GRANT_TABLE::GRANT_TABLE(TABLE *form, TABLE *col_privs)
privs = cols = 0; /* purecov: deadcode */
return; /* purecov: deadcode */
}
- my_hash_insert(&hash_columns, (byte *) mem_check);
+ my_hash_insert(&hash_columns, (uchar *) mem_check);
} while (!col_privs->file->index_next(col_privs->record[0]) &&
!key_cmp_if_same(col_privs,key,0,key_prefix_len));
col_privs->file->ha_index_end();
@@ -2244,11 +2395,11 @@ GRANT_TABLE::~GRANT_TABLE()
}
-static byte* get_grant_table(GRANT_NAME *buff,uint *length,
+static uchar* get_grant_table(GRANT_NAME *buff, size_t *length,
my_bool not_used __attribute__((unused)))
{
*length=buff->key_length;
- return (byte*) buff->hash_key;
+ return (uchar*) buff->hash_key;
}
@@ -2261,10 +2412,10 @@ void free_grant_table(GRANT_TABLE *grant_table)
/* Search after a matching grant. Prefer exact grants before not exact ones */
static GRANT_NAME *name_hash_search(HASH *name_hash,
- const char *host,const char* ip,
- const char *db,
- const char *user, const char *tname,
- bool exact)
+ const char *host,const char* ip,
+ const char *db,
+ const char *user, const char *tname,
+ bool exact)
{
char helping [NAME_LEN*2+USERNAME_LENGTH+3];
uint len;
@@ -2272,10 +2423,10 @@ static GRANT_NAME *name_hash_search(HASH *name_hash,
HASH_SEARCH_STATE state;
len = (uint) (strmov(strmov(strmov(helping,user)+1,db)+1,tname)-helping)+ 1;
- for (grant_name= (GRANT_NAME*) hash_first(name_hash, (byte*) helping,
+ for (grant_name= (GRANT_NAME*) hash_first(name_hash, (uchar*) helping,
len, &state);
grant_name ;
- grant_name= (GRANT_NAME*) hash_next(name_hash,(byte*) helping,
+ grant_name= (GRANT_NAME*) hash_next(name_hash,(uchar*) helping,
len, &state))
{
if (exact)
@@ -2320,7 +2471,7 @@ 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, (byte*) cname,length);
+ return (GRANT_COLUMN*) hash_search(&t->hash_columns, (uchar*) cname,length);
}
@@ -2331,11 +2482,12 @@ static int replace_column_table(GRANT_TABLE *g_t,
ulong rights, bool revoke_grant)
{
int error=0,result=0;
- byte key[MAX_KEY_LENGTH];
+ uchar key[MAX_KEY_LENGTH];
uint key_prefix_length;
KEY_PART_INFO *key_part= table->key_info->key_part;
DBUG_ENTER("replace_column_table");
+ table->use_all_columns();
table->field[0]->store(combo.host.str,combo.host.length,
system_charset_info);
table->field[1]->store(db,(uint) strlen(db),
@@ -2345,7 +2497,7 @@ static int replace_column_table(GRANT_TABLE *g_t,
table->field[3]->store(table_name,(uint) strlen(table_name),
system_charset_info);
- /* Get length of 3 first key parts */
+ /* Get length of 4 first key parts */
key_prefix_length= (key_part[0].store_length + key_part[1].store_length +
key_part[2].store_length + key_part[3].store_length);
key_copy(key, table->record[0], table->key_info, key_prefix_length);
@@ -2356,12 +2508,12 @@ 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);
+ table->file->ha_index_init(0, 1);
while ((column= iter++))
{
ulong privileges= column->rights;
bool old_row_exists=0;
- byte user_key[MAX_KEY_LENGTH];
+ uchar user_key[MAX_KEY_LENGTH];
key_restore(table->record[0],key,table->key_info,
key_prefix_length);
@@ -2371,10 +2523,8 @@ static int replace_column_table(GRANT_TABLE *g_t,
key_copy(user_key, table->record[0], table->key_info,
table->key_info->key_length);
- table->file->extra(HA_EXTRA_RETRIEVE_ALL_COLS);
- if (table->file->index_read(table->record[0], user_key,
- table->key_info->key_length,
- HA_READ_KEY_EXACT))
+ if (table->file->index_read_map(table->record[0], user_key, HA_WHOLE_KEY,
+ HA_READ_KEY_EXACT))
{
if (revoke_grant)
{
@@ -2410,15 +2560,17 @@ static int replace_column_table(GRANT_TABLE *g_t,
{
GRANT_COLUMN *grant_column;
if (privileges)
- error=table->file->update_row(table->record[1],table->record[0]);
+ error=table->file->ha_update_row(table->record[1],table->record[0]);
else
- error=table->file->delete_row(table->record[1]);
- if (error)
+ error=table->file->ha_delete_row(table->record[1]);
+ if (error && error != HA_ERR_RECORD_IS_THE_SAME)
{
table->file->print_error(error,MYF(0)); /* purecov: inspected */
result= -1; /* purecov: inspected */
goto end; /* purecov: inspected */
}
+ else
+ error= 0;
grant_column= column_hash_search(g_t, column->column.ptr(),
column->column.length());
if (grant_column) // Should always be true
@@ -2427,14 +2579,14 @@ static int replace_column_table(GRANT_TABLE *g_t,
else // new grant
{
GRANT_COLUMN *grant_column;
- if ((error=table->file->write_row(table->record[0])))
+ if ((error=table->file->ha_write_row(table->record[0])))
{
table->file->print_error(error,MYF(0)); /* purecov: inspected */
result= -1; /* purecov: inspected */
goto end; /* purecov: inspected */
}
grant_column= new GRANT_COLUMN(column->column,privileges);
- my_hash_insert(&g_t->hash_columns,(byte*) grant_column);
+ my_hash_insert(&g_t->hash_columns,(uchar*) grant_column);
}
}
@@ -2445,14 +2597,13 @@ static int replace_column_table(GRANT_TABLE *g_t,
if (revoke_grant)
{
- byte user_key[MAX_KEY_LENGTH];
+ uchar user_key[MAX_KEY_LENGTH];
key_copy(user_key, table->record[0], table->key_info,
key_prefix_length);
- table->file->extra(HA_EXTRA_RETRIEVE_ALL_COLS);
- if (table->file->index_read(table->record[0], user_key,
- key_prefix_length,
- HA_READ_KEY_EXACT))
+ if (table->file->index_read_map(table->record[0], user_key,
+ (key_part_map)15,
+ HA_READ_KEY_EXACT))
goto end;
/* Scan through all rows with the same host,db,user and table */
@@ -2479,8 +2630,9 @@ static int replace_column_table(GRANT_TABLE *g_t,
if (privileges)
{
int tmp_error;
- if ((tmp_error=table->file->update_row(table->record[1],
- table->record[0])))
+ if ((tmp_error=table->file->ha_update_row(table->record[1],
+ table->record[0])) &&
+ tmp_error != HA_ERR_RECORD_IS_THE_SAME)
{ /* purecov: deadcode */
table->file->print_error(tmp_error,MYF(0)); /* purecov: deadcode */
result= -1; /* purecov: deadcode */
@@ -2492,14 +2644,14 @@ static int replace_column_table(GRANT_TABLE *g_t,
else
{
int tmp_error;
- if ((tmp_error = table->file->delete_row(table->record[1])))
+ if ((tmp_error = table->file->ha_delete_row(table->record[1])))
{ /* purecov: deadcode */
table->file->print_error(tmp_error,MYF(0)); /* purecov: deadcode */
result= -1; /* purecov: deadcode */
goto end; /* purecov: deadcode */
}
if (grant_column)
- hash_delete(&g_t->hash_columns,(byte*) grant_column);
+ hash_delete(&g_t->hash_columns,(uchar*) grant_column);
}
}
} while (!table->file->index_next(table->record[0]) &&
@@ -2522,7 +2674,7 @@ static int replace_table_table(THD *thd, GRANT_TABLE *grant_table,
int old_row_exists = 1;
int error=0;
ulong store_table_rights, store_col_rights;
- byte user_key[MAX_KEY_LENGTH];
+ uchar user_key[MAX_KEY_LENGTH];
DBUG_ENTER("replace_table_table");
strxmov(grantor, thd->security_ctx->user, "@",
@@ -2539,19 +2691,22 @@ static int replace_table_table(THD *thd, GRANT_TABLE *grant_table,
DBUG_RETURN(-1); /* purecov: deadcode */
}
+ table->use_all_columns();
restore_record(table, s->default_values); // Get empty record
- table->field[0]->store(combo.host.str,combo.host.length, system_charset_info);
+ table->field[0]->store(combo.host.str,combo.host.length,
+ system_charset_info);
table->field[1]->store(db,(uint) strlen(db), system_charset_info);
- table->field[2]->store(combo.user.str,combo.user.length, system_charset_info);
- table->field[3]->store(table_name,(uint) strlen(table_name), system_charset_info);
+ table->field[2]->store(combo.user.str,combo.user.length,
+ system_charset_info);
+ table->field[3]->store(table_name,(uint) strlen(table_name),
+ system_charset_info);
store_record(table,record[1]); // store at pos 1
key_copy(user_key, table->record[0], table->key_info,
table->key_info->key_length);
- table->file->extra(HA_EXTRA_RETRIEVE_ALL_COLS);
- if (table->file->index_read_idx(table->record[0], 0,
- user_key, table->key_info->key_length,
- HA_READ_KEY_EXACT))
+ if (table->file->index_read_idx_map(table->record[0], 0, user_key,
+ HA_WHOLE_KEY,
+ HA_READ_KEY_EXACT))
{
/*
The following should never happen as we first check the in memory
@@ -2600,16 +2755,18 @@ static int replace_table_table(THD *thd, GRANT_TABLE *grant_table,
{
if (store_table_rights || store_col_rights)
{
- if ((error=table->file->update_row(table->record[1],table->record[0])))
+ if ((error=table->file->ha_update_row(table->record[1],
+ table->record[0])) &&
+ error != HA_ERR_RECORD_IS_THE_SAME)
goto table_error; /* purecov: deadcode */
}
- else if ((error = table->file->delete_row(table->record[1])))
+ else if ((error = table->file->ha_delete_row(table->record[1])))
goto table_error; /* purecov: deadcode */
}
else
{
- error=table->file->write_row(table->record[0]);
- if (error && error != HA_ERR_FOUND_DUPP_KEY)
+ error=table->file->ha_write_row(table->record[0]);
+ if (table->file->is_fatal_error(error, HA_CHECK_DUP_KEY))
goto table_error; /* purecov: deadcode */
}
@@ -2620,7 +2777,7 @@ static int replace_table_table(THD *thd, GRANT_TABLE *grant_table,
}
else
{
- hash_delete(&column_priv_hash,(byte*) grant_table);
+ hash_delete(&column_priv_hash,(uchar*) grant_table);
}
DBUG_RETURN(0);
@@ -2631,6 +2788,10 @@ table_error:
}
+/**
+ @retval 0 success
+ @retval -1 error
+*/
static int replace_routine_table(THD *thd, GRANT_NAME *grant_name,
TABLE *table, const LEX_USER &combo,
const char *db, const char *routine_name,
@@ -2652,29 +2813,28 @@ static int replace_routine_table(THD *thd, GRANT_NAME *grant_name,
thd->security_ctx->host_or_ip, NullS);
/*
- The following should always succeed as new users are created before
- this function is called!
+ New users are created before this function is called.
+
+ There may be some cases where a routine's definer is removed but the
+ routine remains.
*/
- if (!find_acl_user(combo.host.str, combo.user.str, FALSE))
- {
- my_error(ER_PASSWORD_NO_MATCH,MYF(0));
- DBUG_RETURN(-1);
- }
+ table->use_all_columns();
restore_record(table, s->default_values); // Get empty record
table->field[0]->store(combo.host.str,combo.host.length, &my_charset_latin1);
table->field[1]->store(db,(uint) strlen(db), &my_charset_latin1);
table->field[2]->store(combo.user.str,combo.user.length, &my_charset_latin1);
table->field[3]->store(routine_name,(uint) strlen(routine_name),
&my_charset_latin1);
- table->field[4]->store((longlong)(is_proc ?
+ table->field[4]->store((longlong)(is_proc ?
TYPE_ENUM_PROCEDURE : TYPE_ENUM_FUNCTION),
TRUE);
store_record(table,record[1]); // store at pos 1
- if (table->file->index_read_idx(table->record[0],0,
- (byte*) table->field[0]->ptr,0,
- HA_READ_KEY_EXACT))
+ if (table->file->index_read_idx_map(table->record[0], 0,
+ (uchar*) table->field[0]->ptr,
+ HA_WHOLE_KEY,
+ HA_READ_KEY_EXACT))
{
/*
The following should never happen as we first check the in memory
@@ -2717,16 +2877,18 @@ static int replace_routine_table(THD *thd, GRANT_NAME *grant_name,
{
if (store_proc_rights)
{
- if ((error=table->file->update_row(table->record[1],table->record[0])))
+ if ((error=table->file->ha_update_row(table->record[1],
+ table->record[0])) &&
+ error != HA_ERR_RECORD_IS_THE_SAME)
goto table_error;
}
- else if ((error= table->file->delete_row(table->record[1])))
+ else if ((error= table->file->ha_delete_row(table->record[1])))
goto table_error;
}
else
{
- error=table->file->write_row(table->record[0]);
- if (error && error != HA_ERR_FOUND_DUPP_KEY)
+ error=table->file->ha_write_row(table->record[0]);
+ if (table->file->is_fatal_error(error, HA_CHECK_DUP_KEY))
goto table_error;
}
@@ -2736,7 +2898,7 @@ static int replace_routine_table(THD *thd, GRANT_NAME *grant_name,
}
else
{
- hash_delete(is_proc ? &proc_priv_hash : &func_priv_hash,(byte*) grant_name);
+ hash_delete(is_proc ? &proc_priv_hash : &func_priv_hash,(uchar*) grant_name);
}
DBUG_RETURN(0);
@@ -2826,9 +2988,10 @@ int mysql_table_grant(THD *thd, TABLE_LIST *table_list,
if (!(rights & CREATE_ACL))
{
char buf[FN_REFLEN];
- sprintf(buf,"%s/%s/%s.frm",mysql_data_home, table_list->db,
- table_list->table_name);
- fn_format(buf,buf,"","",4+16+32);
+ build_table_filename(buf, sizeof(buf), table_list->db,
+ table_list->table_name, reg_ext, 0);
+ fn_format(buf, buf, "", "", MY_UNPACK_FILENAME | MY_RESOLVE_SYMLINKS |
+ MY_RETURN_REAL_PATH | MY_APPEND_EXT);
if (access(buf,F_OK))
{
my_error(ER_NO_SUCH_TABLE, MYF(0), table_list->db, table_list->alias);
@@ -2864,19 +3027,26 @@ int mysql_table_grant(THD *thd, TABLE_LIST *table_list,
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";
+ /*
+ 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.
+ */
+ thd->clear_current_stmt_binlog_row_based();
+
#ifdef HAVE_REPLICATION
/*
GRANT and REVOKE are applied the slave in/exclusion rules as they are
some kind of updates to the mysql.% tables.
*/
- if (thd->slave_thread && table_rules_on)
+ if (thd->slave_thread && rpl_filter->is_on())
{
/*
The tables must be marked "updating" so that tables_ok() takes them into
account in tests.
*/
tables[0].updating= tables[1].updating= tables[2].updating= 1;
- if (!tables_ok(thd, tables))
+ if (!(thd->spcont || rpl_filter->tables_ok(0, tables)))
DBUG_RETURN(FALSE);
}
#endif
@@ -2922,12 +3092,8 @@ int mysql_table_grant(THD *thd, TABLE_LIST *table_list,
continue; // Add next user
}
- db_name= (table_list->view_db.length ?
- table_list->view_db.str :
- table_list->db);
- table_name= (table_list->view_name.length ?
- table_list->view_name.str :
- table_list->table_name);
+ db_name= table_list->get_db_name();
+ table_name= table_list->get_table_name();
/* Find/create cached table grant */
grant_table= table_hash_search(Str->host.str, NullS, db_name,
@@ -2950,7 +3116,7 @@ int mysql_table_grant(THD *thd, TABLE_LIST *table_list,
result= TRUE; /* purecov: deadcode */
continue; /* purecov: deadcode */
}
- my_hash_insert(&column_priv_hash,(byte*) grant_table);
+ my_hash_insert(&column_priv_hash,(uchar*) grant_table);
}
/* If revoke_grant, calculate the new column privilege for tables_priv */
@@ -3005,24 +3171,18 @@ int mysql_table_grant(THD *thd, TABLE_LIST *table_list,
}
}
}
-
thd->mem_root= old_root;
pthread_mutex_unlock(&acl_cache->lock);
if (!result) /* success */
{
- if (mysql_bin_log.is_open())
- {
- thd->clear_error();
- Query_log_event qinfo(thd, thd->query, thd->query_length, 0, FALSE);
- mysql_bin_log.write(&qinfo);
- }
+ write_bin_log(thd, TRUE, thd->query, thd->query_length);
}
rw_unlock(&LOCK_grant);
if (!result) /* success */
- send_ok(thd);
+ my_ok(thd);
/* Tables are automatically closed */
thd->lex->restore_backup_query_tables_list(&backup);
@@ -3088,19 +3248,26 @@ bool mysql_routine_grant(THD *thd, TABLE_LIST *table_list, bool is_proc,
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.
+ */
+ thd->clear_current_stmt_binlog_row_based();
+
#ifdef HAVE_REPLICATION
/*
GRANT and REVOKE are applied the slave in/exclusion rules as they are
some kind of updates to the mysql.% tables.
*/
- if (thd->slave_thread && table_rules_on)
+ if (thd->slave_thread && rpl_filter->is_on())
{
/*
The tables must be marked "updating" so that tables_ok() takes them into
account in tests.
*/
tables[0].updating= tables[1].updating= 1;
- if (!tables_ok(thd, tables))
+ if (!(thd->spcont || rpl_filter->tables_ok(0, tables)))
DBUG_RETURN(FALSE);
}
#endif
@@ -3163,33 +3330,28 @@ bool mysql_routine_grant(THD *thd, TABLE_LIST *table_list, bool is_proc,
result= TRUE;
continue;
}
- my_hash_insert(is_proc ? &proc_priv_hash : &func_priv_hash,(byte*) grant_name);
+ my_hash_insert(is_proc ? &proc_priv_hash : &func_priv_hash,(uchar*) grant_name);
}
if (replace_routine_table(thd, grant_name, tables[1].table, *Str,
- db_name, table_name, is_proc, rights, revoke_grant))
+ db_name, table_name, is_proc, rights,
+ revoke_grant) != 0)
{
result= TRUE;
continue;
}
}
-
thd->mem_root= old_root;
pthread_mutex_unlock(&acl_cache->lock);
if (!result && !no_error)
{
- if (mysql_bin_log.is_open())
- {
- thd->clear_error();
- Query_log_event qinfo(thd, thd->query, thd->query_length, 0, FALSE);
- mysql_bin_log.write(&qinfo);
- }
+ write_bin_log(thd, TRUE, thd->query, thd->query_length);
}
rw_unlock(&LOCK_grant);
if (!result && !no_error)
- send_ok(thd);
+ my_ok(thd);
/* Tables are automatically closed */
DBUG_RETURN(result);
@@ -3227,19 +3389,26 @@ bool mysql_grant(THD *thd, const char *db, List <LEX_USER> &list,
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.
+ */
+ thd->clear_current_stmt_binlog_row_based();
+
#ifdef HAVE_REPLICATION
/*
GRANT and REVOKE are applied the slave in/exclusion rules as they are
some kind of updates to the mysql.% tables.
*/
- if (thd->slave_thread && table_rules_on)
+ if (thd->slave_thread && rpl_filter->is_on())
{
/*
The tables must be marked "updating" so that tables_ok() takes them into
account in tests.
*/
tables[0].updating= tables[1].updating= 1;
- if (!tables_ok(thd, tables))
+ if (!(thd->spcont || rpl_filter->tables_ok(0, tables)))
DBUG_RETURN(FALSE);
}
#endif
@@ -3291,19 +3460,14 @@ bool mysql_grant(THD *thd, const char *db, List <LEX_USER> &list,
if (!result)
{
- if (mysql_bin_log.is_open())
- {
- thd->clear_error();
- Query_log_event qinfo(thd, thd->query, thd->query_length, 0, FALSE);
- mysql_bin_log.write(&qinfo);
- }
+ write_bin_log(thd, TRUE, thd->query, thd->query_length);
}
rw_unlock(&LOCK_grant);
close_thread_tables(thd);
if (!result)
- send_ok(thd);
+ my_ok(thd);
DBUG_RETURN(result);
}
@@ -3314,7 +3478,6 @@ bool mysql_grant(THD *thd, const char *db, List <LEX_USER> &list,
void grant_free(void)
{
DBUG_ENTER("grant_free");
- grant_option = FALSE;
hash_free(&column_priv_hash);
hash_free(&proc_priv_hash);
hash_free(&func_priv_hash);
@@ -3323,16 +3486,13 @@ void grant_free(void)
}
-/*
- Initialize structures responsible for table/column-level privilege checking
- and load information for them from tables in the 'mysql' database.
-
- SYNOPSIS
- grant_init()
+/**
+ @brief Initialize structures responsible for table/column-level privilege
+ checking and load information for them from tables in the 'mysql' database.
- RETURN VALUES
- 0 ok
- 1 Could not initialize grant's
+ @return Error status
+ @retval 0 OK
+ @retval 1 Could not initialize grant subsystem.
*/
my_bool grant_init()
@@ -3345,107 +3505,159 @@ 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 */
my_pthread_setspecific_ptr(THR_THD, 0);
- /* Set the grant option flag so we will check grants */
- grant_option= TRUE;
DBUG_RETURN(return_val);
}
-/*
- Initialize structures responsible for table/column-level privilege
- checking and load information about grants from open privilege tables.
+/**
+ @brief Helper function to grant_reload_procs_priv
- SYNOPSIS
- grant_load()
- thd Current thread
- tables List containing open "mysql.tables_priv" and
- "mysql.columns_priv" tables.
+ Reads the procs_priv table into memory hash.
- RETURN VALUES
- FALSE - success
- TRUE - error
+ @param table A pointer to the procs_priv table structure.
+
+ @see grant_reload
+ @see grant_reload_procs_priv
+
+ @return Error state
+ @retval TRUE An error occurred
+ @retval FALSE Success
*/
-static my_bool grant_load(TABLE_LIST *tables)
+static my_bool grant_load_procs_priv(TABLE *p_table)
{
MEM_ROOT *memex_ptr;
my_bool return_val= 1;
- TABLE *t_table, *c_table, *p_table;
bool check_no_resolve= specialflag & SPECIAL_NO_RESOLVE;
MEM_ROOT **save_mem_root_ptr= my_pthread_getspecific_ptr(MEM_ROOT**,
THR_MALLOC);
- DBUG_ENTER("grant_load");
-
- (void) hash_init(&column_priv_hash,system_charset_info,
- 0,0,0, (hash_get_key) get_grant_table,
- (hash_free_key) free_grant_table,0);
+ DBUG_ENTER("grant_load_procs_priv");
(void) hash_init(&proc_priv_hash,system_charset_info,
- 0,0,0, (hash_get_key) get_grant_table,
- 0,0);
+ 0,0,0, (hash_get_key) get_grant_table,
+ 0,0);
(void) hash_init(&func_priv_hash,system_charset_info,
- 0,0,0, (hash_get_key) get_grant_table,
- 0,0);
- init_sql_alloc(&memex, ACL_ALLOC_BLOCK_SIZE, 0);
+ 0,0,0, (hash_get_key) get_grant_table,
+ 0,0);
+ p_table->file->ha_index_init(0, 1);
+ p_table->use_all_columns();
- t_table = tables[0].table; c_table = tables[1].table;
- p_table= tables[2].table;
- t_table->file->ha_index_init(0);
- p_table->file->ha_index_init(0);
- if (!t_table->file->index_first(t_table->record[0]))
+ if (!p_table->file->index_first(p_table->record[0]))
{
memex_ptr= &memex;
my_pthread_setspecific_ptr(THR_MALLOC, &memex_ptr);
do
{
- GRANT_TABLE *mem_check;
- if (!(mem_check=new GRANT_TABLE(t_table,c_table)))
+ GRANT_NAME *mem_check;
+ HASH *hash;
+ if (!(mem_check=new (memex_ptr) GRANT_NAME(p_table)))
{
- /* This could only happen if we are out memory */
- grant_option= FALSE;
- goto end_unlock;
+ /* This could only happen if we are out memory */
+ goto end_unlock;
}
if (check_no_resolve)
{
if (hostname_requires_resolving(mem_check->host.hostname))
{
- sql_print_warning("'tables_priv' entry '%s %s@%s' "
+ sql_print_warning("'procs_priv' entry '%s %s@%s' "
"ignored in --skip-name-resolve mode.",
- mem_check->tname,
- mem_check->user ? mem_check->user : "",
+ mem_check->tname, mem_check->user,
mem_check->host.hostname ?
mem_check->host.hostname : "");
- continue;
- }
+ continue;
+ }
+ }
+ if (p_table->field[4]->val_int() == TYPE_ENUM_PROCEDURE)
+ {
+ hash= &proc_priv_hash;
+ }
+ else
+ if (p_table->field[4]->val_int() == TYPE_ENUM_FUNCTION)
+ {
+ hash= &func_priv_hash;
+ }
+ else
+ {
+ sql_print_warning("'procs_priv' entry '%s' "
+ "ignored, bad routine type",
+ mem_check->tname);
+ continue;
}
+ mem_check->privs= fix_rights_for_procedure(mem_check->privs);
if (! mem_check->ok())
- delete mem_check;
- else if (my_hash_insert(&column_priv_hash,(byte*) mem_check))
+ delete mem_check;
+ else if (my_hash_insert(hash, (uchar*) mem_check))
{
- delete mem_check;
- grant_option= FALSE;
- goto end_unlock;
+ delete mem_check;
+ goto end_unlock;
}
}
- while (!t_table->file->index_next(t_table->record[0]));
+ while (!p_table->file->index_next(p_table->record[0]));
}
- if (!p_table->file->index_first(p_table->record[0]))
+ /* Return ok */
+ return_val= 0;
+
+end_unlock:
+ p_table->file->ha_index_end();
+ my_pthread_setspecific_ptr(THR_MALLOC, save_mem_root_ptr);
+ DBUG_RETURN(return_val);
+}
+
+
+/**
+ @brief Initialize structures responsible for table/column-level privilege
+ checking and load information about grants from open privilege tables.
+
+ @param thd Current thread
+ @param tables List containing open "mysql.tables_priv" and
+ "mysql.columns_priv" tables.
+
+ @see grant_reload
+
+ @return Error state
+ @retval FALSE Success
+ @retval TRUE Error
+*/
+
+static my_bool grant_load(THD *thd, TABLE_LIST *tables)
+{
+ MEM_ROOT *memex_ptr;
+ my_bool return_val= 1;
+ TABLE *t_table= 0, *c_table= 0;
+ bool check_no_resolve= specialflag & SPECIAL_NO_RESOLVE;
+ MEM_ROOT **save_mem_root_ptr= my_pthread_getspecific_ptr(MEM_ROOT**,
+ THR_MALLOC);
+ ulong old_sql_mode= thd->variables.sql_mode;
+ DBUG_ENTER("grant_load");
+
+ thd->variables.sql_mode&= ~MODE_PAD_CHAR_TO_FULL_LENGTH;
+
+ (void) hash_init(&column_priv_hash,system_charset_info,
+ 0,0,0, (hash_get_key) get_grant_table,
+ (hash_free_key) free_grant_table,0);
+
+ t_table = tables[0].table;
+ c_table = tables[1].table;
+ t_table->file->ha_index_init(0, 1);
+ t_table->use_all_columns();
+ c_table->use_all_columns();
+
+ if (!t_table->file->index_first(t_table->record[0]))
{
memex_ptr= &memex;
my_pthread_setspecific_ptr(THR_MALLOC, &memex_ptr);
do
{
- GRANT_NAME *mem_check;
- HASH *hash;
- if (!(mem_check=new GRANT_NAME(p_table)))
+ GRANT_TABLE *mem_check;
+ if (!(mem_check=new (memex_ptr) GRANT_TABLE(t_table,c_table)))
{
/* This could only happen if we are out memory */
- grant_option= FALSE;
goto end_unlock;
}
@@ -3453,75 +3665,113 @@ static my_bool grant_load(TABLE_LIST *tables)
{
if (hostname_requires_resolving(mem_check->host.hostname))
{
- sql_print_warning("'procs_priv' entry '%s %s@%s' "
+ sql_print_warning("'tables_priv' entry '%s %s@%s' "
"ignored in --skip-name-resolve mode.",
- mem_check->tname, mem_check->user,
+ mem_check->tname,
+ mem_check->user ? mem_check->user : "",
mem_check->host.hostname ?
mem_check->host.hostname : "");
continue;
}
}
- if (p_table->field[4]->val_int() == TYPE_ENUM_PROCEDURE)
- {
- hash= &proc_priv_hash;
- }
- else
- if (p_table->field[4]->val_int() == TYPE_ENUM_FUNCTION)
- {
- hash= &func_priv_hash;
- }
- else
- {
- sql_print_warning("'procs_priv' entry '%s' "
- "ignored, bad routine type",
- mem_check->tname);
- continue;
- }
- mem_check->privs= fix_rights_for_procedure(mem_check->privs);
if (! mem_check->ok())
delete mem_check;
- else if (my_hash_insert(hash, (byte*) mem_check))
+ else if (my_hash_insert(&column_priv_hash,(uchar*) mem_check))
{
delete mem_check;
- grant_option= FALSE;
goto end_unlock;
}
}
- while (!p_table->file->index_next(p_table->record[0]));
+ while (!t_table->file->index_next(t_table->record[0]));
}
+
return_val=0; // Return ok
end_unlock:
+ thd->variables.sql_mode= old_sql_mode;
t_table->file->ha_index_end();
- p_table->file->ha_index_end();
my_pthread_setspecific_ptr(THR_MALLOC, save_mem_root_ptr);
DBUG_RETURN(return_val);
}
-/*
- Reload information about table and column level privileges if possible.
+/**
+ @brief Helper function to grant_reload. Reloads procs_priv table is it
+ exists.
- SYNOPSIS
- grant_reload()
- thd Current thread
+ @param thd A pointer to the thread handler object.
- NOTES
- Locked tables are checked by acl_reload() and doesn't have to be checked
- in this call.
- This function is also used for initialization of structures responsible
- for table/column-level privilege checking.
+ @see grant_reload
- RETURN VALUE
- FALSE Success
- TRUE Error
+ @return Error state
+ @retval FALSE Success
+ @retval TRUE An error has occurred.
+*/
+
+static my_bool grant_reload_procs_priv(THD *thd)
+{
+ HASH old_proc_priv_hash, old_func_priv_hash;
+ TABLE_LIST table;
+ 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;
+
+ if (simple_open_n_lock_tables(thd, &table))
+ {
+ close_thread_tables(thd);
+ DBUG_RETURN(TRUE);
+ }
+
+ /* 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;
+
+ rw_wrlock(&LOCK_grant);
+ if ((return_val= grant_load_procs_priv(table.table)))
+ {
+ /* Error; Reverting to old hash */
+ DBUG_PRINT("error",("Reverting to old privileges"));
+ grant_free();
+ proc_priv_hash= old_proc_priv_hash;
+ func_priv_hash= old_func_priv_hash;
+ }
+ else
+ {
+ hash_free(&old_proc_priv_hash);
+ hash_free(&old_func_priv_hash);
+ }
+ rw_unlock(&LOCK_grant);
+
+ close_thread_tables(thd);
+ DBUG_RETURN(return_val);
+}
+
+
+/**
+ @brief Reload information about table and column level privileges if possible
+
+ @param thd Current thread
+
+ Locked tables are checked by acl_reload() and doesn't have to be checked
+ in this call.
+ This function is also used for initialization of structures responsible
+ for table/column-level privilege checking.
+
+ @return Error state
+ @retval FALSE Success
+ @retval TRUE Error
*/
my_bool grant_reload(THD *thd)
{
- TABLE_LIST tables[3];
- HASH old_column_priv_hash, old_proc_priv_hash, old_func_priv_hash;
+ TABLE_LIST tables[2];
+ HASH old_column_priv_hash;
MEM_ROOT old_mem;
my_bool return_val= 1;
DBUG_ENTER("grant_reload");
@@ -3533,12 +3783,10 @@ my_bool grant_reload(THD *thd)
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[2].alias= tables[2].table_name= (char*) "procs_priv";
- tables[0].db= tables[1].db= tables[2].db= (char *) "mysql";
+ tables[0].db= tables[1].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].lock_type= tables[1].lock_type= TL_READ;
+ tables[0].skip_temporary= tables[1].skip_temporary= TRUE;
/*
To avoid deadlocks we should obtain table locks before
obtaining LOCK_grant rwlock.
@@ -3547,35 +3795,45 @@ my_bool grant_reload(THD *thd)
goto end;
rw_wrlock(&LOCK_grant);
- grant_version++;
old_column_priv_hash= column_priv_hash;
- old_proc_priv_hash= proc_priv_hash;
- old_func_priv_hash= func_priv_hash;
+
+ /*
+ Create a new memory pool but save the current memory pool to make an undo
+ opertion possible in case of failure.
+ */
old_mem= memex;
+ init_sql_alloc(&memex, ACL_ALLOC_BLOCK_SIZE, 0);
- if ((return_val= grant_load(tables)))
+ if ((return_val= grant_load(thd, tables)))
{ // Error. Revert to old hash
DBUG_PRINT("error",("Reverting to old privileges"));
grant_free(); /* purecov: deadcode */
column_priv_hash= old_column_priv_hash; /* purecov: deadcode */
- proc_priv_hash= old_proc_priv_hash;
- func_priv_hash= old_func_priv_hash;
memex= old_mem; /* purecov: deadcode */
}
else
{
hash_free(&old_column_priv_hash);
- hash_free(&old_proc_priv_hash);
- hash_free(&old_func_priv_hash);
free_root(&old_mem,MYF(0));
}
rw_unlock(&LOCK_grant);
-end:
close_thread_tables(thd);
+
+ /*
+ It is OK failing to load procs_priv table because we may be
+ working with 4.1 privilege tables.
+ */
+ if (grant_reload_procs_priv(thd))
+ return_val= 1;
+
+ rw_wrlock(&LOCK_grant);
+ grant_version++;
+ rw_unlock(&LOCK_grant);
+
+end:
DBUG_RETURN(return_val);
}
-
/****************************************************************************
Check table level grants
@@ -3624,7 +3882,7 @@ bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables,
of other queries). For simple queries first_not_own_table is 0.
*/
for (i= 0, table= tables;
- table != first_not_own_table && i < number;
+ i < number && table != first_not_own_table;
table= table->next_global, i++)
{
/* Remove SHOW_VIEW_ACL, because it will be checked during making view */
@@ -3645,8 +3903,8 @@ bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables,
if (!want_access)
continue; // ok
- if (!(~table->grant.privilege & want_access) ||
- table->derived || table->schema_table)
+ if (!(~table->grant.privilege & want_access) ||
+ table->is_anonymous_derived_table() || table->schema_table)
{
/*
It is subquery in the FROM clause. VIEW set table->derived after
@@ -3664,8 +3922,8 @@ bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables,
continue;
}
if (!(grant_table= table_hash_search(sctx->host, sctx->ip,
- table->db, sctx->priv_user,
- table->table_name,0)))
+ table->get_db_name(), sctx->priv_user,
+ table->get_table_name(), FALSE)))
{
want_access &= ~table->grant.privilege;
goto err; // No grants
@@ -3701,7 +3959,7 @@ err:
command,
sctx->priv_user,
sctx->host_or_ip,
- table ? table->table_name : "unknown");
+ table ? table->get_table_name() : "unknown");
}
DBUG_RETURN(1);
}
@@ -3832,8 +4090,8 @@ bool check_column_grant_in_table_ref(THD *thd, TABLE_LIST * table_ref,
/* Normal or temporary table. */
TABLE *table= table_ref->table;
grant= &(table->grant);
- db_name= table->s->db;
- table_name= table->s->table_name;
+ db_name= table->s->db.str;
+ table_name= table->s->table_name.str;
}
if (grant->want_privilege)
@@ -3856,7 +4114,7 @@ bool check_column_grant_in_table_ref(THD *thd, TABLE_LIST * table_ref,
@retval 1 Falure
@details This function walks over the columns of a table reference
The columns may originate from different tables, depending on the kind of
- table reference, e.g. join.
+ table reference, e.g. join, view.
For each table it will retrieve the grant information and will use it
to check the required access privileges for the fields requested from it.
*/
@@ -3867,69 +4125,80 @@ bool check_grant_all_columns(THD *thd, ulong want_access_arg,
ulong want_access= want_access_arg;
const char *table_name= NULL;
- if (grant_option)
- {
- const char* db_name;
- GRANT_INFO *grant;
- /* Initialized only to make gcc happy */
- GRANT_TABLE *grant_table= NULL;
+ const char* db_name;
+ GRANT_INFO *grant;
+ /* Initialized only to make gcc happy */
+ GRANT_TABLE *grant_table= NULL;
+ /*
+ Flag that gets set if privilege checking has to be performed on column
+ level.
+ */
+ bool using_column_privileges= FALSE;
- rw_rdlock(&LOCK_grant);
+ rw_rdlock(&LOCK_grant);
- for (; !fields->end_of_fields(); fields->next())
- {
- const char *field_name= fields->name();
+ for (; !fields->end_of_fields(); fields->next())
+ {
+ const char *field_name= fields->name();
- if (table_name != fields->table_name())
+ if (table_name != fields->get_table_name())
+ {
+ table_name= fields->get_table_name();
+ db_name= fields->get_db_name();
+ grant= fields->grant();
+ /* get a fresh one for each table */
+ want_access= want_access_arg & ~grant->privilege;
+ if (want_access)
{
- table_name= fields->table_name();
- db_name= fields->db_name();
- grant= fields->grant();
- /* get a fresh one for each table */
- want_access= want_access_arg & ~grant->privilege;
- if (want_access)
+ /* reload table if someone has modified any grants */
+ if (grant->version != grant_version)
{
- /* reload table if someone has modified any grants */
- if (grant->version != grant_version)
- {
- grant->grant_table=
- table_hash_search(sctx->host, sctx->ip, db_name,
- sctx->priv_user,
- table_name, 0); /* purecov: inspected */
- grant->version= grant_version; /* purecov: inspected */
- }
-
- grant_table= grant->grant_table;
- DBUG_ASSERT (grant_table);
+ grant->grant_table=
+ table_hash_search(sctx->host, sctx->ip, db_name,
+ sctx->priv_user,
+ table_name, 0); /* purecov: inspected */
+ grant->version= grant_version; /* purecov: inspected */
}
- }
- if (want_access)
- {
- GRANT_COLUMN *grant_column=
- column_hash_search(grant_table, field_name,
- (uint) strlen(field_name));
- if (!grant_column || (~grant_column->rights & want_access))
- goto err;
+ grant_table= grant->grant_table;
+ DBUG_ASSERT (grant_table);
}
}
- rw_unlock(&LOCK_grant);
- return 0;
-err:
- rw_unlock(&LOCK_grant);
+ if (want_access)
+ {
+ GRANT_COLUMN *grant_column=
+ column_hash_search(grant_table, field_name,
+ (uint) strlen(field_name));
+ if (grant_column)
+ using_column_privileges= TRUE;
+ if (!grant_column || (~grant_column->rights & want_access))
+ goto err;
+ }
}
- else
- table_name= fields->table_name();
+ rw_unlock(&LOCK_grant);
+ return 0;
+
+err:
+ rw_unlock(&LOCK_grant);
char command[128];
get_privilege_desc(command, sizeof(command), want_access);
- my_error(ER_COLUMNACCESS_DENIED_ERROR, MYF(0),
- command,
- sctx->priv_user,
- sctx->host_or_ip,
- fields->name(),
- table_name);
+ /*
+ Do not give an error message listing a column name unless the user has
+ privilege to see all columns.
+ */
+ if (using_column_privileges)
+ my_error(ER_TABLEACCESS_DENIED_ERROR, MYF(0),
+ command, sctx->priv_user,
+ sctx->host_or_ip, table_name);
+ else
+ my_error(ER_COLUMNACCESS_DENIED_ERROR, MYF(0),
+ command,
+ sctx->priv_user,
+ sctx->host_or_ip,
+ fields->name(),
+ table_name);
return 1;
}
@@ -4080,18 +4349,15 @@ bool check_routine_level_acl(THD *thd, const char *db, const char *name,
bool is_proc)
{
bool no_routine_acl= 1;
- if (grant_option)
- {
- GRANT_NAME *grant_proc;
- Security_context *sctx= thd->security_ctx;
- rw_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);
- }
+ GRANT_NAME *grant_proc;
+ Security_context *sctx= thd->security_ctx;
+ rw_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);
return no_routine_acl;
}
@@ -4186,10 +4452,10 @@ static void add_user_option(String *grant, ulong value, const char *name)
{
char buff[22], *p; // just as in int2str
grant->append(' ');
- grant->append(name, (uint) strlen(name));
+ grant->append(name, strlen(name));
grant->append(' ');
p=int10_to_str(value, buff, 10);
- grant->append(buff,(uint) (p - buff));
+ grant->append(buff,p-buff);
}
}
@@ -4200,13 +4466,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"
+ "CREATE USER", "EVENT", "TRIGGER"
};
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
+ 14, 13, 11, 5, 7
};
@@ -4327,7 +4593,7 @@ bool mysql_show_grants(THD *thd,LEX_USER *lex_user)
{
ssl_options++;
global.append(STRING_WITH_LEN("ISSUER \'"));
- global.append(acl_user->x509_issuer,(uint) strlen(acl_user->x509_issuer));
+ global.append(acl_user->x509_issuer,strlen(acl_user->x509_issuer));
global.append('\'');
}
if (acl_user->x509_subject)
@@ -4335,7 +4601,7 @@ bool mysql_show_grants(THD *thd,LEX_USER *lex_user)
if (ssl_options++)
global.append(' ');
global.append(STRING_WITH_LEN("SUBJECT \'"));
- global.append(acl_user->x509_subject,(uint) strlen(acl_user->x509_subject),
+ global.append(acl_user->x509_subject,strlen(acl_user->x509_subject),
system_charset_info);
global.append('\'');
}
@@ -4344,7 +4610,7 @@ bool mysql_show_grants(THD *thd,LEX_USER *lex_user)
if (ssl_options++)
global.append(' ');
global.append(STRING_WITH_LEN("CIPHER '"));
- global.append(acl_user->ssl_cipher,(uint) strlen(acl_user->ssl_cipher),
+ global.append(acl_user->ssl_cipher,strlen(acl_user->ssl_cipher),
system_charset_info);
global.append('\'');
}
@@ -4424,13 +4690,13 @@ bool mysql_show_grants(THD *thd,LEX_USER *lex_user)
}
}
db.append (STRING_WITH_LEN(" ON "));
- append_identifier(thd, &db, acl_db->db, (uint) strlen(acl_db->db));
+ append_identifier(thd, &db, acl_db->db, strlen(acl_db->db));
db.append (STRING_WITH_LEN(".* TO '"));
db.append(lex_user->user.str, lex_user->user.length,
system_charset_info);
db.append (STRING_WITH_LEN("'@'"));
// host and lex_user->host are equal except for case
- db.append(host, (uint) strlen(host), system_charset_info);
+ db.append(host, strlen(host), system_charset_info);
db.append ('\'');
if (want_access & GRANT_ACL)
db.append(STRING_WITH_LEN(" WITH GRANT OPTION"));
@@ -4536,16 +4802,16 @@ bool mysql_show_grants(THD *thd,LEX_USER *lex_user)
}
global.append(STRING_WITH_LEN(" ON "));
append_identifier(thd, &global, grant_table->db,
- (uint) strlen(grant_table->db));
+ strlen(grant_table->db));
global.append('.');
append_identifier(thd, &global, grant_table->tname,
- (uint) strlen(grant_table->tname));
+ strlen(grant_table->tname));
global.append(STRING_WITH_LEN(" TO '"));
global.append(lex_user->user.str, lex_user->user.length,
system_charset_info);
global.append(STRING_WITH_LEN("'@'"));
// host and lex_user->host are equal except for case
- global.append(host, (uint) strlen(host), system_charset_info);
+ global.append(host, strlen(host), system_charset_info);
global.append('\'');
if (table_access & GRANT_ACL)
global.append(STRING_WITH_LEN(" WITH GRANT OPTION"));
@@ -4578,7 +4844,7 @@ end:
VOID(pthread_mutex_unlock(&acl_cache->lock));
rw_unlock(&LOCK_grant);
- send_eof(thd);
+ my_eof(thd);
DBUG_RETURN(error);
}
@@ -4642,16 +4908,16 @@ static int show_routine_grants(THD* thd, LEX_USER *lex_user, HASH *hash,
global.append(type,typelen);
global.append(' ');
append_identifier(thd, &global, grant_proc->db,
- (uint) strlen(grant_proc->db));
+ strlen(grant_proc->db));
global.append('.');
append_identifier(thd, &global, grant_proc->tname,
- (uint) strlen(grant_proc->tname));
+ strlen(grant_proc->tname));
global.append(STRING_WITH_LEN(" TO '"));
global.append(lex_user->user.str, lex_user->user.length,
system_charset_info);
global.append(STRING_WITH_LEN("'@'"));
// host and lex_user->host are equal except for case
- global.append(host, (uint) strlen(host), system_charset_info);
+ global.append(host, strlen(host), system_charset_info);
global.append('\'');
if (proc_access & GRANT_ACL)
global.append(STRING_WITH_LEN(" WITH GRANT OPTION"));
@@ -4763,7 +5029,7 @@ int open_grant_tables(THD *thd, TABLE_LIST *tables)
GRANT and REVOKE are applied the slave in/exclusion rules as they are
some kind of updates to the mysql.% tables.
*/
- if (thd->slave_thread && table_rules_on)
+ if (thd->slave_thread && rpl_filter->is_on())
{
/*
The tables must be marked "updating" so that tables_ok() takes them into
@@ -4771,7 +5037,7 @@ int open_grant_tables(THD *thd, TABLE_LIST *tables)
*/
tables[0].updating=tables[1].updating=tables[2].updating=
tables[3].updating=tables[4].updating=1;
- if (!tables_ok(thd, tables))
+ 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;;
@@ -4848,13 +5114,17 @@ static int modify_grant_table(TABLE *table, Field *host_field,
system_charset_info);
user_field->store(user_to->user.str, user_to->user.length,
system_charset_info);
- if ((error= table->file->update_row(table->record[1], table->record[0])))
+ if ((error= table->file->ha_update_row(table->record[1],
+ table->record[0])) &&
+ error != HA_ERR_RECORD_IS_THE_SAME)
table->file->print_error(error, MYF(0));
+ else
+ error= 0;
}
else
{
/* delete */
- if ((error=table->file->delete_row(table->record[0])))
+ if ((error=table->file->ha_delete_row(table->record[0])))
table->file->print_error(error, MYF(0));
}
@@ -4905,11 +5175,12 @@ static int handle_grant_table(TABLE_LIST *tables, uint table_no, bool drop,
char *user_str= user_from->user.str;
const char *host;
const char *user;
- byte user_key[MAX_KEY_LENGTH];
+ uchar user_key[MAX_KEY_LENGTH];
uint key_prefix_length;
DBUG_ENTER("handle_grant_table");
THD *thd= current_thd;
+ table->use_all_columns();
if (! table_no) // mysql.user table
{
/*
@@ -4922,7 +5193,7 @@ static int handle_grant_table(TABLE_LIST *tables, uint table_no, bool drop,
by the searched record, if it exists.
*/
DBUG_PRINT("info",("read table: '%s' search: '%s'@'%s'",
- table->s->table_name, user_str, host_str));
+ table->s->table_name.str, user_str, host_str));
host_field->store(host_str, user_from->host.length, system_charset_info);
user_field->store(user_str, user_from->user.length, system_charset_info);
@@ -4930,11 +5201,11 @@ static int handle_grant_table(TABLE_LIST *tables, uint table_no, bool drop,
table->key_info->key_part[1].store_length);
key_copy(user_key, table->record[0], table->key_info, key_prefix_length);
- if ((error= table->file->index_read_idx(table->record[0], 0,
- user_key, key_prefix_length,
- HA_READ_KEY_EXACT)))
+ if ((error= table->file->index_read_idx_map(table->record[0], 0,
+ user_key, (key_part_map)3,
+ HA_READ_KEY_EXACT)))
{
- if (error != HA_ERR_KEY_NOT_FOUND)
+ if (error != HA_ERR_KEY_NOT_FOUND && error != HA_ERR_END_OF_FILE)
{
table->file->print_error(error, MYF(0));
result= -1;
@@ -4965,7 +5236,7 @@ static int handle_grant_table(TABLE_LIST *tables, uint table_no, bool drop,
{
#ifdef EXTRA_DEBUG
DBUG_PRINT("info",("scan table: '%s' search: '%s'@'%s'",
- table->s->table_name, user_str, host_str));
+ table->s->table_name.str, user_str, host_str));
#endif
while ((error= table->file->rnd_next(table->record[0])) !=
HA_ERR_END_OF_FILE)
@@ -5057,6 +5328,8 @@ static int handle_grant_struct(uint struct_no, bool drop,
LINT_INIT(acl_user);
LINT_INIT(acl_db);
LINT_INIT(grant_name);
+ LINT_INIT(user);
+ LINT_INIT(host);
safe_mutex_assert_owner(&acl_cache->lock);
@@ -5131,8 +5404,7 @@ static int handle_grant_struct(uint struct_no, bool drop,
result= 1; /* At least one element found. */
if ( drop )
{
- switch ( struct_no )
- {
+ switch ( struct_no ) {
case 0:
delete_dynamic_element(&acl_users, idx);
break;
@@ -5142,11 +5414,11 @@ static int handle_grant_struct(uint struct_no, bool drop,
break;
case 2:
- hash_delete(&column_priv_hash, (byte*) grant_name);
+ hash_delete(&column_priv_hash, (uchar*) grant_name);
break;
case 3:
- hash_delete(&proc_priv_hash, (byte*) grant_name);
+ hash_delete(&proc_priv_hash, (uchar*) grant_name);
break;
}
elements--;
@@ -5346,6 +5618,13 @@ bool mysql_create_user(THD *thd, List <LEX_USER> &list)
bool some_users_created= FALSE;
DBUG_ENTER("mysql_create_user");
+ /*
+ This statement will be replicated as a statement, even when using
+ row-based replication. The flag will be reset at the end of the
+ statement.
+ */
+ thd->clear_current_stmt_binlog_row_based();
+
/* CREATE USER may be skipped on replication client. */
if ((result= open_grant_tables(thd, tables)))
DBUG_RETURN(result != 1);
@@ -5386,11 +5665,8 @@ bool mysql_create_user(THD *thd, List <LEX_USER> &list)
if (result)
my_error(ER_CANNOT_USER, MYF(0), "CREATE USER", wrong_users.c_ptr_safe());
- if (some_users_created && mysql_bin_log.is_open())
- {
- Query_log_event qinfo(thd, thd->query, thd->query_length, 0, FALSE);
- mysql_bin_log.write(&qinfo);
- }
+ if (some_users_created)
+ write_bin_log(thd, FALSE, thd->query, thd->query_length);
rw_unlock(&LOCK_grant);
close_thread_tables(thd);
@@ -5421,6 +5697,13 @@ bool mysql_drop_user(THD *thd, List <LEX_USER> &list)
bool some_users_deleted= FALSE;
DBUG_ENTER("mysql_drop_user");
+ /*
+ This statement will be replicated as a statement, even when using
+ row-based replication. The flag will be reset at the end of the
+ statement.
+ */
+ thd->clear_current_stmt_binlog_row_based();
+
/* DROP USER may be skipped on replication client. */
if ((result= open_grant_tables(thd, tables)))
DBUG_RETURN(result != 1);
@@ -5452,14 +5735,8 @@ bool mysql_drop_user(THD *thd, List <LEX_USER> &list)
if (result)
my_error(ER_CANNOT_USER, MYF(0), "DROP USER", wrong_users.c_ptr_safe());
- DBUG_PRINT("info", ("thd->net.last_errno: %d", thd->net.last_errno));
- DBUG_PRINT("info", ("thd->net.last_error: %s", thd->net.last_error));
-
- if (some_users_deleted && mysql_bin_log.is_open())
- {
- Query_log_event qinfo(thd, thd->query, thd->query_length, 0, FALSE);
- mysql_bin_log.write(&qinfo);
- }
+ if (some_users_deleted)
+ write_bin_log(thd, FALSE, thd->query, thd->query_length);
rw_unlock(&LOCK_grant);
close_thread_tables(thd);
@@ -5491,6 +5768,13 @@ bool mysql_rename_user(THD *thd, List <LEX_USER> &list)
bool some_users_renamed= FALSE;
DBUG_ENTER("mysql_rename_user");
+ /*
+ This statement will be replicated as a statement, even when using
+ row-based replication. The flag will be reset at the end of the
+ statement.
+ */
+ thd->clear_current_stmt_binlog_row_based();
+
/* RENAME USER may be skipped on replication client. */
if ((result= open_grant_tables(thd, tables)))
DBUG_RETURN(result != 1);
@@ -5536,10 +5820,7 @@ bool mysql_rename_user(THD *thd, List <LEX_USER> &list)
my_error(ER_CANNOT_USER, MYF(0), "RENAME USER", wrong_users.c_ptr_safe());
if (some_users_renamed && mysql_bin_log.is_open())
- {
- Query_log_event qinfo(thd, thd->query, thd->query_length, 0, FALSE);
- mysql_bin_log.write(&qinfo);
- }
+ write_bin_log(thd, FALSE, thd->query, thd->query_length);
rw_unlock(&LOCK_grant);
close_thread_tables(thd);
@@ -5569,6 +5850,13 @@ bool mysql_revoke_all(THD *thd, List <LEX_USER> &list)
TABLE_LIST tables[GRANT_TABLES];
DBUG_ENTER("mysql_revoke_all");
+ /*
+ This statement will be replicated as a statement, even when using
+ row-based replication. The flag will be reset at the end of the
+ statement.
+ */
+ thd->clear_current_stmt_binlog_row_based();
+
if ((result= open_grant_tables(thd, tables)))
DBUG_RETURN(result != 1);
@@ -5618,7 +5906,8 @@ bool mysql_revoke_all(THD *thd, List <LEX_USER> &list)
if (!strcmp(lex_user->user.str,user) &&
!strcmp(lex_user->host.str, host))
{
- if (!replace_db_table(tables[1].table, acl_db->db, *lex_user, ~(ulong)0, 1))
+ if (!replace_db_table(tables[1].table, acl_db->db, *lex_user,
+ ~(ulong)0, 1))
{
/*
Don't increment counter as replace_db_table deleted the
@@ -5695,11 +5984,11 @@ bool mysql_revoke_all(THD *thd, List <LEX_USER> &list)
if (!strcmp(lex_user->user.str,user) &&
!strcmp(lex_user->host.str, host))
{
- if (!replace_routine_table(thd,grant_proc,tables[4].table,*lex_user,
+ if (replace_routine_table(thd,grant_proc,tables[4].table,*lex_user,
grant_proc->db,
grant_proc->tname,
is_proc,
- ~(ulong)0, 1))
+ ~(ulong)0, 1) == 0)
{
revoked= 1;
continue;
@@ -5713,11 +6002,7 @@ bool mysql_revoke_all(THD *thd, List <LEX_USER> &list)
VOID(pthread_mutex_unlock(&acl_cache->lock));
- if (mysql_bin_log.is_open())
- {
- Query_log_event qinfo(thd, thd->query, thd->query_length, 0, FALSE);
- mysql_bin_log.write(&qinfo);
- }
+ write_bin_log(thd, FALSE, thd->query, thd->query_length);
rw_unlock(&LOCK_grant);
close_thread_tables(thd);
@@ -5729,17 +6014,73 @@ bool mysql_revoke_all(THD *thd, List <LEX_USER> &list)
}
-/*
- Revoke privileges for all users on a stored procedure
- SYNOPSIS
- sp_revoke_privileges()
+
+/**
+ If the defining user for a routine does not exist, then the ACL lookup
+ code should raise two errors which we should intercept. We convert the more
+ descriptive error into a warning, and consume the other.
+
+ If any other errors are raised, then we set a flag that should indicate
+ that there was some failure we should complain at a higher level.
+*/
+class Silence_routine_definer_errors : public Internal_error_handler
+{
+public:
+ Silence_routine_definer_errors()
+ : is_grave(FALSE)
+ {}
+
+ virtual ~Silence_routine_definer_errors()
+ {}
+
+ virtual bool handle_error(uint sql_errno, const char *message,
+ MYSQL_ERROR::enum_warning_level level,
+ THD *thd);
+
+ bool has_errors() { return is_grave; }
+
+private:
+ bool is_grave;
+};
+
+bool
+Silence_routine_definer_errors::handle_error(uint sql_errno,
+ const char *message,
+ MYSQL_ERROR::enum_warning_level level,
+ THD *thd)
+{
+ if (level == MYSQL_ERROR::WARN_LEVEL_ERROR)
+ {
+ switch (sql_errno)
+ {
+ case ER_NONEXISTING_PROC_GRANT:
+ /* Convert the error into a warning. */
+ push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, sql_errno, message);
+ return TRUE;
+ default:
+ is_grave= TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+
+/**
+ Revoke privileges for all users on a stored procedure. Use an error handler
+ that converts errors about missing grants into warnings.
+
+ @param
thd The current thread.
+ @param
db DB of the stored procedure
+ @param
name Name of the stored procedure
- RETURN
+ @retval
0 OK.
+ @retval
< 0 Error. Error message not yet sent.
*/
@@ -5750,14 +6091,25 @@ bool sp_revoke_privileges(THD *thd, const char *sp_db, const char *sp_name,
int result;
TABLE_LIST tables[GRANT_TABLES];
HASH *hash= is_proc ? &proc_priv_hash : &func_priv_hash;
+ Silence_routine_definer_errors error_handler;
DBUG_ENTER("sp_revoke_privileges");
if ((result= open_grant_tables(thd, tables)))
DBUG_RETURN(result != 1);
+ /* Be sure to pop this before exiting this scope! */
+ thd->push_internal_handler(&error_handler);
+
rw_wrlock(&LOCK_grant);
VOID(pthread_mutex_lock(&acl_cache->lock));
+ /*
+ 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.
+ */
+ thd->clear_current_stmt_binlog_row_based();
+
/* Remove procedure access */
do
{
@@ -5769,19 +6121,19 @@ bool sp_revoke_privileges(THD *thd, const char *sp_db, const char *sp_name,
{
LEX_USER lex_user;
lex_user.user.str= grant_proc->user;
- lex_user.user.length= (uint) strlen(grant_proc->user);
+ lex_user.user.length= strlen(grant_proc->user);
lex_user.host.str= grant_proc->host.hostname ?
grant_proc->host.hostname : (char*)"";
lex_user.host.length= grant_proc->host.hostname ?
- (uint) strlen(grant_proc->host.hostname) : 0;
- if (!replace_routine_table(thd,grant_proc,tables[4].table,lex_user,
- grant_proc->db, grant_proc->tname,
- is_proc, ~(ulong)0, 1))
+ strlen(grant_proc->host.hostname) : 0;
+
+ if (replace_routine_table(thd,grant_proc,tables[4].table,lex_user,
+ grant_proc->db, grant_proc->tname,
+ is_proc, ~(ulong)0, 1) == 0)
{
revoked= 1;
continue;
}
- result= -1; // Something went wrong
}
counter++;
}
@@ -5791,10 +6143,9 @@ bool sp_revoke_privileges(THD *thd, const char *sp_db, const char *sp_name,
rw_unlock(&LOCK_grant);
close_thread_tables(thd);
- if (result)
- my_message(ER_REVOKE_GRANTS, ER(ER_REVOKE_GRANTS), MYF(0));
+ thd->pop_internal_handler();
- DBUG_RETURN(result);
+ DBUG_RETURN(error_handler.has_errors());
}
@@ -5852,8 +6203,8 @@ int sp_grant_privileges(THD *thd, const char *sp_db, const char *sp_name,
tables->db= (char*)sp_db;
tables->table_name= tables->alias= (char*)sp_name;
- combo->host.length= (uint) strlen(combo->host.str);
- combo->user.length= (uint) strlen(combo->user.str);
+ combo->host.length= strlen(combo->host.str);
+ combo->user.length= strlen(combo->user.str);
combo->host.str= thd->strmake(combo->host.str,combo->host.length);
combo->user.str= thd->strmake(combo->user.str,combo->user.length);
@@ -5963,11 +6314,11 @@ static bool update_schema_privilege(THD *thd, TABLE *table, char *buff,
int i= 2;
CHARSET_INFO *cs= system_charset_info;
restore_record(table, s->default_values);
- table->field[0]->store(buff, strlen(buff), cs);
+ table->field[0]->store(buff, (uint) strlen(buff), cs);
if (db)
- table->field[i++]->store(db, strlen(db), cs);
+ table->field[i++]->store(db, (uint) strlen(db), cs);
if (t_name)
- table->field[i++]->store(t_name, strlen(t_name), cs);
+ table->field[i++]->store(t_name, (uint) strlen(t_name), cs);
if (column)
table->field[i++]->store(column, col_length, cs);
table->field[i++]->store(priv, priv_length, cs);
@@ -6334,12 +6685,6 @@ void fill_effective_table_privileges(THD *thd, GRANT_INFO *grant,
/* db privileges */
grant->privilege|= acl_get(sctx->host, sctx->ip, sctx->priv_user, db, 0);
- if (!grant_option)
- {
- DBUG_PRINT("info", ("privilege 0x%lx", grant->privilege));
- DBUG_VOID_RETURN;
- }
-
/* table privileges */
rw_rdlock(&LOCK_grant);
if (grant->version != grant_version)
diff --git a/sql/sql_acl.h b/sql/sql_acl.h
index 1efcd2e3b6b..9ae17a4bf02 100644
--- a/sql/sql_acl.h
+++ b/sql/sql_acl.h
@@ -13,6 +13,8 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+#include "slave.h" // for tables_ok(), rpl_filter
+
#define SELECT_ACL (1L << 0)
#define INSERT_ACL (1L << 1)
#define UPDATE_ACL (1L << 2)
@@ -39,6 +41,8 @@
#define CREATE_PROC_ACL (1L << 23)
#define ALTER_PROC_ACL (1L << 24)
#define CREATE_USER_ACL (1L << 25)
+#define EVENT_ACL (1L << 26)
+#define TRIGGER_ACL (1L << 27)
/*
don't forget to update
1. static struct show_privileges_st sys_privileges[]
@@ -49,17 +53,16 @@
*/
#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 | \
GRANT_ACL | REFERENCES_ACL | INDEX_ACL | ALTER_ACL | CREATE_TMP_ACL | \
LOCK_TABLES_ACL | EXECUTE_ACL | CREATE_VIEW_ACL | SHOW_VIEW_ACL | \
- CREATE_PROC_ACL | ALTER_PROC_ACL)
+ CREATE_PROC_ACL | ALTER_PROC_ACL | EVENT_ACL | TRIGGER_ACL)
#define TABLE_ACLS \
(SELECT_ACL | INSERT_ACL | UPDATE_ACL | DELETE_ACL | CREATE_ACL | DROP_ACL | \
GRANT_ACL | REFERENCES_ACL | INDEX_ACL | ALTER_ACL | CREATE_VIEW_ACL | \
- SHOW_VIEW_ACL)
+ SHOW_VIEW_ACL | TRIGGER_ACL)
#define COL_ACLS \
(SELECT_ACL | INSERT_ACL | UPDATE_ACL | REFERENCES_ACL)
@@ -76,7 +79,7 @@
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)
+ ALTER_PROC_ACL | CREATE_USER_ACL | EVENT_ACL | TRIGGER_ACL)
#define DEFAULT_CREATE_PROC_ACLS \
(ALTER_PROC_ACL | EXECUTE_ACL)
@@ -94,26 +97,32 @@
#define DB_CHUNK3 (CREATE_VIEW_ACL | SHOW_VIEW_ACL | \
CREATE_PROC_ACL | ALTER_PROC_ACL )
#define DB_CHUNK4 (EXECUTE_ACL)
+#define DB_CHUNK5 (EVENT_ACL | TRIGGER_ACL)
#define fix_rights_for_db(A) (((A) & DB_CHUNK0) | \
(((A) << 4) & DB_CHUNK1) | \
(((A) << 6) & DB_CHUNK2) | \
(((A) << 9) & DB_CHUNK3) | \
- (((A) << 2) & DB_CHUNK4))
+ (((A) << 2) & DB_CHUNK4))| \
+ (((A) << 9) & DB_CHUNK5)
#define get_rights_for_db(A) (((A) & DB_CHUNK0) | \
(((A) & DB_CHUNK1) >> 4) | \
(((A) & DB_CHUNK2) >> 6) | \
(((A) & DB_CHUNK3) >> 9) | \
- (((A) & DB_CHUNK4) >> 2))
+ (((A) & DB_CHUNK4) >> 2))| \
+ (((A) & DB_CHUNK5) >> 9)
#define TBL_CHUNK0 DB_CHUNK0
#define TBL_CHUNK1 DB_CHUNK1
#define TBL_CHUNK2 (CREATE_VIEW_ACL | SHOW_VIEW_ACL)
+#define TBL_CHUNK3 TRIGGER_ACL
#define fix_rights_for_table(A) (((A) & TBL_CHUNK0) | \
(((A) << 4) & TBL_CHUNK1) | \
- (((A) << 11) & TBL_CHUNK2))
+ (((A) << 11) & TBL_CHUNK2) | \
+ (((A) << 15) & TBL_CHUNK3))
#define get_rights_for_table(A) (((A) & TBL_CHUNK0) | \
(((A) & TBL_CHUNK1) >> 4) | \
- (((A) & TBL_CHUNK2) >> 11))
+ (((A) & TBL_CHUNK2) >> 11) | \
+ (((A) & TBL_CHUNK3) >> 15))
#define fix_rights_for_column(A) (((A) & 7) | (((A) & ~7) << 8))
#define get_rights_for_column(A) (((A) & 7) | ((A) >> 8))
#define fix_rights_for_procedure(A) ((((A) << 18) & EXECUTE_ACL) | \
@@ -123,6 +132,36 @@
(((A) & ALTER_PROC_ACL) >> 23) | \
(((A) & GRANT_ACL) >> 8))
+enum mysql_db_table_field
+{
+ MYSQL_DB_FIELD_HOST = 0,
+ MYSQL_DB_FIELD_DB,
+ MYSQL_DB_FIELD_USER,
+ MYSQL_DB_FIELD_SELECT_PRIV,
+ MYSQL_DB_FIELD_INSERT_PRIV,
+ MYSQL_DB_FIELD_UPDATE_PRIV,
+ MYSQL_DB_FIELD_DELETE_PRIV,
+ MYSQL_DB_FIELD_CREATE_PRIV,
+ MYSQL_DB_FIELD_DROP_PRIV,
+ MYSQL_DB_FIELD_GRANT_PRIV,
+ MYSQL_DB_FIELD_REFERENCES_PRIV,
+ MYSQL_DB_FIELD_INDEX_PRIV,
+ MYSQL_DB_FIELD_ALTER_PRIV,
+ MYSQL_DB_FIELD_CREATE_TMP_TABLE_PRIV,
+ MYSQL_DB_FIELD_LOCK_TABLES_PRIV,
+ MYSQL_DB_FIELD_CREATE_VIEW_PRIV,
+ MYSQL_DB_FIELD_SHOW_VIEW_PRIV,
+ MYSQL_DB_FIELD_CREATE_ROUTINE_PRIV,
+ MYSQL_DB_FIELD_ALTER_ROUTINE_PRIV,
+ MYSQL_DB_FIELD_EXECUTE_PRIV,
+ MYSQL_DB_FIELD_EVENT_PRIV,
+ MYSQL_DB_FIELD_TRIGGER_PRIV,
+ MYSQL_DB_FIELD_COUNT
+};
+
+extern TABLE_FIELD_W_TYPE mysql_db_table_fields[];
+extern time_t mysql_db_table_last_check;
+
/* Classes */
struct acl_host_and_ip
diff --git a/sql/sql_analyse.cc b/sql/sql_analyse.cc
index 4b3264069b5..9ca6e0a0a2b 100644
--- a/sql/sql_analyse.cc
+++ b/sql/sql_analyse.cc
@@ -149,7 +149,7 @@ proc_analyse_init(THD *thd, ORDER *param, select_result *result,
case INT_RESULT:
// Check if fieldtype is ulonglong
if (item->type() == Item::FIELD_ITEM &&
- ((Item_field*) item)->field->type() == FIELD_TYPE_LONGLONG &&
+ ((Item_field*) item)->field->type() == MYSQL_TYPE_LONGLONG &&
((Field_longlong*) ((Item_field*) item)->field)->unsigned_flag)
new_field= new field_ulonglong(item, pc);
else
@@ -185,8 +185,7 @@ err:
bool test_if_number(NUM_INFO *info, const char *str, uint str_len)
{
- const char *begin, *end = str + str_len;
-
+ const char *begin, *end= str + str_len;
DBUG_ENTER("test_if_number");
/*
@@ -200,8 +199,8 @@ bool test_if_number(NUM_INFO *info, const char *str, uint str_len)
if (*str == '-')
{
info->negative = 1;
- if (++str == end || *str == '0') // converting -0 to a number
- DBUG_RETURN(0); // might lose information
+ if (++str == end || *str == '0') // converting -0 to a number
+ DBUG_RETURN(0); // might lose information
}
else
info->negative = 0;
@@ -219,31 +218,31 @@ bool test_if_number(NUM_INFO *info, const char *str, uint str_len)
int error;
info->ullval= (ulonglong) my_strtoll10(begin, &endpos, &error);
if (info->integers == 1)
- DBUG_RETURN(0); // a single number can't be zerofill
+ DBUG_RETURN(0); // single number can't be zerofill
info->maybe_zerofill = 1;
- DBUG_RETURN(1); // a zerofill number, or an integer
+ DBUG_RETURN(1); // a zerofill number, or an integer
}
if (*str == '.' || *str == 'e' || *str == 'E')
{
- if (info->zerofill) // can't be zerofill anymore
+ if (info->zerofill) // can't be zerofill anymore
DBUG_RETURN(0);
- if ((str + 1) == end) // number was something like '123[.eE]'
+ if ((str + 1) == end) // number was something like '123[.eE]'
{
char *endpos= (char*) str;
int error;
info->ullval= (ulonglong) my_strtoll10(begin, &endpos, &error);
DBUG_RETURN(1);
}
- if (*str == 'e' || *str == 'E') // number may be something like '1e+50'
+ if (*str == 'e' || *str == 'E') // number may be something like '1e+50'
{
str++;
if (*str != '-' && *str != '+')
- DBUG_RETURN(0);
+ DBUG_RETURN(0);
for (str++; str != end && my_isdigit(system_charset_info,*str); str++) ;
if (str == end)
{
- info->is_float = 1; // we can't use variable decimals here
- DBUG_RETURN(1);
+ info->is_float = 1; // we can't use variable decimals here
+ return 1;
}
DBUG_RETURN(0);
}
@@ -499,7 +498,7 @@ void field_decimal::add()
if (room_in_tree)
{
- char buf[DECIMAL_MAX_FIELD_SIZE];
+ uchar buf[DECIMAL_MAX_FIELD_SIZE];
my_decimal2binary(E_DEC_FATAL_ERROR, dec, buf,
item->max_length, item->decimals);
if (!(element = tree_insert(&tree, (void*)buf, 0, tree.custom_arg)))
@@ -669,7 +668,7 @@ void field_ulonglong::add()
} // field_ulonglong::add
-int analyse::send_row(List<Item> &field_list __attribute__((unused)))
+int analyse::send_row(List<Item> & /* field_list */)
{
field_info **f = f_info;
@@ -765,26 +764,26 @@ int analyse::end_of_records()
{
switch (((Item_field*) (*f)->item)->field->real_type())
{
- case FIELD_TYPE_TIMESTAMP:
+ case MYSQL_TYPE_TIMESTAMP:
ans.append(STRING_WITH_LEN("TIMESTAMP"));
break;
- case FIELD_TYPE_DATETIME:
+ case MYSQL_TYPE_DATETIME:
ans.append(STRING_WITH_LEN("DATETIME"));
break;
- case FIELD_TYPE_DATE:
- case FIELD_TYPE_NEWDATE:
+ case MYSQL_TYPE_DATE:
+ case MYSQL_TYPE_NEWDATE:
ans.append(STRING_WITH_LEN("DATE"));
break;
- case FIELD_TYPE_SET:
+ case MYSQL_TYPE_SET:
ans.append(STRING_WITH_LEN("SET"));
break;
- case FIELD_TYPE_YEAR:
+ case MYSQL_TYPE_YEAR:
ans.append(STRING_WITH_LEN("YEAR"));
break;
- case FIELD_TYPE_TIME:
+ case MYSQL_TYPE_TIME:
ans.append(STRING_WITH_LEN("TIME"));
break;
- case FIELD_TYPE_DECIMAL:
+ case MYSQL_TYPE_DECIMAL:
ans.append(STRING_WITH_LEN("DECIMAL"));
// if item is FIELD_ITEM, it _must_be_ Field_num in this case
if (((Field_num*) ((Item_field*) (*f)->item)->field)->zerofill)
@@ -1034,7 +1033,7 @@ String *field_decimal::avg(String *s, ha_rows rows)
{
if (!(rows - nulls))
{
- s->set((double) 0.0, 1,my_thd_charset);
+ s->set_real((double) 0.0, 1,my_thd_charset);
return s;
}
my_decimal num, avg_val, rounded_avg;
@@ -1055,7 +1054,7 @@ String *field_decimal::std(String *s, ha_rows rows)
{
if (!(rows - nulls))
{
- s->set((double) 0.0, 1,my_thd_charset);
+ s->set_real((double) 0.0, 1,my_thd_charset);
return s;
}
my_decimal num, tmp, sum2, sum2d;
@@ -1068,7 +1067,7 @@ String *field_decimal::std(String *s, ha_rows rows)
my_decimal_sub(E_DEC_FATAL_ERROR, &sum2, sum_sqr+cur_sum, &tmp);
my_decimal_div(E_DEC_FATAL_ERROR, &tmp, &sum2, &num, prec_increment);
my_decimal2double(E_DEC_FATAL_ERROR, &tmp, &std_sqr);
- s->set(((double) std_sqr <= 0.0 ? 0.0 : sqrt(std_sqr)),
+ s->set_real(((double) std_sqr <= 0.0 ? 0.0 : sqrt(std_sqr)),
min(item->decimals + prec_increment, NOT_FIXED_DEC), my_thd_charset);
return s;
@@ -1102,14 +1101,14 @@ int collect_real(double *element, element_count count __attribute__((unused)),
else
info->found = 1;
info->str->append('\'');
- s.set(*element, info->item->decimals, current_thd->charset());
+ s.set_real(*element, info->item->decimals, current_thd->charset());
info->str->append(s);
info->str->append('\'');
return 0;
} // collect_real
-int collect_decimal(char *element, element_count count,
+int collect_decimal(uchar *element, element_count count,
TREE_INFO *info)
{
char buff[DECIMAL_MAX_STR_LENGTH];
diff --git a/sql/sql_analyse.h b/sql/sql_analyse.h
index 2a4226b2d95..8807b40857e 100644
--- a/sql/sql_analyse.h
+++ b/sql/sql_analyse.h
@@ -127,9 +127,9 @@ public:
String *avg(String *s, ha_rows rows)
{
if (!(rows - nulls))
- s->set((double) 0.0, 1,my_thd_charset);
+ s->set_real((double) 0.0, 1,my_thd_charset);
else
- s->set((ulonglong2double(sum) / ulonglong2double(rows - nulls)),
+ s->set_real((ulonglong2double(sum) / ulonglong2double(rows - nulls)),
DEC_IN_AVG,my_thd_charset);
return s;
}
@@ -143,7 +143,7 @@ public:
};
-int collect_decimal(char *element, element_count count,
+int collect_decimal(uchar *element, element_count count,
TREE_INFO *info);
class field_decimal :public field_info
@@ -165,7 +165,7 @@ public:
String *get_min_arg(String *);
String *get_max_arg(String *);
String *avg(String *s, ha_rows rows);
- friend int collect_decimal(char *element, element_count count,
+ friend int collect_decimal(uchar *element, element_count count,
TREE_INFO *info);
tree_walk_action collect_enum()
{ return (tree_walk_action) collect_decimal; }
@@ -189,34 +189,34 @@ public:
void add();
void get_opt_type(String*, ha_rows);
- String *get_min_arg(String *s)
- {
- s->set(min_arg, item->decimals,my_thd_charset);
- return s;
+ String *get_min_arg(String *s)
+ {
+ s->set_real(min_arg, item->decimals, my_thd_charset);
+ return s;
}
- String *get_max_arg(String *s)
- {
- s->set(max_arg, item->decimals,my_thd_charset);
- return s;
+ String *get_max_arg(String *s)
+ {
+ s->set_real(max_arg, item->decimals, my_thd_charset);
+ return s;
}
String *avg(String *s, ha_rows rows)
{
if (!(rows - nulls))
- s->set((double) 0.0, 1,my_thd_charset);
+ s->set_real((double) 0.0, 1,my_thd_charset);
else
- s->set(((double)sum / (double) (rows - nulls)), item->decimals,my_thd_charset);
+ s->set_real(((double)sum / (double) (rows - nulls)), item->decimals,my_thd_charset);
return s;
}
String *std(String *s, ha_rows rows)
{
double tmp = ulonglong2double(rows);
if (!(tmp - nulls))
- s->set((double) 0.0, 1,my_thd_charset);
+ s->set_real((double) 0.0, 1,my_thd_charset);
else
{
double tmp2 = ((sum_sqr - sum * sum / (tmp - nulls)) /
(tmp - nulls));
- s->set(((double) tmp2 <= 0.0 ? 0.0 : sqrt(tmp2)), item->decimals,my_thd_charset);
+ s->set_real(((double) tmp2 <= 0.0 ? 0.0 : sqrt(tmp2)), item->decimals,my_thd_charset);
}
return s;
}
@@ -248,21 +248,21 @@ public:
String *avg(String *s, ha_rows rows)
{
if (!(rows - nulls))
- s->set((double) 0.0, 1,my_thd_charset);
+ s->set_real((double) 0.0, 1,my_thd_charset);
else
- s->set(((double) sum / (double) (rows - nulls)), DEC_IN_AVG,my_thd_charset);
+ s->set_real(((double) sum / (double) (rows - nulls)), DEC_IN_AVG,my_thd_charset);
return s;
}
String *std(String *s, ha_rows rows)
{
double tmp = ulonglong2double(rows);
if (!(tmp - nulls))
- s->set((double) 0.0, 1,my_thd_charset);
+ s->set_real((double) 0.0, 1,my_thd_charset);
else
{
double tmp2 = ((sum_sqr - sum * sum / (tmp - nulls)) /
(tmp - nulls));
- s->set(((double) tmp2 <= 0.0 ? 0.0 : sqrt(tmp2)), DEC_IN_AVG,my_thd_charset);
+ s->set_real(((double) tmp2 <= 0.0 ? 0.0 : sqrt(tmp2)), DEC_IN_AVG,my_thd_charset);
}
return s;
}
@@ -292,9 +292,9 @@ public:
String *avg(String *s, ha_rows rows)
{
if (!(rows - nulls))
- s->set((double) 0.0, 1,my_thd_charset);
+ s->set_real((double) 0.0, 1,my_thd_charset);
else
- s->set((ulonglong2double(sum) / ulonglong2double(rows - nulls)),
+ s->set_real((ulonglong2double(sum) / ulonglong2double(rows - nulls)),
DEC_IN_AVG,my_thd_charset);
return s;
}
@@ -302,13 +302,13 @@ public:
{
double tmp = ulonglong2double(rows);
if (!(tmp - nulls))
- s->set((double) 0.0, 1,my_thd_charset);
+ s->set_real((double) 0.0, 1,my_thd_charset);
else
{
double tmp2 = ((ulonglong2double(sum_sqr) -
ulonglong2double(sum * sum) / (tmp - nulls)) /
(tmp - nulls));
- s->set(((double) tmp2 <= 0.0 ? 0.0 : sqrt(tmp2)), DEC_IN_AVG,my_thd_charset);
+ s->set_real(((double) tmp2 <= 0.0 ? 0.0 : sqrt(tmp2)), DEC_IN_AVG,my_thd_charset);
}
return s;
}
@@ -348,7 +348,7 @@ public:
}
virtual void add() {}
virtual bool change_columns(List<Item> &fields);
- virtual int send_row(List<Item> &fields);
+ virtual int send_row(List<Item> &field_list);
virtual void end_group(void) {}
virtual int end_of_records(void);
friend Procedure *proc_analyse_init(THD *thd, ORDER *param,
diff --git a/sql/sql_array.h b/sql/sql_array.h
index dcef457dce7..e1b22921519 100644
--- a/sql/sql_array.h
+++ b/sql/sql_array.h
@@ -45,7 +45,7 @@ public:
bool append(Elem &el)
{
- return (insert_dynamic(&array, (gptr)&el));
+ return (insert_dynamic(&array, (uchar*)&el));
}
int elements()
diff --git a/sql/sql_base.cc b/sql/sql_base.cc
index 812c060fe15..d17bf085e3b 100644
--- a/sql/sql_base.cc
+++ b/sql/sql_base.cc
@@ -1,4 +1,4 @@
-/* Copyright (C) 2000-2006 MySQL AB
+/* Copyright 2000-2008 MySQL AB, 2008 Sun Microsystems, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -24,10 +24,12 @@
#include <m_ctype.h>
#include <my_dir.h>
#include <hash.h>
-#ifdef __WIN__
+#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
@@ -42,7 +44,7 @@ public:
virtual ~Prelock_error_handler() {}
- virtual bool handle_error(uint sql_errno,
+ virtual bool handle_error(uint sql_errno, const char *message,
MYSQL_ERROR::enum_warning_level level,
THD *thd);
@@ -56,6 +58,7 @@ private:
bool
Prelock_error_handler::handle_error(uint sql_errno,
+ const char * /* message */,
MYSQL_ERROR::enum_warning_level /* level */,
THD * /* thd */)
{
@@ -80,55 +83,70 @@ bool Prelock_error_handler::safely_trapped_errors()
return ((m_handled_errors > 0) && (m_unhandled_errors == 0));
}
+/**
+ @defgroup Data_Dictionary Data Dictionary
+ @{
+*/
TABLE *unused_tables; /* Used by mysql_test */
HASH open_cache; /* Used by mysql_test */
-
-static int open_unireg_entry(THD *thd, TABLE *entry, const char *db,
- const char *name, const char *alias,
- TABLE_LIST *table_list, MEM_ROOT *mem_root,
- uint flags);
+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;
+
+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, const char *path, const char *alias,
- const char *db, const char *table_name,
+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_two_write_locked_tables_with_auto_increment(TABLE_LIST *tables);
+
-extern "C" byte *table_cache_key(const byte *record,uint *length,
+extern "C" uchar *table_cache_key(const uchar *record, size_t *length,
my_bool not_used __attribute__((unused)))
{
TABLE *entry=(TABLE*) record;
- *length= entry->s->key_length;
- return (byte*) entry->s->table_cache_key;
+ *length= entry->s->table_cache_key.length;
+ return (uchar*) entry->s->table_cache_key.str;
}
+
bool table_cache_init(void)
{
return hash_init(&open_cache, &my_charset_bin, table_cache_size+16,
- 0, 0,table_cache_key,
+ 0, 0, table_cache_key,
(hash_free_key) free_cache_entry, 0) != 0;
}
void table_cache_free(void)
{
DBUG_ENTER("table_cache_free");
- close_cached_tables((THD*) 0,0,(TABLE_LIST*) 0);
- if (!open_cache.records) // Safety first
- hash_free(&open_cache);
+ 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;
}
-uint cached_tables(void)
+uint cached_open_tables(void)
{
return open_cache.records;
}
+
#ifdef EXTRA_DEBUG
static void check_unused(void)
{
- uint count=0,idx=0;
+ uint count= 0, open_files= 0, idx= 0;
TABLE *cur_link,*start_link;
if ((start_link=cur_link=unused_tables))
@@ -152,17 +170,514 @@ static void check_unused(void)
TABLE *entry=(TABLE*) hash_element(&open_cache,idx);
if (!entry->in_use)
count--;
+ if (entry->file)
+ 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
}
#else
#define check_unused()
#endif
+
+/*
+ Create a table cache key
+
+ SYNOPSIS
+ create_table_def_key()
+ thd Thread handler
+ key Create key here (must be of size MAX_DBKEY_LENGTH)
+ table_list Table definition
+ tmp_table Set if table is a tmp table
+
+ IMPLEMENTATION
+ The table cache_key is created from:
+ db_name + \0
+ table_name + \0
+
+ if the table is a tmp table, we add the following to make each tmp table
+ unique on the slave:
+
+ 4 bytes for master thread id
+ 4 bytes pseudo thread id
+
+ RETURN
+ Length of key
+*/
+
+uint create_table_def_key(THD *thd, char *key, TABLE_LIST *table_list,
+ bool tmp_table)
+{
+ uint key_length= (uint) (strmov(strmov(key, table_list->db)+1,
+ table_list->table_name)-key)+1;
+ if (tmp_table)
+ {
+ int4store(key + key_length, thd->server_id);
+ int4store(key + key_length + 4, thd->variables.pseudo_thread_id);
+ key_length+= TMP_TABLE_KEY_EXTRA;
+ }
+ return key_length;
+}
+
+
+
+/*****************************************************************************
+ Functions to handle table definition cach (TABLE_SHARE)
+*****************************************************************************/
+
+extern "C" uchar *table_def_key(const uchar *record, size_t *length,
+ my_bool not_used __attribute__((unused)))
+{
+ TABLE_SHARE *entry=(TABLE_SHARE*) record;
+ *length= entry->table_cache_key.length;
+ return (uchar*) entry->table_cache_key.str;
+}
+
+
+static void table_def_free_entry(TABLE_SHARE *share)
+{
+ DBUG_ENTER("table_def_free_entry");
+ 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;
+}
+
+
+bool table_def_init(void)
+{
+ table_def_inited= 1;
+ pthread_mutex_init(&LOCK_table_share, 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;
+}
+
+
+void table_def_free(void)
+{
+ DBUG_ENTER("table_def_free");
+ if (table_def_inited)
+ {
+ table_def_inited= 0;
+ pthread_mutex_destroy(&LOCK_table_share);
+ hash_free(&table_def_cache);
+ }
+ DBUG_VOID_RETURN;
+}
+
+
+uint cached_table_definitions(void)
+{
+ return table_def_cache.records;
+}
+
+
+/*
+ Get TABLE_SHARE for a table.
+
+ get_table_share()
+ thd Thread handle
+ table_list Table that should be opened
+ key Table cache key
+ key_length Length of key
+ db_flags Flags to open_table_def():
+ OPEN_VIEW
+ error out: Error code from open_table_def()
+
+ IMPLEMENTATION
+ Get a table definition from the table definition cache.
+ If it doesn't exist, create a new from the table definition file.
+
+ NOTES
+ We must have wrlock on LOCK_open when we come here
+ (To be changed later)
+
+ RETURN
+ 0 Error
+ # Share for table
+*/
+
+TABLE_SHARE *get_table_share(THD *thd, TABLE_LIST *table_list, char *key,
+ uint key_length, uint db_flags, int *error)
+{
+ TABLE_SHARE *share;
+ DBUG_ENTER("get_table_share");
+
+ *error= 0;
+
+ /* Read table definition from cache */
+ if ((share= (TABLE_SHARE*) hash_search(&table_def_cache,(uchar*) key,
+ key_length)))
+ goto found;
+
+ if (!(share= alloc_table_share(table_list, key, key_length)))
+ {
+ DBUG_RETURN(0);
+ }
+
+ /*
+ 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
+ and using it for the sole purpose of serializing accesses to a
+ static variable, we assign the table id here. We assign it to the
+ share before inserting it into the table_def_cache to be really
+ sure that it cannot be read from the cache without having a table
+ id assigned.
+
+ CAVEAT. This means that the table cannot be used for
+ binlogging/replication purposes, unless get_table_share() has been
+ called directly or indirectly.
+ */
+ assign_new_table_id(share);
+
+ if (my_hash_insert(&table_def_cache, (uchar*) share))
+ {
+ free_table_share(share);
+ DBUG_RETURN(0); // return error
+ }
+ if (open_table_def(thd, share, db_flags))
+ {
+ *error= share->error;
+ (void) 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 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));
+ }
+
+ DBUG_PRINT("exit", ("share: 0x%lx ref_count: %u",
+ (ulong) share, share->ref_count));
+ DBUG_RETURN(share);
+}
+
+
+/*
+ Get a table share. If it didn't exist, try creating it from engine
+
+ For arguments and return values, see get_table_from_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)
+{
+ TABLE_SHARE *share;
+ int tmp;
+ DBUG_ENTER("get_table_share_with_create");
+
+ share= get_table_share(thd, table_list, key, key_length, db_flags, error);
+ /*
+ If share is not NULL, we found an existing share.
+
+ If share is NULL, and there is no error, we're inside
+ pre-locking, which silences 'ER_NO_SUCH_TABLE' errors
+ with the intention to silently drop non-existing tables
+ from the pre-locking list. In this case we still need to try
+ auto-discover before returning a NULL share.
+
+ 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.
+
+ Finally, if share is still NULL, it's a real error and we need
+ to abort.
+
+ @todo Rework alternative ways to deal with ER_NO_SUCH TABLE.
+ */
+ if (share || thd->is_error() && thd->main_da.sql_errno() != ER_NO_SUCH_TABLE)
+
+ DBUG_RETURN(share);
+
+ /* Table didn't exist. Check if some engine can provide it */
+ if ((tmp= ha_create_table_from_engine(thd, table_list->db,
+ table_list->table_name)) < 0)
+ {
+ /*
+ 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.
+ @todo Rework the alternative ways to deal with ER_NO_SUCH TABLE.
+ */
+ if (thd->is_error())
+ {
+ if (table_list->parent_l)
+ {
+ thd->clear_error();
+ my_error(ER_WRONG_MRG_TABLE, MYF(0));
+ }
+ else if (table_list->belong_to_view)
+ {
+ TABLE_LIST *view= table_list->belong_to_view;
+ thd->clear_error();
+ my_error(ER_VIEW_INVALID, MYF(0),
+ view->view_db.str, view->view_name.str);
+ }
+ }
+ DBUG_RETURN(0);
+ }
+ if (tmp)
+ {
+ /* 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);
+ }
+ /* 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));
+}
+
+
+/*
+ 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.
+*/
+
+void release_table_share(TABLE_SHARE *share, enum release_type type)
+{
+ 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);
+
+ pthread_mutex_lock(&share->mutex);
+ if (!--share->ref_count)
+ {
+ if (share->version != refresh_version)
+ to_be_deleted=1;
+ 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 (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;
+}
+
+
+/*
+ Check if table definition exits in cache
+
+ SYNOPSIS
+ get_cached_table_share()
+ db Database name
+ table_name Table name
+
+ RETURN
+ 0 Not cached
+ # TABLE_SHARE for table
+*/
+
+TABLE_SHARE *get_cached_table_share(const char *db, const char *table_name)
+{
+ char key[NAME_LEN*2+2];
+ TABLE_LIST table_list;
+ uint key_length;
+ safe_mutex_assert_owner(&LOCK_open);
+
+ table_list.db= (char*) db;
+ table_list.table_name= (char*) table_name;
+ key_length= create_table_def_key((THD*) 0, key, &table_list, 0);
+ return (TABLE_SHARE*) 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->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
@@ -199,26 +714,23 @@ OPEN_TABLE_LIST *list_open_tables(THD *thd, const char *db, const char *wild)
TABLE *entry=(TABLE*) hash_element(&open_cache,idx);
TABLE_SHARE *share= entry->s;
- DBUG_ASSERT(share->table_name != 0);
- if ((!share->table_name)) // To be removed
- continue; // Shouldn't happen
- if (db && my_strcasecmp(system_charset_info, db, share->db))
+ if (db && my_strcasecmp(system_charset_info, db, share->db.str))
continue;
- if (wild && wild_compare(share->table_name,wild,0))
+ if (wild && wild_compare(share->table_name.str, wild, 0))
continue;
/* Check if user has SELECT privilege for any column in the table */
- table_list.db= (char*) share->db;
- table_list.table_name= (char*) share->table_name;
+ table_list.db= share->db.str;
+ table_list.table_name= share->table_name.str;
table_list.grant.privilege=0;
- if (check_table_access(thd,SELECT_ACL | EXTRA_ACL,&table_list,1))
+ if (check_table_access(thd,SELECT_ACL | EXTRA_ACL,&table_list, 1, TRUE))
continue;
/* need to check if we haven't already listed it */
for (table= open_list ; table ; table=table->next)
{
- if (!strcmp(table->table,share->table_name) &&
- !strcmp(table->db,entry->s->db))
+ if (!strcmp(table->table, share->table_name.str) &&
+ !strcmp(table->db, share->db.str))
{
if (entry->in_use)
table->in_use++;
@@ -230,15 +742,15 @@ OPEN_TABLE_LIST *list_open_tables(THD *thd, const char *db, const char *wild)
if (table)
continue;
if (!(*start_list = (OPEN_TABLE_LIST *)
- sql_alloc(sizeof(**start_list)+share->key_length)))
+ sql_alloc(sizeof(**start_list)+share->table_cache_key.length)))
{
open_list=0; // Out of memory
break;
}
strmov((*start_list)->table=
strmov(((*start_list)->db= (char*) ((*start_list)+1)),
- entry->s->db)+1,
- entry->s->table_name);
+ 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= &(*start_list)->next;
@@ -255,10 +767,17 @@ OPEN_TABLE_LIST *list_open_tables(THD *thd, const char *db, const char *wild)
void intern_close_table(TABLE *table)
{ // Free all structures
+ DBUG_ENTER("intern_close_table");
+ DBUG_PRINT("tcache", ("table: '%s'.'%s' 0x%lx",
+ table->s ? table->s->db.str : "?",
+ table->s ? table->s->table_name.str : "?",
+ (long) table));
+
free_io_cache(table);
delete table->triggers;
- if (table->file)
- VOID(closefrm(table)); // close file
+ if (table->file) // Not true if name lock
+ VOID(closefrm(table, 1)); // close file
+ DBUG_VOID_RETURN;
}
/*
@@ -275,7 +794,9 @@ void intern_close_table(TABLE *table)
static void free_cache_entry(TABLE *table)
{
DBUG_ENTER("free_cache_entry");
- safe_mutex_assert_owner(&LOCK_open);
+
+ /* Assert that MERGE children are not attached before final close. */
+ DBUG_ASSERT(!table->is_children_attached());
intern_close_table(table);
if (!table->in_use)
@@ -290,7 +811,7 @@ static void free_cache_entry(TABLE *table)
}
check_unused(); // consisty check
}
- my_free((gptr) table,MYF(0));
+ my_free((uchar*) table,MYF(0));
DBUG_VOID_RETURN;
}
@@ -302,39 +823,103 @@ void free_io_cache(TABLE *table)
if (table->sort.io_cache)
{
close_cached_file(table->sort.io_cache);
- my_free((gptr) table->sort.io_cache,MYF(0));
+ my_free((uchar*) table->sort.io_cache,MYF(0));
table->sort.io_cache=0;
}
DBUG_VOID_RETURN;
}
+
/*
Close all tables which aren't in use by any thread
- THD can be NULL, but then if_wait_for_refresh must be FALSE
- and tables must be NULL.
+ @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.
+
+ @remark THD can be NULL, but then wait_for_refresh must be FALSE
+ and tables must be NULL.
*/
-bool close_cached_tables(THD *thd, bool if_wait_for_refresh,
- TABLE_LIST *tables)
+bool close_cached_tables(THD *thd, TABLE_LIST *tables, bool have_lock,
+ bool wait_for_refresh, bool wait_for_placeholders)
{
bool result=0;
DBUG_ENTER("close_cached_tables");
- DBUG_ASSERT(thd || (!if_wait_for_refresh && !tables));
+ DBUG_ASSERT(thd || (!wait_for_refresh && !tables));
- VOID(pthread_mutex_lock(&LOCK_open));
+ if (!have_lock)
+ VOID(pthread_mutex_lock(&LOCK_open));
if (!tables)
{
+ refresh_version++; // Force close of open tables
while (unused_tables)
{
#ifdef EXTRA_DEBUG
- if (hash_delete(&open_cache,(byte*) unused_tables))
+ if (hash_delete(&open_cache,(uchar*) unused_tables))
printf("Warning: Couldn't delete open table from hash\n");
#else
- VOID(hash_delete(&open_cache,(byte*) unused_tables));
+ VOID(hash_delete(&open_cache,(uchar*) unused_tables));
#endif
}
- refresh_version++; // Force close of open tables
+ /* 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));
+ }
+ 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);
+ if (table->in_use)
+ table->in_use->some_tables_deleted= 1;
+ }
+ }
}
else
{
@@ -346,13 +931,13 @@ bool close_cached_tables(THD *thd, bool if_wait_for_refresh,
found=1;
}
if (!found)
- if_wait_for_refresh=0; // Nothing to wait for
+ wait_for_refresh=0; // Nothing to wait for
}
#ifndef EMBEDDED_LIBRARY
if (!tables)
kill_delayed_threads();
#endif
- if (if_wait_for_refresh)
+ if (wait_for_refresh)
{
/*
If there is any table that has a lower refresh_version, wait until
@@ -360,11 +945,11 @@ bool close_cached_tables(THD *thd, bool if_wait_for_refresh,
*/
thd->mysys_var->current_mutex= &LOCK_open;
thd->mysys_var->current_cond= &COND_refresh;
- thd->proc_info="Flushing tables";
+ thd_proc_info(thd, "Flushing tables");
close_old_data_files(thd,thd->open_tables,1,1);
- mysql_ha_flush(thd, tables, MYSQL_HA_REOPEN_ON_USAGE | MYSQL_HA_FLUSH_ALL,
- TRUE);
+ mysql_ha_flush(thd);
+
bool found=1;
/* Wait until all threads has closed all the tables we had locked */
DBUG_PRINT("info",
@@ -375,6 +960,9 @@ bool close_cached_tables(THD *thd, bool if_wait_for_refresh,
for (uint idx=0 ; idx < open_cache.records ; idx++)
{
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
@@ -389,7 +977,8 @@ bool close_cached_tables(THD *thd, bool if_wait_for_refresh,
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)
+ 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"));
@@ -408,9 +997,81 @@ bool close_cached_tables(THD *thd, bool if_wait_for_refresh,
thd->in_lock_tables=0;
/* Set version for table */
for (TABLE *table=thd->open_tables; table ; table= table->next)
- table->s->version= refresh_version;
+ {
+ /*
+ Preserve the version (0) of write locked tables so that a impending
+ global read lock won't sneak in.
+ */
+ if (table->reginfo.lock_type < TL_WRITE_ALLOW_WRITE)
+ table->s->version= refresh_version;
+ }
}
- VOID(pthread_mutex_unlock(&LOCK_open));
+ if (!have_lock)
+ VOID(pthread_mutex_unlock(&LOCK_open));
+ 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(thd, 0);
+ pthread_mutex_unlock(&thd->mysys_var->mutex);
+ }
+ DBUG_RETURN(result);
+}
+
+
+/*
+ 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)
+{
+ uint idx;
+ TABLE_LIST tmp, *tables= NULL;
+ bool result= FALSE;
+ DBUG_ENTER("close_cached_connections");
+ DBUG_ASSERT(thd);
+
+ bzero(&tmp, sizeof(TABLE_LIST));
+
+ if (!have_lock)
+ VOID(pthread_mutex_lock(&LOCK_open));
+
+ for (idx= 0; idx < table_def_cache.records; idx++)
+ {
+ TABLE_SHARE *share= (TABLE_SHARE *) 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)
+ continue;
+
+ /* Compare the connection string */
+ if (connection &&
+ (connection->length > share->connect_string.length ||
+ (connection->length < share->connect_string.length &&
+ (share->connect_string.str[connection->length] != '/' &&
+ share->connect_string.str[connection->length] != '\\')) ||
+ strncasecmp(connection->str, share->connect_string.str,
+ connection->length)))
+ continue;
+
+ /* close_cached_tables() only uses these elements */
+ tmp.db= share->db.str;
+ tmp.table_name= share->table_name.str;
+ tmp.next_local= tables;
+
+ tables= (TABLE_LIST *) memdup_root(thd->mem_root, (char*)&tmp,
+ sizeof(TABLE_LIST));
+ }
+
+ 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);
@@ -419,10 +1080,63 @@ bool close_cached_tables(THD *thd, bool if_wait_for_refresh,
thd->proc_info=0;
pthread_mutex_unlock(&thd->mysys_var->mutex);
}
+
DBUG_RETURN(result);
}
+/**
+ Mark all temporary tables which were used by the current statement or
+ substatement as free for reuse, but only if the query_id can be cleared.
+
+ @param thd thread context
+
+ @remark For temp tables associated with a open SQL HANDLER the query_id
+ is not reset until the HANDLER is closed.
+*/
+
+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 all tables in the list which were used by current substatement
as free for reuse.
@@ -449,8 +1163,49 @@ bool close_cached_tables(THD *thd, bool if_wait_for_refresh,
static void mark_used_tables_as_free_for_reuse(THD *thd, TABLE *table)
{
for (; table ; table= table->next)
+ {
if (table->query_id == thd->query_id)
+ {
table->query_id= 0;
+ table->file->ha_reset();
+ }
+ }
+}
+
+
+/**
+ Auxiliary function to close all tables in the open_tables list.
+
+ @param thd Thread context.
+
+ @remark It should not ordinarily be called directly.
+*/
+
+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));
+
+ 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;
+
+ /* 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)
+ {
+ /* Tell threads waiting for refresh that something has happened */
+ broadcast_refresh();
+ }
+
+ VOID(pthread_mutex_unlock(&LOCK_open));
}
@@ -461,29 +1216,30 @@ static void mark_used_tables_as_free_for_reuse(THD *thd, TABLE *table)
SYNOPSIS
close_thread_tables()
thd Thread handler
- lock_in_use Set to 1 (0 = default) if caller has a lock on
- LOCK_open
- skip_derived Set to 1 (0 = default) if we should not free derived
- tables.
- stopper When closing tables from thd->open_tables(->next)*,
- don't close/remove tables starting from stopper.
IMPLEMENTATION
Unlocks tables and frees derived tables.
Put all normal tables used by thread in free list.
- When in prelocked mode it will only close/mark as free for reuse
- tables opened by this substatement, it will also check if we are
- closing tables after execution of complete query (i.e. we are on
- upper level) and will leave prelocked mode if needed.
+ It will only close/mark as free for reuse tables opened by this
+ substatement, it will also check if we are closing tables after
+ execution of complete query (i.e. we are on upper level) and will
+ leave prelocked mode if needed.
*/
-void close_thread_tables(THD *thd, bool lock_in_use, bool skip_derived)
+void close_thread_tables(THD *thd)
{
- bool found_old_table;
+ TABLE *table;
prelocked_mode_type prelocked_mode= thd->prelocked_mode;
DBUG_ENTER("close_thread_tables");
+#ifdef EXTRA_DEBUG
+ DBUG_PRINT("tcache", ("open tables:"));
+ for (table= thd->open_tables; table; table= table->next)
+ DBUG_PRINT("tcache", ("table: '%s'.'%s' 0x%lx", table->s->db.str,
+ table->s->table_name.str, (long) table));
+#endif
+
/*
We are assuming here that thd->derived_tables contains ONLY derived
tables for this substatement. i.e. instead of approach which uses
@@ -495,9 +1251,9 @@ void close_thread_tables(THD *thd, bool lock_in_use, bool skip_derived)
derived tables with (sub-)statement instead of thread and destroy
them at the end of its execution.
*/
- if (thd->derived_tables && !skip_derived)
+ if (thd->derived_tables)
{
- TABLE *table, *next;
+ TABLE *next;
/*
Close all derived tables generated in queries like
SELECT * FROM (SELECT * FROM t1)
@@ -510,44 +1266,49 @@ void close_thread_tables(THD *thd, bool lock_in_use, bool skip_derived)
thd->derived_tables= 0;
}
- if (prelocked_mode)
- {
+ /*
+ 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;
+
/*
- Mark all temporary tables used by this substatement as free for reuse.
+ Reset transaction state, but only if we're not inside a
+ sub-statement of a prelocked statement.
*/
- mark_used_tables_as_free_for_reuse(thd, thd->temporary_tables);
+ if (! prelocked_mode || thd->lex->requires_prelocking())
+ thd->transaction.stmt.reset();
}
if (thd->locked_tables || prelocked_mode)
{
- /*
- Let us commit transaction for statement. Since in 5.0 we only have
- one statement transaction and don't allow several nested statement
- transactions this call will do nothing if we are inside of stored
- function or trigger (i.e. statement transaction is already active and
- does not belong to statement for which we do close_thread_tables()).
- TODO: This should be fixed in later releases.
- */
- ha_commit_stmt(thd);
- /* We are under simple LOCK TABLES so should not do anything else. */
- if (!prelocked_mode)
- DBUG_VOID_RETURN;
+ /* Ensure we are calling ha_reset() for all used tables */
+ mark_used_tables_as_free_for_reuse(thd, thd->open_tables);
- if (!thd->lex->requires_prelocking())
- {
- /*
- If we are executing one of substatements we have to mark
- all tables which it used as free for reuse.
- */
- mark_used_tables_as_free_for_reuse(thd, thd->open_tables);
+ /*
+ We are under simple LOCK TABLES or we're inside a sub-statement
+ of a prelocked statement, so should not do anything else.
+ */
+ if (!prelocked_mode || !thd->lex->requires_prelocking())
DBUG_VOID_RETURN;
- }
- DBUG_ASSERT(prelocked_mode);
/*
- We are in prelocked mode, so we have to leave it now with doing
- implicit UNLOCK TABLES if need.
+ We are in the top-level statement of a prelocked statement,
+ so we have to leave the prelocked mode now with doing implicit
+ UNLOCK TABLES if needed.
*/
DBUG_PRINT("info",("thd->prelocked_mode= NON_PRELOCKED"));
thd->prelocked_mode= NON_PRELOCKED;
@@ -562,45 +1323,28 @@ void close_thread_tables(THD *thd, bool lock_in_use, bool skip_derived)
if (thd->lock)
{
+ /*
+ For RBR we flush the pending event just before we unlock all the
+ tables. This means that we are at the end of a topmost
+ statement, so we ensure that the STMT_END_F flag is set on the
+ pending event. For statements that are *inside* stored
+ functions, the pending event will not be flushed: that will be
+ handled either before writing a query log event (inside
+ binlog_query()) or when preparing a pending event.
+ */
+ thd->binlog_flush_pending_rows_event(TRUE);
mysql_unlock_tables(thd, thd->lock);
thd->lock=0;
}
/*
- assume handlers auto-commit (if some doesn't - transaction handling
- in MySQL should be redesigned to support it; it's a big change,
- and it's not worth it - better to commit explicitly only writing
- transactions, read-only ones should better take care of themselves.
- saves some work in 2pc too)
- see also sql_parse.cc - dispatch_command()
+ 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.
*/
- bzero(&thd->transaction.stmt, sizeof(thd->transaction.stmt));
- if (!thd->active_transaction())
- thd->transaction.xid_state.xid.null();
-
- /* VOID(pthread_sigmask(SIG_SETMASK,&thd->block_signals,NULL)); */
- if (!lock_in_use)
- VOID(pthread_mutex_lock(&LOCK_open));
- safe_mutex_assert_owner(&LOCK_open);
-
- DBUG_PRINT("info", ("thd->open_tables: %p", thd->open_tables));
-
- found_old_table= 0;
- while (thd->open_tables)
- found_old_table|=close_thread_table(thd, &thd->open_tables);
- thd->some_tables_deleted=0;
-
- /* Free tables to hold down open files */
- while (open_cache.records > table_cache_size && unused_tables)
- VOID(hash_delete(&open_cache,(byte*) unused_tables)); /* purecov: tested */
- check_unused();
- if (found_old_table)
- {
- /* Tell threads waiting for refresh that something has happened */
- broadcast_refresh();
- }
- if (!lock_in_use)
- VOID(pthread_mutex_unlock(&LOCK_open));
- /* VOID(pthread_sigmask(SIG_SETMASK,&thd->signals,NULL)); */
+ if (thd->open_tables)
+ close_open_tables(thd);
if (prelocked_mode == PRELOCKED)
{
@@ -609,12 +1353,13 @@ void close_thread_tables(THD *thd, bool lock_in_use, bool skip_derived)
good idea to turn off OPTION_TABLE_LOCK flag.
*/
DBUG_ASSERT(thd->lex->requires_prelocking());
- thd->options&= ~(ulong) (OPTION_TABLE_LOCK);
+ thd->options&= ~(OPTION_TABLE_LOCK);
}
DBUG_VOID_RETURN;
}
+
/* move one table to free list */
bool close_thread_table(THD *thd, TABLE **table_ptr)
@@ -624,12 +1369,21 @@ bool close_thread_table(THD *thd, TABLE **table_ptr)
DBUG_ENTER("close_thread_table");
DBUG_ASSERT(table->key_read == 0);
DBUG_ASSERT(!table->file || table->file->inited == handler::NONE);
+ DBUG_PRINT("tcache", ("table: '%s'.'%s' 0x%lx", table->s->db.str,
+ table->s->table_name.str, (long) table));
*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.
+ */
+ if (table->child_l || table->parent)
+ detach_merge_children(table, TRUE);
+
if (table->needs_reopen_or_name_lock() ||
thd->version != refresh_version || !table->db_stat)
{
- VOID(hash_delete(&open_cache,(byte*) table));
+ VOID(hash_delete(&open_cache,(uchar*) table));
found_old_table=1;
}
else
@@ -640,16 +1394,13 @@ bool close_thread_table(THD *thd, TABLE **table_ptr)
*/
DBUG_ASSERT(!table->open_placeholder);
- if (table->s->flush_version != flush_version)
- {
- table->s->flush_version= flush_version;
- table->file->extra(HA_EXTRA_FLUSH);
- }
- else
- {
- // Free memory and reset for next loop
- table->file->reset();
- }
+ /* 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)
{
@@ -664,64 +1415,57 @@ bool close_thread_table(THD *thd, TABLE **table_ptr)
DBUG_RETURN(found_old_table);
}
- /* Close and delete temporary tables */
-
-void close_temporary(TABLE *table,bool delete_table)
-{
- DBUG_ENTER("close_temporary");
- char path[FN_REFLEN];
- db_type table_type=table->s->db_type;
- strmov(path,table->s->path);
- free_io_cache(table);
- closefrm(table);
- my_free((char*) table,MYF(0));
- if (delete_table)
- rm_temporary_table(table_type, path);
- DBUG_VOID_RETURN;
-}
/* close_temporary_tables' internal, 4 is due to uint4korr definition */
static inline uint tmpkeyval(THD *thd, TABLE *table)
{
- return uint4korr(table->s->table_cache_key + table->s->key_length - 4);
+ return uint4korr(table->s->table_cache_key.str + table->s->table_cache_key.length - 4);
}
-/* Creates one DROP TEMPORARY TABLE binlog event for each pseudo-thread */
+
+/*
+ Close all temporary tables created by 'CREATE TEMPORARY TABLE' for thread
+ creates one DROP TEMPORARY TABLE binlog event for each pseudo-thread
+*/
void close_temporary_tables(THD *thd)
{
TABLE *table;
+ TABLE *next;
+ TABLE *prev_table;
+ /* Assume thd->options has OPTION_QUOTE_SHOW_CREATE */
+ bool was_quote_show= TRUE;
+ LINT_INIT(next);
+
if (!thd->temporary_tables)
return;
- if (!mysql_bin_log.is_open())
+ if (!mysql_bin_log.is_open() || thd->current_stmt_binlog_row_based)
{
- TABLE *next;
- for (table= thd->temporary_tables; table; table= next)
+ TABLE *tmp_next;
+ for (table= thd->temporary_tables; table; table= tmp_next)
{
- next= table->next;
- close_temporary(table, 1);
+ tmp_next= table->next;
+ close_temporary(table, 1, 1);
}
thd->temporary_tables= 0;
return;
}
- TABLE *next,
- *prev_table /* prev link is not maintained in TABLE's double-linked list */;
- bool was_quote_show= true; /* to assume thd->options has OPTION_QUOTE_SHOW_CREATE */
- // Better add "if exists", in case a RESET MASTER has been done
+ /* Better add "if exists", in case a RESET MASTER has been done */
const char stub[]= "DROP /*!40005 TEMPORARY */ TABLE IF EXISTS ";
uint stub_len= sizeof(stub) - 1;
char buf[256];
- memcpy(buf, stub, stub_len);
String s_query= String(buf, sizeof(buf), system_charset_info);
- bool found_user_tables= false;
- LINT_INIT(next);
+ bool found_user_tables= FALSE;
+
+ memcpy(buf, stub, stub_len);
/*
- insertion sort of temp tables by pseudo_thread_id to build ordered list
- of sublists of equal pseudo_thread_id
+ Insertion sort of temp tables by pseudo_thread_id to build ordered list
+ of sublists of equal pseudo_thread_id
*/
+
for (prev_table= thd->temporary_tables, table= prev_table->next;
table;
prev_table= table, table= table->next)
@@ -767,10 +1511,13 @@ void close_temporary_tables(THD *thd)
{
if (is_user_table(table))
{
+ my_thread_id save_pseudo_thread_id= thd->variables.pseudo_thread_id;
/* Set pseudo_thread_id to be that of the processed table */
thd->variables.pseudo_thread_id= tmpkeyval(thd, table);
- /* Loop forward through all tables within the sublist of
- common pseudo_thread_id to create single DROP query */
+ /*
+ Loop forward through all tables within the sublist of
+ common pseudo_thread_id to create single DROP query.
+ */
for (s_query.length(stub_len);
table && is_user_table(table) &&
tmpkeyval(thd, table) == thd->variables.pseudo_thread_id;
@@ -780,13 +1527,13 @@ void close_temporary_tables(THD *thd)
We are going to add 4 ` around the db/table names and possible more
due to special characters in the names
*/
- append_identifier(thd, &s_query, table->s->db, (uint) strlen(table->s->db));
- s_query.q_append('.');
- append_identifier(thd, &s_query, table->s->table_name,
- (uint) strlen(table->s->table_name));
- s_query.q_append(',');
+ append_identifier(thd, &s_query, table->s->db.str, strlen(table->s->db.str));
+ s_query.append('.');
+ append_identifier(thd, &s_query, table->s->table_name.str,
+ strlen(table->s->table_name.str));
+ s_query.append(',');
next= table->next;
- close_temporary(table, 1);
+ close_temporary(table, 1, 1);
}
thd->clear_error();
CHARSET_INFO *cs_save= thd->variables.character_set_client;
@@ -796,29 +1543,30 @@ void close_temporary_tables(THD *thd)
0, FALSE);
thd->variables.character_set_client= cs_save;
/*
- Imagine the thread had created a temp table, then was doing a SELECT, and
- the SELECT was killed. Then it's not clever to mark the statement above as
- "killed", because it's not really a statement updating data, and there
- are 99.99% chances it will succeed on slave.
- If a real update (one updating a persistent table) was killed on the
- master, then this real update will be logged with error_code=killed,
- rightfully causing the slave to stop.
+ Imagine the thread had created a temp table, then was doing a
+ SELECT, and the SELECT was killed. Then it's not clever to
+ mark the statement above as "killed", because it's not really
+ a statement updating data, and there are 99.99% chances it
+ will succeed on slave. If a real update (one updating a
+ persistent table) was killed on the master, then this real
+ update will be logged with error_code=killed, rightfully
+ causing the slave to stop.
*/
qinfo.error_code= 0;
mysql_bin_log.write(&qinfo);
+ thd->variables.pseudo_thread_id= save_pseudo_thread_id;
}
else
{
next= table->next;
- close_temporary(table, 1);
+ close_temporary(table, 1, 1);
}
}
if (!was_quote_show)
- thd->options &= ~OPTION_QUOTE_SHOW_CREATE; /* restore option */
+ thd->options&= ~OPTION_QUOTE_SHOW_CREATE; /* restore option */
thd->temporary_tables=0;
}
-
/*
Find table in list.
@@ -1006,26 +1754,39 @@ void update_non_unique_table_error(TABLE_LIST *update,
}
-TABLE **find_temporary_table(THD *thd, const char *db, const char *table_name)
+TABLE *find_temporary_table(THD *thd, const char *db, const char *table_name)
{
- char key[MAX_DBKEY_LENGTH];
- uint key_length= (uint) (strmov(strmov(key,db)+1,table_name)-key)+1;
- TABLE *table,**prev;
+ TABLE_LIST table_list;
- int4store(key+key_length,thd->server_id);
- key_length += 4;
- int4store(key+key_length,thd->variables.pseudo_thread_id);
- key_length += 4;
+ table_list.db= (char*) db;
+ table_list.table_name= (char*) table_name;
+ return find_temporary_table(thd, &table_list);
+}
- prev= &thd->temporary_tables;
- for (table=thd->temporary_tables ; table ; table=table->next)
+
+TABLE *find_temporary_table(THD *thd, TABLE_LIST *table_list)
+{
+ 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));
+
+ key_length= create_table_def_key(thd, key, table_list, 1);
+ for (table=thd->temporary_tables ; table ; table= table->next)
{
- if (table->s->key_length == key_length &&
- !memcmp(table->s->table_cache_key,key,key_length))
- return prev;
- prev= &table->next;
+ if (table->s->table_cache_key.length == key_length &&
+ !memcmp(table->s->table_cache_key.str, key, 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 0; // Not a temporary table
+ DBUG_RETURN(0); // Not a temporary table
}
@@ -1034,6 +1795,7 @@ TABLE **find_temporary_table(THD *thd, const char *db, const char *table_name)
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.
@@ -1048,30 +1810,121 @@ TABLE **find_temporary_table(THD *thd, const char *db, const char *table_name)
thd->temporary_tables list, it's impossible to tell here whether
we're dealing with an internal or a user temporary table.
- @retval TRUE the table was not found in the list of temporary tables
- of this thread
- @retval FALSE the table was found and dropped successfully.
+ @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
*/
-bool close_temporary_table(THD *thd, const char *db, const char *table_name)
+int drop_temporary_table(THD *thd, TABLE_LIST *table_list)
{
- TABLE *table,**prev;
+ TABLE *table;
+ DBUG_ENTER("drop_temporary_table");
+ DBUG_PRINT("tmptable", ("closing table: '%s'.'%s'",
+ table_list->db, table_list->table_name));
+
+ if (!(table= find_temporary_table(thd, table_list)))
+ DBUG_RETURN(1);
+
+ /* Table might be in use by some outer statement. */
+ if (table->query_id && table->query_id != thd->query_id)
+ {
+ my_error(ER_CANT_REOPEN_TABLE, MYF(0), table->alias);
+ DBUG_RETURN(-1);
+ }
- if (!(prev=find_temporary_table(thd,db,table_name)))
- return 1;
- table= *prev;
- *prev= table->next;
/*
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);
- close_temporary(table, 1);
+ close_temporary_table(thd, table, 1, 1);
+ DBUG_RETURN(0);
+}
+
+/*
+ unlink from thd->temporary tables and close temporary table
+*/
+
+void close_temporary_table(THD *thd, TABLE *table,
+ bool free_share, bool delete_table)
+{
+ DBUG_ENTER("close_temporary_table");
+ DBUG_PRINT("tmptable", ("closing table: '%s'.'%s' 0x%lx alias: '%s'",
+ table->s->db.str, table->s->table_name.str,
+ (long) table, table->alias));
+
+ /*
+ 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;
+ if (table->prev->next)
+ table->next->prev= table->prev;
+ }
+ else
+ {
+ /* removing the item from the list */
+ DBUG_ASSERT(table == thd->temporary_tables);
+ /*
+ slave must reset its temporary list pointer to zero to exclude
+ passing non-zero value to end_slave via rli->save_temporary_tables
+ when no temp tables opened, see an invariant below.
+ */
+ thd->temporary_tables= table->next;
+ if (thd->temporary_tables)
+ table->next->prev= 0;
+ }
if (thd->slave_thread)
- --slave_open_temp_tables;
- return 0;
+ {
+ /* natural invariant of temporary_tables */
+ DBUG_ASSERT(slave_open_temp_tables || !thd->temporary_tables);
+ slave_open_temp_tables--;
+ }
+ close_temporary(table, free_share, delete_table);
+ DBUG_VOID_RETURN;
}
+
+/*
+ Close and delete a temporary table
+
+ NOTE
+ This dosn't unlink table from thd->temporary
+ If this is needed, use close_temporary_table()
+*/
+
+void close_temporary(TABLE *table, bool free_share, bool delete_table)
+{
+ handlerton *table_type= table->s->db_type();
+ DBUG_ENTER("close_temporary");
+ DBUG_PRINT("tmptable", ("closing table: '%s'.'%s'",
+ table->s->db.str, table->s->table_name.str));
+
+ free_io_cache(table);
+ closefrm(table, 0);
+ if (delete_table)
+ rm_temporary_table(table_type, table->s->path.str);
+ if (free_share)
+ {
+ free_table_share(table->s);
+ my_free((char*) table,MYF(0));
+ }
+ DBUG_VOID_RETURN;
+}
+
+
/*
Used by ALTER TABLE when the table is a temporary one. It changes something
only if the ALTER contained a RENAME clause (otherwise, table_name is the old
@@ -1084,22 +1937,19 @@ bool rename_temporary_table(THD* thd, TABLE *table, const char *db,
const char *table_name)
{
char *key;
+ uint key_length;
TABLE_SHARE *share= table->s;
+ TABLE_LIST table_list;
+ DBUG_ENTER("rename_temporary_table");
- if (!(key=(char*) alloc_root(&table->mem_root,
- (uint) strlen(db)+
- (uint) strlen(table_name)+6+4)))
- return 1; /* purecov: inspected */
- share->key_length= (uint)
- (strmov((char*) (share->table_name= strmov(share->table_cache_key= key,
- db)+1),
- table_name) - share->table_cache_key)+1;
- share->db= share->table_cache_key;
- int4store(key+share->key_length, thd->server_id);
- share->key_length+= 4;
- int4store(key+share->key_length, thd->variables.pseudo_thread_id);
- share->key_length+= 4;
- return 0;
+ if (!(key=(char*) alloc_root(&share->mem_root, MAX_DBKEY_LENGTH)))
+ DBUG_RETURN(1); /* purecov: inspected */
+
+ table_list.db= (char*) db;
+ table_list.table_name= (char*) table_name;
+ key_length= create_table_def_key(thd, key, &table_list, 1);
+ share->set_table_cache_key(key, key_length);
+ DBUG_RETURN(0);
}
@@ -1107,6 +1957,9 @@ bool rename_temporary_table(THD* thd, TABLE *table, const char *db,
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 */
@@ -1121,44 +1974,143 @@ static void relink_unused(TABLE *table)
}
-/*
- Remove all instances of table from the current open list
- Free all locks on tables that are done with LOCK TABLES
- */
+/**
+ Prepare an open merge table for close.
+
+ @param[in] thd thread context
+ @param[in] table table to prepare
+ @param[in,out] prev_pp pointer to pointer of previous table
+
+ @detail
+ If the table is a MERGE parent, just detach the children.
+ If the table is a MERGE child, close the parent (incl. detach).
+*/
+
+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.
+ */
+ 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;
+}
-TABLE *unlink_open_table(THD *thd, TABLE *list, TABLE *find)
+
+/**
+ 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).
+*/
+
+void unlink_open_table(THD *thd, TABLE *find, bool unlock)
{
char key[MAX_DBKEY_LENGTH];
- uint key_length= find->s->key_length;
- TABLE *start=list,**prev,*next;
- prev= &start;
+ 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, key_length);
- for (; list ; list=next)
+ 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; )
{
- next=list->next;
- if (list->s->key_length == key_length &&
- !memcmp(list->s->table_cache_key, key, key_length))
+ list= *prev;
+
+ if (list->s->table_cache_key.length == key_length &&
+ !memcmp(list->s->table_cache_key.str, key, key_length))
{
- if (thd->locked_tables)
- mysql_lock_remove(thd, thd->locked_tables, list, TRUE);
- VOID(hash_delete(&open_cache,(byte*) list)); // Close table
+ 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);
+
+ /* Remove table from open_tables list. */
+ *prev= list->next;
+ /* Close table. */
+ VOID(hash_delete(&open_cache,(uchar*) list)); // Close table
}
else
{
- *prev=list; // put in use list
+ /* Step to next entry in open_tables list. */
prev= &list->next;
}
}
- *prev=0;
+
// Notify any 'refresh' threads
broadcast_refresh();
- return start;
+ DBUG_VOID_RETURN;
}
/**
- @brief Auxiliary routine which closes and drops open table.
+ Auxiliary routine which closes and drops open table.
@param thd Thread handle
@param table TABLE object for table to be dropped
@@ -1180,51 +2132,101 @@ void drop_open_table(THD *thd, TABLE *table, const char *db_name,
const char *table_name)
{
if (table->s->tmp_table)
- close_temporary_table(thd, db_name, table_name);
+ close_temporary_table(thd, table, 1, 1);
else
{
- enum db_type table_type= table->s->db_type;
+ 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.
*/
- thd->open_tables= unlink_open_table(thd, thd->open_tables, table);
- quick_rm_table(table_type, db_name, table_name);
+ unlink_open_table(thd, table, FALSE);
+ quick_rm_table(table_type, db_name, table_name, 0);
VOID(pthread_mutex_unlock(&LOCK_open));
}
}
/*
- When we call the following function we must have a lock on
- LOCK_open ; This lock will be unlocked on 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_refresh(THD *thd)
+void wait_for_condition(THD *thd, pthread_mutex_t *mutex, pthread_cond_t *cond)
{
- DBUG_ENTER("wait_for_refresh");
- safe_mutex_assert_owner(&LOCK_open);
-
/* Wait until the current table is up to date */
const char *proc_info;
- thd->mysys_var->current_mutex= &LOCK_open;
- thd->mysys_var->current_cond= &COND_refresh;
+ thd->mysys_var->current_mutex= mutex;
+ thd->mysys_var->current_cond= cond;
proc_info=thd->proc_info;
- thd->proc_info="Waiting for table";
+ thd_proc_info(thd, "Waiting for table");
+ DBUG_ENTER("wait_for_condition");
if (!thd->killed)
- (void) pthread_cond_wait(&COND_refresh,&LOCK_open);
+ (void) pthread_cond_wait(cond, mutex);
- pthread_mutex_unlock(&LOCK_open); // Must be unlocked first
+ /*
+ 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);
pthread_mutex_lock(&thd->mysys_var->mutex);
thd->mysys_var->current_mutex= 0;
thd->mysys_var->current_cond= 0;
- thd->proc_info= proc_info;
+ 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);
+}
+
+
/*
Open table which is already name-locked by this thread.
@@ -1252,10 +2254,7 @@ bool reopen_name_locked_table(THD* thd, TABLE_LIST* table_list, bool link_in)
{
TABLE *table= table_list->table;
TABLE_SHARE *share;
- char *db= table_list->db;
char *table_name= table_list->table_name;
- char key[MAX_DBKEY_LENGTH];
- uint key_length;
TABLE orig_table;
DBUG_ENTER("reopen_name_locked_table");
@@ -1265,12 +2264,10 @@ bool reopen_name_locked_table(THD* thd, TABLE_LIST* table_list, bool link_in)
DBUG_RETURN(TRUE);
orig_table= *table;
- key_length=(uint) (strmov(strmov(key,db)+1,table_name)-key)+1;
- if (open_unireg_entry(thd, table, db, table_name, table_name, 0,
- thd->mem_root, 0) ||
- !(table->s->table_cache_key= memdup_root(&table->mem_root, (char*) key,
- key_length)))
+ 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);
/*
@@ -1284,8 +2281,6 @@ bool reopen_name_locked_table(THD* thd, TABLE_LIST* table_list, bool link_in)
}
share= table->s;
- share->db= share->table_cache_key;
- share->key_length=key_length;
/*
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
@@ -1295,7 +2290,6 @@ bool reopen_name_locked_table(THD* thd, TABLE_LIST* table_list, bool link_in)
before we will get table-level lock on this table.
*/
share->version=0;
- share->flush_version=0;
table->in_use = thd;
check_unused();
@@ -1318,20 +2312,18 @@ bool reopen_name_locked_table(THD* thd, TABLE_LIST* table_list, bool link_in)
table->const_table=0;
table->null_row= table->maybe_null= table->force_index= 0;
table->status=STATUS_NO_RECORD;
- table->keys_in_use_for_query= share->keys_in_use;
- table->used_keys= share->keys_for_keyread;
DBUG_RETURN(FALSE);
}
/**
- @brief Create and insert into table cache placeholder for table
- which will prevent its opening (or creation) (a.k.a lock
- table name).
+ 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
+ @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.
@@ -1341,6 +2333,7 @@ TABLE *table_cache_insert_placeholder(THD *thd, const char *key,
uint key_length)
{
TABLE *table;
+ TABLE_SHARE *share;
char *key_buff;
DBUG_ENTER("table_cache_insert_placeholder");
@@ -1353,22 +2346,20 @@ TABLE *table_cache_insert_placeholder(THD *thd, const char *key,
*/
if (!my_multi_malloc(MYF(MY_WME | MY_ZEROFILL),
&table, sizeof(*table),
+ &share, sizeof(*share),
&key_buff, key_length,
NULL))
DBUG_RETURN(NULL);
- table->s= &table->share_not_to_be_used;
- memcpy(key_buff, key, key_length);
- table->s->table_cache_key= key_buff;
- table->s->db= table->s->table_cache_key;
- table->s->table_name= table->s->table_cache_key + strlen(table->s->db) + 1;
- table->s->key_length= key_length;
+ 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;
+ table->locked_by_name=1;
- if (my_hash_insert(&open_cache, (byte*)table))
+ if (my_hash_insert(&open_cache, (uchar*)table))
{
- my_free((gptr) table, MYF(0));
+ my_free((uchar*) table, MYF(0));
DBUG_RETURN(NULL);
}
@@ -1377,61 +2368,64 @@ TABLE *table_cache_insert_placeholder(THD *thd, const char *key,
/**
- @brief Check if table cache contains an open placeholder for the
- table and if this placeholder was created by another thread.
-
- @param thd Thread context
- @param db Name of database for table in question
- @param table_name Table name
-
- @note The presence of open placeholder indicates that either some
- other thread is trying to create table in question and obtained
- an exclusive name-lock on it or that this table already exists
- and is being flushed at the moment.
-
- @note One should acquire LOCK_open mutex before calling this function.
-
- @note This function is a hack which was introduced in 5.0 only to
- minimize code changes. It doesn't present in 5.1.
-
- @retval TRUE Table cache contains open placeholder for the table
- which was created by some other thread.
- @retval FALSE Otherwise.
+ 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.
*/
-bool table_cache_has_open_placeholder(THD *thd, const char *db,
- const char *table_name)
+bool lock_table_name_if_not_cached(THD *thd, const char *db,
+ const char *table_name, TABLE **table)
{
char key[MAX_DBKEY_LENGTH];
uint key_length;
- HASH_SEARCH_STATE state;
- TABLE *search;
- DBUG_ENTER("table_cache_has_open_placeholder");
+ DBUG_ENTER("lock_table_name_if_not_cached");
- safe_mutex_assert_owner(&LOCK_open);
+ key_length= (uint)(strmov(strmov(key, db) + 1, table_name) - key) + 1;
+ VOID(pthread_mutex_lock(&LOCK_open));
- key_length=(uint) (strmov(strmov(key,db)+1,table_name)-key)+1;
- for (search= (TABLE*) hash_first(&open_cache, (byte*) key, key_length,
- &state);
- search ;
- search= (TABLE*) hash_next(&open_cache, (byte*) key, key_length,
- &state))
+ if (hash_search(&open_cache, (uchar *)key, key_length))
{
- if (search->in_use == thd)
- continue;
- if (search->open_placeholder)
- DBUG_RETURN(1);
+ VOID(pthread_mutex_unlock(&LOCK_open));
+ DBUG_PRINT("info", ("Table is cached, name-lock is not obtained"));
+ *table= 0;
+ DBUG_RETURN(FALSE);
}
- DBUG_RETURN(0);
+ if (!(*table= table_cache_insert_placeholder(thd, key, key_length)))
+ {
+ VOID(pthread_mutex_unlock(&LOCK_open));
+ DBUG_RETURN(TRUE);
+ }
+ (*table)->open_placeholder= 1;
+ (*table)->next= thd->open_tables;
+ thd->open_tables= *table;
+ VOID(pthread_mutex_unlock(&LOCK_open));
+ DBUG_RETURN(FALSE);
}
/**
- @brief Check that table exists on disk or in some storage engine.
+ 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 exists[out] Out parameter which is set to TRUE if table
+ @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.
@note This function assumes that caller owns LOCK_open mutex.
@@ -1456,7 +2450,11 @@ bool check_if_table_exists(THD *thd, TABLE_LIST *table, bool *exists)
*exists= TRUE;
- build_table_path(path, sizeof(path), table->db, table->table_name, reg_ext);
+ 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);
@@ -1528,20 +2526,22 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root,
HASH_SEARCH_STATE state;
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, (char *)&alias))
+ if (check_stack_overrun(thd, STACK_MIN_SIZE_FOR_OPEN, (uchar *)&alias))
DBUG_RETURN(0);
if (thd->killed)
DBUG_RETURN(0);
- key_length= (uint) (strmov(strmov(key, table_list->db)+1,
- table_list->table_name)-key)+1;
- int4store(key + key_length, thd->server_id);
- int4store(key + key_length + 4, thd->variables.pseudo_thread_id);
+
+ key_length= (create_table_def_key(thd, key, table_list, 1) -
+ TMP_TABLE_KEY_EXTRA);
/*
Unless requested otherwise, try to resolve this table in the list
@@ -1554,8 +2554,9 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root,
{
for (table= thd->temporary_tables; table ; table=table->next)
{
- if (table->s->key_length == key_length + TMP_TABLE_KEY_EXTRA &&
- !memcmp(table->s->table_cache_key, key,
+ if (table->s->table_cache_key.length == key_length +
+ TMP_TABLE_KEY_EXTRA &&
+ !memcmp(table->s->table_cache_key.str, key,
key_length + TMP_TABLE_KEY_EXTRA))
{
/*
@@ -1564,15 +2565,17 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root,
is always represented by only one TABLE object in THD, and
it can not be cloned. Emit an error for an unsupported behaviour.
*/
- if (table->query_id == thd->query_id ||
- thd->prelocked_mode && table->query_id)
+ if (table->query_id)
{
+ DBUG_PRINT("error",
+ ("query_id: %lu server_id: %u pseudo_thread_id: %lu",
+ (ulong) table->query_id, (uint) thd->server_id,
+ (ulong) thd->variables.pseudo_thread_id));
my_error(ER_CANT_REOPEN_TABLE, MYF(0), table->alias);
DBUG_RETURN(0);
}
table->query_id= thd->query_id;
- table->clear_query_id= 1;
- thd->tmp_table_used= 1;
+ thd->thread_specific_used= TRUE;
DBUG_PRINT("info",("Using temporary table"));
goto reset;
}
@@ -1601,8 +2604,8 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root,
(int) TL_WRITE_ALLOW_WRITE);
for (table=thd->open_tables; table ; table=table->next)
{
- if (table->s->key_length == key_length &&
- !memcmp(table->s->table_cache_key, key, key_length))
+ if (table->s->table_cache_key.length == key_length &&
+ !memcmp(table->s->table_cache_key.str, key, key_length))
{
if (check_if_used && table->query_id &&
table->query_id != thd->query_id)
@@ -1614,12 +2617,17 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root,
is not already open by some calling stamement.
*/
my_error(ER_CANT_UPDATE_USED_TABLE_IN_SF_OR_TRG, MYF(0),
- table->s->table_name);
+ table->s->table_name.str);
DBUG_RETURN(0);
}
+ /*
+ 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, alias) &&
table->query_id != thd->query_id && /* skip tables already used */
- !(thd->prelocked_mode && table->query_id))
+ !(thd->prelocked_mode && table->query_id) &&
+ !table->parent)
{
int distance= ((int) table->reginfo.lock_type -
(int) table_list->lock_type);
@@ -1668,10 +2676,9 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root,
*/
{
char path[FN_REFLEN];
- db_type not_used;
- strxnmov(path, FN_REFLEN, mysql_data_home, "/", table_list->db, "/",
- table_list->table_name, reg_ext, NullS);
- (void) unpack_filename(path, path);
+ 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)
{
/*
@@ -1681,9 +2688,8 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root,
TABLE tab;
table= &tab;
VOID(pthread_mutex_lock(&LOCK_open));
- if (!open_unireg_entry(thd, table, table_list->db,
- table_list->table_name,
- alias, table_list, mem_root, 0))
+ if (!open_unireg_entry(thd, table, table_list, alias,
+ key, key_length, mem_root, 0))
{
DBUG_ASSERT(table_list->view != 0);
VOID(pthread_mutex_unlock(&LOCK_open));
@@ -1752,7 +2758,7 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root,
deadlock may occur.
*/
if (thd->handler_tables)
- mysql_ha_flush(thd, (TABLE_LIST*) NULL, MYSQL_HA_REOPEN_ON_USAGE, TRUE);
+ mysql_ha_flush(thd);
/*
Actually try to find the table in the open_cache.
@@ -1764,13 +2770,16 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root,
an implicit "pending locks queue" - see
wait_for_locked_table_names for details.
*/
- for (table= (TABLE*) hash_first(&open_cache, (byte*) key, key_length,
+ for (table= (TABLE*) hash_first(&open_cache, (uchar*) key, key_length,
&state);
table && table->in_use ;
- table= (TABLE*) hash_next(&open_cache, (byte*) key, key_length,
+ table= (TABLE*) hash_next(&open_cache, (uchar*) key, key_length,
&state))
{
+ 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.
@@ -1792,6 +2801,7 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root,
DBUG_PRINT("note",
("Found table '%s.%s' with different refresh version",
table_list->db, table_list->table_name));
+
if (flags & MYSQL_LOCK_IGNORE_FLUSH)
{
/* Force close at once after usage */
@@ -1803,7 +2813,7 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root,
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);
+ my_error(ER_UPDATE_TABLE_USED, MYF(0), table->s->table_name.str);
DBUG_RETURN(0);
}
@@ -1838,8 +2848,8 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root,
*/
if (table->in_use != thd)
{
- wait_for_refresh(thd);
- /* wait_for_refresh will unlock LOCK_open for us */
+ /* wait_for_conditionwill unlock LOCK_open for us */
+ wait_for_condition(thd, &LOCK_open, &COND_refresh);
}
else
{
@@ -1856,6 +2866,8 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root,
}
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
@@ -1870,11 +2882,11 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root,
else
{
/* Insert a new TABLE instance into the open cache */
- TABLE_SHARE *share;
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,(byte*) unused_tables)); /* purecov: tested */
+ VOID(hash_delete(&open_cache,(uchar*) unused_tables)); /* purecov: tested */
if (table_list->create)
{
@@ -1916,18 +2928,12 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root,
VOID(pthread_mutex_unlock(&LOCK_open));
DBUG_RETURN(NULL);
}
- error= open_unireg_entry(thd, table, table_list->db,
- table_list->table_name,
- alias, table_list, mem_root,
- (flags & OPEN_VIEW_NO_PARSE));
- if ((error > 0) ||
- (!table_list->view && !error &&
- !(table->s->table_cache_key= memdup_root(&table->mem_root,
- (char*) key,
- key_length))))
- {
- table->next=table->prev=table;
- free_cache_entry(table);
+
+ error= open_unireg_entry(thd, table, table_list, alias, key, key_length,
+ mem_root, (flags & OPEN_VIEW_NO_PARSE));
+ if (error > 0)
+ {
+ my_free((uchar*)table, MYF(0));
VOID(pthread_mutex_unlock(&LOCK_open));
DBUG_RETURN(NULL);
}
@@ -1940,17 +2946,14 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root,
if (error < 0)
table_list->view= (st_lex*)1;
- my_free((gptr)table, MYF(0));
+ my_free((uchar*)table, MYF(0));
VOID(pthread_mutex_unlock(&LOCK_open));
DBUG_RETURN(0); // VIEW
}
- share= table->s;
- share->db= share->table_cache_key;
- share->key_length= key_length;
- share->version= refresh_version;
- share->flush_version= flush_version;
- DBUG_PRINT("info", ("inserting table %p into the cache", table));
- VOID(my_hash_insert(&open_cache,(byte*) table));
+ DBUG_PRINT("info", ("inserting table '%s'.'%s' 0x%lx into the cache",
+ table->s->db.str, table->s->table_name.str,
+ (long) table));
+ VOID(my_hash_insert(&open_cache,(uchar*) table));
}
check_unused(); // Debugging call
@@ -1964,9 +2967,11 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root,
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, alias);
+ table->s->table_name.str, alias);
/* Fix alias if table name changes */
if (strcmp(table->alias, alias))
{
@@ -1981,9 +2986,7 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root,
table->const_table=0;
table->null_row= table->maybe_null= table->force_index= 0;
table->status=STATUS_NO_RECORD;
- table->keys_in_use_for_query= table->s->keys_in_use;
table->insert_values= 0;
- table->used_keys= table->s->keys_for_keyread;
table->fulltext_searched= 0;
table->file->ft_handler= 0;
/* Catch wrong handling of the auto_increment_field_not_null. */
@@ -1993,6 +2996,7 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root,
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();
DBUG_ASSERT(table->key_read == 0);
DBUG_RETURN(table);
}
@@ -2005,63 +3009,65 @@ TABLE *find_locked_table(THD *thd, const char *db,const char *table_name)
for (TABLE *table=thd->open_tables; table ; table=table->next)
{
- if (table->s->key_length == key_length &&
- !memcmp(table->s->table_cache_key,key,key_length))
+ if (table->s->table_cache_key.length == key_length &&
+ !memcmp(table->s->table_cache_key.str, key, key_length))
return table;
}
return(0);
}
-/****************************************************************************
- Reopen an table because the definition has changed. The date file for the
- table is already closed.
+/*
+ Reopen an table because the definition has changed.
SYNOPSIS
reopen_table()
- table Table to be opened
- locked 1 if we have already a lock on LOCK_open
+ table Table object
NOTES
- table->query_id will be 0 if table was reopened
+ 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 ('table' is unchanged if table couldn't be reopened)
-****************************************************************************/
+ RETURN
+ 0 ok
+ 1 error. The old table object is not changed.
+*/
-bool reopen_table(TABLE *table,bool locked)
+bool reopen_table(TABLE *table)
{
TABLE tmp;
- char *db= table->s->table_cache_key;
- const char *table_name= table->s->table_name;
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' 0x%lx", table->s->db.str,
+ table->s->table_name.str, (long) table));
+
+ 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);
#endif
- if (!locked)
- VOID(pthread_mutex_lock(&LOCK_open));
- safe_mutex_assert_owner(&LOCK_open);
-
- if (open_unireg_entry(table->in_use, &tmp, db, table_name,
- table->alias, 0, table->in_use->mem_root, 0))
+ 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,
+ table->s->table_cache_key.str,
+ table->s->table_cache_key.length,
+ thd->mem_root, 0))
goto end;
- free_io_cache(table);
-
- if (!(tmp.s->table_cache_key= memdup_root(&tmp.mem_root,db,
- table->s->key_length)))
- {
- delete tmp.triggers;
- closefrm(&tmp); // End of memory
- goto end;
- }
- tmp.s->db= tmp.s->table_cache_key;
/* This list copies variables set by open_table */
tmp.tablenr= table->tablenr;
@@ -2070,28 +3076,36 @@ bool reopen_table(TABLE *table,bool locked)
tmp.null_row= table->null_row;
tmp.maybe_null= table->maybe_null;
tmp.status= table->status;
- tmp.keys_in_use_for_query= tmp.s->keys_in_use;
- tmp.used_keys= tmp.s->keys_for_keyread;
+
+ tmp.s->table_map_id= table->s->table_map_id;
/* Get state */
- tmp.s->key_length= table->s->key_length;
- tmp.in_use= table->in_use;
+ tmp.in_use= thd;
tmp.reginfo.lock_type=table->reginfo.lock_type;
- tmp.s->version= refresh_version;
- tmp.s->tmp_table= table->s->tmp_table;
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)); // close file, free everything
+ VOID(closefrm(table, 1)); // close file, free everything
*table= tmp;
- table->s= &table->share_not_to_be_used;
- table->file->change_table_ptr(table);
+ table->default_column_bitmaps();
+ table->file->change_table_ptr(table, table->s);
DBUG_ASSERT(table->alias != 0);
for (field=table->field ; *field ; field++)
@@ -2109,47 +3123,153 @@ bool reopen_table(TABLE *table,bool locked)
}
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:
- if (!locked)
- VOID(pthread_mutex_unlock(&LOCK_open));
DBUG_RETURN(error);
}
-/*
- Used with ALTER TABLE:
- Close all instanses of table when LOCK TABLES is in used;
- Close first all instances of table and then reopen them
- */
+/**
+ Close all instances of a table open by this thread and replace
+ them with exclusive name-locks.
-bool close_data_tables(THD *thd,const char *db, const char *table_name)
+ @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.
+*/
+
+void close_data_files_and_morph_locks(THD *thd, const char *db,
+ const char *table_name)
{
TABLE *table;
+ DBUG_ENTER("close_data_files_and_morph_locks");
+
+ safe_mutex_assert_owner(&LOCK_open);
+
+ if (thd->lock)
+ {
+ /*
+ 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;
+ }
+
+ /*
+ 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.
+ */
for (table=thd->open_tables; table ; table=table->next)
{
- if (!strcmp(table->s->table_name, table_name) &&
- !strcmp(table->s->db, db))
+ 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);
+ }
+ }
+ DBUG_VOID_RETURN;
+}
+
+
+/**
+ Reattach MERGE children after reopen.
+
+ @param[in] thd thread context
+ @param[in,out] err_tables_p pointer to pointer of tables in error
+
+ @return status
+ @retval FALSE OK, err_tables_p unchanged
+ @retval TRUE Error, err_tables_p contains table(s)
+*/
+
+static bool reattach_merge(THD *thd, TABLE **err_tables_p)
+{
+ TABLE *table;
+ TABLE *next;
+ TABLE **prv_p= &thd->open_tables;
+ bool error= FALSE;
+ DBUG_ENTER("reattach_merge");
+
+ for (table= thd->open_tables; table; table= next)
+ {
+ next= table->next;
+ DBUG_PRINT("tcache", ("check table: '%s'.'%s' 0x%lx next: 0x%lx",
+ table->s->db.str, table->s->table_name.str,
+ (long) table, (long) next));
+ /* Reattach children for MERGE tables with "closed data files" only. */
+ if (table->child_l && !table->children_attached)
{
- mysql_lock_remove(thd, thd->locked_tables, table, TRUE);
- table->file->close();
- table->db_stat=0;
+ 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);
+ error= TRUE;
+ /* Remove table from open_tables. */
+ *prv_p= next;
+ if (next)
+ prv_p= &next->next;
+ /* Stack table on error list. */
+ table->next= *err_tables_p;
+ *err_tables_p= table;
+ continue;
+ }
+ 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));
+ }
}
+ prv_p= &table->next;
}
- return 0; // For the future
+ DBUG_RETURN(error);
}
/**
- @brief Reopen all tables with closed data files.
+ Reopen all tables with closed data files.
@param thd Thread context
@param get_locks Should we get locks after reopening tables ?
- @param in_refresh Are we in FLUSH TABLES ? TODO: It seems that
- we can remove this parameter.
+ @param mark_share_as_old Mark share as old to protect from a impending
+ global read lock.
@note Since this function can't properly handle prelocking and
create placeholders it should be used in very special
@@ -2163,22 +3283,34 @@ bool close_data_tables(THD *thd,const char *db, const char *table_name)
@return FALSE in case of success, TRUE - otherwise.
*/
-bool reopen_tables(THD *thd,bool get_locks,bool in_refresh)
+bool reopen_tables(THD *thd, bool get_locks, bool mark_share_as_old)
{
+ TABLE *table,*next,**prev;
+ TABLE **tables,**tables_ptr; // For locks
+ TABLE *err_tables= NULL;
+ 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");
- safe_mutex_assert_owner(&LOCK_open);
if (!thd->open_tables)
DBUG_RETURN(0);
- TABLE *table,*next,**prev;
- TABLE **tables,**tables_ptr; // For locks
- bool error=0, not_used;
+ safe_mutex_assert_owner(&LOCK_open);
if (get_locks)
{
- /* The ptr is checked later */
+ /*
+ The ptr is checked later
+ Do not handle locks of MERGE children.
+ */
uint opens=0;
- for (table=thd->open_tables; table ; table=table->next) opens++;
+ 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
@@ -2190,51 +3322,98 @@ bool reopen_tables(THD *thd,bool get_locks,bool in_refresh)
{
uint db_stat=table->db_stat;
next=table->next;
- if (!tables || (!db_stat && reopen_table(table,1)))
+ 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 (table->child_l && !db_stat)
+ merge_table_found= TRUE;
+ if (!tables || (!db_stat && reopen_table(table)))
{
my_error(ER_CANT_REOPEN_TABLE, MYF(0), table->alias);
- VOID(hash_delete(&open_cache,(byte*) table));
+ /*
+ If we could not allocate 'tables', we may close open tables
+ here. If a MERGE table is affected, 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.
+ */
+ if (table->child_l || table->parent)
+ detach_merge_children(table, TRUE);
+ VOID(hash_delete(&open_cache,(uchar*) table));
error=1;
}
else
{
+ DBUG_PRINT("tcache", ("opened. need lock: %d",
+ get_locks && !db_stat && !table->parent));
*prev= table;
prev= &table->next;
- if (get_locks && !db_stat)
+ /* Do not handle locks of MERGE children. */
+ if (get_locks && !db_stat && !table->parent)
*tables_ptr++= table; // need new lock on this
- if (in_refresh)
+ if (mark_share_as_old)
{
table->s->version=0;
table->open_placeholder= 0;
}
}
}
+ *prev=0;
+ /*
+ When all tables are open again, we can re-attach MERGE children to
+ their parents. All TABLE objects are still present.
+ */
+ DBUG_PRINT("tcache", ("re-attaching MERGE tables: %d", merge_table_found));
+ if (!error && merge_table_found && reattach_merge(thd, &err_tables))
+ {
+ while (err_tables)
+ {
+ VOID(hash_delete(&open_cache, (uchar*) err_tables));
+ err_tables= err_tables->next;
+ }
+ }
+ 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;
- /* We should always get these locks */
+ /*
+ 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.
+ */
thd->some_tables_deleted=0;
if ((lock= mysql_lock_tables(thd, tables, (uint) (tables_ptr - tables),
- 0, &not_used)))
+ 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((gptr) tables);
+ my_afree((uchar*) tables);
}
broadcast_refresh();
- *prev=0;
DBUG_RETURN(error);
}
/**
- @brief Close handlers for tables in list, but leave the TABLE structure
- intact so that we can re-open these quickly.
+ 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
@@ -2246,45 +3425,66 @@ bool reopen_tables(THD *thd,bool get_locks,bool in_refresh)
@param send_refresh Should we awake waiters even if we didn't close any tables?
*/
-void close_old_data_files(THD *thd, TABLE *table, bool morph_locks,
- bool send_refresh)
+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");
- bool found=send_refresh;
+
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;
- /*
- Note that it is safe to update version even for open placeholders
- as later in this function we reset TABLE::open_placeholder and thus
- effectively remove them from the table cache.
- */
- if (!morph_locks) // If not from flush tables
- table->s->version= refresh_version; // Let other threads use table
if (table->db_stat)
{
- if (morph_locks)
- {
+ if (morph_locks)
+ {
/*
- 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.
+ Forward lock handling to MERGE parent. But unlock parent
+ once only.
*/
- mysql_lock_abort(thd, table);
- mysql_lock_remove(thd, thd->locked_tables, table, TRUE);
+ TABLE *ulcktbl= table->parent ? table->parent : table;
+ if (ulcktbl->lock_count)
+ {
+ /*
+ 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;
- }
- table->file->close();
- table->db_stat=0;
+ table->open_placeholder= 1;
+ }
+ close_handle_and_leave_table_as_lock(table);
}
- else if (table->open_placeholder)
+ else if (table->open_placeholder && !morph_locks)
{
/*
We come here only in close-for-back-off scenario. So we have to
@@ -2292,8 +3492,11 @@ void close_old_data_files(THD *thd, TABLE *table, bool morph_locks,
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.
*/
- DBUG_ASSERT(!morph_locks);
table->open_placeholder= 0;
}
}
@@ -2312,23 +3515,42 @@ void close_old_data_files(THD *thd, TABLE *table, bool morph_locks,
bool table_is_used(TABLE *table, bool wait_for_name_lock)
{
+ DBUG_ENTER("table_is_used");
do
{
- char *key= table->s->table_cache_key;
- uint key_length= table->s->key_length;
+ char *key= table->s->table_cache_key.str;
+ uint key_length= table->s->table_cache_key.length;
+
+ DBUG_PRINT("loop", ("table_name: %s", table->alias));
HASH_SEARCH_STATE state;
- for (TABLE *search= (TABLE*) hash_first(&open_cache, (byte*) key,
+ for (TABLE *search= (TABLE*) hash_first(&open_cache, (uchar*) key,
key_length, &state);
search ;
- search= (TABLE*) hash_next(&open_cache, (byte*) key,
+ search= (TABLE*) hash_next(&open_cache, (uchar*) key,
key_length, &state))
{
- if (search->locked_by_name && wait_for_name_lock ||
- search->is_name_opened() && search->needs_reopen_or_name_lock())
- return 1; // Table is used
+ 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));
- return 0;
+ DBUG_RETURN(0);
}
@@ -2339,13 +3561,13 @@ bool wait_for_tables(THD *thd)
bool result;
DBUG_ENTER("wait_for_tables");
- thd->proc_info="Waiting for tables";
+ 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, (TABLE_LIST*) NULL, MYSQL_HA_REOPEN_ON_USAGE, TRUE);
+ mysql_ha_flush(thd);
if (!table_is_used(thd->open_tables,1))
break;
(void) pthread_cond_wait(&COND_refresh,&LOCK_open);
@@ -2355,32 +3577,84 @@ bool wait_for_tables(THD *thd)
else
{
/* Now we can open all tables without any interference */
- thd->proc_info="Reopen tables";
+ thd_proc_info(thd, "Reopen tables");
thd->version= refresh_version;
result=reopen_tables(thd,0,0);
}
pthread_mutex_unlock(&LOCK_open);
- thd->proc_info=0;
+ thd_proc_info(thd, 0);
DBUG_RETURN(result);
}
-/* drop tables from locked list */
+/*
+ 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
+*/
+
-bool drop_locked_tables(THD *thd,const char *db, const char *table_name)
+TABLE *drop_locked_tables(THD *thd,const char *db, const char *table_name)
{
- TABLE *table,*next,**prev;
- bool found=0;
+ TABLE *table,*next,**prev, *found= 0;
prev= &thd->open_tables;
+ DBUG_ENTER("drop_locked_tables");
+
+ /*
+ 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 (table= thd->open_tables; table ; table=next)
{
next=table->next;
- if (!strcmp(table->s->table_name, table_name) &&
- !strcmp(table->s->db, db))
+ if (!strcmp(table->s->table_name.str, table_name) &&
+ !strcmp(table->s->db.str, db))
{
- mysql_lock_remove(thd, thd->locked_tables, table, TRUE);
- VOID(hash_delete(&open_cache,(byte*) table));
- found=1;
+ /* 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.
+ */
+ if (table->child_l || table->parent)
+ detach_merge_children(table, TRUE);
+
+ 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->close();
+ }
+ }
+ else
+ {
+ /* We already have a name lock, remove copy */
+ VOID(hash_delete(&open_cache,(uchar*) table));
+ }
}
else
{
@@ -2393,10 +3667,10 @@ bool drop_locked_tables(THD *thd,const char *db, const char *table_name)
broadcast_refresh();
if (thd->locked_tables && thd->locked_tables->table_count == 0)
{
- my_free((gptr) thd->locked_tables,MYF(0));
+ my_free((uchar*) thd->locked_tables,MYF(0));
thd->locked_tables=0;
}
- return found;
+ DBUG_RETURN(found);
}
@@ -2411,10 +3685,11 @@ void abort_locked_tables(THD *thd,const char *db, const char *table_name)
TABLE *table;
for (table= thd->open_tables; table ; table= table->next)
{
- if (!strcmp(table->s->table_name,table_name) &&
- !strcmp(table->s->db, db))
+ if (!strcmp(table->s->table_name.str, table_name) &&
+ !strcmp(table->s->db.str, db))
{
- mysql_lock_abort(thd,table);
+ /* If MERGE child, forward lock handling to parent. */
+ mysql_lock_abort(thd, table->parent ? table->parent : table, TRUE);
break;
}
}
@@ -2422,146 +3697,323 @@ void abort_locked_tables(THD *thd,const char *db, const char *table_name)
/*
+ Function to assign a new table map id to a table share.
+
+ PARAMETERS
+
+ share - Pointer to table share structure
+
+ DESCRIPTION
+
+ We are intentionally not checking that share->mutex is locked
+ since this function should only be called when opening a table
+ share and before it is entered into the table_def_cache (meaning
+ that it cannot be fetched by another thread, even accidentally).
+
+ PRE-CONDITION(S)
+
+ share is non-NULL
+ The LOCK_open mutex is locked
+
+ POST-CONDITION(S)
+
+ share->table_map_id is given a value that with a high certainty is
+ not used by any other table (the only case where a table id can be
+ reused is on wrap-around, which means more than 4 billion table
+ share opens have been executed while one table was open all the
+ time).
+
+ share->table_map_id is not ~0UL.
+ */
+static ulong last_table_id= ~0UL;
+
+void assign_new_table_id(TABLE_SHARE *share)
+{
+
+ DBUG_ENTER("assign_new_table_id");
+
+ /* Preconditions */
+ DBUG_ASSERT(share != NULL);
+ safe_mutex_assert_owner(&LOCK_open);
+
+ ulong tid= ++last_table_id; /* get next id */
+ /*
+ There is one reserved number that cannot be used. Remember to
+ change this when 6-byte global table id's are introduced.
+ */
+ if (unlikely(tid == ~0UL))
+ tid= ++last_table_id;
+ share->table_map_id= tid;
+ DBUG_PRINT("info", ("table_id=%lu", tid));
+
+ /* Post conditions */
+ DBUG_ASSERT(share->table_map_id != ~0UL);
+
+ DBUG_VOID_RETURN;
+}
+
+#ifndef DBUG_OFF
+/* Cause a spurious statement reprepare for debug purposes. */
+static bool inject_reprepare(THD *thd)
+{
+ if (thd->m_reprepare_observer && thd->stmt_arena->is_reprepared == FALSE)
+ {
+ thd->m_reprepare_observer->report_error(thd);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+#endif
+
+/**
+ Compare metadata versions of an element obtained from the table
+ definition cache and its corresponding node in the parse tree.
+
+ @details If the new and the old values mismatch, invoke
+ Metadata_version_observer.
+ At prepared statement prepare, all TABLE_LIST 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 the parse tree, 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.
+
+ @sa Execute_observer
+ @sa check_prepared_statement() to see cases when an observer is installed
+ @sa TABLE_LIST::is_table_ref_id_equal()
+ @sa TABLE_SHARE::get_table_ref_id()
+
+ @param[in] thd used to report errors
+ @param[in,out] tables TABLE_LIST instance created by the parser
+ Metadata version information in this object
+ is updated upon success.
+ @param[in] table_share an element from the table definition cache
+
+ @retval TRUE an error, which has been reported
+ @retval FALSE success, version in TABLE_LIST has been updated
+*/
+
+bool
+check_and_update_table_version(THD *thd,
+ TABLE_LIST *tables, TABLE_SHARE *table_share)
+{
+ if (! tables->is_table_ref_id_equal(table_share))
+ {
+ if (thd->m_reprepare_observer &&
+ thd->m_reprepare_observer->report_error(thd))
+ {
+ /*
+ Version of the table share 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 version and type */
+ tables->set_table_ref_id(table_share);
+ }
+
+ DBUG_EXECUTE_IF("reprepare_each_statement", return inject_reprepare(thd););
+
+ 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
- db Database name
- name Table name
+ table_list TABLE_LIST with db, table_name & belong_to_view
alias Alias name
- table_desc TABLE_LIST descriptor (used with views)
+ 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()
NOTES
Extra argument for open is taken from thd->open_options
+ One must have a lock on LOCK_open when calling this function
RETURN
0 ok
# Error
*/
-static int open_unireg_entry(THD *thd, TABLE *entry, const char *db,
- const char *name, const char *alias,
- TABLE_LIST *table_desc, MEM_ROOT *mem_root,
- uint flags)
+
+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)
{
- char path[FN_REFLEN];
int error;
+ TABLE_SHARE *share;
uint discover_retry_count= 0;
DBUG_ENTER("open_unireg_entry");
- strxmov(path, mysql_data_home, "/", db, "/", name, NullS);
- while ((error= openfrm(thd, path, alias,
- (uint) (HA_OPEN_KEYFILE | HA_OPEN_RNDFILE |
- HA_GET_INDEX | HA_TRY_READ_ONLY |
- NO_ERR_ON_NEW_FRM),
- READ_KEYINFO | COMPUTE_TYPES | EXTRA_RECORD |
- (flags & OPEN_VIEW_NO_PARSE),
- thd->open_options, entry)) &&
- (error != 5 ||
- (fn_format(path, path, 0, reg_ext, MY_UNPACK_FILENAME),
- open_new_frm(thd, path, alias, db, name,
- (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, entry, table_desc, mem_root))))
+ 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);
+ if (share->is_view)
{
- if (!entry->s || !entry->s->crashed)
+ /*
+ 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;
+
+ /* 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, 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)
+ {
+ /*
+ 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;
+ }
+
+ if (table_list->i_s_requested_object & OPEN_VIEW_ONLY)
+ goto err;
+
+ 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, entry, FALSE)))
+ {
+ if (error == 7) // Table def changed
{
+ share->version= 0; // Mark share as old
+ if (discover_retry_count++) // Retry once
+ goto err;
+
/*
- Frm file could not be found on disk
- Since it does not exist, no one can be using it
- LOCK_open has been locked to protect from someone else
- trying to discover the table at the same time.
+ 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 (discover_retry_count++ != 0)
+ if (ha_create_table_from_engine(thd, table_list->db,
+ table_list->table_name))
goto err;
- if (ha_create_table_from_engine(thd, db, name) > 0)
- {
- /* Give right error message */
- thd->clear_error();
- DBUG_PRINT("error", ("Discovery of %s/%s failed", db, name));
- my_printf_error(ER_UNKNOWN_ERROR,
- "Failed to open '%-.64s', error while "
- "unpacking from engine",
- MYF(0), name);
-
+ /*
+ 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;
- }
-
- mysql_reset_errors(thd, 1); // Clear warnings
- thd->clear_error(); // Clear error message
- continue;
- }
-
- // Code below is for repairing a crashed file
- TABLE_LIST table_list;
- bzero((char*) &table_list, sizeof(table_list)); // just for safe
- table_list.db=(char*) db;
- table_list.table_name=(char*) name;
-
- safe_mutex_assert_owner(&LOCK_open);
-
- if ((error=lock_table_name(thd,&table_list)))
- {
- if (error < 0)
- {
- goto err;
- }
- if (wait_for_locked_table_names(thd,&table_list))
+ /* Free share and wait until it's released by all threads */
+ release_table_share(share, RELEASE_WAIT_FOR_DROP);
+ if (!thd->killed)
{
- unlock_table_name(thd,&table_list);
- goto err;
+ mysql_reset_errors(thd, 1); // Clear warnings
+ thd->clear_error(); // Clear error message
+ goto retry;
}
+ DBUG_RETURN(1);
}
- pthread_mutex_unlock(&LOCK_open);
- thd->clear_error(); // Clear error message
- error= 0;
- if (openfrm(thd, path, 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) || ! entry->file ||
- (entry->file->is_crashed() && entry->file->check_and_repair(thd)))
- {
- /* Give right error message */
- thd->clear_error();
- my_error(ER_NOT_KEYFILE, MYF(0), name, my_errno);
- sql_print_error("Couldn't repair table: %s.%s",db,name);
- if (entry->file)
- closefrm(entry);
- error=1;
- }
- else
- thd->clear_error(); // Clear error message
- pthread_mutex_lock(&LOCK_open);
- unlock_table_name(thd,&table_list);
-
- if (error)
+ if (!entry->s || !entry->s->crashed)
goto err;
- break;
- }
-
- if (error == 5)
- DBUG_RETURN((flags & OPEN_VIEW_NO_PARSE)? -1 : 0); // we have just opened VIEW
-
- /*
- We can't mark all tables in 'mysql' database as system since we don't
- allow to lock such tables for writing with any other tables (even with
- other system tables) and some privilege tables need this.
- */
- if (!my_strcasecmp(system_charset_info, db, "mysql") &&
- !my_strcasecmp(system_charset_info, name, "proc"))
- entry->s->system_table= 1;
+ // Code below is for repairing a crashed file
+ if ((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, my_errno);
+ 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);
+ unlock_table_name(thd, table_list);
+
+ if (error)
+ goto err;
+ break;
+ }
- if (Table_triggers_list::check_n_load(thd, db, name, entry, 0))
+ if (Table_triggers_list::check_n_load(thd, share->db.str,
+ share->table_name.str, entry, 0))
+ {
+ closefrm(entry, 0);
goto err;
+ }
/*
If we are here, there was no fatal error (but error may be still
@@ -2573,13 +4025,14 @@ static int open_unireg_entry(THD *thd, TABLE *entry, const char *db,
if (mysql_bin_log.is_open())
{
char *query, *end;
- uint query_buf_size= 20 + 2*NAME_LEN + 1;
- if ((query= (char*)my_malloc(query_buf_size,MYF(MY_WME))))
+ uint query_buf_size= 20 + share->db.length + share->table_name.length +1;
+ if ((query= (char*) my_malloc(query_buf_size,MYF(MY_WME))))
{
+ /* this DELETE FROM is needed even with row-based binlogging */
end = strxmov(strmov(query, "DELETE FROM `"),
- db,"`.`",name,"`", NullS);
- Query_log_event qinfo(thd, query, (ulong)(end-query), 0, FALSE);
- mysql_bin_log.write(&qinfo);
+ share->db.str,"`.`",share->table_name.str,"`", NullS);
+ thd->binlog_query(THD::STMT_QUERY_TYPE,
+ query, (ulong)(end-query), FALSE, FALSE);
my_free(query, MYF(0));
}
else
@@ -2589,26 +4042,386 @@ static int open_unireg_entry(THD *thd, TABLE *entry, const char *db,
DBA on top of warning the client (which will automatically be done
because of MYF(MY_WME) in my_malloc() above).
*/
- sql_print_error("When opening HEAP table, could not allocate \
-memory to write 'DELETE FROM `%s`.`%s`' to the binary log",db,name);
+ sql_print_error("When opening HEAP table, could not allocate memory "
+ "to write 'DELETE FROM `%s`.`%s`' to the binary log",
+ table_list->db, table_list->table_name);
delete entry->triggers;
- if (entry->file)
- closefrm(entry);
+ closefrm(entry, 0);
goto err;
}
}
}
DBUG_RETURN(0);
+
err:
- /* Hide "Table doesn't exist" errors if table belong to view */
- if (thd->net.last_errno == ER_NO_SUCH_TABLE &&
- table_desc && table_desc->belong_to_view)
+ release_table_share(share, RELEASE_NORMAL);
+ DBUG_RETURN(1);
+}
+
+
+/**
+ @brief Add list of MERGE children to a TABLE_LIST list.
+
+ @param[in] tlist the parent TABLE_LIST object just opened
+
+ @return status
+ @retval 0 OK
+ @retval != 0 Error
+
+ @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.
+*/
+
+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));
+
+ /* 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)
+ {
+ my_error(ER_ADMIN_WRONG_MRG_TABLE, MYF(0), tlist->alias);
+ DBUG_RETURN(1);
+ }
+
+ /* Fix children.*/
+ for (child_l= parent->child_l; ; child_l= child_l->next_global)
{
- TABLE_LIST *view= table_desc->belong_to_view;
- thd->clear_error();
- my_error(ER_VIEW_INVALID, MYF(0), view->view_db.str, view->view_name.str);
+ /*
+ 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).
+ */
+
+ /* Set lock type. */
+ child_l->lock_type= tlist->lock_type;
+
+ /* Set parent reference. */
+ child_l->parent_l= tlist;
+
+ /* Break when this was the last child. */
+ if (&child_l->next_global == parent->child_last_l)
+ break;
}
- DBUG_RETURN(1);
+
+ /* Insert children into the table list. */
+ *parent->child_last_l= tlist->next_global;
+ tlist->next_global= parent->child_l;
+
+ /*
+ Do not fix the prev_global pointers. We will remove the
+ chain soon anyway.
+ */
+
+ DBUG_RETURN(0);
+}
+
+
+/**
+ @brief Attach MERGE children to the parent.
+
+ @param[in] tlist the child TABLE_LIST object just opened
+
+ @return status
+ @retval 0 OK
+ @retval != 0 Error
+
+ @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.
+*/
+
+static int attach_merge_children(TABLE_LIST *tlist)
+{
+ 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));
+
+ /* 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);
+
+ /* Attach MyISAM tables to MERGE table. */
+ error= parent->file->extra(HA_EXTRA_ATTACH_CHILDREN);
+
+ /*
+ Remove children from the table list. Even in case of an error.
+ This should prevent tampering with them.
+ */
+ tlist->parent_l->next_global= *parent->child_last_l;
+
+ /*
+ 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.
+ */
+
+ 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));
+
+ /*
+ Note that we have the cildren in the thd->open_tables list at this
+ point.
+ */
+
+ DBUG_RETURN(0);
+}
+
+
+/**
+ @brief Detach MERGE children from the parent.
+
+ @note
+ Call this before the first table of a MERGE table (parent or child)
+ is closed.
+
+ 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.
+
+ 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).
+
+ All references to the children should be removed on handler level
+ and optionally on table level.
+
+ @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()
+*/
+
+void detach_merge_children(TABLE *table, bool clear_refs)
+{
+ TABLE_LIST *child_l;
+ TABLE *parent= table->child_l ? table : table->parent;
+ bool first_detach;
+ DBUG_ENTER("detach_merge_children");
+ /*
+ Either table->child_l or table->parent must be set. Parent must have
+ child_l set.
+ */
+ 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));
+
+ /*
+ 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 ((first_detach= parent->children_attached))
+ {
+ 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));
+ }
+ else
+ DBUG_PRINT("myrg", ("parent is already detached"));
+
+ if (clear_refs)
+ {
+ /* In any case clear the own parent reference. (***) */
+ table->parent= NULL;
+
+ /*
+ On the first detach, 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.
+ */
+ if (first_detach || (table == parent))
+ {
+ /* Clear TABLE references to force new assignment at next open. */
+ for (child_l= parent->child_l; ; child_l= child_l->next_global)
+ {
+ /*
+ Do not DBUG_ASSERT(child_l->table); open_tables might be
+ incomplete.
+
+ Clear the parent reference of the children only on the first
+ detach. The children might already be closed. They will clear
+ it themseves when this function is called for them with
+ 'clear_refs' true. See above "(***)".
+ */
+ if (first_detach && child_l->table)
+ child_l->table->parent= NULL;
+
+ /* Clear the table reference to force new assignment at next open. */
+ child_l->table= NULL;
+
+ /* Break when this was the last child. */
+ if (&child_l->next_global == parent->child_last_l)
+ break;
+ }
+ }
+ }
+
+ DBUG_VOID_RETURN;
+}
+
+
+/**
+ @brief Fix MERGE children after open.
+
+ @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
+
+ @return mismatch
+ @retval FALSE OK, no mismatch
+ @retval TRUE Error, lists mismatch
+
+ @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.
+
+ Other action is to verify that the table definition with respect to
+ the UNION list did not change.
+
+ @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.
+
+ 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.
+
+ If a '*_last' pointer is NULL, the respective list is assumed to be
+ NULL terminated.
+*/
+
+bool fix_merge_after_open(TABLE_LIST *old_child_list, TABLE_LIST **old_last,
+ TABLE_LIST *new_child_list, TABLE_LIST **new_last)
+{
+ 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));
+
+ /* Terminate the lists for easier check of list end. */
+ if (old_last)
+ *old_last= NULL;
+ if (new_last)
+ *new_last= NULL;
+
+ for (;;)
+ {
+ 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.
+ */
+ 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));
+
+ DBUG_RETURN(mismatch);
+}
+
+
+/*
+ Return a appropriate read lock type given a table object.
+
+ @param thd Thread context
+ @param table TABLE object 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. 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().
+*/
+
+thr_lock_type read_lock_type_for_table(THD *thd, TABLE *table)
+{
+ 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->s->table_category == TABLE_CATEGORY_PERFORMANCE))
+ return TL_READ;
+ else
+ return TL_READ_NO_INSERT;
}
@@ -2635,6 +4448,11 @@ err:
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
@@ -2642,7 +4460,7 @@ err:
int open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags)
{
- TABLE_LIST *tables;
+ TABLE_LIST *tables= NULL;
bool refresh;
int result=0;
MEM_ROOT new_frm_mem;
@@ -2661,32 +4479,25 @@ int open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags)
restart:
*counter= 0;
query_tables_last_own= 0;
- thd->proc_info="Opening tables";
+ 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.
- NOTE: We will mark statement as requiring prelocking only if we will
- have non empty table list. But this does not guarantee that in prelocked
- mode we will have some locked tables, because queries which use only
- derived/information schema tables and views possible. Thus "counter"
- may be still zero for prelocked statement...
*/
if (!thd->prelocked_mode && !thd->lex->requires_prelocking() &&
thd->lex->uses_stored_routines())
{
- bool first_no_prelocking, need_prelocking, tabs_changed;
+ 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,
- &tabs_changed))
+ if (sp_cache_routines_and_add_tables(thd, thd->lex, first_no_prelocking))
{
/*
Serious error during reading stored routines from mysql.proc table.
@@ -2696,7 +4507,7 @@ int open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags)
result= -1;
goto err;
}
- else if ((tabs_changed || *start) && need_prelocking)
+ else if (need_prelocking)
{
query_tables_last_own= save_query_tables_last;
*start= thd->lex->query_tables;
@@ -2709,6 +4520,9 @@ int open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags)
*/
for (tables= *start; tables ;tables= tables->next_global)
{
+ DBUG_PRINT("tcache", ("opening table: '%s'.'%s' item: 0x%lx",
+ tables->db, tables->table_name, (long) tables));
+
safe_to_ignore_table= FALSE;
/*
@@ -2731,8 +4545,18 @@ int open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags)
*/
if (tables->schema_table)
{
- if (!mysql_schema_table(thd, thd->lex, tables))
+ /*
+ 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)++;
@@ -2760,6 +4584,10 @@ int open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags)
else
tables->table= open_table(thd, tables, &new_frm_mem, &refresh, flags);
}
+ else
+ DBUG_PRINT("tcache", ("referenced table: '%s'.'%s' 0x%lx",
+ tables->db, tables->table_name,
+ (long) tables->table));
if (!tables->table)
{
@@ -2791,6 +4619,19 @@ int open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags)
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.
+ */
+ if (tables->parent_l)
+ {
+ 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;
+ }
+
if (refresh) // Refresh in progress
{
/*
@@ -2834,7 +4675,7 @@ int open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags)
process its triggers since they never will be activated.
*/
if (!thd->prelocked_mode && !thd->lex->requires_prelocking() &&
- tables->table->triggers &&
+ tables->trg_event_map && tables->table->triggers &&
tables->lock_type >= TL_WRITE_ALLOW_WRITE)
{
if (!query_tables_last_own)
@@ -2858,11 +4699,39 @@ int open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags)
{
if (tables->lock_type == TL_WRITE_DEFAULT)
tables->table->reginfo.lock_type= thd->update_lock_default;
- else if (tables->table->s->tmp_table == NO_TMP_TABLE)
+ else if (tables->lock_type == TL_READ_DEFAULT)
+ tables->table->reginfo.lock_type=
+ read_lock_type_for_table(thd, tables->table);
+ else
tables->table->reginfo.lock_type= tables->lock_type;
}
tables->table->grant= tables->grant;
+ /* Check and update metadata version of a base table. */
+ if (check_and_update_table_version(thd, tables, tables->table->s))
+ {
+ 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
@@ -2889,12 +4758,28 @@ process_view_routines:
}
err:
- thd->proc_info=0;
+ thd_proc_info(thd, 0);
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 (result && tables)
+ {
+ /*
+ 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)
+ {
+ 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;
+ }
+ tables->table= NULL;
+ }
+ DBUG_PRINT("tcache", ("returning: %d", result));
DBUG_RETURN(result);
}
@@ -2934,6 +4819,63 @@ static bool check_lock_and_start_stmt(THD *thd, TABLE *table,
}
+/**
+ @brief Open and lock one 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
+
+ @return table
+ @retval != NULL OK, opened table returned
+ @retval NULL Error
+
+ @note
+ If ok, the following are also set:
+ table_list->lock_type lock_type
+ table_list->table table
+
+ @note
+ If table_l is a list, not a single table, the list is temporarily
+ broken.
+
+ @detail
+ This function is meant as a replacement for open_ltable() when
+ MERGE tables can be opened. open_ltable() cannot open MERGE tables.
+
+ 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
+ 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)
+{
+ TABLE_LIST *save_next_global;
+ DBUG_ENTER("open_n_lock_single_table");
+
+ /* Remember old 'next' pointer. */
+ save_next_global= table_l->next_global;
+ /* Break list. */
+ table_l->next_global= NULL;
+
+ /* Set requested lock type. */
+ table_l->lock_type= lock_type;
+ /* Allow to open real tables only. */
+ table_l->required_type= FRMTYPE_TABLE;
+
+ /* Open the table. */
+ if (simple_open_n_lock_tables(thd, table_l))
+ table_l->table= NULL; /* Just to be sure. */
+
+ /* Restore list. */
+ table_l->next_global= save_next_global;
+
+ DBUG_RETURN(table_l->table);
+}
+
+
/*
Open and lock one table
@@ -2942,6 +4884,7 @@ static bool check_lock_and_start_stmt(THD *thd, TABLE *table,
thd Thread handler
table_list Table to open is first table in this list
lock_type Lock to use for open
+ lock_flags Flags passed to mysql_lock_table
NOTE
This function don't do anything like SP/SF/views/triggers analysis done
@@ -2957,13 +4900,14 @@ static bool check_lock_and_start_stmt(THD *thd, TABLE *table,
table_list->table table
*/
-TABLE *open_ltable(THD *thd, TABLE_LIST *table_list, thr_lock_type lock_type)
+TABLE *open_ltable(THD *thd, TABLE_LIST *table_list, thr_lock_type lock_type,
+ uint lock_flags)
{
TABLE *table;
bool refresh;
DBUG_ENTER("open_ltable");
- thd->proc_info="Opening table";
+ 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;
@@ -2973,6 +4917,17 @@ TABLE *open_ltable(THD *thd, TABLE_LIST *table_list, thr_lock_type lock_type)
if (table)
{
+ if (table->child_l)
+ {
+ /* A MERGE table must not come here. */
+ /* purecov: begin tested */
+ my_error(ER_WRONG_OBJECT, MYF(0), table->s->db.str,
+ table->s->table_name.str, "BASE TABLE");
+ table= 0;
+ goto end;
+ /* purecov: end */
+ }
+
table_list->lock_type= lock_type;
table_list->table= table;
table->grant= table_list->grant;
@@ -2985,61 +4940,26 @@ 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, 0,
- &refresh)))
+ if (! (thd->lock= mysql_lock_tables(thd, &table_list->table, 1,
+ lock_flags, &refresh)))
table= 0;
}
}
- thd->proc_info=0;
- DBUG_RETURN(table);
-}
-
-
-/*
- Open all tables in list and locks them for read without derived
- tables processing.
-
- SYNOPSIS
- simple_open_n_lock_tables()
- thd - thread handler
- tables - list of tables for open&locking
-
- RETURN
- 0 - ok
- -1 - error
-
- NOTE
- The lock will automaticaly be freed by close_thread_tables()
-*/
-int simple_open_n_lock_tables(THD *thd, TABLE_LIST *tables)
-{
- uint counter;
- bool need_reopen;
- DBUG_ENTER("simple_open_n_lock_tables");
-
- for ( ; ; )
- {
- if (open_tables(thd, &tables, &counter, 0))
- DBUG_RETURN(-1);
- if (!lock_tables(thd, tables, counter, &need_reopen))
- break;
- if (!need_reopen)
- DBUG_RETURN(-1);
- close_tables_for_reopen(thd, &tables);
- }
- DBUG_RETURN(0);
+ end:
+ thd_proc_info(thd, 0);
+ DBUG_RETURN(table);
}
/*
- Open all tables in list, locks them and process derived tables
- tables processing.
+ Open all tables in list, locks them and optionally process derived tables.
SYNOPSIS
- open_and_lock_tables()
+ open_and_lock_tables_derived()
thd - thread handler
tables - list of tables for open&locking
+ derived - if to handle derived tables
RETURN
FALSE - ok
@@ -3047,27 +4967,43 @@ int simple_open_n_lock_tables(THD *thd, TABLE_LIST *tables)
NOTE
The lock will automaticaly 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.
*/
-int open_and_lock_tables(THD *thd, TABLE_LIST *tables)
+int open_and_lock_tables_derived(THD *thd, TABLE_LIST *tables, bool derived)
{
uint counter;
bool need_reopen;
- DBUG_ENTER("open_and_lock_tables");
+ DBUG_ENTER("open_and_lock_tables_derived");
+ DBUG_PRINT("enter", ("derived handling: %d", derived));
for ( ; ; )
{
if (open_tables(thd, &tables, &counter, 0))
DBUG_RETURN(-1);
+
+ 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 (mysql_handle_derived(thd->lex, &mysql_derived_prepare) ||
- (thd->fill_derived_tables() &&
- mysql_handle_derived(thd->lex, &mysql_derived_filling)))
+ if (derived &&
+ (mysql_handle_derived(thd->lex, &mysql_derived_prepare) ||
+ (thd->fill_derived_tables() &&
+ mysql_handle_derived(thd->lex, &mysql_derived_filling))))
DBUG_RETURN(TRUE); /* purecov: inspected */
DBUG_RETURN(0);
}
@@ -3126,6 +5062,156 @@ static void mark_real_tables_as_free_for_reuse(TABLE_LIST *table)
}
+/**
+ 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)
+{
+ if (mysql_bin_log.is_open() && (thd->options & OPTION_BIN_LOG))
+ {
+ /*
+ 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_some_set= 0;
+ handler::Table_flags flags_all_set=
+ HA_BINLOG_ROW_CAPABLE | HA_BINLOG_STMT_CAPABLE;
+
+ my_bool multi_engine= FALSE;
+ void* prev_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();
+ if (table->lock_type >= TL_WRITE_ALLOW_WRITE)
+ {
+ ulonglong const flags= table->table->file->ha_table_flags();
+ 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_ht && prev_ht != table->table->file->ht)
+ multi_engine= TRUE;
+ prev_ht= table->table->file->ht;
+ flags_all_set &= flags;
+ flags_some_set |= flags;
+ }
+ }
+
+ DBUG_PRINT("info", ("flags_all_set: %s%s",
+ FLAGSTR(flags_all_set, HA_BINLOG_STMT_CAPABLE),
+ FLAGSTR(flags_all_set, HA_BINLOG_ROW_CAPABLE)));
+ DBUG_PRINT("info", ("flags_some_set: %s%s",
+ FLAGSTR(flags_some_set, HA_BINLOG_STMT_CAPABLE),
+ FLAGSTR(flags_some_set, HA_BINLOG_ROW_CAPABLE)));
+ DBUG_PRINT("info", ("thd->variables.binlog_format: %ld",
+ thd->variables.binlog_format));
+ DBUG_PRINT("info", ("multi_engine: %s",
+ multi_engine ? "TRUE" : "FALSE"));
+
+ int error= 0;
+ if (flags_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_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_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_engine &&
+ (flags_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");
+ }
+
+ 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_all_set & HA_BINLOG_STMT_CAPABLE) == 0)
+ {
+ thd->set_current_stmt_binlog_row_based_if_mixed();
+ }
+ }
+
+ return 0;
+}
+
/*
Lock all tables in list
@@ -3164,16 +5250,10 @@ int lock_tables(THD *thd, TABLE_LIST *tables, uint count, bool *need_reopen)
in prelocked mode.
*/
DBUG_ASSERT(!thd->prelocked_mode || !thd->lex->requires_prelocking());
- /*
- If statement requires prelocking then it has non-empty table list.
- So it is safe to shortcut.
- */
- DBUG_ASSERT(!thd->lex->requires_prelocking() || tables);
-
*need_reopen= FALSE;
- if (!tables)
- DBUG_RETURN(0);
+ if (!tables && !thd->lex->requires_prelocking())
+ DBUG_RETURN(decide_logging_format(thd, tables));
/*
We need this extra check for thd->prelocked_mode because we want to avoid
@@ -3188,6 +5268,7 @@ int lock_tables(THD *thd, TABLE_LIST *tables, uint count, bool *need_reopen)
{
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);
@@ -3202,19 +5283,30 @@ int lock_tables(THD *thd, TABLE_LIST *tables, uint count, bool *need_reopen)
{
thd->in_lock_tables=1;
thd->options|= OPTION_TABLE_LOCK;
+ /*
+ If we have >= 2 different tables to update with auto_inc columns,
+ statement-based binlogging won't work. We can solve this problem in
+ mixed mode by switching to row-based binlogging:
+ */
+ if (thd->variables.binlog_format == BINLOG_FORMAT_MIXED &&
+ has_two_write_locked_tables_with_auto_increment(tables))
+ {
+ thd->lex->set_stmt_unsafe();
+ thd->set_current_stmt_binlog_row_based_if_mixed();
+ }
}
if (! (thd->lock= mysql_lock_tables(thd, start, (uint) (ptr - start),
- MYSQL_LOCK_NOTIFY_IF_NEED_REOPEN,
- need_reopen)))
+ lock_flag, need_reopen)))
{
if (thd->lex->requires_prelocking())
{
- thd->options&= ~(ulong) (OPTION_TABLE_LOCK);
+ thd->options&= ~(OPTION_TABLE_LOCK);
thd->in_lock_tables=0;
}
DBUG_RETURN(-1);
}
+
if (thd->lex->requires_prelocking() &&
thd->lex->sql_command != SQLCOM_LOCK_TABLES)
{
@@ -3233,17 +5325,26 @@ int lock_tables(THD *thd, TABLE_LIST *tables, uint count, bool *need_reopen)
thd->lock= 0;
thd->in_lock_tables=0;
- for (table= tables; table != first_not_own; table= table->next_global)
+ /*
+ 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".
+ Look for example at those places where open_n_lock_single_table()
+ is called. That function implements the temporary breaking of
+ a table list for opening a single table.
+ */
+ for (table= tables;
+ table && table != first_not_own;
+ table= table->next_global)
{
if (!table->placeholder())
{
table->table->query_id= thd->query_id;
if (check_lock_and_start_stmt(thd, table->table, table->lock_type))
{
- ha_rollback_stmt(thd);
mysql_unlock_tables(thd, thd->locked_tables);
thd->locked_tables= 0;
- thd->options&= ~(ulong) (OPTION_TABLE_LOCK);
+ thd->options&= ~(OPTION_TABLE_LOCK);
DBUG_RETURN(-1);
}
}
@@ -3260,12 +5361,21 @@ int lock_tables(THD *thd, TABLE_LIST *tables, uint count, bool *need_reopen)
else
{
TABLE_LIST *first_not_own= thd->lex->first_not_own_table();
- for (table= tables; table != first_not_own; table= table->next_global)
+ /*
+ 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".
+ Look for example at those places where open_n_lock_single_table()
+ is called. That function implements the temporary breaking of
+ a table list for opening a single table.
+ */
+ for (table= tables;
+ table && table != first_not_own;
+ table= table->next_global)
{
if (!table->placeholder() &&
check_lock_and_start_stmt(thd, table->table, table->lock_type))
{
- ha_rollback_stmt(thd);
DBUG_RETURN(-1);
}
}
@@ -3281,7 +5391,8 @@ int lock_tables(THD *thd, TABLE_LIST *tables, uint count, bool *need_reopen)
thd->prelocked_mode= PRELOCKED_UNDER_LOCK_TABLES;
}
}
- DBUG_RETURN(0);
+
+ DBUG_RETURN(decide_logging_format(thd, tables));
}
@@ -3308,15 +5419,28 @@ void close_tables_for_reopen(THD *thd, TABLE_LIST **tables)
sp_remove_not_own_routines(thd->lex);
for (TABLE_LIST *tmp= *tables; tmp; tmp= tmp->next_global)
tmp->table= 0;
- mark_used_tables_as_free_for_reuse(thd, thd->temporary_tables);
close_thread_tables(thd);
}
/*
Open a single table without table caching and don't set it in open_list
- Used by alter_table to open a temporary table and when creating
- a temporary table with CREATE TEMPORARY ...
+
+ 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
*/
TABLE *open_temporary_table(THD *thd, const char *path, const char *db,
@@ -3324,74 +5448,86 @@ TABLE *open_temporary_table(THD *thd, const char *path, const char *db,
{
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");
-
- /*
- The extra size in my_malloc() is for table_cache_key
- 4 bytes for master thread id if we are in the slave
- 1 byte to terminate db
- 1 byte to terminate table_name
- total of 6 extra bytes in my_malloc in addition to table/db stuff
- */
- if (!(tmp_table=(TABLE*) my_malloc(sizeof(*tmp_table)+(uint) strlen(db)+
- (uint) strlen(table_name)+6+4,
- MYF(MY_WME))))
+ DBUG_PRINT("enter",
+ ("table: '%s'.'%s' path: '%s' server_id: %u "
+ "pseudo_thread_id: %lu",
+ db, table_name, path,
+ (uint) thd->server_id, (ulong) thd->variables.pseudo_thread_id));
+
+ table_list.db= (char*) db;
+ table_list.table_name= (char*) table_name;
+ /* Create the cache_key for temporary tables */
+ key_length= create_table_def_key(thd, cache_key, &table_list, 1);
+
+ if (!(tmp_table= (TABLE*) my_malloc(sizeof(*tmp_table) + sizeof(*share) +
+ strlen(path)+1 + key_length,
+ MYF(MY_WME))))
DBUG_RETURN(0); /* purecov: inspected */
- if (openfrm(thd, path, table_name,
- (uint) (HA_OPEN_KEYFILE | HA_OPEN_RNDFILE | HA_GET_INDEX),
- READ_KEYINFO | COMPUTE_TYPES | EXTRA_RECORD,
- ha_open_options,
- tmp_table))
+ share= (TABLE_SHARE*) (tmp_table+1);
+ tmp_path= (char*) (share+1);
+ saved_cache_key= strmov(tmp_path, path)+1;
+ memcpy(saved_cache_key, cache_key, key_length);
+
+ init_tmp_table_share(thd, share, saved_cache_key, key_length,
+ strend(saved_cache_key)+1, tmp_path);
+
+ if (open_table_def(thd, share, 0) ||
+ open_table_from_share(thd, share, table_name,
+ (uint) (HA_OPEN_KEYFILE | HA_OPEN_RNDFILE |
+ HA_GET_INDEX),
+ READ_KEYINFO | COMPUTE_TYPES | EXTRA_RECORD,
+ ha_open_options,
+ tmp_table, FALSE))
{
+ /* 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));
DBUG_RETURN(0);
}
- share= tmp_table->s;
- tmp_table->reginfo.lock_type=TL_WRITE; // Simulate locked
+ tmp_table->reginfo.lock_type= TL_WRITE; // Simulate locked
share->tmp_table= (tmp_table->file->has_transactions() ?
TRANSACTIONAL_TMP_TABLE : NON_TRANSACTIONAL_TMP_TABLE);
- share->table_cache_key= (char*) (tmp_table+1);
- share->db= share->table_cache_key;
- share->key_length= (uint) (strmov(((char*) (share->table_name=
- strmov(share->table_cache_key,
- db)+1)),
- table_name) -
- share->table_cache_key) +1;
- int4store(share->table_cache_key + share->key_length, thd->server_id);
- share->key_length+= 4;
- int4store(share->table_cache_key + share->key_length,
- thd->variables.pseudo_thread_id);
- share->key_length+= 4;
if (link_in_list)
{
- tmp_table->next=thd->temporary_tables;
- thd->temporary_tables=tmp_table;
+ /* growing temp list at the head */
+ tmp_table->next= thd->temporary_tables;
+ if (tmp_table->next)
+ tmp_table->next->prev= tmp_table;
+ thd->temporary_tables= tmp_table;
+ thd->temporary_tables->prev= 0;
if (thd->slave_thread)
slave_open_temp_tables++;
}
tmp_table->pos_in_table_list= 0;
+ DBUG_PRINT("tmptable", ("opened table: '%s'.'%s' 0x%lx", tmp_table->s->db.str,
+ tmp_table->s->table_name.str, (long) tmp_table));
DBUG_RETURN(tmp_table);
}
-bool rm_temporary_table(enum db_type base, char *path)
+bool rm_temporary_table(handlerton *base, char *path)
{
bool error=0;
+ handler *file;
+ char *ext;
DBUG_ENTER("rm_temporary_table");
- fn_format(path, path,"",reg_ext,4);
- unpack_filename(path,path);
+ strmov(ext= strend(path), reg_ext);
if (my_delete(path,MYF(0)))
error=1; /* purecov: inspected */
- *fn_ext(path)='\0'; // remove extension
- handler *file= get_new_handler((TABLE*) 0, current_thd->mem_root, base);
- if (file && file->delete_table(path))
+ *ext= 0; // remove extension
+ file= get_new_handler((TABLE_SHARE*) 0, current_thd->mem_root, base);
+ if (file && file->ha_delete_table(path))
{
error=1;
- sql_print_warning("Could not remove tmp table: '%s', error: %d",
+ sql_print_warning("Could not remove temporary table: '%s', error: %d",
path, my_errno);
}
delete file;
@@ -3416,17 +5552,50 @@ Field *view_ref_found= (Field*) 0x2;
static void update_field_dependencies(THD *thd, Field *field, TABLE *table)
{
- if (thd->set_query_id)
+ DBUG_ENTER("update_field_dependencies");
+ if (thd->mark_used_columns != MARK_COLUMNS_NONE)
{
- if (field->query_id != thd->query_id)
+ MY_BITMAP *current_bitmap, *other_bitmap;
+
+ /*
+ We always want to register the used keys, as the column bitmap may have
+ been set for all fields (for example for view).
+ */
+
+ table->covering_keys.intersect(field->part_of_key);
+ table->merge_keys.merge(field->part_of_key);
+
+ if (thd->mark_used_columns == MARK_COLUMNS_READ)
{
- field->query_id= thd->query_id;
- table->used_fields++;
- table->used_keys.intersect(field->part_of_key);
+ current_bitmap= table->read_set;
+ other_bitmap= table->write_set;
}
else
- thd->dupp_field= field;
+ {
+ current_bitmap= table->write_set;
+ other_bitmap= table->read_set;
+ }
+
+ if (bitmap_fast_test_and_set(current_bitmap, field->field_index))
+ {
+ if (thd->mark_used_columns == MARK_COLUMNS_WRITE)
+ {
+ DBUG_PRINT("warning", ("Found duplicated field"));
+ thd->dup_field= field;
+ }
+ else
+ {
+ DBUG_PRINT("note", ("Field found before"));
+ }
+ DBUG_VOID_RETURN;
+ }
+ if (table->get_fields_in_item_tree)
+ field->flags|= GET_FIXED_FIELDS_FLAG;
+ table->used_fields++;
}
+ else if (table->get_fields_in_item_tree)
+ field->flags|= GET_FIXED_FIELDS_FLAG;
+ DBUG_VOID_RETURN;
}
@@ -3553,6 +5722,7 @@ find_field_in_natural_join(THD *thd, TABLE_LIST *table_ref, const char *name,
DBUG_ASSERT(table_ref->is_natural_join && table_ref->join_columns);
DBUG_ASSERT(*actual_table == NULL);
+ LINT_INIT(arena);
LINT_INIT(found_field);
for (nj_col= NULL, curr_nj_col= field_it++; curr_nj_col;
@@ -3677,8 +5847,18 @@ find_field_in_table(THD *thd, TABLE *table, const char *name, uint length,
table->field[cached_field_index]->field_name, name))
field_ptr= table->field + cached_field_index;
else if (table->s->name_hash.records)
- field_ptr= (Field**) hash_search(&table->s->name_hash, (byte*) name,
+ {
+ field_ptr= (Field**) hash_search(&table->s->name_hash, (uchar*) name,
length);
+ if (field_ptr)
+ {
+ /*
+ field_ptr points to field in TABLE_SHARE. Convert it to the matching
+ field in table
+ */
+ field_ptr= (table->field + (field_ptr - table->s->field));
+ }
+ }
else
{
if (!(field_ptr= table->field))
@@ -3690,15 +5870,16 @@ find_field_in_table(THD *thd, TABLE *table, const char *name, uint length,
if (field_ptr && *field_ptr)
{
- *cached_field_index_ptr= (uint) (field_ptr - table->field);
+ *cached_field_index_ptr= field_ptr - table->field;
field= *field_ptr;
}
else
{
if (!allow_rowid ||
my_strcasecmp(system_charset_info, name, "_rowid") ||
- !(field=table->rowid_field))
+ table->s->rowid_field_offset == 0)
DBUG_RETURN((Field*) 0);
+ field= table->field[table->s->rowid_field_offset-1];
}
update_field_dependencies(thd, field, table);
@@ -3760,6 +5941,9 @@ find_field_in_table_ref(THD *thd, TABLE_LIST *table_list,
{
Field *fld;
DBUG_ENTER("find_field_in_table_ref");
+ DBUG_ASSERT(table_list->alias);
+ DBUG_ASSERT(name);
+ DBUG_ASSERT(item_name);
DBUG_PRINT("enter",
("table: '%s' field name: '%s' item name: '%s' ref 0x%lx",
table_list->alias, name, item_name, (ulong) ref));
@@ -3847,18 +6031,97 @@ find_field_in_table_ref(THD *thd, TABLE_LIST *table_list,
register_tree_change, actual_table);
}
+ if (fld)
+ {
#ifndef NO_EMBEDDED_ACCESS_CHECKS
- /* Check if there are sufficient access rights to the found field. */
- if (fld && check_privileges &&
- check_column_grant_in_table_ref(thd, *actual_table, name, length))
- fld= WRONG_GRANT;
+ /* Check if there are sufficient access rights to the found field. */
+ if (check_privileges &&
+ check_column_grant_in_table_ref(thd, *actual_table, name, length))
+ fld= WRONG_GRANT;
+ else
#endif
-
+ if (thd->mark_used_columns != MARK_COLUMNS_NONE)
+ {
+ /*
+ Get rw_set correct for this field so that the handler
+ knows that this field is involved in the query and gets
+ retrieved/updated
+ */
+ Field *field_to_set= NULL;
+ if (fld == view_ref_found)
+ {
+ Item *it= (*ref)->real_item();
+ if (it->type() == Item::FIELD_ITEM)
+ field_to_set= ((Item_field*)it)->field;
+ else
+ {
+ if (thd->mark_used_columns == MARK_COLUMNS_READ)
+ it->walk(&Item::register_field_in_read_map, 1, (uchar *) 0);
+ }
+ }
+ else
+ field_to_set= fld;
+ if (field_to_set)
+ {
+ TABLE *table= field_to_set->table;
+ if (thd->mark_used_columns == MARK_COLUMNS_READ)
+ bitmap_set_bit(table->read_set, field_to_set->field_index);
+ else
+ bitmap_set_bit(table->write_set, field_to_set->field_index);
+ }
+ }
+ }
DBUG_RETURN(fld);
}
/*
+ Find field in table, no side effects, only purpose is to check for field
+ in table object and get reference to the field if found.
+
+ SYNOPSIS
+ find_field_in_table_sef()
+
+ table table where to find
+ name Name of field searched for
+
+ RETURN
+ 0 field is not found
+ # pointer to field
+*/
+
+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));
+ if (field_ptr)
+ {
+ /*
+ field_ptr points to field in TABLE_SHARE. Convert it to the matching
+ field in table
+ */
+ field_ptr= (table->field + (field_ptr - table->s->field));
+ }
+ }
+ else
+ {
+ if (!(field_ptr= table->field))
+ return (Field *)0;
+ for (; *field_ptr; ++field_ptr)
+ if (!my_strcasecmp(system_charset_info, (*field_ptr)->field_name, name))
+ break;
+ }
+ if (field_ptr)
+ return *field_ptr;
+ else
+ return (Field *)0;
+}
+
+
+/*
Find field in table list.
SYNOPSIS
@@ -4013,8 +6276,8 @@ find_field_in_tables(THD *thd, Item_ident *item,
{
Field *nf=new Field_null(NULL,0,Field::NONE,
cur_field->field_name,
- cur_field->table,
&my_charset_bin);
+ nf->init(cur_table->table);
cur_field= nf;
}
}
@@ -4601,15 +6864,19 @@ mark_common_columns(THD *thd, TABLE_LIST *table_ref_1, TABLE_LIST *table_ref_2,
if (field_1)
{
+ TABLE *table_1= nj_col_1->table_ref->table;
/* Mark field_1 used for table cache. */
- field_1->query_id= thd->query_id;
- nj_col_1->table_ref->table->used_keys.intersect(field_1->part_of_key);
+ bitmap_set_bit(table_1->read_set, field_1->field_index);
+ table_1->covering_keys.intersect(field_1->part_of_key);
+ table_1->merge_keys.merge(field_1->part_of_key);
}
if (field_2)
{
+ TABLE *table_2= nj_col_2->table_ref->table;
/* Mark field_2 used for table cache. */
- field_2->query_id= thd->query_id;
- nj_col_2->table_ref->table->used_keys.intersect(field_2->part_of_key);
+ bitmap_set_bit(table_2->read_set, field_2->field_index);
+ table_2->covering_keys.intersect(field_2->part_of_key);
+ table_2->merge_keys.merge(field_2->part_of_key);
}
if (using_fields != NULL)
@@ -5103,17 +7370,18 @@ int setup_wild(THD *thd, TABLE_LIST *tables, List<Item> &fields,
****************************************************************************/
bool setup_fields(THD *thd, Item **ref_pointer_array,
- List<Item> &fields, bool set_query_id,
+ List<Item> &fields, enum_mark_columns mark_used_columns,
List<Item> *sum_func_list, bool allow_sum_func)
{
reg2 Item *item;
- bool save_set_query_id= thd->set_query_id;
+ enum_mark_columns save_mark_used_columns= thd->mark_used_columns;
nesting_map save_allow_sum_func= thd->lex->allow_sum_func;
List_iterator<Item> it(fields);
bool save_is_item_list_lookup;
DBUG_ENTER("setup_fields");
- thd->set_query_id=set_query_id;
+ 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->where= THD::DEFAULT_WHERE;
@@ -5134,6 +7402,22 @@ bool setup_fields(THD *thd, Item **ref_pointer_array,
if (ref_pointer_array)
bzero(ref_pointer_array, sizeof(Item *) * fields.elements);
+ /*
+ We call set_entry() there (before fix_fields() of the whole list of field
+ items) because:
+ 1) the list of field items has same order as in the query, and the
+ Item_func_get_user_var item may go before the Item_func_set_user_var:
+ SELECT @a, @a := 10 FROM t;
+ 2) The entry->update_query_id value controls constantness of
+ Item_func_get_user_var items, so in presence of Item_func_set_user_var
+ items we have to refresh their entries before fixing of
+ Item_func_get_user_var items.
+ */
+ List_iterator<Item_func_set_user_var> li(thd->lex->set_var_list);
+ Item_func_set_user_var *var;
+ while ((var= li++))
+ var->set_entry(thd, FALSE);
+
Item **ref= ref_pointer_array;
thd->lex->current_select->cur_pos_in_select_list= 0;
while ((item= it++))
@@ -5143,7 +7427,8 @@ bool setup_fields(THD *thd, Item **ref_pointer_array,
{
thd->lex->current_select->is_item_list_lookup= save_is_item_list_lookup;
thd->lex->allow_sum_func= save_allow_sum_func;
- thd->set_query_id= save_set_query_id;
+ thd->mark_used_columns= save_mark_used_columns;
+ DBUG_PRINT("info", ("thd->mark_used_columns: %d", thd->mark_used_columns));
DBUG_RETURN(TRUE); /* purecov: inspected */
}
if (ref)
@@ -5158,8 +7443,9 @@ bool setup_fields(THD *thd, Item **ref_pointer_array,
thd->lex->current_select->cur_pos_in_select_list= UNDEF_POS;
thd->lex->allow_sum_func= save_allow_sum_func;
- thd->set_query_id= save_set_query_id;
- DBUG_RETURN(test(thd->net.report_error));
+ thd->mark_used_columns= save_mark_used_columns;
+ DBUG_PRINT("info", ("thd->mark_used_columns: %d", thd->mark_used_columns));
+ DBUG_RETURN(test(thd->is_error()));
}
@@ -5202,7 +7488,6 @@ TABLE_LIST **make_leaves_list(TABLE_LIST **list, TABLE_LIST *tables)
context name resolution contest to setup table list there
from_clause Top-level list of table references in the FROM clause
tables Table list (select_lex->table_list)
- conds Condition of current SELECT (can be changed by VIEW)
leaves List of join table leaves list (select_lex->leaf_tables)
refresh It is onle refresh for subquery
select_insert It is SELECT ... INSERT command
@@ -5224,7 +7509,7 @@ TABLE_LIST **make_leaves_list(TABLE_LIST **list, TABLE_LIST *tables)
bool setup_tables(THD *thd, Name_resolution_context *context,
List<TABLE_LIST> *from_clause, TABLE_LIST *tables,
- Item **conds, TABLE_LIST **leaves, bool select_insert)
+ TABLE_LIST **leaves, bool select_insert)
{
uint tablenr= 0;
DBUG_ENTER("setup_tables");
@@ -5256,29 +7541,8 @@ bool setup_tables(THD *thd, Name_resolution_context *context,
tablenr= 0;
}
setup_table_map(table, table_list, tablenr);
- table->used_keys= table->s->keys_for_keyread;
- if (table_list->use_index)
- {
- key_map map;
- get_key_map_from_key_list(&map, table, table_list->use_index);
- if (map.is_set_all())
- DBUG_RETURN(1);
- /*
- Don't introduce keys in keys_in_use_for_query that weren't there
- before. FORCE/USE INDEX should not add keys, it should only remove
- all keys except the key(s) specified in the hint.
- */
- table->keys_in_use_for_query.intersect(map);
- }
- if (table_list->ignore_index)
- {
- key_map map;
- get_key_map_from_key_list(&map, table, table_list->ignore_index);
- if (map.is_set_all())
- DBUG_RETURN(1);
- table->keys_in_use_for_query.subtract(map);
- }
- table->used_keys.intersect(table->keys_in_use_for_query);
+ if (table_list->process_index_hints(table))
+ DBUG_RETURN(1);
}
if (tablenr > MAX_TABLES)
{
@@ -5342,31 +7606,31 @@ bool setup_tables_and_check_access(THD *thd,
Name_resolution_context *context,
List<TABLE_LIST> *from_clause,
TABLE_LIST *tables,
- Item **conds, TABLE_LIST **leaves,
+ TABLE_LIST **leaves,
bool select_insert,
ulong want_access_first,
ulong want_access)
{
- TABLE_LIST *leaves_tmp = NULL;
+ TABLE_LIST *leaves_tmp= NULL;
bool first_table= true;
- if (setup_tables (thd, context, from_clause, tables, conds,
- &leaves_tmp, select_insert))
+ if (setup_tables(thd, context, from_clause, tables,
+ &leaves_tmp, select_insert))
return TRUE;
if (leaves)
- *leaves = leaves_tmp;
+ *leaves= leaves_tmp;
for (; leaves_tmp; leaves_tmp= leaves_tmp->next_leaf)
{
if (leaves_tmp->belong_to_view &&
check_single_table_access(thd, first_table ? want_access_first :
- want_access, leaves_tmp))
+ want_access, leaves_tmp, FALSE))
{
tables->hide_view_error(thd);
return TRUE;
}
- first_table= false;
+ first_table= 0;
}
return FALSE;
}
@@ -5478,9 +7742,34 @@ insert_fields(THD *thd, Name_resolution_context *context, const char *db_name,
continue;
#ifndef NO_EMBEDDED_ACCESS_CHECKS
- /* Ensure that we have access rights to all fields to be inserted. */
- if (!((table && (table->grant.privilege & SELECT_ACL) ||
- tables->view && (tables->grant.privilege & SELECT_ACL))) &&
+ /*
+ Ensure that we have access rights to all fields to be inserted. Under
+ some circumstances, this check may be skipped.
+
+ - If any_privileges is true, skip the check.
+
+ - If the SELECT privilege has been found as fulfilled already for both
+ the TABLE and TABLE_LIST objects (and both of these exist, of
+ course), the check is skipped.
+
+ - If the SELECT privilege has been found fulfilled for the TABLE object
+ and the TABLE_LIST represents a derived table other than a view (see
+ below), the check is skipped.
+
+ - If the TABLE_LIST object represents a view, we may skip checking if
+ the SELECT privilege has been found fulfilled for it, regardless of
+ the TABLE object.
+
+ - If there is no TABLE object, the test is skipped if either
+ * the TABLE_LIST does not represent a view, or
+ * the SELECT privilege has been found fulfilled.
+
+ A TABLE_LIST that is not a view may be a subquery, an
+ information_schema table, or a nested table reference. See the comment
+ for TABLE_LIST.
+ */
+ if (!(table && !tables->view && (table->grant.privilege & SELECT_ACL) ||
+ tables->view && (tables->grant.privilege & SELECT_ACL)) &&
!any_privileges)
{
field_iterator.set(tables);
@@ -5489,7 +7778,6 @@ insert_fields(THD *thd, Name_resolution_context *context, const char *db_name,
}
#endif
-
/*
Update the tables used in the query based on the referenced fields. For
views and natural joins this update is performed inside the loop below.
@@ -5539,19 +7827,19 @@ insert_fields(THD *thd, Name_resolution_context *context, const char *db_name,
tables->is_natural_join);
DBUG_ASSERT(item->type() == Item::FIELD_ITEM);
Item_field *fld= (Item_field*) item;
- const char *field_table_name= field_iterator.table_name();
+ const char *field_table_name= field_iterator.get_table_name();
if (!tables->schema_table &&
!(fld->have_privileges=
(get_column_grant(thd, field_iterator.grant(),
- field_iterator.db_name(),
+ field_iterator.get_db_name(),
field_table_name, fld->field_name) &
VIEW_ANY_ACL)))
{
- my_error(ER_COLUMNACCESS_DENIED_ERROR, MYF(0), "ANY",
+ my_error(ER_TABLEACCESS_DENIED_ERROR, MYF(0), "ANY",
thd->security_ctx->priv_user,
thd->security_ctx->host_or_ip,
- fld->field_name, field_table_name);
+ field_table_name);
DBUG_RETURN(TRUE);
}
}
@@ -5559,17 +7847,13 @@ insert_fields(THD *thd, Name_resolution_context *context, const char *db_name,
if ((field= field_iterator.field()))
{
- /*
- Mark if field used before in this select.
- Used by 'insert' to verify if a field name is used twice.
- */
- if (field->query_id == thd->query_id)
- thd->dupp_field= field;
- field->query_id= thd->query_id;
-
+ /* Mark fields as used to allow storage engine to optimze access */
+ bitmap_set_bit(field->table->read_set, field->field_index);
if (table)
- table->used_keys.intersect(field->part_of_key);
-
+ {
+ table->covering_keys.intersect(field->part_of_key);
+ table->merge_keys.merge(field->part_of_key);
+ }
if (tables->is_natural_join)
{
TABLE *field_table;
@@ -5585,17 +7869,14 @@ insert_fields(THD *thd, Name_resolution_context *context, const char *db_name,
if (field_table)
{
thd->used_tables|= field_table->map;
- field_table->used_keys.intersect(field->part_of_key);
+ field_table->covering_keys.intersect(field->part_of_key);
+ field_table->merge_keys.merge(field->part_of_key);
field_table->used_fields++;
}
}
}
else
- {
thd->used_tables|= item->used_tables();
- item->walk(&Item::reset_query_id_processor,
- (byte *)(&thd->query_id));
- }
thd->lex->current_select->cur_pos_in_select_list++;
}
/*
@@ -5666,7 +7947,8 @@ int setup_conds(THD *thd, TABLE_LIST *tables, TABLE_LIST *leaves,
arena->is_conventional())
arena= 0; // For easier test
- thd->set_query_id=1;
+ thd->mark_used_columns= MARK_COLUMNS_READ;
+ DBUG_PRINT("info", ("thd->mark_used_columns: %d", thd->mark_used_columns));
select_lex->cond_count= 0;
select_lex->between_count= 0;
select_lex->max_equal_elems= 0;
@@ -5736,7 +8018,7 @@ int setup_conds(THD *thd, TABLE_LIST *tables, TABLE_LIST *leaves,
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->net.report_error));
+ DBUG_RETURN(test(thd->is_error()));
err_no_arena:
select_lex->is_item_list_lookup= save_is_item_list_lookup;
@@ -5818,7 +8100,7 @@ fill_record(THD * thd, List<Item> &fields, List<Item> &values,
goto err;
}
}
- DBUG_RETURN(thd->net.report_error);
+ DBUG_RETURN(thd->is_error());
err:
if (table)
table->auto_increment_field_not_null= FALSE;
@@ -5903,7 +8185,7 @@ fill_record(THD *thd, Field **ptr, List<Item> &values, bool ignore_errors)
table= (*ptr)->table;
table->auto_increment_field_not_null= FALSE;
}
- while ((field = *ptr++) && !thd->net.report_error)
+ while ((field = *ptr++) && ! thd->is_error())
{
value=v++;
table= field->table;
@@ -5912,7 +8194,7 @@ fill_record(THD *thd, Field **ptr, List<Item> &values, bool ignore_errors)
if (value->save_in_field(field, 0) < 0)
goto err;
}
- DBUG_RETURN(thd->net.report_error);
+ DBUG_RETURN(thd->is_error());
err:
if (table)
@@ -5962,7 +8244,7 @@ my_bool mysql_rm_tmp_tables(void)
char filePath[FN_REFLEN], *tmpdir, filePathCopy[FN_REFLEN];
MY_DIR *dirp;
FILEINFO *file;
- TABLE tmp_table;
+ TABLE_SHARE share;
THD *thd;
DBUG_ENTER("mysql_rm_tmp_tables");
@@ -5974,50 +8256,54 @@ my_bool mysql_rm_tmp_tables(void)
for (i=0; i<=mysql_tmpdir_list.max; i++)
{
tmpdir=mysql_tmpdir_list.list[i];
- /* See if the directory exists */
+ /* See if the directory exists */
if (!(dirp = my_dir(tmpdir,MYF(MY_WME | MY_DONT_SORT))))
continue;
/* Remove all SQLxxx tables from directory */
- for (idx=0 ; idx < (uint) dirp->number_off_files ; idx++)
- {
- file=dirp->dir_entry+idx;
+ for (idx=0 ; idx < (uint) dirp->number_off_files ; idx++)
+ {
+ file=dirp->dir_entry+idx;
- /* skiping . and .. */
- if (file->name[0] == '.' && (!file->name[1] ||
- (file->name[1] == '.' && !file->name[2])))
- continue;
+ /* skiping . and .. */
+ if (file->name[0] == '.' && (!file->name[1] ||
+ (file->name[1] == '.' && !file->name[2])))
+ continue;
- if (!bcmp(file->name,tmp_file_prefix,tmp_file_prefix_length))
- {
- char *ext= fn_ext(file->name);
- size_t ext_len= strlen(ext);
- uint filePath_len= my_snprintf(filePath, sizeof(filePath),
- "%s%s", tmpdir, file->name);
- if (!bcmp(reg_ext, ext, ext_len))
+ if (!bcmp((uchar*) file->name, (uchar*) tmp_file_prefix,
+ tmp_file_prefix_length))
{
- TABLE tmp_table;
- if (!openfrm(thd, filePath, "tmp_table", (uint) 0,
- READ_KEYINFO | COMPUTE_TYPES | EXTRA_RECORD,
- 0, &tmp_table))
+ char *ext= fn_ext(file->name);
+ uint ext_len= strlen(ext);
+ uint filePath_len= my_snprintf(filePath, sizeof(filePath),
+ "%s%c%s", tmpdir, FN_LIBCHAR,
+ file->name);
+ if (!bcmp((uchar*) reg_ext, (uchar*) ext, ext_len))
{
+ handler *handler_file= 0;
/* We should cut file extention before deleting of table */
memcpy(filePathCopy, filePath, filePath_len - ext_len);
filePathCopy[filePath_len - ext_len]= 0;
- tmp_table.file->delete_table(filePathCopy);
- closefrm(&tmp_table);
+ init_tmp_table_share(thd, &share, "", 0, "", filePathCopy);
+ if (!open_table_def(thd, &share, 0) &&
+ ((handler_file= get_new_handler(&share, thd->mem_root,
+ share.db_type()))))
+ {
+ handler_file->ha_delete_table(filePathCopy);
+ delete handler_file;
+ }
+ free_table_share(&share);
}
+ /*
+ File can be already deleted by tmp_table.file->delete_table().
+ So we hide error messages which happnes during deleting of these
+ files(MYF(0)).
+ */
+ VOID(my_delete(filePath, MYF(0)));
}
- /*
- File can be already deleted by tmp_table.file->delete_table().
- So we hide error messages which happnes during deleting of these
- files(MYF(0)).
- */
- VOID(my_delete(filePath, MYF(0)));
}
- }
- my_dirend(dirp);
+ my_dirend(dirp);
}
delete thd;
my_pthread_setspecific_ptr(THR_THD, 0);
@@ -6048,7 +8334,7 @@ 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, db))
+ if (!strcmp(table->s->db.str, db))
{
table->s->version= 0L; /* Free when thread is ready */
if (!table->in_use)
@@ -6056,19 +8342,23 @@ void remove_db_from_cache(const char *db)
}
}
while (unused_tables && !unused_tables->s->version)
- VOID(hash_delete(&open_cache,(byte*) unused_tables));
+ VOID(hash_delete(&open_cache,(uchar*) unused_tables));
}
/*
-** free all unused tables
+ free all unused tables
+
+ NOTE
+ This is called by 'handle_manager' when one wants to periodicly flush
+ all not used tables.
*/
void flush_tables()
{
(void) pthread_mutex_lock(&LOCK_open);
while (unused_tables)
- hash_delete(&open_cache,(byte*) unused_tables);
+ hash_delete(&open_cache,(uchar*) unused_tables);
(void) pthread_mutex_unlock(&LOCK_open);
}
@@ -6094,9 +8384,10 @@ bool remove_table_from_cache(THD *thd, const char *db, const char *table_name,
char key[MAX_DBKEY_LENGTH];
uint key_length;
TABLE *table;
- bool result=0, signalled= 0;
+ 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_PRINT("enter", ("table: '%s'.'%s' flags: %u", db, table_name, flags));
key_length=(uint) (strmov(strmov(key,db)+1,table_name)-key)+1;
for (;;)
@@ -6104,13 +8395,16 @@ bool remove_table_from_cache(THD *thd, const char *db, const char *table_name,
HASH_SEARCH_STATE state;
result= signalled= 0;
- for (table= (TABLE*) hash_first(&open_cache, (byte*) key, key_length,
+ for (table= (TABLE*) hash_first(&open_cache, (uchar*) key, key_length,
&state);
table;
- table= (TABLE*) hash_next(&open_cache, (byte*) key, key_length,
+ 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));
+
table->s->version=0L; /* Free when thread is ready */
if (!(in_use=table->in_use))
{
@@ -6119,7 +8413,13 @@ bool remove_table_from_cache(THD *thd, const char *db, const char *table_name,
}
else if (in_use != thd)
{
- in_use->some_tables_deleted=1;
+ 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"));
@@ -6142,21 +8442,47 @@ bool remove_table_from_cache(THD *thd, const char *db, const char *table_name,
}
/*
Now we must abort all tables locks used by this thread
- as the thread may be waiting to get a lock for another table
+ 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)
{
- if (thd_table->db_stat) // If table is open
+ /* 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)
- VOID(hash_delete(&open_cache,(byte*) unused_tables));
+ 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))
{
/*
@@ -6195,6 +8521,7 @@ bool remove_table_from_cache(THD *thd, const char *db, const char *table_name,
DBUG_RETURN(result);
}
+
int setup_ftfuncs(SELECT_LEX *select_lex)
{
List_iterator<Item_func_match> li(*(select_lex->ftfunc_list)),
@@ -6224,7 +8551,7 @@ int init_ftfuncs(THD *thd, SELECT_LEX *select_lex, bool no_order)
List_iterator<Item_func_match> li(*(select_lex->ftfunc_list));
Item_func_match *ifm;
DBUG_PRINT("info",("Performing FULLTEXT search"));
- thd->proc_info="FULLTEXT initialization";
+ thd_proc_info(thd, "FULLTEXT initialization");
while ((ifm=li++))
ifm->init_search(no_order);
@@ -6239,11 +8566,11 @@ int init_ftfuncs(THD *thd, SELECT_LEX *select_lex, bool no_order)
SYNOPSIS
open_new_frm()
THD thread handler
- path path to .frm
+ path path to .frm file (without extension)
alias alias for table
db database
table_name name of table
- db_stat open flags (for example HA_OPEN_KEYFILE|HA_OPEN_RNDFILE..)
+ db_stat open flags (for example ->OPEN_KEYFILE|HA_OPEN_RNDFILE..)
can be 0 (example in ha_example_table)
prgflag READ_ALL etc..
ha_open_flags HA_OPEN_ABORT_IF_LOCKED etc..
@@ -6253,18 +8580,20 @@ int init_ftfuncs(THD *thd, SELECT_LEX *select_lex, bool no_order)
*/
static bool
-open_new_frm(THD *thd, const char *path, const char *alias,
- const char *db, const char *table_name,
+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)
{
LEX_STRING pathstr;
File_parser *parser;
+ char path[FN_REFLEN];
DBUG_ENTER("open_new_frm");
- pathstr.str= (char*) path;
- pathstr.length= (uint) strlen(path);
+ /* Create path with extension */
+ pathstr.length= (uint) (strxmov(path, share->normalized_path.str, reg_ext,
+ NullS)- path);
+ pathstr.str= path;
if ((parser= sql_parse_prepare(&pathstr, mem_root, 1)))
{
@@ -6272,7 +8601,8 @@ open_new_frm(THD *thd, const char *path, const char *alias,
{
if (table_desc == 0 || table_desc->required_type == FRMTYPE_TABLE)
{
- my_error(ER_WRONG_OBJECT, MYF(0), db, table_name, "BASE TABLE");
+ my_error(ER_WRONG_OBJECT, MYF(0), share->db.str, share->table_name.str,
+ "BASE TABLE");
goto err;
}
if (mysql_make_view(thd, parser, table_desc,
@@ -6282,7 +8612,7 @@ open_new_frm(THD *thd, const char *path, const char *alias,
else
{
/* only VIEWs are supported now */
- my_error(ER_FRM_UNKNOWN_TYPE, MYF(0), path, parser->type()->str);
+ my_error(ER_FRM_UNKNOWN_TYPE, MYF(0), share->path.str, parser->type()->str);
goto err;
}
DBUG_RETURN(0);
@@ -6298,3 +8628,437 @@ bool is_equal(const LEX_STRING *a, const LEX_STRING *b)
{
return a->length == b->length && !strncmp(a->str, b->str, a->length);
}
+
+
+/*
+ SYNOPSIS
+ abort_and_upgrade_lock()
+ 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.
+
+ thd Thread object
+ table Table object
+ db Database name
+ table_name Table name
+ old_lock_level Old lock level
+*/
+
+int abort_and_upgrade_lock(ALTER_PARTITION_PARAM_TYPE *lpt)
+{
+ uint flags= RTFC_WAIT_OTHER_THREAD_FLAG | RTFC_CHECK_KILLED_FLAG;
+ DBUG_ENTER("abort_and_upgrade_locks");
+
+ lpt->old_lock_type= lpt->table->reginfo.lock_type;
+ VOID(pthread_mutex_lock(&LOCK_open));
+ /* If MERGE child, forward lock handling to parent. */
+ mysql_lock_abort(lpt->thd, lpt->table->parent ? lpt->table->parent :
+ lpt->table, TRUE);
+ VOID(remove_table_from_cache(lpt->thd, lpt->db, lpt->table_name, flags));
+ 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);
+ 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 */
+
+
+/*
+ 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=(uint) (strmov(strmov(key,lpt->db)+1,lpt->table_name)-key)+1;
+ 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= THD::KILL_CONNECTION;
+ 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;
+}
+
+
+/*
+ Tells if two (or more) tables have auto_increment columns and we want to
+ lock those tables with a write lock.
+
+ SYNOPSIS
+ 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
+ which you'll want to update (including stored functions, triggers, views
+ inside your statement).
+
+ RETURN
+ 0 No
+ 1 Yes
+*/
+
+static bool
+has_two_write_locked_tables_with_auto_increment(TABLE_LIST *tables)
+{
+ char *first_table_name= NULL, *first_db;
+ LINT_INIT(first_db);
+
+ for (TABLE_LIST *table= tables; table; table= table->next_global)
+ {
+ /* we must do preliminary checks as table->table may be NULL */
+ if (!table->placeholder() &&
+ table->table->found_next_number_field &&
+ (table->lock_type >= TL_WRITE_ALLOW_WRITE))
+ {
+ if (first_table_name == NULL)
+ {
+ first_table_name= table->table_name;
+ first_db= table->db;
+ DBUG_ASSERT(first_db);
+ }
+ else if (strcmp(first_db, table->db) ||
+ strcmp(first_table_name, table->table_name))
+ return 1;
+ }
+ }
+ return 0;
+}
+
+
+/*
+ Open and lock system tables for read.
+
+ SYNOPSIS
+ open_system_tables_for_read()
+ thd Thread context.
+ table_list List of tables to open.
+ backup Pointer to Open_tables_state instance where
+ information about currently open tables will be
+ saved, and from which will be restored when we will
+ end work with system tables.
+
+ NOTES
+ Thanks to restrictions which we put on opening and locking of
+ system tables for writing, we can open and lock them for reading
+ even when we already have some other tables open and locked. One
+ must call close_system_tables() to close systems tables opened
+ with this call.
+
+ RETURN
+ FALSE Success
+ TRUE Error
+*/
+
+bool
+open_system_tables_for_read(THD *thd, TABLE_LIST *table_list,
+ Open_tables_state *backup)
+{
+ DBUG_ENTER("open_system_tables_for_read");
+
+ 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)
+ {
+ 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++;
+ }
+
+ {
+ 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);
+ }
+ if (thd->lock)
+ DBUG_RETURN(FALSE);
+
+error:
+ close_system_tables(thd, backup);
+
+ DBUG_RETURN(TRUE);
+}
+
+
+/*
+ Close system tables, opened with open_system_tables_for_read().
+
+ SYNOPSIS
+ close_system_tables()
+ thd Thread context
+ backup Pointer to Open_tables_state 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_thread_tables(thd);
+ thd->restore_backup_open_tables_state(backup);
+}
+
+
+/*
+ Open and lock one system table for update.
+
+ SYNOPSIS
+ open_system_table_for_update()
+ thd Thread context.
+ one_table Table to open.
+
+ NOTES
+ Table opened with this call should closed using close_thread_tables().
+
+ RETURN
+ 0 Error
+ # Pointer to TABLE object of system table
+*/
+
+TABLE *
+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);
+ if (table)
+ {
+ DBUG_ASSERT(table->s->table_category == TABLE_CATEGORY_SYSTEM);
+ table->use_all_columns();
+ }
+
+ DBUG_RETURN(table);
+}
+
+/**
+ Open a performance schema 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().
+ @param thd The current thread
+ @param one_table Performance schema 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)
+{
+ uint flags= ( MYSQL_LOCK_IGNORE_GLOBAL_READ_LOCK |
+ MYSQL_LOCK_IGNORE_GLOBAL_READ_ONLY |
+ MYSQL_LOCK_IGNORE_FLUSH |
+ MYSQL_LOCK_PERF_SCHEMA);
+ 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");
+
+ 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);
+ /* Make sure all columns get assigned to a default value */
+ table->use_all_columns();
+ table->no_replicate= 1;
+ /*
+ Don't set automatic timestamps as we may want to use time of logging,
+ not from query start
+ */
+ table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET;
+ }
+ else
+ {
+ /*
+ If error in mysql_lock_tables(), open_ltable doesn't close the
+ table. Thread kill during mysql_lock_tables() is such error. But
+ open tables cannot be accepted when restoring the open tables
+ state.
+ */
+ if (thd->killed)
+ 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()
+ 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)
+{
+ 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);
+}
+
+/**
+ @} (end of group Data_Dictionary)
+*/
diff --git a/sql/sql_binlog.cc b/sql/sql_binlog.cc
new file mode 100644
index 00000000000..96e99b57e3c
--- /dev/null
+++ b/sql/sql_binlog.cc
@@ -0,0 +1,244 @@
+/* Copyright (C) 2005-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 St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "mysql_priv.h"
+#include "rpl_rli.h"
+#include "base64.h"
+
+/**
+ Execute a BINLOG statement.
+
+ To execute the BINLOG command properly the server needs to know
+ which format the BINLOG command's event is in. Therefore, the first
+ BINLOG statement seen must be a base64 encoding of the
+ Format_description_log_event, as outputted by mysqlbinlog. This
+ Format_description_log_event is cached in
+ rli->description_event_for_exec.
+
+ @param thd Pointer to THD object for the client thread executing the
+ statement.
+*/
+
+void mysql_client_binlog_statement(THD* thd)
+{
+ DBUG_ENTER("mysql_client_binlog_statement");
+ DBUG_PRINT("info",("binlog base64: '%*s'",
+ (int) (thd->lex->comment.length < 2048 ?
+ thd->lex->comment.length : 2048),
+ thd->lex->comment.str));
+
+ if (check_global_access(thd, SUPER_ACL))
+ DBUG_VOID_RETURN;
+
+ size_t coded_len= thd->lex->comment.length + 1;
+ size_t decoded_len= base64_needed_decoded_length(coded_len);
+ DBUG_ASSERT(coded_len > 0);
+
+ /*
+ Allocation
+ */
+
+ /*
+ If we do not have a Format_description_event, we create a dummy
+ one here. In this case, the first event we read must be a
+ Format_description_event.
+ */
+ my_bool have_fd_event= TRUE;
+ if (!thd->rli_fake)
+ {
+ thd->rli_fake= new Relay_log_info;
+#ifdef HAVE_purify
+ thd->rli_fake->is_fake= TRUE;
+#endif
+ have_fd_event= FALSE;
+ }
+ if (thd->rli_fake && !thd->rli_fake->relay_log.description_event_for_exec)
+ {
+ thd->rli_fake->relay_log.description_event_for_exec=
+ new Format_description_log_event(4);
+ have_fd_event= FALSE;
+ }
+
+ const char *error= 0;
+ char *buf= (char *) my_malloc(decoded_len, MYF(MY_WME));
+ Log_event *ev = 0;
+
+ /*
+ Out of memory check
+ */
+ if (!(thd->rli_fake &&
+ thd->rli_fake->relay_log.description_event_for_exec &&
+ buf))
+ {
+ my_error(ER_OUTOFMEMORY, MYF(0), 1); /* needed 1 bytes */
+ goto end;
+ }
+
+ thd->rli_fake->sql_thd= thd;
+ thd->rli_fake->no_storage= TRUE;
+
+ for (char const *strptr= thd->lex->comment.str ;
+ strptr < thd->lex->comment.str + thd->lex->comment.length ; )
+ {
+ char const *endptr= 0;
+ int bytes_decoded= base64_decode(strptr, coded_len, buf, &endptr);
+
+#ifndef HAVE_purify
+ /*
+ This debug printout should not be used for valgrind builds
+ since it will read from unassigned memory.
+ */
+ DBUG_PRINT("info",
+ ("bytes_decoded: %d strptr: 0x%lx endptr: 0x%lx ('%c':%d)",
+ bytes_decoded, (long) strptr, (long) endptr, *endptr,
+ *endptr));
+#endif
+
+ if (bytes_decoded < 0)
+ {
+ my_error(ER_BASE64_DECODE_ERROR, MYF(0));
+ goto end;
+ }
+ else if (bytes_decoded == 0)
+ break; // If no bytes where read, the string contained only whitespace
+
+ DBUG_ASSERT(bytes_decoded > 0);
+ DBUG_ASSERT(endptr > strptr);
+ coded_len-= endptr - strptr;
+ strptr= endptr;
+
+ /*
+ Now we have one or more events stored in the buffer. The size of
+ the buffer is computed based on how much base64-encoded data
+ there were, so there should be ample space for the data (maybe
+ even too much, since a statement can consist of a considerable
+ number of events).
+
+ TODO: Switch to use a stream-based base64 encoder/decoder in
+ order to be able to read exactly what is necessary.
+ */
+
+ DBUG_PRINT("info",("binlog base64 decoded_len: %lu bytes_decoded: %d",
+ (ulong) decoded_len, bytes_decoded));
+
+ /*
+ Now we start to read events of the buffer, until there are no
+ more.
+ */
+ for (char *bufptr= buf ; bytes_decoded > 0 ; )
+ {
+ /*
+ Checking that the first event in the buffer is not truncated.
+ */
+ ulong event_len= uint4korr(bufptr + EVENT_LEN_OFFSET);
+ DBUG_PRINT("info", ("event_len=%lu, bytes_decoded=%d",
+ event_len, bytes_decoded));
+ if (bytes_decoded < EVENT_LEN_OFFSET || (uint) bytes_decoded < event_len)
+ {
+ my_error(ER_SYNTAX_ERROR, MYF(0));
+ goto end;
+ }
+
+ /*
+ If we have not seen any Format_description_event, then we must
+ see one; it is the only statement that can be read in base64
+ without a prior Format_description_event.
+ */
+ if (!have_fd_event)
+ {
+ int type = bufptr[EVENT_TYPE_OFFSET];
+ if (type == FORMAT_DESCRIPTION_EVENT || type == START_EVENT_V3)
+ have_fd_event= TRUE;
+ else
+ {
+ my_error(ER_NO_FORMAT_DESCRIPTION_EVENT_BEFORE_BINLOG_STATEMENT,
+ MYF(0), Log_event::get_type_str((Log_event_type)type));
+ goto end;
+ }
+ }
+
+ ev= Log_event::read_log_event(bufptr, event_len, &error,
+ thd->rli_fake->relay_log.
+ description_event_for_exec);
+
+ DBUG_PRINT("info",("binlog base64 err=%s", error));
+ if (!ev)
+ {
+ /*
+ This could actually be an out-of-memory, but it is more likely
+ causes by a bad statement
+ */
+ my_error(ER_SYNTAX_ERROR, MYF(0));
+ goto end;
+ }
+
+ bytes_decoded -= event_len;
+ bufptr += event_len;
+
+ DBUG_PRINT("info",("ev->get_type_code()=%d", ev->get_type_code()));
+#ifndef HAVE_purify
+ /*
+ This debug printout should not be used for valgrind builds
+ since it will read from unassigned memory.
+ */
+ DBUG_PRINT("info",("bufptr+EVENT_TYPE_OFFSET: 0x%lx",
+ (long) (bufptr+EVENT_TYPE_OFFSET)));
+ DBUG_PRINT("info", ("bytes_decoded: %d bufptr: 0x%lx buf[EVENT_LEN_OFFSET]: %lu",
+ bytes_decoded, (long) bufptr,
+ (ulong) uint4korr(bufptr+EVENT_LEN_OFFSET)));
+#endif
+ ev->thd= thd;
+ /*
+ We go directly to the application phase, since we don't need
+ to check if the event shall be skipped or not.
+
+ Neither do we have to update the log positions, since that is
+ not used at all: the rli_fake instance is used only for error
+ reporting.
+ */
+#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
+ if (apply_event_and_update_pos(ev, thd, thd->rli_fake, FALSE))
+ {
+ delete ev;
+ /*
+ TODO: Maybe a better error message since the BINLOG statement
+ now contains several events.
+ */
+ my_error(ER_UNKNOWN_ERROR, MYF(0), "Error executing BINLOG statement");
+ goto end;
+ }
+#endif
+
+ /*
+ Format_description_log_event should not be deleted because it
+ will be used to read info about the relay log's format; it
+ will be deleted when the SQL thread does not need it,
+ i.e. when this thread terminates.
+ */
+ if (ev->get_type_code() != FORMAT_DESCRIPTION_EVENT)
+ delete ev;
+ ev= 0;
+ }
+ }
+
+
+ DBUG_PRINT("info",("binlog base64 execution finished successfully"));
+ my_ok(thd);
+
+end:
+ thd->rli_fake->clear_tables_to_lock();
+ my_free(buf, MYF(MY_ALLOW_ZERO_PTR));
+ DBUG_VOID_RETURN;
+}
diff --git a/sql/sql_bitmap.h b/sql/sql_bitmap.h
index 3b507d64df5..97accefe8aa 100644
--- a/sql/sql_bitmap.h
+++ b/sql/sql_bitmap.h
@@ -24,7 +24,7 @@
template <uint default_width> class Bitmap
{
MY_BITMAP map;
- uchar buffer[(default_width+7)/8];
+ uint32 buffer[(default_width+31)/32];
public:
Bitmap() { init(); }
Bitmap(const Bitmap& from) { *this=from; }
@@ -47,14 +47,14 @@ public:
void intersect(ulonglong map2buff)
{
MY_BITMAP map2;
- bitmap_init(&map2, (uchar *)&map2buff, sizeof(ulonglong)*8, 0);
+ bitmap_init(&map2, (uint32 *)&map2buff, sizeof(ulonglong)*8, 0);
bitmap_intersect(&map, &map2);
}
/* Use highest bit for all bits above sizeof(ulonglong)*8. */
void intersect_extended(ulonglong map2buff)
{
intersect(map2buff);
- if (map.bitmap_size > sizeof(ulonglong))
+ if (map.n_bits > sizeof(ulonglong) * 8)
bitmap_set_above(&map, sizeof(ulonglong),
test(map2buff & (LL(1) << (sizeof(ulonglong) * 8 - 1))));
}
@@ -65,11 +65,12 @@ public:
my_bool is_clear_all() const { return bitmap_is_clear_all(&map); }
my_bool is_set_all() const { return bitmap_is_set_all(&map); }
my_bool is_subset(const Bitmap& map2) const { return bitmap_is_subset(&map, &map2.map); }
+ my_bool is_overlapping(const Bitmap& map2) const { return bitmap_is_overlapping(&map, &map2.map); }
my_bool operator==(const Bitmap& map2) const { return bitmap_cmp(&map, &map2.map); }
char *print(char *buf) const
{
char *s=buf;
- const uchar *e=buffer, *b=e+sizeof(buffer)-1;
+ const uchar *e=(uchar *)buffer, *b=e+sizeof(buffer)-1;
while (!*b && b>e)
b--;
if ((*s=_dig_vec_upper[*b >> 4]) != '0')
@@ -131,6 +132,7 @@ public:
my_bool is_clear_all() const { return map == (ulonglong)0; }
my_bool is_set_all() const { return map == ~(ulonglong)0; }
my_bool is_subset(const Bitmap<64>& map2) const { return !(map & ~map2.map); }
+ my_bool is_overlapping(const Bitmap<64>& map2) const { return (map & map2.map)!= 0; }
my_bool operator==(const Bitmap<64>& map2) const { return map == map2.map; }
char *print(char *buf) const { longlong2str(map,buf,16); return buf; }
ulonglong to_ulonglong() const { return map; }
diff --git a/sql/sql_manager.h b/sql/sql_builtin.cc.in
index 7ba1e9c0de2..3becdbaccfe 100644
--- a/sql/sql_manager.h
+++ b/sql/sql_builtin.cc.in
@@ -1,4 +1,4 @@
-/* Copyright (C) 2000 MySQL AB
+/* Copyright (C) 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
@@ -11,8 +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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include <mysql/plugin.h>
+
+typedef struct st_mysql_plugin builtin_plugin[];
+
+extern builtin_plugin
+ builtin_binlog_plugin@mysql_plugin_defs@;
+
+struct st_mysql_plugin *mysqld_builtins[]=
+{
+ builtin_binlog_plugin@mysql_plugin_defs@,(struct st_mysql_plugin *)0
+};
-#ifdef HAVE_BERKELEY_DB
-void berkeley_cleanup_log_files(void);
-#endif /* HAVE_BERKELEY_DB */
diff --git a/sql/sql_cache.cc b/sql/sql_cache.cc
index fe3f0a09670..7c97ee4cf32 100644
--- a/sql/sql_cache.cc
+++ b/sql/sql_cache.cc
@@ -1,4 +1,4 @@
-/* Copyright (C) 2000 MySQL AB
+/* Copyright 2000-2008 MySQL AB, 2008 Sun Microsystems, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -268,6 +268,39 @@ are stored in one block.
If join_results allocated new block(s) then we need call pack_cache again.
+7. Interface
+The query cache interfaces with the rest of the server code through 7
+functions:
+ 1. Query_cache::send_result_to_client
+ - Called before parsing and used to match a statement with the stored
+ queries hash.
+ If a match is found the cached result set is sent through repeated
+ calls to net_real_write. (note: calling thread doesn't have a regis-
+ tered result set writer: thd->net.query_cache_query=0)
+ 2. Query_cache::store_query
+ - Called just before handle_select() and is used to register a result
+ set writer to the statement currently being processed
+ (thd->net.query_cache_query).
+ 3. query_cache_insert
+ - Called from net_real_write to append a result set to a cached query
+ if (and only if) this query has a registered result set writer
+ (thd->net.query_cache_query).
+ 4. Query_cache::invalidate
+ - Called from various places to invalidate query cache based on data-
+ base, table and myisam file name. During an on going invalidation
+ the query cache is temporarily disabled.
+ 5. Query_cache::flush
+ - Used when a RESET QUERY CACHE is issued. This clears the entire
+ cache block by block.
+ 6. Query_cache::resize
+ - Used to change the available memory used by the query cache. This
+ will also invalidate the entrie query cache in one free operation.
+ 7. Query_cache::pack
+ - Used when a FLUSH QUERY CACHE is issued. This changes the order of
+ the used memory blocks in physical memory order and move all avail-
+ able memory to the 'bottom' of the memory.
+
+
TODO list:
- Delayed till after-parsing qache answer (for column rights processing)
@@ -298,12 +331,8 @@ TODO list:
#include <m_ctype.h>
#include <my_dir.h>
#include <hash.h>
-#include "ha_myisammrg.h"
-#ifndef MASTER
-#include "../srclib/myisammrg/myrg_def.h"
-#else
-#include "../myisammrg/myrg_def.h"
-#endif
+#include "../storage/myisammrg/ha_myisammrg.h"
+#include "../storage/myisammrg/myrg_def.h"
#ifdef EMBEDDED_LIBRARY
#include "emb_qcache.h"
@@ -342,6 +371,32 @@ TODO list:
__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)
+{
+ DBUG_ENTER("debug_wait_for_kill");
+ const char *prev_info;
+ THD *thd;
+ thd= current_thd;
+ prev_info= thd->proc_info;
+ thd->proc_info= info;
+ sql_print_information(info);
+ while(!thd->killed)
+ my_sleep(1000);
+ thd->killed= THD::NOT_KILLED;
+ 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)
@@ -369,7 +424,7 @@ TYPELIB query_cache_type_typelib=
inline Query_cache_block * Query_cache_block_table::block()
{
- return (Query_cache_block *)(((byte*)this) -
+ return (Query_cache_block *)(((uchar*)this) -
ALIGN_SIZE(sizeof(Query_cache_block_table)*n) -
ALIGN_SIZE(sizeof(Query_cache_block)));
}
@@ -405,9 +460,9 @@ inline uint Query_cache_block::headers_len()
ALIGN_SIZE(sizeof(Query_cache_block)));
}
-inline gptr Query_cache_block::data(void)
+inline uchar* Query_cache_block::data(void)
{
- return (gptr)( ((byte*)this) + headers_len() );
+ return (uchar*)( ((uchar*)this) + headers_len() );
}
inline Query_cache_query * Query_cache_block::query()
@@ -441,7 +496,7 @@ inline Query_cache_result * Query_cache_block::result()
inline Query_cache_block_table * Query_cache_block::table(TABLE_COUNTER_TYPE n)
{
return ((Query_cache_block_table *)
- (((byte*)this)+ALIGN_SIZE(sizeof(Query_cache_block)) +
+ (((uchar*)this)+ALIGN_SIZE(sizeof(Query_cache_block)) +
n*sizeof(Query_cache_block_table)));
}
@@ -452,13 +507,13 @@ inline Query_cache_block_table * Query_cache_block::table(TABLE_COUNTER_TYPE n)
extern "C"
{
-byte *query_cache_table_get_key(const byte *record, uint *length,
+uchar *query_cache_table_get_key(const uchar *record, size_t *length,
my_bool not_used __attribute__((unused)))
{
Query_cache_block* table_block = (Query_cache_block*) record;
*length = (table_block->used - table_block->headers_len() -
ALIGN_SIZE(sizeof(Query_cache_table)));
- return (((byte *) table_block->data()) +
+ return (((uchar *) table_block->data()) +
ALIGN_SIZE(sizeof(Query_cache_table)));
}
}
@@ -526,7 +581,7 @@ void Query_cache_query::init_n_lock()
my_rwlock_init(&lock, NULL);
lock_writing();
DBUG_PRINT("qcache", ("inited & locked query for block 0x%lx",
- (long) (((byte*) this) -
+ (long) (((uchar*) this) -
ALIGN_SIZE(sizeof(Query_cache_block)))));
DBUG_VOID_RETURN;
}
@@ -536,7 +591,7 @@ void Query_cache_query::unlock_n_destroy()
{
DBUG_ENTER("Query_cache_query::unlock_n_destroy");
DBUG_PRINT("qcache", ("destroyed & unlocked query for block 0x%lx",
- (long) (((byte*) this) -
+ (long) (((uchar*) this) -
ALIGN_SIZE(sizeof(Query_cache_block)))));
/*
The following call is not needed on system where one can destroy an
@@ -550,13 +605,13 @@ void Query_cache_query::unlock_n_destroy()
extern "C"
{
-byte *query_cache_query_get_key(const byte *record, uint *length,
+uchar *query_cache_query_get_key(const uchar *record, size_t *length,
my_bool not_used)
{
Query_cache_block *query_block = (Query_cache_block*) record;
*length = (query_block->used - query_block->headers_len() -
ALIGN_SIZE(sizeof(Query_cache_query)));
- return (((byte *) query_block->data()) +
+ return (((uchar *) query_block->data()) +
ALIGN_SIZE(sizeof(Query_cache_query)));
}
}
@@ -618,50 +673,60 @@ void query_cache_insert(NET *net, const char *packet, ulong length)
if (net->query_cache_query == 0)
DBUG_VOID_RETURN;
- STRUCT_LOCK(&query_cache.structure_guard_mutex);
+ DBUG_EXECUTE_IF("wait_in_query_cache_insert",
+ debug_wait_for_kill("wait_in_query_cache_insert"); );
- if (unlikely(query_cache.query_cache_size == 0 ||
- query_cache.flush_in_progress))
+ STRUCT_LOCK(&query_cache.structure_guard_mutex);
+ bool interrupt;
+ query_cache.wait_while_table_flush_is_in_progress(&interrupt);
+ if (interrupt)
{
STRUCT_UNLOCK(&query_cache.structure_guard_mutex);
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_block*)net->query_cache_query;
+ if (!query_block)
{
- Query_cache_query *header = query_block->query();
- Query_cache_block *result = header->result();
-
- DUMP(&query_cache);
- BLOCK_LOCK_WR(query_block);
- DBUG_PRINT("qcache", ("insert packet %lu bytes long",length));
-
/*
- On success STRUCT_UNLOCK(&query_cache.structure_guard_mutex) will be
- done by query_cache.append_result_data if success (if not we need
- query_cache.structure_guard_mutex locked to free query)
+ We lost the writer and the currently processed query has been
+ invalidated; there is nothing left to do.
*/
- if (!query_cache.append_result_data(&result, length, (gptr) packet,
- query_block))
- {
- DBUG_PRINT("warning", ("Can't append data"));
- header->result(result);
- DBUG_PRINT("qcache", ("free query 0x%lx", (ulong) query_block));
- // The following call will remove the lock on query_block
- query_cache.free_query(query_block);
- // append_result_data no success => we need unlock
- STRUCT_UNLOCK(&query_cache.structure_guard_mutex);
- DBUG_VOID_RETURN;
- }
- header->result(result);
- header->last_pkt_nr= net->pkt_nr;
- BLOCK_UNLOCK_WR(query_block);
- DBUG_EXECUTE("check_querycache",query_cache.check_integrity(0););
+ STRUCT_UNLOCK(&query_cache.structure_guard_mutex);
+ DBUG_VOID_RETURN;
}
- else
+
+ BLOCK_LOCK_WR(query_block);
+ Query_cache_query *header= query_block->query();
+ Query_cache_block *result= header->result();
+
+ DUMP(&query_cache);
+ DBUG_PRINT("qcache", ("insert packet %lu bytes long",length));
+
+ /*
+ On success, STRUCT_UNLOCK is done by append_result_data. Otherwise, we
+ 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))
+ {
+ DBUG_PRINT("warning", ("Can't append data"));
+ header->result(result);
+ DBUG_PRINT("qcache", ("free query 0x%lx", (ulong) query_block));
+ // The following call will remove the lock on query_block
+ query_cache.free_query(query_block);
+ query_cache.refused++;
+ // append_result_data no success => we need unlock
STRUCT_UNLOCK(&query_cache.structure_guard_mutex);
+ DBUG_VOID_RETURN;
+ }
+
+ header->result(result);
+ header->last_pkt_nr= net->pkt_nr;
+ BLOCK_UNLOCK_WR(query_block);
+ DBUG_EXECUTE("check_querycache",query_cache.check_integrity(0););
+
DBUG_VOID_RETURN;
}
@@ -669,24 +734,30 @@ void query_cache_insert(NET *net, const char *packet, ulong length)
void query_cache_abort(NET *net)
{
DBUG_ENTER("query_cache_abort");
+ THD *thd= current_thd;
/* See the comment on double-check locking usage above. */
if (net->query_cache_query == 0)
DBUG_VOID_RETURN;
STRUCT_LOCK(&query_cache.structure_guard_mutex);
-
- if (unlikely(query_cache.query_cache_size == 0 ||
- query_cache.flush_in_progress))
+ bool interrupt;
+ query_cache.wait_while_table_flush_is_in_progress(&interrupt);
+ if (interrupt)
{
STRUCT_UNLOCK(&query_cache.structure_guard_mutex);
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);
- if (query_block) // Test if changed by other thread
+ if (query_block)
{
+ thd_proc_info(thd, "storing result in query cache");
DUMP(&query_cache);
BLOCK_LOCK_WR(query_block);
// The following call will remove the lock on query_block
@@ -710,12 +781,10 @@ void query_cache_end_of_result(THD *thd)
if (thd->net.query_cache_query == 0)
DBUG_VOID_RETURN;
- /*
- Check if the NET layer raised a unreported error -- my_error() and
- as a consequence query_cache_abort() haven't been called. Abort the
- cached result as it might be only partially complete.
- */
- if (thd->killed || thd->net.report_error)
+ /* Ensure that only complete results are cached. */
+ DBUG_ASSERT(thd->main_da.is_eof());
+
+ if (thd->killed)
{
query_cache_abort(&thd->net);
DBUG_VOID_RETURN;
@@ -728,13 +797,23 @@ void query_cache_end_of_result(THD *thd)
STRUCT_LOCK(&query_cache.structure_guard_mutex);
- if (unlikely(query_cache.query_cache_size == 0 ||
- query_cache.flush_in_progress))
- goto end;
+ bool interrupt;
+ query_cache.wait_while_table_flush_is_in_progress(&interrupt);
+ if (interrupt)
+ {
+ STRUCT_UNLOCK(&query_cache.structure_guard_mutex);
+ DBUG_VOID_RETURN;
+ }
query_block= ((Query_cache_block*) thd->net.query_cache_query);
if (query_block)
{
+ /*
+ The writer is still present; finish last result block by chopping it to
+ suitable size if needed and setting block type. Since this is the last
+ block, the writer should be dropped.
+ */
+ thd_proc_info(thd, "storing result in query cache");
DUMP(&query_cache);
BLOCK_LOCK_WR(query_block);
Query_cache_query *header= query_block->query();
@@ -753,7 +832,8 @@ void query_cache_end_of_result(THD *thd)
*/
DBUG_ASSERT(0);
query_cache.free_query(query_block);
- goto end;
+ STRUCT_UNLOCK(&query_cache.structure_guard_mutex);
+ DBUG_VOID_RETURN;
}
last_result_block= header->result()->prev;
@@ -764,14 +844,16 @@ void query_cache_end_of_result(THD *thd)
header->found_rows(current_thd->limit_found_rows);
header->result()->type= Query_cache_block::RESULT;
+
+ /* Drop the writer. */
header->writer(0);
thd->net.query_cache_query= 0;
+
BLOCK_UNLOCK_WR(query_block);
DBUG_EXECUTE("check_querycache",query_cache.check_integrity(1););
}
-end:
STRUCT_UNLOCK(&query_cache.structure_guard_mutex);
DBUG_VOID_RETURN;
}
@@ -783,6 +865,18 @@ void query_cache_invalidate_by_MyISAM_filename(const char *filename)
}
+/*
+ The following function forms part of the C plugin API
+*/
+extern "C"
+void mysql_query_cache_invalidate4(THD *thd,
+ const char *key, unsigned key_length,
+ int using_trx)
+{
+ query_cache.invalidate(thd, key, (uint32) key_length, (my_bool) using_trx);
+}
+
+
/*****************************************************************************
Query_cache methods
*****************************************************************************/
@@ -813,16 +907,56 @@ Query_cache::Query_cache(ulong query_cache_limit_arg,
ulong Query_cache::resize(ulong query_cache_size_arg)
{
+ ulong new_query_cache_size;
DBUG_ENTER("Query_cache::resize");
DBUG_PRINT("qcache", ("from %lu to %lu",query_cache_size,
query_cache_size_arg));
DBUG_ASSERT(initialized);
+
STRUCT_LOCK(&structure_guard_mutex);
+ while (is_flushing())
+ pthread_cond_wait(&COND_cache_status_changed, &structure_guard_mutex);
+ m_cache_status= Query_cache::FLUSH_IN_PROGRESS;
+ STRUCT_UNLOCK(&structure_guard_mutex);
+
+ /*
+ Wait for all readers and writers to exit. When the list of all queries
+ is iterated over with a block level lock, we are done.
+ */
+ Query_cache_block *block= queries_blocks;
+ if (block)
+ {
+ do
+ {
+ BLOCK_LOCK_WR(block);
+ Query_cache_query *query= block->query();
+ if (query && query->writer())
+ {
+ /*
+ 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(0);
+ refused++;
+ }
+ BLOCK_UNLOCK_WR(block);
+ block= block->next;
+ } while (block != queries_blocks);
+ }
free_cache();
+
query_cache_size= query_cache_size_arg;
- ::query_cache_size= init_cache();
+ new_query_cache_size= init_cache();
+
+ STRUCT_LOCK(&structure_guard_mutex);
+ m_cache_status= Query_cache::NO_FLUSH_IN_PROGRESS;
+ pthread_cond_signal(&COND_cache_status_changed);
+ if (new_query_cache_size)
+ DBUG_EXECUTE("check_querycache",check_integrity(1););
STRUCT_UNLOCK(&structure_guard_mutex);
- DBUG_RETURN(::query_cache_size);
+
+ DBUG_RETURN(new_query_cache_size);
}
@@ -862,6 +996,12 @@ void Query_cache::store_query(THD *thd, TABLE_LIST *tables_used)
flags.client_long_flag= test(thd->client_capabilities & CLIENT_LONG_FLAG);
flags.client_protocol_41= test(thd->client_capabilities &
CLIENT_PROTOCOL_41);
+ /*
+ Protocol influences result format, so statement results in the binary
+ protocol (COM_EXECUTE) cannot be served to statements asking for results
+ in the text protocol (COM_QUERY) and vice-versa.
+ */
+ flags.result_in_binary_protocol= (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);
@@ -883,12 +1023,14 @@ void Query_cache::store_query(THD *thd, TABLE_LIST *tables_used)
flags.group_concat_max_len= thd->variables.group_concat_max_len;
flags.div_precision_increment= thd->variables.div_precincrement;
flags.default_week_format= thd->variables.default_week_format;
- DBUG_PRINT("qcache", ("long %d, 4.1: %d, more results %d, pkt_nr: %d, \
+ 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, \
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.more_results_exists,
flags.pkt_nr,
flags.character_set_client_num,
@@ -911,8 +1053,13 @@ def_week_frmt: %lu, in_trans: %d, autocommit: %d",
ha_release_temporary_latches(thd);
STRUCT_LOCK(&structure_guard_mutex);
- if (query_cache_size == 0 || flush_in_progress)
+ if (query_cache_size == 0 || is_flushing())
{
+ /*
+ A table- or a full flush operation can potentially take a long time to
+ finish. We choose not to wait for them and skip caching statements
+ instead.
+ */
STRUCT_UNLOCK(&structure_guard_mutex);
DBUG_VOID_RETURN;
}
@@ -947,15 +1094,15 @@ 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, (byte*) thd->query, tot_length);
+ hash_search(&queries, (uchar*) thd->query, tot_length);
DBUG_PRINT("qcache", ("competitor 0x%lx", (ulong) competitor));
if (competitor == 0)
{
/* Query is not in cache and no one is working with it; Store it */
Query_cache_block *query_block;
- query_block= write_block_data(tot_length, (gptr) thd->query,
+ query_block= write_block_data(tot_length, (uchar*) thd->query,
ALIGN_SIZE(sizeof(Query_cache_query)),
- Query_cache_block::QUERY, local_tables, 1);
+ Query_cache_block::QUERY, local_tables);
if (query_block != 0)
{
DBUG_PRINT("qcache", ("query block 0x%lx allocated, %lu",
@@ -963,7 +1110,7 @@ def_week_frmt: %lu, in_trans: %d, autocommit: %d",
Query_cache_query *header = query_block->query();
header->init_n_lock();
- if (my_hash_insert(&queries, (byte*) query_block))
+ if (my_hash_insert(&queries, (uchar*) query_block))
{
refused++;
DBUG_PRINT("qcache", ("insertion in query hash"));
@@ -976,7 +1123,7 @@ def_week_frmt: %lu, in_trans: %d, autocommit: %d",
{
refused++;
DBUG_PRINT("warning", ("tables list including failed"));
- hash_delete(&queries, (byte *) query_block);
+ hash_delete(&queries, (uchar *) query_block);
header->unlock_n_destroy();
free_memory_block(query_block);
STRUCT_UNLOCK(&structure_guard_mutex);
@@ -985,7 +1132,7 @@ 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= (gptr) query_block;
+ net->query_cache_query= (uchar*) query_block;
header->writer(net);
header->tables_type(tables_type);
@@ -1042,15 +1189,6 @@ Query_cache::send_result_to_client(THD *thd, char *sql, uint query_length)
Query_cache_block_table *block_table, *block_table_end;
ulong tot_length;
Query_cache_query_flags flags;
-#ifndef __WIN__
- const uint spin_treshold= 50000;
- const double lock_time_treshold= 0.1; /* Time in seconds */
- uint spin_count= 0;
- int lock_status= 0;
- ulong new_time= 0;
- ulong stop_time= 0;
-#endif
-
DBUG_ENTER("Query_cache::send_result_to_client");
/*
@@ -1081,7 +1219,7 @@ Query_cache::send_result_to_client(THD *thd, char *sql, uint query_length)
/*
Test if the query is a SELECT
- (pre-space is removed in dispatch_command)
+ (pre-space is removed in dispatch_command).
First '/' looks like comment before command it is not
frequently appeared in real life, consequently we can
@@ -1097,40 +1235,22 @@ Query_cache::send_result_to_client(THD *thd, char *sql, uint query_length)
}
}
-#ifdef __WIN__
STRUCT_LOCK(&structure_guard_mutex);
-#else
- stop_time= my_clock()+(ulong)lock_time_treshold*CLOCKS_PER_SEC;
- while ((lock_status= pthread_mutex_trylock(&structure_guard_mutex)) == EBUSY
- && spin_count < spin_treshold
- && new_time < stop_time)
- {
- spin_count++;
- if (spin_count%5)
- new_time= my_clock();
- my_sleep(0);
- }
- if (lock_status != 0)
- {
- /*
- Query cache is too busy doing something else.
- Fall back on ordinary statement execution. We also mark this
- query as unsafe to cache because otherwise this thread will
- still be halted when the result set is stored to the cache.
- */
- thd->lex->safe_to_cache_query= FALSE;
- goto err;
- }
-#endif
+ if (query_cache_size == 0)
+ goto err_unlock;
- if (query_cache_size == 0 || flush_in_progress)
+ if (is_flushing())
{
- DBUG_PRINT("qcache", ("query cache disabled"));
+ /* Return; Query cache is temporarily disabled while we flush. */
+ DBUG_PRINT("qcache",("query cache disabled"));
goto err_unlock;
}
- /* Check that we haven't forgot to reset the query cache variables */
+ /*
+ 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;
@@ -1147,11 +1267,14 @@ Query_cache::send_result_to_client(THD *thd, char *sql, uint query_length)
DBUG_PRINT("qcache", ("No active database"));
}
+ thd_proc_info(thd, "checking query cache for query");
+
// fill all gaps between fields with 0 to get repeatable key
bzero(&flags, QUERY_CACHE_FLAGS_SIZE);
flags.client_long_flag= test(thd->client_capabilities & CLIENT_LONG_FLAG);
flags.client_protocol_41= test(thd->client_capabilities &
CLIENT_PROTOCOL_41);
+ flags.result_in_binary_protocol= (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);
@@ -1171,12 +1294,14 @@ Query_cache::send_result_to_client(THD *thd, char *sql, uint query_length)
flags.div_precision_increment= thd->variables.div_precincrement;
flags.default_week_format= thd->variables.default_week_format;
flags.lc_time_names= thd->variables.lc_time_names;
- DBUG_PRINT("qcache", ("long %d, 4.1: %d, more results %d, pkt_nr: %d, \
+ 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, \
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.more_results_exists,
flags.pkt_nr,
flags.character_set_client_num,
@@ -1191,9 +1316,9 @@ def_week_frmt: %lu, in_trans: %d, autocommit: %d",
flags.default_week_format,
(int)flags.in_trans,
(int)flags.autocommit));
- memcpy((void *)(sql + (tot_length - QUERY_CACHE_FLAGS_SIZE)),
- &flags, QUERY_CACHE_FLAGS_SIZE);
- query_block = (Query_cache_block *) hash_search(&queries, (byte*) sql,
+ 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);
/* Quick abort on unlocked data */
if (query_block == 0 ||
@@ -1230,6 +1355,7 @@ def_week_frmt: %lu, in_trans: %d, autocommit: %d",
}
// Check access;
+ thd_proc_info(thd, "checking privileges on cached query");
block_table= query_block->table(0);
block_table_end= block_table+query_block->n_tables;
for (; block_table != block_table_end; block_table++)
@@ -1246,9 +1372,9 @@ def_week_frmt: %lu, in_trans: %d, autocommit: %d",
*/
for (tmptable= thd->temporary_tables; tmptable ; tmptable= tmptable->next)
{
- if (tmptable->s->key_length - TMP_TABLE_KEY_EXTRA ==
+ if (tmptable->s->table_cache_key.length - TMP_TABLE_KEY_EXTRA ==
table->key_length() &&
- !memcmp(tmptable->s->table_cache_key, table->data(),
+ !memcmp(tmptable->s->table_cache_key.str, table->data(),
table->key_length()))
{
DBUG_PRINT("qcache",
@@ -1270,7 +1396,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))
+ if (check_table_access(thd,SELECT_ACL,&table_list, 1, TRUE))
{
DBUG_PRINT("qcache",
("probably no SELECT access to %s.%s => return to normal processing",
@@ -1304,7 +1430,9 @@ def_week_frmt: %lu, in_trans: %d, autocommit: %d",
("Handler require invalidation queries of %s.%s %lu-%lu",
table_list.db, table_list.alias,
(ulong) engine_data, (ulong) table->engine_data()));
- invalidate_table((byte *) table->db(), table->key_length());
+ invalidate_table_internal(thd,
+ (uchar *) table->db(),
+ table->key_length());
}
else
thd->lex->safe_to_cache_query= 0; // Don't try to cache this
@@ -1322,6 +1450,7 @@ def_week_frmt: %lu, in_trans: %d, autocommit: %d",
Send cached result to client
*/
#ifndef EMBEDDED_LIBRARY
+ thd_proc_info(thd, "sending cached result to client");
do
{
DBUG_PRINT("qcache", ("Results (len: %lu used: %lu headers: %lu)",
@@ -1348,6 +1477,7 @@ def_week_frmt: %lu, in_trans: %d, autocommit: %d",
thd->limit_found_rows = query->found_rows();
thd->status_var.last_query_cost= 0.0;
+ thd->main_da.disable_status();
BLOCK_UNLOCK_RD(query_block);
DBUG_RETURN(1); // Result sent to client
@@ -1367,32 +1497,26 @@ void Query_cache::invalidate(THD *thd, TABLE_LIST *tables_used,
my_bool using_transactions)
{
DBUG_ENTER("Query_cache::invalidate (table list)");
- STRUCT_LOCK(&structure_guard_mutex);
- if (query_cache_size > 0 && !flush_in_progress)
- {
- DUMP(this);
- using_transactions= using_transactions &&
- (thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN));
- for (; tables_used; tables_used= tables_used->next_local)
- {
- DBUG_ASSERT(!using_transactions || tables_used->table!=0);
- if (tables_used->derived)
- continue;
- if (using_transactions &&
- (tables_used->table->file->table_cache_type() ==
- HA_CACHE_TBL_TRANSACT))
- /*
- Tables_used->table can't be 0 in transaction.
- Only 'drop' invalidate not opened table, but 'drop'
- force transaction finish.
- */
- thd->add_changed_table(tables_used->table);
- else
- invalidate_table(tables_used);
- }
+ using_transactions= using_transactions &&
+ (thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN));
+ for (; tables_used; tables_used= tables_used->next_local)
+ {
+ DBUG_ASSERT(!using_transactions || tables_used->table!=0);
+ if (tables_used->derived)
+ continue;
+ if (using_transactions &&
+ (tables_used->table->file->table_cache_type() ==
+ HA_CACHE_TBL_TRANSACT))
+ /*
+ tables_used->table can't be 0 in transaction.
+ Only 'drop' invalidate not opened table, but 'drop'
+ force transaction finish.
+ */
+ thd->add_changed_table(tables_used->table);
+ else
+ invalidate_table(thd, tables_used);
}
- STRUCT_UNLOCK(&structure_guard_mutex);
DBUG_VOID_RETURN;
}
@@ -1400,21 +1524,14 @@ void Query_cache::invalidate(THD *thd, TABLE_LIST *tables_used,
void Query_cache::invalidate(CHANGED_TABLE_LIST *tables_used)
{
DBUG_ENTER("Query_cache::invalidate (changed table list)");
- if (tables_used)
+ THD *thd= current_thd;
+ for (; tables_used; tables_used= tables_used->next)
{
- STRUCT_LOCK(&structure_guard_mutex);
- if (query_cache_size > 0 && !flush_in_progress)
- {
- DUMP(this);
- for (; tables_used; tables_used= tables_used->next)
- {
- invalidate_table((byte*) tables_used->key, tables_used->key_length);
- DBUG_PRINT("qcache", ("db: %s table: %s", tables_used->key,
- tables_used->key+
- strlen(tables_used->key)+1));
- }
- }
- STRUCT_UNLOCK(&structure_guard_mutex);
+ thd_proc_info(thd, "invalidating query cache entries (table list)");
+ invalidate_table(thd, (uchar*) tables_used->key, tables_used->key_length);
+ DBUG_PRINT("qcache", ("db: %s table: %s", tables_used->key,
+ tables_used->key+
+ strlen(tables_used->key)+1));
}
DBUG_VOID_RETURN;
}
@@ -1432,21 +1549,16 @@ void Query_cache::invalidate(CHANGED_TABLE_LIST *tables_used)
*/
void Query_cache::invalidate_locked_for_write(TABLE_LIST *tables_used)
{
+ THD *thd= current_thd;
DBUG_ENTER("Query_cache::invalidate_locked_for_write");
- if (tables_used)
+ for (; tables_used; tables_used= tables_used->next_local)
{
- STRUCT_LOCK(&structure_guard_mutex);
- if (query_cache_size > 0 && !flush_in_progress)
+ thd_proc_info(thd, "invalidating query cache entries (table)");
+ if (tables_used->lock_type >= TL_WRITE_ALLOW_WRITE &&
+ tables_used->table)
{
- DUMP(this);
- for (; tables_used; tables_used= tables_used->next_local)
- {
- if (tables_used->lock_type & (TL_WRITE_LOW_PRIORITY | TL_WRITE) &&
- tables_used->table)
- invalidate_table(tables_used->table);
- }
+ invalidate_table(thd, tables_used->table);
}
- STRUCT_UNLOCK(&structure_guard_mutex);
}
DBUG_VOID_RETURN;
}
@@ -1460,18 +1572,14 @@ void Query_cache::invalidate(THD *thd, TABLE *table,
{
DBUG_ENTER("Query_cache::invalidate (table)");
- STRUCT_LOCK(&structure_guard_mutex);
- if (query_cache_size > 0 && !flush_in_progress)
- {
- using_transactions= using_transactions &&
- (thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN));
- if (using_transactions &&
- (table->file->table_cache_type() == HA_CACHE_TBL_TRANSACT))
- thd->add_changed_table(table);
- else
- invalidate_table(table);
- }
- STRUCT_UNLOCK(&structure_guard_mutex);
+ using_transactions= using_transactions &&
+ (thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN));
+ if (using_transactions &&
+ (table->file->table_cache_type() == HA_CACHE_TBL_TRANSACT))
+ thd->add_changed_table(table);
+ else
+ invalidate_table(thd, table);
+
DBUG_VOID_RETURN;
}
@@ -1480,56 +1588,130 @@ void Query_cache::invalidate(THD *thd, const char *key, uint32 key_length,
my_bool using_transactions)
{
DBUG_ENTER("Query_cache::invalidate (key)");
-
- STRUCT_LOCK(&structure_guard_mutex);
- if (query_cache_size > 0 && !flush_in_progress)
- {
- using_transactions= using_transactions &&
- (thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN));
- if (using_transactions) // used for innodb => has_transactions() is TRUE
- thd->add_changed_table(key, key_length);
- else
- invalidate_table((byte*)key, key_length);
- }
- STRUCT_UNLOCK(&structure_guard_mutex);
+
+ using_transactions= using_transactions &&
+ (thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN));
+ if (using_transactions) // used for innodb => has_transactions() is TRUE
+ thd->add_changed_table(key, key_length);
+ else
+ invalidate_table(thd, (uchar*)key, key_length);
DBUG_VOID_RETURN;
}
-/*
- Remove all cached queries that uses the given database
+
+/**
+ Synchronize the thread with any flushing operations.
+
+ This helper function is called whenever a thread needs to operate on the
+ query cache structure (example: during invalidation). If a table flush is in
+ progress this function will wait for it to stop. If a full flush is in
+ progress, the function will set the interrupt parameter to indicate that the
+ current operation is redundant and should be interrupted.
+
+ @param[out] interrupt This out-parameter will be set to TRUE if the calling
+ function is redundant and should be interrupted.
+
+ @return If the interrupt-parameter is TRUE then m_cache_status is set to
+ NO_FLUSH_IN_PROGRESS. If the interrupt-parameter is FALSE then
+ m_cache_status is set to FLUSH_IN_PROGRESS.
+ The structure_guard_mutex will in any case be locked.
+*/
+
+void Query_cache::wait_while_table_flush_is_in_progress(bool *interrupt)
+{
+ while (is_flushing())
+ {
+ /*
+ If there already is a full flush in progress query cache isn't enabled
+ and additional flushes are redundant; just return instead.
+ */
+ if (m_cache_status == Query_cache::FLUSH_IN_PROGRESS)
+ {
+ *interrupt= TRUE;
+ return;
+ }
+ /*
+ If a table flush is in progress; wait on cache status to change.
+ */
+ if (m_cache_status == Query_cache::TABLE_FLUSH_IN_PROGRESS)
+ pthread_cond_wait(&COND_cache_status_changed, &structure_guard_mutex);
+ }
+ *interrupt= FALSE;
+}
+
+
+/**
+ Remove all cached queries that uses the given database.
*/
void Query_cache::invalidate(char *db)
{
+ bool restart= FALSE;
DBUG_ENTER("Query_cache::invalidate (db)");
+
STRUCT_LOCK(&structure_guard_mutex);
- if (query_cache_size > 0 && !flush_in_progress)
+ bool interrupt;
+ wait_while_table_flush_is_in_progress(&interrupt);
+ if (interrupt)
+ {
+ STRUCT_UNLOCK(&structure_guard_mutex);
+ return;
+ }
+
+ THD *thd= current_thd;
+
+ if (query_cache_size > 0)
{
- DUMP(this);
- restart_search:
if (tables_blocks)
{
- Query_cache_block *curr= tables_blocks;
- Query_cache_block *next;
- do
- {
- next= curr->next;
- if (strcmp(db, (char*)(curr->table()->db())) == 0)
- invalidate_table(curr);
+ Query_cache_block *table_block = tables_blocks;
+ do {
+ restart= FALSE;
+ do
+ {
+ Query_cache_block *next= table_block->next;
+ Query_cache_table *table = table_block->table();
+ if (strcmp(table->db(),db) == 0)
+ {
+ Query_cache_block_table *list_root= table_block->table(0);
+ invalidate_query_block_list(thd,list_root);
+ }
+
+ table_block= next;
+
+ /*
+ If our root node to used tables became null then the last element
+ in the table list was removed when a query was invalidated;
+ Terminate the search.
+ */
+ if (tables_blocks == 0)
+ {
+ table_block= tables_blocks;
+ }
+ /*
+ If the iterated list has changed underlying structure;
+ we need to restart the search.
+ */
+ else if (table_block->type == Query_cache_block::FREE)
+ {
+ restart= TRUE;
+ table_block= tables_blocks;
+ }
+ /*
+ The used tables are linked in a circular list;
+ loop until we return to the begining.
+ */
+ } while (table_block != tables_blocks);
/*
- invalidate_table can freed block on which point 'next' (if
- table of this block used only in queries which was deleted
- by invalidate_table). As far as we do not allocate new blocks
- and mark all headers of freed blocks as 'FREE' (even if they are
- merged with other blocks) we can just test type of block
- to be sure that block is not deleted
+ Invalidating a table will also mean that all cached queries using
+ this table also will be invalidated. This will in turn change the
+ list of tables associated with these queries and the linked list of
+ used table will be changed. Because of this we might need to restart
+ the search when a table has been invalidated.
*/
- if (next->type == Query_cache_block::FREE)
- goto restart_search;
- curr= next;
- } while (curr != tables_blocks);
- }
+ } while (restart);
+ } // end if( tables_blocks )
}
STRUCT_UNLOCK(&structure_guard_mutex);
@@ -1541,21 +1723,12 @@ void Query_cache::invalidate_by_MyISAM_filename(const char *filename)
{
DBUG_ENTER("Query_cache::invalidate_by_MyISAM_filename");
- STRUCT_LOCK(&structure_guard_mutex);
- if (query_cache_size > 0 && !flush_in_progress)
- {
- /* Calculate the key outside the lock to make the lock shorter */
- char key[MAX_DBKEY_LENGTH];
- uint32 db_length;
- uint key_length= filename_2_table_key(key, filename, &db_length);
- Query_cache_block *table_block;
- if ((table_block = (Query_cache_block*) hash_search(&tables,
- (byte*) key,
- key_length)))
- invalidate_table(table_block);
- }
- STRUCT_UNLOCK(&structure_guard_mutex);
-
+ /* Calculate the key outside the lock to make the lock shorter */
+ char key[MAX_DBKEY_LENGTH];
+ uint32 db_length;
+ uint key_length= filename_2_table_key(key, filename, &db_length);
+ THD *thd= current_thd;
+ invalidate_table(thd,(uchar *)key, key_length);
DBUG_VOID_RETURN;
}
@@ -1577,16 +1750,43 @@ void Query_cache::flush()
DBUG_VOID_RETURN;
}
- /* Join result in cache in 1 block (if result length > join_limit) */
+
+/**
+ Rearrange the memory blocks and join result in cache in 1 block (if
+ result length > join_limit)
+
+ @param[in] join_limit If the minimum length of a result block to be joined.
+ @param[in] iteration_limit The maximum number of packing and joining
+ sequences.
+
+*/
void Query_cache::pack(ulong join_limit, uint iteration_limit)
{
DBUG_ENTER("Query_cache::pack");
+
+ bool interrupt;
+ STRUCT_LOCK(&structure_guard_mutex);
+ wait_while_table_flush_is_in_progress(&interrupt);
+ if (interrupt)
+ {
+ STRUCT_UNLOCK(&structure_guard_mutex);
+ DBUG_VOID_RETURN;
+ }
+
+ if (query_cache_size == 0)
+ {
+ STRUCT_UNLOCK(&structure_guard_mutex);
+ DBUG_VOID_RETURN;
+ }
+
uint i = 0;
do
{
pack_cache();
} while ((++i < iteration_limit) && join_results(join_limit));
+
+ STRUCT_UNLOCK(&structure_guard_mutex);
DBUG_VOID_RETURN;
}
@@ -1605,7 +1805,7 @@ void Query_cache::destroy()
free_cache();
STRUCT_UNLOCK(&structure_guard_mutex);
- pthread_cond_destroy(&COND_flush_finished);
+ pthread_cond_destroy(&COND_cache_status_changed);
pthread_mutex_destroy(&structure_guard_mutex);
initialized = 0;
}
@@ -1621,8 +1821,8 @@ void Query_cache::init()
{
DBUG_ENTER("Query_cache::init");
pthread_mutex_init(&structure_guard_mutex,MY_MUTEX_INIT_FAST);
- pthread_cond_init(&COND_flush_finished, NULL);
- flush_in_progress= FALSE;
+ pthread_cond_init(&COND_cache_status_changed, NULL);
+ m_cache_status= Query_cache::NO_FLUSH_IN_PROGRESS;
initialized = 1;
DBUG_VOID_RETURN;
}
@@ -1636,8 +1836,9 @@ ulong Query_cache::init_cache()
int align;
DBUG_ENTER("Query_cache::init_cache");
+
approx_additional_data_size = (sizeof(Query_cache) +
- sizeof(gptr)*(def_query_hash_size+
+ sizeof(uchar*)*(def_query_hash_size+
def_table_hash_size));
if (query_cache_size < approx_additional_data_size)
goto err;
@@ -1693,7 +1894,7 @@ ulong Query_cache::init_cache()
goto err;
query_cache_size -= additional_data_size;
- if (!(cache= (byte *)
+ if (!(cache= (uchar *)
my_malloc_lock(query_cache_size+additional_data_size, MYF(0))))
goto err;
@@ -1808,64 +2009,36 @@ void Query_cache::make_disabled()
query_cache_size= 0;
queries_blocks= 0;
free_memory= 0;
+ free_memory_blocks= 0;
bins= 0;
steps= 0;
cache= 0;
mem_bin_num= mem_bin_steps= 0;
queries_in_cache= 0;
first_block= 0;
+ total_blocks= 0;
+ tables_blocks= 0;
DBUG_VOID_RETURN;
}
-/*
- free_cache() - free all resources allocated by the cache.
-
- SYNOPSIS
- free_cache()
+/**
+ @class Query_cache
+ Free all resources allocated by the cache.
- DESCRIPTION
- This function frees all resources allocated by the cache. You
- have to call init_cache() before using the cache again.
+ This function frees all resources allocated by the cache. You
+ have to call init_cache() before using the cache again. This function
+ requires the structure_guard_mutex to be locked.
*/
void Query_cache::free_cache()
{
DBUG_ENTER("Query_cache::free_cache");
- if (query_cache_size > 0)
- flush_cache();
- /*
- There may be two free_cache() calls in progress, because we
- release 'structure_guard_mutex' in flush_cache(). When the second
- flush_cache() wakes up from the wait on 'COND_flush_finished', the
- first call to free_cache() has done its job. So we have to test
- 'query_cache_size > 0' the second time to see if the cache wasn't
- reset by other thread, or if it was reset and was re-enabled then.
- If the cache was reset, then we have nothing to do here.
- */
- if (query_cache_size > 0)
- {
-#ifndef DBUG_OFF
- if (bins[0].free_blocks == 0)
- {
- wreck(__LINE__,"no free memory found in (bins[0].free_blocks");
- DBUG_VOID_RETURN;
- }
-#endif
- /* Becasue we did a flush, all cache memory must be in one this block */
- bins[0].free_blocks->destroy();
- total_blocks--;
-#ifndef DBUG_OFF
- if (free_memory != query_cache_size)
- DBUG_PRINT("qcache", ("free memory %lu (should be %lu)",
- free_memory , query_cache_size));
-#endif
- my_free((gptr) cache, MYF(MY_ALLOW_ZERO_PTR));
- make_disabled();
- hash_free(&queries);
- hash_free(&tables);
- }
+ my_free((uchar*) cache, MYF(MY_ALLOW_ZERO_PTR));
+ make_disabled();
+ hash_free(&queries);
+ hash_free(&tables);
DBUG_VOID_RETURN;
}
@@ -1874,24 +2047,17 @@ void Query_cache::free_cache()
*****************************************************************************/
-/*
- flush_cache() - flush the cache.
-
- SYNOPSIS
- flush_cache()
+/**
+ Flush the cache.
- DESCRIPTION
- This function will flush cache contents. It assumes we have
- 'structure_guard_mutex' locked. The function sets the
- flush_in_progress flag and releases the lock, so other threads may
- proceed skipping the cache as if it is disabled. Concurrent
- flushes are performed in turn.
-
- After flush_cache() call, the cache is flushed, all the freed
- memory is accumulated in bin[0], and the 'structure_guard_mutex'
- is locked. However, since we could release the mutex during
- execution, the rest of the cache state could have been changed,
- and should not be relied on.
+ This function will flush cache contents. It assumes we have
+ 'structure_guard_mutex' locked. The function sets the m_cache_status flag and
+ releases the lock, so other threads may proceed skipping the cache as if it
+ is disabled. Concurrent flushes are performed in turn.
+ After flush_cache() call, the cache is flushed, all the freed memory is
+ accumulated in bin[0], and the 'structure_guard_mutex' is locked. However,
+ since we could release the mutex during execution, the rest of the cache
+ state could have been changed, and should not be relied on.
*/
void Query_cache::flush_cache()
@@ -1903,15 +2069,15 @@ void Query_cache::flush_cache()
Query_cache::free_cache()) depends on the fact that after the
flush the cache is empty.
*/
- while (flush_in_progress)
- pthread_cond_wait(&COND_flush_finished, &structure_guard_mutex);
+ while (is_flushing())
+ pthread_cond_wait(&COND_cache_status_changed, &structure_guard_mutex);
/*
- Setting 'flush_in_progress' will prevent other threads from using
+ Setting 'FLUSH_IN_PROGRESS' will prevent other threads from using
the cache while we are in the middle of the flush, and we release
the lock so that other threads won't block.
*/
- flush_in_progress= TRUE;
+ m_cache_status= Query_cache::FLUSH_IN_PROGRESS;
STRUCT_UNLOCK(&structure_guard_mutex);
my_hash_reset(&queries);
@@ -1922,8 +2088,8 @@ void Query_cache::flush_cache()
}
STRUCT_LOCK(&structure_guard_mutex);
- flush_in_progress= FALSE;
- pthread_cond_signal(&COND_flush_finished);
+ m_cache_status= Query_cache::NO_FLUSH_IN_PROGRESS;
+ pthread_cond_signal(&COND_cache_status_changed);
}
/*
@@ -1941,7 +2107,7 @@ my_bool Query_cache::free_old_query()
sequence is breached.
Also we don't need remove locked queries at this point.
*/
- Query_cache_block *query_block = 0;
+ Query_cache_block *query_block= 0;
if (queries_blocks != 0)
{
Query_cache_block *block = queries_blocks;
@@ -2065,7 +2231,7 @@ void Query_cache::free_query(Query_cache_block *query_block)
(ulong) query_block,
query_block->query()->length() ));
- hash_delete(&queries,(byte *) query_block);
+ hash_delete(&queries,(uchar *) query_block);
free_query_internal(query_block);
DBUG_VOID_RETURN;
@@ -2076,11 +2242,10 @@ void Query_cache::free_query(Query_cache_block *query_block)
*****************************************************************************/
Query_cache_block *
-Query_cache::write_block_data(ulong data_len, gptr data,
+Query_cache::write_block_data(ulong data_len, uchar* data,
ulong header_len,
Query_cache_block::block_type type,
- TABLE_COUNTER_TYPE ntab,
- my_bool under_guard)
+ TABLE_COUNTER_TYPE ntab)
{
ulong all_headers_len = (ALIGN_SIZE(sizeof(Query_cache_block)) +
ALIGN_SIZE(ntab*sizeof(Query_cache_block_table)) +
@@ -2090,17 +2255,15 @@ Query_cache::write_block_data(ulong data_len, gptr data,
DBUG_ENTER("Query_cache::write_block_data");
DBUG_PRINT("qcache", ("data: %ld, header: %ld, all header: %ld",
data_len, header_len, all_headers_len));
- Query_cache_block *block = allocate_block(max(align_len,
- min_allocation_unit),
- 1, 0, under_guard);
+ Query_cache_block *block= allocate_block(max(align_len,
+ min_allocation_unit),1, 0);
if (block != 0)
{
block->type = type;
block->n_tables = ntab;
block->used = len;
- memcpy((void*) (((byte *) block)+ all_headers_len),
- (void*) data, data_len);
+ memcpy((uchar *) block+ all_headers_len, data, data_len);
}
DBUG_RETURN(block);
}
@@ -2112,7 +2275,7 @@ Query_cache::write_block_data(ulong data_len, gptr data,
my_bool
Query_cache::append_result_data(Query_cache_block **current_block,
- ulong data_len, gptr data,
+ ulong data_len, uchar* data,
Query_cache_block *query_block)
{
DBUG_ENTER("Query_cache::append_result_data");
@@ -2168,7 +2331,7 @@ Query_cache::append_result_data(Query_cache_block **current_block,
by the next call
*/
success = write_result_data(&new_block, data_len-last_block_free_space,
- (gptr)(((byte*)data)+last_block_free_space),
+ (uchar*)(((uchar*)data)+last_block_free_space),
query_block,
Query_cache_block::RES_CONT);
/*
@@ -2190,8 +2353,7 @@ Query_cache::append_result_data(Query_cache_block **current_block,
ulong to_copy = min(data_len,last_block_free_space);
DBUG_PRINT("qcache", ("use free space %lub at block 0x%lx to copy %lub",
last_block_free_space, (ulong)last_block, to_copy));
- memcpy((void*) (((byte*) last_block) + last_block->used), (void*) data,
- to_copy);
+ memcpy((uchar*) last_block + last_block->used, data, to_copy);
last_block->used+=to_copy;
}
DBUG_RETURN(success);
@@ -2199,7 +2361,7 @@ Query_cache::append_result_data(Query_cache_block **current_block,
my_bool Query_cache::write_result_data(Query_cache_block **result_block,
- ulong data_len, gptr data,
+ ulong data_len, uchar* data,
Query_cache_block *query_block,
Query_cache_block::block_type type)
{
@@ -2225,7 +2387,7 @@ my_bool Query_cache::write_result_data(Query_cache_block **result_block,
ALIGN_SIZE(sizeof(Query_cache_result)));
#ifndef EMBEDDED_LIBRARY
Query_cache_block *block= *result_block;
- byte *rest= (byte*) data;
+ uchar *rest= data;
// Now fill list of blocks that created by allocate_data_chain
do
{
@@ -2233,7 +2395,7 @@ my_bool Query_cache::write_result_data(Query_cache_block **result_block,
ulong length = block->used - headers_len;
DBUG_PRINT("qcache", ("write %lu byte in block 0x%lx",length,
(ulong)block));
- memcpy((void*)(((byte*) block)+headers_len), (void*) rest, length);
+ memcpy((uchar*) block+headers_len, rest, length);
rest += length;
block = block->next;
type = Query_cache_block::RES_CONT;
@@ -2311,8 +2473,7 @@ my_bool Query_cache::allocate_data_chain(Query_cache_block **result_block,
if (!(new_block= allocate_block(max(min_size, align_len),
min_result_data_size == 0,
- all_headers_len + min_result_data_size,
- 1)))
+ all_headers_len + min_result_data_size)))
{
DBUG_PRINT("warning", ("Can't allocate block for results"));
DBUG_RETURN(FALSE);
@@ -2354,50 +2515,109 @@ my_bool Query_cache::allocate_data_chain(Query_cache_block **result_block,
Invalidate the first table in the table_list
*/
-void Query_cache::invalidate_table(TABLE_LIST *table_list)
+void Query_cache::invalidate_table(THD *thd, TABLE_LIST *table_list)
{
if (table_list->table != 0)
- invalidate_table(table_list->table); // Table is open
+ invalidate_table(thd, table_list->table); // Table is open
else
{
char key[MAX_DBKEY_LENGTH];
uint key_length;
- Query_cache_block *table_block;
+
key_length=(uint) (strmov(strmov(key,table_list->db)+1,
table_list->table_name) -key)+ 1;
// We don't store temporary tables => no key_length+=4 ...
- if ((table_block = (Query_cache_block*)
- hash_search(&tables,(byte*) key,key_length)))
- invalidate_table(table_block);
+ invalidate_table(thd, (uchar *)key, key_length);
}
}
-void Query_cache::invalidate_table(TABLE *table)
+void Query_cache::invalidate_table(THD *thd, TABLE *table)
{
- invalidate_table((byte*) table->s->table_cache_key, table->s->key_length);
+ invalidate_table(thd, (uchar*) table->s->table_cache_key.str,
+ table->s->table_cache_key.length);
}
-void Query_cache::invalidate_table(byte * key, uint32 key_length)
+void Query_cache::invalidate_table(THD *thd, uchar * key, uint32 key_length)
{
- Query_cache_block *table_block;
- if ((table_block = ((Query_cache_block*)
- hash_search(&tables, key, key_length))))
- invalidate_table(table_block);
+ bool interrupt;
+ STRUCT_LOCK(&structure_guard_mutex);
+ wait_while_table_flush_is_in_progress(&interrupt);
+ if (interrupt)
+ {
+ STRUCT_UNLOCK(&structure_guard_mutex);
+ return;
+ }
+
+ /*
+ Setting 'TABLE_FLUSH_IN_PROGRESS' will temporarily disable the cache
+ so that structural changes to cache won't block the entire server.
+ However, threads requesting to change the query cache will still have
+ to wait for the flush to finish.
+ */
+ m_cache_status= Query_cache::TABLE_FLUSH_IN_PROGRESS;
+ STRUCT_UNLOCK(&structure_guard_mutex);
+
+ if (query_cache_size > 0)
+ invalidate_table_internal(thd, key, key_length);
+
+ STRUCT_LOCK(&structure_guard_mutex);
+ m_cache_status= Query_cache::NO_FLUSH_IN_PROGRESS;
+
+ /*
+ net_real_write might be waiting on a change on the m_cache_status
+ variable.
+ */
+ pthread_cond_signal(&COND_cache_status_changed);
+ STRUCT_UNLOCK(&structure_guard_mutex);
}
-void Query_cache::invalidate_table(Query_cache_block *table_block)
+
+/**
+ Try to locate and invalidate a table by name.
+ The caller must ensure that no other thread is trying to work with
+ the query cache when this function is executed.
+
+ @pre structure_guard_mutex is acquired or TABLE_FLUSH_IN_PROGRESS is set.
+*/
+
+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);
+ if (table_block)
+ {
+ Query_cache_block_table *list_root= table_block->table(0);
+ invalidate_query_block_list(thd, list_root);
+ }
+}
+
+/**
+ Invalidate a linked list of query cache blocks.
+
+ Each block tries to aquire a block level lock before
+ free_query is a called. This function will in turn affect
+ related table- and result-blocks.
+
+ @param[in,out] thd Thread context.
+ @param[in,out] list_root A pointer to a circular list of query blocks.
+
+*/
+
+void
+Query_cache::invalidate_query_block_list(THD *thd,
+ Query_cache_block_table *list_root)
{
- Query_cache_block_table *list_root = table_block->table(0);
while (list_root->next != list_root)
{
- Query_cache_block *query_block = list_root->next->block();
+ 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););
}
}
-
/*
Register given table list begining with given position in tables table of
block
@@ -2424,7 +2644,7 @@ Query_cache::register_tables_from_list(TABLE_LIST *tables_used,
tables_used;
tables_used= tables_used->next_global, n++, block_table++)
{
- if (tables_used->derived && !tables_used->view)
+ if (tables_used->is_anonymous_derived_table())
{
DBUG_PRINT("qcache", ("derived table skipped"));
n--;
@@ -2456,21 +2676,28 @@ Query_cache::register_tables_from_list(TABLE_LIST *tables_used,
else
{
DBUG_PRINT("qcache",
- ("table %s, db %s, openinfo at 0x%lx, keylen %u, key at 0x%lx",
- tables_used->table->s->table_name,
- tables_used->table->s->table_cache_key,
+ ("table: %s db: %s openinfo: 0x%lx keylen: %lu key: 0x%lx",
+ tables_used->table->s->table_name.str,
+ tables_used->table->s->table_cache_key.str,
(ulong) tables_used->table,
- tables_used->table->s->key_length,
- (ulong) tables_used->table->s->table_cache_key));
- if (!insert_table(tables_used->table->s->key_length,
- tables_used->table->s->table_cache_key, block_table,
+ (ulong) tables_used->table->s->table_cache_key.length,
+ (ulong) tables_used->table->s->table_cache_key.str));
+
+ if (!insert_table(tables_used->table->s->table_cache_key.length,
+ tables_used->table->s->table_cache_key.str,
+ block_table,
tables_used->db_length,
tables_used->table->file->table_cache_type(),
tables_used->callback_func,
tables_used->engine_data))
DBUG_RETURN(0);
- if (tables_used->table->s->db_type == DB_TYPE_MRG_MYISAM)
+#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();
@@ -2493,6 +2720,7 @@ Query_cache::register_tables_from_list(TABLE_LIST *tables_used,
DBUG_RETURN(0);
}
}
+#endif
}
}
DBUG_RETURN(n - counter);
@@ -2521,9 +2749,8 @@ my_bool Query_cache::register_all_tables(Query_cache_block *block,
n= register_tables_from_list(tables_used, 0, block_table);
- if (n)
+ if (n==0)
{
- DBUG_PRINT("qcache", ("failed at table %d", (int) n));
/* Unlink the tables we allocated above */
for (Query_cache_block_table *tmp = block->table(0) ;
tmp != block_table;
@@ -2533,9 +2760,13 @@ my_bool Query_cache::register_all_tables(Query_cache_block *block,
return test(n);
}
-/*
- Insert used tablename in cache
- Returns 0 on error
+
+/**
+ Insert used table name into the cache.
+
+ @return Error status
+ @retval FALSE On error
+ @retval TRUE On success
*/
my_bool
@@ -2549,9 +2780,10 @@ Query_cache::insert_table(uint key_len, char *key,
DBUG_PRINT("qcache", ("insert table node 0x%lx, len %d",
(ulong)node, key_len));
- Query_cache_block *table_block = ((Query_cache_block *)
- hash_search(&tables, (byte*) key,
- key_len));
+ THD *thd= current_thd;
+
+ Query_cache_block *table_block=
+ (Query_cache_block *)hash_search(&tables, (uchar*) key, key_len);
if (table_block &&
table_block->table()->engine_data() != engine_data)
@@ -2566,7 +2798,11 @@ Query_cache::insert_table(uint key_len, char *key,
as far as we delete all queries with this table, table block will be
deleted, too
*/
- invalidate_table(table_block);
+ {
+ Query_cache_block_table *list_root= table_block->table(0);
+ invalidate_query_block_list(thd, list_root);
+ }
+
table_block= 0;
}
@@ -2574,42 +2810,67 @@ Query_cache::insert_table(uint key_len, char *key,
{
DBUG_PRINT("qcache", ("new table block from 0x%lx (%u)",
(ulong) key, (int) key_len));
- table_block = write_block_data(key_len, (gptr) key,
- ALIGN_SIZE(sizeof(Query_cache_table)),
- Query_cache_block::TABLE,
- 1, 1);
+ table_block= write_block_data(key_len, (uchar*) key,
+ ALIGN_SIZE(sizeof(Query_cache_table)),
+ Query_cache_block::TABLE, 1);
if (table_block == 0)
{
DBUG_PRINT("qcache", ("Can't write table name to cache"));
DBUG_RETURN(0);
}
- Query_cache_table *header = table_block->table();
+ Query_cache_table *header= table_block->table();
double_linked_list_simple_include(table_block,
- &tables_blocks);
- Query_cache_block_table *list_root = table_block->table(0);
- list_root->n = 0;
- list_root->next = list_root->prev = list_root;
- if (my_hash_insert(&tables, (const byte *) table_block))
+ &tables_blocks);
+ /*
+ First node in the Query_cache_block_table-chain is the table-type
+ block. This block will only have one Query_cache_block_table (n=0).
+ */
+ Query_cache_block_table *list_root= table_block->table(0);
+ list_root->n= 0;
+
+ /*
+ The node list is circular in nature.
+ */
+ list_root->next= list_root->prev= list_root;
+
+ if (my_hash_insert(&tables, (const uchar *) table_block))
{
DBUG_PRINT("qcache", ("Can't insert table to hash"));
// write_block_data return locked block
free_memory_block(table_block);
DBUG_RETURN(0);
}
- char *db = header->db();
+ char *db= header->db();
header->table(db + db_length + 1);
header->key_length(key_len);
header->type(cache_type);
header->callback(callback);
header->engine_data(engine_data);
+
+ /*
+ We insert this table without the assumption that it isn't refrenenced by
+ any queries.
+ */
+ header->m_cached_query_count= 0;
}
- Query_cache_block_table *list_root = table_block->table(0);
- node->next = list_root->next;
- list_root->next = node;
- node->next->prev = node;
- node->prev = list_root;
- node->parent = table_block->table();
+ /*
+ Table is now in the cache; link the table_block-node associated
+ with the currently processed query into the chain of queries depending
+ on the cached table.
+ */
+ Query_cache_block_table *list_root= table_block->table(0);
+ node->next= list_root->next;
+ list_root->next= node;
+ node->next->prev= node;
+ node->prev= list_root;
+ node->parent= table_block->table();
+ /*
+ Increase the counter to keep track on how long this chain
+ of queries is.
+ */
+ Query_cache_table *table_block_data= table_block->table();
+ table_block_data->m_cached_query_count++;
DBUG_RETURN(1);
}
@@ -2617,16 +2878,28 @@ Query_cache::insert_table(uint key_len, char *key,
void Query_cache::unlink_table(Query_cache_block_table *node)
{
DBUG_ENTER("Query_cache::unlink_table");
- node->prev->next = node->next;
- node->next->prev = node->prev;
- Query_cache_block_table *neighbour = node->next;
+ node->prev->next= node->next;
+ node->next->prev= node->prev;
+ Query_cache_block_table *neighbour= node->next;
+ Query_cache_table *table_block_data= node->parent;
+ table_block_data->m_cached_query_count--;
+
+ DBUG_ASSERT(table_block_data->m_cached_query_count >= 0);
+
if (neighbour->next == neighbour)
{
- // list is empty (neighbor is root of list)
- Query_cache_block *table_block = neighbour->block();
+ DBUG_ASSERT(table_block_data->m_cached_query_count == 0);
+ /*
+ If neighbor is root of list, the list is empty.
+ The root of the list is always a table-type block
+ which contain exactly one Query_cache_block_table
+ node object, thus we can use the block() method
+ to calculate the Query_cache_block address.
+ */
+ Query_cache_block *table_block= neighbour->block();
double_linked_list_exclude(table_block,
- &tables_blocks);
- hash_delete(&tables,(byte *) table_block);
+ &tables_blocks);
+ hash_delete(&tables,(uchar *) table_block);
free_memory_block(table_block);
}
DBUG_VOID_RETURN;
@@ -2637,12 +2910,11 @@ void Query_cache::unlink_table(Query_cache_block_table *node)
*****************************************************************************/
Query_cache_block *
-Query_cache::allocate_block(ulong len, my_bool not_less, ulong min,
- my_bool under_guard)
+Query_cache::allocate_block(ulong len, my_bool not_less, ulong min)
{
DBUG_ENTER("Query_cache::allocate_block");
- DBUG_PRINT("qcache", ("len %lu, not less %d, min %lu, uder_guard %d",
- len, not_less,min,under_guard));
+ DBUG_PRINT("qcache", ("len %lu, not less %d, min %lu",
+ len, not_less,min));
if (len >= min(query_cache_size, query_cache_limit))
{
@@ -2651,17 +2923,6 @@ Query_cache::allocate_block(ulong len, my_bool not_less, ulong min,
DBUG_RETURN(0); // in any case we don't have such piece of memory
}
- if (!under_guard)
- {
- STRUCT_LOCK(&structure_guard_mutex);
-
- if (unlikely(query_cache.query_cache_size == 0 || flush_in_progress))
- {
- STRUCT_UNLOCK(&structure_guard_mutex);
- DBUG_RETURN(0);
- }
- }
-
/* Free old queries until we have enough memory to store this block */
Query_cache_block *block;
do
@@ -2676,8 +2937,6 @@ Query_cache::allocate_block(ulong len, my_bool not_less, ulong min,
split_block(block,ALIGN_SIZE(len));
}
- if (!under_guard)
- STRUCT_UNLOCK(&structure_guard_mutex);
DBUG_RETURN(block);
}
@@ -2781,7 +3040,7 @@ void Query_cache::free_memory_block(Query_cache_block *block)
void Query_cache::split_block(Query_cache_block *block, ulong len)
{
DBUG_ENTER("Query_cache::split_block");
- Query_cache_block *new_block = (Query_cache_block*)(((byte*) block)+len);
+ Query_cache_block *new_block = (Query_cache_block*)(((uchar*) block)+len);
new_block->init(block->length - len);
total_blocks++;
@@ -2912,9 +3171,7 @@ uint Query_cache::find_bin(ulong size)
}
uint bin = steps[left].idx -
(uint)((size - steps[left].size)/steps[left].increment);
-#ifndef DBUG_OFF
- bins_dump();
-#endif
+
DBUG_PRINT("qcache", ("bin %u step %u, size %lu step size %lu",
bin, left, size, steps[left].size));
DBUG_RETURN(bin);
@@ -3013,8 +3270,11 @@ Query_cache::double_linked_list_exclude(Query_cache_block *point,
{
point->next->prev = point->prev;
point->prev->next = point->next;
+ /*
+ If the root is removed; select a new root
+ */
if (point == *list_pointer)
- *list_pointer = point->next;
+ *list_pointer= point->next;
}
DBUG_VOID_RETURN;
}
@@ -3058,7 +3318,7 @@ Query_cache::process_and_count_tables(THD *thd, TABLE_LIST *tables_used,
for (; tables_used; tables_used= tables_used->next_global)
{
table_count++;
-#ifdef HAVE_QUERY_CACHE
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
/*
Disable any attempt to store this statement if there are
column level grants on any referenced tables.
@@ -3094,10 +3354,10 @@ Query_cache::process_and_count_tables(THD *thd, TABLE_LIST *tables_used,
}
else
{
- DBUG_PRINT("qcache", ("table %s, db %s, type %u",
- tables_used->table->s->table_name,
- tables_used->table->s->table_cache_key,
- tables_used->table->s->db_type));
+ DBUG_PRINT("qcache", ("table: %s db: %s type: %u",
+ tables_used->table->s->table_name.str,
+ tables_used->table->s->db.str,
+ tables_used->table->s->db_type()->db_type));
if (tables_used->derived)
{
table_count--;
@@ -3114,20 +3374,26 @@ Query_cache::process_and_count_tables(THD *thd, TABLE_LIST *tables_used,
(*tables_type & HA_CACHE_TBL_NOCACHE) ||
(tables_used->db_length == 5 &&
my_strnncoll(table_alias_charset,
- (uchar*)tables_used->table->s->table_cache_key, 6,
+ (uchar*)tables_used->table->s->table_cache_key.str, 6,
(uchar*)"mysql",6) == 0))
{
DBUG_PRINT("qcache",
- ("select not cacheable: temporary, system or \
- other non-cacheable table(s)"));
+ ("select not cacheable: temporary, system or "
+ "other non-cacheable table(s)"));
DBUG_RETURN(0);
}
- if (tables_used->table->s->db_type == DB_TYPE_MRG_MYISAM)
+#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+= (uint) (file->end_table - file->open_tables);
+ table_count+= (file->end_table - file->open_tables);
}
+#endif
}
}
DBUG_RETURN(table_count);
@@ -3146,11 +3412,10 @@ Query_cache::is_cacheable(THD *thd, uint32 query_len, char *query, LEX *lex,
TABLE_COUNTER_TYPE table_count;
DBUG_ENTER("Query_cache::is_cacheable");
- if (lex->sql_command == SQLCOM_SELECT &&
+ if (query_cache_is_cacheable_query(lex) &&
(thd->variables.query_cache_type == 1 ||
(thd->variables.query_cache_type == 2 && (lex->select_lex.options &
- OPTION_TO_QUERY_CACHE))) &&
- lex->safe_to_cache_query)
+ OPTION_TO_QUERY_CACHE))))
{
DBUG_PRINT("qcache", ("options: %lx %lx type: %u",
(long) OPTION_TO_QUERY_CACHE,
@@ -3200,11 +3465,13 @@ my_bool Query_cache::ask_handler_allowance(THD *thd,
for (; tables_used; tables_used= tables_used->next_global)
{
TABLE *table;
+ handler *handler;
if (!(table= tables_used->table))
continue;
- handler *handler= table->file;
- if (!handler->register_query_cache_table(thd, table->s->table_cache_key,
- table->s->key_length,
+ handler= table->file;
+ if (!handler->register_query_cache_table(thd,
+ table->s->table_cache_key.str,
+ table->s->table_cache_key.length,
&tables_used->callback_func,
&tables_used->engine_data))
{
@@ -3222,21 +3489,20 @@ my_bool Query_cache::ask_handler_allowance(THD *thd,
Packing
*****************************************************************************/
+
+/**
+ Rearrange all memory blocks so that free memory joins at the
+ 'bottom' of the allocated memory block containing all cache data.
+ @see Query_cache::pack(ulong join_limit, uint iteration_limit)
+*/
+
void Query_cache::pack_cache()
{
DBUG_ENTER("Query_cache::pack_cache");
- STRUCT_LOCK(&structure_guard_mutex);
-
- if (unlikely(query_cache_size == 0 || flush_in_progress))
- {
- STRUCT_UNLOCK(&structure_guard_mutex);
- DBUG_VOID_RETURN;
- }
-
DBUG_EXECUTE("check_querycache",query_cache.check_integrity(1););
- byte *border = 0;
+ uchar *border = 0;
Query_cache_block *before = 0;
ulong gap = 0;
my_bool ok = 1;
@@ -3267,12 +3533,11 @@ void Query_cache::pack_cache()
}
DBUG_EXECUTE("check_querycache",query_cache.check_integrity(1););
- STRUCT_UNLOCK(&structure_guard_mutex);
DBUG_VOID_RETURN;
}
-my_bool Query_cache::move_by_type(byte **border,
+my_bool Query_cache::move_by_type(uchar **border,
Query_cache_block **before, ulong *gap,
Query_cache_block *block)
{
@@ -3285,7 +3550,7 @@ my_bool Query_cache::move_by_type(byte **border,
DBUG_PRINT("qcache", ("block 0x%lx FREE", (ulong) block));
if (*border == 0)
{
- *border = (byte *) block;
+ *border = (uchar *) block;
*before = block->pprev;
DBUG_PRINT("qcache", ("gap beginning here"));
}
@@ -3313,12 +3578,12 @@ my_bool Query_cache::move_by_type(byte **border,
*pprev = block->pprev,
*pnext = block->pnext,
*new_block =(Query_cache_block *) *border;
- size_t tablename_offset= block->table()->table() - block->table()->db();
+ uint tablename_offset = block->table()->table() - block->table()->db();
char *data = (char*) block->data();
- byte *key;
- uint key_length;
- key=query_cache_table_get_key((byte*) block, &key_length, 0);
- hash_first(&tables, (byte*) key, key_length, &record_idx);
+ 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);
block->destroy();
new_block->init(len);
@@ -3352,7 +3617,7 @@ my_bool Query_cache::move_by_type(byte **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, (byte*) new_block);
+ 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));
@@ -3375,10 +3640,10 @@ my_bool Query_cache::move_by_type(byte **border,
char *data = (char*) block->data();
Query_cache_block *first_result_block = ((Query_cache_query *)
block->data())->result();
- byte *key;
- uint key_length;
- key=query_cache_query_get_key((byte*) block, &key_length, 0);
- hash_first(&queries, (byte*) key, key_length, &record_idx);
+ 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)));
@@ -3394,7 +3659,7 @@ my_bool Query_cache::move_by_type(byte **border,
queries_blocks = new_block;
Query_cache_block_table *beg_of_table_table= block->table(0),
*end_of_table_table= block->table(n_tables);
- byte *beg_of_new_table_table= (byte*) new_block->table(0);
+ uchar *beg_of_new_table_table= (uchar*) new_block->table(0);
for (TABLE_COUNTER_TYPE j=0; j < n_tables; j++)
{
@@ -3404,8 +3669,8 @@ my_bool Query_cache::move_by_type(byte **border,
if ((beg_of_table_table <= block_table->next) &&
(block_table->next < end_of_table_table))
((Query_cache_block_table *)(beg_of_new_table_table +
- (((byte*)block_table->next) -
- ((byte*)beg_of_table_table))))->prev=
+ (((uchar*)block_table->next) -
+ ((uchar*)beg_of_table_table))))->prev=
block_table;
else
block_table->next->prev= block_table;
@@ -3414,8 +3679,8 @@ my_bool Query_cache::move_by_type(byte **border,
if ((beg_of_table_table <= block_table->prev) &&
(block_table->prev < end_of_table_table))
((Query_cache_block_table *)(beg_of_new_table_table +
- (((byte*)block_table->prev) -
- ((byte*)beg_of_table_table))))->next=
+ (((uchar*)block_table->prev) -
+ ((uchar*)beg_of_table_table))))->next=
block_table;
else
block_table->prev->next = block_table;
@@ -3443,10 +3708,10 @@ my_bool Query_cache::move_by_type(byte **border,
NET *net = new_block->query()->writer();
if (net != 0)
{
- net->query_cache_query= (gptr) new_block;
+ net->query_cache_query= (uchar*) new_block;
}
/* Fix hash to point at moved block */
- hash_replace(&queries, &record_idx, (byte*) new_block);
+ 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;
@@ -3542,8 +3807,7 @@ my_bool Query_cache::join_results(ulong join_limit)
my_bool has_moving = 0;
DBUG_ENTER("Query_cache::join_results");
- STRUCT_LOCK(&structure_guard_mutex);
- if (queries_blocks != 0 && !flush_in_progress)
+ if (queries_blocks != 0)
{
DBUG_ASSERT(query_cache_size > 0);
Query_cache_block *block = queries_blocks;
@@ -3582,7 +3846,7 @@ my_bool Query_cache::join_results(ulong join_limit)
Query_cache_result *new_result = new_result_block->result();
new_result->parent(block);
- byte *write_to = (byte*) new_result->data();
+ uchar *write_to = (uchar*) new_result->data();
Query_cache_block *result_block = first_result;
do
{
@@ -3606,7 +3870,6 @@ my_bool Query_cache::join_results(ulong join_limit)
block = block->next;
} while ( block != queries_blocks );
}
- STRUCT_UNLOCK(&structure_guard_mutex);
DBUG_RETURN(has_moving);
}
@@ -3625,7 +3888,7 @@ uint Query_cache::filename_2_table_key (char *key, const char *path,
filename= tablename + dirname_length(tablename + 2) + 2;
/* Find start of databasename */
for (dbname= filename - 2 ; dbname[-1] != FN_LIBCHAR ; dbname--) ;
- *db_length= (uint) ((filename - dbname) - 1);
+ *db_length= (filename - dbname) - 1;
DBUG_PRINT("qcache", ("table '%-.*s.%s'", *db_length, dbname, filename));
DBUG_RETURN((uint) (strmov(strmake(key, dbname, *db_length) + 1,
@@ -3766,18 +4029,18 @@ void Query_cache::queries_dump()
Query_cache_block *block = queries_blocks;
do
{
- uint len;
- char *str = (char*) query_cache_query_get_key((byte*) block, &len, 0);
+ size_t len;
+ char *str = (char*) query_cache_query_get_key((uchar*) block, &len, 0);
len-= QUERY_CACHE_FLAGS_SIZE; // Point at flags
Query_cache_query_flags flags;
memcpy(&flags, str+len, QUERY_CACHE_FLAGS_SIZE);
str[len]= 0; // make zero ending DB name
- DBUG_PRINT("qcache", ("F: %u C: %u L: %lu T: '%s' (%u) '%s' '%s'",
+ DBUG_PRINT("qcache", ("F: %u C: %u L: %lu T: '%s' (%lu) '%s' '%s'",
flags.client_long_flag,
flags.character_set_client_num,
(ulong)flags.limit,
flags.time_zone->get_name()->ptr(),
- len, str, strend(str)+1));
+ (ulong) len, str, strend(str)+1));
DBUG_PRINT("qcache", ("-b- 0x%lx 0x%lx 0x%lx 0x%lx 0x%lx", (ulong) block,
(ulong) block->next, (ulong) block->prev,
(ulong)block->pnext, (ulong)block->pprev));
@@ -3834,7 +4097,7 @@ void Query_cache::tables_dump()
Query_cache_table *table = table_block->table();
DBUG_PRINT("qcache", ("'%s' '%s'", table->db(), table->table()));
table_block = table_block->next;
- } while ( table_block != tables_blocks);
+ } while (table_block != tables_blocks);
}
else
DBUG_PRINT("qcache", ("no tables in list"));
@@ -3842,6 +4105,14 @@ void Query_cache::tables_dump()
}
+/**
+ Checks integrity of the various linked lists
+
+ @return Error status code
+ @retval FALSE Query cache is operational.
+ @retval TRUE Query cache is broken.
+*/
+
my_bool Query_cache::check_integrity(bool locked)
{
my_bool result = 0;
@@ -3851,14 +4122,8 @@ my_bool Query_cache::check_integrity(bool locked)
if (!locked)
STRUCT_LOCK(&structure_guard_mutex);
- if (unlikely(query_cache_size == 0 || flush_in_progress))
- {
- if (!locked)
- STRUCT_UNLOCK(&query_cache.structure_guard_mutex);
-
- DBUG_PRINT("qcache", ("Query Cache not initialized"));
- DBUG_RETURN(0);
- }
+ while (is_flushing())
+ pthread_cond_wait(&COND_cache_status_changed,&structure_guard_mutex);
if (hash_check(&queries))
{
@@ -3877,6 +4142,10 @@ my_bool Query_cache::check_integrity(bool locked)
Query_cache_block * block = first_block;
do
{
+ /* When checking at system start, there is no block. */
+ if (!block)
+ break;
+
DBUG_PRINT("qcache", ("block 0x%lx, type %u...",
(ulong) block, (uint) block->type));
// Check allignment
@@ -3884,32 +4153,32 @@ my_bool Query_cache::check_integrity(bool locked)
(((long)first_block) % (long)ALIGN_SIZE(1)))
{
DBUG_PRINT("error",
- ("block 0x%lx do not aligned by %d", (long) block,
+ ("block 0x%lx do not aligned by %d", (ulong) block,
(int) ALIGN_SIZE(1)));
result = 1;
}
// Check memory allocation
if (block->pnext == first_block) // Is it last block?
{
- if (((byte*)block) + block->length !=
- ((byte*)first_block) + query_cache_size)
+ if (((uchar*)block) + block->length !=
+ ((uchar*)first_block) + query_cache_size)
{
DBUG_PRINT("error",
("block 0x%lx, type %u, ended at 0x%lx, but cache ended at 0x%lx",
(ulong) block, (uint) block->type,
- (ulong) (((byte*)block) + block->length),
- (ulong) (((byte*)first_block) + query_cache_size)));
+ (ulong) (((uchar*)block) + block->length),
+ (ulong) (((uchar*)first_block) + query_cache_size)));
result = 1;
}
}
else
- if (((byte*)block) + block->length != ((byte*)block->pnext))
+ if (((uchar*)block) + block->length != ((uchar*)block->pnext))
{
DBUG_PRINT("error",
("block 0x%lx, type %u, ended at 0x%lx, but next block begining at 0x%lx",
(ulong) block, (uint) block->type,
- (ulong) (((byte*)block) + block->length),
- (ulong) ((byte*)block->pnext)));
+ (ulong) (((uchar*)block) + block->length),
+ (ulong) ((uchar*)block->pnext)));
}
if (block->type == Query_cache_block::FREE)
free+= block->length;
@@ -3921,8 +4190,8 @@ my_bool Query_cache::check_integrity(bool locked)
Query_cache_memory_bin *bin = *((Query_cache_memory_bin **)
block->data());
//is it correct pointer?
- if (((byte*)bin) < ((byte*)bins) ||
- ((byte*)bin) >= ((byte*)first_block))
+ if (((uchar*)bin) < ((uchar*)bins) ||
+ ((uchar*)bin) >= ((uchar*)first_block))
{
DBUG_PRINT("error",
("free block 0x%lx have bin pointer 0x%lx beyaond of bins array bounds [0x%lx,0x%lx]",
@@ -3934,8 +4203,8 @@ my_bool Query_cache::check_integrity(bool locked)
}
else
{
- int idx = (int) ((((byte*)bin) - ((byte*)bins)) /
- sizeof(Query_cache_memory_bin));
+ int idx = (((uchar*)bin) - ((uchar*)bins)) /
+ sizeof(Query_cache_memory_bin);
if (in_list(bins[idx].free_blocks, block, "free memory"))
result = 1;
}
@@ -3956,7 +4225,7 @@ my_bool Query_cache::check_integrity(bool locked)
Query_cache_block_table *block_table = block->table(j);
Query_cache_block_table *block_table_root =
(Query_cache_block_table *)
- (((byte*)block_table->parent) -
+ (((uchar*)block_table->parent) -
ALIGN_SIZE(sizeof(Query_cache_block_table)));
if (in_table_list(block_table, block_table_root, "table list"))
@@ -3972,15 +4241,15 @@ my_bool Query_cache::check_integrity(bool locked)
case Query_cache_block::RESULT:
{
Query_cache_block * query_block = block->result()->parent();
- if (((byte*)query_block) < ((byte*)first_block) ||
- ((byte*)query_block) >= (((byte*)first_block) + query_cache_size))
+ if (((uchar*)query_block) < ((uchar*)first_block) ||
+ ((uchar*)query_block) >= (((uchar*)first_block) + query_cache_size))
{
DBUG_PRINT("error",
("result block 0x%lx have query block pointer 0x%lx beyaond of block pool bounds [0x%lx,0x%lx]",
(ulong) block,
(ulong) query_block,
(ulong) first_block,
- (ulong) (((byte*)first_block) + query_cache_size)));
+ (ulong) (((uchar*)first_block) + query_cache_size)));
result = 1;
}
else
@@ -4027,10 +4296,10 @@ my_bool Query_cache::check_integrity(bool locked)
{
DBUG_PRINT("qcache", ("block 0x%lx, type %u...",
(ulong) block, (uint) block->type));
- uint length;
- byte *key = query_cache_query_get_key((byte*) block, &length, 0);
- gptr val = hash_search(&queries, key, length);
- if (((gptr)block) != val)
+ size_t length;
+ uchar *key = query_cache_query_get_key((uchar*) block, &length, 0);
+ uchar* val = hash_search(&queries, key, length);
+ if (((uchar*)block) != val)
{
DBUG_PRINT("error", ("block 0x%lx found in queries hash like 0x%lx",
(ulong) block, (ulong) val));
@@ -4062,10 +4331,10 @@ my_bool Query_cache::check_integrity(bool locked)
{
DBUG_PRINT("qcache", ("block 0x%lx, type %u...",
(ulong) block, (uint) block->type));
- uint length;
- byte *key = query_cache_table_get_key((byte*) block, &length, 0);
- gptr val = hash_search(&tables, key, length);
- if (((gptr)block) != val)
+ size_t length;
+ uchar *key = query_cache_table_get_key((uchar*) block, &length, 0);
+ uchar* val = hash_search(&tables, key, length);
+ if (((uchar*)block) != val)
{
DBUG_PRINT("error", ("block 0x%lx found in tables hash like 0x%lx",
(ulong) block, (ulong) val));
diff --git a/sql/sql_cache.h b/sql/sql_cache.h
index 34fc3a5c8d5..f2c33eff614 100644
--- a/sql/sql_cache.h
+++ b/sql/sql_cache.h
@@ -65,17 +65,44 @@ struct Query_cache_query;
struct Query_cache_result;
class Query_cache;
+/**
+ This class represents a node in the linked chain of queries
+ belonging to one table.
+ @note The root of this linked list is not a query-type block, but the table-
+ type block which all queries has in common.
+*/
struct Query_cache_block_table
{
Query_cache_block_table() {} /* Remove gcc warning */
- TABLE_COUNTER_TYPE n; // numbr in table (from 0)
+
+ /**
+ This node holds a position in a static table list belonging
+ to the associated query (base 0).
+ */
+ TABLE_COUNTER_TYPE n;
+
+ /**
+ Pointers to the next and previous node, linking all queries with
+ a common table.
+ */
Query_cache_block_table *next, *prev;
+
+ /**
+ A pointer to the table-type block which all
+ linked queries has in common.
+ */
Query_cache_table *parent;
+
+ /**
+ A method to calculate the address of the query cache block
+ owning this node. The purpose of this calculation is to
+ make it easier to move the query cache block without having
+ to modify all the pointer addresses.
+ */
inline Query_cache_block *block();
};
-
struct Query_cache_block
{
Query_cache_block() {} /* Remove gcc warning */
@@ -98,7 +125,7 @@ struct Query_cache_block
void init(ulong length);
void destroy();
inline uint headers_len();
- inline gptr data(void);
+ inline uchar* data(void);
inline Query_cache_query *query();
inline Query_cache_table *table();
inline Query_cache_result *result();
@@ -115,6 +142,7 @@ struct Query_cache_query
uint8 tbls_type;
unsigned int last_pkt_nr;
+ Query_cache_query() {} /* Remove gcc warning */
inline void init_n_lock();
void unlock_n_destroy();
inline ulonglong found_rows() { return limit_found_rows; }
@@ -128,17 +156,15 @@ struct Query_cache_query
inline ulong length() { return len; }
inline ulong add(ulong packet_len) { return(len+= packet_len); }
inline void length(ulong length_arg) { len= length_arg; }
- inline gptr query()
+ inline uchar* query()
{
- return (gptr)(((byte*)this)+
- ALIGN_SIZE(sizeof(Query_cache_query)));
+ return (((uchar*)this) + ALIGN_SIZE(sizeof(Query_cache_query)));
}
void lock_writing();
void lock_reading();
my_bool try_lock_writing();
void unlock_writing();
void unlock_reading();
- static byte *cache_key(const byte *record, uint *length, my_bool not_used);
};
@@ -153,6 +179,11 @@ struct Query_cache_table
/* data need by some engines */
ulonglong engine_data_buff;
+ /**
+ The number of queries depending of this table.
+ */
+ int32 m_cached_query_count;
+
inline char *db() { return (char *) data(); }
inline char *table() { return tbl; }
inline void table(char *table_arg) { tbl= table_arg; }
@@ -164,9 +195,9 @@ 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 gptr data()
+ inline uchar* data()
{
- return (gptr)(((byte*)this)+
+ return (uchar*)(((uchar*)this)+
ALIGN_SIZE(sizeof(Query_cache_table)));
}
};
@@ -176,9 +207,9 @@ struct Query_cache_result
Query_cache_result() {} /* Remove gcc warning */
Query_cache_block *query;
- inline gptr data()
+ inline uchar* data()
{
- return (gptr)(((byte*) this)+
+ return (uchar*)(((uchar*) this)+
ALIGN_SIZE(sizeof(Query_cache_result)));
}
/* data_continue (if not whole packet contained by this block) */
@@ -189,10 +220,10 @@ struct Query_cache_result
extern "C"
{
- byte *query_cache_query_get_key(const byte *record, uint *length,
- my_bool not_used);
- byte *query_cache_table_get_key(const byte *record, uint *length,
- my_bool not_used);
+ uchar *query_cache_query_get_key(const uchar *record, size_t *length,
+ my_bool not_used);
+ uchar *query_cache_table_get_key(const uchar *record, size_t *length,
+ my_bool not_used);
}
extern "C" void query_cache_invalidate_by_MyISAM_filename(const char* filename);
@@ -239,11 +270,17 @@ public:
ulong free_memory, queries_in_cache, hits, inserts, refused,
free_memory_blocks, total_blocks, lowmem_prunes;
+
private:
- pthread_cond_t COND_flush_finished;
- bool flush_in_progress;
+ pthread_cond_t COND_cache_status_changed;
+
+ enum Cache_status { NO_FLUSH_IN_PROGRESS, FLUSH_IN_PROGRESS,
+ TABLE_FLUSH_IN_PROGRESS };
+
+ Cache_status m_cache_status;
void free_query_internal(Query_cache_block *point);
+ void invalidate_table_internal(THD *thd, uchar *key, uint32 key_length);
protected:
/*
@@ -255,13 +292,13 @@ protected:
2. query block (for operation inside query (query block/results))
Thread doing cache flush releases the mutex once it sets
- flush_in_progress flag, so other threads may bypass the cache as
+ m_cache_status flag, so other threads may bypass the cache as
if it is disabled, not waiting for reset to finish. The exception
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;
- byte *cache; // cache memory
+ uchar *cache; // cache memory
Query_cache_block *first_block; // physical location block list
Query_cache_block *queries_blocks; // query list (LIFO)
Query_cache_block *tables_blocks;
@@ -272,6 +309,7 @@ protected:
/* options */
ulong min_allocation_unit, min_result_data_size;
uint def_query_hash_size, def_table_hash_size;
+
uint mem_bin_num, mem_bin_steps; // See at init_cache & find_bin
my_bool initialized;
@@ -297,10 +335,13 @@ protected:
ulong data_len,
Query_cache_block *query_block,
my_bool first_block);
- void invalidate_table(TABLE_LIST *table);
- void invalidate_table(TABLE *table);
- void invalidate_table(byte *key, uint32 key_length);
- void invalidate_table(Query_cache_block *table_block);
+ void invalidate_table(THD *thd, TABLE_LIST *table);
+ void invalidate_table(THD *thd, TABLE *table);
+ void invalidate_table(THD *thd, uchar *key, uint32 key_length);
+ void invalidate_table(THD *thd, Query_cache_block *table_block);
+ void invalidate_query_block_list(THD *thd,
+ Query_cache_block_table *list_root);
+
TABLE_COUNTER_TYPE
register_tables_from_list(TABLE_LIST *tables_used,
TABLE_COUNTER_TYPE counter,
@@ -324,7 +365,7 @@ protected:
ulong add_size);
void exclude_from_free_memory_list(Query_cache_block *free_block);
void insert_into_free_memory_list(Query_cache_block *new_block);
- my_bool move_by_type(byte **border, Query_cache_block **before,
+ my_bool move_by_type(uchar **border, Query_cache_block **before,
ulong *gap, Query_cache_block *i);
uint find_bin(ulong size);
void move_to_query_list_end(Query_cache_block *block);
@@ -339,6 +380,8 @@ protected:
Query_cache_block *pprev);
my_bool join_results(ulong join_limit);
+ void wait_while_table_flush_is_in_progress(bool *interrupt);
+
/*
Following function control structure_guard_mutex
by themself or don't need structure_guard_mutex
@@ -346,24 +389,22 @@ protected:
ulong init_cache();
void make_disabled();
void free_cache();
- Query_cache_block *write_block_data(ulong data_len, gptr data,
+ Query_cache_block *write_block_data(ulong data_len, uchar* data,
ulong header_len,
Query_cache_block::block_type type,
- TABLE_COUNTER_TYPE ntab = 0,
- my_bool under_guard=0);
+ TABLE_COUNTER_TYPE ntab = 0);
my_bool append_result_data(Query_cache_block **result,
- ulong data_len, gptr data,
+ ulong data_len, uchar* data,
Query_cache_block *parent);
my_bool write_result_data(Query_cache_block **result,
- ulong data_len, gptr data,
+ ulong data_len, uchar* data,
Query_cache_block *parent,
Query_cache_block::block_type
type=Query_cache_block::RESULT);
inline ulong get_min_first_result_data_size();
inline ulong get_min_append_result_data_size();
Query_cache_block *allocate_block(ulong len, my_bool not_less,
- ulong min,
- my_bool under_guard=0);
+ ulong min);
/*
If query is cacheable return number tables in query
(query without tables not cached)
@@ -428,6 +469,11 @@ protected:
friend void query_cache_end_of_result(THD *thd);
friend void query_cache_abort(NET *net);
+ bool is_flushing(void)
+ {
+ return (m_cache_status != Query_cache::NO_FLUSH_IN_PROGRESS);
+ }
+
/*
The following functions are only used when debugging
We don't protect these with ifndef DBUG_OFF to not have to recompile
diff --git a/sql/sql_class.cc b/sql/sql_class.cc
index 91c0aa66761..118dc5af68f 100644
--- a/sql/sql_class.cc
+++ b/sql/sql_class.cc
@@ -1,4 +1,4 @@
-/* Copyright (C) 2000-2006 MySQL AB
+/* Copyright 2000-2008 MySQL AB, 2008 Sun Microsystems, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -26,6 +26,11 @@
#endif
#include "mysql_priv.h"
+#include "rpl_rli.h"
+#include "rpl_record.h"
+#include "slave.h"
+#include <my_bitmap.h>
+#include "log_event.h"
#include <m_ctype.h>
#include <sys/stat.h>
#include <thr_alarm.h>
@@ -55,8 +60,8 @@ const char * const THD::DEFAULT_WHERE= "field list";
/* Used templates */
template class List<Key>;
template class List_iterator<Key>;
-template class List<key_part_spec>;
-template class List_iterator<key_part_spec>;
+template class List<Key_part_spec>;
+template class List_iterator<Key_part_spec>;
template class List<Alter_drop>;
template class List_iterator<Alter_drop>;
template class List<Alter_column>;
@@ -67,11 +72,11 @@ template class List_iterator<Alter_column>;
** User variables
****************************************************************************/
-extern "C" byte *get_var_key(user_var_entry *entry, uint *length,
- my_bool not_used __attribute__((unused)))
+extern "C" uchar *get_var_key(user_var_entry *entry, size_t *length,
+ my_bool not_used __attribute__((unused)))
{
- *length=(uint) entry->name.length;
- return (byte*) entry->name.str;
+ *length= entry->name.length;
+ return (uchar*) entry->name.str;
}
extern "C" void free_user_var(user_var_entry *entry)
@@ -82,11 +87,45 @@ extern "C" void free_user_var(user_var_entry *entry)
my_free((char*) entry,MYF(0));
}
-bool key_part_spec::operator==(const key_part_spec& other) const
+bool Key_part_spec::operator==(const Key_part_spec& other) const
{
return length == other.length && !strcmp(field_name, other.field_name);
}
+/**
+ Construct an (almost) deep copy of this key. Only those
+ elements that are known to never change are not copied.
+ If out of memory, a partial copy is returned and an error is set
+ in THD.
+*/
+
+Key::Key(const Key &rhs, MEM_ROOT *mem_root)
+ :type(rhs.type),
+ key_create_info(rhs.key_create_info),
+ columns(rhs.columns, mem_root),
+ name(rhs.name),
+ generated(rhs.generated)
+{
+ list_copy_and_replace_each_value(columns, mem_root);
+}
+
+/**
+ Construct an (almost) deep copy of this foreign key. Only those
+ elements that are known to never change are not copied.
+ If out of memory, a partial copy is returned and an error is set
+ in THD.
+*/
+
+Foreign_key::Foreign_key(const Foreign_key &rhs, MEM_ROOT *mem_root)
+ :Key(rhs),
+ ref_table(rhs.ref_table),
+ ref_columns(rhs.ref_columns),
+ delete_opt(rhs.delete_opt),
+ update_opt(rhs.update_opt),
+ match_opt(rhs.match_opt)
+{
+ list_copy_and_replace_each_value(ref_columns, mem_root);
+}
/*
Test if a foreign key (= generated key) is a prefix of the given key
@@ -122,9 +161,9 @@ bool foreign_key_prefix(Key *a, Key *b)
if (a->columns.elements > b->columns.elements)
return TRUE; // Can't be prefix
- List_iterator<key_part_spec> col_it1(a->columns);
- List_iterator<key_part_spec> col_it2(b->columns);
- const key_part_spec *col1, *col2;
+ List_iterator<Key_part_spec> col_it1(a->columns);
+ List_iterator<Key_part_spec> col_it2(b->columns);
+ const Key_part_spec *col1, *col2;
#ifdef ENABLE_WHEN_INNODB_CAN_HANDLE_SWAPED_FOREIGN_KEY_COLUMNS
while ((col1= col_it1++))
@@ -159,25 +198,363 @@ bool foreign_key_prefix(Key *a, Key *b)
** Thread specific functions
****************************************************************************/
+/** Push an error to the error stack and return TRUE for now. */
+
+bool
+Reprepare_observer::report_error(THD *thd)
+{
+ my_error(ER_NEED_REPREPARE, MYF(ME_NO_WARNING_FOR_ERROR|ME_NO_SP_HANDLER));
+
+ m_invalidated= TRUE;
+
+ return TRUE;
+}
+
+
Open_tables_state::Open_tables_state(ulong version_arg)
- :version(version_arg)
+ :version(version_arg), state_flags(0U)
{
reset_open_tables_state();
}
+/*
+ The following functions form part of the C plugin API
+*/
+
+extern "C" int mysql_tmpfile(const char *prefix)
+{
+ char filename[FN_REFLEN];
+ File fd = create_temp_file(filename, mysql_tmpdir, prefix,
+#ifdef __WIN__
+ O_BINARY | O_TRUNC | O_SEQUENTIAL |
+ O_SHORT_LIVED |
+#endif /* __WIN__ */
+ O_CREAT | O_EXCL | O_RDWR | O_TEMPORARY,
+ MYF(MY_WME));
+ if (fd >= 0) {
+#ifndef __WIN__
+ /*
+ This can be removed once the following bug is fixed:
+ Bug #28903 create_temp_file() doesn't honor O_TEMPORARY option
+ (file not removed) (Unix)
+ */
+ unlink(filename);
+#endif /* !__WIN__ */
+ }
+
+ return fd;
+}
+
+
+extern "C"
+int thd_in_lock_tables(const THD *thd)
+{
+ return test(thd->in_lock_tables);
+}
+
+
+extern "C"
+int thd_tablespace_op(const THD *thd)
+{
+ return test(thd->tablespace_op);
+}
+
+
+extern "C"
+const char *set_thd_proc_info(THD *thd, const char *info,
+ const char *calling_function,
+ const char *calling_file,
+ const unsigned int calling_line)
+{
+ const char *old_info= thd->proc_info;
+ DBUG_PRINT("proc_info", ("%s:%d %s", calling_file, calling_line,
+ (info != NULL) ? info : "(null)"));
+#if defined(ENABLED_PROFILING) && defined(COMMUNITY_SERVER)
+ thd->profiling.status_change(info, calling_function, calling_file, calling_line);
+#endif
+ thd->proc_info= info;
+ return old_info;
+}
+
+extern "C"
+void **thd_ha_data(const THD *thd, const struct handlerton *hton)
+{
+ return (void **) &thd->ha_data[hton->slot].ha_ptr;
+}
+
+extern "C"
+long long thd_test_options(const THD *thd, long long test_options)
+{
+ return thd->options & test_options;
+}
+
+extern "C"
+int thd_sql_command(const THD *thd)
+{
+ return (int) thd->lex->sql_command;
+}
+
+extern "C"
+int thd_tx_isolation(const THD *thd)
+{
+ return (int) thd->variables.tx_isolation;
+}
+
+extern "C"
+void thd_inc_row_count(THD *thd)
+{
+ thd->row_count++;
+}
+
+
+/**
+ Dumps a text description of a thread, its security context
+ (user, host) and the current query.
+
+ @param thd thread context
+ @param buffer pointer to preferred result buffer
+ @param length length of buffer
+ @param max_query_len how many chars of query to copy (0 for all)
+
+ @req LOCK_thread_count
+
+ @note LOCK_thread_count mutex is not necessary when the function is invoked on
+ the currently running thread (current_thd) or if the caller in some other
+ way guarantees that access to thd->query is serialized.
+
+ @return Pointer to string
+*/
+
+extern "C"
+char *thd_security_context(THD *thd, char *buffer, unsigned int length,
+ unsigned int max_query_len)
+{
+ String str(buffer, length, &my_charset_latin1);
+ const Security_context *sctx= &thd->main_security_ctx;
+ char header[64];
+ int len;
+ /*
+ The pointers thd->query and thd->proc_info might change since they are
+ being modified concurrently. This is acceptable for proc_info since its
+ 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
+ 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);
+ str.length(0);
+ str.append(header, len);
+
+ if (sctx->host)
+ {
+ str.append(' ');
+ str.append(sctx->host);
+ }
+
+ if (sctx->ip)
+ {
+ str.append(' ');
+ str.append(sctx->ip);
+ }
+
+ if (sctx->user)
+ {
+ str.append(' ');
+ str.append(sctx->user);
+ }
+
+ if (proc_info)
+ {
+ str.append(' ');
+ str.append(proc_info);
+ }
+
+ 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);
+ }
+ if (str.c_ptr_safe() == buffer)
+ return buffer;
+
+ /*
+ We have to copy the new string to the destination buffer because the string
+ was reallocated to a larger buffer to be able to fit.
+ */
+ DBUG_ASSERT(buffer != NULL);
+ length= min(str.length(), length-1);
+ memcpy(buffer, str.c_ptr_quick(), length);
+ /* Make sure that the new string is null terminated */
+ buffer[length]= '\0';
+ return buffer;
+}
+
+/**
+ Clear this diagnostics area.
+
+ Normally called at the end of a statement.
+*/
+
+void
+Diagnostics_area::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;
+}
+
+
+/**
+ 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_ASSERT(! is_set());
+#ifdef DBUG_OFF
+ /*
+ In production, refuse to overwrite an error or a custom response
+ with an OK packet.
+ */
+ if (is_error() || is_disabled())
+ return;
+#endif
+ /** Only allowed to report success if has not yet reported an error */
+
+ 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;
+}
+
+
+/**
+ Set EOF status.
+*/
+
+void
+Diagnostics_area::set_eof_status(THD *thd)
+{
+ /** Only allowed to report eof if has not yet reported an error */
+
+ DBUG_ASSERT(! is_set());
+#ifdef DBUG_OFF
+ /*
+ In production, refuse to overwrite an error or a custom response
+ with an EOF packet.
+ */
+ if (is_error() || is_disabled())
+ return;
+#endif
+
+ 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;
+}
+
+/**
+ Set ERROR status.
+*/
+
+void
+Diagnostics_area::set_error_status(THD *thd, uint sql_errno_arg,
+ const char *message_arg)
+{
+ /*
+ 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;
+}
+
+
+/**
+ 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 id */ 0),
- Open_tables_state(refresh_version),
+ Open_tables_state(refresh_version), rli_fake(0),
lock_id(&main_lock_id),
- user_time(0), in_sub_stmt(0), global_read_lock(0), is_fatal_error(0),
- transaction_rollback_request(0), is_fatal_sub_stmt_error(0),
- rand_used(0), time_zone_used(0),
- last_insert_id_used(0), last_insert_id_used_bin_log(0), insert_id_used(0),
- clear_next_insert_id(0), in_lock_tables(0), bootstrap(0),
- derived_tables_processing(FALSE), spcont(NULL),
+ user_time(0), in_sub_stmt(0),
+ binlog_table_maps(0), binlog_flags(0UL),
+ table_map_for_update(0),
+ arg_of_last_insert_id_function(FALSE),
+ first_successful_insert_id_in_prev_stmt(0),
+ first_successful_insert_id_in_prev_stmt_for_binlog(0),
+ first_successful_insert_id_in_cur_stmt(0),
+ stmt_depends_on_first_successful_insert_id_in_prev_stmt(FALSE),
+ global_read_lock(0),
+ is_fatal_error(0),
+ transaction_rollback_request(0),
+ is_fatal_sub_stmt_error(0),
+ rand_used(0),
+ time_zone_used(0),
+ in_lock_tables(0),
+ bootstrap(0),
+ derived_tables_processing(FALSE),
+ spcont(NULL),
m_parser_state(NULL)
{
ulong tmp;
@@ -190,7 +567,6 @@ THD::THD()
init_sql_alloc(&main_mem_root, ALLOC_ROOT_MIN_BLOCK_SIZE, 0);
stmt_arena= this;
thread_stack= 0;
- db= 0;
catalog= (char*)"std"; // the only catalog we have for now
main_security_ctx.init();
security_ctx= &main_security_ctx;
@@ -198,21 +574,26 @@ THD::THD()
query_start_used= 0;
count_cuted_fields= CHECK_FIELD_IGNORE;
killed= NOT_KILLED;
- db_length= col_access=0;
- query_error= tmp_table_used= thread_specific_used= 0;
- next_insert_id=last_insert_id=0;
+ col_access=0;
+ is_slave_error= thread_specific_used= FALSE;
hash_clear(&handler_tables_hash);
tmp_table=0;
used_tables=0;
- cuted_fields= sent_row_count= 0L;
+ cuted_fields= sent_row_count= row_count= 0L;
limit_found_rows= 0;
+ 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;
start_time=(time_t) 0;
- time_after_lock=(time_t) 0;
+ start_utime= 0L;
+ utime_after_lock= 0L;
current_linfo = 0;
slave_thread = 0;
+ bzero(&variables, sizeof(variables));
thread_id= 0;
one_shot_set= 0;
file_id = 0;
@@ -222,6 +603,7 @@ THD::THD()
bzero(ha_data, sizeof(ha_data));
mysys_var=0;
binlog_evt_union.do_union= FALSE;
+ enable_slow_log= 0;
#ifndef DBUG_OFF
dbug_sentry=THD_SENTRY_MAGIC;
#endif
@@ -229,16 +611,15 @@ THD::THD()
net.vio=0;
#endif
client_capabilities= 0; // minimalistic client
- net.last_error[0]=0; // If error on boot
#ifdef HAVE_QUERY_CACHE
query_cache_init_query(&net); // If error on boot
#endif
ull=0;
- system_thread= cleanup_done= abort_on_warning= no_warnings_for_error= 0;
+ system_thread= NON_SYSTEM_THREAD;
+ cleanup_done= abort_on_warning= no_warnings_for_error= 0;
peer_port= 0; // For SHOW PROCESSLIST
-#ifdef __WIN__
- real_id = 0;
-#endif
+ transaction.m_pending_rows_event= 0;
+ transaction.on= 1;
#ifdef SIGNAL_WITH_VIO_CLOSE
active_vio = 0;
#endif
@@ -255,6 +636,9 @@ THD::THD()
init();
/* Initialize sub structures */
init_sql_alloc(&warn_root, WARN_ALLOC_BLOCK_SIZE, WARN_ALLOC_PREALLOC_SIZE);
+#if defined(ENABLED_PROFILING) && defined(COMMUNITY_SERVER)
+ 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,
@@ -271,9 +655,9 @@ THD::THD()
bzero((char*) &user_var_events, sizeof(user_var_events));
/* Protocol */
- protocol= &protocol_simple; // Default protocol
- protocol_simple.init(this);
- protocol_prep.init(this);
+ protocol= &protocol_text; // Default protocol
+ protocol_text.init(this);
+ protocol_binary.init(this);
tablespace_op=FALSE;
tmp= sql_rnd_with_mutex();
@@ -297,12 +681,12 @@ void THD::push_internal_handler(Internal_error_handler *handler)
}
-bool THD::handle_error(uint sql_errno,
+bool THD::handle_error(uint sql_errno, const char *message,
MYSQL_ERROR::enum_warning_level level)
{
if (m_internal_handler)
{
- return m_internal_handler->handle_error(sql_errno, level, this);
+ return m_internal_handler->handle_error(sql_errno, message, level, this);
}
return FALSE; // 'FALSE', as per coding style
@@ -315,6 +699,50 @@ void THD::pop_internal_handler()
m_internal_handler= NULL;
}
+extern "C"
+void *thd_alloc(MYSQL_THD thd, unsigned int size)
+{
+ return thd->alloc(size);
+}
+
+extern "C"
+void *thd_calloc(MYSQL_THD thd, unsigned int size)
+{
+ return thd->calloc(size);
+}
+
+extern "C"
+char *thd_strdup(MYSQL_THD thd, const char *str)
+{
+ return thd->strdup(str);
+}
+
+extern "C"
+char *thd_strmake(MYSQL_THD thd, const char *str, unsigned int size)
+{
+ return thd->strmake(str, size);
+}
+
+extern "C"
+LEX_STRING *thd_make_lex_string(THD *thd, LEX_STRING *lex_str,
+ const char *str, unsigned int size,
+ int allocate_lex_string)
+{
+ return thd->make_lex_string(lex_str, str, size,
+ (bool) allocate_lex_string);
+}
+
+extern "C"
+void *thd_memdup(MYSQL_THD thd, const void* str, unsigned int size)
+{
+ return thd->memdup(str, size);
+}
+
+extern "C"
+void thd_get_xid(const MYSQL_THD thd, MYSQL_XID *xid)
+{
+ *xid = *(MYSQL_XID *) &thd->transaction.xid_state.xid;
+}
/*
Init common variables that has to be reset on start and on change_user
@@ -323,7 +751,7 @@ void THD::pop_internal_handler()
void THD::init(void)
{
pthread_mutex_lock(&LOCK_global_system_variables);
- variables= 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,
@@ -341,6 +769,12 @@ void THD::init(void)
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;
open_options=ha_open_options;
update_lock_default= (variables.low_priority_updates ?
@@ -351,6 +785,7 @@ void THD::init(void)
bzero((char*) warn_count, sizeof(warn_count));
total_warn_count= 0;
update_charset();
+ reset_current_stmt_binlog_row_based();
bzero((char *) &status_var, sizeof(status_var));
}
@@ -396,6 +831,7 @@ void THD::change_user(void)
pthread_mutex_unlock(&LOCK_status);
cleanup();
+ killed= NOT_KILLED;
cleanup_done= 0;
init();
stmt_map.reset();
@@ -412,6 +848,9 @@ void THD::change_user(void)
void THD::cleanup(void)
{
DBUG_ENTER("THD::cleanup");
+ DBUG_ASSERT(cleanup_done == 0);
+
+ killed= KILL_CONNECTION;
#ifdef ENABLE_WHEN_BINLOG_WILL_BE_ABLE_TO_PREPARE
if (transaction.xid_state.xa_state == XA_PREPARED)
{
@@ -427,9 +866,7 @@ void THD::cleanup(void)
lock=locked_tables; locked_tables=0;
close_thread_tables(this);
}
- mysql_ha_flush(this, (TABLE_LIST*) 0,
- MYSQL_HA_CLOSE_FINAL | MYSQL_HA_FLUSH_ALL, FALSE);
- hash_free(&handler_tables_hash);
+ mysql_ha_cleanup(this);
delete_dynamic(&user_var_events);
hash_free(&user_vars);
close_temporary_tables(this);
@@ -447,7 +884,7 @@ void THD::cleanup(void)
pthread_mutex_lock(&LOCK_user_locks);
item_user_lock_release(ull);
pthread_mutex_unlock(&LOCK_user_locks);
- ull= 0;
+ ull= NULL;
}
cleanup_done=1;
@@ -478,6 +915,7 @@ THD::~THD()
cleanup();
ha_close_connection(this);
+ plugin_thdvar_cleanup(this);
DBUG_PRINT("info", ("freeing security context"));
main_security_ctx.destroy();
@@ -491,6 +929,14 @@ THD::~THD()
#ifndef DBUG_OFF
dbug_sentry= THD_SENTRY_GONE;
#endif
+#ifndef EMBEDDED_LIBRARY
+ if (rli_fake)
+ {
+ delete rli_fake;
+ rli_fake= NULL;
+ }
+#endif
+
free_root(&main_mem_root, MYF(0));
DBUG_VOID_RETURN;
}
@@ -512,7 +958,7 @@ THD::~THD()
void add_to_status(STATUS_VAR *to_var, STATUS_VAR *from_var)
{
- ulong *end= (ulong*) ((byte*) to_var +
+ ulong *end= (ulong*) ((uchar*) to_var +
offsetof(STATUS_VAR, last_system_status_var) +
sizeof(ulong));
ulong *to= (ulong*) to_var, *from= (ulong*) from_var;
@@ -521,18 +967,60 @@ void add_to_status(STATUS_VAR *to_var, STATUS_VAR *from_var)
*(to++)+= *(from++);
}
+/*
+ Add the difference between two status variable arrays to another one.
+
+ SYNOPSIS
+ add_diff_to_status
+ to_var add to this array
+ from_var from this array
+ dec_var minus this array
+
+ NOTE
+ This function assumes that all variables are long/ulong.
+*/
+
+void add_diff_to_status(STATUS_VAR *to_var, STATUS_VAR *from_var,
+ STATUS_VAR *dec_var)
+{
+ ulong *end= (ulong*) ((uchar*) to_var + offsetof(STATUS_VAR,
+ last_system_status_var) +
+ sizeof(ulong));
+ ulong *to= (ulong*) to_var, *from= (ulong*) from_var, *dec= (ulong*) dec_var;
+
+ while (to != end)
+ *(to++)+= *(from++) - *(dec++);
+}
+
void THD::awake(THD::killed_state state_to_set)
{
+ DBUG_ENTER("THD::awake");
+ DBUG_PRINT("enter", ("this: 0x%lx", (long) this));
THD_CHECK_SENTRY(this);
safe_mutex_assert_owner(&LOCK_delete);
killed= state_to_set;
if (state_to_set != THD::KILL_QUERY)
{
- thr_alarm_kill(real_id);
+ thr_alarm_kill(thread_id);
+ if (!slave_thread)
+ thread_scheduler.post_kill_notification(this);
#ifdef SIGNAL_WITH_VIO_CLOSE
- close_active_vio();
+ 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 we're killing ourselves, we know that we're not blocked, so this
+ hack is not used.
+ */
+
+ close_active_vio();
+ }
#endif
}
if (mysys_var)
@@ -567,6 +1055,7 @@ void THD::awake(THD::killed_state state_to_set)
}
pthread_mutex_unlock(&mysys_var->mutex);
}
+ DBUG_VOID_RETURN;
}
/*
@@ -580,21 +1069,19 @@ bool THD::store_globals()
Assert that thread_stack is initialized: it's necessary to be able
to track stack overrun.
*/
- DBUG_ASSERT(this->thread_stack);
+ DBUG_ASSERT(thread_stack);
if (my_pthread_setspecific_ptr(THR_THD, this) ||
my_pthread_setspecific_ptr(THR_MALLOC, &mem_root))
return 1;
mysys_var=my_thread_var;
- dbug_thread_id=my_thread_id();
/*
- By default 'slave_proxy_id' is 'thread_id'. They may later become different
- if this is the slave SQL thread.
+ Let mysqld define the thread id (not mysys)
+ This allows us to move THD to different threads if needed.
*/
- /** @todo we already do it in init(), see if we still need to do it here.
- add DBUG_ASSERT(variables.pseudo_thread_id == thread_id)
- */
- variables.pseudo_thread_id= thread_id;
+ mysys_var->id= thread_id;
+ real_id= pthread_self(); // For debugging
+
/*
We have to call thr_lock_info_init() again here as THD may have been
created in another thread
@@ -622,19 +1109,6 @@ bool THD::store_globals()
void THD::cleanup_after_query()
{
- last_insert_id_used= FALSE;
- if (clear_next_insert_id)
- {
- clear_next_insert_id= 0;
- next_insert_id= 0;
-
- /*
- BUG#33029, if one statement in a SP set this member to 1, all
- statment after this statement in the SP would be considered used
- INSERT_ID value, reset this member after each query to fix this.
- */
- insert_id_used= 0;
- }
/*
Reset rand_used so that detection of calls to rand() will save random
seeds if needed by the slave.
@@ -645,12 +1119,52 @@ void THD::cleanup_after_query()
substatements must not set rand_used to 0 because it would remove the
detection of rand() by the calling statement.
*/
- if (!in_sub_stmt)
+ if (!in_sub_stmt) /* stored functions and triggers are a special case */
+ {
+ /* Forget those values, for next binlogger: */
+ stmt_depends_on_first_successful_insert_id_in_prev_stmt= 0;
+ auto_inc_intervals_in_cur_stmt_for_binlog.empty();
rand_used= 0;
+ }
+ if (first_successful_insert_id_in_cur_stmt > 0)
+ {
+ /* set what LAST_INSERT_ID() will return */
+ first_successful_insert_id_in_prev_stmt=
+ first_successful_insert_id_in_cur_stmt;
+ first_successful_insert_id_in_cur_stmt= 0;
+ substitute_null_with_insert_id= TRUE;
+ }
+ arg_of_last_insert_id_function= 0;
/* Free Items that were created during this execution */
free_items();
/* Reset where. */
where= THD::DEFAULT_WHERE;
+ /* reset table map for multi-table update */
+ table_map_for_update= 0;
+}
+
+
+/**
+ Create a LEX_STRING in this connection.
+
+ @param lex_str pointer to LEX_STRING object to be initialized
+ @param str initializer to be copied into lex_str
+ @param length length of str, in bytes
+ @param allocate_lex_string if TRUE, allocate new LEX_STRING object,
+ instead of using lex_str value
+ @return NULL on failure, or pointer to the LEX_STRING object
+*/
+LEX_STRING *THD::make_lex_string(LEX_STRING *lex_str,
+ const char* str, uint length,
+ bool allocate_lex_string)
+{
+ if (allocate_lex_string)
+ if (!(lex_str= (LEX_STRING *)alloc(sizeof(LEX_STRING))))
+ return 0;
+ if (!(lex_str->str= strmake_root(mem_root, str, length)))
+ return 0;
+ lex_str->length= length;
+ return lex_str;
}
@@ -679,9 +1193,9 @@ bool THD::convert_string(LEX_STRING *to, CHARSET_INFO *to_cs,
CHARSET_INFO *from_cs)
{
DBUG_ENTER("convert_string");
- size_s new_length= to_cs->mbmaxlen * from_length;
+ size_t new_length= to_cs->mbmaxlen * from_length;
uint dummy_errors;
- if (!(to->str= alloc(new_length+1)))
+ if (!(to->str= (char*) alloc(new_length+1)))
{
to->length= 0; // Safety fix
DBUG_RETURN(1); // EOM
@@ -764,7 +1278,8 @@ void THD::add_changed_table(TABLE *table)
DBUG_ASSERT((options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)) &&
table->file->has_transactions());
- add_changed_table(table->s->table_cache_key, table->s->key_length);
+ add_changed_table(table->s->table_cache_key.str,
+ (long) table->s->table_cache_key.length);
DBUG_VOID_RETURN;
}
@@ -824,8 +1339,7 @@ CHANGED_TABLE_LIST* THD::changed_table_dup(const char *key, long key_length)
return 0;
}
- new_table->key = (char *) (((byte*)new_table)+
- ALIGN_SIZE(sizeof(CHANGED_TABLE_LIST)));
+ new_table->key= ((char*)new_table)+ ALIGN_SIZE(sizeof(CHANGED_TABLE_LIST));
new_table->next = 0;
new_table->key_length = key_length;
::memcpy(new_table->key, key, key_length);
@@ -840,23 +1354,37 @@ int THD::send_explain_fields(select_result *result)
CHARSET_INFO *cs= system_charset_info;
field_list.push_back(new Item_return_int("id",3, MYSQL_TYPE_LONGLONG));
field_list.push_back(new Item_empty_string("select_type", 19, cs));
- field_list.push_back(item= new Item_empty_string("table", NAME_LEN, cs));
+ field_list.push_back(item= new Item_empty_string("table", NAME_CHAR_LEN, cs));
item->maybe_null= 1;
+ if (lex->describe & DESCRIBE_PARTITIONS)
+ {
+ /* Maximum length of string that make_used_partitions_str() can produce */
+ item= new Item_empty_string("partitions", MAX_PARTITIONS * (1 + FN_LEN),
+ cs);
+ field_list.push_back(item);
+ item->maybe_null= 1;
+ }
field_list.push_back(item= new Item_empty_string("type", 10, cs));
item->maybe_null= 1;
field_list.push_back(item=new Item_empty_string("possible_keys",
- NAME_LEN*MAX_KEY, cs));
+ NAME_CHAR_LEN*MAX_KEY, cs));
item->maybe_null=1;
- field_list.push_back(item=new Item_empty_string("key", NAME_LEN, cs));
+ field_list.push_back(item=new Item_empty_string("key", NAME_CHAR_LEN, cs));
item->maybe_null=1;
field_list.push_back(item=new Item_empty_string("key_len",
- NAME_LEN*MAX_KEY));
+ NAME_CHAR_LEN*MAX_KEY));
item->maybe_null=1;
field_list.push_back(item=new Item_empty_string("ref",
- NAME_LEN*MAX_REF_PARTS, cs));
+ NAME_CHAR_LEN*MAX_REF_PARTS,
+ cs));
item->maybe_null=1;
field_list.push_back(item= new Item_return_int("rows", 10,
MYSQL_TYPE_LONGLONG));
+ if (lex->describe & DESCRIBE_EXTENDED)
+ {
+ field_list.push_back(item= new Item_float("filtered", 0.1234, 2, 4));
+ item->maybe_null=1;
+ }
item->maybe_null= 1;
field_list.push_back(new Item_empty_string("Extra", 255, cs));
return (result->send_fields(field_list,
@@ -988,32 +1516,44 @@ bool select_send::send_fields(List<Item> &list, uint flags)
{
bool res;
if (!(res= thd->protocol->send_fields(&list, flags)))
- status= 1;
+ is_result_set_started= 1;
return res;
}
void select_send::abort()
{
DBUG_ENTER("select_send::abort");
- if (status && thd->spcont &&
- thd->spcont->find_handler(thd, thd->net.last_errno,
+ if (is_result_set_started && thd->spcont &&
+ thd->spcont->find_handler(thd, thd->main_da.sql_errno(),
MYSQL_ERROR::WARN_LEVEL_ERROR))
{
/*
- Executing stored procedure without a handler.
- Here we should actually send an error to the client,
- but as an error will break a multiple result set, the only thing we
- can do for now is to nicely end the current data set and remembering
- the error so that the calling routine will abort
+ 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.
+ 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->net.report_error= 0;
- send_eof();
- thd->net.report_error= 1; // Abort SP
+ thd->protocol->end_partial_result_set(thd);
}
DBUG_VOID_RETURN;
}
+/**
+ Cleanup an instance of this class for re-use
+ at next execution of a prepared statement/
+ stored procedure statement.
+*/
+
+void select_send::cleanup()
+{
+ is_result_set_started= FALSE;
+}
+
/* Send data to client. Returns 0 if ok */
bool select_send::send_data(List<Item> &items)
@@ -1029,7 +1569,7 @@ bool select_send::send_data(List<Item> &items)
InnoDB adaptive hash S-latch to avoid thread deadlocks if it was reserved
by thd
*/
- ha_release_temporary_latches(thd);
+ ha_release_temporary_latches(thd);
List_iterator_fast<Item> li(items);
Protocol *protocol= thd->protocol;
@@ -1049,20 +1589,24 @@ bool select_send::send_data(List<Item> &items)
}
}
thd->sent_row_count++;
- if (!thd->vio_ok())
- DBUG_RETURN(0);
- if (!thd->net.report_error)
+ if (thd->is_error())
+ {
+ protocol->remove_last_row();
+ DBUG_RETURN(1);
+ }
+ if (thd->vio_ok())
DBUG_RETURN(protocol->write());
- protocol->remove_last_row();
- DBUG_RETURN(1);
+ DBUG_RETURN(0);
}
bool select_send::send_eof()
{
- /* We may be passing the control from mysqld to the client: release the
- InnoDB adaptive hash S-latch to avoid thread deadlocks if it was reserved
- by thd */
- ha_release_temporary_latches(thd);
+ /*
+ We may be passing the control from mysqld to the client: release the
+ InnoDB adaptive hash S-latch to avoid thread deadlocks if it was reserved
+ by thd
+ */
+ ha_release_temporary_latches(thd);
/* Unlock tables before sending packet to gain some speed */
if (thd->lock)
@@ -1070,14 +1614,15 @@ bool select_send::send_eof()
mysql_unlock_tables(thd, thd->lock);
thd->lock=0;
}
- if (!thd->net.report_error)
- {
- ::send_eof(thd);
- status= 0;
- return 0;
- }
- else
- return 1;
+ /*
+ 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);
+ is_result_set_started= 0;
+ return FALSE;
}
@@ -1104,7 +1649,14 @@ bool select_to_file::send_eof()
if (my_close(file,MYF(MY_WME)))
error= 1;
if (!error)
- ::send_ok(thd,row_count);
+ {
+ /*
+ 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;
return error;
}
@@ -1172,7 +1724,8 @@ static File create_file(THD *thd, char *path, sql_exchange *exchange,
if (!dirname_length(exchange->file_name))
{
- strxnmov(path, FN_REFLEN, mysql_real_data_home, thd->db ? thd->db : "", NullS);
+ strxnmov(path, FN_REFLEN-1, mysql_real_data_home, thd->db ? thd->db : "",
+ NullS);
(void) fn_format(path, exchange->file_name, path, "", option);
}
else
@@ -1300,7 +1853,7 @@ bool select_export::send_data(List<Item> &items)
uint used_length=0,items_left=items.elements;
List_iterator_fast<Item> li(items);
- if (my_b_write(&cache,(byte*) exchange->line_start->ptr(),
+ if (my_b_write(&cache,(uchar*) exchange->line_start->ptr(),
exchange->line_start->length()))
goto err;
while ((item=li++))
@@ -1311,7 +1864,7 @@ bool select_export::send_data(List<Item> &items)
res=item->str_result(&tmp);
if (res && enclosed)
{
- if (my_b_write(&cache,(byte*) exchange->enclosed->ptr(),
+ if (my_b_write(&cache,(uchar*) exchange->enclosed->ptr(),
exchange->enclosed->length()))
goto err;
}
@@ -1323,10 +1876,10 @@ bool select_export::send_data(List<Item> &items)
{
null_buff[0]=escape_char;
null_buff[1]='N';
- if (my_b_write(&cache,(byte*) null_buff,2))
+ if (my_b_write(&cache,(uchar*) null_buff,2))
goto err;
}
- else if (my_b_write(&cache,(byte*) "NULL",4))
+ else if (my_b_write(&cache,(uchar*) "NULL",4))
goto err;
}
else
@@ -1417,16 +1970,16 @@ bool select_export::send_data(List<Item> &items)
is_ambiguous_field_sep) ?
field_sep_char : escape_char;
tmp_buff[1]= *pos ? *pos : '0';
- if (my_b_write(&cache,(byte*) start,(uint) (pos-start)) ||
- my_b_write(&cache,(byte*) tmp_buff,2))
+ if (my_b_write(&cache,(uchar*) start,(uint) (pos-start)) ||
+ my_b_write(&cache,(uchar*) tmp_buff,2))
goto err;
start=pos+1;
}
}
- if (my_b_write(&cache,(byte*) start,(uint) (pos-start)))
+ if (my_b_write(&cache,(uchar*) start,(uint) (pos-start)))
goto err;
}
- else if (my_b_write(&cache,(byte*) res->ptr(),used_length))
+ else if (my_b_write(&cache,(uchar*) res->ptr(),used_length))
goto err;
}
if (fixed_row_size)
@@ -1442,27 +1995,27 @@ bool select_export::send_data(List<Item> &items)
uint length=item->max_length-used_length;
for (; length > sizeof(space) ; length-=sizeof(space))
{
- if (my_b_write(&cache,(byte*) space,sizeof(space)))
+ if (my_b_write(&cache,(uchar*) space,sizeof(space)))
goto err;
}
- if (my_b_write(&cache,(byte*) space,length))
+ if (my_b_write(&cache,(uchar*) space,length))
goto err;
}
}
if (res && enclosed)
{
- if (my_b_write(&cache, (byte*) exchange->enclosed->ptr(),
+ if (my_b_write(&cache, (uchar*) exchange->enclosed->ptr(),
exchange->enclosed->length()))
goto err;
}
if (--items_left)
{
- if (my_b_write(&cache, (byte*) exchange->field_term->ptr(),
+ if (my_b_write(&cache, (uchar*) exchange->field_term->ptr(),
field_term_length))
goto err;
}
}
- if (my_b_write(&cache,(byte*) exchange->line_term->ptr(),
+ if (my_b_write(&cache,(uchar*) exchange->line_term->ptr(),
exchange->line_term->length()))
goto err;
DBUG_RETURN(0);
@@ -1509,10 +2062,10 @@ bool select_dump::send_data(List<Item> &items)
res=item->str_result(&tmp);
if (!res) // If NULL
{
- if (my_b_write(&cache,(byte*) "",1))
+ if (my_b_write(&cache,(uchar*) "",1))
goto err;
}
- else if (my_b_write(&cache,(byte*) res->ptr(),res->length()))
+ else if (my_b_write(&cache,(uchar*) res->ptr(),res->length()))
{
my_error(ER_ERROR_ON_WRITE, MYF(0), path, my_errno);
goto err;
@@ -1747,18 +2300,20 @@ void Query_arena::cleanup_stmt()
}
/*
- Statement functions
+ Statement functions
*/
Statement::Statement(LEX *lex_arg, MEM_ROOT *mem_root_arg,
enum enum_state state_arg, ulong id_arg)
:Query_arena(mem_root_arg, state_arg),
id(id_arg),
- set_query_id(1),
+ mark_used_columns(MARK_COLUMNS_READ),
lex(lex_arg),
query(0),
query_length(0),
- cursor(0)
+ cursor(0),
+ db(NULL),
+ db_length(0)
{
name.str= NULL;
}
@@ -1773,7 +2328,7 @@ Query_arena::Type Statement::type() const
void Statement::set_statement(Statement *stmt)
{
id= stmt->id;
- set_query_id= stmt->set_query_id;
+ mark_used_columns= stmt->mark_used_columns;
lex= stmt->lex;
query= stmt->query;
query_length= stmt->query_length;
@@ -1847,13 +2402,13 @@ Statement::~Statement()
C_MODE_START
-static byte *
-get_statement_id_as_hash_key(const byte *record, uint *key_length,
+static uchar *
+get_statement_id_as_hash_key(const uchar *record, size_t *key_length,
my_bool not_used __attribute__((unused)))
{
const Statement *statement= (const Statement *) record;
*key_length= sizeof(statement->id);
- return (byte *) &((const Statement *) statement)->id;
+ return (uchar *) &((const Statement *) statement)->id;
}
static void delete_statement_as_hash_key(void *key)
@@ -1861,11 +2416,11 @@ static void delete_statement_as_hash_key(void *key)
delete (Statement *) key;
}
-static byte *get_stmt_name_hash_key(Statement *entry, uint *length,
+static uchar *get_stmt_name_hash_key(Statement *entry, size_t *length,
my_bool not_used __attribute__((unused)))
{
- *length=(uint) entry->name.length;
- return (byte*) entry->name.str;
+ *length= entry->name.length;
+ return (uchar*) entry->name.str;
}
C_MODE_END
@@ -1910,7 +2465,7 @@ Statement_map::Statement_map() :
int Statement_map::insert(THD *thd, Statement *statement)
{
- if (my_hash_insert(&st_hash, (byte*) statement))
+ if (my_hash_insert(&st_hash, (uchar*) statement))
{
/*
Delete is needed only in case of an insert failure. In all other
@@ -1920,7 +2475,7 @@ int Statement_map::insert(THD *thd, Statement *statement)
my_error(ER_OUT_OF_RESOURCES, MYF(0));
goto err_st_hash;
}
- if (statement->name.str && my_hash_insert(&names_hash, (byte*) statement))
+ if (statement->name.str && my_hash_insert(&names_hash, (uchar*) statement))
{
my_error(ER_OUT_OF_RESOURCES, MYF(0));
goto err_names_hash;
@@ -1948,9 +2503,9 @@ int Statement_map::insert(THD *thd, Statement *statement)
err_max:
if (statement->name.str)
- hash_delete(&names_hash, (byte*) statement);
+ hash_delete(&names_hash, (uchar*) statement);
err_names_hash:
- hash_delete(&st_hash, (byte*) statement);
+ hash_delete(&st_hash, (uchar*) statement);
err_st_hash:
return 1;
}
@@ -1971,9 +2526,9 @@ void Statement_map::erase(Statement *statement)
if (statement == last_found_statement)
last_found_statement= 0;
if (statement->name.str)
- hash_delete(&names_hash, (byte *) statement);
+ hash_delete(&names_hash, (uchar *) statement);
- hash_delete(&st_hash, (byte *) statement);
+ hash_delete(&st_hash, (uchar *) statement);
pthread_mutex_lock(&LOCK_prepared_stmt_count);
DBUG_ASSERT(prepared_stmt_count > 0);
prepared_stmt_count--;
@@ -2040,7 +2595,7 @@ bool select_dumpvar::send_data(List<Item> &items)
suv->update();
}
}
- DBUG_RETURN(0);
+ DBUG_RETURN(thd->is_error());
}
bool select_dumpvar::send_eof()
@@ -2048,7 +2603,12 @@ bool select_dumpvar::send_eof()
if (! row_count)
push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
ER_SP_FETCH_NO_DATA, ER(ER_SP_FETCH_NO_DATA));
- ::send_ok(thd,row_count);
+ /*
+ 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);
return 0;
}
@@ -2102,6 +2662,7 @@ void Security_context::init()
host= user= priv_user= ip= 0;
host_or_ip= "connecting host";
priv_host[0]= '\0';
+ master_access= 0;
#ifndef NO_EMBEDDED_ACCESS_CHECKS
db_access= NO_ACCESS;
#endif
@@ -2129,6 +2690,114 @@ void Security_context::skip_grants()
}
+bool Security_context::set_user(char *user_arg)
+{
+ safeFree(user);
+ user= my_strdup(user_arg, MYF(0));
+ return user == 0;
+}
+
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+/**
+ Initialize this security context from the passed in credentials
+ and activate it in the current thread.
+
+ @param thd
+ @param definer_user
+ @param definer_host
+ @param db
+ @param[out] backup Save a pointer to the current security context
+ in the thread. In case of success it points to the
+ saved old context, otherwise it points to NULL.
+
+
+ During execution of a statement, multiple security contexts may
+ be needed:
+ - the security context of the authenticated user, used as the
+ default security context for all top-level statements
+ - in case of a view or a stored program, possibly the security
+ context of the definer of the routine, if the object is
+ defined with SQL SECURITY DEFINER option.
+
+ The currently "active" security context is parameterized in THD
+ member security_ctx. By default, after a connection is
+ established, this member points at the "main" security context
+ - the credentials of the authenticated user.
+
+ Later, if we would like to execute some sub-statement or a part
+ of a statement under credentials of a different user, e.g.
+ definer of a procedure, we authenticate this user in a local
+ instance of Security_context by means of this method (and
+ ultimately by means of acl_getroot_no_password), and make the
+ local instance active in the thread by re-setting
+ thd->security_ctx pointer.
+
+ Note, that the life cycle and memory management of the "main" and
+ temporary security contexts are different.
+ For the main security context, the memory for user/host/ip is
+ allocated on system heap, and the THD class frees this memory in
+ its destructor. The only case when contents of the main security
+ context may change during its life time is when someone issued
+ CHANGE USER command.
+ Memory management of a "temporary" security context is
+ responsibility of the module that creates it.
+
+ @retval TRUE there is no user with the given credentials. The erro
+ is reported in the thread.
+ @retval FALSE success
+*/
+
+bool
+Security_context::
+change_security_context(THD *thd,
+ LEX_STRING *definer_user,
+ LEX_STRING *definer_host,
+ LEX_STRING *db,
+ Security_context **backup)
+{
+ bool needs_change;
+
+ DBUG_ENTER("Security_context::change_security_context");
+
+ DBUG_ASSERT(definer_user->str && definer_host->str);
+
+ *backup= NULL;
+ /*
+ The current security context may have NULL members
+ if we have just started the thread and not authenticated
+ any user. This use case is currently in events worker thread.
+ */
+ needs_change= (thd->security_ctx->priv_user == NULL ||
+ strcmp(definer_user->str, thd->security_ctx->priv_user) ||
+ thd->security_ctx->priv_host == NULL ||
+ my_strcasecmp(system_charset_info, definer_host->str,
+ thd->security_ctx->priv_host));
+ if (needs_change)
+ {
+ if (acl_getroot_no_password(this, definer_user->str, definer_host->str,
+ definer_host->str, db->str))
+ {
+ my_error(ER_NO_SUCH_USER, MYF(0), definer_user->str,
+ definer_host->str);
+ DBUG_RETURN(TRUE);
+ }
+ *backup= thd->security_ctx;
+ thd->security_ctx= this;
+ }
+
+ DBUG_RETURN(FALSE);
+}
+
+
+void
+Security_context::restore_security_context(THD *thd,
+ Security_context *backup)
+{
+ if (backup)
+ thd->security_ctx= backup;
+}
+#endif
+
/****************************************************************************
Handling of open and locked tables states.
@@ -2142,6 +2811,7 @@ void THD::reset_n_backup_open_tables_state(Open_tables_state *backup)
DBUG_ENTER("reset_n_backup_open_tables_state");
backup->set_open_tables_state(this);
reset_open_tables_state();
+ state_flags|= Open_tables_state::BACKUPS_AVAIL;
DBUG_VOID_RETURN;
}
@@ -2156,12 +2826,68 @@ void THD::restore_backup_open_tables_state(Open_tables_state *backup)
DBUG_ASSERT(open_tables == 0 && temporary_tables == 0 &&
handler_tables == 0 && derived_tables == 0 &&
lock == 0 && locked_tables == 0 &&
- prelocked_mode == NON_PRELOCKED);
+ prelocked_mode == NON_PRELOCKED &&
+ m_reprepare_observer == NULL);
set_open_tables_state(backup);
DBUG_VOID_RETURN;
}
+/**
+ 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
+*/
+extern "C" int thd_killed(const MYSQL_THD thd)
+{
+ return(thd->killed);
+}
+
+/**
+ Return the thread id of a user thread
+ @param thd user thread
+ @return thread id
+*/
+extern "C" unsigned long thd_get_thread_id(const MYSQL_THD thd)
+{
+ return((unsigned long)thd->thread_id);
+}
+
+#ifdef INNODB_COMPATIBILITY_HOOKS
+extern "C" struct charset_info_st *thd_charset(MYSQL_THD thd)
+{
+ return(thd->charset());
+}
+
+extern "C" char **thd_query(MYSQL_THD thd)
+{
+ return(&thd->query);
+}
+
+extern "C" int thd_slave_thread(const MYSQL_THD thd)
+{
+ return(thd->slave_thread);
+}
+
+extern "C" int thd_non_transactional_update(const MYSQL_THD thd)
+{
+ return(thd->transaction.all.modified_non_trans_table);
+}
+
+extern "C" int thd_binlog_format(const MYSQL_THD thd)
+{
+ if (mysql_bin_log.is_open() && (thd->options & OPTION_BIN_LOG))
+ return (int) thd->variables.binlog_format;
+ else
+ return BINLOG_FORMAT_UNSPEC;
+}
+
+extern "C" void thd_mark_transaction_to_rollback(MYSQL_THD thd, bool all)
+{
+ mark_transaction_to_rollback(thd, all);
+}
+#endif // INNODB_COMPATIBILITY_HOOKS */
/****************************************************************************
Handling of statement states in functions and triggers.
@@ -2195,46 +2921,67 @@ void THD::restore_backup_open_tables_state(Open_tables_state *backup)
void THD::reset_sub_statement_state(Sub_statement_state *backup,
uint new_state)
{
+#ifndef EMBEDDED_LIBRARY
+ /* BUG#33029, if we are replicating from a buggy master, reset
+ auto_inc_intervals_forced to prevent substatement
+ (triggers/functions) from using erroneous INSERT_ID value
+ */
+ if (rpl_master_erroneous_autoinc(this))
+ {
+ DBUG_ASSERT(backup->auto_inc_intervals_forced.nb_elements() == 0);
+ auto_inc_intervals_forced.swap(&backup->auto_inc_intervals_forced);
+ }
+#endif
+
backup->options= options;
backup->in_sub_stmt= in_sub_stmt;
- backup->no_send_ok= net.no_send_ok;
backup->enable_slow_log= enable_slow_log;
- backup->last_insert_id= last_insert_id;
- backup->next_insert_id= next_insert_id;
- backup->current_insert_id= current_insert_id;
- backup->insert_id_used= insert_id_used;
- backup->last_insert_id_used= last_insert_id_used;
- backup->clear_next_insert_id= clear_next_insert_id;
backup->limit_found_rows= limit_found_rows;
backup->examined_row_count= examined_row_count;
backup->sent_row_count= sent_row_count;
backup->cuted_fields= cuted_fields;
backup->client_capabilities= client_capabilities;
backup->savepoints= transaction.savepoints;
+ backup->first_successful_insert_id_in_prev_stmt=
+ first_successful_insert_id_in_prev_stmt;
+ backup->first_successful_insert_id_in_cur_stmt=
+ first_successful_insert_id_in_cur_stmt;
- if (!lex->requires_prelocking() || is_update_query(lex->sql_command))
+ if ((!lex->requires_prelocking() || is_update_query(lex->sql_command)) &&
+ !current_stmt_binlog_row_based)
+ {
options&= ~OPTION_BIN_LOG;
+ }
- if ((backup->options & OPTION_BIN_LOG) && is_update_query(lex->sql_command))
+ if ((backup->options & OPTION_BIN_LOG) && is_update_query(lex->sql_command)&&
+ !current_stmt_binlog_row_based)
mysql_bin_log.start_union_events(this, this->query_id);
/* Disable result sets */
client_capabilities &= ~CLIENT_MULTI_RESULTS;
in_sub_stmt|= new_state;
- next_insert_id= 0;
- insert_id_used= 0;
examined_row_count= 0;
sent_row_count= 0;
cuted_fields= 0;
transaction.savepoints= 0;
-
- /* Surpress OK packets in case if we will execute statements */
- net.no_send_ok= TRUE;
+ first_successful_insert_id_in_cur_stmt= 0;
}
void THD::restore_sub_statement_state(Sub_statement_state *backup)
{
+#ifndef EMBEDDED_LIBRARY
+ /* BUG#33029, if we are replicating from a buggy master, restore
+ auto_inc_intervals_forced so that the top statement can use the
+ INSERT_ID value set before this statement.
+ */
+ if (rpl_master_erroneous_autoinc(this))
+ {
+ backup->auto_inc_intervals_forced.swap(&auto_inc_intervals_forced);
+ DBUG_ASSERT(backup->auto_inc_intervals_forced.nb_elements() == 0);
+ }
+#endif
+
/*
To save resources we want to release savepoints which were created
during execution of function or trigger before leaving their savepoint
@@ -2252,14 +2999,11 @@ void THD::restore_sub_statement_state(Sub_statement_state *backup)
transaction.savepoints= backup->savepoints;
options= backup->options;
in_sub_stmt= backup->in_sub_stmt;
- net.no_send_ok= backup->no_send_ok;
enable_slow_log= backup->enable_slow_log;
- last_insert_id= backup->last_insert_id;
- next_insert_id= backup->next_insert_id;
- current_insert_id= backup->current_insert_id;
- insert_id_used= backup->insert_id_used;
- last_insert_id_used= backup->last_insert_id_used;
- clear_next_insert_id= backup->clear_next_insert_id;
+ first_successful_insert_id_in_prev_stmt=
+ backup->first_successful_insert_id_in_prev_stmt;
+ first_successful_insert_id_in_cur_stmt=
+ backup->first_successful_insert_id_in_cur_stmt;
limit_found_rows= backup->limit_found_rows;
sent_row_count= backup->sent_row_count;
client_capabilities= backup->client_capabilities;
@@ -2271,7 +3015,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))
+ if ((options & OPTION_BIN_LOG) && is_update_query(lex->sql_command) &&
+ !current_stmt_binlog_row_based)
mysql_bin_log.stop_union_events(this);
/*
@@ -2305,17 +3050,20 @@ void mark_transaction_to_rollback(THD *thd, bool all)
pthread_mutex_t LOCK_xid_cache;
HASH xid_cache;
-static byte *xid_get_hash_key(const byte *ptr,uint *length,
+extern "C" uchar *xid_get_hash_key(const uchar *, size_t *, my_bool);
+extern "C" void xid_free_hash(void *);
+
+uchar *xid_get_hash_key(const uchar *ptr, size_t *length,
my_bool not_used __attribute__((unused)))
{
*length=((XID_STATE*)ptr)->xid.key_length();
return ((XID_STATE*)ptr)->xid.key();
}
-static void xid_free_hash (void *ptr)
+void xid_free_hash(void *ptr)
{
if (!((XID_STATE*)ptr)->in_thd)
- my_free((gptr)ptr, MYF(0));
+ my_free((uchar*)ptr, MYF(0));
}
bool xid_cache_init()
@@ -2357,7 +3105,7 @@ bool xid_cache_insert(XID *xid, enum xa_states xa_state)
xs->xa_state=xa_state;
xs->xid.set(xid);
xs->in_thd=0;
- res=my_hash_insert(&xid_cache, (byte*)xs);
+ res=my_hash_insert(&xid_cache, (uchar*)xs);
}
pthread_mutex_unlock(&LOCK_xid_cache);
return res;
@@ -2369,7 +3117,7 @@ bool xid_cache_insert(XID_STATE *xid_state)
pthread_mutex_lock(&LOCK_xid_cache);
DBUG_ASSERT(hash_search(&xid_cache, xid_state->xid.key(),
xid_state->xid.key_length())==0);
- my_bool res=my_hash_insert(&xid_cache, (byte*)xid_state);
+ my_bool res=my_hash_insert(&xid_cache, (uchar*)xid_state);
pthread_mutex_unlock(&LOCK_xid_cache);
return res;
}
@@ -2378,7 +3126,624 @@ bool xid_cache_insert(XID_STATE *xid_state)
void xid_cache_delete(XID_STATE *xid_state)
{
pthread_mutex_lock(&LOCK_xid_cache);
- hash_delete(&xid_cache, (byte *)xid_state);
+ hash_delete(&xid_cache, (uchar *)xid_state);
pthread_mutex_unlock(&LOCK_xid_cache);
}
+/*
+ Implementation of interface to write rows to the binary log through the
+ thread. The thread is responsible for writing the rows it has
+ inserted/updated/deleted.
+*/
+
+#ifndef MYSQL_CLIENT
+
+/*
+ Template member function for ensuring that there is an rows log
+ event of the apropriate type before proceeding.
+
+ PRE CONDITION:
+ - Events of type 'RowEventT' have the type code 'type_code'.
+
+ POST CONDITION:
+ If a non-NULL pointer is returned, the pending event for thread 'thd' will
+ be an event of type 'RowEventT' (which have the type code 'type_code')
+ will either empty or have enough space to hold 'needed' bytes. In
+ addition, the columns bitmap will be correct for the row, meaning that
+ the pending event will be flushed if the columns in the event differ from
+ the columns suppled to the function.
+
+ RETURNS
+ If no error, a non-NULL pending event (either one which already existed or
+ the newly created one).
+ If error, NULL.
+ */
+
+template <class RowsEventT> Rows_log_event*
+THD::binlog_prepare_pending_rows_event(TABLE* table, uint32 serv_id,
+ MY_BITMAP const* cols,
+ size_t colcnt,
+ size_t needed,
+ bool is_transactional,
+ RowsEventT *hint __attribute__((unused)))
+{
+ DBUG_ENTER("binlog_prepare_pending_rows_event");
+ /* Pre-conditions */
+ DBUG_ASSERT(table->s->table_map_id != ~0UL);
+
+ /* Fetch the type code for the RowsEventT template parameter */
+ int const type_code= RowsEventT::TYPE_CODE;
+
+ /*
+ There is no good place to set up the transactional data, so we
+ have to do it here.
+ */
+ if (binlog_setup_trx_data())
+ DBUG_RETURN(NULL);
+
+ Rows_log_event* pending= binlog_get_pending_rows_event();
+
+ if (unlikely(pending && !pending->is_valid()))
+ DBUG_RETURN(NULL);
+
+ /*
+ Check if the current event is non-NULL and a write-rows
+ event. Also check if the table provided is mapped: if it is not,
+ then we have switched to writing to a new table.
+ If there is no pending event, we need to create one. If there is a pending
+ event, but it's not about the same table id, or not of the same type
+ (between Write, Update and Delete), or not the same affected columns, or
+ going to be too big, flush this event to disk and create a new pending
+ event.
+ */
+ if (!pending ||
+ pending->server_id != serv_id ||
+ pending->get_table_id() != table->s->table_map_id ||
+ pending->get_type_code() != type_code ||
+ pending->get_data_size() + needed > opt_binlog_rows_event_max_size ||
+ pending->get_width() != colcnt ||
+ !bitmap_cmp(pending->get_cols(), cols))
+ {
+ /* Create a new RowsEventT... */
+ Rows_log_event* const
+ ev= new RowsEventT(this, table, table->s->table_map_id, cols,
+ is_transactional);
+ if (unlikely(!ev))
+ DBUG_RETURN(NULL);
+ ev->server_id= serv_id; // I don't like this, it's too easy to forget.
+ /*
+ 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)))
+ {
+ delete ev;
+ DBUG_RETURN(NULL);
+ }
+
+ DBUG_RETURN(ev); /* This is the new pending event */
+ }
+ DBUG_RETURN(pending); /* This is the current pending event */
+}
+
+#ifdef HAVE_EXPLICIT_TEMPLATE_INSTANTIATION
+/*
+ Instantiate the versions we need, we have -fno-implicit-template as
+ compiling option.
+*/
+template Rows_log_event*
+THD::binlog_prepare_pending_rows_event(TABLE*, uint32, MY_BITMAP const*,
+ size_t, size_t, bool,
+ Write_rows_log_event*);
+
+template Rows_log_event*
+THD::binlog_prepare_pending_rows_event(TABLE*, uint32, MY_BITMAP const*,
+ size_t colcnt, size_t, bool,
+ Delete_rows_log_event *);
+
+template Rows_log_event*
+THD::binlog_prepare_pending_rows_event(TABLE*, uint32, MY_BITMAP const*,
+ size_t colcnt, size_t, bool,
+ Update_rows_log_event *);
+#endif
+
+#ifdef NOT_USED
+static char const*
+field_type_name(enum_field_types type)
+{
+ switch (type) {
+ case MYSQL_TYPE_DECIMAL:
+ return "MYSQL_TYPE_DECIMAL";
+ case MYSQL_TYPE_TINY:
+ return "MYSQL_TYPE_TINY";
+ case MYSQL_TYPE_SHORT:
+ return "MYSQL_TYPE_SHORT";
+ case MYSQL_TYPE_LONG:
+ return "MYSQL_TYPE_LONG";
+ case MYSQL_TYPE_FLOAT:
+ return "MYSQL_TYPE_FLOAT";
+ case MYSQL_TYPE_DOUBLE:
+ return "MYSQL_TYPE_DOUBLE";
+ case MYSQL_TYPE_NULL:
+ return "MYSQL_TYPE_NULL";
+ case MYSQL_TYPE_TIMESTAMP:
+ return "MYSQL_TYPE_TIMESTAMP";
+ case MYSQL_TYPE_LONGLONG:
+ return "MYSQL_TYPE_LONGLONG";
+ case MYSQL_TYPE_INT24:
+ return "MYSQL_TYPE_INT24";
+ case MYSQL_TYPE_DATE:
+ return "MYSQL_TYPE_DATE";
+ case MYSQL_TYPE_TIME:
+ return "MYSQL_TYPE_TIME";
+ case MYSQL_TYPE_DATETIME:
+ return "MYSQL_TYPE_DATETIME";
+ case MYSQL_TYPE_YEAR:
+ return "MYSQL_TYPE_YEAR";
+ case MYSQL_TYPE_NEWDATE:
+ return "MYSQL_TYPE_NEWDATE";
+ case MYSQL_TYPE_VARCHAR:
+ return "MYSQL_TYPE_VARCHAR";
+ case MYSQL_TYPE_BIT:
+ return "MYSQL_TYPE_BIT";
+ case MYSQL_TYPE_NEWDECIMAL:
+ return "MYSQL_TYPE_NEWDECIMAL";
+ case MYSQL_TYPE_ENUM:
+ return "MYSQL_TYPE_ENUM";
+ case MYSQL_TYPE_SET:
+ return "MYSQL_TYPE_SET";
+ case MYSQL_TYPE_TINY_BLOB:
+ return "MYSQL_TYPE_TINY_BLOB";
+ case MYSQL_TYPE_MEDIUM_BLOB:
+ return "MYSQL_TYPE_MEDIUM_BLOB";
+ case MYSQL_TYPE_LONG_BLOB:
+ return "MYSQL_TYPE_LONG_BLOB";
+ case MYSQL_TYPE_BLOB:
+ return "MYSQL_TYPE_BLOB";
+ case MYSQL_TYPE_VAR_STRING:
+ return "MYSQL_TYPE_VAR_STRING";
+ case MYSQL_TYPE_STRING:
+ return "MYSQL_TYPE_STRING";
+ case MYSQL_TYPE_GEOMETRY:
+ return "MYSQL_TYPE_GEOMETRY";
+ }
+ return "Unknown";
+}
+#endif
+
+
+namespace {
+ /**
+ Class to handle temporary allocation of memory for row data.
+
+ The responsibilities of the class is to provide memory for
+ packing one or two rows of packed data (depending on what
+ constructor is called).
+
+ In order to make the allocation more efficient for "simple" rows,
+ i.e., rows that do not contain any blobs, a pointer to the
+ allocated memory is of memory is stored in the table structure
+ for simple rows. If memory for a table containing a blob field
+ is requested, only memory for that is allocated, and subsequently
+ released when the object is destroyed.
+
+ */
+ class Row_data_memory {
+ public:
+ /**
+ Build an object to keep track of a block-local piece of memory
+ for storing a row of data.
+
+ @param table
+ Table where the pre-allocated memory is stored.
+
+ @param length
+ Length of data that is needed, if the record contain blobs.
+ */
+ Row_data_memory(TABLE *table, size_t const len1)
+ : m_memory(0)
+ {
+#ifndef DBUG_OFF
+ m_alloc_checked= FALSE;
+#endif
+ allocate_memory(table, len1);
+ m_ptr[0]= has_memory() ? m_memory : 0;
+ m_ptr[1]= 0;
+ }
+
+ Row_data_memory(TABLE *table, size_t const len1, size_t const len2)
+ : m_memory(0)
+ {
+#ifndef DBUG_OFF
+ m_alloc_checked= FALSE;
+#endif
+ allocate_memory(table, len1 + len2);
+ m_ptr[0]= has_memory() ? m_memory : 0;
+ m_ptr[1]= has_memory() ? m_memory + len1 : 0;
+ }
+
+ ~Row_data_memory()
+ {
+ if (m_memory != 0 && m_release_memory_on_destruction)
+ my_free((uchar*) m_memory, MYF(MY_WME));
+ }
+
+ /**
+ Is there memory allocated?
+
+ @retval true There is memory allocated
+ @retval false Memory allocation failed
+ */
+ bool has_memory() const {
+#ifndef DBUG_OFF
+ m_alloc_checked= TRUE;
+#endif
+ return m_memory != 0;
+ }
+
+ uchar *slot(uint s)
+ {
+ DBUG_ASSERT(s < sizeof(m_ptr)/sizeof(*m_ptr));
+ DBUG_ASSERT(m_ptr[s] != 0);
+ DBUG_ASSERT(m_alloc_checked == TRUE);
+ return m_ptr[s];
+ }
+
+ private:
+ void allocate_memory(TABLE *const table, size_t const total_length)
+ {
+ if (table->s->blob_fields == 0)
+ {
+ /*
+ The maximum length of a packed record is less than this
+ length. We use this value instead of the supplied length
+ when allocating memory for records, since we don't know how
+ the memory will be used in future allocations.
+
+ Since table->s->reclength is for unpacked records, we have
+ to add two bytes for each field, which can potentially be
+ added to hold the length of a packed field.
+ */
+ size_t const maxlen= table->s->reclength + 2 * table->s->fields;
+
+ /*
+ Allocate memory for two records if memory hasn't been
+ allocated. We allocate memory for two records so that it can
+ be used when processing update rows as well.
+ */
+ if (table->write_row_record == 0)
+ table->write_row_record=
+ (uchar *) alloc_root(&table->mem_root, 2 * maxlen);
+ m_memory= table->write_row_record;
+ m_release_memory_on_destruction= FALSE;
+ }
+ else
+ {
+ m_memory= (uchar *) my_malloc(total_length, MYF(MY_WME));
+ m_release_memory_on_destruction= TRUE;
+ }
+ }
+
+#ifndef DBUG_OFF
+ mutable bool m_alloc_checked;
+#endif
+ bool m_release_memory_on_destruction;
+ uchar *m_memory;
+ uchar *m_ptr[2];
+ };
+}
+
+
+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());
+
+ /*
+ Pack records into format for transfer. We are allocating more
+ memory than needed, but that doesn't matter.
+ */
+ Row_data_memory memory(table, max_row_length(table, record));
+ if (!memory.has_memory())
+ return HA_ERR_OUT_OF_MEM;
+
+ uchar *row_data= memory.slot(0);
+
+ size_t const len= pack_row(table, cols, row_data, record);
+
+ Rows_log_event* const ev=
+ binlog_prepare_pending_rows_event(table, server_id, cols, colcnt,
+ len, is_trans,
+ static_cast<Write_rows_log_event*>(0));
+
+ if (unlikely(ev == 0))
+ return HA_ERR_OUT_OF_MEM;
+
+ return ev->add_row_data(row_data, len);
+}
+
+int THD::binlog_update_row(TABLE* table, bool is_trans,
+ MY_BITMAP const* cols, size_t colcnt,
+ const uchar *before_record,
+ const uchar *after_record)
+{
+ DBUG_ASSERT(current_stmt_binlog_row_based && 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);
+
+ Row_data_memory row_data(table, before_maxlen, after_maxlen);
+ if (!row_data.has_memory())
+ return HA_ERR_OUT_OF_MEM;
+
+ uchar *before_row= row_data.slot(0);
+ uchar *after_row= row_data.slot(1);
+
+ size_t const before_size= pack_row(table, cols, before_row,
+ before_record);
+ size_t const after_size= pack_row(table, cols, after_row,
+ after_record);
+
+ /*
+ Don't print debug messages when running valgrind since they can
+ trigger false warnings.
+ */
+#ifndef HAVE_purify
+ DBUG_DUMP("before_record", before_record, table->s->reclength);
+ DBUG_DUMP("after_record", after_record, table->s->reclength);
+ DBUG_DUMP("before_row", before_row, before_size);
+ DBUG_DUMP("after_row", after_row, after_size);
+#endif
+
+ Rows_log_event* const ev=
+ binlog_prepare_pending_rows_event(table, server_id, cols, colcnt,
+ before_size + after_size, is_trans,
+ static_cast<Update_rows_log_event*>(0));
+
+ if (unlikely(ev == 0))
+ return HA_ERR_OUT_OF_MEM;
+
+ return
+ ev->add_row_data(before_row, before_size) ||
+ ev->add_row_data(after_row, after_size);
+}
+
+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());
+
+ /*
+ Pack records into format for transfer. We are allocating more
+ memory than needed, but that doesn't matter.
+ */
+ Row_data_memory memory(table, max_row_length(table, record));
+ if (unlikely(!memory.has_memory()))
+ return HA_ERR_OUT_OF_MEM;
+
+ uchar *row_data= memory.slot(0);
+
+ size_t const len= pack_row(table, cols, row_data, record);
+
+ Rows_log_event* const ev=
+ binlog_prepare_pending_rows_event(table, server_id, cols, colcnt,
+ len, is_trans,
+ static_cast<Delete_rows_log_event*>(0));
+
+ if (unlikely(ev == 0))
+ return HA_ERR_OUT_OF_MEM;
+
+ return ev->add_row_data(row_data, len);
+}
+
+
+int THD::binlog_remove_pending_rows_event(bool clear_maps)
+{
+ 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);
+
+ if (clear_maps)
+ binlog_table_maps= 0;
+
+ DBUG_RETURN(0);
+}
+
+int THD::binlog_flush_pending_rows_event(bool stmt_end)
+{
+ DBUG_ENTER("THD::binlog_flush_pending_rows_event");
+ /*
+ We shall flush the pending event even if we are not in row-based
+ mode: it might be the case that we left row-based mode before
+ flushing anything (e.g., if we have explicitly locked tables).
+ */
+ if (!mysql_bin_log.is_open())
+ DBUG_RETURN(0);
+
+ /*
+ Mark the event as the last event of a statement if the stmt_end
+ flag is set.
+ */
+ int error= 0;
+ if (Rows_log_event *pending= binlog_get_pending_rows_event())
+ {
+ if (stmt_end)
+ {
+ pending->set_flags(Rows_log_event::STMT_END_F);
+ pending->flags|= LOG_EVENT_UPDATE_TABLE_MAP_VERSION_F;
+ binlog_table_maps= 0;
+ }
+
+ error= mysql_bin_log.flush_and_set_pending_rows_event(this, 0);
+ }
+
+ DBUG_RETURN(error);
+}
+
+
+#if !defined(DBUG_OFF) && !defined(_lint)
+static const char *
+show_query_type(THD::enum_binlog_query_type qtype)
+{
+ switch (qtype) {
+ case THD::ROW_QUERY_TYPE:
+ 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);
+ }
+ static char buf[64];
+ sprintf(buf, "UNKNOWN#%d", qtype);
+ return buf;
+}
+#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.
+
+ This function should be called after the all calls to ha_*_row()
+ functions have been issued, but before tables are unlocked and
+ closed.
+
+ 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.
+
+ 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.
+
+ RETURN VALUE
+ Error code, or 0 if no error.
+*/
+int THD::binlog_query(THD::enum_binlog_query_type qtype, char const *query_arg,
+ ulong query_len, bool is_trans, bool suppress_use,
+ THD::killed_state killed_status_arg)
+{
+ DBUG_ENTER("THD::binlog_query");
+ DBUG_PRINT("enter", ("qtype: %s query: '%s'",
+ show_query_type(qtype), query_arg));
+ DBUG_ASSERT(query_arg && mysql_bin_log.is_open());
+
+ /*
+ If we are not in prelocked mode, mysql_unlock_tables() will be
+ called after this binlog_query(), so we have to flush the pending
+ rows event with the STMT_END_F set to unlock all tables at the
+ slave side as well.
+
+ 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))
+ DBUG_RETURN(error);
+
+ /*
+ If we are in statement mode and trying to log an unsafe statement,
+ we should print a warning.
+ */
+ if (lex->is_stmt_unsafe() &&
+ variables.binlog_format == BINLOG_FORMAT_STMT)
+ {
+ push_warning(this, MYSQL_ERROR::WARN_LEVEL_WARN,
+ ER_BINLOG_UNSAFE_STATEMENT,
+ ER(ER_BINLOG_UNSAFE_STATEMENT));
+ if (!(binlog_flags & BINLOG_FLAG_UNSAFE_STMT_PRINTED))
+ {
+ sql_print_warning("%s Statement: %.*s",
+ ER(ER_BINLOG_UNSAFE_STATEMENT),
+ MYSQL_ERRMSG_SIZE, query_arg);
+ binlog_flags|= BINLOG_FLAG_UNSAFE_STMT_PRINTED;
+ }
+ }
+
+ switch (qtype) {
+ 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)
+ 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.
+
+ Make sure to change in check_table_binlog_row_based() according
+ to how you treat this.
+ */
+ 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,
+ killed_status_arg);
+ qinfo.flags|= LOG_EVENT_UPDATE_TABLE_MAP_VERSION_F;
+ /*
+ Binlog table maps will be irrelevant after a Query_log_event
+ (they are just removed on the slave side) so after the query
+ log event is written to the binary log, we pretend that no
+ table maps were written.
+ */
+ int error= mysql_bin_log.write(&qinfo);
+ binlog_table_maps= 0;
+ DBUG_RETURN(error);
+ }
+ break;
+
+ case THD::QUERY_TYPE_COUNT:
+ default:
+ DBUG_ASSERT(0 <= qtype && qtype < QUERY_TYPE_COUNT);
+ }
+ DBUG_RETURN(0);
+}
+
+bool Discrete_intervals_list::append(ulonglong start, ulonglong val,
+ ulonglong incr)
+{
+ DBUG_ENTER("Discrete_intervals_list::append");
+ /* first, see if this can be merged with previous */
+ if ((head == NULL) || tail->merge_if_contiguous(start, val, incr))
+ {
+ /* it cannot, so need to add a new interval */
+ Discrete_interval *new_interval= new Discrete_interval(start, val, incr);
+ DBUG_RETURN(append(new_interval));
+ }
+ DBUG_RETURN(0);
+}
+
+bool Discrete_intervals_list::append(Discrete_interval *new_interval)
+{
+ DBUG_ENTER("Discrete_intervals_list::append");
+ if (unlikely(new_interval == NULL))
+ DBUG_RETURN(1);
+ DBUG_PRINT("info",("adding new auto_increment interval"));
+ if (head == NULL)
+ head= current= new_interval;
+ else
+ tail->next= new_interval;
+ tail= new_interval;
+ elements++;
+ DBUG_RETURN(0);
+}
+
+#endif /* !defined(MYSQL_CLIENT) */
diff --git a/sql/sql_class.h b/sql/sql_class.h
index cc7ef7809d4..34c9a193f16 100644
--- a/sql/sql_class.h
+++ b/sql/sql_class.h
@@ -1,4 +1,4 @@
-/* Copyright (C) 2000-2006 MySQL AB
+/* Copyright 2000-2008 MySQL AB, 2008 Sun Microsystems, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -20,25 +20,74 @@
#pragma interface /* gcc class implementation */
#endif
-// TODO: create log.h and move all the log header stuff there
+#include "log.h"
+#include "rpl_tblmap.h"
+
+/**
+ 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;
+};
+
+
+class Relay_log_info;
class Query_log_event;
class Load_log_event;
class Slave_log_event;
-class Format_description_log_event;
class sp_rcontext;
class sp_cache;
class Parser_state;
+class Rows_log_event;
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_log_type { LOG_CLOSED, LOG_TO_BE_OPENED, LOG_NORMAL, LOG_NEW, LOG_BIN};
enum enum_delay_key_write { DELAY_KEY_WRITE_NONE, DELAY_KEY_WRITE_ON,
DELAY_KEY_WRITE_ALL };
-
-enum enum_check_fields { CHECK_FIELD_IGNORE, CHECK_FIELD_WARN,
- CHECK_FIELD_ERROR_FOR_NULL };
+enum enum_slave_exec_mode { SLAVE_EXEC_MODE_STRICT,
+ SLAVE_EXEC_MODE_IDEMPOTENT,
+ SLAVE_EXEC_MODE_LAST_BIT};
+enum enum_mark_columns
+{ MARK_COLUMNS_NONE, MARK_COLUMNS_READ, MARK_COLUMNS_WRITE};
extern char internal_table_name[2];
extern char empty_c_string[1];
@@ -51,124 +100,6 @@ extern const char **errmesg;
#define TC_HEURISTIC_RECOVER_ROLLBACK 2
extern uint tc_heuristic_recover;
-/*
- Transaction Coordinator log - a base abstract class
- for two different implementations
-*/
-class TC_LOG
-{
- public:
- int using_heuristic_recover();
- TC_LOG() {}
- virtual ~TC_LOG() {}
-
- virtual int open(const char *opt_name)=0;
- virtual void close()=0;
- virtual int log_xid(THD *thd, my_xid xid)=0;
- virtual void unlog(ulong cookie, my_xid xid)=0;
-};
-
-class TC_LOG_DUMMY: public TC_LOG // use it to disable the logging
-{
-public:
- TC_LOG_DUMMY() {} /* Remove gcc warning */
- int open(const char *opt_name) { return 0; }
- void close() { }
- int log_xid(THD *thd, my_xid xid) { return 1; }
- void unlog(ulong cookie, my_xid xid) { }
-};
-
-#ifdef HAVE_MMAP
-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
- } PAGE_STATE;
-
- private:
- typedef struct st_page {
- struct st_page *next; // page a linked in a fifo queue
- my_xid *start, *end; // usable area of a page
- my_xid *ptr; // next xid will be written here
- 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
- } PAGE;
-
- char logname[FN_REFLEN];
- File fd;
- my_off_t file_length;
- uint npages, inited;
- uchar *data;
- struct st_page *pages, *syncing, *active, *pool, *pool_last;
- /*
- note that, e.g. LOCK_active is only used to protect
- 'active' pointer, to protect the content of the active page
- 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;
-
- public:
- TC_LOG_MMAP(): inited(0) {}
- int open(const char *opt_name);
- void close();
- int log_xid(THD *thd, my_xid xid);
- void unlog(ulong cookie, my_xid xid);
- int recover();
-
- private:
- void get_active_from_pool();
- int sync();
- int overflow();
-};
-#else
-#define TC_LOG_MMAP TC_LOG_DUMMY
-#endif
-
-extern TC_LOG *tc_log;
-extern TC_LOG_MMAP tc_log_mmap;
-extern TC_LOG_DUMMY tc_log_dummy;
-
-/* log info errors */
-#define LOG_INFO_EOF -1
-#define LOG_INFO_IO -2
-#define LOG_INFO_INVALID -3
-#define LOG_INFO_SEEK -4
-#define LOG_INFO_MEM -6
-#define LOG_INFO_FATAL -7
-#define LOG_INFO_IN_USE -8
-
-/* bitmap to SQL_LOG::close() */
-#define LOG_CLOSE_INDEX 1
-#define LOG_CLOSE_TO_BE_OPENED 2
-#define LOG_CLOSE_STOP_EVENT 4
-
-struct st_relay_log_info;
-
-typedef struct st_log_info
-{
- char log_file_name[FN_REFLEN];
- 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),
- pos(0), fatal(0)
- {
- log_file_name[0] = '\0';
- pthread_mutex_init(&lock, MY_MUTEX_INIT_FAST);
- }
- ~st_log_info() { pthread_mutex_destroy(&lock);}
-} LOG_INFO;
-
typedef struct st_user_var_events
{
user_var_entry *user_var_event;
@@ -181,195 +112,6 @@ typedef struct st_user_var_events
#define RP_LOCK_LOG_IS_ALREADY_LOCKED 1
#define RP_FORCE_ROTATE 2
-class Log_event;
-
-/*
- TODO split MYSQL_LOG into base MYSQL_LOG and
- MYSQL_QUERY_LOG, MYSQL_SLOW_LOG, MYSQL_BIN_LOG
- most of the code from MYSQL_LOG should be in the MYSQL_BIN_LOG
- only (TC_LOG included)
-
- TODO use mmap instead of IO_CACHE for binlog
- (mmap+fsync is two times faster than write+fsync)
-*/
-
-class MYSQL_LOG: public TC_LOG
-{
- private:
- /* LOCK_log and LOCK_index are inited by init_pthread_objects() */
- pthread_mutex_t LOCK_log, LOCK_index;
- pthread_mutex_t LOCK_prep_xids;
- pthread_cond_t COND_prep_xids;
- pthread_cond_t update_cond;
- ulonglong bytes_written;
- time_t last_time,query_start;
- IO_CACHE log_file;
- IO_CACHE index_file;
- /*
- purge_temp is a temp file used in purge_logs so that the index file
- can be updated before deleting files from disk, yielding better crash
- recovery. It is created on demand the first time purge_logs is called
- and then reused for subsequent calls. It is cleaned up in cleanup().
- */
- IO_CACHE purge_temp;
- char *name;
- char time_buff[20],db[NAME_LEN+1];
- char log_file_name[FN_REFLEN],index_file_name[FN_REFLEN];
- /*
- The max size before rotation (usable only if log_type == LOG_BIN: binary
- logs and relay logs).
- For a binlog, max_size should be max_binlog_size.
- For a relay log, it should be max_relay_log_size if this is non-zero,
- max_binlog_size otherwise.
- max_size is set in init(), and dynamically changed (when one does SET
- GLOBAL MAX_BINLOG_SIZE|MAX_RELAY_LOG_SIZE) by fix_max_binlog_size and
- fix_max_relay_log_size).
- */
- ulong max_size;
- ulong prepared_xids; /* for tc log - number of xids to remember */
- volatile enum_log_type log_type;
- enum cache_type io_cache_type;
- // current file sequence number for load data infile binary logging
- uint file_id;
- uint open_count; // For replication
- int readers_count;
- bool write_error, inited;
- bool need_start_event;
- /*
- no_auto_events means we don't want any of these automatic events :
- Start/Rotate/Stop. That is, in 4.x when we rotate a relay log, we don't
- want a Rotate_log event to be written to the relay log. When we start a
- relay log etc. So in 4.x this is 1 for relay logs, 0 for binlogs.
- In 5.0 it's 0 for relay logs too!
- */
- bool no_auto_events;
- friend class Log_event;
-
-public:
- /*
- These describe the log's format. This is used only for relay logs.
- _for_exec is used by the SQL thread, _for_queue by the I/O thread. It's
- necessary to have 2 distinct objects, because the I/O thread may be reading
- events in a different format from what the SQL thread is reading (consider
- the case of a master which has been upgraded from 5.0 to 5.1 without doing
- RESET MASTER, or from 4.x to 5.0).
- */
- Format_description_log_event *description_event_for_exec,
- *description_event_for_queue;
-
- MYSQL_LOG();
- /*
- note that there's no destructor ~MYSQL_LOG() !
- The reason is that we don't want it to be automatically called
- on exit() - but only during the correct shutdown process
- */
-
- int open(const char *opt_name);
- void close();
- int log_xid(THD *thd, my_xid xid);
- void unlog(ulong cookie, my_xid xid);
- int recover(IO_CACHE *log, Format_description_log_event *fdle);
- void reset_bytes_written()
- {
- bytes_written = 0;
- }
- void harvest_bytes_written(ulonglong* counter)
- {
-#ifndef DBUG_OFF
- char buf1[22],buf2[22];
-#endif
- DBUG_ENTER("harvest_bytes_written");
- (*counter)+=bytes_written;
- DBUG_PRINT("info",("counter: %s bytes_written: %s", llstr(*counter,buf1),
- llstr(bytes_written,buf2)));
- bytes_written=0;
- DBUG_VOID_RETURN;
- }
- void set_max_size(ulong max_size_arg);
- void signal_update();
- void wait_for_update(THD* thd, bool master_or_slave);
- void set_need_start_event() { need_start_event = 1; }
- void init(enum_log_type log_type_arg,
- enum cache_type io_cache_type_arg,
- bool no_auto_events_arg, ulong max_size);
- void init_pthread_objects();
- void cleanup();
- bool open(const char *log_name,
- enum_log_type log_type,
- const char *new_name,
- enum cache_type io_cache_type_arg,
- bool no_auto_events_arg, ulong max_size,
- bool null_created);
- const char *generate_name(const char *log_name, const char *suffix,
- bool strip_ext, char *buff);
- /* simplified open_xxx wrappers for the gigantic open above */
- 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, 0, 0, 0);
- }
- 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, 0, 0, 0);
- }
- bool open_index_file(const char *index_file_name_arg,
- const char *log_name);
- void new_file(bool need_lock);
- bool write(THD *thd, enum enum_server_command command,
- const char *format, ...) ATTRIBUTE_FORMAT(printf, 4, 5);
- bool write(THD *thd, const char *query, uint query_length,
- time_t query_start=0);
- bool write(Log_event* event_info); // binary log write
- bool write(THD *thd, IO_CACHE *cache, Log_event *commit_event);
-
- void start_union_events(THD *thd, query_id_t query_id_param);
- void stop_union_events(THD *thd);
- bool is_query_in_union(THD *thd, query_id_t query_id_param);
-
- /*
- v stands for vector
- invoked as appendv(buf1,len1,buf2,len2,...,bufn,lenn,0)
- */
- bool appendv(const char* buf,uint len,...);
- bool append(Log_event* ev);
-
- int generate_new_name(char *new_name,const char *old_name);
- 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);
- void rotate_and_purge(uint flags);
- bool flush_and_sync();
- int purge_logs(const char *to_log, bool included,
- bool need_mutex, bool need_update_threads,
- ulonglong *decrease_log_space);
- int purge_logs_before_date(time_t purge_time);
- int purge_first_log(struct st_relay_log_info* rli, bool included);
- bool reset_logs(THD* thd);
- void close(uint exiting);
-
- // iterating through the log index file
- int find_log_pos(LOG_INFO* linfo, const char* log_name,
- bool need_mutex);
- int find_next_log(LOG_INFO* linfo, bool need_mutex);
- int get_current_log(LOG_INFO* linfo);
- int raw_get_current_log(LOG_INFO* linfo);
- uint next_file_id();
- inline bool is_open() { return log_type != LOG_CLOSED; }
- 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 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 IO_CACHE *get_index_file() { return &index_file;}
- inline uint32 get_open_count() { return open_count; }
-};
-
/*
The COPY_INFO structure is used by INSERT/REPLACE code.
The schema of the row counting by the INSERT/INSERT ... ON DUPLICATE KEY
@@ -383,10 +125,10 @@ public:
was actually changed or not.
*/
typedef struct st_copy_info {
- ha_rows records; /* Number of processed records */
- ha_rows deleted; /* Number of deleted records */
- ha_rows updated; /* Number of updated records */
- ha_rows copied; /* Number of copied records */
+ ha_rows records; /**< Number of processed records */
+ ha_rows deleted; /**< Number of deleted records */
+ ha_rows updated; /**< Number of updated records */
+ ha_rows copied; /**< Number of copied records */
ha_rows error_count;
ha_rows touched; /* Number of touched records */
enum enum_duplicates handle_duplicates;
@@ -400,12 +142,23 @@ typedef struct st_copy_info {
} COPY_INFO;
-class key_part_spec :public Sql_alloc {
+class Key_part_spec :public Sql_alloc {
public:
const char *field_name;
uint length;
- key_part_spec(const char *name,uint len=0) :field_name(name), length(len) {}
- bool operator==(const key_part_spec& other) const;
+ Key_part_spec(const char *name,uint len=0) :field_name(name), length(len) {}
+ bool operator==(const Key_part_spec& other) const;
+ /**
+ Construct a copy of this Key_part_spec. field_name is copied
+ by-pointer as it is known to never change. At the same time
+ 'length' may be reset in mysql_prepare_create_table, and this
+ is why we supply it with a copy.
+
+ @return If out of memory, 0 is returned and an error is set in
+ THD.
+ */
+ Key_part_spec *clone(MEM_ROOT *mem_root) const
+ { return new (mem_root) Key_part_spec(*this); }
};
@@ -416,6 +169,12 @@ public:
enum drop_type type;
Alter_drop(enum drop_type par_type,const char *par_name)
:name(par_name), type(par_type) {}
+ /**
+ Used to make a clone of this object for ALTER/CREATE TABLE
+ @sa comment for Key_part_spec::clone
+ */
+ Alter_drop *clone(MEM_ROOT *mem_root) const
+ { return new (mem_root) Alter_drop(*this); }
};
@@ -425,6 +184,12 @@ public:
Item *def;
Alter_column(const char *par_name,Item *literal)
:name(par_name), def(literal) {}
+ /**
+ Used to make a clone of this object for ALTER/CREATE TABLE
+ @sa comment for Key_part_spec::clone
+ */
+ Alter_column *clone(MEM_ROOT *mem_root) const
+ { return new (mem_root) Alter_column(*this); }
};
@@ -432,24 +197,32 @@ class Key :public Sql_alloc {
public:
enum Keytype { PRIMARY, UNIQUE, MULTIPLE, FULLTEXT, SPATIAL, FOREIGN_KEY};
enum Keytype type;
- enum ha_key_alg algorithm;
- List<key_part_spec> columns;
+ KEY_CREATE_INFO key_create_info;
+ List<Key_part_spec> columns;
const char *name;
bool generated;
- Key(enum Keytype type_par, const char *name_arg, enum ha_key_alg alg_par,
- bool generated_arg, List<key_part_spec> &cols)
- :type(type_par), algorithm(alg_par), columns(cols), name(name_arg),
- generated(generated_arg)
+ Key(enum Keytype type_par, const char *name_arg,
+ KEY_CREATE_INFO *key_info_arg,
+ bool generated_arg, List<Key_part_spec> &cols)
+ :type(type_par), key_create_info(*key_info_arg), columns(cols),
+ name(name_arg), generated(generated_arg)
{}
- ~Key() {}
+ Key(const Key &rhs, MEM_ROOT *mem_root);
+ virtual ~Key() {}
/* Equality comparison of keys (ignoring name) */
friend bool foreign_key_prefix(Key *a, Key *b);
+ /**
+ Used to make a clone of this object for ALTER/CREATE TABLE
+ @sa comment for Key_part_spec::clone
+ */
+ virtual Key *clone(MEM_ROOT *mem_root) const
+ { return new (mem_root) Key(*this, mem_root); }
};
class Table_ident;
-class foreign_key: public Key {
+class Foreign_key: public Key {
public:
enum fk_match_opt { FK_MATCH_UNDEF, FK_MATCH_FULL,
FK_MATCH_PARTIAL, FK_MATCH_SIMPLE};
@@ -457,16 +230,23 @@ public:
FK_OPTION_SET_NULL, FK_OPTION_NO_ACTION, FK_OPTION_DEFAULT};
Table_ident *ref_table;
- List<key_part_spec> ref_columns;
+ List<Key_part_spec> ref_columns;
uint delete_opt, update_opt, match_opt;
- foreign_key(const char *name_arg, List<key_part_spec> &cols,
- Table_ident *table, List<key_part_spec> &ref_cols,
+ Foreign_key(const char *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, HA_KEY_ALG_UNDEF, 0, cols),
+ :Key(FOREIGN_KEY, name_arg, &default_key_create_info, 0, cols),
ref_table(table), ref_columns(ref_cols),
delete_opt(delete_opt_arg), update_opt(update_opt_arg),
match_opt(match_opt_arg)
{}
+ Foreign_key(const Foreign_key &rhs, MEM_ROOT *mem_root);
+ /**
+ Used to make a clone of this object for ALTER/CREATE TABLE
+ @sa comment for Key_part_spec::clone
+ */
+ virtual Key *clone(MEM_ROOT *mem_root) const
+ { return new (mem_root) Foreign_key(*this, mem_root); }
};
typedef struct st_mysql_lock
@@ -487,29 +267,9 @@ public:
#include "sql_lex.h" /* Must be here */
-/* Needed to be able to have an I_List of char* strings in mysqld.cc. */
-
-class i_string: public ilink
-{
-public:
- char* ptr;
- i_string():ptr(0) { }
- i_string(char* s) : ptr(s) {}
-};
-
-/* needed for linked list of two strings for replicate-rewrite-db */
-class i_string_pair: public ilink
-{
-public:
- char* key;
- char* val;
- i_string_pair():key(0),val(0) { }
- i_string_pair(char* key_arg, char* val_arg) : key(key_arg),val(val_arg) {}
-};
-
-
class Delayed_insert;
class select_result;
+class Time_zone;
#define THD_SENTRY_MAGIC 0xfeedd1ff
#define THD_SENTRY_GONE 0xdeadbeef
@@ -518,22 +278,37 @@ class select_result;
struct system_variables
{
+ /*
+ How dynamically allocated system variables are handled:
+
+ The global_system_variables and max_system_variables are "authoritative"
+ They both should have the same 'version' and 'size'.
+ When attempting to access a dynamic variable, if the session version
+ is out of date, then the session version is updated and realloced if
+ neccessary and bytes copied from global to make up for missing data.
+ */
+ 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;
ulonglong max_heap_table_size;
ulonglong tmp_table_size;
+ ulonglong long_query_time;
ha_rows select_limit;
ha_rows max_join_size;
ulong auto_increment_increment, auto_increment_offset;
ulong bulk_insert_buff_size;
ulong join_buff_size;
- ulong long_query_time;
ulong max_allowed_packet;
ulong max_error_count;
ulong max_length_for_sort_data;
ulong max_sort_length;
ulong max_tmp_tables;
ulong max_insert_delayed_threads;
+ ulong min_examined_row_limit;
ulong multi_range_count;
ulong myisam_repair_threads;
ulong myisam_sort_buff_size;
@@ -547,12 +322,13 @@ struct system_variables
ulong optimizer_prune_level;
ulong optimizer_search_depth;
ulong preload_buff_size;
+ ulong profiling_history_size;
ulong query_cache_type;
ulong read_buff_size;
ulong read_rnd_buff_size;
ulong div_precincrement;
ulong sortbuff_size;
- ulong table_type;
+ ulong thread_handling;
ulong tx_isolation;
ulong completion_type;
/* Determines which non-standard SQL behaviour should be enabled */
@@ -569,30 +345,37 @@ struct system_variables
ulong trans_prealloc_size;
ulong log_warnings;
ulong group_concat_max_len;
+ 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)
/*
In slave thread we need to know in behalf of which
thread the query is being run to replicate temp tables properly
*/
- ulong pseudo_thread_id;
+ 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;
-
-#ifdef HAVE_INNOBASE_DB
- my_bool innodb_table_locks;
- my_bool innodb_support_xa;
-#endif /* HAVE_INNOBASE_DB */
-#ifdef HAVE_NDBCLUSTER_DB
- ulong ndb_autoincrement_prefetch_sz;
my_bool ndb_force_send;
+ my_bool ndb_use_copying_alter_table;
my_bool ndb_use_exact_count;
my_bool ndb_use_transactions;
-#endif /* HAVE_NDBCLUSTER_DB */
+ my_bool ndb_index_stat_enable;
+
+ my_bool old_alter_table;
my_bool old_passwords;
+ plugin_ref table_plugin;
+
/* Only charset part of these variables is sensible */
CHARSET_INFO *character_set_filesystem;
CHARSET_INFO *character_set_client;
@@ -654,6 +437,7 @@ typedef struct system_status_var
ulong net_big_packet_count;
ulong opened_tables;
+ ulong opened_shares;
ulong select_full_join_count;
ulong select_full_range_join_count;
ulong select_range_count;
@@ -666,6 +450,7 @@ typedef struct system_status_var
ulong filesort_scan_count;
/* Prepared statements and binary protocol */
ulong com_stmt_prepare;
+ ulong com_stmt_reprepare;
ulong com_stmt_execute;
ulong com_stmt_send_long_data;
ulong com_stmt_fetch;
@@ -675,13 +460,13 @@ typedef struct system_status_var
Number of statements sent from the client
*/
ulong questions;
-
/*
IMPORTANT!
SEE last_system_status_var DEFINITION BELOW.
-
Below 'last_system_status_var' are all variables which doesn't make any
sense to add to the /global/ status variable counter.
+ Status variables which it does not make sense to add to
+ global status variable counter
*/
double last_query_cost;
} STATUS_VAR;
@@ -694,18 +479,20 @@ typedef struct system_status_var
#define last_system_status_var questions
+void mark_transaction_to_rollback(THD *thd, bool all);
+
+#ifdef MYSQL_SERVER
void free_tmp_table(THD *thd, TABLE *entry);
/* The following macro is to make init of Query_arena simpler */
#ifndef DBUG_OFF
-#define INIT_ARENA_DBUG_INFO is_backup_arena= 0
+#define INIT_ARENA_DBUG_INFO is_backup_arena= 0; is_reprepared= FALSE;
#else
#define INIT_ARENA_DBUG_INFO
#endif
-
class Query_arena
{
public:
@@ -717,6 +504,7 @@ public:
MEM_ROOT *mem_root; // Pointer to current memroot
#ifndef DBUG_OFF
bool is_backup_arena; /* True if this arena is used for backup. */
+ bool is_reprepared;
#endif
/*
The states relfects three diffrent life cycles for three
@@ -762,24 +550,24 @@ public:
inline bool is_conventional() const
{ return state == CONVENTIONAL_EXECUTION; }
- inline gptr alloc(unsigned int size) { return alloc_root(mem_root,size); }
- inline gptr calloc(unsigned int size)
+ inline void* alloc(size_t size) { return alloc_root(mem_root,size); }
+ inline void* calloc(size_t size)
{
- gptr ptr;
+ void *ptr;
if ((ptr=alloc_root(mem_root,size)))
- bzero((char*) ptr,size);
+ bzero(ptr, size);
return ptr;
}
inline char *strdup(const char *str)
{ return strdup_root(mem_root,str); }
- inline char *strmake(const char *str, uint size)
+ inline char *strmake(const char *str, size_t size)
{ return strmake_root(mem_root,str,size); }
- inline char *memdup(const char *str, uint size)
+ inline void *memdup(const void *str, size_t size)
{ return memdup_root(mem_root,str,size); }
- inline char *memdup_w_gap(const char *str, uint size, uint gap)
+ inline void *memdup_w_gap(const void *str, size_t size, uint gap)
{
- gptr ptr;
- if ((ptr=alloc_root(mem_root,size+gap)))
+ void *ptr;
+ if ((ptr= alloc_root(mem_root,size+gap)))
memcpy(ptr,str,size);
return ptr;
}
@@ -803,7 +591,7 @@ class Server_side_cursor;
- prepared, that is, contain placeholders,
- opened as cursors. We maintain 1 to 1 relationship between
statement and cursor - if user wants to create another cursor for his
- query, we create another statement for it.
+ query, we create another statement for it.
To perform some action with statement we reset THD part to the state of
that statement, do the action, and then save back modified state from THD
to the statement. It will be changed in near future, and Statement will
@@ -822,10 +610,17 @@ public:
ulong id;
/*
- - if set_query_id=1, we set field->query_id for all fields. In that case
- field list can not contain duplicates.
- */
- bool set_query_id;
+ MARK_COLUMNS_NONE: Means mark_used_colums is not set and no indicator to
+ handler of fields used is set
+ MARK_COLUMNS_READ: Means a bit in read set is set to inform handler
+ that the field is to be read. If field list contains
+ duplicates, then thd->dup_field is set to point
+ to the last found duplicate.
+ MARK_COLUMNS_WRITE: Means a bit is set in write set to inform handler
+ that it needs to update this field in write_row
+ and update_row.
+ */
+ enum enum_mark_columns mark_used_columns;
LEX_STRING name; /* name for named prepared statements */
LEX *lex; // parse tree descriptor
@@ -847,7 +642,7 @@ public:
it. We will see the query_length field as either 0, or the right value
for it.
Assuming that the write and read of an n-bit memory field in an n-bit
- computer is atomic, we can avoid races in the above way.
+ computer is atomic, we can avoid races in the above way.
This printing is needed at least in SHOW PROCESSLIST and SHOW INNODB
STATUS.
*/
@@ -855,6 +650,22 @@ public:
uint32 query_length; // current query length
Server_side_cursor *cursor;
+ /**
+ Name of the current (default) database.
+
+ If there is the current (default) database, "db" contains its name. If
+ there is no current (default) database, "db" is NULL and "db_length" is
+ 0. In other words, "db", "db_length" must either be NULL, or contain a
+ valid database name.
+
+ @note this attribute is set and alloced by the slave SQL thread (for
+ the THD of that thread); that thread is (and must remain, for now) the
+ only responsible for freeing this member.
+ */
+
+ char *db;
+ size_t db_length;
+
public:
/* This constructor is called for backup statements */
@@ -894,7 +705,7 @@ public:
Statement *find_by_name(LEX_STRING *name)
{
Statement *stmt;
- stmt= (Statement*)hash_search(&names_hash, (byte*)name->str,
+ stmt= (Statement*)hash_search(&names_hash, (uchar*)name->str,
name->length);
return stmt;
}
@@ -904,7 +715,7 @@ public:
if (last_found_statement == 0 || id != last_found_statement->id)
{
Statement *stmt;
- stmt= (Statement *) hash_search(&st_hash, (byte *) &id, sizeof(id));
+ stmt= (Statement *) hash_search(&st_hash, (uchar *) &id, sizeof(id));
if (stmt && stmt->name.str)
return NULL;
last_found_statement= stmt;
@@ -931,7 +742,8 @@ private:
struct st_savepoint {
struct st_savepoint *prev;
char *name;
- uint length, nht;
+ uint length;
+ Ha_trx_info *ha_list;
};
enum xa_states {XA_NOTR=0, XA_ACTIVE, XA_IDLE, XA_PREPARED, XA_ROLLBACK_ONLY};
@@ -985,6 +797,20 @@ public:
{
return (*priv_host ? priv_host : (char *)"%");
}
+
+ bool set_user(char *user_arg);
+
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+ bool
+ change_security_context(THD *thd,
+ LEX_STRING *definer_user,
+ LEX_STRING *definer_host,
+ LEX_STRING *db,
+ Security_context **backup);
+
+ void
+ restore_security_context(THD *thd, Security_context *backup);
+#endif
};
@@ -1018,6 +844,20 @@ class Open_tables_state
{
public:
/**
+ As part of class THD, this member is set during execution
+ of a prepared statement. When it is set, it is used
+ by the locking subsystem to report a change in table metadata.
+
+ When Open_tables_state part of THD is reset to open
+ a system or INFORMATION_SCHEMA table, the member is cleared
+ to avoid spurious ER_NEED_REPREPARE errors -- system and
+ INFORMATION_SCHEMA tables are not subject to metadata version
+ tracking.
+ @sa check_and_update_table_version()
+ */
+ Reprepare_observer *m_reprepare_observer;
+
+ /**
List of regular tables in use by this thread. Contains temporary and
base tables that were opened with @see open_tables().
*/
@@ -1057,6 +897,14 @@ public:
THD::prelocked_mode for more info.)
*/
MYSQL_LOCK *locked_tables;
+
+ /*
+ CREATE-SELECT keeps an extra lock for the table being
+ created. This field is used to keep the extra lock available for
+ lower level routines, which would otherwise miss that lock.
+ */
+ 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
@@ -1084,11 +932,20 @@ public:
ulong version;
uint current_tablenr;
+ enum enum_flags {
+ BACKUPS_AVAIL = (1U << 0) /* There are backups available */
+ };
+
+ /*
+ 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.
*/
- Open_tables_state() {};
+ Open_tables_state() : state_flags(0U) { }
Open_tables_state(ulong version_arg);
@@ -1100,8 +957,10 @@ public:
void reset_open_tables_state()
{
open_tables= temporary_tables= handler_tables= derived_tables= 0;
- lock= locked_tables= 0;
+ extra_lock= lock= locked_tables= 0;
prelocked_mode= NON_PRELOCKED;
+ state_flags= 0U;
+ m_reprepare_observer= NULL;
}
};
@@ -1120,17 +979,52 @@ class Sub_statement_state
{
public:
ulonglong options;
- ulonglong last_insert_id, next_insert_id, current_insert_id;
+ ulonglong first_successful_insert_id_in_prev_stmt;
+ ulonglong first_successful_insert_id_in_cur_stmt, insert_id_for_cur_row;
+ Discrete_interval auto_inc_interval_for_cur_row;
+ Discrete_intervals_list auto_inc_intervals_forced;
ulonglong limit_found_rows;
ha_rows cuted_fields, sent_row_count, examined_row_count;
ulong client_capabilities;
uint in_sub_stmt;
- bool enable_slow_log, insert_id_used, clear_next_insert_id;
+ bool enable_slow_log;
bool last_insert_id_used;
- my_bool no_send_ok;
SAVEPOINT *savepoints;
};
+
+/* Flags for the THD::system_thread variable */
+enum enum_thread_type
+{
+ NON_SYSTEM_THREAD= 0,
+ SYSTEM_THREAD_DELAYED_INSERT= 1,
+ SYSTEM_THREAD_SLAVE_IO= 2,
+ SYSTEM_THREAD_SLAVE_SQL= 4,
+ SYSTEM_THREAD_NDBCLUSTER_BINLOG= 8,
+ SYSTEM_THREAD_EVENT_SCHEDULER= 16,
+ SYSTEM_THREAD_EVENT_WORKER= 32
+};
+
+inline char const *
+show_system_thread(enum_thread_type thread)
+{
+#define RETURN_NAME_AS_STRING(NAME) case (NAME): return #NAME
+ switch (thread) {
+ static char buf[64];
+ RETURN_NAME_AS_STRING(NON_SYSTEM_THREAD);
+ RETURN_NAME_AS_STRING(SYSTEM_THREAD_DELAYED_INSERT);
+ RETURN_NAME_AS_STRING(SYSTEM_THREAD_SLAVE_IO);
+ RETURN_NAME_AS_STRING(SYSTEM_THREAD_SLAVE_SQL);
+ RETURN_NAME_AS_STRING(SYSTEM_THREAD_NDBCLUSTER_BINLOG);
+ RETURN_NAME_AS_STRING(SYSTEM_THREAD_EVENT_SCHEDULER);
+ RETURN_NAME_AS_STRING(SYSTEM_THREAD_EVENT_WORKER);
+ default:
+ sprintf(buf, "<UNKNOWN SYSTEM THREAD: %d>", thread);
+ return buf;
+ }
+#undef RETURN_NAME_AS_STRING
+}
+
/**
This class represents the interface for internal error handlers.
Internal error handlers are exception handlers used by the server
@@ -1169,12 +1063,157 @@ public:
@return true if the error is handled
*/
virtual bool handle_error(uint sql_errno,
+ const char *message,
MYSQL_ERROR::enum_warning_level level,
THD *thd) = 0;
};
/**
+ 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, 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
+ {
+ DBUG_ASSERT(m_status == DA_OK || m_status == DA_EOF);
+ return m_total_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;
+
+ /**
+ 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,
+ */
+};
+
+
+/**
+ Storage engine specific thread local data.
+*/
+
+struct Ha_data
+{
+ /**
+ Storage engine specific thread local data.
+ Lifetime: one user connection.
+ */
+ void *ha_ptr;
+ /**
+ 0: Life time: one statement within a transaction. If @@autocommit is
+ on, also represents the entire transaction.
+ @sa trans_register_ha()
+
+ 1: Life time: one transaction within a connection.
+ If the storage engine does not participate in a transaction,
+ this should not be used.
+ @sa trans_register_ha()
+ */
+ Ha_trx_info ha_info[2];
+
+ Ha_data() :ha_ptr(NULL) {}
+};
+
+
+/**
@class THD
For each client connection we create a separate thread with THD serving as
a thread/connection descriptor
@@ -1184,6 +1223,9 @@ class THD :public Statement,
public Open_tables_state
{
public:
+ /* Used to execute base64 coded binlog events in MySQL server */
+ Relay_log_info* rli_fake;
+
/*
Constant for THD::where initialization in the beginning of every query.
@@ -1214,8 +1256,8 @@ public:
NET net; // client connection descriptor
MEM_ROOT warn_root; // For warnings and errors
Protocol *protocol; // Current protocol
- Protocol_simple protocol_simple; // Normal protocol
- Protocol_prep protocol_prep; // Binary 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
@@ -1223,6 +1265,7 @@ public:
struct rand_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 *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
@@ -1236,30 +1279,45 @@ public:
*/
char *thread_stack;
- /*
- db - currently selected database
- catalog - currently selected catalog
- WARNING: some members of THD (currently 'db', 'catalog' and 'query') are
- set and alloced by the slave SQL thread (for the THD of that thread); that
- thread is (and must remain, for now) the only responsible for freeing these
- 3 members. If you add members here, and you add code to set them in
- replication, don't forget to free_them_and_set_them_to_0 in replication
- properly. For details see the 'err:' label of the handle_slave_sql()
- in sql/slave.cc.
- */
- char *db, *catalog;
+ /**
+ Currently selected catalog.
+ */
+ char *catalog;
+
+ /**
+ @note
+ Some members of THD (currently 'Statement::db',
+ 'catalog' and 'query') are set and alloced by the slave SQL thread
+ (for the THD of that thread); that thread is (and must remain, for now)
+ the only responsible for freeing these 3 members. If you add members
+ here, and you add code to set them in replication, don't forget to
+ free_them_and_set_them_to_0 in replication properly. For details see
+ the 'err:' label of the handle_slave_sql() in sql/slave.cc.
+
+ @see handle_slave_sql
+ */
+
Security_context main_security_ctx;
Security_context *security_ctx;
- /* remote (peer) port */
- uint16 peer_port;
/*
Points to info-string that we show in SHOW PROCESSLIST
You are supposed to update thd->proc_info only if you have coded
a time-consuming piece that MySQL can get stuck in for a long time.
+
+ Set it using the thd_proc_info(THD *thread, const char *message)
+ macro/function.
*/
const char *proc_info;
+ /*
+ Used in error messages to tell user in what part of MySQL we found an
+ error. E. g. when where= "having clause", if fix_fields() fails, user
+ will know that the error was in having clause.
+ */
+ const char *where;
+
+ double tmp_double_value; /* Used in set_var.cc */
ulong client_capabilities; /* What the client supports */
ulong max_client_packet_length;
@@ -1267,7 +1325,7 @@ public:
/*
One thread can hold up to one named user-level lock. This variable
points to a lock object if the lock is present. See item_func.cc and
- chapter 'Miscellaneous functions', for functions GET_LOCK, RELEASE_LOCK.
+ chapter 'Miscellaneous functions', for functions GET_LOCK, RELEASE_LOCK.
*/
User_level_lock *ull;
#ifndef DBUG_OFF
@@ -1281,14 +1339,12 @@ public:
enum enum_server_command command;
uint32 server_id;
uint32 file_id; // for LOAD DATA INFILE
- /*
- Used in error messages to tell user in what part of MySQL we found an
- error. E. g. when where= "having clause", if fix_fields() fails, user
- will know that the error was in having clause.
- */
- const char *where;
- time_t start_time,time_after_lock,user_time;
- time_t connect_time,thr_create_time; // track down slow pthread_create
+ /* remote (peer) port */
+ uint16 peer_port;
+ time_t start_time, user_time;
+ ulonglong connect_utime, thr_create_utime; // track down slow pthread_create
+ ulonglong start_utime, utime_after_lock;
+
thr_lock_type update_lock_default;
Delayed_insert *di;
@@ -1296,13 +1352,77 @@ public:
uint in_sub_stmt;
/* container for handler's private per-connection data */
- void *ha_data[MAX_HA];
+ Ha_data ha_data[MAX_HA];
+
+#ifndef MYSQL_CLIENT
+ int binlog_setup_trx_data();
+
+ /*
+ Public interface to write RBR events to the binlog
+ */
+ void binlog_start_trans_and_stmt();
+ void binlog_set_stmt_begin();
+ int binlog_write_table_map(TABLE *table, bool is_transactional);
+ int binlog_write_row(TABLE* table, bool is_transactional,
+ MY_BITMAP const* cols, size_t colcnt,
+ const uchar *buf);
+ int binlog_delete_row(TABLE* table, bool is_transactional,
+ MY_BITMAP const* cols, size_t colcnt,
+ const uchar *buf);
+ int binlog_update_row(TABLE* table, bool is_transactional,
+ MY_BITMAP const* cols, size_t colcnt,
+ const uchar *old_data, const uchar *new_data);
+
+ void set_server_id(uint32 sid) { server_id = sid; }
+
+ /*
+ Member functions to handle pending event for row-level logging.
+ */
+ template <class RowsEventT> Rows_log_event*
+ binlog_prepare_pending_rows_event(TABLE* table, uint32 serv_id,
+ MY_BITMAP const* cols,
+ size_t colcnt,
+ 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);
+
+private:
+ /*
+ 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:
+ uint get_binlog_table_maps() const {
+ return binlog_table_maps;
+ }
+#endif /* MYSQL_CLIENT */
+
+public:
+
struct st_transactions {
SAVEPOINT *savepoints;
THD_TRANS all; // Trans since BEGIN WORK
THD_TRANS stmt; // Trans for current statement
bool on; // see ha_enable_transaction()
XID_STATE xid_state;
+ Rows_log_event *m_pending_rows_event;
+
/*
Tables changed in transaction (that must be invalidated in query cache).
List contain only transactional tables, that not invalidated in query
@@ -1329,9 +1449,9 @@ public:
#endif
}
} transaction;
- Field *dupp_field;
+ Field *dup_field;
#ifndef __WIN__
- sigset_t signals,block_signals;
+ sigset_t signals;
#endif
#ifdef SIGNAL_WITH_VIO_CLOSE
Vio* active_vio;
@@ -1358,35 +1478,149 @@ public:
Note: in the parser, stmt_arena == thd, even for PS/SP.
*/
Query_arena *stmt_arena;
+
/*
- next_insert_id is set on SET INSERT_ID= #. This is used as the next
- generated auto_increment value in handler.cc
+ map for tables that will be updated for a multi-table update query
+ statement, for other query statements, this will be zero.
*/
- ulonglong next_insert_id;
- /* Remember last next_insert_id to reset it if something went wrong */
- ulonglong prev_insert_id;
+ table_map table_map_for_update;
+ /* Tells if LAST_INSERT_ID(#) was called for the current statement */
+ bool arg_of_last_insert_id_function;
/*
- At the beginning of the statement last_insert_id holds the first
- generated value of the previous statement. During statement
- execution it is updated to the value just generated, but then
- restored to the value that was generated first, so for the next
- statement it will again be "the first generated value of the
- previous statement".
-
- It may also be set with "LAST_INSERT_ID(expr)" or
- "@@LAST_INSERT_ID= expr", but the effect of such setting will be
- seen only in the next statement.
+ ALL OVER THIS FILE, "insert_id" means "*automatically generated* value for
+ insertion into an auto_increment column".
*/
- ulonglong last_insert_id;
-
/*
- current_insert_id remembers the first generated value of the
- previous statement, and does not change during statement
- execution. Its value returned from LAST_INSERT_ID() and
- @@LAST_INSERT_ID.
+ This is the first autogenerated insert id which was *successfully*
+ inserted by the previous statement (exactly, if the previous statement
+ didn't successfully insert an autogenerated insert id, then it's the one
+ of the statement before, etc).
+ It can also be set by SET LAST_INSERT_ID=# or SELECT LAST_INSERT_ID(#).
+ It is returned by LAST_INSERT_ID().
+ */
+ ulonglong first_successful_insert_id_in_prev_stmt;
+ /*
+ Variant of the above, used for storing in statement-based binlog. The
+ difference is that the one above can change as the execution of a stored
+ function progresses, while the one below is set once and then does not
+ change (which is the value which statement-based binlog needs).
+ */
+ ulonglong first_successful_insert_id_in_prev_stmt_for_binlog;
+ /*
+ This is the first autogenerated insert id which was *successfully*
+ inserted by the current statement. It is maintained only to set
+ first_successful_insert_id_in_prev_stmt when statement ends.
+ */
+ ulonglong first_successful_insert_id_in_cur_stmt;
+ /*
+ We follow this logic:
+ - when stmt starts, first_successful_insert_id_in_prev_stmt contains the
+ first insert id successfully inserted by the previous stmt.
+ - as stmt makes progress, handler::insert_id_for_cur_row changes;
+ every time get_auto_increment() is called,
+ auto_inc_intervals_in_cur_stmt_for_binlog is augmented with the
+ reserved interval (if statement-based binlogging).
+ - at first successful insertion of an autogenerated value,
+ first_successful_insert_id_in_cur_stmt is set to
+ handler::insert_id_for_cur_row.
+ - when stmt goes to binlog,
+ auto_inc_intervals_in_cur_stmt_for_binlog is binlogged if
+ non-empty.
+ - when stmt ends, first_successful_insert_id_in_prev_stmt is set to
+ first_successful_insert_id_in_cur_stmt.
+ */
+ /*
+ stmt_depends_on_first_successful_insert_id_in_prev_stmt is set when
+ LAST_INSERT_ID() is used by a statement.
+ If it is set, first_successful_insert_id_in_prev_stmt_for_binlog will be
+ stored in the statement-based binlog.
+ This variable is CUMULATIVE along the execution of a stored function or
+ trigger: if one substatement sets it to 1 it will stay 1 until the
+ function/trigger ends, thus making sure that
+ first_successful_insert_id_in_prev_stmt_for_binlog does not change anymore
+ and is propagated to the caller for binlogging.
+ */
+ bool stmt_depends_on_first_successful_insert_id_in_prev_stmt;
+ /*
+ List of auto_increment intervals reserved by the thread so far, for
+ storage in the statement-based binlog.
+ Note that its minimum is not first_successful_insert_id_in_cur_stmt:
+ assuming a table with an autoinc column, and this happens:
+ INSERT INTO ... VALUES(3);
+ SET INSERT_ID=3; INSERT IGNORE ... VALUES (NULL);
+ then the latter INSERT will insert no rows
+ (first_successful_insert_id_in_cur_stmt == 0), but storing "INSERT_ID=3"
+ in the binlog is still needed; the list's minimum will contain 3.
+ This variable is cumulative: if several statements are written to binlog
+ as one (stored functions or triggers are used) this list is the
+ concatenation of all intervals reserved by all statements.
+ */
+ Discrete_intervals_list auto_inc_intervals_in_cur_stmt_for_binlog;
+ /* Used by replication and SET INSERT_ID */
+ Discrete_intervals_list auto_inc_intervals_forced;
+ /*
+ There is BUG#19630 where statement-based replication of stored
+ functions/triggers with two auto_increment columns breaks.
+ We however ensure that it works when there is 0 or 1 auto_increment
+ column; our rules are
+ a) on master, while executing a top statement involving substatements,
+ first top- or sub- statement to generate auto_increment values wins the
+ exclusive right to see its values be written to binlog (the write
+ will be done by the statement or its caller), and the losers won't see
+ their values be written to binlog.
+ b) on slave, while replicating a top statement involving substatements,
+ first top- or sub- statement to need to read auto_increment values from
+ the master's binlog wins the exclusive right to read them (so the losers
+ won't read their values from binlog but instead generate on their own).
+ a) implies that we mustn't backup/restore
+ auto_inc_intervals_in_cur_stmt_for_binlog.
+ b) implies that we mustn't backup/restore auto_inc_intervals_forced.
+
+ If there are more than 1 auto_increment columns, then intervals for
+ different columns may mix into the
+ auto_inc_intervals_in_cur_stmt_for_binlog list, which is logically wrong,
+ but there is no point in preventing this mixing by preventing intervals
+ from the secondly inserted column to come into the list, as such
+ prevention would be wrong too.
+ What will happen in the case of
+ INSERT INTO t1 (auto_inc) VALUES(NULL);
+ where t1 has a trigger which inserts into an auto_inc column of t2, is
+ that in binlog we'll store the interval of t1 and the interval of t2 (when
+ we store intervals, soon), then in slave, t1 will use both intervals, t2
+ will use none; if t1 inserts the same number of rows as on master,
+ normally the 2nd interval will not be used by t1, which is fine. t2's
+ values will be wrong if t2's internal auto_increment counter is different
+ from what it was on master (which is likely). In 5.1, in mixed binlogging
+ mode, row-based binlogging is used for such cases where two
+ auto_increment columns are inserted.
+ */
+ inline void record_first_successful_insert_id_in_cur_stmt(ulonglong id_arg)
+ {
+ if (first_successful_insert_id_in_cur_stmt == 0)
+ first_successful_insert_id_in_cur_stmt= id_arg;
+ }
+ inline ulonglong read_first_successful_insert_id_in_prev_stmt(void)
+ {
+ if (!stmt_depends_on_first_successful_insert_id_in_prev_stmt)
+ {
+ /* It's the first time we read it */
+ first_successful_insert_id_in_prev_stmt_for_binlog=
+ first_successful_insert_id_in_prev_stmt;
+ stmt_depends_on_first_successful_insert_id_in_prev_stmt= 1;
+ }
+ return first_successful_insert_id_in_prev_stmt;
+ }
+ /*
+ Used by Intvar_log_event::do_apply_event() and by "SET INSERT_ID=#"
+ (mysqlbinlog). We'll soon add a variant which can take many intervals in
+ argument.
*/
- ulonglong current_insert_id;
+ inline void force_one_auto_inc_interval(ulonglong next_id)
+ {
+ auto_inc_intervals_forced.empty(); // in case of multiple SET INSERT_ID
+ auto_inc_intervals_forced.append(next_id, ULONGLONG_MAX, 0);
+ }
ulonglong limit_found_rows;
ulonglong options; /* Bitmap of states */
@@ -1422,6 +1656,11 @@ public:
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)
+ PROFILING profiling;
+#endif
+
/*
Id of current query. Statement can be reused to execute several queries
query_id is global in context of the whole MySQL server.
@@ -1431,8 +1670,11 @@ public:
update auto-updatable fields (like auto_increment and timestamp).
*/
query_id_t query_id, warn_id;
- ulong thread_id, col_access;
+ 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;
@@ -1441,11 +1683,11 @@ public:
create_sort_index(); may differ from examined_row_count.
*/
ulong row_count;
- long dbug_thread_id;
- pthread_t real_id;
+ pthread_t real_id; /* For debugging */
+ my_thread_id thread_id;
uint tmp_table, global_read_lock;
- uint server_status,open_options,system_thread;
- uint db_length;
+ 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;
@@ -1468,6 +1710,8 @@ public:
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 locked, some_tables_deleted;
bool last_cuted_field;
bool no_errors, password;
@@ -1498,40 +1742,17 @@ public:
*/
bool is_fatal_sub_stmt_error;
bool query_start_used, rand_used, time_zone_used;
-
- /*
- last_insert_id_used is set when current statement calls
- LAST_INSERT_ID() or reads @@LAST_INSERT_ID.
- */
- bool last_insert_id_used;
-
- /*
- last_insert_id_used is set when current statement or any stored
- function called from this statement calls LAST_INSERT_ID() or
- reads @@LAST_INSERT_ID, so that binary log LAST_INSERT_ID_EVENT be
- generated. Required for statement-based binary log for issuing
- "SET LAST_INSERT_ID= #" before "SELECT func()", if func() reads
- LAST_INSERT_ID.
- */
- bool last_insert_id_used_bin_log;
-
- /*
- insert_id_used is set when current statement updates
- THD::last_insert_id, so that binary log INSERT_ID_EVENT be
- generated.
- */
- bool insert_id_used;
-
- /*
- clear_next_insert_id is set if engine was called at least once
- for this statement to generate auto_increment value.
- */
- bool clear_next_insert_id;
/* for IS NULL => = last_insert_id() fix in remove_eq_conds() */
bool substitute_null_with_insert_id;
bool in_lock_tables;
- bool query_error, bootstrap, cleanup_done;
- bool tmp_table_used;
+ /**
+ True if a slave error. Causes the slave to stop. Not the same
+ as the statement execution error (is_error()), since
+ a statement may be expected to return an error, e.g. because
+ it returned an error on master, and this is OK on the slave.
+ */
+ bool is_slave_error;
+ bool bootstrap, cleanup_done;
/** is set if some thread specific value(s) used in a statement. */
bool thread_specific_used;
@@ -1559,14 +1780,15 @@ public:
/* Used by the sys_var class to store temporary values */
union
{
- my_bool my_bool_value;
- long long_value;
- ulong ulong_value;
+ my_bool my_bool_value;
+ long long_value;
+ ulong ulong_value;
+ ulonglong ulonglong_value;
} sys_var_tmp;
-
+
struct {
- /*
- If true, mysql_bin_log::write(Log_event) call will not write events to
+ /*
+ If true, mysql_bin_log::write(Log_event) call will not write events to
binlog, and maintain 2 below variables instead (use
mysql_bin_log.start_union_events to turn this on)
*/
@@ -1577,13 +1799,13 @@ public:
*/
bool unioned_events;
/*
- If TRUE, at least one mysql_bin_log::write(Log_event e), where
- e.cache_stmt == TRUE call has been made after last
+ If TRUE, at least one mysql_bin_log::write(Log_event e), where
+ e.cache_stmt == TRUE call has been made after last
mysql_bin_log.start_union_events() call.
*/
bool unioned_events_trans;
-
- /*
+
+ /*
'queries' (actually SP statements) that run under inside this binlog
union have thd->query_id >= first_query_id.
*/
@@ -1597,6 +1819,10 @@ public:
*/
Parser_state *m_parser_state;
+#ifdef WITH_PARTITION_STORAGE_ENGINE
+ partition_info *work_part_info;
+#endif
+
THD();
~THD();
@@ -1608,7 +1834,7 @@ public:
killing mysqld) where it's vital to not allocate excessive and not used
memory. Note, that we still don't return error from init_for_queries():
if preallocation fails, we should notice that at the first call to
- alloc_root.
+ alloc_root.
*/
void init_for_queries();
void change_user(void);
@@ -1631,6 +1857,33 @@ public:
void close_active_vio();
#endif
void awake(THD::killed_state state_to_set);
+
+#ifndef MYSQL_CLIENT
+ enum enum_binlog_query_type {
+ /*
+ The query can be logged row-based or statement-based
+ */
+ ROW_QUERY_TYPE,
+
+ /*
+ The query has to be logged statement-based
+ */
+ 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,
+ THD::killed_state killed_err_arg= THD::KILLED_NO_VALUE);
+#endif
+
/*
For enter_cond() / exit_cond() to work the mutex must be got before
enter_cond(); this mutex is then released by exit_cond().
@@ -1661,33 +1914,25 @@ public:
proc_info = old_msg;
pthread_mutex_unlock(&mysys_var->mutex);
}
-
- static inline void safe_time(time_t *t)
+ inline time_t query_start() { query_start_used=1; return start_time; }
+ inline void set_time()
{
- /**
- Wrapper around time() which retries on error (-1)
-
- @details
- This is needed because, despite the documentation, time() may fail
- in some circumstances. Here we retry time() until it succeeds, and
- log the failure so that performance problems related to this can be
- identified.
- */
- while(unlikely(time(t) == ((time_t) -1)))
- sql_print_information("time() failed with %d", errno);
+ if (user_time)
+ {
+ start_time= user_time;
+ start_utime= utime_after_lock= my_micro_time();
+ }
+ else
+ start_utime= utime_after_lock= my_micro_time_and_time(&start_time);
}
-
- inline time_t query_start() { query_start_used=1; return start_time; }
- inline void set_time() { if (user_time) start_time=time_after_lock=user_time; else { safe_time(&start_time); time_after_lock= start_time; }}
- inline void end_time() { safe_time(&start_time); }
- inline void set_time(time_t t) { time_after_lock=start_time=user_time=t; }
- inline void lock_time() { safe_time(&time_after_lock); }
- inline void insert_id(ulonglong id_arg)
+ inline void set_current_time() { start_time= my_time(MY_WME); }
+ inline void set_time(time_t t)
{
- last_insert_id= id_arg;
- insert_id_used=1;
- substitute_null_with_insert_id= TRUE;
+ start_time= user_time= t;
+ start_utime= utime_after_lock= my_micro_time();
}
+ void set_time_after_lock() { utime_after_lock= my_micro_time(); }
+ ulonglong current_utime() { return my_micro_time(); }
inline ulonglong found_rows(void)
{
return limit_found_rows;
@@ -1704,11 +1949,19 @@ public:
{
return !stmt_arena->is_stmt_prepare() && !lex->only_view_structure();
}
- inline gptr trans_alloc(unsigned int size)
+ inline bool fill_information_schema_tables()
+ {
+ return !stmt_arena->is_stmt_prepare();
+ }
+ inline void* trans_alloc(unsigned int size)
{
return alloc_root(&transaction.mem_root,size);
}
+ LEX_STRING *make_lex_string(LEX_STRING *lex_str,
+ const char* str, uint length,
+ bool allocate_lex_string);
+
bool convert_string(LEX_STRING *to, CHARSET_INFO *to_cs,
const char *from, uint from_length,
CHARSET_INFO *from_cs);
@@ -1720,24 +1973,50 @@ public:
CHANGED_TABLE_LIST * changed_table_dup(const char *key, long key_length);
int send_explain_fields(select_result *result);
#ifndef EMBEDDED_LIBRARY
+ /**
+ 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.
+ */
inline void clear_error()
{
- net.last_error[0]= 0;
- net.last_errno= 0;
- net.report_error= 0;
- query_error= 0;
+ DBUG_ENTER("clear_error");
+ if (main_da.is_error())
+ main_da.reset_diagnostics_area();
+ is_slave_error= 0;
+ DBUG_VOID_RETURN;
}
inline bool vio_ok() const { return net.vio != 0; }
#else
void clear_error();
inline bool vio_ok() const { return true; }
#endif
+ /**
+ Mark the current error as fatal. Warning: this does not
+ set any error, it sets a property of the error, so must be
+ followed or prefixed with my_error().
+ */
inline void fatal_error()
{
is_fatal_error= 1;
- net.report_error= 1;
DBUG_PRINT("error",("Fatal error set"));
}
+ /**
+ TRUE if there is an error in the error stack.
+
+ Please use this method instead of direct access to
+ net.report_error.
+
+ If TRUE, the current (sub)-statement should be aborted.
+ The main difference between this member and is_fatal_error
+ is that a fatal error can not be handled by a stored
+ procedure continue handler, whereas a normal error can.
+
+ To raise this flag, use my_error().
+ */
+ inline bool is_error() const { return main_da.is_error(); }
inline CHARSET_INFO *charset() { return variables.character_set_client; }
void update_charset();
@@ -1799,13 +2078,82 @@ public:
void set_n_backup_active_arena(Query_arena *set, Query_arena *backup);
void restore_active_arena(Query_arena *set, Query_arena *backup);
- /*
- Initialize the current database from a NULL-terminated string with length
- If we run out of memory, we free the current database and return TRUE.
- This way the user will notice the error as there will be no current
- database selected (in addition to the error message set by malloc).
+ inline void set_current_stmt_binlog_row_based_if_mixed()
+ {
+ /*
+ 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
+ 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;
+ }
+ inline void set_current_stmt_binlog_row_based()
+ {
+ current_stmt_binlog_row_based= TRUE;
+ }
+ inline void clear_current_stmt_binlog_row_based()
+ {
+ current_stmt_binlog_row_based= FALSE;
+ }
+ inline void reset_current_stmt_binlog_row_based()
+ {
+ /*
+ If there are temporary tables, don't reset back to
+ statement-based. Indeed it could be that:
+ CREATE TEMPORARY TABLE t SELECT UUID(); # row-based
+ # and row-based does not store updates to temp tables
+ # in the binlog.
+ INSERT INTO u SELECT * FROM t; # stmt-based
+ and then the INSERT will fail as data inserted into t was not logged.
+ So we continue with row-based until the temp table is dropped.
+ If we are in a stored function or trigger, we mustn't reset in the
+ middle of its execution (as the binary logging way of a stored function
+ 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))
+ {
+ current_stmt_binlog_row_based=
+ test(variables.binlog_format == BINLOG_FORMAT_ROW);
+ }
+ }
+
+ /**
+ Set the current database; use deep copy of C-string.
+
+ @param new_db a pointer to the new database name.
+ @param new_db_len length of the new database name.
+
+ Initialize the current database from a NULL-terminated string with
+ length. If we run out of memory, we free the current database and
+ return TRUE. This way the user will notice the error as there will be
+ no current database selected (in addition to the error message set by
+ malloc).
+
+ @note This operation just sets {db, db_length}. Switching the current
+ database usually involves other actions, like switching other database
+ attributes including security context. In the future, this operation
+ will be made private and more convenient interface will be provided.
+
+ @return Operation status
+ @retval FALSE Success
+ @retval TRUE Out-of-memory error
*/
- bool set_db(const char *new_db, uint new_db_len)
+ bool set_db(const char *new_db, size_t new_db_len)
{
/* Do not reallocate memory if current chunk is big enough. */
if (db && new_db && db_length >= new_db_len)
@@ -1813,13 +2161,24 @@ public:
else
{
x_free(db);
- db= new_db ? my_strdup_with_length(new_db, new_db_len, MYF(MY_WME)) :
- NULL;
+ db= new_db ? my_strndup(new_db, new_db_len, MYF(MY_WME)) : NULL;
}
db_length= db ? new_db_len : 0;
return new_db && !db;
}
- void reset_db(char *new_db, uint new_db_len)
+
+ /**
+ Set the current database; use shallow copy of C-string.
+
+ @param new_db a pointer to the new database name.
+ @param new_db_len length of the new database name.
+
+ @note This operation just sets {db, db_length}. Switching the current
+ database usually involves other actions, like switching other database
+ attributes including security context. In the future, this operation
+ will be made private and more convenient interface will be provided.
+ */
+ void reset_db(char *new_db, size_t new_db_len)
{
db= new_db;
db_length= new_db_len;
@@ -1829,7 +2188,7 @@ public:
allocate memory for a deep copy: current database may be freed after
a statement is parsed but before it's executed.
*/
- bool copy_db_to(char **p_db, uint *p_db_length)
+ bool copy_db_to(char **p_db, size_t *p_db_length)
{
if (db == NULL)
{
@@ -1837,10 +2196,10 @@ public:
return TRUE;
}
*p_db= strmake(db, db_length);
- if (p_db_length)
- *p_db_length= db_length;
+ *p_db_length= db_length;
return FALSE;
}
+ thd_scheduler scheduler;
public:
/**
@@ -1855,7 +2214,7 @@ public:
@param level the error level
@return true if the error is handled
*/
- virtual bool handle_error(uint sql_errno,
+ virtual bool handle_error(uint sql_errno, const char *message,
MYSQL_ERROR::enum_warning_level level);
/**
@@ -1885,16 +2244,30 @@ private:
};
+/** A short cut for thd->main_da.set_ok_status(). */
+
+inline void
+my_ok(THD *thd, ha_rows affected_rows= 0, ulonglong id= 0,
+ const char *message= NULL)
+{
+ thd->main_da.set_ok_status(thd, affected_rows, id, message);
+}
+
+
+/** A short cut for thd->main_da.set_eof_status(). */
+
+inline void
+my_eof(THD *thd)
+{
+ thd->main_da.set_eof_status(thd);
+}
+
#define tmp_disable_binlog(A) \
{ulonglong tmp_disable_binlog__save_options= (A)->options; \
(A)->options&= ~OPTION_BIN_LOG
#define reenable_binlog(A) (A)->options= tmp_disable_binlog__save_options;}
-/* Flags for the THD::system_thread (bitmap) variable */
-#define SYSTEM_THREAD_DELAYED_INSERT 1
-#define SYSTEM_THREAD_SLAVE_IO 2
-#define SYSTEM_THREAD_SLAVE_SQL 4
/*
Used to hold information about file and file structure in exchange
@@ -1994,14 +2367,20 @@ public:
class select_send :public select_result {
- int status;
+ /**
+ True if we have sent result set metadata to the client.
+ In this case the client always expects us to end the result
+ set with an eof or error packet
+ */
+ bool is_result_set_started;
public:
- select_send() :status(0) {}
+ select_send() :is_result_set_started(FALSE) {}
bool send_fields(List<Item> &list, uint flags);
bool send_data(List<Item> &items);
bool send_eof();
virtual bool check_simple_select() const { return FALSE; }
void abort();
+ virtual void cleanup();
};
@@ -2056,6 +2435,7 @@ class select_export :public select_to_file {
bool is_unsafe_field_sep;
bool fixed_row_size;
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.
@@ -2072,6 +2452,7 @@ 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.
@@ -2091,8 +2472,7 @@ class select_insert :public select_result_interceptor {
TABLE_LIST *table_list;
TABLE *table;
List<Item> *fields;
- ulonglong autoinc_value_of_last_inserted_row; // not autogenerated
- ulonglong autoinc_value_of_first_inserted_row; // autogenerated
+ ulonglong autoinc_value_of_last_inserted_row; // autogenerated or not
COPY_INFO info;
bool insert_into_view;
select_insert(TABLE_LIST *table_list_par,
@@ -2104,6 +2484,7 @@ class select_insert :public select_result_interceptor {
virtual int prepare2(void);
bool send_data(List<Item> &items);
virtual void store_values(List<Item> &values);
+ virtual bool can_rollback_data() { return 0; }
void send_error(uint errcode,const char *err);
bool send_eof();
void abort();
@@ -2116,33 +2497,45 @@ class select_create: public select_insert {
ORDER *group;
TABLE_LIST *create_table;
HA_CREATE_INFO *create_info;
+ TABLE_LIST *select_tables;
Alter_info *alter_info;
- MYSQL_LOCK *lock;
Field **field;
+ /* lock data for tmp table */
+ MYSQL_LOCK *m_lock;
+ /* m_lock or thd->extra_lock */
+ MYSQL_LOCK **m_plock;
public:
- select_create(TABLE_LIST *table_arg,
- HA_CREATE_INFO *create_info_arg,
- Alter_info *alter_info_arg,
- List<Item> &select_fields,
- enum_duplicates duplic, bool ignore)
- :select_insert(NULL, NULL, &select_fields, 0, 0, duplic, ignore),
+ select_create (TABLE_LIST *table_arg,
+ HA_CREATE_INFO *create_info_par,
+ Alter_info *alter_info_arg,
+ List<Item> &select_fields,enum_duplicates duplic, bool ignore,
+ TABLE_LIST *select_tables_arg)
+ :select_insert (NULL, NULL, &select_fields, 0, 0, duplic, ignore),
create_table(table_arg),
- create_info(create_info_arg),
+ create_info(create_info_par),
+ select_tables(select_tables_arg),
alter_info(alter_info_arg),
- lock(0)
- {}
+ m_plock(NULL)
+ {}
int prepare(List<Item> &list, SELECT_LEX_UNIT *u);
+
+ void 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 bool can_rollback_data() { return 1; }
+
+ // Needed for access from local class MY_HOOKS in prepare(), since thd is proteted.
+ const THD *get_thd(void) { return thd; }
+ const HA_CREATE_INFO *get_create_info() { return create_info; };
int prepare2(void) { return 0; }
};
#include <myisam.h>
-/*
- Param to create temporary tables when doing SELECT:s
+/*
+ Param to create temporary tables when doing SELECT:s
NOTE
This structure is copied using memcpy as a part of JOIN.
*/
@@ -2159,7 +2552,7 @@ public:
List<Item> save_copy_funcs;
Copy_field *copy_field, *copy_field_end;
Copy_field *save_copy_field, *save_copy_field_end;
- byte *group_buff;
+ uchar *group_buff;
Item **items_to_copy; /* Fields in tmp table */
MI_COLUMNDEF *recinfo,*start_recinfo;
KEY *keyinfo;
@@ -2170,8 +2563,8 @@ public:
uint quick_group;
bool using_indirect_summary_function;
/* If >0 convert all blob fields to varchar(convert_blob_length) */
- uint convert_blob_length;
- CHARSET_INFO *table_charset;
+ uint convert_blob_length;
+ CHARSET_INFO *table_charset;
bool schema_table;
/*
True if GROUP BY and its aggregate functions are already computed
@@ -2305,7 +2698,7 @@ public:
else
db= db_arg;
}
- inline Table_ident(LEX_STRING table_arg)
+ inline Table_ident(LEX_STRING table_arg)
:table(table_arg), sel((SELECT_LEX_UNIT *)0)
{
db.str=0;
@@ -2351,7 +2744,7 @@ class user_var_entry
};
/*
- Unique -- class for unique (removing of duplicates).
+ Unique -- class for unique (removing of duplicates).
Puts all values to the TREE. If the tree becomes too big,
it's dumped to the file. User can request sorted values, or
just iterate through them. In the last case tree merging is performed in
@@ -2365,7 +2758,7 @@ class Unique :public Sql_alloc
ulonglong max_in_memory_size;
IO_CACHE file;
TREE tree;
- byte *record_pointers;
+ uchar *record_pointers;
bool flush();
uint size;
@@ -2385,9 +2778,9 @@ public:
}
bool get(TABLE *table);
- static double get_use_cost(uint *buffer, uint nkeys, uint key_size,
+ static double get_use_cost(uint *buffer, uint nkeys, uint key_size,
ulonglong max_in_memory_size);
- inline static int get_cost_calc_buff_size(ulong nkeys, uint key_size,
+ inline static int get_cost_calc_buff_size(ulong nkeys, uint key_size,
ulonglong max_in_memory_size)
{
register ulonglong max_elems_in_tree=
@@ -2398,8 +2791,8 @@ public:
void reset();
bool walk(tree_walk_action action, void *walk_action_arg);
- friend int unique_write_to_file(gptr key, element_count count, Unique *unique);
- friend int unique_write_to_ptrs(gptr key, element_count count, Unique *unique);
+ friend int unique_write_to_file(uchar* key, element_count count, Unique *unique);
+ friend int unique_write_to_ptrs(uchar* key, element_count count, Unique *unique);
};
@@ -2431,6 +2824,7 @@ public:
void send_error(uint errcode,const char *err);
int do_deletes();
bool send_eof();
+ virtual void abort();
};
@@ -2471,8 +2865,9 @@ public:
bool send_data(List<Item> &items);
bool initialize_tables (JOIN *join);
void send_error(uint errcode,const char *err);
- int do_updates (bool from_send_error);
+ int do_updates();
bool send_eof();
+ virtual void abort();
};
class my_var : public Sql_alloc {
@@ -2498,6 +2893,7 @@ class select_dumpvar :public select_result_interceptor {
ha_rows row_count;
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.
@@ -2516,8 +2912,34 @@ public:
void cleanup();
};
+/* 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
+/**
+ Must be set for SQL statements that may contain
+ Item expressions and/or use joins and tables.
+ Indicates that the parse tree of such statement may
+ contain rule-based optimizations that depend on metadata
+ (i.e. number of columns in a table), and consequently
+ that the statement must be re-prepared whenever
+ referenced metadata changes. Must not be set for
+ statements that themselves change metadata, e.g. RENAME,
+ ALTER and other DDL, since otherwise will trigger constant
+ reprepare. Consequently, complex item expressions and
+ joins are currently prohibited in these statements.
+*/
+#define CF_REEXECUTION_FRAGILE 32
+
/* Functions in sql_class.cc */
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);
+#endif /* MYSQL_SERVER */
diff --git a/sql/sql_connect.cc b/sql/sql_connect.cc
new file mode 100644
index 00000000000..e4d7cf6feb5
--- /dev/null
+++ b/sql/sql_connect.cc
@@ -0,0 +1,1134 @@
+/* Copyright (C) 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+
+/*
+ Functions to autenticate and handle reqests for a connection
+*/
+
+#include "mysql_priv.h"
+
+#ifdef HAVE_OPENSSL
+/*
+ Without SSL the handshake consists of one packet. This packet
+ has both client capabilites 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.
+ 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 */
+
+#ifdef __WIN__
+extern void win_install_sigabrt_handler();
+#endif
+
+/*
+ Get structure for logging connection data for the current user
+*/
+
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+static HASH hash_user_connections;
+
+static int get_or_create_user_conn(THD *thd, const char *user,
+ const char *host,
+ USER_RESOURCES *mqh)
+{
+ int return_val= 0;
+ size_t temp_len, user_len;
+ char temp_user[USER_HOST_BUFF_SIZE];
+ struct user_conn *uc;
+
+ DBUG_ASSERT(user != 0);
+ DBUG_ASSERT(host != 0);
+
+ 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,
+ (uchar*) temp_user, temp_len)))
+ {
+ /* First connection for user; Create a user connection object */
+ if (!(uc= ((struct user_conn*)
+ my_malloc(sizeof(struct user_conn) + temp_len+1,
+ MYF(MY_WME)))))
+ {
+ /* MY_WME ensures an error is set in THD. */
+ return_val= 1;
+ goto end;
+ }
+ uc->user=(char*) (uc+1);
+ memcpy(uc->user,temp_user,temp_len+1);
+ uc->host= uc->user + user_len + 1;
+ uc->len= temp_len;
+ uc->connections= uc->questions= uc->updates= uc->conn_per_hour= 0;
+ uc->user_resources= *mqh;
+ uc->reset_utime= thd->thr_create_utime;
+ 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);
+ return_val= 1;
+ goto end;
+ }
+ }
+ thd->user_connect=uc;
+ uc->connections++;
+end:
+ (void) pthread_mutex_unlock(&LOCK_user_conn);
+ return return_val;
+
+}
+
+
+/*
+ check if user has already too many connections
+
+ SYNOPSIS
+ check_for_max_user_connections()
+ thd Thread handle
+ uc User connect object
+
+ NOTES
+ If check fails, we decrease user connection count, which means one
+ shouldn't call decrease_user_connections() after this function.
+
+ RETURN
+ 0 ok
+ 1 error
+*/
+
+static
+int check_for_max_user_connections(THD *thd, USER_CONN *uc)
+{
+ int error=0;
+ DBUG_ENTER("check_for_max_user_connections");
+
+ (void) pthread_mutex_lock(&LOCK_user_conn);
+ if (max_user_connections && !uc->user_resources.user_conn &&
+ max_user_connections < (uint) uc->connections)
+ {
+ my_error(ER_TOO_MANY_USER_CONNECTIONS, MYF(0), uc->user);
+ error=1;
+ goto end;
+ }
+ time_out_user_resource_limits(thd, uc);
+ if (uc->user_resources.user_conn &&
+ uc->user_resources.user_conn < uc->connections)
+ {
+ my_error(ER_USER_LIMIT_REACHED, MYF(0), uc->user,
+ "max_user_connections",
+ (long) uc->user_resources.user_conn);
+ error= 1;
+ goto end;
+ }
+ if (uc->user_resources.conn_per_hour &&
+ uc->user_resources.conn_per_hour <= uc->conn_per_hour)
+ {
+ my_error(ER_USER_LIMIT_REACHED, MYF(0), uc->user,
+ "max_connections_per_hour",
+ (long) uc->user_resources.conn_per_hour);
+ error=1;
+ goto end;
+ }
+ uc->conn_per_hour++;
+
+end:
+ if (error)
+ uc->connections--; // no need for decrease_user_connections() here
+ (void) pthread_mutex_unlock(&LOCK_user_conn);
+ DBUG_RETURN(error);
+}
+
+
+/*
+ Decrease user connection count
+
+ SYNOPSIS
+ decrease_user_connections()
+ uc User connection object
+
+ NOTES
+ If there is a n user connection object for a connection
+ (which only happens if 'max_user_connections' is defined or
+ if someone has created a resource grant for a user), then
+ the connection count is always incremented on connect.
+
+ The user connect object is not freed if some users has
+ 'max connections per hour' defined as we need to be able to hold
+ count over the lifetime of the connection.
+*/
+
+void decrease_user_connections(USER_CONN *uc)
+{
+ DBUG_ENTER("decrease_user_connections");
+ (void) pthread_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) pthread_mutex_unlock(&LOCK_user_conn);
+ DBUG_VOID_RETURN;
+}
+
+
+/*
+ Reset per-hour user resource limits when it has been more than
+ an hour since they were last checked
+
+ SYNOPSIS:
+ time_out_user_resource_limits()
+ thd Thread handler
+ uc User connection details
+
+ NOTE:
+ This assumes that the LOCK_user_conn mutex has been acquired, so it is
+ safe to test and modify members of the USER_CONN structure.
+*/
+
+void time_out_user_resource_limits(THD *thd, USER_CONN *uc)
+{
+ ulonglong check_time= thd->start_utime;
+ DBUG_ENTER("time_out_user_resource_limits");
+
+ /* If more than a hour since last check, reset resource checking */
+ if (check_time - uc->reset_utime >= LL(3600000000))
+ {
+ uc->questions=1;
+ uc->updates=0;
+ uc->conn_per_hour=0;
+ uc->reset_utime= check_time;
+ }
+
+ DBUG_VOID_RETURN;
+}
+
+/*
+ Check if maximum queries per hour limit has been reached
+ returns 0 if OK.
+*/
+
+bool check_mqh(THD *thd, uint check_command)
+{
+ bool error= 0;
+ USER_CONN *uc=thd->user_connect;
+ DBUG_ENTER("check_mqh");
+ DBUG_ASSERT(uc != 0);
+
+ (void) pthread_mutex_lock(&LOCK_user_conn);
+
+ time_out_user_resource_limits(thd, uc);
+
+ /* Check that we have not done too many questions / hour */
+ if (uc->user_resources.questions &&
+ uc->questions++ >= uc->user_resources.questions)
+ {
+ my_error(ER_USER_LIMIT_REACHED, MYF(0), uc->user, "max_questions",
+ (long) uc->user_resources.questions);
+ error=1;
+ goto end;
+ }
+ if (check_command < (uint) SQLCOM_END)
+ {
+ /* Check that we have not done too many updates / hour */
+ if (uc->user_resources.updates &&
+ (sql_command_flags[check_command] & CF_CHANGES_DATA) &&
+ uc->updates++ >= uc->user_resources.updates)
+ {
+ my_error(ER_USER_LIMIT_REACHED, MYF(0), uc->user, "max_updates",
+ (long) uc->user_resources.updates);
+ error=1;
+ goto end;
+ }
+ }
+end:
+ (void) pthread_mutex_unlock(&LOCK_user_conn);
+ DBUG_RETURN(error);
+}
+
+#endif /* NO_EMBEDDED_ACCESS_CHECKS */
+
+
+/**
+ Check if user exist and password supplied is correct.
+
+ @param thd thread handle, thd->security_ctx->{host,user,ip} are used
+ @param command originator of the check: now check_user is called
+ during connect and change user procedures; used for
+ logging.
+ @param passwd scrambled password received from client
+ @param passwd_len length of scrambled password
+ @param db database name to connect to, may be NULL
+ @param check_count TRUE if establishing a new connection. In this case
+ check that we have not exceeded the global
+ max_connections limist
+
+ @note Host, user and passwd may point to communication buffer.
+ Current implementation does not depend on that, but future changes
+ should be done with this in mind; 'thd' is INOUT, all other params
+ are 'IN'.
+
+ @retval 0 OK; thd->security_ctx->user/master_access/priv_user/db_access and
+ thd->db are updated; OK is sent to the client.
+ @retval 1 error, e.g. access denied or handshake error, not sent to
+ the client. A message is pushed into the error stack.
+*/
+
+int
+check_user(THD *thd, enum enum_server_command command,
+ const char *passwd, uint passwd_len, const char *db,
+ bool check_count)
+{
+ DBUG_ENTER("check_user");
+ LEX_STRING db_str= { (char *) db, db ? strlen(db) : 0 };
+
+ /*
+ 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
+ pointer if connect failed. Also in case of 'CHANGE USER' failure,
+ current database will be switched to 'no database selected'.
+ */
+ thd->reset_db(NULL, 0);
+
+#ifdef NO_EMBEDDED_ACCESS_CHECKS
+ thd->main_security_ctx.master_access= GLOBAL_ACLS; // Full rights
+ /* Change database if necessary */
+ if (db && db[0])
+ {
+ if (mysql_change_db(thd, &db_str, FALSE))
+ DBUG_RETURN(1);
+ }
+ my_ok(thd);
+ DBUG_RETURN(0);
+#else
+
+ my_bool opt_secure_auth_local;
+ pthread_mutex_lock(&LOCK_global_system_variables);
+ opt_secure_auth_local= opt_secure_auth;
+ pthread_mutex_unlock(&LOCK_global_system_variables);
+
+ /*
+ If the server is running in secure auth mode, short scrambles are
+ forbidden.
+ */
+ if (opt_secure_auth_local && passwd_len == SCRAMBLE_LENGTH_323)
+ {
+ my_error(ER_NOT_SUPPORTED_AUTH_MODE, MYF(0));
+ general_log_print(thd, COM_CONNECT, ER(ER_NOT_SUPPORTED_AUTH_MODE));
+ DBUG_RETURN(1);
+ }
+ if (passwd_len != 0 &&
+ passwd_len != SCRAMBLE_LENGTH &&
+ passwd_len != SCRAMBLE_LENGTH_323)
+ {
+ my_error(ER_HANDSHAKE_ERROR, MYF(0), thd->main_security_ctx.host_or_ip);
+ DBUG_RETURN(1);
+ }
+
+ USER_RESOURCES ur;
+ int res= acl_getroot(thd, &ur, passwd, passwd_len);
+#ifndef EMBEDDED_LIBRARY
+ if (res == -1)
+ {
+ /*
+ This happens when client (new) sends password scrambled with
+ scramble(), but database holds old value (scrambled with
+ scramble_323()). Here we please client to send scrambled_password
+ in old format.
+ */
+ NET *net= &thd->net;
+ if (opt_secure_auth_local)
+ {
+ my_error(ER_SERVER_IS_IN_SECURE_AUTH_MODE, MYF(0),
+ thd->main_security_ctx.user,
+ thd->main_security_ctx.host_or_ip);
+ general_log_print(thd, COM_CONNECT, ER(ER_SERVER_IS_IN_SECURE_AUTH_MODE),
+ thd->main_security_ctx.user,
+ thd->main_security_ctx.host_or_ip);
+ DBUG_RETURN(1);
+ }
+ /* We have to read very specific packet size */
+ if (send_old_password_request(thd) ||
+ my_net_read(net) != SCRAMBLE_LENGTH_323 + 1)
+ {
+ inc_host_errors(&thd->remote.sin_addr);
+ my_error(ER_HANDSHAKE_ERROR, MYF(0), thd->main_security_ctx.host_or_ip);
+ DBUG_RETURN(1);
+ }
+ /* Final attempt to check the user based on reply */
+ /* So as passwd is short, errcode is always >= 0 */
+ res= acl_getroot(thd, &ur, (char *) net->read_pos, SCRAMBLE_LENGTH_323);
+ }
+#endif /*EMBEDDED_LIBRARY*/
+ /* here res is always >= 0 */
+ if (res == 0)
+ {
+ if (!(thd->main_security_ctx.master_access &
+ NO_ACCESS)) // authentication is OK
+ {
+ DBUG_PRINT("info",
+ ("Capabilities: %lu packet_length: %ld Host: '%s' "
+ "Login user: '%s' Priv_user: '%s' Using password: %s "
+ "Access: %lu db: '%s'",
+ thd->client_capabilities,
+ thd->max_client_packet_length,
+ thd->main_security_ctx.host_or_ip,
+ thd->main_security_ctx.user,
+ thd->main_security_ctx.priv_user,
+ passwd_len ? "yes": "no",
+ thd->main_security_ctx.master_access,
+ (thd->db ? thd->db : "*none*")));
+
+ if (check_count)
+ {
+ pthread_mutex_lock(&LOCK_connection_count);
+ bool count_ok= connection_count <= max_connections ||
+ (thd->main_security_ctx.master_access & SUPER_ACL);
+ VOID(pthread_mutex_unlock(&LOCK_connection_count));
+
+ if (!count_ok)
+ { // too many connections
+ my_error(ER_CON_COUNT_ERROR, MYF(0));
+ DBUG_RETURN(1);
+ }
+ }
+
+ /*
+ Log the command before authentication checks, so that the user can
+ check the log for the tried login tried and also to detect
+ break-in attempts.
+ */
+ general_log_print(thd, command,
+ (thd->main_security_ctx.priv_user ==
+ thd->main_security_ctx.user ?
+ (char*) "%s@%s on %s" :
+ (char*) "%s@%s as anonymous on %s"),
+ thd->main_security_ctx.user,
+ thd->main_security_ctx.host_or_ip,
+ db ? db : (char*) "");
+
+ /*
+ This is the default access rights for the current database. It's
+ set to 0 here because we don't have an active database yet (and we
+ may not have an active database to set.
+ */
+ thd->main_security_ctx.db_access=0;
+
+ /* Don't allow user to connect if he has done too many queries */
+ if ((ur.questions || ur.updates || ur.conn_per_hour || ur.user_conn ||
+ max_user_connections) &&
+ get_or_create_user_conn(thd,
+ (opt_old_style_user_limits ? thd->main_security_ctx.user :
+ thd->main_security_ctx.priv_user),
+ (opt_old_style_user_limits ? thd->main_security_ctx.host_or_ip :
+ thd->main_security_ctx.priv_host),
+ &ur))
+ {
+ /* The error is set by get_or_create_user_conn(). */
+ DBUG_RETURN(1);
+ }
+ if (thd->user_connect &&
+ (thd->user_connect->user_resources.conn_per_hour ||
+ thd->user_connect->user_resources.user_conn ||
+ max_user_connections) &&
+ check_for_max_user_connections(thd, thd->user_connect))
+ {
+ /* The error is set in check_for_max_user_connections(). */
+ DBUG_RETURN(1);
+ }
+
+ /* Change database if necessary */
+ if (db && db[0])
+ {
+ if (mysql_change_db(thd, &db_str, FALSE))
+ {
+ /* mysql_change_db() has pushed the error message. */
+ if (thd->user_connect)
+ decrease_user_connections(thd->user_connect);
+ DBUG_RETURN(1);
+ }
+ }
+ my_ok(thd);
+ thd->password= test(passwd_len); // remember for error messages
+ /* Ready to handle queries */
+ DBUG_RETURN(0);
+ }
+ }
+ else if (res == 2) // client gave short hash, server has long hash
+ {
+ my_error(ER_NOT_SUPPORTED_AUTH_MODE, MYF(0));
+ general_log_print(thd, COM_CONNECT, ER(ER_NOT_SUPPORTED_AUTH_MODE));
+ DBUG_RETURN(1);
+ }
+ my_error(ER_ACCESS_DENIED_ERROR, MYF(0),
+ thd->main_security_ctx.user,
+ thd->main_security_ctx.host_or_ip,
+ passwd_len ? ER(ER_YES) : ER(ER_NO));
+ general_log_print(thd, COM_CONNECT, ER(ER_ACCESS_DENIED_ERROR),
+ thd->main_security_ctx.user,
+ thd->main_security_ctx.host_or_ip,
+ passwd_len ? ER(ER_YES) : ER(ER_NO));
+ DBUG_RETURN(1);
+#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.
+*/
+
+extern "C" uchar *get_key_conn(user_conn *buff, size_t *length,
+ my_bool not_used __attribute__((unused)))
+{
+ *length= buff->len;
+ return (uchar*) buff->user;
+}
+
+
+extern "C" void free_user(struct user_conn *uc)
+{
+ my_free((char*) uc,MYF(0));
+}
+
+
+void init_max_user_conn(void)
+{
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+ (void) hash_init(&hash_user_connections,system_charset_info,max_connections,
+ 0,0,
+ (hash_get_key) get_key_conn, (hash_free_key) free_user,
+ 0);
+#endif
+}
+
+
+void free_max_user_conn(void)
+{
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+ hash_free(&hash_user_connections);
+#endif /* NO_EMBEDDED_ACCESS_CHECKS */
+}
+
+
+void reset_mqh(LEX_USER *lu, bool get_them= 0)
+{
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+ (void) pthread_mutex_lock(&LOCK_user_conn);
+ if (lu) // for GRANT
+ {
+ USER_CONN *uc;
+ uint temp_len=lu->user.length+lu->host.length+2;
+ char temp_user[USER_HOST_BUFF_SIZE];
+
+ memcpy(temp_user,lu->user.str,lu->user.length);
+ 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)))
+ {
+ uc->questions=0;
+ get_mqh(temp_user,&temp_user[lu->user.length+1],uc);
+ uc->updates=0;
+ uc->conn_per_hour=0;
+ }
+ }
+ else
+ {
+ /* 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);
+ if (get_them)
+ get_mqh(uc->user,uc->host,uc);
+ uc->questions=0;
+ uc->updates=0;
+ uc->conn_per_hour=0;
+ }
+ }
+ (void) pthread_mutex_unlock(&LOCK_user_conn);
+#endif /* NO_EMBEDDED_ACCESS_CHECKS */
+}
+
+
+void thd_init_client_charset(THD *thd, uint cs_number)
+{
+ /*
+ Use server character set and collation if
+ - opt_character_set_client_handshake is not set
+ - client has not specified a character set
+ - client character set is the same as the servers
+ - client character set doesn't exists in server
+ */
+ if (!opt_character_set_client_handshake ||
+ !(thd->variables.character_set_client= get_charset(cs_number, MYF(0))) ||
+ !my_strcasecmp(&my_charset_latin1,
+ global_system_variables.character_set_client->name,
+ thd->variables.character_set_client->name))
+ {
+ thd->variables.character_set_client=
+ global_system_variables.character_set_client;
+ thd->variables.collation_connection=
+ global_system_variables.collation_connection;
+ thd->variables.character_set_results=
+ global_system_variables.character_set_results;
+ }
+ else
+ {
+ thd->variables.character_set_results=
+ thd->variables.collation_connection=
+ thd->variables.character_set_client;
+ }
+}
+
+
+/*
+ Initialize connection threads
+*/
+
+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;
+}
+
+/*
+ Perform handshake, authorize client and update thd ACL variables.
+
+ SYNOPSIS
+ check_connection()
+ thd thread handle
+
+ RETURN
+ 0 success, OK is sent to user, thd is updated.
+ -1 error, which is sent to user
+ > 0 error code (not sent to user)
+*/
+
+#ifndef EMBEDDED_LIBRARY
+static int check_connection(THD *thd)
+{
+ uint connect_errors= 0;
+ NET *net= &thd->net;
+ ulong pkt_len= 0;
+ char *end;
+
+ 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];
+
+ if (vio_peer_addr(net->vio, ip, &thd->peer_port))
+ {
+ my_error(ER_BAD_HOST_ERROR, MYF(0), thd->main_security_ctx.host_or_ip);
+ return 1;
+ }
+ 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);
+ /* Cut very long hostnames to avoid possible overflows */
+ if (thd->main_security_ctx.host)
+ {
+ if (thd->main_security_ctx.host != my_localhost)
+ thd->main_security_ctx.host[min(strlen(thd->main_security_ctx.host),
+ HOSTNAME_LENGTH)]= 0;
+ thd->main_security_ctx.host_or_ip= thd->main_security_ctx.host;
+ }
+ if (connect_errors > max_connect_errors)
+ {
+ my_error(ER_HOST_IS_BLOCKED, MYF(0), thd->main_security_ctx.host_or_ip);
+ return 1;
+ }
+ }
+ DBUG_PRINT("info",("Host: %s ip: %s",
+ (thd->main_security_ctx.host ?
+ thd->main_security_ctx.host : "unknown host"),
+ (thd->main_security_ctx.ip ?
+ thd->main_security_ctx.ip : "unknown ip")));
+ if (acl_check_host(thd->main_security_ctx.host, thd->main_security_ctx.ip))
+ {
+ my_error(ER_HOST_NOT_PRIVILEGED, MYF(0),
+ thd->main_security_ctx.host_or_ip);
+ return 1;
+ }
+ }
+ else /* Hostname given means that the connection was on a socket */
+ {
+ DBUG_PRINT("info",("Host: %s", thd->main_security_ctx.host));
+ 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));
+ }
+ vio_keepalive(net->vio, TRUE);
+
+ ulong server_capabilites;
+ {
+ /* buff[] needs to big enough to hold the server_version variable */
+ char buff[SERVER_VERSION_LENGTH + SCRAMBLE_LENGTH + 64];
+ server_capabilites= CLIENT_BASIC_FLAGS;
+
+ if (opt_using_transactions)
+ server_capabilites|= CLIENT_TRANSACTIONS;
+#ifdef HAVE_COMPRESS
+ server_capabilites|= CLIENT_COMPRESS;
+#endif /* HAVE_COMPRESS */
+#ifdef HAVE_OPENSSL
+ if (ssl_acceptor_fd)
+ {
+ server_capabilites |= CLIENT_SSL; /* Wow, SSL is available! */
+ server_capabilites |= CLIENT_SSL_VERIFY_SERVER_CERT;
+ }
+#endif /* HAVE_OPENSSL */
+
+ end= strnmov(buff, server_version, SERVER_VERSION_LENGTH) + 1;
+ int4store((uchar*) end, thd->thread_id);
+ end+= 4;
+ /*
+ So as check_connection is the only entry point to authorization
+ procedure, scramble is set here. This gives us new scramble for
+ each handshake.
+ */
+ create_random_string(thd->scramble, SCRAMBLE_LENGTH, &thd->rand);
+ /*
+ Old clients does not understand long scrambles, but can ignore packet
+ tail: that's why first part of the scramble is placed here, and second
+ part at the end of packet.
+ */
+ end= strmake(end, thd->scramble, SCRAMBLE_LENGTH_323) + 1;
+
+ int2store(end, server_capabilites);
+ /* write server characteristics: up to 16 bytes allowed */
+ end[2]=(char) default_charset_info->number;
+ int2store(end+3, thd->server_status);
+ bzero(end+5, 13);
+ end+= 18;
+ /* write scramble tail */
+ end= strmake(end, thd->scramble + SCRAMBLE_LENGTH_323,
+ SCRAMBLE_LENGTH - SCRAMBLE_LENGTH_323) + 1;
+
+ /* At this point we write connection message and read reply */
+ if (net_write_command(net, (uchar) protocol_version, (uchar*) "", 0,
+ (uchar*) buff, (size_t) (end-buff)) ||
+ (pkt_len= my_net_read(net)) == packet_error ||
+ pkt_len < MIN_HANDSHAKE_SIZE)
+ {
+ inc_host_errors(&thd->remote.sin_addr);
+ my_error(ER_HANDSHAKE_ERROR, MYF(0),
+ thd->main_security_ctx.host_or_ip);
+ return 1;
+ }
+ }
+#ifdef _CUSTOMCONFIG_
+#include "_cust_sql_parse.h"
+#endif
+ if (connect_errors)
+ reset_host_errors(&thd->remote.sin_addr);
+ if (thd->packet.alloc(thd->variables.net_buffer_length))
+ return 1; /* The error is set by alloc(). */
+
+ thd->client_capabilities= uint2korr(net->read_pos);
+ if (thd->client_capabilities & CLIENT_PROTOCOL_41)
+ {
+ thd->client_capabilities|= ((ulong) uint2korr(net->read_pos+2)) << 16;
+ thd->max_client_packet_length= uint4korr(net->read_pos+4);
+ DBUG_PRINT("info", ("client_character_set: %d", (uint) net->read_pos[8]));
+ thd_init_client_charset(thd, (uint) net->read_pos[8]);
+ thd->update_charset();
+ end= (char*) net->read_pos+32;
+ }
+ else
+ {
+ thd->max_client_packet_length= uint3korr(net->read_pos+2);
+ end= (char*) net->read_pos+5;
+ }
+ /*
+ Disable those bits which are not supported by the server.
+ This is a precautionary measure, if the client lies. See Bug#27944.
+ */
+ thd->client_capabilities&= server_capabilites;
+
+ if (thd->client_capabilities & CLIENT_IGNORE_SPACE)
+ thd->variables.sql_mode|= MODE_IGNORE_SPACE;
+#ifdef HAVE_OPENSSL
+ DBUG_PRINT("info", ("client capabilities: %lu", thd->client_capabilities));
+ if (thd->client_capabilities & CLIENT_SSL)
+ {
+ /* Do the SSL layering. */
+ if (!ssl_acceptor_fd)
+ {
+ inc_host_errors(&thd->remote.sin_addr);
+ my_error(ER_HANDSHAKE_ERROR, MYF(0), thd->main_security_ctx.host_or_ip);
+ return 1;
+ }
+ DBUG_PRINT("info", ("IO layer change in progress..."));
+ if (sslaccept(ssl_acceptor_fd, net->vio, net->read_timeout))
+ {
+ DBUG_PRINT("error", ("Failed to accept new SSL connection"));
+ inc_host_errors(&thd->remote.sin_addr);
+ my_error(ER_HANDSHAKE_ERROR, MYF(0), thd->main_security_ctx.host_or_ip);
+ return 1;
+ }
+ DBUG_PRINT("info", ("Reading user information over SSL layer"));
+ if ((pkt_len= my_net_read(net)) == packet_error ||
+ pkt_len < NORMAL_HANDSHAKE_SIZE)
+ {
+ DBUG_PRINT("error", ("Failed to read user information (pkt_len= %lu)",
+ pkt_len));
+ inc_host_errors(&thd->remote.sin_addr);
+ my_error(ER_HANDSHAKE_ERROR, MYF(0), thd->main_security_ctx.host_or_ip);
+ return 1;
+ }
+ }
+#endif /* HAVE_OPENSSL */
+
+ if (end >= (char*) net->read_pos+ pkt_len +2)
+ {
+ inc_host_errors(&thd->remote.sin_addr);
+ my_error(ER_HANDSHAKE_ERROR, MYF(0), thd->main_security_ctx.host_or_ip);
+ return 1;
+ }
+
+ if (thd->client_capabilities & CLIENT_INTERACTIVE)
+ thd->variables.net_wait_timeout= thd->variables.net_interactive_timeout;
+ if ((thd->client_capabilities & CLIENT_TRANSACTIONS) &&
+ opt_using_transactions)
+ net->return_status= &thd->server_status;
+
+ char *user= end;
+ char *passwd= strend(user)+1;
+ uint user_len= passwd - user - 1;
+ char *db= passwd;
+ char db_buff[NAME_LEN + 1]; // buffer to store db in utf8
+ char user_buff[USERNAME_LENGTH + 1]; // buffer to store user in utf8
+ uint dummy_errors;
+
+ /*
+ Old clients send null-terminated string as password; new clients send
+ the size (1 byte) + string (not null-terminated). Hence in case of empty
+ password both send '\0'.
+
+ This strlen() can't be easily deleted without changing protocol.
+
+ Cast *passwd to an unsigned char, so that it doesn't extend the sign for
+ *passwd > 127 and become 2**32-127+ after casting to uint.
+ */
+ uint passwd_len= thd->client_capabilities & CLIENT_SECURE_CONNECTION ?
+ (uchar)(*passwd++) : strlen(passwd);
+ db= thd->client_capabilities & CLIENT_CONNECT_WITH_DB ?
+ db + passwd_len + 1 : 0;
+ /* strlen() can't be easily deleted without changing protocol */
+ uint db_len= db ? strlen(db) : 0;
+
+ if (passwd + passwd_len + db_len > (char *)net->read_pos + pkt_len)
+ {
+ inc_host_errors(&thd->remote.sin_addr);
+ my_error(ER_HANDSHAKE_ERROR, MYF(0), thd->main_security_ctx.host_or_ip);
+ return 1;
+ }
+
+ /* Since 4.1 all database names are stored in utf8 */
+ if (db)
+ {
+ db_buff[copy_and_convert(db_buff, sizeof(db_buff)-1,
+ system_charset_info,
+ db, db_len,
+ thd->charset(), &dummy_errors)]= 0;
+ db= db_buff;
+ }
+
+ user_buff[user_len= copy_and_convert(user_buff, sizeof(user_buff)-1,
+ system_charset_info, user, user_len,
+ thd->charset(), &dummy_errors)]= '\0';
+ user= user_buff;
+
+ /* If username starts and ends in "'", chop them off */
+ if (user_len > 1 && user[0] == '\'' && user[user_len - 1] == '\'')
+ {
+ user[user_len-1]= 0;
+ user++;
+ user_len-= 2;
+ }
+
+ if (thd->main_security_ctx.user)
+ x_free(thd->main_security_ctx.user);
+ if (!(thd->main_security_ctx.user= my_strdup(user, MYF(MY_WME))))
+ return 1; /* The error is set by my_strdup(). */
+ return check_user(thd, COM_CONNECT, passwd, passwd_len, db, TRUE);
+}
+
+
+/*
+ Setup thread to be used with the current thread
+
+ SYNOPSIS
+ bool setup_connection_thread_globals()
+ thd Thread/connection handler
+
+ RETURN
+ 0 ok
+ 1 Error (out of memory)
+ In this case we will close the connection and increment status
+*/
+
+bool setup_connection_thread_globals(THD *thd)
+{
+ if (thd->store_globals())
+ {
+ close_connection(thd, ER_OUT_OF_RESOURCES, 1);
+ statistic_increment(aborted_connects,&LOCK_status);
+ thread_scheduler.end_thread(thd, 0);
+ return 1; // Error
+ }
+ return 0;
+}
+
+
+/*
+ Autenticate user, with error reporting
+
+ SYNOPSIS
+ login_connection()
+ thd Thread handler
+
+ NOTES
+ Connection is not closed in case of errors
+
+ RETURN
+ 0 ok
+ 1 error
+*/
+
+
+static bool login_connection(THD *thd)
+{
+ NET *net= &thd->net;
+ int error;
+ DBUG_ENTER("login_connection");
+ DBUG_PRINT("info", ("login_connection called by thread %lu",
+ thd->thread_id));
+
+ /* Use "connect_timeout" value during connection phase */
+ my_net_set_read_timeout(net, connect_timeout);
+ my_net_set_write_timeout(net, connect_timeout);
+
+ error= check_connection(thd);
+ net_end_statement(thd);
+
+ if (error)
+ { // Wrong permissions
+#ifdef __NT__
+ if (vio_type(net->vio) == VIO_TYPE_NAMEDPIPE)
+ my_sleep(1000); /* must wait after eof() */
+#endif
+ statistic_increment(aborted_connects,&LOCK_status);
+ DBUG_RETURN(1);
+ }
+ /* Connect completed, set read/write timeouts back to default */
+ my_net_set_read_timeout(net, thd->variables.net_read_timeout);
+ my_net_set_write_timeout(net, thd->variables.net_write_timeout);
+ DBUG_RETURN(0);
+}
+
+
+/*
+ Close an established connection
+
+ NOTES
+ This mainly updates status variables
+*/
+
+static void end_connection(THD *thd)
+{
+ NET *net= &thd->net;
+ plugin_thdvar_cleanup(thd);
+ if (thd->user_connect)
+ decrease_user_connections(thd->user_connect);
+
+ if (thd->killed || net->error && net->vio != 0)
+ {
+ statistic_increment(aborted_threads,&LOCK_status);
+ }
+
+ 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)));
+ }
+ }
+}
+
+
+/*
+ Initialize THD to handle queries
+*/
+
+static 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
+
+ /*
+ Much of this is duplicated in create_embedded_thd() for the
+ 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))
+ {
+ execute_init_command(thd, &sys_init_connect, &LOCK_sys_init_connect);
+ if (thd->is_error())
+ {
+ thd->killed= THD::KILL_CONNECTION;
+ 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->proc_info=0;
+ thd->set_time();
+ thd->init_for_queries();
+ }
+}
+
+
+/*
+ Thread handler for a connection
+
+ SYNOPSIS
+ handle_one_connection()
+ arg Connection object (THD)
+
+ IMPLEMENTATION
+ This function (normally) does the following:
+ - Initialize thread
+ - Initialize THD to be used with this thread
+ - Authenticate user
+ - Execute all queries sent on the connection
+ - Take connection down
+ - End thread / Handle next connection using thread from thread cache
+*/
+
+pthread_handler_t handle_one_connection(void *arg)
+{
+ THD *thd= (THD*) arg;
+ ulong launch_time= (ulong) ((thd->thr_create_utime= my_micro_time()) -
+ thd->connect_utime);
+
+ if (thread_scheduler.init_new_connection_thread())
+ {
+ close_connection(thd, ER_OUT_OF_RESOURCES, 1);
+ statistic_increment(aborted_connects,&LOCK_status);
+ thread_scheduler.end_thread(thd,0);
+ return 0;
+ }
+ if (launch_time >= slow_launch_time*1000000L)
+ statistic_increment(slow_launch_threads,&LOCK_status);
+
+ /*
+ handle_one_connection() is normally the only way a thread would
+ start and would always be on the very high end of the stack ,
+ therefore, the thread stack always starts at the address of the
+ first local variable of handle_one_connection, which is thd. We
+ need to know the start of the stack so that we could check for
+ stack overruns.
+ */
+ thd->thread_stack= (char*) &thd;
+ if (setup_connection_thread_globals(thd))
+ return 0;
+
+ for (;;)
+ {
+ NET *net= &thd->net;
+
+ lex_start(thd);
+ if (login_connection(thd))
+ goto end_thread;
+
+ prepare_new_connection_state(thd);
+
+ while (!net->error && net->vio != 0 &&
+ !(thd->killed == THD::KILL_CONNECTION))
+ {
+ if (do_command(thd))
+ break;
+ }
+ end_connection(thd);
+
+end_thread:
+ close_connection(thd, 0, 1);
+ if (thread_scheduler.end_thread(thd,1))
+ return 0; // Probably no-threads
+
+ /*
+ If end_thread() returns, we are either running with
+ thread-handler=no-threads or this thread has been schedule to
+ handle the next connection.
+ */
+ thd= current_thd;
+ thd->thread_stack= (char*) &thd;
+ }
+}
+#endif /* EMBEDDED_LIBRARY */
diff --git a/sql/sql_crypt.cc b/sql/sql_crypt.cc
index 38dfc869b3a..aa21d429d90 100644
--- a/sql/sql_crypt.cc
+++ b/sql/sql_crypt.cc
@@ -51,7 +51,7 @@ void SQL_CRYPT::crypt_init(ulong *rand_nr)
decode_buff[+i]=a;
}
for (i=0 ; i <= 255 ; i++)
- encode_buff[(unsigned char) decode_buff[i]]=i;
+ encode_buff[(uchar) decode_buff[i]]=i;
org_rand=rand;
shift=0;
}
@@ -74,7 +74,7 @@ void SQL_CRYPT::decode(char *str,uint length)
for (uint i=0; i < length; i++)
{
shift^=(uint) (my_rnd(&rand)*255.0);
- uint idx= (uint) ((unsigned char) str[0] ^ shift);
+ uint idx= (uint) ((uchar) str[0] ^ shift);
*str = decode_buff[idx];
shift^= (uint) (uchar) *str++;
}
diff --git a/sql/sql_cursor.cc b/sql/sql_cursor.cc
index 83c60814cf3..6f61dc40f66 100644
--- a/sql/sql_cursor.cc
+++ b/sql/sql_cursor.cc
@@ -24,9 +24,9 @@
Declarations.
****************************************************************************/
-/*
+/**
Sensitive_cursor -- a sensitive non-materialized server side
- cursor An instance of this class cursor has its own runtime
+ 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.
@@ -44,7 +44,7 @@ class Sensitive_cursor: public Server_side_cursor
query_id_t query_id;
struct Engine_info
{
- const handlerton *ht;
+ handlerton *ht;
void *read_view;
};
Engine_info ht_info[MAX_HA];
@@ -69,7 +69,7 @@ public:
};
-/*
+/**
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
@@ -98,7 +98,7 @@ public:
};
-/*
+/**
Select_materialize -- a mediator between a cursor query and the
protocol. In case we were not able to open a non-materialzed
cursor, it creates an internal temporary HEAP table, and insert
@@ -109,33 +109,32 @@ public:
class Select_materialize: public select_union
{
- select_result *result; /* the result object of the caller (PS or SP) */
+ select_result *result; /**< the result object of the caller (PS or SP) */
public:
Materialized_cursor *materialized_cursor;
- Select_materialize(select_result *result_arg) :result(result_arg),
- materialized_cursor(0) {}
+ Select_materialize(select_result *result_arg)
+ :result(result_arg), materialized_cursor(0) {}
virtual bool send_fields(List<Item> &list, uint flags);
};
/**************************************************************************/
-/*
+/**
Attempt to open a materialized or non-materialized cursor.
- SYNOPSIS
- mysql_open_cursor()
- thd thread handle
- flags [in] create a materialized cursor or not
- result [in] result class of the caller used as a destination
- for the rows fetched from the cursor
- pcursor [out] a pointer to store a pointer to cursor in
-
- RETURN VALUE
- 0 the query has been successfully executed; in this
- case pcursor may or may not contain
- a pointer to an open cursor.
- non-zero an error, 'pcursor' has been left intact.
+ @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
+
+ @retval
+ 0 the query has been successfully executed; in this
+ case pcursor may or may not contain
+ a pointer to an open cursor.
+ @retval
+ non-zero an error, 'pcursor' has been left intact.
*/
int mysql_open_cursor(THD *thd, uint flags, select_result *result,
@@ -279,6 +278,14 @@ Sensitive_cursor::Sensitive_cursor(THD *thd, select_result *result_arg)
}
+/**
+ 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)
{
@@ -315,14 +322,15 @@ Sensitive_cursor::post_open(THD *thd)
close_at_commit= FALSE; /* reset in case we're reusing the cursor */
info= &ht_info[0];
- for (handlerton **pht= thd->transaction.stmt.ht; *pht; pht++)
+ for (Ha_trx_info *ha_trx_info= thd->transaction.stmt.ha_list;
+ ha_trx_info; ha_trx_info= ha_trx_info->next())
{
- const handlerton *ht= *pht;
+ handlerton *ht= ha_trx_info->ht();
close_at_commit|= test(ht->flags & HTON_CLOSE_CURSORS_AT_COMMIT);
if (ht->create_cursor_read_view)
{
info->ht= ht;
- info->read_view= (ht->create_cursor_read_view)();
+ info->read_view= (ht->create_cursor_read_view)(ht, thd);
++info;
}
}
@@ -334,6 +342,10 @@ Sensitive_cursor::post_open(THD *thd)
}
+/**
+ bzero cursor state in THD.
+*/
+
void
Sensitive_cursor::reset_thd(THD *thd)
{
@@ -393,20 +405,13 @@ Sensitive_cursor::open(JOIN *join_arg)
}
-/*
- SYNOPSIS
- Sensitive_cursor::fetch()
- num_rows fetch up to this number of rows (maybe less)
-
- DESCRIPTION
- Fetch next num_rows rows from the cursor and send them to the client
+/**
+ Fetch next num_rows rows from the cursor and send them to the client.
- Precondition:
- Sensitive_cursor is open
+ Precondition:
+ - Sensitive_cursor is open
- RETURN VALUES:
- none, this function will send OK to the clinet or set an error
- message in THD
+ @param num_rows fetch up to this number of rows (maybe less)
*/
void
@@ -432,7 +437,7 @@ Sensitive_cursor::fetch(ulong num_rows)
thd->set_n_backup_active_arena(this, &backup_arena);
for (info= ht_info; info->read_view ; info++)
- (info->ht->set_cursor_read_view)(info->read_view);
+ (info->ht->set_cursor_read_view)(info->ht, thd, info->read_view);
join->fetch_limit+= num_rows;
@@ -453,7 +458,7 @@ Sensitive_cursor::fetch(ulong num_rows)
reset_thd(thd);
for (info= ht_info; info->read_view; info++)
- (info->ht->set_cursor_read_view)(0);
+ (info->ht->set_cursor_read_view)(info->ht, thd, 0);
if (error == NESTED_LOOP_CURSOR_LIMIT)
{
@@ -478,6 +483,11 @@ Sensitive_cursor::fetch(ulong num_rows)
}
+/**
+ @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()
{
@@ -486,7 +496,7 @@ Sensitive_cursor::close()
for (Engine_info *info= ht_info; info->read_view; info++)
{
- (info->ht->close_cursor_read_view)(info->read_view);
+ (info->ht->close_cursor_read_view)(info->ht, thd, info->read_view);
info->read_view= 0;
info->ht= 0;
}
@@ -587,7 +597,7 @@ int Materialized_cursor::fill_item_list(THD *thd, List<Item> &send_fields)
end:
thd->restore_active_arena(this, &backup_arena);
/* Check for thd->is_error() in case of OOM */
- return rc || thd->net.report_error;
+ return rc || thd->is_error();
}
int Materialized_cursor::open(JOIN *join __attribute__((unused)))
@@ -625,10 +635,9 @@ int Materialized_cursor::open(JOIN *join __attribute__((unused)))
}
-/*
+/**
Fetch up to the given number of rows from a materialized cursor.
- DESCRIPTION
Precondition: the cursor is open.
If the cursor points after the last row, the fetch will automatically
@@ -636,10 +645,6 @@ int Materialized_cursor::open(JOIN *join __attribute__((unused)))
with SERVER_STATUS_LAST_ROW_SENT). This is an extra round trip
and probably should be improved to return
SERVER_STATUS_LAST_ROW_SENT along with the last row.
-
- RETURN VALUE
- none, in case of success the row is sent to the client, otherwise
- an error message is set in THD
*/
void Materialized_cursor::fetch(ulong num_rows)
diff --git a/sql/sql_cursor.h b/sql/sql_cursor.h
index 6edd6b24b36..1f19cbfdbcf 100644
--- a/sql/sql_cursor.h
+++ b/sql/sql_cursor.h
@@ -20,12 +20,14 @@
#pragma interface /* gcc class interface */
#endif
-/*
+/**
+ @file
+
Declarations for implementation of server side cursors. Only
read-only non-scrollable cursors are currently implemented.
*/
-/*
+/**
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).
@@ -36,7 +38,7 @@
class Server_side_cursor: protected Query_arena, public Sql_alloc
{
protected:
- /* Row destination used for fetch */
+ /** Row destination used for fetch */
select_result *result;
public:
Server_side_cursor(MEM_ROOT *mem_root_arg, select_result *result_arg)
@@ -58,8 +60,7 @@ int mysql_open_cursor(THD *thd, uint flags,
select_result *result,
Server_side_cursor **res);
-/* Possible values for flags */
-
+/** 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 80fea3ef1b1..72ae664bba1 100644
--- a/sql/sql_db.cc
+++ b/sql/sql_db.cc
@@ -19,8 +19,10 @@
#include "mysql_priv.h"
#include <mysys_err.h>
#include "sp.h"
+#include "events.h"
#include <my_dir.h>
#include <m_ctype.h>
+#include "log.h"
#ifdef __WIN__
#include <direct.h>
#endif
@@ -37,6 +39,112 @@ static long mysql_rm_known_files(THD *thd, MY_DIR *dirp,
long mysql_rm_arc_files(THD *thd, MY_DIR *dirp, const char *org_path);
static my_bool rm_dir_w_symlink(const char *org_path, my_bool send_error);
+static void mysql_change_db_impl(THD *thd,
+ LEX_STRING *new_db_name,
+ ulong new_db_access,
+ 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;
@@ -55,11 +163,14 @@ typedef struct my_dbopt_st
Function we use in the creation of our hash to get key.
*/
-static byte* dboptions_get_key(my_dbopt_t *opt, uint *length,
- my_bool not_used __attribute__((unused)))
+extern "C" uchar* dboptions_get_key(my_dbopt_t *opt, size_t *length,
+ my_bool not_used);
+
+uchar* dboptions_get_key(my_dbopt_t *opt, size_t *length,
+ my_bool not_used __attribute__((unused)))
{
*length= opt->name_length;
- return (byte*) opt->name;
+ return (uchar*) opt->name;
}
@@ -82,17 +193,19 @@ static inline void write_to_binlog(THD *thd, char *query, uint q_len,
Function to free dboptions hash element
*/
-static void free_dbopt(void *dbopt)
+extern "C" void free_dbopt(void *dbopt);
+
+void free_dbopt(void *dbopt)
{
- my_free((gptr) dbopt, MYF(0));
+ my_free((uchar*) dbopt, MYF(0));
}
/*
- Initialize database option hash
+ Initialize database option hash and locked database hash.
SYNOPSIS
- my_dbopt_init()
+ my_database_names()
NOTES
Must be called before any other database function is called.
@@ -102,7 +215,7 @@ static void free_dbopt(void *dbopt)
1 Fatal error
*/
-bool my_dbopt_init(void)
+bool my_database_names_init(void)
{
bool error= 0;
(void) my_rwlock_init(&LOCK_dboptions, NULL);
@@ -112,27 +225,38 @@ bool my_dbopt_init(void)
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);
+ 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);
+
}
return error;
}
+
/*
- Free database option hash.
+ Free database option hash and locked databases hash.
*/
-void my_dbopt_free(void)
+void my_database_names_free(void)
{
if (dboptions_init)
{
dboptions_init= 0;
hash_free(&dboptions);
(void) rwlock_destroy(&LOCK_dboptions);
+ hash_free(&lock_db_cache);
}
}
+/*
+ Cleanup cached options
+*/
+
void my_dbopt_cleanup(void)
{
rw_wrlock(&LOCK_dboptions);
@@ -166,7 +290,7 @@ 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, (byte*) dbname, length)))
+ if ((opt= (my_dbopt_t*) hash_search(&dboptions, (uchar*) dbname, length)))
{
create->default_table_charset= opt->charset;
error= 0;
@@ -198,12 +322,12 @@ 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, (byte*) dbname, length)))
+ if (!(opt= (my_dbopt_t*) hash_search(&dboptions, (uchar*) dbname, length)))
{
/* Options are not in the hash, insert them */
char *tmp_name;
if (!my_multi_malloc(MYF(MY_WME | MY_ZEROFILL),
- &opt, (uint) sizeof(*opt), &tmp_name, length+1,
+ &opt, (uint) sizeof(*opt), &tmp_name, (uint) length+1,
NullS))
{
error= 1;
@@ -214,9 +338,9 @@ static my_bool put_dbopt(const char *dbname, HA_CREATE_INFO *create)
strmov(opt->name, dbname);
opt->name_length= length;
- if ((error= my_hash_insert(&dboptions, (byte*) opt)))
+ if ((error= my_hash_insert(&dboptions, (uchar*) opt)))
{
- my_free((gptr) opt, MYF(0));
+ my_free(opt, MYF(0));
goto end;
}
}
@@ -238,9 +362,9 @@ void del_dbopt(const char *path)
{
my_dbopt_t *opt;
rw_wrlock(&LOCK_dboptions);
- if ((opt= (my_dbopt_t *)hash_search(&dboptions, (const byte*) path,
- (uint) strlen(path))))
- hash_delete(&dboptions, (byte*) opt);
+ if ((opt= (my_dbopt_t *)hash_search(&dboptions, (const uchar*) path,
+ strlen(path))))
+ hash_delete(&dboptions, (uchar*) opt);
rw_unlock(&LOCK_dboptions);
}
@@ -271,14 +395,14 @@ static bool write_db_opt(THD *thd, const char *path, HA_CREATE_INFO *create)
if ((file=my_create(path, CREATE_MODE,O_RDWR | O_TRUNC,MYF(MY_WME))) >= 0)
{
ulong length;
- length= (ulong) (strxnmov(buf, sizeof(buf), "default-character-set=",
+ length= (ulong) (strxnmov(buf, sizeof(buf)-1, "default-character-set=",
create->default_table_charset->csname,
"\ndefault-collation=",
create->default_table_charset->name,
"\n", NullS) - buf);
/* Error is written by my_write */
- if (!my_write(file,(byte*) buf, length, MYF(MY_NABP+MY_WME)))
+ if (!my_write(file,(uchar*) buf, length, MYF(MY_NABP+MY_WME)))
error=0;
my_close(file,MYF(0));
}
@@ -416,15 +540,48 @@ bool load_db_opt_by_name(THD *thd, const char *db_name,
{
char db_opt_path[FN_REFLEN];
- strxnmov(db_opt_path, sizeof (db_opt_path) - 1, mysql_data_home, "/",
- db_name, "/", MY_DB_OPT_FILE, NullS);
-
- unpack_filename(db_opt_path, db_opt_path);
+ /*
+ Pass an empty file name, and the database options file name as extension
+ to avoid table name to file name encoding.
+ */
+ (void) build_table_filename(db_opt_path, sizeof(db_opt_path),
+ db_name, "", MY_DB_OPT_FILE, 0);
return load_db_opt(thd, db_opt_path, db_create_info);
}
+/**
+ Return default database collation.
+
+ @param thd Thread context.
+ @param db_name Database name.
+
+ @return CHARSET_INFO object. The operation always return valid character
+ set, even if the database does not exist.
+*/
+
+CHARSET_INFO *get_default_db_collation(THD *thd, const char *db_name)
+{
+ HA_CREATE_INFO db_info;
+
+ if (thd->db != NULL && strcmp(db_name, thd->db) == 0)
+ return thd->db_charset;
+
+ load_db_opt_by_name(thd, db_name, &db_info);
+
+ /*
+ NOTE: even if load_db_opt_by_name() fails,
+ db_info.default_table_charset contains valid character set
+ (collation_server). We should not fail if load_db_opt_by_name() fails,
+ because it is valid case. If a database has been created just by
+ "mkdir", it does not contain db.opt file, but it is valid database.
+ */
+
+ return db_info.default_table_charset;
+}
+
+
/*
Create a database
@@ -438,7 +595,7 @@ bool load_db_opt_by_name(THD *thd, const char *db_name,
In this case the entry should not be logged.
SIDE-EFFECTS
- 1. Report back to client that command succeeded (send_ok)
+ 1. Report back to client that command succeeded (my_ok)
2. Report errors to client
3. Log event to binary log
(The 'silent' flags turns off 1 and 3.)
@@ -453,6 +610,7 @@ int mysql_create_db(THD *thd, char *db, HA_CREATE_INFO *create_info,
bool silent)
{
char path[FN_REFLEN+16];
+ char tmp_query[FN_REFLEN+16];
long result= 1;
int error= 0;
MY_STAT stat_info;
@@ -488,8 +646,7 @@ int mysql_create_db(THD *thd, char *db, HA_CREATE_INFO *create_info,
VOID(pthread_mutex_lock(&LOCK_mysql_create_db));
/* Check directory */
- strxmov(path, mysql_data_home, "/", db, NullS);
- path_len= unpack_dirname(path,path); // Convert if not unix
+ path_len= build_table_filename(path, sizeof(path), db, "", "", 0);
path[path_len-1]= 0; // Remove last '/' from path
if (my_stat(path,&stat_info,MYF(0)))
@@ -503,7 +660,7 @@ int mysql_create_db(THD *thd, char *db, HA_CREATE_INFO *create_info,
push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
ER_DB_CREATE_EXISTS, ER(ER_DB_CREATE_EXISTS), db);
if (!silent)
- send_ok(thd);
+ my_ok(thd);
error= 0;
goto exit;
}
@@ -550,15 +707,20 @@ int mysql_create_db(THD *thd, char *db, HA_CREATE_INFO *create_info,
if (!thd->query) // Only in replication
{
- query= path;
- query_length= (uint) (strxmov(path,"create database `", db, "`", NullS) -
- path);
+ query= tmp_query;
+ query_length= (uint) (strxmov(tmp_query,"create database `",
+ db, "`", NullS) - tmp_query);
}
else
{
query= thd->query;
query_length= thd->query_length;
}
+
+ ha_binlog_log_query(thd, 0, LOGCOM_CREATE_DB,
+ query, query_length,
+ db, "");
+
if (mysql_bin_log.is_open())
{
Query_log_event qinfo(thd, query, query_length, 0,
@@ -582,12 +744,12 @@ int mysql_create_db(THD *thd, char *db, HA_CREATE_INFO *create_info,
# database does not exist.
*/
qinfo.db = db;
- qinfo.db_len = (uint) strlen(db);
+ qinfo.db_len = strlen(db);
/* These DDL methods and logging protected with LOCK_mysql_create_db */
mysql_bin_log.write(&qinfo);
}
- send_ok(thd, result);
+ my_ok(thd, result);
}
exit:
@@ -624,16 +786,17 @@ bool mysql_alter_db(THD *thd, const char *db, HA_CREATE_INFO *create_info)
VOID(pthread_mutex_lock(&LOCK_mysql_create_db));
- /* Check directory */
- strxmov(path, mysql_data_home, "/", db, "/", MY_DB_OPT_FILE, NullS);
- fn_format(path, path, "", "", MYF(MY_UNPACK_FILENAME));
+ /*
+ Recreate db options file: /dbpath/.db.opt
+ We pass MY_DB_OPT_FILE as "extension" to avoid
+ "table name to file name" encoding.
+ */
+ build_table_filename(path, sizeof(path), db, "", MY_DB_OPT_FILE, 0);
if ((error=write_db_opt(thd, path, create_info)))
goto exit;
- /*
- Change options if current database is being altered
- TODO: Delete this code
- */
+ /* Change options if current database is being altered. */
+
if (thd->db && !strcmp(thd->db,db))
{
thd->db_charset= create_info->default_table_charset ?
@@ -642,6 +805,10 @@ bool mysql_alter_db(THD *thd, const char *db, HA_CREATE_INFO *create_info)
thd->variables.collation_database= thd->db_charset;
}
+ ha_binlog_log_query(thd, 0, LOGCOM_ALTER_DB,
+ thd->query, thd->query_length,
+ db, "");
+
if (mysql_bin_log.is_open())
{
Query_log_event qinfo(thd, thd->query, thd->query_length, 0,
@@ -653,13 +820,13 @@ bool mysql_alter_db(THD *thd, const char *db, HA_CREATE_INFO *create_info)
default.
*/
qinfo.db = db;
- qinfo.db_len = (uint) strlen(db);
+ qinfo.db_len = strlen(db);
thd->clear_error();
/* These DDL methods and logging protected with LOCK_mysql_create_db */
mysql_bin_log.write(&qinfo);
}
- send_ok(thd, result);
+ my_ok(thd, result);
exit:
VOID(pthread_mutex_unlock(&LOCK_mysql_create_db));
@@ -716,8 +883,7 @@ bool mysql_rm_db(THD *thd,char *db,bool if_exists, bool silent)
VOID(pthread_mutex_lock(&LOCK_mysql_create_db));
- (void) sprintf(path,"%s/%s",mysql_data_home,db);
- length= unpack_dirname(path,path); // Convert if not unix
+ length= build_table_filename(path, sizeof(path), db, "", "", 0);
strmov(path+length, MY_DB_OPT_FILE); // Append db option file name
del_dbopt(path); // Remove dboption hash entry
path[length]= '\0'; // Remove file name
@@ -741,14 +907,37 @@ bool mysql_rm_db(THD *thd,char *db,bool if_exists, bool silent)
remove_db_from_cache(db);
pthread_mutex_unlock(&LOCK_open);
-
error= -1;
+ /*
+ We temporarily disable the binary log while dropping the objects
+ in the database. Since the DROP DATABASE statement is always
+ replicated as a statement, execution of it will drop all objects
+ in the database on the slave as well, so there is no need to
+ replicate the removal of the individual objects in the database
+ as well.
+
+ This is more of a safety precaution, since normally no objects
+ should be dropped while the database is being cleaned, but in
+ the event that a change in the code to remove other objects is
+ made, these drops should still not be logged.
+
+ Notice that the binary log have to be enabled over the call to
+ 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(db);
+ (void) sp_drop_db_routines(thd, db); /* @todo Do not ignore errors */
+#ifdef HAVE_EVENT_SCHEDULER
+ Events::drop_schema_events(thd, db);
+#endif
error = 0;
+ reenable_binlog(thd);
}
}
if (!silent && deleted>=0)
@@ -777,14 +966,15 @@ bool mysql_rm_db(THD *thd,char *db,bool if_exists, bool silent)
default.
*/
qinfo.db = db;
- qinfo.db_len = (uint) strlen(db);
+ qinfo.db_len = strlen(db);
thd->clear_error();
/* These DDL methods and logging protected with LOCK_mysql_create_db */
mysql_bin_log.write(&qinfo);
}
+ thd->clear_error();
thd->server_status|= SERVER_STATUS_DB_DROPPED;
- send_ok(thd, (ulong) deleted);
+ my_ok(thd, (ulong) deleted);
thd->server_status&= ~SERVER_STATUS_DB_DROPPED;
}
else if (mysql_bin_log.is_open())
@@ -793,22 +983,22 @@ bool mysql_rm_db(THD *thd,char *db,bool if_exists, bool silent)
TABLE_LIST *tbl;
uint db_len;
- if (!(query= thd->alloc(MAX_DROP_TABLE_Q_LEN)))
+ if (!(query= (char*) thd->alloc(MAX_DROP_TABLE_Q_LEN)))
goto exit; /* not much else we can do */
query_pos= query_data_start= strmov(query,"drop table ");
query_end= query + MAX_DROP_TABLE_Q_LEN;
- db_len= (uint) strlen(db);
+ db_len= strlen(db);
for (tbl= dropped_tables; tbl; tbl= tbl->next_local)
{
uint tbl_name_len;
/* 3 for the quotes and the comma*/
- tbl_name_len= (uint) strlen(tbl->table_name) + 3;
+ tbl_name_len= strlen(tbl->table_name) + 3;
if (query_pos + tbl_name_len + 1 >= query_end)
{
/* These DDL methods and logging protected with LOCK_mysql_create_db */
- write_to_binlog(thd, query, (uint) (query_pos - 1 - query), db, db_len);
+ write_to_binlog(thd, query, query_pos -1 - query, db, db_len);
query_pos= query_data_start;
}
@@ -821,12 +1011,11 @@ 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 */
- write_to_binlog(thd, query, (uint) (query_pos - 1 - query), db, db_len);
+ write_to_binlog(thd, query, query_pos -1 - query, db, db_len);
}
}
exit:
- (void)sp_drop_db_routines(thd, db); /* QQ Ignore errors for now */
/*
If this database was the client's selected database, we silently
change the client's selected database to nothing (to have an empty
@@ -834,7 +1023,7 @@ exit:
it to 0.
*/
if (thd->db && !strcmp(thd->db, db))
- thd->set_db(NULL, 0);
+ 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:
@@ -893,7 +1082,7 @@ static long mysql_rm_known_files(THD *thd, MY_DIR *dirp, const char *db,
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= thd->memdup(newpath, length+1)) ||
+ 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))
@@ -924,7 +1113,8 @@ static long mysql_rm_known_files(THD *thd, MY_DIR *dirp, const char *db,
found_other_files++;
continue;
}
- extension= fn_ext(file->name);
+ if (!(extension= strrchr(file->name, '.')))
+ extension= strend(file->name);
if (find_type(extension, &deletable_extentions,1+2) <= 0)
{
if (find_type(extension, ha_known_exts(),1+2) <= 0)
@@ -938,12 +1128,20 @@ static long mysql_rm_known_files(THD *thd, MY_DIR *dirp, const char *db,
/* Drop the table nicely */
*extension= 0; // Remove extension
TABLE_LIST *table_list=(TABLE_LIST*)
- thd->calloc((uint) (sizeof(*table_list)+ strlen(db)+strlen(file->name)+2));
+ thd->calloc(sizeof(*table_list) +
+ strlen(db) + 1 +
+ MYSQL50_TABLE_NAME_PREFIX_LENGTH +
+ strlen(file->name) + 1);
+
if (!table_list)
- goto err;
+ goto err;
table_list->db= (char*) (table_list+1);
- strmov(table_list->table_name= strmov(table_list->db,db)+1, file->name);
+ table_list->table_name= strmov(table_list->db, db) + 1;
+ VOID(filename_to_tablename(file->name, table_list->table_name,
+ MYSQL50_TABLE_NAME_PREFIX_LENGTH +
+ strlen(file->name) + 1));
table_list->alias= table_list->table_name; // If lower_case_table_names=2
+ table_list->internal_tmp_table= is_prefix(file->name, tmp_file_prefix);
/* Link into list */
(*tot_list_next)= table_list;
tot_list_next= &table_list->next_local;
@@ -959,7 +1157,7 @@ static long mysql_rm_known_files(THD *thd, MY_DIR *dirp, const char *db,
}
}
if (thd->killed ||
- (tot_list && mysql_rm_table_part2_with_lock(thd, tot_list, 1, 0, 1)))
+ (tot_list && mysql_rm_table_part2(thd, tot_list, 1, 0, 1, 1)))
goto err;
/* Remove RAID directories */
@@ -1196,22 +1394,108 @@ static void mysql_change_db_impl(THD *thd,
}
+
/**
- @brief Change the current database.
+ Backup the current database name before switch.
+
+ @param[in] thd thread handle
+ @param[in, out] saved_db_name IN: "str" points to a buffer where to store
+ the old database name, "length" contains the
+ buffer size
+ OUT: if the current (default) database is
+ not NULL, its name is copied to the
+ buffer pointed at by "str"
+ and "length" is updated accordingly.
+ Otherwise "str" is set to NULL and
+ "length" is set to 0.
+*/
+
+static void backup_current_db_name(THD *thd,
+ LEX_STRING *saved_db_name)
+{
+ if (!thd->db)
+ {
+ /* No current (default) database selected. */
+
+ saved_db_name->str= NULL;
+ saved_db_name->length= 0;
+ }
+ else
+ {
+ strmake(saved_db_name->str, thd->db, saved_db_name->length - 1);
+ saved_db_name->length= thd->db_length;
+ }
+}
+
+
+/**
+ Return TRUE if db1_name is equal to db2_name, FALSE otherwise.
+
+ The function allows to compare database names according to the MySQL
+ rules. The database names db1 and db2 are equal if:
+ - db1 is NULL and db2 is NULL;
+ or
+ - db1 is not-NULL, db2 is not-NULL, db1 is equal (ignoring case) to
+ db2 in system character set (UTF8).
+*/
+
+static inline bool
+cmp_db_names(const char *db1_name,
+ const char *db2_name)
+{
+ return
+ /* db1 is NULL and db2 is NULL */
+ !db1_name && !db2_name ||
+
+ /* db1 is not-NULL, db2 is not-NULL, db1 == db2. */
+ db1_name && db2_name &&
+ my_strcasecmp(system_charset_info, db1_name, db2_name) == 0;
+}
+
+
+/**
+ @brief Change the current database and its attributes unconditionally.
@param thd thread handle
- @param name database name
- @param force_switch if this flag is set (TRUE), mysql_change_db() will
- switch to NULL db if the specified database is not
- available anymore. Corresponding warning will be
- thrown in this case. This flag is used to change
- database in stored-routine-execution code.
-
- @details Check that the database name corresponds to a valid and existent
- database, check access rights (unless called with no_access_check), and
- set the current database. This function is called to change the current
- database upon user request (COM_CHANGE_DB command) or temporarily, to
- execute a stored routine.
+ @param new_db_name database name
+ @param force_switch if force_switch is FALSE, then the operation will fail if
+
+ - new_db_name is NULL or empty;
+
+ - OR new database name is invalid
+ (check_db_name() failed);
+
+ - OR user has no privilege on the new database;
+
+ - OR new database does not exist;
+
+ if force_switch is TRUE, then
+
+ - if new_db_name is NULL or empty, the current
+ database will be NULL, @@collation_database will
+ be set to @@collation_server, the operation will
+ succeed.
+
+ - if new database name is invalid
+ (check_db_name() failed), the current database
+ will be NULL, @@collation_database will be set to
+ @@collation_server, but the operation will fail;
+
+ - user privileges will not be checked
+ (THD::db_access however is updated);
+
+ TODO: is this really the intention?
+ (see sp-security.test).
+
+ - if new database does not exist,the current database
+ will be NULL, @@collation_database will be set to
+ @@collation_server, a warning will be thrown, the
+ operation will succeed.
+
+ @details The function checks that the database name corresponds to a
+ valid and existent database, checks access rights and changes the current
+ database with database attributes (@@collation_database session variable,
+ THD::db_access).
This function is not the only way to switch the database that is
currently employed. When the replication slave thread switches the
@@ -1237,19 +1521,24 @@ bool mysql_change_db(THD *thd, const LEX_STRING *new_db_name, bool force_switch)
Security_context *sctx= thd->security_ctx;
ulong db_access= sctx->db_access;
+ CHARSET_INFO *db_default_cl;
DBUG_ENTER("mysql_change_db");
DBUG_PRINT("enter",("name: '%s'", new_db_name->str));
if (new_db_name == NULL ||
- new_db_name->length == 0 ||
- new_db_name->str == NULL)
+ new_db_name->length == 0)
{
if (force_switch)
{
/*
- This can only happen when we restore the old db in THD after
- execution of a routine is complete. Change db to NULL.
+ This can happen only if we're switching the current database back
+ after loading stored program. The thing is that loading of stored
+ program can happen when there is no current database.
+
+ TODO: actually, new_db_name and new_db_name->str seem to be always
+ non-NULL. In case of stored program, new_db_name->str == "" and
+ new_db_name->length == 0.
*/
mysql_change_db_impl(thd, NULL, 0, thd->variables.collation_server);
@@ -1267,7 +1556,7 @@ bool mysql_change_db(THD *thd, const LEX_STRING *new_db_name, bool force_switch)
if (my_strcasecmp(system_charset_info, new_db_name->str,
INFORMATION_SCHEMA_NAME.str) == 0)
{
- /* Switch database to INFORMATION_SCHEMA. */
+ /* Switch the current database to INFORMATION_SCHEMA. */
mysql_change_db_impl(thd, &INFORMATION_SCHEMA_NAME, SELECT_ACL,
system_charset_info);
@@ -1282,7 +1571,8 @@ bool mysql_change_db(THD *thd, const LEX_STRING *new_db_name, bool force_switch)
TODO: fix check_db_name().
*/
- new_db_file_name.str= my_strdup(new_db_name->str, MYF(MY_WME));
+ new_db_file_name.str= my_strndup(new_db_name->str, new_db_name->length,
+ MYF(MY_WME));
new_db_file_name.length= new_db_name->length;
if (new_db_file_name.str == NULL)
@@ -1293,21 +1583,17 @@ bool mysql_change_db(THD *thd, const LEX_STRING *new_db_name, bool force_switch)
even if we are called from sp_head::execute().
It's next to impossible however to get this error when we are called
- from sp_head::execute(). But let's switch database to NULL in this case
- to be sure.
+ from sp_head::execute(). But let's switch the current database to NULL
+ in this case to be sure.
*/
- if (check_db_name(new_db_file_name.str))
+ 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));
if (force_switch)
- {
- /* Change db to NULL. */
-
mysql_change_db_impl(thd, NULL, 0, thd->variables.collation_server);
- }
DBUG_RETURN(TRUE);
}
@@ -1326,14 +1612,14 @@ bool mysql_change_db(THD *thd, const LEX_STRING *new_db_name, bool force_switch)
if (!force_switch &&
!(db_access & DB_ACLS) &&
- (!grant_option || check_grant_db(thd, new_db_file_name.str)))
+ check_grant_db(thd, new_db_file_name.str))
{
my_error(ER_DBACCESS_DENIED_ERROR, MYF(0),
sctx->priv_user,
sctx->priv_host,
new_db_file_name.str);
- mysql_log.write(thd, COM_INIT_DB, ER(ER_DBACCESS_DENIED_ERROR),
- sctx->priv_user, sctx->priv_host, 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));
DBUG_RETURN(TRUE);
}
@@ -1343,6 +1629,8 @@ bool mysql_change_db(THD *thd, const LEX_STRING *new_db_name, bool force_switch)
{
if (force_switch)
{
+ /* Throw a warning and free new_db_file_name. */
+
push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
ER_BAD_DB_ERROR, ER(ER_BAD_DB_ERROR),
new_db_file_name.str);
@@ -1353,12 +1641,19 @@ bool mysql_change_db(THD *thd, const LEX_STRING *new_db_name, bool force_switch)
mysql_change_db_impl(thd, NULL, 0, thd->variables.collation_server);
+ /* The operation succeed. */
+
DBUG_RETURN(FALSE);
}
else
{
+ /* 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));
+
+ /* The operation failed. */
+
DBUG_RETURN(TRUE);
}
}
@@ -1368,21 +1663,325 @@ bool mysql_change_db(THD *thd, const LEX_STRING *new_db_name, bool force_switch)
attributes and will be freed in THD::~THD().
*/
+ db_default_cl= get_default_db_collation(thd, new_db_file_name.str);
+
+ mysql_change_db_impl(thd, &new_db_file_name, db_access, db_default_cl);
+
+ DBUG_RETURN(FALSE);
+}
+
+
+/**
+ Change the current database and its attributes if needed.
+
+ @param thd thread handle
+ @param new_db_name database name
+ @param[in, out] saved_db_name IN: "str" points to a buffer where to store
+ the old database name, "length" contains the
+ buffer size
+ OUT: if the current (default) database is
+ not NULL, its name is copied to the
+ buffer pointed at by "str"
+ and "length" is updated accordingly.
+ Otherwise "str" is set to NULL and
+ "length" is set to 0.
+ @param force_switch @see mysql_change_db()
+ @param[out] cur_db_changed out-flag to indicate whether the current
+ database has been changed (valid only if
+ the function suceeded)
+*/
+
+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)
+{
+ *cur_db_changed= !cmp_db_names(thd->db, new_db_name->str);
+
+ if (!*cur_db_changed)
+ return FALSE;
+
+ backup_current_db_name(thd, saved_db_name);
+
+ return mysql_change_db(thd, new_db_name, force_switch);
+}
+
+
+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)))
{
- HA_CREATE_INFO db_options;
+ wait_for_condition(thd, &LOCK_lock_db, &COND_refresh);
+ pthread_mutex_lock(&LOCK_lock_db);
+ }
- load_db_opt_by_name(thd, new_db_name->str, &db_options);
+ 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.
+ */
- mysql_change_db_impl(thd, &new_db_file_name, db_access,
- db_options.default_table_charset ?
- db_options.default_table_charset :
- thd->variables.collation_server);
+ while (!thd->killed && creating_table)
+ {
+ wait_for_condition(thd, &LOCK_lock_db, &COND_refresh);
+ pthread_mutex_lock(&LOCK_lock_db);
}
- DBUG_RETURN(FALSE);
+ 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:
+ ALTER DATABASE 'olddb' UPGRADE DATA DIRECTORY NAME.
+
+ If we have managed to rename (move) tables to the new database
+ but something failed on a later step, then we store the
+ RENAME DATABASE event in the log. mysql_rename_db() is atomic in
+ the sense that it will rename all or none of the tables.
+
+ @param thd Current thread
+ @param old_db 5.0 database name, in #mysql50#name format
+ @return 0 on success, 1 on error
+*/
+bool mysql_upgrade_db(THD *thd, LEX_STRING *old_db)
+{
+ int error= 0, change_to_newdb= 0;
+ char path[FN_REFLEN+16];
+ uint length;
+ HA_CREATE_INFO create_info;
+ MY_DIR *dirp;
+ TABLE_LIST *table_list;
+ SELECT_LEX *sl= thd->lex->current_select;
+ LEX_STRING new_db;
+ DBUG_ENTER("mysql_upgrade_db");
+
+ if ((old_db->length <= MYSQL50_TABLE_NAME_PREFIX_LENGTH) ||
+ (strncmp(old_db->str,
+ MYSQL50_TABLE_NAME_PREFIX,
+ MYSQL50_TABLE_NAME_PREFIX_LENGTH) != 0))
+ {
+ my_error(ER_WRONG_USAGE, MYF(0),
+ "ALTER DATABASE UPGRADE DATA DIRECTORY NAME",
+ "name");
+ DBUG_RETURN(1);
+ }
+
+ /* `#mysql50#<name>` converted to encoded `<name>` */
+ 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))
+ DBUG_RETURN(1);
+
+ /*
+ Let's remember if we should do "USE newdb" afterwards.
+ thd->db will be cleared in mysql_rename_db()
+ */
+ if (thd->db && !strcmp(thd->db, old_db->str))
+ change_to_newdb= 1;
+
+ build_table_filename(path, sizeof(path)-1,
+ old_db->str, "", MY_DB_OPT_FILE, 0);
+ if ((load_db_opt(thd, path, &create_info)))
+ create_info.default_table_charset= thd->variables.collation_server;
+
+ length= build_table_filename(path, sizeof(path)-1, old_db->str, "", "", 0);
+ if (length && path[length-1] == FN_LIBCHAR)
+ path[length-1]=0; // remove ending '\'
+ if ((error= my_access(path,F_OK)))
+ {
+ my_error(ER_BAD_DB_ERROR, MYF(0), old_db->str);
+ goto exit;
+ }
+
+ /* Step1: Create the new database */
+ if ((error= mysql_create_db(thd, new_db.str, &create_info, 1)))
+ goto exit;
+
+ /* Step2: Move tables to the new database */
+ if ((dirp = my_dir(path,MYF(MY_DONT_SORT))))
+ {
+ uint nfiles= (uint) dirp->number_off_files;
+ for (uint idx=0 ; idx < nfiles && !thd->killed ; idx++)
+ {
+ FILEINFO *file= dirp->dir_entry + idx;
+ char *extension, tname[FN_REFLEN];
+ LEX_STRING table_str;
+ DBUG_PRINT("info",("Examining: %s", file->name));
+
+ /* skiping non-FRM files */
+ if (my_strcasecmp(files_charset_info,
+ (extension= fn_rext(file->name)), reg_ext))
+ continue;
+
+ /* A frm file found, add the table info rename list */
+ *extension= '\0';
+
+ table_str.length= filename_to_tablename(file->name,
+ tname, sizeof(tname)-1);
+ table_str.str= (char*) sql_memdup(tname, table_str.length + 1);
+ Table_ident *old_ident= new Table_ident(thd, *old_db, table_str, 0);
+ Table_ident *new_ident= new Table_ident(thd, new_db, table_str, 0);
+ if (!old_ident || !new_ident ||
+ !sl->add_table_to_list(thd, old_ident, NULL,
+ TL_OPTION_UPDATING, TL_IGNORE) ||
+ !sl->add_table_to_list(thd, new_ident, NULL,
+ TL_OPTION_UPDATING, TL_IGNORE))
+ {
+ error= 1;
+ my_dirend(dirp);
+ goto exit;
+ }
+ }
+ my_dirend(dirp);
+ }
+
+ if ((table_list= thd->lex->query_tables) &&
+ (error= mysql_rename_tables(thd, table_list, 1)))
+ {
+ /*
+ Failed to move all tables from the old database to the new one.
+ In the best case mysql_rename_tables() moved all tables back to the old
+ database. In the worst case mysql_rename_tables() moved some tables
+ to the new database, then failed, then started to move the tables back,
+ and then failed again. In this situation we have some tables in the
+ old database and some tables in the new database.
+ Let's delete the option file, and then the new database directory.
+ If some tables were left in the new directory, rmdir() will fail.
+ It garantees we never loose any tables.
+ */
+ build_table_filename(path, sizeof(path)-1,
+ new_db.str,"",MY_DB_OPT_FILE, 0);
+ my_delete(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 '\'
+ rmdir(path);
+ goto exit;
+ }
+
+
+ /*
+ Step3: move all remaining files to the new db's directory.
+ Skip db opt file: it's been created by mysql_create_db() in
+ the new directory, and will be dropped by mysql_rm_db() in the old one.
+ Trigger TRN and TRG files are be moved as regular files at the moment,
+ without any special treatment.
+
+ Triggers without explicit database qualifiers in table names work fine:
+ use d1;
+ create trigger trg1 before insert on t2 for each row set @a:=1
+ rename database d1 to d2;
+
+ TODO: Triggers, having the renamed database explicitely written
+ in the table qualifiers.
+ 1. when the same database is renamed:
+ create trigger d1.trg1 before insert on d1.t1 for each row set @a:=1;
+ rename database d1 to d2;
+ Problem: After database renaming, the trigger's body
+ still points to the old database d1.
+ 2. when another database is renamed:
+ create trigger d3.trg1 before insert on d3.t1 for each row
+ insert into d1.t1 values (...);
+ rename database d1 to d2;
+ Problem: After renaming d1 to d2, the trigger's body
+ in the database d3 still points to database d1.
+ */
+
+ if ((dirp = my_dir(path,MYF(MY_DONT_SORT))))
+ {
+ uint nfiles= (uint) dirp->number_off_files;
+ for (uint idx=0 ; idx < nfiles ; idx++)
+ {
+ FILEINFO *file= dirp->dir_entry + idx;
+ char oldname[FN_REFLEN], newname[FN_REFLEN];
+ DBUG_PRINT("info",("Examining: %s", file->name));
+
+ /* skiping . and .. and MY_DB_OPT_FILE */
+ if ((file->name[0] == '.' &&
+ (!file->name[1] || (file->name[1] == '.' && !file->name[2]))) ||
+ !my_strcasecmp(files_charset_info, file->name, MY_DB_OPT_FILE))
+ continue;
+
+ /* pass empty file name, and file->name as extension to avoid encoding */
+ build_table_filename(oldname, sizeof(oldname)-1,
+ 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));
+ }
+ 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.
+ mysql_rm_db() also "unuses" if we drop the current database.
+ */
+ error= mysql_rm_db(thd, old_db->str, 0, 1);
+
+ /* Step8: logging */
+ if (mysql_bin_log.is_open())
+ {
+ Query_log_event qinfo(thd, thd->query, thd->query_length, 0, TRUE);
+ thd->clear_error();
+ mysql_bin_log.write(&qinfo);
+ }
+
+ /* Step9: Let's do "use newdb" if we renamed the current database */
+ if (change_to_newdb)
+ 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);
+}
+
+
+
/*
Check if there is directory for the database name.
@@ -1400,12 +1999,8 @@ bool check_db_dir_existence(const char *db_name)
char db_dir_path[FN_REFLEN];
uint db_dir_path_len;
- strxnmov(db_dir_path, sizeof (db_dir_path) - 1, mysql_data_home, "/",
- db_name, NullS);
-
- db_dir_path_len= unpack_dirname(db_dir_path, db_dir_path);
-
- /* Remove trailing '/' or '\' if exists. */
+ db_dir_path_len= build_table_filename(db_dir_path, sizeof(db_dir_path),
+ db_name, "", "", 0);
if (db_dir_path_len && db_dir_path[db_dir_path_len - 1] == FN_LIBCHAR)
db_dir_path[db_dir_path_len - 1]= 0;
diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc
index 38f89683065..d87eafa3e0c 100644
--- a/sql/sql_delete.cc
+++ b/sql/sql_delete.cc
@@ -20,28 +20,42 @@
*/
#include "mysql_priv.h"
-#include "ha_innodb.h"
#include "sql_select.h"
#include "sp_head.h"
#include "sql_trigger.h"
+/**
+ Implement DELETE SQL word.
+
+ @note Like implementations of other DDL/DML in MySQL, this function
+ relies on the caller to close the thread tables. This is done in the
+ end of dispatch_command().
+*/
+
bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
SQL_LIST *order, ha_rows limit, ulonglong options,
bool reset_auto_increment)
{
- int error;
+ bool will_batch;
+ int error, loc_error;
TABLE *table;
SQL_SELECT *select=0;
READ_RECORD info;
bool using_limit=limit != HA_POS_ERROR;
bool transactional_table, safe_update, const_cond;
+ bool const_cond_result;
+ ha_rows deleted= 0;
bool triggers_applicable;
- ha_rows deleted;
uint usable_index= MAX_KEY;
SELECT_LEX *select_lex= &thd->lex->select_lex;
THD::killed_state killed_status= THD::NOT_KILLED;
DBUG_ENTER("mysql_delete");
+ 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))
DBUG_RETURN(TRUE);
if (!(table= table_list->table))
@@ -50,13 +64,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
table_list->view_db.str, table_list->view_name.str);
DBUG_RETURN(TRUE);
}
- error= table->file->info(HA_STATUS_VARIABLE | HA_STATUS_NO_LOCK);
- if (error)
- {
- table->file->print_error(error, MYF(0));
- DBUG_RETURN(error);
- }
- thd->proc_info="init";
+ thd_proc_info(thd, "init");
table->map=1;
if (mysql_prepare_delete(thd, table_list, &conds))
@@ -94,27 +102,51 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
select_lex->no_error= thd->lex->ignore;
- /* NOTE: TRUNCATE must not invoke triggers. */
-
- triggers_applicable= table->triggers &&
- thd->lex->sql_command != SQLCOM_TRUNCATE;
+ const_cond_result= const_cond && (!conds || conds->val_int());
+ if (thd->is_error())
+ {
+ /* Error evaluating val_int(). */
+ DBUG_RETURN(TRUE);
+ }
/*
Test if the user wants to delete all rows and deletion doesn't have
any side-effects (because of triggers), so we can use optimized
handler::delete_all_rows() method.
- We implement fast TRUNCATE for InnoDB even if triggers are present.
- TRUNCATE ignores triggers.
+
+ 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
+ - there should be no delete triggers associated with the table.
*/
- if (!using_limit && const_cond && (!conds || conds->val_int()) &&
+ if (!using_limit && const_cond_result &&
!(specialflag & (SPECIAL_NO_NEW_FUNC | SPECIAL_SAFE_MODE)) &&
- !(triggers_applicable && table->triggers->has_delete_triggers())
- )
+ (thd->lex->sql_command == SQLCOM_TRUNCATE ||
+ (!thd->current_stmt_binlog_row_based &&
+ !(table->triggers && table->triggers->has_delete_triggers()))))
{
- deleted= table->file->records;
- if (!(error=table->file->delete_all_rows()))
+ /* Update the table->file->stats.records number */
+ table->file->info(HA_STATUS_VARIABLE | HA_STATUS_NO_LOCK);
+ ha_rows const maybe_deleted= table->file->stats.records;
+ DBUG_PRINT("debug", ("Trying to use delete_all_rows()"));
+ if (!(error=table->file->ha_delete_all_rows()))
{
+ /*
+ If delete_all_rows() is used, it is not possible to log the
+ query in row format, so we have to log it in statement format.
+ */
+ query_type= THD::STMT_QUERY_TYPE;
error= -1; // ok
+ deleted= maybe_deleted;
goto cleanup;
}
if (error != HA_ERR_WRONG_COMMAND)
@@ -125,7 +157,6 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
}
/* Handler didn't support fast delete; Delete rows one by one */
}
-
if (conds)
{
Item::cond_result result;
@@ -134,7 +165,19 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
limit= 0;
}
- table->used_keys.clear_all();
+#ifdef WITH_PARTITION_STORAGE_ENGINE
+ 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
+ DBUG_RETURN(0);
+ }
+#endif
+ /* Update the table->file->stats.records number */
+ table->file->info(HA_STATUS_VARIABLE | HA_STATUS_NO_LOCK);
+
+ 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)
@@ -144,14 +187,12 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
delete select;
free_underlaid_joins(thd, select_lex);
thd->row_count_func= 0;
- send_ok(thd,0L);
-
+ 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.
*/
-
DBUG_RETURN(0); // Nothing to delete
}
@@ -188,7 +229,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
if (!(sortorder= make_unireg_sortorder((ORDER*) order->first,
&length, NULL)) ||
(table->sort.found_records = filesort(thd, table, sortorder, length,
- select, HA_POS_ERROR,
+ select, HA_POS_ERROR, 1,
&examined_rows))
== HA_POS_ERROR)
{
@@ -218,30 +259,37 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
else
init_read_record_idx(&info, thd, table, 1, usable_index);
- deleted=0L;
init_ftfuncs(thd, select_lex, 1);
- thd->proc_info="updating";
+ 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 (triggers_applicable &&
+ table->triggers->has_triggers(TRG_EVENT_DELETE,
+ TRG_ACTION_AFTER))
{
- table->triggers->mark_fields_used(thd, TRG_EVENT_DELETE);
- if (table->triggers->has_triggers(TRG_EVENT_DELETE,
- TRG_ACTION_AFTER))
- {
- /*
- The table has AFTER DELETE triggers that might access to subject table
- and therefore might need delete to be done immediately. So we turn-off
- the batching.
- */
- (void) table->file->extra(HA_EXTRA_DELETE_CANNOT_BATCH);
- }
+ /*
+ The table has AFTER DELETE triggers that might access to subject table
+ and therefore might need delete to be done immediately. So we turn-off
+ the batching.
+ */
+ (void) table->file->extra(HA_EXTRA_DELETE_CANNOT_BATCH);
+ will_batch= FALSE;
}
+ else
+ will_batch= !table->file->start_bulk_delete();
+
+
+ table->mark_columns_needed_for_delete();
while (!(error=info.read_record(&info)) && !thd->killed &&
- !thd->net.report_error)
+ ! thd->is_error())
{
- // thd->net.report_error is tested to disallow delete row on error
- if (!(select && select->skip_record())&& !thd->net.report_error )
+ // thd->is_error() is tested to disallow delete row on error
+ if (!(select && select->skip_record())&& ! thd->is_error() )
{
if (triggers_applicable &&
@@ -252,7 +300,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
break;
}
- if (!(error=table->file->delete_row(table->record[0])))
+ if (!(error= table->file->ha_delete_row(table->record[0])))
{
deleted++;
if (triggers_applicable &&
@@ -287,10 +335,16 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
table->file->unlock_row(); // Row failed selection, release lock on it
}
killed_status= thd->killed;
- error= (killed_status == THD::NOT_KILLED)? error : 1;
- thd->proc_info="end";
+ if (killed_status != THD::NOT_KILLED || thd->is_error())
+ error= 1; // Aborted
+ if (will_batch && (loc_error= table->file->end_bulk_delete()))
+ {
+ if (error != 1)
+ table->file->print_error(loc_error,MYF(0));
+ error=1;
+ }
+ thd_proc_info(thd, "end");
end_read_record(&info);
- free_io_cache(table); // Will not do any harm
if (options & OPTION_QUICK)
(void) table->file->extra(HA_EXTRA_NORMAL);
@@ -300,7 +354,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
We're really doing a truncate and need to reset the table's
auto-increment counter.
*/
- int error2= table->file->reset_auto_increment(0);
+ int error2= table->file->ha_reset_auto_increment(0);
if (error2 && (error2 != HA_ERR_WRONG_COMMAND))
{
@@ -321,6 +375,7 @@ cleanup:
delete select;
transactional_table= table->file->has_transactions();
+
if (!transactional_table && deleted > 0)
thd->transaction.stmt.modified_non_trans_table= TRUE;
@@ -329,36 +384,47 @@ cleanup:
{
if (mysql_bin_log.is_open())
{
+ bool const is_trans=
+ thd->lex->sql_command == SQLCOM_TRUNCATE ?
+ FALSE :
+ transactional_table;
+
if (error < 0)
thd->clear_error();
- Query_log_event qinfo(thd, thd->query, thd->query_length,
- transactional_table, FALSE, killed_status);
- if (mysql_bin_log.write(&qinfo) && transactional_table)
+ /*
+ [binlog]: If 'handler::delete_all_rows()' was called and the
+ 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, killed_status);
+
+ if (log_result && transactional_table)
+ {
error=1;
+ }
}
if (thd->transaction.stmt.modified_non_trans_table)
thd->transaction.all.modified_non_trans_table= TRUE;
}
DBUG_ASSERT(transactional_table || !deleted || thd->transaction.stmt.modified_non_trans_table);
free_underlaid_joins(thd, select_lex);
- if (transactional_table)
- {
- if (ha_autocommit_or_rollback(thd,error >= 0))
- error=1;
- }
-
- if (thd->lock)
- {
- mysql_unlock_tables(thd, thd->lock);
- thd->lock=0;
- }
if (error < 0 || (thd->lex->ignore && !thd->is_fatal_error))
{
- thd->row_count_func= deleted;
- send_ok(thd,deleted);
+ /*
+ 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);
DBUG_PRINT("info",("%ld records deleted",(long) deleted));
}
- DBUG_RETURN(error >= 0 || thd->net.report_error);
+ DBUG_RETURN(error >= 0 || thd->is_error());
}
@@ -382,10 +448,23 @@ 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,
- table_list, conds,
+ table_list,
&select_lex->leaf_tables, FALSE,
DELETE_ACL, SELECT_ACL) ||
setup_conds(thd, table_list, select_lex->leaf_tables, conds) ||
@@ -423,7 +502,7 @@ int mysql_prepare_delete(THD *thd, TABLE_LIST *table_list, Item **conds)
extern "C" int refpos_order_cmp(void* arg, const void *a,const void *b)
{
handler *file= (handler*)arg;
- return file->cmp_ref((const byte*)a, (const byte*)b);
+ return file->cmp_ref((const uchar*)a, (const uchar*)b);
}
/*
@@ -453,7 +532,7 @@ int mysql_multi_delete_prepare(THD *thd)
*/
if (setup_tables_and_check_access(thd, &thd->lex->select_lex.context,
&thd->lex->select_lex.top_join_list,
- lex->query_tables, &lex->select_lex.where,
+ lex->query_tables,
&lex->select_lex.leaf_tables, FALSE,
DELETE_ACL, SELECT_ACL))
DBUG_RETURN(TRUE);
@@ -522,7 +601,7 @@ multi_delete::prepare(List<Item> &values, SELECT_LEX_UNIT *u)
DBUG_ENTER("multi_delete::prepare");
unit= u;
do_delete= 1;
- thd->proc_info="deleting from main table";
+ thd_proc_info(thd, "deleting from main table");
DBUG_RETURN(0);
}
@@ -556,25 +635,24 @@ multi_delete::initialize_tables(JOIN *join)
tbl->no_keyread=1;
/* Don't use record cache */
tbl->no_cache= 1;
- tbl->used_keys.clear_all();
+ tbl->covering_keys.clear_all();
if (tbl->file->has_transactions())
transactional_tables= 1;
else
normal_tables= 1;
- if (tbl->triggers)
- {
- tbl->triggers->mark_fields_used(thd, TRG_EVENT_DELETE);
- if (tbl->triggers->has_triggers(TRG_EVENT_DELETE,
+ if (tbl->triggers &&
+ tbl->triggers->has_triggers(TRG_EVENT_DELETE,
TRG_ACTION_AFTER))
- {
- /*
- The table has AFTER DELETE triggers that might access to subject
- table and therefore might need delete to be done immediately.
- So we turn-off the batching.
- */
- (void) tbl->file->extra(HA_EXTRA_DELETE_CANNOT_BATCH);
- }
+ {
+ /*
+ The table has AFTER DELETE triggers that might access to subject
+ table and therefore might need delete to be done immediately.
+ So we turn-off the batching.
+ */
+ (void) tbl->file->extra(HA_EXTRA_DELETE_CANNOT_BATCH);
}
+ tbl->prepare_for_position();
+ tbl->mark_columns_needed_for_delete();
}
else if ((tab->type != JT_SYSTEM && tab->type != JT_CONST) &&
walk == delete_tables)
@@ -614,7 +692,6 @@ multi_delete::~multi_delete()
table_being_deleted= table_being_deleted->next_local)
{
TABLE *table= table_being_deleted->table;
- free_io_cache(table); // Alloced by unique
table->no_keyread=0;
}
@@ -654,7 +731,7 @@ bool multi_delete::send_data(List<Item> &values)
TRG_ACTION_BEFORE, FALSE))
DBUG_RETURN(1);
table->status|= STATUS_DELETED;
- if (!(error=table->file->delete_row(table->record[0])))
+ if (!(error=table->file->ha_delete_row(table->record[0])))
{
deleted++;
if (!table->file->has_transactions())
@@ -691,6 +768,14 @@ void multi_delete::send_error(uint errcode,const char *err)
/* First send error what ever it is ... */
my_message(errcode, err, MYF(0));
+ DBUG_VOID_RETURN;
+}
+
+
+void multi_delete::abort()
+{
+ DBUG_ENTER("multi_delete::abort");
+
/* the error was handled or nothing deleted and no side effects return */
if (error_handled ||
!thd->transaction.stmt.modified_non_trans_table && !deleted)
@@ -706,11 +791,9 @@ void multi_delete::send_error(uint errcode,const char *err)
The same if all tables are transactional, regardless of where we are.
In all other cases do attempt deletes ...
*/
- if ((table_being_deleted == delete_tables &&
- table_being_deleted->table->file->has_transactions()) ||
- !normal_tables)
- ha_rollback_stmt(thd);
- else if (do_delete)
+ if (do_delete && normal_tables &&
+ (table_being_deleted != delete_tables ||
+ !table_being_deleted->table->file->has_transactions()))
{
/*
We have to execute the recorded do_deletes() and write info into the
@@ -729,9 +812,9 @@ void multi_delete::send_error(uint errcode,const char *err)
*/
if (mysql_bin_log.is_open())
{
- Query_log_event qinfo(thd, thd->query, thd->query_length,
- transactional_tables, FALSE);
- mysql_bin_log.write(&qinfo);
+ thd->binlog_query(THD::ROW_QUERY_TYPE,
+ thd->query, thd->query_length,
+ transactional_tables, FALSE);
}
thd->transaction.all.modified_non_trans_table= true;
}
@@ -749,7 +832,8 @@ void multi_delete::send_error(uint errcode,const char *err)
int multi_delete::do_deletes()
{
- int local_error= 0, counter= 0;
+ int local_error= 0, counter= 0, tmp_error;
+ bool will_batch;
DBUG_ENTER("do_deletes");
DBUG_ASSERT(do_delete);
@@ -778,6 +862,7 @@ int multi_delete::do_deletes()
been deleted by foreign key handling
*/
info.ignore_not_found_rows= 1;
+ will_batch= !table->file->start_bulk_delete();
while (!(local_error=info.read_record(&info)) && !thd->killed)
{
if (table->triggers &&
@@ -787,7 +872,7 @@ int multi_delete::do_deletes()
local_error= 1;
break;
}
- if ((local_error=table->file->delete_row(table->record[0])))
+ if ((local_error=table->file->ha_delete_row(table->record[0])))
{
table->file->print_error(local_error,MYF(0));
break;
@@ -801,6 +886,14 @@ int multi_delete::do_deletes()
break;
}
}
+ if (will_batch && (tmp_error= table->file->end_bulk_delete()))
+ {
+ if (!local_error)
+ {
+ local_error= tmp_error;
+ table->file->print_error(local_error,MYF(0));
+ }
+ }
if (last_deleted != deleted && !table->file->has_transactions())
thd->transaction.stmt.modified_non_trans_table= TRUE;
end_read_record(&info);
@@ -823,7 +916,7 @@ int multi_delete::do_deletes()
bool multi_delete::send_eof()
{
THD::killed_state killed_status= THD::NOT_KILLED;
- thd->proc_info="deleting from reference tables";
+ thd_proc_info(thd, "deleting from reference tables");
/* Does deletes for the last n - 1 tables, returns 0 if ok */
int local_error= do_deletes(); // returns 0 if success
@@ -832,7 +925,7 @@ bool multi_delete::send_eof()
local_error= local_error || error;
killed_status= (local_error == 0)? THD::NOT_KILLED : thd->killed;
/* reset used flags */
- thd->proc_info="end";
+ thd_proc_info(thd, "end");
/*
We must invalidate the query cache before binlog writing and
@@ -848,10 +941,13 @@ bool multi_delete::send_eof()
{
if (local_error == 0)
thd->clear_error();
- Query_log_event qinfo(thd, thd->query, thd->query_length,
- transactional_tables, FALSE, killed_status);
- if (mysql_bin_log.write(&qinfo) && !normal_tables)
+ if (thd->binlog_query(THD::ROW_QUERY_TYPE,
+ thd->query, thd->query_length,
+ transactional_tables, FALSE, killed_status) &&
+ !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;
@@ -859,15 +955,10 @@ bool multi_delete::send_eof()
if (local_error != 0)
error_handled= TRUE; // to force early leave from ::send_error()
- /* Commit or rollback the current SQL statement */
- if (transactional_tables)
- if (ha_autocommit_or_rollback(thd,local_error > 0))
- local_error=1;
-
if (!local_error)
{
thd->row_count_func= deleted;
- ::send_ok(thd, deleted);
+ ::my_ok(thd, (ha_rows) thd->row_count_func);
}
return 0;
}
@@ -878,6 +969,26 @@ bool multi_delete::send_eof()
****************************************************************************/
/*
+ 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, save_binlog_row_based= thd->current_stmt_binlog_row_based;
+ DBUG_ENTER("mysql_truncate_by_delete");
+ table_list->lock_type= TL_WRITE;
+ mysql_init_select(thd->lex);
+ thd->clear_current_stmt_binlog_row_based();
+ 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);
+ thd->current_stmt_binlog_row_based= save_binlog_row_based;
+ 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
@@ -893,45 +1004,48 @@ bool mysql_truncate(THD *thd, TABLE_LIST *table_list, bool dont_send_ok)
{
HA_CREATE_INFO create_info;
char path[FN_REFLEN];
- TABLE **table_ptr;
+ TABLE *table;
bool error;
+ uint path_length;
DBUG_ENTER("mysql_truncate");
bzero((char*) &create_info,sizeof(create_info));
/* If it is a temporary table, close and regenerate it */
- if (!dont_send_ok && (table_ptr=find_temporary_table(thd,table_list->db,
- table_list->table_name)))
+ if (!dont_send_ok && (table= find_temporary_table(thd, table_list)))
{
- TABLE *table= *table_ptr;
- table->file->info(HA_STATUS_AUTO | HA_STATUS_NO_LOCK);
- db_type table_type= table->s->db_type;
+ handlerton *table_type= table->s->db_type();
+ TABLE_SHARE *share= table->s;
if (!ha_check_storage_engine_flag(table_type, HTON_CAN_RECREATE))
goto trunc_by_del;
- strmov(path, table->s->path);
- *table_ptr= table->next; // Unlink table from list
- close_temporary(table,0);
- if (thd->slave_thread)
- --slave_open_temp_tables;
- *fn_ext(path)=0; // Remove the .frm extension
- ha_create_table(path, &create_info,1);
+
+ table->file->info(HA_STATUS_AUTO | HA_STATUS_NO_LOCK);
+
+ 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, path, table_list->db,
- table_list->table_name, 1))))
+ if ((error= (int) !(open_temporary_table(thd, share->path.str,
+ share->db.str,
+ share->table_name.str, 1))))
(void) rm_temporary_table(table_type, path);
+ 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 send_ok() to the client.
+ and we will not my_ok() to the client.
*/
goto end;
}
- (void) sprintf(path,"%s/%s/%s%s",mysql_data_home,table_list->db,
- table_list->table_name,reg_ext);
- fn_format(path, path, "", "", MY_UNPACK_FILENAME);
+ path_length= build_table_filename(path, sizeof(path), table_list->db,
+ table_list->table_name, reg_ext, 0);
if (!dont_send_ok)
{
- db_type table_type;
+ enum legacy_db_type table_type;
mysql_frm_type(thd, path, &table_type);
if (table_type == DB_TYPE_UNKNOWN)
{
@@ -939,14 +1053,22 @@ bool mysql_truncate(THD *thd, TABLE_LIST *table_list, bool dont_send_ok)
table_list->db, table_list->table_name);
DBUG_RETURN(TRUE);
}
- if (!ha_check_storage_engine_flag(table_type, HTON_CAN_RECREATE))
+ 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);
}
- *fn_ext(path)=0; // Remove the .frm extension
- error= ha_create_table(path,&create_info,1);
+ // 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:
@@ -954,14 +1076,12 @@ end:
{
if (!error)
{
- if (mysql_bin_log.is_open())
- {
- thd->clear_error();
- Query_log_event qinfo(thd, thd->query, thd->query_length,
- 0, FALSE);
- mysql_bin_log.write(&qinfo);
- }
- send_ok(thd); // This should return record count
+ /*
+ TRUNCATE must always be statement-based binlogged (not row-based) so
+ we don't test current_stmt_binlog_row_based.
+ */
+ write_bin_log(thd, TRUE, thd->query, thd->query_length);
+ my_ok(thd); // This should return record count
}
VOID(pthread_mutex_lock(&LOCK_open));
unlock_table_name(thd, table_list);
@@ -975,16 +1095,7 @@ end:
}
DBUG_RETURN(error);
- trunc_by_del:
- /* Probably InnoDB table */
- ulonglong save_options= thd->options;
- table_list->lock_type= TL_WRITE;
- thd->options&= ~(ulong) (OPTION_BEGIN | OPTION_NOT_AUTOCOMMIT);
- ha_enable_transaction(thd, FALSE);
- mysql_init_select(thd->lex);
- error= mysql_delete(thd, table_list, (COND*) 0, (SQL_LIST*) 0,
- HA_POS_ERROR, LL(0), TRUE);
- ha_enable_transaction(thd, TRUE);
- thd->options= save_options;
+trunc_by_del:
+ error= mysql_truncate_by_delete(thd, table_list);
DBUG_RETURN(error);
}
diff --git a/sql/sql_derived.cc b/sql/sql_derived.cc
index 830d1b7c36f..41be98621a6 100644
--- a/sql/sql_derived.cc
+++ b/sql/sql_derived.cc
@@ -73,29 +73,59 @@ out:
}
-/*
- Create temporary table structure (but do not fill it)
-
- SYNOPSIS
- mysql_derived_prepare()
- thd Thread handle
- lex LEX for this thread
- orig_table_list TABLE_LIST for the upper SELECT
-
- IMPLEMENTATION
- Derived table is resolved with temporary table.
-
- After table creation, the above TABLE_LIST is updated with a new table.
-
- This function is called before any command containing derived table
- is executed.
-
- Derived tables is stored in thd->derived_tables and freed in
- close_thread_tables()
-
- RETURN
- FALSE OK
- TRUE Error
+/**
+ @brief Create temporary table structure (but do not fill it).
+
+ @param thd Thread handle
+ @param lex LEX for this thread
+ @param orig_table_list TABLE_LIST for the upper SELECT
+
+ @details
+
+ This function is called before any command containing derived tables is
+ executed. Currently the function is used for derived tables, i.e.
+
+ - Anonymous derived tables, or
+ - Named derived tables (aka views) with the @c TEMPTABLE algorithm.
+
+ The table reference, contained in @c orig_table_list, is updated with the
+ fields of a new temporary table.
+
+ Derived tables are stored in @c thd->derived_tables and closed by
+ close_thread_tables().
+
+ This function is part of the procedure that starts in
+ open_and_lock_tables(), a procedure that - among other things - introduces
+ new table and table reference objects (to represent derived tables) that
+ don't exist in the privilege database. This means that normal privilege
+ checking cannot handle them. Hence this function does some extra tricks in
+ order to bypass normal privilege checking, by exploiting the fact that the
+ current state of privilege verification is attached as GRANT_INFO structures
+ on the relevant TABLE and TABLE_REF objects.
+
+ For table references, the current state of accrued access is stored inside
+ TABLE_LIST::grant. Hence this function must update the state of fulfilled
+ privileges for the new TABLE_LIST, an operation which is normally performed
+ exclusively by the table and database access checking functions,
+ check_access() and check_grant(), respectively. This modification is done
+ for both views and anonymous derived tables: The @c SELECT privilege is set
+ as fulfilled by the user. However, if a view is referenced and the table
+ reference is queried against directly (see TABLE_LIST::referencing_view),
+ the state of privilege checking (GRANT_INFO struct) is copied as-is to the
+ temporary table.
+
+ This function implements a signature called "derived table processor", and
+ is passed as a function pointer to mysql_handle_derived().
+
+ @note This function sets @c SELECT_ACL for @c TEMPTABLE views as well as
+ anonymous derived tables, but this is ok since later access checking will
+ distinguish between them.
+
+ @see mysql_handle_derived(), mysql_derived_filling(), GRANT_INFO
+
+ @return
+ false OK
+ true Error
*/
bool mysql_derived_prepare(THD *thd, LEX *lex, TABLE_LIST *orig_table_list)
@@ -147,8 +177,9 @@ exit:
/* Hide "Unknown column" or "Unknown function" error */
if (orig_table_list->view)
{
- if (thd->net.last_errno == ER_BAD_FIELD_ERROR ||
- thd->net.last_errno == ER_SP_DOES_NOT_EXIST)
+ if (thd->is_error() &&
+ (thd->main_da.sql_errno() == ER_BAD_FIELD_ERROR ||
+ thd->main_da.sql_errno() == ER_SP_DOES_NOT_EXIST))
{
thd->clear_error();
my_error(ER_VIEW_INVALID, MYF(0), orig_table_list->db,
@@ -176,8 +207,8 @@ exit:
}
orig_table_list->derived_result= derived_result;
orig_table_list->table= table;
- orig_table_list->table_name= (char*) table->s->table_name;
- orig_table_list->table_name_length= (uint) strlen((char*)table->s->table_name);
+ orig_table_list->table_name= table->s->table_name.str;
+ orig_table_list->table_name_length= table->s->table_name.length;
table->derived_select_number= first_select->select_number;
table->s->tmp_table= NON_TRANSACTIONAL_TMP_TABLE;
#ifndef NO_EMBEDDED_ACCESS_CHECKS
@@ -236,9 +267,7 @@ bool mysql_derived_filling(THD *thd, LEX *lex, TABLE_LIST *orig_table_list)
SELECT_LEX *first_select= unit->first_select();
select_union *derived_result= orig_table_list->derived_result;
SELECT_LEX *save_current_select= lex->current_select;
- bool is_union= first_select->next_select() &&
- first_select->next_select()->linkage == UNION_TYPE;
- if (is_union)
+ if (unit->is_union())
{
// execute union without clean up
res= unit->exec();
diff --git a/sql/sql_do.cc b/sql/sql_do.cc
index 2330339db8e..8406a9eaf45 100644
--- a/sql/sql_do.cc
+++ b/sql/sql_do.cc
@@ -23,12 +23,22 @@ bool mysql_do(THD *thd, List<Item> &values)
List_iterator<Item> li(values);
Item *value;
DBUG_ENTER("mysql_do");
- if (setup_fields(thd, 0, values, 0, 0, 0))
+ if (setup_fields(thd, 0, values, MARK_COLUMNS_NONE, 0, 0))
DBUG_RETURN(TRUE);
while ((value = li++))
value->val_int();
free_underlaid_joins(thd, &thd->lex->select_lex);
- thd->clear_error(); // DO always is OK
- send_ok(thd);
+
+ if (thd->is_error())
+ {
+ /*
+ Rollback the effect of the statement, since next instruction
+ will clear the error and the rollback in the end of
+ dispatch_command() won't work.
+ */
+ ha_autocommit_or_rollback(thd, thd->is_error());
+ thd->clear_error(); // DO always is OK
+ }
+ my_ok(thd);
DBUG_RETURN(FALSE);
}
diff --git a/sql/sql_error.cc b/sql/sql_error.cc
index 8f825f83df1..9ea7facbe41 100644
--- a/sql/sql_error.cc
+++ b/sql/sql_error.cc
@@ -109,6 +109,9 @@ MYSQL_ERROR *push_warning(THD *thd, MYSQL_ERROR::enum_warning_level level,
DBUG_ENTER("push_warning");
DBUG_PRINT("enter", ("code: %d, msg: %s", code, msg));
+ DBUG_ASSERT(code != 0);
+ DBUG_ASSERT(msg != NULL);
+
if (level == MYSQL_ERROR::WARN_LEVEL_NOTE &&
!(thd->options & OPTION_SQL_NOTES))
DBUG_RETURN(0);
@@ -137,6 +140,9 @@ MYSQL_ERROR *push_warning(THD *thd, MYSQL_ERROR::enum_warning_level level,
level= MYSQL_ERROR::WARN_LEVEL_ERROR;
}
+ if (thd->handle_error(code, msg, level))
+ DBUG_RETURN(NULL);
+
if (thd->spcont &&
thd->spcont->handle_error(code, level, thd))
{
@@ -147,15 +153,9 @@ MYSQL_ERROR *push_warning(THD *thd, MYSQL_ERROR::enum_warning_level level,
if (thd->warn_list.elements < thd->variables.max_error_count)
{
- /*
- The following code is here to change the allocation to not
- use the thd->mem_root, which is freed after each query
- */
- MEM_ROOT *old_root= thd->mem_root;
- thd->mem_root= &thd->warn_root;
- if ((err= new MYSQL_ERROR(thd, code, level, msg)))
- thd->warn_list.push_back(err);
- thd->mem_root= old_root;
+ /* 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);
}
thd->warn_count[(uint) level]++;
thd->total_warn_count++;
@@ -177,10 +177,13 @@ void push_warning_printf(THD *thd, MYSQL_ERROR::enum_warning_level level,
uint code, const char *format, ...)
{
va_list args;
- char warning[ERRMSGSIZE+20];
+ char warning[MYSQL_ERRMSG_SIZE];
DBUG_ENTER("push_warning_printf");
DBUG_PRINT("enter",("warning: %u", code));
-
+
+ DBUG_ASSERT(code != 0);
+ DBUG_ASSERT(format != NULL);
+
va_start(args,format);
my_vsnprintf(warning, sizeof(warning), format, args);
va_end(args);
@@ -205,8 +208,13 @@ void push_warning_printf(THD *thd, MYSQL_ERROR::enum_warning_level level,
TRUE Error sending data to client
*/
-const char *warning_level_names[]= {"Note", "Warning", "Error", "?"};
-int warning_level_length[]= { 4, 7, 5, 1 };
+const LEX_STRING warning_level_names[]=
+{
+ { C_STRING_WITH_LEN("Note") },
+ { C_STRING_WITH_LEN("Warning") },
+ { C_STRING_WITH_LEN("Error") },
+ { C_STRING_WITH_LEN("?") }
+};
bool mysqld_show_warnings(THD *thd, ulong levels_to_show)
{
@@ -240,13 +248,13 @@ bool mysqld_show_warnings(THD *thd, ulong levels_to_show)
if (idx > unit->select_limit_cnt)
break;
protocol->prepare_for_resend();
- protocol->store(warning_level_names[err->level],
- warning_level_length[err->level], system_charset_info);
+ 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);
if (protocol->write())
DBUG_RETURN(TRUE);
}
- send_eof(thd);
+ my_eof(thd);
DBUG_RETURN(FALSE);
}
diff --git a/sql/sql_error.h b/sql/sql_error.h
index 4dbf3ada8f0..f98264dce50 100644
--- a/sql/sql_error.h
+++ b/sql/sql_error.h
@@ -39,5 +39,5 @@ 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);
-extern const char *warning_level_names[];
-extern int warning_level_length[];
+
+extern const LEX_STRING warning_level_names[];
diff --git a/sql/sql_handler.cc b/sql/sql_handler.cc
index 67f9992499b..28a9fb5c78e 100644
--- a/sql/sql_handler.cc
+++ b/sql/sql_handler.cc
@@ -65,9 +65,6 @@
static enum enum_ha_read_modes rkey_to_rnext[]=
{ RNEXT_SAME, RNEXT, RPREV, RNEXT, RPREV, RNEXT, RPREV, RPREV };
-static int mysql_ha_flush_table(THD *thd, TABLE **table_ptr, uint mode_flags);
-
-
/*
Get hash key and hash key length.
@@ -87,10 +84,10 @@ static int mysql_ha_flush_table(THD *thd, TABLE **table_ptr, uint mode_flags);
Pointer to the TABLE_LIST struct.
*/
-static char *mysql_ha_hash_get_key(TABLE_LIST *tables, uint *key_len_p,
+static char *mysql_ha_hash_get_key(TABLE_LIST *tables, size_t *key_len_p,
my_bool first __attribute__((unused)))
{
- *key_len_p= (uint) strlen(tables->alias) + 1 ; /* include '\0' in comparisons */
+ *key_len_p= strlen(tables->alias) + 1 ; /* include '\0' in comparisons */
return tables->alias;
}
@@ -119,13 +116,15 @@ static void mysql_ha_hash_free(TABLE_LIST *tables)
@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.
- @note Broadcasts refresh if it closed the table.
+ @note Broadcasts refresh if it closed a table with old version.
*/
-static void mysql_ha_close_table(THD *thd, TABLE_LIST *tables)
+static void mysql_ha_close_table(THD *thd, TABLE_LIST *tables,
+ bool is_locked)
{
TABLE **table_ptr;
@@ -143,13 +142,23 @@ static void mysql_ha_close_table(THD *thd, TABLE_LIST *tables)
if (*table_ptr)
{
(*table_ptr)->file->ha_index_or_rnd_end();
- VOID(pthread_mutex_lock(&LOCK_open));
+ 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();
}
- VOID(pthread_mutex_unlock(&LOCK_open));
+ if (! is_locked)
+ VOID(pthread_mutex_unlock(&LOCK_open));
+ }
+ else if (tables->table)
+ {
+ /* Must be a temporary table */
+ TABLE *table= tables->table;
+ table->file->ha_index_or_rnd_end();
+ table->query_id= thd->query_id;
+ table->open_by_handler= 0;
}
}
@@ -201,8 +210,8 @@ bool mysql_ha_open(THD *thd, TABLE_LIST *tables, bool reopen)
}
else if (! reopen) /* Otherwise we have 'tables' already. */
{
- if (hash_search(&thd->handler_tables_hash, (byte*) tables->alias,
- (uint) strlen(tables->alias) + 1))
+ if (hash_search(&thd->handler_tables_hash, (uchar*) tables->alias,
+ strlen(tables->alias) + 1))
{
DBUG_PRINT("info",("duplicate '%s'", tables->alias));
if (! reopen)
@@ -235,7 +244,6 @@ bool mysql_ha_open(THD *thd, TABLE_LIST *tables, bool reopen)
/* for now HANDLER can be used only for real TABLES */
tables->required_type= FRMTYPE_TABLE;
error= open_tables(thd, &tables, &counter, 0);
-
/* restore the state and merge the opened table into handler_tables list */
if (thd->open_tables)
{
@@ -249,7 +257,7 @@ bool mysql_ha_open(THD *thd, TABLE_LIST *tables, bool reopen)
goto err;
/* There can be only one table in '*tables'. */
- if (! (tables->table->file->table_flags() & HA_CAN_SQL_HANDLER))
+ if (! (tables->table->file->ha_table_flags() & HA_CAN_SQL_HANDLER))
{
if (! reopen)
my_error(ER_ILLEGAL_HA, MYF(0), tables->alias);
@@ -259,14 +267,14 @@ bool mysql_ha_open(THD *thd, TABLE_LIST *tables, bool reopen)
if (! reopen)
{
/* copy the TABLE_LIST struct */
- dblen= (uint) strlen(tables->db) + 1;
- namelen= (uint) strlen(tables->table_name) + 1;
- aliaslen= (uint) strlen(tables->alias) + 1;
+ dblen= strlen(tables->db) + 1;
+ namelen= strlen(tables->table_name) + 1;
+ aliaslen= strlen(tables->alias) + 1;
if (!(my_multi_malloc(MYF(MY_WME),
- &hash_tables, sizeof(*hash_tables),
- &db, dblen,
- &name, namelen,
- &alias, aliaslen,
+ &hash_tables, (uint) sizeof(*hash_tables),
+ &db, (uint) dblen,
+ &name, (uint) namelen,
+ &alias, (uint) aliaslen,
NullS)))
goto err;
/* structure copy */
@@ -279,12 +287,18 @@ bool mysql_ha_open(THD *thd, TABLE_LIST *tables, bool reopen)
memcpy(hash_tables->alias, tables->alias, aliaslen);
/* add to hash */
- if (my_hash_insert(&thd->handler_tables_hash, (byte*) hash_tables))
+ if (my_hash_insert(&thd->handler_tables_hash, (uchar*) hash_tables))
goto err;
}
+ /*
+ 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.
+ */
+ tables->table->open_by_handler= 1;
+
if (! reopen)
- send_ok(thd);
+ my_ok(thd);
DBUG_PRINT("exit",("OK"));
DBUG_RETURN(FALSE);
@@ -292,7 +306,7 @@ err:
if (hash_tables)
my_free((char*) hash_tables, MYF(0));
if (tables->table)
- mysql_ha_close_table(thd, tables);
+ mysql_ha_close_table(thd, tables, FALSE);
DBUG_PRINT("exit",("ERROR"));
DBUG_RETURN(TRUE);
}
@@ -323,11 +337,11 @@ bool mysql_ha_close(THD *thd, TABLE_LIST *tables)
tables->db, tables->table_name, tables->alias));
if ((hash_tables= (TABLE_LIST*) hash_search(&thd->handler_tables_hash,
- (byte*) tables->alias,
- (uint) strlen(tables->alias) + 1)))
+ (uchar*) tables->alias,
+ strlen(tables->alias) + 1)))
{
- mysql_ha_close_table(thd, hash_tables);
- hash_delete(&thd->handler_tables_hash, (byte*) hash_tables);
+ mysql_ha_close_table(thd, hash_tables, FALSE);
+ hash_delete(&thd->handler_tables_hash, (uchar*) hash_tables);
}
else
{
@@ -336,7 +350,7 @@ bool mysql_ha_close(THD *thd, TABLE_LIST *tables)
DBUG_RETURN(TRUE);
}
- send_ok(thd);
+ my_ok(thd);
DBUG_PRINT("exit", ("OK"));
DBUG_RETURN(FALSE);
}
@@ -377,7 +391,7 @@ bool mysql_ha_read(THD *thd, TABLE_LIST *tables,
String buffer(buff, sizeof(buff), system_charset_info);
int error, keyno= -1;
uint num_rows;
- byte *key;
+ uchar *key;
uint key_len;
bool need_reopen;
DBUG_ENTER("mysql_ha_read");
@@ -395,13 +409,13 @@ bool mysql_ha_read(THD *thd, TABLE_LIST *tables,
retry:
if ((hash_tables= (TABLE_LIST*) hash_search(&thd->handler_tables_hash,
- (byte*) tables->alias,
- (uint) strlen(tables->alias) + 1)))
+ (uchar*) tables->alias,
+ strlen(tables->alias) + 1)))
{
table= hash_tables->table;
- DBUG_PRINT("info-in-hash",("'%s'.'%s' as '%s' tab %p",
+ DBUG_PRINT("info-in-hash",("'%s'.'%s' as '%s' table: 0x%lx",
hash_tables->db, hash_tables->table_name,
- hash_tables->alias, table));
+ hash_tables->alias, (long) table));
if (!table)
{
/*
@@ -435,7 +449,8 @@ retry:
#if MYSQL_VERSION_ID < 40100
char buff[MAX_DBKEY_LENGTH];
if (*tables->db)
- strxnmov(buff, sizeof(buff), tables->db, ".", tables->table_name, NullS);
+ strxnmov(buff, sizeof(buff)-1, tables->db, ".", tables->table_name,
+ NullS);
else
strncpy(buff, tables->alias, sizeof(buff));
my_error(ER_UNKNOWN_TABLE, MYF(0), buff, "HANDLER");
@@ -464,7 +479,7 @@ retry:
if (need_reopen)
{
- mysql_ha_close_table(thd, tables);
+ mysql_ha_close_table(thd, tables, FALSE);
hash_tables->table= NULL;
/*
The lock might have been aborted, we need to manually reset
@@ -478,6 +493,9 @@ retry:
if (!lock)
goto err0; // mysql_lock_tables() printed error message already
+ // Always read all columns
+ tables->table->read_set= &tables->table->s->all_set;
+
if (cond)
{
if (table->query_id != thd->query_id)
@@ -526,7 +544,7 @@ retry:
if (keyname)
{
table->file->ha_index_or_rnd_end();
- table->file->ha_index_init(keyno);
+ table->file->ha_index_init(keyno, 1);
error= table->file->index_first(table->record[0]);
}
else
@@ -548,7 +566,7 @@ retry:
case RLAST:
DBUG_ASSERT(keyname != 0);
table->file->ha_index_or_rnd_end();
- table->file->ha_index_init(keyno);
+ table->file->ha_index_init(keyno, 1);
error= table->file->index_last(table->record[0]);
mode=RPREV;
break;
@@ -569,8 +587,10 @@ retry:
}
List_iterator<Item> it_ke(*key_expr);
Item *item;
- for (key_len=0 ; (item=it_ke++) ; key_part++)
+ key_part_map keypart_map;
+ for (keypart_map= key_len=0 ; (item=it_ke++) ; key_part++)
{
+ my_bitmap_map *old_map;
// 'item' can be changed by fix_fields() call
if ((!item->fixed &&
item->fix_fields(thd, it_ke.ref())) ||
@@ -581,16 +601,20 @@ retry:
my_error(ER_WRONG_ARGUMENTS,MYF(0),"HANDLER ... READ");
goto err;
}
+ old_map= dbug_tmp_use_all_columns(table, table->write_set);
(void) item->save_in_field(key_part->field, 1);
+ dbug_tmp_restore_column_map(table->write_set, old_map);
key_len+=key_part->store_length;
+ keypart_map= (keypart_map << 1) | 1;
}
- if (!(key= (byte*) thd->calloc(ALIGN_SIZE(key_len))))
+
+ if (!(key= (uchar*) thd->calloc(ALIGN_SIZE(key_len))))
goto err;
table->file->ha_index_or_rnd_end();
- table->file->ha_index_init(keyno);
+ table->file->ha_index_init(keyno, 1);
key_copy(key, table->record[0], table->key_info + keyno, key_len);
- error= table->file->index_read(table->record[0],
- key,key_len,ha_rkey_mode);
+ error= table->file->index_read_map(table->record[0],
+ key, keypart_map, ha_rkey_mode);
mode=rkey_to_rnext[(int)ha_rkey_mode];
break;
}
@@ -634,7 +658,7 @@ retry:
}
ok:
mysql_unlock_tables(thd,lock);
- send_eof(thd);
+ my_eof(thd);
DBUG_PRINT("exit",("OK"));
DBUG_RETURN(FALSE);
@@ -646,161 +670,130 @@ err0:
}
-/*
- Flush (close) a list of HANDLER tables.
-
- SYNOPSIS
- mysql_ha_flush()
- thd Thread identifier.
- tables The list of tables to close. If NULL,
- close all HANDLER tables [marked as flushed].
- mode_flags MYSQL_HA_CLOSE_FINAL finally close the table.
- MYSQL_HA_REOPEN_ON_USAGE mark for reopen.
- MYSQL_HA_FLUSH_ALL flush all tables, not only
- those marked for flush.
- is_locked If LOCK_open is locked.
-
- DESCRIPTION
- The list of HANDLER tables may be NULL, in which case all HANDLER
- tables are closed (if MYSQL_HA_FLUSH_ALL) is set.
- If 'tables' is NULL and MYSQL_HA_FLUSH_ALL is not set,
- all HANDLER tables marked for flush are closed.
- Broadcasts refresh for every table closed.
+/**
+ Scan the handler tables hash for matching tables.
- NOTE
- Since mysql_ha_flush() is called when the base table has to be closed,
- we compare real table names, not aliases. Hence, database names matter.
+ @param thd Thread identifier.
+ @param tables The list of tables to remove.
- RETURN
- 0 ok
+ @return Pointer to head of linked list (TABLE_LIST::next_local) of matching
+ TABLE_LIST elements from handler_tables_hash. Otherwise, NULL if no
+ table was matched.
*/
-int mysql_ha_flush(THD *thd, TABLE_LIST *tables, uint mode_flags,
- bool is_locked)
+static TABLE_LIST *mysql_ha_find(THD *thd, TABLE_LIST *tables)
{
- TABLE_LIST *tmp_tables;
- TABLE **table_ptr;
- bool did_lock= FALSE;
- DBUG_ENTER("mysql_ha_flush");
- DBUG_PRINT("enter", ("tables: %p mode_flags: 0x%02x", tables, mode_flags));
+ TABLE_LIST *hash_tables, *head= NULL, *first= tables;
+ DBUG_ENTER("mysql_ha_find");
- if (tables)
+ /* search for all handlers with matching table names */
+ for (uint i= 0; i < thd->handler_tables_hash.records; i++)
{
- /* Close all tables in the list. */
- for (tmp_tables= tables ; tmp_tables; tmp_tables= tmp_tables->next_local)
+ hash_tables= (TABLE_LIST*) hash_element(&thd->handler_tables_hash, i);
+ for (tables= first; tables; tables= tables->next_local)
{
- DBUG_PRINT("info-in-tables-list",("'%s'.'%s' as '%s'",
- tmp_tables->db, tmp_tables->table_name,
- tmp_tables->alias));
- /* Close all currently open handler tables with the same base table. */
- table_ptr= &(thd->handler_tables);
- while (*table_ptr)
- {
- if ((!*tmp_tables->db ||
- !my_strcasecmp(&my_charset_latin1, (*table_ptr)->s->db,
- tmp_tables->db)) &&
- ! my_strcasecmp(&my_charset_latin1, (*table_ptr)->s->table_name,
- tmp_tables->table_name))
- {
- DBUG_PRINT("info",("*table_ptr '%s'.'%s' as '%s'",
- (*table_ptr)->s->db,
- (*table_ptr)->s->table_name,
- (*table_ptr)->alias));
- /* The first time it is required, lock for close_thread_table(). */
- if (! did_lock && ! is_locked)
- {
- VOID(pthread_mutex_lock(&LOCK_open));
- did_lock= TRUE;
- }
- mysql_ha_flush_table(thd, table_ptr, mode_flags);
- continue;
- }
- table_ptr= &(*table_ptr)->next;
- }
- /* end of handler_tables list */
+ if ((! *tables->db ||
+ ! my_strcasecmp(&my_charset_latin1, hash_tables->db, tables->db)) &&
+ ! my_strcasecmp(&my_charset_latin1, hash_tables->table_name,
+ tables->table_name))
+ break;
}
- /* end of flush tables list */
- }
- else
- {
- /* Close all currently open tables [which are marked for flush]. */
- table_ptr= &(thd->handler_tables);
- while (*table_ptr)
+ if (tables)
{
- if ((mode_flags & MYSQL_HA_FLUSH_ALL) ||
- (*table_ptr)->needs_reopen_or_name_lock())
- {
- /* The first time it is required, lock for close_thread_table(). */
- if (! did_lock && ! is_locked)
- {
- VOID(pthread_mutex_lock(&LOCK_open));
- did_lock= TRUE;
- }
- mysql_ha_flush_table(thd, table_ptr, mode_flags);
- continue;
- }
- table_ptr= &(*table_ptr)->next;
+ hash_tables->next_local= head;
+ head= hash_tables;
}
}
- /* Release the lock if it was taken by this function. */
- if (did_lock)
- VOID(pthread_mutex_unlock(&LOCK_open));
+ DBUG_RETURN(head);
+}
+
+
+/**
+ Remove matching tables from the HANDLER's hash table.
+
+ @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)
+{
+ TABLE_LIST *hash_tables, *next;
+ DBUG_ENTER("mysql_ha_rm_tables");
+
+ DBUG_ASSERT(tables);
+
+ hash_tables= mysql_ha_find(thd, tables);
- DBUG_RETURN(0);
+ while (hash_tables)
+ {
+ next= hash_tables->next_local;
+ if (hash_tables->table)
+ mysql_ha_close_table(thd, hash_tables, is_locked);
+ hash_delete(&thd->handler_tables_hash, (uchar*) hash_tables);
+ hash_tables= next;
+ }
+
+ DBUG_VOID_RETURN;
}
-/*
- Flush (close) a table.
- SYNOPSIS
- mysql_ha_flush_table()
- thd Thread identifier.
- table The table to close.
- mode_flags MYSQL_HA_CLOSE_FINAL finally close the table.
- MYSQL_HA_REOPEN_ON_USAGE mark for reopen.
+/**
+ Flush (close and mark for re-open) all tables that should be should
+ be reopen.
- DESCRIPTION
- Broadcasts refresh if it closed the table.
- The caller must lock LOCK_open.
+ @param thd Thread identifier.
- RETURN
- 0 ok
+ @note Broadcasts refresh if it closed a table with old version.
*/
-static int mysql_ha_flush_table(THD *thd, TABLE **table_ptr, uint mode_flags)
+void mysql_ha_flush(THD *thd)
{
- TABLE_LIST *hash_tables;
- TABLE *table= *table_ptr;
- DBUG_ENTER("mysql_ha_flush_table");
- DBUG_PRINT("enter",("'%s'.'%s' as '%s' flags: 0x%02x",
- table->s->db, table->s->table_name,
- table->alias, mode_flags));
+ TABLE_LIST *hash_tables;
+ DBUG_ENTER("mysql_ha_flush");
- if ((hash_tables= (TABLE_LIST*) hash_search(&thd->handler_tables_hash,
- (byte*) table->alias,
- (uint) strlen(table->alias) + 1)))
+ safe_mutex_assert_owner(&LOCK_open);
+
+ for (uint i= 0; i < thd->handler_tables_hash.records; i++)
{
- if (! (mode_flags & MYSQL_HA_REOPEN_ON_USAGE))
- {
- /* This is a final close. Remove from hash. */
- hash_delete(&thd->handler_tables_hash, (byte*) hash_tables);
- }
- else
+ hash_tables= (TABLE_LIST*) hash_element(&thd->handler_tables_hash, i);
+ if (hash_tables->table && hash_tables->table->needs_reopen_or_name_lock())
{
+ mysql_ha_close_table(thd, hash_tables, TRUE);
/* Mark table as closed, ready for re-open. */
hash_tables->table= NULL;
}
- }
+ }
- safe_mutex_assert_owner(&LOCK_open);
- (*table_ptr)->file->ha_index_or_rnd_end();
- safe_mutex_assert_owner(&LOCK_open);
- if (close_thread_table(thd, table_ptr))
+ DBUG_VOID_RETURN;
+}
+
+
+/**
+ Close all HANDLER's tables.
+
+ @param thd Thread identifier.
+
+ @note Broadcasts refresh if it closed a table with old version.
+*/
+
+void mysql_ha_cleanup(THD *thd)
+{
+ TABLE_LIST *hash_tables;
+ DBUG_ENTER("mysql_ha_cleanup");
+
+ for (uint i= 0; i < thd->handler_tables_hash.records; i++)
{
- /* Tell threads waiting for refresh that something has happened */
- broadcast_refresh();
- }
+ hash_tables= (TABLE_LIST*) hash_element(&thd->handler_tables_hash, i);
+ if (hash_tables->table)
+ mysql_ha_close_table(thd, hash_tables, FALSE);
+ }
- DBUG_RETURN(0);
+ hash_free(&thd->handler_tables_hash);
+
+ DBUG_VOID_RETURN;
}
+
diff --git a/sql/sql_help.cc b/sql/sql_help.cc
index e369a72fa9f..f51ad318568 100644
--- a/sql/sql_help.cc
+++ b/sql/sql_help.cc
@@ -93,6 +93,11 @@ static bool init_fields(THD *thd, TABLE_LIST *tables,
0, REPORT_ALL_ERRORS, 1,
TRUE)))
DBUG_RETURN(1);
+ bitmap_set_bit(find_fields->field->table->read_set,
+ find_fields->field->field_index);
+ /* To make life easier when setting values in keys */
+ bitmap_set_bit(find_fields->field->table->write_set,
+ find_fields->field->field_index);
}
DBUG_RETURN(0);
}
@@ -267,11 +272,10 @@ int get_topics_for_keyword(THD *thd, TABLE *topics, TABLE *relations,
List<String> *names,
String *name, String *description, String *example)
{
- char buff[8]; // Max int length
+ uchar buff[8]; // Max int length
int count= 0;
int iindex_topic, iindex_relations;
Field *rtopic_id, *rkey_id;
-
DBUG_ENTER("get_topics_for_keyword");
if ((iindex_topic= find_type((char*) primary_key_name,
@@ -285,27 +289,27 @@ 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);
- relations->file->ha_index_init(iindex_relations);
+ topics->file->ha_index_init(iindex_topic,1);
+ relations->file->ha_index_init(iindex_relations,1);
rkey_id->store((longlong) key_id, TRUE);
rkey_id->get_key_image(buff, rkey_id->pack_length(), Field::itRAW);
- int key_res= relations->file->index_read(relations->record[0],
- (byte *)buff, rkey_id->pack_length(),
- HA_READ_KEY_EXACT);
+ int key_res= relations->file->index_read_map(relations->record[0],
+ buff, (key_part_map) 1,
+ HA_READ_KEY_EXACT);
for ( ;
!key_res && key_id == (int16) rkey_id->val_int() ;
key_res= relations->file->index_next(relations->record[0]))
{
- char topic_id_buff[8];
+ uchar topic_id_buff[8];
longlong topic_id= rtopic_id->val_int();
Field *field= find_fields[help_topic_help_topic_id].field;
field->store((longlong) topic_id, TRUE);
field->get_key_image(topic_id_buff, field->pack_length(), Field::itRAW);
- if (!topics->file->index_read(topics->record[0], (byte *)topic_id_buff,
- field->pack_length(), HA_READ_KEY_EXACT))
+ if (!topics->file->index_read_map(topics->record[0], topic_id_buff,
+ (key_part_map)1, HA_READ_KEY_EXACT))
{
memorize_variant_topic(thd,topics,count,find_fields,
names,name,description,example);
@@ -562,7 +566,7 @@ SQL_SELECT *prepare_simple_select(THD *thd, Item *cond,
cond->fix_fields(thd, &cond); // can never fail
/* Assume that no indexes cover all required fields */
- table->used_keys.clear_all();
+ table->covering_keys.clear_all();
SQL_SELECT *res= make_select(table, 0, 0, cond, 0, error);
if (*error || (res && res->check_quick(thd, 0, HA_POS_ERROR)) ||
@@ -628,12 +632,12 @@ bool mysqld_help(THD *thd, const char *mask)
List<String> topics_list, categories_list, subcategories_list;
String name, description, example;
int count_topics, count_categories, error;
- size_t mlen= strlen(mask);
+ uint mlen= strlen(mask);
size_t i;
MEM_ROOT *mem_root= thd->mem_root;
DBUG_ENTER("mysqld_help");
- bzero((gptr)tables,sizeof(tables));
+ bzero((uchar*)tables,sizeof(tables));
tables[0].alias= tables[0].table_name= (char*) "help_topic";
tables[0].lock_type= TL_READ;
tables[0].next_global= tables[0].next_local=
@@ -650,17 +654,20 @@ bool mysqld_help(THD *thd, const char *mask)
tables[3].lock_type= TL_READ;
tables[0].db= tables[1].db= tables[2].db= tables[3].db= (char*) "mysql";
- if (open_and_lock_tables(thd, tables))
- goto error;
+ Open_tables_state open_tables_state_backup;
+ if (open_system_tables_for_read(thd, tables, &open_tables_state_backup))
+ goto error2;
+
/*
Init tables and fields to be usable from items
tables do not contain VIEWs => we can pass 0 as conds
*/
- thd->lex->select_lex.context.table_list=
+ thd->lex->select_lex.context.table_list=
thd->lex->select_lex.context.first_name_resolution_table= &tables[0];
- setup_tables(thd, &thd->lex->select_lex.context,
- &thd->lex->select_lex.top_join_list,
- tables, 0, &leaves, FALSE);
+ if (setup_tables(thd, &thd->lex->select_lex.context,
+ &thd->lex->select_lex.top_join_list,
+ tables, &leaves, FALSE))
+ goto error;
memcpy((char*) used_fields, (char*) init_used_fields, sizeof(used_fields));
if (init_fields(thd, tables, used_fields, array_elements(used_fields)))
goto error;
@@ -668,7 +675,7 @@ bool mysqld_help(THD *thd, const char *mask)
tables[i].table->file->init_table_handle_for_HANDLER();
if (!(select=
- prepare_select_for_name(thd,mask,(uint) mlen,tables,tables[0].table,
+ prepare_select_for_name(thd,mask,mlen,tables,tables[0].table,
used_fields[help_topic_name].field,&error)))
goto error;
@@ -681,11 +688,13 @@ bool mysqld_help(THD *thd, const char *mask)
{
int key_id;
if (!(select=
- prepare_select_for_name(thd,mask,(uint) mlen,tables,tables[3].table,
- used_fields[help_keyword_name].field,&error)))
+ prepare_select_for_name(thd,mask,mlen,tables,tables[3].table,
+ used_fields[help_keyword_name].field,
+ &error)))
goto error;
- count_topics=search_keyword(thd,tables[3].table,used_fields,select,&key_id);
+ count_topics= search_keyword(thd,tables[3].table, used_fields, select,
+ &key_id);
delete select;
count_topics= (count_topics != 1) ? 0 :
get_topics_for_keyword(thd,tables[0].table,tables[2].table,
@@ -698,8 +707,9 @@ bool mysqld_help(THD *thd, const char *mask)
int16 category_id;
Field *cat_cat_id= used_fields[help_category_parent_category_id].field;
if (!(select=
- prepare_select_for_name(thd,mask,(uint) mlen,tables,tables[1].table,
- used_fields[help_category_name].field,&error)))
+ prepare_select_for_name(thd,mask,mlen,tables,tables[1].table,
+ used_fields[help_category_name].field,
+ &error)))
goto error;
count_categories= search_categories(thd, tables[1].table, used_fields,
@@ -759,7 +769,7 @@ bool mysqld_help(THD *thd, const char *mask)
send_variant_2_list(mem_root,protocol, &topics_list, "N", 0))
goto error;
if (!(select=
- prepare_select_for_name(thd,mask,(uint) mlen,tables,tables[1].table,
+ prepare_select_for_name(thd,mask,mlen,tables,tables[1].table,
used_fields[help_category_name].field,&error)))
goto error;
search_categories(thd, tables[1].table, used_fields,
@@ -769,10 +779,15 @@ bool mysqld_help(THD *thd, const char *mask)
if (send_variant_2_list(mem_root,protocol, &categories_list, "Y", 0))
goto error;
}
- send_eof(thd);
+ my_eof(thd);
+ close_system_tables(thd, &open_tables_state_backup);
DBUG_RETURN(FALSE);
+
error:
+ close_system_tables(thd, &open_tables_state_backup);
+
+error2:
DBUG_RETURN(TRUE);
}
diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc
index 2fe0a30f519..84f0f2bd0d3 100644
--- a/sql/sql_insert.cc
+++ b/sql/sql_insert.cc
@@ -1,4 +1,4 @@
-/* Copyright (C) 2000-2006 MySQL AB
+/* Copyright 2000-2008 MySQL AB, 2008 Sun Microsystems, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -58,12 +58,14 @@
#include "sp_head.h"
#include "sql_trigger.h"
#include "sql_select.h"
+#include "sql_show.h"
#include "slave.h"
+#include "rpl_mi.h"
#ifndef EMBEDDED_LIBRARY
static bool delayed_get_table(THD *thd, TABLE_LIST *table_list);
-static int write_delayed(THD *thd,TABLE *table, enum_duplicates dup, bool ignore,
- char *query, uint query_length, bool log_on);
+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);
pthread_handler_t handle_delayed_insert(void *arg);
static void unlink_blobs(register TABLE *table);
@@ -187,16 +189,18 @@ static int check_insert_fields(THD *thd, TABLE_LIST *table_list,
return -1;
}
#ifndef NO_EMBEDDED_ACCESS_CHECKS
- if (grant_option)
- {
- Field_iterator_table_ref field_it;
- field_it.set(table_list);
- if (check_grant_all_columns(thd, INSERT_ACL, &field_it))
- return -1;
- }
+ Field_iterator_table_ref field_it;
+ field_it.set(table_list);
+ if (check_grant_all_columns(thd, INSERT_ACL, &field_it))
+ return -1;
#endif
clear_timestamp_auto_bits(table->timestamp_field_type,
TIMESTAMP_AUTO_SET_ON_INSERT);
+ /*
+ No fields are provided so all fields must be provided in the values.
+ Thus we set all bits in the write set.
+ */
+ bitmap_set_all(table->write_set);
}
else
{ // Part field list
@@ -211,7 +215,7 @@ static int check_insert_fields(THD *thd, TABLE_LIST *table_list,
return -1;
}
- thd->dupp_field=0;
+ thd->dup_field= 0;
select_lex->no_wrap_view_item= TRUE;
/* Save the state of the current name resolution context. */
@@ -223,7 +227,7 @@ static int check_insert_fields(THD *thd, TABLE_LIST *table_list,
*/
table_list->next_local= 0;
context->resolve_in_table_list_only(table_list);
- res= setup_fields(thd, 0, fields, 1, 0, 0);
+ res= setup_fields(thd, 0, fields, MARK_COLUMNS_WRITE, 0, 0);
/* Restore the current context. */
ctx_state.restore_state(context, table_list);
@@ -239,15 +243,23 @@ static int check_insert_fields(THD *thd, TABLE_LIST *table_list,
table= table_list->table;
}
- if (check_unique && thd->dupp_field)
+ if (check_unique && thd->dup_field)
{
- my_error(ER_FIELD_SPECIFIED_TWICE, MYF(0), thd->dupp_field->field_name);
+ my_error(ER_FIELD_SPECIFIED_TWICE, MYF(0), thd->dup_field->field_name);
return -1;
}
- if (table->timestamp_field && // Don't set timestamp if used
- table->timestamp_field->query_id == thd->query_id)
- clear_timestamp_auto_bits(table->timestamp_field_type,
- TIMESTAMP_AUTO_SET_ON_INSERT);
+ if (table->timestamp_field) // Don't automaticly set timestamp if used
+ {
+ if (bitmap_is_set(table->write_set,
+ table->timestamp_field->field_index))
+ clear_timestamp_auto_bits(table->timestamp_field_type,
+ TIMESTAMP_AUTO_SET_ON_INSERT);
+ else
+ {
+ bitmap_set_bit(table->write_set,
+ table->timestamp_field->field_index);
+ }
+ }
}
// For the values we need select_priv
#ifndef NO_EMBEDDED_ACCESS_CHECKS
@@ -289,24 +301,22 @@ static int check_update_fields(THD *thd, TABLE_LIST *insert_table_list,
List<Item> &update_fields, table_map *map)
{
TABLE *table= insert_table_list->table;
- query_id_t timestamp_query_id;
- LINT_INIT(timestamp_query_id);
+ my_bool timestamp_mark;
+
+ LINT_INIT(timestamp_mark);
- /*
- Change the query_id for the timestamp column so that we can
- check if this is modified directly.
- */
if (table->timestamp_field)
{
- timestamp_query_id= table->timestamp_field->query_id;
- table->timestamp_field->query_id= thd->query_id - 1;
+ /*
+ Unmark the timestamp field so that we can check if this is modified
+ by update_fields
+ */
+ timestamp_mark= bitmap_test_and_clear(table->write_set,
+ table->timestamp_field->field_index);
}
- /*
- Check the fields we are going to modify. This will set the query_id
- of all used fields to the threads query_id.
- */
- if (setup_fields(thd, 0, update_fields, 1, 0, 0))
+ /* 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->effective_algorithm == VIEW_ALGORITHM_MERGE &&
@@ -316,25 +326,23 @@ static int check_update_fields(THD *thd, TABLE_LIST *insert_table_list,
if (table->timestamp_field)
{
/* Don't set timestamp column if this is modified. */
- if (table->timestamp_field->query_id == thd->query_id)
+ if (bitmap_is_set(table->write_set,
+ table->timestamp_field->field_index))
clear_timestamp_auto_bits(table->timestamp_field_type,
TIMESTAMP_AUTO_SET_ON_UPDATE);
- else
- table->timestamp_field->query_id= timestamp_query_id;
+ if (timestamp_mark)
+ bitmap_set_bit(table->write_set,
+ table->timestamp_field->field_index);
}
-
return 0;
}
-
/*
Prepare triggers for INSERT-like statement.
SYNOPSIS
prepare_triggers_for_insert_stmt()
- thd The current thread
table Table to which insert will happen
- duplic Type of duplicate handling for insert which will happen
NOTE
Prepare triggers for INSERT-like statement by marking fields
@@ -342,8 +350,7 @@ static int check_update_fields(THD *thd, TABLE_LIST *insert_table_list,
cannot be done if there are BEFORE UPDATE/DELETE triggers.
*/
-void prepare_triggers_for_insert_stmt(THD *thd, TABLE *table,
- enum_duplicates duplic)
+void prepare_triggers_for_insert_stmt(TABLE *table)
{
if (table->triggers)
{
@@ -367,35 +374,8 @@ void prepare_triggers_for_insert_stmt(THD *thd, TABLE *table,
*/
(void) table->file->extra(HA_EXTRA_UPDATE_CANNOT_BATCH);
}
- mark_fields_used_by_triggers_for_insert_stmt(thd, table, duplic);
- }
-}
-
-
-/*
- Mark fields used by triggers for INSERT-like statement.
-
- SYNOPSIS
- mark_fields_used_by_triggers_for_insert_stmt()
- thd The current thread
- table Table to which insert will happen
- duplic Type of duplicate handling for insert which will happen
-
- NOTE
- For REPLACE there is no sense in marking particular fields
- used by ON DELETE trigger as to execute it properly we have
- to retrieve and store values for all table columns anyway.
-*/
-
-void mark_fields_used_by_triggers_for_insert_stmt(THD *thd, TABLE *table,
- enum_duplicates duplic)
-{
- if (table->triggers)
- {
- table->triggers->mark_fields_used(thd, TRG_EVENT_INSERT);
- if (duplic == DUP_UPDATE)
- table->triggers->mark_fields_used(thd, TRG_EVENT_UPDATE);
}
+ table->mark_columns_needed_for_insert();
}
@@ -461,7 +441,8 @@ void upgrade_lock_type(THD *thd, thr_lock_type *lock_type,
bool log_on= (thd->options & OPTION_BIN_LOG ||
! (thd->security_ctx->master_access & SUPER_ACL));
- if (log_on && mysql_bin_log.is_open() && is_multi_insert)
+ if (global_system_variables.binlog_format == BINLOG_FORMAT_STMT &&
+ log_on && mysql_bin_log.is_open() && is_multi_insert)
{
/*
Statement-based binary logging does not work in this case, because:
@@ -476,7 +457,15 @@ void upgrade_lock_type(THD *thd, thr_lock_type *lock_type,
zero error code (i.e. "no error"), if then second row fails, query
will fail on slave too and slave will stop (wrongly believing that the
master got no error).
- So we fall back to non-delayed INSERT.
+ So we fallback to non-delayed INSERT.
+ Note that to be fully correct, we should test the "binlog format which
+ the delayed thread is going to use for this row". But in the common case
+ where the global binlog format is not changed and the session binlog
+ format may be changed, that is equal to the global binlog format.
+ We test it without mutex for speed reasons (condition rarely true), and
+ in the common case (global not changed) it is as good as without mutex;
+ if global value is changed, anyway there is uncertainty as the delayed
+ thread may be old and use the before-the-change value.
*/
*lock_type= TL_WRITE;
}
@@ -552,6 +541,10 @@ bool open_and_lock_for_insert_delayed(THD *thd, TABLE_LIST *table_list)
/**
INSERT statement implementation
+
+ @note Like implementations of other DDL/DML in MySQL, this function
+ relies on the caller to close the thread tables. This is done in the
+ end of dispatch_command().
*/
bool mysql_insert(THD *thd,TABLE_LIST *table_list,
@@ -582,8 +575,8 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list,
By default, both logs are enabled (this won't cause problems if the server
runs without --log-update or --log-bin).
*/
- bool log_on= (thd->options & OPTION_BIN_LOG) ||
- (!(thd->security_ctx->master_access & SUPER_ACL));
+ bool log_on= ((thd->options & OPTION_BIN_LOG) ||
+ (!(thd->security_ctx->master_access & SUPER_ACL)));
#endif
thr_lock_type lock_type;
Item *unused_conds= 0;
@@ -622,7 +615,7 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list,
}
lock_type= table_list->lock_type;
- thd->proc_info="init";
+ thd_proc_info(thd, "init");
thd->used_tables=0;
values= its++;
value_count= values->elements;
@@ -668,7 +661,7 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list,
my_error(ER_WRONG_VALUE_COUNT_ON_ROW, MYF(0), counter);
goto abort;
}
- if (setup_fields(thd, 0, *values, 0, 0, 0))
+ if (setup_fields(thd, 0, *values, MARK_COLUMNS_READ, 0, 0))
goto abort;
}
its.rewind ();
@@ -679,7 +672,7 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list,
/*
Fill in the given fields and dump it to the table file
*/
- info.records= info.deleted= info.copied= info.updated= info.touched= 0;
+ bzero((char*) &info,sizeof(info));
info.ignore= ignore;
info.handle_duplicates=duplic;
info.update_fields= &update_fields;
@@ -702,24 +695,15 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list,
if (thd->slave_thread &&
(info.handle_duplicates == DUP_UPDATE) &&
(table->next_number_field != NULL) &&
- rpl_master_has_bug(&active_mi->rli, 24432))
+ rpl_master_has_bug(&active_mi->rli, 24432, TRUE, NULL, NULL))
goto abort;
#endif
error=0;
- id=0;
- thd->proc_info="update";
- if (duplic == DUP_REPLACE)
- {
- if (!table->triggers || !table->triggers->has_delete_triggers())
- table->file->extra(HA_EXTRA_WRITE_CAN_REPLACE);
- /*
- REPLACE should change values of all columns so we should mark
- all columns as columns to be set. As nice side effect we will
- retrieve columns which values are needed for ON DELETE triggers.
- */
- table->file->extra(HA_EXTRA_RETRIEVE_ALL_COLS);
- }
+ thd_proc_info(thd, "update");
+ if (duplic == DUP_REPLACE &&
+ (!table->triggers || !table->triggers->has_delete_triggers()))
+ table->file->extra(HA_EXTRA_WRITE_CAN_REPLACE);
if (duplic == DUP_UPDATE)
table->file->extra(HA_EXTRA_INSERT_WITH_UPDATE);
/*
@@ -741,14 +725,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)
- table->file->start_bulk_insert(values_list.elements);
+ table->file->ha_start_bulk_insert(values_list.elements);
}
thd->abort_on_warning= (!ignore && (thd->variables.sql_mode &
(MODE_STRICT_TRANS_TABLES |
MODE_STRICT_ALL_TABLES)));
- prepare_triggers_for_insert_stmt(thd, table, duplic);
+ prepare_triggers_for_insert_stmt(table);
+
if (table_list->prepare_where(thd, 0, TRUE) ||
table_list->prepare_check_option(thd))
@@ -763,7 +748,7 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list,
table->triggers,
TRG_EVENT_INSERT))
{
- if (values_list.elements != 1 && !thd->net.report_error)
+ if (values_list.elements != 1 && ! thd->is_error())
{
info.records++;
continue;
@@ -794,7 +779,7 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list,
table->triggers,
TRG_EVENT_INSERT))
{
- if (values_list.elements != 1 && ! thd->net.report_error)
+ if (values_list.elements != 1 && ! thd->is_error())
{
info.records++;
continue;
@@ -818,20 +803,13 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list,
#ifndef EMBEDDED_LIBRARY
if (lock_type == TL_WRITE_DELAYED)
{
- error=write_delayed(thd, table, duplic, ignore, query, thd->query_length, log_on);
+ LEX_STRING const st_query = { query, thd->query_length };
+ error=write_delayed(thd, table, duplic, st_query, ignore, log_on);
query=0;
}
else
#endif
error=write_record(thd, table ,&info);
- /*
- If auto_increment values are used, save the first one for
- LAST_INSERT_ID() and for the update log.
- */
- if (! id && thd->insert_id_used)
- { // Get auto increment value
- id= thd->last_insert_id;
- }
if (error)
break;
thd->row_count++;
@@ -849,25 +827,23 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list,
{
if (!error)
{
- id=0; // No auto_increment id
info.copied=values_list.elements;
end_delayed_insert(thd);
}
- query_cache_invalidate3(thd, table_list, 1);
}
else
#endif
{
- if (!thd->prelocked_mode && table->file->end_bulk_insert() && !error)
+ /*
+ Do not do this release if this is a delayed insert, it would steal
+ auto_inc values from the delayed_insert thread as they share TABLE.
+ */
+ table->file->ha_release_auto_increment();
+ if (!thd->prelocked_mode && table->file->ha_end_bulk_insert() && !error)
{
table->file->print_error(my_errno,MYF(0));
error=1;
}
- if (id && values_list.elements != 1)
- thd->insert_id(id); // For update log
- else if (table->next_number_field && info.copied)
- id=table->next_number_field->val_int(); // Return auto_increment value
-
if (duplic != DUP_ERROR || ignore)
table->file->extra(HA_EXTRA_NO_IGNORE_DUP_KEY);
@@ -880,58 +856,57 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list,
For the transactional algorithm to work the invalidation must be
before binlog writing and ha_autocommit_or_rollback
*/
- if (changed)
- query_cache_invalidate3(thd, table_list, 1);
+ query_cache_invalidate3(thd, table_list, 1);
}
if (changed && error <= 0 || thd->transaction.stmt.modified_non_trans_table
- || was_insert_delayed)
+ || was_insert_delayed)
{
if (mysql_bin_log.is_open())
{
- if (error <= 0)
+ if (error <= 0)
{
- /*
- [Guilhem wrote] Temporary errors may have filled
- 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
- space appears, and so when it finishes then the
- write_row() was entirely successful
- */
- /* todo: consider removing */
- thd->clear_error();
- }
- /* bug#22725:
-
- A query which per-row-loop can not be interrupted with
- KILLED, like INSERT, and that does not invoke stored
- routines can be binlogged with neglecting the KILLED error.
-
- If there was no error (error == zero) until after the end of
- inserting loop the KILLED flag that appeared later can be
- disregarded since previously possible invocation of stored
- routines did not result in any error due to the KILLED. In
- such case the flag is ignored for constructing binlog event.
- */
- Query_log_event qinfo(thd, thd->query, thd->query_length,
- transactional_table, FALSE,
- (error>0) ? thd->killed : THD::NOT_KILLED);
- DBUG_ASSERT(thd->killed != THD::KILL_BAD_DATA || error > 0);
- if (mysql_bin_log.write(&qinfo) && transactional_table)
- error=1;
+ /*
+ [Guilhem wrote] Temporary errors may have filled
+ 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
+ space appears, and so when it finishes then the
+ write_row() was entirely successful
+ */
+ /* todo: consider removing */
+ thd->clear_error();
+ }
+ /* bug#22725:
+
+ A query which per-row-loop can not be interrupted with
+ KILLED, like INSERT, and that does not invoke stored
+ routines can be binlogged with neglecting the KILLED error.
+
+ If there was no error (error == zero) until after the end of
+ inserting loop the KILLED flag that appeared later can be
+ disregarded since previously possible invocation of stored
+ routines did not result in any error due to the KILLED. In
+ such case the flag is ignored for constructing binlog event.
+ */
+ DBUG_ASSERT(thd->killed != THD::KILL_BAD_DATA || error > 0);
+ if (thd->binlog_query(THD::ROW_QUERY_TYPE,
+ thd->query, thd->query_length,
+ transactional_table, FALSE,
+ (error>0) ? thd->killed : THD::NOT_KILLED) &&
+ transactional_table)
+ {
+ error=1;
+ }
}
if (thd->transaction.stmt.modified_non_trans_table)
- thd->transaction.all.modified_non_trans_table= TRUE;
+ thd->transaction.all.modified_non_trans_table= TRUE;
}
DBUG_ASSERT(transactional_table || !changed ||
thd->transaction.stmt.modified_non_trans_table);
- if (transactional_table)
- error=ha_autocommit_or_rollback(thd,error);
if (thd->lock)
{
- mysql_unlock_tables(thd, thd->lock);
/*
Invalidate the table in the query cache if something changed
after unlocking when changes become fisible.
@@ -942,24 +917,32 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list,
{
query_cache_invalidate3(thd, table_list, 1);
}
- thd->lock=0;
}
}
- thd->proc_info="end";
+ thd_proc_info(thd, "end");
+ /*
+ We'll report to the client this id:
+ - if the table contains an autoincrement column and we successfully
+ inserted an autogenerated value, the autogenerated value.
+ - if the table contains no autoincrement column and LAST_INSERT_ID(X) was
+ called, X.
+ - if the table contains an autoincrement column, and some rows were
+ inserted, the id of the last "inserted" row (if IGNORE, that value may not
+ have been really inserted but ignored).
+ */
+ 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 :
+ ((table->next_number_field && info.copied) ?
+ table->next_number_field->val_int() : 0));
table->next_number_field=0;
thd->count_cuted_fields= CHECK_FIELD_IGNORE;
- thd->next_insert_id=0; // Reset this if wrongly used
table->auto_increment_field_not_null= FALSE;
if (duplic == DUP_REPLACE &&
(!table->triggers || !table->triggers->has_delete_triggers()))
table->file->extra(HA_EXTRA_WRITE_CANNOT_REPLACE);
- /* Reset value of LAST_INSERT_ID if no rows were inserted or touched */
- if (!info.copied && !info.touched && thd->insert_id_used)
- {
- thd->insert_id(0);
- id=0;
- }
if (error)
goto abort;
if (values_list.elements == 1 && (!(thd->options & OPTION_WARNINGS) ||
@@ -968,7 +951,7 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list,
thd->row_count_func= info.copied + info.deleted +
((thd->client_capabilities & CLIENT_FOUND_ROWS) ?
info.touched : info.updated);
- send_ok(thd, (ulong) thd->row_count_func, id);
+ my_ok(thd, (ulong) thd->row_count_func, id);
}
else
{
@@ -983,7 +966,7 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list,
sprintf(buff, ER(ER_INSERT_INFO), (ulong) info.records,
(ulong) (info.deleted + updated), (ulong) thd->cuted_fields);
thd->row_count_func= info.copied + info.deleted + updated;
- ::send_ok(thd, (ulong) thd->row_count_func, id, buff);
+ ::my_ok(thd, (ulong) thd->row_count_func, id, buff);
}
thd->abort_on_warning= 0;
DBUG_RETURN(FALSE);
@@ -993,6 +976,8 @@ abort:
if (lock_type == TL_WRITE_DELAYED)
end_delayed_insert(thd);
#endif
+ if (table != NULL)
+ table->file->ha_release_auto_increment();
if (!joins_freed)
free_underlaid_joins(thd, &thd->lex->select_lex);
thd->abort_on_warning= 0;
@@ -1029,10 +1014,10 @@ static bool check_view_insertability(THD * thd, TABLE_LIST *view)
Field_translator *trans_start= view->field_translation,
*trans_end= trans_start + num;
Field_translator *trans;
- uint used_fields_buff_size= (table->s->fields + 7) / 8;
- uchar *used_fields_buff= (uchar*)thd->alloc(used_fields_buff_size);
+ uint used_fields_buff_size= bitmap_buffer_size(table->s->fields);
+ uint32 *used_fields_buff= (uint32*)thd->alloc(used_fields_buff_size);
MY_BITMAP used_fields;
- bool save_set_query_id= thd->set_query_id;
+ enum_mark_columns save_mark_used_columns= thd->mark_used_columns;
DBUG_ENTER("check_key_in_view");
if (!used_fields_buff)
@@ -1040,8 +1025,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, used_fields_buff_size * 8,
- 0));
+ VOID(bitmap_init(&used_fields, used_fields_buff, table->s->fields, 0));
bitmap_clear_all(&used_fields);
view->contain_auto_increment= 0;
@@ -1049,20 +1033,20 @@ static bool check_view_insertability(THD * thd, TABLE_LIST *view)
we must not set query_id for fields as they're not
really used in this context
*/
- thd->set_query_id= 0;
+ thd->mark_used_columns= MARK_COLUMNS_NONE;
/* check simplicity and prepare unique test of view */
for (trans= trans_start; trans != trans_end; trans++)
{
if (!trans->item->fixed && trans->item->fix_fields(thd, &trans->item))
{
- thd->set_query_id= save_set_query_id;
+ thd->mark_used_columns= save_mark_used_columns;
DBUG_RETURN(TRUE);
}
Item_field *field;
/* simple SELECT list entry (field without expression) */
if (!(field= trans->item->filed_for_view_update()))
{
- thd->set_query_id= save_set_query_id;
+ thd->mark_used_columns= save_mark_used_columns;
DBUG_RETURN(TRUE);
}
if (field->field->unireg_check == Field::NEXT_NUMBER)
@@ -1074,7 +1058,7 @@ static bool check_view_insertability(THD * thd, TABLE_LIST *view)
*/
trans->item= field;
}
- thd->set_query_id= save_set_query_id;
+ thd->mark_used_columns= save_mark_used_columns;
/* unique test */
for (trans= trans_start; trans != trans_end; trans++)
{
@@ -1107,7 +1091,7 @@ static bool check_view_insertability(THD * thd, TABLE_LIST *view)
*/
static bool mysql_prepare_insert_check_table(THD *thd, TABLE_LIST *table_list,
- List<Item> &fields, COND **where,
+ List<Item> &fields,
bool select_insert)
{
bool insert_into_view= (table_list->view != 0);
@@ -1122,7 +1106,7 @@ static bool mysql_prepare_insert_check_table(THD *thd, TABLE_LIST *table_list,
if (setup_tables_and_check_access(thd, &thd->lex->select_lex.context,
&thd->lex->select_lex.top_join_list,
- table_list, where,
+ table_list,
&thd->lex->select_lex.leaf_tables,
select_insert, INSERT_ACL, SELECT_ACL))
DBUG_RETURN(TRUE);
@@ -1221,8 +1205,7 @@ bool mysql_prepare_insert(THD *thd, TABLE_LIST *table_list,
DBUG_RETURN(TRUE);
}
- if (mysql_prepare_insert_check_table(thd, table_list, fields, where,
- select_insert))
+ if (mysql_prepare_insert_check_table(thd, table_list, fields, select_insert))
DBUG_RETURN(TRUE);
@@ -1244,7 +1227,7 @@ bool mysql_prepare_insert(THD *thd, TABLE_LIST *table_list,
res= check_insert_fields(thd, context->table_list, fields, *values,
!insert_into_view, &map) ||
- setup_fields(thd, 0, *values, 0, 0, 0);
+ setup_fields(thd, 0, *values, MARK_COLUMNS_READ, 0, 0);
if (!res && check_fields)
{
@@ -1268,7 +1251,7 @@ bool mysql_prepare_insert(THD *thd, TABLE_LIST *table_list,
ctx_state.restore_state(context, table_list);
if (!res)
- res= setup_fields(thd, 0, update_values, 1, 0, 0);
+ res= setup_fields(thd, 0, update_values, MARK_COLUMNS_READ, 0, 0);
}
if (res)
@@ -1290,12 +1273,12 @@ bool mysql_prepare_insert(THD *thd, TABLE_LIST *table_list,
select_lex->first_execution= 0;
}
/*
- Only call extra() handler method if we are not performing a DELAYED
+ Only call prepare_for_posistion() if we are not performing a DELAYED
operation. It will instead be executed by delayed insert thread.
*/
if ((duplic == DUP_UPDATE || duplic == DUP_REPLACE) &&
(table->reginfo.lock_type != TL_WRITE_DELAYED))
- table->file->extra(HA_EXTRA_RETRIEVE_PRIMARY_KEY);
+ table->prepare_for_position();
DBUG_RETURN(FALSE);
}
@@ -1342,22 +1325,54 @@ int write_record(THD *thd, TABLE *table,COPY_INFO *info)
{
int error, trg_error= 0;
char *key=0;
+ MY_BITMAP *save_read_set, *save_write_set;
+ ulonglong prev_insert_id= table->file->next_insert_id;
+ ulonglong insert_id_for_cur_row= 0;
DBUG_ENTER("write_record");
info->records++;
+ save_read_set= table->read_set;
+ save_write_set= table->write_set;
+
if (info->handle_duplicates == DUP_REPLACE ||
info->handle_duplicates == DUP_UPDATE)
{
- while ((error=table->file->write_row(table->record[0])))
+ while ((error=table->file->ha_write_row(table->record[0])))
{
uint key_nr;
- if (error != HA_WRITE_SKIP)
+ /*
+ If we do more than one iteration of this loop, from the second one the
+ row will have an explicit value in the autoinc field, which was set at
+ the first call of handler::update_auto_increment(). So we must save
+ the autogenerated value to avoid thd->insert_id_for_cur_row to become
+ 0.
+ */
+ if (table->file->insert_id_for_cur_row > 0)
+ insert_id_for_cur_row= table->file->insert_id_for_cur_row;
+ else
+ table->file->insert_id_for_cur_row= insert_id_for_cur_row;
+ bool is_duplicate_key_error;
+ if (table->file->is_fatal_error(error, HA_CHECK_DUP))
goto err;
+ is_duplicate_key_error= table->file->is_fatal_error(error, 0);
+ if (!is_duplicate_key_error)
+ {
+ /*
+ We come here when we had an ignorable error which is not a duplicate
+ key error. In this we ignore error if ignore flag is set, otherwise
+ report error as usual. We will not do any duplicate key processing.
+ */
+ if (info->ignore)
+ 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)
{
- error=HA_WRITE_SKIP; /* Database can't find key */
+ error= HA_ERR_FOUND_DUPP_KEY; /* Database can't find key */
goto err;
}
+ /* Read all columns for the row we are going to replace */
+ table->use_all_columns();
/*
Don't allow REPLACE to replace a row when a auto_increment column
was used. This ensures that we don't get a problem when the
@@ -1366,11 +1381,11 @@ int write_record(THD *thd, TABLE *table,COPY_INFO *info)
if (info->handle_duplicates == DUP_REPLACE &&
table->next_number_field &&
key_nr == table->s->next_number_index &&
- table->file->auto_increment_column_changed)
+ (insert_id_for_cur_row > 0))
goto err;
- if (table->file->table_flags() & HA_DUPP_POS)
+ if (table->file->ha_table_flags() & HA_DUPLICATE_POS)
{
- if (table->file->rnd_pos(table->record[1],table->file->dupp_ref))
+ if (table->file->rnd_pos(table->record[1],table->file->dup_ref))
goto err;
}
else
@@ -1390,12 +1405,10 @@ int write_record(THD *thd, TABLE *table,COPY_INFO *info)
goto err;
}
}
- key_copy((byte*) key,table->record[0],table->key_info+key_nr,0);
- if ((error=(table->file->index_read_idx(table->record[1],key_nr,
- (byte*) key,
- table->key_info[key_nr].
- key_length,
- HA_READ_KEY_EXACT))))
+ key_copy((uchar*) key,table->record[0],table->key_info+key_nr,0);
+ if ((error=(table->file->index_read_idx_map(table->record[1],key_nr,
+ (uchar*) key, HA_WHOLE_KEY,
+ HA_READ_KEY_EXACT))))
goto err;
}
if (info->handle_duplicates == DUP_UPDATE)
@@ -1426,24 +1439,42 @@ int write_record(THD *thd, TABLE *table,COPY_INFO *info)
if (res == VIEW_CHECK_ERROR)
goto before_trg_err;
- table->file->restore_auto_increment();
- if ((table->file->table_flags() & HA_PARTIAL_COLUMN_READ) ||
- compare_record(table, thd->query_id))
+ 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 ((table->file->ha_table_flags() & HA_PARTIAL_COLUMN_READ &&
+ !bitmap_is_subset(table->write_set, table->read_set)) ||
+ compare_record(table))
{
- if ((error=table->file->update_row(table->record[1],table->record[0])))
+ if ((error=table->file->ha_update_row(table->record[1],
+ table->record[0])) &&
+ error != HA_ERR_RECORD_IS_THE_SAME)
{
- if ((error == HA_ERR_FOUND_DUPP_KEY) && info->ignore)
+ if (info->ignore &&
+ !table->file->is_fatal_error(error, HA_CHECK_DUP_KEY))
{
goto ok_or_after_trg_err;
}
goto err;
}
- info->updated++;
+ if (error != HA_ERR_RECORD_IS_THE_SAME)
+ info->updated++;
+ else
+ error= 0;
+ /*
+ If ON DUP KEY UPDATE updates a row instead of inserting one, it's
+ like a regular UPDATE statement: it should not affect the value of a
+ next SELECT LAST_INSERT_ID() or mysql_insert_id().
+ Except if LAST_INSERT_ID(#) was in the INSERT query, which is
+ handled separately by THD::arg_of_last_insert_id_function.
+ */
+ 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,
- TRG_ACTION_AFTER,
- TRUE));
+ TRG_ACTION_AFTER, TRUE));
info->copied++;
}
@@ -1476,10 +1507,15 @@ int write_record(THD *thd, TABLE *table,COPY_INFO *info)
table->timestamp_field_type == TIMESTAMP_AUTO_SET_ON_BOTH) &&
(!table->triggers || !table->triggers->has_delete_triggers()))
{
- if ((error=table->file->update_row(table->record[1],
- table->record[0])))
+ if ((error=table->file->ha_update_row(table->record[1],
+ table->record[0])) &&
+ error != HA_ERR_RECORD_IS_THE_SAME)
goto err;
- info->deleted++;
+ if (error != HA_ERR_RECORD_IS_THE_SAME)
+ info->deleted++;
+ else
+ error= 0;
+ thd->record_first_successful_insert_id_in_cur_stmt(table->file->insert_id_for_cur_row);
/*
Since we pretend that we have done insert we should call
its after triggers.
@@ -1492,7 +1528,7 @@ int write_record(THD *thd, TABLE *table,COPY_INFO *info)
table->triggers->process_triggers(thd, TRG_EVENT_DELETE,
TRG_ACTION_BEFORE, TRUE))
goto before_trg_err;
- if ((error=table->file->delete_row(table->record[1])))
+ if ((error=table->file->ha_delete_row(table->record[1])))
goto err;
info->deleted++;
if (!table->file->has_transactions())
@@ -1508,18 +1544,38 @@ int write_record(THD *thd, TABLE *table,COPY_INFO *info)
}
}
}
+
+ /*
+ If more than one iteration of the above while loop is done, from the second
+ one the row being inserted will have an explicit value in the autoinc field,
+ which was set at the first call of handler::update_auto_increment(). This
+ value is saved to avoid thd->insert_id_for_cur_row becoming 0. Use this saved
+ autoinc value.
+ */
+ if (table->file->insert_id_for_cur_row == 0)
+ table->file->insert_id_for_cur_row= insert_id_for_cur_row;
+
+ thd->record_first_successful_insert_id_in_cur_stmt(table->file->insert_id_for_cur_row);
+ /*
+ Restore column maps if they where replaced during an duplicate key
+ problem.
+ */
+ if (table->read_set != save_read_set ||
+ table->write_set != save_write_set)
+ table->column_bitmaps_set(save_read_set, save_write_set);
}
- else if ((error=table->file->write_row(table->record[0])))
+ else if ((error=table->file->ha_write_row(table->record[0])))
{
if (!info->ignore ||
- (error != HA_ERR_FOUND_DUPP_KEY && error != HA_ERR_FOUND_DUPP_UNIQUE))
+ table->file->is_fatal_error(error, HA_CHECK_DUP))
goto err;
- table->file->restore_auto_increment();
+ table->file->restore_auto_increment(prev_insert_id);
goto ok_or_after_trg_err;
}
after_trg_n_copied_inc:
info->copied++;
+ thd->record_first_successful_insert_id_in_cur_stmt(table->file->insert_id_for_cur_row);
trg_error= (table->triggers &&
table->triggers->process_triggers(thd, TRG_EVENT_INSERT,
TRG_ACTION_AFTER, TRUE));
@@ -1537,11 +1593,12 @@ err:
if (thd->lex->current_select)
thd->lex->current_select->no_error= 0; // Give error
table->file->print_error(error,MYF(0));
-
+
before_trg_err:
- table->file->restore_auto_increment();
+ table->file->restore_auto_increment(prev_insert_id);
if (key)
my_safe_afree(key, table->s->max_unique_length, MAX_KEY_LENGTH);
+ table->column_bitmaps_set(save_read_set, save_write_set);
DBUG_RETURN(1);
}
@@ -1554,11 +1611,13 @@ int check_that_all_fields_are_given_values(THD *thd, TABLE *entry,
TABLE_LIST *table_list)
{
int err= 0;
+ MY_BITMAP *write_set= entry->write_set;
+
for (Field **field=entry->field ; *field ; field++)
{
- if ((*field)->query_id != thd->query_id &&
+ if (!bitmap_is_set(write_set, (*field)->field_index) &&
((*field)->flags & NO_DEFAULT_VALUE_FLAG) &&
- ((*field)->real_type() != FIELD_TYPE_ENUM))
+ ((*field)->real_type() != MYSQL_TYPE_ENUM))
{
bool view= FALSE;
if (table_list)
@@ -1596,21 +1655,28 @@ int check_that_all_fields_are_given_values(THD *thd, TABLE *entry,
class delayed_row :public ilink {
public:
- char *record,*query;
+ char *record;
enum_duplicates dup;
time_t start_time;
- bool query_start_used,last_insert_id_used,insert_id_used, ignore, log_query;
- ulonglong last_insert_id;
- ulonglong next_insert_id;
+ ulong sql_mode;
+ bool auto_increment_field_not_null;
+ bool query_start_used, ignore, log_query;
+ bool stmt_depends_on_first_successful_insert_id_in_prev_stmt;
+ ulonglong first_successful_insert_id_in_prev_stmt;
+ ulonglong forced_insert_id;
ulong auto_increment_increment;
ulong auto_increment_offset;
timestamp_auto_set_type timestamp_field_type;
- uint query_length;
+ LEX_STRING query;
- delayed_row(enum_duplicates dup_arg, bool ignore_arg, bool log_query_arg)
- :record(0), query(0), dup(dup_arg), ignore(ignore_arg), log_query(log_query_arg) {}
+ delayed_row(LEX_STRING const query_arg, enum_duplicates dup_arg,
+ bool ignore_arg, bool log_query_arg)
+ : record(0), dup(dup_arg), ignore(ignore_arg), log_query(log_query_arg),
+ forced_insert_id(0), query(query_arg)
+ {}
~delayed_row()
{
+ x_free(query.str);
x_free(record);
}
};
@@ -1624,6 +1690,7 @@ public:
class Delayed_insert :public ilink {
uint locks_in_memory;
+ thr_lock_type delayed_lock;
public:
THD thd;
TABLE *table;
@@ -1648,6 +1715,12 @@ public:
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.
+ */
+ thd.lex->set_stmt_unsafe();
+ thd.set_current_stmt_binlog_row_based_if_mixed();
bzero((char*) &thd.net, sizeof(thd.net)); // Safety
bzero((char*) &table_list, sizeof(table_list)); // Safety
@@ -1659,6 +1732,8 @@ public:
pthread_cond_init(&cond_client,NULL);
VOID(pthread_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));
}
~Delayed_insert()
@@ -1720,7 +1795,7 @@ I_List<Delayed_insert> delayed_threads;
static
Delayed_insert *find_handler(THD *thd, TABLE_LIST *table_list)
{
- thd->proc_info="waiting for delay_list";
+ thd_proc_info(thd, "waiting for delay_list");
pthread_mutex_lock(&LOCK_delayed_insert); // Protect master list
I_List_iterator<Delayed_insert> it(delayed_threads);
Delayed_insert *di;
@@ -1802,7 +1877,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="Creating delayed handler";
+ thd_proc_info(thd, "Creating delayed handler");
pthread_mutex_lock(&LOCK_delayed_create);
/*
The first search above was done without LOCK_delayed_create.
@@ -1812,7 +1887,6 @@ bool delayed_get_table(THD *thd, TABLE_LIST *table_list)
{
if (!(di= new Delayed_insert()))
{
- my_error(ER_OUTOFMEMORY,MYF(0),sizeof(Delayed_insert));
thd->fatal_error();
goto end_create;
}
@@ -1849,24 +1923,24 @@ bool delayed_get_table(THD *thd, TABLE_LIST *table_list)
}
/* Wait until table is open */
- thd->proc_info="waiting for handler open";
+ thd_proc_info(thd, "waiting for handler open");
while (!di->thd.killed && !di->table && !thd->killed)
{
pthread_cond_wait(&di->cond_client, &di->mutex);
}
pthread_mutex_unlock(&di->mutex);
- thd->proc_info="got old table";
+ thd_proc_info(thd, "got old table");
if (di->thd.killed)
{
- if (di->thd.net.report_error)
- {
+ if (di->thd.is_error())
+ {
/*
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.
*/
- my_message(di->thd.net.last_errno, di->thd.net.last_error,
+ my_message(di->thd.main_da.sql_errno(), di->thd.main_da.message(),
MYF(0));
}
di->unlock();
@@ -1875,7 +1949,7 @@ bool delayed_get_table(THD *thd, TABLE_LIST *table_list)
if (thd->killed)
{
di->unlock();
- goto end_create;
+ goto end_create;
}
pthread_mutex_lock(&LOCK_delayed_insert);
delayed_threads.append(di);
@@ -1889,16 +1963,16 @@ bool delayed_get_table(THD *thd, TABLE_LIST *table_list)
pthread_mutex_unlock(&di->mutex);
if (table_list->table)
{
- DBUG_ASSERT(thd->net.report_error == 0);
+ DBUG_ASSERT(! thd->is_error());
thd->di= di;
}
/* Unlock the delayed insert object after its last access. */
di->unlock();
- DBUG_RETURN(table_list->table == NULL);
+ DBUG_RETURN((table_list->table == NULL));
end_create:
pthread_mutex_unlock(&LOCK_delayed_create);
- DBUG_RETURN(thd->net.report_error);
+ DBUG_RETURN(thd->is_error());
}
@@ -1923,6 +1997,8 @@ TABLE *Delayed_insert::get_local_table(THD* client_thd)
my_ptrdiff_t adjust_ptrs;
Field **field,**org_field, *found_next_number_field;
TABLE *copy;
+ TABLE_SHARE *share;
+ uchar *bitmap;
DBUG_ENTER("Delayed_insert::get_local_table");
/* First request insert thread to get a lock */
@@ -1930,21 +2006,22 @@ TABLE *Delayed_insert::get_local_table(THD* client_thd)
tables_in_use++;
if (!thd.lock) // Table is not locked
{
- client_thd->proc_info="waiting for handler lock";
+ 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)
{
pthread_cond_wait(&cond_client,&mutex);
}
- client_thd->proc_info="got handler lock";
+ thd_proc_info(client_thd, "got handler lock");
if (client_thd->killed)
goto error;
if (dead)
{
- my_message(thd.net.last_errno, thd.net.last_error, MYF(0));
+ my_message(thd.main_da.sql_errno(), thd.main_da.message(), MYF(0));
goto error;
}
}
+ share= table->s;
/*
Allocate memory for the TABLE object, the field pointers array, and
@@ -1953,26 +2030,22 @@ TABLE *Delayed_insert::get_local_table(THD* client_thd)
bytes. Since the table copy is used for creating one record only,
the other record buffers and alignment are unnecessary.
*/
- client_thd->proc_info="allocating local table";
+ thd_proc_info(client_thd, "allocating local table");
copy= (TABLE*) client_thd->alloc(sizeof(*copy)+
- (table->s->fields+1)*sizeof(Field**)+
- table->s->reclength);
+ (share->fields+1)*sizeof(Field**)+
+ share->reclength +
+ share->column_bitmap_size*2);
if (!copy)
goto error;
/* Copy the TABLE object. */
*copy= *table;
- copy->s= &copy->share_not_to_be_used;
- // No name hashing
- bzero((char*) &copy->s->name_hash,sizeof(copy->s->name_hash));
/* We don't need to change the file handler here */
-
/* Assign the pointers for the field pointers array and the record. */
field= copy->field= (Field**) (copy + 1);
- copy->record[0]= (byte*) (field + table->s->fields + 1);
- memcpy((char*) copy->record[0], (char*) table->record[0],
- table->s->reclength);
-
+ bitmap= (uchar*) (field + share->fields + 1);
+ copy->record[0]= (bitmap + share->column_bitmap_size * 2);
+ memcpy((char*) copy->record[0], (char*) table->record[0], share->reclength);
/*
Make a copy of all fields.
The copied fields need to point into the copied record. This is done
@@ -1981,14 +2054,13 @@ TABLE *Delayed_insert::get_local_table(THD* client_thd)
records. That way we preserve the relative positions in the records.
*/
adjust_ptrs= PTR_BYTE_DIFF(copy->record[0], table->record[0]);
-
found_next_number_field= table->found_next_number_field;
for (org_field= table->field; *org_field; org_field++, field++)
{
if (!(*field= (*org_field)->new_field(client_thd->mem_root, copy, 1)))
goto error;
(*field)->orig_table= copy; // Remove connection
- (*field)->move_field(adjust_ptrs); // Point at copy->record[0]
+ (*field)->move_field_offset(adjust_ptrs); // Point at copy->record[0]
if (*org_field == found_next_number_field)
(*field)->table->found_next_number_field= *field;
}
@@ -1999,20 +2071,26 @@ TABLE *Delayed_insert::get_local_table(THD* client_thd)
{
/* Restore offset as this may have been reset in handle_inserts */
copy->timestamp_field=
- (Field_timestamp*) copy->field[table->s->timestamp_field_offset];
+ (Field_timestamp*) copy->field[share->timestamp_field_offset];
copy->timestamp_field->unireg_check= table->timestamp_field->unireg_check;
copy->timestamp_field_type= copy->timestamp_field->get_auto_set_type();
}
- /* _rowid is not used with delayed insert */
- copy->rowid_field=0;
-
/* Adjust in_use for pointing to client thread */
copy->in_use= client_thd;
/* Adjust lock_count. This table object is not part of a lock. */
copy->lock_count= 0;
+ /* Adjust bitmaps */
+ copy->def_read_set.bitmap= (my_bitmap_map*) bitmap;
+ copy->def_write_set.bitmap= ((my_bitmap_map*)
+ (bitmap + share->column_bitmap_size));
+ copy->tmp_set.bitmap= 0; // To catch errors
+ bzero((char*) bitmap, share->column_bitmap_size*2);
+ copy->read_set= &copy->def_read_set;
+ copy->write_set= &copy->def_write_set;
+
DBUG_RETURN(copy);
/* Got fatal error */
@@ -2027,56 +2105,75 @@ TABLE *Delayed_insert::get_local_table(THD* client_thd)
/* Put a question in queue */
static
-int write_delayed(THD *thd,TABLE *table,enum_duplicates duplic, bool ignore,
- char *query, uint query_length, bool log_on)
+int write_delayed(THD *thd, TABLE *table, enum_duplicates duplic,
+ LEX_STRING query, bool ignore, bool log_on)
{
- delayed_row *row=0;
+ delayed_row *row= 0;
Delayed_insert *di=thd->di;
+ const Discrete_interval *forced_auto_inc;
DBUG_ENTER("write_delayed");
+ DBUG_PRINT("enter", ("query = '%s' length %lu", query.str,
+ (ulong) query.length));
- thd->proc_info="waiting for handler insert";
+ thd_proc_info(thd, "waiting for handler insert");
pthread_mutex_lock(&di->mutex);
while (di->stacked_inserts >= delayed_queue_size && !thd->killed)
pthread_cond_wait(&di->cond_client,&di->mutex);
- thd->proc_info="storing row into queue";
+ thd_proc_info(thd, "storing row into queue");
- if (thd->killed || !(row= new delayed_row(duplic, ignore, log_on)))
+ if (thd->killed)
goto err;
- if (!query)
- query_length=0;
- if (!(row->record= (char*) my_malloc(table->s->reclength+query_length+1,
- MYF(MY_WME))))
- goto err;
- memcpy(row->record, table->record[0], table->s->reclength);
- if (query_length)
+ /*
+ Take a copy of the query string, if there is any. The string will
+ be free'ed when the row is destroyed. If there is no query string,
+ we don't do anything special.
+ */
+
+ if (query.str)
{
- row->query= row->record+table->s->reclength;
- memcpy(row->query,query,query_length+1);
+ char *str;
+ if (!(str= my_strndup(query.str, query.length, MYF(MY_WME))))
+ goto err;
+ query.str= str;
}
- row->query_length= query_length;
+ row= new delayed_row(query, duplic, ignore, log_on);
+ if (row == NULL)
+ {
+ my_free(query.str, MYF(MY_WME));
+ goto err;
+ }
+
+ if (!(row->record= (char*) my_malloc(table->s->reclength, MYF(MY_WME))))
+ goto err;
+ memcpy(row->record, table->record[0], table->s->reclength);
row->start_time= thd->start_time;
row->query_start_used= thd->query_start_used;
- row->last_insert_id_used= thd->last_insert_id_used;
- row->insert_id_used= thd->insert_id_used;
- row->last_insert_id= thd->last_insert_id;
+ /*
+ those are for the binlog: LAST_INSERT_ID() has been evaluated at this
+ time, so record does not need it, but statement-based binlogging of the
+ INSERT will need when the row is actually inserted.
+ As for SET INSERT_ID, DELAYED does not honour it (BUG#20830).
+ */
+ row->stmt_depends_on_first_successful_insert_id_in_prev_stmt=
+ thd->stmt_depends_on_first_successful_insert_id_in_prev_stmt;
+ row->first_successful_insert_id_in_prev_stmt=
+ thd->first_successful_insert_id_in_prev_stmt;
row->timestamp_field_type= table->timestamp_field_type;
- /* The session variable settings can always be copied. */
+ /* Copy session variables. */
row->auto_increment_increment= thd->variables.auto_increment_increment;
row->auto_increment_offset= thd->variables.auto_increment_offset;
- /*
- Next insert id must be set for the first value in a multi-row insert
- only. So clear it after the first use. Assume a multi-row insert.
- Since the user thread doesn't really execute the insert,
- thd->next_insert_id is left untouched between the rows. If we copy
- the same insert id to every row of the multi-row insert, the delayed
- insert thread would copy this before inserting every row. Thus it
- tries to insert all rows with the same insert id. This fails on the
- unique constraint. So just the first row would be really inserted.
- */
- row->next_insert_id= thd->next_insert_id;
- thd->next_insert_id= 0;
+ row->sql_mode= thd->variables.sql_mode;
+ row->auto_increment_field_not_null= table->auto_increment_field_not_null;
+
+ /* Copy the next forced auto increment value, if any. */
+ if ((forced_auto_inc= thd->auto_inc_intervals_forced.get_next()))
+ {
+ row->forced_insert_id= forced_auto_inc->minimum();
+ DBUG_PRINT("delayed", ("transmitting auto_inc: %lu",
+ (ulong) row->forced_insert_id));
+ }
di->rows.push_back(row);
di->stacked_inserts++;
@@ -2161,8 +2258,8 @@ 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);
- thd->thread_id=thread_id++;
- thd->end_time();
+ thd->thread_id= thd->variables.pseudo_thread_id= thread_id++;
+ thd->set_current_time();
threads.append(thd);
thd->killed=abort_loop ? THD::KILL_CONNECTION : THD::NOT_KILLED;
pthread_mutex_unlock(&LOCK_thread_count);
@@ -2176,10 +2273,12 @@ pthread_handler_t handle_delayed_insert(void *arg)
since it does not find one in the list.
*/
pthread_mutex_lock(&di->mutex);
-#if !defined( __WIN__) && !defined(OS2) /* Win32 calls this in pthread_create */
+#if !defined( __WIN__) /* Win32 calls this in pthread_create */
if (my_thread_init())
{
- strmov(thd->net.last_error,ER(thd->net.last_errno=ER_OUT_OF_RESOURCES));
+ /* 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;
}
#endif
@@ -2188,28 +2287,39 @@ pthread_handler_t handle_delayed_insert(void *arg)
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();
- strmov(thd->net.last_error,ER(thd->net.last_errno=ER_OUT_OF_RESOURCES));
- goto end;
+ goto err;
}
-#if !defined(__WIN__) && !defined(OS2) && !defined(__NETWARE__)
- sigset_t set;
- VOID(sigemptyset(&set)); // Get mask in use
- VOID(pthread_sigmask(SIG_UNBLOCK,&set,&thd->block_signals));
-#endif
- /* open table */
+ /*
+ 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();
- if (!(di->table=open_ltable(thd,&di->table_list,TL_WRITE_DELAYED)))
+ /* Open table */
+ if (!(di->table= open_n_lock_single_table(thd, &di->table_list,
+ TL_WRITE_DELAYED)))
{
thd->fatal_error(); // Abort waiting inserts
- goto end;
+ goto err;
}
- if (!(di->table->file->table_flags() & HA_CAN_INSERT_DELAYED))
+ if (!(di->table->file->ha_table_flags() & HA_CAN_INSERT_DELAYED))
{
thd->fatal_error();
- my_error(ER_ILLEGAL_HA, MYF(0), di->table_list.table_name);
- goto end;
+ my_error(ER_DELAYED_NOT_SUPPORTED, MYF(0), di->table_list.table_name);
+ goto err;
}
if (di->table->triggers)
{
@@ -2218,7 +2328,7 @@ pthread_handler_t handle_delayed_insert(void *arg)
not support triggers with delayed insert. Terminate the delayed
thread without an error and thus request lock upgrade.
*/
- goto end;
+ goto err;
}
di->table->copy_blobs=1;
@@ -2255,7 +2365,7 @@ pthread_handler_t handle_delayed_insert(void *arg)
/* Information for pthread_kill */
di->thd.mysys_var->current_mutex= &di->mutex;
di->thd.mysys_var->current_cond= &di->cond;
- di->thd.proc_info="Waiting for INSERT";
+ thd_proc_info(&(di->thd), "Waiting for INSERT");
DBUG_PRINT("info",("Waiting for someone to insert rows"));
while (!thd->killed)
@@ -2290,7 +2400,7 @@ pthread_handler_t handle_delayed_insert(void *arg)
pthread_mutex_unlock(&di->thd.mysys_var->mutex);
pthread_mutex_lock(&di->mutex);
}
- di->thd.proc_info=0;
+ thd_proc_info(&(di->thd), 0);
if (di->tables_in_use && ! thd->lock)
{
@@ -2334,7 +2444,13 @@ pthread_handler_t handle_delayed_insert(void *arg)
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);
}
@@ -2342,7 +2458,23 @@ pthread_handler_t handle_delayed_insert(void *arg)
pthread_cond_broadcast(&di->cond_client); // If waiting clients
}
+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);
+
+#ifndef __WIN__
end:
+#endif
/*
di should be unlinked from the thread handler list and have no active
clients
@@ -2386,7 +2518,7 @@ static void free_delayed_insert_blobs(register TABLE *table)
{
if ((*ptr)->flags & BLOB_FLAG)
{
- char *str;
+ uchar *str;
((Field_blob *) (*ptr))->get_ptr(&str);
my_free(str,MYF(MY_ALLOW_ZERO_PTR));
((Field_blob *) (*ptr))->reset();
@@ -2399,8 +2531,8 @@ bool Delayed_insert::handle_inserts(void)
{
int error;
ulong max_rows;
- bool using_ignore= 0, using_opt_replace= 0;
- bool using_bin_log= mysql_bin_log.is_open();
+ bool using_ignore= 0, using_opt_replace= 0,
+ using_bin_log= mysql_bin_log.is_open();
delayed_row *row;
DBUG_ENTER("handle_inserts");
@@ -2408,21 +2540,27 @@ bool Delayed_insert::handle_inserts(void)
pthread_mutex_unlock(&mutex);
table->next_number_field=table->found_next_number_field;
+ table->use_all_columns();
- thd.proc_info="upgrading lock";
- if (thr_upgrade_write_delay_lock(*thd.lock->locks))
+ thd_proc_info(&thd, "upgrading lock");
+ if (thr_upgrade_write_delay_lock(*thd.lock->locks, delayed_lock))
{
- /* This can only happen if thread is killed by shutdown */
- sql_print_error(ER(ER_DELAYED_CANT_CHANGE_LOCK),table->s->table_name);
+ /*
+ This can happen if thread is killed either by a shutdown
+ or if another thread is removing the current table definition
+ from the table cache.
+ */
+ my_error(ER_DELAYED_CANT_CHANGE_LOCK,MYF(ME_FATALERROR),
+ table->s->table_name.str);
goto err;
}
- thd.proc_info="insert";
+ thd_proc_info(&thd, "insert");
max_rows= delayed_insert_limit;
if (thd.killed || table->needs_reopen_or_name_lock())
{
thd.killed= THD::KILL_CONNECTION;
- max_rows= ~(ulong)0; // Do as much as possible
+ max_rows= ULONG_MAX; // Do as much as possible
}
/*
@@ -2434,13 +2572,6 @@ bool Delayed_insert::handle_inserts(void)
table->file->extra(HA_EXTRA_WRITE_CACHE);
pthread_mutex_lock(&mutex);
- /* Reset auto-increment cacheing */
- if (thd.clear_next_insert_id)
- {
- thd.next_insert_id= 0;
- thd.clear_next_insert_id= 0;
- }
-
while ((row=rows.get()))
{
stacked_inserts--;
@@ -2449,24 +2580,49 @@ bool Delayed_insert::handle_inserts(void)
thd.start_time=row->start_time;
thd.query_start_used=row->query_start_used;
- thd.last_insert_id=row->last_insert_id;
- thd.last_insert_id_used=row->last_insert_id_used;
- thd.insert_id_used=row->insert_id_used;
+ /*
+ To get the exact auto_inc interval to store in the binlog we must not
+ use values from the previous interval (of the previous rows).
+ */
+ bool log_query= (row->log_query && row->query.str != NULL);
+ DBUG_PRINT("delayed", ("query: '%s' length: %lu", row->query.str ?
+ row->query.str : "[NULL]",
+ (ulong) row->query.length));
+ if (log_query)
+ {
+ /*
+ 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,
+ but we won't know this in the insert delayed thread. But before
+ the first value is sufficiently equivalent to after the last
+ value of the previous statement.
+ */
+ table->file->ha_release_auto_increment();
+ thd.auto_inc_intervals_in_cur_stmt_for_binlog.empty();
+ }
+ thd.first_successful_insert_id_in_prev_stmt=
+ row->first_successful_insert_id_in_prev_stmt;
+ thd.stmt_depends_on_first_successful_insert_id_in_prev_stmt=
+ row->stmt_depends_on_first_successful_insert_id_in_prev_stmt;
table->timestamp_field_type= row->timestamp_field_type;
+ table->auto_increment_field_not_null= row->auto_increment_field_not_null;
- /* The session variable settings can always be copied. */
+ /* Copy the session variables. */
thd.variables.auto_increment_increment= row->auto_increment_increment;
thd.variables.auto_increment_offset= row->auto_increment_offset;
- /* Next insert id must be used only if non-zero. */
- if (row->next_insert_id)
- thd.next_insert_id= row->next_insert_id;
- DBUG_PRINT("loop", ("next_insert_id: %lu", (ulong) thd.next_insert_id));
+ thd.variables.sql_mode= row->sql_mode;
+
+ /* Copy a forced insert_id, if any. */
+ if (row->forced_insert_id)
+ {
+ DBUG_PRINT("delayed", ("received auto_inc: %lu",
+ (ulong) row->forced_insert_id));
+ thd.force_one_auto_inc_interval(row->forced_insert_id);
+ }
info.ignore= row->ignore;
info.handle_duplicates= row->dup;
- if (info.handle_duplicates == DUP_UPDATE ||
- info.handle_duplicates == DUP_REPLACE)
- table->file->extra(HA_EXTRA_RETRIEVE_PRIMARY_KEY);
if (info.ignore ||
info.handle_duplicates != DUP_ERROR)
{
@@ -2488,21 +2644,8 @@ bool Delayed_insert::handle_inserts(void)
info.error_count++; // Ignore errors
thread_safe_increment(delayed_insert_errors,&LOCK_delayed_status);
row->log_query = 0;
- /*
- We must reset next_insert_id. Otherwise all following rows may
- become duplicates. If write_record() failed on a duplicate and
- next_insert_id would be left unchanged, the next rows would also
- be tried with the same insert id and would fail. Since the end
- of a multi-row statement is unknown here, all following rows in
- the queue would be dropped, regardless which thread added them.
- After the queue is used up, next_insert_id is cleared and the
- next run will succeed. This could even happen if these come from
- the same multi-row statement as the current queue contents. That
- way it would look somewhat random which rows are rejected after
- a duplicate.
- */
- thd.next_insert_id= 0;
}
+
if (using_ignore)
{
using_ignore=0;
@@ -2513,14 +2656,25 @@ bool Delayed_insert::handle_inserts(void)
using_opt_replace= 0;
table->file->extra(HA_EXTRA_WRITE_CANNOT_REPLACE);
}
- if (row->query && row->log_query && using_bin_log)
+
+ if (log_query && mysql_bin_log.is_open())
{
- Query_log_event qinfo(&thd, row->query, row->query_length, 0, FALSE);
- mysql_bin_log.write(&qinfo);
+ /*
+ 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.
+ */
+ thd.binlog_query(THD::ROW_QUERY_TYPE,
+ row->query.str, row->query.length,
+ FALSE, FALSE);
}
+
if (table->s->blob_fields)
free_delayed_insert_blobs(table);
- thread_safe_sub(delayed_rows_in_use,1,&LOCK_delayed_status);
+ thread_safe_decrement(delayed_rows_in_use,&LOCK_delayed_status);
thread_safe_increment(delayed_insert_writes,&LOCK_delayed_status);
pthread_mutex_lock(&mutex);
@@ -2532,47 +2686,62 @@ bool Delayed_insert::handle_inserts(void)
on this table until all entries has been processed
*/
if (group_count++ >= max_rows && (row= rows.head()) &&
- (!(row->log_query & using_bin_log) ||
- row->query))
+ (!(row->log_query & using_bin_log)))
{
group_count=0;
if (stacked_inserts || tables_in_use) // Let these wait a while
{
if (tables_in_use)
pthread_cond_broadcast(&cond_client); // If waiting clients
- thd.proc_info="reschedule";
+ thd_proc_info(&thd, "reschedule");
pthread_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.net.last_error);
+ sql_print_error("%s", thd.main_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))
{
- /* This should never happen */
- sql_print_error(ER(ER_DELAYED_CANT_CHANGE_LOCK),table->s->table_name);
+ /* This is not known to happen. */
+ my_error(ER_DELAYED_CANT_CHANGE_LOCK,MYF(ME_FATALERROR),
+ table->s->table_name.str);
+ goto err;
}
if (!using_bin_log)
table->file->extra(HA_EXTRA_WRITE_CACHE);
pthread_mutex_lock(&mutex);
- thd.proc_info="insert";
+ thd_proc_info(&thd, "insert");
}
if (tables_in_use)
pthread_cond_broadcast(&cond_client); // If waiting clients
}
}
-
- thd.proc_info=0;
- table->next_number_field=0;
+ thd_proc_info(&thd, 0);
pthread_mutex_unlock(&mutex);
+
+ /*
+ We need to flush the pending event when using row-based
+ replication since the flushing normally done in binlog_query() is
+ not done last in the statement: for delayed inserts, the insert
+ statement is logged *before* all rows are inserted.
+
+ We can flush the pending event without checking the thd->lock
+ since the delayed insert *thread* is not inside a stored function
+ 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);
+
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.net.last_error);
+ sql_print_error("%s", thd.main_da.message());
DBUG_PRINT("error", ("HA_EXTRA_NO_CACHE failed after loop"));
goto err;
}
@@ -2581,7 +2750,9 @@ bool Delayed_insert::handle_inserts(void)
DBUG_RETURN(0);
err:
- DBUG_EXECUTE("error", max_rows= 0;);
+#ifndef DBUG_OFF
+ max_rows= 0; // For DBUG output
+#endif
/* Remove all not used rows */
while ((row=rows.get()))
{
@@ -2593,7 +2764,9 @@ bool Delayed_insert::handle_inserts(void)
delete row;
thread_safe_increment(delayed_insert_errors,&LOCK_delayed_status);
stacked_inserts--;
- DBUG_EXECUTE("error", max_rows++;);
+#ifndef DBUG_OFF
+ max_rows++;
+#endif
}
DBUG_PRINT("error", ("dropped %lu rows after an error", max_rows));
thread_safe_increment(delayed_insert_errors, &LOCK_delayed_status);
@@ -2627,6 +2800,19 @@ bool mysql_insert_select_prepare(THD *thd)
DBUG_ENTER("mysql_insert_select_prepare");
/*
+ Statement-based replication of INSERT ... SELECT ... LIMIT is not safe
+ as order of rows is not defined, so in mixed mode we go to row-based.
+
+ Note that we may consider a statement as safe if ORDER BY primary_key
+ is present or we SELECT a constant. However it may confuse users to
+ see very similiar statements replicated differently.
+ */
+ if (lex->current_select->select_limit)
+ {
+ lex->set_stmt_unsafe();
+ thd->set_current_stmt_binlog_row_based_if_mixed();
+ }
+ /*
SELECT_LEX do not belong to INSERT statement, so we can't add WHERE
clause if table is VIEW
*/
@@ -2665,7 +2851,6 @@ select_insert::select_insert(TABLE_LIST *table_list_par, TABLE *table_par,
bool ignore_check_option_errors)
:table_list(table_list_par), table(table_par), fields(fields_par),
autoinc_value_of_last_inserted_row(0),
- autoinc_value_of_first_inserted_row(0),
insert_into_view(table_list_par && table_list_par->view != 0)
{
bzero((char*) &info,sizeof(info));
@@ -2688,6 +2873,7 @@ select_insert::prepare(List<Item> &values, SELECT_LEX_UNIT *u)
DBUG_ENTER("select_insert::prepare");
unit= u;
+
/*
Since table in which we are going to insert is added to the first
select, LEX::current_select should point to the first select while
@@ -2696,7 +2882,7 @@ select_insert::prepare(List<Item> &values, SELECT_LEX_UNIT *u)
lex->current_select= &lex->select_lex;
res= check_insert_fields(thd, table_list, *fields, values,
!insert_into_view, &map) ||
- setup_fields(thd, 0, values, 0, 0, 0);
+ setup_fields(thd, 0, values, MARK_COLUMNS_READ, 0, 0);
if (!res && fields->elements)
{
@@ -2729,7 +2915,7 @@ select_insert::prepare(List<Item> &values, SELECT_LEX_UNIT *u)
When we are not using GROUP BY and there are no ungrouped aggregate functions
we can refer to other tables in the ON DUPLICATE KEY part.
We use next_name_resolution_table descructively, so check it first (views?)
- */
+ */
DBUG_ASSERT (!table_list->next_name_resolution_table);
if (lex->select_lex.group_list.elements == 0 &&
!lex->select_lex.with_sum_func)
@@ -2738,9 +2924,11 @@ select_insert::prepare(List<Item> &values, SELECT_LEX_UNIT *u)
the INSERT table and the tables in the SELECT part of INSERT ... SELECT.
To do that we must concatenate the two lists
*/
- table_list->next_name_resolution_table= ctx_state.get_first_name_resolution_table();
+ table_list->next_name_resolution_table=
+ ctx_state.get_first_name_resolution_table();
- res= res || setup_fields(thd, 0, *info.update_values, 1, 0, 0);
+ res= res || setup_fields(thd, 0, *info.update_values,
+ MARK_COLUMNS_READ, 0, 0);
if (!res)
{
/*
@@ -2755,9 +2943,10 @@ select_insert::prepare(List<Item> &values, SELECT_LEX_UNIT *u)
while ((item= li++))
{
item->transform(&Item::update_value_transformer,
- (byte*)lex->current_select);
+ (uchar*)lex->current_select);
}
}
+
/* Restore the current context. */
ctx_state.restore_state(context, table_list);
}
@@ -2793,7 +2982,7 @@ select_insert::prepare(List<Item> &values, SELECT_LEX_UNIT *u)
We won't start bulk inserts at all if this statement uses functions or
should invoke triggers since they may access to the same table too.
*/
- table->file->start_bulk_insert((ha_rows) 0);
+ table->file->ha_start_bulk_insert((ha_rows) 0);
}
restore_record(table,s->default_values); // Get empty record
table->next_number_field=table->found_next_number_field;
@@ -2802,19 +2991,16 @@ select_insert::prepare(List<Item> &values, SELECT_LEX_UNIT *u)
if (thd->slave_thread &&
(info.handle_duplicates == DUP_UPDATE) &&
(table->next_number_field != NULL) &&
- rpl_master_has_bug(&active_mi->rli, 24432))
+ rpl_master_has_bug(&active_mi->rli, 24432, TRUE, NULL, NULL))
DBUG_RETURN(1);
#endif
thd->cuted_fields=0;
if (info.ignore || info.handle_duplicates != DUP_ERROR)
table->file->extra(HA_EXTRA_IGNORE_DUP_KEY);
- if (info.handle_duplicates == DUP_REPLACE)
- {
- if (!table->triggers || !table->triggers->has_delete_triggers())
- table->file->extra(HA_EXTRA_WRITE_CAN_REPLACE);
- table->file->extra(HA_EXTRA_RETRIEVE_ALL_COLS);
- }
+ if (info.handle_duplicates == DUP_REPLACE &&
+ (!table->triggers || !table->triggers->has_delete_triggers()))
+ table->file->extra(HA_EXTRA_WRITE_CAN_REPLACE);
if (info.handle_duplicates == DUP_UPDATE)
table->file->extra(HA_EXTRA_INSERT_WITH_UPDATE);
thd->abort_on_warning= (!info.ignore &&
@@ -2825,8 +3011,8 @@ select_insert::prepare(List<Item> &values, SELECT_LEX_UNIT *u)
table_list->prepare_check_option(thd));
if (!res)
- prepare_triggers_for_insert_stmt(thd, table,
- info.handle_duplicates);
+ prepare_triggers_for_insert_stmt(table);
+
DBUG_RETURN(res);
}
@@ -2852,7 +3038,7 @@ int select_insert::prepare2(void)
DBUG_ENTER("select_insert::prepare2");
if (thd->lex->current_select->options & OPTION_BUFFER_RESULT &&
!thd->prelocked_mode)
- table->file->start_bulk_insert((ha_rows) 0);
+ table->file->ha_start_bulk_insert((ha_rows) 0);
DBUG_RETURN(0);
}
@@ -2870,7 +3056,7 @@ select_insert::~select_insert()
{
table->next_number_field=0;
table->auto_increment_field_not_null= FALSE;
- table->file->reset();
+ table->file->ha_reset();
}
thd->count_cuted_fields= CHECK_FIELD_IGNORE;
thd->abort_on_warning= 0;
@@ -2882,6 +3068,7 @@ bool select_insert::send_data(List<Item> &values)
{
DBUG_ENTER("select_insert::send_data");
bool error=0;
+
if (unit->offset_limit_cnt)
{ // using limit offset,count
unit->offset_limit_cnt--;
@@ -2891,7 +3078,7 @@ bool select_insert::send_data(List<Item> &values)
thd->count_cuted_fields= CHECK_FIELD_WARN; // Calculate cuted fields
store_values(values);
thd->count_cuted_fields= CHECK_FIELD_IGNORE;
- if (thd->net.report_error)
+ if (thd->is_error())
DBUG_RETURN(1);
if (table_list) // Not CREATE ... SELECT
{
@@ -2926,21 +3113,16 @@ bool select_insert::send_data(List<Item> &values)
If no value has been autogenerated so far, we need to remember the
value we just saw, we may need to send it to client in the end.
*/
- if (!thd->insert_id_used)
- autoinc_value_of_last_inserted_row= table->next_number_field->val_int();
+ if (thd->first_successful_insert_id_in_cur_stmt == 0) // optimization
+ autoinc_value_of_last_inserted_row=
+ table->next_number_field->val_int();
/*
Clear auto-increment field for the next record, if triggers are used
we will clear it twice, but this should be cheap.
*/
table->next_number_field->reset();
- if (!autoinc_value_of_last_inserted_row && thd->insert_id_used)
- autoinc_value_of_last_inserted_row= thd->last_insert_id;
}
}
-
- if (thd->insert_id_used && !autoinc_value_of_first_inserted_row)
- autoinc_value_of_first_inserted_row= thd->last_insert_id;
-
DBUG_RETURN(error);
}
@@ -2967,52 +3149,48 @@ void select_insert::send_error(uint errcode,const char *err)
bool select_insert::send_eof()
{
- int error, error2;
- bool changed, transactional_table= table->file->has_transactions();
+ int error;
+ bool const trans_table= table->file->has_transactions();
ulonglong id;
+ bool changed;
THD::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->end_bulk_insert():0;
+ error= (!thd->prelocked_mode) ? table->file->ha_end_bulk_insert():0;
table->file->extra(HA_EXTRA_NO_IGNORE_DUP_KEY);
table->file->extra(HA_EXTRA_WRITE_CANNOT_REPLACE);
- /*
- We must invalidate the table in the query cache before binlog writing
- and ha_autocommit_or_rollback
- */
-
if (changed= (info.copied || info.deleted || info.updated))
{
+ /*
+ We must invalidate the table in the query cache before binlog writing
+ 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;
}
- DBUG_ASSERT(transactional_table || !changed ||
+ DBUG_ASSERT(trans_table || !changed ||
thd->transaction.stmt.modified_non_trans_table);
- // For binary log
- if (autoinc_value_of_last_inserted_row)
- {
- if (info.copied)
- thd->insert_id(autoinc_value_of_last_inserted_row);
- else
- {
- autoinc_value_of_first_inserted_row= 0;
- thd->insert_id(0);
- }
- }
- /* Write to binlog before commiting transaction */
+ /*
+ Write to binlog before commiting transaction. No statement will
+ 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.
+ */
if (mysql_bin_log.is_open())
{
if (!error)
thd->clear_error();
- Query_log_event qinfo(thd, thd->query, thd->query_length,
- transactional_table, FALSE, killed_status);
- mysql_bin_log.write(&qinfo);
+ thd->binlog_query(THD::ROW_QUERY_TYPE,
+ thd->query, thd->query_length,
+ trans_table, FALSE, killed_status);
}
- if ((error2=ha_autocommit_or_rollback(thd,error)) && ! error)
- error=error2;
+ table->file->ha_release_auto_increment();
+
if (error)
{
table->file->print_error(error,MYF(0));
@@ -3028,69 +3206,70 @@ bool select_insert::send_eof()
thd->row_count_func= info.copied + info.deleted +
((thd->client_capabilities & CLIENT_FOUND_ROWS) ?
info.touched : info.updated);
- id= autoinc_value_of_first_inserted_row > 0 ?
- autoinc_value_of_first_inserted_row : thd->insert_id_used ?
- thd->last_insert_id : 0;
- ::send_ok(thd, (ulong) thd->row_count_func, id, buff);
+
+ 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);
DBUG_RETURN(0);
}
-void select_insert::abort()
-{
- bool changed, transactional_table;
- DBUG_ENTER("select_insert::abort");
+void select_insert::abort() {
- if (!table)
+ DBUG_ENTER("select_insert::abort");
+ /*
+ If the creation of the table failed (due to a syntax error, for
+ example), no table will have been opened and therefore 'table'
+ will be NULL. In that case, we still need to execute the rollback
+ and the end of the function.
+ */
+ if (table)
{
+ bool changed, transactional_table;
/*
- This can only happen when using CREATE ... SELECT and the table was not
- created becasue of an syntax error
+ If we are not in prelocked mode, we end the bulk insert started
+ before.
*/
- DBUG_VOID_RETURN;
- }
- changed= (info.copied || info.deleted || info.updated);
- transactional_table= table->file->has_transactions();
- if (!thd->prelocked_mode)
- table->file->end_bulk_insert();
- /*
- If at least one row has been inserted/modified and will stay in the table
- (the table doesn't have transactions) (example: we got a duplicate key
- error while inserting into a MyISAM table) we must write to the binlog (and
- the error code will make the slave stop).
- */
- if (thd->transaction.stmt.modified_non_trans_table)
- {
- // For binary log
- if (autoinc_value_of_last_inserted_row)
- {
- if (info.copied)
- thd->insert_id(autoinc_value_of_last_inserted_row);
- else
- {
- autoinc_value_of_first_inserted_row= 0;
- thd->insert_id(0);
- }
- }
- if (mysql_bin_log.is_open())
+ if (!thd->prelocked_mode)
+ table->file->ha_end_bulk_insert();
+
+ /*
+ If at least one row has been inserted/modified and will stay in
+ the table (the table doesn't have transactions) we must write to
+ the binlog (and the error code will make the slave stop).
+
+ For many errors (example: we got a duplicate key error while
+ inserting into a MyISAM table), no row will be added to the table,
+ so passing the error to the slave will not help since there will
+ be an error code mismatch (the inserts will succeed on the slave
+ with no error).
+
+ If table creation failed, the number of rows modified will also be
+ zero, so no check for that is made.
+ */
+ changed= (info.copied || info.deleted || info.updated);
+ transactional_table= table->file->has_transactions();
+ if (thd->transaction.stmt.modified_non_trans_table)
{
- Query_log_event qinfo(thd, thd->query, thd->query_length,
+ if (mysql_bin_log.is_open())
+ thd->binlog_query(THD::ROW_QUERY_TYPE, thd->query, thd->query_length,
transactional_table, FALSE);
- mysql_bin_log.write(&qinfo);
+ 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);
}
- 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);
+ table->file->ha_release_auto_increment();
}
- DBUG_ASSERT(transactional_table || !changed || thd->transaction.stmt.modified_non_trans_table);
- if (changed)
- {
- query_cache_invalidate3(thd, table, 1);
- }
- ha_rollback_stmt(thd);
DBUG_VOID_RETURN;
-
}
+
/***************************************************************************
CREATE TABLE (SELECT) ...
***************************************************************************/
@@ -3116,6 +3295,7 @@ void select_insert::abort()
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:
@@ -3140,9 +3320,11 @@ static TABLE *create_table_from_items(THD *thd, HA_CREATE_INFO *create_info,
TABLE_LIST *create_table,
Alter_info *alter_info,
List<Item> *items,
- MYSQL_LOCK **lock)
+ MYSQL_LOCK **lock,
+ TABLEOP_HOOKS *hooks)
{
- TABLE tmp_table; // Used during 'create_field()'
+ TABLE tmp_table; // Used during 'Create_field()'
+ TABLE_SHARE share;
TABLE *table= 0;
uint select_field_count= items->elements;
/* Add selected items to field list */
@@ -3173,28 +3355,31 @@ static TABLE *create_table_from_items(THD *thd, HA_CREATE_INFO *create_info,
tmp_table.alias= 0;
tmp_table.timestamp_field= 0;
- tmp_table.s= &tmp_table.share_not_to_be_used;
+ tmp_table.s= &share;
+ init_tmp_table_share(thd, &share, "", 0, "", "");
+
tmp_table.s->db_create_options=0;
tmp_table.s->blob_ptr_size= portable_sizeof_char_ptr;
- tmp_table.s->db_low_byte_first= test(create_info->db_type == DB_TYPE_MYISAM ||
- create_info->db_type == DB_TYPE_HEAP);
+ tmp_table.s->db_low_byte_first=
+ test(create_info->db_type == myisam_hton ||
+ create_info->db_type == heap_hton);
tmp_table.null_row=tmp_table.maybe_null=0;
while ((item=it++))
{
- create_field *cr_field;
+ Create_field *cr_field;
Field *field, *def_field;
if (item->type() == Item::FUNC_ITEM)
if (item->result_type() != STRING_RESULT)
field= item->tmp_table_field(&tmp_table);
else
- field= item->tmp_table_field_from_field_type(&tmp_table);
+ field= item->tmp_table_field_from_field_type(&tmp_table, 0);
else
field= create_tmp_field(thd, &tmp_table, item, item->type(),
(Item ***) 0, &tmp_field, &def_field, 0, 0, 0, 0,
0);
if (!field ||
- !(cr_field=new create_field(field,(item->type() == Item::FIELD_ITEM ?
+ !(cr_field=new Create_field(field,(item->type() == Item::FIELD_ITEM ?
((Item_field *)item)->field :
(Field*) 0))))
DBUG_RETURN(0);
@@ -3223,10 +3408,11 @@ static TABLE *create_table_from_items(THD *thd, HA_CREATE_INFO *create_info,
*/
{
tmp_disable_binlog(thd);
- if (!mysql_create_table(thd, create_table->db, create_table->table_name,
- create_info, alter_info, 0, select_field_count))
+ if (!mysql_create_table_no_lock(thd, create_table->db,
+ create_table->table_name,
+ create_info, alter_info, 0,
+ select_field_count))
{
-
if (create_info->table_existed &&
!(create_info->options & HA_LEX_CREATE_TMP_TABLE))
{
@@ -3247,8 +3433,8 @@ static TABLE *create_table_from_items(THD *thd, HA_CREATE_INFO *create_info,
if (reopen_name_locked_table(thd, create_table, FALSE))
{
quick_rm_table(create_info->db_type, create_table->db,
- table_case_name(create_info,
- create_table->table_name));
+ table_case_name(create_info, create_table->table_name),
+ 0);
}
else
table= create_table->table;
@@ -3265,7 +3451,7 @@ static TABLE *create_table_from_items(THD *thd, HA_CREATE_INFO *create_info,
it preparable for open. But let us do close_temporary_table() here
just in case.
*/
- close_temporary_table(thd, create_table->db, create_table->table_name);
+ drop_temporary_table(thd, create_table);
}
}
}
@@ -3277,9 +3463,17 @@ static TABLE *create_table_from_items(THD *thd, HA_CREATE_INFO *create_info,
DBUG_EXECUTE_IF("sleep_create_select_before_lock", my_sleep(6000000););
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_IGNORE_FLUSH, &not_used)) ||
+ hooks->postlock(&table, 1))
{
+ if (*lock)
+ {
+ mysql_unlock_tables(thd, *lock);
+ *lock= 0;
+ }
+
if (!create_info->table_existed)
drop_open_table(thd, table, create_table->db, create_table->table_name);
DBUG_RETURN(0);
@@ -3291,56 +3485,173 @@ static TABLE *create_table_from_items(THD *thd, HA_CREATE_INFO *create_info,
int
select_create::prepare(List<Item> &values, SELECT_LEX_UNIT *u)
{
+ MYSQL_LOCK *extra_lock= NULL;
DBUG_ENTER("select_create::prepare");
+ TABLEOP_HOOKS *hook_ptr= NULL;
+ /*
+ For row-based replication, the CREATE-SELECT statement is written
+ in two pieces: the first one contain the CREATE TABLE statement
+ necessary to create the table and the second part contain the rows
+ that should go into the table.
+
+ For non-temporary tables, the start of the CREATE-SELECT
+ implicitly commits the previous transaction, and all events
+ forming the statement will be stored the transaction cache. At end
+ of the statement, the entire statement is committed as a
+ transaction, and all events are written to the binary log.
+
+ On the master, the table is locked for the duration of the
+ statement, but since the CREATE part is replicated as a simple
+ statement, there is no way to lock the table for accesses on the
+ slave. Hence, we have to hold on to the CREATE part of the
+ statement until the statement has finished.
+ */
+ 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)
+ {
+ all_tables.next_global= select_tables;
+ }
+
+ private:
+ virtual int do_postlock(TABLE **tables, uint count)
+ {
+ THD *thd= const_cast<THD*>(ptr->get_thd());
+ if (int error= decide_logging_format(thd, &all_tables))
+ return error;
+
+ TABLE const *const table = *tables;
+ if (thd->current_stmt_binlog_row_based &&
+ !table->s->tmp_table &&
+ !ptr->get_create_info()->table_existed)
+ {
+ ptr->binlog_show_create_table(tables, count);
+ }
+ return 0;
+ }
+
+ select_create *ptr;
+ TABLE_LIST all_tables;
+ };
+
+ MY_HOOKS hooks(this, create_table, select_tables);
+ hook_ptr= &hooks;
+
unit= u;
- table= create_table_from_items(thd, create_info, create_table,
- alter_info, &values, &lock);
- if (!table)
+
+ /*
+ Start a statement transaction before the create if we are using
+ row-based replication for the statement. If we are creating a
+ temporary table, we need to start a statement transaction.
+ */
+ if ((thd->lex->create_info.options & HA_LEX_CREATE_TMP_TABLE) == 0 &&
+ thd->current_stmt_binlog_row_based &&
+ mysql_bin_log.is_open())
+ {
+ thd->binlog_start_trans_and_stmt();
+ }
+
+ if (!(table= create_table_from_items(thd, create_info, create_table,
+ alter_info, &values,
+ &extra_lock, hook_ptr)))
DBUG_RETURN(-1); // abort() deletes table
+ if (extra_lock)
+ {
+ DBUG_ASSERT(m_plock == NULL);
+
+ if (create_info->options & HA_LEX_CREATE_TMP_TABLE)
+ m_plock= &m_lock;
+ else
+ m_plock= &thd->extra_lock;
+
+ *m_plock= extra_lock;
+ }
+
if (table->s->fields < values.elements)
{
my_error(ER_WRONG_VALUE_COUNT_ON_ROW, MYF(0), 1);
DBUG_RETURN(-1);
}
- /* First field to copy */
+ /* First field to copy */
field= table->field+table->s->fields - values.elements;
/* Mark all fields that are given values */
for (Field **f= field ; *f ; f++)
- (*f)->query_id= thd->query_id;
+ bitmap_set_bit(table->write_set, (*f)->field_index);
/* Don't set timestamp if used */
table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET;
-
table->next_number_field=table->found_next_number_field;
restore_record(table,s->default_values); // Get empty record
thd->cuted_fields=0;
if (info.ignore || info.handle_duplicates != DUP_ERROR)
table->file->extra(HA_EXTRA_IGNORE_DUP_KEY);
- if (info.handle_duplicates == DUP_REPLACE)
- {
- if (!table->triggers || !table->triggers->has_delete_triggers())
- table->file->extra(HA_EXTRA_WRITE_CAN_REPLACE);
- table->file->extra(HA_EXTRA_RETRIEVE_ALL_COLS);
- }
+ if (info.handle_duplicates == DUP_REPLACE &&
+ (!table->triggers || !table->triggers->has_delete_triggers()))
+ 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)
- table->file->start_bulk_insert((ha_rows) 0);
+ table->file->ha_start_bulk_insert((ha_rows) 0);
thd->abort_on_warning= (!info.ignore &&
(thd->variables.sql_mode &
(MODE_STRICT_TRANS_TABLES |
MODE_STRICT_ALL_TABLES)));
if (check_that_all_fields_are_given_values(thd, table, table_list))
DBUG_RETURN(1);
+ table->mark_columns_needed_for_insert();
table->file->extra(HA_EXTRA_WRITE_CACHE);
DBUG_RETURN(0);
}
+void
+select_create::binlog_show_create_table(TABLE **tables, uint count)
+{
+ /*
+ Note 1: In RBR mode, we generate a CREATE TABLE statement for the
+ created table by calling store_create_info() (behaves as SHOW
+ CREATE TABLE). In the event of an error, nothing should be
+ written to the binary log, even if the table is non-transactional;
+ therefore we pretend that the generated CREATE TABLE statement is
+ for a transactional table. The event will then be put in the
+ transaction cache, and any subsequent events (e.g., table-map
+ events and binrow events) will also be put there. We can then use
+ ha_autocommit_or_rollback() to either throw away the entire
+ kaboodle of events, or write them to the binary log.
+
+ We write the CREATE TABLE statement here and not in prepare()
+ since there potentially are sub-selects or accesses to information
+ schema that will do a close_thread_tables(), destroying the
+ statement transaction cache.
+ */
+ DBUG_ASSERT(thd->current_stmt_binlog_row_based);
+ DBUG_ASSERT(tables && *tables && count > 0);
+
+ char buf[2048];
+ String query(buf, sizeof(buf), system_charset_info);
+ int result;
+ TABLE_LIST tmp_table_list;
+
+ memset(&tmp_table_list, 0, sizeof(tmp_table_list));
+ tmp_table_list.table = *tables;
+ query.length(0); // Have to zero it since constructor doesn't
+
+ result= store_create_info(thd, &tmp_table_list, &query, create_info,
+ /* show_database */ TRUE);
+ DBUG_ASSERT(result == 0); /* store_create_info() always return 0 */
+
+ if (mysql_bin_log.is_open())
+ thd->binlog_query(THD::STMT_QUERY_TYPE,
+ query.ptr(), query.length(),
+ /* is_trans */ TRUE,
+ /* suppress_use */ FALSE);
+}
void select_create::store_values(List<Item> &values)
{
@@ -3351,7 +3662,35 @@ void select_create::store_values(List<Item> &values)
void select_create::send_error(uint errcode,const char *err)
{
+ DBUG_ENTER("select_create::send_error");
+
+ DBUG_PRINT("info",
+ ("Current statement %s row-based",
+ thd->current_stmt_binlog_row_based ? "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.
+
+ We disable the binary log since nothing should be written to the
+ binary log. This disabling is important, since we potentially do
+ a "roll back" of non-transactional tables by removing the table,
+ and the actual rollback might generate events that should not be
+ written to the binary log.
+
+ */
+ tmp_disable_binlog(thd);
select_insert::send_error(errcode, err);
+ reenable_binlog(thd);
+
+ DBUG_VOID_RETURN;
}
@@ -3362,12 +3701,24 @@ bool select_create::send_eof()
abort();
else
{
+ /*
+ Do an implicit commit at end of statement for non-temporary
+ tables. This can fail, but we should unlock the table
+ nevertheless.
+ */
+ if (!table->s->tmp_table)
+ {
+ ha_autocommit_or_rollback(thd, 0);
+ end_active_trans(thd);
+ }
+
table->file->extra(HA_EXTRA_NO_IGNORE_DUP_KEY);
table->file->extra(HA_EXTRA_WRITE_CANNOT_REPLACE);
- if (lock)
+ if (m_plock)
{
- mysql_unlock_tables(thd, lock);
- lock= 0;
+ mysql_unlock_tables(thd, *m_plock);
+ *m_plock= NULL;
+ m_plock= NULL;
}
}
return tmp;
@@ -3376,27 +3727,45 @@ bool select_create::send_eof()
void select_create::abort()
{
+ DBUG_ENTER("select_create::abort");
+
/*
- Disable binlog, because we "roll back" partial inserts in ::abort
- by removing the table, even for non-transactional tables.
+ In select_insert::abort() we roll back the statement, including
+ truncating the transaction cache of the binary log. To do this, we
+ pretend that the statement is transactional, even though it might
+ be the case that it was not.
+
+ We roll back the statement prior to deleting the table and prior
+ to releasing the lock on the table, since there might be potential
+ for failure if the rollback is executed after the drop or after
+ unlocking the table.
+
+ We also roll back the statement regardless of whether the creation
+ of the table succeeded or not, since we need to reset the binary
+ log state.
*/
tmp_disable_binlog(thd);
select_insert::abort();
+ thd->transaction.stmt.modified_non_trans_table= FALSE;
reenable_binlog(thd);
+ thd->binlog_flush_pending_rows_event(TRUE);
- if (lock)
+ if (m_plock)
{
- mysql_unlock_tables(thd, lock);
- lock=0;
+ mysql_unlock_tables(thd, *m_plock);
+ *m_plock= NULL;
+ m_plock= NULL;
}
+
if (table)
{
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=0;
+ table=0; // Safety
}
+ DBUG_VOID_RETURN;
}
diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc
index 4c53772bc25..8899acd37ec 100644
--- a/sql/sql_lex.cc
+++ b/sql/sql_lex.cc
@@ -1,4 +1,4 @@
-/* Copyright (C) 2000-2006 MySQL AB
+/* Copyright 2000-2008 MySQL AB, 2008 Sun Microsystems, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -28,17 +28,8 @@
We are using pointer to this variable for distinguishing between assignment
to NEW row field (when parsing trigger definition) and structured variable.
*/
-sys_var_long_ptr trg_new_row_fake_var(0, 0);
-/* Macros to look like lex */
-
-#define yyGet() *(lip->ptr++)
-#define yyGetLast() lip->ptr[-1]
-#define yyPeek() lip->ptr[0]
-#define yyPeek2() lip->ptr[1]
-#define yyUnget() lip->ptr--
-#define yySkip() lip->ptr++
-#define yyLength() ((uint) (lip->ptr - lip->tok_start)-1)
+sys_var *trg_new_row_fake_var= (sys_var*) 0x01;
/* Longest standard keyword name */
#define TOCK_NAME_LENGTH 24
@@ -68,6 +59,17 @@ static uchar to_upper_lex[]=
208,209,210,211,212,213,214,247,216,217,218,219,220,221,222,255
};
+/*
+ Names of the index hints (for error messages). Keep in sync with
+ index_hint_type
+*/
+
+const char * index_hint_type_name[] =
+{
+ "IGNORE INDEX",
+ "USE INDEX",
+ "FORCE INDEX"
+};
inline int lex_casecmp(const char *s, const char *t, uint len)
{
@@ -76,7 +78,7 @@ inline int lex_casecmp(const char *s, const char *t, uint len)
return (int) len+1;
}
-#include "lex_hash.h"
+#include <lex_hash.h>
void lex_init(void)
@@ -115,22 +117,160 @@ Lex_input_stream::Lex_input_stream(THD *thd,
yylineno(1),
yytoklen(0),
yylval(NULL),
- ptr(buffer),
- tok_start(NULL),
- tok_end(NULL),
- end_of_query(buffer + length),
- tok_start_prev(NULL),
- buf(buffer),
+ 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(thd->variables.sql_mode & MODE_IGNORE_SPACE)),
- stmt_prepare_mode(FALSE)
+ stmt_prepare_mode(FALSE),
+ in_comment(NO_COMMENT),
+ m_underscore_cs(NULL)
{
+ m_cpp_buf= (char*) thd->alloc(length + 1);
+ m_cpp_ptr= m_cpp_buf;
}
Lex_input_stream::~Lex_input_stream()
{}
+/**
+ 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
+ statement;
+ 2) Determine the beginning of the body.
+
+ @param thd Thread context.
+ @param begin_ptr Pointer to the start of the body in the pre-processed
+ buffer.
+*/
+
+void Lex_input_stream::body_utf8_start(THD *thd, const char *begin_ptr)
+{
+ DBUG_ASSERT(begin_ptr);
+ DBUG_ASSERT(m_cpp_buf <= begin_ptr && begin_ptr <= m_cpp_buf + m_buf_length);
+
+ uint body_utf8_length=
+ (m_buf_length / thd->variables.character_set_client->mbminlen) *
+ my_charset_utf8_bin.mbmaxlen;
+
+ m_body_utf8= (char *) thd->alloc(body_utf8_length + 1);
+ m_body_utf8_ptr= m_body_utf8;
+ *m_body_utf8_ptr= 0;
+
+ m_cpp_utf8_processed_ptr= begin_ptr;
+}
+
+/**
+ @brief The operation appends unprocessed part of pre-processed buffer till
+ the given pointer (ptr) and sets m_cpp_utf8_processed_ptr to end_ptr.
+
+ The idea is that some tokens in the pre-processed buffer (like character
+ set introducers) should be skipped.
+
+ Example:
+ CPP buffer: SELECT 'str1', _latin1 'str2';
+ m_cpp_utf8_processed_ptr -- points at the "SELECT ...";
+ In order to skip "_latin1", the following call should be made:
+ body_utf8_append(<pointer to "_latin1 ...">, <pointer to " 'str2'...">)
+
+ @param ptr Pointer in the pre-processed buffer, which specifies the
+ end of the chunk, which should be appended to the utf8
+ body.
+ @param end_ptr Pointer in the pre-processed buffer, to which
+ m_cpp_utf8_processed_ptr will be set in the end of the
+ operation.
+*/
+
+void Lex_input_stream::body_utf8_append(const char *ptr,
+ const char *end_ptr)
+{
+ DBUG_ASSERT(m_cpp_buf <= ptr && ptr <= m_cpp_buf + m_buf_length);
+ DBUG_ASSERT(m_cpp_buf <= end_ptr && end_ptr <= m_cpp_buf + m_buf_length);
+
+ if (!m_body_utf8)
+ return;
+
+ if (m_cpp_utf8_processed_ptr >= ptr)
+ return;
+
+ int bytes_to_copy= ptr - m_cpp_utf8_processed_ptr;
+
+ memcpy(m_body_utf8_ptr, m_cpp_utf8_processed_ptr, bytes_to_copy);
+ m_body_utf8_ptr += bytes_to_copy;
+ *m_body_utf8_ptr= 0;
+
+ m_cpp_utf8_processed_ptr= end_ptr;
+}
+
+/**
+ The operation appends unprocessed part of the pre-processed buffer till
+ the given pointer (ptr) and sets m_cpp_utf8_processed_ptr to ptr.
+
+ @param ptr Pointer in the pre-processed buffer, which specifies the end
+ of the chunk, which should be appended to the utf8 body.
+*/
+
+void Lex_input_stream::body_utf8_append(const char *ptr)
+{
+ body_utf8_append(ptr, ptr);
+}
+
+/**
+ The operation converts the specified text literal to the utf8 and appends
+ the result to the utf8-body.
+
+ @param thd Thread context.
+ @param txt Text literal.
+ @param txt_cs Character set of the text literal.
+ @param end_ptr Pointer in the pre-processed buffer, to which
+ m_cpp_utf8_processed_ptr will be set in the end of the
+ operation.
+*/
+
+void Lex_input_stream::body_utf8_append_literal(THD *thd,
+ const LEX_STRING *txt,
+ CHARSET_INFO *txt_cs,
+ const char *end_ptr)
+{
+ if (!m_cpp_utf8_processed_ptr)
+ return;
+
+ LEX_STRING utf_txt;
+
+ if (!my_charset_same(txt_cs, &my_charset_utf8_general_ci))
+ {
+ thd->convert_string(&utf_txt,
+ &my_charset_utf8_general_ci,
+ txt->str, (uint) txt->length,
+ txt_cs);
+ }
+ else
+ {
+ utf_txt.str= txt->str;
+ utf_txt.length= txt->length;
+ }
+
+ /* NOTE: utf_txt.length is in bytes, not in symbols. */
+
+ memcpy(m_body_utf8_ptr, utf_txt.str, utf_txt.length);
+ m_body_utf8_ptr += utf_txt.length;
+ *m_body_utf8_ptr= 0;
+
+ m_cpp_utf8_processed_ptr= end_ptr;
+}
+
/*
This is called before every query that is to be parsed.
@@ -153,6 +293,7 @@ void lex_start(THD *thd)
lex->select_lex.init_query();
lex->value_list.empty();
lex->update_list.empty();
+ lex->set_var_list.empty();
lex->param_list.empty();
lex->view_list.empty();
lex->prepared_stmt_params.empty();
@@ -176,55 +317,82 @@ void lex_start(THD *thd)
lex->derived_tables= 0;
lex->lock_option= TL_READ;
lex->safe_to_cache_query= 1;
- lex->time_zone_tables_used= 0;
lex->leaf_tables_insert= 0;
lex->parsing_options.reset();
lex->empty_field_list_on_rset= 0;
lex->select_lex.select_number= 1;
-
- lex->in_comment=0;
lex->length=0;
+ lex->part_info= 0;
lex->select_lex.in_sum_expr=0;
- lex->select_lex.expr_list.empty();
lex->select_lex.ftfunc_list_alloc.empty();
lex->select_lex.ftfunc_list= &lex->select_lex.ftfunc_list_alloc;
lex->select_lex.group_list.empty();
lex->select_lex.order_list.empty();
- lex->select_lex.udf_list.empty();
- lex->current_select= &lex->select_lex;
- lex->sql_command= lex->orig_sql_command= SQLCOM_END;
+ lex->sql_command= SQLCOM_END;
lex->duplicates= DUP_ERROR;
lex->ignore= 0;
+ lex->spname= NULL;
lex->sphead= NULL;
lex->spcont= NULL;
lex->proc_list.first= 0;
lex->escape_used= FALSE;
+ lex->query_tables= 0;
lex->reset_query_tables_list(FALSE);
+ lex->expr_allows_subselect= TRUE;
+ lex->use_only_table_context= FALSE;
+ lex->name.str= 0;
+ lex->name.length= 0;
+ lex->event_parse_data= NULL;
+ lex->profile_options= PROFILE_NONE;
lex->nest_level=0 ;
lex->allow_sum_func= 0;
lex->in_sum_func= NULL;
+ /*
+ ok, there must be a better solution for this, long-term
+ I tried "bzero" in the sql_yacc.yy code, but that for
+ some reason made the values zero, even if they were set
+ */
+ lex->server_options.server_name= 0;
+ lex->server_options.server_name_length= 0;
+ lex->server_options.host= 0;
+ lex->server_options.db= 0;
+ lex->server_options.username= 0;
+ lex->server_options.password= 0;
+ lex->server_options.scheme= 0;
+ lex->server_options.socket= 0;
+ lex->server_options.owner= 0;
+ lex->server_options.port= -1;
+
+ lex->is_lex_started= TRUE;
DBUG_VOID_RETURN;
}
void lex_end(LEX *lex)
{
- /* Empty in 5.0, non empty in 5.1 */
+ DBUG_ENTER("lex_end");
+ DBUG_PRINT("enter", ("lex: 0x%lx", (long) lex));
+
+ /* release used plugins */
+ plugin_unlock_list(0, (plugin_ref*)lex->plugins.buffer,
+ lex->plugins.elements);
+ reset_dynamic(&lex->plugins);
+
+ DBUG_VOID_RETURN;
}
Yacc_state::~Yacc_state()
{
if (yacc_yyss)
{
- x_free(yacc_yyss);
- x_free(yacc_yyvs);
+ my_free(yacc_yyss, MYF(0));
+ my_free(yacc_yyvs, MYF(0));
}
}
-
static int find_keyword(Lex_input_stream *lip, uint len, bool function)
{
- const char *tok= lip->tok_start;
+ const char *tok= lip->get_tok_start();
SYMBOL *symbol= get_hash_symbol(tok, len, function);
if (symbol)
@@ -232,14 +400,14 @@ static int find_keyword(Lex_input_stream *lip, uint len, bool function)
lip->yylval->symbol.symbol=symbol;
lip->yylval->symbol.str= (char*) tok;
lip->yylval->symbol.length=len;
-
+
if ((symbol->tok == NOT_SYM) &&
(lip->m_thd->variables.sql_mode & MODE_HIGH_NOT_PRECEDENCE))
return NOT2_SYM;
if ((symbol->tok == OR_OR_SYM) &&
!(lip->m_thd->variables.sql_mode & MODE_PIPES_AS_CONCAT))
return OR2_SYM;
-
+
return symbol->tok;
}
return 0;
@@ -264,14 +432,24 @@ bool is_keyword(const char *name, uint len)
return get_hash_symbol(name,len,0)!=0;
}
+bool is_lex_native_function(const LEX_STRING *name)
+{
+ DBUG_ASSERT(name != NULL);
+ return (get_hash_symbol(name->str, (uint) name->length, 1) != 0);
+}
+
/* make a copy of token before ptr and set yytoklen */
static LEX_STRING get_token(Lex_input_stream *lip, uint skip, uint length)
{
LEX_STRING tmp;
- yyUnget(); // ptr points now after last token char
+ lip->yyUnget(); // ptr points now after last token char
tmp.length=lip->yytoklen=length;
- tmp.str= lip->m_thd->strmake(lip->tok_start + skip, tmp.length);
+ tmp.str= lip->m_thd->strmake(lip->get_tok_start() + skip, tmp.length);
+
+ lip->m_cpp_text_start= lip->get_cpp_tok_start() + skip;
+ lip->m_cpp_text_end= lip->m_cpp_text_start + tmp.length;
+
return tmp;
}
@@ -287,17 +465,25 @@ static LEX_STRING get_quoted_token(Lex_input_stream *lip,
uint length, char quote)
{
LEX_STRING tmp;
- byte *from, *to, *end;
- yyUnget(); // ptr points now after last token char
- tmp.length=lip->yytoklen=length;
+ const char *from, *end;
+ char *to;
+ lip->yyUnget(); // ptr points now after last token char
+ tmp.length= lip->yytoklen=length;
tmp.str=(char*) lip->m_thd->alloc(tmp.length+1);
- from= (byte*) lip->tok_start + skip;
- to= (byte*) tmp.str;
+ from= lip->get_tok_start() + skip;
+ to= tmp.str;
end= to+length;
+
+ lip->m_cpp_text_start= lip->get_cpp_tok_start() + skip;
+ lip->m_cpp_text_end= lip->m_cpp_text_start + length;
+
for ( ; to != end; )
{
if ((*to++= *from++) == quote)
+ {
from++; // Skip double quotes
+ lip->m_cpp_text_start++;
+ }
}
*to= 0; // End null for safety
return tmp;
@@ -309,57 +495,65 @@ static LEX_STRING get_quoted_token(Lex_input_stream *lip,
Fix sometimes to do only one scan of the string
*/
-static char *get_text(Lex_input_stream *lip)
+static char *get_text(Lex_input_stream *lip, int pre_skip, int post_skip)
{
reg1 uchar c,sep;
uint found_escape=0;
CHARSET_INFO *cs= lip->m_thd->charset();
lip->tok_bitmap= 0;
- sep= yyGetLast(); // String should end with this
- while (lip->ptr != lip->end_of_query)
+ sep= lip->yyGetLast(); // String should end with this
+ while (! lip->eof())
{
- c= yyGet();
+ c= lip->yyGet();
lip->tok_bitmap|= c;
#ifdef USE_MB
{
int l;
if (use_mb(cs) &&
(l = my_ismbchar(cs,
- lip->ptr-1,
- lip->end_of_query))) {
- lip->ptr += l-1;
- continue;
+ lip->get_ptr() -1,
+ lip->get_end_of_query()))) {
+ lip->skip_binary(l-1);
+ continue;
}
}
#endif
if (c == '\\' &&
- !(lip->m_thd->variables.sql_mode & MODE_NO_BACKSLASH_ESCAPES))
+ !(lip->m_thd->variables.sql_mode & MODE_NO_BACKSLASH_ESCAPES))
{ // Escaped character
found_escape=1;
- if (lip->ptr == lip->end_of_query)
+ if (lip->eof())
return 0;
- yySkip();
+ lip->yySkip();
}
else if (c == sep)
{
- if (c == yyGet()) // Check if two separators in a row
+ if (c == lip->yyGet()) // Check if two separators in a row
{
- found_escape=1; // dupplicate. Remember for delete
+ found_escape=1; // duplicate. Remember for delete
continue;
}
else
- yyUnget();
+ lip->yyUnget();
/* Found end. Unescape and return string */
- const char *str;
- const char *end;
+ const char *str, *end;
char *start;
- str=lip->tok_start+1;
- end=lip->ptr-1;
- if (!(start=(char*) lip->m_thd->alloc((uint) (end-str)+1)))
+ str= lip->get_tok_start();
+ end= lip->get_ptr();
+ /* Extract the text from the token */
+ str += pre_skip;
+ end -= post_skip;
+ DBUG_ASSERT(end >= str);
+
+ if (!(start= (char*) lip->m_thd->alloc((uint) (end-str)+1)))
return (char*) ""; // Sql_alloc has set error flag
+
+ lip->m_cpp_text_start= lip->get_cpp_tok_start() + pre_skip;
+ lip->m_cpp_text_end= lip->get_cpp_ptr() - post_skip;
+
if (!found_escape)
{
lip->yytoklen=(uint) (end-str);
@@ -368,15 +562,14 @@ static char *get_text(Lex_input_stream *lip)
}
else
{
- char *to;
+ char *to;
for (to=start ; str != end ; str++)
{
#ifdef USE_MB
int l;
if (use_mb(cs) &&
- (l = my_ismbchar(cs,
- (const char *)str, (const char *)end))) {
+ (l = my_ismbchar(cs, str, end))) {
while (l--)
*to++ = *str++;
str--;
@@ -422,7 +615,7 @@ static char *get_text(Lex_input_stream *lip)
*to=0;
lip->yytoklen=(uint) (to-start);
}
- return (char*) start;
+ return start;
}
}
return 0; // unexpected end of query
@@ -544,9 +737,7 @@ int MYSQLlex(void *arg, void *yythd)
lip->yylval=yylval; // The global state
- lip->tok_start_prev= lip->tok_start;
-
- lip->tok_start=lip->tok_end=lip->ptr;
+ lip->start_token();
state=lip->next_state;
lip->next_state=MY_LEX_OPERATOR_OR_IDENT;
LINT_INIT(c);
@@ -555,17 +746,22 @@ int MYSQLlex(void *arg, void *yythd)
switch (state) {
case MY_LEX_OPERATOR_OR_IDENT: // Next is operator or keyword
case MY_LEX_START: // Start of token
- // Skip startspace
- for (c=yyGet() ; (state_map[c] == MY_LEX_SKIP) ; c= yyGet())
+ // Skip starting whitespace
+ while(state_map[c= lip->yyPeek()] == MY_LEX_SKIP)
{
if (c == '\n')
lip->yylineno++;
+
+ lip->yySkip();
}
- lip->tok_start=lip->ptr-1; // Start of real token
+
+ /* Start of real token */
+ lip->restart_token();
+ c= lip->yyGet();
state= (enum my_lex_states) state_map[c];
break;
case MY_LEX_ESCAPE:
- if (yyGet() == 'N')
+ if (lip->yyGet() == 'N')
{ // Allow \N as shortcut for NULL
yylval->lex_str.str=(char*) "\\N";
yylval->lex_str.length=2;
@@ -573,40 +769,53 @@ int MYSQLlex(void *arg, void *yythd)
}
case MY_LEX_CHAR: // Unknown or single char token
case MY_LEX_SKIP: // This should not happen
- if (c == '-' && yyPeek() == '-' &&
- (my_isspace(cs,yyPeek2()) ||
- my_iscntrl(cs,yyPeek2())))
+ if (c == '-' && lip->yyPeek() == '-' &&
+ (my_isspace(cs,lip->yyPeekn(1)) ||
+ my_iscntrl(cs,lip->yyPeekn(1))))
{
state=MY_LEX_COMMENT;
break;
}
- yylval->lex_str.str=(char*) (lip->ptr=lip->tok_start);// Set to first chr
- yylval->lex_str.length=1;
- c=yyGet();
+
if (c != ')')
lip->next_state= MY_LEX_START; // Allow signed numbers
+
if (c == ',')
- lip->tok_start=lip->ptr; // Let tok_start point at next item
- /*
- Check for a placeholder: it should not precede a possible identifier
- because of binlogging: when a placeholder is replaced with
- its value in a query for the binlog, the query must stay
- grammatically correct.
- */
- else if (c == '?' && lip->stmt_prepare_mode && !ident_map[yyPeek()])
+ {
+ /*
+ Warning:
+ This is a work around, to make the "remember_name" rule in
+ sql/sql_yacc.yy work properly.
+ The problem is that, when parsing "select expr1, expr2",
+ the code generated by bison executes the *pre* action
+ remember_name (see select_item) *before* actually parsing the
+ first token of expr2.
+ */
+ lip->restart_token();
+ }
+ else
+ {
+ /*
+ Check for a placeholder: it should not precede a possible identifier
+ because of binlogging: when a placeholder is replaced with
+ its value in a query for the binlog, the query must stay
+ grammatically correct.
+ */
+ if (c == '?' && lip->stmt_prepare_mode && !ident_map[lip->yyPeek()])
return(PARAM_MARKER);
+ }
+
return((int) c);
case MY_LEX_IDENT_OR_NCHAR:
- if (yyPeek() != '\'')
- {
+ if (lip->yyPeek() != '\'')
+ {
state= MY_LEX_IDENT;
break;
}
/* Found N'string' */
- lip->tok_start++; // Skip N
- yySkip(); // Skip '
- if (!(yylval->lex_str.str = get_text(lip)))
+ lip->yySkip(); // Skip '
+ if (!(yylval->lex_str.str = get_text(lip, 2, 1)))
{
state= MY_LEX_CHAR; // Read char by char
break;
@@ -616,13 +825,13 @@ int MYSQLlex(void *arg, void *yythd)
return(NCHAR_STRING);
case MY_LEX_IDENT_OR_HEX:
- if (yyPeek() == '\'')
+ if (lip->yyPeek() == '\'')
{ // Found x'hex-number'
state= MY_LEX_HEX_NUMBER;
break;
}
case MY_LEX_IDENT_OR_BIN:
- if (yyPeek() == '\'')
+ if (lip->yyPeek() == '\'')
{ // Found b'bin-number'
state= MY_LEX_BIN_NUMBER;
break;
@@ -633,86 +842,133 @@ int MYSQLlex(void *arg, void *yythd)
if (use_mb(cs))
{
result_state= IDENT_QUOTED;
- if (my_mbcharlen(cs, yyGetLast()) > 1)
+ if (my_mbcharlen(cs, lip->yyGetLast()) > 1)
{
int l = my_ismbchar(cs,
- lip->ptr-1,
- lip->end_of_query);
+ lip->get_ptr() -1,
+ lip->get_end_of_query());
if (l == 0) {
state = MY_LEX_CHAR;
continue;
}
- lip->ptr += l - 1;
+ lip->skip_binary(l - 1);
}
- while (ident_map[c=yyGet()])
+ while (ident_map[c=lip->yyGet()])
{
if (my_mbcharlen(cs, c) > 1)
{
int l;
if ((l = my_ismbchar(cs,
- lip->ptr-1,
- lip->end_of_query)) == 0)
+ lip->get_ptr() -1,
+ lip->get_end_of_query())) == 0)
break;
- lip->ptr += l-1;
+ lip->skip_binary(l-1);
}
}
}
else
#endif
{
- for (result_state= c; ident_map[c= yyGet()]; result_state|= c);
+ for (result_state= c; ident_map[c= lip->yyGet()]; result_state|= c);
/* If there were non-ASCII characters, mark that we must convert */
result_state= result_state & 0x80 ? IDENT_QUOTED : IDENT;
}
- length= (uint) (lip->ptr - lip->tok_start)-1;
- start= lip->ptr;
+ length= lip->yyLength();
+ start= lip->get_ptr();
if (lip->ignore_space)
{
/*
If we find a space then this can't be an identifier. We notice this
below by checking start != lex->ptr.
*/
- for (; state_map[c] == MY_LEX_SKIP ; c= yyGet());
+ for (; state_map[c] == MY_LEX_SKIP ; c= lip->yyGet());
}
- if (start == lip->ptr && c == '.' && ident_map[yyPeek()])
+ if (start == lip->get_ptr() && c == '.' && ident_map[lip->yyPeek()])
lip->next_state=MY_LEX_IDENT_SEP;
else
{ // '(' must follow directly if function
- yyUnget();
+ lip->yyUnget();
if ((tokval = find_keyword(lip, length, c == '(')))
{
lip->next_state= MY_LEX_START; // Allow signed numbers
return(tokval); // Was keyword
}
- yySkip(); // next state does a unget
+ lip->yySkip(); // next state does a unget
}
yylval->lex_str=get_token(lip, 0, length);
- /*
+ /*
Note: "SELECT _bla AS 'alias'"
_bla should be considered as a IDENT if charset haven't been found.
- So we don't use MYF(MY_WME) with get_charset_by_csname to avoid
+ So we don't use MYF(MY_WME) with get_charset_by_csname to avoid
producing an error.
*/
- if ((yylval->lex_str.str[0]=='_') &&
- (lex->underscore_charset=
- get_charset_by_csname(yylval->lex_str.str + 1,
- MY_CS_PRIMARY,MYF(0))))
- return(UNDERSCORE_CHARSET);
+ if (yylval->lex_str.str[0] == '_')
+ {
+ CHARSET_INFO *cs= get_charset_by_csname(yylval->lex_str.str + 1,
+ MY_CS_PRIMARY, MYF(0));
+ if (cs)
+ {
+ yylval->charset= cs;
+ lip->m_underscore_cs= cs;
+
+ lip->body_utf8_append(lip->m_cpp_text_start,
+ lip->get_cpp_tok_start() + length);
+ return(UNDERSCORE_CHARSET);
+ }
+ }
+
+ lip->body_utf8_append(lip->m_cpp_text_start);
+
+ lip->body_utf8_append_literal(thd, &yylval->lex_str, cs,
+ lip->m_cpp_text_end);
+
return(result_state); // IDENT or IDENT_QUOTED
case MY_LEX_IDENT_SEP: // Found ident and now '.'
- yylval->lex_str.str=(char*) lip->ptr;
- yylval->lex_str.length=1;
- c=yyGet(); // should be '.'
+ yylval->lex_str.str= (char*) lip->get_ptr();
+ yylval->lex_str.length= 1;
+ c= lip->yyGet(); // should be '.'
lip->next_state= MY_LEX_IDENT_START;// Next is an ident (not a keyword)
- if (!ident_map[yyPeek()]) // Probably ` or "
+ if (!ident_map[lip->yyPeek()]) // Probably ` or "
lip->next_state= MY_LEX_START;
return((int) c);
case MY_LEX_NUMBER_IDENT: // number or ident which num-start
- while (my_isdigit(cs,(c = yyGet()))) ;
+ if (lip->yyGetLast() == '0')
+ {
+ c= lip->yyGet();
+ if (c == 'x')
+ {
+ while (my_isxdigit(cs,(c = lip->yyGet()))) ;
+ if ((lip->yyLength() >= 3) && !ident_map[c])
+ {
+ /* skip '0x' */
+ yylval->lex_str=get_token(lip, 2, lip->yyLength()-2);
+ return (HEX_NUM);
+ }
+ lip->yyUnget();
+ state= MY_LEX_IDENT_START;
+ break;
+ }
+ else if (c == 'b')
+ {
+ while ((c= lip->yyGet()) == '0' || c == '1');
+ if ((lip->yyLength() >= 3) && !ident_map[c])
+ {
+ /* Skip '0b' */
+ yylval->lex_str= get_token(lip, 2, lip->yyLength()-2);
+ return (BIN_NUM);
+ }
+ lip->yyUnget();
+ state= MY_LEX_IDENT_START;
+ break;
+ }
+ lip->yyUnget();
+ }
+
+ while (my_isdigit(cs, (c = lip->yyGet()))) ;
if (!ident_map[c])
{ // Can't be identifier
state=MY_LEX_INT_OR_REAL;
@@ -721,42 +977,18 @@ int MYSQLlex(void *arg, void *yythd)
if (c == 'e' || c == 'E')
{
// The following test is written this way to allow numbers of type 1e1
- if (my_isdigit(cs,yyPeek()) ||
- (c=(yyGet())) == '+' || c == '-')
+ if (my_isdigit(cs,lip->yyPeek()) ||
+ (c=(lip->yyGet())) == '+' || c == '-')
{ // Allow 1E+10
- if (my_isdigit(cs,yyPeek())) // Number must have digit after sign
+ if (my_isdigit(cs,lip->yyPeek())) // Number must have digit after sign
{
- yySkip();
- while (my_isdigit(cs,yyGet())) ;
- yylval->lex_str=get_token(lip, 0, yyLength());
+ lip->yySkip();
+ while (my_isdigit(cs,lip->yyGet())) ;
+ yylval->lex_str=get_token(lip, 0, lip->yyLength());
return(FLOAT_NUM);
}
}
- yyUnget(); /* purecov: inspected */
- }
- else if (c == 'x' && (lip->ptr - lip->tok_start) == 2 &&
- lip->tok_start[0] == '0' )
- { // Varbinary
- while (my_isxdigit(cs,(c = yyGet()))) ;
- if ((lip->ptr - lip->tok_start) >= 4 && !ident_map[c])
- {
- /* skip '0x' */
- yylval->lex_str=get_token(lip, 2, yyLength()-2);
- return (HEX_NUM);
- }
- yyUnget();
- }
- else if (c == 'b' && (lip->ptr - lip->tok_start) == 2 &&
- lip->tok_start[0] == '0' )
- { // b'bin-number'
- while (my_isxdigit(cs,(c = yyGet()))) ;
- if ((lip->ptr - lip->tok_start) >= 4 && !ident_map[c])
- {
- /* Skip '0b' */
- yylval->lex_str= get_token(lip, 2, yyLength()-2);
- return (BIN_NUM);
- }
- yyUnget();
+ lip->yyUnget();
}
// fall through
case MY_LEX_IDENT_START: // We come here after '.'
@@ -765,48 +997,52 @@ int MYSQLlex(void *arg, void *yythd)
if (use_mb(cs))
{
result_state= IDENT_QUOTED;
- while (ident_map[c=yyGet()])
+ while (ident_map[c=lip->yyGet()])
{
if (my_mbcharlen(cs, c) > 1)
{
int l;
if ((l = my_ismbchar(cs,
- lip->ptr-1,
- lip->end_of_query)) == 0)
+ lip->get_ptr() -1,
+ lip->get_end_of_query())) == 0)
break;
- lip->ptr += l-1;
+ lip->skip_binary(l-1);
}
}
}
else
#endif
{
- for (result_state=0; ident_map[c= yyGet()]; result_state|= c);
+ for (result_state=0; ident_map[c= lip->yyGet()]; result_state|= c);
/* If there were non-ASCII characters, mark that we must convert */
result_state= result_state & 0x80 ? IDENT_QUOTED : IDENT;
}
- if (c == '.' && ident_map[yyPeek()])
+ if (c == '.' && ident_map[lip->yyPeek()])
lip->next_state=MY_LEX_IDENT_SEP;// Next is '.'
- yylval->lex_str= get_token(lip, 0, yyLength());
+ yylval->lex_str= get_token(lip, 0, lip->yyLength());
+
+ lip->body_utf8_append(lip->m_cpp_text_start);
+
+ lip->body_utf8_append_literal(thd, &yylval->lex_str, cs,
+ lip->m_cpp_text_end);
+
return(result_state);
case MY_LEX_USER_VARIABLE_DELIMITER: // Found quote char
{
uint double_quotes= 0;
char quote_char= c; // Used char
- while ((c=yyGet()))
+ while ((c=lip->yyGet()))
{
int var_length;
if ((var_length= my_mbcharlen(cs, c)) == 1)
{
- if (c == (uchar) NAMES_SEP_CHAR)
- break; /* Old .frm format can't handle this char */
if (c == quote_char)
{
- if (yyPeek() != quote_char)
+ if (lip->yyPeek() != quote_char)
break;
- c=yyGet();
+ c=lip->yyGet();
double_quotes++;
continue;
}
@@ -814,78 +1050,84 @@ int MYSQLlex(void *arg, void *yythd)
#ifdef USE_MB
else if (var_length < 1)
break; // Error
- lip->ptr+= var_length-1;
+ lip->skip_binary(var_length-1);
#endif
}
if (double_quotes)
yylval->lex_str=get_quoted_token(lip, 1,
- yyLength() - double_quotes -1,
+ lip->yyLength() - double_quotes -1,
quote_char);
else
- yylval->lex_str=get_token(lip, 1, yyLength() -1);
+ yylval->lex_str=get_token(lip, 1, lip->yyLength() -1);
if (c == quote_char)
- yySkip(); // Skip end `
+ lip->yySkip(); // Skip end `
lip->next_state= MY_LEX_START;
+
+ lip->body_utf8_append(lip->m_cpp_text_start);
+
+ lip->body_utf8_append_literal(thd, &yylval->lex_str, cs,
+ lip->m_cpp_text_end);
+
return(IDENT_QUOTED);
}
case MY_LEX_INT_OR_REAL: // Complete int or incomplete real
if (c != '.')
{ // Found complete integer number.
- yylval->lex_str=get_token(lip, 0, yyLength());
- return int_token(yylval->lex_str.str,yylval->lex_str.length);
+ yylval->lex_str=get_token(lip, 0, lip->yyLength());
+ return int_token(yylval->lex_str.str, (uint) yylval->lex_str.length);
}
// fall through
case MY_LEX_REAL: // Incomplete real number
- while (my_isdigit(cs,c = yyGet())) ;
+ while (my_isdigit(cs,c = lip->yyGet())) ;
if (c == 'e' || c == 'E')
{
- c = yyGet();
+ c = lip->yyGet();
if (c == '-' || c == '+')
- c = yyGet(); // Skip sign
+ c = lip->yyGet(); // Skip sign
if (!my_isdigit(cs,c))
{ // No digit after sign
state= MY_LEX_CHAR;
break;
}
- while (my_isdigit(cs,yyGet())) ;
- yylval->lex_str=get_token(lip, 0, yyLength());
+ while (my_isdigit(cs,lip->yyGet())) ;
+ yylval->lex_str=get_token(lip, 0, lip->yyLength());
return(FLOAT_NUM);
}
- yylval->lex_str=get_token(lip, 0, yyLength());
+ yylval->lex_str=get_token(lip, 0, lip->yyLength());
return(DECIMAL_NUM);
case MY_LEX_HEX_NUMBER: // Found x'hexstring'
- yyGet(); // Skip '
- while (my_isxdigit(cs,(c = yyGet()))) ;
- length=(uint) (lip->ptr - lip->tok_start); // Length of hexnum+3
- if (!(length & 1) || c != '\'')
- {
- return(ABORT_SYM); // Illegal hex constant
- }
- yyGet(); // get_token makes an unget
+ lip->yySkip(); // Accept opening '
+ while (my_isxdigit(cs, (c= lip->yyGet()))) ;
+ if (c != '\'')
+ return(ABORT_SYM); // Illegal hex constant
+ lip->yySkip(); // Accept closing '
+ length= lip->yyLength(); // Length of hexnum+3
+ if ((length % 2) == 0)
+ return(ABORT_SYM); // odd number of hex digits
yylval->lex_str=get_token(lip,
2, // skip x'
length-3); // don't count x' and last '
return (HEX_NUM);
case MY_LEX_BIN_NUMBER: // Found b'bin-string'
- yyGet(); // Skip '
- while ((c= yyGet()) == '0' || c == '1');
- length= (uint) (lip->ptr - lip->tok_start); // Length of bin-num + 3
+ lip->yySkip(); // Accept opening '
+ while ((c= lip->yyGet()) == '0' || c == '1');
if (c != '\'')
- return(ABORT_SYM); // Illegal hex constant
- yyGet(); // get_token makes an unget
+ return(ABORT_SYM); // Illegal hex constant
+ lip->yySkip(); // Accept closing '
+ length= lip->yyLength(); // Length of bin-num + 3
yylval->lex_str= get_token(lip,
2, // skip b'
length-3); // don't count b' and last '
return (BIN_NUM);
case MY_LEX_CMP_OP: // Incomplete comparison operator
- if (state_map[yyPeek()] == MY_LEX_CMP_OP ||
- state_map[yyPeek()] == MY_LEX_LONG_CMP_OP)
- yySkip();
- if ((tokval = find_keyword(lip,(uint) (lip->ptr - lip->tok_start),0)))
+ if (state_map[lip->yyPeek()] == MY_LEX_CMP_OP ||
+ state_map[lip->yyPeek()] == MY_LEX_LONG_CMP_OP)
+ lip->yySkip();
+ if ((tokval = find_keyword(lip, lip->yyLength() + 1, 0)))
{
lip->next_state= MY_LEX_START; // Allow signed numbers
return(tokval);
@@ -894,14 +1136,14 @@ int MYSQLlex(void *arg, void *yythd)
break;
case MY_LEX_LONG_CMP_OP: // Incomplete comparison operator
- if (state_map[yyPeek()] == MY_LEX_CMP_OP ||
- state_map[yyPeek()] == MY_LEX_LONG_CMP_OP)
+ if (state_map[lip->yyPeek()] == MY_LEX_CMP_OP ||
+ state_map[lip->yyPeek()] == MY_LEX_LONG_CMP_OP)
{
- yySkip();
- if (state_map[yyPeek()] == MY_LEX_CMP_OP)
- yySkip();
+ lip->yySkip();
+ if (state_map[lip->yyPeek()] == MY_LEX_CMP_OP)
+ lip->yySkip();
}
- if ((tokval = find_keyword(lip,(uint) (lip->ptr - lip->tok_start),0)))
+ if ((tokval = find_keyword(lip, lip->yyLength() + 1, 0)))
{
lip->next_state= MY_LEX_START; // Found long op
return(tokval);
@@ -910,12 +1152,12 @@ int MYSQLlex(void *arg, void *yythd)
break;
case MY_LEX_BOOL:
- if (c != yyPeek())
+ if (c != lip->yyPeek())
{
state=MY_LEX_CHAR;
break;
}
- yySkip();
+ lip->yySkip();
tokval = find_keyword(lip,2,0); // Is a bool operator
lip->next_state= MY_LEX_START; // Allow signed numbers
return(tokval);
@@ -928,43 +1170,97 @@ int MYSQLlex(void *arg, void *yythd)
}
/* " used for strings */
case MY_LEX_STRING: // Incomplete text string
- if (!(yylval->lex_str.str = get_text(lip)))
+ if (!(yylval->lex_str.str = get_text(lip, 1, 1)))
{
state= MY_LEX_CHAR; // Read char by char
break;
}
yylval->lex_str.length=lip->yytoklen;
+
+ lip->body_utf8_append(lip->m_cpp_text_start);
+
+ lip->body_utf8_append_literal(thd, &yylval->lex_str,
+ lip->m_underscore_cs ? lip->m_underscore_cs : cs,
+ lip->m_cpp_text_end);
+
+ lip->m_underscore_cs= NULL;
+
lex->text_string_is_7bit= (lip->tok_bitmap & 0x80) ? 0 : 1;
return(TEXT_STRING);
case MY_LEX_COMMENT: // Comment
lex->select_lex.options|= OPTION_FOUND_COMMENT;
- while ((c = yyGet()) != '\n' && c) ;
- yyUnget(); // Safety against eof
+ while ((c = lip->yyGet()) != '\n' && c) ;
+ lip->yyUnget(); // Safety against eof
state = MY_LEX_START; // Try again
break;
case MY_LEX_LONG_COMMENT: /* Long C comment? */
- if (yyPeek() != '*')
+ if (lip->yyPeek() != '*')
{
state=MY_LEX_CHAR; // Probable division
break;
}
- yySkip(); // Skip '*'
lex->select_lex.options|= OPTION_FOUND_COMMENT;
- if (yyPeek() == '!') // MySQL command in comment
+ /* Reject '/' '*', since we might need to turn off the echo */
+ lip->yyUnget();
+
+ if (lip->yyPeekn(2) == '!')
{
- ulong version=MYSQL_VERSION_ID;
- yySkip();
- state=MY_LEX_START;
- if (my_isdigit(cs,yyPeek()))
- { // Version number
- version=strtol((char*) lip->ptr,(char**) &lip->ptr,10);
- }
- if (version <= MYSQL_VERSION_ID)
- {
- lex->in_comment=1;
- break;
- }
+ lip->in_comment= DISCARD_COMMENT;
+ /* Accept '/' '*' '!', but do not keep this marker. */
+ lip->set_echo(FALSE);
+ lip->yySkip();
+ lip->yySkip();
+ lip->yySkip();
+
+ /*
+ The special comment format is very strict:
+ '/' '*' '!', followed by exactly
+ 1 digit (major), 2 digits (minor), then 2 digits (dot).
+ 32302 -> 3.23.02
+ 50032 -> 5.0.32
+ 50114 -> 5.1.14
+ */
+ char version_str[6];
+ version_str[0]= lip->yyPeekn(0);
+ version_str[1]= lip->yyPeekn(1);
+ version_str[2]= lip->yyPeekn(2);
+ version_str[3]= lip->yyPeekn(3);
+ version_str[4]= lip->yyPeekn(4);
+ version_str[5]= 0;
+ if ( my_isdigit(cs, version_str[0])
+ && my_isdigit(cs, version_str[1])
+ && my_isdigit(cs, version_str[2])
+ && my_isdigit(cs, version_str[3])
+ && my_isdigit(cs, version_str[4])
+ )
+ {
+ ulong version;
+ version=strtol(version_str, NULL, 10);
+
+ /* Accept 'M' 'm' 'm' 'd' 'd' */
+ lip->yySkipn(5);
+
+ if (version <= MYSQL_VERSION_ID)
+ {
+ /* Expand the content of the special comment as real code */
+ lip->set_echo(TRUE);
+ state=MY_LEX_START;
+ break;
+ }
+ }
+ else
+ {
+ state=MY_LEX_START;
+ lip->set_echo(TRUE);
+ break;
+ }
+ }
+ else
+ {
+ lip->in_comment= PRESERVE_COMMENT;
+ lip->yySkip(); // Accept /
+ lip->yySkip(); // Accept *
}
/*
Discard:
@@ -975,14 +1271,14 @@ int MYSQLlex(void *arg, void *yythd)
the first '*' '/' sequence seen will mark the end.
*/
comment_closed= FALSE;
- while (lip->ptr != lip->end_of_query)
+ while (! lip->eof())
{
- c= yyGet();
+ c= lip->yyGet();
if (c == '*')
{
- if (yyPeek() == '/')
+ if (lip->yyPeek() == '/')
{
- yySkip();
+ lip->yySkip();
comment_closed= TRUE;
state = MY_LEX_START;
break;
@@ -994,36 +1290,49 @@ int MYSQLlex(void *arg, void *yythd)
/* Unbalanced comments with a missing '*' '/' are a syntax error */
if (! comment_closed)
return (ABORT_SYM);
+ state = MY_LEX_START; // Try again
+ lip->in_comment= NO_COMMENT;
+ lip->set_echo(TRUE);
break;
case MY_LEX_END_LONG_COMMENT:
- if (lex->in_comment && yyPeek() == '/')
+ if ((lip->in_comment != NO_COMMENT) && lip->yyPeek() == '/')
{
- yySkip();
- lex->in_comment=0;
- state=MY_LEX_START;
+ /* Reject '*' '/' */
+ lip->yyUnget();
+ /* Accept '*' '/', with the proper echo */
+ lip->set_echo(lip->in_comment == PRESERVE_COMMENT);
+ lip->yySkipn(2);
+ /* And start recording the tokens again */
+ lip->set_echo(TRUE);
+ lip->in_comment=NO_COMMENT;
+ state=MY_LEX_START;
}
else
state=MY_LEX_CHAR; // Return '*'
break;
case MY_LEX_SET_VAR: // Check if ':='
- if (yyPeek() != '=')
+ if (lip->yyPeek() != '=')
{
state=MY_LEX_CHAR; // Return ':'
break;
}
- yySkip();
+ lip->yySkip();
return (SET_VAR);
case MY_LEX_SEMICOLON: // optional line terminator
state= MY_LEX_CHAR; // Return ';'
break;
case MY_LEX_EOL:
- if (lip->ptr >= lip->end_of_query)
+ if (lip->eof())
{
- lip->next_state=MY_LEX_END; // Mark for next loop
+ lip->yyUnget(); // Reject the last '\0'
+ lip->set_echo(FALSE);
+ lip->yySkip();
+ lip->set_echo(TRUE);
/* Unbalanced comments with a missing '*' '/' are a syntax error */
- if (lex->in_comment)
+ if (lip->in_comment != NO_COMMENT)
return (ABORT_SYM);
- return(END_OF_INPUT);
+ lip->next_state=MY_LEX_END; // Mark for next loop
+ return(END_OF_INPUT);
}
state=MY_LEX_CHAR;
break;
@@ -1033,16 +1342,16 @@ int MYSQLlex(void *arg, void *yythd)
/* Actually real shouldn't start with . but allow them anyhow */
case MY_LEX_REAL_OR_POINT:
- if (my_isdigit(cs,yyPeek()))
+ if (my_isdigit(cs,lip->yyPeek()))
state = MY_LEX_REAL; // Real
else
{
state= MY_LEX_IDENT_SEP; // return '.'
- yyUnget(); // Put back '.'
+ lip->yyUnget(); // Put back '.'
}
break;
case MY_LEX_USER_END: // end '@' of user@hostname
- switch (state_map[yyPeek()]) {
+ switch (state_map[lip->yyPeek()]) {
case MY_LEX_STRING:
case MY_LEX_USER_VARIABLE_DELIMITER:
case MY_LEX_STRING_OR_DELIMITER:
@@ -1054,20 +1363,20 @@ int MYSQLlex(void *arg, void *yythd)
lip->next_state=MY_LEX_HOSTNAME;
break;
}
- yylval->lex_str.str=(char*) lip->ptr;
+ yylval->lex_str.str=(char*) lip->get_ptr();
yylval->lex_str.length=1;
return((int) '@');
case MY_LEX_HOSTNAME: // end '@' of user@hostname
- for (c=yyGet() ;
+ for (c=lip->yyGet() ;
my_isalnum(cs,c) || c == '.' || c == '_' || c == '$';
- c= yyGet()) ;
- yylval->lex_str=get_token(lip, 0, yyLength());
+ c= lip->yyGet()) ;
+ yylval->lex_str=get_token(lip, 0, lip->yyLength());
return(LEX_HOSTNAME);
case MY_LEX_SYSTEM_VAR:
- yylval->lex_str.str=(char*) lip->ptr;
+ yylval->lex_str.str=(char*) lip->get_ptr();
yylval->lex_str.length=1;
- yySkip(); // Skip '@'
- lip->next_state= (state_map[yyPeek()] ==
+ lip->yySkip(); // Skip '@'
+ lip->next_state= (state_map[lip->yyPeek()] ==
MY_LEX_USER_VARIABLE_DELIMITER ?
MY_LEX_OPERATOR_OR_IDENT :
MY_LEX_IDENT_OR_KEYWORD);
@@ -1078,28 +1387,47 @@ int MYSQLlex(void *arg, void *yythd)
We should now be able to handle:
[(global | local | session) .]variable_name
*/
-
- for (result_state= 0; ident_map[c= yyGet()]; result_state|= c);
+
+ for (result_state= 0; ident_map[c= lip->yyGet()]; result_state|= c);
/* If there were non-ASCII characters, mark that we must convert */
result_state= result_state & 0x80 ? IDENT_QUOTED : IDENT;
-
+
if (c == '.')
lip->next_state=MY_LEX_IDENT_SEP;
- length= (uint) (lip->ptr - lip->tok_start)-1;
- if (length == 0)
+ length= lip->yyLength();
+ if (length == 0)
return(ABORT_SYM); // Names must be nonempty.
if ((tokval= find_keyword(lip, length,0)))
{
- yyUnget(); // Put back 'c'
+ lip->yyUnget(); // Put back 'c'
return(tokval); // Was keyword
}
yylval->lex_str=get_token(lip, 0, length);
+
+ lip->body_utf8_append(lip->m_cpp_text_start);
+
+ lip->body_utf8_append_literal(thd, &yylval->lex_str, cs,
+ lip->m_cpp_text_end);
+
return(result_state);
}
}
}
+/**
+ Construct a copy of this object to be used for mysql_alter_table
+ and mysql_create_table.
+
+ Historically, these two functions modify their Alter_info
+ arguments. This behaviour breaks re-execution of prepared
+ statements and stored procedures and is compensated by always
+ supplying a copy of Alter_info to these functions.
+
+ @return You need to use check the error in THD for out
+ of memory condition after calling this function.
+*/
+
Alter_info::Alter_info(const Alter_info &rhs, MEM_ROOT *mem_root)
:drop_list(rhs.drop_list, mem_root),
alter_list(rhs.alter_list, mem_root),
@@ -1107,35 +1435,55 @@ Alter_info::Alter_info(const Alter_info &rhs, MEM_ROOT *mem_root)
create_list(rhs.create_list, mem_root),
flags(rhs.flags),
keys_onoff(rhs.keys_onoff),
- tablespace_op(rhs.tablespace_op)
-{}
-
-
-/*
- Skip comment in the end of statement.
+ tablespace_op(rhs.tablespace_op),
+ partition_names(rhs.partition_names, mem_root),
+ no_parts(rhs.no_parts),
+ change_level(rhs.change_level),
+ datetime_field(rhs.datetime_field),
+ error_if_not_empty(rhs.error_if_not_empty)
+{
+ /*
+ Make deep copies of used objects.
+ This is not a fully deep copy - clone() implementations
+ of Alter_drop, Alter_column, Key, foreign_key, Key_part_spec
+ do not copy string constants. At the same length the only
+ reason we make a copy currently is that ALTER/CREATE TABLE
+ code changes input Alter_info definitions, but string
+ constants never change.
+ */
+ list_copy_and_replace_each_value(drop_list, mem_root);
+ list_copy_and_replace_each_value(alter_list, mem_root);
+ list_copy_and_replace_each_value(key_list, mem_root);
+ list_copy_and_replace_each_value(create_list, mem_root);
+ /* partition_names are not deeply copied currently */
+}
- SYNOPSIS
- skip_rear_comments()
- cs character set
- begin pointer to the beginning of statement
- end pointer to the end of statement
- DESCRIPTION
- The function is intended to trim comments at the end of the statement.
+void trim_whitespace(CHARSET_INFO *cs, LEX_STRING *str)
+{
+ /*
+ TODO:
+ This code assumes that there are no multi-bytes characters
+ that can be considered white-space.
+ */
- RETURN
- Pointer to the last non-comment symbol of the statement.
-*/
+ while ((str->length > 0) && (my_isspace(cs, str->str[0])))
+ {
+ str->length --;
+ str->str ++;
+ }
-char *skip_rear_comments(CHARSET_INFO *cs, char *begin, char *end)
-{
- while (begin < end && (end[-1] == '*' ||
- end[-1] == '/' || end[-1] == ';' ||
- my_isspace(cs, end[-1])))
- end-= 1;
- return end;
+ /*
+ FIXME:
+ Also, parsing backward is not safe with multi bytes characters
+ */
+ while ((str->length > 0) && (my_isspace(cs, str->str[str->length-1])))
+ {
+ str->length --;
+ }
}
+
/*
st_select_lex structures initialisations
*/
@@ -1219,23 +1567,19 @@ void st_select_lex::init_select()
group_list.empty();
type= db= 0;
having= 0;
- use_index_ptr= ignore_index_ptr= 0;
table_join_options= 0;
in_sum_expr= with_wild= 0;
options= 0;
sql_cache= SQL_CACHE_UNSPECIFIED;
braces= 0;
- expr_list.empty();
- udf_list.empty();
interval_list.empty();
- use_index.empty();
ftfunc_list_alloc.empty();
inner_sum_func_list= 0;
ftfunc_list= &ftfunc_list_alloc;
linkage= UNSPECIFIED_TYPE;
order_list.elements= 0;
order_list.first= 0;
- order_list.next= (byte**) &order_list.first;
+ order_list.next= (uchar**) &order_list.first;
/* Set limit and offset to default values */
select_limit= 0; /* denotes the default limit = HA_POS_ERROR */
offset_limit= 0; /* denotes the default offset = 0 */
@@ -1423,7 +1767,7 @@ void st_select_lex_unit::exclude_tree()
'last' should be reachable from this st_select_lex_node
*/
-void st_select_lex::mark_as_dependent(SELECT_LEX *last)
+void st_select_lex::mark_as_dependent(st_select_lex *last)
{
/*
Mark all selects from resolved to 1 before select where was
@@ -1456,14 +1800,11 @@ bool st_select_lex_node::inc_in_sum_expr() { return 1; }
uint st_select_lex_node::get_in_sum_expr() { return 0; }
TABLE_LIST* st_select_lex_node::get_table_list() { return 0; }
List<Item>* st_select_lex_node::get_item_list() { return 0; }
-List<String>* st_select_lex_node::get_use_index() { return 0; }
-List<String>* st_select_lex_node::get_ignore_index() { return 0; }
-TABLE_LIST *st_select_lex_node::add_table_to_list(THD *thd, Table_ident *table,
+TABLE_LIST *st_select_lex_node::add_table_to_list (THD *thd, Table_ident *table,
LEX_STRING *alias,
ulong table_join_options,
thr_lock_type flags,
- List<String> *use_index,
- List<String> *ignore_index,
+ List<Index_hint> *hints,
LEX_STRING *option)
{
return 0;
@@ -1509,7 +1850,7 @@ bool st_select_lex::add_order_to_list(THD *thd, Item *item, bool asc)
bool st_select_lex::add_item_to_list(THD *thd, Item *item)
{
DBUG_ENTER("st_select_lex::add_item_to_list");
- DBUG_PRINT("info", ("Item: %p", item));
+ DBUG_PRINT("info", ("Item: 0x%lx", (long) item));
DBUG_RETURN(item_list.push_back(item));
}
@@ -1568,19 +1909,6 @@ List<Item>* st_select_lex::get_item_list()
return &item_list;
}
-
-List<String>* st_select_lex::get_use_index()
-{
- return use_index_ptr;
-}
-
-
-List<String>* st_select_lex::get_ignore_index()
-{
- return ignore_index_ptr;
-}
-
-
ulong st_select_lex::get_table_join_options()
{
return table_join_options;
@@ -1606,7 +1934,7 @@ bool st_select_lex::setup_ref_array(THD *thd, uint order_group_num)
}
-void st_select_lex_unit::print(String *str)
+void st_select_lex_unit::print(String *str, enum_query_type query_type)
{
bool union_all= !union_distinct;
for (SELECT_LEX *sl= first_select(); sl; sl= sl->next_select())
@@ -1621,7 +1949,7 @@ void st_select_lex_unit::print(String *str)
}
if (sl->braces)
str->append('(');
- sl->print(thd, str);
+ sl->print(thd, str, query_type);
if (sl->braces)
str->append(')');
}
@@ -1630,27 +1958,30 @@ void st_select_lex_unit::print(String *str)
if (fake_select_lex->order_list.elements)
{
str->append(STRING_WITH_LEN(" order by "));
- fake_select_lex->print_order(str,
- (ORDER *) fake_select_lex->
- order_list.first);
+ fake_select_lex->print_order(
+ str,
+ (ORDER *) fake_select_lex->order_list.first,
+ query_type);
}
- fake_select_lex->print_limit(thd, str);
+ fake_select_lex->print_limit(thd, str, query_type);
}
}
-void st_select_lex::print_order(String *str, ORDER *order)
+void st_select_lex::print_order(String *str,
+ ORDER *order,
+ enum_query_type query_type)
{
for (; order; order= order->next)
{
if (order->counter_used)
{
char buffer[20];
- uint length= my_snprintf(buffer, 20, "%d", order->counter);
- str->append(buffer, length);
+ size_t length= my_snprintf(buffer, 20, "%d", order->counter);
+ str->append(buffer, (uint) length);
}
else
- (*order->item)->print(str);
+ (*order->item)->print(str, query_type);
if (!order->asc)
str->append(STRING_WITH_LEN(" desc"));
if (order->next)
@@ -1659,7 +1990,9 @@ void st_select_lex::print_order(String *str, ORDER *order)
}
-void st_select_lex::print_limit(THD *thd, String *str)
+void st_select_lex::print_limit(THD *thd,
+ String *str,
+ enum_query_type query_type)
{
SELECT_LEX_UNIT *unit= master_unit();
Item_subselect *item= unit->item;
@@ -1669,7 +2002,7 @@ void st_select_lex::print_limit(THD *thd, String *str)
item->substype() == Item_subselect::ALL_SUBS))
{
DBUG_ASSERT(!item->fixed ||
- select_limit->val_int() == LL(1) && offset_limit == 0);
+ (select_limit->val_int() == LL(1) && offset_limit == 0));
return;
}
@@ -1678,10 +2011,10 @@ void st_select_lex::print_limit(THD *thd, String *str)
str->append(STRING_WITH_LEN(" limit "));
if (offset_limit)
{
- offset_limit->print(str);
+ offset_limit->print(str, query_type);
str->append(',');
}
- select_limit->print(str);
+ select_limit->print(str, query_type);
}
}
@@ -1736,6 +2069,17 @@ void st_lex::cleanup_lex_after_parse_error(THD *thd)
void Query_tables_list::reset_query_tables_list(bool init)
{
+ if (!init && query_tables)
+ {
+ TABLE_LIST *table= query_tables;
+ for (;;)
+ {
+ delete table->view;
+ if (query_tables_last == &table->next_global ||
+ !(table= table->next_global))
+ break;
+ }
+ }
query_tables= 0;
query_tables_last= &query_tables;
query_tables_own_last= 0;
@@ -1755,6 +2099,7 @@ void Query_tables_list::reset_query_tables_list(bool init)
sroutines_list.empty();
sroutines_list_own_last= sroutines_list.next;
sroutines_list_own_elements= 0;
+ binlog_stmt_flags= 0;
}
@@ -1787,8 +2132,13 @@ void Query_tables_list::destroy_query_tables_list()
st_lex::st_lex()
:result(0),
- sql_command(SQLCOM_END)
+ sql_command(SQLCOM_END), option_type(OPT_DEFAULT), is_lex_started(0)
{
+
+ my_init_dynamic_array2(&plugins, sizeof(plugin_ref),
+ plugins_static_buffer,
+ INITIAL_LEX_PLUGIN_LIST_SIZE,
+ INITIAL_LEX_PLUGIN_LIST_SIZE);
reset_query_tables_list(TRUE);
}
@@ -2013,7 +2363,7 @@ uint8 st_lex::get_effective_with_check(TABLE_LIST *view)
*/
bool
-st_lex::copy_db_to(char **p_db, uint *p_db_length) const
+st_lex::copy_db_to(char **p_db, size_t *p_db_length) const
{
if (sphead)
{
@@ -2038,7 +2388,7 @@ st_lex::copy_db_to(char **p_db, uint *p_db_length) const
values - SELECT_LEX with initial values for counters
*/
-void st_select_lex_unit::set_limit(SELECT_LEX *sl)
+void st_select_lex_unit::set_limit(st_select_lex *sl)
{
ha_rows select_limit_val;
ulonglong val;
@@ -2254,7 +2604,7 @@ TABLE_LIST *st_lex::unlink_first_table(bool *link_to_local)
{
select_lex.context.table_list=
select_lex.context.first_name_resolution_table= first->next_local;
- select_lex.table_list.first= (byte*) (first->next_local);
+ select_lex.table_list.first= (uchar*) (first->next_local);
select_lex.table_list.elements--; //safety
first->next_local= 0;
/*
@@ -2310,31 +2660,6 @@ void st_lex::first_lists_tables_same()
/*
- Add implicitly used time zone description tables to global table list
- (if needed).
-
- SYNOPSYS
- st_lex::add_time_zone_tables_to_query_tables()
- thd - pointer to current thread context
-
- RETURN VALUE
- TRUE - error
- FALSE - success
-*/
-
-bool st_lex::add_time_zone_tables_to_query_tables(THD *thd_arg)
-{
- /* We should not add these tables twice */
- if (!time_zone_tables_used)
- {
- time_zone_tables_used= my_tz_get_table_list(thd_arg, &query_tables_last);
- if (time_zone_tables_used == &fake_time_zone_tables_list)
- return TRUE;
- }
- return FALSE;
-}
-
-/*
Link table back that was unlinked with unlink_first_table()
SYNOPSIS
@@ -2360,7 +2685,7 @@ void st_lex::link_first_table_back(TABLE_LIST *first,
{
first->next_local= (TABLE_LIST*) select_lex.table_list.first;
select_lex.context.table_list= first;
- select_lex.table_list.first= (byte*) first;
+ select_lex.table_list.first= (uchar*) first;
select_lex.table_list.elements++; //safety
}
}
@@ -2403,7 +2728,6 @@ void st_lex::cleanup_after_one_table_open()
/* remove underlying units (units of VIEW) subtree */
select_lex.cut_subtree();
}
- time_zone_tables_used= 0;
}
@@ -2443,6 +2767,28 @@ 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()
+
+ RETURN
+ FALSE No routines and tables used
+ TRUE Either or both routines and tables are used.
+*/
+
+bool st_lex::table_or_sp_used()
+{
+ DBUG_ENTER("table_or_sp_used");
+
+ if (sroutines.records || query_tables)
+ DBUG_RETURN(TRUE);
+
+ DBUG_RETURN(FALSE);
+}
+
+
+/*
Do end-of-prepare fixup for list of tables and their merge-VIEWed tables
SYNOPSIS
@@ -2509,6 +2855,7 @@ void st_select_lex::fix_prepare_information(THD *thd, Item **conds,
}
}
+
/*
There are st_select_lex::add_table_to_list &
st_select_lex::set_lock_for_tables are in sql_parse.cc
@@ -2521,3 +2868,80 @@ void st_select_lex::fix_prepare_information(THD *thd, Item **conds,
are in sql_union.cc
*/
+/*
+ Sets the kind of hints to be added by the calls to add_index_hint().
+
+ SYNOPSIS
+ set_index_hint_type()
+ type_arg The kind of hints to be added from now on.
+ clause The clause to use for hints to be added from now on.
+
+ DESCRIPTION
+ Used in filling up the tagged hints list.
+ This list is filled by first setting the kind of the hint as a
+ context variable and then adding hints of the current kind.
+ Then the context variable index_hint_type can be reset to the
+ next hint type.
+*/
+void st_select_lex::set_index_hint_type(enum index_hint_type type_arg,
+ index_clause_map clause)
+{
+ current_index_hint_type= type_arg;
+ current_index_hint_clause= clause;
+}
+
+
+/*
+ Makes an array to store index usage hints (ADD/FORCE/IGNORE INDEX).
+
+ SYNOPSIS
+ alloc_index_hints()
+ thd current thread.
+*/
+
+void st_select_lex::alloc_index_hints (THD *thd)
+{
+ index_hints= new (thd->mem_root) List<Index_hint>();
+}
+
+
+
+/*
+ adds an element to the array storing index usage hints
+ (ADD/FORCE/IGNORE INDEX).
+
+ SYNOPSIS
+ add_index_hint()
+ thd current thread.
+ str name of the index.
+ length number of characters in str.
+
+ RETURN VALUE
+ 0 on success, non-zero otherwise
+*/
+bool st_select_lex::add_index_hint (THD *thd, char *str, uint length)
+{
+ return index_hints->push_front (new (thd->mem_root)
+ Index_hint(current_index_hint_type,
+ current_index_hint_clause,
+ str, length));
+}
+
+/**
+ A routine used by the parser to decide whether we are specifying a full
+ partitioning or if only partitions to add or to split.
+
+ @note This needs to be outside of WITH_PARTITION_STORAGE_ENGINE since it
+ is used from the sql parser that doesn't have any ifdef's
+
+ @retval TRUE Yes, it is part of a management partition command
+ @retval FALSE No, not a management partition command
+*/
+
+bool st_lex::is_partition_management() const
+{
+ return (sql_command == SQLCOM_ALTER_TABLE &&
+ (alter_info.flags == ALTER_ADD_PARTITION ||
+ alter_info.flags == ALTER_REORGANIZE_PARTITION));
+}
+
diff --git a/sql/sql_lex.h b/sql/sql_lex.h
index 9f020f4adc5..0eefe031c8d 100644
--- a/sql/sql_lex.h
+++ b/sql/sql_lex.h
@@ -1,4 +1,4 @@
-/* Copyright (C) 2000-2006 MySQL AB
+/* Copyright 2000-2008 MySQL AB, 2008 Sun Microsystems, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -13,6 +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 */
+/**
+ @defgroup Semantic_Analysis Semantic Analysis
+*/
/* YACC and LEX Definitions */
@@ -24,7 +27,11 @@ class sp_head;
class sp_name;
class sp_instr;
class sp_pcontext;
+class st_alter_tablespace;
+class partition_info;
+class Event_parse_data;
+#ifdef MYSQL_SERVER
/*
The following hack is needed because mysql_yacc.cc does not define
YYSTYPE before including this file
@@ -43,10 +50,16 @@ class sp_pcontext;
#define LEX_YYSTYPE void *
#endif
#endif
+#endif
/*
When a command is added here, be sure it's also added in mysqld.cc
in "struct show_var_st status_vars[]= {" ...
+
+ If the command returns a result set or is not allowed in stored
+ functions or triggers, please also make sure that
+ sp_get_flags_for_command (sp_head.cc) returns proper flags for the
+ added SQLCOM_.
*/
enum enum_sql_command {
@@ -55,8 +68,8 @@ enum enum_sql_command {
SQLCOM_DELETE, SQLCOM_TRUNCATE, SQLCOM_DROP_TABLE, SQLCOM_DROP_INDEX,
SQLCOM_SHOW_DATABASES, SQLCOM_SHOW_TABLES, SQLCOM_SHOW_FIELDS,
- SQLCOM_SHOW_KEYS, SQLCOM_SHOW_VARIABLES, SQLCOM_SHOW_LOGS, SQLCOM_SHOW_STATUS,
- SQLCOM_SHOW_INNODB_STATUS, SQLCOM_SHOW_NDBCLUSTER_STATUS, SQLCOM_SHOW_MUTEX_STATUS,
+ SQLCOM_SHOW_KEYS, SQLCOM_SHOW_VARIABLES, SQLCOM_SHOW_STATUS,
+ SQLCOM_SHOW_ENGINE_LOGS, SQLCOM_SHOW_ENGINE_STATUS, SQLCOM_SHOW_ENGINE_MUTEX,
SQLCOM_SHOW_PROCESSLIST, SQLCOM_SHOW_MASTER_STAT, SQLCOM_SHOW_SLAVE_STAT,
SQLCOM_SHOW_GRANTS, SQLCOM_SHOW_CREATE, SQLCOM_SHOW_CHARSETS,
SQLCOM_SHOW_COLLATIONS, SQLCOM_SHOW_CREATE_DB, SQLCOM_SHOW_TABLE_STATUS,
@@ -94,14 +107,36 @@ enum enum_sql_command {
SQLCOM_XA_START, SQLCOM_XA_END, SQLCOM_XA_PREPARE,
SQLCOM_XA_COMMIT, SQLCOM_XA_ROLLBACK, SQLCOM_XA_RECOVER,
SQLCOM_SHOW_PROC_CODE, SQLCOM_SHOW_FUNC_CODE,
- /* This should be the last !!! */
+ SQLCOM_ALTER_TABLESPACE,
+ SQLCOM_INSTALL_PLUGIN, SQLCOM_UNINSTALL_PLUGIN,
+ SQLCOM_SHOW_AUTHORS, SQLCOM_BINLOG_BASE64_EVENT,
+ SQLCOM_SHOW_PLUGINS,
+ SQLCOM_SHOW_CONTRIBUTORS,
+ SQLCOM_CREATE_SERVER, SQLCOM_DROP_SERVER, SQLCOM_ALTER_SERVER,
+ SQLCOM_CREATE_EVENT, SQLCOM_ALTER_EVENT, SQLCOM_DROP_EVENT,
+ SQLCOM_SHOW_CREATE_EVENT, SQLCOM_SHOW_EVENTS,
+ SQLCOM_SHOW_CREATE_TRIGGER,
+ SQLCOM_ALTER_DB_UPGRADE,
+ SQLCOM_SHOW_PROFILE, SQLCOM_SHOW_PROFILES,
+ /*
+ When a command is added here, be sure it's also added in mysqld.cc
+ in "struct show_var_st status_vars[]= {" ...
+ */
+ /* This should be the last !!! */
SQLCOM_END
};
// describe/explain types
#define DESCRIBE_NORMAL 1
#define DESCRIBE_EXTENDED 2
+/*
+ This is not within #ifdef because we want "EXPLAIN PARTITIONS ..." to produce
+ additional "partitions" column even if partitioning is not compiled in.
+*/
+#define DESCRIBE_PARTITIONS 4
+
+#ifdef MYSQL_SERVER
enum enum_sp_suid_behaviour
{
@@ -121,11 +156,11 @@ enum enum_sp_data_access
const LEX_STRING sp_data_access_name[]=
{
- { (char*) STRING_WITH_LEN("") },
- { (char*) STRING_WITH_LEN("CONTAINS SQL") },
- { (char*) STRING_WITH_LEN("NO SQL") },
- { (char*) STRING_WITH_LEN("READS SQL DATA") },
- { (char*) STRING_WITH_LEN("MODIFIES SQL DATA") }
+ { C_STRING_WITH_LEN("") },
+ { C_STRING_WITH_LEN("CONTAINS SQL") },
+ { C_STRING_WITH_LEN("NO SQL") },
+ { C_STRING_WITH_LEN("READS SQL DATA") },
+ { C_STRING_WITH_LEN("MODIFIES SQL DATA") }
};
#define DERIVED_SUBQUERY 1
@@ -147,18 +182,35 @@ enum enum_drop_mode
typedef List<Item> List_item;
+/* SERVERS CACHE CHANGES */
+typedef struct st_lex_server_options
+{
+ long port;
+ uint server_name_length;
+ char *server_name, *host, *db, *username, *password, *scheme, *socket, *owner;
+} LEX_SERVER_OPTIONS;
+
+
+/**
+ Structure to hold parameters for CHANGE MASTER or START/STOP SLAVE
+ or SHOW NEW MASTER.
+
+ 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
{
char *host, *user, *password, *log_file_name;
uint port, connect_retry;
ulonglong pos;
ulong server_id;
- /*
- Variable for MASTER_SSL option.
- MASTER_SSL=0 in CHANGE MASTER TO corresponds to SSL_DISABLE
- MASTER_SSL=1 corresponds to SSL_ENABLE
- */
- enum {SSL_UNCHANGED=0, SSL_DISABLE, SSL_ENABLE} ssl;
+ /*
+ 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;
@@ -182,6 +234,49 @@ enum tablespace_op_type
};
/*
+ String names used to print a statement with index hints.
+ Keep in sync with index_hint_type.
+*/
+extern const char * index_hint_type_name[];
+typedef uchar index_clause_map;
+
+/*
+ Bits in index_clause_map : one for each possible FOR clause in
+ USE/FORCE/IGNORE INDEX index hint specification
+*/
+#define INDEX_HINT_MASK_JOIN (1)
+#define INDEX_HINT_MASK_GROUP (1 << 1)
+#define INDEX_HINT_MASK_ORDER (1 << 2)
+
+#define INDEX_HINT_MASK_ALL (INDEX_HINT_MASK_JOIN | INDEX_HINT_MASK_GROUP | \
+ INDEX_HINT_MASK_ORDER)
+
+/* Single element of an USE/FORCE/IGNORE INDEX list specified as a SQL hint */
+class Index_hint : public Sql_alloc
+{
+public:
+ /* The type of the hint : USE/FORCE/IGNORE */
+ enum index_hint_type type;
+ /* Where the hit applies to. A bitmask of INDEX_HINT_MASK_<place> values */
+ index_clause_map clause;
+ /*
+ The index name. Empty (str=NULL) name represents an empty list
+ USE INDEX () clause
+ */
+ LEX_STRING key_name;
+
+ Index_hint (enum index_hint_type type_arg, index_clause_map clause_arg,
+ char *str, uint length) :
+ type(type_arg), clause(clause_arg)
+ {
+ key_name.str= str;
+ key_name.length= length;
+ }
+
+ void print(THD *thd, String *str);
+};
+
+/*
The state of the lex parsing for selects
master and slaves are pointers to select_lex.
@@ -333,7 +428,7 @@ public:
static void *operator new(size_t size) throw ()
{
- return (void*) sql_alloc((uint) size);
+ return sql_alloc(size);
}
static void *operator new(size_t size, MEM_ROOT *mem_root) throw ()
{ return (void*) alloc_root(mem_root, (uint) size); }
@@ -359,15 +454,12 @@ public:
virtual uint get_in_sum_expr();
virtual TABLE_LIST* get_table_list();
virtual List<Item>* get_item_list();
- virtual List<String>* get_use_index();
- virtual List<String>* get_ignore_index();
virtual ulong get_table_join_options();
virtual TABLE_LIST *add_table_to_list(THD *thd, Table_ident *table,
LEX_STRING *alias,
ulong table_options,
thr_lock_type flags= TL_UNLOCK,
- List<String> *use_index= 0,
- List<String> *ignore_index= 0,
+ List<Index_hint> *hints= 0,
LEX_STRING *option= 0);
virtual void set_lock_for_tables(thr_lock_type lock_type) {}
@@ -460,7 +552,7 @@ public:
inline void unclean() { cleaned= 0; }
void reinit_exec_mechanism();
- void print(String *str);
+ void print(String *str, enum_query_type query_type);
bool add_fake_select_lex(THD *thd);
void init_prepare_fake_select_lex(THD *thd);
@@ -498,8 +590,7 @@ public:
SQL_LIST table_list;
SQL_LIST group_list; /* GROUP BY clause. */
List<Item> item_list; /* list of fields & expressions */
- List<String> interval_list, use_index, *use_index_ptr,
- ignore_index, *ignore_index_ptr;
+ List<String> interval_list;
bool is_item_list_lookup;
/*
Usualy it is pointer to ftfunc_list_alloc, but in union used to create fake
@@ -520,7 +611,6 @@ public:
const char *type; /* type of select for EXPLAIN */
SQL_LIST order_list; /* ORDER clause */
- List<List_item> expr_list;
SQL_LIST *gorder_list;
Item *select_limit, *offset_limit; /* LIMIT clause parameters */
// Arrays of pointers to top elements of all_fields list
@@ -658,8 +748,7 @@ public:
LEX_STRING *alias,
ulong table_options,
thr_lock_type flags= TL_UNLOCK,
- List<String> *use_index= 0,
- List<String> *ignore_index= 0,
+ List<Index_hint> *hints= 0,
LEX_STRING *option= 0);
TABLE_LIST* get_table_list();
bool init_nested_join(THD *thd);
@@ -668,15 +757,13 @@ public:
void add_joined_table(TABLE_LIST *table);
TABLE_LIST *convert_right_join();
List<Item>* get_item_list();
- List<String>* get_use_index();
- List<String>* get_ignore_index();
ulong get_table_join_options();
void set_lock_for_tables(thr_lock_type lock_type);
inline void init_order()
{
order_list.elements= 0;
order_list.first= 0;
- order_list.next= (byte**) &order_list.first;
+ order_list.next= (uchar**) &order_list.first;
}
/*
This method created for reiniting LEX in mysql_admin_table() and can be
@@ -695,9 +782,11 @@ public:
init_select();
}
bool setup_ref_array(THD *thd, uint order_group_num);
- void print(THD *thd, String *str);
- static void print_order(String *str, ORDER *order);
- void print_limit(THD *thd, String *str);
+ void print(THD *thd, String *str, enum_query_type query_type);
+ static void print_order(String *str,
+ ORDER *order,
+ enum_query_type query_type);
+ void print_limit(THD *thd, String *str, enum_query_type query_type);
void fix_prepare_information(THD *thd, Item **conds, Item **having_conds);
/*
Destroy the used execution plan (JOIN) of this subtree (this
@@ -709,28 +798,73 @@ public:
select lexes.
*/
void cleanup_all_joins(bool full);
+
+ void set_index_hint_type(enum index_hint_type type, index_clause_map clause);
+
+ /*
+ Add a index hint to the tagged list of hints. The type and clause of the
+ hint will be the current ones (set by set_index_hint())
+ */
+ bool add_index_hint (THD *thd, char *str, uint length);
+
+ /* make a list to hold index hints */
+ void alloc_index_hints (THD *thd);
+ /* read and clear the index hints */
+ List<Index_hint>* pop_index_hints(void)
+ {
+ List<Index_hint> *hints= index_hints;
+ index_hints= NULL;
+ return hints;
+ }
+
+ void clear_index_hints(void) { index_hints= NULL; }
+
+private:
+ /* current index hint kind. used in filling up index_hints */
+ enum index_hint_type current_index_hint_type;
+ index_clause_map current_index_hint_clause;
+ /* a list of USE/FORCE/IGNORE INDEX */
+ List<Index_hint> *index_hints;
};
typedef class st_select_lex SELECT_LEX;
-
inline bool st_select_lex_unit::is_union ()
{
return first_select()->next_select() &&
first_select()->next_select()->linkage == UNION_TYPE;
}
-#define ALTER_ADD_COLUMN 1
-#define ALTER_DROP_COLUMN 2
-#define ALTER_CHANGE_COLUMN 4
-#define ALTER_ADD_INDEX 8
-#define ALTER_DROP_INDEX 16
-#define ALTER_RENAME 32
-#define ALTER_ORDER 64
-#define ALTER_OPTIONS 128
-#define ALTER_CHANGE_COLUMN_DEFAULT 256
-#define ALTER_KEYS_ONOFF 512
-#define ALTER_CONVERT 1024
-#define ALTER_FORCE 2048
+#define ALTER_ADD_COLUMN (1L << 0)
+#define ALTER_DROP_COLUMN (1L << 1)
+#define ALTER_CHANGE_COLUMN (1L << 2)
+#define ALTER_ADD_INDEX (1L << 3)
+#define ALTER_DROP_INDEX (1L << 4)
+#define ALTER_RENAME (1L << 5)
+#define ALTER_ORDER (1L << 6)
+#define ALTER_OPTIONS (1L << 7)
+#define ALTER_CHANGE_COLUMN_DEFAULT (1L << 8)
+#define ALTER_KEYS_ONOFF (1L << 9)
+#define ALTER_CONVERT (1L << 10)
+#define ALTER_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)
+
+enum enum_alter_table_change_level
+{
+ ALTER_TABLE_METADATA_ONLY= 0,
+ ALTER_TABLE_DATA_CHANGED= 1,
+ ALTER_TABLE_INDEX_CHANGED= 2
+};
/**
@brief Parsing data for CREATE or ALTER TABLE.
@@ -742,18 +876,28 @@ inline bool st_select_lex_unit::is_union ()
class Alter_info
{
public:
- List<Alter_drop> drop_list;
- List<Alter_column> alter_list;
- List<Key> key_list;
- List<create_field> create_list;
- uint flags;
- enum enum_enable_or_disable keys_onoff;
- enum tablespace_op_type tablespace_op;
+ List<Alter_drop> drop_list;
+ List<Alter_column> alter_list;
+ List<Key> key_list;
+ List<Create_field> create_list;
+ uint flags;
+ enum enum_enable_or_disable keys_onoff;
+ enum tablespace_op_type tablespace_op;
+ List<char> partition_names;
+ uint no_parts;
+ enum_alter_table_change_level change_level;
+ Create_field *datetime_field;
+ bool error_if_not_empty;
+
Alter_info() :
flags(0),
keys_onoff(LEAVE_AS_IS),
- tablespace_op(NO_TABLESPACE_OP)
+ tablespace_op(NO_TABLESPACE_OP),
+ no_parts(0),
+ change_level(ALTER_TABLE_METADATA_ONLY),
+ datetime_field(NULL),
+ error_if_not_empty(FALSE)
{}
void reset()
@@ -765,20 +909,12 @@ public:
flags= 0;
keys_onoff= LEAVE_AS_IS;
tablespace_op= NO_TABLESPACE_OP;
+ no_parts= 0;
+ partition_names.empty();
+ change_level= ALTER_TABLE_METADATA_ONLY;
+ datetime_field= 0;
+ error_if_not_empty= FALSE;
}
- /**
- Construct a copy of this object to be used for mysql_alter_table
- and mysql_create_table. Historically, these two functions modify
- their Alter_info arguments. This behaviour breaks re-execution of
- prepared statements and stored procedures and is compensated by
- always supplying a copy of Alter_info to these functions.
- The constructed copy still shares key Key, Alter_drop, create_field
- and Alter_column elements of the lists - these structures are not
- modified and thus are not copied.
-
- @note You need to use check thd->is_fatal_error for out
- of memory condition after calling this function.
- */
Alter_info(const Alter_info &rhs, MEM_ROOT *mem_root);
private:
Alter_info &operator=(const Alter_info &rhs); // not implemented
@@ -800,7 +936,7 @@ struct st_trg_chistics
enum trg_event_type event;
};
-extern sys_var_long_ptr trg_new_row_fake_var;
+extern sys_var *trg_new_row_fake_var;
enum xa_option_words {XA_NONE, XA_JOIN, XA_RESUME, XA_ONE_PHASE,
XA_SUSPEND, XA_FOR_MIGRATE};
@@ -847,7 +983,7 @@ public:
in which it was right after query parsing.
*/
SQL_LIST sroutines_list;
- byte **sroutines_list_own_last;
+ uchar **sroutines_list_own_last;
uint sroutines_list_own_elements;
/*
@@ -897,12 +1033,48 @@ public:
query_tables_own_last= 0;
}
}
+
+ /**
+ Has the parser/scanner detected that this statement is unsafe?
+ */
+ inline bool is_stmt_unsafe() const {
+ return binlog_stmt_flags & (1U << BINLOG_STMT_FLAG_UNSAFE);
+ }
+
+ /**
+ Flag the current (top-level) statement as unsafe.
+
+ The flag will be reset after the statement has finished.
+
+ */
+ inline void set_stmt_unsafe() {
+ binlog_stmt_flags|= (1U << BINLOG_STMT_FLAG_UNSAFE);
+ }
+
+ inline void clear_stmt_unsafe() {
+ binlog_stmt_flags&= ~(1U << BINLOG_STMT_FLAG_UNSAFE);
+ }
+
/**
true if the parsed tree contains references to stored procedures
or functions, false otherwise
*/
bool uses_stored_routines() const
{ return sroutines_list.elements != 0; }
+
+private:
+ enum enum_binlog_stmt_flag {
+ BINLOG_STMT_FLAG_UNSAFE,
+ BINLOG_STMT_FLAG_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.
+ */
+ uint32 binlog_stmt_flags;
};
@@ -924,15 +1096,282 @@ struct st_parsing_options
/**
- This class represents the character input stream consumed during
+ The state of the lexical parser, when parsing comments.
+*/
+enum enum_comment_state
+{
+ /**
+ Not parsing comments.
+ */
+ NO_COMMENT,
+ /**
+ Parsing comments that need to be preserved.
+ Typically, these are user comments '/' '*' ... '*' '/'.
+ */
+ PRESERVE_COMMENT,
+ /**
+ Parsing comments that need to be discarded.
+ Typically, these are special comments '/' '*' '!' ... '*' '/',
+ or '/' '*' '!' 'M' 'M' 'm' 'm' 'm' ... '*' '/', where the comment
+ markers should not be expanded.
+ */
+ DISCARD_COMMENT
+};
+
+
+/**
+ @brief This class represents the character input stream consumed during
lexical analysis.
+
+ In addition to consuming the input stream, this class performs some
+ comment pre processing, by filtering out out of bound special text
+ from the query input stream.
+ Two buffers, with pointers inside each buffers, are maintained in
+ parallel. The 'raw' buffer is the original query text, which may
+ contain out-of-bound comments. The 'cpp' (for comments pre processor)
+ is the pre-processed buffer that contains only the query text that
+ should be seen once out-of-bound data is removed.
*/
+
class Lex_input_stream
{
public:
Lex_input_stream(THD *thd, const char* buff, unsigned int length);
~Lex_input_stream();
+ /**
+ Set the echo mode.
+
+ When echo is true, characters parsed from the raw input stream are
+ preserved. When false, characters parsed are silently ignored.
+ @param echo the echo mode.
+ */
+ void set_echo(bool echo)
+ {
+ m_echo= echo;
+ }
+
+ /**
+ Skip binary from the input stream.
+ @param n number of bytes to accept.
+ */
+ void skip_binary(int n)
+ {
+ if (m_echo)
+ {
+ memcpy(m_cpp_ptr, m_ptr, n);
+ m_cpp_ptr += n;
+ }
+ m_ptr += n;
+ }
+
+ /**
+ Get a character, and advance in the stream.
+ @return the next character to parse.
+ */
+ char yyGet()
+ {
+ char c= *m_ptr++;
+ if (m_echo)
+ *m_cpp_ptr++ = c;
+ return c;
+ }
+
+ /**
+ Get the last character accepted.
+ @return the last character accepted.
+ */
+ char yyGetLast()
+ {
+ return m_ptr[-1];
+ }
+
+ /**
+ Look at the next character to parse, but do not accept it.
+ */
+ char yyPeek()
+ {
+ return m_ptr[0];
+ }
+
+ /**
+ Look ahead at some character to parse.
+ @param n offset of the character to look up
+ */
+ char yyPeekn(int n)
+ {
+ return m_ptr[n];
+ }
+
+ /**
+ Cancel the effect of the last yyGet() or yySkip().
+ Note that the echo mode should not change between calls to yyGet / yySkip
+ and yyUnget. The caller is responsible for ensuring that.
+ */
+ void yyUnget()
+ {
+ m_ptr--;
+ if (m_echo)
+ m_cpp_ptr--;
+ }
+
+ /**
+ Accept a character, by advancing the input stream.
+ */
+ void yySkip()
+ {
+ if (m_echo)
+ *m_cpp_ptr++ = *m_ptr++;
+ else
+ m_ptr++;
+ }
+
+ /**
+ Accept multiple characters at once.
+ @param n the number of characters to accept.
+ */
+ void yySkipn(int n)
+ {
+ if (m_echo)
+ {
+ memcpy(m_cpp_ptr, m_ptr, n);
+ m_cpp_ptr += n;
+ }
+ m_ptr += n;
+ }
+
+ /**
+ End of file indicator for the query text to parse.
+ @return true if there are no more characters to parse
+ */
+ bool eof()
+ {
+ return (m_ptr >= m_end_of_query);
+ }
+
+ /**
+ End of file indicator for the query text to parse.
+ @param n number of characters expected
+ @return true if there are less than n characters to parse
+ */
+ bool eof(int n)
+ {
+ return ((m_ptr + n) >= m_end_of_query);
+ }
+
+ /** Get the raw query buffer. */
+ const char *get_buf()
+ {
+ return m_buf;
+ }
+
+ /** Get the pre-processed query buffer. */
+ const char *get_cpp_buf()
+ {
+ return m_cpp_buf;
+ }
+
+ /** Get the end of the raw query buffer. */
+ const char *get_end_of_query()
+ {
+ return m_end_of_query;
+ }
+
+ /** Mark the stream position as the start of a new token. */
+ void start_token()
+ {
+ m_tok_start_prev= m_tok_start;
+ m_tok_start= m_ptr;
+ m_tok_end= m_ptr;
+
+ m_cpp_tok_start_prev= m_cpp_tok_start;
+ m_cpp_tok_start= m_cpp_ptr;
+ m_cpp_tok_end= m_cpp_ptr;
+ }
+
+ /**
+ Adjust the starting position of the current token.
+ This is used to compensate for starting whitespace.
+ */
+ void restart_token()
+ {
+ m_tok_start= m_ptr;
+ m_cpp_tok_start= m_cpp_ptr;
+ }
+
+ /** Get the token start position, in the raw buffer. */
+ const char *get_tok_start()
+ {
+ return m_tok_start;
+ }
+
+ /** Get the token start position, in the pre-processed buffer. */
+ const char *get_cpp_tok_start()
+ {
+ return m_cpp_tok_start;
+ }
+
+ /** Get the token end position, in the raw buffer. */
+ const char *get_tok_end()
+ {
+ return m_tok_end;
+ }
+
+ /** Get the token end position, in the pre-processed buffer. */
+ const char *get_cpp_tok_end()
+ {
+ return m_cpp_tok_end;
+ }
+
+ /** Get the previous token start position, in the raw buffer. */
+ const char *get_tok_start_prev()
+ {
+ return m_tok_start_prev;
+ }
+
+ /** Get the current stream pointer, in the raw buffer. */
+ const char *get_ptr()
+ {
+ return m_ptr;
+ }
+
+ /** Get the current stream pointer, in the pre-processed buffer. */
+ const char *get_cpp_ptr()
+ {
+ return m_cpp_ptr;
+ }
+
+ /** Get the length of the current token, in the raw buffer. */
+ uint yyLength()
+ {
+ /*
+ The assumption is that the lexical analyser is always 1 character ahead,
+ which the -1 account for.
+ */
+ DBUG_ASSERT(m_ptr > m_tok_start);
+ return (uint) ((m_ptr - m_tok_start) - 1);
+ }
+
+ /** Get the utf8-body string. */
+ const char *get_body_utf8_str()
+ {
+ return m_body_utf8;
+ }
+
+ /** Get the utf8-body length. */
+ uint get_body_utf8_length()
+ {
+ return (uint) (m_body_utf8_ptr - m_body_utf8);
+ }
+
+ void body_utf8_start(THD *thd, const char *begin_ptr);
+ void body_utf8_append(const char *ptr);
+ void body_utf8_append(const char *ptr, const char *end_ptr);
+ void body_utf8_append_literal(THD *thd,
+ const LEX_STRING *txt,
+ CHARSET_INFO *txt_cs,
+ const char *end_ptr);
+
/** Current thread. */
THD *m_thd;
@@ -945,40 +1384,115 @@ public:
/** Interface with bison, value of the last token parsed. */
LEX_YYSTYPE yylval;
- /** Pointer to the current position in the input stream. */
- const char* ptr;
+private:
+ /** Pointer to the current position in the raw input stream. */
+ const char *m_ptr;
+
+ /** Starting position of the last token parsed, in the raw buffer. */
+ const char *m_tok_start;
+
+ /** Ending position of the previous token parsed, in the raw buffer. */
+ const char *m_tok_end;
- /** Starting position of the last token parsed. */
- const char* tok_start;
+ /** End of the query text in the input stream, in the raw buffer. */
+ const char *m_end_of_query;
- /** Ending position of the last token parsed. */
- const char* tok_end;
+ /** Starting position of the previous token parsed, in the raw buffer. */
+ const char *m_tok_start_prev;
- /** End of the query text in the input stream. */
- const char* end_of_query;
+ /** Begining of the query text in the input stream, in the raw buffer. */
+ const char *m_buf;
- /** Starting position of the previous token parsed. */
- const char* tok_start_prev;
+ /** Length of the raw buffer. */
+ uint m_buf_length;
- /** Begining of the query text in the input stream. */
- const char* buf;
+ /** Echo the parsed stream to the pre-processed buffer. */
+ bool m_echo;
+
+ /** Pre-processed buffer. */
+ char *m_cpp_buf;
+
+ /** Pointer to the current position in the pre-processed input stream. */
+ char *m_cpp_ptr;
+
+ /**
+ Starting position of the last token parsed,
+ in the pre-processed buffer.
+ */
+ const char *m_cpp_tok_start;
+
+ /**
+ Starting position of the previous token parsed,
+ in the pre-procedded buffer.
+ */
+ const char *m_cpp_tok_start_prev;
+
+ /**
+ Ending position of the previous token parsed,
+ in the pre-processed buffer.
+ */
+ const char *m_cpp_tok_end;
+
+ /** UTF8-body buffer created during parsing. */
+ char *m_body_utf8;
+
+ /** Pointer to the current position in the UTF8-body buffer. */
+ char *m_body_utf8_ptr;
+
+ /**
+ Position in the pre-processed buffer. The query from m_cpp_buf to
+ m_cpp_utf_processed_ptr is converted to UTF8-body.
+ */
+ const char *m_cpp_utf8_processed_ptr;
+
+public:
/** Current state of the lexical analyser. */
enum my_lex_states next_state;
- /** Position of ';' in the stream, to delimit multiple queries. */
- const char* found_semicolon;
-
+ /**
+ Position of ';' in the stream, to delimit multiple queries.
+ This delimiter is in the raw buffer.
+ */
+ const char *found_semicolon;
+
/** Token character bitmaps, to detect 7bit strings. */
uchar tok_bitmap;
/** SQL_MODE = IGNORE_SPACE. */
bool ignore_space;
- /*
+
+ /**
TRUE if we're parsing a prepared statement: in this mode
we should allow placeholders and disallow multi-statements.
*/
bool stmt_prepare_mode;
+
+ /** State of the lexical analyser for comments. */
+ enum_comment_state in_comment;
+
+ /**
+ Starting position of the TEXT_STRING or IDENT in the pre-processed
+ buffer.
+
+ NOTE: this member must be used within MYSQLlex() function only.
+ */
+ const char *m_cpp_text_start;
+
+ /**
+ Ending position of the TEXT_STRING or IDENT in the pre-processed
+ buffer.
+
+ NOTE: this member must be used within MYSQLlex() function only.
+ */
+ const char *m_cpp_text_end;
+
+ /**
+ Character set specified by the character-set-introducer.
+
+ NOTE: this member must be used within MYSQLlex() function only.
+ */
+ CHARSET_INFO *m_underscore_cs;
};
@@ -993,7 +1507,8 @@ typedef struct st_lex : public Query_tables_list
/* list of all SELECT_LEX */
SELECT_LEX *all_selects_list;
- char *length,*dec,*change,*name;
+ char *length,*dec,*change;
+ LEX_STRING name;
char *help_arg;
char *backup_dir; /* For RESTORE/BACKUP */
char* to_log; /* For PURGE MASTER LOGS TO */
@@ -1006,12 +1521,26 @@ typedef struct st_lex : public Query_tables_list
LEX_USER *grant_user;
XID *xid;
THD *thd;
- CHARSET_INFO *charset, *underscore_charset;
+
+ /* maintain a list of used plugins for this LEX */
+ DYNAMIC_ARRAY plugins;
+ plugin_ref plugins_static_buffer[INITIAL_LEX_PLUGIN_LIST_SIZE];
+
+ CHARSET_INFO *charset;
bool text_string_is_7bit;
/* store original leaf_tables for INSERT SELECT and PS/SP */
TABLE_LIST *leaf_tables_insert;
- /* Position (first character index) of SELECT of CREATE VIEW statement */
- uint create_view_select_start;
+
+ /** SELECT of CREATE VIEW statement */
+ LEX_STRING create_view_select;
+
+ /** Start of 'ON table', in trigger statements. */
+ const char* raw_trg_on_table_name_begin;
+ /** End of 'ON table', in trigger statements. */
+ const char* raw_trg_on_table_name_end;
+
+ /* Partition info structure filled in by PARTITION BY parse part */
+ partition_info *part_info;
/*
The definer of the object being created (view, trigger, stored routine).
@@ -1019,14 +1548,15 @@ typedef struct st_lex : public Query_tables_list
*/
LEX_USER *definer;
- List<key_part_spec> col_list;
- List<key_part_spec> ref_list;
+ List<Key_part_spec> col_list;
+ List<Key_part_spec> ref_list;
List<String> interval_list;
List<LEX_USER> users_list;
List<LEX_COLUMN> columns;
List<Item> *insert_list,field_list,value_list,update_list;
List<List_item> many_values;
List<set_var_base> var_list;
+ List<Item_func_set_user_var> set_var_list; // in-query assignment list
List<Item_param> param_list;
List<LEX_STRING> view_list; // view list (list of field names in view)
/*
@@ -1045,12 +1575,14 @@ typedef struct st_lex : public Query_tables_list
List<Name_resolution_context> context_stack;
SQL_LIST proc_list, auxiliary_table_list, save_list;
- create_field *last_field;
+ Create_field *last_field;
Item_sum *in_sum_func;
udf_func udf;
HA_CHECK_OPT check_opt; // check/repair options
HA_CREATE_INFO create_info;
+ KEY_CREATE_INFO key_create_info;
LEX_MASTER_INFO mi; // used by CHANGE MASTER
+ LEX_SERVER_OPTIONS server_options;
USER_RESOURCES mqh;
ulong type;
/*
@@ -1063,7 +1595,15 @@ 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, orig_sql_command;
+ enum_sql_command sql_command;
+ /*
+ Usually `expr` rule of yacc is quite reused but some commands better
+ not support subqueries which comes standard with this rule, like
+ KILL, HA_READ, CREATE/ALTER EVENT etc. Set this to `false` to get
+ syntax error back.
+ */
+ bool expr_allows_subselect;
+
thr_lock_type lock_option;
enum SSL_type ssl_type; /* defined in violite.h */
enum enum_duplicates duplicates;
@@ -1076,6 +1616,9 @@ typedef struct st_lex : public Query_tables_list
enum enum_var_type option_type;
enum enum_view_create_mode create_view_mode;
enum enum_drop_mode drop_mode;
+
+ uint profile_query_id;
+ uint profile_options;
uint uint_geom_type;
uint grant, grant_tot_col, which_columns;
uint fk_delete_opt, fk_update_opt, fk_match_option;
@@ -1097,7 +1640,9 @@ typedef struct st_lex : public Query_tables_list
uint8 create_view_algorithm;
uint8 create_view_check;
bool drop_if_exists, drop_temporary, local_file, one_shot_set;
- bool in_comment, verbose, no_write_to_binlog;
+ bool autocommit;
+ bool verbose, no_write_to_binlog;
+
bool tx_chain, tx_release;
/*
Special JOIN::prepare mode: changing of query is prohibited.
@@ -1122,11 +1667,6 @@ typedef struct st_lex : public Query_tables_list
bool prepared_stmt_code_is_varref;
/* Names of user variables holding parameters (in EXECUTE) */
List<LEX_STRING> prepared_stmt_params;
- /*
- Points to part of global table list which contains time zone tables
- implicitly used by the statement.
- */
- TABLE_LIST *time_zone_tables_used;
sp_head *sphead;
sp_name *spname;
bool sp_lex_in_use; /* Keep track on lex usage in SPs for error handling */
@@ -1134,6 +1674,9 @@ typedef struct st_lex : public Query_tables_list
sp_pcontext *spcont;
st_sp_chistics sp_chistics;
+
+ Event_parse_data *event_parse_data;
+
bool only_view; /* used for SHOW CREATE TABLE/VIEW */
/*
field_list was created for view and should be removed before PS/SP
@@ -1163,24 +1706,43 @@ typedef struct st_lex : public Query_tables_list
- CREATE FUNCTION (points to "FUNCTION" or "AGGREGATE");
This pointer is required to add possibly omitted DEFINER-clause to the
- DDL-statement before dumping it to the binlog.
+ DDL-statement before dumping it to the binlog.
*/
const char *stmt_definition_begin;
+ const char *stmt_definition_end;
+
/*
Pointers to part of LOAD DATA statement that should be rewritten
during replication ("LOCAL 'filename' REPLACE INTO" part).
*/
const char *fname_start;
const char *fname_end;
+
+ /**
+ During name resolution search only in the table list given by
+ Name_resolution_context::first_name_resolution_table and
+ Name_resolution_context::last_name_resolution_table
+ (see Item_field::fix_fields()).
+ */
+ bool use_only_table_context;
+ /*
+ Reference to a struct that contains information in various commands
+ to add/create/drop/change table spaces.
+ */
+ st_alter_tablespace *alter_tablespace_info;
+
bool escape_used;
+ bool is_lex_started; /* If lex_start() did run. For debugging. */
st_lex();
virtual ~st_lex()
{
destroy_query_tables_list();
+ plugin_unlock_list(NULL, (plugin_ref *)plugins.buffer, plugins.elements);
+ delete_dynamic(&plugins);
}
inline void uncacheable(uint8 cause)
@@ -1207,7 +1769,6 @@ typedef struct st_lex : public Query_tables_list
TABLE_LIST *unlink_first_table(bool *link_to_local);
void link_first_table_back(TABLE_LIST *first, bool link_to_local);
void first_lists_tables_same();
- bool add_time_zone_tables_to_query_tables(THD *thd);
bool can_be_merged();
bool can_use_merged();
@@ -1253,7 +1814,7 @@ typedef struct st_lex : public Query_tables_list
context_stack.pop();
}
- bool copy_db_to(char **p_db, uint *p_db_length) const;
+ bool copy_db_to(char **p_db, size_t *p_db_length) const;
Name_resolution_context *current_context()
{
@@ -1267,6 +1828,9 @@ typedef struct st_lex : public Query_tables_list
void reset_n_backup_query_tables_list(Query_tables_list *backup);
void restore_backup_query_tables_list(Query_tables_list *backup);
+ bool table_or_sp_used();
+ bool is_partition_management() const;
+
/**
@brief check if the statement is a single-level join
@return result of the check
@@ -1309,13 +1873,13 @@ public:
Bison internal state stack, yyss, when dynamically allocated using
my_yyoverflow().
*/
- gptr yacc_yyss;
+ uchar *yacc_yyss;
/**
Bison internal semantic value stack, yyvs, when dynamically allocated using
my_yyoverflow().
*/
- gptr yacc_yyvs;
+ uchar *yacc_yyvs;
/*
TODO: move more attributes from the LEX structure here.
@@ -1347,7 +1911,7 @@ struct st_lex_local: public st_lex
{
static void *operator new(size_t size) throw()
{
- return (void*) sql_alloc((uint) size);
+ return sql_alloc(size);
}
static void *operator new(size_t size, MEM_ROOT *mem_root) throw()
{
@@ -1364,5 +1928,13 @@ extern void lex_free(void);
extern void lex_start(THD *thd);
extern void lex_end(LEX *lex);
extern int MYSQLlex(void *arg, void *yythd);
-extern char *skip_rear_comments(CHARSET_INFO *cs, char *begin, char *end);
+extern void trim_whitespace(CHARSET_INFO *cs, LEX_STRING *str);
+
+extern bool is_lex_native_function(const LEX_STRING *name);
+
+/**
+ @} (End of group Semantic_Analysis)
+*/
+
+#endif /* MYSQL_SERVER */
diff --git a/sql/sql_list.cc b/sql/sql_list.cc
index 01ab9b91424..49b649133d0 100644
--- a/sql/sql_list.cc
+++ b/sql/sql_list.cc
@@ -36,3 +36,37 @@ void free_list(I_List <i_string> *list)
while ((tmp= list->get()))
delete tmp;
}
+
+
+base_list::base_list(const base_list &rhs, MEM_ROOT *mem_root)
+{
+ if (rhs.elements)
+ {
+ /*
+ It's okay to allocate an array of nodes at once: we never
+ call a destructor for list_node objects anyway.
+ */
+ first= (list_node*) alloc_root(mem_root,
+ sizeof(list_node) * rhs.elements);
+ if (first)
+ {
+ elements= rhs.elements;
+ list_node *dst= first;
+ list_node *src= rhs.first;
+ for (; dst < first + elements - 1; dst++, src= src->next)
+ {
+ dst->info= src->info;
+ dst->next= dst + 1;
+ }
+ /* Copy the last node */
+ dst->info= src->info;
+ dst->next= &end_of_list;
+ /* Setup 'last' member */
+ last= &dst->next;
+ return;
+ }
+ }
+ elements= 0;
+ first= &end_of_list;
+ last= &first;
+}
diff --git a/sql/sql_list.h b/sql/sql_list.h
index 1ad2051f065..0d267111dad 100644
--- a/sql/sql_list.h
+++ b/sql/sql_list.h
@@ -1,3 +1,5 @@
+#ifndef INCLUDES_MYSQL_SQL_LIST_H
+#define INCLUDES_MYSQL_SQL_LIST_H
/* Copyright (C) 2000-2003 MySQL AB
This program is free software; you can redistribute it and/or modify
@@ -25,19 +27,21 @@ class Sql_alloc
public:
static void *operator new(size_t size) throw ()
{
- return (void*) sql_alloc((uint) size);
+ return sql_alloc(size);
}
static void *operator new[](size_t size) throw ()
{
- return (void*) sql_alloc((uint) size);
+ return sql_alloc(size);
}
static void *operator new[](size_t size, MEM_ROOT *mem_root) throw ()
- { return (void*) alloc_root(mem_root, (uint) size); }
+ { return alloc_root(mem_root, size); }
static void *operator new(size_t size, MEM_ROOT *mem_root) throw ()
- { return (void*) alloc_root(mem_root, (uint) size); }
+ { return alloc_root(mem_root, size); }
static void operator delete(void *ptr, size_t size) { TRASH(ptr, size); }
static void operator delete(void *ptr, MEM_ROOT *mem_root)
{ /* never called */ }
+ static void operator delete[](void *ptr, MEM_ROOT *mem_root)
+ { /* never called */ }
static void operator delete[](void *ptr, size_t size) { TRASH(ptr, size); }
#ifdef HAVE_purify
bool dummy;
@@ -111,40 +115,11 @@ public:
}
/**
Construct a deep copy of the argument in memory root mem_root.
- The elements themselves are copied by pointer.
+ The elements themselves are copied by pointer. If you also
+ need to copy elements by value, you should employ
+ list_copy_and_replace_each_value after creating a copy.
*/
- inline base_list(const base_list &rhs, MEM_ROOT *mem_root)
- {
- if (rhs.elements)
- {
- /*
- It's okay to allocate an array of nodes at once: we never
- call a destructor for list_node objects anyway.
- */
- first= (list_node*) alloc_root(mem_root,
- sizeof(list_node) * rhs.elements);
- if (first)
- {
- elements= rhs.elements;
- list_node *dst= first;
- list_node *src= rhs.first;
- for (; dst < first + elements - 1; dst++, src= src->next)
- {
- dst->info= src->info;
- dst->next= dst + 1;
- }
- /* Copy the last node */
- dst->info= src->info;
- dst->next= &end_of_list;
- /* Setup 'last' member */
- last= &dst->next;
- return;
- }
- }
- elements= 0;
- first= &end_of_list;
- last= &first;
- }
+ base_list(const base_list &rhs, MEM_ROOT *mem_root);
inline base_list(bool error) { }
inline bool push_back(void *info)
{
@@ -231,6 +206,15 @@ public:
elements+= list->elements;
}
}
+ /**
+ Swap two lists.
+ */
+ inline void swap(base_list &rhs)
+ {
+ swap_variables(list_node *, first, rhs.first);
+ swap_variables(list_node **, last, rhs.last);
+ swap_variables(uint, elements, rhs.elements);
+ }
inline list_node* last_node() { return *last; }
inline list_node* first_node() { return first;}
inline void *head() { return first->info; }
@@ -472,7 +456,7 @@ struct ilink
}
static void operator delete(void* ptr_arg, size_t size)
{
- my_free((gptr)ptr_arg, MYF(MY_WME|MY_ALLOW_ZERO_PTR));
+ my_free((uchar*)ptr_arg, MYF(MY_WME|MY_ALLOW_ZERO_PTR));
}
inline ilink()
@@ -490,6 +474,28 @@ struct ilink
};
+/* Needed to be able to have an I_List of char* strings in mysqld.cc. */
+
+class i_string: public ilink
+{
+public:
+ const char* ptr;
+ i_string():ptr(0) { }
+ i_string(const char* s) : ptr(s) {}
+};
+
+/* needed for linked list of two strings for replicate-rewrite-db */
+class i_string_pair: public ilink
+{
+public:
+ const char* key;
+ const char* val;
+ i_string_pair():key(0),val(0) { }
+ i_string_pair(const char* key_arg, const char* val_arg) :
+ key(key_arg),val(val_arg) {}
+};
+
+
template <class T> class I_List_iterator;
/*
@@ -573,3 +579,32 @@ public:
I_List_iterator(I_List<T> &a) : base_ilist_iterator(a) {}
inline T* operator++(int) { return (T*) base_ilist_iterator::next(); }
};
+
+/**
+ Make a deep copy of each list element.
+
+ @note A template function and not a template method of class List
+ is employed because of explicit template instantiation:
+ in server code there are explicit instantiations of List<T> and
+ an explicit instantiation of a template requires that any method
+ of the instantiated class used in the template can be resolved.
+ Evidently not all template arguments have clone() method with
+ the right signature.
+
+ @return You must query the error state in THD for out-of-memory
+ situation after calling this function.
+*/
+
+template <typename T>
+inline
+void
+list_copy_and_replace_each_value(List<T> &list, MEM_ROOT *mem_root)
+{
+ /* Make a deep copy of each element */
+ List_iterator<T> it(list);
+ T *el;
+ while ((el= it++))
+ it.replace(el->clone(mem_root));
+}
+
+#endif // INCLUDES_MYSQL_SQL_LIST_H
diff --git a/sql/sql_load.cc b/sql/sql_load.cc
index 5b9f8ea441d..a73286d65a1 100644
--- a/sql/sql_load.cc
+++ b/sql/sql_load.cc
@@ -25,7 +25,7 @@
class READ_INFO {
File file;
- byte *buffer, /* Buffer for read text */
+ 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 */
@@ -40,7 +40,7 @@ class READ_INFO {
public:
bool error,line_cuted,found_null,enclosed;
- byte *row_start, /* Found row starts here */
+ uchar *row_start, /* Found row starts here */
*row_end; /* Found row ends here */
CHARSET_INFO *read_charset;
@@ -118,11 +118,10 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list,
{
char name[FN_REFLEN];
File file;
- TABLE *table;
+ TABLE *table= NULL;
int error;
String *field_term=ex->field_term,*escaped=ex->escaped;
String *enclosed=ex->enclosed;
- Item *unused_conds= 0;
bool is_fifo=0;
#ifndef EMBEDDED_LIBRARY
LOAD_FILE_INFO lf_info;
@@ -153,7 +152,7 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list,
DBUG_RETURN(TRUE);
if (setup_tables_and_check_access(thd, &thd->lex->select_lex.context,
&thd->lex->select_lex.top_join_list,
- table_list, &unused_conds,
+ table_list,
&thd->lex->select_lex.leaf_tables, FALSE,
INSERT_ACL | UPDATE_ACL,
INSERT_ACL | UPDATE_ACL))
@@ -176,7 +175,7 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list,
The main thing to fix to remove this restriction is to ensure that the
table is marked to be 'used for insert' in which case we should never
- mark this table as as 'const table' (ie, one that has only one row).
+ mark this table as 'const table' (ie, one that has only one row).
*/
if (unique_table(thd, table_list, table_list->next_global, 0))
{
@@ -192,39 +191,44 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list,
Field **field;
for (field=table->field; *field ; field++)
fields_vars.push_back(new Item_field(*field));
+ bitmap_set_all(table->write_set);
table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET;
/*
Let us also prepare SET clause, altough it is probably empty
in this case.
*/
- if (setup_fields(thd, 0, set_fields, 1, 0, 0) ||
- setup_fields(thd, 0, set_values, 1, 0, 0))
+ if (setup_fields(thd, 0, set_fields, MARK_COLUMNS_WRITE, 0, 0) ||
+ setup_fields(thd, 0, set_values, MARK_COLUMNS_READ, 0, 0))
DBUG_RETURN(TRUE);
}
else
{ // Part field list
/* TODO: use this conds for 'WITH CHECK OPTIONS' */
- if (setup_fields(thd, 0, fields_vars, 1, 0, 0) ||
- setup_fields(thd, 0, set_fields, 1, 0, 0) ||
+ if (setup_fields(thd, 0, fields_vars, MARK_COLUMNS_WRITE, 0, 0) ||
+ setup_fields(thd, 0, set_fields, MARK_COLUMNS_WRITE, 0, 0) ||
check_that_all_fields_are_given_values(thd, table, table_list))
DBUG_RETURN(TRUE);
/*
Check whenever TIMESTAMP field with auto-set feature specified
explicitly.
*/
- if (table->timestamp_field &&
- table->timestamp_field->query_id == thd->query_id)
- table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET;
- /*
- Fix the expressions in SET clause. This should be done after
- check_that_all_fields_are_given_values() and setting use_timestamp
- since it may update query_id for some fields.
- */
- if (setup_fields(thd, 0, set_values, 1, 0, 0))
+ if (table->timestamp_field)
+ {
+ if (bitmap_is_set(table->write_set,
+ table->timestamp_field->field_index))
+ table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET;
+ else
+ {
+ bitmap_set_bit(table->write_set,
+ table->timestamp_field->field_index);
+ }
+ }
+ /* Fix the expressions in SET clause */
+ if (setup_fields(thd, 0, set_values, MARK_COLUMNS_READ, 0, 0))
DBUG_RETURN(TRUE);
}
- prepare_triggers_for_insert_stmt(thd, table, handle_duplicates);
+ prepare_triggers_for_insert_stmt(table);
uint tot_length=0;
bool use_blobs= 0, use_vars= 0;
@@ -279,7 +283,7 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list,
#endif
if (!dirname_length(ex->file_name))
{
- strxnmov(name, FN_REFLEN, mysql_real_data_home, tdb, NullS);
+ strxnmov(name, FN_REFLEN-1, mysql_real_data_home, tdb, NullS);
(void) fn_format(name, ex->file_name, name, "",
MY_RELATIVE_PATH | MY_UNPACK_FILENAME);
}
@@ -287,7 +291,7 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list,
{
(void) fn_format(name, ex->file_name, mysql_real_data_home, "",
MY_RELATIVE_PATH | MY_UNPACK_FILENAME);
-#if !defined(__WIN__) && !defined(OS2) && ! defined(__NETWARE__)
+#if !defined(__WIN__) && ! defined(__NETWARE__)
MY_STAT stat_info;
if (!my_stat(name,&stat_info,MYF(MY_WME)))
DBUG_RETURN(TRUE);
@@ -295,9 +299,7 @@ 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
-#ifndef __EMX__
(stat_info.st_mode & S_IFLNK) != S_IFLNK && // and not a symlink
-#endif
((stat_info.st_mode & S_IFREG) == S_IFREG ||
(stat_info.st_mode & S_IFIFO) == S_IFIFO)))
{
@@ -372,15 +374,12 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list,
if (ignore ||
handle_duplicates == DUP_REPLACE)
table->file->extra(HA_EXTRA_IGNORE_DUP_KEY);
- if (handle_duplicates == DUP_REPLACE)
- {
- if (!table->triggers ||
- !table->triggers->has_delete_triggers())
+ if (handle_duplicates == DUP_REPLACE &&
+ (!table->triggers ||
+ !table->triggers->has_delete_triggers()))
table->file->extra(HA_EXTRA_WRITE_CAN_REPLACE);
- table->file->extra(HA_EXTRA_RETRIEVE_ALL_COLS);
- }
if (!thd->prelocked_mode)
- table->file->start_bulk_insert((ha_rows) 0);
+ table->file->ha_start_bulk_insert((ha_rows) 0);
table->copy_blobs=1;
thd->abort_on_warning= (!ignore &&
@@ -396,7 +395,7 @@ 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);
- if (!thd->prelocked_mode && table->file->end_bulk_insert() && !error)
+ if (!thd->prelocked_mode && table->file->ha_end_bulk_insert() && !error)
{
table->file->print_error(my_errno, MYF(0));
error= 1;
@@ -434,46 +433,47 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list,
#ifndef EMBEDDED_LIBRARY
if (mysql_bin_log.is_open())
{
- /*
- Make sure last block (the one which caused the error) gets logged.
- This is needed because otherwise after write of
- (to the binlog, not to read_info (which is a cache))
- Delete_file_log_event the bad block will remain in read_info (because
- pre_read is not called at the end of the last block; remember pre_read
- is called whenever a new block is read from disk).
- At the end of mysql_load(), the destructor of read_info will call
- end_io_cache() which will flush read_info, so we will finally have
- this in the binlog:
- Append_block # The last successfull block
- Delete_file
- Append_block # The failing block
- which is nonsense.
- Or could also be (for a small file)
- Create_file # The failing block
- which is nonsense (Delete_file is not written in this case, because:
- Create_file has not been written, so Delete_file is not written, then
- when read_info is destroyed end_io_cache() is called which writes
- Create_file.
- */
- read_info.end_io_cache();
- /* If the file was not empty, wrote_create_file is true */
- if (lf_info.wrote_create_file)
{
- if (thd->transaction.stmt.modified_non_trans_table)
- write_execute_load_query_log_event(thd, handle_duplicates,
- ignore, transactional_table,
- killed_status);
- else
+ /*
+ Make sure last block (the one which caused the error) gets
+ logged. This is needed because otherwise after write of (to
+ the binlog, not to read_info (which is a cache))
+ Delete_file_log_event the bad block will remain in read_info
+ (because pre_read is not called at the end of the last
+ block; remember pre_read is called whenever a new block is
+ read from disk). At the end of mysql_load(), the destructor
+ of read_info will call end_io_cache() which will flush
+ read_info, so we will finally have this in the binlog:
+
+ Append_block # The last successfull block
+ Delete_file
+ Append_block # The failing block
+ which is nonsense.
+ Or could also be (for a small file)
+ Create_file # The failing block
+ which is nonsense (Delete_file is not written in this case, because:
+ Create_file has not been written, so Delete_file is not written, then
+ when read_info is destroyed end_io_cache() is called which writes
+ Create_file.
+ */
+ read_info.end_io_cache();
+ /* If the file was not empty, wrote_create_file is true */
+ if (lf_info.wrote_create_file)
{
- Delete_file_log_event d(thd, db, transactional_table);
- mysql_bin_log.write(&d);
+ if (thd->transaction.stmt.modified_non_trans_table)
+ write_execute_load_query_log_event(thd, handle_duplicates,
+ ignore, transactional_table,
+ killed_status);
+ else
+ {
+ Delete_file_log_event d(thd, db, transactional_table);
+ d.flags|= LOG_EVENT_UPDATE_TABLE_MAP_VERSION_F;
+ mysql_bin_log.write(&d);
+ }
}
}
}
#endif /*!EMBEDDED_LIBRARY*/
- if (transactional_table)
- ha_autocommit_or_rollback(thd,error);
-
error= -1; // Error on read
goto err;
}
@@ -486,30 +486,38 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list,
if (mysql_bin_log.is_open())
{
/*
- As already explained above, we need to call end_io_cache() or the last
- block will be logged only after Execute_load_query_log_event (which is
- wrong), when read_info is destroyed.
- */
- read_info.end_io_cache();
- if (lf_info.wrote_create_file)
- write_execute_load_query_log_event(thd, handle_duplicates,
- ignore, transactional_table,
- killed_status);
+ We need to do the job that is normally done inside
+ binlog_query() here, which is to ensure that the pending event
+ is written before tables are unlocked and before any other
+ events are written. We also need to update the table map
+ version for the binary log to mark that table maps are invalid
+ after this point.
+ */
+ if (thd->current_stmt_binlog_row_based)
+ thd->binlog_flush_pending_rows_event(true);
+ else
+ {
+ /*
+ As already explained above, we need to call end_io_cache() or the last
+ block will be logged only after Execute_load_query_log_event (which is
+ wrong), when read_info is destroyed.
+ */
+ read_info.end_io_cache();
+ if (lf_info.wrote_create_file)
+ {
+ write_execute_load_query_log_event(thd, handle_duplicates, ignore,
+ transactional_table,killed_status);
+ }
+ }
}
#endif /*!EMBEDDED_LIBRARY*/
- if (transactional_table)
- error=ha_autocommit_or_rollback(thd,error);
/* ok to client sent only after binlog write and engine commit */
- send_ok(thd, info.copied + info.deleted, 0L, name);
+ my_ok(thd, info.copied + info.deleted, 0L, name);
err:
DBUG_ASSERT(transactional_table || !(info.copied || info.deleted) ||
thd->transaction.stmt.modified_non_trans_table);
- if (thd->lock)
- {
- mysql_unlock_tables(thd, thd->lock);
- thd->lock=0;
- }
+ table->file->ha_release_auto_increment();
table->auto_increment_field_not_null= FALSE;
thd->abort_on_warning= 0;
DBUG_RETURN(error);
@@ -531,6 +539,7 @@ static bool write_execute_load_query_log_event(THD *thd,
(duplicates == DUP_REPLACE) ? LOAD_DUP_REPLACE :
(ignore ? LOAD_DUP_IGNORE : LOAD_DUP_ERROR),
transactional_table, FALSE, killed_err_arg);
+ e.flags|= LOG_EVENT_UPDATE_TABLE_MAP_VERSION_F;
return mysql_bin_log.write(&e);
}
@@ -574,7 +583,7 @@ read_fixed_length(THD *thd, COPY_INFO &info, TABLE_LIST *table_list,
continue;
}
it.rewind();
- byte *pos=read_info.row_start;
+ uchar *pos=read_info.row_start;
#ifdef HAVE_purify
read_info.row_end[0]=0;
#endif
@@ -608,7 +617,7 @@ read_fixed_length(THD *thd, COPY_INFO &info, TABLE_LIST *table_list,
else
{
uint length;
- byte save_chr;
+ uchar save_chr;
if ((length=(uint) (read_info.row_end-pos)) >
field->field_length)
length=field->field_length;
@@ -649,12 +658,6 @@ read_fixed_length(THD *thd, COPY_INFO &info, TABLE_LIST *table_list,
DBUG_RETURN(1);
/*
- If auto_increment values are used, save the first one for
- LAST_INSERT_ID() and for the binary/update log.
- */
- if (!id && thd->insert_id_used)
- id= thd->last_insert_id;
- /*
We don't need to reset auto-increment field since we are restoring
its default value at the beginning of each loop iteration.
*/
@@ -670,8 +673,6 @@ read_fixed_length(THD *thd, COPY_INFO &info, TABLE_LIST *table_list,
thd->row_count++;
continue_loop:;
}
- if (id && !read_info.error)
- thd->insert_id(id); // For binary/update log
DBUG_RETURN(test(read_info.error));
}
@@ -708,7 +709,7 @@ read_sep_field(THD *thd, COPY_INFO &info, TABLE_LIST *table_list,
while ((item= it++))
{
uint length;
- byte *pos;
+ uchar *pos;
Item *real_item;
if (read_info.read_field())
@@ -741,7 +742,7 @@ read_sep_field(THD *thd, COPY_INFO &info, TABLE_LIST *table_list,
field->set_null();
if (!field->maybe_null())
{
- if (field->type() == FIELD_TYPE_TIMESTAMP)
+ if (field->type() == MYSQL_TYPE_TIMESTAMP)
((Field_timestamp*) field)->set_time();
else if (field != table->next_number_field)
field->set_warning(MYSQL_ERROR::WARN_LEVEL_WARN,
@@ -853,12 +854,6 @@ read_sep_field(THD *thd, COPY_INFO &info, TABLE_LIST *table_list,
if (err)
DBUG_RETURN(1);
/*
- If auto_increment values are used, save the first one for
- LAST_INSERT_ID() and for the binary/update log.
- */
- if (!id && thd->insert_id_used)
- id= thd->last_insert_id;
- /*
We don't need to reset auto-increment field since we are restoring
its default value at the beginning of each loop iteration.
*/
@@ -876,8 +871,6 @@ read_sep_field(THD *thd, COPY_INFO &info, TABLE_LIST *table_list,
thd->row_count++;
continue_loop:;
}
- if (id && !read_info.error)
- thd->insert_id(id); // For binary/update log
DBUG_RETURN(test(read_info.error));
}
@@ -951,7 +944,7 @@ READ_INFO::READ_INFO(File file_par, uint tot_length, CHARSET_INFO *cs,
set_if_bigger(length,line_start.length());
stack=stack_pos=(int*) sql_alloc(sizeof(int)*length);
- if (!(buffer=(byte*) my_malloc(buff_length+1,MYF(0))))
+ if (!(buffer=(uchar*) my_malloc(buff_length+1,MYF(0))))
error=1; /* purecov: inspected */
else
{
@@ -961,7 +954,7 @@ 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((gptr) buffer,MYF(0)); /* purecov: inspected */
+ my_free((uchar*) buffer,MYF(0)); /* purecov: inspected */
error=1;
}
else
@@ -992,7 +985,7 @@ READ_INFO::~READ_INFO()
{
if (need_end_io_cache)
::end_io_cache(&cache);
- my_free((gptr) buffer,MYF(0));
+ my_free((uchar*) buffer,MYF(0));
error=1;
}
}
@@ -1025,7 +1018,7 @@ inline int READ_INFO::terminator(char *ptr,uint length)
int READ_INFO::read_field()
{
int chr,found_enclosed_char;
- byte *to,*new_buffer;
+ uchar *to,*new_buffer;
found_null=0;
if (found_end_of_line)
@@ -1048,7 +1041,7 @@ int READ_INFO::read_field()
if (chr == enclosed_char)
{
found_enclosed_char=enclosed_char;
- *to++=(byte) chr; // If error
+ *to++=(uchar) chr; // If error
}
else
{
@@ -1090,7 +1083,7 @@ int READ_INFO::read_field()
{
if ((chr=GET) == my_b_EOF)
{
- *to++= (byte) escape_char;
+ *to++= (uchar) escape_char;
goto found_eof;
}
/*
@@ -1102,7 +1095,7 @@ int READ_INFO::read_field()
*/
if (escape_char != enclosed_char || chr == escape_char)
{
- *to++ = (byte) unescape((char) chr);
+ *to++ = (uchar) unescape((char) chr);
continue;
}
PUSH(chr);
@@ -1127,7 +1120,7 @@ int READ_INFO::read_field()
{
if ((chr=GET) == found_enclosed_char)
{ // Remove dupplicated
- *to++ = (byte) chr;
+ *to++ = (uchar) chr;
continue;
}
// End of enclosed field if followed by field_term or line_term
@@ -1167,12 +1160,12 @@ int READ_INFO::read_field()
return 0;
}
}
- *to++ = (byte) chr;
+ *to++ = (uchar) chr;
}
/*
** We come here if buffer is too small. Enlarge it and continue
*/
- if (!(new_buffer=(byte*) my_realloc((char*) buffer,buff_length+1+IO_SIZE,
+ if (!(new_buffer=(uchar*) my_realloc((char*) buffer,buff_length+1+IO_SIZE,
MYF(MY_WME))))
return (error=1);
to=new_buffer + (to-buffer);
@@ -1207,7 +1200,7 @@ found_eof:
int READ_INFO::read_fixed_length()
{
int chr;
- byte *to;
+ uchar *to;
if (found_end_of_line)
return 1; // One have to call next_line
@@ -1227,10 +1220,10 @@ int READ_INFO::read_fixed_length()
{
if ((chr=GET) == my_b_EOF)
{
- *to++= (byte) escape_char;
+ *to++= (uchar) escape_char;
goto found_eof;
}
- *to++ =(byte) unescape((char) chr);
+ *to++ =(uchar) unescape((char) chr);
continue;
}
if (chr == line_term_char)
@@ -1242,7 +1235,7 @@ int READ_INFO::read_fixed_length()
return 0;
}
}
- *to++ = (byte) chr;
+ *to++ = (uchar) chr;
}
row_end=to; // Found full line
return 0;
@@ -1273,7 +1266,7 @@ int READ_INFO::next_line()
#ifdef USE_MB
if (my_mbcharlen(read_charset, chr) > 1)
{
- for (int i=1;
+ for (uint i=1;
chr != my_b_EOF && i<my_mbcharlen(read_charset, chr);
i++)
chr = GET;
diff --git a/sql/sql_manager.cc b/sql/sql_manager.cc
index 33905bdb913..b082f65bfb9 100644
--- a/sql/sql_manager.cc
+++ b/sql/sql_manager.cc
@@ -22,27 +22,56 @@
*/
#include "mysql_priv.h"
-#include "sql_manager.h"
-ulong volatile manager_status;
-bool volatile manager_thread_in_use;
+
+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;
+struct handler_cb {
+ struct handler_cb *next;
+ void (*action)(void);
+};
+
+static struct handler_cb * volatile cb_list;
+
+bool mysql_manager_submit(void (*action)())
+{
+ bool result= FALSE;
+ struct handler_cb * volatile *cb;
+ pthread_mutex_lock(&LOCK_manager);
+ cb= &cb_list;
+ while (*cb && (*cb)->action != action)
+ cb= &(*cb)->next;
+ if (!*cb)
+ {
+ *cb= (struct handler_cb *)my_malloc(sizeof(struct handler_cb), MYF(MY_WME));
+ if (!*cb)
+ result= TRUE;
+ else
+ {
+ (*cb)->next= NULL;
+ (*cb)->action= action;
+ }
+ }
+ pthread_mutex_unlock(&LOCK_manager);
+ return result;
+}
+
pthread_handler_t handle_manager(void *arg __attribute__((unused)))
{
int error = 0;
- ulong status;
struct timespec abstime;
bool reset_flush_time = TRUE;
+ struct handler_cb *cb= NULL;
my_thread_init();
DBUG_ENTER("handle_manager");
pthread_detach_this_thread();
manager_thread = pthread_self();
- manager_status = 0;
manager_thread_in_use = 1;
for (;;)
@@ -57,19 +86,22 @@ pthread_handler_t handle_manager(void *arg __attribute__((unused)))
set_timespec(abstime, flush_time);
reset_flush_time = FALSE;
}
- while (!manager_status && (!error || error == EINTR) && !abort_loop)
+ while ((!error || error == EINTR) && !abort_manager)
error= pthread_cond_timedwait(&COND_manager, &LOCK_manager, &abstime);
}
else
{
- while (!manager_status && (!error || error == EINTR) && !abort_loop)
+ while ((!error || error == EINTR) && !abort_manager)
error= pthread_cond_wait(&COND_manager, &LOCK_manager);
}
- status = manager_status;
- manager_status = 0;
+ if (cb == NULL)
+ {
+ cb= cb_list;
+ cb_list= NULL;
+ }
pthread_mutex_unlock(&LOCK_manager);
- if (abort_loop)
+ if (abort_manager)
break;
if (error == ETIMEDOUT || error == ETIME)
@@ -79,18 +111,49 @@ pthread_handler_t handle_manager(void *arg __attribute__((unused)))
reset_flush_time = TRUE;
}
-#ifdef HAVE_BERKELEY_DB
- if (status & MANAGER_BERKELEY_LOG_CLEANUP)
+ while (cb)
{
- berkeley_cleanup_log_files();
- status &= ~MANAGER_BERKELEY_LOG_CLEANUP;
+ struct handler_cb *next= cb->next;
+ cb->action();
+ my_free((uchar*)cb, MYF(0));
+ cb= next;
}
-#endif
-
- if (status)
- DBUG_PRINT("error", ("manager did not handle something: %lx", status));
}
manager_thread_in_use = 0;
+ DBUG_LEAVE; // Can't use DBUG_RETURN after my_thread_end
my_thread_end();
- DBUG_RETURN(NULL);
+ return (NULL);
}
+
+
+/* Start handle manager thread */
+void start_handle_manager()
+{
+ DBUG_ENTER("start_handle_manager");
+ abort_manager = false;
+ 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");
+ }
+ DBUG_VOID_RETURN;
+}
+
+
+/* Initiate shutdown of handle manager thread */
+void stop_handle_manager()
+{
+ DBUG_ENTER("stop_handle_manager");
+ abort_manager = true;
+ pthread_mutex_lock(&LOCK_manager);
+ if (manager_thread_in_use)
+ {
+ DBUG_PRINT("quit", ("initiate shutdown of handle manager thread: 0x%lx",
+ (ulong)manager_thread));
+ pthread_cond_signal(&COND_manager);
+ }
+ pthread_mutex_unlock(&LOCK_manager);
+ DBUG_VOID_RETURN;
+}
+
diff --git a/sql/sql_map.cc b/sql/sql_map.cc
index 282716c6151..55f9b08d3fe 100644
--- a/sql/sql_map.cc
+++ b/sql/sql_map.cc
@@ -24,11 +24,7 @@
#include <sys/mman.h>
#endif
-#ifndef MAP_NORESERVE
-#define MAP_NORESERVE 0 // For IRIX
-#endif
-
-mapped_files::mapped_files(const my_string filename,byte *magic,uint magic_length)
+mapped_files::mapped_files(const char * filename,uchar *magic,uint magic_length)
{
#ifdef HAVE_MMAP
name=my_strdup(filename,MYF(0));
@@ -41,18 +37,18 @@ mapped_files::mapped_files(const my_string filename,byte *magic,uint magic_lengt
struct stat stat_buf;
if (!fstat(file,&stat_buf))
{
- if (!(map=(byte*) my_mmap(0,(size_t)(size=(ulong) stat_buf.st_size),PROT_READ,
+ 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), (my_string) name, error);
+ 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(map,(size_t)size));
+ VOID(my_munmap((char*) map,(size_t)size));
map=0;
}
if (!map)
@@ -70,7 +66,7 @@ mapped_files::~mapped_files()
#ifdef HAVE_MMAP
if (file >= 0)
{
- VOID(my_munmap(map,(size_t)size));
+ VOID(my_munmap((char*) map,(size_t)size));
VOID(my_close(file,MYF(0)));
file= -1; map=0;
}
@@ -86,7 +82,7 @@ static I_List<mapped_files> maps_in_use;
** else alloc new object
*/
-mapped_files *map_file(const my_string name,byte *magic,uint magic_length)
+mapped_files *map_file(const char * name,uchar *magic,uint magic_length)
{
#ifdef HAVE_MMAP
VOID(pthread_mutex_lock(&LOCK_mapped_file));
diff --git a/sql/sql_map.h b/sql/sql_map.h
index d8eb64995aa..a1efba0da6f 100644
--- a/sql/sql_map.h
+++ b/sql/sql_map.h
@@ -21,11 +21,11 @@
#endif
class mapped_files;
-mapped_files *map_file(const my_string name,byte *magic,uint magic_length);
+mapped_files *map_file(const char * name,uchar *magic,uint magic_length);
void unmap_file(mapped_files *map);
class mapped_files :public ilink {
- byte *map;
+ uchar *map;
ha_rows size;
char *name; // name of mapped file
File file; // >= 0 if open
@@ -33,11 +33,11 @@ class mapped_files :public ilink {
uint use_count;
public:
- mapped_files(const my_string name,byte *magic,uint magic_length);
+ mapped_files(const char * name,uchar *magic,uint magic_length);
~mapped_files();
friend class mapped_file;
- friend mapped_files *map_file(const my_string name,byte *magic,
+ friend mapped_files *map_file(const char * name,uchar *magic,
uint magic_length);
friend void unmap_file(mapped_files *map);
};
@@ -47,7 +47,7 @@ class mapped_file
{
mapped_files *file;
public:
- mapped_file(const my_string name,byte *magic,uint magic_length)
+ mapped_file(const char * name,uchar *magic,uint magic_length)
{
file=map_file(name,magic,magic_length); /* old or new map */
}
@@ -55,7 +55,7 @@ public:
{
unmap_file(file); /* free map */
}
- byte *map()
+ uchar *map()
{
return file->map;
}
diff --git a/sql/sql_olap.cc b/sql/sql_olap.cc
index 2749b0d1ec6..dccfcbaf8ac 100644
--- a/sql/sql_olap.cc
+++ b/sql/sql_olap.cc
@@ -54,8 +54,8 @@ static int make_new_olap_select(LEX *lex, SELECT_LEX *select_lex, List<Item> new
new_select->linkage=OLAP_TYPE;
new_select->olap=NON_EXISTING_ONE;
new_select->group_list.elements=0;
- new_select->group_list.first=(byte *)0;
- new_select->group_list.next=(byte **)&new_select->group_list.first;
+ 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);
@@ -154,9 +154,11 @@ int handle_olaps(LEX *lex, SELECT_LEX *select_lex)
if (setup_tables(lex->thd, &select_lex->context, &select_lex->top_join_list,
(TABLE_LIST *)select_lex->table_list.first
- &select_lex->where, &select_lex->leaf_tables, FALSE) ||
- setup_fields(lex->thd, 0, select_lex->item_list, 1, &all_fields,1) ||
- setup_fields(lex->thd, 0, item_list_copy, 1, &all_fields, 1))
+ &select_lex->leaf_tables, 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)
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index 8ee88cefd99..592dbe9f43b 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -1,4 +1,4 @@
-/* Copyright (C) 2000-2003 MySQL AB
+/* Copyright 2000-2008 MySQL AB, 2008 Sun Microsystems, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -16,41 +16,22 @@
#define MYSQL_LEX 1
#include "mysql_priv.h"
#include "sql_repl.h"
+#include "rpl_filter.h"
#include "repl_failsafe.h"
#include <m_ctype.h>
#include <myisam.h>
#include <my_dir.h>
-#ifdef HAVE_INNOBASE_DB
-#include "ha_innodb.h"
-#endif
-
-#ifdef HAVE_NDBCLUSTER_DB
-#include "ha_ndbcluster.h"
-#endif
-
#include "sp_head.h"
#include "sp.h"
#include "sp_cache.h"
+#include "events.h"
#include "sql_trigger.h"
-#ifdef HAVE_OPENSSL
-/*
- Without SSL the handshake consists of one packet. This packet
- has both client capabilites 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.
- Maybe it is better to accept flags other than CLIENT_SSL from the
- second packet?
+/**
+ @defgroup Runtime_Environment Runtime Environment
+ @{
*/
-#define SSL_HANDSHAKE_SIZE 2
-#define NORMAL_HANDSHAKE_SIZE 6
-#define MIN_HANDSHAKE_SIZE 2
-#else
-#define MIN_HANDSHAKE_SIZE 6
-#endif /* HAVE_OPENSSL */
/* Used in error handling only */
#define SP_TYPE_STRING(LP) \
@@ -62,31 +43,43 @@
(LP)->sql_command == SQLCOM_DROP_FUNCTION ? \
"FUNCTION" : "PROCEDURE")
-#ifdef SOLARIS
-extern "C" int gethostname(char *name, int namelen);
-#endif
-
-#ifndef NO_EMBEDDED_ACCESS_CHECKS
-static void time_out_user_resource_limits(THD *thd, USER_CONN *uc);
-static int check_for_max_user_connections(THD *thd, USER_CONN *uc);
-static void decrease_user_connections(USER_CONN *uc);
-#endif /* NO_EMBEDDED_ACCESS_CHECKS */
-static bool check_db_used(THD *thd,TABLE_LIST *tables);
-static void remove_escape(char *name);
-static bool append_file_to_dir(THD *thd, const char **filename_ptr,
- const char *table_name);
+static bool execute_sqlcom_select(THD *thd, TABLE_LIST *all_tables);
static bool check_show_create_table_access(THD *thd, TABLE_LIST *table);
const char *any_db="*any*"; // Special symbol for check_access
-const char *command_name[]={
- "Sleep", "Quit", "Init DB", "Query", "Field List", "Create DB",
- "Drop DB", "Refresh", "Shutdown", "Statistics", "Processlist",
- "Connect","Kill","Debug","Ping","Time","Delayed insert","Change user",
- "Binlog Dump","Table Dump", "Connect Out", "Register Slave",
- "Prepare", "Execute", "Long Data", "Close stmt",
- "Reset stmt", "Set option", "Fetch",
- "Error" // Last command number
+const LEX_STRING command_name[]={
+ { C_STRING_WITH_LEN("Sleep") },
+ { C_STRING_WITH_LEN("Quit") },
+ { C_STRING_WITH_LEN("Init DB") },
+ { C_STRING_WITH_LEN("Query") },
+ { C_STRING_WITH_LEN("Field List") },
+ { C_STRING_WITH_LEN("Create DB") },
+ { C_STRING_WITH_LEN("Drop DB") },
+ { C_STRING_WITH_LEN("Refresh") },
+ { C_STRING_WITH_LEN("Shutdown") },
+ { C_STRING_WITH_LEN("Statistics") },
+ { C_STRING_WITH_LEN("Processlist") },
+ { C_STRING_WITH_LEN("Connect") },
+ { C_STRING_WITH_LEN("Kill") },
+ { C_STRING_WITH_LEN("Debug") },
+ { C_STRING_WITH_LEN("Ping") },
+ { C_STRING_WITH_LEN("Time") },
+ { C_STRING_WITH_LEN("Delayed insert") },
+ { C_STRING_WITH_LEN("Change user") },
+ { C_STRING_WITH_LEN("Binlog Dump") },
+ { C_STRING_WITH_LEN("Table Dump") },
+ { C_STRING_WITH_LEN("Connect Out") },
+ { C_STRING_WITH_LEN("Register Slave") },
+ { C_STRING_WITH_LEN("Prepare") },
+ { C_STRING_WITH_LEN("Execute") },
+ { C_STRING_WITH_LEN("Long Data") },
+ { C_STRING_WITH_LEN("Close stmt") },
+ { C_STRING_WITH_LEN("Reset stmt") },
+ { C_STRING_WITH_LEN("Set option") },
+ { C_STRING_WITH_LEN("Fetch") },
+ { C_STRING_WITH_LEN("Daemon") },
+ { C_STRING_WITH_LEN("Error") } // Last command number
};
const char *xa_state_names[]={
@@ -141,14 +134,6 @@ static bool xa_trans_rollback(THD *thd)
return status;
}
-#ifndef EMBEDDED_LIBRARY
-static bool do_command(THD *thd);
-#endif // EMBEDDED_LIBRARY
-
-#ifdef __WIN__
-extern void win_install_sigabrt_handler(void);
-#endif
-
static void unlock_locked_tables(THD *thd)
{
if (thd->locked_tables)
@@ -160,7 +145,7 @@ static void unlock_locked_tables(THD *thd)
}
-static bool end_active_trans(THD *thd)
+bool end_active_trans(THD *thd)
{
int error=0;
DBUG_ENTER("end_active_trans");
@@ -178,20 +163,21 @@ static bool end_active_trans(THD *thd)
if (thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN |
OPTION_TABLE_LOCK))
{
- DBUG_PRINT("info",("options: 0x%lx", (ulong) thd->options));
+ 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;
- thd->options&= ~(ulong) OPTION_BEGIN;
- thd->transaction.all.modified_non_trans_table= FALSE;
}
+ thd->options&= ~(OPTION_BEGIN | OPTION_KEEP_LOG);
+ thd->transaction.all.modified_non_trans_table= FALSE;
DBUG_RETURN(error);
}
-static bool begin_trans(THD *thd)
+
+bool begin_trans(THD *thd)
{
int error=0;
if (unlikely(thd->in_sub_stmt))
@@ -210,8 +196,7 @@ static bool begin_trans(THD *thd)
else
{
LEX *lex= thd->lex;
- thd->transaction.all.modified_non_trans_table= FALSE;
- thd->options|= (ulong) OPTION_BEGIN;
+ thd->options|= OPTION_BEGIN;
thd->server_status|= SERVER_STATUS_IN_TRANS;
if (lex->start_transaction_opt & MYSQL_START_TRANS_OPT_WITH_CONS_SNAPSHOT)
error= ha_start_consistent_snapshot(thd);
@@ -220,12 +205,13 @@ static bool begin_trans(THD *thd)
}
#ifdef HAVE_REPLICATION
-/*
- Returns true if all tables should be ignored
+/**
+ Returns true if all tables should be ignored.
*/
inline bool all_tables_not_ok(THD *thd, TABLE_LIST *tables)
{
- return table_rules_on && tables && !tables_ok(thd,tables);
+ return rpl_filter->is_on() && tables && !thd->spcont &&
+ !rpl_filter->tables_ok(thd->db, tables);
}
#endif
@@ -242,872 +228,168 @@ static bool some_non_temp_table_to_be_updated(THD *thd, TABLE_LIST *tables)
return 0;
}
-#ifndef NO_EMBEDDED_ACCESS_CHECKS
-static HASH hash_user_connections;
-
-static int get_or_create_user_conn(THD *thd, const char *user,
- const char *host,
- USER_RESOURCES *mqh)
-{
- int return_val= 0;
- size_t temp_len, user_len;
- char temp_user[USER_HOST_BUFF_SIZE];
- struct user_conn *uc;
-
- DBUG_ASSERT(user != 0);
- DBUG_ASSERT(host != 0);
-
- 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,
- (byte*) temp_user, (uint) temp_len)))
- {
- /* First connection for user; Create a user connection object */
- if (!(uc= ((struct user_conn*)
- my_malloc(sizeof(struct user_conn) + temp_len+1,
- MYF(MY_WME)))))
- {
- net_send_error(thd, 0, NullS); // Out of memory
- return_val= 1;
- goto end;
- }
- uc->user=(char*) (uc+1);
- memcpy(uc->user,temp_user,temp_len+1);
- uc->host= uc->user + user_len + 1;
- uc->len= (uint) temp_len;
- uc->connections= uc->questions= uc->updates= uc->conn_per_hour= 0;
- uc->user_resources= *mqh;
- uc->intime= thd->thr_create_time;
- if (my_hash_insert(&hash_user_connections, (byte*) uc))
- {
- my_free((char*) uc,0);
- net_send_error(thd, 0, NullS); // Out of memory
- return_val= 1;
- goto end;
- }
- }
- thd->user_connect=uc;
- uc->connections++;
-end:
- (void) pthread_mutex_unlock(&LOCK_user_conn);
- return return_val;
-
-}
-#endif /* !NO_EMBEDDED_ACCESS_CHECKS */
-
-
-/*
- Check if user exist and password supplied is correct.
-
- SYNOPSIS
- check_user()
- thd thread handle, thd->security_ctx->{host,user,ip} are used
- command originator of the check: now check_user is called
- during connect and change user procedures; used for
- logging.
- passwd scrambled password received from client
- passwd_len length of scrambled password
- db database name to connect to, may be NULL
- check_count dont know exactly
-
- Note, that host, user and passwd may point to communication buffer.
- Current implementation does not depend on that, but future changes
- should be done with this in mind; 'thd' is INOUT, all other params
- are 'IN'.
-
- RETURN VALUE
- 0 OK; thd->security_ctx->user/master_access/priv_user/db_access and
- thd->db are updated; OK is sent to client;
- -1 access denied or handshake error; error is sent to client;
- >0 error, not sent to client
-*/
-
-int check_user(THD *thd, enum enum_server_command command,
- const char *passwd, uint passwd_len, const char *db,
- bool check_count)
-{
- DBUG_ENTER("check_user");
- LEX_STRING db_str= { (char *) db, db ? (uint) strlen(db) : 0 };
-
-#ifdef NO_EMBEDDED_ACCESS_CHECKS
- thd->main_security_ctx.master_access= GLOBAL_ACLS; // Full rights
- /* Change database if necessary */
- if (db && db[0])
- {
- /*
- thd->db is saved in caller and needs to be freed by caller if this
- function returns 0
- */
- thd->reset_db(NULL, 0);
- if (mysql_change_db(thd, &db_str, FALSE))
- {
- /* Send the error to the client */
- net_send_error(thd);
- DBUG_RETURN(-1);
- }
- }
- send_ok(thd);
- DBUG_RETURN(0);
-#else
-
- my_bool opt_secure_auth_local;
- pthread_mutex_lock(&LOCK_global_system_variables);
- opt_secure_auth_local= opt_secure_auth;
- pthread_mutex_unlock(&LOCK_global_system_variables);
-
- /*
- If the server is running in secure auth mode, short scrambles are
- forbidden.
- */
- if (opt_secure_auth_local && passwd_len == SCRAMBLE_LENGTH_323)
- {
- net_printf_error(thd, ER_NOT_SUPPORTED_AUTH_MODE);
- mysql_log.write(thd, COM_CONNECT, ER(ER_NOT_SUPPORTED_AUTH_MODE));
- DBUG_RETURN(-1);
- }
- if (passwd_len != 0 &&
- passwd_len != SCRAMBLE_LENGTH &&
- passwd_len != SCRAMBLE_LENGTH_323)
- DBUG_RETURN(ER_HANDSHAKE_ERROR);
-
- /*
- 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 pointer
- if connect failed. Also in case of 'CHANGE USER' failure, current
- database will be switched to 'no database selected'.
- */
- thd->reset_db(NULL, 0);
-
- USER_RESOURCES ur;
- int res= acl_getroot(thd, &ur, passwd, passwd_len);
-#ifndef EMBEDDED_LIBRARY
- if (res == -1)
- {
- /*
- This happens when client (new) sends password scrambled with
- scramble(), but database holds old value (scrambled with
- scramble_323()). Here we please client to send scrambled_password
- in old format.
- */
- NET *net= &thd->net;
- if (opt_secure_auth_local)
- {
- net_printf_error(thd, ER_SERVER_IS_IN_SECURE_AUTH_MODE,
- thd->main_security_ctx.user,
- thd->main_security_ctx.host_or_ip);
- mysql_log.write(thd, COM_CONNECT, ER(ER_SERVER_IS_IN_SECURE_AUTH_MODE),
- thd->main_security_ctx.user,
- thd->main_security_ctx.host_or_ip);
- DBUG_RETURN(-1);
- }
- /* We have to read very specific packet size */
- if (send_old_password_request(thd) ||
- my_net_read(net) != SCRAMBLE_LENGTH_323 + 1)
- {
- inc_host_errors(&thd->remote.sin_addr);
- DBUG_RETURN(ER_HANDSHAKE_ERROR);
- }
- /* Final attempt to check the user based on reply */
- /* So as passwd is short, errcode is always >= 0 */
- res= acl_getroot(thd, &ur, (char *) net->read_pos, SCRAMBLE_LENGTH_323);
- }
-#endif /*EMBEDDED_LIBRARY*/
- /* here res is always >= 0 */
- if (res == 0)
- {
- if (!(thd->main_security_ctx.master_access &
- NO_ACCESS)) // authentication is OK
- {
- DBUG_PRINT("info",
- ("Capabilities: %lu packet_length: %ld Host: '%s' "
- "Login user: '%s' Priv_user: '%s' Using password: %s "
- "Access: %lu db: '%s'",
- thd->client_capabilities,
- thd->max_client_packet_length,
- thd->main_security_ctx.host_or_ip,
- thd->main_security_ctx.user,
- thd->main_security_ctx.priv_user,
- passwd_len ? "yes": "no",
- thd->main_security_ctx.master_access,
- (thd->db ? thd->db : "*none*")));
-
- if (check_count)
- {
- VOID(pthread_mutex_lock(&LOCK_thread_count));
- bool count_ok= thread_count <= max_connections + delayed_insert_threads
- || (thd->main_security_ctx.master_access & SUPER_ACL);
- VOID(pthread_mutex_unlock(&LOCK_thread_count));
- if (!count_ok)
- { // too many connections
- net_send_error(thd, ER_CON_COUNT_ERROR);
- DBUG_RETURN(-1);
- }
- }
-
- /* Why logging is performed before all checks've passed? */
- mysql_log.write(thd, command,
- (thd->main_security_ctx.priv_user ==
- thd->main_security_ctx.user ?
- (char*) "%s@%s on %s" :
- (char*) "%s@%s as anonymous on %s"),
- thd->main_security_ctx.user,
- thd->main_security_ctx.host_or_ip,
- db ? db : (char*) "");
-
- /*
- This is the default access rights for the current database. It's
- set to 0 here because we don't have an active database yet (and we
- may not have an active database to set.
- */
- thd->main_security_ctx.db_access=0;
-
- /* Don't allow user to connect if he has done too many queries */
- if ((ur.questions || ur.updates || ur.conn_per_hour || ur.user_conn ||
- max_user_connections) &&
- get_or_create_user_conn(thd,
- (opt_old_style_user_limits ? thd->main_security_ctx.user :
- thd->main_security_ctx.priv_user),
- (opt_old_style_user_limits ? thd->main_security_ctx.host_or_ip :
- thd->main_security_ctx.priv_host),
- &ur))
- DBUG_RETURN(-1);
- if (thd->user_connect &&
- (thd->user_connect->user_resources.conn_per_hour ||
- thd->user_connect->user_resources.user_conn ||
- max_user_connections) &&
- check_for_max_user_connections(thd, thd->user_connect))
- DBUG_RETURN(-1);
-
- /* Change database if necessary */
- if (db && db[0])
- {
- if (mysql_change_db(thd, &db_str, FALSE))
- {
- /* Send error to the client */
- net_send_error(thd);
- if (thd->user_connect)
- decrease_user_connections(thd->user_connect);
- DBUG_RETURN(-1);
- }
- }
- send_ok(thd);
- thd->password= test(passwd_len); // remember for error messages
- /* Ready to handle queries */
- DBUG_RETURN(0);
- }
- }
- else if (res == 2) // client gave short hash, server has long hash
- {
- net_printf_error(thd, ER_NOT_SUPPORTED_AUTH_MODE);
- mysql_log.write(thd,COM_CONNECT,ER(ER_NOT_SUPPORTED_AUTH_MODE));
- DBUG_RETURN(-1);
- }
- net_printf_error(thd, ER_ACCESS_DENIED_ERROR,
- thd->main_security_ctx.user,
- thd->main_security_ctx.host_or_ip,
- passwd_len ? ER(ER_YES) : ER(ER_NO));
- mysql_log.write(thd, COM_CONNECT, ER(ER_ACCESS_DENIED_ERROR),
- thd->main_security_ctx.user,
- thd->main_security_ctx.host_or_ip,
- passwd_len ? ER(ER_YES) : ER(ER_NO));
- DBUG_RETURN(-1);
-#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.
-*/
-
-extern "C" byte *get_key_conn(user_conn *buff, uint *length,
- my_bool not_used __attribute__((unused)))
-{
- *length=buff->len;
- return (byte*) buff->user;
-}
-
-extern "C" void free_user(struct user_conn *uc)
-{
- my_free((char*) uc,MYF(0));
-}
-
-void init_max_user_conn(void)
-{
-#ifndef NO_EMBEDDED_ACCESS_CHECKS
- (void) hash_init(&hash_user_connections,system_charset_info,max_connections,
- 0,0,
- (hash_get_key) get_key_conn, (hash_free_key) free_user,
- 0);
-#endif
-}
-
-
-/*
- check if user has already too many connections
-
- SYNOPSIS
- check_for_max_user_connections()
- thd Thread handle
- uc User connect object
-
- NOTES
- If check fails, we decrease user connection count, which means one
- shouldn't call decrease_user_connections() after this function.
-
- RETURN
- 0 ok
- 1 error
-*/
-
-#ifndef NO_EMBEDDED_ACCESS_CHECKS
-
-static int check_for_max_user_connections(THD *thd, USER_CONN *uc)
-{
- int error=0;
- DBUG_ENTER("check_for_max_user_connections");
-
- (void) pthread_mutex_lock(&LOCK_user_conn);
- if (max_user_connections && !uc->user_resources.user_conn &&
- max_user_connections < (uint) uc->connections)
- {
- net_printf_error(thd, ER_TOO_MANY_USER_CONNECTIONS, uc->user);
- error=1;
- goto end;
- }
- time_out_user_resource_limits(thd, uc);
- if (uc->user_resources.user_conn &&
- uc->user_resources.user_conn < uc->connections)
- {
- net_printf_error(thd, ER_USER_LIMIT_REACHED, uc->user,
- "max_user_connections",
- (long) uc->user_resources.user_conn);
- error= 1;
- goto end;
- }
- if (uc->user_resources.conn_per_hour &&
- uc->user_resources.conn_per_hour <= uc->conn_per_hour)
- {
- net_printf_error(thd, ER_USER_LIMIT_REACHED, uc->user,
- "max_connections_per_hour",
- (long) uc->user_resources.conn_per_hour);
- error=1;
- goto end;
- }
- uc->conn_per_hour++;
-
- end:
- if (error)
- uc->connections--; // no need for decrease_user_connections() here
- (void) pthread_mutex_unlock(&LOCK_user_conn);
- DBUG_RETURN(error);
-}
-
-/*
- Decrease user connection count
-
- SYNOPSIS
- decrease_user_connections()
- uc User connection object
-
- NOTES
- If there is a n user connection object for a connection
- (which only happens if 'max_user_connections' is defined or
- if someone has created a resource grant for a user), then
- the connection count is always incremented on connect.
-
- The user connect object is not freed if some users has
- 'max connections per hour' defined as we need to be able to hold
- count over the lifetime of the connection.
-*/
-
-static void decrease_user_connections(USER_CONN *uc)
-{
- DBUG_ENTER("decrease_user_connections");
- (void) pthread_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,(byte*) uc);
- }
- (void) pthread_mutex_unlock(&LOCK_user_conn);
- DBUG_VOID_RETURN;
-}
-
-#endif /* NO_EMBEDDED_ACCESS_CHECKS */
-
-
-void free_max_user_conn(void)
-{
-#ifndef NO_EMBEDDED_ACCESS_CHECKS
- hash_free(&hash_user_connections);
-#endif /* NO_EMBEDDED_ACCESS_CHECKS */
-}
-
+/**
+ Mark all commands that somehow changes a table.
-/*
- Mark all commands that somehow changes a table
- This is used to check number of updates / hour
+ This is used to check number of updates / hour.
sql_command is actually set to SQLCOM_END sometimes
so we need the +1 to include it in the array.
- numbers are:
- 0 - read-only query
- != 0 - query that may change a table
+ See COMMAND_FLAG_xxx for different type of commands
2 - query that returns meaningful ROW_COUNT() -
a number of modified rows
*/
-char uc_update_queries[SQLCOM_END+1];
+uint sql_command_flags[SQLCOM_END+1];
void init_update_queries(void)
{
- bzero((gptr) &uc_update_queries, sizeof(uc_update_queries));
-
- uc_update_queries[SQLCOM_CREATE_TABLE]=1;
- uc_update_queries[SQLCOM_CREATE_INDEX]=1;
- uc_update_queries[SQLCOM_ALTER_TABLE]=1;
- uc_update_queries[SQLCOM_UPDATE]=2;
- uc_update_queries[SQLCOM_UPDATE_MULTI]=2;
- uc_update_queries[SQLCOM_INSERT]=2;
- uc_update_queries[SQLCOM_INSERT_SELECT]=2;
- uc_update_queries[SQLCOM_DELETE]=2;
- uc_update_queries[SQLCOM_DELETE_MULTI]=2;
- uc_update_queries[SQLCOM_TRUNCATE]=1;
- uc_update_queries[SQLCOM_DROP_TABLE]=1;
- uc_update_queries[SQLCOM_LOAD]=1;
- uc_update_queries[SQLCOM_CREATE_DB]=1;
- uc_update_queries[SQLCOM_DROP_DB]=1;
- uc_update_queries[SQLCOM_REPLACE]=2;
- uc_update_queries[SQLCOM_REPLACE_SELECT]=2;
- uc_update_queries[SQLCOM_RENAME_TABLE]=1;
- uc_update_queries[SQLCOM_BACKUP_TABLE]=1;
- uc_update_queries[SQLCOM_RESTORE_TABLE]=1;
- uc_update_queries[SQLCOM_DROP_INDEX]=1;
- uc_update_queries[SQLCOM_CREATE_VIEW]=1;
- uc_update_queries[SQLCOM_DROP_VIEW]=1;
-}
-
-bool is_update_query(enum enum_sql_command command)
-{
- DBUG_ASSERT(command >= 0 && command <= SQLCOM_END);
- return uc_update_queries[command] != 0;
-}
-
-/*
- Reset per-hour user resource limits when it has been more than
- an hour since they were last checked
-
- SYNOPSIS:
- time_out_user_resource_limits()
- thd Thread handler
- uc User connection details
-
- NOTE:
- This assumes that the LOCK_user_conn mutex has been acquired, so it is
- safe to test and modify members of the USER_CONN structure.
-*/
-
-#ifndef NO_EMBEDDED_ACCESS_CHECKS
-
-static void time_out_user_resource_limits(THD *thd, USER_CONN *uc)
-{
- time_t check_time = thd->start_time ? thd->start_time : time(NULL);
- DBUG_ENTER("time_out_user_resource_limits");
-
- /* If more than a hour since last check, reset resource checking */
- if (check_time - uc->intime >= 3600)
- {
- uc->questions=1;
- uc->updates=0;
- uc->conn_per_hour=0;
- uc->intime=check_time;
- }
-
- DBUG_VOID_RETURN;
-}
-
-/*
- Check if maximum queries per hour limit has been reached
- returns 0 if OK.
-*/
-
-static bool check_mqh(THD *thd, uint check_command)
-{
- bool error= 0;
- USER_CONN *uc=thd->user_connect;
- DBUG_ENTER("check_mqh");
- DBUG_ASSERT(uc != 0);
+ 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;
+ sql_command_flags[SQLCOM_ALTER_TABLE]= CF_CHANGES_DATA | CF_WRITE_LOGS_COMMAND;
+ 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;
+ 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;
+ 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;
+
+ 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;
+ sql_command_flags[SQLCOM_SHOW_DATABASES]= CF_STATUS_COMMAND | CF_REEXECUTION_FRAGILE;
+ sql_command_flags[SQLCOM_SHOW_TRIGGERS]= CF_STATUS_COMMAND | CF_REEXECUTION_FRAGILE;
+ sql_command_flags[SQLCOM_SHOW_EVENTS]= CF_STATUS_COMMAND | CF_REEXECUTION_FRAGILE;
+ sql_command_flags[SQLCOM_SHOW_OPEN_TABLES]= CF_STATUS_COMMAND | CF_REEXECUTION_FRAGILE;
+ sql_command_flags[SQLCOM_SHOW_PLUGINS]= CF_STATUS_COMMAND;
+ sql_command_flags[SQLCOM_SHOW_FIELDS]= CF_STATUS_COMMAND | CF_REEXECUTION_FRAGILE;
+ sql_command_flags[SQLCOM_SHOW_KEYS]= CF_STATUS_COMMAND | CF_REEXECUTION_FRAGILE;
+ 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_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_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_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_CREATE]= 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_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_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);
- (void) pthread_mutex_lock(&LOCK_user_conn);
-
- time_out_user_resource_limits(thd, uc);
-
- /* Check that we have not done too many questions / hour */
- if (uc->user_resources.questions &&
- uc->questions++ >= uc->user_resources.questions)
- {
- net_printf_error(thd, ER_USER_LIMIT_REACHED, uc->user, "max_questions",
- (long) uc->user_resources.questions);
- error=1;
- goto end;
- }
- if (check_command < (uint) SQLCOM_END)
- {
- /* Check that we have not done too many updates / hour */
- if (uc->user_resources.updates && uc_update_queries[check_command] &&
- uc->updates++ >= uc->user_resources.updates)
- {
- net_printf_error(thd, ER_USER_LIMIT_REACHED, uc->user, "max_updates",
- (long) uc->user_resources.updates);
- error=1;
- goto end;
- }
- }
-end:
- (void) pthread_mutex_unlock(&LOCK_user_conn);
- DBUG_RETURN(error);
-}
-
-#endif /* NO_EMBEDDED_ACCESS_CHECKS */
-
-
-static void reset_mqh(LEX_USER *lu, bool get_them= 0)
-{
-#ifndef NO_EMBEDDED_ACCESS_CHECKS
- (void) pthread_mutex_lock(&LOCK_user_conn);
- if (lu) // for GRANT
- {
- USER_CONN *uc;
- uint temp_len=lu->user.length+lu->host.length+2;
- char temp_user[USER_HOST_BUFF_SIZE];
-
- memcpy(temp_user,lu->user.str,lu->user.length);
- 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,
- (byte*) temp_user, temp_len)))
- {
- uc->questions=0;
- get_mqh(temp_user,&temp_user[lu->user.length+1],uc);
- uc->updates=0;
- uc->conn_per_hour=0;
- }
- }
- else
- {
- /* 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);
- if (get_them)
- get_mqh(uc->user,uc->host,uc);
- uc->questions=0;
- uc->updates=0;
- uc->conn_per_hour=0;
- }
- }
- (void) pthread_mutex_unlock(&LOCK_user_conn);
-#endif /* NO_EMBEDDED_ACCESS_CHECKS */
-}
+ /*
+ The following is used to preserver CF_ROW_COUNT during the
+ a CALL or EXECUTE statement, so the value generated by the
+ 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;
-void thd_init_client_charset(THD *thd, uint cs_number)
-{
/*
- Use server character set and collation if
- - opt_character_set_client_handshake is not set
- - client has not specified a character set
- - client character set is the same as the servers
- - client character set doesn't exists in server
+ The following admin table operations are allowed
+ on log tables.
*/
- if (!opt_character_set_client_handshake ||
- !(thd->variables.character_set_client= get_charset(cs_number, MYF(0))) ||
- !my_strcasecmp(&my_charset_latin1,
- global_system_variables.character_set_client->name,
- thd->variables.character_set_client->name))
- {
- thd->variables.character_set_client=
- global_system_variables.character_set_client;
- thd->variables.collation_connection=
- global_system_variables.collation_connection;
- thd->variables.character_set_results=
- global_system_variables.character_set_results;
- }
- else
- {
- thd->variables.character_set_results=
- thd->variables.collation_connection=
- thd->variables.character_set_client;
- }
+ sql_command_flags[SQLCOM_REPAIR]= CF_WRITE_LOGS_COMMAND;
+ sql_command_flags[SQLCOM_OPTIMIZE]= CF_WRITE_LOGS_COMMAND;
+ sql_command_flags[SQLCOM_ANALYZE]= CF_WRITE_LOGS_COMMAND;
}
-/*
- Perform handshake, authorize client and update thd ACL variables.
- SYNOPSIS
- check_connection()
- thd thread handle
+bool is_update_query(enum enum_sql_command command)
+{
+ DBUG_ASSERT(command >= 0 && command <= SQLCOM_END);
+ return (sql_command_flags[command] & CF_CHANGES_DATA) != 0;
+}
- RETURN
- 0 success, OK is sent to user, thd is updated.
- -1 error, which is sent to user
- > 0 error code (not sent to user)
+/**
+ Check if a sql command is allowed to write to log tables.
+ @param command The SQL command
+ @return true if writing is allowed
*/
-
-#ifndef EMBEDDED_LIBRARY
-static int check_connection(THD *thd)
+bool is_log_table_write_query(enum enum_sql_command command)
{
- uint connect_errors= 0;
- NET *net= &thd->net;
- ulong pkt_len= 0;
- char *end;
-
- 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];
-
- if (vio_peer_addr(net->vio, ip, &thd->peer_port))
- return (ER_BAD_HOST_ERROR);
- if (!(thd->main_security_ctx.ip= my_strdup(ip,MYF(0))))
- return (ER_OUT_OF_RESOURCES);
- 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);
- /* Cut very long hostnames to avoid possible overflows */
- if (thd->main_security_ctx.host)
- {
- if (thd->main_security_ctx.host != my_localhost)
- thd->main_security_ctx.host[min(strlen(thd->main_security_ctx.host),
- HOSTNAME_LENGTH)]= 0;
- thd->main_security_ctx.host_or_ip= thd->main_security_ctx.host;
- }
- if (connect_errors > max_connect_errors)
- return(ER_HOST_IS_BLOCKED);
- }
- DBUG_PRINT("info",("Host: %s ip: %s",
- (thd->main_security_ctx.host ?
- thd->main_security_ctx.host : "unknown host"),
- (thd->main_security_ctx.ip ?
- thd->main_security_ctx.ip : "unknown ip")));
- if (acl_check_host(thd->main_security_ctx.host, thd->main_security_ctx.ip))
- return(ER_HOST_NOT_PRIVILEGED);
- }
- else /* Hostname given means that the connection was on a socket */
- {
- DBUG_PRINT("info",("Host: %s", thd->main_security_ctx.host));
- 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));
- }
- vio_keepalive(net->vio, TRUE);
- {
- /* buff[] needs to big enough to hold the server_version variable */
- char buff[SERVER_VERSION_LENGTH + SCRAMBLE_LENGTH + 64];
- ulong client_flags = (CLIENT_LONG_FLAG | CLIENT_CONNECT_WITH_DB |
- CLIENT_PROTOCOL_41 | CLIENT_SECURE_CONNECTION);
-
- if (opt_using_transactions)
- client_flags|=CLIENT_TRANSACTIONS;
-#ifdef HAVE_COMPRESS
- client_flags |= CLIENT_COMPRESS;
-#endif /* HAVE_COMPRESS */
-#ifdef HAVE_OPENSSL
- if (ssl_acceptor_fd)
- client_flags |= CLIENT_SSL; /* Wow, SSL is available! */
-#endif /* HAVE_OPENSSL */
-
- end= strnmov(buff, server_version, SERVER_VERSION_LENGTH) + 1;
- int4store((uchar*) end, thd->thread_id);
- end+= 4;
- /*
- So as check_connection is the only entry point to authorization
- procedure, scramble is set here. This gives us new scramble for
- each handshake.
- */
- create_random_string(thd->scramble, SCRAMBLE_LENGTH, &thd->rand);
- /*
- Old clients does not understand long scrambles, but can ignore packet
- tail: that's why first part of the scramble is placed here, and second
- part at the end of packet.
- */
- end= strmake(end, thd->scramble, SCRAMBLE_LENGTH_323) + 1;
-
- int2store(end, client_flags);
- /* write server characteristics: up to 16 bytes allowed */
- end[2]=(char) default_charset_info->number;
- int2store(end+3, thd->server_status);
- bzero(end+5, 13);
- end+= 18;
- /* write scramble tail */
- end= strmake(end, thd->scramble + SCRAMBLE_LENGTH_323,
- SCRAMBLE_LENGTH - SCRAMBLE_LENGTH_323) + 1;
-
- /* At this point we write connection message and read reply */
- if (net_write_command(net, (uchar) protocol_version, "", 0, buff,
- (uint) (end-buff)) ||
- (pkt_len= my_net_read(net)) == packet_error ||
- pkt_len < MIN_HANDSHAKE_SIZE)
- {
- inc_host_errors(&thd->remote.sin_addr);
- return(ER_HANDSHAKE_ERROR);
- }
- }
-#ifdef _CUSTOMCONFIG_
-#include "_cust_sql_parse.h"
-#endif
- if (connect_errors)
- reset_host_errors(&thd->remote.sin_addr);
- if (thd->packet.alloc(thd->variables.net_buffer_length))
- return(ER_OUT_OF_RESOURCES);
-
- thd->client_capabilities=uint2korr(net->read_pos);
- if (thd->client_capabilities & CLIENT_PROTOCOL_41)
- {
- thd->client_capabilities|= ((ulong) uint2korr(net->read_pos+2)) << 16;
- thd->max_client_packet_length= uint4korr(net->read_pos+4);
- DBUG_PRINT("info", ("client_character_set: %d", (uint) net->read_pos[8]));
- thd_init_client_charset(thd, (uint) net->read_pos[8]);
- thd->update_charset();
- end= (char*) net->read_pos+32;
- }
- else
- {
- thd->max_client_packet_length= uint3korr(net->read_pos+2);
- end= (char*) net->read_pos+5;
- }
-
- if (thd->client_capabilities & CLIENT_IGNORE_SPACE)
- thd->variables.sql_mode|= MODE_IGNORE_SPACE;
-#ifdef HAVE_OPENSSL
- DBUG_PRINT("info", ("client capabilities: %lu", thd->client_capabilities));
- if (thd->client_capabilities & CLIENT_SSL)
- {
- /* Do the SSL layering. */
- if (!ssl_acceptor_fd)
- {
- inc_host_errors(&thd->remote.sin_addr);
- return(ER_HANDSHAKE_ERROR);
- }
- DBUG_PRINT("info", ("IO layer change in progress..."));
- if (sslaccept(ssl_acceptor_fd, net->vio, net->read_timeout))
- {
- DBUG_PRINT("error", ("Failed to accept new SSL connection"));
- inc_host_errors(&thd->remote.sin_addr);
- return(ER_HANDSHAKE_ERROR);
- }
- DBUG_PRINT("info", ("Reading user information over SSL layer"));
- if ((pkt_len= my_net_read(net)) == packet_error ||
- pkt_len < NORMAL_HANDSHAKE_SIZE)
- {
- DBUG_PRINT("error", ("Failed to read user information (pkt_len= %lu)",
- pkt_len));
- inc_host_errors(&thd->remote.sin_addr);
- return(ER_HANDSHAKE_ERROR);
- }
- }
-#endif
-
- if (end >= (char*) net->read_pos+ pkt_len +2)
- {
- inc_host_errors(&thd->remote.sin_addr);
- return(ER_HANDSHAKE_ERROR);
- }
-
- if (thd->client_capabilities & CLIENT_INTERACTIVE)
- thd->variables.net_wait_timeout= thd->variables.net_interactive_timeout;
- if ((thd->client_capabilities & CLIENT_TRANSACTIONS) &&
- opt_using_transactions)
- net->return_status= &thd->server_status;
-
- char *user= end;
- char *passwd= strend(user)+1;
- size_t user_len= passwd - user - 1;
- char *db= passwd;
- char db_buff[NAME_LEN + 1]; // buffer to store db in utf8
- char user_buff[USERNAME_LENGTH + 1]; // buffer to store user in utf8
- uint dummy_errors;
-
- /*
- Old clients send null-terminated string as password; new clients send
- the size (1 byte) + string (not null-terminated). Hence in case of empty
- password both send '\0'.
-
- Cast *passwd to an unsigned char, so that it doesn't extend the sign for
- *passwd > 127 and become 2**32-127 after casting to uint.
- */
- uint passwd_len= thd->client_capabilities & CLIENT_SECURE_CONNECTION ?
- (uchar)(*passwd++) : (uint) strlen(passwd);
- db= thd->client_capabilities & CLIENT_CONNECT_WITH_DB ?
- db + passwd_len + 1 : 0;
- size_t db_len= db ? strlen(db) : 0;
-
- if (passwd + passwd_len + db_len > (char *)net->read_pos + pkt_len)
- {
- inc_host_errors(&thd->remote.sin_addr);
- return ER_HANDSHAKE_ERROR;
- }
-
- /* Since 4.1 all database names are stored in utf8 */
- if (db)
- {
- db_buff[copy_and_convert(db_buff, sizeof(db_buff)-1,
- system_charset_info,
- db, (uint) db_len,
- thd->charset(), &dummy_errors)]= 0;
- db= db_buff;
- }
-
- user_buff[user_len= copy_and_convert(user_buff, sizeof(user_buff)-1,
- system_charset_info, user, (uint) user_len,
- thd->charset(), &dummy_errors)]= '\0';
- user= user_buff;
-
- /* If username starts and ends in "'", chop them off */
- if (user_len > 1 && user[0] == '\'' && user[user_len - 1] == '\'')
- {
- user[user_len-1]= 0;
- user++;
- user_len-= 2;
- }
-
- if (thd->main_security_ctx.user)
- x_free(thd->main_security_ctx.user);
- if (!(thd->main_security_ctx.user= my_strdup(user, MYF(0))))
- return (ER_OUT_OF_RESOURCES);
- return check_user(thd, COM_CONNECT, passwd, passwd_len, db, TRUE);
+ DBUG_ASSERT(command >= 0 && command <= SQLCOM_END);
+ 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)
{
Vio* save_vio;
ulong save_client_capabilities;
- thd->proc_info= "Execution of init_command";
+#if defined(ENABLED_PROFILING) && defined(COMMUNITY_SERVER)
+ thd->profiling.start_new_query();
+ thd->profiling.set_query_source(init_command_var->value,
+ init_command_var->value_length);
+#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);
- thd->query= init_command_var->value;
- thd->query_length= init_command_var->value_length;
save_client_capabilities= thd->client_capabilities;
thd->client_capabilities|= CLIENT_MULTI_QUERIES;
/*
@@ -1116,167 +398,23 @@ void execute_init_command(THD *thd, sys_var_str *init_command_var,
*/
save_vio= thd->net.vio;
thd->net.vio= 0;
- thd->net.no_send_error= 0;
- dispatch_command(COM_QUERY, thd, thd->query, thd->query_length+1);
+ dispatch_command(COM_QUERY, thd,
+ init_command_var->value,
+ init_command_var->value_length);
rw_unlock(var_mutex);
thd->client_capabilities= save_client_capabilities;
thd->net.vio= save_vio;
-}
-
-pthread_handler_t handle_one_connection(void *arg)
-{
- THD *thd=(THD*) arg;
- uint launch_time =
- (uint) ((thd->thr_create_time = time(NULL)) - thd->connect_time);
- if (launch_time >= slow_launch_time)
- statistic_increment(slow_launch_threads,&LOCK_status );
-
- pthread_detach_this_thread();
-
-#if !defined( __WIN__) && !defined(OS2) // Win32 calls this in pthread_create
- /* The following calls needs to be done before we call DBUG_ macros */
- if (!(test_flags & TEST_NO_THREADS) & my_thread_init())
- {
- close_connection(thd, ER_OUT_OF_RESOURCES, 1);
- statistic_increment(aborted_connects,&LOCK_status);
- end_thread(thd,0);
- return 0;
- }
+#if defined(ENABLED_PROFILING) && defined(COMMUNITY_SERVER)
+ thd->profiling.finish_current_query();
#endif
-
- /*
- handle_one_connection() is the only way a thread would start
- and would always be on top of the stack, therefore, the thread
- stack always starts at the address of the first local variable
- of handle_one_connection, which is thd. We need to know the
- start of the stack so that we could check for stack overruns.
- */
- DBUG_PRINT("info", ("handle_one_connection called by thread %lu\n",
- thd->thread_id));
- /* now that we've called my_thread_init(), it is safe to call DBUG_* */
-
-#if defined(__WIN__)
- win_install_sigabrt_handler();
-#elif !defined(OS2) && !defined(__NETWARE__)
- sigset_t set;
- VOID(sigemptyset(&set)); // Get mask in use
- VOID(pthread_sigmask(SIG_UNBLOCK,&set,&thd->block_signals));
-#endif
- thd->thread_stack= (char*) &thd;
- if (thd->store_globals())
- {
- close_connection(thd, ER_OUT_OF_RESOURCES, 1);
- statistic_increment(aborted_connects,&LOCK_status);
- end_thread(thd,0);
- return 0;
- }
-
- do
- {
- int error;
- NET *net= &thd->net;
- Security_context *sctx= thd->security_ctx;
- net->no_send_error= 0;
-
- /* Use "connect_timeout" value during connection phase */
- my_net_set_read_timeout(net, connect_timeout);
- my_net_set_write_timeout(net, connect_timeout);
-
- if ((error=check_connection(thd)))
- { // Wrong permissions
- if (error > 0)
- net_printf_error(thd, error, sctx->host_or_ip);
-#ifdef __NT__
- if (vio_type(net->vio) == VIO_TYPE_NAMEDPIPE)
- my_sleep(1000); /* must wait after eof() */
-#endif
- statistic_increment(aborted_connects,&LOCK_status);
- goto end_thread;
- }
-#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)
- net->compress=1; // Use compression
-
- thd->version= refresh_version;
- thd->proc_info= 0;
- thd->command= COM_SLEEP;
- thd->init_for_queries();
-
- if (sys_init_connect.value_length && !(sctx->master_access & SUPER_ACL))
- {
- execute_init_command(thd, &sys_init_connect, &LOCK_sys_init_connect);
- if (thd->query_error)
- {
- thd->killed= THD::KILL_CONNECTION;
- 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", net->last_error);
- }
- thd->proc_info=0;
- thd->init_for_queries();
- }
-
- /* Connect completed, set read/write timeouts back to tdefault */
- my_net_set_read_timeout(net, thd->variables.net_read_timeout);
- my_net_set_write_timeout(net, thd->variables.net_write_timeout);
-
- while (!net->error && net->vio != 0 &&
- !(thd->killed == THD::KILL_CONNECTION))
- {
- net->no_send_error= 0;
- if (do_command(thd))
- break;
- }
- if (thd->user_connect)
- decrease_user_connections(thd->user_connect);
-
- if (thd->killed ||
- net->vio && net->error && net->report_error)
- {
- statistic_increment(aborted_threads, &LOCK_status);
- }
-
- if (net->error && net->vio != 0 && net->report_error)
- {
- if (!thd->killed && thd->variables.log_warnings > 1)
- {
- 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,
- (net->last_errno ? ER(net->last_errno) :
- ER(ER_UNKNOWN_ERROR)));
- }
-
- net_send_error(thd, net->last_errno, NullS);
- }
-
-end_thread:
- close_connection(thd, 0, 1);
- end_thread(thd,1);
- /*
- If end_thread returns, we are either running with --one-thread
- or this thread has been schedule to handle the next query
- */
- thd= current_thd;
- thd->thread_stack= (char*) &thd;
- } while (!(test_flags & TEST_NO_THREADS));
- /* The following is only executed if we are not using --one-thread */
- return(0); /* purecov: deadcode */
}
-#endif /* EMBEDDED_LIBRARY */
-/*
+/**
Execute commands from bootstrap_file.
- Used when creating the initial grant tables
+
+ Used when creating the initial grant tables.
*/
pthread_handler_t handle_bootstrap(void *arg)
@@ -1301,20 +439,16 @@ pthread_handler_t handle_bootstrap(void *arg)
#ifndef EMBEDDED_LIBRARY
pthread_detach_this_thread();
thd->thread_stack= (char*) &thd;
-#if !defined(__WIN__) && !defined(OS2) && !defined(__NETWARE__)
- sigset_t set;
- VOID(sigemptyset(&set)); // Get mask in use
- VOID(pthread_sigmask(SIG_UNBLOCK,&set,&thd->block_signals));
-#endif
#endif /* EMBEDDED_LIBRARY */
if (thd->variables.max_join_size == HA_POS_ERROR)
thd->options |= OPTION_BIG_SELECTS;
- thd->proc_info=0;
+ thd_proc_info(thd, 0);
thd->version=refresh_version;
thd->security_ctx->priv_user=
thd->security_ctx->user= (char*) my_strdup("boot", MYF(MY_WME));
+ thd->security_ctx->priv_host[0]=0;
/*
Make the "client" handle multiple results. This is necessary
to enable stored procedures with SELECTs and Dynamic SQL
@@ -1326,35 +460,49 @@ pthread_handler_t handle_bootstrap(void *arg)
thd->init_for_queries();
while (fgets(buff, thd->net.max_packet, file))
{
- ulong length= (ulong) strlen(buff);
- while (buff[length-1] != '\n' && !feof(file))
- {
- /*
- We got only a part of the current string. Will try to increase
- net buffer then read the rest of the current string.
- */
- if (net_realloc(&(thd->net), 2 * thd->net.max_packet))
- {
- net_send_error(thd, ER_NET_PACKET_TOO_LARGE, NullS);
- thd->fatal_error();
- break;
- }
- buff= (char*) thd->net.buff;
- fgets(buff + length, thd->net.max_packet - length, file);
- length+= (ulong) strlen(buff + length);
- }
- if (thd->is_fatal_error)
- break;
+ /* strlen() can't be deleted because fgets() doesn't return length */
+ ulong length= (ulong) strlen(buff);
+ while (buff[length-1] != '\n' && !feof(file))
+ {
+ /*
+ We got only a part of the current string. Will try to increase
+ net buffer then read the rest of the current string.
+ */
+ /* purecov: begin tested */
+ if (net_realloc(&(thd->net), 2 * thd->net.max_packet))
+ {
+ net_end_statement(thd);
+ bootstrap_error= 1;
+ break;
+ }
+ buff= (char*) thd->net.buff;
+ fgets(buff + length, thd->net.max_packet - length, file);
+ length+= (ulong) strlen(buff + length);
+ /* purecov: end */
+ }
+ if (bootstrap_error)
+ break; /* purecov: inspected */
while (length && (my_isspace(thd->charset(), buff[length-1]) ||
- buff[length-1] == ';'))
+ buff[length-1] == ';'))
length--;
buff[length]=0;
+
+ /* Skip lines starting with delimiter */
+ if (strncmp(buff, STRING_WITH_LEN("delimiter")) == 0)
+ continue;
+
thd->query_length=length;
- thd->query= thd->memdup_w_gap(buff, length+1,
- thd->db_length+1+QUERY_CACHE_FLAGS_SIZE);
+ thd->query= (char*) thd->memdup_w_gap(buff, length+1,
+ thd->db_length+1+
+ QUERY_CACHE_FLAGS_SIZE);
thd->query[length] = '\0';
DBUG_PRINT("query",("%-.4096s",thd->query));
+#if defined(ENABLED_PROFILING) && defined(COMMUNITY_SERVER)
+ thd->profiling.start_new_query();
+ thd->profiling.set_query_source(thd->query, length);
+#endif
+
/*
We don't need to obtain LOCK_thread_count here because in bootstrap
mode we have only one thread.
@@ -1364,16 +512,15 @@ pthread_handler_t handle_bootstrap(void *arg)
mysql_parse(thd, thd->query, length, & found_semicolon);
close_thread_tables(thd); // Free tables
- if (thd->is_fatal_error)
- break;
+ bootstrap_error= thd->is_error();
+ net_end_statement(thd);
- if (thd->net.report_error)
- {
- /* The query failed, send error to log and abort bootstrap */
- net_send_error(thd);
- thd->fatal_error();
+#if defined(ENABLED_PROFILING) && defined(COMMUNITY_SERVER)
+ thd->profiling.finish_current_query();
+#endif
+
+ if (bootstrap_error)
break;
- }
free_root(thd->mem_root,MYF(MY_KEEP_PREALLOC));
#ifdef USING_TRANSACTIONS
@@ -1382,9 +529,6 @@ pthread_handler_t handle_bootstrap(void *arg)
}
end:
- /* Remember the exit code of bootstrap */
- bootstrap_error= thd->is_fatal_error;
-
net_end(&thd->net);
thd->cleanup();
delete thd;
@@ -1401,7 +545,61 @@ end:
}
- /* This works because items are allocated with sql_alloc() */
+/**
+ @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() */
+
+void free_items(Item *item)
+{
+ Item *next;
+ DBUG_ENTER("free_items");
+ for (; item ; item=next)
+ {
+ next=item->next;
+ item->delete_self();
+ }
+ DBUG_VOID_RETURN;
+}
+
+/* This works because items are allocated with sql_alloc() */
void cleanup_items(Item *item)
{
@@ -1411,49 +609,53 @@ void cleanup_items(Item *item)
DBUG_VOID_RETURN;
}
-/*
- Handle COM_TABLE_DUMP command
+/**
+ Handle COM_TABLE_DUMP command.
- SYNOPSIS
- mysql_table_dump
- thd thread handle
- db database name or an empty string. If empty,
- the current database of the connection is used
- tbl_name name of the table to dump
+ @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
- NOTES
+ @note
This function is written to handle one specific command only.
- RETURN VALUE
+ @retval
0 success
+ @retval
1 error, the error message is set in THD
*/
static
-int mysql_table_dump(THD* thd, char* db, char* tbl_name)
+int mysql_table_dump(THD *thd, LEX_STRING *db, char *tbl_name)
{
TABLE* table;
TABLE_LIST* table_list;
int error = 0;
DBUG_ENTER("mysql_table_dump");
- db = (db && db[0]) ? db : thd->db;
+ 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;
+ table_list->db= db->str;
table_list->table_name= table_list->alias= tbl_name;
table_list->lock_type= TL_READ_NO_INSERT;
table_list->prev_global= &table_list; // can be removed after merge with 4.1
- if (!db || check_db_name(db))
+ if (check_db_name(db))
{
- my_error(ER_WRONG_DB_NAME ,MYF(0), db ? db : "NULL");
+ /* purecov: begin inspected */
+ my_error(ER_WRONG_DB_NAME ,MYF(0), db->str ? db->str : "NULL");
goto err;
+ /* purecov: end */
}
if (lower_case_table_names)
my_casedn_str(files_charset_info, tbl_name);
- remove_escape(table_list->table_name);
- if (!(table=open_ltable(thd, table_list, TL_READ_NO_INSERT)))
+ 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))
@@ -1474,16 +676,14 @@ err:
DBUG_RETURN(error);
}
-/*
- Ends the current transaction and (maybe) begin the next
+/**
+ Ends the current transaction and (maybe) begin the next.
- SYNOPSIS
- end_trans()
- thd Current thread
- completion Completion type
+ @param thd Current thread
+ @param completion Completion type
- RETURN
- 0 - OK
+ @retval
+ 0 OK
*/
int end_trans(THD *thd, enum enum_mysql_completiontype completion)
@@ -1512,7 +712,7 @@ int end_trans(THD *thd, enum enum_mysql_completiontype completion)
*/
thd->server_status&= ~SERVER_STATUS_IN_TRANS;
res= ha_commit(thd);
- thd->options&= ~(ulong) OPTION_BEGIN;
+ thd->options&= ~(OPTION_BEGIN | OPTION_KEEP_LOG);
thd->transaction.all.modified_non_trans_table= FALSE;
break;
case COMMIT_RELEASE:
@@ -1530,7 +730,7 @@ int end_trans(THD *thd, enum enum_mysql_completiontype completion)
thd->server_status&= ~SERVER_STATUS_IN_TRANS;
if (ha_rollback(thd))
res= -1;
- thd->options&= ~(ulong) OPTION_BEGIN;
+ 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);
@@ -1552,18 +752,21 @@ int end_trans(THD *thd, enum enum_mysql_completiontype completion)
#ifndef EMBEDDED_LIBRARY
-/*
+/**
Read one command from connection and execute it (query or simple command).
This function is called in loop from thread function.
- SYNOPSIS
- do_command()
- RETURN VALUE
+
+ For profiling to work, it must never be called recursively.
+
+ @retval
0 success
+ @retval
1 request of thread shutdown (see dispatch_command() description)
*/
-static bool do_command(THD *thd)
+bool do_command(THD *thd)
{
+ bool return_value;
char *packet= 0;
ulong packet_length;
NET *net= &thd->net;
@@ -1584,10 +787,20 @@ static bool do_command(THD *thd)
*/
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();
net_new_transaction(net);
- if ((packet_length=my_net_read(net)) == packet_error)
+
+ packet_length= my_net_read(net);
+#if defined(ENABLED_PROFILING) && defined(COMMUNITY_SERVER)
+ thd->profiling.start_new_query();
+#endif
+ if (packet_length == packet_error)
{
DBUG_PRINT("info",("Got error %d reading command from socket %s",
net->error,
@@ -1595,41 +808,62 @@ static bool do_command(THD *thd)
/* Check if we can continue without closing the connection */
+ /* The error must be set. */
+ DBUG_ASSERT(thd->is_error());
+ net_end_statement(thd);
+
if (net->error != 3)
- DBUG_RETURN(TRUE); // We have to close it.
+ {
+ return_value= TRUE; // We have to close it.
+ goto out;
+ }
- net_send_error(thd, net->last_errno, NullS);
net->error= 0;
- DBUG_RETURN(FALSE);
+ return_value= FALSE;
+ goto out;
}
- else
+
+ packet= (char*) net->read_pos;
+ /*
+ 'packet_length' contains length of data, as it was stored in packet
+ header. In case of malformed header, my_net_read returns zero.
+ If packet_length is not zero, my_net_read ensures that the returned
+ number of bytes was actually read from network.
+ There is also an extra safety measure in my_net_read:
+ it sets packet[packet_length]= 0, but only for non-zero packets.
+ */
+ if (packet_length == 0) /* safety */
{
- packet=(char*) net->read_pos;
- command = (enum enum_server_command) (uchar) packet[0];
- if (command >= COM_END)
- command= COM_END; // Wrong command
- DBUG_PRINT("info",("Command on %s = %d (%s)",
- vio_description(net->vio), command,
- command_name[command]));
+ /* Initialize with COM_SLEEP packet */
+ packet[0]= (uchar) COM_SLEEP;
+ packet_length= 1;
}
+ /* Do not rely on my_net_read, extra safety against programming errors. */
+ packet[packet_length]= '\0'; /* safety */
+
+ command= (enum enum_server_command) (uchar) packet[0];
+
+ if (command >= COM_END)
+ command= COM_END; // Wrong command
+
+ DBUG_PRINT("info",("Command on %s = %d (%s)",
+ vio_description(net->vio), command,
+ command_name[command].str));
/* Restore read timeout value */
my_net_set_read_timeout(net, thd->variables.net_read_timeout);
- /*
- packet_length contains length of data, as it was stored in packet
- header. In case of malformed header, packet_length can be zero.
- If packet_length is not zero, my_net_read ensures that this number
- of bytes was actually read from network. Additionally my_net_read
- sets packet[packet_length]= 0 (thus if packet_length == 0,
- command == packet[0] == COM_SLEEP).
- In dispatch_command packet[packet_length] points beyond the end of packet.
- */
- DBUG_RETURN(dispatch_command(command,thd, packet+1, (uint) packet_length));
+ DBUG_ASSERT(packet_length);
+ 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 */
-
/**
@brief Determine if an attempt to update a non-temporary table while the
read-only option was enabled has been made.
@@ -1661,7 +895,7 @@ static my_bool deny_updates_if_read_only_option(THD *thd,
if (user_is_super)
DBUG_RETURN(FALSE);
- if (!uc_update_queries[lex->sql_command])
+ if (!(sql_command_flags[lex->sql_command] & CF_CHANGES_DATA))
DBUG_RETURN(FALSE);
/* Multi update is an exception and is dealt with later. */
@@ -1698,35 +932,34 @@ static my_bool deny_updates_if_read_only_option(THD *thd,
DBUG_RETURN(FALSE);
}
-/*
- Perform one connection-level (COM_XXXX) command.
+/**
+ Perform one connection-level (COM_XXXX) command.
- SYNOPSIS
- dispatch_command()
- thd connection handle
- command type of command to perform
- packet data for the command, packet is always null-terminated
- packet_length length of packet + 1 (to show that data is
- null-terminated) except for COM_SLEEP, where it
- can be zero.
- RETURN VALUE
+ @param command type of command to perform
+ @param thd connection handle
+ @param packet data for the command, packet is always null-terminated
+ @param packet_length length of packet + 1 (to show that data is
+ null-terminated) except for COM_SLEEP, where it
+ can be zero.
+
+ @todo
+ set thd->lex->sql_command to SQLCOM_END here.
+ @todo
+ The following has to be changed to an 8 byte integer
+
+ @retval
0 ok
+ @retval
1 request of thread shutdown, i. e. if command is
COM_QUIT/COM_SHUTDOWN
*/
-
bool dispatch_command(enum enum_server_command command, THD *thd,
char* packet, uint packet_length)
{
NET *net= &thd->net;
bool error= 0;
DBUG_ENTER("dispatch_command");
-
- if (thd->killed == THD::KILL_QUERY || thd->killed == THD::KILL_BAD_DATA)
- {
- thd->killed= THD::NOT_KILLED;
- thd->mysys_var->abort= 0;
- }
+ DBUG_PRINT("info",("packet: '%*.s'; command: %d", packet_length, packet, command));
thd->command=command;
/*
@@ -1738,7 +971,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
thd->set_time();
VOID(pthread_mutex_lock(&LOCK_thread_count));
thd->query_id= global_query_id;
-
+
switch( command ) {
/* Ignore these statements. */
case COM_STATISTICS:
@@ -1755,25 +988,27 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
statistic_increment(thd->status_var.questions, &LOCK_status);
next_query_id();
}
-
+
thread_running++;
/* TODO: set thd->lex->sql_command to SQLCOM_END here */
VOID(pthread_mutex_unlock(&LOCK_thread_count));
- thd->server_status&=
- ~(SERVER_QUERY_NO_INDEX_USED | SERVER_QUERY_NO_GOOD_INDEX_USED);
+ /**
+ Clear the set of flags that are expected to be cleared at the
+ beginning of each command.
+ */
+ thd->server_status&= ~SERVER_STATUS_CLEAR_SET;
switch (command) {
case COM_INIT_DB:
{
LEX_STRING tmp;
- statistic_increment(thd->status_var.com_stat[SQLCOM_CHANGE_DB],
- &LOCK_status);
+ status_var_increment(thd->status_var.com_stat[SQLCOM_CHANGE_DB]);
thd->convert_string(&tmp, system_charset_info,
- packet, (uint) strlen(packet), thd->charset());
+ packet, packet_length, thd->charset());
if (!mysql_change_db(thd, &tmp, FALSE))
{
- mysql_log.write(thd,command,"%s",thd->db);
- send_ok(thd);
+ general_log_write(thd, command, thd->db, thd->db_length);
+ my_ok(thd);
}
break;
}
@@ -1781,47 +1016,54 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
case COM_REGISTER_SLAVE:
{
if (!register_slave(thd, (uchar*)packet, packet_length))
- send_ok(thd);
+ my_ok(thd);
break;
}
#endif
case COM_TABLE_DUMP:
{
- char *db, *tbl_name;
+ char *tbl_name;
+ LEX_STRING db;
+ /* Safe because there is always a trailing \0 at the end of the packet */
uint db_len= *(uchar*) packet;
- if (db_len >= packet_length || db_len > NAME_LEN)
+ 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)
+ 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;
}
- statistic_increment(thd->status_var.com_other, &LOCK_status);
+ status_var_increment(thd->status_var.com_other);
thd->enable_slow_log= opt_log_slow_admin_statements;
- db= thd->alloc(db_len + tbl_len + 2);
- if (!db)
+ 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;
}
- tbl_name= strmake(db, packet + 1, db_len)+1;
+ db.length= db_len;
+ tbl_name= strmake(db.str, packet + 1, db_len)+1;
strmake(tbl_name, packet + db_len + 2, tbl_len);
- mysql_table_dump(thd, db, tbl_name);
+ if (mysql_table_dump(thd, &db, tbl_name) == 0)
+ thd->main_da.disable_status();
break;
}
case COM_CHANGE_USER:
{
+ status_var_increment(thd->status_var.com_other);
+ char *user= (char*) packet, *packet_end= packet + packet_length;
+ /* Safe because there is always a trailing \0 at the end of the packet */
+ char *passwd= strend(user)+1;
+
thd->change_user();
thd->clear_error(); // if errors from rollback
- statistic_increment(thd->status_var.com_other, &LOCK_status);
- char *user= (char*) packet;
- char *passwd= strend(user)+1;
/*
Old clients send null-terminated string ('\0' for empty string) for
password. New clients send the size (1 byte) + string (not null
@@ -1830,31 +1072,61 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
Cast *passwd to an unsigned char, so that it doesn't extend the sign
for *passwd > 127 and become 2**32-127 after casting to uint.
*/
- char db_buff[NAME_LEN+1]; // buffer to store db in utf8
+ char db_buff[NAME_LEN+1]; // buffer to store db in utf8
char *db= passwd;
- size_t passwd_len= thd->client_capabilities & CLIENT_SECURE_CONNECTION ?
- (uchar)(*passwd++) : strlen(passwd);
+ char *save_db;
+ /*
+ If there is no password supplied, the packet must contain '\0',
+ in any type of handshake (4.1 or pre-4.1).
+ */
+ if (passwd >= packet_end)
+ {
+ my_message(ER_UNKNOWN_COM_ERROR, ER(ER_UNKNOWN_COM_ERROR), MYF(0));
+ break;
+ }
+ uint passwd_len= (thd->client_capabilities & CLIENT_SECURE_CONNECTION ?
+ (uchar)(*passwd++) : strlen(passwd));
+ uint dummy_errors, save_db_length, db_length;
+ int res;
+ Security_context save_security_ctx= *thd->security_ctx;
+ USER_CONN *save_user_connect;
+
db+= passwd_len + 1;
-#ifndef EMBEDDED_LIBRARY
- /* Small check for incoming packet */
- if ((uint) ((uchar*) db - net->read_pos) > packet_length)
+ /*
+ Database name is always NUL-terminated, so in case of empty database
+ the packet must contain at least the trailing '\0'.
+ */
+ if (db >= packet_end)
{
my_message(ER_UNKNOWN_COM_ERROR, ER(ER_UNKNOWN_COM_ERROR), MYF(0));
break;
}
-#endif
+ db_length= strlen(db);
+
+ char *ptr= db + db_length + 1;
+ uint cs_number= 0;
+
+ if (ptr < packet_end)
+ {
+ if (ptr + 2 > packet_end)
+ {
+ my_message(ER_UNKNOWN_COM_ERROR, ER(ER_UNKNOWN_COM_ERROR), MYF(0));
+ break;
+ }
+
+ cs_number= uint2korr(ptr);
+ }
+
/* Convert database name to utf8 */
- uint dummy_errors;
db_buff[copy_and_convert(db_buff, sizeof(db_buff)-1,
- system_charset_info, db, (uint) strlen(db),
+ system_charset_info, db, db_length,
thd->charset(), &dummy_errors)]= 0;
db= db_buff;
/* Save user and privileges */
- uint save_db_length= thd->db_length;
- char *save_db= thd->db;
- Security_context save_security_ctx= *thd->security_ctx;
- USER_CONN *save_user_connect= thd->user_connect;
+ save_db_length= thd->db_length;
+ save_db= thd->db;
+ save_user_connect= thd->user_connect;
if (!(thd->security_ctx->user= my_strdup(user, MYF(0))))
{
@@ -1865,13 +1137,11 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
/* Clear variables that are allocated */
thd->user_connect= 0;
- int res= check_user(thd, COM_CHANGE_USER, passwd, (uint) passwd_len, db, FALSE);
+ thd->security_ctx->priv_user= thd->security_ctx->user;
+ res= check_user(thd, COM_CHANGE_USER, passwd, passwd_len, db, FALSE);
if (res)
{
- /* authentication failure, we shall restore old user */
- if (res > 0)
- my_message(ER_UNKNOWN_COM_ERROR, ER(ER_UNKNOWN_COM_ERROR), MYF(0));
x_free(thd->security_ctx->user);
*thd->security_ctx= save_security_ctx;
thd->user_connect= save_user_connect;
@@ -1885,8 +1155,14 @@ 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 */
- x_free((gptr) save_db);
- x_free((gptr) save_security_ctx.user);
+ x_free(save_db);
+ x_free(save_security_ctx.user);
+
+ if (cs_number)
+ {
+ thd_init_client_charset(thd, cs_number);
+ thd->update_charset();
+ }
}
break;
}
@@ -1926,50 +1202,58 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
break; // fatal error is set
char *packet_end= thd->query + thd->query_length;
/* 'b' stands for 'buffer' parameter', special for 'my_snprintf' */
- const char *format= "%.*b";
- const char* found_semicolon= NULL;
+ const char* end_of_stmt= NULL;
- mysql_log.write(thd,command, format, thd->query_length, thd->query);
+ general_log_write(thd, command, thd->query, thd->query_length);
DBUG_PRINT("query",("%-.4096s",thd->query));
+#if defined(ENABLED_PROFILING) && defined(COMMUNITY_SERVER)
+ thd->profiling.set_query_source(thd->query, thd->query_length);
+#endif
if (!(specialflag & SPECIAL_NO_PRIOR))
my_pthread_setprio(pthread_self(),QUERY_PRIOR);
- mysql_parse(thd, thd->query, thd->query_length, & found_semicolon);
+ mysql_parse(thd, thd->query, thd->query_length, &end_of_stmt);
- while (!thd->killed && found_semicolon && !thd->net.report_error)
+ while (!thd->killed && (end_of_stmt != NULL) && ! thd->is_error())
{
- char *next_packet= (char*) found_semicolon;
- net->no_send_error= 0;
+ char *beginning_of_next_stmt= (char*) end_of_stmt;
+
+ net_end_statement(thd);
+ query_cache_end_of_result(thd);
/*
Multiple queries exits, execute them individually
*/
- if (thd->lock || thd->open_tables || thd->derived_tables ||
- thd->prelocked_mode)
- close_thread_tables(thd);
- ulong length= (ulong)(packet_end - next_packet);
+ close_thread_tables(thd);
+ ulong length= (ulong)(packet_end - beginning_of_next_stmt);
log_slow_statement(thd);
/* Remove garbage at start of query */
- while (my_isspace(thd->charset(), *next_packet) && length > 0)
+ while (length > 0 && my_isspace(thd->charset(), *beginning_of_next_stmt))
{
- next_packet++;
+ beginning_of_next_stmt++;
length--;
}
+
+#if defined(ENABLED_PROFILING) && defined(COMMUNITY_SERVER)
+ thd->profiling.finish_current_query();
+ thd->profiling.start_new_query("continuing");
+ thd->profiling.set_query_source(beginning_of_next_stmt, length);
+#endif
+
VOID(pthread_mutex_lock(&LOCK_thread_count));
thd->query_length= length;
- thd->query= next_packet;
+ thd->query= beginning_of_next_stmt;
/*
Count each statement from the client.
*/
statistic_increment(thd->status_var.questions, &LOCK_status);
-
thd->query_id= next_query_id();
thd->set_time(); /* Reset the query start time. */
/* TODO: set thd->lex->sql_command to SQLCOM_END here */
VOID(pthread_mutex_unlock(&LOCK_thread_count));
- mysql_parse(thd, next_packet, length, & found_semicolon);
+ mysql_parse(thd, beginning_of_next_stmt, length, &end_of_stmt);
}
if (!(specialflag & SPECIAL_NO_PRIOR))
@@ -1984,7 +1268,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
break;
#else
{
- char *fields, *pend;
+ char *fields, *packet_end= packet + packet_length, *arg_end;
/* Locked closure of all tables */
TABLE_LIST table_list;
LEX_STRING conv_name;
@@ -1992,16 +1276,18 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
/* used as fields initializator */
lex_start(thd);
- statistic_increment(thd->status_var.com_stat[SQLCOM_SHOW_FIELDS],
- &LOCK_status);
+ 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))
break;
- pend= strend(packet);
+ /*
+ We have name + wildcard in packet, separated by endzero
+ */
+ arg_end= strend(packet);
thd->convert_string(&conv_name, system_charset_info,
- packet, (uint) (pend-packet), thd->charset());
+ packet, (uint) (arg_end - packet), thd->charset());
table_list.alias= table_list.table_name= conv_name.str;
- packet= pend+1;
+ packet= arg_end + 1;
if (!my_strcasecmp(system_charset_info, table_list.db,
INFORMATION_SCHEMA_NAME.str))
@@ -2011,19 +1297,17 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
table_list.schema_table= schema_table;
}
- thd->query_length= (uint) strlen(packet); // for simplicity: don't optimize
- if (!(thd->query=fields=thd->memdup(packet,thd->query_length+1)))
+ thd->query_length= (uint) (packet_end - packet); // Don't count end \0
+ if (!(thd->query=fields= (char*) thd->memdup(packet,thd->query_length+1)))
break;
- mysql_log.write(thd,command,"%s %s",table_list.table_name, fields);
+ 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);
- remove_escape(table_list.table_name); // This can't have wildcards
if (check_access(thd,SELECT_ACL,table_list.db,&table_list.grant.privilege,
0, 0, test(table_list.schema_table)))
break;
- if (grant_option &&
- check_grant(thd, SELECT_ACL, &table_list, 2, UINT_MAX, 0))
+ if (check_grant(thd, SELECT_ACL, &table_list, 2, UINT_MAX, 0))
break;
/* init structures for VIEW processing */
table_list.select_lex= &(thd->lex->select_lex);
@@ -2032,8 +1316,8 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
mysql_reset_thd_for_next_command(thd);
thd->lex->
- select_lex.table_list.link_in_list((byte*) &table_list,
- (byte**) &table_list.next_local);
+ select_lex.table_list.link_in_list((uchar*) &table_list,
+ (uchar**) &table_list.next_local);
thd->lex->add_to_query_tables(&table_list);
/* switch on VIEW optimisation: do not fill temporary tables */
@@ -2046,44 +1330,47 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
#endif
case COM_QUIT:
/* We don't calculate statistics for this command */
- mysql_log.write(thd,command,NullS);
+ general_log_print(thd, command, NullS);
net->error=0; // Don't give 'abort' message
+ thd->main_da.disable_status(); // Don't send anything back
error=TRUE; // End server
break;
+#ifdef REMOVED
case COM_CREATE_DB: // QQ: To be removed
{
- char *db=thd->strdup(packet), *alias;
+ LEX_STRING db, alias;
HA_CREATE_INFO create_info;
- statistic_increment(thd->status_var.com_stat[SQLCOM_CREATE_DB],
- &LOCK_status);
- // null test to handle EOM
- if (!db || !(alias= thd->strdup(db)) || check_db_name(db))
+ status_var_increment(thd->status_var.com_stat[SQLCOM_CREATE_DB]);
+ if (thd->make_lex_string(&db, packet, packet_length, FALSE) ||
+ thd->make_lex_string(&alias, db.str, db.length, FALSE) ||
+ check_db_name(&db))
{
- my_error(ER_WRONG_DB_NAME, MYF(0), db ? db : "NULL");
+ my_error(ER_WRONG_DB_NAME, MYF(0), db.str ? db.str : "NULL");
break;
}
- if (check_access(thd,CREATE_ACL,db,0,1,0,is_schema_db(db)))
+ if (check_access(thd, CREATE_ACL, db.str , 0, 1, 0,
+ is_schema_db(db.str)))
break;
- mysql_log.write(thd,command,packet);
+ general_log_print(thd, command, packet);
bzero(&create_info, sizeof(create_info));
- mysql_create_db(thd, (lower_case_table_names == 2 ? alias : db),
+ mysql_create_db(thd, (lower_case_table_names == 2 ? alias.str : db.str),
&create_info, 0);
break;
}
case COM_DROP_DB: // QQ: To be removed
{
- statistic_increment(thd->status_var.com_stat[SQLCOM_DROP_DB],
- &LOCK_status);
- char *db=thd->strdup(packet);
- /* null test to handle EOM */
- if (!db || check_db_name(db))
+ status_var_increment(thd->status_var.com_stat[SQLCOM_DROP_DB]);
+ LEX_STRING db;
+
+ if (thd->make_lex_string(&db, packet, packet_length, FALSE) ||
+ check_db_name(&db))
{
- my_error(ER_WRONG_DB_NAME, MYF(0), db ? db : "NULL");
+ my_error(ER_WRONG_DB_NAME, MYF(0), db.str ? db.str : "NULL");
break;
}
- if (check_access(thd,DROP_ACL,db,0,1,0,is_schema_db(db)))
+ if (check_access(thd, DROP_ACL, db.str, 0, 1, 0, is_schema_db(db.str)))
break;
if (thd->locked_tables || thd->active_transaction())
{
@@ -2091,10 +1378,11 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
ER(ER_LOCK_OR_ACTIVE_TRANSACTION), MYF(0));
break;
}
- mysql_log.write(thd,command,db);
- mysql_rm_db(thd, db, 0, 0);
+ general_log_write(thd, command, db.str, db.length);
+ mysql_rm_db(thd, db.str, 0, 0);
break;
}
+#endif
#ifndef EMBEDDED_LIBRARY
case COM_BINLOG_DUMP:
{
@@ -2102,7 +1390,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
ushort flags;
uint32 slave_server_id;
- statistic_increment(thd->status_var.com_other,&LOCK_status);
+ status_var_increment(thd->status_var.com_other);
thd->enable_slow_log= opt_log_slow_admin_statements;
if (check_global_access(thd, REPL_SLAVE_ACL))
break;
@@ -2115,7 +1403,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
kill_zombie_dump_threads(slave_server_id);
thd->server_id = slave_server_id;
- mysql_log.write(thd, command, "Log: '%s' Pos: %ld", packet+10,
+ general_log_print(thd, command, "Log: '%s' Pos: %ld", packet+10,
(long) pos);
mysql_binlog_send(thd, thd->strdup(packet + 10), (my_off_t) pos, flags);
unregister_slave(thd,1,1);
@@ -2127,31 +1415,29 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
case COM_REFRESH:
{
bool not_used;
- statistic_increment(thd->status_var.com_stat[SQLCOM_FLUSH],
- &LOCK_status);
+ status_var_increment(thd->status_var.com_stat[SQLCOM_FLUSH]);
ulong options= (ulong) (uchar) packet[0];
if (check_global_access(thd,RELOAD_ACL))
break;
- mysql_log.write(thd,command,NullS);
+ general_log_print(thd, command, NullS);
if (!reload_acl_and_cache(thd, options, (TABLE_LIST*) 0, &not_used))
- send_ok(thd);
+ my_ok(thd);
break;
}
#ifndef EMBEDDED_LIBRARY
case COM_SHUTDOWN:
{
- statistic_increment(thd->status_var.com_other, &LOCK_status);
+ status_var_increment(thd->status_var.com_other);
if (check_global_access(thd,SHUTDOWN_ACL))
break; /* purecov: inspected */
/*
If the client is < 4.1.3, it is going to send us no argument; then
- packet_length is 1, packet[0] is the end 0 of the packet. Note that
+ packet_length is 0, packet[0] is the end 0 of the packet. Note that
SHUTDOWN_DEFAULT is 0. If client is >= 4.1.3, the shutdown level is in
packet[0].
*/
enum mysql_enum_shutdown_level level=
(enum mysql_enum_shutdown_level) (uchar) packet[0];
- DBUG_PRINT("quit",("Got shutdown command for level %u", level));
if (level == SHUTDOWN_DEFAULT)
level= SHUTDOWN_WAIT_ALL_BUFFERS; // soon default will be configurable
else if (level != SHUTDOWN_WAIT_ALL_BUFFERS)
@@ -2160,15 +1446,8 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
break;
}
DBUG_PRINT("quit",("Got shutdown command for level %u", level));
- mysql_log.write(thd,command,NullS);
- send_eof(thd);
-#ifdef __WIN__
- sleep(1); // must wait after eof()
-#endif
-#ifndef OS2
- send_eof(thd); // This is for 'quit request'
-#endif
- close_connection(thd, 0, 1);
+ general_log_print(thd, command, NullS);
+ my_eof(thd);
close_thread_tables(thd); // Free before kill
kill_mysql();
error=TRUE;
@@ -2177,75 +1456,88 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
#endif
case COM_STATISTICS:
{
- mysql_log.write(thd,command,NullS);
- statistic_increment(thd->status_var.com_stat[SQLCOM_SHOW_STATUS],
- &LOCK_status);
-#ifndef EMBEDDED_LIBRARY
- char buff[200];
-#else
- char *buff= thd->net.last_error;
-#endif
-
STATUS_VAR current_global_status_var;
+ ulong uptime;
+ uint length;
+ ulonglong queries_per_second1000;
+ char buff[250];
+ uint buff_len= sizeof(buff);
+
+ general_log_print(thd, command, NullS);
+ status_var_increment(thd->status_var.com_stat[SQLCOM_SHOW_STATUS]);
calc_sum_of_all_status(&current_global_status_var);
-
- ulong uptime = (ulong) (thd->start_time - server_start_time);
- sprintf((char*) buff,
- "Uptime: %lu Threads: %d Questions: %lu Slow queries: %lu Opens: %lu Flush tables: %lu Open tables: %u Queries per second avg: %.3f",
- uptime,
- (int) thread_count, (ulong) thd->query_id,
- current_global_status_var.long_query_count,
- current_global_status_var.opened_tables, refresh_version, cached_tables(),
- (uptime ? (ulonglong2double(thd->query_id) / (double) uptime) :
- (double) 0));
+ if (!(uptime= (ulong) (thd->start_time - server_start_time)))
+ queries_per_second1000= 0;
+ else
+ queries_per_second1000= thd->query_id * LL(1000) / uptime;
+
+ length= 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 EMBEDDED_LIBRARY
+ /* Store the buffer in permanent memory */
+ my_ok(thd, 0, 0, buff);
+#endif
#ifdef SAFEMALLOC
if (sf_malloc_cur_memory) // Using SAFEMALLOC
- sprintf(strend(buff), " Memory in use: %ldK Max memory used: %ldK",
- (sf_malloc_cur_memory+1023L)/1024L,
- (sf_malloc_max_memory+1023L)/1024L);
+ {
+ char *end= buff + length;
+ length+= my_snprintf(end, buff_len - length - 1,
+ end," 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, buff,(uint) strlen(buff)));
+ VOID(my_net_write(net, (uchar*) buff, length));
VOID(net_flush(net));
+ thd->main_da.disable_status();
#endif
break;
}
case COM_PING:
- statistic_increment(thd->status_var.com_other, &LOCK_status);
- send_ok(thd); // Tell client we are alive
+ status_var_increment(thd->status_var.com_other);
+ my_ok(thd); // Tell client we are alive
break;
case COM_PROCESS_INFO:
- statistic_increment(thd->status_var.com_stat[SQLCOM_SHOW_PROCESSLIST],
- &LOCK_status);
+ status_var_increment(thd->status_var.com_stat[SQLCOM_SHOW_PROCESSLIST]);
if (!thd->security_ctx->priv_user[0] &&
check_global_access(thd, PROCESS_ACL))
break;
- mysql_log.write(thd,command,NullS);
+ general_log_print(thd, command, NullS);
mysqld_list_processes(thd,
thd->security_ctx->master_access & PROCESS_ACL ?
NullS : thd->security_ctx->priv_user, 0);
break;
case COM_PROCESS_KILL:
{
- statistic_increment(thd->status_var.com_stat[SQLCOM_KILL], &LOCK_status);
+ status_var_increment(thd->status_var.com_stat[SQLCOM_KILL]);
ulong id=(ulong) uint4korr(packet);
- kill_one_thread(thd,id,false);
+ sql_kill(thd,id,false);
break;
}
case COM_SET_OPTION:
{
- statistic_increment(thd->status_var.com_stat[SQLCOM_SET_OPTION],
- &LOCK_status);
+ status_var_increment(thd->status_var.com_stat[SQLCOM_SET_OPTION]);
uint opt_command= uint2korr(packet);
switch (opt_command) {
case (int) MYSQL_OPTION_MULTI_STATEMENTS_ON:
thd->client_capabilities|= CLIENT_MULTI_STATEMENTS;
- send_eof(thd);
+ my_eof(thd);
break;
case (int) MYSQL_OPTION_MULTI_STATEMENTS_OFF:
thd->client_capabilities&= ~CLIENT_MULTI_STATEMENTS;
- send_eof(thd);
+ my_eof(thd);
break;
default:
my_message(ER_UNKNOWN_COM_ERROR, ER(ER_UNKNOWN_COM_ERROR), MYF(0));
@@ -2254,12 +1546,12 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
break;
}
case COM_DEBUG:
- statistic_increment(thd->status_var.com_other, &LOCK_status);
+ status_var_increment(thd->status_var.com_other);
if (check_global_access(thd, SUPER_ACL))
break; /* purecov: inspected */
mysql_print_status();
- mysql_log.write(thd,command,NullS);
- send_eof(thd);
+ general_log_print(thd, command, NullS);
+ my_eof(thd);
break;
case COM_SLEEP:
case COM_CONNECT: // Impossible here
@@ -2270,35 +1562,39 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
my_message(ER_UNKNOWN_COM_ERROR, ER(ER_UNKNOWN_COM_ERROR), MYF(0));
break;
}
- if (thd->lock || thd->open_tables || thd->derived_tables ||
- thd->prelocked_mode)
+
+ /* 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();
+
+
+ /* report error issued during command execution */
+ if (thd->killed_errno())
{
- thd->proc_info="closing tables";
- close_thread_tables(thd); /* Free tables */
+ if (! thd->main_da.is_set())
+ thd->send_kill_message();
+ }
+ if (thd->killed == THD::KILL_QUERY || thd->killed == THD::KILL_BAD_DATA)
+ {
+ thd->killed= THD::NOT_KILLED;
+ thd->mysys_var->abort= 0;
}
- /*
- assume handlers auto-commit (if some doesn't - transaction handling
- in MySQL should be redesigned to support it; it's a big change,
- and it's not worth it - better to commit explicitly only writing
- transactions, read-only ones should better take care of themselves.
- saves some work in 2pc too)
- see also sql_base.cc - close_thread_tables()
- */
- bzero(&thd->transaction.stmt, sizeof(thd->transaction.stmt));
- if (!thd->active_transaction())
- thd->transaction.xid_state.xid.null();
- /* report error issued during command execution */
- if (thd->killed_errno() && !thd->net.report_error)
- thd->send_kill_message();
- if (thd->net.report_error)
- net_send_error(thd);
+ net_end_statement(thd);
+ query_cache_end_of_result(thd);
+
+ thd->proc_info= "closing tables";
+ /* Free tables */
+ close_thread_tables(thd);
log_slow_statement(thd);
- thd->proc_info="cleaning up";
+ thd_proc_info(thd, "cleaning up");
VOID(pthread_mutex_lock(&LOCK_thread_count)); // For process list
- thd->proc_info=0;
+ thd_proc_info(thd, 0);
thd->command=COM_SLEEP;
thd->query=0;
thd->query_length=0;
@@ -2312,7 +1608,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
void log_slow_statement(THD *thd)
{
- time_t start_of_query;
+ DBUG_ENTER("log_slow_statement");
/*
The following should never be true with our current code base,
@@ -2320,10 +1616,7 @@ void log_slow_statement(THD *thd)
statement in a trigger or stored function
*/
if (unlikely(thd->in_sub_stmt))
- return; // Don't set time for sub stmt
-
- start_of_query= thd->start_time;
- thd->end_time(); // Set start time
+ DBUG_VOID_RETURN; // Don't set time for sub stmt
/*
Do not log administrative statements unless the appropriate option is
@@ -2331,48 +1624,48 @@ void log_slow_statement(THD *thd)
*/
if (thd->enable_slow_log && !thd->user_time)
{
- thd->proc_info="logging slow query";
+ ulonglong end_utime_of_query= thd->current_utime();
+ thd_proc_info(thd, "logging slow query");
- if ((thd->start_time > thd->time_after_lock &&
- (ulong) (thd->start_time - thd->time_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 &&
- /* == SQLCOM_END unless this is a SHOW command */
- thd->lex->orig_sql_command == SQLCOM_END)
+ if (((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++;
- mysql_slow_log.write(thd, thd->query, thd->query_length, start_of_query);
+ slow_log_print(thd, thd->query, thd->query_length, end_utime_of_query);
}
}
+ DBUG_VOID_RETURN;
}
-/*
+/**
Create a TABLE_LIST object for an INFORMATION_SCHEMA table.
- SYNOPSIS
- prepare_schema_table()
- thd thread handle
- lex current lex
- table_ident table alias if it's used
- schema_table_idx the type of the INFORMATION_SCHEMA table to be
- created
-
- DESCRIPTION
This function is used in the parser to convert a SHOW or DESCRIBE
table_name command to a SELECT from INFORMATION_SCHEMA.
It prepares a SELECT_LEX and a TABLE_LIST object to represent the
given command as a SELECT parse tree.
- NOTES
+ @param thd thread handle
+ @param lex current lex
+ @param table_ident table alias if it's used
+ @param schema_table_idx the type of the INFORMATION_SCHEMA table to be
+ created
+
+ @note
Due to the way this function works with memory and LEX it cannot
be used outside the parser (parse tree transformations outside
the parser break PS and SP).
- RETURN VALUE
+ @retval
0 success
+ @retval
1 out of memory or SHOW commands are not allowed
in this version of the server.
*/
@@ -2380,8 +1673,8 @@ void log_slow_statement(THD *thd)
int prepare_schema_table(THD *thd, LEX *lex, Table_ident *table_ident,
enum enum_schema_tables schema_table_idx)
{
- DBUG_ENTER("prepare_schema_table");
SELECT_LEX *schema_select_lex= NULL;
+ DBUG_ENTER("prepare_schema_table");
switch (schema_table_idx) {
case SCH_SCHEMATA:
@@ -2397,63 +1690,68 @@ int prepare_schema_table(THD *thd, LEX *lex, Table_ident *table_ident,
case SCH_TABLES:
case SCH_VIEWS:
case SCH_TRIGGERS:
+ case SCH_EVENTS:
#ifdef DONT_ALLOW_SHOW_COMMANDS
my_message(ER_NOT_ALLOWED_COMMAND,
ER(ER_NOT_ALLOWED_COMMAND), MYF(0)); /* purecov: inspected */
DBUG_RETURN(1);
#else
- if (lex->select_lex.db == NULL &&
- lex->copy_db_to(&lex->select_lex.db, NULL))
{
- DBUG_RETURN(1);
- }
-
- schema_select_lex= new SELECT_LEX();
- schema_select_lex->db= lex->select_lex.db;
- schema_select_lex->table_list.first= NULL;
- remove_escape(schema_select_lex->db); // Fix escaped '_'
+ LEX_STRING db;
+ size_t dummy;
+ if (lex->select_lex.db == NULL &&
+ lex->copy_db_to(&lex->select_lex.db, &dummy))
+ {
+ DBUG_RETURN(1);
+ }
+ schema_select_lex= new SELECT_LEX();
+ db.str= schema_select_lex->db= lex->select_lex.db;
+ schema_select_lex->table_list.first= NULL;
+ db.length= strlen(db.str);
- if (check_db_name(schema_select_lex->db))
- {
- my_error(ER_WRONG_DB_NAME, MYF(0), schema_select_lex->db);
- DBUG_RETURN(1);
+ if (check_db_name(&db))
+ {
+ my_error(ER_WRONG_DB_NAME, MYF(0), db.str);
+ DBUG_RETURN(1);
+ }
+ break;
}
-
-
- break;
#endif
case SCH_COLUMNS:
case SCH_STATISTICS:
+ {
#ifdef DONT_ALLOW_SHOW_COMMANDS
my_message(ER_NOT_ALLOWED_COMMAND,
ER(ER_NOT_ALLOWED_COMMAND), MYF(0)); /* purecov: inspected */
DBUG_RETURN(1);
#else
- {
- DBUG_ASSERT(table_ident);
-
- TABLE_LIST **query_tables_last= lex->query_tables_last;
- schema_select_lex= new SELECT_LEX();
- /* '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,
- (List<String> *) 0, (List<String> *) 0))
- DBUG_RETURN(1);
- lex->query_tables_last= query_tables_last;
-
- TABLE_LIST *dst_table= (TABLE_LIST*) schema_select_lex->table_list.first;
- remove_escape(dst_table->db); // Fix escaped '_'
- remove_escape(dst_table->table_name);
-
- break;
- }
+ DBUG_ASSERT(table_ident);
+ TABLE_LIST **query_tables_last= lex->query_tables_last;
+ schema_select_lex= new SELECT_LEX();
+ /* '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))
+ DBUG_RETURN(1);
+ lex->query_tables_last= query_tables_last;
+ break;
+ }
#endif
+ case SCH_PROFILES:
+ /*
+ Mark this current profiling record to be discarded. We don't
+ wish to have SHOW commands show up in profiling.
+ */
+#if defined(ENABLED_PROFILING) && defined(COMMUNITY_SERVER)
+ thd->profiling.discard_current_query();
+#endif
+ break;
case SCH_OPEN_TABLES:
case SCH_VARIABLES:
case SCH_STATUS:
case SCH_PROCEDURES:
case SCH_CHARSETS:
+ case SCH_ENGINES:
case SCH_COLLATIONS:
case SCH_COLLATION_CHARACTER_SET_APPLICABILITY:
case SCH_USER_PRIVILEGES:
@@ -2474,31 +1772,28 @@ int prepare_schema_table(THD *thd, LEX *lex, Table_ident *table_ident,
TABLE_LIST *table_list= (TABLE_LIST*) select_lex->table_list.first;
table_list->schema_select_lex= schema_select_lex;
table_list->schema_table_reformed= 1;
- statistic_increment(thd->status_var.com_stat[lex->orig_sql_command],
- &LOCK_status);
DBUG_RETURN(0);
}
-/*
- Read query from packet and store in thd->query
- Used in COM_QUERY and COM_STMT_PREPARE
+/**
+ Read query from packet and store in thd->query.
+ Used in COM_QUERY and COM_STMT_PREPARE.
- DESCRIPTION
Sets the following THD variables:
- query
- query_length
+ - query
+ - query_length
- RETURN VALUES
+ @retval
FALSE ok
+ @retval
TRUE error; In this case thd->fatal_error is set
*/
bool alloc_query(THD *thd, const char *packet, uint packet_length)
{
- packet_length--; // Remove end null
/* Remove garbage at start and end of query */
- while (my_isspace(thd->charset(),packet[0]) && packet_length > 0)
+ while (packet_length > 0 && my_isspace(thd->charset(), packet[0]))
{
packet++;
packet_length--;
@@ -2512,7 +1807,7 @@ bool alloc_query(THD *thd, const char *packet, uint packet_length)
}
/* We must allocate some extra memory for query cache */
thd->query_length= 0; // Extra safety: Avoid races
- if (!(thd->query= (char*) thd->memdup_w_gap((gptr) (packet),
+ if (!(thd->query= (char*) thd->memdup_w_gap((uchar*) (packet),
packet_length,
thd->db_length+ 1 +
QUERY_CACHE_FLAGS_SIZE)))
@@ -2545,14 +1840,93 @@ static void reset_one_shot_variables(THD *thd)
}
-/*
- Execute command saved in thd and lex->sql_command
+static
+bool sp_process_definer(THD *thd)
+{
+ DBUG_ENTER("sp_process_definer");
- SYNOPSIS
- mysql_execute_command()
- thd Thread handle
+ LEX *lex= thd->lex;
+
+ /*
+ If the definer is not specified, this means that CREATE-statement missed
+ DEFINER-clause. DEFINER-clause can be missed in two cases:
+
+ - The user submitted a statement w/o the clause. This is a normal
+ case, we should assign CURRENT_USER as definer.
+
+ - Our slave received an updated from the master, that does not
+ replicate definer for stored rountines. We should also assign
+ CURRENT_USER as definer here, but also we should mark this routine
+ as NON-SUID. This is essential for the sake of backward
+ compatibility.
+
+ The problem is the slave thread is running under "special" user (@),
+ that actually does not exist. In the older versions we do not fail
+ execution of a stored routine if its definer does not exist and
+ continue the execution under the authorization of the invoker
+ (BUG#13198). And now if we try to switch to slave-current-user (@),
+ we will fail.
+
+ Actually, this leads to the inconsistent state of master and
+ slave (different definers, different SUID behaviour), but it seems,
+ this is the best we can do.
+ */
+
+ if (!lex->definer)
+ {
+ Query_arena original_arena;
+ Query_arena *ps_arena= thd->activate_stmt_arena_if_needed(&original_arena);
+
+ lex->definer= create_default_definer(thd);
+
+ if (ps_arena)
+ thd->restore_active_arena(ps_arena, &original_arena);
+
+ /* Error has been already reported. */
+ if (lex->definer == NULL)
+ DBUG_RETURN(TRUE);
+
+ if (thd->slave_thread && lex->sphead)
+ lex->sphead->m_chistics->suid= SP_IS_NOT_SUID;
+ }
+ else
+ {
+ /*
+ If the specified definer differs from the current user, we
+ should check that the current user has SUPER privilege (in order
+ to create a stored routine under another user one must have
+ SUPER privilege).
+ */
+ if ((strcmp(lex->definer->user.str, thd->security_ctx->priv_user) ||
+ my_strcasecmp(system_charset_info, lex->definer->host.str,
+ thd->security_ctx->priv_host)) &&
+ check_global_access(thd, SUPER_ACL))
+ {
+ my_error(ER_SPECIFIC_ACCESS_DENIED_ERROR, MYF(0), "SUPER");
+ DBUG_RETURN(TRUE);
+ }
+ }
+
+ /* Check that the specified definer exists. Emit a warning if not. */
+
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+ if (!is_acl_user(lex->definer->host.str, lex->definer->user.str))
+ {
+ push_warning_printf(thd,
+ MYSQL_ERROR::WARN_LEVEL_NOTE,
+ ER_NO_SUCH_USER,
+ ER(ER_NO_SUCH_USER),
+ lex->definer->user.str,
+ lex->definer->host.str);
+ }
+#endif /* NO_EMBEDDED_ACCESS_CHECKS */
- IMPLEMENTATION
+ DBUG_RETURN(FALSE);
+}
+
+
+/**
+ Execute command saved in thd and lex->sql_command.
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
@@ -2564,8 +1938,20 @@ static void reset_one_shot_variables(THD *thd)
global read lock when it succeeds. This needs to be released by
start_waiting_global_read_lock() after the operation.
- RETURN
+ @param thd Thread handle
+
+ @todo
+ - Invalidate the table in the query cache if something changed
+ after unlocking when changes become visible.
+ 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
+ @retval
TRUE Error
*/
@@ -2584,23 +1970,15 @@ mysql_execute_command(THD *thd)
TABLE_LIST *all_tables;
/* most outer SELECT_LEX_UNIT of query */
SELECT_LEX_UNIT *unit= &lex->unit;
+#ifdef HAVE_REPLICATION
+ /* 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");
- thd->net.no_send_error= 0;
-
- /*
- Remember first generated insert id value of the previous
- statement. We remember it here at the beginning of the statement,
- and also in Item_func_last_insert_id::fix_fields() and
- sys_var_last_insert_id::value_ptr(). Last two places are required
- because LAST_INSERT_ID() and @@LAST_INSERT_ID may also be used in
- expression that is not executed with mysql_execute_command().
-
- And we remember it here because some statements read
- @@LAST_INSERT_ID indirectly, like "SELECT * FROM t1 WHERE id IS
- NULL", that may replace "id IS NULL" with "id = <LAST_INSERT_ID>".
- */
- thd->current_insert_id= thd->last_insert_id;
+#ifdef WITH_PARTITION_STORAGE_ENGINE
+ thd->work_part_info= 0;
+#endif
/*
In many cases first table of main SELECT_LEX have special meaning =>
@@ -2632,9 +2010,7 @@ mysql_execute_command(THD *thd)
variables, but for now this is probably good enough.
Don't reset warnings when executing a stored routine.
*/
- if ((all_tables || &lex->select_lex != lex->all_selects_list ||
- lex->sroutines.records) && !thd->spcont ||
- lex->time_zone_tables_used)
+ if ((all_tables || !lex->is_single_level_stmt()) && !thd->spcont)
mysql_reset_errors(thd, 0);
#ifdef HAVE_REPLICATION
@@ -2663,6 +2039,48 @@ mysql_execute_command(THD *thd)
// force searching in slave.cc:tables_ok()
all_tables->updating= 1;
}
+
+ /*
+ For fix of BUG#37051, the master stores the table map for update
+ in the Query_log_event, and the value is assigned to
+ thd->variables.table_map_for_update before executing the update
+ query.
+
+ If thd->variables.table_map_for_update is set, then we are
+ replicating from a new master, we can use this value to apply
+ filter rules without opening all the tables. However If
+ thd->variables.table_map_for_update is not set, then we are
+ replicating from an old master, so we just skip this and
+ continue with the old method. And of course, the bug would still
+ exist for old masters.
+ */
+ if (lex->sql_command == SQLCOM_UPDATE_MULTI &&
+ thd->table_map_for_update)
+ {
+ have_table_map_for_update= TRUE;
+ table_map table_map_for_update= thd->table_map_for_update;
+ uint nr= 0;
+ TABLE_LIST *table;
+ for (table=all_tables; table; table=table->next_global, nr++)
+ {
+ if (table_map_for_update & ((table_map)1 << nr))
+ table->updating= TRUE;
+ else
+ table->updating= FALSE;
+ }
+
+ if (all_tables_not_ok(thd, all_tables))
+ {
+ /* we warn the slave SQL thread */
+ my_message(ER_SLAVE_IGNORED_TABLE, ER(ER_SLAVE_IGNORED_TABLE), MYF(0));
+ if (thd->one_shot_set)
+ reset_one_shot_variables(thd);
+ DBUG_RETURN(0);
+ }
+
+ for (table=all_tables; table; table=table->next_global)
+ table->updating= TRUE;
+ }
/*
Check if statment should be skipped because of slave filtering
@@ -2720,80 +2138,71 @@ mysql_execute_command(THD *thd)
#ifdef HAVE_REPLICATION
} /* endif unlikely slave */
#endif
- if(lex->orig_sql_command == SQLCOM_END)
- statistic_increment(thd->status_var.com_stat[lex->sql_command],
- &LOCK_status);
+ status_var_increment(thd->status_var.com_stat[lex->sql_command]);
DBUG_ASSERT(thd->transaction.stmt.modified_non_trans_table == FALSE);
switch (lex->sql_command) {
- case SQLCOM_SELECT:
- {
- /* assign global limit variable if limit is not given */
- {
- SELECT_LEX *param= lex->unit.global_parameters;
- if (!param->explicit_limit)
- param->select_limit=
- new Item_int((ulonglong)thd->variables.select_limit);
- }
- select_result *sel_result=lex->result;
+ case SQLCOM_SHOW_EVENTS:
+#ifndef HAVE_EVENT_SCHEDULER
+ my_error(ER_NOT_SUPPORTED_YET, MYF(0), "embedded server");
+ break;
+#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);
+ break;
+ case SQLCOM_SHOW_STATUS:
+ {
+ 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)))
+ 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 |
+ SERVER_QUERY_NO_GOOD_INDEX_USED);
+ /*
+ restore status variables, as we don't want 'show status' to cause
+ changes
+ */
+ pthread_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);
+ break;
+ }
+ case SQLCOM_SHOW_DATABASES:
+ case SQLCOM_SHOW_TABLES:
+ case SQLCOM_SHOW_TRIGGERS:
+ case SQLCOM_SHOW_TABLE_STATUS:
+ case SQLCOM_SHOW_OPEN_TABLES:
+ case SQLCOM_SHOW_PLUGINS:
+ case SQLCOM_SHOW_FIELDS:
+ case SQLCOM_SHOW_KEYS:
+ case SQLCOM_SHOW_VARIABLES:
+ case SQLCOM_SHOW_CHARSETS:
+ case SQLCOM_SHOW_COLLATIONS:
+ case SQLCOM_SHOW_STORAGE_ENGINES:
+ case SQLCOM_SHOW_PROFILE:
+ case SQLCOM_SELECT:
+ thd->status_var.last_query_cost= 0.0;
if (all_tables)
{
- if (lex->orig_sql_command != SQLCOM_SHOW_STATUS_PROC &&
- lex->orig_sql_command != SQLCOM_SHOW_STATUS_FUNC)
- res= check_table_access(thd,
- lex->exchange ? SELECT_ACL | FILE_ACL :
- SELECT_ACL,
- all_tables, 0);
+ res= check_table_access(thd,
+ lex->exchange ? SELECT_ACL | FILE_ACL :
+ SELECT_ACL,
+ all_tables, UINT_MAX, FALSE);
}
else
res= check_access(thd,
- lex->exchange ? SELECT_ACL | FILE_ACL : SELECT_ACL,
- any_db, 0, 0, 0, 0);
- if (res)
- goto error;
-
- if (!(res= open_and_lock_tables(thd, all_tables)))
- {
- if (lex->describe)
- {
- /*
- We always use select_send for EXPLAIN, even if it's an EXPLAIN
- for SELECT ... INTO OUTFILE: a user application should be able
- to prepend EXPLAIN to any query and receive output for it,
- even if the query itself redirects the output.
- */
- if (!(sel_result= new select_send()))
- goto error;
- else
- thd->send_explain_fields(sel_result);
- res= mysql_explain_union(thd, &thd->lex->unit, sel_result);
- if (lex->describe & DESCRIBE_EXTENDED)
- {
- char buff[1024];
- String str(buff,(uint32) sizeof(buff), system_charset_info);
- str.length(0);
- thd->lex->unit.print(&str);
- str.append('\0');
- push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
- ER_YES, str.ptr());
- }
- sel_result->send_eof();
- delete sel_result;
- }
- else
- {
- if (!sel_result && !(sel_result= new select_send()))
- goto error;
- query_cache_store_query(thd, all_tables);
- res= handle_select(thd, lex, sel_result, 0);
- if (sel_result != lex->result)
- delete sel_result;
- }
- }
+ lex->exchange ? SELECT_ACL | FILE_ACL : SELECT_ACL,
+ any_db, 0, 0, 0, 0);
+ if (!res)
+ res= execute_sqlcom_select(thd, all_tables);
break;
- }
case SQLCOM_PREPARE:
{
mysql_sql_stmt_prepare(thd);
@@ -2810,7 +2219,7 @@ mysql_execute_command(THD *thd)
break;
}
case SQLCOM_DO:
- if (check_table_access(thd, SELECT_ACL, all_tables, 0) ||
+ if (check_table_access(thd, SELECT_ACL, all_tables, UINT_MAX, FALSE) ||
open_and_lock_tables(thd, all_tables))
goto error;
@@ -2818,7 +2227,7 @@ mysql_execute_command(THD *thd)
break;
case SQLCOM_EMPTY_QUERY:
- send_ok(thd);
+ my_ok(thd);
break;
case SQLCOM_HELP:
@@ -2873,6 +2282,19 @@ mysql_execute_command(THD *thd)
(1L << (uint) MYSQL_ERROR::WARN_LEVEL_ERROR));
break;
}
+ case SQLCOM_SHOW_PROFILES:
+ {
+#if defined(ENABLED_PROFILING) && defined(COMMUNITY_SERVER)
+ thd->profiling.discard_current_query();
+ res= thd->profiling.show_profiles();
+ if (res)
+ goto error;
+#else
+ 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))
@@ -2907,34 +2329,31 @@ mysql_execute_command(THD *thd)
case SQLCOM_BACKUP_TABLE:
{
DBUG_ASSERT(first_table == all_tables && first_table != 0);
- if (check_db_used(thd, all_tables) ||
- check_table_access(thd, SELECT_ACL, all_tables, 0) ||
+ if (check_table_access(thd, SELECT_ACL, all_tables, UINT_MAX, FALSE) ||
check_global_access(thd, FILE_ACL))
goto error; /* purecov: inspected */
thd->enable_slow_log= opt_log_slow_admin_statements;
res = mysql_backup_table(thd, first_table);
- select_lex->table_list.first= (byte*) first_table;
+ select_lex->table_list.first= (uchar*) first_table;
lex->query_tables=all_tables;
break;
}
case SQLCOM_RESTORE_TABLE:
{
DBUG_ASSERT(first_table == all_tables && first_table != 0);
- if (check_db_used(thd, all_tables) ||
- check_table_access(thd, INSERT_ACL, all_tables, 0) ||
+ if (check_table_access(thd, INSERT_ACL, all_tables, UINT_MAX, FALSE) ||
check_global_access(thd, FILE_ACL))
goto error; /* purecov: inspected */
thd->enable_slow_log= opt_log_slow_admin_statements;
res = mysql_restore_table(thd, first_table);
- select_lex->table_list.first= (byte*) first_table;
+ select_lex->table_list.first= (uchar*) first_table;
lex->query_tables=all_tables;
break;
}
case SQLCOM_ASSIGN_TO_KEYCACHE:
{
DBUG_ASSERT(first_table == all_tables && first_table != 0);
- if (check_db_used(thd, all_tables) ||
- check_access(thd, INDEX_ACL, first_table->db,
+ if (check_access(thd, INDEX_ACL, first_table->db,
&first_table->grant.privilege, 0, 0,
test(first_table->schema_table)))
goto error;
@@ -2944,8 +2363,7 @@ mysql_execute_command(THD *thd)
case SQLCOM_PRELOAD_KEYS:
{
DBUG_ASSERT(first_table == all_tables && first_table != 0);
- if (check_db_used(thd, all_tables) ||
- check_access(thd, INDEX_ACL, first_table->db,
+ if (check_access(thd, INDEX_ACL, first_table->db,
&first_table->grant.privilege, 0, 0,
test(first_table->schema_table)))
goto error;
@@ -2974,9 +2392,9 @@ mysql_execute_command(THD *thd)
}
else
{
- push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, 0,
- "the master info structure does not exist");
- send_ok(thd);
+ push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ WARN_NO_MASTER_INFO, ER(WARN_NO_MASTER_INFO));
+ my_ok(thd);
}
pthread_mutex_unlock(&LOCK_active_mi);
break;
@@ -2995,33 +2413,23 @@ mysql_execute_command(THD *thd)
goto error;
if (end_active_trans(thd))
goto error;
- else
- res = load_master_data(thd);
+ res = load_master_data(thd);
break;
#endif /* HAVE_REPLICATION */
-#ifdef HAVE_NDBCLUSTER_DB
- case SQLCOM_SHOW_NDBCLUSTER_STATUS:
- {
- res = ndbcluster_show_status(thd);
- break;
- }
-#endif
-#ifdef HAVE_INNOBASE_DB
- case SQLCOM_SHOW_INNODB_STATUS:
+ case SQLCOM_SHOW_ENGINE_STATUS:
{
- if (check_global_access(thd, SUPER_ACL))
- goto error;
- res = innodb_show_status(thd);
+ if (check_global_access(thd, PROCESS_ACL))
+ goto error;
+ res = ha_show_status(thd, lex->create_info.db_type, HA_ENGINE_STATUS);
break;
}
- case SQLCOM_SHOW_MUTEX_STATUS:
+ case SQLCOM_SHOW_ENGINE_MUTEX:
{
- if (check_global_access(thd, SUPER_ACL))
+ if (check_global_access(thd, PROCESS_ACL))
goto error;
- res = innodb_mutex_show_status(thd);
+ res = ha_show_status(thd, lex->create_info.db_type, HA_ENGINE_MUTEX);
break;
}
-#endif
#ifdef HAVE_REPLICATION
case SQLCOM_LOAD_MASTER_TABLE:
{
@@ -3032,17 +2440,10 @@ mysql_execute_command(THD *thd)
&first_table->grant.privilege, 0, 0,
test(first_table->schema_table)))
goto error; /* purecov: inspected */
- if (grant_option)
- {
- /* Check that the first table has CREATE privilege */
- if (check_grant(thd, CREATE_ACL, all_tables, 0, 1, 0))
- goto error;
- }
- if (strlen(first_table->table_name) > NAME_LEN)
- {
- my_error(ER_WRONG_TABLE_NAME, MYF(0), first_table->table_name);
- break;
- }
+ /* 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.
@@ -3051,7 +2452,7 @@ mysql_execute_command(THD *thd)
if (!fetch_master_table(thd, first_table->db, first_table->table_name,
active_mi, 0, 0))
{
- send_ok(thd);
+ my_ok(thd);
}
pthread_mutex_unlock(&LOCK_active_mi);
break;
@@ -3069,11 +2470,6 @@ mysql_execute_command(THD *thd)
break;
}
}
- else
- {
- /* So that CREATE TEMPORARY TABLE gets to binlog at commit/rollback */
- thd->transaction.all.modified_non_trans_table= TRUE;
- }
DBUG_ASSERT(first_table == all_tables && first_table != 0);
bool link_to_local;
// Skip first table, which is the table we are creating
@@ -3087,11 +2483,16 @@ mysql_execute_command(THD *thd)
referenced from this structure.
*/
HA_CREATE_INFO create_info(lex->create_info);
+ /*
+ We need to copy alter_info for the same reasons of re-execution
+ safety, only in case of Alter_info we have to do (almost) a deep
+ copy.
+ */
Alter_info alter_info(lex->alter_info, thd->mem_root);
if (thd->is_fatal_error)
{
- /* out of memory when creating a copy of alter_info */
+ /* If out of memory when creating a copy of alter_info. */
res= 1;
goto end_with_restore_list;
}
@@ -3099,31 +2500,10 @@ mysql_execute_command(THD *thd)
if ((res= create_table_precheck(thd, select_tables, create_table)))
goto end_with_restore_list;
+ /* Might have been updated in create_table_precheck */
create_info.alias= create_table->alias;
-#ifndef HAVE_READLINK
- if (create_info.data_file_name)
- push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, 0,
- "DATA DIRECTORY option ignored");
- if (create_info.index_file_name)
- push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, 0,
- "INDEX DIRECTORY option ignored");
- create_info.data_file_name= create_info.index_file_name= NULL;
-#else
-
- if (test_if_data_home_dir(lex->create_info.data_file_name))
- {
- my_error(ER_WRONG_ARGUMENTS,MYF(0),"DATA DIRECTORY");
- res= -1;
- break;
- }
- if (test_if_data_home_dir(lex->create_info.index_file_name))
- {
- my_error(ER_WRONG_ARGUMENTS,MYF(0),"INDEX DIRECTORY");
- res= -1;
- break;
- }
-
+#ifdef HAVE_READLINK
/* Fix names if symlinked tables */
if (append_file_to_dir(thd, &create_info.data_file_name,
create_table->table_name) ||
@@ -3163,14 +2543,38 @@ mysql_execute_command(THD *thd)
res= 1;
goto end_with_restore_list;
}
+#ifdef WITH_PARTITION_STORAGE_ENGINE
+ {
+ partition_info *part_info= thd->lex->part_info;
+ if (part_info && !(part_info= thd->lex->part_info->get_clone()))
+ {
+ res= -1;
+ goto end_with_restore_list;
+ }
+ thd->work_part_info= part_info;
+ }
+#endif
if (select_lex->item_list.elements) // With select
{
- select_result *sel_result;
+ select_result *result;
select_lex->options|= SELECT_NO_UNLOCK;
unit->set_limit(select_lex);
- if (!(lex->create_info.options & HA_LEX_CREATE_TMP_TABLE))
+ /*
+ Disable non-empty MERGE tables with CREATE...SELECT. Too
+ complicated. See Bug #26379. Empty MERGE tables are read-only
+ and don't allow CREATE...SELECT anyway.
+ */
+ if (create_info.used_fields & HA_CREATE_USED_UNION)
+ {
+ my_error(ER_WRONG_OBJECT, MYF(0), create_table->db,
+ create_table->table_name, "BASE TABLE");
+ res= 1;
+ goto end_with_restore_list;
+ }
+
+ if (!(create_info.options & HA_LEX_CREATE_TMP_TABLE))
{
lex->link_first_table_back(create_table, link_to_local);
create_table->create= TRUE;
@@ -3210,33 +2614,38 @@ mysql_execute_command(THD *thd)
}
}
}
+
/*
select_create is currently not re-execution friendly and
needs to be created for every execution of a PS/SP.
*/
- if ((sel_result= new select_create(create_table,
- &create_info,
- &alter_info,
- select_lex->item_list,
- lex->duplicates,
- lex->ignore)))
+ if ((result= new select_create(create_table,
+ &create_info,
+ &alter_info,
+ select_lex->item_list,
+ lex->duplicates,
+ lex->ignore,
+ select_tables)))
{
/*
CREATE from SELECT give its SELECT_LEX for SELECT,
and item_list belong to SELECT
*/
- res= handle_select(thd, lex, sel_result, 0);
- delete sel_result;
+ res= handle_select(thd, lex, result, 0);
+ delete result;
}
}
- else if (!(lex->create_info.options & HA_LEX_CREATE_TMP_TABLE))
+ else if (!(create_info.options & HA_LEX_CREATE_TMP_TABLE))
create_table= lex->unlink_first_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;
/* regular create */
- if (lex->create_info.options & HA_LEX_CREATE_TABLE_LIKE)
+ if (create_info.options & HA_LEX_CREATE_TABLE_LIKE)
res= mysql_create_like_table(thd, create_table, select_tables,
&create_info);
else
@@ -3246,7 +2655,7 @@ mysql_execute_command(THD *thd)
&alter_info, 0, 0);
}
if (!res)
- send_ok(thd);
+ my_ok(thd);
}
/* put tables back for PS rexecuting */
@@ -3259,25 +2668,22 @@ end_with_restore_list:
case SQLCOM_DROP_INDEX:
/*
CREATE INDEX and DROP INDEX are implemented by calling ALTER
- TABLE with proper arguments. This isn't very fast but it
- should work for most cases.
-
- In the future ALTER TABLE will notice that only added
- indexes and create these one by one for the existing table
- without having to do a full rebuild.
+ TABLE with proper arguments.
- One should normally create all indexes with CREATE TABLE or
- ALTER TABLE.
+ In the future ALTER TABLE will notice that the request is to
+ only add indexes and create these one by one for the existing
+ table without having to do a full rebuild.
*/
{
- Alter_info alter_info(lex->alter_info, thd->mem_root);
+ /* Prepare stack copies to be re-execution safe */
HA_CREATE_INFO create_info;
+ Alter_info alter_info(lex->alter_info, thd->mem_root);
- if (thd->is_fatal_error) /* out of memory creating a copy of alter_info*/
+ if (thd->is_fatal_error) /* out of memory creating a copy of alter_info */
goto error;
DBUG_ASSERT(first_table == all_tables && first_table != 0);
- if (check_one_table_access(thd, INDEX_ACL, first_table))
+ if (check_one_table_access(thd, INDEX_ACL, all_tables))
goto error; /* purecov: inspected */
if (end_active_trans(thd))
goto error;
@@ -3289,7 +2695,7 @@ end_with_restore_list:
thd->enable_slow_log= opt_log_slow_admin_statements;
bzero((char*) &create_info, sizeof(create_info));
- create_info.db_type= DB_TYPE_DEFAULT;
+ create_info.db_type= 0;
create_info.row_type= ROW_TYPE_NOT_USED;
create_info.default_table_charset= thd->variables.collation_database;
@@ -3338,6 +2744,7 @@ end_with_restore_list:
DBUG_ASSERT(first_table == all_tables && first_table != 0);
{
ulong priv=0;
+ ulong priv_needed= ALTER_ACL;
/*
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
@@ -3349,14 +2756,16 @@ end_with_restore_list:
if (thd->is_fatal_error) /* out of memory creating a copy of alter_info */
goto error;
- if (lex->name && (!lex->name[0] || strlen(lex->name) > NAME_LEN))
- {
- my_error(ER_WRONG_TABLE_NAME, MYF(0), lex->name);
- 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, ALTER_ACL, first_table->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,
@@ -3365,59 +2774,55 @@ end_with_restore_list:
(TABLE_LIST *)
create_info.merge_list.first))
goto error; /* purecov: inspected */
- if (grant_option)
- {
- if (check_grant(thd, ALTER_ACL, all_tables, 0, UINT_MAX, 0))
- goto error;
- if (lex->name && !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;
- 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;
- }
+ 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(thd, MYSQL_ERROR::WARN_LEVEL_WARN, 0,
- "DATA DIRECTORY option ignored");
+ 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(thd, MYSQL_ERROR::WARN_LEVEL_WARN, 0,
- "INDEX DIRECTORY option ignored");
+ 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;
- else
- {
- 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;
- res= mysql_alter_table(thd, select_lex->db, lex->name,
- &create_info,
- first_table,
- &alter_info,
- select_lex->order_list.elements,
- (ORDER *) select_lex->order_list.first,
- lex->ignore);
+ 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;
+ 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);
break;
}
case SQLCOM_RENAME_TABLE:
{
DBUG_ASSERT(first_table == all_tables && first_table != 0);
TABLE_LIST *table;
- if (check_db_used(thd, all_tables))
- goto error;
for (table= first_table; table; table= table->next_local->next_local)
{
if (check_access(thd, ALTER_ACL | DROP_ACL, table->db,
@@ -3426,24 +2831,21 @@ end_with_restore_list:
&table->next_local->grant.privilege, 0, 0,
test(table->next_local->schema_table)))
goto error;
- if (grant_option)
- {
- TABLE_LIST old_list, new_list;
- /*
- we do not need initialize old_list and new_list because we will
- come table[0] and table->next[0] there
- */
- 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)))
- goto error;
- }
+ TABLE_LIST old_list, new_list;
+ /*
+ we do not need initialize old_list and new_list because we will
+ come table[0] and table->next[0] there
+ */
+ 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)))
+ goto error;
}
- query_cache_invalidate3(thd, first_table, 0);
- if (end_active_trans(thd) || mysql_rename_tables(thd, first_table))
+
+ if (end_active_trans(thd) || mysql_rename_tables(thd, first_table, 0))
goto error;
break;
}
@@ -3473,9 +2875,7 @@ end_with_restore_list:
/* Ignore temporary tables if this is "SHOW CREATE VIEW" */
if (lex->only_view)
first_table->skip_temporary= 1;
-
- if (check_db_used(thd, all_tables) ||
- check_show_create_table_access(thd, first_table))
+ if (check_show_create_table_access(thd, first_table))
goto error;
res= mysqld_show_create(thd, first_table);
break;
@@ -3484,8 +2884,8 @@ end_with_restore_list:
case SQLCOM_CHECKSUM:
{
DBUG_ASSERT(first_table == all_tables && first_table != 0);
- if (check_db_used(thd, all_tables) ||
- check_table_access(thd, SELECT_ACL | EXTRA_ACL, all_tables, 0))
+ if (check_table_access(thd, SELECT_ACL | EXTRA_ACL, all_tables,
+ UINT_MAX, FALSE))
goto error; /* purecov: inspected */
res = mysql_checksum_table(thd, first_table, &lex->check_opt);
break;
@@ -3493,58 +2893,52 @@ end_with_restore_list:
case SQLCOM_REPAIR:
{
DBUG_ASSERT(first_table == all_tables && first_table != 0);
- if (check_db_used(thd, all_tables) ||
- check_table_access(thd, SELECT_ACL | INSERT_ACL, all_tables, 0))
+ if (check_table_access(thd, SELECT_ACL | INSERT_ACL, all_tables,
+ UINT_MAX, FALSE))
goto error; /* purecov: inspected */
thd->enable_slow_log= opt_log_slow_admin_statements;
res= mysql_repair_table(thd, first_table, &lex->check_opt);
/* ! we write after unlocking the table */
if (!res && !lex->no_write_to_binlog)
{
- /* Presumably, REPAIR and binlog writing doesn't require synchronization */
- if (mysql_bin_log.is_open())
- {
- thd->clear_error(); // No binlog error generated
- Query_log_event qinfo(thd, thd->query, thd->query_length, 0, FALSE);
- mysql_bin_log.write(&qinfo);
- }
+ /*
+ Presumably, REPAIR and binlog writing doesn't require synchronization
+ */
+ write_bin_log(thd, TRUE, thd->query, thd->query_length);
}
- select_lex->table_list.first= (byte*) first_table;
+ select_lex->table_list.first= (uchar*) first_table;
lex->query_tables=all_tables;
break;
}
case SQLCOM_CHECK:
{
DBUG_ASSERT(first_table == all_tables && first_table != 0);
- if (check_db_used(thd, all_tables) ||
- check_table_access(thd, SELECT_ACL | EXTRA_ACL , all_tables, 0))
+ if (check_table_access(thd, SELECT_ACL | EXTRA_ACL , all_tables,
+ UINT_MAX, FALSE))
goto error; /* purecov: inspected */
thd->enable_slow_log= opt_log_slow_admin_statements;
res = mysql_check_table(thd, first_table, &lex->check_opt);
- select_lex->table_list.first= (byte*) first_table;
+ select_lex->table_list.first= (uchar*) first_table;
lex->query_tables=all_tables;
break;
}
case SQLCOM_ANALYZE:
{
DBUG_ASSERT(first_table == all_tables && first_table != 0);
- if (check_db_used(thd, all_tables) ||
- check_table_access(thd, SELECT_ACL | INSERT_ACL, all_tables, 0))
+ if (check_table_access(thd, SELECT_ACL | INSERT_ACL, all_tables,
+ UINT_MAX, FALSE))
goto error; /* purecov: inspected */
thd->enable_slow_log= opt_log_slow_admin_statements;
- res = mysql_analyze_table(thd, first_table, &lex->check_opt);
+ 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 */
- if (mysql_bin_log.is_open())
- {
- thd->clear_error(); // No binlog error generated
- Query_log_event qinfo(thd, thd->query, thd->query_length, 0, FALSE);
- mysql_bin_log.write(&qinfo);
- }
+ /*
+ Presumably, ANALYZE and binlog writing doesn't require synchronization
+ */
+ write_bin_log(thd, TRUE, thd->query, thd->query_length);
}
- select_lex->table_list.first= (byte*) first_table;
+ select_lex->table_list.first= (uchar*) first_table;
lex->query_tables=all_tables;
break;
}
@@ -3552,8 +2946,8 @@ end_with_restore_list:
case SQLCOM_OPTIMIZE:
{
DBUG_ASSERT(first_table == all_tables && first_table != 0);
- if (check_db_used(thd, all_tables) ||
- check_table_access(thd, SELECT_ACL | INSERT_ACL, all_tables, 0))
+ if (check_table_access(thd, SELECT_ACL | INSERT_ACL, all_tables,
+ UINT_MAX, FALSE))
goto error; /* purecov: inspected */
thd->enable_slow_log= opt_log_slow_admin_statements;
res= (specialflag & (SPECIAL_SAFE_MODE | SPECIAL_NO_NEW_FUNC)) ?
@@ -3562,15 +2956,12 @@ end_with_restore_list:
/* ! we write after unlocking the table */
if (!res && !lex->no_write_to_binlog)
{
- /* Presumably, OPTIMIZE and binlog writing doesn't require synchronization */
- if (mysql_bin_log.is_open())
- {
- thd->clear_error(); // No binlog error generated
- Query_log_event qinfo(thd, thd->query, thd->query_length, 0, FALSE);
- mysql_bin_log.write(&qinfo);
- }
+ /*
+ Presumably, OPTIMIZE and binlog writing doesn't require synchronization
+ */
+ write_bin_log(thd, TRUE, thd->query, thd->query_length);
}
- select_lex->table_list.first= (byte*) first_table;
+ select_lex->table_list.first= (uchar*) first_table;
lex->query_tables=all_tables;
break;
}
@@ -3608,7 +2999,7 @@ end_with_restore_list:
#ifdef HAVE_REPLICATION
/* Check slave filtering rules */
- if (unlikely(thd->slave_thread))
+ if (unlikely(thd->slave_thread && !have_table_map_for_update))
{
if (all_tables_not_ok(thd, all_tables))
{
@@ -3649,6 +3040,36 @@ end_with_restore_list:
break;
}
case SQLCOM_REPLACE:
+#ifndef DBUG_OFF
+ if (mysql_bin_log.is_open())
+ {
+ /*
+ Generate an incident log event before writing the real event
+ to the binary log. We put this event is before the statement
+ since that makes it simpler to check that the statement was
+ not executed on the slave (since incidents usually stop the
+ slave).
+
+ Observe that any row events that are generated will be
+ generated before.
+
+ This is only for testing purposes and will not be present in a
+ release build.
+ */
+
+ Incident incident= INCIDENT_NONE;
+ DBUG_PRINT("debug", ("Just before generate_incident()"));
+ DBUG_EXECUTE_IF("incident_database_resync_on_replace",
+ incident= INCIDENT_LOST_EVENTS;);
+ if (incident)
+ {
+ Incident_log_event ev(thd, incident);
+ mysql_bin_log.write(&ev);
+ mysql_bin_log.rotate_and_purge(RP_FORCE_ROTATE);
+ }
+ DBUG_PRINT("debug", ("Just after generate_incident()"));
+ }
+#endif
case SQLCOM_INSERT:
{
DBUG_ASSERT(first_table == all_tables && first_table != 0);
@@ -3673,7 +3094,8 @@ end_with_restore_list:
had before the statement.
*/
if (first_table->view && !first_table->contain_auto_increment)
- thd->last_insert_id= thd->current_insert_id;
+ thd->first_successful_insert_id_in_cur_stmt=
+ thd->first_successful_insert_id_in_prev_stmt;
break;
}
@@ -3705,7 +3127,7 @@ end_with_restore_list:
{
/* Skip first table, which is the table we are inserting in */
TABLE_LIST *second_table= first_table->next_local;
- select_lex->table_list.first= (byte*) second_table;
+ select_lex->table_list.first= (uchar*) second_table;
select_lex->context.table_list=
select_lex->context.first_name_resolution_table= second_table;
res= mysql_insert_select_prepare(thd);
@@ -3730,15 +3152,13 @@ end_with_restore_list:
/* INSERT ... SELECT should invalidate only the very first table */
TABLE_LIST *save_table= first_table->next_local;
first_table->next_local= 0;
- mysql_unlock_tables(thd, thd->lock);
query_cache_invalidate3(thd, first_table, 1);
first_table->next_local= save_table;
- thd->lock=0;
}
delete sel_result;
}
/* revert changes for SP */
- select_lex->table_list.first= (byte*) first_table;
+ select_lex->table_list.first= (uchar*) first_table;
}
/*
@@ -3748,7 +3168,8 @@ end_with_restore_list:
had before the statement.
*/
if (first_table->view && !first_table->contain_auto_increment)
- thd->last_insert_id= thd->current_insert_id;
+ thd->first_successful_insert_id_in_cur_stmt=
+ thd->first_successful_insert_id_in_prev_stmt;
break;
}
@@ -3759,7 +3180,7 @@ end_with_restore_list:
break;
}
DBUG_ASSERT(first_table == all_tables && first_table != 0);
- if (check_one_table_access(thd, DELETE_ACL, all_tables))
+ if (check_one_table_access(thd, DROP_ACL, all_tables))
goto error;
/*
Don't allow this within a transaction because we want to use
@@ -3818,7 +3239,7 @@ end_with_restore_list:
if (add_item_to_list(thd, new Item_null()))
goto error;
- thd->proc_info="init";
+ thd_proc_info(thd, "init");
if ((res= open_and_lock_tables(thd, all_tables)))
break;
@@ -3839,13 +3260,9 @@ end_with_restore_list:
SELECT_NO_JOIN_CACHE | SELECT_NO_UNLOCK |
OPTION_SETUP_TABLES_DONE,
del_result, unit, select_lex);
- res|= thd->net.report_error;
- if (unlikely(res))
- {
- /* If we had a another error reported earlier then this will be ignored */
- del_result->send_error(ER_UNKNOWN_ERROR, "Execution of the query failed");
+ res|= thd->is_error();
+ if (res)
del_result->abort();
- }
delete del_result;
}
else
@@ -3857,7 +3274,7 @@ end_with_restore_list:
DBUG_ASSERT(first_table == all_tables && first_table != 0);
if (!lex->drop_temporary)
{
- if (check_table_access(thd, DROP_ACL, all_tables, 0))
+ if (check_table_access(thd, DROP_ACL, all_tables, UINT_MAX, FALSE))
goto error; /* purecov: inspected */
if (end_active_trans(thd))
goto error;
@@ -3876,7 +3293,7 @@ end_with_restore_list:
lex->drop_if_exists= 1;
/* So that DROP TEMPORARY TABLE gets to binlog at commit/rollback */
- thd->transaction.all.modified_non_trans_table= TRUE;
+ thd->options|= OPTION_KEEP_LOG;
}
/* DDL and binlog write order protected by LOCK_open */
res= mysql_rm_table(thd, first_table, lex->drop_if_exists,
@@ -3893,8 +3310,11 @@ end_with_restore_list:
thd->security_ctx->priv_user),
lex->verbose);
break;
- case SQLCOM_SHOW_STORAGE_ENGINES:
- res= mysqld_show_storage_engines(thd);
+ case SQLCOM_SHOW_AUTHORS:
+ res= mysqld_show_authors(thd);
+ break;
+ case SQLCOM_SHOW_CONTRIBUTORS:
+ res= mysqld_show_contributors(thd);
break;
case SQLCOM_SHOW_PRIVILEGES:
res= mysqld_show_privileges(thd);
@@ -3902,25 +3322,25 @@ end_with_restore_list:
case SQLCOM_SHOW_COLUMN_TYPES:
res= mysqld_show_column_types(thd);
break;
- case SQLCOM_SHOW_LOGS:
+ case SQLCOM_SHOW_ENGINE_LOGS:
#ifdef DONT_ALLOW_SHOW_COMMANDS
my_message(ER_NOT_ALLOWED_COMMAND, ER(ER_NOT_ALLOWED_COMMAND),
MYF(0)); /* purecov: inspected */
goto error;
#else
{
- if (grant_option && check_access(thd, FILE_ACL, any_db,0,0,0,0))
+ if (check_access(thd, FILE_ACL, any_db,0,0,0,0))
goto error;
- res= mysqld_show_logs(thd);
+ res= ha_show_status(thd, lex->create_info.db_type, HA_ENGINE_LOGS);
break;
}
#endif
case SQLCOM_CHANGE_DB:
{
- LEX_STRING db_str= { (char *) select_lex->db, (uint) strlen(select_lex->db) };
+ LEX_STRING db_str= { (char *) select_lex->db, strlen(select_lex->db) };
if (!mysql_change_db(thd, &db_str, FALSE))
- send_ok(thd);
+ my_ok(thd);
break;
}
@@ -3954,7 +3374,11 @@ end_with_restore_list:
case SQLCOM_SET_OPTION:
{
List<set_var_base> *lex_var_list= &lex->var_list;
- if ((check_table_access(thd, SELECT_ACL, all_tables, 0) ||
+
+ 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))
@@ -3969,8 +3393,20 @@ end_with_restore_list:
about the ONE_SHOT property of that SET. So we use a |= instead of = .
*/
thd->one_shot_set|= lex->one_shot_set;
- send_ok(thd);
+ my_ok(thd);
+ }
+ else
+ {
+ /*
+ We encountered some sort of error, but no message was sent.
+ Send something semi-generic here since we don't know which
+ assignment in the list caused the error.
+ */
+ if (!thd->is_error())
+ my_error(ER_WRONG_ARGUMENTS,MYF(0),"SET");
+ goto error;
}
+
break;
}
@@ -3985,20 +3421,19 @@ end_with_restore_list:
if (thd->options & OPTION_TABLE_LOCK)
{
end_active_trans(thd);
- thd->options&= ~(ulong) (OPTION_TABLE_LOCK);
+ thd->options&= ~(OPTION_TABLE_LOCK);
}
if (thd->global_read_lock)
unlock_global_read_lock(thd);
- send_ok(thd);
+ my_ok(thd);
break;
case SQLCOM_LOCK_TABLES:
unlock_locked_tables(thd);
/* we must end the trasaction first, regardless of anything */
if (end_active_trans(thd))
goto error;
- if (check_db_used(thd, all_tables))
- goto error;
- if (check_table_access(thd, LOCK_TABLES_ACL | SELECT_ACL, all_tables, 0))
+ if (check_table_access(thd, LOCK_TABLES_ACL | SELECT_ACL, all_tables,
+ UINT_MAX, FALSE))
goto error;
thd->in_lock_tables=1;
thd->options|= OPTION_TABLE_LOCK;
@@ -4011,7 +3446,7 @@ end_with_restore_list:
#endif /*HAVE_QUERY_CACHE*/
thd->locked_tables=thd->lock;
thd->lock=0;
- send_ok(thd);
+ my_ok(thd);
}
else
{
@@ -4020,8 +3455,9 @@ end_with_restore_list:
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&= ~(ulong) (OPTION_TABLE_LOCK);
+ thd->options&= ~(OPTION_TABLE_LOCK);
}
thd->in_lock_tables=0;
break;
@@ -4039,9 +3475,10 @@ end_with_restore_list:
break;
}
char *alias;
- if (!(alias=thd->strdup(lex->name)) || check_db_name(lex->name))
+ if (!(alias=thd->strmake(lex->name.str, lex->name.length)) ||
+ check_db_name(&lex->name))
{
- my_error(ER_WRONG_DB_NAME, MYF(0), lex->name);
+ my_error(ER_WRONG_DB_NAME, MYF(0), lex->name.str);
break;
}
/*
@@ -4052,19 +3489,19 @@ end_with_restore_list:
above was not called. So we have to check rules again here.
*/
#ifdef HAVE_REPLICATION
- if (thd->slave_thread &&
- (!db_ok(lex->name, replicate_do_db, replicate_ignore_db) ||
- !db_ok_with_wild_table(lex->name)))
+ if (thd->slave_thread &&
+ (!rpl_filter->db_ok(lex->name.str) ||
+ !rpl_filter->db_ok_with_wild_table(lex->name.str)))
{
my_message(ER_SLAVE_IGNORED_TABLE, ER(ER_SLAVE_IGNORED_TABLE), MYF(0));
break;
}
#endif
-
- if (check_access(thd,CREATE_ACL,lex->name,0,1,0,is_schema_db(lex->name)))
+ if (check_access(thd,CREATE_ACL,lex->name.str, 0, 1, 0,
+ is_schema_db(lex->name.str)))
break;
- res= mysql_create_db(thd,(lower_case_table_names == 2 ? alias : lex->name),
- &create_info, 0);
+ res= mysql_create_db(thd,(lower_case_table_names == 2 ? alias :
+ lex->name.str), &create_info, 0);
break;
}
case SQLCOM_DROP_DB:
@@ -4074,9 +3511,9 @@ end_with_restore_list:
res= -1;
break;
}
- if (check_db_name(lex->name))
+ if (check_db_name(&lex->name))
{
- my_error(ER_WRONG_DB_NAME, MYF(0), lex->name);
+ my_error(ER_WRONG_DB_NAME, MYF(0), lex->name.str);
break;
}
/*
@@ -4088,31 +3525,75 @@ end_with_restore_list:
*/
#ifdef HAVE_REPLICATION
if (thd->slave_thread &&
- (!db_ok(lex->name, replicate_do_db, replicate_ignore_db) ||
- !db_ok_with_wild_table(lex->name)))
+ (!rpl_filter->db_ok(lex->name.str) ||
+ !rpl_filter->db_ok_with_wild_table(lex->name.str)))
+ {
+ my_message(ER_SLAVE_IGNORED_TABLE, ER(ER_SLAVE_IGNORED_TABLE), MYF(0));
+ break;
+ }
+#endif
+ if (check_access(thd,DROP_ACL,lex->name.str,0,1,0,
+ is_schema_db(lex->name.str)))
+ 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) ||
+ !rpl_filter->db_ok_with_wild_table(db->str)))
{
+ res= 1;
my_message(ER_SLAVE_IGNORED_TABLE, ER(ER_SLAVE_IGNORED_TABLE), MYF(0));
break;
}
#endif
- if (check_access(thd,DROP_ACL,lex->name,0,1,0,is_schema_db(lex->name)))
+ if (check_db_name(db))
+ {
+ 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)) ||
+ check_access(thd, DROP_ACL, db->str, 0, 1, 0, is_schema_db(db->str)) ||
+ check_access(thd, CREATE_ACL, db->str, 0, 1, 0, is_schema_db(db->str)))
+ {
+ 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_rm_db(thd, lex->name, lex->drop_if_exists, 0);
+
+ res= mysql_upgrade_db(thd, db);
+ if (!res)
+ my_ok(thd);
break;
}
case SQLCOM_ALTER_DB:
{
- char *db= lex->name;
- DBUG_ASSERT(db); /* Must be set in the parser */
- if (!strip_sp(db) || check_db_name(db))
+ LEX_STRING *db= &lex->name;
+ HA_CREATE_INFO create_info(lex->create_info);
+ if (check_db_name(db))
{
- my_error(ER_WRONG_DB_NAME, MYF(0), lex->name);
+ my_error(ER_WRONG_DB_NAME, MYF(0), db->str);
break;
}
/*
@@ -4124,14 +3605,14 @@ end_with_restore_list:
*/
#ifdef HAVE_REPLICATION
if (thd->slave_thread &&
- (!db_ok(db, replicate_do_db, replicate_ignore_db) ||
- !db_ok_with_wild_table(db)))
+ (!rpl_filter->db_ok(db->str) ||
+ !rpl_filter->db_ok_with_wild_table(db->str)))
{
my_message(ER_SLAVE_IGNORED_TABLE, ER(ER_SLAVE_IGNORED_TABLE), MYF(0));
break;
}
#endif
- if (check_access(thd, ALTER_ACL, db, 0, 1, 0, is_schema_db(db)))
+ if (check_access(thd, ALTER_ACL, db->str, 0, 1, 0, is_schema_db(db->str)))
break;
if (thd->locked_tables || thd->active_transaction())
{
@@ -4139,30 +3620,88 @@ end_with_restore_list:
ER(ER_LOCK_OR_ACTIVE_TRANSACTION), MYF(0));
goto error;
}
- res= mysql_alter_db(thd, db, &lex->create_info);
+ res= mysql_alter_db(thd, db->str, &create_info);
break;
}
case SQLCOM_SHOW_CREATE_DB:
{
DBUG_EXECUTE_IF("4x_server_emul",
my_error(ER_UNKNOWN_ERROR, MYF(0)); goto error;);
- if (!strip_sp(lex->name) || check_db_name(lex->name))
+ if (check_db_name(&lex->name))
{
- my_error(ER_WRONG_DB_NAME, MYF(0), lex->name);
+ my_error(ER_WRONG_DB_NAME, MYF(0), lex->name.str);
break;
}
- if (check_access(thd,SELECT_ACL,lex->name,0,1,0,is_schema_db(lex->name)))
- break;
- res=mysqld_show_create_db(thd,lex->name,&lex->create_info);
+ res= mysqld_show_create_db(thd, lex->name.str, &lex->create_info);
break;
}
+ case SQLCOM_CREATE_EVENT:
+ case SQLCOM_ALTER_EVENT:
+ #ifdef HAVE_EVENT_SCHEDULER
+ do
+ {
+ DBUG_ASSERT(lex->event_parse_data);
+ if (lex->table_or_sp_used())
+ {
+ my_error(ER_NOT_SUPPORTED_YET, MYF(0), "Usage of subqueries or stored "
+ "function calls as part of this statement");
+ break;
+ }
+
+ res= sp_process_definer(thd);
+ if (res)
+ break;
+
+ switch (lex->sql_command) {
+ case SQLCOM_CREATE_EVENT:
+ {
+ bool if_not_exists= (lex->create_info.options &
+ HA_LEX_CREATE_IF_NOT_EXISTS);
+ res= Events::create_event(thd, lex->event_parse_data, if_not_exists);
+ break;
+ }
+ case SQLCOM_ALTER_EVENT:
+ res= Events::update_event(thd, lex->event_parse_data,
+ lex->spname ? &lex->spname->m_db : NULL,
+ lex->spname ? &lex->spname->m_name : NULL);
+ break;
+ default:
+ DBUG_ASSERT(0);
+ }
+ DBUG_PRINT("info",("DDL error code=%d", res));
+ if (!res)
+ my_ok(thd);
+
+ } while (0);
+ /* Don't do it, if we are inside a SP */
+ if (!thd->spcont)
+ {
+ delete lex->sphead;
+ lex->sphead= NULL;
+ }
+ /* lex->unit.cleanup() is called outside, no need to call it here */
+ break;
+ case SQLCOM_SHOW_CREATE_EVENT:
+ res= Events::show_create_event(thd, lex->spname->m_db,
+ lex->spname->m_name);
+ break;
+ case SQLCOM_DROP_EVENT:
+ if (!(res= Events::drop_event(thd,
+ lex->spname->m_db, lex->spname->m_name,
+ lex->drop_if_exists)))
+ my_ok(thd);
+ break;
+#else
+ my_error(ER_NOT_SUPPORTED_YET,MYF(0),"embedded server");
+ break;
+#endif
case SQLCOM_CREATE_FUNCTION: // UDF function
{
if (check_access(thd,INSERT_ACL,"mysql",0,1,0,0))
break;
#ifdef HAVE_DLOPEN
if (!(res = mysql_create_function(thd, &lex->udf)))
- send_ok(thd);
+ my_ok(thd);
#else
my_error(ER_CANT_OPEN_LIBRARY, MYF(0), lex->udf.dl, 0, "feature disabled");
res= TRUE;
@@ -4179,7 +3718,7 @@ end_with_restore_list:
goto error;
/* Conditionally writes to binlog */
if (!(res= mysql_create_user(thd, lex->users_list)))
- send_ok(thd);
+ my_ok(thd);
break;
}
case SQLCOM_DROP_USER:
@@ -4191,7 +3730,7 @@ end_with_restore_list:
goto error;
/* Conditionally writes to binlog */
if (!(res= mysql_drop_user(thd, lex->users_list)))
- send_ok(thd);
+ my_ok(thd);
break;
}
case SQLCOM_RENAME_USER:
@@ -4203,22 +3742,27 @@ end_with_restore_list:
goto error;
/* Conditionally writes to binlog */
if (!(res= mysql_rename_user(thd, lex->users_list)))
- send_ok(thd);
+ my_ok(thd);
break;
}
case SQLCOM_REVOKE_ALL:
{
+ if (end_active_trans(thd))
+ goto error;
if (check_access(thd, UPDATE_ACL, "mysql", 0, 1, 1, 0) &&
check_global_access(thd,CREATE_USER_ACL))
break;
/* Conditionally writes to binlog */
if (!(res = mysql_revoke_all(thd, lex->users_list)))
- send_ok(thd);
+ my_ok(thd);
break;
}
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,
@@ -4268,8 +3812,7 @@ end_with_restore_list:
uint grants= lex->all_privileges
? (PROC_ACLS & ~GRANT_ACL) | (lex->grant & GRANT_ACL)
: lex->grant;
- if (grant_option &&
- check_grant_routine(thd, grants | GRANT_ACL, all_tables,
+ if (check_grant_routine(thd, grants | GRANT_ACL, all_tables,
lex->type == TYPE_ENUM_PROCEDURE, 0))
goto error;
/* Conditionally writes to binlog */
@@ -4280,10 +3823,8 @@ end_with_restore_list:
}
else
{
- if (grant_option && check_grant(thd,
- (lex->grant | lex->grant_tot_col |
- GRANT_ACL),
- all_tables, 0, UINT_MAX, 0))
+ if (check_grant(thd,(lex->grant | lex->grant_tot_col | GRANT_ACL),
+ all_tables, 0, UINT_MAX, 0))
goto error;
/* Conditionally writes to binlog */
res= mysql_table_grant(thd, all_tables, lex->users_list,
@@ -4313,7 +3854,7 @@ end_with_restore_list:
{
if (!(user= get_current_user(thd, tmp_user)))
goto error;
- reset_mqh(user);
+ reset_mqh(user, 0);
}
}
}
@@ -4343,16 +3884,14 @@ end_with_restore_list:
We WANT to write and we CAN write.
! we write after unlocking the table.
*/
- /* Presumably, RESET and binlog writing doesn't require synchronization */
+ /*
+ Presumably, RESET and binlog writing doesn't require synchronization
+ */
if (!lex->no_write_to_binlog && write_to_binlog)
{
- if (mysql_bin_log.is_open())
- {
- Query_log_event qinfo(thd, thd->query, thd->query_length, 0, FALSE);
- mysql_bin_log.write(&qinfo);
- }
+ write_bin_log(thd, FALSE, thd->query, thd->query_length);
}
- send_ok(thd);
+ my_ok(thd);
}
break;
@@ -4361,13 +3900,20 @@ end_with_restore_list:
{
Item *it= (Item *)lex->value_list.head();
+ if (lex->table_or_sp_used())
+ {
+ my_error(ER_NOT_SUPPORTED_YET, MYF(0), "Usage of subqueries or stored "
+ "function calls as part of this statement");
+ break;
+ }
+
if ((!it->fixed && it->fix_fields(lex->thd, &it)) || it->check_cols(1))
{
my_message(ER_SET_CONSTANTS_ONLY, ER(ER_SET_CONSTANTS_ONLY),
MYF(0));
goto error;
}
- kill_one_thread(thd, (ulong)it->val_int(), lex->type & ONLY_KILL_QUERY);
+ sql_kill(thd, (ulong)it->val_int(), lex->type & ONLY_KILL_QUERY);
break;
}
#ifndef NO_EMBEDDED_ACCESS_CHECKS
@@ -4387,15 +3933,12 @@ end_with_restore_list:
#endif
case SQLCOM_HA_OPEN:
DBUG_ASSERT(first_table == all_tables && first_table != 0);
- if (check_db_used(thd, all_tables) ||
- check_table_access(thd, SELECT_ACL, all_tables, 0))
+ if (check_table_access(thd, SELECT_ACL, all_tables, UINT_MAX, FALSE))
goto error;
res= mysql_ha_open(thd, first_table, 0);
break;
case SQLCOM_HA_CLOSE:
DBUG_ASSERT(first_table == all_tables && first_table != 0);
- if (check_db_used(thd, all_tables))
- goto error;
res= mysql_ha_close(thd, first_table);
break;
case SQLCOM_HA_READ:
@@ -4405,8 +3948,6 @@ end_with_restore_list:
if a user has no permissions to read a table, he won't be
able to open it (with SQLCOM_HA_OPEN) in the first place.
*/
- if (check_db_used(thd, all_tables))
- goto error;
unit->set_limit(select_lex);
res= mysql_ha_read(thd, first_table, lex->ha_read_mode, lex->ident.str,
lex->insert_list, lex->ha_rkey_mode, select_lex->where,
@@ -4422,19 +3963,19 @@ end_with_restore_list:
}
if (begin_trans(thd))
goto error;
- send_ok(thd);
+ my_ok(thd);
break;
case SQLCOM_COMMIT:
if (end_trans(thd, lex->tx_release ? COMMIT_RELEASE :
lex->tx_chain ? COMMIT_AND_CHAIN : COMMIT))
goto error;
- send_ok(thd);
+ my_ok(thd);
break;
case SQLCOM_ROLLBACK:
if (end_trans(thd, lex->tx_release ? ROLLBACK_RELEASE :
lex->tx_chain ? ROLLBACK_AND_CHAIN : ROLLBACK))
goto error;
- send_ok(thd);
+ my_ok(thd);
break;
case SQLCOM_RELEASE_SAVEPOINT:
{
@@ -4451,7 +3992,7 @@ end_with_restore_list:
if (ha_release_savepoint(thd, sv))
res= TRUE; // cannot happen
else
- send_ok(thd);
+ my_ok(thd);
thd->transaction.savepoints=sv->prev;
}
else
@@ -4474,12 +4015,13 @@ end_with_restore_list:
res= TRUE; // cannot happen
else
{
- if (thd->transaction.all.modified_non_trans_table &&
+ 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));
- send_ok(thd);
+ my_ok(thd);
}
thd->transaction.savepoints=sv;
}
@@ -4490,7 +4032,7 @@ end_with_restore_list:
case SQLCOM_SAVEPOINT:
if (!(thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN) ||
thd->in_sub_stmt) || !opt_using_transactions)
- send_ok(thd);
+ my_ok(thd);
else
{
SAVEPOINT **sv, *newsv;
@@ -4527,7 +4069,7 @@ end_with_restore_list:
{
newsv->prev=thd->transaction.savepoints;
thd->transaction.savepoints=newsv;
- send_ok(thd);
+ my_ok(thd);
}
}
break;
@@ -4544,7 +4086,7 @@ end_with_restore_list:
Verify that the database name is allowed, optionally
lowercase it.
*/
- if (check_db_name(lex->sphead->m_db.str))
+ if (check_db_name(&lex->sphead->m_db))
{
my_error(ER_WRONG_DB_NAME, MYF(0), lex->sphead->m_db.str);
goto create_sp_error;
@@ -4582,83 +4124,8 @@ end_with_restore_list:
}
#endif
- /*
- If the definer is not specified, this means that CREATE-statement missed
- DEFINER-clause. DEFINER-clause can be missed in two cases:
-
- - The user submitted a statement w/o the clause. This is a normal
- case, we should assign CURRENT_USER as definer.
-
- - Our slave received an updated from the master, that does not
- replicate definer for stored rountines. We should also assign
- CURRENT_USER as definer here, but also we should mark this routine
- as NON-SUID. This is essential for the sake of backward
- compatibility.
-
- The problem is the slave thread is running under "special" user (@),
- that actually does not exist. In the older versions we do not fail
- execution of a stored routine if its definer does not exist and
- continue the execution under the authorization of the invoker
- (BUG#13198). And now if we try to switch to slave-current-user (@),
- we will fail.
-
- Actually, this leads to the inconsistent state of master and
- slave (different definers, different SUID behaviour), but it seems,
- this is the best we can do.
- */
-
- if (!lex->definer)
- {
- bool local_res= FALSE;
- Query_arena original_arena;
- Query_arena *ps_arena = thd->activate_stmt_arena_if_needed(&original_arena);
-
- if (!(lex->definer= create_default_definer(thd)))
- local_res= TRUE;
-
- if (ps_arena)
- thd->restore_active_arena(ps_arena, &original_arena);
-
- /* Error has been already reported. */
- if (local_res)
- goto create_sp_error;
-
- if (thd->slave_thread)
- lex->sphead->m_chistics->suid= SP_IS_NOT_SUID;
- }
-
- /*
- If the specified definer differs from the current user, we should check
- that the current user has SUPER privilege (in order to create a stored
- routine under another user one must have SUPER privilege).
- */
-
- else 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))
- {
- if (check_global_access(thd, SUPER_ACL))
- {
- my_error(ER_SPECIFIC_ACCESS_DENIED_ERROR, MYF(0), "SUPER");
- goto create_sp_error;
- }
- }
-
- /* Check that the specified definer exists. Emit a warning if not. */
-
-#ifndef NO_EMBEDDED_ACCESS_CHECKS
- if (!is_acl_user(lex->definer->host.str,
- lex->definer->user.str))
- {
- push_warning_printf(thd,
- MYSQL_ERROR::WARN_LEVEL_NOTE,
- ER_NO_SUCH_USER,
- ER(ER_NO_SUCH_USER),
- lex->definer->user.str,
- lex->definer->host.str);
- }
-#endif /* NO_EMBEDDED_ACCESS_CHECKS */
+ if (sp_process_definer(thd))
+ goto create_sp_error;
res= (sp_result= lex->sphead->create(thd));
switch (sp_result) {
@@ -4675,7 +4142,6 @@ end_with_restore_list:
push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
ER_PROC_AUTO_GRANT_FAIL,
ER(ER_PROC_AUTO_GRANT_FAIL));
- close_thread_tables(thd);
}
#endif
break;
@@ -4688,6 +4154,9 @@ end_with_restore_list:
case SP_BODY_TOO_LONG:
my_error(ER_TOO_LONG_BODY, MYF(0), name);
break;
+ case SP_FLD_STORE_FAILED:
+ my_error(ER_CANT_CREATE_SROUTINE, MYF(0), name);
+ break;
default:
my_error(ER_SP_STORE_FAILED, MYF(0), SP_TYPE_STRING(lex), name);
break;
@@ -4700,7 +4169,7 @@ end_with_restore_list:
create_sp_error:
if (sp_result != SP_OK )
goto error;
- send_ok(thd);
+ my_ok(thd);
break; /* break super switch */
} /* end case group bracket */
case SQLCOM_CALL:
@@ -4711,7 +4180,7 @@ create_sp_error:
This will cache all SP and SF and open and lock all tables
required for execution.
*/
- if (check_table_access(thd, SELECT_ACL, all_tables, 0) ||
+ if (check_table_access(thd, SELECT_ACL, all_tables, UINT_MAX, FALSE) ||
open_and_lock_tables(thd, all_tables))
goto error;
@@ -4744,8 +4213,6 @@ create_sp_error:
goto error;
}
- my_bool save_no_send_ok= thd->net.no_send_ok;
- thd->net.no_send_ok= TRUE;
if (sp->m_flags & sp_head::MULTI_RESULTS)
{
if (! (thd->client_capabilities & CLIENT_MULTI_RESULTS))
@@ -4755,7 +4222,6 @@ create_sp_error:
back
*/
my_error(ER_SP_BADSELECT, MYF(0), sp->m_qname.str);
- thd->net.no_send_ok= save_no_send_ok;
goto error;
}
/*
@@ -4767,14 +4233,11 @@ create_sp_error:
thd->server_status|= SERVER_MORE_RESULTS_EXISTS;
}
-#ifndef NO_EMBEDDED_ACCESS_CHECKS
if (check_routine_access(thd, EXECUTE_ACL,
sp->m_db.str, sp->m_name.str, TRUE, FALSE))
{
- thd->net.no_send_ok= save_no_send_ok;
goto error;
}
-#endif
select_limit= thd->variables.select_limit;
thd->variables.select_limit= HA_POS_ERROR;
@@ -4796,14 +4259,16 @@ create_sp_error:
thd->variables.select_limit= select_limit;
- thd->net.no_send_ok= save_no_send_ok;
thd->server_status&= ~bits_to_be_cleared;
if (!res)
- send_ok(thd, (ulong) (thd->row_count_func < 0 ? 0 :
- thd->row_count_func));
+ my_ok(thd, (ulong) (thd->row_count_func < 0 ? 0 :
+ thd->row_count_func));
else
+ {
+ DBUG_ASSERT(thd->is_error() || thd->killed);
goto error; // Substatement should already have sent error
+ }
}
break;
}
@@ -4861,17 +4326,21 @@ create_sp_error:
already puts on CREATE FUNCTION.
*/
/* Conditionally writes to binlog */
- if (lex->sql_command == SQLCOM_ALTER_PROCEDURE)
- sp_result= sp_update_procedure(thd, lex->spname,
- &lex->sp_chistics);
- else
- sp_result= sp_update_function(thd, lex->spname, &lex->sp_chistics);
+
+ int type= lex->sql_command == SQLCOM_ALTER_PROCEDURE ?
+ TYPE_ENUM_PROCEDURE :
+ TYPE_ENUM_FUNCTION;
+
+ sp_result= sp_update_routine(thd,
+ type,
+ lex->spname,
+ &lex->sp_chistics);
}
}
switch (sp_result)
{
case SP_OK:
- send_ok(thd);
+ my_ok(thd);
break;
case SP_KEY_NOT_FOUND:
my_error(ER_SP_DOES_NOT_EXIST, MYF(0),
@@ -4914,11 +4383,13 @@ create_sp_error:
ER(ER_PROC_AUTO_REVOKE_FAIL));
}
#endif
- /* Conditionally writes to binlog */
- if (lex->sql_command == SQLCOM_DROP_PROCEDURE)
- sp_result= sp_drop_procedure(thd, lex->spname);
- else
- sp_result= sp_drop_function(thd, lex->spname);
+ /* Conditionally writes to binlog */
+
+ int type= lex->sql_command == SQLCOM_DROP_PROCEDURE ?
+ TYPE_ENUM_PROCEDURE :
+ TYPE_ENUM_FUNCTION;
+
+ sp_result= sp_drop_routine(thd, type, lex->spname);
}
else
{
@@ -4932,10 +4403,9 @@ create_sp_error:
if (check_access(thd, DELETE_ACL, "mysql", 0, 1, 0, 0))
goto error;
- /* Does NOT write to binlog */
if (!(res = mysql_drop_function(thd, &lex->spname->m_name)))
{
- send_ok(thd);
+ my_ok(thd);
break;
}
}
@@ -4952,7 +4422,7 @@ create_sp_error:
res= sp_result;
switch (sp_result) {
case SP_OK:
- send_ok(thd);
+ my_ok(thd);
break;
case SP_KEY_NOT_FOUND:
if (lex->drop_if_exists)
@@ -4961,7 +4431,7 @@ create_sp_error:
ER_SP_DOES_NOT_EXIST, ER(ER_SP_DOES_NOT_EXIST),
SP_COM_STRING(lex), lex->spname->m_name.str);
res= FALSE;
- send_ok(thd);
+ my_ok(thd);
break;
}
my_error(ER_SP_DOES_NOT_EXIST, MYF(0),
@@ -4976,13 +4446,8 @@ create_sp_error:
}
case SQLCOM_SHOW_CREATE_PROC:
{
- if (lex->spname->m_name.length > NAME_LEN)
+ if (sp_show_create_routine(thd, TYPE_ENUM_PROCEDURE, lex->spname))
{
- my_error(ER_TOO_LONG_IDENT, MYF(0), lex->spname->m_name.str);
- goto error;
- }
- if (sp_show_create_procedure(thd, lex->spname) != SP_OK)
- { /* We don't distinguish between errors for now */
my_error(ER_SP_DOES_NOT_EXIST, MYF(0),
SP_COM_STRING(lex), lex->spname->m_name.str);
goto error;
@@ -4991,42 +4456,20 @@ create_sp_error:
}
case SQLCOM_SHOW_CREATE_FUNC:
{
- if (lex->spname->m_name.length > NAME_LEN)
+ if (sp_show_create_routine(thd, TYPE_ENUM_FUNCTION, lex->spname))
{
- my_error(ER_TOO_LONG_IDENT, MYF(0), lex->spname->m_name.str);
- goto error;
- }
- if (sp_show_create_function(thd, lex->spname) != SP_OK)
- { /* We don't distinguish between errors for now */
my_error(ER_SP_DOES_NOT_EXIST, MYF(0),
SP_COM_STRING(lex), lex->spname->m_name.str);
goto error;
}
break;
}
- case SQLCOM_SHOW_STATUS_PROC:
- {
- res= sp_show_status_procedure(thd, (lex->wild ?
- lex->wild->ptr() : NullS));
- break;
- }
- case SQLCOM_SHOW_STATUS_FUNC:
- {
- res= sp_show_status_function(thd, (lex->wild ?
- lex->wild->ptr() : NullS));
- break;
- }
#ifndef DBUG_OFF
case SQLCOM_SHOW_PROC_CODE:
case SQLCOM_SHOW_FUNC_CODE:
{
sp_head *sp;
- if (lex->spname->m_name.length > NAME_LEN)
- {
- my_error(ER_TOO_LONG_IDENT, MYF(0), lex->spname->m_name.str);
- goto error;
- }
if (lex->sql_command == SQLCOM_SHOW_PROC_CODE)
sp= sp_find_routine(thd, TYPE_ENUM_PROCEDURE, lex->spname,
&thd->sp_proc_cache, FALSE);
@@ -5043,6 +4486,19 @@ create_sp_error:
break;
}
#endif // ifndef DBUG_OFF
+ case SQLCOM_SHOW_CREATE_TRIGGER:
+ {
+ if (lex->spname->m_name.length > NAME_LEN)
+ {
+ my_error(ER_TOO_LONG_IDENT, MYF(0), lex->spname->m_name.str);
+ goto error;
+ }
+
+ if (show_create_trigger(thd, lex->spname))
+ goto error; /* Error has been already logged. */
+
+ break;
+ }
case SQLCOM_CREATE_VIEW:
{
/*
@@ -5057,7 +4513,7 @@ create_sp_error:
}
case SQLCOM_DROP_VIEW:
{
- if (check_table_access(thd, DROP_ACL, all_tables, 0) ||
+ if (check_table_access(thd, DROP_ACL, all_tables, UINT_MAX, FALSE) ||
end_active_trans(thd))
goto error;
/* Conditionally writes to binlog. */
@@ -5093,7 +4549,7 @@ create_sp_error:
break;
}
thd->transaction.xid_state.xa_state=XA_ACTIVE;
- send_ok(thd);
+ my_ok(thd);
break;
}
if (thd->lex->xa_opt != XA_NONE)
@@ -5123,9 +4579,9 @@ create_sp_error:
thd->transaction.xid_state.xid.set(thd->lex->xid);
xid_cache_insert(&thd->transaction.xid_state);
thd->transaction.all.modified_non_trans_table= FALSE;
- thd->options|= (ulong) OPTION_BEGIN;
+ thd->options= ((thd->options & ~(OPTION_KEEP_LOG)) | OPTION_BEGIN);
thd->server_status|= SERVER_STATUS_IN_TRANS;
- send_ok(thd);
+ my_ok(thd);
break;
case SQLCOM_XA_END:
/* fake it */
@@ -5148,7 +4604,7 @@ create_sp_error:
if (xa_trans_rolled_back(&thd->transaction.xid_state))
break;
thd->transaction.xid_state.xa_state=XA_IDLE;
- send_ok(thd);
+ my_ok(thd);
break;
case SQLCOM_XA_PREPARE:
if (thd->transaction.xid_state.xa_state != XA_IDLE)
@@ -5170,7 +4626,7 @@ create_sp_error:
break;
}
thd->transaction.xid_state.xa_state=XA_PREPARED;
- send_ok(thd);
+ my_ok(thd);
break;
case SQLCOM_XA_COMMIT:
if (!thd->transaction.xid_state.xid.eq(thd->lex->xid))
@@ -5188,7 +4644,7 @@ create_sp_error:
{
ha_commit_or_rollback_by_xid(thd->lex->xid, 1);
xid_cache_delete(xs);
- send_ok(thd);
+ my_ok(thd);
}
break;
}
@@ -5204,7 +4660,7 @@ create_sp_error:
if ((r= ha_commit(thd)))
my_error(r == 1 ? ER_XA_RBROLLBACK : ER_XAER_RMERR, MYF(0));
else
- send_ok(thd);
+ my_ok(thd);
}
else if (thd->transaction.xid_state.xa_state == XA_PREPARED &&
thd->lex->xa_opt == XA_NONE)
@@ -5219,7 +4675,7 @@ create_sp_error:
if (ha_commit_one_phase(thd, 1))
my_error(ER_XAER_RMERR, MYF(0));
else
- send_ok(thd);
+ my_ok(thd);
start_waiting_global_read_lock(thd);
}
}
@@ -5229,7 +4685,7 @@ create_sp_error:
xa_state_names[thd->transaction.xid_state.xa_state]);
break;
}
- thd->options&= ~(ulong) OPTION_BEGIN;
+ 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);
@@ -5247,7 +4703,7 @@ create_sp_error:
ha_commit_or_rollback_by_xid(thd->lex->xid, 0);
xid_cache_delete(xs);
if (ok)
- send_ok(thd);
+ my_ok(thd);
}
break;
}
@@ -5262,22 +4718,110 @@ create_sp_error:
if (xa_trans_rollback(thd))
my_error(ER_XAER_RMERR, MYF(0));
else
- send_ok(thd);
+ 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) : 0))
+ break;
+ if (!(res= mysql_alter_tablespace(thd, lex->alter_tablespace_info)))
+ my_ok(thd);
+ break;
+ case SQLCOM_INSTALL_PLUGIN:
+ if (! (res= mysql_install_plugin(thd, &thd->lex->comment,
+ &thd->lex->ident)))
+ my_ok(thd);
+ break;
+ case SQLCOM_UNINSTALL_PLUGIN:
+ if (! (res= mysql_uninstall_plugin(thd, &thd->lex->comment)))
+ my_ok(thd);
+ break;
+ case SQLCOM_BINLOG_BASE64_EVENT:
+ {
+#ifndef EMBEDDED_LIBRARY
+ mysql_client_binlog_statement(thd);
+#else /* EMBEDDED_LIBRARY */
+ my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "embedded");
+#endif /* EMBEDDED_LIBRARY */
+ break;
+ }
+ case SQLCOM_CREATE_SERVER:
+ {
+ int error;
+ LEX *lex= thd->lex;
+ DBUG_PRINT("info", ("case SQLCOM_CREATE_SERVER"));
+
+ if (check_global_access(thd, SUPER_ACL))
+ break;
+
+ if ((error= create_server(thd, &lex->server_options)))
+ {
+ DBUG_PRINT("info", ("problem creating server <%s>",
+ lex->server_options.server_name));
+ my_error(error, MYF(0), lex->server_options.server_name);
+ break;
+ }
+ my_ok(thd, 1);
+ break;
+ }
+ case SQLCOM_ALTER_SERVER:
+ {
+ int error;
+ LEX *lex= thd->lex;
+ DBUG_PRINT("info", ("case SQLCOM_ALTER_SERVER"));
+
+ if (check_global_access(thd, SUPER_ACL))
+ break;
+
+ if ((error= alter_server(thd, &lex->server_options)))
+ {
+ DBUG_PRINT("info", ("problem altering server <%s>",
+ lex->server_options.server_name));
+ my_error(error, MYF(0), lex->server_options.server_name);
+ break;
+ }
+ my_ok(thd, 1);
+ break;
+ }
+ case SQLCOM_DROP_SERVER:
+ {
+ int err_code;
+ LEX *lex= thd->lex;
+ DBUG_PRINT("info", ("case SQLCOM_DROP_SERVER"));
+
+ if (check_global_access(thd, SUPER_ACL))
+ break;
+
+ if ((err_code= drop_server(thd, &lex->server_options)))
+ {
+ if (! lex->drop_if_exists && err_code == ER_FOREIGN_SERVER_DOESNT_EXIST)
+ {
+ DBUG_PRINT("info", ("problem dropping server %s",
+ lex->server_options.server_name));
+ my_error(err_code, MYF(0), lex->server_options.server_name);
+ }
+ else
+ {
+ my_ok(thd, 0);
+ }
+ break;
+ }
+ my_ok(thd, 1);
+ break;
+ }
default:
#ifndef EMBEDDED_LIBRARY
DBUG_ASSERT(0); /* Impossible */
#endif
- send_ok(thd);
+ my_ok(thd);
break;
}
- thd->proc_info="query end";
- /* Two binlog-related cleanups: */
+ thd_proc_info(thd, "query end");
/*
+ Binlog-related cleanup:
Reset system variables temporarily modified by SET ONE SHOT.
Exception: If this is a SET, do nothing. This is to allow
@@ -5292,21 +4836,18 @@ create_sp_error:
/*
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 do not change the value for a CALL or EXECUTE statement, so the value
- generated by the last called (or executed) statement is preserved.
- */
- if (lex->sql_command != SQLCOM_CALL && lex->sql_command != SQLCOM_EXECUTE &&
- uc_update_queries[lex->sql_command]<2)
+ 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 end;
+ goto finish;
error:
res= TRUE;
-end:
+finish:
if (need_start_waiting)
{
/*
@@ -5315,26 +4856,84 @@ end:
*/
start_waiting_global_read_lock(thd);
}
- DBUG_RETURN(res || thd->net.report_error);
+ DBUG_RETURN(res || thd->is_error());
}
-/*
+static bool execute_sqlcom_select(THD *thd, TABLE_LIST *all_tables)
+{
+ LEX *lex= thd->lex;
+ select_result *result=lex->result;
+ bool res;
+ /* assign global limit variable if limit is not given */
+ {
+ SELECT_LEX *param= lex->unit.global_parameters;
+ if (!param->explicit_limit)
+ param->select_limit=
+ new Item_int((ulonglong) thd->variables.select_limit);
+ }
+ if (!(res= open_and_lock_tables(thd, all_tables)))
+ {
+ if (lex->describe)
+ {
+ /*
+ We always use select_send for EXPLAIN, even if it's an EXPLAIN
+ for SELECT ... INTO OUTFILE: a user application should be able
+ to prepend EXPLAIN to any query and receive output for it,
+ even if the query itself redirects the output.
+ */
+ if (!(result= new select_send()))
+ return 1; /* purecov: inspected */
+ thd->send_explain_fields(result);
+ res= mysql_explain_union(thd, &thd->lex->unit, result);
+ if (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);
+ str.append('\0');
+ push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
+ ER_YES, str.ptr());
+ }
+ if (res)
+ result->abort();
+ else
+ result->send_eof();
+ delete result;
+ }
+ else
+ {
+ if (!result && !(result= new select_send()))
+ return 1; /* purecov: inspected */
+ query_cache_store_query(thd, all_tables);
+ res= handle_select(thd, lex, result, 0);
+ if (result != lex->result)
+ delete result;
+ }
+ }
+ return res;
+}
+
+
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+/**
Check grants for commands which work only with one table.
- SYNOPSIS
- check_single_table_access()
- thd Thread handler
- privilege requested privilege
- all_tables global table list of query
+ @param thd Thread handler
+ @param privilege requested privilege
+ @param all_tables global table list of query
+ @param no_errors FALSE/TRUE - report/don't report error to
+ the client (using my_error() call).
- RETURN
- 0 - OK
- 1 - access denied, error is sent to client
+ @retval
+ 0 OK
+ @retval
+ 1 access denied, error is sent to client
*/
bool check_single_table_access(THD *thd, ulong privilege,
- TABLE_LIST *all_tables)
+ TABLE_LIST *all_tables, bool no_errors)
{
Security_context * backup_ctx= thd->security_ctx;
@@ -5350,15 +4949,16 @@ 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, 0,
+ &all_tables->grant.privilege, 0, no_errors,
test(all_tables->schema_table)))
goto deny;
/* Show only 1 table for check_grant */
- if (grant_option &&
- !(all_tables->belong_to_view &&
+ if (!(all_tables->belong_to_view &&
(thd->lex->sql_command == SQLCOM_SHOW_FIELDS)) &&
- check_grant(thd, privilege, all_tables, 0, 1, 0))
+ !(all_tables->view &&
+ all_tables->effective_algorithm == VIEW_ALGORITHM_TMPTABLE) &&
+ check_grant(thd, privilege, all_tables, 0, 1, no_errors))
goto deny;
thd->security_ctx= backup_ctx;
@@ -5369,24 +4969,23 @@ deny:
return 1;
}
-/*
+/**
Check grants for commands which work only with one table and all other
tables belonging to subselects or implicitly opened tables.
- SYNOPSIS
- check_one_table_access()
- thd Thread handler
- privilege requested privilege
- all_tables global table list of query
+ @param thd Thread handler
+ @param privilege requested privilege
+ @param all_tables global table list of query
- RETURN
- 0 - OK
- 1 - access denied, error is sent to client
+ @retval
+ 0 OK
+ @retval
+ 1 access denied, error is sent to client
*/
bool check_one_table_access(THD *thd, ulong privilege, TABLE_LIST *all_tables)
{
- if (check_single_table_access (thd,privilege,all_tables))
+ if (check_single_table_access (thd,privilege,all_tables, FALSE))
return 1;
/* Check rights on tables of subselects and implictly opened tables */
@@ -5399,43 +4998,43 @@ bool check_one_table_access(THD *thd, ulong privilege, TABLE_LIST *all_tables)
*/
if (view && subselects_tables->belong_to_view == view)
{
- if (check_single_table_access (thd, privilege, subselects_tables))
+ if (check_single_table_access (thd, privilege, subselects_tables, FALSE))
return 1;
subselects_tables= subselects_tables->next_global;
}
if (subselects_tables &&
- (check_table_access(thd, SELECT_ACL, subselects_tables, 0)))
+ (check_table_access(thd, SELECT_ACL, subselects_tables, UINT_MAX, FALSE)))
return 1;
}
return 0;
}
-/****************************************************************************
- Get the user (global) and database privileges for all used 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.
- NOTES
+ @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.
- RETURN
+ @retval
0 ok
- 1 If we can't get the privileges and we don't use table/column grants.
-
- 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.
-****************************************************************************/
-
+ @retval
+ 1 If we can't get the privileges and we don't use table/column
+ grants.
+*/
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)
{
Security_context *sctx= thd->security_ctx;
-#ifndef NO_EMBEDDED_ACCESS_CHECKS
ulong db_access;
/*
GRANT command:
@@ -5448,7 +5047,6 @@ check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv,
*/
bool db_is_pattern= (test(want_access & GRANT_ACL) &&
dont_check_global_grants);
-#endif
ulong dummy;
DBUG_ENTER("check_access");
DBUG_PRINT("enter",("db: %s want_access: %lu master_access: %lu",
@@ -5458,6 +5056,7 @@ check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv,
else
save_priv= &dummy;
+ thd_proc_info(thd, "checking permissions");
if ((!db || !db[0]) && !thd->db && !dont_check_global_grants)
{
DBUG_PRINT("error",("No database"));
@@ -5487,9 +5086,6 @@ check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv,
}
}
-#ifdef NO_EMBEDDED_ACCESS_CHECKS
- DBUG_RETURN(0);
-#else
if ((sctx->master_access & want_access) == want_access)
{
/*
@@ -5534,9 +5130,8 @@ check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv,
db_access, want_access));
db_access= ((*save_priv=(db_access | sctx->master_access)) & want_access);
- /* grant_option is set if there exists a single table or column grant */
if (db_access == want_access ||
- (grant_option && !dont_check_global_grants &&
+ (!dont_check_global_grants &&
!(want_access & ~(db_access | TABLE_ACLS | PROC_ACLS))))
DBUG_RETURN(FALSE); /* Ok */
@@ -5548,114 +5143,61 @@ check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv,
thd->db :
"unknown"))); /* purecov: tested */
DBUG_RETURN(TRUE); /* purecov: tested */
-#endif /* NO_EMBEDDED_ACCESS_CHECKS */
-}
-
-
-/*
- check for global access and give descriptive error message if it fails
-
- SYNOPSIS
- check_global_access()
- thd Thread handler
- want_access Use should have any of these global rights
-
- WARNING
- One gets access right if one has ANY of the rights in want_access
- This is useful as one in most cases only need one global right,
- but in some case we want to check if the user has SUPER or
- REPL_CLIENT_ACL rights.
-
- RETURN
- 0 ok
- 1 Access denied. In this case an error is sent to the client
-*/
-
-bool check_global_access(THD *thd, ulong want_access)
-{
-#ifdef NO_EMBEDDED_ACCESS_CHECKS
- return 0;
-#else
- char command[128];
- if ((thd->security_ctx->master_access & want_access))
- return 0;
- get_privilege_desc(command, sizeof(command), want_access);
- my_error(ER_SPECIFIC_ACCESS_DENIED_ERROR, MYF(0), command);
- return 1;
-#endif /* NO_EMBEDDED_ACCESS_CHECKS */
}
static bool check_show_access(THD *thd, TABLE_LIST *table)
{
- switch (get_schema_table_idx(table->schema_table))
- {
+ switch (get_schema_table_idx(table->schema_table)) {
case SCH_SCHEMATA:
return (specialflag & SPECIAL_SKIP_SHOW_DB) &&
- check_global_access(thd, SHOW_DB_ACL);
+ check_global_access(thd, SHOW_DB_ACL);
case SCH_TABLE_NAMES:
case SCH_TABLES:
case SCH_VIEWS:
case SCH_TRIGGERS:
- {
- const char *dst_db_name= table->schema_select_lex->db;
-
- DBUG_ASSERT(dst_db_name);
+ case SCH_EVENTS:
+ {
+ const char *dst_db_name= table->schema_select_lex->db;
- if (check_access(thd, SELECT_ACL, dst_db_name,
- &thd->col_access, FALSE, FALSE,
- is_schema_db(dst_db_name)))
- {
- return TRUE;
- }
+ DBUG_ASSERT(dst_db_name);
- if (!thd->col_access && check_grant_db(thd, dst_db_name))
- {
- my_error(ER_DBACCESS_DENIED_ERROR, MYF(0),
- thd->security_ctx->priv_user,
- thd->security_ctx->priv_host,
- dst_db_name);
- return TRUE;
- }
+ if (check_access(thd, SELECT_ACL, dst_db_name,
+ &thd->col_access, FALSE, FALSE,
+ is_schema_db(dst_db_name)))
+ return TRUE;
- return FALSE;
+ if (!thd->col_access && check_grant_db(thd, dst_db_name))
+ {
+ my_error(ER_DBACCESS_DENIED_ERROR, MYF(0),
+ thd->security_ctx->priv_user,
+ thd->security_ctx->priv_host,
+ dst_db_name);
+ return TRUE;
}
+ return FALSE;
+ }
+
case SCH_COLUMNS:
case SCH_STATISTICS:
- {
- TABLE_LIST *dst_table=
- (TABLE_LIST *) table->schema_select_lex->table_list.first;
+ {
+ TABLE_LIST *dst_table;
+ dst_table= (TABLE_LIST *) table->schema_select_lex->table_list.first;
- DBUG_ASSERT(dst_table);
+ DBUG_ASSERT(dst_table);
- if (check_access(thd, SELECT_ACL | EXTRA_ACL,
- dst_table->db,
- &dst_table->grant.privilege,
- FALSE, FALSE,
- test(dst_table->schema_table)))
- {
- return FALSE;
- }
-
- return grant_option &&
- check_grant(thd, SELECT_ACL, dst_table, 2, UINT_MAX, FALSE);
- }
+ if (check_access(thd, SELECT_ACL | EXTRA_ACL,
+ dst_table->db,
+ &dst_table->grant.privilege,
+ FALSE, FALSE,
+ test(dst_table->schema_table)))
+ return FALSE;
- case SCH_OPEN_TABLES:
- case SCH_VARIABLES:
- case SCH_STATUS:
- case SCH_PROCEDURES:
- case SCH_CHARSETS:
- case SCH_COLLATIONS:
- case SCH_COLLATION_CHARACTER_SET_APPLICABILITY:
- case SCH_USER_PRIVILEGES:
- case SCH_SCHEMA_PRIVILEGES:
- case SCH_TABLE_PRIVILEGES:
- case SCH_COLUMN_PRIVILEGES:
- case SCH_TABLE_CONSTRAINTS:
- case SCH_KEY_COLUMN_USAGE:
+ return (check_grant(thd, SELECT_ACL, dst_table, 2, UINT_MAX, FALSE));
+ }
+ default:
break;
}
@@ -5663,46 +5205,42 @@ static bool check_show_access(THD *thd, TABLE_LIST *table)
}
-/*
+/**
Check the privilege for all used tables.
- SYNOPSYS
- check_table_access()
- thd Thread context
- want_access Privileges requested
- tables List of tables to be checked
- no_errors FALSE/TRUE - report/don't report error to
- the client (using my_error() call).
+ @param thd Thread context
+ @param want_access Privileges requested
+ @param tables List of tables to be checked
+ @param number Check at most this number of tables.
+ @param no_errors FALSE/TRUE - report/don't report error to
+ the client (using my_error() call).
- NOTES
+ @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).
- RETURN VALUE
- FALSE - OK
- TRUE - Access denied
+ @retval FALSE OK
+ @retval TRUE Access denied
*/
bool
check_table_access(THD *thd, ulong want_access,TABLE_LIST *tables,
- bool no_errors)
+ uint number, bool no_errors)
{
- uint found=0;
- ulong found_access=0;
-#ifndef NO_EMBEDDED_ACCESS_CHECKS
TABLE_LIST *org_tables= tables;
-#endif
TABLE_LIST *first_not_own_table= thd->lex->first_not_own_table();
+ uint i= 0;
Security_context *sctx= thd->security_ctx, *backup_ctx= thd->security_ctx;
/*
The check that first_not_own_table is not reached is for the case when
the given table list refers to the list for prelocking (contains tables
of other queries). For simple queries first_not_own_table is 0.
*/
- for (; tables != first_not_own_table; tables= tables->next_global)
+ for (; i < number && tables != first_not_own_table;
+ tables= tables->next_global, i++)
{
if (tables->security_ctx)
sctx= tables->security_ctx;
@@ -5732,10 +5270,8 @@ check_table_access(THD *thd, ulong want_access,TABLE_LIST *tables,
continue;
}
- if (tables->derived ||
- (tables->table && (int)tables->table->s->tmp_table) ||
- my_tz_check_n_skip_implicit_tables(&tables,
- thd->lex->time_zone_tables_used))
+ if (tables->is_anonymous_derived_table() ||
+ (tables->table && (int)tables->table->s->tmp_table))
continue;
thd->security_ctx= sctx;
if ((sctx->master_access & want_access) ==
@@ -5744,26 +5280,19 @@ check_table_access(THD *thd, ulong want_access,TABLE_LIST *tables,
tables->grant.privilege= want_access;
else if (tables->db && thd->db && strcmp(tables->db, thd->db) == 0)
{
- if (found && !grant_option) // db already checked
- tables->grant.privilege=found_access;
- else
- {
- if (check_access(thd,want_access,tables->db,&tables->grant.privilege,
- 0, no_errors, test(tables->schema_table)))
- goto deny; // Access denied
- found_access=tables->grant.privilege;
- found=1;
- }
+ 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->db,&tables->grant.privilege,
- 0, no_errors, test(tables->schema_table)))
+ else if (check_access(thd, want_access, tables->get_db_name(),
+ &tables->grant.privilege, 0, no_errors,
+ test(tables->schema_table)))
goto deny;
}
thd->security_ctx= backup_ctx;
- if (grant_option)
- return check_grant(thd,want_access & ~EXTRA_ACL,org_tables,
- test(want_access & EXTRA_ACL), UINT_MAX, no_errors);
- return FALSE;
+ return check_grant(thd,want_access & ~EXTRA_ACL,org_tables,
+ test(want_access & EXTRA_ACL), number, no_errors);
deny:
thd->security_ctx= backup_ctx;
return TRUE;
@@ -5792,26 +5321,20 @@ check_routine_access(THD *thd, ulong want_access,char *db, char *name,
0, no_errors, 0))
return TRUE;
-#ifndef NO_EMBEDDED_ACCESS_CHECKS
- if (grant_option)
return check_grant_routine(thd, want_access, tables, is_proc, no_errors);
-#endif
-
- return FALSE;
}
-/*
- Check if the routine has any of the routine privileges
+/**
+ Check if the routine has any of the routine privileges.
- SYNOPSIS
- check_some_routine_access()
- thd Thread handler
- db Database name
- name Routine name
+ @param thd Thread handler
+ @param db Database name
+ @param name Routine name
- RETURN
+ @retval
0 ok
+ @retval
1 error
*/
@@ -5835,17 +5358,15 @@ bool check_some_routine_access(THD *thd, const char *db, const char *name,
/*
Check if the given table has any of the asked privileges
- SYNOPSIS
- check_some_access()
- thd Thread handler
- want_access Bitmap of possible privileges to check for
+ @param thd Thread handler
+ @param want_access Bitmap of possible privileges to check for
- RETURN
+ @retval
0 ok
+ @retval
1 error
*/
-
bool check_some_access(THD *thd, ulong want_access, TABLE_LIST *table)
{
ulong access;
@@ -5859,7 +5380,7 @@ bool check_some_access(THD *thd, ulong want_access, TABLE_LIST *table)
if (!check_access(thd, access, table->db,
&table->grant.privilege, 0, 1,
test(table->schema_table)) &&
- !grant_option || !check_grant(thd, access, table, 0, 1, 1))
+ !check_grant(thd, access, table, 0, 1, 1))
DBUG_RETURN(0);
}
}
@@ -5867,52 +5388,47 @@ bool check_some_access(THD *thd, ulong want_access, TABLE_LIST *table)
DBUG_RETURN(1);
}
+#endif /*NO_EMBEDDED_ACCESS_CHECKS*/
-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 *tmp;
- for (tmp= table_list; tmp; tmp= tmp->next_local)
- {
- if (!tmp->db || !tmp->db[0])
- tmp->db=db;
- }
- error=check_table_access(thd, SELECT_ACL | UPDATE_ACL | DELETE_ACL,
- table_list,0);
- }
- return error;
-}
+/**
+ check for global access and give descriptive error message if it fails.
+
+ @param thd Thread handler
+ @param want_access Use should have any of these global rights
+
+ @warning
+ One gets access right if one has ANY of the rights in want_access.
+ This is useful as one in most cases only need one global right,
+ but in some case we want to check if the user has SUPER or
+ REPL_CLIENT_ACL rights.
-static bool check_db_used(THD *thd,TABLE_LIST *tables)
+ @retval
+ 0 ok
+ @retval
+ 1 Access denied. In this case an error is sent to the client
+*/
+
+bool check_global_access(THD *thd, ulong want_access)
{
- char *current_db= NULL;
- for (; tables; tables= tables->next_global)
- {
- if (tables->db == NULL)
- {
- /*
- This code never works and should be removed in 5.1. All tables
- that are added to the list of tables should already have its
- database field initialized properly (see st_lex::add_table_to_list).
- */
- DBUG_ASSERT(0);
- if (thd->copy_db_to(&current_db, 0))
- return TRUE;
- tables->db= current_db;
- }
- }
- return FALSE;
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+ char command[128];
+ if ((thd->security_ctx->master_access & want_access))
+ return 0;
+ get_privilege_desc(command, sizeof(command), want_access);
+ my_error(ER_SPECIFIC_ACCESS_DENIED_ERROR, MYF(0), command);
+ return 1;
+#else
+ return 0;
+#endif
}
/****************************************************************************
Check stack size; Send error if there isn't enough stack to continue
****************************************************************************/
+#ifndef EMBEDDED_LIBRARY
+
#if STACK_DIRECTION < 0
#define used_stack(A,B) (long) (A - B)
#else
@@ -5923,24 +5439,25 @@ static bool check_db_used(THD *thd,TABLE_LIST *tables)
long max_stack_used;
#endif
-#ifndef EMBEDDED_LIBRARY
-/*
+/**
+ @note
Note: The 'buf' parameter is necessary, even if it is unused here.
- fix_fields functions has a "dummy" buffer large enough for the
corresponding exec. (Thus we only have to check in fix_fields.)
- Passing to check_stack_overrun() prevents the compiler from removing it.
- */
+*/
bool check_stack_overrun(THD *thd, long margin,
- char *buf __attribute__((unused)))
+ uchar *buf __attribute__((unused)))
{
long stack_used;
DBUG_ASSERT(thd == current_thd);
if ((stack_used=used_stack(thd->thread_stack,(char*) &stack_used)) >=
- (long) (thread_stack - margin))
+ (long) (my_thread_stack_size - margin))
{
- sprintf(errbuff[0],ER(ER_STACK_OVERRUN_NEED_MORE),
- stack_used,thread_stack,margin);
- my_message(ER_STACK_OVERRUN_NEED_MORE,errbuff[0],MYF(0));
+ 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();
return 1;
}
@@ -5964,14 +5481,14 @@ bool my_yyoverflow(short **yyss, YYSTYPE **yyvs, ulong *yystacksize)
if (!state->yacc_yyvs)
old_info= *yystacksize;
*yystacksize= set_zone((*yystacksize)*2,MY_YACC_INIT,MY_YACC_MAX);
- if (!(state->yacc_yyvs= (char*)
+ if (!(state->yacc_yyvs= (uchar*)
my_realloc(state->yacc_yyvs,
- *yystacksize*sizeof(**yyvs),
- MYF(MY_ALLOW_ZERO_PTR | MY_FREE_ON_ERROR))) ||
- !(state->yacc_yyss= (char*)
+ *yystacksize*sizeof(**yyvs),
+ MYF(MY_ALLOW_ZERO_PTR | MY_FREE_ON_ERROR))) ||
+ !(state->yacc_yyss= (uchar*)
my_realloc(state->yacc_yyss,
- *yystacksize*sizeof(**yyss),
- MYF(MY_ALLOW_ZERO_PTR | MY_FREE_ON_ERROR))))
+ *yystacksize*sizeof(**yyss),
+ MYF(MY_ALLOW_ZERO_PTR | MY_FREE_ON_ERROR))))
return 1;
if (old_info)
{
@@ -5989,45 +5506,75 @@ bool my_yyoverflow(short **yyss, YYSTYPE **yyvs, ulong *yystacksize)
}
-/*
+/**
Reset THD part responsible for command processing state.
- DESCRIPTION
This needs to be called before execution of every statement
(prepared or conventional).
+ It is not called by substatements of routines.
- TODO
+ @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.
*/
void mysql_reset_thd_for_next_command(THD *thd)
{
DBUG_ENTER("mysql_reset_thd_for_next_command");
+ DBUG_ASSERT(!thd->spcont); /* not for substatements of routines */
+ DBUG_ASSERT(! thd->in_sub_stmt);
thd->free_list= 0;
thd->select_number= 1;
- thd->query_start_used= thd->insert_id_used=0;
- thd->last_insert_id_used_bin_log= FALSE;
+ /*
+ Those two lines below are theoretically unneeded as
+ THD::cleanup_after_query() should take care of this already.
+ */
+ thd->auto_inc_intervals_in_cur_stmt_for_binlog.empty();
+ thd->stmt_depends_on_first_successful_insert_id_in_prev_stmt= 0;
+
+ thd->query_start_used= 0;
thd->is_fatal_error= thd->time_zone_used= 0;
- thd->server_status&= ~ (SERVER_MORE_RESULTS_EXISTS |
- SERVER_QUERY_NO_INDEX_USED |
- SERVER_QUERY_NO_GOOD_INDEX_USED);
+ /*
+ Clear the status flag that are expected to be cleared at the
+ beginning of each SQL statement.
+ */
+ thd->server_status&= ~SERVER_STATUS_CLEAR_SET;
+ /*
+ If in autocommit mode and not in a transaction, reset
+ OPTION_STATUS_NO_TRANS_UPDATE | OPTION_KEEP_LOG to not get warnings
+ in ha_rollback_trans() about some tables couldn't be rolled back.
+ */
+ if (!(thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)))
+ {
+ thd->options&= ~OPTION_KEEP_LOG;
+ thd->transaction.all.modified_non_trans_table= FALSE;
+ }
DBUG_ASSERT(thd->security_ctx== &thd->main_security_ctx);
- thd->tmp_table_used= 0;
thd->thread_specific_used= FALSE;
- if (!thd->in_sub_stmt)
+
+ if (opt_bin_log)
{
- if (opt_bin_log)
- {
- reset_dynamic(&thd->user_var_events);
- thd->user_var_events_alloc= thd->mem_root;
- }
- thd->clear_error();
- thd->total_warn_count=0; // Warnings for this query
- thd->rand_used= 0;
- thd->sent_row_count= thd->examined_row_count= 0;
+ reset_dynamic(&thd->user_var_events);
+ 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->rand_used= 0;
+ thd->sent_row_count= thd->examined_row_count= 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();
+
+ DBUG_PRINT("debug",
+ ("current_stmt_binlog_row_based: %d",
+ thd->current_stmt_binlog_row_based));
+
DBUG_VOID_RETURN;
}
@@ -6121,17 +5668,14 @@ mysql_new_select(LEX *lex, bool move_down)
DBUG_RETURN(0);
}
-/*
+/**
Create a select to return the same output as 'SELECT @@var_name'.
- SYNOPSIS
- create_select_for_variable()
- var_name Variable name
+ Used for SHOW COUNT(*) [ WARNINGS | ERROR].
- DESCRIPTION
- Used for SHOW COUNT(*) [ WARNINGS | ERROR]
+ This will crash with a core dump if the variable doesn't exists.
- This will crash with a core dump if the variable doesn't exists
+ @param var_name Variable name
*/
void create_select_for_variable(const char *var_name)
@@ -6148,7 +5692,7 @@ void create_select_for_variable(const char *var_name)
mysql_init_select(lex);
lex->sql_command= SQLCOM_SELECT;
tmp.str= (char*) var_name;
- tmp.length=(uint) strlen(var_name);
+ tmp.length=strlen(var_name);
bzero((char*) &null_lex_string.str, sizeof(null_lex_string));
/*
We set the name of Item to @@session.var_name because that then is used
@@ -6157,7 +5701,7 @@ void create_select_for_variable(const char *var_name)
if ((var= get_system_var(thd, OPT_SESSION, tmp, null_lex_string)))
{
end= strxmov(buff, "@@session.", var_name, NullS);
- var->set_name(buff, (uint) (end - buff), system_charset_info);
+ var->set_name(buff, end-buff, system_charset_info);
add_item_to_list(thd, var);
}
DBUG_VOID_RETURN;
@@ -6171,11 +5715,12 @@ 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= using_update_log ? TL_READ_NO_INSERT : TL_READ;
+ lex->lock_option= TL_READ_DEFAULT;
lex->query_tables= 0;
lex->query_tables_last= &lex->query_tables;
}
+
/*
When you modify mysql_parse(), you may need to mofify
mysql_test_parse_for_slave() in this same file.
@@ -6183,11 +5728,12 @@ void mysql_init_multi_delete(LEX *lex)
/**
Parse a query.
- @param thd Current thread
- @param inBuf Begining of the query text
- @param length Length of the query text
- @param [out] semicolon For multi queries, position of the character of
- the next query in the query text.
+
+ @param thd Current thread
+ @param inBuf Begining of the query text
+ @param length Length of the query text
+ @param[out] found_semicolon For multi queries, position of the character of
+ the next query in the query text.
*/
void mysql_parse(THD *thd, const char *inBuf, uint length,
@@ -6224,13 +5770,11 @@ void mysql_parse(THD *thd, const char *inBuf, uint length,
sp_cache_flush_obsolete(&thd->sp_func_cache);
Parser_state parser_state(thd, inBuf, length);
- thd->m_parser_state= &parser_state;
- int err= MYSQLparse(thd);
+ bool err= parse_sql(thd, & parser_state, NULL);
*found_semicolon= parser_state.m_lip.found_semicolon;
- thd->m_parser_state= NULL;
- if (!err && ! thd->is_fatal_error)
+ if (!err)
{
#ifndef NO_EMBEDDED_ACCESS_CHECKS
if (mqh_used && thd->user_connect &&
@@ -6241,7 +5785,7 @@ void mysql_parse(THD *thd, const char *inBuf, uint length,
else
#endif
{
- if (! thd->net.report_error)
+ if (! thd->is_error())
{
/*
Binlog logs a string starting from thd->query and having length
@@ -6253,9 +5797,8 @@ void mysql_parse(THD *thd, const char *inBuf, uint length,
PROCESSLIST.
Note that we don't need LOCK_thread_count to modify query_length.
*/
- if (parser_state.m_lip.found_semicolon &&
- (thd->query_length= (ulong)(parser_state.m_lip.found_semicolon
- - thd->query)))
+ if (*found_semicolon &&
+ (thd->query_length= (ulong)(*found_semicolon - thd->query)))
thd->query_length--;
/* Actually execute the query */
if (*found_semicolon)
@@ -6265,13 +5808,12 @@ void mysql_parse(THD *thd, const char *inBuf, uint length,
}
lex->set_trg_event_type_for_tables();
mysql_execute_command(thd);
- query_cache_end_of_result(thd);
}
}
}
else
{
- DBUG_ASSERT(thd->net.report_error);
+ DBUG_ASSERT(thd->is_error());
DBUG_PRINT("info",("Command aborted. Fatal_error: %d",
thd->is_fatal_error));
@@ -6283,7 +5825,7 @@ void mysql_parse(THD *thd, const char *inBuf, uint length,
thd->lex->sphead= 0;
}
lex->unit.cleanup();
- thd->proc_info="freeing items";
+ thd_proc_info(thd, "freeing items");
thd->end_statement();
thd->cleanup_after_query();
DBUG_ASSERT(thd->change_list.is_empty());
@@ -6303,8 +5845,9 @@ void mysql_parse(THD *thd, const char *inBuf, uint length,
Usable by the replication SQL thread only: just parse a query to know if it
can be ignored because of replicate-*-table rules.
- RETURN VALUES
+ @retval
0 cannot be ignored
+ @retval
1 can be ignored
*/
@@ -6315,14 +5858,10 @@ bool mysql_test_parse_for_slave(THD *thd, char *inBuf, uint length)
DBUG_ENTER("mysql_test_parse_for_slave");
Parser_state parser_state(thd, inBuf, length);
- thd->m_parser_state= &parser_state;
-
lex_start(thd);
mysql_reset_thd_for_next_command(thd);
- int err= MYSQLparse((void*) thd);
- thd->m_parser_state= NULL;
- if (!err && ! thd->is_fatal_error &&
+ if (!parse_sql(thd, & parser_state, NULL) &&
all_tables_not_ok(thd,(TABLE_LIST*) lex->select_lex.table_list.first))
error= 1; /* Ignore question */
thd->end_statement();
@@ -6333,12 +5872,14 @@ bool mysql_test_parse_for_slave(THD *thd, char *inBuf, uint length)
-/*****************************************************************************
-** Store field definition for create
-** Return 0 if ok
-******************************************************************************/
+/**
+ Store field definition for create.
-bool add_field_to_list(THD *thd, char *field_name, enum_field_types type,
+ @return
+ Return 0 if ok
+*/
+
+bool add_field_to_list(THD *thd, LEX_STRING *field_name, enum_field_types type,
char *length, char *decimals,
uint type_modifier,
Item *default_value, Item *on_update_value,
@@ -6347,29 +5888,34 @@ bool add_field_to_list(THD *thd, char *field_name, enum_field_types type,
List<String> *interval_list, CHARSET_INFO *cs,
uint uint_geom_type)
{
- register create_field *new_field;
+ register Create_field *new_field;
LEX *lex= thd->lex;
DBUG_ENTER("add_field_to_list");
- if (strlen(field_name) > NAME_LEN)
+ if (check_string_char_length(field_name, "", NAME_CHAR_LEN,
+ system_charset_info, 1))
{
- my_error(ER_TOO_LONG_IDENT, MYF(0), field_name); /* purecov: inspected */
+ my_error(ER_TOO_LONG_IDENT, MYF(0), field_name->str); /* purecov: inspected */
DBUG_RETURN(1); /* purecov: inspected */
}
if (type_modifier & PRI_KEY_FLAG)
{
- lex->col_list.push_back(new key_part_spec(field_name,0));
- lex->alter_info.key_list.push_back(new Key(Key::PRIMARY, NullS,
- HA_KEY_ALG_UNDEF, 0,
- lex->col_list));
+ Key *key;
+ lex->col_list.push_back(new Key_part_spec(field_name->str, 0));
+ key= new Key(Key::PRIMARY, NullS,
+ &default_key_create_info,
+ 0, lex->col_list);
+ lex->alter_info.key_list.push_back(key);
lex->col_list.empty();
}
if (type_modifier & (UNIQUE_FLAG | UNIQUE_KEY_FLAG))
{
- lex->col_list.push_back(new key_part_spec(field_name,0));
- lex->alter_info.key_list.push_back(new Key(Key::UNIQUE, NullS,
- HA_KEY_ALG_UNDEF, 0,
- lex->col_list));
+ Key *key;
+ lex->col_list.push_back(new Key_part_spec(field_name->str, 0));
+ key= new Key(Key::UNIQUE, NullS,
+ &default_key_create_info, 0,
+ lex->col_list);
+ lex->alter_info.key_list.push_back(key);
lex->col_list.empty();
}
@@ -6384,9 +5930,9 @@ bool add_field_to_list(THD *thd, char *field_name, enum_field_types type,
*/
if (default_value->type() == Item::FUNC_ITEM &&
!(((Item_func*)default_value)->functype() == Item_func::NOW_FUNC &&
- type == FIELD_TYPE_TIMESTAMP))
+ type == MYSQL_TYPE_TIMESTAMP))
{
- my_error(ER_INVALID_DEFAULT, MYF(0), field_name);
+ my_error(ER_INVALID_DEFAULT, MYF(0), field_name->str);
DBUG_RETURN(1);
}
else if (default_value->type() == Item::NULL_ITEM)
@@ -6395,24 +5941,24 @@ bool add_field_to_list(THD *thd, char *field_name, enum_field_types type,
if ((type_modifier & (NOT_NULL_FLAG | AUTO_INCREMENT_FLAG)) ==
NOT_NULL_FLAG)
{
- my_error(ER_INVALID_DEFAULT, MYF(0), field_name);
+ my_error(ER_INVALID_DEFAULT, MYF(0), field_name->str);
DBUG_RETURN(1);
}
}
else if (type_modifier & AUTO_INCREMENT_FLAG)
{
- my_error(ER_INVALID_DEFAULT, MYF(0), field_name);
+ my_error(ER_INVALID_DEFAULT, MYF(0), field_name->str);
DBUG_RETURN(1);
}
}
- if (on_update_value && type != FIELD_TYPE_TIMESTAMP)
+ if (on_update_value && type != MYSQL_TYPE_TIMESTAMP)
{
- my_error(ER_INVALID_ON_UPDATE, MYF(0), field_name);
+ my_error(ER_INVALID_ON_UPDATE, MYF(0), field_name->str);
DBUG_RETURN(1);
}
- if (type == FIELD_TYPE_TIMESTAMP && length)
+ if (type == MYSQL_TYPE_TIMESTAMP && length)
{
/* Display widths are no longer supported for TIMSTAMP as of MySQL 4.1.
In other words, for declarations such as TIMESTAMP(2), TIMESTAMP(4),
@@ -6420,14 +5966,11 @@ bool add_field_to_list(THD *thd, char *field_name, enum_field_types type,
*/
char buf[32];
my_snprintf(buf, sizeof(buf), "TIMESTAMP(%s)", length);
- push_warning_printf(thd,MYSQL_ERROR::WARN_LEVEL_WARN,
- ER_WARN_DEPRECATED_SYNTAX,
- ER(ER_WARN_DEPRECATED_SYNTAX),
- buf, "TIMESTAMP");
+ WARN_DEPRECATED(thd, "5.2", buf, "'TIMESTAMP'");
}
- if (!(new_field= new create_field()) ||
- new_field->init(thd, field_name, type, length, decimals, type_modifier,
+ if (!(new_field= new Create_field()) ||
+ new_field->init(thd, field_name->str, type, length, decimals, type_modifier,
default_value, on_update_value, comment, change,
interval_list, cs, uint_geom_type))
DBUG_RETURN(1);
@@ -6438,7 +5981,7 @@ bool add_field_to_list(THD *thd, char *field_name, enum_field_types type,
}
-/* Store position for column in ALTER TABLE .. ADD column */
+/** Store position for column in ALTER TABLE .. ADD column. */
void store_position_for_column(const char *name)
{
@@ -6457,45 +6000,14 @@ add_proc_to_list(THD* thd, Item *item)
*item_ptr= item;
order->item=item_ptr;
order->free_me=0;
- thd->lex->proc_list.link_in_list((byte*) order,(byte**) &order->next);
+ thd->lex->proc_list.link_in_list((uchar*) order,(uchar**) &order->next);
return 0;
}
-/* Fix escaping of _, % and \ in database and table names (for ODBC) */
-
-static void remove_escape(char *name)
-{
- if (!*name) // For empty DB names
- return;
- char *to;
-#ifdef USE_MB
- char *strend=name+(uint) strlen(name);
-#endif
- for (to=name; *name ; name++)
- {
-#ifdef USE_MB
- int l;
- if (use_mb(system_charset_info) &&
- (l = my_ismbchar(system_charset_info, name, strend)))
- {
- while (l--)
- *to++ = *name++;
- name--;
- continue;
- }
-#endif
- if (*name == '\\' && name[1])
- name++; // Skip '\\'
- *to++= *name;
- }
- *to=0;
-}
-
-/****************************************************************************
-** save order by and tables in own lists
-****************************************************************************/
-
+/**
+ save order by and tables in own lists.
+*/
bool add_to_list(THD *thd, SQL_LIST &list,Item *item,bool asc)
{
@@ -6509,29 +6021,28 @@ bool add_to_list(THD *thd, SQL_LIST &list,Item *item,bool asc)
order->free_me=0;
order->used=0;
order->counter_used= 0;
- list.link_in_list((byte*) order,(byte**) &order->next);
+ list.link_in_list((uchar*) order,(uchar**) &order->next);
DBUG_RETURN(0);
}
-/*
- Add a table to list of used tables
-
- SYNOPSIS
- add_table_to_list()
- table Table to add
- alias alias for table (or null if no alias)
- table_options A set of the following bits:
- TL_OPTION_UPDATING Table will be updated
- TL_OPTION_FORCE_INDEX Force usage of index
- TL_OPTION_ALIAS an alias in multi table DELETE
- lock_type How table should be locked
- use_index List of indexed used in USE INDEX
- ignore_index List of indexed used in IGNORE INDEX
-
- RETURN
+/**
+ Add a table to list of used tables.
+
+ @param table Table to add
+ @param alias alias for table (or null if no alias)
+ @param table_options A set of the following bits:
+ - TL_OPTION_UPDATING : Table will be updated
+ - 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 use_index List of indexed used in USE INDEX
+ @param ignore_index List of indexed used in IGNORE INDEX
+
+ @retval
0 Error
- # Pointer to TABLE_LIST element added to the total table list
+ @retval
+ \# Pointer to TABLE_LIST element added to the total table list
*/
TABLE_LIST *st_select_lex::add_table_to_list(THD *thd,
@@ -6539,8 +6050,7 @@ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd,
LEX_STRING *alias,
ulong table_options,
thr_lock_type lock_type,
- List<String> *use_index_arg,
- List<String> *ignore_index_arg,
+ List<Index_hint> *index_hints_arg,
LEX_STRING *option)
{
register TABLE_LIST *ptr;
@@ -6560,6 +6070,13 @@ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd,
DBUG_RETURN(0);
}
+ if (table->is_derived_table() == FALSE && table->db.str &&
+ check_db_name(&table->db))
+ {
+ my_error(ER_WRONG_DB_NAME, MYF(0), table->db.str);
+ DBUG_RETURN(0);
+ }
+
if (!alias) /* Alias is case sensitive */
{
if (table->sel)
@@ -6568,18 +6085,13 @@ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd,
ER(ER_DERIVED_MUST_HAVE_ALIAS), MYF(0));
DBUG_RETURN(0);
}
- if (!(alias_str=thd->memdup(alias_str,table->table.length+1)))
+ if (!(alias_str= (char*) thd->memdup(alias_str,table->table.length+1)))
DBUG_RETURN(0);
}
if (!(ptr = (TABLE_LIST *) thd->calloc(sizeof(TABLE_LIST))))
DBUG_RETURN(0); /* purecov: inspected */
if (table->db.str)
{
- if (table->is_derived_table() == FALSE && check_db_name(table->db.str))
- {
- my_error(ER_WRONG_DB_NAME, MYF(0), table->db.str);
- DBUG_RETURN(0);
- }
ptr->db= table->db.str;
ptr->db_length= table->db.length;
}
@@ -6602,12 +6114,12 @@ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd,
ST_SCHEMA_TABLE *schema_table= find_schema_table(thd, ptr->table_name);
if (!schema_table ||
(schema_table->hidden &&
- (lex->orig_sql_command == SQLCOM_END || // not a 'show' command
+ ((sql_command_flags[lex->sql_command] & CF_STATUS_COMMAND) == 0 ||
/*
this check is used for show columns|keys from I_S hidden table
*/
- lex->orig_sql_command == SQLCOM_SHOW_FIELDS ||
- lex->orig_sql_command == SQLCOM_SHOW_KEYS)))
+ lex->sql_command == SQLCOM_SHOW_FIELDS ||
+ lex->sql_command == SQLCOM_SHOW_KEYS)))
{
my_error(ER_UNKNOWN_TABLE, MYF(0),
ptr->table_name, INFORMATION_SCHEMA_NAME.str);
@@ -6618,12 +6130,7 @@ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd,
}
ptr->select_lex= lex->current_select;
ptr->cacheable_table= 1;
- if (use_index_arg)
- ptr->use_index=(List<String> *) thd->memdup((gptr) use_index_arg,
- sizeof(*use_index_arg));
- if (ignore_index_arg)
- ptr->ignore_index=(List<String> *) thd->memdup((gptr) ignore_index_arg,
- sizeof(*ignore_index_arg));
+ ptr->index_hints= index_hints_arg;
ptr->option= option ? option->str : 0;
/* check that used name is unique */
if (lock_type != TL_IGNORE)
@@ -6670,7 +6177,7 @@ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd,
previous table reference to 'ptr'. Here we also add one element to the
list 'table_list'.
*/
- table_list.link_in_list((byte*) ptr, (byte**) &ptr->next_local);
+ table_list.link_in_list((uchar*) ptr, (uchar**) &ptr->next_local);
ptr->next_name_resolution_table= NULL;
/* Link table in global list (all used tables) */
lex->add_to_query_tables(ptr);
@@ -6678,14 +6185,9 @@ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd,
}
-/*
- Initialize a new table list for a nested join
-
- SYNOPSIS
- init_nested_join()
- thd current thread
+/**
+ Initialize a new table list for a nested join.
- DESCRIPTION
The function initializes a structure of the TABLE_LIST type
for a nested join. It sets up its nested join list as empty.
The created structure is added to the front of the current
@@ -6694,9 +6196,12 @@ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd,
created empty list after having saved the info on the old level
in the initialized structure.
- RETURN VALUE
- 0, if success
- 1, otherwise
+ @param thd current thread
+
+ @retval
+ 0 if success
+ @retval
+ 1 otherwise
*/
bool st_select_lex::init_nested_join(THD *thd)
@@ -6709,11 +6214,12 @@ bool st_select_lex::init_nested_join(THD *thd)
sizeof(NESTED_JOIN))))
DBUG_RETURN(1);
nested_join= ptr->nested_join=
- ((NESTED_JOIN*) ((byte*) ptr + ALIGN_SIZE(sizeof(TABLE_LIST))));
+ ((NESTED_JOIN*) ((uchar*) ptr + ALIGN_SIZE(sizeof(TABLE_LIST))));
join_list->push_front(ptr);
ptr->embedding= embedding;
ptr->join_list= join_list;
+ ptr->alias= (char*) "(nested_join)";
embedding= ptr;
join_list= &nested_join->join_list;
join_list->empty();
@@ -6721,21 +6227,18 @@ bool st_select_lex::init_nested_join(THD *thd)
}
-/*
- End a nested join table list
-
- SYNOPSIS
- end_nested_join()
- thd current thread
+/**
+ End a nested join table list.
- DESCRIPTION
The function returns to the previous join nest level.
If the current level contains only one member, the function
moves it one level up, eliminating the nest.
- RETURN VALUE
- Pointer to TABLE_LIST element added to the total table list, if success
- 0, otherwise
+ @param thd current thread
+
+ @return
+ - Pointer to TABLE_LIST element added to the total table list, if success
+ - 0, otherwise
*/
TABLE_LIST *st_select_lex::end_nested_join(THD *thd)
@@ -6767,20 +6270,17 @@ TABLE_LIST *st_select_lex::end_nested_join(THD *thd)
}
-/*
- Nest last join operation
-
- SYNOPSIS
- nest_last_join()
- thd current thread
+/**
+ Nest last join operation.
- DESCRIPTION
The function nest last join operation as if it was enclosed in braces.
- RETURN VALUE
- 0 Error
- # Pointer to TABLE_LIST element created for the new nested join
+ @param thd current thread
+ @retval
+ 0 Error
+ @retval
+ \# Pointer to TABLE_LIST element created for the new nested join
*/
TABLE_LIST *st_select_lex::nest_last_join(THD *thd)
@@ -6794,10 +6294,11 @@ TABLE_LIST *st_select_lex::nest_last_join(THD *thd)
sizeof(NESTED_JOIN))))
DBUG_RETURN(0);
nested_join= ptr->nested_join=
- ((NESTED_JOIN*) ((byte*) ptr + ALIGN_SIZE(sizeof(TABLE_LIST))));
+ ((NESTED_JOIN*) ((uchar*) ptr + ALIGN_SIZE(sizeof(TABLE_LIST))));
ptr->embedding= embedding;
ptr->join_list= join_list;
+ ptr->alias= (char*) "(nest_last_join)";
embedded_list= &nested_join->join_list;
embedded_list->empty();
@@ -6824,20 +6325,17 @@ TABLE_LIST *st_select_lex::nest_last_join(THD *thd)
}
-/*
- Add a table to the current join list
-
- SYNOPSIS
- add_joined_table()
- table the table to add
+/**
+ Add a table to the current join list.
- DESCRIPTION
The function puts a table in front of the current join list
of st_select_lex object.
Thus, joined tables are put into this list in the reverse order
(the most outer join operation follows first).
- RETURN VALUE
+ @param table the table to add
+
+ @return
None
*/
@@ -6851,14 +6349,9 @@ void st_select_lex::add_joined_table(TABLE_LIST *table)
}
-/*
- Convert a right join into equivalent left join
-
- SYNOPSIS
- convert_right_join()
- thd current thread
+/**
+ Convert a right join into equivalent left join.
- DESCRIPTION
The function takes the current join list t[0],t[1] ... and
effectively converts it into the list t[1],t[0] ...
Although the outer_join flag for the new nested table contains
@@ -6866,6 +6359,7 @@ void st_select_lex::add_joined_table(TABLE_LIST *table)
operation.
EXAMPLES
+ @verbatim
SELECT * FROM t1 RIGHT JOIN t2 ON on_expr =>
SELECT * FROM t2 LEFT JOIN t1 ON on_expr
@@ -6877,10 +6371,13 @@ void st_select_lex::add_joined_table(TABLE_LIST *table)
SELECT * FROM t1 LEFT JOIN t2 ON on_expr1 RIGHT JOIN t3 ON on_expr2 =>
SELECT * FROM t3 LEFT JOIN (t1 LEFT JOIN t2 ON on_expr2) ON on_expr1
+ @endverbatim
- RETURN
- Pointer to the table representing the inner table, if success
- 0, otherwise
+ @param thd current thread
+
+ @return
+ - Pointer to the table representing the inner table, if success
+ - 0, otherwise
*/
TABLE_LIST *st_select_lex::convert_right_join()
@@ -6896,14 +6393,12 @@ TABLE_LIST *st_select_lex::convert_right_join()
DBUG_RETURN(tab1);
}
-/*
- Set lock for all tables in current select level
+/**
+ Set lock for all tables in current select level.
- SYNOPSIS:
- set_lock_for_tables()
- lock_type Lock to set for tables
+ @param lock_type Lock to set for tables
- NOTE:
+ @note
If lock is a write lock, then tables->updating is set 1
This is to get tables_ok to know that the table is updated by the
query
@@ -6927,27 +6422,29 @@ void st_select_lex::set_lock_for_tables(thr_lock_type lock_type)
}
-/*
- Create a fake SELECT_LEX for a unit
-
- SYNOPSIS:
- add_fake_select_lex()
- thd thread handle
+/**
+ Create a fake SELECT_LEX for a unit.
- DESCRIPTION
The method create a fake SELECT_LEX object for a unit.
This object is created for any union construct containing a union
operation and also for any single select union construct of the form
+ @verbatim
(SELECT ... ORDER BY order_list [LIMIT n]) ORDER BY ...
+ @endvarbatim
or of the form
+ @varbatim
(SELECT ... ORDER BY LIMIT n) ORDER BY ...
+ @endvarbatim
- NOTES
+ @param thd_arg thread handle
+
+ @note
The object is used to retrieve rows from the temporary table
where the result on the union is obtained.
- RETURN VALUES
+ @retval
1 on failure to create the object
+ @retval
0 on success
*/
@@ -6972,7 +6469,7 @@ bool st_select_lex_unit::add_fake_select_lex(THD *thd_arg)
fake_select_lex->context.resolve_in_select_list= TRUE;
fake_select_lex->context.select_lex= fake_select_lex;
- if (!first_sl->next_select())
+ if (!is_union())
{
/*
This works only for
@@ -6989,24 +6486,22 @@ bool st_select_lex_unit::add_fake_select_lex(THD *thd_arg)
}
-/*
+/**
Push a new name resolution context for a JOIN ... ON clause to the
context stack of a query block.
- SYNOPSIS
- push_new_name_resolution_context()
- thd pointer to current thread
- left_op left operand of the JOIN
- right_op rigth operand of the JOIN
-
- DESCRIPTION
Create a new name resolution context for a JOIN ... ON clause,
set the first and last leaves of the list of table references
to be used for name resolution, and push the newly created
context to the stack of contexts of the query.
- RETURN
+ @param thd pointer to current thread
+ @param left_op left operand of the JOIN
+ @param right_op rigth operand of the JOIN
+
+ @retval
FALSE if all is OK
+ @retval
TRUE if a memory allocation error occured
*/
@@ -7026,19 +6521,17 @@ push_new_name_resolution_context(THD *thd,
}
-/*
+/**
Add an ON condition to the second operand of a JOIN ... ON.
- SYNOPSIS
- add_join_on
- b the second operand of a JOIN ... ON
- expr the condition to be added to the ON clause
-
- DESCRIPTION
Add an ON condition to the right operand of a JOIN ... ON clause.
- RETURN
+ @param b the second operand of a JOIN ... ON
+ @param expr the condition to be added to the ON clause
+
+ @retval
FALSE if there was some error
+ @retval
TRUE if all is OK
*/
@@ -7062,18 +6555,10 @@ void add_join_on(TABLE_LIST *b, Item *expr)
}
-/*
+/**
Mark that there is a NATURAL JOIN or JOIN ... USING between two
tables.
- SYNOPSIS
- add_join_natural()
- a Left join argument
- b Right join argument
- using_fields Field names from USING clause
- lex The current st_select_lex
-
- IMPLEMENTATION
This function marks that table b should be joined with a either via
a NATURAL JOIN or via JOIN ... USING. Both join types are special
cases of each other, so we treat them together. The function
@@ -7084,6 +6569,7 @@ void add_join_on(TABLE_LIST *b, Item *expr)
was an outer join.
EXAMPLE
+ @verbatim
SELECT * FROM t1 NATURAL LEFT JOIN t2
<=>
SELECT * FROM t1 LEFT JOIN t2 ON (t1.i=t2.i and t1.j=t2.j ... )
@@ -7095,9 +6581,11 @@ void add_join_on(TABLE_LIST *b, Item *expr)
SELECT * FROM t1 JOIN t2 USING(j) WHERE <some_cond>
<=>
SELECT * FROM t1, t2 WHERE (t1.j=t2.j and <some_cond>)
+ @endverbatim
- RETURN
- None
+ @param a Left join argument
+ @param b Right join argument
+ @param using_fields Field names from USING clause
*/
void add_join_natural(TABLE_LIST *a, TABLE_LIST *b, List<String> *using_fields,
@@ -7108,24 +6596,23 @@ void add_join_natural(TABLE_LIST *a, TABLE_LIST *b, List<String> *using_fields,
}
-/*
+/**
Reload/resets privileges and the different caches.
- SYNOPSIS
- reload_acl_and_cache()
- thd Thread handler (can be NULL!)
- options What should be reset/reloaded (tables, privileges,
- slave...)
- tables Tables to flush (if any)
- write_to_binlog 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
- 0 ok
- !=0 error. thd->killed or thd->net.report_error is set
+ @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 True 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,
@@ -7149,14 +6636,16 @@ bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables,
{
thd->thread_stack= (char*) &tmp_thd;
thd->store_globals();
+ lex_start(thd);
}
if (thd)
{
bool reload_acl_failed= acl_reload(thd);
bool reload_grants_failed= grant_reload(thd);
-
- if (reload_acl_failed || reload_grants_failed)
+ bool reload_servers_failed= servers_reload(thd);
+
+ if (reload_acl_failed || reload_grants_failed || reload_servers_failed)
{
result= 1;
/*
@@ -7181,7 +6670,8 @@ bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables,
{
/*
Flush the normal query log, the update log, the binary log,
- the slow query log, and the relay log (if it exists).
+ the slow query log, the relay log (if it exists) and the log
+ tables.
*/
/*
@@ -7191,8 +6681,6 @@ bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables,
than it would help them)
*/
tmp_write_to_binlog= 0;
- mysql_log.new_file(1);
- mysql_slow_log.new_file(1);
if( mysql_bin_log.is_open() )
{
mysql_bin_log.rotate_and_purge(RP_FORCE_ROTATE);
@@ -7202,7 +6690,11 @@ bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables,
rotate_relay_log(active_mi);
pthread_mutex_unlock(&LOCK_active_mi);
#endif
- if (ha_flush_logs())
+
+ /* flush slow and general logs */
+ logger.flush_logs(thd);
+
+ if (ha_flush_logs(NULL))
result=1;
if (flush_error_log())
result=1;
@@ -7252,8 +6744,8 @@ bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables,
tmp_write_to_binlog= 0;
if (lock_global_read_lock(thd))
return 1; // Killed
- if (close_cached_tables(thd,(options & REFRESH_FAST) ? 0 : 1,
- tables))
+ if (close_cached_tables(thd, tables, FALSE, (options & REFRESH_FAST) ?
+ FALSE : TRUE, TRUE))
result= 1;
if (make_global_read_lock_block_commit(thd)) // Killed
@@ -7265,7 +6757,8 @@ bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables,
}
else
{
- if (close_cached_tables(thd,(options & REFRESH_FAST) ? 0 : 1, tables))
+ if (close_cached_tables(thd, tables, FALSE, (options & REFRESH_FAST) ?
+ FALSE : TRUE, FALSE))
result= 1;
}
my_dbopt_cleanup();
@@ -7284,7 +6777,6 @@ bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables,
if (reset_master(thd))
{
result=1;
- thd->fatal_error(); // Ensure client get error
}
}
#endif
@@ -7306,31 +6798,35 @@ bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables,
}
#endif
if (options & REFRESH_USER_RESOURCES)
- reset_mqh((LEX_USER *) NULL);
+ reset_mqh((LEX_USER *) NULL, 0); /* purecov: inspected */
*write_to_binlog= tmp_write_to_binlog;
return result;
}
-/*
- kill on thread
- SYNOPSIS
- kill_one_thread()
- thd Thread class
- id Thread id
+/**
+ kill on thread.
- NOTES
+ @param thd Thread class
+ @param id Thread id
+ @param only_kill_query Should it kill the query or the connection
+
+ @note
This is written such that we have a short lock on LOCK_thread_count
*/
-void kill_one_thread(THD *thd, ulong id, bool only_kill_query)
+uint kill_one_thread(THD *thd, ulong id, bool only_kill_query)
{
THD *tmp;
uint error=ER_NO_SUCH_THREAD;
+ DBUG_ENTER("kill_one_thread");
+ DBUG_PRINT("enter", ("id=%lu only_kill=%d", id, only_kill_query));
VOID(pthread_mutex_lock(&LOCK_thread_count)); // For unlink from list
I_List_iterator<THD> it(threads);
while ((tmp=it++))
{
+ if (tmp->command == COM_DAEMON)
+ continue;
if (tmp->thread_id == id)
{
pthread_mutex_lock(&tmp->LOCK_delete); // Lock from delete
@@ -7350,18 +6846,35 @@ void kill_one_thread(THD *thd, ulong id, bool only_kill_query)
error=ER_KILL_DENIED_ERROR;
pthread_mutex_unlock(&tmp->LOCK_delete);
}
+ DBUG_PRINT("exit", ("%d", error));
+ DBUG_RETURN(error);
+}
+
- if (!error)
- send_ok(thd);
+/*
+ kills a thread and sends response
+
+ SYNOPSIS
+ sql_kill()
+ thd Thread class
+ id Thread id
+ only_kill_query Should it kill the query or the connection
+*/
+
+void sql_kill(THD *thd, ulong id, bool only_kill_query)
+{
+ uint error;
+ if (!(error= kill_one_thread(thd, id, only_kill_query)))
+ my_ok(thd);
else
my_error(error, MYF(0), id);
}
- /* If pointer is not a null pointer, append filename to it */
+/** If pointer is not a null pointer, append filename to it. */
-static 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)
{
char buff[FN_REFLEN],*ptr, *end;
if (!*filename_ptr)
@@ -7377,7 +6890,7 @@ static bool append_file_to_dir(THD *thd, const char **filename_ptr,
/* Fix is using unix filename format on dos */
strmov(buff,*filename_ptr);
end=convert_dirname(buff, *filename_ptr, NullS);
- if (!(ptr=thd->alloc((uint) (end-buff)+(uint) strlen(table_name)+1)))
+ if (!(ptr= (char*) thd->alloc((size_t) (end-buff) + strlen(table_name)+1)))
return 1; // End of memory
*filename_ptr=ptr;
strxmov(ptr,buff,table_name,NullS);
@@ -7385,14 +6898,12 @@ static bool append_file_to_dir(THD *thd, const char **filename_ptr,
}
-/*
- Check if the select is a simple select (not an union)
-
- SYNOPSIS
- check_simple_select()
+/**
+ Check if the select is a simple select (not an union).
- RETURN VALUES
+ @retval
0 ok
+ @retval
1 error ; In this case the error messege is sent to the client
*/
@@ -7449,17 +6960,15 @@ Comp_creator *comp_ne_creator(bool invert)
}
-/*
- Construct ALL/ANY/SOME subquery Item
+/**
+ Construct ALL/ANY/SOME subquery Item.
- SYNOPSIS
- all_any_subquery_creator()
- left_expr - pointer to left expression
- cmp - compare function creator
- all - true if we create ALL subquery
- select_lex - pointer on parsed subquery structure
+ @param left_expr pointer to left expression
+ @param cmp compare function creator
+ @param all true if we create ALL subquery
+ @param select_lex pointer on parsed subquery structure
- RETURN VALUE
+ @return
constructed Item (or 0 if out of memory)
*/
Item * all_any_subquery_creator(Item *left_expr,
@@ -7482,16 +6991,15 @@ Item * all_any_subquery_creator(Item *left_expr,
}
-/*
- Multi update query pre-check
+/**
+ Multi update query pre-check.
- SYNOPSIS
- multi_update_precheck()
- thd Thread handler
- tables Global/local table list (have to be the same)
+ @param thd Thread handler
+ @param tables Global/local table list (have to be the same)
- RETURN VALUE
+ @retval
FALSE OK
+ @retval
TRUE Error
*/
@@ -7519,12 +7027,11 @@ bool multi_update_precheck(THD *thd, TABLE_LIST *tables)
else if ((check_access(thd, UPDATE_ACL, table->db,
&table->grant.privilege, 0, 1,
test(table->schema_table)) ||
- grant_option &&
check_grant(thd, UPDATE_ACL, table, 0, 1, 1)) &&
(check_access(thd, SELECT_ACL, table->db,
&table->grant.privilege, 0, 0,
test(table->schema_table)) ||
- grant_option && check_grant(thd, SELECT_ACL, table, 0, 1, 0)))
+ check_grant(thd, SELECT_ACL, table, 0, 1, 0)))
DBUG_RETURN(TRUE);
table->table_in_first_from_clause= 1;
@@ -7532,19 +7039,17 @@ bool multi_update_precheck(THD *thd, TABLE_LIST *tables)
/*
Is there tables of subqueries?
*/
- if (&lex->select_lex != lex->all_selects_list || lex->time_zone_tables_used)
+ if (&lex->select_lex != lex->all_selects_list)
{
DBUG_PRINT("info",("Checking sub query list"));
for (table= tables; table; table= table->next_global)
{
- if (!my_tz_check_n_skip_implicit_tables(&table,
- lex->time_zone_tables_used) &&
- !table->table_in_first_from_clause)
+ if (!table->table_in_first_from_clause)
{
if (check_access(thd, SELECT_ACL, table->db,
&table->grant.privilege, 0, 0,
test(table->schema_table)) ||
- grant_option && check_grant(thd, SELECT_ACL, table, 0, 1, 0))
+ check_grant(thd, SELECT_ACL, table, 0, 1, 0))
DBUG_RETURN(TRUE);
}
}
@@ -7562,16 +7067,15 @@ bool multi_update_precheck(THD *thd, TABLE_LIST *tables)
DBUG_RETURN(FALSE);
}
-/*
- Multi delete query pre-check
+/**
+ Multi delete query pre-check.
- SYNOPSIS
- multi_delete_precheck()
- thd Thread handler
- tables Global/local table list
+ @param thd Thread handler
+ @param tables Global/local table list
- RETURN VALUE
+ @retval
FALSE OK
+ @retval
TRUE error
*/
@@ -7585,8 +7089,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_db_used(thd, tables) || check_db_used(thd,aux_tables) ||
- check_table_access(thd, SELECT_ACL, tables, 0))
+ if (check_table_access(thd, SELECT_ACL, tables, UINT_MAX, FALSE))
DBUG_RETURN(TRUE);
/*
@@ -7595,7 +7098,7 @@ bool multi_delete_precheck(THD *thd, TABLE_LIST *tables)
call check_table_access() safely.
*/
thd->lex->query_tables_own_last= 0;
- if (check_table_access(thd, DELETE_ACL, aux_tables, 0))
+ if (check_table_access(thd, DELETE_ACL, aux_tables, UINT_MAX, FALSE))
{
thd->lex->query_tables_own_last= save_query_tables_own_last;
DBUG_RETURN(TRUE);
@@ -7612,17 +7115,16 @@ bool multi_delete_precheck(THD *thd, TABLE_LIST *tables)
}
-/*
+/**
Link tables in auxilary table list of multi-delete with corresponding
elements in main table list, and set proper locks for them.
- SYNOPSIS
- multi_delete_set_locks_and_link_aux_tables()
- lex - pointer to LEX representing multi-delete
+ @param lex pointer to LEX representing multi-delete
- RETURN VALUE
- FALSE - success
- TRUE - error
+ @retval
+ FALSE success
+ @retval
+ TRUE error
*/
bool multi_delete_set_locks_and_link_aux_tables(LEX *lex)
@@ -7665,16 +7167,15 @@ bool multi_delete_set_locks_and_link_aux_tables(LEX *lex)
}
-/*
- simple UPDATE query pre-check
+/**
+ simple UPDATE query pre-check.
- SYNOPSIS
- update_precheck()
- thd Thread handler
- tables Global table list
+ @param thd Thread handler
+ @param tables Global table list
- RETURN VALUE
+ @retval
FALSE OK
+ @retval
TRUE Error
*/
@@ -7686,21 +7187,19 @@ bool update_precheck(THD *thd, TABLE_LIST *tables)
my_message(ER_WRONG_VALUE_COUNT, ER(ER_WRONG_VALUE_COUNT), MYF(0));
DBUG_RETURN(TRUE);
}
- DBUG_RETURN(check_db_used(thd, tables) ||
- check_one_table_access(thd, UPDATE_ACL, tables));
+ DBUG_RETURN(check_one_table_access(thd, UPDATE_ACL, tables));
}
-/*
- simple DELETE query pre-check
+/**
+ simple DELETE query pre-check.
- SYNOPSIS
- delete_precheck()
- thd Thread handler
- tables Global table list
+ @param thd Thread handler
+ @param tables Global table list
- RETURN VALUE
+ @retval
FALSE OK
+ @retval
TRUE error
*/
@@ -7715,16 +7214,15 @@ bool delete_precheck(THD *thd, TABLE_LIST *tables)
}
-/*
- simple INSERT query pre-check
+/**
+ simple INSERT query pre-check.
- SYNOPSIS
- insert_precheck()
- thd Thread handler
- tables Global table list
+ @param thd Thread handler
+ @param tables Global table list
- RETURN VALUE
+ @retval
FALSE OK
+ @retval
TRUE error
*/
@@ -7749,20 +7247,18 @@ bool insert_precheck(THD *thd, TABLE_LIST *tables)
my_message(ER_WRONG_VALUE_COUNT, ER(ER_WRONG_VALUE_COUNT), MYF(0));
DBUG_RETURN(TRUE);
}
- if (check_db_used(thd, tables))
- DBUG_RETURN(TRUE);
DBUG_RETURN(FALSE);
}
/**
- @brief Check privileges for SHOW CREATE TABLE statement.
+ @brief Check privileges for SHOW CREATE TABLE statement.
- @param thd Thread context
- @param table Target table
+ @param thd Thread context
+ @param table Target table
- @retval TRUE Failure
- @retval FALSE Success
+ @retval TRUE Failure
+ @retval FALSE Success
*/
static bool check_show_create_table_access(THD *thd, TABLE_LIST *table)
@@ -7770,21 +7266,20 @@ static bool check_show_create_table_access(THD *thd, TABLE_LIST *table)
return check_access(thd, SELECT_ACL | EXTRA_ACL, table->db,
&table->grant.privilege, 0, 0,
test(table->schema_table)) ||
- grant_option && check_grant(thd, SELECT_ACL, table, 2, UINT_MAX, 0);
+ check_grant(thd, SELECT_ACL, table, 2, UINT_MAX, 0);
}
-/*
- CREATE TABLE query pre-check
+/**
+ CREATE TABLE query pre-check.
- SYNOPSIS
- create_table_precheck()
- thd Thread handler
- tables Global table list
- create_table Table which will be created
+ @param thd Thread handler
+ @param tables Global table list
+ @param create_table Table which will be created
- RETURN VALUE
+ @retval
FALSE OK
+ @retval
TRUE Error
*/
@@ -7797,8 +7292,15 @@ bool create_table_precheck(THD *thd, TABLE_LIST *tables,
bool error= TRUE; // Error message is given
DBUG_ENTER("create_table_precheck");
+ /*
+ Require CREATE [TEMPORARY] privilege on new table; for
+ CREATE TABLE ... SELECT, also require INSERT.
+ */
+
want_priv= ((lex->create_info.options & HA_LEX_CREATE_TMP_TABLE) ?
- CREATE_TMP_ACL : CREATE_ACL);
+ CREATE_TMP_ACL : CREATE_ACL) |
+ (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)) ||
@@ -7806,7 +7308,7 @@ bool create_table_precheck(THD *thd, TABLE_LIST *tables,
(TABLE_LIST *)
lex->create_info.merge_list.first))
goto err;
- if (grant_option && want_priv != CREATE_TMP_ACL &&
+ if (want_priv != CREATE_TMP_ACL &&
check_grant(thd, want_priv, create_table, 0, 1, 0))
goto err;
@@ -7817,7 +7319,7 @@ bool create_table_precheck(THD *thd, TABLE_LIST *tables,
#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, becasue we on execute we have to check that
+ 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).
*/
@@ -7835,7 +7337,7 @@ bool create_table_precheck(THD *thd, TABLE_LIST *tables,
}
}
#endif
- if (tables && check_table_access(thd, SELECT_ACL, tables,0))
+ if (tables && check_table_access(thd, SELECT_ACL, tables, UINT_MAX, FALSE))
goto err;
}
else if (lex->create_info.options & HA_LEX_CREATE_TABLE_LIKE)
@@ -7850,15 +7352,13 @@ err:
}
-/*
- negate given expression
+/**
+ negate given expression.
- SYNOPSIS
- negate_expression()
- thd thread handler
- expr expression for negation
+ @param thd thread handler
+ @param expr expression for negation
- RETURN
+ @return
negated expression
*/
@@ -7885,14 +7385,12 @@ Item *negate_expression(THD *thd, Item *expr)
return new Item_func_not(expr);
}
-/*
- Set the specified definer to the default value, which is the current user in
- the thread.
+/**
+ Set the specified definer to the default value, which is the
+ current user in the thread.
- SYNOPSIS
- get_default_definer()
- thd [in] thread handler
- definer [out] definer
+ @param[in] thd thread handler
+ @param[out] definer definer
*/
void get_default_definer(THD *thd, LEX_USER *definer)
@@ -7900,24 +7398,22 @@ void get_default_definer(THD *thd, LEX_USER *definer)
const Security_context *sctx= thd->security_ctx;
definer->user.str= (char *) sctx->priv_user;
- definer->user.length= (uint) strlen(definer->user.str);
+ definer->user.length= strlen(definer->user.str);
definer->host.str= (char *) sctx->priv_host;
- definer->host.length= (uint) strlen(definer->host.str);
+ definer->host.length= strlen(definer->host.str);
}
-/*
+/**
Create default definer for the specified THD.
- SYNOPSIS
- create_default_definer()
- thd [in] thread handler
+ @param[in] thd thread handler
- RETURN
- On success, return a valid pointer to the created and initialized
+ @return
+ - On success, return a valid pointer to the created and initialized
LEX_USER, which contains definer information.
- On error, return 0.
+ - On error, return 0.
*/
LEX_USER *create_default_definer(THD *thd)
@@ -7933,19 +7429,17 @@ LEX_USER *create_default_definer(THD *thd)
}
-/*
+/**
Create definer with the given user and host names.
- SYNOPSIS
- create_definer()
- thd [in] thread handler
- user_name [in] user name
- host_name [in] host name
+ @param[in] thd thread handler
+ @param[in] user_name user name
+ @param[in] host_name host name
- RETURN
- On success, return a valid pointer to the created and initialized
+ @return
+ - On success, return a valid pointer to the created and initialized
LEX_USER, which contains definer information.
- On error, return 0.
+ - On error, return 0.
*/
LEX_USER *create_definer(THD *thd, LEX_STRING *user_name, LEX_STRING *host_name)
@@ -7964,18 +7458,16 @@ LEX_USER *create_definer(THD *thd, LEX_STRING *user_name, LEX_STRING *host_name)
}
-/*
+/**
Retuns information about user or current user.
- SYNOPSIS
- get_current_user()
- thd [in] thread handler
- user [in] user
+ @param[in] thd thread handler
+ @param[in] user user
- RETURN
- On success, return a valid pointer to initialized
+ @return
+ - On success, return a valid pointer to initialized
LEX_USER, which contains user information.
- On error, return 0.
+ - On error, return 0.
*/
LEX_USER *get_current_user(THD *thd, LEX_USER *user)
@@ -7987,36 +7479,70 @@ LEX_USER *get_current_user(THD *thd, LEX_USER *user)
}
-/*
- Check that length of a string does not exceed some limit.
+/**
+ Check that byte length of a string does not exceed some limit.
- SYNOPSIS
- check_string_length()
- str string to be checked
- err_msg error message to be displayed if the string is too long
- max_length max length
+ @param str string to be checked
+ @param err_msg error message to be displayed if the string is too long
+ @param max_length max length
- RETURN
+ @retval
FALSE the passed string is not longer than max_length
+ @retval
TRUE the passed string is longer than max_length
+
+ NOTE
+ The function is not used in existing code but can be useful later?
*/
-bool check_string_length(LEX_STRING *str, const char *err_msg,
- uint max_length)
+bool check_string_byte_length(LEX_STRING *str, const char *err_msg,
+ uint max_byte_length)
{
- if (str->length <= max_length)
+ if (str->length <= max_byte_length)
return FALSE;
- my_error(ER_WRONG_STRING_LENGTH, MYF(0), str->str, err_msg, max_length);
+ my_error(ER_WRONG_STRING_LENGTH, MYF(0), str->str, err_msg, max_byte_length);
return TRUE;
}
/*
- Check if path does not contain mysql data home directory
+ Check that char length of a string does not exceed some limit.
SYNOPSIS
+ check_string_char_length()
+ str string to be checked
+ err_msg error message to be displayed if the string is too long
+ max_char_length max length in symbols
+ cs string charset
+
+ RETURN
+ FALSE the passed string is not longer than max_char_length
+ TRUE the passed string is longer than max_char_length
+*/
+
+
+bool check_string_char_length(LEX_STRING *str, const char *err_msg,
+ uint max_char_length, CHARSET_INFO *cs,
+ bool no_error)
+{
+ int well_formed_error;
+ uint res= cs->cset->well_formed_len(cs, str->str, str->str + str->length,
+ max_char_length, &well_formed_error);
+
+ if (!well_formed_error && str->length == res)
+ return FALSE;
+
+ if (!no_error)
+ my_error(ER_WRONG_STRING_LENGTH, MYF(0), str->str, err_msg, max_char_length);
+ return TRUE;
+}
+
+
+/*
+ Check if path does not contain mysql data home directory
+ SYNOPSIS
test_if_data_home_dir()
dir directory
conv_home_dir converted data home directory
@@ -8026,7 +7552,6 @@ bool check_string_length(LEX_STRING *str, const char *err_msg,
0 ok
1 error
*/
-
C_MODE_START
int test_if_data_home_dir(const char *dir)
@@ -8080,7 +7605,7 @@ bool check_host_name(LEX_STRING *str)
{
const char *name= str->str;
const char *end= str->str + str->length;
- if (check_string_length(str, ER(ER_HOSTNAME), HOSTNAME_LENGTH))
+ if (check_string_byte_length(str, ER(ER_HOSTNAME), HOSTNAME_LENGTH))
return TRUE;
while (name != end)
@@ -8096,3 +7621,64 @@ bool check_host_name(LEX_STRING *str)
}
return FALSE;
}
+
+
+extern int MYSQLparse(void *thd); // from sql_yacc.cc
+
+
+/**
+ This is a wrapper of MYSQLparse(). All the code should call parse_sql()
+ instead of MYSQLparse().
+
+ @param thd Thread context.
+ @param parser_state Parser state.
+ @param creation_ctx Object creation context.
+
+ @return Error status.
+ @retval FALSE on success.
+ @retval TRUE on parsing error.
+*/
+
+bool parse_sql(THD *thd,
+ Parser_state *parser_state,
+ Object_creation_ctx *creation_ctx)
+{
+ DBUG_ASSERT(thd->m_parser_state == NULL);
+
+ /* Backup creation context. */
+
+ Object_creation_ctx *backup_ctx= NULL;
+
+ if (creation_ctx)
+ backup_ctx= creation_ctx->set_n_backup(thd);
+
+ /* Set parser state. */
+
+ thd->m_parser_state= parser_state;
+
+ /* Parse the query. */
+
+ bool mysql_parse_status= MYSQLparse(thd) != 0;
+
+ /* Check that if MYSQLparse() failed, thd->is_error() is set. */
+
+ DBUG_ASSERT(!mysql_parse_status ||
+ mysql_parse_status && thd->is_error());
+
+ /* Reset parser state. */
+
+ thd->m_parser_state= NULL;
+
+ /* Restore creation context. */
+
+ if (creation_ctx)
+ creation_ctx->restore_env(thd, backup_ctx);
+
+ /* That's it. */
+
+ return mysql_parse_status || thd->is_fatal_error;
+}
+
+/**
+ @} (end of group Runtime_Environment)
+*/
diff --git a/sql/sql_partition.cc b/sql/sql_partition.cc
new file mode 100644
index 00000000000..ae55c194c3c
--- /dev/null
+++ b/sql/sql_partition.cc
@@ -0,0 +1,7126 @@
+/* Copyright 2005-2008 MySQL AB, 2008 Sun Microsystems, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+/*
+ This file is a container for general functionality related
+ 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.
+
+ The first version was written by Mikael Ronstrom.
+
+ This 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.
+*/
+
+/* Some general useful functions */
+
+#define MYSQL_LEX 1
+#include "mysql_priv.h"
+#include <errno.h>
+#include <m_ctype.h>
+#include "my_md5.h"
+
+#ifdef WITH_PARTITION_STORAGE_ENGINE
+#include "ha_partition.h"
+/*
+ Partition related functions declarations and some static constants;
+*/
+const LEX_STRING partition_keywords[]=
+{
+ { C_STRING_WITH_LEN("HASH") },
+ { C_STRING_WITH_LEN("RANGE") },
+ { C_STRING_WITH_LEN("LIST") },
+ { C_STRING_WITH_LEN("KEY") },
+ { C_STRING_WITH_LEN("MAXVALUE") },
+ { C_STRING_WITH_LEN("LINEAR ") }
+};
+static const char *part_str= "PARTITION";
+static const char *sub_str= "SUB";
+static const char *by_str= "BY";
+static const char *space_str= " ";
+static const char *equal_str= "=";
+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(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);
+int get_partition_id_hash_nosub(partition_info *part_info,
+ uint32 *part_id,
+ longlong *func_value);
+int get_partition_id_key_nosub(partition_info *part_info,
+ uint32 *part_id,
+ longlong *func_value);
+int get_partition_id_linear_hash_nosub(partition_info *part_info,
+ uint32 *part_id,
+ longlong *func_value);
+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_hash_sub(partition_info *part_info,
+ uint32 *part_id);
+int get_partition_id_key_sub(partition_info *part_info,
+ uint32 *part_id);
+int get_partition_id_linear_hash_sub(partition_info *part_info,
+ uint32 *part_id);
+int get_partition_id_linear_key_sub(partition_info *part_info,
+ uint32 *part_id);
+static uint32 get_next_partition_via_walking(PARTITION_ITERATOR*);
+static void set_up_range_analysis_info(partition_info *part_info);
+static uint32 get_next_subpartition_via_walking(PARTITION_ITERATOR*);
+#endif
+
+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,
+ uchar *min_value, uchar *max_value,
+ uint flags,
+ PARTITION_ITERATOR *part_iter);
+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);
+
+#ifdef WITH_PARTITION_STORAGE_ENGINE
+/*
+ A support function to check if a name is in a list of strings
+
+ SYNOPSIS
+ is_name_in_list()
+ name String searched for
+ list_names A list of names searched in
+
+ RETURN VALUES
+ TRUE String found
+ FALSE String not found
+*/
+
+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 i= 0;
+
+ do
+ {
+ char *list_name= names_it++;
+ if (!(my_strcasecmp(system_charset_info, name, list_name)))
+ return TRUE;
+ } while (++i < no_names);
+ return FALSE;
+}
+
+
+
+/*
+ Set-up defaults for partitions.
+
+ SYNOPSIS
+ partition_default_handling()
+ table Table object
+ part_info Partition info to set up
+ is_create_table_ind Is this part of a table creation
+ normalized_path Normalized path name of table and database
+
+ RETURN VALUES
+ TRUE Error
+ FALSE Success
+*/
+
+bool partition_default_handling(TABLE *table, partition_info *part_info,
+ bool is_create_table_ind,
+ const char *normalized_path)
+{
+ DBUG_ENTER("partition_default_handling");
+
+ if (part_info->use_default_no_partitions)
+ {
+ if (!is_create_table_ind &&
+ table->file->get_no_parts(normalized_path, &part_info->no_parts))
+ {
+ DBUG_RETURN(TRUE);
+ }
+ }
+ else if (part_info->is_sub_partitioned() &&
+ part_info->use_default_no_subpartitions)
+ {
+ uint no_parts;
+ if (!is_create_table_ind &&
+ (table->file->get_no_parts(normalized_path, &no_parts)))
+ {
+ DBUG_RETURN(TRUE);
+ }
+ DBUG_ASSERT(part_info->no_parts > 0);
+ part_info->no_subparts= no_parts / part_info->no_parts;
+ DBUG_ASSERT((no_parts % part_info->no_parts) == 0);
+ }
+ part_info->set_up_defaults_for_partitioning(table->file,
+ (ulonglong)0, (uint)0);
+ DBUG_RETURN(FALSE);
+}
+
+
+/*
+ Check that the reorganized table will not have duplicate partitions.
+
+ SYNOPSIS
+ check_reorganise_list()
+ new_part_info New partition info
+ old_part_info Old partition info
+ list_part_names The list of partition names that will go away and
+ can be reused in the new table.
+
+ RETURN VALUES
+ TRUE Inacceptable name conflict detected.
+ FALSE New names are OK.
+
+ DESCRIPTION
+ Can handle that the 'new_part_info' and 'old_part_info' the same
+ in which case it checks that the list of names in the partitions
+ doesn't contain any duplicated names.
+*/
+
+bool check_reorganise_list(partition_info *new_part_info,
+ partition_info *old_part_info,
+ List<char> list_part_names)
+{
+ uint new_count, old_count;
+ uint no_new_parts= new_part_info->partitions.elements;
+ uint no_old_parts= old_part_info->partitions.elements;
+ List_iterator<partition_element> new_parts_it(new_part_info->partitions);
+ bool same_part_info= (new_part_info == old_part_info);
+ DBUG_ENTER("check_reorganise_list");
+
+ new_count= 0;
+ do
+ {
+ List_iterator<partition_element> old_parts_it(old_part_info->partitions);
+ char *new_name= (new_parts_it++)->partition_name;
+ new_count++;
+ old_count= 0;
+ do
+ {
+ char *old_name= (old_parts_it++)->partition_name;
+ old_count++;
+ if (same_part_info && old_count == new_count)
+ break;
+ if (!(my_strcasecmp(system_charset_info, old_name, new_name)))
+ {
+ if (!is_name_in_list(old_name, list_part_names))
+ DBUG_RETURN(TRUE);
+ }
+ } while (old_count < no_old_parts);
+ } while (new_count < no_new_parts);
+ DBUG_RETURN(FALSE);
+}
+
+
+/*
+ A useful routine used by update_row for partition handlers to calculate
+ the partition ids of the old and the new record.
+
+ SYNOPSIS
+ get_part_for_update()
+ old_data Buffer of old record
+ new_data Buffer of new record
+ rec0 Reference to table->record[0]
+ part_info Reference to partition information
+ out:old_part_id The returned partition id of old record
+ out:new_part_id The returned partition id of new record
+
+ RETURN VALUE
+ 0 Success
+ > 0 Error code
+*/
+
+int get_parts_for_update(const uchar *old_data, uchar *new_data,
+ const uchar *rec0, partition_info *part_info,
+ uint32 *old_part_id, uint32 *new_part_id,
+ longlong *new_func_value)
+{
+ Field **part_field_array= part_info->full_part_field_array;
+ int error;
+ longlong old_func_value;
+ DBUG_ENTER("get_parts_for_update");
+
+ DBUG_ASSERT(new_data == rec0);
+ set_field_ptr(part_field_array, old_data, rec0);
+ error= part_info->get_partition_id(part_info, old_part_id,
+ &old_func_value);
+ set_field_ptr(part_field_array, rec0, old_data);
+ if (unlikely(error)) // Should never happen
+ {
+ DBUG_ASSERT(0);
+ DBUG_RETURN(error);
+ }
+#ifdef NOT_NEEDED
+ if (new_data == rec0)
+#endif
+ {
+ if (unlikely(error= part_info->get_partition_id(part_info,
+ new_part_id,
+ new_func_value)))
+ {
+ DBUG_RETURN(error);
+ }
+ }
+#ifdef NOT_NEEDED
+ else
+ {
+ /*
+ This branch should never execute but it is written anyways for
+ future use. It will be tested by ensuring that the above
+ condition is false in one test situation before pushing the code.
+ */
+ set_field_ptr(part_field_array, new_data, rec0);
+ error= part_info->get_partition_id(part_info, new_part_id,
+ new_func_value);
+ set_field_ptr(part_field_array, rec0, new_data);
+ if (unlikely(error))
+ {
+ DBUG_RETURN(error);
+ }
+ }
+#endif
+ DBUG_RETURN(0);
+}
+
+
+/*
+ A useful routine used by delete_row for partition handlers to calculate
+ the partition id.
+
+ SYNOPSIS
+ get_part_for_delete()
+ buf Buffer of old record
+ rec0 Reference to table->record[0]
+ part_info Reference to partition information
+ out:part_id The returned partition id to delete from
+
+ RETURN VALUE
+ 0 Success
+ > 0 Error code
+
+ DESCRIPTION
+ Dependent on whether buf is not record[0] we need to prepare the
+ fields. Then we call the function pointer get_partition_id to
+ calculate the partition id.
+*/
+
+int get_part_for_delete(const uchar *buf, const uchar *rec0,
+ partition_info *part_info, uint32 *part_id)
+{
+ int error;
+ longlong func_value;
+ DBUG_ENTER("get_part_for_delete");
+
+ if (likely(buf == rec0))
+ {
+ if (unlikely((error= part_info->get_partition_id(part_info, part_id,
+ &func_value))))
+ {
+ DBUG_RETURN(error);
+ }
+ DBUG_PRINT("info", ("Delete from partition %d", *part_id));
+ }
+ else
+ {
+ Field **part_field_array= part_info->full_part_field_array;
+ set_field_ptr(part_field_array, buf, rec0);
+ error= part_info->get_partition_id(part_info, part_id, &func_value);
+ set_field_ptr(part_field_array, rec0, buf);
+ if (unlikely(error))
+ {
+ DBUG_RETURN(error);
+ }
+ DBUG_PRINT("info", ("Delete from partition %d (path2)", *part_id));
+ }
+ DBUG_RETURN(0);
+}
+
+
+/*
+ This method is used to set-up both partition and subpartitioning
+ field array and used for all types of partitioning.
+ It is part of the logic around fix_partition_func.
+
+ SYNOPSIS
+ set_up_field_array()
+ table TABLE object for which partition fields are set-up
+ sub_part Is the table subpartitioned as well
+
+ RETURN VALUE
+ TRUE Error, some field didn't meet requirements
+ FALSE Ok, partition field array set-up
+
+ DESCRIPTION
+
+ A great number of functions below here is part of the fix_partition_func
+ method. It is used to set up the partition structures for execution from
+ openfrm. It is called at the end of the openfrm when the table struct has
+ been set-up apart from the partition information.
+ It involves:
+ 1) Setting arrays of fields for the partition functions.
+ 2) Setting up binary search array for LIST partitioning
+ 3) Setting up array for binary search for RANGE partitioning
+ 4) Setting up key_map's to assist in quick evaluation whether one
+ can deduce anything from a given index of what partition to use
+ 5) Checking whether a set of partitions can be derived from a range on
+ a field in the partition function.
+ As part of doing this there is also a great number of error controls.
+ This is actually the place where most of the things are checked for
+ partition information when creating a table.
+ Things that are checked includes
+ 1) All fields of partition function in Primary keys and unique indexes
+ (if not supported)
+
+
+ Create an array of partition fields (NULL terminated). Before this method
+ is called fix_fields or find_table_in_sef has been called to set
+ GET_FIXED_FIELDS_FLAG on all fields that are part of the partition
+ function.
+*/
+
+static bool set_up_field_array(TABLE *table,
+ bool is_sub_part)
+{
+ Field **ptr, *field, **field_array;
+ uint no_fields= 0;
+ uint size_field_array;
+ uint i= 0;
+ partition_info *part_info= table->part_info;
+ int result= FALSE;
+ DBUG_ENTER("set_up_field_array");
+
+ ptr= table->field;
+ while ((field= *(ptr++)))
+ {
+ if (field->flags & GET_FIXED_FIELDS_FLAG)
+ no_fields++;
+ }
+ if (no_fields == 0)
+ {
+ /*
+ We are using hidden key as partitioning field
+ */
+ DBUG_ASSERT(!is_sub_part);
+ DBUG_RETURN(result);
+ }
+ size_field_array= (no_fields+1)*sizeof(Field*);
+ field_array= (Field**)sql_alloc(size_field_array);
+ if (unlikely(!field_array))
+ {
+ mem_alloc_error(size_field_array);
+ result= TRUE;
+ }
+ ptr= table->field;
+ while ((field= *(ptr++)))
+ {
+ if (field->flags & GET_FIXED_FIELDS_FLAG)
+ {
+ field->flags&= ~GET_FIXED_FIELDS_FLAG;
+ field->flags|= FIELD_IN_PART_FUNC_FLAG;
+ if (likely(!result))
+ {
+ field_array[i++]= field;
+
+ /*
+ We check that the fields are proper. It is required for each
+ field in a partition function to:
+ 1) Not be a BLOB of any type
+ A BLOB takes too long time to evaluate so we don't want it for
+ performance reasons.
+ */
+
+ if (unlikely(field->flags & BLOB_FLAG))
+ {
+ my_error(ER_BLOB_FIELD_IN_PART_FUNC_ERROR, MYF(0));
+ result= TRUE;
+ }
+ }
+ }
+ }
+ field_array[no_fields]= 0;
+ if (!is_sub_part)
+ {
+ part_info->part_field_array= field_array;
+ part_info->no_part_fields= no_fields;
+ }
+ else
+ {
+ part_info->subpart_field_array= field_array;
+ part_info->no_subpart_fields= no_fields;
+ }
+ DBUG_RETURN(result);
+}
+
+
+
+/*
+ Create a field array including all fields of both the partitioning and the
+ subpartitioning functions.
+
+ SYNOPSIS
+ create_full_part_field_array()
+ thd Thread handle
+ table TABLE object for which partition fields are set-up
+ part_info Reference to partitioning data structure
+
+ RETURN VALUE
+ TRUE Memory allocation of field array failed
+ FALSE Ok
+
+ DESCRIPTION
+ If there is no subpartitioning then the same array is used as for the
+ partitioning. Otherwise a new array is built up using the flag
+ FIELD_IN_PART_FUNC in the field object.
+ This function is called from fix_partition_func
+*/
+
+static bool create_full_part_field_array(THD *thd, TABLE *table,
+ partition_info *part_info)
+{
+ bool result= FALSE;
+ Field **ptr;
+ my_bitmap_map *bitmap_buf;
+ DBUG_ENTER("create_full_part_field_array");
+
+ 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;
+ }
+ else
+ {
+ Field *field, **field_array;
+ uint no_part_fields=0, size_field_array;
+ ptr= table->field;
+ while ((field= *(ptr++)))
+ {
+ if (field->flags & FIELD_IN_PART_FUNC_FLAG)
+ no_part_fields++;
+ }
+ size_field_array= (no_part_fields+1)*sizeof(Field*);
+ field_array= (Field**)sql_alloc(size_field_array);
+ if (unlikely(!field_array))
+ {
+ mem_alloc_error(size_field_array);
+ result= TRUE;
+ goto end;
+ }
+ no_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[no_part_fields]=0;
+ part_info->full_part_field_array= field_array;
+ part_info->no_full_part_fields= no_part_fields;
+ }
+
+ /*
+ Initialize the set of all fields used in partition and subpartition
+ expression. Required for testing of partition fields in write_set
+ when updating. We need to set all bits in read_set because the row
+ may need to be inserted in a different [sub]partition.
+ */
+ if (!(bitmap_buf= (my_bitmap_map*)
+ thd->alloc(bitmap_buffer_size(table->s->fields))))
+ {
+ mem_alloc_error(bitmap_buffer_size(table->s->fields));
+ result= TRUE;
+ goto end;
+ }
+ if (bitmap_init(&part_info->full_part_field_set, bitmap_buf,
+ table->s->fields, FALSE))
+ {
+ mem_alloc_error(table->s->fields);
+ result= TRUE;
+ goto end;
+ }
+ /*
+ full_part_field_array may be NULL if storage engine supports native
+ partitioning.
+ */
+ if ((ptr= part_info->full_part_field_array))
+ for (; *ptr; ptr++)
+ bitmap_set_bit(&part_info->full_part_field_set, (*ptr)->field_index);
+
+end:
+ DBUG_RETURN(result);
+}
+
+
+/*
+
+ Clear flag GET_FIXED_FIELDS_FLAG in all fields of a key previously set by
+ set_indicator_in_key_fields (always used in pairs).
+
+ SYNOPSIS
+ clear_indicator_in_key_fields()
+ key_info Reference to find the key fields
+
+ RETURN VALUE
+ NONE
+
+ DESCRIPTION
+ These support routines is used to set/reset an indicator of all fields
+ in a certain key. It is used in conjunction with another support routine
+ that traverse all fields in the PF to find if all or some fields in the
+ PF is part of the key. This is used to check primary keys and unique
+ keys involve all fields in PF (unless supported) and to derive the
+ key_map's used to quickly decide whether the index can be used to
+ derive which partitions are needed to scan.
+*/
+
+static void clear_indicator_in_key_fields(KEY *key_info)
+{
+ KEY_PART_INFO *key_part;
+ uint key_parts= key_info->key_parts, i;
+ for (i= 0, key_part=key_info->key_part; i < key_parts; i++, key_part++)
+ key_part->field->flags&= (~GET_FIXED_FIELDS_FLAG);
+}
+
+
+/*
+ Set flag GET_FIXED_FIELDS_FLAG in all fields of a key.
+
+ SYNOPSIS
+ set_indicator_in_key_fields
+ key_info Reference to find the key fields
+
+ RETURN VALUE
+ NONE
+*/
+
+static void set_indicator_in_key_fields(KEY *key_info)
+{
+ KEY_PART_INFO *key_part;
+ uint key_parts= key_info->key_parts, i;
+ for (i= 0, key_part=key_info->key_part; i < key_parts; i++, key_part++)
+ key_part->field->flags|= GET_FIXED_FIELDS_FLAG;
+}
+
+
+/*
+ Check if all or some fields in partition field array is part of a key
+ previously used to tag key fields.
+
+ SYNOPSIS
+ check_fields_in_PF()
+ ptr Partition field array
+ out:all_fields Is all fields of partition field array used in key
+ out:some_fields Is some fields of partition field array used in key
+
+ RETURN VALUE
+ all_fields, some_fields
+*/
+
+static void check_fields_in_PF(Field **ptr, bool *all_fields,
+ bool *some_fields)
+{
+ DBUG_ENTER("check_fields_in_PF");
+
+ *all_fields= TRUE;
+ *some_fields= FALSE;
+ if ((!ptr) || !(*ptr))
+ {
+ *all_fields= FALSE;
+ DBUG_VOID_RETURN;
+ }
+ do
+ {
+ /* Check if the field of the PF is part of the current key investigated */
+ if ((*ptr)->flags & GET_FIXED_FIELDS_FLAG)
+ *some_fields= TRUE;
+ else
+ *all_fields= FALSE;
+ } while (*(++ptr));
+ DBUG_VOID_RETURN;
+}
+
+
+/*
+ Clear flag GET_FIXED_FIELDS_FLAG in all fields of the table.
+ This routine is used for error handling purposes.
+
+ SYNOPSIS
+ clear_field_flag()
+ table TABLE object for which partition fields are set-up
+
+ RETURN VALUE
+ NONE
+*/
+
+static void clear_field_flag(TABLE *table)
+{
+ Field **ptr;
+ DBUG_ENTER("clear_field_flag");
+
+ for (ptr= table->field; *ptr; ptr++)
+ (*ptr)->flags&= (~GET_FIXED_FIELDS_FLAG);
+ DBUG_VOID_RETURN;
+}
+
+
+/*
+ find_field_in_table_sef finds the field given its name. All fields get
+ GET_FIXED_FIELDS_FLAG set.
+
+ SYNOPSIS
+ handle_list_of_fields()
+ it A list of field names for the partition function
+ table TABLE object for which partition fields are set-up
+ part_info Reference to partitioning data structure
+ sub_part Is the table subpartitioned as well
+
+ RETURN VALUE
+ TRUE Fields in list of fields not part of table
+ FALSE All fields ok and array created
+
+ DESCRIPTION
+ This routine sets-up the partition field array for KEY partitioning, it
+ also verifies that all fields in the list of fields is actually a part of
+ the table.
+
+*/
+
+
+static bool handle_list_of_fields(List_iterator<char> it,
+ TABLE *table,
+ partition_info *part_info,
+ bool is_sub_part)
+{
+ Field *field;
+ bool result;
+ char *field_name;
+ bool is_list_empty= TRUE;
+ DBUG_ENTER("handle_list_of_fields");
+
+ while ((field_name= it++))
+ {
+ is_list_empty= FALSE;
+ field= find_field_in_table_sef(table, field_name);
+ if (likely(field != 0))
+ field->flags|= GET_FIXED_FIELDS_FLAG;
+ else
+ {
+ my_error(ER_FIELD_NOT_FOUND_PART_ERROR, MYF(0));
+ clear_field_flag(table);
+ result= TRUE;
+ goto end;
+ }
+ }
+ if (is_list_empty)
+ {
+ uint primary_key= table->s->primary_key;
+ if (primary_key != MAX_KEY)
+ {
+ uint no_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++)
+ {
+ Field *field= table->key_info[primary_key].key_part[i].field;
+ field->flags|= GET_FIXED_FIELDS_FLAG;
+ }
+ }
+ else
+ {
+ if (table->s->db_type()->partition_flags &&
+ (table->s->db_type()->partition_flags() & HA_USE_AUTO_PARTITION) &&
+ (table->s->db_type()->partition_flags() & HA_CAN_PARTITION))
+ {
+ /*
+ This engine can handle automatic partitioning and there is no
+ primary key. In this case we rely on that the engine handles
+ partitioning based on a hidden key. Thus we allocate no
+ array for partitioning fields.
+ */
+ DBUG_RETURN(FALSE);
+ }
+ else
+ {
+ my_error(ER_FIELD_NOT_FOUND_PART_ERROR, MYF(0));
+ DBUG_RETURN(TRUE);
+ }
+ }
+ }
+ result= set_up_field_array(table, is_sub_part);
+end:
+ DBUG_RETURN(result);
+}
+
+
+/*
+ Support function to check if all VALUES * (expression) is of the
+ right sign (no signed constants when unsigned partition function)
+
+ SYNOPSIS
+ check_signed_flag()
+ part_info Partition info object
+
+ RETURN VALUES
+ 0 No errors due to sign errors
+ >0 Sign error
+*/
+
+int check_signed_flag(partition_info *part_info)
+{
+ int error= 0;
+ uint i= 0;
+ if (part_info->part_type != HASH_PARTITION &&
+ part_info->part_expr->unsigned_flag)
+ {
+ List_iterator<partition_element> part_it(part_info->partitions);
+ do
+ {
+ partition_element *part_elem= part_it++;
+
+ if (part_elem->signed_flag)
+ {
+ my_error(ER_PARTITION_CONST_DOMAIN_ERROR, MYF(0));
+ error= ER_PARTITION_CONST_DOMAIN_ERROR;
+ break;
+ }
+ } while (++i < part_info->no_parts);
+ }
+ return error;
+}
+
+
+/*
+ The function uses a new feature in fix_fields where the flag
+ GET_FIXED_FIELDS_FLAG is set for all fields in the item tree.
+ This field must always be reset before returning from the function
+ since it is used for other purposes as well.
+
+ SYNOPSIS
+ fix_fields_part_func()
+ thd The thread object
+ func_expr The item tree reference of the partition function
+ 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
+
+ RETURN VALUE
+ TRUE An error occurred, something was wrong with the
+ partition function.
+ FALSE Ok, a partition field array was created
+
+ DESCRIPTION
+ This function is used to build an array of partition fields for the
+ partitioning function and subpartitioning function. The partitioning
+ function is an item tree that must reference at least one field in the
+ table. This is checked first in the parser that the function doesn't
+ contain non-cacheable parts (like a random function) and by checking
+ here that the function isn't a constant function.
+
+ Calculate the number of fields in the partition function.
+ Use it allocate memory for array of Field pointers.
+ Initialise array of field pointers. Use information set when
+ calling fix_fields and reset it immediately after.
+ The get_fields_in_item_tree activates setting of bit in flags
+ on the field object.
+*/
+
+bool fix_fields_part_func(THD *thd, Item* func_expr, TABLE *table,
+ bool is_sub_part, bool is_field_to_be_setup)
+{
+ 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];
+ bool save_use_only_table_context;
+ DBUG_ENTER("fix_fields_part_func");
+
+ if (part_info->fixed)
+ {
+ if (!(is_sub_part || (error= check_signed_flag(part_info))))
+ result= FALSE;
+ 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.
+ */
+
+ 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;
+
+ 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= "partition function";
+ /*
+ In execution we must avoid the use of thd->change_item_tree since
+ we might release memory before statement is completed. We do this
+ by temporarily setting the stmt_arena->mem_root to be the mem_root
+ of the table object, this also ensures that any memory allocated
+ during fix_fields will not be released at end of execution of this
+ statement. Thus the item tree will remain valid also in subsequent
+ executions of this table object. We do however not at the moment
+ support allocations during execution of val_int so any item class
+ 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.
+ */
+
+ save_use_only_table_context= thd->lex->use_only_table_context;
+ thd->lex->use_only_table_context= TRUE;
+
+ error= func_expr->fix_fields(thd, (Item**)0);
+
+ 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);
+ goto end;
+ }
+ thd->where= save_where;
+ if (unlikely(func_expr->const_item()))
+ {
+ my_error(ER_CONST_EXPR_IN_PARTITION_FUNC_ERROR, MYF(0));
+ clear_field_flag(table);
+ goto end;
+ }
+ 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;
+end:
+ table->get_fields_in_item_tree= FALSE;
+ table->map= 0; //Restore old value
+ DBUG_RETURN(result);
+}
+
+
+/*
+ Check that the primary key contains all partition fields if defined
+
+ SYNOPSIS
+ check_primary_key()
+ table TABLE object for which partition fields are set-up
+
+ RETURN VALUES
+ TRUE Not all fields in partitioning function was part
+ of primary key
+ FALSE Ok, all fields of partitioning function were part
+ of primary key
+
+ DESCRIPTION
+ This function verifies that if there is a primary key that it contains
+ all the fields of the partition function.
+ This is a temporary limitation that will hopefully be removed after a
+ while.
+*/
+
+static bool check_primary_key(TABLE *table)
+{
+ uint primary_key= table->s->primary_key;
+ bool all_fields, some_fields;
+ bool result= FALSE;
+ DBUG_ENTER("check_primary_key");
+
+ if (primary_key < MAX_KEY)
+ {
+ set_indicator_in_key_fields(table->key_info+primary_key);
+ check_fields_in_PF(table->part_info->full_part_field_array,
+ &all_fields, &some_fields);
+ clear_indicator_in_key_fields(table->key_info+primary_key);
+ if (unlikely(!all_fields))
+ {
+ my_error(ER_UNIQUE_KEY_NEED_ALL_FIELDS_IN_PF,MYF(0),"PRIMARY KEY");
+ result= TRUE;
+ }
+ }
+ DBUG_RETURN(result);
+}
+
+
+/*
+ Check that unique keys contains all partition fields
+
+ SYNOPSIS
+ check_unique_keys()
+ table TABLE object for which partition fields are set-up
+
+ RETURN VALUES
+ TRUE Not all fields in partitioning function was part
+ of all unique keys
+ FALSE Ok, all fields of partitioning function were part
+ of unique keys
+
+ DESCRIPTION
+ This function verifies that if there is a unique index that it contains
+ all the fields of the partition function.
+ This is a temporary limitation that will hopefully be removed after a
+ while.
+*/
+
+static bool check_unique_keys(TABLE *table)
+{
+ bool all_fields, some_fields;
+ bool result= FALSE;
+ uint keys= table->s->keys;
+ uint i;
+ DBUG_ENTER("check_unique_keys");
+
+ for (i= 0; i < keys; i++)
+ {
+ if (table->key_info[i].flags & HA_NOSAME) //Unique index
+ {
+ set_indicator_in_key_fields(table->key_info+i);
+ check_fields_in_PF(table->part_info->full_part_field_array,
+ &all_fields, &some_fields);
+ clear_indicator_in_key_fields(table->key_info+i);
+ if (unlikely(!all_fields))
+ {
+ my_error(ER_UNIQUE_KEY_NEED_ALL_FIELDS_IN_PF,MYF(0),"UNIQUE INDEX");
+ result= TRUE;
+ break;
+ }
+ }
+ }
+ DBUG_RETURN(result);
+}
+
+
+/*
+ An important optimisation is whether a range on a field can select a subset
+ of the partitions.
+ A prerequisite for this to happen is that the PF is a growing function OR
+ a shrinking function.
+ This can never happen for a multi-dimensional PF. Thus this can only happen
+ with PF with at most one field involved in the PF.
+ The idea is that if the function is a growing function and you know that
+ the field of the PF is 4 <= A <= 6 then we can convert this to a range
+ in the PF instead by setting the range to PF(4) <= PF(A) <= PF(6). In the
+ case of RANGE PARTITIONING and LIST PARTITIONING this can be used to
+ calculate a set of partitions rather than scanning all of them.
+ Thus the following prerequisites are there to check if sets of partitions
+ can be found.
+ 1) Only possible for RANGE and LIST partitioning (not for subpartitioning)
+ 2) Only possible if PF only contains 1 field
+ 3) Possible if PF is a growing function of the field
+ 4) Possible if PF is a shrinking function of the field
+ OBSERVATION:
+ 1) IF f1(A) is a growing function AND f2(A) is a growing function THEN
+ f1(A) + f2(A) is a growing function
+ f1(A) * f2(A) is a growing function if f1(A) >= 0 and f2(A) >= 0
+ 2) IF f1(A) is a growing function and f2(A) is a shrinking function THEN
+ f1(A) / f2(A) is a growing function if f1(A) >= 0 and f2(A) > 0
+ 3) IF A is a growing function then a function f(A) that removes the
+ least significant portion of A is a growing function
+ E.g. DATE(datetime) is a growing function
+ MONTH(datetime) is not a growing/shrinking function
+ 4) IF f1(A) is a growing function and f2(A) is a growing function THEN
+ f1(f2(A)) and f2(f1(A)) are also growing functions
+ 5) IF f1(A) is a shrinking function and f2(A) is a growing function THEN
+ f1(f2(A)) is a shrinking function and f2(f1(A)) is a shrinking function
+ 6) f1(A) = A is a growing function
+ 7) f1(A) = A*a + b (where a and b are constants) is a growing function
+
+ By analysing the item tree of the PF we can use these deducements and
+ derive whether the PF is a growing function or a shrinking function or
+ neither of it.
+
+ If the PF is range capable then a flag is set on the table object
+ indicating this to notify that we can use also ranges on the field
+ of the PF to deduce a set of partitions if the fields of the PF were
+ not all fully bound.
+
+ SYNOPSIS
+ check_range_capable_PF()
+ table TABLE object for which partition fields are set-up
+
+ DESCRIPTION
+ Support for this is not implemented yet.
+*/
+
+void check_range_capable_PF(TABLE *table)
+{
+ DBUG_ENTER("check_range_capable_PF");
+
+ DBUG_VOID_RETURN;
+}
+
+
+/*
+ Set up partition bitmap
+
+ SYNOPSIS
+ set_up_partition_bitmap()
+ thd Thread object
+ part_info Reference to partitioning data structure
+
+ RETURN VALUE
+ TRUE Memory allocation failure
+ FALSE Success
+
+ DESCRIPTION
+ Allocate memory for bitmap of the partitioned table
+ and initialise it.
+*/
+
+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_bytes= bitmap_buffer_size(bitmap_bits);
+ DBUG_ENTER("set_up_partition_bitmap");
+
+ if (!(bitmap_buf= (uint32*)thd->alloc(bitmap_bytes)))
+ {
+ mem_alloc_error(bitmap_bytes);
+ DBUG_RETURN(TRUE);
+ }
+ bitmap_init(&part_info->used_partitions, bitmap_buf, bitmap_bytes*8, FALSE);
+ bitmap_set_all(&part_info->used_partitions);
+ DBUG_RETURN(FALSE);
+}
+
+
+/*
+ Set up partition key maps
+
+ SYNOPSIS
+ set_up_partition_key_maps()
+ table TABLE object for which partition fields are set-up
+ part_info Reference to partitioning data structure
+
+ RETURN VALUES
+ None
+
+ DESCRIPTION
+ This function sets up a couple of key maps to be able to quickly check
+ if an index ever can be used to deduce the partition fields or even
+ a part of the fields of the partition function.
+ We set up the following key_map's.
+ PF = Partition Function
+ 1) All fields of the PF is set even by equal on the first fields in the
+ key
+ 2) All fields of the PF is set if all fields of the key is set
+ 3) At least one field in the PF is set if all fields is set
+ 4) At least one field in the PF is part of the key
+*/
+
+static void set_up_partition_key_maps(TABLE *table,
+ partition_info *part_info)
+{
+ uint keys= table->s->keys;
+ uint i;
+ bool all_fields, some_fields;
+ DBUG_ENTER("set_up_partition_key_maps");
+
+ part_info->all_fields_in_PF.clear_all();
+ part_info->all_fields_in_PPF.clear_all();
+ part_info->all_fields_in_SPF.clear_all();
+ part_info->some_fields_in_PF.clear_all();
+ for (i= 0; i < keys; i++)
+ {
+ set_indicator_in_key_fields(table->key_info+i);
+ check_fields_in_PF(part_info->full_part_field_array,
+ &all_fields, &some_fields);
+ if (all_fields)
+ part_info->all_fields_in_PF.set_bit(i);
+ if (some_fields)
+ part_info->some_fields_in_PF.set_bit(i);
+ if (part_info->is_sub_partitioned())
+ {
+ check_fields_in_PF(part_info->part_field_array,
+ &all_fields, &some_fields);
+ if (all_fields)
+ part_info->all_fields_in_PPF.set_bit(i);
+ check_fields_in_PF(part_info->subpart_field_array,
+ &all_fields, &some_fields);
+ if (all_fields)
+ part_info->all_fields_in_SPF.set_bit(i);
+ }
+ clear_indicator_in_key_fields(table->key_info+i);
+ }
+ DBUG_VOID_RETURN;
+}
+
+
+/*
+ Set up function pointers for partition function
+
+ SYNOPSIS
+ set_up_partition_func_pointers()
+ part_info Reference to partitioning data structure
+
+ RETURN VALUE
+ NONE
+
+ DESCRIPTION
+ Set-up all function pointers for calculation of partition id,
+ subpartition id and the upper part in subpartitioning. This is to speed up
+ execution of get_partition_id which is executed once every record to be
+ written and deleted and twice for updates.
+*/
+
+static void set_up_partition_func_pointers(partition_info *part_info)
+{
+ DBUG_ENTER("set_up_partition_func_pointers");
+
+ if (part_info->is_sub_partitioned())
+ {
+ if (part_info->part_type == RANGE_PARTITION)
+ {
+ 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->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;
+ }
+ }
+ }
+ }
+ else /* No subpartitioning */
+ {
+ 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;
+ else if (part_info->part_type == LIST_PARTITION)
+ part_info->get_partition_id= get_partition_id_list;
+ else /* HASH partitioning */
+ {
+ if (part_info->list_of_part_fields)
+ {
+ if (part_info->linear_hash_ind)
+ part_info->get_partition_id= get_partition_id_linear_key_nosub;
+ else
+ part_info->get_partition_id= get_partition_id_key_nosub;
+ }
+ else
+ {
+ if (part_info->linear_hash_ind)
+ part_info->get_partition_id= get_partition_id_linear_hash_nosub;
+ else
+ part_info->get_partition_id= get_partition_id_hash_nosub;
+ }
+ }
+ }
+ 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())
+ {
+ DBUG_ASSERT(part_info->get_part_partition_id);
+ 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;
+ }
+ 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;
+ }
+ DBUG_VOID_RETURN;
+}
+
+
+/*
+ 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.
+
+ SYNOPSIS
+ set_linear_hash_mask()
+ part_info Reference to partitioning data structure
+ no_parts Number of parts in linear hash partitioning
+
+ RETURN VALUE
+ NONE
+*/
+
+void set_linear_hash_mask(partition_info *part_info, uint no_parts)
+{
+ uint mask;
+
+ for (mask= 1; mask < no_parts; mask<<=1)
+ ;
+ part_info->linear_hash_mask= mask - 1;
+}
+
+
+/*
+ This function calculates the partition id provided the result of the hash
+ function using linear hashing parameters, mask and number of partitions.
+
+ SYNOPSIS
+ 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
+
+ RETURN VALUE
+ part_id The calculated partition identity (starting at 0)
+
+ DESCRIPTION
+ The partition is calculated according to the theory of linear hashing.
+ See e.g. Linear hashing: a new tool for file and table addressing,
+ Reprinted from VLDB-80 in Readings Database Systems, 2nd ed, M. Stonebraker
+ (ed.), Morgan Kaufmann 1994.
+*/
+
+static uint32 get_part_id_from_linear_hash(longlong hash_value, uint mask,
+ uint no_parts)
+{
+ uint32 part_id= (uint32)(hash_value & mask);
+
+ if (part_id >= no_parts)
+ {
+ uint new_mask= ((mask + 1) >> 1) - 1;
+ part_id= (uint32)(hash_value & new_mask);
+ }
+ return part_id;
+}
+
+
+/*
+ Check if a particular field is in need of character set
+ handling for partition functions.
+
+ SYNOPSIS
+ field_is_partition_charset()
+ field The field to check
+
+ RETURN VALUES
+ FALSE Not in need of character set handling
+ TRUE In need of character set handling
+*/
+
+bool field_is_partition_charset(Field *field)
+{
+ if (!(field->type() == MYSQL_TYPE_STRING) &&
+ !(field->type() == MYSQL_TYPE_VARCHAR))
+ return FALSE;
+ {
+ CHARSET_INFO *cs= ((Field_str*)field)->charset();
+ if (!(field->type() == MYSQL_TYPE_STRING) ||
+ !(cs->state & MY_CS_BINSORT))
+ return TRUE;
+ return FALSE;
+ }
+}
+
+
+/*
+ Check that partition function doesn't contain any forbidden
+ character sets and collations.
+
+ SYNOPSIS
+ check_part_func_fields()
+ ptr Array of Field pointers
+ ok_with_charsets Will we report allowed charset
+ fields as ok
+ RETURN VALUES
+ FALSE Success
+ TRUE Error
+
+ DESCRIPTION
+ We will check in this routine that the fields of the partition functions
+ do not contain unallowed parts. It can also be used to check if there
+ are fields that require special care by calling my_strnxfrm before
+ calling the functions to calculate partition id.
+*/
+
+bool check_part_func_fields(Field **ptr, bool ok_with_charsets)
+{
+ Field *field;
+ DBUG_ENTER("check_part_func_fields");
+
+ while ((field= *(ptr++)))
+ {
+ /*
+ For CHAR/VARCHAR fields we need to take special precautions.
+ Binary collation with CHAR is automatically supported. Other
+ types need some kind of standardisation function handling
+ */
+ if (field_is_partition_charset(field))
+ {
+ CHARSET_INFO *cs= ((Field_str*)field)->charset();
+ if (!ok_with_charsets ||
+ cs->mbmaxlen > 1 ||
+ cs->strxfrm_multiply > 1)
+ {
+ DBUG_RETURN(TRUE);
+ }
+ }
+ }
+ DBUG_RETURN(FALSE);
+}
+
+
+/*
+ fix partition functions
+
+ SYNOPSIS
+ fix_partition_func()
+ thd The thread object
+ table TABLE object for which partition fields are set-up
+ is_create_table_ind Indicator of whether openfrm was called as part of
+ CREATE or ALTER TABLE
+
+ RETURN VALUE
+ TRUE Error
+ FALSE Success
+
+ DESCRIPTION
+ The name parameter contains the full table name and is used to get the
+ database name of the table which is used to set-up a correct
+ TABLE_LIST object for use in fix_fields.
+
+NOTES
+ This function is called as part of opening the table by opening the .frm
+ file. It is a part of CREATE TABLE to do this so it is quite permissible
+ that errors due to erroneus syntax isn't found until we come here.
+ If the user has used a non-existing field in the table is one such example
+ of an error that is not discovered until here.
+*/
+
+bool fix_partition_func(THD *thd, TABLE *table,
+ bool is_create_table_ind)
+{
+ bool result= TRUE;
+ partition_info *part_info= table->part_info;
+ enum_mark_columns save_mark_used_columns= thd->mark_used_columns;
+ DBUG_ENTER("fix_partition_func");
+
+ if (part_info->fixed)
+ {
+ DBUG_RETURN(FALSE);
+ }
+ thd->mark_used_columns= MARK_COLUMNS_NONE;
+ DBUG_PRINT("info", ("thd->mark_used_columns: %d", thd->mark_used_columns));
+
+ if (!is_create_table_ind ||
+ thd->lex->sql_command != SQLCOM_CREATE_TABLE)
+ {
+ if (partition_default_handling(table, part_info,
+ is_create_table_ind,
+ table->s->normalized_path.str))
+ {
+ DBUG_RETURN(TRUE);
+ }
+ }
+ if (part_info->is_sub_partitioned())
+ {
+ DBUG_ASSERT(part_info->subpart_type == HASH_PARTITION);
+ /*
+ Subpartition is defined. We need to verify that subpartitioning
+ function is correct.
+ */
+ if (part_info->linear_hash_ind)
+ set_linear_hash_mask(part_info, part_info->no_subparts);
+ if (part_info->list_of_subpart_fields)
+ {
+ List_iterator<char> it(part_info->subpart_field_list);
+ if (unlikely(handle_list_of_fields(it, table, part_info, TRUE)))
+ goto end;
+ }
+ else
+ {
+ if (unlikely(fix_fields_part_func(thd, part_info->subpart_expr,
+ table, TRUE, TRUE)))
+ goto end;
+ if (unlikely(part_info->subpart_expr->result_type() != INT_RESULT))
+ {
+ my_error(ER_PARTITION_FUNC_NOT_ALLOWED_ERROR, MYF(0),
+ "SUBPARTITION");
+ goto end;
+ }
+ }
+ }
+ DBUG_ASSERT(part_info->part_type != NOT_A_PARTITION);
+ /*
+ Partition is defined. We need to verify that partitioning
+ function is correct.
+ */
+ if (part_info->part_type == HASH_PARTITION)
+ {
+ if (part_info->linear_hash_ind)
+ set_linear_hash_mask(part_info, part_info->no_parts);
+ if (part_info->list_of_part_fields)
+ {
+ 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, TRUE)))
+ goto end;
+ if (unlikely(part_info->part_expr->result_type() != INT_RESULT))
+ {
+ my_error(ER_PARTITION_FUNC_NOT_ALLOWED_ERROR, MYF(0), part_str);
+ goto end;
+ }
+ part_info->part_result_type= INT_RESULT;
+ }
+ }
+ else
+ {
+ const char *error_str;
+ if (unlikely(fix_fields_part_func(thd, part_info->part_expr,
+ table, FALSE, TRUE)))
+ goto end;
+ if (part_info->part_type == RANGE_PARTITION)
+ {
+ error_str= partition_keywords[PKW_RANGE].str;
+ if (unlikely(part_info->check_range_constants()))
+ goto end;
+ }
+ else if (part_info->part_type == LIST_PARTITION)
+ {
+ error_str= partition_keywords[PKW_LIST].str;
+ if (unlikely(part_info->check_list_constants()))
+ goto end;
+ }
+ else
+ {
+ DBUG_ASSERT(0);
+ my_error(ER_INCONSISTENT_PARTITION_INFO_ERROR, MYF(0));
+ goto end;
+ }
+ if (unlikely(part_info->no_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))
+ {
+ my_error(ER_PARTITION_FUNC_NOT_ALLOWED_ERROR, MYF(0), part_str);
+ 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->is_sub_partitioned() &&
+ check_part_func_fields(part_info->subpart_field_array, TRUE)))
+ {
+ my_error(ER_PARTITION_FUNCTION_IS_NOT_ALLOWED, MYF(0));
+ goto end;
+ }
+ if (unlikely(create_full_part_field_array(thd, table, part_info)))
+ goto end;
+ if (unlikely(check_primary_key(table)))
+ goto end;
+ if (unlikely((!(table->s->db_type()->partition_flags &&
+ (table->s->db_type()->partition_flags() & HA_CAN_PARTITION_UNIQUE))) &&
+ check_unique_keys(table)))
+ goto end;
+ if (unlikely(set_up_partition_bitmap(thd, part_info)))
+ goto end;
+ if (unlikely(part_info->set_up_charset_field_preps()))
+ {
+ my_error(ER_PARTITION_FUNCTION_IS_NOT_ALLOWED, MYF(0));
+ goto end;
+ }
+ check_range_capable_PF(table);
+ set_up_partition_key_maps(table, part_info);
+ set_up_partition_func_pointers(part_info);
+ set_up_range_analysis_info(part_info);
+ result= FALSE;
+end:
+ thd->mark_used_columns= save_mark_used_columns;
+ DBUG_PRINT("info", ("thd->mark_used_columns: %d", thd->mark_used_columns));
+ DBUG_RETURN(result);
+}
+
+
+/*
+ The code below is support routines for the reverse parsing of the
+ partitioning syntax. This feature is very useful to generate syntax for
+ all default values to avoid all default checking when opening the frm
+ file. It is also used when altering the partitioning by use of various
+ ALTER TABLE commands. Finally it is used for SHOW CREATE TABLES.
+*/
+
+static int add_write(File fptr, const char *buf, uint len)
+{
+ uint len_written= my_write(fptr, (const uchar*)buf, len, MYF(0));
+
+ if (likely(len == len_written))
+ return 0;
+ else
+ return 1;
+}
+
+static int add_string_object(File fptr, String *string)
+{
+ return add_write(fptr, string->ptr(), string->length());
+}
+
+static int add_string(File fptr, const char *string)
+{
+ return add_write(fptr, string, strlen(string));
+}
+
+static int add_string_len(File fptr, const char *string, uint len)
+{
+ return add_write(fptr, string, len);
+}
+
+static int add_space(File fptr)
+{
+ return add_string(fptr, space_str);
+}
+
+static int add_comma(File fptr)
+{
+ return add_string(fptr, comma_str);
+}
+
+static int add_equal(File fptr)
+{
+ return add_string(fptr, equal_str);
+}
+
+static int add_end_parenthesis(File fptr)
+{
+ return add_string(fptr, end_paren_str);
+}
+
+static int add_begin_parenthesis(File fptr)
+{
+ return add_string(fptr, begin_paren_str);
+}
+
+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);
+}
+
+static int add_partition(File fptr)
+{
+ char buff[22];
+ strxmov(buff, part_str, space_str, NullS);
+ return add_string(fptr, buff);
+}
+
+static int add_subpartition(File fptr)
+{
+ int err= add_string(fptr, sub_str);
+
+ return err + add_partition(fptr);
+}
+
+static int add_partition_by(File fptr)
+{
+ char buff[22];
+ strxmov(buff, part_str, space_str, by_str, space_str, NullS);
+ return add_string(fptr, buff);
+}
+
+static int add_subpartition_by(File fptr)
+{
+ int err= add_string(fptr, sub_str);
+
+ return err + add_partition_by(fptr);
+}
+
+static int add_key_partition(File fptr, List<char> field_list)
+{
+ uint i, no_fields;
+ int err;
+
+ List_iterator<char> part_it(field_list);
+ err= add_part_key_word(fptr, partition_keywords[PKW_KEY].str);
+ no_fields= field_list.elements;
+ i= 0;
+ while (i < no_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;
+ append_identifier(thd, &field_string, field_str,
+ strlen(field_str));
+ thd->options= save_options;
+ err+= add_string_object(fptr, &field_string);
+ if (i != (no_fields-1))
+ err+= add_comma(fptr);
+ i++;
+ }
+ return err;
+}
+
+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;
+ append_identifier(thd, &name_string, name,
+ strlen(name));
+ thd->options= save_options;
+ err= add_string_object(fptr, &name_string);
+ return err;
+}
+
+static int add_int(File fptr, longlong number)
+{
+ char buff[32];
+ llstr(number, buff);
+ return add_string(fptr, buff);
+}
+
+static int add_uint(File fptr, ulonglong number)
+{
+ char buff[32];
+ longlong2str(number, buff, 10);
+ return add_string(fptr, buff);
+}
+
+/*
+ Must escape strings in partitioned tables frm-files,
+ parsing it later with mysql_unpack_partition will fail otherwise.
+*/
+static int add_quoted_string(File fptr, const char *quotestr)
+{
+ String orgstr(quotestr, system_charset_info);
+ String escapedstr;
+ int err= add_string(fptr, "'");
+ err+= append_escaped(&escapedstr, &orgstr);
+ err+= add_string(fptr, escapedstr.c_ptr_safe());
+ return err + add_string(fptr, "'");
+}
+
+static int add_keyword_string(File fptr, const char *keyword,
+ bool should_use_quotes,
+ const char *keystr)
+{
+ int err= add_string(fptr, keyword);
+
+ err+= add_space(fptr);
+ err+= add_equal(fptr);
+ err+= add_space(fptr);
+ if (should_use_quotes)
+ err+= add_quoted_string(fptr, keystr);
+ else
+ err+= add_string(fptr, keystr);
+ return err + add_space(fptr);
+}
+
+static int add_keyword_int(File fptr, const char *keyword, longlong num)
+{
+ int err= add_string(fptr, keyword);
+
+ err+= add_space(fptr);
+ err+= add_equal(fptr);
+ err+= add_space(fptr);
+ err+= add_int(fptr, num);
+ return err + add_space(fptr);
+}
+
+static int add_engine(File fptr, handlerton *engine_type)
+{
+ const char *engine_str= ha_resolve_storage_engine_name(engine_type);
+ DBUG_PRINT("info", ("ENGINE: %s", engine_str));
+ int err= add_string(fptr, "ENGINE = ");
+ return err + add_string(fptr, engine_str);
+}
+
+static int add_partition_options(File fptr, partition_element *p_elem)
+{
+ int err= 0;
+
+ err+= add_space(fptr);
+ if (p_elem->tablespace_name)
+ err+= add_keyword_string(fptr,"TABLESPACE", FALSE,
+ p_elem->tablespace_name);
+ if (p_elem->nodegroup_id != UNDEF_NODEGROUP)
+ err+= add_keyword_int(fptr,"NODEGROUP",(longlong)p_elem->nodegroup_id);
+ if (p_elem->part_max_rows)
+ err+= add_keyword_int(fptr,"MAX_ROWS",(longlong)p_elem->part_max_rows);
+ if (p_elem->part_min_rows)
+ err+= add_keyword_int(fptr,"MIN_ROWS",(longlong)p_elem->part_min_rows);
+ if (!(current_thd->variables.sql_mode & MODE_NO_DIR_IN_CREATE))
+ {
+ if (p_elem->data_file_name)
+ err+= add_keyword_string(fptr, "DATA DIRECTORY", TRUE,
+ p_elem->data_file_name);
+ if (p_elem->index_file_name)
+ err+= add_keyword_string(fptr, "INDEX DIRECTORY", TRUE,
+ p_elem->index_file_name);
+ }
+ if (p_elem->part_comment)
+ err+= add_keyword_string(fptr, "COMMENT", TRUE, p_elem->part_comment);
+ return err + add_engine(fptr,p_elem->engine_type);
+}
+
+static int add_partition_values(File fptr, partition_info *part_info, partition_element *p_elem)
+{
+ int err= 0;
+
+ if (part_info->part_type == RANGE_PARTITION)
+ {
+ err+= add_string(fptr, " VALUES LESS THAN ");
+ 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;
+
+ err+= add_begin_parenthesis(fptr);
+ if (p_elem->has_null_value)
+ {
+ err+= add_string(fptr, "NULL");
+ if (no_items == 0)
+ {
+ err+= add_end_parenthesis(fptr);
+ goto end;
+ }
+ err+= add_comma(fptr);
+ }
+ i= 0;
+ do
+ {
+ part_elem_value *list_value= list_val_it++;
+
+ if (!list_value->unsigned_flag)
+ err+= add_int(fptr, list_value->value);
+ else
+ err+= add_uint(fptr, list_value->value);
+ if (i != (no_items-1))
+ err+= add_comma(fptr);
+ } while (++i < no_items);
+ err+= add_end_parenthesis(fptr);
+ }
+end:
+ return err;
+}
+
+/*
+ Generate the partition syntax from the partition data structure.
+ Useful for support of generating defaults, SHOW CREATE TABLES
+ and easy partition management.
+
+ SYNOPSIS
+ generate_partition_syntax()
+ part_info The partitioning data structure
+ buf_length A pointer to the returned buffer length
+ use_sql_alloc Allocate buffer from sql_alloc if true
+ otherwise use my_malloc
+ show_partition_options Should we display partition options
+
+ RETURN VALUES
+ NULL error
+ buf, buf_length Buffer and its length
+
+ DESCRIPTION
+ Here we will generate the full syntax for the given command where all
+ defaults have been expanded. By so doing the it is also possible to
+ make lots of checks of correctness while at it.
+ This could will also be reused for SHOW CREATE TABLES and also for all
+ type ALTER TABLE commands focusing on changing the PARTITION structure
+ in any fashion.
+
+ The implementation writes the syntax to a temporary file (essentially
+ an abstraction of a dynamic array) and if all writes goes well it
+ allocates a buffer and writes the syntax into this one and returns it.
+
+ As a security precaution the file is deleted before writing into it. This
+ means that no other processes on the machine can open and read the file
+ while this processing is ongoing.
+
+ The code is optimised for minimal code size since it is not used in any
+ common queries.
+*/
+
+char *generate_partition_syntax(partition_info *part_info,
+ uint *buf_length,
+ bool use_sql_alloc,
+ bool show_partition_options)
+{
+ uint i,j, tot_no_parts, no_subparts;
+ partition_element *part_elem;
+ ulonglong buffer_length;
+ char path[FN_REFLEN];
+ int err= 0;
+ List_iterator<partition_element> part_it(part_info->partitions);
+ File fptr;
+ char *buf= NULL; //Return buffer
+ DBUG_ENTER("generate_partition_syntax");
+
+ if (unlikely(((fptr= create_temp_file(path,mysql_tmpdir,"psy",
+ O_RDWR | O_BINARY | O_TRUNC |
+ O_TEMPORARY, MYF(MY_WME)))) < 0))
+ DBUG_RETURN(NULL);
+#ifndef __WIN__
+ unlink(path);
+#endif
+ err+= add_space(fptr);
+ err+= add_partition_by(fptr);
+ switch (part_info->part_type)
+ {
+ case RANGE_PARTITION:
+ err+= add_part_key_word(fptr, partition_keywords[PKW_RANGE].str);
+ break;
+ case LIST_PARTITION:
+ err+= add_part_key_word(fptr, partition_keywords[PKW_LIST].str);
+ break;
+ case HASH_PARTITION:
+ 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);
+ else
+ err+= add_hash(fptr);
+ 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();
+ DBUG_RETURN(NULL);
+ }
+ if (part_info->part_expr)
+ 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) &&
+ part_info->use_default_partitions)
+ {
+ err+= add_string(fptr, "\n");
+ err+= add_string(fptr, "PARTITIONS ");
+ err+= add_int(fptr, part_info->no_parts);
+ }
+ if (part_info->is_sub_partitioned())
+ {
+ err+= add_string(fptr, "\n");
+ err+= add_subpartition_by(fptr);
+ /* Must be hash partitioning for subpartitioning */
+ 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);
+ else
+ err+= add_hash(fptr);
+ if (part_info->subpart_expr)
+ 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) &&
+ part_info->use_default_subpartitions)
+ {
+ err+= add_string(fptr, "\n");
+ err+= add_string(fptr, "SUBPARTITIONS ");
+ err+= add_int(fptr, part_info->no_subparts);
+ }
+ }
+ tot_no_parts= part_info->partitions.elements;
+ no_subparts= part_info->no_subparts;
+
+ if (!part_info->use_default_partitions)
+ {
+ bool first= TRUE;
+ err+= add_string(fptr, "\n");
+ err+= add_begin_parenthesis(fptr);
+ i= 0;
+ do
+ {
+ part_elem= part_it++;
+ if (part_elem->part_state != PART_TO_BE_DROPPED &&
+ part_elem->part_state != PART_REORGED_DROPPED)
+ {
+ if (!first)
+ {
+ err+= add_comma(fptr);
+ err+= add_string(fptr, "\n");
+ err+= add_space(fptr);
+ }
+ first= FALSE;
+ err+= add_partition(fptr);
+ err+= add_name_string(fptr, part_elem->partition_name);
+ err+= add_partition_values(fptr, part_info, part_elem);
+ if (!part_info->is_sub_partitioned() ||
+ part_info->use_default_subpartitions)
+ {
+ if (show_partition_options)
+ err+= add_partition_options(fptr, part_elem);
+ }
+ else
+ {
+ err+= add_string(fptr, "\n");
+ err+= add_space(fptr);
+ err+= add_begin_parenthesis(fptr);
+ List_iterator<partition_element> sub_it(part_elem->subpartitions);
+ j= 0;
+ do
+ {
+ part_elem= sub_it++;
+ err+= add_subpartition(fptr);
+ 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))
+ {
+ err+= add_comma(fptr);
+ err+= add_string(fptr, "\n");
+ err+= add_space(fptr);
+ err+= add_space(fptr);
+ }
+ else
+ err+= add_end_parenthesis(fptr);
+ } while (++j < no_subparts);
+ }
+ }
+ if (i == (tot_no_parts-1))
+ err+= add_end_parenthesis(fptr);
+ } while (++i < tot_no_parts);
+ }
+ if (err)
+ goto close_file;
+ buffer_length= my_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))
+ goto close_file;
+ *buf_length= (uint)buffer_length;
+ if (use_sql_alloc)
+ buf= (char*) sql_alloc(*buf_length+1);
+ else
+ buf= (char*) my_malloc(*buf_length+1, MYF(MY_WME));
+ if (!buf)
+ goto close_file;
+
+ if (unlikely(my_read(fptr, (uchar*)buf, *buf_length, MYF(MY_FNABP))))
+ {
+ if (!use_sql_alloc)
+ my_free(buf, MYF(0));
+ else
+ buf= NULL;
+ }
+ else
+ buf[*buf_length]= 0;
+
+close_file:
+ my_close(fptr, MYF(0));
+ DBUG_RETURN(buf);
+}
+
+
+/*
+ Check if partition key fields are modified and if it can be handled by the
+ underlying storage engine.
+
+ SYNOPSIS
+ partition_key_modified
+ table TABLE object for which partition fields are set-up
+ fields Bitmap representing fields to be modified
+
+ RETURN VALUES
+ TRUE Need special handling of UPDATE
+ FALSE Normal UPDATE handling is ok
+*/
+
+bool partition_key_modified(TABLE *table, const MY_BITMAP *fields)
+{
+ Field **fld;
+ partition_info *part_info= table->part_info;
+ DBUG_ENTER("partition_key_modified");
+
+ if (!part_info)
+ DBUG_RETURN(FALSE);
+ if (table->s->db_type()->partition_flags &&
+ (table->s->db_type()->partition_flags() & HA_CAN_UPDATE_PARTITION_KEY))
+ DBUG_RETURN(FALSE);
+ for (fld= part_info->full_part_field_array; *fld; fld++)
+ if (bitmap_is_set(fields, (*fld)->field_index))
+ DBUG_RETURN(TRUE);
+ DBUG_RETURN(FALSE);
+}
+
+
+/*
+ A function to handle correct handling of NULL values in partition
+ functions.
+ SYNOPSIS
+ part_val_int()
+ item_expr The item expression to evaluate
+ out:result The value of the partition function,
+ LONGLONG_MIN if any null value in function
+ RETURN VALUES
+ TRUE Error in val_int()
+ FALSE ok
+*/
+
+static inline int part_val_int(Item *item_expr, longlong *result)
+{
+ *result= item_expr->val_int();
+ if (item_expr->null_value)
+ {
+ if (current_thd->is_error())
+ return TRUE;
+ else
+ *result= LONGLONG_MIN;
+ }
+ return FALSE;
+}
+
+
+/*
+ The next set of functions are used to calculate the partition identity.
+ A handler sets up a variable that corresponds to one of these functions
+ to be able to quickly call it whenever the partition id needs to calculated
+ based on the record in table->record[0] (or set up to fake that).
+ There are 4 functions for hash partitioning and 2 for RANGE/LIST partitions.
+ In addition there are 4 variants for RANGE subpartitioning and 4 variants
+ for LIST subpartitioning thus in total there are 14 variants of this
+ function.
+
+ We have a set of support functions for these 14 variants. There are 4
+ variants of hash functions and there is a function for each. The KEY
+ partitioning uses the function calculate_key_value to calculate the hash
+ value based on an array of fields. The linear hash variants uses the
+ method get_part_id_from_linear_hash to get the partition id using the
+ hash value and some parameters calculated from the number of partitions.
+*/
+
+/*
+ Calculate hash value for KEY partitioning using an array of fields.
+
+ SYNOPSIS
+ calculate_key_value()
+ field_array An array of the fields in KEY partitioning
+
+ RETURN VALUE
+ hash_value calculated
+
+ DESCRIPTION
+ Uses the hash function on the character set of the field. Integer and
+ floating point fields use the binary character set by default.
+*/
+
+static uint32 calculate_key_value(Field **field_array)
+{
+ ulong nr1= 1;
+ ulong nr2= 4;
+
+ do
+ {
+ Field *field= *field_array;
+ field->hash(&nr1, &nr2);
+ } while (*(++field_array));
+ return (uint32) nr1;
+}
+
+
+/*
+ A simple support function to calculate part_id given local part and
+ sub part.
+
+ SYNOPSIS
+ get_part_id_for_sub()
+ loc_part_id Local partition id
+ sub_part_id Subpartition id
+ no_subparts Number of subparts
+*/
+
+inline
+static uint32 get_part_id_for_sub(uint32 loc_part_id, uint32 sub_part_id,
+ uint no_subparts)
+{
+ return (uint32)((loc_part_id * no_subparts) + sub_part_id);
+}
+
+
+/*
+ Calculate part_id for (SUB)PARTITION BY HASH
+
+ SYNOPSIS
+ get_part_id_hash()
+ no_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
+
+ RETURN VALUE
+ != 0 Error code
+ FALSE Success
+*/
+
+static int get_part_id_hash(uint no_parts,
+ Item *part_expr,
+ uint32 *part_id,
+ longlong *func_value)
+{
+ longlong int_hash_id;
+ DBUG_ENTER("get_part_id_hash");
+
+ if (part_val_int(part_expr, func_value))
+ DBUG_RETURN(HA_ERR_NO_PARTITION_FOUND);
+
+ int_hash_id= *func_value % no_parts;
+
+ *part_id= int_hash_id < 0 ? (uint32) -int_hash_id : (uint32) int_hash_id;
+ DBUG_RETURN(FALSE);
+}
+
+
+/*
+ Calculate part_id for (SUB)PARTITION BY LINEAR HASH
+
+ SYNOPSIS
+ 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
+ part_expr Item tree of hash function
+ out:part_id The returned partition id
+ out:func_value Value of hash function
+
+ RETURN VALUE
+ != 0 Error code
+ 0 OK
+*/
+
+static int get_part_id_linear_hash(partition_info *part_info,
+ uint no_parts,
+ Item *part_expr,
+ uint32 *part_id,
+ longlong *func_value)
+{
+ DBUG_ENTER("get_part_id_linear_hash");
+
+ if (part_val_int(part_expr, func_value))
+ DBUG_RETURN(HA_ERR_NO_PARTITION_FOUND);
+
+ *part_id= get_part_id_from_linear_hash(*func_value,
+ part_info->linear_hash_mask,
+ no_parts);
+ DBUG_RETURN(FALSE);
+}
+
+
+/*
+ Calculate part_id for (SUB)PARTITION BY KEY
+
+ SYNOPSIS
+ get_part_id_key()
+ field_array Array of fields for PARTTION KEY
+ no_parts Number of KEY partitions
+
+ RETURN VALUE
+ Calculated partition id
+*/
+
+inline
+static uint32 get_part_id_key(Field **field_array,
+ uint no_parts,
+ longlong *func_value)
+{
+ DBUG_ENTER("get_part_id_key");
+ *func_value= calculate_key_value(field_array);
+ DBUG_RETURN((uint32) (*func_value % no_parts));
+}
+
+
+/*
+ Calculate part_id for (SUB)PARTITION BY LINEAR KEY
+
+ SYNOPSIS
+ get_part_id_linear_key()
+ 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
+
+ RETURN VALUE
+ Calculated partition id
+*/
+
+inline
+static uint32 get_part_id_linear_key(partition_info *part_info,
+ Field **field_array,
+ uint no_parts,
+ longlong *func_value)
+{
+ DBUG_ENTER("get_partition_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));
+}
+
+/*
+ Copy to field buffers and set up field pointers
+
+ SYNOPSIS
+ copy_to_part_field_buffers()
+ ptr Array of fields to copy
+ field_bufs Array of field buffers to copy to
+ restore_ptr Array of pointers to restore to
+
+ RETURN VALUES
+ NONE
+ DESCRIPTION
+ This routine is used to take the data from field pointer, convert
+ it to a standard format and store this format in a field buffer
+ allocated for this purpose. Next the field pointers are moved to
+ point to the field buffers. There is a separate to restore the
+ field pointers after this call.
+*/
+
+static void copy_to_part_field_buffers(Field **ptr,
+ uchar **field_bufs,
+ uchar **restore_ptr)
+{
+ Field *field;
+ while ((field= *(ptr++)))
+ {
+ *restore_ptr= field->ptr;
+ restore_ptr++;
+ if (!field->maybe_null() || !field->is_null())
+ {
+ CHARSET_INFO *cs= ((Field_str*)field)->charset();
+ uint len= field->pack_length();
+ uchar *field_buf= *field_bufs;
+ /*
+ We only use the field buffer for VARCHAR and CHAR strings
+ which isn't of a binary collation. We also only use the
+ field buffer for fields which are not currently NULL.
+ The field buffer will store a normalised string. We use
+ the strnxfrm method to normalise the string.
+ */
+ 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);
+ if (len_bytes == 1)
+ *field_buf= (uchar) field->field_length;
+ else
+ int2store(field_buf, field->field_length);
+ }
+ else
+ {
+ my_strnxfrm(cs, field_buf, len,
+ field->ptr, field->field_length);
+ }
+ field->ptr= field_buf;
+ }
+ field_bufs++;
+ }
+ return;
+}
+
+/*
+ Restore field pointers
+ SYNOPSIS
+ restore_part_field_pointers()
+ ptr Array of fields to restore
+ restore_ptr Array of field pointers to restore to
+
+ RETURN VALUES
+*/
+
+static void restore_part_field_pointers(Field **ptr, uchar **restore_ptr)
+{
+ Field *field;
+ while ((field= *(ptr++)))
+ {
+ field->ptr= *restore_ptr;
+ restore_ptr++;
+ }
+ return;
+}
+
+
+/*
+ This function is used to calculate the main partition to use in the case of
+ subpartitioning and we don't know enough to get the partition identity in
+ total.
+
+ SYNOPSIS
+ get_part_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 The value calculated by partition function
+
+ RETURN VALUE
+ 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.
+ 0 OK
+
+ DESCRIPTION
+
+ It is actually 6 different variants of this function which are called
+ through a function pointer.
+
+ get_partition_id_list
+ get_partition_id_range
+ 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;
+}
+
+
+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);
+ res= part_info->get_part_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;
+}
+
+
+static int get_subpart_id_charset_func(partition_info *part_info,
+ uint32 *part_id)
+{
+ 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_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;
+}
+
+
+int get_partition_id_list(partition_info *part_info,
+ uint32 *part_id,
+ longlong *func_value)
+{
+ 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;
+ longlong part_func_value;
+ int error= part_val_int(part_info->part_expr, &part_func_value);
+ longlong list_value;
+ bool unsigned_flag= part_info->part_expr->unsigned_flag;
+ DBUG_ENTER("get_partition_id_list");
+
+ if (error)
+ goto notfound;
+
+ if (part_info->part_expr->null_value)
+ {
+ if (part_info->has_null_value)
+ {
+ *part_id= part_info->has_null_part_id;
+ DBUG_RETURN(0);
+ }
+ goto notfound;
+ }
+ *func_value= part_func_value;
+ if (unsigned_flag)
+ part_func_value-= 0x8000000000000000ULL;
+ while (max_list_index >= min_list_index)
+ {
+ list_index= (max_list_index + min_list_index) >> 1;
+ list_value= list_array[list_index].list_value;
+ if (list_value < part_func_value)
+ min_list_index= list_index + 1;
+ else if (list_value > part_func_value)
+ {
+ if (!list_index)
+ goto notfound;
+ max_list_index= list_index - 1;
+ }
+ else
+ {
+ *part_id= (uint32)list_array[list_index].partition_id;
+ DBUG_RETURN(0);
+ }
+ }
+notfound:
+ *part_id= 0;
+ DBUG_RETURN(HA_ERR_NO_PARTITION_FOUND);
+}
+
+
+/*
+ Find the sub-array part_info->list_array that corresponds to given interval
+
+ 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
+
+ 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.
+
+ 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
+*/
+
+uint32 get_list_array_idx_for_endpoint_charset(partition_info *part_info,
+ bool left_endpoint,
+ bool include_endpoint)
+{
+ uint32 res;
+ copy_to_part_field_buffers(part_info->part_field_array,
+ part_info->part_field_buffers,
+ part_info->restore_part_field_ptrs);
+ res= get_list_array_idx_for_endpoint(part_info, left_endpoint,
+ include_endpoint);
+ restore_part_field_pointers(part_info->part_field_array,
+ part_info->restore_part_field_ptrs);
+ return res;
+}
+
+uint32 get_list_array_idx_for_endpoint(partition_info *part_info,
+ bool left_endpoint,
+ bool include_endpoint)
+{
+ 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;
+ longlong list_value;
+ /* Get the partitioning function value for the endpoint */
+ longlong part_func_value=
+ part_info->part_expr->val_int_endpoint(left_endpoint, &include_endpoint);
+ bool unsigned_flag= part_info->part_expr->unsigned_flag;
+ DBUG_ENTER("get_list_array_idx_for_endpoint");
+
+ if (part_info->part_expr->null_value)
+ {
+ DBUG_RETURN(0);
+ }
+ if (unsigned_flag)
+ part_func_value-= 0x8000000000000000ULL;
+ DBUG_ASSERT(part_info->no_list_values);
+ do
+ {
+ list_index= (max_list_index + min_list_index) >> 1;
+ list_value= list_array[list_index].list_value;
+ if (list_value < part_func_value)
+ min_list_index= list_index + 1;
+ else if (list_value > part_func_value)
+ {
+ if (!list_index)
+ goto notfound;
+ max_list_index= list_index - 1;
+ }
+ else
+ {
+ DBUG_RETURN(list_index + test(left_endpoint ^ include_endpoint));
+ }
+ } while (max_list_index >= min_list_index);
+notfound:
+ if (list_value < part_func_value)
+ list_index++;
+ DBUG_RETURN(list_index);
+}
+
+
+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 min_part_id= 0;
+ uint max_part_id= max_partition;
+ uint loc_part_id;
+ longlong part_func_value;
+ int error= part_val_int(part_info->part_expr, &part_func_value);
+ bool unsigned_flag= part_info->part_expr->unsigned_flag;
+ DBUG_ENTER("get_partition_id_range");
+
+ if (error)
+ DBUG_RETURN(HA_ERR_NO_PARTITION_FOUND);
+
+ if (part_info->part_expr->null_value)
+ {
+ *part_id= 0;
+ DBUG_RETURN(0);
+ }
+ *func_value= part_func_value;
+ if (unsigned_flag)
+ part_func_value-= 0x8000000000000000ULL;
+ while (max_part_id > min_part_id)
+ {
+ loc_part_id= (max_part_id + min_part_id + 1) >> 1;
+ if (range_array[loc_part_id] <= part_func_value)
+ min_part_id= loc_part_id + 1;
+ else
+ max_part_id= loc_part_id - 1;
+ }
+ loc_part_id= max_part_id;
+ if (part_func_value >= range_array[loc_part_id])
+ if (loc_part_id != max_partition)
+ loc_part_id++;
+ *part_id= (uint32)loc_part_id;
+ if (loc_part_id == max_partition &&
+ part_func_value >= range_array[loc_part_id] &&
+ !part_info->defined_max_value)
+ DBUG_RETURN(HA_ERR_NO_PARTITION_FOUND);
+
+ DBUG_PRINT("exit",("partition: %d", *part_id));
+ DBUG_RETURN(0);
+}
+
+
+/*
+ Find the sub-array of part_info->range_int_array that covers given interval
+
+ SYNOPSIS
+ get_partition_id_range_for_endpoint()
+ part_info Partitioning info (partitioning type must be RANGE)
+ left_endpoint TRUE - the interval is [a; +inf) or (a; +inf)
+ FALSE - the interval is (-inf; a] or (-inf; a).
+ include_endpoint TRUE <=> the endpoint itself is included in the
+ interval
+
+ DESCRIPTION
+ This function finds the sub-array of part_info->range_int_array where the
+ elements have non-empty intersections with the given interval.
+
+ A range_int_array element at index idx represents the interval
+
+ [range_int_array[idx-1], range_int_array[idx]),
+
+ intervals are disjoint and ordered by their right bound, 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 the interval
+ represented by range_int_array[idx] has non empty intersection with
+ 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 the interval
+ 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
+ returned.
+
+ RETURN
+ The edge of corresponding part_info->range_int_array sub-array.
+*/
+
+static uint32
+get_partition_id_range_for_endpoint_charset(partition_info *part_info,
+ bool left_endpoint,
+ bool include_endpoint)
+{
+ uint32 res;
+ copy_to_part_field_buffers(part_info->part_field_array,
+ part_info->part_field_buffers,
+ part_info->restore_part_field_ptrs);
+ res= get_partition_id_range_for_endpoint(part_info, left_endpoint,
+ include_endpoint);
+ restore_part_field_pointers(part_info->part_field_array,
+ part_info->restore_part_field_ptrs);
+ return res;
+}
+
+uint32 get_partition_id_range_for_endpoint(partition_info *part_info,
+ bool left_endpoint,
+ bool include_endpoint)
+{
+ longlong *range_array= part_info->range_int_array;
+ uint max_partition= part_info->no_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=
+ part_info->part_expr->val_int_endpoint(left_endpoint, &include_endpoint);
+
+ bool unsigned_flag= part_info->part_expr->unsigned_flag;
+ DBUG_ENTER("get_partition_id_range_for_endpoint");
+
+ if (part_info->part_expr->null_value)
+ {
+ uint32 ret_part_id= 0;
+ if (!left_endpoint && include_endpoint)
+ ret_part_id= 1;
+ DBUG_RETURN(ret_part_id);
+ }
+ if (unsigned_flag)
+ part_func_value-= 0x8000000000000000ULL;
+ if (left_endpoint && !include_endpoint)
+ part_func_value++;
+ while (max_part_id > min_part_id)
+ {
+ loc_part_id= (max_part_id + min_part_id + 1) >> 1;
+ if (range_array[loc_part_id] <= part_func_value)
+ 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 &&
+ part_func_value >= range_array[loc_part_id+1])
+ {
+ loc_part_id++;
+ }
+ if (left_endpoint)
+ {
+ longlong bound= range_array[loc_part_id];
+ /*
+ In case of PARTITION p VALUES LESS THAN MAXVALUE
+ the maximum value is in the current partition.
+ */
+ if (part_func_value > bound ||
+ (part_func_value == bound && !part_info->defined_max_value))
+ loc_part_id++;
+ }
+ else
+ {
+ if (loc_part_id < max_partition)
+ {
+ if (part_func_value == range_array[loc_part_id])
+ loc_part_id += test(include_endpoint);
+ else if (part_func_value > range_array[loc_part_id])
+ loc_part_id++;
+ }
+ loc_part_id++;
+ }
+ DBUG_RETURN(loc_part_id);
+}
+
+
+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,
+ part_id, func_value);
+}
+
+
+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,
+ part_info->part_expr, part_id, func_value);
+}
+
+
+int get_partition_id_key_nosub(partition_info *part_info,
+ uint32 *part_id,
+ longlong *func_value)
+{
+ *part_id= get_part_id_key(part_info->part_field_array,
+ part_info->no_parts, func_value);
+ return 0;
+}
+
+
+int get_partition_id_linear_key_nosub(partition_info *part_info,
+ 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);
+ 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)
+{
+ uint32 loc_part_id, sub_part_id;
+ uint no_subparts;
+ longlong local_func_value;
+ int error;
+ DBUG_ENTER("get_partition_id_range_sub_key");
+
+ if (unlikely((error= get_partition_id_list(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))))
+ {
+ 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);
+}
+
+
+/*
+ This function is used to calculate the subpartition id
+
+ SYNOPSIS
+ get_subpartition_id()
+ part_info A reference to the partition_info struct where all the
+ desired information is given
+
+ RETURN VALUE
+ part_id The subpartition identity
+
+ DESCRIPTION
+ A routine used in some SELECT's when only partial knowledge of the
+ partitions is known.
+
+ It is actually 4 different variants of this function which are called
+ through a function pointer.
+
+ get_partition_id_hash_sub
+ get_partition_id_key_sub
+ get_partition_id_linear_hash_sub
+ get_partition_id_linear_key_sub
+*/
+
+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,
+ part_id, &func_value);
+}
+
+
+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,
+ part_info->subpart_expr, part_id,
+ &func_value);
+}
+
+
+int get_partition_id_key_sub(partition_info *part_info,
+ uint32 *part_id)
+{
+ longlong func_value;
+ *part_id= get_part_id_key(part_info->subpart_field_array,
+ part_info->no_subparts, &func_value);
+ return FALSE;
+}
+
+
+int get_partition_id_linear_key_sub(partition_info *part_info,
+ uint32 *part_id)
+{
+ longlong func_value;
+ *part_id= get_part_id_linear_key(part_info,
+ part_info->subpart_field_array,
+ part_info->no_subparts, &func_value);
+ return FALSE;
+}
+
+
+/*
+ Set an indicator on all partition fields that are set by the key
+
+ SYNOPSIS
+ set_PF_fields_in_key()
+ key_info Information about the index
+ key_length Length of key
+
+ RETURN VALUE
+ TRUE Found partition field set by key
+ FALSE No partition field set by key
+*/
+
+static bool set_PF_fields_in_key(KEY *key_info, uint key_length)
+{
+ KEY_PART_INFO *key_part;
+ bool found_part_field= FALSE;
+ DBUG_ENTER("set_PF_fields_in_key");
+
+ for (key_part= key_info->key_part; (int)key_length > 0; key_part++)
+ {
+ if (key_part->null_bit)
+ key_length--;
+ if (key_part->type == HA_KEYTYPE_BIT)
+ {
+ if (((Field_bit*)key_part->field)->bit_len)
+ key_length--;
+ }
+ if (key_part->key_part_flag & (HA_BLOB_PART + HA_VAR_LENGTH_PART))
+ {
+ key_length-= HA_KEY_BLOB_LENGTH;
+ }
+ if (key_length < key_part->length)
+ break;
+ key_length-= key_part->length;
+ if (key_part->field->flags & FIELD_IN_PART_FUNC_FLAG)
+ {
+ found_part_field= TRUE;
+ key_part->field->flags|= GET_FIXED_FIELDS_FLAG;
+ }
+ }
+ DBUG_RETURN(found_part_field);
+}
+
+
+/*
+ We have found that at least one partition field was set by a key, now
+ check if a partition function has all its fields bound or not.
+
+ SYNOPSIS
+ check_part_func_bound()
+ ptr Array of fields NULL terminated (partition fields)
+
+ RETURN VALUE
+ TRUE All fields in partition function are set
+ FALSE Not all fields in partition function are set
+*/
+
+static bool check_part_func_bound(Field **ptr)
+{
+ bool result= TRUE;
+ DBUG_ENTER("check_part_func_bound");
+
+ for (; *ptr; ptr++)
+ {
+ if (!((*ptr)->flags & GET_FIXED_FIELDS_FLAG))
+ {
+ result= FALSE;
+ break;
+ }
+ }
+ DBUG_RETURN(result);
+}
+
+
+/*
+ Get the id of the subpartitioning part by using the key buffer of the
+ index scan.
+
+ SYNOPSIS
+ get_sub_part_id_from_key()
+ table The table object
+ buf A buffer that can be used to evaluate the partition function
+ key_info The index object
+ key_spec A key_range containing key and key length
+ out:part_id The returned partition id
+
+ RETURN VALUES
+ TRUE All fields in partition function are set
+ FALSE Not all fields in partition function are set
+
+ DESCRIPTION
+ Use key buffer to set-up record in buf, move field pointers and
+ get the partition identity and restore field pointers afterwards.
+*/
+
+static int get_sub_part_id_from_key(const TABLE *table,uchar *buf,
+ KEY *key_info,
+ const key_range *key_spec,
+ uint32 *part_id)
+{
+ uchar *rec0= table->record[0];
+ partition_info *part_info= table->part_info;
+ int res;
+ DBUG_ENTER("get_sub_part_id_from_key");
+
+ key_restore(buf, (uchar*)key_spec->key, key_info, key_spec->length);
+ if (likely(rec0 == buf))
+ {
+ res= part_info->get_subpartition_id(part_info, part_id);
+ }
+ else
+ {
+ Field **part_field_array= part_info->subpart_field_array;
+ set_field_ptr(part_field_array, buf, rec0);
+ res= part_info->get_subpartition_id(part_info, part_id);
+ set_field_ptr(part_field_array, rec0, buf);
+ }
+ DBUG_RETURN(res);
+}
+
+/*
+ Get the id of the partitioning part by using the key buffer of the
+ index scan.
+
+ SYNOPSIS
+ get_part_id_from_key()
+ table The table object
+ buf A buffer that can be used to evaluate the partition function
+ key_info The index object
+ key_spec A key_range containing key and key length
+ out:part_id Partition to use
+
+ RETURN VALUES
+ TRUE Partition to use not found
+ FALSE Ok, part_id indicates partition to use
+
+ DESCRIPTION
+ Use key buffer to set-up record in buf, move field pointers and
+ get the partition identity and restore field pointers afterwards.
+*/
+
+bool get_part_id_from_key(const TABLE *table, uchar *buf, KEY *key_info,
+ const key_range *key_spec, uint32 *part_id)
+{
+ bool result;
+ uchar *rec0= table->record[0];
+ partition_info *part_info= table->part_info;
+ longlong func_value;
+ DBUG_ENTER("get_part_id_from_key");
+
+ key_restore(buf, (uchar*)key_spec->key, key_info, key_spec->length);
+ if (likely(rec0 == buf))
+ {
+ result= part_info->get_part_partition_id(part_info, part_id,
+ &func_value);
+ }
+ else
+ {
+ Field **part_field_array= part_info->part_field_array;
+ set_field_ptr(part_field_array, buf, rec0);
+ result= part_info->get_part_partition_id(part_info, part_id,
+ &func_value);
+ set_field_ptr(part_field_array, rec0, buf);
+ }
+ DBUG_RETURN(result);
+}
+
+/*
+ Get the partitioning id of the full PF by using the key buffer of the
+ index scan.
+
+ SYNOPSIS
+ get_full_part_id_from_key()
+ table The table object
+ buf A buffer that is used to evaluate the partition function
+ key_info The index object
+ key_spec A key_range containing key and key length
+ out:part_spec A partition id containing start part and end part
+
+ RETURN VALUES
+ part_spec
+ No partitions to scan is indicated by end_part > start_part when returning
+
+ DESCRIPTION
+ Use key buffer to set-up record in buf, move field pointers if needed and
+ get the partition identity and restore field pointers afterwards.
+*/
+
+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 result;
+ partition_info *part_info= table->part_info;
+ uchar *rec0= table->record[0];
+ longlong func_value;
+ DBUG_ENTER("get_full_part_id_from_key");
+
+ key_restore(buf, (uchar*)key_spec->key, key_info, key_spec->length);
+ if (likely(rec0 == buf))
+ {
+ result= part_info->get_partition_id(part_info, &part_spec->start_part,
+ &func_value);
+ }
+ else
+ {
+ Field **part_field_array= part_info->full_part_field_array;
+ set_field_ptr(part_field_array, buf, rec0);
+ result= part_info->get_partition_id(part_info, &part_spec->start_part,
+ &func_value);
+ set_field_ptr(part_field_array, rec0, buf);
+ }
+ part_spec->end_part= part_spec->start_part;
+ if (unlikely(result))
+ part_spec->start_part++;
+ DBUG_VOID_RETURN;
+}
+
+/*
+ Prune the set of partitions to use in query
+
+ SYNOPSIS
+ prune_partition_set()
+ table The table object
+ out:part_spec Contains start part, end part
+
+ DESCRIPTION
+ This function is called to prune the range of partitions to scan by
+ checking the used_partitions bitmap.
+ If start_part > end_part at return it means no partition needs to be
+ scanned. If start_part == end_part it always means a single partition
+ needs to be scanned.
+
+ RETURN VALUE
+ part_spec
+*/
+void prune_partition_set(const TABLE *table, part_id_range *part_spec)
+{
+ int last_partition= -1;
+ uint i;
+ partition_info *part_info= table->part_info;
+
+ DBUG_ENTER("prune_partition_set");
+ for (i= part_spec->start_part; i <= part_spec->end_part; i++)
+ {
+ if (bitmap_is_set(&(part_info->used_partitions), i))
+ {
+ DBUG_PRINT("info", ("Partition %d is set", i));
+ if (last_partition == -1)
+ /* First partition found in set and pruned bitmap */
+ part_spec->start_part= i;
+ last_partition= i;
+ }
+ }
+ if (last_partition == -1)
+ /* No partition found in pruned bitmap */
+ part_spec->start_part= part_spec->end_part + 1;
+ else //if (last_partition != -1)
+ part_spec->end_part= last_partition;
+
+ DBUG_VOID_RETURN;
+}
+
+/*
+ Get the set of partitions to use in query.
+
+ SYNOPSIS
+ get_partition_set()
+ table The table object
+ buf A buffer that can be used to evaluate the partition function
+ index The index of the key used, if MAX_KEY no index used
+ key_spec A key_range containing key and key length
+ out:part_spec Contains start part, end part and indicator if bitmap is
+ used for which partitions to scan
+
+ DESCRIPTION
+ This function is called to discover which partitions to use in an index
+ scan or a full table scan.
+ It returns a range of partitions to scan. If there are holes in this
+ range with partitions that are not needed to scan a bit array is used
+ to signal which partitions to use and which not to use.
+ If start_part > end_part at return it means no partition needs to be
+ scanned. If start_part == end_part it always means a single partition
+ needs to be scanned.
+
+ RETURN VALUE
+ part_spec
+*/
+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 i, part_id;
+ uint sub_part= no_parts;
+ uint32 part_part= no_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;
+ if ((index < MAX_KEY) &&
+ key_spec->flag == (uint)HA_READ_KEY_EXACT &&
+ part_info->some_fields_in_PF.is_set(index))
+ {
+ key_info= table->key_info+index;
+ /*
+ The index can potentially provide at least one PF-field (field in the
+ partition function). Thus it is interesting to continue our probe.
+ */
+ if (key_spec->length == key_info->key_length)
+ {
+ /*
+ The entire key is set so we can check whether we can immediately
+ derive either the complete PF or if we can derive either
+ the top PF or the subpartitioning PF. This can be established by
+ checking precalculated bits on each index.
+ */
+ if (part_info->all_fields_in_PF.is_set(index))
+ {
+ /*
+ We can derive the exact partition to use, no more than this one
+ is needed.
+ */
+ get_full_part_id_from_key(table,buf,key_info,key_spec,part_spec);
+ /*
+ Check if range can be adjusted by looking in used_partitions
+ */
+ prune_partition_set(table, part_spec);
+ DBUG_VOID_RETURN;
+ }
+ else if (part_info->is_sub_partitioned())
+ {
+ if (part_info->all_fields_in_SPF.is_set(index))
+ {
+ if (get_sub_part_id_from_key(table, buf, key_info, key_spec, &sub_part))
+ {
+ part_spec->start_part= no_parts;
+ DBUG_VOID_RETURN;
+ }
+ }
+ else if (part_info->all_fields_in_PPF.is_set(index))
+ {
+ if (get_part_id_from_key(table,buf,key_info,
+ key_spec,(uint32*)&part_part))
+ {
+ /*
+ The value of the RANGE or LIST partitioning was outside of
+ allowed values. Thus it is certain that the result of this
+ scan will be empty.
+ */
+ part_spec->start_part= no_parts;
+ DBUG_VOID_RETURN;
+ }
+ }
+ }
+ }
+ else
+ {
+ /*
+ Set an indicator on all partition fields that are bound.
+ If at least one PF-field was bound it pays off to check whether
+ the PF or PPF or SPF has been bound.
+ (PF = Partition Function, SPF = Subpartition Function and
+ PPF = Partition Function part of subpartitioning)
+ */
+ if ((found_part_field= set_PF_fields_in_key(key_info,
+ key_spec->length)))
+ {
+ if (check_part_func_bound(part_info->full_part_field_array))
+ {
+ /*
+ We were able to bind all fields in the partition function even
+ by using only a part of the key. Calculate the partition to use.
+ */
+ get_full_part_id_from_key(table,buf,key_info,key_spec,part_spec);
+ clear_indicator_in_key_fields(key_info);
+ /*
+ Check if range can be adjusted by looking in used_partitions
+ */
+ prune_partition_set(table, part_spec);
+ DBUG_VOID_RETURN;
+ }
+ else if (part_info->is_sub_partitioned())
+ {
+ if (check_part_func_bound(part_info->subpart_field_array))
+ {
+ if (get_sub_part_id_from_key(table, buf, key_info, key_spec, &sub_part))
+ {
+ part_spec->start_part= no_parts;
+ clear_indicator_in_key_fields(key_info);
+ DBUG_VOID_RETURN;
+ }
+ }
+ else if (check_part_func_bound(part_info->part_field_array))
+ {
+ if (get_part_id_from_key(table,buf,key_info,key_spec,&part_part))
+ {
+ part_spec->start_part= no_parts;
+ clear_indicator_in_key_fields(key_info);
+ DBUG_VOID_RETURN;
+ }
+ }
+ }
+ }
+ }
+ }
+ {
+ /*
+ The next step is to analyse the table condition to see whether any
+ information about which partitions to scan can be derived from there.
+ Currently not implemented.
+ */
+ }
+ /*
+ If we come here we have found a range of sorts we have either discovered
+ 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))
+ {
+ /*
+ We can only arrive here if we are using subpartitioning.
+ */
+ if (part_part != no_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;
+ }
+ else
+ {
+ DBUG_ASSERT(sub_part != no_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)
+ ; //Set bit part_id in bit array
+ }
+ }
+ if (found_part_field)
+ clear_indicator_in_key_fields(key_info);
+ /*
+ Check if range can be adjusted by looking in used_partitions
+ */
+ prune_partition_set(table, part_spec);
+ DBUG_VOID_RETURN;
+}
+
+/*
+ If the table is partitioned we will read the partition info into the
+ .frm file here.
+ -------------------------------
+ | Fileinfo 64 bytes |
+ -------------------------------
+ | Formnames 7 bytes |
+ -------------------------------
+ | Not used 4021 bytes |
+ -------------------------------
+ | Keyinfo + record |
+ -------------------------------
+ | Padded to next multiple |
+ | of IO_SIZE |
+ -------------------------------
+ | Forminfo 288 bytes |
+ -------------------------------
+ | Screen buffer, to make |
+ |field names readable |
+ -------------------------------
+ | Packed field info |
+ |17 + 1 + strlen(field_name) |
+ | + 1 end of file character |
+ -------------------------------
+ | Partition info |
+ -------------------------------
+ We provide the length of partition length in Fileinfo[55-58].
+
+ Read the partition syntax from the frm file and parse it to get the
+ data structures of the partitioning.
+
+ SYNOPSIS
+ mysql_unpack_partition()
+ thd Thread object
+ part_buf Partition info from frm file
+ part_info_len Length of partition syntax
+ table Table object of partitioned table
+ create_table_ind Is it called from CREATE TABLE
+ default_db_type What is the default engine of the table
+ work_part_info_used Flag is raised if we don't create new
+ part_info, but used thd->work_part_info
+
+ RETURN VALUE
+ TRUE Error
+ FALSE Sucess
+
+ DESCRIPTION
+ Read the partition syntax from the current position in the frm file.
+ Initiate a LEX object, save the list of item tree objects to free after
+ the query is done. Set-up partition info object such that parser knows
+ it is called from internally. Call parser to create data structures
+ (best possible recreation of item trees and so forth since there is no
+ serialisation of these objects other than in parseable text format).
+ We need to save the text of the partition functions since it is not
+ possible to retrace this given an item tree.
+*/
+
+bool mysql_unpack_partition(THD *thd,
+ const 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)
+{
+ bool result= TRUE;
+ partition_info *part_info;
+ CHARSET_INFO *old_character_set_client= thd->variables.character_set_client;
+ LEX *old_lex= thd->lex;
+ LEX lex;
+ DBUG_ENTER("mysql_unpack_partition");
+
+ thd->lex= &lex;
+ thd->variables.character_set_client= system_charset_info;
+
+ Parser_state parser_state(thd, part_buf, part_info_len);
+
+ 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.
+ */
+ thd->lex->current_select= old_lex->current_select;
+ /*
+ All Items created is put into a free list on the THD object. This list
+ is used to free all Item objects after completing a query. We don't
+ want that to happen with the Item tree created as part of the partition
+ info. This should be attached to the table object and remain so until
+ the table object is released.
+ Thus we move away the current list temporarily and start a new list that
+ we then save in the partition info structure.
+ */
+ 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;
+ DBUG_PRINT("info", ("Parse: %s", part_buf));
+ if (parse_sql(thd, & parser_state, NULL))
+ {
+ thd->free_items();
+ goto end;
+ }
+ /*
+ The parsed syntax residing in the frm file can still contain defaults.
+ The reason is that the frm file is sometimes saved outside of this
+ MySQL Server and used in backup and restore of clusters or partitioned
+ tables. It is not certain that the restore will restore exactly the
+ same default partitioning.
+
+ The easiest manner of handling this is to simply continue using the
+ part_info we already built up during mysql_create_table if we are
+ in the process of creating a table. If the table already exists we
+ need to discover the number of partitions for the default parts. Since
+ the handler object hasn't been created here yet we need to postpone this
+ to the fix_partition_func method.
+ */
+
+ 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;
+ }
+ }
+ table->part_info= part_info;
+ table->file->set_part_info(part_info);
+ if (!part_info->default_engine_type)
+ part_info->default_engine_type= default_db_type;
+ DBUG_ASSERT(part_info->default_engine_type == default_db_type);
+ DBUG_ASSERT(part_info->default_engine_type->db_type != DB_TYPE_UNKNOWN);
+ DBUG_ASSERT(part_info->default_engine_type != partition_hton);
+
+ {
+ /*
+ This code part allocates memory for the serialised item information for
+ the partition functions. In most cases this is not needed but if the
+ table is used for SHOW CREATE TABLES or ALTER TABLE that modifies
+ partition information it is needed and the info is lost if we don't
+ save it here so unfortunately we have to do it here even if in most
+ cases it is not needed. This is a consequence of that item trees are
+ not serialisable.
+ */
+ uint part_func_len= part_info->part_func_len;
+ uint subpart_func_len= part_info->subpart_func_len;
+ char *part_func_string= NULL;
+ char *subpart_func_string= NULL;
+ if ((part_func_len &&
+ !((part_func_string= (char*) thd->alloc(part_func_len)))) ||
+ (subpart_func_len &&
+ !((subpart_func_string= (char*) thd->alloc(subpart_func_len)))))
+ {
+ mem_alloc_error(part_func_len);
+ thd->free_items();
+ goto end;
+ }
+ if (part_func_len)
+ memcpy(part_func_string, part_info->part_func_string, part_func_len);
+ if (subpart_func_len)
+ memcpy(subpart_func_string, part_info->subpart_func_string,
+ subpart_func_len);
+ part_info->part_func_string= part_func_string;
+ part_info->subpart_func_string= subpart_func_string;
+ }
+
+ result= FALSE;
+end:
+ lex_end(thd->lex);
+ thd->lex= old_lex;
+ thd->variables.character_set_client= old_character_set_client;
+ DBUG_RETURN(result);
+}
+
+
+/*
+ Set engine type on all partition element objects
+ SYNOPSIS
+ set_engine_all_partitions()
+ part_info Partition info
+ engine_type Handlerton reference of engine
+ RETURN VALUES
+ NONE
+*/
+
+static
+void
+set_engine_all_partitions(partition_info *part_info,
+ handlerton *engine_type)
+{
+ uint i= 0;
+ List_iterator<partition_element> part_it(part_info->partitions);
+ do
+ {
+ partition_element *part_elem= part_it++;
+
+ part_elem->engine_type= engine_type;
+ if (part_info->is_sub_partitioned())
+ {
+ List_iterator<partition_element> sub_it(part_elem->subpartitions);
+ uint j= 0;
+
+ do
+ {
+ partition_element *sub_elem= sub_it++;
+
+ sub_elem->engine_type= engine_type;
+ } while (++j < part_info->no_subparts);
+ }
+ } while (++i < part_info->no_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.
+*/
+
+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)
+{
+ 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);
+
+ my_snprintf(tmp_name, sizeof(tmp_name), ER(ER_INSERT_INFO),
+ (ulong) (copied + deleted),
+ (ulong) deleted,
+ (ulong) 0);
+ my_ok(thd, (ha_rows) (copied+deleted),0L, tmp_name);
+ DBUG_RETURN(FALSE);
+}
+
+
+/*
+ We need to check if engine used by all partitions can handle
+ partitioning natively.
+
+ SYNOPSIS
+ check_native_partitioned()
+ create_info Create info in CREATE TABLE
+ out:ret_val Return value
+ part_info Partition info
+ thd Thread object
+
+ RETURN VALUES
+ Value returned in bool ret_value
+ TRUE Native partitioning supported by engine
+ FALSE Need to use partition handler
+
+ Return value from function
+ TRUE Error
+ FALSE Success
+*/
+
+static bool check_native_partitioned(HA_CREATE_INFO *create_info,bool *ret_val,
+ partition_info *part_info, THD *thd)
+{
+ bool table_engine_set;
+ handlerton *engine_type= part_info->default_engine_type;
+ handlerton *old_engine_type= engine_type;
+ DBUG_ENTER("check_native_partitioned");
+
+ if (create_info->used_fields & HA_CREATE_USED_ENGINE)
+ {
+ table_engine_set= TRUE;
+ engine_type= create_info->db_type;
+ }
+ else
+ {
+ table_engine_set= FALSE;
+ if (thd->lex->sql_command != SQLCOM_CREATE_TABLE)
+ {
+ table_engine_set= TRUE;
+ DBUG_ASSERT(engine_type && engine_type != partition_hton);
+ }
+ }
+ DBUG_PRINT("info", ("engine_type = %s, table_engine_set = %u",
+ ha_resolve_storage_engine_name(engine_type),
+ table_engine_set));
+ if (part_info->check_engine_mix(engine_type, table_engine_set))
+ goto error;
+
+ /*
+ All engines are of the same type. Check if this engine supports
+ native partitioning.
+ */
+
+ if (!engine_type)
+ engine_type= old_engine_type;
+ DBUG_PRINT("info", ("engine_type = %s",
+ ha_resolve_storage_engine_name(engine_type)));
+ if (engine_type->partition_flags &&
+ (engine_type->partition_flags() & HA_CAN_PARTITION))
+ {
+ create_info->db_type= engine_type;
+ DBUG_PRINT("info", ("Changed to native partitioning"));
+ *ret_val= TRUE;
+ }
+ DBUG_RETURN(FALSE);
+error:
+ /*
+ Mixed engines not yet supported but when supported it will need
+ the partition handler
+ */
+ my_error(ER_MIX_HANDLER_ERROR, MYF(0));
+ *ret_val= FALSE;
+ DBUG_RETURN(TRUE);
+}
+
+
+/*
+ Sets which partitions to be used in the command
+*/
+uint 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;
+ List_iterator<partition_element> part_it(tab_part_info->partitions);
+
+ do
+ {
+ partition_element *part_elem= part_it++;
+ if ((alter_info->flags & ALTER_ALL_PARTITION) ||
+ (is_name_in_list(part_elem->partition_name,
+ alter_info->partition_names)))
+ {
+ /*
+ Mark the partition.
+ I.e mark the partition as a partition to be "changed" by
+ analyzing/optimizing/rebuilding/checking/repairing
+ */
+ no_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;
+}
+
+
+/*
+ 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
+
+ RETURN VALUES
+ TRUE Error
+ FALSE Success
+ partition_changed
+ fast_alter_partition
+
+ DESCRIPTION
+ This method handles all preparations for ALTER TABLE for partitioned
+ 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
+ by checking the partition management variants and then check the general
+ change patterns.
+*/
+
+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)
+{
+ DBUG_ENTER("prep_alter_part_table");
+
+ /*
+ 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);
+
+ /* ALTER_ADMIN_PARTITION is handled in mysql_admin_table */
+ DBUG_ASSERT(!(alter_info->flags & ALTER_ADMIN_PARTITION));
+
+ if (alter_info->flags &
+ (ALTER_ADD_PARTITION | ALTER_DROP_PARTITION |
+ ALTER_COALESCE_PARTITION | ALTER_REORGANIZE_PARTITION |
+ ALTER_TABLE_REORG | ALTER_REBUILD_PARTITION))
+ {
+ partition_info *tab_part_info= table->part_info;
+ partition_info *alt_part_info= thd->work_part_info;
+ uint flags= 0;
+ if (!tab_part_info)
+ {
+ my_error(ER_PARTITION_MGMT_ON_NONPARTITIONED, MYF(0));
+ DBUG_RETURN(TRUE);
+ }
+ 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)
+ {
+ my_error(ER_REORG_NO_PARAM_ERROR, MYF(0));
+ DBUG_RETURN(TRUE);
+ }
+ new_part_no= table->file->get_default_no_partitions(create_info);
+ curr_part_no= tab_part_info->no_parts;
+ if (new_part_no == curr_part_no)
+ {
+ /*
+ No change is needed, we will have the same number of partitions
+ after the change as before. Thus we can reply ok immediately
+ without any changes at all.
+ */
+ *fast_alter_partition= TRUE;
+ DBUG_RETURN(FALSE);
+ }
+ else if (new_part_no > curr_part_no)
+ {
+ /*
+ We will add more partitions, we use the ADD PARTITION without
+ setting the flag for no default number of partitions
+ */
+ alter_info->flags|= ALTER_ADD_PARTITION;
+ thd->work_part_info->no_parts= new_part_no - curr_part_no;
+ }
+ else
+ {
+ /*
+ We will remove hash partitions, we use the COALESCE PARTITION
+ without setting the flag for no default number of partitions
+ */
+ alter_info->flags|= ALTER_COALESCE_PARTITION;
+ alter_info->no_parts= curr_part_no - new_part_no;
+ }
+ }
+ if (!(flags= table->file->alter_table_flags(alter_info->flags)))
+ {
+ my_error(ER_PARTITION_FUNCTION_FAILURE, MYF(0));
+ DBUG_RETURN(1);
+ }
+ *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 (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");
+ }
+ DBUG_RETURN(TRUE);
+ }
+ if (alter_info->flags & ALTER_ADD_PARTITION)
+ {
+ /*
+ We start by moving the new partitions to the list of temporary
+ partitions. We will then check that the new partitions fit in the
+ 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 new_total_partitions= check_total_partitions;
+ /*
+ We allow quite a lot of values to be supplied by defaults, however we
+ must know the number of new partitions in this case.
+ */
+ if (thd->lex->no_write_to_binlog &&
+ tab_part_info->part_type != HASH_PARTITION)
+ {
+ my_error(ER_NO_BINLOG_ERROR, MYF(0));
+ DBUG_RETURN(TRUE);
+ }
+ if (tab_part_info->defined_max_value)
+ {
+ my_error(ER_PARTITION_MAXVALUE_ERROR, MYF(0));
+ DBUG_RETURN(TRUE);
+ }
+ if (no_new_partitions == 0)
+ {
+ my_error(ER_ADD_PARTITION_NO_NEW_PARTITION, MYF(0));
+ DBUG_RETURN(TRUE);
+ }
+ 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)
+ {
+ my_error(ER_ADD_PARTITION_SUBPART_ERROR, MYF(0));
+ DBUG_RETURN(TRUE);
+ }
+ check_total_partitions= new_total_partitions*
+ alt_part_info->no_subparts;
+ }
+ if (check_total_partitions > MAX_PARTITIONS)
+ {
+ my_error(ER_TOO_MANY_PARTITIONS_ERROR, MYF(0));
+ DBUG_RETURN(TRUE);
+ }
+ 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))
+ {
+ DBUG_RETURN(TRUE);
+ }
+/*
+Handling of on-line cases:
+
+ADD PARTITION for RANGE/LIST PARTITIONING:
+------------------------------------------
+For range and list partitions add partition is simply adding a
+new empty partition to the table. If the handler support this we
+will use the simple method of doing this. The figure below shows
+an example of this and the states involved in making this change.
+
+Existing partitions New added partitions
+------ ------ ------ ------ | ------ ------
+| | | | | | | | | | | | |
+| p0 | | p1 | | p2 | | p3 | | | p4 | | p5 |
+------ ------ ------ ------ | ------ ------
+PART_NORMAL PART_NORMAL PART_NORMAL PART_NORMAL PART_TO_BE_ADDED*2
+PART_NORMAL PART_NORMAL PART_NORMAL PART_NORMAL PART_IS_ADDED*2
+
+The first line is the states before adding the new partitions and the
+second line is after the new partitions are added. All the partitions are
+in the partitions list, no partitions are placed in the temp_partitions
+list.
+
+ADD PARTITION for HASH PARTITIONING
+-----------------------------------
+This little figure tries to show the various partitions involved when
+adding two new partitions to a linear hash based partitioned table with
+four partitions to start with, which lists are used and the states they
+pass through. Adding partitions to a normal hash based is similar except
+that it is always all the existing partitions that are reorganised not
+only a subset of them.
+
+Existing partitions New added partitions
+------ ------ ------ ------ | ------ ------
+| | | | | | | | | | | | |
+| p0 | | p1 | | p2 | | p3 | | | p4 | | p5 |
+------ ------ ------ ------ | ------ ------
+PART_CHANGED PART_CHANGED PART_NORMAL PART_NORMAL PART_TO_BE_ADDED
+PART_IS_CHANGED*2 PART_NORMAL PART_NORMAL PART_IS_ADDED
+PART_NORMAL PART_NORMAL PART_NORMAL PART_NORMAL PART_IS_ADDED
+
+Reorganised existing partitions
+------ ------
+| | | |
+| p0'| | p1'|
+------ ------
+
+p0 - p5 will be in the partitions list of partitions.
+p0' and p1' will actually not exist as separate objects, there presence can
+be deduced from the state of the partition and also the names of those
+partitions can be deduced this way.
+
+After adding the partitions and copying the partition data to p0', p1',
+p4 and p5 from p0 and p1 the states change to adapt for the new situation
+where p0 and p1 is dropped and replaced by p0' and p1' and the new p4 and
+p5 are in the table again.
+
+The first line above shows the states of the partitions before we start
+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 &&
+ tab_part_info->part_type == HASH_PARTITION)
+ {
+ uint part_no= 0, start_part= 1, start_sec_part= 1;
+ uint end_part= 0, end_sec_part= 0;
+ uint upper_2n= tab_part_info->linear_hash_mask + 1;
+ uint lower_2n= upper_2n >> 1;
+ bool all_parts= TRUE;
+ if (tab_part_info->linear_hash_ind &&
+ no_new_partitions < upper_2n)
+ {
+ /*
+ An analysis of which parts needs reorganisation shows that it is
+ divided into two intervals. The first interval is those parts
+ that are reorganised up until upper_2n - 1. From upper_2n and
+ 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).
+ 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
+ lower_2n partitions and going beyond a total of upper_2n we
+ actually get two intervals.
+
+ To exemplify this assume we have 6 partitions to start with and
+ add 1, 2, 3, 5, 6, 7, 8, 9 partitions.
+ The first to add after p5 is p6 = 110 in bit numbers. Thus we
+ can see that 10 = p2 will be partition to reorganise if only one
+ partition.
+ If 2 partitions are added we reorganise [p2, p3]. Those two
+ cases are covered by the second if part below.
+ If 3 partitions are added we reorganise [p2, p3] U [p0,p0]. This
+ part is covered by the else part below.
+ If 5 partitions are added we get [p2,p3] U [p0, p2] = [p0, p3].
+ This is covered by the first if part where we need the max check
+ to here use lower_2n - 1.
+ If 7 partitions are added we get [p2,p3] U [p0, p4] = [p0, p4].
+ This is covered by the first if part but here we use the first
+ calculated end_part.
+ Finally with 9 new partitions we would also reorganise p6 if we
+ used the method below but we cannot reorganise more partitions
+ than what we had from the start and thus we simply set all_parts
+ to TRUE. In this case we don't get into this if-part at all.
+ */
+ all_parts= FALSE;
+ if (no_new_partitions >= lower_2n)
+ {
+ /*
+ In this case there is only one interval since the two intervals
+ overlap and this starts from zero to last_part_no - upper_2n
+ */
+ start_part= 0;
+ end_part= new_total_partitions - (upper_2n + 1);
+ end_part= max(lower_2n - 1, end_part);
+ }
+ else if (new_total_partitions <= upper_2n)
+ {
+ /*
+ 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);
+ }
+ else
+ {
+ /* We have two non-overlapping intervals since we are not
+ passing a 2**n border and we have not at least lower_2n
+ new parts that would ensure that the intervals become
+ overlapping.
+ */
+ start_part= no_orig_partitions - lower_2n;
+ end_part= upper_2n - 1;
+ start_sec_part= 0;
+ end_sec_part= new_total_partitions - (upper_2n + 1);
+ }
+ }
+ List_iterator<partition_element> tab_it(tab_part_info->partitions);
+ part_no= 0;
+ do
+ {
+ partition_element *p_elem= tab_it++;
+ if (all_parts ||
+ (part_no >= start_part && part_no <= end_part) ||
+ (part_no >= start_sec_part && part_no <= end_sec_part))
+ {
+ p_elem->part_state= PART_CHANGED;
+ }
+ } while (++part_no < no_orig_partitions);
+ }
+ /*
+ Need to concatenate the lists here to make it possible to check the
+ partition info for correctness using check_partition_info.
+ For on-line add partition we set the state of this partition to
+ PART_TO_BE_ADDED to ensure that it is known that it is not yet
+ usable (becomes usable when partition is created and the switch of
+ partition configuration is made.
+ */
+ {
+ List_iterator<partition_element> alt_it(alt_part_info->partitions);
+ uint part_count= 0;
+ do
+ {
+ partition_element *part_elem= alt_it++;
+ if (*fast_alter_partition)
+ part_elem->part_state= PART_TO_BE_ADDED;
+ if (tab_part_info->partitions.push_back(part_elem))
+ {
+ mem_alloc_error(1);
+ DBUG_RETURN(TRUE);
+ }
+ } while (++part_count < no_new_partitions);
+ tab_part_info->no_parts+= no_new_partitions;
+ }
+ /*
+ If we specify partitions explicitly we don't use defaults anymore.
+ Using ADD PARTITION also means that we don't have the default number
+ of partitions anymore. We use this code also for Table reorganisations
+ and here we don't set any default flags to FALSE.
+ */
+ if (!(alter_info->flags & ALTER_TABLE_REORG))
+ {
+ if (!alt_part_info->use_default_partitions)
+ {
+ 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->is_auto_partitioned= FALSE;
+ }
+ }
+ else if (alter_info->flags & ALTER_DROP_PARTITION)
+ {
+ /*
+ Drop a partition from a range partition and list partitioning is
+ always safe and can be made more or less immediate. It is necessary
+ however to ensure that the partition to be removed is safely removed
+ and that REPAIR TABLE can remove the partition if for some reason the
+ 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;
+ List_iterator<partition_element> part_it(tab_part_info->partitions);
+
+ tab_part_info->is_auto_partitioned= FALSE;
+ if (!(tab_part_info->part_type == RANGE_PARTITION ||
+ tab_part_info->part_type == LIST_PARTITION))
+ {
+ my_error(ER_ONLY_ON_RANGE_LIST_PARTITION, MYF(0), "DROP");
+ DBUG_RETURN(TRUE);
+ }
+ if (no_parts_dropped >= tab_part_info->no_parts)
+ {
+ my_error(ER_DROP_LAST_PARTITION, MYF(0));
+ DBUG_RETURN(TRUE);
+ }
+ do
+ {
+ partition_element *part_elem= part_it++;
+ if (is_name_in_list(part_elem->partition_name,
+ alter_info->partition_names))
+ {
+ /*
+ Set state to indicate that the partition is to be dropped.
+ */
+ no_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)
+ {
+ my_error(ER_DROP_PARTITION_NON_EXISTENT, MYF(0), "DROP");
+ DBUG_RETURN(TRUE);
+ }
+ if (table->file->is_fk_defined_on_table_or_index(MAX_KEY))
+ {
+ my_error(ER_ROW_IS_REFERENCED, MYF(0));
+ DBUG_RETURN(TRUE);
+ }
+ tab_part_info->no_parts-= no_parts_dropped;
+ }
+ else if (alter_info->flags & ALTER_REBUILD_PARTITION)
+ {
+ uint no_parts_found;
+ uint no_parts_opt= alter_info->partition_names.elements;
+ 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)))
+ {
+ my_error(ER_DROP_PARTITION_NON_EXISTENT, MYF(0), "REBUILD");
+ DBUG_RETURN(TRUE);
+ }
+ if (!(*fast_alter_partition))
+ {
+ table->file->print_error(HA_ERR_WRONG_COMMAND, MYF(0));
+ DBUG_RETURN(TRUE);
+ }
+ }
+ 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;
+ 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);
+ }
+ if (no_parts_coalesced == 0)
+ {
+ my_error(ER_COALESCE_PARTITION_NO_PARTITION, MYF(0));
+ DBUG_RETURN(TRUE);
+ }
+ if (no_parts_coalesced >= tab_part_info->no_parts)
+ {
+ my_error(ER_DROP_LAST_PARTITION, MYF(0));
+ DBUG_RETURN(TRUE);
+ }
+/*
+Online handling:
+COALESCE PARTITION:
+-------------------
+The figure below shows the manner in which partitions are handled when
+performing an on-line coalesce partition and which states they go through
+at start, after adding and copying partitions and finally after dropping
+the partitions to drop. The figure shows an example using four partitions
+to start with, using linear hash and coalescing one partition (always the
+last partition).
+
+Using linear hash then all remaining partitions will have a new reorganised
+part.
+
+Existing partitions Coalesced partition
+------ ------ ------ | ------
+| | | | | | | | |
+| p0 | | p1 | | p2 | | | p3 |
+------ ------ ------ | ------
+PART_NORMAL PART_CHANGED PART_NORMAL PART_REORGED_DROPPED
+PART_NORMAL PART_IS_CHANGED PART_NORMAL PART_TO_BE_DROPPED
+PART_NORMAL PART_NORMAL PART_NORMAL PART_IS_DROPPED
+
+Reorganised existing partitions
+ ------
+ | |
+ | p1'|
+ ------
+
+p0 - p3 is in the partitions list.
+The p1' partition will actually not be in any list it is deduced from the
+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 &&
+ 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)
+ {
+ all_parts= TRUE;
+ }
+ else if (no_parts_remain >= lower_2n)
+ {
+ end_part= tab_part_info->no_parts - (lower_2n + 1);
+ start_part= no_parts_remain - lower_2n;
+ }
+ else
+ {
+ start_part= 0;
+ end_part= tab_part_info->no_parts - (lower_2n + 1);
+ end_sec_part= (lower_2n >> 1) - 1;
+ start_sec_part= end_sec_part - (lower_2n - (no_parts_remain + 1));
+ }
+ }
+ do
+ {
+ partition_element *p_elem= part_it++;
+ if (*fast_alter_partition &&
+ (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 (*fast_alter_partition)
+ 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;
+ }
+ if (!(alter_info->flags & ALTER_TABLE_REORG))
+ {
+ tab_part_info->use_default_no_partitions= FALSE;
+ tab_part_info->is_auto_partitioned= FALSE;
+ }
+ }
+ else if (alter_info->flags & ALTER_REORGANIZE_PARTITION)
+ {
+ /*
+ Reorganise partitions takes a number of partitions that are next
+ to each other (at least for RANGE PARTITIONS) and then uses those
+ to create a set of new partitions. So data is copied from those
+ partitions into the new set of partitions. Those new partitions
+ can have more values in the LIST value specifications or less both
+ are allowed. The ranges can be different but since they are
+ changing a set of consecutive partitions they must cover the same
+ 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 check_total_partitions;
+
+ tab_part_info->is_auto_partitioned= FALSE;
+ if (no_parts_reorged > tab_part_info->no_parts)
+ {
+ my_error(ER_REORG_PARTITION_NOT_EXIST, MYF(0));
+ DBUG_RETURN(TRUE);
+ }
+ if (!(tab_part_info->part_type == RANGE_PARTITION ||
+ tab_part_info->part_type == LIST_PARTITION) &&
+ (no_parts_new != no_parts_reorged))
+ {
+ my_error(ER_REORG_HASH_ONLY_ON_SAME_NO, MYF(0));
+ DBUG_RETURN(TRUE);
+ }
+ if (tab_part_info->is_sub_partitioned() &&
+ alt_part_info->no_subparts &&
+ alt_part_info->no_subparts != tab_part_info->no_subparts)
+ {
+ my_error(ER_PARTITION_WRONG_NO_SUBPART_ERROR, MYF(0));
+ DBUG_RETURN(TRUE);
+ }
+ check_total_partitions= tab_part_info->no_parts + no_parts_new;
+ check_total_partitions-= no_parts_reorged;
+ if (check_total_partitions > MAX_PARTITIONS)
+ {
+ my_error(ER_TOO_MANY_PARTITIONS_ERROR, MYF(0));
+ DBUG_RETURN(TRUE);
+ }
+ 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;
+ DBUG_ASSERT(!alt_part_info->use_default_partitions);
+ if (alt_part_info->set_up_defaults_for_partitioning(table->file,
+ ULL(0),
+ 0))
+ {
+ DBUG_RETURN(TRUE);
+ }
+/*
+Online handling:
+REORGANIZE PARTITION:
+---------------------
+The figure exemplifies the handling of partitions, their state changes and
+how they are organised. It exemplifies four partitions where two of the
+partitions are reorganised (p1 and p2) into two new partitions (p4 and p5).
+The reason of this change could be to change range limits, change list
+values or for hash partitions simply reorganise the partition which could
+also involve moving them to new disks or new node groups (MySQL Cluster).
+
+Existing partitions
+------ ------ ------ ------
+| | | | | | | |
+| p0 | | p1 | | p2 | | p3 |
+------ ------ ------ ------
+PART_NORMAL PART_TO_BE_REORGED PART_NORMAL
+PART_NORMAL PART_TO_BE_DROPPED PART_NORMAL
+PART_NORMAL PART_IS_DROPPED PART_NORMAL
+
+Reorganised new partitions (replacing p1 and p2)
+------ ------
+| | | |
+| p4 | | p5 |
+------ ------
+PART_TO_BE_ADDED
+PART_IS_ADDED
+PART_IS_ADDED
+
+All unchanged partitions and the new partitions are in the partitions list
+in the order they will have when the change is completed. The reorganised
+partitions are placed in the temp_partitions list. PART_IS_ADDED is only a
+temporary state not written in the frm file. It is used to ensure we write
+the generated partition syntax in a correct manner.
+*/
+ {
+ List_iterator<partition_element> tab_it(tab_part_info->partitions);
+ 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++;
+ is_last_partition_reorged= FALSE;
+ if (is_name_in_list(part_elem->partition_name,
+ alter_info->partition_names))
+ {
+ is_last_partition_reorged= TRUE;
+ drop_count++;
+ tab_max_range= part_elem->range_value;
+ if (*fast_alter_partition &&
+ tab_part_info->temp_partitions.push_back(part_elem))
+ {
+ mem_alloc_error(1);
+ DBUG_RETURN(TRUE);
+ }
+ if (*fast_alter_partition)
+ part_elem->part_state= PART_TO_BE_REORGED;
+ if (!found_first)
+ {
+ uint alt_part_count= 0;
+ found_first= TRUE;
+ List_iterator<partition_element>
+ alt_it(alt_part_info->partitions);
+ do
+ {
+ partition_element *alt_part_elem= alt_it++;
+ alt_max_range= alt_part_elem->range_value;
+ if (*fast_alter_partition)
+ 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);
+ }
+ else if (found_last)
+ {
+ my_error(ER_CONSECUTIVE_REORG_PARTITIONS, MYF(0));
+ DBUG_RETURN(TRUE);
+ }
+ else
+ tab_it.remove();
+ }
+ else
+ {
+ if (found_first)
+ found_last= TRUE;
+ }
+ } while (++part_count < tab_part_info->no_parts);
+ if (drop_count != no_parts_reorged)
+ {
+ my_error(ER_DROP_PARTITION_NON_EXISTENT, MYF(0), "REORGANIZE");
+ DBUG_RETURN(TRUE);
+ }
+ 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;
+ }
+ }
+ else
+ {
+ DBUG_ASSERT(FALSE);
+ }
+ *partition_changed= TRUE;
+ thd->work_part_info= tab_part_info;
+ if (alter_info->flags & ALTER_ADD_PARTITION ||
+ alter_info->flags & ALTER_REORGANIZE_PARTITION)
+ {
+ if (tab_part_info->use_default_subpartitions &&
+ !alt_part_info->use_default_subpartitions)
+ {
+ tab_part_info->use_default_subpartitions= FALSE;
+ tab_part_info->use_default_no_subpartitions= FALSE;
+ }
+ if (tab_part_info->check_partition_info(thd, (handlerton**)NULL,
+ table->file, ULL(0), FALSE))
+ {
+ DBUG_RETURN(TRUE);
+ }
+ }
+ }
+ else
+ {
+ /*
+ When thd->lex->part_info has a reference to a partition_info the
+ ALTER TABLE contained a definition of a partitioning.
+
+ Case I:
+ If there was a partition before and there is a new one defined.
+ We use the new partitioning. The new partitioning is already
+ defined in the correct variable so no work is needed to
+ accomplish this.
+ We do however need to update partition_changed to ensure that not
+ only the frm file is changed in the ALTER TABLE command.
+
+ Case IIa:
+ There was a partitioning before and there is no new one defined.
+ Also the user has not specified to remove partitioning explicitly.
+
+ We use the old partitioning also for the new table. We do this
+ by assigning the partition_info from the table loaded in
+ open_table to the partition_info struct used by mysql_create_table
+ later in this method.
+
+ Case IIb:
+ There was a partitioning before and there is no new one defined.
+ The user has specified explicitly to remove partitioning
+
+ Since the user has specified explicitly to remove partitioning
+ we override the old partitioning info and create a new table using
+ the specified engine.
+ In this case the partition also is changed.
+
+ Case III:
+ There was no partitioning before altering the table, there is
+ partitioning defined in the altered table. Use the new partitioning.
+ No work needed since the partitioning info is already in the
+ correct variable.
+
+ In this case we discover one case where the new partitioning is using
+ the same partition function as the default (PARTITION BY KEY or
+ PARTITION BY LINEAR KEY with the list of fields equal to the primary
+ key fields OR PARTITION BY [LINEAR] KEY() for tables without primary
+ key)
+ Also here partition has changed and thus a new table must be
+ created.
+
+ Case IV:
+ There was no partitioning before and no partitioning defined.
+ Obviously no work needed.
+ */
+ if (table->part_info)
+ {
+ if (alter_info->flags & ALTER_REMOVE_PARTITIONING)
+ {
+ DBUG_PRINT("info", ("Remove partitioning"));
+ 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;
+ }
+ DBUG_PRINT("info", ("New engine type: %s",
+ ha_resolve_storage_engine_name(create_info->db_type)));
+ thd->work_part_info= NULL;
+ *partition_changed= TRUE;
+ }
+ else if (!thd->work_part_info)
+ {
+ /*
+ Retain partitioning but possibly with a new storage engine
+ beneath.
+ */
+ thd->work_part_info= table->part_info;
+ if (create_info->used_fields & HA_CREATE_USED_ENGINE &&
+ create_info->db_type != table->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 the user originally didn't specify partitioning to be
+ used we can remove it now.
+ */
+ thd->work_part_info= NULL;
+ }
+ else
+ {
+ /*
+ Ensure that all partitions have the proper engine set-up
+ */
+ set_engine_all_partitions(thd->work_part_info,
+ create_info->db_type);
+ }
+ *partition_changed= TRUE;
+ }
+ }
+ }
+ if (thd->work_part_info)
+ {
+ partition_info *part_info= thd->work_part_info;
+ bool is_native_partitioned= FALSE;
+ /*
+ Need to cater for engine types that can handle partition without
+ using the partition handler.
+ */
+ if (thd->work_part_info != table->part_info)
+ {
+ DBUG_PRINT("info", ("partition changed"));
+ *partition_changed= TRUE;
+ }
+ /*
+ Set up partition default_engine_type either from the create_info
+ or from the previus table
+ */
+ if (create_info->used_fields & HA_CREATE_USED_ENGINE)
+ part_info->default_engine_type= create_info->db_type;
+ else
+ {
+ if (table->part_info)
+ part_info->default_engine_type= table->part_info->default_engine_type;
+ else
+ part_info->default_engine_type= create_info->db_type;
+ }
+ DBUG_ASSERT(part_info->default_engine_type &&
+ part_info->default_engine_type != partition_hton);
+ if (check_native_partitioned(create_info, &is_native_partitioned,
+ part_info, thd))
+ {
+ DBUG_RETURN(TRUE);
+ }
+ if (!is_native_partitioned)
+ {
+ DBUG_ASSERT(create_info->db_type);
+ create_info->db_type= partition_hton;
+ }
+ }
+ }
+ DBUG_RETURN(FALSE);
+}
+
+
+/*
+ Change partitions, used to implement ALTER TABLE ADD/REORGANIZE/COALESCE
+ partitions. This method is used to implement both single-phase and multi-
+ phase implementations of ADD/REORGANIZE/COALESCE partitions.
+
+ SYNOPSIS
+ mysql_change_partitions()
+ lpt Struct containing parameters
+
+ RETURN VALUES
+ TRUE Failure
+ FALSE Success
+
+ DESCRIPTION
+ Request handler to add partitions as set in states of the partition
+
+ Elements of the lpt parameters used:
+ create_info Create information used to create partitions
+ db Database name
+ table_name Table name
+ copied Output parameter where number of copied
+ records are added
+ deleted Output parameter where number of deleted
+ records are added
+*/
+
+static bool mysql_change_partitions(ALTER_PARTITION_PARAM_TYPE *lpt)
+{
+ char path[FN_REFLEN+1];
+ int error;
+ handler *file= lpt->table->file;
+ DBUG_ENTER("mysql_change_partitions");
+
+ build_table_filename(path, sizeof(path), lpt->db, lpt->table_name, "", 0);
+ 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);
+ }
+ DBUG_RETURN(FALSE);
+}
+
+
+/*
+ Rename partitions in an ALTER TABLE of partitions
+
+ SYNOPSIS
+ mysql_rename_partitions()
+ lpt Struct containing parameters
+
+ RETURN VALUES
+ TRUE Failure
+ FALSE Success
+
+ DESCRIPTION
+ Request handler to rename partitions as set in states of the partition
+
+ Parameters used:
+ db Database name
+ table_name Table name
+*/
+
+static bool mysql_rename_partitions(ALTER_PARTITION_PARAM_TYPE *lpt)
+{
+ char path[FN_REFLEN+1];
+ int error;
+ DBUG_ENTER("mysql_rename_partitions");
+
+ build_table_filename(path, sizeof(path), lpt->db, lpt->table_name, "", 0);
+ if ((error= lpt->table->file->ha_rename_partitions(path)))
+ {
+ if (error != 1)
+ lpt->table->file->print_error(error, MYF(0));
+ DBUG_RETURN(TRUE);
+ }
+ DBUG_RETURN(FALSE);
+}
+
+
+/*
+ Drop partitions in an ALTER TABLE of partitions
+
+ SYNOPSIS
+ mysql_drop_partitions()
+ lpt Struct containing parameters
+
+ RETURN VALUES
+ TRUE Failure
+ FALSE Success
+ DESCRIPTION
+ Drop the partitions marked with PART_TO_BE_DROPPED state and remove
+ those partitions from the list.
+
+ Parameters used:
+ table Table object
+ db Database name
+ table_name Table name
+*/
+
+static bool mysql_drop_partitions(ALTER_PARTITION_PARAM_TYPE *lpt)
+{
+ char path[FN_REFLEN+1];
+ partition_info *part_info= lpt->table->part_info;
+ List_iterator<partition_element> part_it(part_info->partitions);
+ uint i= 0;
+ uint remove_count= 0;
+ int error;
+ DBUG_ENTER("mysql_drop_partitions");
+
+ build_table_filename(path, sizeof(path), lpt->db, lpt->table_name, "", 0);
+ if ((error= lpt->table->file->ha_drop_partitions(path)))
+ {
+ lpt->table->file->print_error(error, MYF(0));
+ DBUG_RETURN(TRUE);
+ }
+ do
+ {
+ partition_element *part_elem= part_it++;
+ if (part_elem->part_state == PART_IS_DROPPED)
+ {
+ part_it.remove();
+ remove_count++;
+ }
+ } while (++i < part_info->no_parts);
+ part_info->no_parts-= remove_count;
+ DBUG_RETURN(FALSE);
+}
+
+
+/*
+ Insert log entry into list
+ SYNOPSIS
+ insert_part_info_log_entry_list()
+ log_entry
+ RETURN VALUES
+ NONE
+*/
+
+static void insert_part_info_log_entry_list(partition_info *part_info,
+ DDL_LOG_MEMORY_ENTRY *log_entry)
+{
+ log_entry->next_active_log_entry= part_info->first_log_entry;
+ part_info->first_log_entry= log_entry;
+}
+
+
+/*
+ Release all log entries for this partition info struct
+ SYNOPSIS
+ release_part_info_log_entries()
+ first_log_entry First log entry in list to release
+ RETURN VALUES
+ NONE
+*/
+
+static void release_part_info_log_entries(DDL_LOG_MEMORY_ENTRY *log_entry)
+{
+ DBUG_ENTER("release_part_info_log_entries");
+
+ while (log_entry)
+ {
+ release_ddl_log_memory_entry(log_entry);
+ log_entry= log_entry->next_active_log_entry;
+ }
+ DBUG_VOID_RETURN;
+}
+
+
+/*
+ Log an delete/rename frm file
+ SYNOPSIS
+ write_log_replace_delete_frm()
+ lpt Struct for parameters
+ next_entry Next reference to use in log record
+ from_path Name to rename from
+ to_path Name to rename to
+ replace_flag TRUE if replace, else delete
+ RETURN VALUES
+ TRUE Error
+ FALSE Success
+ DESCRIPTION
+ Support routine that writes a replace or delete of an frm file into the
+ ddl log. It also inserts an entry that keeps track of used space into
+ the partition info object
+*/
+
+static bool write_log_replace_delete_frm(ALTER_PARTITION_PARAM_TYPE *lpt,
+ uint next_entry,
+ const char *from_path,
+ const char *to_path,
+ bool replace_flag)
+{
+ DDL_LOG_ENTRY ddl_log_entry;
+ DDL_LOG_MEMORY_ENTRY *log_entry;
+ DBUG_ENTER("write_log_replace_delete_frm");
+
+ if (replace_flag)
+ ddl_log_entry.action_type= DDL_LOG_REPLACE_ACTION;
+ else
+ ddl_log_entry.action_type= DDL_LOG_DELETE_ACTION;
+ ddl_log_entry.next_entry= next_entry;
+ ddl_log_entry.handler_name= reg_ext;
+ ddl_log_entry.name= to_path;
+ if (replace_flag)
+ ddl_log_entry.from_name= from_path;
+ if (write_ddl_log_entry(&ddl_log_entry, &log_entry))
+ {
+ DBUG_RETURN(TRUE);
+ }
+ insert_part_info_log_entry_list(lpt->part_info, log_entry);
+ DBUG_RETURN(FALSE);
+}
+
+
+/*
+ Log final partition changes in change partition
+ SYNOPSIS
+ write_log_changed_partitions()
+ lpt Struct containing parameters
+ RETURN VALUES
+ TRUE Error
+ FALSE Success
+ DESCRIPTION
+ This code is used to perform safe ADD PARTITION for HASH partitions
+ and COALESCE for HASH partitions and REORGANIZE for any type of
+ partitions.
+ We prepare entries for all partitions except the reorganised partitions
+ in REORGANIZE partition, those are handled by
+ write_log_dropped_partitions. For those partitions that are replaced
+ special care is needed to ensure that this is performed correctly and
+ this requires a two-phased approach with this log as a helper for this.
+
+ This code is closely intertwined with the code in rename_partitions in
+ the partition handler.
+*/
+
+static bool write_log_changed_partitions(ALTER_PARTITION_PARAM_TYPE *lpt,
+ uint *next_entry, const char *path)
+{
+ DDL_LOG_ENTRY ddl_log_entry;
+ partition_info *part_info= lpt->part_info;
+ DDL_LOG_MEMORY_ENTRY *log_entry;
+ char tmp_path[FN_REFLEN];
+ 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 i= 0;
+ DBUG_ENTER("write_log_changed_partitions");
+
+ do
+ {
+ partition_element *part_elem= part_it++;
+ if (part_elem->part_state == PART_IS_CHANGED ||
+ (part_elem->part_state == PART_IS_ADDED && temp_partitions))
+ {
+ if (part_info->is_sub_partitioned())
+ {
+ List_iterator<partition_element> sub_it(part_elem->subpartitions);
+ uint no_subparts= part_info->no_subparts;
+ uint j= 0;
+ do
+ {
+ partition_element *sub_elem= sub_it++;
+ ddl_log_entry.next_entry= *next_entry;
+ ddl_log_entry.handler_name=
+ ha_resolve_storage_engine_name(sub_elem->engine_type);
+ create_subpartition_name(tmp_path, path,
+ part_elem->partition_name,
+ sub_elem->partition_name,
+ TEMP_PART_NAME);
+ create_subpartition_name(normal_path, path,
+ part_elem->partition_name,
+ sub_elem->partition_name,
+ NORMAL_PART_NAME);
+ ddl_log_entry.name= normal_path;
+ ddl_log_entry.from_name= tmp_path;
+ if (part_elem->part_state == PART_IS_CHANGED)
+ ddl_log_entry.action_type= DDL_LOG_REPLACE_ACTION;
+ else
+ ddl_log_entry.action_type= DDL_LOG_RENAME_ACTION;
+ if (write_ddl_log_entry(&ddl_log_entry, &log_entry))
+ {
+ DBUG_RETURN(TRUE);
+ }
+ *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);
+ }
+ else
+ {
+ ddl_log_entry.next_entry= *next_entry;
+ ddl_log_entry.handler_name=
+ ha_resolve_storage_engine_name(part_elem->engine_type);
+ create_partition_name(tmp_path, path,
+ part_elem->partition_name,
+ TEMP_PART_NAME, TRUE);
+ create_partition_name(normal_path, path,
+ part_elem->partition_name,
+ NORMAL_PART_NAME, TRUE);
+ ddl_log_entry.name= normal_path;
+ ddl_log_entry.from_name= tmp_path;
+ if (part_elem->part_state == PART_IS_CHANGED)
+ ddl_log_entry.action_type= DDL_LOG_REPLACE_ACTION;
+ else
+ ddl_log_entry.action_type= DDL_LOG_RENAME_ACTION;
+ if (write_ddl_log_entry(&ddl_log_entry, &log_entry))
+ {
+ DBUG_RETURN(TRUE);
+ }
+ *next_entry= log_entry->entry_pos;
+ part_elem->log_entry= log_entry;
+ insert_part_info_log_entry_list(part_info, log_entry);
+ }
+ }
+ } while (++i < no_elements);
+ DBUG_RETURN(FALSE);
+}
+
+
+/*
+ Log dropped partitions
+ SYNOPSIS
+ write_log_dropped_partitions()
+ lpt Struct containing parameters
+ RETURN VALUES
+ TRUE Error
+ FALSE Success
+*/
+
+static bool write_log_dropped_partitions(ALTER_PARTITION_PARAM_TYPE *lpt,
+ uint *next_entry,
+ const char *path,
+ bool temp_list)
+{
+ DDL_LOG_ENTRY ddl_log_entry;
+ partition_info *part_info= lpt->part_info;
+ DDL_LOG_MEMORY_ENTRY *log_entry;
+ 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;
+ 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--)
+ {
+ partition_element *part_elem;
+ if (temp_list)
+ part_elem= temp_it++;
+ else
+ part_elem= part_it++;
+ if (part_elem->part_state == PART_TO_BE_DROPPED ||
+ part_elem->part_state == PART_TO_BE_ADDED ||
+ part_elem->part_state == PART_CHANGED)
+ {
+ uint name_variant;
+ if (part_elem->part_state == PART_CHANGED ||
+ (part_elem->part_state == PART_TO_BE_ADDED &&
+ no_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 j= 0;
+ do
+ {
+ partition_element *sub_elem= sub_it++;
+ ddl_log_entry.next_entry= *next_entry;
+ ddl_log_entry.handler_name=
+ ha_resolve_storage_engine_name(sub_elem->engine_type);
+ create_subpartition_name(tmp_path, path,
+ part_elem->partition_name,
+ sub_elem->partition_name,
+ name_variant);
+ ddl_log_entry.name= tmp_path;
+ if (write_ddl_log_entry(&ddl_log_entry, &log_entry))
+ {
+ DBUG_RETURN(TRUE);
+ }
+ *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);
+ }
+ else
+ {
+ ddl_log_entry.next_entry= *next_entry;
+ ddl_log_entry.handler_name=
+ ha_resolve_storage_engine_name(part_elem->engine_type);
+ create_partition_name(tmp_path, path,
+ part_elem->partition_name,
+ name_variant, TRUE);
+ ddl_log_entry.name= tmp_path;
+ if (write_ddl_log_entry(&ddl_log_entry, &log_entry))
+ {
+ DBUG_RETURN(TRUE);
+ }
+ *next_entry= log_entry->entry_pos;
+ part_elem->log_entry= log_entry;
+ insert_part_info_log_entry_list(part_info, log_entry);
+ }
+ }
+ }
+ DBUG_RETURN(FALSE);
+}
+
+
+/*
+ Set execute log entry in ddl log for this partitioned table
+ SYNOPSIS
+ set_part_info_exec_log_entry()
+ part_info Partition info object
+ exec_log_entry Log entry
+ RETURN VALUES
+ NONE
+*/
+
+static void set_part_info_exec_log_entry(partition_info *part_info,
+ DDL_LOG_MEMORY_ENTRY *exec_log_entry)
+{
+ part_info->exec_log_entry= exec_log_entry;
+ exec_log_entry->next_active_log_entry= NULL;
+}
+
+
+/*
+ Write the log entry to ensure that the shadow frm file is removed at
+ crash.
+ SYNOPSIS
+ write_log_drop_shadow_frm()
+ lpt Struct containing parameters
+ install_frm Should we log action to install shadow frm or should
+ the action be to remove the shadow frm file.
+ RETURN VALUES
+ TRUE Error
+ FALSE Success
+ DESCRIPTION
+ Prepare an entry to the ddl log indicating a drop/install of the shadow frm
+ file and its corresponding handler file.
+*/
+
+static bool write_log_drop_shadow_frm(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;
+ char shadow_path[FN_REFLEN];
+ DBUG_ENTER("write_log_drop_shadow_frm");
+
+ build_table_shadow_filename(shadow_path, sizeof(shadow_path), lpt);
+ pthread_mutex_lock(&LOCK_gdl);
+ if (write_log_replace_delete_frm(lpt, 0UL, NULL,
+ (const char*)shadow_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))
+ goto error;
+ pthread_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;
+ my_error(ER_DDL_LOG_ERROR, MYF(0));
+ DBUG_RETURN(TRUE);
+}
+
+
+/*
+ Log renaming of shadow frm to real frm name and dropping of old frm
+ SYNOPSIS
+ write_log_rename_frm()
+ lpt Struct containing parameters
+ RETURN VALUES
+ TRUE Error
+ FALSE Success
+ DESCRIPTION
+ Prepare an entry to ensure that we complete the renaming of the frm
+ file if failure occurs in the middle of the rename process.
+*/
+
+static bool write_log_rename_frm(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= part_info->exec_log_entry;
+ char path[FN_REFLEN];
+ char shadow_path[FN_REFLEN];
+ DDL_LOG_MEMORY_ENTRY *old_first_log_entry= part_info->first_log_entry;
+ DBUG_ENTER("write_log_rename_frm");
+
+ part_info->first_log_entry= NULL;
+ build_table_filename(path, sizeof(path), lpt->db,
+ lpt->table_name, "", 0);
+ build_table_shadow_filename(shadow_path, sizeof(shadow_path), lpt);
+ pthread_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;
+ part_info->frm_log_entry= log_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);
+ DBUG_RETURN(FALSE);
+
+error:
+ release_part_info_log_entries(part_info->first_log_entry);
+ pthread_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));
+ DBUG_RETURN(TRUE);
+}
+
+
+/*
+ Write the log entries to ensure that the drop partition command is completed
+ even in the presence of a crash.
+
+ SYNOPSIS
+ write_log_drop_partition()
+ lpt Struct containing parameters
+ RETURN VALUES
+ TRUE Error
+ FALSE Success
+ DESCRIPTION
+ Prepare entries to the ddl log indicating all partitions to drop and to
+ install the shadow frm file and remove the old frm file.
+*/
+
+static bool write_log_drop_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= part_info->exec_log_entry;
+ char tmp_path[FN_REFLEN];
+ char path[FN_REFLEN];
+ uint next_entry= 0;
+ DDL_LOG_MEMORY_ENTRY *old_first_log_entry= part_info->first_log_entry;
+ DBUG_ENTER("write_log_drop_partition");
+
+ part_info->first_log_entry= NULL;
+ build_table_filename(path, sizeof(path), lpt->db,
+ lpt->table_name, "", 0);
+ build_table_filename(tmp_path, sizeof(tmp_path), lpt->db,
+ lpt->table_name, "#", 0);
+ pthread_mutex_lock(&LOCK_gdl);
+ if (write_log_dropped_partitions(lpt, &next_entry, (const char*)path,
+ FALSE))
+ goto error;
+ if (write_log_replace_delete_frm(lpt, next_entry, (const char*)tmp_path,
+ (const char*)path, TRUE))
+ goto error;
+ log_entry= part_info->first_log_entry;
+ part_info->frm_log_entry= log_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);
+ DBUG_RETURN(FALSE);
+
+error:
+ release_part_info_log_entries(part_info->first_log_entry);
+ pthread_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));
+ DBUG_RETURN(TRUE);
+}
+
+
+/*
+ Write the log entries to ensure that the add partition command is not
+ executed at all if a crash before it has completed
+
+ SYNOPSIS
+ write_log_add_change_partition()
+ lpt Struct containing parameters
+ RETURN VALUES
+ TRUE Error
+ FALSE Success
+ DESCRIPTION
+ Prepare entries to the ddl log indicating all partitions to drop and to
+ remove the shadow frm file.
+ We always inject entries backwards in the list in the ddl log since we
+ don't know the entry position until we have written it.
+*/
+
+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;
+ char tmp_path[FN_REFLEN];
+ char path[FN_REFLEN];
+ uint next_entry= 0;
+ DBUG_ENTER("write_log_add_change_partition");
+
+ build_table_filename(path, sizeof(path), lpt->db,
+ lpt->table_name, "", 0);
+ build_table_filename(tmp_path, sizeof(tmp_path), lpt->db,
+ lpt->table_name, "#", 0);
+ pthread_mutex_lock(&LOCK_gdl);
+ 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))
+ goto error;
+ pthread_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;
+ my_error(ER_DDL_LOG_ERROR, MYF(0));
+ DBUG_RETURN(TRUE);
+}
+
+
+/*
+ Write description of how to complete the operation after first phase of
+ change partitions.
+
+ SYNOPSIS
+ write_log_final_change_partition()
+ lpt Struct containing parameters
+ RETURN VALUES
+ 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.
+*/
+
+static bool write_log_final_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= part_info->exec_log_entry;
+ char path[FN_REFLEN];
+ char shadow_path[FN_REFLEN];
+ DDL_LOG_MEMORY_ENTRY *old_first_log_entry= part_info->first_log_entry;
+ uint next_entry= 0;
+ DBUG_ENTER("write_log_final_change_partition");
+
+ part_info->first_log_entry= NULL;
+ build_table_filename(path, sizeof(path), lpt->db,
+ lpt->table_name, "", 0);
+ build_table_shadow_filename(shadow_path, sizeof(shadow_path), lpt);
+ pthread_mutex_lock(&LOCK_gdl);
+ 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))
+ goto error;
+ log_entry= part_info->first_log_entry;
+ part_info->frm_log_entry= log_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);
+ DBUG_RETURN(FALSE);
+
+error:
+ release_part_info_log_entries(part_info->first_log_entry);
+ pthread_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));
+ DBUG_RETURN(TRUE);
+}
+
+
+/*
+ Remove entry from ddl log and release resources for others to use
+
+ SYNOPSIS
+ write_log_completed()
+ lpt Struct containing parameters
+ RETURN VALUES
+ TRUE Error
+ FALSE Success
+*/
+
+static void write_log_completed(ALTER_PARTITION_PARAM_TYPE *lpt,
+ bool dont_crash)
+{
+ partition_info *part_info= lpt->part_info;
+ DDL_LOG_MEMORY_ENTRY *log_entry= part_info->exec_log_entry;
+ DBUG_ENTER("write_log_completed");
+
+ DBUG_ASSERT(log_entry);
+ pthread_mutex_lock(&LOCK_gdl);
+ if (write_execute_ddl_log_entry(0UL, TRUE, &log_entry))
+ {
+ /*
+ Failed to write, Bad...
+ We have completed the operation but have log records to REMOVE
+ stuff that shouldn't be removed. What clever things could one do
+ here? An error output was written to the error output by the
+ above method so we don't do anything here.
+ */
+ ;
+ }
+ 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);
+ part_info->exec_log_entry= NULL;
+ part_info->first_log_entry= NULL;
+ DBUG_VOID_RETURN;
+}
+
+
+/*
+ Release all log entries
+ SYNOPSIS
+ release_log_entries()
+ part_info Partition info struct
+ RETURN VALUES
+ NONE
+*/
+
+static void release_log_entries(partition_info *part_info)
+{
+ pthread_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);
+ part_info->first_log_entry= NULL;
+ part_info->exec_log_entry= NULL;
+}
+
+
+/*
+ Final part of partition changes to handle things when under
+ LOCK TABLES.
+ SYNPOSIS
+ alter_partition_lock_handling()
+ lpt Struct carrying parameters
+ RETURN VALUES
+ NONE
+*/
+static void alter_partition_lock_handling(ALTER_PARTITION_PARAM_TYPE *lpt)
+{
+ int err;
+ if (lpt->thd->locked_tables)
+ {
+ /*
+ When we have the table locked, it is necessary to reopen the table
+ since all table objects were closed and removed as part of the
+ ALTER TABLE of partitioning structure.
+ */
+ pthread_mutex_lock(&LOCK_open);
+ lpt->thd->in_lock_tables= 1;
+ err= reopen_tables(lpt->thd, 1, 1);
+ lpt->thd->in_lock_tables= 0;
+ if (err)
+ {
+ /*
+ Issue a warning since we weren't able to regain the lock again.
+ We also need to unlink table from thread's open list and from
+ table_cache
+ */
+ unlink_open_table(lpt->thd, lpt->table, FALSE);
+ sql_print_warning("We failed to reacquire LOCKs in ALTER TABLE");
+ }
+ pthread_mutex_unlock(&LOCK_open);
+ }
+}
+
+/*
+ Unlock and close table before renaming and dropping partitions
+ SYNOPSIS
+ alter_close_tables()
+ lpt Struct carrying parameters
+ RETURN VALUES
+ 0
+*/
+
+static int alter_close_tables(ALTER_PARTITION_PARAM_TYPE *lpt)
+{
+ THD *thd= lpt->thd;
+ const char *db= lpt->db;
+ const char *table_name= lpt->table_name;
+ DBUG_ENTER("alter_close_tables");
+ /*
+ We need to also unlock tables and close all handlers.
+ We set lock to zero to ensure we don't do this twice
+ and we set db_stat to zero to ensure we don't close twice.
+ */
+ pthread_mutex_lock(&LOCK_open);
+ close_data_files_and_morph_locks(thd, db, table_name);
+ pthread_mutex_unlock(&LOCK_open);
+ DBUG_RETURN(0);
+}
+
+
+/*
+ Handle errors for ALTER TABLE for partitioning
+ SYNOPSIS
+ handle_alter_part_error()
+ lpt Struct carrying parameters
+ not_completed Was request in complete phase when error occurred
+ RETURN VALUES
+ NONE
+*/
+
+void handle_alter_part_error(ALTER_PARTITION_PARAM_TYPE *lpt,
+ bool not_completed,
+ bool drop_partition,
+ bool frm_install)
+{
+ partition_info *part_info= lpt->part_info;
+ DBUG_ENTER("handle_alter_part_error");
+
+ if (!part_info->first_log_entry &&
+ execute_ddl_log_entry(current_thd,
+ part_info->first_log_entry->entry_pos))
+ {
+ /*
+ We couldn't recover from error, most likely manual interaction
+ is required.
+ */
+ write_log_completed(lpt, FALSE);
+ release_log_entries(part_info);
+ if (not_completed)
+ {
+ if (drop_partition)
+ {
+ /* Table is still ok, but we left a shadow frm file behind. */
+ push_warning_printf(lpt->thd, MYSQL_ERROR::WARN_LEVEL_WARN, 1,
+ "%s %s",
+ "Operation was unsuccessful, table is still intact,",
+ "but it is possible that a shadow frm file was left behind");
+ }
+ else
+ {
+ push_warning_printf(lpt->thd, MYSQL_ERROR::WARN_LEVEL_WARN, 1,
+ "%s %s %s %s",
+ "Operation was unsuccessful, table is still intact,",
+ "but it is possible that a shadow frm file was left behind.",
+ "It is also possible that temporary partitions are left behind,",
+ "these could be empty or more or less filled with records");
+ }
+ }
+ else
+ {
+ if (frm_install)
+ {
+ /*
+ Failed during install of shadow frm file, table isn't intact
+ and dropped partitions are still there
+ */
+ push_warning_printf(lpt->thd, MYSQL_ERROR::WARN_LEVEL_WARN, 1,
+ "%s %s %s",
+ "Failed during alter of partitions, table is no longer intact.",
+ "The frm file is in an unknown state, and a backup",
+ "is required.");
+ }
+ else if (drop_partition)
+ {
+ /*
+ Table is ok, we have switched to new table but left dropped
+ partitions still in their places. We remove the log records and
+ ask the user to perform the action manually. We remove the log
+ records and ask the user to perform the action manually.
+ */
+ push_warning_printf(lpt->thd, MYSQL_ERROR::WARN_LEVEL_WARN, 1,
+ "%s %s",
+ "Failed during drop of partitions, table is intact.",
+ "Manual drop of remaining partitions is required");
+ }
+ else
+ {
+ /*
+ We failed during renaming of partitions. The table is most
+ certainly in a very bad state so we give user warning and disable
+ the table by writing an ancient frm version into it.
+ */
+ push_warning_printf(lpt->thd, MYSQL_ERROR::WARN_LEVEL_WARN, 1,
+ "%s %s %s",
+ "Failed during renaming of partitions. We are now in a position",
+ "where table is not reusable",
+ "Table is disabled by writing ancient frm file version into it");
+ }
+ }
+ }
+ else
+ {
+ release_log_entries(part_info);
+ if (not_completed)
+ {
+ /*
+ We hit an error before things were completed but managed
+ to recover from the error. An error occurred and we have
+ restored things to original so no need for further action.
+ */
+ ;
+ }
+ else
+ {
+ /*
+ We hit an error after we had completed most of the operation
+ and were successful in a second attempt so the operation
+ actually is successful now. We need to issue a warning that
+ even though we reported an error the operation was successfully
+ completed.
+ */
+ push_warning_printf(lpt->thd, MYSQL_ERROR::WARN_LEVEL_WARN, 1,"%s %s",
+ "Operation was successfully completed by failure handling,",
+ "after failure of normal operation");
+ }
+ }
+ DBUG_VOID_RETURN;
+}
+
+
+/*
+ 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
+
+ RETURN VALUES
+ TRUE Error
+ FALSE Success
+
+ DESCRIPTION
+ Perform all ALTER TABLE operations for partitioned tables that can be
+ performed fast without a full copy of the original table.
+*/
+
+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)
+{
+ /* Set-up struct used to write frm files */
+ partition_info *part_info= table->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 frm_install= FALSE;
+ DBUG_ENTER("fast_alter_partition_table");
+
+ lpt->thd= thd;
+ 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->key_info_buffer= 0;
+ lpt->key_count= 0;
+ lpt->db= db;
+ lpt->table_name= table_name;
+ lpt->copied= 0;
+ lpt->deleted= 0;
+ lpt->pack_frm_data= NULL;
+ lpt->pack_frm_len= 0;
+ thd->work_part_info= part_info;
+
+ if (fast_alter_partition & HA_PARTITION_ONE_PHASE)
+ {
+ /*
+ In the case where the engine supports one phase online partition
+ changes it is not necessary to have any exclusive locks. The
+ correctness is upheld instead by transactions being aborted if they
+ access the table after its partition definition has changed (if they
+ are still using the old partition definition).
+
+ The handler is in this case responsible to ensure that all users
+ start using the new frm file after it has changed. To implement
+ one phase it is necessary for the handler to have the master copy
+ of the frm file and use discovery mechanisms to renew it. Thus
+ write frm will write the frm, pack the new frm and finally
+ the frm is deleted and the discovery mechanisms will either restore
+ back to the old or installing the new after the change is activated.
+
+ Thus all open tables will be discovered that they are old, if not
+ earlier as soon as they try an operation using the old table. One
+ should ensure that this is checked already when opening a table,
+ even if it is found in the cache of open tables.
+
+ change_partitions will perform all operations and it is the duty of
+ the handler to ensure that the frm files in the system gets updated
+ in synch with the changes made and if an error occurs that a proper
+ error handling is done.
+
+ If the MySQL Server crashes at this moment but the handler succeeds
+ in performing the change then the binlog is not written for the
+ change. There is no way to solve this as long as the binlog is not
+ transactional and even then it is hard to solve it completely.
+
+ The first approach here was to downgrade locks. Now a different approach
+ is decided upon. The idea is that the handler will have access to the
+ Alter_info when store_lock arrives with TL_WRITE_ALLOW_READ. So if the
+ handler knows that this functionality can be handled with a lower lock
+ level it will set the lock level to TL_WRITE_ALLOW_WRITE immediately.
+ Thus the need to downgrade the lock disappears.
+ 1) Write the new frm, pack it and then delete it
+ 2) Perform the change within the handler
+ */
+ if (mysql_write_frm(lpt, WFRM_WRITE_SHADOW | WFRM_PACK_FRM) ||
+ mysql_change_partitions(lpt))
+ {
+ goto err;
+ }
+ }
+ else if (alter_info->flags & ALTER_DROP_PARTITION)
+ {
+ /*
+ Now after all checks and setting state on dropped partitions we can
+ start the actual dropping of the partitions.
+
+ Drop partition is actually two things happening. The first is that
+ a lot of records are deleted. The second is that the behaviour of
+ subsequent updates and writes and deletes will change. The delete
+ part can be handled without any particular high lock level by
+ transactional engines whereas non-transactional engines need to
+ ensure that this change is done with an exclusive lock on the table.
+ The second part, the change of partitioning does however require
+ an exclusive lock to install the new partitioning as one atomic
+ operation. If this is not the case, it is possible for two
+ transactions to see the change in a different order than their
+ serialisation order. Thus we need an exclusive lock for both
+ transactional and non-transactional engines.
+
+ For LIST partitions it could be possible to avoid the exclusive lock
+ (and for RANGE partitions if they didn't rearrange range definitions
+ after a DROP PARTITION) if one ensured that failed accesses to the
+ dropped partitions was aborted for sure (thus only possible for
+ transactional engines).
+
+ 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
+ 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
+ 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
+ where crashes make strange things occur. In this placement it can
+ happen that the ALTER TABLE DROP PARTITION gets performed in the
+ master but not in the slaves if we have a crash, after writing the
+ ddl log but before writing the binlog. A solution to this would
+ 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
+
+ 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") ||
+ mysql_write_frm(lpt, WFRM_WRITE_SHADOW) ||
+ ERROR_INJECT_CRASH("crash_drop_partition_2") ||
+ write_log_drop_partition(lpt) ||
+ ERROR_INJECT_CRASH("crash_drop_partition_3") ||
+ (not_completed= FALSE) ||
+ abort_and_upgrade_lock(lpt) || /* Always returns 0 */
+ ERROR_INJECT_CRASH("crash_drop_partition_4") ||
+ alter_close_tables(lpt) ||
+ ERROR_INJECT_CRASH("crash_drop_partition_5") ||
+ ((!thd->lex->no_write_to_binlog) &&
+ (write_bin_log(thd, FALSE,
+ thd->query, thd->query_length), FALSE)) ||
+ ERROR_INJECT_CRASH("crash_drop_partition_6") ||
+ ((frm_install= TRUE), FALSE) ||
+ mysql_write_frm(lpt, WFRM_INSTALL_SHADOW) ||
+ ((frm_install= FALSE), FALSE) ||
+ ERROR_INJECT_CRASH("crash_drop_partition_7") ||
+ mysql_drop_partitions(lpt) ||
+ ERROR_INJECT_CRASH("crash_drop_partition_8") ||
+ (write_log_completed(lpt, FALSE), FALSE) ||
+ ERROR_INJECT_CRASH("crash_drop_partition_9") ||
+ (alter_partition_lock_handling(lpt), FALSE))
+ {
+ handle_alter_part_error(lpt, not_completed, TRUE, frm_install);
+ goto err;
+ }
+ }
+ else if ((alter_info->flags & ALTER_ADD_PARTITION) &&
+ (part_info->part_type == RANGE_PARTITION ||
+ part_info->part_type == LIST_PARTITION))
+ {
+ /*
+ ADD RANGE/LIST PARTITIONS
+ In this case there are no tuples removed and no tuples are added.
+ Thus the operation is merely adding a new partition. Thus it is
+ necessary to perform the change as an atomic operation. Otherwise
+ someone reading without seeing the new partition could potentially
+ miss updates made by a transaction serialised before it that are
+ inserted into the new partition.
+
+ 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
+ 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
+ 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
+ */
+ if (write_log_add_change_partition(lpt) ||
+ ERROR_INJECT_CRASH("crash_add_partition_1") ||
+ mysql_write_frm(lpt, WFRM_WRITE_SHADOW) ||
+ ERROR_INJECT_CRASH("crash_add_partition_2") ||
+ mysql_change_partitions(lpt) ||
+ ERROR_INJECT_CRASH("crash_add_partition_3") ||
+ abort_and_upgrade_lock(lpt) || /* Always returns 0 */
+ ERROR_INJECT_CRASH("crash_add_partition_4") ||
+ alter_close_tables(lpt) ||
+ ERROR_INJECT_CRASH("crash_add_partition_5") ||
+ ((!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_CRASH("crash_add_partition_8") ||
+ (write_log_completed(lpt, FALSE), FALSE) ||
+ ERROR_INJECT_CRASH("crash_add_partition_9") ||
+ (alter_partition_lock_handling(lpt), FALSE))
+ {
+ handle_alter_part_error(lpt, not_completed, FALSE, frm_install);
+ goto err;
+ }
+ }
+ else
+ {
+ /*
+ ADD HASH PARTITION/
+ COALESCE PARTITION/
+ REBUILD PARTITION/
+ REORGANIZE PARTITION
+
+ In this case all records are still around after the change although
+ possibly organised into new partitions, thus by ensuring that all
+ updates go to both the old and the new partitioning scheme we can
+ actually perform this operation lock-free. The only exception to
+ this is when REORGANIZE PARTITION adds/drops ranges. In this case
+ there needs to be an exclusive lock during the time when the range
+ changes occur.
+ This is only possible if the handler can ensure double-write for a
+ period. The double write will ensure that it doesn't matter where the
+ data is read from since both places are updated for writes. If such
+ double writing is not performed then it is necessary to perform the
+ change with the usual exclusive lock. With double writes it is even
+ possible to perform writes in parallel with the reorganisation of
+ partitions.
+
+ Without double write procedure we get the following procedure.
+ The only difference with using double write is that we can downgrade
+ the lock to TL_WRITE_ALLOW_WRITE. Double write in this case only
+ double writes from old to new. If we had double writing in both
+ directions we could perform the change completely without exclusive
+ lock for HASH partitions.
+ Handlers that perform double writing during the copy phase can actually
+ use a lower lock level. This can be handled inside store_lock in the
+ respective handler.
+
+ 0) Write an entry that removes the shadow frm file if crash occurs
+ 1) Write the shadow frm file of new partitioning
+ 2) Log such that temporary partitions added in change phase are
+ removed in a crash situation
+ 3) Add the new partitions
+ Copy from the reorganised partitions to the new partitions
+ 4) 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
+ */
+ if (write_log_add_change_partition(lpt) ||
+ ERROR_INJECT_CRASH("crash_change_partition_1") ||
+ mysql_write_frm(lpt, WFRM_WRITE_SHADOW) ||
+ ERROR_INJECT_CRASH("crash_change_partition_2") ||
+ mysql_change_partitions(lpt) ||
+ ERROR_INJECT_CRASH("crash_change_partition_3") ||
+ write_log_final_change_partition(lpt) ||
+ ERROR_INJECT_CRASH("crash_change_partition_4") ||
+ (not_completed= FALSE) ||
+ abort_and_upgrade_lock(lpt) || /* Always returns 0 */
+ ERROR_INJECT_CRASH("crash_change_partition_5") ||
+ alter_close_tables(lpt) ||
+ ERROR_INJECT_CRASH("crash_change_partition_6") ||
+ ((!thd->lex->no_write_to_binlog) &&
+ (write_bin_log(thd, FALSE,
+ thd->query, thd->query_length), FALSE)) ||
+ ERROR_INJECT_CRASH("crash_change_partition_7") ||
+ mysql_write_frm(lpt, WFRM_INSTALL_SHADOW) ||
+ ERROR_INJECT_CRASH("crash_change_partition_8") ||
+ mysql_drop_partitions(lpt) ||
+ ERROR_INJECT_CRASH("crash_change_partition_9") ||
+ mysql_rename_partitions(lpt) ||
+ ((frm_install= TRUE), FALSE) ||
+ ERROR_INJECT_CRASH("crash_change_partition_10") ||
+ (write_log_completed(lpt, FALSE), FALSE) ||
+ ERROR_INJECT_CRASH("crash_change_partition_11") ||
+ (alter_partition_lock_handling(lpt), FALSE))
+ {
+ handle_alter_part_error(lpt, not_completed, FALSE, frm_install);
+ goto err;
+ }
+ }
+ /*
+ 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));
+err:
+ close_thread_tables(thd);
+ DBUG_RETURN(TRUE);
+}
+#endif
+
+
+/*
+ Prepare for calling val_int on partition function by setting fields to
+ point to the record where the values of the PF-fields are stored.
+
+ SYNOPSIS
+ set_field_ptr()
+ ptr Array of fields to change ptr
+ new_buf New record pointer
+ old_buf Old record pointer
+
+ DESCRIPTION
+ Set ptr in field objects of field array to refer to new_buf record
+ instead of previously old_buf. Used before calling val_int and after
+ it is used to restore pointers to table->record[0].
+ This routine is placed outside of partition code since it can be useful
+ also for other programs.
+*/
+
+void set_field_ptr(Field **ptr, const uchar *new_buf,
+ const uchar *old_buf)
+{
+ my_ptrdiff_t diff= (new_buf - old_buf);
+ DBUG_ENTER("set_field_ptr");
+
+ do
+ {
+ (*ptr)->move_field_offset(diff);
+ } while (*(++ptr));
+ DBUG_VOID_RETURN;
+}
+
+
+/*
+ Prepare for calling val_int on partition function by setting fields to
+ point to the record where the values of the PF-fields are stored.
+ This variant works on a key_part reference.
+ It is not required that all fields are NOT NULL fields.
+
+ SYNOPSIS
+ set_key_field_ptr()
+ key_info key info with a set of fields to change ptr
+ new_buf New record pointer
+ old_buf Old record pointer
+
+ DESCRIPTION
+ Set ptr in field objects of field array to refer to new_buf record
+ instead of previously old_buf. Used before calling val_int and after
+ it is used to restore pointers to table->record[0].
+ This routine is placed outside of partition code since it can be useful
+ also for other programs.
+*/
+
+void set_key_field_ptr(KEY *key_info, const uchar *new_buf,
+ const uchar *old_buf)
+{
+ KEY_PART_INFO *key_part= key_info->key_part;
+ uint key_parts= key_info->key_parts;
+ uint i= 0;
+ my_ptrdiff_t diff= (new_buf - old_buf);
+ DBUG_ENTER("set_key_field_ptr");
+
+ do
+ {
+ key_part->field->move_field_offset(diff);
+ key_part++;
+ } while (++i < key_parts);
+ DBUG_VOID_RETURN;
+}
+
+
+/*
+ SYNOPSIS
+ mem_alloc_error()
+ size Size of memory attempted to allocate
+ None
+
+ RETURN VALUES
+ None
+
+ DESCRIPTION
+ A routine to use for all the many places in the code where memory
+ allocation error can happen, a tremendous amount of them, needs
+ simple routine that signals this error.
+*/
+
+void mem_alloc_error(size_t size)
+{
+ my_error(ER_OUTOFMEMORY, MYF(0), size);
+}
+
+#ifdef WITH_PARTITION_STORAGE_ENGINE
+/*
+ Return comma-separated list of used partitions in the provided given string
+
+ SYNOPSIS
+ make_used_partitions_str()
+ part_info IN Partitioning info
+ parts_str OUT The string to fill
+
+ DESCRIPTION
+ Generate a list of used partitions (from bits in part_info->used_partitions
+ bitmap), asd store it into the provided String object.
+
+ NOTE
+ The produced string must not be longer then MAX_PARTITIONS * (1 + FN_LEN).
+*/
+
+void make_used_partitions_str(partition_info *part_info, String *parts_str)
+{
+ parts_str->length(0);
+ partition_element *pe;
+ uint partition_id= 0;
+ List_iterator<partition_element> it(part_info->partitions);
+
+ if (part_info->is_sub_partitioned())
+ {
+ partition_element *head_pe;
+ while ((head_pe= it++))
+ {
+ List_iterator<partition_element> it2(head_pe->subpartitions);
+ while ((pe= it2++))
+ {
+ if (bitmap_is_set(&part_info->used_partitions, partition_id))
+ {
+ if (parts_str->length())
+ parts_str->append(',');
+ parts_str->append(head_pe->partition_name,
+ strlen(head_pe->partition_name),
+ system_charset_info);
+ parts_str->append('_');
+ parts_str->append(pe->partition_name,
+ strlen(pe->partition_name),
+ system_charset_info);
+ }
+ partition_id++;
+ }
+ }
+ }
+ else
+ {
+ while ((pe= it++))
+ {
+ if (bitmap_is_set(&part_info->used_partitions, partition_id))
+ {
+ if (parts_str->length())
+ parts_str->append(',');
+ parts_str->append(pe->partition_name, strlen(pe->partition_name),
+ system_charset_info);
+ }
+ partition_id++;
+ }
+ }
+}
+#endif
+
+/****************************************************************************
+ * Partition interval analysis support
+ ***************************************************************************/
+
+/*
+ Setup partition_info::* members related to partitioning range analysis
+
+ SYNOPSIS
+ set_up_partition_func_pointers()
+ part_info Partitioning info structure
+
+ DESCRIPTION
+ Assuming that passed partition_info structure already has correct values
+ for members that specify [sub]partitioning type, table fields, and
+ functions, set up partition_info::* members that are related to
+ Partitioning Interval Analysis (see get_partitions_in_range_iter for its
+ 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
+
+ They both have limited applicability:
+ (1) is applicable for "PARTITION BY <RANGE|LIST>(func(t.field))", where
+ func is a monotonic function.
+
+ (2) is applicable for
+ "[SUB]PARTITION BY <any-partitioning-type>(any_func(t.integer_field))"
+
+ If both are applicable, (1) is preferred over (2).
+
+ This function sets part_info::get_part_iter_for_interval according to
+ this criteria, and also sets some auxilary fields that the function
+ uses.
+*/
+#ifdef WITH_PARTITION_STORAGE_ENGINE
+static void set_up_range_analysis_info(partition_info *part_info)
+{
+ /* Set the catch-all default */
+ part_info->get_part_iter_for_interval= NULL;
+ part_info->get_subpart_iter_for_interval= NULL;
+
+ /*
+ Check if get_part_iter_for_interval_via_mapping() can be used for
+ partitioning
+ */
+ switch (part_info->part_type) {
+ case RANGE_PARTITION:
+ case LIST_PARTITION:
+ 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;
+ }
+ default:
+ ;
+ }
+
+ /*
+ Check if get_part_iter_for_interval_via_walking() can be used for
+ partitioning
+ */
+ if (part_info->no_part_fields == 1)
+ {
+ Field *field= part_info->part_field_array[0];
+ switch (field->type()) {
+ case MYSQL_TYPE_TINY:
+ case MYSQL_TYPE_SHORT:
+ case MYSQL_TYPE_INT24:
+ case MYSQL_TYPE_LONG:
+ case MYSQL_TYPE_LONGLONG:
+ part_info->get_part_iter_for_interval=
+ get_part_iter_for_interval_via_walking;
+ break;
+ default:
+ ;
+ }
+ }
+
+setup_subparts:
+ /*
+ Check if get_part_iter_for_interval_via_walking() can be used for
+ subpartitioning
+ */
+ if (part_info->no_subpart_fields == 1)
+ {
+ Field *field= part_info->subpart_field_array[0];
+ switch (field->type()) {
+ case MYSQL_TYPE_TINY:
+ case MYSQL_TYPE_SHORT:
+ case MYSQL_TYPE_LONG:
+ case MYSQL_TYPE_LONGLONG:
+ part_info->get_subpart_iter_for_interval=
+ get_part_iter_for_interval_via_walking;
+ break;
+ default:
+ ;
+ }
+ }
+}
+
+
+typedef uint32 (*get_endpoint_func)(partition_info*, bool left_endpoint,
+ bool include_endpoint);
+
+/*
+ Partitioning Interval Analysis: Initialize the iterator for "mapping" case
+
+ 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
+
+ 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.
+
+ 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)
+*/
+
+int get_part_iter_for_interval_via_mapping(partition_info *part_info,
+ bool is_subpart,
+ uchar *min_value, uchar *max_value,
+ uint flags,
+ PARTITION_ITERATOR *part_iter)
+{
+ DBUG_ASSERT(!is_subpart);
+ Field *field= part_info->part_field_array[0];
+ uint32 max_endpoint_val;
+ get_endpoint_func get_endpoint;
+ uint field_len= field->pack_length_in_rec();
+ part_iter->ret_null_part= part_iter->ret_null_part_orig= FALSE;
+
+ if (part_info->part_type == RANGE_PARTITION)
+ {
+ if (part_info->part_charset_field_array)
+ 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;
+ part_iter->get_next= get_next_partition_id_range;
+ }
+ else if (part_info->part_type == LIST_PARTITION)
+ {
+
+ if (part_info->part_charset_field_array)
+ 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;
+ part_iter->get_next= get_next_partition_id_list;
+ part_iter->part_info= part_info;
+ if (max_endpoint_val == 0)
+ {
+ /*
+ We handle this special case without optimisations since it is
+ of little practical value but causes a great number of complex
+ checks later in the code.
+ */
+ 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;
+ }
+ }
+ else
+ assert(0);
+
+ /*
+ Find minimum: Do special handling if the interval has left bound in form
+ " NULL <= X ":
+ */
+ if (field->real_maybe_null() && part_info->has_null_value &&
+ !(flags & (NO_MIN_RANGE | NEAR_MIN)) && *min_value)
+ {
+ 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))
+ {
+ /* The right bound is X <= NULL, i.e. it is a "X IS NULL" interval */
+ part_iter->part_nums.end= 0;
+ return 1;
+ }
+ }
+ else
+ {
+ if (flags & NO_MIN_RANGE)
+ part_iter->part_nums.start= part_iter->part_nums.cur= 0;
+ else
+ {
+ /*
+ Store the interval edge in the record buffer, and call the
+ function that maps the edge in table-field space to an edge
+ in ordered-set-of-partitions (for RANGE partitioning) or
+ index-in-ordered-array-of-list-constants (for LIST) space.
+ */
+ store_key_image_to_rec(field, min_value, field_len);
+ bool include_endp= !test(flags & NEAR_MIN);
+ part_iter->part_nums.start= get_endpoint(part_info, 1, include_endp);
+ part_iter->part_nums.cur= part_iter->part_nums.start;
+ if (part_iter->part_nums.start == max_endpoint_val)
+ return 0; /* No partitions */
+ }
+ }
+
+ /* Find maximum, do the same as above but for right interval bound */
+ if (flags & NO_MAX_RANGE)
+ part_iter->part_nums.end= max_endpoint_val;
+ else
+ {
+ store_key_image_to_rec(field, max_value, field_len);
+ bool include_endp= !test(flags & NEAR_MAX);
+ part_iter->part_nums.end= get_endpoint(part_info, 0, include_endp);
+ if (part_iter->part_nums.start >= part_iter->part_nums.end &&
+ !part_iter->ret_null_part)
+ return 0; /* No partitions */
+ }
+ 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
+
+
+/*
+ Partitioning Interval Analysis: Initialize iterator to walk field interval
+
+ SYNOPSIS
+ get_part_iter_for_interval_via_walking()
+ 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
+
+ DESCRIPTION
+ Initialize partition set iterator to walk over interval in integer field
+ space. That is, for "const1 <=? t.field <=? const2" interval, initialize
+ the iterator to return a set of [sub]partitions obtained with the
+ following procedure:
+ get partition id for t.field = const1, return it
+ get partition id for t.field = const1+1, return it
+ ... t.field = const1+2, ...
+ ... ... ...
+ ... t.field = const2 ...
+
+ IMPLEMENTATION
+ See get_partitions_in_range_iter for general description of interval
+ analysis. We support walking over the following intervals:
+ "t.field IS NULL"
+ "c1 <=? t.field <=? c2", where c1 and c2 are finite.
+ 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
+ -1 - All partitions would match, iterator not initialized
+*/
+
+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)
+{
+ Field *field;
+ uint total_parts;
+ partition_iter_func get_next_func;
+ if (is_subpart)
+ {
+ field= part_info->subpart_field_array[0];
+ total_parts= part_info->no_subparts;
+ get_next_func= get_next_subpartition_via_walking;
+ }
+ else
+ {
+ field= part_info->part_field_array[0];
+ total_parts= part_info->no_parts;
+ get_next_func= get_next_partition_via_walking;
+ }
+
+ /* Handle the "t.field IS NULL" interval, it is a special case */
+ if (field->real_maybe_null() && !(flags & (NO_MIN_RANGE | NO_MAX_RANGE)) &&
+ *min_value && *max_value)
+ {
+ /*
+ We don't have a part_iter->get_next() function that would find which
+ partition "t.field IS NULL" belongs to, so find partition that contains
+ NULL right here, and return an iterator over singleton set.
+ */
+ uint32 part_id;
+ field->set_null();
+ if (is_subpart)
+ {
+ if (!part_info->get_subpartition_id(part_info, &part_id))
+ {
+ init_single_partition_iterator(part_id, part_iter);
+ return 1; /* Ok, iterator initialized */
+ }
+ }
+ else
+ {
+ longlong dummy;
+ int res= part_info->is_sub_partitioned() ?
+ part_info->get_part_partition_id(part_info, &part_id,
+ &dummy):
+ part_info->get_partition_id(part_info, &part_id, &dummy);
+ if (!res)
+ {
+ init_single_partition_iterator(part_id, part_iter);
+ return 1; /* Ok, iterator initialized */
+ }
+ }
+ return 0; /* No partitions match */
+ }
+
+ if ((field->real_maybe_null() &&
+ ((!(flags & NO_MIN_RANGE) && *min_value) || // NULL <? X
+ (!(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 */
+ }
+
+ /* Get integers for left and right interval bound */
+ longlong a, b;
+ uint len= field->pack_length_in_rec();
+ store_key_image_to_rec(field, min_value, len);
+ a= field->val_int();
+
+ store_key_image_to_rec(field, max_value, len);
+ b= field->val_int();
+
+ /*
+ Handle a special case where the distance between interval bounds is
+ exactly 4G-1. This interval is too big for range walking, and if it is an
+ (x,y]-type interval then the following "b +=..." code will convert it to
+ an empty interval by "wrapping around" a + 4G-1 + 1 = a.
+ */
+ if ((ulonglong)b - (ulonglong)a == ~0ULL)
+ 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;
+
+ 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;
+}
+
+
+/*
+ PARTITION_ITERATOR::get_next implementation: enumerate partitions in range
+
+ SYNOPSIS
+ get_next_partition_id_range()
+ part_iter Partition set iterator structure
+
+ DESCRIPTION
+ This is implementation of PARTITION_ITERATOR::get_next() that returns
+ [sub]partition ids in [min_partition_id, max_partition_id] range.
+ The function conforms to partition_iter_func type.
+
+ RETURN
+ partition id
+ NOT_A_PARTITION_ID if there are no more partitions
+*/
+
+uint32 get_next_partition_id_range(PARTITION_ITERATOR* part_iter)
+{
+ if (part_iter->part_nums.cur >= part_iter->part_nums.end)
+ {
+ part_iter->part_nums.cur= part_iter->part_nums.start;
+ return NOT_A_PARTITION_ID;
+ }
+ else
+ return part_iter->part_nums.cur++;
+}
+
+
+/*
+ PARTITION_ITERATOR::get_next implementation for LIST partitioning
+
+ SYNOPSIS
+ get_next_partition_id_list()
+ part_iter Partition set iterator structure
+
+ 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.
+ The function conforms to partition_iter_func type.
+
+ RETURN
+ partition id
+ NOT_A_PARTITION_ID if there are no more partitions
+*/
+
+uint32 get_next_partition_id_list(PARTITION_ITERATOR *part_iter)
+{
+ if (part_iter->part_nums.cur == part_iter->part_nums.end)
+ {
+ if (part_iter->ret_null_part)
+ {
+ part_iter->ret_null_part= FALSE;
+ return part_iter->part_info->has_null_part_id;
+ }
+ part_iter->part_nums.cur= part_iter->part_nums.start;
+ part_iter->ret_null_part= part_iter->ret_null_part_orig;
+ return NOT_A_PARTITION_ID;
+ }
+ else
+ return part_iter->part_info->list_array[part_iter->
+ part_nums.cur++].partition_id;
+}
+
+
+/*
+ PARTITION_ITERATOR::get_next implementation: walk over field-space interval
+
+ SYNOPSIS
+ get_next_partition_via_walking()
+ part_iter Partitioning iterator
+
+ DESCRIPTION
+ This implementation of PARTITION_ITERATOR::get_next() returns ids of
+ partitions that contain records with partitioning field value within
+ [start_val, end_val] interval.
+ The function conforms to partition_iter_func type.
+
+ RETURN
+ partition id
+ NOT_A_PARTITION_ID if there are no more partitioning.
+*/
+
+static uint32 get_next_partition_via_walking(PARTITION_ITERATOR *part_iter)
+{
+ uint32 part_id;
+ Field *field= part_iter->part_info->part_field_array[0];
+ while (part_iter->field_vals.cur != part_iter->field_vals.end)
+ {
+ longlong dummy;
+ field->store(part_iter->field_vals.cur++,
+ ((Field_num*)field)->unsigned_flag);
+ if (part_iter->part_info->is_sub_partitioned() &&
+ !part_iter->part_info->get_part_partition_id(part_iter->part_info,
+ &part_id, &dummy) ||
+ !part_iter->part_info->get_partition_id(part_iter->part_info,
+ &part_id, &dummy))
+ return part_id;
+ }
+ part_iter->field_vals.cur= part_iter->field_vals.start;
+ return NOT_A_PARTITION_ID;
+}
+
+
+/* Same as get_next_partition_via_walking, but for subpartitions */
+
+static uint32 get_next_subpartition_via_walking(PARTITION_ITERATOR *part_iter)
+{
+ Field *field= part_iter->part_info->subpart_field_array[0];
+ uint32 res;
+ if (part_iter->field_vals.cur == part_iter->field_vals.end)
+ {
+ part_iter->field_vals.cur= part_iter->field_vals.start;
+ return NOT_A_PARTITION_ID;
+ }
+ field->store(part_iter->field_vals.cur++, FALSE);
+ if (part_iter->part_info->get_subpartition_id(part_iter->part_info,
+ &res))
+ return NOT_A_PARTITION_ID;
+ return res;
+
+}
+
+
+/*
+ Create partition names
+
+ SYNOPSIS
+ create_partition_name()
+ out:out Created partition name string
+ in1 First part
+ in2 Second part
+ name_variant Normal, temporary or renamed partition name
+
+ RETURN VALUE
+ NONE
+
+ DESCRIPTION
+ This method is used to calculate the partition name, service routine to
+ the del_ren_cre_table method.
+*/
+
+void create_partition_name(char *out, const char *in1,
+ const char *in2, uint name_variant,
+ bool translate)
+{
+ char transl_part_name[FN_REFLEN];
+ const char *transl_part;
+
+ if (translate)
+ {
+ tablename_to_filename(in2, transl_part_name, FN_REFLEN);
+ transl_part= transl_part_name;
+ }
+ else
+ transl_part= in2;
+ if (name_variant == NORMAL_PART_NAME)
+ strxmov(out, in1, "#P#", transl_part, NullS);
+ else if (name_variant == TEMP_PART_NAME)
+ strxmov(out, in1, "#P#", transl_part, "#TMP#", NullS);
+ else if (name_variant == RENAMED_PART_NAME)
+ strxmov(out, in1, "#P#", transl_part, "#REN#", NullS);
+}
+
+
+/*
+ Create subpartition name
+
+ SYNOPSIS
+ create_subpartition_name()
+ out:out Created partition name string
+ in1 First part
+ in2 Second part
+ in3 Third part
+ name_variant Normal, temporary or renamed partition name
+
+ RETURN VALUE
+ NONE
+
+ DESCRIPTION
+ This method is used to calculate the subpartition name, service routine to
+ the del_ren_cre_table method.
+*/
+
+void create_subpartition_name(char *out, const char *in1,
+ const char *in2, const char *in3,
+ uint name_variant)
+{
+ char transl_part_name[FN_REFLEN], transl_subpart_name[FN_REFLEN];
+
+ tablename_to_filename(in2, transl_part_name, FN_REFLEN);
+ tablename_to_filename(in3, transl_subpart_name, FN_REFLEN);
+ if (name_variant == NORMAL_PART_NAME)
+ strxmov(out, in1, "#P#", transl_part_name,
+ "#SP#", transl_subpart_name, NullS);
+ else if (name_variant == TEMP_PART_NAME)
+ strxmov(out, in1, "#P#", transl_part_name,
+ "#SP#", transl_subpart_name, "#TMP#", NullS);
+ else if (name_variant == RENAMED_PART_NAME)
+ strxmov(out, in1, "#P#", transl_part_name,
+ "#SP#", transl_subpart_name, "#REN#", NullS);
+}
+#endif
+
diff --git a/sql/sql_partition.h b/sql/sql_partition.h
new file mode 100644
index 00000000000..282e24f1853
--- /dev/null
+++ b/sql/sql_partition.h
@@ -0,0 +1,211 @@
+/* Copyright (C) 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#ifdef __GNUC__
+#pragma interface /* gcc class implementation */
+#endif
+
+/* 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;
+*/
+typedef struct {
+ longlong list_value;
+ uint32 partition_id;
+} LIST_PART_ENTRY;
+
+typedef struct {
+ uint32 start_part;
+ uint32 end_part;
+} part_id_range;
+
+struct st_partition_iter;
+#define NOT_A_PARTITION_ID ((uint32)-1)
+
+bool is_partition_in_list(char *part_name, List<char> list_part_names);
+char *are_partitions_in_table(partition_info *new_part_info,
+ partition_info *old_part_info);
+bool check_reorganise_list(partition_info *new_part_info,
+ partition_info *old_part_info,
+ List<char> list_part_names);
+handler *get_ha_partition(partition_info *part_info);
+int get_parts_for_update(const uchar *old_data, uchar *new_data,
+ const uchar *rec0, partition_info *part_info,
+ uint32 *old_part_id, uint32 *new_part_id,
+ longlong *func_value);
+int get_part_for_delete(const uchar *buf, const uchar *rec0,
+ partition_info *part_info, uint32 *part_id);
+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);
+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);
+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, const 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);
+void make_used_partitions_str(partition_info *part_info, String *parts_str);
+uint32 get_list_array_idx_for_endpoint(partition_info *part_info,
+ bool left_endpoint,
+ bool include_endpoint);
+uint32 get_partition_id_range_for_endpoint(partition_info *part_info,
+ bool left_endpoint,
+ bool include_endpoint);
+bool fix_fields_part_func(THD *thd, Item* func_expr, TABLE *table,
+ bool is_sub_part, bool is_field_to_be_setup);
+
+bool check_part_func_fields(Field **ptr, bool ok_with_charsets);
+bool field_is_partition_charset(Field *field);
+
+/*
+ A "Get next" function for partition iterator.
+
+ SYNOPSIS
+ partition_iter_func()
+ part_iter Partition iterator, you call only "iter.get_next(&iter)"
+
+ DESCRIPTION
+ Depending on whether partitions or sub-partitions are iterated, the
+ function returns next subpartition id/partition number. The sequence of
+ returned numbers is not ordered and may contain duplicates.
+
+ When the end of sequence is reached, NOT_A_PARTITION_ID is returned, and
+ the iterator resets itself (so next get_next() call will start to
+ enumerate the set all over again).
+
+ RETURN
+ NOT_A_PARTITION_ID if there are no more partitions.
+ [sub]partition_id of the next partition
+*/
+
+typedef uint32 (*partition_iter_func)(st_partition_iter* part_iter);
+
+
+/*
+ Partition set iterator. Used to enumerate a set of [sub]partitions
+ obtained in partition interval analysis (see get_partitions_in_range_iter).
+
+ For the user, the only meaningful field is get_next, which may be used as
+ follows:
+ part_iterator.get_next(&part_iterator);
+
+ Initialization is done by any of the following calls:
+ - get_partitions_in_range_iter-type function call
+ - init_single_partition_iterator()
+ - init_all_partitions_iterator()
+ Cleanup is not needed.
+*/
+
+typedef struct st_partition_iter
+{
+ partition_iter_func get_next;
+ /*
+ Valid for "Interval mapping" in LIST partitioning: if true, let the
+ iterator also produce id of the partition that contains NULL value.
+ */
+ bool ret_null_part, ret_null_part_orig;
+ struct st_part_num_range
+ {
+ uint32 start;
+ uint32 cur;
+ uint32 end;
+ };
+
+ struct st_field_value_range
+ {
+ longlong start;
+ longlong cur;
+ longlong end;
+ };
+
+ union
+ {
+ struct st_part_num_range part_nums;
+ struct st_field_value_range field_vals;
+ };
+ partition_info *part_info;
+} PARTITION_ITERATOR;
+
+
+/*
+ Get an iterator for set of partitions that match given field-space interval
+
+ 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
+
+ DESCRIPTION
+ Functions with this signature are used to perform "Partitioning Interval
+ Analysis". This analysis is applicable for any type of [sub]partitioning
+ by some function of a single fieldX. The idea is as follows:
+ Given an interval "const1 <=? fieldX <=? const2", find a set of partitions
+ that may contain records with value of fieldX within the given interval.
+
+ The min_val, max_val and flags parameters specify the interval.
+ The set of partitions is returned by initializing an iterator in *part_iter
+
+ NOTES
+ There are currently two functions of this type:
+ - get_part_iter_for_interval_via_walking
+ - get_part_iter_for_interval_via_mapping
+
+ RETURN
+ 0 - No matching partitions, iterator not initialized
+ 1 - Some partitions would match, iterator intialized for traversing them
+ -1 - All partitions would match, iterator not initialized
+*/
+
+typedef int (*get_partitions_in_range_iter)(partition_info *part_info,
+ bool is_subpart,
+ uchar *min_val, uchar *max_val,
+ uint flags,
+ PARTITION_ITERATOR *part_iter);
+
+#include "partition_info.h"
+
diff --git a/sql/sql_plugin.cc b/sql/sql_plugin.cc
new file mode 100644
index 00000000000..60f205ec8e8
--- /dev/null
+++ b/sql/sql_plugin.cc
@@ -0,0 +1,3314 @@
+/* Copyright (C) 2005 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#include "mysql_priv.h"
+#include <my_pthread.h>
+#include <my_getopt.h>
+#define REPORT_TO_LOG 1
+#define REPORT_TO_USER 2
+
+#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
+
+extern struct st_mysql_plugin *mysqld_builtins[];
+
+char *opt_plugin_load= NULL;
+char *opt_plugin_dir_ptr;
+char opt_plugin_dir[FN_REFLEN];
+/*
+ When you ad a new plugin type, add both a string and make sure that the
+ init and deinit array are correctly updated.
+*/
+const LEX_STRING plugin_type_names[MYSQL_MAX_PLUGIN_TYPE_NUM]=
+{
+ { C_STRING_WITH_LEN("UDF") },
+ { C_STRING_WITH_LEN("STORAGE ENGINE") },
+ { C_STRING_WITH_LEN("FTPARSER") },
+ { C_STRING_WITH_LEN("DAEMON") },
+ { C_STRING_WITH_LEN("INFORMATION SCHEMA") }
+};
+
+extern int initialize_schema_table(st_plugin_int *plugin);
+extern int finalize_schema_table(st_plugin_int *plugin);
+
+/*
+ The number of elements in both plugin_type_initialize and
+ plugin_type_deinitialize should equal to the number of plugins
+ defined.
+*/
+plugin_type_init plugin_type_initialize[MYSQL_MAX_PLUGIN_TYPE_NUM]=
+{
+ 0,ha_initialize_handlerton,0,0,initialize_schema_table
+};
+
+plugin_type_init plugin_type_deinitialize[MYSQL_MAX_PLUGIN_TYPE_NUM]=
+{
+ 0,ha_finalize_handlerton,0,0,finalize_schema_table
+};
+
+#ifdef HAVE_DLOPEN
+static const char *plugin_interface_version_sym=
+ "_mysql_plugin_interface_version_";
+static const char *sizeof_st_plugin_sym=
+ "_mysql_sizeof_struct_st_plugin_";
+static const char *plugin_declarations_sym= "_mysql_plugin_declarations_";
+static int min_plugin_interface_version= MYSQL_PLUGIN_INTERFACE_VERSION & ~0xFF;
+#endif
+
+/* Note that 'int version' must be the first field of every plugin
+ sub-structure (plugin->info).
+*/
+static int min_plugin_info_interface_version[MYSQL_MAX_PLUGIN_TYPE_NUM]=
+{
+ 0x0000,
+ MYSQL_HANDLERTON_INTERFACE_VERSION,
+ MYSQL_FTPARSER_INTERFACE_VERSION,
+ MYSQL_DAEMON_INTERFACE_VERSION,
+ MYSQL_INFORMATION_SCHEMA_INTERFACE_VERSION
+};
+static int cur_plugin_info_interface_version[MYSQL_MAX_PLUGIN_TYPE_NUM]=
+{
+ 0x0000, /* UDF: not implemented */
+ MYSQL_HANDLERTON_INTERFACE_VERSION,
+ MYSQL_FTPARSER_INTERFACE_VERSION,
+ MYSQL_DAEMON_INTERFACE_VERSION,
+ MYSQL_INFORMATION_SCHEMA_INTERFACE_VERSION
+};
+
+static bool initialized= 0;
+
+/*
+ A mutex LOCK_plugin must be acquired before accessing the
+ following variables/structures.
+ We are always manipulating ref count, so a rwlock here is unneccessary.
+*/
+pthread_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 bool reap_needed= false;
+static int plugin_array_version=0;
+
+/*
+ write-lock on LOCK_system_variables_hash is required before modifying
+ the following variables/structures
+*/
+static MEM_ROOT plugin_mem_root;
+static uint global_variables_dynamic_size= 0;
+static HASH bookmark_hash;
+
+
+/*
+ hidden part of opaque value passed to variable check functions.
+ Used to provide a object-like structure to non C++ consumers.
+*/
+struct st_item_value_holder : public st_mysql_value
+{
+ Item *item;
+};
+
+
+/*
+ stored in bookmark_hash, this structure is never removed from the
+ hash and is used to mark a single offset for a thd local variable
+ even if plugins have been uninstalled and reinstalled, repeatedly.
+ This structure is allocated from plugin_mem_root.
+
+ The key format is as follows:
+ 1 byte - variable type code
+ name_len bytes - variable name
+ '\0' - end of key
+*/
+struct st_bookmark
+{
+ uint name_len;
+ int offset;
+ uint version;
+ char key[1];
+};
+
+
+/*
+ skeleton of a plugin variable - portion of structure common to all.
+*/
+struct st_mysql_sys_var
+{
+ MYSQL_PLUGIN_VAR_HEADER;
+};
+
+
+/*
+ sys_var class for access to all plugin variables visible to the user
+*/
+class sys_var_pluginvar: public sys_var
+{
+public:
+ struct st_plugin_int *plugin;
+ struct st_mysql_sys_var *plugin_var;
+
+ static void *operator new(size_t size, MEM_ROOT *mem_root)
+ { return (void*) alloc_root(mem_root, (uint) size); }
+ static void operator delete(void *ptr_arg,size_t size)
+ { TRASH(ptr_arg, size); }
+
+ sys_var_pluginvar(const char *name_arg,
+ struct st_mysql_sys_var *plugin_var_arg)
+ :sys_var(name_arg), plugin_var(plugin_var_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);
+};
+
+
+/* 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 int test_plugin_options(MEM_ROOT *, struct st_plugin_int *,
+ int *, char **, my_bool);
+static bool register_builtin(struct st_mysql_plugin *, struct st_plugin_int *,
+ 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 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 */
+
+
+/****************************************************************************
+ Value type thunks, allows the C world to play in the C++ world
+****************************************************************************/
+
+static int item_value_type(struct st_mysql_value *value)
+{
+ switch (((st_item_value_holder*)value)->item->result_type()) {
+ case INT_RESULT:
+ return MYSQL_VALUE_TYPE_INT;
+ case REAL_RESULT:
+ return MYSQL_VALUE_TYPE_REAL;
+ default:
+ return MYSQL_VALUE_TYPE_STRING;
+ }
+}
+
+static const char *item_val_str(struct st_mysql_value *value,
+ char *buffer, int *length)
+{
+ String str(buffer, *length, system_charset_info), *res;
+ if (!(res= ((st_item_value_holder*)value)->item->val_str(&str)))
+ return NULL;
+ *length= res->length();
+ if (res->c_ptr_quick() == buffer)
+ return buffer;
+
+ /*
+ Lets be nice and create a temporary string since the
+ buffer was too small
+ */
+ return current_thd->strmake(res->c_ptr_quick(), res->length());
+}
+
+
+static int item_val_int(struct st_mysql_value *value, long long *buf)
+{
+ Item *item= ((st_item_value_holder*)value)->item;
+ *buf= item->val_int();
+ if (item->is_null())
+ return 1;
+ return 0;
+}
+
+
+static int item_val_real(struct st_mysql_value *value, double *buf)
+{
+ Item *item= ((st_item_value_holder*)value)->item;
+ *buf= item->val_real();
+ if (item->is_null())
+ return 1;
+ return 0;
+}
+
+
+/****************************************************************************
+ Plugin support code
+****************************************************************************/
+
+#ifdef HAVE_DLOPEN
+
+static struct st_plugin_dl *plugin_dl_find(const LEX_STRING *dl)
+{
+ uint i;
+ struct st_plugin_dl *tmp;
+ DBUG_ENTER("plugin_dl_find");
+ for (i= 0; i < plugin_dl_array.elements; i++)
+ {
+ tmp= *dynamic_element(&plugin_dl_array, i, struct st_plugin_dl **);
+ if (tmp->ref_count &&
+ ! my_strnncoll(files_charset_info,
+ (const uchar *)dl->str, dl->length,
+ (const uchar *)tmp->dl.str, tmp->dl.length))
+ DBUG_RETURN(tmp);
+ }
+ DBUG_RETURN(0);
+}
+
+
+static st_plugin_dl *plugin_dl_insert_or_reuse(struct st_plugin_dl *plugin_dl)
+{
+ uint i;
+ struct st_plugin_dl *tmp;
+ DBUG_ENTER("plugin_dl_insert_or_reuse");
+ for (i= 0; i < plugin_dl_array.elements; i++)
+ {
+ tmp= *dynamic_element(&plugin_dl_array, i, struct st_plugin_dl **);
+ if (! tmp->ref_count)
+ {
+ memcpy(tmp, plugin_dl, sizeof(struct st_plugin_dl));
+ DBUG_RETURN(tmp);
+ }
+ }
+ if (insert_dynamic(&plugin_dl_array, (uchar*)&plugin_dl))
+ DBUG_RETURN(0);
+ tmp= *dynamic_element(&plugin_dl_array, plugin_dl_array.elements - 1,
+ struct st_plugin_dl **)=
+ (struct st_plugin_dl *) memdup_root(&plugin_mem_root, (uchar*)plugin_dl,
+ sizeof(struct st_plugin_dl));
+ DBUG_RETURN(tmp);
+}
+#endif /* HAVE_DLOPEN */
+
+
+static inline void free_plugin_mem(struct st_plugin_dl *p)
+{
+#ifdef HAVE_DLOPEN
+ if (p->handle)
+ dlclose(p->handle);
+#endif
+ my_free(p->dl.str, MYF(MY_ALLOW_ZERO_PTR));
+ if (p->version != MYSQL_PLUGIN_INTERFACE_VERSION)
+ my_free((uchar*)p->plugins, MYF(MY_ALLOW_ZERO_PTR));
+}
+
+
+static st_plugin_dl *plugin_dl_add(const LEX_STRING *dl, int report)
+{
+#ifdef HAVE_DLOPEN
+ char dlpath[FN_REFLEN];
+ uint plugin_dir_len, dummy_errors, dlpathlen;
+ struct st_plugin_dl *tmp, plugin_dl;
+ void *sym;
+ DBUG_ENTER("plugin_dl_add");
+ plugin_dir_len= strlen(opt_plugin_dir);
+ /*
+ Ensure that the dll doesn't have a path.
+ This is done to ensure that only approved libraries from the
+ plugin directory are used (to make this even remotely secure).
+ */
+ if (my_strchr(files_charset_info, dl->str, dl->str + dl->length, FN_LIBCHAR) ||
+ check_string_char_length((LEX_STRING *) dl, "", NAME_CHAR_LEN,
+ system_charset_info, 1) ||
+ plugin_dir_len + dl->length + 1 >= FN_REFLEN)
+ {
+ if (report & REPORT_TO_USER)
+ my_error(ER_UDF_NO_PATHS, MYF(0));
+ if (report & REPORT_TO_LOG)
+ sql_print_error(ER(ER_UDF_NO_PATHS));
+ DBUG_RETURN(0);
+ }
+ /* If this dll is already loaded just increase ref_count. */
+ if ((tmp= plugin_dl_find(dl)))
+ {
+ tmp->ref_count++;
+ DBUG_RETURN(tmp);
+ }
+ bzero(&plugin_dl, sizeof(plugin_dl));
+ /* Compile dll path */
+ dlpathlen=
+ strxnmov(dlpath, sizeof(dlpath) - 1, opt_plugin_dir, "/", dl->str, NullS) -
+ dlpath;
+ plugin_dl.ref_count= 1;
+ /* Open new dll handle */
+ if (!(plugin_dl.handle= dlopen(dlpath, RTLD_NOW)))
+ {
+ const char *errmsg=dlerror();
+ if (!strncmp(dlpath, errmsg, dlpathlen))
+ { // if errmsg starts from dlpath, trim this prefix.
+ errmsg+=dlpathlen;
+ if (*errmsg == ':') errmsg++;
+ if (*errmsg == ' ') errmsg++;
+ }
+ if (report & REPORT_TO_USER)
+ my_error(ER_CANT_OPEN_LIBRARY, MYF(0), dlpath, errno, errmsg);
+ if (report & REPORT_TO_LOG)
+ sql_print_error(ER(ER_CANT_OPEN_LIBRARY), dlpath, errno, errmsg);
+ DBUG_RETURN(0);
+ }
+ /* Determine interface version */
+ if (!(sym= dlsym(plugin_dl.handle, plugin_interface_version_sym)))
+ {
+ free_plugin_mem(&plugin_dl);
+ if (report & REPORT_TO_USER)
+ my_error(ER_CANT_FIND_DL_ENTRY, MYF(0), plugin_interface_version_sym);
+ if (report & REPORT_TO_LOG)
+ sql_print_error(ER(ER_CANT_FIND_DL_ENTRY), plugin_interface_version_sym);
+ DBUG_RETURN(0);
+ }
+ plugin_dl.version= *(int *)sym;
+ /* Versioning */
+ if (plugin_dl.version < min_plugin_interface_version ||
+ (plugin_dl.version >> 8) > (MYSQL_PLUGIN_INTERFACE_VERSION >> 8))
+ {
+ free_plugin_mem(&plugin_dl);
+ if (report & REPORT_TO_USER)
+ my_error(ER_CANT_OPEN_LIBRARY, MYF(0), dlpath, 0,
+ "plugin interface version mismatch");
+ if (report & REPORT_TO_LOG)
+ sql_print_error(ER(ER_CANT_OPEN_LIBRARY), dlpath, 0,
+ "plugin interface version mismatch");
+ DBUG_RETURN(0);
+ }
+ /* Find plugin declarations */
+ if (!(sym= dlsym(plugin_dl.handle, plugin_declarations_sym)))
+ {
+ free_plugin_mem(&plugin_dl);
+ if (report & REPORT_TO_USER)
+ my_error(ER_CANT_FIND_DL_ENTRY, MYF(0), plugin_declarations_sym);
+ if (report & REPORT_TO_LOG)
+ sql_print_error(ER(ER_CANT_FIND_DL_ENTRY), plugin_declarations_sym);
+ DBUG_RETURN(0);
+ }
+
+ if (plugin_dl.version != MYSQL_PLUGIN_INTERFACE_VERSION)
+ {
+ int i;
+ uint sizeof_st_plugin;
+ struct st_mysql_plugin *old, *cur;
+ char *ptr= (char *)sym;
+
+ if ((sym= dlsym(plugin_dl.handle, sizeof_st_plugin_sym)))
+ sizeof_st_plugin= *(int *)sym;
+ else
+ {
+#ifdef ERROR_ON_NO_SIZEOF_PLUGIN_SYMBOL
+ free_plugin_mem(&plugin_dl);
+ if (report & REPORT_TO_USER)
+ my_error(ER_CANT_FIND_DL_ENTRY, MYF(0), sizeof_st_plugin_sym);
+ if (report & REPORT_TO_LOG)
+ sql_print_error(ER(ER_CANT_FIND_DL_ENTRY), sizeof_st_plugin_sym);
+ DBUG_RETURN(0);
+#else
+ /*
+ When the following assert starts failing, we'll have to switch
+ to the upper branch of the #ifdef
+ */
+ DBUG_ASSERT(min_plugin_interface_version == 0);
+ sizeof_st_plugin= (int)offsetof(struct st_mysql_plugin, version);
+#endif
+ }
+
+ for (i= 0;
+ ((struct st_mysql_plugin *)(ptr+i*sizeof_st_plugin))->info;
+ i++)
+ /* no op */;
+
+ cur= (struct st_mysql_plugin*)
+ my_malloc(i*sizeof(struct st_mysql_plugin), MYF(MY_ZEROFILL|MY_WME));
+ if (!cur)
+ {
+ free_plugin_mem(&plugin_dl);
+ if (report & REPORT_TO_USER)
+ my_error(ER_OUTOFMEMORY, MYF(0), plugin_dl.dl.length);
+ if (report & REPORT_TO_LOG)
+ sql_print_error(ER(ER_OUTOFMEMORY), plugin_dl.dl.length);
+ DBUG_RETURN(0);
+ }
+ /*
+ All st_plugin fields not initialized in the plugin explicitly, are
+ set to 0. It matches C standard behaviour for struct initializers that
+ have less values than the struct definition.
+ */
+ for (i=0;
+ (old=(struct st_mysql_plugin *)(ptr+i*sizeof_st_plugin))->info;
+ i++)
+ memcpy(cur+i, old, min(sizeof(cur[i]), sizeof_st_plugin));
+
+ sym= cur;
+ }
+ plugin_dl.plugins= (struct st_mysql_plugin *)sym;
+
+ /* Duplicate and convert dll name */
+ plugin_dl.dl.length= dl->length * files_charset_info->mbmaxlen + 1;
+ if (! (plugin_dl.dl.str= (char*) my_malloc(plugin_dl.dl.length, MYF(0))))
+ {
+ free_plugin_mem(&plugin_dl);
+ if (report & REPORT_TO_USER)
+ my_error(ER_OUTOFMEMORY, MYF(0), plugin_dl.dl.length);
+ if (report & REPORT_TO_LOG)
+ sql_print_error(ER(ER_OUTOFMEMORY), plugin_dl.dl.length);
+ DBUG_RETURN(0);
+ }
+ plugin_dl.dl.length= copy_and_convert(plugin_dl.dl.str, plugin_dl.dl.length,
+ files_charset_info, dl->str, dl->length, system_charset_info,
+ &dummy_errors);
+ plugin_dl.dl.str[plugin_dl.dl.length]= 0;
+ /* Add this dll to array */
+ if (! (tmp= plugin_dl_insert_or_reuse(&plugin_dl)))
+ {
+ free_plugin_mem(&plugin_dl);
+ if (report & REPORT_TO_USER)
+ my_error(ER_OUTOFMEMORY, MYF(0), sizeof(struct st_plugin_dl));
+ if (report & REPORT_TO_LOG)
+ sql_print_error(ER(ER_OUTOFMEMORY), sizeof(struct st_plugin_dl));
+ DBUG_RETURN(0);
+ }
+ DBUG_RETURN(tmp);
+#else
+ DBUG_ENTER("plugin_dl_add");
+ if (report & REPORT_TO_USER)
+ my_error(ER_FEATURE_DISABLED, MYF(0), "plugin", "HAVE_DLOPEN");
+ if (report & REPORT_TO_LOG)
+ sql_print_error(ER(ER_FEATURE_DISABLED), "plugin", "HAVE_DLOPEN");
+ DBUG_RETURN(0);
+#endif
+}
+
+
+static void plugin_dl_del(const LEX_STRING *dl)
+{
+#ifdef HAVE_DLOPEN
+ uint i;
+ DBUG_ENTER("plugin_dl_del");
+
+ safe_mutex_assert_owner(&LOCK_plugin);
+
+ for (i= 0; i < plugin_dl_array.elements; i++)
+ {
+ struct st_plugin_dl *tmp= *dynamic_element(&plugin_dl_array, i,
+ struct st_plugin_dl **);
+ if (tmp->ref_count &&
+ ! my_strnncoll(files_charset_info,
+ (const uchar *)dl->str, dl->length,
+ (const uchar *)tmp->dl.str, tmp->dl.length))
+ {
+ /* Do not remove this element, unless no other plugin uses this dll. */
+ if (! --tmp->ref_count)
+ {
+ free_plugin_mem(tmp);
+ bzero(tmp, sizeof(struct st_plugin_dl));
+ }
+ break;
+ }
+ }
+ DBUG_VOID_RETURN;
+#endif
+}
+
+
+static struct st_plugin_int *plugin_find_internal(const LEX_STRING *name, int type)
+{
+ uint i;
+ DBUG_ENTER("plugin_find_internal");
+ if (! initialized)
+ DBUG_RETURN(0);
+
+ safe_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);
+ if (plugin)
+ DBUG_RETURN(plugin);
+ }
+ }
+ else
+ DBUG_RETURN((st_plugin_int *)
+ hash_search(&plugin_hash[type], (const uchar *)name->str, name->length));
+ DBUG_RETURN(0);
+}
+
+
+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);
+ 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);
+ DBUG_RETURN(rc);
+}
+
+
+bool plugin_is_ready(const LEX_STRING *name, int type)
+{
+ bool rc= FALSE;
+ if (plugin_status(name, type) == SHOW_OPTION_YES)
+ rc= TRUE;
+ return rc;
+}
+
+
+SHOW_COMP_OPTION sys_var_have_plugin::get_option()
+{
+ LEX_STRING plugin_name= { (char *) plugin_name_str, plugin_name_len };
+ return plugin_status(&plugin_name, plugin_type);
+}
+
+
+static plugin_ref intern_plugin_lock(LEX *lex, plugin_ref rc CALLER_INFO_PROTO)
+{
+ st_plugin_int *pi= plugin_ref_to_int(rc);
+ DBUG_ENTER("intern_plugin_lock");
+
+ safe_mutex_assert_owner(&LOCK_plugin);
+
+ if (pi->state & (PLUGIN_IS_READY | PLUGIN_IS_UNINITIALIZED))
+ {
+ plugin_ref plugin;
+#ifdef DBUG_OFF
+ /* built-in plugins don't need ref counting */
+ if (!pi->plugin_dl)
+ DBUG_RETURN(pi);
+
+ plugin= pi;
+#else
+ /*
+ For debugging, we do an additional malloc which allows the
+ 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))))
+ DBUG_RETURN(NULL);
+
+ *plugin= pi;
+#endif
+ pi->ref_count++;
+ DBUG_PRINT("info",("thd: 0x%lx, plugin: \"%s\", ref_count: %d",
+ (long) current_thd, pi->name.str, pi->ref_count));
+
+ if (lex)
+ insert_dynamic(&lex->plugins, (uchar*)&plugin);
+ DBUG_RETURN(plugin);
+ }
+ DBUG_RETURN(NULL);
+}
+
+
+plugin_ref plugin_lock(THD *thd, plugin_ref *ptr CALLER_INFO_PROTO)
+{
+ LEX *lex= thd ? thd->lex : 0;
+ plugin_ref rc;
+ DBUG_ENTER("plugin_lock");
+ pthread_mutex_lock(&LOCK_plugin);
+ rc= my_intern_plugin_lock_ci(lex, *ptr);
+ pthread_mutex_unlock(&LOCK_plugin);
+ DBUG_RETURN(rc);
+}
+
+
+plugin_ref plugin_lock_by_name(THD *thd, const LEX_STRING *name, int type
+ CALLER_INFO_PROTO)
+{
+ 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);
+ if ((plugin= plugin_find_internal(name, type)))
+ rc= my_intern_plugin_lock_ci(lex, plugin_int_to_ref(plugin));
+ pthread_mutex_unlock(&LOCK_plugin);
+ DBUG_RETURN(rc);
+}
+
+
+static st_plugin_int *plugin_insert_or_reuse(struct st_plugin_int *plugin)
+{
+ uint i;
+ struct st_plugin_int *tmp;
+ DBUG_ENTER("plugin_insert_or_reuse");
+ for (i= 0; i < plugin_array.elements; i++)
+ {
+ tmp= *dynamic_element(&plugin_array, i, struct st_plugin_int **);
+ if (tmp->state == PLUGIN_IS_FREED)
+ {
+ memcpy(tmp, plugin, sizeof(struct st_plugin_int));
+ DBUG_RETURN(tmp);
+ }
+ }
+ if (insert_dynamic(&plugin_array, (uchar*)&plugin))
+ DBUG_RETURN(0);
+ tmp= *dynamic_element(&plugin_array, plugin_array.elements - 1,
+ struct st_plugin_int **)=
+ (struct st_plugin_int *) memdup_root(&plugin_mem_root, (uchar*)plugin,
+ sizeof(struct st_plugin_int));
+ DBUG_RETURN(tmp);
+}
+
+
+/*
+ NOTE
+ 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)
+{
+ struct st_plugin_int tmp;
+ struct st_mysql_plugin *plugin;
+ DBUG_ENTER("plugin_add");
+ if (plugin_find_internal(name, MYSQL_ANY_PLUGIN))
+ {
+ if (report & REPORT_TO_USER)
+ my_error(ER_UDF_EXISTS, MYF(0), name->str);
+ if (report & REPORT_TO_LOG)
+ sql_print_error(ER(ER_UDF_EXISTS), name->str);
+ DBUG_RETURN(TRUE);
+ }
+ /* Clear the whole struct to catch future extensions. */
+ bzero((char*) &tmp, sizeof(tmp));
+ 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))
+ {
+ struct st_plugin_int *tmp_plugin_ptr;
+ if (*(int*)plugin->info <
+ min_plugin_info_interface_version[plugin->type] ||
+ ((*(int*)plugin->info) >> 8) >
+ (cur_plugin_info_interface_version[plugin->type] >> 8))
+ {
+ char buf[256];
+ strxnmov(buf, sizeof(buf) - 1, "API version for ",
+ plugin_type_names[plugin->type].str,
+ " plugin is too different", NullS);
+ if (report & REPORT_TO_USER)
+ my_error(ER_CANT_OPEN_LIBRARY, MYF(0), dl->str, 0, buf);
+ if (report & REPORT_TO_LOG)
+ sql_print_error(ER(ER_CANT_OPEN_LIBRARY), dl->str, 0, 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, true))
+ tmp.state= PLUGIN_IS_DISABLED;
+
+ 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);
+ }
+ tmp_plugin_ptr->state= PLUGIN_IS_FREED;
+ }
+ mysql_del_sys_var_chain(tmp.system_vars);
+ goto err;
+
+ /* plugin was disabled */
+ plugin_dl_del(dl);
+ DBUG_RETURN(FALSE);
+ }
+ }
+ if (report & REPORT_TO_USER)
+ my_error(ER_CANT_FIND_DL_ENTRY, MYF(0), name->str);
+ if (report & REPORT_TO_LOG)
+ sql_print_error(ER(ER_CANT_FIND_DL_ENTRY), name->str);
+err:
+ plugin_dl_del(dl);
+ DBUG_RETURN(TRUE);
+}
+
+
+static void plugin_deinitialize(struct st_plugin_int *plugin, bool ref_check)
+{
+ /*
+ we don't want to hold the LOCK_plugin mutex as it may cause
+ deinitialization to deadlock if plugins have worker threads
+ with plugin locks
+ */
+ safe_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.
+ */
+ SHOW_VAR array[2]= {
+ {plugin->plugin->name, (char*)plugin->plugin->status_vars, SHOW_ARRAY},
+ {0, 0, SHOW_UNDEF}
+ };
+ remove_status_vars(array);
+#else
+ remove_status_vars(plugin->plugin->status_vars);
+#endif /* FIX_LATER */
+ }
+
+ if (plugin_type_deinitialize[plugin->plugin->type])
+ {
+ if ((*plugin_type_deinitialize[plugin->plugin->type])(plugin))
+ {
+ sql_print_error("Plugin '%s' of type %s failed deinitialization",
+ plugin->name.str, plugin_type_names[plugin->plugin->type].str);
+ }
+ }
+ else if (plugin->plugin->deinit)
+ {
+ DBUG_PRINT("info", ("Deinitializing plugin: '%s'", plugin->name.str));
+ if (plugin->plugin->deinit(plugin))
+ {
+ DBUG_PRINT("warning", ("Plugin '%s' deinit function returned error.",
+ plugin->name.str));
+ }
+ }
+ plugin->state= PLUGIN_IS_UNINITIALIZED;
+
+ /*
+ We do the check here because NDB has a worker THD which doesn't
+ exit until NDB is shut down.
+ */
+ if (ref_check && plugin->ref_count)
+ sql_print_error("Plugin '%s' has ref_count=%d after deinitialization.",
+ plugin->name.str, plugin->ref_count);
+}
+
+
+static void plugin_del(struct st_plugin_int *plugin)
+{
+ DBUG_ENTER("plugin_del(plugin)");
+ safe_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);
+ 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);
+
+ if (!reap_needed)
+ return;
+
+ reap_needed= false;
+ count= plugin_array.elements;
+ reap= (struct st_plugin_int **)my_alloca(sizeof(plugin)*(count+1));
+ *(reap++)= NULL;
+
+ for (idx= 0; idx < count; idx++)
+ {
+ plugin= *dynamic_element(&plugin_array, idx, struct st_plugin_int **);
+ if (plugin->state == PLUGIN_IS_DELETED && !plugin->ref_count)
+ {
+ /* change the status flag to prevent reaping by another thread */
+ plugin->state= PLUGIN_IS_DYING;
+ *(reap++)= plugin;
+ }
+ }
+
+ pthread_mutex_unlock(&LOCK_plugin);
+
+ list= reap;
+ while ((plugin= *(--list)))
+ plugin_deinitialize(plugin, true);
+
+ pthread_mutex_lock(&LOCK_plugin);
+
+ while ((plugin= *(--reap)))
+ plugin_del(plugin);
+
+ my_afree(reap);
+}
+
+static void intern_plugin_unlock(LEX *lex, plugin_ref plugin)
+{
+ int i;
+ st_plugin_int *pi;
+ DBUG_ENTER("intern_plugin_unlock");
+
+ safe_mutex_assert_owner(&LOCK_plugin);
+
+ if (!plugin)
+ DBUG_VOID_RETURN;
+
+ pi= plugin_ref_to_int(plugin);
+
+#ifdef DBUG_OFF
+ if (!pi->plugin_dl)
+ DBUG_VOID_RETURN;
+#else
+ my_free((uchar*) plugin, MYF(MY_WME));
+#endif
+
+ DBUG_PRINT("info",("unlocking plugin, name= %s, ref_count= %d",
+ pi->name.str, pi->ref_count));
+ if (lex)
+ {
+ /*
+ Remove one instance of this plugin from the use list.
+ We are searching backwards so that plugins locked last
+ could be unlocked faster - optimizing for LIFO semantics.
+ */
+ for (i= lex->plugins.elements - 1; i >= 0; i--)
+ if (plugin == *dynamic_element(&lex->plugins, i, plugin_ref*))
+ {
+ delete_dynamic_element(&lex->plugins, i);
+ break;
+ }
+ DBUG_ASSERT(i >= 0);
+ }
+
+ DBUG_ASSERT(pi->ref_count);
+ pi->ref_count--;
+
+ if (pi->state == PLUGIN_IS_DELETED && !pi->ref_count)
+ reap_needed= true;
+
+ DBUG_VOID_RETURN;
+}
+
+
+void plugin_unlock(THD *thd, plugin_ref plugin)
+{
+ LEX *lex= thd ? thd->lex : 0;
+ DBUG_ENTER("plugin_unlock");
+ if (!plugin)
+ DBUG_VOID_RETURN;
+#ifdef DBUG_OFF
+ /* built-in plugins don't need ref counting */
+ if (!plugin_dlib(plugin))
+ DBUG_VOID_RETURN;
+#endif
+ pthread_mutex_lock(&LOCK_plugin);
+ intern_plugin_unlock(lex, plugin);
+ reap_plugins();
+ pthread_mutex_unlock(&LOCK_plugin);
+ DBUG_VOID_RETURN;
+}
+
+
+void plugin_unlock_list(THD *thd, plugin_ref *list, uint count)
+{
+ LEX *lex= thd ? thd->lex : 0;
+ DBUG_ENTER("plugin_unlock_list");
+ DBUG_ASSERT(list);
+ pthread_mutex_lock(&LOCK_plugin);
+ while (count--)
+ intern_plugin_unlock(lex, *list++);
+ reap_plugins();
+ pthread_mutex_unlock(&LOCK_plugin);
+ DBUG_VOID_RETURN;
+}
+
+
+static int plugin_initialize(struct st_plugin_int *plugin)
+{
+ DBUG_ENTER("plugin_initialize");
+
+ safe_mutex_assert_owner(&LOCK_plugin);
+
+ if (plugin_type_initialize[plugin->plugin->type])
+ {
+ if ((*plugin_type_initialize[plugin->plugin->type])(plugin))
+ {
+ sql_print_error("Plugin '%s' registration as a %s failed.",
+ plugin->name.str, plugin_type_names[plugin->plugin->type].str);
+ goto err;
+ }
+ }
+ else if (plugin->plugin->init)
+ {
+ if (plugin->plugin->init(plugin))
+ {
+ sql_print_error("Plugin '%s' init function returned error.",
+ plugin->name.str);
+ goto err;
+ }
+ }
+
+ plugin->state= PLUGIN_IS_READY;
+
+ 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.
+ */
+ SHOW_VAR array[2]= {
+ {plugin->plugin->name, (char*)plugin->plugin->status_vars, SHOW_ARRAY},
+ {0, 0, SHOW_UNDEF}
+ };
+ if (add_status_vars(array)) // add_status_vars makes a copy
+ goto err;
+#else
+ add_status_vars(plugin->plugin->status_vars); // add_status_vars makes a copy
+#endif /* FIX_LATER */
+ }
+
+ /*
+ set the plugin attribute of plugin's sys vars so they are pointing
+ to the active plugin
+ */
+ if (plugin->system_vars)
+ {
+ sys_var_pluginvar *var= plugin->system_vars->cast_pluginvar();
+ for (;;)
+ {
+ var->plugin= plugin;
+ if (!var->next)
+ break;
+ var= var->next->cast_pluginvar();
+ }
+ }
+
+ DBUG_RETURN(0);
+err:
+ DBUG_RETURN(1);
+}
+
+
+extern "C" uchar *get_plugin_hash_key(const uchar *, size_t *, my_bool);
+extern "C" uchar *get_bookmark_hash_key(const uchar *, size_t *, my_bool);
+
+
+uchar *get_plugin_hash_key(const uchar *buff, size_t *length,
+ my_bool not_used __attribute__((unused)))
+{
+ struct st_plugin_int *plugin= (st_plugin_int *)buff;
+ *length= (uint)plugin->name.length;
+ return((uchar *)plugin->name.str);
+}
+
+
+uchar *get_bookmark_hash_key(const uchar *buff, size_t *length,
+ my_bool not_used __attribute__((unused)))
+{
+ struct st_bookmark *var= (st_bookmark *)buff;
+ *length= var->name_len + 1;
+ return (uchar*) var->key;
+}
+
+
+/*
+ The logic is that we first load and initialize all compiled in plugins.
+ From there we load up the dynamic types (assuming we have not been told to
+ skip this part).
+
+ Finally we initialize everything, aka the dynamic that have yet to initialize.
+*/
+int plugin_init(int *argc, char **argv, int flags)
+{
+ uint i;
+ bool def_enabled, is_myisam;
+ struct st_mysql_plugin **builtins;
+ struct st_mysql_plugin *plugin;
+ struct st_plugin_int tmp, *plugin_ptr, **reap;
+ MEM_ROOT tmp_root;
+ DBUG_ENTER("plugin_init");
+
+ if (initialized)
+ DBUG_RETURN(0);
+
+ init_alloc_root(&plugin_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))
+ goto err;
+
+
+ pthread_mutex_init(&LOCK_plugin, MY_MUTEX_INIT_FAST);
+
+ if (my_init_dynamic_array(&plugin_dl_array,
+ sizeof(struct st_plugin_dl *),16,16) ||
+ my_init_dynamic_array(&plugin_array,
+ sizeof(struct st_plugin_int *),16,16))
+ goto err;
+
+ 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))
+ goto err;
+ }
+
+ pthread_mutex_lock(&LOCK_plugin);
+
+ initialized= 1;
+
+ /*
+ First we register builtin plugins
+ */
+ for (builtins= mysqld_builtins; *builtins; builtins++)
+ {
+ for (plugin= *builtins; plugin->info; plugin++)
+ {
+ if (opt_ignore_builtin_innodb &&
+ !my_strcasecmp(&my_charset_latin1, plugin->name, "InnoDB"))
+ continue;
+ /* by default, ndbcluster and federated are disabled */
+ def_enabled=
+ my_strcasecmp(&my_charset_latin1, plugin->name, "NDBCLUSTER") != 0 &&
+ my_strcasecmp(&my_charset_latin1, plugin->name, "FEDERATED") != 0;
+ bzero(&tmp, sizeof(tmp));
+ tmp.plugin= plugin;
+ tmp.name.str= (char *)plugin->name;
+ tmp.name.length= strlen(plugin->name);
+ tmp.state= 0;
+ free_root(&tmp_root, MYF(MY_MARK_BLOCKS_FREE));
+ if (test_plugin_options(&tmp_root, &tmp, argc, argv, def_enabled))
+ tmp.state= PLUGIN_IS_DISABLED;
+ else
+ tmp.state= PLUGIN_IS_UNINITIALIZED;
+ if (register_builtin(plugin, &tmp, &plugin_ptr))
+ goto err_unlock;
+
+ /* only initialize MyISAM and CSV at this stage */
+ if (!(is_myisam=
+ !my_strcasecmp(&my_charset_latin1, plugin->name, "MyISAM")) &&
+ my_strcasecmp(&my_charset_latin1, plugin->name, "CSV"))
+ continue;
+
+ if (plugin_ptr->state == PLUGIN_IS_UNINITIALIZED &&
+ plugin_initialize(plugin_ptr))
+ goto err_unlock;
+
+ /*
+ initialize the global default storage engine so that it may
+ not be null in any child thread.
+ */
+ if (is_myisam)
+ {
+ DBUG_ASSERT(!global_system_variables.table_plugin);
+ global_system_variables.table_plugin=
+ my_intern_plugin_lock(NULL, plugin_int_to_ref(plugin_ptr));
+ DBUG_ASSERT(plugin_ptr->ref_count == 1);
+ }
+ }
+ }
+
+ /* should now be set to MyISAM storage engine */
+ DBUG_ASSERT(global_system_variables.table_plugin);
+
+ pthread_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);
+ if (!(flags & PLUGIN_INIT_SKIP_PLUGIN_TABLE))
+ plugin_load(&tmp_root, argc, argv);
+ }
+
+ if (flags & PLUGIN_INIT_SKIP_INITIALIZATION)
+ goto end;
+
+ /*
+ Now we initialize all remaining plugins
+ */
+
+ pthread_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_initialize(plugin_ptr))
+ {
+ plugin_ptr->state= PLUGIN_IS_DYING;
+ *(reap++)= plugin_ptr;
+ }
+ }
+ }
+
+ /*
+ Check if any plugins have to be reaped
+ */
+ while ((plugin_ptr= *(--reap)))
+ {
+ pthread_mutex_unlock(&LOCK_plugin);
+ plugin_deinitialize(plugin_ptr, true);
+ pthread_mutex_lock(&LOCK_plugin);
+ plugin_del(plugin_ptr);
+ }
+
+ pthread_mutex_unlock(&LOCK_plugin);
+ my_afree(reap);
+
+end:
+ free_root(&tmp_root, MYF(0));
+
+ DBUG_RETURN(0);
+
+err_unlock:
+ pthread_mutex_unlock(&LOCK_plugin);
+err:
+ free_root(&tmp_root, MYF(0));
+ DBUG_RETURN(1);
+}
+
+
+static bool register_builtin(struct st_mysql_plugin *plugin,
+ struct st_plugin_int *tmp,
+ struct st_plugin_int **ptr)
+{
+ DBUG_ENTER("register_builtin");
+ tmp->ref_count= 0;
+ tmp->plugin_dl= 0;
+
+ if (insert_dynamic(&plugin_array, (uchar*)&tmp))
+ DBUG_RETURN(1);
+
+ *ptr= *dynamic_element(&plugin_array, plugin_array.elements - 1,
+ struct st_plugin_int **)=
+ (struct st_plugin_int *) memdup_root(&plugin_mem_root, (uchar*)tmp,
+ sizeof(struct st_plugin_int));
+
+ if (my_hash_insert(&plugin_hash[plugin->type],(uchar*) *ptr))
+ DBUG_RETURN(1);
+
+ DBUG_RETURN(0);
+}
+
+
+#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_mysql_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, true))
+ 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)
+{
+ TABLE_LIST tables;
+ TABLE *table;
+ READ_RECORD read_record_info;
+ int error;
+ THD *new_thd;
+#ifdef EMBEDDED_LIBRARY
+ bool table_exists;
+#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;
+
+#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)
+ goto end;
+#endif /* EMBEDDED_LIBRARY */
+
+ if (simple_open_n_lock_tables(new_thd, &tables))
+ {
+ DBUG_PRINT("error",("Can't open plugin table"));
+ sql_print_error("Can't open the mysql.plugin table. Please "
+ "run mysql_upgrade to create it.");
+ goto end;
+ }
+ table= tables.table;
+ init_read_record(&read_record_info, new_thd, table, NULL, 1, 0, FALSE);
+ table->use_all_columns();
+ /*
+ 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
+ the mutex here to satisfy the assert
+ */
+ pthread_mutex_lock(&LOCK_plugin);
+ while (!(error= read_record_info.read_record(&read_record_info)))
+ {
+ DBUG_PRINT("info", ("init plugin record"));
+ String str_name, str_dl;
+ get_field(tmp_root, table->field[0], &str_name);
+ get_field(tmp_root, table->field[1], &str_dl);
+
+ LEX_STRING name= {(char *)str_name.ptr(), str_name.length()};
+ LEX_STRING dl= {(char *)str_dl.ptr(), str_dl.length()};
+
+ if (plugin_add(tmp_root, &name, &dl, argc, argv, 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);
+ 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
+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;
+}
+
+
+/*
+ called only by plugin_init()
+*/
+static bool plugin_load_list(MEM_ROOT *tmp_root, int *argc, char **argv,
+ 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_mysql_plugin *plugin;
+ char *p= buffer;
+ DBUG_ENTER("plugin_load_list");
+ while (list)
+ {
+ if (p == buffer + sizeof(buffer) - 1)
+ {
+ sql_print_error("plugin-load parameter too long");
+ DBUG_RETURN(TRUE);
+ }
+
+ switch ((*(p++)= *(list++))) {
+ case '\0':
+ list= NULL; /* terminate the loop */
+ /* fall through */
+#ifndef __WIN__
+ case ':': /* can't use this as delimiter as it may be drive letter */
+#endif
+ case ';':
+ str->str[str->length]= '\0';
+ if (str == &name) // load all plugins in named module
+ {
+ if (!name.length)
+ {
+ p--; /* reset pointer */
+ continue;
+ }
+
+ dl= name;
+ pthread_mutex_lock(&LOCK_plugin);
+ if ((plugin_dl= plugin_dl_add(&dl, REPORT_TO_LOG)))
+ {
+ for (plugin= plugin_dl->plugins; plugin->info; plugin++)
+ {
+ 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
+ }
+ }
+ else
+ {
+ free_root(tmp_root, MYF(MY_MARK_BLOCKS_FREE));
+ pthread_mutex_lock(&LOCK_plugin);
+ if (plugin_add(tmp_root, &name, &dl, argc, argv, REPORT_TO_LOG))
+ goto error;
+ }
+ pthread_mutex_unlock(&LOCK_plugin);
+ name.length= dl.length= 0;
+ dl.str= NULL; name.str= p= buffer;
+ str= &name;
+ continue;
+ case '=':
+ case '#':
+ if (str == &name)
+ {
+ name.str[name.length]= '\0';
+ str= &dl;
+ str->str= p;
+ continue;
+ }
+ default:
+ str->length++;
+ continue;
+ }
+ }
+ DBUG_RETURN(FALSE);
+error:
+ pthread_mutex_unlock(&LOCK_plugin);
+ sql_print_error("Couldn't load plugin named '%s' with soname '%s'.",
+ name.str, dl.str);
+ DBUG_RETURN(TRUE);
+}
+
+
+void plugin_shutdown(void)
+{
+ uint i, count= plugin_array.elements, free_slots= 0;
+ struct st_plugin_int **plugins, *plugin;
+ struct st_plugin_dl **dl;
+ DBUG_ENTER("plugin_shutdown");
+
+ if (initialized)
+ {
+ pthread_mutex_lock(&LOCK_plugin);
+
+ reap_needed= true;
+
+ /*
+ We want to shut down plugins in a reasonable order, this will
+ become important when we have plugins which depend upon each other.
+ Circular references cannot be reaped so they are forced afterwards.
+ TODO: Have an additional step here to notify all active plugins that
+ shutdown is requested to allow plugins to deinitialize in parallel.
+ */
+ while (reap_needed && (count= plugin_array.elements))
+ {
+ reap_plugins();
+ for (i= free_slots= 0; i < count; i++)
+ {
+ plugin= *dynamic_element(&plugin_array, i, struct st_plugin_int **);
+ switch (plugin->state) {
+ case PLUGIN_IS_READY:
+ plugin->state= PLUGIN_IS_DELETED;
+ reap_needed= true;
+ break;
+ case PLUGIN_IS_FREED:
+ case PLUGIN_IS_UNINITIALIZED:
+ free_slots++;
+ break;
+ }
+ }
+ if (!reap_needed)
+ {
+ /*
+ release any plugin references held.
+ */
+ unlock_variables(NULL, &global_system_variables);
+ unlock_variables(NULL, &max_system_variables);
+ }
+ }
+
+ if (count > free_slots)
+ sql_print_warning("Forcing shutdown of %d plugins", count - free_slots);
+
+ plugins= (struct st_plugin_int **) my_alloca(sizeof(void*) * (count+1));
+
+ /*
+ If we have any plugins which did not die cleanly, we force shutdown
+ */
+ for (i= 0; i < count; i++)
+ {
+ plugins[i]= *dynamic_element(&plugin_array, i, struct st_plugin_int **);
+ /* change the state to ensure no reaping races */
+ if (plugins[i]->state == PLUGIN_IS_DELETED)
+ plugins[i]->state= PLUGIN_IS_DYING;
+ }
+ pthread_mutex_unlock(&LOCK_plugin);
+
+ /*
+ We loop through all plugins and call deinit() if they have one.
+ */
+ for (i= 0; i < count; i++)
+ if (!(plugins[i]->state & (PLUGIN_IS_UNINITIALIZED | PLUGIN_IS_FREED |
+ PLUGIN_IS_DISABLED)))
+ {
+ sql_print_information("Plugin '%s' will be forced to shutdown",
+ plugins[i]->name.str);
+ /*
+ We are forcing deinit on plugins so we don't want to do a ref_count
+ check until we have processed all the plugins.
+ */
+ plugin_deinitialize(plugins[i], false);
+ }
+
+ /*
+ 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
+ */
+ pthread_mutex_lock(&LOCK_plugin);
+
+ /*
+ We defer checking ref_counts until after all plugins are deinitialized
+ as some may have worker threads holding on to plugin references.
+ */
+ for (i= 0; i < count; i++)
+ {
+ 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)
+ plugin_del(plugins[i]);
+ }
+
+ /*
+ Now we can deallocate all memory.
+ */
+
+ cleanup_variables(NULL, &global_system_variables);
+ cleanup_variables(NULL, &max_system_variables);
+ pthread_mutex_unlock(&LOCK_plugin);
+
+ initialized= 0;
+ pthread_mutex_destroy(&LOCK_plugin);
+
+ my_afree(plugins);
+ }
+
+ /* Dispose of the memory */
+
+ for (i= 0; i < MYSQL_MAX_PLUGIN_TYPE_NUM; i++)
+ hash_free(&plugin_hash[i]);
+ delete_dynamic(&plugin_array);
+
+ count= plugin_dl_array.elements;
+ dl= (struct st_plugin_dl **)my_alloca(sizeof(void*) * count);
+ for (i= 0; i < count; i++)
+ dl[i]= *dynamic_element(&plugin_dl_array, i, struct st_plugin_dl **);
+ for (i= 0; i < plugin_dl_array.elements; i++)
+ free_plugin_mem(dl[i]);
+ my_afree(dl);
+ delete_dynamic(&plugin_dl_array);
+
+ hash_free(&bookmark_hash);
+ free_root(&plugin_mem_root, MYF(0));
+
+ global_variables_dynamic_size= 0;
+
+ DBUG_VOID_RETURN;
+}
+
+
+bool mysql_install_plugin(THD *thd, const LEX_STRING *name, const LEX_STRING *dl)
+{
+ TABLE_LIST tables;
+ TABLE *table;
+ int error, argc;
+ char *argv[2];
+ struct st_plugin_int *tmp;
+ DBUG_ENTER("mysql_install_plugin");
+
+ 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);
+ /* handle_options() assumes arg0 (program name) always exists */
+ argv[0]= const_cast<char*>(""); // without a cast gcc emits a warning
+ argv[1]= 0;
+ argc= 1;
+ error= plugin_add(thd->mem_root, name, dl, &argc, argv, REPORT_TO_USER);
+ rw_unlock(&LOCK_system_variables_hash);
+
+ if (error || !(tmp= plugin_find_internal(name, MYSQL_ANY_PLUGIN)))
+ goto err;
+
+ 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
+ of the insert into the plugin table, so that it is not replicated in
+ row based mode.
+ */
+ tmp_disable_binlog(thd);
+ 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);
+ error= table->file->ha_write_row(table->record[0]);
+ reenable_binlog(thd);
+ if (error)
+ {
+ table->file->print_error(error, MYF(0));
+ goto deinit;
+ }
+
+ 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);
+}
+
+
+bool mysql_uninstall_plugin(THD *thd, const LEX_STRING *name)
+{
+ TABLE *table;
+ TABLE_LIST tables;
+ struct st_plugin_int *plugin;
+ DBUG_ENTER("mysql_uninstall_plugin");
+
+ bzero(&tables, sizeof(tables));
+ tables.db= (char *)"mysql";
+ tables.table_name= tables.alias= (char *)"plugin";
+
+ /* 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);
+ if (!(plugin= plugin_find_internal(name, MYSQL_ANY_PLUGIN)))
+ {
+ my_error(ER_SP_DOES_NOT_EXIST, MYF(0), "PLUGIN", name->str);
+ goto err;
+ }
+ if (!plugin->plugin_dl)
+ {
+ 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;
+ }
+
+ plugin->state= PLUGIN_IS_DELETED;
+ if (plugin->ref_count)
+ push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ WARN_PLUGIN_BUSY, ER(WARN_PLUGIN_BUSY));
+ else
+ reap_needed= true;
+ reap_plugins();
+ pthread_mutex_unlock(&LOCK_plugin);
+
+ table->use_all_columns();
+ table->field[0]->store(name->str, name->length, system_charset_info);
+ if (! table->file->index_read_idx_map(table->record[0], 0,
+ (uchar *)table->field[0]->ptr,
+ HA_WHOLE_KEY,
+ HA_READ_KEY_EXACT))
+ {
+ int error;
+ /*
+ We do not replicate the UNINSTALL PLUGIN statement. Disable binlogging
+ of the delete from the plugin table, so that it is not replicated in
+ row based mode.
+ */
+ tmp_disable_binlog(thd);
+ error= table->file->ha_delete_row(table->record[0]);
+ reenable_binlog(thd);
+ if (error)
+ {
+ table->file->print_error(error, MYF(0));
+ DBUG_RETURN(TRUE);
+ }
+ }
+ DBUG_RETURN(FALSE);
+err:
+ pthread_mutex_unlock(&LOCK_plugin);
+ DBUG_RETURN(TRUE);
+}
+
+
+bool plugin_foreach_with_mask(THD *thd, plugin_foreach_func *func,
+ int type, uint state_mask, void *arg)
+{
+ uint idx, total;
+ struct st_plugin_int *plugin, **plugins;
+ int version=plugin_array_version;
+ DBUG_ENTER("plugin_foreach_with_mask");
+
+ if (!initialized)
+ DBUG_RETURN(FALSE);
+
+ state_mask= ~state_mask; // do it only once
+
+ pthread_mutex_lock(&LOCK_plugin);
+ total= type == MYSQL_ANY_PLUGIN ? plugin_array.elements
+ : plugin_hash[type].records;
+ /*
+ Do the alloca out here in case we do have a working alloca:
+ leaving the nested stack frame invalidates alloca allocation.
+ */
+ plugins=(struct st_plugin_int **)my_alloca(total*sizeof(plugin));
+ if (type == MYSQL_ANY_PLUGIN)
+ {
+ for (idx= 0; idx < total; idx++)
+ {
+ plugin= *dynamic_element(&plugin_array, idx, struct st_plugin_int **);
+ plugins[idx]= !(plugin->state & state_mask) ? plugin : NULL;
+ }
+ }
+ else
+ {
+ HASH *hash= plugin_hash + type;
+ for (idx= 0; idx < total; idx++)
+ {
+ plugin= (struct st_plugin_int *) hash_element(hash, idx);
+ plugins[idx]= !(plugin->state & state_mask) ? plugin : NULL;
+ }
+ }
+ pthread_mutex_unlock(&LOCK_plugin);
+
+ for (idx= 0; idx < total; idx++)
+ {
+ if (unlikely(version != plugin_array_version))
+ {
+ pthread_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);
+ }
+ plugin= plugins[idx];
+ /* It will stop iterating on first engine error when "func" returns TRUE */
+ if (plugin && func(thd, plugin_int_to_ref(plugin), arg))
+ goto err;
+ }
+
+ my_afree(plugins);
+ DBUG_RETURN(FALSE);
+err:
+ my_afree(plugins);
+ DBUG_RETURN(TRUE);
+}
+
+
+/****************************************************************************
+ Internal type declarations for variables support
+****************************************************************************/
+
+#undef MYSQL_SYSVAR_NAME
+#define MYSQL_SYSVAR_NAME(name) name
+#define PLUGIN_VAR_TYPEMASK 0x007f
+
+#define EXTRA_OPTIONS 3 /* options for: 'foo', 'plugin-foo' and NULL */
+
+typedef DECLARE_MYSQL_SYSVAR_BASIC(sysvar_bool_t, my_bool);
+typedef DECLARE_MYSQL_THDVAR_BASIC(thdvar_bool_t, my_bool);
+typedef DECLARE_MYSQL_SYSVAR_BASIC(sysvar_str_t, char *);
+typedef DECLARE_MYSQL_THDVAR_BASIC(thdvar_str_t, char *);
+
+typedef DECLARE_MYSQL_SYSVAR_TYPELIB(sysvar_enum_t, unsigned long);
+typedef DECLARE_MYSQL_THDVAR_TYPELIB(thdvar_enum_t, unsigned long);
+typedef DECLARE_MYSQL_SYSVAR_TYPELIB(sysvar_set_t, ulonglong);
+typedef DECLARE_MYSQL_THDVAR_TYPELIB(thdvar_set_t, ulonglong);
+
+typedef DECLARE_MYSQL_SYSVAR_SIMPLE(sysvar_int_t, int);
+typedef DECLARE_MYSQL_SYSVAR_SIMPLE(sysvar_long_t, long);
+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_THDVAR_SIMPLE(thdvar_int_t, int);
+typedef DECLARE_MYSQL_THDVAR_SIMPLE(thdvar_long_t, long);
+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);
+
+#define SET_PLUGIN_VAR_RESOLVE(opt)\
+ *(mysql_sys_var_ptr_p*)&((opt)->resolve)= mysql_sys_var_ptr
+typedef uchar *(*mysql_sys_var_ptr_p)(void* a_thd, int offset);
+
+
+/****************************************************************************
+ default variable data check and update functions
+****************************************************************************/
+
+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;
+ int result, length;
+ long long tmp;
+
+ if (value->value_type(value) == MYSQL_VALUE_TYPE_STRING)
+ {
+ 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 > 1)
+ {
+ llstr(tmp, buff);
+ strvalue= buff;
+ goto err;
+ }
+ result= (int) tmp;
+ }
+ *(my_bool *) save= -result;
+ return 0;
+err:
+ my_error(ER_WRONG_VALUE_FOR_VAR, MYF(0), var->name, strvalue);
+ return 1;
+}
+
+
+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;
+ struct my_option options;
+ value->val_int(value, &tmp);
+ plugin_opt_set_limits(&options, var);
+
+ if (var->flags & PLUGIN_VAR_UNSIGNED)
+ *(uint *)save= (uint) getopt_ull_limit_value((ulonglong) tmp, &options,
+ &fixed);
+ else
+ *(int *)save= (int) getopt_ll_limit_value(tmp, &options, &fixed);
+
+ return throw_bounds_warning(thd, fixed, var->flags & PLUGIN_VAR_UNSIGNED,
+ var->name, (longlong) tmp);
+}
+
+
+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;
+ struct my_option options;
+ value->val_int(value, &tmp);
+ plugin_opt_set_limits(&options, var);
+
+ if (var->flags & PLUGIN_VAR_UNSIGNED)
+ *(ulong *)save= (ulong) getopt_ull_limit_value((ulonglong) tmp, &options,
+ &fixed);
+ else
+ *(long *)save= (long) getopt_ll_limit_value(tmp, &options, &fixed);
+
+ return throw_bounds_warning(thd, fixed, var->flags & PLUGIN_VAR_UNSIGNED,
+ var->name, (longlong) tmp);
+}
+
+
+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;
+ struct my_option options;
+ value->val_int(value, &tmp);
+ plugin_opt_set_limits(&options, var);
+
+ if (var->flags & PLUGIN_VAR_UNSIGNED)
+ *(ulonglong *)save= getopt_ull_limit_value((ulonglong) tmp, &options,
+ &fixed);
+ else
+ *(longlong *)save= getopt_ll_limit_value(tmp, &options, &fixed);
+
+ return throw_bounds_warning(thd, fixed, var->flags & PLUGIN_VAR_UNSIGNED,
+ var->name, (longlong) tmp);
+}
+
+static int check_func_str(THD *thd, struct st_mysql_sys_var *var,
+ void *save, st_mysql_value *value)
+{
+ char buff[STRING_BUFFER_USUAL_SIZE];
+ const char *str;
+ int length;
+
+ length= sizeof(buff);
+ if ((str= value->val_str(value, buff, &length)))
+ str= thd->strmake(str, length);
+ *(const char**)save= str;
+ return 0;
+}
+
+
+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;
+ TYPELIB *typelib;
+ long long tmp;
+ long result;
+ int length;
+
+ if (var->flags & PLUGIN_VAR_THDLOCAL)
+ typelib= ((thdvar_enum_t*) var)->typelib;
+ else
+ typelib= ((sysvar_enum_t*) var)->typelib;
+
+ if (value->value_type(value) == MYSQL_VALUE_TYPE_STRING)
+ {
+ 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;
+ goto err;
+ }
+ }
+ else
+ {
+ if (value->val_int(value, &tmp))
+ goto err;
+ if (tmp >= typelib->count)
+ {
+ llstr(tmp, buff);
+ strvalue= buff;
+ 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;
+}
+
+
+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;
+ TYPELIB *typelib;
+ ulonglong result;
+ uint error_len;
+ bool not_used;
+ int length;
+
+ if (var->flags & PLUGIN_VAR_THDLOCAL)
+ typelib= ((thdvar_set_t*) var)->typelib;
+ else
+ typelib= ((sysvar_set_t*)var)->typelib;
+
+ if (value->value_type(value) == MYSQL_VALUE_TYPE_STRING)
+ {
+ length= sizeof(buff);
+ if (!(str= value->val_str(value, buff, &length)))
+ goto err;
+ result= find_set(typelib, str, length, NULL,
+ &error, &error_len, &not_used);
+ if (error_len)
+ {
+ strmake(buff, error, min(sizeof(buff), error_len));
+ strvalue= buff;
+ goto err;
+ }
+ }
+ else
+ {
+ if (value->val_int(value, (long long *)&result))
+ goto err;
+ if (unlikely((result >= (ULL(1) << 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 void update_func_bool(THD *thd, struct st_mysql_sys_var *var,
+ void *tgt, const void *save)
+{
+ *(my_bool *) tgt= *(my_bool *) save ? TRUE : FALSE;
+}
+
+
+static void update_func_int(THD *thd, struct st_mysql_sys_var *var,
+ void *tgt, const void *save)
+{
+ *(int *)tgt= *(int *) save;
+}
+
+
+static void update_func_long(THD *thd, struct st_mysql_sys_var *var,
+ void *tgt, const void *save)
+{
+ *(long *)tgt= *(long *) save;
+}
+
+
+static void update_func_longlong(THD *thd, struct st_mysql_sys_var *var,
+ void *tgt, const void *save)
+{
+ *(longlong *)tgt= *(ulonglong *) save;
+}
+
+
+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;
+ if (var->flags & PLUGIN_VAR_MEMALLOC)
+ {
+ *(char **)tgt= my_strdup(*(char **) save, MYF(0));
+ my_free(old, MYF(0));
+ }
+}
+
+
+/****************************************************************************
+ System Variables support
+****************************************************************************/
+
+
+sys_var *find_sys_var(THD *thd, const char *str, uint length)
+{
+ sys_var *var;
+ sys_var_pluginvar *pi= NULL;
+ 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)) &&
+ (pi= var->cast_pluginvar()))
+ {
+ rw_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 */
+ else
+ if (!(plugin_state(plugin) & PLUGIN_IS_READY))
+ {
+ /* initialization not completed */
+ var= NULL;
+ intern_plugin_unlock(lex, plugin);
+ }
+ }
+ else
+ rw_unlock(&LOCK_system_variables_hash);
+ pthread_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)
+ my_error(ER_UNKNOWN_SYSTEM_VARIABLE, MYF(0), (char*) str);
+ DBUG_RETURN(var);
+}
+
+
+/*
+ called by register_var, construct_options and test_plugin_options.
+ Returns the 'bookmark' for the named variable.
+ LOCK_system_variables_hash should be at least read locked
+*/
+static st_bookmark *find_bookmark(const char *plugin, const char *name,
+ int flags)
+{
+ st_bookmark *result= NULL;
+ uint namelen, length, pluginlen= 0;
+ char *varname, *p;
+
+ if (!(flags & PLUGIN_VAR_THDLOCAL))
+ return NULL;
+
+ namelen= strlen(name);
+ if (plugin)
+ pluginlen= strlen(plugin) + 1;
+ length= namelen + pluginlen + 2;
+ varname= (char*) my_alloca(length);
+
+ if (plugin)
+ {
+ strxmov(varname + 1, plugin, "_", name, NullS);
+ for (p= varname + 1; *p; p++)
+ if (*p == '-')
+ *p= '_';
+ }
+ else
+ memcpy(varname + 1, name, namelen + 1);
+
+ varname[0]= flags & PLUGIN_VAR_TYPEMASK;
+
+ result= (st_bookmark*) hash_search(&bookmark_hash,
+ (const uchar*) varname, length - 1);
+
+ my_afree(varname);
+ return result;
+}
+
+
+/*
+ returns a bookmark for thd-local variables, creating if neccessary.
+ returns null for non thd-local variables.
+ Requires that a write lock is obtained on LOCK_system_variables_hash
+*/
+static st_bookmark *register_var(const char *plugin, const char *name,
+ int flags)
+{
+ uint length= strlen(plugin) + strlen(name) + 3, size= 0, offset, new_size;
+ st_bookmark *result;
+ char *varname, *p;
+
+ if (!(flags & PLUGIN_VAR_THDLOCAL))
+ return NULL;
+
+ switch (flags & PLUGIN_VAR_TYPEMASK) {
+ case PLUGIN_VAR_BOOL:
+ size= sizeof(my_bool);
+ break;
+ case PLUGIN_VAR_INT:
+ size= sizeof(int);
+ break;
+ case PLUGIN_VAR_LONG:
+ case PLUGIN_VAR_ENUM:
+ size= sizeof(long);
+ break;
+ case PLUGIN_VAR_LONGLONG:
+ case PLUGIN_VAR_SET:
+ size= sizeof(ulonglong);
+ break;
+ case PLUGIN_VAR_STR:
+ size= sizeof(char*);
+ break;
+ default:
+ DBUG_ASSERT(0);
+ return NULL;
+ };
+
+ varname= ((char*) my_alloca(length));
+ strxmov(varname + 1, plugin, "_", name, NullS);
+ for (p= varname + 1; *p; p++)
+ if (*p == '-')
+ *p= '_';
+
+ if (!(result= find_bookmark(NULL, varname + 1, flags)))
+ {
+ result= (st_bookmark*) alloc_root(&plugin_mem_root,
+ sizeof(struct st_bookmark) + length-1);
+ varname[0]= flags & PLUGIN_VAR_TYPEMASK;
+ memcpy(result->key, varname, length);
+ result->name_len= length - 2;
+ result->offset= -1;
+
+ DBUG_ASSERT(size && !(size & (size-1))); /* must be power of 2 */
+
+ offset= global_system_variables.dynamic_variables_size;
+ offset= (offset + size - 1) & ~(size - 1);
+ result->offset= (int) offset;
+
+ new_size= (offset + size + 63) & ~63;
+
+ if (new_size > global_variables_dynamic_size)
+ {
+ global_system_variables.dynamic_variables_ptr= (char*)
+ my_realloc(global_system_variables.dynamic_variables_ptr, new_size,
+ MYF(MY_WME | MY_FAE | MY_ALLOW_ZERO_PTR));
+ max_system_variables.dynamic_variables_ptr= (char*)
+ my_realloc(max_system_variables.dynamic_variables_ptr, new_size,
+ MYF(MY_WME | MY_FAE | MY_ALLOW_ZERO_PTR));
+ /*
+ Clear the new variable value space. This is required for string
+ variables. If their value is non-NULL, it must point to a valid
+ string.
+ */
+ bzero(global_system_variables.dynamic_variables_ptr +
+ global_variables_dynamic_size,
+ new_size - global_variables_dynamic_size);
+ bzero(max_system_variables.dynamic_variables_ptr +
+ global_variables_dynamic_size,
+ new_size - global_variables_dynamic_size);
+ global_variables_dynamic_size= new_size;
+ }
+
+ global_system_variables.dynamic_variables_head= offset;
+ max_system_variables.dynamic_variables_head= offset;
+ global_system_variables.dynamic_variables_size= offset + size;
+ max_system_variables.dynamic_variables_size= offset + size;
+ global_system_variables.dynamic_variables_version++;
+ max_system_variables.dynamic_variables_version++;
+
+ result->version= global_system_variables.dynamic_variables_version;
+
+ /* this should succeed because we have already checked if a dup exists */
+ if (my_hash_insert(&bookmark_hash, (uchar*) result))
+ {
+ fprintf(stderr, "failed to add placeholder to hash");
+ DBUG_ASSERT(0);
+ }
+ }
+ my_afree(varname);
+ return result;
+}
+
+
+/*
+ returns a pointer to the memory which holds the thd-local variable or
+ a pointer to the global variable if thd==null.
+ If required, will sync with global variables if the requested variable
+ has not yet been allocated in the current thread.
+*/
+static uchar *intern_sys_var_ptr(THD* thd, int offset, bool global_lock)
+{
+ 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;
+
+ /*
+ dynamic_variables_head points to the largest valid offset
+ */
+ if (!thd->variables.dynamic_variables_ptr ||
+ (uint)offset > thd->variables.dynamic_variables_head)
+ {
+ uint idx;
+
+ rw_rdlock(&LOCK_system_variables_hash);
+
+ thd->variables.dynamic_variables_ptr= (char*)
+ my_realloc(thd->variables.dynamic_variables_ptr,
+ global_variables_dynamic_size,
+ MYF(MY_WME | MY_FAE | MY_ALLOW_ZERO_PTR));
+
+ if (global_lock)
+ pthread_mutex_lock(&LOCK_global_system_variables);
+
+ safe_mutex_assert_owner(&LOCK_global_system_variables);
+
+ memcpy(thd->variables.dynamic_variables_ptr +
+ thd->variables.dynamic_variables_size,
+ global_system_variables.dynamic_variables_ptr +
+ thd->variables.dynamic_variables_size,
+ global_system_variables.dynamic_variables_size -
+ thd->variables.dynamic_variables_size);
+
+ /*
+ now we need to iterate through any newly copied 'defaults'
+ and if it is a string type with MEMALLOC flag, we need to strdup
+ */
+ for (idx= 0; idx < bookmark_hash.records; idx++)
+ {
+ sys_var_pluginvar *pi;
+ sys_var *var;
+ st_bookmark *v= (st_bookmark*) 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)) ||
+ !(pi= var->cast_pluginvar()) ||
+ v->key[0] != (pi->plugin_var->flags & PLUGIN_VAR_TYPEMASK))
+ continue;
+
+ /* Here we do anything special that may be required of the data types */
+
+ if ((pi->plugin_var->flags & PLUGIN_VAR_TYPEMASK) == PLUGIN_VAR_STR &&
+ pi->plugin_var->flags & PLUGIN_VAR_MEMALLOC)
+ {
+ char **pp= (char**) (thd->variables.dynamic_variables_ptr +
+ *(int*)(pi->plugin_var + 1));
+ if ((*pp= *(char**) (global_system_variables.dynamic_variables_ptr +
+ *(int*)(pi->plugin_var + 1))))
+ *pp= my_strdup(*pp, MYF(MY_WME|MY_FAE));
+ }
+ }
+
+ if (global_lock)
+ pthread_mutex_unlock(&LOCK_global_system_variables);
+
+ thd->variables.dynamic_variables_version=
+ global_system_variables.dynamic_variables_version;
+ thd->variables.dynamic_variables_head=
+ global_system_variables.dynamic_variables_head;
+ thd->variables.dynamic_variables_size=
+ global_system_variables.dynamic_variables_size;
+
+ rw_unlock(&LOCK_system_variables_hash);
+ }
+ return (uchar*)thd->variables.dynamic_variables_ptr + offset;
+}
+
+static uchar *mysql_sys_var_ptr(void* a_thd, int offset)
+{
+ return intern_sys_var_ptr((THD *)a_thd, offset, true);
+}
+
+
+void plugin_thdvar_init(THD *thd)
+{
+ plugin_ref old_table_plugin= thd->variables.table_plugin;
+ DBUG_ENTER("plugin_thdvar_init");
+
+ thd->variables.table_plugin= NULL;
+ cleanup_variables(thd, &thd->variables);
+
+ thd->variables= global_system_variables;
+ thd->variables.table_plugin= NULL;
+
+ /* we are going to allocate these lazily */
+ thd->variables.dynamic_variables_version= 0;
+ thd->variables.dynamic_variables_size= 0;
+ thd->variables.dynamic_variables_ptr= 0;
+
+ pthread_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);
+ DBUG_VOID_RETURN;
+}
+
+
+/*
+ Unlocks all system variables which hold a reference
+*/
+static void unlock_variables(THD *thd, struct system_variables *vars)
+{
+ intern_plugin_unlock(NULL, vars->table_plugin);
+ vars->table_plugin= NULL;
+}
+
+
+/*
+ Frees memory used by system variables
+
+ Unlike plugin_vars_free_values() it frees all variables of all plugins,
+ it's used on shutdown.
+*/
+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);
+ 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;
+
+ flags= pivar->plugin_var->flags;
+
+ if ((flags & PLUGIN_VAR_TYPEMASK) == PLUGIN_VAR_STR &&
+ flags & PLUGIN_VAR_THDLOCAL && flags & PLUGIN_VAR_MEMALLOC)
+ {
+ char **ptr= (char**) pivar->real_value_ptr(thd, OPT_SESSION);
+ my_free(*ptr, MYF(MY_WME | MY_FAE | MY_ALLOW_ZERO_PTR));
+ *ptr= NULL;
+ }
+ }
+ rw_unlock(&LOCK_system_variables_hash);
+
+ DBUG_ASSERT(vars->table_plugin == NULL);
+
+ my_free(vars->dynamic_variables_ptr, MYF(MY_ALLOW_ZERO_PTR));
+ vars->dynamic_variables_ptr= NULL;
+ vars->dynamic_variables_size= 0;
+ vars->dynamic_variables_version= 0;
+}
+
+
+void plugin_thdvar_cleanup(THD *thd)
+{
+ uint idx;
+ plugin_ref *list;
+ DBUG_ENTER("plugin_thdvar_cleanup");
+
+ pthread_mutex_lock(&LOCK_plugin);
+
+ unlock_variables(thd, &thd->variables);
+ cleanup_variables(thd, &thd->variables);
+
+ if ((idx= thd->lex->plugins.elements))
+ {
+ list= ((plugin_ref*) thd->lex->plugins.buffer) + idx - 1;
+ DBUG_PRINT("info",("unlocking %d plugins", idx));
+ while ((uchar*) list >= thd->lex->plugins.buffer)
+ intern_plugin_unlock(NULL, *list--);
+ }
+
+ reap_plugins();
+ pthread_mutex_unlock(&LOCK_plugin);
+
+ reset_dynamic(&thd->lex->plugins);
+
+ DBUG_VOID_RETURN;
+}
+
+
+/**
+ @brief Free values of thread variables of a plugin.
+
+ This must be called before a plugin is deleted. Otherwise its
+ variables are no longer accessible and the value space is lost. Note
+ that only string values with PLUGIN_VAR_MEMALLOC are allocated and
+ must be freed.
+
+ @param[in] vars Chain of system variables of a plugin
+*/
+
+static void plugin_vars_free_values(sys_var *vars)
+{
+ DBUG_ENTER("plugin_vars_free_values");
+
+ for (sys_var *var= vars; var; var= var->next)
+ {
+ sys_var_pluginvar *piv= var->cast_pluginvar();
+ if (piv &&
+ ((piv->plugin_var->flags & PLUGIN_VAR_TYPEMASK) == PLUGIN_VAR_STR) &&
+ (piv->plugin_var->flags & PLUGIN_VAR_MEMALLOC))
+ {
+ /* 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));
+ *valptr= NULL;
+ }
+ }
+ DBUG_VOID_RETURN;
+}
+
+
+bool sys_var_pluginvar::check_update_type(Item_result type)
+{
+ if (is_readonly())
+ return 1;
+ switch (plugin_var->flags & PLUGIN_VAR_TYPEMASK) {
+ case PLUGIN_VAR_INT:
+ case PLUGIN_VAR_LONG:
+ case PLUGIN_VAR_LONGLONG:
+ return type != INT_RESULT;
+ case PLUGIN_VAR_STR:
+ return type != STRING_RESULT;
+ default:
+ return 0;
+ }
+}
+
+
+SHOW_TYPE sys_var_pluginvar::show_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;
+ case PLUGIN_VAR_STR:
+ return SHOW_CHAR_PTR;
+ case PLUGIN_VAR_ENUM:
+ case PLUGIN_VAR_SET:
+ return SHOW_CHAR;
+ default:
+ DBUG_ASSERT(0);
+ return SHOW_UNDEF;
+ }
+}
+
+
+uchar* sys_var_pluginvar::real_value_ptr(THD *thd, enum_var_type type)
+{
+ DBUG_ASSERT(thd || (type == OPT_GLOBAL));
+ if (plugin_var->flags & PLUGIN_VAR_THDLOCAL)
+ {
+ if (type == OPT_GLOBAL)
+ thd= NULL;
+
+ return intern_sys_var_ptr(thd, *(int*) (plugin_var+1), false);
+ }
+ return *(uchar**) (plugin_var+1);
+}
+
+
+TYPELIB* sys_var_pluginvar::plugin_var_typelib(void)
+{
+ switch (plugin_var->flags & (PLUGIN_VAR_TYPEMASK | PLUGIN_VAR_THDLOCAL)) {
+ case PLUGIN_VAR_ENUM:
+ return ((sysvar_enum_t *)plugin_var)->typelib;
+ case PLUGIN_VAR_SET:
+ return ((sysvar_set_t *)plugin_var)->typelib;
+ case PLUGIN_VAR_ENUM | PLUGIN_VAR_THDLOCAL:
+ return ((thdvar_enum_t *)plugin_var)->typelib;
+ case PLUGIN_VAR_SET | PLUGIN_VAR_THDLOCAL:
+ return ((thdvar_set_t *)plugin_var)->typelib;
+ default:
+ return NULL;
+ }
+ return NULL;
+}
+
+
+uchar* sys_var_pluginvar::value_ptr(THD *thd, enum_var_type type,
+ LEX_STRING *base)
+{
+ uchar* result;
+
+ result= real_value_ptr(thd, 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[i]);
+ str.append(',');
+ }
+
+ result= (uchar*) "";
+ if (str.length())
+ result= (uchar*) thd->strmake(str.ptr(), str.length()-1);
+ }
+ return result;
+}
+
+
+bool sys_var_pluginvar::check(THD *thd, set_var *var)
+{
+ st_item_value_holder value;
+ DBUG_ASSERT(is_readonly() || 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.item= var->value;
+
+ return is_readonly() ||
+ plugin_var->check(thd, plugin_var, &var->save_result, &value);
+}
+
+
+void sys_var_pluginvar::set_default(THD *thd, enum_var_type type)
+{
+ const void *src;
+ void *tgt;
+
+ DBUG_ASSERT(is_readonly() || plugin_var->update);
+
+ if (is_readonly())
+ return;
+
+ 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);
+ }
+}
+
+
+bool sys_var_pluginvar::update(THD *thd, set_var *var)
+{
+ void *tgt;
+
+ DBUG_ASSERT(is_readonly() || plugin_var->update);
+
+ /* 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;
+
+ 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)
+ {
+ /* 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);
+ }
+ return 0;
+}
+
+
+#define OPTION_SET_LIMITS(type, options, opt) \
+ options->var_type= type; \
+ options->def_value= (opt)->def_val; \
+ options->min_value= (opt)->min_val; \
+ options->max_value= (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)
+{
+ options->sub_size= 0;
+
+ switch (opt->flags & (PLUGIN_VAR_TYPEMASK |
+ PLUGIN_VAR_UNSIGNED | PLUGIN_VAR_THDLOCAL)) {
+ /* global system variables */
+ case PLUGIN_VAR_INT:
+ OPTION_SET_LIMITS(GET_INT, options, (sysvar_int_t*) opt);
+ break;
+ case PLUGIN_VAR_INT | PLUGIN_VAR_UNSIGNED:
+ OPTION_SET_LIMITS(GET_UINT, options, (sysvar_uint_t*) opt);
+ break;
+ case PLUGIN_VAR_LONG:
+ OPTION_SET_LIMITS(GET_LONG, options, (sysvar_long_t*) opt);
+ break;
+ case PLUGIN_VAR_LONG | PLUGIN_VAR_UNSIGNED:
+ OPTION_SET_LIMITS(GET_ULONG, options, (sysvar_ulong_t*) opt);
+ break;
+ case PLUGIN_VAR_LONGLONG:
+ OPTION_SET_LIMITS(GET_LL, options, (sysvar_longlong_t*) opt);
+ break;
+ case PLUGIN_VAR_LONGLONG | PLUGIN_VAR_UNSIGNED:
+ OPTION_SET_LIMITS(GET_ULL, options, (sysvar_ulonglong_t*) opt);
+ break;
+ case PLUGIN_VAR_ENUM:
+ options->var_type= GET_ENUM;
+ options->typelib= ((sysvar_enum_t*) opt)->typelib;
+ options->def_value= ((sysvar_enum_t*) opt)->def_val;
+ options->min_value= options->block_size= 0;
+ options->max_value= options->typelib->count - 1;
+ break;
+ case PLUGIN_VAR_SET:
+ options->var_type= GET_SET;
+ 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;
+ break;
+ case PLUGIN_VAR_BOOL:
+ options->var_type= GET_BOOL;
+ options->def_value= ((sysvar_bool_t*) opt)->def_val;
+ break;
+ case PLUGIN_VAR_STR:
+ options->var_type= ((opt->flags & PLUGIN_VAR_MEMALLOC) ?
+ GET_STR_ALLOC : GET_STR);
+ options->def_value= (intptr) ((sysvar_str_t*) opt)->def_val;
+ break;
+ /* threadlocal variables */
+ case PLUGIN_VAR_INT | PLUGIN_VAR_THDLOCAL:
+ OPTION_SET_LIMITS(GET_INT, options, (thdvar_int_t*) opt);
+ break;
+ case PLUGIN_VAR_INT | PLUGIN_VAR_UNSIGNED | PLUGIN_VAR_THDLOCAL:
+ OPTION_SET_LIMITS(GET_UINT, options, (thdvar_uint_t*) opt);
+ break;
+ case PLUGIN_VAR_LONG | PLUGIN_VAR_THDLOCAL:
+ OPTION_SET_LIMITS(GET_LONG, options, (thdvar_long_t*) opt);
+ break;
+ case PLUGIN_VAR_LONG | PLUGIN_VAR_UNSIGNED | PLUGIN_VAR_THDLOCAL:
+ OPTION_SET_LIMITS(GET_ULONG, options, (thdvar_ulong_t*) opt);
+ break;
+ case PLUGIN_VAR_LONGLONG | PLUGIN_VAR_THDLOCAL:
+ OPTION_SET_LIMITS(GET_LL, options, (thdvar_longlong_t*) opt);
+ break;
+ case PLUGIN_VAR_LONGLONG | PLUGIN_VAR_UNSIGNED | PLUGIN_VAR_THDLOCAL:
+ OPTION_SET_LIMITS(GET_ULL, options, (thdvar_ulonglong_t*) opt);
+ break;
+ case PLUGIN_VAR_ENUM | PLUGIN_VAR_THDLOCAL:
+ options->var_type= GET_ENUM;
+ options->typelib= ((thdvar_enum_t*) opt)->typelib;
+ options->def_value= ((thdvar_enum_t*) opt)->def_val;
+ options->min_value= options->block_size= 0;
+ options->max_value= options->typelib->count - 1;
+ break;
+ case PLUGIN_VAR_SET | PLUGIN_VAR_THDLOCAL:
+ options->var_type= GET_SET;
+ 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;
+ break;
+ case PLUGIN_VAR_BOOL | PLUGIN_VAR_THDLOCAL:
+ options->var_type= GET_BOOL;
+ options->def_value= ((thdvar_bool_t*) opt)->def_val;
+ break;
+ case PLUGIN_VAR_STR | PLUGIN_VAR_THDLOCAL:
+ options->var_type= ((opt->flags & PLUGIN_VAR_MEMALLOC) ?
+ GET_STR_ALLOC : GET_STR);
+ options->def_value= (intptr) ((thdvar_str_t*) opt)->def_val;
+ break;
+ default:
+ DBUG_ASSERT(0);
+ }
+ options->arg_type= REQUIRED_ARG;
+ if (opt->flags & PLUGIN_VAR_NOCMDARG)
+ options->arg_type= NO_ARG;
+ if (opt->flags & PLUGIN_VAR_OPCMDARG)
+ options->arg_type= OPT_ARG;
+}
+
+extern "C" my_bool get_one_plugin_option(int optid, const struct my_option *,
+ char *);
+
+my_bool get_one_plugin_option(int optid __attribute__((unused)),
+ const struct my_option *opt,
+ char *argument)
+{
+ return 0;
+}
+
+
+static int construct_options(MEM_ROOT *mem_root, struct st_plugin_int *tmp,
+ my_option *options, my_bool **enabled,
+ bool can_disable)
+{
+ const char *plugin_name= tmp->plugin->name;
+ uint namelen= strlen(plugin_name), optnamelen;
+ uint buffer_length= namelen * 4 + (can_disable ? 75 : 10);
+ char *name= (char*) alloc_root(mem_root, buffer_length) + 1;
+ char *optname, *p;
+ int index= 0, offset= 0;
+ st_mysql_sys_var *opt, **plugin_option;
+ st_bookmark *v;
+ DBUG_ENTER("construct_options");
+ DBUG_PRINT("plugin", ("plugin: '%s' enabled: %d can_disable: %d",
+ plugin_name, **enabled, can_disable));
+
+ /* support --skip-plugin-foo syntax */
+ memcpy(name, plugin_name, namelen + 1);
+ my_casedn_str(&my_charset_latin1, name);
+ strxmov(name + namelen + 1, "plugin-", name, NullS);
+ /* Now we have namelen + 1 + 7 + namelen + 1 == namelen * 2 + 9. */
+
+ for (p= name + namelen*2 + 8; p > name; p--)
+ if (*p == '_')
+ *p= '-';
+
+ if (can_disable)
+ {
+ strxmov(name + namelen*2 + 10, "Enable ", plugin_name, " plugin. "
+ "Disable with --skip-", name," (will save memory).", NullS);
+ /*
+ Now we have namelen * 2 + 10 (one char unused) + 7 + namelen + 9 +
+ 20 + namelen + 20 + 1 == namelen * 4 + 67.
+ */
+
+ options[0].comment= name + namelen*2 + 10;
+ }
+
+ /*
+ NOTE: 'name' is one char above the allocated buffer!
+ NOTE: This code assumes that 'my_bool' and 'char' are of same size.
+ */
+ *((my_bool *)(name -1))= **enabled;
+ *enabled= (my_bool *)(name - 1);
+
+
+ options[1].name= (options[0].name= name) + namelen + 1;
+ options[0].id= options[1].id= 256; /* must be >255. dup id ok */
+ options[0].var_type= options[1].var_type= GET_BOOL;
+ options[0].arg_type= options[1].arg_type= NO_ARG;
+ options[0].def_value= options[1].def_value= **enabled;
+ options[0].value= options[0].u_max_value=
+ options[1].value= options[1].u_max_value= (uchar**) (name - 1);
+ options+= 2;
+
+ /*
+ Two passes as the 2nd pass will take pointer addresses for use
+ by my_getopt and register_var() in the first pass uses realloc
+ */
+
+ for (plugin_option= tmp->plugin->system_vars;
+ plugin_option && *plugin_option; plugin_option++, index++)
+ {
+ opt= *plugin_option;
+ if (!(opt->flags & PLUGIN_VAR_THDLOCAL))
+ continue;
+ if (!(register_var(name, opt->name, opt->flags)))
+ continue;
+ switch (opt->flags & PLUGIN_VAR_TYPEMASK) {
+ case PLUGIN_VAR_BOOL:
+ SET_PLUGIN_VAR_RESOLVE((thdvar_bool_t *) opt);
+ break;
+ case PLUGIN_VAR_INT:
+ SET_PLUGIN_VAR_RESOLVE((thdvar_int_t *) opt);
+ break;
+ case PLUGIN_VAR_LONG:
+ SET_PLUGIN_VAR_RESOLVE((thdvar_long_t *) opt);
+ break;
+ case PLUGIN_VAR_LONGLONG:
+ SET_PLUGIN_VAR_RESOLVE((thdvar_longlong_t *) opt);
+ break;
+ case PLUGIN_VAR_STR:
+ SET_PLUGIN_VAR_RESOLVE((thdvar_str_t *) opt);
+ break;
+ case PLUGIN_VAR_ENUM:
+ SET_PLUGIN_VAR_RESOLVE((thdvar_enum_t *) opt);
+ break;
+ case PLUGIN_VAR_SET:
+ SET_PLUGIN_VAR_RESOLVE((thdvar_set_t *) opt);
+ break;
+ default:
+ sql_print_error("Unknown variable type code 0x%x in plugin '%s'.",
+ opt->flags, plugin_name);
+ DBUG_RETURN(-1);
+ };
+ }
+
+ for (plugin_option= tmp->plugin->system_vars;
+ plugin_option && *plugin_option; plugin_option++, index++)
+ {
+ switch ((opt= *plugin_option)->flags & PLUGIN_VAR_TYPEMASK) {
+ case PLUGIN_VAR_BOOL:
+ if (!opt->check)
+ opt->check= check_func_bool;
+ if (!opt->update)
+ opt->update= update_func_bool;
+ break;
+ case PLUGIN_VAR_INT:
+ if (!opt->check)
+ opt->check= check_func_int;
+ if (!opt->update)
+ opt->update= update_func_int;
+ break;
+ case PLUGIN_VAR_LONG:
+ if (!opt->check)
+ opt->check= check_func_long;
+ if (!opt->update)
+ opt->update= update_func_long;
+ break;
+ case PLUGIN_VAR_LONGLONG:
+ if (!opt->check)
+ opt->check= check_func_longlong;
+ if (!opt->update)
+ opt->update= update_func_longlong;
+ break;
+ case PLUGIN_VAR_STR:
+ if (!opt->check)
+ opt->check= check_func_str;
+ if (!opt->update)
+ {
+ opt->update= update_func_str;
+ if (!(opt->flags & PLUGIN_VAR_MEMALLOC | PLUGIN_VAR_READONLY))
+ {
+ opt->flags|= PLUGIN_VAR_READONLY;
+ sql_print_warning("Server variable %s of plugin %s was forced "
+ "to be read-only: string variable without "
+ "update_func and PLUGIN_VAR_MEMALLOC flag",
+ opt->name, plugin_name);
+ }
+ }
+ break;
+ case PLUGIN_VAR_ENUM:
+ if (!opt->check)
+ opt->check= check_func_enum;
+ if (!opt->update)
+ opt->update= update_func_long;
+ break;
+ case PLUGIN_VAR_SET:
+ if (!opt->check)
+ opt->check= check_func_set;
+ if (!opt->update)
+ opt->update= update_func_longlong;
+ break;
+ default:
+ sql_print_error("Unknown variable type code 0x%x in plugin '%s'.",
+ opt->flags, plugin_name);
+ DBUG_RETURN(-1);
+ }
+
+ if ((opt->flags & (PLUGIN_VAR_NOCMDOPT | PLUGIN_VAR_THDLOCAL))
+ == PLUGIN_VAR_NOCMDOPT)
+ continue;
+
+ if (!opt->name)
+ {
+ sql_print_error("Missing variable name in plugin '%s'.",
+ plugin_name);
+ DBUG_RETURN(-1);
+ }
+
+ if (!(opt->flags & PLUGIN_VAR_THDLOCAL))
+ {
+ optnamelen= strlen(opt->name);
+ optname= (char*) alloc_root(mem_root, namelen + optnamelen + 2);
+ strxmov(optname, name, "-", opt->name, NullS);
+ optnamelen= namelen + optnamelen + 1;
+ }
+ else
+ {
+ /* this should not fail because register_var should create entry */
+ if (!(v= find_bookmark(name, opt->name, opt->flags)))
+ {
+ sql_print_error("Thread local variable '%s' not allocated "
+ "in plugin '%s'.", opt->name, plugin_name);
+ DBUG_RETURN(-1);
+ }
+
+ *(int*)(opt + 1)= offset= v->offset;
+
+ if (opt->flags & PLUGIN_VAR_NOCMDOPT)
+ continue;
+
+ optname= (char*) memdup_root(mem_root, v->key + 1,
+ (optnamelen= v->name_len) + 1);
+ }
+
+ /* convert '_' to '-' */
+ for (p= optname; *p; p++)
+ if (*p == '_')
+ *p= '-';
+
+ options->name= optname;
+ options->comment= opt->comment;
+ options->app_type= opt;
+ options->id= (options-1)->id + 1;
+
+ plugin_opt_set_limits(options, opt);
+
+ if (opt->flags & PLUGIN_VAR_THDLOCAL)
+ options->value= options->u_max_value= (uchar**)
+ (global_system_variables.dynamic_variables_ptr + offset);
+ else
+ options->value= options->u_max_value= *(uchar***) (opt + 1);
+
+ options[1]= options[0];
+ options[1].name= p= (char*) alloc_root(mem_root, optnamelen + 8);
+ options[1].comment= 0; // hidden
+ strxmov(p, "plugin-", optname, NullS);
+
+ options+= 2;
+ }
+
+ DBUG_RETURN(0);
+}
+
+
+static my_option *construct_help_options(MEM_ROOT *mem_root,
+ struct st_plugin_int *p)
+{
+ st_mysql_sys_var **opt;
+ my_option *opts;
+ my_bool dummy, can_disable;
+ my_bool *dummy2= &dummy;
+ uint count= EXTRA_OPTIONS;
+ DBUG_ENTER("construct_help_options");
+
+ for (opt= p->plugin->system_vars; opt && *opt; opt++, count+= 2);
+
+ if (!(opts= (my_option*) alloc_root(mem_root, sizeof(my_option) * count)))
+ DBUG_RETURN(NULL);
+
+ bzero(opts, sizeof(my_option) * count);
+
+ dummy= TRUE; /* plugin is enabled. */
+
+ can_disable=
+ my_strcasecmp(&my_charset_latin1, p->name.str, "MyISAM") &&
+ my_strcasecmp(&my_charset_latin1, p->name.str, "MEMORY");
+
+ if (construct_options(mem_root, p, opts, &dummy2, can_disable))
+ DBUG_RETURN(NULL);
+
+ DBUG_RETURN(opts);
+}
+
+
+/*
+ SYNOPSIS
+ test_plugin_options()
+ tmp_root temporary scratch space
+ plugin internal plugin structure
+ argc user supplied arguments
+ argv user supplied arguments
+ default_enabled default plugin enable status
+ RETURNS:
+ 0 SUCCESS - plugin should be enabled/loaded
+ NOTE:
+ Requires that a write-lock is held on LOCK_system_variables_hash
+*/
+static int test_plugin_options(MEM_ROOT *tmp_root, struct st_plugin_int *tmp,
+ int *argc, char **argv, my_bool default_enabled)
+{
+ struct sys_var_chain chain= { NULL, NULL };
+ my_bool enabled_saved= default_enabled, can_disable;
+ my_bool *enabled= &default_enabled;
+ MEM_ROOT *mem_root= alloc_root_inited(&tmp->mem_root) ?
+ &tmp->mem_root : &plugin_mem_root;
+ st_mysql_sys_var **opt;
+ my_option *opts= NULL;
+ char *p, *varname;
+ int error;
+ st_mysql_sys_var *o;
+ sys_var *v;
+ struct st_bookmark *var;
+ uint len, count= EXTRA_OPTIONS;
+ DBUG_ENTER("test_plugin_options");
+ DBUG_ASSERT(tmp->plugin && tmp->name.str);
+
+ for (opt= tmp->plugin->system_vars; opt && *opt; opt++)
+ count+= 2; /* --{plugin}-{optname} and --plugin-{plugin}-{optname} */
+
+ can_disable=
+ my_strcasecmp(&my_charset_latin1, tmp->name.str, "MyISAM") &&
+ my_strcasecmp(&my_charset_latin1, tmp->name.str, "MEMORY");
+
+ if (count > EXTRA_OPTIONS || (*argc > 1))
+ {
+ if (!(opts= (my_option*) alloc_root(tmp_root, sizeof(my_option) * count)))
+ {
+ sql_print_error("Out of memory for plugin '%s'.", tmp->name.str);
+ DBUG_RETURN(-1);
+ }
+ bzero(opts, sizeof(my_option) * count);
+
+ if (construct_options(tmp_root, tmp, opts, &enabled, can_disable))
+ {
+ sql_print_error("Bad options for plugin '%s'.", tmp->name.str);
+ DBUG_RETURN(-1);
+ }
+
+ error= handle_options(argc, &argv, opts, get_one_plugin_option);
+ (*argc)++; /* add back one for the program name */
+
+ if (error)
+ {
+ sql_print_error("Parsing options for plugin '%s' failed.",
+ tmp->name.str);
+ goto err;
+ }
+ }
+
+ if (!*enabled && !can_disable)
+ {
+ sql_print_warning("Plugin '%s' cannot be disabled", tmp->name.str);
+ *enabled= TRUE;
+ }
+
+ error= 1;
+
+ if (*enabled)
+ {
+ for (opt= tmp->plugin->system_vars; opt && *opt; opt++)
+ {
+ if (((o= *opt)->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);
+ else
+ {
+ len= tmp->name.length + strlen(o->name) + 2;
+ varname= (char*) alloc_root(mem_root, len);
+ strxmov(varname, tmp->name.str, "-", o->name, NullS);
+ my_casedn_str(&my_charset_latin1, varname);
+
+ for (p= varname; *p; p++)
+ if (*p == '-')
+ *p= '_';
+
+ v= new (mem_root) sys_var_pluginvar(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);
+ }
+ if (chain.first)
+ {
+ chain.last->next = NULL;
+ if (mysql_add_sys_var_chain(chain.first, NULL))
+ {
+ sql_print_error("Plugin '%s' has conflicting system variables",
+ tmp->name.str);
+ goto err;
+ }
+ tmp->system_vars= chain.first;
+ }
+ DBUG_RETURN(0);
+ }
+
+ if (enabled_saved && global_system_variables.log_warnings)
+ sql_print_information("Plugin '%s' disabled by command line option",
+ tmp->name.str);
+err:
+ if (opts)
+ my_cleanup_options(opts);
+ DBUG_RETURN(error);
+}
+
+
+/****************************************************************************
+ 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)
+{
+ 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 (!p->plugin->system_vars ||
+ !(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);
+
+ /* main_options now points to the empty option terminator */
+ insert_dynamic(&all_options, (uchar*) main_options);
+
+ my_print_help((my_option*) all_options.buffer);
+ my_print_variables((my_option*) all_options.buffer);
+
+ delete_dynamic(&all_options);
+ free_root(&mem_root, MYF(0));
+}
+
diff --git a/sql/sql_plugin.h b/sql/sql_plugin.h
new file mode 100644
index 00000000000..8ae38d58845
--- /dev/null
+++ b/sql/sql_plugin.h
@@ -0,0 +1,140 @@
+/* Copyright (C) 2005 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#ifndef _sql_plugin_h
+#define _sql_plugin_h
+
+class sys_var;
+
+/*
+ the following flags are valid for plugin_init()
+*/
+#define PLUGIN_INIT_SKIP_DYNAMIC_LOADING 1
+#define PLUGIN_INIT_SKIP_PLUGIN_TABLE 2
+#define PLUGIN_INIT_SKIP_INITIALIZATION 4
+
+#define INITIAL_LEX_PLUGIN_LIST_SIZE 16
+
+/*
+ the following #define adds server-only members to enum_mysql_show_type,
+ that is defined in plugin.h
+*/
+#define SHOW_FUNC SHOW_FUNC, SHOW_KEY_CACHE_LONG, SHOW_KEY_CACHE_LONGLONG, \
+ SHOW_LONG_STATUS, SHOW_DOUBLE_STATUS, SHOW_HAVE, \
+ SHOW_MY_BOOL, SHOW_HA_ROWS, SHOW_SYS, SHOW_LONG_NOFLUSH, \
+ SHOW_LONGLONG_STATUS
+#include <mysql/plugin.h>
+#undef SHOW_FUNC
+typedef enum enum_mysql_show_type SHOW_TYPE;
+typedef struct st_mysql_show_var SHOW_VAR;
+
+#define MYSQL_ANY_PLUGIN -1
+
+/*
+ different values of st_plugin_int::state
+ though they look like a bitmap, plugin may only
+ be in one of those eigenstates, not in a superposition of them :)
+ It's a bitmap, because it makes it easier to test
+ "whether the state is one of those..."
+*/
+#define PLUGIN_IS_FREED 1
+#define PLUGIN_IS_DELETED 2
+#define PLUGIN_IS_UNINITIALIZED 4
+#define PLUGIN_IS_READY 8
+#define PLUGIN_IS_DYING 16
+#define PLUGIN_IS_DISABLED 32
+
+/* A handle for the dynamic library containing a plugin or plugins. */
+
+struct st_plugin_dl
+{
+ LEX_STRING dl;
+ void *handle;
+ struct st_mysql_plugin *plugins;
+ int version;
+ uint ref_count; /* number of plugins loaded from the library */
+};
+
+/* A handle of a plugin */
+
+struct st_plugin_int
+{
+ LEX_STRING name;
+ struct st_mysql_plugin *plugin;
+ struct st_plugin_dl *plugin_dl;
+ uint state;
+ uint ref_count; /* number of threads using the plugin */
+ 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 */
+};
+
+
+/*
+ See intern_plugin_lock() for the explanation for the
+ conditionally defined plugin_ref type
+*/
+#ifdef DBUG_OFF
+typedef struct st_plugin_int *plugin_ref;
+#define plugin_decl(pi) ((pi)->plugin)
+#define plugin_dlib(pi) ((pi)->plugin_dl)
+#define plugin_data(pi,cast) ((cast)((pi)->data))
+#define plugin_name(pi) (&((pi)->name))
+#define plugin_state(pi) ((pi)->state)
+#define plugin_equals(p1,p2) ((p1) == (p2))
+#else
+typedef struct st_plugin_int **plugin_ref;
+#define plugin_decl(pi) ((pi)[0]->plugin)
+#define plugin_dlib(pi) ((pi)[0]->plugin_dl)
+#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_equals(p1,p2) ((p1) && (p2) && (p1)[0] == (p2)[0])
+#endif
+
+typedef int (*plugin_type_init)(struct st_plugin_int *);
+
+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 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);
+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);
+extern plugin_ref plugin_lock_by_name(THD *thd, const LEX_STRING *name,
+ int type CALLER_INFO_PROTO);
+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 plugin_register_builtin(struct st_mysql_plugin *plugin);
+extern void plugin_thdvar_init(THD *thd);
+extern void plugin_thdvar_cleanup(THD *thd);
+
+typedef my_bool (plugin_foreach_func)(THD *thd,
+ plugin_ref plugin,
+ void *arg);
+#define plugin_foreach(A,B,C,D) plugin_foreach_with_mask(A,B,C,PLUGIN_IS_READY,D)
+extern bool plugin_foreach_with_mask(THD *thd, plugin_foreach_func *func,
+ int type, uint state_mask, void *arg);
+#endif
diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc
index a07fcc37856..cd11b6d4c24 100644
--- a/sql/sql_prepare.cc
+++ b/sql/sql_prepare.cc
@@ -13,7 +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 */
-/**********************************************************************
+/**
+ @file
+
This file contains the implementation of prepared statements.
When one prepares a statement:
@@ -28,23 +30,33 @@ When one prepares a statement:
- Without executing the query, return back to client the total
number of parameters along with result-set metadata information
(if any) in the following format:
+ @verbatim
[STMT_ID:4]
[Column_count:2]
[Param_count:2]
[Params meta info (stubs only for now)] (if Param_count > 0)
[Columns meta info] (if Column_count > 0)
+ @endverbatim
+
+ During prepare the tables used in a statement are opened, but no
+ locks are acquired. Table opening will block any DDL during the
+ operation, and we do not need any locks as we neither read nor
+ modify any data during prepare. Tables are closed after prepare
+ finishes.
When one executes a statement:
- Server gets the command 'COM_STMT_EXECUTE' to execute the
previously prepared query. If there are any parameter markers, then the
client will send the data in the following format:
+ @verbatim
[COM_STMT_EXECUTE:1]
[STMT_ID:4]
[NULL_BITS:(param_count+7)/8)]
[TYPES_SUPPLIED_BY_CLIENT(0/1):1]
[[length]data]
[[length]data] .. [[length]data].
+ @endverbatim
(Note: Except for string/binary types; all other types will not be
supplied with length field)
- If it is a first execute or types of parameters were altered by client,
@@ -53,6 +65,10 @@ When one executes a statement:
- Execute the query without re-parsing and send back the results
to client
+ During execution of prepared statement tables are opened and locked
+ the same way they would for normal (non-prepared) statement
+ execution. Tables are unlocked and closed after the execution.
+
When one supplies long data for a placeholder:
- Server gets the long data in pieces with command type
@@ -65,8 +81,7 @@ When one supplies long data for a placeholder:
server doesn't care; also, the server doesn't notify the client whether
it got the data or not; if there is any error, then it will be returned
at statement execute.
-
-***********************************************************************/
+*/
#include "mysql_priv.h"
#include "sql_select.h" // for JOIN
@@ -81,13 +96,15 @@ When one supplies long data for a placeholder:
#include <mysql_com.h>
#endif
-/* A result class used to send cursor rows using the binary protocol. */
+/**
+ A result class used to send cursor rows using the binary protocol.
+*/
-class Select_fetch_protocol_prep: public select_send
+class Select_fetch_protocol_binary: public select_send
{
- Protocol_prep protocol;
+ Protocol_binary protocol;
public:
- Select_fetch_protocol_prep(THD *thd);
+ Select_fetch_protocol_binary(THD *thd);
virtual bool send_fields(List<Item> &list, uint flags);
virtual bool send_data(List<Item> &items);
virtual bool send_eof();
@@ -102,8 +119,7 @@ public:
/****************************************************************************/
/**
- @class Prepared_statement
- @brief Prepared_statement: a statement that can contain placeholders
+ Prepared_statement: a statement that can contain placeholders.
*/
class Prepared_statement: public Statement
@@ -115,7 +131,7 @@ public:
};
THD *thd;
- Select_fetch_protocol_prep result;
+ Select_fetch_protocol_binary result;
Protocol *protocol;
Item_param **param_array;
uint param_count;
@@ -139,21 +155,30 @@ public:
virtual void cleanup_stmt();
bool set_name(LEX_STRING *name);
inline void close_cursor() { delete cursor; cursor= 0; }
-
+ inline bool is_in_use() { return flags & (uint) IS_IN_USE; }
+ inline bool is_protocol_text() const { return protocol == &thd->protocol_text; }
bool prepare(const char *packet, uint packet_length);
- bool execute(String *expanded_query, bool open_cursor);
+ bool execute_loop(String *expanded_query,
+ bool open_cursor,
+ uchar *packet_arg, uchar *packet_end_arg);
/* Destroy this statement */
- bool deallocate();
+ void deallocate();
private:
/**
- Store the parsed tree of a prepared statement here.
- */
- LEX main_lex;
- /**
The memory root to allocate parsed tree elements (instances of Item,
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,
+ uchar *packet, uchar *packet_end);
+ bool execute(String *expanded_query, bool open_cursor);
+ bool reprepare();
+ bool validate_metadata(Prepared_statement *copy);
+ void swap_prepared_statement(Prepared_statement *copy);
};
@@ -167,25 +192,22 @@ inline bool is_param_null(const uchar *pos, ulong param_no)
return pos[param_no/8] & (1 << (param_no & 7));
}
-/*
+/**
Find a prepared statement in the statement map by id.
- SYNOPSIS
- find_prepared_statement()
- thd thread handle
- id statement id
- where the place from which this function is called (for
- error reporting).
-
- DESCRIPTION
Try to find a prepared statement and set THD error if it's not found.
- RETURN VALUE
+ @param thd thread handle
+ @param id statement id
+ @param where the place from which this function is called (for
+ error reporting).
+
+ @return
0 if the statement was not found, a pointer otherwise.
*/
static Prepared_statement *
-find_prepared_statement(THD *thd, ulong id, const char *where)
+find_prepared_statement(THD *thd, ulong id)
{
/*
To strictly separate namespaces of SQL prepared statements and C API
@@ -195,23 +217,19 @@ find_prepared_statement(THD *thd, ulong id, const char *where)
Statement *stmt= thd->stmt_map.find(id);
if (stmt == 0 || stmt->type() != Query_arena::PREPARED_STATEMENT)
- {
- char llbuf[22];
- my_error(ER_UNKNOWN_STMT_HANDLER, MYF(0), sizeof(llbuf), llstr(id, llbuf),
- where);
- return 0;
- }
+ return NULL;
+
return (Prepared_statement *) stmt;
}
-/*
+/**
Send prepared statement id and metadata to the client after prepare.
- SYNOPSIS
- send_prep_stmt()
+ @todo
+ Fix this nasty upcast from List<Item_param> to List<Item>
- RETURN VALUE
+ @return
0 in case of success, 1 otherwise
*/
@@ -219,8 +237,10 @@ find_prepared_statement(THD *thd, ulong id, const char *where)
static bool send_prep_stmt(Prepared_statement *stmt, uint columns)
{
NET *net= &stmt->thd->net;
- char buff[12];
+ uchar buff[12];
uint tmp;
+ int error;
+ THD *thd= stmt->thd;
DBUG_ENTER("send_prep_stmt");
buff[0]= 0; /* OK packet indicator */
@@ -235,11 +255,16 @@ static bool send_prep_stmt(Prepared_statement *stmt, uint columns)
Send types and names of placeholders to the client
XXX: fix this nasty upcast from List<Item_param> to List<Item>
*/
- DBUG_RETURN(my_net_write(net, buff, sizeof(buff)) ||
- (stmt->param_count &&
- stmt->thd->protocol_simple.send_fields((List<Item> *)
- &stmt->lex->param_list,
- Protocol::SEND_EOF)));
+ error= my_net_write(net, buff, sizeof(buff));
+ if (stmt->param_count && ! error)
+ {
+ error= thd->protocol_text.send_fields((List<Item> *)
+ &stmt->lex->param_list,
+ Protocol::SEND_EOF);
+ }
+ /* Flag that a response has already been sent */
+ thd->main_da.disable_status();
+ DBUG_RETURN(error);
}
#else
static bool send_prep_stmt(Prepared_statement *stmt,
@@ -250,30 +275,29 @@ 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();
return 0;
}
#endif /*!EMBEDDED_LIBRARY*/
-/*
+#ifndef EMBEDDED_LIBRARY
+
+/**
Read the length of the parameter data and return it back to
the caller.
- SYNOPSIS
- get_param_length()
- packet a pointer to the data
- len remaining packet length
-
- DESCRIPTION
Read data length, position the packet to the first byte after it,
and return the length to the caller.
- RETURN VALUE
+ @param packet a pointer to the data
+ @param len remaining packet length
+
+ @return
Length of data piece.
*/
-#ifndef EMBEDDED_LIBRARY
static ulong get_param_length(uchar **packet, ulong len)
{
reg1 uchar *pos= *packet;
@@ -314,16 +338,9 @@ static ulong get_param_length(uchar **packet, ulong len)
#define get_param_length(packet, len) len
#endif /*!EMBEDDED_LIBRARY*/
- /*
- Data conversion routines.
-
- SYNOPSIS
- set_param_xx()
- param parameter item
- pos input data buffer
- len length of data in the buffer
+/**
+ Data conversion routines.
- DESCRIPTION
All these functions read the data from pos, convert it to requested
type and assign to param; pos is advanced to predefined length.
@@ -331,8 +348,9 @@ static ulong get_param_length(uchar **packet, ulong len)
(i.e. when input types altered) and for all subsequent executions
we don't read any values for this.
- RETURN VALUE
- none
+ @param param parameter item
+ @param pos input data buffer
+ @param len length of data in the buffer
*/
static void set_param_tiny(Item_param *param, uchar **pos, ulong len)
@@ -434,6 +452,10 @@ static void set_param_decimal(Item_param *param, uchar **pos, ulong len)
libmysql.c (store_param_{time,date,datetime}).
*/
+/**
+ @todo
+ Add warning 'Data truncated' here
+*/
static void set_param_time(Item_param *param, uchar **pos, ulong len)
{
MYSQL_TIME tm;
@@ -523,6 +545,10 @@ static void set_param_date(Item_param *param, uchar **pos, ulong len)
}
#else/*!EMBEDDED_LIBRARY*/
+/**
+ @todo
+ Add warning 'Data truncated' here
+*/
void set_param_time(Item_param *param, uchar **pos, ulong len)
{
MYSQL_TIME tm= *((MYSQL_TIME*)*pos);
@@ -635,6 +661,7 @@ static void setup_one_conversion_function(THD *thd, Item_param *param,
param->value.cs_info.character_set_of_placeholder= &my_charset_bin;
param->value.cs_info.character_set_client=
thd->variables.character_set_client;
+ DBUG_ASSERT(thd->variables.character_set_client);
param->value.cs_info.final_character_set_of_str_value= &my_charset_bin;
param->item_type= Item::STRING_ITEM;
param->item_result_type= STRING_RESULT;
@@ -674,41 +701,47 @@ static void setup_one_conversion_function(THD *thd, Item_param *param,
}
#ifndef EMBEDDED_LIBRARY
-/*
+/**
Routines to assign parameters from data supplied by the client.
- DESCRIPTION
Update the parameter markers by reading data from the packet and
and generate a valid query for logging.
- NOTES
- This function, along with other _withlog functions is called when one of
+ @note
+ This function, along with other _with_log functions is called when one of
binary, slow or general logs is open. Logging of prepared statements in
all cases is performed by means of conventional queries: if parameter
data was supplied from C API, each placeholder in the query is
replaced with its actual value; if we're logging a [Dynamic] SQL
prepared statement, parameter markers are replaced with variable names.
Example:
+ @verbatim
mysql_stmt_prepare("UPDATE t1 SET a=a*1.25 WHERE a=?")
--> general logs gets [Prepare] UPDATE t1 SET a*1.25 WHERE a=?"
mysql_stmt_execute(stmt);
--> general and binary logs get
[Execute] UPDATE t1 SET a*1.25 WHERE a=1"
- If a statement has been prepared using SQL syntax:
+ @endverbatim
+
+ If a statement has been prepared using SQL syntax:
+ @verbatim
PREPARE stmt FROM "UPDATE t1 SET a=a*1.25 WHERE a=?"
--> general log gets
[Query] PREPARE stmt FROM "UPDATE ..."
EXECUTE stmt USING @a
--> general log gets
- [Query] EXECUTE stmt USING @a;
+ [Query] EXECUTE stmt USING @a;
+ @endverbatim
- RETURN VALUE
- 0 if success, 1 otherwise
+ @retval
+ 0 if success
+ @retval
+ 1 otherwise
*/
-static bool insert_params_withlog(Prepared_statement *stmt, uchar *null_array,
- uchar *read_pos, uchar *data_end,
- String *query)
+static bool insert_params_with_log(Prepared_statement *stmt, uchar *null_array,
+ uchar *read_pos, uchar *data_end,
+ String *query)
{
THD *thd= stmt->thd;
Item_param **begin= stmt->param_array;
@@ -716,7 +749,7 @@ static bool insert_params_withlog(Prepared_statement *stmt, uchar *null_array,
uint32 length= 0;
String str;
const String *res;
- DBUG_ENTER("insert_params_withlog");
+ DBUG_ENTER("insert_params_with_log");
if (query->copy(stmt->query, stmt->query_length, default_charset_info))
DBUG_RETURN(1);
@@ -819,12 +852,12 @@ static bool setup_conversion_functions(Prepared_statement *stmt,
#else
-/*
+/**
Embedded counterparts of parameter assignment routines.
- DESCRIPTION
The main difference between the embedded library and the server is
that in embedded case we don't serialize/deserialize parameters data.
+
Additionally, for unknown reason, the client-side flag raised for
changed types of placeholders is ignored and we simply setup conversion
functions at each execute (TODO: fix).
@@ -866,7 +899,8 @@ static bool emb_insert_params(Prepared_statement *stmt, String *expanded_query)
}
-static bool emb_insert_params_withlog(Prepared_statement *stmt, String *query)
+static bool emb_insert_params_with_log(Prepared_statement *stmt,
+ String *query)
{
THD *thd= stmt->thd;
Item_param **it= stmt->param_array;
@@ -877,7 +911,7 @@ static bool emb_insert_params_withlog(Prepared_statement *stmt, String *query)
const String *res;
uint32 length= 0;
- DBUG_ENTER("emb_insert_params_withlog");
+ DBUG_ENTER("emb_insert_params_with_log");
if (query->copy(stmt->query, stmt->query_length, default_charset_info))
DBUG_RETURN(1);
@@ -916,16 +950,64 @@ static bool emb_insert_params_withlog(Prepared_statement *stmt, String *query)
#endif /*!EMBEDDED_LIBRARY*/
+/**
+ Setup data conversion routines using an array of parameter
+ markers from the original prepared statement.
+ Swap the parameter data of the original prepared
+ statement to the new one.
+
+ Used only when we re-prepare a prepared statement.
+ There are two reasons for this function to exist:
+
+ 1) In the binary client/server protocol, parameter metadata
+ is sent only at first execute. Consequently, if we need to
+ reprepare a prepared statement at a subsequent execution,
+ we may not have metadata information in the packet.
+ In that case we use the parameter array of the original
+ prepared statement to setup parameter types of the new
+ prepared statement.
+
+ 2) In the binary client/server protocol, we may supply
+ long data in pieces. When the last piece is supplied,
+ we assemble the pieces and convert them from client
+ character set to the connection character set. After
+ that the parameter value is only available inside
+ the parameter, the original pieces are lost, and thus
+ we can only assign the corresponding parameter of the
+ reprepared statement from the original value.
+
+ @param[out] param_array_dst parameter markers of the new statement
+ @param[in] param_array_src parameter markers of the original
+ statement
+ @param[in] param_count total number of parameters. Is the
+ same in src and dst arrays, since
+ the statement query is the same
+
+ @return this function never fails
+*/
-/*
+static void
+swap_parameter_array(Item_param **param_array_dst,
+ Item_param **param_array_src,
+ uint param_count)
+{
+ Item_param **dst= param_array_dst;
+ Item_param **src= param_array_src;
+ Item_param **end= param_array_dst + param_count;
+
+ for (; dst < end; ++src, ++dst)
+ (*dst)->set_param_type_and_swap_value(*src);
+}
+
+
+/**
Assign prepared statement parameters from user variables.
- SYNOPSIS
- insert_params_from_vars()
- stmt Statement
- varnames List of variables. Caller must ensure that number of variables
- in the list is equal to number of statement parameters
- query Ignored
+ @param stmt Statement
+ @param varnames List of variables. Caller must ensure that number
+ of variables in the list is equal to number of statement
+ parameters
+ @param query Ignored
*/
static bool insert_params_from_vars(Prepared_statement *stmt,
@@ -944,7 +1026,7 @@ 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,
- (byte*) varname->str,
+ (uchar*) varname->str,
varname->length);
if (param->set_from_user_var(stmt->thd, entry) ||
param->convert_str_value(stmt->thd))
@@ -954,17 +1036,16 @@ static bool insert_params_from_vars(Prepared_statement *stmt,
}
-/*
+/**
Do the same as insert_params_from_vars but also construct query text for
binary log.
- SYNOPSIS
- insert_params_from_vars()
- stmt Prepared statement
- varnames List of variables. Caller must ensure that number of variables
- in the list is equal to number of statement parameters
- query The query with parameter markers replaced with corresponding
- user variables that were used to execute the query.
+ @param stmt Prepared statement
+ @param varnames List of variables. Caller must ensure that number of
+ variables in the list is equal to number of statement
+ parameters
+ @param query The query with parameter markers replaced with corresponding
+ user variables that were used to execute the query.
*/
static bool insert_params_from_vars_with_log(Prepared_statement *stmt,
@@ -991,7 +1072,7 @@ 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, (byte*) varname->str,
+ entry= (user_var_entry *) hash_search(&thd->user_vars, (uchar*) varname->str,
varname->length);
/*
We have to call the setup_one_conversion_function() here to set
@@ -1013,17 +1094,16 @@ static bool insert_params_from_vars_with_log(Prepared_statement *stmt,
DBUG_RETURN(0);
}
-/*
+/**
Validate INSERT statement.
- SYNOPSIS
- mysql_test_insert()
- stmt prepared statement
- tables global/local table list
+ @param stmt prepared statement
+ @param tables global/local table list
- RETURN VALUE
- FALSE success
- TRUE error, error message is set in THD
+ @retval
+ FALSE success
+ @retval
+ TRUE error, error message is set in THD
*/
static bool mysql_test_insert(Prepared_statement *stmt,
@@ -1062,7 +1142,7 @@ static bool mysql_test_insert(Prepared_statement *stmt,
if (table_list->table)
{
// don't allocate insert_values
- table_list->table->insert_values=(byte *)1;
+ table_list->table->insert_values=(uchar *)1;
}
if (mysql_prepare_insert(thd, table_list, table_list->table,
@@ -1074,11 +1154,11 @@ static bool mysql_test_insert(Prepared_statement *stmt,
its.rewind();
if (table_list->lock_type == TL_WRITE_DELAYED &&
- !(table_list->table->file->table_flags() & HA_CAN_INSERT_DELAYED))
+ !(table_list->table->file->ha_table_flags() & HA_CAN_INSERT_DELAYED))
{
- my_error(ER_ILLEGAL_HA, MYF(0), (table_list->view ?
- table_list->view_name.str :
- table_list->table_name));
+ my_error(ER_DELAYED_NOT_SUPPORTED, MYF(0), (table_list->view ?
+ table_list->view_name.str :
+ table_list->table_name));
goto error;
}
while ((values= its++))
@@ -1089,7 +1169,7 @@ static bool mysql_test_insert(Prepared_statement *stmt,
my_error(ER_WRONG_VALUE_COUNT_ON_ROW, MYF(0), counter);
goto error;
}
- if (setup_fields(thd, 0, *values, 0, 0, 0))
+ if (setup_fields(thd, 0, *values, MARK_COLUMNS_NONE, 0, 0))
goto error;
}
}
@@ -1101,18 +1181,21 @@ error:
}
-/*
- Validate UPDATE statement
+/**
+ Validate UPDATE statement.
+
+ @param stmt prepared statement
+ @param tables list of tables used in this query
- SYNOPSIS
- mysql_test_update()
- stmt prepared statement
- tables list of tables used in this query
+ @todo
+ - here we should send types of placeholders to the client.
- RETURN VALUE
- 0 success
- 1 error, error message is set in THD
- 2 convert to multi_update
+ @retval
+ 0 success
+ @retval
+ 1 error, error message is set in THD
+ @retval
+ 2 convert to multi_update
*/
static int mysql_test_update(Prepared_statement *stmt,
@@ -1125,32 +1208,20 @@ static int mysql_test_update(Prepared_statement *stmt,
#ifndef NO_EMBEDDED_ACCESS_CHECKS
uint want_privilege;
#endif
- bool need_reopen;
DBUG_ENTER("mysql_test_update");
- if (update_precheck(thd, table_list))
+ if (update_precheck(thd, table_list) ||
+ open_tables(thd, &table_list, &table_count, 0))
goto error;
- for ( ; ; )
+ if (table_list->multitable_view)
{
- if (open_tables(thd, &table_list, &table_count, 0))
- goto error;
-
- if (table_list->multitable_view)
- {
- 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)
- goto error;
- close_tables_for_reopen(thd, &table_list);
+ 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);
}
/*
@@ -1177,7 +1248,7 @@ static int mysql_test_update(Prepared_statement *stmt,
table_list->register_want_access(want_privilege);
#endif
thd->lex->select_lex.no_wrap_view_item= TRUE;
- res= setup_fields(thd, 0, select->item_list, 1, 0, 0);
+ res= setup_fields(thd, 0, select->item_list, MARK_COLUMNS_READ, 0, 0);
thd->lex->select_lex.no_wrap_view_item= FALSE;
if (res)
goto error;
@@ -1188,7 +1259,7 @@ static int mysql_test_update(Prepared_statement *stmt,
(SELECT_ACL & ~table_list->table->grant.privilege);
table_list->register_want_access(SELECT_ACL);
#endif
- if (setup_fields(thd, 0, stmt->lex->value_list, 0, 0, 0))
+ if (setup_fields(thd, 0, stmt->lex->value_list, MARK_COLUMNS_NONE, 0, 0))
goto error;
/* TODO: here we should send types of placeholders to the client. */
DBUG_RETURN(0);
@@ -1197,17 +1268,16 @@ error:
}
-/*
+/**
Validate DELETE statement.
- SYNOPSIS
- mysql_test_delete()
- stmt prepared statement
- tables list of tables used in this query
+ @param stmt prepared statement
+ @param tables list of tables used in this query
- RETURN VALUE
- FALSE success
- TRUE error, error message is set in THD
+ @retval
+ FALSE success
+ @retval
+ TRUE error, error message is set in THD
*/
static bool mysql_test_delete(Prepared_statement *stmt,
@@ -1218,7 +1288,7 @@ static bool mysql_test_delete(Prepared_statement *stmt,
DBUG_ENTER("mysql_test_delete");
if (delete_precheck(thd, table_list) ||
- open_and_lock_tables(thd, table_list))
+ open_normal_and_derived_tables(thd, table_list, 0))
goto error;
if (!table_list->table)
@@ -1234,26 +1304,25 @@ error:
}
-/*
+/**
Validate SELECT statement.
- SYNOPSIS
- mysql_test_select()
- stmt prepared statement
- tables list of tables used in the query
-
- DESCRIPTION
In case of success, if this query is not EXPLAIN, send column list info
back to the client.
- RETURN VALUE
- 0 success
- 1 error, error message is set in THD
- 2 success, and statement metadata has been sent
+ @param stmt prepared statement
+ @param tables list of tables used in the query
+
+ @retval
+ 0 success
+ @retval
+ 1 error, error message is set in THD
+ @retval
+ 2 success, and statement metadata has been sent
*/
static int mysql_test_select(Prepared_statement *stmt,
- TABLE_LIST *tables, bool text_protocol)
+ TABLE_LIST *tables)
{
THD *thd= stmt->thd;
LEX *lex= stmt->lex;
@@ -1265,7 +1334,7 @@ static int mysql_test_select(Prepared_statement *stmt,
ulong privilege= lex->exchange ? SELECT_ACL | FILE_ACL : SELECT_ACL;
if (tables)
{
- if (check_table_access(thd, privilege, tables,0))
+ if (check_table_access(thd, privilege, tables, UINT_MAX, FALSE))
goto error;
}
else if (check_access(thd, privilege, any_db,0,0,0,0))
@@ -1277,7 +1346,7 @@ static int mysql_test_select(Prepared_statement *stmt,
goto error;
}
- if (open_and_lock_tables(thd, tables))
+ if (open_normal_and_derived_tables(thd, tables, 0))
goto error;
thd->used_tables= 0; // Updated by setup_fields
@@ -1289,7 +1358,7 @@ static int mysql_test_select(Prepared_statement *stmt,
*/
if (unit->prepare(thd, 0, 0))
goto error;
- if (!lex->describe && !text_protocol)
+ if (!lex->describe && !stmt->is_protocol_text())
{
/* Make copy of item list, as change_columns may change it */
List<Item> fields(lex->select_lex.item_list);
@@ -1314,18 +1383,17 @@ error:
}
-/*
+/**
Validate and prepare for execution DO statement expressions.
- SYNOPSIS
- mysql_test_do_fields()
- stmt prepared statement
- tables list of tables used in this query
- values list of expressions
+ @param stmt prepared statement
+ @param tables list of tables used in this query
+ @param values list of expressions
- RETURN VALUE
- FALSE success
- TRUE error, error message is set in THD
+ @retval
+ FALSE success
+ @retval
+ TRUE error, error message is set in THD
*/
static bool mysql_test_do_fields(Prepared_statement *stmt,
@@ -1335,27 +1403,26 @@ static bool mysql_test_do_fields(Prepared_statement *stmt,
THD *thd= stmt->thd;
DBUG_ENTER("mysql_test_do_fields");
- if (tables && check_table_access(thd, SELECT_ACL, tables, 0))
+ if (tables && check_table_access(thd, SELECT_ACL, tables, UINT_MAX, FALSE))
DBUG_RETURN(TRUE);
- if (open_and_lock_tables(thd, tables))
+ if (open_normal_and_derived_tables(thd, tables, 0))
DBUG_RETURN(TRUE);
- DBUG_RETURN(setup_fields(thd, 0, *values, 0, 0, 0));
+ DBUG_RETURN(setup_fields(thd, 0, *values, MARK_COLUMNS_NONE, 0, 0));
}
-/*
- Validate and prepare for execution SET statement expressions
+/**
+ Validate and prepare for execution SET statement expressions.
- SYNOPSIS
- mysql_test_set_fields()
- stmt prepared statement
- tables list of tables used in this query
- values list of expressions
+ @param stmt prepared statement
+ @param tables list of tables used in this query
+ @param values list of expressions
- RETURN VALUE
- FALSE success
- TRUE error, error message is set in THD
+ @retval
+ FALSE success
+ @retval
+ TRUE error, error message is set in THD
*/
static bool mysql_test_set_fields(Prepared_statement *stmt,
@@ -1367,8 +1434,8 @@ static bool mysql_test_set_fields(Prepared_statement *stmt,
THD *thd= stmt->thd;
set_var_base *var;
- if (tables && check_table_access(thd, SELECT_ACL, tables, 0) ||
- open_and_lock_tables(thd, tables))
+ if (tables && check_table_access(thd, SELECT_ACL, tables, UINT_MAX, FALSE) ||
+ open_normal_and_derived_tables(thd, tables, 0))
goto error;
while ((var= it++))
@@ -1382,23 +1449,59 @@ error:
}
-/*
- Check internal SELECT of the prepared command
+/**
+ Validate and prepare for execution CALL statement expressions.
+
+ @param stmt prepared statement
+ @param tables list of tables used in this query
+ @param value_list list of expressions
+
+ @retval FALSE success
+ @retval TRUE error, error message is set in THD
+*/
+
+static bool mysql_test_call_fields(Prepared_statement *stmt,
+ TABLE_LIST *tables,
+ List<Item> *value_list)
+{
+ DBUG_ENTER("mysql_test_call_fields");
+
+ List_iterator<Item> it(*value_list);
+ THD *thd= stmt->thd;
+ Item *item;
+
+ if (tables && check_table_access(thd, SELECT_ACL, tables, UINT_MAX, FALSE) ||
+ open_normal_and_derived_tables(thd, tables, 0))
+ goto err;
+
+ while ((item= it++))
+ {
+ if (!item->fixed && item->fix_fields(thd, it.ref()) ||
+ item->check_cols(1))
+ goto err;
+ }
+ DBUG_RETURN(FALSE);
+err:
+ DBUG_RETURN(TRUE);
+}
+
+
+/**
+ Check internal SELECT of the prepared command.
- SYNOPSIS
- select_like_stmt_test()
- stmt prepared statement
- specific_prepare function of command specific prepare
- setup_tables_done_option options to be passed to LEX::unit.prepare()
+ @param stmt prepared statement
+ @param specific_prepare function of command specific prepare
+ @param setup_tables_done_option options to be passed to LEX::unit.prepare()
- NOTE
+ @note
This function won't directly open tables used in select. They should
be opened either by calling function (and in this case you probably
- should use select_like_stmt_test_with_open_n_lock()) or by
+ should use select_like_stmt_test_with_open()) or by
"specific_prepare" call (like this happens in case of multi-update).
- RETURN VALUE
+ @retval
FALSE success
+ @retval
TRUE error, error message is set in THD
*/
@@ -1421,37 +1524,37 @@ static bool select_like_stmt_test(Prepared_statement *stmt,
DBUG_RETURN(lex->unit.prepare(thd, 0, setup_tables_done_option));
}
-/*
- Check internal SELECT of the prepared command (with opening and
- locking of used tables).
-
- SYNOPSIS
- select_like_stmt_test_with_open_n_lock()
- stmt prepared statement
- tables list of tables to be opened and locked
- before calling specific_prepare function
- specific_prepare function of command specific prepare
- setup_tables_done_option options to be passed to LEX::unit.prepare()
-
- RETURN VALUE
+/**
+ Check internal SELECT of the prepared command (with opening of used
+ tables).
+
+ @param stmt prepared statement
+ @param tables list of tables to be opened
+ before calling specific_prepare function
+ @param specific_prepare function of command specific prepare
+ @param setup_tables_done_option options to be passed to LEX::unit.prepare()
+
+ @retval
FALSE success
+ @retval
TRUE error
*/
static bool
-select_like_stmt_test_with_open_n_lock(Prepared_statement *stmt,
- TABLE_LIST *tables,
- int (*specific_prepare)(THD *thd),
- ulong setup_tables_done_option)
+select_like_stmt_test_with_open(Prepared_statement *stmt,
+ TABLE_LIST *tables,
+ int (*specific_prepare)(THD *thd),
+ ulong setup_tables_done_option)
{
- DBUG_ENTER("select_like_stmt_test_with_open_n_lock");
+ DBUG_ENTER("select_like_stmt_test_with_open");
/*
- We should not call LEX::unit.cleanup() after this open_and_lock_tables()
- call because we don't allow prepared EXPLAIN yet so derived tables will
- clean up after themself.
+ We should not call LEX::unit.cleanup() after this
+ open_normal_and_derived_tables() call because we don't allow
+ prepared EXPLAIN yet so derived tables will clean up after
+ themself.
*/
- if (open_and_lock_tables(stmt->thd, tables))
+ if (open_normal_and_derived_tables(stmt->thd, tables, 0))
DBUG_RETURN(TRUE);
DBUG_RETURN(select_like_stmt_test(stmt, specific_prepare,
@@ -1459,17 +1562,16 @@ select_like_stmt_test_with_open_n_lock(Prepared_statement *stmt,
}
-/*
- Validate and prepare for execution CREATE TABLE statement
+/**
+ Validate and prepare for execution CREATE TABLE statement.
- SYNOPSIS
- mysql_test_create_table()
- stmt prepared statement
- tables list of tables used in this query
+ @param stmt prepared statement
+ @param tables list of tables used in this query
- RETURN VALUE
- FALSE success
- TRUE error, error message is set in THD
+ @retval
+ FALSE success
+ @retval
+ TRUE error, error message is set in THD
*/
static bool mysql_test_create_table(Prepared_statement *stmt)
@@ -1495,7 +1597,7 @@ static bool mysql_test_create_table(Prepared_statement *stmt)
create_table->create= TRUE;
}
- if (open_and_lock_tables(stmt->thd, lex->query_tables))
+ if (open_normal_and_derived_tables(stmt->thd, lex->query_tables, 0))
DBUG_RETURN(TRUE);
if (!(lex->create_info.options & HA_LEX_CREATE_TMP_TABLE))
@@ -1505,6 +1607,17 @@ static bool mysql_test_create_table(Prepared_statement *stmt)
res= select_like_stmt_test(stmt, 0, 0);
}
+ else if (lex->create_info.options & HA_LEX_CREATE_TABLE_LIKE)
+ {
+ /*
+ Check that the source table exist, and also record
+ its metadata version. Even though not strictly necessary,
+ 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))
+ DBUG_RETURN(TRUE);
+ }
/* put tables back for PS rexecuting */
lex->link_first_table_back(create_table, link_to_local);
@@ -1553,15 +1666,14 @@ err:
/*
Validate and prepare for execution a multi update statement.
- SYNOPSIS
- mysql_test_multiupdate()
- stmt prepared statement
- tables list of tables used in this query
- converted converted to multi-update from usual update
+ @param stmt prepared statement
+ @param tables list of tables used in this query
+ @param converted converted to multi-update from usual update
- RETURN VALUE
- FALSE success
- TRUE error, error message is set in THD
+ @retval
+ FALSE success
+ @retval
+ TRUE error, error message is set in THD
*/
static bool mysql_test_multiupdate(Prepared_statement *stmt,
@@ -1577,17 +1689,16 @@ static bool mysql_test_multiupdate(Prepared_statement *stmt,
}
-/*
+/**
Validate and prepare for execution a multi delete statement.
- SYNOPSIS
- mysql_test_multidelete()
- stmt prepared statement
- tables list of tables used in this query
+ @param stmt prepared statement
+ @param tables list of tables used in this query
- RETURN VALUE
- FALSE success
- TRUE error, error message in THD is set.
+ @retval
+ FALSE success
+ @retval
+ TRUE error, error message in THD is set.
*/
static bool mysql_test_multidelete(Prepared_statement *stmt,
@@ -1601,9 +1712,9 @@ static bool mysql_test_multidelete(Prepared_statement *stmt,
}
if (multi_delete_precheck(stmt->thd, tables) ||
- select_like_stmt_test_with_open_n_lock(stmt, tables,
- &mysql_multi_delete_prepare,
- OPTION_SETUP_TABLES_DONE))
+ select_like_stmt_test_with_open(stmt, tables,
+ &mysql_multi_delete_prepare,
+ OPTION_SETUP_TABLES_DONE))
goto error;
if (!tables->table)
{
@@ -1617,17 +1728,16 @@ error:
}
-/*
+/**
Wrapper for mysql_insert_select_prepare, to make change of local tables
- after open_and_lock_tables() call.
+ after open_normal_and_derived_tables() call.
- SYNOPSIS
- mysql_insert_select_prepare_tester()
- thd thread handle
+ @param thd thread handle
- NOTE
- We need to remove the first local table after open_and_lock_tables,
- because mysql_handle_derived uses local tables lists.
+ @note
+ We need to remove the first local table after
+ open_normal_and_derived_tables(), because mysql_handle_derived
+ uses local tables lists.
*/
static int mysql_insert_select_prepare_tester(THD *thd)
@@ -1637,7 +1747,7 @@ static int mysql_insert_select_prepare_tester(THD *thd)
next_local;
/* Skip first table, which is the table we are inserting in */
- first_select->table_list.first= (byte *) second_table;
+ first_select->table_list.first= (uchar *) second_table;
thd->lex->select_lex.context.table_list=
thd->lex->select_lex.context.first_name_resolution_table= second_table;
@@ -1645,17 +1755,16 @@ static int mysql_insert_select_prepare_tester(THD *thd)
}
-/*
+/**
Validate and prepare for execution INSERT ... SELECT statement.
- SYNOPSIS
- mysql_test_insert_select()
- stmt prepared statement
- tables list of tables used in this query
+ @param stmt prepared statement
+ @param tables list of tables used in this query
- RETURN VALUE
- FALSE success
- TRUE error, error message is set in THD
+ @retval
+ FALSE success
+ @retval
+ TRUE error, error message is set in THD
*/
static bool mysql_test_insert_select(Prepared_statement *stmt,
@@ -1668,7 +1777,7 @@ static bool mysql_test_insert_select(Prepared_statement *stmt,
if (tables->table)
{
// don't allocate insert_values
- tables->table->insert_values=(byte *)1;
+ tables->table->insert_values=(uchar *)1;
}
if (insert_precheck(stmt->thd, tables))
@@ -1679,36 +1788,33 @@ static bool mysql_test_insert_select(Prepared_statement *stmt,
DBUG_ASSERT(first_local_table != 0);
res=
- select_like_stmt_test_with_open_n_lock(stmt, tables,
- &mysql_insert_select_prepare_tester,
- OPTION_SETUP_TABLES_DONE);
+ select_like_stmt_test_with_open(stmt, tables,
+ &mysql_insert_select_prepare_tester,
+ OPTION_SETUP_TABLES_DONE);
/* revert changes made by mysql_insert_select_prepare_tester */
- lex->select_lex.table_list.first= (byte*) first_local_table;
+ lex->select_lex.table_list.first= (uchar*) first_local_table;
return res;
}
-/*
+/**
Perform semantic analysis of the parsed tree and send a response packet
to the client.
- SYNOPSIS
- check_prepared_statement()
- stmt prepared statement
-
- DESCRIPTION
This function
- opens all tables and checks access rights
- validates semantics of statement columns and SQL functions
by calling fix_fields.
- RETURN VALUE
- FALSE success, statement metadata is sent to client
- TRUE error, error message is set in THD (but not sent)
+ @param stmt prepared statement
+
+ @retval
+ FALSE success, statement metadata is sent to client
+ @retval
+ TRUE error, error message is set in THD (but not sent)
*/
-static bool check_prepared_statement(Prepared_statement *stmt,
- bool text_protocol)
+static bool check_prepared_statement(Prepared_statement *stmt)
{
THD *thd= stmt->thd;
LEX *lex= stmt->lex;
@@ -1749,9 +1855,23 @@ static bool check_prepared_statement(Prepared_statement *stmt,
case SQLCOM_DELETE:
res= mysql_test_delete(stmt, tables);
break;
-
+ /* The following allow WHERE clause, so they must be tested like SELECT */
+ case SQLCOM_SHOW_DATABASES:
+ case SQLCOM_SHOW_TABLES:
+ case SQLCOM_SHOW_TRIGGERS:
+ case SQLCOM_SHOW_EVENTS:
+ case SQLCOM_SHOW_OPEN_TABLES:
+ case SQLCOM_SHOW_FIELDS:
+ case SQLCOM_SHOW_KEYS:
+ case SQLCOM_SHOW_COLLATIONS:
+ case SQLCOM_SHOW_CHARSETS:
+ case SQLCOM_SHOW_VARIABLES:
+ case SQLCOM_SHOW_STATUS:
+ case SQLCOM_SHOW_TABLE_STATUS:
+ case SQLCOM_SHOW_STATUS_PROC:
+ case SQLCOM_SHOW_STATUS_FUNC:
case SQLCOM_SELECT:
- res= mysql_test_select(stmt, tables, text_protocol);
+ res= mysql_test_select(stmt, tables);
if (res == 2)
{
/* Statement and field info has already been sent */
@@ -1774,6 +1894,9 @@ static bool check_prepared_statement(Prepared_statement *stmt,
res= mysql_test_do_fields(stmt, tables, lex->insert_list);
break;
+ case SQLCOM_CALL:
+ res= mysql_test_call_fields(stmt, tables, &lex->value_list);
+ break;
case SQLCOM_SET_OPTION:
res= mysql_test_set_fields(stmt, tables, &lex->var_list);
break;
@@ -1787,22 +1910,34 @@ static bool check_prepared_statement(Prepared_statement *stmt,
res= mysql_test_insert_select(stmt, tables);
break;
- case SQLCOM_SHOW_DATABASES:
+ /*
+ 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_STATUS:
- case SQLCOM_SHOW_VARIABLES:
- case SQLCOM_SHOW_LOGS:
- case SQLCOM_SHOW_TABLES:
- case SQLCOM_SHOW_OPEN_TABLES:
- case SQLCOM_SHOW_CHARSETS:
- case SQLCOM_SHOW_COLLATIONS:
- case SQLCOM_SHOW_FIELDS:
- case SQLCOM_SHOW_KEYS:
+ 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:
@@ -1811,29 +1946,55 @@ static bool check_prepared_statement(Prepared_statement *stmt,
case SQLCOM_DROP_INDEX:
case SQLCOM_ROLLBACK:
case SQLCOM_TRUNCATE:
- case SQLCOM_CALL:
case SQLCOM_DROP_VIEW:
case SQLCOM_REPAIR:
case SQLCOM_ANALYZE:
case SQLCOM_OPTIMIZE:
+ case SQLCOM_CHANGE_MASTER:
+ case SQLCOM_RESET:
+ case SQLCOM_FLUSH:
+ case SQLCOM_SLAVE_START:
+ case SQLCOM_SLAVE_STOP:
+ case SQLCOM_INSTALL_PLUGIN:
+ case SQLCOM_UNINSTALL_PLUGIN:
+ case SQLCOM_CREATE_DB:
+ case SQLCOM_DROP_DB:
+ case SQLCOM_ALTER_DB_UPGRADE:
+ case SQLCOM_CHECKSUM:
+ case SQLCOM_CREATE_USER:
+ case SQLCOM_RENAME_USER:
+ case SQLCOM_DROP_USER:
+ case SQLCOM_ASSIGN_TO_KEYCACHE:
+ case SQLCOM_PRELOAD_KEYS:
+ case SQLCOM_GRANT:
+ case SQLCOM_REVOKE:
+ case SQLCOM_KILL:
break;
case SQLCOM_PREPARE:
case SQLCOM_EXECUTE:
case SQLCOM_DEALLOCATE_PREPARE:
default:
- /* All other statements are not supported yet. */
- my_message(ER_UNSUPPORTED_PS, ER(ER_UNSUPPORTED_PS), MYF(0));
- goto error;
+ /*
+ Trivial check of all status commands. This is easier than having
+ things in the above case list, as it's less chance for mistakes.
+ */
+ if (!(sql_command_flags[sql_command] & CF_STATUS_COMMAND))
+ {
+ /* All other statements are not supported yet. */
+ my_message(ER_UNSUPPORTED_PS, ER(ER_UNSUPPORTED_PS), MYF(0));
+ goto error;
+ }
+ break;
}
if (res == 0)
- DBUG_RETURN(text_protocol? FALSE : (send_prep_stmt(stmt, 0) ||
- thd->protocol->flush()));
+ DBUG_RETURN(stmt->is_protocol_text() ?
+ FALSE : (send_prep_stmt(stmt, 0) || thd->protocol->flush()));
error:
DBUG_RETURN(TRUE);
}
-/*
+/**
Initialize array of parameters in statement from LEX.
(We need to have quick access to items by number in mysql_stmt_get_longdata).
This is to avoid using malloc/realloc in the parser.
@@ -1869,31 +2030,28 @@ static bool init_param_array(Prepared_statement *stmt)
}
-/*
+/**
COM_STMT_PREPARE handler.
- SYNOPSIS
- mysql_stmt_prepare()
- packet query to be prepared
- packet_length query string length, including ignored
- trailing NULL or quote char.
-
- DESCRIPTION
Given a query string with parameter markers, create a prepared
statement from it and send PS info back to the client.
- NOTES
- This function parses the query and sends the total number of parameters
- and resultset metadata information back to client (if any), without
- executing the query i.e. without any log/disk writes. This allows the
- queries to be re-executed without re-parsing during execute.
-
If parameter markers are found in the query, then store the information
using Item_param along with maintaining a list in lex->param_array, so
that a fast and direct retrieval can be made without going through all
field items.
- RETURN VALUE
+ @param packet query to be prepared
+ @param packet_length query string length, including ignored
+ trailing NULL or quote char.
+
+ @note
+ This function parses the query and sends the total number of parameters
+ and resultset metadata information back to client (if any), without
+ executing the query i.e. without any log/disk writes. This allows the
+ queries to be re-executed without re-parsing during execute.
+
+ @return
none: in case of success a new statement id and metadata is sent
to the client, otherwise an error message is set in THD.
*/
@@ -1909,7 +2067,7 @@ void mysql_stmt_prepare(THD *thd, const char *packet, uint packet_length)
/* First of all clear possible warnings from the previous command */
mysql_reset_thd_for_next_command(thd);
- if (! (stmt= new Prepared_statement(thd, &thd->protocol_prep)))
+ if (! (stmt= new Prepared_statement(thd, &thd->protocol_binary)))
DBUG_VOID_RETURN; /* out of memory: error is set in Sql_alloc */
if (thd->stmt_map.insert(thd, stmt))
@@ -1943,22 +2101,22 @@ void mysql_stmt_prepare(THD *thd, const char *packet, uint packet_length)
DBUG_VOID_RETURN;
}
-/*
- SYNOPSIS
- get_dynamic_sql_string()
- lex in main lex
- query_len out length of the SQL statement (is set only
- in case of success)
-
- DESCRIPTION
- Get an SQL statement text from a user variable or from plain
- text. If the statement is plain text, just assign the
- pointers, otherwise allocate memory in thd->mem_root and copy
- the contents of the variable, possibly with character
- set conversion.
-
- RETURN VALUE
- non-zero success, 0 in case of error (out of memory)
+/**
+ Get an SQL statement text from a user variable or from plain text.
+
+ If the statement is plain text, just assign the
+ pointers, otherwise allocate memory in thd->mem_root and copy
+ the contents of the variable, possibly with character
+ set conversion.
+
+ @param[in] lex main lex
+ @param[out] query_len length of the SQL statement (is set only
+ in case of success)
+
+ @retval
+ non-zero success
+ @retval
+ 0 in case of error (out of memory)
*/
static const char *get_dynamic_sql_string(LEX *lex, uint *query_len)
@@ -1982,7 +2140,7 @@ static const char *get_dynamic_sql_string(LEX *lex, uint *query_len)
*/
if ((entry=
(user_var_entry*)hash_search(&thd->user_vars,
- (byte*)lex->prepared_stmt_code.str,
+ (uchar*)lex->prepared_stmt_code.str,
lex->prepared_stmt_code.length))
&& entry->value)
{
@@ -2011,7 +2169,7 @@ static const char *get_dynamic_sql_string(LEX *lex, uint *query_len)
len= (needs_conversion ? var_value->length() * to_cs->mbmaxlen :
var_value->length());
- if (!(query_str= alloc_root(thd->mem_root, len+1)))
+ if (!(query_str= (char*) alloc_root(thd->mem_root, len+1)))
goto end;
if (needs_conversion)
@@ -2036,7 +2194,7 @@ end:
}
-/* Init PS/SP specific parse tree members. */
+/** Init PS/SP specific parse tree members. */
static void init_stmt_after_parse(LEX *lex)
{
@@ -2049,19 +2207,16 @@ static void init_stmt_after_parse(LEX *lex)
sl->uncacheable&= ~UNCACHEABLE_PREPARE;
}
-/*
+/**
SQLCOM_PREPARE implementation.
- SYNOPSIS
- mysql_sql_stmt_prepare()
- thd thread handle
-
- DESCRIPTION
Prepare an SQL prepared statement. This is called from
mysql_execute_command and should therefore behave like an
ordinary query (e.g. should not reset any global THD data).
- RETURN VALUE
+ @param thd thread handle
+
+ @return
none: in case of success, OK packet is sent to the client,
otherwise an error message is set in THD
*/
@@ -2074,8 +2229,8 @@ void mysql_sql_stmt_prepare(THD *thd)
const char *query;
uint query_len;
DBUG_ENTER("mysql_sql_stmt_prepare");
- DBUG_ASSERT(thd->protocol == &thd->protocol_simple);
LINT_INIT(query_len);
+ DBUG_ASSERT(thd->protocol == &thd->protocol_text);
if ((stmt= (Prepared_statement*) thd->stmt_map.find_by_name(name)))
{
@@ -2083,12 +2238,17 @@ void mysql_sql_stmt_prepare(THD *thd)
If there is a statement with the same name, remove it. It is ok to
remove old and fail to insert a new one at the same time.
*/
- if (stmt->deallocate())
+ if (stmt->is_in_use())
+ {
+ my_error(ER_PS_NO_RECURSION, MYF(0));
DBUG_VOID_RETURN;
+ }
+
+ stmt->deallocate();
}
if (! (query= get_dynamic_sql_string(lex, &query_len)) ||
- ! (stmt= new Prepared_statement(thd, &thd->protocol_simple)))
+ ! (stmt= new Prepared_statement(thd, &thd->protocol_text)))
{
DBUG_VOID_RETURN; /* out of memory */
}
@@ -2106,18 +2266,25 @@ void mysql_sql_stmt_prepare(THD *thd)
DBUG_VOID_RETURN;
}
- if (stmt->prepare(query, query_len+1))
+ if (stmt->prepare(query, query_len))
{
/* Statement map deletes the statement on erase */
thd->stmt_map.erase(stmt);
}
else
- send_ok(thd, 0L, 0L, "Statement prepared");
+ my_ok(thd, 0L, 0L, "Statement prepared");
DBUG_VOID_RETURN;
}
-/* Reinit prepared statement/stored procedure before execution */
+/**
+ Reinit prepared statement/stored procedure before execution.
+
+ @todo
+ When the new table structure is ready, then have a status bit
+ to indicate the table is altered, and re-do the setup_*
+ and open the tables back.
+*/
void reinit_stmt_before_use(THD *thd, LEX *lex)
{
@@ -2223,13 +2390,11 @@ void reinit_stmt_before_use(THD *thd, LEX *lex)
}
-/*
- Clears parameters from data left from previous execution or long data
+/**
+ Clears parameters from data left from previous execution or long data.
- SYNOPSIS
- reset_stmt_params()
- stmt prepared statement for which parameters should
- be reset
+ @param stmt prepared statement for which parameters should
+ be reset
*/
static void reset_stmt_params(Prepared_statement *stmt)
@@ -2241,22 +2406,19 @@ static void reset_stmt_params(Prepared_statement *stmt)
}
-/*
+/**
COM_STMT_EXECUTE handler: execute a previously prepared statement.
- SYNOPSIS
- mysql_stmt_execute()
- thd current thread
- packet parameter types and data, if any
- packet_length packet length, including the terminator character.
-
- DESCRIPTION
If there are any parameters, then replace parameter markers with the
data supplied from the client, and then execute the statement.
This function uses binary protocol to send a possible result set
to the client.
- RETURN VALUE
+ @param thd current thread
+ @param packet_arg parameter types and data, if any
+ @param packet_length packet length, including the terminator character.
+
+ @return
none: in case of success OK packet or a result set is sent to the
client, otherwise an error message is set in THD.
*/
@@ -2268,11 +2430,9 @@ void mysql_stmt_execute(THD *thd, char *packet_arg, uint packet_length)
ulong flags= (ulong) packet[4];
/* Query text for binary, general or slow log, if any of them is open */
String expanded_query;
-#ifndef EMBEDDED_LIBRARY
- uchar *packet_end= packet + packet_length - 1;
-#endif
+ uchar *packet_end= packet + packet_length;
Prepared_statement *stmt;
- bool error;
+ bool open_cursor;
DBUG_ENTER("mysql_stmt_execute");
packet+= 9; /* stmt_id + 5 bytes of flags */
@@ -2280,64 +2440,35 @@ void mysql_stmt_execute(THD *thd, char *packet_arg, uint packet_length)
/* First of all clear possible warnings from the previous command */
mysql_reset_thd_for_next_command(thd);
- if (!(stmt= find_prepared_statement(thd, stmt_id, "mysql_stmt_execute")))
+ if (!(stmt= find_prepared_statement(thd, stmt_id)))
+ {
+ char llbuf[22];
+ my_error(ER_UNKNOWN_STMT_HANDLER, MYF(0), sizeof(llbuf),
+ llstr(stmt_id, llbuf), "mysql_stmt_execute");
DBUG_VOID_RETURN;
+ }
+#if defined(ENABLED_PROFILING) && defined(COMMUNITY_SERVER)
+ thd->profiling.set_query_source(stmt->query, stmt->query_length);
+#endif
DBUG_PRINT("exec_query", ("%s", stmt->query));
- DBUG_PRINT("info",("stmt: %p", stmt));
+ DBUG_PRINT("info",("stmt: 0x%lx", (long) stmt));
sp_cache_flush_obsolete(&thd->sp_proc_cache);
sp_cache_flush_obsolete(&thd->sp_func_cache);
-#ifndef EMBEDDED_LIBRARY
- if (stmt->param_count)
- {
- uchar *null_array= packet;
- if (setup_conversion_functions(stmt, &packet, packet_end) ||
- stmt->set_params(stmt, null_array, packet, packet_end,
- &expanded_query))
- goto set_params_data_err;
- }
-#else
- /*
- In embedded library we re-install conversion routines each time
- we set params, and also we don't need to parse packet.
- So we do it in one function.
- */
- if (stmt->param_count && stmt->set_params_data(stmt, &expanded_query))
- goto set_params_data_err;
-#endif
- if (!(specialflag & SPECIAL_NO_PRIOR))
- my_pthread_setprio(pthread_self(),QUERY_PRIOR);
+ open_cursor= test(flags & (ulong) CURSOR_TYPE_READ_ONLY);
- /*
- If the free_list is not empty, we'll wrongly free some externally
- allocated items when cleaning up after validation of the prepared
- statement.
- */
- DBUG_ASSERT(thd->free_list == NULL);
+ stmt->execute_loop(&expanded_query, open_cursor, packet, packet_end);
- error= stmt->execute(&expanded_query,
- test(flags & (ulong) CURSOR_TYPE_READ_ONLY));
- if (!(specialflag & SPECIAL_NO_PRIOR))
- my_pthread_setprio(pthread_self(), WAIT_PRIOR);
DBUG_VOID_RETURN;
-set_params_data_err:
- my_error(ER_WRONG_ARGUMENTS, MYF(0), "mysql_stmt_execute");
- reset_stmt_params(stmt);
- DBUG_VOID_RETURN;
}
-/*
+/**
SQLCOM_EXECUTE implementation.
- SYNOPSIS
- mysql_sql_stmt_execute()
- thd thread handle
-
- DESCRIPTION
Execute prepared statement using parameter values from
lex->prepared_stmt_params and send result to the client using
text protocol. This is called from mysql_execute_command and
@@ -2345,7 +2476,9 @@ set_params_data_err:
global THD data, such as warning count, server status, etc).
This function uses text protocol to send a possible result set.
- RETURN
+ @param thd thread handle
+
+ @return
none: in case of success, OK (or result set) packet is sent to the
client, otherwise an error is set in THD
*/
@@ -2358,7 +2491,7 @@ void mysql_sql_stmt_execute(THD *thd)
/* Query text for binary, general or slow log, if any of them is open */
String expanded_query;
DBUG_ENTER("mysql_sql_stmt_execute");
- DBUG_PRINT("info", ("EXECUTE: %.*s\n", name->length, name->str));
+ DBUG_PRINT("info", ("EXECUTE: %.*s\n", (int) name->length, name->str));
if (!(stmt= (Prepared_statement*) thd->stmt_map.find_by_name(name)))
{
@@ -2373,38 +2506,20 @@ void mysql_sql_stmt_execute(THD *thd)
DBUG_VOID_RETURN;
}
- DBUG_PRINT("info",("stmt: %p", stmt));
+ DBUG_PRINT("info",("stmt: 0x%lx", (long) stmt));
- /*
- If the free_list is not empty, we'll wrongly free some externally
- allocated items when cleaning up after validation of the prepared
- statement.
- */
- DBUG_ASSERT(thd->free_list == NULL);
-
- if (stmt->set_params_from_vars(stmt, lex->prepared_stmt_params,
- &expanded_query))
- goto set_params_data_err;
-
- (void) stmt->execute(&expanded_query, FALSE);
+ (void) stmt->execute_loop(&expanded_query, FALSE, NULL, NULL);
DBUG_VOID_RETURN;
-
-set_params_data_err:
- my_error(ER_WRONG_ARGUMENTS, MYF(0), "EXECUTE");
- reset_stmt_params(stmt);
- DBUG_VOID_RETURN;
}
-/*
- COM_STMT_FETCH handler: fetches requested amount of rows from cursor
+/**
+ COM_STMT_FETCH handler: fetches requested amount of rows from cursor.
- SYNOPSIS
- mysql_stmt_fetch()
- thd Thread handle
- packet Packet from client (with stmt_id & num_rows)
- packet_length Length of packet
+ @param thd Thread handle
+ @param packet Packet from client (with stmt_id & num_rows)
+ @param packet_length Length of packet
*/
void mysql_stmt_fetch(THD *thd, char *packet, uint packet_length)
@@ -2419,9 +2534,14 @@ void mysql_stmt_fetch(THD *thd, char *packet, uint packet_length)
/* First of all clear possible warnings from the previous command */
mysql_reset_thd_for_next_command(thd);
- statistic_increment(thd->status_var.com_stmt_fetch, &LOCK_status);
- if (!(stmt= find_prepared_statement(thd, stmt_id, "mysql_stmt_fetch")))
+ status_var_increment(thd->status_var.com_stmt_fetch);
+ if (!(stmt= find_prepared_statement(thd, stmt_id)))
+ {
+ char llbuf[22];
+ my_error(ER_UNKNOWN_STMT_HANDLER, MYF(0), sizeof(llbuf),
+ llstr(stmt_id, llbuf), "mysql_stmt_fetch");
DBUG_VOID_RETURN;
+ }
cursor= stmt->cursor;
if (!cursor)
@@ -2455,22 +2575,20 @@ void mysql_stmt_fetch(THD *thd, char *packet, uint packet_length)
}
-/*
+/**
Reset a prepared statement in case there was a recoverable error.
- SYNOPSIS
- mysql_stmt_reset()
- thd Thread handle
- packet Packet with stmt id
- DESCRIPTION
This function resets statement to the state it was right after prepare.
It can be used to:
- - clear an error happened during mysql_stmt_send_long_data
- - cancel long data stream for all placeholders without
- having to call mysql_stmt_execute.
- - close an open cursor
+ - clear an error happened during mysql_stmt_send_long_data
+ - cancel long data stream for all placeholders without
+ having to call mysql_stmt_execute.
+ - close an open cursor
Sends 'OK' packet in case of success (statement was reset)
or 'ERROR' packet (unrecoverable error/statement not found/etc).
+
+ @param thd Thread handle
+ @param packet Packet with stmt id
*/
void mysql_stmt_reset(THD *thd, char *packet)
@@ -2483,9 +2601,14 @@ void mysql_stmt_reset(THD *thd, char *packet)
/* First of all clear possible warnings from the previous command */
mysql_reset_thd_for_next_command(thd);
- statistic_increment(thd->status_var.com_stmt_reset, &LOCK_status);
- if (!(stmt= find_prepared_statement(thd, stmt_id, "mysql_stmt_reset")))
+ status_var_increment(thd->status_var.com_stmt_reset);
+ if (!(stmt= find_prepared_statement(thd, stmt_id)))
+ {
+ char llbuf[22];
+ my_error(ER_UNKNOWN_STMT_HANDLER, MYF(0), sizeof(llbuf),
+ llstr(stmt_id, llbuf), "mysql_stmt_reset");
DBUG_VOID_RETURN;
+ }
stmt->close_cursor();
@@ -2497,15 +2620,19 @@ void mysql_stmt_reset(THD *thd, char *packet)
stmt->state= Query_arena::PREPARED;
- send_ok(thd);
+ general_log_print(thd, thd->command, NullS);
+
+ my_ok(thd);
DBUG_VOID_RETURN;
}
-/*
+/**
Delete a prepared statement from memory.
- Note: we don't send any reply to this command.
+
+ @note
+ we don't send any reply to this command.
*/
void mysql_stmt_close(THD *thd, char *packet)
@@ -2515,32 +2642,31 @@ void mysql_stmt_close(THD *thd, char *packet)
Prepared_statement *stmt;
DBUG_ENTER("mysql_stmt_close");
- if (!(stmt= find_prepared_statement(thd, stmt_id, "mysql_stmt_close")))
- goto out;
+ thd->main_da.disable_status();
+
+ if (!(stmt= find_prepared_statement(thd, stmt_id)))
+ DBUG_VOID_RETURN;
/*
The only way currently a statement can be deallocated when it's
in use is from within Dynamic SQL.
*/
- DBUG_ASSERT(! (stmt->flags & (uint) Prepared_statement::IS_IN_USE));
- (void) stmt->deallocate();
+ DBUG_ASSERT(! stmt->is_in_use());
+ stmt->deallocate();
+ general_log_print(thd, thd->command, NullS);
-out:
- /* clear errors, response packet is not expected */
- thd->clear_error();
DBUG_VOID_RETURN;
}
-/*
+/**
SQLCOM_DEALLOCATE implementation.
- DESCRIPTION
Close an SQL prepared statement. As this can be called from Dynamic
SQL, we should be careful to not close a statement that is currently
being executed.
- RETURN VALUE
+ @return
none: OK packet is sent in case of success, otherwise an error
message is set in THD
*/
@@ -2549,34 +2675,33 @@ void mysql_sql_stmt_close(THD *thd)
{
Prepared_statement* stmt;
LEX_STRING *name= &thd->lex->prepared_stmt_name;
- DBUG_PRINT("info", ("DEALLOCATE PREPARE: %.*s\n", name->length, name->str));
+ DBUG_PRINT("info", ("DEALLOCATE PREPARE: %.*s\n", (int) name->length,
+ name->str));
if (! (stmt= (Prepared_statement*) thd->stmt_map.find_by_name(name)))
- {
my_error(ER_UNKNOWN_STMT_HANDLER, MYF(0),
name->length, name->str, "DEALLOCATE PREPARE");
- return;
+ else if (stmt->is_in_use())
+ my_error(ER_PS_NO_RECURSION, MYF(0));
+ else
+ {
+ stmt->deallocate();
+ my_ok(thd);
}
-
- if (stmt->deallocate() == 0)
- send_ok(thd);
}
-/*
+/**
Handle long data in pieces from client.
- SYNOPSIS
- mysql_stmt_get_longdata()
- thd Thread handle
- packet String to append
- packet_length Length of string (including end \0)
-
- DESCRIPTION
Get a part of a long data. To make the protocol efficient, we are
not sending any return packets here. If something goes wrong, then
we will send the error on 'execute' We assume that the client takes
care of checking that all parts are sent to the server. (No checking
that we get a 'end of column' in the server is performed).
+
+ @param thd Thread handle
+ @param packet String to append
+ @param packet_length Length of string (including end \\0)
*/
void mysql_stmt_get_longdata(THD *thd, char *packet, ulong packet_length)
@@ -2586,23 +2711,24 @@ void mysql_stmt_get_longdata(THD *thd, char *packet, ulong packet_length)
Prepared_statement *stmt;
Item_param *param;
#ifndef EMBEDDED_LIBRARY
- char *packet_end= packet + packet_length - 1;
+ char *packet_end= packet + packet_length;
#endif
DBUG_ENTER("mysql_stmt_get_longdata");
- statistic_increment(thd->status_var.com_stmt_send_long_data, &LOCK_status);
+ status_var_increment(thd->status_var.com_stmt_send_long_data);
+
+ thd->main_da.disable_status();
#ifndef EMBEDDED_LIBRARY
/* Minimal size of long data packet is 6 bytes */
- if (packet_length <= MYSQL_LONG_DATA_HEADER)
- goto out;
+ if (packet_length < MYSQL_LONG_DATA_HEADER)
+ DBUG_VOID_RETURN;
#endif
stmt_id= uint4korr(packet);
packet+= 4;
- if (!(stmt=find_prepared_statement(thd, stmt_id,
- "mysql_stmt_send_long_data")))
- goto out;
+ if (!(stmt=find_prepared_statement(thd, stmt_id)))
+ DBUG_VOID_RETURN;
param_number= uint2korr(packet);
packet+= 2;
@@ -2614,7 +2740,7 @@ void mysql_stmt_get_longdata(THD *thd, char *packet, ulong packet_length)
stmt->last_errno= ER_WRONG_ARGUMENTS;
sprintf(stmt->last_error, ER(ER_WRONG_ARGUMENTS),
"mysql_stmt_send_long_data");
- goto out;
+ DBUG_VOID_RETURN;
}
#endif
@@ -2631,22 +2757,21 @@ void mysql_stmt_get_longdata(THD *thd, char *packet, ulong packet_length)
sprintf(stmt->last_error, ER(ER_OUTOFMEMORY), 0);
}
-out:
- /* clear errors, response packet is not expected */
- thd->clear_error();
+ general_log_print(thd, thd->command, NullS);
+
DBUG_VOID_RETURN;
}
/***************************************************************************
- Select_fetch_protocol_prep
+ Select_fetch_protocol_binary
****************************************************************************/
-Select_fetch_protocol_prep::Select_fetch_protocol_prep(THD *thd_arg)
+Select_fetch_protocol_binary::Select_fetch_protocol_binary(THD *thd_arg)
:protocol(thd_arg)
{}
-bool Select_fetch_protocol_prep::send_fields(List<Item> &list, uint flags)
+bool Select_fetch_protocol_binary::send_fields(List<Item> &list, uint flags)
{
bool rc;
Protocol *save_protocol= thd->protocol;
@@ -2664,19 +2789,15 @@ bool Select_fetch_protocol_prep::send_fields(List<Item> &list, uint flags)
return rc;
}
-bool Select_fetch_protocol_prep::send_eof()
+bool Select_fetch_protocol_binary::send_eof()
{
- Protocol *save_protocol= thd->protocol;
-
- thd->protocol= &protocol;
- ::send_eof(thd);
- thd->protocol= save_protocol;
+ ::my_eof(thd);
return FALSE;
}
bool
-Select_fetch_protocol_prep::send_data(List<Item> &fields)
+Select_fetch_protocol_binary::send_data(List<Item> &fields)
{
Protocol *save_protocol= thd->protocol;
bool rc;
@@ -2692,7 +2813,7 @@ Select_fetch_protocol_prep::send_data(List<Item> &fields)
****************************************************************************/
Prepared_statement::Prepared_statement(THD *thd_arg, Protocol *protocol_arg)
- :Statement(&main_lex, &main_mem_root,
+ :Statement(NULL, &main_mem_root,
INITIALIZED, ++thd_arg->statement_id_counter),
thd(thd_arg),
result(thd_arg),
@@ -2700,7 +2821,8 @@ Prepared_statement::Prepared_statement(THD *thd_arg, Protocol *protocol_arg)
param_array(0),
param_count(0),
last_errno(0),
- flags((uint) IS_IN_USE)
+ flags((uint) IS_IN_USE),
+ m_sp_cache_version(0)
{
init_sql_alloc(&main_mem_root, thd_arg->variables.query_alloc_block_size,
thd_arg->variables.query_prealloc_size);
@@ -2710,15 +2832,26 @@ Prepared_statement::Prepared_statement(THD *thd_arg, Protocol *protocol_arg)
void Prepared_statement::setup_set_params()
{
- /* Setup binary logging */
+ /*
+ Note: BUG#25843 applies here too (query cache lookup uses thd->db, not
+ db from "prepare" time).
+ */
+ if (query_cache_maybe_disabled(thd)) // we won't expand the query
+ lex->safe_to_cache_query= FALSE; // so don't cache it at Execution
+
+ /*
+ Decide if we have to expand the query (because we must write it to logs or
+ because we want to look it up in the query cache) or not.
+ */
if (mysql_bin_log.is_open() && is_update_query(lex->sql_command) ||
- mysql_log.is_open() || mysql_slow_log.is_open())
+ opt_log || opt_slow_log ||
+ query_cache_is_cacheable_query(lex))
{
set_params_from_vars= insert_params_from_vars_with_log;
#ifndef EMBEDDED_LIBRARY
- set_params= insert_params_withlog;
+ set_params= insert_params_with_log;
#else
- set_params_data= emb_insert_params_withlog;
+ set_params_data= emb_insert_params_with_log;
#endif
}
else
@@ -2733,25 +2866,30 @@ void Prepared_statement::setup_set_params()
}
-/*
- DESCRIPTION
- Destroy this prepared statement, cleaning up all used memory
- and resources. This is called from ::deallocate() to
- handle COM_STMT_CLOSE and DEALLOCATE PREPARE or when
- THD ends and all prepared statements are freed.
+/**
+ Destroy this prepared statement, cleaning up all used memory
+ and resources.
+
+ This is called from ::deallocate() to handle COM_STMT_CLOSE and
+ DEALLOCATE PREPARE or when THD ends and all prepared statements are freed.
*/
Prepared_statement::~Prepared_statement()
{
DBUG_ENTER("Prepared_statement::~Prepared_statement");
- DBUG_PRINT("enter",("stmt: %p cursor: %p", this, cursor));
+ DBUG_PRINT("enter",("stmt: 0x%lx cursor: 0x%lx",
+ (long) this, (long) cursor));
delete cursor;
/*
We have to call free on the items even if cleanup is called as some items,
like Item_param, don't free everything until free_items()
*/
free_items();
- delete lex->result;
+ if (lex)
+ {
+ delete lex->result;
+ delete (st_lex_local *) lex;
+ }
free_root(&main_mem_root, MYF(0));
DBUG_VOID_RETURN;
}
@@ -2766,7 +2904,7 @@ Query_arena::Type Prepared_statement::type() const
void Prepared_statement::cleanup_stmt()
{
DBUG_ENTER("Prepared_statement::cleanup_stmt");
- DBUG_PRINT("enter",("stmt: %p", this));
+ DBUG_PRINT("enter",("stmt: 0x%lx", (long) this));
DBUG_ASSERT(lex->sphead == 0);
/* The order is important */
@@ -2783,10 +2921,38 @@ void Prepared_statement::cleanup_stmt()
bool Prepared_statement::set_name(LEX_STRING *name_arg)
{
name.length= name_arg->length;
- name.str= memdup_root(mem_root, (char*) name_arg->str, name_arg->length);
+ name.str= (char*) memdup_root(mem_root, name_arg->str, name_arg->length);
return name.str == 0;
}
+
+/**
+ Remember the current database.
+
+ We must reset/restore the current database during execution of
+ a prepared statement since it affects execution environment:
+ privileges, @@character_set_database, and other.
+
+ @return Returns an error if out of memory.
+*/
+
+bool
+Prepared_statement::set_db(const char *db_arg, uint db_length_arg)
+{
+ /* Remember the current database. */
+ if (db_arg && db_length_arg)
+ {
+ db= this->strmake(db_arg, db_length_arg);
+ db_length= db_length_arg;
+ }
+ else
+ {
+ db= NULL;
+ db_length= 0;
+ }
+ return db_arg != NULL && db == NULL;
+}
+
/**************************************************************************
Common parts of mysql_[sql]_stmt_prepare, mysql_[sql]_stmt_execute.
Essentially, these functions do all the magic of preparing/executing
@@ -2794,28 +2960,24 @@ bool Prepared_statement::set_name(LEX_STRING *name_arg)
global THD state management to the caller.
***************************************************************************/
-/*
+/**
Parse statement text, validate the statement, and prepare it for execution.
- SYNOPSIS
- Prepared_statement::prepare()
- packet statement text
- packet_len
-
- DESCRIPTION
You should not change global THD state in this function, if at all
possible: it may be called from any context, e.g. when executing
a COM_* command, and SQLCOM_* command, or a stored procedure.
- NOTES
- Precondition.
- -------------
+ @param packet statement text
+ @param packet_len
+
+ @note
+ Precondition:
The caller must ensure that thd->change_list and thd->free_list
is empty: this function will not back them up but will free
in the end of its execution.
- Postcondition.
- --------------
+ @note
+ Postcondition:
thd->mem_root contains unused memory allocated during validation.
*/
@@ -2830,7 +2992,13 @@ bool Prepared_statement::prepare(const char *packet, uint packet_len)
However, it seems handy if com_stmt_prepare is increased always,
no matter what kind of prepare is processed.
*/
- statistic_increment(thd->status_var.com_stmt_prepare, &LOCK_status);
+ status_var_increment(thd->status_var.com_stmt_prepare);
+
+ if (! (lex= new (mem_root) st_lex_local))
+ DBUG_RETURN(TRUE);
+
+ if (set_db(thd->db, thd->db_length))
+ DBUG_RETURN(TRUE);
/*
alloc_query() uses thd->memroot && thd->query, so we should call
@@ -2851,15 +3019,13 @@ bool Prepared_statement::prepare(const char *packet, uint packet_len)
Parser_state parser_state(thd, thd->query, thd->query_length);
parser_state.m_lip.stmt_prepare_mode= TRUE;
- thd->m_parser_state= &parser_state;
lex_start(thd);
- lex->safe_to_cache_query= FALSE;
- int err= MYSQLparse((void *)thd);
- thd->m_parser_state= NULL;
- lex->set_trg_event_type_for_tables();
- error= err || thd->is_fatal_error ||
- thd->net.report_error || init_param_array(this);
+ error= parse_sql(thd, & parser_state, NULL) ||
+ thd->is_error() ||
+ init_param_array(this);
+
+ lex->set_trg_event_type_for_tables();
/*
While doing context analysis of the query (in check_prepared_statement)
@@ -2884,7 +3050,7 @@ bool Prepared_statement::prepare(const char *packet, uint packet_len)
*/
if (error == 0)
- error= check_prepared_statement(this, name.str != 0);
+ error= check_prepared_statement(this);
/*
Currently CREATE PROCEDURE/TRIGGER/EVENT are prohibited in prepared
@@ -2909,6 +3075,20 @@ bool Prepared_statement::prepare(const char *packet, uint packet_len)
init_stmt_after_parse(lex);
state= Query_arena::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
@@ -2926,38 +3106,330 @@ bool Prepared_statement::prepare(const char *packet, uint packet_len)
the general log.
*/
if (thd->spcont == NULL)
- {
- const char *format= "[%lu] %.*b";
- mysql_log.write(thd, COM_STMT_PREPARE, format, id,
- query_length, query);
-
- }
+ general_log_write(thd, COM_STMT_PREPARE, query, query_length);
}
DBUG_RETURN(error);
}
-/*
- Execute a prepared statement.
- SYNOPSIS
- Prepared_statement::execute()
- expanded_query A query for binlogging which has all parameter
- markers ('?') replaced with their actual values.
- open_cursor True if an attempt to open a cursor should be made.
- Currenlty used only in the binary protocol.
+/**
+ Assign parameter values either from variables, in case of SQL PS
+ or from the execute packet.
+
+ @param expanded_query a container with the original SQL statement.
+ '?' placeholders will be replaced with
+ their values in case of success.
+ The result is used for logging and replication
+ @param packet pointer to execute packet.
+ NULL in case of SQL PS
+ @param packet_end end of the packet. NULL in case of SQL PS
+
+ @todo Use a paremeter source class family instead of 'if's, and
+ support stored procedure variables.
+
+ @retval TRUE an error occurred when assigning a parameter (likely
+ a conversion error or out of memory, or malformed packet)
+ @retval FALSE success
+*/
+
+bool
+Prepared_statement::set_parameters(String *expanded_query,
+ uchar *packet, uchar *packet_end)
+{
+ bool is_sql_ps= packet == NULL;
+ bool res= FALSE;
+
+ if (is_sql_ps)
+ {
+ /* SQL prepared statement */
+ res= set_params_from_vars(this, thd->lex->prepared_stmt_params,
+ expanded_query);
+ }
+ else if (param_count)
+ {
+#ifndef EMBEDDED_LIBRARY
+ uchar *null_array= packet;
+ res= (setup_conversion_functions(this, &packet, packet_end) ||
+ set_params(this, null_array, packet, packet_end, expanded_query));
+#else
+ /*
+ In embedded library we re-install conversion routines each time
+ we set parameters, and also we don't need to parse packet.
+ So we do it in one function.
+ */
+ res= set_params_data(this, expanded_query);
+#endif
+ }
+ if (res)
+ {
+ my_error(ER_WRONG_ARGUMENTS, MYF(0),
+ is_sql_ps ? "EXECUTE" : "mysql_stmt_execute");
+ reset_stmt_params(this);
+ }
+ return res;
+}
+
+
+/**
+ Execute a prepared statement. Re-prepare it a limited number
+ of times if necessary.
+
+ Try to execute a prepared statement. If there is a metadata
+ validation error, prepare a new copy of the prepared statement,
+ swap the old and the new statements, and try again.
+ If there is a validation error again, repeat the above, but
+ perform no more than MAX_REPREPARE_ATTEMPTS.
+
+ @note We have to try several times in a loop since we
+ release metadata locks on tables after prepared statement
+ prepare. Therefore, a DDL statement may sneak in between prepare
+ 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
+ @retval FALSE successfully executed the statement, perhaps
+ after having reprepared it a few times.
+*/
+
+bool
+Prepared_statement::execute_loop(String *expanded_query,
+ bool open_cursor,
+ uchar *packet,
+ uchar *packet_end)
+{
+ const int MAX_REPREPARE_ATTEMPTS= 3;
+ Reprepare_observer reprepare_observer;
+ bool error;
+ int reprepare_attempt= 0;
+
+ if (set_parameters(expanded_query, packet, packet_end))
+ return TRUE;
+
+reexecute:
+ reprepare_observer.reset_reprepare_observer();
+
+ /*
+ If the free_list is not empty, we'll wrongly free some externally
+ allocated items when cleaning up after validation of the prepared
+ statement.
+ */
+ DBUG_ASSERT(thd->free_list == NULL);
+
+ /*
+ Install the metadata observer. If some metadata version is
+ different from prepare time and an observer is installed,
+ the observer method will be invoked to push an error into
+ the error stack.
+ */
+ if (sql_command_flags[lex->sql_command] &
+ CF_REEXECUTION_FRAGILE)
+ {
+ DBUG_ASSERT(thd->m_reprepare_observer == NULL);
+ 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);
+ thd->clear_error();
+
+ error= reprepare();
+
+ if (! error) /* Success */
+ goto reexecute;
+ }
+ reset_stmt_params(this);
+
+ return error;
+}
+
+
+/**
+ Reprepare this prepared statement.
+
+ Currently this is implemented by creating a new prepared
+ statement, preparing it with the original query and then
+ swapping the new statement and the original one.
+
+ @retval TRUE an error occurred. Possible errors include
+ incompatibility of new and old result set
+ metadata
+ @retval FALSE success, the statement has been reprepared
+*/
+
+bool
+Prepared_statement::reprepare()
+{
+ char saved_cur_db_name_buf[NAME_LEN+1];
+ LEX_STRING saved_cur_db_name=
+ { saved_cur_db_name_buf, sizeof(saved_cur_db_name_buf) };
+ LEX_STRING stmt_db_name= { db, db_length };
+ bool cur_db_changed;
+ bool error;
+
+ Prepared_statement copy(thd, &thd->protocol_text);
+
+ status_var_increment(thd->status_var.com_stmt_reprepare);
+
+ if (mysql_opt_change_db(thd, &stmt_db_name, &saved_cur_db_name, TRUE,
+ &cur_db_changed))
+ return TRUE;
+
+ error= (name.str && copy.set_name(&name) ||
+ copy.prepare(query, query_length) ||
+ validate_metadata(&copy));
+
+ if (cur_db_changed)
+ mysql_change_db(thd, &saved_cur_db_name, TRUE);
+
+ if (! error)
+ {
+ swap_prepared_statement(&copy);
+ swap_parameter_array(param_array, copy.param_array, param_count);
+#ifndef DBUG_OFF
+ is_reprepared= TRUE;
+#endif
+ /*
+ Clear possible warnings during reprepare, it has to be completely
+ transparent to the user. We use mysql_reset_errors() 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);
+ }
+ return error;
+}
+
+
+/**
+ Validate statement result set metadata (if the statement returns
+ a result set).
+
+ Currently we only check that the number of columns of the result
+ set did not change.
+ This is a helper method used during re-prepare.
+
+ @param[in] copy the re-prepared prepared statement to verify
+ the metadata of
+
+ @retval TRUE error, ER_PS_REBIND is reported
+ @retval FALSE statement return no or compatible metadata
+*/
+
+
+bool Prepared_statement::validate_metadata(Prepared_statement *copy)
+{
+ /**
+ If this is an SQL prepared statement or EXPLAIN,
+ return FALSE -- the metadata of the original SELECT,
+ if any, has not been sent to the client.
+ */
+ if (is_protocol_text() || lex->describe)
+ return FALSE;
+
+ if (lex->select_lex.item_list.elements !=
+ copy->lex->select_lex.item_list.elements)
+ {
+ /** Column counts mismatch, update the client */
+ thd->server_status|= SERVER_STATUS_METADATA_CHANGED;
+ }
+
+ return FALSE;
+}
+
+
+/**
+ Replace the original prepared statement with a prepared copy.
+
+ This is a private helper that is used as part of statement
+ reprepare
+
+ @return This function does not return any errors.
+*/
+
+void
+Prepared_statement::swap_prepared_statement(Prepared_statement *copy)
+{
+ Statement tmp_stmt;
+
+ /* Swap memory roots. */
+ swap_variables(MEM_ROOT, main_mem_root, copy->main_mem_root);
+
+ /* Swap the arenas */
+ tmp_stmt.set_query_arena(this);
+ set_query_arena(copy);
+ copy->set_query_arena(&tmp_stmt);
+
+ /* Swap the statement parent classes */
+ tmp_stmt.set_statement(this);
+ set_statement(copy);
+ copy->set_statement(&tmp_stmt);
+
+ /* Swap ids back, we need the original id */
+ swap_variables(ulong, id, copy->id);
+ /* Swap mem_roots back, they must continue pointing at the main_mem_roots */
+ swap_variables(MEM_ROOT *, mem_root, copy->mem_root);
+ /*
+ Swap the old and the new parameters array. The old array
+ 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);
+ /* 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);
+ DBUG_ASSERT(thd == copy->thd);
+ last_error[0]= '\0';
+ last_errno= 0;
+ /* Do not swap protocols, the copy always has protocol_text */
+}
+
+
+/**
+ Execute a prepared statement.
- DESCRIPTION
You should not change global THD state in this function, if at all
possible: it may be called from any context, e.g. when executing
a COM_* command, and SQLCOM_* command, or a stored procedure.
- NOTES
- Preconditions, postconditions.
- ------------------------------
- See the comment for Prepared_statement::prepare().
+ @param expanded_query A query for binlogging which has all parameter
+ markers ('?') replaced with their actual values.
+ @param open_cursor True if an attempt to open a cursor should be made.
+ Currenlty used only in the binary protocol.
- RETURN
- FALSE ok
+ @note
+ Preconditions, postconditions.
+ - See the comment for Prepared_statement::prepare().
+
+ @retval
+ FALSE ok
+ @retval
TRUE Error
*/
@@ -2967,7 +3439,14 @@ bool Prepared_statement::execute(String *expanded_query, bool open_cursor)
Query_arena *old_stmt_arena;
bool error= TRUE;
- statistic_increment(thd->status_var.com_stmt_execute, &LOCK_status);
+ char saved_cur_db_name_buf[NAME_LEN+1];
+ LEX_STRING saved_cur_db_name=
+ { saved_cur_db_name_buf, sizeof(saved_cur_db_name_buf) };
+ bool cur_db_changed;
+
+ LEX_STRING stmt_db_name= { db, db_length };
+
+ status_var_increment(thd->status_var.com_stmt_execute);
/* Check if we got an error when sending long data */
if (state == Query_arena::ERROR)
@@ -2982,6 +3461,19 @@ 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
@@ -3015,9 +3507,24 @@ bool Prepared_statement::execute(String *expanded_query, bool open_cursor)
*/
thd->set_n_backup_statement(this, &stmt_backup);
+
+ /*
+ Change the current database (if needed).
+
+ Force switching, because the database of the prepared statement may be
+ NULL (prepared statements can be created while no current database
+ selected).
+ */
+
+ if (mysql_opt_change_db(thd, &stmt_db_name, &saved_cur_db_name, TRUE,
+ &cur_db_changed))
+ goto error;
+
+ /* Allocate query. */
+
if (expanded_query->length() &&
alloc_query(thd, (char*) expanded_query->ptr(),
- expanded_query->length()+1))
+ expanded_query->length()))
{
my_error(ER_OUTOFMEMORY, 0, expanded_query->length());
goto error;
@@ -3031,12 +3538,6 @@ bool Prepared_statement::execute(String *expanded_query, bool open_cursor)
stmt_backup.query_length= thd->query_length;
/*
- Save orig_sql_command as we use it to disable slow logging for SHOW
- commands (see log_slow_statement()).
- */
- stmt_backup.lex->orig_sql_command= thd->lex->orig_sql_command;
-
- /*
At first execution of prepared statement we may perform logical
transformations of the query tree. Such changes should be performed
on the parse tree of current prepared statement and new items should
@@ -3048,20 +3549,44 @@ bool Prepared_statement::execute(String *expanded_query, bool open_cursor)
reinit_stmt_before_use(thd, lex);
thd->protocol= protocol; /* activate stmt protocol */
- error= (open_cursor ?
- mysql_open_cursor(thd, (uint) ALWAYS_MATERIALIZED_CURSOR,
- &result, &cursor) :
- mysql_execute_command(thd));
- thd->protocol= &thd->protocol_simple; /* use normal protocol */
+
+ /* Go! */
+
+ if (open_cursor)
+ error= mysql_open_cursor(thd, (uint) ALWAYS_MATERIALIZED_CURSOR,
+ &result, &cursor);
+ else
+ {
+ /*
+ Try to find it in the query cache, if not, execute it.
+ Note that multi-statements cannot exist here (they are not supported in
+ prepared statements).
+ */
+ if (query_cache_send_result_to_client(thd, thd->query,
+ thd->query_length) <= 0)
+ {
+ error= mysql_execute_command(thd);
+ }
+ }
+
+ /*
+ Restore the current database (if changed).
+
+ Force switching back to the saved current database (if changed),
+ because it may be NULL. In this case, mysql_change_db() would generate
+ an error.
+ */
+
+ if (cur_db_changed)
+ mysql_change_db(thd, &saved_cur_db_name, TRUE);
+
+ thd->protocol= &thd->protocol_text; /* use normal protocol */
/* Assert that if an error, no cursor is open */
DBUG_ASSERT(! (error && cursor));
if (! cursor)
- {
cleanup_stmt();
- reset_stmt_params(this);
- }
thd->set_statement(&stmt_backup);
thd->stmt_arena= old_stmt_arena;
@@ -3085,11 +3610,7 @@ bool Prepared_statement::execute(String *expanded_query, bool open_cursor)
the general log.
*/
if (error == 0 && thd->spcont == NULL)
- {
- const char *format= "[%lu] %.*b";
- mysql_log.write(thd, COM_STMT_EXECUTE, format, id,
- thd->query_length, thd->query);
- }
+ general_log_write(thd, COM_STMT_EXECUTE, thd->query, thd->query_length);
error:
flags&= ~ (uint) IS_IN_USE;
@@ -3097,18 +3618,12 @@ error:
}
-/* Common part of DEALLOCATE PREPARE and mysql_stmt_close */
+/** Common part of DEALLOCATE PREPARE and mysql_stmt_close. */
-bool Prepared_statement::deallocate()
+void Prepared_statement::deallocate()
{
/* We account deallocate in the same manner as mysql_stmt_close */
- statistic_increment(thd->status_var.com_stmt_close, &LOCK_status);
- if (flags & (uint) IS_IN_USE)
- {
- my_error(ER_PS_NO_RECURSION, MYF(0));
- return TRUE;
- }
+ status_var_increment(thd->status_var.com_stmt_close);
/* Statement map calls delete stmt on erase */
thd->stmt_map.erase(this);
- return FALSE;
}
diff --git a/sql/sql_profile.cc b/sql/sql_profile.cc
new file mode 100644
index 00000000000..8c9b147089f
--- /dev/null
+++ b/sql/sql_profile.cc
@@ -0,0 +1,672 @@
+/* Copyright (C) 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+
+/**
+ @file
+
+ Implement query profiling as as list of metaphorical fences, with one fence
+ per query, and each fencepost a change of thd->proc_info state (with a
+ snapshot of system statistics). When asked, we can then iterate over the
+ fenceposts and calculate the distance between them, to inform the user what
+ happened during a particular query or thd->proc_info state.
+
+ User variables that inform profiling behavior:
+ - "profiling", boolean, session only, "Are queries profiled?"
+ - "profiling_history_size", integer, session + global, "Num queries stored?"
+*/
+
+
+#include "mysql_priv.h"
+#include "my_sys.h"
+
+#define TIME_FLOAT_DIGITS 9
+/** two vals encoded: (dec*100)+len */
+#define TIME_I_S_DECIMAL_SIZE (TIME_FLOAT_DIGITS*100)+(TIME_FLOAT_DIGITS-3)
+
+#define MAX_QUERY_LENGTH 300
+
+/* Reserved for systems that can't record the function name in source. */
+const char * const _unknown_func_ = "<unknown>";
+
+/**
+ Connects Information_Schema and Profiling.
+*/
+int fill_query_profile_statistics_info(THD *thd, TABLE_LIST *tables,
+ Item *cond)
+{
+#if defined(ENABLED_PROFILING) && defined(COMMUNITY_SERVER)
+ return(thd->profiling.fill_statistics_info(thd, tables, cond));
+#else
+ my_error(ER_FEATURE_DISABLED, MYF(0), "SHOW PROFILE", "enable-profiling");
+ return(1);
+#endif
+}
+
+ST_FIELD_INFO query_profile_statistics_info[]=
+{
+ /* name, length, type, value, maybe_null, old_name, open_method */
+ {"QUERY_ID", 20, MYSQL_TYPE_LONG, 0, false, "Query_id", SKIP_OPEN_TABLE},
+ {"SEQ", 20, MYSQL_TYPE_LONG, 0, false, "Seq", SKIP_OPEN_TABLE},
+ {"STATE", 30, MYSQL_TYPE_STRING, 0, false, "Status", SKIP_OPEN_TABLE},
+ {"DURATION", TIME_I_S_DECIMAL_SIZE, MYSQL_TYPE_DECIMAL, 0, false, "Duration", SKIP_OPEN_TABLE},
+ {"CPU_USER", TIME_I_S_DECIMAL_SIZE, MYSQL_TYPE_DECIMAL, 0, true, "CPU_user", SKIP_OPEN_TABLE},
+ {"CPU_SYSTEM", TIME_I_S_DECIMAL_SIZE, MYSQL_TYPE_DECIMAL, 0, true, "CPU_system", SKIP_OPEN_TABLE},
+ {"CONTEXT_VOLUNTARY", 20, MYSQL_TYPE_LONG, 0, true, "Context_voluntary", SKIP_OPEN_TABLE},
+ {"CONTEXT_INVOLUNTARY", 20, MYSQL_TYPE_LONG, 0, true, "Context_involuntary", SKIP_OPEN_TABLE},
+ {"BLOCK_OPS_IN", 20, MYSQL_TYPE_LONG, 0, true, "Block_ops_in", SKIP_OPEN_TABLE},
+ {"BLOCK_OPS_OUT", 20, MYSQL_TYPE_LONG, 0, true, "Block_ops_out", SKIP_OPEN_TABLE},
+ {"MESSAGES_SENT", 20, MYSQL_TYPE_LONG, 0, true, "Messages_sent", SKIP_OPEN_TABLE},
+ {"MESSAGES_RECEIVED", 20, MYSQL_TYPE_LONG, 0, true, "Messages_received", SKIP_OPEN_TABLE},
+ {"PAGE_FAULTS_MAJOR", 20, MYSQL_TYPE_LONG, 0, true, "Page_faults_major", SKIP_OPEN_TABLE},
+ {"PAGE_FAULTS_MINOR", 20, MYSQL_TYPE_LONG, 0, true, "Page_faults_minor", SKIP_OPEN_TABLE},
+ {"SWAPS", 20, MYSQL_TYPE_LONG, 0, true, "Swaps", SKIP_OPEN_TABLE},
+ {"SOURCE_FUNCTION", 30, MYSQL_TYPE_STRING, 0, true, "Source_function", SKIP_OPEN_TABLE},
+ {"SOURCE_FILE", 20, MYSQL_TYPE_STRING, 0, true, "Source_file", SKIP_OPEN_TABLE},
+ {"SOURCE_LINE", 20, MYSQL_TYPE_LONG, 0, true, "Source_line", SKIP_OPEN_TABLE},
+ {NULL, 0, MYSQL_TYPE_STRING, 0, true, NULL, 0}
+};
+
+
+int make_profile_table_for_show(THD *thd, ST_SCHEMA_TABLE *schema_table)
+{
+ int profile_options = thd->lex->profile_options;
+ int fields_include_condition_truth_values[]= {
+ FALSE, /* Query_id */
+ FALSE, /* Seq */
+ TRUE, /* Status */
+ TRUE, /* Duration */
+ profile_options & PROFILE_CPU, /* CPU_user */
+ profile_options & PROFILE_CPU, /* CPU_system */
+ profile_options & PROFILE_CONTEXT, /* Context_voluntary */
+ profile_options & PROFILE_CONTEXT, /* Context_involuntary */
+ profile_options & PROFILE_BLOCK_IO, /* Block_ops_in */
+ profile_options & PROFILE_BLOCK_IO, /* Block_ops_out */
+ profile_options & PROFILE_IPC, /* Messages_sent */
+ profile_options & PROFILE_IPC, /* Messages_received */
+ profile_options & PROFILE_PAGE_FAULTS, /* Page_faults_major */
+ profile_options & PROFILE_PAGE_FAULTS, /* Page_faults_minor */
+ profile_options & PROFILE_SWAPS, /* Swaps */
+ profile_options & PROFILE_SOURCE, /* Source_function */
+ profile_options & PROFILE_SOURCE, /* Source_file */
+ profile_options & PROFILE_SOURCE, /* Source_line */
+ };
+
+ ST_FIELD_INFO *field_info;
+ Name_resolution_context *context= &thd->lex->select_lex.context;
+ int i;
+
+ for (i= 0; schema_table->fields_info[i].field_name != NULL; i++)
+ {
+ if (! fields_include_condition_truth_values[i])
+ continue;
+
+ field_info= &schema_table->fields_info[i];
+ Item_field *field= new Item_field(context,
+ NullS, NullS, field_info->field_name);
+ if (field)
+ {
+ field->set_name(field_info->old_name,
+ (uint) strlen(field_info->old_name),
+ system_charset_info);
+ if (add_item_to_list(thd, field))
+ return 1;
+ }
+ }
+ return 0;
+}
+
+
+#if defined(ENABLED_PROFILING) && defined(COMMUNITY_SERVER)
+
+#define RUSAGE_USEC(tv) ((tv).tv_sec*1000*1000 + (tv).tv_usec)
+#define RUSAGE_DIFF_USEC(tv1, tv2) (RUSAGE_USEC((tv1))-RUSAGE_USEC((tv2)))
+
+
+PROF_MEASUREMENT::PROF_MEASUREMENT(QUERY_PROFILE *profile_arg, const char
+ *status_arg)
+ :profile(profile_arg)
+{
+ collect();
+ set_label(status_arg, NULL, NULL, 0);
+}
+
+PROF_MEASUREMENT::PROF_MEASUREMENT(QUERY_PROFILE *profile_arg,
+ const char *status_arg,
+ const char *function_arg,
+ const char *file_arg,
+ unsigned int line_arg)
+ :profile(profile_arg)
+{
+ collect();
+ set_label(status_arg, function_arg, file_arg, line_arg);
+}
+
+PROF_MEASUREMENT::~PROF_MEASUREMENT()
+{
+ if (allocated_status_memory != NULL)
+ my_free(allocated_status_memory, MYF(0));
+ status= function= file= NULL;
+}
+
+void PROF_MEASUREMENT::set_label(const char *status_arg,
+ const char *function_arg,
+ const char *file_arg, unsigned int line_arg)
+{
+ size_t sizes[3]; /* 3 == status+function+file */
+ char *cursor;
+
+ /*
+ Compute all the space we'll need to allocate one block for everything
+ we'll need, instead of N mallocs.
+ */
+ sizes[0]= (status_arg == NULL) ? 0 : strlen(status_arg) + 1;
+ sizes[1]= (function_arg == NULL) ? 0 : strlen(function_arg) + 1;
+ sizes[2]= (file_arg == NULL) ? 0 : strlen(file_arg) + 1;
+
+ allocated_status_memory= (char *) my_malloc(sizes[0] + sizes[1] + sizes[2], MYF(0));
+ DBUG_ASSERT(allocated_status_memory != NULL);
+
+ cursor= allocated_status_memory;
+
+ if (status_arg != NULL)
+ {
+ strcpy(cursor, status_arg);
+ status= cursor;
+ cursor+= sizes[0];
+ }
+ else
+ status= NULL;
+
+ if (function_arg != NULL)
+ {
+ strcpy(cursor, function_arg);
+ function= cursor;
+ cursor+= sizes[1];
+ }
+ else
+ function= NULL;
+
+ if (file_arg != NULL)
+ {
+ strcpy(cursor, file_arg);
+ file= cursor;
+ cursor+= sizes[2];
+ }
+ else
+ file= NULL;
+
+ line= line_arg;
+}
+
+/**
+ This updates the statistics for this moment of time. It captures the state
+ of the running system, so later we can compare points in time and infer what
+ happened in the mean time. It should only be called immediately upon
+ instantiation of this PROF_MEASUREMENT.
+
+ @todo Implement resource capture for OSes not like BSD.
+*/
+void PROF_MEASUREMENT::collect()
+{
+ time_usecs= (double) my_getsystime() / 10.0; /* 1 sec was 1e7, now is 1e6 */
+#ifdef HAVE_GETRUSAGE
+ getrusage(RUSAGE_SELF, &rusage);
+#endif
+}
+
+
+QUERY_PROFILE::QUERY_PROFILE(PROFILING *profiling_arg, const char *status_arg)
+ :profiling(profiling_arg), profiling_query_id(0), query_source(NULL)
+{
+ profile_start= new PROF_MEASUREMENT(this, status_arg);
+ entries.push_back(profile_start);
+ profile_end= profile_start;
+}
+
+QUERY_PROFILE::~QUERY_PROFILE()
+{
+ while (! entries.is_empty())
+ delete entries.pop();
+
+ if (query_source != NULL)
+ my_free(query_source, MYF(0));
+}
+
+/**
+ @todo Provide a way to include the full text, as in SHOW PROCESSLIST.
+*/
+void QUERY_PROFILE::set_query_source(char *query_source_arg,
+ uint query_length_arg)
+{
+ /* Truncate to avoid DoS attacks. */
+ uint length= min(MAX_QUERY_LENGTH, query_length_arg);
+
+ DBUG_ASSERT(query_source == NULL); /* we don't leak memory */
+ if (query_source_arg != NULL)
+ query_source= my_strndup(query_source_arg, length, MYF(0));
+}
+
+void QUERY_PROFILE::new_status(const char *status_arg,
+ const char *function_arg, const char *file_arg,
+ unsigned int line_arg)
+{
+ PROF_MEASUREMENT *prof;
+ DBUG_ENTER("QUERY_PROFILE::status");
+
+ 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);
+ else
+ prof= new PROF_MEASUREMENT(this, status_arg);
+
+ profile_end= prof;
+ entries.push_back(prof);
+
+ DBUG_VOID_RETURN;
+}
+
+
+
+PROFILING::PROFILING()
+ :profile_id_counter(1), current(NULL), last(NULL)
+{
+}
+
+PROFILING::~PROFILING()
+{
+ while (! history.is_empty())
+ delete history.pop();
+
+ if (current != NULL)
+ delete current;
+}
+
+/**
+ A new state is given, and that signals the profiler to start a new
+ timed step for the current query's profile.
+
+ @param status_arg name of this step
+ @param function_arg calling function (usually supplied from compiler)
+ @param function_arg calling file (usually supplied from compiler)
+ @param function_arg calling line number (usually supplied from compiler)
+*/
+void PROFILING::status_change(const char *status_arg,
+ const char *function_arg,
+ const char *file_arg, unsigned int line_arg)
+{
+ DBUG_ENTER("PROFILING::status_change");
+
+ if (status_arg == NULL) /* We don't know how to handle that */
+ DBUG_VOID_RETURN;
+
+ if (current == NULL) /* This profile was already discarded. */
+ DBUG_VOID_RETURN;
+
+ if (unlikely(enabled))
+ current->new_status(status_arg, function_arg, file_arg, line_arg);
+
+ DBUG_VOID_RETURN;
+}
+
+/**
+ Prepare to start processing a new query. It is an error to do this
+ if there's a query already in process; nesting is not supported.
+
+ @param initial_state (optional) name of period before first state change
+*/
+void PROFILING::start_new_query(const char *initial_state)
+{
+ DBUG_ENTER("PROFILING::start_new_query");
+
+ /* This should never happen unless the server is radically altered. */
+ if (unlikely(current != NULL))
+ {
+ DBUG_PRINT("warning", ("profiling code was asked to start a new query "
+ "before the old query was finished. This is "
+ "probably a bug."));
+ finish_current_query();
+ }
+
+ enabled= (((thd)->options & OPTION_PROFILING) != 0);
+
+ if (! enabled) DBUG_VOID_RETURN;
+
+ DBUG_ASSERT(current == NULL);
+ current= new QUERY_PROFILE(this, initial_state);
+
+ DBUG_VOID_RETURN;
+}
+
+/**
+ Throw away the current profile, because it's useless or unwanted
+ or corrupted.
+*/
+void PROFILING::discard_current_query()
+{
+ DBUG_ENTER("PROFILING::discard_current_profile");
+
+ delete current;
+ current= NULL;
+
+ DBUG_VOID_RETURN;
+}
+
+/**
+ Try to save the current profile entry, clean up the data if it shouldn't be
+ saved, and maintain the profile history size. Naturally, this may not
+ succeed if the profile was previously discarded, and that's expected.
+*/
+void PROFILING::finish_current_query()
+{
+ DBUG_ENTER("PROFILING::finish_current_profile");
+ if (current != NULL)
+ {
+ /* The last fence-post, so we can support the span before this. */
+ status_change("ending", NULL, NULL, 0);
+
+ if ((enabled) && /* ON at start? */
+ ((thd->options & OPTION_PROFILING) != 0) && /* and ON at end? */
+ (current->query_source != NULL) &&
+ (! current->entries.is_empty()))
+ {
+ current->profiling_query_id= next_profile_id(); /* assign an id */
+
+ history.push_back(current);
+ last= current; /* never contains something that is not in the history. */
+ current= NULL;
+ }
+ else
+ {
+ delete current;
+ current= NULL;
+ }
+ }
+
+ /* Maintain the history size. */
+ while (history.elements > thd->variables.profiling_history_size)
+ delete history.pop();
+
+ DBUG_VOID_RETURN;
+}
+
+bool PROFILING::show_profiles()
+{
+ DBUG_ENTER("PROFILING::show_profiles");
+ QUERY_PROFILE *prof;
+ List<Item> field_list;
+
+ field_list.push_back(new Item_return_int("Query_ID", 10,
+ MYSQL_TYPE_LONG));
+ field_list.push_back(new Item_return_int("Duration", TIME_FLOAT_DIGITS-1,
+ MYSQL_TYPE_DOUBLE));
+ field_list.push_back(new Item_empty_string("Query", 40));
+
+ if (thd->protocol->send_fields(&field_list,
+ Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
+ DBUG_RETURN(TRUE);
+
+ SELECT_LEX *sel= &thd->lex->select_lex;
+ SELECT_LEX_UNIT *unit= &thd->lex->unit;
+ ha_rows idx= 0;
+ Protocol *protocol= thd->protocol;
+
+ unit->set_limit(sel);
+
+ void *iterator;
+ for (iterator= history.new_iterator();
+ iterator != NULL;
+ iterator= history.iterator_next(iterator))
+ {
+ prof= history.iterator_value(iterator);
+
+ String elapsed;
+
+ PROF_MEASUREMENT *ps= prof->profile_start;
+ PROF_MEASUREMENT *pe= prof->profile_end;
+
+ if (++idx <= unit->offset_limit_cnt)
+ continue;
+ if (idx > unit->select_limit_cnt)
+ break;
+
+ protocol->prepare_for_resend();
+ protocol->store((uint32)(prof->profiling_query_id));
+ protocol->store((double)(pe->time_usecs - ps->time_usecs)/(1000.0*1000),
+ (uint32) TIME_FLOAT_DIGITS-1, &elapsed);
+ if (prof->query_source != NULL)
+ protocol->store(prof->query_source, strlen(prof->query_source),
+ system_charset_info);
+ else
+ protocol->store_null();
+
+ if (protocol->write())
+ DBUG_RETURN(TRUE);
+ }
+ my_eof(thd);
+ DBUG_RETURN(FALSE);
+}
+
+/**
+ At a point in execution where we know the query source, save the text
+ of it in the query profile.
+
+ This must be called exactly once per descrete statement.
+*/
+void PROFILING::set_query_source(char *query_source_arg, uint query_length_arg)
+{
+ DBUG_ENTER("PROFILING::set_query_source");
+
+ if (! enabled)
+ DBUG_VOID_RETURN;
+
+ if (current != NULL)
+ current->set_query_source(query_source_arg, query_length_arg);
+ else
+ DBUG_PRINT("info", ("no current profile to send query source to"));
+ DBUG_VOID_RETURN;
+}
+
+/**
+ Fill the information schema table, "query_profile", as defined in show.cc .
+ There are two ways to get to this function: Selecting from the information
+ schema, and a SHOW command.
+*/
+int PROFILING::fill_statistics_info(THD *thd, TABLE_LIST *tables, Item *cond)
+{
+ DBUG_ENTER("PROFILING::fill_statistics_info");
+ TABLE *table= tables->table;
+ ulonglong row_number= 0;
+
+ QUERY_PROFILE *query;
+ /* Go through each query in this thread's stored history... */
+ void *history_iterator;
+ for (history_iterator= history.new_iterator();
+ history_iterator != NULL;
+ history_iterator= history.iterator_next(history_iterator))
+ {
+ query= history.iterator_value(history_iterator);
+
+ /*
+ Because we put all profiling info into a table that may be reordered, let
+ us also include a numbering of each state per query. The query_id and
+ the "seq" together are unique.
+ */
+ ulonglong seq;
+
+ void *entry_iterator;
+ PROF_MEASUREMENT *entry, *previous= NULL;
+ /* ...and for each query, go through all its state-change steps. */
+ for (seq= 0, entry_iterator= query->entries.new_iterator();
+ entry_iterator != NULL;
+ entry_iterator= query->entries.iterator_next(entry_iterator),
+ seq++, previous=entry, row_number++)
+ {
+ entry= query->entries.iterator_value(entry_iterator);
+
+ /* Skip the first. We count spans of fence, not fence-posts. */
+ if (previous == NULL) continue;
+
+ if (thd->lex->sql_command == SQLCOM_SHOW_PROFILE)
+ {
+ /*
+ We got here via a SHOW command. That means that we stored
+ information about the query we wish to show and that isn't
+ in a WHERE clause at a higher level to filter out rows we
+ wish to exclude.
+
+ Because that functionality isn't available in the server yet,
+ we must filter here, at the wrong level. Once one can con-
+ struct where and having conditions at the SQL layer, then this
+ condition should be ripped out.
+ */
+ if (thd->lex->profile_query_id == 0) /* 0 == show final query */
+ {
+ if (query != last)
+ continue;
+ }
+ else
+ {
+ if (thd->lex->profile_query_id != query->profiling_query_id)
+ continue;
+ }
+ }
+
+ /* Set default values for this row. */
+ restore_record(table, s->default_values);
+
+ /*
+ The order of these fields is set by the query_profile_statistics_info
+ array.
+ */
+ table->field[0]->store((ulonglong) query->profiling_query_id, TRUE);
+ table->field[1]->store((ulonglong) seq, TRUE); /* the step in the sequence */
+ /*
+ This entry, n, has a point in time, T(n), and a status phrase, S(n).
+ The status phrase S(n) describes the period of time that begins at
+ T(n). The previous status phrase S(n-1) describes the period of time
+ that starts at T(n-1) and ends at T(n). Since we want to describe the
+ time that a status phrase took T(n)-T(n-1), this line must describe the
+ previous status.
+ */
+ table->field[2]->store(previous->status, strlen(previous->status),
+ system_charset_info);
+
+ my_decimal duration_decimal;
+ double2my_decimal(E_DEC_FATAL_ERROR,
+ (entry->time_usecs-previous->time_usecs)/(1000.0*1000),
+ &duration_decimal);
+
+ table->field[3]->store_decimal(&duration_decimal);
+
+
+#ifdef HAVE_GETRUSAGE
+
+ my_decimal cpu_utime_decimal, cpu_stime_decimal;
+
+ double2my_decimal(E_DEC_FATAL_ERROR,
+ RUSAGE_DIFF_USEC(entry->rusage.ru_utime,
+ previous->rusage.ru_utime) /
+ (1000.0*1000),
+ &cpu_utime_decimal);
+
+ double2my_decimal(E_DEC_FATAL_ERROR,
+ RUSAGE_DIFF_USEC(entry->rusage.ru_stime,
+ previous->rusage.ru_stime) /
+ (1000.0*1000),
+ &cpu_stime_decimal);
+
+ table->field[4]->store_decimal(&cpu_utime_decimal);
+ table->field[5]->store_decimal(&cpu_stime_decimal);
+ table->field[4]->set_notnull();
+ table->field[5]->set_notnull();
+#else
+ /* TODO: Add CPU-usage info for non-BSD systems */
+#endif
+
+#ifdef HAVE_GETRUSAGE
+ table->field[6]->store((uint32)(entry->rusage.ru_nvcsw -
+ previous->rusage.ru_nvcsw));
+ table->field[6]->set_notnull();
+ table->field[7]->store((uint32)(entry->rusage.ru_nivcsw -
+ previous->rusage.ru_nivcsw));
+ table->field[7]->set_notnull();
+#else
+ /* TODO: Add context switch info for non-BSD systems */
+#endif
+
+#ifdef HAVE_GETRUSAGE
+ table->field[8]->store((uint32)(entry->rusage.ru_inblock -
+ previous->rusage.ru_inblock));
+ table->field[8]->set_notnull();
+ table->field[9]->store((uint32)(entry->rusage.ru_oublock -
+ previous->rusage.ru_oublock));
+ table->field[9]->set_notnull();
+#else
+ /* TODO: Add block IO info for non-BSD systems */
+#endif
+
+#ifdef HAVE_GETRUSAGE
+ table->field[10]->store((uint32)(entry->rusage.ru_msgsnd -
+ previous->rusage.ru_msgsnd), true);
+ table->field[10]->set_notnull();
+ table->field[11]->store((uint32)(entry->rusage.ru_msgrcv -
+ previous->rusage.ru_msgrcv), true);
+ table->field[11]->set_notnull();
+#else
+ /* TODO: Add message info for non-BSD systems */
+#endif
+
+#ifdef HAVE_GETRUSAGE
+ table->field[12]->store((uint32)(entry->rusage.ru_majflt -
+ previous->rusage.ru_majflt), true);
+ table->field[12]->set_notnull();
+ table->field[13]->store((uint32)(entry->rusage.ru_minflt -
+ previous->rusage.ru_minflt), true);
+ table->field[13]->set_notnull();
+#else
+ /* TODO: Add page fault info for non-BSD systems */
+#endif
+
+#ifdef HAVE_GETRUSAGE
+ table->field[14]->store((uint32)(entry->rusage.ru_nswap -
+ previous->rusage.ru_nswap), true);
+ table->field[14]->set_notnull();
+#else
+ /* TODO: Add swap info for non-BSD systems */
+#endif
+
+ /* Emit the location that started this step, not that ended it. */
+ if ((previous->function != NULL) && (previous->file != NULL))
+ {
+ table->field[15]->store(previous->function, strlen(previous->function),
+ system_charset_info);
+ table->field[15]->set_notnull();
+ table->field[16]->store(previous->file, strlen(previous->file), system_charset_info);
+ table->field[16]->set_notnull();
+ table->field[17]->store(previous->line, true);
+ table->field[17]->set_notnull();
+ }
+
+ if (schema_table_store_record(thd, table))
+ DBUG_RETURN(1);
+
+ }
+ }
+
+ DBUG_RETURN(0);
+}
+#endif /* ENABLED_PROFILING */
diff --git a/sql/sql_profile.h b/sql/sql_profile.h
new file mode 100644
index 00000000000..b5537487d26
--- /dev/null
+++ b/sql/sql_profile.h
@@ -0,0 +1,297 @@
+/* Copyright (C) 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#ifndef _SQL_PROFILE_H
+#define _SQL_PROFILE_H
+
+#if __STDC_VERSION__ < 199901L
+# if __GNUC__ >= 2
+# define __func__ __FUNCTION__
+# else
+# define __func__ _unknown_func_
+extern const char * const _unknown_func_;
+# endif
+#elif defined(_MSC_VER)
+# if _MSC_VER < 1300
+# define __func__ _unknown_func_
+extern const char * const _unknown_func_;
+# else
+# define __func__ __FUNCTION__
+# endif
+#elif defined(__BORLANDC__)
+# define __func__ __FUNC__
+#else
+# define __func__ _unknown_func_
+extern const char * const _unknown_func_;
+#endif
+
+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);
+
+
+#define PROFILE_NONE (uint)0
+#define PROFILE_CPU (uint)(1<<0)
+#define PROFILE_MEMORY (uint)(1<<1)
+#define PROFILE_BLOCK_IO (uint)(1<<2)
+#define PROFILE_CONTEXT (uint)(1<<3)
+#define PROFILE_PAGE_FAULTS (uint)(1<<4)
+#define PROFILE_IPC (uint)(1<<5)
+#define PROFILE_SWAPS (uint)(1<<6)
+#define PROFILE_SOURCE (uint)(1<<16)
+#define PROFILE_ALL (uint)(~0)
+
+
+#if defined(ENABLED_PROFILING) && defined(COMMUNITY_SERVER)
+#include "mysql_priv.h"
+
+#ifdef HAVE_SYS_RESOURCE_H
+#include <sys/resource.h>
+#endif
+
+
+class PROF_MEASUREMENT;
+class QUERY_PROFILE;
+class PROFILING;
+
+
+/**
+ Implements a persistent FIFO using server List method names. Not
+ thread-safe. Intended to be used on thread-local data only.
+*/
+template <class T> class Queue
+{
+private:
+
+ struct queue_item
+ {
+ T *payload;
+ struct queue_item *next, *previous;
+ };
+
+ struct queue_item *first, *last;
+
+public:
+ Queue()
+ {
+ elements= 0;
+ first= last= NULL;
+ }
+
+ void empty()
+ {
+ struct queue_item *i, *after_i;
+ for (i= first; i != NULL; i= after_i)
+ {
+ after_i= i->next;
+ my_free((char *) i, MYF(0));
+ }
+ elements= 0;
+ }
+
+ ulong elements; /* The count of items in the Queue */
+
+ void push_back(T *payload)
+ {
+ struct queue_item *new_item;
+
+ new_item= (struct queue_item *) my_malloc(sizeof(struct queue_item), MYF(0));
+
+ new_item->payload= payload;
+
+ if (first == NULL)
+ first= new_item;
+ if (last != NULL)
+ {
+ DBUG_ASSERT(last->next == NULL);
+ last->next= new_item;
+ }
+ new_item->previous= last;
+ new_item->next= NULL;
+ last= new_item;
+
+ elements++;
+ }
+
+ T *pop()
+ {
+ struct queue_item *old_item= first;
+ T *ret= NULL;
+
+ if (first == NULL)
+ {
+ DBUG_PRINT("warning", ("tried to pop nonexistent item from Queue"));
+ return NULL;
+ }
+
+ ret= old_item->payload;
+ if (first->next != NULL)
+ first->next->previous= NULL;
+ else
+ last= NULL;
+ first= first->next;
+
+ my_free((char *)old_item, MYF(0));
+ elements--;
+
+ return ret;
+ }
+
+ bool is_empty()
+ {
+ DBUG_ASSERT(((elements > 0) && (first != NULL)) || ((elements == 0) || (first == NULL)));
+ return (elements == 0);
+ }
+
+ void *new_iterator()
+ {
+ return first;
+ }
+
+ void *iterator_next(void *current)
+ {
+ return ((struct queue_item *) current)->next;
+ }
+
+ T *iterator_value(void *current)
+ {
+ return ((struct queue_item *) current)->payload;
+ }
+
+};
+
+
+/**
+ A single entry in a single profile.
+*/
+class PROF_MEASUREMENT
+{
+private:
+ friend class QUERY_PROFILE;
+ friend class PROFILING;
+
+ QUERY_PROFILE *profile;
+ char *status;
+#ifdef HAVE_GETRUSAGE
+ struct rusage rusage;
+#endif
+
+ char *function;
+ char *file;
+ unsigned int line;
+
+ double time_usecs;
+ char *allocated_status_memory;
+
+ void set_label(const char *status_arg, const char *function_arg,
+ const char *file_arg, unsigned int line_arg);
+ void clean_up();
+
+ PROF_MEASUREMENT();
+ PROF_MEASUREMENT(QUERY_PROFILE *profile_arg, const char *status_arg);
+ PROF_MEASUREMENT(QUERY_PROFILE *profile_arg, const char *status_arg,
+ const char *function_arg,
+ const char *file_arg, unsigned int line_arg);
+ ~PROF_MEASUREMENT();
+ void collect();
+};
+
+
+/**
+ The full profile for a single query, and includes multiple PROF_MEASUREMENT
+ objects.
+*/
+class QUERY_PROFILE
+{
+private:
+ friend class PROFILING;
+
+ PROFILING *profiling;
+
+ query_id_t profiling_query_id; /* Session-specific id. */
+ char *query_source;
+
+ PROF_MEASUREMENT *profile_start;
+ PROF_MEASUREMENT *profile_end;
+ Queue<PROF_MEASUREMENT> entries;
+
+
+ QUERY_PROFILE(PROFILING *profiling_arg, const char *status_arg);
+ ~QUERY_PROFILE();
+
+ void set_query_source(char *query_source_arg, uint query_length_arg);
+
+ /* Add a profile status change to the current profile. */
+ void new_status(const char *status_arg,
+ const char *function_arg,
+ const char *file_arg, unsigned int line_arg);
+
+ /* Reset the contents of this profile entry. */
+ void reset();
+
+ /* Show this profile. This is called by PROFILING. */
+ bool show(uint options);
+};
+
+
+/**
+ Profiling state for a single THD; contains multiple QUERY_PROFILE objects.
+*/
+class PROFILING
+{
+private:
+ friend class PROF_MEASUREMENT;
+ friend class QUERY_PROFILE;
+
+ /*
+ Not the system query_id, but a counter unique to profiling.
+ */
+ query_id_t profile_id_counter;
+ THD *thd;
+ bool keeping;
+ bool enabled;
+
+ QUERY_PROFILE *current;
+ QUERY_PROFILE *last;
+ Queue<QUERY_PROFILE> history;
+
+ query_id_t next_profile_id() { return(profile_id_counter++); }
+
+public:
+ PROFILING();
+ ~PROFILING();
+ void set_query_source(char *query_source_arg, uint query_length_arg);
+
+ void start_new_query(const char *initial_state= "starting");
+
+ void discard_current_query();
+
+ void finish_current_query();
+
+ void status_change(const char *status_arg,
+ const char *function_arg,
+ const char *file_arg, unsigned int line_arg);
+
+ inline void set_thd(THD *thd_arg) { thd= thd_arg; };
+
+ /* SHOW PROFILES */
+ bool show_profiles();
+
+ /* ... from INFORMATION_SCHEMA.PROFILING ... */
+ int fill_statistics_info(THD *thd, TABLE_LIST *tables, Item *cond);
+};
+
+# endif /* HAVE_PROFILING */
+#endif /* _SQL_PROFILE_H */
diff --git a/sql/sql_rename.cc b/sql/sql_rename.cc
index f6766aec285..fc87356e452 100644
--- a/sql/sql_rename.cc
+++ b/sql/sql_rename.cc
@@ -31,10 +31,12 @@ static TABLE_LIST *reverse_table_list(TABLE_LIST *table_list);
second entry is the new name.
*/
-bool mysql_rename_tables(THD *thd, TABLE_LIST *table_list)
+bool mysql_rename_tables(THD *thd, TABLE_LIST *table_list, bool silent)
{
bool error= 1;
TABLE_LIST *ren_table= 0;
+ int to_table;
+ char *rename_log_table[2]= {NULL, NULL};
DBUG_ENTER("mysql_rename_tables");
/*
@@ -49,12 +51,95 @@ bool mysql_rename_tables(THD *thd, TABLE_LIST *table_list)
DBUG_RETURN(1);
}
+ mysql_ha_rm_tables(thd, table_list, FALSE);
+
if (wait_if_global_read_lock(thd,0,1))
DBUG_RETURN(1);
- VOID(pthread_mutex_lock(&LOCK_open));
- if (lock_table_names(thd, table_list))
+
+ if (logger.is_log_table_enabled(QUERY_LOG_GENERAL) ||
+ logger.is_log_table_enabled(QUERY_LOG_SLOW))
+ {
+
+ /*
+ Rules for rename of a log table:
+
+ IF 1. Log tables are enabled
+ AND 2. Rename operates on the log table and nothing is being
+ renamed to the log table.
+ DO 3. Throw an error message.
+ ELSE 4. Perform rename.
+ */
+
+ for (to_table= 0, ren_table= table_list; ren_table;
+ to_table= 1 - to_table, ren_table= ren_table->next_local)
+ {
+ int log_table_rename= 0;
+
+ if ((log_table_rename=
+ check_if_log_table(ren_table->db_length, ren_table->db,
+ ren_table->table_name_length,
+ ren_table->table_name, 1)))
+ {
+ /*
+ as we use log_table_rename as an array index, we need it to start
+ with 0, while QUERY_LOG_SLOW == 1 and QUERY_LOG_GENERAL == 2.
+ So, we shift the value to start with 0;
+ */
+ log_table_rename--;
+ if (rename_log_table[log_table_rename])
+ {
+ if (to_table)
+ rename_log_table[log_table_rename]= NULL;
+ else
+ {
+ /*
+ Two renames of "log_table TO" w/o rename "TO log_table" in
+ between.
+ */
+ my_error(ER_CANT_RENAME_LOG_TABLE, MYF(0), ren_table->table_name,
+ ren_table->table_name);
+ DBUG_RETURN(1);
+ }
+ }
+ else
+ {
+ if (to_table)
+ {
+ /*
+ Attempt to rename a table TO log_table w/o renaming
+ log_table TO some table.
+ */
+ my_error(ER_CANT_RENAME_LOG_TABLE, MYF(0), ren_table->table_name,
+ ren_table->table_name);
+ DBUG_RETURN(1);
+ }
+ else
+ {
+ /* save the name of the log table to report an error */
+ rename_log_table[log_table_rename]= ren_table->table_name;
+ }
+ }
+ }
+ }
+ if (rename_log_table[0] || rename_log_table[1])
+ {
+ if (rename_log_table[0])
+ my_error(ER_CANT_RENAME_LOG_TABLE, MYF(0), rename_log_table[0],
+ rename_log_table[0]);
+ else
+ my_error(ER_CANT_RENAME_LOG_TABLE, MYF(0), rename_log_table[1],
+ rename_log_table[1]);
+ DBUG_RETURN(1);
+ }
+ }
+
+ pthread_mutex_lock(&LOCK_open);
+ if (lock_table_names_exclusively(thd, table_list))
+ {
+ pthread_mutex_unlock(&LOCK_open);
goto err;
-
+ }
+
error=0;
if ((ren_table=rename_tables(thd,table_list,0)))
{
@@ -77,23 +162,33 @@ bool mysql_rename_tables(THD *thd, TABLE_LIST *table_list)
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);
/* Lets hope this doesn't fail as the result will be messy */
- if (!error)
+ if (!silent && !error)
{
- if (mysql_bin_log.is_open())
- {
- thd->clear_error();
- Query_log_event qinfo(thd, thd->query, thd->query_length, 0, FALSE);
- mysql_bin_log.write(&qinfo);
- }
- send_ok(thd);
+ write_bin_log(thd, TRUE, thd->query, thd->query_length);
+ my_ok(thd);
}
+ 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:
- pthread_mutex_unlock(&LOCK_open);
start_waiting_global_read_lock(thd);
DBUG_RETURN(error);
}
@@ -152,7 +247,7 @@ do_rename(THD *thd, TABLE_LIST *ren_table, char *new_db, char *new_table_name,
char name[FN_REFLEN];
const char *new_alias, *old_alias;
frm_type_enum frm_type;
- db_type table_type;
+ enum legacy_db_type table_type;
DBUG_ENTER("do_rename");
@@ -166,30 +261,25 @@ do_rename(THD *thd, TABLE_LIST *ren_table, char *new_db, char *new_table_name,
old_alias= ren_table->table_name;
new_alias= new_table_name;
}
- sprintf(name,"%s/%s/%s%s",mysql_data_home,
- new_db, new_alias, reg_ext);
- unpack_filename(name, name);
+ build_table_filename(name, sizeof(name),
+ new_db, new_alias, reg_ext, 0);
if (!access(name,F_OK))
{
my_error(ER_TABLE_EXISTS_ERROR, MYF(0), new_alias);
DBUG_RETURN(1); // This can't be skipped
}
- sprintf(name,"%s/%s/%s%s",mysql_data_home,
- ren_table->db, old_alias,
- reg_ext);
- unpack_filename(name, name);
+ build_table_filename(name, sizeof(name),
+ ren_table->db, old_alias, reg_ext, 0);
frm_type= mysql_frm_type(thd, name, &table_type);
switch (frm_type)
{
case FRMTYPE_TABLE:
- {
- if (table_type == DB_TYPE_UNKNOWN)
- my_error(ER_FILE_NOT_FOUND, MYF(0), name, my_errno);
- else
{
- if (!(rc= mysql_rename_table(table_type, ren_table->db, old_alias,
- new_db, new_alias)))
+ if (!(rc= mysql_rename_table(ha_resolve_by_legacy_type(thd,
+ table_type),
+ ren_table->db, old_alias,
+ new_db, new_alias, 0)))
{
if ((rc= Table_triggers_list::change_table_name(thd, ren_table->db,
old_alias,
@@ -202,13 +292,14 @@ do_rename(THD *thd, TABLE_LIST *ren_table, char *new_db, char *new_table_name,
triggers appropriately. So let us revert operations on .frm
and handler's data and report about failure to rename table.
*/
- (void) mysql_rename_table(table_type, new_db, new_alias,
- ren_table->db, old_alias);
+ (void) mysql_rename_table(ha_resolve_by_legacy_type(thd,
+ table_type),
+ new_db, new_alias,
+ ren_table->db, old_alias, 0);
}
}
}
break;
- }
case FRMTYPE_VIEW:
/* change of schema is not allowed */
if (strcmp(ren_table->db, new_db))
diff --git a/sql/sql_repl.cc b/sql/sql_repl.cc
index 5966176012f..6285a2dfb55 100644
--- a/sql/sql_repl.cc
+++ b/sql/sql_repl.cc
@@ -16,8 +16,10 @@
#include "mysql_priv.h"
#ifdef HAVE_REPLICATION
+#include "rpl_mi.h"
#include "sql_repl.h"
#include "log_event.h"
+#include "rpl_filter.h"
#include <my_dir.h>
int max_binlog_dump_events = 0; // unlimited
@@ -61,7 +63,7 @@ static int fake_rotate_event(NET* net, String* packet, char* log_file_name,
ulong event_len = ident_len + LOG_EVENT_HEADER_LEN + ROTATE_HEADER_LEN;
int4store(header + SERVER_ID_OFFSET, server_id);
int4store(header + EVENT_LEN_OFFSET, event_len);
- int2store(header + FLAGS_OFFSET, 0);
+ int2store(header + FLAGS_OFFSET, LOG_EVENT_ARTIFICIAL_F);
// TODO: check what problems this may cause and fix them
int4store(header + LOG_POS_OFFSET, 0);
@@ -70,7 +72,7 @@ static int fake_rotate_event(NET* net, String* packet, char* log_file_name,
int8store(buf+R_POS_OFFSET,position);
packet->append(buf, ROTATE_HEADER_LEN);
packet->append(p,ident_len);
- if (my_net_write(net, (char*)packet->ptr(), packet->length()))
+ if (my_net_write(net, (uchar*) packet->ptr(), packet->length()))
{
*errmsg = "failed on my_net_write()";
DBUG_RETURN(-1);
@@ -81,12 +83,13 @@ static int fake_rotate_event(NET* net, String* packet, char* log_file_name,
static int send_file(THD *thd)
{
NET* net = &thd->net;
- int fd = -1,bytes, error = 1;
+ int fd = -1, error = 1;
+ size_t bytes;
char fname[FN_REFLEN+1];
const char *errmsg = 0;
int old_timeout;
unsigned long packet_len;
- char buf[IO_SIZE]; // It's safe to alloc this
+ uchar buf[IO_SIZE]; // It's safe to alloc this
DBUG_ENTER("send_file");
/*
@@ -119,7 +122,7 @@ static int send_file(THD *thd)
goto err;
}
- while ((bytes = (int) my_read(fd, (byte*) buf, IO_SIZE, MYF(0))) > 0)
+ while ((long) (bytes= my_read(fd, buf, IO_SIZE, MYF(0))) > 0)
{
if (my_net_write(net, buf, bytes))
{
@@ -129,7 +132,7 @@ static int send_file(THD *thd)
}
end:
- if (my_net_write(net, "", 0) || net_flush(net) ||
+ if (my_net_write(net, (uchar*) "", 0) || net_flush(net) ||
(my_net_read(net) == packet_error))
{
errmsg = "while negotiating file transfer close";
@@ -215,7 +218,8 @@ bool log_in_use(const char* log_name)
if ((linfo = tmp->current_linfo))
{
pthread_mutex_lock(&linfo->lock);
- result = !bcmp(log_name, linfo->log_file_name, log_name_len);
+ result = !bcmp((uchar*) log_name, (uchar*) linfo->log_file_name,
+ log_name_len);
pthread_mutex_unlock(&linfo->lock);
if (result)
break;
@@ -239,6 +243,7 @@ bool purge_error_message(THD* thd, int res)
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;
}
@@ -247,17 +252,28 @@ bool purge_error_message(THD* thd, int res)
my_message(errmsg, ER(errmsg), MYF(0));
return TRUE;
}
- send_ok(thd);
+ my_ok(thd);
return FALSE;
}
+/**
+ Execute a PURGE BINARY LOGS TO <log> command.
+
+ @param thd Pointer to THD object for the client thread executing the
+ statement.
+
+ @param to_log Name of the last log to purge.
+
+ @retval FALSE success
+ @retval TRUE failure
+*/
bool purge_master_logs(THD* thd, const char* to_log)
{
char search_file_name[FN_REFLEN];
if (!mysql_bin_log.is_open())
{
- send_ok(thd);
+ my_ok(thd);
return FALSE;
}
@@ -268,11 +284,22 @@ bool purge_master_logs(THD* thd, const char* to_log)
}
+/**
+ Execute a PURGE BINARY LOGS BEFORE <date> command.
+
+ @param thd Pointer to THD object for the client thread executing the
+ statement.
+
+ @param purge_time Date before which logs should be purged.
+
+ @retval FALSE success
+ @retval TRUE failure
+*/
bool purge_master_logs_before_date(THD* thd, time_t purge_time)
{
if (!mysql_bin_log.is_open())
{
- send_ok(thd);
+ my_ok(thd);
return 0;
}
return purge_error_message(thd,
@@ -480,7 +507,7 @@ impossible position";
int4store((char*) packet->ptr()+LOG_EVENT_MINIMAL_HEADER_LEN+
ST_CREATED_OFFSET+1, (ulong) 0);
/* send it */
- if (my_net_write(net, (char*)packet->ptr(), packet->length()))
+ if (my_net_write(net, (uchar*) packet->ptr(), packet->length()))
{
errmsg = "Failed on my_net_write()";
my_errno= ER_UNKNOWN_ERROR;
@@ -538,7 +565,7 @@ impossible position";
else if ((*packet)[EVENT_TYPE_OFFSET+1] == STOP_EVENT)
binlog_can_be_corrupted= FALSE;
- if (my_net_write(net, (char*)packet->ptr(), packet->length()))
+ if (my_net_write(net, (uchar*) packet->ptr(), packet->length()))
{
errmsg = "Failed on my_net_write()";
my_errno= ER_UNKNOWN_ERROR;
@@ -650,8 +677,8 @@ impossible position";
if (read_packet)
{
- thd->proc_info = "Sending binlog event to slave";
- if (my_net_write(net, (char*)packet->ptr(), packet->length()) )
+ 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;
@@ -688,7 +715,7 @@ impossible position";
bool loop_breaker = 0;
/* need this to break out of the for loop from switch */
- thd->proc_info = "Finished reading one binlog; switching to next binlog";
+ thd_proc_info(thd, "Finished reading one binlog; switching to next binlog");
switch (mysql_bin_log.find_next_log(&linfo, 1)) {
case LOG_INFO_EOF:
loop_breaker = (flags & BINLOG_DUMP_NON_BLOCK);
@@ -733,15 +760,15 @@ end:
end_io_cache(&log);
(void)my_close(file, MYF(MY_WME));
- send_eof(thd);
- thd->proc_info = "Waiting to finalize termination";
+ my_eof(thd);
+ thd_proc_info(thd, "Waiting to finalize termination");
pthread_mutex_lock(&LOCK_thread_count);
thd->current_linfo = 0;
pthread_mutex_unlock(&LOCK_thread_count);
DBUG_VOID_RETURN;
err:
- thd->proc_info = "Waiting to finalize termination";
+ thd_proc_info(thd, "Waiting to finalize termination");
end_io_cache(&log);
/*
Exclude iteration through thread list
@@ -760,7 +787,21 @@ err:
DBUG_VOID_RETURN;
}
-int start_slave(THD* thd , MASTER_INFO* mi, bool net_report)
+
+/**
+ Execute a START SLAVE statement.
+
+ @param thd Pointer to THD object for the client thread executing the
+ statement.
+
+ @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.
+
+ @retval 0 success
+ @retval 1 error
+*/
+int start_slave(THD* thd , Master_info* mi, bool net_report)
{
int slave_errno= 0;
int thread_mask;
@@ -797,7 +838,7 @@ int start_slave(THD* thd , MASTER_INFO* mi, bool net_report)
if (thd->lex->mi.pos)
{
- mi->rli.until_condition= RELAY_LOG_INFO::UNTIL_MASTER_POS;
+ mi->rli.until_condition= Relay_log_info::UNTIL_MASTER_POS;
mi->rli.until_log_pos= thd->lex->mi.pos;
/*
We don't check thd->lex->mi.log_file_name for NULL here
@@ -808,15 +849,15 @@ int start_slave(THD* thd , MASTER_INFO* mi, bool net_report)
}
else if (thd->lex->mi.relay_log_pos)
{
- mi->rli.until_condition= RELAY_LOG_INFO::UNTIL_RELAY_POS;
+ 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);
}
else
- clear_until_condition(&mi->rli);
+ mi->rli.clear_until_condition();
- if (mi->rli.until_condition != RELAY_LOG_INFO::UNTIL_NONE)
+ if (mi->rli.until_condition != Relay_log_info::UNTIL_NONE)
{
/* Preparing members for effective until condition checking */
const char *p= fn_ext(mi->rli.until_log_name);
@@ -838,7 +879,7 @@ int start_slave(THD* thd , MASTER_INFO* mi, bool net_report)
/* mark the cached result of the UNTIL comparison as "undefined" */
mi->rli.until_log_names_cmp_result=
- RELAY_LOG_INFO::UNTIL_LOG_NAMES_CMP_UNKNOWN;
+ Relay_log_info::UNTIL_LOG_NAMES_CMP_UNKNOWN;
/* Issuing warning then started without --skip-slave-start */
if (!opt_skip_slave_start)
@@ -879,13 +920,26 @@ int start_slave(THD* thd , MASTER_INFO* mi, bool net_report)
DBUG_RETURN(1);
}
else if (net_report)
- send_ok(thd);
+ my_ok(thd);
DBUG_RETURN(0);
}
-int stop_slave(THD* thd, MASTER_INFO* mi, bool net_report )
+/**
+ Execute a STOP SLAVE statement.
+
+ @param thd Pointer to THD object for the client thread executing the
+ statement.
+
+ @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.
+
+ @retval 0 success
+ @retval 1 error
+*/
+int stop_slave(THD* thd, Master_info* mi, bool net_report )
{
DBUG_ENTER("stop_slave");
@@ -895,7 +949,7 @@ int stop_slave(THD* thd, MASTER_INFO* mi, bool net_report )
if (check_access(thd, SUPER_ACL, any_db,0,0,0,0))
DBUG_RETURN(1);
- thd->proc_info = "Killing slave";
+ thd_proc_info(thd, "Killing slave");
int thread_mask;
lock_slave_threads(mi);
// Get a mask of _running_ threads
@@ -922,7 +976,7 @@ int stop_slave(THD* thd, MASTER_INFO* mi, bool net_report )
ER(ER_SLAVE_WAS_NOT_RUNNING));
}
unlock_slave_threads(mi);
- thd->proc_info = 0;
+ thd_proc_info(thd, 0);
if (slave_errno)
{
@@ -931,27 +985,24 @@ int stop_slave(THD* thd, MASTER_INFO* mi, bool net_report )
DBUG_RETURN(1);
}
else if (net_report)
- send_ok(thd);
+ my_ok(thd);
DBUG_RETURN(0);
}
-/*
- Remove all relay logs and start replication from the start
-
- SYNOPSIS
- reset_slave()
- thd Thread handler
- mi Master info for the slave
+/**
+ Execute a RESET SLAVE statement.
- RETURN
- 0 ok
- 1 error
-*/
+ @param thd Pointer to THD object of the client thread executing the
+ statement.
+ @param mi Pointer to Master_info object for the slave.
-int reset_slave(THD *thd, MASTER_INFO* mi)
+ @retval 0 success
+ @retval 1 error
+*/
+int reset_slave(THD *thd, Master_info* mi)
{
MY_STAT stat_area;
char fname[FN_REFLEN];
@@ -968,6 +1019,9 @@ int reset_slave(THD *thd, MASTER_INFO* mi)
error=1;
goto err;
}
+
+ ha_reset_slave(thd);
+
// delete relay logs, clear relay log coordinates
if ((error= purge_relay_logs(&mi->rli, thd,
1 /* just reset */,
@@ -986,8 +1040,8 @@ int reset_slave(THD *thd, MASTER_INFO* mi)
Reset errors (the idea is that we forget about the
old master).
*/
- clear_slave_error(&mi->rli);
- clear_until_condition(&mi->rli);
+ mi->rli.clear_error();
+ mi->rli.clear_until_condition();
// close master_info_file, relay_log_info_file, set mi->inited=rli->inited=0
end_master_info(mi);
@@ -1062,7 +1116,19 @@ void kill_zombie_dump_threads(uint32 slave_server_id)
}
-bool change_master(THD* thd, MASTER_INFO* mi)
+/**
+ Execute a CHANGE MASTER statement.
+
+ @param thd Pointer to THD object for the client thread executing the
+ statement.
+
+ @param mi Pointer to Master_info object belonging to the slave's IO
+ thread.
+
+ @retval FALSE success
+ @retval TRUE error
+*/
+bool change_master(THD* thd, Master_info* mi)
{
int thread_mask;
const char* errmsg= 0;
@@ -1078,7 +1144,7 @@ bool change_master(THD* thd, MASTER_INFO* mi)
DBUG_RETURN(TRUE);
}
- thd->proc_info = "Changing master";
+ thd_proc_info(thd, "Changing master");
LEX_MASTER_INFO* lex_mi= &thd->lex->mi;
// TODO: see if needs re-write
if (init_master_info(mi, master_info_file, relay_log_info_file, 0,
@@ -1128,6 +1194,11 @@ bool change_master(THD* thd, MASTER_INFO* mi)
if (lex_mi->ssl != LEX_MASTER_INFO::SSL_UNCHANGED)
mi->ssl= (lex_mi->ssl == LEX_MASTER_INFO::SSL_ENABLE);
+
+ if (lex_mi->ssl_verify_server_cert != LEX_MASTER_INFO::SSL_UNCHANGED)
+ mi->ssl_verify_server_cert=
+ (lex_mi->ssl_verify_server_cert == LEX_MASTER_INFO::SSL_ENABLE);
+
if (lex_mi->ssl_ca)
strmake(mi->ssl_ca, lex_mi->ssl_ca, sizeof(mi->ssl_ca)-1);
if (lex_mi->ssl_capath)
@@ -1140,7 +1211,8 @@ bool change_master(THD* thd, MASTER_INFO* mi)
strmake(mi->ssl_key, lex_mi->ssl_key, sizeof(mi->ssl_key)-1);
#ifndef HAVE_OPENSSL
if (lex_mi->ssl || lex_mi->ssl_ca || lex_mi->ssl_capath ||
- lex_mi->ssl_cert || lex_mi->ssl_cipher || lex_mi->ssl_key )
+ lex_mi->ssl_cert || lex_mi->ssl_cipher || lex_mi->ssl_key ||
+ lex_mi->ssl_verify_server_cert )
push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
ER_SLAVE_IGNORED_SSL_PARAMS, ER(ER_SLAVE_IGNORED_SSL_PARAMS));
#endif
@@ -1203,7 +1275,7 @@ bool change_master(THD* thd, MASTER_INFO* mi)
if (need_relay_log_purge)
{
relay_log_purge= 1;
- thd->proc_info="Purging old relay logs";
+ thd_proc_info(thd, "Purging old relay logs");
if (purge_relay_logs(&mi->rli, thd,
0 /* not only reset, but also reinit */,
&errmsg))
@@ -1250,8 +1322,8 @@ bool change_master(THD* thd, MASTER_INFO* mi)
pthread_mutex_lock(&mi->rli.data_lock);
mi->rli.abort_pos_wait++; /* for MASTER_POS_WAIT() to abort */
/* Clear the errors, for a clean start */
- clear_slave_error(&mi->rli);
- clear_until_condition(&mi->rli);
+ mi->rli.clear_error();
+ mi->rli.clear_until_condition();
/*
If we don't write new coordinates to disk now, then old will remain in
relay-log.info until START SLAVE is issued; but if mysqld is shutdown
@@ -1264,11 +1336,21 @@ bool change_master(THD* thd, MASTER_INFO* mi)
pthread_mutex_unlock(&mi->rli.data_lock);
unlock_slave_threads(mi);
- thd->proc_info = 0;
- send_ok(thd);
+ thd_proc_info(thd, 0);
+ my_ok(thd);
DBUG_RETURN(FALSE);
}
+
+/**
+ Execute a RESET MASTER statement.
+
+ @param thd Pointer to THD object of the client thread executing the
+ statement.
+
+ @retval 0 success
+ @retval 1 error
+*/
int reset_master(THD* thd)
{
if (!mysql_bin_log.is_open())
@@ -1298,15 +1380,24 @@ int cmp_master_pos(const char* log_file_name1, ulonglong log_pos1,
}
+/**
+ Execute a SHOW BINLOG EVENTS statement.
+
+ @param thd Pointer to THD object for the client thread executing the
+ statement.
+
+ @retval FALSE success
+ @retval TRUE failure
+*/
bool mysql_show_binlog_events(THD* thd)
{
Protocol *protocol= thd->protocol;
- DBUG_ENTER("mysql_show_binlog_events");
List<Item> field_list;
const char *errmsg = 0;
bool ret = TRUE;
IO_CACHE log;
File file = -1;
+ DBUG_ENTER("mysql_show_binlog_events");
Log_event::init_show_field_list(&field_list);
if (protocol->send_fields(&field_list,
@@ -1316,6 +1407,13 @@ bool mysql_show_binlog_events(THD* thd)
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);
+
if (mysql_bin_log.is_open())
{
LEX_MASTER_INFO *lex_mi= &thd->lex->mi;
@@ -1361,12 +1459,12 @@ bool mysql_show_binlog_events(THD* thd)
pthread_mutex_lock(log_lock);
/*
- open_binlog() sought to position 4.
- Read the first event in case it's a Format_description_log_event, to
- know the format. If there's no such event, we are 3.23 or 4.x. This
- code, like before, can't read 3.23 binlogs.
- This code will fail on a mixed relay log (one which has Format_desc then
- Rotate then Format_desc).
+ open_binlog() sought to position 4.
+ Read the first event in case it's a Format_description_log_event, to
+ know the format. If there's no such event, we are 3.23 or 4.x. This
+ code, like before, can't read 3.23 binlogs.
+ 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);
if (ev)
@@ -1389,7 +1487,8 @@ bool mysql_show_binlog_events(THD* thd)
}
for (event_count = 0;
- (ev = Log_event::read_log_event(&log,(pthread_mutex_t*)0,description_event)); )
+ (ev = Log_event::read_log_event(&log,(pthread_mutex_t*) 0,
+ description_event)); )
{
if (event_count >= limit_start &&
ev->net_send(protocol, linfo.log_file_name, pos))
@@ -1431,7 +1530,7 @@ err:
my_error(ER_ERROR_WHEN_EXECUTING_COMMAND, MYF(0),
"SHOW BINLOG EVENTS", errmsg);
else
- send_eof(thd);
+ my_eof(thd);
pthread_mutex_lock(&LOCK_thread_count);
thd->current_linfo = 0;
@@ -1440,6 +1539,15 @@ err:
}
+/**
+ Execute a SHOW MASTER STATUS statement.
+
+ @param thd Pointer to THD object for the client thread executing the
+ statement.
+
+ @retval FALSE success
+ @retval TRUE failure
+*/
bool show_binlog_info(THD* thd)
{
Protocol *protocol= thd->protocol;
@@ -1463,28 +1571,25 @@ bool show_binlog_info(THD* thd)
int dir_len = dirname_length(li.log_file_name);
protocol->store(li.log_file_name + dir_len, &my_charset_bin);
protocol->store((ulonglong) li.pos);
- protocol->store(&binlog_do_db);
- protocol->store(&binlog_ignore_db);
+ protocol->store(binlog_filter->get_do_db());
+ protocol->store(binlog_filter->get_ignore_db());
if (protocol->write())
DBUG_RETURN(TRUE);
}
- send_eof(thd);
+ my_eof(thd);
DBUG_RETURN(FALSE);
}
-/*
- Send a list of all binary logs to client
+/**
+ Execute a SHOW BINARY LOGS statement.
- SYNOPSIS
- show_binlogs()
- thd Thread specific variable
+ @param thd Pointer to THD object for the client thread executing the
+ statement.
- RETURN VALUES
- FALSE OK
- TRUE error
+ @retval FALSE success
+ @retval TRUE failure
*/
-
bool show_binlogs(THD* thd)
{
IO_CACHE *index_file;
@@ -1550,7 +1655,7 @@ bool show_binlogs(THD* thd)
goto err;
}
mysql_bin_log.unlock_index();
- send_eof(thd);
+ my_eof(thd);
DBUG_RETURN(FALSE);
err:
@@ -1573,9 +1678,11 @@ int log_loaded_block(IO_CACHE* file)
LOAD_FILE_INFO *lf_info;
uint block_len;
/* buffer contains position where we started last read */
- char* buffer= (char*) my_b_get_buffer_start(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)
+ 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))
DBUG_RETURN(0);
@@ -1606,6 +1713,124 @@ 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_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");
+
+
+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;
+}
+
#endif /* HAVE_REPLICATION */
diff --git a/sql/sql_repl.h b/sql/sql_repl.h
index cf400218cc2..d5c9040f8dc 100644
--- a/sql/sql_repl.h
+++ b/sql/sql_repl.h
@@ -13,6 +13,8 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+#include "rpl_filter.h"
+
#ifdef HAVE_REPLICATION
#include "slave.h"
@@ -30,25 +32,24 @@ typedef struct st_slave_info
extern my_bool opt_show_slave_auth_info;
extern char *master_host, *master_info_file;
extern bool server_id_supplied;
-extern I_List<i_string> binlog_do_db, binlog_ignore_db;
extern int max_binlog_dump_events;
extern my_bool opt_sporadic_binlog_dump_fail;
-int start_slave(THD* thd, MASTER_INFO* mi, bool net_report);
-int stop_slave(THD* thd, MASTER_INFO* mi, bool net_report);
-bool change_master(THD* thd, MASTER_INFO* mi);
+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_slave(THD *thd, Master_info* mi);
int reset_master(THD* thd);
bool purge_master_logs(THD* thd, const char* to_log);
bool purge_master_logs_before_date(THD* thd, time_t purge_time);
bool log_in_use(const char* log_name);
void adjust_linfo_offsets(my_off_t purge_offset);
bool show_binlogs(THD* thd);
-extern int init_master_info(MASTER_INFO* mi);
+extern int init_master_info(Master_info* mi);
void kill_zombie_dump_threads(uint32 slave_server_id);
int check_binlog_magic(IO_CACHE* log, const char** errmsg);
@@ -60,6 +61,7 @@ typedef struct st_load_file_info
} LOAD_FILE_INFO;
int log_loaded_block(IO_CACHE* file);
+int init_replication_sys_vars();
#endif /* HAVE_REPLICATION */
diff --git a/sql/sql_select.cc b/sql/sql_select.cc
index c797e25850a..75c11c2ac64 100644
--- a/sql/sql_select.cc
+++ b/sql/sql_select.cc
@@ -1,4 +1,4 @@
-/* Copyright (C) 2000-2006 MySQL AB
+/* Copyright 2000-2008 MySQL AB, 2008 Sun Microsystems, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -13,8 +13,16 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+/**
+ @file
+
+ @brief
+ mysql_select and join optimization
-/* mysql_select and join optimization */
+
+ @defgroup Query_Optimizer Query Optimizer
+ @{
+*/
#ifdef USE_PRAGMA_IMPLEMENTATION
#pragma implementation // gcc: Class implementation
@@ -25,6 +33,7 @@
#include "sql_cursor.h"
#include <m_ctype.h>
+#include <my_bit.h>
#include <hash.h>
#include <ft_global.h>
@@ -72,11 +81,11 @@ static int join_tab_cmp_straight(const void* ptr1, const void* ptr2);
static bool find_best(JOIN *join,table_map rest_tables,uint index,
double record_count,double read_time);
static uint cache_record_length(JOIN *join,uint index);
-static double prev_record_reads(JOIN *join,table_map found_ref);
+static double prev_record_reads(JOIN *join, uint idx, table_map found_ref);
static bool get_best_combination(JOIN *join);
static store_key *get_store_key(THD *thd,
KEYUSE *keyuse, table_map used_tables,
- KEY_PART_INFO *key_part, char *key_buff,
+ KEY_PART_INFO *key_part, uchar *key_buff,
uint maybe_null);
static void make_outerjoin_info(JOIN *join);
static bool make_join_select(JOIN *join,SQL_SELECT *select,COND *item);
@@ -117,7 +126,7 @@ static int do_select(JOIN *join,List<Item> *fields,TABLE *tmp_table,
static enum_nested_loop_state
evaluate_join_record(JOIN *join, JOIN_TAB *join_tab,
- int error, my_bool *report_error);
+ int error);
static enum_nested_loop_state
evaluate_null_complemented_join_record(JOIN *join, JOIN_TAB *join_tab);
static enum_nested_loop_state
@@ -162,13 +171,15 @@ static COND *make_cond_for_table(COND *cond,table_map table,
static Item* part_of_refkey(TABLE *form,Field *field);
uint find_shortest_key(TABLE *table, const key_map *usable_keys);
static bool test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,
- ha_rows select_limit, bool no_changes);
+ ha_rows select_limit, bool no_changes,
+ key_map *map);
static bool list_contains_unique_index(TABLE *table,
bool (*find_func) (Field *, void *), void *data);
static bool find_field_in_item_list (Field *field, void *data);
static bool find_field_in_order_list (Field *field, void *data);
static int create_sort_index(THD *thd, JOIN *join, ORDER *order,
- ha_rows filesort_limit, ha_rows select_limit);
+ ha_rows filesort_limit, ha_rows select_limit,
+ bool is_order_by);
static int remove_duplicates(JOIN *join,TABLE *entry,List<Item> &fields,
Item *having);
static int remove_dup_with_compare(THD *thd, TABLE *entry, Field **field,
@@ -219,8 +230,8 @@ static void add_group_and_distinct_keys(JOIN *join, JOIN_TAB *join_tab);
static bool test_if_ref(Item_field *left_item,Item *right_item);
-/*
- This handles SELECT with and without UNION
+/**
+ This handles SELECT with and without UNION.
*/
bool handle_select(THD *thd, LEX *lex, select_result *result,
@@ -230,7 +241,8 @@ bool handle_select(THD *thd, LEX *lex, select_result *result,
register SELECT_LEX *select_lex = &lex->select_lex;
DBUG_ENTER("handle_select");
- if (select_lex->next_select() || select_lex->master_unit()->fake_select_lex)
+ if (select_lex->master_unit()->is_union() ||
+ select_lex->master_unit()->fake_select_lex)
res= mysql_union(thd, lex, result, &lex->unit, setup_tables_done_option);
else
{
@@ -256,8 +268,8 @@ bool handle_select(THD *thd, LEX *lex, select_result *result,
result, unit, select_lex);
}
DBUG_PRINT("info",("res: %d report_error: %d", res,
- thd->net.report_error));
- res|= thd->net.report_error;
+ thd->is_error()));
+ res|= thd->is_error();
if (unlikely(res))
result->abort();
@@ -375,8 +387,8 @@ fix_inner_refs(THD *thd, List<Item> &all_fields, SELECT_LEX *select,
return res;
}
-/*
- Function to setup clauses without sum functions
+/**
+ Function to setup clauses without sum functions.
*/
inline int setup_without_group(THD *thd, Item **ref_pointer_array,
TABLE_LIST *tables,
@@ -419,10 +431,17 @@ inline int setup_without_group(THD *thd, Item **ref_pointer_array,
mysql_select assumes that all tables are already opened
*****************************************************************************/
-/*
+/**
Prepare of whole select (including sub queries in future).
- return -1 on error
- 0 on success
+
+ @todo
+ Add check of calculation of GROUP functions and fields:
+ SELECT COUNT(*)+table.col1 from table1;
+
+ @retval
+ -1 on error
+ @retval
+ 0 on success
*/
int
JOIN::prepare(Item ***rref_pointer_array,
@@ -448,7 +467,7 @@ JOIN::prepare(Item ***rref_pointer_array,
select_lex= select_lex_arg;
select_lex->join= this;
join_list= &select_lex->top_join_list;
- union_part= (unit_arg->first_select()->next_select() != 0);
+ union_part= unit_arg->is_union();
thd->lex->current_select->is_item_list_lookup= 1;
/*
@@ -462,9 +481,8 @@ JOIN::prepare(Item ***rref_pointer_array,
if (!(select_options & OPTION_SETUP_TABLES_DONE) &&
setup_tables_and_check_access(thd, &select_lex->context, join_list,
- tables_list, &conds,
- &select_lex->leaf_tables, FALSE,
- SELECT_ACL, SELECT_ACL))
+ tables_list, &select_lex->leaf_tables,
+ FALSE, SELECT_ACL, SELECT_ACL))
DBUG_RETURN(-1);
TABLE_LIST *table_ptr;
@@ -475,7 +493,7 @@ JOIN::prepare(Item ***rref_pointer_array,
if (setup_wild(thd, tables_list, fields_list, &all_fields, wild_num) ||
select_lex->setup_ref_array(thd, og_num) ||
- setup_fields(thd, (*rref_pointer_array), fields_list, 1,
+ setup_fields(thd, (*rref_pointer_array), fields_list, MARK_COLUMNS_READ,
&all_fields, 1) ||
setup_without_group(thd, (*rref_pointer_array), tables_list,
select_lex->leaf_tables, fields_list,
@@ -495,7 +513,7 @@ JOIN::prepare(Item ***rref_pointer_array,
(having->fix_fields(thd, &having) ||
having->check_cols(1)));
select_lex->having_fix_field= 0;
- if (having_fix_rc || thd->net.report_error)
+ if (having_fix_rc || thd->is_error())
DBUG_RETURN(-1); /* purecov: inspected */
thd->lex->allow_sum_func= save_allow_sum_func;
}
@@ -609,13 +627,6 @@ JOIN::prepare(Item ***rref_pointer_array,
goto err; /* purecov: inspected */
}
}
-#ifdef NOT_NEEDED
- else if (!group_list && procedure->flags & PROC_GROUP)
- {
- my_message(ER_NO_GROUP_FOR_PROC, MYF(0));
- goto err;
- }
-#endif
if (order && (procedure->flags & PROC_NO_SORT))
{ /* purecov: inspected */
my_message(ER_ORDER_WITH_PROC, ER(ER_ORDER_WITH_PROC),
@@ -721,7 +732,7 @@ void JOIN::remove_subq_pushed_predicates(Item **where)
static void save_index_subquery_explain_info(JOIN_TAB *join_tab, Item* where)
{
join_tab->packed_info= TAB_INFO_HAVE_VALUE;
- if (join_tab->table->used_keys.is_set(join_tab->ref.key))
+ if (join_tab->table->covering_keys.is_set(join_tab->ref.key))
join_tab->packed_info |= TAB_INFO_USING_INDEX;
if (where)
join_tab->packed_info |= TAB_INFO_USING_WHERE;
@@ -736,11 +747,16 @@ static void save_index_subquery_explain_info(JOIN_TAB *join_tab, Item* where)
}
-/*
+/**
global select optimisation.
- return 0 - success
- 1 - error
- error code saved in field 'error'
+
+ @note
+ error code saved in field 'error'
+
+ @retval
+ 0 success
+ @retval
+ 1 error
*/
int
@@ -752,9 +768,7 @@ JOIN::optimize()
DBUG_RETURN(0);
optimized= 1;
- if (thd->lex->orig_sql_command != SQLCOM_SHOW_STATUS)
- thd->status_var.last_query_cost= 0.0;
-
+ thd_proc_info(thd, "optimizing");
row_limit= ((select_distinct || order || group_list) ? HA_POS_ERROR :
unit->select_limit_cnt);
/* select_limit is used to decide if we are likely to scan the whole table */
@@ -814,7 +828,7 @@ JOIN::optimize()
}
conds= optimize_cond(this, conds, join_list, &cond_value);
- if (thd->net.report_error)
+ if (thd->is_error())
{
error= 1;
DBUG_PRINT("error",("Error from optimize_cond"));
@@ -823,7 +837,7 @@ JOIN::optimize()
{
having= optimize_cond(this, having, join_list, &having_value);
- if (thd->net.report_error)
+ if (thd->is_error())
{
error= 1;
DBUG_PRINT("error",("Error from optimize_cond"));
@@ -847,6 +861,26 @@ JOIN::optimize()
}
}
+#ifdef WITH_PARTITION_STORAGE_ENGINE
+ {
+ TABLE_LIST *tbl;
+ for (tbl= select_lex->leaf_tables; tbl; tbl= tbl->next_leaf)
+ {
+ /*
+ If tbl->embedding!=NULL that means that this table is in the inner
+ part of the nested outer join, and we can't do partition pruning
+ (TODO: check if this limitation can be lifted)
+ */
+ if (!tbl->embedding)
+ {
+ Item *prune_cond= tbl->on_expr? tbl->on_expr : conds;
+ tbl->table->no_partitions_used= prune_partitions(thd, tbl->table,
+ prune_cond);
+ }
+ }
+ }
+#endif
+
/* Optimize count(*), min() and max() */
if (tables_list && tmp_table_param.sum_func_count && ! group_list)
{
@@ -863,15 +897,23 @@ JOIN::optimize()
{
DBUG_PRINT("info",("No matching min/max row"));
zero_result_cause= "No matching min/max row";
+ tables= 0;
error=0;
DBUG_RETURN(0);
}
if (res > 1)
{
- thd->fatal_error();
error= res;
DBUG_PRINT("error",("Error from opt_sum_query"));
- DBUG_RETURN(1);
+ DBUG_RETURN(1);
+ }
+ if (res < 0)
+ {
+ DBUG_PRINT("info",("No matching min/max row"));
+ zero_result_cause= "No matching min/max row";
+ tables= 0;
+ error=0;
+ DBUG_RETURN(0);
}
DBUG_PRINT("info",("Select tables optimized away"));
zero_result_cause= "Select tables optimized away";
@@ -892,7 +934,8 @@ JOIN::optimize()
make_cond_for_table(conds, PSEUDO_TABLE_BITS, 0);
DBUG_EXECUTE("where",
print_where(table_independent_conds,
- "where after opt_sum_query()"););
+ "where after opt_sum_query()",
+ QT_ORDINARY););
conds= table_independent_conds;
}
}
@@ -907,7 +950,7 @@ JOIN::optimize()
sort_by_table= get_sort_by_table(order, group_list, select_lex->leaf_tables);
/* Calculate how to do the join */
- thd->proc_info= "statistics";
+ thd_proc_info(thd, "statistics");
if (make_join_statistics(this, select_lex->leaf_tables, conds, &keyuse) ||
thd->is_fatal_error)
{
@@ -917,7 +960,7 @@ JOIN::optimize()
/* Remove distinct if only const tables */
select_distinct= select_distinct && (const_tables != tables);
- thd->proc_info= "preparing";
+ thd_proc_info(thd, "preparing");
if (result->initialize_tables(this))
{
DBUG_PRINT("error",("Error: initialize_tables() failed"));
@@ -972,7 +1015,10 @@ JOIN::optimize()
{
conds= substitute_for_best_equal_field(conds, cond_equal, map2table);
conds->update_used_tables();
- DBUG_EXECUTE("where", print_where(conds, "after substitute_best_equal"););
+ DBUG_EXECUTE("where",
+ print_where(conds,
+ "after substitute_best_equal",
+ QT_ORDINARY););
}
/*
@@ -1009,7 +1055,7 @@ JOIN::optimize()
{
ORDER *org_order= order;
order=remove_const(this, order,conds,1, &simple_order);
- if (thd->net.report_error)
+ if (thd->is_error())
{
error= 1;
DBUG_PRINT("error",("Error from remove_const"));
@@ -1064,6 +1110,12 @@ JOIN::optimize()
*/
if (!order || test_if_subpart(group_list, order))
order= skip_sort_order ? 0 : group_list;
+ /*
+ If we have an IGNORE INDEX FOR GROUP BY(fields) clause, this must be
+ rewritten to IGNORE INDEX FOR ORDER BY(fields).
+ */
+ join_tab->table->keys_in_use_for_order_by=
+ join_tab->table->keys_in_use_for_group_by;
group_list= 0;
group= 0;
}
@@ -1100,14 +1152,15 @@ JOIN::optimize()
JOIN_TAB *tab= &join_tab[const_tables];
bool all_order_fields_used;
if (order)
- skip_sort_order= test_if_skip_sort_order(tab, order, select_limit, 1);
+ skip_sort_order= test_if_skip_sort_order(tab, order, select_limit, 1,
+ &tab->table->keys_in_use_for_order_by);
if ((group_list=create_distinct_group(thd, select_lex->ref_pointer_array,
order, fields_list, all_fields,
&all_order_fields_used)))
{
bool skip_group= (skip_sort_order &&
- test_if_skip_sort_order(tab, group_list, select_limit,
- 1) != 0);
+ test_if_skip_sort_order(tab, group_list, select_limit, 1,
+ &tab->table->keys_in_use_for_group_by) != 0);
count_field_types(select_lex, &tmp_table_param, all_fields, 0);
if ((skip_group && all_order_fields_used) ||
select_limit == HA_POS_ERROR ||
@@ -1142,7 +1195,7 @@ JOIN::optimize()
group_list= remove_const(this, (old_group_list= group_list), conds,
rollup.state == ROLLUP::STATE_NONE,
&simple_group);
- if (thd->net.report_error)
+ if (thd->is_error())
{
error= 1;
DBUG_PRINT("error",("Error from remove_const"));
@@ -1165,7 +1218,7 @@ JOIN::optimize()
{
group_list= procedure->group= remove_const(this, procedure->group, conds,
1, &simple_group);
- if (thd->net.report_error)
+ if (thd->is_error())
{
error= 1;
DBUG_PRINT("error",("Error from remove_const"));
@@ -1218,7 +1271,7 @@ JOIN::optimize()
if (!group_list && !order &&
unit->item && unit->item->substype() == Item_subselect::IN_SUBS &&
tables == 1 && conds &&
- !unit->first_select()->next_select())
+ !unit->is_union())
{
if (!having)
{
@@ -1272,23 +1325,16 @@ JOIN::optimize()
}
/*
- Need to tell Innobase that to play it safe, it should fetch all
- columns of the tables: this is because MySQL may build row
- pointers for the rows, and for all columns of the primary key the
- field->query_id has not necessarily been set to thd->query_id by
- MySQL.
+ Need to tell handlers that to play it safe, it should fetch all
+ columns of the primary key of the tables: this is because MySQL may
+ build row pointers for the rows, and for all columns of the primary key
+ the read set has not necessarily been set by the server code.
*/
-
-#ifdef HAVE_INNOBASE_DB
if (need_tmp || select_distinct || group_list || order)
{
- for (uint i_h = const_tables; i_h < tables; i_h++)
- {
- TABLE* table_h = join_tab[i_h].table;
- table_h->file->extra(HA_EXTRA_RETRIEVE_PRIMARY_KEY);
- }
+ for (uint i = const_tables; i < tables; i++)
+ join_tab[i].table->prepare_for_position();
}
-#endif
DBUG_EXECUTE("info",TEST_join(this););
@@ -1306,15 +1352,18 @@ JOIN::optimize()
join_tab[const_tables].type != JT_REF_OR_NULL &&
(order && simple_order || group_list && simple_group))
{
- if (add_ref_to_table_cond(thd,&join_tab[const_tables]))
+ if (add_ref_to_table_cond(thd,&join_tab[const_tables])) {
DBUG_RETURN(1);
+ }
}
if (!(select_options & SELECT_BIG_RESULT) &&
((group_list &&
(!simple_group ||
!test_if_skip_sort_order(&join_tab[const_tables], group_list,
- unit->select_limit_cnt, 0))) ||
+ unit->select_limit_cnt, 0,
+ &join_tab[const_tables].table->
+ keys_in_use_for_group_by))) ||
select_distinct) &&
tmp_table_param.quick_group && !procedure)
{
@@ -1329,7 +1378,7 @@ JOIN::optimize()
for (ORDER *tmp_order= order; tmp_order ; tmp_order=tmp_order->next)
{
Item *item= *tmp_order->item;
- if (item->walk(&Item::is_expensive_processor,(byte*)0))
+ if (item->walk(&Item::is_expensive_processor, 0, (uchar*)0))
{
/* Force tmp table without sort */
need_tmp=1; simple_order=simple_group=0;
@@ -1365,7 +1414,7 @@ JOIN::optimize()
if (need_tmp)
{
DBUG_PRINT("info",("Creating tmp table"));
- thd->proc_info="Creating tmp table";
+ thd_proc_info(thd, "Creating tmp table");
init_items_ref_array();
@@ -1385,7 +1434,7 @@ JOIN::optimize()
!thd->lex->current_select->with_sum_func) ?
select_limit : HA_POS_ERROR;
- if (!(exec_tmp_table1 =
+ if (!(exec_tmp_table1=
create_tmp_table(thd, &tmp_table_param, all_fields,
tmp_group,
group_list ? 0 : select_distinct,
@@ -1393,7 +1442,9 @@ JOIN::optimize()
select_options,
tmp_rows_limit,
(char *) "")))
+ {
DBUG_RETURN(1);
+ }
/*
We don't have to store rows in temp table that doesn't match HAVING if:
@@ -1413,28 +1464,34 @@ JOIN::optimize()
if (group_list && simple_group)
{
DBUG_PRINT("info",("Sorting for group"));
- thd->proc_info="Sorting for group";
+ thd_proc_info(thd, "Sorting for group");
if (create_sort_index(thd, this, group_list,
- HA_POS_ERROR, HA_POS_ERROR) ||
+ HA_POS_ERROR, HA_POS_ERROR, FALSE) ||
alloc_group_fields(this, group_list) ||
make_sum_func_list(all_fields, fields_list, 1) ||
setup_sum_funcs(thd, sum_funcs))
- DBUG_RETURN(1);
+ {
+ DBUG_RETURN(1);
+ }
group_list=0;
}
else
{
if (make_sum_func_list(all_fields, fields_list, 0) ||
setup_sum_funcs(thd, sum_funcs))
- DBUG_RETURN(1);
+ {
+ DBUG_RETURN(1);
+ }
+
if (!group_list && ! exec_tmp_table1->distinct && order && simple_order)
{
- DBUG_PRINT("info",("Sorting for order"));
- thd->proc_info="Sorting for order";
- if (create_sort_index(thd, this, order,
- HA_POS_ERROR, HA_POS_ERROR))
- DBUG_RETURN(1);
- order=0;
+ thd_proc_info(thd, "Sorting for order");
+ if (create_sort_index(thd, this, order,
+ HA_POS_ERROR, HA_POS_ERROR, TRUE))
+ {
+ DBUG_RETURN(1);
+ }
+ order=0;
}
}
@@ -1459,7 +1516,9 @@ JOIN::optimize()
{
/* Should always succeed */
if (test_if_skip_sort_order(&join_tab[const_tables],
- order, unit->select_limit_cnt, 0))
+ order, unit->select_limit_cnt, 0,
+ &join_tab[const_tables].table->
+ keys_in_use_for_order_by))
order=0;
}
}
@@ -1478,8 +1537,8 @@ JOIN::optimize()
}
-/*
- Restore values in temporary join
+/**
+ Restore values in temporary join.
*/
void JOIN::restore_tmp()
{
@@ -1501,14 +1560,14 @@ JOIN::reinit()
if (exec_tmp_table1)
{
exec_tmp_table1->file->extra(HA_EXTRA_RESET_STATE);
- exec_tmp_table1->file->delete_all_rows();
+ exec_tmp_table1->file->ha_delete_all_rows();
free_io_cache(exec_tmp_table1);
filesort_free_buffers(exec_tmp_table1,0);
}
if (exec_tmp_table2)
{
exec_tmp_table2->file->extra(HA_EXTRA_RESET_STATE);
- exec_tmp_table2->file->delete_all_rows();
+ exec_tmp_table2->file->ha_delete_all_rows();
free_io_cache(exec_tmp_table2);
filesort_free_buffers(exec_tmp_table2,0);
}
@@ -1559,7 +1618,7 @@ JOIN::save_join_tab()
{
if (!join_tab_save && select_lex->master_unit()->uncacheable)
{
- if (!(join_tab_save= (JOIN_TAB*)thd->memdup((gptr) join_tab,
+ if (!(join_tab_save= (JOIN_TAB*)thd->memdup((uchar*) join_tab,
sizeof(JOIN_TAB) * tables)))
return 1;
}
@@ -1567,8 +1626,16 @@ JOIN::save_join_tab()
}
-/*
- Exec select
+/**
+ Exec select.
+
+ @todo
+ Note, that create_sort_index calls test_if_skip_sort_order and may
+ finally replace sorting with index scan if there is a LIMIT clause in
+ the query. It's never shown in EXPLAIN!
+
+ @todo
+ When can we have here thd->net.report_error not zero?
*/
void
JOIN::exec()
@@ -1577,6 +1644,7 @@ JOIN::exec()
int tmp_error;
DBUG_ENTER("JOIN::exec");
+ thd_proc_info(thd, "executing");
error= 0;
if (procedure)
{
@@ -1658,6 +1726,10 @@ JOIN::exec()
DBUG_VOID_RETURN;
}
+ if ((this->select_lex->options & OPTION_SCHEMA_TABLE) &&
+ get_schema_tables_result(this, PROCESSED_BY_JOIN_EXEC))
+ DBUG_VOID_RETURN;
+
if (select_options & SELECT_DESCRIBE)
{
/*
@@ -1680,7 +1752,9 @@ JOIN::exec()
(const_tables == tables ||
((simple_order || skip_sort_order) &&
test_if_skip_sort_order(&join_tab[const_tables], order,
- select_limit, 0))))
+ select_limit, 0,
+ &join_tab[const_tables].table->
+ keys_in_use_for_query))))
order=0;
having= tmp_having;
select_describe(this, need_tmp,
@@ -1701,13 +1775,6 @@ JOIN::exec()
*/
curr_join->examined_rows= 0;
- if ((curr_join->select_lex->options & OPTION_SCHEMA_TABLE) &&
- !thd->lex->describe &&
- get_schema_tables_result(curr_join, PROCESSED_BY_JOIN_EXEC))
- {
- DBUG_VOID_RETURN;
- }
-
/* Create a tmp table if distinct or if the sort is too complicated */
if (need_tmp)
{
@@ -1724,8 +1791,11 @@ JOIN::exec()
curr_tmp_table= exec_tmp_table1;
/* Copy data to the temporary table */
- thd->proc_info= "Copying to tmp table";
+ thd_proc_info(thd, "Copying to tmp table");
DBUG_PRINT("info", ("%s", thd->proc_info));
+ if (!curr_join->sort_and_group &&
+ curr_join->const_tables != curr_join->tables)
+ curr_join->join_tab[curr_join->const_tables].sorted= 0;
if ((tmp_error= do_select(curr_join, (List<Item> *) 0, curr_tmp_table, 0)))
{
error= tmp_error;
@@ -1848,13 +1918,13 @@ JOIN::exec()
}
if (curr_join->group_list)
{
- thd->proc_info= "Creating sort index";
+ thd_proc_info(thd, "Creating sort index");
if (curr_join->join_tab == join_tab && save_join_tab())
{
DBUG_VOID_RETURN;
}
if (create_sort_index(thd, curr_join, curr_join->group_list,
- HA_POS_ERROR, HA_POS_ERROR) ||
+ HA_POS_ERROR, HA_POS_ERROR, FALSE) ||
make_group_fields(this, curr_join))
{
DBUG_VOID_RETURN;
@@ -1862,7 +1932,7 @@ JOIN::exec()
sortorder= curr_join->sortorder;
}
- thd->proc_info="Copying to group table";
+ thd_proc_info(thd, "Copying to group table");
DBUG_PRINT("info", ("%s", thd->proc_info));
tmp_error= -1;
if (curr_join != this)
@@ -1883,6 +1953,9 @@ JOIN::exec()
1, TRUE))
DBUG_VOID_RETURN;
curr_join->group_list= 0;
+ if (!curr_join->sort_and_group &&
+ curr_join->const_tables != curr_join->tables)
+ curr_join->join_tab[curr_join->const_tables].sorted= 0;
if (setup_sum_funcs(curr_join->thd, curr_join->sum_funcs) ||
(tmp_error= do_select(curr_join, (List<Item> *) 0, curr_tmp_table,
0)))
@@ -1918,7 +1991,7 @@ JOIN::exec()
curr_join->join_free(); /* Free quick selects */
if (curr_join->select_distinct && ! curr_join->group_list)
{
- thd->proc_info="Removing duplicates";
+ thd_proc_info(thd, "Removing duplicates");
if (curr_join->tmp_having)
curr_join->tmp_having->update_used_tables();
if (remove_duplicates(curr_join, curr_tmp_table,
@@ -1981,7 +2054,7 @@ JOIN::exec()
if (curr_join->group_list || curr_join->order)
{
DBUG_PRINT("info",("Sorting for send_fields"));
- thd->proc_info="Sorting result";
+ 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)
@@ -2017,12 +2090,14 @@ JOIN::exec()
curr_table->select_cond= curr_table->select->cond;
curr_table->select_cond->top_level_item();
DBUG_EXECUTE("where",print_where(curr_table->select->cond,
- "select and having"););
+ "select and having",
+ QT_ORDINARY););
curr_join->tmp_having= make_cond_for_table(curr_join->tmp_having,
~ (table_map) 0,
~used_tables);
DBUG_EXECUTE("where",print_where(curr_join->tmp_having,
- "having after sort"););
+ "having after sort",
+ QT_ORDINARY););
}
}
{
@@ -2069,15 +2144,26 @@ JOIN::exec()
curr_join->group_list : curr_join->order,
curr_join->select_limit,
(select_options & OPTION_FOUND_ROWS ?
- HA_POS_ERROR : unit->select_limit_cnt)))
+ HA_POS_ERROR : unit->select_limit_cnt),
+ curr_join->group_list ? TRUE : FALSE))
DBUG_VOID_RETURN;
sortorder= curr_join->sortorder;
+ if (curr_join->const_tables != curr_join->tables &&
+ !curr_join->join_tab[curr_join->const_tables].table->sort.io_cache)
+ {
+ /*
+ If no IO cache exists for the first table then we are using an
+ INDEX SCAN and no filesort. Thus we should not remove the sorted
+ attribute on the INDEX SCAN.
+ */
+ skip_sort_order= 1;
+ }
}
}
- /* XXX: When can we have here thd->net.report_error not zero? */
- if (thd->net.report_error)
+ /* XXX: When can we have here thd->is_error() not zero? */
+ if (thd->is_error())
{
- error= thd->net.report_error;
+ error= thd->is_error();
DBUG_VOID_RETURN;
}
curr_join->having= curr_join->tmp_having;
@@ -2105,7 +2191,7 @@ JOIN::exec()
}
else
{
- thd->proc_info="Sending data";
+ 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),
@@ -2134,8 +2220,11 @@ JOIN::exec()
}
-/*
- Clean up join. Return error that hold JOIN.
+/**
+ Clean up join.
+
+ @return
+ Return error that hold JOIN.
*/
int
@@ -2150,9 +2239,7 @@ JOIN::destroy()
{
JOIN_TAB *tab, *end;
for (tab= join_tab, end= tab+tables ; tab != end ; tab++)
- {
tab->cleanup();
- }
}
tmp_join->tmp_join= 0;
tmp_table_param.copy_field=0;
@@ -2171,49 +2258,48 @@ JOIN::destroy()
DBUG_RETURN(error);
}
-/*
+/**
An entry point to single-unit select (a select without UNION).
- SYNOPSIS
- mysql_select()
-
- thd thread handler
- rref_pointer_array a reference to ref_pointer_array of
- the top-level select_lex for this query
- tables list of all tables used in this query.
- The tables have been pre-opened.
- wild_num number of wildcards used in the top level
- select of this query.
- For example statement
- SELECT *, t1.*, catalog.t2.* FROM t0, t1, t2;
- has 3 wildcards.
- fields list of items in SELECT list of the top-level
- select
- e.g. SELECT a, b, c FROM t1 will have Item_field
- for a, b and c in this list.
- conds top level item of an expression representing
- WHERE clause of the top level select
- og_num total number of ORDER BY and GROUP BY clauses
- arguments
- order linked list of ORDER BY agruments
- group linked list of GROUP BY arguments
- having top level item of HAVING expression
- proc_param list of PROCEDUREs
- select_options select options (BIG_RESULT, etc)
- result an instance of result set handling class.
- This object is responsible for send result
- set rows to the client or inserting them
- into a table.
- select_lex the only SELECT_LEX of this query
- unit top-level UNIT of this query
- UNIT is an artificial object created by the parser
- for every SELECT clause.
- e.g. SELECT * FROM t1 WHERE a1 IN (SELECT * FROM t2)
- has 2 unions.
-
- RETURN VALUE
- FALSE success
- TRUE an error
+ @param thd thread handler
+ @param rref_pointer_array a reference to ref_pointer_array of
+ the top-level select_lex for this query
+ @param tables list of all tables used in this query.
+ The tables have been pre-opened.
+ @param wild_num number of wildcards used in the top level
+ select of this query.
+ For example statement
+ SELECT *, t1.*, catalog.t2.* FROM t0, t1, t2;
+ has 3 wildcards.
+ @param fields list of items in SELECT list of the top-level
+ select
+ e.g. SELECT a, b, c FROM t1 will have Item_field
+ for a, b and c in this list.
+ @param conds top level item of an expression representing
+ WHERE clause of the top level select
+ @param og_num total number of ORDER BY and GROUP BY clauses
+ arguments
+ @param order linked list of ORDER BY agruments
+ @param group linked list of GROUP BY arguments
+ @param having top level item of HAVING expression
+ @param proc_param list of PROCEDUREs
+ @param select_options select options (BIG_RESULT, etc)
+ @param result an instance of result set handling class.
+ This object is responsible for send result
+ set rows to the client or inserting them
+ into a table.
+ @param select_lex the only SELECT_LEX of this query
+ @param unit top-level UNIT of this query
+ UNIT is an artificial object created by the
+ parser for every SELECT clause.
+ e.g.
+ SELECT * FROM t1 WHERE a1 IN (SELECT * FROM t2)
+ has 2 unions.
+
+ @retval
+ FALSE success
+ @retval
+ TRUE an error
*/
bool
@@ -2265,7 +2351,7 @@ mysql_select(THD *thd, Item ***rref_pointer_array,
{
if (!(join= new JOIN(thd, fields, select_options, result)))
DBUG_RETURN(TRUE);
- thd->proc_info="init";
+ thd_proc_info(thd, "init");
thd->used_tables=0; // Updated by setup_fields
if (err= join->prepare(rref_pointer_array, tables, wild_num,
conds, og_num, order, group, having, proc_param,
@@ -2286,7 +2372,7 @@ mysql_select(THD *thd, Item ***rref_pointer_array,
join->having_history= (join->having?join->having:join->tmp_having);
}
- if (thd->net.report_error)
+ if (thd->is_error())
goto err;
join->exec();
@@ -2310,9 +2396,9 @@ mysql_select(THD *thd, Item ***rref_pointer_array,
err:
if (free_join)
{
- thd->proc_info="end";
+ thd_proc_info(thd, "end");
err|= select_lex->cleanup();
- DBUG_RETURN(err || thd->net.report_error);
+ DBUG_RETURN(err || thd->is_error());
}
DBUG_RETURN(join->error);
}
@@ -2329,7 +2415,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
- char buff[STACK_BUFF_ALLOC];
+ uchar buff[STACK_BUFF_ALLOC];
#endif
if (check_stack_overrun(thd, STACK_MIN_SIZE, buff))
DBUG_RETURN(0); // Fatal error flag is set
@@ -2364,12 +2450,13 @@ typedef struct st_sargable_param
uint num_values; /* number of values in the above array */
} SARGABLE_PARAM;
-/*
- Calculate the best possible join and initialize the join structure
+/**
+ Calculate the best possible join and initialize the join structure.
- RETURN VALUES
- 0 ok
- 1 Fatal error
+ @retval
+ 0 ok
+ @retval
+ 1 Fatal error
*/
static bool
@@ -2431,13 +2518,18 @@ make_join_statistics(JOIN *join, TABLE_LIST *tables, COND *conds,
s->dependent= tables->dep_tables;
s->key_dependent= 0;
if (tables->schema_table)
- table->file->records= 2;
+ table->file->stats.records= 2;
+ table->quick_condition_rows= table->file->stats.records;
s->on_expr_ref= &tables->on_expr;
if (*s->on_expr_ref)
{
/* s is the only inner table of an outer join */
- if (!table->file->records && !embedding)
+#ifdef WITH_PARTITION_STORAGE_ENGINE
+ if ((!table->file->stats.records || table->no_partitions_used) && !embedding)
+#else
+ if (!table->file->stats.records && !embedding)
+#endif
{ // Empty table
s->dependent= 0; // Ignore LEFT JOIN depend.
set_position(join,const_count++,s,(KEYUSE*) 0);
@@ -2464,9 +2556,15 @@ make_join_statistics(JOIN *join, TABLE_LIST *tables, COND *conds,
while (embedding);
continue;
}
-
- if ((table->s->system || table->file->records <= 1) && ! s->dependent &&
- !(table->file->table_flags() & HA_NOT_EXACT_COUNT) &&
+#ifdef WITH_PARTITION_STORAGE_ENGINE
+ const bool no_partitions_used= table->no_partitions_used;
+#else
+ const bool no_partitions_used= FALSE;
+#endif
+ if ((table->s->system || table->file->stats.records <= 1 ||
+ no_partitions_used) &&
+ !s->dependent &&
+ (table->file->ha_table_flags() & HA_STATS_RECORDS_IS_EXACT) &&
!table->fulltext_searched && !join->no_const_tables)
{
set_position(join,const_count++,s,(KEYUSE*) 0);
@@ -2590,8 +2688,8 @@ make_join_statistics(JOIN *join, TABLE_LIST *tables, COND *conds,
// All dep. must be constants
if (s->dependent & ~(found_const_table_map))
continue;
- if (table->file->records <= 1L &&
- !(table->file->table_flags() & HA_NOT_EXACT_COUNT) &&
+ if (table->file->stats.records <= 1L &&
+ (table->file->ha_table_flags() & HA_STATS_RECORDS_IS_EXACT) &&
!table->pos_in_table_list->embedding)
{ // system table
int tmp= 0;
@@ -2703,7 +2801,7 @@ make_join_statistics(JOIN *join, TABLE_LIST *tables, COND *conds,
continue;
}
/* Approximate found rows and time to read them */
- s->found_records=s->records=s->table->file->records;
+ s->found_records=s->records=s->table->file->stats.records;
s->read_time=(ha_rows) s->table->file->scan_time();
/*
@@ -2782,7 +2880,7 @@ make_join_statistics(JOIN *join, TABLE_LIST *tables, COND *conds,
}
else
{
- memcpy((gptr) join->best_positions,(gptr) join->positions,
+ memcpy((uchar*) join->best_positions,(uchar*) join->positions,
sizeof(POSITION)*join->const_tables);
join->best_read=1.0;
}
@@ -2799,13 +2897,14 @@ make_join_statistics(JOIN *join, TABLE_LIST *tables, COND *conds,
keyuse Pointer to possible keys
*****************************************************************************/
-typedef struct key_field_t { // Used when finding key fields
+/// Used when finding key fields
+typedef struct key_field_t {
Field *field;
- Item *val; // May be empty if diff constant
+ Item *val; ///< May be empty if diff constant
uint level;
uint optimize;
bool eq_func;
- /*
+ /**
If true, the condition this struct represents will not be satisfied
when val IS NULL.
*/
@@ -2817,22 +2916,28 @@ typedef struct key_field_t { // Used when finding key fields
#define KEY_OPTIMIZE_EXISTS 1
#define KEY_OPTIMIZE_REF_OR_NULL 2
-/*
- Merge new key definitions to old ones, remove those not used in both
+/**
+ Merge new key definitions to old ones, remove those not used in both.
- This is called for OR between different levels
+ This is called for OR between different levels.
To be able to do 'ref_or_null' we merge a comparison of a column
and 'column IS NULL' to one test. This is useful for sub select queries
- that are internally transformed to something like:
+ that are internally transformed to something like:.
+ @code
SELECT * FROM t1 WHERE t1.key=outer_ref_field or t1.key IS NULL
+ @endcode
- KEY_FIELD::null_rejecting is processed as follows:
+ KEY_FIELD::null_rejecting is processed as follows: @n
result has null_rejecting=true if it is set for both ORed references.
for example:
- (t2.key = t1.field OR t2.key = t1.field) -> null_rejecting=true
- (t2.key = t1.field OR t2.key <=> t1.field) -> null_rejecting=false
+ - (t2.key = t1.field OR t2.key = t1.field) -> null_rejecting=true
+ - (t2.key = t1.field OR t2.key <=> t1.field) -> null_rejecting=false
+
+ @todo
+ The result of this is that we're missing some 'ref' accesses.
+ OptimizerTeam: Fix this
*/
static KEY_FIELD *
@@ -2943,25 +3048,23 @@ merge_key_fields(KEY_FIELD *start,KEY_FIELD *new_fields,KEY_FIELD *end,
}
-/*
+/**
Add a possible key to array of possible keys if it's usable as a key
- SYNPOSIS
- add_key_field()
- key_fields Pointer to add key, if usable
- and_level And level, to be stored in KEY_FIELD
- cond Condition predicate
- field Field used in comparision
- eq_func True if we used =, <=> or IS NULL
- value Value used for comparison with field
- usable_tables Tables which can be used for key optimization
- sargables IN/OUT Array of found sargable candidates
+ @param key_fields Pointer to add key, if usable
+ @param and_level And level, to be stored in KEY_FIELD
+ @param cond Condition predicate
+ @param field Field used in comparision
+ @param eq_func True if we used =, <=> or IS NULL
+ @param value Value used for comparison with field
+ @param usable_tables Tables which can be used for key optimization
+ @param sargables IN/OUT Array of found sargable candidates
- NOTES
+ @note
If we are doing a NOT NULL comparison on a NOT NULL field in a outer join
table, we store this to be able to do not exists optimization later.
- RETURN
+ @returns
*key_fields is incremented if we stored a key in the array
*/
@@ -3021,7 +3124,10 @@ add_key_field(KEY_FIELD **key_fields,uint and_level, Item_func *cond,
bool is_const=1;
for (uint i=0; i<num_values; i++)
- is_const&= value[i]->const_item();
+ {
+ if (!(is_const&= value[i]->const_item()))
+ break;
+ }
if (is_const)
stat[0].const_keys.merge(possible_keys);
else if (!eq_func)
@@ -3109,26 +3215,25 @@ add_key_field(KEY_FIELD **key_fields,uint and_level, Item_func *cond,
(*key_fields)++;
}
-/*
- Add possible keys to array of possible keys originated from a simple predicate
-
- SYNPOSIS
- add_key_equal_fields()
- key_fields Pointer to add key, if usable
- and_level And level, to be stored in KEY_FIELD
- cond Condition predicate
- field Field used in comparision
- eq_func True if we used =, <=> or IS NULL
- value Value used for comparison with field
- Is NULL for BETWEEN and IN
- usable_tables Tables which can be used for key optimization
- sargables IN/OUT Array of found sargable candidates
-
- NOTES
+/**
+ Add possible keys to array of possible keys originated from a simple
+ predicate.
+
+ @param key_fields Pointer to add key, if usable
+ @param and_level And level, to be stored in KEY_FIELD
+ @param cond Condition predicate
+ @param field Field used in comparision
+ @param eq_func True if we used =, <=> or IS NULL
+ @param value Value used for comparison with field
+ Is NULL for BETWEEN and IN
+ @param usable_tables Tables which can be used for key optimization
+ @param sargables IN/OUT Array of found sargable candidates
+
+ @note
If field items f1 and f2 belong to the same multiple equality and
a key is added for f1, the the same key is added for f2.
- RETURN
+ @returns
*key_fields is incremented if we stored a key in the array
*/
@@ -3214,7 +3319,7 @@ add_key_fields(JOIN *join, KEY_FIELD **key_fields, uint *and_level,
if (!join->group_list && !join->order &&
join->unit->item &&
join->unit->item->substype() == Item_subselect::IN_SUBS &&
- !join->unit->first_select()->next_select())
+ !join->unit->is_union())
{
KEY_FIELD *save= *key_fields;
add_key_fields(join, key_fields, and_level, cond_arg, usable_tables,
@@ -3359,9 +3464,13 @@ add_key_fields(JOIN *join, KEY_FIELD **key_fields, uint *and_level,
}
}
-/*
- Add all keys with uses 'field' for some keypart
- If field->and_level != and_level then only mark key_part as const_part
+/**
+ Add all keys with uses 'field' for some keypart.
+
+ If field->and_level != and_level then only mark key_part as const_part.
+
+ @todo
+ ft-keys in non-ft queries. SerG
*/
static uint
@@ -3402,7 +3511,7 @@ add_key_part(DYNAMIC_ARRAY *keyuse_array,KEY_FIELD *key_field)
keyuse.optimize= key_field->optimize & KEY_OPTIMIZE_REF_OR_NULL;
keyuse.null_rejecting= key_field->null_rejecting;
keyuse.cond_guard= key_field->cond_guard;
- VOID(insert_dynamic(keyuse_array,(gptr) &keyuse));
+ VOID(insert_dynamic(keyuse_array,(uchar*) &keyuse));
}
}
}
@@ -3469,7 +3578,7 @@ add_ft_keys(DYNAMIC_ARRAY *keyuse_array,
keyuse.used_tables=cond_func->key_item()->used_tables();
keyuse.optimize= 0;
keyuse.keypart_map= 0;
- VOID(insert_dynamic(keyuse_array,(gptr) &keyuse));
+ VOID(insert_dynamic(keyuse_array,(uchar*) &keyuse));
}
@@ -3494,33 +3603,37 @@ sort_keyuse(KEYUSE *a,KEYUSE *b)
/*
- Add to KEY_FIELD array all 'ref' access candidates within nested join
-
- SYNPOSIS
- add_key_fields_for_nj()
- nested_join_table IN Nested join pseudo-table to process
- end INOUT End of the key field array
- and_level INOUT And-level
- sargables IN/OUT Array of found sargable candidates
+ Add to KEY_FIELD array all 'ref' access candidates within nested join.
- DESCRIPTION
This function populates KEY_FIELD array with entries generated from the
ON condition of the given nested join, and does the same for nested joins
contained within this nested join.
- NOTES
+ @param[in] nested_join_table Nested join pseudo-table to process
+ @param[in,out] end End of the key field array
+ @param[in,out] and_level And-level
+ @param[in,out] sargables Array of found sargable candidates
+
+
+ @note
We can add accesses to the tables that are direct children of this nested
join (1), and are not inner tables w.r.t their neighbours (2).
Example for #1 (outer brackets pair denotes nested join this function is
invoked for):
+ @code
... LEFT JOIN (t1 LEFT JOIN (t2 ... ) ) ON cond
+ @endcode
Example for #2:
+ @code
... LEFT JOIN (t1 LEFT JOIN t2 ) ON cond
+ @endcode
In examples 1-2 for condition cond, we can add 'ref' access candidates to
t1 only.
Example #3:
+ @code
... LEFT JOIN (t1, t2 LEFT JOIN t3 ON inner_cond) ON cond
+ @endcode
Here we can add 'ref' access candidates for t1 and t2, but not for t3.
*/
@@ -3546,25 +3659,25 @@ static void add_key_fields_for_nj(JOIN *join, TABLE_LIST *nested_join_table,
}
-/*
- Update keyuse array with all possible keys we can use to fetch rows
+/**
+ Update keyuse array with all possible keys we can use to fetch rows.
- SYNOPSIS
- update_ref_and_keys()
- thd
- keyuse OUT Put here ordered array of KEYUSE structures
- join_tab Array in tablenr_order
- tables Number of tables in join
- cond WHERE condition (note that the function analyzes
- join_tab[i]->on_expr too)
- normal_tables Tables not inner w.r.t some outer join (ones for which
- we can make ref access based the WHERE clause)
- select_lex current SELECT
- sargables OUT Array of found sargable candidates
+ @param thd
+ @param[out] keyuse Put here ordered array of KEYUSE structures
+ @param join_tab Array in tablenr_order
+ @param tables Number of tables in join
+ @param cond WHERE condition (note that the function analyzes
+ join_tab[i]->on_expr too)
+ @param normal_tables Tables not inner w.r.t some outer join (ones
+ for which we can make ref access based the WHERE
+ clause)
+ @param select_lex current SELECT
+ @param[out] sargables Array of found sargable candidates
- RETURN
- 0 - OK
- 1 - Out of memory.
+ @retval
+ 0 OK
+ @retval
+ 1 Out of memory.
*/
static bool
@@ -3682,7 +3795,7 @@ update_ref_and_keys(THD *thd, DYNAMIC_ARRAY *keyuse,JOIN_TAB *join_tab,
(qsort_cmp) sort_keyuse);
bzero((char*) &key_end,sizeof(key_end)); /* Add for easy testing */
- VOID(insert_dynamic(keyuse,(gptr) &key_end));
+ VOID(insert_dynamic(keyuse,(uchar*) &key_end));
use=save_pos=dynamic_element(keyuse,0,KEYUSE*);
prev= &key_end;
@@ -3703,7 +3816,11 @@ update_ref_and_keys(THD *thd, DYNAMIC_ARRAY *keyuse,JOIN_TAB *join_tab,
continue;
}
- *save_pos= *use;
+#ifdef HAVE_purify
+ /* Valgrind complains about overlapped memcpy when save_pos==use. */
+ if (save_pos != use)
+#endif
+ *save_pos= *use;
prev=use;
found_eq_constant= !use->used_tables;
/* Save ptr to first use */
@@ -3713,14 +3830,14 @@ update_ref_and_keys(THD *thd, DYNAMIC_ARRAY *keyuse,JOIN_TAB *join_tab,
save_pos++;
}
i=(uint) (save_pos-(KEYUSE*) keyuse->buffer);
- VOID(set_dynamic(keyuse,(gptr) &key_end,i));
+ VOID(set_dynamic(keyuse,(uchar*) &key_end,i));
keyuse->elements=i;
}
return FALSE;
}
-/*
- Update some values in keyuse for faster choose_plan() loop
+/**
+ Update some values in keyuse for faster choose_plan() loop.
*/
static void optimize_keyuse(JOIN *join, DYNAMIC_ARRAY *keyuse_array)
@@ -3748,7 +3865,7 @@ static void optimize_keyuse(JOIN *join, DYNAMIC_ARRAY *keyuse_array)
if (map == 1) // Only one table
{
TABLE *tmp_table=join->all_tables[tablenr];
- keyuse->ref_table_rows= max(tmp_table->file->records, 100);
+ keyuse->ref_table_rows= max(tmp_table->file->stats.records, 100);
}
}
/*
@@ -3761,23 +3878,21 @@ static void optimize_keyuse(JOIN *join, DYNAMIC_ARRAY *keyuse_array)
}
-/*
+/**
Discover the indexes that can be used for GROUP BY or DISTINCT queries.
- SYNOPSIS
- add_group_and_distinct_keys()
- join
- join_tab
+ If the query has a GROUP BY clause, find all indexes that contain all
+ GROUP BY fields, and add those indexes to join->const_keys.
- DESCRIPTION
- If the query has a GROUP BY clause, find all indexes that contain all
- GROUP BY fields, and add those indexes to join->const_keys.
- If the query has a DISTINCT clause, find all indexes that contain all
- SELECT fields, and add those indexes to join->const_keys.
- This allows later on such queries to be processed by a
- QUICK_GROUP_MIN_MAX_SELECT.
+ If the query has a DISTINCT clause, find all indexes that contain all
+ SELECT fields, and add those indexes to join->const_keys.
+ This allows later on such queries to be processed by a
+ QUICK_GROUP_MIN_MAX_SELECT.
- RETURN
+ @param join
+ @param join_tab
+
+ @return
None
*/
@@ -3793,8 +3908,8 @@ add_group_and_distinct_keys(JOIN *join, JOIN_TAB *join_tab)
if (join->group_list)
{ /* Collect all query fields referenced in the GROUP clause. */
for (cur_group= join->group_list; cur_group; cur_group= cur_group->next)
- (*cur_group->item)->walk(&Item::collect_item_field_processor,
- (byte*) &indexed_fields);
+ (*cur_group->item)->walk(&Item::collect_item_field_processor, 0,
+ (uchar*) &indexed_fields);
}
else if (join->select_distinct)
{ /* Collect all query fields referenced in the SELECT clause. */
@@ -3802,7 +3917,8 @@ add_group_and_distinct_keys(JOIN *join, JOIN_TAB *join_tab)
List_iterator<Item> select_items_it(select_items);
Item *item;
while ((item= select_items_it++))
- item->walk(&Item::collect_item_field_processor, (byte*) &indexed_fields);
+ item->walk(&Item::collect_item_field_processor, 0,
+ (uchar*) &indexed_fields);
}
else
return;
@@ -3828,7 +3944,7 @@ add_group_and_distinct_keys(JOIN *join, JOIN_TAB *join_tab)
which uses least records
*****************************************************************************/
-/* Save const tables first as used tables */
+/** Save const tables first as used tables. */
static void
set_position(JOIN *join,uint idx,JOIN_TAB *table,KEYUSE *key)
@@ -3836,6 +3952,7 @@ set_position(JOIN *join,uint idx,JOIN_TAB *table,KEYUSE *key)
join->positions[idx].table= table;
join->positions[idx].key=key;
join->positions[idx].records_read=1.0; /* This is a const table */
+ join->positions[idx].ref_depend_map= 0;
/* Move the const table as down as possible in best_ref */
JOIN_TAB **pos=join->best_ref+idx+1;
@@ -3850,31 +3967,28 @@ set_position(JOIN *join,uint idx,JOIN_TAB *table,KEYUSE *key)
}
-/*
- Find the best access path for an extension of a partial execution plan and
- add this path to the plan.
-
- SYNOPSIS
- best_access_path()
- join pointer to the structure providing all context info
- for the query
- s the table to be joined by the function
- thd thread for the connection that submitted the query
- remaining_tables set of tables not included into the partial plan yet
- idx the length of the partial plan
- record_count estimate for the number of records returned by the partial
- plan
- read_time the cost of the partial plan
-
- DESCRIPTION
- The function finds the best access path to table 's' from the passed
- partial plan where an access path is the general term for any means to
- access the data in 's'. An access path may use either an index or a scan,
- whichever is cheaper. The input partial plan is passed via the array
- 'join->positions' of length 'idx'. The chosen access method for 's' and its
- cost are stored in 'join->positions[idx]'.
-
- RETURN
+/**
+ Find the best access path for an extension of a partial execution
+ plan and add this path to the plan.
+
+ The function finds the best access path to table 's' from the passed
+ partial plan where an access path is the general term for any means to
+ access the data in 's'. An access path may use either an index or a scan,
+ whichever is cheaper. The input partial plan is passed via the array
+ 'join->positions' of length 'idx'. The chosen access method for 's' and its
+ cost are stored in 'join->positions[idx]'.
+
+ @param join pointer to the structure providing all context info
+ for the query
+ @param s the table to be joined by the function
+ @param thd thread for the connection that submitted the query
+ @param remaining_tables set of tables not included into the partial plan yet
+ @param idx the length of the partial plan
+ @param record_count estimate for the number of records returned by the
+ partial plan
+ @param read_time the cost of the partial plan
+
+ @return
None
*/
@@ -3893,6 +4007,7 @@ best_access_path(JOIN *join,
double best= DBL_MAX;
double best_time= DBL_MAX;
double records= DBL_MAX;
+ table_map best_ref_depends_map= 0;
double tmp;
ha_rows rec;
DBUG_ENTER("best_access_path");
@@ -3920,13 +4035,20 @@ best_access_path(JOIN *join,
/* Calculate how many key segments of the current key we can use */
start_key= keyuse;
- do
- { /* for each keypart */
+
+ do /* For each keypart */
+ {
uint keypart= keyuse->keypart;
table_map best_part_found_ref= 0;
double best_prev_record_reads= DBL_MAX;
- do
+
+ do /* For each way to access the keypart */
{
+
+ /*
+ if 1. expression doesn't refer to forward tables
+ 2. we won't get two ref-or-null's
+ */
if (!(remaining_tables & keyuse->used_tables) &&
!(ref_or_null_part && (keyuse->optimize &
KEY_OPTIMIZE_REF_OR_NULL)))
@@ -3934,8 +4056,9 @@ best_access_path(JOIN *join,
found_part|= keyuse->keypart_map;
if (!(keyuse->used_tables & ~join->const_table_map))
const_part|= keyuse->keypart_map;
- double tmp2= prev_record_reads(join, (found_ref |
- keyuse->used_tables));
+
+ double tmp2= prev_record_reads(join, idx, (found_ref |
+ keyuse->used_tables));
if (tmp2 < best_prev_record_reads)
{
best_part_found_ref= keyuse->used_tables & ~join->const_table_map;
@@ -3974,7 +4097,7 @@ best_access_path(JOIN *join,
Really, there should be records=0.0 (yes!)
but 1.0 would be probably safer
*/
- tmp= prev_record_reads(join, found_ref);
+ tmp= prev_record_reads(join, idx, found_ref);
records= 1.0;
}
else
@@ -3989,7 +4112,7 @@ best_access_path(JOIN *join,
max_key_part= (uint) ~0;
if ((keyinfo->flags & (HA_NOSAME | HA_NULL_PART_KEY)) == HA_NOSAME)
{
- tmp = prev_record_reads(join, found_ref);
+ tmp = prev_record_reads(join, idx, found_ref);
records=1.0;
}
else
@@ -4054,10 +4177,10 @@ best_access_path(JOIN *join,
/* Limit the number of matched rows */
tmp= records;
set_if_smaller(tmp, (double) thd->variables.max_seeks_for_key);
- if (table->used_keys.is_set(key))
+ if (table->covering_keys.is_set(key))
{
/* we can use only index tree */
- uint keys_per_block= table->file->block_size/2/
+ uint keys_per_block= table->file->stats.block_size/2/
(keyinfo->key_length+table->file->ref_length)+1;
tmp= record_count*(tmp+keys_per_block-1)/keys_per_block;
}
@@ -4126,7 +4249,30 @@ best_access_path(JOIN *join,
{
/* Check if we have statistic about the distribution */
if ((records= keyinfo->rec_per_key[max_key_part-1]))
+ {
+ /*
+ Fix for the case where the index statistics is too
+ optimistic: If
+ (1) We're considering ref(const) and there is quick select
+ on the same index,
+ (2) and that quick select uses more keyparts (i.e. it will
+ scan equal/smaller interval then this ref(const))
+ (3) and E(#rows) for quick select is higher then our
+ estimate,
+ Then
+ We'll use E(#rows) from quick select.
+
+ Q: Why do we choose to use 'ref'? Won't quick select be
+ cheaper in some cases ?
+ TODO: figure this out and adjust the plan choice if needed.
+ */
+ if (!found_ref && table->quick_keys.is_set(key) && // (1)
+ table->quick_key_parts[key] > max_key_part && // (2)
+ records < (double)table->quick_rows[key]) // (3)
+ records= (double)table->quick_rows[key];
+
tmp= records;
+ }
else
{
/*
@@ -4198,10 +4344,10 @@ best_access_path(JOIN *join,
/* Limit the number of matched rows */
set_if_smaller(tmp, (double) thd->variables.max_seeks_for_key);
- if (table->used_keys.is_set(key))
+ if (table->covering_keys.is_set(key))
{
/* we can use only index tree */
- uint keys_per_block= table->file->block_size/2/
+ uint keys_per_block= table->file->stats.block_size/2/
(keyinfo->key_length+table->file->ref_length)+1;
tmp= record_count*(tmp+keys_per_block-1)/keys_per_block;
}
@@ -4219,6 +4365,7 @@ best_access_path(JOIN *join,
best_records= records;
best_key= start_key;
best_max_key_part= max_key_part;
+ best_ref_depends_map= found_ref;
}
}
records= best_records;
@@ -4255,21 +4402,31 @@ best_access_path(JOIN *join,
if ((records >= s->found_records || best > s->read_time) && // (1)
!(s->quick && best_key && s->quick->index == best_key->key && // (2)
best_max_key_part >= s->table->quick_key_parts[best_key->key]) &&// (2)
- !((s->table->file->table_flags() & HA_TABLE_SCAN_ON_INDEX) && // (3)
- ! s->table->used_keys.is_clear_all() && best_key && !s->quick) &&// (3)
+ !((s->table->file->ha_table_flags() & HA_TABLE_SCAN_ON_INDEX) && // (3)
+ ! s->table->covering_keys.is_clear_all() && best_key && !s->quick) &&// (3)
!(s->table->force_index && best_key && !s->quick)) // (4)
{ // Check full join
ha_rows rnd_records= s->found_records;
/*
- If there is a restriction on the table, assume that 25% of the
- rows can be skipped on next part.
- This is to force tables that this table depends on before this
- table
+ If there is a filtering condition on the table (i.e. ref analyzer found
+ at least one "table.keyXpartY= exprZ", where exprZ refers only to tables
+ preceding this table in the join order we're now considering), then
+ assume that 25% of the rows will be filtered out by this condition.
+
+ This heuristic is supposed to force tables used in exprZ to be before
+ this table in join order.
*/
if (found_constraint)
rnd_records-= rnd_records/4;
/*
+ If applicable, get a more accurate estimate. Don't use the two
+ heuristics at once.
+ */
+ if (s->table->quick_condition_rows != s->found_records)
+ rnd_records= s->table->quick_condition_rows;
+
+ /*
Range optimizer never proposes a RANGE if it isn't better
than FULL: so if RANGE is present, it's always preferred to FULL.
Here we estimate its cost.
@@ -4280,6 +4437,10 @@ best_access_path(JOIN *join,
For each record we:
- read record range through 'quick'
- skip rows which does not satisfy WHERE constraints
+ TODO:
+ We take into account possible use of join cache for ALL/index
+ access (see first else-branch below), but we don't take it into
+ account here for range/index_merge access. Find out why this is so.
*/
tmp= record_count *
(s->quick->read_time +
@@ -4333,6 +4494,8 @@ best_access_path(JOIN *join,
best= tmp;
records= rows2double(rnd_records);
best_key= 0;
+ /* range/index_merge/ALL/index access method are "independent", so: */
+ best_ref_depends_map= 0;
}
}
@@ -4341,6 +4504,7 @@ best_access_path(JOIN *join,
join->positions[idx].read_time= best;
join->positions[idx].key= best_key;
join->positions[idx].table= s;
+ join->positions[idx].ref_depend_map= best_ref_depends_map;
if (!best_key &&
idx == join->const_tables &&
@@ -4352,24 +4516,26 @@ best_access_path(JOIN *join,
}
-/*
+/**
Selects and invokes a search strategy for an optimal query plan.
- SYNOPSIS
- choose_plan()
- join pointer to the structure providing all context info for
- the query
- join_tables set of the tables in the query
+ The function checks user-configurable parameters that control the search
+ strategy for an optimal plan, selects the search method and then invokes
+ it. Each specific optimization procedure stores the final optimal plan in
+ the array 'join->best_positions', and the cost of the plan in
+ 'join->best_read'.
- DESCRIPTION
- The function checks user-configurable parameters that control the search
- strategy for an optimal plan, selects the search method and then invokes
- it. Each specific optimization procedure stores the final optimal plan in
- the array 'join->best_positions', and the cost of the plan in
- 'join->best_read'.
+ @param join pointer to the structure providing all context info for
+ the query
+ @param join_tables set of the tables in the query
- RETURN VALUES
+ @todo
+ 'MAX_TABLES+2' denotes the old implementation of find_best before
+ the greedy version. Will be removed when greedy_search is approved.
+
+ @retval
FALSE ok
+ @retval
TRUE Fatal error
*/
@@ -4422,25 +4588,21 @@ choose_plan(JOIN *join, table_map join_tables)
/*
Store the cost of this query into a user variable
- Don't update last_query_cost for 'show status' command.
Don't update last_query_cost for statements that are not "flat joins" :
i.e. they have subqueries, unions or call stored procedures.
TODO: calculate a correct cost for a query with subqueries and UNIONs.
*/
- if (join->thd->lex->orig_sql_command != SQLCOM_SHOW_STATUS &&
- join->thd->lex->is_single_level_stmt())
+ if (join->thd->lex->is_single_level_stmt())
join->thd->status_var.last_query_cost= join->best_read;
DBUG_RETURN(FALSE);
}
-/*
+/**
Compare two JOIN_TAB objects based on the number of accessed records.
- SYNOPSIS
- join_tab_cmp()
- ptr1 pointer to first JOIN_TAB object
- ptr2 pointer to second JOIN_TAB object
+ @param ptr1 pointer to first JOIN_TAB object
+ @param ptr2 pointer to second JOIN_TAB object
NOTES
The order relation implemented by join_tab_cmp() is not transitive,
@@ -4453,9 +4615,11 @@ choose_plan(JOIN *join, table_map join_tables)
b: dependent = 0x0 table->map = 0x2 found_records = 3 ptr = 0x907e838
c: dependent = 0x6 table->map = 0x10 found_records = 2 ptr = 0x907ecd0
- RETURN
+ @retval
1 if first is bigger
- -1 if second is bigger
+ @retval
+ -1 if second is bigger
+ @retval
0 if equal
*/
@@ -4477,7 +4641,7 @@ join_tab_cmp(const void* ptr1, const void* ptr2)
}
-/*
+/**
Same as join_tab_cmp, but for use with SELECT_STRAIGHT_JOIN.
*/
@@ -4494,27 +4658,33 @@ join_tab_cmp_straight(const void* ptr1, const void* ptr2)
return jt1 > jt2 ? 1 : (jt1 < jt2 ? -1 : 0);
}
-/*
+/**
Heuristic procedure to automatically guess a reasonable degree of
exhaustiveness for the greedy search procedure.
- SYNOPSIS
- determine_search_depth()
- join pointer to the structure providing all context info for the query
+ The procedure estimates the optimization time and selects a search depth
+ big enough to result in a near-optimal QEP, that doesn't take too long to
+ find. If the number of tables in the query exceeds some constant, then
+ search_depth is set to this constant.
- DESCRIPTION
- The procedure estimates the optimization time and selects a search depth
- big enough to result in a near-optimal QEP, that doesn't take too long to
- find. If the number of tables in the query exceeds some constant, then
- search_depth is set to this constant.
+ @param join pointer to the structure providing all context info for
+ the query
- NOTES
+ @note
This is an extremely simplistic implementation that serves as a stub for a
more advanced analysis of the join. Ideally the search depth should be
determined by learning from previous query optimizations, because it will
depend on the CPU power (and other factors).
- RETURN
+ @todo
+ this value should be determined dynamically, based on statistics:
+ uint max_tables_for_exhaustive_opt= 7;
+
+ @todo
+ this value could be determined by some mapping of the form:
+ depth : table_count -> [max_tables_for_exhaustive_opt..MAX_EXHAUSTIVE]
+
+ @return
A positive integer that specifies the search depth (and thus the
exhaustiveness) of the depth-first search algorithm used by
'greedy_search'.
@@ -4541,16 +4711,9 @@ determine_search_depth(JOIN *join)
}
-/*
+/**
Select the best ways to access the tables in a query without reordering them.
- SYNOPSIS
- optimize_straight_join()
- join pointer to the structure providing all context info for
- the query
- join_tables set of the tables in the query
-
- DESCRIPTION
Find the best access paths for each query table and compute their costs
according to their order in the array 'join->best_ref' (thus without
reordering the join tables). The function calls sequentially
@@ -4558,15 +4721,17 @@ determine_search_depth(JOIN *join)
access method. The final optimal plan is stored in the array
'join->best_positions', and the corresponding cost in 'join->best_read'.
- NOTES
+ @param join pointer to the structure providing all context info for
+ the query
+ @param join_tables set of the tables in the query
+
+ @note
This function can be applied to:
- queries with STRAIGHT_JOIN
- internally to compute the cost of an arbitrary QEP
+ @par
Thus 'optimize_straight_join' can be used at any stage of the query
optimization process to finalize a QEP as it is.
-
- RETURN
- None
*/
static void
@@ -4593,37 +4758,30 @@ optimize_straight_join(JOIN *join, table_map join_tables)
if (join->sort_by_table &&
join->sort_by_table != join->positions[join->const_tables].table->table)
read_time+= record_count; // We have to make a temp table
- memcpy((gptr) join->best_positions, (gptr) join->positions,
+ memcpy((uchar*) join->best_positions, (uchar*) join->positions,
sizeof(POSITION)*idx);
join->best_read= read_time;
}
-/*
+/**
Find a good, possibly optimal, query execution plan (QEP) by a greedy search.
- SYNOPSIS
- join pointer to the structure providing all context info
- for the query
- remaining_tables set of tables not included into the partial plan yet
- search_depth controlls the exhaustiveness of the search
- prune_level the pruning heuristics that should be applied during
- search
-
- DESCRIPTION
The search procedure uses a hybrid greedy/exhaustive search with controlled
exhaustiveness. The search is performed in N = card(remaining_tables)
steps. Each step evaluates how promising is each of the unoptimized tables,
selects the most promising table, and extends the current partial QEP with
that table. Currenly the most 'promising' table is the one with least
- expensive extension.
+ expensive extension.\
+
There are two extreme cases:
- 1. When (card(remaining_tables) < search_depth), the estimate finds the best
- complete continuation of the partial QEP. This continuation can be
- used directly as a result of the search.
- 2. When (search_depth == 1) the 'best_extension_by_limited_search'
- consideres the extension of the current QEP with each of the remaining
- unoptimized tables.
+ -# When (card(remaining_tables) < search_depth), the estimate finds the
+ best complete continuation of the partial QEP. This continuation can be
+ used directly as a result of the search.
+ -# When (search_depth == 1) the 'best_extension_by_limited_search'
+ consideres the extension of the current QEP with each of the remaining
+ unoptimized tables.
+
All other cases are in-between these two extremes. Thus the parameter
'search_depth' controlls the exhaustiveness of the search. The higher the
value, the longer the optimizaton time and possibly the better the
@@ -4631,16 +4789,18 @@ optimize_straight_join(JOIN *join, table_map join_tables)
estimated, but the more likely to get a bad QEP.
All intermediate and final results of the procedure are stored in 'join':
- join->positions modified for every partial QEP that is explored
- join->best_positions modified for the current best complete QEP
- join->best_read modified for the current best complete QEP
- join->best_ref might be partially reordered
+ - join->positions : modified for every partial QEP that is explored
+ - join->best_positions: modified for the current best complete QEP
+ - join->best_read : modified for the current best complete QEP
+ - join->best_ref : might be partially reordered
+
The final optimal plan is stored in 'join->best_positions', and its
corresponding cost in 'join->best_read'.
- NOTES
+ @note
The following pseudocode describes the algorithm of 'greedy_search':
+ @code
procedure greedy_search
input: remaining_tables
output: pplan;
@@ -4654,6 +4814,7 @@ optimize_straight_join(JOIN *join, table_map join_tables)
return pplan;
}
+ @endcode
where 'best_extension' is a placeholder for a procedure that selects the
most "promising" of all tables in 'remaining_tables'.
Currently this estimate is performed by calling
@@ -4661,16 +4822,26 @@ optimize_straight_join(JOIN *join, table_map join_tables)
current QEP of size 'search_depth', thus the complexity of 'greedy_search'
mainly depends on that of 'best_extension_by_limited_search'.
+ @par
If 'best_extension()' == 'best_extension_by_limited_search()', then the
worst-case complexity of this algorithm is <=
O(N*N^search_depth/search_depth). When serch_depth >= N, then the
complexity of greedy_search is O(N!).
+ @par
In the future, 'greedy_search' might be extended to support other
implementations of 'best_extension', e.g. some simpler quadratic procedure.
- RETURN VALUES
+ @param join pointer to the structure providing all context info
+ for the query
+ @param remaining_tables set of tables not included into the partial plan yet
+ @param search_depth controlls the exhaustiveness of the search
+ @param prune_level the pruning heuristics that should be applied during
+ search
+
+ @retval
FALSE ok
+ @retval
TRUE Fatal error
*/
@@ -4758,29 +4929,10 @@ greedy_search(JOIN *join,
}
-/*
+/**
Find a good, possibly optimal, query execution plan (QEP) by a possibly
exhaustive search.
- SYNOPSIS
- best_extension_by_limited_search()
- join pointer to the structure providing all context info for
- the query
- remaining_tables set of tables not included into the partial plan yet
- idx length of the partial QEP in 'join->positions';
- since a depth-first search is used, also corresponds to
- the current depth of the search tree;
- also an index in the array 'join->best_ref';
- record_count estimate for the number of records returned by the best
- partial plan
- read_time the cost of the best partial plan
- search_depth maximum depth of the recursion and thus size of the found
- optimal plan (0 < search_depth <= join->tables+1).
- prune_level pruning heuristics that should be applied during
- optimization
- (values: 0 = EXHAUSTIVE, 1 = PRUNE_BY_TIME_OR_ROWS)
-
- DESCRIPTION
The procedure searches for the optimal ordering of the query tables in set
'remaining_tables' of size N, and the corresponding optimal access paths to
each table. The choice of a table order and an access path for each table
@@ -4807,16 +4959,18 @@ greedy_search(JOIN *join,
The final optimal plan is stored in 'join->best_positions'. The
corresponding cost of the optimal plan is in 'join->best_read'.
- NOTES
+ @note
The procedure uses a recursive depth-first search where the depth of the
recursion (and thus the exhaustiveness of the search) is controlled by the
parameter 'search_depth'.
+ @note
The pseudocode below describes the algorithm of
'best_extension_by_limited_search'. The worst-case complexity of this
algorithm is O(N*N^search_depth/search_depth). When serch_depth >= N, then
the complexity of greedy_search is O(N!).
+ @code
procedure best_extension_by_limited_search(
pplan in, // in, partial plan of tables-joined-so-far
pplan_cost, // in, cost of pplan
@@ -4856,18 +5010,39 @@ greedy_search(JOIN *join,
}
}
}
+ @endcode
- IMPLEMENTATION
+ @note
When 'best_extension_by_limited_search' is called for the first time,
'join->best_read' must be set to the largest possible value (e.g. DBL_MAX).
The actual implementation provides a way to optionally use pruning
heuristic (controlled by the parameter 'prune_level') to reduce the search
space by skipping some partial plans.
+
+ @note
The parameter 'search_depth' provides control over the recursion
depth, and thus the size of the resulting optimal plan.
- RETURN VALUES
+ @param join pointer to the structure providing all context info
+ for the query
+ @param remaining_tables set of tables not included into the partial plan yet
+ @param idx length of the partial QEP in 'join->positions';
+ since a depth-first search is used, also corresponds
+ to the current depth of the search tree;
+ also an index in the array 'join->best_ref';
+ @param record_count estimate for the number of records returned by the
+ best partial plan
+ @param read_time the cost of the best partial plan
+ @param search_depth maximum depth of the recursion and thus size of the
+ found optimal plan
+ (0 < search_depth <= join->tables+1).
+ @param prune_level pruning heuristics that should be applied during
+ optimization
+ (values: 0 = EXHAUSTIVE, 1 = PRUNE_BY_TIME_OR_ROWS)
+
+ @retval
FALSE ok
+ @retval
TRUE Fatal error
*/
@@ -4886,6 +5061,9 @@ best_extension_by_limited_search(JOIN *join,
if (thd->killed) // Abort
DBUG_RETURN(TRUE);
+ DBUG_EXECUTE("opt", print_plan(join, idx, read_time, record_count, idx,
+ "SOFAR:"););
+
/*
'join' is a partial plan with lower cost than the best plan so far,
so continue expanding it further with the tables in 'remaining_tables'.
@@ -4987,7 +5165,7 @@ best_extension_by_limited_search(JOIN *join,
current_read_time+= current_record_count;
if ((search_depth == 1) || (current_read_time < join->best_read))
{
- memcpy((gptr) join->best_positions, (gptr) join->positions,
+ memcpy((uchar*) join->best_positions, (uchar*) join->positions,
sizeof(POSITION) * (idx + 1));
join->best_read= current_read_time - 0.001;
}
@@ -5004,8 +5182,9 @@ best_extension_by_limited_search(JOIN *join,
}
-/*
- TODO: this function is here only temporarily until 'greedy_search' is
+/**
+ @todo
+ - TODO: this function is here only temporarily until 'greedy_search' is
tested and accepted.
RETURN VALUES
@@ -5032,7 +5211,7 @@ find_best(JOIN *join,table_map rest_tables,uint idx,double record_count,
read_time+=record_count; // We have to make a temp table
if (read_time < join->best_read)
{
- memcpy((gptr) join->best_positions,(gptr) join->positions,
+ memcpy((uchar*) join->best_positions,(uchar*) join->positions,
sizeof(POSITION)*idx);
join->best_read= read_time - 0.001;
}
@@ -5086,19 +5265,20 @@ find_best(JOIN *join,table_map rest_tables,uint idx,double record_count,
}
-/*
- Find how much space the prevous read not const tables takes in cache
+/**
+ Find how much space the prevous read not const tables takes in cache.
*/
static void calc_used_field_length(THD *thd, JOIN_TAB *join_tab)
{
uint null_fields,blobs,fields,rec_length;
- null_fields=blobs=fields=rec_length=0;
-
Field **f_ptr,*field;
+ MY_BITMAP *read_set= join_tab->table->read_set;;
+
+ null_fields= blobs= fields= rec_length=0;
for (f_ptr=join_tab->table->field ; (field= *f_ptr) ; f_ptr++)
{
- if (field->query_id == thd->query_id)
+ if (bitmap_is_set(read_set, field->field_index))
{
uint flags=field->flags;
fields++;
@@ -5115,7 +5295,7 @@ static void calc_used_field_length(THD *thd, JOIN_TAB *join_tab)
rec_length+=sizeof(my_bool);
if (blobs)
{
- uint blob_length=(uint) (join_tab->table->file->mean_rec_length-
+ uint blob_length=(uint) (join_tab->table->file->stats.mean_rec_length-
(join_tab->table->s->reclength- rec_length));
rec_length+=(uint) max(4,blob_length);
}
@@ -5145,26 +5325,94 @@ cache_record_length(JOIN *join,uint idx)
}
+/*
+ Get the number of different row combinations for subset of partial join
+
+ SYNOPSIS
+ prev_record_reads()
+ join The join structure
+ idx Number of tables in the partial join order (i.e. the
+ partial join order is in join->positions[0..idx-1])
+ found_ref Bitmap of tables for which we need to find # of distinct
+ row combinations.
+
+ DESCRIPTION
+ Given a partial join order (in join->positions[0..idx-1]) and a subset of
+ tables within that join order (specified in found_ref), find out how many
+ distinct row combinations of subset tables will be in the result of the
+ partial join order.
+
+ This is used as follows: Suppose we have a table accessed with a ref-based
+ method. The ref access depends on current rows of tables in found_ref.
+ We want to count # of different ref accesses. We assume two ref accesses
+ will be different if at least one of access parameters is different.
+ Example: consider a query
+
+ SELECT * FROM t1, t2, t3 WHERE t1.key=c1 AND t2.key=c2 AND t3.key=t1.field
+
+ and a join order:
+ t1, ref access on t1.key=c1
+ t2, ref access on t2.key=c2
+ t3, ref access on t3.key=t1.field
+
+ For t1: n_ref_scans = 1, n_distinct_ref_scans = 1
+ For t2: n_ref_scans = records_read(t1), n_distinct_ref_scans=1
+ For t3: n_ref_scans = records_read(t1)*records_read(t2)
+ n_distinct_ref_scans = #records_read(t1)
+
+ The reason for having this function (at least the latest version of it)
+ is that we need to account for buffering in join execution.
+
+ An edge-case example: if we have a non-first table in join accessed via
+ ref(const) or ref(param) where there is a small number of different
+ values of param, then the access will likely hit the disk cache and will
+ not require any disk seeks.
+
+ The proper solution would be to assume an LRU disk cache of some size,
+ calculate probability of cache hits, etc. For now we just count
+ identical ref accesses as one.
+
+ RETURN
+ Expected number of row combinations
+*/
+
static double
-prev_record_reads(JOIN *join,table_map found_ref)
+prev_record_reads(JOIN *join, uint idx, table_map found_ref)
{
double found=1.0;
- found_ref&= ~OUTER_REF_TABLE_BIT;
- for (POSITION *pos=join->positions ; found_ref ; pos++)
+ POSITION *pos_end= join->positions - 1;
+ for (POSITION *pos= join->positions + idx - 1; pos != pos_end; pos--)
{
if (pos->table->table->map & found_ref)
{
- found_ref&= ~pos->table->table->map;
- found*=pos->records_read;
+ found_ref|= pos->ref_depend_map;
+ /*
+ For the case of "t1 LEFT JOIN t2 ON ..." where t2 is a const table
+ with no matching row we will get position[t2].records_read==0.
+ Actually the size of output is one null-complemented row, therefore
+ we will use value of 1 whenever we get records_read==0.
+
+ Note
+ - the above case can't occur if inner part of outer join has more
+ than one table: table with no matches will not be marked as const.
+
+ - Ideally we should add 1 to records_read for every possible null-
+ complemented row. We're not doing it because: 1. it will require
+ non-trivial code and add overhead. 2. The value of records_read
+ is an inprecise estimate and adding 1 (or, in the worst case,
+ #max_nested_outer_joins=64-1) will not make it any more precise.
+ */
+ if (pos->records_read)
+ found*= pos->records_read;
}
}
return found;
}
-/*****************************************************************************
+/**
Set up join struct according to best position.
-*****************************************************************************/
+*/
static bool
get_best_combination(JOIN *join)
@@ -5274,7 +5522,7 @@ static bool create_ref_for_key(JOIN *join, JOIN_TAB *j, KEYUSE *org_keyuse,
j->ref.key_parts=keyparts;
j->ref.key_length=length;
j->ref.key=(int) key;
- if (!(j->ref.key_buff= (byte*) thd->calloc(ALIGN_SIZE(length)*2)) ||
+ if (!(j->ref.key_buff= (uchar*) thd->calloc(ALIGN_SIZE(length)*2)) ||
!(j->ref.key_copy= (store_key**) thd->alloc((sizeof(store_key*) *
(keyparts+1)))) ||
!(j->ref.items= (Item**) thd->alloc(sizeof(Item*)*keyparts)) ||
@@ -5288,7 +5536,7 @@ static bool create_ref_for_key(JOIN *join, JOIN_TAB *j, KEYUSE *org_keyuse,
keyuse=org_keyuse;
store_key **ref_key= j->ref.key_copy;
- byte *key_buff=j->ref.key_buff, *null_ref_key= 0;
+ uchar *key_buff=j->ref.key_buff, *null_ref_key= 0;
bool keyuse_uses_no_tables= TRUE;
if (ftkey)
{
@@ -5319,8 +5567,8 @@ static bool create_ref_for_key(JOIN *join, JOIN_TAB *j, KEYUSE *org_keyuse,
!(join->select_options & SELECT_DESCRIBE))
{ // Compare against constant
store_key_item tmp(thd, keyinfo->key_part[i].field,
- (char*)key_buff + maybe_null,
- maybe_null ? (char*) key_buff : 0,
+ key_buff + maybe_null,
+ maybe_null ? key_buff : 0,
keyinfo->key_part[i].length, keyuse->val);
if (thd->is_fatal_error)
DBUG_RETURN(TRUE);
@@ -5330,7 +5578,7 @@ static bool create_ref_for_key(JOIN *join, JOIN_TAB *j, KEYUSE *org_keyuse,
*ref_key++= get_store_key(thd,
keyuse,join->const_table_map,
&keyinfo->key_part[i],
- (char*) key_buff,maybe_null);
+ key_buff, maybe_null);
/*
Remember if we are going to use REF_OR_NULL
But only if field _really_ can be null i.e. we force JT_REF
@@ -5374,7 +5622,7 @@ static bool create_ref_for_key(JOIN *join, JOIN_TAB *j, KEYUSE *org_keyuse,
static store_key *
get_store_key(THD *thd, KEYUSE *keyuse, table_map used_tables,
- KEY_PART_INFO *key_part, char *key_buff, uint maybe_null)
+ KEY_PART_INFO *key_part, uchar *key_buff, uint maybe_null)
{
if (!((~used_tables) & keyuse->used_tables)) // if const item
{
@@ -5406,17 +5654,23 @@ get_store_key(THD *thd, KEYUSE *keyuse, table_map used_tables,
keyuse->val);
}
-/*
- This function is only called for const items on fields which are keys
- returns 1 if there was some conversion made when the field was stored.
+/**
+ 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;
- THD *thd= field->table->in_use;
+ 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
@@ -5426,6 +5680,7 @@ store_val_in_field(Field *field, Item *item, enum_check_fields check_flag)
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;
}
@@ -5507,22 +5762,18 @@ inline void add_cond_and_fix(Item **e1, Item *e2)
}
-/*
- Add to join_tab->select_cond[i] "table.field IS NOT NULL" conditions we've
- inferred from ref/eq_ref access performed.
-
- SYNOPSIS
- add_not_null_conds()
- join Join to process
+/**
+ Add to join_tab->select_cond[i] "table.field IS NOT NULL" conditions
+ we've inferred from ref/eq_ref access performed.
- NOTES
This function is a part of "Early NULL-values filtering for ref access"
optimization.
Example of this optimization:
- For query SELECT * FROM t1,t2 WHERE t2.key=t1.field
- and plan " any-access(t1), ref(t2.key=t1.field) "
- add "t1.field IS NOT NULL" to t1's table condition.
+ For query SELECT * FROM t1,t2 WHERE t2.key=t1.field @n
+ and plan " any-access(t1), ref(t2.key=t1.field) " @n
+ add "t1.field IS NOT NULL" to t1's table condition. @n
+
Description of the optimization:
We look through equalities choosen to perform ref/eq_ref access,
@@ -5534,8 +5785,10 @@ inline void add_cond_and_fix(Item **e1, Item *e2)
Exception from that is the case when referred_tab->join != join.
I.e. don't add NOT NULL constraints from any embedded subquery.
Consider this query:
+ @code
SELECT A.f2 FROM t1 LEFT JOIN t2 A ON A.f2 = f1
WHERE A.f3=(SELECT MIN(f3) FROM t2 C WHERE A.f4 = C.f4) OR A.f3 IS NULL;
+ @endocde
Here condition A.f3 IS NOT NULL is going to be added to the WHERE
condition of the embedding query.
Another example:
@@ -5597,7 +5850,8 @@ static void add_not_null_conds(JOIN *join)
if (notnull->fix_fields(join->thd, &notnull))
DBUG_VOID_RETURN;
DBUG_EXECUTE("where",print_where(notnull,
- referred_tab->table->alias););
+ referred_tab->table->alias,
+ QT_ORDINARY););
add_cond_and_fix(&referred_tab->select_cond, notnull);
}
}
@@ -5606,36 +5860,31 @@ static void add_not_null_conds(JOIN *join)
DBUG_VOID_RETURN;
}
-/*
- Build a predicate guarded by match variables for embedding outer joins
-
- SYNOPSIS
- add_found_match_trig_cond()
- tab the first inner table for most nested outer join
- cond the predicate to be guarded
- root_tab the first inner table to stop
-
- DESCRIPTION
- The function recursively adds guards for predicate cond
- assending from tab to the first inner table next embedding
- nested outer join and so on until it reaches root_tab
- (root_tab can be 0).
-
- RETURN VALUE
- pointer to the guarded predicate, if success
- 0, otherwise
-*/
+/**
+ Build a predicate guarded by match variables for embedding outer joins.
+ The function recursively adds guards for predicate cond
+ assending from tab to the first inner table next embedding
+ nested outer join and so on until it reaches root_tab
+ (root_tab can be 0).
+
+ @param tab the first inner table for most nested outer join
+ @param cond the predicate to be guarded (must be set)
+ @param root_tab the first inner table to stop
+
+ @return
+ - pointer to the guarded predicate, if success
+ - 0, otherwise
+*/
static COND*
add_found_match_trig_cond(JOIN_TAB *tab, COND *cond, JOIN_TAB *root_tab)
{
COND *tmp;
- if (tab == root_tab || !cond)
+ DBUG_ASSERT(cond != 0);
+ if (tab == root_tab)
return cond;
if ((tmp= add_found_match_trig_cond(tab->first_upper, cond, root_tab)))
- {
tmp= new Item_func_trig_cond(tmp, &tab->found);
- }
if (tmp)
{
tmp->quick_fix_field();
@@ -5645,14 +5894,9 @@ add_found_match_trig_cond(JOIN_TAB *tab, COND *cond, JOIN_TAB *root_tab)
}
-/*
- Fill in outer join related info for the execution plan structure
-
- SYNOPSIS
- make_outerjoin_info()
- join - reference to the info fully describing the query
+/**
+ Fill in outer join related info for the execution plan structure.
- DESCRIPTION
For each outer join operation left after simplification of the
original query the function set up the following pointers in the linear
structure join->join_tab representing the selected execution plan.
@@ -5667,21 +5911,25 @@ add_found_match_trig_cond(JOIN_TAB *tab, COND *cond, JOIN_TAB *root_tab)
corresponding first inner table through the field t0->on_expr_ref.
Here ti are structures of the JOIN_TAB type.
- EXAMPLE
- For the query:
- SELECT * FROM t1
- LEFT JOIN
- (t2, t3 LEFT JOIN t4 ON t3.a=t4.a)
- ON (t1.a=t2.a AND t1.b=t3.b)
- WHERE t1.c > 5,
+ EXAMPLE. For the query:
+ @code
+ SELECT * FROM t1
+ LEFT JOIN
+ (t2, t3 LEFT JOIN t4 ON t3.a=t4.a)
+ ON (t1.a=t2.a AND t1.b=t3.b)
+ WHERE t1.c > 5,
+ @endcode
+
given the execution plan with the table order t1,t2,t3,t4
is selected, the following references will be set;
t4->last_inner=[t4], t4->first_inner=[t4], t4->first_upper=[t2]
t2->last_inner=[t4], t2->first_inner=t3->first_inner=[t2],
on expression (t1.a=t2.a AND t1.b=t3.b) will be attached to
*t2->on_expr_ref, while t3.a=t4.a will be attached to *t4->on_expr_ref.
-
- NOTES
+
+ @param join reference to the info fully describing the query
+
+ @note
The function assumes that the simplification procedure has been
already applied to the join query (see simplify_joins).
This function can be called only after the execution plan
@@ -5761,7 +6009,30 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
make_cond_for_table(cond,
join->const_table_map,
(table_map) 0);
- DBUG_EXECUTE("where",print_where(const_cond,"constants"););
+ DBUG_EXECUTE("where",print_where(const_cond,"constants", QT_ORDINARY););
+ for (JOIN_TAB *tab= join->join_tab+join->const_tables;
+ tab < join->join_tab+join->tables ; tab++)
+ {
+ if (*tab->on_expr_ref)
+ {
+ JOIN_TAB *cond_tab= tab->first_inner;
+ COND *tmp= make_cond_for_table(*tab->on_expr_ref,
+ join->const_table_map,
+ ( table_map) 0);
+ if (!tmp)
+ continue;
+ tmp= new Item_func_trig_cond(tmp, &cond_tab->not_null_compl);
+ if (!tmp)
+ DBUG_RETURN(1);
+ tmp->quick_fix_field();
+ cond_tab->select_cond= !cond_tab->select_cond ? tmp :
+ new Item_cond_and(cond_tab->select_cond,
+ tmp);
+ if (!cond_tab->select_cond)
+ DBUG_RETURN(1);
+ cond_tab->select_cond->quick_fix_field();
+ }
+ }
if (const_cond && !const_cond->val_int())
{
DBUG_PRINT("info",("Found impossible WHERE condition"));
@@ -5774,6 +6045,10 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
for (uint i=join->const_tables ; i < join->tables ; i++)
{
JOIN_TAB *tab=join->join_tab+i;
+ /*
+ first_inner is the X in queries like:
+ SELECT * FROM t1 LEFT OUTER JOIN (t2 JOIN t3) ON X
+ */
JOIN_TAB *first_inner_tab= tab->first_inner;
table_map current_map= tab->table->map;
bool use_quick_range=0;
@@ -5798,6 +6073,12 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
tab->ref.key= -1;
tab->ref.key_parts=0; // Don't use ref key.
join->best_positions[i].records_read= rows2double(tab->quick->records);
+ /*
+ We will use join cache here : prevent sorting of the first
+ table only and sort at the end.
+ */
+ if (i != join->const_tables && join->tables > join->const_tables + 1)
+ join->full_join= 1;
}
tmp= NULL;
@@ -5824,15 +6105,15 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
*/
DBUG_PRINT("info", ("Item_int"));
tmp= new Item_int((longlong) 1,1); // Always true
- DBUG_PRINT("info", ("Item_int 0x%lx", (ulong)tmp));
}
}
if (tmp || !cond)
{
- DBUG_EXECUTE("where",print_where(tmp,tab->table->alias););
- SQL_SELECT *sel=tab->select=(SQL_SELECT*)
- thd->memdup((gptr) select, sizeof(SQL_SELECT));
+ DBUG_EXECUTE("where",print_where(tmp,tab->table->alias, QT_ORDINARY););
+ SQL_SELECT *sel= tab->select= ((SQL_SELECT*)
+ thd->memdup((uchar*) select,
+ sizeof(*select)));
if (!sel)
DBUG_RETURN(1); // End of memory
/*
@@ -5869,7 +6150,7 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
tab->select_cond= sel->cond= NULL;
sel->head=tab->table;
- DBUG_EXECUTE("where",print_where(tmp,tab->table->alias););
+ DBUG_EXECUTE("where",print_where(tmp,tab->table->alias, QT_ORDINARY););
if (tab->quick)
{
/* Use quick key read if it's a constant and it's not used
@@ -5981,9 +6262,9 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
current_map,
current_map)))
{
- DBUG_EXECUTE("where",print_where(tmp,"cache"););
+ DBUG_EXECUTE("where",print_where(tmp,"cache", QT_ORDINARY););
tab->cache.select=(SQL_SELECT*)
- thd->memdup((gptr) sel, sizeof(SQL_SELECT));
+ thd->memdup((uchar*) sel, sizeof(SQL_SELECT));
tab->cache.select->cond=tmp;
tab->cache.select->read_tables=join->const_table_map;
}
@@ -6088,9 +6369,9 @@ static void
make_join_readinfo(JOIN *join, ulonglong options)
{
uint i;
-
bool statistics= test(!(join->select_options & SELECT_DESCRIBE));
bool ordered_set= 0;
+ bool sorted= 1;
DBUG_ENTER("make_join_readinfo");
for (i=join->const_tables ; i < join->tables ; i++)
@@ -6115,6 +6396,8 @@ make_join_readinfo(JOIN *join, ulonglong options)
(join->sort_by_table == (TABLE *) 1 && i != join->const_tables)))
ordered_set= 1;
+ tab->sorted= sorted;
+ sorted= 0; // only first must be sorted
switch (tab->type) {
case JT_SYSTEM: // Only happens with left join
table->status=STATUS_NO_RECORD;
@@ -6125,7 +6408,7 @@ make_join_readinfo(JOIN *join, ulonglong options)
table->status=STATUS_NO_RECORD;
tab->read_first_record= join_read_const;
tab->read_record.read_record= join_no_more_records;
- if (table->used_keys.is_set(tab->ref.key) &&
+ if (table->covering_keys.is_set(tab->ref.key) &&
!table->no_keyread)
{
table->key_read=1;
@@ -6143,7 +6426,7 @@ make_join_readinfo(JOIN *join, ulonglong options)
tab->quick=0;
tab->read_first_record= join_read_key;
tab->read_record.read_record= join_no_more_records;
- if (table->used_keys.is_set(tab->ref.key) &&
+ if (table->covering_keys.is_set(tab->ref.key) &&
!table->no_keyread)
{
table->key_read=1;
@@ -6160,7 +6443,7 @@ make_join_readinfo(JOIN *join, ulonglong options)
}
delete tab->quick;
tab->quick=0;
- if (table->used_keys.is_set(tab->ref.key) &&
+ if (table->covering_keys.is_set(tab->ref.key) &&
!table->no_keyread)
{
table->key_read=1;
@@ -6204,8 +6487,7 @@ make_join_readinfo(JOIN *join, ulonglong options)
join->thd->server_status|=SERVER_QUERY_NO_GOOD_INDEX_USED;
tab->read_first_record= join_init_quick_read_record;
if (statistics)
- statistic_increment(join->thd->status_var.select_range_check_count,
- &LOCK_status);
+ status_var_increment(join->thd->status_var.select_range_check_count);
}
else
{
@@ -6215,15 +6497,13 @@ make_join_readinfo(JOIN *join, ulonglong options)
if (tab->select && tab->select->quick)
{
if (statistics)
- statistic_increment(join->thd->status_var.select_range_count,
- &LOCK_status);
+ status_var_increment(join->thd->status_var.select_range_count);
}
else
{
join->thd->server_status|=SERVER_QUERY_NO_INDEX_USED;
if (statistics)
- statistic_increment(join->thd->status_var.select_scan_count,
- &LOCK_status);
+ status_var_increment(join->thd->status_var.select_scan_count);
}
}
else
@@ -6231,30 +6511,39 @@ make_join_readinfo(JOIN *join, ulonglong options)
if (tab->select && tab->select->quick)
{
if (statistics)
- statistic_increment(join->thd->status_var.select_full_range_join_count,
- &LOCK_status);
+ status_var_increment(join->thd->status_var.select_full_range_join_count);
}
else
{
join->thd->server_status|=SERVER_QUERY_NO_INDEX_USED;
if (statistics)
- statistic_increment(join->thd->status_var.select_full_join_count,
- &LOCK_status);
+ status_var_increment(join->thd->status_var.select_full_join_count);
}
}
if (!table->no_keyread)
{
if (tab->select && tab->select->quick &&
tab->select->quick->index != MAX_KEY && //not index_merge
- table->used_keys.is_set(tab->select->quick->index))
+ table->covering_keys.is_set(tab->select->quick->index))
{
table->key_read=1;
table->file->extra(HA_EXTRA_KEYREAD);
}
- else if (!table->used_keys.is_clear_all() &&
+ else if (!table->covering_keys.is_clear_all() &&
!(tab->select && tab->select->quick))
{ // Only read index tree
- tab->index=find_shortest_key(table, & table->used_keys);
+ /*
+ 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
+ */
+ tab->index=find_shortest_key(table, & table->covering_keys);
tab->read_first_record= join_read_first;
tab->type=JT_NEXT; // Read with index_first / index_next
}
@@ -6274,20 +6563,18 @@ make_join_readinfo(JOIN *join, ulonglong options)
}
-/*
- Give error if we some tables are done with a full join
+/**
+ Give error if we some tables are done with a full join.
- SYNOPSIS
- error_if_full_join()
- join Join condition
+ This is used by multi_table_update and multi_table_delete when running
+ in safe mode.
- USAGE
- This is used by multi_table_update and multi_table_delete when running
- in safe mode
+ @param join Join condition
- RETURN VALUES
- 0 ok
- 1 Error (full join used)
+ @retval
+ 0 ok
+ @retval
+ 1 Error (full join used)
*/
bool error_if_full_join(JOIN *join)
@@ -6307,11 +6594,8 @@ bool error_if_full_join(JOIN *join)
}
-/*
- cleanup JOIN_TAB
-
- SYNOPSIS
- JOIN_TAB::cleanup()
+/**
+ cleanup JOIN_TAB.
*/
void JOIN_TAB::cleanup()
@@ -6322,6 +6606,7 @@ void JOIN_TAB::cleanup()
quick= 0;
x_free(cache.buff);
cache.buff= 0;
+ limit= 0;
if (table)
{
if (table->key_read)
@@ -6340,11 +6625,10 @@ void JOIN_TAB::cleanup()
}
-/*
+/**
Partially cleanup JOIN after it has executed: close index or rnd read
(table cursors), free quick selects.
- DESCRIPTION
This function is called in the end of execution of a JOIN, before the used
tables are unlocked and closed.
@@ -6364,23 +6648,24 @@ void JOIN_TAB::cleanup()
If a JOIN is executed for a subquery or if it has a subquery, we can't
do the full cleanup and need to do a partial cleanup only.
- o If a JOIN is not the top level join, we must not unlock the tables
- because the outer select may not have been evaluated yet, and we
- can't unlock only selected tables of a query.
-
- o Additionally, if this JOIN corresponds to a correlated subquery, we
- should not free quick selects and join buffers because they will be
- needed for the next execution of the correlated subquery.
-
- o However, if this is a JOIN for a [sub]select, which is not
- a correlated subquery itself, but has subqueries, we can free it
- fully and also free JOINs of all its subqueries. The exception
- is a subquery in SELECT list, e.g:
- SELECT a, (select max(b) from t1) group by c
- This subquery will not be evaluated at first sweep and its value will
- not be inserted into the temporary table. Instead, it's evaluated
- when selecting from the temporary table. Therefore, it can't be freed
- here even though it's not correlated.
+ - If a JOIN is not the top level join, we must not unlock the tables
+ because the outer select may not have been evaluated yet, and we
+ can't unlock only selected tables of a query.
+ - Additionally, if this JOIN corresponds to a correlated subquery, we
+ should not free quick selects and join buffers because they will be
+ needed for the next execution of the correlated subquery.
+ - However, if this is a JOIN for a [sub]select, which is not
+ a correlated subquery itself, but has subqueries, we can free it
+ fully and also free JOINs of all its subqueries. The exception
+ is a subquery in SELECT list, e.g: @n
+ SELECT a, (select max(b) from t1) group by c @n
+ This subquery will not be evaluated at first sweep and its value will
+ not be inserted into the temporary table. Instead, it's evaluated
+ when selecting from the temporary table. Therefore, it can't be freed
+ here even though it's not correlated.
+
+ @todo
+ Unlock tables even if the join isn't top level select in the tree
*/
void JOIN::join_free()
@@ -6440,15 +6725,15 @@ void JOIN::join_free()
}
-/*
- Free resources of given join
+/**
+ Free resources of given join.
- SYNOPSIS
- JOIN::cleanup()
- fill - true if we should free all resources, call with full==1 should be
- last, before it this function can be called with full==0
+ @param fill true if we should free all resources, call with full==1
+ should be last, before it this function can be called with
+ full==0
- NOTE: with subquery this function definitely will be called several times,
+ @note
+ With subquery this function definitely will be called several times,
but even for simple query it can be called several times.
*/
@@ -6480,7 +6765,7 @@ void JOIN::cleanup(bool full)
for (tab= join_tab, end= tab+tables; tab != end; tab++)
{
if (tab->table)
- tab->table->file->ha_index_or_rnd_end();
+ tab->table->file->ha_index_or_rnd_end();
}
}
}
@@ -6524,21 +6809,25 @@ void JOIN::cleanup(bool full)
}
-/*****************************************************************************
+/**
Remove the following expressions from ORDER BY and GROUP BY:
- Constant expressions
+ Constant expressions @n
Expression that only uses tables that are of type EQ_REF and the reference
is in the ORDER list or if all refereed tables are of the above type.
In the following, the X field can be removed:
+ @code
SELECT * FROM t1,t2 WHERE t1.a=t2.a ORDER BY t1.a,t2.X
SELECT * FROM t1,t2,t3 WHERE t1.a=t2.a AND t2.b=t3.b ORDER BY t1.a,t3.X
+ @endcode
These can't be optimized:
+ @code
SELECT * FROM t1,t2 WHERE t1.a=t2.a ORDER BY t2.X,t1.a
SELECT * FROM t1,t2 WHERE t1.a=t2.a AND t1.b=t2.b ORDER BY t1.a,t2.c
SELECT * FROM t1,t2 WHERE t1.a=t2.a ORDER BY t2.b,t1.a
-*****************************************************************************/
+ @endcode
+*/
static bool
eq_ref_table(JOIN *join, ORDER *start_order, JOIN_TAB *tab)
@@ -6607,7 +6896,7 @@ only_eq_ref_tables(JOIN *join,ORDER *order,table_map tables)
}
-/* Update the dependency map for the tables */
+/** Update the dependency map for the tables. */
static void update_depend_map(JOIN *join)
{
@@ -6634,7 +6923,7 @@ static void update_depend_map(JOIN *join)
}
-/* Update the dependency map for the sort order */
+/** Update the dependency map for the sort order. */
static void update_depend_map(JOIN *join, ORDER *order)
{
@@ -6659,25 +6948,23 @@ static void update_depend_map(JOIN *join, ORDER *order)
}
-/*
- Remove all constants and check if ORDER only contains simple expressions
-
- SYNOPSIS
- remove_const()
- join Join handler
- first_order List of SORT or GROUP order
- cond WHERE statement
- change_list Set to 1 if we should remove things from list
- If this is not set, then only simple_order is
- calculated
- simple_order Set to 1 if we are only using simple expressions
+/**
+ Remove all constants and check if ORDER only contains simple
+ expressions.
- RETURN
- Returns new sort order
+ 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.
- 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
+ @param join Join handler
+ @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
+ @return
+ Returns new sort order
*/
static ORDER *
@@ -6833,25 +7120,23 @@ template class List_iterator<Item_func_match>;
#endif
-/*
- Find the multiple equality predicate containing a field
-
- SYNOPSIS
- find_item_equal()
- cond_equal multiple equalities to search in
- field field to look for
- inherited_fl :out set up to TRUE if multiple equality is found
- on upper levels (not on current level of cond_equal)
-
- DESCRIPTION
- The function retrieves the multiple equalities accessed through
- the con_equal structure from current level and up looking for
- an equality containing field. It stops retrieval as soon as the equality
- is found and set up inherited_fl to TRUE if it's found on upper levels.
-
- RETURN
- Item_equal for the found multiple equality predicate if a success;
- NULL - otherwise.
+/**
+ Find the multiple equality predicate containing a field.
+
+ The function retrieves the multiple equalities accessed through
+ the con_equal structure from current level and up looking for
+ an equality containing field. It stops retrieval as soon as the equality
+ is found and set up inherited_fl to TRUE if it's found on upper levels.
+
+ @param cond_equal multiple equalities to search in
+ @param field field to look for
+ @param[out] inherited_fl set up to TRUE if multiple equality is found
+ on upper levels (not on current level of
+ cond_equal)
+
+ @return
+ - Item_equal for the found multiple equality predicate if a success;
+ - NULL otherwise.
*/
Item_equal *find_item_equal(COND_EQUAL *cond_equal, Field *field,
@@ -6877,18 +7162,9 @@ finish:
}
-/*
- Check whether an equality can be used to build multiple equalities
-
- SYNOPSIS
- check_simple_equality()
- left_item left term of the quality to be checked
- right_item right term of the equality to be checked
- item equality item if the equality originates from a condition
- predicate, 0 if the equality is the result of row elimination
- cond_equal multiple equalities that must hold together with the equality
+/**
+ Check whether an equality can be used to build multiple equalities.
- DESCRIPTION
This function first checks whether the equality (left_item=right_item)
is a simple equality i.e. the one that equates a field with another field
or a constant (field=field_item or field=const_item).
@@ -6903,22 +7179,24 @@ finish:
This guarantees that the set of multiple equalities covering equality
predicates will be minimal.
- EXAMPLE
+ EXAMPLE:
For the where condition
- WHERE a=b AND b=c AND
- (b=2 OR f=e)
+ @code
+ WHERE a=b AND b=c AND
+ (b=2 OR f=e)
+ @endcode
the check_equality will be called for the following equality
predicates a=b, b=c, b=2 and f=e.
- For a=b it will be called with *cond_equal=(0,[]) and will transform
- *cond_equal into (0,[Item_equal(a,b)]).
- For b=c it will be called with *cond_equal=(0,[Item_equal(a,b)])
- and will transform *cond_equal into CE=(0,[Item_equal(a,b,c)]).
- For b=2 it will be called with *cond_equal=(ptr(CE),[])
- and will transform *cond_equal into (ptr(CE),[Item_equal(2,a,b,c)]).
- For f=e it will be called with *cond_equal=(ptr(CE), [])
- and will transform *cond_equal into (ptr(CE),[Item_equal(f,e)]).
-
- NOTES
+ - For a=b it will be called with *cond_equal=(0,[]) and will transform
+ *cond_equal into (0,[Item_equal(a,b)]).
+ - For b=c it will be called with *cond_equal=(0,[Item_equal(a,b)])
+ and will transform *cond_equal into CE=(0,[Item_equal(a,b,c)]).
+ - For b=2 it will be called with *cond_equal=(ptr(CE),[])
+ and will transform *cond_equal into (ptr(CE),[Item_equal(2,a,b,c)]).
+ - For f=e it will be called with *cond_equal=(ptr(CE), [])
+ and will transform *cond_equal into (ptr(CE),[Item_equal(f,e)]).
+
+ @note
Now only fields that have the same type definitions (verified by
the Field::eq_def method) are placed to the same multiple equalities.
Because of this some equality predicates are not eliminated and
@@ -6930,8 +7208,8 @@ finish:
equality. But at the same time it would allow us to get rid
of constant propagation completely: it would be done by the call
to build_equal_items_for_cond.
-
- IMPLEMENTATION
+
+
The implementation does not follow exactly the above rules to
build a new multiple equality for the equality predicate.
If it processes the equality of the form field1=field2, it
@@ -6951,9 +7229,18 @@ finish:
acceptable, as this happens rarely. The implementation without
copying would be much more complicated.
- RETURN
+ @param left_item left term of the quality to be checked
+ @param right_item right term of the equality to be checked
+ @param item equality item if the equality originates from a condition
+ predicate, 0 if the equality is the result of row
+ elimination
+ @param cond_equal multiple equalities that must hold together with the
+ equality
+
+ @retval
TRUE if the predicate is a simple equality predicate to be used
- for building multiple equalities
+ for building multiple equalities
+ @retval
FALSE otherwise
*/
@@ -7122,30 +7409,30 @@ static bool check_simple_equality(Item *left_item, Item *right_item,
}
-/*
- Convert row equalities into a conjunction of regular equalities
-
- SYNOPSIS
- check_row_equality()
- thd thread handle
- left_row left term of the row equality to be processed
- right_row right term of the row equality to be processed
- cond_equal multiple equalities that must hold together with the predicate
- eq_list results of conversions of row equalities that are not simple
- enough to form multiple equalities
+/**
+ Convert row equalities into a conjunction of regular equalities.
- DESCRIPTION
The function converts a row equality of the form (E1,...,En)=(E'1,...,E'n)
into a list of equalities E1=E'1,...,En=E'n. For each of these equalities
- Ei=E'i the function checks whether it is a simple equality or a row equality.
- If it is a simple equality it is used to expand multiple equalities of
- cond_equal. If it is a row equality it converted to a sequence of equalities
- between row elements. If Ei=E'i is neither a simple equality nor a row
- equality the item for this predicate is added to eq_list.
-
- RETURN
- TRUE if conversion has succeeded (no fatal error)
- FALSE otherwise
+ Ei=E'i the function checks whether it is a simple equality or a row
+ equality. If it is a simple equality it is used to expand multiple
+ equalities of cond_equal. If it is a row equality it converted to a
+ sequence of equalities between row elements. If Ei=E'i is neither a
+ simple equality nor a row equality the item for this predicate is added
+ to eq_list.
+
+ @param thd thread handle
+ @param left_row left term of the row equality to be processed
+ @param right_row right term of the row equality to be processed
+ @param cond_equal multiple equalities that must hold together with the
+ predicate
+ @param eq_list results of conversions of row equalities that are not
+ simple enough to form multiple equalities
+
+ @retval
+ TRUE if conversion has succeeded (no fatal error)
+ @retval
+ FALSE otherwise
*/
static bool check_row_equality(THD *thd, Item *left_row, Item_row *right_row,
@@ -7187,18 +7474,9 @@ static bool check_row_equality(THD *thd, Item *left_row, Item_row *right_row,
}
-/*
- Eliminate row equalities and form multiple equalities predicates
-
- SYNOPSIS
- check_equality()
- thd thread handle
- item predicate to process
- cond_equal multiple equalities that must hold together with the predicate
- eq_list results of conversions of row equalities that are not simple
- enough to form multiple equalities
+/**
+ Eliminate row equalities and form multiple equalities predicates.
- DESCRIPTION
This function checks whether the item is a simple equality
i.e. the one that equates a field with another field or a constant
(field=field_item or field=constant_item), or, a row equality.
@@ -7206,11 +7484,20 @@ static bool check_row_equality(THD *thd, Item *left_row, Item_row *right_row,
in the lists referenced directly or indirectly by cond_equal inferring
the given simple equality. If it doesn't find any, it builds/expands
multiple equality that covers the predicate.
- Row equalities are eliminated substituted for conjunctive regular equalities
- which are treated in the same way as original equality predicates.
-
- RETURN
+ Row equalities are eliminated substituted for conjunctive regular
+ equalities which are treated in the same way as original equality
+ predicates.
+
+ @param thd thread handle
+ @param item predicate to process
+ @param cond_equal multiple equalities that must hold together with the
+ predicate
+ @param eq_list results of conversions of row equalities that are not
+ simple enough to form multiple equalities
+
+ @retval
TRUE if re-writing rules have been applied
+ @retval
FALSE otherwise, i.e.
if the predicate is not an equality,
or, if the equality is neither a simple one nor a row equality,
@@ -7242,16 +7529,9 @@ static bool check_equality(THD *thd, Item *item, COND_EQUAL *cond_equal,
}
-/*
- Replace all equality predicates in a condition by multiple equality items
-
- SYNOPSIS
- build_equal_items_for_cond()
- thd thread handle
- cond condition(expression) where to make replacement
- inherited path to all inherited multiple equality items
+/**
+ Replace all equality predicates in a condition by multiple equality items.
- DESCRIPTION
At each 'and' level the function detects items for equality predicates
and replaced them by a set of multiple equality items of class Item_equal,
taking into account inherited equalities from upper levels.
@@ -7270,7 +7550,7 @@ static bool check_equality(THD *thd, Item *item, COND_EQUAL *cond_equal,
equality lists of each Item_cond_and object assigning it to
thd->lex->current_select->max_equal_elems.
- NOTES
+ @note
Multiple equality predicate =(f1,..fn) is equivalent to the conjuction of
f1=f2, .., fn-1=fn. It substitutes any inference from these
equality predicates that is equivalent to the conjunction.
@@ -7288,7 +7568,6 @@ static bool check_equality(THD *thd, Item *item, COND_EQUAL *cond_equal,
but if additionally =(t4.d,t2.b) is inherited, it
will be replaced by (=(t1.a,t2.b,t3.c,t4.d) AND t2.b>5)
- IMPLEMENTATION
The function performs the substitution in a recursive descent by
the condtion tree, passing to the next AND level a chain of multiple
equality predicates which have been built at the upper levels.
@@ -7302,10 +7581,15 @@ static bool check_equality(THD *thd, Item *item, COND_EQUAL *cond_equal,
- join them into disjoint Item_equal() groups
- process the included OR conditions recursively to do the same for
lower AND levels.
+
We need to do things in this order as lower AND levels need to know about
all possible Item_equal objects in upper levels.
- RETURN
+ @param thd thread handle
+ @param cond condition(expression) where to make replacement
+ @param inherited path to all inherited multiple equality items
+
+ @return
pointer to the transformed condition
*/
@@ -7442,30 +7726,21 @@ static COND *build_equal_items_for_cond(THD *thd, COND *cond,
as soon the field is not of a string type or the field reference is
an argument of a comparison predicate.
*/
- byte *is_subst_valid= (byte *) 1;
+ uchar *is_subst_valid= (uchar *) 1;
cond= cond->compile(&Item::subst_argument_checker,
&is_subst_valid,
&Item::equal_fields_propagator,
- (byte *) inherited);
+ (uchar *) inherited);
cond->update_used_tables();
}
return cond;
}
-/*
+/**
Build multiple equalities for a condition and all on expressions that
- inherit these multiple equalities
-
- SYNOPSIS
- build_equal_items()
- thd thread handle
- cond condition to build the multiple equalities for
- inherited path to all inherited multiple equality items
- join_list list of join tables to which the condition refers to
- cond_equal_ref :out pointer to the structure to place built equalities in
+ inherit these multiple equalities.
- DESCRIPTION
The function first applies the build_equal_items_for_cond function
to build all multiple equalities for condition cond utilizing equalities
referred through the parameter inherited. The extended set of
@@ -7474,14 +7749,16 @@ static COND *build_equal_items_for_cond(THD *thd, COND *cond,
all on expressions whose direct references can be found in join_list
and who inherit directly the multiple equalities just having built.
- NOTES
+ @note
The on expression used in an outer join operation inherits all equalities
- from the on expression of the embedding join, if there is any, or
+ from the on expression of the embedding join, if there is any, or
otherwise - from the where condition.
This fact is not obvious, but presumably can be proved.
Consider the following query:
+ @code
SELECT * FROM (t1,t2) LEFT JOIN (t3,t4) ON t1.a=t3.a AND t2.a=t4.a
WHERE t1.a=t2.a;
+ @endcode
If the on expression in the query inherits =(t1.a,t2.a), then we
can build the multiple equality =(t1.a,t2.a,t3.a,t4.a) that infers
the equality t3.a=t4.a. Although the on expression
@@ -7491,23 +7768,38 @@ static COND *build_equal_items_for_cond(THD *thd, COND *cond,
Interesting that multiple equality =(t1.a,t2.a,t3.a,t4.a) allows us
to use t1.a=t3.a AND t3.a=t4.a under the on condition:
+ @code
SELECT * FROM (t1,t2) LEFT JOIN (t3,t4) ON t1.a=t3.a AND t3.a=t4.a
WHERE t1.a=t2.a
+ @endcode
This query equivalent to:
+ @code
SELECT * FROM (t1 LEFT JOIN (t3,t4) ON t1.a=t3.a AND t3.a=t4.a),t2
WHERE t1.a=t2.a
+ @endcode
Similarly the original query can be rewritten to the query:
+ @code
SELECT * FROM (t1,t2) LEFT JOIN (t3,t4) ON t2.a=t4.a AND t3.a=t4.a
WHERE t1.a=t2.a
+ @endcode
that is equivalent to:
+ @code
SELECT * FROM (t2 LEFT JOIN (t3,t4)ON t2.a=t4.a AND t3.a=t4.a), t1
WHERE t1.a=t2.a
+ @endcode
Thus, applying equalities from the where condition we basically
can get more freedom in performing join operations.
Althogh we don't use this property now, it probably makes sense to use
it in the future.
-
- RETURN
+ @param thd Thread handler
+ @param cond condition to build the multiple equalities for
+ @param inherited path to all inherited multiple equality items
+ @param join_list list of join tables to which the condition
+ refers to
+ @param[out] cond_equal_ref pointer to the structure to place built
+ equalities in
+
+ @return
pointer to the transformed condition containing multiple equalities
*/
@@ -7565,25 +7857,24 @@ static COND *build_equal_items(THD *thd, COND *cond,
}
-/*
- Compare field items by table order in the execution plan
-
- SYNOPSIS
- compare_fields_by_table_order()
- field1 first field item to compare
- field2 second field item to compare
- table_join_idx index to tables determining table order
+/**
+ Compare field items by table order in the execution plan.
- DESCRIPTION
field1 considered as better than field2 if the table containing
field1 is accessed earlier than the table containing field2.
The function finds out what of two fields is better according
this criteria.
- RETURN
- 1, if field1 is better than field2
- -1, if field2 is better than field1
- 0, otherwise
+ @param field1 first field item to compare
+ @param field2 second field item to compare
+ @param table_join_idx index to tables determining table order
+
+ @retval
+ 1 if field1 is better than field2
+ @retval
+ -1 if field2 is better than field1
+ @retval
+ 0 otherwise
*/
static int compare_fields_by_table_order(Item_field *field1,
@@ -7605,21 +7896,14 @@ static int compare_fields_by_table_order(Item_field *field1,
if (outer_ref)
return cmp;
JOIN_TAB **idx= (JOIN_TAB **) table_join_idx;
- cmp= (uint) (idx[field2->field->table->tablenr] - idx[field1->field->table->tablenr]);
+ cmp= idx[field2->field->table->tablenr]-idx[field1->field->table->tablenr];
return cmp < 0 ? -1 : (cmp ? 1 : 0);
}
-/*
- Generate minimal set of simple equalities equivalent to a multiple equality
-
- SYNOPSIS
- eliminate_item_equal()
- cond condition to add the generated equality to
- upper_levels structure to access multiple equality of upper levels
- item_equal multiple equality to generate simple equality from
+/**
+ Generate minimal set of simple equalities equivalent to a multiple equality.
- DESCRIPTION
The function retrieves the fields of the multiple equality item
item_equal and for each field f:
- if item_equal contains const it generates the equality f=const_item;
@@ -7627,7 +7911,11 @@ static int compare_fields_by_table_order(Item_field *field1,
f=item_equal->get_first().
All generated equality are added to the cond conjunction.
- NOTES
+ @param cond condition to add the generated equality to
+ @param upper_levels structure to access multiple equality of upper levels
+ @param item_equal multiple equality to generate simple equality from
+
+ @note
Before generating an equality function checks that it has not
been generated for multiple equalities of the upper levels.
E.g. for the following where condition
@@ -7647,10 +7935,10 @@ static int compare_fields_by_table_order(Item_field *field1,
If cond is equal to 0, then not more then one equality is generated
and a pointer to it is returned as the result of the function.
- RETURN
- The condition with generated simple equalities or
+ @return
+ - The condition with generated simple equalities or
a pointer to the simple generated equality, if success.
- 0, otherwise.
+ - 0, otherwise.
*/
static Item *eliminate_item_equal(COND *cond, COND_EQUAL *upper_levels,
@@ -7725,17 +8013,10 @@ static Item *eliminate_item_equal(COND *cond, COND_EQUAL *upper_levels,
}
-/*
- Substitute every field reference in a condition by the best equal field
- and eliminate all multiple equality predicates
-
- SYNOPSIS
- substitute_for_best_equal_field()
- cond condition to process
- cond_equal multiple equalities to take into consideration
- table_join_idx index to tables determining field preference
+/**
+ Substitute every field reference in a condition by the best equal field
+ and eliminate all multiple equality predicates.
- DESCRIPTION
The function retrieves the cond condition and for each encountered
multiple equality predicate it sorts the field references in it
according to the order of tables specified by the table_join_idx
@@ -7746,14 +8027,17 @@ static Item *eliminate_item_equal(COND *cond, COND_EQUAL *upper_levels,
After this the function retrieves all other conjuncted
predicates substitute every field reference by the field reference
to the first equal field or equal constant if there are any.
-
- NOTES
+ @param cond condition to process
+ @param cond_equal multiple equalities to take into consideration
+ @param table_join_idx index to tables determining field preference
+
+ @note
At the first glance full sort of fields in multiple equality
seems to be an overkill. Yet it's not the case due to possible
new fields in multiple equality item of lower levels. We want
the order in them to comply with the order of upper levels.
- RETURN
+ @return
The transformed condition
*/
@@ -7828,20 +8112,17 @@ static COND* substitute_for_best_equal_field(COND *cond,
}
-/*
+/**
Check appearance of new constant items in multiple equalities
- of a condition after reading a constant table
-
- SYNOPSIS
- update_const_equal_items()
- cond condition whose multiple equalities are to be checked
- table constant table that has been read
+ of a condition after reading a constant table.
- DESCRIPTION
The function retrieves the cond condition and for each encountered
multiple equality checks whether new constants have appeared after
reading the constant (single row) table tab. If so it adjusts
the multiple equality appropriately.
+
+ @param cond condition whose multiple equalities are to be checked
+ @param table constant table that has been read
*/
static void update_const_equal_items(COND *cond, JOIN_TAB *tab)
@@ -7875,6 +8156,22 @@ static void update_const_equal_items(COND *cond, JOIN_TAB *tab)
key_map possible_keys= field->key_start;
possible_keys.intersect(field->table->keys_in_use_for_query);
stat[0].const_keys.merge(possible_keys);
+
+ /*
+ For each field in the multiple equality (for which we know that it
+ is a constant) we have to find its corresponding key part, and set
+ that key part in const_key_parts.
+ */
+ if (!possible_keys.is_clear_all())
+ {
+ TABLE *tab= field->table;
+ KEYUSE *use;
+ for (use= stat->keyuse; use && use->table == tab; use++)
+ if (possible_keys.is_set(use->key) &&
+ tab->key_info[use->key].key_part[use->keypart].field ==
+ field)
+ tab->const_key_parts[use->key]|= use->keypart_map;
+ }
}
}
}
@@ -7964,14 +8261,12 @@ change_cond_ref_to_const(THD *thd, I_List<COND_CMP> *save_list,
}
}
-/*
- Remove additional condition inserted by IN/ALL/ANY transformation
+/**
+ Remove additional condition inserted by IN/ALL/ANY transformation.
- SYNOPSIS
- remove_additional_cond()
- conds Condition for processing
+ @param conds condition for processing
- RETURN VALUES
+ @return
new conditions
*/
@@ -8059,17 +8354,10 @@ propagate_cond_constants(THD *thd, I_List<COND_CMP> *save_list,
}
-/*
- Simplify joins replacing outer joins by inner joins whenever it's possible
-
- SYNOPSIS
- simplify_joins()
- join reference to the query info
- join_list list representation of the join to be converted
- conds conditions to add on expressions for converted joins
- top true <=> conds is the where condition
+/**
+ Simplify joins replacing outer joins by inner joins whenever it's
+ possible.
- DESCRIPTION
The function, during a retrieval of join_list, eliminates those
outer joins that can be converted into inner join, possibly nested.
It also moves the on expressions for the converted outer joins
@@ -8093,26 +8381,39 @@ propagate_cond_constants(THD *thd, I_List<COND_CMP> *save_list,
The function also removes all braces that can be removed from the join
expression without changing its meaning.
- NOTES
+ @note
An outer join can be replaced by an inner join if the where condition
or the on expression for an embedding nested join contains a conjunctive
predicate rejecting null values for some attribute of the inner tables.
E.g. in the query:
+ @code
SELECT * FROM t1 LEFT JOIN t2 ON t2.a=t1.a WHERE t2.b < 5
+ @endcode
the predicate t2.b < 5 rejects nulls.
The query is converted first to:
+ @code
SELECT * FROM t1 INNER JOIN t2 ON t2.a=t1.a WHERE t2.b < 5
+ @endcode
then to the equivalent form:
- SELECT * FROM t1, t2 ON t2.a=t1.a WHERE t2.b < 5 AND t2.a=t1.a.
+ @code
+ SELECT * FROM t1, t2 ON t2.a=t1.a WHERE t2.b < 5 AND t2.a=t1.a
+ @endcode
+
Similarly the following query:
+ @code
SELECT * from t1 LEFT JOIN (t2, t3) ON t2.a=t1.a t3.b=t1.b
WHERE t2.c < 5
+ @endcode
is converted to:
+ @code
SELECT * FROM t1, (t2, t3) WHERE t2.c < 5 AND t2.a=t1.a t3.b=t1.b
+ @endcode
+
One conversion might trigger another:
+ @code
SELECT * FROM t1 LEFT JOIN t2 ON t2.a=t1.a
LEFT JOIN t3 ON t3.b=t2.b
WHERE t3 IS NOT NULL =>
@@ -8120,16 +8421,26 @@ propagate_cond_constants(THD *thd, I_List<COND_CMP> *save_list,
WHERE t3 IS NOT NULL AND t3.b=t2.b =>
SELECT * FROM t1, t2, t3
WHERE t3 IS NOT NULL AND t3.b=t2.b AND t2.a=t1.a
-
+ @endcode
+
The function removes all unnecessary braces from the expression
produced by the conversions.
- E.g. SELECT * FROM t1, (t2, t3) WHERE t2.c < 5 AND t2.a=t1.a AND t3.b=t1.b
+ E.g.
+ @code
+ SELECT * FROM t1, (t2, t3) WHERE t2.c < 5 AND t2.a=t1.a AND t3.b=t1.b
+ @endcode
finally is converted to:
+ @code
SELECT * FROM t1, t2, t3 WHERE t2.c < 5 AND t2.a=t1.a AND t3.b=t1.b
+ @endcode
+
+
It also will remove braces from the following queries:
+ @code
SELECT * from (t1 LEFT JOIN t2 ON t2.a=t1.a) LEFT JOIN t3 ON t3.b=t2.b
SELECT * from (t1, (t2,t3)) WHERE t1.a=t2.a AND t2.b=t3.b.
+ @endcode
The benefit of this simplification procedure is that it might return
a query for which the optimizer can evaluate execution plan with more
@@ -8137,20 +8448,26 @@ propagate_cond_constants(THD *thd, I_List<COND_CMP> *save_list,
consider any plan where one of the inner tables is before some of outer
tables.
- IMPLEMENTATION.
+
The function is implemented by a recursive procedure. On the recursive
ascent all attributes are calculated, all outer joins that can be
converted are replaced and then all unnecessary braces are removed.
As join list contains join tables in the reverse order sequential
elimination of outer joins does not require extra recursive calls.
- EXAMPLES
Here is an example of a join query with invalid cross references:
+ @code
SELECT * FROM t1 LEFT JOIN t2 ON t2.a=t3.a LEFT JOIN t3 ON t3.b=t1.b
-
- RETURN VALUE
- The new condition, if success
- 0, otherwise
+ @endcode
+
+ @param join reference to the query info
+ @param join_list list representation of the join to be converted
+ @param conds conditions to add on expressions for converted joins
+ @param top true <=> conds is the where condition
+
+ @return
+ - The new condition, if success
+ - 0, otherwise
*/
static COND *
@@ -8322,26 +8639,23 @@ simplify_joins(JOIN *join, List<TABLE_LIST> *join_list, COND *conds, bool top)
}
-/*
- Assign each nested join structure a bit in nested_join_map
-
- SYNOPSIS
- build_bitmap_for_nested_joins()
- join Join being processed
- join_list List of tables
- first_unused Number of first unused bit in nested_join_map before the
- call
+/**
+ Assign each nested join structure a bit in nested_join_map.
- DESCRIPTION
Assign each nested join structure (except "confluent" ones - those that
embed only one element) a bit in nested_join_map.
- NOTE
+ @param join Join being processed
+ @param join_list List of tables
+ @param first_unused Number of first unused bit in nested_join_map before the
+ call
+
+ @note
This function is called after simplify_joins(), when there are no
redundant nested joins, #non_confluent_nested_joins <= #tables_in_join so
we will not run out of bits in nested_join_map.
- RETURN
+ @return
First unused bit in nested_join_map after the call.
*/
@@ -8377,17 +8691,14 @@ static uint build_bitmap_for_nested_joins(List<TABLE_LIST> *join_list,
}
-/*
- Set NESTED_JOIN::counter=0 in all nested joins in passed list
-
- SYNOPSIS
- reset_nj_counters()
- join_list List of nested joins to process. It may also contain base
- tables which will be ignored.
+/**
+ Set NESTED_JOIN::counter=0 in all nested joins in passed list.
- DESCRIPTION
Recursively set NESTED_JOIN::counter=0 for all nested joins contained in
the passed join_list.
+
+ @param join_list List of nested joins to process. It may also contain base
+ tables which will be ignored.
*/
static void reset_nj_counters(List<TABLE_LIST> *join_list)
@@ -8408,92 +8719,93 @@ static void reset_nj_counters(List<TABLE_LIST> *join_list)
}
-/*
- Check interleaving with an inner tables of an outer join for extension table
-
- SYNOPSIS
- check_interleaving_with_nj()
- next_tab Table we're going to extend the current partial join with
+/**
+ Check interleaving with an inner tables of an outer join for
+ extension table.
- DESCRIPTION
Check if table next_tab can be added to current partial join order, and
if yes, record that it has been added.
The function assumes that both current partial join order and its
extension with next_tab are valid wrt table dependencies.
- IMPLEMENTATION
- LIMITATIONS ON JOIN ORDER
- The nested [outer] joins executioner algorithm imposes these limitations
- on join order:
- 1. "Outer tables first" - any "outer" table must be before any
- corresponding "inner" table.
- 2. "No interleaving" - tables inside a nested join must form a continuous
- sequence in join order (i.e. the sequence must not be interrupted by
- tables that are outside of this nested join).
-
- #1 is checked elsewhere, this function checks #2 provided that #1 has
- been already checked.
-
- WHY NEED NON-INTERLEAVING
- Consider an example:
-
- select * from t0 join t1 left join (t2 join t3) on cond1
-
- The join order "t1 t2 t0 t3" is invalid:
-
- table t0 is outside of the nested join, so WHERE condition for t0 is
- attached directly to t0 (without triggers, and it may be used to access
- t0). Applying WHERE(t0) to (t2,t0,t3) record is invalid as we may miss
- combinations of (t1, t2, t3) that satisfy condition cond1, and produce a
- null-complemented (t1, t2.NULLs, t3.NULLs) row, which should not have
- been produced.
-
- If table t0 is not between t2 and t3, the problem doesn't exist:
- * If t0 is located after (t2,t3), WHERE(t0) is applied after nested join
- processing has finished.
- * If t0 is located before (t2,t3), predicates like WHERE_cond(t0, t2) are
- wrapped into condition triggers, which takes care of correct nested
- join processing.
-
- HOW IT IS IMPLEMENTED
- The limitations on join order can be rephrased as follows: for valid
- join order one must be able to:
- 1. write down the used tables in the join order on one line.
- 2. for each nested join, put one '(' and one ')' on the said line
- 3. write "LEFT JOIN" and "ON (...)" where appropriate
- 4. get a query equivalent to the query we're trying to execute.
-
- Calls to check_interleaving_with_nj() are equivalent to writing the
- above described line from left to right.
- A single check_interleaving_with_nj(A,B) call is equivalent to writing
- table B and appropriate brackets on condition that table A and
- appropriate brackets is the last what was written. Graphically the
- transition is as follows:
-
- +---- current position
- |
- ... last_tab ))) | ( next_tab ) )..) | ...
- X Y Z |
- +- need to move to this
- position.
-
- Notes about the position:
- The caller guarantees that there is no more then one X-bracket by
- checking "!(remaining_tables & s->dependent)" before calling this
- function. X-bracket may have a pair in Y-bracket.
-
- When "writing" we store/update this auxilary info about the current
- position:
- 1. join->cur_embedding_map - bitmap of pairs of brackets (aka nested
- joins) we've opened but didn't close.
- 2. {each NESTED_JOIN structure not simplified away}->counter - number
- of this nested join's children that have already been added to to
- the partial join order.
-
- RETURN
- FALSE Join order extended, nested joins info about current join order
- (see NOTE section) updated.
+ @verbatim
+ IMPLEMENTATION
+ LIMITATIONS ON JOIN ORDER
+ The nested [outer] joins executioner algorithm imposes these limitations
+ on join order:
+ 1. "Outer tables first" - any "outer" table must be before any
+ corresponding "inner" table.
+ 2. "No interleaving" - tables inside a nested join must form a continuous
+ sequence in join order (i.e. the sequence must not be interrupted by
+ tables that are outside of this nested join).
+
+ #1 is checked elsewhere, this function checks #2 provided that #1 has
+ been already checked.
+
+ WHY NEED NON-INTERLEAVING
+ Consider an example:
+
+ select * from t0 join t1 left join (t2 join t3) on cond1
+
+ The join order "t1 t2 t0 t3" is invalid:
+
+ table t0 is outside of the nested join, so WHERE condition for t0 is
+ attached directly to t0 (without triggers, and it may be used to access
+ t0). Applying WHERE(t0) to (t2,t0,t3) record is invalid as we may miss
+ combinations of (t1, t2, t3) that satisfy condition cond1, and produce a
+ null-complemented (t1, t2.NULLs, t3.NULLs) row, which should not have
+ been produced.
+
+ If table t0 is not between t2 and t3, the problem doesn't exist:
+ If t0 is located after (t2,t3), WHERE(t0) is applied after nested join
+ processing has finished.
+ If t0 is located before (t2,t3), predicates like WHERE_cond(t0, t2) are
+ wrapped into condition triggers, which takes care of correct nested
+ join processing.
+
+ HOW IT IS IMPLEMENTED
+ The limitations on join order can be rephrased as follows: for valid
+ join order one must be able to:
+ 1. write down the used tables in the join order on one line.
+ 2. for each nested join, put one '(' and one ')' on the said line
+ 3. write "LEFT JOIN" and "ON (...)" where appropriate
+ 4. get a query equivalent to the query we're trying to execute.
+
+ Calls to check_interleaving_with_nj() are equivalent to writing the
+ above described line from left to right.
+ A single check_interleaving_with_nj(A,B) call is equivalent to writing
+ table B and appropriate brackets on condition that table A and
+ appropriate brackets is the last what was written. Graphically the
+ transition is as follows:
+
+ +---- current position
+ |
+ ... last_tab ))) | ( next_tab ) )..) | ...
+ X Y Z |
+ +- need to move to this
+ position.
+
+ Notes about the position:
+ The caller guarantees that there is no more then one X-bracket by
+ checking "!(remaining_tables & s->dependent)" before calling this
+ function. X-bracket may have a pair in Y-bracket.
+
+ When "writing" we store/update this auxilary info about the current
+ position:
+ 1. join->cur_embedding_map - bitmap of pairs of brackets (aka nested
+ joins) we've opened but didn't close.
+ 2. {each NESTED_JOIN structure not simplified away}->counter - number
+ of this nested join's children that have already been added to to
+ the partial join order.
+ @endverbatim
+
+ @param next_tab Table we're going to extend the current partial join with
+
+ @retval
+ FALSE Join order extended, nested joins info about current join
+ order (see NOTE section) updated.
+ @retval
TRUE Requested join order extension not allowed.
*/
@@ -8542,19 +8854,16 @@ static bool check_interleaving_with_nj(JOIN_TAB *next_tab)
}
-/*
- Nested joins perspective: Remove the last table from the join order
+/**
+ Nested joins perspective: Remove the last table from the join order.
- SYNOPSIS
- restore_prev_nj_state()
- last join table to remove, it is assumed to be the last in current
- partial join order.
-
- DESCRIPTION
Remove the last table from the partial join order and update the nested
joins counters and join->cur_embedding_map. It is ok to call this
function for the first table in join order (for which
check_interleaving_with_nj has not been called)
+
+ @param last join table to remove, it is assumed to be the last in current
+ partial join order.
*/
static void restore_prev_nj_state(JOIN_TAB *last)
@@ -8594,10 +8903,10 @@ optimize_cond(JOIN *join, COND *conds, List<TABLE_LIST> *join_list,
predicate. Substitute a constant instead of this field if the
multiple equality contains a constant.
*/
- DBUG_EXECUTE("where", print_where(conds, "original"););
+ DBUG_EXECUTE("where", print_where(conds, "original", QT_ORDINARY););
conds= build_equal_items(join->thd, conds, NULL, join_list,
&join->cond_equal);
- DBUG_EXECUTE("where",print_where(conds,"after equal_items"););
+ DBUG_EXECUTE("where",print_where(conds,"after equal_items", QT_ORDINARY););
/* change field = field to field = const for each found field = const */
propagate_cond_constants(thd, (I_List<COND_CMP> *) 0, conds, conds);
@@ -8605,20 +8914,23 @@ optimize_cond(JOIN *join, COND *conds, List<TABLE_LIST> *join_list,
Remove all instances of item == item
Remove all and-levels where CONST item != CONST item
*/
- DBUG_EXECUTE("where",print_where(conds,"after const change"););
+ DBUG_EXECUTE("where",print_where(conds,"after const change", QT_ORDINARY););
conds= remove_eq_conds(thd, conds, cond_value) ;
- DBUG_EXECUTE("info",print_where(conds,"after remove"););
+ DBUG_EXECUTE("info",print_where(conds,"after remove", QT_ORDINARY););
}
DBUG_RETURN(conds);
}
-/*
- 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 )
+/**
+ Remove const and eq items.
+
+ @return
+ Return new item, or NULL if no condition @n
+ 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 )
*/
COND *
@@ -8702,7 +9014,8 @@ 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->current_insert_id && thd->substitute_null_with_insert_id)
+ (thd->first_successful_insert_id_in_prev_stmt > 0 &&
+ thd->substitute_null_with_insert_id))
{
#ifdef HAVE_QUERY_CACHE
query_cache_abort(&thd->net);
@@ -8710,16 +9023,9 @@ remove_eq_conds(THD *thd, COND *cond, Item::cond_result *cond_value)
COND *new_cond;
if ((new_cond= new Item_func_eq(args[0],
new Item_int("last_insert_id()",
- thd->current_insert_id,
- MY_INT64_NUM_DECIMAL_DIGITS))))
+ thd->read_first_successful_insert_id_in_prev_stmt(),
+ MY_INT64_NUM_DECIMAL_DIGITS))))
{
- /*
- Set THD::last_insert_id_used_bin_log manually, as this
- statement uses LAST_INSERT_ID() in a sense, and should
- issue LAST_INSERT_ID_EVENT.
- */
- thd->last_insert_id_used_bin_log= TRUE;
-
cond=new_cond;
/*
Item_func_eq can't be fixed after creation so we do not check
@@ -8728,11 +9034,15 @@ remove_eq_conds(THD *thd, COND *cond, Item::cond_result *cond_value)
*/
cond->fix_fields(thd, &cond);
}
- thd->substitute_null_with_insert_id= FALSE; // Clear for next request
+ /*
+ IS NULL should be mapped to LAST_INSERT_ID only for first row, so
+ 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() == FIELD_TYPE_DATE) ||
- (field->type() == FIELD_TYPE_DATETIME)) &&
+ else if (((field->type() == MYSQL_TYPE_DATE) ||
+ (field->type() == MYSQL_TYPE_DATETIME)) &&
(field->flags & NOT_NULL_FLAG) &&
!field->table->maybe_null)
{
@@ -8811,8 +9121,8 @@ test_if_equality_guarantees_uniqueness(Item *l, Item *r)
l->collation.collation == r->collation.collation)));
}
-/*
- Return 1 if the item is a const value in all the WHERE clause
+/**
+ Return TRUE if the item is a const value in all the WHERE clause.
*/
static bool
@@ -8873,30 +9183,29 @@ const_expression_in_where(COND *cond, Item *comp_item, Item **const_item)
Create internal temporary table
****************************************************************************/
-/*
- Create field for temporary table from given field
-
- SYNOPSIS
- create_tmp_field_from_field()
- thd Thread handler
- org_field field from which new field will be created
- name New field name
- table Temporary table
- item !=NULL if item->result_field should point to new field.
- This is relevant for how fill_record() is going to work:
- If item != NULL then fill_record() will update
- the record in the original table.
- If item == NULL then fill_record() will update
- the temporary table
- convert_blob_length If >0 create a varstring(convert_blob_length) field
- instead of blob.
-
- RETURN
- 0 on error
+/**
+ Create field for temporary table from given field.
+
+ @param thd Thread handler
+ @param org_field field from which new field will be created
+ @param name New field name
+ @param table Temporary table
+ @param item !=NULL if item->result_field should point to new field.
+ This is relevant for how fill_record() is going to work:
+ If item != NULL then fill_record() will update
+ the record in the original table.
+ If item == NULL then fill_record() will update
+ the temporary table
+ @param convert_blob_length If >0 create a varstring(convert_blob_length)
+ field instead of blob.
+
+ @retval
+ NULL on error
+ @retval
new_created field
*/
-Field* create_tmp_field_from_field(THD *thd, Field* org_field,
+Field *create_tmp_field_from_field(THD *thd, Field *org_field,
const char *name, TABLE *table,
Item_field *item, uint convert_blob_length)
{
@@ -8910,13 +9219,15 @@ Field* create_tmp_field_from_field(THD *thd, Field* org_field,
(org_field->flags & BLOB_FLAG))
new_field= new Field_varstring(convert_blob_length,
org_field->maybe_null(),
- org_field->field_name, table,
+ org_field->field_name, table->s,
org_field->charset());
else
new_field= org_field->new_field(thd->mem_root, table,
table == org_field->table);
if (new_field)
{
+ new_field->init(table);
+ new_field->orig_table= org_field->orig_table;
if (item)
item->result_field= new_field;
else
@@ -8933,28 +9244,27 @@ Field* create_tmp_field_from_field(THD *thd, Field* org_field,
return new_field;
}
-/*
- Create field for temporary table using type of given item
-
- SYNOPSIS
- create_tmp_field_from_item()
- thd Thread handler
- item Item to create a field for
- table Temporary table
- copy_func If set and item is a function, store copy of item
- in this array
- modify_item 1 if item->result_field should point to new item.
- This is relevent for how fill_record() is going to
- work:
- If modify_item is 1 then fill_record() will update
- the record in the original table.
- If modify_item is 0 then fill_record() will update
- the temporary table
- convert_blob_length If >0 create a varstring(convert_blob_length) field
- instead of blob.
-
- RETURN
- 0 on error
+/**
+ Create field for temporary table using type of given item.
+
+ @param thd Thread handler
+ @param item Item to create a field for
+ @param table Temporary table
+ @param copy_func If set and item is a function, store copy of
+ item in this array
+ @param modify_item 1 if item->result_field should point to new
+ item. This is relevent for how fill_record()
+ is going to work:
+ If modify_item is 1 then fill_record() will
+ update the record in the original table.
+ If modify_item is 0 then fill_record() will
+ update the temporary table
+ @param convert_blob_length If >0 create a varstring(convert_blob_length)
+ field instead of blob.
+
+ @retval
+ 0 on error
+ @retval
new_created field
*/
@@ -8962,14 +9272,14 @@ static Field *create_tmp_field_from_item(THD *thd, Item *item, TABLE *table,
Item ***copy_func, bool modify_item,
uint convert_blob_length)
{
- bool maybe_null=item->maybe_null;
+ bool maybe_null= item->maybe_null;
Field *new_field;
LINT_INIT(new_field);
switch (item->result_type()) {
case REAL_RESULT:
- new_field=new Field_double(item->max_length, maybe_null,
- item->name, table, item->decimals, TRUE);
+ new_field= new Field_double(item->max_length, maybe_null,
+ item->name, item->decimals, TRUE);
break;
case INT_RESULT:
/*
@@ -8980,10 +9290,10 @@ static Field *create_tmp_field_from_item(THD *thd, Item *item, TABLE *table,
*/
if (item->max_length >= (MY_INT32_NUM_DECIMAL_DIGITS - 1))
new_field=new Field_longlong(item->max_length, maybe_null,
- item->name, table, item->unsigned_flag);
+ item->name, item->unsigned_flag);
else
new_field=new Field_long(item->max_length, maybe_null,
- item->name, table, item->unsigned_flag);
+ item->name, item->unsigned_flag);
break;
case STRING_RESULT:
DBUG_ASSERT(item->collation.collation);
@@ -8997,7 +9307,7 @@ static Field *create_tmp_field_from_item(THD *thd, Item *item, TABLE *table,
type == MYSQL_TYPE_TIME || type == MYSQL_TYPE_DATE ||
type == MYSQL_TYPE_NEWDATE ||
type == MYSQL_TYPE_TIMESTAMP || type == MYSQL_TYPE_GEOMETRY)
- new_field= item->tmp_table_field_from_field_type(table);
+ new_field= item->tmp_table_field_from_field_type(table, 1);
/*
Make sure that the blob fits into a Field_varstring which has
2-byte lenght.
@@ -9006,7 +9316,7 @@ static Field *create_tmp_field_from_item(THD *thd, Item *item, TABLE *table,
convert_blob_length <= Field_varstring::MAX_SIZE &&
convert_blob_length)
new_field= new Field_varstring(convert_blob_length, maybe_null,
- item->name, table,
+ item->name, table->s,
item->collation.collation);
else
new_field= item->make_string_field(table);
@@ -9047,16 +9357,19 @@ static Field *create_tmp_field_from_item(THD *thd, Item *item, TABLE *table,
}
new_field= new Field_new_decimal(len, maybe_null, item->name,
- table, dec, item->unsigned_flag);
+ dec, item->unsigned_flag);
break;
}
case ROW_RESULT:
default:
// This case should never be choosen
DBUG_ASSERT(0);
- new_field= 0; // to satisfy compiler (uninitialized variable)
+ new_field= 0;
break;
}
+ if (new_field)
+ new_field->init(table);
+
if (copy_func && item->is_result_field())
*((*copy_func)++) = item; // Save for copy_funcs
if (modify_item)
@@ -9067,17 +9380,16 @@ static Field *create_tmp_field_from_item(THD *thd, Item *item, TABLE *table,
}
-/*
- Create field for information schema table
+/**
+ Create field for information schema table.
- SYNOPSIS
- create_tmp_field_for_schema()
- thd Thread handler
- table Temporary table
- item Item to create a field for
+ @param thd Thread handler
+ @param table Temporary table
+ @param item Item to create a field for
- RETURN
+ @retval
0 on error
+ @retval
new_created field
*/
@@ -9085,44 +9397,48 @@ Field *create_tmp_field_for_schema(THD *thd, Item *item, TABLE *table)
{
if (item->field_type() == MYSQL_TYPE_VARCHAR)
{
- if (item->max_length > MAX_FIELD_VARCHARLENGTH /
- item->collation.collation->mbmaxlen)
- return new Field_blob(item->max_length, item->maybe_null,
- item->name, table, item->collation.collation);
- return new Field_varstring(item->max_length, item->maybe_null, item->name,
- table, item->collation.collation);
+ Field *field;
+ if (item->max_length > MAX_FIELD_VARCHARLENGTH)
+ field= new Field_blob(item->max_length, item->maybe_null,
+ item->name, item->collation.collation);
+ else
+ field= new Field_varstring(item->max_length, item->maybe_null,
+ item->name,
+ table->s, item->collation.collation);
+ if (field)
+ field->init(table);
+ return field;
}
- return item->tmp_table_field_from_field_type(table);
+ return item->tmp_table_field_from_field_type(table, 0);
}
-/*
- Create field for temporary table
-
- SYNOPSIS
- create_tmp_field()
- thd Thread handler
- table Temporary table
- item Item to create a field for
- type Type of item (normally item->type)
- copy_func If set and item is a function, store copy of item
- in this array
- from_field if field will be created using other field as example,
- pointer example field will be written here
- default_field If field has a default value field, store it here
- group 1 if we are going to do a relative group by on result
- modify_item 1 if item->result_field should point to new item.
- This is relevent for how fill_record() is going to
- work:
- If modify_item is 1 then fill_record() will update
- the record in the original table.
- If modify_item is 0 then fill_record() will update
- the temporary table
- convert_blob_length If >0 create a varstring(convert_blob_length) field
- instead of blob.
-
- RETURN
+/**
+ Create field for temporary table.
+
+ @param thd Thread handler
+ @param table Temporary table
+ @param item Item to create a field for
+ @param type Type of item (normally item->type)
+ @param copy_func If set and item is a function, store copy of item
+ in this array
+ @param from_field if field will be created using other field as example,
+ pointer example field will be written here
+ @param default_field If field has a default value field, store it here
+ @param group 1 if we are going to do a relative group by on result
+ @param modify_item 1 if item->result_field should point to new item.
+ This is relevent for how fill_record() is going to
+ work:
+ If modify_item is 1 then fill_record() will update
+ the record in the original table.
+ If modify_item is 0 then fill_record() will update
+ the temporary table
+ @param convert_blob_length If >0 create a varstring(convert_blob_length)
+ field instead of blob.
+
+ @retval
0 on error
+ @retval
new_created field
*/
@@ -9145,6 +9461,7 @@ Field *create_tmp_field(THD *thd, TABLE *table,Item *item, Item::Type type,
item= item->real_item();
type= Item::FIELD_ITEM;
}
+
switch (type) {
case Item::SUM_FUNC_ITEM:
{
@@ -9174,7 +9491,7 @@ Field *create_tmp_field(THD *thd, TABLE *table,Item *item, Item::Type type,
field->result_field= result;
}
else if (table_cant_handle_bit_fields && field->field->type() ==
- FIELD_TYPE_BIT)
+ MYSQL_TYPE_BIT)
{
*from_field= field->field;
result= create_tmp_field_from_item(thd, item, table, copy_func,
@@ -9198,6 +9515,36 @@ Field *create_tmp_field(THD *thd, TABLE *table,Item *item, Item::Type type,
}
/* Fall through */
case Item::FUNC_ITEM:
+ if (((Item_func *) item)->functype() == Item_func::FUNC_SP)
+ {
+ Item_func_sp *item_func_sp= (Item_func_sp *) item;
+ Field *sp_result_field= item_func_sp->get_sp_result_field();
+
+ if (make_copy_field)
+ {
+ DBUG_ASSERT(item_func_sp->result_field);
+ *from_field= item_func_sp->result_field;
+ }
+ else
+ {
+ *((*copy_func)++)= item;
+ }
+
+ Field *result_field=
+ create_tmp_field_from_field(thd,
+ sp_result_field,
+ item_func_sp->name,
+ table,
+ NULL,
+ convert_blob_length);
+
+ if (modify_item)
+ item->set_result_field(result_field);
+
+ return result_field;
+ }
+
+ /* Fall through */
case Item::COND_ITEM:
case Item::FIELD_AVG_ITEM:
case Item::FIELD_STD_ITEM:
@@ -9228,34 +9575,54 @@ Field *create_tmp_field(THD *thd, TABLE *table,Item *item, Item::Type type,
}
}
-
/*
- Create a temp table according to a field list.
+ Set up column usage bitmaps for a temporary table
- SYNOPSIS
- create_tmp_table()
- thd thread handle
- param a description used as input to create the table
- fields list of items that will be used to define
- column types of the table (also see NOTES)
- group TODO document
- distinct should table rows be distinct
- save_sum_fields see NOTES
- select_options
- rows_limit
- table_alias possible name of the temporary table that can be used
- for name resolving; can be "".
+ IMPLEMENTATION
+ For temporary tables, we need one bitmap with all columns set and
+ a tmp_set bitmap to be used by things like filesort.
+*/
- DESCRIPTION
- Given field pointers are changed to point at tmp_table for
- send_fields. 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
- corresponding Item_field items, pointing at the fields in the
- temporary table, unless this was prohibited by TRUE
- value of argument save_sum_fields. The Item_field objects
- are created in THD memory root.
+void setup_tmp_table_column_bitmaps(TABLE *table, uchar *bitmaps)
+{
+ uint field_count= table->s->fields;
+ bitmap_init(&table->def_read_set, (my_bitmap_map*) bitmaps, field_count,
+ FALSE);
+ bitmap_init(&table->tmp_set,
+ (my_bitmap_map*) (bitmaps+ bitmap_buffer_size(field_count)),
+ field_count, FALSE);
+ /* write_set and all_set are copies of read_set */
+ table->def_write_set= table->def_read_set;
+ table->s->all_set= table->def_read_set;
+ bitmap_set_all(&table->s->all_set);
+ table->default_column_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
+ 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
+ corresponding Item_field items, pointing at the fields in the
+ temporary table, unless this was prohibited by TRUE
+ value of argument save_sum_fields. The Item_field objects
+ are created in THD memory root.
+
+ @param thd thread handle
+ @param param a description used as input to create the table
+ @param fields list of items that will be used to define
+ column types of the table (also see NOTES)
+ @param group TODO document
+ @param distinct should table rows be distinct
+ @param save_sum_fields see NOTES
+ @param select_options
+ @param rows_limit
+ @param table_alias possible name of the temporary table that can
+ be used for name resolving; can be "".
*/
#define STRING_TOTAL_LENGTH_TO_PACK_ROWS 128
@@ -9271,17 +9638,19 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields,
{
MEM_ROOT *mem_root_save, own_root;
TABLE *table;
+ TABLE_SHARE *share;
uint i,field_count,null_count,null_pack_length;
uint copy_func_count= param->func_count;
uint hidden_null_count, hidden_null_pack_length, hidden_field_count;
uint blob_count,group_null_items, string_count;
uint temp_pool_slot=MY_BIT_NONE;
+ uint fieldnr= 0;
ulong reclength, string_total_length;
bool using_unique_constraint= 0;
bool use_packed_rows= 0;
bool not_all_columns= !(select_options & TMP_TABLE_ALL_COLUMNS);
char *tmpname,path[FN_REFLEN];
- byte *pos,*group_buff;
+ uchar *pos, *group_buff, *bitmaps;
uchar *null_flags;
Field **reg_field, **from_field, **default_field;
uint *blob_field;
@@ -9295,14 +9664,15 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields,
/* Treat sum functions as normal ones when loose index scan is used. */
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",
- (int) distinct, (int) save_sum_fields,
- (ulong) rows_limit,test(group)));
+ DBUG_PRINT("enter",
+ ("distinct: %d save_sum_fields: %d rows_limit: %lu group: %d",
+ (int) distinct, (int) save_sum_fields,
+ (ulong) rows_limit,test(group)));
- statistic_increment(thd->status_var.created_tmp_tables, &LOCK_status);
+ status_var_increment(thd->status_var.created_tmp_tables);
if (use_temp_pool && !(test_flags & TEST_KEEP_TMP_TABLES))
- temp_pool_slot = bitmap_set_next(&temp_pool);
+ temp_pool_slot = bitmap_lock_set_next(&temp_pool);
if (temp_pool_slot != MY_BIT_NONE) // we got a slot
sprintf(path, "%s_%lx_%i", tmp_file_prefix,
@@ -9354,6 +9724,7 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields,
if (!multi_alloc_root(&own_root,
&table, sizeof(*table),
+ &share, sizeof(*share),
&reg_field, sizeof(Field*) * (field_count+1),
&default_field, sizeof(Field*) * (field_count),
&blob_field, sizeof(uint)*(field_count+1),
@@ -9365,19 +9736,20 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields,
&param->start_recinfo,
sizeof(*param->recinfo)*(field_count*2+4),
&tmpname, (uint) strlen(path)+1,
- &group_buff, group && ! using_unique_constraint ?
- param->group_length : 0,
+ &group_buff, (group && ! using_unique_constraint ?
+ param->group_length : 0),
+ &bitmaps, bitmap_buffer_size(field_count)*2,
NullS))
{
if (temp_pool_slot != MY_BIT_NONE)
- bitmap_clear_bit(&temp_pool, temp_pool_slot);
+ bitmap_lock_clear_bit(&temp_pool, temp_pool_slot);
DBUG_RETURN(NULL); /* purecov: inspected */
}
/* Copy_field belongs to TMP_TABLE_PARAM, allocate it in THD mem_root */
if (!(param->copy_field= copy= new (thd->mem_root) Copy_field[field_count]))
{
if (temp_pool_slot != MY_BIT_NONE)
- bitmap_clear_bit(&temp_pool, temp_pool_slot);
+ bitmap_lock_clear_bit(&temp_pool, temp_pool_slot);
free_root(&own_root, MYF(0)); /* purecov: inspected */
DBUG_RETURN(NULL); /* purecov: inspected */
}
@@ -9403,21 +9775,18 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields,
table->copy_blobs= 1;
table->in_use= thd;
table->quick_keys.init();
- table->used_keys.init();
+ table->covering_keys.init();
table->keys_in_use_for_query.init();
- table->s= &table->share_not_to_be_used;
- table->s->blob_field= blob_field;
- table->s->table_name= table->s->path= tmpname;
- table->s->db= "";
- table->s->blob_ptr_size= mi_portable_sizeof_char_ptr;
- table->s->tmp_table= NON_TRANSACTIONAL_TMP_TABLE;
- table->s->db_low_byte_first=1; // True for HEAP and MyISAM
- table->s->table_charset= param->table_charset;
- table->s->keys_for_keyread.init();
- table->s->keys_in_use.init();
- /* For easier error reporting */
- table->s->table_cache_key= (char*) (table->s->db= "");
+ table->s= share;
+ init_tmp_table_share(thd, share, "", 0, tmpname, tmpname);
+ share->blob_field= blob_field;
+ share->blob_ptr_size= portable_sizeof_char_ptr;
+ share->db_low_byte_first=1; // True for HEAP and MyISAM
+ share->table_charset= param->table_charset;
+ share->primary_key= MAX_KEY; // Indicate no primary key
+ share->keys_for_keyread.init();
+ share->keys_in_use.init();
/* Calculate which type of fields we will store in the temporary table */
@@ -9461,10 +9830,9 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields,
Item *arg= sum_item->get_arg(i);
if (!arg->const_item())
{
- uint field_index= (uint) (reg_field - table->field);
Field *new_field=
create_tmp_field(thd, table, arg, arg->type(), &copy_func,
- tmp_from_field, &default_field[field_index],
+ tmp_from_field, &default_field[fieldnr],
group != 0,not_all_columns,
distinct, 0,
param->convert_blob_length);
@@ -9474,12 +9842,11 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields,
reclength+=new_field->pack_length();
if (new_field->flags & BLOB_FLAG)
{
- *blob_field++= field_index;
+ *blob_field++= fieldnr;
blob_count++;
}
- if (new_field->type() == FIELD_TYPE_BIT)
+ if (new_field->type() == MYSQL_TYPE_BIT)
total_uneven_bit_length+= new_field->field_length & 7;
- new_field->field_index= field_index;
*(reg_field++)= new_field;
if (new_field->real_type() == MYSQL_TYPE_STRING ||
new_field->real_type() == MYSQL_TYPE_VARCHAR)
@@ -9499,13 +9866,12 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields,
*/
arg->maybe_null=1;
}
- new_field->query_id= thd->query_id;
+ new_field->field_index= fieldnr++;
}
}
}
else
{
- uint field_index= (uint) (reg_field - table->field);
/*
The last parameter to create_tmp_field() is a bit tricky:
@@ -9522,7 +9888,7 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields,
Field *new_field= (param->schema_table) ?
create_tmp_field_for_schema(thd, item, table) :
create_tmp_field(thd, table, item, type, &copy_func,
- tmp_from_field, &default_field[field_index],
+ tmp_from_field, &default_field[fieldnr],
group != 0,
!force_copy_fields &&
(not_all_columns || group !=0),
@@ -9541,11 +9907,11 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields,
reclength+=new_field->pack_length();
if (!(new_field->flags & NOT_NULL_FLAG))
null_count++;
- if (new_field->type() == FIELD_TYPE_BIT)
+ if (new_field->type() == MYSQL_TYPE_BIT)
total_uneven_bit_length+= new_field->field_length & 7;
if (new_field->flags & BLOB_FLAG)
{
- *blob_field++= field_index;
+ *blob_field++= fieldnr;
blob_count++;
}
if (item->marker == 4 && item->maybe_null)
@@ -9553,9 +9919,8 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields,
group_null_items++;
new_field->flags|= GROUP_FLAG;
}
- new_field->query_id= thd->query_id;
- new_field->field_index= field_index;
- *(reg_field++) =new_field;
+ new_field->field_index= fieldnr++;
+ *(reg_field++)= new_field;
}
if (!--hidden_field_count)
{
@@ -9568,22 +9933,26 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields,
We need to update hidden_field_count as we may have stored group
functions with constant arguments
*/
- param->hidden_field_count= (uint) (reg_field - table->field);
+ param->hidden_field_count= fieldnr;
null_count= 0;
}
}
+ DBUG_ASSERT(fieldnr == (uint) (reg_field - table->field));
DBUG_ASSERT(field_count >= (uint) (reg_field - table->field));
- field_count= (uint) (reg_field - table->field);
+ field_count= fieldnr;
*reg_field= 0;
*blob_field= 0; // End marker
+ share->fields= field_count;
/* 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))
{
- table->file= get_new_handler(table, &table->mem_root,
- table->s->db_type= DB_TYPE_MYISAM);
+ share->db_plugin= ha_lock_engine(0, myisam_hton);
+ table->file= get_new_handler(share, &table->mem_root,
+ share->db_type());
if (group &&
(param->group_parts > table->file->max_key_parts() ||
param->group_length > table->file->max_key_length()))
@@ -9591,14 +9960,18 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields,
}
else
{
- table->file= get_new_handler(table, &table->mem_root,
- table->s->db_type= DB_TYPE_HEAP);
+ share->db_plugin= ha_lock_engine(0, heap_hton);
+ table->file= get_new_handler(share, &table->mem_root,
+ share->db_type());
}
+ if (!table->file)
+ goto err;
+
if (!using_unique_constraint)
reclength+= group_null_items; // null flag is stored separately
- table->s->blob_fields= blob_count;
+ share->blob_fields= blob_count;
if (blob_count == 0)
{
/* We need to ensure that first byte is not 0 for the delete link */
@@ -9620,34 +9993,35 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields,
string_total_length / string_count >= AVG_STRING_LENGTH_TO_PACK_ROWS))
use_packed_rows= 1;
- table->s->fields= field_count;
- table->s->reclength= reclength;
+ share->reclength= reclength;
{
uint alloc_length=ALIGN_SIZE(reclength+MI_UNIQUE_HASH_LENGTH+1);
- table->s->rec_buff_length= alloc_length;
- if (!(table->record[0]= (byte*)
+ share->rec_buff_length= alloc_length;
+ if (!(table->record[0]= (uchar*)
alloc_root(&table->mem_root, alloc_length*3)))
goto err;
table->record[1]= table->record[0]+alloc_length;
- table->s->default_values= table->record[1]+alloc_length;
+ share->default_values= table->record[1]+alloc_length;
}
copy_func[0]=0; // End marker
- param->func_count= (uint) (copy_func - param->items_to_copy);
+ param->func_count= copy_func - param->items_to_copy;
+
+ setup_tmp_table_column_bitmaps(table, bitmaps);
recinfo=param->start_recinfo;
null_flags=(uchar*) table->record[0];
pos=table->record[0]+ null_pack_length;
if (null_pack_length)
{
- bzero((byte*) recinfo,sizeof(*recinfo));
+ bzero((uchar*) recinfo,sizeof(*recinfo));
recinfo->type=FIELD_NORMAL;
recinfo->length=null_pack_length;
recinfo++;
bfill(null_flags,null_pack_length,255); // Set null fields
table->null_flags= (uchar*) table->record[0];
- table->s->null_fields= null_count+ hidden_null_count;
- table->s->null_bytes= null_pack_length;
+ share->null_fields= null_count+ hidden_null_count;
+ share->null_bytes= null_pack_length;
}
null_count= (blob_count == 0) ? 1 : 0;
hidden_field_count=param->hidden_field_count;
@@ -9655,7 +10029,7 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields,
{
Field *field= *reg_field;
uint length;
- bzero((byte*) recinfo,sizeof(*recinfo));
+ bzero((uchar*) recinfo,sizeof(*recinfo));
if (!(field->flags & NOT_NULL_FLAG))
{
@@ -9669,20 +10043,20 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields,
recinfo->length=1;
recinfo->type=FIELD_NORMAL;
recinfo++;
- bzero((byte*) recinfo,sizeof(*recinfo));
+ bzero((uchar*) recinfo,sizeof(*recinfo));
}
else
{
recinfo->null_bit= 1 << (null_count & 7);
recinfo->null_pos= null_count/8;
}
- field->move_field((char*) pos,null_flags+null_count/8,
+ field->move_field(pos,null_flags+null_count/8,
1 << (null_count & 7));
null_count++;
}
else
- field->move_field((char*) pos,(uchar*) 0,0);
- if (field->type() == FIELD_TYPE_BIT)
+ field->move_field(pos,(uchar*) 0,0);
+ if (field->type() == MYSQL_TYPE_BIT)
{
/* We have to reserve place for extra bits among null bits */
((Field_bit*) field)->set_bit_ptr(null_flags + null_count / 8,
@@ -9697,18 +10071,17 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields,
*/
if (default_field[i] && default_field[i]->ptr)
{
- /*
- default_field[i] is set only in the cases when 'field' can
- inherit the default value that is defined for the field referred
- by the Item_field object from which 'field' has been created.
+ /*
+ default_field[i] is set only in the cases when 'field' can
+ inherit the default value that is defined for the field referred
+ by the Item_field object from which 'field' has been created.
*/
my_ptrdiff_t diff;
Field *orig_field= default_field[i];
-
/* Get the value from default_values */
diff= (my_ptrdiff_t) (orig_field->table->s->default_values-
orig_field->table->record[0]);
- orig_field->move_field(diff); // Points now at default_values
+ orig_field->move_field_offset(diff); // Points now at default_values
if (orig_field->is_real_null())
field->set_null();
else
@@ -9716,8 +10089,8 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields,
field->set_notnull();
memcpy(field->ptr, orig_field->ptr, field->pack_length());
}
- orig_field->move_field(-diff); // Back to record[0]
- }
+ orig_field->move_field_offset(-diff); // Back to record[0]
+ }
if (from_field[i])
{ /* Not a table Item */
@@ -9749,19 +10122,19 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields,
store_record(table,s->default_values); // Make empty default record
if (thd->variables.tmp_table_size == ~ (ulonglong) 0) // No limit
- table->s->max_rows= ~(ha_rows) 0;
+ share->max_rows= ~(ha_rows) 0;
else
- table->s->max_rows= (ha_rows) (((table->s->db_type == DB_TYPE_HEAP) ?
- min(thd->variables.tmp_table_size,
- thd->variables.max_heap_table_size) :
- thd->variables.tmp_table_size)/
- table->s->reclength);
- set_if_bigger(table->s->max_rows,1); // For dummy start options
+ share->max_rows= (ha_rows) (((share->db_type() == heap_hton) ?
+ min(thd->variables.tmp_table_size,
+ thd->variables.max_heap_table_size) :
+ thd->variables.tmp_table_size) /
+ share->reclength);
+ set_if_bigger(share->max_rows,1); // For dummy start options
/*
Push the LIMIT clause to the temporary table creation, so that we
materialize only up to 'rows_limit' records instead of all result records.
*/
- set_if_smaller(table->s->max_rows, rows_limit);
+ set_if_smaller(share->max_rows, rows_limit);
param->end_write_records= rows_limit;
keyinfo= param->keyinfo;
@@ -9771,8 +10144,8 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields,
DBUG_PRINT("info",("Creating group key in temporary table"));
table->group=group; /* Table is grouped by key */
param->group_buff=group_buff;
- table->s->keys=1;
- table->s->uniques= test(using_unique_constraint);
+ share->keys=1;
+ share->uniques= test(using_unique_constraint);
table->key_info=keyinfo;
keyinfo->key_part=key_part_info;
keyinfo->flags=HA_NOSAME;
@@ -9788,7 +10161,7 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields,
bool maybe_null=(*cur_group->item)->maybe_null;
key_part_info->null_bit=0;
key_part_info->field= field;
- key_part_info->offset= field->offset();
+ key_part_info->offset= field->offset(table->record[0]);
key_part_info->length= (uint16) field->key_length();
key_part_info->type= (uint8) field->key_type();
key_part_info->key_type =
@@ -9800,10 +10173,10 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields,
{
cur_group->buff=(char*) group_buff;
if (!(cur_group->field= field->new_key_field(thd->mem_root,table,
- (char*) group_buff +
- test(maybe_null),
- field->null_ptr,
- field->null_bit)))
+ group_buff +
+ test(maybe_null),
+ field->null_ptr,
+ field->null_bit)))
goto err; /* purecov: inspected */
if (maybe_null)
{
@@ -9842,11 +10215,11 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields,
keyinfo->key_parts= ((field_count-param->hidden_field_count)+
test(null_pack_length));
table->distinct= 1;
- table->s->keys= 1;
+ share->keys= 1;
if (blob_count)
{
using_unique_constraint=1;
- table->s->uniques= 1;
+ share->uniques= 1;
}
if (!(key_part_info= (KEY_PART_INFO*)
alloc_root(&table->mem_root,
@@ -9865,12 +10238,15 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields,
key_part_info->null_bit=0;
key_part_info->offset=hidden_null_pack_length;
key_part_info->length=null_pack_length;
- key_part_info->field=new Field_string((char*) table->record[0],
- (uint32) key_part_info->length,
- (uchar*) 0,
- (uint) 0,
- Field::NONE,
- NullS, table, &my_charset_bin);
+ key_part_info->field= new Field_string(table->record[0],
+ (uint32) key_part_info->length,
+ (uchar*) 0,
+ (uint) 0,
+ Field::NONE,
+ NullS, &my_charset_bin);
+ if (!key_part_info->field)
+ goto err;
+ key_part_info->field->init(table);
key_part_info->key_type=FIELDFLAG_BINARY;
key_part_info->type= HA_KEYTYPE_BINARY;
key_part_info++;
@@ -9882,7 +10258,7 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields,
{
key_part_info->null_bit=0;
key_part_info->field= *reg_field;
- key_part_info->offset= (*reg_field)->offset();
+ key_part_info->offset= (*reg_field)->offset(table->record[0]);
key_part_info->length= (uint16) (*reg_field)->pack_length();
key_part_info->type= (uint8) (*reg_field)->key_type();
key_part_info->key_type =
@@ -9895,8 +10271,8 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields,
if (thd->is_fatal_error) // If end of memory
goto err; /* purecov: inspected */
- table->s->db_record_offset= 1;
- if (table->s->db_type == DB_TYPE_MYISAM)
+ share->db_record_offset= 1;
+ if (share->db_type() == myisam_hton)
{
if (create_myisam_tmp_table(table,param,select_options))
goto err;
@@ -9912,24 +10288,18 @@ err:
thd->mem_root= mem_root_save;
free_tmp_table(thd,table); /* purecov: inspected */
if (temp_pool_slot != MY_BIT_NONE)
- bitmap_clear_bit(&temp_pool, temp_pool_slot);
+ bitmap_lock_clear_bit(&temp_pool, temp_pool_slot);
DBUG_RETURN(NULL); /* purecov: inspected */
}
/****************************************************************************/
-/*
+/**
Create a reduced TABLE object with properly set up Field list from a
list of field definitions.
- SYNOPSIS
- create_virtual_tmp_table()
- thd connection handle
- field_list list of column definitions
-
- DESCRIPTION
- The created table doesn't have a table handler assotiated with
+ The created table doesn't have a table handler associated with
it, has no keys, no group/distinct, no copy_funcs array.
The sole purpose of this TABLE object is to use the power of Field
class to read/write data to/from table->record[0]. Then one can store
@@ -9937,90 +10307,100 @@ err:
The table is created in THD mem_root, so are the table's fields.
Consequently, if you don't BLOB fields, you don't need to free it.
- RETURN
+ @param thd connection handle
+ @param field_list list of column definitions
+
+ @return
0 if out of memory, TABLE object in case of success
*/
-TABLE *create_virtual_tmp_table(THD *thd, List<create_field> &field_list)
+TABLE *create_virtual_tmp_table(THD *thd, List<Create_field> &field_list)
{
uint field_count= field_list.elements;
uint blob_count= 0;
Field **field;
- create_field *cdef; /* column definition */
+ Create_field *cdef; /* column definition */
uint record_length= 0;
uint null_count= 0; /* number of columns which may be null */
uint null_pack_length; /* NULL representation array length */
- TABLE_SHARE *s;
- /* Create the table and list of all fields */
- TABLE *table= (TABLE*) thd->calloc(sizeof(*table));
- field= (Field**) thd->alloc((field_count + 1) * sizeof(Field*));
- if (!table || !field)
- return 0;
-
- table->field= field;
- table->s= s= &table->share_not_to_be_used;
- s->fields= field_count;
+ uint *blob_field;
+ uchar *bitmaps;
+ TABLE *table;
+ TABLE_SHARE *share;
- if (!(s->blob_field= (uint*)thd->alloc((field_list.elements + 1) *
- sizeof(uint))))
+ if (!multi_alloc_root(thd->mem_root,
+ &table, sizeof(*table),
+ &share, sizeof(*share),
+ &field, (field_count + 1) * sizeof(Field*),
+ &blob_field, (field_count+1) *sizeof(uint),
+ &bitmaps, bitmap_buffer_size(field_count)*2,
+ NullS))
return 0;
- s->blob_ptr_size= mi_portable_sizeof_char_ptr;
+ bzero(table, sizeof(*table));
+ bzero(share, sizeof(*share));
+ table->field= field;
+ table->s= share;
+ share->blob_field= blob_field;
+ share->fields= field_count;
+ share->blob_ptr_size= portable_sizeof_char_ptr;
+ setup_tmp_table_column_bitmaps(table, bitmaps);
/* Create all fields and calculate the total length of record */
- List_iterator_fast<create_field> it(field_list);
+ List_iterator_fast<Create_field> it(field_list);
while ((cdef= it++))
{
- *field= make_field(0, cdef->length,
+ *field= make_field(share, 0, cdef->length,
(uchar*) (f_maybe_null(cdef->pack_flag) ? "" : 0),
f_maybe_null(cdef->pack_flag) ? 1 : 0,
cdef->pack_flag, cdef->sql_type, cdef->charset,
cdef->geom_type, cdef->unireg_check,
- cdef->interval, cdef->field_name, table);
+ cdef->interval, cdef->field_name);
if (!*field)
goto error;
- record_length+= (**field).pack_length();
- if (! ((**field).flags & NOT_NULL_FLAG))
- ++null_count;
+ (*field)->init(table);
+ record_length+= (*field)->pack_length();
+ if (! ((*field)->flags & NOT_NULL_FLAG))
+ null_count++;
if ((*field)->flags & BLOB_FLAG)
- s->blob_field[blob_count++]= (uint) (field - table->field);
+ share->blob_field[blob_count++]= (uint) (field - table->field);
- ++field;
+ field++;
}
*field= NULL; /* mark the end of the list */
- s->blob_field[blob_count]= 0; /* mark the end of the list */
- s->blob_fields= blob_count;
+ share->blob_field[blob_count]= 0; /* mark the end of the list */
+ share->blob_fields= blob_count;
null_pack_length= (null_count + 7)/8;
- s->reclength= record_length + null_pack_length;
- s->rec_buff_length= ALIGN_SIZE(s->reclength + 1);
- table->record[0]= (byte*) thd->alloc(s->rec_buff_length);
+ share->reclength= record_length + null_pack_length;
+ share->rec_buff_length= ALIGN_SIZE(share->reclength + 1);
+ table->record[0]= (uchar*) thd->alloc(share->rec_buff_length);
if (!table->record[0])
goto error;
if (null_pack_length)
{
table->null_flags= (uchar*) table->record[0];
- s->null_fields= null_count;
- s->null_bytes= null_pack_length;
+ share->null_fields= null_count;
+ share->null_bytes= null_pack_length;
}
table->in_use= thd; /* field->reset() may access table->in_use */
{
/* Set up field pointers */
- byte *null_pos= table->record[0];
- byte *field_pos= null_pos + s->null_bytes;
+ uchar *null_pos= table->record[0];
+ uchar *field_pos= null_pos + share->null_bytes;
uint null_bit= 1;
for (field= table->field; *field; ++field)
{
Field *cur_field= *field;
if ((cur_field->flags & NOT_NULL_FLAG))
- cur_field->move_field((char*) field_pos);
+ cur_field->move_field(field_pos);
else
{
- cur_field->move_field((char*) field_pos, (uchar*) null_pos, null_bit);
+ cur_field->move_field(field_pos, (uchar*) null_pos, null_bit);
null_bit<<= 1;
if (null_bit == (1 << 8))
{
@@ -10044,8 +10424,8 @@ error:
static bool open_tmp_table(TABLE *table)
{
int error;
- if ((error=table->file->ha_open(table->s->table_name,O_RDWR,
- HA_OPEN_TMP_TABLE)))
+ if ((error=table->file->ha_open(table, table->s->table_name.str,O_RDWR,
+ HA_OPEN_TMP_TABLE | HA_OPEN_INTERNAL_TABLE)))
{
table->file->print_error(error,MYF(0)); /* purecov: inspected */
table->db_stat=0;
@@ -10063,9 +10443,10 @@ static bool create_myisam_tmp_table(TABLE *table,TMP_TABLE_PARAM *param,
MI_KEYDEF keydef;
MI_UNIQUEDEF uniquedef;
KEY *keyinfo=param->keyinfo;
+ TABLE_SHARE *share= table->s;
DBUG_ENTER("create_myisam_tmp_table");
- if (table->s->keys)
+ if (share->keys)
{ // Get keys for ni_create
bool using_unique_constraint=0;
HA_KEYSEG *seg= (HA_KEYSEG*) alloc_root(&table->mem_root,
@@ -10076,11 +10457,11 @@ static bool create_myisam_tmp_table(TABLE *table,TMP_TABLE_PARAM *param,
bzero(seg, sizeof(*seg) * keyinfo->key_parts);
if (keyinfo->key_length >= table->file->max_key_length() ||
keyinfo->key_parts > table->file->max_key_parts() ||
- table->s->uniques)
+ share->uniques)
{
/* Can't create a key; Make a unique constraint instead of a key */
- table->s->keys= 0;
- table->s->uniques= 1;
+ share->keys= 0;
+ share->uniques= 1;
using_unique_constraint=1;
bzero((char*) &uniquedef,sizeof(uniquedef));
uniquedef.keysegs=keyinfo->key_parts;
@@ -10088,11 +10469,11 @@ static bool create_myisam_tmp_table(TABLE *table,TMP_TABLE_PARAM *param,
uniquedef.null_are_equal=1;
/* Create extra column for hash value */
- bzero((byte*) param->recinfo,sizeof(*param->recinfo));
+ bzero((uchar*) param->recinfo,sizeof(*param->recinfo));
param->recinfo->type= FIELD_CHECK;
param->recinfo->length=MI_UNIQUE_HASH_LENGTH;
param->recinfo++;
- table->s->reclength+=MI_UNIQUE_HASH_LENGTH;
+ share->reclength+=MI_UNIQUE_HASH_LENGTH;
}
else
{
@@ -10114,7 +10495,7 @@ static bool create_myisam_tmp_table(TABLE *table,TMP_TABLE_PARAM *param,
seg->type=
((keyinfo->key_part[i].key_type & FIELDFLAG_BINARY) ?
HA_KEYTYPE_VARBINARY2 : HA_KEYTYPE_VARTEXT2);
- seg->bit_start= (uint8)(field->pack_length() - table->s->blob_ptr_size);
+ seg->bit_start= (uint8)(field->pack_length() - share->blob_ptr_size);
seg->flag= HA_BLOB_PART;
seg->length=0; // Whole blob in unique constraint
}
@@ -10147,10 +10528,10 @@ static bool create_myisam_tmp_table(TABLE *table,TMP_TABLE_PARAM *param,
OPTION_BIG_TABLES)
create_info.data_file_length= ~(ulonglong) 0;
- if ((error=mi_create(table->s->table_name,table->s->keys,&keydef,
+ if ((error=mi_create(share->table_name.str, share->keys, &keydef,
(uint) (param->recinfo-param->start_recinfo),
param->start_recinfo,
- table->s->uniques, &uniquedef,
+ share->uniques, &uniquedef,
&create_info,
HA_CREATE_TMP_TABLE)))
{
@@ -10158,9 +10539,8 @@ static bool create_myisam_tmp_table(TABLE *table,TMP_TABLE_PARAM *param,
table->db_stat=0;
goto err;
}
- statistic_increment(table->in_use->status_var.created_tmp_disk_tables,
- &LOCK_status);
- table->s->db_record_offset= 1;
+ status_var_increment(table->in_use->status_var.created_tmp_disk_tables);
+ share->db_record_offset= 1;
DBUG_RETURN(0);
err:
DBUG_RETURN(1);
@@ -10176,22 +10556,14 @@ free_tmp_table(THD *thd, TABLE *entry)
DBUG_PRINT("enter",("table: %s",entry->alias));
save_proc_info=thd->proc_info;
- thd->proc_info="removing tmp table";
+ thd_proc_info(thd, "removing tmp table");
if (entry->file)
{
if (entry->db_stat)
- {
- (void) entry->file->close();
- }
- /*
- We can't call ha_delete_table here as the table may created in mixed case
- here and we have to ensure that delete_table gets the table name in
- the original case.
- */
- if (!(test_flags & TEST_KEEP_TMP_TABLES) ||
- entry->s->db_type == DB_TYPE_HEAP)
- entry->file->delete_table(entry->s->table_name);
+ entry->file->ha_drop_table(entry->s->table_name.str);
+ else
+ entry->file->ha_delete_table(entry->s->table_name.str);
delete entry->file;
}
@@ -10201,48 +10573,54 @@ free_tmp_table(THD *thd, TABLE *entry)
free_io_cache(entry);
if (entry->temp_pool_slot != MY_BIT_NONE)
- bitmap_clear_bit(&temp_pool, entry->temp_pool_slot);
+ bitmap_lock_clear_bit(&temp_pool, entry->temp_pool_slot);
+
+ plugin_unlock(0, entry->s->db_plugin);
free_root(&own_root, MYF(0)); /* the table is allocated in its own root */
- thd->proc_info=save_proc_info;
+ thd_proc_info(thd, save_proc_info);
DBUG_VOID_RETURN;
}
-/*
-* If a HEAP table gets full, create a MyISAM table and copy all rows to this
+/**
+ If a HEAP table gets full, create a MyISAM table and copy all rows
+ to this.
*/
bool create_myisam_from_heap(THD *thd, TABLE *table, TMP_TABLE_PARAM *param,
int error, bool ignore_last_dupp_key_error)
{
TABLE new_table;
+ TABLE_SHARE share;
const char *save_proc_info;
int write_err;
DBUG_ENTER("create_myisam_from_heap");
- if (table->s->db_type != DB_TYPE_HEAP || error != HA_ERR_RECORD_FILE_FULL)
+ if (table->s->db_type() != heap_hton ||
+ error != HA_ERR_RECORD_FILE_FULL)
{
table->file->print_error(error,MYF(0));
DBUG_RETURN(1);
}
new_table= *table;
- new_table.s= &new_table.share_not_to_be_used;
- new_table.s->db_type= DB_TYPE_MYISAM;
- if (!(new_table.file= get_new_handler(&new_table, &new_table.mem_root,
- DB_TYPE_MYISAM)))
+ share= *table->s;
+ new_table.s= &share;
+ new_table.s->db_plugin= ha_lock_engine(thd, myisam_hton);
+ if (!(new_table.file= get_new_handler(&share, &new_table.mem_root,
+ new_table.s->db_type())))
DBUG_RETURN(1); // End of memory
save_proc_info=thd->proc_info;
- thd->proc_info="converting HEAP to MyISAM";
+ thd_proc_info(thd, "converting HEAP to MyISAM");
- if (create_myisam_tmp_table(&new_table,param,
+ if (create_myisam_tmp_table(&new_table, param,
thd->lex->select_lex.options | thd->options))
goto err2;
if (open_tmp_table(&new_table))
goto err1;
if (table->file->indexes_are_disabled())
- new_table.file->disable_indexes(HA_KEY_SWITCH_ALL);
+ new_table.file->ha_disable_indexes(HA_KEY_SWITCH_ALL);
table->file->ha_index_or_rnd_end();
table->file->ha_rnd_init(1);
if (table->no_rows)
@@ -10256,41 +10634,50 @@ bool create_myisam_from_heap(THD *thd, TABLE *table, TMP_TABLE_PARAM *param,
To use start_bulk_insert() (which is new in 4.1) we need to find
all places where a corresponding end_bulk_insert() should be put.
*/
- table->file->info(HA_STATUS_VARIABLE); /* update table->file->records */
- new_table.file->start_bulk_insert(table->file->records);
+ table->file->info(HA_STATUS_VARIABLE); /* update table->file->stats.records */
+ new_table.file->ha_start_bulk_insert(table->file->stats.records);
#else
/* HA_EXTRA_WRITE_CACHE can stay until close, no need to disable it */
new_table.file->extra(HA_EXTRA_WRITE_CACHE);
#endif
- /* copy all old rows */
+ /*
+ copy all old rows from heap table to MyISAM table
+ This is the only code that uses record[1] to read/write but this
+ is safe as this is a temporary MyISAM table without timestamp/autoincrement
+ or partitioning.
+ */
while (!table->file->rnd_next(new_table.record[1]))
{
- write_err=new_table.file->write_row(new_table.record[1]);
+ write_err= new_table.file->ha_write_row(new_table.record[1]);
DBUG_EXECUTE_IF("raise_error", write_err= HA_ERR_FOUND_DUPP_KEY ;);
if (write_err)
goto err;
}
/* copy row that filled HEAP table */
- if ((write_err=new_table.file->write_row(table->record[0])))
+ if ((write_err=new_table.file->ha_write_row(table->record[0])))
{
- if (write_err != HA_ERR_FOUND_DUPP_KEY &&
- write_err != HA_ERR_FOUND_DUPP_UNIQUE || !ignore_last_dupp_key_error)
- goto err;
+ if (new_table.file->is_fatal_error(write_err, HA_CHECK_DUP) ||
+ !ignore_last_dupp_key_error)
+ goto err;
}
/* remove heap table and change to use myisam table */
(void) table->file->ha_rnd_end();
- (void) table->file->close();
- (void) table->file->delete_table(table->s->table_name);
+ (void) table->file->close(); // This deletes the table !
delete table->file;
table->file=0;
+ plugin_unlock(0, table->s->db_plugin);
+ share.db_plugin= my_plugin_lock(0, &share.db_plugin);
+ new_table.s= table->s; // Keep old share
*table= new_table;
- table->s= &table->share_not_to_be_used;
- table->file->change_table_ptr(table);
+ *table->s= share;
+
+ table->file->change_table_ptr(table, table->s);
+ table->use_all_columns();
if (save_proc_info)
- thd->proc_info= (!strcmp(save_proc_info,"Copying to tmp table") ?
- "Copying to tmp table on disk" : save_proc_info);
+ thd_proc_info(thd, (!strcmp(save_proc_info,"Copying to tmp table") ?
+ "Copying to tmp table on disk" : save_proc_info));
DBUG_RETURN(0);
err:
@@ -10299,26 +10686,24 @@ bool create_myisam_from_heap(THD *thd, TABLE *table, TMP_TABLE_PARAM *param,
(void) table->file->ha_rnd_end();
(void) new_table.file->close();
err1:
- new_table.file->delete_table(new_table.s->table_name);
- delete new_table.file;
+ new_table.file->ha_delete_table(new_table.s->table_name.str);
err2:
- thd->proc_info=save_proc_info;
+ delete new_table.file;
+ thd_proc_info(thd, save_proc_info);
table->mem_root= new_table.mem_root;
DBUG_RETURN(1);
}
-/*
- SYNOPSIS
- setup_end_select_func()
- join join to setup the function for.
+/**
+ @details
+ Rows produced by a join sweep may end up in a temporary table or be sent
+ to a client. Setup the function of the nested loop join algorithm which
+ handles final fully constructed and matched records.
- DESCRIPTION
- Rows produced by a join sweep may end up in a temporary table or be sent
- to a client. Setup the function of the nested loop join algorithm which
- handles final fully constructed and matched records.
+ @param join join to setup the function for.
- RETURN
+ @return
end_select function to use. This function can't fail.
*/
@@ -10383,12 +10768,16 @@ Next_select_func setup_end_select_func(JOIN *join)
}
-/****************************************************************************
- Make a join of all tables and write it on socket or to table
- Return: 0 if ok
- 1 if error is sent
- -1 if error should be sent
-****************************************************************************/
+/**
+ Make a join of all tables and write it on socket or to table.
+
+ @retval
+ 0 if ok
+ @retval
+ 1 if error is sent
+ @retval
+ -1 if error should be sent
+*/
static int
do_select(JOIN *join,List<Item> *fields,TABLE *table,Procedure *procedure)
@@ -10398,7 +10787,7 @@ do_select(JOIN *join,List<Item> *fields,TABLE *table,Procedure *procedure)
JOIN_TAB *join_tab;
DBUG_ENTER("do_select");
LINT_INIT(join_tab);
-
+
join->procedure=procedure;
join->tmp_table= table; /* Save for easy recursion */
join->fields= fields;
@@ -10409,7 +10798,7 @@ do_select(JOIN *join,List<Item> *fields,TABLE *table,Procedure *procedure)
empty_record(table);
if (table->group && join->tmp_table_param.sum_func_count &&
table->s->keys && !table->file->inited)
- table->file->ha_index_init(0);
+ table->file->ha_index_init(0, 0);
}
/* Set up select_end */
Next_select_func end_select= setup_end_select_func(join);
@@ -10428,9 +10817,9 @@ do_select(JOIN *join,List<Item> *fields,TABLE *table,Procedure *procedure)
*/
if (!join->conds || join->conds->val_int())
{
- error= (*end_select)(join,join_tab,0);
+ error= (*end_select)(join, 0, 0);
if (error == NESTED_LOOP_OK || error == NESTED_LOOP_QUERY_LIMIT)
- error= (*end_select)(join,join_tab,1);
+ error= (*end_select)(join, 0, 1);
/*
If we don't go through evaluate_join_record(), do the counting
@@ -10502,7 +10891,7 @@ do_select(JOIN *join,List<Item> *fields,TABLE *table,Procedure *procedure)
DBUG_PRINT("error",("Error: do_select() failed"));
}
#endif
- DBUG_RETURN(join->thd->net.report_error ? -1 : rc);
+ DBUG_RETURN(join->thd->is_error() ? -1 : rc);
}
@@ -10535,35 +10924,30 @@ sub_select_cache(JOIN *join,JOIN_TAB *join_tab,bool end_of_records)
return rc;
}
-/*
- Retrieve records ends with a given beginning from the result of a join
-
- SYNPOSIS
- sub_select()
- join pointer to the structure providing all context info for the query
- join_tab the first next table of the execution plan to be retrieved
- end_records true when we need to perform final steps of retrival
+/**
+ Retrieve records ends with a given beginning from the result of a join.
- DESCRIPTION
For a given partial join record consisting of records from the tables
preceding the table join_tab in the execution plan, the function
retrieves all matching full records from the result set and
send them to the result set stream.
- NOTES
+ @note
The function effectively implements the final (n-k) nested loops
of nested loops join algorithm, where k is the ordinal number of
the join_tab table and n is the total number of tables in the join query.
It performs nested loops joins with all conjunctive predicates from
the where condition pushed as low to the tables as possible.
E.g. for the query
- SELECT * FROM t1,t2,t3
- WHERE t1.a=t2.a AND t2.b=t3.b AND t1.a BETWEEN 5 AND 9
+ @code
+ SELECT * FROM t1,t2,t3
+ WHERE t1.a=t2.a AND t2.b=t3.b AND t1.a BETWEEN 5 AND 9
+ @endcode
the predicate (t1.a BETWEEN 5 AND 9) will be pushed to table t1,
given the selected plan prescribes to nest retrievals of the
joined tables in the following order: t1,t2,t3.
A pushed down predicate are attached to the table which it pushed to,
- at the field select_cond.
+ at the field join_tab->select_cond.
When executing a nested loop of level k the function runs through
the rows of 'join_tab' and for each row checks the pushed condition
attached to the table.
@@ -10574,9 +10958,11 @@ sub_select_cache(JOIN *join,JOIN_TAB *join_tab,bool end_of_records)
the execution plan. In this case the pushed down predicates can be
checked only at certain conditions.
Suppose for the query
- SELECT * FROM t1 LEFT JOIN (t2,t3) ON t3.a=t1.a
- WHERE t1>2 AND (t2.b>5 OR t2.b IS NULL)
- the optimizer has chosen a plan with the table order t1,t2,t3.
+ @code
+ SELECT * FROM t1 LEFT JOIN (t2,t3) ON t3.a=t1.a
+ WHERE t1>2 AND (t2.b>5 OR t2.b IS NULL)
+ @endcode
+ the optimizer has chosen a plan with the table order t1,t2,t3.
The predicate P1=t1>2 will be pushed down to the table t1, while the
predicate P2=(t2.b>5 OR t2.b IS NULL) will be attached to the table
t2. But the second predicate can not be unconditionally tested right
@@ -10602,16 +10988,18 @@ sub_select_cache(JOIN *join,JOIN_TAB *join_tab,bool end_of_records)
is complemented by nulls for t2 and t3. Then the pushed down predicates
are checked for the composed row almost in the same way as it had
been done for the first row with a match. The only difference is
- the predicates from on expressions are not checked.
+ the predicates from on expressions are not checked.
- IMPLEMENTATION
+ @par
+ @b IMPLEMENTATION
+ @par
The function forms output rows for a current partial join of k
tables tables recursively.
For each partial join record ending with a certain row from
join_tab it calls sub_select that builds all possible matching
tails from the result set.
To be able check predicates conditionally items of the class
- Item_func_trig_cond are employed.
+ Item_func_trig_cond are employed.
An object of this class is constructed from an item of class COND
and a pointer to a guarding boolean variable.
When the value of the guard variable is true the value of the object
@@ -10620,11 +11008,13 @@ sub_select_cache(JOIN *join,JOIN_TAB *join_tab,bool end_of_records)
To carry out a return to a nested loop level of join table t the pointer
to t is remembered in the field 'return_tab' of the join structure.
Consider the following query:
- SELECT * FROM t1,
- LEFT JOIN
- (t2, t3 LEFT JOIN (t4,t5) ON t5.a=t3.a)
- ON t4.a=t2.a
- WHERE (t2.b=5 OR t2.b IS NULL) AND (t4.b=2 OR t4.b IS NULL)
+ @code
+ SELECT * FROM t1,
+ LEFT JOIN
+ (t2, t3 LEFT JOIN (t4,t5) ON t5.a=t3.a)
+ ON t4.a=t2.a
+ WHERE (t2.b=5 OR t2.b IS NULL) AND (t4.b=2 OR t4.b IS NULL)
+ @endcode
Suppose the chosen execution plan dictates the order t1,t2,t3,t4,t5
and suppose for a given joined rows from tables t1,t2,t3 there are
no rows in the result set yet.
@@ -10639,11 +11029,18 @@ sub_select_cache(JOIN *join,JOIN_TAB *join_tab,bool end_of_records)
Thus, when the first row from t5 with t5.a=t3.a is found
this pointer for t5 is changed from t4 to t2.
- STRUCTURE NOTES
+ @par
+ @b STRUCTURE @b NOTES
+ @par
join_tab->first_unmatched points always backwards to the first inner
table of the embedding nested join, if any.
- RETURN
+ @param join pointer to the structure providing all context info for
+ the query
+ @param join_tab the first next table of the execution plan to be retrieved
+ @param end_records true when we need to perform final steps of retrival
+
+ @return
return one of enum_nested_loop_state, except NESTED_LOOP_NO_MORE_ROWS.
*/
@@ -10656,7 +11053,6 @@ sub_select(JOIN *join,JOIN_TAB *join_tab,bool end_of_records)
int error;
enum_nested_loop_state rc;
- my_bool *report_error= &(join->thd->net.report_error);
READ_RECORD *info= &join_tab->read_record;
if (join->resume_nested_loop)
@@ -10688,13 +11084,13 @@ sub_select(JOIN *join,JOIN_TAB *join_tab,bool end_of_records)
join->thd->row_count= 0;
error= (*join_tab->read_first_record)(join_tab);
- rc= evaluate_join_record(join, join_tab, error, report_error);
+ rc= evaluate_join_record(join, join_tab, error);
}
while (rc == NESTED_LOOP_OK)
{
error= info->read_record(info);
- rc= evaluate_join_record(join, join_tab, error, report_error);
+ rc= evaluate_join_record(join, join_tab, error);
}
if (rc == NESTED_LOOP_NO_MORE_ROWS &&
@@ -10707,10 +11103,9 @@ sub_select(JOIN *join,JOIN_TAB *join_tab,bool end_of_records)
}
-/*
+/**
Process one record of the nested loop join.
- DESCRIPTION
This function will evaluate parts of WHERE/ON clauses that are
applicable to the partial record on hand and in case of success
submit this record to the next level of the nested loop.
@@ -10718,13 +11113,13 @@ sub_select(JOIN *join,JOIN_TAB *join_tab,bool end_of_records)
static enum_nested_loop_state
evaluate_join_record(JOIN *join, JOIN_TAB *join_tab,
- int error, my_bool *report_error)
+ int error)
{
bool not_used_in_distinct=join_tab->not_used_in_distinct;
ha_rows found_records=join->found_records;
COND *select_cond= join_tab->select_cond;
- if (error > 0 || (*report_error)) // Fatal error
+ if (error > 0 || (join->thd->is_error())) // Fatal error
return NESTED_LOOP_ERROR;
if (error < 0)
return NESTED_LOOP_NO_MORE_ROWS;
@@ -10834,8 +11229,9 @@ evaluate_join_record(JOIN *join, JOIN_TAB *join_tab,
}
-/*
- DESCRIPTION
+/**
+
+ @details
Construct a NULL complimented partial join record and feed it to the next
level of the nested loop. This function is used in case we have
an OUTER join and no matching record was found.
@@ -10979,7 +11375,7 @@ flush_cached_records(JOIN *join,JOIN_TAB *join_tab,bool skip_last)
Returns -1 if row was not found, 0 if row was found and 1 on errors
*****************************************************************************/
-/* Help function when we get some an error from the table handler */
+/** Help function when we get some an error from the table handler. */
int report_error(TABLE *table, int error)
{
@@ -10994,7 +11390,7 @@ int report_error(TABLE *table, int error)
*/
if (error != HA_ERR_LOCK_DEADLOCK && error != HA_ERR_LOCK_WAIT_TIMEOUT)
sql_print_error("Got error %d when reading table '%s'",
- error, table->s->path);
+ error, table->s->path.str);
table->file->print_error(error,MYF(0));
return 1;
}
@@ -11004,9 +11400,10 @@ int safe_index_read(JOIN_TAB *tab)
{
int error;
TABLE *table= tab->table;
- if ((error=table->file->index_read(table->record[0],
- tab->ref.key_buff,
- tab->ref.key_length, HA_READ_KEY_EXACT)))
+ if ((error=table->file->index_read_map(table->record[0],
+ tab->ref.key_buff,
+ make_prev_keypart_map(tab->ref.key_parts),
+ HA_READ_KEY_EXACT)))
return report_error(table, error);
return 0;
}
@@ -11029,13 +11426,14 @@ join_read_const_table(JOIN_TAB *tab, POSITION *pos)
tab->info="const row not found";
/* 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)
DBUG_RETURN(error);
}
}
else
{
- if (!table->key_read && table->used_keys.is_set(tab->ref.key) &&
+ if (!table->key_read && table->covering_keys.is_set(tab->ref.key) &&
!table->no_keyread &&
(int) table->reginfo.lock_type <= (int) TL_READ_HIGH_PRIORITY)
{
@@ -11054,6 +11452,7 @@ join_read_const_table(JOIN_TAB *tab, POSITION *pos)
tab->info="unique row not found";
/* 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)
DBUG_RETURN(error);
}
@@ -11115,17 +11514,17 @@ join_read_system(JOIN_TAB *tab)
}
-/*
- Read a table when there is at most one matching row
+/**
+ Read a table when there is at most one matching row.
- SYNOPSIS
- join_read_const()
- tab Table to read
+ @param tab Table to read
- RETURN
+ @retval
0 Row was found
- -1 Row was not found
- 1 Got an error (other than row not found) during read
+ @retval
+ -1 Row was not found
+ @retval
+ 1 Got an error (other than row not found) during read
*/
static int
@@ -11136,20 +11535,21 @@ join_read_const(JOIN_TAB *tab)
if (table->status & STATUS_GARBAGE) // If first read
{
table->status= 0;
- if (cp_buffer_from_ref(tab->join->thd, &tab->ref))
+ if (cp_buffer_from_ref(tab->join->thd, table, &tab->ref))
error=HA_ERR_KEY_NOT_FOUND;
else
{
- error=table->file->index_read_idx(table->record[0],tab->ref.key,
- (byte*) tab->ref.key_buff,
- tab->ref.key_length,HA_READ_KEY_EXACT);
+ error=table->file->index_read_idx_map(table->record[0],tab->ref.key,
+ (uchar*) tab->ref.key_buff,
+ make_prev_keypart_map(tab->ref.key_parts),
+ HA_READ_KEY_EXACT);
}
if (error)
{
table->status= STATUS_NOT_FOUND;
mark_as_null_row(tab->table);
empty_record(table);
- if (error != HA_ERR_KEY_NOT_FOUND)
+ if (error != HA_ERR_KEY_NOT_FOUND && error != HA_ERR_END_OF_FILE)
return report_error(table, error);
return -1;
}
@@ -11172,7 +11572,9 @@ join_read_key(JOIN_TAB *tab)
TABLE *table= tab->table;
if (!table->file->inited)
- table->file->ha_index_init(tab->ref.key);
+ {
+ table->file->ha_index_init(tab->ref.key, tab->sorted);
+ }
if (cmp_buffer_with_ref(tab) ||
(table->status & (STATUS_GARBAGE | STATUS_NO_PARENT | STATUS_NULL_ROW)))
{
@@ -11181,10 +11583,11 @@ join_read_key(JOIN_TAB *tab)
table->status=STATUS_NOT_FOUND;
return -1;
}
- error=table->file->index_read(table->record[0],
- tab->ref.key_buff,
- tab->ref.key_length,HA_READ_KEY_EXACT);
- if (error && error != HA_ERR_KEY_NOT_FOUND)
+ error=table->file->index_read_map(table->record[0],
+ tab->ref.key_buff,
+ make_prev_keypart_map(tab->ref.key_parts),
+ HA_READ_KEY_EXACT);
+ if (error && error != HA_ERR_KEY_NOT_FOUND && error != HA_ERR_END_OF_FILE)
return report_error(table, error);
}
table->null_row=0;
@@ -11219,8 +11622,8 @@ join_read_always_key(JOIN_TAB *tab)
/* Initialize the index first */
if (!table->file->inited)
- table->file->ha_index_init(tab->ref.key);
-
+ table->file->ha_index_init(tab->ref.key, tab->sorted);
+
/* Perform "Late NULLs Filtering" (see internals manual for explanations) */
for (uint i= 0 ; i < tab->ref.key_parts ; i++)
{
@@ -11228,13 +11631,14 @@ join_read_always_key(JOIN_TAB *tab)
return -1;
}
- if (cp_buffer_from_ref(tab->join->thd, &tab->ref))
+ if (cp_buffer_from_ref(tab->join->thd, table, &tab->ref))
return -1;
- if ((error=table->file->index_read(table->record[0],
- tab->ref.key_buff,
- tab->ref.key_length,HA_READ_KEY_EXACT)))
+ if ((error=table->file->index_read_map(table->record[0],
+ tab->ref.key_buff,
+ make_prev_keypart_map(tab->ref.key_parts),
+ HA_READ_KEY_EXACT)))
{
- if (error != HA_ERR_KEY_NOT_FOUND)
+ if (error != HA_ERR_KEY_NOT_FOUND && error != HA_ERR_END_OF_FILE)
return report_error(table, error);
return -1; /* purecov: inspected */
}
@@ -11242,9 +11646,9 @@ join_read_always_key(JOIN_TAB *tab)
}
-/*
+/**
This function is used when optimizing away ORDER BY in
- SELECT * FROM t1 WHERE a=1 ORDER BY a DESC,b DESC
+ SELECT * FROM t1 WHERE a=1 ORDER BY a DESC,b DESC.
*/
static int
@@ -11254,14 +11658,14 @@ join_read_last_key(JOIN_TAB *tab)
TABLE *table= tab->table;
if (!table->file->inited)
- table->file->ha_index_init(tab->ref.key);
- if (cp_buffer_from_ref(tab->join->thd, &tab->ref))
+ table->file->ha_index_init(tab->ref.key, tab->sorted);
+ if (cp_buffer_from_ref(tab->join->thd, table, &tab->ref))
return -1;
- if ((error=table->file->index_read_last(table->record[0],
- tab->ref.key_buff,
- tab->ref.key_length)))
+ if ((error=table->file->index_read_last_map(table->record[0],
+ tab->ref.key_buff,
+ make_prev_keypart_map(tab->ref.key_parts))))
{
- if (error != HA_ERR_KEY_NOT_FOUND)
+ if (error != HA_ERR_KEY_NOT_FOUND && error != HA_ERR_END_OF_FILE)
return report_error(table, error);
return -1; /* purecov: inspected */
}
@@ -11360,7 +11764,7 @@ join_read_first(JOIN_TAB *tab)
{
int error;
TABLE *table=tab->table;
- if (!table->key_read && table->used_keys.is_set(tab->index) &&
+ if (!table->key_read && table->covering_keys.is_set(tab->index) &&
!table->no_keyread)
{
table->key_read=1;
@@ -11373,7 +11777,7 @@ join_read_first(JOIN_TAB *tab)
tab->read_record.index=tab->index;
tab->read_record.record=table->record[0];
if (!table->file->inited)
- table->file->ha_index_init(tab->index);
+ table->file->ha_index_init(tab->index, tab->sorted);
if ((error=tab->table->file->index_first(tab->table->record[0])))
{
if (error != HA_ERR_KEY_NOT_FOUND && error != HA_ERR_END_OF_FILE)
@@ -11399,7 +11803,7 @@ join_read_last(JOIN_TAB *tab)
{
TABLE *table=tab->table;
int error;
- if (!table->key_read && table->used_keys.is_set(tab->index) &&
+ if (!table->key_read && table->covering_keys.is_set(tab->index) &&
!table->no_keyread)
{
table->key_read=1;
@@ -11412,7 +11816,7 @@ join_read_last(JOIN_TAB *tab)
tab->read_record.index=tab->index;
tab->read_record.record=table->record[0];
if (!table->file->inited)
- table->file->ha_index_init(tab->index);
+ table->file->ha_index_init(tab->index, 1);
if ((error= tab->table->file->index_last(tab->table->record[0])))
return report_error(table, error);
return 0;
@@ -11436,10 +11840,11 @@ join_ft_read_first(JOIN_TAB *tab)
TABLE *table= tab->table;
if (!table->file->inited)
- table->file->ha_index_init(tab->ref.key);
+ table->file->ha_index_init(tab->ref.key, 1);
#if NOT_USED_YET
- if (cp_buffer_from_ref(tab->join->thd, &tab->ref)) // as ft-key doesn't use store_key's
- return -1; // see also FT_SELECT::init()
+ /* 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();
@@ -11458,8 +11863,8 @@ join_ft_read_next(READ_RECORD *info)
}
-/*
- Reading of key with key reference and one part that may be NULL
+/**
+ Reading of key with key reference and one part that may be NULL.
*/
int
@@ -11546,7 +11951,7 @@ end_send(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
if ((join->tables == 1) && !join->tmp_table && !join->sort_and_group
&& !join->send_group_parts && !join->having && !jt->select_cond &&
!(jt->select && jt->select->quick) &&
- !(jt->table->file->table_flags() & HA_NOT_EXACT_COUNT) &&
+ (jt->table->file->ha_table_flags() & HA_STATS_RECORDS_IS_EXACT) &&
(jt->ref.key < 0))
{
/* Join over all rows in table; Return number of found rows */
@@ -11562,7 +11967,7 @@ end_send(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
else
{
table->file->info(HA_STATUS_VARIABLE);
- join->send_records = table->file->records;
+ join->send_records= table->file->stats.records;
}
}
else
@@ -11735,7 +12140,7 @@ end_write(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
if (item->maybe_null)
{
Field *field=item->get_tmp_table_field();
- field->ptr[-1]= (byte) (field->is_null() ? 1 : 0);
+ field->ptr[-1]= (uchar) (field->is_null() ? 1 : 0);
}
}
}
@@ -11744,10 +12149,9 @@ end_write(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
{
int error;
join->found_records++;
- if ((error=table->file->write_row(table->record[0])))
+ if ((error=table->file->ha_write_row(table->record[0])))
{
- if (error == HA_ERR_FOUND_DUPP_KEY ||
- error == HA_ERR_FOUND_DUPP_UNIQUE)
+ if (!table->file->is_fatal_error(error, HA_CHECK_DUP))
goto end;
if (create_myisam_from_heap(join->thd, table, &join->tmp_table_param,
error,1))
@@ -11769,8 +12173,8 @@ end:
DBUG_RETURN(NESTED_LOOP_OK);
}
-/* Group by searching after group record and updating it if possible */
/* ARGSUSED */
+/** Group by searching after group record and updating it if possible. */
static enum_nested_loop_state
end_update(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
@@ -11800,14 +12204,15 @@ end_update(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
if (item->maybe_null)
group->buff[-1]= (char) group->field->is_null();
}
- if (!table->file->index_read(table->record[1],
- join->tmp_table_param.group_buff,0,
- HA_READ_KEY_EXACT))
+ if (!table->file->index_read_map(table->record[1],
+ join->tmp_table_param.group_buff,
+ HA_WHOLE_KEY,
+ HA_READ_KEY_EXACT))
{ /* Update old record */
restore_record(table,record[1]);
update_tmptable_sum_func(join->sum_funcs,table);
- if ((error=table->file->update_row(table->record[1],
- table->record[0])))
+ if ((error=table->file->ha_update_row(table->record[1],
+ table->record[0])))
{
table->file->print_error(error,MYF(0)); /* purecov: inspected */
DBUG_RETURN(NESTED_LOOP_ERROR); /* purecov: inspected */
@@ -11830,13 +12235,13 @@ end_update(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
}
init_tmptable_sum_functions(join->sum_funcs);
copy_funcs(join->tmp_table_param.items_to_copy);
- if ((error=table->file->write_row(table->record[0])))
+ if ((error=table->file->ha_write_row(table->record[0])))
{
if (create_myisam_from_heap(join->thd, table, &join->tmp_table_param,
error, 0))
DBUG_RETURN(NESTED_LOOP_ERROR); // Not a table_is_full error
/* Change method to update rows */
- table->file->ha_index_init(0);
+ table->file->ha_index_init(0, 0);
join->join_tab[join->tables-1].next_select=end_unique_update;
}
join->send_records++;
@@ -11844,7 +12249,7 @@ end_update(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
}
-/* Like end_update, but this is done with unique constraints instead of keys */
+/** Like end_update, but this is done with unique constraints instead of keys. */
static enum_nested_loop_state
end_unique_update(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
@@ -11866,7 +12271,7 @@ end_unique_update(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
copy_fields(&join->tmp_table_param); // Groups are copied twice.
copy_funcs(join->tmp_table_param.items_to_copy);
- if (!(error=table->file->write_row(table->record[0])))
+ if (!(error=table->file->ha_write_row(table->record[0])))
join->send_records++; // New group
else
{
@@ -11875,15 +12280,15 @@ end_unique_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 */
}
- if (table->file->rnd_pos(table->record[1],table->file->dupp_ref))
+ if (table->file->rnd_pos(table->record[1],table->file->dup_ref))
{
table->file->print_error(error,MYF(0)); /* purecov: inspected */
DBUG_RETURN(NESTED_LOOP_ERROR); /* purecov: inspected */
}
restore_record(table,record[1]);
update_tmptable_sum_func(join->sum_funcs,table);
- if ((error=table->file->update_row(table->record[1],
- table->record[0])))
+ if ((error=table->file->ha_update_row(table->record[1],
+ table->record[0])))
{
table->file->print_error(error,MYF(0)); /* purecov: inspected */
DBUG_RETURN(NESTED_LOOP_ERROR); /* purecov: inspected */
@@ -11926,7 +12331,7 @@ end_write_group(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
join->sum_funcs_end[send_group_parts]);
if (!join->having || join->having->val_int())
{
- int error= table->file->write_row(table->record[0]);
+ int error= table->file->ha_write_row(table->record[0]);
if (error && create_myisam_from_heap(join->thd, table,
&join->tmp_table_param,
error, 0))
@@ -11975,7 +12380,10 @@ end_write_group(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
in sorted order.
*****************************************************************************/
-/* Return 1 if right_item is used removable reference key on left_item */
+/**
+ @return
+ 1 if right_item is used removable reference key on left_item
+*/
static bool test_if_ref(Item_field *left_item,Item *right_item)
{
@@ -12002,7 +12410,7 @@ static bool test_if_ref(Item_field *left_item,Item *right_item)
if (field->binary() &&
field->real_type() != MYSQL_TYPE_STRING &&
field->real_type() != MYSQL_TYPE_VARCHAR &&
- (field->type() != FIELD_TYPE_FLOAT || field->decimals() == 0))
+ (field->type() != MYSQL_TYPE_FLOAT || field->decimals() == 0))
{
return !store_val_in_field(field, right_item, CHECK_FIELD_WARN);
}
@@ -12130,23 +12538,27 @@ part_of_refkey(TABLE *table,Field *field)
/**
- Test if a key can be used to resolve ORDER BY
-
- used_key_parts is set to correct key parts used if return value != 0
- (On other cases, used_key_part may be changed).
- Note that the value may actually be greater than the number of index
- key parts. This can happen for storage engines that have the primary
- key parts as a suffix for every secondary key.
-
- @param order Sort order
- @param table Table to sort
- @param idx Index to check
- @param[out] used_key_parts Return value for used key parts.
-
- @return indication if the key can be used for sorting
- @retval 1 key can be used for reading data in order.
- @retval 0 Key can't be used
- @retval -1 Reverse read on the key can be used
+ Test if one can use the key to resolve ORDER BY.
+
+ @param order Sort order
+ @param table Table to sort
+ @param idx Index to check
+ @param used_key_parts Return value for used key parts.
+
+
+ @note
+ used_key_parts is set to correct key parts used if return value != 0
+ (On other cases, used_key_part may be changed)
+ Note that the value may actually be greater than the number of index
+ key parts. This can happen for storage engines that have the primary
+ key parts as a suffix for every secondary key.
+
+ @retval
+ 1 key is ok.
+ @retval
+ 0 Key can't be used
+ @retval
+ -1 Reverse key can be used
*/
static int test_if_order_by_key(ORDER *order, TABLE *table, uint idx,
@@ -12180,7 +12592,7 @@ static int test_if_order_by_key(ORDER *order, TABLE *table, uint idx,
the primary key as a suffix.
*/
if (!on_primary_key &&
- (table->file->table_flags() & HA_PRIMARY_KEY_IN_READ_INDEX) &&
+ (table->file->ha_table_flags() & HA_PRIMARY_KEY_IN_READ_INDEX) &&
table->s->primary_key != MAX_KEY)
{
on_primary_key= TRUE;
@@ -12258,20 +12670,19 @@ uint find_shortest_key(TABLE *table, const key_map *usable_keys)
return best;
}
-/*
+/**
Test if a second key is the subkey of the first one.
- SYNOPSIS
- is_subkey()
- key_part First key parts
- ref_key_part Second key parts
- ref_key_part_end Last+1 part of the second key
+ @param key_part First key parts
+ @param ref_key_part Second key parts
+ @param ref_key_part_end Last+1 part of the second key
- NOTE
+ @note
Second key MUST be shorter than the first one.
- RETURN
+ @retval
1 is a subkey
+ @retval
0 no sub key
*/
@@ -12285,17 +12696,16 @@ is_subkey(KEY_PART_INFO *key_part, KEY_PART_INFO *ref_key_part,
return 1;
}
-/*
- Test if we can use one of the 'usable_keys' instead of 'ref' key for sorting
+/**
+ Test if we can use one of the 'usable_keys' instead of 'ref' key
+ for sorting.
- SYNOPSIS
- test_if_subkey()
- ref Number of key, used for WHERE clause
- usable_keys Keys for testing
+ @param ref Number of key, used for WHERE clause
+ @param usable_keys Keys for testing
- RETURN
- MAX_KEY If we can't use other key
- the number of found key Otherwise
+ @return
+ - MAX_KEY If we can't use other key
+ - the number of found key Otherwise
*/
static uint
@@ -12326,24 +12736,19 @@ test_if_subkey(ORDER *order, TABLE *table, uint ref, uint ref_key_parts,
}
-/*
+/**
Check if GROUP BY/DISTINCT can be optimized away because the set is
already known to be distinct.
-
- SYNOPSIS
- list_contains_unique_index ()
- table The table to operate on.
- find_func function to iterate over the list and search
- for a field
- DESCRIPTION
- Used in removing the GROUP BY/DISTINCT of the following types of
- statements:
- SELECT [DISTINCT] <unique_key_cols>... FROM <single_table_ref>
- [GROUP BY <unique_key_cols>,...]
+ Used in removing the GROUP BY/DISTINCT of the following types of
+ statements:
+ @code
+ SELECT [DISTINCT] <unique_key_cols>... FROM <single_table_ref>
+ [GROUP BY <unique_key_cols>,...]
+ @endcode
If (a,b,c is distinct)
- then <any combination of a,b,c>,{whatever} is also distinct
+ then <any combination of a,b,c>,{whatever} is also distinct
This function checks if all the key parts of any of the unique keys
of the table are referenced by a list : either the select list
@@ -12352,9 +12757,14 @@ test_if_subkey(ORDER *order, TABLE *table, uint ref, uint ref_key_parts,
If the above holds and the key parts cannot contain NULLs then we
can safely remove the GROUP BY/DISTINCT,
as no result set can be more distinct than an unique key.
-
- RETURN VALUE
+
+ @param table The table to operate on.
+ @param find_func function to iterate over the list and search
+ for a field
+
+ @retval
1 found
+ @retval
0 not found.
*/
@@ -12387,20 +12797,17 @@ list_contains_unique_index(TABLE *table,
}
-/*
+/**
Helper function for list_contains_unique_index.
Find a field reference in a list of ORDER structures.
-
- SYNOPSIS
- find_field_in_order_list ()
- field The field to search for.
- data ORDER *.The list to search in
-
- DESCRIPTION
- Finds a direct reference of the Field in the list.
-
- RETURN VALUE
+ Finds a direct reference of the Field in the list.
+
+ @param field The field to search for.
+ @param data ORDER *.The list to search in
+
+ @retval
1 found
+ @retval
0 not found.
*/
@@ -12423,20 +12830,17 @@ find_field_in_order_list (Field *field, void *data)
}
-/*
+/**
Helper function for list_contains_unique_index.
Find a field reference in a dynamic list of Items.
-
- SYNOPSIS
- find_field_in_item_list ()
- field in The field to search for.
- data in List<Item> *.The list to search in
-
- DESCRIPTION
- Finds a direct reference of the Field in the list.
-
- RETURN VALUE
+ Finds a direct reference of the Field in the list.
+
+ @param[in] field The field to search for.
+ @param[in] data List<Item> *.The list to search in
+
+ @retval
1 found
+ @retval
0 not found.
*/
@@ -12461,26 +12865,36 @@ find_field_in_item_list (Field *field, void *data)
}
-/*
+/**
Test if we can skip the ORDER BY by using an index.
If we can use an index, the JOIN_TAB / tab->select struct
is changed to use the index.
- Return:
- 0 We have to use filesort to do the sorting
- 1 We can use an index.
+ The index must cover all fields in <order>, or it will not be considered.
+
+ @todo
+ - sergeyp: Results of all index merge selects actually are ordered
+ by clustered PK values.
+
+ @retval
+ 0 We have to use filesort to do the sorting
+ @retval
+ 1 We can use an index.
*/
static bool
test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,ha_rows select_limit,
- bool no_changes)
+ bool no_changes, key_map *map)
{
int ref_key;
uint ref_key_parts;
+ int order_direction;
+ uint used_key_parts;
TABLE *table=tab->table;
SQL_SELECT *select=tab->select;
key_map usable_keys;
+ QUICK_SELECT_I *save_quick= 0;
DBUG_ENTER("test_if_skip_sort_order");
LINT_INIT(ref_key_parts);
@@ -12488,8 +12902,7 @@ test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,ha_rows select_limit,
Keys disabled by ALTER TABLE ... DISABLE KEYS should have already
been taken into account.
*/
- usable_keys= table->keys_in_use_for_query;
- DBUG_ASSERT(usable_keys.is_subset(table->s->keys_in_use));
+ usable_keys= *map;
for (ORDER *tmp_order=order; tmp_order ; tmp_order=tmp_order->next)
{
@@ -12516,6 +12929,7 @@ test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,ha_rows select_limit,
else if (select && select->quick) // Range found by opt_range
{
int quick_type= select->quick->get_type();
+ save_quick= select->quick;
/*
assume results are not ordered when index merge is used
TODO: sergeyp: Results of all index merge selects actually are ordered
@@ -12535,8 +12949,6 @@ test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,ha_rows select_limit,
/*
We come here when there is a REF key.
*/
- int order_direction;
- uint used_key_parts;
if (!usable_keys.is_set(ref_key))
{
/*
@@ -12547,8 +12959,8 @@ test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,ha_rows select_limit,
If using index only read, only consider other possible index only
keys
*/
- if (table->used_keys.is_set(ref_key))
- usable_keys.intersect(table->used_keys);
+ if (table->covering_keys.is_set(ref_key))
+ usable_keys.intersect(table->covering_keys);
if ((new_ref_key= test_if_subkey(order, table, ref_key, ref_key_parts,
&usable_keys)) < MAX_KEY)
{
@@ -12597,72 +13009,50 @@ test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,ha_rows select_limit,
}
/* Check if we get the rows in requested sorted order by using the key */
if (usable_keys.is_set(ref_key) &&
- (order_direction = test_if_order_by_key(order,table,ref_key,
- &used_key_parts)))
- {
- if (order_direction == -1) // If ORDER BY ... DESC
- {
- if (select && select->quick)
- {
- /*
- Don't reverse the sort order, if it's already done.
- (In some cases test_if_order_by_key() can be called multiple times
- */
- if (!select->quick->reverse_sorted())
- {
- int quick_type= select->quick->get_type();
- if (quick_type == QUICK_SELECT_I::QS_TYPE_INDEX_MERGE ||
- quick_type == QUICK_SELECT_I::QS_TYPE_ROR_INTERSECT ||
- quick_type == QUICK_SELECT_I::QS_TYPE_ROR_UNION ||
- quick_type == QUICK_SELECT_I::QS_TYPE_GROUP_MIN_MAX)
- DBUG_RETURN(0); // Use filesort
-
- /* ORDER BY range_key DESC */
- QUICK_SELECT_DESC *tmp=new QUICK_SELECT_DESC((QUICK_RANGE_SELECT*)(select->quick),
- used_key_parts);
- if (!tmp || tmp->error)
- {
- delete tmp;
- DBUG_RETURN(0); // Reverse sort not supported
- }
- select->quick=tmp;
- }
- DBUG_RETURN(1);
- }
- if (tab->ref.key_parts <= used_key_parts)
- {
- /*
- SELECT * FROM t1 WHERE a=1 ORDER BY a DESC,b DESC
-
- Use a traversal function that starts by reading the last row
- with key part (A) and then traverse the index backwards.
- */
- tab->read_first_record= join_read_last_key;
- tab->read_record.read_record= join_read_prev_same;
- /* fall through */
- }
- }
- else if (select && select->quick)
- select->quick->sorted= 1;
- DBUG_RETURN(1); /* No need to sort */
- }
+ (order_direction= test_if_order_by_key(order,table,ref_key,
+ &used_key_parts)))
+ goto check_reverse_order;
}
- else
{
- /* check if we can use a key to resolve the group */
- /* Tables using JT_NEXT are handled here */
+ /*
+ 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;
+ int best_key_direction;
+ ha_rows best_records;
+ double read_time;
+ int best_key= -1;
+ bool is_best_covering= FALSE;
+ double fanout= 1;
+ 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 >= table->file->records)
+ if (select_limit >= table_records)
{
+ /*
+ filesort() and join cache are usually faster than reading in
+ index order and not using join cache
+ */
+ if (tab->type == JT_ALL && tab->join->tables > tab->join->const_tables + 1)
+ DBUG_RETURN(0);
keys= *table->file->keys_to_use_for_scanning();
- keys.merge(table->used_keys);
+ keys.merge(table->covering_keys);
/*
We are adding here also the index specified in FORCE INDEX clause,
@@ -12670,38 +13060,261 @@ test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,ha_rows select_limit,
This is to allow users to use index in ORDER BY.
*/
if (table->force_index)
- keys.merge(table->keys_in_use_for_query);
+ 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 && 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->tables; i++)
+ fanout*= join->best_positions[i].records_read; // fanout is always >= 1
+
for (nr=0; nr < table->s->keys ; nr++)
{
- uint not_used;
- if (keys.is_set(nr))
+ int direction;
+ if (keys.is_set(nr) &&
+ (direction= test_if_order_by_key(order, table, nr, &used_key_parts)))
+ {
+ bool is_covering= table->covering_keys.is_set(nr) ||
+ nr == table->s->primary_key &&
+ table->file->primary_key_is_clustered();
+
+ /*
+ 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)
+ {
+ rec_per_key= keyinfo->rec_per_key[used_key_parts-1];
+ 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);
+ }
+ /*
+ 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 (is_covering ||
+ ref_key < 0 && (group || table->force_index) ||
+ 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))
+ {
+ best_key= nr;
+ best_key_parts= keyinfo->key_parts;
+ best_records= quick_records;
+ is_best_covering= is_covering;
+ best_key_direction= direction;
+ }
+ }
+ }
+ }
+ }
+ if (best_key >= 0)
+ {
+ bool quick_created= FALSE;
+ if (table->quick_keys.is_set(best_key) && best_key != ref_key)
{
- int flag;
- if ((flag= test_if_order_by_key(order, table, nr, &not_used)))
+ key_map map;
+ map.clear_all(); // Force the creation of quick select
+ map.set_bit(best_key); // only best_key.
+ quick_created=
+ select->test_quick_select(join->thd, map, 0,
+ join->select_options & OPTION_FOUND_ROWS ?
+ HA_POS_ERROR :
+ join->unit->select_limit_cnt,
+ 0) > 0;
+ }
+ if (!no_changes)
+ {
+ if (!quick_created)
{
- if (!no_changes)
- {
- tab->index=nr;
- tab->read_first_record= (flag > 0 ? join_read_first:
- join_read_last);
- tab->type=JT_NEXT; // Read with index_first(), index_next()
- if (table->used_keys.is_set(nr))
- {
- table->key_read=1;
- table->file->extra(HA_EXTRA_KEYREAD);
- }
- }
- DBUG_RETURN(1);
+ tab->index= best_key;
+ tab->read_first_record= best_key_direction > 0 ?
+ join_read_first:join_read_last;
+ tab->type=JT_NEXT; // Read with index_first(), index_next()
+ if (select && select->quick)
+ {
+ delete select->quick;
+ select->quick= 0;
+ }
+ if (table->covering_keys.is_set(best_key))
+ {
+ table->key_read=1;
+ table->file->extra(HA_EXTRA_KEYREAD);
+ }
+ else if (table->key_read)
+ {
+ /*
+ Clear the covering key read flags that might have been
+ previously set for some key other than the current best_key.
+ */
+ table->key_read= 0;
+ table->file->extra(HA_EXTRA_NO_KEYREAD);
+ }
+
+ table->file->ha_index_or_rnd_end();
+ if (join->select_options & SELECT_DESCRIBE)
+ {
+ tab->ref.key= -1;
+ tab->ref.key_parts= 0;
+ if (select_limit < table_records)
+ tab->limit= select_limit;
+ }
+ }
+ else if (tab->type != JT_ALL)
+ {
+ /*
+ We're about to use a quick access to the table.
+ We need to change the access method so as the quick access
+ method is actually used.
+ */
+ DBUG_ASSERT(tab->select->quick);
+ tab->type=JT_ALL;
+ tab->use_quick=1;
+ tab->ref.key= -1;
+ tab->ref.key_parts=0; // Don't use ref key.
+ tab->read_first_record= join_init_read_record;
+ if (tab->is_using_loose_index_scan())
+ join->tmp_table_param.precomputed_group_by= TRUE;
+ /*
+ TODO: update the number of records in join->best_positions[tablenr]
+ */
+ }
+ }
+ used_key_parts= best_key_parts;
+ order_direction= best_key_direction;
+ }
+ else
+ DBUG_RETURN(0);
+ }
+
+check_reverse_order:
+ if (order_direction == -1) // If ORDER BY ... DESC
+ {
+ if (select && select->quick)
+ {
+ /*
+ Don't reverse the sort order, if it's already done.
+ (In some cases test_if_order_by_key() can be called multiple times
+ */
+ if (!select->quick->reverse_sorted())
+ {
+ QUICK_SELECT_DESC *tmp;
+ int quick_type= select->quick->get_type();
+ if (quick_type == QUICK_SELECT_I::QS_TYPE_INDEX_MERGE ||
+ quick_type == QUICK_SELECT_I::QS_TYPE_ROR_INTERSECT ||
+ quick_type == QUICK_SELECT_I::QS_TYPE_ROR_UNION ||
+ quick_type == QUICK_SELECT_I::QS_TYPE_GROUP_MIN_MAX)
+ {
+ tab->limit= 0;
+ select->quick= save_quick;
+ DBUG_RETURN(0); // Use filesort
+ }
+
+ /* ORDER BY range_key DESC */
+ tmp= new QUICK_SELECT_DESC((QUICK_RANGE_SELECT*)(select->quick),
+ used_key_parts);
+ if (!tmp || tmp->error)
+ {
+ delete tmp;
+ select->quick= save_quick;
+ tab->limit= 0;
+ DBUG_RETURN(0); // Reverse sort not supported
}
+ select->quick=tmp;
}
}
+ else if (tab->type != JT_NEXT &&
+ tab->ref.key >= 0 && tab->ref.key_parts <= used_key_parts)
+ {
+ /*
+ SELECT * FROM t1 WHERE a=1 ORDER BY a DESC,b DESC
+
+ Use a traversal function that starts by reading the last row
+ with key part (A) and then traverse the index backwards.
+ */
+ tab->read_first_record= join_read_last_key;
+ tab->read_record.read_record= join_read_prev_same;
+ }
}
- DBUG_RETURN(0); // Can't use index.
+ else if (select && select->quick)
+ select->quick->sorted= 1;
+ DBUG_RETURN(1);
}
@@ -12716,6 +13329,8 @@ test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,ha_rows select_limit,
filesort_limit Max number of rows that needs to be sorted
select_limit Max number of rows in final output
Used to decide if we should use index or not
+ is_order_by true if we are sorting on ORDER BY, false if GROUP BY
+ Used to decide if we should use index or not
IMPLEMENTATION
@@ -12734,7 +13349,8 @@ test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,ha_rows select_limit,
static int
create_sort_index(THD *thd, JOIN *join, ORDER *order,
- ha_rows filesort_limit, ha_rows select_limit)
+ ha_rows filesort_limit, ha_rows select_limit,
+ bool is_order_by)
{
uint length= 0;
ha_rows examined_rows;
@@ -12759,7 +13375,9 @@ create_sort_index(THD *thd, JOIN *join, ORDER *order,
!(join->select_options & SELECT_BIG_RESULT) ||
select && select->quick &&
select->quick->get_type() == QUICK_SELECT_I::QS_TYPE_GROUP_MIN_MAX) &&
- test_if_skip_sort_order(tab,order,select_limit,0))
+ test_if_skip_sort_order(tab,order,select_limit,0,
+ is_order_by ? &table->keys_in_use_for_order_by :
+ &table->keys_in_use_for_group_by))
DBUG_RETURN(0);
for (ORDER *ord= join->order; ord; ord= ord->next)
length++;
@@ -12806,14 +13424,14 @@ create_sort_index(THD *thd, JOIN *join, ORDER *order,
/* Fill schema tables with data before filesort if it's necessary */
if ((join->select_lex->options & OPTION_SCHEMA_TABLE) &&
- !thd->lex->describe &&
get_schema_tables_result(join, PROCESSED_BY_CREATE_SORT_INDEX))
goto err;
if (table->s->tmp_table)
table->file->info(HA_STATUS_VARIABLE); // Get record count
table->sort.found_records=filesort(thd, table,join->sortorder, length,
- select, filesort_limit, &examined_rows);
+ select, filesort_limit, 0,
+ &examined_rows);
tab->records= table->sort.found_records; // For SQL_CALC_ROWS
if (select)
{
@@ -12836,18 +13454,18 @@ err:
DBUG_RETURN(-1);
}
-/*
- Add the HAVING criteria to table->select
+#ifdef NOT_YET
+/**
+ Add the HAVING criteria to table->select.
*/
-#ifdef NOT_YET
static bool fix_having(JOIN *join, Item **having)
{
(*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;
- DBUG_EXECUTE("where",print_where(*having,"having"););
+ DBUG_EXECUTE("where",print_where(*having,"having", QT_ORDINARY););
Item* sort_table_cond=make_cond_for_table(*having,used_tables,used_tables);
if (sort_table_cond)
{
@@ -12864,9 +13482,11 @@ static bool fix_having(JOIN *join, Item **having)
table->select_cond=table->select->cond;
table->select_cond->top_level_item();
DBUG_EXECUTE("where",print_where(table->select_cond,
- "select and having"););
+ "select and having",
+ QT_ORDINARY););
*having=make_cond_for_table(*having,~ (table_map) 0,~used_tables);
- DBUG_EXECUTE("where",print_where(*having,"having after make_cond"););
+ DBUG_EXECUTE("where",
+ print_where(*having,"having after make_cond", QT_ORDINARY););
}
return 0;
}
@@ -12939,15 +13559,16 @@ remove_duplicates(JOIN *join, TABLE *entry,List<Item> &fields, Item *having)
DBUG_RETURN(0);
}
Field **first_field=entry->field+entry->s->fields - field_count;
- offset= field_count ?
- entry->field[entry->s->fields - field_count]->offset() : 0;
+ offset= (field_count ?
+ entry->field[entry->s->fields - field_count]->
+ offset(entry->record[0]) : 0);
reclength=entry->s->reclength-offset;
free_io_cache(entry); // Safety
entry->file->info(HA_STATUS_VARIABLE);
- if (entry->s->db_type == DB_TYPE_HEAP ||
+ if (entry->s->db_type() == heap_hton ||
(!entry->s->blob_fields &&
- ((ALIGN_SIZE(reclength) + HASH_OVERHEAD) * entry->file->records <
+ ((ALIGN_SIZE(reclength) + HASH_OVERHEAD) * entry->file->stats.records <
thd->variables.sortbuff_size)))
error=remove_dup_with_hash_index(join->thd, entry,
field_count, first_field,
@@ -12966,7 +13587,7 @@ static int remove_dup_with_compare(THD *thd, TABLE *table, Field **first_field,
{
handler *file=table->file;
char *org_record,*new_record;
- byte *record;
+ uchar *record;
int error;
ulong reclength= table->s->reclength-offset;
DBUG_ENTER("remove_dup_with_compare");
@@ -12994,7 +13615,7 @@ static int remove_dup_with_compare(THD *thd, TABLE *table, Field **first_field,
}
if (having && !having->val_int())
{
- if ((error=file->delete_row(record)))
+ if ((error=file->ha_delete_row(record)))
goto err;
error=file->rnd_next(record);
continue;
@@ -13021,7 +13642,7 @@ static int remove_dup_with_compare(THD *thd, TABLE *table, Field **first_field,
}
if (compare_record(table, first_field) == 0)
{
- if ((error=file->delete_row(record)))
+ if ((error=file->ha_delete_row(record)))
goto err;
}
else if (!found)
@@ -13046,9 +13667,11 @@ err:
}
-/*
- Generate a hash index for each row to quickly find duplicate rows
- Note that this will not work on tables with blobs!
+/**
+ Generate a hash index for each row to quickly find duplicate rows.
+
+ @note
+ Note that this will not work on tables with blobs!
*/
static int remove_dup_with_hash_index(THD *thd, TABLE *table,
@@ -13057,7 +13680,7 @@ static int remove_dup_with_hash_index(THD *thd, TABLE *table,
ulong key_length,
Item *having)
{
- byte *key_buffer, *key_pos, *record=table->record[0];
+ uchar *key_buffer, *key_pos, *record=table->record[0];
int error;
handler *file= table->file;
ulong extra_length= ALIGN_SIZE(key_length)-key_length;
@@ -13068,7 +13691,7 @@ static int remove_dup_with_hash_index(THD *thd, TABLE *table,
if (!my_multi_malloc(MYF(MY_WME),
&key_buffer,
(uint) ((key_length + extra_length) *
- (long) file->records),
+ (long) file->stats.records),
&field_lengths,
(uint) (field_count*sizeof(*field_lengths)),
NullS))
@@ -13090,7 +13713,7 @@ static int remove_dup_with_hash_index(THD *thd, TABLE *table,
extra_length= ALIGN_SIZE(key_length)-key_length;
}
- if (hash_init(&hash, &my_charset_bin, (uint) file->records, 0,
+ if (hash_init(&hash, &my_charset_bin, (uint) file->stats.records, 0,
key_length, (hash_get_key) 0, 0, 0))
{
my_free((char*) key_buffer,MYF(0));
@@ -13101,7 +13724,7 @@ static int remove_dup_with_hash_index(THD *thd, TABLE *table,
key_pos=key_buffer;
for (;;)
{
- byte *org_key_pos;
+ uchar *org_key_pos;
if (thd->killed)
{
thd->send_kill_message();
@@ -13118,7 +13741,7 @@ static int remove_dup_with_hash_index(THD *thd, TABLE *table,
}
if (having && !having->val_int())
{
- if ((error=file->delete_row(record)))
+ if ((error=file->ha_delete_row(record)))
goto err;
continue;
}
@@ -13128,14 +13751,14 @@ static int remove_dup_with_hash_index(THD *thd, TABLE *table,
field_length=field_lengths;
for (Field **ptr= first_field ; *ptr ; ptr++)
{
- (*ptr)->sort_string((char*) key_pos,*field_length);
+ (*ptr)->sort_string(key_pos,*field_length);
key_pos+= *field_length++;
}
/* Check if it exists before */
if (hash_search(&hash, org_key_pos, key_length))
{
/* Duplicated found ; Remove the row */
- if ((error=file->delete_row(record)))
+ if ((error=file->ha_delete_row(record)))
goto err;
}
else
@@ -13232,7 +13855,7 @@ join_init_cache(THD *thd,JOIN_TAB *tables,uint table_count)
sizeof(CACHE_FIELD*))))
{
- my_free((gptr) cache->buff,MYF(0)); /* purecov: inspected */
+ my_free((uchar*) cache->buff,MYF(0)); /* purecov: inspected */
cache->buff=0; /* purecov: inspected */
DBUG_RETURN(1); /* purecov: inspected */
}
@@ -13245,14 +13868,14 @@ join_init_cache(THD *thd,JOIN_TAB *tables,uint table_count)
{
bool have_bit_fields= FALSE;
uint null_fields=0,used_fields;
-
Field **f_ptr,*field;
+ MY_BITMAP *read_set= tables[i].table->read_set;
for (f_ptr=tables[i].table->field,used_fields=tables[i].used_fields ;
used_fields ;
f_ptr++)
{
field= *f_ptr;
- if (field->query_id == thd->query_id)
+ if (bitmap_is_set(read_set, field->field_index))
{
used_fields--;
length+=field->fill_cache_field(copy);
@@ -13269,7 +13892,7 @@ join_init_cache(THD *thd,JOIN_TAB *tables,uint table_count)
/* Copy null bits from table */
if (null_fields || have_bit_fields)
{ /* must copy null bits */
- copy->str=(char*) tables[i].table->null_flags;
+ copy->str= tables[i].table->null_flags;
copy->length= tables[i].table->s->null_bytes;
copy->strip=0;
copy->blob_field=0;
@@ -13280,7 +13903,7 @@ join_init_cache(THD *thd,JOIN_TAB *tables,uint table_count)
/* If outer join table, copy null_row flag */
if (tables[i].table->maybe_null)
{
- copy->str= (char*) &tables[i].table->null_row;
+ copy->str= (uchar*) &tables[i].table->null_row;
copy->length=sizeof(tables[i].table->null_row);
copy->strip=0;
copy->blob_field=0;
@@ -13343,13 +13966,13 @@ store_record_in_cache(JOIN_CACHE *cache)
{
if (last_record)
{
- copy->blob_field->get_image((char*) pos,copy->length+sizeof(char*),
+ copy->blob_field->get_image(pos, copy->length+sizeof(char*),
copy->blob_field->charset());
pos+=copy->length+sizeof(char*);
}
else
{
- copy->blob_field->get_image((char*) pos,copy->length, // blob length
+ copy->blob_field->get_image(pos, copy->length, // blob length
copy->blob_field->charset());
memcpy(pos+copy->length,copy->str,copy->blob_length); // Blob data
pos+=copy->length+copy->blob_length;
@@ -13359,7 +13982,7 @@ store_record_in_cache(JOIN_CACHE *cache)
{
if (copy->strip)
{
- char *str,*end;
+ uchar *str,*end;
for (str=copy->str,end= str+copy->length;
end > str && end[-1] == ' ' ;
end--) ;
@@ -13415,13 +14038,13 @@ read_cached_record(JOIN_TAB *tab)
{
if (last_record)
{
- copy->blob_field->set_image((char*) pos,copy->length+sizeof(char*),
+ copy->blob_field->set_image(pos, copy->length+sizeof(char*),
copy->blob_field->charset());
pos+=copy->length+sizeof(char*);
}
else
{
- copy->blob_field->set_ptr((char*) pos,(char*) pos+copy->length);
+ copy->blob_field->set_ptr(pos, pos+copy->length);
pos+=copy->length+copy->blob_field->get_length();
}
}
@@ -13454,7 +14077,8 @@ cmp_buffer_with_ref(JOIN_TAB *tab)
{
memcpy(tab->ref.key_buff2, tab->ref.key_buff, tab->ref.key_length);
}
- if ((tab->ref.key_err= cp_buffer_from_ref(tab->join->thd, &tab->ref)) ||
+ if ((tab->ref.key_err= cp_buffer_from_ref(tab->join->thd, tab->table,
+ &tab->ref)) ||
diff)
return 1;
return memcmp(tab->ref.key_buff2, tab->ref.key_buff, tab->ref.key_length)
@@ -13463,20 +14087,24 @@ cmp_buffer_with_ref(JOIN_TAB *tab)
bool
-cp_buffer_from_ref(THD *thd, TABLE_REF *ref)
+cp_buffer_from_ref(THD *thd, TABLE *table, TABLE_REF *ref)
{
enum enum_check_fields save_count_cuted_fields= thd->count_cuted_fields;
thd->count_cuted_fields= CHECK_FIELD_IGNORE;
+ my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->write_set);
+ bool result= 0;
+
for (store_key **copy=ref->key_copy ; *copy ; copy++)
{
if ((*copy)->copy() & 1)
{
- thd->count_cuted_fields= save_count_cuted_fields;
- return 1; // Something went wrong
+ result= 1;
+ break;
}
}
thd->count_cuted_fields= save_count_cuted_fields;
- return 0;
+ dbug_tmp_restore_column_map(table->write_set, old_map);
+ return result;
}
@@ -13484,37 +14112,37 @@ cp_buffer_from_ref(THD *thd, TABLE_REF *ref)
Group and order functions
*****************************************************************************/
-/*
+/**
Resolve an ORDER BY or GROUP BY column reference.
- SYNOPSIS
- find_order_in_list()
- thd [in] Pointer to current thread structure
- ref_pointer_array [in/out] All select, group and order by fields
- tables [in] List of tables to search in (usually FROM clause)
- order [in] Column reference to be resolved
- fields [in] List of fields to search in (usually SELECT list)
- all_fields [in/out] All select, group and order by fields
- is_group_field [in] True if order is a GROUP field, false if
- ORDER by field
-
- DESCRIPTION
- Given a column reference (represented by 'order') from a GROUP BY or ORDER
- BY clause, find the actual column it represents. If the column being
- resolved is from the GROUP BY clause, the procedure searches the SELECT
- list 'fields' and the columns in the FROM list 'tables'. If 'order' is from
- the ORDER BY clause, only the SELECT list is being searched.
-
- If 'order' is resolved to an Item, then order->item is set to the found
- Item. If there is no item for the found column (that is, it was resolved
- into a table field), order->item is 'fixed' and is added to all_fields and
- ref_pointer_array.
-
- RETURN
+ Given a column reference (represented by 'order') from a GROUP BY or ORDER
+ BY clause, find the actual column it represents. If the column being
+ resolved is from the GROUP BY clause, the procedure searches the SELECT
+ list 'fields' and the columns in the FROM list 'tables'. If 'order' is from
+ the ORDER BY clause, only the SELECT list is being searched.
+
+ If 'order' is resolved to an Item, then order->item is set to the found
+ Item. If there is no item for the found column (that is, it was resolved
+ into a table field), order->item is 'fixed' and is added to all_fields and
+ ref_pointer_array.
+
+ ref_pointer_array and all_fields are updated.
+
+ @param[in] thd Pointer to current thread structure
+ @param[in,out] ref_pointer_array All select, group and order by fields
+ @param[in] tables List of tables to search in (usually
+ FROM clause)
+ @param[in] order Column reference to be resolved
+ @param[in] fields List of fields to search in (usually
+ SELECT list)
+ @param[in,out] all_fields All select, group and order by fields
+ @param[in] is_group_field True if order is a GROUP field, false if
+ ORDER by field
+
+ @retval
FALSE if OK
+ @retval
TRUE if error occurred
-
- ref_pointer_array and all_fields are updated
*/
static bool
@@ -13648,9 +14276,11 @@ find_order_in_list(THD *thd, Item **ref_pointer_array, TABLE_LIST *tables,
}
-/*
- Change order to point at item in select list. If item isn't a number
- and doesn't exits in the select list, add it the the field list.
+/**
+ Change order to point at item in select list.
+
+ If item isn't a number and doesn't exits in the select list, add it the
+ the field list.
*/
int setup_order(THD *thd, Item **ref_pointer_array, TABLE_LIST *tables,
@@ -13667,27 +14297,30 @@ int setup_order(THD *thd, Item **ref_pointer_array, TABLE_LIST *tables,
}
-/*
+/**
Intitialize the GROUP BY list.
- SYNOPSIS
- setup_group()
- thd Thread handler
- ref_pointer_array We store references to all fields that was not in
- 'fields' here.
- fields All fields in the select part. Any item in 'order'
- that is part of these list is replaced by a pointer
- to this fields.
- all_fields Total list of all unique fields used by the select.
- All items in 'order' that was not part of fields will
- be added first to this list.
- order The fields we should do GROUP BY on.
- hidden_group_fields Pointer to flag that is set to 1 if we added any fields
- to all_fields.
-
- RETURN
- 0 ok
- 1 error (probably out of memory)
+ @param thd Thread handler
+ @param ref_pointer_array We store references to all fields that was
+ not in 'fields' here.
+ @param fields All fields in the select part. Any item in
+ 'order' that is part of these list is replaced
+ by a pointer to this fields.
+ @param all_fields Total list of all unique fields used by the
+ select. All items in 'order' that was not part
+ of fields will be added first to this list.
+ @param order The fields we should do GROUP BY on.
+ @param hidden_group_fields Pointer to flag that is set to 1 if we added
+ any fields to all_fields.
+
+ @todo
+ change ER_WRONG_FIELD_WITH_GROUP to more detailed
+ ER_NON_GROUPING_FIELD_USED
+
+ @retval
+ 0 ok
+ @retval
+ 1 error (probably out of memory)
*/
int
@@ -13780,8 +14413,11 @@ next_field:
return 0;
}
-/*
- Add fields with aren't used at start of field list. Return FALSE if ok
+/**
+ Add fields with aren't used at start of field list.
+
+ @return
+ FALSE if ok
*/
static bool
@@ -13789,11 +14425,11 @@ setup_new_fields(THD *thd, List<Item> &fields,
List<Item> &all_fields, ORDER *new_field)
{
Item **item;
- DBUG_ENTER("setup_new_fields");
-
- thd->set_query_id=1; // Not really needed, but...
uint counter;
enum_resolution_type not_used;
+ DBUG_ENTER("setup_new_fields");
+
+ thd->mark_used_columns= MARK_COLUMNS_READ; // Not really needed, but...
for (; new_field ; new_field= new_field->next)
{
if ((item= find_item_in_list(*new_field->item, fields, &counter,
@@ -13811,10 +14447,11 @@ setup_new_fields(THD *thd, List<Item> &fields,
DBUG_RETURN(0);
}
-/*
- Create a group by that consist of all non const fields. Try to use
- the fields in the order given by 'order' to allow one to optimize
- away 'order by'.
+/**
+ Create a group by that consist of all non const fields.
+
+ Try to use the fields in the order given by 'order' to allow one to
+ optimize away 'order by'.
*/
static ORDER *
@@ -13901,9 +14538,9 @@ next_item:
}
-/*****************************************************************************
- Update join with count of the different type of fields
-*****************************************************************************/
+/**
+ Update join with count of the different type of fields.
+*/
void
count_field_types(SELECT_LEX *select_lex, TMP_TABLE_PARAM *param,
@@ -13953,8 +14590,9 @@ count_field_types(SELECT_LEX *select_lex, TMP_TABLE_PARAM *param,
}
-/*
- Return 1 if second is a subpart of first argument
+/**
+ Return 1 if second is a subpart of first argument.
+
If first parts has different direction, change it to second part
(group is sorted like order)
*/
@@ -13972,10 +14610,9 @@ test_if_subpart(ORDER *a,ORDER *b)
return test(!b);
}
-/*
+/**
Return table number if there is only one table in sort order
- and group and order is compatible
- else return 0;
+ and group and order is compatible, else return 0.
*/
static TABLE *
@@ -14006,7 +14643,9 @@ get_sort_by_table(ORDER *a,ORDER *b,TABLE_LIST *tables)
}
- /* calc how big buffer we need for comparing group entries */
+/**
+ calc how big buffer we need for comparing group entries.
+*/
static void
calc_group_buffer(JOIN *join,ORDER *group)
@@ -14022,11 +14661,11 @@ calc_group_buffer(JOIN *join,ORDER *group)
if (field)
{
enum_field_types type;
- if ((type= field->type()) == FIELD_TYPE_BLOB)
+ if ((type= field->type()) == MYSQL_TYPE_BLOB)
key_length+=MAX_BLOB_WIDTH; // Can't be used as a key
else if (type == MYSQL_TYPE_VARCHAR || type == MYSQL_TYPE_VAR_STRING)
key_length+= field->field_length + HA_KEY_BLOB_LENGTH;
- else if (type == FIELD_TYPE_BIT)
+ else if (type == MYSQL_TYPE_BIT)
{
/* Bit is usually stored as a longlong key for group fields */
key_length+= 8; // Big enough
@@ -14077,6 +14716,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();
}
}
@@ -14090,17 +14730,17 @@ calc_group_buffer(JOIN *join,ORDER *group)
}
-/*
- allocate group fields or take prepared (cached)
+/**
+ allocate group fields or take prepared (cached).
- SYNOPSIS
- make_group_fields()
- main_join - join of current select
- curr_join - current join (join of current select or temporary copy of it)
+ @param main_join join of current select
+ @param curr_join current join (join of current select or temporary copy
+ of it)
- RETURN
- 0 - ok
- 1 - failed
+ @retval
+ 0 ok
+ @retval
+ 1 failed
*/
static bool
@@ -14121,9 +14761,10 @@ make_group_fields(JOIN *main_join, JOIN *curr_join)
}
-/*
- Get a list of buffers for saveing last group
- Groups are saved in reverse order for easyer check loop
+/**
+ Get a list of buffers for saveing last group.
+
+ Groups are saved in reverse order for easyer check loop.
*/
static bool
@@ -14161,27 +14802,33 @@ test_if_group_changed(List<Cached_item> &list)
}
-/*
- Setup copy_fields to save fields at start of new group
+/**
+ Setup copy_fields to save fields at start of new group.
- setup_copy_fields()
- thd - THD pointer
- param - temporary table parameters
- ref_pointer_array - array of pointers to top elements of filed list
- res_selected_fields - new list of items of select item list
- res_all_fields - new list of all items
- elements - number of elements in select item list
- all_fields - all fields list
+ Setup copy_fields to save fields at start of new group
- DESCRIPTION
- Setup copy_fields to save fields at start of new group
- 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
-
- RETURN
- 0 - ok
- !=0 - error
+ 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.
+
+ @param thd THD pointer
+ @param param temporary table parameters
+ @param ref_pointer_array array of pointers to top elements of filed list
+ @param res_selected_fields new list of items of select item list
+ @param res_all_fields new list of all items
+ @param elements number of elements in select item list
+ @param all_fields all fields list
+
+ @todo
+ In most cases this result will be sent to the user.
+ This should be changed to use copy_int or copy_real depending
+ on how the value is to be used: In some cases this may be an
+ argument in a group function, like: IF(ISNULL(col),0,COUNT(*))
+
+ @retval
+ 0 ok
+ @retval
+ !=0 error
*/
bool
@@ -14193,6 +14840,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);
res_selected_fields.empty();
res_all_fields.empty();
List_iterator_fast<Item> itr(res_all_fields);
@@ -14205,12 +14853,19 @@ setup_copy_fields(THD *thd, TMP_TABLE_PARAM *param,
goto err2;
param->copy_funcs.empty();
+ IF_DBUG(copy_start= copy);
for (i= 0; (pos= li++); i++)
{
Field *field;
- char *tmp;
+ uchar *tmp;
Item *real_pos= pos->real_item();
- if (real_pos->type() == Item::FIELD_ITEM)
+ /*
+ Aggregate functions can be substituted for fields (by e.g. temp tables).
+ We need to filter those substituted fields out.
+ */
+ if (real_pos->type() == Item::FIELD_ITEM &&
+ !(real_pos != pos &&
+ ((Item_ref *)pos)->ref_type() == Item_ref::AGGREGATE_REF))
{
Item_field *item;
if (!(item= new Item_field(thd, ((Item_field*) real_pos))))
@@ -14253,11 +14908,11 @@ setup_copy_fields(THD *thd, TMP_TABLE_PARAM *param,
another extra byte to not get warnings from purify in
Field_string::val_int
*/
- tmp= (char*) sql_alloc(field->pack_length()+2);
- if (!tmp)
+ if (!(tmp= (uchar*) sql_alloc(field->pack_length()+2)))
goto err;
if (copy)
{
+ DBUG_ASSERT (param->field_count > (uint) (copy - copy_start));
copy->set(tmp, item->result_field);
item->result_field->move_field(copy->to_ptr,copy->to_null_ptr,1);
#ifdef HAVE_purify
@@ -14316,8 +14971,8 @@ err2:
}
-/*
- Make a copy of all simple SELECT'ed items
+/**
+ Make a copy of all simple SELECT'ed items.
This is done at the start of a new group so that we can retrieve
these later when the group changes.
@@ -14339,14 +14994,13 @@ copy_fields(TMP_TABLE_PARAM *param)
}
-/*
- Make an array of pointers to sum_functions to speed up sum_func calculation
-
- SYNOPSIS
- alloc_func_list()
+/**
+ Make an array of pointers to sum_functions to speed up
+ sum_func calculation.
- RETURN
+ @retval
0 ok
+ @retval
1 Error
*/
@@ -14391,18 +15045,17 @@ bool JOIN::alloc_func_list()
}
-/*
- Initialize 'sum_funcs' array with all Item_sum objects
+/**
+ Initialize 'sum_funcs' array with all Item_sum objects.
- SYNOPSIS
- make_sum_func_list()
- field_list All items
- send_fields Items in select list
- before_group_by Set to 1 if this is called before GROUP BY handling
- recompute Set to TRUE if sum_funcs must be recomputed
+ @param field_list All items
+ @param send_fields 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
- RETURN
+ @retval
0 ok
+ @retval
1 error
*/
@@ -14443,21 +15096,21 @@ bool JOIN::make_sum_func_list(List<Item> &field_list, List<Item> &send_fields,
}
-/*
+/**
Change all funcs and sum_funcs to fields in tmp table, and create
new list of all items.
- change_to_use_tmp_fields()
- thd - THD pointer
- ref_pointer_array - array of pointers to top elements of filed list
- res_selected_fields - new list of items of select item list
- res_all_fields - new list of all items
- elements - number of elements in select item list
- all_fields - all fields list
-
- RETURN
- 0 - ok
- !=0 - error
+ @param thd THD pointer
+ @param ref_pointer_array array of pointers to top elements of filed list
+ @param res_selected_fields new list of items of select item list
+ @param res_all_fields new list of all items
+ @param elements number of elements in select item list
+ @param all_fields all fields list
+
+ @retval
+ 0 ok
+ @retval
+ !=0 error
*/
static bool
@@ -14477,7 +15130,7 @@ change_to_use_tmp_fields(THD *thd, Item **ref_pointer_array,
for (i= 0; (item= it++); i++)
{
Field *field;
-
+
if ((item->with_sum_func && item->type() != Item::SUM_FUNC_ITEM) ||
(item->type() == Item::FUNC_ITEM &&
((Item_func*)item)->functype() == Item_func::SUSERVAR_FUNC))
@@ -14513,7 +15166,7 @@ change_to_use_tmp_fields(THD *thd, Item **ref_pointer_array,
char buff[256];
String str(buff,sizeof(buff),&my_charset_bin);
str.length(0);
- item->print(&str);
+ item->print(&str, QT_ORDINARY);
item_field->name= sql_strmake(str.ptr(),str.length());
}
#endif
@@ -14534,20 +15187,20 @@ change_to_use_tmp_fields(THD *thd, Item **ref_pointer_array,
}
-/*
- Change all sum_func refs to fields to point at fields in tmp table
- Change all funcs to be fields in tmp table
-
- change_refs_to_tmp_fields()
- thd - THD pointer
- ref_pointer_array - array of pointers to top elements of filed list
- res_selected_fields - new list of items of select item list
- res_all_fields - new list of all items
- elements - number of elements in select item list
- all_fields - all fields list
-
- RETURN
+/**
+ Change all sum_func refs to fields to point at fields in tmp table.
+ Change all funcs to be fields in tmp table.
+
+ @param thd THD pointer
+ @param ref_pointer_array array of pointers to top elements of filed list
+ @param res_selected_fields new list of items of select item list
+ @param res_all_fields new list of all items
+ @param elements number of elements in select item list
+ @param all_fields all fields list
+
+ @retval
0 ok
+ @retval
1 error
*/
@@ -14585,16 +15238,15 @@ change_refs_to_tmp_fields(THD *thd, Item **ref_pointer_array,
******************************************************************************/
-/*
- Call ::setup for all sum functions
+/**
+ Call ::setup for all sum functions.
- SYNOPSIS
- setup_sum_funcs()
- thd thread handler
- func_ptr sum function list
+ @param thd thread handler
+ @param func_ptr sum function list
- RETURN
+ @retval
FALSE ok
+ @retval
TRUE error
*/
@@ -14620,7 +15272,7 @@ init_tmptable_sum_functions(Item_sum **func_ptr)
}
- /* Update record 0 in tmp_table from record 1 */
+/** Update record 0 in tmp_table from record 1. */
static void
update_tmptable_sum_func(Item_sum **func_ptr,
@@ -14632,7 +15284,7 @@ update_tmptable_sum_func(Item_sum **func_ptr,
}
- /* Copy result of sum functions to record in tmp_table */
+/** Copy result of sum functions to record in tmp_table. */
static void
copy_sum_funcs(Item_sum **func_ptr, Item_sum **end_ptr)
@@ -14671,7 +15323,7 @@ update_sum_func(Item_sum **func_ptr)
return 0;
}
- /* Copy result of functions to record in tmp_table */
+/** Copy result of functions to record in tmp_table. */
void
copy_funcs(Item **func_ptr)
@@ -14682,9 +15334,9 @@ copy_funcs(Item **func_ptr)
}
-/*
+/**
Create a condition for a const reference and add this to the
- currenct select for the table
+ currenct select for the table.
*/
static bool add_ref_to_table_cond(THD *thd, JOIN_TAB *join_tab)
@@ -14724,12 +15376,11 @@ static bool add_ref_to_table_cond(THD *thd, JOIN_TAB *join_tab)
}
-/*
+/**
Free joins of subselect of this select.
- free_underlaid_joins()
- thd - THD pointer
- select - pointer to st_select_lex which subselects joins we will free
+ @param thd THD pointer
+ @param select pointer to st_select_lex which subselects joins we will free
*/
void free_underlaid_joins(THD *thd, SELECT_LEX *select)
@@ -14744,41 +15395,43 @@ void free_underlaid_joins(THD *thd, SELECT_LEX *select)
ROLLUP handling
****************************************************************************/
-/*
- Replace occurences of group by fields in an expression by ref items
+/**
+ Replace occurences of group by fields in an expression by ref items.
- SYNOPSIS
- change_group_ref()
- thd reference to the context
- expr expression to make replacement
- group_list list of references to group by items
- changed out: returns 1 if item contains a replaced field item
+ The function replaces occurrences of group by fields in expr
+ by ref objects for these fields unless they are under aggregate
+ functions.
+ The function also corrects value of the the maybe_null attribute
+ for the items of all subexpressions containing group by fields.
- DESCRIPTION
- The function replaces occurrences of group by fields in expr
- by ref objects for these fields unless they are under aggregate
- functions.
- The function also corrects value of the the maybe_null attribute
- for the items of all subexpressions containing group by fields.
+ @b EXAMPLES
+ @code
+ SELECT a+1 FROM t1 GROUP BY a WITH ROLLUP
+ SELECT SUM(a)+a FROM t1 GROUP BY a WITH ROLLUP
+ @endcode
+
+ @b IMPLEMENTATION
- IMPLEMENTATION
The function recursively traverses the tree of the expr expression,
looks for occurrences of the group by fields that are not under
aggregate functions and replaces them for the corresponding ref items.
- NOTES
+ @note
This substitution is needed GROUP BY queries with ROLLUP if
SELECT list contains expressions over group by attributes.
- TODO: Some functions are not null-preserving. For those functions
+ @param thd reference to the context
+ @param expr expression to make replacement
+ @param group_list list of references to group by items
+ @param changed out: returns 1 if item contains a replaced field item
+
+ @todo
+ - TODO: Some functions are not null-preserving. For those functions
updating of the maybe_null attribute is an overkill.
- EXAMPLES
- SELECT a+1 FROM t1 GROUP BY a WITH ROLLUP
- SELECT SUM(a)+a FROM t1 GROUP BY a WITH ROLLUP
-
- RETURN
+ @retval
0 if ok
+ @retval
1 on error
*/
@@ -14827,7 +15480,7 @@ static bool change_group_ref(THD *thd, Item_func *expr, ORDER *group_list,
}
-/* Allocate memory needed for other rollup functions */
+/** Allocate memory needed for other rollup functions. */
bool JOIN::rollup_init()
{
@@ -14930,22 +15583,20 @@ bool JOIN::rollup_init()
}
-/*
- Fill up rollup structures with pointers to fields to use
+/**
+ Fill up rollup structures with pointers to fields to use.
- SYNOPSIS
- rollup_make_fields()
- fields_arg List of all fields (hidden and real ones)
- sel_fields Pointer to selected fields
- func Store here a pointer to all fields
+ Creates copies of item_sum items for each sum level.
- IMPLEMENTATION:
- Creates copies of item_sum items for each sum level
+ @param fields_arg List of all fields (hidden and real ones)
+ @param sel_fields Pointer to selected fields
+ @param func Store here a pointer to all fields
- RETURN
- 0 if ok
- In this case func is pointing to next not used element.
- 1 on error
+ @retval
+ 0 if ok;
+ In this case func is pointing to next not used element.
+ @retval
+ 1 on error
*/
bool JOIN::rollup_make_fields(List<Item> &fields_arg, List<Item> &sel_fields,
@@ -15063,21 +15714,22 @@ bool JOIN::rollup_make_fields(List<Item> &fields_arg, List<Item> &sel_fields,
return 0;
}
-/*
- Send all rollup levels higher than the current one to the client
+/**
+ Send all rollup levels higher than the current one to the client.
- SYNOPSIS:
- rollup_send_data()
- idx Level we are on:
- 0 = Total sum level
- 1 = First group changed (a)
- 2 = Second group changed (a,b)
+ @b SAMPLE
+ @code
+ SELECT a, b, c SUM(b) FROM t1 GROUP BY a,b WITH ROLLUP
+ @endcode
- SAMPLE
- SELECT a, b, c SUM(b) FROM t1 GROUP BY a,b WITH ROLLUP
+ @param idx Level we are on:
+ - 0 = Total sum level
+ - 1 = First group changed (a)
+ - 2 = Second group changed (a,b)
- RETURN
- 0 ok
+ @retval
+ 0 ok
+ @retval
1 If send_data_failed()
*/
@@ -15103,22 +15755,23 @@ int JOIN::rollup_send_data(uint idx)
return 0;
}
-/*
- Write all rollup levels higher than the current one to a temp table
-
- SYNOPSIS:
- rollup_write_data()
- idx Level we are on:
- 0 = Total sum level
- 1 = First group changed (a)
- 2 = Second group changed (a,b)
- table reference to temp table
-
- SAMPLE
- SELECT a, b, SUM(c) FROM t1 GROUP BY a,b WITH ROLLUP
-
- RETURN
- 0 ok
+/**
+ Write all rollup levels higher than the current one to a temp table.
+
+ @b SAMPLE
+ @code
+ SELECT a, b, SUM(c) FROM t1 GROUP BY a,b WITH ROLLUP
+ @endcode
+
+ @param idx Level we are on:
+ - 0 = Total sum level
+ - 1 = First group changed (a)
+ - 2 = Second group changed (a,b)
+ @param table reference to temp table
+
+ @retval
+ 0 ok
+ @retval
1 if write_data_failed()
*/
@@ -15142,7 +15795,7 @@ int JOIN::rollup_write_data(uint idx, TABLE *table_arg)
item->save_in_result_field(1);
}
copy_sum_funcs(sum_funcs_end[i+1], sum_funcs_end[i]);
- if ((write_error= table_arg->file->write_row(table_arg->record[0])))
+ if ((write_error= table_arg->file->ha_write_row(table_arg->record[0])))
{
if (create_myisam_from_heap(thd, table_arg, &tmp_table_param,
write_error, 0))
@@ -15155,12 +15808,9 @@ int JOIN::rollup_write_data(uint idx, TABLE *table_arg)
return 0;
}
-/*
+/**
clear results if there are not rows found for group
(end_send_group/end_write_group)
-
- SYNOPSYS
- JOIN::clear()
*/
void JOIN::clear()
@@ -15176,11 +15826,11 @@ void JOIN::clear()
}
}
-/****************************************************************************
- EXPLAIN handling
+/**
+ EXPLAIN handling.
- Send a description about what how the select will be done to stdout
-****************************************************************************/
+ Send a description about what how the select will be done to stdout.
+*/
static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
bool distinct,const char *message)
@@ -15200,15 +15850,24 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
thd->server_status&= ~(SERVER_QUERY_NO_INDEX_USED | SERVER_QUERY_NO_GOOD_INDEX_USED);
join->unit->offset_limit_cnt= 0;
+ /*
+ NOTE: the number/types of items pushed into item_list must be in sync with
+ EXPLAIN column types as they're "defined" in THD::send_explain_fields()
+ */
if (message)
{
item_list.push_back(new Item_int((int32)
join->select_lex->select_number));
item_list.push_back(new Item_string(join->select_lex->type,
- (uint) strlen(join->select_lex->type), cs));
+ strlen(join->select_lex->type), cs));
for (uint i=0 ; i < 7; i++)
item_list.push_back(item_null);
- item_list.push_back(new Item_string(message,(uint) strlen(message),cs));
+ if (join->thd->lex->describe & DESCRIBE_PARTITIONS)
+ item_list.push_back(item_null);
+ if (join->thd->lex->describe & DESCRIBE_EXTENDED)
+ item_list.push_back(item_null);
+
+ item_list.push_back(new Item_string(message,strlen(message),cs));
if (result->send_data(item_list))
join->error= 1;
}
@@ -15227,7 +15886,7 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
item_list.push_back(new Item_null);
/* select_type */
item_list.push_back(new Item_string(join->select_lex->type,
- (uint) strlen(join->select_lex->type),
+ strlen(join->select_lex->type),
cs));
/* table */
{
@@ -15252,9 +15911,12 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
}
item_list.push_back(new Item_string(table_name_buffer, len, cs));
}
+ /* partitions */
+ if (join->thd->lex->describe & DESCRIBE_PARTITIONS)
+ item_list.push_back(item_null);
/* type */
item_list.push_back(new Item_string(join_type_str[JT_ALL],
- (uint) strlen(join_type_str[JT_ALL]),
+ strlen(join_type_str[JT_ALL]),
cs));
/* possible_keys */
item_list.push_back(item_null);
@@ -15264,6 +15926,9 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
item_list.push_back(item_null);
/* ref */
item_list.push_back(item_null);
+ /* in_rows */
+ if (join->thd->lex->describe & DESCRIBE_EXTENDED)
+ item_list.push_back(item_null);
/* rows */
item_list.push_back(item_null);
/* extra */
@@ -15283,6 +15948,7 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
{
JOIN_TAB *tab=join->join_tab+i;
TABLE *table=tab->table;
+ TABLE_LIST *table_list= tab->table->pos_in_table_list;
char buff[512];
char buff1[512], buff2[512], buff3[512];
char keylen_str_buf[64];
@@ -15303,7 +15969,7 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
join->select_lex->select_number));
/* select_type */
item_list.push_back(new Item_string(join->select_lex->type,
- (uint) strlen(join->select_lex->type),
+ strlen(join->select_lex->type),
cs));
if (tab->type == JT_ALL && tab->select && tab->select->quick)
{
@@ -15328,12 +15994,31 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
{
TABLE_LIST *real_table= table->pos_in_table_list;
item_list.push_back(new Item_string(real_table->alias,
- (uint) strlen(real_table->alias),
+ strlen(real_table->alias),
cs));
}
- /* type */
+ /* "partitions" column */
+ if (join->thd->lex->describe & DESCRIBE_PARTITIONS)
+ {
+#ifdef WITH_PARTITION_STORAGE_ENGINE
+ partition_info *part_info;
+ if (!table->derived_select_number &&
+ (part_info= table->part_info))
+ {
+ Item_string *item_str= new Item_string(cs);
+ make_used_partitions_str(part_info, &item_str->str_value);
+ item_list.push_back(item_str);
+ }
+ else
+ item_list.push_back(item_null);
+#else
+ /* just produce empty column if partitioning is not compiled in */
+ item_list.push_back(item_null);
+#endif
+ }
+ /* "type" column */
item_list.push_back(new Item_string(join_type_str[tab->type],
- (uint) strlen(join_type_str[tab->type]),
+ strlen(join_type_str[tab->type]),
cs));
/* Build "possible_keys" value and add it to item_list */
if (!tab->keys.is_clear_all())
@@ -15346,7 +16031,7 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
if (tmp1.length())
tmp1.append(',');
tmp1.append(table->key_info[j].name,
- (uint) strlen(table->key_info[j].name),
+ strlen(table->key_info[j].name),
system_charset_info);
}
}
@@ -15362,17 +16047,17 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
KEY *key_info=table->key_info+ tab->ref.key;
register uint length;
item_list.push_back(new Item_string(key_info->name,
- (uint) strlen(key_info->name),
+ strlen(key_info->name),
system_charset_info));
- length= (uint) (longlong2str(tab->ref.key_length, keylen_str_buf, 10) -
- keylen_str_buf);
+ length= longlong2str(tab->ref.key_length, keylen_str_buf, 10) -
+ keylen_str_buf;
item_list.push_back(new Item_string(keylen_str_buf, length,
system_charset_info));
for (store_key **ref=tab->ref.key_copy ; *ref ; ref++)
{
if (tmp2.length())
tmp2.append(',');
- tmp2.append((*ref)->name(), (uint) strlen((*ref)->name()),
+ tmp2.append((*ref)->name(), strlen((*ref)->name()),
system_charset_info);
}
item_list.push_back(new Item_string(tmp2.ptr(),tmp2.length(),cs));
@@ -15382,9 +16067,9 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
KEY *key_info=table->key_info+ tab->index;
register uint length;
item_list.push_back(new Item_string(key_info->name,
- (uint) strlen(key_info->name),cs));
- length= (uint) (longlong2str(key_info->key_length, keylen_str_buf, 10) -
- keylen_str_buf);
+ strlen(key_info->name),cs));
+ length= longlong2str(key_info->key_length, keylen_str_buf, 10) -
+ keylen_str_buf;
item_list.push_back(new Item_string(keylen_str_buf,
length,
system_charset_info));
@@ -15399,25 +16084,80 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
}
else
{
- item_list.push_back(item_null);
+ if (table_list->schema_table &&
+ table_list->schema_table->i_s_requested_object & OPTIMIZE_I_S_TABLE)
+ {
+ const char *tmp_buff;
+ int f_idx;
+ if (table_list->has_db_lookup_value)
+ {
+ f_idx= table_list->schema_table->idx_field1;
+ tmp_buff= table_list->schema_table->fields_info[f_idx].field_name;
+ tmp2.append(tmp_buff, strlen(tmp_buff), cs);
+ }
+ if (table_list->has_table_lookup_value)
+ {
+ if (table_list->has_db_lookup_value)
+ tmp2.append(',');
+ f_idx= table_list->schema_table->idx_field2;
+ tmp_buff= table_list->schema_table->fields_info[f_idx].field_name;
+ tmp2.append(tmp_buff, strlen(tmp_buff), cs);
+ }
+ if (tmp2.length())
+ item_list.push_back(new Item_string(tmp2.ptr(),tmp2.length(),cs));
+ else
+ item_list.push_back(item_null);
+ }
+ else
+ item_list.push_back(item_null);
item_list.push_back(item_null);
item_list.push_back(item_null);
}
+
/* Add "rows" field to item_list. */
- item_list.push_back(new Item_int((longlong) (ulonglong)
- join->best_positions[i]. records_read,
- MY_INT64_NUM_DECIMAL_DIGITS));
+ if (table_list->schema_table)
+ {
+ /* in_rows */
+ if (join->thd->lex->describe & DESCRIBE_EXTENDED)
+ item_list.push_back(item_null);
+ /* rows */
+ item_list.push_back(item_null);
+ }
+ 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)
+ examined_rows= tab->limit ? tab->limit : tab->table->file->records();
+ else
+ examined_rows=(ha_rows)join->best_positions[i].records_read;
+
+ item_list.push_back(new Item_int((longlong) (ulonglong) examined_rows,
+ MY_INT64_NUM_DECIMAL_DIGITS));
+
+ /* Add "filtered" field to item_list. */
+ if (join->thd->lex->describe & DESCRIBE_EXTENDED)
+ {
+ float f= 0.0;
+ if (examined_rows)
+ f= (float) (100.0 * join->best_positions[i].records_read /
+ examined_rows);
+ item_list.push_back(new Item_float(f, 2));
+ }
+ }
+
/* Build "Extra" field and add it to item_list. */
my_bool key_read=table->key_read;
if ((tab->type == JT_NEXT || tab->type == JT_CONST) &&
- table->used_keys.is_set(tab->index))
+ table->covering_keys.is_set(tab->index))
key_read=1;
if (quick_type == QUICK_SELECT_I::QS_TYPE_ROR_INTERSECT &&
!((QUICK_ROR_INTERSECT_SELECT*)tab->select->quick)->need_to_fetch_row)
key_read=1;
if (tab->info)
- item_list.push_back(new Item_string(tab->info,(uint) strlen(tab->info),cs));
+ item_list.push_back(new Item_string(tab->info,strlen(tab->info),cs));
else if (tab->packed_info & TAB_INFO_HAVE_VALUE)
{
if (tab->packed_info & TAB_INFO_USING_INDEX)
@@ -15467,13 +16207,31 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
if (thd->lex->describe & DESCRIBE_EXTENDED)
{
extra.append(STRING_WITH_LEN(": "));
- ((COND *)pushed_cond)->print(&extra);
+ ((COND *)pushed_cond)->print(&extra, QT_ORDINARY);
}
}
else
extra.append(STRING_WITH_LEN("; Using where"));
}
}
+ if (table_list->schema_table &&
+ table_list->schema_table->i_s_requested_object & OPTIMIZE_I_S_TABLE)
+ {
+ if (!table_list->table_open_method)
+ extra.append(STRING_WITH_LEN("; Skip_open_table"));
+ else if (table_list->table_open_method == OPEN_FRM_ONLY)
+ extra.append(STRING_WITH_LEN("; Open_frm_only"));
+ else
+ extra.append(STRING_WITH_LEN("; Open_full_table"));
+ if (table_list->has_db_lookup_value &&
+ table_list->has_table_lookup_value)
+ extra.append(STRING_WITH_LEN("; Scanned 0 databases"));
+ else if (table_list->has_db_lookup_value ||
+ table_list->has_table_lookup_value)
+ extra.append(STRING_WITH_LEN("; Scanned 1 database"));
+ else
+ extra.append(STRING_WITH_LEN("; Scanned all databases"));
+ }
if (key_read)
{
if (quick_type == QUICK_SELECT_I::QS_TYPE_GROUP_MIN_MAX)
@@ -15504,6 +16262,8 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
break;
}
}
+ if (i > 0 && tab[-1].next_select == sub_select_cache)
+ extra.append(STRING_WITH_LEN("; Using join buffer"));
/* Skip initial "; "*/
const char *str= extra.ptr();
@@ -15560,7 +16320,7 @@ bool mysql_explain_union(THD *thd, SELECT_LEX_UNIT *unit, select_result *result)
"UNION")));
sl->options|= SELECT_DESCRIBE;
}
- if (first->next_select())
+ if (unit->is_union())
{
unit->fake_select_lex->select_number= UINT_MAX; // jost for initialization
unit->fake_select_lex->type= "UNION RESULT";
@@ -15586,21 +16346,23 @@ bool mysql_explain_union(THD *thd, SELECT_LEX_UNIT *unit, select_result *result)
first->options | thd->options | SELECT_DESCRIBE,
result, unit, first);
}
- DBUG_RETURN(res || thd->net.report_error);
+ DBUG_RETURN(res || thd->is_error());
}
-/*
- Print joins from the FROM clause
+/**
+ Print joins from the FROM clause.
- SYNOPSIS
- print_join()
- thd thread handler
- str string where table should be printed
- tables list of tables in join
+ @param thd thread handler
+ @param str string where table should be printed
+ @param tables list of tables in join
+ @query_type type of the query is being generated
*/
-static void print_join(THD *thd, String *str, List<TABLE_LIST> *tables)
+static void print_join(THD *thd,
+ String *str,
+ List<TABLE_LIST> *tables,
+ enum_query_type query_type)
{
/* List is reversed => we should reverse it before using */
List_iterator_fast<TABLE_LIST> ti(*tables);
@@ -15613,7 +16375,7 @@ static void print_join(THD *thd, String *str, List<TABLE_LIST> *tables)
*t= ti++;
DBUG_ASSERT(tables->elements >= 1);
- (*table)->print(thd, str);
+ (*table)->print(thd, str, query_type);
TABLE_LIST **end= table + tables->elements;
for (TABLE_LIST **tbl= table + 1; tbl < end; tbl++)
@@ -15628,11 +16390,11 @@ static void print_join(THD *thd, String *str, List<TABLE_LIST> *tables)
str->append(STRING_WITH_LEN(" straight_join "));
else
str->append(STRING_WITH_LEN(" join "));
- curr->print(thd, str);
+ curr->print(thd, str, query_type);
if (curr->on_expr)
{
str->append(STRING_WITH_LEN(" on("));
- curr->on_expr->print(str);
+ curr->on_expr->print(str, query_type);
str->append(')');
}
}
@@ -15640,9 +16402,9 @@ static void print_join(THD *thd, String *str, List<TABLE_LIST> *tables)
/**
- @brief Print an index hint for a table
+ @brief Print an index hint
- @details Prints out the USE|FORCE|IGNORE index hints for a table.
+ @details Prints out the USE|FORCE|IGNORE index hint.
@param thd the current thread
@param[out] str appends the index hint here
@@ -15653,55 +16415,41 @@ static void print_join(THD *thd, String *str, List<TABLE_LIST> *tables)
*/
void
-TABLE_LIST::print_index_hint(THD *thd, String *str,
- const char *hint, uint32 hint_length,
- List<String> indexes)
+Index_hint::print(THD *thd, String *str)
{
- List_iterator_fast<String> li(indexes);
- String *idx;
- bool first= 1;
- size_t find_length= strlen(primary_key_name);
-
- str->append (' ');
- str->append (hint, hint_length);
+ switch (type)
+ {
+ case INDEX_HINT_IGNORE: str->append(STRING_WITH_LEN("IGNORE INDEX")); break;
+ case INDEX_HINT_USE: str->append(STRING_WITH_LEN("USE INDEX")); break;
+ case INDEX_HINT_FORCE: str->append(STRING_WITH_LEN("FORCE INDEX")); break;
+ }
str->append (STRING_WITH_LEN(" ("));
- while ((idx = li++))
+ if (key_name.length)
{
- if (first)
- first= 0;
- else
- str->append(',');
- /*
- It's safe to use ptr() here because we compare the length first
- and we rely that my_strcasecmp will not access more than length()
- chars from the string. See test_if_string_in_list() for similar
- implementation.
- */
- if (find_length == idx->length() &&
- !my_strcasecmp (system_charset_info, primary_key_name,
- idx->ptr()))
+ if (thd && !my_strnncoll(system_charset_info,
+ (const uchar *)key_name.str, key_name.length,
+ (const uchar *)primary_key_name,
+ strlen(primary_key_name)))
str->append(primary_key_name);
else
- append_identifier (thd, str, idx->ptr(), idx->length());
+ append_identifier(thd, str, key_name.str, key_name.length);
}
str->append(')');
}
-/*
- Print table as it should be in join list
+/**
+ Print table as it should be in join list.
- SYNOPSIS
- TABLE_LIST::print();
- str string where table should bbe printed
+ @param str string where table should be printed
*/
-void TABLE_LIST::print(THD *thd, String *str)
+void TABLE_LIST::print(THD *thd, String *str, enum_query_type query_type)
{
if (nested_join)
{
str->append('(');
- print_join(thd, str, &nested_join->join_list);
+ print_join(thd, str, &nested_join->join_list, query_type);
str->append(')');
}
else
@@ -15724,7 +16472,7 @@ void TABLE_LIST::print(THD *thd, String *str)
{
// A derived table
str->append('(');
- derived->print(str);
+ derived->print(str, query_type);
str->append(')');
cmp_name= ""; // Force printing of alias
}
@@ -15741,7 +16489,7 @@ void TABLE_LIST::print(THD *thd, String *str)
if (schema_table)
{
append_identifier(thd, str, schema_table_name,
- (uint) strlen(schema_table_name));
+ strlen(schema_table_name));
cmp_name= schema_table_name;
}
else
@@ -15766,24 +16514,25 @@ void TABLE_LIST::print(THD *thd, String *str)
}
}
- append_identifier(thd, str, t_alias, (uint) strlen(t_alias));
+ append_identifier(thd, str, t_alias, strlen(t_alias));
}
- if (use_index)
+ if (index_hints)
{
- if (force_index)
- print_index_hint(thd, str, STRING_WITH_LEN("FORCE INDEX"), *use_index);
- else
- print_index_hint(thd, str, STRING_WITH_LEN("USE INDEX"), *use_index);
- }
- if (ignore_index)
- print_index_hint (thd, str, STRING_WITH_LEN("IGNORE INDEX"), *ignore_index);
+ List_iterator<Index_hint> it(*index_hints);
+ Index_hint *hint;
+ while ((hint= it++))
+ {
+ str->append (STRING_WITH_LEN(" "));
+ hint->print (thd, str);
+ }
+ }
}
}
-void st_select_lex::print(THD *thd, String *str)
+void st_select_lex::print(THD *thd, String *str, enum_query_type query_type)
{
/* QQ: thd may not be set for sub queries, but this should be fixed */
if (!thd)
@@ -15831,7 +16580,7 @@ void st_select_lex::print(THD *thd, String *str)
first= 0;
else
str->append(',');
- item->print_item_w_name(str);
+ item->print_item_w_name(str, query_type);
}
/*
@@ -15842,7 +16591,7 @@ void st_select_lex::print(THD *thd, String *str)
{
str->append(STRING_WITH_LEN(" from "));
/* go through join tree */
- print_join(thd, str, &top_join_list);
+ print_join(thd, str, &top_join_list, query_type);
}
else if (where)
{
@@ -15861,7 +16610,7 @@ void st_select_lex::print(THD *thd, String *str)
{
str->append(STRING_WITH_LEN(" where "));
if (cur_where)
- cur_where->print(str);
+ cur_where->print(str, query_type);
else
str->append(cond_value != Item::COND_FALSE ? "1" : "0");
}
@@ -15870,7 +16619,7 @@ void st_select_lex::print(THD *thd, String *str)
if (group_list.elements)
{
str->append(STRING_WITH_LEN(" group by "));
- print_order(str, (ORDER *) group_list.first);
+ print_order(str, (ORDER *) group_list.first, query_type);
switch (olap)
{
case CUBE_TYPE:
@@ -15893,7 +16642,7 @@ void st_select_lex::print(THD *thd, String *str)
{
str->append(STRING_WITH_LEN(" having "));
if (cur_having)
- cur_having->print(str);
+ cur_having->print(str, query_type);
else
str->append(having_value != Item::COND_FALSE ? "1" : "0");
}
@@ -15901,26 +16650,25 @@ void st_select_lex::print(THD *thd, String *str)
if (order_list.elements)
{
str->append(STRING_WITH_LEN(" order by "));
- print_order(str, (ORDER *) order_list.first);
+ print_order(str, (ORDER *) order_list.first, query_type);
}
// limit
- print_limit(thd, str);
+ print_limit(thd, str, query_type);
// PROCEDURE unsupported here
}
-/*
- change select_result object of JOIN
+/**
+ change select_result object of JOIN.
- SYNOPSIS
- JOIN::change_result()
- res new select_result object
+ @param res new select_result object
- RETURN
- FALSE - OK
- TRUE - error
+ @retval
+ FALSE OK
+ @retval
+ TRUE error
*/
bool JOIN::change_result(select_result *res)
@@ -15934,3 +16682,7 @@ bool JOIN::change_result(select_result *res)
}
DBUG_RETURN(FALSE);
}
+
+/**
+ @} (end of group Query_Optimizer)
+*/
diff --git a/sql/sql_select.h b/sql/sql_select.h
index 75a905043d2..7d794b71f4d 100644
--- a/sql/sql_select.h
+++ b/sql/sql_select.h
@@ -14,7 +14,12 @@
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
-/* classes to use when handling where clause */
+/**
+ @file
+
+ @brief
+ classes to use when handling where clause
+*/
#ifdef USE_PRAGMA_INTERFACE
#pragma interface /* gcc class implementation */
@@ -25,12 +30,12 @@
typedef struct keyuse_t {
TABLE *table;
- Item *val; /* or value if no field */
+ Item *val; /**< or value if no field */
table_map used_tables;
uint key, keypart, optimize;
key_part_map keypart_map;
ha_rows ref_table_rows;
- /*
+ /**
If true, the comparison this value was created from will not be
satisfied if val has NULL 'value'.
*/
@@ -53,13 +58,13 @@ class store_key;
typedef struct st_table_ref
{
bool key_err;
- uint key_parts; // num of ...
- uint key_length; // length of key_buff
- int key; // key no
- byte *key_buff; // value to look for with key
- byte *key_buff2; // key_buff+key_length
+ uint key_parts; ///< num of ...
+ uint key_length; ///< length of key_buff
+ int key; ///< key no
+ uchar *key_buff; ///< value to look for with key
+ uchar *key_buff2; ///< key_buff+key_length
store_key **key_copy; //
- Item **items; // val()'s for each keypart
+ Item **items; ///< val()'s for each keypart
/*
Array of pointers to trigger variables. Some/all of the pointers may be
NULL. The ref access can be used iff
@@ -72,25 +77,25 @@ typedef struct st_table_ref
underlying conditions is switched off (see subquery code for more details)
*/
bool **cond_guards;
- /*
+ /**
(null_rejecting & (1<<i)) means the condition is '=' and no matching
rows will be produced if items[i] IS NULL (see add_not_null_conds())
*/
key_part_map null_rejecting;
- table_map depend_map; // Table depends on these tables.
- byte *null_ref_key; // null byte position in the key_buf.
- // used for REF_OR_NULL optimization.
+ table_map depend_map; ///< Table depends on these tables.
+ /* null byte position in the key_buf. Used for REF_OR_NULL optimization */
+ uchar *null_ref_key;
} TABLE_REF;
-/*
-** CACHE_FIELD and JOIN_CACHE is used on full join to cache records in outer
-** table
-*/
+/**
+ CACHE_FIELD and JOIN_CACHE is used on full join to cache records in outer
+ table
+*/
typedef struct st_cache_field {
- char *str;
- uint length,blob_length;
+ uchar *str;
+ uint length, blob_length;
Field_blob *blob_field;
bool strip;
} CACHE_FIELD;
@@ -105,7 +110,7 @@ typedef struct st_join_cache {
/*
-** The structs which holds the join connections and join states
+ The structs which holds the join connections and join states
*/
enum join_type { JT_UNKNOWN,JT_SYSTEM,JT_CONST,JT_EQ_REF,JT_REF,JT_MAYBE_REF,
JT_ALL, JT_RANGE, JT_NEXT, JT_FT, JT_REF_OR_NULL,
@@ -132,21 +137,22 @@ typedef enum_nested_loop_state
typedef int (*Read_record_func)(struct st_join_table *tab);
Next_select_func setup_end_select_func(JOIN *join);
+
typedef struct st_join_table {
st_join_table() {} /* Remove gcc warning */
TABLE *table;
- KEYUSE *keyuse; /* pointer to first used key */
+ KEYUSE *keyuse; /**< pointer to first used key */
SQL_SELECT *select;
COND *select_cond;
QUICK_SELECT_I *quick;
- Item **on_expr_ref; /* pointer to the associated on expression */
- COND_EQUAL *cond_equal; /* multiple equalities for the on expression */
- st_join_table *first_inner; /* first inner table for including outerjoin */
- bool found; /* true after all matches or null complement */
- bool not_null_compl;/* true before null complement is added */
- st_join_table *last_inner; /* last table table for embedding outer join */
- st_join_table *first_upper; /* first inner table for embedding outer join */
- st_join_table *first_unmatched; /* used for optimization purposes only */
+ Item **on_expr_ref; /**< pointer to the associated on expression */
+ COND_EQUAL *cond_equal; /**< multiple equalities for the on expression */
+ st_join_table *first_inner; /**< first inner table for including outerjoin */
+ bool found; /**< true after all matches or null complement */
+ bool not_null_compl;/**< true before null complement is added */
+ st_join_table *last_inner; /**< last table table for embedding outer join */
+ st_join_table *first_upper; /**< first inner table for embedding outer join */
+ st_join_table *first_unmatched; /**< used for optimization purposes only */
/* Special content for EXPLAIN 'Extra' column or NULL if none */
const char *info;
@@ -167,23 +173,44 @@ typedef struct st_join_table {
Read_record_func save_read_first_record;/* to save read_first_record */
int (*save_read_record) (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 */
+ key_map const_keys; /**< Keys with constant part */
+ key_map checked_keys; /**< Keys checked in find_best */
key_map needed_reg;
- key_map keys; /* all keys with can be used */
- ha_rows records,found_records,read_time;
+ key_map keys; /**< all keys with can be used */
+
+ /* Either #rows in the table or 1 for const table. */
+ ha_rows records;
+ /*
+ Number of records that will be scanned (yes scanned, not returned) by the
+ best 'independent' access method, i.e. table scan or QUICK_*_SELECT)
+ */
+ ha_rows found_records;
+ /*
+ Cost of accessing the table using "ALL" or range/index_merge access
+ method (but not 'index' for some reason), i.e. this matches method which
+ E(#records) is in found_records.
+ */
+ ha_rows read_time;
+
table_map dependent,key_dependent;
uint use_quick,index;
- uint status; // Save status for cache
+ uint status; ///< Save status for cache
uint used_fields,used_fieldlength,used_blobs;
enum join_type type;
bool cached_eq_ref_table,eq_ref_table,not_used_in_distinct;
+ bool sorted;
+ /*
+ If it's not 0 the number stored this field indicates that the index
+ scan has been chosen to access the table data and we expect to scan
+ this number of rows for the table.
+ */
+ ha_rows limit;
TABLE_REF ref;
JOIN_CACHE cache;
JOIN *join;
- /* Bitmap of nested joins this table is part of */
+ /** Bitmap of nested joins this table is part of */
nested_join_map embedding_map;
-
+
void cleanup();
inline bool is_using_loose_index_scan()
{
@@ -198,15 +225,38 @@ enum_nested_loop_state sub_select_cache(JOIN *join, JOIN_TAB *join_tab, bool
enum_nested_loop_state sub_select(JOIN *join,JOIN_TAB *join_tab, bool
end_of_records);
-
-typedef struct st_position /* Used in find_best */
+/**
+ Information about a position of table within a join order. Used in join
+ optimization.
+*/
+typedef struct st_position
{
+ /*
+ The "fanout": number of output rows that will be produced (after
+ pushed down selection condition is applied) per each row combination of
+ previous tables.
+ */
double records_read;
+
+ /*
+ Cost accessing the table in course of the entire complete join execution,
+ i.e. cost of one access method use (e.g. 'range' or 'ref' scan ) times
+ number the access method will be invoked.
+ */
double read_time;
JOIN_TAB *table;
+
+ /*
+ NULL - 'index' or 'range' or 'index_merge' or 'ALL' access is used.
+ Other - [eq_]ref[_or_null] access is used. Pointer to {t.keypart1 = expr}
+ */
KEYUSE *key;
+
+ /* If ref-based access is used: bitmap of tables this table depends on */
+ table_map ref_depend_map;
} POSITION;
+
typedef struct st_rollup
{
enum State { STATE_NONE, STATE_INITED, STATE_READY };
@@ -219,18 +269,18 @@ typedef struct st_rollup
class JOIN :public Sql_alloc
{
- JOIN(const JOIN &rhs); /* not implemented */
- JOIN& operator=(const JOIN &rhs); /* not implemented */
+ JOIN(const JOIN &rhs); /**< not implemented */
+ JOIN& operator=(const JOIN &rhs); /**< not implemented */
public:
JOIN_TAB *join_tab,**best_ref;
- JOIN_TAB **map2table; // mapping between table indexes and JOIN_TABs
- JOIN_TAB *join_tab_save; // saved join_tab for subquery reexecution
+ JOIN_TAB **map2table; ///< mapping between table indexes and JOIN_TABs
+ JOIN_TAB *join_tab_save; ///< saved join_tab for subquery reexecution
TABLE **table,**all_tables,*sort_by_table;
uint tables,const_tables;
uint send_group_parts;
bool sort_and_group,first_record,full_join,group, no_field_update;
bool do_send_rows;
- /*
+ /**
TRUE when we want to resume nested loop iterations when
fetching data from a cursor
*/
@@ -241,7 +291,7 @@ public:
*/
table_map outer_join;
ha_rows send_records,found_records,examined_rows,row_limit, select_limit;
- /*
+ /**
Used to fetch no more than given amount of rows per one
fetch operation of server side cursor.
The value is checked in end_send and end_send_group in fashion, similar
@@ -253,7 +303,7 @@ public:
ha_rows fetch_limit;
POSITION positions[MAX_TABLES+1],best_positions[MAX_TABLES+1];
- /*
+ /* *
Bitmap of nested joins embedding the position at the end of the current
partial join (valid only during join optimizer run).
*/
@@ -263,25 +313,25 @@ public:
List<Item> *fields;
List<Cached_item> group_fields, group_fields_cache;
TABLE *tmp_table;
- // used to store 2 possible tmp table of SELECT
+ /// used to store 2 possible tmp table of SELECT
TABLE *exec_tmp_table1, *exec_tmp_table2;
THD *thd;
Item_sum **sum_funcs, ***sum_funcs_end;
- /* second copy of sumfuncs (for queries with 2 temporary tables */
+ /** second copy of sumfuncs (for queries with 2 temporary tables */
Item_sum **sum_funcs2, ***sum_funcs_end2;
Procedure *procedure;
Item *having;
- Item *tmp_having; // To store having when processed temporary table
- Item *having_history; // Store having for explain
+ Item *tmp_having; ///< To store having when processed temporary table
+ Item *having_history; ///< Store having for explain
ulonglong select_options;
select_result *result;
TMP_TABLE_PARAM tmp_table_param;
MYSQL_LOCK *lock;
- // unit structure (with global parameters) for this select
+ /// unit structure (with global parameters) for this select
SELECT_LEX_UNIT *unit;
- // select that processed
+ /// select that processed
SELECT_LEX *select_lex;
- /*
+ /**
TRUE <=> optimizer must not mark any table as a constant table.
This is needed for subqueries in form "a IN (SELECT .. UNION SELECT ..):
when we optimize the select that reads the results of the union from a
@@ -290,11 +340,11 @@ public:
*/
bool no_const_tables;
- JOIN *tmp_join; // copy of this JOIN to be used with temporary tables
- ROLLUP rollup; // Used with rollup
+ JOIN *tmp_join; ///< copy of this JOIN to be used with temporary tables
+ ROLLUP rollup; ///< Used with rollup
- bool select_distinct; // Set if SELECT DISTINCT
- /*
+ bool select_distinct; ///< Set if SELECT DISTINCT
+ /**
If we have the GROUP BY statement in the query,
but the group_list was emptied by optimizer, this
flag is TRUE.
@@ -309,42 +359,42 @@ public:
It's also set if ORDER/GROUP BY is empty.
*/
bool simple_order, simple_group;
- /*
+ /**
Is set only in case if we have a GROUP BY clause
and no ORDER BY after constant elimination of 'order'.
*/
bool no_order;
- /* Is set if we have a GROUP BY and we have ORDER BY on a constant. */
+ /** Is set if we have a GROUP BY and we have ORDER BY on a constant. */
bool skip_sort_order;
bool need_tmp, hidden_group_fields;
DYNAMIC_ARRAY keyuse;
Item::cond_result cond_value, having_value;
- List<Item> all_fields; // to store all fields that used in query
- //Above list changed to use temporary table
+ List<Item> all_fields; ///< to store all fields that used in query
+ ///Above list changed to use temporary table
List<Item> tmp_all_fields1, tmp_all_fields2, tmp_all_fields3;
- //Part, shared with list above, emulate following list
+ ///Part, shared with list above, emulate following list
List<Item> tmp_fields_list1, tmp_fields_list2, tmp_fields_list3;
- List<Item> &fields_list; // hold field list passed to mysql_select
+ List<Item> &fields_list; ///< hold field list passed to mysql_select
List<Item> procedure_fields_list;
int error;
ORDER *order, *group_list, *proc_param; //hold parameters of mysql_select
COND *conds; // ---"---
Item *conds_history; // store WHERE for explain
- TABLE_LIST *tables_list; //hold 'tables' parameter of mysql_select
- List<TABLE_LIST> *join_list; // list of joined tables in reverse order
+ TABLE_LIST *tables_list; ///<hold 'tables' parameter of mysql_select
+ List<TABLE_LIST> *join_list; ///< list of joined tables in reverse order
COND_EQUAL *cond_equal;
- SQL_SELECT *select; //created in optimisation phase
- JOIN_TAB *return_tab; //used only for outer joins
- Item **ref_pointer_array; //used pointer reference for this select
+ SQL_SELECT *select; ///<created in optimisation phase
+ JOIN_TAB *return_tab; ///<used only for outer joins
+ Item **ref_pointer_array; ///<used pointer reference for this select
// Copy of above to be used with different lists
Item **items0, **items1, **items2, **items3, **current_ref_pointer_array;
- uint ref_pointer_array_size; // size of above in bytes
- const char *zero_result_cause; // not 0 if exec must return zero result
+ uint ref_pointer_array_size; ///< size of above in bytes
+ const char *zero_result_cause; ///< not 0 if exec must return zero result
- bool union_part; // this subselect is part of union
- bool optimized; // flag to avoid double optimization in EXPLAIN
+ bool union_part; ///< this subselect is part of union
+ bool optimized; ///< flag to avoid double optimization in EXPLAIN
/*
storage for caching buffers allocated during query execution.
@@ -456,14 +506,14 @@ public:
int rollup_send_data(uint idx);
int rollup_write_data(uint idx, TABLE *table);
void remove_subq_pushed_predicates(Item **where);
- /*
+ /**
Release memory and, if possible, the open tables held by this execution
plan (and nested plans). It's used to release some tables before
the end of execution in order to increase concurrency and reduce
memory consumption.
*/
void join_free();
- /* Cleanup this JOIN, possibly for reuse */
+ /** Cleanup this JOIN, possibly for reuse */
void cleanup(bool full);
void clear();
bool save_join_tab();
@@ -520,32 +570,33 @@ int opt_sum_query(TABLE_LIST *tables, List<Item> &all_fields,COND *conds);
/* from sql_delete.cc, used by opt_range.cc */
extern "C" int refpos_order_cmp(void* arg, const void *a,const void *b);
-/* class to copying an field/item to a key struct */
+/** class to copying an field/item to a key struct */
class store_key :public Sql_alloc
{
public:
bool null_key; /* TRUE <=> the value of the key has a null part */
enum store_key_result { STORE_KEY_OK, STORE_KEY_FATAL, STORE_KEY_CONV };
- store_key(THD *thd, Field *field_arg, char *ptr, char *null, uint length)
+ store_key(THD *thd, Field *field_arg, uchar *ptr, uchar *null, uint length)
:null_key(0), null_ptr(null), err(0)
{
- if (field_arg->type() == FIELD_TYPE_BLOB
- || field_arg->type() == FIELD_TYPE_GEOMETRY)
+ if (field_arg->type() == MYSQL_TYPE_BLOB
+ || field_arg->type() == MYSQL_TYPE_GEOMETRY)
{
/*
Key segments are always packed with a 2 byte length prefix.
See mi_rkey for details.
*/
- to_field=new Field_varstring(ptr, length, 2, (uchar*) null, 1,
- Field::NONE, field_arg->field_name,
- field_arg->table, field_arg->charset());
+ to_field= new Field_varstring(ptr, length, 2, null, 1,
+ Field::NONE, field_arg->field_name,
+ field_arg->table->s, field_arg->charset());
+ to_field->init(field_arg->table);
}
else
to_field=field_arg->new_key_field(thd->mem_root, field_arg->table,
- ptr, (uchar*) null, 1);
+ ptr, null, 1);
}
- virtual ~store_key() {} /* Not actually needed */
+ virtual ~store_key() {} /** Not actually needed */
virtual const char *name() const=0;
/**
@@ -574,8 +625,8 @@ public:
protected:
Field *to_field; // Store data here
- char *null_ptr;
- char err;
+ uchar *null_ptr;
+ uchar err;
virtual enum store_key_result copy_inner()=0;
};
@@ -586,11 +637,12 @@ class store_key_field: public store_key
Copy_field copy_field;
const char *field_name;
public:
- store_key_field(THD *thd, Field *to_field_arg, char *ptr, char *null_ptr_arg,
+ store_key_field(THD *thd, Field *to_field_arg, uchar *ptr,
+ uchar *null_ptr_arg,
uint length, Field *from_field, const char *name_arg)
:store_key(thd, to_field_arg,ptr,
null_ptr_arg ? null_ptr_arg : from_field->maybe_null() ? &err
- : NullS,length), field_name(name_arg)
+ : (uchar*) 0, length), field_name(name_arg)
{
if (to_field)
{
@@ -602,7 +654,11 @@ class store_key_field: public store_key
protected:
enum store_key_result copy_inner()
{
+ TABLE *table= copy_field.to_field->table;
+ my_bitmap_map *old_map= dbug_tmp_use_all_columns(table,
+ table->write_set);
copy_field.do_copy(&copy_field);
+ dbug_tmp_restore_column_map(table->write_set, old_map);
null_key= to_field->is_null();
return err != 0 ? STORE_KEY_FATAL : STORE_KEY_OK;
}
@@ -614,18 +670,22 @@ class store_key_item :public store_key
protected:
Item *item;
public:
- store_key_item(THD *thd, Field *to_field_arg, char *ptr, char *null_ptr_arg,
- uint length, Item *item_arg)
- :store_key(thd, to_field_arg,ptr,
+ store_key_item(THD *thd, Field *to_field_arg, uchar *ptr,
+ uchar *null_ptr_arg, uint length, Item *item_arg)
+ :store_key(thd, to_field_arg, ptr,
null_ptr_arg ? null_ptr_arg : item_arg->maybe_null ?
- &err : NullS, length), item(item_arg)
+ &err : (uchar*) 0, length), item(item_arg)
{}
const char *name() const { return "func"; }
protected:
enum store_key_result copy_inner()
{
+ TABLE *table= to_field->table;
+ my_bitmap_map *old_map= dbug_tmp_use_all_columns(table,
+ table->write_set);
int res= item->save_in_field(to_field, 1);
+ dbug_tmp_restore_column_map(table->write_set, old_map);
null_key= to_field->is_null() || item->null_value;
return (err != 0 || res > 2 ? STORE_KEY_FATAL : (store_key_result) res);
}
@@ -636,12 +696,12 @@ class store_key_const_item :public store_key_item
{
bool inited;
public:
- store_key_const_item(THD *thd, Field *to_field_arg, char *ptr,
- char *null_ptr_arg, uint length,
+ store_key_const_item(THD *thd, Field *to_field_arg, uchar *ptr,
+ uchar *null_ptr_arg, uint length,
Item *item_arg)
:store_key_item(thd, to_field_arg,ptr,
null_ptr_arg ? null_ptr_arg : item_arg->maybe_null ?
- &err : NullS, length, item_arg), inited(0)
+ &err : (uchar*) 0, length, item_arg), inited(0)
{
}
const char *name() const { return "const"; }
@@ -664,7 +724,7 @@ protected:
}
};
-bool cp_buffer_from_ref(THD *thd, TABLE_REF *ref);
+bool cp_buffer_from_ref(THD *thd, TABLE *table, TABLE_REF *ref);
bool error_if_full_join(JOIN *join);
int report_error(TABLE *table, int error);
int safe_index_read(JOIN_TAB *tab);
diff --git a/sql/sql_servers.cc b/sql/sql_servers.cc
new file mode 100644
index 00000000000..b1c856c87aa
--- /dev/null
+++ b/sql/sql_servers.cc
@@ -0,0 +1,1305 @@
+/* Copyright (C) 2000-2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+
+/*
+ The servers are saved in the system table "servers"
+
+ Currently, when the user performs an ALTER SERVER or a DROP SERVER
+ operation, it will cause all open tables which refer to the named
+ server connection to be flushed. This may cause some undesirable
+ behaviour with regard to currently running transactions. It is
+ expected that the DBA knows what s/he is doing when s/he performs
+ the ALTER SERVER or DROP SERVER operation.
+
+ TODO:
+ It is desirable for us to implement a callback mechanism instead where
+ callbacks can be registered for specific server protocols. The callback
+ will be fired when such a server name has been created/altered/dropped
+ or when statistics are to be gathered such as how many actual connections.
+ Storage engines etc will be able to make use of the callback so that
+ currently running transactions etc will not be disrupted.
+*/
+
+#include "mysql_priv.h"
+#include "hash_filo.h"
+#include <m_ctype.h>
+#include <stdarg.h>
+#include "sp_head.h"
+#include "sp.h"
+
+/*
+ We only use 1 mutex to guard the data structures - THR_LOCK_servers.
+ Read locked when only reading data and write-locked for all other access.
+*/
+
+static HASH servers_cache;
+static MEM_ROOT mem;
+static rw_lock_t THR_LOCK_servers;
+
+static bool get_server_from_table_to_cache(TABLE *table);
+
+/* insert functions */
+static int insert_server(THD *thd, FOREIGN_SERVER *server_options);
+static int insert_server_record(TABLE *table, FOREIGN_SERVER *server);
+static int insert_server_record_into_cache(FOREIGN_SERVER *server);
+static FOREIGN_SERVER *
+prepare_server_struct_for_insert(LEX_SERVER_OPTIONS *server_options);
+/* drop functions */
+static int delete_server_record(TABLE *table,
+ char *server_name,
+ size_t server_name_length);
+static int delete_server_record_in_cache(LEX_SERVER_OPTIONS *server_options);
+
+/* update functions */
+static void prepare_server_struct_for_update(LEX_SERVER_OPTIONS *server_options,
+ FOREIGN_SERVER *existing,
+ FOREIGN_SERVER *altered);
+static int update_server(THD *thd, FOREIGN_SERVER *existing,
+ FOREIGN_SERVER *altered);
+static int update_server_record(TABLE *table, FOREIGN_SERVER *server);
+static int update_server_record_in_cache(FOREIGN_SERVER *existing,
+ FOREIGN_SERVER *altered);
+/* utility functions */
+static void merge_server_struct(FOREIGN_SERVER *from, FOREIGN_SERVER *to);
+
+
+
+static uchar *servers_cache_get_key(FOREIGN_SERVER *server, size_t *length,
+ my_bool not_used __attribute__((unused)))
+{
+ DBUG_ENTER("servers_cache_get_key");
+ DBUG_PRINT("info", ("server_name_length %d server_name %s",
+ server->server_name_length,
+ server->server_name));
+
+ *length= (uint) server->server_name_length;
+ DBUG_RETURN((uchar*) server->server_name);
+}
+
+
+/*
+ Initialize structures responsible for servers used in federated
+ server scheme information for them from the server
+ table in the 'mysql' database.
+
+ SYNOPSIS
+ servers_init()
+ dont_read_server_table TRUE if we want to skip loading data from
+ server table and disable privilege checking.
+
+ NOTES
+ This function is mostly responsible for preparatory steps, main work
+ on initialization and grants loading is done in servers_reload().
+
+ RETURN VALUES
+ 0 ok
+ 1 Could not initialize servers
+*/
+
+bool servers_init(bool dont_read_servers_table)
+{
+ THD *thd;
+ bool return_val= FALSE;
+ DBUG_ENTER("servers_init");
+
+ /* init the mutex */
+ if (my_rwlock_init(&THR_LOCK_servers, NULL))
+ 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))
+ {
+ 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);
+
+ if (dont_read_servers_table)
+ goto end;
+
+ /*
+ To be able to run this from boot, we allocate a temporary THD
+ */
+ if (!(thd=new THD))
+ 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
+ by zeros at startup.
+ */
+ return_val= servers_reload(thd);
+ delete thd;
+ /* Remember that we don't have a THD */
+ my_pthread_setspecific_ptr(THR_THD, 0);
+
+end:
+ DBUG_RETURN(return_val);
+}
+
+/*
+ Initialize server structures
+
+ SYNOPSIS
+ servers_load()
+ thd Current thread
+ tables List containing open "mysql.servers"
+
+ RETURN VALUES
+ FALSE Success
+ TRUE Error
+
+ TODO
+ Revert back to old list if we failed to load new one.
+*/
+
+static bool servers_load(THD *thd, TABLE_LIST *tables)
+{
+ TABLE *table;
+ READ_RECORD read_record_info;
+ bool return_val= TRUE;
+ DBUG_ENTER("servers_load");
+
+ my_hash_reset(&servers_cache);
+ free_root(&mem, MYF(0));
+ init_alloc_root(&mem, ACL_ALLOC_BLOCK_SIZE, 0);
+
+ init_read_record(&read_record_info,thd,table=tables[0].table,NULL,1,0,
+ FALSE);
+ while (!(read_record_info.read_record(&read_record_info)))
+ {
+ /* return_val is already TRUE, so no need to set */
+ if ((get_server_from_table_to_cache(table)))
+ goto end;
+ }
+
+ return_val= FALSE;
+
+end:
+ end_read_record(&read_record_info);
+ DBUG_RETURN(return_val);
+}
+
+
+/*
+ Forget current servers cache and read new servers
+ from the conneciton table.
+
+ SYNOPSIS
+ servers_reload()
+ thd Current thread
+
+ NOTE
+ All tables of calling thread which were open and locked by LOCK TABLES
+ statement will be unlocked and closed.
+ This function is also used for initialization of structures responsible
+ for user/db-level privilege checking.
+
+ RETURN VALUE
+ FALSE Success
+ TRUE Failure
+*/
+
+bool servers_reload(THD *thd)
+{
+ TABLE_LIST tables[1];
+ 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);
+
+ 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;
+
+ if (simple_open_n_lock_tables(thd, tables))
+ {
+ sql_print_error("Can't open and lock privilege tables: %s",
+ thd->main_da.message());
+ goto end;
+ }
+
+ if ((return_val= servers_load(thd, tables)))
+ { // Error. Revert to old list
+ /* blast, for now, we have no servers, discuss later way to preserve */
+
+ DBUG_PRINT("error",("Reverting to old privileges"));
+ servers_free();
+ }
+
+end:
+ close_thread_tables(thd);
+ DBUG_PRINT("info", ("unlocking servers_cache"));
+ rw_unlock(&THR_LOCK_servers);
+ DBUG_RETURN(return_val);
+}
+
+
+/*
+ Initialize structures responsible for servers used in federated
+ server scheme information for them from the server
+ table in the 'mysql' database.
+
+ SYNOPSIS
+ get_server_from_table_to_cache()
+ TABLE *table open table pointer
+
+
+ NOTES
+ This function takes a TABLE pointer (pointing to an opened
+ table). With this open table, a FOREIGN_SERVER struct pointer
+ is allocated into root memory, then each member of the FOREIGN_SERVER
+ struct is populated. A char pointer takes the return value of get_field
+ for each column we're interested in obtaining, and if that pointer
+ isn't 0x0, the FOREIGN_SERVER member is set to that value, otherwise,
+ is set to the value of an empty string, since get_field would set it to
+ 0x0 if the column's value is empty, even if the default value for that
+ column is NOT NULL.
+
+ RETURN VALUES
+ 0 ok
+ 1 could not insert server struct into global servers cache
+*/
+
+static bool
+get_server_from_table_to_cache(TABLE *table)
+{
+ /* alloc a server struct */
+ char *ptr;
+ char * const blank= (char*)"";
+ FOREIGN_SERVER *server= (FOREIGN_SERVER *)alloc_root(&mem,
+ sizeof(FOREIGN_SERVER));
+ DBUG_ENTER("get_server_from_table_to_cache");
+ table->use_all_columns();
+
+ /* get each field into the server struct ptr */
+ server->server_name= get_field(&mem, table->field[0]);
+ server->server_name_length= (uint) strlen(server->server_name);
+ ptr= get_field(&mem, table->field[1]);
+ server->host= ptr ? ptr : blank;
+ ptr= get_field(&mem, table->field[2]);
+ server->db= ptr ? ptr : blank;
+ ptr= get_field(&mem, table->field[3]);
+ server->username= ptr ? ptr : blank;
+ ptr= get_field(&mem, table->field[4]);
+ server->password= ptr ? ptr : blank;
+ ptr= get_field(&mem, table->field[5]);
+ server->sport= ptr ? ptr : blank;
+
+ server->port= server->sport ? atoi(server->sport) : 0;
+
+ ptr= get_field(&mem, table->field[6]);
+ server->socket= ptr && strlen(ptr) ? ptr : blank;
+ ptr= get_field(&mem, table->field[7]);
+ server->scheme= ptr ? ptr : blank;
+ ptr= get_field(&mem, table->field[8]);
+ server->owner= ptr ? ptr : blank;
+ DBUG_PRINT("info", ("server->server_name %s", server->server_name));
+ DBUG_PRINT("info", ("server->host %s", server->host));
+ DBUG_PRINT("info", ("server->db %s", server->db));
+ DBUG_PRINT("info", ("server->username %s", server->username));
+ DBUG_PRINT("info", ("server->password %s", server->password));
+ DBUG_PRINT("info", ("server->socket %s", server->socket));
+ if (my_hash_insert(&servers_cache, (uchar*) server))
+ {
+ DBUG_PRINT("info", ("had a problem inserting server %s at %lx",
+ server->server_name, (long unsigned int) server));
+ // error handling needed here
+ DBUG_RETURN(TRUE);
+ }
+ DBUG_RETURN(FALSE);
+}
+
+
+/*
+ SYNOPSIS
+ insert_server()
+ THD *thd - thread pointer
+ FOREIGN_SERVER *server - pointer to prepared FOREIGN_SERVER struct
+
+ NOTES
+ This function takes a server object that is has all members properly
+ prepared, ready to be inserted both into the mysql.servers table and
+ the servers cache.
+
+ THR_LOCK_servers must be write locked.
+
+ RETURN VALUES
+ 0 - no error
+ other - error code
+*/
+
+static int
+insert_server(THD *thd, FOREIGN_SERVER *server)
+{
+ int error= -1;
+ TABLE_LIST tables;
+ TABLE *table;
+
+ DBUG_ENTER("insert_server");
+
+ bzero((char*) &tables, sizeof(tables));
+ tables.db= (char*) "mysql";
+ tables.alias= tables.table_name= (char*) "servers";
+
+ /* need to open before acquiring THR_LOCK_plugin or it will deadlock */
+ if (! (table= open_ltable(thd, &tables, TL_WRITE, 0)))
+ goto end;
+
+ /* insert the server into the table */
+ if ((error= insert_server_record(table, server)))
+ goto end;
+
+ /* insert the server into the cache */
+ if ((error= insert_server_record_into_cache(server)))
+ goto end;
+
+end:
+ DBUG_RETURN(error);
+}
+
+
+/*
+ SYNOPSIS
+ int insert_server_record_into_cache()
+ FOREIGN_SERVER *server
+
+ NOTES
+ This function takes a FOREIGN_SERVER pointer to an allocated (root mem)
+ and inserts it into the global servers cache
+
+ THR_LOCK_servers must be write locked.
+
+ RETURN VALUE
+ 0 - no error
+ >0 - error code
+
+*/
+
+static int
+insert_server_record_into_cache(FOREIGN_SERVER *server)
+{
+ int error=0;
+ DBUG_ENTER("insert_server_record_into_cache");
+ /*
+ We succeded in insertion of the server to the table, now insert
+ the server to the cache
+ */
+ DBUG_PRINT("info", ("inserting server %s at %lx, length %d",
+ server->server_name, (long unsigned int) server,
+ server->server_name_length));
+ if (my_hash_insert(&servers_cache, (uchar*) server))
+ {
+ DBUG_PRINT("info", ("had a problem inserting server %s at %lx",
+ server->server_name, (long unsigned int) server));
+ // error handling needed here
+ error= 1;
+ }
+ DBUG_RETURN(error);
+}
+
+
+/*
+ SYNOPSIS
+ store_server_fields()
+ TABLE *table
+ FOREIGN_SERVER *server
+
+ NOTES
+ This function takes an opened table object, and a pointer to an
+ allocated FOREIGN_SERVER struct, and then stores each member of
+ the FOREIGN_SERVER to the appropriate fields in the table, in
+ advance of insertion into the mysql.servers table
+
+ RETURN VALUE
+ VOID
+
+*/
+
+static void
+store_server_fields(TABLE *table, FOREIGN_SERVER *server)
+{
+
+ table->use_all_columns();
+ /*
+ "server" has already been prepped by prepare_server_struct_for_<>
+ so, all we need to do is check if the value is set (> -1 for port)
+
+ If this happens to be an update, only the server members that
+ have changed will be set. If an insert, then all will be set,
+ even if with empty strings
+ */
+ if (server->host)
+ table->field[1]->store(server->host,
+ (uint) strlen(server->host), system_charset_info);
+ if (server->db)
+ table->field[2]->store(server->db,
+ (uint) strlen(server->db), system_charset_info);
+ if (server->username)
+ table->field[3]->store(server->username,
+ (uint) strlen(server->username), system_charset_info);
+ if (server->password)
+ table->field[4]->store(server->password,
+ (uint) strlen(server->password), system_charset_info);
+ if (server->port > -1)
+ table->field[5]->store(server->port);
+
+ if (server->socket)
+ table->field[6]->store(server->socket,
+ (uint) strlen(server->socket), system_charset_info);
+ if (server->scheme)
+ table->field[7]->store(server->scheme,
+ (uint) strlen(server->scheme), system_charset_info);
+ if (server->owner)
+ table->field[8]->store(server->owner,
+ (uint) strlen(server->owner), system_charset_info);
+}
+
+/*
+ SYNOPSIS
+ insert_server_record()
+ TABLE *table
+ FOREIGN_SERVER *server
+
+ NOTES
+ This function takes the arguments of an open table object and a pointer
+ to an allocated FOREIGN_SERVER struct. It stores the server_name into
+ the first field of the table (the primary key, server_name column). With
+ this, index_read_idx is called, if the record is found, an error is set
+ to ER_FOREIGN_SERVER_EXISTS (the server with that server name exists in the
+ table), if not, then store_server_fields stores all fields of the
+ FOREIGN_SERVER to the table, then ha_write_row is inserted. If an error
+ is encountered in either index_read_idx or ha_write_row, then that error
+ is returned
+
+ RETURN VALUE
+ 0 - no errors
+ >0 - error code
+
+ */
+
+static
+int insert_server_record(TABLE *table, FOREIGN_SERVER *server)
+{
+ int error;
+ DBUG_ENTER("insert_server_record");
+ table->use_all_columns();
+
+ empty_record(table);
+
+ /* set the field that's the PK to the value we're looking for */
+ table->field[0]->store(server->server_name,
+ server->server_name_length,
+ system_charset_info);
+
+ /* read index until record is that specified in server_name */
+ if ((error= table->file->index_read_idx_map(table->record[0], 0,
+ (uchar *)table->field[0]->ptr,
+ HA_WHOLE_KEY,
+ HA_READ_KEY_EXACT)))
+ {
+ /* if not found, err */
+ if (error != HA_ERR_KEY_NOT_FOUND && error != HA_ERR_END_OF_FILE)
+ {
+ table->file->print_error(error, MYF(0));
+ error= 1;
+ }
+ /* store each field to be inserted */
+ store_server_fields(table, server);
+
+ DBUG_PRINT("info",("record for server '%s' not found!",
+ server->server_name));
+ /* write/insert the new server */
+ if ((error=table->file->ha_write_row(table->record[0])))
+ {
+ table->file->print_error(error, MYF(0));
+ }
+ else
+ error= 0;
+ }
+ else
+ error= ER_FOREIGN_SERVER_EXISTS;
+ DBUG_RETURN(error);
+}
+
+/*
+ SYNOPSIS
+ drop_server()
+ THD *thd
+ LEX_SERVER_OPTIONS *server_options
+
+ NOTES
+ This function takes as its arguments a THD object pointer and a pointer
+ to a LEX_SERVER_OPTIONS struct from the parser. The member 'server_name'
+ of this LEX_SERVER_OPTIONS struct contains the value of the server to be
+ deleted. The mysql.servers table is opened via open_ltable, a table object
+ returned, the servers cache mutex locked, then delete_server_record is
+ called with this table object and LEX_SERVER_OPTIONS server_name and
+ server_name_length passed, containing the name of the server to be
+ dropped/deleted, then delete_server_record_in_cache is called to delete
+ the server from the servers cache.
+
+ RETURN VALUE
+ 0 - no error
+ > 0 - error code
+*/
+
+int drop_server(THD *thd, LEX_SERVER_OPTIONS *server_options)
+{
+ int error;
+ TABLE_LIST tables;
+ TABLE *table;
+ LEX_STRING name= { server_options->server_name,
+ server_options->server_name_length };
+
+ DBUG_ENTER("drop_server");
+ 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";
+
+ rw_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)))
+ {
+ error= my_errno;
+ goto end;
+ }
+
+ error= delete_server_record(table, name.str, name.length);
+
+ /* close the servers table before we call closed_cached_connection_tables */
+ close_thread_tables(thd);
+
+ if (close_cached_connection_tables(thd, TRUE, &name))
+ {
+ push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ ER_UNKNOWN_ERROR, "Server connection in use");
+ }
+
+end:
+ rw_unlock(&THR_LOCK_servers);
+ DBUG_RETURN(error);
+}
+
+
+/*
+
+ SYNOPSIS
+ delete_server_record_in_cache()
+ LEX_SERVER_OPTIONS *server_options
+
+ NOTES
+ This function's argument is a LEX_SERVER_OPTIONS struct pointer. This
+ function uses the "server_name" and "server_name_length" members of the
+ lex->server_options to search for the server in the servers_cache. Upon
+ returned the server (pointer to a FOREIGN_SERVER struct), it then deletes
+ that server from the servers_cache hash.
+
+ RETURN VALUE
+ 0 - no error
+
+*/
+
+static int
+delete_server_record_in_cache(LEX_SERVER_OPTIONS *server_options)
+{
+ int error= ER_FOREIGN_SERVER_DOESNT_EXIST;
+ FOREIGN_SERVER *server;
+ DBUG_ENTER("delete_server_record_in_cache");
+
+ DBUG_PRINT("info",("trying to obtain server name %s length %d",
+ server_options->server_name,
+ server_options->server_name_length));
+
+
+ if (!(server= (FOREIGN_SERVER *) 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,
+ server_options->server_name_length));
+ goto end;
+ }
+ /*
+ We succeded in deletion of the server to the table, now delete
+ the server from the cache
+ */
+ DBUG_PRINT("info",("deleting server %s length %d",
+ server->server_name,
+ server->server_name_length));
+
+ VOID(hash_delete(&servers_cache, (uchar*) server));
+
+ error= 0;
+
+end:
+ DBUG_RETURN(error);
+}
+
+
+/*
+
+ SYNOPSIS
+ update_server()
+ THD *thd
+ FOREIGN_SERVER *existing
+ FOREIGN_SERVER *altered
+
+ NOTES
+ This function takes as arguments a THD object pointer, and two pointers,
+ one pointing to the existing FOREIGN_SERVER struct "existing" (which is
+ the current record as it is) and another pointer pointing to the
+ FOREIGN_SERVER struct with the members containing the modified/altered
+ values that need to be updated in both the mysql.servers table and the
+ servers_cache. It opens a table, passes the table and the altered
+ FOREIGN_SERVER pointer, which will be used to update the mysql.servers
+ table for the particular server via the call to update_server_record,
+ and in the servers_cache via update_server_record_in_cache.
+
+ THR_LOCK_servers must be write locked.
+
+ RETURN VALUE
+ 0 - no error
+ >0 - error code
+
+*/
+
+int update_server(THD *thd, FOREIGN_SERVER *existing, FOREIGN_SERVER *altered)
+{
+ int error;
+ TABLE *table;
+ TABLE_LIST tables;
+ DBUG_ENTER("update_server");
+
+ bzero((char*) &tables, sizeof(tables));
+ tables.db= (char*)"mysql";
+ tables.alias= tables.table_name= (char*)"servers";
+
+ if (!(table= open_ltable(thd, &tables, TL_WRITE, 0)))
+ {
+ error= my_errno;
+ goto end;
+ }
+
+ if ((error= update_server_record(table, altered)))
+ goto end;
+
+ error= update_server_record_in_cache(existing, altered);
+
+ /*
+ Perform a reload so we don't have a 'hole' in our mem_root
+ */
+ servers_load(thd, &tables);
+
+end:
+ DBUG_RETURN(error);
+}
+
+
+/*
+
+ SYNOPSIS
+ update_server_record_in_cache()
+ FOREIGN_SERVER *existing
+ FOREIGN_SERVER *altered
+
+ NOTES
+ This function takes as an argument the FOREIGN_SERVER structi pointer
+ for the existing server and the FOREIGN_SERVER struct populated with only
+ the members which have been updated. It then "merges" the "altered" struct
+ members to the existing server, the existing server then represents an
+ updated server. Then, the existing record is deleted from the servers_cache
+ HASH, then the updated record inserted, in essence replacing the old
+ record.
+
+ THR_LOCK_servers must be write locked.
+
+ RETURN VALUE
+ 0 - no error
+ 1 - error
+
+*/
+
+int update_server_record_in_cache(FOREIGN_SERVER *existing,
+ FOREIGN_SERVER *altered)
+{
+ int error= 0;
+ DBUG_ENTER("update_server_record_in_cache");
+
+ /*
+ update the members that haven't been change in the altered server struct
+ with the values of the existing server struct
+ */
+ merge_server_struct(existing, altered);
+
+ /*
+ delete the existing server struct from the server cache
+ */
+ VOID(hash_delete(&servers_cache, (uchar*)existing));
+
+ /*
+ Insert the altered server struct into the server cache
+ */
+ if (my_hash_insert(&servers_cache, (uchar*)altered))
+ {
+ DBUG_PRINT("info", ("had a problem inserting server %s at %lx",
+ altered->server_name, (long unsigned int) altered));
+ error= ER_OUT_OF_RESOURCES;
+ }
+
+ DBUG_RETURN(error);
+}
+
+
+/*
+
+ SYNOPSIS
+ merge_server_struct()
+ FOREIGN_SERVER *from
+ FOREIGN_SERVER *to
+
+ NOTES
+ This function takes as its arguments two pointers each to an allocated
+ FOREIGN_SERVER struct. The first FOREIGN_SERVER struct represents the struct
+ that we will obtain values from (hence the name "from"), the second
+ FOREIGN_SERVER struct represents which FOREIGN_SERVER struct we will be
+ "copying" any members that have a value to (hence the name "to")
+
+ RETURN VALUE
+ VOID
+
+*/
+
+void merge_server_struct(FOREIGN_SERVER *from, FOREIGN_SERVER *to)
+{
+ DBUG_ENTER("merge_server_struct");
+ if (!to->host)
+ to->host= strdup_root(&mem, from->host);
+ if (!to->db)
+ to->db= strdup_root(&mem, from->db);
+ if (!to->username)
+ to->username= strdup_root(&mem, from->username);
+ if (!to->password)
+ to->password= strdup_root(&mem, from->password);
+ if (to->port == -1)
+ to->port= from->port;
+ if (!to->socket && from->socket)
+ to->socket= strdup_root(&mem, from->socket);
+ if (!to->scheme && from->scheme)
+ to->scheme= strdup_root(&mem, from->scheme);
+ if (!to->owner)
+ to->owner= strdup_root(&mem, from->owner);
+
+ DBUG_VOID_RETURN;
+}
+
+
+/*
+
+ SYNOPSIS
+ update_server_record()
+ TABLE *table
+ FOREIGN_SERVER *server
+
+ NOTES
+ This function takes as its arguments an open TABLE pointer, and a pointer
+ to an allocated FOREIGN_SERVER structure representing an updated record
+ which needs to be inserted. The primary key, server_name is stored to field
+ 0, then index_read_idx is called to read the index to that record, the
+ record then being ready to be updated, if found. If not found an error is
+ set and error message printed. If the record is found, store_record is
+ called, then store_server_fields stores each field from the the members of
+ the updated FOREIGN_SERVER struct.
+
+ RETURN VALUE
+ 0 - no error
+
+*/
+
+
+static int
+update_server_record(TABLE *table, FOREIGN_SERVER *server)
+{
+ int error=0;
+ DBUG_ENTER("update_server_record");
+ 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,
+ server->server_name_length,
+ system_charset_info);
+
+ if ((error= table->file->index_read_idx_map(table->record[0], 0,
+ (uchar *)table->field[0]->ptr,
+ ~(longlong)0,
+ HA_READ_KEY_EXACT)))
+ {
+ if (error != HA_ERR_KEY_NOT_FOUND && error != HA_ERR_END_OF_FILE)
+ table->file->print_error(error, MYF(0));
+ DBUG_PRINT("info",("server not found!"));
+ error= ER_FOREIGN_SERVER_DOESNT_EXIST;
+ }
+ else
+ {
+ /* ok, so we can update since the record exists in the table */
+ store_record(table,record[1]);
+ store_server_fields(table, server);
+ if ((error=table->file->ha_update_row(table->record[1],
+ table->record[0])) &&
+ error != HA_ERR_RECORD_IS_THE_SAME)
+ {
+ DBUG_PRINT("info",("problems with ha_update_row %d", error));
+ goto end;
+ }
+ else
+ error= 0;
+ }
+
+end:
+ DBUG_RETURN(error);
+}
+
+
+/*
+
+ SYNOPSIS
+ delete_server_record()
+ TABLE *table
+ char *server_name
+ int server_name_length
+
+ NOTES
+
+ RETURN VALUE
+ 0 - no error
+
+*/
+
+static int
+delete_server_record(TABLE *table,
+ char *server_name, size_t server_name_length)
+{
+ int error;
+ DBUG_ENTER("delete_server_record");
+ table->use_all_columns();
+
+ /* set the field that's the PK to the value we're looking for */
+ table->field[0]->store(server_name, server_name_length, system_charset_info);
+
+ if ((error= table->file->index_read_idx_map(table->record[0], 0,
+ (uchar *)table->field[0]->ptr,
+ HA_WHOLE_KEY,
+ HA_READ_KEY_EXACT)))
+ {
+ if (error != HA_ERR_KEY_NOT_FOUND && error != HA_ERR_END_OF_FILE)
+ table->file->print_error(error, MYF(0));
+ DBUG_PRINT("info",("server not found!"));
+ error= ER_FOREIGN_SERVER_DOESNT_EXIST;
+ }
+ else
+ {
+ if ((error= table->file->ha_delete_row(table->record[0])))
+ table->file->print_error(error, MYF(0));
+ }
+
+ DBUG_RETURN(error);
+}
+
+/*
+
+ SYNOPSIS
+ create_server()
+ THD *thd
+ LEX_SERVER_OPTIONS *server_options
+
+ NOTES
+
+ RETURN VALUE
+ 0 - no error
+
+*/
+
+int create_server(THD *thd, LEX_SERVER_OPTIONS *server_options)
+{
+ int error= ER_FOREIGN_SERVER_EXISTS;
+ FOREIGN_SERVER *server;
+
+ DBUG_ENTER("create_server");
+ DBUG_PRINT("info", ("server_options->server_name %s",
+ server_options->server_name));
+
+ rw_wrlock(&THR_LOCK_servers);
+
+ /* hit the memory first */
+ if (hash_search(&servers_cache, (uchar*) server_options->server_name,
+ server_options->server_name_length))
+ goto end;
+
+
+ if (!(server= prepare_server_struct_for_insert(server_options)))
+ {
+ /* purecov: begin inspected */
+ error= ER_OUT_OF_RESOURCES;
+ goto end;
+ /* purecov: end */
+ }
+
+ error= insert_server(thd, server);
+
+ DBUG_PRINT("info", ("error returned %d", error));
+
+end:
+ rw_unlock(&THR_LOCK_servers);
+ DBUG_RETURN(error);
+}
+
+
+/*
+
+ SYNOPSIS
+ alter_server()
+ THD *thd
+ LEX_SERVER_OPTIONS *server_options
+
+ NOTES
+
+ RETURN VALUE
+ 0 - no error
+
+*/
+
+int alter_server(THD *thd, LEX_SERVER_OPTIONS *server_options)
+{
+ int error= ER_FOREIGN_SERVER_DOESNT_EXIST;
+ FOREIGN_SERVER *altered, *existing;
+ LEX_STRING name= { server_options->server_name,
+ server_options->server_name_length };
+ DBUG_ENTER("alter_server");
+ DBUG_PRINT("info", ("server_options->server_name %s",
+ server_options->server_name));
+
+ rw_wrlock(&THR_LOCK_servers);
+
+ if (!(existing= (FOREIGN_SERVER *) hash_search(&servers_cache,
+ (uchar*) name.str,
+ name.length)))
+ goto end;
+
+ altered= (FOREIGN_SERVER *)alloc_root(&mem,
+ sizeof(FOREIGN_SERVER));
+
+ prepare_server_struct_for_update(server_options, existing, altered);
+
+ error= update_server(thd, existing, altered);
+
+ /* close the servers table before we call closed_cached_connection_tables */
+ close_thread_tables(thd);
+
+ if (close_cached_connection_tables(thd, FALSE, &name))
+ {
+ push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ ER_UNKNOWN_ERROR, "Server connection in use");
+ }
+
+end:
+ DBUG_PRINT("info", ("error returned %d", error));
+ rw_unlock(&THR_LOCK_servers);
+ DBUG_RETURN(error);
+}
+
+
+/*
+
+ SYNOPSIS
+ prepare_server_struct_for_insert()
+ LEX_SERVER_OPTIONS *server_options
+
+ NOTES
+ As FOREIGN_SERVER members are allocated on mem_root, we do not need to
+ free them in case of error.
+
+ RETURN VALUE
+ On success filled FOREIGN_SERVER, or NULL in case out of memory.
+
+*/
+
+static FOREIGN_SERVER *
+prepare_server_struct_for_insert(LEX_SERVER_OPTIONS *server_options)
+{
+ char *unset_ptr= (char*)"";
+ FOREIGN_SERVER *server;
+ DBUG_ENTER("prepare_server_struct");
+
+ if (!(server= (FOREIGN_SERVER *)alloc_root(&mem, sizeof(FOREIGN_SERVER))))
+ DBUG_RETURN(NULL); /* purecov: inspected */
+
+ /* these two MUST be set */
+ if (!(server->server_name= strdup_root(&mem, server_options->server_name)))
+ DBUG_RETURN(NULL); /* purecov: inspected */
+ server->server_name_length= server_options->server_name_length;
+
+ if (!(server->host= server_options->host ?
+ strdup_root(&mem, server_options->host) : unset_ptr))
+ DBUG_RETURN(NULL); /* purecov: inspected */
+
+ if (!(server->db= server_options->db ?
+ strdup_root(&mem, server_options->db) : unset_ptr))
+ DBUG_RETURN(NULL); /* purecov: inspected */
+
+ if (!(server->username= server_options->username ?
+ strdup_root(&mem, server_options->username) : unset_ptr))
+ DBUG_RETURN(NULL); /* purecov: inspected */
+
+ if (!(server->password= server_options->password ?
+ strdup_root(&mem, server_options->password) : unset_ptr))
+ DBUG_RETURN(NULL); /* purecov: inspected */
+
+ /* set to 0 if not specified */
+ server->port= server_options->port > -1 ?
+ server_options->port : 0;
+
+ if (!(server->socket= server_options->socket ?
+ strdup_root(&mem, server_options->socket) : unset_ptr))
+ DBUG_RETURN(NULL); /* purecov: inspected */
+
+ if (!(server->scheme= server_options->scheme ?
+ strdup_root(&mem, server_options->scheme) : unset_ptr))
+ DBUG_RETURN(NULL); /* purecov: inspected */
+
+ if (!(server->owner= server_options->owner ?
+ strdup_root(&mem, server_options->owner) : unset_ptr))
+ DBUG_RETURN(NULL); /* purecov: inspected */
+
+ DBUG_RETURN(server);
+}
+
+/*
+
+ SYNOPSIS
+ prepare_server_struct_for_update()
+ LEX_SERVER_OPTIONS *server_options
+
+ NOTES
+
+ RETURN VALUE
+ 0 - no error
+
+*/
+
+static void
+prepare_server_struct_for_update(LEX_SERVER_OPTIONS *server_options,
+ FOREIGN_SERVER *existing,
+ FOREIGN_SERVER *altered)
+{
+ DBUG_ENTER("prepare_server_struct_for_update");
+
+ altered->server_name= strdup_root(&mem, server_options->server_name);
+ altered->server_name_length= server_options->server_name_length;
+ DBUG_PRINT("info", ("existing name %s altered name %s",
+ existing->server_name, altered->server_name));
+
+ /*
+ The logic here is this: is this value set AND is it different
+ than the existing value?
+ */
+ altered->host=
+ (server_options->host && (strcmp(server_options->host, existing->host))) ?
+ strdup_root(&mem, server_options->host) : 0;
+
+ altered->db=
+ (server_options->db && (strcmp(server_options->db, existing->db))) ?
+ strdup_root(&mem, server_options->db) : 0;
+
+ altered->username=
+ (server_options->username &&
+ (strcmp(server_options->username, existing->username))) ?
+ strdup_root(&mem, server_options->username) : 0;
+
+ altered->password=
+ (server_options->password &&
+ (strcmp(server_options->password, existing->password))) ?
+ strdup_root(&mem, server_options->password) : 0;
+
+ /*
+ port is initialised to -1, so if unset, it will be -1
+ */
+ altered->port= (server_options->port > -1 &&
+ server_options->port != existing->port) ?
+ server_options->port : -1;
+
+ altered->socket=
+ (server_options->socket &&
+ (strcmp(server_options->socket, existing->socket))) ?
+ strdup_root(&mem, server_options->socket) : 0;
+
+ altered->scheme=
+ (server_options->scheme &&
+ (strcmp(server_options->scheme, existing->scheme))) ?
+ strdup_root(&mem, server_options->scheme) : 0;
+
+ altered->owner=
+ (server_options->owner &&
+ (strcmp(server_options->owner, existing->owner))) ?
+ strdup_root(&mem, server_options->owner) : 0;
+
+ DBUG_VOID_RETURN;
+}
+
+/*
+
+ SYNOPSIS
+ servers_free()
+ bool end
+
+ NOTES
+
+ RETURN VALUE
+ void
+
+*/
+
+void servers_free(bool end)
+{
+ DBUG_ENTER("servers_free");
+ if (!hash_inited(&servers_cache))
+ DBUG_VOID_RETURN;
+ if (!end)
+ {
+ free_root(&mem, MYF(MY_MARK_BLOCKS_FREE));
+ my_hash_reset(&servers_cache);
+ DBUG_VOID_RETURN;
+ }
+ rwlock_destroy(&THR_LOCK_servers);
+ free_root(&mem,MYF(0));
+ hash_free(&servers_cache);
+ DBUG_VOID_RETURN;
+}
+
+
+/*
+ SYNOPSIS
+
+ clone_server(MEM_ROOT *mem_root, FOREIGN_SERVER *orig, FOREIGN_SERVER *buff)
+
+ Create a clone of FOREIGN_SERVER. If the supplied mem_root is of
+ thd->mem_root then the copy is automatically disposed at end of statement.
+
+ NOTES
+
+ ARGS
+ MEM_ROOT pointer (strings are copied into this mem root)
+ FOREIGN_SERVER pointer (made a copy of)
+ FOREIGN_SERVER buffer (if not-NULL, this pointer is returned)
+
+ RETURN VALUE
+ FOREIGN_SEVER pointer (copy of one supplied FOREIGN_SERVER)
+*/
+
+static FOREIGN_SERVER *clone_server(MEM_ROOT *mem, const FOREIGN_SERVER *server,
+ FOREIGN_SERVER *buffer)
+{
+ DBUG_ENTER("sql_server.cc:clone_server");
+
+ if (!buffer)
+ buffer= (FOREIGN_SERVER *) alloc_root(mem, sizeof(FOREIGN_SERVER));
+
+ buffer->server_name= strmake_root(mem, server->server_name,
+ server->server_name_length);
+ buffer->port= server->port;
+ buffer->server_name_length= server->server_name_length;
+
+ /* TODO: We need to examine which of these can really be NULL */
+ buffer->db= server->db ? strdup_root(mem, server->db) : NULL;
+ buffer->scheme= server->scheme ? strdup_root(mem, server->scheme) : NULL;
+ buffer->username= server->username? strdup_root(mem, server->username): NULL;
+ buffer->password= server->password? strdup_root(mem, server->password): NULL;
+ buffer->socket= server->socket ? strdup_root(mem, server->socket) : NULL;
+ buffer->owner= server->owner ? strdup_root(mem, server->owner) : NULL;
+ buffer->host= server->host ? strdup_root(mem, server->host) : NULL;
+
+ DBUG_RETURN(buffer);
+}
+
+
+/*
+
+ SYNOPSIS
+ get_server_by_name()
+ const char *server_name
+
+ NOTES
+
+ RETURN VALUE
+ FOREIGN_SERVER *
+
+*/
+
+FOREIGN_SERVER *get_server_by_name(MEM_ROOT *mem, const char *server_name,
+ FOREIGN_SERVER *buff)
+{
+ size_t server_name_length;
+ FOREIGN_SERVER *server;
+ DBUG_ENTER("get_server_by_name");
+ DBUG_PRINT("info", ("server_name %s", server_name));
+
+ server_name_length= strlen(server_name);
+
+ if (! server_name || !strlen(server_name))
+ {
+ DBUG_PRINT("info", ("server_name not defined!"));
+ DBUG_RETURN((FOREIGN_SERVER *)NULL);
+ }
+
+ 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)))
+ {
+ DBUG_PRINT("info", ("server_name %s length %d not found!",
+ server_name, server_name_length));
+ server= (FOREIGN_SERVER *) NULL;
+ }
+ /* otherwise, make copy of server */
+ else
+ server= clone_server(mem, server, buff);
+
+ DBUG_PRINT("info", ("unlocking servers_cache"));
+ rw_unlock(&THR_LOCK_servers);
+ DBUG_RETURN(server);
+
+}
diff --git a/sql/sql_servers.h b/sql/sql_servers.h
new file mode 100644
index 00000000000..63c691893d1
--- /dev/null
+++ b/sql/sql_servers.h
@@ -0,0 +1,43 @@
+/* Copyright (C) 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 St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "slave.h" // for tables_ok(), rpl_filter
+
+/* structs */
+typedef struct st_federated_server
+{
+ char *server_name;
+ long port;
+ uint server_name_length;
+ char *db, *scheme, *username, *password, *socket, *owner, *host, *sport;
+} FOREIGN_SERVER;
+
+/* cache handlers */
+bool servers_init(bool dont_read_server_table);
+bool servers_reload(THD *thd);
+void servers_free(bool end=0);
+
+/* insert functions */
+int create_server(THD *thd, LEX_SERVER_OPTIONS *server_options);
+
+/* drop functions */
+int drop_server(THD *thd, LEX_SERVER_OPTIONS *server_options);
+
+/* update functions */
+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);
diff --git a/sql/sql_show.cc b/sql/sql_show.cc
index 5f4ece810b8..3c4dc103ddd 100644
--- a/sql/sql_show.cc
+++ b/sql/sql_show.cc
@@ -1,4 +1,4 @@
-/* Copyright (C) 2000-2004 MySQL AB
+/* Copyright 2000-2008 MySQL AB, 2008 Sun Microsystems, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -18,15 +18,51 @@
#include "mysql_priv.h"
#include "sql_select.h" // For select_describe
+#include "sql_show.h"
#include "repl_failsafe.h"
#include "sp.h"
#include "sp_head.h"
#include "sql_trigger.h"
+#include "authors.h"
+#include "contributors.h"
+#ifdef HAVE_EVENT_SCHEDULER
+#include "events.h"
+#include "event_data_objects.h"
+#endif
#include <my_dir.h>
-#ifdef HAVE_BERKELEY_DB
-#include "ha_berkeley.h" // For berkeley_show_logs
+#define STR_OR_NIL(S) ((S) ? (S) : "<nil>")
+
+#ifdef WITH_PARTITION_STORAGE_ENGINE
+#include "ha_partition.h"
#endif
+enum enum_i_s_events_fields
+{
+ ISE_EVENT_CATALOG= 0,
+ ISE_EVENT_SCHEMA,
+ ISE_EVENT_NAME,
+ ISE_DEFINER,
+ ISE_TIME_ZONE,
+ ISE_EVENT_BODY,
+ ISE_EVENT_DEFINITION,
+ ISE_EVENT_TYPE,
+ ISE_EXECUTE_AT,
+ ISE_INTERVAL_VALUE,
+ ISE_INTERVAL_FIELD,
+ ISE_SQL_MODE,
+ ISE_STARTS,
+ ISE_ENDS,
+ ISE_STATUS,
+ ISE_ON_COMPLETION,
+ ISE_CREATED,
+ ISE_LAST_ALTERED,
+ ISE_LAST_EXECUTED,
+ ISE_EVENT_COMMENT,
+ ISE_ORIGINATOR,
+ ISE_CLIENT_CS,
+ ISE_CONNECTION_CL,
+ ISE_DB_CL
+};
#ifndef NO_EMBEDDED_ACCESS_CHECKS
static const char *grant_names[]={
@@ -38,55 +74,200 @@ static TYPELIB grant_types = { sizeof(grant_names)/sizeof(char **),
grant_names, NULL};
#endif
-static int
-store_create_info(THD *thd, TABLE_LIST *table_list, String *packet);
+/* Match the values of enum ha_choice */
+static const char *ha_choice_values[] = {"", "0", "1"};
+
+static void store_key_options(THD *thd, String *packet, TABLE *table,
+ KEY *key_info);
+
static void
append_algorithm(TABLE_LIST *table, String *buff);
-static int
-view_store_create_info(THD *thd, TABLE_LIST *table, String *buff);
-bool schema_table_store_record(THD *thd, TABLE *table);
+
+static COND * make_cond_for_info_schema(COND *cond, TABLE_LIST *table);
+
+/***************************************************************************
+** List all table types supported
+***************************************************************************/
+
+static int make_version_string(char *buf, int buf_length, uint version)
+{
+ return my_snprintf(buf, buf_length, "%d.%d", version>>8,version&0xff);
+}
+
+static my_bool show_plugins(THD *thd, plugin_ref plugin,
+ void *arg)
+{
+ TABLE *table= (TABLE*) arg;
+ struct st_mysql_plugin *plug= plugin_decl(plugin);
+ struct st_plugin_dl *plugin_dl= plugin_dlib(plugin);
+ CHARSET_INFO *cs= system_charset_info;
+ char version_buf[20];
+
+ restore_record(table, s->default_values);
+
+ table->field[0]->store(plugin_name(plugin)->str,
+ plugin_name(plugin)->length, cs);
+
+ table->field[1]->store(version_buf,
+ make_version_string(version_buf, sizeof(version_buf), plug->version),
+ cs);
+
+
+ switch (plugin_state(plugin)) {
+ /* case PLUGIN_IS_FREED: does not happen */
+ case PLUGIN_IS_DELETED:
+ table->field[2]->store(STRING_WITH_LEN("DELETED"), cs);
+ break;
+ case PLUGIN_IS_UNINITIALIZED:
+ table->field[2]->store(STRING_WITH_LEN("INACTIVE"), cs);
+ break;
+ case PLUGIN_IS_READY:
+ table->field[2]->store(STRING_WITH_LEN("ACTIVE"), cs);
+ break;
+ case PLUGIN_IS_DISABLED:
+ table->field[2]->store(STRING_WITH_LEN("DISABLED"), cs);
+ break;
+ default:
+ DBUG_ASSERT(0);
+ }
+
+ table->field[3]->store(plugin_type_names[plug->type].str,
+ plugin_type_names[plug->type].length,
+ cs);
+ table->field[4]->store(version_buf,
+ make_version_string(version_buf, sizeof(version_buf),
+ *(uint *)plug->info), cs);
+
+ if (plugin_dl)
+ {
+ table->field[5]->store(plugin_dl->dl.str, plugin_dl->dl.length, cs);
+ table->field[5]->set_notnull();
+ table->field[6]->store(version_buf,
+ make_version_string(version_buf, sizeof(version_buf),
+ plugin_dl->version),
+ cs);
+ table->field[6]->set_notnull();
+ }
+ else
+ {
+ table->field[5]->set_null();
+ table->field[6]->set_null();
+ }
+
+
+ if (plug->author)
+ {
+ table->field[7]->store(plug->author, strlen(plug->author), cs);
+ table->field[7]->set_notnull();
+ }
+ else
+ table->field[7]->set_null();
+
+ if (plug->descr)
+ {
+ table->field[8]->store(plug->descr, strlen(plug->descr), cs);
+ table->field[8]->set_notnull();
+ }
+ else
+ table->field[8]->set_null();
+
+ switch (plug->license) {
+ case PLUGIN_LICENSE_GPL:
+ table->field[9]->store(PLUGIN_LICENSE_GPL_STRING,
+ strlen(PLUGIN_LICENSE_GPL_STRING), cs);
+ break;
+ case PLUGIN_LICENSE_BSD:
+ table->field[9]->store(PLUGIN_LICENSE_BSD_STRING,
+ strlen(PLUGIN_LICENSE_BSD_STRING), cs);
+ break;
+ default:
+ table->field[9]->store(PLUGIN_LICENSE_PROPRIETARY_STRING,
+ strlen(PLUGIN_LICENSE_PROPRIETARY_STRING), cs);
+ break;
+ }
+ table->field[9]->set_notnull();
+
+ return schema_table_store_record(thd, table);
+}
+
+
+int fill_plugins(THD *thd, TABLE_LIST *tables, COND *cond)
+{
+ DBUG_ENTER("fill_plugins");
+ TABLE *table= tables->table;
+
+ if (plugin_foreach_with_mask(thd, show_plugins, MYSQL_ANY_PLUGIN,
+ ~PLUGIN_IS_FREED, table))
+ DBUG_RETURN(1);
+
+ DBUG_RETURN(0);
+}
/***************************************************************************
-** List all table types supported
+** List all Authors.
+** If you can update it, you get to be in it :)
***************************************************************************/
-bool mysqld_show_storage_engines(THD *thd)
+bool mysqld_show_authors(THD *thd)
{
List<Item> field_list;
Protocol *protocol= thd->protocol;
- DBUG_ENTER("mysqld_show_storage_engines");
+ DBUG_ENTER("mysqld_show_authors");
- field_list.push_back(new Item_empty_string("Engine",10));
- field_list.push_back(new Item_empty_string("Support",10));
+ field_list.push_back(new Item_empty_string("Name",40));
+ field_list.push_back(new Item_empty_string("Location",40));
field_list.push_back(new Item_empty_string("Comment",80));
if (protocol->send_fields(&field_list,
Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
DBUG_RETURN(TRUE);
- const char *default_type_name=
- ha_get_storage_engine((enum db_type)thd->variables.table_type);
-
- handlerton **types;
- for (types= sys_table_types; *types; types++)
+ show_table_authors_st *authors;
+ for (authors= show_table_authors; authors->name; authors++)
{
- if (!((*types)->flags & HTON_HIDDEN))
- {
- protocol->prepare_for_resend();
- protocol->store((*types)->name, system_charset_info);
- const char *option_name= show_comp_option_name[(int) (*types)->state];
+ protocol->prepare_for_resend();
+ protocol->store(authors->name, system_charset_info);
+ protocol->store(authors->location, system_charset_info);
+ protocol->store(authors->comment, system_charset_info);
+ if (protocol->write())
+ DBUG_RETURN(TRUE);
+ }
+ my_eof(thd);
+ DBUG_RETURN(FALSE);
+}
- if ((*types)->state == SHOW_OPTION_YES &&
- !my_strcasecmp(system_charset_info, default_type_name, (*types)->name))
- option_name= "DEFAULT";
- protocol->store(option_name, system_charset_info);
- protocol->store((*types)->comment, system_charset_info);
- if (protocol->write())
- DBUG_RETURN(TRUE);
- }
+
+/***************************************************************************
+** List all Contributors.
+** Please get permission before updating
+***************************************************************************/
+
+bool mysqld_show_contributors(THD *thd)
+{
+ List<Item> field_list;
+ Protocol *protocol= thd->protocol;
+ DBUG_ENTER("mysqld_show_contributors");
+
+ field_list.push_back(new Item_empty_string("Name",40));
+ field_list.push_back(new Item_empty_string("Location",40));
+ field_list.push_back(new Item_empty_string("Comment",80));
+
+ if (protocol->send_fields(&field_list,
+ Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
+ DBUG_RETURN(TRUE);
+
+ show_table_contributors_st *contributors;
+ for (contributors= show_table_contributors; contributors->name; contributors++)
+ {
+ protocol->prepare_for_resend();
+ protocol->store(contributors->name, system_charset_info);
+ protocol->store(contributors->location, system_charset_info);
+ protocol->store(contributors->comment, system_charset_info);
+ if (protocol->write())
+ DBUG_RETURN(TRUE);
}
- send_eof(thd);
+ my_eof(thd);
DBUG_RETURN(FALSE);
}
@@ -112,6 +293,9 @@ static struct show_privileges_st sys_privileges[]=
{"Create user", "Server Admin", "To create new users"},
{"Delete", "Tables", "To delete existing rows"},
{"Drop", "Databases,Tables", "To drop databases, tables, and views"},
+#ifdef HAVE_EVENT_SCHEDULER
+ {"Event","Server Admin","To create, alter, drop and execute events"},
+#endif
{"Execute", "Functions,Procedures", "To execute stored routines"},
{"File", "File access on server", "To read and write files on the server"},
{"Grant option", "Databases,Tables,Functions,Procedures", "To give to other users those privileges you possess"},
@@ -128,6 +312,7 @@ static struct show_privileges_st sys_privileges[]=
{"Show view","Tables","To see views with SHOW CREATE VIEW"},
{"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"},
{"Update", "Tables", "To update existing rows"},
{"Usage","Server Admin","No privileges - allow connect only"},
{NullS, NullS, NullS}
@@ -141,7 +326,7 @@ bool mysqld_show_privileges(THD *thd)
field_list.push_back(new Item_empty_string("Privilege",10));
field_list.push_back(new Item_empty_string("Context",15));
- field_list.push_back(new Item_empty_string("Comment",NAME_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))
@@ -157,7 +342,7 @@ bool mysqld_show_privileges(THD *thd)
if (protocol->write())
DBUG_RETURN(TRUE);
}
- send_eof(thd);
+ my_eof(thd);
DBUG_RETURN(FALSE);
}
@@ -217,8 +402,8 @@ bool mysqld_show_column_types(THD *thd)
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_LEN));
- field_list.push_back(new Item_empty_string("Comment",NAME_LEN));
+ 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))
@@ -245,7 +430,7 @@ bool mysqld_show_column_types(THD *thd)
if (protocol->write())
DBUG_RETURN(TRUE);
}
- send_eof(thd);
+ my_eof(thd);
DBUG_RETURN(FALSE);
}
@@ -269,21 +454,17 @@ bool mysqld_show_column_types(THD *thd)
FIND_FILES_DIR no such directory, or directory can't be read
*/
-enum find_files_result {
- FIND_FILES_OK,
- FIND_FILES_OOM,
- FIND_FILES_DIR
-};
-static
find_files_result
-find_files(THD *thd, List<char> *files, const char *db,
+find_files(THD *thd, List<LEX_STRING> *files, const char *db,
const char *path, const char *wild, bool dir)
{
uint i;
char *ext;
MY_DIR *dirp;
FILEINFO *file;
+ LEX_STRING *file_name= 0;
+ uint file_name_len;
#ifndef NO_EMBEDDED_ACCESS_CHECKS
uint col_access=thd->col_access;
#endif
@@ -293,6 +474,7 @@ find_files(THD *thd, List<char> *files, const char *db,
if (wild && !wild[0])
wild=0;
+
bzero((char*) &table_list,sizeof(table_list));
if (!(dirp = my_dir(path,MYF(dir ? MY_WANT_STAT : 0))))
@@ -306,9 +488,14 @@ find_files(THD *thd, List<char> *files, const char *db,
for (i=0 ; i < (uint) dirp->number_off_files ; i++)
{
+ char uname[NAME_LEN + 1]; /* Unencoded name */
file=dirp->dir_entry+i;
if (dir)
{ /* Return databases */
+ if ((file->name[0] == '.' &&
+ ((file->name[1] == '.' && file->name[2] == '\0') ||
+ file->name[1] == '\0')))
+ continue; /* . or .. */
#ifdef USE_SYMDIR
char *ext;
char buff[FN_REFLEN];
@@ -325,25 +512,35 @@ find_files(THD *thd, List<char> *files, const char *db,
continue;
}
#endif
- if (file->name[0] == '.' || !MY_S_ISDIR(file->mystat->st_mode) ||
- (wild && wild_compare(file->name,wild,0)))
- continue;
+ if (!MY_S_ISDIR(file->mystat->st_mode))
+ continue;
+
+ file_name_len= filename_to_tablename(file->name, uname, sizeof(uname));
+ if (wild && wild_compare(uname, wild, 0))
+ continue;
+ if (!(file_name=
+ thd->make_lex_string(file_name, uname, file_name_len, TRUE)))
+ {
+ my_dirend(dirp);
+ DBUG_RETURN(FIND_FILES_OOM);
+ }
}
else
{
// Return only .frm files which aren't temp files.
- if (my_strcasecmp(system_charset_info, ext=fn_ext(file->name),reg_ext) ||
- is_prefix(file->name,tmp_file_prefix))
+ if (my_strcasecmp(system_charset_info, ext=fn_rext(file->name),reg_ext) ||
+ is_prefix(file->name, tmp_file_prefix))
continue;
*ext=0;
+ file_name_len= filename_to_tablename(file->name, uname, sizeof(uname));
if (wild)
{
if (lower_case_table_names)
{
- if (wild_case_compare(files_charset_info, file->name, wild))
+ if (wild_case_compare(files_charset_info, uname, wild))
continue;
}
- else if (wild_compare(file->name,wild,0))
+ else if (wild_compare(uname, wild, 0))
continue;
}
}
@@ -352,15 +549,17 @@ find_files(THD *thd, List<char> *files, const char *db,
if (db && !(col_access & TABLE_ACLS))
{
table_list.db= (char*) db;
- table_list.db_length= (uint) strlen(db);
- table_list.table_name= file->name;
- table_list.table_name_length= (uint) strlen(file->name);
+ table_list.db_length= strlen(db);
+ table_list.table_name= uname;
+ table_list.table_name_length= file_name_len;
table_list.grant.privilege=col_access;
if (check_grant(thd, TABLE_ACLS, &table_list, 1, 1, 1))
continue;
}
#endif
- if (files->push_back(thd->strdup(file->name)))
+ if (!(file_name=
+ thd->make_lex_string(file_name, uname, file_name_len, TRUE)) ||
+ files->push_back(file_name))
{
my_dirend(dirp);
DBUG_RETURN(FIND_FILES_OOM);
@@ -369,7 +568,7 @@ find_files(THD *thd, List<char> *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);
}
@@ -391,7 +590,8 @@ mysqld_show_create(THD *thd, TABLE_LIST *table_list)
/* Only one table for now, but VIEW can involve several tables */
if (open_normal_and_derived_tables(thd, table_list, 0))
{
- if (!table_list->view || thd->net.last_errno != ER_VIEW_INVALID)
+ if (!table_list->view ||
+ thd->is_error() && thd->main_da.sql_errno() != ER_VIEW_INVALID)
DBUG_RETURN(TRUE);
/*
@@ -418,21 +618,30 @@ mysqld_show_create(THD *thd, TABLE_LIST *table_list)
}
buffer.length(0);
+
+ if (table_list->view)
+ buffer.set_charset(table_list->view_creation_ctx->get_client_cs());
+
if ((table_list->view ?
view_store_create_info(thd, table_list, &buffer) :
- store_create_info(thd, table_list, &buffer)))
+ store_create_info(thd, table_list, &buffer, NULL,
+ FALSE /* show_database */)))
DBUG_RETURN(TRUE);
List<Item> field_list;
if (table_list->view)
{
- field_list.push_back(new Item_empty_string("View",NAME_LEN));
+ field_list.push_back(new Item_empty_string("View",NAME_CHAR_LEN));
field_list.push_back(new Item_empty_string("Create View",
max(buffer.length(),1024)));
+ field_list.push_back(new Item_empty_string("character_set_client",
+ MY_CS_NAME_SIZE));
+ field_list.push_back(new Item_empty_string("collation_connection",
+ MY_CS_NAME_SIZE));
}
else
{
- field_list.push_back(new Item_empty_string("Table",NAME_LEN));
+ field_list.push_back(new Item_empty_string("Table",NAME_CHAR_LEN));
// 1024 is for not to confuse old clients
field_list.push_back(new Item_empty_string("Create Table",
max(buffer.length(),1024)));
@@ -452,16 +661,30 @@ mysqld_show_create(THD *thd, TABLE_LIST *table_list)
else
protocol->store(table_list->table->alias, system_charset_info);
}
- protocol->store(buffer.ptr(), buffer.length(), buffer.charset());
+
+ if (table_list->view)
+ {
+ protocol->store(buffer.ptr(), buffer.length(),
+ table_list->view_creation_ctx->get_client_cs());
+
+ protocol->store(table_list->view_creation_ctx->get_client_cs()->csname,
+ system_charset_info);
+
+ protocol->store(table_list->view_creation_ctx->get_connection_cl()->name,
+ system_charset_info);
+ }
+ else
+ protocol->store(buffer.ptr(), buffer.length(), buffer.charset());
if (protocol->write())
DBUG_RETURN(TRUE);
- send_eof(thd);
+
+ my_eof(thd);
DBUG_RETURN(FALSE);
}
bool mysqld_show_create_db(THD *thd, char *dbname,
- const HA_CREATE_INFO *create_info)
+ HA_CREATE_INFO *create_info)
{
char buff[2048];
String buffer(buff, sizeof(buff), system_charset_info);
@@ -474,24 +697,18 @@ bool mysqld_show_create_db(THD *thd, char *dbname,
Protocol *protocol=thd->protocol;
DBUG_ENTER("mysql_show_create_db");
- if (check_db_name(dbname))
- {
- my_error(ER_WRONG_DB_NAME, MYF(0), dbname);
- DBUG_RETURN(TRUE);
- }
-
#ifndef NO_EMBEDDED_ACCESS_CHECKS
if (test_all_bits(sctx->master_access, DB_ACLS))
db_access=DB_ACLS;
else
db_access= (acl_get(sctx->host, sctx->ip, sctx->priv_user, dbname, 0) |
sctx->master_access);
- if (!(db_access & DB_ACLS) && (!grant_option || check_grant_db(thd,dbname)))
+ if (!(db_access & DB_ACLS) && check_grant_db(thd,dbname))
{
my_error(ER_DBACCESS_DENIED_ERROR, MYF(0),
sctx->priv_user, sctx->host_or_ip, dbname);
- mysql_log.write(thd,COM_INIT_DB,ER(ER_DBACCESS_DENIED_ERROR),
- sctx->priv_user, sctx->host_or_ip, dbname);
+ general_log_print(thd,COM_INIT_DB,ER(ER_DBACCESS_DENIED_ERROR),
+ sctx->priv_user, sctx->host_or_ip, dbname);
DBUG_RETURN(TRUE);
}
#endif
@@ -512,7 +729,7 @@ bool mysqld_show_create_db(THD *thd, char *dbname,
load_db_opt_by_name(thd, dbname, &create);
}
List<Item> field_list;
- field_list.push_back(new Item_empty_string("Database",NAME_LEN));
+ 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,
@@ -520,12 +737,12 @@ bool mysqld_show_create_db(THD *thd, char *dbname,
DBUG_RETURN(TRUE);
protocol->prepare_for_resend();
- protocol->store(dbname, (uint) strlen(dbname), system_charset_info);
+ protocol->store(dbname, strlen(dbname), system_charset_info);
buffer.length(0);
buffer.append(STRING_WITH_LEN("CREATE DATABASE "));
if (create_options & HA_LEX_CREATE_IF_NOT_EXISTS)
buffer.append(STRING_WITH_LEN("/*!32312 IF NOT EXISTS*/ "));
- append_identifier(thd, &buffer, dbname, (uint) strlen(dbname));
+ append_identifier(thd, &buffer, dbname, strlen(dbname));
if (create.default_table_charset)
{
@@ -543,33 +760,10 @@ bool mysqld_show_create_db(THD *thd, char *dbname,
if (protocol->write())
DBUG_RETURN(TRUE);
- send_eof(thd);
+ my_eof(thd);
DBUG_RETURN(FALSE);
}
-bool
-mysqld_show_logs(THD *thd)
-{
- List<Item> field_list;
- Protocol *protocol= thd->protocol;
- DBUG_ENTER("mysqld_show_logs");
-
- field_list.push_back(new Item_empty_string("File",FN_REFLEN));
- field_list.push_back(new Item_empty_string("Type",10));
- field_list.push_back(new Item_empty_string("Status",10));
-
- if (protocol->send_fields(&field_list,
- Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
- DBUG_RETURN(TRUE);
-
-#ifdef HAVE_BERKELEY_DB
- if ((have_berkeley_db == SHOW_OPTION_YES) && berkeley_show_logs(protocol))
- DBUG_RETURN(TRUE);
-#endif
-
- send_eof(thd);
- DBUG_RETURN(FALSE);
-}
/****************************************************************************
@@ -605,10 +799,10 @@ mysqld_list_fields(THD *thd, TABLE_LIST *table_list, const char *wild)
}
}
restore_record(table, s->default_values); // Get empty record
- if (thd->protocol->send_fields(&field_list, Protocol::SEND_DEFAULTS |
- Protocol::SEND_EOF))
+ table->use_all_columns();
+ if (thd->protocol->send_fields(&field_list, Protocol::SEND_DEFAULTS))
DBUG_VOID_RETURN;
- thd->protocol->flush();
+ my_eof(thd);
DBUG_VOID_RETURN;
}
@@ -619,10 +813,11 @@ 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));
+ DBUG_PRINT("enter",("table: %s",table_list->table->s->table_name.str));
protocol->prepare_for_resend();
- if (store_create_info(thd, table_list, packet))
+ if (store_create_info(thd, table_list, packet, NULL,
+ FALSE /* show_database */))
DBUG_RETURN(-1);
if (fd < 0)
@@ -633,7 +828,7 @@ mysqld_dump_create_info(THD *thd, TABLE_LIST *table_list, int fd)
}
else
{
- if (my_write(fd, (const byte*) packet->ptr(), packet->length(),
+ if (my_write(fd, (const uchar*) packet->ptr(), packet->length(),
MYF(MY_WME)))
DBUG_RETURN(-1);
}
@@ -657,6 +852,7 @@ mysqld_dump_create_info(THD *thd, TABLE_LIST *table_list, int fd)
static const char *require_quotes(const char *name, uint name_length)
{
uint length;
+ bool pure_digit= TRUE;
const char *end= name + name_length;
for (; name < end ; name++)
@@ -665,7 +861,11 @@ static const char *require_quotes(const char *name, uint name_length)
length= my_mbcharlen(system_charset_info, chr);
if (length == 1 && !system_charset_info->ident_map[chr])
return name;
+ if (length == 1 && (chr < '0' || chr > '9'))
+ pure_digit= FALSE;
}
+ if (pure_digit)
+ return name;
return 0;
}
@@ -691,7 +891,7 @@ append_identifier(THD *thd, String *packet, const char *name, uint length)
if (q == EOF)
{
- packet->append(name, length, system_charset_info);
+ packet->append(name, length, packet->charset());
return;
}
@@ -709,7 +909,7 @@ append_identifier(THD *thd, String *packet, const char *name, uint length)
uchar chr= (uchar) *name;
length= my_mbcharlen(system_charset_info, chr);
/*
- my_mbcharlen can retur 0 on a wrong multibyte
+ my_mbcharlen can return 0 on a wrong multibyte
sequence. It is possible when upgrading from 4.0,
and identifier contains some accented characters.
The manual says it does not work. So we'll just
@@ -719,7 +919,7 @@ append_identifier(THD *thd, String *packet, const char *name, uint length)
length= 1;
if (length == 1 && chr == (uchar) quote_char)
packet->append(&quote_char, 1, system_charset_info);
- packet->append(name, length, packet->charset());
+ packet->append(name, length, system_charset_info);
}
packet->append(&quote_char, 1, system_charset_info);
}
@@ -791,7 +991,6 @@ 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,
Field *field, String *def_value,
bool quoted)
@@ -858,9 +1057,31 @@ static bool get_field_default_value(THD *thd, TABLE *table,
return has_default;
}
+/*
+ Build a CREATE TABLE statement for a table.
-static int
-store_create_info(THD *thd, TABLE_LIST *table_list, String *packet)
+ SYNOPSIS
+ store_create_info()
+ thd The thread
+ table_list A list containing one table to write statement
+ for.
+ packet Pointer to a string where statement will be
+ written.
+ create_info_arg Pointer to create information that can be used
+ to tailor the format of the statement. Can be
+ NULL, in which case only SQL_MODE is considered
+ when building the statement.
+
+ NOTE
+ Currently always return 0, but might return error code in the
+ future.
+
+ RETURN
+ 0 OK
+ */
+
+int store_create_info(THD *thd, TABLE_LIST *table_list, String *packet,
+ HA_CREATE_INFO *create_info_arg, bool show_database)
{
List<Item> field_list;
char tmp[MAX_FIELD_WIDTH], *for_str, buff[128], def_value_buf[MAX_FIELD_WIDTH];
@@ -874,17 +1095,19 @@ store_create_info(THD *thd, TABLE_LIST *table_list, String *packet)
handler *file= table->file;
TABLE_SHARE *share= table->s;
HA_CREATE_INFO create_info;
- my_bool foreign_db_mode= (thd->variables.sql_mode & (MODE_POSTGRESQL |
- MODE_ORACLE |
- MODE_MSSQL |
- MODE_DB2 |
- MODE_MAXDB |
- MODE_ANSI)) != 0;
- my_bool limited_mysql_mode= (thd->variables.sql_mode &
- (MODE_NO_FIELD_OPTIONS | MODE_MYSQL323 |
- MODE_MYSQL40)) != 0;
+ bool show_table_options= FALSE;
+ bool foreign_db_mode= (thd->variables.sql_mode & (MODE_POSTGRESQL |
+ MODE_ORACLE |
+ MODE_MSSQL |
+ MODE_DB2 |
+ MODE_MAXDB |
+ MODE_ANSI)) != 0;
+ bool limited_mysql_mode= (thd->variables.sql_mode & (MODE_NO_FIELD_OPTIONS |
+ MODE_MYSQL323 |
+ MODE_MYSQL40)) != 0;
+ my_bitmap_map *old_map;
DBUG_ENTER("store_create_info");
- DBUG_PRINT("enter",("table: %s", table->s->table_name));
+ DBUG_PRINT("enter",("table: %s", table->s->table_name.str));
restore_record(table, s->default_values); // Get empty record
@@ -892,13 +1115,47 @@ store_create_info(THD *thd, TABLE_LIST *table_list, String *packet)
packet->append(STRING_WITH_LEN("CREATE TEMPORARY TABLE "));
else
packet->append(STRING_WITH_LEN("CREATE TABLE "));
+ if (create_info_arg &&
+ (create_info_arg->options & HA_LEX_CREATE_IF_NOT_EXISTS))
+ packet->append(STRING_WITH_LEN("IF NOT EXISTS "));
if (table_list->schema_table)
alias= table_list->schema_table->table_name;
else
- alias= (lower_case_table_names == 2 ? table->alias :
- share->table_name);
- append_identifier(thd, packet, alias, (uint) strlen(alias));
+ {
+ if (lower_case_table_names == 2)
+ alias= table->alias;
+ else
+ {
+ alias= share->table_name.str;
+ }
+ }
+
+ /*
+ Print the database before the table name if told to do that. The
+ database name is only printed in the event that it is different
+ from the current database. The main reason for doing this is to
+ avoid having to update gazillions of tests and result files, but
+ it also saves a few bytes of the binary log.
+ */
+ if (show_database)
+ {
+ const LEX_STRING *const db=
+ table_list->schema_table ? &INFORMATION_SCHEMA_NAME : &table->s->db;
+ if (strcmp(db->str, thd->db) != 0)
+ {
+ append_identifier(thd, packet, db->str, db->length);
+ packet->append(STRING_WITH_LEN("."));
+ }
+ }
+
+ append_identifier(thd, packet, alias, strlen(alias));
packet->append(STRING_WITH_LEN(" (\n"));
+ /*
+ We need this to get default values from the table
+ We have to restore the read_set if we are called from insert in case
+ of row based replication.
+ */
+ old_map= tmp_use_all_columns(table, table->read_set);
for (ptr=table->field ; (field= *ptr); ptr++)
{
@@ -908,7 +1165,7 @@ store_create_info(THD *thd, TABLE_LIST *table_list, String *packet)
packet->append(STRING_WITH_LEN(",\n"));
packet->append(STRING_WITH_LEN(" "));
- append_identifier(thd,packet,field->field_name, (uint) strlen(field->field_name));
+ append_identifier(thd,packet,field->field_name, strlen(field->field_name));
packet->append(' ');
// check for surprises from the previous call to Field::sql_type()
if (type.ptr() != tmp)
@@ -924,7 +1181,7 @@ store_create_info(THD *thd, TABLE_LIST *table_list, String *packet)
{
if (field->charset() != share->table_charset)
{
- packet->append(STRING_WITH_LEN(" character set "));
+ packet->append(STRING_WITH_LEN(" CHARACTER SET "));
packet->append(field->charset()->csname);
}
/*
@@ -933,14 +1190,14 @@ store_create_info(THD *thd, TABLE_LIST *table_list, String *packet)
*/
if (!(field->charset()->state & MY_CS_PRIMARY))
{
- packet->append(STRING_WITH_LEN(" collate "));
+ packet->append(STRING_WITH_LEN(" COLLATE "));
packet->append(field->charset()->name);
}
}
if (flags & NOT_NULL_FLAG)
packet->append(STRING_WITH_LEN(" NOT NULL"));
- else if (field->type() == FIELD_TYPE_TIMESTAMP)
+ else if (field->type() == MYSQL_TYPE_TIMESTAMP)
{
/*
TIMESTAMP field require explicit NULL flag, because unlike
@@ -951,17 +1208,17 @@ store_create_info(THD *thd, TABLE_LIST *table_list, String *packet)
if (get_field_default_value(thd, table, field, &def_value, 1))
{
- packet->append(STRING_WITH_LEN(" default "));
+ packet->append(STRING_WITH_LEN(" DEFAULT "));
packet->append(def_value.ptr(), def_value.length(), system_charset_info);
}
if (!limited_mysql_mode && table->timestamp_field == field &&
field->unireg_check != Field::TIMESTAMP_DN_FIELD)
- packet->append(STRING_WITH_LEN(" on update CURRENT_TIMESTAMP"));
+ packet->append(STRING_WITH_LEN(" ON UPDATE CURRENT_TIMESTAMP"));
if (field->unireg_check == Field::NEXT_NUMBER &&
!(thd->variables.sql_mode & MODE_NO_FIELD_OPTIONS))
- packet->append(STRING_WITH_LEN(" auto_increment"));
+ packet->append(STRING_WITH_LEN(" AUTO_INCREMENT"));
if (field->comment.length)
{
@@ -972,6 +1229,8 @@ store_create_info(THD *thd, TABLE_LIST *table_list, String *packet)
key_info= table->key_info;
bzero((char*) &create_info, sizeof(create_info));
+ /* Allow update_create_info to update row type */
+ create_info.row_type= share->row_type;
file->update_create_info(&create_info);
primary_key= share->primary_key;
@@ -984,35 +1243,24 @@ store_create_info(THD *thd, TABLE_LIST *table_list, String *packet)
if (i == primary_key && !strcmp(key_info->name, primary_key_name))
{
found_primary=1;
- packet->append(STRING_WITH_LEN("PRIMARY "));
+ /*
+ No space at end, because a space will be added after where the
+ identifier would go, but that is not added for primary key.
+ */
+ packet->append(STRING_WITH_LEN("PRIMARY KEY"));
}
else if (key_info->flags & HA_NOSAME)
- packet->append(STRING_WITH_LEN("UNIQUE "));
+ packet->append(STRING_WITH_LEN("UNIQUE KEY "));
else if (key_info->flags & HA_FULLTEXT)
- packet->append(STRING_WITH_LEN("FULLTEXT "));
+ packet->append(STRING_WITH_LEN("FULLTEXT KEY "));
else if (key_info->flags & HA_SPATIAL)
- packet->append(STRING_WITH_LEN("SPATIAL "));
- packet->append(STRING_WITH_LEN("KEY "));
+ packet->append(STRING_WITH_LEN("SPATIAL KEY "));
+ else
+ packet->append(STRING_WITH_LEN("KEY "));
if (!found_primary)
- append_identifier(thd, packet, key_info->name, (uint) strlen(key_info->name));
-
- if (!(thd->variables.sql_mode & MODE_NO_KEY_OPTIONS) &&
- !limited_mysql_mode && !foreign_db_mode)
- {
- if (key_info->algorithm == HA_KEY_ALG_BTREE)
- packet->append(STRING_WITH_LEN(" USING BTREE"));
+ append_identifier(thd, packet, key_info->name, strlen(key_info->name));
- if (key_info->algorithm == HA_KEY_ALG_HASH)
- packet->append(STRING_WITH_LEN(" USING HASH"));
-
- // +BAR: send USING only in non-default case: non-spatial rtree
- if ((key_info->algorithm == HA_KEY_ALG_RTREE) &&
- !(key_info->flags & HA_SPATIAL))
- packet->append(STRING_WITH_LEN(" USING RTREE"));
-
- // No need to send USING FULLTEXT, it is sent as FULLTEXT KEY
- }
packet->append(STRING_WITH_LEN(" ("));
for (uint j=0 ; j < key_info->key_parts ; j++,key_part++)
@@ -1022,21 +1270,30 @@ store_create_info(THD *thd, TABLE_LIST *table_list, String *packet)
if (key_part->field)
append_identifier(thd,packet,key_part->field->field_name,
- (uint) strlen(key_part->field->field_name));
+ strlen(key_part->field->field_name));
if (key_part->field &&
(key_part->length !=
table->field[key_part->fieldnr-1]->key_length() &&
!(key_info->flags & (HA_FULLTEXT | HA_SPATIAL))))
{
+ char *end;
buff[0] = '(';
- char* end=int10_to_str((long) key_part->length /
- key_part->field->charset()->mbmaxlen,
- buff + 1,10);
+ end= int10_to_str((long) key_part->length /
+ key_part->field->charset()->mbmaxlen,
+ buff + 1,10);
*end++ = ')';
packet->append(buff,(uint) (end-buff));
}
}
packet->append(')');
+ store_key_options(thd, packet, table, key_info);
+ if (key_info->parser)
+ {
+ LEX_STRING *parser_name= plugin_name(key_info->parser);
+ packet->append(STRING_WITH_LEN(" /*!50100 WITH PARSER "));
+ append_identifier(thd, packet, parser_name->str, parser_name->length);
+ packet->append(STRING_WITH_LEN(" */ "));
+ }
}
/*
@@ -1046,18 +1303,48 @@ store_create_info(THD *thd, TABLE_LIST *table_list, String *packet)
if ((for_str= file->get_foreign_key_create_info()))
{
- packet->append(for_str, (uint) strlen(for_str));
+ packet->append(for_str, strlen(for_str));
file->free_foreign_key_create_info(for_str);
}
packet->append(STRING_WITH_LEN("\n)"));
if (!(thd->variables.sql_mode & MODE_NO_TABLE_OPTIONS) && !foreign_db_mode)
{
- if (thd->variables.sql_mode & (MODE_MYSQL323 | MODE_MYSQL40))
- packet->append(STRING_WITH_LEN(" TYPE="));
+ show_table_options= TRUE;
+ /*
+ 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
+ THEN add ENGINE only if it was used when creating the table
+ */
+ if (!create_info_arg ||
+ (create_info_arg->used_fields & HA_CREATE_USED_ENGINE))
+ {
+ if (thd->variables.sql_mode & (MODE_MYSQL323 | MODE_MYSQL40))
+ packet->append(STRING_WITH_LEN(" TYPE="));
+ else
+ packet->append(STRING_WITH_LEN(" ENGINE="));
+#ifdef WITH_PARTITION_STORAGE_ENGINE
+ if (table->part_info)
+ packet->append(ha_resolve_storage_engine_name(
+ table->part_info->default_engine_type));
else
- packet->append(STRING_WITH_LEN(" ENGINE="));
- packet->append(file->table_type());
+ packet->append(file->table_type());
+#else
+ packet->append(file->table_type());
+#endif
+ }
/*
Add AUTO_INCREMENT=... if there is an AUTO_INCREMENT column,
@@ -1073,7 +1360,7 @@ store_create_info(THD *thd, TABLE_LIST *table_list, String *packet)
if (create_info.auto_increment_value > 1)
{
char *end;
- packet->append(" AUTO_INCREMENT=", 16);
+ packet->append(STRING_WITH_LEN(" AUTO_INCREMENT="));
end= longlong10_to_str(create_info.auto_increment_value, buff,10);
packet->append(buff, (uint) (end - buff));
}
@@ -1083,12 +1370,20 @@ store_create_info(THD *thd, TABLE_LIST *table_list, String *packet)
!(thd->variables.sql_mode & MODE_MYSQL323) &&
!(thd->variables.sql_mode & MODE_MYSQL40))
{
- packet->append(STRING_WITH_LEN(" DEFAULT CHARSET="));
- packet->append(share->table_charset->csname);
- if (!(share->table_charset->state & MY_CS_PRIMARY))
+ /*
+ IF check_create_info
+ THEN add DEFAULT CHARSET only if it was used when creating the table
+ */
+ if (!create_info_arg ||
+ (create_info_arg->used_fields & HA_CREATE_USED_DEFAULT_CHARSET))
{
- packet->append(STRING_WITH_LEN(" COLLATE="));
- packet->append(table->s->table_charset->name);
+ packet->append(STRING_WITH_LEN(" DEFAULT CHARSET="));
+ packet->append(share->table_charset->csname);
+ if (!(share->table_charset->state & MY_CS_PRIMARY))
+ {
+ packet->append(STRING_WITH_LEN(" COLLATE="));
+ packet->append(table->s->table_charset->name);
+ }
}
}
@@ -1120,14 +1415,32 @@ store_create_info(THD *thd, TABLE_LIST *table_list, String *packet)
packet->append(STRING_WITH_LEN(" PACK_KEYS=1"));
if (share->db_create_options & HA_OPTION_NO_PACK_KEYS)
packet->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)
packet->append(STRING_WITH_LEN(" CHECKSUM=1"));
+ if (share->page_checksum != HA_CHOICE_UNDEF)
+ {
+ packet->append(STRING_WITH_LEN(" PAGE_CHECKSUM="));
+ packet->append(ha_choice_values[(uint) share->page_checksum], 1);
+ }
if (share->db_create_options & HA_OPTION_DELAY_KEY_WRITE)
packet->append(STRING_WITH_LEN(" DELAY_KEY_WRITE=1"));
- if (share->row_type != ROW_TYPE_DEFAULT)
+ if (create_info.row_type != ROW_TYPE_DEFAULT)
{
packet->append(STRING_WITH_LEN(" ROW_FORMAT="));
- packet->append(ha_row_type[(uint) share->row_type]);
+ packet->append(ha_row_type[(uint) create_info.row_type]);
+ }
+ if (share->transactional != HA_CHOICE_UNDEF)
+ {
+ packet->append(STRING_WITH_LEN(" TRANSACTIONAL="));
+ packet->append(ha_choice_values[(uint) share->transactional], 1);
+ }
+ if (table->s->key_block_size)
+ {
+ char *end;
+ packet->append(STRING_WITH_LEN(" KEY_BLOCK_SIZE="));
+ end= longlong10_to_str(table->s->key_block_size, buff, 10);
+ packet->append(buff, (uint) (end - buff));
}
table->file->append_create_info(packet);
if (share->comment.length)
@@ -1140,21 +1453,75 @@ store_create_info(THD *thd, TABLE_LIST *table_list, String *packet)
packet->append(STRING_WITH_LEN(" CONNECTION="));
append_unescaped(packet, share->connect_string.str, share->connect_string.length);
}
- if (file->raid_type)
- {
- uint length;
- length= my_snprintf(buff,sizeof(buff),
- " RAID_TYPE=%s RAID_CHUNKS=%d RAID_CHUNKSIZE=%ld",
- my_raid_type(file->raid_type), file->raid_chunks,
- file->raid_chunksize/RAID_BLOCK_SIZE);
- packet->append(buff, length);
- }
append_directory(thd, packet, "DATA", create_info.data_file_name);
append_directory(thd, packet, "INDEX", create_info.index_file_name);
}
+#ifdef WITH_PARTITION_STORAGE_ENGINE
+ {
+ /*
+ 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,
+ &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));
+ }
+ }
+#endif
+ tmp_restore_column_map(table->read_set, old_map);
DBUG_RETURN(0);
}
+
+static void store_key_options(THD *thd, String *packet, TABLE *table,
+ KEY *key_info)
+{
+ bool limited_mysql_mode= (thd->variables.sql_mode &
+ (MODE_NO_FIELD_OPTIONS | MODE_MYSQL323 |
+ MODE_MYSQL40)) != 0;
+ bool foreign_db_mode= (thd->variables.sql_mode & (MODE_POSTGRESQL |
+ MODE_ORACLE |
+ MODE_MSSQL |
+ MODE_DB2 |
+ MODE_MAXDB |
+ MODE_ANSI)) != 0;
+ char *end, buff[32];
+
+ if (!(thd->variables.sql_mode & MODE_NO_KEY_OPTIONS) &&
+ !limited_mysql_mode && !foreign_db_mode)
+ {
+
+ if (key_info->algorithm == HA_KEY_ALG_BTREE)
+ packet->append(STRING_WITH_LEN(" USING BTREE"));
+
+ if (key_info->algorithm == HA_KEY_ALG_HASH)
+ packet->append(STRING_WITH_LEN(" USING HASH"));
+
+ /* send USING only in non-default case: non-spatial rtree */
+ if ((key_info->algorithm == HA_KEY_ALG_RTREE) &&
+ !(key_info->flags & HA_SPATIAL))
+ packet->append(STRING_WITH_LEN(" USING RTREE"));
+
+ if ((key_info->flags & HA_USES_BLOCK_SIZE) &&
+ table->s->key_block_size != key_info->block_size)
+ {
+ packet->append(STRING_WITH_LEN(" KEY_BLOCK_SIZE="));
+ end= longlong10_to_str(key_info->block_size, buff, 10);
+ packet->append(buff, (uint) (end - buff));
+ }
+ }
+}
+
+
void
view_store_options(THD *thd, TABLE_LIST *table, String *buff)
{
@@ -1196,7 +1563,6 @@ static void append_algorithm(TABLE_LIST *table, String *buff)
}
}
-
/*
Append DEFINER clause to the given buffer.
@@ -1219,7 +1585,7 @@ void append_definer(THD *thd, String *buffer, const LEX_STRING *definer_user,
}
-static int
+int
view_store_create_info(THD *thd, TABLE_LIST *table, String *buff)
{
my_bool foreign_db_mode= (thd->variables.sql_mode & (MODE_POSTGRESQL |
@@ -1269,7 +1635,7 @@ view_store_create_info(THD *thd, TABLE_LIST *table, String *buff)
We can't just use table->query, because our SQL_MODE may trigger
a different syntax, like when ANSI_QUOTES is defined.
*/
- table->view->unit.print(buff);
+ table->view->unit.print(buff, QT_ORDINARY);
if (table->with_check != VIEW_CHECK_NONE)
{
@@ -1321,10 +1687,10 @@ void mysqld_list_processes(THD *thd,const char *user, bool verbose)
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("Host",LIST_PROCESS_HOST_LEN));
- field_list.push_back(field=new Item_empty_string("db",NAME_LEN));
+ field_list.push_back(field=new Item_empty_string("db",NAME_CHAR_LEN));
field->maybe_null=1;
field_list.push_back(new Item_empty_string("Command",16));
- field_list.push_back(new Item_return_int("Time",7, FIELD_TYPE_LONG));
+ field_list.push_back(new Item_return_int("Time",7, MYSQL_TYPE_LONG));
field_list.push_back(field=new Item_empty_string("State",30));
field->maybe_null=1;
field_list.push_back(field=new Item_empty_string("Info",max_query_length));
@@ -1354,7 +1720,7 @@ void mysqld_list_processes(THD *thd,const char *user, bool verbose)
if (tmp->peer_port && (tmp_sctx->host || tmp_sctx->ip) &&
thd->security_ctx->host_or_ip[0])
{
- if ((thd_info->host= thd->alloc(LIST_PROCESS_HOST_LEN+1)))
+ 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);
}
@@ -1385,15 +1751,7 @@ void mysqld_list_processes(THD *thd,const char *user, bool verbose)
if (mysys_var)
pthread_mutex_unlock(&mysys_var->mutex);
-#if !defined(DONT_USE_THR_ALARM) && ! defined(SCO)
- if (pthread_kill(tmp->real_id,0))
- tmp->proc_info="*** DEAD ***"; // This shouldn't happen
-#endif
-#ifdef EXTRA_DEBUG
- thd_info->start_time= tmp->time_after_lock;
-#else
thd_info->start_time= tmp->start_time;
-#endif
thd_info->query=0;
if (tmp->query)
{
@@ -1412,7 +1770,7 @@ void mysqld_list_processes(THD *thd,const char *user, bool verbose)
VOID(pthread_mutex_unlock(&LOCK_thread_count));
thread_info *thd_info;
- time_t now= time(0);
+ time_t now= my_time(0);
while ((thd_info=thread_infos.get()))
{
protocol->prepare_for_resend();
@@ -1423,7 +1781,7 @@ void mysqld_list_processes(THD *thd,const char *user, bool verbose)
if (thd_info->proc_info)
protocol->store(thd_info->proc_info, system_charset_info);
else
- protocol->store(command_name[thd_info->command], system_charset_info);
+ protocol->store(command_name[thd_info->command].str, system_charset_info);
if (thd_info->start_time)
protocol->store((uint32) (now - thd_info->start_time));
else
@@ -1433,53 +1791,365 @@ void mysqld_list_processes(THD *thd,const char *user, bool verbose)
if (protocol->write())
break; /* purecov: inspected */
}
- send_eof(thd);
+ my_eof(thd);
DBUG_VOID_RETURN;
}
+int fill_schema_processlist(THD* thd, TABLE_LIST* tables, COND* cond)
+{
+ TABLE *table= tables->table;
+ CHARSET_INFO *cs= system_charset_info;
+ char *user;
+ time_t now= my_time(0);
+ DBUG_ENTER("fill_process_list");
+
+ user= thd->security_ctx->master_access & PROCESS_ACL ?
+ NullS : thd->security_ctx->priv_user;
+
+ VOID(pthread_mutex_lock(&LOCK_thread_count));
+
+ if (!thd->killed)
+ {
+ 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;
+ const char *val;
+
+ if ((!tmp->vio_ok() && !tmp->system_thread) ||
+ (user && (!tmp_sctx->user || strcmp(tmp_sctx->user, user))))
+ continue;
+
+ restore_record(table, s->default_values);
+ /* ID */
+ table->field[0]->store((longlong) tmp->thread_id, TRUE);
+ /* USER */
+ val= tmp_sctx->user ? tmp_sctx->user :
+ (tmp->system_thread ? "system user" : "unauthenticated user");
+ table->field[1]->store(val, strlen(val), cs);
+ /* HOST */
+ if (tmp->peer_port && (tmp_sctx->host || tmp_sctx->ip) &&
+ thd->security_ctx->host_or_ip[0])
+ {
+ char host[LIST_PROCESS_HOST_LEN + 1];
+ my_snprintf(host, LIST_PROCESS_HOST_LEN, "%s:%u",
+ tmp_sctx->host_or_ip, tmp->peer_port);
+ table->field[2]->store(host, strlen(host), cs);
+ }
+ else
+ table->field[2]->store(tmp_sctx->host_or_ip,
+ strlen(tmp_sctx->host_or_ip), cs);
+ /* DB */
+ if (tmp->db)
+ {
+ table->field[3]->store(tmp->db, strlen(tmp->db), cs);
+ table->field[3]->set_notnull();
+ }
+
+ if ((mysys_var= tmp->mysys_var))
+ pthread_mutex_lock(&mysys_var->mutex);
+ /* COMMAND */
+ if ((val= (char *) (tmp->killed == THD::KILL_CONNECTION? "Killed" : 0)))
+ table->field[4]->store(val, strlen(val), cs);
+ else
+ table->field[4]->store(command_name[tmp->command].str,
+ command_name[tmp->command].length, cs);
+ /* MYSQL_TIME */
+ table->field[5]->store((uint32)(tmp->start_time ?
+ now - tmp->start_time : 0), TRUE);
+ /* STATE */
+#ifndef EMBEDDED_LIBRARY
+ val= (char*) (tmp->locked ? "Locked" :
+ 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 *) "Writing to net";
+#endif
+ if (val)
+ {
+ table->field[6]->store(val, strlen(val), cs);
+ table->field[6]->set_notnull();
+ }
+
+ if (mysys_var)
+ pthread_mutex_unlock(&mysys_var->mutex);
+
+ /* INFO */
+ if (tmp->query)
+ {
+ table->field[7]->store(tmp->query,
+ min(PROCESS_LIST_INFO_WIDTH,
+ tmp->query_length), cs);
+ table->field[7]->set_notnull();
+ }
+
+ if (schema_table_store_record(thd, table))
+ {
+ VOID(pthread_mutex_unlock(&LOCK_thread_count));
+ DBUG_RETURN(1);
+ }
+ }
+ }
+
+ VOID(pthread_mutex_unlock(&LOCK_thread_count));
+ DBUG_RETURN(0);
+}
+
/*****************************************************************************
Status functions
*****************************************************************************/
+static DYNAMIC_ARRAY all_status_vars;
+static bool status_vars_inited= 0;
+static int show_var_cmp(const void *var1, const void *var2)
+{
+ return strcmp(((SHOW_VAR*)var1)->name, ((SHOW_VAR*)var2)->name);
+}
+
+/*
+ deletes all the SHOW_UNDEF elements from the array and calls
+ delete_dynamic() if it's completely empty.
+*/
+static void shrink_var_array(DYNAMIC_ARRAY *array)
+{
+ uint a,b;
+ SHOW_VAR *all= dynamic_element(array, 0, SHOW_VAR *);
+
+ for (a= b= 0; b < array->elements; b++)
+ if (all[b].type != SHOW_UNDEF)
+ all[a++]= all[b];
+ if (a)
+ {
+ bzero(all+a, sizeof(SHOW_VAR)); // writing NULL-element to the end
+ array->elements= a;
+ }
+ else // array is completely empty - delete it
+ delete_dynamic(array);
+}
+
+/*
+ Adds an array of SHOW_VAR entries to the output of SHOW STATUS
+
+ SYNOPSIS
+ add_status_vars(SHOW_VAR *list)
+ list - an array of SHOW_VAR entries to add to all_status_vars
+ the last entry must be {0,0,SHOW_UNDEF}
+
+ NOTE
+ The handling of all_status_vars[] is completely internal, it's allocated
+ automatically when something is added to it, and deleted completely when
+ the last entry is removed.
+
+ As a special optimization, if add_status_vars() is called before
+ init_status_vars(), it assumes "startup mode" - neither concurrent access
+ to the array nor SHOW STATUS are possible (thus it skips locks and qsort)
+
+ The last entry of the all_status_vars[] should always be {0,0,SHOW_UNDEF}
+*/
+int add_status_vars(SHOW_VAR *list)
+{
+ int res= 0;
+ if (status_vars_inited)
+ pthread_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))
+ {
+ res= 1;
+ goto err;
+ }
+ while (list->name)
+ res|= insert_dynamic(&all_status_vars, (uchar*)list++);
+ res|= insert_dynamic(&all_status_vars, (uchar*)list); // appending NULL-element
+ all_status_vars.elements--; // but next insert_dynamic should overwite it
+ if (status_vars_inited)
+ sort_dynamic(&all_status_vars, show_var_cmp);
+err:
+ if (status_vars_inited)
+ pthread_mutex_unlock(&LOCK_status);
+ return res;
+}
+
+/*
+ Make all_status_vars[] usable for SHOW STATUS
+
+ NOTE
+ See add_status_vars(). Before init_status_vars() call, add_status_vars()
+ works in a special fast "startup" mode. Thus init_status_vars()
+ should be called as late as possible but before enabling multi-threading.
+*/
+void init_status_vars()
+{
+ status_vars_inited=1;
+ sort_dynamic(&all_status_vars, show_var_cmp);
+}
+
+void reset_status_vars()
+{
+ SHOW_VAR *ptr= (SHOW_VAR*) all_status_vars.buffer;
+ SHOW_VAR *last= ptr + all_status_vars.elements;
+ for (; ptr < last; ptr++)
+ {
+ /* Note that SHOW_LONG_NOFLUSH variables are not reset */
+ if (ptr->type == SHOW_LONG)
+ *(ulong*) ptr->value= 0;
+ }
+}
+
+/*
+ catch-all cleanup function, cleans up everything no matter what
+
+ DESCRIPTION
+ This function is not strictly required if all add_to_status/
+ remove_status_vars are properly paired, but it's a safety measure that
+ deletes everything from the all_status_vars[] even if some
+ remove_status_vars were forgotten
+*/
+void free_status_vars()
+{
+ delete_dynamic(&all_status_vars);
+}
+
+/*
+ Removes an array of SHOW_VAR entries from the output of SHOW STATUS
+
+ SYNOPSIS
+ remove_status_vars(SHOW_VAR *list)
+ list - an array of SHOW_VAR entries to remove to all_status_vars
+ the last entry must be {0,0,SHOW_UNDEF}
+
+ NOTE
+ there's lots of room for optimizing this, especially in non-sorted mode,
+ but nobody cares - it may be called only in case of failed plugin
+ initialization in the mysqld startup.
+*/
+
+void remove_status_vars(SHOW_VAR *list)
+{
+ if (status_vars_inited)
+ {
+ pthread_mutex_lock(&LOCK_status);
+ SHOW_VAR *all= dynamic_element(&all_status_vars, 0, SHOW_VAR *);
+ int a= 0, b= all_status_vars.elements, c= (a+b)/2;
+
+ for (; list->name; list++)
+ {
+ int res= 0;
+ for (a= 0, b= all_status_vars.elements; b-a > 1; c= (a+b)/2)
+ {
+ res= show_var_cmp(list, all+c);
+ if (res < 0)
+ b= c;
+ else if (res > 0)
+ a= c;
+ else
+ break;
+ }
+ if (res == 0)
+ all[c].type= SHOW_UNDEF;
+ }
+ shrink_var_array(&all_status_vars);
+ pthread_mutex_unlock(&LOCK_status);
+ }
+ else
+ {
+ SHOW_VAR *all= dynamic_element(&all_status_vars, 0, SHOW_VAR *);
+ uint i;
+ for (; list->name; list++)
+ {
+ for (i= 0; i < all_status_vars.elements; i++)
+ {
+ if (show_var_cmp(list, all+i))
+ continue;
+ all[i].type= SHOW_UNDEF;
+ break;
+ }
+ }
+ shrink_var_array(&all_status_vars);
+ }
+}
+
+inline void make_upper(char *buf)
+{
+ for (; *buf; buf++)
+ *buf= my_toupper(system_charset_info, *buf);
+}
static bool show_status_array(THD *thd, const char *wild,
- show_var_st *variables,
+ SHOW_VAR *variables,
enum enum_var_type value_type,
struct system_status_var *status_var,
- const char *prefix, TABLE *table)
+ const char *prefix, TABLE *table,
+ bool ucase_names,
+ COND *cond)
{
- char buff[1024], *prefix_end;
- /* the variable name should not be longer then 80 characters */
- char name_buffer[80];
- size_t len;
+ MY_ALIGNED_BYTE_ARRAY(buff_data, SHOW_VAR_FUNC_BUFF_SIZE, long);
+ char * const buff= (char *) &buff_data;
+ char *prefix_end;
+ /* the variable name should not be longer than 64 characters */
+ char name_buffer[64];
+ int len;
LEX_STRING null_lex_str;
+ SHOW_VAR tmp, *var;
+ COND *partial_cond= 0;
+ enum_check_fields save_count_cuted_fields= thd->count_cuted_fields;
+ bool res= FALSE;
CHARSET_INFO *charset= system_charset_info;
DBUG_ENTER("show_status_array");
+ thd->count_cuted_fields= CHECK_FIELD_WARN;
null_lex_str.str= 0; // For sys_var->value_ptr()
null_lex_str.length= 0;
prefix_end=strnmov(name_buffer, prefix, sizeof(name_buffer)-1);
- len= name_buffer + sizeof(name_buffer) - prefix_end;
+ if (*prefix)
+ *prefix_end++= '_';
+ len=name_buffer + sizeof(name_buffer) - prefix_end;
+ partial_cond= make_cond_for_info_schema(cond, table->pos_in_table_list);
for (; variables->name; variables++)
{
- strnmov(prefix_end, variables->name, (uint) len);
+ strnmov(prefix_end, variables->name, len);
name_buffer[sizeof(name_buffer)-1]=0; /* Safety */
- SHOW_TYPE show_type=variables->type;
- if (show_type == SHOW_VARS)
+ if (ucase_names)
+ make_upper(name_buffer);
+
+ restore_record(table, s->default_values);
+ table->field[0]->store(name_buffer, strlen(name_buffer),
+ system_charset_info);
+ /*
+ if var->type is SHOW_FUNC, call the function.
+ Repeat as necessary, if new var is again SHOW_FUNC
+ */
+ for (var=variables; var->type == SHOW_FUNC; var= &tmp)
+ ((mysql_show_var_func)(var->value))(thd, &tmp, buff);
+
+ SHOW_TYPE show_type=var->type;
+ if (show_type == SHOW_ARRAY)
{
- show_status_array(thd, wild, (show_var_st *) variables->value,
- value_type, status_var, variables->name, table);
+ show_status_array(thd, wild, (SHOW_VAR *) var->value, value_type,
+ status_var, name_buffer, table, ucase_names, partial_cond);
}
else
{
if (!(wild && wild[0] && wild_case_compare(system_charset_info,
- name_buffer, wild)))
+ name_buffer, wild)) &&
+ (!partial_cond || partial_cond->val_int()))
{
- char *value=variables->value;
+ char *value=var->value;
const char *pos, *end; // We assign a lot of const's
- long nr;
+
+ pthread_mutex_lock(&LOCK_global_system_variables);
+
if (show_type == SHOW_SYS)
{
sys_var *var= ((sys_var *) value);
@@ -1489,17 +2159,27 @@ static bool show_status_array(THD *thd, const char *wild,
}
pos= end= buff;
+ /*
+ note that value may be == buff. All SHOW_xxx code below
+ should still work in this case
+ */
switch (show_type) {
+ case SHOW_DOUBLE_STATUS:
+ value= ((char *) status_var + (ulong) value);
+ /* fall through */
+ case SHOW_DOUBLE:
+ end= buff + my_sprintf(buff, (buff, "%f", *(double*) value));
+ break;
case SHOW_LONG_STATUS:
- case SHOW_LONG_CONST_STATUS:
value= ((char *) status_var + (ulong) value);
/* fall through */
case SHOW_LONG:
- case SHOW_LONG_CONST:
+ 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 + (ulonglong) value);
+ /* fall through */
case SHOW_LONGLONG:
end= longlong10_to_str(*(longlong*) value, buff, 10);
break;
@@ -1512,7 +2192,6 @@ 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_CONST:
case SHOW_INT:
end= int10_to_str((long) *(uint32*) value, buff, 10);
break;
@@ -1530,295 +2209,45 @@ static bool show_status_array(THD *thd, const char *wild,
end= strend(pos);
break;
}
- case SHOW_STARTTIME:
- nr= (long) (thd->query_start() - server_start_time);
- end= int10_to_str(nr, buff, 10);
- break;
- case SHOW_QUERIES:
- end= int10_to_str((long) thd->query_id, buff, 10);
- break;
-#ifdef HAVE_REPLICATION
- case SHOW_RPL_STATUS:
- end= strmov(buff, rpl_status_type[(int)rpl_status]);
- break;
- case SHOW_SLAVE_RUNNING:
- {
- pthread_mutex_lock(&LOCK_active_mi);
- end= strmov(buff, (active_mi && active_mi->slave_running &&
- active_mi->rli.slave_running) ? "ON" : "OFF");
- pthread_mutex_unlock(&LOCK_active_mi);
- break;
- }
- case SHOW_SLAVE_RETRIED_TRANS:
- {
- /*
- TODO: in 5.1 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);
- if (active_mi)
- {
- pthread_mutex_lock(&active_mi->rli.data_lock);
- end= int10_to_str(active_mi->rli.retried_trans, buff, 10);
- pthread_mutex_unlock(&active_mi->rli.data_lock);
- }
- pthread_mutex_unlock(&LOCK_active_mi);
- break;
- }
- case SHOW_SLAVE_SKIP_ERRORS:
- {
- MY_BITMAP *bitmap= (MY_BITMAP *)value;
- if (!use_slave_mask || bitmap_is_clear_all(bitmap))
- {
- end= strmov(buff, "OFF");
- }
- else if (bitmap_is_set_all(bitmap))
- {
- end= strmov(buff, "ALL");
- }
- else
- {
- /* 10 is enough assuming errors are max 4 digits */
- int i;
- for (i= 1;
- i < MAX_SLAVE_ERROR && (uint) (end-buff) < sizeof(buff)-10;
- i++)
- {
- if (bitmap_is_set(bitmap, i))
- {
- end= int10_to_str(i, (char*) end, 10);
- *(char*) end++= ',';
- }
- }
- if (end != buff)
- end--; // Remove last ','
- if (i < MAX_SLAVE_ERROR)
- end= strmov((char*) end, "..."); // Couldn't show all errors
- }
- break;
- }
-#endif /* HAVE_REPLICATION */
- case SHOW_OPENTABLES:
- end= int10_to_str((long) cached_tables(), buff, 10);
- break;
- case SHOW_CHAR_PTR:
+ case SHOW_CHAR_PTR:
{
if (!(pos= *(char**) value))
pos= "";
end= strend(pos);
break;
}
- case SHOW_DOUBLE_STATUS:
- {
- value= ((char *) status_var + (ulong) value);
- end= buff + sprintf(buff, "%f", *(double*) value);
- break;
- }
-#ifdef HAVE_OPENSSL
- /* First group - functions relying on CTX */
- case SHOW_SSL_CTX_SESS_ACCEPT:
- end= int10_to_str((long) (!ssl_acceptor_fd ? 0 :
- SSL_CTX_sess_accept(ssl_acceptor_fd->
- ssl_context)),
- buff, 10);
- break;
- case SHOW_SSL_CTX_SESS_ACCEPT_GOOD:
- end= int10_to_str((long) (!ssl_acceptor_fd ? 0 :
- SSL_CTX_sess_accept_good(ssl_acceptor_fd->
- ssl_context)),
- buff, 10);
- break;
- case SHOW_SSL_CTX_SESS_CONNECT_GOOD:
- end= int10_to_str((long) (!ssl_acceptor_fd ? 0 :
- SSL_CTX_sess_connect_good(ssl_acceptor_fd->
- ssl_context)),
- buff, 10);
- break;
- case SHOW_SSL_CTX_SESS_ACCEPT_RENEGOTIATE:
- end= int10_to_str((long) (!ssl_acceptor_fd ? 0 :
- SSL_CTX_sess_accept_renegotiate(ssl_acceptor_fd->ssl_context)),
- buff, 10);
- break;
- case SHOW_SSL_CTX_SESS_CONNECT_RENEGOTIATE:
- end= int10_to_str((long) (!ssl_acceptor_fd ? 0 :
- SSL_CTX_sess_connect_renegotiate(ssl_acceptor_fd-> ssl_context)),
- buff, 10);
- break;
- case SHOW_SSL_CTX_SESS_CB_HITS:
- end= int10_to_str((long) (!ssl_acceptor_fd ? 0 :
- SSL_CTX_sess_cb_hits(ssl_acceptor_fd->
- ssl_context)),
- buff, 10);
- break;
- case SHOW_SSL_CTX_SESS_HITS:
- end= int10_to_str((long) (!ssl_acceptor_fd ? 0 :
- SSL_CTX_sess_hits(ssl_acceptor_fd->
- ssl_context)),
- buff, 10);
- break;
- case SHOW_SSL_CTX_SESS_CACHE_FULL:
- end= int10_to_str((long) (!ssl_acceptor_fd ? 0 :
- SSL_CTX_sess_cache_full(ssl_acceptor_fd->
- ssl_context)),
- buff, 10);
- break;
- case SHOW_SSL_CTX_SESS_MISSES:
- end= int10_to_str((long) (!ssl_acceptor_fd ? 0 :
- SSL_CTX_sess_misses(ssl_acceptor_fd->
- ssl_context)),
- buff, 10);
- break;
- case SHOW_SSL_CTX_SESS_TIMEOUTS:
- end= int10_to_str((long) (!ssl_acceptor_fd ? 0 :
- SSL_CTX_sess_timeouts(ssl_acceptor_fd->ssl_context)),
- buff,10);
- break;
- case SHOW_SSL_CTX_SESS_NUMBER:
- end= int10_to_str((long) (!ssl_acceptor_fd ? 0 :
- SSL_CTX_sess_number(ssl_acceptor_fd->ssl_context)),
- buff,10);
- break;
- case SHOW_SSL_CTX_SESS_CONNECT:
- end= int10_to_str((long) (!ssl_acceptor_fd ? 0 :
- SSL_CTX_sess_connect(ssl_acceptor_fd->ssl_context)),
- buff,10);
- break;
- case SHOW_SSL_CTX_SESS_GET_CACHE_SIZE:
- end= int10_to_str((long) (!ssl_acceptor_fd ? 0 :
- SSL_CTX_sess_get_cache_size(ssl_acceptor_fd->ssl_context)),
- buff,10);
- break;
- case SHOW_SSL_CTX_GET_VERIFY_MODE:
- end= int10_to_str((long) (!ssl_acceptor_fd ? 0 :
- SSL_CTX_get_verify_mode(ssl_acceptor_fd->ssl_context)),
- buff,10);
- break;
- case SHOW_SSL_CTX_GET_VERIFY_DEPTH:
- end= int10_to_str((long) (!ssl_acceptor_fd ? 0 :
- SSL_CTX_get_verify_depth(ssl_acceptor_fd->ssl_context)),
- buff,10);
- break;
- case SHOW_SSL_CTX_GET_SESSION_CACHE_MODE:
- if (!ssl_acceptor_fd)
- {
- pos= "NONE";
- end= pos+4;
- break;
- }
- switch (SSL_CTX_get_session_cache_mode(ssl_acceptor_fd->ssl_context))
- {
- case SSL_SESS_CACHE_OFF:
- pos= "OFF";
- break;
- case SSL_SESS_CACHE_CLIENT:
- pos= "CLIENT";
- break;
- case SSL_SESS_CACHE_SERVER:
- pos= "SERVER";
- break;
- case SSL_SESS_CACHE_BOTH:
- pos= "BOTH";
- break;
- case SSL_SESS_CACHE_NO_AUTO_CLEAR:
- pos= "NO_AUTO_CLEAR";
- break;
- case SSL_SESS_CACHE_NO_INTERNAL_LOOKUP:
- pos= "NO_INTERNAL_LOOKUP";
- break;
- default:
- pos= "Unknown";
- break;
- }
- end= strend(pos);
- break;
- /* First group - functions relying on SSL */
- case SHOW_SSL_GET_VERSION:
- pos= (thd->net.vio->ssl_arg ?
- SSL_get_version((SSL*) thd->net.vio->ssl_arg) : "");
- end= strend(pos);
- break;
- case SHOW_SSL_SESSION_REUSED:
- end= int10_to_str((long) (thd->net.vio->ssl_arg ?
- SSL_session_reused((SSL*) thd->net.vio->
- ssl_arg) :
- 0),
- buff, 10);
- break;
- case SHOW_SSL_GET_DEFAULT_TIMEOUT:
- end= int10_to_str((long) (thd->net.vio->ssl_arg ?
- SSL_get_default_timeout((SSL*) thd->net.vio->
- ssl_arg) :
- 0),
- buff, 10);
- break;
- case SHOW_SSL_GET_VERIFY_MODE:
- end= int10_to_str((long) (thd->net.vio->ssl_arg ?
- SSL_get_verify_mode((SSL*) thd->net.vio->
- ssl_arg):
- 0),
- buff, 10);
- break;
- case SHOW_SSL_GET_VERIFY_DEPTH:
- end= int10_to_str((long) (thd->net.vio->ssl_arg ?
- SSL_get_verify_depth((SSL*) thd->net.vio->
- ssl_arg):
- 0),
- buff, 10);
- break;
- case SHOW_SSL_GET_CIPHER:
- pos= (thd->net.vio->ssl_arg ?
- SSL_get_cipher((SSL*) thd->net.vio->ssl_arg) : "" );
- end= strend(pos);
- break;
- case SHOW_SSL_GET_CIPHER_LIST:
- if (thd->net.vio->ssl_arg)
- {
- char *to= buff;
- char *buff_end= buff + sizeof(buff);
- for (int i= 0; to < buff_end; i++)
- {
- const char *p= SSL_get_cipher_list((SSL*) thd->net.vio->ssl_arg,i);
- if (p == NULL)
- break;
- to= strnmov(to, p, (uint) (buff_end-to-1));
- *to++= ':';
- }
- if (to != buff)
- to--; // Remove last ':'
- end= to;
- }
- break;
-
-#endif /* HAVE_OPENSSL */
case SHOW_KEY_CACHE_LONG:
- case SHOW_KEY_CACHE_CONST_LONG:
- value= (value-(char*) &dflt_key_cache_var)+ (char*) dflt_key_cache;
+ value= (char*) dflt_key_cache + (ulong)value;
end= int10_to_str(*(long*) value, buff, 10);
break;
case SHOW_KEY_CACHE_LONGLONG:
- value= (value-(char*) &dflt_key_cache_var)+ (char*) dflt_key_cache;
+ value= (char*) dflt_key_cache + (ulong)value;
end= longlong10_to_str(*(longlong*) value, buff, 10);
break;
- case SHOW_NET_COMPRESSION:
- end= strmov(buff, thd->net.compress ? "ON" : "OFF");
- break;
- case SHOW_UNDEF: // Show never happen
- case SHOW_SYS:
- break; // Return empty string
+ case SHOW_UNDEF:
+ break; // Return empty string
+ case SHOW_SYS: // Cannot happen
default:
+ DBUG_ASSERT(0);
break;
}
- restore_record(table, s->default_values);
- table->field[0]->store(name_buffer, (uint) strlen(name_buffer),
- system_charset_info);
table->field[1]->store(pos, (uint32) (end - pos), charset);
+ thd->count_cuted_fields= CHECK_FIELD_IGNORE;
+ table->field[1]->set_notnull();
+
+ pthread_mutex_unlock(&LOCK_global_system_variables);
+
if (schema_table_store_record(thd, table))
- DBUG_RETURN(TRUE);
+ {
+ res= TRUE;
+ goto end;
+ }
}
}
}
-
- DBUG_RETURN(FALSE);
+end:
+ thd->count_cuted_fields= save_count_cuted_fields;
+ DBUG_RETURN(res);
}
@@ -1846,31 +2275,14 @@ void calc_sum_of_all_status(STATUS_VAR *to)
}
-LEX_STRING *make_lex_string(THD *thd, LEX_STRING *lex_str,
- const char* str, uint length,
- bool allocate_lex_string)
-{
- MEM_ROOT *mem= thd->mem_root;
- if (allocate_lex_string)
- if (!(lex_str= (LEX_STRING *)thd->alloc(sizeof(LEX_STRING))))
- return 0;
- lex_str->str= strmake_root(mem, str, length);
- lex_str->length= length;
- return lex_str;
-}
-
-
-/* INFORMATION_SCHEMA name */
-LEX_STRING INFORMATION_SCHEMA_NAME=
- { (char *) STRING_WITH_LEN("information_schema") };
-
/* This is only used internally, but we need it here as a forward reference */
extern ST_SCHEMA_TABLE schema_tables[];
-typedef struct st_index_field_values
+typedef struct st_lookup_field_values
{
- const char *db_value, *table_value;
-} INDEX_FIELD_VALUES;
+ LEX_STRING db_value, table_value;
+ bool wild_db_value, wild_table_value;
+} LOOKUP_FIELD_VALUES;
/*
@@ -1890,7 +2302,7 @@ typedef struct st_index_field_values
bool schema_table_store_record(THD *thd, TABLE *table)
{
int error;
- if ((error= table->file->write_row(table->record[0])))
+ if ((error= table->file->ha_write_row(table->record[0])))
{
if (create_myisam_from_heap(thd, table,
table->pos_in_table_list->schema_table_param,
@@ -1901,40 +2313,149 @@ bool schema_table_store_record(THD *thd, TABLE *table)
}
-void get_index_field_values(LEX *lex, INDEX_FIELD_VALUES *index_field_values)
+int make_table_list(THD *thd, SELECT_LEX *sel,
+ LEX_STRING *db_name, LEX_STRING *table_name)
{
- const char *wild= lex->wild ? lex->wild->ptr() : NullS;
- switch (lex->orig_sql_command) {
- case SQLCOM_SHOW_DATABASES:
- index_field_values->db_value= wild;
- break;
- case SQLCOM_SHOW_TABLES:
- case SQLCOM_SHOW_TABLE_STATUS:
- case SQLCOM_SHOW_TRIGGERS:
- index_field_values->db_value= lex->select_lex.db;
- index_field_values->table_value= wild;
- break;
- default:
- index_field_values->db_value= NullS;
- index_field_values->table_value= NullS;
- break;
+ Table_ident *table_ident;
+ table_ident= new Table_ident(thd, *db_name, *table_name, 1);
+ sel->init_query();
+ if (!sel->add_table_to_list(thd, table_ident, 0, 0, TL_READ))
+ return 1;
+ return 0;
+}
+
+
+/**
+ @brief Get lookup value from the part of 'WHERE' condition
+
+ @details This function gets lookup value from
+ the part of 'WHERE' condition if it's possible and
+ fill appropriate lookup_field_vals struct field
+ with this value.
+
+ @param[in] thd thread handler
+ @param[in] item_func part of WHERE condition
+ @param[in] table I_S table
+ @param[in, out] lookup_field_vals Struct which holds lookup values
+
+ @return
+ 0 success
+ 1 error, there can be no matching records for the condition
+*/
+
+bool get_lookup_value(THD *thd, Item_func *item_func,
+ TABLE_LIST *table,
+ LOOKUP_FIELD_VALUES *lookup_field_vals)
+{
+ ST_SCHEMA_TABLE *schema_table= table->schema_table;
+ ST_FIELD_INFO *field_info= schema_table->fields_info;
+ const char *field_name1= schema_table->idx_field1 >= 0 ?
+ field_info[schema_table->idx_field1].field_name : "";
+ const char *field_name2= schema_table->idx_field2 >= 0 ?
+ field_info[schema_table->idx_field2].field_name : "";
+
+ if (item_func->functype() == Item_func::EQ_FUNC ||
+ item_func->functype() == Item_func::EQUAL_FUNC)
+ {
+ int idx_field, idx_val;
+ char tmp[MAX_FIELD_WIDTH];
+ String *tmp_str, str_buff(tmp, sizeof(tmp), system_charset_info);
+ Item_field *item_field;
+ CHARSET_INFO *cs= system_charset_info;
+
+ if (item_func->arguments()[0]->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 &&
+ item_func->arguments()[0]->const_item())
+ {
+ idx_field= 1;
+ idx_val= 0;
+ }
+ else
+ return 0;
+
+ item_field= (Item_field*) item_func->arguments()[idx_field];
+ if (table->table != item_field->field->table)
+ return 0;
+ tmp_str= item_func->arguments()[idx_val]->val_str(&str_buff);
+
+ /* impossible value */
+ if (!tmp_str)
+ return 1;
+
+ /* Lookup value is database name */
+ if (!cs->coll->strnncollsp(cs, (uchar *) field_name1, strlen(field_name1),
+ (uchar *) item_field->field_name,
+ strlen(item_field->field_name), 0))
+ {
+ thd->make_lex_string(&lookup_field_vals->db_value, tmp_str->ptr(),
+ tmp_str->length(), FALSE);
+ }
+ /* Lookup value is table name */
+ else if (!cs->coll->strnncollsp(cs, (uchar *) field_name2,
+ strlen(field_name2),
+ (uchar *) item_field->field_name,
+ strlen(item_field->field_name), 0))
+ {
+ thd->make_lex_string(&lookup_field_vals->table_value, tmp_str->ptr(),
+ tmp_str->length(), FALSE);
+ }
}
+ return 0;
}
-int make_table_list(THD *thd, SELECT_LEX *sel,
- char *db, char *table)
+/**
+ @brief Calculates lookup values from 'WHERE' condition
+
+ @details This function calculates lookup value(database name, table name)
+ from 'WHERE' condition if it's possible and
+ fill lookup_field_vals struct fields with these values.
+
+ @param[in] thd thread handler
+ @param[in] cond WHERE condition
+ @param[in] table I_S table
+ @param[in, out] lookup_field_vals Struct which holds lookup values
+
+ @return
+ 0 success
+ 1 error, there can be no matching records for the condition
+*/
+
+bool calc_lookup_values_from_cond(THD *thd, COND *cond, TABLE_LIST *table,
+ LOOKUP_FIELD_VALUES *lookup_field_vals)
{
- Table_ident *table_ident;
- LEX_STRING ident_db, ident_table;
- ident_db.str= db;
- ident_db.length= (uint) strlen(db);
- ident_table.str= table;
- ident_table.length= (uint) strlen(table);
- table_ident= new Table_ident(thd, ident_db, ident_table, 1);
- sel->init_query();
- if (!sel->add_table_to_list(thd, table_ident, 0, 0, TL_READ,
- (List<String> *) 0, (List<String> *) 0))
+ if (!cond)
+ return 0;
+
+ if (cond->type() == Item::COND_ITEM)
+ {
+ if (((Item_cond*) cond)->functype() == Item_func::COND_AND_FUNC)
+ {
+ List_iterator<Item> li(*((Item_cond*) cond)->argument_list());
+ Item *item;
+ while ((item= li++))
+ {
+ if (item->type() == Item::FUNC_ITEM)
+ {
+ if (get_lookup_value(thd, (Item_func*)item, table, lookup_field_vals))
+ return 1;
+ }
+ else
+ {
+ if (calc_lookup_values_from_cond(thd, item, table, lookup_field_vals))
+ return 1;
+ }
+ }
+ }
+ return 0;
+ }
+ else if (cond->type() == Item::FUNC_ITEM &&
+ get_lookup_value(thd, (Item_func*) cond, table, lookup_field_vals))
return 1;
return 0;
}
@@ -1957,21 +2478,23 @@ bool uses_only_table_name_fields(Item *item, TABLE_LIST *table)
CHARSET_INFO *cs= system_charset_info;
ST_SCHEMA_TABLE *schema_table= table->schema_table;
ST_FIELD_INFO *field_info= schema_table->fields_info;
- const char *field_name1= schema_table->idx_field1 >= 0 ? field_info[schema_table->idx_field1].field_name : "";
- const char *field_name2= schema_table->idx_field2 >= 0 ? field_info[schema_table->idx_field2].field_name : "";
+ const char *field_name1= schema_table->idx_field1 >= 0 ?
+ field_info[schema_table->idx_field1].field_name : "";
+ const char *field_name2= schema_table->idx_field2 >= 0 ?
+ field_info[schema_table->idx_field2].field_name : "";
if (table->table != item_field->field->table ||
- (cs->coll->strnncollsp(cs, (uchar *) field_name1, (uint) strlen(field_name1),
- (uchar *) item_field->field_name,
- (uint) strlen(item_field->field_name), 0) &&
- cs->coll->strnncollsp(cs, (uchar *) field_name2, (uint) strlen(field_name2),
- (uchar *) item_field->field_name,
- (uint) strlen(item_field->field_name), 0)))
+ (cs->coll->strnncollsp(cs, (uchar *) field_name1, strlen(field_name1),
+ (uchar *) item_field->field_name,
+ strlen(item_field->field_name), 0) &&
+ cs->coll->strnncollsp(cs, (uchar *) field_name2, strlen(field_name2),
+ (uchar *) item_field->field_name,
+ strlen(item_field->field_name), 0)))
return 0;
}
else if (item->type() == Item::REF_ITEM)
return uses_only_table_name_fields(item->real_item(), table);
- if (item->type() == Item::SUBSELECT_ITEM &&
- !item->const_item())
+
+ if (item->type() == Item::SUBSELECT_ITEM && !item->const_item())
return 0;
return 1;
@@ -2034,6 +2557,62 @@ static COND * make_cond_for_info_schema(COND *cond, TABLE_LIST *table)
}
+/**
+ @brief Calculate lookup values(database name, table name)
+
+ @details This function calculates lookup values(database name, table name)
+ from 'WHERE' condition or wild values (for 'SHOW' commands only)
+ from LEX struct and fill lookup_field_vals struct field
+ with these values.
+
+ @param[in] thd thread handler
+ @param[in] cond WHERE condition
+ @param[in] tables I_S table
+ @param[in, out] lookup_field_values Struct which holds lookup values
+
+ @return
+ 0 success
+ 1 error, there can be no matching records for the condition
+*/
+
+bool get_lookup_field_values(THD *thd, COND *cond, TABLE_LIST *tables,
+ LOOKUP_FIELD_VALUES *lookup_field_values)
+{
+ LEX *lex= thd->lex;
+ const char *wild= lex->wild ? lex->wild->ptr() : NullS;
+ bzero((char*) lookup_field_values, sizeof(LOOKUP_FIELD_VALUES));
+ switch (lex->sql_command) {
+ case SQLCOM_SHOW_DATABASES:
+ if (wild)
+ {
+ lookup_field_values->db_value.str= (char*) wild;
+ lookup_field_values->db_value.length= strlen(wild);
+ lookup_field_values->wild_db_value= 1;
+ }
+ return 0;
+ case SQLCOM_SHOW_TABLES:
+ case SQLCOM_SHOW_TABLE_STATUS:
+ case SQLCOM_SHOW_TRIGGERS:
+ case SQLCOM_SHOW_EVENTS:
+ lookup_field_values->db_value.str= lex->select_lex.db;
+ lookup_field_values->db_value.length=strlen(lex->select_lex.db);
+ if (wild)
+ {
+ lookup_field_values->table_value.str= (char*)wild;
+ lookup_field_values->table_value.length= strlen(wild);
+ lookup_field_values->wild_table_value= 1;
+ }
+ return 0;
+ default:
+ /*
+ The "default" is for queries over I_S.
+ All previous cases handle SHOW commands.
+ */
+ return calc_lookup_values_from_cond(thd, cond, tables, lookup_field_values);
+ }
+}
+
+
enum enum_schema_tables get_schema_table_idx(ST_SCHEMA_TABLE *schema_table)
{
return (enum enum_schema_tables) (schema_table - &schema_tables[0]);
@@ -2051,64 +2630,67 @@ enum enum_schema_tables get_schema_table_idx(ST_SCHEMA_TABLE *schema_table)
idx_field_vals idx_field_vals->db_name contains db name or
wild string
with_i_schema returns 1 if we added 'IS' name to list
- otherwise returns 0
- is_wild_value if value is 1 then idx_field_vals->db_name is
- wild string otherwise it's db name;
+ otherwise returns 0
RETURN
zero success
non-zero error
*/
-int make_db_list(THD *thd, List<char> *files,
- INDEX_FIELD_VALUES *idx_field_vals,
- bool *with_i_schema, bool is_wild_value)
+int make_db_list(THD *thd, List<LEX_STRING> *files,
+ LOOKUP_FIELD_VALUES *lookup_field_vals,
+ bool *with_i_schema)
{
- LEX *lex= thd->lex;
+ LEX_STRING *i_s_name_copy= 0;
+ i_s_name_copy= thd->make_lex_string(i_s_name_copy,
+ INFORMATION_SCHEMA_NAME.str,
+ INFORMATION_SCHEMA_NAME.length, TRUE);
*with_i_schema= 0;
- get_index_field_values(lex, idx_field_vals);
- if (is_wild_value)
+ if (lookup_field_vals->wild_db_value)
{
/*
This part of code is only for SHOW DATABASES command.
idx_field_vals->db_value can be 0 when we don't use
LIKE clause (see also get_index_field_values() function)
*/
- if (!idx_field_vals->db_value ||
+ if (!lookup_field_vals->db_value.str ||
!wild_case_compare(system_charset_info,
INFORMATION_SCHEMA_NAME.str,
- idx_field_vals->db_value))
+ lookup_field_vals->db_value.str))
{
*with_i_schema= 1;
- if (files->push_back(thd->strdup(INFORMATION_SCHEMA_NAME.str)))
+ if (files->push_back(i_s_name_copy))
return 1;
}
return (find_files(thd, files, NullS, mysql_data_home,
- idx_field_vals->db_value, 1) != FIND_FILES_OK);
+ lookup_field_vals->db_value.str, 1) != FIND_FILES_OK);
}
+
/*
- This part of code is for SHOW TABLES, SHOW TABLE STATUS commands.
- idx_field_vals->db_value can't be 0 (see get_index_field_values()
- function). lex->orig_sql_command can be not equal to SQLCOM_END
- only in case of executing of SHOW commands.
+ If we have db lookup vaule we just add it to list and
+ exit from the function
*/
- if (lex->orig_sql_command != SQLCOM_END)
+ if (lookup_field_vals->db_value.str)
{
if (!my_strcasecmp(system_charset_info, INFORMATION_SCHEMA_NAME.str,
- idx_field_vals->db_value))
+ lookup_field_vals->db_value.str))
{
*with_i_schema= 1;
- return files->push_back(thd->strdup(INFORMATION_SCHEMA_NAME.str));
+ if (files->push_back(i_s_name_copy))
+ return 1;
+ return 0;
}
- return files->push_back(thd->strdup(idx_field_vals->db_value));
+ if (files->push_back(&lookup_field_vals->db_value))
+ return 1;
+ return 0;
}
/*
Create list of existing databases. It is used in case
of select from information schema table
*/
- if (files->push_back(thd->strdup(INFORMATION_SCHEMA_NAME.str)))
+ if (files->push_back(i_s_name_copy))
return 1;
*with_i_schema= 1;
return (find_files(thd, files, NullS,
@@ -2116,9 +2698,54 @@ int make_db_list(THD *thd, List<char> *files,
}
-int schema_tables_add(THD *thd, List<char> *files, const char *wild)
+struct st_add_schema_table
+{
+ List<LEX_STRING> *files;
+ const char *wild;
+};
+
+
+static my_bool add_schema_table(THD *thd, plugin_ref plugin,
+ void* p_data)
+{
+ LEX_STRING *file_name= 0;
+ st_add_schema_table *data= (st_add_schema_table *)p_data;
+ List<LEX_STRING> *file_list= data->files;
+ const char *wild= data->wild;
+ ST_SCHEMA_TABLE *schema_table= plugin_data(plugin, ST_SCHEMA_TABLE *);
+ DBUG_ENTER("add_schema_table");
+
+ if (schema_table->hidden)
+ DBUG_RETURN(0);
+ if (wild)
+ {
+ if (lower_case_table_names)
+ {
+ if (wild_case_compare(files_charset_info,
+ schema_table->table_name,
+ wild))
+ DBUG_RETURN(0);
+ }
+ else if (wild_compare(schema_table->table_name, wild, 0))
+ DBUG_RETURN(0);
+ }
+
+ if ((file_name= thd->make_lex_string(file_name, schema_table->table_name,
+ strlen(schema_table->table_name),
+ TRUE)) &&
+ !file_list->push_back(file_name))
+ DBUG_RETURN(0);
+ DBUG_RETURN(1);
+}
+
+
+int schema_tables_add(THD *thd, List<LEX_STRING> *files, const char *wild)
{
+ LEX_STRING *file_name= 0;
ST_SCHEMA_TABLE *tmp_schema_table= schema_tables;
+ st_add_schema_table add_data;
+ DBUG_ENTER("schema_tables_add");
+
for (; tmp_schema_table->table_name; tmp_schema_table++)
{
if (tmp_schema_table->hidden)
@@ -2135,45 +2762,429 @@ int schema_tables_add(THD *thd, List<char> *files, const char *wild)
else if (wild_compare(tmp_schema_table->table_name, wild, 0))
continue;
}
- if (files->push_back(thd->strdup(tmp_schema_table->table_name)))
- return 1;
+ if ((file_name=
+ thd->make_lex_string(file_name, tmp_schema_table->table_name,
+ strlen(tmp_schema_table->table_name), TRUE)) &&
+ !files->push_back(file_name))
+ continue;
+ DBUG_RETURN(1);
+ }
+
+ add_data.files= files;
+ add_data.wild= wild;
+ if (plugin_foreach(thd, add_schema_table,
+ MYSQL_INFORMATION_SCHEMA_PLUGIN, &add_data))
+ DBUG_RETURN(1);
+
+ DBUG_RETURN(0);
+}
+
+
+/**
+ @brief Create table names list
+
+ @details The function creates the list of table names in
+ database
+
+ @param[in] thd thread handler
+ @param[in] table_names List of table names in database
+ @param[in] lex pointer to LEX struct
+ @param[in] lookup_field_vals pointer to LOOKUP_FIELD_VALUE struct
+ @param[in] with_i_schema TRUE means that we add I_S tables to list
+ @param[in] db_name database name
+
+ @return Operation status
+ @retval 0 ok
+ @retval 1 fatal error
+ @retval 2 Not fatal error; Safe to ignore this file list
+*/
+
+static int
+make_table_name_list(THD *thd, List<LEX_STRING> *table_names, LEX *lex,
+ LOOKUP_FIELD_VALUES *lookup_field_vals,
+ bool with_i_schema, LEX_STRING *db_name)
+{
+ char path[FN_REFLEN];
+ build_table_filename(path, sizeof(path), db_name->str, "", "", 0);
+ if (!lookup_field_vals->wild_table_value &&
+ lookup_field_vals->table_value.str)
+ {
+ if (with_i_schema)
+ {
+ if (find_schema_table(thd, lookup_field_vals->table_value.str))
+ {
+ if (table_names->push_back(&lookup_field_vals->table_value))
+ return 1;
+ }
+ }
+ else
+ {
+ if (table_names->push_back(&lookup_field_vals->table_value))
+ return 1;
+ /*
+ Check that table is relevant in current transaction.
+ (used for ndb engine, see ndbcluster_find_files(), ha_ndbcluster.cc)
+ */
+ VOID(ha_find_files(thd, db_name->str, path,
+ lookup_field_vals->table_value.str, 0,
+ table_names));
+ }
+ return 0;
+ }
+
+ /*
+ This call will add all matching the wildcards (if specified) IS tables
+ to the list
+ */
+ if (with_i_schema)
+ return (schema_tables_add(thd, table_names,
+ lookup_field_vals->table_value.str));
+
+ find_files_result res= find_files(thd, table_names, db_name->str, path,
+ lookup_field_vals->table_value.str, 0);
+ if (res != FIND_FILES_OK)
+ {
+ /*
+ Downgrade errors about problems with database directory to
+ warnings if this is not a 'SHOW' command. Another thread
+ may have dropped database, and we may still have a name
+ for that directory.
+ */
+ if (res == FIND_FILES_DIR)
+ {
+ if (lex->sql_command != SQLCOM_SELECT)
+ return 1;
+ thd->clear_error();
+ return 2;
+ }
+ return 1;
}
return 0;
}
+/**
+ @brief Fill I_S table for SHOW COLUMNS|INDEX commands
+
+ @param[in] thd thread handler
+ @param[in] tables TABLE_LIST for I_S table
+ @param[in] schema_table pointer to I_S structure
+ @param[in] open_tables_state_backup pointer to Open_tables_state object
+ which is used to save|restore original
+ status of variables related to
+ open tables state
+
+ @return Operation status
+ @retval 0 success
+ @retval 1 error
+*/
+
+static int
+fill_schema_show_cols_or_idxs(THD *thd, TABLE_LIST *tables,
+ ST_SCHEMA_TABLE *schema_table,
+ Open_tables_state *open_tables_state_backup)
+{
+ LEX *lex= thd->lex;
+ bool res;
+ LEX_STRING tmp_lex_string, tmp_lex_string1, *db_name, *table_name;
+ enum_sql_command save_sql_command= lex->sql_command;
+ TABLE_LIST *show_table_list= (TABLE_LIST*) tables->schema_select_lex->
+ table_list.first;
+ TABLE *table= tables->table;
+ int error= 1;
+ DBUG_ENTER("fill_schema_show");
+
+ lex->all_selects_list= tables->schema_select_lex;
+ /*
+ Restore thd->temporary_tables to be able to process
+ temporary tables(only for 'show index' & 'show columns').
+ This should be changed when processing of temporary tables for
+ I_S tables will be done.
+ */
+ thd->temporary_tables= open_tables_state_backup->temporary_tables;
+ /*
+ Let us set fake sql_command so views won't try to merge
+ themselves into main statement. If we don't do this,
+ SELECT * from information_schema.xxxx will cause problems.
+ SQLCOM_SHOW_FIELDS is used because it satisfies 'only_view_structure()'
+ */
+ lex->sql_command= SQLCOM_SHOW_FIELDS;
+ res= open_normal_and_derived_tables(thd, show_table_list,
+ MYSQL_LOCK_IGNORE_FLUSH);
+ lex->sql_command= save_sql_command;
+ /*
+ get_all_tables() returns 1 on failure and 0 on success thus
+ return only these and not the result code of ::process_table()
+
+ We should use show_table_list->alias instead of
+ show_table_list->table_name because table_name
+ could be changed during opening of I_S tables. It's safe
+ to use alias because alias contains original table name
+ in this case(this part of code is used only for
+ 'show columns' & 'show statistics' commands).
+ */
+ table_name= thd->make_lex_string(&tmp_lex_string1, show_table_list->alias,
+ strlen(show_table_list->alias), FALSE);
+ if (!show_table_list->view)
+ db_name= thd->make_lex_string(&tmp_lex_string, show_table_list->db,
+ show_table_list->db_length, FALSE);
+ else
+ db_name= &show_table_list->view_db;
+
+
+ error= test(schema_table->process_table(thd, show_table_list,
+ table, res, db_name,
+ table_name));
+ thd->temporary_tables= 0;
+ close_tables_for_reopen(thd, &show_table_list);
+ DBUG_RETURN(error);
+}
+
+
+/**
+ @brief Fill I_S table for SHOW TABLE NAMES commands
+
+ @param[in] thd thread handler
+ @param[in] table TABLE struct for I_S table
+ @param[in] db_name database name
+ @param[in] table_name table name
+ @param[in] with_i_schema I_S table if TRUE
+
+ @return Operation status
+ @retval 0 success
+ @retval 1 error
+*/
+
+static int fill_schema_table_names(THD *thd, TABLE *table,
+ LEX_STRING *db_name, LEX_STRING *table_name,
+ bool with_i_schema)
+{
+ if (with_i_schema)
+ {
+ table->field[3]->store(STRING_WITH_LEN("SYSTEM VIEW"),
+ system_charset_info);
+ }
+ else
+ {
+ enum legacy_db_type not_used;
+ char path[FN_REFLEN];
+ (void) build_table_filename(path, sizeof(path), db_name->str,
+ table_name->str, reg_ext, 0);
+ switch (mysql_frm_type(thd, path, &not_used)) {
+ case FRMTYPE_ERROR:
+ table->field[3]->store(STRING_WITH_LEN("ERROR"),
+ system_charset_info);
+ break;
+ case FRMTYPE_TABLE:
+ table->field[3]->store(STRING_WITH_LEN("BASE TABLE"),
+ system_charset_info);
+ break;
+ case FRMTYPE_VIEW:
+ table->field[3]->store(STRING_WITH_LEN("VIEW"),
+ system_charset_info);
+ break;
+ default:
+ DBUG_ASSERT(0);
+ }
+ if (thd->is_error() && thd->main_da.sql_errno() == ER_NO_SUCH_TABLE)
+ {
+ thd->clear_error();
+ return 0;
+ }
+ }
+ if (schema_table_store_record(thd, table))
+ return 1;
+ return 0;
+}
+
+
+/**
+ @brief Get open table method
+
+ @details The function calculates the method which will be used
+ for table opening:
+ SKIP_OPEN_TABLE - do not open table
+ OPEN_FRM_ONLY - open FRM file only
+ OPEN_FULL_TABLE - open FRM, data, index files
+ @param[in] tables I_S table table_list
+ @param[in] schema_table I_S table struct
+ @param[in] schema_table_idx I_S table index
+
+ @return return a set of flags
+ @retval SKIP_OPEN_TABLE | OPEN_FRM_ONLY | OPEN_FULL_TABLE
+*/
+
+uint get_table_open_method(TABLE_LIST *tables,
+ ST_SCHEMA_TABLE *schema_table,
+ enum enum_schema_tables schema_table_idx)
+{
+ /*
+ determine which method will be used for table opening
+ */
+ if (schema_table->i_s_requested_object & OPTIMIZE_I_S_TABLE)
+ {
+ Field **ptr, *field;
+ int table_open_method= 0, field_indx= 0;
+ uint star_table_open_method= OPEN_FULL_TABLE;
+ bool used_star= true; // true if '*' is used in select
+ for (ptr=tables->table->field; (field= *ptr) ; ptr++)
+ {
+ star_table_open_method=
+ min(star_table_open_method,
+ schema_table->fields_info[field_indx].open_method);
+ if (bitmap_is_set(tables->table->read_set, field->field_index))
+ {
+ used_star= false;
+ table_open_method|= schema_table->fields_info[field_indx].open_method;
+ }
+ field_indx++;
+ }
+ if (used_star)
+ return star_table_open_method;
+ return table_open_method;
+ }
+ /* I_S tables which use get_all_tables but can not be optimized */
+ return (uint) OPEN_FULL_TABLE;
+}
+
+
+/**
+ @brief Fill I_S table with data from FRM file only
+
+ @param[in] thd thread handler
+ @param[in] table TABLE struct for I_S table
+ @param[in] schema_table I_S table struct
+ @param[in] db_name database name
+ @param[in] table_name table name
+ @param[in] schema_table_idx I_S table index
+
+ @return Operation status
+ @retval 0 Table is processed and we can continue
+ with new table
+ @retval 1 It's view and we have to use
+ open_tables function for this table
+*/
+
+static int fill_schema_table_from_frm(THD *thd,TABLE *table,
+ ST_SCHEMA_TABLE *schema_table,
+ LEX_STRING *db_name,
+ LEX_STRING *table_name,
+ enum enum_schema_tables schema_table_idx)
+{
+ TABLE_SHARE *share;
+ TABLE tbl;
+ TABLE_LIST table_list;
+ uint res= 0;
+ int error;
+ char key[MAX_DBKEY_LENGTH];
+ uint key_length;
+
+ 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;
+ key_length= create_table_def_key(thd, key, &table_list, 0);
+ pthread_mutex_lock(&LOCK_open);
+ share= get_table_share(thd, &table_list, key,
+ key_length, OPEN_VIEW, &error);
+ if (!share)
+ {
+ res= 0;
+ goto err;
+ }
+
+ if (share->is_view)
+ {
+ if (schema_table->i_s_requested_object & OPEN_TABLE_ONLY)
+ {
+ /* skip view processing */
+ res= 0;
+ goto err1;
+ }
+ else if (schema_table->i_s_requested_object & OPEN_VIEW_FULL)
+ {
+ /*
+ tell get_all_tables() to fall back to
+ open_normal_and_derived_tables()
+ */
+ res= 1;
+ goto err1;
+ }
+ }
+
+ if (share->is_view ||
+ !open_table_from_share(thd, share, table_name->str, 0,
+ (READ_KEYINFO | COMPUTE_TYPES |
+ 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;
+ res= schema_table->process_table(thd, &table_list, table,
+ res, db_name, table_name);
+ closefrm(&tbl, true);
+ goto err;
+ }
+
+err1:
+ release_table_share(share, RELEASE_NORMAL);
+
+err:
+ pthread_mutex_unlock(&LOCK_open);
+ thd->clear_error();
+ return res;
+}
+
+
+
+/**
+ @brief Fill I_S tables whose data are retrieved
+ from frm files and storage engine
+
+ @details The information schema tables are internally represented as
+ temporary tables that are filled at query execution time.
+ Those I_S tables whose data are retrieved
+ from frm files and storage engine are filled by the function
+ get_all_tables().
+
+ @param[in] thd thread handler
+ @param[in] tables I_S table
+ @param[in] cond 'WHERE' condition
+
+ @return Operation status
+ @retval 0 success
+ @retval 1 error
+*/
+
int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond)
{
LEX *lex= thd->lex;
TABLE *table= tables->table;
- SELECT_LEX *select_lex= &lex->select_lex;
SELECT_LEX *old_all_select_lex= lex->all_selects_list;
enum_sql_command save_sql_command= lex->sql_command;
SELECT_LEX *lsel= tables->schema_select_lex;
ST_SCHEMA_TABLE *schema_table= tables->schema_table;
SELECT_LEX sel;
- INDEX_FIELD_VALUES idx_field_vals;
- char path[FN_REFLEN], *end, *base_name, *orig_base_name, *file_name;
- uint len;
+ LOOKUP_FIELD_VALUES lookup_field_vals;
+ LEX_STRING *db_name, *table_name;
bool with_i_schema;
enum enum_schema_tables schema_table_idx;
- List<char> bases;
- List_iterator_fast<char> it(bases);
- COND *partial_cond;
+ List<LEX_STRING> db_names;
+ List_iterator_fast<LEX_STRING> it(db_names);
+ COND *partial_cond= 0;
uint derived_tables= lex->derived_tables;
int error= 1;
- db_type not_used;
Open_tables_state open_tables_state_backup;
bool save_view_prepare_mode= lex->view_prepare_mode;
Query_tables_list query_tables_list_backup;
#ifndef NO_EMBEDDED_ACCESS_CHECKS
Security_context *sctx= thd->security_ctx;
#endif
+ uint table_open_method;
DBUG_ENTER("get_all_tables");
- LINT_INIT(end);
- LINT_INIT(len);
-
lex->view_prepare_mode= TRUE;
lex->reset_n_backup_query_tables_list(&query_tables_list_backup);
@@ -2184,181 +3195,172 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond)
*/
thd->reset_n_backup_open_tables_state(&open_tables_state_backup);
+ /*
+ this branch processes SHOW FIELDS, SHOW INDEXES commands.
+ see sql_parse.cc, prepare_schema_table() function where
+ this values are initialized
+ */
if (lsel && lsel->table_list.first)
{
- TABLE_LIST *show_table_list= (TABLE_LIST*) lsel->table_list.first;
- bool res;
-
- lex->all_selects_list= lsel;
- /*
- Restore thd->temporary_tables to be able to process
- temporary tables(only for 'show index' & 'show columns').
- This should be changed when processing of temporary tables for
- I_S tables will be done.
- */
- thd->temporary_tables= open_tables_state_backup.temporary_tables;
- /*
- Let us set fake sql_command so views won't try to merge
- themselves into main statement. If we don't do this,
- SELECT * from information_schema.xxxx will cause problems.
- SQLCOM_SHOW_FIELDS is used because it satisfies 'only_view_structure()'
- */
- lex->sql_command= SQLCOM_SHOW_FIELDS;
- res= open_normal_and_derived_tables(thd, show_table_list,
- MYSQL_LOCK_IGNORE_FLUSH);
- lex->sql_command= save_sql_command;
- /*
- get_all_tables() returns 1 on failure and 0 on success thus
- return only these and not the result code of ::process_table()
-
- We should use show_table_list->alias instead of
- show_table_list->table_name because table_name
- could be changed during opening of I_S tables. It's safe
- to use alias because alias contains original table name
- in this case(this part of code is used only for
- 'show columns' & 'show statistics' commands).
- */
- error= test(schema_table->process_table(thd, show_table_list,
- table, res,
- (show_table_list->view ?
- show_table_list->view_db.str :
- show_table_list->db),
- show_table_list->alias));
- thd->temporary_tables= 0;
- close_tables_for_reopen(thd, &show_table_list);
+ error= fill_schema_show_cols_or_idxs(thd, tables, schema_table,
+ &open_tables_state_backup);
goto err;
}
schema_table_idx= get_schema_table_idx(schema_table);
+ if (get_lookup_field_values(thd, cond, tables, &lookup_field_vals))
+ {
+ error= 0;
+ goto err;
+ }
+ DBUG_PRINT("INDEX VALUES",("db_name='%s', table_name='%s'",
+ STR_OR_NIL(lookup_field_vals.db_value.str),
+ STR_OR_NIL(lookup_field_vals.table_value.str)));
+
+ if (!lookup_field_vals.wild_db_value && !lookup_field_vals.wild_table_value)
+ {
+ /*
+ if lookup value is empty string then
+ it's impossible table name or db name
+ */
+ if (lookup_field_vals.db_value.str &&
+ !lookup_field_vals.db_value.str[0] ||
+ lookup_field_vals.table_value.str &&
+ !lookup_field_vals.table_value.str[0])
+ {
+ error= 0;
+ goto err;
+ }
+ }
+
+ if (lookup_field_vals.db_value.length &&
+ !lookup_field_vals.wild_db_value)
+ tables->has_db_lookup_value= TRUE;
+ if (lookup_field_vals.table_value.length &&
+ !lookup_field_vals.wild_table_value)
+ tables->has_table_lookup_value= TRUE;
- if (make_db_list(thd, &bases, &idx_field_vals,
- &with_i_schema, 0))
+ if (tables->has_db_lookup_value && tables->has_table_lookup_value)
+ partial_cond= 0;
+ 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 */
+ error= 0;
goto err;
+ }
- partial_cond= make_cond_for_info_schema(cond, tables);
+ if (make_db_list(thd, &db_names, &lookup_field_vals, &with_i_schema))
+ goto err;
it.rewind(); /* To get access to new elements in basis list */
- while ((orig_base_name= base_name= it++) ||
- /*
- generate error for non existing database.
- (to save old behaviour for SHOW TABLES FROM db)
- */
- ((lex->orig_sql_command == SQLCOM_SHOW_TABLES ||
- lex->orig_sql_command == SQLCOM_SHOW_TABLE_STATUS) &&
- (base_name= select_lex->db) && !bases.elements))
+ while ((db_name= it++))
{
#ifndef NO_EMBEDDED_ACCESS_CHECKS
- if (!check_access(thd,SELECT_ACL, base_name,
+ if (!check_access(thd,SELECT_ACL, db_name->str,
&thd->col_access, 0, 1, with_i_schema) ||
sctx->master_access & (DB_ACLS | SHOW_DB_ACL) ||
- acl_get(sctx->host, sctx->ip, sctx->priv_user, base_name,0) ||
- (grant_option && !check_grant_db(thd, base_name)))
+ acl_get(sctx->host, sctx->ip, sctx->priv_user, db_name->str, 0) ||
+ !check_grant_db(thd, db_name->str))
#endif
{
- List<char> files;
- if (with_i_schema) // information schema table names
- {
- if (schema_tables_add(thd, &files, idx_field_vals.table_value))
- goto err;
- }
- else
+ thd->no_warnings_for_error= 1;
+ List<LEX_STRING> table_names;
+ int res= make_table_name_list(thd, &table_names, lex,
+ &lookup_field_vals,
+ with_i_schema, db_name);
+ if (res == 2) /* Not fatal error, continue */
+ continue;
+ if (res)
+ goto err;
+
+ List_iterator_fast<LEX_STRING> it_files(table_names);
+ while ((table_name= it_files++))
{
- strxmov(path, mysql_data_home, "/", base_name, NullS);
- end= path + (len= unpack_dirname(path,path));
- len= FN_LEN - len;
- find_files_result res= find_files(thd, &files, base_name,
- path, idx_field_vals.table_value, 0);
- if (res != FIND_FILES_OK)
+ restore_record(table, s->default_values);
+ table->field[schema_table->idx_field1]->
+ store(db_name->str, db_name->length, system_charset_info);
+ table->field[schema_table->idx_field2]->
+ store(table_name->str, table_name->length, system_charset_info);
+
+ if (!partial_cond || partial_cond->val_int())
{
/*
- Downgrade errors about problems with database directory to
- warnings if this is not a 'SHOW' command. Another thread
- may have dropped database, and we may still have a name
- for that directory.
+ If table is I_S.tables and open_table_method is 0 (eg SKIP_OPEN)
+ we can skip table opening and we don't have lookup value for
+ table name or lookup value is wild string(table name list is
+ already created by make_table_name_list() function).
*/
- if (res == FIND_FILES_DIR && lex->orig_sql_command == SQLCOM_END)
+ if (!table_open_method && schema_table_idx == SCH_TABLES &&
+ (!lookup_field_vals.table_value.length ||
+ lookup_field_vals.wild_table_value))
{
- push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
- thd->net.last_errno, thd->net.last_error);
- thd->clear_error();
+ if (schema_table_store_record(thd, table))
+ goto err; /* Out of space in temporary table */
continue;
}
- else
- {
- goto err;
- }
- }
- if (lower_case_table_names)
- orig_base_name= thd->strdup(base_name);
- }
- List_iterator_fast<char> it_files(files);
- while ((file_name= it_files++))
- {
- restore_record(table, s->default_values);
- table->field[schema_table->idx_field1]->
- store(base_name, (uint) strlen(base_name), system_charset_info);
- table->field[schema_table->idx_field2]->
- store(file_name, (uint) strlen(file_name),system_charset_info);
- if (!partial_cond || partial_cond->val_int())
- {
+ /* SHOW TABLE NAMES command */
if (schema_table_idx == SCH_TABLE_NAMES)
{
- if (lex->verbose || lex->orig_sql_command == SQLCOM_END)
- {
- if (with_i_schema)
- {
- table->field[3]->store(STRING_WITH_LEN("SYSTEM VIEW"),
- system_charset_info);
- }
- else
- {
- my_snprintf(end, len, "/%s%s", file_name, reg_ext);
- switch (mysql_frm_type(thd, path, &not_used)) {
- case FRMTYPE_ERROR:
- table->field[3]->store(STRING_WITH_LEN("ERROR"),
- system_charset_info);
- break;
- case FRMTYPE_TABLE:
- table->field[3]->store(STRING_WITH_LEN("BASE TABLE"),
- system_charset_info);
- break;
- case FRMTYPE_VIEW:
- table->field[3]->store(STRING_WITH_LEN("VIEW"),
- system_charset_info);
- break;
- default:
- DBUG_ASSERT(0);
- }
- }
- }
- if (schema_table_store_record(thd, table))
- goto err;
+ if (fill_schema_table_names(thd, tables->table, db_name,
+ table_name, with_i_schema))
+ continue;
}
else
{
+ if (!(table_open_method & ~OPEN_FRM_ONLY) &&
+ !with_i_schema)
+ {
+ if (!fill_schema_table_from_frm(thd, table, schema_table, db_name,
+ table_name, schema_table_idx))
+ continue;
+ }
+
int res;
+ LEX_STRING tmp_lex_string, orig_db_name;
/*
- Set the parent lex of 'sel' because it is needed by sel.init_query()
- which is called inside make_table_list.
+ Set the parent lex of 'sel' because it is needed by
+ sel.init_query() which is called inside make_table_list.
*/
+ thd->no_warnings_for_error= 1;
sel.parent_lex= lex;
- if (make_table_list(thd, &sel, base_name, file_name))
+ /* db_name can be changed in make_table_list() func */
+ if (!thd->make_lex_string(&orig_db_name, db_name->str,
+ db_name->length, FALSE))
+ goto err;
+ if (make_table_list(thd, &sel, db_name, table_name))
goto err;
TABLE_LIST *show_table_list= (TABLE_LIST*) sel.table_list.first;
lex->all_selects_list= &sel;
lex->derived_tables= 0;
lex->sql_command= SQLCOM_SHOW_FIELDS;
+ show_table_list->i_s_requested_object=
+ schema_table->i_s_requested_object;
res= open_normal_and_derived_tables(thd, show_table_list,
MYSQL_LOCK_IGNORE_FLUSH);
lex->sql_command= save_sql_command;
- /*
- They can drop table after table names list creation and
- before table opening. We open non existing table and
- get ER_NO_SUCH_TABLE error. In this case we do not store
- the record into I_S table and clear error.
+ /*
+ XXX: show_table_list has a flag i_is_requested,
+ and when it's set, open_normal_and_derived_tables()
+ can return an error without setting an error message
+ in THD, which is a hack. This is why we have to
+ check for res, then for thd->is_error() only then
+ for thd->main_da.sql_errno().
*/
- if (thd->net.last_errno == ER_NO_SUCH_TABLE)
+ if (res && thd->is_error() &&
+ thd->main_da.sql_errno() == ER_NO_SUCH_TABLE)
{
+ /*
+ Hide error for not existing table.
+ This error can occur for example when we use
+ where condition with db name and table name and this
+ table does not exist.
+ */
res= 0;
thd->clear_error();
}
@@ -2371,12 +3373,14 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond)
to use alias because alias contains original table name
in this case.
*/
+ thd->make_lex_string(&tmp_lex_string, show_table_list->alias,
+ strlen(show_table_list->alias), FALSE);
res= schema_table->process_table(thd, show_table_list, table,
- res, orig_base_name,
- show_table_list->alias);
+ res, &orig_db_name,
+ &tmp_lex_string);
close_tables_for_reopen(thd, &show_table_list);
- DBUG_ASSERT(!lex->query_tables_own_last);
}
+ DBUG_ASSERT(!lex->query_tables_own_last);
if (res)
goto err;
}
@@ -2402,27 +3406,27 @@ err:
}
-bool store_schema_shemata(THD* thd, TABLE *table, const char *db_name,
+bool store_schema_shemata(THD* thd, TABLE *table, LEX_STRING *db_name,
CHARSET_INFO *cs)
{
restore_record(table, s->default_values);
- table->field[1]->store(db_name, (uint) strlen(db_name), system_charset_info);
- table->field[2]->store(cs->csname, (uint) strlen(cs->csname), system_charset_info);
- table->field[3]->store(cs->name, (uint) strlen(cs->name), 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);
return schema_table_store_record(thd, table);
}
-int fill_schema_shemata(THD *thd, TABLE_LIST *tables, COND *cond)
+int fill_schema_schemata(THD *thd, TABLE_LIST *tables, COND *cond)
{
/*
TODO: fill_schema_shemata() is called when new client is connected.
Returning error status in this case leads to client hangup.
*/
- INDEX_FIELD_VALUES idx_field_vals;
- List<char> files;
- char *file_name;
+ LOOKUP_FIELD_VALUES lookup_field_vals;
+ List<LEX_STRING> db_names;
+ LEX_STRING *db_name;
bool with_i_schema;
HA_CREATE_INFO create;
TABLE *table= tables->table;
@@ -2431,16 +3435,39 @@ int fill_schema_shemata(THD *thd, TABLE_LIST *tables, COND *cond)
#endif
DBUG_ENTER("fill_schema_shemata");
- if (make_db_list(thd, &files, &idx_field_vals,
- &with_i_schema, 1))
+ if (get_lookup_field_values(thd, cond, tables, &lookup_field_vals))
+ DBUG_RETURN(0);
+ DBUG_PRINT("INDEX VALUES",("db_name='%s', table_name='%s'",
+ lookup_field_vals.db_value.str,
+ lookup_field_vals.table_value.str));
+ if (make_db_list(thd, &db_names, &lookup_field_vals,
+ &with_i_schema))
DBUG_RETURN(1);
- List_iterator_fast<char> it(files);
- while ((file_name=it++))
+ /*
+ If we have lookup db value we should check that the database exists
+ */
+ if(lookup_field_vals.db_value.str && !lookup_field_vals.wild_db_value &&
+ !with_i_schema)
+ {
+ char path[FN_REFLEN+16];
+ uint path_len;
+ MY_STAT stat_info;
+ if (!lookup_field_vals.db_value.str[0])
+ DBUG_RETURN(0);
+ path_len= build_table_filename(path, sizeof(path),
+ lookup_field_vals.db_value.str, "", "", 0);
+ path[path_len-1]= 0;
+ if (!my_stat(path,&stat_info,MYF(0)))
+ DBUG_RETURN(0);
+ }
+
+ List_iterator_fast<LEX_STRING> it(db_names);
+ while ((db_name=it++))
{
if (with_i_schema) // information schema name is always first in list
{
- if (store_schema_shemata(thd, table, file_name,
+ if (store_schema_shemata(thd, table, db_name,
system_charset_info))
DBUG_RETURN(1);
with_i_schema= 0;
@@ -2448,13 +3475,12 @@ int fill_schema_shemata(THD *thd, TABLE_LIST *tables, COND *cond)
}
#ifndef NO_EMBEDDED_ACCESS_CHECKS
if (sctx->master_access & (DB_ACLS | SHOW_DB_ACL) ||
- acl_get(sctx->host, sctx->ip, sctx->priv_user, file_name,0) ||
- (grant_option && !check_grant_db(thd, file_name)))
+ acl_get(sctx->host, sctx->ip, sctx->priv_user, db_name->str, 0) ||
+ !check_grant_db(thd, db_name->str))
#endif
{
- load_db_opt_by_name(thd, file_name, &create);
-
- if (store_schema_shemata(thd, table, file_name,
+ load_db_opt_by_name(thd, db_name->str, &create);
+ if (store_schema_shemata(thd, table, db_name,
create.default_table_charset))
DBUG_RETURN(1);
}
@@ -2465,8 +3491,8 @@ int fill_schema_shemata(THD *thd, TABLE_LIST *tables, COND *cond)
static int get_schema_tables_record(THD *thd, TABLE_LIST *tables,
TABLE *table, bool res,
- const char *base_name,
- const char *file_name)
+ LEX_STRING *db_name,
+ LEX_STRING *table_name)
{
const char *tmp_buff;
MYSQL_TIME time;
@@ -2474,21 +3500,21 @@ 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[1]->store(base_name, (uint) strlen(base_name), cs);
- table->field[2]->store(file_name, (uint) strlen(file_name), cs);
+ table->field[1]->store(db_name->str, db_name->length, cs);
+ table->field[2]->store(table_name->str, table_name->length, cs);
if (res)
{
/*
there was errors during opening tables
*/
- const char *error= thd->net.last_error;
+ const char *error= thd->is_error() ? thd->main_da.message() : "";
if (tables->view)
table->field[3]->store(STRING_WITH_LEN("VIEW"), cs);
else if (tables->schema_table)
table->field[3]->store(STRING_WITH_LEN("SYSTEM VIEW"), cs);
else
table->field[3]->store(STRING_WITH_LEN("BASE TABLE"), cs);
- table->field[20]->store(error, (uint) strlen(error), cs);
+ table->field[20]->store(error, strlen(error), cs);
thd->clear_error();
}
else if (tables->view)
@@ -2498,12 +3524,12 @@ static int get_schema_tables_record(THD *thd, TABLE_LIST *tables,
}
else
{
+ char option_buff[350],*ptr;
TABLE *show_table= tables->table;
TABLE_SHARE *share= show_table->s;
handler *file= show_table->file;
-
- file->info(HA_STATUS_VARIABLE | HA_STATUS_TIME | HA_STATUS_AUTO |
- HA_STATUS_NO_LOCK);
+ handlerton *tmp_db_type= share->db_type();
+ bool is_partitioned= FALSE;
if (share->tmp_table == SYSTEM_TMP_TABLE)
table->field[3]->store(STRING_WITH_LEN("SYSTEM VIEW"), cs);
else if (share->tmp_table)
@@ -2517,84 +3543,18 @@ static int get_schema_tables_record(THD *thd, TABLE_LIST *tables,
continue;
table->field[i]->set_notnull();
}
- tmp_buff= file->table_type();
- table->field[4]->store(tmp_buff, (uint) strlen(tmp_buff), cs);
- table->field[5]->store((longlong) share->frm_version, TRUE);
- enum row_type row_type = file->get_row_type();
- switch (row_type) {
- case ROW_TYPE_NOT_USED:
- case ROW_TYPE_DEFAULT:
- tmp_buff= ((share->db_options_in_use &
- HA_OPTION_COMPRESS_RECORD) ? "Compressed" :
- (share->db_options_in_use & HA_OPTION_PACK_RECORD) ?
- "Dynamic" : "Fixed");
- break;
- case ROW_TYPE_FIXED:
- tmp_buff= "Fixed";
- break;
- case ROW_TYPE_DYNAMIC:
- tmp_buff= "Dynamic";
- break;
- case ROW_TYPE_COMPRESSED:
- tmp_buff= "Compressed";
- break;
- case ROW_TYPE_REDUNDANT:
- tmp_buff= "Redundant";
- break;
- case ROW_TYPE_COMPACT:
- tmp_buff= "Compact";
- break;
- }
- table->field[6]->store(tmp_buff, (uint) strlen(tmp_buff), cs);
- if (!tables->schema_table)
- {
- table->field[7]->store((longlong) file->records, TRUE);
- table->field[7]->set_notnull();
- }
- table->field[8]->store((longlong) file->mean_rec_length, TRUE);
- table->field[9]->store((longlong) file->data_file_length, TRUE);
- if (file->max_data_file_length)
+#ifdef WITH_PARTITION_STORAGE_ENGINE
+ if (share->db_type() == partition_hton &&
+ share->partition_info_len)
{
- table->field[10]->store((longlong) file->max_data_file_length, TRUE);
- }
- table->field[11]->store((longlong) file->index_file_length, TRUE);
- table->field[12]->store((longlong) file->delete_length, TRUE);
- if (show_table->found_next_number_field)
- {
- table->field[13]->store((longlong) file->auto_increment_value, TRUE);
- table->field[13]->set_notnull();
- }
- if (file->create_time)
- {
- thd->variables.time_zone->gmt_sec_to_TIME(&time,
- (my_time_t) file->create_time);
- table->field[14]->store_time(&time, MYSQL_TIMESTAMP_DATETIME);
- table->field[14]->set_notnull();
- }
- if (file->update_time)
- {
- thd->variables.time_zone->gmt_sec_to_TIME(&time,
- (my_time_t) file->update_time);
- table->field[15]->store_time(&time, MYSQL_TIMESTAMP_DATETIME);
- table->field[15]->set_notnull();
- }
- if (file->check_time)
- {
- thd->variables.time_zone->gmt_sec_to_TIME(&time,
- (my_time_t) file->check_time);
- table->field[16]->store_time(&time, MYSQL_TIMESTAMP_DATETIME);
- table->field[16]->set_notnull();
- }
- tmp_buff= (share->table_charset ?
- share->table_charset->name : "default");
- table->field[17]->store(tmp_buff, (uint) strlen(tmp_buff), cs);
- if (file->table_flags() & (ulong) HA_HAS_CHECKSUM)
- {
- table->field[18]->store((longlong) file->checksum(), TRUE);
- table->field[18]->set_notnull();
+ 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);
- char option_buff[350],*ptr;
ptr=option_buff;
if (share->min_rows)
{
@@ -2615,37 +3575,124 @@ static int get_schema_tables_record(THD *thd, TABLE_LIST *tables,
ptr=strmov(ptr," pack_keys=1");
if (share->db_create_options & HA_OPTION_NO_PACK_KEYS)
ptr=strmov(ptr," pack_keys=0");
+ /* We use CHECKSUM, instead of TABLE_CHECKSUM, for backward compability */
if (share->db_create_options & HA_OPTION_CHECKSUM)
ptr=strmov(ptr," checksum=1");
+ if (share->page_checksum != HA_CHOICE_UNDEF)
+ ptr= strxmov(ptr, " page_checksum=",
+ ha_choice_values[(uint) share->page_checksum], NullS);
if (share->db_create_options & HA_OPTION_DELAY_KEY_WRITE)
ptr=strmov(ptr," delay_key_write=1");
if (share->row_type != ROW_TYPE_DEFAULT)
ptr=strxmov(ptr, " row_format=",
ha_row_type[(uint) share->row_type],
NullS);
- if (file->raid_type)
+ if (share->transactional != HA_CHOICE_UNDEF)
+ {
+ ptr= strxmov(ptr, " TRANSACTIONAL=",
+ (share->transactional == HA_CHOICE_YES ? "1" : "0"),
+ NullS);
+ }
+ if (share->key_block_size)
{
- char buff[100];
- my_snprintf(buff,sizeof(buff),
- " raid_type=%s raid_chunks=%d raid_chunksize=%ld",
- my_raid_type(file->raid_type), file->raid_chunks,
- file->raid_chunksize/RAID_BLOCK_SIZE);
- ptr=strmov(ptr,buff);
+ ptr= strmov(ptr, " KEY_BLOCK_SIZE=");
+ ptr= longlong10_to_str(share->key_block_size, ptr, 10);
}
+#ifdef WITH_PARTITION_STORAGE_ENGINE
+ if (is_partitioned)
+ ptr= strmov(ptr, " partitioned");
+#endif
+ if (share->transactional != HA_CHOICE_UNDEF)
+ ptr= strxmov(ptr, " transactional=",
+ ha_choice_values[(uint) share->transactional], NullS);
table->field[19]->store(option_buff+1,
(ptr == option_buff ? 0 :
(uint) (ptr-option_buff)-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)
{
- char *comment;
- comment= show_table->file->update_table_comment(share->comment.str);
- if (comment)
+ file->info(HA_STATUS_VARIABLE | HA_STATUS_TIME | HA_STATUS_AUTO);
+ enum row_type row_type = file->get_row_type();
+ switch (row_type) {
+ case ROW_TYPE_NOT_USED:
+ case ROW_TYPE_DEFAULT:
+ tmp_buff= ((share->db_options_in_use &
+ HA_OPTION_COMPRESS_RECORD) ? "Compressed" :
+ (share->db_options_in_use & HA_OPTION_PACK_RECORD) ?
+ "Dynamic" : "Fixed");
+ break;
+ case ROW_TYPE_FIXED:
+ tmp_buff= "Fixed";
+ break;
+ case ROW_TYPE_DYNAMIC:
+ tmp_buff= "Dynamic";
+ break;
+ case ROW_TYPE_COMPRESSED:
+ tmp_buff= "Compressed";
+ break;
+ case ROW_TYPE_REDUNDANT:
+ tmp_buff= "Redundant";
+ break;
+ case ROW_TYPE_COMPACT:
+ tmp_buff= "Compact";
+ break;
+ case ROW_TYPE_PAGE:
+ tmp_buff= "Paged";
+ break;
+ }
+ table->field[6]->store(tmp_buff, strlen(tmp_buff), cs);
+ if (!tables->schema_table)
+ {
+ table->field[7]->store((longlong) file->stats.records, TRUE);
+ table->field[7]->set_notnull();
+ }
+ table->field[8]->store((longlong) file->stats.mean_rec_length, TRUE);
+ table->field[9]->store((longlong) file->stats.data_file_length, TRUE);
+ if (file->stats.max_data_file_length)
+ {
+ table->field[10]->store((longlong) file->stats.max_data_file_length,
+ TRUE);
+ }
+ table->field[11]->store((longlong) file->stats.index_file_length, TRUE);
+ table->field[12]->store((longlong) file->stats.delete_length, TRUE);
+ if (show_table->found_next_number_field)
+ {
+ table->field[13]->store((longlong) file->stats.auto_increment_value,
+ TRUE);
+ table->field[13]->set_notnull();
+ }
+ if (file->stats.create_time)
+ {
+ thd->variables.time_zone->gmt_sec_to_TIME(&time,
+ (my_time_t) file->stats.create_time);
+ table->field[14]->store_time(&time, MYSQL_TIMESTAMP_DATETIME);
+ table->field[14]->set_notnull();
+ }
+ if (file->stats.update_time)
{
- table->field[20]->store(comment,
- (comment == share->comment.str ?
- share->comment.length :
- (uint) strlen(comment)), cs);
- if (comment != share->comment.str)
- my_free(comment, MYF(0));
+ thd->variables.time_zone->gmt_sec_to_TIME(&time,
+ (my_time_t) file->stats.update_time);
+ table->field[15]->store_time(&time, MYSQL_TIMESTAMP_DATETIME);
+ table->field[15]->set_notnull();
+ }
+ if (file->stats.check_time)
+ {
+ thd->variables.time_zone->gmt_sec_to_TIME(&time,
+ (my_time_t) file->stats.check_time);
+ table->field[16]->store_time(&time, MYSQL_TIMESTAMP_DATETIME);
+ table->field[16]->set_notnull();
+ }
+ if (file->ha_table_flags() & (ulong) HA_HAS_CHECKSUM)
+ {
+ table->field[18]->store((longlong) file->checksum(), TRUE);
+ table->field[18]->set_notnull();
}
}
}
@@ -2655,29 +3702,28 @@ static int get_schema_tables_record(THD *thd, TABLE_LIST *tables,
static int get_schema_column_record(THD *thd, TABLE_LIST *tables,
TABLE *table, bool res,
- const char *base_name,
- const char *file_name)
+ LEX_STRING *db_name,
+ LEX_STRING *table_name)
{
LEX *lex= thd->lex;
const char *wild= lex->wild ? lex->wild->ptr() : NullS;
CHARSET_INFO *cs= system_charset_info;
TABLE *show_table;
- handler *file;
Field **ptr,*field;
int count;
- uint base_name_length, file_name_length;
DBUG_ENTER("get_schema_column_record");
if (res)
{
- if (lex->orig_sql_command != SQLCOM_SHOW_FIELDS)
+ if (lex->sql_command != SQLCOM_SHOW_FIELDS)
{
/*
I.e. we are in SELECT FROM INFORMATION_SCHEMA.COLUMS
rather than in SHOW COLUMNS
*/
- push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
- thd->net.last_errno, thd->net.last_error);
+ if (thd->is_error())
+ push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ thd->main_da.sql_errno(), thd->main_da.message());
thd->clear_error();
res= 0;
}
@@ -2685,17 +3731,14 @@ static int get_schema_column_record(THD *thd, TABLE_LIST *tables,
}
show_table= tables->table;
- file= show_table->file;
count= 0;
- file->info(HA_STATUS_VARIABLE | HA_STATUS_NO_LOCK);
restore_record(show_table, s->default_values);
- base_name_length= (uint) strlen(base_name);
- file_name_length= (uint) strlen(file_name);
+ show_table->use_all_columns(); // Required for default
- for (ptr=show_table->field; (field= *ptr) ; ptr++)
+ for (ptr= show_table->field; (field= *ptr) ; ptr++)
{
const char *tmp_buff;
- byte *pos;
+ uchar *pos;
bool is_blob;
uint flags=field->flags;
char tmp[MAX_FIELD_WIDTH];
@@ -2714,10 +3757,10 @@ static int get_schema_column_record(THD *thd, TABLE_LIST *tables,
#ifndef NO_EMBEDDED_ACCESS_CHECKS
uint col_access;
- check_access(thd,SELECT_ACL | EXTRA_ACL, base_name,
+ check_access(thd,SELECT_ACL | EXTRA_ACL, db_name->str,
&tables->grant.privilege, 0, 0, test(tables->schema_table));
col_access= get_column_grant(thd, &tables->grant,
- base_name, file_name,
+ db_name->str, table_name->str,
field->field_name) & COL_ACLS;
if (!tables->schema_table && !col_access)
continue;
@@ -2733,15 +3776,15 @@ static int get_schema_column_record(THD *thd, TABLE_LIST *tables,
table->field[17]->store(tmp+1,end == tmp ? 0 : (uint) (end-tmp-1), cs);
#endif
- table->field[1]->store(base_name, base_name_length, cs);
- table->field[2]->store(file_name, file_name_length, cs);
- table->field[3]->store(field->field_name, (uint) strlen(field->field_name),
+ 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[14]->store(type.ptr(), type.length(), cs);
tmp_buff= strchr(type.ptr(), '(');
- table->field[7]->store(type.ptr(), (uint)
+ table->field[7]->store(type.ptr(),
(tmp_buff ? tmp_buff - type.ptr() :
type.length()), cs);
@@ -2750,11 +3793,10 @@ static int get_schema_column_record(THD *thd, TABLE_LIST *tables,
table->field[5]->store(type.ptr(), type.length(), cs);
table->field[5]->set_notnull();
}
-
- pos=(byte*) ((flags & NOT_NULL_FLAG) ? "NO" : "YES");
+ pos=(uchar*) ((flags & NOT_NULL_FLAG) ? "NO" : "YES");
table->field[6]->store((const char*) pos,
- (uint) strlen((const char*) pos), cs);
- is_blob= (field->type() == FIELD_TYPE_BLOB);
+ 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
@@ -2778,25 +3820,25 @@ static int get_schema_column_record(THD *thd, TABLE_LIST *tables,
decimals= field->decimals();
switch (field->type()) {
- case FIELD_TYPE_NEWDECIMAL:
+ case MYSQL_TYPE_NEWDECIMAL:
field_length= ((Field_new_decimal*) field)->precision;
break;
- case FIELD_TYPE_DECIMAL:
+ case MYSQL_TYPE_DECIMAL:
field_length= field->field_length - (decimals ? 2 : 1);
break;
- case FIELD_TYPE_TINY:
- case FIELD_TYPE_SHORT:
- case FIELD_TYPE_LONG:
- case FIELD_TYPE_LONGLONG:
- case FIELD_TYPE_INT24:
+ case MYSQL_TYPE_TINY:
+ case MYSQL_TYPE_SHORT:
+ case MYSQL_TYPE_LONG:
+ case MYSQL_TYPE_LONGLONG:
+ case MYSQL_TYPE_INT24:
field_length= field->max_display_length() - 1;
break;
- case FIELD_TYPE_BIT:
+ case MYSQL_TYPE_BIT:
field_length= field->max_display_length();
decimals= -1; // return NULL
break;
- case FIELD_TYPE_FLOAT:
- case FIELD_TYPE_DOUBLE:
+ case MYSQL_TYPE_FLOAT:
+ case MYSQL_TYPE_DOUBLE:
field_length= field->field_length;
if (decimals == NOT_FIXED_DEC)
decimals= -1; // return NULL
@@ -2819,25 +3861,28 @@ static int get_schema_column_record(THD *thd, TABLE_LIST *tables,
if (field->has_charset())
{
- pos=(byte*) field->charset()->csname;
+ pos=(uchar*) field->charset()->csname;
table->field[12]->store((const char*) pos,
- (uint) strlen((const char*) pos), cs);
+ strlen((const char*) pos), cs);
table->field[12]->set_notnull();
- pos=(byte*) field->charset()->name;
+ pos=(uchar*) field->charset()->name;
table->field[13]->store((const char*) pos,
- (uint) strlen((const char*) pos), cs);
+ strlen((const char*) pos), cs);
table->field[13]->set_notnull();
}
- pos=(byte*) ((field->flags & PRI_KEY_FLAG) ? "PRI" :
+ pos=(uchar*) ((field->flags & PRI_KEY_FLAG) ? "PRI" :
(field->flags & UNIQUE_KEY_FLAG) ? "UNI" :
(field->flags & MULTIPLE_KEY_FLAG) ? "MUL":"");
table->field[15]->store((const char*) pos,
- (uint) strlen((const char*) pos), cs);
+ strlen((const char*) pos), cs);
end= tmp;
if (field->unireg_check == Field::NEXT_NUMBER)
- end=strmov(tmp,"auto_increment");
- table->field[16]->store(tmp, (uint) (end-tmp), cs);
+ table->field[16]->store(STRING_WITH_LEN("auto_increment"), cs);
+ if (show_table->timestamp_field == field &&
+ field->unireg_check != Field::TIMESTAMP_DN_FIELD)
+ table->field[16]->store(STRING_WITH_LEN("on update CURRENT_TIMESTAMP"),
+ cs);
table->field[18]->store(field->comment.str, field->comment.length, cs);
if (schema_table_store_record(thd, table))
@@ -2860,15 +3905,16 @@ int fill_schema_charsets(THD *thd, TABLE_LIST *tables, COND *cond)
CHARSET_INFO *tmp_cs= cs[0];
if (tmp_cs && (tmp_cs->state & MY_CS_PRIMARY) &&
(tmp_cs->state & MY_CS_AVAILABLE) &&
+ !(tmp_cs->state & MY_CS_HIDDEN) &&
!(wild && wild[0] &&
wild_case_compare(scs, tmp_cs->csname,wild)))
{
const char *comment;
restore_record(table, s->default_values);
- table->field[0]->store(tmp_cs->csname, (uint) strlen(tmp_cs->csname), scs);
- table->field[1]->store(tmp_cs->name, (uint) strlen(tmp_cs->name), scs);
+ table->field[0]->store(tmp_cs->csname, strlen(tmp_cs->csname), scs);
+ table->field[1]->store(tmp_cs->name, strlen(tmp_cs->name), scs);
comment= tmp_cs->comment ? tmp_cs->comment : "";
- table->field[2]->store(comment, (uint) strlen(comment), scs);
+ table->field[2]->store(comment, strlen(comment), scs);
table->field[3]->store((longlong) tmp_cs->mbmaxlen, TRUE);
if (schema_table_store_record(thd, table))
return 1;
@@ -2878,6 +3924,81 @@ int fill_schema_charsets(THD *thd, TABLE_LIST *tables, COND *cond)
}
+static my_bool iter_schema_engines(THD *thd, plugin_ref plugin,
+ void *ptable)
+{
+ TABLE *table= (TABLE *) ptable;
+ handlerton *hton= plugin_data(plugin, handlerton *);
+ const char *wild= thd->lex->wild ? thd->lex->wild->ptr() : NullS;
+ CHARSET_INFO *scs= system_charset_info;
+ handlerton *default_type= ha_default_handlerton(thd);
+ DBUG_ENTER("iter_schema_engines");
+
+
+ /* Disabled plugins */
+ if (plugin_state(plugin) != PLUGIN_IS_READY)
+ {
+
+ struct st_mysql_plugin *plug= plugin_decl(plugin);
+ if (!(wild && wild[0] &&
+ wild_case_compare(scs, plug->name,wild)))
+ {
+ restore_record(table, s->default_values);
+ table->field[0]->store(plug->name, strlen(plug->name), scs);
+ table->field[1]->store(C_STRING_WITH_LEN("NO"), scs);
+ table->field[2]->store(plug->descr, strlen(plug->descr), scs);
+ if (schema_table_store_record(thd, table))
+ DBUG_RETURN(1);
+ }
+ DBUG_RETURN(0);
+ }
+
+ if (!(hton->flags & HTON_HIDDEN))
+ {
+ LEX_STRING *name= plugin_name(plugin);
+ if (!(wild && wild[0] &&
+ wild_case_compare(scs, name->str,wild)))
+ {
+ LEX_STRING yesno[2]= {{ C_STRING_WITH_LEN("NO") },
+ { C_STRING_WITH_LEN("YES") }};
+ LEX_STRING *tmp;
+ const char *option_name= show_comp_option_name[(int) hton->state];
+ restore_record(table, s->default_values);
+
+ table->field[0]->store(name->str, name->length, scs);
+ if (hton->state == SHOW_OPTION_YES && default_type == hton)
+ option_name= "DEFAULT";
+ table->field[1]->store(option_name, strlen(option_name), scs);
+ table->field[2]->store(plugin_decl(plugin)->descr,
+ strlen(plugin_decl(plugin)->descr), scs);
+ tmp= &yesno[test(hton->commit)];
+ table->field[3]->store(tmp->str, tmp->length, scs);
+ table->field[3]->set_notnull();
+ tmp= &yesno[test(hton->prepare)];
+ table->field[4]->store(tmp->str, tmp->length, scs);
+ table->field[4]->set_notnull();
+ tmp= &yesno[test(hton->savepoint_set)];
+ table->field[5]->store(tmp->str, tmp->length, scs);
+ table->field[5]->set_notnull();
+
+ if (schema_table_store_record(thd, table))
+ DBUG_RETURN(1);
+ }
+ }
+ DBUG_RETURN(0);
+}
+
+int fill_schema_engines(THD *thd, TABLE_LIST *tables, COND *cond)
+{
+ DBUG_ENTER("fill_schema_engines");
+ if (plugin_foreach_with_mask(thd, iter_schema_engines,
+ MYSQL_STORAGE_ENGINE_PLUGIN,
+ ~PLUGIN_IS_FREED, tables->table))
+ DBUG_RETURN(1);
+ DBUG_RETURN(0);
+}
+
+
int fill_schema_collation(THD *thd, TABLE_LIST *tables, COND *cond)
{
CHARSET_INFO **cs;
@@ -2888,7 +4009,8 @@ int fill_schema_collation(THD *thd, TABLE_LIST *tables, COND *cond)
{
CHARSET_INFO **cl;
CHARSET_INFO *tmp_cs= cs[0];
- if (!tmp_cs || !(tmp_cs->state & MY_CS_AVAILABLE) ||
+ if (!tmp_cs || !(tmp_cs->state & MY_CS_AVAILABLE) ||
+ (tmp_cs->state & MY_CS_HIDDEN) ||
!(tmp_cs->state & MY_CS_PRIMARY))
continue;
for (cl= all_charsets; cl < all_charsets+255 ;cl ++)
@@ -2902,13 +4024,13 @@ int fill_schema_collation(THD *thd, TABLE_LIST *tables, COND *cond)
{
const char *tmp_buff;
restore_record(table, s->default_values);
- table->field[0]->store(tmp_cl->name, (uint) strlen(tmp_cl->name), scs);
- table->field[1]->store(tmp_cl->csname , (uint) strlen(tmp_cl->csname), scs);
+ table->field[0]->store(tmp_cl->name, strlen(tmp_cl->name), scs);
+ table->field[1]->store(tmp_cl->csname , strlen(tmp_cl->csname), scs);
table->field[2]->store((longlong) tmp_cl->number, TRUE);
tmp_buff= (tmp_cl->state & MY_CS_PRIMARY) ? "Yes" : "";
- table->field[3]->store(tmp_buff, (uint) strlen(tmp_buff), scs);
+ table->field[3]->store(tmp_buff, strlen(tmp_buff), scs);
tmp_buff= (tmp_cl->state & MY_CS_COMPILED)? "Yes" : "";
- table->field[4]->store(tmp_buff, (uint) strlen(tmp_buff), scs);
+ table->field[4]->store(tmp_buff, strlen(tmp_buff), scs);
table->field[5]->store((longlong) tmp_cl->strxfrm_multiply, TRUE);
if (schema_table_store_record(thd, table))
return 1;
@@ -2938,8 +4060,8 @@ int fill_schema_coll_charset_app(THD *thd, TABLE_LIST *tables, COND *cond)
!my_charset_same(tmp_cs,tmp_cl))
continue;
restore_record(table, s->default_values);
- table->field[0]->store(tmp_cl->name, (uint) strlen(tmp_cl->name), scs);
- table->field[1]->store(tmp_cl->csname , (uint) strlen(tmp_cl->csname), scs);
+ table->field[0]->store(tmp_cl->name, strlen(tmp_cl->name), scs);
+ table->field[1]->store(tmp_cl->csname , strlen(tmp_cl->csname), scs);
if (schema_table_store_record(thd, table))
return 1;
}
@@ -2961,16 +4083,18 @@ bool store_schema_proc(THD *thd, TABLE *table, TABLE *proc_table,
get_field(thd->mem_root, proc_table->field[11], &definer);
if (!full_access)
full_access= !strcmp(sp_user, definer.ptr());
- if (!full_access && check_some_routine_access(thd, sp_db.ptr(), sp_name.ptr(),
- proc_table->field[2]->val_int() ==
+ if (!full_access && check_some_routine_access(thd, sp_db.ptr(),
+ sp_name.ptr(),
+ proc_table->field[2]->
+ val_int() ==
TYPE_ENUM_PROCEDURE))
return 0;
- if (lex->orig_sql_command == SQLCOM_SHOW_STATUS_PROC &&
+ if (lex->sql_command == SQLCOM_SHOW_STATUS_PROC &&
proc_table->field[2]->val_int() == TYPE_ENUM_PROCEDURE ||
- lex->orig_sql_command == SQLCOM_SHOW_STATUS_FUNC &&
+ lex->sql_command == SQLCOM_SHOW_STATUS_FUNC &&
proc_table->field[2]->val_int() == TYPE_ENUM_FUNCTION ||
- lex->orig_sql_command == SQLCOM_END)
+ (sql_command_flags[lex->sql_command] & CF_STATUS_COMMAND) == 0)
{
restore_record(table, s->default_values);
if (!wild || !wild[0] || !wild_compare(sp_name.ptr(), wild, 0))
@@ -2990,7 +4114,7 @@ bool store_schema_proc(THD *thd, TABLE *table, TABLE *proc_table,
}
if (full_access)
{
- get_field(thd->mem_root, proc_table->field[10], &tmp_string);
+ get_field(thd->mem_root, proc_table->field[19], &tmp_string);
table->field[7]->store(tmp_string.ptr(), tmp_string.length(), cs);
table->field[7]->set_notnull();
}
@@ -3013,6 +4137,16 @@ bool store_schema_proc(THD *thd, TABLE *table, TABLE *proc_table,
get_field(thd->mem_root, proc_table->field[15], &tmp_string);
table->field[18]->store(tmp_string.ptr(), tmp_string.length(), cs);
table->field[19]->store(definer.ptr(), definer.length(), cs);
+
+ get_field(thd->mem_root, proc_table->field[16], &tmp_string);
+ table->field[20]->store(tmp_string.ptr(), tmp_string.length(), cs);
+
+ get_field(thd->mem_root, proc_table->field[17], &tmp_string);
+ table->field[21]->store(tmp_string.ptr(), tmp_string.length(), cs);
+
+ get_field(thd->mem_root, proc_table->field[18], &tmp_string);
+ table->field[22]->store(tmp_string.ptr(), tmp_string.length(), cs);
+
return schema_table_store_record(thd, table);
}
}
@@ -3041,12 +4175,12 @@ int fill_schema_proc(THD *thd, TABLE_LIST *tables, COND *cond)
proc_tables.table_name= proc_tables.alias= (char*) "proc";
proc_tables.table_name_length= 4;
proc_tables.lock_type= TL_READ;
- full_access= !check_table_access(thd, SELECT_ACL, &proc_tables, 1);
+ full_access= !check_table_access(thd, SELECT_ACL, &proc_tables, 1, TRUE);
if (!(proc_table= open_proc_table_for_read(thd, &open_tables_state_backup)))
{
DBUG_RETURN(1);
}
- proc_table->file->ha_index_init(0);
+ proc_table->file->ha_index_init(0, 1);
if ((res= proc_table->file->index_first(proc_table->record[0])))
{
res= (res == HA_ERR_END_OF_FILE) ? 0 : 1;
@@ -3068,29 +4202,29 @@ int fill_schema_proc(THD *thd, TABLE_LIST *tables, COND *cond)
err:
proc_table->file->ha_index_end();
- close_proc_table(thd, &open_tables_state_backup);
+ close_system_tables(thd, &open_tables_state_backup);
DBUG_RETURN(res);
}
static int get_schema_stat_record(THD *thd, TABLE_LIST *tables,
TABLE *table, bool res,
- const char *base_name,
- const char *file_name)
+ LEX_STRING *db_name,
+ LEX_STRING *table_name)
{
CHARSET_INFO *cs= system_charset_info;
DBUG_ENTER("get_schema_stat_record");
if (res)
{
- if (thd->lex->orig_sql_command != SQLCOM_SHOW_KEYS)
+ if (thd->lex->sql_command != SQLCOM_SHOW_KEYS)
{
/*
I.e. we are in SELECT FROM INFORMATION_SCHEMA.STATISTICS
rather than in SHOW KEYS
*/
- if (!tables->view)
+ if (thd->is_error())
push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
- thd->net.last_errno, thd->net.last_error);
+ thd->main_da.sql_errno(), thd->main_da.message());
thd->clear_error();
res= 0;
}
@@ -3099,10 +4233,11 @@ static int get_schema_stat_record(THD *thd, TABLE_LIST *tables,
else if (!tables->view)
{
TABLE *show_table= tables->table;
- KEY *key_info=show_table->key_info;
- show_table->file->info(HA_STATUS_VARIABLE |
- HA_STATUS_NO_LOCK |
- HA_STATUS_TIME);
+ KEY *key_info=show_table->s->key_info;
+ if (show_table->file)
+ show_table->file->info(HA_STATUS_VARIABLE |
+ HA_STATUS_NO_LOCK |
+ HA_STATUS_TIME);
for (uint i=0 ; i < show_table->s->keys ; i++,key_info++)
{
KEY_PART_INFO *key_part= key_info->key_part;
@@ -3110,45 +4245,48 @@ 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[1]->store(base_name, (uint) strlen(base_name), cs);
- table->field[2]->store(file_name, (uint) strlen(file_name), 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 &
HA_NOSAME) ? 0 : 1), TRUE);
- table->field[4]->store(base_name, (uint) strlen(base_name), cs);
- table->field[5]->store(key_info->name, (uint) strlen(key_info->name), cs);
+ table->field[4]->store(db_name->str, db_name->length, cs);
+ table->field[5]->store(key_info->name, strlen(key_info->name), cs);
table->field[6]->store((longlong) (j+1), TRUE);
str=(key_part->field ? key_part->field->field_name :
"?unknown field?");
- table->field[7]->store(str, (uint) strlen(str), cs);
- if (show_table->file->index_flags(i, j, 0) & HA_READ_ORDER)
- {
- table->field[8]->store(((key_part->key_part_flag &
- HA_REVERSE_SORT) ?
- "D" : "A"), 1, cs);
- table->field[8]->set_notnull();
- }
- KEY *key=show_table->key_info+i;
- if (key->rec_per_key[j])
+ table->field[7]->store(str, strlen(str), cs);
+ if (show_table->file)
{
- ha_rows records=(show_table->file->records /
- key->rec_per_key[j]);
- table->field[9]->store((longlong) records, TRUE);
- table->field[9]->set_notnull();
+ if (show_table->file->index_flags(i, j, 0) & HA_READ_ORDER)
+ {
+ table->field[8]->store(((key_part->key_part_flag &
+ HA_REVERSE_SORT) ?
+ "D" : "A"), 1, cs);
+ table->field[8]->set_notnull();
+ }
+ KEY *key=show_table->key_info+i;
+ if (key->rec_per_key[j])
+ {
+ ha_rows records=(show_table->file->stats.records /
+ key->rec_per_key[j]);
+ table->field[9]->store((longlong) records, TRUE);
+ table->field[9]->set_notnull();
+ }
+ str= show_table->file->index_type(i);
+ table->field[13]->store(str, strlen(str), cs);
}
if (!(key_info->flags & HA_FULLTEXT) &&
(key_part->field &&
key_part->length !=
- show_table->field[key_part->fieldnr-1]->key_length()))
+ show_table->s->field[key_part->fieldnr-1]->key_length()))
{
table->field[10]->store((longlong) key_part->length /
- key_part->field->charset()->mbmaxlen, 1);
+ key_part->field->charset()->mbmaxlen, TRUE);
table->field[10]->set_notnull();
}
uint flags= key_part->field ? key_part->field->flags : 0;
const char *pos=(char*) ((flags & NOT_NULL_FLAG) ? "" : "YES");
- table->field[12]->store(pos, (uint) strlen(pos), cs);
- pos= show_table->file->index_type(i);
- table->field[13]->store(pos, (uint) strlen(pos), cs);
+ table->field[12]->store(pos, strlen(pos), cs);
if (!show_table->s->keys_in_use.is_set(i))
table->field[14]->store(STRING_WITH_LEN("disabled"), cs);
else
@@ -3165,19 +4303,28 @@ static int get_schema_stat_record(THD *thd, TABLE_LIST *tables,
static int get_schema_views_record(THD *thd, TABLE_LIST *tables,
TABLE *table, bool res,
- const char *base_name,
- const char *file_name)
+ LEX_STRING *db_name,
+ 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;
if (tables->view)
{
Security_context *sctx= thd->security_ctx;
- if (!tables->allowed_show)
+ if (!only_share && !tables->allowed_show)
{
if (!my_strcasecmp(system_charset_info, tables->definer.user.str,
sctx->priv_user) &&
@@ -3207,75 +4354,90 @@ static int get_schema_views_record(THD *thd, TABLE_LIST *tables,
#endif
}
restore_record(table, s->default_values);
- table->field[1]->store(tables->view_db.str, tables->view_db.length, cs);
- table->field[2]->store(tables->view_name.str, tables->view_name.length, cs);
- if (tables->allowed_show)
+ tmp_db_name= &tables->view_db;
+ tmp_table_name= &tables->view_name;
+ if (only_share)
{
- char buff[2048];
- String qwe_str(buff, sizeof(buff), cs);
- qwe_str.length(0);
- qwe_str.append(STRING_WITH_LEN("/* "));
- append_algorithm(tables, &qwe_str);
- qwe_str.append(STRING_WITH_LEN("*/ "));
- qwe_str.append(tables->query.str, tables->query.length);
- table->field[3]->store(qwe_str.ptr(), qwe_str.length(), cs);
+ tmp_db_name= db_name;
+ tmp_table_name= table_name;
}
-
- if (tables->with_check != VIEW_CHECK_NONE)
+ 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->with_check == VIEW_CHECK_LOCAL)
- table->field[4]->store(STRING_WITH_LEN("LOCAL"), cs);
+ 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);
+ }
else
- table->field[4]->store(STRING_WITH_LEN("CASCADED"), cs);
- }
- else
- table->field[4]->store(STRING_WITH_LEN("NONE"), cs);
+ table->field[4]->store(STRING_WITH_LEN("NONE"), cs);
- 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.
- */
- List<Item> *fields= &tables->view->select_lex.item_list;
- List_iterator<Item> it(*fields);
- Item *item;
- Item_field *field;
- /*
- check that at least one column in view is updatable
- */
- while ((item= it++))
+ updatable_view= 0;
+ if (tables->algorithm != VIEW_ALGORITHM_TMPTABLE)
{
- if ((field= item->filed_for_view_update()) && field->field &&
- !field->field->table->pos_in_table_list->schema_table)
+ /*
+ 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);
+ Item *item;
+ Item_field *field;
+ /*
+ check that at least one column in view is updatable
+ */
+ while ((item= it++))
{
- updatable_view= 1;
- break;
+ if ((field= item->filed_for_view_update()) && field->field &&
+ !field->field->table->pos_in_table_list->schema_table)
+ {
+ updatable_view= 1;
+ break;
+ }
}
+ if (updatable_view && !tables->view->can_be_merged())
+ updatable_view= 0;
}
- if (updatable_view && !tables->view->can_be_merged())
- updatable_view= 0;
+ if (updatable_view)
+ 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);
+
+ table->field[9]->store(tables->view_creation_ctx->
+ get_connection_cl()->name,
+ strlen(tables->view_creation_ctx->
+ get_connection_cl()->name), cs);
}
- if (updatable_view)
- table->field[5]->store(STRING_WITH_LEN("YES"), cs);
- else
- table->field[5]->store(STRING_WITH_LEN("NO"), cs);
- definer_len= (uint) (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);
+
if (schema_table_store_record(thd, table))
DBUG_RETURN(1);
- if (res)
+ if (res && thd->is_error())
push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
- thd->net.last_errno, thd->net.last_error);
+ thd->main_da.sql_errno(), thd->main_da.message());
}
if (res)
thd->clear_error();
@@ -3283,16 +4445,16 @@ static int get_schema_views_record(THD *thd, TABLE_LIST *tables,
}
-bool store_constraints(THD *thd, TABLE *table, const char *db,
- const char *tname, const char *key_name,
+bool store_constraints(THD *thd, TABLE *table, LEX_STRING *db_name,
+ LEX_STRING *table_name, const char *key_name,
uint key_len, const char *con_type, uint con_len)
{
CHARSET_INFO *cs= system_charset_info;
restore_record(table, s->default_values);
- table->field[1]->store(db, (uint) strlen(db), 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, (uint) strlen(db), cs);
- table->field[4]->store(tname, (uint) strlen(tname), cs);
+ table->field[3]->store(db_name->str, db_name->length, cs);
+ table->field[4]->store(table_name->str, table_name->length, cs);
table->field[5]->store(con_type, con_len, cs);
return schema_table_store_record(thd, table);
}
@@ -3300,15 +4462,15 @@ bool store_constraints(THD *thd, TABLE *table, const char *db,
static int get_schema_constraints_record(THD *thd, TABLE_LIST *tables,
TABLE *table, bool res,
- const char *base_name,
- const char *file_name)
+ LEX_STRING *db_name,
+ LEX_STRING *table_name)
{
DBUG_ENTER("get_schema_constraints_record");
if (res)
{
- if (!tables->view)
+ if (thd->is_error())
push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
- thd->net.last_errno, thd->net.last_error);
+ thd->main_da.sql_errno(), thd->main_da.message());
thd->clear_error();
DBUG_RETURN(0);
}
@@ -3328,15 +4490,15 @@ static int get_schema_constraints_record(THD *thd, TABLE_LIST *tables,
if (i == primary_key && !strcmp(key_info->name, primary_key_name))
{
- if (store_constraints(thd, table, base_name, file_name, key_info->name,
- (uint) strlen(key_info->name),
+ if (store_constraints(thd, table, db_name, table_name, key_info->name,
+ strlen(key_info->name),
STRING_WITH_LEN("PRIMARY KEY")))
DBUG_RETURN(1);
}
else if (key_info->flags & HA_NOSAME)
{
- if (store_constraints(thd, table, base_name, file_name, key_info->name,
- (uint) strlen(key_info->name),
+ if (store_constraints(thd, table, db_name, table_name, key_info->name,
+ strlen(key_info->name),
STRING_WITH_LEN("UNIQUE")))
DBUG_RETURN(1);
}
@@ -3347,9 +4509,9 @@ static int get_schema_constraints_record(THD *thd, TABLE_LIST *tables,
List_iterator_fast<FOREIGN_KEY_INFO> it(f_key_list);
while ((f_key_info=it++))
{
- if (store_constraints(thd, table, base_name, file_name,
+ if (store_constraints(thd, table, db_name, table_name,
f_key_info->forein_id->str,
- (uint) strlen(f_key_info->forein_id->str),
+ strlen(f_key_info->forein_id->str),
"FOREIGN KEY", 11))
DBUG_RETURN(1);
}
@@ -3358,25 +4520,27 @@ static int get_schema_constraints_record(THD *thd, TABLE_LIST *tables,
}
-static bool store_trigger(THD *thd, TABLE *table, const char *db,
- const char *tname, LEX_STRING *trigger_name,
+static bool store_trigger(THD *thd, TABLE *table, LEX_STRING *db_name,
+ LEX_STRING *table_name, LEX_STRING *trigger_name,
enum trg_event_type event,
enum trg_action_time_type timing,
LEX_STRING *trigger_stmt,
ulong sql_mode,
- LEX_STRING *definer_buffer)
+ LEX_STRING *definer_buffer,
+ LEX_STRING *client_cs_name,
+ LEX_STRING *connection_cl_name,
+ LEX_STRING *db_cl_name)
{
CHARSET_INFO *cs= system_charset_info;
- byte *sql_mode_str;
- ulong sql_mode_len;
+ LEX_STRING sql_mode_rep;
restore_record(table, s->default_values);
- table->field[1]->store(db, (uint) strlen(db), 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[5]->store(db, (uint) strlen(db), cs);
- table->field[6]->store(tname, (uint) strlen(tname), 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);
table->field[10]->store(STRING_WITH_LEN("ROW"), cs);
table->field[11]->store(trg_action_time_type_names[timing].str,
@@ -3384,20 +4548,23 @@ static bool store_trigger(THD *thd, TABLE *table, const char *db,
table->field[14]->store(STRING_WITH_LEN("OLD"), cs);
table->field[15]->store(STRING_WITH_LEN("NEW"), cs);
- sql_mode_str=
- sys_var_thd_sql_mode::symbolic_mode_representation(thd,
- sql_mode,
- &sql_mode_len);
- table->field[17]->store((const char*)sql_mode_str, sql_mode_len, cs);
- table->field[18]->store((const char *)definer_buffer->str, definer_buffer->length, cs);
+ sys_var_thd_sql_mode::symbolic_mode_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);
+ table->field[20]->store(connection_cl_name->str,
+ connection_cl_name->length, cs);
+ table->field[21]->store(db_cl_name->str, db_cl_name->length, cs);
+
return schema_table_store_record(thd, table);
}
static int get_schema_triggers_record(THD *thd, TABLE_LIST *tables,
TABLE *table, bool res,
- const char *base_name,
- const char *file_name)
+ LEX_STRING *db_name,
+ LEX_STRING *table_name)
{
DBUG_ENTER("get_schema_triggers_record");
/*
@@ -3406,9 +4573,9 @@ static int get_schema_triggers_record(THD *thd, TABLE_LIST *tables,
*/
if (res)
{
- if (!tables->view)
+ if (thd->is_error())
push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
- thd->net.last_errno, thd->net.last_error);
+ thd->main_da.sql_errno(), thd->main_da.message());
thd->clear_error();
DBUG_RETURN(0);
}
@@ -3417,10 +4584,8 @@ static int get_schema_triggers_record(THD *thd, TABLE_LIST *tables,
Table_triggers_list *triggers= tables->table->triggers;
int event, timing;
-#ifndef NO_EMBEDDED_ACCESS_CHECKS
- if (!(thd->security_ctx->master_access & SUPER_ACL))
+ if (check_table_access(thd, TRIGGER_ACL, tables, 1, TRUE))
goto ret;
-#endif
for (event= 0; event < (int)TRG_EVENT_MAX; event++)
{
@@ -3431,39 +4596,48 @@ static int get_schema_triggers_record(THD *thd, TABLE_LIST *tables,
ulong sql_mode;
char definer_holder[USER_HOST_BUFF_SIZE];
LEX_STRING definer_buffer;
+ LEX_STRING client_cs_name;
+ LEX_STRING connection_cl_name;
+ LEX_STRING db_cl_name;
+
definer_buffer.str= definer_holder;
if (triggers->get_trigger_info(thd, (enum trg_event_type) event,
(enum trg_action_time_type)timing,
&trigger_name, &trigger_stmt,
&sql_mode,
- &definer_buffer))
+ &definer_buffer,
+ &client_cs_name,
+ &connection_cl_name,
+ &db_cl_name))
continue;
- if (store_trigger(thd, table, base_name, file_name, &trigger_name,
+ if (store_trigger(thd, table, db_name, table_name, &trigger_name,
(enum trg_event_type) event,
(enum trg_action_time_type) timing, &trigger_stmt,
sql_mode,
- &definer_buffer))
+ &definer_buffer,
+ &client_cs_name,
+ &connection_cl_name,
+ &db_cl_name))
DBUG_RETURN(1);
}
}
}
-#ifndef NO_EMBEDDED_ACCESS_CHECKS
ret:
-#endif
DBUG_RETURN(0);
}
-void store_key_column_usage(TABLE *table, const char*db, const char *tname,
- const char *key_name, uint key_len,
- const char *con_type, uint con_len, longlong idx)
+void store_key_column_usage(TABLE *table, LEX_STRING *db_name,
+ LEX_STRING *table_name, const char *key_name,
+ uint key_len, const char *con_type, uint con_len,
+ longlong idx)
{
CHARSET_INFO *cs= system_charset_info;
- table->field[1]->store(db, (uint) strlen(db), cs);
+ table->field[1]->store(db_name->str, db_name->length, cs);
table->field[2]->store(key_name, key_len, cs);
- table->field[4]->store(db, (uint) strlen(db), cs);
- table->field[5]->store(tname, (uint) strlen(tname), 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);
table->field[7]->store((longlong) idx, TRUE);
}
@@ -3472,15 +4646,15 @@ void store_key_column_usage(TABLE *table, const char*db, const char *tname,
static int get_schema_key_column_usage_record(THD *thd,
TABLE_LIST *tables,
TABLE *table, bool res,
- const char *base_name,
- const char *file_name)
+ LEX_STRING *db_name,
+ LEX_STRING *table_name)
{
DBUG_ENTER("get_schema_key_column_usage_record");
if (res)
{
- if (!tables->view)
+ if (thd->is_error())
push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
- thd->net.last_errno, thd->net.last_error);
+ thd->main_da.sql_errno(), thd->main_da.message());
thd->clear_error();
DBUG_RETURN(0);
}
@@ -3505,11 +4679,11 @@ static int get_schema_key_column_usage_record(THD *thd,
{
f_idx++;
restore_record(table, s->default_values);
- store_key_column_usage(table, base_name, file_name,
+ store_key_column_usage(table, db_name, table_name,
key_info->name,
- (uint) strlen(key_info->name),
+ strlen(key_info->name),
key_part->field->field_name,
- (uint) strlen(key_part->field->field_name),
+ strlen(key_part->field->field_name),
(longlong) f_idx);
if (schema_table_store_record(thd, table))
DBUG_RETURN(1);
@@ -3532,7 +4706,7 @@ static int get_schema_key_column_usage_record(THD *thd,
r_info= it1++;
f_idx++;
restore_record(table, s->default_values);
- store_key_column_usage(table, base_name, file_name,
+ store_key_column_usage(table, db_name, table_name,
f_key_info->forein_id->str,
f_key_info->forein_id->length,
f_info->str, f_info->length,
@@ -3559,6 +4733,538 @@ 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)
+{
+ List_iterator<char> part_it(field_list);
+ ulong no_fields= field_list.elements;
+ const char *field_str;
+ str->length(0);
+ while ((field_str= part_it++))
+ {
+ str->append(field_str);
+ if (--no_fields != 0)
+ str->append(",");
+ }
+ return;
+}
+#endif
+
+
+static void store_schema_partitions_record(THD *thd, TABLE *schema_table,
+ TABLE *showing_table,
+ partition_element *part_elem,
+ handler *file, uint part_id)
+{
+ TABLE* table= schema_table;
+ CHARSET_INFO *cs= system_charset_info;
+ PARTITION_INFO stat_info;
+ MYSQL_TIME time;
+ file->get_dynamic_partition_info(&stat_info, part_id);
+ 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);
+ if (stat_info.max_data_file_length)
+ {
+ table->field[15]->store((longlong) stat_info.max_data_file_length, TRUE);
+ table->field[15]->set_notnull();
+ }
+ table->field[16]->store((longlong) stat_info.index_file_length, TRUE);
+ table->field[17]->store((longlong) stat_info.delete_length, TRUE);
+ if (stat_info.create_time)
+ {
+ thd->variables.time_zone->gmt_sec_to_TIME(&time,
+ (my_time_t)stat_info.create_time);
+ table->field[18]->store_time(&time, MYSQL_TIMESTAMP_DATETIME);
+ table->field[18]->set_notnull();
+ }
+ if (stat_info.update_time)
+ {
+ thd->variables.time_zone->gmt_sec_to_TIME(&time,
+ (my_time_t)stat_info.update_time);
+ table->field[19]->store_time(&time, MYSQL_TIMESTAMP_DATETIME);
+ table->field[19]->set_notnull();
+ }
+ if (stat_info.check_time)
+ {
+ thd->variables.time_zone->gmt_sec_to_TIME(&time,
+ (my_time_t)stat_info.check_time);
+ table->field[20]->store_time(&time, MYSQL_TIMESTAMP_DATETIME);
+ table->field[20]->set_notnull();
+ }
+ if (file->ha_table_flags() & (ulong) HA_HAS_CHECKSUM)
+ {
+ table->field[21]->store((longlong) stat_info.check_sum, TRUE);
+ table->field[21]->set_notnull();
+ }
+ if (part_elem)
+ {
+ if (part_elem->part_comment)
+ table->field[22]->store(part_elem->part_comment,
+ strlen(part_elem->part_comment), cs);
+ else
+ table->field[22]->store(STRING_WITH_LEN(""), cs);
+ if (part_elem->nodegroup_id != UNDEF_NODEGROUP)
+ table->field[23]->store((longlong) part_elem->nodegroup_id, TRUE);
+ else
+ table->field[23]->store(STRING_WITH_LEN("default"), cs);
+
+ table->field[24]->set_notnull();
+ if (part_elem->tablespace_name)
+ table->field[24]->store(part_elem->tablespace_name,
+ strlen(part_elem->tablespace_name), cs);
+ else
+ {
+ 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();
+ }
+ }
+ return;
+}
+
+
+static int get_schema_partitions_record(THD *thd, TABLE_LIST *tables,
+ TABLE *table, bool res,
+ LEX_STRING *db_name,
+ LEX_STRING *table_name)
+{
+ CHARSET_INFO *cs= system_charset_info;
+ char buff[61];
+ String tmp_res(buff, sizeof(buff), cs);
+ String tmp_str;
+ TABLE *show_table= tables->table;
+ handler *file;
+#ifdef WITH_PARTITION_STORAGE_ENGINE
+ partition_info *part_info;
+#endif
+ DBUG_ENTER("get_schema_partitions_record");
+
+ if (res)
+ {
+ if (thd->is_error())
+ push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ thd->main_da.sql_errno(), thd->main_da.message());
+ thd->clear_error();
+ DBUG_RETURN(0);
+ }
+ file= show_table->file;
+#ifdef WITH_PARTITION_STORAGE_ENGINE
+ part_info= show_table->part_info;
+ if (part_info)
+ {
+ partition_element *part_elem;
+ List_iterator<partition_element> part_it(part_info->partitions);
+ uint part_pos= 0, part_id= 0;
+
+ restore_record(table, s->default_values);
+ table->field[1]->store(db_name->str, db_name->length, cs);
+ table->field[2]->store(table_name->str, table_name->length, cs);
+
+
+ /* 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);
+ break;
+ case HASH_PARTITION:
+ tmp_res.length(0);
+ if (part_info->linear_hash_ind)
+ tmp_res.append(partition_keywords[PKW_LINEAR].str,
+ partition_keywords[PKW_LINEAR].length);
+ if (part_info->list_of_part_fields)
+ tmp_res.append(partition_keywords[PKW_KEY].str,
+ partition_keywords[PKW_KEY].length);
+ else
+ tmp_res.append(partition_keywords[PKW_HASH].str,
+ partition_keywords[PKW_HASH].length);
+ table->field[7]->store(tmp_res.ptr(), tmp_res.length(), cs);
+ break;
+ default:
+ DBUG_ASSERT(0);
+ my_error(ER_OUT_OF_RESOURCES, MYF(0));
+ current_thd->fatal_error();
+ DBUG_RETURN(1);
+ }
+ table->field[7]->set_notnull();
+
+ /* Partition expression */
+ if (part_info->part_expr)
+ {
+ table->field[9]->store(part_info->part_func_string,
+ part_info->part_func_len, cs);
+ }
+ else if (part_info->list_of_part_fields)
+ {
+ collect_partition_expr(part_info->part_field_list, &tmp_str);
+ table->field[9]->store(tmp_str.ptr(), tmp_str.length(), cs);
+ }
+ table->field[9]->set_notnull();
+
+ if (part_info->is_sub_partitioned())
+ {
+ /* Subpartition method */
+ tmp_res.length(0);
+ if (part_info->linear_hash_ind)
+ tmp_res.append(partition_keywords[PKW_LINEAR].str,
+ partition_keywords[PKW_LINEAR].length);
+ if (part_info->list_of_subpart_fields)
+ tmp_res.append(partition_keywords[PKW_KEY].str,
+ partition_keywords[PKW_KEY].length);
+ else
+ tmp_res.append(partition_keywords[PKW_HASH].str,
+ partition_keywords[PKW_HASH].length);
+ table->field[8]->store(tmp_res.ptr(), tmp_res.length(), cs);
+ table->field[8]->set_notnull();
+
+ /* Subpartition expression */
+ if (part_info->subpart_expr)
+ {
+ table->field[10]->store(part_info->subpart_func_string,
+ part_info->subpart_func_len, cs);
+ }
+ else if (part_info->list_of_subpart_fields)
+ {
+ collect_partition_expr(part_info->subpart_field_list, &tmp_str);
+ table->field[10]->store(tmp_str.ptr(), tmp_str.length(), cs);
+ }
+ table->field[10]->set_notnull();
+ }
+
+ while ((part_elem= part_it++))
+ {
+ table->field[3]->store(part_elem->partition_name,
+ strlen(part_elem->partition_name), cs);
+ table->field[3]->set_notnull();
+ /* PARTITION_ORDINAL_POSITION */
+ table->field[5]->store((longlong) ++part_pos, TRUE);
+ table->field[5]->set_notnull();
+
+ /* 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);
+ 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;
+ tmp_str.length(0);
+ tmp_res.length(0);
+ if (part_elem->has_null_value)
+ {
+ tmp_str.append("NULL");
+ if (no_items > 0)
+ tmp_str.append(",");
+ }
+ while ((list_value= list_val_it++))
+ {
+ 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 (--no_items != 0)
+ tmp_str.append(",");
+ };
+ table->field[11]->store(tmp_str.ptr(), tmp_str.length(), cs);
+ table->field[11]->set_notnull();
+ }
+
+ if (part_elem->subpartitions.elements)
+ {
+ List_iterator<partition_element> sub_it(part_elem->subpartitions);
+ partition_element *subpart_elem;
+ uint subpart_pos= 0;
+
+ while ((subpart_elem= sub_it++))
+ {
+ table->field[4]->store(subpart_elem->partition_name,
+ strlen(subpart_elem->partition_name), cs);
+ table->field[4]->set_notnull();
+ /* SUBPARTITION_ORDINAL_POSITION */
+ table->field[6]->store((longlong) ++subpart_pos, TRUE);
+ table->field[6]->set_notnull();
+
+ store_schema_partitions_record(thd, table, show_table, subpart_elem,
+ file, part_id);
+ part_id++;
+ if(schema_table_store_record(thd, table))
+ DBUG_RETURN(1);
+ }
+ }
+ else
+ {
+ store_schema_partitions_record(thd, table, show_table, part_elem,
+ file, part_id);
+ part_id++;
+ if(schema_table_store_record(thd, table))
+ DBUG_RETURN(1);
+ }
+ }
+ DBUG_RETURN(0);
+ }
+ else
+#endif
+ {
+ store_schema_partitions_record(thd, table, show_table, 0, file, 0);
+ if(schema_table_store_record(thd, table))
+ DBUG_RETURN(1);
+ }
+ DBUG_RETURN(0);
+}
+
+
+#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
+ I_S.EVENTS
+
+ Synopsis
+ copy_event_to_schema_table()
+ thd Thread
+ sch_table The schema table (information_schema.event)
+ event_table The event table to use for loading (mysql.event).
+
+ Returns
+ 0 OK
+ 1 Error
+*/
+
+int
+copy_event_to_schema_table(THD *thd, TABLE *sch_table, TABLE *event_table)
+{
+ const char *wild= thd->lex->wild ? thd->lex->wild->ptr() : NullS;
+ CHARSET_INFO *scs= system_charset_info;
+ MYSQL_TIME time;
+ Event_timed et;
+ DBUG_ENTER("copy_event_to_schema_table");
+
+ restore_record(sch_table, s->default_values);
+
+ if (et.load_from_row(thd, event_table))
+ {
+ my_error(ER_CANNOT_LOAD_FROM_TABLE, MYF(0), event_table->alias);
+ DBUG_RETURN(1);
+ }
+
+ if (!(!wild || !wild[0] || !wild_compare(et.name.str, wild, 0)))
+ DBUG_RETURN(0);
+
+ /*
+ Skip events in schemas one does not have access to. The check is
+ optimized. It's guaranteed in case of SHOW EVENTS that the user
+ 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)))
+ DBUG_RETURN(0);
+
+ /* ->field[0] is EVENT_CATALOG and is by default NULL */
+
+ sch_table->field[ISE_EVENT_SCHEMA]->
+ store(et.dbname.str, et.dbname.length,scs);
+ sch_table->field[ISE_EVENT_NAME]->
+ store(et.name.str, et.name.length, scs);
+ sch_table->field[ISE_DEFINER]->
+ store(et.definer.str, et.definer.length, scs);
+ const String *tz_name= et.time_zone->get_name();
+ sch_table->field[ISE_TIME_ZONE]->
+ store(tz_name->ptr(), tz_name->length(), scs);
+ sch_table->field[ISE_EVENT_BODY]->
+ store(STRING_WITH_LEN("SQL"), scs);
+ sch_table->field[ISE_EVENT_DEFINITION]->store(
+ et.body_utf8.str, et.body_utf8.length, scs);
+
+ /* SQL_MODE */
+ {
+ LEX_STRING sql_mode;
+ sys_var_thd_sql_mode::symbolic_mode_representation(thd, et.sql_mode,
+ &sql_mode);
+ sch_table->field[ISE_SQL_MODE]->
+ store(sql_mode.str, sql_mode.length, scs);
+ }
+
+ int not_used=0;
+
+ if (et.expression)
+ {
+ String show_str;
+ /* type */
+ sch_table->field[ISE_EVENT_TYPE]->store(STRING_WITH_LEN("RECURRING"), scs);
+
+ if (Events::reconstruct_interval_expression(&show_str, et.interval,
+ et.expression))
+ DBUG_RETURN(1);
+
+ sch_table->field[ISE_INTERVAL_VALUE]->set_notnull();
+ sch_table->field[ISE_INTERVAL_VALUE]->
+ store(show_str.ptr(), show_str.length(), scs);
+
+ LEX_STRING *ival= &interval_type_to_name[et.interval];
+ sch_table->field[ISE_INTERVAL_FIELD]->set_notnull();
+ sch_table->field[ISE_INTERVAL_FIELD]->store(ival->str, ival->length, scs);
+
+ /* starts & ends . STARTS is always set - see sql_yacc.yy */
+ et.time_zone->gmt_sec_to_TIME(&time, et.starts);
+ sch_table->field[ISE_STARTS]->set_notnull();
+ sch_table->field[ISE_STARTS]->
+ store_time(&time, MYSQL_TIMESTAMP_DATETIME);
+
+ if (!et.ends_null)
+ {
+ et.time_zone->gmt_sec_to_TIME(&time, et.ends);
+ sch_table->field[ISE_ENDS]->set_notnull();
+ sch_table->field[ISE_ENDS]->
+ store_time(&time, MYSQL_TIMESTAMP_DATETIME);
+ }
+ }
+ else
+ {
+ /* type */
+ sch_table->field[ISE_EVENT_TYPE]->store(STRING_WITH_LEN("ONE TIME"), scs);
+
+ et.time_zone->gmt_sec_to_TIME(&time, et.execute_at);
+ sch_table->field[ISE_EXECUTE_AT]->set_notnull();
+ sch_table->field[ISE_EXECUTE_AT]->
+ store_time(&time, MYSQL_TIMESTAMP_DATETIME);
+ }
+
+ /* status */
+
+ switch (et.status)
+ {
+ case Event_parse_data::ENABLED:
+ sch_table->field[ISE_STATUS]->store(STRING_WITH_LEN("ENABLED"), scs);
+ break;
+ case Event_parse_data::SLAVESIDE_DISABLED:
+ sch_table->field[ISE_STATUS]->store(STRING_WITH_LEN("SLAVESIDE_DISABLED"),
+ scs);
+ break;
+ case Event_parse_data::DISABLED:
+ sch_table->field[ISE_STATUS]->store(STRING_WITH_LEN("DISABLED"), scs);
+ break;
+ default:
+ DBUG_ASSERT(0);
+ }
+ sch_table->field[ISE_ORIGINATOR]->store(et.originator, TRUE);
+
+ /* on_completion */
+ if (et.on_completion == Event_parse_data::ON_COMPLETION_DROP)
+ sch_table->field[ISE_ON_COMPLETION]->
+ store(STRING_WITH_LEN("NOT PRESERVE"), scs);
+ else
+ sch_table->field[ISE_ON_COMPLETION]->
+ store(STRING_WITH_LEN("PRESERVE"), scs);
+
+ number_to_datetime(et.created, &time, 0, &not_used);
+ DBUG_ASSERT(not_used==0);
+ sch_table->field[ISE_CREATED]->store_time(&time, MYSQL_TIMESTAMP_DATETIME);
+
+ number_to_datetime(et.modified, &time, 0, &not_used);
+ DBUG_ASSERT(not_used==0);
+ sch_table->field[ISE_LAST_ALTERED]->
+ store_time(&time, MYSQL_TIMESTAMP_DATETIME);
+
+ if (et.last_executed)
+ {
+ et.time_zone->gmt_sec_to_TIME(&time, et.last_executed);
+ sch_table->field[ISE_LAST_EXECUTED]->set_notnull();
+ sch_table->field[ISE_LAST_EXECUTED]->
+ store_time(&time, MYSQL_TIMESTAMP_DATETIME);
+ }
+
+ sch_table->field[ISE_EVENT_COMMENT]->
+ store(et.comment.str, et.comment.length, scs);
+
+ sch_table->field[ISE_CLIENT_CS]->set_notnull();
+ sch_table->field[ISE_CLIENT_CS]->store(
+ et.creation_ctx->get_client_cs()->csname,
+ strlen(et.creation_ctx->get_client_cs()->csname),
+ scs);
+
+ sch_table->field[ISE_CONNECTION_CL]->set_notnull();
+ sch_table->field[ISE_CONNECTION_CL]->store(
+ et.creation_ctx->get_connection_cl()->name,
+ strlen(et.creation_ctx->get_connection_cl()->name),
+ scs);
+
+ sch_table->field[ISE_DB_CL]->set_notnull();
+ sch_table->field[ISE_DB_CL]->store(
+ et.creation_ctx->get_db_cl()->name,
+ strlen(et.creation_ctx->get_db_cl()->name),
+ scs);
+
+ if (schema_table_store_record(thd, sch_table))
+ DBUG_RETURN(1);
+
+ DBUG_RETURN(0);
+}
+#endif
+
int fill_open_tables(THD *thd, TABLE_LIST *tables, COND *cond)
{
DBUG_ENTER("fill_open_tables");
@@ -3573,8 +5279,8 @@ int fill_open_tables(THD *thd, TABLE_LIST *tables, COND *cond)
for (; open_list ; open_list=open_list->next)
{
restore_record(table, s->default_values);
- table->field[0]->store(open_list->db, (uint) strlen(open_list->db), cs);
- table->field[1]->store(open_list->table, (uint) strlen(open_list->table), cs);
+ table->field[0]->store(open_list->db, strlen(open_list->db), cs);
+ table->field[1]->store(open_list->table, strlen(open_list->table), cs);
table->field[2]->store((longlong) open_list->in_use, TRUE);
table->field[3]->store((longlong) open_list->locked, TRUE);
if (schema_table_store_record(thd, table))
@@ -3590,10 +5296,20 @@ int fill_variables(THD *thd, TABLE_LIST *tables, COND *cond)
int res= 0;
LEX *lex= thd->lex;
const char *wild= lex->wild ? lex->wild->ptr() : NullS;
- pthread_mutex_lock(&LOCK_global_system_variables);
- res= show_status_array(thd, wild, init_vars,
- lex->option_type, 0, "", tables->table);
- pthread_mutex_unlock(&LOCK_global_system_variables);
+ enum enum_schema_tables schema_table_idx=
+ get_schema_table_idx(tables->schema_table);
+ enum enum_var_type option_type= OPT_SESSION;
+ bool upper_case_names= (schema_table_idx != SCH_VARIABLES);
+ bool sorted_vars= (schema_table_idx == SCH_VARIABLES);
+
+ if (lex->option_type == OPT_GLOBAL ||
+ schema_table_idx == SCH_GLOBAL_VARIABLES)
+ option_type= OPT_GLOBAL;
+
+ 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, cond);
+ rw_unlock(&LOCK_system_variables_hash);
DBUG_RETURN(res);
}
@@ -3604,20 +5320,159 @@ int fill_status(THD *thd, TABLE_LIST *tables, COND *cond)
LEX *lex= thd->lex;
const char *wild= lex->wild ? lex->wild->ptr() : NullS;
int res= 0;
- STATUS_VAR tmp;
- ha_update_statistics(); /* Export engines statistics */
+ STATUS_VAR *tmp1, tmp;
+ enum enum_schema_tables schema_table_idx=
+ get_schema_table_idx(tables->schema_table);
+ enum enum_var_type option_type;
+ bool upper_case_names= (schema_table_idx != SCH_STATUS);
+
+ if (schema_table_idx == SCH_STATUS)
+ {
+ option_type= lex->option_type;
+ if (option_type == OPT_GLOBAL)
+ tmp1= &tmp;
+ else
+ tmp1= thd->initial_status_var;
+ }
+ else if (schema_table_idx == SCH_GLOBAL_STATUS)
+ {
+ option_type= OPT_GLOBAL;
+ tmp1= &tmp;
+ }
+ else
+ {
+ option_type= OPT_SESSION;
+ tmp1= &thd->status_var;
+ }
+
pthread_mutex_lock(&LOCK_status);
- if (lex->option_type == OPT_GLOBAL)
+ if (option_type == OPT_GLOBAL)
calc_sum_of_all_status(&tmp);
- res= show_status_array(thd, wild, status_vars, OPT_GLOBAL,
- (lex->option_type == OPT_GLOBAL ?
- &tmp: &thd->status_var), "",tables->table);
+ res= show_status_array(thd, wild,
+ (SHOW_VAR *)all_status_vars.buffer,
+ option_type, tmp1, "", tables->table,
+ upper_case_names, cond);
pthread_mutex_unlock(&LOCK_status);
DBUG_RETURN(res);
}
/*
+ Fill and store records into I_S.referential_constraints table
+
+ SYNOPSIS
+ get_referential_constraints_record()
+ thd thread handle
+ tables table list struct(processed table)
+ table I_S table
+ res 1 means the error during opening of the processed table
+ 0 means processed table is opened without error
+ base_name db name
+ file_name table name
+
+ RETURN
+ 0 ok
+ # error
+*/
+
+static int
+get_referential_constraints_record(THD *thd, TABLE_LIST *tables,
+ TABLE *table, bool res,
+ LEX_STRING *db_name, LEX_STRING *table_name)
+{
+ CHARSET_INFO *cs= system_charset_info;
+ DBUG_ENTER("get_referential_constraints_record");
+
+ if (res)
+ {
+ if (thd->is_error())
+ push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ thd->main_da.sql_errno(), thd->main_da.message());
+ thd->clear_error();
+ DBUG_RETURN(0);
+ }
+ if (!tables->view)
+ {
+ 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;
+ List_iterator_fast<FOREIGN_KEY_INFO> it(f_key_list);
+ while ((f_key_info= it++))
+ {
+ restore_record(table, s->default_values);
+ 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,
+ f_key_info->referenced_db->length, cs);
+ table->field[10]->store(f_key_info->referenced_table->str,
+ f_key_info->referenced_table->length, cs);
+ if (f_key_info->referenced_key_name)
+ {
+ table->field[5]->store(f_key_info->referenced_key_name->str,
+ f_key_info->referenced_key_name->length, cs);
+ table->field[5]->set_notnull();
+ }
+ else
+ table->field[5]->set_null();
+ table->field[6]->store(STRING_WITH_LEN("NONE"), cs);
+ table->field[7]->store(f_key_info->update_method->str,
+ f_key_info->update_method->length, cs);
+ table->field[8]->store(f_key_info->delete_method->str,
+ f_key_info->delete_method->length, cs);
+ if (schema_table_store_record(thd, table))
+ DBUG_RETURN(1);
+ }
+ }
+ DBUG_RETURN(0);
+}
+
+struct schema_table_ref
+{
+ const char *table_name;
+ ST_SCHEMA_TABLE *schema_table;
+};
+
+
+/*
+ Find schema_tables elment by name
+
+ SYNOPSIS
+ find_schema_table_in_plugin()
+ thd thread handler
+ plugin plugin
+ table_name table name
+
+ RETURN
+ 0 table not found
+ 1 found the schema table
+*/
+static my_bool find_schema_table_in_plugin(THD *thd, plugin_ref plugin,
+ void* p_table)
+{
+ schema_table_ref *p_schema_table= (schema_table_ref *)p_table;
+ const char* table_name= p_schema_table->table_name;
+ ST_SCHEMA_TABLE *schema_table= plugin_data(plugin, ST_SCHEMA_TABLE *);
+ DBUG_ENTER("find_schema_table_in_plugin");
+
+ if (!my_strcasecmp(system_charset_info,
+ schema_table->table_name,
+ table_name)) {
+ p_schema_table->schema_table= schema_table;
+ DBUG_RETURN(1);
+ }
+
+ DBUG_RETURN(0);
+}
+
+
+/*
Find schema_tables elment by name
SYNOPSIS
@@ -3627,20 +5482,29 @@ int fill_status(THD *thd, TABLE_LIST *tables, COND *cond)
RETURN
0 table not found
- # pointer to 'shema_tables' element
+ # pointer to 'schema_tables' element
*/
ST_SCHEMA_TABLE *find_schema_table(THD *thd, const char* table_name)
{
+ schema_table_ref schema_table_a;
ST_SCHEMA_TABLE *schema_table= schema_tables;
+ DBUG_ENTER("find_schema_table");
+
for (; schema_table->table_name; schema_table++)
{
if (!my_strcasecmp(system_charset_info,
schema_table->table_name,
table_name))
- return schema_table;
+ DBUG_RETURN(schema_table);
}
- return 0;
+
+ schema_table_a.table_name= table_name;
+ if (plugin_foreach(thd, find_schema_table_in_plugin,
+ MYSQL_INFORMATION_SCHEMA_PLUGIN, &schema_table_a))
+ DBUG_RETURN(schema_table_a.schema_table);
+
+ DBUG_RETURN(NULL);
}
@@ -3650,17 +5514,25 @@ ST_SCHEMA_TABLE *get_schema_table(enum enum_schema_tables schema_table_idx)
}
-/*
- Create information_schema table using schema_table data
+/**
+ Create information_schema table using schema_table data.
- SYNOPSIS
- create_schema_table()
+ @note
+ For MYSQL_TYPE_DECIMAL fields only, the field_length member has encoded
+ into it two numbers, based on modulus of base-10 numbers. In the ones
+ position is the number of decimals. Tens position is unused. In the
+ hundreds and thousands position is a two-digit decimal number representing
+ length. Encode this value with (decimals*100)+length , where
+ 0<decimals<10 and 0<=length<100 .
+
+ @param
thd thread handler
- schema_table pointer to 'shema_tables' element
- RETURN
- # Pointer to created table
- 0 Can't create table
+ @param table_list Used to pass I_S table information(fields info, tables
+ parameters etc) and table name.
+
+ @retval \# Pointer to created table
+ @retval NULL Can't create table
*/
TABLE *create_schema_table(THD *thd, TABLE_LIST *table_list)
@@ -3677,16 +5549,26 @@ TABLE *create_schema_table(THD *thd, TABLE_LIST *table_list)
for (; fields_info->field_name; fields_info++)
{
switch (fields_info->field_type) {
+ case MYSQL_TYPE_TINY:
case MYSQL_TYPE_LONG:
- if (!(item= new Item_int(fields_info->field_name,
- fields_info->value,
- fields_info->field_length)))
+ case MYSQL_TYPE_SHORT:
+ case MYSQL_TYPE_LONGLONG:
+ case MYSQL_TYPE_INT24:
+ if (!(item= new Item_return_int(fields_info->field_name,
+ fields_info->field_length,
+ fields_info->field_type,
+ fields_info->value)))
{
DBUG_RETURN(0);
}
+ item->unsigned_flag= (fields_info->field_flags & MY_I_S_UNSIGNED);
break;
+ case MYSQL_TYPE_DATE:
+ case MYSQL_TYPE_TIME:
case MYSQL_TYPE_TIMESTAMP:
- if (!(item=new Item_datetime(fields_info->field_name)))
+ case MYSQL_TYPE_DATETIME:
+ if (!(item=new Item_return_date_time(fields_info->field_name,
+ fields_info->field_type)))
{
DBUG_RETURN(0);
}
@@ -3697,6 +5579,32 @@ TABLE *create_schema_table(THD *thd, TABLE_LIST *table_list)
fields_info->field_length)) == NULL)
DBUG_RETURN(NULL);
break;
+ case MYSQL_TYPE_DECIMAL:
+ case MYSQL_TYPE_NEWDECIMAL:
+ if (!(item= new Item_decimal((longlong) fields_info->value, false)))
+ {
+ DBUG_RETURN(0);
+ }
+ item->unsigned_flag= (fields_info->field_flags & MY_I_S_UNSIGNED);
+ item->decimals= fields_info->field_length%10;
+ item->max_length= (fields_info->field_length/100)%100;
+ if (item->unsigned_flag == 0)
+ item->max_length+= 1;
+ if (item->decimals > 0)
+ item->max_length+= 1;
+ item->set_name(fields_info->field_name,
+ strlen(fields_info->field_name), cs);
+ break;
+ case MYSQL_TYPE_TINY_BLOB:
+ case MYSQL_TYPE_MEDIUM_BLOB:
+ case MYSQL_TYPE_LONG_BLOB:
+ case MYSQL_TYPE_BLOB:
+ if (!(item= new Item_blob(fields_info->field_name,
+ fields_info->field_length)))
+ {
+ DBUG_RETURN(0);
+ }
+ break;
default:
/* Don't let unimplemented types pass through. Could be a grave error. */
DBUG_ASSERT(fields_info->field_type == MYSQL_TYPE_STRING);
@@ -3706,15 +5614,15 @@ TABLE *create_schema_table(THD *thd, TABLE_LIST *table_list)
DBUG_RETURN(0);
}
item->set_name(fields_info->field_name,
- (uint) strlen(fields_info->field_name), cs);
+ strlen(fields_info->field_name), cs);
break;
}
field_list.push_back(item);
- item->maybe_null= fields_info->maybe_null;
+ item->maybe_null= (fields_info->field_flags & MY_I_S_MAYBE_NULL);
field_count++;
}
TMP_TABLE_PARAM *tmp_table_param =
- (TMP_TABLE_PARAM*) (thd->calloc(sizeof(TMP_TABLE_PARAM)));
+ (TMP_TABLE_PARAM*) (thd->alloc(sizeof(TMP_TABLE_PARAM)));
tmp_table_param->init();
tmp_table_param->table_charset= cs;
tmp_table_param->field_count= field_count;
@@ -3726,6 +5634,12 @@ TABLE *create_schema_table(THD *thd, TABLE_LIST *table_list)
TMP_TABLE_ALL_COLUMNS),
HA_POS_ERROR, table_list->alias)))
DBUG_RETURN(0);
+ my_bitmap_map* bitmaps=
+ (my_bitmap_map*) thd->alloc(bitmap_buffer_size(field_count));
+ bitmap_init(&table->def_read_set, (my_bitmap_map*) bitmaps, field_count,
+ FALSE);
+ table->read_set= &table->def_read_set;
+ bitmap_clear_all(table->read_set);
table_list->schema_table_param= tmp_table_param;
DBUG_RETURN(table);
}
@@ -3742,8 +5656,8 @@ TABLE *create_schema_table(THD *thd, TABLE_LIST *table_list)
schema_table pointer to 'schema_tables' element
RETURN
- -1 errror
- 0 success
+ 1 error
+ 0 success
*/
int make_old_format(THD *thd, ST_SCHEMA_TABLE *schema_table)
@@ -3759,7 +5673,7 @@ int make_old_format(THD *thd, ST_SCHEMA_TABLE *schema_table)
if (field)
{
field->set_name(field_info->old_name,
- (uint) strlen(field_info->old_name),
+ strlen(field_info->old_name),
system_charset_info);
if (add_item_to_list(thd, field))
return 1;
@@ -3828,7 +5742,7 @@ int make_table_names_old_format(THD *thd, ST_SCHEMA_TABLE *schema_table)
field= new Item_field(context, NullS, NullS, field_info->field_name);
if (add_item_to_list(thd, field))
return 1;
- field->set_name(field_info->old_name, (uint) strlen(field_info->old_name),
+ field->set_name(field_info->old_name, strlen(field_info->old_name),
system_charset_info);
}
return 0;
@@ -3854,7 +5768,7 @@ int make_columns_old_format(THD *thd, ST_SCHEMA_TABLE *schema_table)
if (field)
{
field->set_name(field_info->old_name,
- (uint) strlen(field_info->old_name),
+ strlen(field_info->old_name),
system_charset_info);
if (add_item_to_list(thd, field))
return 1;
@@ -3879,7 +5793,7 @@ int make_character_sets_old_format(THD *thd, ST_SCHEMA_TABLE *schema_table)
if (field)
{
field->set_name(field_info->old_name,
- (uint) strlen(field_info->old_name),
+ strlen(field_info->old_name),
system_charset_info);
if (add_item_to_list(thd, field))
return 1;
@@ -3891,7 +5805,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, -1};
+ int fields_arr[]= {2, 3, 4, 19, 16, 15, 14, 18, 20, 21, 22, -1};
int *field_num= fields_arr;
ST_FIELD_INFO *field_info;
Name_resolution_context *context= &thd->lex->select_lex.context;
@@ -3904,7 +5818,7 @@ int make_proc_old_format(THD *thd, ST_SCHEMA_TABLE *schema_table)
if (field)
{
field->set_name(field_info->old_name,
- (uint) strlen(field_info->old_name),
+ strlen(field_info->old_name),
system_charset_info);
if (add_item_to_list(thd, field))
return 1;
@@ -3933,9 +5847,7 @@ int mysql_schema_table(THD *thd, LEX *lex, TABLE_LIST *table_list)
TABLE *table;
DBUG_ENTER("mysql_schema_table");
if (!(table= table_list->schema_table->create_table(thd, table_list)))
- {
DBUG_RETURN(1);
- }
table->s->tmp_table= SYSTEM_TMP_TABLE;
table->grant.privilege= SELECT_ACL;
/*
@@ -3949,8 +5861,8 @@ int mysql_schema_table(THD *thd, LEX *lex, TABLE_LIST *table_list)
table->alias_name_used= my_strcasecmp(table_alias_charset,
table_list->schema_table_name,
table_list->alias);
- table_list->table_name= (char*) table->s->table_name;
- table_list->table_name_length= (uint) strlen(table->s->table_name);
+ table_list->table_name= table->s->table_name.str;
+ table_list->table_name_length= table->s->table_name.length;
table_list->table= table;
table->next= thd->derived_tables;
thd->derived_tables= table;
@@ -4018,19 +5930,19 @@ int make_schema_select(THD *thd, SELECT_LEX *sel,
{
ST_SCHEMA_TABLE *schema_table= get_schema_table(schema_table_idx);
LEX_STRING db, table;
- DBUG_ENTER("mysql_schema_select");
+ DBUG_ENTER("make_schema_select");
+ DBUG_PRINT("enter", ("mysql_schema_select: %s", schema_table->table_name));
/*
We have to make non const db_name & table_name
because of lower_case_table_names
*/
- make_lex_string(thd, &db, INFORMATION_SCHEMA_NAME.str,
- INFORMATION_SCHEMA_NAME.length, 0);
- make_lex_string(thd, &table, schema_table->table_name,
- (uint) strlen(schema_table->table_name), 0);
+ thd->make_lex_string(&db, INFORMATION_SCHEMA_NAME.str,
+ INFORMATION_SCHEMA_NAME.length, 0);
+ thd->make_lex_string(&table, schema_table->table_name,
+ strlen(schema_table->table_name), 0);
if (schema_table->old_format(thd, schema_table) || /* Handle old syntax */
!sel->add_table_to_list(thd, new Table_ident(thd, db, table, 0),
- 0, 0, TL_READ, (List<String> *) 0,
- (List<String> *) 0))
+ 0, 0, TL_READ))
{
DBUG_RETURN(1);
}
@@ -4067,10 +5979,20 @@ bool get_schema_tables_result(JOIN *join,
break;
TABLE_LIST *table_list= tab->table->pos_in_table_list;
- if (table_list->schema_table && thd->fill_derived_tables())
+ if (table_list->schema_table && thd->fill_information_schema_tables())
{
bool is_subselect= (&lex->unit != lex->current_select->master_unit() &&
lex->current_select->master_unit()->item);
+
+ /* A value of 0 indicates a dummy implementation */
+ if (table_list->schema_table->fill_table == 0)
+ continue;
+
+ /* skip I_S optimizations specific to get_all_tables */
+ if (thd->lex->describe &&
+ (table_list->schema_table->fill_table != get_all_tables))
+ continue;
+
/*
If schema table is already processed and
the statement is not a subselect then
@@ -4093,13 +6015,13 @@ bool get_schema_tables_result(JOIN *join,
{
table_list->table->file->extra(HA_EXTRA_NO_CACHE);
table_list->table->file->extra(HA_EXTRA_RESET_STATE);
- table_list->table->file->delete_all_rows();
+ table_list->table->file->ha_delete_all_rows();
free_io_cache(table_list->table);
filesort_free_buffers(table_list->table,1);
table_list->table->null_row= 0;
}
else
- table_list->table->file->records= 0;
+ table_list->table->file->stats.records= 0;
if (table_list->schema_table->fill_table(thd, table_list,
tab->select_cond))
@@ -4118,343 +6040,700 @@ bool get_schema_tables_result(JOIN *join,
DBUG_RETURN(result);
}
+struct run_hton_fill_schema_files_args
+{
+ TABLE_LIST *tables;
+ COND *cond;
+};
+
+static my_bool run_hton_fill_schema_files(THD *thd, plugin_ref plugin,
+ void *arg)
+{
+ struct run_hton_fill_schema_files_args *args=
+ (run_hton_fill_schema_files_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);
+ return false;
+}
+
+int fill_schema_files(THD *thd, TABLE_LIST *tables, COND *cond)
+{
+ DBUG_ENTER("fill_schema_files");
+
+ struct run_hton_fill_schema_files_args args;
+ args.tables= tables;
+ args.cond= cond;
+
+ plugin_foreach(thd, run_hton_fill_schema_files,
+ MYSQL_STORAGE_ENGINE_PLUGIN, &args);
+
+ DBUG_RETURN(0);
+}
+
ST_FIELD_INFO schema_fields_info[]=
{
- {"CATALOG_NAME", FN_REFLEN, MYSQL_TYPE_STRING, 0, 1, 0},
- {"SCHEMA_NAME", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, "Database"},
- {"DEFAULT_CHARACTER_SET_NAME", 64, MYSQL_TYPE_STRING, 0, 0, 0},
- {"DEFAULT_COLLATION_NAME", 64, MYSQL_TYPE_STRING, 0, 0, 0},
- {"SQL_PATH", FN_REFLEN, MYSQL_TYPE_STRING, 0, 1, 0},
- {0, 0, MYSQL_TYPE_STRING, 0, 0, 0}
+ {"CATALOG_NAME", FN_REFLEN, MYSQL_TYPE_STRING, 0, 1, 0, SKIP_OPEN_TABLE},
+ {"SCHEMA_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, "Database",
+ SKIP_OPEN_TABLE},
+ {"DEFAULT_CHARACTER_SET_NAME", MY_CS_NAME_SIZE, MYSQL_TYPE_STRING, 0, 0, 0,
+ SKIP_OPEN_TABLE},
+ {"DEFAULT_COLLATION_NAME", MY_CS_NAME_SIZE, MYSQL_TYPE_STRING, 0, 0, 0,
+ SKIP_OPEN_TABLE},
+ {"SQL_PATH", FN_REFLEN, MYSQL_TYPE_STRING, 0, 1, 0, SKIP_OPEN_TABLE},
+ {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}
};
ST_FIELD_INFO tables_fields_info[]=
{
- {"TABLE_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 1, 0},
- {"TABLE_SCHEMA",NAME_LEN, MYSQL_TYPE_STRING, 0, 0, 0},
- {"TABLE_NAME", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, "Name"},
- {"TABLE_TYPE", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, 0},
- {"ENGINE", NAME_LEN, MYSQL_TYPE_STRING, 0, 1, "Engine"},
- {"VERSION", MY_INT64_NUM_DECIMAL_DIGITS , MYSQL_TYPE_LONG, 0, 1, "Version"},
- {"ROW_FORMAT", 10, MYSQL_TYPE_STRING, 0, 1, "Row_format"},
- {"TABLE_ROWS", MY_INT64_NUM_DECIMAL_DIGITS , MYSQL_TYPE_LONG, 0, 1, "Rows"},
- {"AVG_ROW_LENGTH", MY_INT64_NUM_DECIMAL_DIGITS , MYSQL_TYPE_LONG, 0, 1,
- "Avg_row_length"},
- {"DATA_LENGTH", MY_INT64_NUM_DECIMAL_DIGITS , MYSQL_TYPE_LONG, 0, 1,
- "Data_length"},
- {"MAX_DATA_LENGTH", MY_INT64_NUM_DECIMAL_DIGITS , MYSQL_TYPE_LONG, 0, 1,
- "Max_data_length"},
- {"INDEX_LENGTH", MY_INT64_NUM_DECIMAL_DIGITS , MYSQL_TYPE_LONG, 0, 1,
- "Index_length"},
- {"DATA_FREE", MY_INT64_NUM_DECIMAL_DIGITS , MYSQL_TYPE_LONG, 0, 1, "Data_free"},
- {"AUTO_INCREMENT", MY_INT64_NUM_DECIMAL_DIGITS , MYSQL_TYPE_LONG, 0, 1,
- "Auto_increment"},
- {"CREATE_TIME", 0, MYSQL_TYPE_TIMESTAMP, 0, 1, "Create_time"},
- {"UPDATE_TIME", 0, MYSQL_TYPE_TIMESTAMP, 0, 1, "Update_time"},
- {"CHECK_TIME", 0, MYSQL_TYPE_TIMESTAMP, 0, 1, "Check_time"},
- {"TABLE_COLLATION", 64, MYSQL_TYPE_STRING, 0, 1, "Collation"},
- {"CHECKSUM", MY_INT64_NUM_DECIMAL_DIGITS , MYSQL_TYPE_LONG, 0, 1, "Checksum"},
- {"CREATE_OPTIONS", 255, MYSQL_TYPE_STRING, 0, 1, "Create_options"},
- {"TABLE_COMMENT", 80, MYSQL_TYPE_STRING, 0, 0, "Comment"},
- {0, 0, MYSQL_TYPE_STRING, 0, 0, 0}
+ {"TABLE_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 1, 0, SKIP_OPEN_TABLE},
+ {"TABLE_SCHEMA", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE},
+ {"TABLE_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, "Name",
+ SKIP_OPEN_TABLE},
+ {"TABLE_TYPE", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FRM_ONLY},
+ {"ENGINE", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, "Engine", OPEN_FRM_ONLY},
+ {"VERSION", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0,
+ (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), "Version", OPEN_FRM_ONLY},
+ {"ROW_FORMAT", 10, MYSQL_TYPE_STRING, 0, 1, "Row_format", OPEN_FULL_TABLE},
+ {"TABLE_ROWS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0,
+ (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), "Rows", OPEN_FULL_TABLE},
+ {"AVG_ROW_LENGTH", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0,
+ (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), "Avg_row_length", OPEN_FULL_TABLE},
+ {"DATA_LENGTH", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0,
+ (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), "Data_length", OPEN_FULL_TABLE},
+ {"MAX_DATA_LENGTH", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0,
+ (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), "Max_data_length", OPEN_FULL_TABLE},
+ {"INDEX_LENGTH", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0,
+ (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), "Index_length", OPEN_FULL_TABLE},
+ {"DATA_FREE", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0,
+ (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), "Data_free", OPEN_FULL_TABLE},
+ {"AUTO_INCREMENT", MY_INT64_NUM_DECIMAL_DIGITS , MYSQL_TYPE_LONGLONG, 0,
+ (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), "Auto_increment", OPEN_FULL_TABLE},
+ {"CREATE_TIME", 0, MYSQL_TYPE_DATETIME, 0, 1, "Create_time", OPEN_FULL_TABLE},
+ {"UPDATE_TIME", 0, MYSQL_TYPE_DATETIME, 0, 1, "Update_time", OPEN_FULL_TABLE},
+ {"CHECK_TIME", 0, MYSQL_TYPE_DATETIME, 0, 1, "Check_time", OPEN_FULL_TABLE},
+ {"TABLE_COLLATION", MY_CS_NAME_SIZE, MYSQL_TYPE_STRING, 0, 1, "Collation",
+ OPEN_FRM_ONLY},
+ {"CHECKSUM", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0,
+ (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), "Checksum", OPEN_FULL_TABLE},
+ {"CREATE_OPTIONS", 255, MYSQL_TYPE_STRING, 0, 1, "Create_options",
+ OPEN_FRM_ONLY},
+ {"TABLE_COMMENT", 80, 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},
- {"TABLE_SCHEMA", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, 0},
- {"TABLE_NAME", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, 0},
- {"COLUMN_NAME", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, "Field"},
- {"ORDINAL_POSITION", MY_INT64_NUM_DECIMAL_DIGITS , MYSQL_TYPE_LONG, 0, 0, 0},
- {"COLUMN_DEFAULT", MAX_FIELD_VARCHARLENGTH, MYSQL_TYPE_STRING, 0, 1, "Default"},
- {"IS_NULLABLE", 3, MYSQL_TYPE_STRING, 0, 0, "Null"},
- {"DATA_TYPE", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, 0},
- {"CHARACTER_MAXIMUM_LENGTH", MY_INT64_NUM_DECIMAL_DIGITS , MYSQL_TYPE_LONG, 0, 1,
- 0},
- {"CHARACTER_OCTET_LENGTH", MY_INT64_NUM_DECIMAL_DIGITS , MYSQL_TYPE_LONG, 0, 1, 0},
- {"NUMERIC_PRECISION", MY_INT64_NUM_DECIMAL_DIGITS , MYSQL_TYPE_LONG, 0, 1, 0},
- {"NUMERIC_SCALE", MY_INT64_NUM_DECIMAL_DIGITS , MYSQL_TYPE_LONG, 0, 1, 0},
- {"CHARACTER_SET_NAME", 64, MYSQL_TYPE_STRING, 0, 1, 0},
- {"COLLATION_NAME", 64, MYSQL_TYPE_STRING, 0, 1, "Collation"},
- {"COLUMN_TYPE", 65535, MYSQL_TYPE_STRING, 0, 0, "Type"},
- {"COLUMN_KEY", 3, MYSQL_TYPE_STRING, 0, 0, "Key"},
- {"EXTRA", 20, MYSQL_TYPE_STRING, 0, 0, "Extra"},
- {"PRIVILEGES", 80, MYSQL_TYPE_STRING, 0, 0, "Privileges"},
- {"COLUMN_COMMENT", 255, MYSQL_TYPE_STRING, 0, 0, "Comment"},
- {0, 0, MYSQL_TYPE_STRING, 0, 0, 0}
+ {"TABLE_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 1, 0, OPEN_FRM_ONLY},
+ {"TABLE_SCHEMA", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FRM_ONLY},
+ {"TABLE_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FRM_ONLY},
+ {"COLUMN_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, "Field",
+ OPEN_FRM_ONLY},
+ {"ORDINAL_POSITION", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0,
+ MY_I_S_UNSIGNED, 0, OPEN_FRM_ONLY},
+ {"COLUMN_DEFAULT", MAX_FIELD_VARCHARLENGTH, MYSQL_TYPE_STRING, 0,
+ 1, "Default", OPEN_FRM_ONLY},
+ {"IS_NULLABLE", 3, MYSQL_TYPE_STRING, 0, 0, "Null", OPEN_FRM_ONLY},
+ {"DATA_TYPE", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FRM_ONLY},
+ {"CHARACTER_MAXIMUM_LENGTH", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG,
+ 0, (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), 0, OPEN_FRM_ONLY},
+ {"CHARACTER_OCTET_LENGTH", MY_INT64_NUM_DECIMAL_DIGITS , MYSQL_TYPE_LONGLONG,
+ 0, (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), 0, OPEN_FRM_ONLY},
+ {"NUMERIC_PRECISION", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG,
+ 0, (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), 0, OPEN_FRM_ONLY},
+ {"NUMERIC_SCALE", MY_INT64_NUM_DECIMAL_DIGITS , MYSQL_TYPE_LONGLONG,
+ 0, (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), 0, OPEN_FRM_ONLY},
+ {"CHARACTER_SET_NAME", MY_CS_NAME_SIZE, MYSQL_TYPE_STRING, 0, 1, 0,
+ OPEN_FRM_ONLY},
+ {"COLLATION_NAME", MY_CS_NAME_SIZE, MYSQL_TYPE_STRING, 0, 1, "Collation",
+ OPEN_FRM_ONLY},
+ {"COLUMN_TYPE", 65535, MYSQL_TYPE_STRING, 0, 0, "Type", OPEN_FRM_ONLY},
+ {"COLUMN_KEY", 3, MYSQL_TYPE_STRING, 0, 0, "Key", OPEN_FRM_ONLY},
+ {"EXTRA", 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},
+ {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}
};
ST_FIELD_INFO charsets_fields_info[]=
{
- {"CHARACTER_SET_NAME", 64, MYSQL_TYPE_STRING, 0, 0, "Charset"},
- {"DEFAULT_COLLATE_NAME", 64, MYSQL_TYPE_STRING, 0, 0, "Default collation"},
- {"DESCRIPTION", 60, MYSQL_TYPE_STRING, 0, 0, "Description"},
- {"MAXLEN", 3 ,MYSQL_TYPE_LONG, 0, 0, "Maxlen"},
- {0, 0, MYSQL_TYPE_STRING, 0, 0, 0}
+ {"CHARACTER_SET_NAME", MY_CS_NAME_SIZE, MYSQL_TYPE_STRING, 0, 0, "Charset",
+ SKIP_OPEN_TABLE},
+ {"DEFAULT_COLLATE_NAME", MY_CS_NAME_SIZE, MYSQL_TYPE_STRING, 0, 0,
+ "Default collation", SKIP_OPEN_TABLE},
+ {"DESCRIPTION", 60, MYSQL_TYPE_STRING, 0, 0, "Description",
+ SKIP_OPEN_TABLE},
+ {"MAXLEN", 3, MYSQL_TYPE_LONGLONG, 0, 0, "Maxlen", SKIP_OPEN_TABLE},
+ {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}
};
ST_FIELD_INFO collation_fields_info[]=
{
- {"COLLATION_NAME", 64, MYSQL_TYPE_STRING, 0, 0, "Collation"},
- {"CHARACTER_SET_NAME", 64, MYSQL_TYPE_STRING, 0, 0, "Charset"},
- {"ID", MY_INT32_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Id"},
- {"IS_DEFAULT", 3, MYSQL_TYPE_STRING, 0, 0, "Default"},
- {"IS_COMPILED", 3, MYSQL_TYPE_STRING, 0, 0, "Compiled"},
- {"SORTLEN", 3 ,MYSQL_TYPE_LONG, 0, 0, "Sortlen"},
- {0, 0, MYSQL_TYPE_STRING, 0, 0, 0}
+ {"COLLATION_NAME", MY_CS_NAME_SIZE, MYSQL_TYPE_STRING, 0, 0, "Collation",
+ SKIP_OPEN_TABLE},
+ {"CHARACTER_SET_NAME", MY_CS_NAME_SIZE, MYSQL_TYPE_STRING, 0, 0, "Charset",
+ SKIP_OPEN_TABLE},
+ {"ID", MY_INT32_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0, 0, "Id",
+ SKIP_OPEN_TABLE},
+ {"IS_DEFAULT", 3, MYSQL_TYPE_STRING, 0, 0, "Default", SKIP_OPEN_TABLE},
+ {"IS_COMPILED", 3, MYSQL_TYPE_STRING, 0, 0, "Compiled", SKIP_OPEN_TABLE},
+ {"SORTLEN", 3, MYSQL_TYPE_LONGLONG, 0, 0, "Sortlen", SKIP_OPEN_TABLE},
+ {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}
+};
+
+
+ST_FIELD_INFO engines_fields_info[]=
+{
+ {"ENGINE", 64, MYSQL_TYPE_STRING, 0, 0, "Engine", SKIP_OPEN_TABLE},
+ {"SUPPORT", 8, MYSQL_TYPE_STRING, 0, 0, "Support", SKIP_OPEN_TABLE},
+ {"COMMENT", 80, MYSQL_TYPE_STRING, 0, 0, "Comment", SKIP_OPEN_TABLE},
+ {"TRANSACTIONS", 3, MYSQL_TYPE_STRING, 0, 1, "Transactions", SKIP_OPEN_TABLE},
+ {"XA", 3, MYSQL_TYPE_STRING, 0, 1, "XA", SKIP_OPEN_TABLE},
+ {"SAVEPOINTS", 3 ,MYSQL_TYPE_STRING, 0, 1, "Savepoints", SKIP_OPEN_TABLE},
+ {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}
};
+ST_FIELD_INFO events_fields_info[]=
+{
+ {"EVENT_CATALOG", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, 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},
+ {"TIME_ZONE", 64, MYSQL_TYPE_STRING, 0, 0, "Time zone", SKIP_OPEN_TABLE},
+ {"EVENT_BODY", 8, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE},
+ {"EVENT_DEFINITION", 65535, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE},
+ {"EVENT_TYPE", 9, MYSQL_TYPE_STRING, 0, 0, "Type", SKIP_OPEN_TABLE},
+ {"EXECUTE_AT", 0, MYSQL_TYPE_DATETIME, 0, 1, "Execute at", SKIP_OPEN_TABLE},
+ {"INTERVAL_VALUE", 256, MYSQL_TYPE_STRING, 0, 1, "Interval value",
+ SKIP_OPEN_TABLE},
+ {"INTERVAL_FIELD", 18, MYSQL_TYPE_STRING, 0, 1, "Interval field",
+ SKIP_OPEN_TABLE},
+ {"SQL_MODE", 32*256, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE},
+ {"STARTS", 0, MYSQL_TYPE_DATETIME, 0, 1, "Starts", SKIP_OPEN_TABLE},
+ {"ENDS", 0, MYSQL_TYPE_DATETIME, 0, 1, "Ends", SKIP_OPEN_TABLE},
+ {"STATUS", 18, MYSQL_TYPE_STRING, 0, 0, "Status", SKIP_OPEN_TABLE},
+ {"ON_COMPLETION", 12, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE},
+ {"CREATED", 0, MYSQL_TYPE_DATETIME, 0, 0, 0, SKIP_OPEN_TABLE},
+ {"LAST_ALTERED", 0, MYSQL_TYPE_DATETIME, 0, 0, 0, SKIP_OPEN_TABLE},
+ {"LAST_EXECUTED", 0, MYSQL_TYPE_DATETIME, 0, 1, 0, SKIP_OPEN_TABLE},
+ {"EVENT_COMMENT", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE},
+ {"ORIGINATOR", 10, MYSQL_TYPE_LONGLONG, 0, 0, "Originator", SKIP_OPEN_TABLE},
+ {"CHARACTER_SET_CLIENT", MY_CS_NAME_SIZE, MYSQL_TYPE_STRING, 0, 0,
+ "character_set_client", SKIP_OPEN_TABLE},
+ {"COLLATION_CONNECTION", MY_CS_NAME_SIZE, MYSQL_TYPE_STRING, 0, 0,
+ "collation_connection", SKIP_OPEN_TABLE},
+ {"DATABASE_COLLATION", MY_CS_NAME_SIZE, MYSQL_TYPE_STRING, 0, 0,
+ "Database Collation", SKIP_OPEN_TABLE},
+ {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}
+};
+
+
+
ST_FIELD_INFO coll_charset_app_fields_info[]=
{
- {"COLLATION_NAME", 64, MYSQL_TYPE_STRING, 0, 0, 0},
- {"CHARACTER_SET_NAME", 64, MYSQL_TYPE_STRING, 0, 0, 0},
- {0, 0, MYSQL_TYPE_STRING, 0, 0, 0}
+ {"COLLATION_NAME", MY_CS_NAME_SIZE, MYSQL_TYPE_STRING, 0, 0, 0,
+ SKIP_OPEN_TABLE},
+ {"CHARACTER_SET_NAME", MY_CS_NAME_SIZE, MYSQL_TYPE_STRING, 0, 0, 0,
+ SKIP_OPEN_TABLE},
+ {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}
};
ST_FIELD_INFO proc_fields_info[]=
{
- {"SPECIFIC_NAME", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, 0},
- {"ROUTINE_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 1, 0},
- {"ROUTINE_SCHEMA", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, "Db"},
- {"ROUTINE_NAME", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, "Name"},
- {"ROUTINE_TYPE", 9, MYSQL_TYPE_STRING, 0, 0, "Type"},
- {"DTD_IDENTIFIER", NAME_LEN, MYSQL_TYPE_STRING, 0, 1, 0},
- {"ROUTINE_BODY", 8, MYSQL_TYPE_STRING, 0, 0, 0},
- {"ROUTINE_DEFINITION", 65535, MYSQL_TYPE_STRING, 0, 1, 0},
- {"EXTERNAL_NAME", NAME_LEN, MYSQL_TYPE_STRING, 0, 1, 0},
- {"EXTERNAL_LANGUAGE", NAME_LEN, MYSQL_TYPE_STRING, 0, 1, 0},
- {"PARAMETER_STYLE", 8, MYSQL_TYPE_STRING, 0, 0, 0},
- {"IS_DETERMINISTIC", 3, MYSQL_TYPE_STRING, 0, 0, 0},
- {"SQL_DATA_ACCESS", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, 0},
- {"SQL_PATH", NAME_LEN, MYSQL_TYPE_STRING, 0, 1, 0},
- {"SECURITY_TYPE", 7, MYSQL_TYPE_STRING, 0, 0, "Security_type"},
- {"CREATED", 0, MYSQL_TYPE_TIMESTAMP, 0, 0, "Created"},
- {"LAST_ALTERED", 0, MYSQL_TYPE_TIMESTAMP, 0, 0, "Modified"},
- {"SQL_MODE", 65535, MYSQL_TYPE_STRING, 0, 0, 0},
- {"ROUTINE_COMMENT", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, "Comment"},
- {"DEFINER", 77, MYSQL_TYPE_STRING, 0, 0, "Definer"},
- {0, 0, MYSQL_TYPE_STRING, 0, 0, 0}
+ {"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_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},
+ {"ROUTINE_BODY", 8, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE},
+ {"ROUTINE_DEFINITION", 65535, MYSQL_TYPE_STRING, 0, 1, 0, SKIP_OPEN_TABLE},
+ {"EXTERNAL_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, 0, SKIP_OPEN_TABLE},
+ {"EXTERNAL_LANGUAGE", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, 0,
+ SKIP_OPEN_TABLE},
+ {"PARAMETER_STYLE", 8, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE},
+ {"IS_DETERMINISTIC", 3, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE},
+ {"SQL_DATA_ACCESS", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0,
+ SKIP_OPEN_TABLE},
+ {"SQL_PATH", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, 0, SKIP_OPEN_TABLE},
+ {"SECURITY_TYPE", 7, MYSQL_TYPE_STRING, 0, 0, "Security_type",
+ SKIP_OPEN_TABLE},
+ {"CREATED", 0, MYSQL_TYPE_DATETIME, 0, 0, "Created", SKIP_OPEN_TABLE},
+ {"LAST_ALTERED", 0, MYSQL_TYPE_DATETIME, 0, 0, "Modified", SKIP_OPEN_TABLE},
+ {"SQL_MODE", 32*256, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE},
+ {"ROUTINE_COMMENT", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, "Comment",
+ SKIP_OPEN_TABLE},
+ {"DEFINER", 77, MYSQL_TYPE_STRING, 0, 0, "Definer", SKIP_OPEN_TABLE},
+ {"CHARACTER_SET_CLIENT", MY_CS_NAME_SIZE, MYSQL_TYPE_STRING, 0, 0,
+ "character_set_client", SKIP_OPEN_TABLE},
+ {"COLLATION_CONNECTION", MY_CS_NAME_SIZE, MYSQL_TYPE_STRING, 0, 0,
+ "collation_connection", SKIP_OPEN_TABLE},
+ {"DATABASE_COLLATION", MY_CS_NAME_SIZE, MYSQL_TYPE_STRING, 0, 0,
+ "Database Collation", SKIP_OPEN_TABLE},
+ {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}
};
ST_FIELD_INFO stat_fields_info[]=
{
- {"TABLE_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 1, 0},
- {"TABLE_SCHEMA", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, 0},
- {"TABLE_NAME", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, "Table"},
- {"NON_UNIQUE", 1, MYSQL_TYPE_LONG, 0, 0, "Non_unique"},
- {"INDEX_SCHEMA", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, 0},
- {"INDEX_NAME", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, "Key_name"},
- {"SEQ_IN_INDEX", 2, MYSQL_TYPE_LONG, 0, 0, "Seq_in_index"},
- {"COLUMN_NAME", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, "Column_name"},
- {"COLLATION", 1, MYSQL_TYPE_STRING, 0, 1, "Collation"},
- {"CARDINALITY", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 1, "Cardinality"},
- {"SUB_PART", 3, MYSQL_TYPE_LONG, 0, 1, "Sub_part"},
- {"PACKED", 10, MYSQL_TYPE_STRING, 0, 1, "Packed"},
- {"NULLABLE", 3, MYSQL_TYPE_STRING, 0, 0, "Null"},
- {"INDEX_TYPE", 16, MYSQL_TYPE_STRING, 0, 0, "Index_type"},
- {"COMMENT", 16, MYSQL_TYPE_STRING, 0, 1, "Comment"},
- {0, 0, MYSQL_TYPE_STRING, 0, 0, 0}
+ {"TABLE_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 1, 0, OPEN_FRM_ONLY},
+ {"TABLE_SCHEMA", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FRM_ONLY},
+ {"TABLE_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, "Table", OPEN_FRM_ONLY},
+ {"NON_UNIQUE", 1, MYSQL_TYPE_LONGLONG, 0, 0, "Non_unique", OPEN_FRM_ONLY},
+ {"INDEX_SCHEMA", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FRM_ONLY},
+ {"INDEX_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, "Key_name",
+ OPEN_FRM_ONLY},
+ {"SEQ_IN_INDEX", 2, MYSQL_TYPE_LONGLONG, 0, 0, "Seq_in_index", OPEN_FRM_ONLY},
+ {"COLUMN_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, "Column_name",
+ OPEN_FRM_ONLY},
+ {"COLLATION", 1, MYSQL_TYPE_STRING, 0, 1, "Collation", OPEN_FRM_ONLY},
+ {"CARDINALITY", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0, 1,
+ "Cardinality", OPEN_FULL_TABLE},
+ {"SUB_PART", 3, MYSQL_TYPE_LONGLONG, 0, 1, "Sub_part", OPEN_FRM_ONLY},
+ {"PACKED", 10, MYSQL_TYPE_STRING, 0, 1, "Packed", OPEN_FRM_ONLY},
+ {"NULLABLE", 3, MYSQL_TYPE_STRING, 0, 0, "Null", OPEN_FRM_ONLY},
+ {"INDEX_TYPE", 16, MYSQL_TYPE_STRING, 0, 0, "Index_type", OPEN_FULL_TABLE},
+ {"COMMENT", 16, MYSQL_TYPE_STRING, 0, 1, "Comment", OPEN_FRM_ONLY},
+ {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},
- {"TABLE_SCHEMA", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, 0},
- {"TABLE_NAME", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, 0},
- {"VIEW_DEFINITION", 65535, MYSQL_TYPE_STRING, 0, 0, 0},
- {"CHECK_OPTION", 8, MYSQL_TYPE_STRING, 0, 0, 0},
- {"IS_UPDATABLE", 3, MYSQL_TYPE_STRING, 0, 0, 0},
- {"DEFINER", 77, MYSQL_TYPE_STRING, 0, 0, 0},
- {"SECURITY_TYPE", 7, MYSQL_TYPE_STRING, 0, 0, 0},
- {0, 0, MYSQL_TYPE_STRING, 0, 0, 0}
+ {"TABLE_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 1, 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},
+ {"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},
+ {"CHARACTER_SET_CLIENT", MY_CS_NAME_SIZE, MYSQL_TYPE_STRING, 0, 0, 0,
+ OPEN_FULL_TABLE},
+ {"COLLATION_CONNECTION", MY_CS_NAME_SIZE, MYSQL_TYPE_STRING, 0, 0, 0,
+ OPEN_FULL_TABLE},
+ {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}
};
ST_FIELD_INFO user_privileges_fields_info[]=
{
- {"GRANTEE", 81, MYSQL_TYPE_STRING, 0, 0, 0},
- {"TABLE_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 1, 0},
- {"PRIVILEGE_TYPE", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, 0},
- {"IS_GRANTABLE", 3, MYSQL_TYPE_STRING, 0, 0, 0},
- {0, 0, MYSQL_TYPE_STRING, 0, 0, 0}
+ {"GRANTEE", 81, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE},
+ {"TABLE_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 1, 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}
};
ST_FIELD_INFO schema_privileges_fields_info[]=
{
- {"GRANTEE", 81, MYSQL_TYPE_STRING, 0, 0, 0},
- {"TABLE_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 1, 0},
- {"TABLE_SCHEMA", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, 0},
- {"PRIVILEGE_TYPE", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, 0},
- {"IS_GRANTABLE", 3, MYSQL_TYPE_STRING, 0, 0, 0},
- {0, 0, MYSQL_TYPE_STRING, 0, 0, 0}
+ {"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_SCHEMA", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE},
+ {"PRIVILEGE_TYPE", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE},
+ {"IS_GRANTABLE", 3, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE},
+ {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}
};
ST_FIELD_INFO table_privileges_fields_info[]=
{
- {"GRANTEE", 81, MYSQL_TYPE_STRING, 0, 0, 0},
- {"TABLE_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 1, 0},
- {"TABLE_SCHEMA", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, 0},
- {"TABLE_NAME", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, 0},
- {"PRIVILEGE_TYPE", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, 0},
- {"IS_GRANTABLE", 3, MYSQL_TYPE_STRING, 0, 0, 0},
- {0, 0, MYSQL_TYPE_STRING, 0, 0, 0}
+ {"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_SCHEMA", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE},
+ {"TABLE_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE},
+ {"PRIVILEGE_TYPE", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE},
+ {"IS_GRANTABLE", 3, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE},
+ {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}
};
ST_FIELD_INFO column_privileges_fields_info[]=
{
- {"GRANTEE", 81, MYSQL_TYPE_STRING, 0, 0, 0},
- {"TABLE_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 1, 0},
- {"TABLE_SCHEMA", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, 0},
- {"TABLE_NAME", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, 0},
- {"COLUMN_NAME", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, 0},
- {"PRIVILEGE_TYPE", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, 0},
- {"IS_GRANTABLE", 3, MYSQL_TYPE_STRING, 0, 0, 0},
- {0, 0, MYSQL_TYPE_STRING, 0, 0, 0}
+ {"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_SCHEMA", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE},
+ {"TABLE_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE},
+ {"COLUMN_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE},
+ {"PRIVILEGE_TYPE", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE},
+ {"IS_GRANTABLE", 3, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE},
+ {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}
};
ST_FIELD_INFO table_constraints_fields_info[]=
{
- {"CONSTRAINT_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 1, 0},
- {"CONSTRAINT_SCHEMA", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, 0},
- {"CONSTRAINT_NAME", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, 0},
- {"TABLE_SCHEMA", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, 0},
- {"TABLE_NAME", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, 0},
- {"CONSTRAINT_TYPE", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, 0},
- {0, 0, MYSQL_TYPE_STRING, 0, 0, 0}
+ {"CONSTRAINT_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 1, 0, OPEN_FULL_TABLE},
+ {"CONSTRAINT_SCHEMA", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0,
+ OPEN_FULL_TABLE},
+ {"CONSTRAINT_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0,
+ OPEN_FULL_TABLE},
+ {"TABLE_SCHEMA", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FULL_TABLE},
+ {"TABLE_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FULL_TABLE},
+ {"CONSTRAINT_TYPE", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0,
+ OPEN_FULL_TABLE},
+ {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}
};
ST_FIELD_INFO key_column_usage_fields_info[]=
{
- {"CONSTRAINT_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 1, 0},
- {"CONSTRAINT_SCHEMA", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, 0},
- {"CONSTRAINT_NAME", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, 0},
- {"TABLE_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 1, 0},
- {"TABLE_SCHEMA", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, 0},
- {"TABLE_NAME", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, 0},
- {"COLUMN_NAME", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, 0},
- {"ORDINAL_POSITION", 10 ,MYSQL_TYPE_LONG, 0, 0, 0},
- {"POSITION_IN_UNIQUE_CONSTRAINT", 10 ,MYSQL_TYPE_LONG, 0, 1, 0},
- {"REFERENCED_TABLE_SCHEMA", NAME_LEN, MYSQL_TYPE_STRING, 0, 1, 0},
- {"REFERENCED_TABLE_NAME", NAME_LEN, MYSQL_TYPE_STRING, 0, 1, 0},
- {"REFERENCED_COLUMN_NAME", NAME_LEN, MYSQL_TYPE_STRING, 0, 1, 0},
- {0, 0, MYSQL_TYPE_STRING, 0, 0, 0}
+ {"CONSTRAINT_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 1, 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_SCHEMA", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FULL_TABLE},
+ {"TABLE_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FULL_TABLE},
+ {"COLUMN_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FULL_TABLE},
+ {"ORDINAL_POSITION", 10 ,MYSQL_TYPE_LONGLONG, 0, 0, 0, OPEN_FULL_TABLE},
+ {"POSITION_IN_UNIQUE_CONSTRAINT", 10 ,MYSQL_TYPE_LONGLONG, 0, 1, 0,
+ OPEN_FULL_TABLE},
+ {"REFERENCED_TABLE_SCHEMA", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, 0,
+ OPEN_FULL_TABLE},
+ {"REFERENCED_TABLE_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, 0,
+ OPEN_FULL_TABLE},
+ {"REFERENCED_COLUMN_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, 0,
+ OPEN_FULL_TABLE},
+ {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}
};
ST_FIELD_INFO table_names_fields_info[]=
{
- {"TABLE_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 1, 0},
- {"TABLE_SCHEMA",NAME_LEN, MYSQL_TYPE_STRING, 0, 0, 0},
- {"TABLE_NAME", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, "Tables_in_"},
- {"TABLE_TYPE", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, "Table_type"},
- {0, 0, MYSQL_TYPE_STRING, 0, 0, 0}
+ {"TABLE_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 1, 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, "Tables_in_",
+ SKIP_OPEN_TABLE},
+ {"TABLE_TYPE", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, "Table_type",
+ OPEN_FRM_ONLY},
+ {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}
};
ST_FIELD_INFO open_tables_fields_info[]=
{
- {"Database", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, "Database"},
- {"Table",NAME_LEN, MYSQL_TYPE_STRING, 0, 0, "Table"},
- {"In_use", 1, MYSQL_TYPE_LONG, 0, 0, "In_use"},
- {"Name_locked", 4, MYSQL_TYPE_LONG, 0, 0, "Name_locked"},
- {0, 0, MYSQL_TYPE_STRING, 0, 0, 0}
+ {"Database", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, "Database",
+ SKIP_OPEN_TABLE},
+ {"Table",NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, "Table", SKIP_OPEN_TABLE},
+ {"In_use", 1, MYSQL_TYPE_LONGLONG, 0, 0, "In_use", SKIP_OPEN_TABLE},
+ {"Name_locked", 4, MYSQL_TYPE_LONGLONG, 0, 0, "Name_locked", SKIP_OPEN_TABLE},
+ {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}
};
ST_FIELD_INFO triggers_fields_info[]=
{
- {"TRIGGER_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 1, 0},
- {"TRIGGER_SCHEMA",NAME_LEN, MYSQL_TYPE_STRING, 0, 0, 0},
- {"TRIGGER_NAME", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, "Trigger"},
- {"EVENT_MANIPULATION", 6, MYSQL_TYPE_STRING, 0, 0, "Event"},
- {"EVENT_OBJECT_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 1, 0},
- {"EVENT_OBJECT_SCHEMA",NAME_LEN, MYSQL_TYPE_STRING, 0, 0, 0},
- {"EVENT_OBJECT_TABLE", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, "Table"},
- {"ACTION_ORDER", 4, MYSQL_TYPE_LONG, 0, 0, 0},
- {"ACTION_CONDITION", 65535, MYSQL_TYPE_STRING, 0, 1, 0},
- {"ACTION_STATEMENT", 65535, MYSQL_TYPE_STRING, 0, 0, "Statement"},
- {"ACTION_ORIENTATION", 9, MYSQL_TYPE_STRING, 0, 0, 0},
- {"ACTION_TIMING", 6, MYSQL_TYPE_STRING, 0, 0, "Timing"},
- {"ACTION_REFERENCE_OLD_TABLE", NAME_LEN, MYSQL_TYPE_STRING, 0, 1, 0},
- {"ACTION_REFERENCE_NEW_TABLE", NAME_LEN, MYSQL_TYPE_STRING, 0, 1, 0},
- {"ACTION_REFERENCE_OLD_ROW", 3, MYSQL_TYPE_STRING, 0, 0, 0},
- {"ACTION_REFERENCE_NEW_ROW", 3, MYSQL_TYPE_STRING, 0, 0, 0},
- {"CREATED", 0, MYSQL_TYPE_TIMESTAMP, 0, 1, "Created"},
- {"SQL_MODE", 65535, MYSQL_TYPE_STRING, 0, 0, "sql_mode"},
- {"DEFINER", 65535, MYSQL_TYPE_STRING, 0, 0, "Definer"},
- {0, 0, MYSQL_TYPE_STRING, 0, 0, 0}
+ {"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_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},
+ {"EVENT_OBJECT_SCHEMA",NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0,
+ OPEN_FULL_TABLE},
+ {"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},
+ {"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},
+ {"ACTION_REFERENCE_OLD_TABLE", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, 0,
+ OPEN_FULL_TABLE},
+ {"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},
+ {"CHARACTER_SET_CLIENT", MY_CS_NAME_SIZE, MYSQL_TYPE_STRING, 0, 0,
+ "character_set_client", OPEN_FULL_TABLE},
+ {"COLLATION_CONNECTION", MY_CS_NAME_SIZE, MYSQL_TYPE_STRING, 0, 0,
+ "collation_connection", OPEN_FULL_TABLE},
+ {"DATABASE_COLLATION", MY_CS_NAME_SIZE, MYSQL_TYPE_STRING, 0, 0,
+ "Database Collation", OPEN_FULL_TABLE},
+ {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_SCHEMA",NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FULL_TABLE},
+ {"TABLE_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FULL_TABLE},
+ {"PARTITION_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, 0, OPEN_FULL_TABLE},
+ {"SUBPARTITION_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, 0,
+ OPEN_FULL_TABLE},
+ {"PARTITION_ORDINAL_POSITION", 21 , MYSQL_TYPE_LONGLONG, 0,
+ (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), 0, OPEN_FULL_TABLE},
+ {"SUBPARTITION_ORDINAL_POSITION", 21 , MYSQL_TYPE_LONGLONG, 0,
+ (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), 0, OPEN_FULL_TABLE},
+ {"PARTITION_METHOD", 12, MYSQL_TYPE_STRING, 0, 1, 0, OPEN_FULL_TABLE},
+ {"SUBPARTITION_METHOD", 12, MYSQL_TYPE_STRING, 0, 1, 0, OPEN_FULL_TABLE},
+ {"PARTITION_EXPRESSION", 65535, MYSQL_TYPE_STRING, 0, 1, 0, OPEN_FULL_TABLE},
+ {"SUBPARTITION_EXPRESSION", 65535, MYSQL_TYPE_STRING, 0, 1, 0,
+ OPEN_FULL_TABLE},
+ {"PARTITION_DESCRIPTION", 65535, MYSQL_TYPE_STRING, 0, 1, 0, OPEN_FULL_TABLE},
+ {"TABLE_ROWS", 21 , MYSQL_TYPE_LONGLONG, 0, MY_I_S_UNSIGNED, 0,
+ OPEN_FULL_TABLE},
+ {"AVG_ROW_LENGTH", 21 , MYSQL_TYPE_LONGLONG, 0, MY_I_S_UNSIGNED, 0,
+ OPEN_FULL_TABLE},
+ {"DATA_LENGTH", 21 , MYSQL_TYPE_LONGLONG, 0, MY_I_S_UNSIGNED, 0,
+ OPEN_FULL_TABLE},
+ {"MAX_DATA_LENGTH", 21 , MYSQL_TYPE_LONGLONG, 0,
+ (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), 0, OPEN_FULL_TABLE},
+ {"INDEX_LENGTH", 21 , MYSQL_TYPE_LONGLONG, 0, MY_I_S_UNSIGNED, 0,
+ OPEN_FULL_TABLE},
+ {"DATA_FREE", 21 , MYSQL_TYPE_LONGLONG, 0, MY_I_S_UNSIGNED, 0,
+ OPEN_FULL_TABLE},
+ {"CREATE_TIME", 0, MYSQL_TYPE_DATETIME, 0, 1, 0, OPEN_FULL_TABLE},
+ {"UPDATE_TIME", 0, MYSQL_TYPE_DATETIME, 0, 1, 0, OPEN_FULL_TABLE},
+ {"CHECK_TIME", 0, MYSQL_TYPE_DATETIME, 0, 1, 0, OPEN_FULL_TABLE},
+ {"CHECKSUM", 21 , MYSQL_TYPE_LONGLONG, 0,
+ (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), 0, OPEN_FULL_TABLE},
+ {"PARTITION_COMMENT", 80, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FULL_TABLE},
+ {"NODEGROUP", 12 , MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FULL_TABLE},
+ {"TABLESPACE_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, 0,
+ OPEN_FULL_TABLE},
+ {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}
};
ST_FIELD_INFO variables_fields_info[]=
{
- {"Variable_name", 80, MYSQL_TYPE_STRING, 0, 0, "Variable_name"},
- {"Value", FN_REFLEN, MYSQL_TYPE_STRING, 0, 0, "Value"},
- {0, 0, MYSQL_TYPE_STRING, 0, 0, 0}
+ {"VARIABLE_NAME", 64, MYSQL_TYPE_STRING, 0, 0, "Variable_name",
+ SKIP_OPEN_TABLE},
+ {"VARIABLE_VALUE", 1024, MYSQL_TYPE_STRING, 0, 1, "Value", SKIP_OPEN_TABLE},
+ {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}
+};
+
+
+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},
+ {"HOST", LIST_PROCESS_HOST_LEN, MYSQL_TYPE_STRING, 0, 0, "Host",
+ SKIP_OPEN_TABLE},
+ {"DB", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, "Db", SKIP_OPEN_TABLE},
+ {"COMMAND", 16, MYSQL_TYPE_STRING, 0, 0, "Command", SKIP_OPEN_TABLE},
+ {"TIME", 7, MYSQL_TYPE_LONGLONG, 0, 0, "Time", SKIP_OPEN_TABLE},
+ {"STATE", 64, MYSQL_TYPE_STRING, 0, 1, "State", SKIP_OPEN_TABLE},
+ {"INFO", PROCESS_LIST_INFO_WIDTH, MYSQL_TYPE_STRING, 0, 1, "Info",
+ SKIP_OPEN_TABLE},
+ {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}
+};
+
+
+ST_FIELD_INFO plugin_fields_info[]=
+{
+ {"PLUGIN_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, "Name",
+ SKIP_OPEN_TABLE},
+ {"PLUGIN_VERSION", 20, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE},
+ {"PLUGIN_STATUS", 10, MYSQL_TYPE_STRING, 0, 0, "Status", SKIP_OPEN_TABLE},
+ {"PLUGIN_TYPE", 80, MYSQL_TYPE_STRING, 0, 0, "Type", SKIP_OPEN_TABLE},
+ {"PLUGIN_TYPE_VERSION", 20, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE},
+ {"PLUGIN_LIBRARY", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, "Library",
+ SKIP_OPEN_TABLE},
+ {"PLUGIN_LIBRARY_VERSION", 20, MYSQL_TYPE_STRING, 0, 1, 0, SKIP_OPEN_TABLE},
+ {"PLUGIN_AUTHOR", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, 0, SKIP_OPEN_TABLE},
+ {"PLUGIN_DESCRIPTION", 65535, MYSQL_TYPE_STRING, 0, 1, 0, SKIP_OPEN_TABLE},
+ {"PLUGIN_LICENSE", 80, MYSQL_TYPE_STRING, 0, 1, "License", SKIP_OPEN_TABLE},
+ {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}
+};
+
+ST_FIELD_INFO files_fields_info[]=
+{
+ {"FILE_ID", 4, MYSQL_TYPE_LONGLONG, 0, 0, 0, SKIP_OPEN_TABLE},
+ {"FILE_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, 0, SKIP_OPEN_TABLE},
+ {"FILE_TYPE", 20, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE},
+ {"TABLESPACE_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, 0,
+ SKIP_OPEN_TABLE},
+ {"TABLE_CATALOG", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, 0, SKIP_OPEN_TABLE},
+ {"TABLE_SCHEMA", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, 0, SKIP_OPEN_TABLE},
+ {"TABLE_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, 0, SKIP_OPEN_TABLE},
+ {"LOGFILE_GROUP_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, 0,
+ SKIP_OPEN_TABLE},
+ {"LOGFILE_GROUP_NUMBER", 4, MYSQL_TYPE_LONGLONG, 0, 1, 0, SKIP_OPEN_TABLE},
+ {"ENGINE", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE},
+ {"FULLTEXT_KEYS", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, 0, SKIP_OPEN_TABLE},
+ {"DELETED_ROWS", 4, MYSQL_TYPE_LONGLONG, 0, 1, 0, SKIP_OPEN_TABLE},
+ {"UPDATE_COUNT", 4, MYSQL_TYPE_LONGLONG, 0, 1, 0, SKIP_OPEN_TABLE},
+ {"FREE_EXTENTS", 4, MYSQL_TYPE_LONGLONG, 0, 1, 0, SKIP_OPEN_TABLE},
+ {"TOTAL_EXTENTS", 4, MYSQL_TYPE_LONGLONG, 0, 1, 0, SKIP_OPEN_TABLE},
+ {"EXTENT_SIZE", 4, MYSQL_TYPE_LONGLONG, 0, 0, 0, SKIP_OPEN_TABLE},
+ {"INITIAL_SIZE", 21, MYSQL_TYPE_LONGLONG, 0,
+ (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), 0, SKIP_OPEN_TABLE},
+ {"MAXIMUM_SIZE", 21, MYSQL_TYPE_LONGLONG, 0,
+ (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), 0, SKIP_OPEN_TABLE},
+ {"AUTOEXTEND_SIZE", 21, MYSQL_TYPE_LONGLONG, 0,
+ (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), 0, SKIP_OPEN_TABLE},
+ {"CREATION_TIME", 0, MYSQL_TYPE_DATETIME, 0, 1, 0, SKIP_OPEN_TABLE},
+ {"LAST_UPDATE_TIME", 0, MYSQL_TYPE_DATETIME, 0, 1, 0, SKIP_OPEN_TABLE},
+ {"LAST_ACCESS_TIME", 0, MYSQL_TYPE_DATETIME, 0, 1, 0, SKIP_OPEN_TABLE},
+ {"RECOVER_TIME", 4, MYSQL_TYPE_LONGLONG, 0, 1, 0, SKIP_OPEN_TABLE},
+ {"TRANSACTION_COUNTER", 4, MYSQL_TYPE_LONGLONG, 0, 1, 0, SKIP_OPEN_TABLE},
+ {"VERSION", 21 , MYSQL_TYPE_LONGLONG, 0,
+ (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), "Version", SKIP_OPEN_TABLE},
+ {"ROW_FORMAT", 10, MYSQL_TYPE_STRING, 0, 1, "Row_format", SKIP_OPEN_TABLE},
+ {"TABLE_ROWS", 21 , MYSQL_TYPE_LONGLONG, 0,
+ (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), "Rows", SKIP_OPEN_TABLE},
+ {"AVG_ROW_LENGTH", 21 , MYSQL_TYPE_LONGLONG, 0,
+ (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), "Avg_row_length", SKIP_OPEN_TABLE},
+ {"DATA_LENGTH", 21 , MYSQL_TYPE_LONGLONG, 0,
+ (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), "Data_length", SKIP_OPEN_TABLE},
+ {"MAX_DATA_LENGTH", 21 , MYSQL_TYPE_LONGLONG, 0,
+ (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), "Max_data_length", SKIP_OPEN_TABLE},
+ {"INDEX_LENGTH", 21 , MYSQL_TYPE_LONGLONG, 0,
+ (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), "Index_length", SKIP_OPEN_TABLE},
+ {"DATA_FREE", 21 , MYSQL_TYPE_LONGLONG, 0,
+ (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), "Data_free", SKIP_OPEN_TABLE},
+ {"CREATE_TIME", 0, MYSQL_TYPE_DATETIME, 0, 1, "Create_time", SKIP_OPEN_TABLE},
+ {"UPDATE_TIME", 0, MYSQL_TYPE_DATETIME, 0, 1, "Update_time", SKIP_OPEN_TABLE},
+ {"CHECK_TIME", 0, MYSQL_TYPE_DATETIME, 0, 1, "Check_time", SKIP_OPEN_TABLE},
+ {"CHECKSUM", 21 , MYSQL_TYPE_LONGLONG, 0,
+ (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), "Checksum", SKIP_OPEN_TABLE},
+ {"STATUS", 20, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE},
+ {"EXTRA", 255, MYSQL_TYPE_STRING, 0, 1, 0, SKIP_OPEN_TABLE},
+ {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}
+};
+
+void init_fill_schema_files_row(TABLE* table)
+{
+ int i;
+ for(i=0; files_fields_info[i].field_name!=NULL; i++)
+ table->field[i]->set_null();
+
+ table->field[IS_FILES_STATUS]->set_notnull();
+ table->field[IS_FILES_STATUS]->store("NORMAL", 6, system_charset_info);
+}
+
+ST_FIELD_INFO referential_constraints_fields_info[]=
+{
+ {"CONSTRAINT_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 1, 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,
+ OPEN_FULL_TABLE},
+ {"UNIQUE_CONSTRAINT_SCHEMA", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0,
+ OPEN_FULL_TABLE},
+ {"UNIQUE_CONSTRAINT_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0,
+ MY_I_S_MAYBE_NULL, 0, OPEN_FULL_TABLE},
+ {"MATCH_OPTION", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FULL_TABLE},
+ {"UPDATE_RULE", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FULL_TABLE},
+ {"DELETE_RULE", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FULL_TABLE},
+ {"TABLE_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FULL_TABLE},
+ {"REFERENCED_TABLE_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0,
+ OPEN_FULL_TABLE},
+ {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}
};
/*
Description of ST_FIELD_INFO in table.h
+
+ Make sure that the order of schema_tables and enum_schema_tables are the same.
+
*/
ST_SCHEMA_TABLE schema_tables[]=
{
{"CHARACTER_SETS", charsets_fields_info, create_schema_table,
- fill_schema_charsets, make_character_sets_old_format, 0, -1, -1, 0},
+ fill_schema_charsets, make_character_sets_old_format, 0, -1, -1, 0, 0},
{"COLLATIONS", collation_fields_info, create_schema_table,
- fill_schema_collation, make_old_format, 0, -1, -1, 0},
+ fill_schema_collation, make_old_format, 0, -1, -1, 0, 0},
{"COLLATION_CHARACTER_SET_APPLICABILITY", coll_charset_app_fields_info,
- create_schema_table, fill_schema_coll_charset_app, 0, 0, -1, -1, 0},
+ create_schema_table, fill_schema_coll_charset_app, 0, 0, -1, -1, 0, 0},
{"COLUMNS", columns_fields_info, create_schema_table,
- get_all_tables, make_columns_old_format, get_schema_column_record, 1, 2, 0},
+ get_all_tables, make_columns_old_format, get_schema_column_record, 1, 2, 0,
+ OPTIMIZE_I_S_TABLE|OPEN_VIEW_FULL},
{"COLUMN_PRIVILEGES", column_privileges_fields_info, create_schema_table,
- fill_schema_column_privileges, 0, 0, -1, -1, 0},
+ fill_schema_column_privileges, 0, 0, -1, -1, 0, 0},
+ {"ENGINES", engines_fields_info, create_schema_table,
+ fill_schema_engines, make_old_format, 0, -1, -1, 0, 0},
+#ifdef HAVE_EVENT_SCHEDULER
+ {"EVENTS", events_fields_info, create_schema_table,
+ Events::fill_schema_events, make_old_format, 0, -1, -1, 0, 0},
+#else
+ {"EVENTS", events_fields_info, create_schema_table,
+ 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},
+ {"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,
+ fill_variables, make_old_format, 0, 0, -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},
+ get_all_tables, 0, get_schema_key_column_usage_record, 4, 5, 0,
+ OPEN_TABLE_ONLY},
{"OPEN_TABLES", open_tables_fields_info, create_schema_table,
- fill_open_tables, make_old_format, 0, -1, -1, 1},
+ fill_open_tables, make_old_format, 0, -1, -1, 1, 0},
+ {"PARTITIONS", partitions_fields_info, create_schema_table,
+ get_all_tables, 0, get_schema_partitions_record, 1, 2, 0, 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,
+ fill_schema_processlist, make_old_format, 0, -1, -1, 0, 0},
+ {"PROFILING", query_profile_statistics_info, create_schema_table,
+ fill_query_profile_statistics_info, make_profile_table_for_show,
+ 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,
- fill_schema_proc, make_proc_old_format, 0, -1, -1, 0},
+ fill_schema_proc, make_proc_old_format, 0, -1, -1, 0, 0},
{"SCHEMATA", schema_fields_info, create_schema_table,
- fill_schema_shemata, make_schemata_old_format, 0, 1, -1, 0},
+ fill_schema_schemata, make_schemata_old_format, 0, 1, -1, 0, 0},
{"SCHEMA_PRIVILEGES", schema_privileges_fields_info, create_schema_table,
- fill_schema_schema_privileges, 0, 0, -1, -1, 0},
+ fill_schema_schema_privileges, 0, 0, -1, -1, 0, 0},
+ {"SESSION_STATUS", variables_fields_info, create_schema_table,
+ fill_status, make_old_format, 0, 0, -1, 0, 0},
+ {"SESSION_VARIABLES", variables_fields_info, create_schema_table,
+ fill_variables, make_old_format, 0, 0, -1, 0, 0},
{"STATISTICS", stat_fields_info, create_schema_table,
- get_all_tables, make_old_format, get_schema_stat_record, 1, 2, 0},
+ get_all_tables, make_old_format, get_schema_stat_record, 1, 2, 0,
+ OPEN_TABLE_ONLY|OPTIMIZE_I_S_TABLE},
{"STATUS", variables_fields_info, create_schema_table, fill_status,
- make_old_format, 0, -1, -1, 1},
+ make_old_format, 0, 0, -1, 1, 0},
{"TABLES", tables_fields_info, create_schema_table,
- get_all_tables, make_old_format, get_schema_tables_record, 1, 2, 0},
+ get_all_tables, make_old_format, get_schema_tables_record, 1, 2, 0,
+ OPTIMIZE_I_S_TABLE},
{"TABLE_CONSTRAINTS", table_constraints_fields_info, create_schema_table,
- get_all_tables, 0, get_schema_constraints_record, 3, 4, 0},
+ get_all_tables, 0, get_schema_constraints_record, 3, 4, 0, OPEN_TABLE_ONLY},
{"TABLE_NAMES", table_names_fields_info, create_schema_table,
- get_all_tables, make_table_names_old_format, 0, 1, 2, 1},
+ get_all_tables, make_table_names_old_format, 0, 1, 2, 1, 0},
{"TABLE_PRIVILEGES", table_privileges_fields_info, create_schema_table,
- fill_schema_table_privileges, 0, 0, -1, -1, 0},
+ fill_schema_table_privileges, 0, 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},
+ 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,
- fill_schema_user_privileges, 0, 0, -1, -1, 0},
+ fill_schema_user_privileges, 0, 0, -1, -1, 0, 0},
{"VARIABLES", variables_fields_info, create_schema_table, fill_variables,
- make_old_format, 0, -1, -1, 1},
+ make_old_format, 0, 0, -1, 1, 0},
{"VIEWS", view_fields_info, create_schema_table,
- get_all_tables, 0, get_schema_views_record, 1, 2, 0},
- {0, 0, 0, 0, 0, 0, 0, 0, 0}
+ get_all_tables, 0, get_schema_views_record, 1, 2, 0,
+ OPEN_VIEW_ONLY|OPTIMIZE_I_S_TABLE},
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
};
@@ -4462,3 +6741,357 @@ ST_SCHEMA_TABLE schema_tables[]=
template class List_iterator_fast<char>;
template class List<char>;
#endif
+
+int initialize_schema_table(st_plugin_int *plugin)
+{
+ ST_SCHEMA_TABLE *schema_table;
+ DBUG_ENTER("initialize_schema_table");
+
+ if (!(schema_table= (ST_SCHEMA_TABLE *)my_malloc(sizeof(ST_SCHEMA_TABLE),
+ MYF(MY_WME | MY_ZEROFILL))))
+ DBUG_RETURN(1);
+ /* Historical Requirement */
+ plugin->data= schema_table; // shortcut for the future
+ if (plugin->plugin->init)
+ {
+ schema_table->create_table= create_schema_table;
+ schema_table->old_format= make_old_format;
+ schema_table->idx_field1= -1,
+ schema_table->idx_field2= -1;
+
+ /* Make the name available to the init() function. */
+ schema_table->table_name= plugin->name.str;
+
+ if (plugin->plugin->init(schema_table))
+ {
+ sql_print_error("Plugin '%s' init function returned error.",
+ plugin->name.str);
+ plugin->data= NULL;
+ my_free(schema_table, MYF(0));
+ DBUG_RETURN(1);
+ }
+
+ /* Make sure the plugin name is not set inside the init() function. */
+ schema_table->table_name= plugin->name.str;
+ }
+ DBUG_RETURN(0);
+}
+
+int finalize_schema_table(st_plugin_int *plugin)
+{
+ ST_SCHEMA_TABLE *schema_table= (ST_SCHEMA_TABLE *)plugin->data;
+ DBUG_ENTER("finalize_schema_table");
+
+ if (schema_table && plugin->plugin->deinit)
+ {
+ DBUG_PRINT("info", ("Deinitializing plugin: '%s'", plugin->name.str));
+ if (plugin->plugin->deinit(NULL))
+ {
+ DBUG_PRINT("warning", ("Plugin '%s' deinit function returned error.",
+ plugin->name.str));
+ }
+ my_free(schema_table, MYF(0));
+ }
+ DBUG_RETURN(0);
+}
+
+
+/**
+ Output trigger information (SHOW CREATE TRIGGER) to the client.
+
+ @param thd Thread context.
+ @param triggers List of triggers for the table.
+ @param trigger_idx Index of the trigger to dump.
+
+ @return Operation status
+ @retval TRUE Error.
+ @retval FALSE Success.
+*/
+
+static bool show_create_trigger_impl(THD *thd,
+ Table_triggers_list *triggers,
+ int trigger_idx)
+{
+ int ret_code;
+
+ Protocol *p= thd->protocol;
+ List<Item> fields;
+
+ LEX_STRING trg_name;
+ ulonglong trg_sql_mode;
+ LEX_STRING trg_sql_mode_str;
+ LEX_STRING trg_sql_original_stmt;
+ LEX_STRING trg_client_cs_name;
+ LEX_STRING trg_connection_cl_name;
+ LEX_STRING trg_db_cl_name;
+
+ CHARSET_INFO *trg_client_cs;
+
+ /*
+ TODO: Check privileges here. This functionality will be added by
+ implementation of the following WL items:
+ - WL#2227: New privileges for new objects
+ - WL#3482: Protect SHOW CREATE PROCEDURE | FUNCTION | VIEW | TRIGGER
+ properly
+
+ SHOW TRIGGERS and I_S.TRIGGERS will be affected too.
+ */
+
+ /* Prepare trigger "object". */
+
+ triggers->get_trigger_info(thd,
+ trigger_idx,
+ &trg_name,
+ &trg_sql_mode,
+ &trg_sql_original_stmt,
+ &trg_client_cs_name,
+ &trg_connection_cl_name,
+ &trg_db_cl_name);
+
+ sys_var_thd_sql_mode::symbolic_mode_representation(thd,
+ trg_sql_mode,
+ &trg_sql_mode_str);
+
+ /* Resolve trigger client character set. */
+
+ if (resolve_charset(trg_client_cs_name.str, NULL, &trg_client_cs))
+ return TRUE;
+
+ /* Send header. */
+
+ fields.push_back(new Item_empty_string("Trigger", NAME_LEN));
+ fields.push_back(new Item_empty_string("sql_mode", trg_sql_mode_str.length));
+
+ {
+ /*
+ NOTE: SQL statement field must be not less than 1024 in order not to
+ confuse old clients.
+ */
+
+ Item_empty_string *stmt_fld=
+ new Item_empty_string("SQL Original Statement",
+ max(trg_sql_original_stmt.length, 1024));
+
+ stmt_fld->maybe_null= TRUE;
+
+ fields.push_back(stmt_fld);
+ }
+
+ fields.push_back(new Item_empty_string("character_set_client",
+ MY_CS_NAME_SIZE));
+
+ fields.push_back(new Item_empty_string("collation_connection",
+ MY_CS_NAME_SIZE));
+
+ 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))
+ return TRUE;
+
+ /* Send data. */
+
+ p->prepare_for_resend();
+
+ p->store(trg_name.str,
+ trg_name.length,
+ system_charset_info);
+
+ p->store(trg_sql_mode_str.str,
+ trg_sql_mode_str.length,
+ system_charset_info);
+
+ p->store(trg_sql_original_stmt.str,
+ trg_sql_original_stmt.length,
+ trg_client_cs);
+
+ p->store(trg_client_cs_name.str,
+ trg_client_cs_name.length,
+ system_charset_info);
+
+ p->store(trg_connection_cl_name.str,
+ trg_connection_cl_name.length,
+ system_charset_info);
+
+ p->store(trg_db_cl_name.str,
+ trg_db_cl_name.length,
+ system_charset_info);
+
+ ret_code= p->write();
+
+ if (!ret_code)
+ my_eof(thd);
+
+ return ret_code != 0;
+}
+
+
+/**
+ Read TRN and TRG files to obtain base table name for the specified
+ trigger name and construct TABE_LIST object for the base table.
+
+ @param thd Thread context.
+ @param trg_name Trigger name.
+
+ @return TABLE_LIST object corresponding to the base table.
+
+ TODO: This function is a copy&paste from add_table_to_list() and
+ sp_add_to_query_tables(). The problem is that in order to be compatible
+ with Stored Programs (Prepared Statements), we should not touch thd->lex.
+ The "source" functions also add created TABLE_LIST object to the
+ thd->lex->query_tables.
+
+ The plan to eliminate this copy&paste is to:
+
+ - get rid of sp_add_to_query_tables() and use Lex::add_table_to_list().
+ Only add_table_to_list() must be used to add tables from the parser
+ into Lex::query_tables list.
+
+ - 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)
+{
+ char trn_path_buff[FN_REFLEN];
+
+ LEX_STRING trn_path= { trn_path_buff, 0 };
+ LEX_STRING tbl_name;
+
+ build_trn_path(thd, trg_name, &trn_path);
+
+ if (check_trn_exists(&trn_path))
+ {
+ my_error(ER_TRG_DOES_NOT_EXIST, MYF(0));
+ return NULL;
+ }
+
+ if (load_table_name_for_trigger(thd, trg_name, &trn_path, &tbl_name))
+ 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), 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);
+
+ table->table_name_length= tbl_name.length;
+ table->table_name= thd->strmake(tbl_name.str, tbl_name.length);
+
+ table->alias= thd->strmake(tbl_name.str, tbl_name.length);
+
+ table->lock_type= TL_IGNORE;
+ table->cacheable_table= 0;
+
+ 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.
+
+ @param thd Thread context.
+ @param trg_name Trigger name.
+
+ @return Operation status
+ @retval TRUE Error.
+ @retval FALSE Success.
+*/
+
+bool show_create_trigger(THD *thd, const sp_name *trg_name)
+{
+ TABLE_LIST *lst= get_trigger_table(thd, trg_name);
+
+ if (!lst)
+ 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.
+ */
+
+ uint num_tables; /* NOTE: unused, only to pass to open_tables(). */
+
+ if (open_tables(thd, &lst, &num_tables, 0))
+ {
+ my_error(ER_TRG_CANT_OPEN_TABLE, MYF(0),
+ (const char *) trg_name->m_db.str,
+ (const char *) lst->table_name);
+
+ return TRUE;
+
+ /* Perform closing actions and return error status. */
+ }
+
+ DBUG_ASSERT(num_tables == 1);
+
+ Table_triggers_list *triggers= lst->table->triggers;
+
+ if (!triggers)
+ {
+ my_error(ER_TRG_DOES_NOT_EXIST, MYF(0));
+ return TRUE;
+ }
+
+ int trigger_idx= triggers->find_trigger_by_name(&trg_name->m_name);
+
+ if (trigger_idx < 0)
+ {
+ my_error(ER_TRG_CORRUPTED_FILE, MYF(0),
+ (const char *) trg_name->m_db.str,
+ (const char *) lst->table_name);
+
+ return TRUE;
+ }
+
+ return 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.
+ */
+}
diff --git a/sql/sql_show.h b/sql/sql_show.h
new file mode 100644
index 00000000000..fa067a46033
--- /dev/null
+++ b/sql/sql_show.h
@@ -0,0 +1,41 @@
+/* Copyright 2006-2008 MySQL AB, 2008 Sun Microsystems, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#ifndef SQL_SHOW_H
+#define SQL_SHOW_H
+
+/* Forward declarations */
+class String;
+class THD;
+struct st_ha_create_information;
+typedef st_ha_create_information HA_CREATE_INFO;
+struct TABLE_LIST;
+
+enum find_files_result {
+ FIND_FILES_OK,
+ FIND_FILES_OOM,
+ FIND_FILES_DIR
+};
+
+find_files_result find_files(THD *thd, List<LEX_STRING> *files, const char *db,
+ const char *path, const char *wild, bool dir);
+
+int store_create_info(THD *thd, TABLE_LIST *table_list, String *packet,
+ HA_CREATE_INFO *create_info_arg, bool show_database);
+int view_store_create_info(THD *thd, TABLE_LIST *table, String *buff);
+
+int copy_event_to_schema_table(THD *thd, TABLE *sch_table, TABLE *event_table);
+
+#endif /* SQL_SHOW_H */
diff --git a/sql/sql_string.cc b/sql/sql_string.cc
index e7051883d36..9024f98bd92 100644
--- a/sql/sql_string.cc
+++ b/sql/sql_string.cc
@@ -32,7 +32,7 @@
required by the string function
*/
-extern gptr sql_alloc(unsigned size);
+extern uchar* sql_alloc(unsigned size);
extern void sql_element_free(void *ptr);
#include "sql_string.h"
@@ -95,29 +95,19 @@ bool String::realloc(uint32 alloc_length)
return FALSE;
}
-bool String::set(longlong num, CHARSET_INFO *cs)
+bool String::set_int(longlong num, bool unsigned_flag, CHARSET_INFO *cs)
{
uint l=20*cs->mbmaxlen+1;
+ int base= unsigned_flag ? 10 : -10;
if (alloc(l))
return TRUE;
- str_length=(uint32) (cs->cset->longlong10_to_str)(cs,Ptr,l,-10,num);
+ str_length=(uint32) (cs->cset->longlong10_to_str)(cs,Ptr,l,base,num);
str_charset=cs;
return FALSE;
}
-bool String::set(ulonglong num, CHARSET_INFO *cs)
-{
- uint l=20*cs->mbmaxlen+1;
-
- if (alloc(l))
- return TRUE;
- str_length=(uint32) (cs->cset->longlong10_to_str)(cs,Ptr,l,10,num);
- str_charset=cs;
- return FALSE;
-}
-
-bool String::set(double num,uint decimals, CHARSET_INFO *cs)
+bool String::set_real(double num,uint decimals, CHARSET_INFO *cs)
{
char buff[331];
uint dummy_errors;
@@ -335,7 +325,7 @@ bool String::set_or_copy_aligned(const char *str,uint32 arg_length,
return copy_aligned(str, arg_length, offset, cs);
}
- /* Copy with charset convertion */
+ /* Copy with charset conversion */
bool String::copy(const char *str, uint32 arg_length,
CHARSET_INFO *from_cs, CHARSET_INFO *to_cs, uint *errors)
@@ -506,7 +496,7 @@ bool String::append(FILE* file, uint32 arg_length, myf my_flags)
{
if (realloc(str_length+arg_length))
return TRUE;
- if (my_fread(file, (byte*) Ptr + str_length, arg_length, my_flags))
+ if (my_fread(file, (uchar*) Ptr + str_length, arg_length, my_flags))
{
shrink(str_length);
return TRUE;
@@ -520,7 +510,7 @@ bool String::append(IO_CACHE* file, uint32 arg_length)
{
if (realloc(str_length+arg_length))
return TRUE;
- if (my_b_read(file, (byte*) Ptr + str_length, arg_length))
+ if (my_b_read(file, (uchar*) Ptr + str_length, arg_length))
{
shrink(str_length);
return TRUE;
@@ -645,7 +635,7 @@ bool String::replace(uint32 offset,uint32 arg_length,
{
if (realloc(str_length+(uint32) diff))
return TRUE;
- bmove_upp(Ptr+str_length+diff,Ptr+str_length,
+ bmove_upp((uchar*) Ptr+str_length+diff, (uchar*) Ptr+str_length,
str_length-offset-arg_length);
}
if (to_length)
@@ -723,8 +713,8 @@ void String::qs_append(uint i)
int sortcmp(const String *s,const String *t, CHARSET_INFO *cs)
{
return cs->coll->strnncollsp(cs,
- (unsigned char *) s->ptr(),s->length(),
- (unsigned char *) t->ptr(),t->length(), 0);
+ (uchar *) s->ptr(),s->length(),
+ (uchar *) t->ptr(),t->length(), 0);
}
@@ -737,7 +727,7 @@ int sortcmp(const String *s,const String *t, CHARSET_INFO *cs)
t Second string
NOTE:
- Strings are compared as a stream of unsigned chars
+ Strings are compared as a stream of uchars
RETURN
< 0 s < t
@@ -805,10 +795,8 @@ copy_and_convert(char *to, uint32 to_length, CHARSET_INFO *to_cs,
const uchar *from_end= (const uchar*) from+from_length;
char *to_start= to;
uchar *to_end= (uchar*) to+to_length;
- int (*mb_wc)(struct charset_info_st *, my_wc_t *, const uchar *,
- const uchar *) = from_cs->cset->mb_wc;
- int (*wc_mb)(struct charset_info_st *, my_wc_t, uchar *s, uchar *e)=
- to_cs->cset->wc_mb;
+ my_charset_conv_mb_wc mb_wc= from_cs->cset->mb_wc;
+ my_charset_conv_wc_mb wc_mb= to_cs->cset->wc_mb;
uint error_count= 0;
while (1)
@@ -852,6 +840,68 @@ outp:
}
+/**
+ Copy string with HEX-encoding of "bad" characters.
+
+ @details This functions copies the string pointed by "src"
+ to the string pointed by "dst". Not more than "srclen" bytes
+ are read from "src". Any sequences of bytes representing
+ a not-well-formed substring (according to cs) are hex-encoded,
+ and all well-formed substrings (according to cs) are copied as is.
+ Not more than "dstlen" bytes are written to "dst". The number
+ of bytes written to "dst" is returned.
+
+ @param cs character set pointer of the destination string
+ @param[out] dst destination string
+ @param dstlen size of dst
+ @param src source string
+ @param srclen length of src
+
+ @retval result length
+*/
+
+size_t
+my_copy_with_hex_escaping(CHARSET_INFO *cs,
+ char *dst, size_t dstlen,
+ const char *src, size_t srclen)
+{
+ const char *srcend= src + srclen;
+ char *dst0= dst;
+
+ for ( ; src < srcend ; )
+ {
+ size_t chlen;
+ if ((chlen= my_ismbchar(cs, src, srcend)))
+ {
+ if (dstlen < chlen)
+ break; /* purecov: inspected */
+ memcpy(dst, src, chlen);
+ src+= chlen;
+ dst+= chlen;
+ dstlen-= chlen;
+ }
+ else if (*src & 0x80)
+ {
+ if (dstlen < 4)
+ break; /* purecov: inspected */
+ *dst++= '\\';
+ *dst++= 'x';
+ *dst++= _dig_vec_upper[((unsigned char) *src) >> 4];
+ *dst++= _dig_vec_upper[((unsigned char) *src) & 15];
+ src++;
+ dstlen-= 4;
+ }
+ else
+ {
+ if (dstlen < 1)
+ break; /* purecov: inspected */
+ *dst++= *src++;
+ dstlen--;
+ }
+ }
+ return dst - dst0;
+}
+
/*
copy a string,
with optional character set conversion,
@@ -950,10 +1000,8 @@ well_formed_copy_nchars(CHARSET_INFO *to_cs,
{
int cnvres;
my_wc_t wc;
- int (*mb_wc)(struct charset_info_st *, my_wc_t *,
- const uchar *, const uchar *)= from_cs->cset->mb_wc;
- int (*wc_mb)(struct charset_info_st *, my_wc_t,
- uchar *s, uchar *e)= to_cs->cset->wc_mb;
+ my_charset_conv_mb_wc mb_wc= from_cs->cset->mb_wc;
+ my_charset_conv_wc_mb wc_mb= to_cs->cset->wc_mb;
const uchar *from_end= (const uchar*) from + from_length;
uchar *to_end= (uchar*) to + to_length;
char *to_start= to;
diff --git a/sql/sql_string.h b/sql/sql_string.h
index 4432451464e..be11fea70dc 100644
--- a/sql/sql_string.h
+++ b/sql/sql_string.h
@@ -23,8 +23,6 @@
#define NOT_FIXED_DEC 31
#endif
-#define STRING_WITH_LEN(X) ((const char*) X), ((uint) (sizeof(X) - 1))
-
class String;
int sortcmp(const String *a,const String *b, CHARSET_INFO *cs);
String *copy_if_not_alloced(String *a,String *b,uint32 arg_length);
@@ -39,6 +37,9 @@ uint32 well_formed_copy_nchars(CHARSET_INFO *to_cs,
const char **well_formed_error_pos,
const char **cannot_convert_error_pos,
const char **from_end_pos);
+size_t my_copy_with_hex_escaping(CHARSET_INFO *cs,
+ char *dst, size_t dstlen,
+ const char *src, size_t srclen);
class String
{
@@ -149,9 +150,12 @@ public:
}
str_charset=cs;
}
- bool set(longlong num, CHARSET_INFO *cs);
- bool set(ulonglong num, CHARSET_INFO *cs);
- bool set(double num,uint decimals, CHARSET_INFO *cs);
+ bool set_int(longlong num, bool unsigned_flag, CHARSET_INFO *cs);
+ bool set(longlong num, CHARSET_INFO *cs)
+ { return set_int(num, false, cs); }
+ bool set(ulonglong num, CHARSET_INFO *cs)
+ { return set_int((longlong)num, true, cs); }
+ bool set_real(double num,uint decimals, CHARSET_INFO *cs);
/*
PMG 2004.11.12
diff --git a/sql/sql_table.cc b/sql/sql_table.cc
index 45707246f90..94426e914f8 100644
--- a/sql/sql_table.cc
+++ b/sql/sql_table.cc
@@ -1,4 +1,4 @@
-/* Copyright (C) 2000-2004 MySQL AB
+/* Copyright 2000-2008 MySQL AB, 2008 Sun Microsystems, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -16,61 +16,1456 @@
/* drop and alter of tables */
#include "mysql_priv.h"
-#ifdef HAVE_BERKELEY_DB
-#include "ha_berkeley.h"
-#endif
#include <hash.h>
#include <myisam.h>
#include <my_dir.h>
#include "sp_head.h"
#include "sql_trigger.h"
+#include "sql_show.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);
static char *make_unique_key_name(const char *field_name,KEY *start,KEY *end);
static int copy_data_between_tables(TABLE *from,TABLE *to,
- List<create_field> &create, bool ignore,
+ List<Create_field> &create, bool ignore,
uint order_num, ORDER *order,
ha_rows *copied,ha_rows *deleted,
enum enum_enable_or_disable keys_onoff,
bool error_if_not_empty);
-static bool prepare_blob_field(THD *thd, create_field *sql_field);
-static bool check_engine(THD *thd, const char *table_name,
- enum db_type *new_engine);
-static void set_tmp_file_path(char *buf, size_t bufsize, THD *thd);
+static bool prepare_blob_field(THD *thd, Create_field *sql_field);
+static bool check_engine(THD *, const char *, HA_CREATE_INFO *);
+static int
+mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
+ Alter_info *alter_info,
+ bool tmp_table,
+ uint *db_options,
+ handler *file, KEY **key_info_buffer,
+ uint *key_count, int select_field_count);
+static bool
+mysql_prepare_alter_table(THD *thd, TABLE *table,
+ HA_CREATE_INFO *create_info,
+ Alter_info *alter_info);
+
+#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= THD::NOT_KILLED;
+}
+#endif
/*
- Build the path to a file for a table (or the base path that can
- then have various extensions stuck on to it).
+ Translate a file name to a table name (WL #1324).
SYNOPSIS
- build_table_path()
- buff Buffer to build the path into
- bufflen sizeof(buff)
- db Name of database
- table Name of table
- ext Filename extension
+ filename_to_tablename()
+ from The file name in my_charset_filename.
+ to OUT The table name in system_charset_info.
+ to_length The size of the table name buffer.
RETURN
- 0 Error
- # Size of path
- */
+ Table name length.
+*/
+
+uint filename_to_tablename(const char *from, char *to, uint to_length)
+{
+ 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))
+ {
+ /* Temporary table name. */
+ res= (strnmov(to, from, to_length) - to);
+ }
+ else
+ {
+ res= strconvert(&my_charset_filename, from,
+ system_charset_info, to, to_length, &errors);
+ if (errors) // Old 5.0 name
+ {
+ res= (strxnmov(to, to_length, MYSQL50_TABLE_NAME_PREFIX, from, NullS) -
+ to);
+ sql_print_error("Invalid (old?) table or database name '%s'", from);
+ /*
+ TODO: add a stored procedure for fix table and database names,
+ and mention its name in error log.
+ */
+ }
+ }
+
+ DBUG_PRINT("exit", ("to '%s'", to));
+ DBUG_RETURN(res);
+}
+
+
+/**
+ Check if given string begins with "#mysql50#" prefix, cut it if so.
+
+ @param from string to check and cut
+ @param to[out] buffer for result string
+ @param to_length its size
+
+ @retval
+ 0 no prefix found
+ @retval
+ non-0 result string length
+*/
+
+uint check_n_cut_mysql50_prefix(const char *from, char *to, uint to_length)
+{
+ if (from[0] == '#' &&
+ !strncmp(from, MYSQL50_TABLE_NAME_PREFIX,
+ MYSQL50_TABLE_NAME_PREFIX_LENGTH))
+ return (uint) (strmake(to, from + MYSQL50_TABLE_NAME_PREFIX_LENGTH,
+ to_length - 1) - to);
+ return 0;
+}
+
+
+/*
+ Translate a table name to a file name (WL #1324).
+
+ SYNOPSIS
+ tablename_to_filename()
+ from The table name in system_charset_info.
+ to OUT The file name in my_charset_filename.
+ to_length The size of the file name buffer.
+
+ RETURN
+ File name length.
+*/
+
+uint tablename_to_filename(const char *from, char *to, uint to_length)
+{
+ uint errors, length;
+ DBUG_ENTER("tablename_to_filename");
+ DBUG_PRINT("enter", ("from '%s'", from));
+
+ if ((length= check_n_cut_mysql50_prefix(from, to, to_length)))
+ DBUG_RETURN(length);
+ length= strconvert(system_charset_info, from,
+ &my_charset_filename, to, to_length, &errors);
+ if (check_if_legal_tablename(to) &&
+ length + 4 < to_length)
+ {
+ memcpy(to + length, "@@@", 4);
+ length+= 3;
+ }
+ DBUG_PRINT("exit", ("to '%s'", to));
+ DBUG_RETURN(length);
+}
+
+
+/*
+ Creates path to a file: mysql_data_dir/db/table.ext
+
+ SYNOPSIS
+ build_table_filename()
+ buff Where to write result in my_charset_filename.
+ This may be the same as table_name.
+ bufflen buff size
+ db Database name in system_charset_info.
+ table_name Table name in system_charset_info.
+ ext File extension.
+ flags FN_FROM_IS_TMP or FN_TO_IS_TMP or FN_IS_TMP
+ table_name is temporary, do not change.
+
+ NOTES
+
+ Uses database and table name, and extension to create
+ a file name in mysql_data_dir. Database and table
+ names are converted from system_charset_info into "fscs".
+ Unless flags indicate a temporary table name.
+ 'db' is always converted.
+ 'ext' is not converted.
+
+ The conversion suppression is required for ALTER TABLE. This
+ statement creates intermediate tables. These are regular
+ (non-temporary) tables with a temporary name. Their path names must
+ be derivable from the table name. So we cannot use
+ build_tmptable_filename() for them.
+
+ RETURN
+ path length
+*/
+
+uint build_table_filename(char *buff, size_t bufflen, const char *db,
+ const char *table_name, const char *ext, uint flags)
+{
+ char dbbuff[FN_REFLEN];
+ char tbbuff[FN_REFLEN];
+ DBUG_ENTER("build_table_filename");
+ DBUG_PRINT("enter", ("db: '%s' table_name: '%s' ext: '%s' flags: %x",
+ db, table_name, ext, flags));
+
+ 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(db, dbbuff, sizeof(dbbuff)));
+
+ char *end = buff + bufflen;
+ /* Don't add FN_ROOTDIR if mysql_data_home already includes it */
+ char *pos = strnmov(buff, mysql_data_home, bufflen);
+ size_t rootdir_len= strlen(FN_ROOTDIR);
+ if (pos - rootdir_len >= buff &&
+ memcmp(pos - rootdir_len, FN_ROOTDIR, rootdir_len) != 0)
+ pos= strnmov(pos, FN_ROOTDIR, end - pos);
+ pos= strxnmov(pos, end - pos, dbbuff, FN_ROOTDIR, NullS);
+#ifdef USE_SYMDIR
+ unpack_dirname(buff, buff);
+ pos= strend(buff);
+#endif
+ pos= strxnmov(pos, end - pos, tbbuff, ext, NullS);
+
+ DBUG_PRINT("exit", ("buff: '%s'", buff));
+ DBUG_RETURN(pos - buff);
+}
-uint build_table_path(char *buff, size_t bufflen, const char *db,
- const char *table, const char *ext)
+
+/*
+ Creates path to a file: mysql_tmpdir/#sql1234_12_1.ext
+
+ SYNOPSIS
+ build_tmptable_filename()
+ thd The thread handle.
+ buff Where to write result in my_charset_filename.
+ bufflen buff size
+
+ NOTES
+
+ Uses current_pid, thread_id, and tmp_table counter to create
+ a file name in mysql_tmpdir.
+
+ RETURN
+ path length
+*/
+
+uint build_tmptable_filename(THD* thd, char *buff, size_t bufflen)
{
- strxnmov(buff, (uint) (bufflen - 1), mysql_data_home, "/", db, "/", table,
- ext, NullS);
- return unpack_filename(buff,buff);
+ DBUG_ENTER("build_tmptable_filename");
+
+ char *p= strnmov(buff, mysql_tmpdir, bufflen);
+ my_snprintf(p, bufflen - (p - buff), "/%s%lx_%lx_%x%s",
+ tmp_file_prefix, current_pid,
+ thd->thread_id, thd->tmp_table++, reg_ext);
+
+ if (lower_case_table_names)
+ {
+ /* Convert all except tmpdir to lower case */
+ my_casedn_str(files_charset_info, p);
+ }
+
+ size_t length= unpack_filename(buff, buff);
+ DBUG_PRINT("exit", ("buff: '%s'", buff));
+ DBUG_RETURN(length);
}
+/*
+--------------------------------------------------------------------------
+
+ MODULE: DDL log
+ -----------------
+
+ This module is used to ensure that we can recover from crashes that occur
+ in the middle of a meta-data operation in MySQL. E.g. DROP TABLE t1, t2;
+ We need to ensure that both t1 and t2 are dropped and not only t1 and
+ also that each table drop is entirely done and not "half-baked".
+
+ To support this we create log entries for each meta-data statement in the
+ ddl log while we are executing. These entries are dropped when the
+ operation is completed.
+
+ At recovery those entries that were not completed will be executed.
+
+ There is only one ddl log in the system and it is protected by a mutex
+ and there is a global struct that contains information about its current
+ state.
+
+ History:
+ First version written in 2006 by Mikael Ronstrom
+--------------------------------------------------------------------------
+*/
+
+
+struct st_global_ddl_log
+{
+ /*
+ We need to adjust buffer size to be able to handle downgrades/upgrades
+ where IO_SIZE has changed. We'll set the buffer size such that we can
+ handle that the buffer size was upto 4 times bigger in the version
+ that wrote the DDL log.
+ */
+ char file_entry_buf[4*IO_SIZE];
+ char file_name_str[FN_REFLEN];
+ char *file_name;
+ DDL_LOG_MEMORY_ENTRY *first_free;
+ DDL_LOG_MEMORY_ENTRY *first_used;
+ uint num_entries;
+ File file_id;
+ uint name_len;
+ uint io_size;
+ bool inited;
+ bool do_release;
+ bool recovery_phase;
+ st_global_ddl_log() : inited(false), do_release(false) {}
+};
+
+st_global_ddl_log global_ddl_log;
+
+pthread_mutex_t LOCK_gdl;
+
+#define DDL_LOG_ENTRY_TYPE_POS 0
+#define DDL_LOG_ACTION_TYPE_POS 1
+#define DDL_LOG_PHASE_POS 2
+#define DDL_LOG_NEXT_ENTRY_POS 4
+#define DDL_LOG_NAME_POS 8
+
+#define DDL_LOG_NUM_ENTRY_POS 0
+#define DDL_LOG_NAME_LEN_POS 4
+#define DDL_LOG_IO_SIZE_POS 8
+
+/*
+ Read one entry from ddl log file
+ SYNOPSIS
+ read_ddl_log_file_entry()
+ entry_no Entry number to read
+ RETURN VALUES
+ TRUE Error
+ FALSE Success
+*/
+
+static bool read_ddl_log_file_entry(uint entry_no)
+{
+ bool error= FALSE;
+ File file_id= global_ddl_log.file_id;
+ uchar *file_entry_buf= (uchar*)global_ddl_log.file_entry_buf;
+ uint io_size= global_ddl_log.io_size;
+ DBUG_ENTER("read_ddl_log_file_entry");
+
+ if (my_pread(file_id, file_entry_buf, io_size, io_size * entry_no,
+ MYF(MY_WME)) != io_size)
+ error= TRUE;
+ DBUG_RETURN(error);
+}
+
+
+/*
+ Write one entry from ddl log file
+ SYNOPSIS
+ write_ddl_log_file_entry()
+ entry_no Entry number to read
+ RETURN VALUES
+ TRUE Error
+ FALSE Success
+*/
+
+static bool write_ddl_log_file_entry(uint entry_no)
+{
+ bool error= FALSE;
+ File file_id= global_ddl_log.file_id;
+ char *file_entry_buf= (char*)global_ddl_log.file_entry_buf;
+ DBUG_ENTER("write_ddl_log_file_entry");
+
+ if (my_pwrite(file_id, (uchar*)file_entry_buf,
+ IO_SIZE, IO_SIZE * entry_no, MYF(MY_WME)) != IO_SIZE)
+ error= TRUE;
+ DBUG_RETURN(error);
+}
+
+
+/*
+ Write ddl log header
+ SYNOPSIS
+ write_ddl_log_header()
+ RETURN VALUES
+ TRUE Error
+ FALSE Success
+*/
+
+static bool write_ddl_log_header()
+{
+ uint16 const_var;
+ bool error= FALSE;
+ DBUG_ENTER("write_ddl_log_header");
+
+ int4store(&global_ddl_log.file_entry_buf[DDL_LOG_NUM_ENTRY_POS],
+ global_ddl_log.num_entries);
+ const_var= FN_LEN;
+ int4store(&global_ddl_log.file_entry_buf[DDL_LOG_NAME_LEN_POS],
+ (ulong) const_var);
+ const_var= IO_SIZE;
+ int4store(&global_ddl_log.file_entry_buf[DDL_LOG_IO_SIZE_POS],
+ (ulong) const_var);
+ if (write_ddl_log_file_entry(0UL))
+ {
+ sql_print_error("Error writing ddl log header");
+ DBUG_RETURN(TRUE);
+ }
+ VOID(sync_ddl_log());
+ DBUG_RETURN(error);
+}
+
+
+/*
+ Create ddl log file name
+ SYNOPSIS
+ create_ddl_log_file_name()
+ file_name Filename setup
+ RETURN VALUES
+ NONE
+*/
+
+static inline void create_ddl_log_file_name(char *file_name)
+{
+ strxmov(file_name, mysql_data_home, "/", "ddl_log.log", NullS);
+}
+
+
+/*
+ Read header of ddl log file
+ SYNOPSIS
+ read_ddl_log_header()
+ RETURN VALUES
+ > 0 Last entry in ddl log
+ 0 No entries in ddl log
+ DESCRIPTION
+ When we read the ddl log header we get information about maximum sizes
+ of names in the ddl log and we also get information about the number
+ of entries in the ddl log.
+*/
+
+static uint read_ddl_log_header()
+{
+ char *file_entry_buf= (char*)global_ddl_log.file_entry_buf;
+ char file_name[FN_REFLEN];
+ uint entry_no;
+ bool successful_open= FALSE;
+ DBUG_ENTER("read_ddl_log_header");
+
+ 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 (read_ddl_log_file_entry(0UL))
+ {
+ /* Write message into error log */
+ sql_print_error("Failed to read ddl log file in recovery");
+ }
+ else
+ successful_open= TRUE;
+ }
+ entry_no= uint4korr(&file_entry_buf[DDL_LOG_NUM_ENTRY_POS]);
+ global_ddl_log.name_len= uint4korr(&file_entry_buf[DDL_LOG_NAME_LEN_POS]);
+ if (successful_open)
+ {
+ global_ddl_log.io_size= uint4korr(&file_entry_buf[DDL_LOG_IO_SIZE_POS]);
+ DBUG_ASSERT(global_ddl_log.io_size <=
+ sizeof(global_ddl_log.file_entry_buf));
+ }
+ else
+ {
+ entry_no= 0;
+ }
+ 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));
+ global_ddl_log.do_release= true;
+ DBUG_RETURN(entry_no);
+}
+
+
+/*
+ Read a ddl log entry
+ SYNOPSIS
+ read_ddl_log_entry()
+ read_entry Number of entry to read
+ out:entry_info Information from entry
+ RETURN VALUES
+ TRUE Error
+ FALSE Success
+ DESCRIPTION
+ Read a specified entry in the ddl log
+*/
+
+bool read_ddl_log_entry(uint read_entry, DDL_LOG_ENTRY *ddl_log_entry)
+{
+ char *file_entry_buf= (char*)&global_ddl_log.file_entry_buf;
+ uint inx;
+ uchar single_char;
+ DBUG_ENTER("read_ddl_log_entry");
+
+ if (read_ddl_log_file_entry(read_entry))
+ {
+ DBUG_RETURN(TRUE);
+ }
+ ddl_log_entry->entry_pos= read_entry;
+ single_char= file_entry_buf[DDL_LOG_ENTRY_TYPE_POS];
+ ddl_log_entry->entry_type= (enum ddl_log_entry_code)single_char;
+ single_char= file_entry_buf[DDL_LOG_ACTION_TYPE_POS];
+ ddl_log_entry->action_type= (enum ddl_log_action_code)single_char;
+ ddl_log_entry->phase= file_entry_buf[DDL_LOG_PHASE_POS];
+ ddl_log_entry->next_entry= uint4korr(&file_entry_buf[DDL_LOG_NEXT_ENTRY_POS]);
+ ddl_log_entry->name= &file_entry_buf[DDL_LOG_NAME_POS];
+ inx= DDL_LOG_NAME_POS + global_ddl_log.name_len;
+ ddl_log_entry->from_name= &file_entry_buf[inx];
+ inx+= global_ddl_log.name_len;
+ ddl_log_entry->handler_name= &file_entry_buf[inx];
+ DBUG_RETURN(FALSE);
+}
+
+
+/*
+ Initialise ddl log
+ SYNOPSIS
+ init_ddl_log()
+
+ DESCRIPTION
+ Write the header of the ddl log file and length of names. Also set
+ number of entries to zero.
+
+ RETURN VALUES
+ TRUE Error
+ FALSE Success
+*/
+
+static bool init_ddl_log()
+{
+ char file_name[FN_REFLEN];
+ DBUG_ENTER("init_ddl_log");
+
+ if (global_ddl_log.inited)
+ goto end;
+
+ global_ddl_log.io_size= IO_SIZE;
+ 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)
+ {
+ /* Couldn't create ddl log file, this is serious error */
+ sql_print_error("Failed to open ddl log file");
+ DBUG_RETURN(TRUE);
+ }
+ global_ddl_log.inited= TRUE;
+ if (write_ddl_log_header())
+ {
+ VOID(my_close(global_ddl_log.file_id, MYF(MY_WME)));
+ global_ddl_log.inited= FALSE;
+ DBUG_RETURN(TRUE);
+ }
+
+end:
+ DBUG_RETURN(FALSE);
+}
+
+
+/*
+ Execute one action in a ddl log entry
+ SYNOPSIS
+ execute_ddl_log_action()
+ ddl_log_entry Information in action entry to execute
+ RETURN VALUES
+ TRUE Error
+ FALSE Success
+*/
+
+static int execute_ddl_log_action(THD *thd, DDL_LOG_ENTRY *ddl_log_entry)
+{
+ bool frm_action= FALSE;
+ LEX_STRING handler_name;
+ handler *file= NULL;
+ MEM_ROOT mem_root;
+ int error= TRUE;
+ char to_path[FN_REFLEN];
+ char from_path[FN_REFLEN];
+#ifdef WITH_PARTITION_STORAGE_ENGINE
+ char *par_ext= (char*)".par";
+#endif
+ handlerton *hton;
+ DBUG_ENTER("execute_ddl_log_action");
+
+ if (ddl_log_entry->entry_type == DDL_IGNORE_LOG_ENTRY_CODE)
+ {
+ DBUG_RETURN(FALSE);
+ }
+ handler_name.str= (char*)ddl_log_entry->handler_name;
+ handler_name.length= strlen(ddl_log_entry->handler_name);
+ init_sql_alloc(&mem_root, TABLE_ALLOC_BLOCK_SIZE, 0);
+ if (!strcmp(ddl_log_entry->handler_name, reg_ext))
+ frm_action= TRUE;
+ else
+ {
+ plugin_ref plugin= ha_resolve_by_name(thd, &handler_name);
+ if (!plugin)
+ {
+ my_error(ER_ILLEGAL_HA, MYF(0), ddl_log_entry->handler_name);
+ goto error;
+ }
+ hton= plugin_data(plugin, handlerton*);
+ file= get_new_handler((TABLE_SHARE*)0, &mem_root, hton);
+ if (!file)
+ {
+ mem_alloc_error(sizeof(handler));
+ goto error;
+ }
+ }
+ switch (ddl_log_entry->action_type)
+ {
+ case DDL_LOG_REPLACE_ACTION:
+ case DDL_LOG_DELETE_ACTION:
+ {
+ if (ddl_log_entry->phase == 0)
+ {
+ if (frm_action)
+ {
+ strxmov(to_path, ddl_log_entry->name, reg_ext, NullS);
+ if ((error= my_delete(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)));
+#endif
+ }
+ else
+ {
+ if ((error= file->ha_delete_table(ddl_log_entry->name)))
+ {
+ if (error != ENOENT && error != HA_ERR_NO_SUCH_TABLE)
+ break;
+ }
+ }
+ if ((deactivate_ddl_log_entry(ddl_log_entry->entry_pos)))
+ break;
+ VOID(sync_ddl_log());
+ error= FALSE;
+ if (ddl_log_entry->action_type == DDL_LOG_DELETE_ACTION)
+ break;
+ }
+ DBUG_ASSERT(ddl_log_entry->action_type == DDL_LOG_REPLACE_ACTION);
+ /*
+ Fall through and perform the rename action of the replace
+ action. We have already indicated the success of the delete
+ action in the log entry by stepping up the phase.
+ */
+ }
+ case DDL_LOG_RENAME_ACTION:
+ {
+ error= TRUE;
+ if (frm_action)
+ {
+ 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)))
+ 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)));
+#endif
+ }
+ else
+ {
+ if (file->ha_rename_table(ddl_log_entry->from_name,
+ ddl_log_entry->name))
+ break;
+ }
+ if ((deactivate_ddl_log_entry(ddl_log_entry->entry_pos)))
+ break;
+ VOID(sync_ddl_log());
+ error= FALSE;
+ break;
+ }
+ default:
+ DBUG_ASSERT(0);
+ break;
+ }
+ delete file;
+error:
+ free_root(&mem_root, MYF(0));
+ DBUG_RETURN(error);
+}
+
+
+/*
+ Get a free entry in the ddl log
+ SYNOPSIS
+ get_free_ddl_log_entry()
+ out:active_entry A ddl log memory entry returned
+ RETURN VALUES
+ TRUE Error
+ FALSE Success
+*/
+
+static bool get_free_ddl_log_entry(DDL_LOG_MEMORY_ENTRY **active_entry,
+ bool *write_header)
+{
+ DDL_LOG_MEMORY_ENTRY *used_entry;
+ DDL_LOG_MEMORY_ENTRY *first_used= global_ddl_log.first_used;
+ DBUG_ENTER("get_free_ddl_log_entry");
+
+ if (global_ddl_log.first_free == NULL)
+ {
+ if (!(used_entry= (DDL_LOG_MEMORY_ENTRY*)my_malloc(
+ sizeof(DDL_LOG_MEMORY_ENTRY), MYF(MY_WME))))
+ {
+ sql_print_error("Failed to allocate memory for ddl log free list");
+ DBUG_RETURN(TRUE);
+ }
+ global_ddl_log.num_entries++;
+ used_entry->entry_pos= global_ddl_log.num_entries;
+ *write_header= TRUE;
+ }
+ else
+ {
+ used_entry= global_ddl_log.first_free;
+ global_ddl_log.first_free= used_entry->next_log_entry;
+ *write_header= FALSE;
+ }
+ /*
+ Move from free list to used list
+ */
+ used_entry->next_log_entry= first_used;
+ used_entry->prev_log_entry= NULL;
+ global_ddl_log.first_used= used_entry;
+ if (first_used)
+ first_used->prev_log_entry= used_entry;
+
+ *active_entry= used_entry;
+ DBUG_RETURN(FALSE);
+}
+
+
+/*
+ External interface methods for the DDL log Module
+ ---------------------------------------------------
+*/
+
+/*
+ SYNOPSIS
+ write_ddl_log_entry()
+ ddl_log_entry Information about log entry
+ out:entry_written Entry information written into
+
+ RETURN VALUES
+ TRUE Error
+ FALSE Success
+
+ DESCRIPTION
+ A careful write of the ddl log is performed to ensure that we can
+ handle crashes occurring during CREATE and ALTER TABLE processing.
+*/
+
+bool write_ddl_log_entry(DDL_LOG_ENTRY *ddl_log_entry,
+ DDL_LOG_MEMORY_ENTRY **active_entry)
+{
+ bool error, write_header;
+ DBUG_ENTER("write_ddl_log_entry");
+
+ if (init_ddl_log())
+ {
+ DBUG_RETURN(TRUE);
+ }
+ global_ddl_log.file_entry_buf[DDL_LOG_ENTRY_TYPE_POS]=
+ (char)DDL_LOG_ENTRY_CODE;
+ global_ddl_log.file_entry_buf[DDL_LOG_ACTION_TYPE_POS]=
+ (char)ddl_log_entry->action_type;
+ global_ddl_log.file_entry_buf[DDL_LOG_PHASE_POS]= 0;
+ int4store(&global_ddl_log.file_entry_buf[DDL_LOG_NEXT_ENTRY_POS],
+ ddl_log_entry->next_entry);
+ DBUG_ASSERT(strlen(ddl_log_entry->name) < FN_LEN);
+ strmake(&global_ddl_log.file_entry_buf[DDL_LOG_NAME_POS],
+ ddl_log_entry->name, FN_LEN - 1);
+ if (ddl_log_entry->action_type == DDL_LOG_RENAME_ACTION ||
+ ddl_log_entry->action_type == DDL_LOG_REPLACE_ACTION)
+ {
+ DBUG_ASSERT(strlen(ddl_log_entry->from_name) < FN_LEN);
+ strmake(&global_ddl_log.file_entry_buf[DDL_LOG_NAME_POS + FN_LEN],
+ ddl_log_entry->from_name, FN_LEN - 1);
+ }
+ else
+ global_ddl_log.file_entry_buf[DDL_LOG_NAME_POS + FN_LEN]= 0;
+ DBUG_ASSERT(strlen(ddl_log_entry->handler_name) < FN_LEN);
+ strmake(&global_ddl_log.file_entry_buf[DDL_LOG_NAME_POS + (2*FN_LEN)],
+ ddl_log_entry->handler_name, FN_LEN - 1);
+ if (get_free_ddl_log_entry(active_entry, &write_header))
+ {
+ DBUG_RETURN(TRUE);
+ }
+ error= FALSE;
+ if (write_ddl_log_file_entry((*active_entry)->entry_pos))
+ {
+ error= TRUE;
+ sql_print_error("Failed to write entry_no = %u",
+ (*active_entry)->entry_pos);
+ }
+ if (write_header && !error)
+ {
+ VOID(sync_ddl_log());
+ if (write_ddl_log_header())
+ error= TRUE;
+ }
+ if (error)
+ release_ddl_log_memory_entry(*active_entry);
+ DBUG_RETURN(error);
+}
+
+
+/*
+ Write final entry in the ddl log
+ SYNOPSIS
+ write_execute_ddl_log_entry()
+ first_entry First entry in linked list of entries
+ to execute, if 0 = NULL it means that
+ the entry is removed and the entries
+ are put into the free list.
+ complete Flag indicating we are simply writing
+ info about that entry has been completed
+ in:out:active_entry Entry to execute, 0 = NULL if the entry
+ is written first time and needs to be
+ returned. In this case the entry written
+ is returned in this parameter
+ RETURN VALUES
+ TRUE Error
+ FALSE Success
+
+ DESCRIPTION
+ This is the last write in the ddl log. The previous log entries have
+ already been written but not yet synched to disk.
+ We write a couple of log entries that describes action to perform.
+ This entries are set-up in a linked list, however only when a first
+ execute entry is put as the first entry these will be executed.
+ This routine writes this first
+*/
+
+bool write_execute_ddl_log_entry(uint first_entry,
+ bool complete,
+ DDL_LOG_MEMORY_ENTRY **active_entry)
+{
+ bool write_header= FALSE;
+ char *file_entry_buf= (char*)global_ddl_log.file_entry_buf;
+ DBUG_ENTER("write_execute_ddl_log_entry");
+
+ if (init_ddl_log())
+ {
+ DBUG_RETURN(TRUE);
+ }
+ if (!complete)
+ {
+ /*
+ We haven't synched the log entries yet, we synch them now before
+ writing the execute entry. If complete is true we haven't written
+ any log entries before, we are only here to write the execute
+ entry to indicate it is done.
+ */
+ VOID(sync_ddl_log());
+ file_entry_buf[DDL_LOG_ENTRY_TYPE_POS]= (char)DDL_LOG_EXECUTE_CODE;
+ }
+ else
+ file_entry_buf[DDL_LOG_ENTRY_TYPE_POS]= (char)DDL_IGNORE_LOG_ENTRY_CODE;
+ file_entry_buf[DDL_LOG_ACTION_TYPE_POS]= 0; /* Ignored for execute entries */
+ file_entry_buf[DDL_LOG_PHASE_POS]= 0;
+ int4store(&file_entry_buf[DDL_LOG_NEXT_ENTRY_POS], first_entry);
+ file_entry_buf[DDL_LOG_NAME_POS]= 0;
+ file_entry_buf[DDL_LOG_NAME_POS + FN_LEN]= 0;
+ file_entry_buf[DDL_LOG_NAME_POS + 2*FN_LEN]= 0;
+ if (!(*active_entry))
+ {
+ if (get_free_ddl_log_entry(active_entry, &write_header))
+ {
+ DBUG_RETURN(TRUE);
+ }
+ }
+ if (write_ddl_log_file_entry((*active_entry)->entry_pos))
+ {
+ sql_print_error("Error writing execute entry in ddl log");
+ release_ddl_log_memory_entry(*active_entry);
+ DBUG_RETURN(TRUE);
+ }
+ VOID(sync_ddl_log());
+ if (write_header)
+ {
+ if (write_ddl_log_header())
+ {
+ release_ddl_log_memory_entry(*active_entry);
+ DBUG_RETURN(TRUE);
+ }
+ }
+ DBUG_RETURN(FALSE);
+}
+
+
+/*
+ For complex rename operations we need to deactivate individual entries.
+ SYNOPSIS
+ deactivate_ddl_log_entry()
+ entry_no Entry position of record to change
+ RETURN VALUES
+ TRUE Error
+ FALSE Success
+ DESCRIPTION
+ During replace operations where we start with an existing table called
+ t1 and a replacement table called t1#temp or something else and where
+ we want to delete t1 and rename t1#temp to t1 this is not possible to
+ do in a safe manner unless the ddl log is informed of the phases in
+ the change.
+
+ Delete actions are 1-phase actions that can be ignored immediately after
+ being executed.
+ Rename actions from x to y is also a 1-phase action since there is no
+ interaction with any other handlers named x and y.
+ Replace action where drop y and x -> y happens needs to be a two-phase
+ action. Thus the first phase will drop y and the second phase will
+ rename x -> y.
+*/
+
+bool deactivate_ddl_log_entry(uint entry_no)
+{
+ char *file_entry_buf= (char*)global_ddl_log.file_entry_buf;
+ DBUG_ENTER("deactivate_ddl_log_entry");
+
+ if (!read_ddl_log_file_entry(entry_no))
+ {
+ if (file_entry_buf[DDL_LOG_ENTRY_TYPE_POS] == DDL_LOG_ENTRY_CODE)
+ {
+ if (file_entry_buf[DDL_LOG_ACTION_TYPE_POS] == DDL_LOG_DELETE_ACTION ||
+ file_entry_buf[DDL_LOG_ACTION_TYPE_POS] == DDL_LOG_RENAME_ACTION ||
+ (file_entry_buf[DDL_LOG_ACTION_TYPE_POS] == DDL_LOG_REPLACE_ACTION &&
+ file_entry_buf[DDL_LOG_PHASE_POS] == 1))
+ file_entry_buf[DDL_LOG_ENTRY_TYPE_POS]= DDL_IGNORE_LOG_ENTRY_CODE;
+ else if (file_entry_buf[DDL_LOG_ACTION_TYPE_POS] == DDL_LOG_REPLACE_ACTION)
+ {
+ DBUG_ASSERT(file_entry_buf[DDL_LOG_PHASE_POS] == 0);
+ file_entry_buf[DDL_LOG_PHASE_POS]= 1;
+ }
+ else
+ {
+ DBUG_ASSERT(0);
+ }
+ if (write_ddl_log_file_entry(entry_no))
+ {
+ sql_print_error("Error in deactivating log entry. Position = %u",
+ entry_no);
+ DBUG_RETURN(TRUE);
+ }
+ }
+ }
+ else
+ {
+ sql_print_error("Failed in reading entry before deactivating it");
+ DBUG_RETURN(TRUE);
+ }
+ DBUG_RETURN(FALSE);
+}
+
+
+/*
+ Sync ddl log file
+ SYNOPSIS
+ sync_ddl_log()
+ RETURN VALUES
+ TRUE Error
+ FALSE Success
+*/
+
+bool sync_ddl_log()
+{
+ bool error= FALSE;
+ DBUG_ENTER("sync_ddl_log");
+
+ if ((!global_ddl_log.recovery_phase) &&
+ init_ddl_log())
+ {
+ DBUG_RETURN(TRUE);
+ }
+ if (my_sync(global_ddl_log.file_id, MYF(0)))
+ {
+ /* Write to error log */
+ sql_print_error("Failed to sync ddl log");
+ error= TRUE;
+ }
+ DBUG_RETURN(error);
+}
+
+
+/*
+ Release a log memory entry
+ SYNOPSIS
+ release_ddl_log_memory_entry()
+ log_memory_entry Log memory entry to release
+ RETURN VALUES
+ NONE
+*/
+
+void release_ddl_log_memory_entry(DDL_LOG_MEMORY_ENTRY *log_entry)
+{
+ DDL_LOG_MEMORY_ENTRY *first_free= global_ddl_log.first_free;
+ 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");
+
+ global_ddl_log.first_free= log_entry;
+ log_entry->next_log_entry= first_free;
+
+ if (prev_log_entry)
+ prev_log_entry->next_log_entry= next_log_entry;
+ else
+ global_ddl_log.first_used= next_log_entry;
+ if (next_log_entry)
+ next_log_entry->prev_log_entry= prev_log_entry;
+ DBUG_VOID_RETURN;
+}
+
+
+/*
+ Execute one entry in the ddl log. Executing an entry means executing
+ a linked list of actions.
+ SYNOPSIS
+ execute_ddl_log_entry()
+ first_entry Reference to first action in entry
+ RETURN VALUES
+ TRUE Error
+ FALSE Success
+*/
+
+bool execute_ddl_log_entry(THD *thd, uint first_entry)
+{
+ DDL_LOG_ENTRY ddl_log_entry;
+ uint read_entry= first_entry;
+ DBUG_ENTER("execute_ddl_log_entry");
+
+ pthread_mutex_lock(&LOCK_gdl);
+ do
+ {
+ if (read_ddl_log_entry(read_entry, &ddl_log_entry))
+ {
+ /* Write to error log and continue with next log entry */
+ sql_print_error("Failed to read entry = %u from ddl log",
+ read_entry);
+ break;
+ }
+ DBUG_ASSERT(ddl_log_entry.entry_type == DDL_LOG_ENTRY_CODE ||
+ ddl_log_entry.entry_type == DDL_IGNORE_LOG_ENTRY_CODE);
+
+ if (execute_ddl_log_action(thd, &ddl_log_entry))
+ {
+ /* Write to error log and continue with next log entry */
+ sql_print_error("Failed to execute action for entry = %u from ddl log",
+ read_entry);
+ break;
+ }
+ read_entry= ddl_log_entry.next_entry;
+ } while (read_entry);
+ pthread_mutex_unlock(&LOCK_gdl);
+ DBUG_RETURN(FALSE);
+}
+
+
+/*
+ Close the ddl log
+ SYNOPSIS
+ close_ddl_log()
+ RETURN VALUES
+ NONE
+*/
+
+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)));
+ global_ddl_log.file_id= (File) -1;
+ }
+ DBUG_VOID_RETURN;
+}
+
+
+/*
+ Execute the ddl log at recovery of MySQL Server
+ SYNOPSIS
+ execute_ddl_log_recovery()
+ RETURN VALUES
+ NONE
+*/
+
+void execute_ddl_log_recovery()
+{
+ uint num_entries, i;
+ THD *thd;
+ DDL_LOG_ENTRY ddl_log_entry;
+ char file_name[FN_REFLEN];
+ DBUG_ENTER("execute_ddl_log_recovery");
+
+ /*
+ Initialise global_ddl_log struct
+ */
+ bzero(global_ddl_log.file_entry_buf, sizeof(global_ddl_log.file_entry_buf));
+ global_ddl_log.inited= FALSE;
+ global_ddl_log.recovery_phase= TRUE;
+ global_ddl_log.io_size= IO_SIZE;
+ global_ddl_log.file_id= (File) -1;
+
+ /*
+ To be able to run this from boot, we allocate a temporary THD
+ */
+ if (!(thd=new THD))
+ DBUG_VOID_RETURN;
+ thd->thread_stack= (char*) &thd;
+ thd->store_globals();
+
+ num_entries= read_ddl_log_header();
+ for (i= 1; i < num_entries + 1; i++)
+ {
+ if (read_ddl_log_entry(i, &ddl_log_entry))
+ {
+ sql_print_error("Failed to read entry no = %u from ddl log",
+ i);
+ continue;
+ }
+ if (ddl_log_entry.entry_type == DDL_LOG_EXECUTE_CODE)
+ {
+ if (execute_ddl_log_entry(thd, ddl_log_entry.next_entry))
+ {
+ /* Real unpleasant scenario but we continue anyways. */
+ continue;
+ }
+ }
+ }
+ close_ddl_log();
+ create_ddl_log_file_name(file_name);
+ VOID(my_delete(file_name, MYF(0)));
+ global_ddl_log.recovery_phase= FALSE;
+ delete thd;
+ /* Remember that we don't have a THD */
+ my_pthread_setspecific_ptr(THR_THD, 0);
+ DBUG_VOID_RETURN;
+}
+
+
+/*
+ Release all memory allocated to the ddl log
+ SYNOPSIS
+ release_ddl_log()
+ RETURN VALUES
+ NONE
+*/
+
+void release_ddl_log()
+{
+ DDL_LOG_MEMORY_ENTRY *free_list= global_ddl_log.first_free;
+ DDL_LOG_MEMORY_ENTRY *used_list= global_ddl_log.first_used;
+ DBUG_ENTER("release_ddl_log");
+
+ if (!global_ddl_log.do_release)
+ DBUG_VOID_RETURN;
+
+ pthread_mutex_lock(&LOCK_gdl);
+ while (used_list)
+ {
+ DDL_LOG_MEMORY_ENTRY *tmp= used_list->next_log_entry;
+ my_free(used_list, MYF(0));
+ used_list= tmp;
+ }
+ while (free_list)
+ {
+ DDL_LOG_MEMORY_ENTRY *tmp= free_list->next_log_entry;
+ my_free(free_list, MYF(0));
+ free_list= tmp;
+ }
+ close_ddl_log();
+ global_ddl_log.inited= 0;
+ pthread_mutex_unlock(&LOCK_gdl);
+ VOID(pthread_mutex_destroy(&LOCK_gdl));
+ global_ddl_log.do_release= false;
+ DBUG_VOID_RETURN;
+}
+
+
+/*
+---------------------------------------------------------------------------
+
+ END MODULE DDL log
+ --------------------
+
+---------------------------------------------------------------------------
+*/
+
+
+/**
+ @brief construct a temporary shadow file name.
+
+ @details Make a shadow file name used by ALTER TABLE to construct the
+ modified table (with keeping the original). The modified table is then
+ moved back as original table. The name must start with the temp file
+ prefix so it gets filtered out by table files listing routines.
+
+ @param[out] buff buffer to receive the constructed name
+ @param bufflen size of buff
+ @param lpt alter table data structure
+
+ @retval path length
+*/
+
+uint build_table_shadow_filename(char *buff, size_t bufflen,
+ ALTER_PARTITION_PARAM_TYPE *lpt)
+{
+ char tmp_name[FN_REFLEN];
+ my_snprintf (tmp_name, sizeof (tmp_name), "%s-%s", tmp_file_prefix,
+ lpt->table_name);
+ return build_table_filename(buff, bufflen, lpt->db, tmp_name, "", FN_IS_TMP);
+}
+
+
+/*
+ SYNOPSIS
+ mysql_write_frm()
+ lpt Struct carrying many parameters needed for this
+ method
+ flags Flags as defined below
+ WFRM_INITIAL_WRITE If set we need to prepare table before
+ creating the frm file
+ WFRM_INSTALL_SHADOW If set we should install the new frm
+ WFRM_KEEP_SHARE If set we know that the share is to be
+ retained and thus we should ensure share
+ object is correct, if not set we don't
+ set the new partition syntax string since
+ we know the share object is destroyed.
+ WFRM_PACK_FRM If set we should pack the frm file and delete
+ the frm file
+
+ RETURN VALUES
+ TRUE Error
+ FALSE Success
+
+ DESCRIPTION
+ A support method that creates a new frm file and in this process it
+ regenerates the partition data. It works fine also for non-partitioned
+ tables since it only handles partitioned data if it exists.
+*/
+
+bool mysql_write_frm(ALTER_PARTITION_PARAM_TYPE *lpt, uint flags)
+{
+ /*
+ Prepare table to prepare for writing a new frm file where the
+ partitions in add/drop state have temporarily changed their state
+ We set tmp_table to avoid get errors on naming of primary key index.
+ */
+ int error= 0;
+ char path[FN_REFLEN+1];
+ char shadow_path[FN_REFLEN+1];
+ char shadow_frm_name[FN_REFLEN+1];
+ char frm_name[FN_REFLEN+1];
+#ifdef WITH_PARTITION_STORAGE_ENGINE
+ char *part_syntax_buf;
+ uint syntax_len;
+#endif
+ DBUG_ENTER("mysql_write_frm");
+
+ /*
+ Build shadow frm file name
+ */
+ build_table_shadow_filename(shadow_path, sizeof(shadow_path), lpt);
+ strxmov(shadow_frm_name, shadow_path, reg_ext, NullS);
+ if (flags & WFRM_WRITE_SHADOW)
+ {
+ if (mysql_prepare_create_table(lpt->thd, lpt->create_info,
+ lpt->alter_info,
+ /*tmp_table*/ 1,
+ &lpt->db_options,
+ lpt->table->file,
+ &lpt->key_info_buffer,
+ &lpt->key_count,
+ /*select_field_count*/ 0))
+ {
+ DBUG_RETURN(TRUE);
+ }
+#ifdef WITH_PARTITION_STORAGE_ENGINE
+ {
+ partition_info *part_info= lpt->table->part_info;
+ if (part_info)
+ {
+ if (!(part_syntax_buf= generate_partition_syntax(part_info,
+ &syntax_len,
+ TRUE, TRUE)))
+ {
+ DBUG_RETURN(TRUE);
+ }
+ part_info->part_info_string= part_syntax_buf;
+ part_info->part_info_len= syntax_len;
+ }
+ }
+#endif
+ /* Write shadow frm file */
+ lpt->create_info->table_options= lpt->db_options;
+ if ((mysql_create_frm(lpt->thd, shadow_frm_name, lpt->db,
+ lpt->table_name, lpt->create_info,
+ lpt->alter_info->create_list, lpt->key_count,
+ lpt->key_info_buffer, lpt->table->file)) ||
+ lpt->table->file->ha_create_handler_files(shadow_path, NULL,
+ CHF_CREATE_FLAG,
+ lpt->create_info))
+ {
+ my_delete(shadow_frm_name, MYF(0));
+ error= 1;
+ goto end;
+ }
+ }
+ if (flags & WFRM_PACK_FRM)
+ {
+ /*
+ We need to pack the frm file and after packing it we delete the
+ frm file to ensure it doesn't get used. This is only used for
+ handlers that have the main version of the frm file stored in the
+ handler.
+ */
+ uchar *data;
+ size_t length;
+ if (readfrm(shadow_path, &data, &length) ||
+ packfrm(data, length, &lpt->pack_frm_data, &lpt->pack_frm_len))
+ {
+ my_free(data, MYF(MY_ALLOW_ZERO_PTR));
+ my_free(lpt->pack_frm_data, MYF(MY_ALLOW_ZERO_PTR));
+ mem_alloc_error(length);
+ error= 1;
+ goto end;
+ }
+ error= my_delete(shadow_frm_name, MYF(MY_WME));
+ }
+ if (flags & WFRM_INSTALL_SHADOW)
+ {
+#ifdef WITH_PARTITION_STORAGE_ENGINE
+ partition_info *part_info= lpt->part_info;
+#endif
+ /*
+ Build frm file name
+ */
+ build_table_filename(path, sizeof(path), lpt->db,
+ lpt->table_name, "", 0);
+ strxmov(frm_name, path, reg_ext, NullS);
+ /*
+ When we are changing to use new frm file we need to ensure that we
+ don't collide with another thread in process to open the frm file.
+ We start by deleting the .frm file and possible .par file. Then we
+ write to the DDL log that we have completed the delete phase by
+ increasing the phase of the log entry. Next step is to rename the
+ new .frm file and the new .par file to the real name. After
+ 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)) ||
+#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)) ||
+ 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)))
+#endif
+ {
+ error= 1;
+ goto err;
+ }
+#ifdef WITH_PARTITION_STORAGE_ENGINE
+ if (part_info && (flags & WFRM_KEEP_SHARE))
+ {
+ TABLE_SHARE *share= lpt->table->s;
+ char *tmp_part_syntax_str;
+ if (!(part_syntax_buf= generate_partition_syntax(part_info,
+ &syntax_len,
+ TRUE, TRUE)))
+ {
+ error= 1;
+ goto err;
+ }
+ if (share->partition_info_buffer_size < syntax_len + 1)
+ {
+ share->partition_info_buffer_size= syntax_len+1;
+ if (!(tmp_part_syntax_str= (char*) strmake_root(&share->mem_root,
+ part_syntax_buf,
+ syntax_len)))
+ {
+ error= 1;
+ goto err;
+ }
+ share->partition_info= 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;
+ 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());
+#endif
+ }
+
+end:
+ DBUG_RETURN(error);
+}
+
+
+/*
+ SYNOPSIS
+ write_bin_log()
+ thd Thread object
+ clear_error is clear_error to be called
+ query Query to log
+ query_length Length of query
+
+ RETURN VALUES
+ NONE
+
+ DESCRIPTION
+ Write the binlog if open, routine used in multiple places in this
+ file
+*/
+
+void write_bin_log(THD *thd, bool clear_error,
+ char const *query, ulong query_length)
+{
+ if (mysql_bin_log.is_open())
+ {
+ if (clear_error)
+ thd->clear_error();
+ thd->binlog_query(THD::STMT_QUERY_TYPE,
+ query, query_length, FALSE, FALSE);
+ }
+}
/*
@@ -120,71 +1515,17 @@ bool mysql_rm_table(THD *thd,TABLE_LIST *tables, my_bool if_exists,
LOCK_open during wait_if_global_read_lock(), other threads could not
close their tables. This would make a pretty deadlock.
*/
- thd->mysys_var->current_mutex= &LOCK_open;
- thd->mysys_var->current_cond= &COND_refresh;
- VOID(pthread_mutex_lock(&LOCK_open));
-
error= mysql_rm_table_part2(thd, tables, if_exists, drop_temporary, 0, 0);
- pthread_mutex_unlock(&LOCK_open);
-
- pthread_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);
-
if (need_start_waiters)
start_waiting_global_read_lock(thd);
if (error)
DBUG_RETURN(TRUE);
- send_ok(thd);
+ my_ok(thd);
DBUG_RETURN(FALSE);
}
-
-/*
- delete (drop) tables.
-
- SYNOPSIS
- mysql_rm_table_part2_with_lock()
- thd Thread handle
- tables List of tables to delete
- if_exists If 1, don't give error if one table doesn't exists
- dont_log_query Don't write query to log files. This will also not
- generate warnings if the handler files doesn't exists
-
- NOTES
- Works like documented in mysql_rm_table(), but don't check
- global_read_lock and don't send_ok packet to server.
-
- RETURN
- 0 ok
- 1 error
-*/
-
-int mysql_rm_table_part2_with_lock(THD *thd,
- TABLE_LIST *tables, bool if_exists,
- bool drop_temporary, bool dont_log_query)
-{
- int error;
- thd->mysys_var->current_mutex= &LOCK_open;
- thd->mysys_var->current_cond= &COND_refresh;
- VOID(pthread_mutex_lock(&LOCK_open));
-
- error= mysql_rm_table_part2(thd, tables, if_exists, drop_temporary, 1,
- dont_log_query);
-
- pthread_mutex_unlock(&LOCK_open);
-
- pthread_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);
- return error;
-}
-
-
/*
Execute the drop of a normal or temporary table
@@ -220,16 +1561,60 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists,
bool dont_log_query)
{
TABLE_LIST *table;
- char path[FN_REFLEN], *alias;
+ char path[FN_REFLEN], *alias;
+ uint path_length;
String wrong_tables;
- int error;
+ int error= 0;
+ int non_temp_tables_count= 0;
bool some_tables_deleted=0, tmp_table_deleted=0, foreign_key_error=0;
+ String built_query;
DBUG_ENTER("mysql_rm_table_part2");
LINT_INIT(alias);
+ LINT_INIT(path_length);
- if (!drop_temporary && lock_table_names(thd, tables))
+ if (thd->current_stmt_binlog_row_based && !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 ");
+ }
+
+ mysql_ha_rm_tables(thd, tables, FALSE);
+
+ pthread_mutex_lock(&LOCK_open);
+
+ /*
+ If we have the table in the definition cache, we don't have to check the
+ .frm file to find if the table is a normal table (not view) and what
+ engine to use.
+ */
+
+ for (table= tables; table; table= table->next_local)
+ {
+ TABLE_SHARE *share;
+ table->db_type= NULL;
+ if ((share= get_cached_table_share(table->db, table->table_name)))
+ table->db_type= share->db_type();
+
+ /* Disable drop of enabled log tables */
+ if (share && (share->table_category == TABLE_CATEGORY_PERFORMANCE) &&
+ check_if_log_table(table->db_length, table->db,
+ table->table_name_length, table->table_name, 1))
+ {
+ my_error(ER_BAD_LOG_STATEMENT, MYF(0), "DROP");
+ pthread_mutex_unlock(&LOCK_open);
+ DBUG_RETURN(1);
+ }
+ }
+
+ if (!drop_temporary && lock_table_names_exclusively(thd, tables))
+ {
+ pthread_mutex_unlock(&LOCK_open);
DBUG_RETURN(1);
+ }
/* Don't give warnings for not found errors, as we already generate notes */
thd->no_warnings_for_error= 1;
@@ -237,37 +1622,85 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists,
for (table= tables; table; table= table->next_local)
{
char *db=table->db;
- db_type table_type= DB_TYPE_UNKNOWN;
+ handlerton *table_type;
+ enum legacy_db_type frm_db_type;
+
+ DBUG_PRINT("table", ("table_l: '%s'.'%s' table: 0x%lx s: 0x%lx",
+ 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;
+ continue;
+ case -1:
+ DBUG_ASSERT(thd->in_sub_stmt);
+ error= 1;
+ goto err_with_placeholders;
+ default:
+ // temporary table not found
+ error= 0;
+ }
- mysql_ha_flush(thd, table, MYSQL_HA_CLOSE_FINAL, TRUE);
- if (!close_temporary_table(thd, db, table->table_name))
+ /*
+ 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 (thd->current_stmt_binlog_row_based && !dont_log_query)
{
- tmp_table_deleted=1;
- continue; // removed temporary table
+ non_temp_tables_count++;
+ /*
+ Don't write the database name if it is the current one (or if
+ thd->db is NULL).
+ */
+ built_query.append("`");
+ if (thd->db == NULL || strcmp(db,thd->db) != 0)
+ {
+ built_query.append(db);
+ built_query.append("`.`");
+ }
+
+ built_query.append(table->table_name);
+ built_query.append("`,");
}
- error=0;
+ table_type= table->db_type;
if (!drop_temporary)
{
+ TABLE *locked_table;
abort_locked_tables(thd, db, table->table_name);
remove_table_from_cache(thd, db, table->table_name,
RTFC_WAIT_OTHER_THREAD_FLAG |
RTFC_CHECK_KILLED_FLAG);
- drop_locked_tables(thd, db, table->table_name);
+ /*
+ If the table was used in lock tables, remember it so that
+ unlock_table_names can free it
+ */
+ if ((locked_table= drop_locked_tables(thd, db, table->table_name)))
+ table->table= locked_table;
+
if (thd->killed)
{
- error=-1;
+ error= -1;
goto err_with_placeholders;
}
alias= (lower_case_table_names == 2) ? table->alias : table->table_name;
- /* remove form file and isam files */
- build_table_path(path, sizeof(path), db, alias, reg_ext);
+ /* remove .frm file and engine files */
+ path_length= build_table_filename(path, sizeof(path), db, alias, reg_ext,
+ table->internal_tmp_table ?
+ FN_IS_TMP : 0);
}
if (drop_temporary ||
- (access(path,F_OK) &&
- ha_create_table_from_engine(thd,db,alias)) ||
- (!drop_view &&
- mysql_frm_type(thd, path, &table_type) != FRMTYPE_TABLE))
+ (table_type == NULL &&
+ (access(path, F_OK) &&
+ ha_create_table_from_engine(thd, db, alias)) ||
+ (!drop_view &&
+ mysql_frm_type(thd, path, &frm_db_type) != FRMTYPE_TABLE)))
{
// Table was not found on disk and table can't be created from engine
if (if_exists)
@@ -280,14 +1713,21 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists,
else
{
char *end;
- if (table_type == DB_TYPE_UNKNOWN)
- mysql_frm_type(thd, path, &table_type);
- *(end=fn_ext(path))=0; // Remove extension for delete
- error= ha_delete_table(thd, table_type, path, table->table_name,
+ if (table_type == NULL)
+ {
+ mysql_frm_type(thd, path, &frm_db_type);
+ table_type= ha_resolve_by_legacy_type(thd, frm_db_type);
+ }
+ // Remove extension for delete
+ *(end= path + path_length - reg_ext_length)= '\0';
+ error= ha_delete_table(thd, table_type, path, db, table->table_name,
!dont_log_query);
if ((error == ENOENT || error == HA_ERR_NO_SUCH_TABLE) &&
- (if_exists || table_type == DB_TYPE_UNKNOWN))
+ (if_exists || table_type == NULL))
+ {
error= 0;
+ thd->clear_error();
+ }
if (error == HA_ERR_ROW_IS_REFERENCED)
{
/* the table is referenced by a foreign key constraint */
@@ -313,8 +1753,15 @@ 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));
}
+ DBUG_PRINT("table", ("table: 0x%lx s: 0x%lx", (long) table->table,
+ table->table ? (long) table->table->s : (long) -1));
}
- thd->tmp_table_used= tmp_table_deleted;
+ /*
+ It's safe to unlock LOCK_open: we have an exclusive lock
+ on the table name.
+ */
+ pthread_mutex_unlock(&LOCK_open);
+ thd->thread_specific_used|= tmp_table_deleted;
error= 0;
if (wrong_tables.length())
{
@@ -329,33 +1776,89 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists,
if (some_tables_deleted || tmp_table_deleted || !error)
{
query_cache_invalidate3(thd, tables, 0);
- if (!dont_log_query && mysql_bin_log.is_open())
+ if (!dont_log_query)
{
- if (!error)
- thd->clear_error();
- Query_log_event qinfo(thd, thd->query, thd->query_length, FALSE, FALSE);
- mysql_bin_log.write(&qinfo);
+ if (!thd->current_stmt_binlog_row_based ||
+ non_temp_tables_count > 0 && !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.
+ */
+ write_bin_log(thd, !error, thd->query, thd->query_length);
+ }
+ else if (thd->current_stmt_binlog_row_based &&
+ non_temp_tables_count > 0 &&
+ tmp_table_deleted)
+ {
+ /*
+ 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 has not been created on the
+ slave, 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
+ built_query.append(" /* generated by server */");
+ write_bin_log(thd, !error, built_query.ptr(), built_query.length());
+ }
+ /*
+ The remaining cases are:
+ - no tables where deleted and
+ - only temporary tables where deleted and row-based
+ replication is used.
+ In both these cases, nothing should be written to the binary
+ log.
+ */
}
}
-
+ pthread_mutex_lock(&LOCK_open);
err_with_placeholders:
- if (!drop_temporary)
- unlock_table_names(thd, tables, (TABLE_LIST*) 0);
+ unlock_table_names(thd, tables, (TABLE_LIST*) 0);
+ pthread_mutex_unlock(&LOCK_open);
thd->no_warnings_for_error= 0;
DBUG_RETURN(error);
}
-int quick_rm_table(enum db_type base,const char *db,
- const char *table_name)
+/*
+ Quickly remove a table.
+
+ SYNOPSIS
+ quick_rm_table()
+ base The handlerton handle.
+ db The database name.
+ table_name The table name.
+ flags flags for build_table_filename().
+
+ RETURN
+ 0 OK
+ != 0 Error
+*/
+
+bool quick_rm_table(handlerton *base,const char *db,
+ const char *table_name, uint flags)
{
char path[FN_REFLEN];
- int error=0;
- build_table_path(path, sizeof(path), db, table_name, reg_ext);
+ bool error= 0;
+ DBUG_ENTER("quick_rm_table");
+
+ uint path_length= build_table_filename(path, sizeof(path),
+ db, table_name, reg_ext, flags);
if (my_delete(path,MYF(0)))
- error=1; /* purecov: inspected */
- *fn_ext(path)= 0; // Remove reg_ext
- return ha_delete_table(current_thd, base, path, table_name, 0) || error;
+ error= 1; /* purecov: inspected */
+ path[path_length - reg_ext_length]= '\0'; // Remove reg_ext
+ if (!(flags & FRM_ONLY))
+ error|= ha_delete_table(current_thd, base, path, db, table_name, 0);
+ DBUG_RETURN(error);
}
/*
@@ -488,7 +1991,7 @@ void calculate_interval_lengths(CHARSET_INFO *cs, TYPELIB *interval,
for (pos= interval->type_names, len= interval->type_lengths;
*pos ; pos++, len++)
{
- uint length= cs->cset->numchars(cs, *pos, *pos + *len);
+ size_t length= cs->cset->numchars(cs, *pos, *pos + *len);
*tot_length+= length;
set_if_bigger(*max_length, (uint32)length);
}
@@ -506,7 +2009,7 @@ void calculate_interval_lengths(CHARSET_INFO *cs, TYPELIB *interval,
table_flags table flags
DESCRIPTION
- This function prepares a create_field instance.
+ This function prepares a Create_field instance.
Fields such as pack_flag are valid after this call.
RETURN VALUES
@@ -514,25 +2017,25 @@ void calculate_interval_lengths(CHARSET_INFO *cs, TYPELIB *interval,
1 Error
*/
-int prepare_create_field(create_field *sql_field,
+int prepare_create_field(Create_field *sql_field,
uint *blob_columns,
int *timestamps, int *timestamps_with_niladic,
- uint table_flags)
+ longlong table_flags)
{
unsigned int dup_val_count;
DBUG_ENTER("prepare_field");
/*
- This code came from mysql_prepare_table.
+ This code came from mysql_prepare_create_table.
Indent preserved to make patching easier
*/
DBUG_ASSERT(sql_field->charset);
switch (sql_field->sql_type) {
- case FIELD_TYPE_BLOB:
- case FIELD_TYPE_MEDIUM_BLOB:
- case FIELD_TYPE_TINY_BLOB:
- case FIELD_TYPE_LONG_BLOB:
+ case MYSQL_TYPE_BLOB:
+ case MYSQL_TYPE_MEDIUM_BLOB:
+ case MYSQL_TYPE_TINY_BLOB:
+ case MYSQL_TYPE_LONG_BLOB:
sql_field->pack_flag=FIELDFLAG_BLOB |
pack_length_to_packflag(sql_field->pack_length -
portable_sizeof_char_ptr);
@@ -542,7 +2045,7 @@ int prepare_create_field(create_field *sql_field,
sql_field->unireg_check=Field::BLOB_FIELD;
(*blob_columns)++;
break;
- case FIELD_TYPE_GEOMETRY:
+ case MYSQL_TYPE_GEOMETRY:
#ifdef HAVE_SPATIAL
if (!(table_flags & HA_CAN_GEOMETRY))
{
@@ -582,12 +2085,12 @@ int prepare_create_field(create_field *sql_field,
}
#endif
/* fall through */
- case FIELD_TYPE_STRING:
+ case MYSQL_TYPE_STRING:
sql_field->pack_flag=0;
if (sql_field->charset->state & MY_CS_BINSORT)
sql_field->pack_flag|=FIELDFLAG_BINARY;
break;
- case FIELD_TYPE_ENUM:
+ case MYSQL_TYPE_ENUM:
sql_field->pack_flag=pack_length_to_packflag(sql_field->pack_length) |
FIELDFLAG_INTERVAL;
if (sql_field->charset->state & MY_CS_BINSORT)
@@ -598,7 +2101,7 @@ int prepare_create_field(create_field *sql_field,
sql_field->charset, &dup_val_count))
DBUG_RETURN(1);
break;
- case FIELD_TYPE_SET:
+ case MYSQL_TYPE_SET:
sql_field->pack_flag=pack_length_to_packflag(sql_field->pack_length) |
FIELDFLAG_BITFIELD;
if (sql_field->charset->state & MY_CS_BINSORT)
@@ -615,19 +2118,20 @@ int prepare_create_field(create_field *sql_field,
DBUG_RETURN(1);
}
break;
- case FIELD_TYPE_DATE: // Rest of string types
- case FIELD_TYPE_NEWDATE:
- case FIELD_TYPE_TIME:
- case FIELD_TYPE_DATETIME:
- case FIELD_TYPE_NULL:
+ case MYSQL_TYPE_DATE: // Rest of string types
+ case MYSQL_TYPE_NEWDATE:
+ case MYSQL_TYPE_TIME:
+ case MYSQL_TYPE_DATETIME:
+ case MYSQL_TYPE_NULL:
sql_field->pack_flag=f_settype((uint) sql_field->sql_type);
break;
- case FIELD_TYPE_BIT:
+ case MYSQL_TYPE_BIT:
/*
- We have sql_field->pack_flag already set here, see mysql_prepare_table().
+ We have sql_field->pack_flag already set here, see
+ mysql_prepare_create_table().
*/
break;
- case FIELD_TYPE_NEWDECIMAL:
+ case MYSQL_TYPE_NEWDECIMAL:
sql_field->pack_flag=(FIELDFLAG_NUMBER |
(sql_field->flags & UNSIGNED_FLAG ? 0 :
FIELDFLAG_DECIMAL) |
@@ -635,7 +2139,7 @@ int prepare_create_field(create_field *sql_field,
FIELDFLAG_ZEROFILL : 0) |
(sql_field->decimals << FIELDFLAG_DEC_SHIFT));
break;
- case FIELD_TYPE_TIMESTAMP:
+ case MYSQL_TYPE_TIMESTAMP:
/* We should replace old TIMESTAMP fields with their newer analogs */
if (sql_field->unireg_check == Field::TIMESTAMP_OLD_FIELD)
{
@@ -673,10 +2177,16 @@ int prepare_create_field(create_field *sql_field,
Preparation for table creation
SYNOPSIS
- mysql_prepare_table()
- thd Thread object
- create_info Create information (like MAX_ROWS)
- alter_info List of columns and indexes to create
+ mysql_prepare_create_table()
+ thd Thread object.
+ create_info Create information (like MAX_ROWS).
+ alter_info List of columns and indexes to create
+ tmp_table If a temporary table is to be created.
+ db_options INOUT Table options (like HA_OPTION_PACK_RECORD).
+ file The handler for the new table.
+ key_info_buffer OUT An array of KEY structs for the indexes.
+ key_count OUT The number of elements in the array.
+ select_field_count The number of fields coming from a select table.
DESCRIPTION
Prepares the table and key structures for table creation.
@@ -685,19 +2195,20 @@ int prepare_create_field(create_field *sql_field,
sets create_info->varchar if the table has a varchar
RETURN VALUES
- 0 ok
- -1 error
+ FALSE OK
+ TRUE error
*/
-static int mysql_prepare_table(THD *thd, HA_CREATE_INFO *create_info,
- Alter_info *alter_info,
- bool tmp_table,
- uint *db_options,
- handler *file, KEY **key_info_buffer,
- uint *key_count, int select_field_count)
+static int
+mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
+ Alter_info *alter_info,
+ bool tmp_table,
+ uint *db_options,
+ handler *file, KEY **key_info_buffer,
+ uint *key_count, int select_field_count)
{
const char *key_name;
- create_field *sql_field,*dup_field;
+ Create_field *sql_field,*dup_field;
uint field,null_fields,blob_columns,max_key_length;
ulong record_offset= 0;
KEY *key_info;
@@ -705,10 +2216,10 @@ static int mysql_prepare_table(THD *thd, HA_CREATE_INFO *create_info,
int timestamps= 0, timestamps_with_niladic= 0;
int field_no,dup_no;
int select_field_pos,auto_increment=0;
- List_iterator<create_field> it(alter_info->create_list);
- List_iterator<create_field> it2(alter_info->create_list);
+ List_iterator<Create_field> it(alter_info->create_list);
+ List_iterator<Create_field> it2(alter_info->create_list);
uint total_uneven_bit_length= 0;
- DBUG_ENTER("mysql_prepare_table");
+ DBUG_ENTER("mysql_prepare_create_table");
select_field_pos= alter_info->create_list.elements - select_field_count;
null_fields=blob_columns=0;
@@ -745,7 +2256,7 @@ static int mysql_prepare_table(THD *thd, HA_CREATE_INFO *create_info,
strmake(strmake(tmp, save_cs->csname, sizeof(tmp)-4),
STRING_WITH_LEN("_bin"));
my_error(ER_UNKNOWN_COLLATION, MYF(0), tmp);
- DBUG_RETURN(-1);
+ DBUG_RETURN(TRUE);
}
/*
@@ -754,36 +2265,33 @@ static int mysql_prepare_table(THD *thd, HA_CREATE_INFO *create_info,
*/
if (sql_field->def &&
save_cs != sql_field->def->collation.collation &&
- (sql_field->sql_type == FIELD_TYPE_VAR_STRING ||
- sql_field->sql_type == FIELD_TYPE_STRING ||
- sql_field->sql_type == FIELD_TYPE_SET ||
- sql_field->sql_type == FIELD_TYPE_ENUM))
- {
- Query_arena backup_arena;
- bool need_to_change_arena= !thd->stmt_arena->is_conventional();
- if (need_to_change_arena)
- {
- /* Asser that we don't do that at every PS execute */
- DBUG_ASSERT(thd->stmt_arena->is_first_stmt_execute() ||
- thd->stmt_arena->is_first_sp_execute());
- thd->set_n_backup_active_arena(thd->stmt_arena, &backup_arena);
- }
-
+ (sql_field->sql_type == MYSQL_TYPE_VAR_STRING ||
+ sql_field->sql_type == MYSQL_TYPE_STRING ||
+ sql_field->sql_type == MYSQL_TYPE_SET ||
+ sql_field->sql_type == MYSQL_TYPE_ENUM))
+ {
+ /*
+ Starting from 5.1 we work here with a copy of Create_field
+ created by the caller, not with the instance that was
+ originally created during parsing. It's OK to create
+ a temporary item and initialize with it a member of the
+ copy -- this item will be thrown away along with the copy
+ at the end of execution, and thus not introduce a dangling
+ pointer in the parsed tree of a prepared statement or a
+ stored procedure statement.
+ */
sql_field->def= sql_field->def->safe_charset_converter(save_cs);
- if (need_to_change_arena)
- thd->restore_active_arena(thd->stmt_arena, &backup_arena);
-
if (sql_field->def == NULL)
{
/* Could not convert */
my_error(ER_INVALID_DEFAULT, MYF(0), sql_field->field_name);
- DBUG_RETURN(-1);
+ DBUG_RETURN(TRUE);
}
}
- if (sql_field->sql_type == FIELD_TYPE_SET ||
- sql_field->sql_type == FIELD_TYPE_ENUM)
+ if (sql_field->sql_type == MYSQL_TYPE_SET ||
+ sql_field->sql_type == MYSQL_TYPE_ENUM)
{
uint32 dummy;
CHARSET_INFO *cs= sql_field->charset;
@@ -797,12 +2305,11 @@ static int mysql_prepare_table(THD *thd, HA_CREATE_INFO *create_info,
if (!interval)
{
/*
- Create the typelib in prepared statement memory if we're
- executing one.
+ Create the typelib in runtime memory - we will free the
+ occupied memory at the same time when we free this
+ sql_field -- at the end of execution.
*/
- MEM_ROOT *stmt_root= thd->stmt_arena->mem_root;
-
- interval= sql_field->interval= typelib(stmt_root,
+ interval= sql_field->interval= typelib(thd->mem_root,
sql_field->interval_list);
List_iterator<String> int_it(sql_field->interval_list);
String conv, *tmp;
@@ -813,13 +2320,13 @@ static int mysql_prepare_table(THD *thd, HA_CREATE_INFO *create_info,
DBUG_ASSERT(comma_length > 0);
for (uint i= 0; (tmp= int_it++); i++)
{
- uint lengthsp;
+ size_t lengthsp;
if (String::needs_conversion(tmp->length(), tmp->charset(),
cs, &dummy))
{
uint cnv_errs;
conv.copy(tmp->ptr(), tmp->length(), tmp->charset(), cs, &cnv_errs);
- interval->type_names[i]= strmake_root(stmt_root, conv.ptr(),
+ interval->type_names[i]= strmake_root(thd->mem_root, conv.ptr(),
conv.length());
interval->type_lengths[i]= conv.length();
}
@@ -829,21 +2336,21 @@ static int mysql_prepare_table(THD *thd, HA_CREATE_INFO *create_info,
interval->type_lengths[i]);
interval->type_lengths[i]= lengthsp;
((uchar *)interval->type_names[i])[lengthsp]= '\0';
- if (sql_field->sql_type == FIELD_TYPE_SET)
+ if (sql_field->sql_type == MYSQL_TYPE_SET)
{
if (cs->coll->instr(cs, interval->type_names[i],
interval->type_lengths[i],
comma_buf, comma_length, NULL, 0))
{
my_error(ER_ILLEGAL_VALUE_FOR_TYPE, MYF(0), "set", tmp->ptr());
- DBUG_RETURN(-1);
+ DBUG_RETURN(TRUE);
}
}
}
sql_field->interval_list.empty(); // Don't need interval_list anymore
}
- if (sql_field->sql_type == FIELD_TYPE_SET)
+ if (sql_field->sql_type == MYSQL_TYPE_SET)
{
uint32 field_length;
if (sql_field->def != NULL)
@@ -857,7 +2364,7 @@ static int mysql_prepare_table(THD *thd, HA_CREATE_INFO *create_info,
if ((sql_field->flags & NOT_NULL_FLAG) != 0)
{
my_error(ER_INVALID_DEFAULT, MYF(0), sql_field->field_name);
- DBUG_RETURN(-1);
+ DBUG_RETURN(TRUE);
}
/* else, NULL is an allowed value */
@@ -873,16 +2380,16 @@ static int mysql_prepare_table(THD *thd, HA_CREATE_INFO *create_info,
if (not_found)
{
my_error(ER_INVALID_DEFAULT, MYF(0), sql_field->field_name);
- DBUG_RETURN(-1);
+ DBUG_RETURN(TRUE);
}
}
calculate_interval_lengths(cs, interval, &dummy, &field_length);
sql_field->length= field_length + (interval->count - 1);
}
- else /* FIELD_TYPE_ENUM */
+ else /* MYSQL_TYPE_ENUM */
{
uint32 field_length;
- DBUG_ASSERT(sql_field->sql_type == FIELD_TYPE_ENUM);
+ DBUG_ASSERT(sql_field->sql_type == MYSQL_TYPE_ENUM);
if (sql_field->def != NULL)
{
String str, *def= sql_field->def->val_str(&str);
@@ -891,7 +2398,7 @@ static int mysql_prepare_table(THD *thd, HA_CREATE_INFO *create_info,
if ((sql_field->flags & NOT_NULL_FLAG) != 0)
{
my_error(ER_INVALID_DEFAULT, MYF(0), sql_field->field_name);
- DBUG_RETURN(-1);
+ DBUG_RETURN(TRUE);
}
/* else, the defaults yield the correct length for NULLs. */
@@ -902,7 +2409,7 @@ static int mysql_prepare_table(THD *thd, HA_CREATE_INFO *create_info,
if (find_type2(interval, def->ptr(), def->length(), cs) == 0) /* not found */
{
my_error(ER_INVALID_DEFAULT, MYF(0), sql_field->field_name);
- DBUG_RETURN(-1);
+ DBUG_RETURN(TRUE);
}
}
}
@@ -912,10 +2419,10 @@ static int mysql_prepare_table(THD *thd, HA_CREATE_INFO *create_info,
set_if_smaller(sql_field->length, MAX_FIELD_WIDTH-1);
}
- if (sql_field->sql_type == FIELD_TYPE_BIT)
+ if (sql_field->sql_type == MYSQL_TYPE_BIT)
{
sql_field->pack_flag= FIELDFLAG_NUMBER;
- if (file->table_flags() & HA_CAN_BIT_FIELD)
+ if (file->ha_table_flags() & HA_CAN_BIT_FIELD)
total_uneven_bit_length+= sql_field->length & 7;
else
sql_field->pack_flag|= FIELDFLAG_TREAT_BIT_AS_CHAR;
@@ -923,7 +2430,7 @@ static int mysql_prepare_table(THD *thd, HA_CREATE_INFO *create_info,
sql_field->create_length_to_internal_length();
if (prepare_blob_field(thd, sql_field))
- DBUG_RETURN(-1);
+ DBUG_RETURN(TRUE);
if (!(sql_field->flags & NOT_NULL_FLAG))
null_fields++;
@@ -931,7 +2438,7 @@ static int mysql_prepare_table(THD *thd, HA_CREATE_INFO *create_info,
if (check_column_name(sql_field->field_name))
{
my_error(ER_WRONG_COLUMN_NAME, MYF(0), sql_field->field_name);
- DBUG_RETURN(-1);
+ DBUG_RETURN(TRUE);
}
/* Check if we have used the same field name before */
@@ -948,7 +2455,7 @@ static int mysql_prepare_table(THD *thd, HA_CREATE_INFO *create_info,
if (field_no < select_field_pos || dup_no >= select_field_pos)
{
my_error(ER_DUP_FIELDNAME, MYF(0), sql_field->field_name);
- DBUG_RETURN(-1);
+ DBUG_RETURN(TRUE);
}
else
{
@@ -998,10 +2505,10 @@ static int mysql_prepare_table(THD *thd, HA_CREATE_INFO *create_info,
if (prepare_create_field(sql_field, &blob_columns,
&timestamps, &timestamps_with_niladic,
- file->table_flags()))
- DBUG_RETURN(-1);
+ file->ha_table_flags()))
+ DBUG_RETURN(TRUE);
if (sql_field->sql_type == MYSQL_TYPE_VARCHAR)
- create_info->varchar= 1;
+ create_info->varchar= TRUE;
sql_field->offset= record_offset;
if (MTYP_TYPENR(sql_field->unireg_check) == Field::NEXT_NUMBER)
auto_increment++;
@@ -1011,26 +2518,26 @@ static int mysql_prepare_table(THD *thd, HA_CREATE_INFO *create_info,
{
my_message(ER_TOO_MUCH_AUTO_TIMESTAMP_COLS,
ER(ER_TOO_MUCH_AUTO_TIMESTAMP_COLS), MYF(0));
- DBUG_RETURN(-1);
+ DBUG_RETURN(TRUE);
}
if (auto_increment > 1)
{
my_message(ER_WRONG_AUTO_KEY, ER(ER_WRONG_AUTO_KEY), MYF(0));
- DBUG_RETURN(-1);
+ DBUG_RETURN(TRUE);
}
if (auto_increment &&
- (file->table_flags() & HA_NO_AUTO_INCREMENT))
+ (file->ha_table_flags() & HA_NO_AUTO_INCREMENT))
{
my_message(ER_TABLE_CANT_HANDLE_AUTO_INCREMENT,
ER(ER_TABLE_CANT_HANDLE_AUTO_INCREMENT), MYF(0));
- DBUG_RETURN(-1);
+ DBUG_RETURN(TRUE);
}
- if (blob_columns && (file->table_flags() & HA_NO_BLOBS))
+ if (blob_columns && (file->ha_table_flags() & HA_NO_BLOBS))
{
my_message(ER_TABLE_CANT_HANDLE_BLOB, ER(ER_TABLE_CANT_HANDLE_BLOB),
MYF(0));
- DBUG_RETURN(-1);
+ DBUG_RETURN(TRUE);
}
/* Create keys */
@@ -1049,17 +2556,20 @@ static int mysql_prepare_table(THD *thd, HA_CREATE_INFO *create_info,
while ((key=key_iterator++))
{
+ DBUG_PRINT("info", ("key name: '%s' type: %d", key->name ? key->name :
+ "(none)" , key->type));
+ LEX_STRING key_name_str;
if (key->type == Key::FOREIGN_KEY)
{
fk_key_count++;
- foreign_key *fk_key= (foreign_key*) key;
+ Foreign_key *fk_key= (Foreign_key*) key;
if (fk_key->ref_columns.elements &&
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"),
ER(ER_KEY_REF_DO_NOT_MATCH_TABLE_REF));
- DBUG_RETURN(-1);
+ DBUG_RETURN(TRUE);
}
continue;
}
@@ -1068,12 +2578,15 @@ static int mysql_prepare_table(THD *thd, HA_CREATE_INFO *create_info,
if (key->columns.elements > tmp)
{
my_error(ER_TOO_MANY_KEY_PARTS,MYF(0),tmp);
- DBUG_RETURN(-1);
+ DBUG_RETURN(TRUE);
}
- if (key->name && strlen(key->name) > NAME_LEN)
+ 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,
+ system_charset_info, 1))
{
my_error(ER_TOO_LONG_IDENT, MYF(0), key->name);
- DBUG_RETURN(-1);
+ DBUG_RETURN(TRUE);
}
key_iterator2.rewind ();
if (key->type != Key::FOREIGN_KEY)
@@ -1109,31 +2622,31 @@ static int mysql_prepare_table(THD *thd, HA_CREATE_INFO *create_info,
key_parts+=key->columns.elements;
else
(*key_count)--;
- if (key->name && !tmp_table &&
+ if (key->name && !tmp_table && (key->type != Key::PRIMARY) &&
!my_strcasecmp(system_charset_info,key->name,primary_key_name))
{
my_error(ER_WRONG_NAME_FOR_INDEX, MYF(0), key->name);
- DBUG_RETURN(-1);
+ DBUG_RETURN(TRUE);
}
}
tmp=file->max_keys();
if (*key_count > tmp)
{
my_error(ER_TOO_MANY_KEYS,MYF(0),tmp);
- DBUG_RETURN(-1);
+ DBUG_RETURN(TRUE);
}
- (*key_info_buffer) = key_info= (KEY*) sql_calloc(sizeof(KEY)* *key_count);
+ (*key_info_buffer)= key_info= (KEY*) sql_calloc(sizeof(KEY) * (*key_count));
key_part_info=(KEY_PART_INFO*) sql_calloc(sizeof(KEY_PART_INFO)*key_parts);
if (!*key_info_buffer || ! key_part_info)
- DBUG_RETURN(-1); // Out of memory
+ DBUG_RETURN(TRUE); // Out of memory
key_iterator.rewind();
key_number=0;
for (; (key=key_iterator++) ; key_number++)
{
uint key_length=0;
- key_part_spec *column;
+ Key_part_spec *column;
if (key->name == ignore_key)
{
@@ -1145,12 +2658,16 @@ static int mysql_prepare_table(THD *thd, HA_CREATE_INFO *create_info,
break;
}
- switch(key->type){
+ switch (key->type) {
case Key::MULTIPLE:
key_info->flags= 0;
break;
case Key::FULLTEXT:
key_info->flags= HA_FULLTEXT;
+ if ((key_info->parser_name= &key->key_create_info.parser_name)->str)
+ key_info->flags|= HA_USES_PARSER;
+ else
+ key_info->parser_name= 0;
break;
case Key::SPATIAL:
#ifdef HAVE_SPATIAL
@@ -1159,7 +2676,7 @@ static int mysql_prepare_table(THD *thd, HA_CREATE_INFO *create_info,
#else
my_error(ER_FEATURE_DISABLED, MYF(0),
sym_group_geom.name, sym_group_geom.needed_define);
- DBUG_RETURN(-1);
+ DBUG_RETURN(TRUE);
#endif
case Key::FOREIGN_KEY:
key_number--; // Skip this key
@@ -1174,15 +2691,15 @@ static int mysql_prepare_table(THD *thd, HA_CREATE_INFO *create_info,
key_info->key_parts=(uint8) key->columns.elements;
key_info->key_part=key_part_info;
key_info->usable_key_parts= key_number;
- key_info->algorithm=key->algorithm;
+ key_info->algorithm= key->key_create_info.algorithm;
if (key->type == Key::FULLTEXT)
{
- if (!(file->table_flags() & HA_CAN_FULLTEXT))
+ if (!(file->ha_table_flags() & HA_CAN_FULLTEXT))
{
my_message(ER_TABLE_CANT_HANDLE_FT, ER(ER_TABLE_CANT_HANDLE_FT),
MYF(0));
- DBUG_RETURN(-1);
+ DBUG_RETURN(TRUE);
}
}
/*
@@ -1196,16 +2713,16 @@ static int mysql_prepare_table(THD *thd, HA_CREATE_INFO *create_info,
/* TODO: Add proper checks if handler supports key_type and algorithm */
if (key_info->flags & HA_SPATIAL)
{
- if (!(file->table_flags() & HA_CAN_RTREEKEYS))
+ if (!(file->ha_table_flags() & HA_CAN_RTREEKEYS))
{
my_message(ER_TABLE_CANT_HANDLE_SPKEYS, ER(ER_TABLE_CANT_HANDLE_SPKEYS),
MYF(0));
- DBUG_RETURN(-1);
+ DBUG_RETURN(TRUE);
}
if (key_info->key_parts != 1)
{
my_error(ER_WRONG_ARGUMENTS, MYF(0), "SPATIAL INDEX");
- DBUG_RETURN(-1);
+ DBUG_RETURN(TRUE);
}
}
else if (key_info->algorithm == HA_KEY_ALG_RTREE)
@@ -1214,24 +2731,36 @@ static int mysql_prepare_table(THD *thd, HA_CREATE_INFO *create_info,
if ((key_info->key_parts & 1) == 1)
{
my_error(ER_WRONG_ARGUMENTS, MYF(0), "RTREE INDEX");
- DBUG_RETURN(-1);
+ DBUG_RETURN(TRUE);
}
/* TODO: To be deleted */
my_error(ER_NOT_SUPPORTED_YET, MYF(0), "RTREE INDEX");
- DBUG_RETURN(-1);
+ DBUG_RETURN(TRUE);
#else
my_error(ER_FEATURE_DISABLED, MYF(0),
sym_group_rtree.name, sym_group_rtree.needed_define);
- DBUG_RETURN(-1);
+ DBUG_RETURN(TRUE);
#endif
}
- List_iterator<key_part_spec> cols(key->columns), cols2(key->columns);
+ /* Take block size from key part or table part */
+ /*
+ TODO: Add warning if block size changes. We can't do it here, as
+ this may depend on the size of the key
+ */
+ key_info->block_size= (key->key_create_info.block_size ?
+ key->key_create_info.block_size :
+ create_info->key_block_size);
+
+ if (key_info->block_size)
+ key_info->flags|= HA_USES_BLOCK_SIZE;
+
+ List_iterator<Key_part_spec> cols(key->columns), cols2(key->columns);
CHARSET_INFO *ft_key_charset=0; // for FULLTEXT
for (uint column_nr=0 ; (column=cols++) ; column_nr++)
{
uint length;
- key_part_spec *dup_column;
+ Key_part_spec *dup_column;
it.rewind();
field=0;
@@ -1243,7 +2772,7 @@ static int mysql_prepare_table(THD *thd, HA_CREATE_INFO *create_info,
if (!sql_field)
{
my_error(ER_KEY_COLUMN_DOES_NOT_EXITS, MYF(0), column->field_name);
- DBUG_RETURN(-1);
+ DBUG_RETURN(TRUE);
}
while ((dup_column= cols2++) != column)
{
@@ -1253,7 +2782,7 @@ static int mysql_prepare_table(THD *thd, HA_CREATE_INFO *create_info,
my_printf_error(ER_DUP_FIELDNAME,
ER(ER_DUP_FIELDNAME),MYF(0),
column->field_name);
- DBUG_RETURN(-1);
+ DBUG_RETURN(TRUE);
}
}
cols2.rewind();
@@ -1283,13 +2812,19 @@ static int mysql_prepare_table(THD *thd, HA_CREATE_INFO *create_info,
{
column->length*= sql_field->charset->mbmaxlen;
+ if (key->type == Key::SPATIAL && column->length)
+ {
+ my_error(ER_WRONG_SUB_KEY, MYF(0));
+ DBUG_RETURN(TRUE);
+ }
+
if (f_is_blob(sql_field->pack_flag) ||
(f_is_geom(sql_field->pack_flag) && key->type != Key::SPATIAL))
{
- if (!(file->table_flags() & HA_CAN_INDEX_BLOBS))
+ if (!(file->ha_table_flags() & HA_CAN_INDEX_BLOBS))
{
my_error(ER_BLOB_USED_AS_KEY, MYF(0), column->field_name);
- DBUG_RETURN(-1);
+ DBUG_RETURN(TRUE);
}
if (f_is_geom(sql_field->pack_flag) && sql_field->geom_type ==
Field::GEOM_POINT)
@@ -1297,7 +2832,7 @@ static int mysql_prepare_table(THD *thd, HA_CREATE_INFO *create_info,
if (!column->length)
{
my_error(ER_BLOB_KEY_WITHOUT_LENGTH, MYF(0), column->field_name);
- DBUG_RETURN(-1);
+ DBUG_RETURN(TRUE);
}
}
#ifdef HAVE_SPATIAL
@@ -1323,22 +2858,24 @@ static int mysql_prepare_table(THD *thd, HA_CREATE_INFO *create_info,
null_fields--;
}
else
- key_info->flags|= HA_NULL_PART_KEY;
- if (!(file->table_flags() & HA_NULL_IN_KEY))
- {
- my_error(ER_NULL_COLUMN_IN_INDEX, MYF(0), column->field_name);
- DBUG_RETURN(-1);
- }
- if (key->type == Key::SPATIAL)
- {
- my_message(ER_SPATIAL_CANT_HAVE_NULL,
- ER(ER_SPATIAL_CANT_HAVE_NULL), MYF(0));
- DBUG_RETURN(-1);
- }
+ {
+ 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);
+ DBUG_RETURN(TRUE);
+ }
+ if (key->type == Key::SPATIAL)
+ {
+ my_message(ER_SPATIAL_CANT_HAVE_NULL,
+ ER(ER_SPATIAL_CANT_HAVE_NULL), MYF(0));
+ DBUG_RETURN(TRUE);
+ }
+ }
}
if (MTYP_TYPENR(sql_field->unireg_check) == Field::NEXT_NUMBER)
{
- if (column_nr == 0 || (file->table_flags() & HA_AUTO_PART_KEY))
+ if (column_nr == 0 || (file->ha_table_flags() & HA_AUTO_PART_KEY))
auto_increment--; // Field is used
}
}
@@ -1370,7 +2907,7 @@ static int mysql_prepare_table(THD *thd, HA_CREATE_INFO *create_info,
else
{
my_error(ER_TOO_LONG_KEY,MYF(0),length);
- DBUG_RETURN(-1);
+ DBUG_RETURN(TRUE);
}
}
}
@@ -1378,20 +2915,20 @@ static int mysql_prepare_table(THD *thd, HA_CREATE_INFO *create_info,
(column->length > length ||
!Field::type_can_have_key_part (sql_field->sql_type) ||
((f_is_packed(sql_field->pack_flag) ||
- ((file->table_flags() & HA_NO_PREFIX_CHAR_KEYS) &&
+ ((file->ha_table_flags() & HA_NO_PREFIX_CHAR_KEYS) &&
(key_info->flags & HA_NOSAME))) &&
column->length != length)))
{
my_message(ER_WRONG_SUB_KEY, ER(ER_WRONG_SUB_KEY), MYF(0));
- DBUG_RETURN(-1);
+ DBUG_RETURN(TRUE);
}
- else if (!(file->table_flags() & HA_NO_PREFIX_CHAR_KEYS))
+ else if (!(file->ha_table_flags() & HA_NO_PREFIX_CHAR_KEYS))
length=column->length;
}
else if (length == 0)
{
my_error(ER_WRONG_KEY_COLUMN, MYF(0), column->field_name);
- DBUG_RETURN(-1);
+ DBUG_RETURN(TRUE);
}
if (length > file->max_key_part_length() && key->type != Key::FULLTEXT)
{
@@ -1410,7 +2947,7 @@ static int mysql_prepare_table(THD *thd, HA_CREATE_INFO *create_info,
else
{
my_error(ER_TOO_LONG_KEY,MYF(0),length);
- DBUG_RETURN(-1);
+ DBUG_RETURN(TRUE);
}
}
key_part_info->length=(uint16) length;
@@ -1443,7 +2980,7 @@ static int mysql_prepare_table(THD *thd, HA_CREATE_INFO *create_info,
{
my_message(ER_MULTIPLE_PRI_KEY, ER(ER_MULTIPLE_PRI_KEY),
MYF(0));
- DBUG_RETURN(-1);
+ DBUG_RETURN(TRUE);
}
key_name=primary_key_name;
primary_key=1;
@@ -1454,7 +2991,7 @@ static int mysql_prepare_table(THD *thd, HA_CREATE_INFO *create_info,
if (check_if_keyname_exists(key_name, *key_info_buffer, key_info))
{
my_error(ER_DUP_KEYNAME, MYF(0), key_name);
- DBUG_RETURN(-1);
+ DBUG_RETURN(TRUE);
}
key_info->name=(char*) key_name;
}
@@ -1462,7 +2999,7 @@ static int mysql_prepare_table(THD *thd, HA_CREATE_INFO *create_info,
if (!key_info->name || check_column_name(key_info->name))
{
my_error(ER_WRONG_NAME_FOR_INDEX, MYF(0), key_info->name);
- DBUG_RETURN(-1);
+ DBUG_RETURN(TRUE);
}
if (!(key_info->flags & HA_NULL_PART_KEY))
unique_key=1;
@@ -1470,27 +3007,90 @@ static int mysql_prepare_table(THD *thd, HA_CREATE_INFO *create_info,
if (key_length > max_key_length && key->type != Key::FULLTEXT)
{
my_error(ER_TOO_LONG_KEY,MYF(0),max_key_length);
- DBUG_RETURN(-1);
+ DBUG_RETURN(TRUE);
}
key_info++;
}
if (!unique_key && !primary_key &&
- (file->table_flags() & HA_REQUIRE_PRIMARY_KEY))
+ (file->ha_table_flags() & HA_REQUIRE_PRIMARY_KEY))
{
my_message(ER_REQUIRES_PRIMARY_KEY, ER(ER_REQUIRES_PRIMARY_KEY), MYF(0));
- DBUG_RETURN(-1);
+ DBUG_RETURN(TRUE);
}
if (auto_increment > 0)
{
my_message(ER_WRONG_AUTO_KEY, ER(ER_WRONG_AUTO_KEY), MYF(0));
- DBUG_RETURN(-1);
+ DBUG_RETURN(TRUE);
}
/* Sort keys in optimized order */
- my_qsort((gptr) *key_info_buffer, *key_count, sizeof(KEY),
- (qsort_cmp) sort_keys);
+ my_qsort((uchar*) *key_info_buffer, *key_count, sizeof(KEY),
+ (qsort_cmp) sort_keys);
create_info->null_bits= null_fields;
- DBUG_RETURN(0);
+ /* Check fields. */
+ it.rewind();
+ while ((sql_field=it++))
+ {
+ Field::utype type= (Field::utype) MTYP_TYPENR(sql_field->unireg_check);
+
+ if (thd->variables.sql_mode & MODE_NO_ZERO_DATE &&
+ !sql_field->def &&
+ sql_field->sql_type == MYSQL_TYPE_TIMESTAMP &&
+ (sql_field->flags & NOT_NULL_FLAG) &&
+ (type == Field::NONE || type == Field::TIMESTAMP_UN_FIELD))
+ {
+ /*
+ An error should be reported if:
+ - NO_ZERO_DATE SQL mode is active;
+ - there is no explicit DEFAULT clause (default column value);
+ - this is a TIMESTAMP column;
+ - the column is not NULL;
+ - this is not the DEFAULT CURRENT_TIMESTAMP column.
+
+ In other words, an error should be reported if
+ - NO_ZERO_DATE SQL mode is active;
+ - the column definition is equivalent to
+ 'column_name TIMESTAMP DEFAULT 0'.
+ */
+
+ my_error(ER_INVALID_DEFAULT, MYF(0), sql_field->field_name);
+ DBUG_RETURN(TRUE);
+ }
+ }
+
+ DBUG_RETURN(FALSE);
+}
+
+
+/*
+ Set table default charset, if not set
+
+ SYNOPSIS
+ set_table_default_charset()
+ create_info Table create information
+
+ DESCRIPTION
+ If the table character set was not given explicitely,
+ let's fetch the database default character set and
+ apply it to the table.
+*/
+
+static void set_table_default_charset(THD *thd,
+ HA_CREATE_INFO *create_info, char *db)
+{
+ /*
+ If the table character set was not given explicitly,
+ let's fetch the database default character set and
+ apply it to the table.
+ */
+ if (!create_info->default_table_charset)
+ {
+ HA_CREATE_INFO db_info;
+
+ load_db_opt_by_name(thd, db, &db_info);
+
+ create_info->default_table_charset= db_info.default_table_charset;
+ }
}
@@ -1507,7 +3107,7 @@ static int mysql_prepare_table(THD *thd, HA_CREATE_INFO *create_info,
In this case the error is given
*/
-static bool prepare_blob_field(THD *thd, create_field *sql_field)
+static bool prepare_blob_field(THD *thd, Create_field *sql_field)
{
DBUG_ENTER("prepare_blob_field");
@@ -1524,7 +3124,7 @@ static bool prepare_blob_field(THD *thd, create_field *sql_field)
MAX_FIELD_VARCHARLENGTH / sql_field->charset->mbmaxlen);
DBUG_RETURN(1);
}
- sql_field->sql_type= FIELD_TYPE_BLOB;
+ sql_field->sql_type= MYSQL_TYPE_BLOB;
sql_field->flags|= BLOB_FLAG;
sprintf(warn_buff, ER(ER_AUTO_CONVERT), sql_field->field_name,
(sql_field->charset == &my_charset_bin) ? "VARBINARY" : "VARCHAR",
@@ -1532,7 +3132,7 @@ static bool prepare_blob_field(THD *thd, create_field *sql_field)
push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, ER_AUTO_CONVERT,
warn_buff);
}
-
+
if ((sql_field->flags & BLOB_FLAG) && sql_field->length)
{
if (sql_field->sql_type == FIELD_TYPE_BLOB ||
@@ -1550,8 +3150,9 @@ static bool prepare_blob_field(THD *thd, create_field *sql_field)
/*
- Preparation of create_field for SP function return values.
- Based on code used in the inner loop of mysql_prepare_table() above
+ Preparation of Create_field for SP function return values.
+ Based on code used in the inner loop of mysql_prepare_create_table()
+ above.
SYNOPSIS
sp_prepare_create_field()
@@ -1563,13 +3164,13 @@ static bool prepare_blob_field(THD *thd, create_field *sql_field)
*/
-void sp_prepare_create_field(THD *thd, create_field *sql_field)
+void sp_prepare_create_field(THD *thd, Create_field *sql_field)
{
- if (sql_field->sql_type == FIELD_TYPE_SET ||
- sql_field->sql_type == FIELD_TYPE_ENUM)
+ if (sql_field->sql_type == MYSQL_TYPE_SET ||
+ sql_field->sql_type == MYSQL_TYPE_ENUM)
{
uint32 field_length, dummy;
- if (sql_field->sql_type == FIELD_TYPE_SET)
+ if (sql_field->sql_type == MYSQL_TYPE_SET)
{
calculate_interval_lengths(sql_field->charset,
sql_field->interval, &dummy,
@@ -1577,7 +3178,7 @@ void sp_prepare_create_field(THD *thd, create_field *sql_field)
sql_field->length= field_length +
(sql_field->interval->count - 1);
}
- else /* FIELD_TYPE_ENUM */
+ else /* MYSQL_TYPE_ENUM */
{
calculate_interval_lengths(sql_field->charset,
sql_field->interval,
@@ -1587,7 +3188,7 @@ void sp_prepare_create_field(THD *thd, create_field *sql_field)
set_if_smaller(sql_field->length, MAX_FIELD_WIDTH-1);
}
- if (sql_field->sql_type == FIELD_TYPE_BIT)
+ if (sql_field->sql_type == MYSQL_TYPE_BIT)
{
sql_field->pack_flag= FIELDFLAG_NUMBER |
FIELDFLAG_TREAT_BIT_AS_CHAR;
@@ -1603,46 +3204,53 @@ void sp_prepare_create_field(THD *thd, create_field *sql_field)
Create a table
SYNOPSIS
- mysql_create_table()
- thd Thread object
- db Database
- table_name Table name
- create_info [in/out] Create information (like MAX_ROWS)
- alter_info [in/out] List of columns and indexes to create
- internal_tmp_table Set to 1 if this is an internal temporary table
- (From ALTER TABLE)
+ mysql_create_table_no_lock()
+ thd Thread object
+ db Database
+ table_name Table name
+ create_info Create information (like MAX_ROWS)
+ fields List of fields to create
+ keys List of keys to create
+ internal_tmp_table Set to 1 if this is an internal temporary table
+ (From ALTER TABLE)
+ select_field_count
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.
+
no_log is needed for the case of CREATE ... SELECT,
as the logging will be done later in sql_insert.cc
select_field_count is also used for CREATE ... SELECT,
and must be zero for standard create of table.
- Note that structures passed as 'create_info' and 'alter_info' parameters
- may be modified by this function. It is responsibility of the caller to
- make a copy of create_info in order to provide correct execution in
- prepared statements/stored routines.
-
RETURN VALUES
FALSE OK
TRUE error
*/
-bool mysql_create_table(THD *thd,const char *db, const char *table_name,
- HA_CREATE_INFO *create_info,
- Alter_info *alter_info,
- bool internal_tmp_table,
- uint select_field_count)
+bool mysql_create_table_no_lock(THD *thd,
+ const char *db, const char *table_name,
+ HA_CREATE_INFO *create_info,
+ Alter_info *alter_info,
+ bool internal_tmp_table,
+ uint select_field_count)
{
char path[FN_REFLEN];
+ uint path_length;
const char *alias;
uint db_options, key_count;
KEY *key_info_buffer;
handler *file;
bool error= TRUE;
- DBUG_ENTER("mysql_create_table");
+ DBUG_ENTER("mysql_create_table_no_lock");
+ DBUG_PRINT("enter", ("db: '%s' table: '%s' tmp: %d",
+ db, table_name, internal_tmp_table));
+
/* Check for duplicate fields and check type of table to create */
if (!alter_info->create_list.elements)
@@ -1651,77 +3259,202 @@ bool mysql_create_table(THD *thd,const char *db, const char *table_name,
MYF(0));
DBUG_RETURN(TRUE);
}
- if (check_engine(thd, table_name, &create_info->db_type))
+ if (check_engine(thd, table_name, create_info))
DBUG_RETURN(TRUE);
db_options= create_info->table_options;
if (create_info->row_type == ROW_TYPE_DYNAMIC)
db_options|=HA_OPTION_PACK_RECORD;
alias= table_case_name(create_info, table_name);
- file= get_new_handler((TABLE*) 0, thd->mem_root, create_info->db_type);
-
-#ifdef NOT_USED
- /*
- if there is a technical reason for a handler not to have support
- for temp. tables this code can be re-enabled.
- Otherwise, if a handler author has a wish to prohibit usage of
- temporary tables for his handler he should implement a check in
- ::create() method
- */
- if ((create_info->options & HA_LEX_CREATE_TMP_TABLE) &&
- (file->table_flags() & HA_NO_TEMP_TABLES))
+ if (!(file= get_new_handler((TABLE_SHARE*) 0, thd->mem_root,
+ create_info->db_type)))
{
- my_error(ER_ILLEGAL_HA, MYF(0), table_name);
+ mem_alloc_error(sizeof(handler));
DBUG_RETURN(TRUE);
}
-#endif
+#ifdef WITH_PARTITION_STORAGE_ENGINE
+ partition_info *part_info= thd->work_part_info;
- /*
- If the table character set was not given explicitely,
- let's fetch the database default character set and
- apply it to the table.
- */
- if (!create_info->default_table_charset)
+ if (!part_info && create_info->db_type->partition_flags &&
+ (create_info->db_type->partition_flags() & HA_USE_AUTO_PARTITION))
{
- HA_CREATE_INFO db_info;
-
- load_db_opt_by_name(thd, db, &db_info);
+ /*
+ Table is not defined as a partitioned table but the engine handles
+ all tables as partitioned. The handler will set up the partition info
+ object with the default settings.
+ */
+ thd->work_part_info= part_info= new partition_info();
+ if (!part_info)
+ {
+ mem_alloc_error(sizeof(partition_info));
+ DBUG_RETURN(TRUE);
+ }
+ file->set_auto_partitions(part_info);
+ part_info->default_engine_type= create_info->db_type;
+ part_info->is_auto_partitioned= TRUE;
+ }
+ if (part_info)
+ {
+ /*
+ The table has been specified as a partitioned table.
+ If this is part of an ALTER TABLE the handler will be the partition
+ handler but we need to specify the default handler to use for
+ partitions also in the call to check_partition_info. We transport
+ this information in the default_db_type variable, it is either
+ DB_TYPE_DEFAULT or the engine set in the ALTER TABLE command.
+
+ Check that we don't use foreign keys in the table since it won't
+ work even with InnoDB beneath it.
+ */
+ List_iterator<Key> key_iterator(alter_info->key_list);
+ Key *key;
+ handlerton *part_engine_type= create_info->db_type;
+ char *part_syntax_buf;
+ uint syntax_len;
+ handlerton *engine_type;
+ if (create_info->options & HA_LEX_CREATE_TMP_TABLE)
+ {
+ my_error(ER_PARTITION_NO_TEMPORARY, MYF(0));
+ goto err;
+ }
+ while ((key= key_iterator++))
+ {
+ if (key->type == Key::FOREIGN_KEY &&
+ !part_info->is_auto_partitioned)
+ {
+ my_error(ER_CANNOT_ADD_FOREIGN, MYF(0));
+ goto err;
+ }
+ }
+ if ((part_engine_type == partition_hton) &&
+ part_info->default_engine_type)
+ {
+ /*
+ This only happens at ALTER TABLE.
+ default_engine_type was assigned from the engine set in the ALTER
+ TABLE command.
+ */
+ ;
+ }
+ else
+ {
+ if (create_info->used_fields & HA_CREATE_USED_ENGINE)
+ {
+ part_info->default_engine_type= create_info->db_type;
+ }
+ else
+ {
+ if (part_info->default_engine_type == NULL)
+ {
+ part_info->default_engine_type= ha_checktype(thd,
+ DB_TYPE_DEFAULT, 0, 0);
+ }
+ }
+ }
+ DBUG_PRINT("info", ("db_type = %s create_info->db_type = %s",
+ ha_resolve_storage_engine_name(part_info->default_engine_type),
+ ha_resolve_storage_engine_name(create_info->db_type)));
+ if (part_info->check_partition_info(thd, &engine_type, file,
+ create_info, TRUE))
+ goto err;
+ part_info->default_engine_type= engine_type;
- create_info->default_table_charset= db_info.default_table_charset;
+ /*
+ We reverse the partitioning parser and generate a standard format
+ for syntax stored in frm file.
+ */
+ if (!(part_syntax_buf= generate_partition_syntax(part_info,
+ &syntax_len,
+ TRUE, TRUE)))
+ goto err;
+ part_info->part_info_string= part_syntax_buf;
+ part_info->part_info_len= syntax_len;
+ if ((!(engine_type->partition_flags &&
+ engine_type->partition_flags() & HA_CAN_PARTITION)) ||
+ create_info->db_type == partition_hton)
+ {
+ /*
+ The handler assigned to the table cannot handle partitioning.
+ Assign the partition handler as the handler of the table.
+ */
+ DBUG_PRINT("info", ("db_type: %s",
+ ha_resolve_storage_engine_name(create_info->db_type)));
+ delete file;
+ create_info->db_type= partition_hton;
+ if (!(file= get_ha_partition(part_info)))
+ {
+ DBUG_RETURN(TRUE);
+ }
+ /*
+ If we have default number of partitions or subpartitions we
+ might require to set-up the part_info object such that it
+ 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 !=
+ file->get_default_no_partitions(create_info))
+ {
+ uint i;
+ List_iterator<partition_element> part_it(part_info->partitions);
+ part_it++;
+ DBUG_ASSERT(thd->lex->sql_command != SQLCOM_CREATE_TABLE);
+ for (i= 1; i < part_info->partitions.elements; i++)
+ (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 !=
+ 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);
+ }
+ }
+ else if (create_info->db_type != engine_type)
+ {
+ /*
+ We come here when we don't use a partitioned handler.
+ Since we use a partitioned table it must be "native partitioned".
+ We have switched engine from defaults, most likely only specified
+ engines in partition clauses.
+ */
+ delete file;
+ if (!(file= get_new_handler((TABLE_SHARE*) 0, thd->mem_root,
+ engine_type)))
+ {
+ mem_alloc_error(sizeof(handler));
+ DBUG_RETURN(TRUE);
+ }
+ }
}
+#endif
- if (mysql_prepare_table(thd, create_info, alter_info, internal_tmp_table,
- &db_options, file,
- &key_info_buffer, &key_count,
- select_field_count))
- DBUG_RETURN(TRUE);
+ 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,
+ &key_info_buffer, &key_count,
+ select_field_count))
+ goto err;
/* Check if table exists */
if (create_info->options & HA_LEX_CREATE_TMP_TABLE)
{
- set_tmp_file_path(path, sizeof(path), thd);
+ path_length= build_tmptable_filename(thd, path, sizeof(path));
create_info->table_options|=HA_CREATE_DELAY_KEY_WRITE;
}
else
{
- #ifdef FN_DEVCHAR
- /* check if the table name contains FN_DEVCHAR when defined */
- const char *start= alias;
- while (*start != '\0')
- {
- if (*start == FN_DEVCHAR)
- {
- my_error(ER_WRONG_TABLE_NAME, MYF(0), alias);
- DBUG_RETURN(TRUE);
- }
- start++;
- }
- #endif
- build_table_path(path, sizeof(path), db, alias, reg_ext);
+ path_length= build_table_filename(path, sizeof(path), db, alias, reg_ext,
+ internal_tmp_table ? FN_IS_TMP : 0);
}
/* Check if table already exists */
- if ((create_info->options & HA_LEX_CREATE_TMP_TABLE)
- && find_temporary_table(thd,db,table_name))
+ if ((create_info->options & HA_LEX_CREATE_TMP_TABLE) &&
+ find_temporary_table(thd, db, table_name))
{
if (create_info->options & HA_LEX_CREATE_IF_NOT_EXISTS)
{
@@ -1729,29 +3462,35 @@ bool mysql_create_table(THD *thd,const char *db, const char *table_name,
push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
ER_TABLE_EXISTS_ERROR, ER(ER_TABLE_EXISTS_ERROR),
alias);
- DBUG_RETURN(FALSE);
+ error= 0;
+ goto err;
}
- DBUG_PRINT("info",("1"));
my_error(ER_TABLE_EXISTS_ERROR, MYF(0), alias);
- DBUG_RETURN(TRUE);
+ goto err;
}
+
VOID(pthread_mutex_lock(&LOCK_open));
if (!internal_tmp_table && !(create_info->options & HA_LEX_CREATE_TMP_TABLE))
{
- /*
- Inspecting table cache for placeholders created by concurrent
- CREATE TABLE ... SELECT statements to avoid interfering with them
- is 5.0-only solution. Starting from 5.1 we solve this problem by
- obtaining name-lock on the table to be created first.
- */
- if (table_cache_has_open_placeholder(thd, db, table_name) ||
- !access(path, F_OK))
+ if (!access(path,F_OK))
{
if (create_info->options & HA_LEX_CREATE_IF_NOT_EXISTS)
goto warn;
- DBUG_PRINT("info",("2"));
my_error(ER_TABLE_EXISTS_ERROR,MYF(0),table_name);
- goto end;
+ goto unlock_and_end;
+ }
+ /*
+ We don't assert here, but check the result, because the table could be
+ in the table definition cache and in the same time the .frm could be
+ missing from the disk, in case of manual intervention which deletes
+ the .frm file. The user has to use FLUSH TABLES; to clear the cache.
+ Then she could create the table. This case is pretty obscure and
+ therefore we don't introduce a new error message only for it.
+ */
+ if (get_cached_table_share(db, alias))
+ {
+ my_error(ER_TABLE_EXISTS_ERROR, MYF(0), table_name);
+ goto unlock_and_end;
}
}
@@ -1781,47 +3520,88 @@ bool mysql_create_table(THD *thd,const char *db, const char *table_name,
if (create_if_not_exists)
goto warn;
my_error(ER_TABLE_EXISTS_ERROR,MYF(0),table_name);
- goto end;
+ goto unlock_and_end;
break;
default:
DBUG_PRINT("info", ("error: %u from storage engine", retcode));
my_error(retcode, MYF(0),table_name);
- goto end;
+ goto unlock_and_end;
}
}
- thd->proc_info="creating table";
+ thd_proc_info(thd, "creating table");
create_info->table_existed= 0; // Mark that table is created
- if (thd->variables.sql_mode & MODE_NO_DIR_IN_CREATE)
+#ifdef HAVE_READLINK
+ if (test_if_data_home_dir(create_info->data_file_name))
+ {
+ my_error(ER_WRONG_ARGUMENTS, MYF(0), "DATA DIRECTORY");
+ goto unlock_and_end;
+ }
+ if (test_if_data_home_dir(create_info->index_file_name))
+ {
+ my_error(ER_WRONG_ARGUMENTS, MYF(0), "INDEX DIRECTORY");
+ goto unlock_and_end;
+ }
+
+#ifdef WITH_PARTITION_STORAGE_ENGINE
+ if (check_partition_dirs(thd->lex->part_info))
+ {
+ goto unlock_and_end;
+ }
+#endif /* WITH_PARTITION_STORAGE_ENGINE */
+
+ if (!my_use_symdir || (thd->variables.sql_mode & MODE_NO_DIR_IN_CREATE))
+#endif /* HAVE_READLINK */
+ {
+ if (create_info->data_file_name)
+ push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ WARN_OPTION_IGNORED, ER(WARN_OPTION_IGNORED),
+ "DATA DIRECTORY");
+ if (create_info->index_file_name)
+ push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ WARN_OPTION_IGNORED, ER(WARN_OPTION_IGNORED),
+ "INDEX DIRECTORY");
create_info->data_file_name= create_info->index_file_name= 0;
+ }
create_info->table_options=db_options;
+ path[path_length - reg_ext_length]= '\0'; // Remove .frm extension
if (rea_create_table(thd, path, db, table_name,
create_info, alter_info->create_list,
- key_count, key_info_buffer))
- goto end;
+ key_count, key_info_buffer, file))
+ goto unlock_and_end;
+
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)))
{
(void) rm_temporary_table(create_info->db_type, path);
- goto end;
+ goto unlock_and_end;
}
- thd->tmp_table_used= 1;
- }
- if (!internal_tmp_table && mysql_bin_log.is_open())
- {
- thd->clear_error();
- Query_log_event qinfo(thd, thd->query, thd->query_length, FALSE, FALSE);
- mysql_bin_log.write(&qinfo);
+ thd->thread_specific_used= TRUE;
}
- error= FALSE;
-end:
+ /*
+ 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))))
+ write_bin_log(thd, TRUE, thd->query, thd->query_length);
+ error= FALSE;
+unlock_and_end:
VOID(pthread_mutex_unlock(&LOCK_open));
- thd->proc_info="After create";
+
+err:
+ thd_proc_info(thd, "After create");
+ delete file;
DBUG_RETURN(error);
warn:
@@ -1830,9 +3610,87 @@ warn:
ER_TABLE_EXISTS_ERROR, ER(ER_TABLE_EXISTS_ERROR),
alias);
create_info->table_existed= 1; // Mark that table existed
- goto end;
+ goto unlock_and_end;
}
+
+/*
+ Database and name-locking aware wrapper for mysql_create_table_no_lock(),
+*/
+
+bool mysql_create_table(THD *thd, const char *db, const char *table_name,
+ HA_CREATE_INFO *create_info,
+ Alter_info *alter_info,
+ bool internal_tmp_table,
+ uint select_field_count)
+{
+ TABLE *name_lock= 0;
+ bool result;
+ 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)))
+ {
+ wait_for_condition(thd, &LOCK_lock_db, &COND_refresh);
+ pthread_mutex_lock(&LOCK_lock_db);
+ }
+
+ if (thd->killed)
+ {
+ pthread_mutex_unlock(&LOCK_lock_db);
+ DBUG_RETURN(TRUE);
+ }
+ creating_table++;
+ pthread_mutex_unlock(&LOCK_lock_db);
+
+ 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;
+ }
+ else
+ {
+ my_error(ER_TABLE_EXISTS_ERROR,MYF(0),table_name);
+ result= TRUE;
+ }
+ goto unlock;
+ }
+ }
+
+ result= mysql_create_table_no_lock(thd, db, table_name, create_info,
+ alter_info,
+ internal_tmp_table,
+ select_field_count);
+
+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);
+ DBUG_RETURN(result);
+}
+
+
/*
** Give the key name after the first field with an optional '_#' after
**/
@@ -1876,24 +3734,50 @@ make_unique_key_name(const char *field_name,KEY *start,KEY *end)
** Alter a table definition
****************************************************************************/
+
+/*
+ Rename a table.
+
+ SYNOPSIS
+ mysql_rename_table()
+ base The handlerton handle.
+ old_db The old database name.
+ old_name The old table name.
+ new_db The new database name.
+ new_name The new table name.
+ flags flags for build_table_filename().
+ FN_FROM_IS_TMP old_name is temporary.
+ FN_TO_IS_TMP new_name is temporary.
+ NO_FRM_RENAME Don't rename the FRM file
+ but only the table in the storage engine.
+
+ RETURN
+ FALSE OK
+ TRUE Error
+*/
+
bool
-mysql_rename_table(enum db_type base,
- const char *old_db,
- const char *old_name,
- const char *new_db,
- const char *new_name)
+mysql_rename_table(handlerton *base, const char *old_db,
+ const char *old_name, const char *new_db,
+ const char *new_name, uint flags)
{
THD *thd= current_thd;
char from[FN_REFLEN], to[FN_REFLEN], lc_from[FN_REFLEN], lc_to[FN_REFLEN];
char *from_base= from, *to_base= to;
char tmp_name[NAME_LEN+1];
- handler *file= (base == DB_TYPE_UNKNOWN ? 0 :
- get_new_handler((TABLE*) 0, thd->mem_root, base));
+ handler *file;
int error=0;
DBUG_ENTER("mysql_rename_table");
+ DBUG_PRINT("enter", ("old: '%s'.'%s' new: '%s'.'%s'",
+ old_db, old_name, new_db, new_name));
+
+ file= (base == NULL ? 0 :
+ get_new_handler((TABLE_SHARE*) 0, thd->mem_root, base));
- build_table_path(from, sizeof(from), old_db, old_name, "");
- build_table_path(to, sizeof(to), new_db, new_name, "");
+ build_table_filename(from, sizeof(from), old_db, old_name, "",
+ flags & FN_FROM_IS_TMP);
+ build_table_filename(to, sizeof(to), new_db, new_name, "",
+ flags & FN_TO_IS_TMP);
/*
If lower_case_table_names == 2 (case-preserving but case-insensitive
@@ -1901,27 +3785,29 @@ mysql_rename_table(enum db_type base,
a lowercase file name, but we leave the .frm in mixed case.
*/
if (lower_case_table_names == 2 && file &&
- !(file->table_flags() & HA_FILE_BASED))
+ !(file->ha_table_flags() & HA_FILE_BASED))
{
strmov(tmp_name, old_name);
my_casedn_str(files_charset_info, tmp_name);
- build_table_path(lc_from, sizeof(lc_from), old_db, tmp_name, "");
+ build_table_filename(lc_from, sizeof(lc_from), old_db, tmp_name, "",
+ flags & FN_FROM_IS_TMP);
from_base= lc_from;
strmov(tmp_name, new_name);
my_casedn_str(files_charset_info, tmp_name);
- build_table_path(lc_to, sizeof(lc_to), new_db, tmp_name, "");
+ build_table_filename(lc_to, sizeof(lc_to), new_db, tmp_name, "",
+ flags & FN_TO_IS_TMP);
to_base= lc_to;
}
- if (!file || !(error=file->rename_table(from_base, to_base)))
+ if (!file || !(error=file->ha_rename_table(from_base, to_base)))
{
- if (rename_file_ext(from,to,reg_ext))
+ if (!(flags & NO_FRM_RENAME) && rename_file_ext(from,to,reg_ext))
{
error=my_errno;
/* Restore old file name */
if (file)
- file->rename_table(to_base, from_base);
+ file->ha_rename_table(to_base, from_base);
}
}
delete file;
@@ -1940,8 +3826,9 @@ mysql_rename_table(enum db_type base,
wait_while_table_is_used()
thd Thread handler
table Table to remove from cache
- function HA_EXTRA_PREPARE_FOR_DELETE if table is to be deleted
- HA_EXTRA_FORCE_REOPEN if table is not be used
+ 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.
@@ -1951,20 +3838,24 @@ mysql_rename_table(enum db_type base,
Win32 clients must also have a WRITE LOCK on the table !
*/
-static void wait_while_table_is_used(THD *thd,TABLE *table,
- enum ha_extra_function function)
+void wait_while_table_is_used(THD *thd, TABLE *table,
+ enum ha_extra_function function)
{
- DBUG_PRINT("enter",("table: %s", table->s->table_name));
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);
VOID(table->file->extra(function));
/* Mark all tables that are in use as 'old' */
- mysql_lock_abort(thd, table); // end threads waiting on lock
+ 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,
- table->s->table_name, RTFC_WAIT_OTHER_THREAD_FLAG);
+ remove_table_from_cache(thd, table->s->db.str,
+ table->s->table_name.str,
+ RTFC_WAIT_OTHER_THREAD_FLAG);
DBUG_VOID_RETURN;
}
@@ -1989,7 +3880,7 @@ void close_cached_table(THD *thd, TABLE *table)
{
DBUG_ENTER("close_cached_table");
- wait_while_table_is_used(thd, table, HA_EXTRA_PREPARE_FOR_DELETE);
+ wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN);
/* Close lock if this is not got with LOCK TABLES */
if (thd->lock)
{
@@ -1997,7 +3888,7 @@ void close_cached_table(THD *thd, TABLE *table)
thd->lock=0; // Start locked threads
}
/* Close all copies of 'table'. This also frees all LOCK TABLES lock */
- thd->open_tables=unlink_open_table(thd,thd->open_tables,table);
+ unlink_open_table(thd, table, TRUE);
/* When lock on LOCK_open is freed other threads can continue */
broadcast_refresh();
@@ -2035,23 +3926,22 @@ static int prepare_for_restore(THD* thd, TABLE_LIST* table,
else
{
char* backup_dir= thd->lex->backup_dir;
- char src_path[FN_REFLEN], dst_path[FN_REFLEN];
+ char src_path[FN_REFLEN], dst_path[FN_REFLEN], uname[FN_REFLEN];
char* table_name= table->table_name;
char* db= table->db;
- if (fn_format_relative_to_data_home(src_path, table_name, backup_dir,
- reg_ext))
+ VOID(tablename_to_filename(table->table_name, uname, sizeof(uname)));
+
+ if (fn_format_relative_to_data_home(src_path, uname, backup_dir, reg_ext))
DBUG_RETURN(-1); // protect buffer overflow
- my_snprintf(dst_path, sizeof(dst_path), "%s%s/%s",
- mysql_real_data_home, db, table_name);
+ build_table_filename(dst_path, sizeof(dst_path),
+ db, table_name, reg_ext, 0);
if (lock_and_wait_for_table_name(thd,table))
DBUG_RETURN(-1);
- if (my_copy(src_path,
- fn_format(dst_path, dst_path,"", reg_ext, 4),
- MYF(MY_WME)))
+ if (my_copy(src_path, dst_path, MYF(MY_WME)))
{
pthread_mutex_lock(&LOCK_open);
unlock_table_name(thd, table);
@@ -2081,16 +3971,22 @@ static int prepare_for_restore(THD* thd, TABLE_LIST* table,
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,
+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))
@@ -2098,12 +3994,39 @@ static int prepare_for_repair(THD* thd, TABLE_LIST *table_list,
if (!(table= table_list->table)) /* if open_ltable failed */
{
- char name[FN_REFLEN];
- build_table_path(name, sizeof(name), table_list->db,
- table_list->table_name, "");
- if (openfrm(thd, name, "", 0, 0, 0, &tmp_table))
+ 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);
+ }
+
+ /* A MERGE table must not come here. */
+ DBUG_ASSERT(!table->child_l);
+
+ /*
+ 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;
}
/*
@@ -2116,14 +4039,10 @@ static int prepare_for_repair(THD* thd, TABLE_LIST *table_list,
- Run a normal repair using the new index file and the old data file
*/
- char from[FN_REFLEN],tmp[FN_REFLEN+32];
- const char **ext= table->file->bas_ext();
- MY_STAT stat_info;
-
if (table->s->frm_version != FRM_VER_TRUE_VARCHAR)
{
error= send_check_errmsg(thd, table_list, "repair",
- "Failed reparing incompatible .FRM file");
+ "Failed repairing incompatible .frm file");
goto end;
}
@@ -2133,10 +4052,12 @@ static int prepare_for_repair(THD* thd, TABLE_LIST *table_list,
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
- strxmov(from, table->s->path, ext[1], NullS); // Name of 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
@@ -2200,7 +4121,11 @@ static int prepare_for_repair(THD* thd, TABLE_LIST *table_list,
end:
if (table == &tmp_table)
- closefrm(table); // Free allocated memory
+ {
+ pthread_mutex_lock(&LOCK_open);
+ closefrm(table, 1); // Free allocated memory
+ pthread_mutex_unlock(&LOCK_open);
+ }
DBUG_RETURN(error);
}
@@ -2234,7 +4159,9 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables,
int result_code;
DBUG_ENTER("mysql_admin_table");
- field_list.push_back(item = new Item_empty_string("Table", NAME_LEN*2));
+ 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;
@@ -2246,17 +4173,19 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables,
Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
DBUG_RETURN(TRUE);
- mysql_ha_flush(thd, tables, MYSQL_HA_CLOSE_FINAL, FALSE);
+ mysql_ha_rm_tables(thd, tables, FALSE);
+
for (table= tables; table; table= table->next_local)
{
char table_name[NAME_LEN*2+2];
char* db = table->db;
bool fatal_error=0;
+ DBUG_PRINT("admin", ("table: '%s'.'%s'", table->db, table->table_name));
+ DBUG_PRINT("admin", ("extra_open_options: %u", extra_open_options));
strxmov(table_name, db, ".", table->table_name, NullS);
thd->open_options|= extra_open_options;
table->lock_type= lock_type;
-
/* open only one table from local list of command */
{
TABLE_LIST *save_next_global, *save_next_local;
@@ -2264,7 +4193,7 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables,
table->next_global= 0;
save_next_local= table->next_local;
table->next_local= 0;
- select->table_list.first= (byte*)table;
+ select->table_list.first= (uchar*)table;
/*
Time zone tables and SP tables can be add to lex->query_tables list,
so it have to be prepared.
@@ -2277,21 +4206,74 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables,
thd->no_warnings_for_error= no_warnings_for_error;
if (view_operator_func == NULL)
table->required_type=FRMTYPE_TABLE;
+
open_and_lock_tables(thd, table);
thd->no_warnings_for_error= 0;
table->next_global= save_next_global;
table->next_local= save_next_local;
thd->open_options&= ~extra_open_options;
+#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"));
;
}
}
@@ -2306,6 +4288,7 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables,
*/
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));
@@ -2320,6 +4303,7 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables,
if (table->view)
{
+ DBUG_PRINT("admin", ("calling view_operator_func"));
result_code= (*view_operator_func)(thd, table);
goto send_result;
}
@@ -2332,8 +4316,10 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables,
if ((table->table->db_stat & HA_READ_ONLY) && open_for_modify)
{
+ /* purecov: begin inspected */
char buff[FN_REFLEN + MYSQL_ERRMSG_SIZE];
- uint length;
+ 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);
@@ -2341,25 +4327,32 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables,
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);
- remove_table_from_cache(thd, table->table->s->db,
- table->table->s->table_name,
+ 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);
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. */
@@ -2369,6 +4362,8 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables,
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);
@@ -2377,27 +4372,37 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables,
system_charset_info);
if (protocol->write())
goto err;
+ /* purecov: end */
}
- if (operator_func == &handler::ha_repair)
+ if (operator_func == &handler::ha_repair &&
+ !(check_opt->sql_flags & TT_USEFRM))
{
if ((table->table->file->check_old_types() == HA_ADMIN_NEEDS_ALTER) ||
(table->table->file->ha_check_for_upgrade(check_opt) ==
HA_ADMIN_NEEDS_ALTER))
{
- my_bool save_no_send_ok= thd->net.no_send_ok;
+ DBUG_PRINT("admin", ("recreating table"));
+ ha_autocommit_or_rollback(thd, 1);
close_thread_tables(thd);
tmp_disable_binlog(thd); // binlogging is done by caller if wanted
- thd->net.no_send_ok= TRUE;
result_code= mysql_recreate_table(thd, table);
- thd->net.no_send_ok= save_no_send_ok;
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();
goto send_result;
}
-
}
+ 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));
send_result:
@@ -2411,8 +4416,9 @@ send_result:
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],
- warning_level_length[err->level], 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;
@@ -2429,8 +4435,8 @@ send_result_message:
switch (result_code) {
case HA_ADMIN_NOT_IMPLEMENTED:
{
- char buf[ERRMSGSIZE+20];
- uint length=my_snprintf(buf, ERRMSGSIZE,
+ 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);
@@ -2439,8 +4445,8 @@ send_result_message:
case HA_ADMIN_NOT_BASE_TABLE:
{
- char buf[ERRMSGSIZE+20];
- uint length= my_snprintf(buf, ERRMSGSIZE,
+ 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);
@@ -2485,33 +4491,53 @@ send_result_message:
case HA_ADMIN_TRY_ALTER:
{
- my_bool save_no_send_ok= thd->net.no_send_ok;
/*
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);
+ protocol->store(STRING_WITH_LEN(
+ "Table does not support optimize, doing recreate + analyze instead"),
+ system_charset_info);
+ if (protocol->write())
+ goto err;
+ ha_autocommit_or_rollback(thd, 0);
close_thread_tables(thd);
+ 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;
tmp_disable_binlog(thd); // binlogging is done by caller if wanted
- thd->net.no_send_ok= TRUE;
result_code= mysql_recreate_table(thd, table);
- thd->net.no_send_ok= save_no_send_ok;
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();
+ ha_autocommit_or_rollback(thd, 0);
close_thread_tables(thd);
if (!result_code) // recreation went ok
{
- if ((table->table= open_ltable(thd, table, lock_type)) &&
- ((result_code= table->table->file->analyze(thd, check_opt)) > 0))
+ 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
}
+ /* 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
{
- const char *err_msg;
- if ((err_msg= thd->net.last_error))
+ DBUG_ASSERT(thd->is_error());
+ if (thd->is_error())
{
+ const char *err_msg= thd->main_da.message();
if (!thd->vio_ok())
{
sql_print_error(err_msg);
@@ -2521,12 +4547,14 @@ send_result_message:
/* Hijack the row already in-progress. */
protocol->store(STRING_WITH_LEN("error"), system_charset_info);
protocol->store(err_msg, system_charset_info);
- (void)protocol->write();
+ 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;
@@ -2537,7 +4565,7 @@ send_result_message:
case HA_ADMIN_WRONG_CHECKSUM:
{
protocol->store(STRING_WITH_LEN("note"), system_charset_info);
- protocol->store(ER(ER_VIEW_CHECKSUM), (uint) strlen(ER(ER_VIEW_CHECKSUM)),
+ protocol->store(ER(ER_VIEW_CHECKSUM), strlen(ER(ER_VIEW_CHECKSUM)),
system_charset_info);
break;
}
@@ -2545,11 +4573,12 @@ send_result_message:
case HA_ADMIN_NEEDS_UPGRADE:
case HA_ADMIN_NEEDS_ALTER:
{
- char buf[ERRMSGSIZE];
- uint length;
+ char buf[MYSQL_ERRMSG_SIZE];
+ size_t length;
protocol->store(STRING_WITH_LEN("error"), system_charset_info);
- length=my_snprintf(buf, ERRMSGSIZE, ER(ER_TABLE_NEEDS_UPGRADE), table->table_name);
+ 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;
@@ -2557,8 +4586,8 @@ send_result_message:
default: // Probably HA_ADMIN_INTERNAL_ERROR
{
- char buf[ERRMSGSIZE+20];
- uint length=my_snprintf(buf, ERRMSGSIZE,
+ 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);
@@ -2578,24 +4607,28 @@ send_result_message:
else
{
pthread_mutex_lock(&LOCK_open);
- remove_table_from_cache(thd, table->table->s->db,
- table->table->s->table_name, RTFC_NO_FLAG);
+ remove_table_from_cache(thd, table->table->s->db.str,
+ table->table->s->table_name.str, RTFC_NO_FLAG);
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);
- lex->reset_query_tables_list(FALSE);
table->table=0; // For query cache
if (protocol->write())
goto err;
}
- send_eof(thd);
+ my_eof(thd);
DBUG_RETURN(FALSE);
- err:
+
+err:
+ ha_autocommit_or_rollback(thd, 1);
+ end_trans(thd, ROLLBACK);
close_thread_tables(thd); // Shouldn't be needed
if (table)
table->table=0;
@@ -2606,19 +4639,23 @@ send_result_message:
bool mysql_backup_table(THD* thd, TABLE_LIST* table_list)
{
DBUG_ENTER("mysql_backup_table");
+ WARN_DEPRECATED(thd, "5.2", "BACKUP TABLE",
+ "MySQL Administrator (mysqldump, mysql)");
DBUG_RETURN(mysql_admin_table(thd, table_list, 0,
"backup", TL_READ, 0, 0, 0, 0,
- &handler::backup, 0));
+ &handler::ha_backup, 0));
}
bool mysql_restore_table(THD* thd, TABLE_LIST* table_list)
{
DBUG_ENTER("mysql_restore_table");
+ WARN_DEPRECATED(thd, "5.2", "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::restore, 0));
+ &handler::ha_restore, 0));
}
@@ -2639,7 +4676,7 @@ 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::optimize, 0));
+ &handler::ha_optimize, 0));
}
@@ -2734,12 +4771,66 @@ int reassign_keycache_tables(THD *thd, KEY_CACHE *src_cache,
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, 0, 0, 0, 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);
+}
+
+
/*
Create a table identical to the specified table
@@ -2749,104 +4840,78 @@ bool mysql_preload_keys(THD* thd, TABLE_LIST* tables)
table Table list element for target table
src_table Table list element for source table
create_info Create info
- table_ident Src table_ident
RETURN VALUES
FALSE OK
TRUE error
*/
-bool mysql_create_like_table(THD* thd, TABLE_LIST* table, TABLE_LIST *src_table,
+bool mysql_create_like_table(THD* thd, TABLE_LIST* table, TABLE_LIST* src_table,
HA_CREATE_INFO *create_info)
{
- TABLE **tmp_table;
+ TABLE *name_lock= 0;
char src_path[FN_REFLEN], dst_path[FN_REFLEN];
+ uint dst_path_length;
char *db= table->db;
char *table_name= table->table_name;
int err;
bool res= TRUE;
- db_type not_used;
+ uint not_used;
+#ifdef WITH_PARTITION_STORAGE_ENGINE
+ char tmp_path[FN_REFLEN];
+#endif
+ char ts_name[FN_LEN];
DBUG_ENTER("mysql_create_like_table");
+
/*
- By taking name-lock on the source table and holding LOCK_open mutex we
- ensure that no concurrent DDL operation will mess with this table. Note
- that holding only name-lock is not enough for this, because it won't block
- other DDL statements that only take name-locks on the table and don't
- open it (simple name-locks are not exclusive between each other).
-
- Unfortunately, simply opening this table is not enough for our purproses,
- since in 5.0 ALTER TABLE may change .FRM files on disk even if there are
- connections that still have old version of table open. This 'optimization'
- was removed in 5.1 so there we open the source table instead of taking
- name-lock on it.
-
- We also have to acquire LOCK_open to make copying of .frm file, call to
- ha_create_table() and binlogging atomic against concurrent DML and DDL
- operations on the target 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.
*/
- if (lock_and_wait_for_table_name(thd, src_table))
- DBUG_RETURN(res);
-
- pthread_mutex_lock(&LOCK_open);
-
- if ((tmp_table= find_temporary_table(thd, src_table->db,
- src_table->table_name)))
- strxmov(src_path, (*tmp_table)->s->path, reg_ext, NullS);
- else
- {
- char *tablename_pos= strxmov(src_path, mysql_data_home, "/", NullS);
- strxmov(tablename_pos, src_table->db, "/", src_table->table_name,
- reg_ext, NullS);
- if (lower_case_table_names)
- my_casedn_str(files_charset_info, tablename_pos);
- /* Resolve symlinks (for windows) */
- fn_format(src_path, src_path, "", "", MYF(MY_UNPACK_FILENAME));
- if (access(src_path, F_OK))
- {
- my_error(ER_BAD_TABLE_ERROR, MYF(0), src_table->table_name);
- goto err;
- }
- }
+ if (open_tables(thd, &src_table, &not_used, 0))
+ DBUG_RETURN(TRUE);
- /*
- create like should be not allowed for Views, Triggers, ...
+ /*
+ 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 (mysql_frm_type(thd, src_path, &not_used) != FRMTYPE_TABLE)
+ if ((src_table->table->file->get_tablespace_name(thd, ts_name, FN_LEN)))
{
- my_error(ER_WRONG_OBJECT, MYF(0), src_table->db, src_table->table_name,
- "BASE TABLE");
- goto err;
+ create_info->tablespace= ts_name;
+ create_info->storage_media= HA_SM_DISK;
}
+ strxmov(src_path, src_table->table->s->path.str, reg_ext, NullS);
+
DBUG_EXECUTE_IF("sleep_create_like_before_check_if_exists", my_sleep(6000000););
/*
- Validate the destination table
-
- skip the destination table name checking as this is already
- validated.
+ 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 (find_temporary_table(thd, db, table_name))
goto table_exists;
- set_tmp_file_path(dst_path, sizeof(dst_path), thd);
+ dst_path_length= build_tmptable_filename(thd, dst_path, sizeof(dst_path));
create_info->table_options|= HA_CREATE_DELAY_KEY_WRITE;
}
else
{
- strxmov(dst_path, mysql_data_home, "/", db, "/", table_name,
- reg_ext, NullS);
- fn_format(dst_path, dst_path, "", "", MYF(MY_UNPACK_FILENAME));
-
- /*
- Note that starting from 5.1 we obtain name-lock on target
- table instead of inspecting table cache for presence
- of open placeholders (see comment in mysql_create_table()).
- */
- if (table_cache_has_open_placeholder(thd, db, table_name) ||
- !access(dst_path, F_OK))
+ 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),
+ db, table_name, reg_ext, 0);
+ if (!access(dst_path, F_OK))
goto table_exists;
}
@@ -2854,27 +4919,62 @@ bool mysql_create_like_table(THD* thd, TABLE_LIST* table, TABLE_LIST *src_table,
/*
Create a new table by copying from source table
+
+ 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.
*/
- if (my_copy(src_path, dst_path, MYF(MY_DONT_OVERWRITE_FILE)))
+ 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, MYF(MY_DONT_OVERWRITE_FILE)))
{
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;
}
- DBUG_EXECUTE_IF("sleep_create_like_before_ha_create", my_sleep(6000000););
-
/*
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).
*/
- *fn_ext(dst_path)= 0;
+#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.
+ There is no way to find out here if the original table is a
+ partitioned table so we copy the file and ignore any errors.
+ */
+ 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(dst_path, create_info, 1);
+ err= ha_create_table(thd, dst_path, db, table_name, create_info, 1);
+ VOID(pthread_mutex_unlock(&LOCK_open));
if (create_info->options & HA_LEX_CREATE_TMP_TABLE)
{
@@ -2884,23 +4984,77 @@ bool mysql_create_like_table(THD* thd, TABLE_LIST* table, TABLE_LIST *src_table,
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); /* purecov: inspected */
+ table_name, 0); /* purecov: inspected */
goto err; /* purecov: inspected */
}
DBUG_EXECUTE_IF("sleep_create_like_before_binlogging", my_sleep(6000000););
- // Must be written before unlock
- if (mysql_bin_log.is_open())
+ /*
+ We have to write the query before we unlock the tables.
+ */
+ if (thd->current_stmt_binlog_row_based)
{
- thd->clear_error();
- Query_log_event qinfo(thd, thd->query, thd->query_length, FALSE, FALSE);
- mysql_bin_log.write(&qinfo);
+ /*
+ Since temporary tables are not replicated under row-based
+ replication, CREATE TABLE ... LIKE ... needs special
+ treatement. We have four cases to consider, according to the
+ following decision table:
+
+ ==== ========= ========= ==============================
+ Case Target Source Write to binary log
+ ==== ========= ========= ==============================
+ 1 normal normal Original statement
+ 2 normal temporary Generated statement
+ 3 temporary normal Nothing
+ 4 temporary temporary Nothing
+ ==== ========= ========= ==============================
+ */
+ if (!(create_info->options & HA_LEX_CREATE_TMP_TABLE))
+ {
+ if (src_table->table->s->tmp_table) // Case 2
+ {
+ char buf[2048];
+ String query(buf, sizeof(buf), system_charset_info);
+ query.length(0); // Have to zero it since constructor doesn't
+
+ /*
+ 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.
+ */
+ 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));
+
+ IF_DBUG(int result=)
+ store_create_info(thd, table, &query,
+ create_info, FALSE /* show_database */);
+
+ DBUG_ASSERT(result == 0); // store_create_info() always return 0
+ write_bin_log(thd, TRUE, query.ptr(), query.length());
+ }
+ else // Case 1
+ write_bin_log(thd, TRUE, thd->query, thd->query_length);
+ }
+ /*
+ Case 3 and 4 does nothing under RBR
+ */
}
+ else
+ write_bin_log(thd, TRUE, thd->query, thd->query_length);
+
res= FALSE;
goto err;
@@ -2918,34 +5072,30 @@ table_exists:
my_error(ER_TABLE_EXISTS_ERROR, MYF(0), table_name);
err:
- unlock_table_name(thd, src_table);
- pthread_mutex_unlock(&LOCK_open);
+ 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)
{
-#ifdef OS2
- thr_lock_type lock_type = TL_WRITE;
-#else
thr_lock_type lock_type = TL_READ_NO_INSERT;
-#endif
DBUG_ENTER("mysql_analyze_table");
DBUG_RETURN(mysql_admin_table(thd, tables, check_opt,
"analyze", lock_type, 1, 0, 0, 0,
- &handler::analyze, 0));
+ &handler::ha_analyze, 0));
}
bool mysql_check_table(THD* thd, TABLE_LIST* tables,HA_CHECK_OPT* check_opt)
{
-#ifdef OS2
- thr_lock_type lock_type = TL_WRITE;
-#else
thr_lock_type lock_type = TL_READ_NO_INSERT;
-#endif
DBUG_ENTER("mysql_check_table");
DBUG_RETURN(mysql_admin_table(thd, tables, check_opt,
@@ -2971,7 +5121,7 @@ mysql_discard_or_import_tablespace(THD *thd,
ALTER TABLE
*/
- thd->proc_info="discard_or_import_tablespace";
+ thd_proc_info(thd, "discard_or_import_tablespace");
discard= test(tablespace_op == DISCARD_TABLESPACE);
@@ -2980,15 +5130,15 @@ mysql_discard_or_import_tablespace(THD *thd,
not complain when we lock the table
*/
thd->tablespace_op= TRUE;
- if (!(table=open_ltable(thd,table_list,TL_WRITE)))
+ if (!(table=open_ltable(thd, table_list, TL_WRITE, 0)))
{
thd->tablespace_op=FALSE;
DBUG_RETURN(-1);
}
- error=table->file->discard_or_import_tablespace(discard);
+ error= table->file->ha_discard_or_import_tablespace(discard);
- thd->proc_info="end";
+ thd_proc_info(thd, "end");
if (error)
goto err;
@@ -3000,23 +5150,20 @@ mysql_discard_or_import_tablespace(THD *thd,
query_cache_invalidate3(thd, table_list, 0);
/* The ALTER TABLE is always in its own transaction */
- error = ha_commit_stmt(thd);
- if (ha_commit(thd))
+ error = ha_autocommit_or_rollback(thd, 0);
+ if (end_active_trans(thd))
error=1;
if (error)
goto err;
- if (mysql_bin_log.is_open())
- {
- Query_log_event qinfo(thd, thd->query, thd->query_length, FALSE, FALSE);
- mysql_bin_log.write(&qinfo);
- }
+ write_bin_log(thd, FALSE, thd->query, thd->query_length);
+
err:
- close_thread_tables(thd);
+ ha_autocommit_or_rollback(thd, error);
thd->tablespace_op=FALSE;
if (error == 0)
{
- send_ok(thd);
+ my_ok(thd);
DBUG_RETURN(0);
}
@@ -3027,352 +5174,460 @@ err:
/*
- Manages enabling/disabling of indexes for ALTER TABLE
-
SYNOPSIS
- alter_table_manage_keys()
- table Target table
- indexes_were_disabled Whether the indexes of the from table
- were disabled
- keys_onoff ENABLE | DISABLE | LEAVE_AS_IS
+ compare_tables()
+ table The original table.
+ alter_info Alter options, fields and keys for the new
+ table.
+ create_info Create options for the new table.
+ order_num Number of order list elements.
+ need_copy_table OUT Result of the comparison. Undefined if error.
+ Otherwise is one of:
+ ALTER_TABLE_METADATA_ONLY No copy needed
+ ALTER_TABLE_DATA_CHANGED Data changes,
+ copy needed
+ ALTER_TABLE_INDEX_CHANGED Index changes,
+ copy might be needed
+ key_info_buffer OUT An array of KEY structs for new indexes
+ index_drop_buffer OUT An array of offsets into table->key_info.
+ index_drop_count OUT The number of elements in the array.
+ index_add_buffer OUT An array of offsets into key_info_buffer.
+ index_add_count OUT The number of elements in the array.
+ candidate_key_count OUT The number of candidate keys in original table.
+
+ DESCRIPTION
+ 'table' (first argument) contains information of the original
+ table, which includes all corresponding parts that the new
+ table has in arguments create_list, key_list and create_info.
+
+ By comparing the changes between the original and new table
+ we can determine how much it has changed after ALTER TABLE
+ and whether we need to make a copy of the table, or just change
+ the .frm file.
+
+ If there are no data changes, but index changes, 'index_drop_buffer'
+ and/or 'index_add_buffer' are populated with offsets into
+ table->key_info or key_info_buffer respectively for the indexes
+ that need to be dropped and/or (re-)created.
RETURN VALUES
- FALSE OK
- TRUE Error
+ TRUE error
+ FALSE success
*/
static
-bool alter_table_manage_keys(TABLE *table, int indexes_were_disabled,
- enum enum_enable_or_disable keys_onoff)
+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)
{
- int error= 0;
- DBUG_ENTER("alter_table_manage_keys");
- DBUG_PRINT("enter", ("table=%p were_disabled=%d on_off=%d",
- table, indexes_were_disabled, keys_onoff));
-
- switch (keys_onoff) {
- case ENABLE:
- error= table->file->enable_indexes(HA_KEY_SWITCH_NONUNIQ_SAVE);
- break;
- case LEAVE_AS_IS:
- if (!indexes_were_disabled)
- break;
- /* fall-through: disabled indexes */
- case DISABLE:
- error= table->file->disable_indexes(HA_KEY_SWITCH_NONUNIQ_SAVE);
- }
-
- if (error == HA_ERR_WRONG_COMMAND)
- {
- push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
- ER_ILLEGAL_HA, ER(ER_ILLEGAL_HA), table->s->table_name);
- error= 0;
- } else if (error)
- table->file->print_error(error, MYF(0));
+ Field **f_ptr, *field;
+ uint changes= 0, tmp;
+ uint key_count;
+ List_iterator_fast<Create_field> new_field_it, tmp_new_field_it;
+ Create_field *new_field, *tmp_new_field;
+ KEY_PART_INFO *key_part;
+ KEY_PART_INFO *end;
+ THD *thd= table->in_use;
+ /*
+ Remember if the new definition has new VARCHAR column;
+ create_info->varchar will be reset in mysql_prepare_create_table.
+ */
+ bool varchar= create_info->varchar;
+ bool not_nullable= true;
+ DBUG_ENTER("compare_tables");
- DBUG_RETURN(error);
-}
+ /*
+ Create a copy of alter_info.
+ To compare the new and old table definitions, we need to "prepare"
+ the new definition - transform it from parser output to a format
+ that describes the final table layout (all column defaults are
+ initialized, duplicate columns are removed). This is done by
+ mysql_prepare_create_table. Unfortunately,
+ mysql_prepare_create_table performs its transformations
+ "in-place", that is, modifies the argument. Since we would
+ like to keep 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
+ destroy the copy.
+ */
+ Alter_info tmp_alter_info(*alter_info, thd->mem_root);
+ uint db_options= 0; /* not used */
+ /* Create the prepared information. */
+ if (mysql_prepare_create_table(thd, create_info,
+ &tmp_alter_info,
+ (table->s->tmp_table != NO_TMP_TABLE),
+ &db_options,
+ table->file, key_info_buffer,
+ &key_count, 0))
+ DBUG_RETURN(1);
+ /* Allocate result buffers. */
+ if (! (*index_drop_buffer=
+ (uint*) thd->alloc(sizeof(uint) * table->s->keys)) ||
+ ! (*index_add_buffer=
+ (uint*) thd->alloc(sizeof(uint) * tmp_alter_info.key_list.elements)))
+ DBUG_RETURN(1);
+
+ /*
+ Some very basic checks. If number of fields changes, or the
+ handler, we need to run full ALTER TABLE. In the future
+ new fields can be added and old dropped without copy, but
+ not yet.
+ Test also that engine was not given during ALTER TABLE, or
+ we are force to run regular alter table (copy).
+ E.g. ALTER TABLE tbl_name ENGINE=MyISAM.
-/*
- Alter table
+ For the following ones we also want to run regular alter table:
+ ALTER TABLE tbl_name ORDER BY ..
+ ALTER TABLE tbl_name CONVERT TO CHARACTER SET ..
+ At the moment we can't handle altering temporary tables without a copy.
+ We also test if OPTIMIZE TABLE was given and was mapped to alter table.
+ In that case we always do full copy.
- NOTE
- The structures passed as 'create_info' and 'alter_info' parameters may
- be modified by this function. It is responsibility of the caller to make
- a copy of create_info in order to provide correct execution in prepared
- statements/stored routines.
-*/
+ There was a bug prior to mysql-4.0.25. Number of null fields was
+ calculated incorrectly. As a result frm and data files gets out of
+ sync after fast alter table. There is no way to determine by which
+ mysql version (in 4.0 and 4.1 branches) table was created, thus we
+ disable fast alter table for all tables created by mysql versions
+ prior to 5.0 branch.
+ See BUG#6236.
+ */
+ if (table->s->fields != alter_info->create_list.elements ||
+ table->s->db_type() != create_info->db_type ||
+ table->s->tmp_table ||
+ create_info->used_fields & HA_CREATE_USED_ENGINE ||
+ create_info->used_fields & HA_CREATE_USED_CHARSET ||
+ create_info->used_fields & HA_CREATE_USED_DEFAULT_CHARSET ||
+ create_info->used_fields & HA_CREATE_USED_ROW_FORMAT ||
+ create_info->used_fields & HA_CREATE_USED_PACK_KEYS ||
+ create_info->used_fields & HA_CREATE_USED_MAX_ROWS ||
+ (alter_info->flags & (ALTER_RECREATE | ALTER_FOREIGN_KEY)) ||
+ order_num ||
+ !table->s->mysql_version ||
+ (table->s->frm_version < FRM_VER_TRUE_VARCHAR && varchar))
+ {
+ *need_copy_table= ALTER_TABLE_DATA_CHANGED;
+ DBUG_RETURN(0);
+ }
-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)
-{
- TABLE *table,*new_table=0;
- int error= 0;
- char tmp_name[80],old_name[32],new_name_buff[FN_REFLEN];
- char new_alias_buff[FN_REFLEN], *table_name, *db, *new_alias, *alias;
- char index_file[FN_REFLEN], data_file[FN_REFLEN];
- ha_rows copied,deleted;
- ulonglong next_insert_id;
- uint db_create_options, used_fields;
- enum db_type old_db_type, new_db_type, table_type;
- bool need_copy_table;
- bool no_table_reopen= FALSE, varchar= FALSE;
- frm_type_enum frm_type;
/*
- Throw an error if the table to be altered isn't empty.
- Used in DATE/DATETIME fields default value checking.
+ Use transformed info to evaluate possibility of fast ALTER TABLE
+ but use the preserved field to persist modifications.
*/
- bool error_if_not_empty= FALSE;
+ new_field_it.init(alter_info->create_list);
+ tmp_new_field_it.init(tmp_alter_info.create_list);
+
/*
- A field used for error reporting in DATE/DATETIME fields default
- value checking.
+ Go through fields and check if the original ones are compatible
+ with new table.
*/
- create_field *new_datetime_field= 0;
- DBUG_ENTER("mysql_alter_table");
+ for (f_ptr= table->field, new_field= new_field_it++,
+ tmp_new_field= tmp_new_field_it++;
+ (field= *f_ptr);
+ f_ptr++, new_field= new_field_it++,
+ tmp_new_field= tmp_new_field_it++)
+ {
+ /* Make sure we have at least the default charset in use. */
+ if (!new_field->charset)
+ new_field->charset= create_info->default_table_charset;
- thd->proc_info="init";
- table_name=table_list->table_name;
- alias= (lower_case_table_names == 2) ? table_list->alias : table_name;
+ /* Check that NULL behavior is same for old and new fields */
+ if ((tmp_new_field->flags & NOT_NULL_FLAG) !=
+ (uint) (field->flags & NOT_NULL_FLAG))
+ {
+ *need_copy_table= ALTER_TABLE_DATA_CHANGED;
+ DBUG_RETURN(0);
+ }
- db=table_list->db;
- if (!new_db || !my_strcasecmp(table_alias_charset, new_db, db))
- new_db= db;
- used_fields=create_info->used_fields;
-
- mysql_ha_flush(thd, table_list, MYSQL_HA_CLOSE_FINAL, FALSE);
+ /* Don't pack rows in old tables if the user has requested this. */
+ if (create_info->row_type == ROW_TYPE_DYNAMIC ||
+ (tmp_new_field->flags & BLOB_FLAG) ||
+ tmp_new_field->sql_type == MYSQL_TYPE_VARCHAR &&
+ create_info->row_type != ROW_TYPE_FIXED)
+ create_info->table_options|= HA_OPTION_PACK_RECORD;
- /* 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));
- sprintf(new_name_buff,"%s/%s/%s%s",mysql_data_home, db, table_name, reg_ext);
- unpack_filename(new_name_buff, new_name_buff);
- frm_type= mysql_frm_type(thd, new_name_buff, &table_type);
- /* Rename a view */
- if (frm_type == FRMTYPE_VIEW && !(alter_info->flags & ~ALTER_RENAME))
+ /* Check if field was renamed */
+ field->flags&= ~FIELD_IS_RENAMED;
+ if (my_strcasecmp(system_charset_info,
+ field->field_name,
+ tmp_new_field->field_name))
+ field->flags|= FIELD_IS_RENAMED;
+
+ /* Evaluate changes bitmap and send to check_if_incompatible_data() */
+ if (!(tmp= field->is_equal(tmp_new_field)))
+ {
+ *need_copy_table= ALTER_TABLE_DATA_CHANGED;
+ DBUG_RETURN(0);
+ }
+ // Clear indexed marker
+ field->flags&= ~FIELD_IN_ADD_INDEX;
+ changes|= tmp;
+ }
+
+ /*
+ Go through keys and check if the original ones are compatible
+ with new table.
+ */
+ KEY *table_key;
+ KEY *table_key_end= table->key_info + table->s->keys;
+ KEY *new_key;
+ KEY *new_key_end= *key_info_buffer + key_count;
+
+ DBUG_PRINT("info", ("index count old: %d new: %d",
+ table->s->keys, key_count));
+ /*
+ Step through all keys of the old table and search matching new keys.
+ */
+ *index_drop_count= 0;
+ *index_add_count= 0;
+ *candidate_key_count= 0;
+ for (table_key= table->key_info; table_key < table_key_end; table_key++)
{
- /*
- 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
+ KEY_PART_INFO *table_part;
+ KEY_PART_INFO *table_part_end= table_key->key_part + table_key->key_parts;
+ KEY_PART_INFO *new_part;
+
+ /*
+ Check if key is a candidate key, i.e. a unique index with no index
+ fields nullable, then key is either already primary key or could
+ be promoted to primary key if the original primary key is dropped.
+ Count all candidate keys.
*/
-
- if (thd->locked_tables || thd->active_transaction())
+ not_nullable= true;
+ for (table_part= table_key->key_part;
+ table_part < table_part_end;
+ table_part++)
{
- my_message(ER_LOCK_OR_ACTIVE_TRANSACTION,
- ER(ER_LOCK_OR_ACTIVE_TRANSACTION), MYF(0));
- DBUG_RETURN(1);
+ not_nullable= not_nullable && (! table_part->field->maybe_null());
}
+ if ((table_key->flags & HA_NOSAME) && not_nullable)
+ (*candidate_key_count)++;
- if (wait_if_global_read_lock(thd,0,1))
- DBUG_RETURN(1);
- VOID(pthread_mutex_lock(&LOCK_open));
- if (lock_table_names(thd, table_list))
+ /* Search a new key with the same name. */
+ for (new_key= *key_info_buffer; new_key < new_key_end; new_key++)
{
- error= 1;
- goto view_err;
+ if (! strcmp(table_key->name, new_key->name))
+ break;
}
-
- if (!do_rename(thd, table_list, new_db, new_name, new_name, 1))
+ if (new_key >= new_key_end)
{
- if (mysql_bin_log.is_open())
- {
- thd->clear_error();
- Query_log_event qinfo(thd, thd->query, thd->query_length, 0, FALSE);
- mysql_bin_log.write(&qinfo);
- }
- send_ok(thd);
+ /* Key not found. Add the offset of the key to the drop buffer. */
+ (*index_drop_buffer)[(*index_drop_count)++]= table_key - table->key_info;
+ DBUG_PRINT("info", ("index dropped: '%s'", table_key->name));
+ continue;
}
- unlock_table_names(thd, table_list, (TABLE_LIST*) 0);
+ /* Check that the key types are compatible between old and new tables. */
+ if ((table_key->algorithm != new_key->algorithm) ||
+ ((table_key->flags & HA_KEYFLAG_MASK) !=
+ (new_key->flags & HA_KEYFLAG_MASK)) ||
+ (table_key->key_parts != new_key->key_parts))
+ goto index_changed;
-view_err:
- pthread_mutex_unlock(&LOCK_open);
- start_waiting_global_read_lock(thd);
- DBUG_RETURN(error);
+ /*
+ Check that the key parts remain compatible between the old and
+ new tables.
+ */
+ for (table_part= table_key->key_part, new_part= new_key->key_part;
+ table_part < table_part_end;
+ table_part++, new_part++)
+ {
+ /*
+ Key definition has changed if we are using a different field or
+ if the used key part length is different. We know that the fields
+ did not change. Comparing field numbers is sufficient.
+ */
+ if ((table_part->length != new_part->length) ||
+ (table_part->fieldnr - 1 != new_part->fieldnr))
+ goto index_changed;
+ }
+ continue;
+
+ index_changed:
+ /* Key modified. Add the offset of the key to both buffers. */
+ (*index_drop_buffer)[(*index_drop_count)++]= table_key - table->key_info;
+ (*index_add_buffer)[(*index_add_count)++]= new_key - *key_info_buffer;
+ key_part= new_key->key_part;
+ end= key_part + new_key->key_parts;
+ for(; key_part != end; key_part++)
+ {
+ // Mark field to be part of new key
+ field= table->field[key_part->fieldnr];
+ field->flags|= FIELD_IN_ADD_INDEX;
+ }
+ DBUG_PRINT("info", ("index changed: '%s'", table_key->name));
}
- if (!(table=open_ltable(thd,table_list,TL_WRITE_ALLOW_READ)))
- DBUG_RETURN(TRUE);
+ /*end of for (; table_key < table_key_end;) */
- /* Check that we are not trying to rename to an existing table */
- if (new_name)
+ /*
+ Step through all keys of the new table and find matching old keys.
+ */
+ for (new_key= *key_info_buffer; new_key < new_key_end; new_key++)
{
- strmov(new_name_buff,new_name);
- strmov(new_alias= new_alias_buff, new_name);
- if (lower_case_table_names)
+ /* Search an old key with the same name. */
+ for (table_key= table->key_info; table_key < table_key_end; table_key++)
{
- if (lower_case_table_names != 2)
- {
- my_casedn_str(files_charset_info, new_name_buff);
- new_alias= new_name; // Create lower case table name
- }
- my_casedn_str(files_charset_info, new_name);
- }
- if (new_db == db &&
- !my_strcasecmp(table_alias_charset, new_name_buff, table_name))
- {
- /*
- Source and destination table names are equal: make later check
- easier.
- */
- new_alias= new_name= table_name;
+ if (! strcmp(table_key->name, new_key->name))
+ break;
}
- else
+ if (table_key >= table_key_end)
{
- if (table->s->tmp_table)
+ /* Key not found. Add the offset of the key to the add buffer. */
+ (*index_add_buffer)[(*index_add_count)++]= new_key - *key_info_buffer;
+ key_part= new_key->key_part;
+ end= key_part + new_key->key_parts;
+ for(; key_part != end; key_part++)
{
- if (find_temporary_table(thd,new_db,new_name_buff))
- {
- my_error(ER_TABLE_EXISTS_ERROR, MYF(0), new_name_buff);
- DBUG_RETURN(TRUE);
- }
- }
- else
- {
- char dir_buff[FN_REFLEN];
- bool exists;
- strxnmov(dir_buff, FN_REFLEN, mysql_real_data_home, new_db, NullS);
- VOID(pthread_mutex_lock(&LOCK_open));
- exists= (table_cache_has_open_placeholder(thd, new_db, new_name) ||
- !access(fn_format(new_name_buff, new_name_buff, dir_buff,
- reg_ext, 0), F_OK));
- VOID(pthread_mutex_unlock(&LOCK_open));
- if (exists)
- {
- /* Table will be closed in do_command() */
- my_error(ER_TABLE_EXISTS_ERROR, MYF(0), new_alias);
- DBUG_RETURN(TRUE);
- }
+ // Mark field to be part of new key
+ field= table->field[key_part->fieldnr];
+ field->flags|= FIELD_IN_ADD_INDEX;
}
+ DBUG_PRINT("info", ("index added: '%s'", new_key->name));
}
}
- else
+
+ /* Check if changes are compatible with current handler without a copy */
+ if (table->file->check_if_incompatible_data(create_info, changes))
{
- new_alias= (lower_case_table_names == 2) ? alias : table_name;
- new_name= table_name;
+ *need_copy_table= ALTER_TABLE_DATA_CHANGED;
+ DBUG_RETURN(0);
}
- old_db_type= table->s->db_type;
- if (create_info->db_type == DB_TYPE_DEFAULT)
- create_info->db_type= old_db_type;
- if (check_engine(thd, new_name, &create_info->db_type))
- DBUG_RETURN(TRUE);
- new_db_type= create_info->db_type;
- if (create_info->row_type == ROW_TYPE_NOT_USED)
- create_info->row_type= table->s->row_type;
-
- DBUG_PRINT("info", ("old type: %d new type: %d", old_db_type, new_db_type));
- if (ha_check_storage_engine_flag(old_db_type, HTON_ALTER_NOT_SUPPORTED) ||
- ha_check_storage_engine_flag(new_db_type, HTON_ALTER_NOT_SUPPORTED))
+ if (*index_drop_count || *index_add_count)
{
- DBUG_PRINT("info", ("doesn't support alter"));
- my_error(ER_ILLEGAL_HA, MYF(0), table_name);
- DBUG_RETURN(TRUE);
+ *need_copy_table= ALTER_TABLE_INDEX_CHANGED;
+ DBUG_RETURN(0);
}
-
- thd->proc_info="setup";
- if (!(alter_info->flags & ~(ALTER_RENAME | ALTER_KEYS_ONOFF)) &&
- !table->s->tmp_table) // no need to touch frm
- {
- switch (alter_info->keys_onoff) {
- 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, HA_EXTRA_FORCE_REOPEN);
- VOID(pthread_mutex_unlock(&LOCK_open));
- error= table->file->enable_indexes(HA_KEY_SWITCH_NONUNIQ_SAVE);
- /* COND_refresh will be signaled in close_thread_tables() */
- break;
- case DISABLE:
- VOID(pthread_mutex_lock(&LOCK_open));
- wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN);
- VOID(pthread_mutex_unlock(&LOCK_open));
- error=table->file->disable_indexes(HA_KEY_SWITCH_NONUNIQ_SAVE);
- /* COND_refresh will be signaled in close_thread_tables() */
- break;
- }
- if (error == HA_ERR_WRONG_COMMAND)
- {
- error= 0;
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
- ER_ILLEGAL_HA, ER(ER_ILLEGAL_HA),
- table->alias);
- }
- VOID(pthread_mutex_lock(&LOCK_open));
- /*
- 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.
- */
+ *need_copy_table= ALTER_TABLE_METADATA_ONLY; // Tables are compatible
+ DBUG_RETURN(0);
+}
- if (!error && (new_name != table_name || new_db != db))
- {
- thd->proc_info="rename";
- /*
- Then do a 'simple' rename of the table. First we need to close all
- instances of 'source' table.
- */
- close_cached_table(thd, table);
- /*
- Then, we want check once again that target table does not exist.
- Note that we can't fully rely on results of previous check since
- no lock was taken on target table during it. We also can't do this
- before calling close_cached_table() as the latter temporarily
- releases LOCK_open mutex.
- Also note that starting from 5.1 we use approach with obtaining
- of name-lock on target table.
- */
- if (table_cache_has_open_placeholder(thd, new_db, new_name) ||
- !access(new_name_buff,F_OK))
- {
- my_error(ER_TABLE_EXISTS_ERROR, MYF(0), new_name);
- error= -1;
- }
- else
- {
- *fn_ext(new_name)=0;
- if (mysql_rename_table(old_db_type,db,table_name,new_db,new_alias))
- error= -1;
- else if (Table_triggers_list::change_table_name(thd, db, table_name,
- new_db, new_alias))
- {
- VOID(mysql_rename_table(old_db_type, new_db, new_alias, db,
- table_name));
- error= -1;
- }
- }
- }
- if (error == HA_ERR_WRONG_COMMAND)
- {
- error= 0;
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
- ER_ILLEGAL_HA, ER(ER_ILLEGAL_HA),
- table->alias);
- }
+/*
+ Manages enabling/disabling of indexes for ALTER TABLE
- if (!error)
- {
- if (mysql_bin_log.is_open())
- {
- thd->clear_error();
- Query_log_event qinfo(thd, thd->query, thd->query_length, FALSE, FALSE);
- mysql_bin_log.write(&qinfo);
- }
- send_ok(thd);
- }
- else if (error > 0)
- {
- table->file->print_error(error, MYF(0));
- error= -1;
- }
- VOID(pthread_mutex_unlock(&LOCK_open));
- table_list->table= NULL; // For query cache
- query_cache_invalidate3(thd, table_list, 0);
- DBUG_RETURN(error);
+ SYNOPSIS
+ alter_table_manage_keys()
+ table Target table
+ indexes_were_disabled Whether the indexes of the from table
+ were disabled
+ keys_onoff ENABLE | DISABLE | LEAVE_AS_IS
+
+ RETURN VALUES
+ FALSE OK
+ TRUE Error
+*/
+
+static
+bool alter_table_manage_keys(TABLE *table, int indexes_were_disabled,
+ enum enum_enable_or_disable keys_onoff)
+{
+ int error= 0;
+ DBUG_ENTER("alter_table_manage_keys");
+ DBUG_PRINT("enter", ("table=%p were_disabled=%d on_off=%d",
+ table, indexes_were_disabled, keys_onoff));
+
+ switch (keys_onoff) {
+ case ENABLE:
+ error= table->file->ha_enable_indexes(HA_KEY_SWITCH_NONUNIQ_SAVE);
+ break;
+ case LEAVE_AS_IS:
+ if (!indexes_were_disabled)
+ break;
+ /* fall-through: disabled indexes */
+ case DISABLE:
+ error= table->file->ha_disable_indexes(HA_KEY_SWITCH_NONUNIQ_SAVE);
}
- /* Full alter table */
+ if (error == HA_ERR_WRONG_COMMAND)
+ {
+ push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
+ ER_ILLEGAL_HA, ER(ER_ILLEGAL_HA),
+ table->s->table_name.str);
+ error= 0;
+ } else if (error)
+ table->file->print_error(error, MYF(0));
+
+ DBUG_RETURN(error);
+}
+
+/**
+ Prepare column and key definitions for CREATE TABLE in ALTER TABLE.
+
+ This function transforms parse output of ALTER TABLE - lists of
+ columns and keys to add, drop or modify into, essentially,
+ CREATE TABLE definition - a list of columns and keys of the new
+ table. While doing so, it also performs some (bug not all)
+ 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
+ is not possible, perhaps because the ALTER statement contains
+ instructions that require change in table data, not only in
+ table definition or indexes.
+
+ @param[in,out] thd thread handle. Used as a memory pool
+ and source of environment information.
+ @param[in] table the source table, open and locked
+ Used as an interface to the storage engine
+ to acquire additional information about
+ the original table.
+ @param[in,out] create_info A blob with CREATE/ALTER TABLE
+ parameters
+ @param[in,out] alter_info Another blob with ALTER/CREATE parameters.
+ Originally create_info was used only in
+ CREATE TABLE and alter_info only in ALTER TABLE.
+ But since ALTER might end-up doing CREATE,
+ this distinction is gone and we just carry
+ around two structures.
+
+ @return
+ Fills various create_info members based on information retrieved
+ from the storage engine.
+ Sets create_info->varchar if the table has a VARCHAR column.
+ Prepares alter_info->create_list and alter_info->key_list with
+ columns and keys of the new table.
+ @retval TRUE error, out of memory or a semantical error in ALTER
+ TABLE instructions
+ @retval FALSE success
+*/
+
+static bool
+mysql_prepare_alter_table(THD *thd, TABLE *table,
+ HA_CREATE_INFO *create_info,
+ Alter_info *alter_info)
+{
+ /* New column definitions are added here */
+ List<Create_field> new_create_list;
+ /* New key definitions are added here */
+ List<Key> new_key_list;
+ List_iterator<Alter_drop> drop_it(alter_info->drop_list);
+ List_iterator<Create_field> def_it(alter_info->create_list);
+ List_iterator<Alter_column> alter_it(alter_info->alter_list);
+ List_iterator<Key> key_it(alter_info->key_list);
+ List_iterator<Create_field> find_it(new_create_list);
+ List_iterator<Create_field> field_it(new_create_list);
+ List<Key_part_spec> key_parts;
+ uint db_create_options= (table->s->db_create_options
+ & ~(HA_OPTION_PACK_RECORD));
+ uint used_fields= create_info->used_fields;
+ KEY *key_info=table->key_info;
+ bool rc= TRUE;
+
+ DBUG_ENTER("mysql_prepare_alter_table");
+
+ create_info->varchar= FALSE;
/* Let new create options override the old ones */
if (!(used_fields & HA_CREATE_USED_MIN_ROWS))
create_info->min_rows= table->s->min_rows;
@@ -3386,25 +5641,35 @@ view_err:
{
/* Table has an autoincrement, copy value to new table */
table->file->info(HA_STATUS_AUTO);
- create_info->auto_increment_value= table->file->auto_increment_value;
+ create_info->auto_increment_value= table->file->stats.auto_increment_value;
}
+ if (!(used_fields & HA_CREATE_USED_KEY_BLOCK_SIZE))
+ create_info->key_block_size= table->s->key_block_size;
+ if (!(used_fields & HA_CREATE_USED_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));
+ /*
+ 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
- List_iterator<Alter_drop> drop_it(alter_info->drop_list);
- List_iterator<create_field> def_it(alter_info->create_list);
- List_iterator<Alter_column> alter_it(alter_info->alter_list);
- Alter_info new_info; // Add new columns and indexes here
- create_field *def;
+ Create_field *def;
/*
First collect all fields from table which isn't in drop_list
*/
-
Field **f_ptr,*field;
for (f_ptr=table->field ; (field= *f_ptr) ; f_ptr++)
{
if (field->type() == MYSQL_TYPE_STRING)
- varchar= TRUE;
+ create_info->varchar= TRUE;
/* Check if field should be dropped */
Alter_drop *drop;
drop_it.rewind();
@@ -3441,13 +5706,18 @@ view_err:
def->field=field;
if (!def->after)
{
- new_info.create_list.push_back(def);
+ new_create_list.push_back(def);
def_it.remove();
}
}
else
- { // Use old field value
- new_info.create_list.push_back(def= new create_field(field, field));
+ {
+ /*
+ This field was not dropped and not changed, add it to the list
+ for the new table.
+ */
+ def= new Create_field(field, field);
+ new_create_list.push_back(def);
alter_it.rewind(); // Change default if ALTER
Alter_column *alter;
while ((alter=alter_it++))
@@ -3457,10 +5727,10 @@ view_err:
}
if (alter)
{
- if (def->sql_type == FIELD_TYPE_BLOB)
+ if (def->sql_type == MYSQL_TYPE_BLOB)
{
my_error(ER_BLOB_CANT_HAVE_DEFAULT, MYF(0), def->change);
- DBUG_RETURN(TRUE);
+ goto err;
}
if ((def->def=alter->def)) // Use new default
def->flags&= ~NO_DEFAULT_VALUE_FLAG;
@@ -3471,13 +5741,12 @@ view_err:
}
}
def_it.rewind();
- List_iterator<create_field> find_it(new_info.create_list);
while ((def=def_it++)) // Add new columns
{
if (def->change && ! def->field)
{
- my_error(ER_BAD_FIELD_ERROR, MYF(0), def->change, table_name);
- DBUG_RETURN(TRUE);
+ my_error(ER_BAD_FIELD_ERROR, MYF(0), def->change, table->s->table_name.str);
+ goto err;
}
/*
Check that the DATE/DATETIME not null field we are going to add is
@@ -3488,20 +5757,21 @@ view_err:
*/
if ((def->sql_type == MYSQL_TYPE_DATE ||
def->sql_type == MYSQL_TYPE_NEWDATE ||
- def->sql_type == MYSQL_TYPE_DATETIME) && !new_datetime_field &&
+ def->sql_type == MYSQL_TYPE_DATETIME) &&
+ !alter_info->datetime_field &&
!(~def->flags & (NO_DEFAULT_VALUE_FLAG | NOT_NULL_FLAG)) &&
thd->variables.sql_mode & MODE_NO_ZERO_DATE)
{
- new_datetime_field= def;
- error_if_not_empty= TRUE;
+ alter_info->datetime_field= def;
+ alter_info->error_if_not_empty= TRUE;
}
if (!def->after)
- new_info.create_list.push_back(def);
+ new_create_list.push_back(def);
else if (def->after == first_keyword)
- new_info.create_list.push_front(def);
+ new_create_list.push_front(def);
else
{
- create_field *find;
+ Create_field *find;
find_it.rewind();
while ((find=find_it++)) // Add new columns
{
@@ -3510,23 +5780,24 @@ view_err:
}
if (!find)
{
- my_error(ER_BAD_FIELD_ERROR, MYF(0), def->after, table_name);
- DBUG_RETURN(TRUE);
+ my_error(ER_BAD_FIELD_ERROR, MYF(0), def->after, table->s->table_name.str);
+ goto err;
}
find_it.after(def); // Put element after this
+ alter_info->change_level= ALTER_TABLE_DATA_CHANGED;
}
}
if (alter_info->alter_list.elements)
{
my_error(ER_BAD_FIELD_ERROR, MYF(0),
- alter_info->alter_list.head()->name, table_name);
- DBUG_RETURN(TRUE);
+ alter_info->alter_list.head()->name, table->s->table_name.str);
+ goto err;
}
- if (!new_info.create_list.elements)
+ if (!new_create_list.elements)
{
my_message(ER_CANT_REMOVE_ALL_FIELDS, ER(ER_CANT_REMOVE_ALL_FIELDS),
MYF(0));
- DBUG_RETURN(TRUE);
+ goto err;
}
/*
@@ -3534,11 +5805,6 @@ view_err:
for which some fields exists.
*/
- List_iterator<Key> key_it(alter_info->key_list);
- List_iterator<create_field> field_it(new_info.create_list);
- List<key_part_spec> key_parts;
-
- KEY *key_info=table->key_info;
for (uint i=0 ; i < table->s->keys ; i++,key_info++)
{
char *key_name= key_info->name;
@@ -3563,7 +5829,7 @@ view_err:
if (!key_part->field)
continue; // Wrong field (from UNIREG)
const char *key_part_name=key_part->field->field_name;
- create_field *cfield;
+ Create_field *cfield;
field_it.rewind();
while ((cfield=field_it++))
{
@@ -3596,6 +5862,8 @@ view_err:
*/
if (!Field::type_can_have_key_part(cfield->field->type()) ||
!Field::type_can_have_key_part(cfield->sql_type) ||
+ /* spatial keys can't have sub-key length */
+ (key_info->flags & HA_SPATIAL) ||
(cfield->field->field_length == key_part_length &&
!f_is_blob(key_part->key_type)) ||
(cfield->length && (cfield->length < key_part_length /
@@ -3603,13 +5871,21 @@ view_err:
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,
+ key_parts.push_back(new Key_part_spec(cfield->field_name,
key_part_length));
}
if (key_parts.elements)
{
+ KEY_CREATE_INFO key_create_info;
Key *key;
enum Key::Keytype key_type;
+ bzero((char*) &key_create_info, sizeof(key_create_info));
+
+ key_create_info.algorithm= key_info->algorithm;
+ if (key_info->flags & HA_USES_BLOCK_SIZE)
+ key_create_info.block_size= key_info->block_size;
+ if (key_info->flags & HA_USES_PARSER)
+ key_create_info.parser_name= *plugin_name(key_info->parser);
if (key_info->flags & HA_SPATIAL)
key_type= Key::SPATIAL;
@@ -3626,10 +5902,10 @@ view_err:
key_type= Key::MULTIPLE;
key= new Key(key_type, key_name,
- key_info->algorithm,
+ &key_create_info,
test(key_info->flags & HA_GENERATED_KEY),
key_parts);
- new_info.key_list.push_back(key);
+ new_key_list.push_back(key);
}
}
{
@@ -3637,12 +5913,12 @@ view_err:
while ((key=key_it++)) // Add new keys
{
if (key->type != Key::FOREIGN_KEY)
- new_info.key_list.push_back(key);
+ new_key_list.push_back(key);
if (key->name &&
!my_strcasecmp(system_charset_info,key->name,primary_key_name))
{
my_error(ER_WRONG_NAME_FOR_INDEX, MYF(0), key->name);
- DBUG_RETURN(TRUE);
+ goto err;
}
}
}
@@ -3660,17 +5936,6 @@ view_err:
goto err;
}
- db_create_options= table->s->db_create_options & ~(HA_OPTION_PACK_RECORD);
- my_snprintf(tmp_name, sizeof(tmp_name), "%s-%lx_%lx", tmp_file_prefix,
- current_pid, thd->thread_id);
- /* Safety fix for innodb */
- if (lower_case_table_names)
- my_casedn_str(files_charset_info, tmp_name);
- if (new_db_type != old_db_type && !table->file->can_switch_engines()) {
- my_error(ER_ROW_IS_REFERENCED, MYF(0));
- goto err;
- }
- create_info->db_type=new_db_type;
if (!create_info->comment.str)
{
create_info->comment.str= table->s->comment.str;
@@ -3694,32 +5959,702 @@ view_err:
if (table->s->tmp_table)
create_info->options|=HA_LEX_CREATE_TMP_TABLE;
+ rc= FALSE;
+ alter_info->create_list.swap(new_create_list);
+ alter_info->key_list.swap(new_key_list);
+err:
+ DBUG_RETURN(rc);
+}
+
+
+/*
+ Alter table
+
+ SYNOPSIS
+ mysql_alter_table()
+ thd Thread handle
+ new_db If there is a RENAME clause
+ new_name If there is a RENAME clause
+ create_info Information from the parsing phase about new
+ table properties.
+ table_list The table to change.
+ alter_info Lists of fields, keys to be changed, added
+ or dropped.
+ order_num How many ORDER BY fields has been specified.
+ order List of fields to ORDER BY.
+ ignore Whether we have ALTER IGNORE TABLE
+
+ DESCRIPTION
+ This is a veery long function and is everything but the kitchen sink :)
+ It is used to alter a table and not only by ALTER TABLE but also
+ CREATE|DROP INDEX are mapped on this function.
+
+ When the ALTER TABLE statement just does a RENAME or ENABLE|DISABLE KEYS,
+ or both, then this function short cuts its operation by renaming
+ the table and/or enabling/disabling the keys. In this case, the FRM is
+ not changed, directly by mysql_alter_table. However, if there is a
+ RENAME + change of a field, or an index, the short cut is not used.
+ See how `create_list` is used to generate the new FRM regarding the
+ structure of the fields. The same is done for the indices of the table.
+
+ Important is the fact, that this function tries to do as little work as
+ possible, by finding out whether a intermediate table is needed to copy
+ data into and when finishing the altering to use it as the original table.
+ For this reason the function compare_tables() is called, which decides
+ based on all kind of data how similar are the new and the original
+ tables.
+
+ RETURN VALUES
+ FALSE OK
+ TRUE Error
+*/
+
+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)
+{
+ TABLE *table, *new_table= 0, *name_lock= 0;
+ int error= 0;
+ char tmp_name[80],old_name[32],new_name_buff[FN_REFLEN];
+ char new_alias_buff[FN_REFLEN], *table_name, *db, *new_alias, *alias;
+ char index_file[FN_REFLEN], data_file[FN_REFLEN];
+ char path[FN_REFLEN];
+ 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;
+ bool partition_changed= FALSE;
+#endif
+ bool need_lock_for_indexes= TRUE;
+ KEY *key_info_buffer;
+ uint index_drop_count;
+ uint *index_drop_buffer;
+ uint index_add_count;
+ uint *index_add_buffer;
+ uint candidate_key_count;
+ bool committed= 0;
+ bool no_pk;
+ DBUG_ENTER("mysql_alter_table");
+
+ LINT_INIT(index_add_count);
+ LINT_INIT(index_drop_count);
+ LINT_INIT(index_add_buffer);
+ LINT_INIT(index_drop_buffer);
+ LINT_INIT(candidate_key_count);
+
+ /*
+ Check if we attempt to alter mysql.slow_log or
+ mysql.general_log table and return an error if
+ it is the case.
+ TODO: this design is obsolete and will be removed.
+ */
+ if (table_list && table_list->db && table_list->table_name)
+ {
+ int table_kind= 0;
+
+ table_kind= check_if_log_table(table_list->db_length, table_list->db,
+ table_list->table_name_length,
+ table_list->table_name, 0);
+
+ if (table_kind)
+ {
+ /* Disable alter of enabled log tables */
+ if (logger.is_log_table_enabled(table_kind))
+ {
+ my_error(ER_BAD_LOG_STATEMENT, MYF(0), "ALTER");
+ DBUG_RETURN(TRUE);
+ }
+
+ /* Disable alter of log tables to unsupported engine */
+ if ((create_info->used_fields & HA_CREATE_USED_ENGINE) &&
+ (!create_info->db_type || /* unknown engine */
+ !(create_info->db_type->flags & HTON_SUPPORT_LOG_TABLES)))
+ {
+ my_error(ER_UNSUPORTED_LOG_ENGINE, MYF(0));
+ DBUG_RETURN(TRUE);
+ }
+
+#ifdef WITH_PARTITION_STORAGE_ENGINE
+ if (alter_info->flags & ALTER_PARTITION)
+ {
+ my_error(ER_WRONG_USAGE, MYF(0), "PARTITION", "log table");
+ DBUG_RETURN(TRUE);
+ }
+#endif
+ }
+ }
+
+ /*
+ Assign variables table_name, new_name, db, new_db, path, reg_path
+ to simplify further comparisions: we want to see if it's a RENAME
+ later just by comparing the pointers, avoiding the need for strcmp.
+ */
+ thd_proc_info(thd, "init");
+ table_name=table_list->table_name;
+ alias= (lower_case_table_names == 2) ? table_list->alias : table_name;
+ db=table_list->db;
+ if (!new_db || !my_strcasecmp(table_alias_charset, new_db, db))
+ new_db= db;
+ build_table_filename(reg_path, sizeof(reg_path), db, table_name, reg_ext, 0);
+ build_table_filename(path, sizeof(path), db, table_name, "", 0);
+
+ mysql_ha_rm_tables(thd, table_list, FALSE);
+
+ /* 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))
+ {
+ /*
+ 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
+ */
+
+ 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);
+ }
+
+ 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);
+ mysql_bin_log.write(&qinfo);
+ }
+ my_ok(thd);
+ }
+
+ unlock_table_names(thd, table_list, (TABLE_LIST*) 0);
+
+view_err:
+ pthread_mutex_unlock(&LOCK_open);
+ start_waiting_global_read_lock(thd);
+ DBUG_RETURN(error);
+ }
+
+ if (!(table= open_n_lock_single_table(thd, table_list, TL_WRITE_ALLOW_READ)))
+ DBUG_RETURN(TRUE);
+ table->use_all_columns();
+
+ /*
+ Prohibit changing of the UNION list of a non-temporary MERGE table
+ under LOCK tables. It would be quite difficult to reuse a shrinked
+ 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 &&
+ (create_info->used_fields & HA_CREATE_USED_UNION) &&
+ (table->s->tmp_table == NO_TMP_TABLE))
+ {
+ my_error(ER_LOCK_OR_ACTIVE_TRANSACTION, MYF(0));
+ DBUG_RETURN(TRUE);
+ }
+
+ /* Check that we are not trying to rename to an existing table */
+ if (new_name)
+ {
+ DBUG_PRINT("info", ("new_db.new_name: '%s'.'%s'", new_db, new_name));
+ strmov(new_name_buff,new_name);
+ strmov(new_alias= new_alias_buff, new_name);
+ if (lower_case_table_names)
+ {
+ if (lower_case_table_names != 2)
+ {
+ my_casedn_str(files_charset_info, new_name_buff);
+ new_alias= new_name; // Create lower case table name
+ }
+ my_casedn_str(files_charset_info, new_name);
+ }
+ if (new_db == db &&
+ !my_strcasecmp(table_alias_charset, new_name_buff, table_name))
+ {
+ /*
+ Source and destination table names are equal: make later check
+ easier.
+ */
+ new_alias= new_name= table_name;
+ }
+ else
+ {
+ if (table->s->tmp_table != NO_TMP_TABLE)
+ {
+ if (find_temporary_table(thd,new_db,new_name_buff))
+ {
+ my_error(ER_TABLE_EXISTS_ERROR, MYF(0), new_name_buff);
+ DBUG_RETURN(TRUE);
+ }
+ }
+ else
+ {
+ if (lock_table_name_if_not_cached(thd, new_db, new_name, &name_lock))
+ DBUG_RETURN(TRUE);
+ if (!name_lock)
+ {
+ my_error(ER_TABLE_EXISTS_ERROR, MYF(0), new_alias);
+ DBUG_RETURN(TRUE);
+ }
+
+ build_table_filename(new_name_buff, sizeof(new_name_buff),
+ new_db, new_name_buff, reg_ext, 0);
+ if (!access(new_name_buff, F_OK))
+ {
+ /* Table will be closed in do_command() */
+ my_error(ER_TABLE_EXISTS_ERROR, MYF(0), new_alias);
+ goto err;
+ }
+ }
+ }
+ }
+ else
+ {
+ new_alias= (lower_case_table_names == 2) ? alias : table_name;
+ new_name= table_name;
+ }
+
+ old_db_type= table->s->db_type();
+ if (!create_info->db_type)
+ {
+#ifdef WITH_PARTITION_STORAGE_ENGINE
+ if (table->part_info &&
+ create_info->used_fields & HA_CREATE_USED_ENGINE)
+ {
+ /*
+ This case happens when the user specified
+ ENGINE = x where x is a non-existing storage engine
+ We set create_info->db_type to default_engine_type
+ to ensure we don't change underlying engine type
+ due to a erroneously given engine name.
+ */
+ create_info->db_type= table->part_info->default_engine_type;
+ }
+ else
+#endif
+ create_info->db_type= old_db_type;
+ }
+
+ if (check_engine(thd, new_name, create_info))
+ goto err;
+ new_db_type= create_info->db_type;
+
+ if ((new_db_type != old_db_type ||
+ alter_info->flags & ALTER_PARTITION) &&
+ !table->file->can_switch_engines())
+ {
+ my_error(ER_ROW_IS_REFERENCED, MYF(0));
+ goto err;
+ }
+
+ if (create_info->row_type == ROW_TYPE_NOT_USED)
+ create_info->row_type= table->s->row_type;
+
+ DBUG_PRINT("info", ("old type: %s new type: %s",
+ ha_resolve_storage_engine_name(old_db_type),
+ ha_resolve_storage_engine_name(new_db_type)));
+ if (ha_check_storage_engine_flag(old_db_type, HTON_ALTER_NOT_SUPPORTED) ||
+ ha_check_storage_engine_flag(new_db_type, HTON_ALTER_NOT_SUPPORTED))
+ {
+ DBUG_PRINT("info", ("doesn't support alter"));
+ my_error(ER_ILLEGAL_HA, MYF(0), table_name);
+ goto err;
+ }
+
+ thd_proc_info(thd, "setup");
+ if (!(alter_info->flags & ~(ALTER_RENAME | ALTER_KEYS_ONOFF)) &&
+ !table->s->tmp_table) // no need to touch frm
+ {
+ switch (alter_info->keys_onoff) {
+ 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, HA_EXTRA_FORCE_REOPEN);
+ VOID(pthread_mutex_unlock(&LOCK_open));
+ DBUG_EXECUTE_IF("sleep_alter_enable_indexes", my_sleep(6000000););
+ error= table->file->ha_enable_indexes(HA_KEY_SWITCH_NONUNIQ_SAVE);
+ /* COND_refresh will be signaled in close_thread_tables() */
+ break;
+ case DISABLE:
+ VOID(pthread_mutex_lock(&LOCK_open));
+ wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN);
+ VOID(pthread_mutex_unlock(&LOCK_open));
+ error=table->file->ha_disable_indexes(HA_KEY_SWITCH_NONUNIQ_SAVE);
+ /* COND_refresh will be signaled in close_thread_tables() */
+ break;
+ default:
+ DBUG_ASSERT(FALSE);
+ error= 0;
+ break;
+ }
+ if (error == HA_ERR_WRONG_COMMAND)
+ {
+ error= 0;
+ push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
+ ER_ILLEGAL_HA, ER(ER_ILLEGAL_HA),
+ table->alias);
+ }
+
+ VOID(pthread_mutex_lock(&LOCK_open));
+ /*
+ 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");
+ /*
+ Then do a 'simple' rename of the table. First we need to close all
+ instances of 'source' table.
+ */
+ close_cached_table(thd, table);
+ /*
+ 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.
+ TODO: Investigate if we need this access() check at all.
+ */
+ if (!access(new_name_buff,F_OK))
+ {
+ my_error(ER_TABLE_EXISTS_ERROR, MYF(0), new_name);
+ error= -1;
+ }
+ else
+ {
+ *fn_ext(new_name)=0;
+ if (mysql_rename_table(old_db_type,db,table_name,new_db,new_alias, 0))
+ error= -1;
+ else if (Table_triggers_list::change_table_name(thd, db, table_name,
+ new_db, new_alias))
+ {
+ VOID(mysql_rename_table(old_db_type, new_db, new_alias, db,
+ table_name, 0));
+ error= -1;
+ }
+ }
+ }
+
+ if (error == HA_ERR_WRONG_COMMAND)
+ {
+ error= 0;
+ push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
+ ER_ILLEGAL_HA, ER(ER_ILLEGAL_HA),
+ table->alias);
+ }
+
+ if (!error)
+ {
+ write_bin_log(thd, TRUE, thd->query, thd->query_length);
+ my_ok(thd);
+ }
+ else if (error > 0)
+ {
+ 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);
+ DBUG_RETURN(error);
+ }
+
+ /* We have to do full alter table. */
+
+#ifdef WITH_PARTITION_STORAGE_ENGINE
+ if (prep_alter_part_table(thd, table, alter_info, create_info, old_db_type,
+ &partition_changed, &fast_alter_partition))
+ goto err;
+#endif
+ /*
+ If the old table had partitions and we are doing ALTER TABLE ...
+ engine= <new_engine>, the new table must preserve the original
+ partitioning. That means that the new engine is still the
+ partitioning engine, not the engine specified in the parser.
+ This is discovered in prep_alter_part_table, which in such case
+ updates create_info->db_type.
+ Now we need to update the stack copy of create_info->db_type,
+ as otherwise we won't be able to correctly move the files of the
+ temporary table to the result table files.
+ */
+ new_db_type= create_info->db_type;
+
+ if (mysql_prepare_alter_table(thd, table, create_info, alter_info))
+ goto err;
+
+ need_copy_table= alter_info->change_level;
+
+ set_table_default_charset(thd, create_info, db);
+
+ if (thd->variables.old_alter_table
+ || (table->s->db_type() != create_info->db_type)
+#ifdef WITH_PARTITION_STORAGE_ENGINE
+ || partition_changed
+#endif
+ )
+ need_copy_table= ALTER_TABLE_DATA_CHANGED;
+ else
+ {
+ 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))
+ goto err;
+
+ if (need_copy_table == ALTER_TABLE_METADATA_ONLY)
+ need_copy_table= need_copy_table_res;
+ }
+
+ /*
+ If there are index changes only, try to do them online. "Index
+ changes only" means also that the handler for the table does not
+ change. The table is open and locked. The handler can be accessed.
+ */
+ if (need_copy_table == ALTER_TABLE_INDEX_CHANGED)
+ {
+ int pk_changed= 0;
+ ulong alter_flags= 0;
+ ulong needed_online_flags= 0;
+ ulong needed_fast_flags= 0;
+ KEY *key;
+ uint *idx_p;
+ uint *idx_end_p;
+
+ alter_flags= table->file->alter_table_flags(alter_info->flags);
+ DBUG_PRINT("info", ("alter_flags: %lu", alter_flags));
+ /* Check dropped indexes. */
+ for (idx_p= index_drop_buffer, idx_end_p= idx_p + index_drop_count;
+ idx_p < idx_end_p;
+ idx_p++)
+ {
+ key= table->key_info + *idx_p;
+ DBUG_PRINT("info", ("index dropped: '%s'", key->name));
+ if (key->flags & HA_NOSAME)
+ {
+ /*
+ Unique key. Check for "PRIMARY".
+ or if dropping last unique key
+ */
+ if ((uint) (key - table->key_info) == table->s->primary_key)
+ {
+ DBUG_PRINT("info", ("Dropping primary key"));
+ /* Primary key. */
+ needed_online_flags|= HA_ONLINE_DROP_PK_INDEX;
+ needed_fast_flags|= HA_ONLINE_DROP_PK_INDEX_NO_WRITES;
+ pk_changed++;
+ candidate_key_count--;
+ }
+ else
+ {
+ KEY_PART_INFO *part_end= key->key_part + key->key_parts;
+ bool is_candidate_key= true;
+
+ /* Non-primary unique key. */
+ needed_online_flags|= HA_ONLINE_DROP_UNIQUE_INDEX;
+ needed_fast_flags|= HA_ONLINE_DROP_UNIQUE_INDEX_NO_WRITES;
+
+ /*
+ Check if all fields in key are declared
+ NOT NULL and adjust candidate_key_count
+ */
+ for (KEY_PART_INFO *key_part= key->key_part;
+ key_part < part_end;
+ key_part++)
+ is_candidate_key=
+ (is_candidate_key &&
+ (! table->field[key_part->fieldnr-1]->maybe_null()));
+ if (is_candidate_key)
+ candidate_key_count--;
+ }
+ }
+ else
+ {
+ /* Non-unique key. */
+ needed_online_flags|= HA_ONLINE_DROP_INDEX;
+ needed_fast_flags|= HA_ONLINE_DROP_INDEX_NO_WRITES;
+ }
+ }
+ no_pk= ((table->s->primary_key == MAX_KEY) ||
+ (needed_online_flags & HA_ONLINE_DROP_PK_INDEX));
+ /* Check added indexes. */
+ for (idx_p= index_add_buffer, idx_end_p= idx_p + index_add_count;
+ idx_p < idx_end_p;
+ idx_p++)
+ {
+ key= key_info_buffer + *idx_p;
+ DBUG_PRINT("info", ("index added: '%s'", key->name));
+ if (key->flags & HA_NOSAME)
+ {
+ /* Unique key */
+
+ KEY_PART_INFO *part_end= key->key_part + key->key_parts;
+ bool is_candidate_key= true;
+
+ /*
+ Check if all fields in key are declared
+ NOT NULL
+ */
+ for (KEY_PART_INFO *key_part= key->key_part;
+ key_part < part_end;
+ key_part++)
+ is_candidate_key=
+ (is_candidate_key &&
+ (! table->field[key_part->fieldnr]->maybe_null()));
+
+ /*
+ Check for "PRIMARY"
+ or if adding first unique key
+ defined on non-nullable fields
+ */
+
+ if ((!my_strcasecmp(system_charset_info,
+ key->name, primary_key_name)) ||
+ (no_pk && candidate_key_count == 0 && is_candidate_key))
+ {
+ DBUG_PRINT("info", ("Adding primary key"));
+ /* Primary key. */
+ needed_online_flags|= HA_ONLINE_ADD_PK_INDEX;
+ needed_fast_flags|= HA_ONLINE_ADD_PK_INDEX_NO_WRITES;
+ 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;
+ }
+ }
+ else
+ {
+ /* Non-unique key. */
+ needed_online_flags|= HA_ONLINE_ADD_INDEX;
+ needed_fast_flags|= HA_ONLINE_ADD_INDEX_NO_WRITES;
+ }
+ }
+
+ if ((candidate_key_count > 0) &&
+ (needed_online_flags & HA_ONLINE_DROP_PK_INDEX))
+ {
+ /*
+ 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;
+ pk_changed= 2;
+ }
+
+ DBUG_PRINT("info", ("needed_online_flags: 0x%lx, needed_fast_flags: 0x%lx",
+ needed_online_flags, needed_fast_flags));
+ /*
+ Online or fast 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.
+ */
+ if ( pk_changed < 2 )
+ {
+ if ((alter_flags & needed_online_flags) == needed_online_flags)
+ {
+ /* All required online flags are present. */
+ need_copy_table= ALTER_TABLE_METADATA_ONLY;
+ need_lock_for_indexes= FALSE;
+ }
+ else if ((alter_flags & needed_fast_flags) == needed_fast_flags)
+ {
+ /* All required fast flags are present. */
+ need_copy_table= ALTER_TABLE_METADATA_ONLY;
+ }
+ }
+ DBUG_PRINT("info", ("need_copy_table: %u need_lock: %d",
+ need_copy_table, need_lock_for_indexes));
+ }
+
/*
better have a negative test here, instead of positive, like
alter_info->flags & ALTER_ADD_COLUMN|ALTER_ADD_INDEX|...
so that ALTER TABLE won't break when somebody will add new flag
+ */
+ if (need_copy_table == ALTER_TABLE_METADATA_ONLY)
+ create_info->frm_only= 1;
- MySQL uses frm version to determine the type of the data fields and
- their layout. See Field_string::type() for details.
- Thus, if the table is too old we may have to rebuild the data to
- update the layout.
+#ifdef WITH_PARTITION_STORAGE_ENGINE
+ if (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));
+ }
+#endif
- There was a bug prior to mysql-4.0.25. Number of null fields was
- calculated incorrectly. As a result frm and data files gets out of
- sync after fast alter table. There is no way to determine by which
- mysql version (in 4.0 and 4.1 branches) table was created, thus we
- disable fast alter table for all tables created by mysql versions
- prior to 5.0 branch.
- See BUG#6236.
- */
- need_copy_table= (alter_info->flags &
- ~(ALTER_CHANGE_COLUMN_DEFAULT|ALTER_OPTIONS) ||
- (create_info->used_fields &
- ~(HA_CREATE_USED_COMMENT|HA_CREATE_USED_PASSWORD)) ||
- table->s->tmp_table ||
- !table->s->mysql_version ||
- (table->s->frm_version < FRM_VER_TRUE_VARCHAR && varchar));
- create_info->frm_only= !need_copy_table;
+ my_snprintf(tmp_name, sizeof(tmp_name), "%s-%lx_%lx", tmp_file_prefix,
+ current_pid, thd->thread_id);
+ /* Safety fix for innodb */
+ if (lower_case_table_names)
+ my_casedn_str(files_charset_info, tmp_name);
/*
Handling of symlinked tables:
@@ -3736,8 +6671,8 @@ view_err:
old data and index files. Create also symlinks to point at
the new tables.
Copy data.
- At end, rename temporary tables and symlinks to temporary table
- to final table name.
+ At end, rename intermediate tables, and symlinks to intermediate
+ table, to final table name.
Remove old table and old symlinks
If rename is made to another database:
@@ -3745,7 +6680,6 @@ view_err:
Copy data.
Remove old table and symlinks.
*/
-
if (!strcmp(db, new_db)) // Ignore symlink if db changed
{
if (create_info->index_file_name)
@@ -3768,16 +6702,23 @@ view_err:
else
create_info->data_file_name=create_info->index_file_name=0;
- /* We don't log the statement, it will be logged later. */
- {
- tmp_disable_binlog(thd);
- error= mysql_create_table(thd, new_db, tmp_name,
- create_info, &new_info, 1, 0);
- reenable_binlog(thd);
- if (error)
- DBUG_RETURN(error);
- }
- if (need_copy_table)
+ /*
+ Create a table with a temporary name.
+ With create_info->frm_only == 1 this creates a .frm file only.
+ We don't log the statement, it will be logged later.
+ */
+ tmp_disable_binlog(thd);
+ error= mysql_create_table_no_lock(thd, new_db, tmp_name,
+ create_info,
+ alter_info,
+ 1, 0);
+ reenable_binlog(thd);
+ if (error)
+ goto err;
+
+ /* Open the table if we need to copy the data. */
+ DBUG_PRINT("info", ("need_copy_table: %u", need_copy_table));
+ if (need_copy_table != ALTER_TABLE_METADATA_ONLY)
{
if (table->s->tmp_table)
{
@@ -3785,66 +6726,155 @@ view_err:
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);
}
else
{
char path[FN_REFLEN];
- my_snprintf(path, sizeof(path), "%s/%s/%s", mysql_data_home,
- new_db, tmp_name);
- fn_format(path,path,"","",4);
+ /* table is a normal table: Create temporary table in same directory */
+ build_table_filename(path, sizeof(path), new_db, tmp_name, "",
+ FN_IS_TMP);
+ /* Open our intermediate table */
new_table=open_temporary_table(thd, path, new_db, tmp_name,0);
}
if (!new_table)
- {
- VOID(quick_rm_table(new_db_type,new_db,tmp_name));
- goto err;
- }
+ goto err1;
+ /*
+ Note: In case of MERGE table, we do not attach children. We do not
+ copy data for MERGE tables. Only the children have data.
+ */
}
- /* We don't want update TIMESTAMP fields during ALTER TABLE. */
+ /* Copy the data if necessary. */
thd->count_cuted_fields= CHECK_FIELD_WARN; // calc cuted fields
thd->cuted_fields=0L;
- thd->proc_info="copy to tmp table";
- next_insert_id=thd->next_insert_id; // Remember for logging
copied=deleted=0;
- if (new_table && !new_table->s->is_view)
+ /*
+ We do not copy data for MERGE tables. Only the children have data.
+ MERGE tables have HA_NO_COPY_ON_ALTER set.
+ */
+ if (new_table && !(new_table->file->ha_table_flags() & HA_NO_COPY_ON_ALTER))
{
+ /* 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;
- error= copy_data_between_tables(table, new_table, new_info.create_list,
- ignore, order_num, order,
- &copied, &deleted, alter_info->keys_onoff,
- error_if_not_empty);
+ thd_proc_info(thd, "copy to tmp table");
+ error= copy_data_between_tables(table, new_table,
+ alter_info->create_list, ignore,
+ order_num, order, &copied, &deleted,
+ alter_info->keys_onoff,
+ alter_info->error_if_not_empty);
}
- else if (!new_table)
+ else
{
VOID(pthread_mutex_lock(&LOCK_open));
wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN);
VOID(pthread_mutex_unlock(&LOCK_open));
+ thd_proc_info(thd, "manage keys");
alter_table_manage_keys(table, table->file->indexes_are_disabled(),
alter_info->keys_onoff);
- error= ha_commit_stmt(thd);
- if (ha_commit(thd))
+ error= ha_autocommit_or_rollback(thd, 0);
+ if (end_active_trans(thd))
error= 1;
}
-
- thd->last_insert_id=next_insert_id; // Needed for correct log
thd->count_cuted_fields= CHECK_FIELD_IGNORE;
- if (table->s->tmp_table)
+ /* If we did not need to copy, we might still need to add/drop indexes. */
+ if (! new_table)
{
- /* We changed a temporary table */
- if (error)
+ uint *key_numbers;
+ uint *keyno_p;
+ KEY *key_info;
+ KEY *key;
+ uint *idx_p;
+ uint *idx_end_p;
+ KEY_PART_INFO *key_part;
+ KEY_PART_INFO *part_end;
+ DBUG_PRINT("info", ("No new_table, checking add/drop index"));
+
+ table->file->ha_prepare_for_alter();
+ if (index_add_count)
+ {
+ /* The add_index() method takes an array of KEY structs. */
+ key_info= (KEY*) thd->alloc(sizeof(KEY) * index_add_count);
+ key= key_info;
+ for (idx_p= index_add_buffer, idx_end_p= idx_p + index_add_count;
+ idx_p < idx_end_p;
+ idx_p++, key++)
+ {
+ /* Copy the KEY struct. */
+ *key= key_info_buffer[*idx_p];
+ /* Fix the key parts. */
+ part_end= key->key_part + key->key_parts;
+ for (key_part= key->key_part; key_part < part_end; key_part++)
+ key_part->field= table->field[key_part->fieldnr];
+ }
+ /* Add the indexes. */
+ if ((error= table->file->add_index(table, key_info, index_add_count)))
+ {
+ /*
+ Exchange the key_info for the error message. If we exchange
+ key number by key name in the message later, we need correct info.
+ */
+ KEY *save_key_info= table->key_info;
+ table->key_info= key_info;
+ table->file->print_error(error, MYF(0));
+ table->key_info= save_key_info;
+ goto err1;
+ }
+ }
+ /*end of if (index_add_count)*/
+
+ if (index_drop_count)
{
+ /* The prepare_drop_index() method takes an array of key numbers. */
+ key_numbers= (uint*) thd->alloc(sizeof(uint) * index_drop_count);
+ keyno_p= key_numbers;
+ /* Get the number of each key. */
+ for (idx_p= index_drop_buffer, idx_end_p= idx_p + index_drop_count;
+ idx_p < idx_end_p;
+ idx_p++, keyno_p++)
+ *keyno_p= *idx_p;
/*
- The following function call will free the new_table pointer,
- in close_temporary_table(), so we can safely directly jump to err
+ Tell the handler to prepare for drop indexes.
+ This re-numbers the indexes to get rid of gaps.
*/
- close_temporary_table(thd, new_db, tmp_name);
- goto err;
+ if ((error= table->file->prepare_drop_index(table, key_numbers,
+ index_drop_count)))
+ {
+ table->file->print_error(error, MYF(0));
+ goto err1;
+ }
+
+ /* Tell the handler to finally drop the indexes. */
+ if ((error= table->file->final_drop_index(table)))
+ {
+ table->file->print_error(error, MYF(0));
+ goto err1;
+ }
}
+ /*end of if (index_drop_count)*/
+
+ /*
+ The final .frm file is already created as a temporary file
+ and will be renamed to the original table name later.
+ */
+
+ /* Need to commit before a table is unlocked (NDB requirement). */
+ DBUG_PRINT("info", ("Committing before unlocking table"));
+ if (ha_autocommit_or_rollback(thd, 0) || end_active_trans(thd))
+ goto err1;
+ committed= 1;
+ }
+ /*end of if (! new_table) for add/drop index*/
+
+ if (table->s->tmp_table != NO_TMP_TABLE)
+ {
+ /* We changed a temporary table */
+ if (error)
+ goto err1;
/* Close lock if this is a transactional table */
if (thd->lock)
{
@@ -3852,194 +6882,235 @@ view_err:
thd->lock=0;
}
/* Remove link to old table and rename the new one */
- close_temporary_table(thd, table->s->db, table_name);
+ 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))
- { // Fatal error
- close_temporary_table(thd,new_db,tmp_name);
- my_free((gptr) new_table,MYF(0));
- goto err;
- }
- /*
- Writing to the binlog does not need to be synchronized for temporary tables,
- which are thread-specific.
- */
- if (mysql_bin_log.is_open())
- {
- thd->clear_error();
- Query_log_event qinfo(thd, thd->query, thd->query_length, FALSE, FALSE);
- mysql_bin_log.write(&qinfo);
- }
+ goto err1;
+ /* We don't replicate alter table statement on temporary tables */
+ if (!thd->current_stmt_binlog_row_based)
+ write_bin_log(thd, TRUE, thd->query, thd->query_length);
goto end_temporary;
}
if (new_table)
{
- intern_close_table(new_table); /* close temporary table */
- my_free((gptr) new_table,MYF(0));
+ /*
+ 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));
}
VOID(pthread_mutex_lock(&LOCK_open));
if (error)
{
- VOID(quick_rm_table(new_db_type,new_db,tmp_name));
+ VOID(quick_rm_table(new_db_type, new_db, tmp_name, FN_IS_TMP));
VOID(pthread_mutex_unlock(&LOCK_open));
goto err;
}
/*
- Data is copied. Now we rename the old table to a temp name,
- rename the new one to the old name, remove all entries from the old table
- from the cache, free all locks, close the old table and remove it.
+ Data is copied. Now we:
+ 1) Wait until all other threads close old version of table.
+ 2) Close instances of table open by this thread and replace them
+ with exclusive name-locks.
+ 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.
*/
- thd->proc_info="rename result table";
+ thd_proc_info(thd, "rename result table");
my_snprintf(old_name, sizeof(old_name), "%s2-%lx-%lx", tmp_file_prefix,
current_pid, thd->thread_id);
if (lower_case_table_names)
my_casedn_str(files_charset_info, old_name);
-#if (!defined( __WIN__) && !defined( __EMX__) && !defined( OS2))
- if (table->file->has_transactions())
-#endif
- {
- /*
- Win32 and InnoDB can't drop a table that is in use, so we must
- close the original table at before doing the rename
- */
- close_cached_table(thd, table);
- table=0; // Marker that table is closed
- no_table_reopen= TRUE;
- }
-#if (!defined( __WIN__) && !defined( __EMX__) && !defined( OS2))
- else
- table->file->extra(HA_EXTRA_FORCE_REOPEN); // Don't use this file anymore
-#endif
+ wait_while_table_is_used(thd, table, HA_EXTRA_PREPARE_FOR_RENAME);
+ close_data_files_and_morph_locks(thd, db, table_name);
- if (new_name != table_name || new_db != db)
+ error=0;
+ save_old_db_type= old_db_type;
+
+ /*
+ This leads to the storage engine (SE) not being notified for renames in
+ mysql_rename_table(), because we just juggle with the FRM and nothing
+ more. If we have an intermediate table, then we notify the SE that
+ it should become the actual table. Later, we will recycle the old table.
+ However, in case of ALTER TABLE RENAME there might be no intermediate
+ table. This is when the old and new tables are compatible, according to
+ compare_table(). Then, we need one additional call to
+ mysql_rename_table() with flag NO_FRM_RENAME, which does nothing else but
+ actual rename in the SE and the FRM is not touched. Note that, if the
+ table is renamed and the SE is also changed, then an intermediate table
+ is created and the additional call will not take place.
+ */
+ if (need_copy_table == ALTER_TABLE_METADATA_ONLY)
{
- /*
- Check that there is no table with target name. See the
- comment describing code for 'simple' ALTER TABLE ... RENAME.
- */
- if (table_cache_has_open_placeholder(thd, new_db, new_name) ||
- !access(new_name_buff,F_OK))
- {
- error=1;
- my_error(ER_TABLE_EXISTS_ERROR, MYF(0), new_name_buff);
- VOID(quick_rm_table(new_db_type,new_db,tmp_name));
- VOID(pthread_mutex_unlock(&LOCK_open));
- goto err;
- }
+ DBUG_ASSERT(new_db_type == old_db_type);
+ /* This type cannot happen in regular ALTER. */
+ new_db_type= old_db_type= NULL;
}
-
- error=0;
- if (!need_copy_table)
- new_db_type=old_db_type=DB_TYPE_UNKNOWN; // this type cannot happen in regular ALTER
- if (mysql_rename_table(old_db_type,db,table_name,db,old_name))
+ if (mysql_rename_table(old_db_type, db, table_name, db, old_name,
+ FN_TO_IS_TMP))
{
error=1;
- VOID(quick_rm_table(new_db_type,new_db,tmp_name));
+ 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) ||
+ 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))
-
- { // Try to get everything back
+ {
+ /* Try to get everything back. */
error=1;
- VOID(quick_rm_table(new_db_type,new_db,new_alias));
- VOID(quick_rm_table(new_db_type,new_db,tmp_name));
- VOID(mysql_rename_table(old_db_type,db,old_name,db,alias));
+ 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));
}
+
if (error)
{
- /*
- This shouldn't happen. We solve this the safe way by
- closing the locked table.
- */
- if (table)
- close_cached_table(thd,table);
- VOID(pthread_mutex_unlock(&LOCK_open));
- goto err;
- }
- if (thd->lock || new_name != table_name || no_table_reopen) // True if WIN32
- {
- /*
- Not table locking or alter table with rename
- free locks and remove old table
- */
- if (table)
- close_cached_table(thd,table);
- VOID(quick_rm_table(old_db_type,db,old_name));
+ /* This shouldn't happen. But let us play it safe. */
+ goto err_with_placeholders;
}
- else
+
+ if (need_copy_table == ALTER_TABLE_METADATA_ONLY)
{
/*
- Using LOCK TABLES without rename.
- This code is never executed on WIN32!
- Remove old renamed table, reopen table and get new locks
+ Now we have to inform handler that new .FRM file is in place.
+ To do this we need to obtain a handler object for it.
+ NO need to tamper with MERGE tables. The real open is done later.
*/
- if (table)
- {
- VOID(table->file->extra(HA_EXTRA_FORCE_REOPEN)); // Use new file
- /* Mark in-use copies old */
- remove_table_from_cache(thd,db,table_name,RTFC_NO_FLAG);
- /* end threads waiting on lock */
- mysql_lock_abort(thd,table);
- }
- VOID(quick_rm_table(old_db_type,db,old_name));
- if (close_data_tables(thd,db,table_name) ||
- reopen_tables(thd,1,0))
- { // This shouldn't happen
- if (table)
- close_cached_table(thd,table); // Remove lock for table
- VOID(pthread_mutex_unlock(&LOCK_open));
- goto err;
+ TABLE *t_table;
+ 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;
+ }
+ 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.
+ */
+ DBUG_ASSERT(t_table == table);
+ table->open_placeholder= 1;
+ close_handle_and_leave_table_as_lock(table);
}
}
- thd->proc_info="end";
- if (mysql_bin_log.is_open())
+
+ VOID(quick_rm_table(old_db_type, db, old_name, FN_IS_TMP));
+
+ if (thd->locked_tables && new_name == table_name && new_db == db)
{
- thd->clear_error();
- Query_log_event qinfo(thd, thd->query, thd->query_length, FALSE, FALSE);
- mysql_bin_log.write(&qinfo);
+ thd->in_lock_tables= 1;
+ error= reopen_tables(thd, 1, 1);
+ thd->in_lock_tables= 0;
+ if (error)
+ goto err_with_placeholders;
}
- broadcast_refresh();
VOID(pthread_mutex_unlock(&LOCK_open));
-#ifdef HAVE_BERKELEY_DB
- if (old_db_type == DB_TYPE_BERKELEY_DB)
+
+ thd_proc_info(thd, "end");
+
+ DBUG_EXECUTE_IF("sleep_alter_before_main_binlog", my_sleep(6000000););
+
+ ha_binlog_log_query(thd, create_info->db_type, LOGCOM_ALTER_TABLE,
+ thd->query, thd->query_length,
+ db, table_name);
+
+ DBUG_ASSERT(!(mysql_bin_log.is_open() &&
+ thd->current_stmt_binlog_row_based &&
+ (create_info->options & HA_LEX_CREATE_TMP_TABLE)));
+ write_bin_log(thd, TRUE, thd->query, thd->query_length);
+
+ if (ha_check_storage_engine_flag(old_db_type, HTON_FLUSH_AFTER_RENAME))
{
/*
For the alter table to be properly flushed to the logs, we
have to open the new table. If not, we get a problem on server
- shutdown.
+ shutdown. But we do not need to attach MERGE children.
*/
char path[FN_REFLEN];
- build_table_path(path, sizeof(path), new_db, table_name, "");
- table=open_temporary_table(thd, path, new_db, tmp_name,0);
- if (table)
+ TABLE *t_table;
+ build_table_filename(path, sizeof(path), new_db, table_name, "", 0);
+ t_table= open_temporary_table(thd, path, new_db, tmp_name, 0);
+ if (t_table)
{
- intern_close_table(table);
- my_free((char*) table, MYF(0));
+ intern_close_table(t_table);
+ my_free(t_table, MYF(0));
}
else
- sql_print_warning("Could not open BDB table %s.%s after rename\n",
+ sql_print_warning("Could not open table %s.%s after rename\n",
new_db,table_name);
- (void) berkeley_flush_logs();
+ ha_flush_logs(old_db_type);
}
-#endif
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 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);
+ }
+
end_temporary:
my_snprintf(tmp_name, sizeof(tmp_name), ER(ER_INSERT_INFO),
(ulong) (copied + deleted), (ulong) deleted,
(ulong) thd->cuted_fields);
- send_ok(thd, copied + deleted, 0L, tmp_name);
+ my_ok(thd, copied + deleted, 0L, tmp_name);
thd->some_tables_deleted=0;
DBUG_RETURN(FALSE);
+err1:
+ 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));
+
err:
/*
No default value was provided for a DATE/DATETIME field, the
@@ -4047,11 +7118,11 @@ err:
the table to be altered isn't empty.
Report error here.
*/
- if (error_if_not_empty && thd->row_count)
+ if (alter_info->error_if_not_empty && thd->row_count)
{
const char *f_val= 0;
enum enum_mysql_timestamp_type t_type= MYSQL_TIMESTAMP_DATE;
- switch (new_datetime_field->sql_type)
+ switch (alter_info->datetime_field->sql_type)
{
case MYSQL_TYPE_DATE:
case MYSQL_TYPE_NEWDATE:
@@ -4070,16 +7141,34 @@ err:
thd->abort_on_warning= TRUE;
make_truncated_value_warning(thd, MYSQL_ERROR::WARN_LEVEL_ERROR,
f_val, strlength(f_val), t_type,
- new_datetime_field->field_name);
+ 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:
+ /*
+ 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.
+ */
+ unlink_open_table(thd, table, FALSE);
+ if (name_lock)
+ unlink_open_table(thd, name_lock, FALSE);
+ VOID(pthread_mutex_unlock(&LOCK_open));
+ DBUG_RETURN(TRUE);
+}
+/* mysql_alter_table */
static int
copy_data_between_tables(TABLE *from,TABLE *to,
- List<create_field> &create,
+ List<Create_field> &create,
bool ignore,
uint order_num, ORDER *order,
ha_rows *copied,
@@ -4100,6 +7189,7 @@ copy_data_between_tables(TABLE *from,TABLE *to,
ha_rows examined_rows;
bool auto_increment_field_copied= 0;
ulong save_sql_mode;
+ ulonglong prev_insert_id;
DBUG_ENTER("copy_data_between_tables");
/*
@@ -4127,12 +7217,12 @@ copy_data_between_tables(TABLE *from,TABLE *to,
MODE_STRICT_ALL_TABLES));
from->file->info(HA_STATUS_VARIABLE);
- to->file->start_bulk_insert(from->file->records);
+ to->file->ha_start_bulk_insert(from->file->stats.records);
save_sql_mode= thd->variables.sql_mode;
- List_iterator<create_field> it(create);
- create_field *def;
+ List_iterator<Create_field> it(create);
+ Create_field *def;
copy_end=copy;
for (Field **ptr=to->field ; *ptr ; ptr++)
{
@@ -4160,31 +7250,39 @@ copy_data_between_tables(TABLE *from,TABLE *to,
if (order)
{
- from->sort.io_cache=(IO_CACHE*) my_malloc(sizeof(IO_CACHE),
- MYF(MY_FAE | MY_ZEROFILL));
- bzero((char*) &tables,sizeof(tables));
- tables.table= from;
- tables.alias= tables.table_name= (char*) from->s->table_name;
- tables.db= (char*) from->s->db;
- error=1;
+ if (to->s->primary_key != MAX_KEY && to->file->primary_key_is_clustered())
+ {
+ char warn_buff[MYSQL_ERRMSG_SIZE];
+ my_snprintf(warn_buff, sizeof(warn_buff),
+ "ORDER BY ignored as there is a user-defined clustered index"
+ " in the table '%-.192s'", from->s->table_name.str);
+ push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_UNKNOWN_ERROR,
+ warn_buff);
+ }
+ else
+ {
+ from->sort.io_cache=(IO_CACHE*) my_malloc(sizeof(IO_CACHE),
+ MYF(MY_FAE | MY_ZEROFILL));
+ bzero((char *) &tables, sizeof(tables));
+ tables.table= from;
+ tables.alias= tables.table_name= from->s->table_name.str;
+ tables.db= from->s->db.str;
+ error= 1;
- if (thd->lex->select_lex.setup_ref_array(thd, order_num) ||
- setup_order(thd, thd->lex->select_lex.ref_pointer_array,
- &tables, fields, all_fields, order) ||
- !(sortorder=make_unireg_sortorder(order, &length, NULL)) ||
- (from->sort.found_records = filesort(thd, from, sortorder, length,
- (SQL_SELECT *) 0, HA_POS_ERROR,
- &examined_rows)) ==
- HA_POS_ERROR)
- goto err;
+ if (thd->lex->select_lex.setup_ref_array(thd, order_num) ||
+ setup_order(thd, thd->lex->select_lex.ref_pointer_array,
+ &tables, fields, all_fields, order) ||
+ !(sortorder= make_unireg_sortorder(order, &length, NULL)) ||
+ (from->sort.found_records= filesort(thd, from, sortorder, length,
+ (SQL_SELECT *) 0, HA_POS_ERROR,
+ 1, &examined_rows)) ==
+ HA_POS_ERROR)
+ goto err;
+ }
};
- /*
- Handler must be told explicitly to retrieve all columns, because
- this function does not set field->query_id in the columns to the
- current query id
- */
- from->file->extra(HA_EXTRA_RETRIEVE_ALL_COLS);
+ /* Tell handler that we have values for all columns in the to table */
+ to->use_all_columns();
init_read_record(&info, thd, from, (SQL_SELECT *) 0, 1, 1, FALSE);
if (ignore)
to->file->extra(HA_EXTRA_IGNORE_DUP_KEY);
@@ -4217,18 +7315,33 @@ copy_data_between_tables(TABLE *from,TABLE *to,
{
copy_ptr->do_copy(copy_ptr);
}
- error=to->file->write_row((byte*) to->record[0]);
+ prev_insert_id= to->file->next_insert_id;
+ error=to->file->ha_write_row(to->record[0]);
to->auto_increment_field_not_null= FALSE;
if (error)
{
if (!ignore ||
- (error != HA_ERR_FOUND_DUPP_KEY &&
- error != HA_ERR_FOUND_DUPP_UNIQUE))
+ to->file->is_fatal_error(error, HA_CHECK_DUP))
{
+ if (!to->file->is_fatal_error(error, HA_CHECK_DUP))
+ {
+ uint key_nr= to->file->get_dup_key(error);
+ if ((int) key_nr >= 0)
+ {
+ const char *err_msg= ER(ER_DUP_ENTRY_WITH_KEY_NAME);
+ if (key_nr == 0 &&
+ (to->key_info[0].key_part[0].field->flags &
+ AUTO_INCREMENT_FLAG))
+ err_msg= ER(ER_DUP_ENTRY_AUTOINCREMENT_CASE);
+ to->file->print_keydup_error(key_nr, err_msg);
+ break;
+ }
+ }
+
to->file->print_error(error,MYF(0));
break;
}
- to->file->restore_auto_increment();
+ to->file->restore_auto_increment(prev_insert_id);
delete_count++;
}
else
@@ -4238,7 +7351,7 @@ copy_data_between_tables(TABLE *from,TABLE *to,
free_io_cache(from);
delete [] copy; // This is never 0
- if (to->file->end_bulk_insert() && error <= 0)
+ if (to->file->ha_end_bulk_insert() && error <= 0)
{
to->file->print_error(my_errno,MYF(0));
error=1;
@@ -4255,9 +7368,9 @@ copy_data_between_tables(TABLE *from,TABLE *to,
Ensure that the new table is saved properly to disk so that we
can do a rename
*/
- if (ha_commit_stmt(thd))
+ if (ha_autocommit_or_rollback(thd, 0))
error=1;
- if (ha_commit(thd))
+ if (end_active_trans(thd))
error=1;
err:
@@ -4266,6 +7379,7 @@ copy_data_between_tables(TABLE *from,TABLE *to,
free_io_cache(from);
*copied= found_count;
*deleted=delete_count;
+ to->file->ha_release_auto_increment();
if (to->file->ha_external_lock(thd,F_UNLCK))
error=1;
DBUG_RETURN(error > 0 ? -1 : 0);
@@ -4289,20 +7403,26 @@ bool mysql_recreate_table(THD *thd, TABLE_LIST *table_list)
Alter_info alter_info;
DBUG_ENTER("mysql_recreate_table");
+ DBUG_ASSERT(!table_list->next_global);
+ /*
+ table_list->table has been closed and freed. Do not reference
+ uninitialized data. open_tables() could fail.
+ */
+ table_list->table= NULL;
bzero((char*) &create_info, sizeof(create_info));
- create_info.db_type=DB_TYPE_DEFAULT;
create_info.row_type=ROW_TYPE_NOT_USED;
create_info.default_table_charset=default_charset_info;
/* Force alter table to recreate table */
- alter_info.flags= ALTER_CHANGE_COLUMN;
+ alter_info.flags= (ALTER_CHANGE_COLUMN | ALTER_RECREATE);
DBUG_RETURN(mysql_alter_table(thd, NullS, NullS, &create_info,
- table_list, &alter_info,
- 0, (ORDER *) 0, 0));
+ table_list, &alter_info, 0,
+ (ORDER *) 0, 0));
}
-bool mysql_checksum_table(THD *thd, TABLE_LIST *tables, HA_CHECK_OPT *check_opt)
+bool mysql_checksum_table(THD *thd, TABLE_LIST *tables,
+ HA_CHECK_OPT *check_opt)
{
TABLE_LIST *table;
List<Item> field_list;
@@ -4319,6 +7439,7 @@ bool mysql_checksum_table(THD *thd, TABLE_LIST *tables, HA_CHECK_OPT *check_opt)
Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
DBUG_RETURN(TRUE);
+ /* Open one table after the other to keep lock time as short as possible. */
for (table= tables; table; table= table->next_local)
{
char table_name[NAME_LEN*2+2];
@@ -4326,7 +7447,7 @@ bool mysql_checksum_table(THD *thd, TABLE_LIST *tables, HA_CHECK_OPT *check_opt)
strxmov(table_name, table->db ,".", table->table_name, NullS);
- t= table->table= open_ltable(thd, table, TL_READ);
+ t= table->table= open_n_lock_single_table(thd, table, TL_READ);
thd->clear_error(); // these errors shouldn't get client
protocol->prepare_for_resend();
@@ -4340,10 +7461,10 @@ bool mysql_checksum_table(THD *thd, TABLE_LIST *tables, HA_CHECK_OPT *check_opt)
}
else
{
- if (t->file->table_flags() & HA_HAS_CHECKSUM &&
+ if (t->file->ha_table_flags() & HA_HAS_CHECKSUM &&
!(check_opt->flags & T_EXTEND))
protocol->store((ulonglong)t->file->checksum());
- else if (!(t->file->table_flags() & HA_HAS_CHECKSUM) &&
+ else if (!(t->file->ha_table_flags() & HA_HAS_CHECKSUM) &&
(check_opt->flags & T_QUICK))
protocol->store_null();
else
@@ -4352,10 +7473,7 @@ bool mysql_checksum_table(THD *thd, TABLE_LIST *tables, HA_CHECK_OPT *check_opt)
ha_checksum crc= 0;
uchar null_mask=256 - (1 << t->s->last_null_bit_pos);
- /* InnoDB must be told explicitly to retrieve all columns, because
- this function does not set field->query_id in the columns to the
- current query id */
- t->file->extra(HA_EXTRA_RETRIEVE_ALL_COLS);
+ t->use_all_columns();
if (t->file->ha_rnd_init(1))
protocol->store_null();
@@ -4384,15 +7502,15 @@ bool mysql_checksum_table(THD *thd, TABLE_LIST *tables, HA_CHECK_OPT *check_opt)
for (uint i= 0; i < t->s->fields; i++ )
{
Field *f= t->field[i];
- if ((f->type() == FIELD_TYPE_BLOB) ||
+ if ((f->type() == MYSQL_TYPE_BLOB) ||
(f->type() == MYSQL_TYPE_VARCHAR))
{
String tmp;
f->val_str(&tmp);
- row_crc= my_checksum(row_crc, (byte*) tmp.ptr(), tmp.length());
+ row_crc= my_checksum(row_crc, (uchar*) tmp.ptr(), tmp.length());
}
else
- row_crc= my_checksum(row_crc, (byte*) f->ptr,
+ row_crc= my_checksum(row_crc, f->ptr,
f->pack_length());
}
@@ -4410,7 +7528,7 @@ bool mysql_checksum_table(THD *thd, TABLE_LIST *tables, HA_CHECK_OPT *check_opt)
goto err;
}
- send_eof(thd);
+ my_eof(thd);
DBUG_RETURN(FALSE);
err:
@@ -4421,35 +7539,35 @@ bool mysql_checksum_table(THD *thd, TABLE_LIST *tables, HA_CHECK_OPT *check_opt)
}
static bool check_engine(THD *thd, const char *table_name,
- enum db_type *new_engine)
+ HA_CREATE_INFO *create_info)
{
- enum db_type req_engine= *new_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, req_engine, no_substitution, 1)) == DB_TYPE_UNKNOWN)
+ if (!(*new_engine= ha_checktype(thd, ha_legacy_type(req_engine),
+ no_substitution, 1)))
return TRUE;
- if (req_engine != *new_engine)
+ if (req_engine && req_engine != *new_engine)
{
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
ER_WARN_USING_OTHER_HANDLER,
ER(ER_WARN_USING_OTHER_HANDLER),
- ha_get_storage_engine(*new_engine),
+ ha_resolve_storage_engine_name(*new_engine),
table_name);
}
- return FALSE;
-}
-
-static void set_tmp_file_path(char *buf, size_t bufsize, THD *thd)
-{
- char *p= strnmov(buf, mysql_tmpdir, (uint) bufsize);
- my_snprintf(p, bufsize - (p - buf), "%s%lx_%lx_%x%s",
- tmp_file_prefix, current_pid,
- thd->thread_id, thd->tmp_table++, reg_ext);
- if (lower_case_table_names)
+ if (create_info->options & HA_LEX_CREATE_TMP_TABLE &&
+ ha_check_storage_engine_flag(*new_engine, HTON_TEMPORARY_NOT_SUPPORTED))
{
- /* Convert all except tmpdir to lower case */
- my_casedn_str(files_charset_info, p);
+ if (create_info->used_fields & HA_CREATE_USED_ENGINE)
+ {
+ my_error(ER_ILLEGAL_HA_CREATE_OPTION, MYF(0),
+ ha_resolve_storage_engine_name(*new_engine), "TEMPORARY");
+ *new_engine= 0;
+ return TRUE;
+ }
+ *new_engine= myisam_hton;
}
+ return FALSE;
}
diff --git a/sql/sql_tablespace.cc b/sql/sql_tablespace.cc
new file mode 100644
index 00000000000..9fec0e3bc63
--- /dev/null
+++ b/sql/sql_tablespace.cc
@@ -0,0 +1,71 @@
+/* Copyright (C) 2000-2004 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+/* drop and alter of tablespaces */
+
+#include "mysql_priv.h"
+
+int mysql_alter_tablespace(THD *thd, st_alter_tablespace *ts_info)
+{
+ int error= HA_ADMIN_NOT_IMPLEMENTED;
+ handlerton *hton= ts_info->storage_engine;
+
+ DBUG_ENTER("mysql_alter_tablespace");
+ /*
+ If the user haven't defined an engine, this will fallback to using the
+ default storage engine.
+ */
+ if (hton == NULL || hton->state != SHOW_OPTION_YES)
+ {
+ hton= ha_default_handlerton(thd);
+ if (ts_info->storage_engine != 0)
+ push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_ERROR,
+ ER_WARN_USING_OTHER_HANDLER,
+ ER(ER_WARN_USING_OTHER_HANDLER),
+ ha_resolve_storage_engine_name(hton),
+ ts_info->tablespace_name ? ts_info->tablespace_name
+ : ts_info->logfile_group_name);
+ }
+
+ if (hton->alter_tablespace)
+ {
+ if ((error= hton->alter_tablespace(hton, thd, ts_info)))
+ {
+ if (error == HA_ADMIN_NOT_IMPLEMENTED)
+ {
+ my_error(ER_CHECK_NOT_IMPLEMENTED, MYF(0), "");
+ }
+ else if (error == 1)
+ {
+ DBUG_RETURN(1);
+ }
+ else
+ {
+ my_error(error, MYF(0));
+ }
+ DBUG_RETURN(error);
+ }
+ }
+ else
+ {
+ push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_ERROR,
+ ER_ILLEGAL_HA_CREATE_OPTION,
+ ER(ER_ILLEGAL_HA_CREATE_OPTION),
+ ha_resolve_storage_engine_name(hton),
+ "TABLESPACE or LOGFILE GROUP");
+ }
+ write_bin_log(thd, FALSE, thd->query, thd->query_length);
+ DBUG_RETURN(FALSE);
+}
diff --git a/sql/sql_test.cc b/sql/sql_test.cc
index 465f53cc30c..78932396efe 100644
--- a/sql/sql_test.cc
+++ b/sql/sql_test.cc
@@ -27,6 +27,10 @@
#include <sys/malloc.h>
#endif
+#ifdef HAVE_EVENT_SCHEDULER
+#include "events.h"
+#endif
+
static const char *lock_descriptions[] =
{
"No lock",
@@ -47,14 +51,14 @@ static const char *lock_descriptions[] =
#ifndef DBUG_OFF
void
-print_where(COND *cond,const char *info)
+print_where(COND *cond,const char *info, enum_query_type query_type)
{
if (cond)
{
char buff[256];
String str(buff,(uint32) sizeof(buff), system_charset_info);
str.length(0);
- cond->print(&str);
+ cond->print(&str, query_type);
str.append('\0');
DBUG_LOCK_FILE;
(void) fprintf(DBUG_FILE,"\nWHERE:(%s) ",info);
@@ -71,17 +75,18 @@ void print_cached_tables(void)
uint idx,count,unused;
TABLE *start_link,*lnk;
+ /* purecov: begin tested */
VOID(pthread_mutex_lock(&LOCK_open));
- puts("DB Table Version Thread L.thread Open Lock");
+ puts("DB Table Version Thread Open Lock");
for (idx=unused=0 ; idx < open_cache.records ; idx++)
{
TABLE *entry=(TABLE*) hash_element(&open_cache,idx);
- printf("%-14.14s %-32s%6ld%8ld%10ld%6d %s\n",
- entry->s->db, entry->s->table_name, entry->s->version,
+ 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->in_use ? entry->in_use->dbug_thread_id : 0L,
- entry->db_stat ? 1 : 0, entry->in_use ? lock_descriptions[(int)entry->reginfo.lock_type] : "Not in use");
+ entry->db_stat ? 1 : 0,
+ entry->in_use ? lock_descriptions[(int)entry->reginfo.lock_type] : "Not in use");
if (!entry->in_use)
unused++;
}
@@ -108,6 +113,7 @@ void print_cached_tables(void)
printf("Error: File hash table is corrupted\n");
fflush(stdout);
VOID(pthread_mutex_unlock(&LOCK_open));
+ /* purecov: end */
return;
}
@@ -139,7 +145,7 @@ void TEST_filesort(SORT_FIELD *sortorder,uint s_length)
else
{
str.length(0);
- sortorder->item->print(&str);
+ sortorder->item->print(&str, QT_ORDINARY);
out.append(str);
}
}
@@ -245,14 +251,15 @@ print_plan(JOIN* join, uint idx, double record_count, double read_time,
if (join->best_read == DBL_MAX)
{
fprintf(DBUG_FILE,
- "%s; idx:%u, best: DBL_MAX, atime: %g, itime: %g, count: %g\n",
- info, idx, current_read_time, read_time, record_count);
+ "%s; idx: %u best: DBL_MAX atime: %g itime: %g count: %g\n",
+ info, idx, current_read_time, read_time, record_count);
}
else
{
fprintf(DBUG_FILE,
- "%s; idx:%u, best: %g, accumulated: %g, increment: %g, count: %g\n",
- info, idx, join->best_read, current_read_time, read_time, record_count);
+ "%s; idx :%u best: %g accumulated: %g increment: %g count: %g\n",
+ info, idx, join->best_read, current_read_time, read_time,
+ record_count);
}
/* Print the tables in JOIN->positions */
@@ -262,7 +269,7 @@ print_plan(JOIN* join, uint idx, double record_count, double read_time,
pos = join->positions[i];
table= pos.table->table;
if (table)
- fputs(table->s->table_name, DBUG_FILE);
+ fputs(table->s->table_name.str, DBUG_FILE);
fputc(' ', DBUG_FILE);
}
fputc('\n', DBUG_FILE);
@@ -279,7 +286,7 @@ print_plan(JOIN* join, uint idx, double record_count, double read_time,
pos= join->best_positions[i];
table= pos.table->table;
if (table)
- fputs(table->s->table_name, DBUG_FILE);
+ fputs(table->s->table_name.str, DBUG_FILE);
fputc(' ', DBUG_FILE);
}
}
@@ -290,7 +297,7 @@ print_plan(JOIN* join, uint idx, double record_count, double read_time,
for (plan_nodes= join->best_ref ; *plan_nodes ; plan_nodes++)
{
join_table= (*plan_nodes);
- fputs(join_table->table->s->table_name, DBUG_FILE);
+ fputs(join_table->table->s->table_name.str, DBUG_FILE);
fprintf(DBUG_FILE, "(%lu,%lu,%lu)",
(ulong) join_table->found_records,
(ulong) join_table->records,
@@ -337,14 +344,14 @@ static void push_locks_into_array(DYNAMIC_ARRAY *ar, THR_LOCK_DATA *data,
{
TABLE_LOCK_INFO table_lock_info;
table_lock_info.thread_id= table->in_use->thread_id;
- memcpy(table_lock_info.table_name, table->s->table_cache_key,
- table->s->key_length);
+ memcpy(table_lock_info.table_name, table->s->table_cache_key.str,
+ table->s->table_cache_key.length);
table_lock_info.table_name[strlen(table_lock_info.table_name)]='.';
table_lock_info.waiting=wait;
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,(gptr) &table_lock_info));
+ VOID(push_dynamic(ar,(uchar*) &table_lock_info));
}
}
}
@@ -389,7 +396,7 @@ static void display_table_locks(void)
VOID(pthread_mutex_unlock(&THR_LOCK_lock));
if (!saved_table_locks.elements) goto end;
- qsort((gptr) dynamic_element(&saved_table_locks,0,TABLE_LOCK_INFO *),saved_table_locks.elements,sizeof(TABLE_LOCK_INFO),(qsort_cmp) dl_compare);
+ qsort((uchar*) dynamic_element(&saved_table_locks,0,TABLE_LOCK_INFO *),saved_table_locks.elements,sizeof(TABLE_LOCK_INFO),(qsort_cmp) dl_compare);
freeze_size(&saved_table_locks);
puts("\nThread database.table_name Locked/Waiting Lock_type\n");
@@ -454,7 +461,7 @@ void mysql_print_status()
VOID(my_getwd(current_dir, sizeof(current_dir),MYF(0)));
printf("Current dir: %s\n", current_dir);
printf("Running threads: %d Stack size: %ld\n", thread_count,
- (long) thread_stack);
+ (long) my_thread_stack_size);
thr_print_locks(); // Write some debug info
#ifndef DBUG_OFF
print_cached_tables();
@@ -485,7 +492,7 @@ Open tables: %10lu\n\
Open files: %10lu\n\
Open streams: %10lu\n",
tmp.opened_tables,
- (ulong) cached_tables(),
+ (ulong) cached_open_tables(),
(ulong) my_file_opened,
(ulong) my_stream_opened);
@@ -503,8 +510,10 @@ Next alarm time: %lu\n",
display_table_locks();
fflush(stdout);
my_checkmalloc();
- TERMINATE(stdout); // Write malloc information
-
+ 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\
@@ -529,7 +538,11 @@ Estimated memory (with thread stack): %ld\n",
(int) info.uordblks,
(int) info.fordblks,
(int) info.keepcost,
- (long) (thread_count * thread_stack + info.hblkhd + info.arena));
+ (long) (thread_count * my_thread_stack_size + info.hblkhd + info.arena));
+#endif
+
+#ifdef HAVE_EVENT_SCHEDULER
+ Events::dump_internal_status();
#endif
puts("");
}
diff --git a/sql/sql_trigger.cc b/sql/sql_trigger.cc
index 930e3601699..8cab8fff2f3 100644
--- a/sql/sql_trigger.cc
+++ b/sql/sql_trigger.cc
@@ -20,12 +20,155 @@
#include "sql_trigger.h"
#include "parse_file.h"
-static const LEX_STRING triggers_file_type=
- {(char *) STRING_WITH_LEN("TRIGGERS")};
+/*************************************************************************/
-const char * const triggers_file_ext= ".TRG";
+template <class T>
+inline T *alloc_type(MEM_ROOT *m)
+{
+ return (T *) alloc_root(m, sizeof (T));
+}
/*
+ NOTE: Since alloc_type() is declared as inline, alloc_root() calls should
+ be inlined by the compiler. So, implementation of alloc_root() is not
+ needed. However, let's put the implementation in object file just in case
+ of stupid MS or other old compilers.
+*/
+
+template LEX_STRING *alloc_type<LEX_STRING>(MEM_ROOT *m);
+template ulonglong *alloc_type<ulonglong>(MEM_ROOT *m);
+
+inline LEX_STRING *alloc_lex_string(MEM_ROOT *m)
+{
+ return alloc_type<LEX_STRING>(m);
+}
+
+/*************************************************************************/
+/**
+ Trigger_creation_ctx -- creation context of triggers.
+*/
+
+class Trigger_creation_ctx : public Stored_program_creation_ctx,
+ public Sql_alloc
+{
+public:
+ static Trigger_creation_ctx *create(THD *thd,
+ const char *db_name,
+ const char *table_name,
+ const LEX_STRING *client_cs_name,
+ const LEX_STRING *connection_cl_name,
+ const LEX_STRING *db_cl_name);
+
+public:
+ virtual Stored_program_creation_ctx *clone(MEM_ROOT *mem_root)
+ {
+ return new (mem_root) Trigger_creation_ctx(m_client_cs,
+ m_connection_cl,
+ m_db_cl);
+ }
+
+protected:
+ virtual Object_creation_ctx *create_backup_ctx(THD *thd) const
+ {
+ return new Trigger_creation_ctx(thd);
+ }
+
+private:
+ Trigger_creation_ctx(THD *thd)
+ :Stored_program_creation_ctx(thd)
+ { }
+
+ Trigger_creation_ctx(CHARSET_INFO *client_cs,
+ CHARSET_INFO *connection_cl,
+ CHARSET_INFO *db_cl)
+ :Stored_program_creation_ctx(client_cs, connection_cl, db_cl)
+ { }
+};
+
+/**************************************************************************
+ Trigger_creation_ctx implementation.
+**************************************************************************/
+
+Trigger_creation_ctx *
+Trigger_creation_ctx::create(THD *thd,
+ const char *db_name,
+ const char *table_name,
+ const LEX_STRING *client_cs_name,
+ const LEX_STRING *connection_cl_name,
+ const LEX_STRING *db_cl_name)
+{
+ CHARSET_INFO *client_cs;
+ CHARSET_INFO *connection_cl;
+ CHARSET_INFO *db_cl;
+
+ bool invalid_creation_ctx= FALSE;
+
+ if (resolve_charset(client_cs_name->str,
+ thd->variables.character_set_client,
+ &client_cs))
+ {
+ sql_print_warning("Trigger for table '%s'.'%s': "
+ "invalid character_set_client value (%s).",
+ (const char *) db_name,
+ (const char *) table_name,
+ (const char *) client_cs_name->str);
+
+ invalid_creation_ctx= TRUE;
+ }
+
+ if (resolve_collation(connection_cl_name->str,
+ thd->variables.collation_connection,
+ &connection_cl))
+ {
+ sql_print_warning("Trigger for table '%s'.'%s': "
+ "invalid collation_connection value (%s).",
+ (const char *) db_name,
+ (const char *) table_name,
+ (const char *) connection_cl_name->str);
+
+ invalid_creation_ctx= TRUE;
+ }
+
+ if (resolve_collation(db_cl_name->str, NULL, &db_cl))
+ {
+ sql_print_warning("Trigger for table '%s'.'%s': "
+ "invalid database_collation value (%s).",
+ (const char *) db_name,
+ (const char *) table_name,
+ (const char *) db_cl_name->str);
+
+ invalid_creation_ctx= TRUE;
+ }
+
+ if (invalid_creation_ctx)
+ {
+ push_warning_printf(thd,
+ MYSQL_ERROR::WARN_LEVEL_WARN,
+ ER_TRG_INVALID_CREATION_CTX,
+ ER(ER_TRG_INVALID_CREATION_CTX),
+ (const char *) db_name,
+ (const char *) table_name);
+ }
+
+ /*
+ If we failed to resolve the database collation, load the default one
+ from the disk.
+ */
+
+ if (!db_cl)
+ db_cl= get_default_db_collation(thd, db_name);
+
+ return new Trigger_creation_ctx(client_cs, connection_cl, db_cl);
+}
+
+/*************************************************************************/
+
+static const LEX_STRING triggers_file_type=
+ { C_STRING_WITH_LEN("TRIGGERS") };
+
+const char * const TRG_EXT= ".TRG";
+
+/**
Table of .TRG file field descriptors.
We have here only one field now because in nearest future .TRG
files will be merged into .FRM files (so we don't need something
@@ -34,37 +177,52 @@ const char * const triggers_file_ext= ".TRG";
static File_option triggers_file_parameters[]=
{
{
- {(char *) STRING_WITH_LEN("triggers") },
+ { C_STRING_WITH_LEN("triggers") },
my_offsetof(class Table_triggers_list, definitions_list),
FILE_OPTIONS_STRLIST
},
{
- {(char *) STRING_WITH_LEN("sql_modes") },
+ { C_STRING_WITH_LEN("sql_modes") },
my_offsetof(class Table_triggers_list, definition_modes_list),
FILE_OPTIONS_ULLLIST
},
{
- {(char *) STRING_WITH_LEN("definers") },
+ { C_STRING_WITH_LEN("definers") },
my_offsetof(class Table_triggers_list, definers_list),
FILE_OPTIONS_STRLIST
},
+ {
+ { C_STRING_WITH_LEN("client_cs_names") },
+ my_offsetof(class Table_triggers_list, client_cs_names),
+ FILE_OPTIONS_STRLIST
+ },
+ {
+ { C_STRING_WITH_LEN("connection_cl_names") },
+ my_offsetof(class Table_triggers_list, connection_cl_names),
+ FILE_OPTIONS_STRLIST
+ },
+ {
+ { C_STRING_WITH_LEN("db_cl_names") },
+ my_offsetof(class Table_triggers_list, db_cl_names),
+ FILE_OPTIONS_STRLIST
+ },
{ { 0, 0 }, 0, FILE_OPTIONS_STRING }
};
File_option sql_modes_parameters=
{
- {(char*) STRING_WITH_LEN("sql_modes") },
+ { C_STRING_WITH_LEN("sql_modes") },
my_offsetof(class Table_triggers_list, definition_modes_list),
FILE_OPTIONS_ULLLIST
};
-/*
+/**
This must be kept up to date whenever a new option is added to the list
above, as it specifies the number of required parameters of the trigger in
.trg file.
*/
-static const int TRG_NUM_REQUIRED_PARAMETERS= 4;
+static const int TRG_NUM_REQUIRED_PARAMETERS= 6;
/*
Structure representing contents of .TRN file which are used to support
@@ -77,14 +235,14 @@ struct st_trigname
};
static const LEX_STRING trigname_file_type=
- {(char *) STRING_WITH_LEN("TRIGGERNAME")};
+ { C_STRING_WITH_LEN("TRIGGERNAME") };
-const char * const trigname_file_ext= ".TRN";
+const char * const TRN_EXT= ".TRN";
static File_option trigname_file_parameters[]=
{
{
- {(char *) STRING_WITH_LEN("trigger_table")},
+ { C_STRING_WITH_LEN("trigger_table")},
offsetof(struct st_trigname, trigger_table),
FILE_OPTIONS_ESTRING
},
@@ -94,15 +252,15 @@ static File_option trigname_file_parameters[]=
const LEX_STRING trg_action_time_type_names[]=
{
- { (char *) STRING_WITH_LEN("BEFORE") },
- { (char *) STRING_WITH_LEN("AFTER") }
+ { C_STRING_WITH_LEN("BEFORE") },
+ { C_STRING_WITH_LEN("AFTER") }
};
const LEX_STRING trg_event_type_names[]=
{
- { (char *) STRING_WITH_LEN("INSERT") },
- { (char *) STRING_WITH_LEN("UPDATE") },
- { (char *) STRING_WITH_LEN("DELETE") }
+ { C_STRING_WITH_LEN("INSERT") },
+ { C_STRING_WITH_LEN("UPDATE") },
+ { C_STRING_WITH_LEN("DELETE") }
};
@@ -114,10 +272,11 @@ public:
Handle_old_incorrect_sql_modes_hook(char *file_path)
:path(file_path)
{};
- virtual bool process_unknown_string(char *&unknown_key, gptr base,
+ virtual bool process_unknown_string(char *&unknown_key, uchar* base,
MEM_ROOT *mem_root, char *end);
};
+
class Handle_old_incorrect_trigger_table_hook: public Unknown_key_hook
{
public:
@@ -125,30 +284,35 @@ public:
LEX_STRING *trigger_table_arg)
:path(file_path), trigger_table_value(trigger_table_arg)
{};
- virtual bool process_unknown_string(char *&unknown_key, gptr base,
+ virtual bool process_unknown_string(char *&unknown_key, uchar* base,
MEM_ROOT *mem_root, char *end);
private:
char *path;
LEX_STRING *trigger_table_value;
};
-/*
+
+/**
Create or drop trigger for table.
- SYNOPSIS
- mysql_create_or_drop_trigger()
- thd - current thread context (including trigger definition in LEX)
- tables - table list containing one table for which trigger is created.
- create - whenever we create (TRUE) or drop (FALSE) trigger
+ @param thd current thread context (including trigger definition in LEX)
+ @param tables table list containing one table for which trigger is created.
+ @param create whenever we create (TRUE) or drop (FALSE) trigger
- NOTE
+ @note
This function is mainly responsible for opening and locking of table and
invalidation of all its instances in table cache after trigger creation.
Real work on trigger creation/dropping is done inside Table_triggers_list
methods.
- RETURN VALUE
+ @todo
+ TODO: We should check if user has TRIGGER privilege for table here.
+ Now we just require SUPER privilege for creating/dropping because
+ we don't have proper privilege checking for triggers in place yet.
+
+ @retval
FALSE Success
+ @retval
TRUE error
*/
bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create)
@@ -163,6 +327,7 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create)
TABLE *table;
bool result= TRUE;
String stmt_query;
+ bool need_start_waiting= FALSE;
DBUG_ENTER("mysql_create_or_drop_trigger");
@@ -195,14 +360,6 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create)
}
/*
- TODO: We should check if user has TRIGGER privilege for table here.
- Now we just require SUPER privilege for creating/dropping because
- we don't have proper privilege checking for triggers in place yet.
- */
- if (check_global_access(thd, SUPER_ACL))
- DBUG_RETURN(TRUE);
-
- /*
There is no DETERMINISTIC clause for triggers, so can't check it.
But a trigger can in theory be used to do nasty things (if it supported
DROP for example) so we do the check for privileges. For now there is
@@ -222,10 +379,12 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create)
/*
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. (Acquiring LOCK_open is not enough because
- global read lock is held without holding LOCK_open).
+ 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 (wait_if_global_read_lock(thd, 0, 1))
+ if (!thd->locked_tables &&
+ !(need_start_waiting= !wait_if_global_read_lock(thd, 0, 1)))
DBUG_RETURN(TRUE);
VOID(pthread_mutex_lock(&LOCK_open));
@@ -255,6 +414,22 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create)
}
}
+ /*
+ Check that the user has TRIGGER privilege on the subject table.
+ */
+ {
+ bool err_status;
+ 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);
+
+ thd->lex->query_tables_own_last= save_query_tables_own_last;
+
+ if (err_status)
+ goto end;
+ }
+
/* We should have only one table in table list. */
DBUG_ASSERT(tables->next_global == 0);
@@ -265,16 +440,30 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create)
goto end;
}
- if (lock_table_names(thd, tables))
- goto end;
-
/* We also don't allow creation of triggers on views. */
tables->required_type= FRMTYPE_TABLE;
- if (reopen_name_locked_table(thd, tables, TRUE))
+ /* Keep consistent with respect to other DDL statements */
+ mysql_ha_rm_tables(thd, tables, TRUE);
+
+ if (thd->locked_tables)
{
- unlock_table_name(thd, tables);
- goto end;
+ /* Table must be write locked */
+ if (name_lock_locked_table(thd, tables))
+ goto end;
+ }
+ else
+ {
+ /* Grab the name lock and insert the placeholder*/
+ if (lock_table_names(thd, tables))
+ goto end;
+
+ /* Convert the placeholder to a real table */
+ if (reopen_name_locked_table(thd, tables, TRUE))
+ {
+ unlock_table_name(thd, tables);
+ goto end;
+ }
}
table= tables->table;
@@ -294,63 +483,75 @@ 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));
-end:
-
- if (!result)
+ /* Under LOCK TABLES we must reopen the table to activate the trigger. */
+ if (!result && thd->locked_tables)
{
- if (mysql_bin_log.is_open())
+ /* 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))
{
- thd->clear_error();
+ /* To be safe remove this table from the set of LOCKED TABLES */
+ unlink_open_table(thd, tables->table, FALSE);
- /* Such a statement can always go directly to binlog, no trans cache. */
- Query_log_event qinfo(thd, stmt_query.ptr(), stmt_query.length(), 0,
- FALSE);
- mysql_bin_log.write(&qinfo);
+ /*
+ 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;
+ }
+
+end:
+
+ if (!result)
+ {
+ write_bin_log(thd, TRUE, stmt_query.ptr(), stmt_query.length());
}
VOID(pthread_mutex_unlock(&LOCK_open));
- start_waiting_global_read_lock(thd);
+
+ if (need_start_waiting)
+ start_waiting_global_read_lock(thd);
if (!result)
- send_ok(thd);
+ my_ok(thd);
DBUG_RETURN(result);
}
-/*
+/**
Create trigger for table.
- SYNOPSIS
- create_trigger()
- thd - current thread context (including trigger definition in
- LEX)
- tables - table list containing one open table for which the
- trigger is created.
- stmt_query - [OUT] after successful return, this string contains
- well-formed statement for creation this trigger.
+ @param thd current thread context (including trigger definition in
+ LEX)
+ @param tables table list containing one open table for which the
+ trigger is created.
+ @param[out] stmt_query after successful return, this string contains
+ well-formed statement for creation this trigger.
- NOTE
+ @note
- Assumes that trigger name is fully qualified.
- NULL-string means the following LEX_STRING instance:
- { str = 0; length = 0 }.
+ { str = 0; length = 0 }.
- In other words, definer_user and definer_host should contain
- simultaneously NULL-strings (non-SUID/old trigger) or valid strings
- (SUID/new trigger).
+ simultaneously NULL-strings (non-SUID/old trigger) or valid strings
+ (SUID/new trigger).
- RETURN VALUE
- False - success
- True - error
+ @retval
+ False success
+ @retval
+ True error
*/
bool Table_triggers_list::create_trigger(THD *thd, TABLE_LIST *tables,
String *stmt_query)
{
LEX *lex= thd->lex;
TABLE *table= tables->table;
- char dir_buff[FN_REFLEN], file_buff[FN_REFLEN], trigname_buff[FN_REFLEN],
- trigname_path[FN_REFLEN];
- LEX_STRING dir, file, trigname_file;
+ char file_buff[FN_REFLEN], trigname_buff[FN_REFLEN];
+ LEX_STRING file, trigname_file;
LEX_STRING *trg_def;
LEX_STRING definer_user;
LEX_STRING definer_host;
@@ -359,17 +560,21 @@ bool Table_triggers_list::create_trigger(THD *thd, TABLE_LIST *tables,
LEX_STRING *trg_definer;
Item_trigger_field *trg_field;
struct st_trigname trigname;
+ LEX_STRING *trg_client_cs_name;
+ LEX_STRING *trg_connection_cl_name;
+ LEX_STRING *trg_db_cl_name;
/* Trigger must be in the same schema as target table. */
- if (my_strcasecmp(table_alias_charset, table->s->db, lex->spname->m_db.str))
+ if (my_strcasecmp(table_alias_charset, table->s->db.str,
+ lex->spname->m_db.str))
{
my_error(ER_TRG_IN_WRONG_SCHEMA, MYF(0));
return 1;
}
/* We don't allow creation of several triggers of the same type yet */
- if (bodies[lex->trg_chistics.event][lex->trg_chistics.action_time])
+ if (bodies[lex->trg_chistics.event][lex->trg_chistics.action_time] != 0)
{
my_error(ER_NOT_SUPPORTED_YET, MYF(0),
"multiple triggers with the same action time"
@@ -456,20 +661,18 @@ bool Table_triggers_list::create_trigger(THD *thd, TABLE_LIST *tables,
sql_create_definition_file() files handles renaming and backup of older
versions
*/
- strxnmov(dir_buff, FN_REFLEN, mysql_data_home, "/", tables->db, "/", NullS);
- dir.length= unpack_filename(dir_buff, dir_buff);
- dir.str= dir_buff;
- file.length= (uint) (strxnmov(file_buff, FN_REFLEN, tables->table_name,
- triggers_file_ext, NullS) - file_buff);
+ file.length= build_table_filename(file_buff, FN_REFLEN - 1,
+ tables->db, tables->table_name,
+ TRG_EXT, 0);
file.str= file_buff;
- trigname_file.length= (uint) (strxnmov(trigname_buff, FN_REFLEN,
- lex->spname->m_name.str,
- trigname_file_ext, NullS) - trigname_buff);
+ trigname_file.length= build_table_filename(trigname_buff, FN_REFLEN-1,
+ tables->db,
+ lex->spname->m_name.str,
+ TRN_EXT, 0);
trigname_file.str= trigname_buff;
- strxnmov(trigname_path, FN_REFLEN, dir_buff, trigname_buff, NullS);
/* Use the filesystem to enforce trigger namespace constraints. */
- if (!access(trigname_path, F_OK))
+ if (!access(trigname_buff, F_OK))
{
my_error(ER_TRG_ALREADY_EXISTS, MYF(0));
return 1;
@@ -478,8 +681,8 @@ bool Table_triggers_list::create_trigger(THD *thd, TABLE_LIST *tables,
trigname.trigger_table.str= tables->table_name;
trigname.trigger_table.length= tables->table_name_length;
- if (sql_create_definition_file(&dir, &trigname_file, &trigname_file_type,
- (gptr)&trigname, trigname_file_parameters))
+ if (sql_create_definition_file(NULL, &trigname_file, &trigname_file_type,
+ (uchar*)&trigname, trigname_file_parameters))
return 1;
/*
@@ -490,16 +693,26 @@ bool Table_triggers_list::create_trigger(THD *thd, TABLE_LIST *tables,
QQ: Hmm... probably we should not care about setting up active thread
mem_root too.
*/
- if (!(trg_def= (LEX_STRING *)alloc_root(&table->mem_root,
- sizeof(LEX_STRING))) ||
+ if (!(trg_def= alloc_lex_string(&table->mem_root)) ||
definitions_list.push_back(trg_def, &table->mem_root) ||
- !(trg_sql_mode= (ulonglong*)alloc_root(&table->mem_root,
- sizeof(ulonglong))) ||
+
+ !(trg_sql_mode= alloc_type<ulonglong>(&table->mem_root)) ||
definition_modes_list.push_back(trg_sql_mode, &table->mem_root) ||
- !(trg_definer= (LEX_STRING*) alloc_root(&table->mem_root,
- sizeof(LEX_STRING))) ||
- definers_list.push_back(trg_definer, &table->mem_root))
+
+ !(trg_definer= alloc_lex_string(&table->mem_root)) ||
+ definers_list.push_back(trg_definer, &table->mem_root) ||
+
+ !(trg_client_cs_name= alloc_lex_string(&table->mem_root)) ||
+ client_cs_names.push_back(trg_client_cs_name, &table->mem_root) ||
+
+ !(trg_connection_cl_name= alloc_lex_string(&table->mem_root)) ||
+ connection_cl_names.push_back(trg_connection_cl_name, &table->mem_root) ||
+
+ !(trg_db_cl_name= alloc_lex_string(&table->mem_root)) ||
+ db_cl_names.push_back(trg_db_cl_name, &table->mem_root))
+ {
goto err_with_cleanup;
+ }
*trg_sql_mode= thd->variables.sql_mode;
@@ -524,8 +737,8 @@ bool Table_triggers_list::create_trigger(THD *thd, TABLE_LIST *tables,
definer_host= lex->definer->host;
trg_definer->str= trg_definer_holder;
- trg_definer->length= (uint) (strxmov(trg_definer->str, definer_user.str, "@",
- definer_host.str, NullS) - trg_definer->str);
+ trg_definer->length= strxmov(trg_definer->str, definer_user.str, "@",
+ definer_host.str, NullS) - trg_definer->str;
}
else
{
@@ -542,6 +755,21 @@ bool Table_triggers_list::create_trigger(THD *thd, TABLE_LIST *tables,
}
/*
+ Fill character set information:
+ - client character set contains charset info only;
+ - connection collation contains pair {character set, collation};
+ - database collation contains pair {character set, collation};
+ */
+
+ lex_string_set(trg_client_cs_name, thd->charset()->csname);
+
+ lex_string_set(trg_connection_cl_name,
+ thd->variables.collation_connection->name);
+
+ lex_string_set(trg_db_cl_name,
+ get_default_db_collation(thd, tables->db)->name);
+
+ /*
Create well-formed trigger definition query. Original query is not
appropriated, because definer-clause can be not truncated.
*/
@@ -558,135 +786,138 @@ bool Table_triggers_list::create_trigger(THD *thd, TABLE_LIST *tables,
append_definer(thd, stmt_query, &definer_user, &definer_host);
}
- stmt_query->append(thd->lex->stmt_definition_begin,
- (uint) ((char *) thd->lex->sphead->m_body_begin -
- thd->lex->stmt_definition_begin +
- thd->lex->sphead->m_body.length));
+ LEX_STRING stmt_definition;
+ stmt_definition.str= (char*) thd->lex->stmt_definition_begin;
+ stmt_definition.length= thd->lex->stmt_definition_end
+ - thd->lex->stmt_definition_begin;
+ trim_whitespace(thd->charset(), & stmt_definition);
+
+ stmt_query->append(stmt_definition.str, stmt_definition.length);
trg_def->str= stmt_query->c_ptr();
trg_def->length= stmt_query->length();
/* Create trigger definition file. */
- if (!sql_create_definition_file(&dir, &file, &triggers_file_type,
- (gptr)this, triggers_file_parameters))
+ if (!sql_create_definition_file(NULL, &file, &triggers_file_type,
+ (uchar*)this, triggers_file_parameters))
return 0;
err_with_cleanup:
- my_delete(trigname_path, MYF(MY_WME));
+ my_delete(trigname_buff, MYF(MY_WME));
return 1;
}
-/*
- Deletes the .TRG file for a table
-
- SYNOPSIS
- rm_trigger_file()
- path - char buffer of size FN_REFLEN to be used
- for constructing path to .TRG file.
- db - table's database name
- table_name - table's name
-
- RETURN VALUE
- False - success
- True - error
+/**
+ Deletes the .TRG file for a table.
+
+ @param path char buffer of size FN_REFLEN to be used
+ for constructing path to .TRG file.
+ @param db table's database name
+ @param table_name table's name
+
+ @retval
+ False success
+ @retval
+ True error
*/
static bool rm_trigger_file(char *path, const char *db,
const char *table_name)
{
- strxnmov(path, FN_REFLEN, mysql_data_home, "/", db, "/", table_name,
- triggers_file_ext, NullS);
- unpack_filename(path, path);
+ build_table_filename(path, FN_REFLEN-1, db, table_name, TRG_EXT, 0);
return my_delete(path, MYF(MY_WME));
}
-/*
- Deletes the .TRN file for a trigger
-
- SYNOPSIS
- rm_trigname_file()
- path - char buffer of size FN_REFLEN to be used
- for constructing path to .TRN file.
- db - trigger's database name
- table_name - trigger's name
-
- RETURN VALUE
- False - success
- True - error
+/**
+ Deletes the .TRN file for a trigger.
+
+ @param path char buffer of size FN_REFLEN to be used
+ for constructing path to .TRN file.
+ @param db trigger's database name
+ @param table_name trigger's name
+
+ @retval
+ False success
+ @retval
+ True error
*/
static bool rm_trigname_file(char *path, const char *db,
const char *trigger_name)
{
- strxnmov(path, FN_REFLEN, mysql_data_home, "/", db, "/", trigger_name,
- trigname_file_ext, NullS);
- unpack_filename(path, path);
+ build_table_filename(path, FN_REFLEN - 1, db, trigger_name, TRN_EXT, 0);
return my_delete(path, MYF(MY_WME));
}
-/*
+/**
Helper function that saves .TRG file for Table_triggers_list object.
- SYNOPSIS
- save_trigger_file()
- triggers Table_triggers_list object for which file should be saved
- db Name of database for subject table
- table_name Name of subject table
+ @param triggers Table_triggers_list object for which file should be saved
+ @param db Name of database for subject table
+ @param table_name Name of subject table
- RETURN VALUE
+ @retval
FALSE Success
+ @retval
TRUE Error
*/
static bool save_trigger_file(Table_triggers_list *triggers, const char *db,
const char *table_name)
{
- char dir_buff[FN_REFLEN], file_buff[FN_REFLEN];
- LEX_STRING dir, file;
-
- strxnmov(dir_buff, FN_REFLEN, mysql_data_home, "/", db, "/", NullS);
- dir.length= unpack_filename(dir_buff, dir_buff);
- dir.str= dir_buff;
- file.length= (uint) (strxnmov(file_buff, FN_REFLEN, table_name, triggers_file_ext,
- NullS) - file_buff);
- file.str= file_buff;
+ char file_buff[FN_REFLEN];
+ LEX_STRING file;
- return sql_create_definition_file(&dir, &file, &triggers_file_type,
- (gptr)triggers, triggers_file_parameters);
+ file.length= build_table_filename(file_buff, FN_REFLEN - 1, db, table_name,
+ TRG_EXT, 0);
+ file.str= file_buff;
+ return sql_create_definition_file(NULL, &file, &triggers_file_type,
+ (uchar*)triggers, triggers_file_parameters);
}
-/*
+/**
Drop trigger for table.
- SYNOPSIS
- drop_trigger()
- thd - current thread context
- (including trigger definition in LEX)
- tables - table list containing one open table for which trigger
- is dropped.
- stmt_query - [OUT] after successful return, this string contains
- well-formed statement for creation this trigger.
-
- RETURN VALUE
- False - success
- True - error
+ @param thd current thread context
+ (including trigger definition in LEX)
+ @param tables table list containing one open table for which trigger
+ is dropped.
+ @param[out] stmt_query after successful return, this string contains
+ well-formed statement for creation this trigger.
+
+ @todo
+ Probably instead of removing .TRG file we should move
+ to archive directory but this should be done as part of
+ parse_file.cc functionality (because we will need it
+ elsewhere).
+
+ @retval
+ False success
+ @retval
+ True error
*/
bool Table_triggers_list::drop_trigger(THD *thd, TABLE_LIST *tables,
String *stmt_query)
{
- LEX *lex= thd->lex;
+ const char *sp_name= thd->lex->spname->m_name.str; // alias
+
LEX_STRING *name;
- List_iterator_fast<LEX_STRING> it_name(names_list);
- List_iterator<LEX_STRING> it_def(definitions_list);
- List_iterator<ulonglong> it_mod(definition_modes_list);
- List_iterator<LEX_STRING> it_definer(definers_list);
char path[FN_REFLEN];
+ List_iterator_fast<LEX_STRING> it_name(names_list);
+
+ List_iterator<ulonglong> it_mod(definition_modes_list);
+ List_iterator<LEX_STRING> it_def(definitions_list);
+ List_iterator<LEX_STRING> it_definer(definers_list);
+ List_iterator<LEX_STRING> it_client_cs_name(client_cs_names);
+ List_iterator<LEX_STRING> it_connection_cl_name(connection_cl_names);
+ List_iterator<LEX_STRING> it_db_cl_name(db_cl_names);
+
stmt_query->append(thd->query, thd->query_length);
while ((name= it_name++))
@@ -694,9 +925,11 @@ bool Table_triggers_list::drop_trigger(THD *thd, TABLE_LIST *tables,
it_def++;
it_mod++;
it_definer++;
+ it_client_cs_name++;
+ it_connection_cl_name++;
+ it_db_cl_name++;
- if (my_strcasecmp(table_alias_charset, lex->spname->m_name.str,
- name->str) == 0)
+ if (my_strcasecmp(table_alias_charset, sp_name, name->str) == 0)
{
/*
Again we don't care much about other things required for
@@ -705,6 +938,9 @@ bool Table_triggers_list::drop_trigger(THD *thd, TABLE_LIST *tables,
it_def.remove();
it_mod.remove();
it_definer.remove();
+ it_client_cs_name.remove();
+ it_connection_cl_name.remove();
+ it_db_cl_name.remove();
if (definitions_list.is_empty())
{
@@ -723,7 +959,7 @@ bool Table_triggers_list::drop_trigger(THD *thd, TABLE_LIST *tables,
return 1;
}
- if (rm_trigname_file(path, tables->db, lex->spname->m_name.str))
+ if (rm_trigname_file(path, tables->db, sp_name))
return 1;
return 0;
}
@@ -746,18 +982,17 @@ Table_triggers_list::~Table_triggers_list()
}
-/*
+/**
Prepare array of Field objects referencing to TABLE::record[1] instead
of record[0] (they will represent OLD.* row values in ON UPDATE trigger
and in ON DELETE trigger which will be called during REPLACE execution).
- SYNOPSIS
- prepare_record1_accessors()
- table - pointer to TABLE object for which we are creating fields.
+ @param table pointer to TABLE object for which we are creating fields.
- RETURN VALUE
- False - success
- True - error
+ @retval
+ False success
+ @retval
+ True error
*/
bool Table_triggers_list::prepare_record1_accessors(TABLE *table)
{
@@ -777,8 +1012,8 @@ bool Table_triggers_list::prepare_record1_accessors(TABLE *table)
if (!(*old_fld= (*fld)->new_field(&table->mem_root, table,
table == (*fld)->table)))
return 1;
- (*old_fld)->move_field((my_ptrdiff_t)(table->record[1] -
- table->record[0]));
+ (*old_fld)->move_field_offset((my_ptrdiff_t)(table->record[1] -
+ table->record[0]));
}
*old_fld= 0;
@@ -786,12 +1021,10 @@ bool Table_triggers_list::prepare_record1_accessors(TABLE *table)
}
-/*
+/**
Adjust Table_triggers_list with new TABLE pointer.
- SYNOPSIS
- set_table()
- new_table - new pointer to TABLE instance
+ @param new_table new pointer to TABLE instance
*/
void Table_triggers_list::set_table(TABLE *new_table)
@@ -805,20 +1038,26 @@ void Table_triggers_list::set_table(TABLE *new_table)
}
-/*
+/**
Check whenever .TRG file for table exist and load all triggers it contains.
- SYNOPSIS
- check_n_load()
- thd - current thread context
- db - table's database name
- table_name - table's name
- table - pointer to table object
- names_only - stop after loading trigger names
-
- RETURN VALUE
- False - success
- True - error
+ @param thd current thread context
+ @param db table's database name
+ @param table_name table's name
+ @param table pointer to table object
+ @param names_only stop after loading trigger names
+
+ @todo
+ A lot of things to do here e.g. how about other funcs and being
+ more paranoical ?
+
+ @todo
+ This could be avoided if there is no triggers for UPDATE and DELETE.
+
+ @retval
+ False success
+ @retval
+ True error
*/
bool Table_triggers_list::check_n_load(THD *thd, const char *db,
@@ -832,9 +1071,8 @@ bool Table_triggers_list::check_n_load(THD *thd, const char *db,
DBUG_ENTER("Table_triggers_list::check_n_load");
- strxnmov(path_buff, FN_REFLEN, mysql_data_home, "/", db, "/", table_name,
- triggers_file_ext, NullS);
- path.length= unpack_filename(path_buff, path_buff);
+ path.length= build_table_filename(path_buff, FN_REFLEN - 1,
+ db, table_name, TRG_EXT, 0);
path.str= path_buff;
// QQ: should we analyze errno somehow ?
@@ -863,11 +1101,15 @@ bool Table_triggers_list::check_n_load(THD *thd, const char *db,
we should initialize the list for safety:
- sql_modes;
- definers;
+ - character sets (client, connection, database);
*/
triggers->definition_modes_list.empty();
triggers->definers_list.empty();
+ triggers->client_cs_names.empty();
+ triggers->connection_cl_names.empty();
+ triggers->db_cl_names.empty();
- if (parser->parse((gptr)triggers, &table->mem_root,
+ if (parser->parse((uchar*)triggers, &table->mem_root,
triggers_file_parameters,
TRG_NUM_REQUIRED_PARAMETERS,
&sql_modes_hook))
@@ -886,8 +1128,7 @@ bool Table_triggers_list::check_n_load(THD *thd, const char *db,
We use one mode (current) for all triggers, because we have not
information about mode in old format.
*/
- if (!(trg_sql_mode= (ulonglong*)alloc_root(&table->mem_root,
- sizeof(ulonglong))))
+ if (!(trg_sql_mode= alloc_type<ulonglong>(&table->mem_root)))
{
DBUG_RETURN(1); // EOM
}
@@ -916,8 +1157,7 @@ bool Table_triggers_list::check_n_load(THD *thd, const char *db,
LEX_STRING *trg_definer;
- if (! (trg_definer= (LEX_STRING*)alloc_root(&table->mem_root,
- sizeof(LEX_STRING))))
+ if (!(trg_definer= alloc_lex_string(&table->mem_root)))
DBUG_RETURN(1); // EOM
trg_definer->str= (char*) "";
@@ -935,10 +1175,85 @@ bool Table_triggers_list::check_n_load(THD *thd, const char *db,
it.rewind();
}
+ if (!triggers->definitions_list.is_empty() &&
+ (triggers->client_cs_names.is_empty() ||
+ triggers->connection_cl_names.is_empty() ||
+ triggers->db_cl_names.is_empty()))
+ {
+ /*
+ It is old file format => we should fill lists of character sets.
+ */
+
+ LEX_STRING *trg_client_cs_name;
+ LEX_STRING *trg_connection_cl_name;
+ LEX_STRING *trg_db_cl_name;
+
+ if (!triggers->client_cs_names.is_empty() ||
+ !triggers->connection_cl_names.is_empty() ||
+ !triggers->db_cl_names.is_empty())
+ {
+ my_error(ER_TRG_CORRUPTED_FILE, MYF(0),
+ (const char *) db,
+ (const char *) table_name);
+
+ DBUG_RETURN(1); // EOM
+ }
+
+ push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ ER_TRG_NO_CREATION_CTX,
+ ER(ER_TRG_NO_CREATION_CTX),
+ (const char*) db,
+ (const char*) table_name);
+
+ if (!(trg_client_cs_name= alloc_lex_string(&table->mem_root)) ||
+ !(trg_connection_cl_name= alloc_lex_string(&table->mem_root)) ||
+ !(trg_db_cl_name= alloc_lex_string(&table->mem_root)))
+ {
+ DBUG_RETURN(1); // EOM
+ }
+
+ /*
+ Backward compatibility: assume that the query is in the current
+ character set.
+ */
+
+ lex_string_set(trg_client_cs_name,
+ thd->variables.character_set_client->csname);
+
+ lex_string_set(trg_connection_cl_name,
+ thd->variables.collation_connection->name);
+
+ lex_string_set(trg_db_cl_name,
+ thd->variables.collation_database->name);
+
+ while (it++)
+ {
+ if (triggers->client_cs_names.push_back(trg_client_cs_name,
+ &table->mem_root) ||
+
+ triggers->connection_cl_names.push_back(trg_connection_cl_name,
+ &table->mem_root) ||
+
+ triggers->db_cl_names.push_back(trg_db_cl_name,
+ &table->mem_root))
+ {
+ DBUG_RETURN(1); // EOM
+ }
+ }
+
+ it.rewind();
+ }
+
DBUG_ASSERT(triggers->definition_modes_list.elements ==
triggers->definitions_list.elements);
DBUG_ASSERT(triggers->definers_list.elements ==
triggers->definitions_list.elements);
+ DBUG_ASSERT(triggers->client_cs_names.elements ==
+ triggers->definitions_list.elements);
+ DBUG_ASSERT(triggers->connection_cl_names.elements ==
+ triggers->definitions_list.elements);
+ DBUG_ASSERT(triggers->db_cl_names.elements ==
+ triggers->definitions_list.elements);
table->triggers= triggers;
@@ -951,6 +1266,9 @@ bool Table_triggers_list::check_n_load(THD *thd, const char *db,
List_iterator_fast<ulonglong> itm(triggers->definition_modes_list);
List_iterator_fast<LEX_STRING> it_definer(triggers->definers_list);
+ List_iterator_fast<LEX_STRING> it_client_cs_name(triggers->client_cs_names);
+ List_iterator_fast<LEX_STRING> it_connection_cl_name(triggers->connection_cl_names);
+ 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;
@@ -960,7 +1278,7 @@ bool Table_triggers_list::check_n_load(THD *thd, const char *db,
save_db.str= thd->db;
save_db.length= thd->db_length;
- thd->reset_db((char*) db, (uint) strlen(db));
+ thd->reset_db((char*) db, strlen(db));
while ((trg_create_str= it++))
{
trg_sql_mode= itm++;
@@ -968,15 +1286,22 @@ bool Table_triggers_list::check_n_load(THD *thd, const char *db,
thd->variables.sql_mode= (ulong)*trg_sql_mode;
- Parser_state parser_state(thd, trg_create_str->str,
+ Parser_state parser_state(thd,
+ trg_create_str->str,
trg_create_str->length);
- thd->m_parser_state= &parser_state;
+
+ Trigger_creation_ctx *creation_ctx=
+ Trigger_creation_ctx::create(thd,
+ db,
+ table_name,
+ it_client_cs_name++,
+ it_connection_cl_name++,
+ it_db_cl_name++);
+
lex_start(thd);
thd->spcont= NULL;
- int err= MYSQLparse((void *)thd);
- thd->m_parser_state= NULL;
- if (err || thd->is_fatal_error)
+ if (parse_sql(thd, & parser_state, creation_ctx))
{
/* Currently sphead is always deleted in case of a parse error */
DBUG_ASSERT(lex.sphead == 0);
@@ -994,8 +1319,11 @@ bool Table_triggers_list::check_n_load(THD *thd, const char *db,
lex.sphead->set_info(0, 0, &lex.sp_chistics, (ulong) *trg_sql_mode);
- triggers->bodies[lex.trg_chistics.event]
- [lex.trg_chistics.action_time]= lex.sphead;
+ 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;
if (!trg_definer->length)
{
@@ -1015,7 +1343,7 @@ bool Table_triggers_list::check_n_load(THD *thd, const char *db,
schema.
*/
- lex.sphead->set_definer("", 0);
+ lex.sphead->set_definer((char*) "", 0);
/*
Triggers without definer information are executed under the
@@ -1031,21 +1359,36 @@ bool Table_triggers_list::check_n_load(THD *thd, const char *db,
&table->mem_root))
goto err_with_lex_cleanup;
- if (!(on_table_name= (LEX_STRING*) alloc_root(&table->mem_root,
- sizeof(LEX_STRING))))
+ if (!(on_table_name= alloc_lex_string(&table->mem_root)))
goto err_with_lex_cleanup;
- *on_table_name= lex.ident;
+
+ on_table_name->str= (char*) lex.raw_trg_on_table_name_begin;
+ on_table_name->length= lex.raw_trg_on_table_name_end
+ - lex.raw_trg_on_table_name_begin;
+
if (triggers->on_table_names_list.push_back(on_table_name, &table->mem_root))
goto err_with_lex_cleanup;
-
+#ifndef DBUG_OFF
/*
Let us check that we correctly update trigger definitions when we
rename tables with triggers.
+
+ In special cases like "RENAME TABLE `#mysql50#somename` TO `somename`"
+ or "ALTER DATABASE `#mysql50#somename` UPGRADE DATA DIRECTORY NAME"
+ we might be given table or database name with "#mysql50#" prefix (and
+ trigger's definiton contains un-prefixed version of the same name).
+ To remove this prefix we use check_n_cut_mysql50_prefix().
*/
- DBUG_ASSERT(!my_strcasecmp(table_alias_charset, lex.query_tables->db, db) &&
- !my_strcasecmp(table_alias_charset, lex.query_tables->table_name,
- table_name));
+ char fname[NAME_LEN + 1];
+ DBUG_ASSERT((!my_strcasecmp(table_alias_charset, lex.query_tables->db, db) ||
+ (check_n_cut_mysql50_prefix(db, fname, sizeof(fname)) &&
+ !my_strcasecmp(table_alias_charset, lex.query_tables->db, fname))) &&
+ (!my_strcasecmp(table_alias_charset, lex.query_tables->table_name,
+ table_name) ||
+ (check_n_cut_mysql50_prefix(table_name, fname, sizeof(fname)) &&
+ !my_strcasecmp(table_alias_charset, lex.query_tables->table_name, fname))));
+#endif
if (names_only)
{
lex_end(&lex);
@@ -1074,7 +1417,7 @@ bool Table_triggers_list::check_n_load(THD *thd, const char *db,
trg_field;
trg_field= trg_field->next_trg_field)
{
- trg_field->setup_field(thd, table,
+ trg_field->setup_field(thd, table,
&triggers->subject_table_grants[lex.trg_chistics.event]
[lex.trg_chistics.action_time]);
}
@@ -1103,7 +1446,7 @@ err_with_lex_cleanup:
be merged into .FRM anyway.
*/
my_error(ER_WRONG_OBJECT, MYF(0),
- table_name, triggers_file_ext+1, "TRIGGER");
+ table_name, TRG_EXT + 1, "TRIGGER");
DBUG_RETURN(1);
}
@@ -1111,24 +1454,23 @@ err_with_lex_cleanup:
}
-/*
- Obtains and returns trigger metadata
-
- SYNOPSIS
- get_trigger_info()
- thd - current thread context
- event - trigger event type
- time_type - trigger action time
- name - returns name of trigger
- stmt - returns statement of trigger
- sql_mode - returns sql_mode of trigger
- definer_user - returns definer/creator of trigger. The caller is
- responsible to allocate enough space for storing definer
- information.
-
- RETURN VALUE
- False - success
- True - error
+/**
+ Obtains and returns trigger metadata.
+
+ @param thd current thread context
+ @param event trigger event type
+ @param time_type trigger action time
+ @param trigger_name returns name of trigger
+ @param trigger_stmt returns statement of trigger
+ @param sql_mode returns sql_mode of trigger
+ @param definer returns definer/creator of trigger. The caller is
+ responsible to allocate enough space for storing
+ definer information.
+
+ @retval
+ False success
+ @retval
+ True error
*/
bool Table_triggers_list::get_trigger_info(THD *thd, trg_event_type event,
@@ -1136,14 +1478,20 @@ bool Table_triggers_list::get_trigger_info(THD *thd, trg_event_type event,
LEX_STRING *trigger_name,
LEX_STRING *trigger_stmt,
ulong *sql_mode,
- LEX_STRING *definer)
+ LEX_STRING *definer,
+ LEX_STRING *client_cs_name,
+ LEX_STRING *connection_cl_name,
+ LEX_STRING *db_cl_name)
{
sp_head *body;
DBUG_ENTER("get_trigger_info");
if ((body= bodies[event][time_type]))
{
+ Stored_program_creation_ctx *creation_ctx=
+ bodies[event][time_type]->get_creation_ctx();
+
*trigger_name= body->m_name;
- *trigger_stmt= body->m_body;
+ *trigger_stmt= body->m_body_utf8;
*sql_mode= body->m_sql_mode;
if (body->m_chistics->suid == SP_IS_NOT_SUID)
@@ -1153,111 +1501,155 @@ bool Table_triggers_list::get_trigger_info(THD *thd, trg_event_type event,
}
else
{
- definer->length= (uint) (strxmov(definer->str, body->m_definer_user.str, "@",
- body->m_definer_host.str, NullS) - definer->str);
+ definer->length= strxmov(definer->str, body->m_definer_user.str, "@",
+ body->m_definer_host.str, NullS) - definer->str;
}
+ lex_string_set(client_cs_name,
+ creation_ctx->get_client_cs()->csname);
+
+ lex_string_set(connection_cl_name,
+ creation_ctx->get_connection_cl()->name);
+
+ lex_string_set(db_cl_name,
+ creation_ctx->get_db_cl()->name);
+
DBUG_RETURN(0);
}
DBUG_RETURN(1);
}
-/*
+void Table_triggers_list::get_trigger_info(THD *thd,
+ int trigger_idx,
+ LEX_STRING *trigger_name,
+ ulonglong *sql_mode,
+ LEX_STRING *sql_original_stmt,
+ LEX_STRING *client_cs_name,
+ LEX_STRING *connection_cl_name,
+ LEX_STRING *db_cl_name)
+{
+ List_iterator_fast<LEX_STRING> it_trigger_name(names_list);
+ List_iterator_fast<ulonglong> it_sql_mode(definition_modes_list);
+ List_iterator_fast<LEX_STRING> it_sql_orig_stmt(definitions_list);
+ List_iterator_fast<LEX_STRING> it_client_cs_name(client_cs_names);
+ List_iterator_fast<LEX_STRING> it_connection_cl_name(connection_cl_names);
+ List_iterator_fast<LEX_STRING> it_db_cl_name(db_cl_names);
+
+ for (int i = 0; i < trigger_idx; ++i)
+ {
+ it_trigger_name.next_fast();
+ it_sql_mode.next_fast();
+ it_sql_orig_stmt.next_fast();
+
+ it_client_cs_name.next_fast();
+ it_connection_cl_name.next_fast();
+ it_db_cl_name.next_fast();
+ }
+
+ *trigger_name= *(it_trigger_name++);
+ *sql_mode= *(it_sql_mode++);
+ *sql_original_stmt= *(it_sql_orig_stmt++);
+
+ *client_cs_name= *(it_client_cs_name++);
+ *connection_cl_name= *(it_connection_cl_name++);
+ *db_cl_name= *(it_db_cl_name++);
+}
+
+
+int Table_triggers_list::find_trigger_by_name(const LEX_STRING *trg_name)
+{
+ List_iterator_fast<LEX_STRING> it(names_list);
+
+ for (int i = 0; ; ++i)
+ {
+ LEX_STRING *cur_name= it++;
+
+ if (!cur_name)
+ return -1;
+
+ if (strcmp(cur_name->str, trg_name->str) == 0)
+ return i;
+ }
+}
+
+/**
Find trigger's table from trigger identifier and add it to
the statement table list.
- SYNOPSIS
- mysql_table_for_trigger()
- thd - current thread context
- trig - identifier for trigger
- if_exists - treat a not existing trigger as a warning if TRUE
- table - pointer to TABLE_LIST object for the table trigger (output)
-
- RETURN VALUE
- 0 Success
- 1 Error
+ @param[in] thd Thread context.
+ @param[in] trg_name Trigger name.
+ @param[in] if_exists TRUE if SQL statement contains "IF EXISTS" clause.
+ That means a warning instead of error should be
+ thrown if trigger with given name does not exist.
+ @param[out] table Pointer to TABLE_LIST object for the
+ table trigger.
+
+ @return Operation status
+ @retval FALSE On success.
+ @retval TRUE Otherwise.
*/
-int
-add_table_for_trigger(THD *thd, sp_name *trig, bool if_exists,
- TABLE_LIST **table)
+bool add_table_for_trigger(THD *thd,
+ const sp_name *trg_name,
+ bool if_exists,
+ TABLE_LIST **table)
{
LEX *lex= thd->lex;
- char path_buff[FN_REFLEN];
- LEX_STRING path;
- File_parser *parser;
- struct st_trigname trigname;
- Handle_old_incorrect_trigger_table_hook trigger_table_hook(
- path_buff, &trigname.trigger_table);
-
+ char trn_path_buff[FN_REFLEN];
+ LEX_STRING trn_path= { trn_path_buff, 0 };
+ LEX_STRING tbl_name;
+
DBUG_ENTER("add_table_for_trigger");
- DBUG_ASSERT(table != NULL);
- strxnmov(path_buff, FN_REFLEN, mysql_data_home, "/", trig->m_db.str, "/",
- trig->m_name.str, trigname_file_ext, NullS);
- path.length= unpack_filename(path_buff, path_buff);
- path.str= path_buff;
+ build_trn_path(thd, trg_name, &trn_path);
- if (access(path_buff, F_OK))
+ if (check_trn_exists(&trn_path))
{
if (if_exists)
{
push_warning_printf(thd,
- MYSQL_ERROR::WARN_LEVEL_NOTE,
- ER_TRG_DOES_NOT_EXIST,
- ER(ER_TRG_DOES_NOT_EXIST));
+ MYSQL_ERROR::WARN_LEVEL_NOTE,
+ ER_TRG_DOES_NOT_EXIST,
+ ER(ER_TRG_DOES_NOT_EXIST));
+
*table= NULL;
- DBUG_RETURN(0);
+
+ DBUG_RETURN(FALSE);
}
my_error(ER_TRG_DOES_NOT_EXIST, MYF(0));
- DBUG_RETURN(1);
- }
-
- if (!(parser= sql_parse_prepare(&path, thd->mem_root, 1)))
- DBUG_RETURN(1);
-
- if (!is_equal(&trigname_file_type, parser->type()))
- {
- my_error(ER_WRONG_OBJECT, MYF(0), trig->m_name.str, trigname_file_ext+1,
- "TRIGGERNAME");
- DBUG_RETURN(1);
+ DBUG_RETURN(TRUE);
}
- if (parser->parse((gptr)&trigname, thd->mem_root,
- trigname_file_parameters, 1,
- &trigger_table_hook))
- DBUG_RETURN(1);
+ if (load_table_name_for_trigger(thd, trg_name, &trn_path, &tbl_name))
+ DBUG_RETURN(TRUE);
/* We need to reset statement table list to be PS/SP friendly. */
lex->query_tables= 0;
lex->query_tables_last= &lex->query_tables;
- *table= sp_add_to_query_tables(thd, lex, trig->m_db.str,
- trigname.trigger_table.str, TL_IGNORE);
- if (! *table)
- DBUG_RETURN(1);
+ *table= sp_add_to_query_tables(thd, lex, trg_name->m_db.str,
+ tbl_name.str, TL_IGNORE);
- DBUG_RETURN(0);
+ DBUG_RETURN(*table ? FALSE : TRUE);
}
-/*
+/**
Drop all triggers for table.
- SYNOPSIS
- drop_all_triggers()
- thd - current thread context
- db - schema for table
- name - name for table
+ @param thd current thread context
+ @param db schema for table
+ @param name name for table
- NOTE
+ @note
The calling thread should hold the LOCK_open mutex;
- RETURN VALUE
- False - success
- True - error
+ @retval
+ False success
+ @retval
+ True error
*/
bool Table_triggers_list::drop_all_triggers(THD *thd, char *db, char *name)
@@ -1270,8 +1662,6 @@ bool Table_triggers_list::drop_all_triggers(THD *thd, char *db, char *name)
bzero(&table, sizeof(table));
init_alloc_root(&table.mem_root, 8192, 0);
- safe_mutex_assert_owner(&LOCK_open);
-
if (Table_triggers_list::check_n_load(thd, db, name, &table, 1))
{
result= 1;
@@ -1309,25 +1699,26 @@ end:
}
-/*
+/**
Update .TRG file after renaming triggers' subject table
(change name of table in triggers' definitions).
- SYNOPSIS
- change_table_name_in_triggers()
- thd Thread context
- db_name Database of subject table
- old_table_name Old subject table's name
- new_table_name New subject table's name
+ @param thd Thread context
+ @param old_db_name Old database of subject table
+ @param new_db_name New database of subject table
+ @param old_table_name Old subject table's name
+ @param new_table_name New subject table's name
- RETURN VALUE
+ @retval
FALSE Success
+ @retval
TRUE Failure
*/
bool
Table_triggers_list::change_table_name_in_triggers(THD *thd,
- const char *db_name,
+ const char *old_db_name,
+ const char *new_db_name,
LEX_STRING *old_table_name,
LEX_STRING *new_table_name)
{
@@ -1337,7 +1728,7 @@ Table_triggers_list::change_table_name_in_triggers(THD *thd,
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);
- uint on_q_table_name_len, before_on_len;
+ size_t on_q_table_name_len, before_on_len;
String buff;
DBUG_ASSERT(definitions_list.elements == on_table_names_list.elements &&
@@ -1350,7 +1741,12 @@ Table_triggers_list::change_table_name_in_triggers(THD *thd,
/* Construct CREATE TRIGGER statement with new table name. */
buff.length(0);
- before_on_len= (uint) (on_table_name->str - def->str);
+
+ /* WARNING: 'on_table_name' is supposed to point inside 'def' */
+ DBUG_ASSERT(on_table_name->str > def->str);
+ DBUG_ASSERT(on_table_name->str < (def->str + def->length));
+ before_on_len= on_table_name->str - def->str;
+
buff.append(def->str, before_on_len);
buff.append(STRING_WITH_LEN("ON "));
append_identifier(thd, &buff, new_table_name->str, new_table_name->length);
@@ -1362,8 +1758,8 @@ Table_triggers_list::change_table_name_in_triggers(THD *thd,
It is OK to allocate some memory on table's MEM_ROOT since this
table instance will be thrown out at the end of rename anyway.
*/
- new_def.str= memdup_root(&trigger_table->mem_root, buff.ptr(),
- buff.length());
+ new_def.str= (char*) memdup_root(&trigger_table->mem_root, buff.ptr(),
+ buff.length());
new_def.length= buff.length();
on_table_name->str= new_def.str + before_on_len;
on_table_name->length= on_q_table_name_len;
@@ -1375,86 +1771,92 @@ Table_triggers_list::change_table_name_in_triggers(THD *thd,
if (thd->is_fatal_error)
return TRUE; /* OOM */
- if (save_trigger_file(this, db_name, new_table_name->str))
+ if (save_trigger_file(this, new_db_name, new_table_name->str))
return TRUE;
- if (rm_trigger_file(path_buff, db_name, old_table_name->str))
+ if (rm_trigger_file(path_buff, old_db_name, old_table_name->str))
{
- (void) rm_trigger_file(path_buff, db_name, new_table_name->str);
+ (void) rm_trigger_file(path_buff, new_db_name, new_table_name->str);
return TRUE;
}
return FALSE;
}
-/*
- Iterate though Table_triggers_list::names_list list and update .TRN files
- after renaming triggers' subject table.
+/**
+ Iterate though Table_triggers_list::names_list list and update
+ .TRN files after renaming triggers' subject table.
- SYNOPSIS
- change_table_name_in_trignames()
- db_name Database of subject table
- new_table_name New subject table's name
- stopper Pointer to Table_triggers_list::names_list at
- which we should stop updating.
+ @param old_db_name Old database of subject table
+ @param new_db_name New database of subject table
+ @param new_table_name New subject table's name
+ @param stopper Pointer to Table_triggers_list::names_list at
+ which we should stop updating.
- RETURN VALUE
+ @retval
0 Success
+ @retval
non-0 Failure, pointer to Table_triggers_list::names_list element
- for which update failed.
+ for which update failed.
*/
LEX_STRING*
-Table_triggers_list::change_table_name_in_trignames(const char *db_name,
+Table_triggers_list::change_table_name_in_trignames(const char *old_db_name,
+ const char *new_db_name,
LEX_STRING *new_table_name,
LEX_STRING *stopper)
{
- char dir_buff[FN_REFLEN], trigname_buff[FN_REFLEN];
+ char trigname_buff[FN_REFLEN];
struct st_trigname trigname;
- LEX_STRING dir, trigname_file;
+ LEX_STRING trigname_file;
LEX_STRING *trigger;
List_iterator_fast<LEX_STRING> it_name(names_list);
- strxnmov(dir_buff, FN_REFLEN, mysql_data_home, "/", db_name, "/", NullS);
- dir.length= unpack_filename(dir_buff, dir_buff);
- dir.str= dir_buff;
-
while ((trigger= it_name++) != stopper)
{
- trigname_file.length= (uint) (strxnmov(trigname_buff, FN_REFLEN, trigger->str,
- trigname_file_ext, NullS) - trigname_buff);
+ trigname_file.length= build_table_filename(trigname_buff, FN_REFLEN-1,
+ new_db_name, trigger->str,
+ TRN_EXT, 0);
trigname_file.str= trigname_buff;
trigname.trigger_table= *new_table_name;
- if (sql_create_definition_file(&dir, &trigname_file, &trigname_file_type,
- (gptr)&trigname, trigname_file_parameters))
+ if (sql_create_definition_file(NULL, &trigname_file, &trigname_file_type,
+ (uchar*)&trigname, trigname_file_parameters))
return trigger;
+
+ /* Remove stale .TRN file in case of database upgrade */
+ if (old_db_name)
+ {
+ if (rm_trigname_file(trigname_buff, old_db_name, trigger->str))
+ {
+ (void) rm_trigname_file(trigname_buff, new_db_name, trigger->str);
+ return trigger;
+ }
+ }
}
return 0;
}
-/*
+/**
Update .TRG and .TRN files after renaming triggers' subject table.
- SYNOPSIS
- change_table_name()
- thd Thread context
- db Old database of subject table
- old_table Old name of subject table
- new_db New database for subject table
- new_table New name of subject table
+ @param[in,out] thd Thread context
+ @param[in] db Old database 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
- NOTE
+ @note
This method tries to leave trigger related files in consistent state,
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.
- RETURN VALUE
- FALSE Success
- TRUE Error
+ @retval FALSE Success
+ @retval TRUE Error
*/
bool Table_triggers_list::change_table_name(THD *thd, const char *db,
@@ -1464,13 +1866,26 @@ bool Table_triggers_list::change_table_name(THD *thd, const char *db,
{
TABLE table;
bool result= 0;
+ bool upgrading50to51= FALSE;
LEX_STRING *err_trigname;
DBUG_ENTER("change_table_name");
bzero(&table, sizeof(table));
init_alloc_root(&table.mem_root, 8192, 0);
- safe_mutex_assert_owner(&LOCK_open);
+ /*
+ 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.
+ */
+#ifndef DBUG_OFF
+ uchar key[MAX_DBKEY_LENGTH];
+ uint key_length= (uint) (strmov(strmov((char*)&key[0], db)+1,
+ old_table)-(char*)&key[0])+1;
+
+ if (!is_table_name_exclusively_locked_by_this_thread(thd, key, key_length))
+ safe_mutex_assert_owner(&LOCK_open);
+#endif
DBUG_ASSERT(my_strcasecmp(table_alias_charset, db, new_db) ||
my_strcasecmp(table_alias_charset, old_table, new_table));
@@ -1482,21 +1897,34 @@ bool Table_triggers_list::change_table_name(THD *thd, const char *db,
}
if (table.triggers)
{
- LEX_STRING_WITH_INIT old_table_name(old_table, (uint) strlen(old_table));
- LEX_STRING_WITH_INIT new_table_name(new_table, (uint) strlen(new_table));
+ LEX_STRING old_table_name= { (char *) old_table, strlen(old_table) };
+ LEX_STRING new_table_name= { (char *) new_table, strlen(new_table) };
/*
Since triggers should be in the same schema as their subject tables
moving table with them between two schemas raises too many questions.
(E.g. what should happen if in new schema we already have trigger
with same name ?).
+
+ In case of "ALTER DATABASE `#mysql50#db1` UPGRADE DATA DIRECTORY NAME"
+ we will be given table name with "#mysql50#" prefix
+ To remove this prefix we use check_n_cut_mysql50_prefix().
*/
if (my_strcasecmp(table_alias_charset, db, new_db))
{
- my_error(ER_TRG_IN_WRONG_SCHEMA, MYF(0));
- result= 1;
- goto end;
+ char dbname[NAME_LEN + 1];
+ if (check_n_cut_mysql50_prefix(db, dbname, sizeof(dbname)) &&
+ !my_strcasecmp(table_alias_charset, dbname, new_db))
+ {
+ upgrading50to51= TRUE;
+ }
+ else
+ {
+ my_error(ER_TRG_IN_WRONG_SCHEMA, MYF(0));
+ result= 1;
+ goto end;
+ }
}
- if (table.triggers->change_table_name_in_triggers(thd, db,
+ if (table.triggers->change_table_name_in_triggers(thd, db, new_db,
&old_table_name,
&new_table_name))
{
@@ -1504,7 +1932,8 @@ bool Table_triggers_list::change_table_name(THD *thd, const char *db,
goto end;
}
if ((err_trigname= table.triggers->change_table_name_in_trignames(
- db, &new_table_name, 0)))
+ upgrading50to51 ? db : NULL,
+ new_db, &new_table_name, 0)))
{
/*
If we were unable to update one of .TRN files properly we will
@@ -1512,16 +1941,17 @@ bool Table_triggers_list::change_table_name(THD *thd, const char *db,
We assume that we will be able to undo our changes without errors
(we can't do much if there will be an error anyway).
*/
- (void) table.triggers->change_table_name_in_trignames(db,
- &old_table_name,
- err_trigname);
- (void) table.triggers->change_table_name_in_triggers(thd, db,
- &new_table_name,
- &old_table_name);
+ (void) table.triggers->change_table_name_in_trignames(
+ upgrading50to51 ? new_db : NULL, db,
+ &old_table_name, err_trigname);
+ (void) table.triggers->change_table_name_in_triggers(
+ thd, db, new_db,
+ &new_table_name, &old_table_name);
result= 1;
goto end;
}
}
+
end:
delete table.triggers;
free_root(&table.mem_root, MYF(0));
@@ -1529,61 +1959,79 @@ end:
}
-bool Table_triggers_list::process_triggers(THD *thd, trg_event_type event,
+/**
+ Execute trigger for given (event, time) pair.
+
+ The operation executes trigger for the specified event (insert, update,
+ delete) and time (after, before) if it is set.
+
+ @param thd
+ @param event
+ @param time_type
+ @param old_row_is_record1
+
+ @return Error status.
+ @retval FALSE on success.
+ @retval TRUE on error.
+*/
+
+bool Table_triggers_list::process_triggers(THD *thd,
+ trg_event_type event,
trg_action_time_type time_type,
bool old_row_is_record1)
{
- bool err_status= FALSE;
+ bool err_status;
+ Sub_statement_state statement_state;
sp_head *sp_trigger= bodies[event][time_type];
- if (sp_trigger)
- {
- Sub_statement_state statement_state;
+ if (sp_trigger == NULL)
+ return FALSE;
- if (old_row_is_record1)
- {
- old_field= record1_field;
- new_field= trigger_table->field;
- }
- else
- {
- new_field= record1_field;
- old_field= trigger_table->field;
- }
- /*
- This trigger must have been processed by the pre-locking
- algorithm.
- */
- DBUG_ASSERT(trigger_table->pos_in_table_list->trg_event_map &
- static_cast<uint>(1 << static_cast<int>(event)));
-
- thd->reset_sub_statement_state(&statement_state, SUB_STMT_TRIGGER);
- err_status= sp_trigger->execute_trigger
- (thd, trigger_table->s->db, trigger_table->s->table_name,
- &subject_table_grants[event][time_type]);
- thd->restore_sub_statement_state(&statement_state);
+ if (old_row_is_record1)
+ {
+ old_field= record1_field;
+ new_field= trigger_table->field;
}
+ else
+ {
+ new_field= record1_field;
+ old_field= trigger_table->field;
+ }
+ /*
+ This trigger must have been processed by the pre-locking
+ algorithm.
+ */
+ DBUG_ASSERT(trigger_table->pos_in_table_list->trg_event_map &
+ static_cast<uint>(1 << static_cast<int>(event)));
+
+ thd->reset_sub_statement_state(&statement_state, SUB_STMT_TRIGGER);
+
+ err_status=
+ sp_trigger->execute_trigger(thd,
+ &trigger_table->s->db,
+ &trigger_table->s->table_name,
+ &subject_table_grants[event][time_type]);
+
+ thd->restore_sub_statement_state(&statement_state);
return err_status;
}
-/*
- Mark fields of subject table which we read/set in its triggers as such.
-
- SYNOPSIS
- mark_fields_used()
- thd Current thread context
- event Type of event triggers for which we are going to ins
-
- DESCRIPTION
- This method marks fields of subject table which are read/set in its
- triggers as such (by setting Field::query_id equal to THD::query_id)
- and thus informs handler that values for these fields should be
- retrieved/stored during execution of statement.
+/**
+ Mark fields of subject table which we read/set in its triggers
+ as such.
+
+ This method marks fields of subject table which are read/set in its
+ triggers as such (by properly updating TABLE::read_set/write_set)
+ and thus informs handler that values for these fields should be
+ retrieved/stored during execution of statement.
+
+ @param thd Current thread context
+ @param event Type of event triggers for which we are going to inspect
*/
-void Table_triggers_list::mark_fields_used(THD *thd, trg_event_type event)
+void Table_triggers_list::mark_fields_used(trg_event_type event)
{
int action_time;
Item_trigger_field *trg_field;
@@ -1595,61 +2043,34 @@ void Table_triggers_list::mark_fields_used(THD *thd, trg_event_type event)
{
/* We cannot mark fields which does not present in table. */
if (trg_field->field_idx != (uint)-1)
- trigger_table->field[trg_field->field_idx]->query_id = thd->query_id;
+ {
+ bitmap_set_bit(trigger_table->read_set, trg_field->field_idx);
+ if (trg_field->get_settable_routine_parameter())
+ bitmap_set_bit(trigger_table->write_set, trg_field->field_idx);
+ }
}
}
+ trigger_table->file->column_bitmaps_signal();
}
-/*
- Check if field of subject table can be changed in before update trigger.
-
- SYNOPSIS
- is_updated_in_before_update_triggers()
- field Field object for field to be checked
-
- NOTE
- Field passed to this function should be bound to the same
- TABLE object as Table_triggers_list.
-
- RETURN VALUE
- TRUE Field is changed
- FALSE Otherwise
-*/
+/**
+ Trigger BUG#14090 compatibility hook.
-bool Table_triggers_list::is_updated_in_before_update_triggers(Field *fld)
-{
- Item_trigger_field *trg_fld;
- for (trg_fld= trigger_fields[TRG_EVENT_UPDATE][TRG_ACTION_BEFORE];
- trg_fld != 0;
- trg_fld= trg_fld->next_trg_field)
- {
- if (trg_fld->get_settable_routine_parameter() &&
- trg_fld->field_idx != (uint)-1 &&
- trigger_table->field[trg_fld->field_idx]->eq(fld))
- return TRUE;
- }
- return FALSE;
-}
+ @param[in,out] unknown_key reference on the line with unknown
+ parameter and the parsing point
+ @param[in] base base address for parameter writing
+ (structure like TABLE)
+ @param[in] mem_root MEM_ROOT for parameters allocation
+ @param[in] end the end of the configuration
+ @note
+ NOTE: this hook process back compatibility for incorrectly written
+ sql_modes parameter (see BUG#14090).
-/*
- Trigger BUG#14090 compatibility hook
-
- SYNOPSIS
- Handle_old_incorrect_sql_modes_hook::process_unknown_string()
- unknown_key [in/out] reference on the line with unknown
- parameter and the parsing point
- base [in] base address for parameter writing (structure
- like TABLE)
- mem_root [in] MEM_ROOT for parameters allocation
- end [in] the end of the configuration
-
- NOTE: this hook process back compatibility for incorrectly written
- sql_modes parameter (see BUG#14090).
-
- RETURN
+ @retval
FALSE OK
+ @retval
TRUE Error
*/
@@ -1657,12 +2078,12 @@ bool Table_triggers_list::is_updated_in_before_update_triggers(Field *fld)
bool
Handle_old_incorrect_sql_modes_hook::process_unknown_string(char *&unknown_key,
- gptr base,
+ uchar* base,
MEM_ROOT *mem_root,
char *end)
{
DBUG_ENTER("Handle_old_incorrect_sql_modes_hook::process_unknown_string");
- DBUG_PRINT("info", ("unknown key:%60s", unknown_key));
+ DBUG_PRINT("info", ("unknown key: %60s", unknown_key));
if (unknown_key + INVALID_SQL_MODES_LENGTH + 1 < end &&
unknown_key[INVALID_SQL_MODES_LENGTH] == '=' &&
@@ -1691,20 +2112,19 @@ Handle_old_incorrect_sql_modes_hook::process_unknown_string(char *&unknown_key,
DBUG_RETURN(FALSE);
}
-/*
+#define INVALID_TRIGGER_TABLE_LENGTH 15
+
+/**
Trigger BUG#15921 compatibility hook. For details see
Handle_old_incorrect_sql_modes_hook::process_unknown_string().
*/
-
-#define INVALID_TRIGGER_TABLE_LENGTH 15
-
bool
Handle_old_incorrect_trigger_table_hook::
-process_unknown_string(char *&unknown_key, gptr base, MEM_ROOT *mem_root,
+process_unknown_string(char *&unknown_key, uchar* base, MEM_ROOT *mem_root,
char *end)
{
DBUG_ENTER("Handle_old_incorrect_trigger_table_hook::process_unknown_string");
- DBUG_PRINT("info", ("unknown key:%60s", unknown_key));
+ DBUG_PRINT("info", ("unknown key: %60s", unknown_key));
if (unknown_key + INVALID_TRIGGER_TABLE_LENGTH + 1 < end &&
unknown_key[INVALID_TRIGGER_TABLE_LENGTH] == '=' &&
@@ -1731,3 +2151,95 @@ process_unknown_string(char *&unknown_key, gptr base, MEM_ROOT *mem_root,
}
DBUG_RETURN(FALSE);
}
+
+
+/**
+ Contruct path to TRN-file.
+
+ @param thd[in] Thread context.
+ @param trg_name[in] Trigger name.
+ @param trn_path[out] Variable to store constructed path
+*/
+
+void build_trn_path(THD *thd, const sp_name *trg_name, LEX_STRING *trn_path)
+{
+ /* Construct path to the TRN-file. */
+
+ trn_path->length= build_table_filename(trn_path->str,
+ FN_REFLEN - 1,
+ trg_name->m_db.str,
+ trg_name->m_name.str,
+ TRN_EXT,
+ 0);
+}
+
+
+/**
+ Check if TRN-file exists.
+
+ @return
+ @retval TRUE if TRN-file does not exist.
+ @retval FALSE if TRN-file exists.
+*/
+
+bool check_trn_exists(const LEX_STRING *trn_path)
+{
+ return access(trn_path->str, F_OK) != 0;
+}
+
+
+/**
+ Retrieve table name for given trigger.
+
+ @param thd[in] Thread context.
+ @param trg_name[in] Trigger name.
+ @param trn_path[in] Path to the corresponding TRN-file.
+ @param tbl_name[out] Variable to store retrieved table name.
+
+ @return Error status.
+ @retval FALSE on success.
+ @retval TRUE if table name could not be retrieved.
+*/
+
+bool load_table_name_for_trigger(THD *thd,
+ const sp_name *trg_name,
+ const LEX_STRING *trn_path,
+ LEX_STRING *tbl_name)
+{
+ File_parser *parser;
+ struct st_trigname trn_data;
+
+ Handle_old_incorrect_trigger_table_hook trigger_table_hook(
+ trn_path->str,
+ &trn_data.trigger_table);
+
+ DBUG_ENTER("load_table_name_for_trigger");
+
+ /* Parse the TRN-file. */
+
+ if (!(parser= sql_parse_prepare(trn_path, thd->mem_root, TRUE)))
+ DBUG_RETURN(TRUE);
+
+ if (!is_equal(&trigname_file_type, parser->type()))
+ {
+ my_error(ER_WRONG_OBJECT, MYF(0),
+ trg_name->m_name.str,
+ TRN_EXT + 1,
+ "TRIGGERNAME");
+
+ DBUG_RETURN(TRUE);
+ }
+
+ if (parser->parse((uchar*) &trn_data, thd->mem_root,
+ trigname_file_parameters, 1,
+ &trigger_table_hook))
+ DBUG_RETURN(TRUE);
+
+ /* Copy trigger table name. */
+
+ *tbl_name= trn_data.trigger_table;
+
+ /* That's all. */
+
+ DBUG_RETURN(FALSE);
+}
diff --git a/sql/sql_trigger.h b/sql/sql_trigger.h
index 1dc573995f1..f6754a75284 100644
--- a/sql/sql_trigger.h
+++ b/sql/sql_trigger.h
@@ -14,28 +14,28 @@
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
-/*
+/**
This class holds all information about triggers of table.
- QQ: Will it be merged into TABLE in future ?
+ QQ: Will it be merged into TABLE in the future ?
*/
class Table_triggers_list: public Sql_alloc
{
- /* Triggers as SPs grouped by event, action_time */
- sp_head *bodies[TRG_EVENT_MAX][TRG_ACTION_MAX];
- /*
+ /** Triggers as SPs grouped by event, action_time */
+ sp_head *bodies[TRG_EVENT_MAX][TRG_ACTION_MAX];
+ /**
Heads of the lists linking items for all fields used in triggers
grouped by event and action_time.
*/
Item_trigger_field *trigger_fields[TRG_EVENT_MAX][TRG_ACTION_MAX];
- /*
+ /**
Copy of TABLE::Field array with field pointers set to TABLE::record[1]
buffer instead of TABLE::record[0] (used for OLD values in on UPDATE
trigger and DELETE trigger when it is called for REPLACE).
*/
Field **record1_field;
- /*
+ /**
During execution of trigger new_field and old_field should point to the
array of fields representing new or old version of row correspondingly
(so it can point to TABLE::field or to Tale_triggers_list::record1_field)
@@ -45,36 +45,44 @@ class Table_triggers_list: public Sql_alloc
/* TABLE instance for which this triggers list object was created */
TABLE *trigger_table;
- /*
+ /**
Names of triggers.
Should correspond to order of triggers on definitions_list,
used in CREATE/DROP TRIGGER for looking up trigger by name.
*/
List<LEX_STRING> names_list;
- /*
+ /**
List of "ON table_name" parts in trigger definitions, used for
updating trigger definitions during RENAME TABLE.
*/
List<LEX_STRING> on_table_names_list;
- /*
+ /**
Grant information for each trigger (pair: subject table, trigger definer).
*/
GRANT_INFO subject_table_grants[TRG_EVENT_MAX][TRG_ACTION_MAX];
public:
- /*
+ /**
Field responsible for storing triggers definitions in file.
It have to be public because we are using it directly from parser.
*/
List<LEX_STRING> definitions_list;
- /*
+ /**
List of sql modes for triggers
*/
List<ulonglong> definition_modes_list;
List<LEX_STRING> definers_list;
+ /* Character set context, used for parsing and executing triggers. */
+
+ List<LEX_STRING> client_cs_names;
+ List<LEX_STRING> connection_cl_names;
+ List<LEX_STRING> db_cl_names;
+
+ /* End of character ser context. */
+
Table_triggers_list(TABLE *table_arg):
record1_field(0), trigger_table(table_arg)
{
@@ -89,11 +97,26 @@ public:
bool process_triggers(THD *thd, trg_event_type event,
trg_action_time_type time_type,
bool old_row_is_record1);
+
bool get_trigger_info(THD *thd, trg_event_type event,
trg_action_time_type time_type,
LEX_STRING *trigger_name, LEX_STRING *trigger_stmt,
ulong *sql_mode,
- LEX_STRING *definer);
+ LEX_STRING *definer,
+ LEX_STRING *client_cs_name,
+ LEX_STRING *connection_cl_name,
+ LEX_STRING *db_cl_name);
+
+ void get_trigger_info(THD *thd,
+ int trigger_idx,
+ LEX_STRING *trigger_name,
+ ulonglong *sql_mode,
+ LEX_STRING *sql_original_stmt,
+ LEX_STRING *client_cs_name,
+ LEX_STRING *connection_cl_name,
+ LEX_STRING *db_cl_name);
+
+ int find_trigger_by_name(const LEX_STRING *trigger_name);
static bool check_n_load(THD *thd, const char *db, const char *table_name,
TABLE *table, bool names_only);
@@ -115,9 +138,7 @@ public:
void set_table(TABLE *new_table);
- void mark_fields_used(THD *thd, trg_event_type event);
-
- bool is_updated_in_before_update_triggers(Field *fld);
+ void mark_fields_used(trg_event_type event);
friend class Item_trigger_field;
friend int sp_cache_routines_and_add_tables_for_triggers(THD *thd, LEX *lex,
@@ -125,11 +146,13 @@ public:
private:
bool prepare_record1_accessors(TABLE *table);
- LEX_STRING* change_table_name_in_trignames(const char *db_name,
+ LEX_STRING* change_table_name_in_trignames(const char *old_db_name,
+ const char *new_db_name,
LEX_STRING *new_table_name,
LEX_STRING *stopper);
bool change_table_name_in_triggers(THD *thd,
- const char *db_name,
+ const char *old_db_name,
+ const char *new_db_name,
LEX_STRING *old_table_name,
LEX_STRING *new_table_name);
};
@@ -137,6 +160,17 @@ private:
extern const LEX_STRING trg_action_time_type_names[];
extern const LEX_STRING trg_event_type_names[];
-int
-add_table_for_trigger(THD *thd, sp_name *trig, bool if_exists,
- TABLE_LIST **table);
+bool add_table_for_trigger(THD *thd,
+ const sp_name *trg_name,
+ bool continue_if_not_exist,
+ TABLE_LIST **table);
+
+void build_trn_path(THD *thd, const sp_name *trg_name, LEX_STRING *trn_path);
+
+bool check_trn_exists(const LEX_STRING *trn_path);
+
+bool load_table_name_for_trigger(THD *thd,
+ const sp_name *trg_name,
+ const LEX_STRING *trn_path,
+ LEX_STRING *tbl_name);
+
diff --git a/sql/sql_udf.cc b/sql/sql_udf.cc
index f1b16a627ac..c60dac42fb8 100644
--- a/sql/sql_udf.cc
+++ b/sql/sql_udf.cc
@@ -37,36 +37,10 @@
#ifdef HAVE_DLOPEN
extern "C"
{
-#if defined(__WIN__)
- void* dlsym(void* lib,const char* name)
- {
- return GetProcAddress((HMODULE)lib,name);
- }
- void* dlopen(const char* libname,int unused)
- {
- return LoadLibraryEx(libname,NULL,0);
- }
- void dlclose(void* lib)
- {
- FreeLibrary((HMODULE)lib);
- }
-
-#elif !defined(OS2)
-#include <dlfcn.h>
-#endif
-
#include <stdarg.h>
#include <hash.h>
}
-#ifndef RTLD_NOW
-#define RTLD_NOW 1 // For FreeBSD 2.2.2
-#endif
-
-#ifndef HAVE_DLERROR
-#define dlerror() ""
-#endif
-
static bool initialized = 0;
static MEM_ROOT mem;
static HASH udf_hash;
@@ -118,12 +92,12 @@ static char *init_syms(udf_func *tmp, char *nm)
}
-extern "C" byte* get_hash_key(const byte *buff,uint *length,
+extern "C" uchar* get_hash_key(const uchar *buff, size_t *length,
my_bool not_used __attribute__((unused)))
{
udf_func *udf=(udf_func*) buff;
*length=(uint) udf->name.length;
- return (byte*) udf->name.str;
+ return (uchar*) udf->name.str;
}
@@ -139,8 +113,8 @@ void udf_init()
READ_RECORD read_record_info;
TABLE *table;
int error;
- char db[]= "mysql"; /* A subject to casednstr, can't be constant */
DBUG_ENTER("ufd_init");
+ char db[]= "mysql"; /* A subject to casednstr, can't be constant */
if (initialized)
DBUG_VOID_RETURN;
@@ -161,9 +135,10 @@ 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((gptr) &tables,sizeof(tables));
+ bzero((uchar*) &tables,sizeof(tables));
tables.alias= tables.table_name= (char*) "func";
tables.lock_type = TL_READ;
tables.db= db;
@@ -171,13 +146,15 @@ void udf_init()
if (simple_open_n_lock_tables(new_thd, &tables))
{
DBUG_PRINT("error",("Can't open udf table"));
- sql_print_error("Can't open the mysql.func table. Please run the mysql_install_db script to create it.");
+ sql_print_error("Can't open the mysql.func table. Please "
+ "run mysql_upgrade to create it.");
goto end;
}
table= tables.table;
init_read_record(&read_record_info, new_thd, table, NULL,1,0,FALSE);
- while (!(error = read_record_info.read_record(&read_record_info)))
+ table->use_all_columns();
+ while (!(error= read_record_info.read_record(&read_record_info)))
{
DBUG_PRINT("info",("init udf record"));
LEX_STRING name;
@@ -193,17 +170,21 @@ void udf_init()
Ensure that the .dll doesn't have a path
This is done to ensure that only approved dll from the system
directories are used (to make this even remotely secure).
+
+ On windows we must check both FN_LIBCHAR and '/'.
*/
- if (strchr(dl_name, '/') ||
- IF_WIN(strchr(dl_name, '\\'),0) ||
- strlen(name.str) > NAME_LEN)
+ if (my_strchr(files_charset_info, dl_name,
+ dl_name + strlen(dl_name), FN_LIBCHAR) ||
+ IF_WIN(my_strchr(files_charset_info, dl_name,
+ dl_name + strlen(dl_name), '/'), 0) ||
+ check_string_char_length(&name, "", NAME_CHAR_LEN,
+ system_charset_info, 1))
{
sql_print_error("Invalid row in mysql.func table for function '%.64s'",
name.str);
continue;
}
-
if (!(tmp= add_udf(&name,(Item_result) table->field[1]->val_int(),
dl_name, udftype)))
{
@@ -215,19 +196,12 @@ void udf_init()
if (dl == NULL)
{
char dlpath[FN_REFLEN];
- if (*opt_plugin_dir)
- strxnmov(dlpath, sizeof(dlpath) - 1, opt_plugin_dir, "/", tmp->dl,
- NullS);
- else
- {
- strxnmov(dlpath, sizeof(dlpath)-1, tmp->dl, NullS);
- push_warning(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_UNKNOWN_ERROR,
- "plugin_dir was not specified");
- }
- if (!(dl = dlopen(dlpath, RTLD_NOW)))
+ strxnmov(dlpath, sizeof(dlpath) - 1, opt_plugin_dir, "/", tmp->dl,
+ NullS);
+ if (!(dl= dlopen(dlpath, RTLD_NOW)))
{
/* Print warning to log */
- sql_print_error(ER(ER_CANT_OPEN_LIBRARY), tmp->dl,errno,dlerror());
+ sql_print_error(ER(ER_CANT_OPEN_LIBRARY), tmp->dl, errno, dlerror());
/* Keep the udf in the hash so that we can remove it later */
continue;
}
@@ -294,7 +268,7 @@ static void del_udf(udf_func *udf)
DBUG_ENTER("del_udf");
if (!--udf->usage_count)
{
- hash_delete(&udf_hash,(byte*) udf);
+ hash_delete(&udf_hash,(uchar*) udf);
using_udf_functions=udf_hash.records != 0;
}
else
@@ -308,7 +282,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,(byte*) udf,(byte*) name,name_length);
+ hash_update(&udf_hash,(uchar*) udf,(uchar*) name,name_length);
}
DBUG_VOID_RETURN;
}
@@ -328,7 +302,7 @@ void free_udf(udf_func *udf)
We come here when someone has deleted the udf function
while another thread still was using the udf
*/
- hash_delete(&udf_hash,(byte*) udf);
+ hash_delete(&udf_hash,(uchar*) udf);
using_udf_functions=udf_hash.records != 0;
if (!find_udf_dl(udf->dl))
dlclose(udf->dlhandle);
@@ -354,7 +328,7 @@ udf_func *find_udf(const char *name,uint length,bool mark_used)
else
rw_rdlock(&THR_LOCK_udf); /* Called during parsing */
- if ((udf=(udf_func*) hash_search(&udf_hash,(byte*) name,
+ if ((udf=(udf_func*) hash_search(&udf_hash,(uchar*) name,
length ? length : (uint) strlen(name))))
{
if (!udf->dlhandle)
@@ -401,13 +375,21 @@ static udf_func *add_udf(LEX_STRING *name, Item_result ret, char *dl,
tmp->returns = ret;
tmp->type = type;
tmp->usage_count=1;
- if (my_hash_insert(&udf_hash,(byte*) tmp))
+ if (my_hash_insert(&udf_hash,(uchar*) tmp))
return 0;
using_udf_functions=1;
return tmp;
}
+/**
+ Create a user defined function.
+
+ @note Like implementations of other DDL/DML in MySQL, this function
+ relies on the caller to close the thread tables. This is done in the
+ end of dispatch_command().
+*/
+
int mysql_create_function(THD *thd,udf_func *udf)
{
int error;
@@ -433,20 +415,33 @@ int mysql_create_function(THD *thd,udf_func *udf)
Ensure that the .dll doesn't have a path
This is done to ensure that only approved dll from the system
directories are used (to make this even remotely secure).
+
+ On windows we must check both FN_LIBCHAR and '/'.
*/
- if (strchr(udf->dl, '/') || IF_WIN(strchr(udf->dl, '\\'),0))
+ if (my_strchr(files_charset_info, udf->dl,
+ udf->dl + strlen(udf->dl), FN_LIBCHAR) ||
+ IF_WIN(my_strchr(files_charset_info, udf->dl,
+ udf->dl + strlen(udf->dl), '/'), 0))
{
my_message(ER_UDF_NO_PATHS, ER(ER_UDF_NO_PATHS), MYF(0));
DBUG_RETURN(1);
}
- if (udf->name.length > NAME_LEN)
+ if (check_string_char_length(&udf->name, "", NAME_CHAR_LEN,
+ system_charset_info, 1))
{
my_error(ER_TOO_LONG_IDENT, MYF(0), udf->name.str);
DBUG_RETURN(1);
}
+ /*
+ Turn off row binlogging of this statement and use statement-based
+ so that all supporting tables are updated for CREATE FUNCTION command.
+ */
+ if (thd->current_stmt_binlog_row_based)
+ thd->clear_current_stmt_binlog_row_based();
+
rw_wrlock(&THR_LOCK_udf);
- if ((hash_search(&udf_hash,(byte*) udf->name.str, udf->name.length)))
+ if ((hash_search(&udf_hash,(uchar*) udf->name.str, udf->name.length)))
{
my_error(ER_UDF_EXISTS, MYF(0), udf->name.str);
goto err;
@@ -454,22 +449,13 @@ int mysql_create_function(THD *thd,udf_func *udf)
if (!(dl = find_udf_dl(udf->dl)))
{
char dlpath[FN_REFLEN];
- if (*opt_plugin_dir)
- strxnmov(dlpath, sizeof(dlpath) - 1, opt_plugin_dir, "/", udf->dl,
- NullS);
- else
- {
- strxnmov(dlpath, sizeof(dlpath)-1, udf->dl, NullS);
- push_warning(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_UNKNOWN_ERROR,
- "plugin_dir was not specified");
- }
- DBUG_PRINT("info", ("Calling dlopen, udf->dl: %s", dlpath));
+ strxnmov(dlpath, sizeof(dlpath) - 1, opt_plugin_dir, "/", udf->dl, NullS);
if (!(dl = dlopen(dlpath, RTLD_NOW)))
{
DBUG_PRINT("error",("dlopen of %s failed, error: %d (%s)",
- udf->dl,errno,dlerror()));
+ udf->dl, errno, dlerror()));
my_error(ER_CANT_OPEN_LIBRARY, MYF(0),
- udf->dl, errno, dlerror());
+ udf->dl, errno, dlerror());
goto err;
}
new_dl=1;
@@ -500,18 +486,17 @@ int mysql_create_function(THD *thd,udf_func *udf)
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)))
+ if (!(table = open_ltable(thd, &tables, TL_WRITE, 0)))
goto err;
-
+ table->use_all_columns();
restore_record(table, s->default_values); // Default values for fields
table->field[0]->store(u_d->name.str, u_d->name.length, system_charset_info);
table->field[1]->store((longlong) u_d->returns, TRUE);
table->field[2]->store(u_d->dl,(uint) strlen(u_d->dl), system_charset_info);
if (table->s->fields >= 4) // If not old func format
table->field[3]->store((longlong) u_d->type, TRUE);
- error = table->file->write_row(table->record[0]);
+ error = table->file->ha_write_row(table->record[0]);
- close_thread_tables(thd);
if (error)
{
my_error(ER_ERROR_ON_WRITE, MYF(0), "mysql.func", error);
@@ -519,6 +504,10 @@ int mysql_create_function(THD *thd,udf_func *udf)
goto err;
}
rw_unlock(&THR_LOCK_udf);
+
+ /* Binlog the create function. */
+ write_bin_log(thd, TRUE, thd->query, thd->query_length);
+
DBUG_RETURN(0);
err:
@@ -537,6 +526,7 @@ int mysql_drop_function(THD *thd,const LEX_STRING *udf_name)
char *exact_name_str;
uint exact_name_len;
DBUG_ENTER("mysql_drop_function");
+
if (!initialized)
{
if (opt_noacl)
@@ -545,8 +535,16 @@ int mysql_drop_function(THD *thd,const LEX_STRING *udf_name)
my_message(ER_OUT_OF_RESOURCES, ER(ER_OUT_OF_RESOURCES), MYF(0));
DBUG_RETURN(1);
}
+
+ /*
+ Turn off row binlogging of this statement and use statement-based
+ so that all supporting tables are updated for DROP FUNCTION command.
+ */
+ if (thd->current_stmt_binlog_row_based)
+ thd->clear_current_stmt_binlog_row_based();
+
rw_wrlock(&THR_LOCK_udf);
- if (!(udf=(udf_func*) hash_search(&udf_hash,(byte*) udf_name->str,
+ if (!(udf=(udf_func*) hash_search(&udf_hash,(uchar*) udf_name->str,
(uint) udf_name->length)))
{
my_error(ER_FUNCTION_NOT_DEFINED, MYF(0), udf_name->str);
@@ -565,22 +563,26 @@ int mysql_drop_function(THD *thd,const LEX_STRING *udf_name)
bzero((char*) &tables,sizeof(tables));
tables.db=(char*) "mysql";
tables.table_name= tables.alias= (char*) "func";
- if (!(table = open_ltable(thd,&tables,TL_WRITE)))
+ if (!(table = open_ltable(thd, &tables, TL_WRITE, 0)))
goto err;
+ table->use_all_columns();
table->field[0]->store(exact_name_str, exact_name_len, &my_charset_bin);
- table->file->extra(HA_EXTRA_RETRIEVE_ALL_COLS);
- if (!table->file->index_read_idx(table->record[0], 0,
- (byte*) table->field[0]->ptr,
- table->key_info[0].key_length,
- HA_READ_KEY_EXACT))
+ if (!table->file->index_read_idx_map(table->record[0], 0,
+ (uchar*) table->field[0]->ptr,
+ HA_WHOLE_KEY,
+ HA_READ_KEY_EXACT))
{
int error;
- if ((error = table->file->delete_row(table->record[0])))
+ 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);
+ rw_unlock(&THR_LOCK_udf);
+
+ /* Binlog the drop function. */
+ write_bin_log(thd, TRUE, thd->query, thd->query_length);
+
DBUG_RETURN(0);
err:
rw_unlock(&THR_LOCK_udf);
diff --git a/sql/sql_union.cc b/sql/sql_union.cc
index 2875aefbd97..fd3036e3d80 100644
--- a/sql/sql_union.cc
+++ b/sql/sql_union.cc
@@ -1,4 +1,4 @@
-/* Copyright (C) 2000-2003 MySQL AB
+/* Copyright 2000-2008 MySQL AB, 2008 Sun Microsystems, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -58,13 +58,13 @@ bool select_union::send_data(List<Item> &values)
return 0;
}
fill_record(thd, table->field, values, 1);
- if (thd->net.report_error)
+ if (thd->is_error())
return 1;
- if ((error= table->file->write_row(table->record[0])))
+ if ((error= table->file->ha_write_row(table->record[0])))
{
/* create_myisam_from_heap will generate error if needed */
- if (error != HA_ERR_FOUND_DUPP_KEY && error != HA_ERR_FOUND_DUPP_UNIQUE &&
+ if (table->file->is_fatal_error(error, HA_CHECK_DUP) &&
create_myisam_from_heap(thd, table, &tmp_table_param, error, 1))
return 1;
}
@@ -144,8 +144,8 @@ void
st_select_lex_unit::init_prepare_fake_select_lex(THD *thd_arg)
{
thd_arg->lex->current_select= fake_select_lex;
- fake_select_lex->table_list.link_in_list((byte *)&result_table_list,
- (byte **)
+ fake_select_lex->table_list.link_in_list((uchar *)&result_table_list,
+ (uchar **)
&result_table_list.next_local);
fake_select_lex->context.table_list=
fake_select_lex->context.first_name_resolution_table=
@@ -161,8 +161,8 @@ st_select_lex_unit::init_prepare_fake_select_lex(THD *thd_arg)
order;
order=order->next)
{
- (*order->item)->walk(&Item::change_context_processor,
- (byte *) &fake_select_lex->context);
+ (*order->item)->walk(&Item::change_context_processor, 0,
+ (uchar*) &fake_select_lex->context);
}
}
@@ -173,7 +173,7 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result,
SELECT_LEX *lex_select_save= thd_arg->lex->current_select;
SELECT_LEX *sl, *first_sl= first_select();
select_result *tmp_result;
- bool is_union;
+ bool is_union_select;
TABLE *empty_table= 0;
DBUG_ENTER("st_select_lex_unit::prepare");
@@ -211,11 +211,11 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result,
thd_arg->lex->current_select= sl= first_sl;
found_rows_for_union= first_sl->options & OPTION_FOUND_ROWS;
- is_union= first_sl->next_select() || fake_select_lex;
+ is_union_select= is_union() || fake_select_lex;
/* Global option */
- if (is_union)
+ if (is_union_select)
{
if (!(tmp_result= union_result= new select_union))
goto err;
@@ -246,7 +246,7 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result,
thd_arg->lex->current_select= sl;
- can_skip_order_by= is_union && !(sl->braces && sl->explicit_limit);
+ can_skip_order_by= is_union_select && !(sl->braces && sl->explicit_limit);
saved_error= join->prepare(&sl->ref_pointer_array,
(TABLE_LIST*) sl->table_list.first,
@@ -259,7 +259,7 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result,
(ORDER*) 0 : (ORDER *)sl->order_list.first,
(ORDER*) sl->group_list.first,
sl->having,
- (is_union ? (ORDER*) 0 :
+ (is_union_select ? (ORDER*) 0 :
(ORDER*) thd_arg->lex->proc_list.first),
sl, this);
/* There are no * in the statement anymore (for PS) */
@@ -272,7 +272,7 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result,
Use items list of underlaid select for derived tables to preserve
information about fields lengths and exact types
*/
- if (!is_union)
+ if (!is_union_select)
types= first_sl->item_list;
else if (sl == first_sl)
{
@@ -315,7 +315,7 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result,
}
}
- if (is_union)
+ if (is_union_select)
{
/*
Check that it was possible to aggregate
@@ -439,10 +439,10 @@ bool st_select_lex_unit::exec()
{
item->assigned(0); // We will reinit & rexecute unit
item->reset();
- table->file->delete_all_rows();
+ table->file->ha_delete_all_rows();
}
/* re-enabling indexes for next subselect iteration */
- if (union_distinct && table->file->enable_indexes(HA_KEY_SWITCH_ALL))
+ if (union_distinct && table->file->ha_enable_indexes(HA_KEY_SWITCH_ALL))
{
DBUG_ASSERT(0);
}
@@ -480,11 +480,11 @@ bool st_select_lex_unit::exec()
}
if (!saved_error)
{
- records_at_start= table->file->records;
+ records_at_start= table->file->stats.records;
sl->join->exec();
if (sl == union_distinct)
{
- if (table->file->disable_indexes(HA_KEY_SWITCH_ALL))
+ if (table->file->ha_disable_indexes(HA_KEY_SWITCH_ALL))
DBUG_RETURN(TRUE);
table->no_keyread=1;
}
@@ -524,7 +524,7 @@ bool st_select_lex_unit::exec()
rows and actual rows added to the temporary table.
*/
add_rows+= (ulonglong) (thd->limit_found_rows - (ulonglong)
- ((table->file->records - records_at_start)));
+ ((table->file->stats.records - records_at_start)));
}
}
}
@@ -607,7 +607,7 @@ bool st_select_lex_unit::exec()
fake_select_lex->table_list.empty();
if (!saved_error)
{
- thd->limit_found_rows = (ulonglong)table->file->records + add_rows;
+ thd->limit_found_rows = (ulonglong)table->file->stats.records + add_rows;
thd->examined_row_count+= examined_rows;
}
/*
@@ -669,7 +669,7 @@ void st_select_lex_unit::reinit_exec_mechanism()
{
prepared= optimized= executed= 0;
#ifndef DBUG_OFF
- if (first_select()->next_select())
+ if (is_union())
{
List_iterator_fast<Item> it(item_list);
Item *field;
@@ -736,7 +736,6 @@ bool st_select_lex_unit::change_result(select_subselect *new_result,
List<Item> *st_select_lex_unit::get_unit_column_types()
{
SELECT_LEX *sl= first_select();
- bool is_union= test(sl->next_select());
bool is_procedure= test(sl->join->procedure);
if (is_procedure)
@@ -747,7 +746,7 @@ List<Item> *st_select_lex_unit::get_unit_column_types()
}
- if (is_union)
+ if (is_union())
{
DBUG_ASSERT(prepared);
/* Types are generated during prepare */
diff --git a/sql/sql_update.cc b/sql/sql_update.cc
index f15db220a3b..b3bd5d0bc57 100644
--- a/sql/sql_update.cc
+++ b/sql/sql_update.cc
@@ -1,4 +1,4 @@
-/* Copyright (C) 2000-2006 MySQL AB
+/* Copyright 2000-2008 MySQL AB, 2008 Sun Microsystems, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -26,7 +26,7 @@
/* Return 0 if row hasn't changed */
-bool compare_record(TABLE *table, query_id_t query_id)
+bool compare_record(TABLE *table)
{
if (table->s->blob_fields + table->s->varchar_fields == 0)
return cmp_record(table,record[1]);
@@ -36,9 +36,9 @@ bool compare_record(TABLE *table, query_id_t query_id)
table->s->null_bytes))
return TRUE; // Diff in NULL value
/* Compare updated fields */
- for (Field **ptr=table->field ; *ptr ; ptr++)
+ for (Field **ptr= table->field ; *ptr ; ptr++)
{
- if ((*ptr)->query_id == query_id &&
+ if (bitmap_is_set(table->write_set, (*ptr)->field_index) &&
(*ptr)->cmp_binary_offset(table->s->rec_buff_length))
return TRUE;
}
@@ -83,6 +83,75 @@ static bool check_fields(THD *thd, List<Item> &items)
}
+/**
+ Re-read record if more columns are needed for error message.
+
+ If we got a duplicate key error, we want to write an error
+ message containing the value of the duplicate key. If we do not have
+ all fields of the key value in record[0], we need to re-read the
+ record with a proper read_set.
+
+ @param[in] error error number
+ @param[in] table table
+*/
+
+static void prepare_record_for_error_message(int error, TABLE *table)
+{
+ Field **field_p;
+ Field *field;
+ uint keynr;
+ MY_BITMAP unique_map; /* Fields in offended unique. */
+ my_bitmap_map unique_map_buf[bitmap_buffer_size(MAX_FIELDS)];
+ DBUG_ENTER("prepare_record_for_error_message");
+
+ /*
+ Only duplicate key errors print the key value.
+ If storage engine does always read all columns, we have the value alraedy.
+ */
+ if ((error != HA_ERR_FOUND_DUPP_KEY) ||
+ !(table->file->ha_table_flags() & HA_PARTIAL_COLUMN_READ))
+ DBUG_VOID_RETURN;
+
+ /*
+ Get the number of the offended index.
+ We will see MAX_KEY if the engine cannot determine the affected index.
+ */
+ if ((keynr= table->file->get_dup_key(error)) >= MAX_KEY)
+ DBUG_VOID_RETURN;
+
+ /* Create unique_map with all fields used by that index. */
+ bitmap_init(&unique_map, unique_map_buf, table->s->fields, FALSE);
+ table->mark_columns_used_by_index_no_reset(keynr, &unique_map);
+
+ /* Subtract read_set and write_set. */
+ bitmap_subtract(&unique_map, table->read_set);
+ bitmap_subtract(&unique_map, table->write_set);
+
+ /*
+ If the unique index uses columns that are neither in read_set
+ nor in write_set, we must re-read the record.
+ Otherwise no need to do anything.
+ */
+ if (bitmap_is_clear_all(&unique_map))
+ DBUG_VOID_RETURN;
+
+ /* Get identifier of last read record into table->file->ref. */
+ table->file->position(table->record[0]);
+ /* Add all fields used by unique index to read_set. */
+ bitmap_union(table->read_set, &unique_map);
+ /* Tell the engine about the new set. */
+ table->file->column_bitmaps_signal();
+ /* Read record that is identified by table->file->ref. */
+ (void) table->file->rnd_pos(table->record[1], table->file->ref);
+ /* Copy the newly read columns into the new record. */
+ for (field_p= table->field; (field= *field_p); field_p++)
+ if (bitmap_is_set(&unique_map, field->field_index))
+ field->copy_from_tmp(table->s->rec_buff_length);
+
+ DBUG_VOID_RETURN;
+}
+
+
/*
Process usual UPDATE
@@ -115,30 +184,28 @@ int mysql_update(THD *thd,
{
bool using_limit= limit != HA_POS_ERROR;
bool safe_update= test(thd->options & OPTION_SAFE_UPDATES);
- bool used_key_is_modified, transactional_table;
+ bool used_key_is_modified, transactional_table, will_batch;
bool can_compare_record;
int res;
- int error;
- uint used_index= MAX_KEY;
+ int error, loc_error;
+ uint used_index= MAX_KEY, dup_key_found;
bool need_sort= TRUE;
#ifndef NO_EMBEDDED_ACCESS_CHECKS
uint want_privilege;
#endif
uint table_count= 0;
- query_id_t query_id=thd->query_id, timestamp_query_id;
ha_rows updated, found;
- key_map old_used_keys;
+ key_map old_covering_keys;
TABLE *table;
SQL_SELECT *select;
READ_RECORD info;
SELECT_LEX *select_lex= &thd->lex->select_lex;
- bool need_reopen;
+ bool need_reopen;
+ ulonglong id;
List<Item> all_fields;
THD::killed_state killed_status= THD::NOT_KILLED;
DBUG_ENTER("mysql_update");
- LINT_INIT(timestamp_query_id);
-
for ( ; ; )
{
if (open_tables(thd, &table_list, &table_count, 0))
@@ -165,12 +232,11 @@ int mysql_update(THD *thd,
mysql_handle_derived(thd->lex, &mysql_derived_filling)))
DBUG_RETURN(1);
- thd->proc_info="init";
+ thd_proc_info(thd, "init");
table= table_list->table;
- table->file->info(HA_STATUS_VARIABLE | HA_STATUS_NO_LOCK);
- /* Calculate "table->used_keys" based on the WHERE */
- table->used_keys= table->s->keys_in_use;
+ /* Calculate "table->covering_keys" based on the WHERE */
+ table->covering_keys= table->s->keys_in_use;
table->quick_keys.clear_all();
#ifndef NO_EMBEDDED_ACCESS_CHECKS
@@ -181,23 +247,13 @@ int mysql_update(THD *thd,
if (mysql_prepare_update(thd, table_list, &conds, order_num, order))
DBUG_RETURN(1);
- old_used_keys= table->used_keys; // Keys used in WHERE
- /*
- Change the query_id for the timestamp column so that we can
- check if this is modified directly
- */
- if (table->timestamp_field)
- {
- timestamp_query_id=table->timestamp_field->query_id;
- table->timestamp_field->query_id=thd->query_id-1;
- }
-
+ old_covering_keys= table->covering_keys; // Keys used in WHERE
/* Check the fields we are going to modify */
#ifndef NO_EMBEDDED_ACCESS_CHECKS
table_list->grant.want_privilege= table->grant.want_privilege= want_privilege;
table_list->register_want_access(want_privilege);
#endif
- if (setup_fields_with_no_wrap(thd, 0, fields, 1, 0, 0))
+ if (setup_fields_with_no_wrap(thd, 0, fields, MARK_COLUMNS_WRITE, 0, 0))
DBUG_RETURN(1); /* purecov: inspected */
if (table_list->view && check_fields(thd, fields))
{
@@ -211,10 +267,16 @@ int mysql_update(THD *thd,
if (table->timestamp_field)
{
// Don't set timestamp column if this is modified
- if (table->timestamp_field->query_id == thd->query_id)
+ if (bitmap_is_set(table->write_set,
+ table->timestamp_field->field_index))
table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET;
else
- table->timestamp_field->query_id=timestamp_query_id;
+ {
+ if (table->timestamp_field_type == TIMESTAMP_AUTO_SET_ON_UPDATE ||
+ table->timestamp_field_type == TIMESTAMP_AUTO_SET_ON_BOTH)
+ bitmap_set_bit(table->write_set,
+ table->timestamp_field->field_index);
+ }
}
#ifndef NO_EMBEDDED_ACCESS_CHECKS
@@ -222,7 +284,7 @@ int mysql_update(THD *thd,
table_list->grant.want_privilege= table->grant.want_privilege=
(SELECT_ACL & ~table->grant.privilege);
#endif
- if (setup_fields(thd, 0, values, 1, 0, 0))
+ if (setup_fields(thd, 0, values, MARK_COLUMNS_READ, 0, 0))
{
free_underlaid_joins(thd, select_lex);
DBUG_RETURN(1); /* purecov: inspected */
@@ -239,8 +301,31 @@ int mysql_update(THD *thd,
if (cond_value == Item::COND_FALSE)
limit= 0; // Impossible WHERE
}
+
+ /*
+ If a timestamp field settable on UPDATE is present then to avoid wrong
+ update force the table handler to retrieve write-only fields to be able
+ to compare records and detect data change.
+ */
+ if (table->file->ha_table_flags() & HA_PARTIAL_COLUMN_READ &&
+ table->timestamp_field &&
+ (table->timestamp_field_type == TIMESTAMP_AUTO_SET_ON_UPDATE ||
+ table->timestamp_field_type == TIMESTAMP_AUTO_SET_ON_BOTH))
+ bitmap_union(table->read_set, table->write_set);
// Don't count on usage of 'only index' when calculating which key to use
- table->used_keys.clear_all();
+ table->covering_keys.clear_all();
+
+#ifdef WITH_PARTITION_STORAGE_ENGINE
+ if (prune_partitions(thd, table, conds))
+ {
+ free_underlaid_joins(thd, select_lex);
+ my_ok(thd); // No matching records
+ DBUG_RETURN(0);
+ }
+#endif
+ /* Update the table->file->stats.records number */
+ table->file->info(HA_STATUS_VARIABLE | HA_STATUS_NO_LOCK);
+
select= make_select(table, 0, 0, conds, 0, &error);
if (error || !limit ||
(select && select->check_quick(thd, safe_update, limit)))
@@ -251,7 +336,7 @@ int mysql_update(THD *thd,
{
DBUG_RETURN(1); // Error in where
}
- send_ok(thd); // No matching records
+ my_ok(thd); // No matching records
DBUG_RETURN(0);
}
if (!select && limit != HA_POS_ERROR)
@@ -271,13 +356,16 @@ int mysql_update(THD *thd,
}
}
init_ftfuncs(thd, select_lex, 1);
+
+ 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(&fields));
+ select->quick->is_keys_used(table->write_set));
}
else
{
@@ -285,28 +373,38 @@ int mysql_update(THD *thd,
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, fields);
+ 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!
*/
- table->file->extra(HA_EXTRA_RETRIEVE_ALL_COLS);
- if (used_index < MAX_KEY && old_used_keys.is_set(used_index))
+ if (used_index < MAX_KEY && old_covering_keys.is_set(used_index))
{
table->key_read=1;
- table->file->extra(HA_EXTRA_KEYREAD);
+ table->mark_columns_used_by_index(used_index);
+ }
+ else
+ {
+ table->use_all_columns();
}
- /* note: can actually avoid sorting below.. */
+ /* note: We avoid sorting avoid if we sort on the used index */
if (order && (need_sort || used_key_is_modified))
{
/*
Doing an ORDER BY; Let filesort find and sort the rows we are going
to update
+ NOTE: filesort will call table->prepare_for_position()
*/
uint length= 0;
SORT_FIELD *sortorder;
@@ -315,12 +413,11 @@ int mysql_update(THD *thd,
table->sort.io_cache = (IO_CACHE *) my_malloc(sizeof(IO_CACHE),
MYF(MY_FAE | MY_ZEROFILL));
if (!(sortorder=make_unireg_sortorder(order, &length, NULL)) ||
- (table->sort.found_records = filesort(thd, table, sortorder, length,
- select, limit,
- &examined_rows))
+ (table->sort.found_records= filesort(thd, table, sortorder, length,
+ select, limit, 1,
+ &examined_rows))
== HA_POS_ERROR)
{
- free_io_cache(table);
goto err;
}
/*
@@ -346,6 +443,7 @@ int mysql_update(THD *thd,
/* If quick select is used, initialize it before retrieving rows. */
if (select && select->quick && select->quick->reset())
goto err;
+ table->file->try_semi_consistent_read(1);
/*
When we get here, we have one of the following options:
@@ -357,18 +455,22 @@ int mysql_update(THD *thd,
B.2 quick select is not used, this is full index scan (with LIMIT)
Full index scan must be started with init_read_record_idx
*/
+
if (used_index == MAX_KEY || (select && select->quick))
init_read_record(&info, thd, table, select, 0, 1, FALSE);
else
init_read_record_idx(&info, thd, table, 1, used_index);
- thd->proc_info="Searching rows for update";
+ thd_proc_info(thd, "Searching rows for update");
ha_rows tmp_limit= limit;
while (!(error=info.read_record(&info)) && !thd->killed)
{
if (!(select && select->skip_record()))
{
+ if (table->file->was_semi_consistent_read())
+ continue; /* repeat the read of the same row if it still exists */
+
table->file->position(table->record[0]);
if (my_b_write(&tempfile,table->file->ref,
table->file->ref_length))
@@ -388,6 +490,7 @@ int mysql_update(THD *thd,
if (thd->killed && !error)
error= 1; // Aborted
limit= tmp_limit;
+ table->file->try_semi_consistent_read(0);
end_read_record(&info);
/* Change select to use tempfile */
@@ -411,56 +514,69 @@ int mysql_update(THD *thd,
goto err;
}
if (table->key_read)
- {
- table->key_read=0;
- table->file->extra(HA_EXTRA_NO_KEYREAD);
- }
+ table->restore_column_maps_after_mark_index();
}
if (ignore)
table->file->extra(HA_EXTRA_IGNORE_DUP_KEY);
if (select && select->quick && select->quick->reset())
- goto err;
+ goto err;
+ table->file->try_semi_consistent_read(1);
init_read_record(&info, thd, table, select, 0, 1, FALSE);
updated= found= 0;
- thd->count_cuted_fields= CHECK_FIELD_WARN; /* calc cuted fields */
+ /*
+ Generate an error (in TRADITIONAL mode) or warning
+ when trying to set a NOT NULL field to NULL.
+ */
+ thd->count_cuted_fields= CHECK_FIELD_WARN;
thd->cuted_fields=0L;
- thd->proc_info="Updating";
- query_id=thd->query_id;
+ thd_proc_info(thd, "Updating");
transactional_table= table->file->has_transactions();
thd->abort_on_warning= test(!ignore &&
(thd->variables.sql_mode &
(MODE_STRICT_TRANS_TABLES |
MODE_STRICT_ALL_TABLES)));
-
- if (table->triggers)
+ if (table->triggers &&
+ table->triggers->has_triggers(TRG_EVENT_UPDATE,
+ TRG_ACTION_AFTER))
{
- table->triggers->mark_fields_used(thd, TRG_EVENT_UPDATE);
- if (table->triggers->has_triggers(TRG_EVENT_UPDATE,
- TRG_ACTION_AFTER))
- {
- /*
- The table has AFTER UPDATE triggers that might access to subject
- table and therefore might need update to be done immediately.
- So we turn-off the batching.
- */
- (void) table->file->extra(HA_EXTRA_UPDATE_CANNOT_BATCH);
- }
+ /*
+ The table has AFTER UPDATE triggers that might access to subject
+ table and therefore might need update to be done immediately.
+ So we turn-off the batching.
+ */
+ (void) table->file->extra(HA_EXTRA_UPDATE_CANNOT_BATCH);
+ will_batch= FALSE;
}
+ else
+ will_batch= !table->file->start_bulk_update();
+
+ /*
+ Assure that we can use position()
+ if we need to create an error message.
+ */
+ if (table->file->ha_table_flags() & HA_PARTIAL_COLUMN_READ)
+ table->prepare_for_position();
/*
We can use compare_record() to optimize away updates if
- the table handler is returning all columns
+ the table handler is returning all columns OR if
+ if all updated columns are read
*/
- can_compare_record= !(table->file->table_flags() &
- HA_PARTIAL_COLUMN_READ);
+ can_compare_record= (!(table->file->ha_table_flags() &
+ HA_PARTIAL_COLUMN_READ) ||
+ bitmap_is_subset(table->write_set, table->read_set));
+
while (!(error=info.read_record(&info)) && !thd->killed)
{
if (!(select && select->skip_record()))
{
+ if (table->file->was_semi_consistent_read())
+ continue; /* repeat the read of the same row if it still exists */
+
store_record(table,record[1]);
if (fill_record_n_invoke_before_triggers(thd, fields, values, 0,
table->triggers,
@@ -469,7 +585,7 @@ int mysql_update(THD *thd,
found++;
- if (!can_compare_record || compare_record(table, query_id))
+ if (!can_compare_record || compare_record(table))
{
if ((res= table_list->view_check_option(thd, ignore)) !=
VIEW_CHECK_OK)
@@ -483,43 +599,131 @@ int mysql_update(THD *thd,
break;
}
}
- if (!(error=table->file->update_row((byte*) table->record[1],
- (byte*) table->record[0])))
+ if (will_batch)
{
- updated++;
-
- if (table->triggers &&
- table->triggers->process_triggers(thd, TRG_EVENT_UPDATE,
- TRG_ACTION_AFTER, TRUE))
- {
- error= 1;
- break;
- }
+ /*
+ Typically a batched handler can execute the batched jobs when:
+ 1) When specifically told to do so
+ 2) When it is not a good idea to batch anymore
+ 3) When it is necessary to send batch for other reasons
+ (One such reason is when READ's must be performed)
+
+ 1) is covered by exec_bulk_update calls.
+ 2) and 3) is handled by the bulk_update_row method.
+
+ bulk_update_row can execute the updates including the one
+ defined in the bulk_update_row or not including the row
+ in the call. This is up to the handler implementation and can
+ vary from call to call.
+
+ The dup_key_found reports the number of duplicate keys found
+ in those updates actually executed. It only reports those if
+ the extra call with HA_EXTRA_IGNORE_DUP_KEY have been issued.
+ If this hasn't been issued it returns an error code and can
+ ignore this number. Thus any handler that implements batching
+ for UPDATE IGNORE must also handle this extra call properly.
+
+ If a duplicate key is found on the record included in this
+ call then it should be included in the count of dup_key_found
+ and error should be set to 0 (only if these errors are ignored).
+ */
+ error= table->file->ha_bulk_update_row(table->record[1],
+ table->record[0],
+ &dup_key_found);
+ limit+= dup_key_found;
+ updated-= dup_key_found;
}
- else if (!ignore || error != HA_ERR_FOUND_DUPP_KEY)
+ else
{
+ /* Non-batched update */
+ error= table->file->ha_update_row(table->record[1],
+ table->record[0]);
+ }
+ if (!error || error == HA_ERR_RECORD_IS_THE_SAME)
+ {
+ if (error != HA_ERR_RECORD_IS_THE_SAME)
+ updated++;
+ else
+ error= 0;
+ }
+ else if (!ignore ||
+ table->file->is_fatal_error(error, HA_CHECK_DUP_KEY))
+ {
/*
- If (ignore && error == HA_ERR_FOUND_DUPP_KEY) we don't have to
+ If (ignore && error is ignorable) we don't have to
do anything; otherwise...
*/
- if (error != HA_ERR_FOUND_DUPP_KEY)
+ if (table->file->is_fatal_error(error, HA_CHECK_DUP_KEY))
thd->fatal_error(); /* Other handler errors are fatal */
- table->file->print_error(error,MYF(0));
- error= 1;
- break;
- }
+
+ prepare_record_for_error_message(error, table);
+ table->file->print_error(error,MYF(0));
+ error= 1;
+ break;
+ }
}
-
- if (!--limit && using_limit)
+
+ if (table->triggers &&
+ table->triggers->process_triggers(thd, TRG_EVENT_UPDATE,
+ TRG_ACTION_AFTER, TRUE))
{
- error= -1; // Simulate end of file
+ error= 1;
break;
}
+
+ if (!--limit && using_limit)
+ {
+ /*
+ We have reached end-of-file in most common situations where no
+ batching has occurred and if batching was supposed to occur but
+ no updates were made and finally when the batch execution was
+ performed without error and without finding any duplicate keys.
+ If the batched updates were performed with errors we need to
+ check and if no error but duplicate key's found we need to
+ continue since those are not counted for in limit.
+ */
+ if (will_batch &&
+ ((error= table->file->exec_bulk_update(&dup_key_found)) ||
+ dup_key_found))
+ {
+ if (error)
+ {
+ /* purecov: begin inspected */
+ /*
+ The handler should not report error of duplicate keys if they
+ are ignored. This is a requirement on batching handlers.
+ */
+ prepare_record_for_error_message(error, table);
+ table->file->print_error(error,MYF(0));
+ error= 1;
+ break;
+ /* purecov: end */
+ }
+ /*
+ Either an error was found and we are ignoring errors or there
+ were duplicate keys found. In both cases we need to correct
+ the counters and continue the loop.
+ */
+ limit= dup_key_found; //limit is 0 when we get here so need to +
+ updated-= dup_key_found;
+ }
+ else
+ {
+ error= -1; // Simulate end of file
+ break;
+ }
+ }
}
else
table->file->unlock_row();
thd->row_count++;
+ if (thd->is_error())
+ {
+ error= 1;
+ break;
+ }
}
+ dup_key_found= 0;
/*
Caching the killed status to pass as the arg to query event constuctor;
The cached value can not change whereas the killed status can
@@ -536,14 +740,37 @@ int mysql_update(THD *thd,
};);
error= (killed_status == THD::NOT_KILLED)? error : 1;
+ if (error &&
+ will_batch &&
+ (loc_error= table->file->exec_bulk_update(&dup_key_found)))
+ /*
+ An error has occurred when a batched update was performed and returned
+ an error indication. It cannot be an allowed duplicate key error since
+ we require the batching handler to treat this as a normal behavior.
+
+ Otherwise we simply remove the number of duplicate keys records found
+ in the batched update.
+ */
+ {
+ /* purecov: begin inspected */
+ thd->fatal_error();
+ prepare_record_for_error_message(loc_error, table);
+ table->file->print_error(loc_error,MYF(0));
+ error= 1;
+ /* purecov: end */
+ }
+ else
+ updated-= dup_key_found;
+ if (will_batch)
+ table->file->end_bulk_update();
+ table->file->try_semi_consistent_read(0);
if (!transactional_table && updated > 0)
thd->transaction.stmt.modified_non_trans_table= TRUE;
end_read_record(&info);
- free_io_cache(table); // If ORDER BY
delete select;
- thd->proc_info="end";
+ thd_proc_info(thd, "end");
VOID(table->file->extra(HA_EXTRA_NO_IGNORE_DUP_KEY));
/*
@@ -570,27 +797,23 @@ int mysql_update(THD *thd,
{
if (error < 0)
thd->clear_error();
- Query_log_event qinfo(thd, thd->query, thd->query_length,
- transactional_table, FALSE, killed_status);
- if (mysql_bin_log.write(&qinfo) && transactional_table)
- error=1; // Rollback update
+ if (thd->binlog_query(THD::ROW_QUERY_TYPE,
+ thd->query, thd->query_length,
+ transactional_table, FALSE, killed_status) &&
+ transactional_table)
+ {
+ 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);
- if (transactional_table)
- {
- if (ha_autocommit_or_rollback(thd, error >= 0))
- error=1;
- }
- if (thd->lock)
- {
- mysql_unlock_tables(thd, thd->lock);
- thd->lock=0;
- }
+ /* If LAST_INSERT_ID(X) was used, report X */
+ id= thd->arg_of_last_insert_id_function ?
+ thd->first_successful_insert_id_in_prev_stmt : 0;
if (error < 0)
{
@@ -599,14 +822,12 @@ int mysql_update(THD *thd,
(ulong) thd->cuted_fields);
thd->row_count_func=
(thd->client_capabilities & CLIENT_FOUND_ROWS) ? found : updated;
- send_ok(thd, (ulong) thd->row_count_func,
- thd->insert_id_used ? thd->last_insert_id : 0L,buff);
+ my_ok(thd, (ulong) thd->row_count_func, id, buff);
DBUG_PRINT("info",("%ld records updated", (long) updated));
}
thd->count_cuted_fields= CHECK_FIELD_IGNORE; /* calc cuted fields */
thd->abort_on_warning= 0;
- free_io_cache(table);
- DBUG_RETURN((error >= 0 || thd->net.report_error) ? 1 : 0);
+ DBUG_RETURN((error >= 0 || thd->is_error()) ? 1 : 0);
err:
delete select;
@@ -639,26 +860,37 @@ bool mysql_prepare_update(THD *thd, TABLE_LIST *table_list,
Item **conds, uint order_num, ORDER *order)
{
Item *fake_conds= 0;
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
TABLE *table= table_list->table;
- TABLE_LIST tables;
+#endif
List<Item> all_fields;
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);
table_list->register_want_access(SELECT_ACL);
#endif
- bzero((char*) &tables,sizeof(tables)); // For ORDER BY
- tables.table= table;
- tables.alias= table_list->alias;
thd->lex->allow_sum_func= 0;
if (setup_tables_and_check_access(thd, &select_lex->context,
&select_lex->top_join_list,
- table_list, conds,
+ table_list,
&select_lex->leaf_tables,
FALSE, UPDATE_ACL, SELECT_ACL) ||
setup_conds(thd, table_list, select_lex->leaf_tables, conds) ||
@@ -753,12 +985,12 @@ reopen_tables:
if (setup_tables_and_check_access(thd, &lex->select_lex.context,
&lex->select_lex.top_join_list,
- table_list, &lex->select_lex.where,
+ table_list,
&lex->select_lex.leaf_tables, FALSE,
UPDATE_ACL, SELECT_ACL))
DBUG_RETURN(TRUE);
- if (setup_fields_with_no_wrap(thd, 0, *fields, 1, 0, 0))
+ if (setup_fields_with_no_wrap(thd, 0, *fields, MARK_COLUMNS_WRITE, 0, 0))
DBUG_RETURN(TRUE);
for (tl= table_list; tl ; tl= tl->next_local)
@@ -775,7 +1007,7 @@ reopen_tables:
DBUG_RETURN(TRUE);
}
- tables_for_update= get_table_map(fields);
+ thd->table_map_for_update= tables_for_update= get_table_map(fields);
/*
Setup timestamp handling and locking mode
@@ -786,7 +1018,8 @@ reopen_tables:
TABLE *table= tl->table;
/* Only set timestamp column if this is not modified */
if (table->timestamp_field &&
- table->timestamp_field->query_id == thd->query_id)
+ bitmap_is_set(table->write_set,
+ table->timestamp_field->field_index))
table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET;
/* if table will be updated then check that it is unique */
@@ -798,9 +1031,7 @@ reopen_tables:
DBUG_RETURN(TRUE);
}
- if (table->triggers)
- table->triggers->mark_fields_used(thd, TRG_EVENT_UPDATE);
-
+ table->mark_columns_needed_for_update();
DBUG_PRINT("info",("setting table `%s` for update", tl->alias));
/*
If table will be updated we should not downgrade lock for it and
@@ -815,7 +1046,7 @@ reopen_tables:
correct order of statements. Otherwise, we use a TL_READ lock to
improve performance.
*/
- tl->lock_type= using_update_log ? TL_READ_NO_INSERT : TL_READ;
+ tl->lock_type= read_lock_type_for_table(thd, table);
tl->updating= 0;
/* Update TABLE::lock_type accordingly. */
if (!tl->placeholder() && !using_lock_tables)
@@ -831,7 +1062,7 @@ reopen_tables:
if (check_access(thd, want_privilege,
tl->db, &tl->grant.privilege, 0, 0,
test(tl->schema_table)) ||
- (grant_option && check_grant(thd, want_privilege, tl, 0, 1, 0)))
+ check_grant(thd, want_privilege, tl, 0, 1, 0))
DBUG_RETURN(TRUE);
}
}
@@ -987,8 +1218,8 @@ bool mysql_multi_update(THD *thd,
OPTION_SETUP_TABLES_DONE,
result, unit, select_lex);
DBUG_PRINT("info",("res: %d report_error: %d", res,
- thd->net.report_error));
- res|= thd->net.report_error;
+ (int) thd->is_error()));
+ res|= thd->is_error();
if (unlikely(res))
{
/* If we had a another error reported earlier then this will be ignored */
@@ -1033,7 +1264,7 @@ int multi_update::prepare(List<Item> &not_used_values,
thd->count_cuted_fields= CHECK_FIELD_WARN;
thd->cuted_fields=0L;
- thd->proc_info="updating main table";
+ thd_proc_info(thd, "updating main table");
tables_to_update= get_table_map(fields);
@@ -1044,11 +1275,11 @@ int multi_update::prepare(List<Item> &not_used_values,
}
/*
- We have to check values after setup_tables to get used_keys right in
+ We have to check values after setup_tables to get covering_keys right in
reference tables
*/
- if (setup_fields(thd, 0, *values, 1, 0, 0))
+ if (setup_fields(thd, 0, *values, MARK_COLUMNS_READ, 0, 0))
DBUG_RETURN(1);
/*
@@ -1069,24 +1300,21 @@ int multi_update::prepare(List<Item> &not_used_values,
sizeof(*tl));
if (!tl)
DBUG_RETURN(1);
- update.link_in_list((byte*) tl, (byte**) &tl->next_local);
+ update.link_in_list((uchar*) tl, (uchar**) &tl->next_local);
tl->shared= table_count++;
table->no_keyread=1;
- table->used_keys.clear_all();
+ table->covering_keys.clear_all();
table->pos_in_table_list= tl;
- if (table->triggers)
+ if (table->triggers &&
+ table->triggers->has_triggers(TRG_EVENT_UPDATE,
+ TRG_ACTION_AFTER))
{
- table->triggers->mark_fields_used(thd, TRG_EVENT_UPDATE);
- if (table->triggers->has_triggers(TRG_EVENT_UPDATE,
- TRG_ACTION_AFTER))
- {
- /*
- The table has AFTER UPDATE triggers that might access to subject
- table and therefore might need update to be done immediately.
- So we turn-off the batching.
- */
- (void) table->file->extra(HA_EXTRA_UPDATE_CANNOT_BATCH);
- }
+ /*
+ The table has AFTER UPDATE triggers that might access to subject
+ table and therefore might need update to be done immediately.
+ So we turn-off the batching.
+ */
+ (void) table->file->extra(HA_EXTRA_UPDATE_CANNOT_BATCH);
}
}
}
@@ -1141,7 +1369,6 @@ int multi_update::prepare(List<Item> &not_used_values,
thd Thread handler
join_tab How table is used in join
all_tables List of tables
- fields Fields that are updated
NOTES
We can update the first table in join on the fly if we know that
@@ -1156,9 +1383,8 @@ int multi_update::prepare(List<Item> &not_used_values,
- Table is not joined to itself.
- When checking for above cases we also should take into account that
- BEFORE UPDATE trigger potentially may change value of any field in row
- being updated.
+ This function gets information about fields to be updated from
+ the TABLE::write_set bitmap.
WARNING
This code is a bit dependent of how make_join_readinfo() works.
@@ -1169,8 +1395,7 @@ int multi_update::prepare(List<Item> &not_used_values,
*/
static bool safe_update_on_fly(THD *thd, JOIN_TAB *join_tab,
- TABLE_LIST *table_ref, TABLE_LIST *all_tables,
- List<Item> *fields)
+ TABLE_LIST *table_ref, TABLE_LIST *all_tables)
{
TABLE *table= join_tab->table;
if (unique_table(thd, table_ref, all_tables, 0))
@@ -1182,20 +1407,21 @@ static bool safe_update_on_fly(THD *thd, JOIN_TAB *join_tab,
return TRUE; // At most one matching row
case JT_REF:
case JT_REF_OR_NULL:
- return !is_key_used(table, join_tab->ref.key, *fields);
+ return !is_key_used(table, join_tab->ref.key, table->write_set);
case JT_ALL:
/* If range search on index */
if (join_tab->quick)
- return !join_tab->quick->is_keys_used(fields);
+ return !join_tab->quick->is_keys_used(table->write_set);
/* If scanning in clustered key */
- if ((table->file->table_flags() & HA_PRIMARY_KEY_IN_READ_INDEX) &&
+ if ((table->file->ha_table_flags() & HA_PRIMARY_KEY_IN_READ_INDEX) &&
table->s->primary_key < MAX_KEY)
- return !is_key_used(table, table->s->primary_key, *fields);
+ return !is_key_used(table, table->s->primary_key, table->write_set);
return TRUE;
default:
break; // Avoid compler warning
}
return FALSE;
+
}
@@ -1236,18 +1462,20 @@ multi_update::initialize_tables(JOIN *join)
uint cnt= table_ref->shared;
List<Item> temp_fields;
ORDER group;
+ TMP_TABLE_PARAM *tmp_param;
+ table->mark_columns_needed_for_update();
if (ignore)
table->file->extra(HA_EXTRA_IGNORE_DUP_KEY);
if (table == main_table) // First table in join
{
- if (safe_update_on_fly(thd, join->join_tab, table_ref, all_tables,
- fields_for_table[cnt]))
+ if (safe_update_on_fly(thd, join->join_tab, table_ref, all_tables))
{
table_to_update= main_table; // Update table on the fly
continue;
}
}
+ table->prepare_for_position();
/*
enable uncacheable flag if we update a view with check option
@@ -1292,7 +1520,7 @@ loop_end:
}
}
- TMP_TABLE_PARAM *tmp_param= tmp_table_param+cnt;
+ tmp_param= tmp_table_param+cnt;
/*
Create a temporary table to store all fields that are changed for this
@@ -1307,10 +1535,10 @@ loop_end:
do
{
Field_string *field= new Field_string(tbl->file->ref_length, 0,
- tbl->alias,
- tbl, &my_charset_bin);
+ tbl->alias, &my_charset_bin);
if (!field)
DBUG_RETURN(1);
+ field->init(tbl);
/*
The field will be converted to varstring when creating tmp table if
table to be updated was created by mysql 4.1. Deny this.
@@ -1386,6 +1614,7 @@ bool multi_update::send_data(List<Item> &not_used_values)
for (cur_table= update_tables; cur_table; cur_table= cur_table->next_local)
{
TABLE *table= cur_table->table;
+ uint offset= cur_table->shared;
/*
Check if we are using outer join and we didn't find the row
or if we have already updated this row in the previous call to this
@@ -1401,17 +1630,18 @@ bool multi_update::send_data(List<Item> &not_used_values)
if (table->status & (STATUS_NULL_ROW | STATUS_UPDATED))
continue;
- uint offset= cur_table->shared;
- table->file->position(table->record[0]);
/*
We can use compare_record() to optimize away updates if
- the table handler is returning all columns
+ the table handler is returning all columns OR if
+ if all updated columns are read
*/
if (table == table_to_update)
{
bool can_compare_record;
- can_compare_record= !(table->file->table_flags() &
- HA_PARTIAL_COLUMN_READ);
+ can_compare_record= (!(table->file->ha_table_flags() &
+ HA_PARTIAL_COLUMN_READ) ||
+ bitmap_is_subset(table->write_set,
+ table->read_set));
table->status|= STATUS_UPDATED;
store_record(table,record[1]);
if (fill_record_n_invoke_before_triggers(thd, *fields_for_table[offset],
@@ -1421,7 +1651,7 @@ bool multi_update::send_data(List<Item> &not_used_values)
DBUG_RETURN(1);
found++;
- if (!can_compare_record || compare_record(table, thd->query_id))
+ if (!can_compare_record || compare_record(table))
{
int error;
if ((error= cur_table->view_check_option(thd, ignore)) !=
@@ -1433,33 +1663,42 @@ bool multi_update::send_data(List<Item> &not_used_values)
else if (error == VIEW_CHECK_ERROR)
DBUG_RETURN(1);
}
- if (!updated++)
- {
- /*
- Inform the main table that we are going to update the table even
- while we may be scanning it. This will flush the read cache
- if it's used.
- */
- main_table->file->extra(HA_EXTRA_PREPARE_FOR_UPDATE);
- }
- if ((error=table->file->update_row(table->record[1],
- table->record[0])))
- {
- updated--;
- if (!ignore || error != HA_ERR_FOUND_DUPP_KEY)
- {
+ if (!updated++)
+ {
+ /*
+ Inform the main table that we are going to update the table even
+ while we may be scanning it. This will flush the read cache
+ if it's used.
+ */
+ main_table->file->extra(HA_EXTRA_PREPARE_FOR_UPDATE);
+ }
+ if ((error=table->file->ha_update_row(table->record[1],
+ table->record[0])) &&
+ error != HA_ERR_RECORD_IS_THE_SAME)
+ {
+ updated--;
+ if (!ignore ||
+ table->file->is_fatal_error(error, HA_CHECK_DUP_KEY))
+ {
/*
- If (ignore && error == HA_ERR_FOUND_DUPP_KEY) we don't have to
+ If (ignore && error == is ignorable) we don't have to
do anything; otherwise...
*/
- if (error != HA_ERR_FOUND_DUPP_KEY)
+ if (table->file->is_fatal_error(error, HA_CHECK_DUP_KEY))
thd->fatal_error(); /* Other handler errors are fatal */
- table->file->print_error(error,MYF(0));
- DBUG_RETURN(1);
- }
- }
+
+ prepare_record_for_error_message(error, table);
+ table->file->print_error(error,MYF(0));
+ DBUG_RETURN(1);
+ }
+ }
else
{
+ if (error == HA_ERR_RECORD_IS_THE_SAME)
+ {
+ error= 0;
+ updated--;
+ }
/* non-transactional or transactional table got modified */
/* either multi_update class' flag is raised in its branch */
if (table->file->has_transactions())
@@ -1469,21 +1708,17 @@ bool multi_update::send_data(List<Item> &not_used_values)
trans_safe= 0;
thd->transaction.stmt.modified_non_trans_table= TRUE;
}
- if (table->triggers &&
- table->triggers->process_triggers(thd, TRG_EVENT_UPDATE,
- TRG_ACTION_AFTER, TRUE))
- DBUG_RETURN(1);
}
}
+ if (table->triggers &&
+ table->triggers->process_triggers(thd, TRG_EVENT_UPDATE,
+ TRG_ACTION_AFTER, TRUE))
+ DBUG_RETURN(1);
}
else
{
int error;
TABLE *tmp_table= tmp_tables[offset];
- /* Store regular updated fields in the row. */
- fill_record(thd,
- tmp_table->field + 1 + unupdated_check_opt_tables.elements,
- *values_for_table[offset], 1);
/*
For updatable VIEW store rowid of the updated table and
rowids of tables used in the CHECK OPTION condition.
@@ -1493,8 +1728,7 @@ bool multi_update::send_data(List<Item> &not_used_values)
TABLE *tbl= table;
do
{
- if (tbl != table)
- tbl->file->position(tbl->record[0]);
+ tbl->file->position(tbl->record[0]);
memcpy((char*) tmp_table->field[field_num]->ptr,
(char*) tbl->file->ref, tbl->file->ref_length);
/*
@@ -1506,8 +1740,13 @@ bool multi_update::send_data(List<Item> &not_used_values)
field_num++;
} while ((tbl= tbl_it++));
+ /* Store regular updated fields in the row. */
+ fill_record(thd,
+ tmp_table->field + 1 + unupdated_check_opt_tables.elements,
+ *values_for_table[offset], 1);
+
/* Write row, ignoring duplicated updates to a row */
- error= tmp_table->file->write_row(tmp_table->record[0]);
+ error= tmp_table->file->ha_write_row(tmp_table->record[0]);
if (error != HA_ERR_FOUND_DUPP_KEY && error != HA_ERR_FOUND_DUPP_UNIQUE)
{
if (error &&
@@ -1529,7 +1768,11 @@ void multi_update::send_error(uint errcode,const char *err)
{
/* First send error what ever it is ... */
my_error(errcode, MYF(0), err);
+}
+
+void multi_update::abort()
+{
/* the error was handled or nothing deleted and no side effects return */
if (error_handled ||
!thd->transaction.stmt.modified_non_trans_table && !updated)
@@ -1543,13 +1786,8 @@ void multi_update::send_error(uint errcode,const char *err)
If not attempt to do remaining updates.
*/
- if (trans_safe)
+ if (! trans_safe)
{
- DBUG_ASSERT(!updated || transactional_tables);
- (void) ha_autocommit_or_rollback(thd, 1);
- }
- else
- {
DBUG_ASSERT(thd->transaction.stmt.modified_non_trans_table);
if (do_update && table_count > 1)
{
@@ -1558,7 +1796,7 @@ void multi_update::send_error(uint errcode,const char *err)
todo/fixme: do_update() is never called with the arg 1.
should it change the signature to become argless?
*/
- VOID(do_updates(0));
+ VOID(do_updates());
}
}
if (thd->transaction.stmt.modified_non_trans_table)
@@ -1574,29 +1812,24 @@ void multi_update::send_error(uint errcode,const char *err)
got caught and if happens later the killed error is written
into repl event.
*/
- Query_log_event qinfo(thd, thd->query, thd->query_length,
- transactional_tables, FALSE);
- mysql_bin_log.write(&qinfo);
+ thd->binlog_query(THD::ROW_QUERY_TYPE,
+ thd->query, thd->query_length,
+ transactional_tables, FALSE);
}
thd->transaction.all.modified_non_trans_table= TRUE;
}
DBUG_ASSERT(trans_safe || !updated || thd->transaction.stmt.modified_non_trans_table);
-
- if (transactional_tables)
- {
- (void) ha_autocommit_or_rollback(thd, 1);
- }
}
-int multi_update::do_updates(bool from_send_error)
+int multi_update::do_updates()
{
TABLE_LIST *cur_table;
int local_error= 0;
ha_rows org_updated;
TABLE *table, *tmp_table;
List_iterator_fast<TABLE> check_opt_it(unupdated_check_opt_tables);
- DBUG_ENTER("do_updates");
+ DBUG_ENTER("multi_update::do_updates");
do_update= 0; // Don't retry this function
if (!found)
@@ -1640,8 +1873,10 @@ int multi_update::do_updates(bool from_send_error)
if ((local_error = tmp_table->file->ha_rnd_init(1)))
goto err;
- can_compare_record= !(table->file->table_flags() &
- HA_PARTIAL_COLUMN_READ);
+ can_compare_record= (!(table->file->ha_table_flags() &
+ HA_PARTIAL_COLUMN_READ) ||
+ bitmap_is_subset(table->write_set,
+ table->read_set));
for (;;)
{
@@ -1664,7 +1899,7 @@ int multi_update::do_updates(bool from_send_error)
{
if((local_error=
tbl->file->rnd_pos(tbl->record[0],
- (byte *) tmp_table->field[field_num]->ptr)))
+ (uchar *) tmp_table->field[field_num]->ptr)))
goto err;
field_num++;
} while((tbl= check_opt_it++));
@@ -1683,7 +1918,7 @@ int multi_update::do_updates(bool from_send_error)
TRG_ACTION_BEFORE, TRUE))
goto err2;
- if (!can_compare_record || compare_record(table, thd->query_id))
+ if (!can_compare_record || compare_record(table))
{
int error;
if ((error= cur_table->view_check_option(thd, ignore)) !=
@@ -1694,19 +1929,24 @@ int multi_update::do_updates(bool from_send_error)
else if (error == VIEW_CHECK_ERROR)
goto err;
}
- if ((local_error=table->file->update_row(table->record[1],
- table->record[0])))
+ if ((local_error=table->file->ha_update_row(table->record[1],
+ table->record[0])) &&
+ local_error != HA_ERR_RECORD_IS_THE_SAME)
{
- if (!ignore || local_error != HA_ERR_FOUND_DUPP_KEY)
+ if (!ignore ||
+ table->file->is_fatal_error(local_error, HA_CHECK_DUP_KEY))
goto err;
}
- updated++;
-
- if (table->triggers &&
- table->triggers->process_triggers(thd, TRG_EVENT_UPDATE,
- TRG_ACTION_AFTER, TRUE))
- goto err2;
+ if (local_error != HA_ERR_RECORD_IS_THE_SAME)
+ updated++;
+ else
+ local_error= 0;
}
+
+ if (table->triggers &&
+ table->triggers->process_triggers(thd, TRG_EVENT_UPDATE,
+ TRG_ACTION_AFTER, TRUE))
+ goto err2;
}
if (updated != org_updated)
@@ -1729,9 +1969,9 @@ int multi_update::do_updates(bool from_send_error)
DBUG_RETURN(0);
err:
- if (!from_send_error)
{
thd->fatal_error();
+ prepare_record_for_error_message(local_error, table);
table->file->print_error(local_error,MYF(0));
}
@@ -1761,20 +2001,22 @@ err2:
bool multi_update::send_eof()
{
char buff[STRING_BUFFER_USUAL_SIZE];
+ ulonglong id;
THD::killed_state killed_status= THD::NOT_KILLED;
- thd->proc_info="updating reference tables";
+ DBUG_ENTER("multi_update::send_eof");
+ thd_proc_info(thd, "updating reference tables");
/*
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) : 0;
+ int 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.
*/
killed_status= (local_error == 0)? THD::NOT_KILLED : thd->killed;
- thd->proc_info= "end";
+ thd_proc_info(thd, "end");
/* We must invalidate the query cache before binlog writing and
ha_autocommit_... */
@@ -1800,10 +2042,13 @@ bool multi_update::send_eof()
{
if (local_error == 0)
thd->clear_error();
- Query_log_event qinfo(thd, thd->query, thd->query_length,
- transactional_tables, FALSE, killed_status);
- if (mysql_bin_log.write(&qinfo) && trans_safe)
- local_error= 1; // Rollback update
+ if (thd->binlog_query(THD::ROW_QUERY_TYPE,
+ thd->query, thd->query_length,
+ transactional_tables, FALSE, killed_status) &&
+ trans_safe)
+ {
+ local_error= 1; // Rollback update
+ }
}
if (thd->transaction.stmt.modified_non_trans_table)
thd->transaction.all.modified_non_trans_table= TRUE;
@@ -1811,26 +2056,20 @@ bool multi_update::send_eof()
if (local_error != 0)
error_handled= TRUE; // to force early leave from ::send_error()
- if (transactional_tables)
- {
- if (ha_autocommit_or_rollback(thd, local_error != 0))
- local_error=1;
- }
-
if (local_error > 0) // if the above log write did not fail ...
{
/* Safety: If we haven't got an error before (can happen in do_updates) */
my_message(ER_UNKNOWN_ERROR, "An error occured in multi-table update",
MYF(0));
- return TRUE;
+ DBUG_RETURN(TRUE);
}
-
+ id= thd->arg_of_last_insert_id_function ?
+ thd->first_successful_insert_id_in_prev_stmt : 0;
sprintf(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;
- ::send_ok(thd, (ulong) thd->row_count_func,
- thd->insert_id_used ? thd->last_insert_id : 0L,buff);
- return FALSE;
+ ::my_ok(thd, (ulong) thd->row_count_func, id, buff);
+ DBUG_RETURN(FALSE);
}
diff --git a/sql/sql_view.cc b/sql/sql_view.cc
index 9e0fa87d5f5..fa71dd860f8 100644
--- a/sql/sql_view.cc
+++ b/sql/sql_view.cc
@@ -24,7 +24,7 @@
#define MD5_BUFF_LENGTH 33
-const LEX_STRING view_type= { (char*) STRING_WITH_LEN("VIEW") };
+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);
@@ -61,7 +61,7 @@ static void make_unique_view_field_name(Item *target,
char *name= (target->orig_name ?
target->orig_name :
target->name);
- uint name_len, attempt;
+ size_t name_len, attempt;
char buff[NAME_LEN+1];
List_iterator_fast<Item> itc(item_list);
@@ -268,11 +268,11 @@ bool create_view_precheck(THD *thd, TABLE_LIST *tables, TABLE_LIST *view,
*/
if ((check_access(thd, CREATE_VIEW_ACL, view->db, &view->grant.privilege,
0, 0, is_schema_db(view->db)) ||
- grant_option && check_grant(thd, CREATE_VIEW_ACL, view, 0, 1, 0)) ||
+ check_grant(thd, CREATE_VIEW_ACL, view, 0, 1, 0)) ||
(mode != VIEW_CREATE_NEW &&
(check_access(thd, DROP_ACL, view->db, &view->grant.privilege,
0, 0, is_schema_db(view->db)) ||
- grant_option && check_grant(thd, DROP_ACL, view, 0, 1, 0))))
+ check_grant(thd, DROP_ACL, view, 0, 1, 0))))
goto err;
for (sl= select_lex; sl; sl= sl->next_select())
@@ -322,7 +322,7 @@ bool create_view_precheck(THD *thd, TABLE_LIST *tables, TABLE_LIST *view,
{
if (check_access(thd, SELECT_ACL, tbl->db,
&tbl->grant.privilege, 0, 0, test(tbl->schema_table)) ||
- grant_option && check_grant(thd, SELECT_ACL, tbl, 0, 1, 0))
+ check_grant(thd, SELECT_ACL, tbl, 0, 1, 0))
goto err;
}
}
@@ -351,7 +351,7 @@ bool create_view_precheck(THD *thd, TABLE_LIST *tables, TABLE_LIST *view,
res= FALSE;
err:
- DBUG_RETURN(res || thd->net.report_error);
+ DBUG_RETURN(res || thd->is_error());
}
#else
@@ -397,7 +397,7 @@ bool mysql_create_view(THD *thd, TABLE_LIST *views,
/* This is ensured in the parser. */
DBUG_ASSERT(!lex->proc_list.first && !lex->result &&
- !lex->param_list.elements && !lex->derived_tables);
+ !lex->param_list.elements);
if (mode != VIEW_CREATE_NEW)
{
@@ -545,7 +545,7 @@ bool mysql_create_view(THD *thd, TABLE_LIST *views,
}
while ((item= it++, name= nm++))
{
- item->set_name(name->str, name->length, system_charset_info);
+ item->set_name(name->str, (uint) name->length, system_charset_info);
item->is_autogenerated_name= FALSE;
}
}
@@ -616,15 +616,14 @@ bool mysql_create_view(THD *thd, TABLE_LIST *views,
{
String buff;
const LEX_STRING command[3]=
- {{(char *)STRING_WITH_LEN("CREATE ")},
- {(char *)STRING_WITH_LEN("ALTER ")},
- {(char *)STRING_WITH_LEN("CREATE OR REPLACE ")}};
+ {{ C_STRING_WITH_LEN("CREATE ") },
+ { C_STRING_WITH_LEN("ALTER ") },
+ { C_STRING_WITH_LEN("CREATE OR REPLACE ") }};
buff.append(command[thd->lex->create_view_mode].str,
command[thd->lex->create_view_mode].length);
view_store_options(thd, views, &buff);
buff.append(STRING_WITH_LEN("VIEW "));
-
/* Test if user supplied a db (ie: we did not use thd->db) */
if (views->db && views->db[0] &&
(thd->db == NULL || strcmp(views->db, thd->db)))
@@ -640,7 +639,7 @@ bool mysql_create_view(THD *thd, TABLE_LIST *views,
List_iterator_fast<LEX_STRING> names(lex->view_list);
LEX_STRING *name;
int i;
-
+
for (i= 0; (name= names++); i++)
{
buff.append(i ? ", " : "(");
@@ -649,14 +648,10 @@ bool mysql_create_view(THD *thd, TABLE_LIST *views,
buff.append(')');
}
buff.append(STRING_WITH_LEN(" AS "));
- buff.append(views->query.str, views->query.length);
- if (views->with_check == VIEW_CHECK_LOCAL)
- buff.append(STRING_WITH_LEN(" WITH LOCAL CHECK OPTION"));
- else if (views->with_check == VIEW_CHECK_CASCADED)
- buff.append(STRING_WITH_LEN(" WITH CASCADED CHECK OPTION"));
-
- Query_log_event qinfo(thd, buff.ptr(), buff.length(), 0, FALSE);
- mysql_bin_log.write(&qinfo);
+ buff.append(views->source.str, views->source.length);
+
+ thd->binlog_query(THD::STMT_QUERY_TYPE,
+ buff.ptr(), buff.length(), FALSE, FALSE);
}
VOID(pthread_mutex_unlock(&LOCK_open));
@@ -666,20 +661,20 @@ bool mysql_create_view(THD *thd, TABLE_LIST *views,
if (res)
goto err;
- send_ok(thd);
+ my_ok(thd);
lex->link_first_table_back(view, link_to_local);
DBUG_RETURN(0);
err:
- thd->proc_info= "end";
+ thd_proc_info(thd, "end");
lex->link_first_table_back(view, link_to_local);
unit->cleanup();
- DBUG_RETURN(res || thd->net.report_error);
+ DBUG_RETURN(res || thd->is_error());
}
/* number of required parameters for making view */
-static const int required_view_parameters= 9;
+static const int required_view_parameters= 14;
/*
table of VIEW .frm field descriptors
@@ -688,39 +683,48 @@ static const int required_view_parameters= 9;
parse()
*/
static File_option view_parameters[]=
-{{{(char*) STRING_WITH_LEN("query")},
- my_offsetof(TABLE_LIST, query),
+{{{ C_STRING_WITH_LEN("query")},
+ my_offsetof(TABLE_LIST, select_stmt),
FILE_OPTIONS_ESTRING},
- {{(char*) STRING_WITH_LEN("md5")},
+ {{ C_STRING_WITH_LEN("md5")},
my_offsetof(TABLE_LIST, md5),
FILE_OPTIONS_STRING},
- {{(char*) STRING_WITH_LEN("updatable")},
+ {{ C_STRING_WITH_LEN("updatable")},
my_offsetof(TABLE_LIST, updatable_view),
FILE_OPTIONS_ULONGLONG},
- {{(char*) STRING_WITH_LEN("algorithm")},
+ {{ C_STRING_WITH_LEN("algorithm")},
my_offsetof(TABLE_LIST, algorithm),
FILE_OPTIONS_ULONGLONG},
- {{(char*) STRING_WITH_LEN("definer_user")},
+ {{ C_STRING_WITH_LEN("definer_user")},
my_offsetof(TABLE_LIST, definer.user),
FILE_OPTIONS_STRING},
- {{(char*) STRING_WITH_LEN("definer_host")},
+ {{ C_STRING_WITH_LEN("definer_host")},
my_offsetof(TABLE_LIST, definer.host),
FILE_OPTIONS_STRING},
- {{(char*) STRING_WITH_LEN("suid")},
+ {{ C_STRING_WITH_LEN("suid")},
my_offsetof(TABLE_LIST, view_suid),
FILE_OPTIONS_ULONGLONG},
- {{(char*) STRING_WITH_LEN("with_check_option")},
+ {{ C_STRING_WITH_LEN("with_check_option")},
my_offsetof(TABLE_LIST, with_check),
FILE_OPTIONS_ULONGLONG},
- {{(char*) STRING_WITH_LEN("timestamp")},
+ {{ C_STRING_WITH_LEN("timestamp")},
my_offsetof(TABLE_LIST, timestamp),
FILE_OPTIONS_TIMESTAMP},
- {{(char*)STRING_WITH_LEN("create-version")},
+ {{ C_STRING_WITH_LEN("create-version")},
my_offsetof(TABLE_LIST, file_version),
FILE_OPTIONS_ULONGLONG},
- {{(char*) STRING_WITH_LEN("source")},
+ {{ C_STRING_WITH_LEN("source")},
my_offsetof(TABLE_LIST, source),
FILE_OPTIONS_ESTRING},
+ {{(char*) STRING_WITH_LEN("client_cs_name")},
+ my_offsetof(TABLE_LIST, view_client_cs_name),
+ FILE_OPTIONS_STRING},
+ {{(char*) STRING_WITH_LEN("connection_cl_name")},
+ my_offsetof(TABLE_LIST, view_connection_cl_name),
+ FILE_OPTIONS_STRING},
+ {{(char*) STRING_WITH_LEN("view_body_utf8")},
+ my_offsetof(TABLE_LIST, view_body_utf8),
+ FILE_OPTIONS_ESTRING},
{{NullS, 0}, 0,
FILE_OPTIONS_STRING}
};
@@ -747,42 +751,77 @@ static int mysql_register_view(THD *thd, TABLE_LIST *view,
enum_view_create_mode mode)
{
LEX *lex= thd->lex;
- char buff[4096];
- String str(buff,(uint32) sizeof(buff), system_charset_info);
+
+ /*
+ View definition query -- a SELECT statement that fully defines view. It
+ is generated from the Item-tree built from the original (specified by
+ the user) query. The idea is that generated query should eliminates all
+ ambiguities and fix view structure at CREATE-time (once for all).
+ Item::print() virtual operation is used to generate view definition
+ query.
+
+ INFORMATION_SCHEMA query (IS query) -- a SQL statement describing a
+ view that is shown in INFORMATION_SCHEMA. Basically, it is 'view
+ definition query' with text literals converted to UTF8 and without
+ character set introducers.
+
+ For example:
+ Let's suppose we have:
+ CREATE TABLE t1(a INT, b INT);
+ User specified query:
+ CREATE VIEW v1(x, y) AS SELECT * FROM t1;
+ Generated query:
+ SELECT a AS x, b AS y FROM t1;
+ IS query:
+ SELECT a AS x, b AS y FROM t1;
+
+ View definition query is stored in the client character set.
+ */
+ char view_query_buff[4096];
+ String view_query(view_query_buff,
+ sizeof (view_query_buff),
+ thd->charset());
+
+ char is_query_buff[4096];
+ String is_query(is_query_buff,
+ sizeof (is_query_buff),
+ system_charset_info);
+
char md5[MD5_BUFF_LENGTH];
bool can_be_merged;
- char dir_buff[FN_REFLEN], file_buff[FN_REFLEN];
- LEX_STRING dir, file;
+ char dir_buff[FN_REFLEN], path_buff[FN_REFLEN];
+ LEX_STRING dir, file, path;
int error= 0;
DBUG_ENTER("mysql_register_view");
- /* print query */
- str.length(0);
+ /* Generate view definition and IS queries. */
+ view_query.length(0);
+ is_query.length(0);
{
ulong sql_mode= thd->variables.sql_mode & MODE_ANSI_QUOTES;
thd->variables.sql_mode&= ~MODE_ANSI_QUOTES;
- lex->unit.print(&str);
+
+ lex->unit.print(&view_query, QT_ORDINARY);
+ lex->unit.print(&is_query, QT_IS);
+
thd->variables.sql_mode|= sql_mode;
}
- DBUG_PRINT("info", ("View: %s", str.ptr()));
+ DBUG_PRINT("info", ("View: %s", view_query.ptr()));
/* fill structure */
- if (!make_lex_string(thd, &view->query, str.ptr(), str.length(), false))
+ view->source= thd->lex->create_view_select;
+
+ if (!thd->make_lex_string(&view->select_stmt, view_query.ptr(),
+ view_query.length(), false))
{
my_error(ER_OUT_OF_RESOURCES, MYF(0));
error= -1;
goto err;
}
- view->source.str= thd->query + thd->lex->create_view_select_start;
- view->source.length= (uint) ((char *)skip_rear_comments(thd->charset(),
- (char *)view->source.str,
- (char *)thd->query +
- thd->query_length) -
- view->source.str);
view->file_version= 1;
view->calc_md5(md5);
- if (!(view->md5.str= thd->memdup(md5, 32)))
+ if (!(view->md5.str= (char*) thd->memdup(md5, 32)))
{
my_error(ER_OUT_OF_RESOURCES, MYF(0));
error= -1;
@@ -827,15 +866,17 @@ static int mysql_register_view(THD *thd, TABLE_LIST *view,
}
loop_out:
/* print file name */
- (void) my_snprintf(dir_buff, FN_REFLEN, "%s/%s/",
- mysql_data_home, view->db);
- unpack_filename(dir_buff, dir_buff);
+ dir.length= build_table_filename(dir_buff, sizeof(dir_buff),
+ view->db, "", "", 0);
dir.str= dir_buff;
- dir.length= (uint) strlen(dir_buff);
- file.str= file_buff;
- file.length= (uint) (strxnmov(file_buff, FN_REFLEN, view->table_name, reg_ext,
- NullS) - file_buff);
+ path.length= build_table_filename(path_buff, sizeof(path_buff),
+ view->db, view->table_name, reg_ext, 0);
+ path.str= path_buff;
+
+ file.str= path.str + dir.length;
+ file.length= path.length - dir.length;
+
/* init timestamp */
if (!view->timestamp.str)
view->timestamp.str= view->timestamp_buffer;
@@ -847,8 +888,8 @@ loop_out:
File_parser *parser;
path.str= path_buff;
- fn_format(path_buff, file.str, dir.str, 0, MY_UNPACK_FILENAME);
- path.length= (uint) strlen(path_buff);
+ fn_format(path_buff, file.str, dir.str, "", MY_UNPACK_FILENAME);
+ path.length= strlen(path_buff);
if (!access(path.str, F_OK))
{
@@ -888,6 +929,29 @@ loop_out:
}
}
+ /* Initialize view creation context from the environment. */
+
+ view->view_creation_ctx= View_creation_ctx::create(thd);
+
+ /*
+ Set LEX_STRING attributes in view-structure for parser to create
+ frm-file.
+ */
+
+ lex_string_set(&view->view_client_cs_name,
+ view->view_creation_ctx->get_client_cs()->csname);
+
+ lex_string_set(&view->view_connection_cl_name,
+ view->view_creation_ctx->get_connection_cl()->name);
+
+ if (!thd->make_lex_string(&view->view_body_utf8, is_query.ptr(),
+ is_query.length(), false))
+ {
+ my_error(ER_OUT_OF_RESOURCES, MYF(0));
+ error= -1;
+ goto err;
+ }
+
/*
Check that table of main select do not used in subqueries.
@@ -899,7 +963,7 @@ loop_out:
UNION
*/
if (view->updatable_view &&
- !lex->select_lex.next_select() &&
+ !lex->select_lex.master_unit()->is_union() &&
!((TABLE_LIST*)lex->select_lex.table_list.first)->next_local &&
find_table_in_global_list(lex->query_tables->next_global,
lex->query_tables->db,
@@ -917,13 +981,15 @@ loop_out:
}
if (sql_create_definition_file(&dir, &file, view_file_type,
- (gptr)view, view_parameters))
+ (uchar*)view, view_parameters))
{
- error= thd->net.report_error? -1 : 1;
+ error= thd->is_error() ? -1 : 1;
goto err;
}
DBUG_RETURN(0);
err:
+ view->select_stmt.str= NULL;
+ view->select_stmt.length= 0;
view->md5.str= NULL;
view->md5.length= 0;
DBUG_RETURN(error);
@@ -952,9 +1018,10 @@ bool mysql_make_view(THD *thd, File_parser *parser, TABLE_LIST *table,
LEX *old_lex, *lex;
Query_arena *arena, backup;
TABLE_LIST *top_view= table->top_table();
- int res;
+ bool parse_status;
bool result, view_is_mergeable;
TABLE_LIST *view_main_select_tables;
+
DBUG_ENTER("mysql_make_view");
DBUG_PRINT("info", ("table: 0x%lx (%s)", (ulong) table, table->table_name));
@@ -980,13 +1047,10 @@ bool mysql_make_view(THD *thd, File_parser *parser, TABLE_LIST *table,
DBUG_RETURN(0);
}
- List<String> *index_list= table->use_index ? table->use_index
- : table->ignore_index;
- if (index_list)
+ if (table->index_hints && table->index_hints->elements)
{
- DBUG_ASSERT(index_list->head()); // should never fail
- my_error(ER_KEY_DOES_NOT_EXITS, MYF(0), index_list->head()->c_ptr(),
- table->table_name);
+ my_error(ER_KEY_DOES_NOT_EXITS, MYF(0),
+ table->index_hints->head()->key_name.str, table->table_name);
DBUG_RETURN(TRUE);
}
@@ -1031,7 +1095,7 @@ bool mysql_make_view(THD *thd, File_parser *parser, TABLE_LIST *table,
TODO: when VIEWs will be stored in cache, table mem_root should
be used here
*/
- if (parser->parse((gptr)table, thd->mem_root, view_parameters,
+ if (parser->parse((uchar*)table, thd->mem_root, view_parameters,
required_view_parameters, &file_parser_dummy_hook))
goto err;
@@ -1065,6 +1129,14 @@ bool mysql_make_view(THD *thd, File_parser *parser, TABLE_LIST *table,
/*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.
+ */
+
+ table->view_creation_ctx= View_creation_ctx::create(thd, table);
+
+ /*
TODO: TABLE mem root should be used here when VIEW will be stored in
TABLE cache
@@ -1076,21 +1148,23 @@ bool mysql_make_view(THD *thd, File_parser *parser, TABLE_LIST *table,
char old_db_buf[NAME_LEN+1];
LEX_STRING old_db= { old_db_buf, sizeof(old_db_buf) };
bool dbchanged;
- Parser_state parser_state(thd, table->query.str, table->query.length);
+ Parser_state parser_state(thd,
+ table->select_stmt.str,
+ table->select_stmt.length);
/*
Use view db name as thread default database, in order to ensure
that the view is parsed and prepared correctly.
*/
- if ((result= sp_use_new_db(thd, table->view_db, &old_db, 1, &dbchanged)))
+ if ((result= mysql_opt_change_db(thd, &table->view_db, &old_db, 1,
+ &dbchanged)))
goto end;
- thd->m_parser_state= &parser_state;
lex_start(thd);
view_select= &lex->select_lex;
view_select->select_number= ++thd->select_number;
- ulong save_mode= thd->variables.sql_mode;
+ ulong 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
@@ -1117,22 +1191,23 @@ bool mysql_make_view(THD *thd, File_parser *parser, TABLE_LIST *table,
*/
thd->variables.sql_mode&= ~(MODE_PIPES_AS_CONCAT | MODE_ANSI_QUOTES |
MODE_IGNORE_SPACE | MODE_NO_BACKSLASH_ESCAPES);
- CHARSET_INFO *save_cs= thd->variables.character_set_client;
- thd->variables.character_set_client= system_charset_info;
- res= MYSQLparse((void *)thd);
- thd->m_parser_state= NULL;
+
+ /* Parse the query. */
+
+ parse_status= parse_sql(thd, & parser_state, table->view_creation_ctx);
+
+ /* Restore environment. */
if ((old_lex->sql_command == SQLCOM_SHOW_FIELDS) ||
(old_lex->sql_command == SQLCOM_SHOW_CREATE))
lex->sql_command= old_lex->sql_command;
- thd->variables.character_set_client= save_cs;
- thd->variables.sql_mode= save_mode;
+ thd->variables.sql_mode= saved_mode;
if (dbchanged && mysql_change_db(thd, &old_db, TRUE))
goto err;
}
- if (!res && !thd->is_fatal_error)
+ if (!parse_status)
{
TABLE_LIST *view_tables= lex->query_tables;
TABLE_LIST *view_tables_tail= 0;
@@ -1146,8 +1221,8 @@ bool mysql_make_view(THD *thd, File_parser *parser, TABLE_LIST *table,
if (!table->prelocking_placeholder &&
(old_lex->sql_command == SQLCOM_SELECT && old_lex->describe))
{
- if (check_table_access(thd, SELECT_ACL, view_tables, 1) &&
- check_table_access(thd, SHOW_VIEW_ACL, table, 1))
+ if (check_table_access(thd, SELECT_ACL, view_tables, UINT_MAX, TRUE) &&
+ check_table_access(thd, SHOW_VIEW_ACL, table, UINT_MAX, TRUE))
{
my_message(ER_VIEW_NO_EXPLAIN, ER(ER_VIEW_NO_EXPLAIN), MYF(0));
goto err;
@@ -1157,7 +1232,7 @@ bool mysql_make_view(THD *thd, File_parser *parser, TABLE_LIST *table,
(old_lex->sql_command == SQLCOM_SHOW_CREATE) &&
!table->belong_to_view)
{
- if (check_table_access(thd, SHOW_VIEW_ACL, table, 0))
+ if (check_table_access(thd, SHOW_VIEW_ACL, table, UINT_MAX, FALSE))
goto err;
}
@@ -1215,6 +1290,12 @@ bool mysql_make_view(THD *thd, File_parser *parser, TABLE_LIST *table,
table->next_global= view_tables;
}
+ /*
+ 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();
view_is_mergeable= (table->algorithm != VIEW_ALGORITHM_TMPTABLE &&
lex->can_be_merged());
LINT_INIT(view_main_select_tables);
@@ -1439,11 +1520,11 @@ ok:
(st_select_lex_node**)&old_lex->all_selects_list;
ok2:
- if (!old_lex->time_zone_tables_used && thd->lex->time_zone_tables_used)
- old_lex->time_zone_tables_used= thd->lex->time_zone_tables_used;
+ DBUG_ASSERT(lex == thd->lex);
+ thd->lex= old_lex; // Needed for prepare_security
result= !table->prelocking_placeholder && table->prepare_security(thd);
- lex_end(thd->lex);
+ lex_end(lex);
end:
if (arena)
thd->restore_active_arena(arena, &backup);
@@ -1476,24 +1557,24 @@ err:
bool mysql_drop_view(THD *thd, TABLE_LIST *views, enum_drop_mode drop_mode)
{
- DBUG_ENTER("mysql_drop_view");
char path[FN_REFLEN];
TABLE_LIST *view;
- frm_type_enum type;
- db_type not_used;
String non_existant_views;
char *wrong_object_db= NULL, *wrong_object_name= NULL;
bool error= FALSE;
+ enum legacy_db_type not_used;
bool some_views_deleted= FALSE;
bool something_wrong= FALSE;
+ DBUG_ENTER("mysql_drop_view");
VOID(pthread_mutex_lock(&LOCK_open));
for (view= views; view; view= view->next_local)
{
- strxnmov(path, FN_REFLEN, mysql_data_home, "/", view->db, "/",
- view->table_name, reg_ext, NullS);
- (void) unpack_filename(path, path);
- type= FRMTYPE_ERROR;
+ TABLE_SHARE *share;
+ frm_type_enum type= FRMTYPE_ERROR;
+ build_table_filename(path, sizeof(path),
+ view->db, view->table_name, reg_ext, 0);
+
if (access(path, F_OK) ||
FRMTYPE_VIEW != (type= mysql_frm_type(thd, path, &not_used)))
{
@@ -1524,7 +1605,22 @@ bool mysql_drop_view(THD *thd, TABLE_LIST *views, enum_drop_mode drop_mode)
}
if (my_delete(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
+ */
+ 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);
+ }
query_cache_invalidate3(thd, view, 0);
sp_cache_invalidate();
}
@@ -1542,20 +1638,19 @@ bool mysql_drop_view(THD *thd, TABLE_LIST *views, enum_drop_mode drop_mode)
something_wrong= error || wrong_object_name || non_existant_views.length();
if (some_views_deleted || !something_wrong)
{
- if (!something_wrong)
- thd->clear_error();
- Query_log_event qinfo(thd, thd->query, thd->query_length, 0, FALSE);
- mysql_bin_log.write(&qinfo);
+ /* if something goes wrong, bin-log with possible error code,
+ otherwise bin-log with error code cleared.
+ */
+ write_bin_log(thd, !something_wrong, thd->query, thd->query_length);
}
VOID(pthread_mutex_unlock(&LOCK_open));
-
+
if (something_wrong)
{
DBUG_RETURN(TRUE);
}
-
- send_ok(thd);
+ my_ok(thd);
DBUG_RETURN(FALSE);
}
@@ -1573,18 +1668,18 @@ bool mysql_drop_view(THD *thd, TABLE_LIST *views, enum_drop_mode drop_mode)
FRMTYPE_VIEW view
*/
-frm_type_enum mysql_frm_type(THD *thd, char *path, db_type *dbt)
+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
- int error;
+ 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, (byte*) header, sizeof(header), MYF(MY_WME | MY_NABP));
+ error= my_read(file, (uchar*) header, sizeof(header), MYF(MY_NABP));
my_close(file, MYF(MY_WME));
if (error)
@@ -1602,7 +1697,7 @@ frm_type_enum mysql_frm_type(THD *thd, char *path, db_type *dbt)
(header[2] < FRM_VER+3 || header[2] > FRM_VER+4)))
DBUG_RETURN(FRMTYPE_TABLE);
- *dbt= ha_checktype(thd, (enum db_type) (uint) *(header + 3), 0, 0);
+ *dbt= (enum legacy_db_type) (uint) *(header + 3);
DBUG_RETURN(FRMTYPE_TABLE); // Is probably a .frm table
}
@@ -1616,8 +1711,8 @@ frm_type_enum mysql_frm_type(THD *thd, char *path, db_type *dbt)
view view for check with opened table
DESCRIPTION
- If it is VIEW and query have LIMIT clause then check that undertlying
- table of viey contain one of following:
+ If it is VIEW and query have LIMIT clause then check that underlying
+ table of view contain one of following:
1) primary key of underlying table
2) unique key underlying table with fields for which NULL value is
impossible
@@ -1657,17 +1752,19 @@ bool check_key_in_view(THD *thd, TABLE_LIST *view)
this operation should not have influence on Field::query_id, to avoid
marking as used fields which are not used
*/
- bool save_set_query_id= thd->set_query_id;
- thd->set_query_id= 0;
+ enum_mark_columns save_mark_used_columns= thd->mark_used_columns;
+ thd->mark_used_columns= MARK_COLUMNS_NONE;
+ DBUG_PRINT("info", ("thd->mark_used_columns: %d", thd->mark_used_columns));
for (Field_translator *fld= trans; fld < end_of_trans; fld++)
{
if (!fld->item->fixed && fld->item->fix_fields(thd, &fld->item))
{
- thd->set_query_id= save_set_query_id;
+ thd->mark_used_columns= save_mark_used_columns;
return TRUE;
}
}
- thd->set_query_id= save_set_query_id;
+ thd->mark_used_columns= save_mark_used_columns;
+ DBUG_PRINT("info", ("thd->mark_used_columns: %d", thd->mark_used_columns));
}
/* Loop over all keys to see if a unique-not-null key is used */
for (;key_info != key_info_end ; key_info++)
@@ -1816,25 +1913,23 @@ mysql_rename_view(THD *thd,
const char *new_name,
TABLE_LIST *view)
{
- LEX_STRING pathstr, file;
+ LEX_STRING pathstr;
File_parser *parser;
- char view_path[FN_REFLEN];
+ char path_buff[FN_REFLEN];
bool error= TRUE;
-
DBUG_ENTER("mysql_rename_view");
- strxnmov(view_path, FN_REFLEN, mysql_data_home, "/", view->db, "/",
- view->table_name, reg_ext, NullS);
- (void) unpack_filename(view_path, view_path);
-
- pathstr.str= (char *)view_path;
- pathstr.length= (uint) strlen(view_path);
+ pathstr.str= (char *) path_buff;
+ pathstr.length= build_table_filename(path_buff, sizeof(path_buff) - 1,
+ view->db, view->table_name,
+ reg_ext, 0);
if ((parser= sql_parse_prepare(&pathstr, thd->mem_root, 1)) &&
is_equal(&view_type, parser->type()))
{
TABLE_LIST view_def;
- char dir_buff[FN_REFLEN], file_buff[FN_REFLEN];
+ char dir_buff[FN_REFLEN];
+ LEX_STRING dir, file;
/*
To be PS-friendly we should either to restore state of
@@ -1847,7 +1942,7 @@ mysql_rename_view(THD *thd,
view_def.view_suid= TRUE;
/* get view definition and source */
- if (parser->parse((gptr)&view_def, thd->mem_root, view_parameters,
+ if (parser->parse((uchar*)&view_def, thd->mem_root, view_parameters,
array_elements(view_parameters)-1,
&file_parser_dummy_hook))
goto err;
@@ -1856,18 +1951,19 @@ mysql_rename_view(THD *thd,
if (rename_in_schema_file(thd, view->db, view->table_name, new_name))
goto err;
- strxnmov(dir_buff, FN_REFLEN, mysql_data_home, "/", view->db, "/", NullS);
- (void) unpack_filename(dir_buff, dir_buff);
+ dir.str= dir_buff;
+ dir.length= build_table_filename(dir_buff, sizeof(dir_buff) - 1,
+ view->db, "", "", 0);
- pathstr.str= (char*)dir_buff;
- pathstr.length= (uint) strlen(dir_buff);
+ pathstr.str= path_buff;
+ pathstr.length= build_table_filename(path_buff, sizeof(path_buff) - 1,
+ view->db, new_name, reg_ext, 0);
- file.str= file_buff;
- file.length= (uint) (strxnmov(file_buff, FN_REFLEN, new_name, reg_ext, NullS)
- - file_buff);
+ file.str= pathstr.str + dir.length;
+ file.length= pathstr.length - dir.length;
- if (sql_create_definition_file(&pathstr, &file, view_file_type,
- (gptr)&view_def, view_parameters))
+ if (sql_create_definition_file(&dir, &file, view_file_type,
+ (uchar*)&view_def, view_parameters))
{
/* restore renamed view in case of error */
rename_in_schema_file(thd, view->db, new_name, view->table_name);
diff --git a/sql/sql_view.h b/sql/sql_view.h
index 1d45283352b..b8138663489 100644
--- a/sql/sql_view.h
+++ b/sql/sql_view.h
@@ -30,7 +30,7 @@ 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, db_type *dbt);
+frm_type_enum mysql_frm_type(THD *thd, char *path, enum legacy_db_type *dbt);
int view_checksum(THD *thd, TABLE_LIST *view);
diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy
index 0eefe782354..7df035bbfc3 100644
--- a/sql/sql_yacc.yy
+++ b/sql/sql_yacc.yy
@@ -1,4 +1,4 @@
-/* Copyright (C) 2000-2003 MySQL AB
+/* Copyright 2000-2008 MySQL AB, 2008 Sun Microsystems, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -15,8 +15,13 @@
/* sql_yacc.yy */
+/**
+ @defgroup Parser Parser
+ @{
+*/
+
%{
-/* thd is passed as an arg to yyparse(), and subsequently to yylex().
+/* 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.
*/
@@ -27,7 +32,7 @@
#define MYSQL_YACC
#define YYINITDEPTH 100
-#define YYMAXDEPTH 3200 /* Because of 64K stack */
+#define YYMAXDEPTH 3200 /* Because of 64K stack */
#define Lex (YYTHD->lex)
#define Select Lex->current_select
#include "mysql_priv.h"
@@ -38,20 +43,27 @@
#include "sp_pcontext.h"
#include "sp_rcontext.h"
#include "sp.h"
+#include "event_parse_data.h"
#include <myisam.h>
#include <myisammrg.h>
int yylex(void *yylval, void *yythd);
-const LEX_STRING null_lex_str={0,0};
+const LEX_STRING null_lex_str= {0,0};
-#define yyoverflow(A,B,C,D,E,F) {ulong val= *(F); if (my_yyoverflow((B), (D), &val)) { yyerror((char*) (A)); return 2; } else { *(F)= (YYSIZE_T)val; }}
-
-#undef WARN_DEPRECATED /* this macro is also defined in mysql_priv.h */
-#define WARN_DEPRECATED(A,B) \
- push_warning_printf(((THD *)yythd), MYSQL_ERROR::WARN_LEVEL_WARN, \
- ER_WARN_DEPRECATED_SYNTAX, \
- ER(ER_WARN_DEPRECATED_SYNTAX), (A), (B));
+#define yyoverflow(A,B,C,D,E,F) \
+ { \
+ ulong val= *(F); \
+ if (my_yyoverflow((B), (D), &val)) \
+ { \
+ yyerror((char*) (A)); \
+ return 2; \
+ } \
+ else \
+ { \
+ *(F)= (YYSIZE_T)val; \
+ } \
+ }
#define MYSQL_YYABORT \
do \
@@ -67,6 +79,29 @@ const LEX_STRING null_lex_str={0,0};
MYSQL_YYABORT; \
}
+/*
+ Work around for broken code generated by bison 1.875.
+
+ The code generated by bison 1.875a and later, bison 2.1 and bison 2.2 is ok.
+ With bison 1.875 however, the generated code contains:
+<pre>
+ yyerrlab1:
+ #if defined (__GNUC_MINOR__) && 2093 <= (__GNUC__ * 1000 + __GNUC_MINOR__)
+ __attribute__ ((__unused__))
+ #endif
+</pre>
+ This usage of __attribute__ is illegal, so we remove it.
+ See the following references for details:
+ http://lists.gnu.org/archive/html/bug-bison/2004-02/msg00014.html
+ http://gcc.gnu.org/bugzilla/show_bug.cgi?id=14273
+*/
+
+#if defined (__GNUC_MINOR__) && 2093 <= (__GNUC__ * 1000 + __GNUC_MINOR__)
+#undef __attribute__
+#define __attribute__(X)
+#endif
+
+
#ifndef DBUG_OFF
#define YYDEBUG 1
#else
@@ -89,7 +124,7 @@ void my_parse_error(const char *s)
THD *thd= current_thd;
Lex_input_stream *lip= & thd->m_parser_state->m_lip;
- const char *yytext= lip->tok_start;
+ const char *yytext= lip->get_tok_start();
/* Push an error into the error stack */
my_printf_error(ER_PARSE_ERROR, ER(ER_PARSE_ERROR), MYF(0), s,
(yytext ? yytext : ""),
@@ -156,6 +191,17 @@ void turn_parser_debug_on()
}
#endif
+static bool is_native_function(THD *thd, const LEX_STRING *name)
+{
+ if (find_native_function_builder(thd, *name))
+ return true;
+
+ if (is_lex_native_function(name))
+ return true;
+
+ return false;
+}
+
/**
Helper action for a case statement (entering the CASE).
@@ -417,6 +463,7 @@ Item* handle_sql2003_note184_exception(THD *thd, Item* left, bool equal,
int num;
ulong ulong_num;
ulonglong ulonglong_number;
+ longlong longlong_number;
LEX_STRING lex_str;
LEX_STRING *lex_str_ptr;
LEX_SYMBOL symbol;
@@ -427,7 +474,7 @@ Item* handle_sql2003_note184_exception(THD *thd, Item* left, bool equal,
List<Item> *item_list;
List<String> *string_list;
String *string;
- key_part_spec *key_part;
+ Key_part_spec *key_part;
TABLE_LIST *table_list;
udf_func *udf;
LEX_USER *lex_user;
@@ -435,12 +482,13 @@ Item* handle_sql2003_note184_exception(THD *thd, Item* left, bool equal,
enum enum_var_type var_type;
Key::Keytype key_type;
enum ha_key_alg key_alg;
- enum db_type db_type;
+ handlerton *db_type;
enum row_type row_type;
enum ha_rkey_function ha_rkey_mode;
enum enum_tx_isolation tx_isolation;
enum Cast_target cast_type;
enum Item_udftype udf_type;
+ enum ha_choice choice;
CHARSET_INFO *charset;
thr_lock_type lock_type;
interval_type interval, interval_time_st;
@@ -451,223 +499,237 @@ Item* handle_sql2003_note184_exception(THD *thd, Item* left, bool equal,
struct { int vars, conds, hndlrs, curs; } spblock;
sp_name *spname;
struct st_lex *lex;
+ sp_head *sphead;
+ struct p_elem_val *p_elem_value;
+ enum index_hint_type index_hint;
}
%{
bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%}
-%pure_parser /* We have threads */
+%pure_parser /* We have threads */
/*
- Currently there are 240 shift/reduce conflicts.
+ Currently there are 169 shift/reduce conflicts.
We should not introduce new conflicts any more.
*/
-%expect 240
+%expect 169
-%token END_OF_INPUT
+/*
+ Comments for TOKENS.
+ For each token, please include in the same line a comment that contains
+ the following tags:
+ SQL-2003-R : Reserved keyword as per SQL-2003
+ SQL-2003-N : Non Reserved keyword as per SQL-2003
+ SQL-1999-R : Reserved keyword as per SQL-1999
+ SQL-1999-N : Non Reserved keyword as per SQL-1999
+ MYSQL : MySQL extention (unspecified)
+ MYSQL-FUNC : MySQL extention, function
+ INTERNAL : Not a real token, lex optimization
+ OPERATOR : SQL operator
+ FUTURE-USE : Reserved for futur use
+
+ This makes the code grep-able, and helps maintenance.
+*/
-%token ABORT_SYM
-%token ACTION
-%token ADD
-%token ADDDATE_SYM
-%token AFTER_SYM
+%token ABORT_SYM /* INTERNAL (used in lex) */
+%token ACCESSIBLE_SYM
+%token ACTION /* SQL-2003-N */
+%token ADD /* SQL-2003-R */
+%token ADDDATE_SYM /* MYSQL-FUNC */
+%token AFTER_SYM /* SQL-2003-N */
%token AGAINST
%token AGGREGATE_SYM
%token ALGORITHM_SYM
-%token ALL
-%token ALTER
+%token ALL /* SQL-2003-R */
+%token ALTER /* SQL-2003-R */
%token ANALYZE_SYM
-%token AND_AND_SYM
-%token AND_SYM
-%token ANY_SYM
-%token AS
-%token ASC
-%token ASCII_SYM
-%token ASENSITIVE_SYM
-%token ATAN
+%token AND_AND_SYM /* OPERATOR */
+%token AND_SYM /* SQL-2003-R */
+%token ANY_SYM /* SQL-2003-R */
+%token AS /* SQL-2003-R */
+%token ASC /* SQL-2003-N */
+%token ASCII_SYM /* MYSQL-FUNC */
+%token ASENSITIVE_SYM /* FUTURE-USE */
+%token AT_SYM /* SQL-2003-R */
+%token AUTHORS_SYM
+%token AUTOEXTEND_SIZE_SYM
%token AUTO_INC
%token AVG_ROW_LENGTH
-%token AVG_SYM
+%token AVG_SYM /* SQL-2003-N */
%token BACKUP_SYM
-%token BEFORE_SYM
-%token BEGIN_SYM
-%token BENCHMARK_SYM
-%token BERKELEY_DB_SYM
-%token BIGINT
-%token BINARY
+%token BEFORE_SYM /* SQL-2003-N */
+%token BEGIN_SYM /* SQL-2003-R */
+%token BETWEEN_SYM /* SQL-2003-R */
+%token BIGINT /* SQL-2003-R */
+%token BINARY /* SQL-2003-R */
%token BINLOG_SYM
%token BIN_NUM
-%token BIT_AND
-%token BIT_OR
-%token BIT_SYM
-%token BIT_XOR
-%token BLOB_SYM
-%token BOOLEAN_SYM
+%token BIT_AND /* MYSQL-FUNC */
+%token BIT_OR /* MYSQL-FUNC */
+%token BIT_SYM /* MYSQL-FUNC */
+%token BIT_XOR /* MYSQL-FUNC */
+%token BLOB_SYM /* SQL-2003-R */
+%token BLOCK_SYM
+%token BOOLEAN_SYM /* SQL-2003-R */
%token BOOL_SYM
-%token BOTH
+%token BOTH /* SQL-2003-R */
%token BTREE_SYM
-%token BY
+%token BY /* SQL-2003-R */
%token BYTE_SYM
%token CACHE_SYM
-%token CALL_SYM
-%token CASCADE
-%token CASCADED
-%token CAST_SYM
-%token CHAIN_SYM
+%token CALL_SYM /* SQL-2003-R */
+%token CASCADE /* SQL-2003-N */
+%token CASCADED /* SQL-2003-R */
+%token CASE_SYM /* SQL-2003-R */
+%token CAST_SYM /* SQL-2003-R */
+%token CHAIN_SYM /* SQL-2003-N */
%token CHANGE
%token CHANGED
%token CHARSET
-%token CHAR_SYM
+%token CHAR_SYM /* SQL-2003-R */
%token CHECKSUM_SYM
-%token CHECK_SYM
+%token CHECK_SYM /* SQL-2003-R */
%token CIPHER_SYM
%token CLIENT_SYM
-%token CLOSE_SYM
-%token COALESCE
+%token CLOSE_SYM /* SQL-2003-R */
+%token COALESCE /* SQL-2003-N */
%token CODE_SYM
-%token COLLATE_SYM
-%token COLLATION_SYM
+%token COLLATE_SYM /* SQL-2003-R */
+%token COLLATION_SYM /* SQL-2003-N */
%token COLUMNS
-%token COLUMN_SYM
+%token COLUMN_SYM /* SQL-2003-R */
%token COMMENT_SYM
-%token COMMITTED_SYM
-%token COMMIT_SYM
+%token COMMITTED_SYM /* SQL-2003-N */
+%token COMMIT_SYM /* SQL-2003-R */
%token COMPACT_SYM
+%token COMPLETION_SYM
%token COMPRESSED_SYM
-%token CONCAT
-%token CONCAT_WS
%token CONCURRENT
-%token CONDITION_SYM
+%token CONDITION_SYM /* SQL-2003-N */
%token CONNECTION_SYM
%token CONSISTENT_SYM
-%token CONSTRAINT
-%token CONTAINS_SYM
-%token CONTINUE_SYM
-%token CONVERT_SYM
-%token CONVERT_TZ_SYM
-%token COUNT_SYM
-%token CREATE
-%token CROSS
-%token CUBE_SYM
-%token CURDATE
-%token CURRENT_USER
-%token CURSOR_SYM
-%token CURTIME
+%token CONSTRAINT /* SQL-2003-R */
+%token CONTAINS_SYM /* SQL-2003-N */
+%token CONTEXT_SYM
+%token CONTINUE_SYM /* SQL-2003-R */
+%token CONTRIBUTORS_SYM
+%token CONVERT_SYM /* SQL-2003-N */
+%token COUNT_SYM /* SQL-2003-N */
+%token CPU_SYM
+%token CREATE /* SQL-2003-R */
+%token CROSS /* SQL-2003-R */
+%token CUBE_SYM /* SQL-2003-R */
+%token CURDATE /* MYSQL-FUNC */
+%token CURRENT_USER /* SQL-2003-R */
+%token CURSOR_SYM /* SQL-2003-R */
+%token CURTIME /* MYSQL-FUNC */
%token DATABASE
%token DATABASES
-%token DATA_SYM
+%token DATAFILE_SYM
+%token DATA_SYM /* SQL-2003-N */
%token DATETIME
-%token DATE_ADD_INTERVAL
-%token DATE_SUB_INTERVAL
-%token DATE_SYM
+%token DATE_ADD_INTERVAL /* MYSQL-FUNC */
+%token DATE_SUB_INTERVAL /* MYSQL-FUNC */
+%token DATE_SYM /* SQL-2003-R */
%token DAY_HOUR_SYM
%token DAY_MICROSECOND_SYM
%token DAY_MINUTE_SYM
%token DAY_SECOND_SYM
-%token DAY_SYM
-%token DEALLOCATE_SYM
+%token DAY_SYM /* SQL-2003-R */
+%token DEALLOCATE_SYM /* SQL-2003-R */
%token DECIMAL_NUM
-%token DECIMAL_SYM
-%token DECLARE_SYM
-%token DECODE_SYM
-%token DEFAULT
+%token DECIMAL_SYM /* SQL-2003-R */
+%token DECLARE_SYM /* SQL-2003-R */
+%token DEFAULT /* SQL-2003-R */
%token DEFINER_SYM
%token DELAYED_SYM
%token DELAY_KEY_WRITE_SYM
-%token DELETE_SYM
-%token DESC
-%token DESCRIBE
-%token DES_DECRYPT_SYM
-%token DES_ENCRYPT_SYM
+%token DELETE_SYM /* SQL-2003-R */
+%token DESC /* SQL-2003-N */
+%token DESCRIBE /* SQL-2003-R */
%token DES_KEY_FILE
-%token DETERMINISTIC_SYM
+%token DETERMINISTIC_SYM /* SQL-2003-R */
%token DIRECTORY_SYM
%token DISABLE_SYM
%token DISCARD
-%token DISTINCT
+%token DISK_SYM
+%token DISTINCT /* SQL-2003-R */
%token DIV_SYM
-%token DOUBLE_SYM
+%token DOUBLE_SYM /* SQL-2003-R */
%token DO_SYM
-%token DROP
+%token DROP /* SQL-2003-R */
%token DUAL_SYM
%token DUMPFILE
%token DUPLICATE_SYM
-%token DYNAMIC_SYM
-%token EACH_SYM
+%token DYNAMIC_SYM /* SQL-2003-R */
+%token EACH_SYM /* SQL-2003-R */
+%token ELSE /* SQL-2003-R */
%token ELSEIF_SYM
-%token ELT_FUNC
%token ENABLE_SYM
%token ENCLOSED
-%token ENCODE_SYM
-%token ENCRYPT
-%token END
+%token END /* SQL-2003-R */
+%token ENDS_SYM
+%token END_OF_INPUT /* INTERNAL */
%token ENGINES_SYM
%token ENGINE_SYM
%token ENUM
-%token EQ
-%token EQUAL_SYM
+%token EQ /* OPERATOR */
+%token EQUAL_SYM /* OPERATOR */
%token ERRORS
%token ESCAPED
-%token ESCAPE_SYM
+%token ESCAPE_SYM /* SQL-2003-R */
%token EVENTS_SYM
-%token EXECUTE_SYM
-%token EXISTS
+%token EVENT_SYM
+%token EVERY_SYM /* SQL-2003-N */
+%token EXECUTE_SYM /* SQL-2003-R */
+%token EXISTS /* SQL-2003-R */
%token EXIT_SYM
%token EXPANSION_SYM
-%token EXPORT_SET
%token EXTENDED_SYM
-%token EXTRACT_SYM
-%token FALSE_SYM
+%token EXTENT_SIZE_SYM
+%token EXTRACT_SYM /* SQL-2003-N */
+%token FALSE_SYM /* SQL-2003-R */
%token FAST_SYM
-%token FETCH_SYM
-%token FIELD_FUNC
+%token FAULTS_SYM
+%token FETCH_SYM /* SQL-2003-R */
%token FILE_SYM
-%token FIRST_SYM
+%token FIRST_SYM /* SQL-2003-N */
%token FIXED_SYM
%token FLOAT_NUM
-%token FLOAT_SYM
+%token FLOAT_SYM /* SQL-2003-R */
%token FLUSH_SYM
%token FORCE_SYM
-%token FOREIGN
-%token FORMAT_SYM
-%token FOR_SYM
-%token FOUND_SYM
+%token FOREIGN /* SQL-2003-R */
+%token FOR_SYM /* SQL-2003-R */
+%token FOUND_SYM /* SQL-2003-R */
%token FRAC_SECOND_SYM
%token FROM
-%token FROM_UNIXTIME
-%token FULL
+%token FULL /* SQL-2003-R */
%token FULLTEXT_SYM
-%token FUNCTION_SYM
-%token FUNC_ARG0
-%token FUNC_ARG1
-%token FUNC_ARG2
-%token FUNC_ARG3
+%token FUNCTION_SYM /* SQL-2003-R */
%token GE
-%token GEOMCOLLFROMTEXT
%token GEOMETRYCOLLECTION
%token GEOMETRY_SYM
-%token GEOMFROMTEXT
-%token GEOMFROMWKB
-%token GET_FORMAT
-%token GLOBAL_SYM
-%token GRANT
+%token GET_FORMAT /* MYSQL-FUNC */
+%token GLOBAL_SYM /* SQL-2003-R */
+%token GRANT /* SQL-2003-R */
%token GRANTS
-%token GREATEST_SYM
-%token GROUP
+%token GROUP_SYM /* SQL-2003-R */
%token GROUP_CONCAT_SYM
-%token GROUP_UNIQUE_USERS
-%token GT_SYM
+%token GT_SYM /* OPERATOR */
%token HANDLER_SYM
%token HASH_SYM
-%token HAVING
+%token HAVING /* SQL-2003-R */
%token HELP_SYM
%token HEX_NUM
%token HIGH_PRIORITY
+%token HOST_SYM
%token HOSTS_SYM
%token HOUR_MICROSECOND_SYM
%token HOUR_MINUTE_SYM
%token HOUR_SECOND_SYM
-%token HOUR_SYM
+%token HOUR_SYM /* SQL-2003-R */
%token IDENT
%token IDENTIFIED_SYM
%token IDENT_QUOTED
@@ -677,65 +739,66 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%token INDEXES
%token INDEX_SYM
%token INFILE
-%token INNER_SYM
+%token INITIAL_SIZE_SYM
+%token INNER_SYM /* SQL-2003-R */
%token INNOBASE_SYM
-%token INOUT_SYM
-%token INSENSITIVE_SYM
-%token INSERT
+%token INOUT_SYM /* SQL-2003-R */
+%token INSENSITIVE_SYM /* SQL-2003-R */
+%token INSERT /* SQL-2003-R */
%token INSERT_METHOD
-%token INTERVAL_SYM
-%token INTO
-%token INT_SYM
+%token INSTALL_SYM
+%token INTERVAL_SYM /* SQL-2003-R */
+%token INTO /* SQL-2003-R */
+%token INT_SYM /* SQL-2003-R */
%token INVOKER_SYM
-%token IN_SYM
-%token IS
-%token ISOLATION
+%token IN_SYM /* SQL-2003-R */
+%token IO_SYM
+%token IPC_SYM
+%token IS /* SQL-2003-R */
+%token ISOLATION /* SQL-2003-R */
%token ISSUER_SYM
%token ITERATE_SYM
-%token JOIN_SYM
+%token JOIN_SYM /* SQL-2003-R */
%token KEYS
-%token KEY_SYM
+%token KEY_BLOCK_SIZE
+%token KEY_SYM /* SQL-2003-N */
%token KILL_SYM
-%token LABEL_SYM
-%token LANGUAGE_SYM
-%token LAST_INSERT_ID
-%token LAST_SYM
-%token LE
-%token LEADING
-%token LEAST_SYM
+%token LANGUAGE_SYM /* SQL-2003-R */
+%token LAST_SYM /* SQL-2003-N */
+%token LE /* OPERATOR */
+%token LEADING /* SQL-2003-R */
%token LEAVES
%token LEAVE_SYM
-%token LEFT
+%token LEFT /* SQL-2003-R */
+%token LESS_SYM
%token LEVEL_SYM
%token LEX_HOSTNAME
-%token LIKE
+%token LIKE /* SQL-2003-R */
%token LIMIT
-%token LINEFROMTEXT
+%token LINEAR_SYM
%token LINES
%token LINESTRING
+%token LIST_SYM
%token LOAD
-%token LOCAL_SYM
-%token LOCATE
-%token LOCATOR_SYM
+%token LOCAL_SYM /* SQL-2003-R */
+%token LOCATOR_SYM /* SQL-2003-N */
%token LOCKS_SYM
%token LOCK_SYM
+%token LOGFILE_SYM
%token LOGS_SYM
-%token LOG_SYM
%token LONGBLOB
%token LONGTEXT
%token LONG_NUM
%token LONG_SYM
%token LOOP_SYM
%token LOW_PRIORITY
-%token LT
-%token MAKE_SET_SYM
+%token LT /* OPERATOR */
%token MASTER_CONNECT_RETRY_SYM
%token MASTER_HOST_SYM
%token MASTER_LOG_FILE_SYM
%token MASTER_LOG_POS_SYM
%token MASTER_PASSWORD_SYM
%token MASTER_PORT_SYM
-%token MASTER_POS_WAIT
%token MASTER_SERVER_ID_SYM
%token MASTER_SSL_CAPATH_SYM
%token MASTER_SSL_CA_SYM
@@ -743,121 +806,138 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%token MASTER_SSL_CIPHER_SYM
%token MASTER_SSL_KEY_SYM
%token MASTER_SSL_SYM
+%token MASTER_SSL_VERIFY_SERVER_CERT_SYM
%token MASTER_SYM
%token MASTER_USER_SYM
-%token MATCH
+%token MATCH /* SQL-2003-R */
%token MAX_CONNECTIONS_PER_HOUR
%token MAX_QUERIES_PER_HOUR
%token MAX_ROWS
-%token MAX_SYM
+%token MAX_SIZE_SYM
+%token MAX_SYM /* SQL-2003-N */
%token MAX_UPDATES_PER_HOUR
%token MAX_USER_CONNECTIONS_SYM
+%token MAX_VALUE_SYM /* SQL-2003-N */
%token MEDIUMBLOB
%token MEDIUMINT
%token MEDIUMTEXT
%token MEDIUM_SYM
-%token MERGE_SYM
-%token MICROSECOND_SYM
+%token MEMORY_SYM
+%token MERGE_SYM /* SQL-2003-R */
+%token MICROSECOND_SYM /* MYSQL-FUNC */
%token MIGRATE_SYM
%token MINUTE_MICROSECOND_SYM
%token MINUTE_SECOND_SYM
-%token MINUTE_SYM
+%token MINUTE_SYM /* SQL-2003-R */
%token MIN_ROWS
-%token MIN_SYM
-%token MLINEFROMTEXT
+%token MIN_SYM /* SQL-2003-N */
%token MODE_SYM
-%token MODIFIES_SYM
+%token MODIFIES_SYM /* SQL-2003-R */
%token MODIFY_SYM
-%token MOD_SYM
-%token MONTH_SYM
-%token MPOINTFROMTEXT
-%token MPOLYFROMTEXT
+%token MOD_SYM /* SQL-2003-N */
+%token MONTH_SYM /* SQL-2003-R */
%token MULTILINESTRING
%token MULTIPOINT
%token MULTIPOLYGON
%token MUTEX_SYM
-%token NAMES_SYM
-%token NAME_SYM
-%token NATIONAL_SYM
-%token NATURAL
+%token NAMES_SYM /* SQL-2003-N */
+%token NAME_SYM /* SQL-2003-N */
+%token NATIONAL_SYM /* SQL-2003-R */
+%token NATURAL /* SQL-2003-R */
%token NCHAR_STRING
-%token NCHAR_SYM
+%token NCHAR_SYM /* SQL-2003-R */
%token NDBCLUSTER_SYM
-%token NE
-%token NEW_SYM
-%token NEXT_SYM
-%token NONE_SYM
+%token NE /* OPERATOR */
+%token NEG
+%token NEW_SYM /* SQL-2003-R */
+%token NEXT_SYM /* SQL-2003-N */
+%token NODEGROUP_SYM
+%token NONE_SYM /* SQL-2003-R */
%token NOT2_SYM
-%token NOT_SYM
+%token NOT_SYM /* SQL-2003-R */
%token NOW_SYM
-%token NO_SYM
+%token NO_SYM /* SQL-2003-R */
+%token NO_WAIT_SYM
%token NO_WRITE_TO_BINLOG
-%token NULL_SYM
+%token NULL_SYM /* SQL-2003-R */
%token NUM
-%token NUMERIC_SYM
+%token NUMERIC_SYM /* SQL-2003-R */
%token NVARCHAR_SYM
%token OFFSET_SYM
-%token OJ_SYM
%token OLD_PASSWORD
-%token ON
+%token ON /* SQL-2003-R */
%token ONE_SHOT_SYM
%token ONE_SYM
-%token OPEN_SYM
+%token OPEN_SYM /* SQL-2003-R */
%token OPTIMIZE
-%token OPTION
+%token OPTIONS_SYM
+%token OPTION /* SQL-2003-N */
%token OPTIONALLY
%token OR2_SYM
-%token ORDER_SYM
-%token OR_OR_SYM
-%token OR_SYM
+%token ORDER_SYM /* SQL-2003-R */
+%token OR_OR_SYM /* OPERATOR */
+%token OR_SYM /* SQL-2003-R */
%token OUTER
%token OUTFILE
-%token OUT_SYM
+%token OUT_SYM /* SQL-2003-R */
+%token OWNER_SYM
%token PACK_KEYS_SYM
-%token PARTIAL
-%token PASSWORD
+%token PAGE_SYM
+%token PAGE_CHECKSUM_SYM
%token PARAM_MARKER
+%token PARSER_SYM
+%token PARTIAL /* SQL-2003-N */
+%token PARTITIONING_SYM
+%token PARTITIONS_SYM
+%token PARTITION_SYM /* SQL-2003-R */
+%token PASSWORD
%token PHASE_SYM
-%token POINTFROMTEXT
+%token PLUGINS_SYM
+%token PLUGIN_SYM
%token POINT_SYM
-%token POLYFROMTEXT
%token POLYGON
-%token POSITION_SYM
-%token PRECISION
-%token PREPARE_SYM
+%token PORT_SYM
+%token POSITION_SYM /* SQL-2003-N */
+%token PRECISION /* SQL-2003-R */
+%token PREPARE_SYM /* SQL-2003-R */
+%token PRESERVE_SYM
%token PREV_SYM
-%token PRIMARY_SYM
-%token PRIVILEGES
-%token PROCEDURE
+%token PRIMARY_SYM /* SQL-2003-R */
+%token PRIVILEGES /* SQL-2003-N */
+%token PROCEDURE /* SQL-2003-R */
%token PROCESS
%token PROCESSLIST_SYM
+%token PROFILE_SYM
+%token PROFILES_SYM
%token PURGE
%token QUARTER_SYM
%token QUERY_SYM
%token QUICK
-%token RAID_0_SYM
-%token RAID_CHUNKS
-%token RAID_CHUNKSIZE
-%token RAID_STRIPED_SYM
-%token RAID_TYPE
-%token RAND
-%token READS_SYM
-%token READ_SYM
-%token REAL
+%token RANGE_SYM /* SQL-2003-R */
+%token READS_SYM /* SQL-2003-R */
+%token READ_ONLY_SYM
+%token READ_SYM /* SQL-2003-N */
+%token READ_WRITE_SYM
+%token REAL /* SQL-2003-R */
+%token REBUILD_SYM
%token RECOVER_SYM
+%token REDOFILE_SYM
+%token REDO_BUFFER_SIZE_SYM
%token REDUNDANT_SYM
-%token REFERENCES
+%token REFERENCES /* SQL-2003-R */
%token REGEXP
%token RELAY_LOG_FILE_SYM
%token RELAY_LOG_POS_SYM
%token RELAY_THREAD
-%token RELEASE_SYM
+%token RELEASE_SYM /* SQL-2003-R */
%token RELOAD
+%token REMOVE_SYM
%token RENAME
+%token REORGANIZE_SYM
%token REPAIR
-%token REPEATABLE_SYM
-%token REPEAT_SYM
-%token REPLACE
+%token REPEATABLE_SYM /* SQL-2003-N */
+%token REPEAT_SYM /* MYSQL-FUNC */
+%token REPLACE /* MYSQL-FUNC */
%token REPLICATION
%token REQUIRE_SYM
%token RESET_SYM
@@ -865,246 +945,272 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%token RESTORE_SYM
%token RESTRICT
%token RESUME_SYM
-%token RETURNS_SYM
-%token RETURN_SYM
-%token REVOKE
-%token RIGHT
-%token ROLLBACK_SYM
-%token ROLLUP_SYM
-%token ROUND
-%token ROUTINE_SYM
-%token ROWS_SYM
-%token ROW_COUNT_SYM
+%token RETURNS_SYM /* SQL-2003-R */
+%token RETURN_SYM /* SQL-2003-R */
+%token REVOKE /* SQL-2003-R */
+%token RIGHT /* SQL-2003-R */
+%token ROLLBACK_SYM /* SQL-2003-R */
+%token ROLLUP_SYM /* SQL-2003-R */
+%token ROUTINE_SYM /* SQL-2003-N */
+%token ROWS_SYM /* SQL-2003-R */
%token ROW_FORMAT_SYM
-%token ROW_SYM
+%token ROW_SYM /* SQL-2003-R */
%token RTREE_SYM
-%token SAVEPOINT_SYM
+%token SAVEPOINT_SYM /* SQL-2003-R */
+%token SCHEDULE_SYM
%token SECOND_MICROSECOND_SYM
-%token SECOND_SYM
-%token SECURITY_SYM
-%token SELECT_SYM
-%token SENSITIVE_SYM
+%token SECOND_SYM /* SQL-2003-R */
+%token SECURITY_SYM /* SQL-2003-N */
+%token SELECT_SYM /* SQL-2003-R */
+%token SENSITIVE_SYM /* FUTURE-USE */
%token SEPARATOR_SYM
-%token SERIALIZABLE_SYM
+%token SERIALIZABLE_SYM /* SQL-2003-N */
%token SERIAL_SYM
-%token SESSION_SYM
-%token SET
+%token SESSION_SYM /* SQL-2003-N */
+%token SERVER_SYM
+%token SERVER_OPTIONS
+%token SET /* SQL-2003-R */
%token SET_VAR
%token SHARE_SYM
-%token SHIFT_LEFT
-%token SHIFT_RIGHT
+%token SHIFT_LEFT /* OPERATOR */
+%token SHIFT_RIGHT /* OPERATOR */
%token SHOW
%token SHUTDOWN
%token SIGNED_SYM
-%token SIMPLE_SYM
+%token SIMPLE_SYM /* SQL-2003-N */
%token SLAVE
-%token SMALLINT
+%token SMALLINT /* SQL-2003-R */
%token SNAPSHOT_SYM
+%token SOCKET_SYM
+%token SONAME_SYM
%token SOUNDS_SYM
+%token SOURCE_SYM
%token SPATIAL_SYM
-%token SPECIFIC_SYM
-%token SQLEXCEPTION_SYM
-%token SQLSTATE_SYM
-%token SQLWARNING_SYM
+%token SPECIFIC_SYM /* SQL-2003-R */
+%token SQLEXCEPTION_SYM /* SQL-2003-R */
+%token SQLSTATE_SYM /* SQL-2003-R */
+%token SQLWARNING_SYM /* SQL-2003-R */
%token SQL_BIG_RESULT
%token SQL_BUFFER_RESULT
%token SQL_CACHE_SYM
%token SQL_CALC_FOUND_ROWS
%token SQL_NO_CACHE_SYM
%token SQL_SMALL_RESULT
-%token SQL_SYM
+%token SQL_SYM /* SQL-2003-R */
%token SQL_THREAD
%token SSL_SYM
%token STARTING
-%token START_SYM
+%token STARTS_SYM
+%token START_SYM /* SQL-2003-R */
%token STATUS_SYM
+%token STDDEV_SAMP_SYM /* SQL-2003-N */
%token STD_SYM
-%token STDDEV_SAMP_SYM
%token STOP_SYM
%token STORAGE_SYM
%token STRAIGHT_JOIN
%token STRING_SYM
%token SUBDATE_SYM
%token SUBJECT_SYM
-%token SUBSTRING
-%token SUBSTRING_INDEX
-%token SUM_SYM
+%token SUBPARTITIONS_SYM
+%token SUBPARTITION_SYM
+%token SUBSTRING /* SQL-2003-N */
+%token SUM_SYM /* SQL-2003-N */
%token SUPER_SYM
%token SUSPEND_SYM
+%token SWAPS_SYM
+%token SWITCHES_SYM
%token SYSDATE
%token TABLES
%token TABLESPACE
-%token TABLE_SYM
-%token TEMPORARY
+%token TABLE_REF_PRIORITY
+%token TABLE_SYM /* SQL-2003-R */
+%token TABLE_CHECKSUM_SYM
+%token TEMPORARY /* SQL-2003-N */
%token TEMPTABLE_SYM
%token TERMINATED
%token TEXT_STRING
%token TEXT_SYM
-%token TIMESTAMP
+%token THAN_SYM
+%token THEN_SYM /* SQL-2003-R */
+%token TIMESTAMP /* SQL-2003-R */
%token TIMESTAMP_ADD
%token TIMESTAMP_DIFF
-%token TIME_SYM
+%token TIME_SYM /* SQL-2003-R */
%token TINYBLOB
%token TINYINT
%token TINYTEXT
-%token TO_SYM
-%token TRAILING
+%token TO_SYM /* SQL-2003-R */
+%token TRAILING /* SQL-2003-R */
%token TRANSACTION_SYM
-%token TRIGGER_SYM
+%token TRANSACTIONAL_SYM
%token TRIGGERS_SYM
-%token TRIM
-%token TRUE_SYM
+%token TRIGGER_SYM /* SQL-2003-R */
+%token TRIM /* SQL-2003-N */
+%token TRUE_SYM /* SQL-2003-R */
%token TRUNCATE_SYM
%token TYPES_SYM
-%token TYPE_SYM
+%token TYPE_SYM /* SQL-2003-N */
%token UDF_RETURNS_SYM
-%token UDF_SONAME_SYM
%token ULONGLONG_NUM
-%token UNCOMMITTED_SYM
+%token UNCOMMITTED_SYM /* SQL-2003-N */
%token UNDEFINED_SYM
%token UNDERSCORE_CHARSET
-%token UNDO_SYM
+%token UNDOFILE_SYM
+%token UNDO_BUFFER_SIZE_SYM
+%token UNDO_SYM /* FUTURE-USE */
%token UNICODE_SYM
-%token UNION_SYM
+%token UNINSTALL_SYM
+%token UNION_SYM /* SQL-2003-R */
%token UNIQUE_SYM
-%token UNIQUE_USERS
-%token UNIX_TIMESTAMP
-%token UNKNOWN_SYM
+%token UNKNOWN_SYM /* SQL-2003-R */
%token UNLOCK_SYM
%token UNSIGNED
%token UNTIL_SYM
-%token UPDATE_SYM
+%token UPDATE_SYM /* SQL-2003-R */
%token UPGRADE_SYM
-%token USAGE
-%token USER
+%token USAGE /* SQL-2003-N */
+%token USER /* SQL-2003-R */
%token USE_FRM
%token USE_SYM
-%token USING
+%token USING /* SQL-2003-R */
%token UTC_DATE_SYM
%token UTC_TIMESTAMP_SYM
%token UTC_TIME_SYM
-%token VAR_SAMP_SYM
-%token VALUES
-%token VALUE_SYM
+%token VALUES /* SQL-2003-R */
+%token VALUE_SYM /* SQL-2003-R */
%token VARBINARY
-%token VARCHAR
+%token VARCHAR /* SQL-2003-R */
%token VARIABLES
%token VARIANCE_SYM
-%token VARYING
-%token VIEW_SYM
+%token VARYING /* SQL-2003-R */
+%token VAR_SAMP_SYM
+%token VIEW_SYM /* SQL-2003-N */
+%token WAIT_SYM
%token WARNINGS
%token WEEK_SYM
-%token WHEN_SYM
-%token WHERE
+%token WHEN_SYM /* SQL-2003-R */
+%token WHERE /* SQL-2003-R */
%token WHILE_SYM
-%token WITH
-%token WORK_SYM
-%token WRITE_SYM
+%token WITH /* SQL-2003-R */
+%token WORK_SYM /* SQL-2003-N */
+%token WRAPPER_SYM
+%token WRITE_SYM /* SQL-2003-N */
%token X509_SYM
%token XA_SYM
%token XOR
-%token YEARWEEK
%token YEAR_MONTH_SYM
-%token YEAR_SYM
+%token YEAR_SYM /* SQL-2003-R */
%token ZEROFILL
%left JOIN_SYM INNER_SYM STRAIGHT_JOIN CROSS LEFT RIGHT
/* A dummy token to force the priority of table_ref production in a join. */
%left TABLE_REF_PRIORITY
%left SET_VAR
-%left OR_OR_SYM OR_SYM OR2_SYM
-%left XOR
-%left AND_SYM AND_AND_SYM
-%left BETWEEN_SYM CASE_SYM WHEN_SYM THEN_SYM ELSE
-%left EQ EQUAL_SYM GE GT_SYM LE LT NE IS LIKE REGEXP IN_SYM
-%left '|'
-%left '&'
-%left SHIFT_LEFT SHIFT_RIGHT
-%left '-' '+'
-%left '*' '/' '%' DIV_SYM MOD_SYM
+%left OR_OR_SYM OR_SYM OR2_SYM
+%left XOR
+%left AND_SYM AND_AND_SYM
+%left BETWEEN_SYM CASE_SYM WHEN_SYM THEN_SYM ELSE
+%left EQ EQUAL_SYM GE GT_SYM LE LT NE IS LIKE REGEXP IN_SYM
+%left '|'
+%left '&'
+%left SHIFT_LEFT SHIFT_RIGHT
+%left '-' '+'
+%left '*' '/' '%' DIV_SYM MOD_SYM
%left '^'
-%left NEG '~'
-%right NOT_SYM NOT2_SYM
-%right BINARY COLLATE_SYM
-%left INTERVAL_SYM
+%left NEG '~'
+%right NOT_SYM NOT2_SYM
+%right BINARY COLLATE_SYM
+%left INTERVAL_SYM
%type <lex_str>
IDENT IDENT_QUOTED TEXT_STRING DECIMAL_NUM FLOAT_NUM NUM LONG_NUM HEX_NUM
- LEX_HOSTNAME ULONGLONG_NUM field_ident select_alias ident ident_or_text
- UNDERSCORE_CHARSET 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
+ 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
%type <lex_str_ptr>
- opt_table_alias
+ opt_table_alias
%type <table>
- table_ident table_ident_nodb references xid
+ table_ident table_ident_nodb references xid
%type <simple_string>
- remember_name remember_end opt_ident opt_db text_or_password
- opt_constraint constraint ident_or_empty
+ remember_name remember_end opt_ident opt_db text_or_password
+ opt_constraint constraint
%type <string>
- text_string opt_gconcat_separator
+ text_string opt_gconcat_separator
%type <num>
- type int_type real_type order_dir lock_option
- udf_type if_exists opt_local opt_table_options table_options
+ type 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_ignore_leaves fulltext_options spatial_type union_option
start_transaction_opts opt_chain opt_release
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
+ ev_alter_on_schedule_completion opt_ev_rename_to opt_ev_sql_stmt
%type <ulong_num>
- ulong_num raid_types merge_insert_types
+ ulong_num real_ulong_num merge_insert_types
%type <ulonglong_number>
- ulonglong_num
+ ulonglong_num real_ulonglong_num size_number
+
+%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
+ 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
- variable variable_aux
- bool_pri
- predicate bit_expr
- table_wild simple_expr udf_expr
- expr_or_default set_expr_or_default interval_expr
- param_marker geometry_function
- signed_literal now_or_signed_literal opt_escape
- sp_opt_default
- simple_ident_nospvar simple_ident_q
+ literal text_literal insert_ident order_ident
+ simple_ident select_item2 expr opt_expr opt_else sum_expr in_sum_expr
+ variable variable_aux bool_pri
+ predicate bit_expr
+ table_wild simple_expr udf_expr
+ expr_or_default set_expr_or_default
+ param_marker geometry_function
+ signed_literal now_or_signed_literal opt_escape
+ sp_opt_default
+ simple_ident_nospvar simple_ident_q
field_or_var limit_option
+ part_func_expr
+ function_call_keyword
+ function_call_nonkeyword
+ function_call_generic
+ function_call_conflict
%type <item_num>
- NUM_literal
+ NUM_literal
%type <item_list>
- expr_list udf_expr_list udf_expr_list2 when_list
- ident_list ident_list_arg opt_expr_list
+ expr_list opt_udf_expr_list udf_expr_list when_list
+ ident_list ident_list_arg opt_expr_list
%type <var_type>
option_type opt_var_type opt_var_ident_type
%type <key_type>
- key_type opt_unique_or_fulltext constraint_key_type
+ key_type opt_unique_or_fulltext constraint_key_type
%type <key_alg>
- key_alg opt_btree_or_rtree
+ btree_or_rtree
%type <string_list>
- key_usage_list using_list
+ using_list
%type <key_part>
- key_part
+ key_part
%type <table_list>
- join_table_list join_table
- table_factor table_ref
+ join_table_list join_table
+ table_factor table_ref esc_table_ref
select_derived derived_table_list
%type <date_time_type> date_time_type;
@@ -1114,7 +1220,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%type <interval_time_st> interval_time_stamp
-%type <db_type> storage_engines
+%type <db_type> storage_engines known_storage_engines
%type <row_type> row_types
@@ -1124,84 +1230,121 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%type <cast_type> cast_type
-%type <symbol> FUNC_ARG0 FUNC_ARG1 FUNC_ARG2 FUNC_ARG3 keyword keyword_sp
+%type <symbol> keyword keyword_sp
%type <lex_user> user grant_user
%type <charset>
- opt_collate
- charset_name
- charset_name_or_default
- old_or_new_charset_name
- old_or_new_charset_name_or_default
- collation_name
- collation_name_or_default
- opt_load_data_charset
+ opt_collate
+ charset_name
+ charset_name_or_default
+ old_or_new_charset_name
+ old_or_new_charset_name_or_default
+ collation_name
+ collation_name_or_default
+ opt_load_data_charset
+ UNDERSCORE_CHARSET
%type <variable> internal_variable_name
%type <select_lex> subselect take_first_select
- get_select_lex
+ get_select_lex
%type <boolfunc2creator> comp_op
%type <NONE>
- query verb_clause create change select do drop insert replace insert2
- insert_values update delete truncate rename
- 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
- field_list field_list_item field_spec kill column_def key_def
- keycache_list assign_to_keycache preload_list preload_keys
- 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
- expr_list2 udf_expr_list3 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
- opt_delete_options opt_delete_option varchar nchar nvarchar
- opt_outer table_list table_name opt_option opt_place
- opt_attribute opt_attribute_list attribute column_list column_list_id
- opt_column_list grant_privileges grant_ident grant_list grant_option
- object_privilege object_privilege_list user_list rename_list
- clear_privileges flush_options flush_option
- equal optional_braces opt_key_definition key_usage_list2
- opt_mi_check_type opt_to mi_check_types normal_join
- table_to_table_list table_to_table opt_table_list opt_as
- handler_rkey_function handler_read_or_scan
- single_multi table_wild_list table_wild_one opt_wild
- union_clause union_list
- precision subselect_start opt_and charset
- subselect_end select_var_list select_var_list_init help opt_field_length field_length
- opt_extended_describe
+ query verb_clause create change select do drop insert replace insert2
+ insert_values update delete truncate rename
+ 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
+ field_list field_list_item field_spec kill column_def key_def
+ keycache_list assign_to_keycache preload_list preload_keys
+ 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
+ opt_delete_options opt_delete_option varchar nchar nvarchar
+ opt_outer table_list table_name table_alias_ref_list table_alias_ref
+ opt_option opt_place
+ opt_attribute opt_attribute_list attribute column_list column_list_id
+ opt_column_list grant_privileges grant_ident grant_list grant_option
+ object_privilege object_privilege_list user_list rename_list
+ clear_privileges flush_options flush_option
+ 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
+ handler_rkey_function handler_read_or_scan
+ single_multi table_wild_list table_wild_one opt_wild
+ union_clause union_list
+ precision subselect_start opt_and charset
+ subselect_end select_var_list select_var_list_init help
+ field_length opt_field_length
+ opt_extended_describe
prepare prepare_src execute deallocate
- statement sp_suid
- sp_c_chistics sp_a_chistics sp_chistic sp_c_chistic xa
+ 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
- view_replace_or_algorithm view_replace view_algorithm_opt
- view_algorithm view_or_trigger_or_sp definer_tail
- view_suid view_tail view_list_opt view_list view_select
- view_check_option trigger_tail sp_tail sf_tail udf_tail
- case_stmt_specification simple_case_stmt searched_case_stmt
+ view_replace_or_algorithm view_replace
+ view_algorithm view_or_trigger_or_sp_or_event
+ definer_tail no_definer_tail
+ view_suid view_tail view_list_opt view_list view_select
+ view_check_option trigger_tail sp_tail sf_tail udf_tail event_tail
+ install uninstall partition_entry binlog_base64_event
+ init_key_options key_options key_opts key_opt key_using_alg
+ server_def server_options_list server_option
definer_opt no_definer definer
END_OF_INPUT
%type <NONE> call sp_proc_stmts sp_proc_stmts1 sp_proc_stmt
+%type <NONE> sp_proc_stmt_statement sp_proc_stmt_return
+%type <NONE> sp_proc_stmt_if
+%type <NONE> sp_labeled_control sp_proc_stmt_unlabeled
+%type <NONE> sp_labeled_block sp_unlabeled_block
+%type <NONE> sp_proc_stmt_leave
+%type <NONE> sp_proc_stmt_iterate
+%type <NONE> sp_proc_stmt_open sp_proc_stmt_fetch sp_proc_stmt_close
+%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 <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 <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
+ '-' '+' '*' '/' '%' '(' ')'
+ ',' '!' '{' '}' '&' '|' AND_SYM OR_SYM OR_OR_SYM BETWEEN_SYM CASE_SYM
+ THEN_SYM WHEN_SYM DIV_SYM MOD_SYM OR2_SYM AND_AND_SYM
%%
+/*
+ Indentation of grammar rules:
+
+rule: <-- starts at col 1
+ rule1a rule1b rule1c <-- starts at col 11
+ { <-- starts at col 11
+ code <-- starts at col 13, indentation is 2 spaces
+ }
+ | rule2a rule2b
+ {
+ code
+ }
+ ; <-- on a line by itself, starts at col 9
+
+ Also, please do not use any <TAB>, but spaces.
+ Having a uniform indentation in this file helps
+ code reviews, patches, merges, and make maintenance easier.
+ Tip: grep [[:cntrl:]] sql_yacc.yy
+ Thanks.
+*/
query:
END_OF_INPUT
@@ -1222,7 +1365,7 @@ query:
if ((YYTHD->client_capabilities & CLIENT_MULTI_QUERIES) &&
! lip->stmt_prepare_mode &&
- ! (lip->ptr >= lip->end_of_query))
+ ! lip->eof())
{
/*
We found a well formed query, and multi queries are allowed:
@@ -1231,7 +1374,7 @@ query:
of the parser.
*/
lip->next_state= MY_LEX_END;
- lip->found_semicolon= lip->ptr;
+ lip->found_semicolon= lip->get_ptr();
}
else
{
@@ -1254,440 +1397,718 @@ opt_end_of_input:
;
verb_clause:
- statement
- | begin
- ;
+ statement
+ | begin
+ ;
/* Verb clauses, except begin */
statement:
- alter
- | analyze
- | backup
- | call
- | change
- | check
- | checksum
- | commit
- | create
+ alter
+ | analyze
+ | backup
+ | binlog_base64_event
+ | call
+ | change
+ | check
+ | checksum
+ | commit
+ | create
| deallocate
- | delete
- | describe
- | do
- | drop
+ | delete
+ | describe
+ | do
+ | drop
| execute
- | flush
- | grant
- | handler
- | help
- | insert
- | kill
- | load
- | lock
- | optimize
+ | flush
+ | grant
+ | handler
+ | help
+ | insert
+ | install
+ | kill
+ | load
+ | lock
+ | optimize
| keycache
- | preload
+ | partition_entry
+ | preload
| prepare
- | purge
- | release
- | rename
- | repair
- | replace
- | reset
- | restore
- | revoke
- | rollback
- | savepoint
- | select
- | set
- | show
- | slave
- | start
- | truncate
- | unlock
- | update
- | use
- | xa
+ | purge
+ | release
+ | rename
+ | repair
+ | replace
+ | reset
+ | restore
+ | revoke
+ | rollback
+ | savepoint
+ | select
+ | set
+ | show
+ | slave
+ | start
+ | truncate
+ | uninstall
+ | unlock
+ | update
+ | use
+ | xa
;
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;
- };
+ deallocate_or_drop PREPARE_SYM ident
+ {
+ THD *thd= YYTHD;
+ LEX *lex= thd->lex;
+ lex->sql_command= SQLCOM_DEALLOCATE_PREPARE;
+ lex->prepared_stmt_name= $3;
+ }
+ ;
deallocate_or_drop:
- DEALLOCATE_SYM |
- DROP
- ;
-
+ DEALLOCATE_SYM
+ | 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;
- };
+ PREPARE_SYM ident FROM prepare_src
+ {
+ THD *thd= YYTHD;
+ LEX *lex= thd->lex;
+ lex->sql_command= SQLCOM_PREPARE;
+ lex->prepared_stmt_name= $2;
+ }
+ ;
prepare_src:
- TEXT_STRING_sys
- {
- THD *thd=YYTHD;
- LEX *lex= thd->lex;
- lex->prepared_stmt_code= $1;
- lex->prepared_stmt_code_is_varref= FALSE;
- }
+ 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;
- };
+ {
+ THD *thd= YYTHD;
+ LEX *lex= thd->lex;
+ lex->prepared_stmt_code= $2;
+ lex->prepared_stmt_code_is_varref= TRUE;
+ }
+ ;
execute:
- EXECUTE_SYM ident
- {
- THD *thd=YYTHD;
- LEX *lex= thd->lex;
- lex->sql_command= SQLCOM_EXECUTE;
- lex->prepared_stmt_name= $2;
- }
- execute_using
- {}
+ EXECUTE_SYM ident
+ {
+ THD *thd= YYTHD;
+ LEX *lex= thd->lex;
+ lex->sql_command= SQLCOM_EXECUTE;
+ lex->prepared_stmt_name= $2;
+ }
+ execute_using
+ {}
;
execute_using:
- /* nothing */
+ /* nothing */
| USING execute_var_list
;
execute_var_list:
- execute_var_list ',' execute_var_ident
+ execute_var_list ',' execute_var_ident
| execute_var_ident
;
-execute_var_ident: '@' ident_or_text
- {
- LEX *lex=Lex;
- LEX_STRING *lexstr= (LEX_STRING*)sql_memdup(&$2, sizeof(LEX_STRING));
- if (!lexstr || lex->prepared_stmt_params.push_back(lexstr))
+execute_var_ident:
+ '@' ident_or_text
+ {
+ LEX *lex=Lex;
+ LEX_STRING *lexstr= (LEX_STRING*)sql_memdup(&$2, sizeof(LEX_STRING));
+ if (!lexstr || lex->prepared_stmt_params.push_back(lexstr))
MYSQL_YYABORT;
- }
+ }
;
/* help */
help:
- HELP_SYM
- {
- if (Lex->sphead)
- {
- my_error(ER_SP_BADSTATEMENT, MYF(0), "HELP");
- MYSQL_YYABORT;
- }
- }
- ident_or_text
- {
- LEX *lex= Lex;
- lex->sql_command= SQLCOM_HELP;
- lex->help_arg= $3.str;
- };
+ HELP_SYM
+ {
+ if (Lex->sphead)
+ {
+ my_error(ER_SP_BADSTATEMENT, MYF(0), "HELP");
+ MYSQL_YYABORT;
+ }
+ }
+ ident_or_text
+ {
+ LEX *lex= Lex;
+ lex->sql_command= SQLCOM_HELP;
+ lex->help_arg= $3.str;
+ }
+ ;
/* change master */
change:
- CHANGE MASTER_SYM TO_SYM
- {
- LEX *lex = Lex;
- lex->sql_command = SQLCOM_CHANGE_MASTER;
- bzero((char*) &lex->mi, sizeof(lex->mi));
- }
- master_defs
- {}
- ;
+ CHANGE MASTER_SYM TO_SYM
+ {
+ LEX *lex = Lex;
+ lex->sql_command = SQLCOM_CHANGE_MASTER;
+ bzero((char*) &lex->mi, sizeof(lex->mi));
+ }
+ master_defs
+ {}
+ ;
master_defs:
- master_def
- | master_defs ',' master_def;
+ master_def
+ | master_defs ',' master_def
+ ;
master_def:
- MASTER_HOST_SYM EQ TEXT_STRING_sys
- {
- Lex->mi.host = $3.str;
- }
- |
- MASTER_USER_SYM EQ TEXT_STRING_sys
- {
- Lex->mi.user = $3.str;
- }
- |
- MASTER_PASSWORD_SYM EQ TEXT_STRING_sys
- {
- Lex->mi.password = $3.str;
- }
- |
- MASTER_PORT_SYM EQ ulong_num
- {
- Lex->mi.port = $3;
- }
- |
- MASTER_CONNECT_RETRY_SYM EQ ulong_num
- {
- Lex->mi.connect_retry = $3;
- }
- | MASTER_SSL_SYM EQ ulong_num
- {
- Lex->mi.ssl= $3 ?
- LEX_MASTER_INFO::SSL_ENABLE : LEX_MASTER_INFO::SSL_DISABLE;
- }
- | MASTER_SSL_CA_SYM EQ TEXT_STRING_sys
- {
- Lex->mi.ssl_ca= $3.str;
- }
- | MASTER_SSL_CAPATH_SYM EQ TEXT_STRING_sys
- {
- Lex->mi.ssl_capath= $3.str;
- }
- | MASTER_SSL_CERT_SYM EQ TEXT_STRING_sys
- {
- Lex->mi.ssl_cert= $3.str;
- }
- | MASTER_SSL_CIPHER_SYM EQ TEXT_STRING_sys
- {
- Lex->mi.ssl_cipher= $3.str;
- }
- | MASTER_SSL_KEY_SYM EQ TEXT_STRING_sys
- {
- Lex->mi.ssl_key= $3.str;
- }
- |
- master_file_def
- ;
+ MASTER_HOST_SYM EQ TEXT_STRING_sys
+ {
+ Lex->mi.host = $3.str;
+ }
+ | MASTER_USER_SYM EQ TEXT_STRING_sys
+ {
+ Lex->mi.user = $3.str;
+ }
+ | MASTER_PASSWORD_SYM EQ TEXT_STRING_sys
+ {
+ Lex->mi.password = $3.str;
+ }
+ | MASTER_PORT_SYM EQ ulong_num
+ {
+ Lex->mi.port = $3;
+ }
+ | MASTER_CONNECT_RETRY_SYM EQ ulong_num
+ {
+ Lex->mi.connect_retry = $3;
+ }
+ | MASTER_SSL_SYM EQ ulong_num
+ {
+ Lex->mi.ssl= $3 ?
+ LEX_MASTER_INFO::SSL_ENABLE : LEX_MASTER_INFO::SSL_DISABLE;
+ }
+ | MASTER_SSL_CA_SYM EQ TEXT_STRING_sys
+ {
+ Lex->mi.ssl_ca= $3.str;
+ }
+ | MASTER_SSL_CAPATH_SYM EQ TEXT_STRING_sys
+ {
+ Lex->mi.ssl_capath= $3.str;
+ }
+ | MASTER_SSL_CERT_SYM EQ TEXT_STRING_sys
+ {
+ Lex->mi.ssl_cert= $3.str;
+ }
+ | MASTER_SSL_CIPHER_SYM EQ TEXT_STRING_sys
+ {
+ Lex->mi.ssl_cipher= $3.str;
+ }
+ | MASTER_SSL_KEY_SYM EQ TEXT_STRING_sys
+ {
+ Lex->mi.ssl_key= $3.str;
+ }
+ | 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;
+ }
+ | master_file_def
+ ;
master_file_def:
- MASTER_LOG_FILE_SYM EQ TEXT_STRING_sys
- {
- Lex->mi.log_file_name = $3.str;
- }
- | MASTER_LOG_POS_SYM EQ ulonglong_num
- {
- Lex->mi.pos = $3;
- /*
- If the user specified a value < BIN_LOG_HEADER_SIZE, adjust it
- instead of causing subsequent errors.
- We need to do it in this file, because only there we know that
- MASTER_LOG_POS has been explicitely specified. On the contrary
- in change_master() (sql_repl.cc) we cannot distinguish between 0
- (MASTER_LOG_POS explicitely specified as 0) and 0 (unspecified),
- whereas we want to distinguish (specified 0 means "read the binlog
- from 0" (4 in fact), unspecified means "don't change the position
- (keep the preceding value)").
- */
- Lex->mi.pos = max(BIN_LOG_HEADER_SIZE, Lex->mi.pos);
- }
- | RELAY_LOG_FILE_SYM EQ TEXT_STRING_sys
- {
- Lex->mi.relay_log_name = $3.str;
- }
- | RELAY_LOG_POS_SYM EQ ulong_num
- {
- Lex->mi.relay_log_pos = $3;
- /* Adjust if < BIN_LOG_HEADER_SIZE (same comment as Lex->mi.pos) */
- Lex->mi.relay_log_pos = max(BIN_LOG_HEADER_SIZE, Lex->mi.relay_log_pos);
- }
- ;
+ MASTER_LOG_FILE_SYM EQ TEXT_STRING_sys
+ {
+ Lex->mi.log_file_name = $3.str;
+ }
+ | MASTER_LOG_POS_SYM EQ ulonglong_num
+ {
+ Lex->mi.pos = $3;
+ /*
+ If the user specified a value < BIN_LOG_HEADER_SIZE, adjust it
+ instead of causing subsequent errors.
+ We need to do it in this file, because only there we know that
+ MASTER_LOG_POS has been explicitely specified. On the contrary
+ in change_master() (sql_repl.cc) we cannot distinguish between 0
+ (MASTER_LOG_POS explicitely specified as 0) and 0 (unspecified),
+ whereas we want to distinguish (specified 0 means "read the binlog
+ from 0" (4 in fact), unspecified means "don't change the position
+ (keep the preceding value)").
+ */
+ Lex->mi.pos = max(BIN_LOG_HEADER_SIZE, Lex->mi.pos);
+ }
+ | RELAY_LOG_FILE_SYM EQ TEXT_STRING_sys
+ {
+ Lex->mi.relay_log_name = $3.str;
+ }
+ | RELAY_LOG_POS_SYM EQ ulong_num
+ {
+ Lex->mi.relay_log_pos = $3;
+ /* Adjust if < BIN_LOG_HEADER_SIZE (same comment as Lex->mi.pos) */
+ Lex->mi.relay_log_pos = max(BIN_LOG_HEADER_SIZE, Lex->mi.relay_log_pos);
+ }
+ ;
/* create a table */
create:
- CREATE opt_table_options TABLE_SYM opt_if_not_exists table_ident
- {
- THD *thd= YYTHD;
- LEX *lex=Lex;
- lex->sql_command= SQLCOM_CREATE_TABLE;
- if (!lex->select_lex.add_table_to_list(thd, $5, NULL,
- TL_OPTION_UPDATING,
- TL_WRITE))
- MYSQL_YYABORT;
- lex->alter_info.reset();
- lex->col_list.empty();
- lex->change=NullS;
- bzero((char*) &lex->create_info,sizeof(lex->create_info));
- lex->create_info.options=$2 | $4;
- lex->create_info.db_type= (enum db_type) lex->thd->variables.table_type;
- lex->create_info.default_table_charset= NULL;
- }
- create2
- { Lex->current_select= &Lex->select_lex; }
- | CREATE opt_unique_or_fulltext INDEX_SYM ident key_alg ON table_ident
- {
- LEX *lex=Lex;
- lex->sql_command= SQLCOM_CREATE_INDEX;
- if (!lex->current_select->add_table_to_list(lex->thd, $7, NULL,
- TL_OPTION_UPDATING))
- MYSQL_YYABORT;
+ 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))
+ MYSQL_YYABORT;
+ lex->alter_info.reset();
+ lex->col_list.empty();
+ lex->change=NullS;
+ bzero((char*) &lex->create_info,sizeof(lex->create_info));
+ lex->create_info.options=$2 | $4;
+ lex->create_info.db_type= ha_default_handlerton(thd);
+ lex->create_info.default_table_charset= NULL;
+ lex->name.str= 0;
+ lex->name.length= 0;
+ }
+ create2
+ {
+ LEX *lex= YYTHD->lex;
+ lex->current_select= &lex->select_lex;
+ if (!lex->create_info.db_type)
+ {
+ lex->create_info.db_type= ha_default_handlerton(YYTHD);
+ push_warning_printf(YYTHD, MYSQL_ERROR::WARN_LEVEL_WARN,
+ ER_WARN_USING_OTHER_HANDLER,
+ ER(ER_WARN_USING_OTHER_HANDLER),
+ ha_resolve_storage_engine_name(lex->create_info.db_type),
+ $5->table.str);
+ }
+ }
+ | CREATE opt_unique_or_fulltext INDEX_SYM ident key_alg ON
+ table_ident
+ {
+ LEX *lex=Lex;
+ lex->sql_command= SQLCOM_CREATE_INDEX;
+ if (!lex->current_select->add_table_to_list(lex->thd, $7,
+ NULL,
+ TL_OPTION_UPDATING))
+ MYSQL_YYABORT;
lex->alter_info.reset();
lex->alter_info.flags= ALTER_ADD_INDEX;
- lex->col_list.empty();
- lex->change=NullS;
- }
- '(' key_list ')'
- {
- LEX *lex=Lex;
- Key *key= new Key($2, $4.str, $5, 0, lex->col_list);
+ lex->col_list.empty();
+ lex->change=NullS;
+ }
+ '(' key_list ')' key_options
+ {
+ LEX *lex=Lex;
+ Key *key;
+ if ($2 != Key::FULLTEXT && lex->key_create_info.parser_name.str)
+ {
+ my_parse_error(ER(ER_SYNTAX_ERROR));
+ MYSQL_YYABORT;
+ }
+ key= new Key($2, $4.str, &lex->key_create_info, 0,
+ lex->col_list);
if (key == NULL)
MYSQL_YYABORT;
-
lex->alter_info.key_list.push_back(key);
- lex->col_list.empty();
- }
- | CREATE DATABASE opt_if_not_exists ident
- {
- Lex->create_info.default_table_charset= NULL;
- Lex->create_info.used_fields= 0;
- }
- opt_create_database_options
- {
- LEX *lex=Lex;
- lex->sql_command=SQLCOM_CREATE_DB;
- lex->name=$4.str;
+ lex->col_list.empty();
+ }
+ | CREATE DATABASE opt_if_not_exists ident
+ {
+ Lex->create_info.default_table_charset= NULL;
+ Lex->create_info.used_fields= 0;
+ }
+ opt_create_database_options
+ {
+ LEX *lex=Lex;
+ lex->sql_command=SQLCOM_CREATE_DB;
+ lex->name= $4;
lex->create_info.options=$3;
- }
- | CREATE
- {
+ }
+ | CREATE
+ {
Lex->create_view_mode= VIEW_CREATE_NEW;
Lex->create_view_algorithm= VIEW_ALGORITHM_UNDEFINED;
Lex->create_view_suid= TRUE;
- }
- view_or_trigger_or_sp
- {}
- | CREATE USER clear_privileges grant_list
- {
- Lex->sql_command = SQLCOM_CREATE_USER;
}
- ;
+ view_or_trigger_or_sp_or_event
+ {}
+ | CREATE USER clear_privileges grant_list
+ {
+ Lex->sql_command = SQLCOM_CREATE_USER;
+ }
+ | CREATE LOGFILE_SYM GROUP_SYM logfile_group_info
+ {
+ Lex->alter_tablespace_info->ts_cmd_type= CREATE_LOGFILE_GROUP;
+ }
+ | CREATE TABLESPACE tablespace_info
+ {
+ Lex->alter_tablespace_info->ts_cmd_type= CREATE_TABLESPACE;
+ }
+ | CREATE server_def
+ {
+ Lex->sql_command= SQLCOM_CREATE_SERVER;
+ }
+ ;
+
+server_def:
+ SERVER_SYM
+ ident_or_text
+ FOREIGN DATA_SYM WRAPPER_SYM
+ ident_or_text
+ OPTIONS_SYM '(' server_options_list ')'
+ {
+ Lex->server_options.server_name= $2.str;
+ Lex->server_options.server_name_length= $2.length;
+ Lex->server_options.scheme= $6.str;
+ }
+ ;
+
+server_options_list:
+ server_option
+ | server_options_list ',' server_option
+ ;
+
+server_option:
+ USER TEXT_STRING_sys
+ {
+ Lex->server_options.username= $2.str;
+ }
+ | HOST_SYM TEXT_STRING_sys
+ {
+ Lex->server_options.host= $2.str;
+ }
+ | DATABASE TEXT_STRING_sys
+ {
+ Lex->server_options.db= $2.str;
+ }
+ | OWNER_SYM TEXT_STRING_sys
+ {
+ Lex->server_options.owner= $2.str;
+ }
+ | PASSWORD TEXT_STRING_sys
+ {
+ Lex->server_options.password= $2.str;
+ }
+ | SOCKET_SYM TEXT_STRING_sys
+ {
+ Lex->server_options.socket= $2.str;
+ }
+ | PORT_SYM ulong_num
+ {
+ Lex->server_options.port= $2;
+ }
+ ;
+
+event_tail:
+ EVENT_SYM opt_if_not_exists sp_name
+ {
+ THD *thd= YYTHD;
+ LEX *lex=Lex;
+
+ lex->create_info.options= $2;
+ if (!(lex->event_parse_data= Event_parse_data::new_instance(thd)))
+ MYSQL_YYABORT;
+ lex->event_parse_data->identifier= $3;
+ lex->event_parse_data->on_completion=
+ Event_parse_data::ON_COMPLETION_DROP;
+
+ lex->sql_command= SQLCOM_CREATE_EVENT;
+ /* We need that for disallowing subqueries */
+ }
+ ON SCHEDULE_SYM ev_schedule_time
+ opt_ev_on_completion
+ opt_ev_status
+ opt_ev_comment
+ DO_SYM ev_sql_stmt
+ {
+ /*
+ sql_command is set here because some rules in ev_sql_stmt
+ can overwrite it
+ */
+ Lex->sql_command= SQLCOM_CREATE_EVENT;
+ }
+ ;
+
+ev_schedule_time:
+ EVERY_SYM expr interval
+ {
+ Lex->event_parse_data->item_expression= $2;
+ Lex->event_parse_data->interval= $3;
+ }
+ ev_starts
+ ev_ends
+ | AT_SYM expr
+ {
+ Lex->event_parse_data->item_execute_at= $2;
+ }
+ ;
+
+opt_ev_status:
+ /* empty */ { $$= 0; }
+ | ENABLE_SYM
+ {
+ Lex->event_parse_data->status= Event_parse_data::ENABLED;
+ $$= 1;
+ }
+ | DISABLE_SYM ON SLAVE
+ {
+ Lex->event_parse_data->status= Event_parse_data::SLAVESIDE_DISABLED;
+ $$= 1;
+ }
+ | DISABLE_SYM
+ {
+ Lex->event_parse_data->status= Event_parse_data::DISABLED;
+ $$= 1;
+ }
+ ;
+
+ev_starts:
+ /* empty */
+ {
+ Item *item= new (YYTHD->mem_root) Item_func_now_local();
+ if (item == NULL)
+ MYSQL_YYABORT;
+ Lex->event_parse_data->item_starts= item;
+ }
+ | STARTS_SYM expr
+ {
+ Lex->event_parse_data->item_starts= $2;
+ }
+ ;
+
+ev_ends:
+ /* empty */
+ | ENDS_SYM expr
+ {
+ Lex->event_parse_data->item_ends= $2;
+ }
+ ;
+
+opt_ev_on_completion:
+ /* empty */ { $$= 0; }
+ | ev_on_completion
+ ;
+
+ev_on_completion:
+ ON COMPLETION_SYM PRESERVE_SYM
+ {
+ Lex->event_parse_data->on_completion=
+ Event_parse_data::ON_COMPLETION_PRESERVE;
+ $$= 1;
+ }
+ | ON COMPLETION_SYM NOT_SYM PRESERVE_SYM
+ {
+ Lex->event_parse_data->on_completion=
+ Event_parse_data::ON_COMPLETION_DROP;
+ $$= 1;
+ }
+ ;
+
+opt_ev_comment:
+ /* empty */ { $$= 0; }
+ | COMMENT_SYM TEXT_STRING_sys
+ {
+ Lex->comment= Lex->event_parse_data->comment= $2;
+ $$= 1;
+ }
+ ;
+
+ev_sql_stmt:
+ {
+ THD *thd= YYTHD;
+ LEX *lex= thd->lex;
+ Lex_input_stream *lip= YYLIP;
+
+ /*
+ This stops the following :
+ - CREATE EVENT ... DO CREATE EVENT ...;
+ - ALTER EVENT ... DO CREATE EVENT ...;
+ - CREATE EVENT ... DO ALTER EVENT DO ....;
+ - CREATE PROCEDURE ... BEGIN CREATE EVENT ... END|
+ This allows:
+ - CREATE EVENT ... DO DROP EVENT yyy;
+ - CREATE EVENT ... DO ALTER EVENT yyy;
+ (the nested ALTER EVENT can have anything but DO clause)
+ - ALTER EVENT ... DO ALTER EVENT yyy;
+ (the nested ALTER EVENT can have anything but DO clause)
+ - ALTER EVENT ... DO DROP EVENT yyy;
+ - CREATE PROCEDURE ... BEGIN ALTER EVENT ... END|
+ (the nested ALTER EVENT can have anything but DO clause)
+ - CREATE PROCEDURE ... BEGIN DROP EVENT ... END|
+ */
+ if (lex->sphead)
+ {
+ my_error(ER_EVENT_RECURSION_FORBIDDEN, MYF(0));
+ MYSQL_YYABORT;
+ }
+
+ if (!(lex->sphead= new sp_head()))
+ MYSQL_YYABORT;
+
+ lex->sphead->reset_thd_mem_root(thd);
+ lex->sphead->init(lex);
+ lex->sphead->init_sp_name(thd, lex->event_parse_data->identifier);
+
+ lex->sphead->m_type= TYPE_ENUM_PROCEDURE;
+
+ bzero((char *)&lex->sp_chistics, sizeof(st_sp_chistics));
+ lex->sphead->m_chistics= &lex->sp_chistics;
+
+ lex->sphead->set_body_start(thd, lip->get_cpp_ptr());
+ }
+ ev_sql_stmt_inner
+ {
+ THD *thd= YYTHD;
+ LEX *lex= thd->lex;
+
+ /* return back to the original memory root ASAP */
+ lex->sphead->set_stmt_end(thd);
+ lex->sphead->restore_thd_mem_root(thd);
+
+ lex->sp_chistics.suid= SP_IS_SUID; //always the definer!
+
+ lex->event_parse_data->body_changed= TRUE;
+ }
+ ;
+
+ev_sql_stmt_inner:
+ sp_proc_stmt_statement
+ | sp_proc_stmt_return
+ | sp_proc_stmt_if
+ | case_stmt_specification
+ | sp_labeled_block
+ | sp_unlabeled_block
+ | sp_labeled_control
+ | sp_proc_stmt_unlabeled
+ | sp_proc_stmt_leave
+ | sp_proc_stmt_iterate
+ | sp_proc_stmt_open
+ | sp_proc_stmt_fetch
+ | sp_proc_stmt_close
+ ;
clear_privileges:
- /* Nothing */
- {
- LEX *lex=Lex;
- lex->users_list.empty();
- lex->columns.empty();
- lex->grant= lex->grant_tot_col= 0;
- lex->all_privileges= 0;
- lex->select_lex.db= 0;
- lex->ssl_type= SSL_TYPE_NOT_SPECIFIED;
- lex->ssl_cipher= lex->x509_subject= lex->x509_issuer= 0;
- bzero((char *)&(lex->mqh),sizeof(lex->mqh));
- }
+ /* Nothing */
+ {
+ LEX *lex=Lex;
+ lex->users_list.empty();
+ lex->columns.empty();
+ lex->grant= lex->grant_tot_col= 0;
+ lex->all_privileges= 0;
+ lex->select_lex.db= 0;
+ lex->ssl_type= SSL_TYPE_NOT_SPECIFIED;
+ lex->ssl_cipher= lex->x509_subject= lex->x509_issuer= 0;
+ bzero((char *)&(lex->mqh),sizeof(lex->mqh));
+ }
;
sp_name:
- ident '.' ident
- {
- if (!$1.str || check_db_name($1.str))
+ ident '.' ident
+ {
+ if (!$1.str || check_db_name(&$1))
{
- my_error(ER_WRONG_DB_NAME, MYF(0), $1.str);
- MYSQL_YYABORT;
- }
- if (check_routine_name($3))
+ my_error(ER_WRONG_DB_NAME, MYF(0), $1.str);
+ MYSQL_YYABORT;
+ }
+ if (check_routine_name(&$3))
{
- my_error(ER_SP_WRONG_NAME, MYF(0), $3.str);
- MYSQL_YYABORT;
- }
- $$= new sp_name($1, $3, true);
+ MYSQL_YYABORT;
+ }
+ $$= new sp_name($1, $3, true);
if ($$ == NULL)
MYSQL_YYABORT;
- $$->init_qname(YYTHD);
- }
- | ident
- {
- LEX *lex= Lex;
+ $$->init_qname(YYTHD);
+ }
+ | ident
+ {
+ THD *thd= YYTHD;
+ LEX *lex= thd->lex;
LEX_STRING db;
-
- if (check_routine_name($1))
+ if (check_routine_name(&$1))
{
- my_error(ER_SP_WRONG_NAME, MYF(0), $1.str);
- MYSQL_YYABORT;
- }
+ MYSQL_YYABORT;
+ }
if (lex->copy_db_to(&db.str, &db.length))
MYSQL_YYABORT;
- $$= new sp_name(db, $1, false);
+ $$= new sp_name(db, $1, false);
if ($$ == NULL)
MYSQL_YYABORT;
- $$->init_qname(YYTHD);
- }
- ;
-
+ $$->init_qname(thd);
+ }
+ ;
+
sp_a_chistics:
- /* Empty */ {}
- | sp_a_chistics sp_chistic {}
- ;
+ /* Empty */ {}
+ | sp_a_chistics sp_chistic {}
+ ;
sp_c_chistics:
- /* Empty */ {}
- | sp_c_chistics sp_c_chistic {}
- ;
+ /* Empty */ {}
+ | sp_c_chistics sp_c_chistic {}
+ ;
/* Characteristics for both create and alter */
sp_chistic:
- COMMENT_SYM TEXT_STRING_sys
- { Lex->sp_chistics.comment= $2; }
- | LANGUAGE_SYM SQL_SYM
- { /* Just parse it, we only have one language for now. */ }
- | NO_SYM SQL_SYM
- { Lex->sp_chistics.daccess= SP_NO_SQL; }
- | CONTAINS_SYM SQL_SYM
- { Lex->sp_chistics.daccess= SP_CONTAINS_SQL; }
- | READS_SYM SQL_SYM DATA_SYM
- { Lex->sp_chistics.daccess= SP_READS_SQL_DATA; }
- | MODIFIES_SYM SQL_SYM DATA_SYM
- { Lex->sp_chistics.daccess= SP_MODIFIES_SQL_DATA; }
- | sp_suid
- { }
- ;
+ COMMENT_SYM TEXT_STRING_sys
+ { Lex->sp_chistics.comment= $2; }
+ | LANGUAGE_SYM SQL_SYM
+ { /* Just parse it, we only have one language for now. */ }
+ | NO_SYM SQL_SYM
+ { Lex->sp_chistics.daccess= SP_NO_SQL; }
+ | CONTAINS_SYM SQL_SYM
+ { Lex->sp_chistics.daccess= SP_CONTAINS_SQL; }
+ | READS_SYM SQL_SYM DATA_SYM
+ { Lex->sp_chistics.daccess= SP_READS_SQL_DATA; }
+ | MODIFIES_SYM SQL_SYM DATA_SYM
+ { Lex->sp_chistics.daccess= SP_MODIFIES_SQL_DATA; }
+ | sp_suid
+ {}
+ ;
/* Create characteristics */
sp_c_chistic:
- sp_chistic { }
- | DETERMINISTIC_SYM { Lex->sp_chistics.detistic= TRUE; }
- | not DETERMINISTIC_SYM { Lex->sp_chistics.detistic= FALSE; }
- ;
+ sp_chistic { }
+ | DETERMINISTIC_SYM { Lex->sp_chistics.detistic= TRUE; }
+ | not DETERMINISTIC_SYM { Lex->sp_chistics.detistic= FALSE; }
+ ;
sp_suid:
- SQL_SYM SECURITY_SYM DEFINER_SYM
- {
- Lex->sp_chistics.suid= SP_IS_SUID;
- }
- | SQL_SYM SECURITY_SYM INVOKER_SYM
- {
- Lex->sp_chistics.suid= SP_IS_NOT_SUID;
- }
- ;
+ SQL_SYM SECURITY_SYM DEFINER_SYM
+ {
+ Lex->sp_chistics.suid= SP_IS_SUID;
+ }
+ | SQL_SYM SECURITY_SYM INVOKER_SYM
+ {
+ Lex->sp_chistics.suid= SP_IS_NOT_SUID;
+ }
+ ;
call:
- CALL_SYM sp_name
- {
- LEX *lex = Lex;
-
- lex->sql_command= SQLCOM_CALL;
- lex->spname= $2;
- lex->value_list.empty();
- sp_add_used_routine(lex, YYTHD, $2, TYPE_ENUM_PROCEDURE);
- }
+ CALL_SYM sp_name
+ {
+ LEX *lex = Lex;
+
+ lex->sql_command= SQLCOM_CALL;
+ lex->spname= $2;
+ lex->value_list.empty();
+ sp_add_used_routine(lex, YYTHD, $2, TYPE_ENUM_PROCEDURE);
+ }
opt_sp_cparam_list {}
- ;
+ ;
/* CALL parameters */
opt_sp_cparam_list:
- /* Empty */
- | '(' opt_sp_cparams ')'
- ;
+ /* Empty */
+ | '(' opt_sp_cparams ')'
+ ;
opt_sp_cparams:
/* Empty */
@@ -1695,58 +2116,58 @@ opt_sp_cparams:
;
sp_cparams:
- sp_cparams ',' expr
- {
- Lex->value_list.push_back($3);
- }
- | expr
- {
- Lex->value_list.push_back($1);
- }
- ;
+ sp_cparams ',' expr
+ {
+ Lex->value_list.push_back($3);
+ }
+ | expr
+ {
+ Lex->value_list.push_back($1);
+ }
+ ;
/* Stored FUNCTION parameter declaration list */
sp_fdparam_list:
- /* Empty */
- | sp_fdparams
- ;
+ /* Empty */
+ | sp_fdparams
+ ;
sp_fdparams:
- sp_fdparams ',' sp_fdparam
- | sp_fdparam
- ;
+ sp_fdparams ',' sp_fdparam
+ | sp_fdparam
+ ;
sp_init_param:
- /* Empty */
- {
- LEX *lex= Lex;
-
- lex->length= 0;
- lex->dec= 0;
- lex->type= 0;
-
- lex->default_value= 0;
- lex->on_update_value= 0;
-
- lex->comment= null_lex_str;
- lex->charset= NULL;
-
- lex->interval_list.empty();
- lex->uint_geom_type= 0;
- }
- ;
+ /* Empty */
+ {
+ LEX *lex= Lex;
+
+ lex->length= 0;
+ lex->dec= 0;
+ lex->type= 0;
+
+ lex->default_value= 0;
+ lex->on_update_value= 0;
+
+ lex->comment= null_lex_str;
+ lex->charset= NULL;
+
+ lex->interval_list.empty();
+ lex->uint_geom_type= 0;
+ }
+ ;
sp_fdparam:
- ident sp_init_param type
- {
- LEX *lex= Lex;
- sp_pcontext *spc= lex->spcont;
-
- if (spc->find_variable(&$1, TRUE))
- {
- my_error(ER_SP_DUP_PARAM, MYF(0), $1.str);
- MYSQL_YYABORT;
- }
+ ident sp_init_param type
+ {
+ LEX *lex= Lex;
+ sp_pcontext *spc= lex->spcont;
+
+ if (spc->find_variable(&$1, TRUE))
+ {
+ my_error(ER_SP_DUP_PARAM, MYF(0), $1.str);
+ MYSQL_YYABORT;
+ }
sp_variable_t *spvar= spc->push_variable(&$1,
(enum enum_field_types)$3,
sp_param_in);
@@ -1759,31 +2180,31 @@ sp_fdparam:
}
spvar->field_def.field_name= spvar->name.str;
spvar->field_def.pack_flag |= FIELDFLAG_MAYBE_NULL;
- }
- ;
+ }
+ ;
/* Stored PROCEDURE parameter declaration list */
sp_pdparam_list:
- /* Empty */
- | sp_pdparams
- ;
+ /* Empty */
+ | sp_pdparams
+ ;
sp_pdparams:
- sp_pdparams ',' sp_pdparam
- | sp_pdparam
- ;
+ sp_pdparams ',' sp_pdparam
+ | sp_pdparam
+ ;
sp_pdparam:
- sp_opt_inout sp_init_param ident type
- {
- LEX *lex= Lex;
- sp_pcontext *spc= lex->spcont;
-
- if (spc->find_variable(&$3, TRUE))
- {
- my_error(ER_SP_DUP_PARAM, MYF(0), $3.str);
- MYSQL_YYABORT;
- }
+ sp_opt_inout sp_init_param ident type
+ {
+ LEX *lex= Lex;
+ sp_pcontext *spc= lex->spcont;
+
+ if (spc->find_variable(&$3, TRUE))
+ {
+ my_error(ER_SP_DUP_PARAM, MYF(0), $3.str);
+ MYSQL_YYABORT;
+ }
sp_variable_t *spvar= spc->push_variable(&$3,
(enum enum_field_types)$4,
(sp_param_mode_t)$1);
@@ -1796,81 +2217,77 @@ sp_pdparam:
}
spvar->field_def.field_name= spvar->name.str;
spvar->field_def.pack_flag |= FIELDFLAG_MAYBE_NULL;
- }
- ;
+ }
+ ;
sp_opt_inout:
- /* Empty */ { $$= sp_param_in; }
- | IN_SYM { $$= sp_param_in; }
- | OUT_SYM { $$= sp_param_out; }
- | INOUT_SYM { $$= sp_param_inout; }
- ;
+ /* Empty */ { $$= sp_param_in; }
+ | IN_SYM { $$= sp_param_in; }
+ | OUT_SYM { $$= sp_param_out; }
+ | INOUT_SYM { $$= sp_param_inout; }
+ ;
sp_proc_stmts:
- /* Empty */ {}
- | sp_proc_stmts sp_proc_stmt ';'
- ;
+ /* Empty */ {}
+ | sp_proc_stmts sp_proc_stmt ';'
+ ;
sp_proc_stmts1:
- sp_proc_stmt ';' {}
- | sp_proc_stmts1 sp_proc_stmt ';'
- ;
+ sp_proc_stmt ';' {}
+ | sp_proc_stmts1 sp_proc_stmt ';'
+ ;
sp_decls:
- /* Empty */
- {
- $$.vars= $$.conds= $$.hndlrs= $$.curs= 0;
- }
- | sp_decls sp_decl ';'
- {
- /* We check for declarations out of (standard) order this way
- because letting the grammar rules reflect it caused tricky
- shift/reduce conflicts with the wrong result. (And we get
- better error handling this way.) */
- if (($2.vars || $2.conds) && ($1.curs || $1.hndlrs))
- { /* Variable or condition following cursor or handler */
- my_message(ER_SP_VARCOND_AFTER_CURSHNDLR,
+ /* Empty */
+ {
+ $$.vars= $$.conds= $$.hndlrs= $$.curs= 0;
+ }
+ | sp_decls sp_decl ';'
+ {
+ /* We check for declarations out of (standard) order this way
+ because letting the grammar rules reflect it caused tricky
+ shift/reduce conflicts with the wrong result. (And we get
+ better error handling this way.) */
+ if (($2.vars || $2.conds) && ($1.curs || $1.hndlrs))
+ { /* Variable or condition following cursor or handler */
+ my_message(ER_SP_VARCOND_AFTER_CURSHNDLR,
ER(ER_SP_VARCOND_AFTER_CURSHNDLR), MYF(0));
- MYSQL_YYABORT;
- }
- if ($2.curs && $1.hndlrs)
- { /* Cursor following handler */
- my_message(ER_SP_CURSOR_AFTER_HANDLER,
+ MYSQL_YYABORT;
+ }
+ if ($2.curs && $1.hndlrs)
+ { /* Cursor following handler */
+ my_message(ER_SP_CURSOR_AFTER_HANDLER,
ER(ER_SP_CURSOR_AFTER_HANDLER), MYF(0));
- MYSQL_YYABORT;
- }
- $$.vars= $1.vars + $2.vars;
- $$.conds= $1.conds + $2.conds;
- $$.hndlrs= $1.hndlrs + $2.hndlrs;
- $$.curs= $1.curs + $2.curs;
- }
- ;
+ MYSQL_YYABORT;
+ }
+ $$.vars= $1.vars + $2.vars;
+ $$.conds= $1.conds + $2.conds;
+ $$.hndlrs= $1.hndlrs + $2.hndlrs;
+ $$.curs= $1.curs + $2.curs;
+ }
+ ;
sp_decl:
DECLARE_SYM sp_decl_idents
{
LEX *lex= Lex;
- if (lex->sphead->reset_lex(YYTHD))
- MYSQL_YYABORT;
+ lex->sphead->reset_lex(YYTHD);
lex->spcont->declare_var_boundary($2);
}
type
sp_opt_default
{
+ THD *thd= YYTHD;
LEX *lex= Lex;
sp_pcontext *pctx= lex->spcont;
- if (pctx == 0)
- {
- MYSQL_YYABORT;
- }
uint num_vars= pctx->context_var_count();
enum enum_field_types var_type= (enum enum_field_types) $4;
Item *dflt_value_item= $5;
if (!dflt_value_item)
{
- dflt_value_item= new Item_null();
+ dflt_value_item= new (thd->mem_root) Item_null();
if (dflt_value_item == NULL)
MYSQL_YYABORT;
/* QQ Set to the var_type with null_value? */
@@ -1917,131 +2334,120 @@ sp_decl:
$$.vars= $2;
$$.conds= $$.hndlrs= $$.curs= 0;
}
- | DECLARE_SYM ident CONDITION_SYM FOR_SYM sp_cond
- {
- 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;
- }
- YYTHD->lex->spcont->push_cond(&$2, $5);
- $$.vars= $$.hndlrs= $$.curs= 0;
- $$.conds= 1;
- }
- | DECLARE_SYM sp_handler_type HANDLER_SYM FOR_SYM
- {
- LEX *lex= Lex;
- sp_head *sp= lex->sphead;
+ | DECLARE_SYM ident CONDITION_SYM FOR_SYM sp_cond
+ {
+ 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;
+ }
+ YYTHD->lex->spcont->push_cond(&$2, $5);
+ $$.vars= $$.hndlrs= $$.curs= 0;
+ $$.conds= 1;
+ }
+ | DECLARE_SYM sp_handler_type HANDLER_SYM FOR_SYM
+ {
+ LEX *lex= Lex;
+ sp_head *sp= lex->sphead;
lex->spcont= lex->spcont->push_context(LABEL_HANDLER_SCOPE);
- sp_pcontext *ctx= lex->spcont;
- sp_instr_hpush_jump *i=
+ 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)
MYSQL_YYABORT;
+ sp->add_instr(i);
+ sp->push_backpatch(i, ctx->push_label((char *)"", 0));
+ }
+ sp_hcond_list sp_proc_stmt
+ {
+ LEX *lex= Lex;
+ sp_head *sp= lex->sphead;
+ sp_pcontext *ctx= lex->spcont;
+ sp_label_t *hlab= lex->spcont->pop_label(); /* After this hdlr */
+ sp_instr_hreturn *i;
- sp->add_instr(i);
- sp->push_backpatch(i, ctx->push_label((char *)"", 0));
- }
- sp_hcond_list sp_proc_stmt
- {
- LEX *lex= Lex;
- sp_head *sp= lex->sphead;
- sp_pcontext *ctx= lex->spcont;
- sp_label_t *hlab= lex->spcont->pop_label(); /* After this hdlr */
- sp_instr_hreturn *i;
-
- if ($2 == SP_HANDLER_CONTINUE)
- {
- i= new sp_instr_hreturn(sp->instructions(), ctx,
- ctx->current_var_count());
- if (i == NULL )
+ if ($2 == SP_HANDLER_CONTINUE)
+ {
+ i= new sp_instr_hreturn(sp->instructions(), ctx,
+ ctx->current_var_count());
+ if (i == NULL)
MYSQL_YYABORT;
- sp->add_instr(i);
- }
- else
- { /* EXIT or UNDO handler, just jump to the end of the block */
- i= new sp_instr_hreturn(sp->instructions(), ctx, 0);
+ sp->add_instr(i);
+ }
+ 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)
MYSQL_YYABORT;
- sp->add_instr(i);
- sp->push_backpatch(i, lex->spcont->last_label()); /* Block end */
- }
- lex->sphead->backpatch(hlab);
+ sp->add_instr(i);
+ sp->push_backpatch(i, lex->spcont->last_label()); /* Block end */
+ }
+ lex->sphead->backpatch(hlab);
lex->spcont= ctx->pop_context();
- $$.vars= $$.conds= $$.curs= 0;
- $$.hndlrs= $6;
- lex->spcont->add_handlers($6);
- }
- | DECLARE_SYM ident CURSOR_SYM FOR_SYM sp_cursor_stmt
- {
- LEX *lex= Lex;
- sp_head *sp= lex->sphead;
- sp_pcontext *ctx= lex->spcont;
- uint offp;
- sp_instr_cpush *i;
-
- if (ctx->find_cursor(&$2, &offp, TRUE))
- {
- my_error(ER_SP_DUP_CURS, MYF(0), $2.str);
- delete $5;
- MYSQL_YYABORT;
- }
+ $$.vars= $$.conds= $$.curs= 0;
+ $$.hndlrs= $6;
+ lex->spcont->add_handlers($6);
+ }
+ | DECLARE_SYM ident CURSOR_SYM FOR_SYM sp_cursor_stmt
+ {
+ LEX *lex= Lex;
+ sp_head *sp= lex->sphead;
+ sp_pcontext *ctx= lex->spcont;
+ uint offp;
+ sp_instr_cpush *i;
+
+ if (ctx->find_cursor(&$2, &offp, TRUE))
+ {
+ my_error(ER_SP_DUP_CURS, MYF(0), $2.str);
+ delete $5;
+ MYSQL_YYABORT;
+ }
i= new sp_instr_cpush(sp->instructions(), ctx, $5,
ctx->current_cursor_count());
- if (i == NULL)
+ if (i == NULL)
MYSQL_YYABORT;
sp->add_instr(i);
- ctx->push_cursor(&$2);
- $$.vars= $$.conds= $$.hndlrs= 0;
- $$.curs= 1;
- }
- ;
+ ctx->push_cursor(&$2);
+ $$.vars= $$.conds= $$.hndlrs= 0;
+ $$.curs= 1;
+ }
+ ;
sp_cursor_stmt:
- {
- if(Lex->sphead->reset_lex(YYTHD))
- MYSQL_YYABORT;
-
- /* We use statement here just be able to get a better
- error message. Using 'select' works too, but will then
- result in a generic "syntax error" if a non-select
- statement is given. */
- }
- statement
- {
- LEX *lex= Lex;
-
- if (lex->sql_command != SQLCOM_SELECT)
- {
- my_message(ER_SP_BAD_CURSOR_QUERY, ER(ER_SP_BAD_CURSOR_QUERY),
- MYF(0));
- MYSQL_YYABORT;
- }
- if (lex->result)
- {
- my_message(ER_SP_BAD_CURSOR_SELECT, ER(ER_SP_BAD_CURSOR_SELECT),
+ {
+ Lex->sphead->reset_lex(YYTHD);
+ }
+ select
+ {
+ LEX *lex= Lex;
+
+ DBUG_ASSERT(lex->sql_command == SQLCOM_SELECT);
+
+ if (lex->result)
+ {
+ my_message(ER_SP_BAD_CURSOR_SELECT, ER(ER_SP_BAD_CURSOR_SELECT),
MYF(0));
- MYSQL_YYABORT;
- }
- lex->sp_lex_in_use= TRUE;
- $$= lex;
- lex->sphead->restore_lex(YYTHD);
- }
- ;
+ MYSQL_YYABORT;
+ }
+ lex->sp_lex_in_use= TRUE;
+ $$= lex;
+ lex->sphead->restore_lex(YYTHD);
+ }
+ ;
sp_handler_type:
- EXIT_SYM { $$= SP_HANDLER_EXIT; }
- | CONTINUE_SYM { $$= SP_HANDLER_CONTINUE; }
-/* | UNDO_SYM { QQ No yet } */
- ;
+ EXIT_SYM { $$= SP_HANDLER_EXIT; }
+ | CONTINUE_SYM { $$= SP_HANDLER_CONTINUE; }
+ /*| UNDO_SYM { QQ No yet } */
+ ;
sp_hcond_list:
sp_hcond_element
@@ -2051,252 +2457,270 @@ sp_hcond_list:
;
sp_hcond_element:
- sp_hcond
- {
- LEX *lex= Lex;
- sp_head *sp= lex->sphead;
- sp_pcontext *ctx= lex->spcont->parent_context();
-
- if (ctx->find_handler($1))
- {
- my_message(ER_SP_DUP_HANDLER, ER(ER_SP_DUP_HANDLER), MYF(0));
- MYSQL_YYABORT;
- }
- else
- {
- sp_instr_hpush_jump *i=
+ sp_hcond
+ {
+ LEX *lex= Lex;
+ sp_head *sp= lex->sphead;
+ sp_pcontext *ctx= lex->spcont->parent_context();
+
+ if (ctx->find_handler($1))
+ {
+ my_message(ER_SP_DUP_HANDLER, ER(ER_SP_DUP_HANDLER), MYF(0));
+ MYSQL_YYABORT;
+ }
+ else
+ {
+ sp_instr_hpush_jump *i=
(sp_instr_hpush_jump *)sp->last_instruction();
- i->add_condition($1);
- ctx->push_handler($1);
- }
- }
- ;
+ i->add_condition($1);
+ ctx->push_handler($1);
+ }
+ }
+ ;
sp_cond:
- ulong_num
- { /* mysql errno */
- $$= (sp_cond_type_t *)YYTHD->alloc(sizeof(sp_cond_type_t));
+ ulong_num
+ { /* mysql errno */
+ $$= (sp_cond_type_t *)YYTHD->alloc(sizeof(sp_cond_type_t));
if ($$ == NULL)
- YYABORT;
- $$->type= sp_cond_type_t::number;
- $$->mysqlerr= $1;
- }
- | 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));
+ MYSQL_YYABORT;
+ $$->type= sp_cond_type_t::number;
+ $$->mysqlerr= $1;
+ }
+ | 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));
if ($$ == NULL)
- YYABORT;
- $$->type= sp_cond_type_t::state;
- memcpy($$->sqlstate, $3.str, 5);
- $$->sqlstate[5]= '\0';
- }
- ;
+ MYSQL_YYABORT;
+ $$->type= sp_cond_type_t::state;
+ memcpy($$->sqlstate, $3.str, 5);
+ $$->sqlstate[5]= '\0';
+ }
+ ;
opt_value:
- /* Empty */ {}
- | VALUE_SYM {}
- ;
+ /* Empty */ {}
+ | VALUE_SYM {}
+ ;
sp_hcond:
- sp_cond
- {
- $$= $1;
- }
- | ident /* CONDITION name */
- {
- $$= Lex->spcont->find_cond(&$1);
- if ($$ == NULL)
- {
- my_error(ER_SP_COND_MISMATCH, MYF(0), $1.str);
- MYSQL_YYABORT;
- }
- }
- | SQLWARNING_SYM /* SQLSTATEs 01??? */
- {
- $$= (sp_cond_type_t *)YYTHD->alloc(sizeof(sp_cond_type_t));
+ sp_cond
+ {
+ $$= $1;
+ }
+ | ident /* CONDITION name */
+ {
+ $$= Lex->spcont->find_cond(&$1);
if ($$ == NULL)
- YYABORT;
- $$->type= sp_cond_type_t::warning;
- }
- | not FOUND_SYM /* SQLSTATEs 02??? */
- {
- $$= (sp_cond_type_t *)YYTHD->alloc(sizeof(sp_cond_type_t));
+ {
+ my_error(ER_SP_COND_MISMATCH, MYF(0), $1.str);
+ MYSQL_YYABORT;
+ }
+ }
+ | SQLWARNING_SYM /* SQLSTATEs 01??? */
+ {
+ $$= (sp_cond_type_t *)YYTHD->alloc(sizeof(sp_cond_type_t));
if ($$ == NULL)
- YYABORT;
- $$->type= sp_cond_type_t::notfound;
- }
- | SQLEXCEPTION_SYM /* All other SQLSTATEs */
- {
- $$= (sp_cond_type_t *)YYTHD->alloc(sizeof(sp_cond_type_t));
+ MYSQL_YYABORT;
+ $$->type= sp_cond_type_t::warning;
+ }
+ | not FOUND_SYM /* SQLSTATEs 02??? */
+ {
+ $$= (sp_cond_type_t *)YYTHD->alloc(sizeof(sp_cond_type_t));
if ($$ == NULL)
- YYABORT;
- $$->type= sp_cond_type_t::exception;
- }
- ;
+ MYSQL_YYABORT;
+ $$->type= sp_cond_type_t::notfound;
+ }
+ | SQLEXCEPTION_SYM /* All other SQLSTATEs */
+ {
+ $$= (sp_cond_type_t *)YYTHD->alloc(sizeof(sp_cond_type_t));
+ if ($$ == NULL)
+ MYSQL_YYABORT;
+ $$->type= sp_cond_type_t::exception;
+ }
+ ;
sp_decl_idents:
- ident
- {
+ ident
+ {
/* NOTE: field definition is filled in sp_decl section. */
- LEX *lex= Lex;
- sp_pcontext *spc= lex->spcont;
-
- if (spc->find_variable(&$1, TRUE))
- {
- my_error(ER_SP_DUP_VAR, MYF(0), $1.str);
- MYSQL_YYABORT;
- }
- spc->push_variable(&$1, (enum_field_types)0, sp_param_in);
- $$= 1;
- }
- | sp_decl_idents ',' ident
- {
+ LEX *lex= Lex;
+ sp_pcontext *spc= lex->spcont;
+
+ if (spc->find_variable(&$1, TRUE))
+ {
+ my_error(ER_SP_DUP_VAR, MYF(0), $1.str);
+ MYSQL_YYABORT;
+ }
+ spc->push_variable(&$1, (enum_field_types)0, sp_param_in);
+ $$= 1;
+ }
+ | sp_decl_idents ',' ident
+ {
/* NOTE: field definition is filled in sp_decl section. */
- LEX *lex= Lex;
- sp_pcontext *spc= lex->spcont;
-
- if (spc->find_variable(&$3, TRUE))
- {
- my_error(ER_SP_DUP_VAR, MYF(0), $3.str);
- MYSQL_YYABORT;
- }
- spc->push_variable(&$3, (enum_field_types)0, sp_param_in);
- $$= $1 + 1;
- }
- ;
+ LEX *lex= Lex;
+ sp_pcontext *spc= lex->spcont;
+
+ if (spc->find_variable(&$3, TRUE))
+ {
+ my_error(ER_SP_DUP_VAR, MYF(0), $3.str);
+ MYSQL_YYABORT;
+ }
+ spc->push_variable(&$3, (enum_field_types)0, sp_param_in);
+ $$= $1 + 1;
+ }
+ ;
sp_opt_default:
- /* Empty */ { $$ = NULL; }
+ /* Empty */ { $$ = NULL; }
| DEFAULT expr { $$ = $2; }
- ;
+ ;
sp_proc_stmt:
- {
+ sp_proc_stmt_statement
+ | sp_proc_stmt_return
+ | sp_proc_stmt_if
+ | case_stmt_specification
+ | sp_labeled_block
+ | sp_unlabeled_block
+ | sp_labeled_control
+ | sp_proc_stmt_unlabeled
+ | sp_proc_stmt_leave
+ | sp_proc_stmt_iterate
+ | sp_proc_stmt_open
+ | sp_proc_stmt_fetch
+ | sp_proc_stmt_close
+ ;
+
+sp_proc_stmt_if:
+ IF
+ { Lex->sphead->new_cont_backpatch(NULL); }
+ sp_if END IF
+ { Lex->sphead->do_cont_backpatch(); }
+ ;
+
+sp_proc_stmt_statement:
+ {
THD *thd= YYTHD;
- LEX *lex= thd->lex;
+ LEX *lex= thd->lex;
Lex_input_stream *lip= YYLIP;
- if (lex->sphead->reset_lex(thd))
- MYSQL_YYABORT;
- lex->sphead->m_tmp_query= lip->tok_start;
- }
- statement
- {
+ lex->sphead->reset_lex(thd);
+ lex->sphead->m_tmp_query= lip->get_tok_start();
+ }
+ statement
+ {
THD *thd= YYTHD;
- LEX *lex= thd->lex;
+ LEX *lex= thd->lex;
Lex_input_stream *lip= YYLIP;
- sp_head *sp= lex->sphead;
+ sp_head *sp= lex->sphead;
sp->m_flags|= sp_get_flags_for_command(lex);
- if (lex->sql_command == SQLCOM_CHANGE_DB)
- { /* "USE db" doesn't work in a procedure */
- my_error(ER_SP_BADSTATEMENT, MYF(0), "USE");
- MYSQL_YYABORT;
- }
- /*
+ if (lex->sql_command == SQLCOM_CHANGE_DB)
+ { /* "USE db" doesn't work in a procedure */
+ my_error(ER_SP_BADSTATEMENT, MYF(0), "USE");
+ MYSQL_YYABORT;
+ }
+ /*
Don't add an instruction for SET statements, since all
instructions for them were already added during processing
of "set" rule.
- */
+ */
DBUG_ASSERT(lex->sql_command != SQLCOM_SET_OPTION ||
lex->var_list.is_empty());
if (lex->sql_command != SQLCOM_SET_OPTION)
- {
- sp_instr_stmt *i= new sp_instr_stmt(sp->instructions(),
+ {
+ sp_instr_stmt *i=new sp_instr_stmt(sp->instructions(),
lex->spcont, lex);
if (i == NULL)
MYSQL_YYABORT;
+
/*
Extract the query statement from the tokenizer. The
end is either lex->ptr, if there was no lookahead,
lex->tok_end otherwise.
*/
if (yychar == YYEMPTY)
- i->m_query.length= lip->ptr - sp->m_tmp_query;
+ i->m_query.length= lip->get_ptr() - sp->m_tmp_query;
else
- i->m_query.length= lip->tok_end - sp->m_tmp_query;
+ i->m_query.length= lip->get_tok_end() - sp->m_tmp_query;
i->m_query.str= strmake_root(thd->mem_root,
sp->m_tmp_query,
i->m_query.length);
sp->add_instr(i);
}
- sp->restore_lex(thd);
- }
- | RETURN_SYM
- {
- if(Lex->sphead->reset_lex(YYTHD))
- MYSQL_YYABORT;
+ sp->restore_lex(thd);
}
+ ;
+
+sp_proc_stmt_return:
+ RETURN_SYM
+ { Lex->sphead->reset_lex(YYTHD); }
expr
- {
- LEX *lex= Lex;
- sp_head *sp= lex->sphead;
-
- if (sp->m_type != TYPE_ENUM_FUNCTION)
- {
- my_message(ER_SP_BADRETURN, ER(ER_SP_BADRETURN), MYF(0));
- MYSQL_YYABORT;
- }
- else
- {
- sp_instr_freturn *i;
-
- i= new sp_instr_freturn(sp->instructions(), lex->spcont, $3,
+ {
+ LEX *lex= Lex;
+ sp_head *sp= lex->sphead;
+
+ if (sp->m_type != TYPE_ENUM_FUNCTION)
+ {
+ my_message(ER_SP_BADRETURN, ER(ER_SP_BADRETURN), MYF(0));
+ MYSQL_YYABORT;
+ }
+ else
+ {
+ sp_instr_freturn *i;
+
+ i= new sp_instr_freturn(sp->instructions(), lex->spcont, $3,
sp->m_return_field_def.sql_type, lex);
if (i == NULL)
MYSQL_YYABORT;
- sp->add_instr(i);
- sp->m_flags|= sp_head::HAS_RETURN;
- }
- sp->restore_lex(YYTHD);
- }
- | IF
- { Lex->sphead->new_cont_backpatch(NULL); }
- sp_if END IF
- { Lex->sphead->do_cont_backpatch(); }
- | case_stmt_specification
- | sp_labeled_control
- {}
- | { /* Unlabeled controls get a secret label. */
- LEX *lex= Lex;
-
- lex->spcont->push_label((char *)"", lex->sphead->instructions());
- }
- sp_unlabeled_control
- {
- LEX *lex= Lex;
-
- lex->sphead->backpatch(lex->spcont->pop_label());
- }
- | sp_labeled_block
- {}
- | sp_unlabeled_block
- {}
- | LEAVE_SYM label_ident
- {
- LEX *lex= Lex;
- sp_head *sp = lex->sphead;
- sp_pcontext *ctx= lex->spcont;
- sp_label_t *lab= ctx->find_label($2.str);
-
- if (! lab)
- {
- my_error(ER_SP_LILABEL_MISMATCH, MYF(0), "LEAVE", $2.str);
- MYSQL_YYABORT;
- }
- else
- {
- sp_instr_jump *i;
- uint ip= sp->instructions();
- uint n;
+ sp->add_instr(i);
+ sp->m_flags|= sp_head::HAS_RETURN;
+ }
+ sp->restore_lex(YYTHD);
+ }
+ ;
+
+sp_proc_stmt_unlabeled:
+ { /* Unlabeled controls get a secret label. */
+ LEX *lex= Lex;
+
+ lex->spcont->push_label((char *)"", lex->sphead->instructions());
+ }
+ sp_unlabeled_control
+ {
+ LEX *lex= Lex;
+
+ lex->sphead->backpatch(lex->spcont->pop_label());
+ }
+ ;
+
+sp_proc_stmt_leave:
+ LEAVE_SYM label_ident
+ {
+ LEX *lex= Lex;
+ sp_head *sp = lex->sphead;
+ sp_pcontext *ctx= lex->spcont;
+ sp_label_t *lab= ctx->find_label($2.str);
+
+ if (! lab)
+ {
+ my_error(ER_SP_LILABEL_MISMATCH, MYF(0), "LEAVE", $2.str);
+ MYSQL_YYABORT;
+ }
+ else
+ {
+ sp_instr_jump *i;
+ uint ip= sp->instructions();
+ uint n;
/*
When jumping to a BEGIN-END block end, the target jump
points to the block hpop/cpop cleanup instructions,
@@ -2307,219 +2731,229 @@ sp_proc_stmt:
*/
bool exclusive= (lab->type == SP_LAB_BEGIN);
- n= ctx->diff_handlers(lab->ctx, exclusive);
- if (n)
+ n= ctx->diff_handlers(lab->ctx, exclusive);
+ if (n)
{
sp_instr_hpop *hpop= new sp_instr_hpop(ip++, ctx, n);
if (hpop == NULL)
MYSQL_YYABORT;
- sp->add_instr(hpop);
+ sp->add_instr(hpop);
}
- n= ctx->diff_cursors(lab->ctx, exclusive);
- if (n)
+ n= ctx->diff_cursors(lab->ctx, exclusive);
+ if (n)
{
sp_instr_cpop *cpop= new sp_instr_cpop(ip++, ctx, n);
if (cpop == NULL)
MYSQL_YYABORT;
- sp->add_instr(cpop);
+ sp->add_instr(cpop);
}
- i= new sp_instr_jump(ip, ctx);
+ i= new sp_instr_jump(ip, ctx);
if (i == NULL)
MYSQL_YYABORT;
- sp->push_backpatch(i, lab); /* Jumping forward */
+ sp->push_backpatch(i, lab); /* Jumping forward */
sp->add_instr(i);
- }
- }
- | ITERATE_SYM label_ident
- {
- LEX *lex= Lex;
- sp_head *sp= lex->sphead;
- sp_pcontext *ctx= lex->spcont;
- sp_label_t *lab= ctx->find_label($2.str);
-
- if (! lab || lab->type != SP_LAB_ITER)
- {
- my_error(ER_SP_LILABEL_MISMATCH, MYF(0), "ITERATE", $2.str);
- MYSQL_YYABORT;
- }
- else
- {
- sp_instr_jump *i;
- uint ip= sp->instructions();
- uint n;
-
- n= ctx->diff_handlers(lab->ctx, FALSE); /* Inclusive the dest. */
- if (n)
+ }
+ }
+ ;
+
+sp_proc_stmt_iterate:
+ ITERATE_SYM label_ident
+ {
+ LEX *lex= Lex;
+ sp_head *sp= lex->sphead;
+ sp_pcontext *ctx= lex->spcont;
+ sp_label_t *lab= ctx->find_label($2.str);
+
+ if (! lab || lab->type != SP_LAB_ITER)
+ {
+ my_error(ER_SP_LILABEL_MISMATCH, MYF(0), "ITERATE", $2.str);
+ MYSQL_YYABORT;
+ }
+ else
+ {
+ sp_instr_jump *i;
+ uint ip= sp->instructions();
+ uint n;
+
+ n= ctx->diff_handlers(lab->ctx, FALSE); /* Inclusive the dest. */
+ if (n)
{
sp_instr_hpop *hpop= new sp_instr_hpop(ip++, ctx, n);
if (hpop == NULL)
MYSQL_YYABORT;
- sp->add_instr(hpop);
+ sp->add_instr(hpop);
}
- n= ctx->diff_cursors(lab->ctx, FALSE); /* Inclusive the dest. */
- if (n)
+ n= ctx->diff_cursors(lab->ctx, FALSE); /* Inclusive the dest. */
+ if (n)
{
sp_instr_cpop *cpop= new sp_instr_cpop(ip++, ctx, n);
if (cpop == NULL)
MYSQL_YYABORT;
- sp->add_instr(cpop);
+ sp->add_instr(cpop);
}
- i= new sp_instr_jump(ip, ctx, lab->ip); /* Jump back */
+ i= new sp_instr_jump(ip, ctx, lab->ip); /* Jump back */
if (i == NULL)
MYSQL_YYABORT;
sp->add_instr(i);
- }
- }
- | OPEN_SYM ident
- {
- LEX *lex= Lex;
- sp_head *sp= lex->sphead;
- uint offset;
- sp_instr_copen *i;
-
- if (! lex->spcont->find_cursor(&$2, &offset))
- {
- my_error(ER_SP_CURSOR_MISMATCH, MYF(0), $2.str);
- MYSQL_YYABORT;
- }
- i= new sp_instr_copen(sp->instructions(), lex->spcont, offset);
+ }
+ }
+ ;
+
+sp_proc_stmt_open:
+ OPEN_SYM ident
+ {
+ LEX *lex= Lex;
+ sp_head *sp= lex->sphead;
+ uint offset;
+ sp_instr_copen *i;
+
+ if (! lex->spcont->find_cursor(&$2, &offset))
+ {
+ my_error(ER_SP_CURSOR_MISMATCH, MYF(0), $2.str);
+ MYSQL_YYABORT;
+ }
+ i= new sp_instr_copen(sp->instructions(), lex->spcont, offset);
if (i == NULL)
MYSQL_YYABORT;
- sp->add_instr(i);
- }
- | FETCH_SYM sp_opt_fetch_noise ident INTO
- {
- LEX *lex= Lex;
- sp_head *sp= lex->sphead;
- uint offset;
- sp_instr_cfetch *i;
-
- if (! lex->spcont->find_cursor(&$3, &offset))
- {
- my_error(ER_SP_CURSOR_MISMATCH, MYF(0), $3.str);
- MYSQL_YYABORT;
- }
- i= new sp_instr_cfetch(sp->instructions(), lex->spcont, offset);
+ sp->add_instr(i);
+ }
+ ;
+
+sp_proc_stmt_fetch:
+ FETCH_SYM sp_opt_fetch_noise ident INTO
+ {
+ LEX *lex= Lex;
+ sp_head *sp= lex->sphead;
+ uint offset;
+ sp_instr_cfetch *i;
+
+ if (! lex->spcont->find_cursor(&$3, &offset))
+ {
+ my_error(ER_SP_CURSOR_MISMATCH, MYF(0), $3.str);
+ MYSQL_YYABORT;
+ }
+ i= new sp_instr_cfetch(sp->instructions(), lex->spcont, offset);
if (i == NULL)
MYSQL_YYABORT;
- sp->add_instr(i);
- }
- sp_fetch_list
- { }
- | CLOSE_SYM ident
- {
- LEX *lex= Lex;
- sp_head *sp= lex->sphead;
- uint offset;
- sp_instr_cclose *i;
-
- if (! lex->spcont->find_cursor(&$2, &offset))
- {
- my_error(ER_SP_CURSOR_MISMATCH, MYF(0), $2.str);
- MYSQL_YYABORT;
- }
- i= new sp_instr_cclose(sp->instructions(), lex->spcont, offset);
+ sp->add_instr(i);
+ }
+ sp_fetch_list
+ {}
+ ;
+
+sp_proc_stmt_close:
+ CLOSE_SYM ident
+ {
+ LEX *lex= Lex;
+ sp_head *sp= lex->sphead;
+ uint offset;
+ sp_instr_cclose *i;
+
+ if (! lex->spcont->find_cursor(&$2, &offset))
+ {
+ my_error(ER_SP_CURSOR_MISMATCH, MYF(0), $2.str);
+ MYSQL_YYABORT;
+ }
+ i= new sp_instr_cclose(sp->instructions(), lex->spcont, offset);
if (i == NULL)
MYSQL_YYABORT;
- sp->add_instr(i);
- }
- ;
+ sp->add_instr(i);
+ }
+ ;
sp_opt_fetch_noise:
- /* Empty */
- | NEXT_SYM FROM
- | FROM
- ;
+ /* Empty */
+ | NEXT_SYM FROM
+ | FROM
+ ;
sp_fetch_list:
- ident
- {
- LEX *lex= Lex;
- sp_head *sp= lex->sphead;
- sp_pcontext *spc= lex->spcont;
- sp_variable_t *spv;
-
- if (!spc || !(spv = spc->find_variable(&$1)))
- {
- my_error(ER_SP_UNDECLARED_VAR, MYF(0), $1.str);
- MYSQL_YYABORT;
- }
- else
- {
- /* An SP local variable */
- sp_instr_cfetch *i= (sp_instr_cfetch *)sp->last_instruction();
-
- i->add_to_varlist(spv);
- }
- }
- |
- sp_fetch_list ',' ident
- {
- LEX *lex= Lex;
- sp_head *sp= lex->sphead;
- sp_pcontext *spc= lex->spcont;
- sp_variable_t *spv;
-
- if (!spc || !(spv = spc->find_variable(&$3)))
- {
- my_error(ER_SP_UNDECLARED_VAR, MYF(0), $3.str);
- MYSQL_YYABORT;
- }
- else
- {
- /* An SP local variable */
- sp_instr_cfetch *i= (sp_instr_cfetch *)sp->last_instruction();
-
- i->add_to_varlist(spv);
- }
- }
- ;
+ ident
+ {
+ LEX *lex= Lex;
+ sp_head *sp= lex->sphead;
+ sp_pcontext *spc= lex->spcont;
+ sp_variable_t *spv;
-sp_if:
+ if (!spc || !(spv = spc->find_variable(&$1)))
+ {
+ my_error(ER_SP_UNDECLARED_VAR, MYF(0), $1.str);
+ MYSQL_YYABORT;
+ }
+ else
+ {
+ /* An SP local variable */
+ sp_instr_cfetch *i= (sp_instr_cfetch *)sp->last_instruction();
+
+ i->add_to_varlist(spv);
+ }
+ }
+ | sp_fetch_list ',' ident
{
- if (Lex->sphead->reset_lex(YYTHD))
+ LEX *lex= Lex;
+ sp_head *sp= lex->sphead;
+ sp_pcontext *spc= lex->spcont;
+ sp_variable_t *spv;
+
+ if (!spc || !(spv = spc->find_variable(&$3)))
+ {
+ my_error(ER_SP_UNDECLARED_VAR, MYF(0), $3.str);
MYSQL_YYABORT;
+ }
+ else
+ {
+ /* An SP local variable */
+ sp_instr_cfetch *i= (sp_instr_cfetch *)sp->last_instruction();
+
+ i->add_to_varlist(spv);
+ }
}
+ ;
+
+sp_if:
+ { Lex->sphead->reset_lex(YYTHD); }
expr THEN_SYM
- {
- LEX *lex= Lex;
- sp_head *sp= lex->sphead;
- sp_pcontext *ctx= lex->spcont;
- uint ip= sp->instructions();
- sp_instr_jump_if_not *i = new sp_instr_jump_if_not(ip, ctx,
+ {
+ LEX *lex= Lex;
+ sp_head *sp= lex->sphead;
+ sp_pcontext *ctx= lex->spcont;
+ uint ip= sp->instructions();
+ sp_instr_jump_if_not *i = new sp_instr_jump_if_not(ip, ctx,
$2, lex);
if (i == NULL)
MYSQL_YYABORT;
- 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);
sp->restore_lex(YYTHD);
- }
- sp_proc_stmts1
- {
- sp_head *sp= Lex->sphead;
- sp_pcontext *ctx= Lex->spcont;
- uint ip= sp->instructions();
- sp_instr_jump *i = new sp_instr_jump(ip, ctx);
+ }
+ sp_proc_stmts1
+ {
+ sp_head *sp= Lex->sphead;
+ sp_pcontext *ctx= Lex->spcont;
+ uint ip= sp->instructions();
+ sp_instr_jump *i = new sp_instr_jump(ip, ctx);
if (i == NULL)
MYSQL_YYABORT;
- sp->add_instr(i);
- sp->backpatch(ctx->pop_label());
- sp->push_backpatch(i, ctx->push_label((char *)"", 0));
- }
- sp_elseifs
- {
- LEX *lex= Lex;
- lex->sphead->backpatch(lex->spcont->pop_label());
- }
- ;
+ sp->add_instr(i);
+ sp->backpatch(ctx->pop_label());
+ sp->push_backpatch(i, ctx->push_label((char *)"", 0));
+ }
+ sp_elseifs
+ {
+ LEX *lex= Lex;
+
+ lex->sphead->backpatch(lex->spcont->pop_label());
+ }
+ ;
sp_elseifs:
- /* Empty */
- | ELSEIF_SYM sp_if
- | ELSE sp_proc_stmts1
- ;
+ /* Empty */
+ | ELSEIF_SYM sp_if
+ | ELSE sp_proc_stmts1
+ ;
case_stmt_specification:
simple_case_stmt
@@ -2531,8 +2965,7 @@ simple_case_stmt:
{
LEX *lex= Lex;
case_stmt_action_case(lex);
- if (lex->sphead->reset_lex(YYTHD))
- MYSQL_YYABORT; /* For expr $3 */
+ lex->sphead->reset_lex(YYTHD); /* For expr $3 */
}
expr
{
@@ -2581,8 +3014,7 @@ searched_when_clause_list:
simple_when_clause:
WHEN_SYM
{
- if (Lex->sphead->reset_lex(YYTHD))
- MYSQL_YYABORT; /* For expr $3 */
+ Lex->sphead->reset_lex(YYTHD); /* For expr $3 */
}
expr
{
@@ -2603,8 +3035,7 @@ simple_when_clause:
searched_when_clause:
WHEN_SYM
{
- if (Lex->sphead->reset_lex(YYTHD))
- MYSQL_YYABORT; /* For expr $3 */
+ Lex->sphead->reset_lex(YYTHD); /* For expr $3 */
}
expr
{
@@ -2636,45 +3067,45 @@ else_clause_opt:
;
sp_labeled_control:
- label_ident ':'
- {
- LEX *lex= Lex;
- sp_pcontext *ctx= lex->spcont;
- sp_label_t *lab= ctx->find_label($1.str);
-
- if (lab)
- {
- my_error(ER_SP_LABEL_REDEFINE, MYF(0), $1.str);
- MYSQL_YYABORT;
- }
- else
- {
- lab= lex->spcont->push_label($1.str,
- lex->sphead->instructions());
- lab->type= SP_LAB_ITER;
- }
- }
- sp_unlabeled_control sp_opt_label
- {
- LEX *lex= Lex;
+ label_ident ':'
+ {
+ LEX *lex= Lex;
+ sp_pcontext *ctx= lex->spcont;
+ sp_label_t *lab= ctx->find_label($1.str);
+
+ if (lab)
+ {
+ my_error(ER_SP_LABEL_REDEFINE, MYF(0), $1.str);
+ MYSQL_YYABORT;
+ }
+ else
+ {
+ lab= lex->spcont->push_label($1.str,
+ lex->sphead->instructions());
+ lab->type= SP_LAB_ITER;
+ }
+ }
+ sp_unlabeled_control sp_opt_label
+ {
+ LEX *lex= Lex;
sp_label_t *lab= lex->spcont->pop_label();
- if ($5.str)
- {
- if (my_strcasecmp(system_charset_info, $5.str, lab->name) != 0)
- {
- my_error(ER_SP_LABEL_MISMATCH, MYF(0), $5.str);
- MYSQL_YYABORT;
- }
- }
- lex->sphead->backpatch(lab);
- }
- ;
+ if ($5.str)
+ {
+ if (my_strcasecmp(system_charset_info, $5.str, lab->name) != 0)
+ {
+ my_error(ER_SP_LABEL_MISMATCH, MYF(0), $5.str);
+ MYSQL_YYABORT;
+ }
+ }
+ lex->sphead->backpatch(lab);
+ }
+ ;
sp_opt_label:
- /* Empty */ { $$= null_lex_str; }
+ /* Empty */ { $$= null_lex_str; }
| label_ident { $$= $1; }
- ;
+ ;
sp_labeled_block:
label_ident ':'
@@ -2724,96 +3155,89 @@ sp_unlabeled_block:
;
sp_block_content:
- BEGIN_SYM
- { /* QQ This is just a dummy for grouping declarations and statements
- together. No [[NOT] ATOMIC] yet, and we need to figure out how
- make it coexist with the existing BEGIN COMMIT/ROLLBACK. */
- LEX *lex= Lex;
- lex->spcont= lex->spcont->push_context(LABEL_DEFAULT_SCOPE);
- }
- sp_decls
- sp_proc_stmts
- END
- {
- LEX *lex= Lex;
- sp_head *sp= lex->sphead;
- sp_pcontext *ctx= lex->spcont;
-
- sp->backpatch(ctx->last_label()); /* We always have a label */
- if ($3.hndlrs)
- {
- sp_instr_hpop *hpop= new sp_instr_hpop(sp->instructions(), ctx,
- $3.hndlrs);
- if (hpop == NULL)
+ BEGIN_SYM
+ { /* QQ This is just a dummy for grouping declarations and statements
+ together. No [[NOT] ATOMIC] yet, and we need to figure out how
+ make it coexist with the existing BEGIN COMMIT/ROLLBACK. */
+ LEX *lex= Lex;
+ lex->spcont= lex->spcont->push_context(LABEL_DEFAULT_SCOPE);
+ }
+ sp_decls
+ sp_proc_stmts
+ END
+ {
+ LEX *lex= Lex;
+ sp_head *sp= lex->sphead;
+ sp_pcontext *ctx= lex->spcont;
+ sp_instr *i;
+
+ sp->backpatch(ctx->last_label()); /* We always have a label */
+ if ($3.hndlrs)
+ {
+ i= new sp_instr_hpop(sp->instructions(), ctx, $3.hndlrs);
+ if (i == NULL)
MYSQL_YYABORT;
- sp->add_instr(hpop);
+ sp->add_instr(i);
}
- if ($3.curs)
+ if ($3.curs)
{
- sp_instr_cpop *cpop= new sp_instr_cpop(sp->instructions(), ctx,
- $3.curs);
- if (cpop == NULL)
+ i= new sp_instr_cpop(sp->instructions(), ctx, $3.curs);
+ if (i == NULL)
MYSQL_YYABORT;
- sp->add_instr(cpop);
+ sp->add_instr(i);
}
- lex->spcont= ctx->pop_context();
- }
+ lex->spcont= ctx->pop_context();
+ }
;
sp_unlabeled_control:
- LOOP_SYM
- sp_proc_stmts1 END LOOP_SYM
- {
- LEX *lex= Lex;
- uint ip= lex->sphead->instructions();
- sp_label_t *lab= lex->spcont->last_label(); /* Jumping back */
- sp_instr_jump *i = new sp_instr_jump(ip, lex->spcont, lab->ip);
- if (i == NULL)
- MYSQL_YYABORT;
- lex->sphead->add_instr(i);
- }
- | WHILE_SYM
+ LOOP_SYM
+ sp_proc_stmts1 END LOOP_SYM
{
- if (Lex->sphead->reset_lex(YYTHD))
+ LEX *lex= Lex;
+ uint ip= lex->sphead->instructions();
+ sp_label_t *lab= lex->spcont->last_label(); /* Jumping back */
+ sp_instr_jump *i = new sp_instr_jump(ip, lex->spcont, lab->ip);
+ if (i == NULL)
MYSQL_YYABORT;
+ lex->sphead->add_instr(i);
}
+ | WHILE_SYM
+ { Lex->sphead->reset_lex(YYTHD); }
expr DO_SYM
- {
- LEX *lex= Lex;
- sp_head *sp= lex->sphead;
- uint ip= sp->instructions();
- sp_instr_jump_if_not *i = new sp_instr_jump_if_not(ip, lex->spcont,
- $3, lex);
+ {
+ LEX *lex= Lex;
+ sp_head *sp= lex->sphead;
+ uint ip= sp->instructions();
+ sp_instr_jump_if_not *i = new sp_instr_jump_if_not(ip, lex->spcont,
+ $3, lex);
if (i == NULL)
MYSQL_YYABORT;
- /* Jumping forward */
- sp->push_backpatch(i, lex->spcont->last_label());
+ /* Jumping forward */
+ sp->push_backpatch(i, lex->spcont->last_label());
sp->new_cont_backpatch(i);
sp->add_instr(i);
sp->restore_lex(YYTHD);
- }
- sp_proc_stmts1 END WHILE_SYM
- {
- LEX *lex= Lex;
- uint ip= lex->sphead->instructions();
- sp_label_t *lab= lex->spcont->last_label(); /* Jumping back */
- sp_instr_jump *i = new sp_instr_jump(ip, lex->spcont, lab->ip);
+ }
+ sp_proc_stmts1 END WHILE_SYM
+ {
+ LEX *lex= Lex;
+ uint ip= lex->sphead->instructions();
+ sp_label_t *lab= lex->spcont->last_label(); /* Jumping back */
+ sp_instr_jump *i = new sp_instr_jump(ip, lex->spcont, lab->ip);
if (i == NULL)
MYSQL_YYABORT;
- lex->sphead->add_instr(i);
+ lex->sphead->add_instr(i);
lex->sphead->do_cont_backpatch();
- }
- | REPEAT_SYM sp_proc_stmts1 UNTIL_SYM
- {
- if (Lex->sphead->reset_lex(YYTHD))
- MYSQL_YYABORT;
}
+ | REPEAT_SYM sp_proc_stmts1 UNTIL_SYM
+ { Lex->sphead->reset_lex(YYTHD); }
expr END REPEAT_SYM
- {
- LEX *lex= Lex;
- uint ip= lex->sphead->instructions();
- sp_label_t *lab= lex->spcont->last_label(); /* Jumping back */
- sp_instr_jump_if_not *i = new sp_instr_jump_if_not(ip, lex->spcont,
+ {
+ LEX *lex= Lex;
+ uint ip= lex->sphead->instructions();
+ sp_label_t *lab= lex->spcont->last_label(); /* Jumping back */
+ sp_instr_jump_if_not *i = new sp_instr_jump_if_not(ip, lex->spcont,
$5, lab->ip,
lex);
if (i == NULL)
@@ -2822,139 +3246,1186 @@ sp_unlabeled_control:
lex->sphead->restore_lex(YYTHD);
/* We can shortcut the cont_backpatch here */
i->m_cont_dest= ip+1;
- }
- ;
+ }
+ ;
trg_action_time:
- BEFORE_SYM
+ BEFORE_SYM
{ Lex->trg_chistics.action_time= TRG_ACTION_BEFORE; }
- | AFTER_SYM
+ | AFTER_SYM
{ Lex->trg_chistics.action_time= TRG_ACTION_AFTER; }
;
trg_event:
- INSERT
+ INSERT
{ Lex->trg_chistics.event= TRG_EVENT_INSERT; }
| UPDATE_SYM
{ Lex->trg_chistics.event= TRG_EVENT_UPDATE; }
| DELETE_SYM
{ Lex->trg_chistics.event= TRG_EVENT_DELETE; }
;
+/*
+ This part of the parser contains common code for all TABLESPACE
+ commands.
+ CREATE TABLESPACE name ...
+ ALTER TABLESPACE name CHANGE DATAFILE ...
+ ALTER TABLESPACE name ADD DATAFILE ...
+ ALTER TABLESPACE name access_mode
+ CREATE LOGFILE GROUP_SYM name ...
+ ALTER LOGFILE GROUP_SYM name ADD UNDOFILE ..
+ ALTER LOGFILE GROUP_SYM name ADD REDOFILE ..
+ DROP TABLESPACE name
+ DROP LOGFILE GROUP_SYM name
+*/
+change_tablespace_access:
+ tablespace_name
+ ts_access_mode
+ ;
+
+change_tablespace_info:
+ tablespace_name
+ CHANGE ts_datafile
+ change_ts_option_list
+ ;
+
+tablespace_info:
+ tablespace_name
+ ADD ts_datafile
+ opt_logfile_group_name
+ tablespace_option_list
+ ;
+
+opt_logfile_group_name:
+ /* empty */ {}
+ | USE_SYM LOGFILE_SYM GROUP_SYM ident
+ {
+ LEX *lex= Lex;
+ lex->alter_tablespace_info->logfile_group_name= $4.str;
+ }
+ ;
+
+alter_tablespace_info:
+ tablespace_name
+ ADD ts_datafile
+ alter_tablespace_option_list
+ {
+ Lex->alter_tablespace_info->ts_alter_tablespace_type= ALTER_TABLESPACE_ADD_FILE;
+ }
+ | tablespace_name
+ DROP ts_datafile
+ alter_tablespace_option_list
+ {
+ Lex->alter_tablespace_info->ts_alter_tablespace_type= ALTER_TABLESPACE_DROP_FILE;
+ }
+ ;
+
+logfile_group_info:
+ logfile_group_name
+ add_log_file
+ logfile_group_option_list
+ ;
+
+alter_logfile_group_info:
+ logfile_group_name
+ add_log_file
+ alter_logfile_group_option_list
+ ;
+
+add_log_file:
+ ADD lg_undofile
+ | ADD lg_redofile
+ ;
+
+change_ts_option_list:
+ /* empty */ {}
+ change_ts_options
+ ;
+
+change_ts_options:
+ change_ts_option
+ | change_ts_options change_ts_option
+ | change_ts_options ',' change_ts_option
+ ;
+
+change_ts_option:
+ opt_ts_initial_size
+ | opt_ts_autoextend_size
+ | opt_ts_max_size
+ ;
+
+tablespace_option_list:
+ tablespace_options
+ ;
+
+tablespace_options:
+ tablespace_option
+ | tablespace_options tablespace_option
+ | tablespace_options ',' tablespace_option
+ ;
+
+tablespace_option:
+ opt_ts_initial_size
+ | opt_ts_autoextend_size
+ | opt_ts_max_size
+ | opt_ts_extent_size
+ | opt_ts_nodegroup
+ | opt_ts_engine
+ | ts_wait
+ | opt_ts_comment
+ ;
+
+alter_tablespace_option_list:
+ alter_tablespace_options
+ ;
+
+alter_tablespace_options:
+ alter_tablespace_option
+ | alter_tablespace_options alter_tablespace_option
+ | alter_tablespace_options ',' alter_tablespace_option
+ ;
+
+alter_tablespace_option:
+ opt_ts_initial_size
+ | opt_ts_autoextend_size
+ | opt_ts_max_size
+ | opt_ts_engine
+ | ts_wait
+ ;
+
+logfile_group_option_list:
+ logfile_group_options
+ ;
+
+logfile_group_options:
+ logfile_group_option
+ | logfile_group_options logfile_group_option
+ | logfile_group_options ',' logfile_group_option
+ ;
+
+logfile_group_option:
+ opt_ts_initial_size
+ | opt_ts_undo_buffer_size
+ | opt_ts_redo_buffer_size
+ | opt_ts_nodegroup
+ | opt_ts_engine
+ | ts_wait
+ | opt_ts_comment
+ ;
+
+alter_logfile_group_option_list:
+ alter_logfile_group_options
+ ;
+
+alter_logfile_group_options:
+ alter_logfile_group_option
+ | alter_logfile_group_options alter_logfile_group_option
+ | alter_logfile_group_options ',' alter_logfile_group_option
+ ;
+
+alter_logfile_group_option:
+ opt_ts_initial_size
+ | opt_ts_engine
+ | ts_wait
+ ;
+
+
+ts_datafile:
+ DATAFILE_SYM TEXT_STRING_sys
+ {
+ LEX *lex= Lex;
+ lex->alter_tablespace_info->data_file_name= $2.str;
+ }
+ ;
+
+lg_undofile:
+ UNDOFILE_SYM TEXT_STRING_sys
+ {
+ LEX *lex= Lex;
+ lex->alter_tablespace_info->undo_file_name= $2.str;
+ }
+ ;
+
+lg_redofile:
+ REDOFILE_SYM TEXT_STRING_sys
+ {
+ LEX *lex= Lex;
+ lex->alter_tablespace_info->redo_file_name= $2.str;
+ }
+ ;
+
+tablespace_name:
+ ident
+ {
+ LEX *lex= Lex;
+ lex->alter_tablespace_info= new st_alter_tablespace();
+ if (lex->alter_tablespace_info == NULL)
+ MYSQL_YYABORT;
+ lex->alter_tablespace_info->tablespace_name= $1.str;
+ lex->sql_command= SQLCOM_ALTER_TABLESPACE;
+ }
+ ;
+
+logfile_group_name:
+ ident
+ {
+ LEX *lex= Lex;
+ lex->alter_tablespace_info= new st_alter_tablespace();
+ if (lex->alter_tablespace_info == NULL)
+ MYSQL_YYABORT;
+ lex->alter_tablespace_info->logfile_group_name= $1.str;
+ lex->sql_command= SQLCOM_ALTER_TABLESPACE;
+ }
+ ;
+
+ts_access_mode:
+ READ_ONLY_SYM
+ {
+ LEX *lex= Lex;
+ lex->alter_tablespace_info->ts_access_mode= TS_READ_ONLY;
+ }
+ | READ_WRITE_SYM
+ {
+ LEX *lex= Lex;
+ lex->alter_tablespace_info->ts_access_mode= TS_READ_WRITE;
+ }
+ | NOT_SYM ACCESSIBLE_SYM
+ {
+ LEX *lex= Lex;
+ lex->alter_tablespace_info->ts_access_mode= TS_NOT_ACCESSIBLE;
+ }
+ ;
+
+opt_ts_initial_size:
+ INITIAL_SIZE_SYM opt_equal size_number
+ {
+ LEX *lex= Lex;
+ lex->alter_tablespace_info->initial_size= $3;
+ }
+ ;
+
+opt_ts_autoextend_size:
+ AUTOEXTEND_SIZE_SYM opt_equal size_number
+ {
+ LEX *lex= Lex;
+ lex->alter_tablespace_info->autoextend_size= $3;
+ }
+ ;
+
+opt_ts_max_size:
+ MAX_SIZE_SYM opt_equal size_number
+ {
+ LEX *lex= Lex;
+ lex->alter_tablespace_info->max_size= $3;
+ }
+ ;
+
+opt_ts_extent_size:
+ EXTENT_SIZE_SYM opt_equal size_number
+ {
+ LEX *lex= Lex;
+ lex->alter_tablespace_info->extent_size= $3;
+ }
+ ;
+
+opt_ts_undo_buffer_size:
+ UNDO_BUFFER_SIZE_SYM opt_equal size_number
+ {
+ LEX *lex= Lex;
+ lex->alter_tablespace_info->undo_buffer_size= $3;
+ }
+ ;
+
+opt_ts_redo_buffer_size:
+ REDO_BUFFER_SIZE_SYM opt_equal size_number
+ {
+ LEX *lex= Lex;
+ lex->alter_tablespace_info->redo_buffer_size= $3;
+ }
+ ;
+
+opt_ts_nodegroup:
+ NODEGROUP_SYM opt_equal real_ulong_num
+ {
+ LEX *lex= Lex;
+ if (lex->alter_tablespace_info->nodegroup_id != UNDEF_NODEGROUP)
+ {
+ my_error(ER_FILEGROUP_OPTION_ONLY_ONCE,MYF(0),"NODEGROUP");
+ MYSQL_YYABORT;
+ }
+ lex->alter_tablespace_info->nodegroup_id= $3;
+ }
+ ;
+
+opt_ts_comment:
+ COMMENT_SYM opt_equal TEXT_STRING_sys
+ {
+ LEX *lex= Lex;
+ if (lex->alter_tablespace_info->ts_comment != NULL)
+ {
+ my_error(ER_FILEGROUP_OPTION_ONLY_ONCE,MYF(0),"COMMENT");
+ MYSQL_YYABORT;
+ }
+ lex->alter_tablespace_info->ts_comment= $3.str;
+ }
+ ;
+
+opt_ts_engine:
+ opt_storage ENGINE_SYM opt_equal storage_engines
+ {
+ LEX *lex= Lex;
+ if (lex->alter_tablespace_info->storage_engine != NULL)
+ {
+ my_error(ER_FILEGROUP_OPTION_ONLY_ONCE,MYF(0),
+ "STORAGE ENGINE");
+ MYSQL_YYABORT;
+ }
+ lex->alter_tablespace_info->storage_engine= $4;
+ }
+ ;
+
+opt_ts_wait:
+ /* empty */
+ | ts_wait
+ ;
+
+ts_wait:
+ WAIT_SYM
+ {
+ LEX *lex= Lex;
+ lex->alter_tablespace_info->wait_until_completed= TRUE;
+ }
+ | NO_WAIT_SYM
+ {
+ LEX *lex= Lex;
+ if (!(lex->alter_tablespace_info->wait_until_completed))
+ {
+ my_error(ER_FILEGROUP_OPTION_ONLY_ONCE,MYF(0),"NO_WAIT");
+ MYSQL_YYABORT;
+ }
+ lex->alter_tablespace_info->wait_until_completed= FALSE;
+ }
+ ;
+
+size_number:
+ real_ulong_num { $$= $1;}
+ | IDENT
+ {
+ ulonglong number;
+ uint text_shift_number= 0;
+ longlong prefix_number;
+ char *start_ptr= $1.str;
+ uint str_len= $1.length;
+ char *end_ptr= start_ptr + str_len;
+ int error;
+ prefix_number= my_strtoll10(start_ptr, &end_ptr, &error);
+ if ((start_ptr + str_len - 1) == end_ptr)
+ {
+ switch (end_ptr[0])
+ {
+ case 'g':
+ case 'G':
+ text_shift_number+=10;
+ case 'm':
+ case 'M':
+ text_shift_number+=10;
+ case 'k':
+ case 'K':
+ text_shift_number+=10;
+ break;
+ default:
+ {
+ my_error(ER_WRONG_SIZE_NUMBER, MYF(0));
+ MYSQL_YYABORT;
+ }
+ }
+ if (prefix_number >> 31)
+ {
+ my_error(ER_SIZE_OVERFLOW_ERROR, MYF(0));
+ MYSQL_YYABORT;
+ }
+ number= prefix_number << text_shift_number;
+ }
+ else
+ {
+ my_error(ER_WRONG_SIZE_NUMBER, MYF(0));
+ MYSQL_YYABORT;
+ }
+ $$= number;
+ }
+ ;
+
+/*
+ End tablespace part
+*/
create2:
- '(' create2a {}
- | opt_create_table_options create3 {}
+ '(' create2a {}
+ | opt_create_table_options
+ opt_partitioning
+ create3 {}
| LIKE table_ident
{
- Lex->create_info.options|= HA_LEX_CREATE_TABLE_LIKE;
- if (!Lex->select_lex.add_table_to_list(YYTHD, $2, NULL, 0, TL_READ))
+ 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);
+ if (! src_table)
MYSQL_YYABORT;
+ /* CREATE TABLE ... LIKE is not allowed for views. */
+ src_table->required_type= FRMTYPE_TABLE;
}
| '(' LIKE table_ident ')'
{
- Lex->create_info.options|= HA_LEX_CREATE_TABLE_LIKE;
- if (!Lex->select_lex.add_table_to_list(YYTHD, $3, NULL, 0, TL_READ))
+ 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);
+ if (! src_table)
MYSQL_YYABORT;
+ /* CREATE TABLE ... LIKE is not allowed for views. */
+ src_table->required_type= FRMTYPE_TABLE;
}
;
create2a:
- field_list ')' opt_create_table_options create3 {}
- | create_select ')' { Select->set_braces(1);} union_opt {}
+ field_list ')' opt_create_table_options
+ opt_partitioning
+ create3 {}
+ | opt_partitioning
+ create_select ')'
+ { Select->set_braces(1);}
+ union_opt {}
;
create3:
- /* empty */ {}
- | opt_duplicate opt_as create_select
- { Select->set_braces(0);} union_clause {}
- | opt_duplicate opt_as '(' create_select ')'
- { Select->set_braces(1);} union_opt {}
+ /* empty */ {}
+ | opt_duplicate opt_as create_select
+ { Select->set_braces(0);}
+ union_clause {}
+ | opt_duplicate opt_as '(' create_select ')'
+ { Select->set_braces(1);}
+ union_opt {}
+ ;
+
+/*
+ This part of the parser is about handling of the partition information.
+
+ It's first version was written by Mikael Ronström with lots of answers to
+ questions provided by Antony Curtis.
+
+ The partition grammar can be called from three places.
+ 1) CREATE TABLE ... PARTITION ..
+ 2) ALTER TABLE table_name PARTITION ...
+ 3) PARTITION ...
+
+ The first place is called when a new table is created from a MySQL client.
+ The second place is called when a table is altered with the ALTER TABLE
+ command from a MySQL client.
+ The third place is called when opening an frm file and finding partition
+ info in the .frm file. It is necessary to avoid allowing PARTITION to be
+ an allowed entry point for SQL client queries. This is arranged by setting
+ some state variables before arriving here.
+
+ To be able to handle errors we will only set error code in this code
+ and handle the error condition in the function calling the parser. This
+ is necessary to ensure we can also handle errors when calling the parser
+ from the openfrm function.
+*/
+opt_partitioning:
+ /* empty */ {}
+ | partitioning
+ ;
+
+partitioning:
+ PARTITION_SYM
+ {
+#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_FEATURE_DISABLED, MYF(0),
+ "partitioning", "--with-partition");
+ MYSQL_YYABORT;
+ }
+ lex->part_info= new partition_info();
+ if (!lex->part_info)
+ {
+ mem_alloc_error(sizeof(partition_info));
+ MYSQL_YYABORT;
+ }
+ if (lex->sql_command == SQLCOM_ALTER_TABLE)
+ {
+ lex->alter_info.flags|= ALTER_PARTITION;
+ }
+#else
+ my_error(ER_FEATURE_DISABLED, MYF(0),
+ "partitioning", "--with-partition");
+ MYSQL_YYABORT;
+#endif
+
+ }
+ partition
+ ;
+
+partition_entry:
+ PARTITION_SYM
+ {
+ LEX *lex= Lex;
+ if (!lex->part_info)
+ {
+ my_parse_error(ER(ER_PARTITION_ENTRY_ERROR));
+ MYSQL_YYABORT;
+ }
+ /*
+ We enter here when opening the frm file to translate
+ partition info string into part_info data structure.
+ */
+ }
+ partition {}
+ ;
+
+partition:
+ BY part_type_def opt_no_parts opt_sub_part part_defs
+ ;
+
+part_type_def:
+ opt_linear KEY_SYM '(' part_field_list ')'
+ {
+ LEX *lex= Lex;
+ lex->part_info->list_of_part_fields= TRUE;
+ lex->part_info->part_type= HASH_PARTITION;
+ }
+ | opt_linear HASH_SYM
+ { Lex->part_info->part_type= HASH_PARTITION; }
+ part_func {}
+ | RANGE_SYM
+ { Lex->part_info->part_type= RANGE_PARTITION; }
+ part_func {}
+ | LIST_SYM
+ { Lex->part_info->part_type= LIST_PARTITION; }
+ part_func {}
+ ;
+
+opt_linear:
+ /* empty */ {}
+ | LINEAR_SYM
+ { Lex->part_info->linear_hash_ind= TRUE;}
+ ;
+
+part_field_list:
+ /* empty */ {}
+ | part_field_item_list {}
+ ;
+
+part_field_item_list:
+ part_field_item {}
+ | part_field_item_list ',' part_field_item {}
+ ;
+
+part_field_item:
+ ident
+ {
+ if (Lex->part_info->part_field_list.push_back($1.str))
+ {
+ mem_alloc_error(1);
+ MYSQL_YYABORT;
+ }
+ }
+ ;
+
+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;
+ }
+ ;
+
+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;
+ }
+ ;
+
+
+opt_no_parts:
+ /* empty */ {}
+ | PARTITIONS_SYM real_ulong_num
+ {
+ uint no_parts= $2;
+ LEX *lex= Lex;
+ if (no_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;
+ }
+ ;
+
+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
+ '(' sub_part_field_list ')'
+ {
+ LEX *lex= Lex;
+ lex->part_info->subpart_type= HASH_PARTITION;
+ lex->part_info->list_of_subpart_fields= TRUE;
+ }
+ opt_no_subparts {}
+ ;
+
+sub_part_field_list:
+ sub_part_field_item {}
+ | sub_part_field_list ',' sub_part_field_item {}
+ ;
+
+sub_part_field_item:
+ ident
+ {
+ if (Lex->part_info->subpart_field_list.push_back($1.str))
+ {
+ mem_alloc_error(1);
+ MYSQL_YYABORT;
+ }
+ }
+ ;
+
+part_func_expr:
+ bit_expr
+ {
+ LEX *lex= Lex;
+ bool not_corr_func;
+ not_corr_func= !lex->safe_to_cache_query;
+ lex->safe_to_cache_query= 1;
+ if (not_corr_func)
+ {
+ my_parse_error(ER(ER_CONST_EXPR_IN_PARTITION_FUNC_ERROR));
+ MYSQL_YYABORT;
+ }
+ $$=$1;
+ }
+ ;
+
+opt_no_subparts:
+ /* empty */ {}
+ | SUBPARTITIONS_SYM real_ulong_num
+ {
+ uint no_parts= $2;
+ LEX *lex= Lex;
+ if (no_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;
+ }
+ ;
+
+part_defs:
+ /* empty */
+ {}
+ | '(' part_def_list ')'
+ {
+ LEX *lex= Lex;
+ partition_info *part_info= lex->part_info;
+ uint count_curr_parts= part_info->partitions.elements;
+ if (part_info->no_parts != 0)
+ {
+ if (part_info->no_parts !=
+ count_curr_parts)
+ {
+ my_parse_error(ER(ER_PARTITION_WRONG_NO_PART_ERROR));
+ MYSQL_YYABORT;
+ }
+ }
+ else if (count_curr_parts > 0)
+ {
+ part_info->no_parts= count_curr_parts;
+ }
+ part_info->count_curr_subparts= 0;
+ }
+ ;
+
+part_def_list:
+ part_definition {}
+ | part_def_list ',' part_definition {}
+ ;
+
+part_definition:
+ PARTITION_SYM
+ {
+ LEX *lex= Lex;
+ partition_info *part_info= lex->part_info;
+ partition_element *p_elem= new partition_element();
+
+ if (!p_elem || part_info->partitions.push_back(p_elem))
+ {
+ mem_alloc_error(sizeof(partition_element));
+ MYSQL_YYABORT;
+ }
+ p_elem->part_state= PART_NORMAL;
+ 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_name
+ opt_part_values
+ opt_part_options
+ opt_sub_partition
+ {}
;
+part_name:
+ ident
+ {
+ LEX *lex= Lex;
+ partition_info *part_info= lex->part_info;
+ partition_element *p_elem= part_info->curr_part_elem;
+ p_elem->partition_name= $1.str;
+ }
+ ;
+
+opt_part_values:
+ /* empty */
+ {
+ LEX *lex= Lex;
+ if (! lex->is_partition_management())
+ {
+ if (lex->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)
+ {
+ my_error(ER_PARTITION_REQUIRES_VALUES_ERROR, MYF(0),
+ "LIST", "IN");
+ MYSQL_YYABORT;
+ }
+ }
+ else
+ lex->part_info->part_type= HASH_PARTITION;
+ }
+ | VALUES LESS_SYM THAN_SYM part_func_max
+ {
+ LEX *lex= Lex;
+ if (! lex->is_partition_management())
+ {
+ if (Lex->part_info->part_type != RANGE_PARTITION)
+ {
+ my_error(ER_PARTITION_WRONG_VALUES_ERROR, MYF(0),
+ "RANGE", "LESS THAN");
+ MYSQL_YYABORT;
+ }
+ }
+ else
+ lex->part_info->part_type= RANGE_PARTITION;
+ }
+ | VALUES IN_SYM '(' part_list_func ')'
+ {
+ LEX *lex= Lex;
+ if (! lex->is_partition_management())
+ {
+ if (Lex->part_info->part_type != LIST_PARTITION)
+ {
+ my_error(ER_PARTITION_WRONG_VALUES_ERROR, MYF(0),
+ "LIST", "IN");
+ MYSQL_YYABORT;
+ }
+ }
+ else
+ lex->part_info->part_type= LIST_PARTITION;
+ }
+ ;
+
+part_func_max:
+ max_value_sym
+ {
+ LEX *lex= Lex;
+ if (lex->part_info->defined_max_value)
+ {
+ my_parse_error(ER(ER_PARTITION_MAXVALUE_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)
+ {
+ my_parse_error(ER(ER_PARTITION_MAXVALUE_ERROR));
+ MYSQL_YYABORT;
+ }
+ if (Lex->part_info->curr_part_elem->has_null_value)
+ {
+ my_parse_error(ER(ER_NULL_IN_VALUES_LESS_THAN));
+ MYSQL_YYABORT;
+ }
+ }
+ ;
+
+max_value_sym:
+ MAX_VALUE_SYM
+ | '(' MAX_VALUE_SYM ')'
+ ;
+
+part_range_func:
+ '(' part_bit_expr ')'
+ {
+ 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;
+ }
+ ;
+
+part_list_func:
+ part_list_item {}
+ | part_list_func ',' part_list_item {}
+ ;
+
+part_list_item:
+ part_bit_expr
+ {
+ 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))
+ {
+ mem_alloc_error(sizeof(part_elem_value));
+ MYSQL_YYABORT;
+ }
+ }
+ ;
+
+part_bit_expr:
+ bit_expr
+ {
+ 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)
+ {
+ mem_alloc_error(sizeof(part_elem_value));
+ MYSQL_YYABORT;
+ }
+ if (part_expr->walk(&Item::check_partition_func_processor, 0,
+ NULL))
+ {
+ my_error(ER_PARTITION_FUNCTION_IS_NOT_ALLOWED, MYF(0));
+ 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))
+ {
+ 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))
+ {
+ 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;
+ }
+ else if (part_expr->result_type() != INT_RESULT)
+ {
+ 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)
+ {
+ my_parse_error(ER(ER_PARTITION_WRONG_NO_SUBPART_ERROR));
+ MYSQL_YYABORT;
+ }
+ }
+ | '(' sub_part_list ')'
+ {
+ LEX *lex= Lex;
+ partition_info *part_info= lex->part_info;
+ if (part_info->no_subparts != 0)
+ {
+ if (part_info->no_subparts !=
+ part_info->count_curr_subparts)
+ {
+ my_parse_error(ER(ER_PARTITION_WRONG_NO_SUBPART_ERROR));
+ MYSQL_YYABORT;
+ }
+ }
+ else if (part_info->count_curr_subparts > 0)
+ {
+ if (part_info->partitions.elements > 1)
+ {
+ my_parse_error(ER(ER_PARTITION_WRONG_NO_SUBPART_ERROR));
+ MYSQL_YYABORT;
+ }
+ part_info->no_subparts= part_info->count_curr_subparts;
+ }
+ part_info->count_curr_subparts= 0;
+ }
+ ;
+
+sub_part_list:
+ sub_part_definition {}
+ | sub_part_list ',' sub_part_definition {}
+ ;
+
+sub_part_definition:
+ SUBPARTITION_SYM
+ {
+ LEX *lex= Lex;
+ 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 (!sub_p_elem ||
+ curr_part->subpartitions.push_back(sub_p_elem))
+ {
+ mem_alloc_error(sizeof(partition_element));
+ MYSQL_YYABORT;
+ }
+ part_info->curr_part_elem= sub_p_elem;
+ part_info->use_default_subpartitions= FALSE;
+ part_info->use_default_no_subpartitions= FALSE;
+ part_info->count_curr_subparts++;
+ }
+ sub_name opt_part_options {}
+ ;
+
+sub_name:
+ ident_or_text
+ { Lex->part_info->curr_part_elem->partition_name= $1.str; }
+ ;
+
+opt_part_options:
+ /* empty */ {}
+ | opt_part_option_list {}
+ ;
+
+opt_part_option_list:
+ opt_part_option_list opt_part_option {}
+ | opt_part_option {}
+ ;
+
+opt_part_option:
+ TABLESPACE opt_equal ident_or_text
+ { Lex->part_info->curr_part_elem->tablespace_name= $3.str; }
+ | opt_storage ENGINE_SYM opt_equal storage_engines
+ {
+ LEX *lex= Lex;
+ lex->part_info->curr_part_elem->engine_type= $4;
+ lex->part_info->default_engine_type= $4;
+ }
+ | NODEGROUP_SYM opt_equal real_ulong_num
+ { Lex->part_info->curr_part_elem->nodegroup_id= (uint16) $3; }
+ | MAX_ROWS opt_equal real_ulonglong_num
+ { Lex->part_info->curr_part_elem->part_max_rows= (ha_rows) $3; }
+ | MIN_ROWS opt_equal real_ulonglong_num
+ { Lex->part_info->curr_part_elem->part_min_rows= (ha_rows) $3; }
+ | DATA_SYM DIRECTORY_SYM opt_equal TEXT_STRING_sys
+ { Lex->part_info->curr_part_elem->data_file_name= $4.str; }
+ | INDEX_SYM DIRECTORY_SYM opt_equal TEXT_STRING_sys
+ { Lex->part_info->curr_part_elem->index_file_name= $4.str; }
+ | COMMENT_SYM opt_equal TEXT_STRING_sys
+ { Lex->part_info->curr_part_elem->part_comment= $3.str; }
+ ;
+
+/*
+ End of partition parser part
+*/
+
create_select:
SELECT_SYM
{
- LEX *lex=Lex;
- lex->lock_option= using_update_log ? TL_READ_NO_INSERT : TL_READ;
- if (lex->sql_command == SQLCOM_INSERT)
- lex->sql_command= SQLCOM_INSERT_SELECT;
- else if (lex->sql_command == SQLCOM_REPLACE)
- lex->sql_command= SQLCOM_REPLACE_SELECT;
- /*
+ 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)
+ lex->sql_command= SQLCOM_REPLACE_SELECT;
+ /*
The following work only with the local list, the global list
is created correctly in this case
- */
- lex->current_select->table_list.save_and_clear(&lex->save_list);
- mysql_init_select(lex);
- lex->current_select->parsing_place= SELECT_LIST;
+ */
+ lex->current_select->table_list.save_and_clear(&lex->save_list);
+ mysql_init_select(lex);
+ lex->current_select->parsing_place= SELECT_LIST;
}
select_options select_item_list
- {
- Select->parsing_place= NO_MATTER;
- }
- opt_select_from
- {
- /*
+ {
+ Select->parsing_place= NO_MATTER;
+ }
+ opt_select_from
+ {
+ /*
The following work only with the local list, the global list
is created correctly in this case
- */
- Lex->current_select->table_list.push_front(&Lex->save_list);
- }
+ */
+ Lex->current_select->table_list.push_front(&Lex->save_list);
+ }
;
opt_as:
- /* empty */ {}
- | AS {};
+ /* empty */ {}
+ | AS {}
+ ;
opt_create_database_options:
- /* empty */ {}
- | create_database_options {};
+ /* empty */ {}
+ | create_database_options {}
+ ;
create_database_options:
- create_database_option {}
- | create_database_options create_database_option {};
+ create_database_option {}
+ | create_database_options create_database_option {}
+ ;
create_database_option:
- default_collation {}
- | default_charset {};
+ default_collation {}
+ | default_charset {}
+ ;
opt_table_options:
- /* empty */ { $$= 0; }
- | table_options { $$= $1;};
+ /* empty */ { $$= 0; }
+ | table_options { $$= $1;}
+ ;
table_options:
- table_option { $$=$1; }
- | table_option table_options { $$= $1 | $2; };
+ table_option { $$=$1; }
+ | table_option table_options { $$= $1 | $2; }
+ ;
table_option:
- TEMPORARY { $$=HA_LEX_CREATE_TMP_TABLE; };
+ TEMPORARY { $$=HA_LEX_CREATE_TMP_TABLE; }
+ ;
opt_if_not_exists:
- /* empty */ { $$= 0; }
- | IF not EXISTS { $$=HA_LEX_CREATE_IF_NOT_EXISTS; };
+ /* empty */ { $$= 0; }
+ | IF not EXISTS { $$=HA_LEX_CREATE_IF_NOT_EXISTS; }
+ ;
opt_create_table_options:
- /* empty */
- | create_table_options;
+ /* empty */
+ | create_table_options
+ ;
create_table_options_space_separated:
- create_table_option
- | create_table_option create_table_options_space_separated;
+ create_table_option
+ | create_table_option create_table_options_space_separated
+ ;
create_table_options:
- create_table_option
- | create_table_option create_table_options
- | create_table_option ',' create_table_options;
+ create_table_option
+ | create_table_option create_table_options
+ | create_table_option ',' create_table_options
+ ;
create_table_option:
- ENGINE_SYM opt_equal storage_engines { 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("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; Lex->create_info.used_fields|= HA_CREATE_USED_MAX_ROWS;}
- | MIN_ROWS opt_equal ulonglong_num { Lex->create_info.min_rows= $3; Lex->create_info.used_fields|= HA_CREATE_USED_MIN_ROWS;}
- | AVG_ROW_LENGTH opt_equal ulong_num { Lex->create_info.avg_row_length=$3; Lex->create_info.used_fields|= HA_CREATE_USED_AVG_ROW_LENGTH;}
- | PASSWORD opt_equal TEXT_STRING_sys { Lex->create_info.password=$3.str; Lex->create_info.used_fields|= HA_CREATE_USED_PASSWORD; }
- | COMMENT_SYM opt_equal TEXT_STRING_sys { Lex->create_info.comment=$3; Lex->create_info.used_fields|= HA_CREATE_USED_COMMENT; }
- | AUTO_INC opt_equal ulonglong_num { Lex->create_info.auto_increment_value=$3; Lex->create_info.used_fields|= HA_CREATE_USED_AUTO;}
+ ENGINE_SYM opt_equal storage_engines
+ {
+ 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, "5.2", "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;
+ Lex->create_info.used_fields|= HA_CREATE_USED_MAX_ROWS;
+ }
+ | MIN_ROWS opt_equal ulonglong_num
+ {
+ Lex->create_info.min_rows= $3;
+ Lex->create_info.used_fields|= HA_CREATE_USED_MIN_ROWS;
+ }
+ | AVG_ROW_LENGTH opt_equal ulong_num
+ {
+ Lex->create_info.avg_row_length=$3;
+ Lex->create_info.used_fields|= HA_CREATE_USED_AVG_ROW_LENGTH;
+ }
+ | PASSWORD opt_equal TEXT_STRING_sys
+ {
+ Lex->create_info.password=$3.str;
+ Lex->create_info.used_fields|= HA_CREATE_USED_PASSWORD;
+ }
+ | COMMENT_SYM opt_equal TEXT_STRING_sys
+ {
+ Lex->create_info.comment=$3;
+ Lex->create_info.used_fields|= HA_CREATE_USED_COMMENT;
+ }
+ | AUTO_INC opt_equal ulonglong_num
+ {
+ Lex->create_info.auto_increment_value=$3;
+ Lex->create_info.used_fields|= HA_CREATE_USED_AUTO;
+ }
| PACK_KEYS_SYM opt_equal ulong_num
{
switch($3) {
@@ -2976,162 +4447,239 @@ create_table_option:
~(HA_OPTION_PACK_KEYS | HA_OPTION_NO_PACK_KEYS);
Lex->create_info.used_fields|= HA_CREATE_USED_PACK_KEYS;
}
- | CHECKSUM_SYM opt_equal ulong_num { Lex->create_info.table_options|= $3 ? HA_OPTION_CHECKSUM : HA_OPTION_NO_CHECKSUM; Lex->create_info.used_fields|= HA_CREATE_USED_CHECKSUM; }
- | DELAY_KEY_WRITE_SYM opt_equal ulong_num { Lex->create_info.table_options|= $3 ? HA_OPTION_DELAY_KEY_WRITE : HA_OPTION_NO_DELAY_KEY_WRITE; Lex->create_info.used_fields|= HA_CREATE_USED_DELAY_KEY_WRITE; }
- | ROW_FORMAT_SYM opt_equal row_types { Lex->create_info.row_type= $3; Lex->create_info.used_fields|= HA_CREATE_USED_ROW_FORMAT; }
- | RAID_TYPE opt_equal raid_types
- {
- my_error(ER_WARN_DEPRECATED_SYNTAX, MYF(0), "RAID_TYPE", "PARTITION");
- MYSQL_YYABORT;
- }
- | RAID_CHUNKS opt_equal ulong_num
- {
- my_error(ER_WARN_DEPRECATED_SYNTAX, MYF(0), "RAID_CHUNKS", "PARTITION");
- MYSQL_YYABORT;
- }
- | RAID_CHUNKSIZE opt_equal ulong_num
- {
- my_error(ER_WARN_DEPRECATED_SYNTAX, MYF(0), "RAID_CHUNKSIZE", "PARTITION");
- MYSQL_YYABORT;
- }
- | UNION_SYM opt_equal '(' opt_table_list ')'
- {
- /* Move the union list to the merge_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=
- (byte*) (table_list->next_local);
- lex->select_lex.table_list.elements=1;
- lex->select_lex.table_list.next=
- (byte**) &(table_list->next_local);
- table_list->next_local= 0;
- lex->create_info.used_fields|= HA_CREATE_USED_UNION;
- }
- | default_charset
- | default_collation
- | INSERT_METHOD opt_equal merge_insert_types { Lex->create_info.merge_insert_method= $3; Lex->create_info.used_fields|= HA_CREATE_USED_INSERT_METHOD;}
- | DATA_SYM DIRECTORY_SYM opt_equal TEXT_STRING_sys { Lex->create_info.data_file_name= $4.str; Lex->create_info.used_fields|= HA_CREATE_USED_DATADIR; }
- | INDEX_SYM DIRECTORY_SYM opt_equal TEXT_STRING_sys { Lex->create_info.index_file_name= $4.str; Lex->create_info.used_fields|= HA_CREATE_USED_INDEXDIR; }
- | CONNECTION_SYM opt_equal TEXT_STRING_sys { Lex->create_info.connect_string.str= $3.str; Lex->create_info.connect_string.length= $3.length; Lex->create_info.used_fields|= HA_CREATE_USED_CONNECTION; }
+ | CHECKSUM_SYM opt_equal ulong_num
+ {
+ Lex->create_info.table_options|= $3 ? HA_OPTION_CHECKSUM : HA_OPTION_NO_CHECKSUM;
+ Lex->create_info.used_fields|= HA_CREATE_USED_CHECKSUM;
+ }
+ | TABLE_CHECKSUM_SYM opt_equal ulong_num
+ {
+ Lex->create_info.table_options|= $3 ? HA_OPTION_CHECKSUM : HA_OPTION_NO_CHECKSUM;
+ Lex->create_info.used_fields|= HA_CREATE_USED_CHECKSUM;
+ }
+ | PAGE_CHECKSUM_SYM opt_equal choice
+ {
+ Lex->create_info.used_fields|= HA_CREATE_USED_PAGE_CHECKSUM;
+ Lex->create_info.page_checksum= $3;
+ }
+ | DELAY_KEY_WRITE_SYM opt_equal ulong_num
+ {
+ Lex->create_info.table_options|= $3 ? HA_OPTION_DELAY_KEY_WRITE : HA_OPTION_NO_DELAY_KEY_WRITE;
+ Lex->create_info.used_fields|= HA_CREATE_USED_DELAY_KEY_WRITE;
+ }
+ | ROW_FORMAT_SYM opt_equal row_types
+ {
+ Lex->create_info.row_type= $3;
+ Lex->create_info.used_fields|= HA_CREATE_USED_ROW_FORMAT;
+ }
+ | UNION_SYM opt_equal '(' opt_table_list ')'
+ {
+ /* Move the union list to the merge_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=
+ (uchar*) (table_list->next_local);
+ lex->select_lex.table_list.elements=1;
+ lex->select_lex.table_list.next=
+ (uchar**) &(table_list->next_local);
+ table_list->next_local= 0;
+ lex->create_info.used_fields|= HA_CREATE_USED_UNION;
+ }
+ | default_charset
+ | default_collation
+ | INSERT_METHOD opt_equal merge_insert_types
+ {
+ Lex->create_info.merge_insert_method= $3;
+ Lex->create_info.used_fields|= HA_CREATE_USED_INSERT_METHOD;
+ }
+ | DATA_SYM DIRECTORY_SYM opt_equal TEXT_STRING_sys
+ {
+ Lex->create_info.data_file_name= $4.str;
+ Lex->create_info.used_fields|= HA_CREATE_USED_DATADIR;
+ }
+ | INDEX_SYM DIRECTORY_SYM opt_equal TEXT_STRING_sys
+ {
+ Lex->create_info.index_file_name= $4.str;
+ Lex->create_info.used_fields|= HA_CREATE_USED_INDEXDIR;
+ }
+ | TABLESPACE ident
+ {Lex->create_info.tablespace= $2.str;}
+ | STORAGE_SYM DISK_SYM
+ {Lex->create_info.storage_media= HA_SM_DISK;}
+ | STORAGE_SYM MEMORY_SYM
+ {Lex->create_info.storage_media= HA_SM_MEMORY;}
+ | CONNECTION_SYM opt_equal TEXT_STRING_sys
+ {
+ Lex->create_info.connect_string.str= $3.str;
+ Lex->create_info.connect_string.length= $3.length;
+ Lex->create_info.used_fields|= HA_CREATE_USED_CONNECTION;
+ }
+ | KEY_BLOCK_SIZE opt_equal ulong_num
+ {
+ Lex->create_info.used_fields|= HA_CREATE_USED_KEY_BLOCK_SIZE;
+ Lex->create_info.key_block_size= $3;
+ }
+ | TRANSACTIONAL_SYM opt_equal choice
+ {
+ Lex->create_info.used_fields|= HA_CREATE_USED_TRANSACTIONAL;
+ Lex->create_info.transactional= $3;
+ }
;
default_charset:
- opt_default charset opt_equal charset_name_or_default
- {
- HA_CREATE_INFO *cinfo= &Lex->create_info;
- if ((cinfo->used_fields & HA_CREATE_USED_DEFAULT_CHARSET) &&
- cinfo->default_table_charset && $4 &&
- !my_charset_same(cinfo->default_table_charset,$4))
- {
- my_error(ER_CONFLICTING_DECLARATIONS, MYF(0),
- "CHARACTER SET ", cinfo->default_table_charset->csname,
- "CHARACTER SET ", $4->csname);
- MYSQL_YYABORT;
- }
- Lex->create_info.default_table_charset= $4;
- Lex->create_info.used_fields|= HA_CREATE_USED_DEFAULT_CHARSET;
- };
-
-default_collation:
- opt_default COLLATE_SYM opt_equal collation_name_or_default
- {
- HA_CREATE_INFO *cinfo= &Lex->create_info;
- if ((cinfo->used_fields & HA_CREATE_USED_DEFAULT_CHARSET) &&
- cinfo->default_table_charset && $4 &&
- !my_charset_same(cinfo->default_table_charset,$4))
+ opt_default charset opt_equal charset_name_or_default
+ {
+ HA_CREATE_INFO *cinfo= &Lex->create_info;
+ if ((cinfo->used_fields & HA_CREATE_USED_DEFAULT_CHARSET) &&
+ cinfo->default_table_charset && $4 &&
+ !my_charset_same(cinfo->default_table_charset,$4))
{
- my_error(ER_COLLATION_CHARSET_MISMATCH, MYF(0),
- $4->name, cinfo->default_table_charset->csname);
+ my_error(ER_CONFLICTING_DECLARATIONS, MYF(0),
+ "CHARACTER SET ", cinfo->default_table_charset->csname,
+ "CHARACTER SET ", $4->csname);
MYSQL_YYABORT;
}
Lex->create_info.default_table_charset= $4;
Lex->create_info.used_fields|= HA_CREATE_USED_DEFAULT_CHARSET;
- };
+ }
+ ;
+
+default_collation:
+ opt_default COLLATE_SYM opt_equal collation_name_or_default
+ {
+ HA_CREATE_INFO *cinfo= &Lex->create_info;
+ if ((cinfo->used_fields & HA_CREATE_USED_DEFAULT_CHARSET) &&
+ cinfo->default_table_charset && $4 &&
+ !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;
+ }
+ ;
storage_engines:
- ident_or_text
- {
- $$ = ha_resolve_by_name($1.str,$1.length);
- if ($$ == DB_TYPE_UNKNOWN) {
- my_error(ER_UNKNOWN_STORAGE_ENGINE, MYF(0), $1.str);
- MYSQL_YYABORT;
- }
- };
+ ident_or_text
+ {
+ plugin_ref plugin= ha_resolve_by_name(YYTHD, &$1);
+
+ if (plugin)
+ $$= plugin_data(plugin, handlerton*);
+ else
+ {
+ if (YYTHD->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,
+ ER_UNKNOWN_STORAGE_ENGINE,
+ ER(ER_UNKNOWN_STORAGE_ENGINE),
+ $1.str);
+ }
+ }
+ ;
+
+known_storage_engines:
+ ident_or_text
+ {
+ plugin_ref plugin;
+ if ((plugin= ha_resolve_by_name(YYTHD, &$1)))
+ $$= plugin_data(plugin, handlerton*);
+ else
+ {
+ my_error(ER_UNKNOWN_STORAGE_ENGINE, MYF(0), $1.str);
+ MYSQL_YYABORT;
+ }
+ }
+ ;
row_types:
- DEFAULT { $$= ROW_TYPE_DEFAULT; }
- | FIXED_SYM { $$= ROW_TYPE_FIXED; }
- | DYNAMIC_SYM { $$= ROW_TYPE_DYNAMIC; }
- | COMPRESSED_SYM { $$= ROW_TYPE_COMPRESSED; }
- | REDUNDANT_SYM { $$= ROW_TYPE_REDUNDANT; }
- | COMPACT_SYM { $$= ROW_TYPE_COMPACT; };
-
-raid_types:
- RAID_STRIPED_SYM { $$= RAID_TYPE_0; }
- | RAID_0_SYM { $$= RAID_TYPE_0; }
- | ulong_num { $$=$1;};
+ DEFAULT { $$= ROW_TYPE_DEFAULT; }
+ | FIXED_SYM { $$= ROW_TYPE_FIXED; }
+ | DYNAMIC_SYM { $$= ROW_TYPE_DYNAMIC; }
+ | COMPRESSED_SYM { $$= ROW_TYPE_COMPRESSED; }
+ | REDUNDANT_SYM { $$= ROW_TYPE_REDUNDANT; }
+ | COMPACT_SYM { $$= ROW_TYPE_COMPACT; }
+ | PAGE_SYM { $$= ROW_TYPE_PAGE; }
+ ;
merge_insert_types:
- NO_SYM { $$= MERGE_INSERT_DISABLED; }
+ NO_SYM { $$= MERGE_INSERT_DISABLED; }
| FIRST_SYM { $$= MERGE_INSERT_TO_FIRST; }
- | LAST_SYM { $$= MERGE_INSERT_TO_LAST; };
+ | LAST_SYM { $$= MERGE_INSERT_TO_LAST; }
+ ;
opt_select_from:
- opt_limit_clause {}
- | select_from select_lock_type;
+ opt_limit_clause {}
+ | select_from select_lock_type
+ ;
udf_type:
- STRING_SYM {$$ = (int) STRING_RESULT; }
- | REAL {$$ = (int) REAL_RESULT; }
+ STRING_SYM {$$ = (int) STRING_RESULT; }
+ | REAL {$$ = (int) REAL_RESULT; }
| DECIMAL_SYM {$$ = (int) DECIMAL_RESULT; }
- | INT_SYM {$$ = (int) INT_RESULT; };
+ | INT_SYM {$$ = (int) INT_RESULT; }
+ ;
field_list:
- field_list_item
- | field_list ',' field_list_item;
-
+ field_list_item
+ | field_list ',' field_list_item
+ ;
field_list_item:
- column_def
- | key_def
- ;
+ column_def
+ | key_def
+ ;
column_def:
- field_spec opt_check_constraint
- | field_spec references
- {
- Lex->col_list.empty(); /* Alloced by sql_alloc */
- }
- ;
+ field_spec opt_check_constraint
+ | field_spec references
+ {
+ Lex->col_list.empty(); /* Alloced by sql_alloc */
+ }
+ ;
key_def:
- key_type opt_ident key_alg '(' key_list ')' key_alg
- {
- LEX *lex=Lex;
- Key *key= new Key($1, $2, $7 ? $7 : $3, 0, lex->col_list);
+ key_type opt_ident key_alg '(' key_list ')' key_options
+ {
+ LEX *lex=Lex;
+ if ($1 != Key::FULLTEXT && lex->key_create_info.parser_name.str)
+ {
+ my_parse_error(ER(ER_SYNTAX_ERROR));
+ MYSQL_YYABORT;
+ }
+ Key *key= new Key($1, $2, &lex->key_create_info, 0,
+ lex->col_list);
if (key == NULL)
MYSQL_YYABORT;
lex->alter_info.key_list.push_back(key);
-
- lex->col_list.empty(); /* Alloced by sql_alloc */
- }
- | opt_constraint constraint_key_type opt_ident key_alg '(' key_list ')' key_alg
- {
- LEX *lex=Lex;
- const char *key_name= $3 ? $3:$1;
- Key *key= new Key($2, key_name, $4, 0, lex->col_list);
+ lex->col_list.empty(); /* Alloced by sql_alloc */
+ }
+ | opt_constraint constraint_key_type opt_ident key_alg
+ '(' key_list ')' key_options
+ {
+ LEX *lex=Lex;
+ const char *key_name= $3 ? $3 : $1;
+ Key *key= new Key($2, key_name, &lex->key_create_info, 0,
+ lex->col_list);
if (key == NULL)
MYSQL_YYABORT;
lex->alter_info.key_list.push_back(key);
- lex->col_list.empty(); /* Alloced by sql_alloc */
- }
- | opt_constraint FOREIGN KEY_SYM opt_ident '(' key_list ')' references
- {
- LEX *lex=Lex;
- const char *key_name= $4 ? $4 : $1;
- Key *key= new foreign_key(key_name, lex->col_list,
+ lex->col_list.empty(); /* Alloced by sql_alloc */
+ }
+ | 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,
$8,
lex->ref_list,
lex->fk_delete_opt,
@@ -3141,696 +4689,1144 @@ key_def:
MYSQL_YYABORT;
lex->alter_info.key_list.push_back(key);
key= new Key(Key::MULTIPLE, key_name,
- HA_KEY_ALG_UNDEF, 1,
+ &default_key_create_info, 1,
lex->col_list);
if (key == NULL)
MYSQL_YYABORT;
lex->alter_info.key_list.push_back(key);
- lex->col_list.empty(); /* Alloced by sql_alloc */
- }
- | constraint opt_check_constraint
- {
- Lex->col_list.empty(); /* Alloced by sql_alloc */
- }
- | opt_constraint check_constraint
- {
- Lex->col_list.empty(); /* Alloced by sql_alloc */
- }
- ;
+ lex->col_list.empty(); /* Alloced by sql_alloc */
+ /* 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 */
+ }
+ ;
opt_check_constraint:
- /* empty */
- | check_constraint
- ;
+ /* empty */
+ | check_constraint
+ ;
check_constraint:
- CHECK_SYM expr
- ;
+ CHECK_SYM expr
+ ;
opt_constraint:
- /* empty */ { $$=(char*) 0; }
- | constraint { $$= $1; }
- ;
+ /* empty */ { $$=(char*) 0; }
+ | constraint { $$= $1; }
+ ;
constraint:
- CONSTRAINT opt_ident { $$=$2; }
- ;
+ CONSTRAINT opt_ident { $$=$2; }
+ ;
field_spec:
- field_ident
- {
- LEX *lex=Lex;
- lex->length=lex->dec=0; lex->type=0;
- lex->default_value= lex->on_update_value= 0;
- lex->comment=null_lex_str;
- lex->charset=NULL;
- }
- type opt_attribute
- {
- LEX *lex=Lex;
- if (add_field_to_list(lex->thd, $1.str,
- (enum enum_field_types) $3,
- lex->length,lex->dec,lex->type,
- lex->default_value, lex->on_update_value,
- &lex->comment,
- lex->change,&lex->interval_list,lex->charset,
- lex->uint_geom_type))
- MYSQL_YYABORT;
- };
+ field_ident
+ {
+ LEX *lex=Lex;
+ lex->length=lex->dec=0;
+ lex->type=0;
+ lex->default_value= lex->on_update_value= 0;
+ lex->comment=null_lex_str;
+ lex->charset=NULL;
+ }
+ type opt_attribute
+ {
+ LEX *lex=Lex;
+ if (add_field_to_list(lex->thd, &$1, (enum enum_field_types) $3,
+ lex->length,lex->dec,lex->type,
+ lex->default_value, lex->on_update_value,
+ &lex->comment,
+ lex->change,&lex->interval_list,lex->charset,
+ lex->uint_geom_type))
+ MYSQL_YYABORT;
+ }
+ ;
type:
- int_type opt_field_length field_options { $$=$1; }
- | real_type opt_precision field_options { $$=$1; }
- | FLOAT_SYM float_options field_options { $$=FIELD_TYPE_FLOAT; }
- | BIT_SYM { Lex->length= (char*) "1";
- $$=FIELD_TYPE_BIT; }
- | BIT_SYM field_length { $$=FIELD_TYPE_BIT; }
- | BOOL_SYM { Lex->length=(char*) "1";
- $$=FIELD_TYPE_TINY; }
- | BOOLEAN_SYM { Lex->length=(char*) "1";
- $$=FIELD_TYPE_TINY; }
- | char field_length opt_binary { $$=FIELD_TYPE_STRING; }
- | char opt_binary { Lex->length=(char*) "1";
- $$=FIELD_TYPE_STRING; }
- | nchar field_length opt_bin_mod { $$=FIELD_TYPE_STRING;
- Lex->charset=national_charset_info; }
- | nchar opt_bin_mod { Lex->length=(char*) "1";
- $$=FIELD_TYPE_STRING;
- Lex->charset=national_charset_info; }
- | BINARY field_length { Lex->charset=&my_charset_bin;
- $$=FIELD_TYPE_STRING; }
- | BINARY { Lex->length= (char*) "1";
- Lex->charset=&my_charset_bin;
- $$=FIELD_TYPE_STRING; }
- | varchar field_length opt_binary { $$= MYSQL_TYPE_VARCHAR; }
- | nvarchar field_length opt_bin_mod { $$= MYSQL_TYPE_VARCHAR;
- Lex->charset=national_charset_info; }
- | VARBINARY field_length { Lex->charset=&my_charset_bin;
- $$= MYSQL_TYPE_VARCHAR; }
- | YEAR_SYM opt_field_length field_options { $$=FIELD_TYPE_YEAR; }
- | DATE_SYM { $$=FIELD_TYPE_DATE; }
- | TIME_SYM { $$=FIELD_TYPE_TIME; }
- | TIMESTAMP opt_field_length
- {
- if (YYTHD->variables.sql_mode & MODE_MAXDB)
- $$=FIELD_TYPE_DATETIME;
- else
+ int_type opt_field_length field_options { $$=$1; }
+ | real_type opt_precision field_options { $$=$1; }
+ | FLOAT_SYM float_options field_options { $$=MYSQL_TYPE_FLOAT; }
+ | BIT_SYM
+ {
+ Lex->length= (char*) "1";
+ $$=MYSQL_TYPE_BIT;
+ }
+ | BIT_SYM field_length
+ {
+ $$=MYSQL_TYPE_BIT;
+ }
+ | BOOL_SYM
+ {
+ Lex->length= (char*) "1";
+ $$=MYSQL_TYPE_TINY;
+ }
+ | BOOLEAN_SYM
+ {
+ Lex->length= (char*) "1";
+ $$=MYSQL_TYPE_TINY;
+ }
+ | char field_length opt_binary
+ {
+ $$=MYSQL_TYPE_STRING;
+ }
+ | char opt_binary
+ {
+ Lex->length= (char*) "1";
+ $$=MYSQL_TYPE_STRING;
+ }
+ | nchar field_length opt_bin_mod
+ {
+ $$=MYSQL_TYPE_STRING;
+ Lex->charset=national_charset_info;
+ }
+ | nchar opt_bin_mod
+ {
+ Lex->length= (char*) "1";
+ $$=MYSQL_TYPE_STRING;
+ Lex->charset=national_charset_info;
+ }
+ | BINARY field_length
+ {
+ Lex->charset=&my_charset_bin;
+ $$=MYSQL_TYPE_STRING;
+ }
+ | BINARY
+ {
+ Lex->length= (char*) "1";
+ Lex->charset=&my_charset_bin;
+ $$=MYSQL_TYPE_STRING;
+ }
+ | varchar field_length opt_binary
+ {
+ $$= MYSQL_TYPE_VARCHAR;
+ }
+ | nvarchar field_length opt_bin_mod
+ {
+ $$= MYSQL_TYPE_VARCHAR;
+ Lex->charset=national_charset_info;
+ }
+ | VARBINARY field_length
+ {
+ Lex->charset=&my_charset_bin;
+ $$= MYSQL_TYPE_VARCHAR;
+ }
+ | YEAR_SYM opt_field_length field_options
+ { $$=MYSQL_TYPE_YEAR; }
+ | DATE_SYM
+ { $$=MYSQL_TYPE_DATE; }
+ | TIME_SYM
+ { $$=MYSQL_TYPE_TIME; }
+ | TIMESTAMP opt_field_length
+ {
+ if (YYTHD->variables.sql_mode & MODE_MAXDB)
+ $$=MYSQL_TYPE_DATETIME;
+ else
{
/*
Unlike other types TIMESTAMP fields are NOT NULL by default.
*/
Lex->type|= NOT_NULL_FLAG;
- $$=FIELD_TYPE_TIMESTAMP;
+ $$=MYSQL_TYPE_TIMESTAMP;
}
- }
- | DATETIME { $$=FIELD_TYPE_DATETIME; }
- | TINYBLOB { Lex->charset=&my_charset_bin;
- $$=FIELD_TYPE_TINY_BLOB; }
- | BLOB_SYM opt_field_length { Lex->charset=&my_charset_bin;
- $$=FIELD_TYPE_BLOB; }
- | spatial_type
+ }
+ | DATETIME
+ { $$=MYSQL_TYPE_DATETIME; }
+ | TINYBLOB
+ {
+ Lex->charset=&my_charset_bin;
+ $$=MYSQL_TYPE_TINY_BLOB;
+ }
+ | BLOB_SYM opt_field_length
+ {
+ Lex->charset=&my_charset_bin;
+ $$=MYSQL_TYPE_BLOB;
+ }
+ | spatial_type
{
#ifdef HAVE_SPATIAL
Lex->charset=&my_charset_bin;
Lex->uint_geom_type= (uint)$1;
- $$=FIELD_TYPE_GEOMETRY;
+ $$=MYSQL_TYPE_GEOMETRY;
#else
my_error(ER_FEATURE_DISABLED, MYF(0),
sym_group_geom.name, sym_group_geom.needed_define);
MYSQL_YYABORT;
#endif
}
- | MEDIUMBLOB { Lex->charset=&my_charset_bin;
- $$=FIELD_TYPE_MEDIUM_BLOB; }
- | LONGBLOB { Lex->charset=&my_charset_bin;
- $$=FIELD_TYPE_LONG_BLOB; }
- | LONG_SYM VARBINARY { Lex->charset=&my_charset_bin;
- $$=FIELD_TYPE_MEDIUM_BLOB; }
- | LONG_SYM varchar opt_binary { $$=FIELD_TYPE_MEDIUM_BLOB; }
- | TINYTEXT opt_binary { $$=FIELD_TYPE_TINY_BLOB; }
- | TEXT_SYM opt_field_length opt_binary { $$=FIELD_TYPE_BLOB; }
- | MEDIUMTEXT opt_binary { $$=FIELD_TYPE_MEDIUM_BLOB; }
- | LONGTEXT opt_binary { $$=FIELD_TYPE_LONG_BLOB; }
- | DECIMAL_SYM float_options field_options
- { $$=FIELD_TYPE_NEWDECIMAL;}
- | NUMERIC_SYM float_options field_options
- { $$=FIELD_TYPE_NEWDECIMAL;}
- | FIXED_SYM float_options field_options
- { $$=FIELD_TYPE_NEWDECIMAL;}
- | ENUM {Lex->interval_list.empty();} '(' string_list ')' opt_binary
- { $$=FIELD_TYPE_ENUM; }
- | SET { Lex->interval_list.empty();} '(' string_list ')' opt_binary
- { $$=FIELD_TYPE_SET; }
- | LONG_SYM opt_binary { $$=FIELD_TYPE_MEDIUM_BLOB; }
- | SERIAL_SYM
- {
- $$=FIELD_TYPE_LONGLONG;
- Lex->type|= (AUTO_INCREMENT_FLAG | NOT_NULL_FLAG | UNSIGNED_FLAG |
- UNIQUE_FLAG);
- }
- ;
+ | MEDIUMBLOB
+ {
+ Lex->charset=&my_charset_bin;
+ $$=MYSQL_TYPE_MEDIUM_BLOB;
+ }
+ | LONGBLOB
+ {
+ Lex->charset=&my_charset_bin;
+ $$=MYSQL_TYPE_LONG_BLOB;
+ }
+ | LONG_SYM VARBINARY
+ {
+ Lex->charset=&my_charset_bin;
+ $$=MYSQL_TYPE_MEDIUM_BLOB;
+ }
+ | LONG_SYM varchar opt_binary
+ { $$=MYSQL_TYPE_MEDIUM_BLOB; }
+ | TINYTEXT opt_binary
+ { $$=MYSQL_TYPE_TINY_BLOB; }
+ | TEXT_SYM opt_field_length opt_binary
+ { $$=MYSQL_TYPE_BLOB; }
+ | MEDIUMTEXT opt_binary
+ { $$=MYSQL_TYPE_MEDIUM_BLOB; }
+ | LONGTEXT opt_binary
+ { $$=MYSQL_TYPE_LONG_BLOB; }
+ | DECIMAL_SYM float_options field_options
+ { $$=MYSQL_TYPE_NEWDECIMAL;}
+ | NUMERIC_SYM float_options field_options
+ { $$=MYSQL_TYPE_NEWDECIMAL;}
+ | FIXED_SYM float_options field_options
+ { $$=MYSQL_TYPE_NEWDECIMAL;}
+ | ENUM
+ {Lex->interval_list.empty();}
+ '(' string_list ')' opt_binary
+ { $$=MYSQL_TYPE_ENUM; }
+ | SET
+ { Lex->interval_list.empty();}
+ '(' string_list ')' opt_binary
+ { $$=MYSQL_TYPE_SET; }
+ | LONG_SYM opt_binary
+ { $$=MYSQL_TYPE_MEDIUM_BLOB; }
+ | SERIAL_SYM
+ {
+ $$=MYSQL_TYPE_LONGLONG;
+ Lex->type|= (AUTO_INCREMENT_FLAG | NOT_NULL_FLAG | UNSIGNED_FLAG |
+ UNIQUE_FLAG);
+ }
+ ;
spatial_type:
- GEOMETRY_SYM { $$= Field::GEOM_GEOMETRY; }
- | GEOMETRYCOLLECTION { $$= Field::GEOM_GEOMETRYCOLLECTION; }
- | POINT_SYM { Lex->length= (char*)"25";
- $$= Field::GEOM_POINT;
- }
- | MULTIPOINT { $$= Field::GEOM_MULTIPOINT; }
- | LINESTRING { $$= Field::GEOM_LINESTRING; }
- | MULTILINESTRING { $$= Field::GEOM_MULTILINESTRING; }
- | POLYGON { $$= Field::GEOM_POLYGON; }
- | MULTIPOLYGON { $$= Field::GEOM_MULTIPOLYGON; }
- ;
+ GEOMETRY_SYM { $$= Field::GEOM_GEOMETRY; }
+ | GEOMETRYCOLLECTION { $$= Field::GEOM_GEOMETRYCOLLECTION; }
+ | POINT_SYM
+ {
+ Lex->length= (char*)"25";
+ $$= Field::GEOM_POINT;
+ }
+ | MULTIPOINT { $$= Field::GEOM_MULTIPOINT; }
+ | LINESTRING { $$= Field::GEOM_LINESTRING; }
+ | MULTILINESTRING { $$= Field::GEOM_MULTILINESTRING; }
+ | POLYGON { $$= Field::GEOM_POLYGON; }
+ | MULTIPOLYGON { $$= Field::GEOM_MULTIPOLYGON; }
+ ;
char:
- CHAR_SYM {}
- ;
+ CHAR_SYM {}
+ ;
nchar:
- NCHAR_SYM {}
- | NATIONAL_SYM CHAR_SYM {}
- ;
+ NCHAR_SYM {}
+ | NATIONAL_SYM CHAR_SYM {}
+ ;
varchar:
- char VARYING {}
- | VARCHAR {}
- ;
+ char VARYING {}
+ | VARCHAR {}
+ ;
nvarchar:
- NATIONAL_SYM VARCHAR {}
- | NVARCHAR_SYM {}
- | NCHAR_SYM VARCHAR {}
- | NATIONAL_SYM CHAR_SYM VARYING {}
- | NCHAR_SYM VARYING {}
- ;
+ NATIONAL_SYM VARCHAR {}
+ | NVARCHAR_SYM {}
+ | NCHAR_SYM VARCHAR {}
+ | NATIONAL_SYM CHAR_SYM VARYING {}
+ | NCHAR_SYM VARYING {}
+ ;
int_type:
- INT_SYM { $$=FIELD_TYPE_LONG; }
- | TINYINT { $$=FIELD_TYPE_TINY; }
- | SMALLINT { $$=FIELD_TYPE_SHORT; }
- | MEDIUMINT { $$=FIELD_TYPE_INT24; }
- | BIGINT { $$=FIELD_TYPE_LONGLONG; };
+ INT_SYM { $$=MYSQL_TYPE_LONG; }
+ | TINYINT { $$=MYSQL_TYPE_TINY; }
+ | SMALLINT { $$=MYSQL_TYPE_SHORT; }
+ | MEDIUMINT { $$=MYSQL_TYPE_INT24; }
+ | BIGINT { $$=MYSQL_TYPE_LONGLONG; }
+ ;
real_type:
- REAL { $$= YYTHD->variables.sql_mode & MODE_REAL_AS_FLOAT ?
- FIELD_TYPE_FLOAT : FIELD_TYPE_DOUBLE; }
- | DOUBLE_SYM { $$=FIELD_TYPE_DOUBLE; }
- | DOUBLE_SYM PRECISION { $$=FIELD_TYPE_DOUBLE; };
-
+ REAL
+ {
+ $$= YYTHD->variables.sql_mode & MODE_REAL_AS_FLOAT ?
+ MYSQL_TYPE_FLOAT : MYSQL_TYPE_DOUBLE;
+ }
+ | DOUBLE_SYM
+ { $$=MYSQL_TYPE_DOUBLE; }
+ | DOUBLE_SYM PRECISION
+ { $$=MYSQL_TYPE_DOUBLE; }
+ ;
float_options:
- /* empty */ { Lex->dec=Lex->length= (char*)0; }
- | field_length { Lex->dec= (char*)0; }
- | precision {};
+ /* empty */
+ { Lex->dec=Lex->length= (char*)0; }
+ | field_length
+ { Lex->dec= (char*)0; }
+ | precision
+ {}
+ ;
precision:
- '(' NUM ',' NUM ')'
- {
- LEX *lex=Lex;
- lex->length=$2.str; lex->dec=$4.str;
- };
+ '(' NUM ',' NUM ')'
+ {
+ LEX *lex=Lex;
+ lex->length=$2.str;
+ lex->dec=$4.str;
+ }
+ ;
field_options:
- /* empty */ {}
- | field_opt_list {};
+ /* empty */ {}
+ | field_opt_list {}
+ ;
field_opt_list:
- field_opt_list field_option {}
- | field_option {};
+ field_opt_list field_option {}
+ | field_option {}
+ ;
field_option:
- SIGNED_SYM {}
- | UNSIGNED { Lex->type|= UNSIGNED_FLAG;}
- | ZEROFILL { Lex->type|= UNSIGNED_FLAG | ZEROFILL_FLAG; };
-
-opt_field_length:
- /* empty */ { Lex->length=(char*) NULL; } /* use default length */
- | field_length {};
+ SIGNED_SYM {}
+ | UNSIGNED { Lex->type|= UNSIGNED_FLAG;}
+ | ZEROFILL { Lex->type|= UNSIGNED_FLAG | ZEROFILL_FLAG; }
+ ;
field_length:
- '(' LONG_NUM ')' { Lex->length= $2.str; }
+ '(' LONG_NUM ')' { Lex->length= $2.str; }
| '(' ULONGLONG_NUM ')' { Lex->length= $2.str; }
| '(' DECIMAL_NUM ')' { Lex->length= $2.str; }
| '(' NUM ')' { Lex->length= $2.str; };
+opt_field_length:
+ /* empty */ { Lex->length=(char*) 0; /* use default length */ }
+ | field_length { }
+
opt_precision:
- /* empty */ {}
- | precision {};
+ /* empty */ {}
+ | precision {}
+ ;
opt_attribute:
- /* empty */ {}
- | opt_attribute_list {};
+ /* empty */ {}
+ | opt_attribute_list {}
+ ;
opt_attribute_list:
- opt_attribute_list attribute {}
- | attribute;
+ opt_attribute_list attribute {}
+ | attribute
+ ;
attribute:
- NULL_SYM { Lex->type&= ~ NOT_NULL_FLAG; }
- | not NULL_SYM { Lex->type|= NOT_NULL_FLAG; }
- | DEFAULT now_or_signed_literal { Lex->default_value=$2; }
- | ON UPDATE_SYM NOW_SYM optional_braces
- { Lex->on_update_value= new Item_func_now_local(); }
- | AUTO_INC { Lex->type|= AUTO_INCREMENT_FLAG | NOT_NULL_FLAG; }
- | SERIAL_SYM DEFAULT VALUE_SYM
- {
- LEX *lex=Lex;
- lex->type|= AUTO_INCREMENT_FLAG | NOT_NULL_FLAG | UNIQUE_FLAG;
- lex->alter_info.flags|= ALTER_ADD_INDEX;
- }
- | opt_primary KEY_SYM
- {
- LEX *lex=Lex;
- lex->type|= PRI_KEY_FLAG | NOT_NULL_FLAG;
- lex->alter_info.flags|= ALTER_ADD_INDEX;
- }
- | UNIQUE_SYM
- {
- LEX *lex=Lex;
- lex->type|= UNIQUE_FLAG;
- lex->alter_info.flags|= ALTER_ADD_INDEX;
- }
- | UNIQUE_SYM KEY_SYM
- {
- LEX *lex=Lex;
- lex->type|= UNIQUE_KEY_FLAG;
- lex->alter_info.flags|= ALTER_ADD_INDEX;
- }
- | COMMENT_SYM TEXT_STRING_sys { Lex->comment= $2; }
- | COLLATE_SYM collation_name
- {
- if (Lex->charset && !my_charset_same(Lex->charset,$2))
- {
- my_error(ER_COLLATION_CHARSET_MISMATCH, MYF(0),
+ NULL_SYM { Lex->type&= ~ NOT_NULL_FLAG; }
+ | not NULL_SYM { Lex->type|= NOT_NULL_FLAG; }
+ | DEFAULT now_or_signed_literal { Lex->default_value=$2; }
+ | ON UPDATE_SYM NOW_SYM optional_braces
+ {
+ Item *item= new (YYTHD->mem_root) Item_func_now_local();
+ if (item == NULL)
+ MYSQL_YYABORT;
+ Lex->on_update_value= item;
+ }
+ | AUTO_INC { Lex->type|= AUTO_INCREMENT_FLAG | NOT_NULL_FLAG; }
+ | SERIAL_SYM DEFAULT VALUE_SYM
+ {
+ LEX *lex=Lex;
+ lex->type|= AUTO_INCREMENT_FLAG | NOT_NULL_FLAG | UNIQUE_FLAG;
+ lex->alter_info.flags|= ALTER_ADD_INDEX;
+ }
+ | opt_primary KEY_SYM
+ {
+ LEX *lex=Lex;
+ lex->type|= PRI_KEY_FLAG | NOT_NULL_FLAG;
+ lex->alter_info.flags|= ALTER_ADD_INDEX;
+ }
+ | UNIQUE_SYM
+ {
+ LEX *lex=Lex;
+ lex->type|= UNIQUE_FLAG;
+ lex->alter_info.flags|= ALTER_ADD_INDEX;
+ }
+ | UNIQUE_SYM KEY_SYM
+ {
+ LEX *lex=Lex;
+ lex->type|= UNIQUE_KEY_FLAG;
+ lex->alter_info.flags|= ALTER_ADD_INDEX;
+ }
+ | COMMENT_SYM TEXT_STRING_sys { Lex->comment= $2; }
+ | COLLATE_SYM collation_name
+ {
+ if (Lex->charset && !my_charset_same(Lex->charset,$2))
+ {
+ my_error(ER_COLLATION_CHARSET_MISMATCH, MYF(0),
$2->name,Lex->charset->csname);
- MYSQL_YYABORT;
- }
- else
- {
- Lex->charset=$2;
- }
- }
- ;
+ MYSQL_YYABORT;
+ }
+ else
+ {
+ Lex->charset=$2;
+ }
+ }
+ ;
now_or_signed_literal:
- NOW_SYM optional_braces
+ NOW_SYM optional_braces
{
- $$= new Item_func_now_local();
+ $$= new (YYTHD->mem_root) Item_func_now_local();
if ($$ == NULL)
MYSQL_YYABORT;
}
- | signed_literal { $$=$1; }
+ | signed_literal
+ { $$=$1; }
;
charset:
- CHAR_SYM SET {}
- | CHARSET {}
- ;
+ CHAR_SYM SET {}
+ | CHARSET {}
+ ;
charset_name:
- ident_or_text
- {
- if (!($$=get_charset_by_csname($1.str,MY_CS_PRIMARY,MYF(0))))
- {
- my_error(ER_UNKNOWN_CHARACTER_SET, MYF(0), $1.str);
- MYSQL_YYABORT;
- }
- }
- | BINARY { $$= &my_charset_bin; }
- ;
+ ident_or_text
+ {
+ if (!($$=get_charset_by_csname($1.str,MY_CS_PRIMARY,MYF(0))))
+ {
+ my_error(ER_UNKNOWN_CHARACTER_SET, MYF(0), $1.str);
+ MYSQL_YYABORT;
+ }
+ }
+ | BINARY { $$= &my_charset_bin; }
+ ;
charset_name_or_default:
- charset_name { $$=$1; }
- | DEFAULT { $$=NULL; } ;
+ charset_name { $$=$1; }
+ | DEFAULT { $$=NULL; }
+ ;
opt_load_data_charset:
- /* Empty */ { $$= NULL; }
- | charset charset_name_or_default { $$= $2; }
- ;
+ /* Empty */ { $$= NULL; }
+ | charset charset_name_or_default { $$= $2; }
+ ;
old_or_new_charset_name:
- ident_or_text
- {
- if (!($$=get_charset_by_csname($1.str,MY_CS_PRIMARY,MYF(0))) &&
- !($$=get_old_charset_by_name($1.str)))
- {
- my_error(ER_UNKNOWN_CHARACTER_SET, MYF(0), $1.str);
- MYSQL_YYABORT;
- }
- }
- | BINARY { $$= &my_charset_bin; }
- ;
+ ident_or_text
+ {
+ if (!($$=get_charset_by_csname($1.str,MY_CS_PRIMARY,MYF(0))) &&
+ !($$=get_old_charset_by_name($1.str)))
+ {
+ my_error(ER_UNKNOWN_CHARACTER_SET, MYF(0), $1.str);
+ MYSQL_YYABORT;
+ }
+ }
+ | BINARY { $$= &my_charset_bin; }
+ ;
old_or_new_charset_name_or_default:
- old_or_new_charset_name { $$=$1; }
- | DEFAULT { $$=NULL; } ;
+ old_or_new_charset_name { $$=$1; }
+ | DEFAULT { $$=NULL; }
+ ;
collation_name:
- ident_or_text
- {
- if (!($$=get_charset_by_name($1.str,MYF(0))))
- {
- my_error(ER_UNKNOWN_COLLATION, MYF(0), $1.str);
- MYSQL_YYABORT;
- }
- };
+ ident_or_text
+ {
+ if (!($$=get_charset_by_name($1.str,MYF(0))))
+ {
+ my_error(ER_UNKNOWN_COLLATION, MYF(0), $1.str);
+ MYSQL_YYABORT;
+ }
+ }
+ ;
opt_collate:
- /* empty */ { $$=NULL; }
- | COLLATE_SYM collation_name_or_default { $$=$2; }
- ;
+ /* empty */ { $$=NULL; }
+ | COLLATE_SYM collation_name_or_default { $$=$2; }
+ ;
collation_name_or_default:
- collation_name { $$=$1; }
- | DEFAULT { $$=NULL; } ;
+ collation_name { $$=$1; }
+ | DEFAULT { $$=NULL; }
+ ;
opt_default:
- /* empty */ {}
- | DEFAULT {};
+ /* empty */ {}
+ | 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
- {
- 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 opt_bin_mod { Lex->charset=$2; }
- | BINARY opt_bin_charset { Lex->type|= BINCMP_FLAG; };
+ /* 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
+ {
+ 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 opt_bin_mod { Lex->charset=$2; }
+ | BINARY opt_bin_charset { Lex->type|= BINCMP_FLAG; }
+ ;
opt_bin_mod:
- /* empty */ { }
- | BINARY { Lex->type|= BINCMP_FLAG; };
+ /* empty */ { }
+ | 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; } ;
+ /* 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 */
- | PRIMARY_SYM
- ;
+ /* empty */
+ | PRIMARY_SYM
+ ;
references:
- REFERENCES table_ident
- {
- LEX *lex=Lex;
- lex->fk_delete_opt= lex->fk_update_opt= lex->fk_match_option= 0;
- lex->ref_list.empty();
- }
- opt_ref_list
- {
- $$=$2;
- };
+ REFERENCES table_ident
+ {
+ LEX *lex=Lex;
+ lex->fk_delete_opt= lex->fk_update_opt= lex->fk_match_option= 0;
+ lex->ref_list.empty();
+ }
+ opt_ref_list
+ {
+ $$=$2;
+ }
+ ;
opt_ref_list:
- /* empty */ opt_on_delete {}
- | '(' ref_list ')' opt_on_delete {};
+ /* empty */ opt_on_delete {}
+ | '(' ref_list ')' opt_on_delete {}
+ ;
ref_list:
ref_list ',' ident
{
- key_part_spec *key= new key_part_spec($3.str);
+ Key_part_spec *key= new Key_part_spec($3.str);
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.str);
if (key == NULL)
MYSQL_YYABORT;
Lex->ref_list.push_back(key);
}
;
-
opt_on_delete:
- /* empty */ {}
- | opt_on_delete_list {};
+ /* empty */ {}
+ | opt_on_delete_list {}
+ ;
opt_on_delete_list:
- opt_on_delete_list opt_on_delete_item {}
- | opt_on_delete_item {};
+ opt_on_delete_list opt_on_delete_item {}
+ | opt_on_delete_item {}
+ ;
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; };
+ 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; }
+ ;
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 { $$= (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; }
+ ;
key_type:
- key_or_index { $$= Key::MULTIPLE; }
- | FULLTEXT_SYM opt_key_or_index { $$= Key::FULLTEXT; }
- | SPATIAL_SYM opt_key_or_index
- {
+ key_or_index { $$= Key::MULTIPLE; }
+ | FULLTEXT_SYM opt_key_or_index { $$= Key::FULLTEXT; }
+ | SPATIAL_SYM opt_key_or_index
+ {
#ifdef HAVE_SPATIAL
- $$= Key::SPATIAL;
+ $$= Key::SPATIAL;
#else
- my_error(ER_FEATURE_DISABLED, MYF(0),
+ my_error(ER_FEATURE_DISABLED, MYF(0),
sym_group_geom.name, sym_group_geom.needed_define);
- MYSQL_YYABORT;
+ MYSQL_YYABORT;
#endif
- };
+ }
+ ;
constraint_key_type:
- PRIMARY_SYM KEY_SYM { $$= Key::PRIMARY; }
- | UNIQUE_SYM opt_key_or_index { $$= Key::UNIQUE; };
+ PRIMARY_SYM KEY_SYM { $$= Key::PRIMARY; }
+ | UNIQUE_SYM opt_key_or_index { $$= Key::UNIQUE; }
+ ;
key_or_index:
- KEY_SYM {}
- | INDEX_SYM {};
+ KEY_SYM {}
+ | INDEX_SYM {}
+ ;
opt_key_or_index:
- /* empty */ {}
- | key_or_index
- ;
+ /* empty */ {}
+ | key_or_index
+ ;
keys_or_index:
- KEYS {}
- | INDEX_SYM {}
- | INDEXES {};
+ KEYS {}
+ | INDEX_SYM {}
+ | INDEXES {}
+ ;
opt_unique_or_fulltext:
- /* empty */ { $$= Key::MULTIPLE; }
- | UNIQUE_SYM { $$= Key::UNIQUE; }
- | FULLTEXT_SYM { $$= Key::FULLTEXT;}
- | SPATIAL_SYM
- {
+ /* empty */ { $$= Key::MULTIPLE; }
+ | UNIQUE_SYM { $$= Key::UNIQUE; }
+ | FULLTEXT_SYM { $$= Key::FULLTEXT;}
+ | SPATIAL_SYM
+ {
#ifdef HAVE_SPATIAL
- $$= Key::SPATIAL;
+ $$= Key::SPATIAL;
#else
my_error(ER_FEATURE_DISABLED, MYF(0),
sym_group_geom.name, sym_group_geom.needed_define);
- MYSQL_YYABORT;
+ MYSQL_YYABORT;
#endif
- }
+ }
+ ;
+
+init_key_options:
+ {
+ Lex->key_create_info= default_key_create_info;
+ }
;
+/*
+ For now, key_alg initializies lex->key_create_info.
+ In the future, when all key options are after key definition,
+ we can remove key_alg and move init_key_options to key_options
+*/
+
key_alg:
- /* empty */ { $$= HA_KEY_ALG_UNDEF; }
- | USING opt_btree_or_rtree { $$= $2; }
- | TYPE_SYM opt_btree_or_rtree { $$= $2; };
-
-opt_btree_or_rtree:
- BTREE_SYM { $$= HA_KEY_ALG_BTREE; }
- | RTREE_SYM
- {
- $$= HA_KEY_ALG_RTREE;
- }
- | HASH_SYM { $$= HA_KEY_ALG_HASH; };
+ init_key_options
+ | init_key_options key_using_alg
+ ;
+
+key_options:
+ /* empty */ {}
+ | key_opts
+ ;
+
+key_opts:
+ key_opt
+ | key_opts key_opt
+ ;
+
+key_using_alg:
+ USING btree_or_rtree { Lex->key_create_info.algorithm= $2; }
+ | TYPE_SYM btree_or_rtree { Lex->key_create_info.algorithm= $2; }
+ ;
+
+key_opt:
+ key_using_alg
+ | KEY_BLOCK_SIZE opt_equal ulong_num
+ { Lex->key_create_info.block_size= $3; }
+ | WITH PARSER_SYM IDENT_sys
+ {
+ if (plugin_is_ready(&$3, MYSQL_FTPARSER_PLUGIN))
+ Lex->key_create_info.parser_name= $3;
+ else
+ {
+ my_error(ER_FUNCTION_NOT_DEFINED, MYF(0), $3.str);
+ MYSQL_YYABORT;
+ }
+ }
+ ;
+
+btree_or_rtree:
+ BTREE_SYM { $$= HA_KEY_ALG_BTREE; }
+ | RTREE_SYM { $$= HA_KEY_ALG_RTREE; }
+ | HASH_SYM { $$= HA_KEY_ALG_HASH; }
+ ;
key_list:
- key_list ',' key_part order_dir { Lex->col_list.push_back($3); }
- | key_part order_dir { Lex->col_list.push_back($1); };
+ key_list ',' key_part order_dir { Lex->col_list.push_back($3); }
+ | key_part order_dir { Lex->col_list.push_back($1); }
+ ;
key_part:
ident
{
- $$= new key_part_spec($1.str);
+ $$= new Key_part_spec($1.str);
if ($$ == NULL)
MYSQL_YYABORT;
}
- | ident '(' NUM ')'
+ | ident '(' NUM ')'
{
int key_part_len= atoi($3.str);
if (!key_part_len)
{
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.str,(uint) key_part_len);
if ($$ == NULL)
MYSQL_YYABORT;
}
;
opt_ident:
- /* empty */ { $$=(char*) 0; } /* Defaultlength */
- | field_ident { $$=$1.str; };
+ /* empty */ { $$=(char*) 0; /* Default length */ }
+ | field_ident { $$=$1.str; }
+ ;
opt_component:
- /* empty */ { $$= null_lex_str; }
- | '.' ident { $$= $2; };
+ /* empty */ { $$= null_lex_str; }
+ | '.' ident { $$= $2; }
+ ;
string_list:
- text_string { Lex->interval_list.push_back($1); }
- | string_list ',' text_string { Lex->interval_list.push_back($3); };
+ text_string { Lex->interval_list.push_back($1); }
+ | string_list ',' text_string { Lex->interval_list.push_back($3); };
/*
** Alter table
*/
alter:
- ALTER opt_ignore TABLE_SYM table_ident
- {
- THD *thd= YYTHD;
- LEX *lex= thd->lex;
- lex->sql_command= SQLCOM_ALTER_TABLE;
- lex->name= 0;
- lex->duplicates= DUP_ERROR;
- if (!lex->select_lex.add_table_to_list(thd, $4, NULL,
- TL_OPTION_UPDATING))
- MYSQL_YYABORT;
- lex->col_list.empty();
- lex->select_lex.init_order();
- lex->select_lex.db=
- ((TABLE_LIST*) lex->select_lex.table_list.first)->db;
- lex->name=0;
- bzero((char*) &lex->create_info,sizeof(lex->create_info));
- lex->create_info.db_type= DB_TYPE_DEFAULT;
- lex->create_info.default_table_charset= NULL;
- lex->create_info.row_type= ROW_TYPE_NOT_USED;
- lex->alter_info.reset();
- }
- alter_list
- {}
- | ALTER DATABASE ident_or_empty
+ ALTER opt_ignore 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))
+ MYSQL_YYABORT;
+ lex->alter_info.reset();
+ lex->col_list.empty();
+ lex->select_lex.init_order();
+ lex->select_lex.db=
+ ((TABLE_LIST*) lex->select_lex.table_list.first)->db;
+ bzero((char*) &lex->create_info,sizeof(lex->create_info));
+ lex->create_info.db_type= 0;
+ lex->create_info.default_table_charset= NULL;
+ lex->create_info.row_type= ROW_TYPE_NOT_USED;
+ lex->alter_info.reset();
+ lex->no_write_to_binlog= 0;
+ lex->create_info.storage_media= HA_SM_DEFAULT;
+ }
+ alter_commands
+ {}
+ | ALTER DATABASE ident_or_empty
{
Lex->create_info.default_table_charset= NULL;
Lex->create_info.used_fields= 0;
}
create_database_options
- {
- LEX *lex=Lex;
- lex->sql_command=SQLCOM_ALTER_DB;
- lex->name= $3;
- if (lex->name == NULL && lex->copy_db_to(&lex->name, NULL))
- MYSQL_YYABORT;
- }
- | ALTER PROCEDURE sp_name
- {
- LEX *lex= Lex;
-
- if (lex->sphead)
- {
- my_error(ER_SP_NO_DROP_SP, MYF(0), "PROCEDURE");
- MYSQL_YYABORT;
- }
- bzero((char *)&lex->sp_chistics, sizeof(st_sp_chistics));
- }
- sp_a_chistics
- {
- LEX *lex=Lex;
-
- lex->sql_command= SQLCOM_ALTER_PROCEDURE;
- lex->spname= $3;
- }
- | ALTER FUNCTION_SYM sp_name
- {
- LEX *lex= Lex;
-
- if (lex->sphead)
- {
- my_error(ER_SP_NO_DROP_SP, MYF(0), "FUNCTION");
- MYSQL_YYABORT;
- }
- bzero((char *)&lex->sp_chistics, sizeof(st_sp_chistics));
- }
- sp_a_chistics
- {
- LEX *lex=Lex;
-
- lex->sql_command= SQLCOM_ALTER_FUNCTION;
- lex->spname= $3;
- }
- | ALTER view_algorithm_opt definer_opt view_suid
- VIEW_SYM table_ident
- {
- THD *thd= YYTHD;
- LEX *lex= thd->lex;
- if (lex->sphead)
+ {
+ LEX *lex=Lex;
+ lex->sql_command=SQLCOM_ALTER_DB;
+ lex->name= $3;
+ if (lex->name.str == NULL &&
+ lex->copy_db_to(&lex->name.str, &lex->name.length))
+ MYSQL_YYABORT;
+ }
+ | ALTER DATABASE ident UPGRADE_SYM DATA_SYM DIRECTORY_SYM NAME_SYM
+ {
+ LEX *lex= Lex;
+ if (lex->sphead)
+ {
+ my_error(ER_SP_NO_DROP_SP, MYF(0), "DATABASE");
+ MYSQL_YYABORT;
+ }
+ lex->sql_command= SQLCOM_ALTER_DB_UPGRADE;
+ lex->name= $3;
+ }
+ | ALTER PROCEDURE sp_name
+ {
+ LEX *lex= Lex;
+
+ if (lex->sphead)
+ {
+ my_error(ER_SP_NO_DROP_SP, MYF(0), "PROCEDURE");
+ MYSQL_YYABORT;
+ }
+ bzero((char *)&lex->sp_chistics, sizeof(st_sp_chistics));
+ }
+ sp_a_chistics
+ {
+ LEX *lex=Lex;
+
+ lex->sql_command= SQLCOM_ALTER_PROCEDURE;
+ lex->spname= $3;
+ }
+ | ALTER FUNCTION_SYM sp_name
+ {
+ LEX *lex= Lex;
+
+ if (lex->sphead)
+ {
+ my_error(ER_SP_NO_DROP_SP, MYF(0), "FUNCTION");
+ MYSQL_YYABORT;
+ }
+ bzero((char *)&lex->sp_chistics, sizeof(st_sp_chistics));
+ }
+ sp_a_chistics
+ {
+ LEX *lex=Lex;
+
+ lex->sql_command= SQLCOM_ALTER_FUNCTION;
+ lex->spname= $3;
+ }
+ | ALTER view_algorithm definer_opt
+ {
+ LEX *lex= Lex;
+
+ if (lex->sphead)
{
my_error(ER_SP_BADSTATEMENT, MYF(0), "ALTER VIEW");
MYSQL_YYABORT;
}
- lex->sql_command= SQLCOM_CREATE_VIEW;
- lex->create_view_mode= VIEW_ALTER;
- /* first table in list is target VIEW name */
- lex->select_lex.add_table_to_list(thd, $6, NULL, TL_OPTION_UPDATING);
- }
- view_list_opt AS view_select view_check_option
- {}
- ;
+ lex->create_view_mode= VIEW_ALTER;
+ }
+ view_tail
+ {}
+ | ALTER definer_opt
+ /*
+ We have two separate rules for ALTER VIEW rather that
+ optional view_algorithm above, to resolve the ambiguity
+ with the ALTER EVENT below.
+ */
+ {
+ LEX *lex= Lex;
+
+ if (lex->sphead)
+ {
+ my_error(ER_SP_BADSTATEMENT, MYF(0), "ALTER VIEW");
+ MYSQL_YYABORT;
+ }
+ lex->create_view_algorithm= VIEW_ALGORITHM_UNDEFINED;
+ lex->create_view_mode= VIEW_ALTER;
+ }
+ view_tail
+ {}
+ | ALTER definer_opt EVENT_SYM sp_name
+ {
+ /*
+ It is safe to use Lex->spname because
+ ALTER EVENT xxx RENATE TO yyy DO ALTER EVENT RENAME TO
+ is not allowed. Lex->spname is used in the case of RENAME TO
+ If it had to be supported spname had to be added to
+ Event_parse_data.
+ */
+
+ if (!(Lex->event_parse_data= Event_parse_data::new_instance(YYTHD)))
+ MYSQL_YYABORT;
+ Lex->event_parse_data->identifier= $4;
+
+ Lex->sql_command= SQLCOM_ALTER_EVENT;
+ }
+ ev_alter_on_schedule_completion
+ opt_ev_rename_to
+ opt_ev_status
+ opt_ev_comment
+ opt_ev_sql_stmt
+ {
+ if (!($6 || $7 || $8 || $9 || $10))
+ {
+ my_parse_error(ER(ER_SYNTAX_ERROR));
+ MYSQL_YYABORT;
+ }
+ /*
+ sql_command is set here because some rules in ev_sql_stmt
+ can overwrite it
+ */
+ Lex->sql_command= SQLCOM_ALTER_EVENT;
+ }
+ | ALTER TABLESPACE alter_tablespace_info
+ {
+ LEX *lex= Lex;
+ lex->alter_tablespace_info->ts_cmd_type= ALTER_TABLESPACE;
+ }
+ | ALTER LOGFILE_SYM GROUP_SYM alter_logfile_group_info
+ {
+ LEX *lex= Lex;
+ lex->alter_tablespace_info->ts_cmd_type= ALTER_LOGFILE_GROUP;
+ }
+ | ALTER TABLESPACE change_tablespace_info
+ {
+ LEX *lex= Lex;
+ lex->alter_tablespace_info->ts_cmd_type= CHANGE_FILE_TABLESPACE;
+ }
+ | ALTER TABLESPACE change_tablespace_access
+ {
+ LEX *lex= Lex;
+ lex->alter_tablespace_info->ts_cmd_type= ALTER_ACCESS_MODE_TABLESPACE;
+ }
+ | ALTER SERVER_SYM ident_or_text OPTIONS_SYM '(' server_options_list ')'
+ {
+ LEX *lex= Lex;
+ lex->sql_command= SQLCOM_ALTER_SERVER;
+ lex->server_options.server_name= $3.str;
+ lex->server_options.server_name_length= $3.length;
+ }
+ ;
+
+ev_alter_on_schedule_completion:
+ /* empty */ { $$= 0;}
+ | ON SCHEDULE_SYM ev_schedule_time { $$= 1; }
+ | ev_on_completion { $$= 1; }
+ | ON SCHEDULE_SYM ev_schedule_time ev_on_completion { $$= 1; }
+ ;
+
+opt_ev_rename_to:
+ /* empty */ { $$= 0;}
+ | RENAME TO_SYM sp_name
+ {
+ /*
+ Use lex's spname to hold the new name.
+ The original name is in the Event_parse_data object
+ */
+ Lex->spname= $3;
+ $$= 1;
+ }
+ ;
+
+opt_ev_sql_stmt:
+ /* empty*/ { $$= 0;}
+ | DO_SYM ev_sql_stmt { $$= 1; }
+ ;
ident_or_empty:
- /* empty */ { $$= 0; }
- | ident { $$= $1.str; };
+ /* empty */ { $$.str= 0; $$.length= 0; }
+ | ident { $$= $1; }
+ ;
+
+alter_commands:
+ /* empty */
+ | DISCARD TABLESPACE { Lex->alter_info.tablespace_op= DISCARD_TABLESPACE; }
+ | IMPORT TABLESPACE { Lex->alter_info.tablespace_op= IMPORT_TABLESPACE; }
+ | alter_list
+ opt_partitioning
+ | alter_list
+ remove_partitioning
+ | remove_partitioning
+ | partitioning
+/*
+ This part was added for release 5.1 by Mikael Ronström.
+ From here we insert a number of commands to manage the partitions of a
+ partitioned table such as adding partitions, dropping partitions,
+ reorganising partitions in various manners. In future releases the list
+ will be longer and also include moving partitions to a
+ new table and so forth.
+*/
+ | add_partition_rule
+ | DROP PARTITION_SYM alt_part_name_list
+ {
+ Lex->alter_info.flags|= ALTER_DROP_PARTITION;
+ }
+ | REBUILD_SYM PARTITION_SYM opt_no_write_to_binlog
+ all_or_alt_part_name_list
+ {
+ LEX *lex= Lex;
+ lex->alter_info.flags|= ALTER_REBUILD_PARTITION;
+ lex->no_write_to_binlog= $3;
+ }
+ | 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->no_write_to_binlog= $3;
+ lex->check_opt.init();
+ }
+ 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->no_write_to_binlog= $3;
+ lex->check_opt.init();
+ }
+ | 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->check_opt.init();
+ }
+ 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->no_write_to_binlog= $3;
+ lex->check_opt.init();
+ }
+ opt_mi_repair_type
+ | COALESCE PARTITION_SYM opt_no_write_to_binlog real_ulong_num
+ {
+ LEX *lex= Lex;
+ lex->alter_info.flags|= ALTER_COALESCE_PARTITION;
+ lex->no_write_to_binlog= $3;
+ lex->alter_info.no_parts= $4;
+ }
+ | reorg_partition_rule
+ ;
+
+remove_partitioning:
+ REMOVE_SYM PARTITIONING_SYM
+ {
+ Lex->alter_info.flags|= ALTER_REMOVE_PARTITIONING;
+ }
+ ;
+
+all_or_alt_part_name_list:
+ ALL
+ {
+ Lex->alter_info.flags|= ALTER_ALL_PARTITION;
+ }
+ | alt_part_name_list
+ ;
+
+add_partition_rule:
+ ADD PARTITION_SYM opt_no_write_to_binlog
+ {
+ LEX *lex= Lex;
+ lex->part_info= new partition_info();
+ if (!lex->part_info)
+ {
+ mem_alloc_error(sizeof(partition_info));
+ MYSQL_YYABORT;
+ }
+ lex->alter_info.flags|= ALTER_ADD_PARTITION;
+ lex->no_write_to_binlog= $3;
+ }
+ add_part_extra
+ {}
+ ;
+
+add_part_extra:
+ /* empty */
+ | '(' part_def_list ')'
+ {
+ LEX *lex= Lex;
+ lex->part_info->no_parts= lex->part_info->partitions.elements;
+ }
+ | PARTITIONS_SYM real_ulong_num
+ {
+ LEX *lex= Lex;
+ lex->part_info->no_parts= $2;
+ }
+ ;
+
+reorg_partition_rule:
+ REORGANIZE_SYM PARTITION_SYM opt_no_write_to_binlog
+ {
+ LEX *lex= Lex;
+ lex->part_info= new partition_info();
+ if (!lex->part_info)
+ {
+ mem_alloc_error(sizeof(partition_info));
+ MYSQL_YYABORT;
+ }
+ lex->no_write_to_binlog= $3;
+ }
+ reorg_parts_rule
+ ;
+
+reorg_parts_rule:
+ /* empty */
+ {
+ Lex->alter_info.flags|= ALTER_TABLE_REORG;
+ }
+ | alt_part_name_list
+ {
+ Lex->alter_info.flags|= ALTER_REORGANIZE_PARTITION;
+ }
+ INTO '(' part_def_list ')'
+ {
+ LEX *lex= Lex;
+ lex->part_info->no_parts= lex->part_info->partitions.elements;
+ }
+ ;
+
+alt_part_name_list:
+ alt_part_name_item {}
+ | alt_part_name_list ',' alt_part_name_item {}
+ ;
+
+alt_part_name_item:
+ ident
+ {
+ if (Lex->alter_info.partition_names.push_back($1.str))
+ {
+ mem_alloc_error(1);
+ MYSQL_YYABORT;
+ }
+ }
+ ;
+
+/*
+ End of management of partition commands
+*/
alter_list:
- | DISCARD TABLESPACE { Lex->alter_info.tablespace_op= DISCARD_TABLESPACE; }
- | IMPORT TABLESPACE { Lex->alter_info.tablespace_op= IMPORT_TABLESPACE; }
- | alter_list_item
- | alter_list ',' alter_list_item;
+ alter_list_item
+ | alter_list ',' alter_list_item
+ ;
add_column:
- ADD opt_column
- {
- LEX *lex=Lex;
- lex->change=0;
- lex->alter_info.flags|= ALTER_ADD_COLUMN;
- };
+ ADD opt_column
+ {
+ LEX *lex=Lex;
+ lex->change=0;
+ lex->alter_info.flags|= ALTER_ADD_COLUMN;
+ }
+ ;
alter_list_item:
- add_column column_def opt_place { }
- | ADD key_def
- {
- Lex->alter_info.flags|= ALTER_ADD_INDEX;
- }
- | add_column '(' field_list ')'
- {
- Lex->alter_info.flags|= ALTER_ADD_COLUMN | ALTER_ADD_INDEX;
- }
- | CHANGE opt_column field_ident
- {
- LEX *lex=Lex;
- lex->change= $3.str;
- lex->alter_info.flags|= ALTER_CHANGE_COLUMN;
- }
+ add_column column_def opt_place { }
+ | ADD key_def
+ {
+ Lex->alter_info.flags|= ALTER_ADD_INDEX;
+ }
+ | add_column '(' field_list ')'
+ {
+ Lex->alter_info.flags|= ALTER_ADD_COLUMN | ALTER_ADD_INDEX;
+ }
+ | CHANGE opt_column field_ident
+ {
+ LEX *lex=Lex;
+ lex->change= $3.str;
+ lex->alter_info.flags|= ALTER_CHANGE_COLUMN;
+ }
field_spec opt_place
| MODIFY_SYM opt_column field_ident
{
@@ -3838,242 +5834,246 @@ alter_list_item:
lex->length=lex->dec=0; lex->type=0;
lex->default_value= lex->on_update_value= 0;
lex->comment=null_lex_str;
- lex->charset= NULL;
- lex->alter_info.flags|= ALTER_CHANGE_COLUMN;
+ lex->charset= NULL;
+ lex->alter_info.flags|= ALTER_CHANGE_COLUMN;
}
type opt_attribute
{
LEX *lex=Lex;
- if (add_field_to_list(lex->thd,$3.str,
+ if (add_field_to_list(lex->thd,&$3,
(enum enum_field_types) $5,
lex->length,lex->dec,lex->type,
lex->default_value, lex->on_update_value,
&lex->comment,
- $3.str, &lex->interval_list, lex->charset,
- lex->uint_geom_type))
- MYSQL_YYABORT;
+ $3.str, &lex->interval_list, lex->charset,
+ lex->uint_geom_type))
+ MYSQL_YYABORT;
}
opt_place
- | DROP opt_column field_ident opt_restrict
- {
- LEX *lex=Lex;
+ | DROP opt_column field_ident opt_restrict
+ {
+ LEX *lex=Lex;
Alter_drop *ad= new Alter_drop(Alter_drop::COLUMN, $3.str);
if (ad == NULL)
MYSQL_YYABORT;
- lex->alter_info.drop_list.push_back(ad);
- lex->alter_info.flags|= ALTER_DROP_COLUMN;
- }
- | DROP FOREIGN KEY_SYM opt_ident
+ lex->alter_info.drop_list.push_back(ad);
+ lex->alter_info.flags|= ALTER_DROP_COLUMN;
+ }
+ | DROP FOREIGN KEY_SYM opt_ident
{
- Lex->alter_info.flags|= ALTER_DROP_INDEX;
+ Lex->alter_info.flags|= ALTER_DROP_INDEX | ALTER_FOREIGN_KEY;
}
- | DROP PRIMARY_SYM KEY_SYM
- {
- LEX *lex=Lex;
+ | DROP PRIMARY_SYM KEY_SYM
+ {
+ LEX *lex=Lex;
Alter_drop *ad= new Alter_drop(Alter_drop::KEY, primary_key_name);
if (ad == NULL)
MYSQL_YYABORT;
- lex->alter_info.drop_list.push_back(ad);
- lex->alter_info.flags|= ALTER_DROP_INDEX;
- }
- | DROP key_or_index field_ident
- {
- LEX *lex=Lex;
+ lex->alter_info.drop_list.push_back(ad);
+ lex->alter_info.flags|= ALTER_DROP_INDEX;
+ }
+ | DROP key_or_index field_ident
+ {
+ LEX *lex=Lex;
Alter_drop *ad= new Alter_drop(Alter_drop::KEY, $3.str);
if (ad == NULL)
MYSQL_YYABORT;
- lex->alter_info.drop_list.push_back(ad);
- lex->alter_info.flags|= ALTER_DROP_INDEX;
- }
- | DISABLE_SYM KEYS
+ lex->alter_info.drop_list.push_back(ad);
+ lex->alter_info.flags|= ALTER_DROP_INDEX;
+ }
+ | DISABLE_SYM KEYS
{
- LEX *lex=Lex;
+ LEX *lex=Lex;
lex->alter_info.keys_onoff= DISABLE;
- lex->alter_info.flags|= ALTER_KEYS_ONOFF;
+ lex->alter_info.flags|= ALTER_KEYS_ONOFF;
}
- | ENABLE_SYM KEYS
+ | ENABLE_SYM KEYS
{
- LEX *lex=Lex;
+ LEX *lex=Lex;
lex->alter_info.keys_onoff= ENABLE;
- lex->alter_info.flags|= ALTER_KEYS_ONOFF;
+ lex->alter_info.flags|= ALTER_KEYS_ONOFF;
}
- | ALTER opt_column field_ident SET DEFAULT signed_literal
- {
- LEX *lex=Lex;
- Alter_column *ac= new Alter_column($3.str, $6);
+ | ALTER opt_column field_ident SET DEFAULT signed_literal
+ {
+ LEX *lex=Lex;
+ Alter_column *ac= new Alter_column($3.str,$6);
if (ac == NULL)
MYSQL_YYABORT;
- lex->alter_info.alter_list.push_back(ac);
- lex->alter_info.flags|= ALTER_CHANGE_COLUMN_DEFAULT;
- }
- | ALTER opt_column field_ident DROP DEFAULT
- {
- LEX *lex=Lex;
+ lex->alter_info.alter_list.push_back(ac);
+ lex->alter_info.flags|= ALTER_CHANGE_COLUMN_DEFAULT;
+ }
+ | ALTER opt_column field_ident DROP DEFAULT
+ {
+ LEX *lex=Lex;
Alter_column *ac= new Alter_column($3.str, (Item*) 0);
if (ac == NULL)
MYSQL_YYABORT;
- lex->alter_info.alter_list.push_back(ac);
- lex->alter_info.flags|= ALTER_CHANGE_COLUMN_DEFAULT;
- }
- | RENAME opt_to table_ident
- {
- LEX *lex=Lex;
- lex->select_lex.db=$3->db.str;
+ lex->alter_info.alter_list.push_back(ac);
+ lex->alter_info.flags|= ALTER_CHANGE_COLUMN_DEFAULT;
+ }
+ | RENAME opt_to table_ident
+ {
+ LEX *lex=Lex;
+ size_t dummy;
+ lex->select_lex.db=$3->db.str;
if (lex->select_lex.db == NULL &&
- lex->copy_db_to(&lex->select_lex.db, NULL))
+ lex->copy_db_to(&lex->select_lex.db, &dummy))
{
MYSQL_YYABORT;
}
if (check_table_name($3->table.str,$3->table.length) ||
- $3->db.str && check_db_name($3->db.str))
+ $3->db.str && check_db_name(&$3->db))
{
my_error(ER_WRONG_TABLE_NAME, MYF(0), $3->table.str);
MYSQL_YYABORT;
}
- lex->name= $3->table.str;
- lex->alter_info.flags|= ALTER_RENAME;
- }
- | CONVERT_SYM TO_SYM charset charset_name_or_default opt_collate
- {
- if (!$4)
- {
- THD *thd= YYTHD;
- $4= thd->variables.collation_database;
- }
- $5= $5 ? $5 : $4;
- if (!my_charset_same($4,$5))
- {
- my_error(ER_COLLATION_CHARSET_MISMATCH, MYF(0),
+ lex->name= $3->table;
+ lex->alter_info.flags|= ALTER_RENAME;
+ }
+ | CONVERT_SYM TO_SYM charset charset_name_or_default opt_collate
+ {
+ if (!$4)
+ {
+ THD *thd= YYTHD;
+ $4= thd->variables.collation_database;
+ }
+ $5= $5 ? $5 : $4;
+ if (!my_charset_same($4,$5))
+ {
+ my_error(ER_COLLATION_CHARSET_MISMATCH, MYF(0),
$5->name, $4->csname);
- MYSQL_YYABORT;
- }
- LEX *lex= Lex;
- lex->create_info.table_charset=
- lex->create_info.default_table_charset= $5;
- lex->create_info.used_fields|= (HA_CREATE_USED_CHARSET |
- HA_CREATE_USED_DEFAULT_CHARSET);
- lex->alter_info.flags|= ALTER_CONVERT;
- }
+ MYSQL_YYABORT;
+ }
+ LEX *lex= Lex;
+ lex->create_info.table_charset=
+ lex->create_info.default_table_charset= $5;
+ lex->create_info.used_fields|= (HA_CREATE_USED_CHARSET |
+ HA_CREATE_USED_DEFAULT_CHARSET);
+ lex->alter_info.flags|= ALTER_CONVERT;
+ }
| create_table_options_space_separated
- {
- LEX *lex=Lex;
- lex->alter_info.flags|= ALTER_OPTIONS;
- }
- | FORCE_SYM
- {
- Lex->alter_info.flags|= ALTER_FORCE;
- }
- | alter_order_clause
- {
- LEX *lex=Lex;
- lex->alter_info.flags|= ALTER_ORDER;
- };
+ {
+ LEX *lex=Lex;
+ lex->alter_info.flags|= ALTER_OPTIONS;
+ }
+ | FORCE_SYM
+ {
+ Lex->alter_info.flags|= ALTER_FORCE;
+ }
+ | alter_order_clause
+ {
+ LEX *lex=Lex;
+ lex->alter_info.flags|= ALTER_ORDER;
+ }
+ ;
opt_column:
- /* empty */ {}
- | COLUMN_SYM {};
+ /* empty */ {}
+ | COLUMN_SYM {}
+ ;
opt_ignore:
- /* empty */ { Lex->ignore= 0;}
- | IGNORE_SYM { Lex->ignore= 1;}
- ;
+ /* empty */ { Lex->ignore= 0;}
+ | IGNORE_SYM { Lex->ignore= 1;}
+ ;
opt_restrict:
- /* empty */ { Lex->drop_mode= DROP_DEFAULT; }
- | RESTRICT { Lex->drop_mode= DROP_RESTRICT; }
- | CASCADE { Lex->drop_mode= DROP_CASCADE; }
- ;
+ /* empty */ { Lex->drop_mode= DROP_DEFAULT; }
+ | RESTRICT { Lex->drop_mode= DROP_RESTRICT; }
+ | CASCADE { Lex->drop_mode= DROP_CASCADE; }
+ ;
opt_place:
- /* empty */ {}
- | AFTER_SYM ident { store_position_for_column($2.str); }
- | FIRST_SYM { store_position_for_column(first_keyword); };
+ /* empty */ {}
+ | AFTER_SYM ident { store_position_for_column($2.str); }
+ | FIRST_SYM { store_position_for_column(first_keyword); }
+ ;
opt_to:
- /* empty */ {}
- | TO_SYM {}
- | EQ {}
- | AS {};
+ /* empty */ {}
+ | TO_SYM {}
+ | EQ {}
+ | AS {}
+ ;
/*
SLAVE START and SLAVE STOP are deprecated. We keep them for compatibility.
*/
slave:
- START_SYM SLAVE slave_thread_opts
+ START_SYM SLAVE slave_thread_opts
{
- LEX *lex=Lex;
+ 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));
+ 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
{}
| STOP_SYM SLAVE slave_thread_opts
{
- LEX *lex=Lex;
+ LEX *lex=Lex;
lex->sql_command = SQLCOM_SLAVE_STOP;
- lex->type = 0;
+ lex->type = 0;
/* If you change this code don't forget to update SLAVE STOP too */
}
- | SLAVE START_SYM slave_thread_opts
- {
- 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 START_SYM slave_thread_opts
+ {
+ 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
{}
- | SLAVE STOP_SYM slave_thread_opts
- {
- LEX *lex=Lex;
- lex->sql_command = SQLCOM_SLAVE_STOP;
- lex->type = 0;
- }
+ | SLAVE STOP_SYM slave_thread_opts
+ {
+ LEX *lex=Lex;
+ lex->sql_command = SQLCOM_SLAVE_STOP;
+ lex->type = 0;
+ }
;
-
start:
- START_SYM TRANSACTION_SYM start_transaction_opts
- {
- LEX *lex= Lex;
- lex->sql_command= SQLCOM_BEGIN;
- lex->start_transaction_opt= $3;
- }
- ;
+ START_SYM TRANSACTION_SYM start_transaction_opts
+ {
+ LEX *lex= Lex;
+ lex->sql_command= SQLCOM_BEGIN;
+ lex->start_transaction_opt= $3;
+ }
+ ;
start_transaction_opts:
- /*empty*/ { $$ = 0; }
+ /*empty*/ { $$ = 0; }
| WITH CONSISTENT_SYM SNAPSHOT_SYM
- {
- $$= MYSQL_START_TRANS_OPT_WITH_CONS_SNAPSHOT;
- }
+ {
+ $$= MYSQL_START_TRANS_OPT_WITH_CONS_SNAPSHOT;
+ }
;
slave_thread_opts:
- { Lex->slave_thd_opt= 0; }
- slave_thread_opt_list
- {}
- ;
+ { Lex->slave_thd_opt= 0; }
+ slave_thread_opt_list
+ {}
+ ;
slave_thread_opt_list:
- slave_thread_opt
- | slave_thread_opt_list ',' slave_thread_opt
- ;
+ slave_thread_opt
+ | slave_thread_opt_list ',' slave_thread_opt
+ ;
slave_thread_opt:
- /*empty*/ {}
- | SQL_THREAD { Lex->slave_thd_opt|=SLAVE_SQL; }
- | RELAY_THREAD { Lex->slave_thd_opt|=SLAVE_IO; }
- ;
+ /*empty*/ {}
+ | SQL_THREAD { Lex->slave_thd_opt|=SLAVE_SQL; }
+ | RELAY_THREAD { Lex->slave_thd_opt|=SLAVE_IO; }
+ ;
slave_until:
- /*empty*/ {}
- | UNTIL_SYM slave_until_opts
+ /*empty*/ {}
+ | UNTIL_SYM slave_until_opts
{
LEX *lex=Lex;
if ((lex->mi.log_file_name || lex->mi.pos) &&
@@ -4085,158 +6085,177 @@ slave_until:
ER(ER_BAD_SLAVE_UNTIL_COND), MYF(0));
MYSQL_YYABORT;
}
-
}
- ;
+ ;
slave_until_opts:
- master_file_def
- | slave_until_opts ',' master_file_def ;
-
+ master_file_def
+ | 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;
- };
+ 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;
- };
+ 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;
- }
- table_list opt_checksum_type
- {}
- ;
+ CHECKSUM_SYM table_or_tables
+ {
+ LEX *lex=Lex;
+ lex->sql_command = SQLCOM_CHECKSUM;
+ }
+ table_list opt_checksum_type
+ {}
+ ;
opt_checksum_type:
- /* nothing */ { Lex->check_opt.flags= 0; }
- | QUICK { Lex->check_opt.flags= T_QUICK; }
- | EXTENDED_SYM { Lex->check_opt.flags= T_EXTEND; }
+ /* nothing */ { Lex->check_opt.flags= 0; }
+ | QUICK { Lex->check_opt.flags= T_QUICK; }
+ | EXTENDED_SYM { Lex->check_opt.flags= T_EXTEND; }
;
repair:
- REPAIR opt_no_write_to_binlog table_or_tables
- {
- LEX *lex=Lex;
- lex->sql_command = SQLCOM_REPAIR;
- lex->no_write_to_binlog= $2;
- lex->check_opt.init();
- }
- table_list opt_mi_repair_type
- {}
- ;
+ REPAIR opt_no_write_to_binlog table_or_tables
+ {
+ LEX *lex=Lex;
+ lex->sql_command = SQLCOM_REPAIR;
+ lex->no_write_to_binlog= $2;
+ lex->check_opt.init();
+ lex->alter_info.reset();
+ }
+ table_list opt_mi_repair_type
+ {}
+ ;
opt_mi_repair_type:
- /* empty */ { Lex->check_opt.flags = T_MEDIUM; }
- | mi_repair_types {};
+ /* empty */ { Lex->check_opt.flags = T_MEDIUM; }
+ | mi_repair_types {}
+ ;
mi_repair_types:
- mi_repair_type {}
- | mi_repair_type mi_repair_types {};
+ mi_repair_type {}
+ | mi_repair_type mi_repair_types {}
+ ;
mi_repair_type:
- QUICK { Lex->check_opt.flags|= T_QUICK; }
- | EXTENDED_SYM { Lex->check_opt.flags|= T_EXTEND; }
- | USE_FRM { Lex->check_opt.sql_flags|= TT_USEFRM; };
+ QUICK { Lex->check_opt.flags|= T_QUICK; }
+ | EXTENDED_SYM { Lex->check_opt.flags|= T_EXTEND; }
+ | USE_FRM { Lex->check_opt.sql_flags|= TT_USEFRM; }
+ ;
analyze:
- ANALYZE_SYM opt_no_write_to_binlog table_or_tables
- {
- LEX *lex=Lex;
- lex->sql_command = SQLCOM_ANALYZE;
- lex->no_write_to_binlog= $2;
- lex->check_opt.init();
- }
- table_list
- {}
- ;
+ ANALYZE_SYM opt_no_write_to_binlog table_or_tables
+ {
+ LEX *lex=Lex;
+ lex->sql_command = SQLCOM_ANALYZE;
+ lex->no_write_to_binlog= $2;
+ lex->check_opt.init();
+ lex->alter_info.reset();
+ }
+ table_list
+ {}
+ ;
+
+binlog_base64_event:
+ BINLOG_SYM TEXT_STRING_sys
+ {
+ Lex->sql_command = SQLCOM_BINLOG_BASE64_EVENT;
+ Lex->comment= $2;
+ }
+ ;
check:
- CHECK_SYM table_or_tables
- {
- LEX *lex=Lex;
-
- if (lex->sphead)
- {
- my_error(ER_SP_BADSTATEMENT, MYF(0), "CHECK");
- MYSQL_YYABORT;
- }
- lex->sql_command = SQLCOM_CHECK;
- lex->check_opt.init();
- }
- table_list opt_mi_check_type
- {}
- ;
+ CHECK_SYM table_or_tables
+ {
+ LEX *lex=Lex;
+
+ if (lex->sphead)
+ {
+ my_error(ER_SP_BADSTATEMENT, MYF(0), "CHECK");
+ MYSQL_YYABORT;
+ }
+ lex->sql_command = SQLCOM_CHECK;
+ lex->check_opt.init();
+ lex->alter_info.reset();
+ }
+ table_list opt_mi_check_type
+ {}
+ ;
opt_mi_check_type:
- /* empty */ { Lex->check_opt.flags = T_MEDIUM; }
- | mi_check_types {};
+ /* empty */ { Lex->check_opt.flags = T_MEDIUM; }
+ | mi_check_types {}
+ ;
mi_check_types:
- mi_check_type {}
- | mi_check_type mi_check_types {};
+ mi_check_type {}
+ | mi_check_type mi_check_types {}
+ ;
mi_check_type:
- QUICK { Lex->check_opt.flags|= T_QUICK; }
- | FAST_SYM { Lex->check_opt.flags|= T_FAST; }
- | MEDIUM_SYM { Lex->check_opt.flags|= T_MEDIUM; }
- | EXTENDED_SYM { Lex->check_opt.flags|= T_EXTEND; }
- | CHANGED { Lex->check_opt.flags|= T_CHECK_ONLY_CHANGED; }
- | FOR_SYM UPGRADE_SYM { Lex->check_opt.sql_flags|= TT_FOR_UPGRADE; };
+ QUICK { Lex->check_opt.flags|= T_QUICK; }
+ | FAST_SYM { Lex->check_opt.flags|= T_FAST; }
+ | MEDIUM_SYM { Lex->check_opt.flags|= T_MEDIUM; }
+ | EXTENDED_SYM { Lex->check_opt.flags|= T_EXTEND; }
+ | CHANGED { Lex->check_opt.flags|= T_CHECK_ONLY_CHANGED; }
+ | FOR_SYM UPGRADE_SYM { Lex->check_opt.sql_flags|= TT_FOR_UPGRADE; }
+ ;
optimize:
- OPTIMIZE opt_no_write_to_binlog table_or_tables
- {
- LEX *lex=Lex;
- lex->sql_command = SQLCOM_OPTIMIZE;
- lex->no_write_to_binlog= $2;
- lex->check_opt.init();
- }
- table_list
- {}
- ;
+ OPTIMIZE opt_no_write_to_binlog table_or_tables
+ {
+ LEX *lex=Lex;
+ lex->sql_command = SQLCOM_OPTIMIZE;
+ lex->no_write_to_binlog= $2;
+ lex->check_opt.init();
+ lex->alter_info.reset();
+ }
+ table_list
+ {}
+ ;
opt_no_write_to_binlog:
- /* empty */ { $$= 0; }
- | NO_WRITE_TO_BINLOG { $$= 1; }
- | LOCAL_SYM { $$= 1; }
- ;
+ /* empty */ { $$= 0; }
+ | NO_WRITE_TO_BINLOG { $$= 1; }
+ | LOCAL_SYM { $$= 1; }
+ ;
rename:
- RENAME table_or_tables
- {
- Lex->sql_command= SQLCOM_RENAME_TABLE;
- }
- table_to_table_list
- {}
- | RENAME USER clear_privileges rename_list
+ RENAME table_or_tables
{
- Lex->sql_command = SQLCOM_RENAME_USER;
+ Lex->sql_command= SQLCOM_RENAME_TABLE;
}
- ;
+ table_to_table_list
+ {}
+ | RENAME USER clear_privileges rename_list
+ {
+ Lex->sql_command = SQLCOM_RENAME_USER;
+ }
+ ;
rename_list:
- user TO_SYM user
- {
- if (Lex->users_list.push_back($1) || Lex->users_list.push_back($3))
- MYSQL_YYABORT;
- }
+ user TO_SYM user
+ {
+ if (Lex->users_list.push_back($1) || Lex->users_list.push_back($3))
+ MYSQL_YYABORT;
+ }
| rename_list ',' user TO_SYM user
{
if (Lex->users_list.push_back($3) || Lex->users_list.push_back($5))
@@ -4245,103 +6264,96 @@ rename_list:
;
table_to_table_list:
- table_to_table
- | table_to_table_list ',' table_to_table;
+ table_to_table
+ | table_to_table_list ',' table_to_table
+ ;
table_to_table:
- table_ident TO_SYM table_ident
- {
- LEX *lex=Lex;
- SELECT_LEX *sl= lex->current_select;
- if (!sl->add_table_to_list(lex->thd, $1,NULL,TL_OPTION_UPDATING,
- TL_IGNORE) ||
- !sl->add_table_to_list(lex->thd, $3,NULL,TL_OPTION_UPDATING,
- TL_IGNORE))
- MYSQL_YYABORT;
- };
+ table_ident TO_SYM table_ident
+ {
+ LEX *lex=Lex;
+ SELECT_LEX *sl= lex->current_select;
+ if (!sl->add_table_to_list(lex->thd, $1,NULL,TL_OPTION_UPDATING,
+ TL_IGNORE) ||
+ !sl->add_table_to_list(lex->thd, $3,NULL,TL_OPTION_UPDATING,
+ TL_IGNORE))
+ MYSQL_YYABORT;
+ }
+ ;
keycache:
- CACHE_SYM INDEX_SYM keycache_list IN_SYM key_cache_name
- {
- LEX *lex=Lex;
- lex->sql_command= SQLCOM_ASSIGN_TO_KEYCACHE;
- lex->ident= $5;
- }
+ CACHE_SYM INDEX_SYM keycache_list IN_SYM key_cache_name
+ {
+ LEX *lex=Lex;
+ lex->sql_command= SQLCOM_ASSIGN_TO_KEYCACHE;
+ lex->ident= $5;
+ }
;
keycache_list:
- assign_to_keycache
- | keycache_list ',' assign_to_keycache;
+ assign_to_keycache
+ | keycache_list ',' assign_to_keycache
+ ;
assign_to_keycache:
- table_ident cache_keys_spec
- {
- LEX *lex=Lex;
- SELECT_LEX *sel= &lex->select_lex;
- if (!sel->add_table_to_list(lex->thd, $1, NULL, 0,
- TL_READ,
- sel->get_use_index(),
- (List<String> *)0))
- MYSQL_YYABORT;
- }
+ table_ident cache_keys_spec
+ {
+ if (!Select->add_table_to_list(YYTHD, $1, NULL, 0, TL_READ,
+ Select->pop_index_hints()))
+ MYSQL_YYABORT;
+ }
;
key_cache_name:
- ident { $$= $1; }
- | DEFAULT { $$ = default_key_cache_base; }
- ;
+ ident { $$= $1; }
+ | DEFAULT { $$ = default_key_cache_base; }
+ ;
preload:
- LOAD INDEX_SYM INTO CACHE_SYM
- {
- LEX *lex=Lex;
- lex->sql_command=SQLCOM_PRELOAD_KEYS;
- }
- preload_list
- {}
- ;
+ LOAD INDEX_SYM INTO CACHE_SYM
+ {
+ LEX *lex=Lex;
+ lex->sql_command=SQLCOM_PRELOAD_KEYS;
+ }
+ preload_list
+ {}
+ ;
preload_list:
- preload_keys
- | preload_list ',' preload_keys;
+ preload_keys
+ | preload_list ',' preload_keys
+ ;
preload_keys:
- table_ident cache_keys_spec opt_ignore_leaves
- {
- LEX *lex=Lex;
- SELECT_LEX *sel= &lex->select_lex;
- if (!sel->add_table_to_list(lex->thd, $1, NULL, $3,
- TL_READ,
- sel->get_use_index(),
- (List<String> *)0))
- MYSQL_YYABORT;
- }
- ;
+ table_ident cache_keys_spec opt_ignore_leaves
+ {
+ if (!Select->add_table_to_list(YYTHD, $1, NULL, $3, TL_READ,
+ Select->pop_index_hints()))
+ MYSQL_YYABORT;
+ }
+ ;
cache_keys_spec:
- { Select->interval_list.empty(); }
- cache_key_list_or_empty
- {
- LEX *lex=Lex;
- SELECT_LEX *sel= &lex->select_lex;
- sel->use_index= sel->interval_list;
- }
+ {
+ Lex->select_lex.alloc_index_hints(YYTHD);
+ Select->set_index_hint_type(INDEX_HINT_USE,
+ global_system_variables.old_mode ?
+ INDEX_HINT_MASK_JOIN :
+ INDEX_HINT_MASK_ALL);
+ }
+ cache_key_list_or_empty
;
cache_key_list_or_empty:
- /* empty */ { Lex->select_lex.use_index_ptr= 0; }
- | opt_key_or_index '(' key_usage_list2 ')'
- {
- SELECT_LEX *sel= &Lex->select_lex;
- sel->use_index_ptr= &sel->use_index;
- }
- ;
+ /* empty */ { }
+ | key_or_index '(' opt_key_usage_list ')'
+ ;
opt_ignore_leaves:
- /* empty */
- { $$= 0; }
- | IGNORE_SYM LEAVES { $$= TL_OPTION_IGNORE_LEAVES; }
- ;
+ /* empty */
+ { $$= 0; }
+ | IGNORE_SYM LEAVES { $$= TL_OPTION_IGNORE_LEAVES; }
+ ;
/*
Select : retrieve data from table
@@ -4349,29 +6361,29 @@ opt_ignore_leaves:
select:
- select_init
- {
- LEX *lex= Lex;
- lex->sql_command= SQLCOM_SELECT;
- }
- ;
+ select_init
+ {
+ LEX *lex= Lex;
+ lex->sql_command= SQLCOM_SELECT;
+ }
+ ;
/* Need select_init2 for subselects. */
select_init:
- SELECT_SYM select_init2
- |
- '(' select_paren ')' union_opt;
+ SELECT_SYM select_init2
+ | '(' select_paren ')' union_opt
+ ;
select_paren:
- SELECT_SYM select_part2
- {
- LEX *lex= Lex;
+ SELECT_SYM select_part2
+ {
+ LEX *lex= Lex;
SELECT_LEX * sel= lex->current_select;
- if (sel->set_braces(1))
- {
+ if (sel->set_braces(1))
+ {
my_parse_error(ER(ER_SYNTAX_ERROR));
- MYSQL_YYABORT;
- }
+ MYSQL_YYABORT;
+ }
if (sel->linkage == UNION_TYPE &&
!sel->master_unit()->first_select()->braces &&
sel->master_unit()->first_select()->linkage ==
@@ -4389,58 +6401,59 @@ select_paren:
MYSQL_YYABORT;
}
/* select in braces, can't contain global parameters */
- if (sel->master_unit()->fake_select_lex)
+ if (sel->master_unit()->fake_select_lex)
sel->master_unit()->global_parameters=
sel->master_unit()->fake_select_lex;
}
- | '(' select_paren ')';
+ | '(' select_paren ')'
+ ;
select_init2:
- select_part2
- {
- LEX *lex= Lex;
- if (lex == NULL)
- MYSQL_YYABORT;
- 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;
- }
- }
- union_clause
- ;
+ select_part2
+ {
+ 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;
+ }
+ }
+ union_clause
+ ;
select_part2:
- {
- LEX *lex= Lex;
- SELECT_LEX *sel= lex->current_select;
- if (sel->linkage != UNION_TYPE)
- mysql_init_select(lex);
- lex->current_select->parsing_place= SELECT_LIST;
- }
- select_options select_item_list
- {
- Select->parsing_place= NO_MATTER;
- }
- select_into select_lock_type;
+ {
+ LEX *lex= Lex;
+ SELECT_LEX *sel= lex->current_select;
+ if (sel->linkage != UNION_TYPE)
+ mysql_init_select(lex);
+ lex->current_select->parsing_place= SELECT_LIST;
+ }
+ select_options select_item_list
+ {
+ Select->parsing_place= NO_MATTER;
+ }
+ select_into select_lock_type
+ ;
select_into:
- opt_order_clause opt_limit_clause {}
+ opt_order_clause opt_limit_clause {}
| into
- | select_from
- | into select_from
- | select_from into;
+ | select_from
+ | into select_from
+ | select_from into
+ ;
select_from:
- FROM join_table_list where_clause group_clause having_clause
- opt_order_clause opt_limit_clause procedure_clause
+ FROM join_table_list where_clause group_clause having_clause
+ opt_order_clause opt_limit_clause procedure_clause
{
Select->context.table_list=
Select->context.first_name_resolution_table=
@@ -4451,55 +6464,56 @@ select_from:
and DUAL is system table without fields.
Is "SELECT 1 FROM DUAL" any better than "SELECT 1" ?
Hmmm :) */
- ;
+ ;
select_options:
- /* empty*/
- | select_option_list
- {
- if (test_all_bits(Select->options, SELECT_ALL | SELECT_DISTINCT))
- {
- my_error(ER_WRONG_USAGE, MYF(0), "ALL", "DISTINCT");
+ /* empty*/
+ | select_option_list
+ {
+ if (Select->options & SELECT_DISTINCT && Select->options & SELECT_ALL)
+ {
+ my_error(ER_WRONG_USAGE, MYF(0), "ALL", "DISTINCT");
MYSQL_YYABORT;
- }
+ }
}
- ;
+ ;
select_option_list:
- select_option_list select_option
- | select_option;
+ select_option_list select_option
+ | select_option
+ ;
select_option:
- STRAIGHT_JOIN { Select->options|= SELECT_STRAIGHT_JOIN; }
- | HIGH_PRIORITY
- {
- if (check_simple_select())
- MYSQL_YYABORT;
- Lex->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())
- MYSQL_YYABORT;
- Select->options|= OPTION_BUFFER_RESULT;
- }
- | SQL_CALC_FOUND_ROWS
- {
- if (check_simple_select())
- MYSQL_YYABORT;
- Select->options|= OPTION_FOUND_ROWS;
- }
- | SQL_NO_CACHE_SYM
+ STRAIGHT_JOIN { Select->options|= SELECT_STRAIGHT_JOIN; }
+ | HIGH_PRIORITY
+ {
+ if (check_simple_select())
+ MYSQL_YYABORT;
+ Lex->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())
+ MYSQL_YYABORT;
+ Select->options|= OPTION_BUFFER_RESULT;
+ }
+ | SQL_CALC_FOUND_ROWS
+ {
+ if (check_simple_select())
+ 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.options&= ~OPTION_TO_QUERY_CACHE;
Lex->select_lex.sql_cache= SELECT_LEX::SQL_NO_CACHE;
}
- | SQL_CACHE_SYM
- {
+ | SQL_CACHE_SYM
+ {
/*
Honor this flag only if SQL_NO_CACHE wasn't specified AND
we are parsing the outermost SELECT in the query.
@@ -4508,55 +6522,56 @@ select_option:
Lex->current_select == &Lex->select_lex)
{
Lex->safe_to_cache_query=1;
- Lex->select_lex.options|= OPTION_TO_QUERY_CACHE;
+ Lex->select_lex.options|= OPTION_TO_QUERY_CACHE;
Lex->select_lex.sql_cache= SELECT_LEX::SQL_CACHE;
}
- }
- | ALL { Select->options|= SELECT_ALL; }
- ;
+ }
+ | ALL { Select->options|= SELECT_ALL; }
+ ;
select_lock_type:
- /* empty */
- | FOR_SYM UPDATE_SYM
- {
- LEX *lex=Lex;
- lex->current_select->set_lock_for_tables(TL_WRITE);
- lex->safe_to_cache_query=0;
- }
- | LOCK_SYM IN_SYM SHARE_SYM MODE_SYM
- {
- LEX *lex=Lex;
- lex->current_select->
- set_lock_for_tables(TL_READ_WITH_SHARED_LOCKS);
- lex->safe_to_cache_query=0;
- }
- ;
+ /* empty */
+ | FOR_SYM UPDATE_SYM
+ {
+ LEX *lex=Lex;
+ lex->current_select->set_lock_for_tables(TL_WRITE);
+ lex->safe_to_cache_query=0;
+ }
+ | LOCK_SYM IN_SYM SHARE_SYM MODE_SYM
+ {
+ LEX *lex=Lex;
+ lex->current_select->
+ set_lock_for_tables(TL_READ_WITH_SHARED_LOCKS);
+ lex->safe_to_cache_query=0;
+ }
+ ;
select_item_list:
- select_item_list ',' select_item
- | select_item
- | '*'
- {
- THD *thd= YYTHD;
- Item *item= new Item_field(&thd->lex->current_select->context,
- NULL, NULL, "*");
+ select_item_list ',' select_item
+ | select_item
+ | '*'
+ {
+ THD *thd= YYTHD;
+ Item *item= new (thd->mem_root)
+ Item_field(&thd->lex->current_select->context,
+ NULL, NULL, "*");
if (item == NULL)
MYSQL_YYABORT;
- if (add_item_to_list(thd, item))
- MYSQL_YYABORT;
- (thd->lex->current_select->with_wild)++;
- };
-
+ if (add_item_to_list(thd, item))
+ MYSQL_YYABORT;
+ (thd->lex->current_select->with_wild)++;
+ }
+ ;
select_item:
- remember_name select_item2 remember_end select_alias
- {
+ remember_name select_item2 remember_end select_alias
+ {
THD *thd= YYTHD;
DBUG_ASSERT($1 < $3);
- if (add_item_to_list(thd, $2))
- MYSQL_YYABORT;
- if ($4.str)
+ if (add_item_to_list(thd, $2))
+ MYSQL_YYABORT;
+ if ($4.str)
{
if (Lex->sql_command == SQLCOM_CREATE_VIEW &&
check_column_name($4.str))
@@ -4565,40 +6580,44 @@ select_item:
MYSQL_YYABORT;
}
$2->is_autogenerated_name= FALSE;
- $2->set_name($4.str, $4.length, system_charset_info);
+ $2->set_name($4.str, $4.length, system_charset_info);
}
- else if (!$2->name)
+ else if (!$2->name)
{
- $2->set_name($1, (uint) ($3 - $1), thd->charset());
- }
- };
-
+ $2->set_name($1, (uint) ($3 - $1), thd->charset());
+ }
+ }
+ ;
remember_name:
- {
- $$= (char*) YYLIP->tok_start;
- };
+ {
+ $$= (char*) YYLIP->get_cpp_tok_start();
+ }
+ ;
remember_end:
- {
- $$=(char*) YYLIP->tok_end;
- };
+ {
+ $$= (char*) YYLIP->get_cpp_tok_end();
+ }
+ ;
select_item2:
- table_wild { $$=$1; } /* table.* */
- | expr { $$=$1; };
+ table_wild { $$=$1; /* table.* */ }
+ | expr { $$=$1; }
+ ;
select_alias:
- /* empty */ { $$=null_lex_str;}
- | AS ident { $$=$2; }
- | AS TEXT_STRING_sys { $$=$2; }
- | ident { $$=$1; }
- | TEXT_STRING_sys { $$=$1; }
- ;
+ /* empty */ { $$=null_lex_str;}
+ | AS ident { $$=$2; }
+ | AS TEXT_STRING_sys { $$=$2; }
+ | ident { $$=$1; }
+ | TEXT_STRING_sys { $$=$1; }
+ ;
optional_braces:
- /* empty */ {}
- | '(' ')' {};
+ /* empty */ {}
+ | '(' ')' {}
+ ;
/* all possible expressions */
expr:
@@ -4703,7 +6722,7 @@ expr:
MYSQL_YYABORT;
}
}
- | NOT_SYM expr %prec NOT_SYM
+ | NOT_SYM expr %prec NOT_SYM
{
$$= negate_expression(YYTHD, $2);
if ($$ == NULL)
@@ -4735,13 +6754,13 @@ expr:
}
| bool_pri IS UNKNOWN_SYM %prec IS
{
- $$= new Item_func_isnull($1);
+ $$= new (YYTHD->mem_root) Item_func_isnull($1);
if ($$ == NULL)
MYSQL_YYABORT;
}
| bool_pri IS not UNKNOWN_SYM %prec IS
{
- $$= new Item_func_isnotnull($1);
+ $$= new (YYTHD->mem_root) Item_func_isnotnull($1);
if ($$ == NULL)
MYSQL_YYABORT;
}
@@ -4749,37 +6768,38 @@ expr:
;
bool_pri:
- bool_pri IS NULL_SYM %prec IS
+ bool_pri IS NULL_SYM %prec IS
{
- $$= new Item_func_isnull($1);
+ $$= new (YYTHD->mem_root) Item_func_isnull($1);
if ($$ == NULL)
MYSQL_YYABORT;
}
- | bool_pri IS not NULL_SYM %prec IS
+ | bool_pri IS not NULL_SYM %prec IS
{
- $$= new Item_func_isnotnull($1);
+ $$= new (YYTHD->mem_root) Item_func_isnotnull($1);
if ($$ == NULL)
MYSQL_YYABORT;
}
- | bool_pri EQUAL_SYM predicate %prec EQUAL_SYM
+ | bool_pri EQUAL_SYM predicate %prec EQUAL_SYM
{
- $$= new Item_func_equal($1,$3);
+ $$= new (YYTHD->mem_root) Item_func_equal($1,$3);
if ($$ == NULL)
MYSQL_YYABORT;
}
- | bool_pri comp_op predicate %prec EQ
- {
+ | bool_pri comp_op predicate %prec EQ
+ {
$$= (*$2)(0)->create($1,$3);
if ($$ == NULL)
MYSQL_YYABORT;
}
- | bool_pri comp_op all_or_any '(' subselect ')' %prec EQ
- {
+ | bool_pri comp_op all_or_any '(' subselect ')' %prec EQ
+ {
$$= all_any_subquery_creator($1, $2, $3, $5);
if ($$ == NULL)
MYSQL_YYABORT;
}
- | predicate ;
+ | predicate
+ ;
predicate:
bit_expr IN_SYM '(' subselect ')'
@@ -4828,1013 +6848,911 @@ predicate:
item->negate();
$$= item;
}
- | bit_expr BETWEEN_SYM bit_expr AND_SYM predicate
- {
- $$= new Item_func_between($1,$3,$5);
+ | bit_expr BETWEEN_SYM bit_expr AND_SYM predicate
+ {
+ $$= new (YYTHD->mem_root) Item_func_between($1,$3,$5);
if ($$ == NULL)
MYSQL_YYABORT;
}
- | bit_expr not BETWEEN_SYM bit_expr AND_SYM predicate
+ | bit_expr not BETWEEN_SYM bit_expr AND_SYM predicate
{
- Item_func_between *item= new Item_func_between($1,$4,$6);
+ Item_func_between *item;
+ item= new (YYTHD->mem_root) Item_func_between($1,$4,$6);
if (item == NULL)
MYSQL_YYABORT;
item->negate();
$$= item;
}
- | bit_expr SOUNDS_SYM LIKE bit_expr
- {
- Item *item1= new Item_func_soundex($1);
- Item *item4= new Item_func_soundex($4);
+ | 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);
if ((item1 == NULL) || (item4 == NULL))
MYSQL_YYABORT;
- $$= new Item_func_eq(item1, item4);
+ $$= new (YYTHD->mem_root) Item_func_eq(item1, item4);
if ($$ == NULL)
MYSQL_YYABORT;
}
- | bit_expr LIKE simple_expr opt_escape
+ | bit_expr LIKE simple_expr opt_escape
{
- $$= new Item_func_like($1,$3,$4,Lex->escape_used);
+ $$= new (YYTHD->mem_root) Item_func_like($1,$3,$4,Lex->escape_used);
if ($$ == NULL)
MYSQL_YYABORT;
}
- | bit_expr not LIKE simple_expr opt_escape
+ | bit_expr not LIKE simple_expr opt_escape
{
- Item *item= new Item_func_like($1,$4,$5, Lex->escape_used);
+ Item *item= new (YYTHD->mem_root) Item_func_like($1,$4,$5,
+ Lex->escape_used);
if (item == NULL)
MYSQL_YYABORT;
- $$= new Item_func_not(item);
+ $$= new (YYTHD->mem_root) Item_func_not(item);
if ($$ == NULL)
MYSQL_YYABORT;
}
- | bit_expr REGEXP bit_expr
+ | bit_expr REGEXP bit_expr
{
- $$= new Item_func_regex($1,$3);
+ $$= new (YYTHD->mem_root) Item_func_regex($1,$3);
if ($$ == NULL)
MYSQL_YYABORT;
}
- | bit_expr not REGEXP bit_expr
+ | bit_expr not REGEXP bit_expr
{
- Item *item= new Item_func_regex($1,$4);
+ Item *item= new (YYTHD->mem_root) Item_func_regex($1,$4);
if (item == NULL)
MYSQL_YYABORT;
$$= negate_expression(YYTHD, item);
if ($$ == NULL)
MYSQL_YYABORT;
}
- | bit_expr ;
+ | bit_expr
+ ;
bit_expr:
bit_expr '|' bit_expr %prec '|'
{
- $$= new Item_func_bit_or($1,$3);
+ $$= new (YYTHD->mem_root) Item_func_bit_or($1,$3);
if ($$ == NULL)
MYSQL_YYABORT;
}
| bit_expr '&' bit_expr %prec '&'
{
- $$= new Item_func_bit_and($1,$3);
+ $$= new (YYTHD->mem_root) Item_func_bit_and($1,$3);
if ($$ == NULL)
MYSQL_YYABORT;
}
| bit_expr SHIFT_LEFT bit_expr %prec SHIFT_LEFT
{
- $$= new Item_func_shift_left($1,$3);
+ $$= new (YYTHD->mem_root) Item_func_shift_left($1,$3);
if ($$ == NULL)
MYSQL_YYABORT;
}
| bit_expr SHIFT_RIGHT bit_expr %prec SHIFT_RIGHT
{
- $$= new Item_func_shift_right($1,$3);
+ $$= new (YYTHD->mem_root) Item_func_shift_right($1,$3);
if ($$ == NULL)
MYSQL_YYABORT;
}
| bit_expr '+' bit_expr %prec '+'
{
- $$= new Item_func_plus($1,$3);
+ $$= new (YYTHD->mem_root) Item_func_plus($1,$3);
if ($$ == NULL)
MYSQL_YYABORT;
}
| bit_expr '-' bit_expr %prec '-'
{
- $$= new Item_func_minus($1,$3);
+ $$= new (YYTHD->mem_root) Item_func_minus($1,$3);
if ($$ == NULL)
MYSQL_YYABORT;
}
- | bit_expr '+' interval_expr interval %prec '+'
+ | bit_expr '+' INTERVAL_SYM expr interval %prec '+'
{
- $$= new Item_date_add_interval($1,$3,$4,0);
+ $$= new (YYTHD->mem_root) Item_date_add_interval($1,$4,$5,0);
if ($$ == NULL)
MYSQL_YYABORT;
}
- | bit_expr '-' interval_expr interval %prec '-'
+ | bit_expr '-' INTERVAL_SYM expr interval %prec '-'
{
- $$= new Item_date_add_interval($1,$3,$4,1);
+ $$= new (YYTHD->mem_root) Item_date_add_interval($1,$4,$5,1);
if ($$ == NULL)
MYSQL_YYABORT;
}
| bit_expr '*' bit_expr %prec '*'
{
- $$= new Item_func_mul($1,$3);
+ $$= new (YYTHD->mem_root) Item_func_mul($1,$3);
if ($$ == NULL)
MYSQL_YYABORT;
}
| bit_expr '/' bit_expr %prec '/'
{
- $$= new Item_func_div($1,$3);
+ $$= new (YYTHD->mem_root) Item_func_div($1,$3);
if ($$ == NULL)
MYSQL_YYABORT;
}
| bit_expr '%' bit_expr %prec '%'
{
- $$= new Item_func_mod($1,$3);
+ $$= new (YYTHD->mem_root) Item_func_mod($1,$3);
if ($$ == NULL)
MYSQL_YYABORT;
}
| bit_expr DIV_SYM bit_expr %prec DIV_SYM
{
- $$= new Item_func_int_div($1,$3);
+ $$= new (YYTHD->mem_root) Item_func_int_div($1,$3);
if ($$ == NULL)
MYSQL_YYABORT;
}
| bit_expr MOD_SYM bit_expr %prec MOD_SYM
{
- $$= new Item_func_mod($1,$3);
+ $$= new (YYTHD->mem_root) Item_func_mod($1,$3);
if ($$ == NULL)
MYSQL_YYABORT;
}
| bit_expr '^' bit_expr
{
- $$= new Item_func_bit_xor($1,$3);
+ $$= new (YYTHD->mem_root) Item_func_bit_xor($1,$3);
if ($$ == NULL)
MYSQL_YYABORT;
}
| simple_expr
;
-or: OR_SYM | OR2_SYM;
-and: AND_SYM | AND_AND_SYM;
-not: NOT_SYM | NOT2_SYM;
-not2: '!' | NOT2_SYM;
+or:
+ OR_SYM
+ | OR2_SYM
+ ;
-comp_op: EQ { $$ = &comp_eq_creator; }
- | GE { $$ = &comp_ge_creator; }
- | GT_SYM { $$ = &comp_gt_creator; }
- | LE { $$ = &comp_le_creator; }
- | LT { $$ = &comp_lt_creator; }
- | NE { $$ = &comp_ne_creator; }
- ;
+and:
+ AND_SYM
+ | AND_AND_SYM
+ ;
+
+not:
+ NOT_SYM
+ | NOT2_SYM
+ ;
+
+not2:
+ '!'
+ | NOT2_SYM
+ ;
-all_or_any: ALL { $$ = 1; }
- | ANY_SYM { $$ = 0; }
+comp_op:
+ EQ { $$ = &comp_eq_creator; }
+ | GE { $$ = &comp_ge_creator; }
+ | GT_SYM { $$ = &comp_gt_creator; }
+ | LE { $$ = &comp_le_creator; }
+ | LT { $$ = &comp_lt_creator; }
+ | NE { $$ = &comp_ne_creator; }
;
-interval_expr:
- INTERVAL_SYM expr %prec INTERVAL_SYM
- { $$=$2; }
+all_or_any:
+ ALL { $$ = 1; }
+ | ANY_SYM { $$ = 0; }
;
simple_expr:
- simple_ident
- | simple_expr COLLATE_SYM ident_or_text %prec NEG
- {
- Item *item= new Item_string($3.str, $3.length, YYTHD->charset());
- if (item == NULL)
+ simple_ident
+ | function_call_keyword
+ | function_call_nonkeyword
+ | function_call_generic
+ | 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());
+ if (i1 == NULL)
MYSQL_YYABORT;
- $$= new Item_func_set_collation($1, item);
+ $$= new (thd->mem_root) Item_func_set_collation($1, i1);
if ($$ == NULL)
MYSQL_YYABORT;
- }
- | literal
- | param_marker
- | variable
- | sum_expr
- | simple_expr OR_OR_SYM simple_expr
- {
- $$= new Item_func_concat($1, $3);
+ }
+ | literal
+ | param_marker
+ | variable
+ | sum_expr
+ | simple_expr OR_OR_SYM simple_expr
+ {
+ $$= new (YYTHD->mem_root) Item_func_concat($1, $3);
if ($$ == NULL)
MYSQL_YYABORT;
}
- | '+' simple_expr %prec NEG
- { $$= $2; }
- | '-' simple_expr %prec NEG
+ | '+' simple_expr %prec NEG
+ {
+ $$= $2;
+ }
+ | '-' simple_expr %prec NEG
{
- $$= new Item_func_neg($2);
+ $$= new (YYTHD->mem_root) Item_func_neg($2);
if ($$ == NULL)
MYSQL_YYABORT;
}
- | '~' simple_expr %prec NEG
+ | '~' simple_expr %prec NEG
{
- $$= new Item_func_bit_neg($2);
+ $$= new (YYTHD->mem_root) Item_func_bit_neg($2);
if ($$ == NULL)
MYSQL_YYABORT;
}
- | not2 simple_expr %prec NEG
+ | not2 simple_expr %prec NEG
{
$$= negate_expression(YYTHD, $2);
if ($$ == NULL)
MYSQL_YYABORT;
}
- | '(' subselect ')'
+ | '(' subselect ')'
{
- $$= new Item_singlerow_subselect($2);
+ $$= new (YYTHD->mem_root) Item_singlerow_subselect($2);
if ($$ == NULL)
MYSQL_YYABORT;
}
- | '(' expr ')' { $$= $2; }
- | '(' expr ',' expr_list ')'
- {
- $4->push_front($2);
- $$= new Item_row(*$4);
+ | '(' expr ')'
+ { $$= $2; }
+ | '(' expr ',' expr_list ')'
+ {
+ $4->push_front($2);
+ $$= new (YYTHD->mem_root) Item_row(*$4);
if ($$ == NULL)
MYSQL_YYABORT;
- }
- | ROW_SYM '(' expr ',' expr_list ')'
- {
- $5->push_front($3);
- $$= new Item_row(*$5);
+ }
+ | ROW_SYM '(' expr ',' expr_list ')'
+ {
+ $5->push_front($3);
+ $$= new (YYTHD->mem_root) Item_row(*$5);
if ($$ == NULL)
MYSQL_YYABORT;
- }
- | EXISTS '(' subselect ')'
+ }
+ | EXISTS '(' subselect ')'
{
- $$= new Item_exists_subselect($3);
+ $$= new (YYTHD->mem_root) Item_exists_subselect($3);
if ($$ == NULL)
MYSQL_YYABORT;
}
- | '{' ident expr '}'
+ | '{' ident expr '}'
{ $$= $3; }
| MATCH ident_list_arg AGAINST '(' bit_expr fulltext_options ')'
{
$2->push_front($5);
- Item_func_match *item= new Item_func_match(*$2,$6);
- if (item == NULL)
+ Item_func_match *i1= new (YYTHD->mem_root) Item_func_match(*$2, $6);
+ if (i1 == NULL)
MYSQL_YYABORT;
- Select->add_ftfunc_to_list(item);
- $$= item;
+ Select->add_ftfunc_to_list(i1);
+ $$= i1;
}
- | ASCII_SYM '(' expr ')'
+ | BINARY simple_expr %prec NEG
{
- $$= new Item_func_ascii($3);
+ $$= create_func_cast(YYTHD, $2, ITEM_CAST_CHAR, NULL, NULL,
+ &my_charset_bin);
if ($$ == NULL)
MYSQL_YYABORT;
}
- | BINARY simple_expr %prec NEG
- {
- $$= create_func_cast($2, ITEM_CAST_CHAR, NULL, NULL, &my_charset_bin);
- if ($$ == NULL)
- MYSQL_YYABORT;
- }
- | CAST_SYM '(' expr AS cast_type ')'
- {
+ | CAST_SYM '(' expr AS cast_type ')'
+ {
LEX *lex= Lex;
- $$= create_func_cast($3, $5, lex->length, lex->dec, lex->charset);
- if ($$ == NULL)
- MYSQL_YYABORT;
- }
- | CASE_SYM opt_expr when_list opt_else END
- {
- $$= new Item_func_case(* $3, $2, $4 );
+ $$= create_func_cast(YYTHD, $3, $5, lex->length, lex->dec,
+ lex->charset);
if ($$ == NULL)
MYSQL_YYABORT;
}
- | CONVERT_SYM '(' expr ',' cast_type ')'
- {
- $$= create_func_cast($3, $5, Lex->length, Lex->dec, Lex->charset);
- if ($$ == NULL)
- MYSQL_YYABORT;
- }
- | CONVERT_SYM '(' expr USING charset_name ')'
- {
- $$= new Item_func_conv_charset($3,$5);
+ | CASE_SYM opt_expr when_list opt_else END
+ {
+ $$= new (YYTHD->mem_root) Item_func_case(* $3, $2, $4 );
if ($$ == NULL)
MYSQL_YYABORT;
}
- | DEFAULT '(' simple_ident ')'
- {
- if ($3->is_splocal())
- {
- Item_splocal *il= static_cast<Item_splocal *>($3);
-
- my_error(ER_WRONG_COLUMN_NAME, MYF(0), il->my_name()->str);
- MYSQL_YYABORT;
- }
- $$= new Item_default_value(Lex->current_context(), $3);
- if ($$ == NULL)
- MYSQL_YYABORT;
- }
- | VALUES '(' simple_ident_nospvar ')'
- {
- $$= new Item_insert_value(Lex->current_context(), $3);
+ | CONVERT_SYM '(' expr ',' cast_type ')'
+ {
+ $$= create_func_cast(YYTHD, $3, $5, Lex->length, Lex->dec,
+ Lex->charset);
if ($$ == NULL)
MYSQL_YYABORT;
}
- | FUNC_ARG0 '(' ')'
- {
- if (!$1.symbol->create_func)
- {
- my_error(ER_FEATURE_DISABLED, MYF(0),
- $1.symbol->group->name,
- $1.symbol->group->needed_define);
- MYSQL_YYABORT;
- }
- $$= ((Item*(*)(void))($1.symbol->create_func))();
- if ($$ == NULL)
- MYSQL_YYABORT;
- }
- | FUNC_ARG1 '(' expr ')'
- {
- if (!$1.symbol->create_func)
- {
- my_error(ER_FEATURE_DISABLED, MYF(0),
- $1.symbol->group->name,
- $1.symbol->group->needed_define);
- MYSQL_YYABORT;
- }
- $$= ((Item*(*)(Item*))($1.symbol->create_func))($3);
- if ($$ == NULL)
- MYSQL_YYABORT;
- }
- | FUNC_ARG2 '(' expr ',' expr ')'
- {
- if (!$1.symbol->create_func)
- {
- my_error(ER_FEATURE_DISABLED, MYF(0),
- $1.symbol->group->name,
- $1.symbol->group->needed_define);
- MYSQL_YYABORT;
- }
- $$= ((Item*(*)(Item*,Item*))($1.symbol->create_func))($3,$5);
- if ($$ == NULL)
- MYSQL_YYABORT;
- }
- | FUNC_ARG3 '(' expr ',' expr ',' expr ')'
- {
- if (!$1.symbol->create_func)
- {
- my_error(ER_FEATURE_DISABLED, MYF(0),
- $1.symbol->group->name,
- $1.symbol->group->needed_define);
- MYSQL_YYABORT;
- }
- $$= ((Item*(*)(Item*,Item*,Item*))($1.symbol->create_func))($3,$5,$7);
- if ($$ == NULL)
- MYSQL_YYABORT;
- }
- | ADDDATE_SYM '(' expr ',' expr ')'
- {
- $$= new Item_date_add_interval($3, $5, INTERVAL_DAY, 0);
+ | CONVERT_SYM '(' expr USING charset_name ')'
+ {
+ $$= new (YYTHD->mem_root) Item_func_conv_charset($3,$5);
if ($$ == NULL)
MYSQL_YYABORT;
}
- | ADDDATE_SYM '(' expr ',' INTERVAL_SYM expr interval ')'
- {
- $$= new Item_date_add_interval($3, $6, $7, 0);
- if ($$ == NULL)
+ | DEFAULT '(' simple_ident ')'
+ {
+ if ($3->is_splocal())
+ {
+ Item_splocal *il= static_cast<Item_splocal *>($3);
+
+ my_error(ER_WRONG_COLUMN_NAME, MYF(0), il->my_name()->str);
MYSQL_YYABORT;
- }
- | REPEAT_SYM '(' expr ',' expr ')'
- {
- $$= new Item_func_repeat($3,$5);
+ }
+ $$= new (YYTHD->mem_root) Item_default_value(Lex->current_context(),
+ $3);
if ($$ == NULL)
MYSQL_YYABORT;
}
- | ATAN '(' expr ')'
- {
- $$= new Item_func_atan($3);
+ | VALUES '(' simple_ident_nospvar ')'
+ {
+ $$= new (YYTHD->mem_root) Item_insert_value(Lex->current_context(),
+ $3);
if ($$ == NULL)
MYSQL_YYABORT;
}
- | ATAN '(' expr ',' expr ')'
- {
- $$= new Item_func_atan($3,$5);
+ | 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);
if ($$ == NULL)
MYSQL_YYABORT;
}
- | CHAR_SYM '(' expr_list ')'
- {
- $$= new Item_func_char(*$3);
+ ;
+
+/*
+ Function call syntax using official SQL 2003 keywords.
+ Because the function name is an official token,
+ a dedicated grammar rule is needed in the parser.
+ There is no potential for conflicts
+*/
+function_call_keyword:
+ CHAR_SYM '(' expr_list ')'
+ {
+ $$= new (YYTHD->mem_root) Item_func_char(*$3);
if ($$ == NULL)
MYSQL_YYABORT;
}
- | CHAR_SYM '(' expr_list USING charset_name ')'
- {
- $$= new Item_func_char(*$3, $5);
+ | CHAR_SYM '(' expr_list USING charset_name ')'
+ {
+ $$= new (YYTHD->mem_root) Item_func_char(*$3, $5);
if ($$ == NULL)
MYSQL_YYABORT;
}
- | CHARSET '(' expr ')'
- {
- $$= new Item_func_charset($3);
+ | CURRENT_USER optional_braces
+ {
+ $$= new (YYTHD->mem_root) Item_func_current_user(Lex->current_context());
if ($$ == NULL)
MYSQL_YYABORT;
+ Lex->set_stmt_unsafe();
+ Lex->safe_to_cache_query= 0;
}
- | COALESCE '(' expr_list ')'
- {
- $$= new Item_func_coalesce(* $3);
+ | DATE_SYM '(' expr ')'
+ {
+ $$= new (YYTHD->mem_root) Item_date_typecast($3);
if ($$ == NULL)
MYSQL_YYABORT;
}
- | COLLATION_SYM '(' expr ')'
- {
- $$= new Item_func_collation($3);
+ | DAY_SYM '(' expr ')'
+ {
+ $$= new (YYTHD->mem_root) Item_func_dayofmonth($3);
if ($$ == NULL)
MYSQL_YYABORT;
}
- | CONCAT '(' expr_list ')'
- {
- $$= new Item_func_concat(* $3);
+ | HOUR_SYM '(' expr ')'
+ {
+ $$= new (YYTHD->mem_root) Item_func_hour($3);
if ($$ == NULL)
MYSQL_YYABORT;
}
- | CONCAT_WS '(' expr ',' expr_list ')'
- {
- $5->push_front($3);
- $$= new Item_func_concat_ws(*$5);
+ | INSERT '(' expr ',' expr ',' expr ',' expr ')'
+ {
+ $$= new (YYTHD->mem_root) Item_func_insert($3,$5,$7,$9);
if ($$ == NULL)
MYSQL_YYABORT;
}
- | CONVERT_TZ_SYM '(' expr ',' expr ',' expr ')'
- {
- if (Lex->add_time_zone_tables_to_query_tables(YYTHD))
- MYSQL_YYABORT;
- $$= new Item_func_convert_tz($3, $5, $7);
- if ($$ == NULL)
+ | INTERVAL_SYM '(' expr ',' expr ')' %prec INTERVAL_SYM
+ {
+ THD *thd= YYTHD;
+ List<Item> *list= new (thd->mem_root) List<Item>;
+ if (list == NULL)
MYSQL_YYABORT;
- }
- | CURDATE optional_braces
- {
- $$= new Item_func_curdate_local();
- if ($$ == NULL)
+ list->push_front($5);
+ list->push_front($3);
+ Item_row *item= new (thd->mem_root) Item_row(*list);
+ if (item == NULL)
MYSQL_YYABORT;
- Lex->safe_to_cache_query=0;
- }
- | CURTIME optional_braces
- {
- $$= new Item_func_curtime_local();
+ $$= new (thd->mem_root) Item_func_interval(item);
if ($$ == NULL)
MYSQL_YYABORT;
- Lex->safe_to_cache_query=0;
}
- | CURTIME '(' expr ')'
- {
- $$= new Item_func_curtime_local($3);
- if ($$ == NULL)
- MYSQL_YYABORT;
- Lex->safe_to_cache_query=0;
- }
- | CURRENT_USER optional_braces
+ | INTERVAL_SYM '(' expr ',' expr ',' expr_list ')' %prec INTERVAL_SYM
{
- $$= new Item_func_current_user(Lex->current_context());
- if ($$ == NULL)
+ THD *thd= YYTHD;
+ $7->push_front($5);
+ $7->push_front($3);
+ Item_row *item= new (thd->mem_root) Item_row(*$7);
+ if (item == NULL)
MYSQL_YYABORT;
- Lex->safe_to_cache_query= 0;
- }
- | DATE_ADD_INTERVAL '(' expr ',' interval_expr interval ')'
- {
- $$= new Item_date_add_interval($3,$5,$6,0);
+ $$= new (thd->mem_root) Item_func_interval(item);
if ($$ == NULL)
MYSQL_YYABORT;
}
- | DATE_SUB_INTERVAL '(' expr ',' interval_expr interval ')'
- {
- $$= new Item_date_add_interval($3,$5,$6,1);
+ | LEFT '(' expr ',' expr ')'
+ {
+ $$= new (YYTHD->mem_root) Item_func_left($3,$5);
if ($$ == NULL)
MYSQL_YYABORT;
}
- | DATABASE '(' ')'
- {
- $$= new Item_func_database();
- if ($$ == NULL)
- MYSQL_YYABORT;
- Lex->safe_to_cache_query=0;
- }
- | DATE_SYM '(' expr ')'
- {
- $$= new Item_date_typecast($3);
+ | MINUTE_SYM '(' expr ')'
+ {
+ $$= new (YYTHD->mem_root) Item_func_minute($3);
if ($$ == NULL)
MYSQL_YYABORT;
}
- | DAY_SYM '(' expr ')'
- {
- $$= new Item_func_dayofmonth($3);
+ | MONTH_SYM '(' expr ')'
+ {
+ $$= new (YYTHD->mem_root) Item_func_month($3);
if ($$ == NULL)
MYSQL_YYABORT;
}
- | ELT_FUNC '(' expr ',' expr_list ')'
- {
- $5->push_front($3);
- $$= new Item_func_elt(*$5);
+ | RIGHT '(' expr ',' expr ')'
+ {
+ $$= new (YYTHD->mem_root) Item_func_right($3,$5);
if ($$ == NULL)
MYSQL_YYABORT;
}
- | MAKE_SET_SYM '(' expr ',' expr_list ')'
- {
- $$= new Item_func_make_set($3, *$5);
+ | SECOND_SYM '(' expr ')'
+ {
+ $$= new (YYTHD->mem_root) Item_func_second($3);
if ($$ == NULL)
MYSQL_YYABORT;
}
- | ENCRYPT '(' expr ')'
- {
- $$= new Item_func_encrypt($3);
- if ($$ == NULL)
- MYSQL_YYABORT;
- Lex->uncacheable(UNCACHEABLE_RAND);
- }
- | ENCRYPT '(' expr ',' expr ')'
+ | TIME_SYM '(' expr ')'
{
- $$= new Item_func_encrypt($3,$5);
+ $$= new (YYTHD->mem_root) Item_time_typecast($3);
if ($$ == NULL)
MYSQL_YYABORT;
}
- | DECODE_SYM '(' expr ',' TEXT_STRING_literal ')'
- {
- $$= new Item_func_decode($3,$5.str);
+ | TIMESTAMP '(' expr ')'
+ {
+ $$= new (YYTHD->mem_root) Item_datetime_typecast($3);
if ($$ == NULL)
MYSQL_YYABORT;
}
- | ENCODE_SYM '(' expr ',' TEXT_STRING_literal ')'
- {
- $$= new Item_func_encode($3,$5.str);
+ | TIMESTAMP '(' expr ',' expr ')'
+ {
+ $$= new (YYTHD->mem_root) Item_func_add_time($3, $5, 1, 0);
if ($$ == NULL)
MYSQL_YYABORT;
}
- | DES_DECRYPT_SYM '(' expr ')'
+ | TRIM '(' expr ')'
{
- $$= new Item_func_des_decrypt($3);
+ $$= new (YYTHD->mem_root) Item_func_trim($3);
if ($$ == NULL)
MYSQL_YYABORT;
}
- | DES_DECRYPT_SYM '(' expr ',' expr ')'
+ | TRIM '(' LEADING expr FROM expr ')'
{
- $$= new Item_func_des_decrypt($3,$5);
+ $$= new (YYTHD->mem_root) Item_func_ltrim($6,$4);
if ($$ == NULL)
MYSQL_YYABORT;
}
- | DES_ENCRYPT_SYM '(' expr ')'
+ | TRIM '(' TRAILING expr FROM expr ')'
{
- $$= new Item_func_des_encrypt($3);
+ $$= new (YYTHD->mem_root) Item_func_rtrim($6,$4);
if ($$ == NULL)
MYSQL_YYABORT;
}
- | DES_ENCRYPT_SYM '(' expr ',' expr ')'
+ | TRIM '(' BOTH expr FROM expr ')'
{
- $$= new Item_func_des_encrypt($3,$5);
+ $$= new (YYTHD->mem_root) Item_func_trim($6,$4);
if ($$ == NULL)
MYSQL_YYABORT;
}
- | EXPORT_SET '(' expr ',' expr ',' expr ')'
+ | TRIM '(' LEADING FROM expr ')'
{
- $$= new Item_func_export_set($3, $5, $7);
+ $$= new (YYTHD->mem_root) Item_func_ltrim($5);
if ($$ == NULL)
MYSQL_YYABORT;
}
- | EXPORT_SET '(' expr ',' expr ',' expr ',' expr ')'
+ | TRIM '(' TRAILING FROM expr ')'
{
- $$= new Item_func_export_set($3, $5, $7, $9);
+ $$= new (YYTHD->mem_root) Item_func_rtrim($5);
if ($$ == NULL)
MYSQL_YYABORT;
}
- | EXPORT_SET '(' expr ',' expr ',' expr ',' expr ',' expr ')'
+ | TRIM '(' BOTH FROM expr ')'
{
- $$= new Item_func_export_set($3, $5, $7, $9, $11);
+ $$= new (YYTHD->mem_root) Item_func_trim($5);
if ($$ == NULL)
MYSQL_YYABORT;
}
- | FORMAT_SYM '(' expr ',' NUM ')'
- {
- $$= new Item_func_format($3,atoi($5.str));
+ | TRIM '(' expr FROM expr ')'
+ {
+ $$= new (YYTHD->mem_root) Item_func_trim($5,$3);
if ($$ == NULL)
MYSQL_YYABORT;
}
- | FROM_UNIXTIME '(' expr ')'
- {
- $$= new Item_func_from_unixtime($3);
+ | USER '(' ')'
+ {
+ $$= new (YYTHD->mem_root) Item_func_user();
if ($$ == NULL)
MYSQL_YYABORT;
+ Lex->set_stmt_unsafe();
+ Lex->safe_to_cache_query=0;
}
- | FROM_UNIXTIME '(' expr ',' expr ')'
- {
- Item *item= new Item_func_from_unixtime($3);
- if (item == NULL)
- MYSQL_YYABORT;
- $$= new Item_func_date_format (item, $5, 0);
- if ($$ == NULL)
- MYSQL_YYABORT;
- }
- | FIELD_FUNC '(' expr ',' expr_list ')'
- {
- $5->push_front($3);
- $$= new Item_func_field(*$5);
+ | YEAR_SYM '(' expr ')'
+ {
+ $$= new (YYTHD->mem_root) Item_func_year($3);
if ($$ == NULL)
MYSQL_YYABORT;
}
- | geometry_function
- {
-#ifdef HAVE_SPATIAL
- $$= $1;
- /* $1 may be NULL, GEOM_NEW not tested for out of memory */
- if ($$ == NULL)
- MYSQL_YYABORT;
-#else
- my_error(ER_FEATURE_DISABLED, MYF(0),
- sym_group_geom.name, sym_group_geom.needed_define);
- MYSQL_YYABORT;
-#endif
- }
- | GET_FORMAT '(' date_time_type ',' expr ')'
- {
- $$= new Item_func_get_format($3, $5);
+ ;
+
+/*
+ Function calls using non reserved keywords, with special syntaxic forms.
+ Dedicated grammar rules are needed because of the syntax,
+ but also have the potential to cause incompatibilities with other
+ parts of the language.
+ MAINTAINER:
+ The only reasons a function should be added here are:
+ - for compatibility reasons with another SQL syntax (CURDATE),
+ - for typing reasons (GET_FORMAT)
+ Any other 'Syntaxic sugar' enhancements should be *STRONGLY*
+ discouraged.
+*/
+function_call_nonkeyword:
+ ADDDATE_SYM '(' expr ',' expr ')'
+ {
+ $$= new (YYTHD->mem_root) Item_date_add_interval($3, $5,
+ INTERVAL_DAY, 0);
if ($$ == NULL)
MYSQL_YYABORT;
}
- | HOUR_SYM '(' expr ')'
- {
- $$= new Item_func_hour($3);
+ | ADDDATE_SYM '(' expr ',' INTERVAL_SYM expr interval ')'
+ {
+ $$= new (YYTHD->mem_root) Item_date_add_interval($3, $6, $7, 0);
if ($$ == NULL)
MYSQL_YYABORT;
}
- | IF '(' expr ',' expr ',' expr ')'
- {
- $$= new Item_func_if($3,$5,$7);
+ | CURDATE optional_braces
+ {
+ $$= new (YYTHD->mem_root) Item_func_curdate_local();
if ($$ == NULL)
MYSQL_YYABORT;
+ Lex->safe_to_cache_query=0;
}
- | INSERT '(' expr ',' expr ',' expr ',' expr ')'
- {
- $$= new Item_func_insert($3,$5,$7,$9);
+ | CURTIME optional_braces
+ {
+ $$= new (YYTHD->mem_root) Item_func_curtime_local();
if ($$ == NULL)
MYSQL_YYABORT;
+ Lex->safe_to_cache_query=0;
}
- | interval_expr interval '+' expr
- /* we cannot put interval before - */
- {
- $$= new Item_date_add_interval($4,$1,$2,0);
+ | CURTIME '(' expr ')'
+ {
+ $$= new (YYTHD->mem_root) Item_func_curtime_local($3);
if ($$ == NULL)
MYSQL_YYABORT;
+ Lex->safe_to_cache_query=0;
}
- | interval_expr
- {
- if ($1->type() != Item::ROW_ITEM)
- {
- my_parse_error(ER(ER_SYNTAX_ERROR));
- MYSQL_YYABORT;
- }
- $$= new Item_func_interval((Item_row *)$1);
+ | DATE_ADD_INTERVAL '(' expr ',' INTERVAL_SYM expr interval ')'
+ %prec INTERVAL_SYM
+ {
+ $$= new (YYTHD->mem_root) Item_date_add_interval($3,$6,$7,0);
if ($$ == NULL)
MYSQL_YYABORT;
}
- | LAST_INSERT_ID '(' ')'
- {
- $$= new Item_func_last_insert_id();
- if ($$ == NULL)
- MYSQL_YYABORT;
- Lex->safe_to_cache_query= 0;
- }
- | LAST_INSERT_ID '(' expr ')'
- {
- $$= new Item_func_last_insert_id($3);
- if ($$ == NULL)
- MYSQL_YYABORT;
- Lex->safe_to_cache_query= 0;
- }
- | LEFT '(' expr ',' expr ')'
- {
- $$= new Item_func_left($3,$5);
+ | DATE_SUB_INTERVAL '(' expr ',' INTERVAL_SYM expr interval ')'
+ %prec INTERVAL_SYM
+ {
+ $$= new (YYTHD->mem_root) Item_date_add_interval($3,$6,$7,1);
if ($$ == NULL)
MYSQL_YYABORT;
}
- | LOCATE '(' expr ',' expr ')'
- {
- $$= new Item_func_locate($5,$3);
+ | EXTRACT_SYM '(' interval FROM expr ')'
+ {
+ $$=new (YYTHD->mem_root) Item_extract( $3, $5);
if ($$ == NULL)
MYSQL_YYABORT;
}
- | LOCATE '(' expr ',' expr ',' expr ')'
- {
- $$= new Item_func_locate($5,$3,$7);
+ | GET_FORMAT '(' date_time_type ',' expr ')'
+ {
+ $$= new (YYTHD->mem_root) Item_func_get_format($3, $5);
if ($$ == NULL)
MYSQL_YYABORT;
}
- | GREATEST_SYM '(' expr ',' expr_list ')'
- {
- $5->push_front($3);
- $$= new Item_func_max(*$5);
+ | NOW_SYM optional_braces
+ {
+ $$= new (YYTHD->mem_root) Item_func_now_local();
if ($$ == NULL)
MYSQL_YYABORT;
+ Lex->safe_to_cache_query=0;
}
- | LEAST_SYM '(' expr ',' expr_list ')'
- {
- $5->push_front($3);
- $$= new Item_func_min(*$5);
+ | NOW_SYM '(' expr ')'
+ {
+ $$= new (YYTHD->mem_root) Item_func_now_local($3);
if ($$ == NULL)
MYSQL_YYABORT;
+ Lex->safe_to_cache_query=0;
}
- | LOG_SYM '(' expr ')'
- {
- $$= new Item_func_log($3);
+ | POSITION_SYM '(' bit_expr IN_SYM expr ')'
+ {
+ $$ = new (YYTHD->mem_root) Item_func_locate($5,$3);
if ($$ == NULL)
MYSQL_YYABORT;
}
- | LOG_SYM '(' expr ',' expr ')'
- {
- $$= new Item_func_log($3, $5);
+ | SUBDATE_SYM '(' expr ',' expr ')'
+ {
+ $$= new (YYTHD->mem_root) Item_date_add_interval($3, $5,
+ INTERVAL_DAY, 1);
if ($$ == NULL)
MYSQL_YYABORT;
}
- | MASTER_POS_WAIT '(' expr ',' expr ')'
- {
- $$= new Item_master_pos_wait($3, $5);
+ | SUBDATE_SYM '(' expr ',' INTERVAL_SYM expr interval ')'
+ {
+ $$= new (YYTHD->mem_root) Item_date_add_interval($3, $6, $7, 1);
if ($$ == NULL)
MYSQL_YYABORT;
- Lex->safe_to_cache_query=0;
}
- | MASTER_POS_WAIT '(' expr ',' expr ',' expr ')'
- {
- $$= new Item_master_pos_wait($3, $5, $7);
- if ($$ == NULL)
- MYSQL_YYABORT;
- Lex->safe_to_cache_query=0;
- }
- | MICROSECOND_SYM '(' expr ')'
- {
- $$= new Item_func_microsecond($3);
+ | SUBSTRING '(' expr ',' expr ',' expr ')'
+ {
+ $$= new (YYTHD->mem_root) Item_func_substr($3,$5,$7);
if ($$ == NULL)
MYSQL_YYABORT;
}
- | MINUTE_SYM '(' expr ')'
- {
- $$= new Item_func_minute($3);
+ | SUBSTRING '(' expr ',' expr ')'
+ {
+ $$= new (YYTHD->mem_root) Item_func_substr($3,$5);
if ($$ == NULL)
MYSQL_YYABORT;
}
- | MOD_SYM '(' expr ',' expr ')'
- {
- $$= new Item_func_mod( $3, $5);
+ | SUBSTRING '(' expr FROM expr FOR_SYM expr ')'
+ {
+ $$= new (YYTHD->mem_root) Item_func_substr($3,$5,$7);
if ($$ == NULL)
MYSQL_YYABORT;
}
- | MONTH_SYM '(' expr ')'
- {
- $$= new Item_func_month($3);
+ | SUBSTRING '(' expr FROM expr ')'
+ {
+ $$= new (YYTHD->mem_root) Item_func_substr($3,$5);
if ($$ == NULL)
MYSQL_YYABORT;
}
- | NOW_SYM optional_braces
- {
- $$= new Item_func_now_local();
+ | SYSDATE optional_braces
+ {
+ if (global_system_variables.sysdate_is_now == 0)
+ $$= new (YYTHD->mem_root) Item_func_sysdate_local();
+ else
+ $$= new (YYTHD->mem_root) Item_func_now_local();
if ($$ == NULL)
MYSQL_YYABORT;
Lex->safe_to_cache_query=0;
}
- | NOW_SYM '(' expr ')'
- {
- $$= new Item_func_now_local($3);
+ | SYSDATE '(' expr ')'
+ {
+ if (global_system_variables.sysdate_is_now == 0)
+ $$= new (YYTHD->mem_root) Item_func_sysdate_local($3);
+ else
+ $$= new (YYTHD->mem_root) Item_func_now_local($3);
if ($$ == NULL)
MYSQL_YYABORT;
Lex->safe_to_cache_query=0;
}
- | PASSWORD '(' expr ')'
- {
- $$= YYTHD->variables.old_passwords ?
- (Item *) new Item_func_old_password($3) :
- (Item *) new Item_func_password($3);
- if ($$ == NULL)
- MYSQL_YYABORT;
- }
- | OLD_PASSWORD '(' expr ')'
- {
- $$= new Item_func_old_password($3);
- if ($$ == NULL)
- MYSQL_YYABORT;
- }
- | POSITION_SYM '(' bit_expr IN_SYM expr ')'
- {
- $$= new Item_func_locate($5,$3);
- if ($$ == NULL)
- MYSQL_YYABORT;
- }
- | QUARTER_SYM '(' expr ')'
- {
- $$= new Item_func_quarter($3);
+ | TIMESTAMP_ADD '(' interval_time_stamp ',' expr ',' expr ')'
+ {
+ $$= new (YYTHD->mem_root) Item_date_add_interval($7,$5,$3,0);
if ($$ == NULL)
MYSQL_YYABORT;
}
- | RAND '(' expr ')'
- {
- $$= new Item_func_rand($3);
+ | TIMESTAMP_DIFF '(' interval_time_stamp ',' expr ',' expr ')'
+ {
+ $$= new (YYTHD->mem_root) Item_func_timestamp_diff($5,$7,$3);
if ($$ == NULL)
MYSQL_YYABORT;
- Lex->uncacheable(UNCACHEABLE_RAND);
}
- | RAND '(' ')'
- {
- $$= new Item_func_rand();
+ | UTC_DATE_SYM optional_braces
+ {
+ $$= new (YYTHD->mem_root) Item_func_curdate_utc();
if ($$ == NULL)
MYSQL_YYABORT;
- Lex->uncacheable(UNCACHEABLE_RAND);
+ Lex->safe_to_cache_query=0;
}
- | REPLACE '(' expr ',' expr ',' expr ')'
- {
- $$= new Item_func_replace($3,$5,$7);
+ | UTC_TIME_SYM optional_braces
+ {
+ $$= new (YYTHD->mem_root) Item_func_curtime_utc();
if ($$ == NULL)
MYSQL_YYABORT;
+ Lex->safe_to_cache_query=0;
}
- | RIGHT '(' expr ',' expr ')'
- {
- $$= new Item_func_right($3,$5);
+ | UTC_TIMESTAMP_SYM optional_braces
+ {
+ $$= new (YYTHD->mem_root) Item_func_now_utc();
if ($$ == NULL)
MYSQL_YYABORT;
+ Lex->safe_to_cache_query=0;
}
- | ROUND '(' expr ')'
- {
- Item *item= new Item_int((char*)"0",0,1);
- if (item == NULL)
- MYSQL_YYABORT;
- $$= new Item_func_round($3, item, 0);
+ ;
+
+/*
+ Functions calls using a non reserved keyword, and using a regular syntax.
+ Because the non reserved keyword is used in another part of the grammar,
+ a dedicated rule is needed here.
+*/
+function_call_conflict:
+ ASCII_SYM '(' expr ')'
+ {
+ $$= new (YYTHD->mem_root) Item_func_ascii($3);
if ($$ == NULL)
MYSQL_YYABORT;
}
- | ROUND '(' expr ',' expr ')'
+ | CHARSET '(' expr ')'
{
- $$= new Item_func_round($3,$5,0);
+ $$= new (YYTHD->mem_root) Item_func_charset($3);
if ($$ == NULL)
MYSQL_YYABORT;
}
- | ROW_COUNT_SYM '(' ')'
- {
- $$= new Item_func_row_count();
- if ($$ == NULL)
- MYSQL_YYABORT;
- Lex->safe_to_cache_query= 0;
- }
- | SUBDATE_SYM '(' expr ',' expr ')'
- {
- $$= new Item_date_add_interval($3, $5, INTERVAL_DAY, 1);
+ | COALESCE '(' expr_list ')'
+ {
+ $$= new (YYTHD->mem_root) Item_func_coalesce(* $3);
if ($$ == NULL)
MYSQL_YYABORT;
}
- | SUBDATE_SYM '(' expr ',' INTERVAL_SYM expr interval ')'
- {
- $$= new Item_date_add_interval($3, $6, $7, 1);
+ | COLLATION_SYM '(' expr ')'
+ {
+ $$= new (YYTHD->mem_root) Item_func_collation($3);
if ($$ == NULL)
MYSQL_YYABORT;
}
- | SECOND_SYM '(' expr ')'
- {
- $$= new Item_func_second($3);
+ | DATABASE '(' ')'
+ {
+ $$= new (YYTHD->mem_root) Item_func_database();
if ($$ == NULL)
MYSQL_YYABORT;
+ Lex->safe_to_cache_query=0;
}
- | SUBSTRING '(' expr ',' expr ',' expr ')'
- {
- $$= new Item_func_substr($3,$5,$7);
+ | IF '(' expr ',' expr ',' expr ')'
+ {
+ $$= new (YYTHD->mem_root) Item_func_if($3,$5,$7);
if ($$ == NULL)
MYSQL_YYABORT;
}
- | SUBSTRING '(' expr ',' expr ')'
- {
- $$= new Item_func_substr($3,$5);
+ | MICROSECOND_SYM '(' expr ')'
+ {
+ $$= new (YYTHD->mem_root) Item_func_microsecond($3);
if ($$ == NULL)
MYSQL_YYABORT;
}
- | SUBSTRING '(' expr FROM expr FOR_SYM expr ')'
- {
- $$= new Item_func_substr($3,$5,$7);
+ | MOD_SYM '(' expr ',' expr ')'
+ {
+ $$ = new (YYTHD->mem_root) Item_func_mod($3, $5);
if ($$ == NULL)
MYSQL_YYABORT;
}
- | SUBSTRING '(' expr FROM expr ')'
- {
- $$= new Item_func_substr($3,$5);
+ | OLD_PASSWORD '(' expr ')'
+ {
+ $$= new (YYTHD->mem_root) Item_func_old_password($3);
if ($$ == NULL)
MYSQL_YYABORT;
}
- | SUBSTRING_INDEX '(' expr ',' expr ',' expr ')'
- {
- $$= new Item_func_substr_index($3,$5,$7);
- if ($$ == NULL)
+ | PASSWORD '(' expr ')'
+ {
+ THD *thd= YYTHD;
+ Item* i1;
+ if (thd->variables.old_passwords)
+ i1= new (thd->mem_root) Item_func_old_password($3);
+ else
+ i1= new (thd->mem_root) Item_func_password($3);
+ if (i1 == NULL)
MYSQL_YYABORT;
+ $$= i1;
}
- | SYSDATE optional_braces
+ | QUARTER_SYM '(' expr ')'
{
- if (global_system_variables.sysdate_is_now == 0)
- $$= new Item_func_sysdate_local();
- else $$= new Item_func_now_local();
+ $$ = new (YYTHD->mem_root) Item_func_quarter($3);
if ($$ == NULL)
MYSQL_YYABORT;
- Lex->safe_to_cache_query=0;
}
- | SYSDATE '(' expr ')'
+ | REPEAT_SYM '(' expr ',' expr ')'
{
- if (global_system_variables.sysdate_is_now == 0)
- $$= new Item_func_sysdate_local($3);
- else $$= new Item_func_now_local($3);
+ $$= new (YYTHD->mem_root) Item_func_repeat($3,$5);
if ($$ == NULL)
MYSQL_YYABORT;
- Lex->safe_to_cache_query=0;
}
- | TIME_SYM '(' expr ')'
- {
- $$= new Item_time_typecast($3);
+ | REPLACE '(' expr ',' expr ',' expr ')'
+ {
+ $$= new (YYTHD->mem_root) Item_func_replace($3,$5,$7);
if ($$ == NULL)
MYSQL_YYABORT;
}
- | TIMESTAMP '(' expr ')'
- {
- $$= new Item_datetime_typecast($3);
+ | TRUNCATE_SYM '(' expr ',' expr ')'
+ {
+ $$= new (YYTHD->mem_root) Item_func_round($3,$5,1);
if ($$ == NULL)
MYSQL_YYABORT;
}
- | TIMESTAMP '(' expr ',' expr ')'
- {
- $$= new Item_func_add_time($3, $5, 1, 0);
- if ($$ == NULL)
+ | WEEK_SYM '(' expr ')'
+ {
+ THD *thd= YYTHD;
+ Item *i1= new (thd->mem_root) Item_int((char*) "0",
+ thd->variables.default_week_format,
+ 1);
+ if (i1 == NULL)
MYSQL_YYABORT;
- }
- | TIMESTAMP_ADD '(' interval_time_stamp ',' expr ',' expr ')'
- {
- $$= new Item_date_add_interval($7,$5,$3,0);
+ $$= new (thd->mem_root) Item_func_week($3, i1);
if ($$ == NULL)
MYSQL_YYABORT;
}
- | TIMESTAMP_DIFF '(' interval_time_stamp ',' expr ',' expr ')'
- {
- $$= new Item_func_timestamp_diff($5,$7,$3);
+ | WEEK_SYM '(' expr ',' expr ')'
+ {
+ $$= new (YYTHD->mem_root) Item_func_week($3,$5);
if ($$ == NULL)
MYSQL_YYABORT;
}
- | TRIM '(' expr ')'
- {
- $$= new Item_func_trim($3);
+ | geometry_function
+ {
+#ifdef HAVE_SPATIAL
+ $$= $1;
+ /* $1 may be NULL, GEOM_NEW not tested for out of memory */
if ($$ == NULL)
MYSQL_YYABORT;
+#else
+ my_error(ER_FEATURE_DISABLED, MYF(0),
+ sym_group_geom.name, sym_group_geom.needed_define);
+ MYSQL_YYABORT;
+#endif
}
- | TRIM '(' LEADING expr FROM expr ')'
- {
- $$= new Item_func_ltrim($6,$4);
- if ($$ == NULL)
- MYSQL_YYABORT;
+ ;
+
+geometry_function:
+ CONTAINS_SYM '(' expr ',' expr ')'
+ {
+ $$= GEOM_NEW(YYTHD,
+ Item_func_spatial_rel($3, $5,
+ Item_func::SP_CONTAINS_FUNC));
}
- | TRIM '(' TRAILING expr FROM expr ')'
- {
- $$= new Item_func_rtrim($6,$4);
- if ($$ == NULL)
- MYSQL_YYABORT;
+ | GEOMETRYCOLLECTION '(' expr_list ')'
+ {
+ $$= GEOM_NEW(YYTHD,
+ Item_func_spatial_collection(* $3,
+ Geometry::wkb_geometrycollection,
+ Geometry::wkb_point));
}
- | TRIM '(' BOTH expr FROM expr ')'
- {
- $$= new Item_func_trim($6,$4);
- if ($$ == NULL)
- MYSQL_YYABORT;
+ | LINESTRING '(' expr_list ')'
+ {
+ $$= GEOM_NEW(YYTHD,
+ Item_func_spatial_collection(* $3,
+ Geometry::wkb_linestring,
+ Geometry::wkb_point));
}
- | TRIM '(' LEADING FROM expr ')'
- {
- $$= new Item_func_ltrim($5);
- if ($$ == NULL)
- MYSQL_YYABORT;
+ | MULTILINESTRING '(' expr_list ')'
+ {
+ $$= GEOM_NEW(YYTHD,
+ Item_func_spatial_collection(* $3,
+ Geometry::wkb_multilinestring,
+ Geometry::wkb_linestring));
}
- | TRIM '(' TRAILING FROM expr ')'
- {
- $$= new Item_func_rtrim($5);
- if ($$ == NULL)
- MYSQL_YYABORT;
+ | MULTIPOINT '(' expr_list ')'
+ {
+ $$= GEOM_NEW(YYTHD,
+ Item_func_spatial_collection(* $3,
+ Geometry::wkb_multipoint,
+ Geometry::wkb_point));
}
- | TRIM '(' BOTH FROM expr ')'
- {
- $$= new Item_func_trim($5);
- if ($$ == NULL)
- MYSQL_YYABORT;
+ | MULTIPOLYGON '(' expr_list ')'
+ {
+ $$= GEOM_NEW(YYTHD,
+ Item_func_spatial_collection(* $3,
+ Geometry::wkb_multipolygon,
+ Geometry::wkb_polygon));
}
- | TRIM '(' expr FROM expr ')'
- {
- $$= new Item_func_trim($5,$3);
- if ($$ == NULL)
- MYSQL_YYABORT;
+ | POINT_SYM '(' expr ',' expr ')'
+ {
+ $$= GEOM_NEW(YYTHD, Item_func_point($3,$5));
}
- | TRUNCATE_SYM '(' expr ',' expr ')'
- {
- $$= new Item_func_round($3,$5,1);
- if ($$ == NULL)
- MYSQL_YYABORT;
+ | POLYGON '(' expr_list ')'
+ {
+ $$= GEOM_NEW(YYTHD,
+ Item_func_spatial_collection(* $3,
+ Geometry::wkb_polygon,
+ Geometry::wkb_linestring));
}
- | ident '.' ident '(' opt_expr_list ')'
- {
- LEX *lex= Lex;
- sp_name *name= new sp_name($1, $3, true);
- if (name == NULL)
- MYSQL_YYABORT;
- name->init_qname(YYTHD);
- sp_add_used_routine(lex, YYTHD, name, TYPE_ENUM_FUNCTION);
- if ($5)
- $$= new Item_func_sp(Lex->current_context(), name, *$5);
- else
- $$= new Item_func_sp(Lex->current_context(), name);
- if ($$ == NULL)
- MYSQL_YYABORT;
- lex->safe_to_cache_query=0;
- }
- | IDENT_sys '('
+ ;
+
+/*
+ Regular function calls.
+ The function name is *not* a token, and therefore is guaranteed to not
+ introduce side effects to the language in general.
+ MAINTAINER:
+ All the new functions implemented for new features should fit into
+ this category. The place to implement the function itself is
+ in sql/item_create.cc
+*/
+function_call_generic:
+ IDENT_sys '('
{
#ifdef HAVE_DLOPEN
udf_func *udf= 0;
@@ -5849,501 +7767,284 @@ simple_expr:
MYSQL_YYABORT;
}
}
- lex->current_select->udf_list.push_front(udf);
+ /* Temporary placing the result of find_udf in $3 */
+ $<udf>$= udf;
#endif
}
- udf_expr_list ')'
+ opt_udf_expr_list ')'
{
- LEX *lex= Lex;
-#ifdef HAVE_DLOPEN
- udf_func *udf;
+ THD *thd= YYTHD;
+ Create_func *builder;
+ Item *item= NULL;
- if (NULL != (udf= lex->current_select->udf_list.pop()))
- {
- if (udf->type == UDFTYPE_AGGREGATE)
- Select->in_sum_expr--;
+ /*
+ Implementation note:
+ names are resolved with the following order:
+ - MySQL native functions,
+ - User Defined Functions,
+ - Stored Functions (assuming the current <use> database)
- switch (udf->returns) {
- case STRING_RESULT:
- if (udf->type == UDFTYPE_FUNCTION)
- {
- if ($4 != NULL)
- $$ = new Item_func_udf_str(udf, *$4);
- else
- $$ = new Item_func_udf_str(udf);
- }
- else
- {
- if ($4 != NULL)
- $$ = new Item_sum_udf_str(udf, *$4);
- else
- $$ = new Item_sum_udf_str(udf);
- }
- break;
- case REAL_RESULT:
- if (udf->type == UDFTYPE_FUNCTION)
- {
- if ($4 != NULL)
- $$ = new Item_func_udf_float(udf, *$4);
- else
- $$ = new Item_func_udf_float(udf);
- }
- else
- {
- if ($4 != NULL)
- $$ = new Item_sum_udf_float(udf, *$4);
- else
- $$ = new Item_sum_udf_float(udf);
- }
- break;
- case INT_RESULT:
- if (udf->type == UDFTYPE_FUNCTION)
- {
- if ($4 != NULL)
- $$ = new Item_func_udf_int(udf, *$4);
- else
- $$ = new Item_func_udf_int(udf);
- }
- else
- {
- if ($4 != NULL)
- $$ = new Item_sum_udf_int(udf, *$4);
- else
- $$ = new Item_sum_udf_int(udf);
- }
- break;
- case DECIMAL_RESULT:
- if (udf->type == UDFTYPE_FUNCTION)
- {
- if ($4 != NULL)
- $$ = new Item_func_udf_decimal(udf, *$4);
- else
- $$ = new Item_func_udf_decimal(udf);
- }
- else
- {
- if ($4 != NULL)
- $$ = new Item_sum_udf_decimal(udf, *$4);
- else
- $$ = new Item_sum_udf_decimal(udf);
- }
- break;
- default:
- MYSQL_YYABORT;
- }
+ This will be revised with WL#2128 (SQL PATH)
+ */
+ builder= find_native_function_builder(thd, $1);
+ if (builder)
+ {
+ item= builder->create(thd, $1, $4);
}
else
-#endif /* HAVE_DLOPEN */
{
- THD *thd= lex->thd;
- LEX_STRING db;
- if (! thd->db && ! lex->sphead)
- {
- /*
- The proper error message should be in the lines of:
- Can't resolve <name>() to a function call,
- because this function:
- - is not a native function,
- - is not a user defined function,
- - can not match a stored function since no database is selected.
- Reusing ER_SP_DOES_NOT_EXIST have a message consistent with
- the case when a default database exist, see below.
- */
- my_error(ER_SP_DOES_NOT_EXIST, MYF(0),
- "FUNCTION", $1.str);
- MYSQL_YYABORT;
- }
-
- if (lex->copy_db_to(&db.str, &db.length))
- MYSQL_YYABORT;
+#ifdef HAVE_DLOPEN
+ /* Retrieving the result of find_udf */
+ udf_func *udf= $<udf>3;
- /*
- From here, the parser assumes <name>() is a stored function,
- as a last choice. This later can lead to ER_SP_DOES_NOT_EXIST.
- */
- sp_name *name= new sp_name(db, $1, false);
- if (name == NULL)
- MYSQL_YYABORT;
- name->init_qname(thd);
+ if (udf)
+ {
+ if (udf->type == UDFTYPE_AGGREGATE)
+ {
+ Select->in_sum_expr--;
+ }
- sp_add_used_routine(lex, YYTHD, name, TYPE_ENUM_FUNCTION);
- if ($4)
- $$= new Item_func_sp(Lex->current_context(), name, *$4);
+ item= Create_udf_func::s_singleton.create(thd, udf, $4);
+ }
else
- $$= new Item_func_sp(Lex->current_context(), name);
- }
- lex->safe_to_cache_query=0;
+#endif
+ {
+ builder= find_qualified_function_builder(thd);
+ DBUG_ASSERT(builder);
+ item= builder->create(thd, $1, $4);
+ }
+ }
- if ($$ == NULL)
- MYSQL_YYABORT;
- }
- | UNIQUE_USERS '(' text_literal ',' NUM ',' NUM ',' expr_list ')'
- {
- $$= new Item_func_unique_users($3,atoi($5.str),atoi($7.str), * $9);
- if ($$ == NULL)
- MYSQL_YYABORT;
- }
- | UNIX_TIMESTAMP '(' ')'
- {
- $$= new Item_func_unix_timestamp();
- if ($$ == NULL)
- MYSQL_YYABORT;
- Lex->safe_to_cache_query=0;
- }
- | UNIX_TIMESTAMP '(' expr ')'
- {
- $$= new Item_func_unix_timestamp($3);
- if ($$ == NULL)
- MYSQL_YYABORT;
- }
- | USER '(' ')'
- {
- $$= new Item_func_user();
- if ($$ == NULL)
- MYSQL_YYABORT;
- Lex->safe_to_cache_query=0;
- }
- | UTC_DATE_SYM optional_braces
- {
- $$= new Item_func_curdate_utc();
- if ($$ == NULL)
- MYSQL_YYABORT;
- Lex->safe_to_cache_query=0;
- }
- | UTC_TIME_SYM optional_braces
- {
- $$= new Item_func_curtime_utc();
- if ($$ == NULL)
- MYSQL_YYABORT;
- Lex->safe_to_cache_query=0;
- }
- | UTC_TIMESTAMP_SYM optional_braces
- {
- $$= new Item_func_now_utc();
- if ($$ == NULL)
- MYSQL_YYABORT;
- Lex->safe_to_cache_query=0;
- }
- | WEEK_SYM '(' expr ')'
- {
- Item *item= new Item_int((char*) "0",
- YYTHD->variables.default_week_format,
- 1);
- if (item == NULL)
- MYSQL_YYABORT;
- $$= new Item_func_week($3, item);
- if ($$ == NULL)
- MYSQL_YYABORT;
- }
- | WEEK_SYM '(' expr ',' expr ')'
- {
- $$= new Item_func_week($3,$5);
- if ($$ == NULL)
- MYSQL_YYABORT;
- }
- | YEAR_SYM '(' expr ')'
- {
- $$= new Item_func_year($3);
- if ($$ == NULL)
- MYSQL_YYABORT;
- }
- | YEARWEEK '(' expr ')'
- {
- Item *item= new Item_int((char*) "0",0,1);
- if (item == NULL)
- MYSQL_YYABORT;
- $$= new Item_func_yearweek($3, item);
- if ($$ == NULL)
- MYSQL_YYABORT;
- }
- | YEARWEEK '(' expr ',' expr ')'
- {
- $$= new Item_func_yearweek($3, $5);
- if ($$ == NULL)
+ if (! ($$= item))
+ {
MYSQL_YYABORT;
+ }
}
- | BENCHMARK_SYM '(' ulong_num ',' expr ')'
- {
- $$=new Item_func_benchmark($3,$5);
- if ($$ == NULL)
- MYSQL_YYABORT;
- Lex->uncacheable(UNCACHEABLE_SIDEEFFECT);
- }
- | EXTRACT_SYM '(' interval FROM expr ')'
+ | ident '.' ident '(' opt_expr_list ')'
{
- $$=new Item_extract( $3, $5);
- if ($$ == NULL)
+ THD *thd= YYTHD;
+ Create_qfunc *builder;
+ Item *item= NULL;
+
+ /*
+ The following in practice calls:
+ <code>Create_sp_func::create()</code>
+ and builds a stored function.
+
+ However, it's important to maintain the interface between the
+ parser and the implementation in item_create.cc clean,
+ since this will change with WL#2128 (SQL PATH):
+ - INFORMATION_SCHEMA.version() is the SQL 99 syntax for the native
+ function version(),
+ - MySQL.version() is the SQL 2003 syntax for the native function
+ version() (a vendor can specify any schema).
+ */
+
+ builder= find_qualified_function_builder(thd);
+ DBUG_ASSERT(builder);
+ item= builder->create(thd, $1, $3, true, $5);
+
+ if (! ($$= item))
+ {
MYSQL_YYABORT;
+ }
}
;
-geometry_function:
- CONTAINS_SYM '(' expr ',' expr ')'
- { $$= GEOM_NEW(Item_func_spatial_rel($3, $5, Item_func::SP_CONTAINS_FUNC)); }
- | GEOMFROMTEXT '(' expr ')'
- { $$= GEOM_NEW(Item_func_geometry_from_text($3)); }
- | GEOMFROMTEXT '(' expr ',' expr ')'
- { $$= GEOM_NEW(Item_func_geometry_from_text($3, $5)); }
- | GEOMFROMWKB '(' expr ')'
- { $$= GEOM_NEW(Item_func_geometry_from_wkb($3)); }
- | GEOMFROMWKB '(' expr ',' expr ')'
- { $$= GEOM_NEW(Item_func_geometry_from_wkb($3, $5)); }
- | GEOMETRYCOLLECTION '(' expr_list ')'
- { $$= GEOM_NEW(Item_func_spatial_collection(* $3,
- Geometry::wkb_geometrycollection,
- Geometry::wkb_point)); }
- | LINESTRING '(' expr_list ')'
- { $$= GEOM_NEW(Item_func_spatial_collection(* $3,
- Geometry::wkb_linestring, Geometry::wkb_point)); }
- | MULTILINESTRING '(' expr_list ')'
- { $$= GEOM_NEW( Item_func_spatial_collection(* $3,
- Geometry::wkb_multilinestring, Geometry::wkb_linestring)); }
- | MLINEFROMTEXT '(' expr ')'
- { $$= GEOM_NEW(Item_func_geometry_from_text($3)); }
- | MLINEFROMTEXT '(' expr ',' expr ')'
- { $$= GEOM_NEW(Item_func_geometry_from_text($3, $5)); }
- | MPOINTFROMTEXT '(' expr ')'
- { $$= GEOM_NEW(Item_func_geometry_from_text($3)); }
- | MPOINTFROMTEXT '(' expr ',' expr ')'
- { $$= GEOM_NEW(Item_func_geometry_from_text($3, $5)); }
- | MPOLYFROMTEXT '(' expr ')'
- { $$= GEOM_NEW(Item_func_geometry_from_text($3)); }
- | MPOLYFROMTEXT '(' expr ',' expr ')'
- { $$= GEOM_NEW(Item_func_geometry_from_text($3, $5)); }
- | MULTIPOINT '(' expr_list ')'
- { $$= GEOM_NEW(Item_func_spatial_collection(* $3,
- Geometry::wkb_multipoint, Geometry::wkb_point)); }
- | MULTIPOLYGON '(' expr_list ')'
- { $$= GEOM_NEW(Item_func_spatial_collection(* $3,
- Geometry::wkb_multipolygon, Geometry::wkb_polygon)); }
- | POINT_SYM '(' expr ',' expr ')'
- { $$= GEOM_NEW(Item_func_point($3,$5)); }
- | POINTFROMTEXT '(' expr ')'
- { $$= GEOM_NEW(Item_func_geometry_from_text($3)); }
- | POINTFROMTEXT '(' expr ',' expr ')'
- { $$= GEOM_NEW(Item_func_geometry_from_text($3, $5)); }
- | POLYFROMTEXT '(' expr ')'
- { $$= GEOM_NEW(Item_func_geometry_from_text($3)); }
- | POLYFROMTEXT '(' expr ',' expr ')'
- { $$= GEOM_NEW(Item_func_geometry_from_text($3, $5)); }
- | POLYGON '(' expr_list ')'
- { $$= GEOM_NEW(Item_func_spatial_collection(* $3,
- Geometry::wkb_polygon, Geometry::wkb_linestring)); }
- | GEOMCOLLFROMTEXT '(' expr ')'
- { $$= GEOM_NEW(Item_func_geometry_from_text($3)); }
- | GEOMCOLLFROMTEXT '(' expr ',' expr ')'
- { $$= GEOM_NEW(Item_func_geometry_from_text($3, $5)); }
- | LINEFROMTEXT '(' expr ')'
- { $$= GEOM_NEW(Item_func_geometry_from_text($3)); }
- | LINEFROMTEXT '(' expr ',' expr ')'
- { $$= GEOM_NEW(Item_func_geometry_from_text($3, $5)); }
- ;
-
fulltext_options:
- /* nothing */ { $$= FT_NL; }
- | WITH QUERY_SYM EXPANSION_SYM { $$= FT_NL | FT_EXPAND; }
- | IN_SYM BOOLEAN_SYM MODE_SYM { $$= FT_BOOL; }
+ opt_natural_language_mode opt_query_expansion
+ { $$= $1 | $2; }
+ | IN_SYM BOOLEAN_SYM MODE_SYM
+ { $$= FT_BOOL; }
;
-udf_expr_list:
- /* empty */ { $$= NULL; }
- | udf_expr_list2 { $$= $1;}
- ;
+opt_natural_language_mode:
+ /* nothing */ { $$= FT_NL; }
+ | IN_SYM NATURAL LANGUAGE_SYM MODE_SYM { $$= FT_NL; }
+ ;
-udf_expr_list2:
- {
- List<Item> *list= new List<Item>;
- if (list == NULL)
+opt_query_expansion:
+ /* nothing */ { $$= 0; }
+ | WITH QUERY_SYM EXPANSION_SYM { $$= FT_EXPAND; }
+ ;
+
+opt_udf_expr_list:
+ /* empty */ { $$= NULL; }
+ | udf_expr_list { $$= $1; }
+ ;
+
+udf_expr_list:
+ udf_expr
+ {
+ $$= new (YYTHD->mem_root) List<Item>;
+ if ($$ == NULL)
MYSQL_YYABORT;
- Select->expr_list.push_front(list);
+ $$->push_back($1);
}
- udf_expr_list3
- { $$= Select->expr_list.pop(); }
- ;
-
-udf_expr_list3:
- udf_expr
- {
- Select->expr_list.head()->push_back($1);
- }
- | udf_expr_list3 ',' udf_expr
- {
- Select->expr_list.head()->push_back($3);
- }
- ;
+ | udf_expr_list ',' udf_expr
+ {
+ $1->push_back($3);
+ $$= $1;
+ }
+ ;
udf_expr:
- remember_name expr remember_end select_alias
- {
- udf_func *udf= Select->udf_list.head();
- /*
- Use Item::name as a storage for the attribute value of user
- defined function argument. It is safe to use Item::name
- because the syntax will not allow having an explicit name here.
- See WL#1017 re. udf attributes.
- */
- if ($4.str)
+ remember_name expr remember_end select_alias
{
- if (!udf)
+ /*
+ Use Item::name as a storage for the attribute value of user
+ defined function argument. It is safe to use Item::name
+ because the syntax will not allow having an explicit name here.
+ See WL#1017 re. udf attributes.
+ */
+ if ($4.str)
{
- /*
- Disallow using AS to specify explicit names for the arguments
- of stored routine calls
- */
- my_parse_error(ER(ER_SYNTAX_ERROR));
- MYSQL_YYABORT;
+ $2->is_autogenerated_name= FALSE;
+ $2->set_name($4.str, $4.length, system_charset_info);
}
-
- $2->is_autogenerated_name= FALSE;
- $2->set_name($4.str, $4.length, system_charset_info);
+ else
+ $2->set_name($1, (uint) ($3 - $1), YYTHD->charset());
+ $$= $2;
}
- else if (udf)
- $2->set_name($1, (uint) ($3 - $1), YYTHD->charset());
- $$= $2;
- }
- ;
+ ;
sum_expr:
- AVG_SYM '(' in_sum_expr ')'
- {
- $$=new Item_sum_avg($3);
+ AVG_SYM '(' in_sum_expr ')'
+ {
+ $$= new (YYTHD->mem_root) Item_sum_avg($3);
if ($$ == NULL)
MYSQL_YYABORT;
}
- | AVG_SYM '(' DISTINCT in_sum_expr ')'
- {
- $$=new Item_sum_avg_distinct($4);
+ | AVG_SYM '(' DISTINCT in_sum_expr ')'
+ {
+ $$= new (YYTHD->mem_root) Item_sum_avg_distinct($4);
if ($$ == NULL)
MYSQL_YYABORT;
}
- | BIT_AND '(' in_sum_expr ')'
- {
- $$=new Item_sum_and($3);
+ | BIT_AND '(' in_sum_expr ')'
+ {
+ $$= new (YYTHD->mem_root) Item_sum_and($3);
if ($$ == NULL)
MYSQL_YYABORT;
}
- | BIT_OR '(' in_sum_expr ')'
- {
- $$=new Item_sum_or($3);
+ | BIT_OR '(' in_sum_expr ')'
+ {
+ $$= new (YYTHD->mem_root) Item_sum_or($3);
if ($$ == NULL)
MYSQL_YYABORT;
}
- | BIT_XOR '(' in_sum_expr ')'
- {
- $$=new Item_sum_xor($3);
+ | BIT_XOR '(' in_sum_expr ')'
+ {
+ $$= new (YYTHD->mem_root) Item_sum_xor($3);
if ($$ == NULL)
MYSQL_YYABORT;
}
- | COUNT_SYM '(' opt_all '*' ')'
- {
- Item *item= new Item_int((int32) 0L,1);
+ | COUNT_SYM '(' opt_all '*' ')'
+ {
+ Item *item= new (YYTHD->mem_root) Item_int((int32) 0L,1);
if (item == NULL)
MYSQL_YYABORT;
- $$=new Item_sum_count(new Item_int((int32) 0L,1));
+ $$= new (YYTHD->mem_root) Item_sum_count(item);
if ($$ == NULL)
MYSQL_YYABORT;
}
- | COUNT_SYM '(' in_sum_expr ')'
- {
- $$=new Item_sum_count($3);
- if ($$ == NULL)
- MYSQL_YYABORT;
- }
- | COUNT_SYM '(' DISTINCT
- { Select->in_sum_expr++; }
- expr_list
- { Select->in_sum_expr--; }
- ')'
- {
- $$=new Item_sum_count_distinct(* $5);
+ | COUNT_SYM '(' in_sum_expr ')'
+ {
+ $$= new (YYTHD->mem_root) Item_sum_count($3);
if ($$ == NULL)
MYSQL_YYABORT;
}
- | GROUP_UNIQUE_USERS '(' text_literal ',' NUM ',' NUM ',' in_sum_expr ')'
- {
- $$= new Item_sum_unique_users($3,atoi($5.str),atoi($7.str),$9);
+ | COUNT_SYM '(' DISTINCT
+ { Select->in_sum_expr++; }
+ expr_list
+ { Select->in_sum_expr--; }
+ ')'
+ {
+ $$= new (YYTHD->mem_root) Item_sum_count_distinct(* $5);
if ($$ == NULL)
MYSQL_YYABORT;
}
- | MIN_SYM '(' in_sum_expr ')'
- {
- $$=new Item_sum_min($3);
+ | MIN_SYM '(' in_sum_expr ')'
+ {
+ $$= new (YYTHD->mem_root) Item_sum_min($3);
if ($$ == NULL)
MYSQL_YYABORT;
}
-/*
- According to ANSI SQL, DISTINCT is allowed and has
- no sence inside MIN and MAX grouping functions; so MIN|MAX(DISTINCT ...)
- is processed like an ordinary MIN | MAX()
- */
- | MIN_SYM '(' DISTINCT in_sum_expr ')'
- {
- $$=new Item_sum_min($4);
+ /*
+ According to ANSI SQL, DISTINCT is allowed and has
+ no sense inside MIN and MAX grouping functions; so MIN|MAX(DISTINCT ...)
+ is processed like an ordinary MIN | MAX()
+ */
+ | MIN_SYM '(' DISTINCT in_sum_expr ')'
+ {
+ $$= new (YYTHD->mem_root) Item_sum_min($4);
if ($$ == NULL)
MYSQL_YYABORT;
}
- | MAX_SYM '(' in_sum_expr ')'
- {
- $$=new Item_sum_max($3);
+ | MAX_SYM '(' in_sum_expr ')'
+ {
+ $$= new (YYTHD->mem_root) Item_sum_max($3);
if ($$ == NULL)
MYSQL_YYABORT;
}
- | MAX_SYM '(' DISTINCT in_sum_expr ')'
- {
- $$=new Item_sum_max($4);
+ | MAX_SYM '(' DISTINCT in_sum_expr ')'
+ {
+ $$= new (YYTHD->mem_root) Item_sum_max($4);
if ($$ == NULL)
MYSQL_YYABORT;
}
- | STD_SYM '(' in_sum_expr ')'
- {
- $$=new Item_sum_std($3, 0);
+ | STD_SYM '(' in_sum_expr ')'
+ {
+ $$= new (YYTHD->mem_root) Item_sum_std($3, 0);
if ($$ == NULL)
MYSQL_YYABORT;
}
- | VARIANCE_SYM '(' in_sum_expr ')'
- {
- $$=new Item_sum_variance($3, 0);
+ | VARIANCE_SYM '(' in_sum_expr ')'
+ {
+ $$= new (YYTHD->mem_root) Item_sum_variance($3, 0);
if ($$ == NULL)
MYSQL_YYABORT;
}
- | STDDEV_SAMP_SYM '(' in_sum_expr ')'
- {
- $$=new Item_sum_std($3, 1);
+ | STDDEV_SAMP_SYM '(' in_sum_expr ')'
+ {
+ $$= new (YYTHD->mem_root) Item_sum_std($3, 1);
if ($$ == NULL)
MYSQL_YYABORT;
}
- | VAR_SAMP_SYM '(' in_sum_expr ')'
- {
- $$=new Item_sum_variance($3, 1);
+ | VAR_SAMP_SYM '(' in_sum_expr ')'
+ {
+ $$= new (YYTHD->mem_root) Item_sum_variance($3, 1);
if ($$ == NULL)
MYSQL_YYABORT;
}
- | SUM_SYM '(' in_sum_expr ')'
- {
- $$=new Item_sum_sum($3);
+ | SUM_SYM '(' in_sum_expr ')'
+ {
+ $$= new (YYTHD->mem_root) Item_sum_sum($3);
if ($$ == NULL)
MYSQL_YYABORT;
}
- | SUM_SYM '(' DISTINCT in_sum_expr ')'
- {
- $$=new Item_sum_sum_distinct($4);
+ | SUM_SYM '(' DISTINCT in_sum_expr ')'
+ {
+ $$= new (YYTHD->mem_root) Item_sum_sum_distinct($4);
if ($$ == NULL)
MYSQL_YYABORT;
}
- | GROUP_CONCAT_SYM '(' opt_distinct
- { Select->in_sum_expr++; }
- expr_list opt_gorder_clause
- opt_gconcat_separator
- ')'
- {
+ | GROUP_CONCAT_SYM '(' opt_distinct
+ { Select->in_sum_expr++; }
+ expr_list opt_gorder_clause
+ opt_gconcat_separator
+ ')'
+ {
SELECT_LEX *sel= Select;
- sel->in_sum_expr--;
- $$=new Item_func_group_concat(Lex->current_context(), $3, $5,
- sel->gorder_list, $7);
+ sel->in_sum_expr--;
+ $$= new (YYTHD->mem_root)
+ Item_func_group_concat(Lex->current_context(), $3, $5,
+ sel->gorder_list, $7);
if ($$ == NULL)
MYSQL_YYABORT;
- $5->empty();
- };
+ $5->empty();
+ }
+ ;
variable:
'@'
@@ -6358,20 +8059,22 @@ variable:
{
$$= $3;
}
- ;
+ ;
variable_aux:
ident_or_text SET_VAR expr
{
- $$= new Item_func_set_user_var($1, $3);
+ Item_func_set_user_var *item;
+ $$= item= new (YYTHD->mem_root) Item_func_set_user_var($1, $3);
if ($$ == NULL)
MYSQL_YYABORT;
LEX *lex= Lex;
lex->uncacheable(UNCACHEABLE_RAND);
+ lex->set_var_list.push_back(item);
}
| ident_or_text
{
- $$= new Item_func_get_user_var($1);
+ $$= new (YYTHD->mem_root) Item_func_get_user_var($1);
if ($$ == NULL)
MYSQL_YYABORT;
LEX *lex= Lex;
@@ -6379,6 +8082,7 @@ variable_aux:
}
| '@' opt_var_ident_type ident_or_text opt_component
{
+ /* disallow "SELECT @@global.global.variable" */
if ($3.str && $4.str && check_reserved_words(&$3))
{
my_parse_error(ER(ER_SYNTAX_ERROR));
@@ -6386,12 +8090,15 @@ variable_aux:
}
if (!($$= get_system_var(YYTHD, $2, $3, $4)))
MYSQL_YYABORT;
+ if (!((Item_func_get_system_var*) $$)->is_written_to_binlog())
+ Lex->set_stmt_unsafe();
}
;
opt_distinct:
- /* empty */ { $$ = 0; }
- |DISTINCT { $$ = 1; };
+ /* empty */ { $$ = 0; }
+ | DISTINCT { $$ = 1; }
+ ;
opt_gconcat_separator:
/* empty */
@@ -6403,98 +8110,114 @@ opt_gconcat_separator:
| SEPARATOR_SYM text_string { $$ = $2; }
;
-
opt_gorder_clause:
- /* empty */
- {
+ /* empty */
+ {
Select->gorder_list = NULL;
- }
- | order_clause
+ }
+ | order_clause
{
SELECT_LEX *select= Select;
select->gorder_list=
- (SQL_LIST*) sql_memdup((char*) &select->order_list,
- sizeof(st_sql_list));
+ (SQL_LIST*) sql_memdup((char*) &select->order_list,
+ sizeof(st_sql_list));
if (select->gorder_list == NULL)
MYSQL_YYABORT;
- select->order_list.empty();
- };
-
+ select->order_list.empty();
+ }
+ ;
in_sum_expr:
- opt_all
- {
- LEX *lex= Lex;
- if (lex->current_select->inc_in_sum_expr())
- {
- my_parse_error(ER(ER_SYNTAX_ERROR));
- MYSQL_YYABORT;
- }
- }
- expr
- {
- Select->in_sum_expr--;
- $$= $3;
- };
+ opt_all
+ {
+ LEX *lex= Lex;
+ if (lex->current_select->inc_in_sum_expr())
+ {
+ my_parse_error(ER(ER_SYNTAX_ERROR));
+ MYSQL_YYABORT;
+ }
+ }
+ expr
+ {
+ Select->in_sum_expr--;
+ $$= $3;
+ }
+ ;
cast_type:
- BINARY opt_field_length { $$=ITEM_CAST_CHAR; Lex->charset= &my_charset_bin; Lex->dec= 0; }
- | CHAR_SYM opt_field_length opt_binary { $$=ITEM_CAST_CHAR; Lex->dec= 0; }
- | NCHAR_SYM opt_field_length { $$=ITEM_CAST_CHAR; Lex->charset= national_charset_info; Lex->dec=0; }
- | SIGNED_SYM { $$=ITEM_CAST_SIGNED_INT; Lex->charset= NULL; Lex->dec=Lex->length= (char*)0; }
- | SIGNED_SYM INT_SYM { $$=ITEM_CAST_SIGNED_INT; Lex->charset= NULL; Lex->dec=Lex->length= (char*)0; }
- | UNSIGNED { $$=ITEM_CAST_UNSIGNED_INT; Lex->charset= NULL; Lex->dec=Lex->length= (char*)0; }
- | UNSIGNED INT_SYM { $$=ITEM_CAST_UNSIGNED_INT; Lex->charset= NULL; Lex->dec=Lex->length= (char*)0; }
- | DATE_SYM { $$=ITEM_CAST_DATE; Lex->charset= NULL; Lex->dec=Lex->length= (char*)0; }
- | TIME_SYM { $$=ITEM_CAST_TIME; Lex->charset= NULL; Lex->dec=Lex->length= (char*)0; }
- | DATETIME { $$=ITEM_CAST_DATETIME; Lex->charset= NULL; Lex->dec=Lex->length= (char*)0; }
- | DECIMAL_SYM float_options { $$=ITEM_CAST_DECIMAL; Lex->charset= NULL; }
- ;
+ BINARY opt_field_length
+ { $$=ITEM_CAST_CHAR; Lex->charset= &my_charset_bin; Lex->dec= 0; }
+ | CHAR_SYM opt_field_length opt_binary
+ { $$=ITEM_CAST_CHAR; Lex->dec= 0; }
+ | NCHAR_SYM opt_field_length
+ { $$=ITEM_CAST_CHAR; Lex->charset= national_charset_info; Lex->dec=0; }
+ | SIGNED_SYM
+ { $$=ITEM_CAST_SIGNED_INT; Lex->charset= NULL; Lex->dec=Lex->length= (char*)0; }
+ | SIGNED_SYM INT_SYM
+ { $$=ITEM_CAST_SIGNED_INT; Lex->charset= NULL; Lex->dec=Lex->length= (char*)0; }
+ | UNSIGNED
+ { $$=ITEM_CAST_UNSIGNED_INT; Lex->charset= NULL; Lex->dec=Lex->length= (char*)0; }
+ | UNSIGNED INT_SYM
+ { $$=ITEM_CAST_UNSIGNED_INT; Lex->charset= NULL; Lex->dec=Lex->length= (char*)0; }
+ | DATE_SYM
+ { $$=ITEM_CAST_DATE; Lex->charset= NULL; Lex->dec=Lex->length= (char*)0; }
+ | TIME_SYM
+ { $$=ITEM_CAST_TIME; Lex->charset= NULL; Lex->dec=Lex->length= (char*)0; }
+ | DATETIME
+ { $$=ITEM_CAST_DATETIME; Lex->charset= NULL; Lex->dec=Lex->length= (char*)0; }
+ | DECIMAL_SYM float_options
+ { $$=ITEM_CAST_DECIMAL; Lex->charset= NULL; }
+ ;
opt_expr_list:
- /* empty */ { $$= NULL; }
- | expr_list { $$= $1;}
- ;
+ /* empty */ { $$= NULL; }
+ | expr_list { $$= $1;}
+ ;
expr_list:
- {
- List<Item> *list= new List<Item>;
- if (list == NULL)
- MYSQL_YYABORT;
- Select->expr_list.push_front(list);
- }
- expr_list2
- { $$= Select->expr_list.pop(); };
-
-expr_list2:
- expr { Select->expr_list.head()->push_back($1); }
- | expr_list2 ',' expr { Select->expr_list.head()->push_back($3); };
+ expr
+ {
+ $$= new (YYTHD->mem_root) List<Item>;
+ if ($$ == NULL)
+ MYSQL_YYABORT;
+ $$->push_back($1);
+ }
+ | expr_list ',' expr
+ {
+ $1->push_back($3);
+ $$= $1;
+ }
+ ;
ident_list_arg:
ident_list { $$= $1; }
- | '(' ident_list ')' { $$= $2; };
+ | '(' ident_list ')' { $$= $2; }
+ ;
ident_list:
- {
- List<Item> *list= new List<Item>;
- if (list == NULL)
- MYSQL_YYABORT;
- Select->expr_list.push_front(new List<Item>);
- }
- ident_list2
- { $$= Select->expr_list.pop(); };
-
-ident_list2:
- simple_ident { Select->expr_list.head()->push_back($1); }
- | ident_list2 ',' simple_ident { Select->expr_list.head()->push_back($3); };
+ simple_ident
+ {
+ $$= new (YYTHD->mem_root) List<Item>;
+ if ($$ == NULL)
+ MYSQL_YYABORT;
+ $$->push_back($1);
+ }
+ | ident_list ',' simple_ident
+ {
+ $1->push_back($3);
+ $$= $1;
+ }
+ ;
opt_expr:
- /* empty */ { $$= NULL; }
- | expr { $$= $1; };
+ /* empty */ { $$= NULL; }
+ | expr { $$= $1; }
+ ;
opt_else:
- /* empty */ { $$= NULL; }
- | ELSE expr { $$= $2; };
+ /* empty */ { $$= NULL; }
+ | ELSE expr { $$= $2; }
+ ;
when_list:
WHEN_SYM expr THEN_SYM expr
@@ -6515,23 +8238,35 @@ when_list:
/* Warning - may return NULL in case of incomplete SELECT */
table_ref:
- table_factor { $$=$1; }
+ table_factor { $$=$1; }
| join_table
{
- LEX *lex= Lex;
+ LEX *lex= Lex;
if (!($$= lex->current_select->nest_last_join(lex->thd)))
MYSQL_YYABORT;
}
;
join_table_list:
- derived_table_list { MYSQL_YYABORT_UNLESS($$=$1); }
- ;
+ derived_table_list { MYSQL_YYABORT_UNLESS($$=$1); }
+ ;
+
+/*
+ The ODBC escape syntax for Outer Join is: '{' OJ join_table '}'
+ The parser does not define OJ as a token, any ident is accepted
+ instead in $2 (ident). Also, all productions from table_ref can
+ be escaped, not only join_table. Both syntax extensions are safe
+ and are ignored.
+*/
+esc_table_ref:
+ table_ref { $$=$1; }
+ | '{' ident table_ref '}' { $$=$3; }
+ ;
/* Warning - may return NULL in case of incomplete SELECT */
derived_table_list:
- table_ref { $$=$1; }
- | derived_table_list ',' table_ref
+ esc_table_ref { $$=$1; }
+ | derived_table_list ',' esc_table_ref
{
MYSQL_YYABORT_UNLESS($1 && ($$=$3));
}
@@ -6545,17 +8280,17 @@ derived_table_list:
and subsequent optimization phases.
*/
join_table:
-/* INNER JOIN variants */
- /*
- Use %prec to evaluate production 'table_ref' before 'normal_join'
- so that [INNER | CROSS] JOIN is properly nested as other
- left-associative joins.
- */
- table_ref normal_join table_ref %prec TABLE_REF_PRIORITY
+ /* INNER JOIN variants */
+ /*
+ Use %prec to evaluate production 'table_ref' before 'normal_join'
+ so that [INNER | CROSS] JOIN is properly nested as other
+ left-associative joins.
+ */
+ table_ref normal_join table_ref %prec TABLE_REF_PRIORITY
{ MYSQL_YYABORT_UNLESS($1 && ($$=$3)); }
- | table_ref STRAIGHT_JOIN table_factor
- { MYSQL_YYABORT_UNLESS($1 && ($$=$3)); $3->straight=1; }
- | table_ref normal_join table_ref
+ | table_ref STRAIGHT_JOIN table_factor
+ { MYSQL_YYABORT_UNLESS($1 && ($$=$3)); $3->straight=1; }
+ | table_ref normal_join table_ref
ON
{
MYSQL_YYABORT_UNLESS($1 && $3);
@@ -6565,7 +8300,7 @@ join_table:
Select->parsing_place= IN_ON;
}
expr
- {
+ {
add_join_on($3,$6);
Lex->pop_context();
Select->parsing_place= NO_MATTER;
@@ -6586,21 +8321,21 @@ join_table:
Lex->pop_context();
Select->parsing_place= NO_MATTER;
}
- | table_ref normal_join table_ref
- USING
- {
+ | table_ref normal_join table_ref
+ USING
+ {
MYSQL_YYABORT_UNLESS($1 && $3);
- }
- '(' using_list ')'
+ }
+ '(' using_list ')'
{ add_join_natural($1,$3,$7,Select); $$=$3; }
- | table_ref NATURAL JOIN_SYM table_factor
- {
+ | table_ref NATURAL JOIN_SYM table_factor
+ {
MYSQL_YYABORT_UNLESS($1 && ($$=$4));
add_join_natural($1,$4,NULL,Select);
}
-/* LEFT JOIN variants */
- | table_ref LEFT opt_outer JOIN_SYM table_ref
+ /* LEFT JOIN variants */
+ | table_ref LEFT opt_outer JOIN_SYM table_ref
ON
{
MYSQL_YYABORT_UNLESS($1 && $5);
@@ -6610,33 +8345,33 @@ join_table:
Select->parsing_place= IN_ON;
}
expr
- {
+ {
add_join_on($5,$8);
Lex->pop_context();
$5->outer_join|=JOIN_TYPE_LEFT;
$$=$5;
Select->parsing_place= NO_MATTER;
}
- | table_ref LEFT opt_outer JOIN_SYM table_factor
- {
+ | table_ref LEFT opt_outer JOIN_SYM table_factor
+ {
MYSQL_YYABORT_UNLESS($1 && $5);
- }
- USING '(' using_list ')'
+ }
+ USING '(' using_list ')'
{
add_join_natural($1,$5,$9,Select);
$5->outer_join|=JOIN_TYPE_LEFT;
$$=$5;
}
- | table_ref NATURAL LEFT opt_outer JOIN_SYM table_factor
- {
+ | table_ref NATURAL LEFT opt_outer JOIN_SYM table_factor
+ {
MYSQL_YYABORT_UNLESS($1 && $6);
- add_join_natural($1,$6,NULL,Select);
- $6->outer_join|=JOIN_TYPE_LEFT;
- $$=$6;
- }
+ add_join_natural($1,$6,NULL,Select);
+ $6->outer_join|=JOIN_TYPE_LEFT;
+ $$=$6;
+ }
-/* RIGHT JOIN variants */
- | table_ref RIGHT opt_outer JOIN_SYM table_ref
+ /* RIGHT JOIN variants */
+ | table_ref RIGHT opt_outer JOIN_SYM table_ref
ON
{
MYSQL_YYABORT_UNLESS($1 && $5);
@@ -6647,90 +8382,68 @@ join_table:
}
expr
{
- LEX *lex= Lex;
+ LEX *lex= Lex;
if (!($$= lex->current_select->convert_right_join()))
MYSQL_YYABORT;
add_join_on($$, $8);
Lex->pop_context();
Select->parsing_place= NO_MATTER;
}
- | table_ref RIGHT opt_outer JOIN_SYM table_factor
- {
+ | table_ref RIGHT opt_outer JOIN_SYM table_factor
+ {
MYSQL_YYABORT_UNLESS($1 && $5);
- }
- USING '(' using_list ')'
+ }
+ USING '(' using_list ')'
{
- LEX *lex= Lex;
+ LEX *lex= Lex;
if (!($$= lex->current_select->convert_right_join()))
MYSQL_YYABORT;
add_join_natural($$,$5,$9,Select);
}
- | table_ref NATURAL RIGHT opt_outer JOIN_SYM table_factor
- {
+ | table_ref NATURAL RIGHT opt_outer JOIN_SYM table_factor
+ {
MYSQL_YYABORT_UNLESS($1 && $6);
- add_join_natural($6,$1,NULL,Select);
- LEX *lex= Lex;
+ add_join_natural($6,$1,NULL,Select);
+ LEX *lex= Lex;
if (!($$= lex->current_select->convert_right_join()))
MYSQL_YYABORT;
- };
+ }
+ ;
normal_join:
- JOIN_SYM {}
- | INNER_SYM JOIN_SYM {}
- | CROSS JOIN_SYM {}
- ;
+ JOIN_SYM {}
+ | INNER_SYM JOIN_SYM {}
+ | CROSS JOIN_SYM {}
+ ;
/* Warning - may return NULL in case of incomplete SELECT */
table_factor:
- {
- SELECT_LEX *sel= Select;
- sel->use_index_ptr=sel->ignore_index_ptr=0;
- sel->table_join_options= 0;
- }
- table_ident opt_table_alias opt_key_definition
- {
- LEX *lex= Lex;
- SELECT_LEX *sel= lex->current_select;
- if (!($$= sel->add_table_to_list(lex->thd, $2, $3,
- sel->get_table_join_options(),
- lex->lock_option,
- sel->get_use_index(),
- sel->get_ignore_index())))
- MYSQL_YYABORT;
- sel->add_joined_table($$);
- }
- | '{' ident table_ref LEFT OUTER JOIN_SYM table_ref
- ON
{
- /* Change the current name resolution context to a local context. */
- if (push_new_name_resolution_context(YYTHD, $3, $7))
- MYSQL_YYABORT;
-
+ SELECT_LEX *sel= Select;
+ sel->table_join_options= 0;
}
- expr '}'
- {
- LEX *lex= Lex;
- MYSQL_YYABORT_UNLESS($3 && $7);
- add_join_on($7,$10);
- Lex->pop_context();
- $7->outer_join|=JOIN_TYPE_LEFT;
- $$=$7;
- if (!($$= lex->current_select->nest_last_join(lex->thd)))
+ table_ident opt_table_alias opt_key_definition
+ {
+ if (!($$= Select->add_table_to_list(YYTHD, $2, $3,
+ Select->get_table_join_options(),
+ Lex->lock_option,
+ Select->pop_index_hints())))
MYSQL_YYABORT;
+ Select->add_joined_table($$);
}
- | select_derived_init get_select_lex select_derived2
+ | select_derived_init get_select_lex select_derived2
{
LEX *lex= Lex;
SELECT_LEX *sel= lex->current_select;
if ($1)
{
- if (sel->set_braces(1))
- {
+ if (sel->set_braces(1))
+ {
my_parse_error(ER(ER_SYNTAX_ERROR));
- MYSQL_YYABORT;
- }
+ MYSQL_YYABORT;
+ }
/* select in braces, can't contain global parameters */
- if (sel->master_unit()->fake_select_lex)
+ if (sel->master_unit()->fake_select_lex)
sel->master_unit()->global_parameters=
sel->master_unit()->fake_select_lex;
}
@@ -6740,55 +8453,55 @@ 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
- {
- /* Use $2 instead of Lex->current_select as derived table will
- alter value of Lex->current_select. */
-
- if (!($3 || $6) && $2->embedding &&
- !$2->embedding->nested_join->join_list.elements)
+ | '(' get_select_lex select_derived union_opt ')' opt_table_alias
{
- /* we have a derived table ($3 == NULL) but no alias,
- Since we are nested in further parentheses so we
- can pass NULL to the outer level parentheses
- Permits parsing of "((((select ...))) as xyz)" */
- $$= 0;
- }
- else
- if (!$3)
- {
- /* Handle case of derived table, alias may be NULL if there
- are no outer parentheses, add_table_to_list() will throw
- error in this case */
- LEX *lex=Lex;
- SELECT_LEX *sel= lex->current_select;
- SELECT_LEX_UNIT *unit= sel->master_unit();
- lex->current_select= sel= unit->outer_select();
- if (!($$= sel->
- add_table_to_list(lex->thd, new Table_ident(unit), $6, 0,
- TL_READ,(List<String> *)0,
- (List<String> *)0)))
-
- MYSQL_YYABORT;
- sel->add_joined_table($$);
- lex->pop_context();
+ /* Use $2 instead of Lex->current_select as derived table will
+ alter value of Lex->current_select. */
+
+ if (!($3 || $6) && $2->embedding &&
+ !$2->embedding->nested_join->join_list.elements)
+ {
+ /* we have a derived table ($3 == NULL) but no alias,
+ Since we are nested in further parentheses so we
+ can pass NULL to the outer level parentheses
+ Permits parsing of "((((select ...))) as xyz)" */
+ $$= 0;
+ }
+ else if (!$3)
+ {
+ /* Handle case of derived table, alias may be NULL if there
+ are no outer parentheses, add_table_to_list() will throw
+ error in this case */
+ LEX *lex=Lex;
+ SELECT_LEX *sel= lex->current_select;
+ SELECT_LEX_UNIT *unit= sel->master_unit();
+ lex->current_select= sel= unit->outer_select();
+ Table_ident *ti= new Table_ident(unit);
+ if (ti == NULL)
+ MYSQL_YYABORT;
+ if (!($$= sel->add_table_to_list(lex->thd,
+ ti, $6, 0,
+ TL_READ)))
+
+ MYSQL_YYABORT;
+ sel->add_joined_table($$);
+ lex->pop_context();
+ }
+ else if ($4 || $6)
+ {
+ /* simple nested joins cannot have aliases or unions */
+ my_parse_error(ER(ER_SYNTAX_ERROR));
+ MYSQL_YYABORT;
+ }
+ else
+ $$= $3;
}
- else
- if ($4 || $6)
- {
- /* simple nested joins cannot have aliases or unions */
- my_parse_error(ER(ER_SYNTAX_ERROR));
- MYSQL_YYABORT;
- }
- else
- $$= $3;
- }
;
/* handle contents of parentheses in join expression */
select_derived:
- get_select_lex
- {
+ get_select_lex
+ {
LEX *lex= Lex;
if ($1->init_nested_join(lex->thd))
MYSQL_YYABORT;
@@ -6804,38 +8517,37 @@ select_derived:
if (!$3 && $$)
{
my_parse_error(ER(ER_SYNTAX_ERROR));
- MYSQL_YYABORT;
+ MYSQL_YYABORT;
}
}
- ;
+ ;
select_derived2:
- {
- LEX *lex= Lex;
- lex->derived_tables|= DERIVED_SUBQUERY;
- if (lex->sql_command == (int)SQLCOM_HA_READ ||
- lex->sql_command == (int)SQLCOM_KILL ||
- lex->sql_command == (int)SQLCOM_PURGE)
- {
- my_parse_error(ER(ER_SYNTAX_ERROR));
- MYSQL_YYABORT;
- }
- if (lex->current_select->linkage == GLOBAL_OPTIONS_TYPE ||
- mysql_new_select(lex, 1))
- MYSQL_YYABORT;
- mysql_init_select(lex);
- lex->current_select->linkage= DERIVED_TABLE_TYPE;
- lex->current_select->parsing_place= SELECT_LIST;
- }
- select_options select_item_list
- {
- Select->parsing_place= NO_MATTER;
- }
- opt_select_from
+ {
+ LEX *lex= Lex;
+ lex->derived_tables|= DERIVED_SUBQUERY;
+ if (!lex->expr_allows_subselect ||
+ lex->sql_command == (int)SQLCOM_PURGE)
+ {
+ my_parse_error(ER(ER_SYNTAX_ERROR));
+ MYSQL_YYABORT;
+ }
+ if (lex->current_select->linkage == GLOBAL_OPTIONS_TYPE ||
+ mysql_new_select(lex, 1))
+ MYSQL_YYABORT;
+ mysql_init_select(lex);
+ lex->current_select->linkage= DERIVED_TABLE_TYPE;
+ lex->current_select->parsing_place= SELECT_LIST;
+ }
+ select_options select_item_list
+ {
+ Select->parsing_place= NO_MATTER;
+ }
+ opt_select_from
;
get_select_lex:
- /* Empty */ { $$= Select; }
+ /* Empty */ { $$= Select; }
;
select_derived_init:
@@ -6852,11 +8564,11 @@ select_derived_init:
SELECT_LEX *sel= lex->current_select;
TABLE_LIST *embedding;
if (!sel->embedding || sel->end_nested_join(lex->thd))
- {
+ {
/* we are not in parentheses */
my_parse_error(ER(ER_SYNTAX_ERROR));
- MYSQL_YYABORT;
- }
+ MYSQL_YYABORT;
+ }
embedding= Select->embedding;
$$= embedding &&
!embedding->nested_join->join_list.elements;
@@ -6865,90 +8577,85 @@ select_derived_init:
;
opt_outer:
- /* empty */ {}
- | OUTER {};
+ /* empty */ {}
+ | OUTER {}
+ ;
+
+index_hint_clause:
+ /* empty */
+ {
+ $$= global_system_variables.old_mode ?
+ INDEX_HINT_MASK_JOIN : INDEX_HINT_MASK_ALL;
+ }
+ | FOR_SYM JOIN_SYM { $$= INDEX_HINT_MASK_JOIN; }
+ | FOR_SYM ORDER_SYM BY { $$= INDEX_HINT_MASK_ORDER; }
+ | FOR_SYM GROUP_SYM BY { $$= INDEX_HINT_MASK_GROUP; }
+ ;
+
+index_hint_type:
+ FORCE_SYM { $$= INDEX_HINT_FORCE; }
+ | IGNORE_SYM { $$= INDEX_HINT_IGNORE; }
+ ;
+
+index_hint_definition:
+ index_hint_type key_or_index index_hint_clause
+ {
+ Select->set_index_hint_type($1, $3);
+ }
+ '(' key_usage_list ')'
+ | USE_SYM key_or_index index_hint_clause
+ {
+ Select->set_index_hint_type(INDEX_HINT_USE, $3);
+ }
+ '(' opt_key_usage_list ')'
+ ;
-opt_for_join:
- /* empty */
- | FOR_SYM JOIN_SYM;
+index_hints_list:
+ index_hint_definition
+ | index_hints_list index_hint_definition
+ ;
+
+opt_index_hints_list:
+ /* empty */
+ | { Select->alloc_index_hints(YYTHD); } index_hints_list
+ ;
opt_key_definition:
- /* empty */ {}
- | USE_SYM key_usage_list
- {
- SELECT_LEX *sel= Select;
- sel->use_index= *$2;
- sel->use_index_ptr= &sel->use_index;
- }
- | FORCE_SYM key_usage_list
- {
- SELECT_LEX *sel= Select;
- sel->use_index= *$2;
- sel->use_index_ptr= &sel->use_index;
- sel->table_join_options|= TL_OPTION_FORCE_INDEX;
- }
- | IGNORE_SYM key_usage_list
- {
- SELECT_LEX *sel= Select;
- sel->ignore_index= *$2;
- sel->ignore_index_ptr= &sel->ignore_index;
- };
+ { Select->clear_index_hints(); }
+ opt_index_hints_list
+ ;
-key_usage_list:
- key_or_index opt_for_join
- { Select->interval_list.empty(); }
- '(' key_list_or_empty ')'
- { $$= &Select->interval_list; }
- ;
+opt_key_usage_list:
+ /* empty */ { Select->add_index_hint(YYTHD, NULL, 0); }
+ | key_usage_list {}
+ ;
-key_list_or_empty:
- /* empty */ {}
- | key_usage_list2 {}
- ;
+key_usage_element:
+ ident
+ { Select->add_index_hint(YYTHD, $1.str, $1.length); }
+ | PRIMARY_SYM
+ { Select->add_index_hint(YYTHD, (char *)"PRIMARY", 7); }
+ ;
-key_usage_list2:
- key_usage_list2 ',' ident
- {
- String *s= new (YYTHD->mem_root) String((const char*) $3.str,
- $3.length,
- system_charset_info);
- if (s == NULL)
- MYSQL_YYABORT;
- Select->interval_list.push_back(s);
- }
- | ident
- {
- String *s= new (YYTHD->mem_root) String((const char*) $1.str,
- $1.length,
- system_charset_info);
- if (s == NULL)
- MYSQL_YYABORT;
- Select->interval_list.push_back(s);
- }
- | PRIMARY_SYM
- {
- String *s= new (YYTHD->mem_root) String("PRIMARY", 7,
- system_charset_info);
- if (s == NULL)
- MYSQL_YYABORT;
- Select->interval_list.push_back(s);
- }
+key_usage_list:
+ key_usage_element
+ | key_usage_list ',' key_usage_element
;
using_list:
- ident
- {
+ ident
+ {
if (!($$= new List<String>))
- MYSQL_YYABORT;
+ MYSQL_YYABORT;
String *s= new (YYTHD->mem_root) String((const char *) $1.str,
$1.length,
system_charset_info);
if (s == NULL)
MYSQL_YYABORT;
$$->push_back(s);
- }
- | using_list ',' ident
- {
+ }
+ | using_list ',' ident
+ {
String *s= new (YYTHD->mem_root) String((const char *) $3.str,
$3.length,
system_charset_info);
@@ -6956,21 +8663,23 @@ using_list:
MYSQL_YYABORT;
$1->push_back(s);
$$= $1;
- };
+ }
+ ;
interval:
- interval_time_st {}
- | DAY_HOUR_SYM { $$=INTERVAL_DAY_HOUR; }
- | DAY_MICROSECOND_SYM { $$=INTERVAL_DAY_MICROSECOND; }
- | DAY_MINUTE_SYM { $$=INTERVAL_DAY_MINUTE; }
- | DAY_SECOND_SYM { $$=INTERVAL_DAY_SECOND; }
- | HOUR_MICROSECOND_SYM { $$=INTERVAL_HOUR_MICROSECOND; }
- | HOUR_MINUTE_SYM { $$=INTERVAL_HOUR_MINUTE; }
- | HOUR_SECOND_SYM { $$=INTERVAL_HOUR_SECOND; }
- | MINUTE_MICROSECOND_SYM { $$=INTERVAL_MINUTE_MICROSECOND; }
- | MINUTE_SECOND_SYM { $$=INTERVAL_MINUTE_SECOND; }
- | SECOND_MICROSECOND_SYM { $$=INTERVAL_SECOND_MICROSECOND; }
- | YEAR_MONTH_SYM { $$=INTERVAL_YEAR_MONTH; };
+ interval_time_st {}
+ | DAY_HOUR_SYM { $$=INTERVAL_DAY_HOUR; }
+ | DAY_MICROSECOND_SYM { $$=INTERVAL_DAY_MICROSECOND; }
+ | DAY_MINUTE_SYM { $$=INTERVAL_DAY_MINUTE; }
+ | DAY_SECOND_SYM { $$=INTERVAL_DAY_SECOND; }
+ | HOUR_MICROSECOND_SYM { $$=INTERVAL_HOUR_MICROSECOND; }
+ | HOUR_MINUTE_SYM { $$=INTERVAL_HOUR_MINUTE; }
+ | HOUR_SECOND_SYM { $$=INTERVAL_HOUR_SECOND; }
+ | MINUTE_MICROSECOND_SYM { $$=INTERVAL_MINUTE_MICROSECOND; }
+ | MINUTE_SECOND_SYM { $$=INTERVAL_MINUTE_SECOND; }
+ | SECOND_MICROSECOND_SYM { $$=INTERVAL_SECOND_MICROSECOND; }
+ | YEAR_MONTH_SYM { $$=INTERVAL_YEAR_MONTH; }
+ ;
interval_time_stamp:
interval_time_st {}
@@ -6988,38 +8697,39 @@ interval_time_stamp:
implementation without changing its
resolution.
*/
- WARN_DEPRECATED("FRAC_SECOND", "MICROSECOND"); // Will be removed in 6.2
+ WARN_DEPRECATED(yythd, "6.2", "FRAC_SECOND", "MICROSECOND");
}
;
interval_time_st:
- DAY_SYM { $$=INTERVAL_DAY; }
- | WEEK_SYM { $$=INTERVAL_WEEK; }
- | HOUR_SYM { $$=INTERVAL_HOUR; }
- | MINUTE_SYM { $$=INTERVAL_MINUTE; }
- | MONTH_SYM { $$=INTERVAL_MONTH; }
- | QUARTER_SYM { $$=INTERVAL_QUARTER; }
- | SECOND_SYM { $$=INTERVAL_SECOND; }
- | MICROSECOND_SYM { $$=INTERVAL_MICROSECOND; }
- | YEAR_SYM { $$=INTERVAL_YEAR; }
+ DAY_SYM { $$=INTERVAL_DAY; }
+ | WEEK_SYM { $$=INTERVAL_WEEK; }
+ | HOUR_SYM { $$=INTERVAL_HOUR; }
+ | MINUTE_SYM { $$=INTERVAL_MINUTE; }
+ | MONTH_SYM { $$=INTERVAL_MONTH; }
+ | QUARTER_SYM { $$=INTERVAL_QUARTER; }
+ | SECOND_SYM { $$=INTERVAL_SECOND; }
+ | MICROSECOND_SYM { $$=INTERVAL_MICROSECOND; }
+ | YEAR_SYM { $$=INTERVAL_YEAR; }
;
date_time_type:
- DATE_SYM {$$=MYSQL_TIMESTAMP_DATE;}
- | TIME_SYM {$$=MYSQL_TIMESTAMP_TIME;}
- | DATETIME {$$=MYSQL_TIMESTAMP_DATETIME;}
- | TIMESTAMP {$$=MYSQL_TIMESTAMP_DATETIME;}
+ DATE_SYM {$$=MYSQL_TIMESTAMP_DATE;}
+ | TIME_SYM {$$=MYSQL_TIMESTAMP_TIME;}
+ | DATETIME {$$=MYSQL_TIMESTAMP_DATETIME;}
+ | TIMESTAMP {$$=MYSQL_TIMESTAMP_DATETIME;}
;
table_alias:
- /* empty */
- | AS
- | EQ;
+ /* empty */
+ | AS
+ | EQ
+ ;
opt_table_alias:
- /* empty */ { $$=0; }
- | table_alias ident
- {
+ /* empty */ { $$=0; }
+ | table_alias ident
+ {
$$= (LEX_STRING*) sql_memdup(&$2,sizeof(LEX_STRING));
if ($$ == NULL)
MYSQL_YYABORT;
@@ -7027,101 +8737,103 @@ opt_table_alias:
;
opt_all:
- /* empty */
- | ALL
- ;
+ /* empty */
+ | ALL
+ ;
where_clause:
- /* empty */ { Select->where= 0; }
- | WHERE
+ /* empty */ { Select->where= 0; }
+ | WHERE
{
Select->parsing_place= IN_WHERE;
}
expr
- {
+ {
SELECT_LEX *select= Select;
- select->where= $3;
+ select->where= $3;
select->parsing_place= NO_MATTER;
- if ($3)
- $3->top_level_item();
- }
- ;
+ if ($3)
+ $3->top_level_item();
+ }
+ ;
having_clause:
- /* empty */
- | HAVING
- {
- Select->parsing_place= IN_HAVING;
- }
- expr
- {
- SELECT_LEX *sel= Select;
- sel->having= $3;
- sel->parsing_place= NO_MATTER;
- if ($3)
- $3->top_level_item();
- }
- ;
+ /* empty */
+ | HAVING
+ {
+ Select->parsing_place= IN_HAVING;
+ }
+ expr
+ {
+ SELECT_LEX *sel= Select;
+ sel->having= $3;
+ sel->parsing_place= NO_MATTER;
+ if ($3)
+ $3->top_level_item();
+ }
+ ;
opt_escape:
- ESCAPE_SYM simple_expr
+ ESCAPE_SYM simple_expr
{
Lex->escape_used= TRUE;
$$= $2;
}
- | /* empty */
+ | /* empty */
{
+ THD *thd= YYTHD;
Lex->escape_used= FALSE;
- $$= ((YYTHD->variables.sql_mode & MODE_NO_BACKSLASH_ESCAPES) ?
- new Item_string("", 0, &my_charset_latin1) :
- new Item_string("\\", 1, &my_charset_latin1));
+ $$= ((thd->variables.sql_mode & MODE_NO_BACKSLASH_ESCAPES) ?
+ new (thd->mem_root) Item_string("", 0, &my_charset_latin1) :
+ new (thd->mem_root) Item_string("\\", 1, &my_charset_latin1));
if ($$ == NULL)
MYSQL_YYABORT;
}
;
-
/*
group by statement in select
*/
group_clause:
- /* empty */
- | GROUP BY group_list olap_opt;
+ /* empty */
+ | GROUP_SYM BY group_list olap_opt
+ ;
group_list:
- group_list ',' order_ident order_dir
- { if (add_group_to_list(YYTHD, $3,(bool) $4)) MYSQL_YYABORT; }
- | order_ident order_dir
- { if (add_group_to_list(YYTHD, $1,(bool) $2)) MYSQL_YYABORT; };
+ group_list ',' order_ident order_dir
+ { if (add_group_to_list(YYTHD, $3,(bool) $4)) MYSQL_YYABORT; }
+ | order_ident order_dir
+ { if (add_group_to_list(YYTHD, $1,(bool) $2)) MYSQL_YYABORT; }
+ ;
olap_opt:
- /* empty */ {}
- | WITH CUBE_SYM
- {
- LEX *lex=Lex;
- if (lex->current_select->linkage == GLOBAL_OPTIONS_TYPE)
- {
- my_error(ER_WRONG_USAGE, MYF(0), "WITH CUBE",
- "global union parameters");
- MYSQL_YYABORT;
- }
- lex->current_select->olap= CUBE_TYPE;
- my_error(ER_NOT_SUPPORTED_YET, MYF(0), "CUBE");
- MYSQL_YYABORT; /* To be deleted in 5.1 */
- }
- | WITH ROLLUP_SYM
- {
- LEX *lex= Lex;
- if (lex->current_select->linkage == GLOBAL_OPTIONS_TYPE)
- {
- my_error(ER_WRONG_USAGE, MYF(0), "WITH ROLLUP",
- "global union parameters");
- MYSQL_YYABORT;
- }
- lex->current_select->olap= ROLLUP_TYPE;
- }
- ;
+ /* empty */ {}
+ | WITH CUBE_SYM
+ {
+ LEX *lex=Lex;
+ if (lex->current_select->linkage == GLOBAL_OPTIONS_TYPE)
+ {
+ my_error(ER_WRONG_USAGE, MYF(0), "WITH CUBE",
+ "global union parameters");
+ MYSQL_YYABORT;
+ }
+ lex->current_select->olap= CUBE_TYPE;
+ my_error(ER_NOT_SUPPORTED_YET, MYF(0), "CUBE");
+ MYSQL_YYABORT; /* To be deleted in 5.1 */
+ }
+ | WITH ROLLUP_SYM
+ {
+ LEX *lex= Lex;
+ if (lex->current_select->linkage == GLOBAL_OPTIONS_TYPE)
+ {
+ my_error(ER_WRONG_USAGE, MYF(0), "WITH ROLLUP",
+ "global union parameters");
+ MYSQL_YYABORT;
+ }
+ lex->current_select->olap= ROLLUP_TYPE;
+ }
+ ;
/*
Order by statement in ALTER TABLE
@@ -7151,97 +8863,102 @@ alter_order_item:
*/
opt_order_clause:
- /* empty */
- | order_clause;
+ /* empty */
+ | order_clause
+ ;
order_clause:
- ORDER_SYM BY
- {
- LEX *lex=Lex;
- SELECT_LEX *sel= lex->current_select;
- SELECT_LEX_UNIT *unit= sel-> master_unit();
- if (sel->linkage != GLOBAL_OPTIONS_TYPE &&
- sel->olap != UNSPECIFIED_OLAP_TYPE &&
- (sel->linkage != UNION_TYPE || sel->braces))
- {
- my_error(ER_WRONG_USAGE, MYF(0),
- "CUBE/ROLLUP", "ORDER BY");
- MYSQL_YYABORT;
- }
- if (lex->sql_command != SQLCOM_ALTER_TABLE && !unit->fake_select_lex)
+ ORDER_SYM BY
{
- /*
- A query of the of the form (SELECT ...) ORDER BY order_list is
- executed in the same way as the query
- SELECT ... ORDER BY order_list
- unless the SELECT construct contains ORDER BY or LIMIT clauses.
- Otherwise we create a fake SELECT_LEX if it has not been created
- yet.
- */
- SELECT_LEX *first_sl= unit->first_select();
- if (!first_sl->next_select() &&
- (first_sl->order_list.elements ||
- first_sl->select_limit) &&
- unit->add_fake_select_lex(lex->thd))
+ LEX *lex=Lex;
+ SELECT_LEX *sel= lex->current_select;
+ SELECT_LEX_UNIT *unit= sel-> master_unit();
+ if (sel->linkage != GLOBAL_OPTIONS_TYPE &&
+ sel->olap != UNSPECIFIED_OLAP_TYPE &&
+ (sel->linkage != UNION_TYPE || sel->braces))
+ {
+ my_error(ER_WRONG_USAGE, MYF(0),
+ "CUBE/ROLLUP", "ORDER BY");
MYSQL_YYABORT;
+ }
+ if (lex->sql_command != SQLCOM_ALTER_TABLE && !unit->fake_select_lex)
+ {
+ /*
+ A query of the of the form (SELECT ...) ORDER BY order_list is
+ executed in the same way as the query
+ SELECT ... ORDER BY order_list
+ unless the SELECT construct contains ORDER BY or LIMIT clauses.
+ Otherwise we create a fake SELECT_LEX if it has not been created
+ yet.
+ */
+ SELECT_LEX *first_sl= unit->first_select();
+ if (!unit->is_union() &&
+ (first_sl->order_list.elements ||
+ first_sl->select_limit) &&
+ unit->add_fake_select_lex(lex->thd))
+ MYSQL_YYABORT;
+ }
}
- } order_list;
+ order_list
+ ;
order_list:
- order_list ',' order_ident order_dir
- { if (add_order_to_list(YYTHD, $3,(bool) $4)) MYSQL_YYABORT; }
- | order_ident order_dir
- { if (add_order_to_list(YYTHD, $1,(bool) $2)) MYSQL_YYABORT; };
+ order_list ',' order_ident order_dir
+ { if (add_order_to_list(YYTHD, $3,(bool) $4)) MYSQL_YYABORT; }
+ | order_ident order_dir
+ { if (add_order_to_list(YYTHD, $1,(bool) $2)) MYSQL_YYABORT; }
+ ;
order_dir:
- /* empty */ { $$ = 1; }
- | ASC { $$ =1; }
- | DESC { $$ =0; };
-
+ /* empty */ { $$ = 1; }
+ | ASC { $$ =1; }
+ | DESC { $$ =0; }
+ ;
opt_limit_clause_init:
- /* empty */
- {
- LEX *lex= Lex;
- SELECT_LEX *sel= lex->current_select;
- sel->offset_limit= 0;
- sel->select_limit= 0;
- }
- | limit_clause {}
- ;
+ /* empty */
+ {
+ LEX *lex= Lex;
+ SELECT_LEX *sel= lex->current_select;
+ sel->offset_limit= 0;
+ sel->select_limit= 0;
+ }
+ | limit_clause {}
+ ;
opt_limit_clause:
- /* empty */ {}
- | limit_clause {}
- ;
+ /* empty */ {}
+ | limit_clause {}
+ ;
limit_clause:
- LIMIT limit_options {}
- ;
+ LIMIT limit_options {}
+ ;
limit_options:
- limit_option
- {
+ limit_option
+ {
SELECT_LEX *sel= Select;
sel->select_limit= $1;
sel->offset_limit= 0;
- sel->explicit_limit= 1;
- }
- | limit_option ',' limit_option
- {
- SELECT_LEX *sel= Select;
- sel->select_limit= $3;
- sel->offset_limit= $1;
- sel->explicit_limit= 1;
- }
- | limit_option OFFSET_SYM limit_option
- {
- SELECT_LEX *sel= Select;
- sel->select_limit= $1;
- sel->offset_limit= $3;
- sel->explicit_limit= 1;
- }
- ;
+ sel->explicit_limit= 1;
+ }
+ | limit_option ',' limit_option
+ {
+ SELECT_LEX *sel= Select;
+ sel->select_limit= $3;
+ sel->offset_limit= $1;
+ sel->explicit_limit= 1;
+ }
+ | limit_option OFFSET_SYM limit_option
+ {
+ SELECT_LEX *sel= Select;
+ sel->select_limit= $1;
+ sel->offset_limit= $3;
+ sel->explicit_limit= 1;
+ }
+ ;
+
limit_option:
param_marker
{
@@ -7249,59 +8966,90 @@ limit_option:
}
| ULONGLONG_NUM
{
- $$= new Item_uint($1.str, $1.length);
+ $$= new (YYTHD->mem_root) Item_uint($1.str, $1.length);
if ($$ == NULL)
MYSQL_YYABORT;
}
| LONG_NUM
{
- $$= new Item_uint($1.str, $1.length);
+ $$= new (YYTHD->mem_root) Item_uint($1.str, $1.length);
if ($$ == NULL)
MYSQL_YYABORT;
}
| NUM
{
- $$= new Item_uint($1.str, $1.length);
+ $$= new (YYTHD->mem_root) Item_uint($1.str, $1.length);
if ($$ == NULL)
MYSQL_YYABORT;
}
;
delete_limit_clause:
- /* empty */
- {
- LEX *lex=Lex;
- lex->current_select->select_limit= 0;
- }
- | LIMIT limit_option
- {
- SELECT_LEX *sel= Select;
- sel->select_limit= $2;
- sel->explicit_limit= 1;
- };
+ /* empty */
+ {
+ LEX *lex=Lex;
+ lex->current_select->select_limit= 0;
+ }
+ | LIMIT limit_option
+ {
+ SELECT_LEX *sel= Select;
+ sel->select_limit= $2;
+ sel->explicit_limit= 1;
+ }
+ ;
ulong_num:
NUM { int error; $$= (ulong) my_strtoll10($1.str, (char**) 0, &error); }
- | HEX_NUM { $$= (ulong) strtol($1.str, (char**) 0, 16); }
- | LONG_NUM { int error; $$= (ulong) my_strtoll10($1.str, (char**) 0, &error); }
- | ULONGLONG_NUM { int error; $$= (ulong) my_strtoll10($1.str, (char**) 0, &error); }
+ | HEX_NUM { $$= (ulong) strtol($1.str, (char**) 0, 16); }
+ | LONG_NUM { int error; $$= (ulong) my_strtoll10($1.str, (char**) 0, &error); }
+ | ULONGLONG_NUM { int error; $$= (ulong) my_strtoll10($1.str, (char**) 0, &error); }
| DECIMAL_NUM { int error; $$= (ulong) my_strtoll10($1.str, (char**) 0, &error); }
- | FLOAT_NUM { int error; $$= (ulong) my_strtoll10($1.str, (char**) 0, &error); }
- ;
+ | FLOAT_NUM { int error; $$= (ulong) my_strtoll10($1.str, (char**) 0, &error); }
+ ;
+
+real_ulong_num:
+ NUM { int error; $$= (ulong) my_strtoll10($1.str, (char**) 0, &error); }
+ | HEX_NUM { $$= (ulong) strtol($1.str, (char**) 0, 16); }
+ | LONG_NUM { int error; $$= (ulong) my_strtoll10($1.str, (char**) 0, &error); }
+ | ULONGLONG_NUM { int error; $$= (ulong) my_strtoll10($1.str, (char**) 0, &error); }
+ | dec_num_error { MYSQL_YYABORT; }
+ ;
ulonglong_num:
- NUM { int error; $$= (ulonglong) my_strtoll10($1.str, (char**) 0, &error); }
- | ULONGLONG_NUM { int error; $$= (ulonglong) my_strtoll10($1.str, (char**) 0, &error); }
- | LONG_NUM { int error; $$= (ulonglong) my_strtoll10($1.str, (char**) 0, &error); }
- | DECIMAL_NUM { int error; $$= (ulonglong) my_strtoll10($1.str, (char**) 0, &error); }
- | FLOAT_NUM { int error; $$= (ulonglong) my_strtoll10($1.str, (char**) 0, &error); }
+ NUM { int error; $$= (ulonglong) my_strtoll10($1.str, (char**) 0, &error); }
+ | ULONGLONG_NUM { int error; $$= (ulonglong) my_strtoll10($1.str, (char**) 0, &error); }
+ | LONG_NUM { int error; $$= (ulonglong) my_strtoll10($1.str, (char**) 0, &error); }
+ | DECIMAL_NUM { int error; $$= (ulonglong) my_strtoll10($1.str, (char**) 0, &error); }
+ | FLOAT_NUM { int error; $$= (ulonglong) my_strtoll10($1.str, (char**) 0, &error); }
+ ;
+
+real_ulonglong_num:
+ NUM { int error; $$= (ulonglong) my_strtoll10($1.str, (char**) 0, &error); }
+ | ULONGLONG_NUM { int error; $$= (ulonglong) my_strtoll10($1.str, (char**) 0, &error); }
+ | LONG_NUM { int error; $$= (ulonglong) my_strtoll10($1.str, (char**) 0, &error); }
+ | dec_num_error { MYSQL_YYABORT; }
+ ;
+
+dec_num_error:
+ dec_num
+ { my_parse_error(ER(ER_ONLY_INTEGERS_ALLOWED)); }
+ ;
+
+dec_num:
+ DECIMAL_NUM
+ | FLOAT_NUM
+ ;
+
+choice:
+ ulong_num { $$= $1 != 0 ? HA_CHOICE_YES : HA_CHOICE_NO; }
+ | DEFAULT { $$= HA_CHOICE_UNDEF; }
;
procedure_clause:
- /* empty */
- | PROCEDURE ident /* Procedure name */
- {
- LEX *lex=Lex;
+ /* empty */
+ | PROCEDURE ident /* Procedure name */
+ {
+ LEX *lex=Lex;
if (! lex->parsing_options.allows_select_procedure)
{
@@ -7309,265 +9057,272 @@ procedure_clause:
MYSQL_YYABORT;
}
- if (&lex->select_lex != lex->current_select)
- {
- my_error(ER_WRONG_USAGE, MYF(0), "PROCEDURE", "subquery");
- MYSQL_YYABORT;
- }
- lex->proc_list.elements=0;
- lex->proc_list.first=0;
- lex->proc_list.next= (byte**) &lex->proc_list.first;
- Item_field *item= new Item_field(&lex->current_select->context,
- NULL,NULL,$2.str);
+ if (&lex->select_lex != lex->current_select)
+ {
+ my_error(ER_WRONG_USAGE, MYF(0), "PROCEDURE", "subquery");
+ MYSQL_YYABORT;
+ }
+ lex->proc_list.elements=0;
+ lex->proc_list.first=0;
+ lex->proc_list.next= (uchar**) &lex->proc_list.first;
+ Item_field *item= new (YYTHD->mem_root)
+ Item_field(&lex->current_select->context,
+ NULL, NULL, $2.str);
if (item == NULL)
MYSQL_YYABORT;
- if (add_proc_to_list(lex->thd, item))
- MYSQL_YYABORT;
- Lex->uncacheable(UNCACHEABLE_SIDEEFFECT);
- }
- '(' procedure_list ')';
-
+ if (add_proc_to_list(lex->thd, item))
+ MYSQL_YYABORT;
+ Lex->uncacheable(UNCACHEABLE_SIDEEFFECT);
+ }
+ '(' procedure_list ')'
+ ;
procedure_list:
- /* empty */ {}
- | procedure_list2 {};
+ /* empty */ {}
+ | procedure_list2 {}
+ ;
procedure_list2:
- procedure_list2 ',' procedure_item
- | procedure_item;
+ procedure_list2 ',' procedure_item
+ | procedure_item
+ ;
procedure_item:
- remember_name expr
- {
+ remember_name expr remember_end
+ {
THD *thd= YYTHD;
- Lex_input_stream *lip= YYLIP;
-
- if (add_proc_to_list(thd, $2))
- MYSQL_YYABORT;
- if (!$2->name)
- $2->set_name($1,(uint) ((char*) lip->tok_end - $1),
- thd->charset());
- }
- ;
+ if (add_proc_to_list(thd, $2))
+ MYSQL_YYABORT;
+ if (!$2->name)
+ $2->set_name($1, (uint) ($3 - $1), thd->charset());
+ }
+ ;
select_var_list_init:
- {
- LEX *lex=Lex;
- if (!lex->describe &&
- (!(lex->result= new select_dumpvar(lex->nest_level))))
- MYSQL_YYABORT;
- }
- select_var_list
- {}
- ;
+ {
+ LEX *lex=Lex;
+ if (!lex->describe &&
+ (!(lex->result= new select_dumpvar(lex->nest_level))))
+ MYSQL_YYABORT;
+ }
+ select_var_list
+ {}
+ ;
select_var_list:
- select_var_list ',' select_var_ident
- | select_var_ident {}
- ;
+ select_var_list ',' select_var_ident
+ | select_var_ident {}
+ ;
select_var_ident:
- '@' ident_or_text
- {
- LEX *lex=Lex;
- if (lex->result)
- {
- my_var *var= new my_var($2,0,0,(enum_field_types)0);
- if (var == NULL)
- MYSQL_YYABORT;
- ((select_dumpvar *)lex->result)->var_list.push_back(var);
- }
- else
- {
- /*
- The parser won't create select_result instance only
- if it's an EXPLAIN.
- */
- DBUG_ASSERT(lex->describe);
- }
- }
- | ident_or_text
- {
- LEX *lex=Lex;
- sp_variable_t *t;
-
- if (!lex->spcont || !(t=lex->spcont->find_variable(&$1)))
- {
- my_error(ER_SP_UNDECLARED_VAR, MYF(0), $1.str);
- MYSQL_YYABORT;
- }
- if (lex->result)
- {
- my_var *var= new my_var($1,1,t->offset,t->type);
- if (var == NULL)
- MYSQL_YYABORT;
- ((select_dumpvar *)lex->result)->var_list.push_back(var);
+ '@' ident_or_text
+ {
+ LEX *lex=Lex;
+ if (lex->result)
+ {
+ my_var *var= new my_var($2,0,0,(enum_field_types)0);
+ if (var == NULL)
+ MYSQL_YYABORT;
+ ((select_dumpvar *)lex->result)->var_list.push_back(var);
+ }
+ else
+ {
+ /*
+ The parser won't create select_result instance only
+ if it's an EXPLAIN.
+ */
+ DBUG_ASSERT(lex->describe);
+ }
+ }
+ | ident_or_text
+ {
+ LEX *lex=Lex;
+ sp_variable_t *t;
+
+ if (!lex->spcont || !(t=lex->spcont->find_variable(&$1)))
+ {
+ my_error(ER_SP_UNDECLARED_VAR, MYF(0), $1.str);
+ MYSQL_YYABORT;
+ }
+ if (lex->result)
+ {
+ my_var *var= new my_var($1,1,t->offset,t->type);
+ if (var == NULL)
+ MYSQL_YYABORT;
+ ((select_dumpvar *)lex->result)->var_list.push_back(var);
#ifndef DBUG_OFF
- var->sp= lex->sphead;
+ var->sp= lex->sphead;
#endif
- }
- else
- {
- /*
- The parser won't create select_result instance only
- if it's an EXPLAIN.
- */
- DBUG_ASSERT(lex->describe);
- }
- }
- ;
+ }
+ else
+ {
+ /*
+ The parser won't create select_result instance only
+ if it's an EXPLAIN.
+ */
+ DBUG_ASSERT(lex->describe);
+ }
+ }
+ ;
into:
- INTO
- {
- if (! Lex->parsing_options.allows_select_into)
+ INTO
{
- my_error(ER_VIEW_SELECT_CLAUSE, MYF(0), "INTO");
- MYSQL_YYABORT;
+ if (! Lex->parsing_options.allows_select_into)
+ {
+ my_error(ER_VIEW_SELECT_CLAUSE, MYF(0), "INTO");
+ MYSQL_YYABORT;
+ }
}
- }
- into_destination
+ into_destination
;
into_destination:
- OUTFILE TEXT_STRING_filesystem
- {
- 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)))
- MYSQL_YYABORT;
- }
- opt_field_term opt_line_term
- | DUMPFILE TEXT_STRING_filesystem
- {
- LEX *lex=Lex;
- if (!lex->describe)
- {
- 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)))
- MYSQL_YYABORT;
- }
- }
+ OUTFILE TEXT_STRING_filesystem
+ {
+ 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)))
+ MYSQL_YYABORT;
+ }
+ opt_field_term opt_line_term
+ | DUMPFILE TEXT_STRING_filesystem
+ {
+ LEX *lex=Lex;
+ if (!lex->describe)
+ {
+ 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)))
+ MYSQL_YYABORT;
+ }
+ }
| select_var_list_init
- {
- Lex->uncacheable(UNCACHEABLE_SIDEEFFECT);
- }
+ {
+ Lex->uncacheable(UNCACHEABLE_SIDEEFFECT);
+ }
;
/*
DO statement
*/
-do: DO_SYM
- {
- LEX *lex=Lex;
- lex->sql_command = SQLCOM_DO;
- mysql_init_select(lex);
- }
- expr_list
- {
- Lex->insert_list= $3;
- }
- ;
+do:
+ DO_SYM
+ {
+ LEX *lex=Lex;
+ lex->sql_command = SQLCOM_DO;
+ mysql_init_select(lex);
+ }
+ expr_list
+ {
+ Lex->insert_list= $3;
+ }
+ ;
/*
Drop : delete tables or index or user
*/
drop:
- DROP opt_temporary table_or_tables if_exists table_list opt_restrict
- {
- LEX *lex=Lex;
- lex->sql_command = SQLCOM_DROP_TABLE;
- lex->drop_temporary= $2;
- lex->drop_if_exists= $4;
- }
- | DROP INDEX_SYM ident ON table_ident {}
- {
- LEX *lex=Lex;
- Alter_drop *ad= new Alter_drop(Alter_drop::KEY, $3.str);
- if (ad == NULL)
- MYSQL_YYABORT;
- lex->sql_command= SQLCOM_DROP_INDEX;
- lex->alter_info.reset();
- lex->alter_info.flags= ALTER_DROP_INDEX;
- lex->alter_info.drop_list.push_back(ad);
- if (!lex->current_select->add_table_to_list(lex->thd, $5, NULL,
- TL_OPTION_UPDATING))
- MYSQL_YYABORT;
- }
- | DROP DATABASE if_exists ident
- {
- LEX *lex=Lex;
- lex->sql_command= SQLCOM_DROP_DB;
- lex->drop_if_exists=$3;
- lex->name=$4.str;
- }
- | DROP FUNCTION_SYM if_exists ident '.' ident
- {
+ DROP opt_temporary table_or_tables if_exists table_list opt_restrict
+ {
+ LEX *lex=Lex;
+ lex->sql_command = SQLCOM_DROP_TABLE;
+ lex->drop_temporary= $2;
+ lex->drop_if_exists= $4;
+ }
+ | DROP INDEX_SYM ident ON table_ident {}
+ {
+ LEX *lex=Lex;
+ Alter_drop *ad= new Alter_drop(Alter_drop::KEY, $3.str);
+ if (ad == NULL)
+ MYSQL_YYABORT;
+ lex->sql_command= SQLCOM_DROP_INDEX;
+ lex->alter_info.reset();
+ lex->alter_info.flags= ALTER_DROP_INDEX;
+ lex->alter_info.drop_list.push_back(ad);
+ if (!lex->current_select->add_table_to_list(lex->thd, $5, NULL,
+ TL_OPTION_UPDATING))
+ MYSQL_YYABORT;
+ }
+ | DROP DATABASE if_exists ident
+ {
+ LEX *lex=Lex;
+ lex->sql_command= SQLCOM_DROP_DB;
+ lex->drop_if_exists=$3;
+ lex->name= $4;
+ }
+ | DROP FUNCTION_SYM if_exists ident '.' ident
+ {
THD *thd= YYTHD;
LEX *lex= thd->lex;
sp_name *spname;
- if (lex->sphead)
- {
- my_error(ER_SP_NO_DROP_SP, MYF(0), "FUNCTION");
- MYSQL_YYABORT;
- }
- lex->sql_command = SQLCOM_DROP_FUNCTION;
- lex->drop_if_exists= $3;
- spname= new sp_name($4, $6, true);
+ if (lex->sphead)
+ {
+ my_error(ER_SP_NO_DROP_SP, MYF(0), "FUNCTION");
+ MYSQL_YYABORT;
+ }
+ lex->sql_command = SQLCOM_DROP_FUNCTION;
+ lex->drop_if_exists= $3;
+ spname= new sp_name($4, $6, true);
if (spname == NULL)
MYSQL_YYABORT;
- spname->init_qname(thd);
- lex->spname= spname;
- }
- | DROP FUNCTION_SYM if_exists ident
- {
+ spname->init_qname(thd);
+ lex->spname= spname;
+ }
+ | DROP FUNCTION_SYM if_exists ident
+ {
THD *thd= YYTHD;
LEX *lex= thd->lex;
LEX_STRING db= {0, 0};
sp_name *spname;
- if (lex->sphead)
- {
- my_error(ER_SP_NO_DROP_SP, MYF(0), "FUNCTION");
- MYSQL_YYABORT;
- }
+ if (lex->sphead)
+ {
+ my_error(ER_SP_NO_DROP_SP, MYF(0), "FUNCTION");
+ MYSQL_YYABORT;
+ }
if (thd->db && lex->copy_db_to(&db.str, &db.length))
MYSQL_YYABORT;
- lex->sql_command = SQLCOM_DROP_FUNCTION;
- lex->drop_if_exists= $3;
- spname= new sp_name(db, $4, false);
+ lex->sql_command = SQLCOM_DROP_FUNCTION;
+ lex->drop_if_exists= $3;
+ spname= new sp_name(db, $4, false);
if (spname == NULL)
MYSQL_YYABORT;
- spname->init_qname(thd);
- lex->spname= spname;
- }
- | DROP PROCEDURE if_exists sp_name
- {
- LEX *lex=Lex;
- if (lex->sphead)
- {
- my_error(ER_SP_NO_DROP_SP, MYF(0), "PROCEDURE");
- MYSQL_YYABORT;
- }
- lex->sql_command = SQLCOM_DROP_PROCEDURE;
- lex->drop_if_exists= $3;
- lex->spname= $4;
- }
- | DROP USER clear_privileges user_list
- {
- Lex->sql_command = SQLCOM_DROP_USER;
- }
- | DROP VIEW_SYM if_exists table_list opt_restrict
- {
- LEX *lex= Lex;
- lex->sql_command= SQLCOM_DROP_VIEW;
- lex->drop_if_exists= $3;
- }
+ spname->init_qname(thd);
+ lex->spname= spname;
+ }
+ | DROP PROCEDURE if_exists sp_name
+ {
+ LEX *lex=Lex;
+ if (lex->sphead)
+ {
+ my_error(ER_SP_NO_DROP_SP, MYF(0), "PROCEDURE");
+ MYSQL_YYABORT;
+ }
+ lex->sql_command = SQLCOM_DROP_PROCEDURE;
+ lex->drop_if_exists= $3;
+ lex->spname= $4;
+ }
+ | DROP USER clear_privileges user_list
+ {
+ Lex->sql_command = SQLCOM_DROP_USER;
+ }
+ | DROP VIEW_SYM if_exists table_list opt_restrict
+ {
+ LEX *lex= Lex;
+ lex->sql_command= SQLCOM_DROP_VIEW;
+ lex->drop_if_exists= $3;
+ }
+ | DROP EVENT_SYM if_exists sp_name
+ {
+ Lex->drop_if_exists= $3;
+ Lex->spname= $4;
+ Lex->sql_command = SQLCOM_DROP_EVENT;
+ }
| DROP TRIGGER_SYM if_exists sp_name
{
LEX *lex= Lex;
@@ -7575,71 +9330,105 @@ drop:
lex->drop_if_exists= $3;
lex->spname= $4;
}
- ;
+ | DROP TABLESPACE tablespace_name opt_ts_engine opt_ts_wait
+ {
+ LEX *lex= Lex;
+ lex->alter_tablespace_info->ts_cmd_type= DROP_TABLESPACE;
+ }
+ | DROP LOGFILE_SYM GROUP_SYM logfile_group_name opt_ts_engine opt_ts_wait
+ {
+ LEX *lex= Lex;
+ lex->alter_tablespace_info->ts_cmd_type= DROP_LOGFILE_GROUP;
+ }
+ | DROP SERVER_SYM if_exists ident_or_text
+ {
+ Lex->sql_command = SQLCOM_DROP_SERVER;
+ Lex->drop_if_exists= $3;
+ Lex->server_options.server_name= $4.str;
+ Lex->server_options.server_name_length= $4.length;
+ }
+ ;
table_list:
- table_name
- | table_list ',' table_name;
+ table_name
+ | table_list ',' table_name
+ ;
table_name:
- table_ident
- {
- if (!Select->add_table_to_list(YYTHD, $1, NULL, TL_OPTION_UPDATING))
- MYSQL_YYABORT;
- }
- ;
+ table_ident
+ {
+ if (!Select->add_table_to_list(YYTHD, $1, NULL, TL_OPTION_UPDATING))
+ MYSQL_YYABORT;
+ }
+ ;
+
+table_alias_ref_list:
+ table_alias_ref
+ | table_alias_ref_list ',' table_alias_ref
+ ;
+
+table_alias_ref:
+ table_ident
+ {
+ if (!Select->add_table_to_list(YYTHD, $1, NULL,
+ TL_OPTION_UPDATING | TL_OPTION_ALIAS,
+ Lex->lock_option ))
+ MYSQL_YYABORT;
+ }
+ ;
if_exists:
- /* empty */ { $$= 0; }
- | IF EXISTS { $$= 1; }
- ;
+ /* empty */ { $$= 0; }
+ | IF EXISTS { $$= 1; }
+ ;
opt_temporary:
- /* empty */ { $$= 0; }
- | TEMPORARY { $$= 1; }
- ;
+ /* empty */ { $$= 0; }
+ | TEMPORARY { $$= 1; }
+ ;
/*
** Insert : add new data to table
*/
insert:
- INSERT
- {
- LEX *lex= Lex;
- lex->sql_command= SQLCOM_INSERT;
- lex->duplicates= DUP_ERROR;
- mysql_init_select(lex);
- /* for subselects */
- lex->lock_option= (using_update_log) ? TL_READ_NO_INSERT : TL_READ;
- } insert_lock_option
- opt_ignore insert2
- {
- Select->set_lock_for_tables($3);
- Lex->current_select= &Lex->select_lex;
- }
- insert_field_spec opt_insert_update
- {}
- ;
+ INSERT
+ {
+ LEX *lex= Lex;
+ 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
+ {
+ Select->set_lock_for_tables($3);
+ Lex->current_select= &Lex->select_lex;
+ }
+ insert_field_spec opt_insert_update
+ {}
+ ;
replace:
- REPLACE
- {
- LEX *lex=Lex;
- lex->sql_command = SQLCOM_REPLACE;
- lex->duplicates= DUP_REPLACE;
- mysql_init_select(lex);
- }
- replace_lock_option insert2
- {
- Select->set_lock_for_tables($3);
- Lex->current_select= &Lex->select_lex;
- }
- insert_field_spec
- {}
- ;
+ REPLACE
+ {
+ LEX *lex=Lex;
+ lex->sql_command = SQLCOM_REPLACE;
+ lex->duplicates= DUP_REPLACE;
+ mysql_init_select(lex);
+ }
+ replace_lock_option insert2
+ {
+ Select->set_lock_for_tables($3);
+ Lex->current_select= &Lex->select_lex;
+ }
+ insert_field_spec
+ {}
+ ;
insert_lock_option:
- /* empty */
+ /* empty */
{
#ifdef HAVE_QUERY_CACHE
/*
@@ -7652,315 +9441,395 @@ insert_lock_option:
$$= TL_WRITE_CONCURRENT_INSERT;
#endif
}
- | LOW_PRIORITY { $$= TL_WRITE_LOW_PRIORITY; }
- | DELAYED_SYM { $$= TL_WRITE_DELAYED; }
- | HIGH_PRIORITY { $$= TL_WRITE; }
- ;
+ | LOW_PRIORITY { $$= TL_WRITE_LOW_PRIORITY; }
+ | DELAYED_SYM { $$= TL_WRITE_DELAYED; }
+ | HIGH_PRIORITY { $$= TL_WRITE; }
+ ;
replace_lock_option:
- opt_low_priority { $$= $1; }
- | DELAYED_SYM { $$= TL_WRITE_DELAYED; };
+ opt_low_priority { $$= $1; }
+ | DELAYED_SYM { $$= TL_WRITE_DELAYED; }
+ ;
insert2:
- INTO insert_table {}
- | insert_table {};
+ INTO insert_table {}
+ | insert_table {}
+ ;
insert_table:
- table_name
- {
- LEX *lex=Lex;
- lex->field_list.empty();
- lex->many_values.empty();
- lex->insert_list=0;
- };
+ table_name
+ {
+ LEX *lex=Lex;
+ lex->field_list.empty();
+ lex->many_values.empty();
+ lex->insert_list=0;
+ };
insert_field_spec:
- insert_values {}
- | '(' ')' insert_values {}
- | '(' fields ')' insert_values {}
- | SET
- {
- LEX *lex=Lex;
- if (!(lex->insert_list = new List_item) ||
- lex->many_values.push_back(lex->insert_list))
- MYSQL_YYABORT;
- }
- ident_eq_list;
+ insert_values {}
+ | '(' ')' insert_values {}
+ | '(' fields ')' insert_values {}
+ | SET
+ {
+ LEX *lex=Lex;
+ if (!(lex->insert_list = new List_item) ||
+ lex->many_values.push_back(lex->insert_list))
+ MYSQL_YYABORT;
+ }
+ ident_eq_list
+ ;
fields:
- fields ',' insert_ident { Lex->field_list.push_back($3); }
- | insert_ident { Lex->field_list.push_back($1); };
+ fields ',' insert_ident { Lex->field_list.push_back($3); }
+ | insert_ident { Lex->field_list.push_back($1); }
+ ;
insert_values:
- VALUES values_list {}
- | VALUE_SYM values_list {}
- | create_select { Select->set_braces(0);} union_clause {}
- | '(' create_select ')' { Select->set_braces(1);} union_opt {}
+ VALUES values_list {}
+ | VALUE_SYM values_list {}
+ | create_select
+ { Select->set_braces(0);}
+ union_clause {}
+ | '(' create_select ')'
+ { Select->set_braces(1);}
+ union_opt {}
;
values_list:
- values_list ',' no_braces
- | no_braces;
+ values_list ',' no_braces
+ | no_braces
+ ;
ident_eq_list:
- ident_eq_list ',' ident_eq_value
- |
- ident_eq_value;
+ ident_eq_list ',' ident_eq_value
+ | ident_eq_value
+ ;
ident_eq_value:
- simple_ident_nospvar equal expr_or_default
- {
- LEX *lex=Lex;
- if (lex->field_list.push_back($1) ||
- lex->insert_list->push_back($3))
- MYSQL_YYABORT;
- };
-
-equal: EQ {}
- | SET_VAR {}
- ;
+ simple_ident_nospvar equal expr_or_default
+ {
+ LEX *lex=Lex;
+ if (lex->field_list.push_back($1) ||
+ lex->insert_list->push_back($3))
+ MYSQL_YYABORT;
+ }
+ ;
+
+equal:
+ EQ {}
+ | SET_VAR {}
+ ;
opt_equal:
- /* empty */ {}
- | equal {}
- ;
+ /* empty */ {}
+ | equal {}
+ ;
no_braces:
- '('
- {
- if (!(Lex->insert_list = new List_item))
- MYSQL_YYABORT;
- }
- opt_values ')'
- {
- LEX *lex=Lex;
- if (lex->many_values.push_back(lex->insert_list))
- MYSQL_YYABORT;
- };
+ '('
+ {
+ if (!(Lex->insert_list = new List_item))
+ MYSQL_YYABORT;
+ }
+ opt_values ')'
+ {
+ LEX *lex=Lex;
+ if (lex->many_values.push_back(lex->insert_list))
+ MYSQL_YYABORT;
+ }
+ ;
opt_values:
- /* empty */ {}
- | values;
+ /* empty */ {}
+ | values
+ ;
values:
- values ',' expr_or_default
- {
- if (Lex->insert_list->push_back($3))
- MYSQL_YYABORT;
- }
- | expr_or_default
- {
- if (Lex->insert_list->push_back($1))
- MYSQL_YYABORT;
- }
- ;
+ values ',' expr_or_default
+ {
+ if (Lex->insert_list->push_back($3))
+ MYSQL_YYABORT;
+ }
+ | expr_or_default
+ {
+ if (Lex->insert_list->push_back($1))
+ MYSQL_YYABORT;
+ }
+ ;
expr_or_default:
- expr { $$= $1;}
- | DEFAULT
+ expr { $$= $1;}
+ | DEFAULT
{
- $$= new Item_default_value(Lex->current_context());
+ $$= new (YYTHD->mem_root) Item_default_value(Lex->current_context());
if ($$ == NULL)
MYSQL_YYABORT;
}
- ;
+ ;
opt_insert_update:
- /* empty */
- | ON DUPLICATE_SYM { Lex->duplicates= DUP_UPDATE; }
+ /* empty */
+ | ON DUPLICATE_SYM { Lex->duplicates= DUP_UPDATE; }
KEY_SYM UPDATE_SYM insert_update_list
;
/* Update rows in a table */
update:
- UPDATE_SYM
- {
- 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
- SET update_list
- {
- LEX *lex= Lex;
- if (lex->select_lex.table_list.elements > 1)
- lex->sql_command= SQLCOM_UPDATE_MULTI;
- else if (lex->select_lex.get_table_list()->derived)
- {
- /* it is single table update and it is update of derived table */
- my_error(ER_NON_UPDATABLE_TABLE, MYF(0),
- lex->select_lex.get_table_list()->alias, "UPDATE");
- MYSQL_YYABORT;
- }
- /*
- In case of multi-update setting write lock for all tables may
- be too pessimistic. We will decrease lock level if possible in
- mysql_multi_update().
- */
- Select->set_lock_for_tables($3);
- }
- where_clause opt_order_clause delete_limit_clause {}
- ;
+ UPDATE_SYM
+ {
+ 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
+ SET update_list
+ {
+ LEX *lex= Lex;
+ if (lex->select_lex.table_list.elements > 1)
+ lex->sql_command= SQLCOM_UPDATE_MULTI;
+ else if (lex->select_lex.get_table_list()->derived)
+ {
+ /* it is single table update and it is update of derived table */
+ my_error(ER_NON_UPDATABLE_TABLE, MYF(0),
+ lex->select_lex.get_table_list()->alias, "UPDATE");
+ MYSQL_YYABORT;
+ }
+ /*
+ In case of multi-update setting write lock for all tables may
+ be too pessimistic. We will decrease lock level if possible in
+ mysql_multi_update().
+ */
+ Select->set_lock_for_tables($3);
+ }
+ where_clause opt_order_clause delete_limit_clause {}
+ ;
update_list:
- update_list ',' update_elem
- | update_elem;
+ update_list ',' update_elem
+ | update_elem
+ ;
update_elem:
- simple_ident_nospvar equal expr_or_default
- {
- if (add_item_to_list(YYTHD, $1) || add_value_to_list(YYTHD, $3))
- MYSQL_YYABORT;
- };
+ simple_ident_nospvar equal expr_or_default
+ {
+ if (add_item_to_list(YYTHD, $1) || add_value_to_list(YYTHD, $3))
+ MYSQL_YYABORT;
+ }
+ ;
insert_update_list:
- insert_update_list ',' insert_update_elem
- | insert_update_elem;
+ insert_update_list ',' insert_update_elem
+ | insert_update_elem
+ ;
insert_update_elem:
- simple_ident_nospvar equal expr_or_default
- {
- LEX *lex= Lex;
- if (lex->update_list.push_back($1) ||
- lex->value_list.push_back($3))
- MYSQL_YYABORT;
- };
+ simple_ident_nospvar equal expr_or_default
+ {
+ LEX *lex= Lex;
+ if (lex->update_list.push_back($1) ||
+ lex->value_list.push_back($3))
+ MYSQL_YYABORT;
+ }
+ ;
opt_low_priority:
- /* empty */ { $$= TL_WRITE_DEFAULT; }
- | LOW_PRIORITY { $$= TL_WRITE_LOW_PRIORITY; };
+ /* empty */ { $$= TL_WRITE_DEFAULT; }
+ | LOW_PRIORITY { $$= TL_WRITE_LOW_PRIORITY; }
+ ;
/* Delete rows from a table */
delete:
- DELETE_SYM
- {
- LEX *lex= Lex;
- lex->sql_command= SQLCOM_DELETE;
- mysql_init_select(lex);
- lex->lock_option= TL_WRITE_DEFAULT;
- lex->ignore= 0;
- lex->select_lex.init_order();
- }
- opt_delete_options single_multi {}
- ;
+ DELETE_SYM
+ {
+ LEX *lex= Lex;
+ lex->sql_command= SQLCOM_DELETE;
+ mysql_init_select(lex);
+ lex->lock_option= TL_WRITE_DEFAULT;
+ lex->ignore= 0;
+ lex->select_lex.init_order();
+ }
+ 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))
- MYSQL_YYABORT;
- }
- where_clause opt_order_clause
- delete_limit_clause {}
- | table_wild_list
- { mysql_init_multi_delete(Lex); }
+ FROM table_ident
+ {
+ if (!Select->add_table_to_list(YYTHD, $2, NULL, TL_OPTION_UPDATING,
+ Lex->lock_option))
+ MYSQL_YYABORT;
+ }
+ where_clause opt_order_clause
+ delete_limit_clause {}
+ | table_wild_list
+ { mysql_init_multi_delete(Lex); }
FROM join_table_list where_clause
{
if (multi_delete_set_locks_and_link_aux_tables(Lex))
MYSQL_YYABORT;
}
- | FROM table_wild_list
- { mysql_init_multi_delete(Lex); }
- USING join_table_list where_clause
+ | FROM table_alias_ref_list
+ { mysql_init_multi_delete(Lex); }
+ 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
- {
- Table_ident *ti= new Table_ident($1);
- if (ti == NULL)
- MYSQL_YYABORT;
- if (!Select->add_table_to_list(YYTHD, ti, $3,
- TL_OPTION_UPDATING |
- TL_OPTION_ALIAS, Lex->lock_option))
- MYSQL_YYABORT;
- }
- | ident '.' ident opt_wild opt_table_alias
- {
+ ident opt_wild opt_table_alias
+ {
+ Table_ident *ti= new Table_ident($1);
+ if (ti == NULL)
+ MYSQL_YYABORT;
+ if (!Select->add_table_to_list(YYTHD,
+ ti,
+ $3,
+ TL_OPTION_UPDATING | TL_OPTION_ALIAS,
+ Lex->lock_option))
+ MYSQL_YYABORT;
+ }
+ | ident '.' ident opt_wild opt_table_alias
+ {
Table_ident *ti= new Table_ident(YYTHD, $1, $3, 0);
if (ti == NULL)
MYSQL_YYABORT;
- if (!Select->add_table_to_list(YYTHD,
+ if (!Select->add_table_to_list(YYTHD,
ti,
- $5,
- TL_OPTION_UPDATING |
- TL_OPTION_ALIAS,
- Lex->lock_option))
- MYSQL_YYABORT;
- }
- ;
+ $5,
+ TL_OPTION_UPDATING | TL_OPTION_ALIAS,
+ Lex->lock_option))
+ MYSQL_YYABORT;
+ }
+ ;
opt_wild:
- /* empty */ {}
- | '.' '*' {};
-
+ /* empty */ {}
+ | '.' '*' {}
+ ;
opt_delete_options:
- /* empty */ {}
- | opt_delete_option opt_delete_options {};
+ /* empty */ {}
+ | opt_delete_option opt_delete_options {}
+ ;
opt_delete_option:
- QUICK { Select->options|= OPTION_QUICK; }
- | LOW_PRIORITY { Lex->lock_option= TL_WRITE_LOW_PRIORITY; }
- | IGNORE_SYM { Lex->ignore= 1; };
+ QUICK { Select->options|= OPTION_QUICK; }
+ | LOW_PRIORITY { Lex->lock_option= TL_WRITE_LOW_PRIORITY; }
+ | IGNORE_SYM { Lex->ignore= 1; }
+ ;
truncate:
- TRUNCATE_SYM opt_table_sym table_name
- {
- LEX* lex= Lex;
- lex->sql_command= SQLCOM_TRUNCATE;
- lex->select_lex.options= 0;
- lex->select_lex.sql_cache= SELECT_LEX::SQL_CACHE_UNSPECIFIED;
- lex->select_lex.init_order();
- }
- ;
+ TRUNCATE_SYM opt_table_sym table_name
+ {
+ LEX* lex= Lex;
+ lex->sql_command= SQLCOM_TRUNCATE;
+ lex->select_lex.options= 0;
+ lex->select_lex.sql_cache= SELECT_LEX::SQL_CACHE_UNSPECIFIED;
+ lex->select_lex.init_order();
+ }
+ ;
opt_table_sym:
- /* empty */
- | TABLE_SYM;
+ /* empty */
+ | TABLE_SYM
+ ;
+
+opt_profile_defs:
+ /* empty */
+ | profile_defs;
+
+profile_defs:
+ profile_def
+ | profile_defs ',' profile_def;
+
+profile_def:
+ CPU_SYM
+ {
+ Lex->profile_options|= PROFILE_CPU;
+ }
+ | MEMORY_SYM
+ {
+ Lex->profile_options|= PROFILE_MEMORY;
+ }
+ | BLOCK_SYM IO_SYM
+ {
+ Lex->profile_options|= PROFILE_BLOCK_IO;
+ }
+ | CONTEXT_SYM SWITCHES_SYM
+ {
+ Lex->profile_options|= PROFILE_CONTEXT;
+ }
+ | PAGE_SYM FAULTS_SYM
+ {
+ Lex->profile_options|= PROFILE_PAGE_FAULTS;
+ }
+ | IPC_SYM
+ {
+ Lex->profile_options|= PROFILE_IPC;
+ }
+ | SWAPS_SYM
+ {
+ Lex->profile_options|= PROFILE_SWAPS;
+ }
+ | SOURCE_SYM
+ {
+ Lex->profile_options|= PROFILE_SOURCE;
+ }
+ | ALL
+ {
+ Lex->profile_options|= PROFILE_ALL;
+ }
+ ;
+
+opt_profile_args:
+ /* empty */
+ {
+ Lex->profile_query_id= 0;
+ }
+ | FOR_SYM QUERY_SYM NUM
+ {
+ Lex->profile_query_id= atoi($3.str);
+ }
+ ;
/* Show things */
-show: SHOW
- {
- LEX *lex=Lex;
- lex->wild=0;
- lex->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
- {}
- ;
+show:
+ 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
+ {}
+ ;
show_param:
- DATABASES wild_and_where
- {
- LEX *lex= Lex;
- lex->sql_command= SQLCOM_SELECT;
- lex->orig_sql_command= SQLCOM_SHOW_DATABASES;
- if (prepare_schema_table(YYTHD, lex, 0, SCH_SCHEMATA))
- MYSQL_YYABORT;
- }
+ DATABASES wild_and_where
+ {
+ LEX *lex= Lex;
+ lex->sql_command= SQLCOM_SHOW_DATABASES;
+ if (prepare_schema_table(YYTHD, lex, 0, SCH_SCHEMATA))
+ MYSQL_YYABORT;
+ }
| opt_full TABLES opt_db wild_and_where
{
LEX *lex= Lex;
- lex->sql_command= SQLCOM_SELECT;
- lex->orig_sql_command= SQLCOM_SHOW_TABLES;
+ lex->sql_command= SQLCOM_SHOW_TABLES;
lex->select_lex.db= $3;
if (prepare_schema_table(YYTHD, lex, 0, SCH_TABLE_NAMES))
MYSQL_YYABORT;
@@ -7968,230 +9837,288 @@ show_param:
| opt_full TRIGGERS_SYM opt_db wild_and_where
{
LEX *lex= Lex;
- lex->sql_command= SQLCOM_SELECT;
- lex->orig_sql_command= SQLCOM_SHOW_TRIGGERS;
+ lex->sql_command= SQLCOM_SHOW_TRIGGERS;
lex->select_lex.db= $3;
if (prepare_schema_table(YYTHD, lex, 0, SCH_TRIGGERS))
MYSQL_YYABORT;
}
+ | EVENTS_SYM opt_db wild_and_where
+ {
+ LEX *lex= Lex;
+ lex->sql_command= SQLCOM_SHOW_EVENTS;
+ lex->select_lex.db= $2;
+ if (prepare_schema_table(YYTHD, lex, 0, SCH_EVENTS))
+ MYSQL_YYABORT;
+ }
| TABLE_SYM STATUS_SYM opt_db wild_and_where
{
LEX *lex= Lex;
- lex->sql_command= SQLCOM_SELECT;
- lex->orig_sql_command= SQLCOM_SHOW_TABLE_STATUS;
+ lex->sql_command= SQLCOM_SHOW_TABLE_STATUS;
lex->select_lex.db= $3;
if (prepare_schema_table(YYTHD, lex, 0, SCH_TABLES))
MYSQL_YYABORT;
}
| OPEN_SYM TABLES opt_db wild_and_where
- {
- LEX *lex= Lex;
- lex->sql_command= SQLCOM_SELECT;
- lex->orig_sql_command= SQLCOM_SHOW_OPEN_TABLES;
- lex->select_lex.db= $3;
+ {
+ 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;
- }
- | ENGINE_SYM storage_engines
- { Lex->create_info.db_type= $2; }
- show_engine_param
- | opt_full COLUMNS from_or_in table_ident opt_db wild_and_where
- {
- LEX *lex= Lex;
- lex->sql_command= SQLCOM_SELECT;
- lex->orig_sql_command= SQLCOM_SHOW_FIELDS;
- if ($5)
- $4->change_db($5);
- if (prepare_schema_table(YYTHD, lex, $4, SCH_COLUMNS))
- MYSQL_YYABORT;
- }
- | NEW_SYM MASTER_SYM FOR_SYM SLAVE WITH MASTER_LOG_FILE_SYM EQ
- TEXT_STRING_sys AND_SYM MASTER_LOG_POS_SYM EQ ulonglong_num
- AND_SYM MASTER_SERVER_ID_SYM EQ
- ulong_num
- {
- Lex->sql_command = SQLCOM_SHOW_NEW_MASTER;
- Lex->mi.log_file_name = $8.str;
- Lex->mi.pos = $12;
- Lex->mi.server_id = $16;
+ }
+ | opt_full PLUGIN_SYM
+ {
+ LEX *lex= Lex;
+ WARN_DEPRECATED(yythd, "5.2", "SHOW PLUGIN", "'SHOW PLUGINS'");
+ lex->sql_command= SQLCOM_SHOW_PLUGINS;
+ if (prepare_schema_table(YYTHD, lex, 0, SCH_PLUGINS))
+ MYSQL_YYABORT;
+ }
+ | PLUGINS_SYM
+ {
+ LEX *lex= Lex;
+ lex->sql_command= SQLCOM_SHOW_PLUGINS;
+ if (prepare_schema_table(YYTHD, lex, 0, SCH_PLUGINS))
+ MYSQL_YYABORT;
+ }
+ | ENGINE_SYM known_storage_engines show_engine_param
+ { Lex->create_info.db_type= $2; }
+ | ENGINE_SYM ALL show_engine_param
+ { Lex->create_info.db_type= NULL; }
+ | opt_full COLUMNS from_or_in table_ident opt_db wild_and_where
+ {
+ LEX *lex= Lex;
+ lex->sql_command= SQLCOM_SHOW_FIELDS;
+ if ($5)
+ $4->change_db($5);
+ if (prepare_schema_table(YYTHD, 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;
+ Lex->sql_command = SQLCOM_SHOW_BINLOGS;
}
| SLAVE HOSTS_SYM
{
- Lex->sql_command = SQLCOM_SHOW_SLAVE_HOSTS;
+ Lex->sql_command = SQLCOM_SHOW_SLAVE_HOSTS;
}
| BINLOG_SYM EVENTS_SYM binlog_in binlog_from
{
- LEX *lex= Lex;
- lex->sql_command= SQLCOM_SHOW_BINLOG_EVENTS;
+ LEX *lex= Lex;
+ lex->sql_command= SQLCOM_SHOW_BINLOG_EVENTS;
} opt_limit_clause_init
| keys_or_index from_or_in table_ident opt_db where_clause
{
LEX *lex= Lex;
- lex->sql_command= SQLCOM_SELECT;
- lex->orig_sql_command= SQLCOM_SHOW_KEYS;
- if ($4)
- $3->change_db($4);
+ 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("SHOW TABLE TYPES", "SHOW [STORAGE] ENGINES");
- }
- | opt_storage ENGINES_SYM
- {
- LEX *lex=Lex;
- lex->sql_command= SQLCOM_SHOW_STORAGE_ENGINES;
- }
- | PRIVILEGES
- {
- LEX *lex=Lex;
- lex->sql_command= SQLCOM_SHOW_PRIVILEGES;
- }
+ }
+ | 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, "5.2", "SHOW TABLE TYPES", "'SHOW [STORAGE] ENGINES'");
+ if (prepare_schema_table(YYTHD, lex, 0, SCH_ENGINES))
+ 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))
+ MYSQL_YYABORT;
+ }
+ | AUTHORS_SYM
+ {
+ LEX *lex=Lex;
+ lex->sql_command= SQLCOM_SHOW_AUTHORS;
+ }
+ | CONTRIBUTORS_SYM
+ {
+ LEX *lex=Lex;
+ lex->sql_command= SQLCOM_SHOW_CONTRIBUTORS;
+ }
+ | PRIVILEGES
+ {
+ LEX *lex=Lex;
+ lex->sql_command= SQLCOM_SHOW_PRIVILEGES;
+ }
| COUNT_SYM '(' '*' ')' WARNINGS
{ (void) create_select_for_variable("warning_count"); }
| COUNT_SYM '(' '*' ')' ERRORS
- { (void) create_select_for_variable("error_count"); }
+ { (void) create_select_for_variable("error_count"); }
| WARNINGS opt_limit_clause_init
{ Lex->sql_command = SQLCOM_SHOW_WARNS;}
| ERRORS opt_limit_clause_init
{ Lex->sql_command = SQLCOM_SHOW_ERRORS;}
+ | PROFILES_SYM
+ { Lex->sql_command = SQLCOM_SHOW_PROFILES; }
+ | PROFILE_SYM opt_profile_defs opt_profile_args opt_limit_clause_init
+ {
+ LEX *lex= Lex;
+ lex->sql_command= SQLCOM_SHOW_PROFILE;
+ if (prepare_schema_table(YYTHD, lex, NULL, SCH_PROFILES) != 0)
+ YYABORT;
+ }
| opt_var_type STATUS_SYM wild_and_where
{
LEX *lex= Lex;
- lex->sql_command= SQLCOM_SELECT;
- lex->orig_sql_command= SQLCOM_SHOW_STATUS;
+ lex->sql_command= SQLCOM_SHOW_STATUS;
lex->option_type= $1;
if (prepare_schema_table(YYTHD, lex, 0, SCH_STATUS))
MYSQL_YYABORT;
- }
+ }
| INNOBASE_SYM STATUS_SYM
- { Lex->sql_command = SQLCOM_SHOW_INNODB_STATUS; WARN_DEPRECATED("SHOW INNODB STATUS", "SHOW ENGINE INNODB STATUS"); }
+ {
+ 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, "5.2", "SHOW INNODB STATUS", "'SHOW ENGINE INNODB STATUS'");
+ }
| MUTEX_SYM STATUS_SYM
- { Lex->sql_command = SQLCOM_SHOW_MUTEX_STATUS; }
- | opt_full PROCESSLIST_SYM
- { Lex->sql_command= SQLCOM_SHOW_PROCESSLIST;}
+ {
+ 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, "5.2", "SHOW MUTEX STATUS", "'SHOW ENGINE INNODB MUTEX'");
+ }
+ | opt_full PROCESSLIST_SYM
+ { Lex->sql_command= SQLCOM_SHOW_PROCESSLIST;}
| opt_var_type VARIABLES wild_and_where
- {
+ {
LEX *lex= Lex;
- lex->sql_command= SQLCOM_SELECT;
- lex->orig_sql_command= SQLCOM_SHOW_VARIABLES;
+ lex->sql_command= SQLCOM_SHOW_VARIABLES;
lex->option_type= $1;
if (prepare_schema_table(YYTHD, lex, 0, SCH_VARIABLES))
MYSQL_YYABORT;
- }
+ }
| charset wild_and_where
{
LEX *lex= Lex;
- lex->sql_command= SQLCOM_SELECT;
- lex->orig_sql_command= SQLCOM_SHOW_CHARSETS;
+ lex->sql_command= SQLCOM_SHOW_CHARSETS;
if (prepare_schema_table(YYTHD, lex, 0, SCH_CHARSETS))
MYSQL_YYABORT;
}
| COLLATION_SYM wild_and_where
{
LEX *lex= Lex;
- lex->sql_command= SQLCOM_SELECT;
- lex->orig_sql_command= SQLCOM_SHOW_COLLATIONS;
+ lex->sql_command= SQLCOM_SHOW_COLLATIONS;
if (prepare_schema_table(YYTHD, lex, 0, SCH_COLLATIONS))
MYSQL_YYABORT;
}
- | BERKELEY_DB_SYM LOGS_SYM
- { Lex->sql_command= SQLCOM_SHOW_LOGS; WARN_DEPRECATED("SHOW BDB LOGS", "SHOW ENGINE BDB LOGS"); }
- | LOGS_SYM
- { Lex->sql_command= SQLCOM_SHOW_LOGS; WARN_DEPRECATED("SHOW LOGS", "SHOW ENGINE BDB LOGS"); }
- | GRANTS
- {
- LEX *lex=Lex;
- lex->sql_command= SQLCOM_SHOW_GRANTS;
- LEX_USER *curr_user;
+ | GRANTS
+ {
+ LEX *lex=Lex;
+ lex->sql_command= SQLCOM_SHOW_GRANTS;
+ LEX_USER *curr_user;
if (!(curr_user= (LEX_USER*) lex->thd->alloc(sizeof(st_lex_user))))
MYSQL_YYABORT;
bzero(curr_user, sizeof(st_lex_user));
- lex->grant_user= curr_user;
- }
- | GRANTS FOR_SYM user
- {
- LEX *lex=Lex;
- lex->sql_command= SQLCOM_SHOW_GRANTS;
- lex->grant_user=$3;
- lex->grant_user->password=null_lex_str;
- }
- | CREATE DATABASE opt_if_not_exists ident
- {
- Lex->sql_command=SQLCOM_SHOW_CREATE_DB;
- Lex->create_info.options=$3;
- Lex->name=$4.str;
- }
+ lex->grant_user= curr_user;
+ }
+ | GRANTS FOR_SYM user
+ {
+ LEX *lex=Lex;
+ lex->sql_command= SQLCOM_SHOW_GRANTS;
+ lex->grant_user=$3;
+ lex->grant_user->password=null_lex_str;
+ }
+ | CREATE DATABASE opt_if_not_exists ident
+ {
+ Lex->sql_command=SQLCOM_SHOW_CREATE_DB;
+ Lex->create_info.options=$3;
+ Lex->name= $4;
+ }
| CREATE TABLE_SYM table_ident
{
LEX *lex= Lex;
- lex->sql_command = SQLCOM_SHOW_CREATE;
- if (!lex->select_lex.add_table_to_list(YYTHD, $3, NULL,0))
- MYSQL_YYABORT;
+ lex->sql_command = SQLCOM_SHOW_CREATE;
+ if (!lex->select_lex.add_table_to_list(YYTHD, $3, NULL,0))
+ MYSQL_YYABORT;
lex->only_view= 0;
- }
+ lex->create_info.storage_media= HA_SM_DEFAULT;
+ }
| CREATE VIEW_SYM table_ident
{
LEX *lex= Lex;
- lex->sql_command = SQLCOM_SHOW_CREATE;
- if (!lex->select_lex.add_table_to_list(YYTHD, $3, NULL, 0))
- MYSQL_YYABORT;
+ lex->sql_command = SQLCOM_SHOW_CREATE;
+ if (!lex->select_lex.add_table_to_list(YYTHD, $3, NULL, 0))
+ MYSQL_YYABORT;
lex->only_view= 1;
- }
+ }
| MASTER_SYM STATUS_SYM
{
- Lex->sql_command = SQLCOM_SHOW_MASTER_STAT;
+ Lex->sql_command = SQLCOM_SHOW_MASTER_STAT;
}
| SLAVE STATUS_SYM
{
- Lex->sql_command = SQLCOM_SHOW_SLAVE_STAT;
+ Lex->sql_command = SQLCOM_SHOW_SLAVE_STAT;
}
- | CREATE PROCEDURE sp_name
- {
- LEX *lex= Lex;
+ | CREATE PROCEDURE sp_name
+ {
+ LEX *lex= Lex;
- lex->sql_command = SQLCOM_SHOW_CREATE_PROC;
- lex->spname= $3;
- }
- | CREATE FUNCTION_SYM sp_name
- {
- LEX *lex= Lex;
+ lex->sql_command = SQLCOM_SHOW_CREATE_PROC;
+ lex->spname= $3;
+ }
+ | CREATE FUNCTION_SYM sp_name
+ {
+ LEX *lex= Lex;
- lex->sql_command = SQLCOM_SHOW_CREATE_FUNC;
- lex->spname= $3;
- }
- | PROCEDURE STATUS_SYM wild_and_where
- {
+ lex->sql_command = SQLCOM_SHOW_CREATE_FUNC;
+ lex->spname= $3;
+ }
+ | CREATE TRIGGER_SYM sp_name
+ {
LEX *lex= Lex;
- lex->sql_command= SQLCOM_SELECT;
- lex->orig_sql_command= SQLCOM_SHOW_STATUS_PROC;
- if (!sp_add_to_query_tables(YYTHD, lex, "mysql", "proc", TL_READ))
- MYSQL_YYABORT;
+ lex->sql_command= SQLCOM_SHOW_CREATE_TRIGGER;
+ lex->spname= $3;
+ }
+ | PROCEDURE STATUS_SYM wild_and_where
+ {
+ LEX *lex= Lex;
+ lex->sql_command= SQLCOM_SHOW_STATUS_PROC;
if (prepare_schema_table(YYTHD, lex, 0, SCH_PROCEDURES))
MYSQL_YYABORT;
- }
- | FUNCTION_SYM STATUS_SYM wild_and_where
- {
+ }
+ | FUNCTION_SYM STATUS_SYM wild_and_where
+ {
LEX *lex= Lex;
- lex->sql_command= SQLCOM_SELECT;
- lex->orig_sql_command= SQLCOM_SHOW_STATUS_FUNC;
- if (!sp_add_to_query_tables(YYTHD, lex, "mysql", "proc", TL_READ))
- MYSQL_YYABORT;
+ lex->sql_command= SQLCOM_SHOW_STATUS_FUNC;
if (prepare_schema_table(YYTHD, lex, 0, SCH_PROCEDURES))
MYSQL_YYABORT;
- }
+ }
| PROCEDURE CODE_SYM sp_name
{
#ifdef DBUG_OFF
@@ -8199,7 +10126,7 @@ show_param:
MYSQL_YYABORT;
#else
Lex->sql_command= SQLCOM_SHOW_PROC_CODE;
- Lex->spname= $3;
+ Lex->spname= $3;
#endif
}
| FUNCTION_SYM CODE_SYM sp_name
@@ -8209,123 +10136,117 @@ show_param:
MYSQL_YYABORT;
#else
Lex->sql_command= SQLCOM_SHOW_FUNC_CODE;
- Lex->spname= $3;
+ Lex->spname= $3;
#endif
}
+ | CREATE EVENT_SYM sp_name
+ {
+ Lex->spname= $3;
+ Lex->sql_command = SQLCOM_SHOW_CREATE_EVENT;
+ }
;
show_engine_param:
- STATUS_SYM
- {
- switch (Lex->create_info.db_type) {
- case DB_TYPE_NDBCLUSTER:
- Lex->sql_command = SQLCOM_SHOW_NDBCLUSTER_STATUS;
- break;
- case DB_TYPE_INNODB:
- Lex->sql_command = SQLCOM_SHOW_INNODB_STATUS;
- break;
- default:
- my_error(ER_NOT_SUPPORTED_YET, MYF(0), "STATUS");
- MYSQL_YYABORT;
- }
- }
- | LOGS_SYM
- {
- switch (Lex->create_info.db_type) {
- case DB_TYPE_BERKELEY_DB:
- Lex->sql_command = SQLCOM_SHOW_LOGS;
- break;
- default:
- my_error(ER_NOT_SUPPORTED_YET, MYF(0), "LOGS");
- MYSQL_YYABORT;
- }
- };
+ STATUS_SYM
+ { Lex->sql_command= SQLCOM_SHOW_ENGINE_STATUS; }
+ | MUTEX_SYM
+ { Lex->sql_command= SQLCOM_SHOW_ENGINE_MUTEX; }
+ | LOGS_SYM
+ { Lex->sql_command= SQLCOM_SHOW_ENGINE_LOGS; }
+ ;
master_or_binary:
- MASTER_SYM
- | BINARY;
+ MASTER_SYM
+ | BINARY
+ ;
opt_storage:
- /* empty */
- | STORAGE_SYM;
+ /* empty */
+ | STORAGE_SYM
+ ;
opt_db:
- /* empty */ { $$= 0; }
- | from_or_in ident { $$= $2.str; };
+ /* empty */ { $$= 0; }
+ | from_or_in ident { $$= $2.str; }
+ ;
opt_full:
- /* empty */ { Lex->verbose=0; }
- | FULL { Lex->verbose=1; };
+ /* empty */ { Lex->verbose=0; }
+ | FULL { Lex->verbose=1; }
+ ;
from_or_in:
- FROM
- | IN_SYM;
+ FROM
+ | IN_SYM
+ ;
binlog_in:
- /* empty */ { Lex->mi.log_file_name = 0; }
- | IN_SYM TEXT_STRING_sys { Lex->mi.log_file_name = $2.str; };
+ /* empty */ { Lex->mi.log_file_name = 0; }
+ | IN_SYM TEXT_STRING_sys { Lex->mi.log_file_name = $2.str; }
+ ;
binlog_from:
- /* empty */ { Lex->mi.pos = 4; /* skip magic number */ }
- | FROM ulonglong_num { Lex->mi.pos = $2; };
+ /* empty */ { Lex->mi.pos = 4; /* skip magic number */ }
+ | FROM ulonglong_num { Lex->mi.pos = $2; }
+ ;
wild_and_where:
- /* empty */
- | LIKE TEXT_STRING_sys
- {
- Lex->wild= new (YYTHD->mem_root) String($2.str, $2.length,
- system_charset_info);
- if (Lex->wild == NULL)
- MYSQL_YYABORT;
- }
- | WHERE expr
- {
- Select->where= $2;
- if ($2)
- $2->top_level_item();
- }
- ;
-
+ /* empty */
+ | LIKE TEXT_STRING_sys
+ {
+ Lex->wild= new (YYTHD->mem_root) String($2.str, $2.length,
+ system_charset_info);
+ if (Lex->wild == NULL)
+ MYSQL_YYABORT;
+ }
+ | WHERE expr
+ {
+ Select->where= $2;
+ if ($2)
+ $2->top_level_item();
+ }
+ ;
/* A Oracle compatible synonym for show */
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_SELECT;
- lex->orig_sql_command= SQLCOM_SHOW_FIELDS;
- lex->select_lex.db= 0;
- lex->verbose= 0;
- if (prepare_schema_table(YYTHD, lex, $2, SCH_COLUMNS))
- MYSQL_YYABORT;
- }
- opt_describe_column {}
- | describe_command opt_extended_describe
- { Lex->describe|= DESCRIBE_NORMAL; }
- select
- {
- LEX *lex=Lex;
- lex->select_lex.options|= SELECT_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))
+ MYSQL_YYABORT;
+ }
+ opt_describe_column {}
+ | describe_command opt_extended_describe
+ { Lex->describe|= DESCRIBE_NORMAL; }
+ select
+ {
+ LEX *lex=Lex;
+ lex->select_lex.options|= SELECT_DESCRIBE;
+ }
+ ;
describe_command:
- DESC
- | DESCRIBE;
+ DESC
+ | DESCRIBE
+ ;
opt_extended_describe:
- /* empty */ {}
- | EXTENDED_SYM { Lex->describe|= DESCRIBE_EXTENDED; }
- ;
+ /* empty */ {}
+ | EXTENDED_SYM { Lex->describe|= DESCRIBE_EXTENDED; }
+ | PARTITIONS_SYM { Lex->describe|= DESCRIBE_PARTITIONS; }
+ ;
opt_describe_column:
- /* empty */ {}
- | text_string { Lex->wild= $1; }
- | ident
- {
+ /* empty */ {}
+ | text_string { Lex->wild= $1; }
+ | ident
+ {
Lex->wild= new (YYTHD->mem_root) String((const char*) $1.str,
$1.length,
system_charset_info);
@@ -8338,190 +10259,214 @@ opt_describe_column:
/* flush things */
flush:
- FLUSH_SYM opt_no_write_to_binlog
- {
- LEX *lex=Lex;
- lex->sql_command= SQLCOM_FLUSH;
- lex->type= 0;
- lex->no_write_to_binlog= $2;
- }
- flush_options
- {}
- ;
+ FLUSH_SYM opt_no_write_to_binlog
+ {
+ LEX *lex=Lex;
+ lex->sql_command= SQLCOM_FLUSH;
+ lex->type= 0;
+ lex->no_write_to_binlog= $2;
+ }
+ flush_options
+ {}
+ ;
flush_options:
- flush_options ',' flush_option
- | flush_option;
+ flush_options ',' flush_option
+ | flush_option
+ ;
flush_option:
- table_or_tables { Lex->type|= REFRESH_TABLES; } opt_table_list {}
- | TABLES WITH READ_SYM LOCK_SYM { Lex->type|= REFRESH_TABLES | REFRESH_READ_LOCK; }
- | QUERY_SYM CACHE_SYM { Lex->type|= REFRESH_QUERY_CACHE_FREE; }
- | HOSTS_SYM { Lex->type|= REFRESH_HOSTS; }
- | PRIVILEGES { Lex->type|= REFRESH_GRANT; }
- | LOGS_SYM { Lex->type|= REFRESH_LOG; }
- | STATUS_SYM { Lex->type|= REFRESH_STATUS; }
- | SLAVE { Lex->type|= REFRESH_SLAVE; }
- | MASTER_SYM { Lex->type|= REFRESH_MASTER; }
- | DES_KEY_FILE { Lex->type|= REFRESH_DES_KEY_FILE; }
- | RESOURCES { Lex->type|= REFRESH_USER_RESOURCES; };
+ table_or_tables
+ { Lex->type|= REFRESH_TABLES; }
+ opt_table_list {}
+ | TABLES WITH READ_SYM LOCK_SYM
+ { Lex->type|= REFRESH_TABLES | REFRESH_READ_LOCK; }
+ | QUERY_SYM CACHE_SYM
+ { Lex->type|= REFRESH_QUERY_CACHE_FREE; }
+ | HOSTS_SYM
+ { Lex->type|= REFRESH_HOSTS; }
+ | PRIVILEGES
+ { Lex->type|= REFRESH_GRANT; }
+ | LOGS_SYM
+ { Lex->type|= REFRESH_LOG; }
+ | STATUS_SYM
+ { Lex->type|= REFRESH_STATUS; }
+ | SLAVE
+ { Lex->type|= REFRESH_SLAVE; }
+ | MASTER_SYM
+ { Lex->type|= REFRESH_MASTER; }
+ | DES_KEY_FILE
+ { Lex->type|= REFRESH_DES_KEY_FILE; }
+ | RESOURCES
+ { Lex->type|= REFRESH_USER_RESOURCES; }
+ ;
opt_table_list:
- /* empty */ {;}
- | table_list {;};
+ /* empty */ {}
+ | table_list {}
+ ;
reset:
- RESET_SYM
- {
- LEX *lex=Lex;
- lex->sql_command= SQLCOM_RESET; lex->type=0;
- } reset_options
- {}
- ;
+ RESET_SYM
+ {
+ LEX *lex=Lex;
+ lex->sql_command= SQLCOM_RESET; lex->type=0;
+ }
+ reset_options
+ {}
+ ;
reset_options:
- reset_options ',' reset_option
- | reset_option;
+ reset_options ',' reset_option
+ | reset_option
+ ;
reset_option:
- SLAVE { Lex->type|= REFRESH_SLAVE; }
+ SLAVE { Lex->type|= REFRESH_SLAVE; }
| MASTER_SYM { Lex->type|= REFRESH_MASTER; }
- | QUERY_SYM CACHE_SYM { Lex->type|= REFRESH_QUERY_CACHE;};
+ | QUERY_SYM CACHE_SYM { Lex->type|= REFRESH_QUERY_CACHE;}
+ ;
purge:
- PURGE
- {
- LEX *lex=Lex;
- lex->type=0;
- lex->sql_command = SQLCOM_PURGE;
- } purge_options
- {}
- ;
+ PURGE
+ {
+ LEX *lex=Lex;
+ lex->type=0;
+ lex->sql_command = SQLCOM_PURGE;
+ }
+ purge_options
+ {}
+ ;
purge_options:
- master_or_binary LOGS_SYM purge_option
- ;
+ master_or_binary LOGS_SYM purge_option
+ ;
purge_option:
- TO_SYM TEXT_STRING_sys
- {
- Lex->to_log = $2.str;
- }
- | BEFORE_SYM expr
- {
- LEX *lex= Lex;
- lex->value_list.empty();
- lex->value_list.push_front($2);
- lex->sql_command= SQLCOM_PURGE_BEFORE;
- }
- ;
+ TO_SYM TEXT_STRING_sys
+ {
+ Lex->to_log = $2.str;
+ }
+ | BEFORE_SYM expr
+ {
+ LEX *lex= Lex;
+ lex->value_list.empty();
+ lex->value_list.push_front($2);
+ lex->sql_command= SQLCOM_PURGE_BEFORE;
+ }
+ ;
/* kill threads */
kill:
- KILL_SYM { Lex->sql_command= SQLCOM_KILL; } kill_option expr
- {
- LEX *lex=Lex;
- lex->value_list.empty();
- lex->value_list.push_front($4);
- };
+ KILL_SYM kill_option expr
+ {
+ LEX *lex=Lex;
+ lex->value_list.empty();
+ lex->value_list.push_front($3);
+ lex->sql_command= SQLCOM_KILL;
+ }
+ ;
kill_option:
- /* empty */ { Lex->type= 0; }
- | CONNECTION_SYM { Lex->type= 0; }
- | QUERY_SYM { Lex->type= ONLY_KILL_QUERY; }
+ /* empty */ { Lex->type= 0; }
+ | CONNECTION_SYM { Lex->type= 0; }
+ | QUERY_SYM { Lex->type= ONLY_KILL_QUERY; }
;
/* change database */
-use: USE_SYM ident
- {
- LEX *lex=Lex;
- lex->sql_command=SQLCOM_CHANGE_DB;
- lex->select_lex.db= $2.str;
- };
+use:
+ USE_SYM ident
+ {
+ LEX *lex=Lex;
+ lex->sql_command=SQLCOM_CHANGE_DB;
+ lex->select_lex.db= $2.str;
+ }
+ ;
/* import, export of files */
-load: LOAD DATA_SYM
- {
- THD *thd= YYTHD;
- LEX *lex= thd->lex;
- Lex_input_stream *lip= YYLIP;
-
- if (lex->sphead)
- {
- my_error(ER_SP_BADSTATEMENT, MYF(0), "LOAD DATA");
- MYSQL_YYABORT;
- }
- lex->fname_start= lip->ptr;
- }
- load_data
- {}
- |
- LOAD TABLE_SYM table_ident FROM MASTER_SYM
- {
- LEX *lex=Lex;
- if (lex->sphead)
- {
- my_error(ER_SP_BADSTATEMENT, MYF(0), "LOAD TABLE");
- MYSQL_YYABORT;
- }
- lex->sql_command = SQLCOM_LOAD_MASTER_TABLE;
- WARN_DEPRECATED("LOAD TABLE FROM MASTER",
- "mysqldump or future "
- "BACKUP/RESTORE DATABASE facility");
- if (!Select->add_table_to_list(YYTHD, $3, NULL, TL_OPTION_UPDATING))
- MYSQL_YYABORT;
- };
+load:
+ LOAD DATA_SYM
+ {
+ THD *thd= YYTHD;
+ LEX *lex= thd->lex;
+ Lex_input_stream *lip= YYLIP;
+
+ if (lex->sphead)
+ {
+ my_error(ER_SP_BADSTATEMENT, MYF(0), "LOAD DATA");
+ MYSQL_YYABORT;
+ }
+ lex->fname_start= lip->get_ptr();
+ }
+ load_data
+ {}
+ | LOAD TABLE_SYM table_ident FROM MASTER_SYM
+ {
+ LEX *lex=Lex;
+ WARN_DEPRECATED(yythd, "5.2", "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->duplicates= DUP_ERROR;
- lex->ignore= 0;
- if (!(lex->exchange= new sql_exchange($4.str, 0)))
- MYSQL_YYABORT;
- }
- opt_duplicate INTO
- {
- Lex->fname_end= YYLIP->ptr;
- }
- TABLE_SYM table_ident
- {
- LEX *lex=Lex;
- if (!Select->add_table_to_list(YYTHD, $10, NULL, TL_OPTION_UPDATING,
- lex->lock_option))
- MYSQL_YYABORT;
- lex->field_list.empty();
- lex->update_list.empty();
- lex->value_list.empty();
- }
- opt_load_data_charset
- { Lex->exchange->cs= $12; }
- 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("LOAD DATA FROM MASTER",
- "mysqldump or future "
- "BACKUP/RESTORE DATABASE facility");
- };
+ 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->duplicates= DUP_ERROR;
+ lex->ignore= 0;
+ if (!(lex->exchange= new sql_exchange($4.str, 0)))
+ MYSQL_YYABORT;
+ }
+ opt_duplicate INTO
+ {
+ Lex->fname_end= YYLIP->get_ptr();
+ }
+ TABLE_SYM table_ident
+ {
+ LEX *lex=Lex;
+ if (!Select->add_table_to_list(YYTHD, $10, NULL, TL_OPTION_UPDATING,
+ lex->lock_option))
+ MYSQL_YYABORT;
+ lex->field_list.empty();
+ lex->update_list.empty();
+ lex->value_list.empty();
+ }
+ opt_load_data_charset
+ { Lex->exchange->cs= $12; }
+ 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, "5.2", "LOAD DATA FROM MASTER",
+ "mysqldump or future "
+ "BACKUP/RESTORE DATABASE facility");
+ }
+ ;
opt_local:
- /* empty */ { $$=0;}
- | LOCAL_SYM { $$=1;};
+ /* empty */ { $$=0;}
+ | LOCAL_SYM { $$=1;}
+ ;
load_data_lock:
- /* empty */ { $$= TL_WRITE_DEFAULT; }
- | CONCURRENT
+ /* empty */ { $$= TL_WRITE_DEFAULT; }
+ | CONCURRENT
{
#ifdef HAVE_QUERY_CACHE
/*
@@ -8533,35 +10478,38 @@ load_data_lock:
#endif
$$= TL_WRITE_CONCURRENT_INSERT;
}
- | LOW_PRIORITY { $$= TL_WRITE_LOW_PRIORITY; };
-
+ | LOW_PRIORITY { $$= TL_WRITE_LOW_PRIORITY; }
+ ;
opt_duplicate:
- /* empty */ { Lex->duplicates=DUP_ERROR; }
- | REPLACE { Lex->duplicates=DUP_REPLACE; }
- | IGNORE_SYM { Lex->ignore= 1; };
+ /* empty */ { Lex->duplicates=DUP_ERROR; }
+ | REPLACE { Lex->duplicates=DUP_REPLACE; }
+ | IGNORE_SYM { Lex->ignore= 1; }
+ ;
opt_field_term:
- /* empty */
- | COLUMNS field_term_list;
+ /* empty */
+ | COLUMNS field_term_list
+ ;
field_term_list:
- field_term_list field_term
- | field_term;
+ field_term_list field_term
+ | field_term
+ ;
field_term:
- TERMINATED BY text_string
+ TERMINATED BY text_string
{
DBUG_ASSERT(Lex->exchange != 0);
Lex->exchange->field_term= $3;
}
- | OPTIONALLY ENCLOSED BY text_string
- {
+ | OPTIONALLY ENCLOSED BY text_string
+ {
LEX *lex= Lex;
DBUG_ASSERT(lex->exchange != 0);
lex->exchange->enclosed= $4;
lex->exchange->opt_enclosed= 1;
- }
+ }
| ENCLOSED BY text_string
{
DBUG_ASSERT(Lex->exchange != 0);
@@ -8571,18 +10519,21 @@ field_term:
{
DBUG_ASSERT(Lex->exchange != 0);
Lex->exchange->escaped= $3;
- };
+ }
+ ;
opt_line_term:
- /* empty */
- | LINES line_term_list;
+ /* empty */
+ | LINES line_term_list
+ ;
line_term_list:
- line_term_list line_term
- | line_term;
+ line_term_list line_term
+ | line_term
+ ;
line_term:
- TERMINATED BY text_string
+ TERMINATED BY text_string
{
DBUG_ASSERT(Lex->exchange != 0);
Lex->exchange->line_term= $3;
@@ -8591,85 +10542,95 @@ line_term:
{
DBUG_ASSERT(Lex->exchange != 0);
Lex->exchange->line_start= $3;
- };
+ }
+ ;
opt_ignore_lines:
- /* empty */
+ /* empty */
| IGNORE_SYM NUM LINES
{
DBUG_ASSERT(Lex->exchange != 0);
Lex->exchange->skip_lines= atol($2.str);
- };
+ }
+ ;
opt_field_or_var_spec:
- /* empty */ { }
- | '(' fields_or_vars ')' { }
- | '(' ')' { };
+ /* empty */ {}
+ | '(' fields_or_vars ')' {}
+ | '(' ')' {}
+ ;
fields_or_vars:
- fields_or_vars ',' field_or_var
+ fields_or_vars ',' field_or_var
{ Lex->field_list.push_back($3); }
| field_or_var
{ Lex->field_list.push_back($1); }
;
field_or_var:
- simple_ident_nospvar {$$= $1;}
+ simple_ident_nospvar {$$= $1;}
| '@' ident_or_text
{
- $$= new Item_user_var_as_out_param($2);
+ $$= new (YYTHD->mem_root) Item_user_var_as_out_param($2);
if ($$ == NULL)
MYSQL_YYABORT;
}
;
opt_load_data_set_spec:
- /* empty */ { }
- | SET insert_update_list { };
-
+ /* empty */ {}
+ | SET insert_update_list {}
+ ;
/* Common definitions */
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 &&
- my_charset_is_ascii_based(cs_cli) ?
- MY_REPERTOIRE_ASCII : MY_REPERTOIRE_UNICODE30;
- if (thd->charset_is_collation_connection ||
- (repertoire == MY_REPERTOIRE_ASCII &&
- my_charset_is_ascii_based(cs_con)))
- tmp= $1;
- else
+ TEXT_STRING
{
- if (thd->convert_string(&tmp, cs_con, $1.str, $1.length, cs_cli))
+ 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 &&
+ my_charset_is_ascii_based(cs_cli) ?
+ MY_REPERTOIRE_ASCII : MY_REPERTOIRE_UNICODE30;
+ if (thd->charset_is_collation_connection ||
+ (repertoire == MY_REPERTOIRE_ASCII &&
+ my_charset_is_ascii_based(cs_con)))
+ tmp= $1;
+ else
+ {
+ if (thd->convert_string(&tmp, cs_con, $1.str, $1.length, cs_cli))
+ MYSQL_YYABORT;
+ }
+ $$= new (thd->mem_root) Item_string(tmp.str, tmp.length, cs_con,
+ DERIVATION_COERCIBLE,
+ repertoire);
+ if ($$ == NULL)
MYSQL_YYABORT;
}
- $$= new Item_string(tmp.str, tmp.length, cs_con,
- DERIVATION_COERCIBLE, repertoire);
- if ($$ == NULL)
- MYSQL_YYABORT;
- }
| NCHAR_STRING
- {
- uint repertoire= Lex->text_string_is_7bit ?
- MY_REPERTOIRE_ASCII : MY_REPERTOIRE_UNICODE30;
- DBUG_ASSERT(my_charset_is_ascii_based(national_charset_info));
- $$= new Item_string($1.str, $1.length, national_charset_info,
- DERIVATION_COERCIBLE, repertoire);
- if ($$ == NULL)
- MYSQL_YYABORT;
- }
- | UNDERSCORE_CHARSET TEXT_STRING
{
- $$= new Item_string($2.str, $2.length, Lex->underscore_charset);
+ 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,
+ national_charset_info,
+ DERIVATION_COERCIBLE,
+ repertoire);
if ($$ == NULL)
MYSQL_YYABORT;
- ((Item_string*) $$)->set_repertoire_from_value();
+ }
+ | UNDERSCORE_CHARSET TEXT_STRING
+ {
+ Item_string *str= new (YYTHD->mem_root) Item_string($2.str,
+ $2.length, $1);
+ if (str == NULL)
+ MYSQL_YYABORT;
+ str->set_repertoire_from_value();
+ str->set_cs_specified(TRUE);
+
+ $$= str;
}
| text_literal TEXT_STRING_literal
{
@@ -8690,7 +10651,7 @@ text_literal:
;
text_string:
- TEXT_STRING_literal
+ TEXT_STRING_literal
{
$$= new (YYTHD->mem_root) String($1.str,
$1.length,
@@ -8698,101 +10659,100 @@ text_string:
if ($$ == NULL)
MYSQL_YYABORT;
}
- | HEX_NUM
- {
- Item *tmp= new Item_hex_string($1.str, $1.length);
+ | HEX_NUM
+ {
+ Item *tmp= new (YYTHD->mem_root) Item_hex_string($1.str, $1.length);
if (tmp == NULL)
MYSQL_YYABORT;
- /*
- it is OK only emulate fix_fields, because we need only
+ /*
+ it is OK only emulate fix_fields, because we need only
value of constant
- */
+ */
tmp->quick_fix_field();
$$= tmp->val_str((String*) 0);
- }
+ }
| BIN_NUM
{
- Item *tmp= new Item_bin_string($1.str, $1.length);
+ Item *tmp= new (YYTHD->mem_root) Item_bin_string($1.str, $1.length);
if (tmp == NULL)
MYSQL_YYABORT;
- /*
- it is OK only emulate fix_fields, because we need only
+ /*
+ it is OK only emulate fix_fields, because we need only
value of constant
- */
+ */
tmp->quick_fix_field();
$$= tmp->val_str((String*) 0);
}
- ;
+ ;
param_marker:
- PARAM_MARKER
- {
- THD *thd= YYTHD;
- LEX *lex= thd->lex;
- Lex_input_stream *lip= YYLIP;
- Item_param *item;
- if (! lex->parsing_options.allows_variable)
+ PARAM_MARKER
{
- my_error(ER_VIEW_SELECT_VARIABLE, MYF(0));
- MYSQL_YYABORT;
- }
- item= new Item_param((uint) (lip->tok_start - thd->query));
- if (!($$= item) || lex->param_list.push_back(item))
- {
- my_message(ER_OUT_OF_RESOURCES, ER(ER_OUT_OF_RESOURCES), MYF(0));
- MYSQL_YYABORT;
+ THD *thd= YYTHD;
+ LEX *lex= thd->lex;
+ Lex_input_stream *lip= YYLIP;
+ Item_param *item;
+ if (! lex->parsing_options.allows_variable)
+ {
+ my_error(ER_VIEW_SELECT_VARIABLE, MYF(0));
+ MYSQL_YYABORT;
+ }
+ item= new (thd->mem_root) Item_param((uint) (lip->get_tok_start() - thd->query));
+ if (!($$= item) || lex->param_list.push_back(item))
+ {
+ my_message(ER_OUT_OF_RESOURCES, ER(ER_OUT_OF_RESOURCES), MYF(0));
+ MYSQL_YYABORT;
+ }
}
- }
- ;
+ ;
signed_literal:
- literal { $$ = $1; }
- | '+' NUM_literal { $$ = $2; }
- | '-' NUM_literal
- {
- $2->max_length++;
- $$= $2->neg();
- }
- ;
-
+ literal { $$ = $1; }
+ | '+' NUM_literal { $$ = $2; }
+ | '-' NUM_literal
+ {
+ $2->max_length++;
+ $$= $2->neg();
+ }
+ ;
literal:
- text_literal { $$ = $1; }
- | NUM_literal { $$ = $1; }
- | NULL_SYM
+ text_literal { $$ = $1; }
+ | NUM_literal { $$ = $1; }
+ | NULL_SYM
{
- $$ = new Item_null();
+ $$ = new (YYTHD->mem_root) Item_null();
if ($$ == NULL)
MYSQL_YYABORT;
YYLIP->next_state= MY_LEX_OPERATOR_OR_IDENT;
}
- | FALSE_SYM
+ | FALSE_SYM
{
- $$= new Item_int((char*) "FALSE",0,1);
+ $$= new (YYTHD->mem_root) Item_int((char*) "FALSE",0,1);
if ($$ == NULL)
MYSQL_YYABORT;
}
- | TRUE_SYM
+ | TRUE_SYM
{
- $$= new Item_int((char*) "TRUE",1,1);
+ $$= new (YYTHD->mem_root) Item_int((char*) "TRUE",1,1);
if ($$ == NULL)
MYSQL_YYABORT;
}
- | HEX_NUM
+ | HEX_NUM
{
- $$= new Item_hex_string($1.str, $1.length);
+ $$ = new (YYTHD->mem_root) Item_hex_string($1.str, $1.length);
if ($$ == NULL)
MYSQL_YYABORT;
}
- | BIN_NUM
+ | BIN_NUM
{
- $$= new Item_bin_string($1.str, $1.length);
+ $$= new (YYTHD->mem_root) Item_bin_string($1.str, $1.length);
if ($$ == NULL)
MYSQL_YYABORT;
}
| UNDERSCORE_CHARSET HEX_NUM
{
- Item *tmp= new Item_hex_string($2.str, $2.length);
+ Item *tmp= new (YYTHD->mem_root) Item_hex_string($2.str, $2.length);
if (tmp == NULL)
MYSQL_YYABORT;
/*
@@ -8801,77 +10761,97 @@ literal:
*/
tmp->quick_fix_field();
String *str= tmp->val_str((String*) 0);
+
Item_string *item_str;
- item_str= new Item_string(NULL, /* name will be set in select_item */
- str ? str->ptr() : "",
- str ? str->length() : 0,
- Lex->underscore_charset);
+ item_str= new (YYTHD->mem_root)
+ Item_string(NULL, /* name will be set in select_item */
+ str ? str->ptr() : "",
+ str ? str->length() : 0,
+ $1);
if (!item_str ||
!item_str->check_well_formed_result(&item_str->str_value, TRUE))
{
MYSQL_YYABORT;
}
+
item_str->set_repertoire_from_value();
+ item_str->set_cs_specified(TRUE);
+
$$= item_str;
}
- | UNDERSCORE_CHARSET BIN_NUM
+ | UNDERSCORE_CHARSET BIN_NUM
{
- Item *tmp= new Item_bin_string($2.str, $2.length);
+ Item *tmp= new (YYTHD->mem_root) Item_bin_string($2.str, $2.length);
if (tmp == NULL)
MYSQL_YYABORT;
- /*
- it is OK only emulate fix_fieds, because we need only
+ /*
+ it is OK only emulate fix_fieds, because we need only
value of constant
- */
+ */
tmp->quick_fix_field();
- String *str= tmp->val_str((String*) 0);
+ String *str= tmp->val_str((String*) 0);
+
Item_string *item_str;
- item_str= new Item_string(NULL, /* name will be set in select_item */
- str ? str->ptr() : "",
- str ? str->length() : 0,
- Lex->underscore_charset);
+ item_str= new (YYTHD->mem_root)
+ Item_string(NULL, /* name will be set in select_item */
+ str ? str->ptr() : "",
+ str ? str->length() : 0,
+ $1);
if (!item_str ||
!item_str->check_well_formed_result(&item_str->str_value, TRUE))
{
MYSQL_YYABORT;
}
+
+ item_str->set_cs_specified(TRUE);
+
$$= item_str;
}
- | DATE_SYM text_literal { $$ = $2; }
- | TIME_SYM text_literal { $$ = $2; }
- | TIMESTAMP text_literal { $$ = $2; };
+ | DATE_SYM text_literal { $$ = $2; }
+ | TIME_SYM text_literal { $$ = $2; }
+ | TIMESTAMP text_literal { $$ = $2; }
+ ;
NUM_literal:
- NUM
+ NUM
{
int error;
- $$ = new Item_int($1.str,
- (longlong) my_strtoll10($1.str, NULL, &error),
- $1.length);
+ $$= new (YYTHD->mem_root)
+ Item_int($1.str,
+ (longlong) my_strtoll10($1.str, NULL, &error),
+ $1.length);
+ if ($$ == NULL)
+ MYSQL_YYABORT;
}
- | LONG_NUM
+ | LONG_NUM
{
int error;
- $$ = new Item_int($1.str,
- (longlong) my_strtoll10($1.str, NULL, &error),
- $1.length);
+ $$= new (YYTHD->mem_root)
+ Item_int($1.str,
+ (longlong) my_strtoll10($1.str, NULL, &error),
+ $1.length);
+ if ($$ == NULL)
+ MYSQL_YYABORT;
}
- | ULONGLONG_NUM
+ | ULONGLONG_NUM
{
- $$= new Item_uint($1.str, $1.length);
+ $$= new (YYTHD->mem_root) Item_uint($1.str, $1.length);
+ if ($$ == NULL)
+ MYSQL_YYABORT;
}
| DECIMAL_NUM
- {
- $$= new Item_decimal($1.str, $1.length, YYTHD->charset());
- if (($$ == NULL) || (YYTHD->net.report_error))
+ {
+ $$= new (YYTHD->mem_root) Item_decimal($1.str, $1.length,
+ YYTHD->charset());
+ if (($$ == NULL) || (YYTHD->is_error()))
{
MYSQL_YYABORT;
}
}
- | FLOAT_NUM
+ | FLOAT_NUM
{
- $$= new Item_float($1.str, $1.length);
- if (($$ == NULL) || (YYTHD->net.report_error))
+ $$= new (YYTHD->mem_root) Item_float($1.str, $1.length);
+ if (($$ == NULL) || (YYTHD->is_error()))
{
MYSQL_YYABORT;
}
@@ -8883,207 +10863,250 @@ NUM_literal:
**********************************************************************/
insert_ident:
- simple_ident_nospvar { $$=$1; }
- | table_wild { $$=$1; };
+ simple_ident_nospvar { $$=$1; }
+ | table_wild { $$=$1; }
+ ;
table_wild:
- ident '.' '*'
- {
- SELECT_LEX *sel= Select;
- $$ = new Item_field(Lex->current_context(), NullS, $1.str, "*");
- if ($$ == NULL)
- MYSQL_YYABORT;
- sel->with_wild++;
- }
- | ident '.' ident '.' '*'
- {
- SELECT_LEX *sel= Select;
- $$ = new Item_field(Lex->current_context(), (YYTHD->client_capabilities &
- CLIENT_NO_SCHEMA ? NullS : $1.str),
- $3.str,"*");
- if ($$ == NULL)
- MYSQL_YYABORT;
- sel->with_wild++;
- }
- ;
+ ident '.' '*'
+ {
+ SELECT_LEX *sel= Select;
+ $$= new (YYTHD->mem_root) Item_field(Lex->current_context(),
+ NullS, $1.str, "*");
+ if ($$ == NULL)
+ MYSQL_YYABORT;
+ sel->with_wild++;
+ }
+ | ident '.' ident '.' '*'
+ {
+ THD *thd= YYTHD;
+ SELECT_LEX *sel= Select;
+ const char* schema= thd->client_capabilities & CLIENT_NO_SCHEMA ?
+ NullS : $1.str;
+ $$= new (thd->mem_root) Item_field(Lex->current_context(),
+ schema,
+ $3.str,"*");
+ if ($$ == NULL)
+ MYSQL_YYABORT;
+ sel->with_wild++;
+ }
+ ;
order_ident:
- expr { $$=$1; };
+ expr { $$=$1; }
+ ;
simple_ident:
- ident
- {
- THD *thd= YYTHD;
- LEX *lex= thd->lex;
- Lex_input_stream *lip= YYLIP;
- sp_variable_t *spv;
- sp_pcontext *spc = lex->spcont;
- if (spc && (spv = spc->find_variable(&$1)))
- {
- /* We're compiling a stored procedure and found a variable */
- if (! lex->parsing_options.allows_variable)
+ ident
+ {
+ THD *thd= YYTHD;
+ LEX *lex= thd->lex;
+ Lex_input_stream *lip= YYLIP;
+ sp_variable_t *spv;
+ sp_pcontext *spc = lex->spcont;
+ if (spc && (spv = spc->find_variable(&$1)))
{
- my_error(ER_VIEW_SELECT_VARIABLE, MYF(0));
- MYSQL_YYABORT;
- }
+ /* We're compiling a stored procedure and found a variable */
+ if (! lex->parsing_options.allows_variable)
+ {
+ my_error(ER_VIEW_SELECT_VARIABLE, MYF(0));
+ MYSQL_YYABORT;
+ }
- Item_splocal *splocal;
- splocal= new Item_splocal($1, spv->offset, spv->type,
- lip->tok_start_prev -
- lex->sphead->m_tmp_query,
- lip->tok_end - lip->tok_start_prev);
- if (splocal == NULL)
- MYSQL_YYABORT;
+ Item_splocal *splocal;
+ splocal= new (thd->mem_root)
+ Item_splocal($1, spv->offset, spv->type,
+ lip->get_tok_start_prev() - lex->sphead->m_tmp_query,
+ lip->get_tok_end() - lip->get_tok_start_prev());
+ if (splocal == NULL)
+ MYSQL_YYABORT;
#ifndef DBUG_OFF
- splocal->m_sp= lex->sphead;
+ splocal->m_sp= lex->sphead;
#endif
- $$= splocal;
- lex->safe_to_cache_query=0;
- }
- else
- {
- SELECT_LEX *sel=Select;
- $$= (sel->parsing_place != IN_HAVING ||
- sel->get_in_sum_expr() > 0) ?
- (Item*) new Item_field(Lex->current_context(), NullS, NullS, $1.str) :
- (Item*) new Item_ref(Lex->current_context(), NullS, NullS, $1.str);
- if ($$ == NULL)
- MYSQL_YYABORT;
- }
- }
+ $$= splocal;
+ lex->safe_to_cache_query=0;
+ }
+ else
+ {
+ SELECT_LEX *sel=Select;
+ if ((sel->parsing_place != IN_HAVING) ||
+ (sel->get_in_sum_expr() > 0))
+ {
+ $$= new (thd->mem_root) Item_field(Lex->current_context(),
+ NullS, NullS, $1.str);
+ }
+ else
+ {
+ $$= new (thd->mem_root) Item_ref(Lex->current_context(),
+ NullS, NullS, $1.str);
+ }
+ if ($$ == NULL)
+ MYSQL_YYABORT;
+ }
+ }
| simple_ident_q { $$= $1; }
- ;
+ ;
simple_ident_nospvar:
- ident
- {
- SELECT_LEX *sel=Select;
- $$= (sel->parsing_place != IN_HAVING ||
- sel->get_in_sum_expr() > 0) ?
- (Item*) new Item_field(Lex->current_context(), NullS, NullS, $1.str) :
- (Item*) new Item_ref(Lex->current_context(), NullS, NullS, $1.str);
- if ($$ == NULL)
- MYSQL_YYABORT;
- }
- | simple_ident_q { $$= $1; }
- ;
-
-simple_ident_q:
- ident '.' ident
- {
- THD *thd= YYTHD;
- LEX *lex= thd->lex;
-
- /*
- FIXME This will work ok in simple_ident_nospvar case because
- we can't meet simple_ident_nospvar in trigger now. But it
- should be changed in future.
- */
- if (lex->sphead && lex->sphead->m_type == TYPE_ENUM_TRIGGER &&
- (!my_strcasecmp(system_charset_info, $1.str, "NEW") ||
- !my_strcasecmp(system_charset_info, $1.str, "OLD")))
+ ident
{
- Item_trigger_field *trg_fld;
- bool new_row= ($1.str[0]=='N' || $1.str[0]=='n');
-
- if (lex->trg_chistics.event == TRG_EVENT_INSERT &&
- !new_row)
+ THD *thd= YYTHD;
+ SELECT_LEX *sel=Select;
+ if ((sel->parsing_place != IN_HAVING) ||
+ (sel->get_in_sum_expr() > 0))
{
- my_error(ER_TRG_NO_SUCH_ROW_IN_TRG, MYF(0), "OLD", "on INSERT");
- MYSQL_YYABORT;
+ $$= new (thd->mem_root) Item_field(Lex->current_context(),
+ NullS, NullS, $1.str);
}
-
- if (lex->trg_chistics.event == TRG_EVENT_DELETE &&
- new_row)
+ else
{
- my_error(ER_TRG_NO_SUCH_ROW_IN_TRG, MYF(0), "NEW", "on DELETE");
- MYSQL_YYABORT;
+ $$= new (thd->mem_root) Item_ref(Lex->current_context(),
+ NullS, NullS, $1.str);
}
-
- DBUG_ASSERT(!new_row ||
- (lex->trg_chistics.event == TRG_EVENT_INSERT ||
- lex->trg_chistics.event == TRG_EVENT_UPDATE));
- const bool read_only=
- !(new_row && lex->trg_chistics.action_time == TRG_ACTION_BEFORE);
- if (!(trg_fld= new Item_trigger_field(Lex->current_context(),
- new_row ?
- Item_trigger_field::NEW_ROW:
- Item_trigger_field::OLD_ROW,
- $3.str,
- SELECT_ACL,
- read_only)))
+ if ($$ == NULL)
MYSQL_YYABORT;
+ }
+ | simple_ident_q { $$= $1; }
+ ;
+
+simple_ident_q:
+ ident '.' ident
+ {
+ THD *thd= YYTHD;
+ LEX *lex= thd->lex;
/*
- Let us add this item to list of all Item_trigger_field objects
- in trigger.
+ FIXME This will work ok in simple_ident_nospvar case because
+ we can't meet simple_ident_nospvar in trigger now. But it
+ should be changed in future.
*/
- lex->trg_table_fields.link_in_list((byte *)trg_fld,
- (byte**)&trg_fld->next_trg_field);
+ if (lex->sphead && lex->sphead->m_type == TYPE_ENUM_TRIGGER &&
+ (!my_strcasecmp(system_charset_info, $1.str, "NEW") ||
+ !my_strcasecmp(system_charset_info, $1.str, "OLD")))
+ {
+ Item_trigger_field *trg_fld;
+ bool new_row= ($1.str[0]=='N' || $1.str[0]=='n');
+
+ if (lex->trg_chistics.event == TRG_EVENT_INSERT &&
+ !new_row)
+ {
+ my_error(ER_TRG_NO_SUCH_ROW_IN_TRG, MYF(0), "OLD", "on INSERT");
+ MYSQL_YYABORT;
+ }
+
+ if (lex->trg_chistics.event == TRG_EVENT_DELETE &&
+ new_row)
+ {
+ my_error(ER_TRG_NO_SUCH_ROW_IN_TRG, MYF(0), "NEW", "on DELETE");
+ MYSQL_YYABORT;
+ }
+
+ DBUG_ASSERT(!new_row ||
+ (lex->trg_chistics.event == TRG_EVENT_INSERT ||
+ lex->trg_chistics.event == TRG_EVENT_UPDATE));
+ const bool read_only=
+ !(new_row && lex->trg_chistics.action_time == TRG_ACTION_BEFORE);
+ trg_fld= new (thd->mem_root)
+ Item_trigger_field(Lex->current_context(),
+ new_row ?
+ Item_trigger_field::NEW_ROW:
+ Item_trigger_field::OLD_ROW,
+ $3.str,
+ SELECT_ACL,
+ read_only);
+ if (trg_fld == NULL)
+ MYSQL_YYABORT;
+
+ /*
+ Let us add this item to list of all Item_trigger_field objects
+ in trigger.
+ */
+ lex->trg_table_fields.link_in_list((uchar*) trg_fld,
+ (uchar**) &trg_fld->next_trg_field);
+
+ $$= trg_fld;
+ }
+ else
+ {
+ SELECT_LEX *sel= lex->current_select;
+ if (sel->no_table_names_allowed)
+ {
+ my_error(ER_TABLENAME_NOT_ALLOWED_HERE,
+ MYF(0), $1.str, thd->where);
+ }
+ if ((sel->parsing_place != IN_HAVING) ||
+ (sel->get_in_sum_expr() > 0))
+ {
+ $$= new (thd->mem_root) Item_field(Lex->current_context(),
+ NullS, $1.str, $3.str);
+ }
+ else
+ {
+ $$= new (thd->mem_root) Item_ref(Lex->current_context(),
+ NullS, $1.str, $3.str);
+ }
+ if ($$ == NULL)
+ MYSQL_YYABORT;
+ }
+ }
+ | '.' ident '.' ident
+ {
+ THD *thd= YYTHD;
+ LEX *lex= thd->lex;
+ SELECT_LEX *sel= lex->current_select;
+ if (sel->no_table_names_allowed)
+ {
+ my_error(ER_TABLENAME_NOT_ALLOWED_HERE,
+ MYF(0), $2.str, thd->where);
+ }
+ if ((sel->parsing_place != IN_HAVING) ||
+ (sel->get_in_sum_expr() > 0))
+ {
+ $$= new (thd->mem_root) Item_field(Lex->current_context(),
+ NullS, $2.str, $4.str);
- $$= (Item *)trg_fld;
+ }
+ else
+ {
+ $$= new (thd->mem_root) Item_ref(Lex->current_context(),
+ NullS, $2.str, $4.str);
+ }
+ if ($$ == NULL)
+ MYSQL_YYABORT;
}
- else
+ | ident '.' ident '.' ident
{
- SELECT_LEX *sel= lex->current_select;
- if (sel->no_table_names_allowed)
- {
- my_error(ER_TABLENAME_NOT_ALLOWED_HERE,
- MYF(0), $1.str, thd->where);
- }
- $$= (sel->parsing_place != IN_HAVING ||
- sel->get_in_sum_expr() > 0) ?
- (Item*) new Item_field(Lex->current_context(), NullS, $1.str, $3.str) :
- (Item*) new Item_ref(Lex->current_context(), NullS, $1.str, $3.str);
+ THD *thd= YYTHD;
+ LEX *lex= thd->lex;
+ SELECT_LEX *sel= lex->current_select;
+ const char* schema= (thd->client_capabilities & CLIENT_NO_SCHEMA ?
+ NullS : $1.str);
+ if (sel->no_table_names_allowed)
+ {
+ my_error(ER_TABLENAME_NOT_ALLOWED_HERE,
+ MYF(0), $3.str, thd->where);
+ }
+ if ((sel->parsing_place != IN_HAVING) ||
+ (sel->get_in_sum_expr() > 0))
+ {
+ $$= new (thd->mem_root) Item_field(Lex->current_context(),
+ schema,
+ $3.str, $5.str);
+ }
+ else
+ {
+ $$= new (thd->mem_root) Item_ref(Lex->current_context(),
+ schema,
+ $3.str, $5.str);
+ }
if ($$ == NULL)
MYSQL_YYABORT;
}
- }
- | '.' ident '.' ident
- {
- THD *thd= YYTHD;
- LEX *lex= thd->lex;
- SELECT_LEX *sel= lex->current_select;
- if (sel->no_table_names_allowed)
- {
- my_error(ER_TABLENAME_NOT_ALLOWED_HERE,
- MYF(0), $2.str, thd->where);
- }
- $$= (sel->parsing_place != IN_HAVING ||
- sel->get_in_sum_expr() > 0) ?
- (Item*) new Item_field(Lex->current_context(), NullS, $2.str, $4.str) :
- (Item*) new Item_ref(Lex->current_context(), NullS, $2.str, $4.str);
- if ($$ == NULL)
- MYSQL_YYABORT;
- }
- | ident '.' ident '.' ident
- {
- THD *thd= YYTHD;
- LEX *lex= thd->lex;
- SELECT_LEX *sel= lex->current_select;
- if (sel->no_table_names_allowed)
- {
- my_error(ER_TABLENAME_NOT_ALLOWED_HERE,
- MYF(0), $3.str, thd->where);
- }
- $$= (sel->parsing_place != IN_HAVING ||
- sel->get_in_sum_expr() > 0) ?
- (Item*) new Item_field(Lex->current_context(),
- (YYTHD->client_capabilities &
- CLIENT_NO_SCHEMA ? NullS : $1.str),
- $3.str, $5.str) :
- (Item*) new Item_ref(Lex->current_context(),
- (YYTHD->client_capabilities &
- CLIENT_NO_SCHEMA ? NullS : $1.str),
- $3.str, $5.str);
- if ($$ == NULL)
- MYSQL_YYABORT;
- };
-
+ ;
field_ident:
- ident { $$=$1;}
- | ident '.' ident '.' ident
+ ident { $$=$1;}
+ | ident '.' ident '.' ident
{
TABLE_LIST *table= (TABLE_LIST*) Select->table_list.first;
if (my_strcasecmp(table_alias_charset, $1.str, table->db))
@@ -9099,7 +11122,7 @@ field_ident:
}
$$=$5;
}
- | ident '.' ident
+ | ident '.' ident
{
TABLE_LIST *table= (TABLE_LIST*) Select->table_list.first;
if (my_strcasecmp(table_alias_charset, $1.str, table->alias))
@@ -9109,45 +11132,48 @@ field_ident:
}
$$=$3;
}
- | '.' ident { $$=$2;} /* For Delphi */;
+ | '.' ident { $$=$2;} /* For Delphi */
+ ;
table_ident:
- ident
+ ident
{
- $$=new Table_ident($1);
+ $$= new Table_ident($1);
if ($$ == NULL)
MYSQL_YYABORT;
}
- | ident '.' ident
+ | ident '.' ident
{
- $$=new Table_ident(YYTHD, $1,$3,0);
+ $$= new Table_ident(YYTHD, $1,$3,0);
if ($$ == NULL)
MYSQL_YYABORT;
}
- | '.' ident
+ | '.' ident
{
- $$=new Table_ident($2); /* For Delphi */
+ /* For Delphi */
+ $$= new Table_ident($2);
if ($$ == NULL)
MYSQL_YYABORT;
}
;
table_ident_nodb:
- ident
+ ident
{
LEX_STRING db={(char*) any_db,3};
- $$=new Table_ident(YYTHD, db,$1,0);
+ $$= new Table_ident(YYTHD, db,$1,0);
if ($$ == NULL)
MYSQL_YYABORT;
}
;
IDENT_sys:
- IDENT { $$= $1; }
- | IDENT_QUOTED
- {
- THD *thd= YYTHD;
- if (thd->charset_is_system_charset)
+ IDENT { $$= $1; }
+ | IDENT_QUOTED
+ {
+ THD *thd= YYTHD;
+
+ if (thd->charset_is_system_charset)
{
CHARSET_INFO *cs= system_charset_info;
int dummy_error;
@@ -9160,170 +11186,190 @@ IDENT_sys:
cs->csname, $1.str + wlen);
MYSQL_YYABORT;
}
- $$= $1;
+ $$= $1;
}
- else
+ else
{
- if (thd->convert_string(&$$, system_charset_info,
- $1.str, $1.length, thd->charset()))
+ if (thd->convert_string(&$$, system_charset_info,
+ $1.str, $1.length, thd->charset()))
MYSQL_YYABORT;
}
- }
- ;
+ }
+ ;
TEXT_STRING_sys:
- TEXT_STRING
- {
- THD *thd= YYTHD;
- if (thd->charset_is_system_charset)
- $$= $1;
- else
+ TEXT_STRING
{
- if (thd->convert_string(&$$, system_charset_info,
- $1.str, $1.length, thd->charset()))
- MYSQL_YYABORT;
+ THD *thd= YYTHD;
+
+ if (thd->charset_is_system_charset)
+ $$= $1;
+ else
+ {
+ if (thd->convert_string(&$$, system_charset_info,
+ $1.str, $1.length, thd->charset()))
+ MYSQL_YYABORT;
+ }
}
- }
- ;
+ ;
TEXT_STRING_literal:
- TEXT_STRING
- {
- THD *thd= YYTHD;
- if (thd->charset_is_collation_connection)
- $$= $1;
- else
+ TEXT_STRING
{
- if (thd->convert_string(&$$, thd->variables.collation_connection,
- $1.str, $1.length, thd->charset()))
- MYSQL_YYABORT;
- }
- }
- ;
+ THD *thd= YYTHD;
+ if (thd->charset_is_collation_connection)
+ $$= $1;
+ else
+ {
+ if (thd->convert_string(&$$, thd->variables.collation_connection,
+ $1.str, $1.length, thd->charset()))
+ MYSQL_YYABORT;
+ }
+ }
+ ;
TEXT_STRING_filesystem:
- TEXT_STRING
- {
- THD *thd= YYTHD;
- if (thd->charset_is_character_set_filesystem)
- $$= $1;
- else
+ TEXT_STRING
{
- if (thd->convert_string(&$$, thd->variables.character_set_filesystem,
- $1.str, $1.length, thd->charset()))
- MYSQL_YYABORT;
+ THD *thd= YYTHD;
+
+ if (thd->charset_is_character_set_filesystem)
+ $$= $1;
+ else
+ {
+ if (thd->convert_string(&$$,
+ thd->variables.character_set_filesystem,
+ $1.str, $1.length, thd->charset()))
+ MYSQL_YYABORT;
+ }
}
- }
- ;
+ ;
ident:
- IDENT_sys { $$=$1; }
- | keyword
- {
- THD *thd= YYTHD;
- $$.str= thd->strmake($1.str, $1.length);
- if ($$.str == NULL)
- MYSQL_YYABORT;
- $$.length= $1.length;
- }
- ;
+ IDENT_sys { $$=$1; }
+ | keyword
+ {
+ THD *thd= YYTHD;
+ $$.str= thd->strmake($1.str, $1.length);
+ if ($$.str == NULL)
+ MYSQL_YYABORT;
+ $$.length= $1.length;
+ }
+ ;
label_ident:
- IDENT_sys { $$=$1; }
- | keyword_sp
- {
- THD *thd= YYTHD;
- $$.str= thd->strmake($1.str, $1.length);
- if ($$.str == NULL)
- MYSQL_YYABORT;
- $$.length= $1.length;
- }
- ;
+ IDENT_sys { $$=$1; }
+ | keyword_sp
+ {
+ THD *thd= YYTHD;
+ $$.str= thd->strmake($1.str, $1.length);
+ if ($$.str == NULL)
+ MYSQL_YYABORT;
+ $$.length= $1.length;
+ }
+ ;
ident_or_text:
- ident { $$=$1;}
- | TEXT_STRING_sys { $$=$1;}
- | LEX_HOSTNAME { $$=$1;};
+ ident { $$=$1;}
+ | TEXT_STRING_sys { $$=$1;}
+ | LEX_HOSTNAME { $$=$1;}
+ ;
user:
- ident_or_text
- {
- THD *thd= YYTHD;
- if (!($$=(LEX_USER*) thd->alloc(sizeof(st_lex_user))))
- MYSQL_YYABORT;
- $$->user = $1;
- $$->host.str= (char *) "%";
- $$->host.length= 1;
-
- if (check_string_length(&$$->user,
- ER(ER_USERNAME), USERNAME_LENGTH))
- 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;
-
- if (check_string_length(&$$->user,
- ER(ER_USERNAME), USERNAME_LENGTH) ||
+ ident_or_text
+ {
+ THD *thd= YYTHD;
+ if (!($$=(LEX_USER*) thd->alloc(sizeof(st_lex_user))))
+ MYSQL_YYABORT;
+ $$->user = $1;
+ $$->host.str= (char *) "%";
+ $$->host.length= 1;
+
+ if (check_string_char_length(&$$->user, ER(ER_USERNAME),
+ 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;
+
+ if (check_string_char_length(&$$->user, ER(ER_USERNAME),
+ USERNAME_CHAR_LENGTH,
+ system_charset_info, 0) ||
check_host_name(&$$->host))
- MYSQL_YYABORT;
- }
- | CURRENT_USER optional_braces
- {
- if (!($$=(LEX_USER*) YYTHD->alloc(sizeof(st_lex_user))))
- MYSQL_YYABORT;
- /*
- empty LEX_USER means current_user and
- will be handled in the get_current_user() function
- later
- */
- bzero($$, sizeof(LEX_USER));
- };
+ MYSQL_YYABORT;
+ }
+ | CURRENT_USER optional_braces
+ {
+ if (!($$=(LEX_USER*) YYTHD->alloc(sizeof(st_lex_user))))
+ MYSQL_YYABORT;
+ /*
+ empty LEX_USER means current_user and
+ will be handled in the get_current_user() function
+ later
+ */
+ bzero($$, sizeof(LEX_USER));
+ }
+ ;
/* Keyword that we allow for identifiers (except SP labels) */
keyword:
- keyword_sp {}
- | ASCII_SYM {}
- | BACKUP_SYM {}
- | BEGIN_SYM {}
- | BYTE_SYM {}
- | CACHE_SYM {}
- | CHARSET {}
- | CHECKSUM_SYM {}
- | CLOSE_SYM {}
- | COMMENT_SYM {}
- | COMMIT_SYM {}
- | CONTAINS_SYM {}
+ keyword_sp {}
+ | ASCII_SYM {}
+ | BACKUP_SYM {}
+ | BEGIN_SYM {}
+ | BYTE_SYM {}
+ | CACHE_SYM {}
+ | CHARSET {}
+ | CHECKSUM_SYM {}
+ | CLOSE_SYM {}
+ | COMMENT_SYM {}
+ | COMMIT_SYM {}
+ | CONTAINS_SYM {}
| DEALLOCATE_SYM {}
- | DO_SYM {}
- | END {}
- | EXECUTE_SYM {}
- | FLUSH_SYM {}
- | HANDLER_SYM {}
- | HELP_SYM {}
- | LANGUAGE_SYM {}
- | NO_SYM {}
- | OPEN_SYM {}
+ | DO_SYM {}
+ | END {}
+ | EXECUTE_SYM {}
+ | FLUSH_SYM {}
+ | HANDLER_SYM {}
+ | HELP_SYM {}
+ | HOST_SYM {}
+ | INSTALL_SYM {}
+ | LANGUAGE_SYM {}
+ | NO_SYM {}
+ | OPEN_SYM {}
+ | OPTIONS_SYM {}
+ | OWNER_SYM {}
+ | PARSER_SYM {}
+ | PARTITION_SYM {}
+ | PORT_SYM {}
| PREPARE_SYM {}
- | REPAIR {}
- | RESET_SYM {}
- | RESTORE_SYM {}
- | ROLLBACK_SYM {}
- | SAVEPOINT_SYM {}
- | SECURITY_SYM {}
- | SIGNED_SYM {}
- | SLAVE {}
- | START_SYM {}
- | STOP_SYM {}
- | TRUNCATE_SYM {}
- | UNICODE_SYM {}
+ | REMOVE_SYM {}
+ | REPAIR {}
+ | RESET_SYM {}
+ | RESTORE_SYM {}
+ | ROLLBACK_SYM {}
+ | SAVEPOINT_SYM {}
+ | SECURITY_SYM {}
+ | SERVER_SYM {}
+ | SIGNED_SYM {}
+ | SOCKET_SYM {}
+ | SLAVE {}
+ | SONAME_SYM {}
+ | START_SYM {}
+ | STOP_SYM {}
+ | TRUNCATE_SYM {}
+ | UNICODE_SYM {}
+ | UNINSTALL_SYM {}
+ | WRAPPER_SYM {}
| XA_SYM {}
| UPGRADE_SYM {}
- ;
+ ;
/*
* Keywords that we allow for labels in SPs.
@@ -9332,592 +11378,666 @@ keyword:
* conflicts.
*/
keyword_sp:
- ACTION {}
- | ADDDATE_SYM {}
- | AFTER_SYM {}
- | AGAINST {}
- | AGGREGATE_SYM {}
- | ALGORITHM_SYM {}
- | ANY_SYM {}
- | AUTO_INC {}
- | AVG_ROW_LENGTH {}
- | AVG_SYM {}
- | BERKELEY_DB_SYM {}
- | BINLOG_SYM {}
- | BIT_SYM {}
- | BOOL_SYM {}
- | BOOLEAN_SYM {}
- | BTREE_SYM {}
- | CASCADED {}
- | CHAIN_SYM {}
- | CHANGED {}
- | CIPHER_SYM {}
- | CLIENT_SYM {}
- | CODE_SYM {}
- | COLLATION_SYM {}
- | COLUMNS {}
- | COMMITTED_SYM {}
- | COMPACT_SYM {}
- | COMPRESSED_SYM {}
- | CONCURRENT {}
- | CONNECTION_SYM {}
- | CONSISTENT_SYM {}
- | CUBE_SYM {}
- | DATA_SYM {}
- | DATETIME {}
- | DATE_SYM {}
- | DAY_SYM {}
- | DEFINER_SYM {}
- | DELAY_KEY_WRITE_SYM {}
- | DES_KEY_FILE {}
- | DIRECTORY_SYM {}
- | DISCARD {}
- | DUMPFILE {}
- | DUPLICATE_SYM {}
- | DYNAMIC_SYM {}
- | ENUM {}
- | ENGINE_SYM {}
- | ENGINES_SYM {}
- | ERRORS {}
- | ESCAPE_SYM {}
- | EVENTS_SYM {}
- | EXPANSION_SYM {}
- | EXTENDED_SYM {}
- | FAST_SYM {}
- | FOUND_SYM {}
- | DISABLE_SYM {}
- | ENABLE_SYM {}
- | FULL {}
- | FILE_SYM {}
- | FIRST_SYM {}
- | FIXED_SYM {}
- | FRAC_SECOND_SYM {}
- | GEOMETRY_SYM {}
- | GEOMETRYCOLLECTION {}
- | GET_FORMAT {}
- | GRANTS {}
- | GLOBAL_SYM {}
- | HASH_SYM {}
- | HOSTS_SYM {}
- | HOUR_SYM {}
- | IDENTIFIED_SYM {}
- | INVOKER_SYM {}
- | IMPORT {}
- | INDEXES {}
- | ISOLATION {}
- | ISSUER_SYM {}
- | INNOBASE_SYM {}
- | INSERT_METHOD {}
- | RELAY_THREAD {}
- | LAST_SYM {}
- | LEAVES {}
- | LEVEL_SYM {}
- | LINESTRING {}
- | LOCAL_SYM {}
- | LOCKS_SYM {}
- | LOGS_SYM {}
- | MAX_ROWS {}
- | MASTER_SYM {}
- | MASTER_HOST_SYM {}
- | MASTER_PORT_SYM {}
- | MASTER_LOG_FILE_SYM {}
- | MASTER_LOG_POS_SYM {}
- | MASTER_USER_SYM {}
- | MASTER_PASSWORD_SYM {}
- | MASTER_SERVER_ID_SYM {}
- | MASTER_CONNECT_RETRY_SYM {}
- | MASTER_SSL_SYM {}
- | MASTER_SSL_CA_SYM {}
- | MASTER_SSL_CAPATH_SYM {}
- | MASTER_SSL_CERT_SYM {}
- | MASTER_SSL_CIPHER_SYM {}
- | MASTER_SSL_KEY_SYM {}
- | MAX_CONNECTIONS_PER_HOUR {}
- | MAX_QUERIES_PER_HOUR {}
- | MAX_UPDATES_PER_HOUR {}
- | MAX_USER_CONNECTIONS_SYM {}
- | MEDIUM_SYM {}
- | MERGE_SYM {}
- | MICROSECOND_SYM {}
- | MIGRATE_SYM {}
- | MINUTE_SYM {}
- | MIN_ROWS {}
- | MODIFY_SYM {}
- | MODE_SYM {}
- | MONTH_SYM {}
- | MULTILINESTRING {}
- | MULTIPOINT {}
- | MULTIPOLYGON {}
- | MUTEX_SYM {}
- | NAME_SYM {}
- | NAMES_SYM {}
- | NATIONAL_SYM {}
- | NCHAR_SYM {}
- | NDBCLUSTER_SYM {}
- | NEXT_SYM {}
- | NEW_SYM {}
- | NONE_SYM {}
- | NVARCHAR_SYM {}
- | OFFSET_SYM {}
- | OLD_PASSWORD {}
- | ONE_SHOT_SYM {}
- | ONE_SYM {}
- | PACK_KEYS_SYM {}
- | PARTIAL {}
- | PASSWORD {}
- | PHASE_SYM {}
- | POINT_SYM {}
- | POLYGON {}
- | PREV_SYM {}
- | PRIVILEGES {}
- | PROCESS {}
- | PROCESSLIST_SYM {}
- | QUARTER_SYM {}
- | QUERY_SYM {}
- | QUICK {}
- | RAID_0_SYM {}
- | RAID_CHUNKS {}
- | RAID_CHUNKSIZE {}
- | RAID_STRIPED_SYM {}
- | RAID_TYPE {}
- | RECOVER_SYM {}
- | REDUNDANT_SYM {}
- | RELAY_LOG_FILE_SYM {}
- | RELAY_LOG_POS_SYM {}
- | RELOAD {}
- | REPEATABLE_SYM {}
- | REPLICATION {}
- | RESOURCES {}
- | RESUME_SYM {}
- | RETURNS_SYM {}
- | ROLLUP_SYM {}
- | ROUTINE_SYM {}
- | ROWS_SYM {}
- | ROW_FORMAT_SYM {}
- | ROW_SYM {}
- | RTREE_SYM {}
- | SECOND_SYM {}
- | SERIAL_SYM {}
- | SERIALIZABLE_SYM {}
- | SESSION_SYM {}
- | SIMPLE_SYM {}
- | SHARE_SYM {}
- | SHUTDOWN {}
- | SNAPSHOT_SYM {}
- | SOUNDS_SYM {}
- | SQL_CACHE_SYM {}
- | SQL_BUFFER_RESULT {}
- | SQL_NO_CACHE_SYM {}
- | SQL_THREAD {}
- | STATUS_SYM {}
- | STORAGE_SYM {}
- | STRING_SYM {}
- | SUBDATE_SYM {}
- | SUBJECT_SYM {}
- | SUPER_SYM {}
- | SUSPEND_SYM {}
- | TABLES {}
- | TABLESPACE {}
- | TEMPORARY {}
- | TEMPTABLE_SYM {}
- | TEXT_SYM {}
- | TRANSACTION_SYM {}
- | TRIGGERS_SYM {}
- | TIMESTAMP {}
- | TIMESTAMP_ADD {}
- | TIMESTAMP_DIFF {}
- | TIME_SYM {}
- | TYPES_SYM {}
- | TYPE_SYM {}
- | UDF_RETURNS_SYM {}
- | FUNCTION_SYM {}
- | UNCOMMITTED_SYM {}
- | UNDEFINED_SYM {}
- | UNKNOWN_SYM {}
- | UNTIL_SYM {}
- | USER {}
- | USE_FRM {}
- | VARIABLES {}
- | VIEW_SYM {}
- | VALUE_SYM {}
- | WARNINGS {}
- | WEEK_SYM {}
- | WORK_SYM {}
- | X509_SYM {}
- | YEAR_SYM {}
- ;
+ ACTION {}
+ | ADDDATE_SYM {}
+ | AFTER_SYM {}
+ | AGAINST {}
+ | AGGREGATE_SYM {}
+ | ALGORITHM_SYM {}
+ | ANY_SYM {}
+ | AT_SYM {}
+ | AUTHORS_SYM {}
+ | AUTO_INC {}
+ | AUTOEXTEND_SIZE_SYM {}
+ | AVG_ROW_LENGTH {}
+ | AVG_SYM {}
+ | BINLOG_SYM {}
+ | BIT_SYM {}
+ | BLOCK_SYM {}
+ | BOOL_SYM {}
+ | BOOLEAN_SYM {}
+ | BTREE_SYM {}
+ | CASCADED {}
+ | CHAIN_SYM {}
+ | CHANGED {}
+ | CIPHER_SYM {}
+ | CLIENT_SYM {}
+ | COALESCE {}
+ | CODE_SYM {}
+ | COLLATION_SYM {}
+ | COLUMNS {}
+ | COMMITTED_SYM {}
+ | COMPACT_SYM {}
+ | COMPLETION_SYM {}
+ | COMPRESSED_SYM {}
+ | CONCURRENT {}
+ | CONNECTION_SYM {}
+ | CONSISTENT_SYM {}
+ | CONTEXT_SYM {}
+ | CONTRIBUTORS_SYM {}
+ | CPU_SYM {}
+ | CUBE_SYM {}
+ | DATA_SYM {}
+ | DATAFILE_SYM {}
+ | DATETIME {}
+ | DATE_SYM {}
+ | DAY_SYM {}
+ | DEFINER_SYM {}
+ | DELAY_KEY_WRITE_SYM {}
+ | DES_KEY_FILE {}
+ | DIRECTORY_SYM {}
+ | DISABLE_SYM {}
+ | DISCARD {}
+ | DISK_SYM {}
+ | DUMPFILE {}
+ | DUPLICATE_SYM {}
+ | DYNAMIC_SYM {}
+ | ENDS_SYM {}
+ | ENUM {}
+ | ENGINE_SYM {}
+ | ENGINES_SYM {}
+ | ERRORS {}
+ | ESCAPE_SYM {}
+ | EVENT_SYM {}
+ | EVENTS_SYM {}
+ | EVERY_SYM {}
+ | EXPANSION_SYM {}
+ | EXTENDED_SYM {}
+ | EXTENT_SIZE_SYM {}
+ | FAULTS_SYM {}
+ | FAST_SYM {}
+ | FOUND_SYM {}
+ | ENABLE_SYM {}
+ | FULL {}
+ | FILE_SYM {}
+ | FIRST_SYM {}
+ | FIXED_SYM {}
+ | FRAC_SECOND_SYM {}
+ | GEOMETRY_SYM {}
+ | GEOMETRYCOLLECTION {}
+ | GET_FORMAT {}
+ | GRANTS {}
+ | GLOBAL_SYM {}
+ | HASH_SYM {}
+ | HOSTS_SYM {}
+ | HOUR_SYM {}
+ | IDENTIFIED_SYM {}
+ | INVOKER_SYM {}
+ | IMPORT {}
+ | INDEXES {}
+ | INITIAL_SIZE_SYM {}
+ | IO_SYM {}
+ | IPC_SYM {}
+ | ISOLATION {}
+ | ISSUER_SYM {}
+ | INNOBASE_SYM {}
+ | INSERT_METHOD {}
+ | KEY_BLOCK_SIZE {}
+ | LAST_SYM {}
+ | LEAVES {}
+ | LESS_SYM {}
+ | LEVEL_SYM {}
+ | LINESTRING {}
+ | LIST_SYM {}
+ | LOCAL_SYM {}
+ | LOCKS_SYM {}
+ | LOGFILE_SYM {}
+ | LOGS_SYM {}
+ | MAX_ROWS {}
+ | MASTER_SYM {}
+ | MASTER_HOST_SYM {}
+ | MASTER_PORT_SYM {}
+ | MASTER_LOG_FILE_SYM {}
+ | MASTER_LOG_POS_SYM {}
+ | MASTER_USER_SYM {}
+ | MASTER_PASSWORD_SYM {}
+ | MASTER_SERVER_ID_SYM {}
+ | MASTER_CONNECT_RETRY_SYM {}
+ | MASTER_SSL_SYM {}
+ | MASTER_SSL_CA_SYM {}
+ | MASTER_SSL_CAPATH_SYM {}
+ | MASTER_SSL_CERT_SYM {}
+ | MASTER_SSL_CIPHER_SYM {}
+ | MASTER_SSL_KEY_SYM {}
+ | MAX_CONNECTIONS_PER_HOUR {}
+ | MAX_QUERIES_PER_HOUR {}
+ | MAX_SIZE_SYM {}
+ | MAX_UPDATES_PER_HOUR {}
+ | MAX_USER_CONNECTIONS_SYM {}
+ | MAX_VALUE_SYM {}
+ | MEDIUM_SYM {}
+ | MEMORY_SYM {}
+ | MERGE_SYM {}
+ | MICROSECOND_SYM {}
+ | MIGRATE_SYM {}
+ | MINUTE_SYM {}
+ | MIN_ROWS {}
+ | MODIFY_SYM {}
+ | MODE_SYM {}
+ | MONTH_SYM {}
+ | MULTILINESTRING {}
+ | MULTIPOINT {}
+ | MULTIPOLYGON {}
+ | MUTEX_SYM {}
+ | NAME_SYM {}
+ | NAMES_SYM {}
+ | NATIONAL_SYM {}
+ | NCHAR_SYM {}
+ | NDBCLUSTER_SYM {}
+ | NEXT_SYM {}
+ | NEW_SYM {}
+ | NO_WAIT_SYM {}
+ | NODEGROUP_SYM {}
+ | NONE_SYM {}
+ | NVARCHAR_SYM {}
+ | OFFSET_SYM {}
+ | OLD_PASSWORD {}
+ | ONE_SHOT_SYM {}
+ | ONE_SYM {}
+ | PACK_KEYS_SYM {}
+ | PAGE_SYM {}
+ | PAGE_CHECKSUM_SYM {}
+ | PARTIAL {}
+ | PARTITIONING_SYM {}
+ | PARTITIONS_SYM {}
+ | PASSWORD {}
+ | PHASE_SYM {}
+ | PLUGIN_SYM {}
+ | PLUGINS_SYM {}
+ | POINT_SYM {}
+ | POLYGON {}
+ | PRESERVE_SYM {}
+ | PREV_SYM {}
+ | PRIVILEGES {}
+ | PROCESS {}
+ | PROCESSLIST_SYM {}
+ | PROFILE_SYM {}
+ | PROFILES_SYM {}
+ | QUARTER_SYM {}
+ | QUERY_SYM {}
+ | QUICK {}
+ | READ_ONLY_SYM {}
+ | REBUILD_SYM {}
+ | RECOVER_SYM {}
+ | REDO_BUFFER_SIZE_SYM {}
+ | REDOFILE_SYM {}
+ | REDUNDANT_SYM {}
+ | RELAY_LOG_FILE_SYM {}
+ | RELAY_LOG_POS_SYM {}
+ | RELAY_THREAD {}
+ | RELOAD {}
+ | REORGANIZE_SYM {}
+ | REPEATABLE_SYM {}
+ | REPLICATION {}
+ | RESOURCES {}
+ | RESUME_SYM {}
+ | RETURNS_SYM {}
+ | ROLLUP_SYM {}
+ | ROUTINE_SYM {}
+ | ROWS_SYM {}
+ | ROW_FORMAT_SYM {}
+ | ROW_SYM {}
+ | RTREE_SYM {}
+ | SCHEDULE_SYM {}
+ | SECOND_SYM {}
+ | SERIAL_SYM {}
+ | SERIALIZABLE_SYM {}
+ | SESSION_SYM {}
+ | SIMPLE_SYM {}
+ | SHARE_SYM {}
+ | SHUTDOWN {}
+ | SNAPSHOT_SYM {}
+ | SOUNDS_SYM {}
+ | SOURCE_SYM {}
+ | SQL_CACHE_SYM {}
+ | SQL_BUFFER_RESULT {}
+ | SQL_NO_CACHE_SYM {}
+ | SQL_THREAD {}
+ | STARTS_SYM {}
+ | STATUS_SYM {}
+ | STORAGE_SYM {}
+ | STRING_SYM {}
+ | SUBDATE_SYM {}
+ | SUBJECT_SYM {}
+ | SUBPARTITION_SYM {}
+ | SUBPARTITIONS_SYM {}
+ | SUPER_SYM {}
+ | SUSPEND_SYM {}
+ | SWAPS_SYM {}
+ | SWITCHES_SYM {}
+ | TABLES {}
+ | TABLE_CHECKSUM_SYM {}
+ | TABLESPACE {}
+ | TEMPORARY {}
+ | TEMPTABLE_SYM {}
+ | TEXT_SYM {}
+ | THAN_SYM {}
+ | TRANSACTION_SYM {}
+ | TRANSACTIONAL_SYM {}
+ | TRIGGERS_SYM {}
+ | TIMESTAMP {}
+ | TIMESTAMP_ADD {}
+ | TIMESTAMP_DIFF {}
+ | TIME_SYM {}
+ | TYPES_SYM {}
+ | TYPE_SYM {}
+ | UDF_RETURNS_SYM {}
+ | FUNCTION_SYM {}
+ | UNCOMMITTED_SYM {}
+ | UNDEFINED_SYM {}
+ | UNDO_BUFFER_SIZE_SYM {}
+ | UNDOFILE_SYM {}
+ | UNKNOWN_SYM {}
+ | UNTIL_SYM {}
+ | USER {}
+ | USE_FRM {}
+ | VARIABLES {}
+ | VIEW_SYM {}
+ | VALUE_SYM {}
+ | WARNINGS {}
+ | WAIT_SYM {}
+ | WEEK_SYM {}
+ | WORK_SYM {}
+ | X509_SYM {}
+ | YEAR_SYM {}
+ ;
/* Option functions */
set:
- SET opt_option
- {
- LEX *lex=Lex;
- lex->sql_command= SQLCOM_SET_OPTION;
- mysql_init_select(lex);
- lex->option_type=OPT_SESSION;
- lex->var_list.empty();
- lex->one_shot_set= 0;
- }
- option_value_list
- {}
- ;
+ SET opt_option
+ {
+ LEX *lex=Lex;
+ lex->sql_command= SQLCOM_SET_OPTION;
+ mysql_init_select(lex);
+ lex->option_type=OPT_SESSION;
+ lex->var_list.empty();
+ lex->one_shot_set= 0;
+ lex->autocommit= 0;
+ }
+ option_value_list
+ {}
+ ;
opt_option:
- /* empty */ {}
- | OPTION {};
+ /* empty */ {}
+ | OPTION {}
+ ;
option_value_list:
- option_type_value
- | option_value_list ',' option_type_value;
+ option_type_value
+ | option_value_list ',' option_type_value
+ ;
option_type_value:
- {
- THD *thd= YYTHD;
- LEX *lex= thd->lex;
- Lex_input_stream *lip= YYLIP;
-
- if (lex->sphead)
{
- /*
- If we are in SP we want have own LEX for each assignment.
- This is mostly because it is hard for several sp_instr_set
- and sp_instr_set_trigger instructions share one LEX.
- (Well, it is theoretically possible but adds some extra
- overhead on preparation for execution stage and IMO less
- robust).
-
- QQ: May be we should simply prohibit group assignments in SP?
- */
- if (Lex->sphead->reset_lex(thd))
- MYSQL_YYABORT;
- lex= thd->lex;
+ THD *thd= YYTHD;
+ LEX *lex= thd->lex;
+ Lex_input_stream *lip= YYLIP;
- /* Set new LEX as if we at start of set rule. */
- lex->sql_command= SQLCOM_SET_OPTION;
- mysql_init_select(lex);
- lex->option_type=OPT_SESSION;
- lex->var_list.empty();
- lex->one_shot_set= 0;
- lex->sphead->m_tmp_query= lip->tok_start;
+ if (lex->sphead)
+ {
+ /*
+ If we are in SP we want have own LEX for each assignment.
+ This is mostly because it is hard for several sp_instr_set
+ and sp_instr_set_trigger instructions share one LEX.
+ (Well, it is theoretically possible but adds some extra
+ overhead on preparation for execution stage and IMO less
+ robust).
+
+ QQ: May be we should simply prohibit group assignments in SP?
+ */
+ lex->sphead->reset_lex(thd);
+ lex= thd->lex;
+
+ /* Set new LEX as if we at start of set rule. */
+ lex->sql_command= SQLCOM_SET_OPTION;
+ mysql_init_select(lex);
+ lex->option_type=OPT_SESSION;
+ lex->var_list.empty();
+ lex->one_shot_set= 0;
+ lex->autocommit= 0;
+ lex->sphead->m_tmp_query= lip->get_tok_start();
+ }
}
- }
- ext_option_value
- {
- THD *thd= YYTHD;
- LEX *lex= thd->lex;
- Lex_input_stream *lip= YYLIP;
-
- if (lex->sphead)
+ ext_option_value
{
- sp_head *sp= lex->sphead;
+ THD *thd= YYTHD;
+ LEX *lex= thd->lex;
+ Lex_input_stream *lip= YYLIP;
- if (!lex->var_list.is_empty())
- {
- /*
- We have assignment to user or system variable or
- option setting, so we should construct sp_instr_stmt
- for it.
- */
- LEX_STRING qbuff;
- sp_instr_stmt *i;
+ if (lex->sphead)
+ {
+ sp_head *sp= lex->sphead;
- if (!(i= new sp_instr_stmt(sp->instructions(), lex->spcont,
- lex)))
- MYSQL_YYABORT;
+ if (!lex->var_list.is_empty())
+ {
+ /*
+ We have assignment to user or system variable or
+ option setting, so we should construct sp_instr_stmt
+ for it.
+ */
+ LEX_STRING qbuff;
+ sp_instr_stmt *i;
- /*
- Extract the query statement from the tokenizer. The
- end is either lip->ptr, if there was no lookahead,
- lip->tok_end otherwise.
- */
- if (yychar == YYEMPTY)
- qbuff.length= lip->ptr - sp->m_tmp_query;
- else
- qbuff.length= lip->tok_end - sp->m_tmp_query;
+ if (!(i= new sp_instr_stmt(sp->instructions(), lex->spcont,
+ lex)))
+ MYSQL_YYABORT;
- if (!(qbuff.str= alloc_root(thd->mem_root, qbuff.length + 5)))
- MYSQL_YYABORT;
+ /*
+ Extract the query statement from the tokenizer. The
+ end is either lip->ptr, if there was no lookahead,
+ lip->tok_end otherwise.
+ */
+ if (yychar == YYEMPTY)
+ qbuff.length= lip->get_ptr() - sp->m_tmp_query;
+ else
+ qbuff.length= lip->get_tok_end() - sp->m_tmp_query;
- strmake(strmake(qbuff.str, "SET ", 4), sp->m_tmp_query,
- qbuff.length);
- qbuff.length+= 4;
- i->m_query= qbuff;
- sp->add_instr(i);
+ if (!(qbuff.str= (char*) alloc_root(thd->mem_root,
+ qbuff.length + 5)))
+ MYSQL_YYABORT;
+
+ strmake(strmake(qbuff.str, "SET ", 4), sp->m_tmp_query,
+ qbuff.length);
+ qbuff.length+= 4;
+ i->m_query= qbuff;
+ sp->add_instr(i);
+ }
+ lex->sphead->restore_lex(thd);
}
- lex->sphead->restore_lex(thd);
}
- };
+ ;
option_type:
- option_type2 {}
- | GLOBAL_SYM { $$=OPT_GLOBAL; }
- | LOCAL_SYM { $$=OPT_SESSION; }
- | SESSION_SYM { $$=OPT_SESSION; }
- ;
+ option_type2 {}
+ | GLOBAL_SYM { $$=OPT_GLOBAL; }
+ | LOCAL_SYM { $$=OPT_SESSION; }
+ | SESSION_SYM { $$=OPT_SESSION; }
+ ;
option_type2:
- /* empty */ { $$= OPT_DEFAULT; }
- | ONE_SHOT_SYM { Lex->one_shot_set= 1; $$= OPT_SESSION; }
- ;
+ /* empty */ { $$= OPT_DEFAULT; }
+ | ONE_SHOT_SYM { Lex->one_shot_set= 1; $$= OPT_SESSION; }
+ ;
opt_var_type:
- /* empty */ { $$=OPT_SESSION; }
- | GLOBAL_SYM { $$=OPT_GLOBAL; }
- | LOCAL_SYM { $$=OPT_SESSION; }
- | SESSION_SYM { $$=OPT_SESSION; }
- ;
+ /* empty */ { $$=OPT_SESSION; }
+ | GLOBAL_SYM { $$=OPT_GLOBAL; }
+ | LOCAL_SYM { $$=OPT_SESSION; }
+ | SESSION_SYM { $$=OPT_SESSION; }
+ ;
opt_var_ident_type:
- /* empty */ { $$=OPT_DEFAULT; }
- | GLOBAL_SYM '.' { $$=OPT_GLOBAL; }
- | LOCAL_SYM '.' { $$=OPT_SESSION; }
- | SESSION_SYM '.' { $$=OPT_SESSION; }
- ;
+ /* empty */ { $$=OPT_DEFAULT; }
+ | GLOBAL_SYM '.' { $$=OPT_GLOBAL; }
+ | LOCAL_SYM '.' { $$=OPT_SESSION; }
+ | SESSION_SYM '.' { $$=OPT_SESSION; }
+ ;
ext_option_value:
- sys_option_value
- | option_type2 option_value;
+ sys_option_value
+ | option_type2 option_value
+ ;
sys_option_value:
- option_type internal_variable_name equal set_expr_or_default
- {
- LEX *lex=Lex;
-
- if ($2.var == &trg_new_row_fake_var)
+ option_type internal_variable_name equal set_expr_or_default
{
- /* We are in trigger and assigning value to field of new row */
- Item *it;
- Item_trigger_field *trg_fld;
- sp_instr_set_trigger_field *sp_fld;
- LINT_INIT(sp_fld);
- if ($1)
+ THD *thd= YYTHD;
+ LEX *lex=Lex;
+
+ if ($2.var == trg_new_row_fake_var)
{
- my_parse_error(ER(ER_SYNTAX_ERROR));
- MYSQL_YYABORT;
+ /* We are in trigger and assigning value to field of new row */
+ Item *it;
+ Item_trigger_field *trg_fld;
+ sp_instr_set_trigger_field *sp_fld;
+ LINT_INIT(sp_fld);
+ if ($1)
+ {
+ my_parse_error(ER(ER_SYNTAX_ERROR));
+ MYSQL_YYABORT;
+ }
+ if ($4)
+ it= $4;
+ else
+ {
+ /* QQ: Shouldn't this be field's default value ? */
+ it= new Item_null();
+ }
+
+ DBUG_ASSERT(lex->trg_chistics.action_time == TRG_ACTION_BEFORE &&
+ (lex->trg_chistics.event == TRG_EVENT_INSERT ||
+ lex->trg_chistics.event == TRG_EVENT_UPDATE));
+
+ trg_fld= new (thd->mem_root)
+ Item_trigger_field(Lex->current_context(),
+ Item_trigger_field::NEW_ROW,
+ $2.base_name.str,
+ UPDATE_ACL, FALSE);
+ if (trg_fld == NULL)
+ MYSQL_YYABORT;
+
+ sp_fld= new sp_instr_set_trigger_field(lex->sphead->
+ instructions(),
+ lex->spcont,
+ trg_fld,
+ it, lex);
+ if (sp_fld == NULL)
+ MYSQL_YYABORT;
+
+ /*
+ Let us add this item to list of all Item_trigger_field
+ objects in trigger.
+ */
+ lex->trg_table_fields.link_in_list((uchar *)trg_fld,
+ (uchar **) &trg_fld->
+ next_trg_field);
+
+ lex->sphead->add_instr(sp_fld);
+ }
+ else if ($2.var)
+ { /* System variable */
+ if ($1)
+ lex->option_type= $1;
+ set_var *var= new set_var(lex->option_type, $2.var,
+ &$2.base_name, $4);
+ if (var == NULL)
+ MYSQL_YYABORT;
+ lex->var_list.push_back(var);
}
- if ($4)
- it= $4;
else
{
- /* QQ: Shouldn't this be field's default value ? */
- it= new Item_null();
- }
-
- DBUG_ASSERT(lex->trg_chistics.action_time == TRG_ACTION_BEFORE &&
- (lex->trg_chistics.event == TRG_EVENT_INSERT ||
- lex->trg_chistics.event == TRG_EVENT_UPDATE));
- if (!(trg_fld= new Item_trigger_field(Lex->current_context(),
- Item_trigger_field::NEW_ROW,
- $2.base_name.str,
- UPDATE_ACL, FALSE)) ||
- !(sp_fld= new sp_instr_set_trigger_field(lex->sphead->
- instructions(),
- lex->spcont,
- trg_fld,
- it, lex)))
- MYSQL_YYABORT;
+ /* An SP local variable */
+ sp_pcontext *ctx= lex->spcont;
+ sp_variable_t *spv;
+ sp_instr_set *sp_set;
+ Item *it;
+ if ($1)
+ {
+ my_parse_error(ER(ER_SYNTAX_ERROR));
+ MYSQL_YYABORT;
+ }
- /*
- Let us add this item to list of all Item_trigger_field
- objects in trigger.
- */
- lex->trg_table_fields.link_in_list((byte *)trg_fld,
- (byte **)&trg_fld->next_trg_field);
+ spv= ctx->find_variable(&$2.base_name);
- lex->sphead->add_instr(sp_fld);
- }
- else if ($2.var)
- { /* System variable */
- if ($1)
- lex->option_type= $1;
- lex->var_list.push_back(new set_var(lex->option_type, $2.var,
- &$2.base_name, $4));
+ if ($4)
+ it= $4;
+ else if (spv->dflt)
+ it= spv->dflt;
+ else
+ {
+ it= new (thd->mem_root) Item_null();
+ if (it == NULL)
+ MYSQL_YYABORT;
+ }
+ sp_set= new sp_instr_set(lex->sphead->instructions(), ctx,
+ spv->offset, it, spv->type, lex, TRUE);
+ if (sp_set == NULL)
+ MYSQL_YYABORT;
+ lex->sphead->add_instr(sp_set);
+ }
}
- else
+ | option_type TRANSACTION_SYM ISOLATION LEVEL_SYM isolation_types
{
- /* An SP local variable */
- sp_pcontext *ctx= lex->spcont;
- sp_variable_t *spv;
- sp_instr_set *sp_set;
- Item *it;
- if ($1)
- {
- my_parse_error(ER(ER_SYNTAX_ERROR));
+ THD *thd= YYTHD;
+ LEX *lex=Lex;
+ lex->option_type= $1;
+ Item *item= new (thd->mem_root) Item_int((int32) $5);
+ if (item == NULL)
MYSQL_YYABORT;
- }
-
- spv= ctx->find_variable(&$2.base_name);
-
- if ($4)
- it= $4;
- else if (spv->dflt)
- it= spv->dflt;
- else
- it= new Item_null();
- if (it == NULL)
+ set_var *var= new set_var(lex->option_type,
+ find_sys_var(thd, "tx_isolation"),
+ &null_lex_str,
+ item);
+ if (var == NULL)
MYSQL_YYABORT;
- sp_set= new sp_instr_set(lex->sphead->instructions(), ctx,
- spv->offset, it, spv->type, lex, TRUE);
- lex->sphead->add_instr(sp_set);
+ lex->var_list.push_back(var);
}
- }
- | option_type TRANSACTION_SYM ISOLATION LEVEL_SYM isolation_types
- {
- LEX *lex=Lex;
- if ($1)
- lex->option_type= $1;
- Item *item= new Item_int((int32) $5);
- if (item == NULL)
- MYSQL_YYABORT;
- set_var *var= new set_var(lex->option_type,
- find_sys_var("tx_isolation"),
- &null_lex_str,
- item);
- if (var == NULL)
- MYSQL_YYABORT;
- lex->var_list.push_back(var);
- }
;
option_value:
- '@' ident_or_text equal expr
- {
- Item_func_set_user_var *item= new Item_func_set_user_var($2,$4);
- if (item == NULL)
- MYSQL_YYABORT;
- set_var_user *var= new set_var_user(item);
- if (var == NULL)
- MYSQL_YYABORT;
- Lex->var_list.push_back(var);
- }
- | '@' '@' opt_var_ident_type internal_variable_name equal set_expr_or_default
- {
- LEX *lex=Lex;
+ '@' ident_or_text equal expr
+ {
+ Item_func_set_user_var *item;
+ item= new (YYTHD->mem_root) Item_func_set_user_var($2, $4);
+ if (item == NULL)
+ MYSQL_YYABORT;
+ set_var_user *var= new set_var_user(item);
+ if (var == NULL)
+ MYSQL_YYABORT;
+ Lex->var_list.push_back(var);
+ }
+ | '@' '@' opt_var_ident_type internal_variable_name equal set_expr_or_default
+ {
+ LEX *lex=Lex;
set_var *var= new set_var($3, $4.var, &$4.base_name, $6);
if (var == NULL)
MYSQL_YYABORT;
- lex->var_list.push_back(var);
- }
- | charset old_or_new_charset_name_or_default
- {
- THD *thd= YYTHD;
- LEX *lex= Lex;
- $2= $2 ? $2: global_system_variables.character_set_client;
- set_var_collation_client *var;
- var= new set_var_collation_client($2,
- thd->variables.collation_database,
- $2);
- if (var == NULL)
- MYSQL_YYABORT;
- lex->var_list.push_back(var);
- }
+ lex->var_list.push_back(var);
+ }
+ | 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;
+ set_var_collation_client *var;
+ var= new set_var_collation_client(cs2,
+ thd->variables.collation_database,
+ cs2);
+ if (var == NULL)
+ MYSQL_YYABORT;
+ lex->var_list.push_back(var);
+ }
| NAMES_SYM equal expr
- {
- LEX *lex= Lex;
+ {
+ LEX *lex= Lex;
sp_pcontext *spc= lex->spcont;
- LEX_STRING names;
+ LEX_STRING names;
- names.str= (char *)"names";
- names.length= 5;
- if (spc && spc->find_variable(&names))
+ names.str= (char *)"names";
+ names.length= 5;
+ if (spc && spc->find_variable(&names))
my_error(ER_SP_BAD_VAR_SHADOW, MYF(0), names.str);
else
my_parse_error(ER(ER_SYNTAX_ERROR));
- MYSQL_YYABORT;
- }
- | NAMES_SYM charset_name_or_default opt_collate
- {
- LEX *lex= Lex;
- $2= $2 ? $2 : global_system_variables.character_set_client;
- $3= $3 ? $3 : $2;
- if (!my_charset_same($2,$3))
- {
- my_error(ER_COLLATION_CHARSET_MISMATCH, MYF(0),
- $3->name, $2->csname);
- MYSQL_YYABORT;
- }
- set_var_collation_client *var;
- var= new set_var_collation_client($3,$3,$3);
- if (var == NULL)
MYSQL_YYABORT;
- lex->var_list.push_back(var);
- }
- | PASSWORD equal text_or_password
- {
- THD *thd=YYTHD;
- LEX_USER *user;
- LEX *lex= Lex;
+ }
+ | NAMES_SYM charset_name_or_default opt_collate
+ {
+ LEX *lex= Lex;
+ CHARSET_INFO *cs2;
+ CHARSET_INFO *cs3;
+ cs2= $2 ? $2 : global_system_variables.character_set_client;
+ cs3= $3 ? $3 : cs2;
+ if (!my_charset_same(cs2, cs3))
+ {
+ my_error(ER_COLLATION_CHARSET_MISMATCH, MYF(0),
+ cs3->name, cs2->csname);
+ MYSQL_YYABORT;
+ }
+ set_var_collation_client *var;
+ var= new set_var_collation_client(cs3, cs3, cs3);
+ if (var == NULL)
+ MYSQL_YYABORT;
+ lex->var_list.push_back(var);
+ }
+ | PASSWORD equal text_or_password
+ {
+ THD *thd= YYTHD;
+ LEX *lex= thd->lex;
+ LEX_USER *user;
sp_pcontext *spc= lex->spcont;
- LEX_STRING pw;
+ LEX_STRING pw;
- pw.str= (char *)"password";
- pw.length= 8;
- if (spc && spc->find_variable(&pw))
- {
+ pw.str= (char *)"password";
+ pw.length= 8;
+ if (spc && spc->find_variable(&pw))
+ {
my_error(ER_SP_BAD_VAR_SHADOW, MYF(0), pw.str);
- MYSQL_YYABORT;
- }
- if (!(user=(LEX_USER*) thd->alloc(sizeof(LEX_USER))))
- MYSQL_YYABORT;
- user->host=null_lex_str;
- user->user.str=thd->security_ctx->priv_user;
+ MYSQL_YYABORT;
+ }
+ if (!(user=(LEX_USER*) thd->alloc(sizeof(LEX_USER))))
+ MYSQL_YYABORT;
+ user->host=null_lex_str;
+ user->user.str=thd->security_ctx->priv_user;
set_var_password *var= new set_var_password(user, $3);
if (var == NULL)
MYSQL_YYABORT;
- thd->lex->var_list.push_back(var);
- }
- | PASSWORD FOR_SYM user equal text_or_password
- {
+ thd->lex->var_list.push_back(var);
+ thd->lex->autocommit= TRUE;
+ if (lex->sphead)
+ lex->sphead->m_flags|= sp_head::HAS_SET_AUTOCOMMIT_STMT;
+ }
+ | PASSWORD FOR_SYM user equal text_or_password
+ {
set_var_password *var= new set_var_password($3,$5);
if (var == NULL)
MYSQL_YYABORT;
- Lex->var_list.push_back(var);
- }
- ;
+ Lex->var_list.push_back(var);
+ Lex->autocommit= TRUE;
+ if (Lex->sphead)
+ Lex->sphead->m_flags|= sp_head::HAS_SET_AUTOCOMMIT_STMT;
+ }
+ ;
internal_variable_name:
- ident
- {
- LEX *lex= Lex;
- sp_pcontext *spc= lex->spcont;
- sp_variable_t *spv;
-
- /* We have to lookup here since local vars can shadow sysvars */
- if (!spc || !(spv = spc->find_variable(&$1)))
- {
- /* Not an SP local variable */
- sys_var *tmp=find_sys_var($1.str, $1.length);
- if (!tmp)
- MYSQL_YYABORT;
- $$.var= tmp;
- $$.base_name= null_lex_str;
- /*
- If this is time_zone variable we should open time zone
- describing tables
- */
- if (tmp == &sys_time_zone &&
- lex->add_time_zone_tables_to_query_tables(YYTHD))
- MYSQL_YYABORT;
- else if (spc && tmp == &sys_autocommit)
+ ident
+ {
+ THD *thd= YYTHD;
+ LEX *lex= thd->lex;
+ sp_pcontext *spc= lex->spcont;
+ sp_variable_t *spv;
+
+ /* We have to lookup here since local vars can shadow sysvars */
+ if (!spc || !(spv = spc->find_variable(&$1)))
{
- /*
- We don't allow setting AUTOCOMMIT from a stored function
- or trigger.
- */
- lex->sphead->m_flags|= sp_head::HAS_SET_AUTOCOMMIT_STMT;
+ /* Not an SP local variable */
+ sys_var *tmp=find_sys_var(thd, $1.str, $1.length);
+ if (!tmp)
+ MYSQL_YYABORT;
+ $$.var= tmp;
+ $$.base_name= null_lex_str;
+ if (spc && tmp == &sys_autocommit)
+ {
+ /*
+ We don't allow setting AUTOCOMMIT from a stored function
+ or trigger.
+ */
+ lex->sphead->m_flags|= sp_head::HAS_SET_AUTOCOMMIT_STMT;
+ }
+ }
+ else
+ {
+ /* An SP local variable */
+ $$.var= NULL;
+ $$.base_name= $1;
}
- }
- else
- {
- /* An SP local variable */
- $$.var= NULL;
- $$.base_name= $1;
- }
- }
- | ident '.' ident
- {
+ }
+ | ident '.' ident
+ {
LEX *lex= Lex;
if (check_reserved_words(&$1))
{
@@ -9945,12 +12065,12 @@ internal_variable_name:
MYSQL_YYABORT;
}
/* This special combination will denote field of NEW row */
- $$.var= &trg_new_row_fake_var;
+ $$.var= trg_new_row_fake_var;
$$.base_name= $3;
}
else
{
- sys_var *tmp=find_sys_var($3.str, $3.length);
+ sys_var *tmp=find_sys_var(YYTHD, $3.str, $3.length);
if (!tmp)
MYSQL_YYABORT;
if (!tmp->is_struct())
@@ -9958,546 +12078,564 @@ internal_variable_name:
$$.var= tmp;
$$.base_name= $1;
}
- }
- | DEFAULT '.' ident
- {
- sys_var *tmp=find_sys_var($3.str, $3.length);
- if (!tmp)
- MYSQL_YYABORT;
- if (!tmp->is_struct())
- my_error(ER_VARIABLE_IS_NOT_STRUCT, MYF(0), $3.str);
- $$.var= tmp;
- $$.base_name.str= (char*) "default";
- $$.base_name.length= 7;
- }
+ }
+ | DEFAULT '.' ident
+ {
+ sys_var *tmp=find_sys_var(YYTHD, $3.str, $3.length);
+ if (!tmp)
+ MYSQL_YYABORT;
+ if (!tmp->is_struct())
+ my_error(ER_VARIABLE_IS_NOT_STRUCT, MYF(0), $3.str);
+ $$.var= tmp;
+ $$.base_name.str= (char*) "default";
+ $$.base_name.length= 7;
+ }
;
isolation_types:
- READ_SYM UNCOMMITTED_SYM { $$= ISO_READ_UNCOMMITTED; }
- | READ_SYM COMMITTED_SYM { $$= ISO_READ_COMMITTED; }
- | REPEATABLE_SYM READ_SYM { $$= ISO_REPEATABLE_READ; }
- | SERIALIZABLE_SYM { $$= ISO_SERIALIZABLE; }
- ;
+ READ_SYM UNCOMMITTED_SYM { $$= ISO_READ_UNCOMMITTED; }
+ | READ_SYM COMMITTED_SYM { $$= ISO_READ_COMMITTED; }
+ | REPEATABLE_SYM READ_SYM { $$= ISO_REPEATABLE_READ; }
+ | SERIALIZABLE_SYM { $$= ISO_SERIALIZABLE; }
+ ;
text_or_password:
- TEXT_STRING { $$=$1.str;}
- | PASSWORD '(' TEXT_STRING ')'
- {
- $$= $3.length ? YYTHD->variables.old_passwords ?
- Item_func_old_password::alloc(YYTHD, $3.str) :
- Item_func_password::alloc(YYTHD, $3.str) :
- $3.str;
+ TEXT_STRING { $$=$1.str;}
+ | PASSWORD '(' TEXT_STRING ')'
+ {
+ $$= $3.length ? YYTHD->variables.old_passwords ?
+ Item_func_old_password::alloc(YYTHD, $3.str) :
+ Item_func_password::alloc(YYTHD, $3.str) :
+ $3.str;
if ($$ == NULL)
MYSQL_YYABORT;
- }
- | OLD_PASSWORD '(' TEXT_STRING ')'
- {
- $$= $3.length ? Item_func_old_password::alloc(YYTHD, $3.str) :
- $3.str;
+ }
+ | OLD_PASSWORD '(' TEXT_STRING ')'
+ {
+ $$= $3.length ? Item_func_old_password::alloc(YYTHD, $3.str) :
+ $3.str;
if ($$ == NULL)
MYSQL_YYABORT;
- }
- ;
+ }
+ ;
set_expr_or_default:
- expr { $$=$1; }
- | DEFAULT { $$=0; }
- | ON
+ expr { $$=$1; }
+ | DEFAULT { $$=0; }
+ | ON
{
- $$=new Item_string("ON", 2, system_charset_info);
+ $$=new (YYTHD->mem_root) Item_string("ON", 2, system_charset_info);
if ($$ == NULL)
MYSQL_YYABORT;
}
- | ALL
+ | ALL
{
- $$=new Item_string("ALL", 3, system_charset_info);
+ $$=new (YYTHD->mem_root) Item_string("ALL", 3, system_charset_info);
if ($$ == NULL)
MYSQL_YYABORT;
}
- | BINARY
+ | BINARY
{
- $$=new Item_string("binary", 6, system_charset_info);
+ $$=new (YYTHD->mem_root) Item_string("binary", 6, system_charset_info);
if ($$ == NULL)
MYSQL_YYABORT;
}
- ;
-
+ ;
/* Lock function */
lock:
- LOCK_SYM table_or_tables
- {
- LEX *lex= Lex;
-
- if (lex->sphead)
- {
- my_error(ER_SP_BADSTATEMENT, MYF(0), "LOCK");
- MYSQL_YYABORT;
- }
- lex->sql_command= SQLCOM_LOCK_TABLES;
- }
- table_lock_list
- {}
- ;
+ LOCK_SYM table_or_tables
+ {
+ LEX *lex= Lex;
+
+ if (lex->sphead)
+ {
+ my_error(ER_SP_BADSTATEMENT, MYF(0), "LOCK");
+ MYSQL_YYABORT;
+ }
+ lex->sql_command= SQLCOM_LOCK_TABLES;
+ }
+ table_lock_list
+ {}
+ ;
table_or_tables:
- TABLE_SYM
- | TABLES;
+ TABLE_SYM
+ | TABLES
+ ;
table_lock_list:
- table_lock
- | table_lock_list ',' table_lock;
+ table_lock
+ | table_lock_list ',' table_lock
+ ;
table_lock:
- table_ident opt_table_alias lock_option
- {
- if (!Select->add_table_to_list(YYTHD, $1, $2, 0, (thr_lock_type) $3))
- MYSQL_YYABORT;
- }
+ table_ident opt_table_alias lock_option
+ {
+ if (!Select->add_table_to_list(YYTHD, $1, $2, 0, (thr_lock_type) $3))
+ MYSQL_YYABORT;
+ }
;
lock_option:
- READ_SYM { $$=TL_READ_NO_INSERT; }
- | WRITE_SYM { $$=TL_WRITE_DEFAULT; }
- | LOW_PRIORITY WRITE_SYM { $$=TL_WRITE_LOW_PRIORITY; }
- | READ_SYM LOCAL_SYM { $$= TL_READ; }
+ READ_SYM { $$= TL_READ_NO_INSERT; }
+ | WRITE_SYM { $$= TL_WRITE_DEFAULT; }
+ | LOW_PRIORITY WRITE_SYM { $$= TL_WRITE_LOW_PRIORITY; }
+ | READ_SYM LOCAL_SYM { $$= TL_READ; }
;
unlock:
- UNLOCK_SYM
- {
- LEX *lex= Lex;
+ UNLOCK_SYM
+ {
+ LEX *lex= Lex;
- if (lex->sphead)
- {
- my_error(ER_SP_BADSTATEMENT, MYF(0), "UNLOCK");
- MYSQL_YYABORT;
- }
- lex->sql_command= SQLCOM_UNLOCK_TABLES;
- }
- table_or_tables
- {}
+ if (lex->sphead)
+ {
+ my_error(ER_SP_BADSTATEMENT, MYF(0), "UNLOCK");
+ MYSQL_YYABORT;
+ }
+ lex->sql_command= SQLCOM_UNLOCK_TABLES;
+ }
+ table_or_tables
+ {}
;
-
/*
** Handler: direct access to ISAM functions
*/
handler:
- HANDLER_SYM table_ident OPEN_SYM opt_table_alias
- {
- LEX *lex= Lex;
- if (lex->sphead)
- {
- my_error(ER_SP_BADSTATEMENT, MYF(0), "HANDLER");
- MYSQL_YYABORT;
- }
- lex->sql_command = SQLCOM_HA_OPEN;
- if (!lex->current_select->add_table_to_list(lex->thd, $2, $4, 0))
- MYSQL_YYABORT;
- }
- | HANDLER_SYM table_ident_nodb CLOSE_SYM
- {
- LEX *lex= Lex;
- if (lex->sphead)
- {
- my_error(ER_SP_BADSTATEMENT, MYF(0), "HANDLER");
- MYSQL_YYABORT;
- }
- lex->sql_command = SQLCOM_HA_CLOSE;
- if (!lex->current_select->add_table_to_list(lex->thd, $2, 0, 0))
- MYSQL_YYABORT;
- }
- | HANDLER_SYM table_ident_nodb READ_SYM
- {
- LEX *lex=Lex;
- if (lex->sphead)
- {
- my_error(ER_SP_BADSTATEMENT, MYF(0), "HANDLER");
- MYSQL_YYABORT;
- }
- lex->sql_command = SQLCOM_HA_READ;
- lex->ha_rkey_mode= HA_READ_KEY_EXACT; /* Avoid purify warnings */
- Item *one= new Item_int((int32) 1);
- if (one == NULL)
- MYSQL_YYABORT;
- lex->current_select->select_limit= one;
- lex->current_select->offset_limit= 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 {}
+ HANDLER_SYM table_ident OPEN_SYM opt_table_alias
+ {
+ LEX *lex= Lex;
+ if (lex->sphead)
+ {
+ my_error(ER_SP_BADSTATEMENT, MYF(0), "HANDLER");
+ MYSQL_YYABORT;
+ }
+ lex->sql_command = SQLCOM_HA_OPEN;
+ if (!lex->current_select->add_table_to_list(lex->thd, $2, $4, 0))
+ MYSQL_YYABORT;
+ }
+ | HANDLER_SYM table_ident_nodb CLOSE_SYM
+ {
+ LEX *lex= Lex;
+ if (lex->sphead)
+ {
+ my_error(ER_SP_BADSTATEMENT, MYF(0), "HANDLER");
+ MYSQL_YYABORT;
+ }
+ lex->sql_command = SQLCOM_HA_CLOSE;
+ if (!lex->current_select->add_table_to_list(lex->thd, $2, 0, 0))
+ MYSQL_YYABORT;
+ }
+ | HANDLER_SYM table_ident_nodb READ_SYM
+ {
+ LEX *lex=Lex;
+ if (lex->sphead)
+ {
+ my_error(ER_SP_BADSTATEMENT, MYF(0), "HANDLER");
+ MYSQL_YYABORT;
+ }
+ 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);
+ if (one == NULL)
+ MYSQL_YYABORT;
+ lex->current_select->select_limit= one;
+ lex->current_select->offset_limit= 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;
+ }
;
handler_read_or_scan:
- handler_scan_function { Lex->ident= null_lex_str; }
+ handler_scan_function { Lex->ident= null_lex_str; }
| ident handler_rkey_function { Lex->ident= $1; }
;
handler_scan_function:
- FIRST_SYM { Lex->ha_read_mode = RFIRST; }
- | NEXT_SYM { Lex->ha_read_mode = RNEXT; }
+ FIRST_SYM { Lex->ha_read_mode = RFIRST; }
+ | NEXT_SYM { Lex->ha_read_mode = RNEXT; }
;
handler_rkey_function:
- FIRST_SYM { Lex->ha_read_mode = RFIRST; }
- | NEXT_SYM { Lex->ha_read_mode = RNEXT; }
- | PREV_SYM { Lex->ha_read_mode = RPREV; }
- | LAST_SYM { Lex->ha_read_mode = RLAST; }
- | handler_rkey_mode
- {
- LEX *lex=Lex;
- lex->ha_read_mode = RKEY;
- lex->ha_rkey_mode=$1;
- if (!(lex->insert_list = new List_item))
- MYSQL_YYABORT;
- } '(' values ')' { }
+ FIRST_SYM { Lex->ha_read_mode = RFIRST; }
+ | NEXT_SYM { Lex->ha_read_mode = RNEXT; }
+ | PREV_SYM { Lex->ha_read_mode = RPREV; }
+ | LAST_SYM { Lex->ha_read_mode = RLAST; }
+ | handler_rkey_mode
+ {
+ LEX *lex=Lex;
+ lex->ha_read_mode = RKEY;
+ lex->ha_rkey_mode=$1;
+ if (!(lex->insert_list = new List_item))
+ MYSQL_YYABORT;
+ }
+ '(' values ')'
+ {}
;
handler_rkey_mode:
- EQ { $$=HA_READ_KEY_EXACT; }
- | GE { $$=HA_READ_KEY_OR_NEXT; }
- | LE { $$=HA_READ_KEY_OR_PREV; }
- | GT_SYM { $$=HA_READ_AFTER_KEY; }
- | LT { $$=HA_READ_BEFORE_KEY; }
+ EQ { $$=HA_READ_KEY_EXACT; }
+ | GE { $$=HA_READ_KEY_OR_NEXT; }
+ | LE { $$=HA_READ_KEY_OR_PREV; }
+ | GT_SYM { $$=HA_READ_AFTER_KEY; }
+ | LT { $$=HA_READ_BEFORE_KEY; }
;
/* GRANT / REVOKE */
revoke:
- REVOKE clear_privileges revoke_command
- {}
+ REVOKE clear_privileges revoke_command
+ {}
;
revoke_command:
- grant_privileges ON opt_table grant_ident FROM grant_list
- {
- LEX *lex= Lex;
- lex->sql_command= SQLCOM_REVOKE;
- lex->type= 0;
- }
- |
- grant_privileges ON FUNCTION_SYM grant_ident FROM grant_list
- {
- LEX *lex= Lex;
- if (lex->columns.elements)
+ grant_privileges ON opt_table grant_ident FROM grant_list
{
- my_parse_error(ER(ER_SYNTAX_ERROR));
- MYSQL_YYABORT;
+ LEX *lex= Lex;
+ lex->sql_command= SQLCOM_REVOKE;
+ lex->type= 0;
}
- lex->sql_command= SQLCOM_REVOKE;
- lex->type= TYPE_ENUM_FUNCTION;
-
- }
- |
- grant_privileges ON PROCEDURE grant_ident FROM grant_list
- {
- LEX *lex= Lex;
- if (lex->columns.elements)
+ | grant_privileges ON FUNCTION_SYM grant_ident FROM grant_list
{
- my_parse_error(ER(ER_SYNTAX_ERROR));
- MYSQL_YYABORT;
+ LEX *lex= Lex;
+ if (lex->columns.elements)
+ {
+ my_parse_error(ER(ER_SYNTAX_ERROR));
+ MYSQL_YYABORT;
+ }
+ lex->sql_command= SQLCOM_REVOKE;
+ lex->type= TYPE_ENUM_FUNCTION;
}
- lex->sql_command= SQLCOM_REVOKE;
- lex->type= TYPE_ENUM_PROCEDURE;
- }
- |
- ALL opt_privileges ',' GRANT OPTION FROM grant_list
- {
- Lex->sql_command = SQLCOM_REVOKE_ALL;
- }
- ;
+ | grant_privileges ON PROCEDURE grant_ident FROM grant_list
+ {
+ LEX *lex= Lex;
+ if (lex->columns.elements)
+ {
+ my_parse_error(ER(ER_SYNTAX_ERROR));
+ MYSQL_YYABORT;
+ }
+ lex->sql_command= SQLCOM_REVOKE;
+ lex->type= TYPE_ENUM_PROCEDURE;
+ }
+ | ALL opt_privileges ',' GRANT OPTION FROM grant_list
+ {
+ Lex->sql_command = SQLCOM_REVOKE_ALL;
+ }
+ ;
grant:
- GRANT clear_privileges grant_command
- {}
+ GRANT clear_privileges grant_command
+ {}
;
grant_command:
- grant_privileges ON opt_table grant_ident TO_SYM grant_list
- require_clause grant_options
- {
- LEX *lex= Lex;
- lex->sql_command= SQLCOM_GRANT;
- lex->type= 0;
- }
- |
- grant_privileges ON FUNCTION_SYM grant_ident TO_SYM grant_list
- require_clause grant_options
- {
- LEX *lex= Lex;
- if (lex->columns.elements)
+ grant_privileges ON opt_table grant_ident TO_SYM grant_list
+ require_clause grant_options
{
- my_parse_error(ER(ER_SYNTAX_ERROR));
- MYSQL_YYABORT;
+ LEX *lex= Lex;
+ lex->sql_command= SQLCOM_GRANT;
+ lex->type= 0;
}
- lex->sql_command= SQLCOM_GRANT;
- lex->type= TYPE_ENUM_FUNCTION;
- }
- |
- grant_privileges ON PROCEDURE grant_ident TO_SYM grant_list
- require_clause grant_options
- {
- LEX *lex= Lex;
- if (lex->columns.elements)
+ | grant_privileges ON FUNCTION_SYM grant_ident TO_SYM grant_list
+ require_clause grant_options
{
- my_parse_error(ER(ER_SYNTAX_ERROR));
- MYSQL_YYABORT;
+ LEX *lex= Lex;
+ if (lex->columns.elements)
+ {
+ my_parse_error(ER(ER_SYNTAX_ERROR));
+ MYSQL_YYABORT;
+ }
+ lex->sql_command= SQLCOM_GRANT;
+ lex->type= TYPE_ENUM_FUNCTION;
+ }
+ | grant_privileges ON PROCEDURE grant_ident TO_SYM grant_list
+ require_clause grant_options
+ {
+ LEX *lex= Lex;
+ if (lex->columns.elements)
+ {
+ my_parse_error(ER(ER_SYNTAX_ERROR));
+ MYSQL_YYABORT;
+ }
+ lex->sql_command= SQLCOM_GRANT;
+ lex->type= TYPE_ENUM_PROCEDURE;
}
- lex->sql_command= SQLCOM_GRANT;
- lex->type= TYPE_ENUM_PROCEDURE;
- }
;
opt_table:
- /* Empty */
- | TABLE_SYM ;
-
+ /* Empty */
+ | TABLE_SYM
+ ;
+
grant_privileges:
- object_privilege_list { }
- | ALL opt_privileges
- {
- Lex->all_privileges= 1;
- Lex->grant= GLOBAL_ACLS;
- }
+ object_privilege_list {}
+ | ALL opt_privileges
+ {
+ Lex->all_privileges= 1;
+ Lex->grant= GLOBAL_ACLS;
+ }
;
opt_privileges:
- /* empty */
- | PRIVILEGES
- ;
+ /* empty */
+ | PRIVILEGES
+ ;
object_privilege_list:
- object_privilege
- | object_privilege_list ',' object_privilege;
+ object_privilege
+ | object_privilege_list ',' object_privilege
+ ;
object_privilege:
- SELECT_SYM { Lex->which_columns = SELECT_ACL;} opt_column_list {}
- | INSERT { Lex->which_columns = INSERT_ACL;} opt_column_list {}
- | UPDATE_SYM { Lex->which_columns = UPDATE_ACL; } opt_column_list {}
- | REFERENCES { Lex->which_columns = REFERENCES_ACL;} opt_column_list {}
- | DELETE_SYM { Lex->grant |= DELETE_ACL;}
- | USAGE {}
- | INDEX_SYM { Lex->grant |= INDEX_ACL;}
- | ALTER { Lex->grant |= ALTER_ACL;}
- | CREATE { Lex->grant |= CREATE_ACL;}
- | DROP { Lex->grant |= DROP_ACL;}
- | EXECUTE_SYM { Lex->grant |= EXECUTE_ACL;}
- | RELOAD { Lex->grant |= RELOAD_ACL;}
- | SHUTDOWN { Lex->grant |= SHUTDOWN_ACL;}
- | PROCESS { Lex->grant |= PROCESS_ACL;}
- | FILE_SYM { Lex->grant |= FILE_ACL;}
- | GRANT OPTION { Lex->grant |= GRANT_ACL;}
- | SHOW DATABASES { Lex->grant |= SHOW_DB_ACL;}
- | SUPER_SYM { Lex->grant |= SUPER_ACL;}
- | CREATE TEMPORARY TABLES { Lex->grant |= CREATE_TMP_ACL;}
- | LOCK_SYM TABLES { Lex->grant |= LOCK_TABLES_ACL; }
- | REPLICATION SLAVE { Lex->grant |= REPL_SLAVE_ACL; }
- | REPLICATION CLIENT_SYM { Lex->grant |= REPL_CLIENT_ACL; }
- | CREATE VIEW_SYM { Lex->grant |= CREATE_VIEW_ACL; }
- | SHOW VIEW_SYM { Lex->grant |= SHOW_VIEW_ACL; }
- | CREATE ROUTINE_SYM { Lex->grant |= CREATE_PROC_ACL; }
- | ALTER ROUTINE_SYM { Lex->grant |= ALTER_PROC_ACL; }
- | CREATE USER { Lex->grant |= CREATE_USER_ACL; }
- ;
-
+ SELECT_SYM
+ { Lex->which_columns = SELECT_ACL;}
+ opt_column_list {}
+ | INSERT
+ { Lex->which_columns = INSERT_ACL;}
+ opt_column_list {}
+ | UPDATE_SYM
+ { Lex->which_columns = UPDATE_ACL; }
+ opt_column_list {}
+ | REFERENCES
+ { Lex->which_columns = REFERENCES_ACL;}
+ opt_column_list {}
+ | DELETE_SYM { Lex->grant |= DELETE_ACL;}
+ | USAGE {}
+ | INDEX_SYM { Lex->grant |= INDEX_ACL;}
+ | ALTER { Lex->grant |= ALTER_ACL;}
+ | CREATE { Lex->grant |= CREATE_ACL;}
+ | DROP { Lex->grant |= DROP_ACL;}
+ | EXECUTE_SYM { Lex->grant |= EXECUTE_ACL;}
+ | RELOAD { Lex->grant |= RELOAD_ACL;}
+ | SHUTDOWN { Lex->grant |= SHUTDOWN_ACL;}
+ | PROCESS { Lex->grant |= PROCESS_ACL;}
+ | FILE_SYM { Lex->grant |= FILE_ACL;}
+ | GRANT OPTION { Lex->grant |= GRANT_ACL;}
+ | SHOW DATABASES { Lex->grant |= SHOW_DB_ACL;}
+ | SUPER_SYM { Lex->grant |= SUPER_ACL;}
+ | CREATE TEMPORARY TABLES { Lex->grant |= CREATE_TMP_ACL;}
+ | LOCK_SYM TABLES { Lex->grant |= LOCK_TABLES_ACL; }
+ | REPLICATION SLAVE { Lex->grant |= REPL_SLAVE_ACL; }
+ | REPLICATION CLIENT_SYM { Lex->grant |= REPL_CLIENT_ACL; }
+ | CREATE VIEW_SYM { Lex->grant |= CREATE_VIEW_ACL; }
+ | SHOW VIEW_SYM { Lex->grant |= SHOW_VIEW_ACL; }
+ | CREATE ROUTINE_SYM { Lex->grant |= CREATE_PROC_ACL; }
+ | ALTER ROUTINE_SYM { Lex->grant |= ALTER_PROC_ACL; }
+ | CREATE USER { Lex->grant |= CREATE_USER_ACL; }
+ | EVENT_SYM { Lex->grant |= EVENT_ACL;}
+ | TRIGGER_SYM { Lex->grant |= TRIGGER_ACL; }
+ ;
opt_and:
- /* empty */ {}
- | AND_SYM {}
- ;
+ /* empty */ {}
+ | AND_SYM {}
+ ;
require_list:
- require_list_element opt_and require_list
- | require_list_element
- ;
+ require_list_element opt_and require_list
+ | require_list_element
+ ;
require_list_element:
- SUBJECT_SYM TEXT_STRING
- {
- LEX *lex=Lex;
- if (lex->x509_subject)
- {
- my_error(ER_DUP_ARGUMENT, MYF(0), "SUBJECT");
- MYSQL_YYABORT;
- }
- lex->x509_subject=$2.str;
- }
- | ISSUER_SYM TEXT_STRING
- {
- LEX *lex=Lex;
- if (lex->x509_issuer)
- {
- my_error(ER_DUP_ARGUMENT, MYF(0), "ISSUER");
- MYSQL_YYABORT;
- }
- lex->x509_issuer=$2.str;
- }
- | CIPHER_SYM TEXT_STRING
- {
- LEX *lex=Lex;
- if (lex->ssl_cipher)
- {
- my_error(ER_DUP_ARGUMENT, MYF(0), "CIPHER");
- MYSQL_YYABORT;
- }
- lex->ssl_cipher=$2.str;
- }
- ;
+ SUBJECT_SYM TEXT_STRING
+ {
+ LEX *lex=Lex;
+ if (lex->x509_subject)
+ {
+ my_error(ER_DUP_ARGUMENT, MYF(0), "SUBJECT");
+ MYSQL_YYABORT;
+ }
+ lex->x509_subject=$2.str;
+ }
+ | ISSUER_SYM TEXT_STRING
+ {
+ LEX *lex=Lex;
+ if (lex->x509_issuer)
+ {
+ my_error(ER_DUP_ARGUMENT, MYF(0), "ISSUER");
+ MYSQL_YYABORT;
+ }
+ lex->x509_issuer=$2.str;
+ }
+ | CIPHER_SYM TEXT_STRING
+ {
+ LEX *lex=Lex;
+ if (lex->ssl_cipher)
+ {
+ my_error(ER_DUP_ARGUMENT, MYF(0), "CIPHER");
+ MYSQL_YYABORT;
+ }
+ lex->ssl_cipher=$2.str;
+ }
+ ;
grant_ident:
- '*'
- {
- LEX *lex= Lex;
- if (lex->copy_db_to(&lex->current_select->db, NULL))
- MYSQL_YYABORT;
- if (lex->grant == GLOBAL_ACLS)
- lex->grant = DB_ACLS & ~GRANT_ACL;
- else if (lex->columns.elements)
- {
- my_message(ER_ILLEGAL_GRANT_FOR_TABLE,
+ '*'
+ {
+ LEX *lex= Lex;
+ size_t dummy;
+ if (lex->copy_db_to(&lex->current_select->db, &dummy))
+ MYSQL_YYABORT;
+ if (lex->grant == GLOBAL_ACLS)
+ lex->grant = DB_ACLS & ~GRANT_ACL;
+ else if (lex->columns.elements)
+ {
+ my_message(ER_ILLEGAL_GRANT_FOR_TABLE,
ER(ER_ILLEGAL_GRANT_FOR_TABLE), MYF(0));
- MYSQL_YYABORT;
- }
- }
- | ident '.' '*'
- {
- LEX *lex= Lex;
- lex->current_select->db = $1.str;
- if (lex->grant == GLOBAL_ACLS)
- lex->grant = DB_ACLS & ~GRANT_ACL;
- else if (lex->columns.elements)
- {
- my_message(ER_ILLEGAL_GRANT_FOR_TABLE,
+ MYSQL_YYABORT;
+ }
+ }
+ | ident '.' '*'
+ {
+ LEX *lex= Lex;
+ lex->current_select->db = $1.str;
+ if (lex->grant == GLOBAL_ACLS)
+ lex->grant = DB_ACLS & ~GRANT_ACL;
+ else if (lex->columns.elements)
+ {
+ my_message(ER_ILLEGAL_GRANT_FOR_TABLE,
ER(ER_ILLEGAL_GRANT_FOR_TABLE), MYF(0));
- MYSQL_YYABORT;
- }
- }
- | '*' '.' '*'
- {
- LEX *lex= Lex;
- lex->current_select->db = NULL;
- if (lex->grant == GLOBAL_ACLS)
- lex->grant= GLOBAL_ACLS & ~GRANT_ACL;
- else if (lex->columns.elements)
- {
- my_message(ER_ILLEGAL_GRANT_FOR_TABLE,
+ MYSQL_YYABORT;
+ }
+ }
+ | '*' '.' '*'
+ {
+ LEX *lex= Lex;
+ lex->current_select->db = NULL;
+ if (lex->grant == GLOBAL_ACLS)
+ lex->grant= GLOBAL_ACLS & ~GRANT_ACL;
+ else if (lex->columns.elements)
+ {
+ my_message(ER_ILLEGAL_GRANT_FOR_TABLE,
ER(ER_ILLEGAL_GRANT_FOR_TABLE), MYF(0));
- MYSQL_YYABORT;
- }
- }
- | table_ident
- {
- LEX *lex=Lex;
- if (!lex->current_select->add_table_to_list(lex->thd, $1,NULL,
+ MYSQL_YYABORT;
+ }
+ }
+ | table_ident
+ {
+ LEX *lex=Lex;
+ if (!lex->current_select->add_table_to_list(lex->thd, $1,NULL,
TL_OPTION_UPDATING))
- MYSQL_YYABORT;
- if (lex->grant == GLOBAL_ACLS)
- lex->grant = TABLE_ACLS & ~GRANT_ACL;
- }
- ;
-
+ MYSQL_YYABORT;
+ if (lex->grant == GLOBAL_ACLS)
+ lex->grant = TABLE_ACLS & ~GRANT_ACL;
+ }
+ ;
user_list:
- user { if (Lex->users_list.push_back($1)) MYSQL_YYABORT;}
- | user_list ',' user
- {
- if (Lex->users_list.push_back($3))
- MYSQL_YYABORT;
- }
- ;
-
+ user
+ {
+ if (Lex->users_list.push_back($1))
+ MYSQL_YYABORT;
+ }
+ | user_list ',' user
+ {
+ if (Lex->users_list.push_back($3))
+ MYSQL_YYABORT;
+ }
+ ;
grant_list:
- grant_user { if (Lex->users_list.push_back($1)) MYSQL_YYABORT;}
- | grant_list ',' grant_user
- {
- if (Lex->users_list.push_back($3))
- MYSQL_YYABORT;
- }
- ;
-
+ grant_user
+ {
+ if (Lex->users_list.push_back($1))
+ MYSQL_YYABORT;
+ }
+ | grant_list ',' grant_user
+ {
+ if (Lex->users_list.push_back($3))
+ MYSQL_YYABORT;
+ }
+ ;
grant_user:
- user IDENTIFIED_SYM BY TEXT_STRING
- {
- $$=$1; $1->password=$4;
- if ($4.length)
- {
- if (YYTHD->variables.old_passwords)
- {
- char *buff=
- (char *) YYTHD->alloc(SCRAMBLED_PASSWORD_CHAR_LENGTH_323+1);
- if (buff == NULL)
- MYSQL_YYABORT;
- make_scrambled_password_323(buff, $4.str);
- $1->password.str= buff;
- $1->password.length= SCRAMBLED_PASSWORD_CHAR_LENGTH_323;
- }
- else
- {
- char *buff=
- (char *) YYTHD->alloc(SCRAMBLED_PASSWORD_CHAR_LENGTH+1);
- if (buff == NULL)
- MYSQL_YYABORT;
- make_scrambled_password(buff, $4.str);
- $1->password.str= buff;
- $1->password.length= SCRAMBLED_PASSWORD_CHAR_LENGTH;
- }
- }
- }
- | user IDENTIFIED_SYM BY PASSWORD TEXT_STRING
- { $$= $1; $1->password= $5; }
- | user
- { $$= $1; $1->password= null_lex_str; }
+ user IDENTIFIED_SYM BY TEXT_STRING
+ {
+ $$=$1; $1->password=$4;
+ if ($4.length)
+ {
+ if (YYTHD->variables.old_passwords)
+ {
+ char *buff=
+ (char *) YYTHD->alloc(SCRAMBLED_PASSWORD_CHAR_LENGTH_323+1);
+ if (buff == NULL)
+ MYSQL_YYABORT;
+ make_scrambled_password_323(buff, $4.str);
+ $1->password.str= buff;
+ $1->password.length= SCRAMBLED_PASSWORD_CHAR_LENGTH_323;
+ }
+ else
+ {
+ char *buff=
+ (char *) YYTHD->alloc(SCRAMBLED_PASSWORD_CHAR_LENGTH+1);
+ if (buff == NULL)
+ MYSQL_YYABORT;
+ make_scrambled_password(buff, $4.str);
+ $1->password.str= buff;
+ $1->password.length= SCRAMBLED_PASSWORD_CHAR_LENGTH;
+ }
+ }
+ }
+ | user IDENTIFIED_SYM BY PASSWORD TEXT_STRING
+ { $$= $1; $1->password= $5; }
+ | user
+ { $$= $1; $1->password= null_lex_str; }
;
-
opt_column_list:
- /* empty */
- {
- LEX *lex=Lex;
- lex->grant |= lex->which_columns;
- }
- | '(' column_list ')';
+ /* empty */
+ {
+ LEX *lex=Lex;
+ lex->grant |= lex->which_columns;
+ }
+ | '(' column_list ')'
+ ;
column_list:
- column_list ',' column_list_id
- | column_list_id;
+ column_list ',' column_list_id
+ | column_list_id
+ ;
column_list_id:
- ident
- {
- String *new_str = new (YYTHD->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);
- class LEX_COLUMN *point;
- LEX *lex=Lex;
- while ((point=iter++))
- {
- if (!my_strcasecmp(system_charset_info,
- point->column.ptr(), new_str->ptr()))
- break;
- }
- lex->grant_tot_col|= lex->which_columns;
- if (point)
- point->rights |= lex->which_columns;
- else
+ ident
{
- LEX_COLUMN *col= new LEX_COLUMN (*new_str,lex->which_columns);
- if (col == NULL)
+ String *new_str = new (YYTHD->mem_root) String((const char*) $1.str,$1.length,system_charset_info);
+ if (new_str == NULL)
MYSQL_YYABORT;
- lex->columns.push_back(col);
+ List_iterator <LEX_COLUMN> iter(Lex->columns);
+ class LEX_COLUMN *point;
+ LEX *lex=Lex;
+ while ((point=iter++))
+ {
+ if (!my_strcasecmp(system_charset_info,
+ point->column.ptr(), new_str->ptr()))
+ break;
+ }
+ lex->grant_tot_col|= lex->which_columns;
+ if (point)
+ point->rights |= lex->which_columns;
+ else
+ {
+ LEX_COLUMN *col= new LEX_COLUMN (*new_str,lex->which_columns);
+ if (col == NULL)
+ MYSQL_YYABORT;
+ lex->columns.push_back(col);
+ }
}
- }
;
-
-require_clause: /* empty */
+require_clause:
+ /* empty */
| REQUIRE_SYM require_list
{
Lex->ssl_type=SSL_TYPE_SPECIFIED;
@@ -10510,216 +12648,219 @@ require_clause: /* empty */
{
Lex->ssl_type=SSL_TYPE_X509;
}
- | REQUIRE_SYM NONE_SYM
- {
- Lex->ssl_type=SSL_TYPE_NONE;
- }
- ;
+ | REQUIRE_SYM NONE_SYM
+ {
+ Lex->ssl_type=SSL_TYPE_NONE;
+ }
+ ;
grant_options:
- /* empty */ {}
- | WITH grant_option_list;
+ /* empty */ {}
+ | WITH grant_option_list
+ ;
grant_option_list:
- grant_option_list grant_option {}
- | grant_option {}
+ grant_option_list grant_option {}
+ | grant_option {}
;
grant_option:
- GRANT OPTION { Lex->grant |= GRANT_ACL;}
+ GRANT OPTION { Lex->grant |= GRANT_ACL;}
| MAX_QUERIES_PER_HOUR ulong_num
- {
- LEX *lex=Lex;
- lex->mqh.questions=$2;
- lex->mqh.specified_limits|= USER_RESOURCES::QUERIES_PER_HOUR;
- }
+ {
+ LEX *lex=Lex;
+ lex->mqh.questions=$2;
+ lex->mqh.specified_limits|= USER_RESOURCES::QUERIES_PER_HOUR;
+ }
| MAX_UPDATES_PER_HOUR ulong_num
- {
- LEX *lex=Lex;
- lex->mqh.updates=$2;
- lex->mqh.specified_limits|= USER_RESOURCES::UPDATES_PER_HOUR;
- }
+ {
+ LEX *lex=Lex;
+ lex->mqh.updates=$2;
+ lex->mqh.specified_limits|= USER_RESOURCES::UPDATES_PER_HOUR;
+ }
| MAX_CONNECTIONS_PER_HOUR ulong_num
- {
- LEX *lex=Lex;
- lex->mqh.conn_per_hour= $2;
- lex->mqh.specified_limits|= USER_RESOURCES::CONNECTIONS_PER_HOUR;
- }
+ {
+ LEX *lex=Lex;
+ lex->mqh.conn_per_hour= $2;
+ lex->mqh.specified_limits|= USER_RESOURCES::CONNECTIONS_PER_HOUR;
+ }
| MAX_USER_CONNECTIONS_SYM ulong_num
- {
- LEX *lex=Lex;
- lex->mqh.user_conn= $2;
- lex->mqh.specified_limits|= USER_RESOURCES::USER_CONNECTIONS;
- }
+ {
+ LEX *lex=Lex;
+ lex->mqh.user_conn= $2;
+ lex->mqh.specified_limits|= USER_RESOURCES::USER_CONNECTIONS;
+ }
;
begin:
- BEGIN_SYM
- {
- LEX *lex=Lex;
- lex->sql_command = SQLCOM_BEGIN;
- lex->start_transaction_opt= 0;
- }
- opt_work {}
- ;
+ BEGIN_SYM
+ {
+ LEX *lex=Lex;
+ lex->sql_command = SQLCOM_BEGIN;
+ lex->start_transaction_opt= 0;
+ }
+ opt_work {}
+ ;
opt_work:
- /* empty */ {}
- | WORK_SYM {}
+ /* empty */ {}
+ | WORK_SYM {}
;
opt_chain:
- /* empty */ { $$= (YYTHD->variables.completion_type == 1); }
- | AND_SYM NO_SYM CHAIN_SYM { $$=0; }
- | AND_SYM CHAIN_SYM { $$=1; }
- ;
+ /* empty */
+ { $$= (YYTHD->variables.completion_type == 1); }
+ | AND_SYM NO_SYM CHAIN_SYM { $$=0; }
+ | AND_SYM CHAIN_SYM { $$=1; }
+ ;
opt_release:
- /* empty */ { $$= (YYTHD->variables.completion_type == 2); }
- | RELEASE_SYM { $$=1; }
- | NO_SYM RELEASE_SYM { $$=0; }
- ;
-
+ /* empty */
+ { $$= (YYTHD->variables.completion_type == 2); }
+ | RELEASE_SYM { $$=1; }
+ | NO_SYM RELEASE_SYM { $$=0; }
+;
+
opt_savepoint:
- /* empty */ {}
- | SAVEPOINT_SYM {}
- ;
+ /* empty */ {}
+ | SAVEPOINT_SYM {}
+ ;
commit:
- COMMIT_SYM opt_work opt_chain opt_release
- {
- LEX *lex=Lex;
- lex->sql_command= SQLCOM_COMMIT;
- lex->tx_chain= $3;
- lex->tx_release= $4;
- }
- ;
+ COMMIT_SYM opt_work opt_chain opt_release
+ {
+ LEX *lex=Lex;
+ lex->sql_command= SQLCOM_COMMIT;
+ lex->tx_chain= $3;
+ lex->tx_release= $4;
+ }
+ ;
rollback:
- ROLLBACK_SYM opt_work opt_chain opt_release
- {
- LEX *lex=Lex;
- lex->sql_command= SQLCOM_ROLLBACK;
- lex->tx_chain= $3;
- lex->tx_release= $4;
- }
- | ROLLBACK_SYM opt_work
- TO_SYM opt_savepoint ident
- {
- LEX *lex=Lex;
- lex->sql_command= SQLCOM_ROLLBACK_TO_SAVEPOINT;
- lex->ident= $5;
- }
- ;
+ ROLLBACK_SYM opt_work opt_chain opt_release
+ {
+ LEX *lex=Lex;
+ lex->sql_command= SQLCOM_ROLLBACK;
+ lex->tx_chain= $3;
+ lex->tx_release= $4;
+ }
+ | ROLLBACK_SYM opt_work
+ TO_SYM opt_savepoint ident
+ {
+ LEX *lex=Lex;
+ lex->sql_command= SQLCOM_ROLLBACK_TO_SAVEPOINT;
+ lex->ident= $5;
+ }
+ ;
savepoint:
- SAVEPOINT_SYM ident
- {
- LEX *lex=Lex;
- lex->sql_command= SQLCOM_SAVEPOINT;
- lex->ident= $2;
- }
- ;
+ SAVEPOINT_SYM ident
+ {
+ LEX *lex=Lex;
+ lex->sql_command= SQLCOM_SAVEPOINT;
+ lex->ident= $2;
+ }
+ ;
release:
- RELEASE_SYM SAVEPOINT_SYM ident
- {
- LEX *lex=Lex;
- lex->sql_command= SQLCOM_RELEASE_SAVEPOINT;
- lex->ident= $3;
- }
- ;
-
+ RELEASE_SYM SAVEPOINT_SYM ident
+ {
+ LEX *lex=Lex;
+ lex->sql_command= SQLCOM_RELEASE_SAVEPOINT;
+ lex->ident= $3;
+ }
+ ;
+
/*
UNIONS : glue selects together
*/
union_clause:
- /* empty */ {}
- | union_list
- ;
+ /* empty */ {}
+ | union_list
+ ;
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))
+ UNION_SYM union_option
{
- /*
- 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.
+ 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))
+ 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
+ {
+ /*
+ Remove from the name resolution context stack the context of the
+ last select in the union.
*/
- my_error(ER_WRONG_USAGE, MYF(0), "UNION", "INTO");
- MYSQL_YYABORT;
+ Lex->pop_context();
}
- 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))
- 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
- {
- /*
- Remove from the name resolution context stack the context of the
- last select in the union.
- */
- Lex->pop_context();
- }
- ;
+ ;
union_opt:
- /* Empty */ { $$= 0; }
- | union_list { $$= 1; }
- | union_order_or_limit { $$= 1; }
- ;
+ /* Empty */ { $$= 0; }
+ | union_list { $$= 1; }
+ | union_order_or_limit { $$= 1; }
+ ;
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;
- SELECT_LEX_UNIT *unit= sel->master_unit();
- SELECT_LEX *fake= unit->fake_select_lex;
- if (fake)
- {
- unit->global_parameters= fake;
- fake->no_table_names_allowed= 1;
- lex->current_select= fake;
- }
- thd->where= "global ORDER clause";
- }
- order_or_limit
- {
- THD *thd= YYTHD;
- thd->lex->current_select->no_table_names_allowed= 0;
- thd->where= "";
+ {
+ THD *thd= YYTHD;
+ LEX *lex= thd->lex;
+ DBUG_ASSERT(lex->current_select->linkage != GLOBAL_OPTIONS_TYPE);
+ SELECT_LEX *sel= lex->current_select;
+ SELECT_LEX_UNIT *unit= sel->master_unit();
+ SELECT_LEX *fake= unit->fake_select_lex;
+ if (fake)
+ {
+ unit->global_parameters= fake;
+ fake->no_table_names_allowed= 1;
+ lex->current_select= fake;
+ }
+ thd->where= "global ORDER clause";
}
- ;
+ order_or_limit
+ {
+ THD *thd= YYTHD;
+ thd->lex->current_select->no_table_names_allowed= 0;
+ thd->where= "";
+ }
+ ;
order_or_limit:
- order_clause opt_limit_clause_init
- | limit_clause
- ;
+ order_clause opt_limit_clause_init
+ | limit_clause
+ ;
union_option:
- /* empty */ { $$=1; }
- | DISTINCT { $$=1; }
- | ALL { $$=0; }
+ /* empty */ { $$=1; }
+ | DISTINCT { $$=1; }
+ | ALL { $$=0; }
;
take_first_select: /* empty */
@@ -10740,41 +12881,42 @@ subselect:
};
subselect_start:
- {
- LEX *lex=Lex;
- if (lex->sql_command == (int)SQLCOM_HA_READ ||
- lex->sql_command == (int)SQLCOM_KILL ||
- lex->sql_command == (int)SQLCOM_PURGE)
- {
- my_parse_error(ER(ER_SYNTAX_ERROR));
- MYSQL_YYABORT;
- }
- /*
- we are making a "derived table" for the parenthesis
- as we need to have a lex level to fit the union
- after the parenthesis, e.g.
- (SELECT .. ) UNION ... becomes
- SELECT * FROM ((SELECT ...) UNION ...)
- */
- if (mysql_new_select(Lex, 1))
- MYSQL_YYABORT;
- };
+ {
+ LEX *lex=Lex;
+ if (!lex->expr_allows_subselect ||
+ lex->sql_command == (int)SQLCOM_PURGE)
+ {
+ my_parse_error(ER(ER_SYNTAX_ERROR));
+ MYSQL_YYABORT;
+ }
+ /*
+ we are making a "derived table" for the parenthesis
+ as we need to have a lex level to fit the union
+ after the parenthesis, e.g.
+ (SELECT .. ) UNION ... becomes
+ SELECT * FROM ((SELECT ...) UNION ...)
+ */
+ if (mysql_new_select(Lex, 1))
+ MYSQL_YYABORT;
+ }
+ ;
subselect_end:
- {
- LEX *lex=Lex;
- lex->pop_context();
- SELECT_LEX *child= lex->current_select;
- lex->current_select = lex->current_select->return_after_parsing();
- lex->nest_level--;
- lex->current_select->n_child_sum_items += child->n_sum_items;
- /*
- A subselect can add fields to an outer select. Reserve space for
- them.
- */
- lex->current_select->select_n_where_fields+=
+ {
+ LEX *lex=Lex;
+ lex->pop_context();
+ SELECT_LEX *child= lex->current_select;
+ lex->current_select = lex->current_select->return_after_parsing();
+ lex->nest_level--;
+ lex->current_select->n_child_sum_items += child->n_sum_items;
+ /*
+ A subselect can add fields to an outer select. Reserve space for
+ them.
+ */
+ lex->current_select->select_n_where_fields+=
child->select_n_where_fields;
- };
+ }
+ ;
/**************************************************************************
@@ -10782,29 +12924,31 @@ subselect_end:
**************************************************************************/
-view_or_trigger_or_sp:
- definer definer_tail
- {}
- | no_definer no_definer_tail
- {}
- | view_replace_or_algorithm definer_opt view_tail
- {}
- ;
+view_or_trigger_or_sp_or_event:
+ definer definer_tail
+ {}
+ | no_definer no_definer_tail
+ {}
+ | view_replace_or_algorithm definer_opt view_tail
+ {}
+ ;
definer_tail:
- view_tail
- | trigger_tail
- | sp_tail
- | sf_tail
- ;
+ view_tail
+ | trigger_tail
+ | sp_tail
+ | sf_tail
+ | event_tail
+ ;
no_definer_tail:
- view_tail
- | trigger_tail
- | sp_tail
- | sf_tail
- | udf_tail
- ;
+ view_tail
+ | trigger_tail
+ | sp_tail
+ | sf_tail
+ | udf_tail
+ | event_tail
+ ;
/**************************************************************************
@@ -10826,7 +12970,7 @@ no_definer:
handle CREATE TRIGGER statements which come to replication thread
from older master servers (i.e. to create non-suid trigger in this
case).
- */
+ */
YYTHD->lex->definer= 0;
}
;
@@ -10836,7 +12980,7 @@ definer:
{
YYTHD->lex->definer= get_current_user(YYTHD, $3);
}
-;
+ ;
/**************************************************************************
@@ -10845,130 +12989,109 @@ definer:
**************************************************************************/
view_replace_or_algorithm:
- view_replace
- {}
- | view_replace view_algorithm
- {}
- | view_algorithm
- {}
- ;
+ view_replace
+ {}
+ | view_replace view_algorithm
+ {}
+ | view_algorithm
+ {}
+ ;
view_replace:
- OR_SYM REPLACE
- { Lex->create_view_mode= VIEW_CREATE_OR_REPLACE; }
- ;
+ OR_SYM REPLACE
+ { Lex->create_view_mode= VIEW_CREATE_OR_REPLACE; }
+ ;
view_algorithm:
- ALGORITHM_SYM EQ UNDEFINED_SYM
- { Lex->create_view_algorithm= VIEW_ALGORITHM_UNDEFINED; }
- | ALGORITHM_SYM EQ MERGE_SYM
- { Lex->create_view_algorithm= VIEW_ALGORITHM_MERGE; }
- | ALGORITHM_SYM EQ TEMPTABLE_SYM
- { Lex->create_view_algorithm= VIEW_ALGORITHM_TMPTABLE; }
- ;
-
-view_algorithm_opt:
- /* empty */
- { Lex->create_view_algorithm= VIEW_ALGORITHM_UNDEFINED; }
- | view_algorithm
- {}
- ;
+ ALGORITHM_SYM EQ UNDEFINED_SYM
+ { Lex->create_view_algorithm= VIEW_ALGORITHM_UNDEFINED; }
+ | ALGORITHM_SYM EQ MERGE_SYM
+ { Lex->create_view_algorithm= VIEW_ALGORITHM_MERGE; }
+ | ALGORITHM_SYM EQ TEMPTABLE_SYM
+ { Lex->create_view_algorithm= VIEW_ALGORITHM_TMPTABLE; }
+ ;
view_suid:
- /* empty */
- { Lex->create_view_suid= VIEW_SUID_DEFAULT; }
- | SQL_SYM SECURITY_SYM DEFINER_SYM
- { Lex->create_view_suid= VIEW_SUID_DEFINER; }
- | SQL_SYM SECURITY_SYM INVOKER_SYM
- { Lex->create_view_suid= VIEW_SUID_INVOKER; }
- ;
+ /* empty */
+ { Lex->create_view_suid= VIEW_SUID_DEFAULT; }
+ | SQL_SYM SECURITY_SYM DEFINER_SYM
+ { Lex->create_view_suid= VIEW_SUID_DEFINER; }
+ | SQL_SYM SECURITY_SYM INVOKER_SYM
+ { Lex->create_view_suid= VIEW_SUID_INVOKER; }
+ ;
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))
- MYSQL_YYABORT;
- }
- view_list_opt AS view_select view_check_option
- {}
- ;
+ 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))
+ MYSQL_YYABORT;
+ }
+ view_list_opt AS view_select
+ ;
view_list_opt:
- /* empty */
- {}
- | '(' view_list ')'
- ;
+ /* empty */
+ {}
+ | '(' view_list ')'
+ ;
view_list:
- ident
- {
- LEX_STRING *ls= (LEX_STRING*) sql_memdup(&$1, sizeof(LEX_STRING));
- if (ls == NULL)
- MYSQL_YYABORT;
- Lex->view_list.push_back(ls);
- }
- | view_list ',' ident
- {
- LEX_STRING *ls= (LEX_STRING*) sql_memdup(&$3, sizeof(LEX_STRING));
- if (ls == NULL)
- MYSQL_YYABORT;
- Lex->view_list.push_back(ls);
- }
- ;
+ ident
+ {
+ Lex->view_list.push_back((LEX_STRING*)
+ sql_memdup(&$1, sizeof(LEX_STRING)));
+ }
+ | view_list ',' ident
+ {
+ Lex->view_list.push_back((LEX_STRING*)
+ sql_memdup(&$3, sizeof(LEX_STRING)));
+ }
+ ;
view_select:
- {
- LEX *lex= Lex;
- lex->parsing_options.allows_variable= FALSE;
- lex->parsing_options.allows_select_into= FALSE;
- lex->parsing_options.allows_select_procedure= FALSE;
- lex->parsing_options.allows_derived= FALSE;
- }
- view_select_aux
- {
- LEX *lex= Lex;
- lex->parsing_options.allows_variable= TRUE;
- lex->parsing_options.allows_select_into= TRUE;
- lex->parsing_options.allows_select_procedure= TRUE;
- lex->parsing_options.allows_derived= TRUE;
- }
+ {
+ LEX *lex= Lex;
+ lex->parsing_options.allows_variable= FALSE;
+ lex->parsing_options.allows_select_into= FALSE;
+ lex->parsing_options.allows_select_procedure= FALSE;
+ lex->parsing_options.allows_derived= FALSE;
+ lex->create_view_select.str= (char *) YYLIP->get_cpp_ptr();
+ }
+ 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);
+ lex->create_view_select.length= len;
+ lex->create_view_select.str= (char *) create_view_select;
+ trim_whitespace(thd->charset(), &lex->create_view_select);
+ lex->parsing_options.allows_variable= TRUE;
+ lex->parsing_options.allows_select_into= TRUE;
+ lex->parsing_options.allows_select_procedure= TRUE;
+ lex->parsing_options.allows_derived= TRUE;
+ }
;
view_select_aux:
- SELECT_SYM remember_name select_init2
- {
- THD *thd=YYTHD;
- LEX *lex= thd->lex;
- char *stmt_beg= (lex->sphead ?
- (char *)lex->sphead->m_tmp_query :
- thd->query);
- lex->create_view_select_start= $2 - stmt_beg;
- }
- | '(' remember_name select_paren ')' union_opt
- {
- THD *thd=YYTHD;
- LEX *lex= thd->lex;
- char *stmt_beg= (lex->sphead ?
- (char *)lex->sphead->m_tmp_query :
- thd->query);
- lex->create_view_select_start= $2 - stmt_beg;
- }
- ;
+ SELECT_SYM select_init2
+ | '(' select_paren ')' union_opt
+ ;
view_check_option:
- /* empty */
- { Lex->create_view_check= VIEW_CHECK_NONE; }
- | WITH CHECK_SYM OPTION
- { Lex->create_view_check= VIEW_CHECK_CASCADED; }
- | WITH CASCADED CHECK_SYM OPTION
- { Lex->create_view_check= VIEW_CHECK_CASCADED; }
- | WITH LOCAL_SYM CHECK_SYM OPTION
- { Lex->create_view_check= VIEW_CHECK_LOCAL; }
- ;
+ /* empty */
+ { Lex->create_view_check= VIEW_CHECK_NONE; }
+ | WITH CHECK_SYM OPTION
+ { Lex->create_view_check= VIEW_CHECK_CASCADED; }
+ | WITH CASCADED CHECK_SYM OPTION
+ { Lex->create_view_check= VIEW_CHECK_CASCADED; }
+ | WITH LOCAL_SYM CHECK_SYM OPTION
+ { Lex->create_view_check= VIEW_CHECK_LOCAL; }
+ ;
/**************************************************************************
@@ -10977,64 +13100,77 @@ view_check_option:
**************************************************************************/
trigger_tail:
- TRIGGER_SYM remember_name sp_name trg_action_time trg_event
- ON remember_name table_ident FOR_SYM remember_name EACH_SYM ROW_SYM
- {
- THD *thd= YYTHD;
- LEX *lex= thd->lex;
- Lex_input_stream *lip= YYLIP;
- sp_head *sp;
-
- if (lex->sphead)
- {
- my_error(ER_SP_NO_RECURSIVE_CREATE, MYF(0), "TRIGGER");
- MYSQL_YYABORT;
- }
-
- if (!(sp= new sp_head()))
- MYSQL_YYABORT;
- sp->reset_thd_mem_root(thd);
- sp->init(lex);
- sp->m_type= TYPE_ENUM_TRIGGER;
- sp->init_sp_name(thd, $3);
-
- lex->stmt_definition_begin= $2;
- lex->ident.str= $7;
- lex->ident.length= $10 - $7;
-
- lex->sphead= sp;
- lex->spname= $3;
-
- bzero((char *)&lex->sp_chistics, sizeof(st_sp_chistics));
- lex->sphead->m_chistics= &lex->sp_chistics;
- lex->sphead->m_body_begin= lip->ptr;
- while (my_isspace(system_charset_info, lex->sphead->m_body_begin[0]))
- ++lex->sphead->m_body_begin;
- }
- sp_proc_stmt
- {
- LEX *lex= Lex;
- sp_head *sp= lex->sphead;
-
- lex->sql_command= SQLCOM_CREATE_TRIGGER;
- sp->init_strings(YYTHD, lex);
- sp->restore_thd_mem_root(YYTHD);
-
- if (sp->is_not_allowed_in_function("trigger"))
- MYSQL_YYABORT;
-
- /*
- We have to do it after parsing trigger body, because some of
- sp_proc_stmt alternatives are not saving/restoring LEX, so
- lex->query_tables can be wiped out.
- */
- if (!lex->select_lex.add_table_to_list(YYTHD, $8,
- (LEX_STRING*) 0,
- TL_OPTION_UPDATING,
- TL_IGNORE))
- MYSQL_YYABORT;
- }
- ;
+ TRIGGER_SYM
+ remember_name
+ sp_name
+ trg_action_time
+ trg_event
+ ON
+ remember_name /* $7 */
+ { /* $8 */
+ Lex->raw_trg_on_table_name_begin= YYLIP->get_tok_start();
+ }
+ table_ident /* $9 */
+ FOR_SYM
+ remember_name /* $11 */
+ { /* $12 */
+ Lex->raw_trg_on_table_name_end= YYLIP->get_tok_start();
+ }
+ EACH_SYM
+ ROW_SYM
+ { /* $15 */
+ THD *thd= YYTHD;
+ LEX *lex= thd->lex;
+ Lex_input_stream *lip= YYLIP;
+ sp_head *sp;
+
+ if (lex->sphead)
+ {
+ my_error(ER_SP_NO_RECURSIVE_CREATE, MYF(0), "TRIGGER");
+ MYSQL_YYABORT;
+ }
+
+ if (!(sp= new sp_head()))
+ MYSQL_YYABORT;
+ sp->reset_thd_mem_root(thd);
+ sp->init(lex);
+ sp->m_type= TYPE_ENUM_TRIGGER;
+ sp->init_sp_name(thd, $3);
+ lex->stmt_definition_begin= $2;
+ lex->ident.str= $7;
+ lex->ident.length= $11 - $7;
+
+ lex->sphead= sp;
+ lex->spname= $3;
+
+ bzero((char *)&lex->sp_chistics, sizeof(st_sp_chistics));
+ lex->sphead->m_chistics= &lex->sp_chistics;
+ lex->sphead->set_body_start(thd, lip->get_cpp_ptr());
+ }
+ sp_proc_stmt /* $16 */
+ { /* $17 */
+ LEX *lex= Lex;
+ sp_head *sp= lex->sphead;
+
+ lex->sql_command= SQLCOM_CREATE_TRIGGER;
+ sp->set_stmt_end(YYTHD);
+ sp->restore_thd_mem_root(YYTHD);
+
+ if (sp->is_not_allowed_in_function("trigger"))
+ MYSQL_YYABORT;
+
+ /*
+ We have to do it after parsing trigger body, because some of
+ sp_proc_stmt alternatives are not saving/restoring LEX, so
+ lex->query_tables can be wiped out.
+ */
+ if (!lex->select_lex.add_table_to_list(YYTHD, $9,
+ (LEX_STRING*) 0,
+ TL_OPTION_UPDATING,
+ TL_IGNORE))
+ MYSQL_YYABORT;
+ }
+ ;
/**************************************************************************
@@ -11044,249 +13180,340 @@ trigger_tail:
udf_tail:
AGGREGATE_SYM remember_name FUNCTION_SYM ident
- RETURNS_SYM udf_type UDF_SONAME_SYM TEXT_STRING_sys
- {
- LEX *lex=Lex;
- lex->sql_command = SQLCOM_CREATE_FUNCTION;
- lex->udf.type= UDFTYPE_AGGREGATE;
- lex->stmt_definition_begin= $2;
- lex->udf.name = $4;
- lex->udf.returns=(Item_result) $6;
- lex->udf.dl=$8.str;
- }
+ RETURNS_SYM udf_type SONAME_SYM TEXT_STRING_sys
+ {
+ THD *thd= YYTHD;
+ LEX *lex= thd->lex;
+ if (is_native_function(thd, & $4))
+ {
+ my_error(ER_NATIVE_FCT_NAME_COLLISION, MYF(0),
+ $4.str);
+ MYSQL_YYABORT;
+ }
+ lex->sql_command = SQLCOM_CREATE_FUNCTION;
+ lex->udf.type= UDFTYPE_AGGREGATE;
+ lex->stmt_definition_begin= $2;
+ lex->udf.name = $4;
+ lex->udf.returns=(Item_result) $6;
+ lex->udf.dl=$8.str;
+ }
| remember_name FUNCTION_SYM ident
- RETURNS_SYM udf_type UDF_SONAME_SYM TEXT_STRING_sys
- {
- LEX *lex=Lex;
- lex->sql_command = SQLCOM_CREATE_FUNCTION;
- lex->udf.type= UDFTYPE_FUNCTION;
- lex->stmt_definition_begin= $1;
- lex->udf.name = $3;
- lex->udf.returns=(Item_result) $5;
- lex->udf.dl=$7.str;
- }
+ RETURNS_SYM udf_type SONAME_SYM TEXT_STRING_sys
+ {
+ THD *thd= YYTHD;
+ LEX *lex= thd->lex;
+ if (is_native_function(thd, & $3))
+ {
+ my_error(ER_NATIVE_FCT_NAME_COLLISION, MYF(0),
+ $3.str);
+ MYSQL_YYABORT;
+ }
+ lex->sql_command = SQLCOM_CREATE_FUNCTION;
+ lex->udf.type= UDFTYPE_FUNCTION;
+ lex->stmt_definition_begin= $1;
+ lex->udf.name = $3;
+ lex->udf.returns=(Item_result) $5;
+ lex->udf.dl=$7.str;
+ }
;
sf_tail:
remember_name /* $1 */
FUNCTION_SYM /* $2 */
sp_name /* $3 */
- '(' /* 44 */
+ '(' /* $4 */
{ /* $5 */
THD *thd= YYTHD;
- LEX *lex= thd->lex;
+ LEX *lex= thd->lex;
Lex_input_stream *lip= YYLIP;
- sp_head *sp;
-
- lex->stmt_definition_begin= $1;
- lex->spname= $3;
+ sp_head *sp;
+ const char* tmp_param_begin;
- if (lex->sphead)
- {
- my_error(ER_SP_NO_RECURSIVE_CREATE, MYF(0), "FUNCTION");
- MYSQL_YYABORT;
- }
+ lex->stmt_definition_begin= $1;
+ lex->spname= $3;
- /* Order is important here: new - reset - init */
- sp= new sp_head();
+ if (lex->sphead)
+ {
+ my_error(ER_SP_NO_RECURSIVE_CREATE, MYF(0), "FUNCTION");
+ MYSQL_YYABORT;
+ }
+ /* Order is important here: new - reset - init */
+ sp= new sp_head();
if (sp == NULL)
MYSQL_YYABORT;
- sp->reset_thd_mem_root(thd);
- sp->init(lex);
+ sp->reset_thd_mem_root(thd);
+ sp->init(lex);
sp->init_sp_name(thd, lex->spname);
- sp->m_type= TYPE_ENUM_FUNCTION;
- lex->sphead= sp;
- lex->sphead->m_param_begin= lip->tok_start+1;
- }
+ sp->m_type= TYPE_ENUM_FUNCTION;
+ lex->sphead= sp;
+
+ tmp_param_begin= lip->get_cpp_tok_start();
+ tmp_param_begin++;
+ lex->sphead->m_param_begin= tmp_param_begin;
+ }
sp_fdparam_list /* $6 */
')' /* $7 */
{ /* $8 */
- Lex->sphead->m_param_end= YYLIP->tok_start;
- }
+ Lex->sphead->m_param_end= YYLIP->get_cpp_tok_start();
+ }
RETURNS_SYM /* $9 */
- { /* $10 */
- LEX *lex= Lex;
- lex->charset= NULL;
- lex->length= lex->dec= NULL;
- lex->interval_list.empty();
- lex->type= 0;
- }
+ { /* $10 */
+ LEX *lex= Lex;
+ lex->charset= NULL;
+ lex->length= lex->dec= NULL;
+ lex->interval_list.empty();
+ lex->type= 0;
+ }
type /* $11 */
{ /* $12 */
- LEX *lex= Lex;
- sp_head *sp= lex->sphead;
+ LEX *lex= Lex;
+ sp_head *sp= lex->sphead;
+ /*
+ This was disabled in 5.1.12. See bug #20701
+ When collation support in SP is implemented, then this test
+ should be removed.
+ */
+ if (($11 == MYSQL_TYPE_STRING || $11 == MYSQL_TYPE_VARCHAR)
+ && (lex->type & BINCMP_FLAG))
+ {
+ my_error(ER_NOT_SUPPORTED_YET, MYF(0), "return value collation");
+ MYSQL_YYABORT;
+ }
if (sp->fill_field_definition(YYTHD, lex,
(enum enum_field_types) $11,
&sp->m_return_field_def))
MYSQL_YYABORT;
- bzero((char *)&lex->sp_chistics, sizeof(st_sp_chistics));
- }
+ bzero((char *)&lex->sp_chistics, sizeof(st_sp_chistics));
+ }
sp_c_chistics /* $13 */
{ /* $14 */
THD *thd= YYTHD;
- LEX *lex= thd->lex;
+ LEX *lex= thd->lex;
Lex_input_stream *lip= YYLIP;
- lex->sphead->m_chistics= &lex->sp_chistics;
- lex->sphead->m_body_begin= lip->tok_start;
- }
+ lex->sphead->m_chistics= &lex->sp_chistics;
+ lex->sphead->set_body_start(thd, lip->get_cpp_tok_start());
+ }
sp_proc_stmt /* $15 */
- {
- LEX *lex= Lex;
- sp_head *sp= lex->sphead;
+ {
+ THD *thd= YYTHD;
+ LEX *lex= thd->lex;
+ sp_head *sp= lex->sphead;
if (sp->is_not_allowed_in_function("function"))
MYSQL_YYABORT;
- lex->sql_command= SQLCOM_CREATE_SPFUNCTION;
- sp->init_strings(YYTHD, lex);
+ lex->sql_command= SQLCOM_CREATE_SPFUNCTION;
+ sp->set_stmt_end(thd);
if (!(sp->m_flags & sp_head::HAS_RETURN))
{
my_error(ER_SP_NORETURN, MYF(0), sp->m_qname.str);
MYSQL_YYABORT;
}
- sp->restore_thd_mem_root(YYTHD);
- }
- ;
-
+ if (is_native_function(thd, & sp->m_name))
+ {
+ /*
+ This warning will be printed when
+ [1] A client query is parsed,
+ [2] A stored function is loaded by db_load_routine.
+ Printing the warning for [2] is intentional, to cover the
+ following scenario:
+ - A user define a SF 'foo' using MySQL 5.N
+ - An application uses select foo(), and works.
+ - MySQL 5.{N+1} defines a new native function 'foo', as
+ part of a new feature.
+ - MySQL 5.{N+1} documentation is updated, and should mention
+ that there is a potential incompatible change in case of
+ existing stored function named 'foo'.
+ - The user deploys 5.{N+1}. At this point, 'select foo()'
+ means something different, and the user code is most likely
+ broken (it's only safe if the code is 'select db.foo()').
+ With a warning printed when the SF is loaded (which has to occur
+ before the call), the warning will provide a hint explaining
+ the root cause of a later failure of 'select foo()'.
+ With no warning printed, the user code will fail with no
+ apparent reason.
+ Printing a warning each time db_load_routine is executed for
+ an ambiguous function is annoying, since that can happen a lot,
+ but in practice should not happen unless there *are* name
+ collisions.
+ If a collision exists, it should not be silenced but fixed.
+ */
+ push_warning_printf(thd,
+ MYSQL_ERROR::WARN_LEVEL_NOTE,
+ ER_NATIVE_FCT_NAME_COLLISION,
+ ER(ER_NATIVE_FCT_NAME_COLLISION),
+ sp->m_name.str);
+ }
+ sp->restore_thd_mem_root(thd);
+ }
+ ;
sp_tail:
- PROCEDURE remember_name sp_name
- {
- LEX *lex= Lex;
- sp_head *sp;
-
- if (lex->sphead)
- {
- my_error(ER_SP_NO_RECURSIVE_CREATE, MYF(0), "PROCEDURE");
- MYSQL_YYABORT;
- }
-
- lex->stmt_definition_begin= $2;
-
- /* Order is important here: new - reset - init */
- sp= new sp_head();
- if (sp == NULL)
- MYSQL_YYABORT;
- sp->reset_thd_mem_root(YYTHD);
- sp->init(lex);
- sp->m_type= TYPE_ENUM_PROCEDURE;
- sp->init_sp_name(YYTHD, $3);
-
- lex->sphead= sp;
- }
- '('
- {
- Lex->sphead->m_param_begin= YYLIP->tok_start+1;
- }
- sp_pdparam_list
- ')'
- {
- THD *thd= YYTHD;
- LEX *lex= thd->lex;
- Lex_input_stream *lip= YYLIP;
-
- lex->sphead->m_param_end= lip->tok_start;
- bzero((char *)&lex->sp_chistics, sizeof(st_sp_chistics));
- }
- sp_c_chistics
- {
- THD *thd= YYTHD;
- LEX *lex= thd->lex;
- Lex_input_stream *lip= YYLIP;
-
- lex->sphead->m_chistics= &lex->sp_chistics;
- lex->sphead->m_body_begin= lip->tok_start;
- }
- sp_proc_stmt
- {
- LEX *lex= Lex;
- sp_head *sp= lex->sphead;
-
- sp->init_strings(YYTHD, lex);
- lex->sql_command= SQLCOM_CREATE_PROCEDURE;
- sp->restore_thd_mem_root(YYTHD);
- }
- ;
+ PROCEDURE remember_name sp_name
+ {
+ LEX *lex= Lex;
+ sp_head *sp;
+
+ if (lex->sphead)
+ {
+ my_error(ER_SP_NO_RECURSIVE_CREATE, MYF(0), "PROCEDURE");
+ MYSQL_YYABORT;
+ }
+
+ lex->stmt_definition_begin= $2;
+
+ /* Order is important here: new - reset - init */
+ sp= new sp_head();
+ if (sp == NULL)
+ MYSQL_YYABORT;
+ sp->reset_thd_mem_root(YYTHD);
+ sp->init(lex);
+ sp->m_type= TYPE_ENUM_PROCEDURE;
+ sp->init_sp_name(YYTHD, $3);
+
+ lex->sphead= sp;
+ }
+ '('
+ {
+ const char* tmp_param_begin;
+
+ tmp_param_begin= YYLIP->get_cpp_tok_start();
+ tmp_param_begin++;
+ Lex->sphead->m_param_begin= tmp_param_begin;
+ }
+ sp_pdparam_list
+ ')'
+ {
+ THD *thd= YYTHD;
+ LEX *lex= thd->lex;
+
+ lex->sphead->m_param_end= YYLIP->get_cpp_tok_start();
+ bzero((char *)&lex->sp_chistics, sizeof(st_sp_chistics));
+ }
+ sp_c_chistics
+ {
+ THD *thd= YYTHD;
+ LEX *lex= thd->lex;
+
+ lex->sphead->m_chistics= &lex->sp_chistics;
+ lex->sphead->set_body_start(thd, YYLIP->get_cpp_tok_start());
+ }
+ sp_proc_stmt
+ {
+ LEX *lex= Lex;
+ sp_head *sp= lex->sphead;
+
+ sp->set_stmt_end(YYTHD);
+ lex->sql_command= SQLCOM_CREATE_PROCEDURE;
+ sp->restore_thd_mem_root(YYTHD);
+ }
+ ;
/*************************************************************************/
-xa: XA_SYM begin_or_start xid opt_join_or_resume
- {
- Lex->sql_command = SQLCOM_XA_START;
- }
- | XA_SYM END xid opt_suspend
- {
- Lex->sql_command = SQLCOM_XA_END;
- }
- | XA_SYM PREPARE_SYM xid
- {
- Lex->sql_command = SQLCOM_XA_PREPARE;
- }
- | XA_SYM COMMIT_SYM xid opt_one_phase
- {
- Lex->sql_command = SQLCOM_XA_COMMIT;
- }
- | XA_SYM ROLLBACK_SYM xid
- {
- Lex->sql_command = SQLCOM_XA_ROLLBACK;
- }
- | XA_SYM RECOVER_SYM
- {
- Lex->sql_command = SQLCOM_XA_RECOVER;
- }
- ;
-
-xid: text_string
- {
- MYSQL_YYABORT_UNLESS($1->length() <= MAXGTRIDSIZE);
- if (!(Lex->xid=(XID *)YYTHD->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))))
- 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))))
- MYSQL_YYABORT;
- Lex->xid->set($5, $1->ptr(), $1->length(), $3->ptr(), $3->length());
- }
- ;
-
-begin_or_start: BEGIN_SYM {}
- | START_SYM {}
- ;
+xa:
+ XA_SYM begin_or_start xid opt_join_or_resume
+ {
+ Lex->sql_command = SQLCOM_XA_START;
+ }
+ | XA_SYM END xid opt_suspend
+ {
+ Lex->sql_command = SQLCOM_XA_END;
+ }
+ | XA_SYM PREPARE_SYM xid
+ {
+ Lex->sql_command = SQLCOM_XA_PREPARE;
+ }
+ | XA_SYM COMMIT_SYM xid opt_one_phase
+ {
+ Lex->sql_command = SQLCOM_XA_COMMIT;
+ }
+ | XA_SYM ROLLBACK_SYM xid
+ {
+ Lex->sql_command = SQLCOM_XA_ROLLBACK;
+ }
+ | XA_SYM RECOVER_SYM
+ {
+ Lex->sql_command = SQLCOM_XA_RECOVER;
+ }
+ ;
+
+xid:
+ text_string
+ {
+ MYSQL_YYABORT_UNLESS($1->length() <= MAXGTRIDSIZE);
+ if (!(Lex->xid=(XID *)YYTHD->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))))
+ 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))))
+ MYSQL_YYABORT;
+ Lex->xid->set($5, $1->ptr(), $1->length(), $3->ptr(), $3->length());
+ }
+ ;
+
+begin_or_start:
+ BEGIN_SYM {}
+ | START_SYM {}
+ ;
opt_join_or_resume:
- /* nothing */ { Lex->xa_opt=XA_NONE; }
- | JOIN_SYM { Lex->xa_opt=XA_JOIN; }
- | RESUME_SYM { Lex->xa_opt=XA_RESUME; }
- ;
+ /* nothing */ { Lex->xa_opt=XA_NONE; }
+ | JOIN_SYM { Lex->xa_opt=XA_JOIN; }
+ | RESUME_SYM { Lex->xa_opt=XA_RESUME; }
+ ;
opt_one_phase:
- /* nothing */ { Lex->xa_opt=XA_NONE; }
- | ONE_SYM PHASE_SYM { Lex->xa_opt=XA_ONE_PHASE; }
- ;
+ /* nothing */ { Lex->xa_opt=XA_NONE; }
+ | ONE_SYM PHASE_SYM { Lex->xa_opt=XA_ONE_PHASE; }
+ ;
opt_suspend:
- /* nothing */ { Lex->xa_opt=XA_NONE; }
- | SUSPEND_SYM { Lex->xa_opt=XA_SUSPEND; }
- opt_migrate
- ;
+ /* nothing */
+ { Lex->xa_opt=XA_NONE; }
+ | SUSPEND_SYM
+ { Lex->xa_opt=XA_SUSPEND; }
+ opt_migrate
+ ;
opt_migrate:
- /* nothing */ { }
- | FOR_SYM MIGRATE_SYM { Lex->xa_opt=XA_FOR_MIGRATE; }
- ;
+ /* nothing */ {}
+ | FOR_SYM MIGRATE_SYM { Lex->xa_opt=XA_FOR_MIGRATE; }
+ ;
+install:
+ INSTALL_SYM PLUGIN_SYM ident SONAME_SYM TEXT_STRING_sys
+ {
+ LEX *lex= Lex;
+ lex->sql_command= SQLCOM_INSTALL_PLUGIN;
+ lex->comment= $3;
+ lex->ident= $5;
+ }
+ ;
+
+uninstall:
+ UNINSTALL_SYM PLUGIN_SYM ident
+ {
+ LEX *lex= Lex;
+ lex->sql_command= SQLCOM_UNINSTALL_PLUGIN;
+ lex->comment= $3;
+ }
+ ;
+/**
+ @} (end of group Parser)
+*/
diff --git a/sql/stacktrace.c b/sql/stacktrace.c
deleted file mode 100644
index 4e6ad68c172..00000000000
--- a/sql/stacktrace.c
+++ /dev/null
@@ -1,589 +0,0 @@
-/* Copyright (C) 2000 MySQL AB
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; version 2 of the License.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
-
-/* Workaround for Bug#32082: VOID redefinition on Win results in compile errors*/
-#define DONT_DEFINE_VOID 1
-
-#include <my_global.h>
-#include "stacktrace.h"
-
-#ifndef __WIN__
-#include <signal.h>
-#include <my_pthread.h>
-#ifdef HAVE_STACKTRACE
-#include <unistd.h>
-#include <strings.h>
-
-#define PTR_SANE(p) ((p) && (char*)(p) >= heap_start && (char*)(p) <= heap_end)
-
-char *heap_start;
-
-void safe_print_str(const char* name, const char* val, int max_len)
-{
- char *heap_end= (char*) sbrk(0);
- fprintf(stderr, "%s at %p ", name, val);
-
- if (!PTR_SANE(val))
- {
- fprintf(stderr, " is invalid pointer\n");
- return;
- }
-
- fprintf(stderr, "= ");
- for (; max_len && PTR_SANE(val) && *val; --max_len)
- fputc(*val++, stderr);
- fputc('\n', stderr);
-}
-
-#ifdef TARGET_OS_LINUX
-
-#ifdef __i386__
-#define SIGRETURN_FRAME_OFFSET 17
-#endif
-
-#ifdef __x86_64__
-#define SIGRETURN_FRAME_OFFSET 23
-#endif
-
-static my_bool is_nptl;
-
-/* Check if we are using NPTL or LinuxThreads on Linux */
-void check_thread_lib(void)
-{
- char buf[5];
-
-#ifdef _CS_GNU_LIBPTHREAD_VERSION
- confstr(_CS_GNU_LIBPTHREAD_VERSION, buf, sizeof(buf));
- is_nptl = !strncasecmp(buf, "NPTL", sizeof(buf));
-#else
- is_nptl = 0;
-#endif
-}
-
-#if defined(__alpha__) && defined(__GNUC__)
-/*
- The only way to backtrace without a symbol table on alpha
- is to find stq fp,N(sp), and the first byte
- of the instruction opcode will give us the value of N. From this
- we can find where the old value of fp is stored
-*/
-
-#define MAX_INSTR_IN_FUNC 10000
-
-inline uchar** find_prev_fp(uint32* pc, uchar** fp)
-{
- int i;
- for (i = 0; i < MAX_INSTR_IN_FUNC; ++i,--pc)
- {
- uchar* p = (uchar*)pc;
- if (p[2] == 222 && p[3] == 35)
- {
- return (uchar**)((uchar*)fp - *(short int*)p);
- }
- }
- return 0;
-}
-
-inline uint32* find_prev_pc(uint32* pc, uchar** fp)
-{
- int i;
- for (i = 0; i < MAX_INSTR_IN_FUNC; ++i,--pc)
- {
- char* p = (char*)pc;
- if (p[1] == 0 && p[2] == 94 && p[3] == -73)
- {
- uint32* prev_pc = (uint32*)*((fp+p[0]/sizeof(fp)));
- return prev_pc;
- }
- }
- return 0;
-}
-#endif /* defined(__alpha__) && defined(__GNUC__) */
-
-
-void print_stacktrace(gptr stack_bottom, ulong thread_stack)
-{
- uchar** fp;
- uint frame_count = 0, sigreturn_frame_count;
-#if defined(__alpha__) && defined(__GNUC__)
- uint32* pc;
-#endif
- LINT_INIT(fp);
-
-
-#ifdef __i386__
- __asm __volatile__ ("movl %%ebp,%0"
- :"=r"(fp)
- :"r"(fp));
-#endif
-#ifdef __x86_64__
- __asm __volatile__ ("movq %%rbp,%0"
- :"=r"(fp)
- :"r"(fp));
-#endif
-#if defined(__alpha__) && defined(__GNUC__)
- __asm __volatile__ ("mov $30,%0"
- :"=r"(fp)
- :"r"(fp));
-#endif
- if (!fp)
- {
- fprintf(stderr, "frame pointer is NULL, did you compile with\n\
--fomit-frame-pointer? Aborting backtrace!\n");
- return;
- }
-
- if (!stack_bottom || (gptr) stack_bottom > (gptr) &fp)
- {
- ulong tmp= min(0x10000,thread_stack);
- /* Assume that the stack starts at the previous even 65K */
- stack_bottom= (gptr) (((ulong) &fp + tmp) &
- ~(ulong) 0xFFFF);
- fprintf(stderr, "Cannot determine thread, fp=%p, backtrace may not be correct.\n", fp);
- }
- if (fp > (uchar**) stack_bottom ||
- fp < (uchar**) stack_bottom - thread_stack)
- {
- fprintf(stderr, "Bogus stack limit or frame pointer,\
- fp=%p, stack_bottom=%p, thread_stack=%ld, aborting backtrace.\n",
- fp, stack_bottom, thread_stack);
- return;
- }
-
- fprintf(stderr, "Stack range sanity check OK, backtrace follows:\n");
-#if defined(__alpha__) && defined(__GNUC__)
- fprintf(stderr, "Warning: Alpha stacks are difficult -\
- will be taking some wild guesses, stack trace may be incorrect or \
- terminate abruptly\n");
- /* On Alpha, we need to get pc */
- __asm __volatile__ ("bsr %0, do_next; do_next: "
- :"=r"(pc)
- :"r"(pc));
-#endif /* __alpha__ */
-
- /* We are 1 frame above signal frame with NPTL and 2 frames above with LT */
- sigreturn_frame_count = is_nptl ? 1 : 2;
-
- while (fp < (uchar**) stack_bottom)
- {
-#if defined(__i386__) || defined(__x86_64__)
- uchar** new_fp = (uchar**)*fp;
- fprintf(stderr, "%p\n", frame_count == sigreturn_frame_count ?
- *(fp + SIGRETURN_FRAME_OFFSET) : *(fp + 1));
-#endif /* defined(__386__) || defined(__x86_64__) */
-
-#if defined(__alpha__) && defined(__GNUC__)
- uchar** new_fp = find_prev_fp(pc, fp);
- if (frame_count == sigreturn_frame_count - 1)
- {
- new_fp += 90;
- }
-
- if (fp && pc)
- {
- pc = find_prev_pc(pc, fp);
- if (pc)
- fprintf(stderr, "%p\n", pc);
- else
- {
- fprintf(stderr, "Not smart enough to deal with the rest\
- of this stack\n");
- goto end;
- }
- }
- else
- {
- fprintf(stderr, "Not smart enough to deal with the rest of this stack\n");
- goto end;
- }
-#endif /* defined(__alpha__) && defined(__GNUC__) */
- if (new_fp <= fp )
- {
- fprintf(stderr, "New value of fp=%p failed sanity check,\
- terminating stack trace!\n", new_fp);
- goto end;
- }
- fp = new_fp;
- ++frame_count;
- }
-
- fprintf(stderr, "Stack trace seems successful - bottom reached\n");
-
-end:
- fprintf(stderr, "Please read http://dev.mysql.com/doc/mysql/en/using-stack-trace.html and follow instructions on how to resolve the stack trace. Resolved\n\
-stack trace is much more helpful in diagnosing the problem, so please do \n\
-resolve it\n");
-}
-#endif /* TARGET_OS_LINUX */
-#endif /* HAVE_STACKTRACE */
-
-/* Produce a core for the thread */
-
-#ifdef NOT_USED /* HAVE_LINUXTHREADS */
-void write_core(int sig)
-{
- signal(sig, SIG_DFL);
- if (fork() != 0) exit(1); /* Abort main program */
- /* Core will be written at exit */
-}
-#else
-void write_core(int sig)
-{
- signal(sig, SIG_DFL);
-#ifdef HAVE_gcov
- /*
- For GCOV build, crashing will prevent the writing of code coverage
- information from this process, causing gcov output to be incomplete.
- So we force the writing of coverage information here before terminating.
- */
- extern void __gcov_flush(void);
- __gcov_flush();
-#endif
- pthread_kill(pthread_self(), sig);
-#if defined(P_MYID) && !defined(SCO)
- /* On Solaris, the above kill is not enough */
- sigsend(P_PID,P_MYID,sig);
-#endif
-}
-#endif
-#else /* __WIN__*/
-
-#include <dbghelp.h>
-#include <tlhelp32.h>
-
-/*
- Stack tracing on Windows is implemented using Debug Helper library(dbghelp.dll)
- We do not redistribute dbghelp and the one comes with older OS (up to Windows 2000)
- is missing some important functions like functions StackWalk64 or MinidumpWriteDump.
- Hence, we have to load functions at runtime using LoadLibrary/GetProcAddress.
-*/
-
-typedef DWORD (WINAPI *SymSetOptions_FctType)(DWORD dwOptions);
-typedef BOOL (WINAPI *SymGetModuleInfo64_FctType)
- (HANDLE,DWORD64,PIMAGEHLP_MODULE64) ;
-typedef BOOL (WINAPI *SymGetSymFromAddr64_FctType)
- (HANDLE,DWORD64,PDWORD64,PIMAGEHLP_SYMBOL64) ;
-typedef BOOL (WINAPI *SymGetLineFromAddr64_FctType)
- (HANDLE,DWORD64,PDWORD,PIMAGEHLP_LINE64);
-typedef BOOL (WINAPI *SymInitialize_FctType)
- (HANDLE,PSTR,BOOL);
-typedef BOOL (WINAPI *StackWalk64_FctType)
- (DWORD,HANDLE,HANDLE,LPSTACKFRAME64,PVOID,PREAD_PROCESS_MEMORY_ROUTINE64,
- PFUNCTION_TABLE_ACCESS_ROUTINE64,PGET_MODULE_BASE_ROUTINE64 ,
- PTRANSLATE_ADDRESS_ROUTINE64);
-typedef BOOL (WINAPI *MiniDumpWriteDump_FctType)(
- IN HANDLE hProcess,
- IN DWORD ProcessId,
- IN HANDLE hFile,
- IN MINIDUMP_TYPE DumpType,
- IN CONST PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam, OPTIONAL
- IN CONST PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam, OPTIONAL
- IN CONST PMINIDUMP_CALLBACK_INFORMATION CallbackParam OPTIONAL
- );
-
-static SymSetOptions_FctType pSymSetOptions;
-static SymGetModuleInfo64_FctType pSymGetModuleInfo64;
-static SymGetSymFromAddr64_FctType pSymGetSymFromAddr64;
-static SymInitialize_FctType pSymInitialize;
-static StackWalk64_FctType pStackWalk64;
-static SymGetLineFromAddr64_FctType pSymGetLineFromAddr64;
-static MiniDumpWriteDump_FctType pMiniDumpWriteDump;
-
-static EXCEPTION_POINTERS *exception_ptrs;
-
-#define MODULE64_SIZE_WINXP 576
-#define STACKWALK_MAX_FRAMES 64
-
-/*
- Dynamically load dbghelp functions
-*/
-BOOL init_dbghelp_functions()
-{
- static BOOL first_time= TRUE;
- static BOOL rc;
- HMODULE hDbghlp;
-
- if(first_time)
- {
- first_time= FALSE;
- hDbghlp= LoadLibrary("dbghelp");
- if(!hDbghlp)
- {
- rc= FALSE;
- return rc;
- }
- pSymSetOptions= (SymSetOptions_FctType)
- GetProcAddress(hDbghlp,"SymSetOptions");
- pSymInitialize= (SymInitialize_FctType)
- GetProcAddress(hDbghlp,"SymInitialize");
- pSymGetModuleInfo64= (SymGetModuleInfo64_FctType)
- GetProcAddress(hDbghlp,"SymGetModuleInfo64");
- pSymGetLineFromAddr64= (SymGetLineFromAddr64_FctType)
- GetProcAddress(hDbghlp,"SymGetLineFromAddr64");
- pSymGetSymFromAddr64=(SymGetSymFromAddr64_FctType)
- GetProcAddress(hDbghlp,"SymGetSymFromAddr64");
- pStackWalk64= (StackWalk64_FctType)
- GetProcAddress(hDbghlp,"StackWalk64");
- pMiniDumpWriteDump = (MiniDumpWriteDump_FctType)
- GetProcAddress(hDbghlp,"MiniDumpWriteDump");
-
- rc = (BOOL)(pSymSetOptions && pSymInitialize && pSymGetModuleInfo64
- && pSymGetLineFromAddr64 && pSymGetSymFromAddr64 && pStackWalk64);
- }
- return rc;
-}
-
-void set_exception_pointers(EXCEPTION_POINTERS *ep)
-{
- exception_ptrs = ep;
-}
-
-
-/*
- Get symbol path - semicolon-separated list of directories to search for debug
- symbols. We expect PDB in the same directory as corresponding exe or dll,
- so the path is build from directories of the loaded modules. If environment
- variable _NT_SYMBOL_PATH is set, it's value appended to the symbol search path
-*/
-static void get_symbol_path(char *path, size_t size)
-{
- HANDLE hSnap;
- char *envvar;
-
- path[0]= '\0';
- /*
- Enumerate all modules, and add their directories to the path.
- Avoid duplicate entries.
- */
- hSnap= CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, GetCurrentProcessId());
- if (hSnap != INVALID_HANDLE_VALUE)
- {
- BOOL ret;
- MODULEENTRY32 mod;
- mod.dwSize= sizeof(MODULEENTRY32);
- for (ret= Module32First(hSnap, &mod); ret; ret= Module32Next(hSnap, &mod))
- {
- char *module_dir= mod.szExePath;
- char *p= strrchr(module_dir,'\\');
- if (!p)
- {
- /*
- Path separator was not found. Not known to happen, if ever happens,
- will indicate current directory.
- */
- module_dir[0]= '.';
- p= module_dir + 1;
- }
- *p++= ';';
- *p= '\0';
-
- if (!strstr(path, module_dir))
- {
- size_t dir_len = strlen(module_dir);
- if (size > dir_len)
- {
- strncat(path, module_dir, size-1);
- size -= dir_len;
- }
- }
- }
- CloseHandle(hSnap);
- }
-
- /* Add _NT_SYMBOL_PATH, if present. */
- envvar= getenv("_NT_SYMBOL_PATH");
- if(envvar && size)
- {
- strncat(path, envvar, size-1);
- }
-}
-
-#define MAX_SYMBOL_PATH 32768
-
-/* Platform SDK in VS2003 does not have definition for SYMOPT_NO_PROMPTS*/
-#ifndef SYMOPT_NO_PROMPTS
-#define SYMOPT_NO_PROMPTS 0
-#endif
-
-void print_stacktrace(gptr unused1, ulong unused2)
-{
- HANDLE hProcess= GetCurrentProcess();
- HANDLE hThread= GetCurrentThread();
- static IMAGEHLP_MODULE64 module= {sizeof(module)};
- static IMAGEHLP_SYMBOL64_PACKAGE package;
- DWORD64 addr;
- DWORD machine;
- int i;
- CONTEXT context;
- STACKFRAME64 frame={0};
- static char symbol_path[MAX_SYMBOL_PATH];
-
- if(!exception_ptrs || !init_dbghelp_functions())
- return;
-
- /* Copy context, as stackwalking on original will unwind the stack */
- context = *(exception_ptrs->ContextRecord);
- /*Initialize symbols.*/
- pSymSetOptions(SYMOPT_LOAD_LINES|SYMOPT_NO_PROMPTS|SYMOPT_DEFERRED_LOADS|SYMOPT_DEBUG);
- get_symbol_path(symbol_path, sizeof(symbol_path));
- pSymInitialize(hProcess, symbol_path, TRUE);
-
- /*Prepare stackframe for the first StackWalk64 call*/
- frame.AddrFrame.Mode= frame.AddrPC.Mode= frame.AddrStack.Mode= AddrModeFlat;
-#if (defined _M_IX86)
- machine= IMAGE_FILE_MACHINE_I386;
- frame.AddrFrame.Offset= context.Ebp;
- frame.AddrPC.Offset= context.Eip;
- frame.AddrStack.Offset= context.Esp;
-#elif (defined _M_X64)
- machine = IMAGE_FILE_MACHINE_AMD64;
- frame.AddrFrame.Offset= context.Rbp;
- frame.AddrPC.Offset= context.Rip;
- frame.AddrStack.Offset= context.Rsp;
-#else
- /*There is currently no need to support IA64*/
-#pragma error ("unsupported architecture")
-#endif
-
- package.sym.SizeOfStruct= sizeof(package.sym);
- package.sym.MaxNameLength= sizeof(package.name);
-
- /*Walk the stack, output useful information*/
- for(i= 0; i< STACKWALK_MAX_FRAMES;i++)
- {
- DWORD64 function_offset= 0;
- DWORD line_offset= 0;
- IMAGEHLP_LINE64 line= {sizeof(line)};
- BOOL have_module= FALSE;
- BOOL have_symbol= FALSE;
- BOOL have_source= FALSE;
-
- if(!pStackWalk64(machine, hProcess, hThread, &frame, &context, 0, 0, 0 ,0))
- break;
- addr= frame.AddrPC.Offset;
-
- have_module= pSymGetModuleInfo64(hProcess,addr,&module);
-#ifdef _M_IX86
- if(!have_module)
- {
- /*
- ModuleInfo structure has been "compatibly" extended in releases after XP,
- and its size was increased. To make XP dbghelp.dll function
- happy, pretend passing the old structure.
- */
- module.SizeOfStruct= MODULE64_SIZE_WINXP;
- have_module= pSymGetModuleInfo64(hProcess, addr, &module);
- }
-#endif
-
- have_symbol= pSymGetSymFromAddr64(hProcess, addr, &function_offset,
- &(package.sym));
- have_source= pSymGetLineFromAddr64(hProcess, addr, &line_offset, &line);
-
- fprintf(stderr, "%p ", addr);
- if(have_module)
- {
- char *base_image_name= strrchr(module.ImageName, '\\');
- if(base_image_name)
- base_image_name++;
- else
- base_image_name= module.ImageName;
- fprintf(stderr, "%s!", base_image_name);
- }
- if(have_symbol)
- fprintf(stderr, "%s()", package.sym.Name);
- else if(have_module)
- fprintf(stderr, "???");
-
- if(have_source)
- {
- char *base_file_name= strrchr(line.FileName, '\\');
- if(base_file_name)
- base_file_name++;
- else
- base_file_name= line.FileName;
- fprintf(stderr,"[%s:%u]", base_file_name, line.LineNumber);
- }
- fprintf(stderr, "\n");
- }
- fflush(stderr);
-}
-
-
-/*
- Write dump. The dump is created in current directory,
- file name is constructed from executable name plus
- ".dmp" extension
-*/
-void write_core(int unused)
-{
- char path[MAX_PATH];
- char dump_fname[MAX_PATH]= "core.dmp";
- MINIDUMP_EXCEPTION_INFORMATION info;
- HANDLE hFile;
-
- if(!exception_ptrs || !init_dbghelp_functions() || !pMiniDumpWriteDump)
- return;
-
- info.ExceptionPointers= exception_ptrs;
- info.ClientPointers= FALSE;
- info.ThreadId= GetCurrentThreadId();
-
- if(GetModuleFileName(NULL, path, sizeof(path)))
- {
- _splitpath(path, NULL, NULL,dump_fname,NULL);
- strncat(dump_fname, ".dmp", sizeof(dump_fname));
- }
-
- hFile= CreateFile(dump_fname, GENERIC_WRITE, 0, 0, CREATE_ALWAYS,
- FILE_ATTRIBUTE_NORMAL, 0);
- if(hFile)
- {
- /* Create minidump */
- if(pMiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(),
- hFile, MiniDumpNormal, &info, 0, 0))
- {
- fprintf(stderr, "Minidump written to %s\n",
- _fullpath(path, dump_fname, sizeof(path)) ? path : dump_fname);
- }
- else
- {
- fprintf(stderr,"MiniDumpWriteDump() failed, last error %u\n",
- GetLastError());
- }
- CloseHandle(hFile);
- }
- else
- {
- fprintf(stderr, "CreateFile(%s) failed, last error %u\n", dump_fname,
- GetLastError());
- }
- fflush(stderr);
-}
-
-
-void safe_print_str(const char *name, const char *val, int len)
-{
- fprintf(stderr,"%s at %p", name, val);
- __try
- {
- fprintf(stderr,"=%.*s\n", len, val);
- }
- __except(EXCEPTION_EXECUTE_HANDLER)
- {
- fprintf(stderr,"is an invalid string pointer\n");
- }
-}
-#endif /*__WIN__*/
diff --git a/sql/stacktrace.h b/sql/stacktrace.h
deleted file mode 100644
index e5e17cc5b9b..00000000000
--- a/sql/stacktrace.h
+++ /dev/null
@@ -1,62 +0,0 @@
-/* Copyright (C) 2000 MySQL AB
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; version 2 of the License.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#ifdef TARGET_OS_LINUX
-#if defined(HAVE_STACKTRACE) || (defined (__x86_64__) || defined (__i386__) || (defined(__alpha__) && defined(__GNUC__)))
-#undef HAVE_STACKTRACE
-#define HAVE_STACKTRACE
-
-extern char* __bss_start;
-extern char* heap_start;
-
-#define init_stacktrace() do { \
- heap_start = (char*) &__bss_start; \
- check_thread_lib(); \
- } while(0);
-void check_thread_lib(void);
-#endif /* defined (__i386__) || (defined(__alpha__) && defined(__GNUC__))) */
-#elif defined (__WIN__)
-#define HAVE_STACKTRACE
-extern void set_exception_pointers(EXCEPTION_POINTERS *ep);
-#define init_stacktrace() {}
-#endif
-
-#ifdef HAVE_STACKTRACE
-void print_stacktrace(gptr stack_bottom, ulong thread_stack);
-void safe_print_str(const char* name, const char* val, int max_len);
-#else
-/* Define empty prototypes for functions that are not implemented */
-#define init_stacktrace() {}
-#define print_stacktrace(A,B) {}
-#define safe_print_str(A,B,C) {}
-#endif /* HAVE_STACKTRACE */
-
-
-#if !defined(__NETWARE__)
-#define HAVE_WRITE_CORE
-#endif
-
-#ifdef HAVE_WRITE_CORE
-void write_core(int sig);
-#endif
-
-
-#ifdef __cplusplus
-}
-#endif
diff --git a/sql/strfunc.cc b/sql/strfunc.cc
index 308e6fd3dcd..c03365cfc2b 100644
--- a/sql/strfunc.cc
+++ b/sql/strfunc.cc
@@ -104,7 +104,8 @@ ulonglong find_set(TYPELIB *lib, const char *str, uint length, CHARSET_INFO *cs,
> 0 position in TYPELIB->type_names +1
*/
-uint find_type(TYPELIB *lib, const char *find, uint length, bool part_match)
+uint find_type(const TYPELIB *lib, const char *find, uint length,
+ bool part_match)
{
uint found_count=0, found_pos=0;
const char *end= find+length;
@@ -144,7 +145,8 @@ uint find_type(TYPELIB *lib, const char *find, uint length, bool part_match)
>0 Offset+1 in typelib for matched string
*/
-uint find_type2(TYPELIB *typelib, const char *x, uint length, CHARSET_INFO *cs)
+uint find_type2(const TYPELIB *typelib, const char *x, uint length,
+ CHARSET_INFO *cs)
{
int pos;
const char *j;
@@ -234,3 +236,108 @@ uint check_word(TYPELIB *lib, const char *val, const char *end,
*end_of_word= ptr;
return res;
}
+
+
+/*
+ Converts a string between character sets
+
+ SYNOPSIS
+ strconvert()
+ from_cs source character set
+ from source, a null terminated string
+ to destination buffer
+ to_length destination buffer length
+
+ NOTES
+ 'to' is always terminated with a '\0' character.
+ If there is no enough space to convert whole string,
+ only prefix is converted, and terminated with '\0'.
+
+ RETURN VALUES
+ result string length
+*/
+
+
+uint strconvert(CHARSET_INFO *from_cs, const char *from,
+ CHARSET_INFO *to_cs, char *to, uint to_length, uint *errors)
+{
+ int cnvres;
+ my_wc_t wc;
+ char *to_start= to;
+ uchar *to_end= (uchar*) to + to_length - 1;
+ my_charset_conv_mb_wc mb_wc= from_cs->cset->mb_wc;
+ my_charset_conv_wc_mb wc_mb= to_cs->cset->wc_mb;
+ uint error_count= 0;
+
+ while (1)
+ {
+ /*
+ Using 'from + 10' is safe:
+ - it is enough to scan a single character in any character set.
+ - if remaining string is shorter than 10, then mb_wc will return
+ with error because of unexpected '\0' character.
+ */
+ if ((cnvres= (*mb_wc)(from_cs, &wc,
+ (uchar*) from, (uchar*) from + 10)) > 0)
+ {
+ if (!wc)
+ break;
+ from+= cnvres;
+ }
+ else if (cnvres == MY_CS_ILSEQ)
+ {
+ error_count++;
+ from++;
+ wc= '?';
+ }
+ else
+ break; // Impossible char.
+
+outp:
+
+ if ((cnvres= (*wc_mb)(to_cs, wc, (uchar*) to, to_end)) > 0)
+ to+= cnvres;
+ else if (cnvres == MY_CS_ILUNI && wc != '?')
+ {
+ error_count++;
+ wc= '?';
+ goto outp;
+ }
+ else
+ break;
+ }
+ *to= '\0';
+ *errors= error_count;
+ return (uint32) (to - to_start);
+
+}
+
+
+/*
+ Searches for a LEX_STRING in an LEX_STRING array.
+
+ SYNOPSIS
+ find_string_in_array()
+ heap The array
+ needle The string to search for
+
+ NOTE
+ The last LEX_STRING in the array should have str member set to NULL
+
+ RETURN VALUES
+ -1 Not found
+ >=0 Ordinal position
+*/
+
+int find_string_in_array(LEX_STRING * const haystack, LEX_STRING * const needle,
+ CHARSET_INFO * const cs)
+{
+ const LEX_STRING *pos;
+ for (pos= haystack; pos->str; pos++)
+ if (!cs->coll->strnncollsp(cs, (uchar *) pos->str, pos->length,
+ (uchar *) needle->str, needle->length, 0))
+ {
+ return (pos - haystack);
+ }
+ return -1;
+}
diff --git a/sql/structs.h b/sql/structs.h
index 9882119b7c5..0a20eee0e9a 100644
--- a/sql/structs.h
+++ b/sql/structs.h
@@ -19,22 +19,6 @@
struct st_table;
class Field;
-typedef struct st_lex_string
-{
- char *str;
- uint length;
-} LEX_STRING;
-
-typedef struct st_lex_string_with_init :public st_lex_string
-{
- st_lex_string_with_init(const char *str_arg, uint length_arg)
- {
- str= (char*) str_arg;
- length= length_arg;
- }
-} LEX_STRING_WITH_INIT;
-
-
typedef struct st_date_time_format {
uchar positions[8];
char time_separator; /* Separator between hour and minute */
@@ -44,8 +28,8 @@ typedef struct st_date_time_format {
typedef struct st_keyfile_info { /* used with ha_info() */
- byte ref[MAX_REFLENGTH]; /* Pointer to current row */
- byte dupp_ref[MAX_REFLENGTH]; /* Pointer to dupp row */
+ uchar ref[MAX_REFLENGTH]; /* Pointer to current row */
+ uchar dupp_ref[MAX_REFLENGTH]; /* Pointer to dupp row */
uint ref_length; /* Length of ref (1-8) */
uint block_size; /* index block size */
File filenr; /* (uniq) filenr for table */
@@ -91,7 +75,17 @@ typedef struct st_key {
uint key_parts; /* How many key_parts */
uint extra_length;
uint usable_key_parts; /* Should normally be = key_parts */
+ uint block_size;
enum ha_key_alg algorithm;
+ /*
+ Note that parser is used when the table is opened for use, and
+ parser_name is used when the table is being created.
+ */
+ union
+ {
+ plugin_ref parser; /* Fulltext [pre]parser */
+ LEX_STRING *parser_name; /* Fulltext [pre]parser name */
+ };
KEY_PART_INFO *key_part;
char *name; /* Name of key */
/*
@@ -132,10 +126,10 @@ typedef struct st_read_record { /* Parameter to read_record */
uint cache_records;
uint ref_length,struct_length,reclength,rec_cache_size,error_offset;
uint index;
- byte *ref_pos; /* pointer to form->refpos */
- byte *record;
- byte *rec_buf; /* to read field values after filesort */
- byte *cache,*cache_pos,*cache_end,*read_positions;
+ 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;
} READ_RECORD;
@@ -164,55 +158,16 @@ typedef struct st_known_date_time_format {
const char *time_format;
} KNOWN_DATE_TIME_FORMAT;
-
-enum SHOW_TYPE
-{
- SHOW_UNDEF,
- SHOW_LONG, SHOW_LONGLONG, SHOW_INT, SHOW_CHAR, SHOW_CHAR_PTR,
- SHOW_DOUBLE_STATUS,
- SHOW_BOOL, SHOW_MY_BOOL, SHOW_OPENTABLES, SHOW_STARTTIME, SHOW_QUERIES,
- SHOW_LONG_CONST, SHOW_INT_CONST, SHOW_HAVE, SHOW_SYS, SHOW_HA_ROWS,
- SHOW_VARS,
-#ifdef HAVE_OPENSSL
- SHOW_SSL_CTX_SESS_ACCEPT, SHOW_SSL_CTX_SESS_ACCEPT_GOOD,
- SHOW_SSL_GET_VERSION, SHOW_SSL_CTX_GET_SESSION_CACHE_MODE,
- SHOW_SSL_CTX_SESS_CB_HITS, SHOW_SSL_CTX_SESS_ACCEPT_RENEGOTIATE,
- SHOW_SSL_CTX_SESS_NUMBER, SHOW_SSL_SESSION_REUSED,
- SHOW_SSL_CTX_SESS_GET_CACHE_SIZE, SHOW_SSL_GET_CIPHER,
- SHOW_SSL_GET_DEFAULT_TIMEOUT, SHOW_SSL_GET_VERIFY_MODE,
- SHOW_SSL_CTX_GET_VERIFY_MODE, SHOW_SSL_GET_VERIFY_DEPTH,
- SHOW_SSL_CTX_GET_VERIFY_DEPTH, SHOW_SSL_CTX_SESS_CONNECT,
- SHOW_SSL_CTX_SESS_CONNECT_RENEGOTIATE, SHOW_SSL_CTX_SESS_CONNECT_GOOD,
- SHOW_SSL_CTX_SESS_HITS, SHOW_SSL_CTX_SESS_MISSES,
- SHOW_SSL_CTX_SESS_TIMEOUTS, SHOW_SSL_CTX_SESS_CACHE_FULL,
- SHOW_SSL_GET_CIPHER_LIST,
-#endif /* HAVE_OPENSSL */
- SHOW_NET_COMPRESSION,
- SHOW_RPL_STATUS, SHOW_SLAVE_RUNNING, SHOW_SLAVE_RETRIED_TRANS,
- SHOW_KEY_CACHE_LONG, SHOW_KEY_CACHE_CONST_LONG, SHOW_KEY_CACHE_LONGLONG,
- SHOW_LONG_STATUS, SHOW_LONG_CONST_STATUS, SHOW_SLAVE_SKIP_ERRORS,
- SHOW_LONGLONG_STATUS
-};
-
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 show_var_st *);
-
-
-typedef struct show_var_st {
- const char *name;
- char *value;
- SHOW_TYPE type;
-} SHOW_VAR;
-
+typedef int *(*update_var)(THD *, struct st_mysql_show_var *);
typedef struct st_lex_user {
LEX_STRING user, host, password;
} LEX_USER;
-
/*
This structure specifies the maximum amount of resources which
can be consumed by each account. Zero value of a member means
@@ -223,7 +178,7 @@ typedef struct user_resources {
uint questions;
/*
Maximum number of updating statements per hour (which statements are
- updating is defined by uc_update_queries array).
+ updating is defined by sql_command_flags array).
*/
uint updates;
/* Maximum number of connections established per hour. */
@@ -254,6 +209,11 @@ typedef struct user_conn {
char *user;
/* Pointer to host part of the key. */
char *host;
+ /**
+ The moment of time when per hour counters were reset last time
+ (i.e. start of "hour" for conn_per_hour, updates, questions counters).
+ */
+ ulonglong reset_utime;
/* Total length of the key. */
uint len;
/* Current amount of concurrent connections for this account. */
@@ -265,11 +225,6 @@ typedef struct user_conn {
uint conn_per_hour, updates, questions;
/* Maximum amount of resources which account is allowed to consume. */
USER_RESOURCES user_resources;
- /*
- The moment of time when per hour counters were reset last time
- (i.e. start of "hour" for conn_per_hour, updates, questions counters).
- */
- time_t intime;
} USER_CONN;
/* Bits in form->update */
@@ -293,3 +248,131 @@ typedef struct user_conn {
#define STATUS_UPDATED 16 /* Record is updated by formula */
#define STATUS_NULL_ROW 32 /* table->null_row is set */
#define STATUS_DELETED 64
+
+/*
+ Such interval is "discrete": it is the set of
+ { auto_inc_interval_min + k * increment,
+ 0 <= k <= (auto_inc_interval_values-1) }
+ Where "increment" is maintained separately by the user of this class (and is
+ currently only thd->variables.auto_increment_increment).
+ It mustn't derive from Sql_alloc, because SET INSERT_ID needs to
+ allocate memory which must stay allocated for use by the next statement.
+*/
+class Discrete_interval {
+private:
+ ulonglong interval_min;
+ ulonglong interval_values;
+ ulonglong interval_max; // excluded bound. Redundant.
+public:
+ Discrete_interval *next; // used when linked into Discrete_intervals_list
+ void replace(ulonglong start, ulonglong val, ulonglong incr)
+ {
+ interval_min= start;
+ interval_values= val;
+ interval_max= (val == ULONGLONG_MAX) ? val : start + val * incr;
+ }
+ Discrete_interval(ulonglong start, ulonglong val, ulonglong incr) :
+ next(NULL) { replace(start, val, incr); };
+ Discrete_interval() : next(NULL) { replace(0, 0, 0); };
+ ulonglong minimum() const { return interval_min; };
+ ulonglong values() const { return interval_values; };
+ ulonglong maximum() const { return interval_max; };
+ /*
+ If appending [3,5] to [1,2], we merge both in [1,5] (they should have the
+ same increment for that, user of the class has to ensure that). That is
+ just a space optimization. Returns 0 if merge succeeded.
+ */
+ bool merge_if_contiguous(ulonglong start, ulonglong val, ulonglong incr)
+ {
+ if (interval_max == start)
+ {
+ if (val == ULONGLONG_MAX)
+ {
+ interval_values= interval_max= val;
+ }
+ else
+ {
+ interval_values+= val;
+ interval_max= start + val * incr;
+ }
+ return 0;
+ }
+ return 1;
+ };
+};
+
+/* List of Discrete_interval objects */
+class Discrete_intervals_list {
+private:
+ Discrete_interval *head;
+ Discrete_interval *tail;
+ /*
+ When many intervals are provided at the beginning of the execution of a
+ statement (in a replication slave or SET INSERT_ID), "current" points to
+ the interval being consumed by the thread now (so "current" goes from
+ "head" to "tail" then to NULL).
+ */
+ Discrete_interval *current;
+ uint elements; // number of elements
+ void set_members(Discrete_interval *h, Discrete_interval *t,
+ Discrete_interval *c, uint el)
+ {
+ head= h;
+ tail= t;
+ current= c;
+ elements= el;
+ }
+ void operator=(Discrete_intervals_list &); /* prevent use of these */
+ Discrete_intervals_list(const Discrete_intervals_list &);
+
+public:
+ Discrete_intervals_list() : head(NULL), current(NULL), elements(0) {};
+ void empty_no_free()
+ {
+ set_members(NULL, NULL, NULL, 0);
+ }
+ void empty()
+ {
+ for (Discrete_interval *i= head; i;)
+ {
+ Discrete_interval *next= i->next;
+ delete i;
+ i= next;
+ }
+ empty_no_free();
+ }
+ void copy_shallow(const Discrete_intervals_list * dli)
+ {
+ head= dli->get_head();
+ tail= dli->get_tail();
+ current= dli->get_current();
+ elements= dli->nb_elements();
+ }
+ void swap (Discrete_intervals_list * dli)
+ {
+ Discrete_interval *h, *t, *c;
+ uint el;
+ h= dli->get_head();
+ t= dli->get_tail();
+ c= dli->get_current();
+ el= dli->nb_elements();
+ dli->copy_shallow(this);
+ set_members(h, t, c, el);
+ }
+ const Discrete_interval* get_next()
+ {
+ Discrete_interval *tmp= current;
+ if (current != NULL)
+ current= current->next;
+ return tmp;
+ }
+ ~Discrete_intervals_list() { empty(); };
+ bool append(ulonglong start, ulonglong val, ulonglong incr);
+ bool append(Discrete_interval *interval);
+ ulonglong minimum() const { return (head ? head->minimum() : 0); };
+ ulonglong maximum() const { return (head ? tail->maximum() : 0); };
+ uint nb_elements() const { return elements; }
+ Discrete_interval* get_head() const { return head; };
+ Discrete_interval* get_tail() const { return tail; };
+ Discrete_interval* get_current() const { return current; };
+};
diff --git a/sql/table.cc b/sql/table.cc
index b5126633469..17454ffb012 100644
--- a/sql/table.cc
+++ b/sql/table.cc
@@ -1,4 +1,4 @@
-/* Copyright (C) 2000-2006 MySQL AB
+/* Copyright 2000-2008 MySQL AB, 2008 Sun Microsystems, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -17,142 +17,677 @@
/* Some general useful functions */
#include "mysql_priv.h"
-#include <errno.h>
+#include "sql_trigger.h"
#include <m_ctype.h>
#include "my_md5.h"
+/* INFORMATION_SCHEMA name */
+LEX_STRING INFORMATION_SCHEMA_NAME= {C_STRING_WITH_LEN("information_schema")};
+
+/* MYSQL_SCHEMA name */
+LEX_STRING MYSQL_SCHEMA_NAME= {C_STRING_WITH_LEN("mysql")};
+
+/* GENERAL_LOG name */
+LEX_STRING GENERAL_LOG_NAME= {C_STRING_WITH_LEN("general_log")};
+
+/* SLOW_LOG name */
+LEX_STRING SLOW_LOG_NAME= {C_STRING_WITH_LEN("slow_log")};
+
/* Functions defined in this file */
-static void frm_error(int error,TABLE *form,const char *name,
- int errortype, int errarg);
+void open_table_error(TABLE_SHARE *share, int error, int db_errno,
+ myf errortype, int errarg);
+static int open_binary_frm(THD *thd, TABLE_SHARE *share,
+ uchar *head, File file);
static void fix_type_pointers(const char ***array, TYPELIB *point_to_type,
uint types, char **names);
-static uint find_field(TABLE *form,uint start,uint length);
+static uint find_field(Field **fields, uchar *record, uint start, uint length);
+
+inline bool is_system_table_name(const char *name, uint length);
+
+/**************************************************************************
+ Object_creation_ctx implementation.
+**************************************************************************/
+
+Object_creation_ctx *Object_creation_ctx::set_n_backup(THD *thd)
+{
+ Object_creation_ctx *backup_ctx;
+ DBUG_ENTER("Object_creation_ctx::set_n_backup");
+
+ backup_ctx= create_backup_ctx(thd);
+ change_env(thd);
+
+ DBUG_RETURN(backup_ctx);
+}
+
+void Object_creation_ctx::restore_env(THD *thd, Object_creation_ctx *backup_ctx)
+{
+ if (!backup_ctx)
+ return;
+
+ backup_ctx->change_env(thd);
+
+ delete backup_ctx;
+}
+
+/**************************************************************************
+ Default_object_creation_ctx implementation.
+**************************************************************************/
+
+Default_object_creation_ctx::Default_object_creation_ctx(THD *thd)
+ : m_client_cs(thd->variables.character_set_client),
+ m_connection_cl(thd->variables.collation_connection)
+{ }
+
+Default_object_creation_ctx::Default_object_creation_ctx(
+ CHARSET_INFO *client_cs, CHARSET_INFO *connection_cl)
+ : m_client_cs(client_cs),
+ m_connection_cl(connection_cl)
+{ }
+
+Object_creation_ctx *
+Default_object_creation_ctx::create_backup_ctx(THD *thd) const
+{
+ return new Default_object_creation_ctx(thd);
+}
+
+void Default_object_creation_ctx::change_env(THD *thd) const
+{
+ thd->variables.character_set_client= m_client_cs;
+ thd->variables.collation_connection= m_connection_cl;
+ thd->update_charset();
+}
+
+/**************************************************************************
+ View_creation_ctx implementation.
+**************************************************************************/
+
+View_creation_ctx *View_creation_ctx::create(THD *thd)
+{
+ View_creation_ctx *ctx= new (thd->mem_root) View_creation_ctx(thd);
+
+ return ctx;
+}
+
+/*************************************************************************/
-static byte* get_field_name(Field **buff,uint *length,
- my_bool not_used __attribute__((unused)))
+View_creation_ctx * View_creation_ctx::create(THD *thd,
+ TABLE_LIST *view)
+{
+ View_creation_ctx *ctx= new (thd->mem_root) View_creation_ctx(thd);
+
+ /* Throw a warning if there is NULL cs name. */
+
+ if (!view->view_client_cs_name.str ||
+ !view->view_connection_cl_name.str)
+ {
+ push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
+ ER_VIEW_NO_CREATION_CTX,
+ ER(ER_VIEW_NO_CREATION_CTX),
+ (const char *) view->db,
+ (const char *) view->table_name);
+
+ ctx->m_client_cs= system_charset_info;
+ ctx->m_connection_cl= system_charset_info;
+
+ return ctx;
+ }
+
+ /* Resolve cs names. Throw a warning if there is unknown cs name. */
+
+ bool invalid_creation_ctx;
+
+ invalid_creation_ctx= resolve_charset(view->view_client_cs_name.str,
+ system_charset_info,
+ &ctx->m_client_cs);
+
+ invalid_creation_ctx= resolve_collation(view->view_connection_cl_name.str,
+ system_charset_info,
+ &ctx->m_connection_cl) ||
+ invalid_creation_ctx;
+
+ if (invalid_creation_ctx)
+ {
+ sql_print_warning("View '%s'.'%s': there is unknown charset/collation "
+ "names (client: '%s'; connection: '%s').",
+ (const char *) view->db,
+ (const char *) view->table_name,
+ (const char *) view->view_client_cs_name.str,
+ (const char *) view->view_connection_cl_name.str);
+
+ push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
+ ER_VIEW_INVALID_CREATION_CTX,
+ ER(ER_VIEW_INVALID_CREATION_CTX),
+ (const char *) view->db,
+ (const char *) view->table_name);
+ }
+
+ return ctx;
+}
+
+/*************************************************************************/
+
+/* Get column name from column hash */
+
+static uchar *get_field_name(Field **buff, size_t *length,
+ my_bool not_used __attribute__((unused)))
{
*length= (uint) strlen((*buff)->field_name);
- return (byte*) (*buff)->field_name;
+ return (uchar*) (*buff)->field_name;
+}
+
+
+/*
+ Returns pointer to '.frm' extension of the file name.
+
+ SYNOPSIS
+ fn_rext()
+ name file name
+
+ DESCRIPTION
+ Checks file name part starting with the rightmost '.' character,
+ and returns it if it is equal to '.frm'.
+
+ TODO
+ It is a good idea to get rid of this function modifying the code
+ to garantee that the functions presently calling fn_rext() always
+ get arguments in the same format: either with '.frm' or without '.frm'.
+
+ RETURN VALUES
+ Pointer to the '.frm' extension. If there is no extension,
+ or extension is not '.frm', pointer at the end of file name.
+*/
+
+char *fn_rext(char *name)
+{
+ char *res= strrchr(name, '.');
+ if (res && !strcmp(res, reg_ext))
+ return res;
+ return name + strlen(name);
+}
+
+TABLE_CATEGORY get_table_category(const LEX_STRING *db, const LEX_STRING *name)
+{
+ DBUG_ASSERT(db != NULL);
+ DBUG_ASSERT(name != NULL);
+
+ if ((db->length == INFORMATION_SCHEMA_NAME.length) &&
+ (my_strcasecmp(system_charset_info,
+ INFORMATION_SCHEMA_NAME.str,
+ db->str) == 0))
+ {
+ return TABLE_CATEGORY_INFORMATION;
+ }
+
+ if ((db->length == MYSQL_SCHEMA_NAME.length) &&
+ (my_strcasecmp(system_charset_info,
+ 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;
+ }
+
+ if ((name->length == SLOW_LOG_NAME.length) &&
+ (my_strcasecmp(system_charset_info,
+ SLOW_LOG_NAME.str,
+ name->str) == 0))
+ {
+ return TABLE_CATEGORY_PERFORMANCE;
+ }
+ }
+
+ return TABLE_CATEGORY_USER;
+}
+
+
+/*
+ Allocate a setup TABLE_SHARE structure
+
+ SYNOPSIS
+ alloc_table_share()
+ TABLE_LIST Take database and table name from there
+ key Table cache key (db \0 table_name \0...)
+ key_length Length of key
+
+ RETURN
+ 0 Error (out of memory)
+ # Share
+*/
+
+TABLE_SHARE *alloc_table_share(TABLE_LIST *table_list, char *key,
+ uint key_length)
+{
+ MEM_ROOT mem_root;
+ TABLE_SHARE *share;
+ char *key_buff, *path_buff;
+ char path[FN_REFLEN];
+ uint path_length;
+ DBUG_ENTER("alloc_table_share");
+ DBUG_PRINT("enter", ("table: '%s'.'%s'",
+ table_list->db, table_list->table_name));
+
+ path_length= build_table_filename(path, sizeof(path) - 1,
+ table_list->db,
+ table_list->table_name, "", 0);
+ init_sql_alloc(&mem_root, TABLE_ALLOC_BLOCK_SIZE, 0);
+ if (multi_alloc_root(&mem_root,
+ &share, sizeof(*share),
+ &key_buff, key_length,
+ &path_buff, path_length + 1,
+ NULL))
+ {
+ bzero((char*) share, sizeof(*share));
+
+ share->set_table_cache_key(key_buff, key, key_length);
+
+ share->path.str= path_buff;
+ share->path.length= path_length;
+ strmov(share->path.str, path);
+ share->normalized_path.str= share->path.str;
+ share->normalized_path.length= path_length;
+
+ share->version= refresh_version;
+
+ /*
+ This constant is used to mark that no table map version has been
+ assigned. No arithmetic is done on the value: it will be
+ overwritten with a value taken from MYSQL_BIN_LOG.
+ */
+ share->table_map_version= ~(ulonglong)0;
+
+ /*
+ Since alloc_table_share() can be called without any locking (for
+ example, ha_create_table... functions), we do not assign a table
+ map id here. Instead we assign a value that is not used
+ elsewhere, and then assign a table map id inside open_table()
+ under the protection of the LOCK_open mutex.
+ */
+ share->table_map_id= ~0UL;
+ share->cached_row_logging_check= -1;
+
+ 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);
+ }
+ DBUG_RETURN(share);
+}
+
+
+/*
+ Initialize share for temporary tables
+
+ SYNOPSIS
+ init_tmp_table_share()
+ thd thread handle
+ share Share to fill
+ key Table_cache_key, as generated from create_table_def_key.
+ must start with db name.
+ key_length Length of key
+ table_name Table name
+ path Path to file (possible in lower case) without .frm
+
+ NOTES
+ This is different from alloc_table_share() because temporary tables
+ don't have to be shared between threads or put into the table def
+ cache, so we can do some things notable simpler and faster
+
+ If table is not put in thd->temporary_tables (happens only when
+ one uses OPEN TEMPORARY) then one can specify 'db' as key and
+ use key_length= 0 as neither table_cache_key or key_length will be used).
+*/
+
+void init_tmp_table_share(THD *thd, TABLE_SHARE *share, const char *key,
+ uint key_length, const char *table_name,
+ const char *path)
+{
+ DBUG_ENTER("init_tmp_table_share");
+ DBUG_PRINT("enter", ("table: '%s'.'%s'", key, table_name));
+
+ bzero((char*) share, sizeof(*share));
+ init_sql_alloc(&share->mem_root, TABLE_ALLOC_BLOCK_SIZE, 0);
+ share->table_category= TABLE_CATEGORY_TEMPORARY;
+ share->tmp_table= INTERNAL_TMP_TABLE;
+ share->db.str= (char*) key;
+ share->db.length= strlen(key);
+ share->table_cache_key.str= (char*) key;
+ share->table_cache_key.length= key_length;
+ share->table_name.str= (char*) table_name;
+ share->table_name.length= strlen(table_name);
+ share->path.str= (char*) path;
+ share->normalized_path.str= (char*) path;
+ share->path.length= share->normalized_path.length= strlen(path);
+ share->frm_version= FRM_VER_TRUE_VARCHAR;
+
+ /*
+ Temporary tables are not replicated, but we set up these fields
+ anyway to be able to catch errors.
+ */
+ share->table_map_version= ~(ulonglong)0;
+ share->cached_row_logging_check= -1;
+
+ /*
+ table_map_id is also used for MERGE tables to suppress repeated
+ compatibility checks.
+ */
+ share->table_map_id= (ulong) thd->query_id;
+
+ DBUG_VOID_RETURN;
}
+
/*
- Open a .frm file
+ Free table share and memory used by it
SYNOPSIS
- openfrm()
+ 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)
+ {
+ /* 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;
+ }
+ }
+ /* 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;
+}
+
- name path to table-file "db/name"
- alias alias for table
- db_stat open flags (for example HA_OPEN_KEYFILE|HA_OPEN_RNDFILE..)
- can be 0 (example in ha_example_table)
- prgflag READ_ALL etc..
- ha_open_flags HA_OPEN_ABORT_IF_LOCKED etc..
- outparam result table
+/**
+ Return TRUE if a table name matches one of the system table names.
+ Currently these are:
+
+ help_category, help_keyword, help_relation, help_topic,
+ proc, event
+ time_zone, time_zone_leap_second, time_zone_name, time_zone_transition,
+ time_zone_transition_type
+
+ This function trades accuracy for speed, so may return false
+ positives. Presumably mysql.* database is for internal purposes only
+ and should not contain user tables.
+*/
+
+inline bool is_system_table_name(const char *name, uint length)
+{
+ CHARSET_INFO *ci= system_charset_info;
+
+ return (
+ /* mysql.proc table */
+ length == 4 &&
+ my_tolower(ci, name[0]) == 'p' &&
+ my_tolower(ci, name[1]) == 'r' &&
+ my_tolower(ci, name[2]) == 'o' &&
+ my_tolower(ci, name[3]) == 'c' ||
+
+ length > 4 &&
+ (
+ /* one of mysql.help* tables */
+ my_tolower(ci, name[0]) == 'h' &&
+ my_tolower(ci, name[1]) == 'e' &&
+ my_tolower(ci, name[2]) == 'l' &&
+ my_tolower(ci, name[3]) == 'p' ||
+
+ /* one of mysql.time_zone* tables */
+ my_tolower(ci, name[0]) == 't' &&
+ my_tolower(ci, name[1]) == 'i' &&
+ my_tolower(ci, name[2]) == 'm' &&
+ my_tolower(ci, name[3]) == 'e' ||
+
+ /* mysql.event table */
+ my_tolower(ci, name[0]) == 'e' &&
+ my_tolower(ci, name[1]) == 'v' &&
+ my_tolower(ci, name[2]) == 'e' &&
+ my_tolower(ci, name[3]) == 'n' &&
+ my_tolower(ci, name[4]) == 't'
+ )
+ );
+}
+
+
+/*
+ Read table definition from a binary / text based .frm file
+
+ SYNOPSIS
+ open_table_def()
+ thd Thread handler
+ share Fill this with table definition
+ db_flags Bit mask of the following flags: OPEN_VIEW
+
+ NOTES
+ This function is called when the table definition is not cached in
+ table_def_cache
+ The data is returned in 'share', which is alloced by
+ alloc_table_share().. The code assumes that share is initialized.
RETURN VALUES
0 ok
- 1 Error (see frm_error)
- 2 Error (see frm_error)
+ 1 Error (see open_table_error)
+ 2 Error (see open_table_error)
3 Wrong data in .frm file
- 4 Error (see frm_error)
- 5 Error (see frm_error: charset unavailable)
+ 4 Error (see open_table_error)
+ 5 Error (see open_table_error: charset unavailable)
6 Unknown .frm version
*/
-int openfrm(THD *thd, const char *name, const char *alias, uint db_stat,
- uint prgflag, uint ha_open_flags, TABLE *outparam)
-{
- reg1 uint i;
- reg2 uchar *strpos;
- int j,error, errarg= 0;
- uint rec_buff_length,n_length,int_length,records,key_parts,keys,
- interval_count,interval_parts,read_length,db_create_options;
- uint key_info_length, com_length;
- ulong pos, record_offset;
- char index_file[FN_REFLEN], *names, *keynames, *comment_pos;
- uchar head[288],*disk_buff,new_field_pack_flag;
- my_string record;
- const char **int_array;
- bool use_hash, null_field_first;
- bool error_reported= FALSE;
- File file;
- Field **field_ptr,*reg_field;
- KEY *keyinfo;
- KEY_PART_INFO *key_part;
- uchar *null_pos;
- uint null_bit_pos, new_frm_ver, field_pack_length;
- SQL_CRYPT *crypted=0;
+int open_table_def(THD *thd, TABLE_SHARE *share, uint db_flags)
+{
+ int error, table_type;
+ bool error_given;
+ File file;
+ uchar head[288], *disk_buff;
+ char path[FN_REFLEN];
MEM_ROOT **root_ptr, *old_root;
- TABLE_SHARE *share;
- DBUG_ENTER("openfrm");
- DBUG_PRINT("enter",("name: '%s' form: 0x%lx", name, (long) outparam));
+ DBUG_ENTER("open_table_def");
+ DBUG_PRINT("enter", ("table: '%s'.'%s' path: '%s'", share->db.str,
+ share->table_name.str, share->normalized_path.str));
error= 1;
+ error_given= 0;
disk_buff= NULL;
- root_ptr= my_pthread_getspecific_ptr(MEM_ROOT**, THR_MALLOC);
- old_root= *root_ptr;
-
- bzero((char*) outparam,sizeof(*outparam));
- outparam->in_use= thd;
- outparam->s= share= &outparam->share_not_to_be_used;
- if ((file=my_open(fn_format(index_file, name, "", reg_ext,
- MY_UNPACK_FILENAME),
- O_RDONLY | O_SHARE,
- MYF(0)))
- < 0)
- goto err;
+ strxmov(path, share->normalized_path.str, reg_ext, NullS);
+ if ((file= my_open(path, O_RDONLY | O_SHARE, MYF(0))) < 0)
+ {
+ /*
+ We don't try to open 5.0 unencoded name, if
+ - non-encoded name contains '@' signs,
+ because '@' can be misinterpreted.
+ It is not clear if '@' is escape character in 5.1,
+ or a normal character in 5.0.
+
+ - non-encoded db or table name contain "#mysql50#" prefix.
+ This kind of tables must have been opened only by the
+ my_open() above.
+ */
+ if (strchr(share->table_name.str, '@') ||
+ !strncmp(share->db.str, MYSQL50_TABLE_NAME_PREFIX,
+ MYSQL50_TABLE_NAME_PREFIX_LENGTH) ||
+ !strncmp(share->table_name.str, MYSQL50_TABLE_NAME_PREFIX,
+ MYSQL50_TABLE_NAME_PREFIX_LENGTH))
+ goto err_not_open;
+
+ /* Try unencoded 5.0 name */
+ uint length;
+ strxnmov(path, sizeof(path)-1,
+ mysql_data_home, "/", share->db.str, "/",
+ share->table_name.str, reg_ext, NullS);
+ length= unpack_filename(path, path) - reg_ext_length;
+ /*
+ The following is a safety test and should never fail
+ as the old file name should never be longer than the new one.
+ */
+ DBUG_ASSERT(length <= share->normalized_path.length);
+ /*
+ If the old and the new names have the same length,
+ then table name does not have tricky characters,
+ so no need to check the old file name.
+ */
+ if (length == share->normalized_path.length ||
+ ((file= my_open(path, O_RDONLY | O_SHARE, MYF(0))) < 0))
+ goto err_not_open;
+
+ /* Unencoded 5.0 table name found */
+ path[length]= '\0'; // Remove .frm extension
+ strmov(share->normalized_path.str, path);
+ share->normalized_path.length= length;
+ }
error= 4;
- if (my_read(file,(byte*) head,64,MYF(MY_NABP)))
+ if (my_read(file, head, 64, MYF(MY_NABP)))
goto err;
- if (memcmp(head, STRING_WITH_LEN("TYPE=")) == 0)
+ if (head[0] == (uchar) 254 && head[1] == 1)
{
- // new .frm
- my_close(file,MYF(MY_WME));
-
- if (db_stat & NO_ERR_ON_NEW_FRM)
- DBUG_RETURN(5);
- file= -1;
- // caller can't process new .frm
+ if (head[2] == FRM_VER || head[2] == FRM_VER+1 ||
+ (head[2] >= FRM_VER+3 && head[2] <= FRM_VER+4))
+ {
+ /* Open view only */
+ if (db_flags & OPEN_VIEW_ONLY)
+ {
+ error_given= 1;
+ goto err;
+ }
+ table_type= 1;
+ }
+ else
+ {
+ error= 6; // Unkown .frm version
+ goto err;
+ }
+ }
+ else if (memcmp(head, STRING_WITH_LEN("TYPE=")) == 0)
+ {
+ error= 5;
+ if (memcmp(head+5,"VIEW",4) == 0)
+ {
+ share->is_view= 1;
+ if (db_flags & OPEN_VIEW)
+ error= 0;
+ }
goto err;
}
- if (prgflag & OPEN_VIEW_NO_PARSE)
+ else
goto err;
- share->blob_ptr_size= sizeof(char*);
- outparam->db_stat= db_stat;
- init_sql_alloc(&outparam->mem_root, TABLE_ALLOC_BLOCK_SIZE, 0);
- *root_ptr= &outparam->mem_root;
+ /* No handling of text based files yet */
+ if (table_type == 1)
+ {
+ root_ptr= my_pthread_getspecific_ptr(MEM_ROOT**, THR_MALLOC);
+ old_root= *root_ptr;
+ *root_ptr= &share->mem_root;
+ error= open_binary_frm(thd, share, head, file);
+ *root_ptr= old_root;
+ error_given= 1;
+ }
- share->table_name= strdup_root(&outparam->mem_root,
- name+dirname_length(name));
- share->path= strdup_root(&outparam->mem_root, name);
- outparam->alias= my_strdup(alias, MYF(MY_WME));
- if (!share->table_name || !share->path || !outparam->alias)
- goto err;
- *fn_ext(share->table_name)='\0'; // Remove extension
- *fn_ext(share->path)='\0'; // Remove extension
+ share->table_category= get_table_category(& share->db, & share->table_name);
- if (head[0] != (uchar) 254 || head[1] != 1)
- goto err; /* purecov: inspected */
- if (head[2] != FRM_VER && head[2] != FRM_VER+1 &&
- ! (head[2] >= FRM_VER+3 && head[2] <= FRM_VER+4))
+ if (!error)
+ thd->status_var.opened_shares++;
+
+err:
+ my_close(file, MYF(MY_WME));
+
+err_not_open:
+ if (error && !error_given)
{
- error= 6;
- goto err; /* purecov: inspected */
+ share->error= error;
+ open_table_error(share, error, (share->open_errno= my_errno), 0);
}
- new_field_pack_flag=head[27];
+
+ DBUG_RETURN(error);
+}
+
+
+/*
+ Read data from a binary .frm file from MySQL 3.23 - 5.0 into TABLE_SHARE
+*/
+
+static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head,
+ File file)
+{
+ int error, errarg= 0;
+ uint new_frm_ver, field_pack_length, new_field_pack_flag;
+ uint interval_count, interval_parts, read_length, int_length;
+ uint db_create_options, keys, key_parts, n_length;
+ uint key_info_length, com_length, null_bit_pos;
+ uint extra_rec_buf_length;
+ uint i,j;
+ bool use_hash;
+ char *keynames, *names, *comment_pos;
+ uchar *record;
+ uchar *disk_buff, *strpos, *null_flags, *null_pos;
+ ulong pos, record_offset, *rec_per_key, rec_buff_length;
+ handler *handler_file= 0;
+ KEY *keyinfo;
+ KEY_PART_INFO *key_part;
+ SQL_CRYPT *crypted=0;
+ Field **field_ptr, *reg_field;
+ const char **interval_array;
+ enum legacy_db_type legacy_db_type;
+ my_bitmap_map *bitmaps;
+ DBUG_ENTER("open_binary_frm");
+
+ new_field_pack_flag= head[27];
new_frm_ver= (head[2] - FRM_VER);
field_pack_length= new_frm_ver < 2 ? 11 : 17;
+ disk_buff= 0;
- error=3;
+ error= 3;
if (!(pos=get_form_pos(file,head,(TYPELIB*) 0)))
goto err; /* purecov: inspected */
- *fn_ext(index_file)='\0'; // Remove .frm extension
share->frm_version= head[2];
/*
@@ -164,20 +699,35 @@ int openfrm(THD *thd, const char *name, const char *alias, uint db_stat,
if (share->frm_version == FRM_VER_TRUE_VARCHAR -1 && head[33] == 5)
share->frm_version= FRM_VER_TRUE_VARCHAR;
- share->db_type= ha_checktype(thd,(enum db_type) (uint) *(head+3),0,0);
- share->db_create_options= db_create_options=uint2korr(head+30);
+#ifdef WITH_PARTITION_STORAGE_ENGINE
+ if (*(head+61) &&
+ !(share->default_part_db_type=
+ ha_checktype(thd, (enum legacy_db_type) (uint) *(head+61), 1, 0)))
+ goto err;
+ DBUG_PRINT("info", ("default_part_db_type = %u", head[61]));
+#endif
+ legacy_db_type= (enum legacy_db_type) (uint) *(head+3);
+ DBUG_ASSERT(share->db_plugin == NULL);
+ /*
+ if the storage engine is dynamic, no point in resolving it by its
+ dynamically allocated legacy_db_type. We will resolve it later by name.
+ */
+ if (legacy_db_type > DB_TYPE_UNKNOWN &&
+ legacy_db_type < DB_TYPE_FIRST_DYNAMIC)
+ share->db_plugin= ha_lock_engine(NULL,
+ ha_checktype(thd, legacy_db_type, 0, 0));
+ share->db_create_options= db_create_options= uint2korr(head+30);
share->db_options_in_use= share->db_create_options;
share->mysql_version= uint4korr(head+51);
- null_field_first= 0;
+ share->null_field_first= 0;
if (!head[32]) // New frm file in 3.23
{
share->avg_row_length= uint4korr(head+34);
- share-> row_type= (row_type) head[40];
- share->raid_type= head[41];
- share->raid_chunks= head[42];
- share->raid_chunksize= uint4korr(head+43);
+ 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));
- null_field_first= 1;
+ share->null_field_first= 1;
}
if (!share->table_charset)
{
@@ -188,7 +738,7 @@ int openfrm(THD *thd, const char *name, const char *alias, uint db_stat,
sql_print_warning("'%s' had no or invalid character set, "
"and default character set is multi-byte, "
"so character column sizes may have changed",
- name);
+ share->path.str);
}
share->table_charset= default_charset_info;
}
@@ -196,7 +746,7 @@ int openfrm(THD *thd, const char *name, const char *alias, uint db_stat,
if (db_create_options & HA_OPTION_LONG_BLOB_PTR)
share->blob_ptr_size= portable_sizeof_char_ptr;
/* Set temporarily a good value for db_low_byte_first */
- share->db_low_byte_first= test(share->db_type != DB_TYPE_ISAM);
+ share->db_low_byte_first= test(legacy_db_type != DB_TYPE_ISAM);
error=4;
share->max_rows= uint4korr(head+18);
share->min_rows= uint4korr(head+22);
@@ -204,7 +754,7 @@ int openfrm(THD *thd, const char *name, const char *alias, uint db_stat,
/* Read keyinformation */
key_info_length= (uint) uint2korr(head+28);
VOID(my_seek(file,(ulong) uint2korr(head+6),MY_SEEK_SET,MYF(0)));
- if (read_string(file,(gptr*) &disk_buff,key_info_length))
+ if (read_string(file,(uchar**) &disk_buff,key_info_length))
goto err; /* purecov: inspected */
if (disk_buff[0] & 0x80)
{
@@ -218,33 +768,30 @@ int openfrm(THD *thd, const char *name, const char *alias, uint db_stat,
}
share->keys_for_keyread.init(0);
share->keys_in_use.init(keys);
- outparam->quick_keys.init();
- outparam->used_keys.init();
- outparam->keys_in_use_for_query.init();
n_length=keys*sizeof(KEY)+key_parts*sizeof(KEY_PART_INFO);
- if (!(keyinfo = (KEY*) alloc_root(&outparam->mem_root,
- n_length+uint2korr(disk_buff+4))))
+ if (!(keyinfo = (KEY*) alloc_root(&share->mem_root,
+ n_length + uint2korr(disk_buff+4))))
goto err; /* purecov: inspected */
bzero((char*) keyinfo,n_length);
- outparam->key_info=keyinfo;
+ share->key_info= keyinfo;
key_part= my_reinterpret_cast(KEY_PART_INFO*) (keyinfo+keys);
strpos=disk_buff+6;
- ulong *rec_per_key;
- if (!(rec_per_key= (ulong*) alloc_root(&outparam->mem_root,
+ if (!(rec_per_key= (ulong*) alloc_root(&share->mem_root,
sizeof(ulong*)*key_parts)))
goto err;
for (i=0 ; i < keys ; i++, keyinfo++)
{
- keyinfo->table= outparam;
+ keyinfo->table= 0; // Updated in open_frm
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
@@ -294,10 +841,8 @@ int openfrm(THD *thd, const char *name, const char *alias, uint db_stat,
#ifdef HAVE_CRYPTED_FRM
else if (*(head+26) == 2)
{
- *root_ptr= old_root
- crypted=get_crypt_for_frm();
- *root_ptr= &outparam->mem_root;
- outparam->crypted=1;
+ crypted= get_crypt_for_frm();
+ share->crypted= 1;
}
#endif
@@ -305,94 +850,178 @@ int openfrm(THD *thd, const char *name, const char *alias, uint db_stat,
((uint2korr(head+14) == 0xffff ?
uint4korr(head+47) : uint2korr(head+14))));
- if ((n_length= uint2korr(head+55)))
+ if ((n_length= uint4korr(head+55)))
{
/* Read extra data segment */
- char *buff, *next_chunk, *buff_end;
- if (!(next_chunk= buff= my_malloc(n_length, MYF(MY_WME))))
+ uchar *buff, *next_chunk, *buff_end;
+ DBUG_PRINT("info", ("extra segment size is %u bytes", n_length));
+ if (!(next_chunk= buff= (uchar*) my_malloc(n_length, MYF(MY_WME))))
goto err;
- buff_end= buff + n_length;
- if (my_pread(file, (byte*)buff, n_length, record_offset + share->reclength,
+ if (my_pread(file, buff, n_length, record_offset + share->reclength,
MYF(MY_NABP)))
{
my_free(buff, MYF(0));
goto err;
}
share->connect_string.length= uint2korr(buff);
- if (! (share->connect_string.str= strmake_root(&outparam->mem_root,
- next_chunk + 2, share->connect_string.length)))
+ if (!(share->connect_string.str= strmake_root(&share->mem_root,
+ (char*) next_chunk + 2,
+ share->connect_string.
+ length)))
{
my_free(buff, MYF(0));
goto err;
}
next_chunk+= share->connect_string.length + 2;
+ buff_end= buff + n_length;
if (next_chunk + 2 < buff_end)
{
uint str_db_type_length= uint2korr(next_chunk);
- share->db_type= ha_resolve_by_name(next_chunk + 2, str_db_type_length);
- DBUG_PRINT("enter", ("Setting dbtype to: %d - %d - '%.*s'\n",
- share->db_type,
- str_db_type_length, str_db_type_length,
- next_chunk + 2));
+ 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 != NULL && !plugin_equals(tmp_plugin, share->db_plugin))
+ {
+ if (legacy_db_type > DB_TYPE_UNKNOWN &&
+ legacy_db_type < DB_TYPE_FIRST_DYNAMIC &&
+ legacy_db_type != ha_legacy_type(
+ plugin_data(tmp_plugin, handlerton *)))
+ {
+ /* bad file, legacy_db_type did not match the name */
+ my_free(buff, MYF(0));
+ goto err;
+ }
+ /*
+ tmp_plugin is locked with a local lock.
+ we unlock the old value of share->db_plugin before
+ replacing it with a globally locked version of tmp_plugin
+ */
+ plugin_unlock(NULL, share->db_plugin);
+ share->db_plugin= my_plugin_lock(NULL, &tmp_plugin);
+ DBUG_PRINT("info", ("setting dbtype to '%.*s' (%d)",
+ str_db_type_length, next_chunk + 2,
+ ha_legacy_type(share->db_type())));
+ }
+#ifdef WITH_PARTITION_STORAGE_ENGINE
+ else if (str_db_type_length == 9 &&
+ !strncmp((char *) next_chunk + 2, "partition", 9))
+ {
+ /*
+ Use partition handler
+ tmp_plugin is locked with a local lock.
+ we unlock the old value of share->db_plugin before
+ replacing it with a globally locked version of tmp_plugin
+ */
+ plugin_unlock(NULL, share->db_plugin);
+ share->db_plugin= ha_lock_engine(NULL, partition_hton);
+ DBUG_PRINT("info", ("setting dbtype to '%.*s' (%d)",
+ str_db_type_length, next_chunk + 2,
+ ha_legacy_type(share->db_type())));
+ }
+#endif
+ else if (!tmp_plugin)
+ {
+ /* purecov: begin inspected */
+ error= 8;
+ my_error(ER_UNKNOWN_STORAGE_ENGINE, MYF(0), name.str);
+ my_free(buff, MYF(0));
+ goto err;
+ /* purecov: end */
+ }
next_chunk+= str_db_type_length + 2;
}
+ if (next_chunk + 5 < buff_end)
+ {
+ uint32 partition_info_len = uint4korr(next_chunk);
+#ifdef WITH_PARTITION_STORAGE_ENGINE
+ if ((share->partition_info_buffer_size=
+ share->partition_info_len= partition_info_len))
+ {
+ if (!(share->partition_info= (char*)
+ memdup_root(&share->mem_root, next_chunk + 4,
+ partition_info_len + 1)))
+ {
+ my_free(buff, MYF(0));
+ goto err;
+ }
+ }
+#else
+ if (partition_info_len)
+ {
+ DBUG_PRINT("info", ("WITH_PARTITION_STORAGE_ENGINE is not defined"));
+ my_free(buff, MYF(0));
+ goto err;
+ }
+#endif
+ next_chunk+= 5 + partition_info_len;
+ }
+#if MYSQL_VERSION_ID < 50200
+ if (share->mysql_version >= 50106 && share->mysql_version <= 50109)
+ {
+ /*
+ Partition state array was here in version 5.1.6 to 5.1.9, this code
+ makes it possible to load a 5.1.6 table in later versions. Can most
+ likely be removed at some point in time. Will only be used for
+ upgrades within 5.1 series of versions. Upgrade to 5.2 can only be
+ done from newer 5.1 versions.
+ */
+ next_chunk+= 4;
+ }
+ else if (share->mysql_version >= 50110)
+#endif
+ {
+ /* New auto_partitioned indicator introduced in 5.1.11 */
+#ifdef WITH_PARTITION_STORAGE_ENGINE
+ share->auto_partitioned= *next_chunk;
+#endif
+ next_chunk++;
+ }
+ keyinfo= share->key_info;
+ for (i= 0; i < keys; i++, keyinfo++)
+ {
+ if (keyinfo->flags & HA_USES_PARSER)
+ {
+ LEX_STRING parser_name;
+ if (next_chunk >= buff_end)
+ {
+ DBUG_PRINT("error",
+ ("fulltext key uses parser that is not defined in .frm"));
+ my_free(buff, MYF(0));
+ goto err;
+ }
+ parser_name.str= (char*) next_chunk;
+ parser_name.length= strlen((char*) next_chunk);
+ next_chunk+= parser_name.length + 1;
+ keyinfo->parser= my_plugin_lock_by_name(NULL, &parser_name,
+ MYSQL_FTPARSER_PLUGIN);
+ if (! keyinfo->parser)
+ {
+ my_error(ER_PLUGIN_IS_NOT_LOADED, MYF(0), parser_name.str);
+ my_free(buff, MYF(0));
+ goto err;
+ }
+ }
+ }
my_free(buff, MYF(0));
}
- /* Allocate handler */
- if (!(outparam->file= get_new_handler(outparam, &outparam->mem_root,
- share->db_type)))
- goto err;
+ share->key_block_size= uint2korr(head+62);
error=4;
- outparam->reginfo.lock_type= TL_UNLOCK;
- outparam->current_lock=F_UNLCK;
- if ((db_stat & HA_OPEN_KEYFILE) || (prgflag & DELAYED_OPEN))
- records=2;
- else
- records=1;
- if (prgflag & (READ_ALL+EXTRA_RECORD))
- records++;
- /* QQ: TODO, remove the +1 from below */
- rec_buff_length= ALIGN_SIZE(share->reclength + 1 +
- outparam->file->extra_rec_buf_length());
+ extra_rec_buf_length= uint2korr(head+59);
+ rec_buff_length= ALIGN_SIZE(share->reclength + 1 + extra_rec_buf_length);
share->rec_buff_length= rec_buff_length;
- if (!(record= (char *) alloc_root(&outparam->mem_root,
- rec_buff_length * records)))
+ if (!(record= (uchar *) alloc_root(&share->mem_root,
+ rec_buff_length)))
goto err; /* purecov: inspected */
- share->default_values= (byte *) record;
-
- if (my_pread(file,(byte*) record, (uint) share->reclength,
+ share->default_values= record;
+ if (my_pread(file, record, (size_t) share->reclength,
record_offset, MYF(MY_NABP)))
- goto err; /* purecov: inspected */
+ goto err; /* purecov: inspected */
- if (records == 1)
- {
- /* We are probably in hard repair, and the buffers should not be used */
- outparam->record[0]= outparam->record[1]= share->default_values;
- }
- else
- {
- outparam->record[0]= (byte *) record+ rec_buff_length;
- if (records > 2)
- outparam->record[1]= (byte *) record+ rec_buff_length*2;
- else
- outparam->record[1]= outparam->record[0]; // Safety
- }
-
-#ifdef HAVE_purify
- /*
- We need this because when we read var-length rows, we are not updating
- bytes after end of varchar
- */
- if (records > 1)
- {
- memcpy(outparam->record[0], share->default_values, rec_buff_length);
- if (records > 2)
- memcpy(outparam->record[1], share->default_values, rec_buff_length);
- }
-#endif
VOID(my_seek(file,pos,MY_SEEK_SET,MYF(0)));
- if (my_read(file,(byte*) head,288,MYF(MY_NABP)))
+ if (my_read(file, head,288,MYF(MY_NABP)))
goto err;
#ifdef HAVE_CRYPTED_FRM
if (crypted)
@@ -412,24 +1041,24 @@ int openfrm(THD *thd, const char *name, const char *alias, uint db_stat,
share->null_fields= uint2korr(head+282);
com_length= uint2korr(head+284);
share->comment.length= (int) (head[46]);
- share->comment.str= strmake_root(&outparam->mem_root, (char*) head+47,
+ share->comment.str= strmake_root(&share->mem_root, (char*) head+47,
share->comment.length);
DBUG_PRINT("info",("i_count: %d i_parts: %d index: %d n_length: %d int_length: %d com_length: %d", interval_count,interval_parts, share->keys,n_length,int_length, com_length));
if (!(field_ptr = (Field **)
- alloc_root(&outparam->mem_root,
+ alloc_root(&share->mem_root,
(uint) ((share->fields+1)*sizeof(Field*)+
interval_count*sizeof(TYPELIB)+
(share->fields+interval_parts+
- keys+3)*sizeof(my_string)+
+ keys+3)*sizeof(char *)+
(n_length+int_length+com_length)))))
goto err; /* purecov: inspected */
- outparam->field=field_ptr;
+ share->field= field_ptr;
read_length=(uint) (share->fields * field_pack_length +
pos+ (uint) (n_length+int_length+com_length));
- if (read_string(file,(gptr*) &disk_buff,read_length))
+ if (read_string(file,(uchar**) &disk_buff,read_length))
goto err; /* purecov: inspected */
#ifdef HAVE_CRYPTED_FRM
if (crypted)
@@ -442,8 +1071,8 @@ int openfrm(THD *thd, const char *name, const char *alias, uint db_stat,
strpos= disk_buff+pos;
share->intervals= (TYPELIB*) (field_ptr+share->fields+1);
- int_array= (const char **) (share->intervals+interval_count);
- names= (char*) (int_array+share->fields+interval_parts+keys+3);
+ interval_array= (const char **) (share->intervals+interval_count);
+ names= (char*) (interval_array+share->fields+interval_parts+keys+3);
if (!interval_count)
share->intervals= 0; // For better debugging
memcpy((char*) names, strpos+(share->fields*field_pack_length),
@@ -451,10 +1080,10 @@ int openfrm(THD *thd, const char *name, const char *alias, uint db_stat,
comment_pos= names+(n_length+int_length);
memcpy(comment_pos, disk_buff+read_length-com_length, com_length);
- fix_type_pointers(&int_array, &share->fieldnames, 1, &names);
+ fix_type_pointers(&interval_array, &share->fieldnames, 1, &names);
if (share->fieldnames.count != share->fields)
goto err;
- fix_type_pointers(&int_array, share->intervals, interval_count,
+ fix_type_pointers(&interval_array, share->intervals, interval_count,
&names);
{
@@ -465,27 +1094,30 @@ int openfrm(THD *thd, const char *name, const char *alias, uint db_stat,
interval++)
{
uint count= (uint) (interval->count + 1) * sizeof(uint);
- if (!(interval->type_lengths= (uint *) alloc_root(&outparam->mem_root,
+ if (!(interval->type_lengths= (uint *) alloc_root(&share->mem_root,
count)))
goto err;
for (count= 0; count < interval->count; count++)
{
char *val= (char*) interval->type_names[count];
- interval->type_lengths[count]= (uint) strlen(val);
+ interval->type_lengths[count]= strlen(val);
}
interval->type_lengths[count]= 0;
}
}
if (keynames)
- fix_type_pointers(&int_array, &share->keynames, 1, &keynames);
- VOID(my_close(file,MYF(MY_WME)));
- file= -1;
+ fix_type_pointers(&interval_array, &share->keynames, 1, &keynames);
+
+ /* Allocate handler */
+ if (!(handler_file= get_new_handler(share, thd->mem_root,
+ share->db_type())))
+ goto err;
- record= (char*) outparam->record[0]-1; /* Fieldstart = 1 */
- if (null_field_first)
+ record= share->default_values-1; /* Fieldstart = 1 */
+ if (share->null_field_first)
{
- outparam->null_flags=null_pos=(uchar*) record+1;
+ null_flags= null_pos= (uchar*) record+1;
null_bit_pos= (db_create_options & HA_OPTION_PACK_RECORD) ? 0 : 1;
/*
null_bytes below is only correct under the condition that
@@ -494,13 +1126,15 @@ int openfrm(THD *thd, const char *name, const char *alias, uint db_stat,
*/
share->null_bytes= (share->null_fields + null_bit_pos + 7) / 8;
}
+#ifndef WE_WANT_TO_SUPPORT_VERY_OLD_FRM_FILES
else
{
share->null_bytes= (share->null_fields+7)/8;
- outparam->null_flags= null_pos=
- (uchar*) (record+1+share->reclength-share->null_bytes);
+ null_flags= null_pos= (uchar*) (record + 1 +share->reclength -
+ share->null_bytes);
null_bit_pos= 0;
}
+#endif
use_hash= share->fields >= MAX_FIELDS_BEFORE_HASH;
if (use_hash)
@@ -529,7 +1163,7 @@ int openfrm(THD *thd, const char *name, const char *alias, uint db_stat,
field_type=(enum_field_types) (uint) strpos[13];
/* charset and geometry_type share the same byte in frm */
- if (field_type == FIELD_TYPE_GEOMETRY)
+ if (field_type == MYSQL_TYPE_GEOMETRY)
{
#ifdef HAVE_SPATIAL
geom_type= (Field::geometry_type) strpos[14];
@@ -604,7 +1238,7 @@ int openfrm(THD *thd, const char *name, const char *alias, uint db_stat,
}
#ifndef TO_BE_DELETED_ON_PRODUCTION
- if (field_type == FIELD_TYPE_NEWDECIMAL && !share->mysql_version)
+ if (field_type == MYSQL_TYPE_NEWDECIMAL && !share->mysql_version)
{
/*
Fix pack length of old decimal values from 5.0.3 -> 5.0.4
@@ -615,16 +1249,23 @@ int openfrm(THD *thd, const char *name, const char *alias, uint db_stat,
field_length= my_decimal_precision_to_length(field_length,
decimals,
f_is_dec(pack_flag) == 0);
- sql_print_error("Found incompatible DECIMAL field '%s' in %s; Please do \"ALTER TABLE '%s' FORCE\" to fix it!", share->fieldnames.type_names[i], name, share->table_name);
+ sql_print_error("Found incompatible DECIMAL field '%s' in %s; "
+ "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,
ER_CRASHED_ON_USAGE,
- "Found incompatible DECIMAL field '%s' in %s; Please do \"ALTER TABLE '%s' FORCE\" to fix it!", share->fieldnames.type_names[i], name, share->table_name);
+ "Found incompatible DECIMAL field '%s' in %s; "
+ "Please do \"ALTER TABLE '%s' FORCE\" to fix it!",
+ share->fieldnames.type_names[i],
+ share->table_name.str,
+ share->table_name.str);
share->crashed= 1; // Marker for CHECK TABLE
}
#endif
- *field_ptr=reg_field=
- make_field(record+recpos,
+ *field_ptr= reg_field=
+ make_field(share, record+recpos,
(uint32) field_length,
null_pos, null_bit_pos,
pack_flag,
@@ -635,8 +1276,7 @@ int openfrm(THD *thd, const char *name, const char *alias, uint db_stat,
(interval_nr ?
share->intervals+interval_nr-1 :
(TYPELIB*) 0),
- share->fieldnames.type_names[i],
- outparam);
+ share->fieldnames.type_names[i]);
if (!reg_field) // Not supported field type
{
error= 4;
@@ -645,7 +1285,7 @@ int openfrm(THD *thd, const char *name, const char *alias, uint db_stat,
reg_field->field_index= i;
reg_field->comment=comment;
- if (field_type == FIELD_TYPE_BIT && !f_bit_as_char(pack_flag))
+ if (field_type == MYSQL_TYPE_BIT && !f_bit_as_char(pack_flag))
{
if ((null_bit_pos+= field_length & 7) > 7)
{
@@ -660,12 +1300,15 @@ int openfrm(THD *thd, const char *name, const char *alias, uint db_stat,
}
if (f_no_default(pack_flag))
reg_field->flags|= NO_DEFAULT_VALUE_FLAG;
+
if (reg_field->unireg_check == Field::NEXT_NUMBER)
- outparam->found_next_number_field= reg_field;
- if (outparam->timestamp_field == reg_field)
+ share->found_next_number_field= field_ptr;
+ if (share->timestamp_field == reg_field)
share->timestamp_field_offset= i;
+
if (use_hash)
- (void) my_hash_insert(&share->name_hash,(byte*) field_ptr); // never fail
+ (void) my_hash_insert(&share->name_hash,
+ (uchar*) field_ptr); // never fail
}
*field_ptr=0; // End marker
@@ -674,17 +1317,17 @@ int openfrm(THD *thd, const char *name, const char *alias, uint db_stat,
{
uint primary_key=(uint) (find_type((char*) primary_key_name,
&share->keynames, 3) - 1);
- uint ha_option=outparam->file->table_flags();
- keyinfo=outparam->key_info;
- key_part=keyinfo->key_part;
+ 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++)
{
- uint usable_parts=0;
+ uint usable_parts= 0;
keyinfo->name=(char*) share->keynames.type_names[key];
/* Fix fulltext keys for old .frm files */
- if (outparam->key_info[key].flags & HA_FULLTEXT)
- outparam->key_info[key].algorithm= HA_KEY_ALG_FULLTEXT;
+ if (share->key_info[key].flags & HA_FULLTEXT)
+ share->key_info[key].algorithm= HA_KEY_ALG_FULLTEXT;
if (primary_key >= MAX_KEY && (keyinfo->flags & HA_NOSAME))
{
@@ -697,8 +1340,8 @@ int openfrm(THD *thd, const char *name, const char *alias, uint db_stat,
{
uint fieldnr= key_part[i].fieldnr;
if (!fieldnr ||
- outparam->field[fieldnr-1]->null_ptr ||
- outparam->field[fieldnr-1]->key_length() !=
+ share->field[fieldnr-1]->null_ptr ||
+ share->field[fieldnr-1]->key_length() !=
key_part[i].length)
{
primary_key=MAX_KEY; // Can't be used
@@ -709,137 +1352,134 @@ int openfrm(THD *thd, const char *name, const char *alias, uint db_stat,
for (i=0 ; i < keyinfo->key_parts ; key_part++,i++)
{
+ Field *field;
if (new_field_pack_flag <= 1)
- key_part->fieldnr=(uint16) find_field(outparam,
- (uint) key_part->offset,
- (uint) key_part->length);
-#ifdef EXTRA_DEBUG
- if (key_part->fieldnr > share->fields)
- goto err; // sanity check
-#endif
- if (key_part->fieldnr)
- { // Should always be true !
- Field *field=key_part->field=outparam->field[key_part->fieldnr-1];
- key_part->type= field->key_type();
- if (field->null_ptr)
- {
- key_part->null_offset=(uint) ((byte*) field->null_ptr -
- outparam->record[0]);
- key_part->null_bit= field->null_bit;
- key_part->store_length+=HA_KEY_NULL_LENGTH;
- keyinfo->flags|=HA_NULL_PART_KEY;
- keyinfo->extra_length+= HA_KEY_NULL_LENGTH;
- keyinfo->key_length+= HA_KEY_NULL_LENGTH;
- }
- if (field->type() == FIELD_TYPE_BLOB ||
- field->real_type() == MYSQL_TYPE_VARCHAR ||
- field->type() == FIELD_TYPE_GEOMETRY)
- {
- if (field->type() == FIELD_TYPE_BLOB ||
- field->type() == FIELD_TYPE_GEOMETRY)
- key_part->key_part_flag|= HA_BLOB_PART;
- else
- key_part->key_part_flag|= HA_VAR_LENGTH_PART;
- keyinfo->extra_length+=HA_KEY_BLOB_LENGTH;
- key_part->store_length+=HA_KEY_BLOB_LENGTH;
- keyinfo->key_length+= HA_KEY_BLOB_LENGTH;
- /*
- Mark that there may be many matching values for one key
- combination ('a', 'a ', 'a '...)
- */
- if (!(field->flags & BINARY_FLAG))
- keyinfo->flags|= HA_END_SPACE_KEY;
- }
- if (field->type() == MYSQL_TYPE_BIT)
- key_part->key_part_flag|= HA_BIT_PART;
-
- if (i == 0 && key != primary_key)
- field->flags |= ((keyinfo->flags & HA_NOSAME) &&
- (keyinfo->key_parts == 1)) ?
- UNIQUE_KEY_FLAG : MULTIPLE_KEY_FLAG;
- if (i == 0)
- field->key_start.set_bit(key);
- if (field->key_length() == key_part->length &&
- !(field->flags & BLOB_FLAG))
- {
- if (outparam->file->index_flags(key, i, 0) & HA_KEYREAD_ONLY)
- {
- share->keys_for_keyread.set_bit(key);
- field->part_of_key.set_bit(key);
- }
- if (outparam->file->index_flags(key, i, 1) & HA_READ_ORDER)
- field->part_of_sortkey.set_bit(key);
- }
- if (!(key_part->key_part_flag & HA_REVERSE_SORT) &&
- usable_parts == i)
- usable_parts++; // For FILESORT
- field->flags|= PART_KEY_FLAG;
- if (key == primary_key)
- {
- field->flags|= PRI_KEY_FLAG;
- /*
- If this field is part of the primary key and all keys contains
- the primary key, then we can use any key to find this column
- */
- if (ha_option & HA_PRIMARY_KEY_IN_READ_INDEX)
- {
- field->part_of_key= share->keys_in_use;
- if (field->part_of_sortkey.is_set(key))
- field->part_of_sortkey= share->keys_in_use;
- }
- }
- if (field->key_length() != key_part->length)
- {
+ key_part->fieldnr= (uint16) find_field(share->field,
+ share->default_values,
+ (uint) key_part->offset,
+ (uint) key_part->length);
+ if (!key_part->fieldnr)
+ {
+ error= 4; // Wrong file
+ goto err;
+ }
+ field= key_part->field= share->field[key_part->fieldnr-1];
+ key_part->type= field->key_type();
+ if (field->null_ptr)
+ {
+ key_part->null_offset=(uint) ((uchar*) field->null_ptr -
+ share->default_values);
+ key_part->null_bit= field->null_bit;
+ key_part->store_length+=HA_KEY_NULL_LENGTH;
+ keyinfo->flags|=HA_NULL_PART_KEY;
+ keyinfo->extra_length+= HA_KEY_NULL_LENGTH;
+ keyinfo->key_length+= HA_KEY_NULL_LENGTH;
+ }
+ if (field->type() == MYSQL_TYPE_BLOB ||
+ field->real_type() == MYSQL_TYPE_VARCHAR ||
+ field->type() == MYSQL_TYPE_GEOMETRY)
+ {
+ if (field->type() == MYSQL_TYPE_BLOB ||
+ field->type() == MYSQL_TYPE_GEOMETRY)
+ key_part->key_part_flag|= HA_BLOB_PART;
+ else
+ key_part->key_part_flag|= HA_VAR_LENGTH_PART;
+ keyinfo->extra_length+=HA_KEY_BLOB_LENGTH;
+ key_part->store_length+=HA_KEY_BLOB_LENGTH;
+ keyinfo->key_length+= HA_KEY_BLOB_LENGTH;
+ /*
+ Mark that there may be many matching values for one key
+ combination ('a', 'a ', 'a '...)
+ */
+ if (!(field->flags & BINARY_FLAG))
+ keyinfo->flags|= HA_END_SPACE_KEY;
+ }
+ if (field->type() == MYSQL_TYPE_BIT)
+ key_part->key_part_flag|= HA_BIT_PART;
+
+ if (i == 0 && key != primary_key)
+ field->flags |= (((keyinfo->flags & HA_NOSAME) &&
+ (keyinfo->key_parts == 1)) ?
+ UNIQUE_KEY_FLAG : MULTIPLE_KEY_FLAG);
+ if (i == 0)
+ field->key_start.set_bit(key);
+ if (field->key_length() == key_part->length &&
+ !(field->flags & BLOB_FLAG))
+ {
+ if (handler_file->index_flags(key, i, 0) & HA_KEYREAD_ONLY)
+ {
+ share->keys_for_keyread.set_bit(key);
+ field->part_of_key.set_bit(key);
+ 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);
+ }
+ if (!(key_part->key_part_flag & HA_REVERSE_SORT) &&
+ usable_parts == i)
+ usable_parts++; // For FILESORT
+ field->flags|= PART_KEY_FLAG;
+ if (key == primary_key)
+ {
+ field->flags|= PRI_KEY_FLAG;
+ /*
+ If this field is part of the primary key and all keys contains
+ the primary key, then we can use any key to find this column
+ */
+ if (ha_option & HA_PRIMARY_KEY_IN_READ_INDEX)
+ {
+ if (field->key_length() == key_part->length &&
+ !(field->flags & BLOB_FLAG))
+ field->part_of_key= share->keys_in_use;
+ if (field->part_of_sortkey.is_set(key))
+ field->part_of_sortkey= share->keys_in_use;
+ }
+ }
+ if (field->key_length() != key_part->length)
+ {
#ifndef TO_BE_DELETED_ON_PRODUCTION
- if (field->type() == FIELD_TYPE_NEWDECIMAL)
- {
- /*
- Fix a fatal error in decimal key handling that causes crashes
- on Innodb. We fix it by reducing the key length so that
- InnoDB never gets a too big key when searching.
- This allows the end user to do an ALTER TABLE to fix the
- error.
- */
- keyinfo->key_length-= (key_part->length - field->key_length());
- key_part->store_length-= (uint16)(key_part->length -
- field->key_length());
- key_part->length= (uint16)field->key_length();
- sql_print_error("Found wrong key definition in %s; Please do \"ALTER TABLE '%s' FORCE \" to fix it!", name, share->table_name);
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_ERROR,
- ER_CRASHED_ON_USAGE,
- "Found wrong key definition in %s; Please do \"ALTER TABLE '%s' FORCE\" to fix it!", name, share->table_name);
-
- share->crashed= 1; // Marker for CHECK TABLE
- goto to_be_deleted;
- }
+ if (field->type() == MYSQL_TYPE_NEWDECIMAL)
+ {
+ /*
+ Fix a fatal error in decimal key handling that causes crashes
+ on Innodb. We fix it by reducing the key length so that
+ InnoDB never gets a too big key when searching.
+ This allows the end user to do an ALTER TABLE to fix the
+ error.
+ */
+ keyinfo->key_length-= (key_part->length - field->key_length());
+ key_part->store_length-= (uint16)(key_part->length -
+ field->key_length());
+ key_part->length= (uint16)field->key_length();
+ sql_print_error("Found wrong key definition in %s; "
+ "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,
+ ER_CRASHED_ON_USAGE,
+ "Found wrong key definition in %s; "
+ "Please do \"ALTER TABLE '%s' FORCE\" to fix "
+ "it!",
+ share->table_name.str,
+ share->table_name.str);
+ share->crashed= 1; // Marker for CHECK TABLE
+ goto to_be_deleted;
+ }
#endif
- key_part->key_part_flag|= HA_PART_KEY_SEG;
- if (!(field->flags & BLOB_FLAG))
- { // Create a new field
- field=key_part->field=field->new_field(&outparam->mem_root,
- outparam,
- outparam == field->table);
- field->field_length=key_part->length;
- }
- }
+ key_part->key_part_flag|= HA_PART_KEY_SEG;
+ }
to_be_deleted:
- /*
- If the field can be NULL, don't optimize away the test
- key_part_column = expression from the WHERE clause
- as we need to test for NULL = NULL.
- */
- if (field->real_maybe_null())
- key_part->key_part_flag|= HA_NULL_PART;
- }
- else
- { // Error: shorten key
- keyinfo->key_parts=usable_parts;
- keyinfo->flags=0;
- }
+ /*
+ If the field can be NULL, don't optimize away the test
+ key_part_column = expression from the WHERE clause
+ as we need to test for NULL = NULL.
+ */
+ if (field->real_maybe_null())
+ key_part->key_part_flag|= HA_NULL_PART;
}
- keyinfo->usable_key_parts=usable_parts; // Filesort
+ keyinfo->usable_key_parts= usable_parts; // Filesort
set_if_bigger(share->max_key_length,keyinfo->key_length+
keyinfo->key_parts);
@@ -860,11 +1500,15 @@ int openfrm(THD *thd, const char *name, const char *alias, uint db_stat,
If we are using an integer as the primary key then allow the user to
refer to it as '_rowid'
*/
- if (outparam->key_info[primary_key].key_parts == 1)
+ if (share->key_info[primary_key].key_parts == 1)
{
- Field *field= outparam->key_info[primary_key].key_part[0].field;
+ Field *field= share->key_info[primary_key].key_part[0].field;
if (field && field->result_type() == INT_RESULT)
- outparam->rowid_field=field;
+ {
+ /* note that fieldnr here (and rowid_field_offset) starts from 1 */
+ share->rowid_field_offset= (share->key_info[primary_key].key_part[0].
+ fieldnr);
+ }
}
}
else
@@ -872,27 +1516,31 @@ int openfrm(THD *thd, const char *name, const char *alias, uint db_stat,
}
else
share->primary_key= MAX_KEY;
- x_free((gptr) disk_buff);
+ x_free((uchar*) disk_buff);
disk_buff=0;
if (new_field_pack_flag <= 1)
{
/* Old file format with default as not null */
uint null_length= (share->null_fields+7)/8;
- bfill(share->default_values + (outparam->null_flags - (uchar*) record),
+ bfill(share->default_values + (null_flags - (uchar*) record),
null_length, 255);
}
- if ((reg_field=outparam->found_next_number_field))
+ if (share->found_next_number_field)
{
+ reg_field= *share->found_next_number_field;
if ((int) (share->next_number_index= (uint)
- find_ref_key(outparam,reg_field,
- &share->next_number_key_offset)) < 0)
+ find_ref_key(share->key_info, share->keys,
+ share->default_values, reg_field,
+ &share->next_number_key_offset,
+ &share->next_number_keypart)) < 0)
{
- reg_field->unireg_check=Field::NONE; /* purecov: inspected */
- outparam->found_next_number_field=0;
+ /* Wrong field definition */
+ error= 4;
+ goto err;
}
else
- reg_field->flags|=AUTO_INCREMENT_FLAG;
+ reg_field->flags |= AUTO_INCREMENT_FLAG;
}
if (share->blob_fields)
@@ -902,10 +1550,10 @@ int openfrm(THD *thd, const char *name, const char *alias, uint db_stat,
/* Store offsets to blob fields to find them fast */
if (!(share->blob_field= save=
- (uint*) alloc_root(&outparam->mem_root,
+ (uint*) alloc_root(&share->mem_root,
(uint) (share->blob_fields* sizeof(uint)))))
goto err;
- for (k=0, ptr= outparam->field ; *ptr ; ptr++, k++)
+ for (k=0, ptr= share->field ; *ptr ; ptr++, k++)
{
if ((*ptr)->flags & BLOB_FLAG)
(*save++)= k;
@@ -916,18 +1564,313 @@ int openfrm(THD *thd, const char *name, const char *alias, uint db_stat,
the correct null_bytes can now be set, since bitfields have been taken
into account
*/
- share->null_bytes= (uint) (null_pos - (uchar*) outparam->null_flags +
+ share->null_bytes= (null_pos - (uchar*) null_flags +
(null_bit_pos + 7) / 8);
share->last_null_bit_pos= null_bit_pos;
+ share->db_low_byte_first= handler_file->low_byte_first();
+ share->column_bitmap_size= bitmap_buffer_size(share->fields);
+
+ if (!(bitmaps= (my_bitmap_map*) alloc_root(&share->mem_root,
+ share->column_bitmap_size)))
+ goto err;
+ bitmap_init(&share->all_set, bitmaps, share->fields, FALSE);
+ bitmap_set_all(&share->all_set);
+
+ delete handler_file;
+#ifndef DBUG_OFF
+ if (use_hash)
+ (void) hash_check(&share->name_hash);
+#endif
+ DBUG_RETURN (0);
+
+ err:
+ share->error= error;
+ share->open_errno= my_errno;
+ share->errarg= errarg;
+ x_free((uchar*) disk_buff);
+ delete crypted;
+ delete handler_file;
+ hash_free(&share->name_hash);
+
+ open_table_error(share, error, share->open_errno, errarg);
+ DBUG_RETURN(error);
+} /* open_binary_frm */
+
+
+/*
+ Open a table based on a TABLE_SHARE
+
+ SYNOPSIS
+ open_table_from_share()
+ thd Thread handler
+ share Table definition
+ alias Alias for table
+ db_stat open flags (for example HA_OPEN_KEYFILE|
+ HA_OPEN_RNDFILE..) can be 0 (example in
+ ha_example_table)
+ prgflag READ_ALL etc..
+ ha_open_flags HA_OPEN_ABORT_IF_LOCKED etc..
+ outparam result table
+
+ RETURN VALUES
+ 0 ok
+ 1 Error (see open_table_error)
+ 2 Error (see open_table_error)
+ 3 Wrong data in .frm file
+ 4 Error (see open_table_error)
+ 5 Error (see open_table_error: charset unavailable)
+ 7 Table definition has changed in engine
+*/
+
+int open_table_from_share(THD *thd, TABLE_SHARE *share, const char *alias,
+ uint db_stat, uint prgflag, uint ha_open_flags,
+ TABLE *outparam, bool is_create_table)
+{
+ int error;
+ uint records, i, bitmap_size;
+ bool error_reported= FALSE;
+ uchar *record, *bitmaps;
+ Field **field_ptr;
+ DBUG_ENTER("open_table_from_share");
+ 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);
+
+ error= 1;
+ bzero((char*) outparam, sizeof(*outparam));
+ outparam->in_use= thd;
+ outparam->s= share;
+ outparam->db_stat= db_stat;
+ outparam->write_row_record= NULL;
+
+ init_sql_alloc(&outparam->mem_root, TABLE_ALLOC_BLOCK_SIZE, 0);
+
+ if (!(outparam->alias= my_strdup(alias, MYF(MY_WME))))
+ goto err;
+ outparam->quick_keys.init();
+ outparam->covering_keys.init();
+ outparam->keys_in_use_for_query.init();
+
+ /* Allocate handler */
+ outparam->file= 0;
+ if (!(prgflag & OPEN_FRM_FILE_ONLY))
+ {
+ if (!(outparam->file= get_new_handler(share, &outparam->mem_root,
+ share->db_type())))
+ goto err;
+ }
+ else
+ {
+ DBUG_ASSERT(!db_stat);
+ }
+
+ error= 4;
+ outparam->reginfo.lock_type= TL_UNLOCK;
+ outparam->current_lock= F_UNLCK;
+ records=0;
+ if ((db_stat & HA_OPEN_KEYFILE) || (prgflag & DELAYED_OPEN))
+ records=1;
+ if (prgflag & (READ_ALL+EXTRA_RECORD))
+ records++;
+
+ if (!(record= (uchar*) alloc_root(&outparam->mem_root,
+ share->rec_buff_length * records)))
+ goto err; /* purecov: inspected */
+
+ if (records == 0)
+ {
+ /* We are probably in hard repair, and the buffers should not be used */
+ outparam->record[0]= outparam->record[1]= share->default_values;
+ }
+ else
+ {
+ outparam->record[0]= record;
+ if (records > 1)
+ outparam->record[1]= record+ share->rec_buff_length;
+ else
+ outparam->record[1]= outparam->record[0]; // Safety
+ }
+
+#ifdef HAVE_purify
+ /*
+ We need this because when we read var-length rows, we are not updating
+ bytes after end of varchar
+ */
+ if (records > 1)
+ {
+ memcpy(outparam->record[0], share->default_values, share->rec_buff_length);
+ memcpy(outparam->record[1], share->default_values, share->null_bytes);
+ if (records > 2)
+ memcpy(outparam->record[1], share->default_values,
+ share->rec_buff_length);
+ }
+#endif
+
+ if (!(field_ptr = (Field **) alloc_root(&outparam->mem_root,
+ (uint) ((share->fields+1)*
+ sizeof(Field*)))))
+ goto err; /* purecov: inspected */
+
+ outparam->field= field_ptr;
+
+ record= (uchar*) outparam->record[0]-1; /* Fieldstart = 1 */
+ if (share->null_field_first)
+ outparam->null_flags= (uchar*) record+1;
+ else
+ outparam->null_flags= (uchar*) (record+ 1+ share->reclength -
+ share->null_bytes);
+
+ /* Setup copy of fields from share, but use the right alias and record */
+ for (i=0 ; i < share->fields; i++, field_ptr++)
+ {
+ if (!((*field_ptr)= share->field[i]->clone(&outparam->mem_root, outparam)))
+ goto err;
+ }
+ (*field_ptr)= 0; // End marker
+
+ if (share->found_next_number_field)
+ outparam->found_next_number_field=
+ outparam->field[(uint) (share->found_next_number_field - share->field)];
+ if (share->timestamp_field)
+ outparam->timestamp_field= (Field_timestamp*) outparam->field[share->timestamp_field_offset];
+
+
+ /* Fix key->name and key_part->field */
+ if (share->key_parts)
+ {
+ 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);
+ 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));
+
+ 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));
+
+ for (key_info_end= key_info + share->keys ;
+ key_info < key_info_end ;
+ key_info++)
+ {
+ KEY_PART_INFO *key_part_end;
+
+ key_info->table= outparam;
+ key_info->key_part= key_part;
+
+ for (key_part_end= key_part+ key_info->key_parts ;
+ key_part < key_part_end ;
+ key_part++)
+ {
+ Field *field= key_part->field= outparam->field[key_part->fieldnr-1];
+
+ if (field->key_length() != key_part->length &&
+ !(field->flags & BLOB_FLAG))
+ {
+ /*
+ We are using only a prefix of the column as a key:
+ Create a new field for the key part that matches the index
+ */
+ field= key_part->field=field->new_field(&outparam->mem_root,
+ outparam, 0);
+ field->field_length= key_part->length;
+ }
+ }
+ }
+ }
+
+#ifdef WITH_PARTITION_STORAGE_ENGINE
+ if (share->partition_info_len && outparam->file)
+ {
+ /*
+ In this execution we must avoid calling thd->change_item_tree since
+ we might release memory before statement is completed. We do this
+ by changing to a new statement arena. As part of this arena we also
+ set the memory root to be the memory root of the table since we
+ call the parser and fix_fields which both can allocate memory for
+ item objects. We keep the arena to ensure that we can release the
+ free_list when closing the table object.
+ SEE Bug #21658
+ */
+
+ Query_arena *backup_stmt_arena_ptr= thd->stmt_arena;
+ Query_arena backup_arena;
+ Query_arena part_func_arena(&outparam->mem_root, Query_arena::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,
+ outparam, is_create_table,
+ share->default_part_db_type,
+ &work_part_info_used);
+ if (tmp)
+ {
+ thd->stmt_arena= backup_stmt_arena_ptr;
+ thd->restore_active_arena(&part_func_arena, &backup_arena);
+ goto partititon_err;
+ }
+ outparam->part_info->is_auto_partitioned= share->auto_partitioned;
+ DBUG_PRINT("info", ("autopartitioned: %u", share->auto_partitioned));
+ /* we should perform the fix_partition_func in either local or
+ caller's arena depending on work_part_info_used value
+ */
+ if (!work_part_info_used)
+ tmp= fix_partition_func(thd, outparam, is_create_table);
+ thd->stmt_arena= backup_stmt_arena_ptr;
+ thd->restore_active_arena(&part_func_arena, &backup_arena);
+ if (!tmp)
+ {
+ if (work_part_info_used)
+ tmp= fix_partition_func(thd, outparam, is_create_table);
+ outparam->part_info->item_free_list= part_func_arena.free_list;
+ }
+partititon_err:
+ if (tmp)
+ {
+ if (is_create_table)
+ {
+ /*
+ During CREATE/ALTER TABLE it is ok to receive errors here.
+ It is not ok if it happens during the opening of an frm
+ file as part of a normal query.
+ */
+ error_reported= TRUE;
+ }
+ goto err;
+ }
+ }
+#endif
+
+ /* Allocate bitmaps */
+
+ bitmap_size= share->column_bitmap_size;
+ if (!(bitmaps= (uchar*) alloc_root(&outparam->mem_root, bitmap_size*3)))
+ goto err;
+ bitmap_init(&outparam->def_read_set,
+ (my_bitmap_map*) bitmaps, share->fields, FALSE);
+ bitmap_init(&outparam->def_write_set,
+ (my_bitmap_map*) (bitmaps+bitmap_size), share->fields, FALSE);
+ bitmap_init(&outparam->tmp_set,
+ (my_bitmap_map*) (bitmaps+bitmap_size*2), share->fields, FALSE);
+ outparam->default_column_bitmaps();
+
/* The table struct is now initialized; Open the table */
- error=2;
+ error= 2;
if (db_stat)
{
int ha_err;
- unpack_filename(index_file,index_file);
if ((ha_err= (outparam->file->
- ha_open(index_file,
+ ha_open(outparam, share->normalized_path.str,
(db_stat & HA_READ_ONLY ? O_RDONLY : O_RDWR),
(db_stat & HA_OPEN_TEMPORARY ? HA_OPEN_TMP_TABLE :
((db_stat & HA_WAIT_IF_LOCKED) ||
@@ -942,56 +1885,79 @@ int openfrm(THD *thd, const char *name, const char *alias, uint db_stat,
outparam->file->auto_repair() &&
!(ha_open_flags & HA_OPEN_FOR_REPAIR));
- if (ha_err == HA_ERR_NO_SUCH_TABLE)
- {
- /* The table did not exists in storage engine, use same error message
- as if the .frm file didn't exist */
- error= 1;
- my_errno= ENOENT;
- }
- else
+ switch (ha_err)
{
- outparam->file->print_error(ha_err, MYF(0));
- error_reported= TRUE;
+ case HA_ERR_NO_SUCH_TABLE:
+ /*
+ The table did not exists in storage engine, use same error message
+ as if the .frm file didn't exist
+ */
+ error= 1;
+ my_errno= ENOENT;
+ break;
+ case EMFILE:
+ /*
+ Too many files opened, use same error message as if the .frm
+ file can't open
+ */
+ DBUG_PRINT("error", ("open file: %s failed, too many files opened (errno: %d)",
+ share->normalized_path.str, ha_err));
+ error= 1;
+ my_errno= EMFILE;
+ break;
+ default:
+ outparam->file->print_error(ha_err, MYF(0));
+ error_reported= TRUE;
+ if (ha_err == HA_ERR_TABLE_DEF_CHANGED)
+ error= 7;
+ break;
}
goto err; /* purecov: inspected */
}
}
- share->db_low_byte_first= outparam->file->low_byte_first();
- *root_ptr= old_root;
- thd->status_var.opened_tables++;
-#ifndef DBUG_OFF
- if (use_hash)
- (void) hash_check(&share->name_hash);
+#if defined(HAVE_purify) && !defined(DBUG_OFF)
+ bzero((char*) bitmaps, bitmap_size*3);
#endif
+
+ outparam->no_replicate= outparam->file &&
+ test(outparam->file->ha_table_flags() &
+ HA_HAS_OWN_BINLOGGING);
+ thd->status_var.opened_tables++;
+
DBUG_RETURN (0);
err:
- x_free((gptr) disk_buff);
- if (file > 0)
- VOID(my_close(file,MYF(MY_WME)));
-
- delete crypted;
- *root_ptr= old_root;
if (! error_reported)
- frm_error(error,outparam,name,ME_ERROR+ME_WAITTANG, errarg);
+ open_table_error(share, error, my_errno, 0);
delete outparam->file;
- outparam->file=0; // For easier errorchecking
+#ifdef WITH_PARTITION_STORAGE_ENGINE
+ if (outparam->part_info)
+ free_items(outparam->part_info->item_free_list);
+#endif
+ outparam->file= 0; // For easier error checking
outparam->db_stat=0;
- hash_free(&share->name_hash);
free_root(&outparam->mem_root, MYF(0)); // Safe to call on bzero'd root
my_free((char*) outparam->alias, MYF(MY_ALLOW_ZERO_PTR));
DBUG_RETURN (error);
-} /* openfrm */
+}
+
+/*
+ Free information allocated by openfrm
- /* close a .frm file and it's tables */
+ SYNOPSIS
+ closefrm()
+ table TABLE object to free
+ free_share Is 1 if we also want to free table_share
+*/
-int closefrm(register TABLE *table)
+int closefrm(register TABLE *table, bool free_share)
{
int error=0;
DBUG_ENTER("closefrm");
+ DBUG_PRINT("enter", ("table: 0x%lx", (long) table));
+
if (table->db_stat)
error=table->file->close();
my_free((char*) table->alias, MYF(MY_ALLOW_ZERO_PTR));
@@ -1004,7 +1970,21 @@ int closefrm(register TABLE *table)
}
delete table->file;
table->file= 0; /* For easier errorchecking */
- hash_free(&table->s->name_hash);
+#ifdef WITH_PARTITION_STORAGE_ENGINE
+ if (table->part_info)
+ {
+ free_items(table->part_info->item_free_list);
+ table->part_info->item_free_list= 0;
+ table->part_info= 0;
+ }
+#endif
+ if (free_share)
+ {
+ if (table->s->tmp_table == NO_TMP_TABLE)
+ release_table_share(table->s, RELEASE_NORMAL);
+ else
+ free_table_share(table->s);
+ }
free_root(&table->mem_root, MYF(0));
DBUG_RETURN(error);
}
@@ -1022,6 +2002,28 @@ void free_blobs(register TABLE *table)
}
+/**
+ Reclaim temporary blob storage which is bigger than
+ a threshold.
+
+ @param table A handle to the TABLE object containing blob fields
+ @param size The threshold value.
+
+*/
+
+void free_field_buffers_larger_than(TABLE *table, uint32 size)
+{
+ uint *ptr, *end;
+ for (ptr= table->s->blob_field, end=ptr + table->s->blob_fields ;
+ ptr != end ;
+ ptr++)
+ {
+ Field_blob *blob= (Field_blob*) table->field[*ptr];
+ if (blob->get_field_buffer_size() > size)
+ blob->free();
+ }
+}
+
/* Find where a form starts */
/* if formname is NullS then only formnames is read */
@@ -1033,7 +2035,7 @@ ulong get_form_pos(File file, uchar *head, TYPELIB *save_names)
DBUG_ENTER("get_form_pos");
names=uint2korr(head+8);
- a_length=(names+2)*sizeof(my_string); /* Room for two extra */
+ a_length=(names+2)*sizeof(char *); /* Room for two extra */
if (!save_names)
a_length=0;
@@ -1044,12 +2046,12 @@ ulong get_form_pos(File file, uchar *head, TYPELIB *save_names)
{
length=uint2korr(head+4);
VOID(my_seek(file,64L,MY_SEEK_SET,MYF(0)));
- if (!(buf= (uchar*) my_malloc((uint) length+a_length+names*4,
+ if (!(buf= (uchar*) my_malloc((size_t) length+a_length+names*4,
MYF(MY_WME))) ||
- my_read(file,(byte*) buf+a_length,(uint) (length+names*4),
+ my_read(file, buf+a_length, (size_t) (length+names*4),
MYF(MY_NABP)))
{ /* purecov: inspected */
- x_free((gptr) buf); /* purecov: inspected */
+ x_free((uchar*) buf); /* purecov: inspected */
DBUG_RETURN(0L); /* purecov: inspected */
}
pos= buf+a_length+length;
@@ -1058,7 +2060,7 @@ ulong get_form_pos(File file, uchar *head, TYPELIB *save_names)
if (! save_names)
{
if (names)
- my_free((gptr) buf,MYF(0));
+ my_free((uchar*) buf,MYF(0));
}
else if (!names)
bzero((char*) save_names,sizeof(save_names));
@@ -1072,19 +2074,24 @@ ulong get_form_pos(File file, uchar *head, TYPELIB *save_names)
}
- /* Read string from a file with malloc */
+/*
+ Read string from a file with malloc
+
+ NOTES:
+ We add an \0 at end of the read string to make reading of C strings easier
+*/
-int read_string(File file, gptr *to, uint length)
+int read_string(File file, uchar**to, size_t length)
{
DBUG_ENTER("read_string");
- x_free((gptr) *to);
- if (!(*to= (gptr) my_malloc(length+1,MYF(MY_WME))) ||
- my_read(file,(byte*) *to,length,MYF(MY_NABP)))
+ x_free(*to);
+ if (!(*to= (uchar*) my_malloc(length+1,MYF(MY_WME))) ||
+ my_read(file, *to, length,MYF(MY_NABP)))
{
- x_free((gptr) *to); /* purecov: inspected */
- *to= 0; /* purecov: inspected */
- DBUG_RETURN(1); /* purecov: inspected */
+ x_free(*to); /* purecov: inspected */
+ *to= 0; /* purecov: inspected */
+ DBUG_RETURN(1); /* purecov: inspected */
}
*((char*) *to+length)= '\0';
DBUG_RETURN (0);
@@ -1098,7 +2105,7 @@ ulong make_new_entry(File file, uchar *fileinfo, TYPELIB *formnames,
{
uint i,bufflength,maxlength,n_length,length,names;
ulong endpos,newpos;
- char buff[IO_SIZE];
+ uchar buff[IO_SIZE];
uchar *pos;
DBUG_ENTER("make_new_entry");
@@ -1118,17 +2125,17 @@ ulong make_new_entry(File file, uchar *fileinfo, TYPELIB *formnames,
while (endpos > maxlength)
{
VOID(my_seek(file,(ulong) (endpos-bufflength),MY_SEEK_SET,MYF(0)));
- if (my_read(file,(byte*) buff,bufflength,MYF(MY_NABP+MY_WME)))
+ if (my_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,(byte*) buff,bufflength,MYF(MY_NABP+MY_WME))))
+ if ((my_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,(byte*) buff,bufflength,MYF(MY_NABP+MY_WME)))
+ if (my_write(file,buff,bufflength,MYF(MY_NABP+MY_WME)))
DBUG_RETURN(0L);
maxlength+=IO_SIZE; /* Fix old ref */
int2store(fileinfo+6,maxlength);
@@ -1143,15 +2150,15 @@ ulong make_new_entry(File file, uchar *fileinfo, TYPELIB *formnames,
if (n_length == 1 )
{ /* First name */
length++;
- VOID(strxmov(buff,"/",newname,"/",NullS));
+ VOID(strxmov((char*) buff,"/",newname,"/",NullS));
}
else
- VOID(strxmov(buff,newname,"/",NullS)); /* purecov: inspected */
+ 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,(byte*) buff,(uint) length+1,MYF(MY_NABP+MY_WME)) ||
- (names && my_write(file,(byte*) (*formnames->type_names+n_length-1),
+ 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,(byte*) fileinfo+10,(uint) 4,MYF(MY_NABP+MY_WME)))
+ my_write(file, fileinfo+10, 4,MYF(MY_NABP+MY_WME)))
DBUG_RETURN(0L); /* purecov: inspected */
int2store(fileinfo+8,names+1);
@@ -1163,38 +2170,44 @@ ulong make_new_entry(File file, uchar *fileinfo, TYPELIB *formnames,
/* error message when opening a form file */
-static void frm_error(int error, TABLE *form, const char *name,
- myf errortype, int errarg)
+void open_table_error(TABLE_SHARE *share, int error, int db_errno, int errarg)
{
int err_no;
char buff[FN_REFLEN];
- const char *form_dev="",*datext;
- const char *real_name= (char*) name+dirname_length(name);
- DBUG_ENTER("frm_error");
+ myf errortype= ME_ERROR+ME_WAITTANG;
+ DBUG_ENTER("open_table_error");
switch (error) {
+ case 7:
case 1:
- if (my_errno == ENOENT)
+ if (db_errno == ENOENT)
+ my_error(ER_NO_SUCH_TABLE, MYF(0), share->db.str, share->table_name.str);
+ else
{
- char *db;
- uint length=dirname_part(buff,name);
- buff[length-1]=0;
- db=buff+dirname_length(buff);
- my_error(ER_NO_SUCH_TABLE, MYF(0), db, real_name);
+ strxmov(buff, share->normalized_path.str, reg_ext, NullS);
+ my_error((db_errno == EMFILE) ? ER_CANT_OPEN_FILE : ER_FILE_NOT_FOUND,
+ errortype, buff, db_errno);
}
- else
- my_error((my_errno == EMFILE) ? ER_CANT_OPEN_FILE : ER_FILE_NOT_FOUND,
- errortype,
- fn_format(buff, name, form_dev, reg_ext, 0), my_errno);
break;
case 2:
{
- datext= form->file ? *form->file->bas_ext() : "";
- datext= datext==NullS ? "" : datext;
- err_no= (my_errno == ENOENT) ? ER_FILE_NOT_FOUND : (my_errno == EAGAIN) ?
+ handler *file= 0;
+ const char *datext= "";
+
+ if (share->db_type() != NULL)
+ {
+ if ((file= get_new_handler(share, current_thd->mem_root,
+ share->db_type())))
+ {
+ if (!(datext= *file->bas_ext()))
+ datext= "";
+ }
+ }
+ err_no= (db_errno == ENOENT) ? ER_FILE_NOT_FOUND : (db_errno == EAGAIN) ?
ER_FILE_USED : ER_CANT_OPEN_FILE;
- my_error(err_no,errortype,
- fn_format(buff,real_name,form_dev,datext,2),my_errno);
+ strxmov(buff, share->normalized_path.str, datext, NullS);
+ my_error(err_no,errortype, buff, db_errno);
+ delete file;
break;
}
case 5:
@@ -1208,23 +2221,26 @@ static void frm_error(int error, TABLE *form, const char *name,
}
my_printf_error(ER_UNKNOWN_COLLATION,
"Unknown collation '%s' in table '%-.64s' definition",
- MYF(0), csname, real_name);
+ MYF(0), csname, share->table_name.str);
break;
}
case 6:
+ strxmov(buff, share->normalized_path.str, reg_ext, NullS);
my_printf_error(ER_NOT_FORM_FILE,
"Table '%-.64s' was created with a different version "
- "of MySQL and cannot be read",
- MYF(0), name);
+ "of MySQL and cannot be read",
+ MYF(0), buff);
+ break;
+ case 8:
break;
default: /* Better wrong error than none */
case 4:
- my_error(ER_NOT_FORM_FILE, errortype,
- fn_format(buff, name, form_dev, reg_ext, 0));
+ strxmov(buff, share->normalized_path.str, reg_ext, NullS);
+ my_error(ER_NOT_FORM_FILE, errortype, buff, 0);
break;
}
DBUG_VOID_RETURN;
-} /* frm_error */
+} /* open_table_error */
/*
@@ -1304,22 +2320,21 @@ TYPELIB *typelib(MEM_ROOT *mem_root, List<String> &strings)
# field number +1
*/
-static uint find_field(TABLE *form,uint start,uint length)
+static uint find_field(Field **fields, uchar *record, uint start, uint length)
{
Field **field;
- uint i, pos, fields;
+ uint i, pos;
- pos=0;
- fields= form->s->fields;
- for (field=form->field, i=1 ; i<= fields ; i++,field++)
+ pos= 0;
+ for (field= fields, i=1 ; *field ; i++,field++)
{
- if ((*field)->offset() == start)
+ if ((*field)->offset(record) == start)
{
if ((*field)->key_length() == length)
return (i);
- if (!pos || form->field[pos-1]->pack_length() <
+ if (!pos || fields[pos-1]->pack_length() <
(*field)->pack_length())
- pos=i;
+ pos= i;
}
}
return (pos);
@@ -1409,15 +2424,16 @@ void append_unescaped(String *res, const char *pos, uint length)
res->append('\'');
}
+
/* Create a .frm file */
-File create_frm(THD *thd, my_string name, const char *db,
+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)
{
register File file;
ulong length;
- char fill[IO_SIZE];
+ uchar fill[IO_SIZE];
int create_flags= O_RDWR | O_TRUNC;
if (create_info->options & HA_LEX_CREATE_TMP_TABLE)
@@ -1429,12 +2445,6 @@ File create_frm(THD *thd, my_string name, const char *db,
if (create_info->min_rows > UINT_MAX32)
create_info->min_rows= UINT_MAX32;
- /*
- Ensure that raid_chunks can't be larger than 255, as this would cause
- problems with drop database
- */
- set_if_smaller(create_info->raid_chunks, 255);
-
if ((file= my_create(name, CREATE_MODE, create_flags, MYF(0))) >= 0)
{
uint key_length, tmp_key_length;
@@ -1445,7 +2455,8 @@ File create_frm(THD *thd, my_string name, const char *db,
fileinfo[1]= 1;
fileinfo[2]= FRM_VER+3+ test(create_info->varchar);
- fileinfo[3]= (uchar) ha_checktype(thd,create_info->db_type,0,0);
+ fileinfo[3]= (uchar) ha_legacy_type(
+ ha_checktype(thd,ha_legacy_type(create_info->db_type),0,0));
fileinfo[4]=1;
int2store(fileinfo+6,IO_SIZE); /* Next block starts here */
/*
@@ -1469,7 +2480,9 @@ File create_frm(THD *thd, my_string name, const char *db,
int2store(fileinfo+16,reclength);
int4store(fileinfo+18,create_info->max_rows);
int4store(fileinfo+22,create_info->min_rows);
+ /* fileinfo[26] is set in mysql_create_frm() */
fileinfo[27]=2; // Use long pack-fields
+ /* fileinfo[28 & 29] is set to key_info_length in mysql_create_frm() */
create_info->table_options|=HA_OPTION_LONG_BLOB_PTR; // Use portable blob pointers
int2store(fileinfo+30,create_info->table_options);
fileinfo[32]=0; // No filename anymore
@@ -1477,18 +2490,29 @@ File create_frm(THD *thd, my_string name, const char *db,
int4store(fileinfo+34,create_info->avg_row_length);
fileinfo[38]= (create_info->default_table_charset ?
create_info->default_table_charset->number : 0);
+ fileinfo[39]= (uchar) ((uint) create_info->transactional |
+ ((uint) create_info->page_checksum << 2));
fileinfo[40]= (uchar) create_info->row_type;
- fileinfo[41]= (uchar) create_info->raid_type;
- fileinfo[42]= (uchar) create_info->raid_chunks;
- int4store(fileinfo+43,create_info->raid_chunksize);
+ /* Next few bytes where for RAID support */
+ fileinfo[41]= 0;
+ fileinfo[42]= 0;
+ fileinfo[43]= 0;
+ fileinfo[44]= 0;
+ fileinfo[45]= 0;
+ fileinfo[46]= 0;
int4store(fileinfo+47, key_length);
tmp= MYSQL_VERSION_ID; // Store to avoid warning from int4store
int4store(fileinfo+51, tmp);
- int2store(fileinfo+55, create_info->extra_size);
+ int4store(fileinfo+55, create_info->extra_size);
+ /*
+ 59-60 is reserved for extra_rec_buf_length,
+ 61 for default_part_db_type
+ */
+ int2store(fileinfo+62, create_info->key_block_size);
bzero(fill,IO_SIZE);
for (; length > IO_SIZE ; length-= IO_SIZE)
{
- if (my_write(file,(byte*) fill,IO_SIZE,MYF(MY_WME | MY_NABP)))
+ if (my_write(file,fill, IO_SIZE, MYF(MY_WME | MY_NABP)))
{
VOID(my_close(file,MYF(0)));
VOID(my_delete(name,MYF(0)));
@@ -1517,11 +2541,9 @@ void update_create_info_from_table(HA_CREATE_INFO *create_info, TABLE *table)
create_info->table_options= share->db_create_options;
create_info->avg_row_length= share->avg_row_length;
create_info->row_type= share->row_type;
- create_info->raid_type= share->raid_type;
- create_info->raid_chunks= share->raid_chunks;
- create_info->raid_chunksize= share->raid_chunksize;
create_info->default_table_charset= share->table_charset;
create_info->table_charset= 0;
+ create_info->comment= share->comment;
DBUG_VOID_RETURN;
}
@@ -1597,13 +2619,37 @@ char *get_field(MEM_ROOT *mem, Field *field)
return to;
}
+/*
+ DESCRIPTION
+ given a buffer with a key value, and a map of keyparts
+ that are present in this value, returns the length of the value
+*/
+uint calculate_key_len(TABLE *table, uint key, const uchar *buf,
+ key_part_map keypart_map)
+{
+ /* works only with key prefixes */
+ DBUG_ASSERT(((keypart_map + 1) & keypart_map) == 0);
+
+ 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;
+ uint length= 0;
+
+ while (key_part < end_key_part && keypart_map)
+ {
+ length+= key_part->store_length;
+ keypart_map >>= 1;
+ key_part++;
+ }
+ return length;
+}
/*
Check if database name is valid
SYNPOSIS
check_db_name()
- name Name of database
+ org_name Name of database and length
NOTES
If lower_case_table_names is set then database is converted to lower case
@@ -1613,51 +2659,52 @@ char *get_field(MEM_ROOT *mem, Field *field)
1 error
*/
-bool check_db_name(char *name)
+bool check_db_name(LEX_STRING *org_name)
{
- char *start= name;
- /* Used to catch empty names and names with end space */
- bool last_char_is_space= TRUE;
+ char *name= org_name->str;
+ uint name_length= org_name->length;
+
+ if (!name_length || name_length > NAME_LEN)
+ return 1;
if (lower_case_table_names && name != any_db)
my_casedn_str(files_charset_info, name);
- while (*name)
- {
#if defined(USE_MB) && defined(USE_MB_IDENT)
- last_char_is_space= my_isspace(system_charset_info, *name);
- if (use_mb(system_charset_info))
+ if (use_mb(system_charset_info))
+ {
+ name_length= 0;
+ bool last_char_is_space= TRUE;
+ char *end= name + org_name->length;
+ while (name < end)
{
- int len=my_ismbchar(system_charset_info, name,
- name+system_charset_info->mbmaxlen);
- if (len)
- {
- name += len;
- continue;
- }
+ int len;
+ last_char_is_space= my_isspace(system_charset_info, *name);
+ len= my_ismbchar(system_charset_info, name, end);
+ if (!len)
+ len= 1;
+ name+= len;
+ name_length++;
}
-#else
- last_char_is_space= *name==' ';
-#endif
- if (*name == '/' || *name == '\\' || *name == FN_LIBCHAR ||
- *name == FN_EXTCHAR)
- return 1;
- name++;
+ return (last_char_is_space || name_length > NAME_CHAR_LEN);
}
- return last_char_is_space || (uint) (name - start) > NAME_LEN;
+ else
+#endif
+ return ((org_name->str[org_name->length - 1] != ' ') ||
+ (name_length > NAME_CHAR_LEN)); /* purecov: inspected */
}
/*
Allow anything as a table name, as long as it doesn't contain an
- a '/', or a '.' character
- or ' ' at the end
+ ' ' at the end
returns 1 on error
*/
bool check_table_name(const char *name, uint length)
{
+ uint name_length= 0; // name length in symbols
const char *end= name+length;
if (!length || length > NAME_LEN)
return 1;
@@ -1678,16 +2725,16 @@ bool check_table_name(const char *name, uint length)
if (len)
{
name += len;
+ name_length++;
continue;
}
}
#endif
- if (*name == '/' || *name == '\\' || *name == FN_EXTCHAR)
- return 1;
name++;
+ name_length++;
}
#if defined(USE_MB) && defined(USE_MB_IDENT)
- return last_char_is_space;
+ return (last_char_is_space || name_length > NAME_CHAR_LEN) ;
#else
return 0;
#endif
@@ -1696,7 +2743,7 @@ bool check_table_name(const char *name, uint length)
bool check_column_name(const char *name)
{
- const char *start= name;
+ uint name_length= 0; // name length in symbols
bool last_char_is_space= TRUE;
while (*name)
@@ -1710,6 +2757,7 @@ bool check_column_name(const char *name)
if (len)
{
name += len;
+ name_length++;
continue;
}
}
@@ -1719,11 +2767,153 @@ bool check_column_name(const char *name)
if (*name == NAMES_SEP_CHAR)
return 1;
name++;
+ name_length++;
}
/* Error if empty or too long column name */
- return last_char_is_space || (uint) (name - start) > NAME_LEN;
+ return last_char_is_space || (uint) name_length > NAME_CHAR_LEN;
}
+
+/**
+ Checks whether a table is intact. Should be done *just* after the table has
+ been opened.
+
+ @param[in] table The table to check
+ @param[in] table_f_count Expected number of columns in the table
+ @param[in] table_def Expected structure of the table (column name
+ and type)
+
+ @retval FALSE OK
+ @retval TRUE There was an error. An error message is output
+ to the error log. We do not push an error
+ message into the error stack because this
+ function is currently only called at start up,
+ and such errors never reach the user.
+*/
+
+my_bool
+table_check_intact(TABLE *table, const uint table_f_count,
+ const TABLE_FIELD_W_TYPE *table_def)
+{
+ uint i;
+ my_bool error= FALSE;
+ my_bool fields_diff_count;
+ DBUG_ENTER("table_check_intact");
+ DBUG_PRINT("info",("table: %s expected_count: %d",
+ table->alias, table_f_count));
+
+ fields_diff_count= (table->s->fields != table_f_count);
+ if (fields_diff_count)
+ {
+ DBUG_PRINT("info", ("Column count has changed, checking the definition"));
+
+ /* previous MySQL version */
+ if (MYSQL_VERSION_ID > table->s->mysql_version)
+ {
+ sql_print_error(ER(ER_COL_COUNT_DOESNT_MATCH_PLEASE_UPDATE),
+ table->alias, table_f_count, table->s->fields,
+ table->s->mysql_version, MYSQL_VERSION_ID);
+ DBUG_RETURN(TRUE);
+ }
+ else if (MYSQL_VERSION_ID == table->s->mysql_version)
+ {
+ sql_print_error(ER(ER_COL_COUNT_DOESNT_MATCH_CORRUPTED), table->alias,
+ table_f_count, table->s->fields);
+ DBUG_RETURN(TRUE);
+ }
+ /*
+ Something has definitely changed, but we're running an older
+ version of MySQL with new system tables.
+ Let's check column definitions. If a column was added at
+ the end of the table, then we don't care much since such change
+ is backward compatible.
+ */
+ }
+ char buffer[STRING_BUFFER_USUAL_SIZE];
+ for (i=0 ; i < table_f_count; i++, table_def++)
+ {
+ String sql_type(buffer, sizeof(buffer), system_charset_info);
+ sql_type.length(0);
+ if (i < table->s->fields)
+ {
+ Field *field= table->field[i];
+
+ if (strncmp(field->field_name, table_def->name.str,
+ table_def->name.length))
+ {
+ /*
+ Name changes are not fatal, we use ordinal numbers to access columns.
+ Still this can be a sign of a tampered table, output an error
+ to the error log.
+ */
+ sql_print_error("Incorrect definition of table %s.%s: "
+ "expected column '%s' at position %d, found '%s'.",
+ table->s->db.str, table->alias, table_def->name.str, i,
+ field->field_name);
+ }
+ field->sql_type(sql_type);
+ /*
+ Generally, if column types don't match, then something is
+ wrong.
+
+ However, we only compare column definitions up to the
+ length of the original definition, since we consider the
+ following definitions compatible:
+
+ 1. DATETIME and DATETIM
+ 2. INT(11) and INT(11
+ 3. SET('one', 'two') and SET('one', 'two', 'more')
+
+ For SETs or ENUMs, if the same prefix is there it's OK to
+ add more elements - they will get higher ordinal numbers and
+ the new table definition is backward compatible with the
+ original one.
+ */
+ if (strncmp(sql_type.c_ptr_safe(), table_def->type.str,
+ table_def->type.length - 1))
+ {
+ sql_print_error("Incorrect definition of table %s.%s: "
+ "expected column '%s' at position %d to have type "
+ "%s, found type %s.", table->s->db.str, table->alias,
+ table_def->name.str, i, table_def->type.str,
+ sql_type.c_ptr_safe());
+ error= TRUE;
+ }
+ else if (table_def->cset.str && !field->has_charset())
+ {
+ sql_print_error("Incorrect definition of table %s.%s: "
+ "expected the type of column '%s' at position %d "
+ "to have character set '%s' but the type has no "
+ "character set.", table->s->db.str, table->alias,
+ table_def->name.str, i, table_def->cset.str);
+ error= TRUE;
+ }
+ else if (table_def->cset.str &&
+ strcmp(field->charset()->csname, table_def->cset.str))
+ {
+ sql_print_error("Incorrect definition of table %s.%s: "
+ "expected the type of column '%s' at position %d "
+ "to have character set '%s' but found "
+ "character set '%s'.", table->s->db.str, table->alias,
+ table_def->name.str, i, table_def->cset.str,
+ field->charset()->csname);
+ error= TRUE;
+ }
+ }
+ else
+ {
+ sql_print_error("Incorrect definition of table %s.%s: "
+ "expected column '%s' at position %d to have type %s "
+ " but the column is not found.",
+ table->s->db.str, table->alias,
+ table_def->name.str, i, table_def->type.str);
+ error= TRUE;
+ }
+ }
+ DBUG_RETURN(error);
+}
+
+
/*
Create Item_field for each column in the table.
@@ -1795,7 +2985,7 @@ void TABLE_LIST::calc_md5(char *buffer)
my_MD5_CTX context;
uchar digest[16];
my_MD5Init(&context);
- my_MD5Update(&context,(uchar *) query.str, query.length);
+ my_MD5Update(&context,(uchar *) select_stmt.str, select_stmt.length);
my_MD5Final(digest, &context);
sprintf((char *) buffer,
"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
@@ -1806,16 +2996,27 @@ void TABLE_LIST::calc_md5(char *buffer)
}
-/*
- set underlying TABLE for table place holder of VIEW
+/**
+ @brief Set underlying table for table place holder of view.
- DESCRIPTION
- Replace all views that only uses one table with the table itself.
- This allows us to treat the view as a simple table and even update
- it (it is a kind of optimisation)
+ @details
- SYNOPSIS
- TABLE_LIST::set_underlying_merge()
+ Replace all views that only use one table with the table itself. This
+ allows us to treat the view as a simple table and even update it (it is a
+ kind of optimization).
+
+ @note
+
+ This optimization is potentially dangerous as it makes views
+ masquerade as base tables: Views don't have the pointer TABLE_LIST::table
+ set to non-@c NULL.
+
+ We may have the case where a view accesses tables not normally accessible
+ in the current Security_context (only in the definer's
+ Security_context). According to the table's GRANT_INFO (TABLE::grant),
+ access is fulfilled, but this is implicitly meant in the definer's security
+ context. Hence we must never look at only a TABLE's GRANT_INFO without
+ looking at the one of the referring TABLE_LIST.
*/
void TABLE_LIST::set_underlying_merge()
@@ -1879,7 +3080,7 @@ bool TABLE_LIST::setup_underlying(THD *thd)
List_iterator_fast<Item> it(select->item_list);
uint field_count= 0;
- if (check_stack_overrun(thd, STACK_MIN_SIZE, (char *)&field_count))
+ if (check_stack_overrun(thd, STACK_MIN_SIZE, (uchar*) &field_count))
{
DBUG_RETURN(TRUE);
}
@@ -2127,31 +3328,32 @@ bool TABLE_LIST::prep_check_option(THD *thd, uint8 check_opt_type)
}
-/*
+/**
Hide errors which show view underlying table information
- SYNOPSIS
- TABLE_LIST::hide_view_error()
- thd thread handler
+ @param[in,out] thd thread handler
+ @pre This method can be called only if there is an error.
*/
void TABLE_LIST::hide_view_error(THD *thd)
{
/* Hide "Unknown column" or "Unknown function" error */
- if (thd->net.last_errno == ER_BAD_FIELD_ERROR ||
- thd->net.last_errno == ER_SP_DOES_NOT_EXIST ||
- thd->net.last_errno == ER_PROCACCESS_DENIED_ERROR ||
- thd->net.last_errno == ER_COLUMNACCESS_DENIED_ERROR ||
- thd->net.last_errno == ER_TABLEACCESS_DENIED_ERROR ||
- thd->net.last_errno == ER_TABLE_NOT_LOCKED ||
- thd->net.last_errno == ER_NO_SUCH_TABLE)
+ 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_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)
{
TABLE_LIST *top= top_table();
- thd->clear_error();
+ thd->clear_error();
my_error(ER_VIEW_INVALID, MYF(0), top->view_db.str, top->view_name.str);
}
- else if (thd->net.last_errno == ER_NO_DEFAULT_FOR_FIELD)
+ else if (thd->main_da.sql_errno() == ER_NO_DEFAULT_FOR_FIELD)
{
TABLE_LIST *top= top_table();
thd->clear_error();
@@ -2205,7 +3407,7 @@ void TABLE_LIST::cleanup_items()
for (Field_translator *transl= field_translation;
transl < field_translation_end;
transl++)
- transl->item->walk(&Item::cleanup_processor, 0);
+ transl->item->walk(&Item::cleanup_processor, 0, 0);
}
@@ -2299,7 +3501,7 @@ bool TABLE_LIST::set_insert_values(MEM_ROOT *mem_root)
if (table)
{
if (!table->insert_values &&
- !(table->insert_values= (byte *)alloc_root(mem_root,
+ !(table->insert_values= (uchar *)alloc_root(mem_root,
table->s->rec_buff_length)))
return TRUE;
}
@@ -2701,9 +3903,9 @@ const char *Natural_join_column::db_name()
are inconsistent in this respect.
*/
DBUG_ASSERT(!strcmp(table_ref->db,
- table_ref->table->s->db) ||
+ table_ref->table->s->db.str) ||
(table_ref->schema_table &&
- table_ref->table->s->db[0] == 0));
+ table_ref->table->s->db.str[0] == 0));
return table_ref->db;
}
@@ -2787,7 +3989,7 @@ Item *create_view_field(THD *thd, TABLE_LIST *view, Item **field_ref,
field= *field_ref;
}
thd->lex->current_select->no_wrap_view_item= save_wrapper;
- if (thd->lex->current_select->no_wrap_view_item)
+ if (save_wrapper)
{
DBUG_RETURN(field);
}
@@ -2894,7 +4096,7 @@ void Field_iterator_table_ref::next()
}
-const char *Field_iterator_table_ref::table_name()
+const char *Field_iterator_table_ref::get_table_name()
{
if (table_ref->view)
return table_ref->view_name.str;
@@ -2902,12 +4104,12 @@ const char *Field_iterator_table_ref::table_name()
return natural_join_it.column_ref()->table_name();
DBUG_ASSERT(!strcmp(table_ref->table_name,
- table_ref->table->s->table_name));
+ table_ref->table->s->table_name.str));
return table_ref->table_name;
}
-const char *Field_iterator_table_ref::db_name()
+const char *Field_iterator_table_ref::get_db_name()
{
if (table_ref->view)
return table_ref->view_db.str;
@@ -2919,9 +4121,9 @@ const char *Field_iterator_table_ref::db_name()
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) ||
+ DBUG_ASSERT(!strcmp(table_ref->db, table_ref->table->s->db.str) ||
(table_ref->schema_table &&
- table_ref->table->s->db[0] == 0));
+ table_ref->table->s->db.str[0] == 0));
return table_ref->db;
}
@@ -2983,7 +4185,7 @@ Field_iterator_table_ref::get_or_create_column_ref(THD *thd, TABLE_LIST *parent_
TABLE_LIST *add_table_ref= parent_table_ref ?
parent_table_ref : table_ref;
LINT_INIT(field_count);
-
+
if (field_it == &table_field_it)
{
/* The field belongs to a stored table. */
@@ -3000,8 +4202,8 @@ Field_iterator_table_ref::get_or_create_column_ref(THD *thd, TABLE_LIST *parent_
/* The field belongs to a merge view or information schema table. */
Field_translator *translated_field= view_field_it.field_translator();
nj_col= new Natural_join_column(translated_field, table_ref);
- field_count= (uint) (table_ref->field_translation_end -
- table_ref->field_translation);
+ field_count= table_ref->field_translation_end -
+ table_ref->field_translation;
}
else
{
@@ -3085,6 +4287,279 @@ Field_iterator_table_ref::get_natural_column_ref()
return nj_col;
}
+/*****************************************************************************
+ Functions to handle column usage bitmaps (read_set, write_set etc...)
+*****************************************************************************/
+
+/* Reset all columns bitmaps */
+
+void st_table::clear_column_bitmaps()
+{
+ /*
+ Reset column read/write usage. It's identical to:
+ bitmap_clear_all(&table->def_read_set);
+ bitmap_clear_all(&table->def_write_set);
+ */
+ bzero((char*) def_read_set.bitmap, s->column_bitmap_size*2);
+ column_bitmaps_set(&def_read_set, &def_write_set);
+}
+
+
+/*
+ Tell handler we are going to call position() and rnd_pos() later.
+
+ NOTES:
+ This is needed for handlers that uses the primary key to find the
+ row. In this case we have to extend the read bitmap with the primary
+ key fields.
+*/
+
+void st_table::prepare_for_position()
+{
+ DBUG_ENTER("st_table::prepare_for_position");
+
+ if ((file->ha_table_flags() & HA_PRIMARY_KEY_IN_READ_INDEX) &&
+ s->primary_key < MAX_KEY)
+ {
+ mark_columns_used_by_index_no_reset(s->primary_key, read_set);
+ /* signal change */
+ file->column_bitmaps_signal();
+ }
+ DBUG_VOID_RETURN;
+}
+
+
+/*
+ Mark that only fields from one key is used
+
+ 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()
+*/
+
+void st_table::mark_columns_used_by_index(uint index)
+{
+ MY_BITMAP *bitmap= &tmp_set;
+ DBUG_ENTER("st_table::mark_columns_used_by_index");
+
+ (void) file->extra(HA_EXTRA_KEYREAD);
+ bitmap_clear_all(bitmap);
+ mark_columns_used_by_index_no_reset(index, bitmap);
+ column_bitmaps_set(bitmap, bitmap);
+ DBUG_VOID_RETURN;
+}
+
+
+/*
+ Restore to use normal column maps after key read
+
+ NOTES
+ This reverse the change done by mark_columns_used_by_index
+
+ WARNING
+ For this to work, one must have the normal table maps in place
+ when calling mark_columns_used_by_index
+*/
+
+void st_table::restore_column_maps_after_mark_index()
+{
+ DBUG_ENTER("st_table::restore_column_maps_after_mark_index");
+
+ key_read= 0;
+ (void) file->extra(HA_EXTRA_NO_KEYREAD);
+ default_column_bitmaps();
+ file->column_bitmaps_signal();
+ DBUG_VOID_RETURN;
+}
+
+
+/*
+ mark columns used by key, but don't reset other fields
+*/
+
+void st_table::mark_columns_used_by_index_no_reset(uint index,
+ MY_BITMAP *bitmap)
+{
+ KEY_PART_INFO *key_part= key_info[index].key_part;
+ KEY_PART_INFO *key_part_end= (key_part +
+ key_info[index].key_parts);
+ for (;key_part != key_part_end; key_part++)
+ bitmap_set_bit(bitmap, key_part->fieldnr-1);
+}
+
+
+/*
+ Mark auto-increment fields as used fields in both read and write maps
+
+ NOTES
+ This is needed in insert & update as the auto-increment field is
+ always set and sometimes read.
+*/
+
+void st_table::mark_auto_increment_column()
+{
+ DBUG_ASSERT(found_next_number_field);
+ /*
+ We must set bit in read set as update_auto_increment() is using the
+ store() to check overflow of auto_increment values
+ */
+ bitmap_set_bit(read_set, found_next_number_field->field_index);
+ bitmap_set_bit(write_set, found_next_number_field->field_index);
+ if (s->next_number_keypart)
+ mark_columns_used_by_index_no_reset(s->next_number_index, read_set);
+ file->column_bitmaps_signal();
+}
+
+
+/*
+ Mark columns needed for doing an delete of a row
+
+ DESCRIPTON
+ Some table engines don't have a cursor on the retrieve rows
+ so they need either to use the primary key or all columns to
+ be able to delete a row.
+
+ If the engine needs this, the function works as follows:
+ - If primary key exits, mark the primary key columns to be read.
+ - If not, mark all columns to be read
+
+ If the engine has HA_REQUIRES_KEY_COLUMNS_FOR_DELETE, we will
+ mark all key columns as 'to-be-read'. This allows the engine to
+ loop over the given record to find all keys and doesn't have to
+ retrieve the row again.
+*/
+
+void st_table::mark_columns_needed_for_delete()
+{
+ if (triggers)
+ triggers->mark_fields_used(TRG_EVENT_DELETE);
+ if (file->ha_table_flags() & HA_REQUIRES_KEY_COLUMNS_FOR_DELETE)
+ {
+ Field **reg_field;
+ for (reg_field= field ; *reg_field ; reg_field++)
+ {
+ if ((*reg_field)->flags & PART_KEY_FLAG)
+ bitmap_set_bit(read_set, (*reg_field)->field_index);
+ }
+ file->column_bitmaps_signal();
+ }
+ if (file->ha_table_flags() & HA_PRIMARY_KEY_REQUIRED_FOR_DELETE)
+ {
+ /*
+ If the handler has no cursor capabilites, we have to read either
+ the primary key, the hidden primary key or all columns to be
+ able to do an delete
+ */
+ if (s->primary_key == MAX_KEY)
+ file->use_hidden_primary_key();
+ else
+ {
+ mark_columns_used_by_index_no_reset(s->primary_key, read_set);
+ file->column_bitmaps_signal();
+ }
+ }
+}
+
+
+/*
+ Mark columns needed for doing an update of a row
+
+ DESCRIPTON
+ Some engines needs to have all columns in an update (to be able to
+ build a complete row). If this is the case, we mark all not
+ updated columns to be read.
+
+ If this is no the case, we do like in the delete case and mark
+ if neeed, either the primary key column or all columns to be read.
+ (see mark_columns_needed_for_delete() for details)
+
+ If the engine has HA_REQUIRES_KEY_COLUMNS_FOR_DELETE, we will
+ mark all USED key columns as 'to-be-read'. This allows the engine to
+ loop over the given record to find all changed keys and doesn't have to
+ retrieve the row again.
+*/
+
+void st_table::mark_columns_needed_for_update()
+{
+ DBUG_ENTER("mark_columns_needed_for_update");
+ if (triggers)
+ triggers->mark_fields_used(TRG_EVENT_UPDATE);
+ if (file->ha_table_flags() & HA_REQUIRES_KEY_COLUMNS_FOR_DELETE)
+ {
+ /* Mark all used key columns for read */
+ Field **reg_field;
+ for (reg_field= field ; *reg_field ; reg_field++)
+ {
+ /* Merge keys is all keys that had a column refered to in the query */
+ if (merge_keys.is_overlapping((*reg_field)->part_of_key))
+ bitmap_set_bit(read_set, (*reg_field)->field_index);
+ }
+ file->column_bitmaps_signal();
+ }
+ if (file->ha_table_flags() & HA_PRIMARY_KEY_REQUIRED_FOR_DELETE)
+ {
+ /*
+ If the handler has no cursor capabilites, we have to read either
+ the primary key, the hidden primary key or all columns to be
+ able to do an update
+ */
+ if (s->primary_key == MAX_KEY)
+ file->use_hidden_primary_key();
+ else
+ {
+ mark_columns_used_by_index_no_reset(s->primary_key, read_set);
+ file->column_bitmaps_signal();
+ }
+ }
+ DBUG_VOID_RETURN;
+}
+
+
+/*
+ Mark columns the handler needs for doing an insert
+
+ For now, this is used to mark fields used by the trigger
+ as changed.
+*/
+
+void st_table::mark_columns_needed_for_insert()
+{
+ if (triggers)
+ {
+ /*
+ We don't need to mark columns which are used by ON DELETE and
+ ON UPDATE triggers, which may be invoked in case of REPLACE or
+ INSERT ... ON DUPLICATE KEY UPDATE, since before doing actual
+ row replacement or update write_record() will mark all table
+ fields as used.
+ */
+ triggers->mark_fields_used(TRG_EVENT_INSERT);
+ }
+ if (found_next_number_field)
+ mark_auto_increment_column();
+}
+
+
+/**
+ @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
+
+ @detail
+ 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));
+}
+
/*
Cleanup this table for re-execution.
@@ -3133,6 +4608,193 @@ Item_subselect *TABLE_LIST::containing_subselect()
return (select_lex ? select_lex->master_unit()->item : 0);
}
+/*
+ Compiles the tagged hints list and fills up the bitmasks.
+
+ SYNOPSIS
+ process_index_hints()
+ table the TABLE to operate on.
+
+ 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 and st_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
+ (if non-empty) is appended to the USE INDEX list and a flag is set.
+
+ Multiple hints of the same kind are processed so that each clause
+ is applied to what is computed in the previous clause.
+ For example:
+ USE INDEX (i1) USE INDEX (i2)
+ is equivalent to
+ USE INDEX (i1,i2)
+ and means "consider only i1 and i2".
+
+ Similarly
+ USE INDEX () USE INDEX (i1)
+ is equivalent to
+ USE INDEX (i1)
+ and means "consider only the index i1"
+
+ It is OK to have the same index several times, e.g. "USE INDEX (i1,i1)" is
+ not an error.
+
+ Different kind of hints (USE/FORCE/IGNORE) are processed in the following
+ order:
+ 1. All indexes in USE (or FORCE) INDEX are added to the mask.
+ 2. All IGNORE INDEX
+
+ e.g. "USE INDEX i1, IGNORE INDEX i1, USE INDEX i1" will not use i1 at all
+ as if we had "USE INDEX i1, USE INDEX i1, IGNORE INDEX i1".
+
+ As an optimization if there is a covering index, and we have
+ IGNORE INDEX FOR GROUP/ORDER, and this index is used for the JOIN part,
+ then we have to ignore the IGNORE INDEX FROM GROUP/ORDER.
+
+ RETURN VALUE
+ FALSE no errors found
+ TRUE found and reported an error.
+*/
+bool TABLE_LIST::process_index_hints(TABLE *tbl)
+{
+ /* initialize the result variables */
+ tbl->keys_in_use_for_query= tbl->keys_in_use_for_group_by=
+ tbl->keys_in_use_for_order_by= tbl->s->keys_in_use;
+
+ /* index hint list processing */
+ if (index_hints)
+ {
+ key_map index_join[INDEX_HINT_FORCE + 1];
+ key_map index_order[INDEX_HINT_FORCE + 1];
+ key_map index_group[INDEX_HINT_FORCE + 1];
+ Index_hint *hint;
+ int type;
+ bool have_empty_use_join= FALSE, have_empty_use_order= FALSE,
+ have_empty_use_group= FALSE;
+ List_iterator <Index_hint> iter(*index_hints);
+
+ /* initialize temporary variables used to collect hints of each kind */
+ for (type= INDEX_HINT_IGNORE; type <= INDEX_HINT_FORCE; type++)
+ {
+ index_join[type].clear_all();
+ index_order[type].clear_all();
+ index_group[type].clear_all();
+ }
+
+ /* iterate over the hints list */
+ while ((hint= iter++))
+ {
+ uint pos;
+
+ /* process empty USE INDEX () */
+ if (hint->type == INDEX_HINT_USE && !hint->key_name.str)
+ {
+ if (hint->clause & INDEX_HINT_MASK_JOIN)
+ {
+ index_join[hint->type].clear_all();
+ have_empty_use_join= TRUE;
+ }
+ if (hint->clause & INDEX_HINT_MASK_ORDER)
+ {
+ index_order[hint->type].clear_all();
+ have_empty_use_order= TRUE;
+ }
+ if (hint->clause & INDEX_HINT_MASK_GROUP)
+ {
+ index_group[hint->type].clear_all();
+ have_empty_use_group= TRUE;
+ }
+ continue;
+ }
+
+ /*
+ Check if an index with the given name exists and get his offset in
+ the keys bitmask for the table
+ */
+ if (tbl->s->keynames.type_names == 0 ||
+ (pos= find_type(&tbl->s->keynames, hint->key_name.str,
+ hint->key_name.length, 1)) <= 0)
+ {
+ my_error(ER_KEY_DOES_NOT_EXITS, MYF(0), hint->key_name.str, alias);
+ return 1;
+ }
+
+ pos--;
+
+ /* add to the appropriate clause mask */
+ if (hint->clause & INDEX_HINT_MASK_JOIN)
+ index_join[hint->type].set_bit (pos);
+ if (hint->clause & INDEX_HINT_MASK_ORDER)
+ index_order[hint->type].set_bit (pos);
+ if (hint->clause & INDEX_HINT_MASK_GROUP)
+ index_group[hint->type].set_bit (pos);
+ }
+
+ /* cannot mix USE INDEX and FORCE INDEX */
+ if ((!index_join[INDEX_HINT_FORCE].is_clear_all() ||
+ !index_order[INDEX_HINT_FORCE].is_clear_all() ||
+ !index_group[INDEX_HINT_FORCE].is_clear_all()) &&
+ (!index_join[INDEX_HINT_USE].is_clear_all() || have_empty_use_join ||
+ !index_order[INDEX_HINT_USE].is_clear_all() || have_empty_use_order ||
+ !index_group[INDEX_HINT_USE].is_clear_all() || have_empty_use_group))
+ {
+ my_error(ER_WRONG_USAGE, MYF(0), index_hint_type_name[INDEX_HINT_USE],
+ index_hint_type_name[INDEX_HINT_FORCE]);
+ return 1;
+ }
+
+ /* process FORCE INDEX as USE INDEX with a flag */
+ if (!index_join[INDEX_HINT_FORCE].is_clear_all() ||
+ !index_order[INDEX_HINT_FORCE].is_clear_all() ||
+ !index_group[INDEX_HINT_FORCE].is_clear_all())
+ {
+ tbl->force_index= TRUE;
+ index_join[INDEX_HINT_USE].merge(index_join[INDEX_HINT_FORCE]);
+ index_order[INDEX_HINT_USE].merge(index_order[INDEX_HINT_FORCE]);
+ index_group[INDEX_HINT_USE].merge(index_group[INDEX_HINT_FORCE]);
+ }
+
+ /* apply USE INDEX */
+ if (!index_join[INDEX_HINT_USE].is_clear_all() || have_empty_use_join)
+ tbl->keys_in_use_for_query.intersect(index_join[INDEX_HINT_USE]);
+ if (!index_order[INDEX_HINT_USE].is_clear_all() || have_empty_use_order)
+ tbl->keys_in_use_for_order_by.intersect (index_order[INDEX_HINT_USE]);
+ if (!index_group[INDEX_HINT_USE].is_clear_all() || have_empty_use_group)
+ tbl->keys_in_use_for_group_by.intersect (index_group[INDEX_HINT_USE]);
+
+ /* apply IGNORE INDEX */
+ tbl->keys_in_use_for_query.subtract (index_join[INDEX_HINT_IGNORE]);
+ tbl->keys_in_use_for_order_by.subtract (index_order[INDEX_HINT_IGNORE]);
+ tbl->keys_in_use_for_group_by.subtract (index_group[INDEX_HINT_IGNORE]);
+ }
+
+ /* make sure covering_keys don't include indexes disabled with a hint */
+ tbl->covering_keys.intersect(tbl->keys_in_use_for_query);
+ return 0;
+}
+
+
+size_t max_row_length(TABLE *table, const uchar *data)
+{
+ TABLE_SHARE *table_s= table->s;
+ size_t length= table_s->reclength + 2 * table_s->fields;
+ uint *const beg= table_s->blob_field;
+ uint *const end= beg + table_s->blob_fields;
+
+ for (uint *ptr= beg ; ptr != end ; ++ptr)
+ {
+ Field_blob* const blob= (Field_blob*) table->field[*ptr];
+ length+= blob->get_length((const uchar*)
+ (data + blob->offset(table->record[0]))) +
+ HA_KEY_BLOB_LENGTH;
+ }
+ return length;
+}
+
/*****************************************************************************
** Instansiate templates
*****************************************************************************/
diff --git a/sql/table.h b/sql/table.h
index 0884f5ba3d2..cb53013cd59 100644
--- a/sql/table.h
+++ b/sql/table.h
@@ -1,4 +1,4 @@
-/* Copyright (C) 2000-2006 MySQL AB
+/* Copyright 2000-2008 MySQL AB, 2008 Sun Microsystems, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -22,9 +22,33 @@ class Item_field;
class GRANT_TABLE;
class st_select_lex_unit;
class st_select_lex;
+class partition_info;
class COND_EQUAL;
class Security_context;
+/*************************************************************************/
+
+/**
+ View_creation_ctx -- creation context of view objects.
+*/
+
+class View_creation_ctx : public Default_object_creation_ctx,
+ public Sql_alloc
+{
+public:
+ static View_creation_ctx *create(THD *thd);
+
+ static View_creation_ctx *create(THD *thd,
+ TABLE_LIST *view);
+
+private:
+ View_creation_ctx(THD *thd)
+ : Default_object_creation_ctx(thd)
+ { }
+};
+
+/*************************************************************************/
+
/* Order clause list element */
typedef struct st_order {
@@ -43,23 +67,74 @@ typedef struct st_order {
table_map used, depend_map;
} ORDER;
+/**
+ @brief The current state of the privilege checking process for the current
+ user, SQL statement and SQL object.
+
+ @details The privilege checking process is divided into phases depending on
+ the level of the privilege to be checked and the type of object to be
+ accessed. Due to the mentioned scattering of privilege checking
+ functionality, it is necessary to keep track of the state of the
+ process. This information is stored in privilege, want_privilege, and
+ orig_want_privilege.
+
+ A GRANT_INFO also serves as a cache of the privilege hash tables. Relevant
+ members are grant_table and version.
+ */
typedef struct st_grant_info
{
+ /**
+ @brief A copy of the privilege information regarding the current host,
+ database, object and user.
+
+ @details The version of this copy is found in GRANT_INFO::version.
+ */
GRANT_TABLE *grant_table;
+ /**
+ @brief Used for cache invalidation when caching privilege information.
+
+ @details The privilege information is stored on disk, with dedicated
+ caches residing in memory: table-level and column-level privileges,
+ respectively, have their own dedicated caches.
+
+ The GRANT_INFO works as a level 1 cache with this member updated to the
+ current value of the global variable @c grant_version (@c static variable
+ in sql_acl.cc). It is updated Whenever the GRANT_INFO is refreshed from
+ the level 2 cache. The level 2 cache is the @c column_priv_hash structure
+ (@c static variable in sql_acl.cc)
+
+ @see grant_version
+ */
uint version;
+ /**
+ @brief The set of privileges that the current user has fulfilled for a
+ certain host, database, and object.
+
+ @details This field is continually updated throughout the access checking
+ process. In each step the "wanted privilege" is checked against the
+ fulfilled privileges. When/if the intersection of these sets is empty,
+ access is granted.
+
+ The set is implemented as a bitmap, with the bits defined in sql_acl.h.
+ */
ulong privilege;
+ /**
+ @brief the set of privileges that the current user needs to fulfil in
+ order to carry out the requested operation.
+ */
ulong want_privilege;
- /*
+ /**
Stores the requested access acl of top level tables list. Is used to
check access rights to the underlying tables of a view.
*/
ulong orig_want_privilege;
} GRANT_INFO;
-enum tmp_table_type {NO_TMP_TABLE=0,
- NON_TRANSACTIONAL_TMP_TABLE=1, TRANSACTIONAL_TMP_TABLE=2,
- SYSTEM_TMP_TABLE=3};
-
+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
@@ -70,7 +145,6 @@ enum trg_event_type
TRG_EVENT_MAX
};
-
enum frm_type_enum
{
FRMTYPE_ERROR= 0,
@@ -78,18 +152,20 @@ enum frm_type_enum
FRMTYPE_VIEW
};
+enum release_type { RELEASE_NORMAL, RELEASE_WAIT_FOR_DROP };
+
typedef struct st_filesort_info
{
- IO_CACHE *io_cache; /* If sorted through filebyte */
- uchar **sort_keys; /* Buffer for sorting keys */
- byte *buffpek; /* Buffer for buffpek structures */
- uint buffpek_len; /* Max number of buffpeks in the buffer */
- byte *addon_buf; /* Pointer to a buffer if sorted with fields */
- uint addon_length; /* Length of the buffer */
+ IO_CACHE *io_cache; /* If sorted through filesort */
+ uchar **sort_keys; /* Buffer for sorting keys */
+ uchar *buffpek; /* Buffer for buffpek structures */
+ uint buffpek_len; /* Max number of buffpeks in the buffer */
+ uchar *addon_buf; /* Pointer to a buffer if sorted with fields */
+ size_t addon_length; /* Length of the buffer */
struct st_sort_addon_field *addon_field; /* Pointer to the fields info */
- void (*unpack)(struct st_sort_addon_field *, byte *); /* To unpack back */
- byte *record_pointers; /* If sorted in memory */
- ha_rows found_records; /* How many records in sort */
+ void (*unpack)(struct st_sort_addon_field *, uchar *); /* To unpack back */
+ uchar *record_pointers; /* If sorted in memory */
+ ha_rows found_records; /* How many records in sort */
} FILESORT_INFO;
@@ -115,6 +191,100 @@ class Field_timestamp;
class Field_blob;
class Table_triggers_list;
+/**
+ Category of table found in the table share.
+*/
+enum enum_table_category
+{
+ /**
+ Unknown value.
+ */
+ TABLE_UNKNOWN_CATEGORY=0,
+
+ /**
+ Temporary table.
+ The table is visible only in the session.
+ Therefore,
+ - FLUSH TABLES WITH READ LOCK
+ - SET GLOBAL READ_ONLY = ON
+ do not apply to this table.
+ Note that LOCK TABLE t FOR READ/WRITE
+ can be used on temporary tables.
+ Temporary tables are not part of the table cache.
+ */
+ TABLE_CATEGORY_TEMPORARY=1,
+
+ /**
+ User table.
+ These tables do honor:
+ - LOCK TABLE t FOR READ/WRITE
+ - FLUSH TABLES WITH READ LOCK
+ - SET GLOBAL READ_ONLY = ON
+ User tables are cached in the table cache.
+ */
+ TABLE_CATEGORY_USER=2,
+
+ /**
+ System table, maintained by the server.
+ These tables do honor:
+ - LOCK TABLE t FOR READ/WRITE
+ - FLUSH TABLES WITH READ LOCK
+ - SET GLOBAL READ_ONLY = ON
+ Typically, writes to system tables are performed by
+ the server implementation, not explicitly be a user.
+ System tables are cached in the table cache.
+ */
+ TABLE_CATEGORY_SYSTEM=3,
+
+ /**
+ Information schema tables.
+ These tables are an interface provided by the system
+ to inspect the system metadata.
+ 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
+ an INFORMATION_SCHEMA table.
+ Nothing is directly written to information schema tables.
+ Note that this value is not used currently,
+ since information schema tables are not shared,
+ but implemented as session specific temporary tables.
+ */
+ /*
+ TODO: Fixing the performance issues of I_S will lead
+ to I_S tables in the table cache, which should use
+ this table type.
+ */
+ TABLE_CATEGORY_INFORMATION=4,
+
+ /**
+ 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 explicitely
+ a PERFORMANCE_SCHEMA table.
+ An example of PERFORMANCE_SCHEMA tables are:
+ - mysql.slow_log
+ - mysql.general_log,
+ which *are* updated 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 log tables).
+ The server implementation perform writes.
+ Performance tables are cached in the table cache.
+ */
+ TABLE_CATEGORY_PERFORMANCE=5
+};
+typedef enum enum_table_category TABLE_CATEGORY;
+
+TABLE_CATEGORY get_table_category(const LEX_STRING *db,
+ const LEX_STRING *name);
+
/*
This structure is shared between different table objects. There is one
instance of table share per one table in the database.
@@ -122,32 +292,52 @@ class Table_triggers_list;
typedef struct st_table_share
{
+ st_table_share() {} /* Remove gcc warning */
+
+ /** Category of this table. */
+ TABLE_CATEGORY table_category;
+
/* hash of field names (contains pointers to elements of field array) */
HASH name_hash; /* hash of field names */
MEM_ROOT mem_root;
TYPELIB keynames; /* Pointers to keynames */
TYPELIB fieldnames; /* Pointer to fieldnames */
TYPELIB *intervals; /* pointer to interval info */
-#ifdef NOT_YET
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 */
- struct st_table *used_next, /* Link to used tables */
- **used_prev;
+#endif
+
/* The following is copied to each TABLE on OPEN */
Field **field;
+ Field **found_next_number_field;
+ Field *timestamp_field; /* Used only during open */
KEY *key_info; /* data of keys in database */
-#endif
uint *blob_field; /* Index to blobs in Field arrray*/
- byte *default_values; /* row with default values */
+
+ uchar *default_values; /* row with default values */
LEX_STRING comment; /* Comment about table */
CHARSET_INFO *table_charset; /* Default charset of string fields */
- /* A pair "database_name\0table_name\0", widely used as simply a db name */
- char *table_cache_key;
- const char *db; /* Pointer to db */
- const char *table_name; /* Table name (for open) */
- const char *path; /* Path to .frm file (from datadir) */
+ MY_BITMAP all_set;
+ /*
+ Key which is used for looking-up table in table cache and in the list
+ of thread's temporary tables. Has the form of:
+ "database_name\0table_name\0" + optional part for temporary tables.
+
+ Note that all three 'table_cache_key', 'db' and 'table_name' members
+ must be set (and be non-zero) for tables in table cache. They also
+ should correspond to each other.
+ To ensure this one can use set_table_cache() methods.
+ */
+ LEX_STRING table_cache_key;
+ LEX_STRING db; /* Pointer to db */
+ LEX_STRING table_name; /* Table name (for open) */
+ LEX_STRING path; /* Path to .frm file (from datadir) */
+ LEX_STRING normalized_path; /* unpack_filename(path) */
LEX_STRING connect_string;
/*
@@ -156,20 +346,29 @@ typedef struct st_table_share
*/
key_map keys_in_use;
key_map keys_for_keyread;
+ ha_rows min_rows, max_rows; /* create information */
ulong avg_row_length; /* create information */
ulong raid_chunksize;
- ulong version, flush_version, mysql_version;
+ ulong version, mysql_version;
ulong timestamp_offset; /* Set to offset+1 of record */
ulong reclength; /* Recordlength */
- ha_rows min_rows, max_rows; /* create information */
- enum db_type db_type; /* table_type for handler */
+ plugin_ref db_plugin; /* storage engine plugin */
+ inline handlerton *db_type() const /* table_type for handler */
+ {
+ // DBUG_ASSERT(db_plugin);
+ return db_plugin ? plugin_data(db_plugin, handlerton*) : NULL;
+ }
enum row_type row_type; /* How rows are stored */
enum tmp_table_type tmp_table;
+ enum ha_choice transactional;
+ 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;
- uint key_length; /* Length of table_cache_key */
uint fields; /* Number of fields */
uint rec_buff_length; /* Size of table->record[] buffer */
uint keys, key_parts;
@@ -177,37 +376,234 @@ typedef struct st_table_share
uint uniques; /* Number of UNIQUE index */
uint null_fields; /* number of null fields */
uint blob_fields; /* number of blob fields */
+ uint timestamp_field_offset; /* Field number for timestamp field */
uint varchar_fields; /* number of varchar fields */
uint db_create_options; /* Create options from database */
uint db_options_in_use; /* Options in use */
uint db_record_offset; /* if HA_REC_IN_SEQ */
uint raid_type, raid_chunks;
- uint open_count; /* Number of tables in open list */
+ uint rowid_field_offset; /* Field_nr +1 to rowid field */
/* Index of auto-updated TIMESTAMP field in field array */
uint primary_key;
- uint timestamp_field_offset;
- uint next_number_index;
- uint next_number_key_offset;
- uchar frm_version;
- my_bool system; /* Set if system record */
- my_bool crypted; /* If .frm file is crypted */
- my_bool db_low_byte_first; /* Portable row format */
- my_bool crashed;
- my_bool is_view;
- my_bool name_lock, replace_with_name_lock;
- /*
- TRUE if this is a system table like 'mysql.proc', which we want to be
- able to open and lock even when we already have some tables open and
- locked. To avoid deadlocks we have to put certain restrictions on
- locking of this table for writing. FALSE - otherwise.
- */
- my_bool system_table;
+ uint next_number_index; /* autoincrement key number */
+ uint next_number_key_offset; /* autoinc keypart offset in a key */
+ uint next_number_keypart; /* autoinc keypart number in a key */
+ uint error, open_errno, errarg; /* error from open_table_def() */
+ uint column_bitmap_size;
+ uchar frm_version;
+ bool null_field_first;
+ bool system; /* Set if system table (one record) */
+ bool crypted; /* If .frm file is crypted */
+ bool db_low_byte_first; /* Portable row format */
+ bool crashed;
+ bool is_view;
+ bool name_lock, replace_with_name_lock;
+ bool waiting_on_cond; /* Protection against free */
+ ulong table_map_id; /* for row-based replication */
+ ulonglong table_map_version;
+
+ /*
+ Cache for row-based replication table share checks that does not
+ need to be repeated. Possible values are: -1 when cache value is
+ not calculated yet, 0 when table *shall not* be replicated, 1 when
+ table *may* be replicated.
+ */
+ int cached_row_logging_check;
+
+#ifdef WITH_PARTITION_STORAGE_ENGINE
+ /** @todo: Move into *ha_data for partitioning */
+ bool auto_partitioned;
+ const char *partition_info;
+ uint partition_info_len;
+ uint partition_info_buffer_size;
+ const char *part_state;
+ uint part_state_len;
+ handlerton *default_part_db_type;
+#endif
+
+ /** place to store storage engine specific data */
+ void *ha_data;
+
+
+ /*
+ Set share's table cache key and update its db and table name appropriately.
+
+ SYNOPSIS
+ set_table_cache_key()
+ key_buff Buffer with already built table cache key to be
+ referenced from share.
+ key_length Key length.
+
+ NOTES
+ Since 'key_buff' buffer will be referenced from share it should has same
+ life-time as share itself.
+ This method automatically ensures that TABLE_SHARE::table_name/db have
+ appropriate values by using table cache key as their source.
+ */
+
+ void set_table_cache_key(char *key_buff, uint key_length)
+ {
+ table_cache_key.str= key_buff;
+ table_cache_key.length= key_length;
+ /*
+ Let us use the fact that the key is "db/0/table_name/0" + optional
+ part for temporary tables.
+ */
+ db.str= table_cache_key.str;
+ db.length= strlen(db.str);
+ table_name.str= db.str + db.length + 1;
+ table_name.length= strlen(table_name.str);
+ }
+
+
+ /*
+ Set share's table cache key and update its db and table name appropriately.
+
+ SYNOPSIS
+ set_table_cache_key()
+ key_buff Buffer to be used as storage for table cache key
+ (should be at least key_length bytes).
+ key Value for table cache key.
+ key_length Key length.
+
+ NOTE
+ Since 'key_buff' buffer will be used as storage for table cache key
+ it should has same life-time as share itself.
+ */
+
+ void set_table_cache_key(char *key_buff, const char *key, uint key_length)
+ {
+ memcpy(key_buff, key, key_length);
+ set_table_cache_key(key_buff, key_length);
+ }
+
+ inline bool honor_global_locks()
+ {
+ return ((table_category == TABLE_CATEGORY_USER)
+ || (table_category == TABLE_CATEGORY_SYSTEM));
+ }
+
+ inline bool require_write_privileges()
+ {
+ return (table_category == TABLE_CATEGORY_PERFORMANCE);
+ }
+
+ inline ulong get_table_def_version()
+ {
+ return table_map_id;
+ }
+
+ /**
+ Convert unrelated members of TABLE_SHARE to one enum
+ representing its type.
+
+ @todo perhaps we need to have a member instead of a function.
+ */
+ enum enum_table_ref_type get_table_ref_type() const
+ {
+ if (is_view)
+ return TABLE_REF_VIEW;
+ switch (tmp_table) {
+ case NO_TMP_TABLE:
+ return TABLE_REF_BASE_TABLE;
+ case SYSTEM_TMP_TABLE:
+ return TABLE_REF_I_S_TABLE;
+ default:
+ return TABLE_REF_TMP_TABLE;
+ }
+ }
+ /**
+ Return a table metadata version.
+ * for base tables, we return table_map_id.
+ It is assigned from a global counter incremented for each
+ new table loaded into the table definition cache (TDC).
+ * for temporary tables it's table_map_id again. But for
+ temporary tables table_map_id is assigned from
+ thd->query_id. The latter is assigned from a thread local
+ counter incremented for every new SQL statement. Since
+ temporary tables are thread-local, each temporary table
+ gets a unique id.
+ * for everything else (views, information schema tables),
+ the version id is zero.
+
+ This choice of version id is a large compromise
+ to have a working prepared statement validation in 5.1. In
+ future version ids will be persistent, as described in WL#4180.
+
+ Let's try to explain why and how this limited solution allows
+ to validate prepared statements.
+
+ Firstly, sets (in mathematical sense) of version numbers
+ never intersect for different table types. Therefore,
+ version id of a temporary table is never compared with
+ a version id of a view, and vice versa.
+
+ Secondly, for base tables, we know that each DDL flushes the
+ respective share from the TDC. This ensures that whenever
+ a table is altered or dropped and recreated, it gets a new
+ version id.
+ Unfortunately, since elements of the TDC are also flushed on
+ LRU basis, this choice of version ids leads to false positives.
+ E.g. when the TDC size is too small, we may have a SELECT
+ * FROM INFORMATION_SCHEMA.TABLES flush all its elements, which
+ in turn will lead to a validation error and a subsequent
+ reprepare of all prepared statements. This is
+ considered acceptable, since as long as prepared statements are
+ automatically reprepared, spurious invalidation is only
+ a performance hit. Besides, no better simple solution exists.
+
+ For temporary tables, using thd->query_id ensures that if
+ a temporary table was altered or recreated, a new version id is
+ assigned. This suits validation needs very well and will perhaps
+ never change.
+
+ Metadata of information schema tables never changes.
+ Thus we can safely assume 0 for a good enough version id.
+
+ Views are a special and tricky case. A view is always inlined
+ into the parse tree of a prepared statement at prepare.
+ Thus, when we execute a prepared statement, the parse tree
+ will not get modified even if the view is replaced with another
+ view. Therefore, we can safely choose 0 for version id of
+ views and effectively never invalidate a prepared statement
+ when a view definition is altered. Note, that this leads to
+ wrong binary log in statement-based replication, since we log
+ prepared statement execution in form Query_log_events
+ containing conventional statements. But since there is no
+ metadata locking for views, the very same problem exists for
+ conventional statements alone, as reported in Bug#25144. The only
+ difference between prepared and conventional execution is,
+ effectively, that for prepared statements the race condition
+ window is much wider.
+ In 6.0 we plan to support view metadata locking (WL#3726) and
+ extend table definition cache to cache views (WL#4298).
+ When this is done, views will be handled in the same fashion
+ as the base tables.
+
+ Finally, by taking into account table type, we always
+ track that a change has taken place when a view is replaced
+ with a base table, a base table is replaced with a temporary
+ table and so on.
+
+ @sa TABLE_LIST::is_table_ref_id_equal()
+ */
+ ulong get_table_ref_version() const
+ {
+ return (tmp_table == SYSTEM_TMP_TABLE || is_view) ? 0 : table_map_id;
+ }
+
} TABLE_SHARE;
extern ulong refresh_version;
/* Information for one open table */
+enum index_hint_type
+{
+ INDEX_HINT_IGNORE,
+ INDEX_HINT_USE,
+ INDEX_HINT_FORCE
+};
struct st_table {
st_table() {} /* Remove gcc warning */
@@ -220,16 +616,27 @@ struct st_table {
#endif
struct st_table *next, *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 */
+
THD *in_use; /* Which thread uses this */
Field **field; /* Pointer to fields */
- byte *record[2]; /* Pointer to records */
- byte *insert_values; /* used by INSERT ... UPDATE */
- key_map quick_keys, used_keys;
-
+ uchar *record[2]; /* Pointer to records */
+ uchar *write_row_record; /* Used as optimisation in
+ THD::write_row */
+ uchar *insert_values; /* used by INSERT ... UPDATE */
+ /*
+ Map of keys that can be used to retrieve all data from this table
+ needed by the query without reading the row.
+ */
+ key_map covering_keys;
+ key_map quick_keys, merge_keys;
/*
A set of keys that can be used in the query that references this
- table
+ table.
All indexes disabled on the table's TABLE_SHARE (see TABLE::s) will be
subtracted from this set upon instantiation. Thus for any TABLE t it holds
@@ -239,12 +646,14 @@ struct st_table {
The set is implemented as a bitmap.
*/
key_map keys_in_use_for_query;
- key_map merge_keys;
+ /* Map of keys that can be used to calculate GROUP BY without sorting */
+ key_map keys_in_use_for_group_by;
+ /* Map of keys that can be used to calculate ORDER BY without sorting */
+ key_map keys_in_use_for_order_by;
KEY *key_info; /* data of keys in database */
- Field *next_number_field, /* Set if next_number is activated */
- *found_next_number_field, /* Set on open */
- *rowid_field;
+ Field *next_number_field; /* Set if next_number is activated */
+ Field *found_next_number_field; /* Set on open */
Field_timestamp *timestamp_field;
/* Table's triggers, 0 if there are no of them */
@@ -253,13 +662,51 @@ struct st_table {
ORDER *group;
const char *alias; /* alias or table name */
uchar *null_flags;
+ my_bitmap_map *bitmap_init_value;
+ MY_BITMAP def_read_set, def_write_set, tmp_set; /* containers */
+ MY_BITMAP *read_set, *write_set; /* Active column sets */
+ /*
+ The ID of the query that opened and is using this table. Has different
+ meanings depending on the table type.
+
+ Temporary tables:
+
+ table->query_id is set to thd->query_id for the duration of a statement
+ and is reset to 0 once it is closed by the same statement. A non-zero
+ table->query_id means that a statement is using the table even if it's
+ not the current statement (table is in use by some outer statement).
+
+ Non-temporary tables:
+
+ Under pre-locked or LOCK TABLES mode: query_id is set to thd->query_id
+ for the duration of a statement and is reset to 0 once it is closed by
+ the same statement. A non-zero query_id is used to control which tables
+ in the list of pre-opened and locked tables are actually being used.
+ */
query_id_t query_id;
+ /*
+ For each key that has quick_keys.is_set(key) == TRUE: estimate of #records
+ and max #key parts that range access would use.
+ */
ha_rows quick_rows[MAX_KEY];
+
+ /* Bitmaps of key parts that =const for the entire join. */
key_part_map const_key_parts[MAX_KEY];
+
uint quick_key_parts[MAX_KEY];
uint quick_n_ranges[MAX_KEY];
+ /*
+ Estimate of number of records that satisfy SARGable part of the table
+ condition, or table->file->records if no SARGable condition could be
+ constructed.
+ This value is used by join optimizer as an estimate of number of records
+ that will pass the table condition (condition that depends on fields of
+ this table and constants)
+ */
+ ha_rows quick_condition_rows;
+
/*
If this table has TIMESTAMP field with auto-set property (pointed by
timestamp_field member) then this variable indicates during which
@@ -298,6 +745,12 @@ struct st_table {
NULL, including columns declared as "not null" (see maybe_null).
*/
my_bool null_row;
+
+ /*
+ TODO: Each of the following flags take up 8 bits. They can just as easily
+ be put into one single unsigned long and instead of taking up 18
+ bytes, it would take up 4.
+ */
my_bool force_index;
my_bool distinct,const_table,no_rows;
my_bool key_read, no_keyread;
@@ -319,11 +772,13 @@ struct st_table {
not rely on that.
*/
my_bool open_placeholder;
+ my_bool locked_by_logger;
+ my_bool no_replicate;
my_bool locked_by_name;
my_bool fulltext_searched;
my_bool no_cache;
- /* To signal that we should reset query_id for tables and cols */
- my_bool clear_query_id;
+ /* To signal that the table is associated with a HANDLER statement */
+ my_bool open_by_handler;
/*
To indicate that a non-null value of the auto_increment field
was provided by the user or retrieved from the current record.
@@ -332,15 +787,53 @@ struct st_table {
my_bool auto_increment_field_not_null;
my_bool insert_or_update; /* Can be used by the handler */
my_bool alias_name_used; /* true if table_name is alias */
+ my_bool get_fields_in_item_tree; /* Signal to fix_field */
+ /* If MERGE children attached to parent. See top comment in ha_myisammrg.cc */
+ my_bool children_attached;
REGINFO reginfo; /* field connections */
MEM_ROOT mem_root;
GRANT_INFO grant;
FILESORT_INFO sort;
- TABLE_SHARE share_not_to_be_used; /* To be deleted when true shares */
+#ifdef WITH_PARTITION_STORAGE_ENGINE
+ partition_info *part_info; /* Partition related information */
+ bool no_partitions_used; /* If true, all partitions have been pruned away */
+#endif
bool fill_item_list(List<Item> *item_list) const;
void reset_item_list(List<Item> *item_list) const;
+ void clear_column_bitmaps(void);
+ void prepare_for_position(void);
+ void mark_columns_used_by_index_no_reset(uint index, MY_BITMAP *map);
+ void mark_columns_used_by_index(uint index);
+ void restore_column_maps_after_mark_index();
+ void mark_auto_increment_column(void);
+ void mark_columns_needed_for_update(void);
+ void mark_columns_needed_for_delete(void);
+ void mark_columns_needed_for_insert(void);
+ inline void column_bitmaps_set(MY_BITMAP *read_set_arg,
+ MY_BITMAP *write_set_arg)
+ {
+ read_set= read_set_arg;
+ write_set= write_set_arg;
+ if (file)
+ file->column_bitmaps_signal();
+ }
+ inline void column_bitmaps_set_no_signal(MY_BITMAP *read_set_arg,
+ MY_BITMAP *write_set_arg)
+ {
+ read_set= read_set_arg;
+ write_set= write_set_arg;
+ }
+ inline void use_all_columns()
+ {
+ column_bitmaps_set(&s->all_set, &s->all_set);
+ }
+ inline void default_column_bitmaps()
+ {
+ read_set= &def_read_set;
+ write_set= &def_write_set;
+ }
/* Is table open or should be treated as such by name-locking? */
inline bool is_name_opened() { return db_stat || open_placeholder; }
/*
@@ -348,6 +841,7 @@ struct st_table {
*/
inline bool needs_reopen_or_name_lock()
{ return s->version != refresh_version; }
+ bool is_children_attached(void);
};
enum enum_schema_table_state
@@ -362,11 +856,16 @@ typedef struct st_foreign_key_info
LEX_STRING *forein_id;
LEX_STRING *referenced_db;
LEX_STRING *referenced_table;
- LEX_STRING *constraint_method;
+ LEX_STRING *update_method;
+ LEX_STRING *delete_method;
+ LEX_STRING *referenced_key_name;
List<LEX_STRING> foreign_fields;
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
{
@@ -375,11 +874,23 @@ enum enum_schema_tables
SCH_COLLATION_CHARACTER_SET_APPLICABILITY,
SCH_COLUMNS,
SCH_COLUMN_PRIVILEGES,
+ SCH_ENGINES,
+ SCH_EVENTS,
+ SCH_FILES,
+ SCH_GLOBAL_STATUS,
+ SCH_GLOBAL_VARIABLES,
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,
@@ -393,14 +904,47 @@ enum enum_schema_tables
};
+#define MY_I_S_MAYBE_NULL 1
+#define MY_I_S_UNSIGNED 2
+
+
+#define SKIP_OPEN_TABLE 0 // do not open table
+#define OPEN_FRM_ONLY 1 // open FRM file only
+#define OPEN_FULL_TABLE 2 // open FRM,MYD, MYI files
+
typedef struct st_field_info
{
+ /**
+ This is used as column name.
+ */
const char* field_name;
+ /**
+ For string-type columns, this is the maximum number of
+ characters. Otherwise, it is the 'display-length' for the column.
+ */
uint field_length;
+ /**
+ This denotes data type for the column. For the most part, there seems to
+ be one entry in the enum for each SQL data type, although there seem to
+ be a number of additional entries in the enum.
+ */
enum enum_field_types field_type;
int value;
- bool maybe_null;
+ /**
+ This is used to set column attributes. By default, columns are @c NOT
+ @c NULL and @c SIGNED, and you can deviate from the default
+ by setting the appopriate flags. You can use either one of the flags
+ @c MY_I_S_MAYBE_NULL and @cMY_I_S_UNSIGNED or
+ combine them using the bitwise or operator @c |. Both flags are
+ defined in table.h.
+ */
+ uint field_flags; // Field atributes(maybe_null, signed, unsigned etc.)
const char* old_name;
+ /**
+ This should be one of @c SKIP_OPEN_TABLE,
+ @c OPEN_FRM_ONLY or @c OPEN_FULL_TABLE.
+ */
+ uint open_method;
} ST_FIELD_INFO;
@@ -417,11 +961,11 @@ typedef struct st_schema_table
int (*fill_table) (THD *thd, TABLE_LIST *tables, COND *cond);
/* Handle fileds for old SHOW */
int (*old_format) (THD *thd, struct st_schema_table *schema_table);
- int (*process_table) (THD *thd, TABLE_LIST *tables,
- TABLE *table, bool res, const char *base_name,
- const char *file_name);
+ int (*process_table) (THD *thd, TABLE_LIST *tables, TABLE *table,
+ bool res, LEX_STRING *db_name, LEX_STRING *table_name);
int idx_field1, idx_field2;
bool hidden;
+ uint i_s_requested_object; /* the object we need to open(TABLE | VIEW) */
} ST_SCHEMA_TABLE;
@@ -446,6 +990,9 @@ typedef struct st_schema_table
#define VIEW_CHECK_ERROR 1
#define VIEW_CHECK_SKIP 2
+/** 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;
@@ -524,9 +1071,25 @@ public:
(TABLE_LIST::join_using_fields != NULL)
*/
+class Index_hint;
struct TABLE_LIST
{
TABLE_LIST() {} /* Remove gcc warning */
+
+ /**
+ Prepare TABLE_LIST that consists of one table instance to use in
+ simple_open_and_lock_tables
+ */
+ inline void init_one_table(const char *db_name_arg,
+ const char *table_name_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;
+ lock_type= lock_type_arg;
+ }
+
/*
List of tables local to a subquery (used by SQL_LIST). Considers
views as leaves (unlike 'next_leaf' below). Created at parse time
@@ -580,8 +1143,9 @@ struct TABLE_LIST
*/
TABLE_LIST *next_name_resolution_table;
/* Index names in a "... JOIN ... USE/IGNORE INDEX ..." clause. */
- List<String> *use_index, *ignore_index;
- TABLE *table; /* opened table */
+ List<Index_hint> *index_hints;
+ TABLE *table; /* opened table */
+ uint table_id; /* table id (from binlog) for opened table */
/*
select_result for derived table to pass it from table creation to table
filling procedure
@@ -595,6 +1159,27 @@ struct TABLE_LIST
can see this lists can't be merged)
*/
TABLE_LIST *correspondent_table;
+ /**
+ @brief Normally, this field is non-null for anonymous derived tables only.
+
+ @details This field is set to non-null for
+
+ - Anonymous derived tables, In this case it points to the SELECT_LEX_UNIT
+ representing the derived table. E.g. for a query
+
+ @verbatim SELECT * FROM (SELECT a FROM t1) b @endverbatim
+
+ For the @c TABLE_LIST representing the derived table @c b, @c derived
+ points to the SELECT_LEX_UNIT representing the result of the query within
+ parenteses.
+
+ - Views. This is set for views with @verbatim ALGORITHM = TEMPTABLE
+ @endverbatim by mysql_make_view().
+
+ @note Inside views, a subquery in the @c FROM clause is not allowed.
+ @note Do not use this field to separate views/base tables/anonymous
+ derived tables. Use TABLE_LIST::is_anonymous_derived_table().
+ */
st_select_lex_unit *derived; /* SELECT_LEX_UNIT of derived table */
ST_SCHEMA_TABLE *schema_table; /* Information_schema table */
st_select_lex *schema_select_lex;
@@ -629,6 +1214,8 @@ struct TABLE_LIST
(non-zero only for merged underlying tables of a view).
*/
TABLE_LIST *referencing_view;
+ /* Ptr to parent MERGE table list item. See top comment in ha_myisammrg.cc */
+ TABLE_LIST *parent_l;
/*
Security context (non-zero only for tables which belong
to view with SQL SECURITY DEFINER)
@@ -648,7 +1235,7 @@ struct TABLE_LIST
TABLE_LIST *next_leaf;
Item *where; /* VIEW WHERE clause condition */
Item *check_option; /* WITH CHECK OPTION condition */
- LEX_STRING query; /* text of (CRETE/SELECT) statement */
+ LEX_STRING select_stmt; /* text of (CREATE/SELECT) statement */
LEX_STRING md5; /* md5 of query text */
LEX_STRING source; /* source of CREATE VIEW */
LEX_STRING view_db; /* saved view database */
@@ -657,7 +1244,15 @@ struct TABLE_LIST
st_lex_user definer; /* definer of view */
ulonglong file_version; /* version of file's field set */
ulonglong updatable_view; /* VIEW can be updated */
- ulonglong algorithm; /* 0 any, 1 tmp tables , 2 merging */
+ /**
+ @brief The declared algorithm, if this is a view.
+ @details One of
+ - VIEW_ALGORITHM_UNDEFINED
+ - VIEW_ALGORITHM_TMPTABLE
+ - VIEW_ALGORITHM_MERGE
+ @to do Replace with an enum
+ */
+ ulonglong algorithm;
ulonglong view_suid; /* view is suid (TRUE dy default) */
ulonglong with_check; /* WITH CHECK OPTION */
/*
@@ -665,7 +1260,15 @@ struct TABLE_LIST
algorithm)
*/
uint8 effective_with_check;
- uint8 effective_algorithm; /* which algorithm was really used */
+ /**
+ @brief The view algorithm that is actually used, if this is a view.
+ @details One of
+ - VIEW_ALGORITHM_UNDEFINED
+ - VIEW_ALGORITHM_TMPTABLE
+ - VIEW_ALGORITHM_MERGE
+ @to do Replace with an enum
+ */
+ uint8 effective_algorithm;
GRANT_INFO grant;
/* data need by some engines in query cache*/
ulonglong engine_data;
@@ -674,8 +1277,8 @@ struct TABLE_LIST
thr_lock_type lock_type;
uint outer_join; /* Which join type */
uint shared; /* Used in multi-upd */
- uint db_length;
- uint32 table_name_length;
+ size_t db_length;
+ size_t table_name_length;
bool updatable; /* VIEW/TABLE can be updated now */
bool straight; /* optimize with prev table */
bool updating; /* for replicate-do/ignore table */
@@ -700,6 +1303,7 @@ struct TABLE_LIST
bool check_option_processed;
/* FRMTYPE_ERROR if any type is acceptable */
enum frm_type_enum required_type;
+ handlerton *db_type; /* table_type for handler */
char timestamp_buffer[20]; /* buffer for timestamp (19+1) */
/*
This TABLE_LIST object is just placeholder for prelocking, it will be
@@ -712,6 +1316,33 @@ struct TABLE_LIST
... SELECT implementation).
*/
bool create;
+ bool internal_tmp_table;
+
+
+ /* View creation context. */
+
+ View_creation_ctx *view_creation_ctx;
+
+ /*
+ Attributes to save/load view creation context in/from frm-file.
+
+ Ther are required only to be able to use existing parser to load
+ view-definition file. As soon as the parser parsed the file, view
+ creation context is initialized and the attributes become redundant.
+
+ These attributes MUST NOT be used for any purposes but the parsing.
+ */
+
+ LEX_STRING view_client_cs_name;
+ LEX_STRING view_connection_cl_name;
+
+ /*
+ View definition (SELECT-statement) in the UTF-form.
+ */
+
+ LEX_STRING view_body_utf8;
+
+ /* End of view definition context. */
/**
Indicates what triggers we need to pre-load for this TABLE_LIST
@@ -720,6 +1351,10 @@ struct TABLE_LIST
*/
uint8 trg_event_map;
+ uint i_s_requested_object;
+ bool has_db_lookup_value;
+ bool has_table_lookup_value;
+ uint table_open_method;
enum enum_schema_table_state schema_table_state;
void calc_md5(char *buffer);
void set_underlying_merge();
@@ -728,10 +1363,10 @@ struct TABLE_LIST
void cleanup_items();
bool placeholder()
{
- return derived || view || schema_table || create && !table->db_stat ||
+ return derived || view || schema_table || (create && !table->db_stat) ||
!table;
}
- void print(THD *thd, String *str);
+ void print(THD *thd, String *str, enum_query_type query_type);
bool check_single_table(TABLE_LIST **table, table_map map,
TABLE_LIST *view);
bool set_insert_values(MEM_ROOT *mem_root);
@@ -770,15 +1405,88 @@ struct TABLE_LIST
void reinit_before_use(THD *thd);
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.
+ */
+ 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
+ definition cache element.
+
+ @sa check_and_update_table_version()
+ */
+ inline
+ bool is_table_ref_id_equal(TABLE_SHARE *s) const
+ {
+ return (m_table_ref_type == s->get_table_ref_type() &&
+ m_table_ref_version == s->get_table_ref_version());
+ }
+
+ /**
+ Record the value of metadata version of the corresponding
+ table definition cache element in this parse tree node.
+
+ @sa check_and_update_table_version()
+ */
+ inline
+ void set_table_ref_id(TABLE_SHARE *s)
+ {
+ m_table_ref_type= s->get_table_ref_type();
+ m_table_ref_version= s->get_table_ref_version();
+ }
+
+ /**
+ @brief True if this TABLE_LIST represents an anonymous derived table,
+ i.e. the result of a subquery.
+ */
+ bool is_anonymous_derived_table() const { return derived && !view; }
+
+ /**
+ @brief Returns the name of the database that the referenced table belongs
+ to.
+ */
+ char *get_db_name() { return view != NULL ? view_db.str : db; }
+
+ /**
+ @brief Returns the name of the table that this TABLE_LIST represents.
+
+ @details The unqualified table name or view name for a table or view,
+ respectively.
+ */
+ char *get_table_name() { return view != NULL ? view_name.str : table_name; }
+
private:
bool prep_check_option(THD *thd, uint8 check_opt_type);
bool prep_where(THD *thd, Item **conds, bool no_where_clause);
- void print_index_hint(THD *thd, String *str, const char *hint,
- uint32 hint_length, List<String>);
/*
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() */
+ ulong m_table_ref_version;
};
class Item;
@@ -894,8 +1602,8 @@ public:
bool end_of_fields()
{ return (table_ref == last_leaf && field_it->end_of_fields()); }
const char *name() { return field_it->name(); }
- const char *table_name();
- const char *db_name();
+ const char *get_table_name();
+ const char *get_db_name();
GRANT_INFO *grant();
Item *create_item(THD *thd) { return field_it->create_item(thd); }
Field *field() { return field_it->field(); }
@@ -936,4 +1644,82 @@ typedef struct st_open_table_list{
uint32 in_use,locked;
} OPEN_TABLE_LIST;
+typedef struct st_table_field_w_type
+{
+ LEX_STRING name;
+ LEX_STRING type;
+ LEX_STRING cset;
+} TABLE_FIELD_W_TYPE;
+
+
+my_bool
+table_check_intact(TABLE *table, const uint table_f_count,
+ const TABLE_FIELD_W_TYPE *table_def);
+
+static inline my_bitmap_map *tmp_use_all_columns(TABLE *table,
+ MY_BITMAP *bitmap)
+{
+ my_bitmap_map *old= bitmap->bitmap;
+ bitmap->bitmap= table->s->all_set.bitmap;
+ return old;
+}
+
+
+static inline void tmp_restore_column_map(MY_BITMAP *bitmap,
+ my_bitmap_map *old)
+{
+ bitmap->bitmap= old;
+}
+
+/* The following is only needed for debugging */
+
+static inline my_bitmap_map *dbug_tmp_use_all_columns(TABLE *table,
+ MY_BITMAP *bitmap)
+{
+#ifndef DBUG_OFF
+ return tmp_use_all_columns(table, bitmap);
+#else
+ return 0;
+#endif
+}
+
+static inline void dbug_tmp_restore_column_map(MY_BITMAP *bitmap,
+ my_bitmap_map *old)
+{
+#ifndef DBUG_OFF
+ tmp_restore_column_map(bitmap, old);
+#endif
+}
+
+
+/*
+ Variant of the above : handle both read and write sets.
+ Provide for the possiblity of the read set being the same as the write set
+*/
+static inline void dbug_tmp_use_all_columns(TABLE *table,
+ my_bitmap_map **save,
+ MY_BITMAP *read_set,
+ MY_BITMAP *write_set)
+{
+#ifndef DBUG_OFF
+ save[0]= read_set->bitmap;
+ save[1]= write_set->bitmap;
+ (void) tmp_use_all_columns(table, read_set);
+ (void) tmp_use_all_columns(table, write_set);
+#endif
+}
+
+
+static inline void dbug_tmp_restore_column_maps(MY_BITMAP *read_set,
+ MY_BITMAP *write_set,
+ my_bitmap_map **old)
+{
+#ifndef DBUG_OFF
+ tmp_restore_column_map(read_set, old[0]);
+ tmp_restore_column_map(write_set, old[1]);
+#endif
+}
+
+
+size_t max_row_length(TABLE *table, const uchar *data);
diff --git a/sql/thr_malloc.cc b/sql/thr_malloc.cc
index 4ebcf1c50af..6bf43b51df0 100644
--- a/sql/thr_malloc.cc
+++ b/sql/thr_malloc.cc
@@ -23,31 +23,30 @@ extern "C" {
{
sql_print_error(ER(ER_OUT_OF_RESOURCES));
- THD *thd=current_thd;
+ THD *thd= current_thd;
if (thd)
{
- /*
- This thread is Out Of Memory.
- An OOM condition is a fatal error.
- It should not be caught by error handlers in stored procedures.
- Also, recording that SQL condition in the condition area could
- cause more memory allocations, which in turn could raise more
- OOM conditions, causing recursion in the error handling code itself.
- As a result, my_error() should not be invoked, and the
- thread diagnostics area is set to an error status directly.
- The visible result for a client application will be:
- - a query fails with an ER_OUT_OF_RESOURCES error,
- returned in the error packet.
- - SHOW ERROR/SHOW WARNINGS may be empty.
- */
-
- NET *net= &thd->net;
- thd->fatal_error();
- if (!net->last_error[0]) // Return only first message
+ if (! thd->is_error())
{
- strmake(net->last_error, ER(ER_OUT_OF_RESOURCES),
- sizeof(net->last_error)-1);
- net->last_errno= ER_OUT_OF_RESOURCES;
+ /*
+ This thread is Out Of Memory.
+ An OOM condition is a fatal error.
+ It should not be caught by error handlers in stored procedures.
+ Also, recording that SQL condition in the condition area could
+ cause more memory allocations, which in turn could raise more
+ OOM conditions, causing recursion in the error handling code itself.
+ As a result, my_error() should not be invoked, and the
+ thread diagnostics area is set to an error status directly.
+ Note that Diagnostics_area::set_error_status() is safe,
+ since it does not call any memory allocation routines.
+ The visible result for a client application will be:
+ - a query fails with an ER_OUT_OF_RESOURCES error,
+ 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));
}
}
}
@@ -60,26 +59,25 @@ void init_sql_alloc(MEM_ROOT *mem_root, uint block_size, uint pre_alloc)
}
-gptr sql_alloc(uint Size)
+void *sql_alloc(size_t Size)
{
MEM_ROOT *root= *my_pthread_getspecific_ptr(MEM_ROOT**,THR_MALLOC);
- char *ptr= (char*) alloc_root(root,Size);
- return ptr;
+ return alloc_root(root,Size);
}
-gptr sql_calloc(uint size)
+void *sql_calloc(size_t size)
{
- gptr ptr;
+ void *ptr;
if ((ptr=sql_alloc(size)))
- bzero((char*) ptr,size);
+ bzero(ptr,size);
return ptr;
}
char *sql_strdup(const char *str)
{
- uint len=(uint) strlen(str)+1;
+ size_t len= strlen(str)+1;
char *pos;
if ((pos= (char*) sql_alloc(len)))
memcpy(pos,str,len);
@@ -87,7 +85,7 @@ char *sql_strdup(const char *str)
}
-char *sql_strmake(const char *str,uint len)
+char *sql_strmake(const char *str, size_t len)
{
char *pos;
if ((pos= (char*) sql_alloc(len+1)))
@@ -99,10 +97,10 @@ char *sql_strmake(const char *str,uint len)
}
-gptr sql_memdup(const void *ptr,uint len)
+void* sql_memdup(const void *ptr, size_t len)
{
- char *pos;
- if ((pos= (char*) sql_alloc(len)))
+ void *pos;
+ if ((pos= sql_alloc(len)))
memcpy(pos,ptr,len);
return pos;
}
@@ -112,17 +110,17 @@ void sql_element_free(void *ptr __attribute__((unused)))
-char *sql_strmake_with_convert(const char *str, uint32 arg_length,
+char *sql_strmake_with_convert(const char *str, size_t arg_length,
CHARSET_INFO *from_cs,
- uint32 max_res_length,
- CHARSET_INFO *to_cs, uint32 *result_length)
+ size_t max_res_length,
+ CHARSET_INFO *to_cs, size_t *result_length)
{
char *pos;
- uint32 new_length= to_cs->mbmaxlen*arg_length;
+ size_t new_length= to_cs->mbmaxlen*arg_length;
max_res_length--; // Reserve place for end null
set_if_smaller(new_length, max_res_length);
- if (!(pos= sql_alloc(new_length+1)))
+ if (!(pos= (char*) sql_alloc(new_length+1)))
return pos; // Error
if ((from_cs == &my_charset_bin) || (to_cs == &my_charset_bin))
diff --git a/sql/time.cc b/sql/time.cc
index fb8a51fd0eb..a6619cf4cee 100644
--- a/sql/time.cc
+++ b/sql/time.cc
@@ -23,6 +23,41 @@
/* Some functions to calculate dates */
#ifndef TESTTIME
+
+/*
+ Name description of interval names used in statements.
+
+ 'interval_type_to_name' is ordered and sorted on interval size and
+ interval complexity.
+ Order of elements in 'interval_type_to_name' should correspond to
+ the order of elements in 'interval_type' enum
+
+ See also interval_type, interval_names
+*/
+
+LEX_STRING interval_type_to_name[INTERVAL_LAST] = {
+ { C_STRING_WITH_LEN("YEAR")},
+ { C_STRING_WITH_LEN("QUARTER")},
+ { C_STRING_WITH_LEN("MONTH")},
+ { C_STRING_WITH_LEN("WEEK")},
+ { C_STRING_WITH_LEN("DAY")},
+ { C_STRING_WITH_LEN("HOUR")},
+ { C_STRING_WITH_LEN("MINUTE")},
+ { C_STRING_WITH_LEN("SECOND")},
+ { C_STRING_WITH_LEN("MICROSECOND")},
+ { C_STRING_WITH_LEN("YEAR_MONTH")},
+ { C_STRING_WITH_LEN("DAY_HOUR")},
+ { C_STRING_WITH_LEN("DAY_MINUTE")},
+ { C_STRING_WITH_LEN("DAY_SECOND")},
+ { C_STRING_WITH_LEN("HOUR_MINUTE")},
+ { C_STRING_WITH_LEN("HOUR_SECOND")},
+ { C_STRING_WITH_LEN("MINUTE_SECOND")},
+ { C_STRING_WITH_LEN("DAY_MICROSECOND")},
+ { C_STRING_WITH_LEN("HOUR_MICROSECOND")},
+ { C_STRING_WITH_LEN("MINUTE_MICROSECOND")},
+ { C_STRING_WITH_LEN("SECOND_MICROSECOND")}
+};
+
/* Calc weekday from daynr */
/* Returns 0 for monday, 1 for tuesday .... */
@@ -279,6 +314,11 @@ void localtime_to_TIME(MYSQL_TIME *to, struct tm *from)
void calc_time_from_sec(MYSQL_TIME *to, long seconds, long microseconds)
{
long t_seconds;
+ // to->neg is not cleared, it may already be set to a useful value
+ to->time_type= MYSQL_TIMESTAMP_TIME;
+ to->year= 0;
+ to->month= 0;
+ to->day= 0;
to->hour= seconds/3600L;
t_seconds= seconds%3600L;
to->minute= t_seconds/60L;
@@ -690,6 +730,7 @@ void make_truncated_value_warning(THD *thd, MYSQL_ERROR::enum_warning_level leve
char buff[128];
String str(buff,(uint32) sizeof(buff), system_charset_info);
str.copy(str_val, str_length, system_charset_info);
+ str[str_length]= 0; // Ensure we have end 0 for snprintf
switch (time_type) {
case MYSQL_TIMESTAMP_DATE:
@@ -709,12 +750,237 @@ void make_truncated_value_warning(THD *thd, MYSQL_ERROR::enum_warning_level leve
type_str, str.c_ptr(), field_name,
(ulong) thd->row_count);
else
- cs->cset->snprintf(cs, warn_buff, sizeof(warn_buff),
- ER(ER_TRUNCATED_WRONG_VALUE),
- type_str, str.c_ptr());
+ {
+ 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());
+ else
+ cs->cset->snprintf(cs, warn_buff, sizeof(warn_buff),
+ ER(ER_WRONG_VALUE), type_str, str.c_ptr());
+ }
push_warning(thd, level,
ER_TRUNCATED_WRONG_VALUE, warn_buff);
}
+/* Daynumber from year 0 to 9999-12-31 */
+#define MAX_DAY_NUMBER 3652424L
+
+bool date_add_interval(MYSQL_TIME *ltime, interval_type int_type, INTERVAL interval)
+{
+ long period, sign;
+
+ ltime->neg= 0;
+
+ sign= (interval.neg ? -1 : 1);
+
+ switch (int_type) {
+ case INTERVAL_SECOND:
+ case INTERVAL_SECOND_MICROSECOND:
+ case INTERVAL_MICROSECOND:
+ case INTERVAL_MINUTE:
+ case INTERVAL_HOUR:
+ case INTERVAL_MINUTE_MICROSECOND:
+ case INTERVAL_MINUTE_SECOND:
+ case INTERVAL_HOUR_MICROSECOND:
+ case INTERVAL_HOUR_SECOND:
+ case INTERVAL_HOUR_MINUTE:
+ case INTERVAL_DAY_MICROSECOND:
+ case INTERVAL_DAY_SECOND:
+ case INTERVAL_DAY_MINUTE:
+ case INTERVAL_DAY_HOUR:
+ {
+ longlong sec, days, daynr, microseconds, extra_sec;
+ ltime->time_type= MYSQL_TIMESTAMP_DATETIME; // Return full date
+ microseconds= ltime->second_part + sign*interval.second_part;
+ extra_sec= microseconds/1000000L;
+ microseconds= microseconds%1000000L;
+
+ sec=((ltime->day-1)*3600*24L+ltime->hour*3600+ltime->minute*60+
+ ltime->second +
+ sign* (longlong) (interval.day*3600*24L +
+ interval.hour*LL(3600)+interval.minute*LL(60)+
+ interval.second))+ extra_sec;
+ if (microseconds < 0)
+ {
+ microseconds+= LL(1000000);
+ sec--;
+ }
+ days= sec/(3600*LL(24));
+ sec-= days*3600*LL(24);
+ if (sec < 0)
+ {
+ days--;
+ sec+= 3600*LL(24);
+ }
+ ltime->second_part= (uint) microseconds;
+ ltime->second= (uint) (sec % 60);
+ ltime->minute= (uint) (sec/60 % 60);
+ ltime->hour= (uint) (sec/3600);
+ daynr= calc_daynr(ltime->year,ltime->month,1) + days;
+ /* Day number from year 0 to 9999-12-31 */
+ if ((ulonglong) daynr > MAX_DAY_NUMBER)
+ goto invalid_date;
+ get_date_from_daynr((long) daynr, &ltime->year, &ltime->month,
+ &ltime->day);
+ break;
+ }
+ case INTERVAL_DAY:
+ case INTERVAL_WEEK:
+ period= (calc_daynr(ltime->year,ltime->month,ltime->day) +
+ sign * (long) interval.day);
+ /* Daynumber from year 0 to 9999-12-31 */
+ if ((ulong) period > MAX_DAY_NUMBER)
+ goto invalid_date;
+ get_date_from_daynr((long) period,&ltime->year,&ltime->month,&ltime->day);
+ break;
+ case INTERVAL_YEAR:
+ ltime->year+= sign * (long) interval.year;
+ if ((ulong) ltime->year >= 10000L)
+ goto invalid_date;
+ if (ltime->month == 2 && ltime->day == 29 &&
+ calc_days_in_year(ltime->year) != 366)
+ ltime->day=28; // Was leap-year
+ break;
+ case INTERVAL_YEAR_MONTH:
+ case INTERVAL_QUARTER:
+ case INTERVAL_MONTH:
+ period= (ltime->year*12 + sign * (long) interval.year*12 +
+ ltime->month-1 + sign * (long) interval.month);
+ if ((ulong) period >= 120000L)
+ goto invalid_date;
+ ltime->year= (uint) (period / 12);
+ ltime->month= (uint) (period % 12L)+1;
+ /* Adjust day if the new month doesn't have enough days */
+ if (ltime->day > days_in_month[ltime->month-1])
+ {
+ ltime->day = days_in_month[ltime->month-1];
+ if (ltime->month == 2 && calc_days_in_year(ltime->year) == 366)
+ ltime->day++; // Leap-year
+ }
+ break;
+ default:
+ goto null_date;
+ }
+
+ return 0; // Ok
+
+invalid_date:
+ push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ ER_DATETIME_FUNCTION_OVERFLOW,
+ ER(ER_DATETIME_FUNCTION_OVERFLOW),
+ "datetime");
+null_date:
+ return 1;
+}
+
+
+/*
+ Calculate difference between two datetime values as seconds + microseconds.
+
+ SYNOPSIS
+ calc_time_diff()
+ l_time1 - TIME/DATE/DATETIME value
+ l_time2 - TIME/DATE/DATETIME value
+ l_sign - 1 absolute values are substracted,
+ -1 absolute values are added.
+ seconds_out - Out parameter where difference between
+ l_time1 and l_time2 in seconds is stored.
+ microseconds_out- Out parameter where microsecond part of difference
+ between l_time1 and l_time2 is stored.
+
+ NOTE
+ This function calculates difference between l_time1 and l_time2 absolute
+ values. So one should set l_sign and correct result if he want to take
+ signs into account (i.e. for MYSQL_TIME values).
+
+ RETURN VALUES
+ Returns sign of difference.
+ 1 means negative result
+ 0 means positive result
+
+*/
+
+bool
+calc_time_diff(MYSQL_TIME *l_time1, MYSQL_TIME *l_time2, int l_sign, longlong *seconds_out,
+ long *microseconds_out)
+{
+ long days;
+ bool neg;
+ longlong microseconds;
+
+ /*
+ We suppose that if first argument is MYSQL_TIMESTAMP_TIME
+ the second argument should be TIMESTAMP_TIME also.
+ We should check it before calc_time_diff call.
+ */
+ if (l_time1->time_type == MYSQL_TIMESTAMP_TIME) // Time value
+ days= (long)l_time1->day - l_sign * (long)l_time2->day;
+ else
+ {
+ days= calc_daynr((uint) l_time1->year,
+ (uint) l_time1->month,
+ (uint) l_time1->day);
+ if (l_time2->time_type == MYSQL_TIMESTAMP_TIME)
+ days-= l_sign * (long)l_time2->day;
+ else
+ days-= l_sign*calc_daynr((uint) l_time2->year,
+ (uint) l_time2->month,
+ (uint) l_time2->day);
+ }
+
+ microseconds= ((longlong)days*LL(86400) +
+ (longlong)(l_time1->hour*3600L +
+ l_time1->minute*60L +
+ l_time1->second) -
+ l_sign*(longlong)(l_time2->hour*3600L +
+ l_time2->minute*60L +
+ l_time2->second)) * LL(1000000) +
+ (longlong)l_time1->second_part -
+ l_sign*(longlong)l_time2->second_part;
+
+ neg= 0;
+ if (microseconds < 0)
+ {
+ microseconds= -microseconds;
+ neg= 1;
+ }
+ *seconds_out= microseconds/1000000L;
+ *microseconds_out= (long) (microseconds%1000000L);
+ return neg;
+}
+
+
+/*
+ Compares 2 MYSQL_TIME structures
+
+ SYNOPSIS
+ my_time_compare()
+
+ a - first time
+ b - second time
+
+ RETURN VALUE
+ -1 - a < b
+ 0 - a == b
+ 1 - a > b
+
+ NOTES
+ TIME.second_part is not considered during comparison
+*/
+
+int
+my_time_compare(MYSQL_TIME *a, MYSQL_TIME *b)
+{
+ my_ulonglong a_t= TIME_to_ulonglong_datetime(a);
+ my_ulonglong b_t= TIME_to_ulonglong_datetime(b);
+
+ if (a_t > b_t)
+ return 1;
+ else if (a_t < b_t)
+ return -1;
+
+ return 0;
+}
#endif
diff --git a/sql/tzfile.h b/sql/tzfile.h
index 1a57c0c5f69..1ff82d62329 100644
--- a/sql/tzfile.h
+++ b/sql/tzfile.h
@@ -34,14 +34,14 @@
#define TZ_MAGIC "TZif"
struct tzhead {
- char tzh_magic[4]; /* TZ_MAGIC */
- char tzh_reserved[16]; /* reserved for future use */
- char tzh_ttisgmtcnt[4]; /* coded number of trans. time flags */
- char tzh_ttisstdcnt[4]; /* coded number of trans. time flags */
- char tzh_leapcnt[4]; /* coded number of leap seconds */
- char tzh_timecnt[4]; /* coded number of transition times */
- char tzh_typecnt[4]; /* coded number of local time types */
- char tzh_charcnt[4]; /* coded number of abbr. chars */
+ uchar tzh_magic[4]; /* TZ_MAGIC */
+ uchar tzh_reserved[16]; /* reserved for future use */
+ uchar tzh_ttisgmtcnt[4]; /* coded number of trans. time flags */
+ uchar tzh_ttisstdcnt[4]; /* coded number of trans. time flags */
+ uchar tzh_leapcnt[4]; /* coded number of leap seconds */
+ uchar tzh_timecnt[4]; /* coded number of transition times */
+ uchar tzh_typecnt[4]; /* coded number of local time types */
+ uchar tzh_charcnt[4]; /* coded number of abbr. chars */
};
/*
diff --git a/sql/tztime.cc b/sql/tztime.cc
index d3d952e3c1e..53870915973 100644
--- a/sql/tztime.cc
+++ b/sql/tztime.cc
@@ -106,7 +106,7 @@ typedef struct st_time_zone_info
uint revcnt; // Number of transition descr. for TIME->my_time_t conversion
/* The following are dynamical arrays are allocated in MEM_ROOT */
my_time_t *ats; // Times of transitions between time types
- unsigned char *types; // Local time types for transitions
+ uchar *types; // Local time types for transitions
TRAN_TYPE_INFO *ttis; // Local time types descriptions
#ifdef ABBR_ARE_USED
/* Storage for local time types abbreviations. They are stored as ASCIIZ */
@@ -153,7 +153,7 @@ static my_bool prepare_tz_info(TIME_ZONE_INFO *sp, MEM_ROOT *storage);
static my_bool
tz_load(const char *name, TIME_ZONE_INFO *sp, MEM_ROOT *storage)
{
- char *p;
+ uchar *p;
int read_from_file;
uint i;
FILE *file;
@@ -164,8 +164,8 @@ tz_load(const char *name, TIME_ZONE_INFO *sp, MEM_ROOT *storage)
union
{
struct tzhead tzhead;
- char buf[sizeof(struct tzhead) + sizeof(my_time_t) * TZ_MAX_TIMES +
- TZ_MAX_TIMES + sizeof(TRAN_TYPE_INFO) * TZ_MAX_TYPES +
+ uchar buf[sizeof(struct tzhead) + sizeof(my_time_t) * TZ_MAX_TIMES +
+ TZ_MAX_TIMES + sizeof(TRAN_TYPE_INFO) * TZ_MAX_TYPES +
#ifdef ABBR_ARE_USED
max(TZ_MAX_CHARS + 1, (2 * (MY_TZNAME_MAX + 1))) +
#endif
@@ -189,7 +189,7 @@ tz_load(const char *name, TIME_ZONE_INFO *sp, MEM_ROOT *storage)
sp->timecnt= int4net(u.tzhead.tzh_timecnt);
sp->typecnt= int4net(u.tzhead.tzh_typecnt);
sp->charcnt= int4net(u.tzhead.tzh_charcnt);
- p= u.tzhead.tzh_charcnt + sizeof u.tzhead.tzh_charcnt;
+ p= u.tzhead.tzh_charcnt + sizeof(u.tzhead.tzh_charcnt);
if (sp->leapcnt > TZ_MAX_LEAPS ||
sp->typecnt == 0 || sp->typecnt > TZ_MAX_TYPES ||
sp->timecnt > TZ_MAX_TIMES ||
@@ -221,7 +221,7 @@ tz_load(const char *name, TIME_ZONE_INFO *sp, MEM_ROOT *storage)
sp->ats= (my_time_t *)tzinfo_buf;
tzinfo_buf+= ALIGN_SIZE(sp->timecnt * sizeof(my_time_t));
- sp->types= (unsigned char *)tzinfo_buf;
+ sp->types= (uchar *)tzinfo_buf;
tzinfo_buf+= ALIGN_SIZE(sp->timecnt);
sp->ttis= (TRAN_TYPE_INFO *)tzinfo_buf;
tzinfo_buf+= ALIGN_SIZE(sp->typecnt * sizeof(TRAN_TYPE_INFO));
@@ -236,7 +236,7 @@ tz_load(const char *name, TIME_ZONE_INFO *sp, MEM_ROOT *storage)
for (i= 0; i < sp->timecnt; i++)
{
- sp->types[i]= (unsigned char) *p++;
+ sp->types[i]= (uchar) *p++;
if (sp->types[i] >= sp->typecnt)
return 1;
}
@@ -247,10 +247,10 @@ tz_load(const char *name, TIME_ZONE_INFO *sp, MEM_ROOT *storage)
ttisp= &sp->ttis[i];
ttisp->tt_gmtoff= int4net(p);
p+= 4;
- ttisp->tt_isdst= (unsigned char) *p++;
+ ttisp->tt_isdst= (uchar) *p++;
if (ttisp->tt_isdst != 0 && ttisp->tt_isdst != 1)
return 1;
- ttisp->tt_abbrind= (unsigned char) *p++;
+ ttisp->tt_abbrind= (uchar) *p++;
if (ttisp->tt_abbrind > sp->charcnt)
return 1;
}
@@ -546,8 +546,8 @@ sec_to_TIME(MYSQL_TIME * tmp, my_time_t t, long offset)
int yleap;
const uint *ip;
- days= t / SECS_PER_DAY;
- rem= t % SECS_PER_DAY;
+ days= (long) (t / SECS_PER_DAY);
+ rem= (long) (t % SECS_PER_DAY);
/*
We do this as separate step after dividing t, because this
@@ -807,7 +807,6 @@ sec_since_epoch(int year, int mon, int mday, int hour, int min ,int sec)
SECS_PER_MIN + sec;
}
-
/*
Converts local time in broken down MYSQL_TIME representation to my_time_t
representation.
@@ -1416,7 +1415,9 @@ Time_zone_offset::get_name() const
static Time_zone_utc tz_UTC;
static Time_zone_system tz_SYSTEM;
+static Time_zone_offset tz_OFFSET0(0);
+Time_zone *my_tz_OFFSET0= &tz_OFFSET0;
Time_zone *my_tz_UTC= &tz_UTC;
Time_zone *my_tz_SYSTEM= &tz_SYSTEM;
@@ -1456,15 +1457,15 @@ static bool time_zone_tables_exist= 1;
static const LEX_STRING tz_tables_names[MY_TZ_TABLES_COUNT]=
{
- {(char *) STRING_WITH_LEN("time_zone_name")},
- {(char *) STRING_WITH_LEN("time_zone")},
- {(char *) STRING_WITH_LEN("time_zone_transition_type")},
- {(char *) STRING_WITH_LEN("time_zone_transition")}
+ { C_STRING_WITH_LEN("time_zone_name")},
+ { C_STRING_WITH_LEN("time_zone")},
+ { C_STRING_WITH_LEN("time_zone_transition_type")},
+ { C_STRING_WITH_LEN("time_zone_transition")}
};
/* Name of database to which those tables belong. */
-static const LEX_STRING tz_tables_db_name= {(char *) STRING_WITH_LEN("mysql")};
+static const LEX_STRING tz_tables_db_name= { C_STRING_WITH_LEN("mysql")};
class Tz_names_entry: public Sql_alloc
@@ -1480,42 +1481,39 @@ public:
they should obey C calling conventions.
*/
-extern "C" byte* my_tz_names_get_key(Tz_names_entry *entry, uint *length,
- my_bool not_used __attribute__((unused)))
+extern "C" uchar *
+my_tz_names_get_key(Tz_names_entry *entry, size_t *length,
+ my_bool not_used __attribute__((unused)))
{
*length= entry->name.length();
- return (byte*) entry->name.ptr();
+ return (uchar*) entry->name.ptr();
}
-extern "C" byte* my_offset_tzs_get_key(Time_zone_offset *entry, uint *length,
- my_bool not_used __attribute__((unused)))
+extern "C" uchar *
+my_offset_tzs_get_key(Time_zone_offset *entry,
+ size_t *length,
+ my_bool not_used __attribute__((unused)))
{
*length= sizeof(long);
- return (byte*) &entry->offset;
+ return (uchar*) &entry->offset;
}
/*
- Prepare table list with time zone related tables from preallocated array
- and add to global table list.
+ Prepare table list with time zone related tables from preallocated array.
SYNOPSIS
tz_init_table_list()
tz_tabs - pointer to preallocated array of MY_TZ_TABLES_COUNT
TABLE_LIST objects
- global_next_ptr - pointer to variable which points to global_next member
- of last element of global table list (or list root
- then list is empty) (in/out).
DESCRIPTION
This function prepares list of TABLE_LIST objects which can be used
- for opening of time zone tables from preallocated array. It also links
- this list to the end of global table list (it will read and update
- accordingly variable pointed by global_next_ptr for this).
+ for opening of time zone tables from preallocated array.
*/
static void
-tz_init_table_list(TABLE_LIST *tz_tabs, TABLE_LIST ***global_next_ptr)
+tz_init_table_list(TABLE_LIST *tz_tabs)
{
bzero(tz_tabs, sizeof(TABLE_LIST) * MY_TZ_TABLES_COUNT);
@@ -1532,64 +1530,6 @@ tz_init_table_list(TABLE_LIST *tz_tabs, TABLE_LIST ***global_next_ptr)
if (i != 0)
tz_tabs[i].prev_global= &tz_tabs[i-1].next_global;
}
-
- /* Link into global list */
- tz_tabs[0].prev_global= *global_next_ptr;
- **global_next_ptr= tz_tabs;
- /* Update last-global-pointer to point to pointer in last table */
- *global_next_ptr= &tz_tabs[MY_TZ_TABLES_COUNT-1].next_global;
-}
-
-
-/*
- Fake table list object, pointer to which is returned by
- my_tz_get_tables_list() as indication of error.
-*/
-TABLE_LIST fake_time_zone_tables_list;
-
-/*
- Create table list with time zone related tables and add it to the end
- of global table list.
-
- SYNOPSIS
- my_tz_get_table_list()
- thd - current thread object
- global_next_ptr - pointer to variable which points to global_next member
- of last element of global table list (or list root
- then list is empty) (in/out).
-
- DESCRIPTION
- This function creates list of TABLE_LIST objects allocated in thd's
- memroot, which can be used for opening of time zone tables. It will also
- link this list to the end of global table list (it will read and update
- accordingly variable pointed by global_next_ptr for this).
-
- NOTE
- my_tz_check_n_skip_implicit_tables() function depends on fact that
- elements of list created are allocated as TABLE_LIST[MY_TZ_TABLES_COUNT]
- array.
-
- RETURN VALUES
- Returns pointer to first TABLE_LIST object, (could be 0 if time zone
- tables don't exist) and &fake_time_zone_tables_list in case of error.
-*/
-
-TABLE_LIST *
-my_tz_get_table_list(THD *thd, TABLE_LIST ***global_next_ptr)
-{
- TABLE_LIST *tz_tabs;
- DBUG_ENTER("my_tz_get_table_list");
-
- if (!time_zone_tables_exist)
- DBUG_RETURN(0);
-
- if (!(tz_tabs= (TABLE_LIST *)thd->alloc(sizeof(TABLE_LIST) *
- MY_TZ_TABLES_COUNT)))
- DBUG_RETURN(&fake_time_zone_tables_list);
-
- tz_init_table_list(tz_tabs, global_next_ptr);
-
- DBUG_RETURN(tz_tabs);
}
@@ -1622,8 +1562,8 @@ my_bool
my_tz_init(THD *org_thd, const char *default_tzname, my_bool bootstrap)
{
THD *thd;
- TABLE_LIST *tables= 0;
- TABLE_LIST tables_buff[1+MY_TZ_TABLES_COUNT], **last_global_next_ptr;
+ 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;
@@ -1638,10 +1578,11 @@ 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))
+ 0, 0, (hash_get_key) my_tz_names_get_key, 0, 0))
{
sql_print_error("Fatal error: OOM while initializing time zones");
goto end;
@@ -1665,7 +1606,7 @@ my_tz_init(THD *org_thd, const char *default_tzname, my_bool bootstrap)
}
tmp_tzname->name.set(STRING_WITH_LEN("SYSTEM"), &my_charset_latin1);
tmp_tzname->tz= my_tz_SYSTEM;
- if (my_hash_insert(&tz_names, (const byte *)tmp_tzname))
+ if (my_hash_insert(&tz_names, (const uchar *)tmp_tzname))
{
sql_print_error("Fatal error: OOM while initializing time zones");
goto end_with_cleanup;
@@ -1685,27 +1626,30 @@ my_tz_init(THD *org_thd, const char *default_tzname, my_bool bootstrap)
*/
thd->set_db(db, sizeof(db)-1);
- bzero((char*) &tables_buff, sizeof(TABLE_LIST));
- tables_buff[0].alias= tables_buff[0].table_name=
+ bzero((char*) &tz_tables[0], sizeof(TABLE_LIST));
+ tz_tables[0].alias= tz_tables[0].table_name=
(char*)"time_zone_leap_second";
- tables_buff[0].lock_type= TL_READ;
- tables_buff[0].db= db;
+ tz_tables[0].table_name_length= 21;
+ tz_tables[0].db= db;
+ tz_tables[0].db_length= sizeof(db)-1;
+ tz_tables[0].lock_type= TL_READ;
+
+ 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;
+
/*
- Fill TABLE_LIST for the rest of the time zone describing tables
- and link it to first one.
+ We need to open only mysql.time_zone_leap_second, but we try to
+ open all time zone tables to see if they exist.
*/
- last_global_next_ptr= &(tables_buff[0].next_global);
- tz_init_table_list(tables_buff + 1, &last_global_next_ptr);
-
- if (simple_open_n_lock_tables(thd, tables_buff))
+ if (open_system_tables_for_read(thd, tz_tables, &open_tables_state_backup))
{
sql_print_warning("Can't open and lock time zone table: %s "
- "trying to live without them", thd->net.last_error);
+ "trying to live without them", thd->main_da.message());
/* We will try emulate that everything is ok */
return_val= time_zone_tables_exist= 0;
goto end_with_setting_default_tz;
}
- tables= tables_buff + 1;
/*
Now we are going to load leap seconds descriptions that are shared
@@ -1721,13 +1665,15 @@ my_tz_init(THD *org_thd, const char *default_tzname, my_bool bootstrap)
goto end_with_close;
}
- table= tables_buff[0].table;
+ 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);
+ (void)table->file->ha_index_init(0, 1);
+ table->use_all_columns();
+
tz_leapcnt= 0;
res= table->file->index_first(table->record[0]);
@@ -1748,7 +1694,7 @@ my_tz_init(THD *org_thd, const char *default_tzname, my_bool bootstrap)
tz_leapcnt++;
DBUG_PRINT("info",
- ("time_zone_leap_second table: tz_leapcnt: %u tt_time: %lu offset=%ld",
+ ("time_zone_leap_second table: tz_leapcnt: %u tt_time: %lu offset: %ld",
tz_leapcnt, (ulong) tz_lsis[tz_leapcnt-1].ls_trans,
tz_lsis[tz_leapcnt-1].ls_corr));
@@ -1776,7 +1722,12 @@ end_with_setting_default_tz:
if (default_tzname)
{
String tmp_tzname2(default_tzname, &my_charset_latin1);
- if (!(global_system_variables.time_zone= my_tz_find(&tmp_tzname2, tables)))
+ /*
+ Time zone tables may be open here, and my_tz_find() may open
+ most of them once more, but this is OK for system tables open
+ for READ.
+ */
+ if (!(global_system_variables.time_zone= my_tz_find(thd, &tmp_tzname2)))
{
sql_print_error("Fatal error: Illegal or unknown default time zone '%s'",
default_tzname);
@@ -1785,8 +1736,11 @@ end_with_setting_default_tz:
}
end_with_close:
- thd->version--; /* Force close to free memory */
- close_thread_tables(thd);
+ if (time_zone_tables_exist)
+ {
+ thd->version--; /* Force close to free memory */
+ close_system_tables(thd, &open_tables_state_backup);
+ }
end_with_cleanup:
@@ -1866,18 +1820,16 @@ tz_load_from_open_tables(const String *tz_name, TABLE_LIST *tz_tables)
TIME_ZONE_INFO structure
*/
my_time_t ats[TZ_MAX_TIMES];
- unsigned char types[TZ_MAX_TIMES];
+ uchar types[TZ_MAX_TIMES];
TRAN_TYPE_INFO ttis[TZ_MAX_TYPES];
#ifdef ABBR_ARE_USED
char chars[max(TZ_MAX_CHARS + 1, (2 * (MY_TZNAME_MAX + 1)))];
#endif
-
DBUG_ENTER("tz_load_from_open_tables");
-
/* Prepare tz_info for loading also let us make copy of time zone name */
- if (!(alloc_buff= alloc_root(&tz_storage, sizeof(TIME_ZONE_INFO) +
- tz_name->length() + 1)))
+ if (!(alloc_buff= (char*) alloc_root(&tz_storage, sizeof(TIME_ZONE_INFO) +
+ tz_name->length() + 1)))
{
sql_print_error("Out of memory while loading time zone description");
return 0;
@@ -1904,12 +1856,12 @@ tz_load_from_open_tables(const String *tz_name, TABLE_LIST *tz_tables)
mysql.time_zone* tables are MyISAM and these operations always succeed
for MyISAM.
*/
- (void)table->file->ha_index_init(0);
+ (void)table->file->ha_index_init(0, 1);
- if (table->file->index_read(table->record[0], (byte*)table->field[0]->ptr,
- 0, HA_READ_KEY_EXACT))
+ if (table->file->index_read_map(table->record[0], table->field[0]->ptr,
+ HA_WHOLE_KEY, HA_READ_KEY_EXACT))
{
-#ifdef EXTRA_DEBUG
+#ifdef EXTRA_DEBUG
/*
Most probably user has mistyped time zone name, so no need to bark here
unless we need it for debugging.
@@ -1931,10 +1883,10 @@ tz_load_from_open_tables(const String *tz_name, TABLE_LIST *tz_tables)
table= tz_tables->table;
tz_tables= tz_tables->next_local;
table->field[0]->store((longlong) tzid, TRUE);
- (void)table->file->ha_index_init(0);
+ (void)table->file->ha_index_init(0, 1);
- if (table->file->index_read(table->record[0], (byte*)table->field[0]->ptr,
- 0, HA_READ_KEY_EXACT))
+ if (table->file->index_read_map(table->record[0], table->field[0]->ptr,
+ HA_WHOLE_KEY, HA_READ_KEY_EXACT))
{
sql_print_error("Can't find description of time zone '%u'", tzid);
goto end;
@@ -1958,11 +1910,10 @@ tz_load_from_open_tables(const String *tz_name, TABLE_LIST *tz_tables)
table= tz_tables->table;
tz_tables= tz_tables->next_local;
table->field[0]->store((longlong) tzid, TRUE);
- (void)table->file->ha_index_init(0);
+ (void)table->file->ha_index_init(0, 1);
- // FIXME Is there any better approach than explicitly specifying 4 ???
- res= table->file->index_read(table->record[0], (byte*)table->field[0]->ptr,
- 4, HA_READ_KEY_EXACT);
+ res= table->file->index_read_map(table->record[0], table->field[0]->ptr,
+ (key_part_map)1, HA_READ_KEY_EXACT);
while (!res)
{
ttid= (uint)table->field[1]->val_int();
@@ -2010,7 +1961,7 @@ tz_load_from_open_tables(const String *tz_name, TABLE_LIST *tz_tables)
tz_info->typecnt= ttid + 1;
res= table->file->index_next_same(table->record[0],
- (byte*)table->field[0]->ptr, 4);
+ table->field[0]->ptr, 4);
}
if (res != HA_ERR_END_OF_FILE)
@@ -2030,11 +1981,10 @@ 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);
+ (void)table->file->ha_index_init(0, 1);
- // FIXME Is there any better approach than explicitly specifying 4 ???
- res= table->file->index_read(table->record[0], (byte*)table->field[0]->ptr,
- 4, HA_READ_KEY_EXACT);
+ res= table->file->index_read_map(table->record[0], table->field[0]->ptr,
+ (key_part_map)1, HA_READ_KEY_EXACT);
while (!res)
{
ttime= (my_time_t)table->field[1]->val_int();
@@ -2060,11 +2010,11 @@ tz_load_from_open_tables(const String *tz_name, TABLE_LIST *tz_tables)
tz_info->timecnt++;
DBUG_PRINT("info",
- ("time_zone_transition table: tz_id: %u tt_time: %lu tt_id: %u",
- tzid, (ulong) ttime, ttid));
+ ("time_zone_transition table: tz_id: %u tt_time: %lu tt_id: %u",
+ tzid, (ulong) ttime, ttid));
res= table->file->index_next_same(table->record[0],
- (byte*)table->field[0]->ptr, 4);
+ table->field[0]->ptr, 4);
}
/*
@@ -2084,24 +2034,24 @@ tz_load_from_open_tables(const String *tz_name, TABLE_LIST *tz_tables)
/*
Now we will allocate memory and init TIME_ZONE_INFO structure.
*/
- if (!(alloc_buff= alloc_root(&tz_storage,
- ALIGN_SIZE(sizeof(my_time_t) *
- tz_info->timecnt) +
- ALIGN_SIZE(tz_info->timecnt) +
+ if (!(alloc_buff= (char*) alloc_root(&tz_storage,
+ ALIGN_SIZE(sizeof(my_time_t) *
+ tz_info->timecnt) +
+ ALIGN_SIZE(tz_info->timecnt) +
#ifdef ABBR_ARE_USED
- ALIGN_SIZE(tz_info->charcnt) +
+ ALIGN_SIZE(tz_info->charcnt) +
#endif
- sizeof(TRAN_TYPE_INFO) * tz_info->typecnt)))
+ sizeof(TRAN_TYPE_INFO) *
+ tz_info->typecnt)))
{
sql_print_error("Out of memory while loading time zone description");
goto end;
}
-
- tz_info->ats= (my_time_t *)alloc_buff;
+ tz_info->ats= (my_time_t *) alloc_buff;
memcpy(tz_info->ats, ats, tz_info->timecnt * sizeof(my_time_t));
alloc_buff+= ALIGN_SIZE(sizeof(my_time_t) * tz_info->timecnt);
- tz_info->types= (unsigned char *)alloc_buff;
+ tz_info->types= (uchar *)alloc_buff;
memcpy(tz_info->types, types, tz_info->timecnt);
alloc_buff+= ALIGN_SIZE(tz_info->timecnt);
#ifdef ABBR_ARE_USED
@@ -2133,7 +2083,7 @@ tz_load_from_open_tables(const String *tz_name, TABLE_LIST *tz_tables)
&(tmp_tzname->name))) ||
(tmp_tzname->name.set(tz_name_buff, tz_name->length(),
&my_charset_latin1),
- my_hash_insert(&tz_names, (const byte *)tmp_tzname)))
+ my_hash_insert(&tz_names, (const uchar *)tmp_tzname)))
{
sql_print_error("Out of memory while loading time zone");
goto end;
@@ -2238,8 +2188,8 @@ str_to_offset(const char *str, uint length, long *offset)
SYNOPSIS
my_tz_find()
+ thd - pointer to thread THD structure
name - time zone specification
- tz_tables - list of opened'n'locked time zone describing tables
DESCRIPTION
This function checks if name is one of time zones described in db,
@@ -2261,11 +2211,10 @@ str_to_offset(const char *str, uint length, long *offset)
values as parameter without additional external check and this property
is used by @@time_zone variable handling code).
- It will perform lookup in system tables (mysql.time_zone*) if needed
- using tz_tables as list of already opened tables (for info about this
- list look at tz_load_from_open_tables() description). It won't perform
- such lookup if no time zone describing tables were found during server
- start up.
+ It will perform lookup in system tables (mysql.time_zone*),
+ opening and locking them, and closing afterwards. It won't perform
+ such lookup if no time zone describing tables were found during
+ server start up.
RETURN VALUE
Pointer to corresponding Time_zone object. 0 - in case of bad time zone
@@ -2273,17 +2222,14 @@ str_to_offset(const char *str, uint length, long *offset)
*/
Time_zone *
-my_tz_find(const String * name, TABLE_LIST *tz_tables)
+my_tz_find(THD *thd, const String *name)
{
Tz_names_entry *tmp_tzname;
Time_zone *result_tz= 0;
long offset;
-
DBUG_ENTER("my_tz_find");
DBUG_PRINT("enter", ("time zone name='%s'",
- name ? ((String *)name)->c_ptr_safe() : "NULL"));
-
- DBUG_ASSERT(!time_zone_tables_exist || tz_tables || current_thd->slave_thread);
+ name ? ((String *)name)->c_ptr_safe() : "NULL"));
if (!name)
DBUG_RETURN(0);
@@ -2294,13 +2240,13 @@ my_tz_find(const String * name, TABLE_LIST *tz_tables)
{
if (!(result_tz= (Time_zone_offset *)hash_search(&offset_tzs,
- (const byte *)&offset,
+ (const uchar *)&offset,
sizeof(long))))
{
DBUG_PRINT("info", ("Creating new Time_zone_offset object"));
if (!(result_tz= new (&tz_storage) Time_zone_offset(offset)) ||
- my_hash_insert(&offset_tzs, (const byte *) result_tz))
+ my_hash_insert(&offset_tzs, (const uchar *) result_tz))
{
result_tz= 0;
sql_print_error("Fatal error: Out of memory "
@@ -2312,11 +2258,22 @@ my_tz_find(const String * name, TABLE_LIST *tz_tables)
{
result_tz= 0;
if ((tmp_tzname= (Tz_names_entry *)hash_search(&tz_names,
- (const byte *)name->ptr(),
+ (const uchar *)name->ptr(),
name->length())))
result_tz= tmp_tzname->tz;
- else if (time_zone_tables_exist && tz_tables)
- result_tz= tz_load_from_open_tables(name, tz_tables);
+ else if (time_zone_tables_exist)
+ {
+ TABLE_LIST tz_tables[MY_TZ_TABLES_COUNT];
+ Open_tables_state open_tables_state_backup;
+
+ tz_init_table_list(tz_tables);
+ if (!open_system_tables_for_read(thd, tz_tables,
+ &open_tables_state_backup))
+ {
+ result_tz= tz_load_from_open_tables(name, tz_tables);
+ close_system_tables(thd, &open_tables_state_backup);
+ }
+ }
}
VOID(pthread_mutex_unlock(&tz_LOCK));
@@ -2325,58 +2282,6 @@ my_tz_find(const String * name, TABLE_LIST *tz_tables)
}
-/*
- A more standalone version of my_tz_find(): will open tz tables if needed.
- This is so far only used by replication, where time zone setting does not
- happen in the usual query context.
-
- SYNOPSIS
- my_tz_find_with_opening_tz_tables()
- thd - pointer to thread's THD structure
- name - time zone specification
-
- DESCRIPTION
- This function tries to find a time zone which matches the named passed in
- argument. If it fails, it will open time zone tables and re-try the
- search.
- This function is needed for the slave SQL thread, which does not do the
- addition of time zone tables which is usually done during query parsing
- (as time zone setting by slave does not happen in mysql_parse() but
- before). So it needs to open tz tables by itself if needed.
- See notes of my_tz_find() as they also apply here.
-
- RETURN VALUE
- Pointer to corresponding Time_zone object. 0 - in case of bad time zone
- specification or other error.
-
-*/
-Time_zone *my_tz_find_with_opening_tz_tables(THD *thd, const String *name)
-{
- Time_zone *tz;
- DBUG_ENTER("my_tz_find_with_opening_tables");
- DBUG_ASSERT(thd);
- DBUG_ASSERT(thd->slave_thread); // intended for use with slave thread only
- if (!(tz= my_tz_find(name, 0)) && time_zone_tables_exist)
- {
- /*
- Probably we have not loaded this time zone yet so let us look it up in
- our time zone tables. Note that if we don't have tz tables on this
- slave, we don't even try.
- */
- TABLE_LIST tables[MY_TZ_TABLES_COUNT];
- TABLE_LIST *dummy;
- TABLE_LIST **dummyp= &dummy;
- tz_init_table_list(tables, &dummyp);
- if (simple_open_n_lock_tables(thd, tables))
- DBUG_RETURN(0);
- tz= my_tz_find(name, tables);
- /* We need to close tables _now_ to not pollute coming query */
- close_thread_tables(thd);
- }
- DBUG_RETURN(tz);
-}
-
-
/**
Convert leap seconds into non-leap
diff --git a/sql/tztime.h b/sql/tztime.h
index 750b8dacbe1..9bf103519c4 100644
--- a/sql/tztime.h
+++ b/sql/tztime.h
@@ -20,7 +20,7 @@
#if !defined(TESTTIME) && !defined(TZINFO2SQL)
-/*
+/**
This class represents abstract time zone and provides
basic interface for MYSQL_TIME <-> my_time_t conversion.
Actual time zones which are specified by DB, or via offset
@@ -30,7 +30,7 @@ class Time_zone: public Sql_alloc
{
public:
Time_zone() {} /* Remove gcc warning */
- /*
+ /**
Converts local time in broken down MYSQL_TIME representation to
my_time_t (UTC seconds since Epoch) represenation.
Returns 0 in case of error. Sets in_dst_time_gap to true if date provided
@@ -38,19 +38,19 @@ public:
*/
virtual my_time_t TIME_to_gmt_sec(const MYSQL_TIME *t,
my_bool *in_dst_time_gap) const = 0;
- /*
+ /**
Converts time in my_time_t representation to local time in
broken down MYSQL_TIME representation.
*/
virtual void gmt_sec_to_TIME(MYSQL_TIME *tmp, my_time_t t) const = 0;
- /*
+ /**
Because of constness of String returned by get_name() time zone name
have to be already zeroended to be able to use String::ptr() instead
of c_ptr().
*/
virtual const String * get_name() const = 0;
- /*
+ /**
We need this only for surpressing warnings, objects of this type are
allocated on MEM_ROOT and should not require destruction.
*/
@@ -62,15 +62,13 @@ protected:
extern Time_zone * my_tz_UTC;
extern Time_zone * my_tz_SYSTEM;
-extern TABLE_LIST * my_tz_get_table_list(THD *thd, TABLE_LIST ***global_next_ptr);
-extern Time_zone * my_tz_find(const String *name, TABLE_LIST *tz_tables);
-extern Time_zone * my_tz_find_with_opening_tz_tables(THD *thd, const String *name);
+extern Time_zone * my_tz_OFFSET0;
+extern Time_zone * my_tz_find(THD *thd, const String *name);
extern my_bool my_tz_init(THD *org_thd, const char *default_tzname, my_bool bootstrap);
extern void my_tz_free();
+extern my_time_t sec_since_epoch_TIME(MYSQL_TIME *t);
-extern TABLE_LIST fake_time_zone_tables_list;
-
-/*
+/**
Number of elements in table list produced by my_tz_get_table_list()
(this table list contains tables which are needed for dynamical loading
of time zone descriptions). Actually it is imlementation detail that
@@ -79,34 +77,5 @@ extern TABLE_LIST fake_time_zone_tables_list;
static const int MY_TZ_TABLES_COUNT= 4;
-/*
- Check if we have pointer to the begining of list of implicitly used time
- zone tables, set SELECT_ACL for them and fast-forward to its end.
-
- SYNOPSIS
- my_tz_check_n_skip_implicit_tables()
- table - (in/out) pointer to element of table list to check
- tz_tables - list of implicitly used time zone tables received
- from my_tz_get_table_list() function.
-
- NOTE
- This function relies on my_tz_get_table_list() implementation.
-
- RETURN VALUE
- TRUE - if table points to the beggining of tz_tables list
- FALSE - otherwise.
-*/
-inline bool my_tz_check_n_skip_implicit_tables(TABLE_LIST **table,
- TABLE_LIST *tz_tables)
-{
- if (*table == tz_tables)
- {
- for (int i= 0; i < MY_TZ_TABLES_COUNT; i++)
- (*table)[i].grant.privilege= SELECT_ACL;
- (*table)+= MY_TZ_TABLES_COUNT - 1;
- return TRUE;
- }
- return FALSE;
-}
#endif /* !defined(TESTTIME) && !defined(TZINFO2SQL) */
diff --git a/sql/udf_example.c b/sql/udf_example.c
index db48984eed8..30d85d95034 100644
--- a/sql/udf_example.c
+++ b/sql/udf_example.c
@@ -683,7 +683,7 @@ longlong sequence(UDF_INIT *initid __attribute__((unused)), UDF_ARGS *args,
****************************************************************************/
#ifdef __WIN__
-#include <winsock.h>
+#include <winsock2.h>
#else
#include <sys/socket.h>
#include <netinet/in.h>
diff --git a/sql/uniques.cc b/sql/uniques.cc
index a0d1beaf0f9..858bedb04cd 100644
--- a/sql/uniques.cc
+++ b/sql/uniques.cc
@@ -34,7 +34,7 @@
#include "sql_sort.h"
-int unique_write_to_file(gptr key, element_count count, Unique *unique)
+int unique_write_to_file(uchar* key, element_count count, Unique *unique)
{
/*
Use unique->size (size of element stored in the tree) and not
@@ -42,11 +42,10 @@ int unique_write_to_file(gptr key, element_count count, Unique *unique)
when tree implementation chooses to store pointer to key in TREE_ELEMENT
(instead of storing the element itself there)
*/
- return my_b_write(&unique->file, (byte*) key,
- unique->size) ? 1 : 0;
+ return my_b_write(&unique->file, key, unique->size) ? 1 : 0;
}
-int unique_write_to_ptrs(gptr key, element_count count, Unique *unique)
+int unique_write_to_ptrs(uchar* key, element_count count, Unique *unique)
{
memcpy(unique->record_pointers, key, unique->size);
unique->record_pointers+=unique->size;
@@ -330,7 +329,7 @@ bool Unique::flush()
if (tree_walk(&tree, (tree_walk_action) unique_write_to_file,
(void*) this, left_root_right) ||
- insert_dynamic(&file_ptrs, (gptr) &file_ptr))
+ insert_dynamic(&file_ptrs, (uchar*) &file_ptr))
return 1;
delete_tree(&tree);
return 0;
@@ -369,11 +368,11 @@ Unique::reset()
C_MODE_START
-static int buffpek_compare(void *arg, byte *key_ptr1, byte *key_ptr2)
+static int buffpek_compare(void *arg, uchar *key_ptr1, uchar *key_ptr2)
{
BUFFPEK_COMPARE_CONTEXT *ctx= (BUFFPEK_COMPARE_CONTEXT *) arg;
return ctx->key_compare(ctx->key_compare_arg,
- *((byte **) key_ptr1), *((byte **)key_ptr2));
+ *((uchar **) key_ptr1), *((uchar **)key_ptr2));
}
C_MODE_END
@@ -450,7 +449,7 @@ static bool merge_walk(uchar *merge_buffer, ulong merge_buffer_size,
if (bytes_read == (uint) (-1))
goto end;
DBUG_ASSERT(bytes_read);
- queue_insert(&queue, (byte *) top);
+ queue_insert(&queue, (uchar *) top);
}
top= (BUFFPEK *) queue_top(&queue);
while (queue.elements > 1)
@@ -581,7 +580,7 @@ bool Unique::get(TABLE *table)
if (my_b_tell(&file) == 0)
{
/* Whole tree is in memory; Don't use disk if you don't need to */
- if ((record_pointers=table->sort.record_pointers= (byte*)
+ if ((record_pointers=table->sort.record_pointers= (uchar*)
my_malloc(size * tree.elements_in_tree, MYF(0))))
{
(void) tree_walk(&tree, (tree_walk_action) unique_write_to_ptrs,
@@ -640,7 +639,7 @@ bool Unique::get(TABLE *table)
goto err;
error=0;
err:
- x_free((gptr) sort_buffer);
+ x_free(sort_buffer);
if (flush_io_cache(outfile))
error=1;
diff --git a/sql/unireg.cc b/sql/unireg.cc
index d658365abd0..da018ebec3d 100644
--- a/sql/unireg.cc
+++ b/sql/unireg.cc
@@ -23,29 +23,58 @@
str is a (long) to record position where 0 is the first position.
*/
-#define USES_TYPES
#include "mysql_priv.h"
#include <m_ctype.h>
#include <assert.h>
#define FCOMP 17 /* Bytes for a packed field */
-static uchar * pack_screens(List<create_field> &create_fields,
+static uchar * pack_screens(List<Create_field> &create_fields,
uint *info_length, uint *screens, bool small_file);
static uint pack_keys(uchar *keybuff,uint key_count, KEY *key_info,
ulong data_offset);
-static bool pack_header(uchar *forminfo,enum db_type table_type,
- List<create_field> &create_fields,
+static bool pack_header(uchar *forminfo,enum legacy_db_type table_type,
+ List<Create_field> &create_fields,
uint info_length, uint screens, uint table_options,
ulong data_offset, handler *file);
-static uint get_interval_id(uint *int_count,List<create_field> &create_fields,
- create_field *last_field);
-static bool pack_fields(File file, List<create_field> &create_fields,
+static uint get_interval_id(uint *int_count,List<Create_field> &create_fields,
+ Create_field *last_field);
+static bool pack_fields(File file, List<Create_field> &create_fields,
ulong data_offset);
-static bool make_empty_rec(THD *thd, int file, enum db_type table_type,
+static bool make_empty_rec(THD *thd, int file, enum legacy_db_type table_type,
uint table_options,
- List<create_field> &create_fields,
- uint reclength, ulong data_offset);
+ List<Create_field> &create_fields,
+ uint reclength, ulong data_offset,
+ handler *handler);
+
+/**
+ An interceptor to hijack ER_TOO_MANY_FIELDS error from
+ pack_screens and retry again without UNIREG screens.
+
+ XXX: what is a UNIREG screen?
+*/
+
+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);
+ bool is_handled;
+ Pack_header_error_handler() :is_handled(FALSE) {}
+};
+
+
+bool
+Pack_header_error_handler::
+handle_error(uint sql_errno,
+ const char * /* message */,
+ MYSQL_ERROR::enum_warning_level /* level */,
+ THD * /* thd */)
+{
+ is_handled= (sql_errno == ER_TOO_MANY_FIELDS);
+ return is_handled;
+}
/*
Create a frm (table definition) file
@@ -53,7 +82,7 @@ static bool make_empty_rec(THD *thd, int file, enum db_type table_type,
SYNOPSIS
mysql_create_frm()
thd Thread handler
- file_name Name of file (including database and .frm)
+ file_name Path for file (including database and .frm)
db Name of database
table Name of table
create_info create info parameters
@@ -61,21 +90,21 @@ static bool make_empty_rec(THD *thd, int file, enum db_type table_type,
keys number of keys to create
key_info Keys to create
db_file Handler to use. May be zero, in which case we use
- create_info->db_type
+ create_info->db_type
RETURN
false ok
true error
*/
-bool mysql_create_frm(THD *thd, my_string file_name,
+bool mysql_create_frm(THD *thd, const char *file_name,
const char *db, const char *table,
HA_CREATE_INFO *create_info,
- List<create_field> &create_fields,
+ List<Create_field> &create_fields,
uint keys, KEY *key_info,
handler *db_file)
{
LEX_STRING str_db_type;
- uint reclength, info_length, screens, key_info_length, maxlength, tmp_len;
+ uint reclength, info_length, screens, key_info_length, maxlength, tmp_len, i;
ulong key_buff_length;
File file;
ulong filepos, data_offset;
@@ -83,50 +112,83 @@ bool mysql_create_frm(THD *thd, my_string file_name,
TYPELIB formnames;
uchar *screen_buff;
char buff[128];
+#ifdef WITH_PARTITION_STORAGE_ENGINE
+ partition_info *part_info= thd->work_part_info;
+#endif
+ Pack_header_error_handler pack_header_error_handler;
+ int error;
DBUG_ENTER("mysql_create_frm");
+ DBUG_ASSERT(*fn_rext((char*)file_name)); // Check .frm extension
formnames.type_names=0;
if (!(screen_buff=pack_screens(create_fields,&info_length,&screens,0)))
DBUG_RETURN(1);
- if (db_file == NULL)
- db_file= get_new_handler((TABLE*) 0, thd->mem_root, create_info->db_type);
+ DBUG_ASSERT(db_file != NULL);
/* If fixed row records, we need one bit to check for deleted rows */
if (!(create_info->table_options & HA_OPTION_PACK_RECORD))
create_info->null_bits++;
data_offset= (create_info->null_bits + 7) / 8;
- if (pack_header(forminfo, create_info->db_type,create_fields,info_length,
- screens, create_info->table_options,
- data_offset, db_file))
+ thd->push_internal_handler(&pack_header_error_handler);
+
+ error= pack_header(forminfo, ha_legacy_type(create_info->db_type),
+ create_fields,info_length,
+ screens, create_info->table_options,
+ data_offset, db_file);
+
+ thd->pop_internal_handler();
+
+ if (error)
{
- my_free((gptr) screen_buff,MYF(0));
- if (thd->net.last_errno != ER_TOO_MANY_FIELDS)
+ my_free(screen_buff, MYF(0));
+ if (! pack_header_error_handler.is_handled)
DBUG_RETURN(1);
// Try again without UNIREG screens (to get more columns)
- thd->net.last_error[0]=0;
if (!(screen_buff=pack_screens(create_fields,&info_length,&screens,1)))
DBUG_RETURN(1);
- if (pack_header(forminfo, create_info->db_type, create_fields,info_length,
+ if (pack_header(forminfo, ha_legacy_type(create_info->db_type),
+ create_fields,info_length,
screens, create_info->table_options, data_offset, db_file))
{
- my_free((gptr) screen_buff,MYF(0));
+ my_free(screen_buff, MYF(0));
DBUG_RETURN(1);
}
}
reclength=uint2korr(forminfo+266);
/* Calculate extra data segment length */
- str_db_type.str= (char *) ha_get_storage_engine(create_info->db_type);
- str_db_type.length= (uint) strlen(str_db_type.str);
+ str_db_type.str= (char *) ha_resolve_storage_engine_name(create_info->db_type);
+ str_db_type.length= strlen(str_db_type.str);
+ /* str_db_type */
create_info->extra_size= (2 + str_db_type.length +
2 + create_info->connect_string.length);
+ /*
+ Partition:
+ Length of partition info = 4 byte
+ Potential NULL byte at end of partition info string = 1 byte
+ Indicator if auto-partitioned table = 1 byte
+ => Total 6 byte
+ */
+ create_info->extra_size+= 6;
+#ifdef WITH_PARTITION_STORAGE_ENGINE
+ if (part_info)
+ {
+ create_info->extra_size+= part_info->part_info_len;
+ }
+#endif
+
+ for (i= 0; i < keys; i++)
+ {
+ if (key_info[i].parser_name)
+ create_info->extra_size+= key_info[i].parser_name->length + 1;
+ }
if ((file=create_frm(thd, file_name, db, table, reclength, fileinfo,
create_info, keys)) < 0)
{
- my_free((gptr) screen_buff,MYF(0));
+ my_free(screen_buff, MYF(0));
DBUG_RETURN(1);
}
@@ -183,32 +245,70 @@ bool mysql_create_frm(THD *thd, my_string file_name,
strmake((char*) forminfo+47, create_info->comment.str ?
create_info->comment.str : "", create_info->comment.length);
forminfo[46]=(uchar) create_info->comment.length;
- if (my_pwrite(file,(byte*) fileinfo,64,0L,MYF_RW) ||
- my_pwrite(file,(byte*) keybuff,key_info_length,
+#ifdef WITH_PARTITION_STORAGE_ENGINE
+ if (part_info)
+ {
+ fileinfo[61]= (uchar) ha_legacy_type(part_info->default_engine_type);
+ DBUG_PRINT("info", ("part_db_type = %d", fileinfo[61]));
+ }
+#endif
+ int2store(fileinfo+59,db_file->extra_rec_buf_length());
+
+ if (my_pwrite(file, fileinfo, 64, 0L, MYF_RW) ||
+ my_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)));
- if (make_empty_rec(thd,file,create_info->db_type,create_info->table_options,
- create_fields,reclength, data_offset))
+ 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 byte*)buff, 2, MYF(MY_NABP)) ||
- my_write(file, (const byte*)create_info->connect_string.str,
+ if (my_write(file, (const uchar*)buff, 2, MYF(MY_NABP)) ||
+ my_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 byte*)buff, 2, MYF(MY_NABP)) ||
- my_write(file, (const byte*)str_db_type.str,
+ if (my_write(file, (const uchar*)buff, 2, MYF(MY_NABP)) ||
+ my_write(file, (const uchar*)str_db_type.str,
str_db_type.length, MYF(MY_NABP)))
goto err;
-
+
+#ifdef WITH_PARTITION_STORAGE_ENGINE
+ if (part_info)
+ {
+ 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,
+ part_info->part_info_len + 1, MYF_RW) ||
+ my_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))
+ goto err;
+ }
+ for (i= 0; i < keys; i++)
+ {
+ 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)))
+ goto err;
+ }
+ }
+
VOID(my_seek(file,filepos,MY_SEEK_SET,MYF(0)));
- if (my_write(file,(byte*) forminfo,288,MYF_RW) ||
- my_write(file,(byte*) screen_buff,info_length,MYF_RW) ||
+ if (my_write(file, forminfo, 288, MYF_RW) ||
+ my_write(file, screen_buff, info_length, MYF_RW) ||
pack_fields(file, create_fields, data_offset))
goto err;
@@ -221,7 +321,7 @@ bool mysql_create_frm(THD *thd, my_string file_name,
goto err;
uint read_length=uint2korr(forminfo)-256;
VOID(my_seek(file,filepos+256,MY_SEEK_SET,MYF(0)));
- if (read_string(file,(gptr*) &disk_buff,read_length))
+ if (read_string(file,(uchar**) &disk_buff,read_length))
goto err;
crypted->encode(disk_buff,read_length);
delete crypted;
@@ -234,12 +334,14 @@ bool mysql_create_frm(THD *thd, my_string file_name,
}
#endif
- my_free((gptr) screen_buff,MYF(0));
- my_free((gptr) keybuff, MYF(0));
+ my_free(screen_buff,MYF(0));
+ my_free(keybuff, MYF(0));
if (opt_sync_frm && !(create_info->options & HA_LEX_CREATE_TMP_TABLE) &&
- my_sync(file, MYF(MY_WME)))
- goto err2;
+ (my_sync(file, MYF(MY_WME)) ||
+ my_sync_dir_by_file(file_name, MYF(MY_WME))))
+ goto err2;
+
if (my_close(file,MYF(MY_WME)))
goto err3;
@@ -248,8 +350,8 @@ bool mysql_create_frm(THD *thd, my_string file_name,
Restore all UCS2 intervals.
HEX representation of them is not needed anymore.
*/
- List_iterator<create_field> it(create_fields);
- create_field *field;
+ List_iterator<Create_field> it(create_fields);
+ Create_field *field;
while ((field=it++))
{
if (field->save_interval)
@@ -262,8 +364,8 @@ bool mysql_create_frm(THD *thd, my_string file_name,
DBUG_RETURN(0);
err:
- my_free((gptr) screen_buff,MYF(0));
- my_free((gptr) keybuff, MYF(0));
+ my_free(screen_buff, MYF(0));
+ my_free(keybuff, MYF(0));
err2:
VOID(my_close(file,MYF(MY_WME)));
err3:
@@ -278,54 +380,65 @@ err3:
SYNOPSIS
rea_create_table()
thd Thread handler
- file_name Name of file (including database and .frm)
- db Name of database
- table Name of table
+ path Name of file (including database, without .frm)
+ db Data base name
+ table_name Table name
create_info create info parameters
create_fields Fields to create
keys number of keys to create
key_info Keys to create
- db_file Handler to use. May be zero, in which case we use
- create_info->db_type
+ file Handler to use
+
RETURN
0 ok
1 error
*/
-int rea_create_table(THD *thd, my_string file_name,
- const char *db, const char *table,
- HA_CREATE_INFO *create_info,
- List<create_field> &create_fields,
- uint keys, KEY *key_info)
+int rea_create_table(THD *thd, const char *path,
+ const char *db, const char *table_name,
+ HA_CREATE_INFO *create_info,
+ List<Create_field> &create_fields,
+ uint keys, KEY *key_info, handler *file)
{
DBUG_ENTER("rea_create_table");
- if (mysql_create_frm(thd, file_name, db, table, create_info,
- create_fields, keys, key_info, NULL))
+ char frm_name[FN_REFLEN];
+ strxmov(frm_name, path, reg_ext, NullS);
+ if (mysql_create_frm(thd, frm_name, db, table_name, create_info,
+ create_fields, keys, key_info, file))
+
DBUG_RETURN(1);
+
+ // Make sure mysql_create_frm din't remove extension
+ DBUG_ASSERT(*fn_rext(frm_name));
if (thd->variables.keep_files_on_create)
create_info->options|= HA_CREATE_KEEP_FILES;
- if (!create_info->frm_only && ha_create_table(file_name,create_info,0))
- {
- my_delete(file_name,MYF(0));
- DBUG_RETURN(1);
- }
+ if (file->ha_create_handler_files(path, NULL, CHF_CREATE_FLAG, create_info))
+ goto err_handler;
+ if (!create_info->frm_only && ha_create_table(thd, path, db, table_name,
+ create_info,0))
+ goto err_handler;
DBUG_RETURN(0);
+
+err_handler:
+ VOID(file->ha_create_handler_files(path, NULL, CHF_DELETE_FLAG, create_info));
+ my_delete(frm_name, MYF(0));
+ DBUG_RETURN(1);
} /* rea_create_table */
/* Pack screens to a screen for save in a form-file */
-static uchar * pack_screens(List<create_field> &create_fields,
- uint *info_length, uint *screens,
- bool small_file)
+static uchar *pack_screens(List<Create_field> &create_fields,
+ uint *info_length, uint *screens,
+ bool small_file)
{
reg1 uint i;
uint row,start_row,end_row,fields_on_screen;
uint length,cols;
uchar *info,*pos,*start_screen;
uint fields=create_fields.elements;
- List_iterator<create_field> it(create_fields);
+ List_iterator<Create_field> it(create_fields);
DBUG_ENTER("pack_screens");
start_row=4; end_row=22; cols=80; fields_on_screen=end_row+1-start_row;
@@ -333,7 +446,7 @@ static uchar * pack_screens(List<create_field> &create_fields,
*screens=(fields-1)/fields_on_screen+1;
length= (*screens) * (SC_INFO_LENGTH+ (cols>> 1)+4);
- create_field *field;
+ Create_field *field;
while ((field=it++))
length+=(uint) strlen(field->field_name)+1+TE_INFO_LENGTH+cols/2;
@@ -346,7 +459,7 @@ static uchar * pack_screens(List<create_field> &create_fields,
it.rewind();
for (i=0 ; i < fields ; i++)
{
- create_field *cfield=it++;
+ Create_field *cfield=it++;
if (row++ == end_row)
{
if (i)
@@ -362,7 +475,7 @@ static uchar * pack_screens(List<create_field> &create_fields,
pos[0]= (uchar) start_row-2; /* Header string */
pos[1]= (uchar) (cols >> 2);
pos[2]= (uchar) (cols >> 1) +1;
- strfill((my_string) pos+3,(uint) (cols >> 1),' ');
+ strfill((char *) pos+3,(uint) (cols >> 1),' ');
pos+=(cols >> 1)+4;
}
length=(uint) strlen(cfield->field_name);
@@ -409,7 +522,7 @@ static uint pack_keys(uchar *keybuff, uint key_count, KEY *keyinfo,
int2store(pos+2,key->key_length);
pos[4]= (uchar) key->key_parts;
pos[5]= (uchar) key->algorithm;
- pos[6]=pos[7]=0; // For the future
+ int2store(pos+6, key->block_size);
pos+=8;
key_parts+=key->key_parts;
DBUG_PRINT("loop", ("flags: %lu key_parts: %d at 0x%lx",
@@ -465,8 +578,8 @@ static uint pack_keys(uchar *keybuff, uint key_count, KEY *keyinfo,
/* Make formheader */
-static bool pack_header(uchar *forminfo, enum db_type table_type,
- List<create_field> &create_fields,
+static bool pack_header(uchar *forminfo, enum legacy_db_type table_type,
+ List<Create_field> &create_fields,
uint info_length, uint screens, uint table_options,
ulong data_offset, handler *file)
{
@@ -489,8 +602,8 @@ static bool pack_header(uchar *forminfo, enum db_type table_type,
/* Check fields */
- List_iterator<create_field> it(create_fields);
- create_field *field;
+ List_iterator<Create_field> it(create_fields);
+ Create_field *field;
while ((field=it++))
{
uint tmp_len= system_charset_info->cset->charpos(system_charset_info,
@@ -527,7 +640,7 @@ static bool pack_header(uchar *forminfo, enum db_type table_type,
We mark first TIMESTAMP field with NOW() in DEFAULT or ON UPDATE
as auto-update field.
*/
- if (field->sql_type == FIELD_TYPE_TIMESTAMP &&
+ if (field->sql_type == MYSQL_TYPE_TIMESTAMP &&
MTYP_TYPENR(field->unireg_check) != Field::NONE &&
!time_stamp_pos)
time_stamp_pos= (uint) field->offset+ (uint) data_offset + 1;
@@ -571,7 +684,8 @@ static bool pack_header(uchar *forminfo, enum db_type table_type,
length= field->save_interval->type_lengths[pos];
hex_length= length * 2;
field->interval->type_lengths[pos]= hex_length;
- field->interval->type_names[pos]= dst= sql_alloc(hex_length + 1);
+ field->interval->type_names[pos]= dst= (char*) sql_alloc(hex_length +
+ 1);
octet2hex(dst, src, length);
}
}
@@ -631,11 +745,11 @@ static bool pack_header(uchar *forminfo, enum db_type table_type,
/* get each unique interval each own id */
-static uint get_interval_id(uint *int_count,List<create_field> &create_fields,
- create_field *last_field)
+static uint get_interval_id(uint *int_count,List<Create_field> &create_fields,
+ Create_field *last_field)
{
- List_iterator<create_field> it(create_fields);
- create_field *field;
+ List_iterator<Create_field> it(create_fields);
+ Create_field *field;
TYPELIB *interval=last_field->interval;
while ((field=it++) != last_field)
@@ -659,18 +773,18 @@ static uint get_interval_id(uint *int_count,List<create_field> &create_fields,
/* Save fields, fieldnames and intervals */
-static bool pack_fields(File file, List<create_field> &create_fields,
+static bool pack_fields(File file, List<Create_field> &create_fields,
ulong data_offset)
{
reg2 uint i;
uint int_count, comment_length=0;
uchar buff[MAX_FIELD_WIDTH];
- create_field *field;
+ Create_field *field;
DBUG_ENTER("pack_fields");
/* Write field info */
- List_iterator<create_field> it(create_fields);
+ List_iterator<Create_field> it(create_fields);
int_count=0;
while ((field=it++))
@@ -687,7 +801,7 @@ static bool pack_fields(File file, List<create_field> &create_fields,
int2store(buff+10,field->unireg_check);
buff[12]= (uchar) field->interval_id;
buff[13]= (uchar) field->sql_type;
- if (field->sql_type == FIELD_TYPE_GEOMETRY)
+ if (field->sql_type == MYSQL_TYPE_GEOMETRY)
{
buff[14]= (uchar) field->geom_type;
#ifndef HAVE_SPATIAL
@@ -701,13 +815,13 @@ 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,(byte*) buff,FCOMP,MYF_RW))
+ if (my_write(file, buff, FCOMP, MYF_RW))
DBUG_RETURN(1);
}
/* Write fieldnames */
buff[0]=(uchar) NAMES_SEP_CHAR;
- if (my_write(file,(byte*) buff,1,MYF_RW))
+ if (my_write(file, buff, 1, MYF_RW))
DBUG_RETURN(1);
i=0;
it.rewind();
@@ -717,7 +831,7 @@ static bool pack_fields(File file, List<create_field> &create_fields,
*pos++=NAMES_SEP_CHAR;
if (i == create_fields.elements-1)
*pos++=0;
- if (my_write(file,(byte*) buff,(uint) (pos-(char*) buff),MYF_RW))
+ if (my_write(file, buff, (size_t) (pos-(char*) buff),MYF_RW))
DBUG_RETURN(1);
i++;
}
@@ -777,7 +891,7 @@ static bool pack_fields(File file, List<create_field> &create_fields,
tmp.append('\0'); // End of intervall
}
}
- if (my_write(file,(byte*) tmp.ptr(),tmp.length(),MYF_RW))
+ if (my_write(file,(uchar*) tmp.ptr(),tmp.length(),MYF_RW))
DBUG_RETURN(1);
}
if (comment_length)
@@ -787,7 +901,7 @@ static bool pack_fields(File file, List<create_field> &create_fields,
while ((field=it++))
{
if (field->comment.length)
- if (my_write(file, (byte*) field->comment.str, field->comment.length,
+ if (my_write(file, (uchar*) field->comment.str, field->comment.length,
MYF_RW))
DBUG_RETURN(1);
}
@@ -798,31 +912,30 @@ static bool pack_fields(File file, List<create_field> &create_fields,
/* save an empty record on start of formfile */
-static bool make_empty_rec(THD *thd, File file,enum db_type table_type,
+static bool make_empty_rec(THD *thd, File file,enum legacy_db_type table_type,
uint table_options,
- List<create_field> &create_fields,
+ List<Create_field> &create_fields,
uint reclength,
- ulong data_offset)
+ ulong data_offset,
+ handler *handler)
{
- int error;
+ int error= 0;
Field::utype type;
uint null_count;
uchar *buff,*null_pos;
TABLE table;
- create_field *field;
- handler *handler;
+ TABLE_SHARE share;
+ Create_field *field;
enum_check_fields old_count_cuted_fields= thd->count_cuted_fields;
DBUG_ENTER("make_empty_rec");
/* We need a table to generate columns for default values */
- bzero((char*) &table,sizeof(table));
- table.s= &table.share_not_to_be_used;
- handler= get_new_handler((TABLE*) 0, thd->mem_root, table_type);
+ bzero((char*) &table, sizeof(table));
+ bzero((char*) &share, sizeof(share));
+ table.s= &share;
- if (!handler ||
- !(buff=(uchar*) my_malloc((uint) reclength,MYF(MY_WME | MY_ZEROFILL))))
+ if (!(buff=(uchar*) my_malloc((size_t) reclength,MYF(MY_WME | MY_ZEROFILL))))
{
- delete handler;
DBUG_RETURN(1);
}
@@ -838,46 +951,47 @@ static bool make_empty_rec(THD *thd, File file,enum db_type table_type,
}
null_pos= buff;
- List_iterator<create_field> it(create_fields);
+ List_iterator<Create_field> it(create_fields);
thd->count_cuted_fields= CHECK_FIELD_WARN; // To find wrong default values
while ((field=it++))
{
/*
regfield don't have to be deleted as it's allocated with sql_alloc()
*/
- Field *regfield=make_field((char*) buff+field->offset + data_offset,
- field->length,
- null_pos + null_count / 8,
- null_count & 7,
- field->pack_flag,
- field->sql_type,
- field->charset,
- field->geom_type,
- field->unireg_check,
- field->save_interval ? field->save_interval :
- field->interval,
- field->field_name,
- &table);
+ Field *regfield= make_field(&share,
+ buff+field->offset + data_offset,
+ field->length,
+ null_pos + null_count / 8,
+ null_count & 7,
+ field->pack_flag,
+ field->sql_type,
+ field->charset,
+ field->geom_type,
+ field->unireg_check,
+ field->save_interval ? field->save_interval :
+ field->interval,
+ field->field_name);
if (!regfield)
{
error= 1;
goto err; // End of memory
}
+ /* save_in_field() will access regfield->table->in_use */
+ regfield->init(&table);
+
if (!(field->flags & NOT_NULL_FLAG))
{
*regfield->null_ptr|= regfield->null_bit;
null_count++;
}
- if (field->sql_type == FIELD_TYPE_BIT && !f_bit_as_char(field->pack_flag))
+ if (field->sql_type == MYSQL_TYPE_BIT && !f_bit_as_char(field->pack_flag))
null_count+= field->length & 7;
type= (Field::utype) MTYP_TYPENR(field->unireg_check);
- if (field->def &&
- (regfield->real_type() != FIELD_TYPE_YEAR ||
- field->def->val_int() != 0))
+ if (field->def)
{
int res= field->def->save_in_field(regfield, 1);
/* If not ok or warning of level 'note' */
@@ -885,10 +999,11 @@ static bool make_empty_rec(THD *thd, File file,enum db_type table_type,
{
my_error(ER_INVALID_DEFAULT, MYF(0), regfield->field_name);
error= 1;
+ delete regfield; //To avoid memory leak
goto err;
}
}
- else if (regfield->real_type() == FIELD_TYPE_ENUM &&
+ else if (regfield->real_type() == MYSQL_TYPE_ENUM &&
(field->flags & NOT_NULL_FLAG))
{
regfield->set_notnull();
@@ -910,11 +1025,10 @@ static bool make_empty_rec(THD *thd, File file,enum db_type table_type,
if (null_count & 7)
*(null_pos + null_count / 8)|= ~(((uchar) 1 << (null_count & 7)) - 1);
- error=(int) my_write(file,(byte*) buff, (uint) reclength,MYF_RW);
+ error= my_write(file, buff, (size_t) reclength,MYF_RW) != 0;
err:
- my_free((gptr) buff,MYF(MY_FAE));
- delete handler;
+ my_free(buff, MYF(MY_FAE));
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 8e01e6222e6..6f9c44d98f9 100644
--- a/sql/unireg.h
+++ b/sql/unireg.h
@@ -84,6 +84,7 @@
#define PSEUDO_TABLE_BITS (PARAM_TABLE_BIT | OUTER_REF_TABLE_BIT | \
RAND_TABLE_BIT)
#define MAX_FIELDS 4096 /* Limit in the .frm file */
+#define MAX_PARTITIONS 1024
#define MAX_SELECT_NESTING (sizeof(nesting_map)*8-1)
@@ -108,7 +109,6 @@
#define READ_RECORD_BUFFER (uint) (IO_SIZE*8) /* Pointer_buffer_size */
#define DISK_BUFFER_SIZE (uint) (IO_SIZE*16) /* Size of diskbuffer */
-#define POSTFIX_ERROR DBL_MAX
#define ME_INFO (ME_HOLDTANG+ME_OLDWIN+ME_NOREFRESH)
#define ME_ERROR (ME_BELL+ME_OLDWIN+ME_NOREFRESH)
@@ -152,14 +152,48 @@
#define DONT_GIVE_ERROR 256 /* Don't do frm_error on openfrm */
#define READ_SCREENS 1024 /* Read screens, info and helpfile */
#define DELAYED_OPEN 4096 /* Open table later */
-#define NO_ERR_ON_NEW_FRM 8192 /* stop error sending on new format */
+#define OPEN_VIEW 8192 /* Allow open on view */
#define OPEN_VIEW_NO_PARSE 16384 /* Open frm only if it's a view,
but do not parse view itself */
+/**
+ This flag is used in function get_all_tables() which fills
+ I_S tables with data which are retrieved from frm files and storage engine
+ The flag means that we need to open FRM file only to get necessary data.
+*/
+#define OPEN_FRM_FILE_ONLY 32768
+/**
+ This flag is used in function get_all_tables() which fills
+ I_S tables with data which are retrieved from frm files and storage engine
+ The flag means that we need to process tables only to get necessary data.
+ Views are not processed.
+*/
+#define OPEN_TABLE_ONLY OPEN_FRM_FILE_ONLY*2
+/**
+ This flag is used in function get_all_tables() which fills
+ I_S tables with data which are retrieved from frm files and storage engine
+ The flag means that we need to process views only to get necessary data.
+ Tables are not processed.
+*/
+#define OPEN_VIEW_ONLY OPEN_TABLE_ONLY*2
+/**
+ This flag is used in function get_all_tables() which fills
+ I_S tables with data which are retrieved from frm files and storage engine.
+ The flag means that we need to open a view using
+ open_normal_and_derived_tables() function.
+*/
+#define OPEN_VIEW_FULL OPEN_VIEW_ONLY*2
+/**
+ This flag is used in function get_all_tables() which fills
+ I_S tables with data which are retrieved from frm files and storage engine.
+ The flag means that I_S table uses optimization algorithm.
+*/
+#define OPTIMIZE_I_S_TABLE OPEN_VIEW_FULL*2
+
#define 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)
+#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
diff --git a/sql/watchdog_mysqld b/sql/watchdog_mysqld
deleted file mode 100755
index 0b26bb15acd..00000000000
--- a/sql/watchdog_mysqld
+++ /dev/null
@@ -1,126 +0,0 @@
-#!/usr/bin/perl
-# Copyright (C) 1979-1998 TcX AB & Monty Program KB & Detron HB
-#
-# This software is distributed with NO WARRANTY OF ANY KIND. No author or
-# distributor accepts any responsibility for the consequences of using it, or
-# for whether it serves any particular purpose or works at all, unless he or
-# she says so in writing. Refer to the Free Public License (the "License")
-# for full details.
-#
-# Every copy of this file must include a copy of the License, normally in a
-# plain ASCII text file named PUBLIC. The License grants you the right to
-# copy, modify and redistribute this file, but only under certain conditions
-# described in the License. Among other things, the License requires that
-# the copyright notice and this notice be preserved on all copies. */
-
-#
-# This scripts is started by safe_mysqld. It checks that MySQL is alive and
-# working ( = answering to ping). If not, force mysqld down, check all
-# tables and let safe_mysqld restart the server.
-#
-# For this to work, you should have procmail installed as the commands
-# 'lockfile' and is used to sync with safe_mysqld
-#
-# NOTE: You should only use this script as a last resort if mysqld locks
-# up unexpectedly in a critical application and you have to get it to
-# work temporarily while waiting for a solution from mysql@tcx.se or
-# mysql-support@tcx.se
-
-
-use POSIX "waitpid";
-
-# Arguments from safe_mysqld
-
-if ($#ARGV != 4)
-{
- print "$0: Wrong number of arguments. Aborting\n";
- exit 1;
-}
-
-$lock_file=shift; # File to lock to sync with safe_mysqld
-$pid_file=shift; # Pid file used by mysqld
-$bin_dir=shift; # Directory where mysqladmin is
-$test_timeout=shift; # Time between testing if mysqld is alive
-$wait_timeout=shift; # How long time to wait for ping
-
-$|=1; # autoflush
-
-# Check that mysqld has started properly
-
-for ($i=1 ; $i < 10 ; $i ++)
-{
- last if (-e $pid_file);
-}
-sleep(1); # If server has just created the file
-if (($mysqld_pid=`cat $pid_file`) <= 0)
-{
- print "$0: Error: Invalid pidfile (contains '$mysqld_pid'). Aborting\n";
-}
-
-# Start pinging mysqld
-
-for (;;)
-{
- sleep($test_timeout); # Time between tests
- `lockfile $lock_file > /dev/null 2>&1`; # Sync with safe_mysqld
- if (($pid=fork()) == 0)
- {
- setpgrp(0,0);
- exit(int(system("$bin_dir/mysqladmin -w status > /dev/null")/256));
- }
- for ($i=0; ($res=waitpid(-1,&POSIX::WNOHANG)) == 0 && $i < $wait_timeout ; $i++)
- {
- sleep(1);
- }
- if ($res == 0)
- {
- print "$0: Warning: mysqld hanged; Killing it so that safe_mysqld can restart it!\n";
- $mysqld_pid= `cat $pid_file`;
- if ($mysqld_pid <= 0)
- {
- print "$0: Error: Invalid pidfile (contains '$mysqld_pid'). Aborting\n";
- system("rm -f $lock_file");
- kill(-9,$pid);
- exit 1;
- }
- print "$0: Sending signal 15 to $mysqld_pid\n";
- kill(-15, $pid,$mysqld_pid); # Give it a last change to die nicely
- for ($i=0 ; $i < 5 ; $i++) { sleep(1); } # Wait 5 seconds (signal safe)
- waitpid(-1,&POSIX::WNOHANG);
- if (kill(0,$pid,$mysqld_pid) != 0)
- {
- print "$0: Sending signal 9 to $mysqld_pid\n";
- kill(-9,$pid,$mysqld_pid); # No time to be nice anymore
- sleep(2); # Give system time to clean up
- waitpid(-1,&POSIX::WNOHANG);
- if (kill(0,$mysqld_pid) != 0)
- {
- print "$0: Warning: mysqld don't want to die. Aborting\n";
- system("rm -f $lock_file");
- exit 1;
- }
- }
- # safe_mysqld will not restart mysqld if the pid file doesn't exists
- system("rm $pid_file");
- system("touch $pid_file");
- }
- elsif ($res == -1)
- {
- print "$0: Error: waitpid returned $res when wating for pid $pid\nPlease verify that $0 is correct for your system\n";
- system("rm -f $lock_file");
- exit 1;
- }
- else
- {
- $exit_code=int($?/256);
- if ($exit_code != 0)
- {
- print "$0: Warning: mysqladmin returned exit code $exit_code\n";
- }
- else
- {
- #print "mysqld is alive and feeling well\n";
- }
- }
- system("rm -f $lock_file"); # safemysqld will now take over
-}